免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1756 | 回复: 0
打印 上一主题 下一主题

Linux——UDA1341驱动 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-09-11 17:06 |只看该作者 |倒序浏览
   平台信息:Linux2.6.28+s3c2440
   前言:由于最近需要在s3c2440上调试声卡驱动,达到左右声道可以同时录放音的功能。
   我在google上搜索,网上像分析UDA1341驱动的真的很多,有人多高手都分析的相当到位,呵呵,小弟学习了他们的经验的基础上,终于调试出了可以做全双工使用的声卡驱动。同时编写了上层接口函数,测试完全通过。
static int __init s3c2410_uda1341_init ( void )
{
    memzero ( &input_stream, sizeof ( audio_stream_t ) );
    memzero ( &output_stream, sizeof ( audio_stream_t ) );
    return platform_driver_register ( &s3c2410iis_driver );
    想平台注册驱动设备结构体
}
static void __exit s3c2410_uda1341_exit ( void )
{
    platform_driver_unregister ( &s3c2410iis_driver );
    向平台注销驱动设备结构体
}
module_init ( s3c2410_uda1341_init );
module_exit ( s3c2410_uda1341_exit );
驱动的注册和初始化就不用一一介绍了,接着往下看。
static struct platform_driver s3c2410iis_driver =
{
    .probe        = s3c2410iis_probe,
    .remove        = s3c2410iis_remove,
    .driver        =
    {
        .name    = "s3c2410-iis",
        .owner    = THIS_MODULE,   
    },
};
这个结构体里最重要的就是probe函数
static int s3c2410iis_probe ( struct platform_device *pdev )
{
    unsigned long flags;
    int ret;
   
    dbg ( dbg_debug, "s3c2410iis_probe...\n" );
    res = platform_get_resource ( pdev, IORESOURCE_MEM, 0 );
申请平台资源
    if ( res == NULL )
    {
        dbg ( dbg_err, PFX "failed to get memory region resouce\n" );
        ret = -ENOENT;
        goto probe_out;
    }
    res = request_mem_region( res->start, RESSIZE( res ), pdev->name );
分配内存空间
    if ( res == 0 )
    {
        dbg ( dbg_err, PFX "failed to request io memory region resouce\n" );
        ret = -ENOENT;
        goto probe_out;
    }
    iis_base = ioremap( res->start, RESSIZE( res ) );
物理地址到虚拟地址进行隐射
    if ( iis_base == 0 )
    {
        dbg ( dbg_err, PFX "failed to ioremap() io memory region\n" );
        ret = -ENOENT;
        goto probe_free_mem_region;
    }
    iis_clock = clk_get ( &pdev->dev, "iis" );
取得IIS时钟信号
    if ( iis_clock == NULL )
    {
        dbg ( dbg_err, PFX "failed to find clock source\n" );
        ret = -EINVAL;
        goto probe_iounmap;
    }
    clk_enable ( iis_clock );
  使能时钟  
    local_irq_save ( flags );
    /* GPB 4: L3CLOCK, OUTPUT */
    s3c2410_gpio_cfgpin ( S3C2410_GPB4, S3C2410_GPB4_OUTP );
    s3c2410_gpio_pullup ( S3C2410_GPB4, 1 );
    /* GPB 3: L3DATA, OUTPUT */
    s3c2410_gpio_cfgpin ( S3C2410_GPB3, S3C2410_GPB3_OUTP );
    s3c2410_gpio_pullup ( S3C2410_GPB3, 1 );
    /* GPB 2: L3MODE, OUTPUT */
    s3c2410_gpio_cfgpin ( S3C2410_GPB2, S3C2410_GPB2_OUTP );
    s3c2410_gpio_pullup ( S3C2410_GPB2, 1 );
    /* GPE 3: I2SSDI */
    s3c2410_gpio_cfgpin ( S3C2410_GPE3, S3C2410_GPE3_I2SSDI );
    s3c2410_gpio_pullup ( S3C2410_GPE3, 1 );
    /* GPE 0: I2SLRCK */
    s3c2410_gpio_cfgpin ( S3C2410_GPE0, S3C2410_GPE0_I2SLRCK );
    s3c2410_gpio_pullup ( S3C2410_GPE0, 1 );
    /* GPE 1: I2SSCLK */
    s3c2410_gpio_cfgpin ( S3C2410_GPE1, S3C2410_GPE1_I2SSCLK );
    s3c2410_gpio_pullup ( S3C2410_GPE1, 1 );
    /* GPE 2: CDCLK */
    s3c2410_gpio_cfgpin ( S3C2410_GPE2, S3C2410_GPE2_CDCLK );
    s3c2410_gpio_pullup ( S3C2410_GPE2, 1 );
    /* GPE 4: I2SSDO */
    s3c2410_gpio_cfgpin ( S3C2410_GPE4, S3C2410_GPE4_I2SSDO );
    s3c2410_gpio_pullup ( S3C2410_GPE4, 1 );
    local_irq_restore ( flags );
   
    init_s3c2410_iis_bus();
初始化IIS总线
    init_uda1341();
初始化UDA1341
    output_stream.dma_ch = DMA_CH2;
   
    /*
     * Modified by Zhj EB
     * the api s3c2410_dma_request has been changed since 2.6.25
     * it returns the dma channel instead of -1 in the new kernel version
     */
    if ( !audio_init_dma ( &output_stream, "UDA1341 out" ) )
    {
        audio_clear_dma ( &output_stream, &s3c2410iis_dma_out );
        dbg ( dbg_err, AUDIO_NAME_VERBOSE
                 ": unable to get DMA channels\n" );
        ret =  -EBUSY;
        goto probe_clk_free;
    }
    input_stream.dma_ch = DMA_CH1;
    if ( !audio_init_dma ( &input_stream, "UDA1341 in" ) )
    {
        audio_clear_dma ( &input_stream, &s3c2410iis_dma_in );
        dbg ( dbg_err, AUDIO_NAME_VERBOSE
                 ": unable to get DMA channels\n" );
        ret = -EBUSY;
        goto probe_clk_free;
    }
    audio_dev_dsp = register_sound_dsp ( &smdk2410_audio_fops, -1 );
    audio_dev_mixer = register_sound_mixer ( &smdk2410_mixer_fops, -1 );
注册语音设备,,设备操作结构体:smdk2410_audio_fops
    dbg ( dbg_info, AUDIO_NAME_VERBOSE " initialized\n" );
    return 0;
probe_clk_free:
    clk_disable( iis_clock );
    clk_put( iis_clock );
probe_iounmap:
    iounmap( iis_base );
probe_free_mem_region:
    release_mem_region( res->start, RESSIZE( res ) );
probe_out:
    return ret;
}
static void init_s3c2410_iis_bus ( void )
{
    __raw_writel ( 0, iis_base + S3C2410_IISPSR );
初始化标志寄存器
    __raw_writel ( 0, iis_base + S3C2410_IISCON );
初始化控制寄存器
    __raw_writel ( 0, iis_base + S3C2410_IISMOD );
初始化模式寄存器
    __raw_writel ( 0, iis_base + S3C2410_IISFCON );
初始化FIFO控制寄存器
    clk_disable ( iis_clock );
}
static struct file_operations smdk2410_audio_fops =
{
llseek:
    smdk2410_audio_llseek,
write:
    smdk2410_audio_write,
read:
    smdk2410_audio_read,
poll:
    smdk2410_audio_poll,
ioctl:
    smdk2410_audio_ioctl,
open:
    smdk2410_audio_open,
release:
    smdk2410_audio_release
};
static int smdk2410_audio_open ( struct inode *inode, struct file *file )
{
设备打开函数,控制设备打开的模式,计算设备打开的次数,同时初始化设备的基本状态
    int cold = !audio_active;
    DPRINTK ( "audio_open\n" );
    if ( ( file->f_flags & O_ACCMODE ) == O_RDONLY )
只读模式打开[录音]
    {
        if ( audio_rd_refcount )     
            return -EBUSY;
        audio_rd_refcount++;
    }
    else if ( ( file->f_flags & O_ACCMODE ) == O_WRONLY )
只写模式打开[放音]
    {
        if ( audio_wr_refcount )
            return -EBUSY;
        audio_wr_refcount++;
    }
    else if ( ( file->f_flags & O_ACCMODE ) == O_RDWR )
    {
        if ( audio_rd_refcount || audio_wr_refcount )
            return -EBUSY;
        audio_rd_refcount++;
        audio_wr_refcount++;
    }
    else
        return -EINVAL;
    if ( cold )
    {
        audio_rate = AUDIO_RATE_DEFAULT;
        audio_channels = AUDIO_CHANNELS_DEFAULT;
        audio_fragsize = AUDIO_FRAGSIZE_DEFAULT;
        audio_nbfrags = AUDIO_NBFRAGS_DEFAULT;
        init_s3c2410_iis_bus_rxtx();
对IIS工作模式的初始化。。。。!!!!!!!!!!!
        if ( ( file->f_mode & FMODE_WRITE ) )
        {
        //    init_s3c2410_iis_bus_tx();
            audio_clear_buf ( &output_stream );
            /*
             * Modified by Zhj EB
             */
            if ( !output_stream.buffers && audio_setup_buf( &output_stream ) )
            {
                return -ENOMEM;
            }
        }
        if ( ( file->f_mode & FMODE_READ ) )
        {
        //    init_s3c2410_iis_bus_rx();
            audio_clear_buf ( &input_stream );
        }
    }
    return 0;
}
static void init_s3c2410_iis_bus_rxtx(void)
{
    unsigned int iiscon,iismod,iisfcon;
    __raw_writel ( 0, iis_base + S3C2410_IISPSR );
    __raw_writel ( 0, iis_base + S3C2410_IISCON );
    __raw_writel ( 0, iis_base + S3C2410_IISMOD );
    __raw_writel ( 0, iis_base + S3C2410_IISFCON );
    clk_enable ( iis_clock );
    iiscon = iismod = iisfcon = 0;
    iiscon |= S3C2410_IISCON_PSCEN;
    iiscon |= S3C2410_IISCON_IISEN;
    iismod |= S3C2410_IISMOD_MASTER;
    iismod |= S3C2410_IISMOD_LR_LLOW;
    iismod |= S3C2410_IISMOD_MSB;
    iismod |= S3C2410_IISMOD_16BIT;
    iismod |= S3C2410_IISMOD_384FS;
    iismod |= S3C2410_IISMOD_32FS;
    iismod |= S3C2410_IISMOD_TXRXMODE;
    iisfcon |= S3C2410_IISFCON_RXDMA|S3C2410_IISFCON_RXENABLE;
    iisfcon |= S3C2410_IISFCON_TXDMA|S3C2410_IISFCON_TXENABLE;
    iiscon |= S3C2410_IISCON_RXDMAEN | S3C2410_IISCON_IISEN;
    iiscon |= S3C2410_IISCON_TXDMAEN | S3C2410_IISCON_IISEN;
    iiscon &= ( ~S3C2410_IISCON_RXIDLE );
    iiscon &= ( ~S3C2410_IISCON_TXIDLE );
    audio_set_dsp_speed ( audio_rate );
    __raw_writel ( iismod, iis_base + S3C2410_IISMOD );
    __raw_writel ( iisfcon, iis_base + S3C2410_IISFCON );
    __raw_writel ( iiscon, iis_base + S3C2410_IISCON );
设置寄存器,这个需要对照datasheet看,这样看起来更清晰。
主要就是设置工作在RXTX模式,设置数据传输位,使能DMA等
}
open做好了后,就是套路了,read和write函数。
static ssize_t smdk2410_audio_read ( struct file *file, char *buffer,
                                     size_t count, loff_t * ppos )
{
    const char *buffer0 = buffer;
    audio_stream_t *s = &input_stream;
    int chunksize, ret = 0;
    DPRINTK ( "audio_read: count=%d\n", count );
    if ( !s->buffers )
    {
        int i;
        if ( audio_setup_buf ( s ) )
            return -ENOMEM;
        for ( i = 0; i nbfrags; i++ )
        {
            audio_buf_t *b = s->buf;
            down ( &b->sem );
            s3c2410_dma_enqueue ( s->dma_ch, ( void * ) b, b->dma_addr, s->fragsize );
申请DMA传输队列
            NEXT_BUF ( s, buf );
        }
    }
    while ( count > 0 )
    {
        audio_buf_t *b = s->buf;
        /* Wait for a buffer to become full */
        if ( file->f_flags & O_NONBLOCK )
        {
            ret = -EAGAIN;
            if ( down_trylock ( &b->sem ) )
                break;
        }
        else
        {
            ret = -ERESTARTSYS;
            if ( down_interruptible ( &b->sem ) )
                break;
        }
        chunksize = b->size;
        if ( chunksize > count )
            chunksize = count;
        DPRINTK ( "read %d from %d\n", chunksize, s->buf_idx );
        if ( copy_to_user ( buffer, b->start + s->fragsize - b->size,
                            chunksize ) )
        {
            up ( &b->sem );
            return -EFAULT;
        }
        b->size -= chunksize;
        buffer += chunksize;
        count -= chunksize;
        if ( b->size > 0 )
        {
            up ( &b->sem );
            break;
        }
        /* Make current buffer available for DMA again */
        s3c2410_dma_enqueue ( s->dma_ch, ( void * ) b, b->dma_addr, s->fragsize );
        NEXT_BUF ( s, buf );
    }
    if ( ( buffer - buffer0 ) )
        ret = buffer - buffer0;
    return ret;
}
write函数和read基本查不多,以上只是一个过程,没有做太多的分析,主要是本人研究的不够透,但是我期待大家的讨论,这样可以共同提高。
本次驱动的调试和修改,大部分源码摘自于网络,经过我测试,基本没有问题。欢迎大家通过EMAIL向我索要源码。
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/91378/showart_2050536.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP