zoukankan      html  css  js  c++  java
  • ALSA声卡09_从零编写之参数设置_学习笔记

    1、参数设置分析

    (1)open: soc_pcm_open 依次调用cpu_dai, dma, codec_dai, machine的open或startup函数


    只在dma的open函数里添加参数相关的代码


    (2)SNDRV_PCM_IOCTL_HW_PARAMS: soc_pcm_hw_params 依次调用machine,codec_dai,cpu_dai,platform(dma)的hw_params函数
        
    在uda1341.c, s3c2440-iis.c里实现hw_params函数(把裸板程序里面的相关代码移过来)


    (s3c2440-dma.c 主要涉及数据传输,在下一节实现hw_params函数)

    (3)打开声卡的时候会调用到machine部分的dma.c的snd_pcm_ops的dma_open函数)


    2、open函数

    (1)s3c2440_dma.c(Platform)

    static int s3c2440_dma_open(struct snd_pcm_substream *substream)
    {
    struct snd_pcm_runtime *runtime = substream->runtime;
        int ret;
        /* 设置属性 */
    snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
    snd_soc_set_runtime_hwparams(substream, &s3c2440_dma_hardware);
        }
    return 0;
    }

    (2)snd_pcm_hardware结构体

    static const struct snd_pcm_hardware s3c2440_dma_hardware = {
    .info= SNDRV_PCM_INFO_INTERLEAVED | //数据的排列方式(左右左右左右还是左左左右右右)
       SNDRV_PCM_INFO_BLOCK_TRANSFER |
       SNDRV_PCM_INFO_MMAP |
       SNDRV_PCM_INFO_MMAP_VALID |
       SNDRV_PCM_INFO_PAUSE |
       SNDRV_PCM_INFO_RESUME,
    .formats= SNDRV_PCM_FMTBIT_S16_LE |  //所支持的音频数据格式
       SNDRV_PCM_FMTBIT_U16_LE |
       SNDRV_PCM_FMTBIT_U8 |
       SNDRV_PCM_FMTBIT_S8,
    .channels_min= 2,//通道数
    .channels_max= 2,
    .buffer_bytes_max= 128*1024,
    .period_bytes_min= PAGE_SIZE,
    .period_bytes_max= PAGE_SIZE*2,
    .periods_min= 2,
    .periods_max= 128,
    .fifo_size= 32,
    };

    3、hw_params函数(硬件参数)

    (1)uda1341.c(codec)

    static int uda1341_hw_params(struct snd_pcm_substream *substream,
    struct snd_pcm_hw_params *params,
    struct snd_soc_dai *dai)
    {
        /* 根据params的值,设置UDA1341的寄存器 
         * 比如时钟设置,格式
         */
        /* 为了简单, 在uda1341_init_regs里就设置好时钟、格式等参数 */

        return 0;
    }

    Uda1341的初始化

    static void uda1341_init_regs(struct snd_soc_codec *codec)
    {


    /* GPB 4: L3CLOCK */
    /* GPB 3: L3DATA */
    /* GPB 2: L3MODE */

        *gpbcon &= ~((3<<4) | (3<<6) | (3<<8));
        *gpbcon |= ((1<<4) | (1<<6) | (1<<8));

        uda1341_write_reg(codec, UDA1341_STATUS0, 0x40 | STAT0_SC_384FS | STAT0_DC_FILTER); // reset uda1341
        uda1341_write_reg(codec, UDA1341_STATUS1, STAT1_ADC_ON | STAT1_DAC_ON);


        uda1341_write_reg(codec, UDA1341_DATA00, DATA0_VOLUME(0x0)); // maximum volume
        uda1341_write_reg(codec, UDA1341_DATA01, DATA1_BASS(0)| DATA1_TREBLE(0));
        uda1341_write_reg(codec, UDA1341_DATA10, 0);  // not mute
    }


    驱动程序函数uda1341_soc_probe调用uda1341_init_regs函数



    /* 所有寄存器的默认值 */
    static const char 
    uda1341_reg[UDA1341_REG_NUM] = {
      
      /* DATA0 */
        0x00, 0x40, 0x80,
        
    /* Extended address registers */
    0x04, 0x04, 0x04, 0x00, 0x00, 0x00,


      
      /* data1 */
        0x00,
        
        /* status regs */
        0x00, 0x83,
    };

    probe函数

    static int uda1341_soc_probe(struct snd_soc_codec *codec)
    {
        int ret;
        uda1341_init_regs(codec);
        return ret;
    }


    (2)s3c2440_iis.c(Platform)

    static int s3c2440_i2s_hw_params(struct snd_pcm_substream *substream,
    struct snd_pcm_hw_params *params,
    struct snd_soc_dai *dai)
    {
        /* 根据params设置IIS控制器 */


        int tmp_fs;
        int i;
        int min = 0xffff;
        int pre = 0;
        unsigned int fs;
        struct clk *clk = clk_get(NULL, "pclk");

    入口函数映射寄存器(出口函数取消映射)
        /*gpecon   = ioremap(0x56000040, 4);//0x56000040是物理地址,大小是4字节
        iis_regs = ioremap(0x55000000, sizeof(struct s3c2440_iis_regs));*/

        /* 配置GPIO用于IIS */ 

        *gpecon &= ~((3<<0) | (3<<2) | (3<<4) | (3<<6) | (3<<8));
        *gpecon |= ((2<<0) | (2<<2) | (2<<4) | (2<<6) | (2<<8));
        
        
        /* bit[9] : Master clock select, 0-PCLK
         * bit[8] : 0 = Master mode
         * bit[7:6] : 10 = Transmit mode
         * bit[4] : 0-IIS compatible format
         * bit[2] : 384fs, 确定了MASTER CLOCK之后, fs = MASTER CLOCK/384
         * bit[1:0] : Serial bit clock frequency select, 32fs
         */

         寄存器映射(写入一个结构体后再映射)

    static volatile struct s3c2440_iis_regs *iis_regs;

    struct s3c2440_iis_regs {
        unsigned int iiscon ; 
        unsigned int iismod ; 
        unsigned int iispsr ; 
        unsigned int iisfcon; 
        unsigned int iisfifo; 
    };
        iis_regs = ioremap(0x55000000, sizeof(struct s3c2440_iis_regs));//寄存器映射

        if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE)//每个采样值占据的位数(是8位还是16位根据params决定)
            iis_regs->iismod = (2<<6) | (0<<4) | (1<<3) | (1<<2) | (1);
        else if (params_format(params) == SNDRV_PCM_FORMAT_S8)
            iis_regs->iismod = (2<<6) | (0<<4) | (0<<3) | (1<<2) | (1);
        else
            return -EINVAL;//其他值是错误的
        struct clk *clk = clk_get(NULL, "pclk");

    PCLK=clk_get_rate(clk);最后要用clk_put(clk);
        /* Master clock = PCLK/(n+1)
         * fs = Master clock / 384//fs是采样率
         * fs = PCLK / (n+1) / 384
         */

        fs = params_rate(params);//采样频率根据params得到
        for (i = 0; i <= 31; i++)
        {
            tmp_fs = clk_get_rate(clk)/384/(i+1);
            if (ABS(tmp_fs, fs) < min)
            {
                min = ABS(tmp_fs, fs);
                pre = i;
            }
        }
        iis_regs->iispsr = (pre << 5) | (pre);


        /*
         * bit15 : Transmit FIFO access mode select, 1-DMA
         * bit13 : Transmit FIFO, 1-enable
         */
        iis_regs->iisfcon = (1<<15) | (1<<13);
        
        /*
         * bit[5] : Transmit DMA service request, 1-enable
         * bit[1] : IIS prescaler, 1-enable
         */
        iis_regs->iiscon = (1<<5) | (1<<1) ;


        clk_put(clk);
        
        return 0;
    }
    (3)s3c2440_dma.c(Platform)

    static int s3c2440_dma_hw_params(struct snd_pcm_substream *substream,
    struct snd_pcm_hw_params *params)
    {
    struct snd_pcm_runtime *runtime = substream->runtime;
    unsigned long totbytes = params_buffer_bytes(params);
        
        /* 根据params设置DMA */
    snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);


        /* s3c2440_dma_new分配了很大的DMA BUFFER
         * params决定使用多大
         */
    runtime->dma_bytes            = totbytes;
        playback_dma_info.buffer_size = totbytes;
        playback_dma_info.period_size = params_period_bytes(params);


        return 0;
    }

    4、uda1341.c

    codec部分

    static struct snd_soc_codec_driver soc_codec_dev_uda1341 = {
        .probe = uda1341_soc_probe,
      
      /* UDA1341的寄存器不支持读操作,只支持写操作
         * 要知道某个寄存器的当前值,
         * 只能在写入时保存起来(cache)
         */
    .reg_cache_size = sizeof(uda1341_reg),
     //存放的是寄存器的值(cache有多大,看看寄存器个数)
    .reg_word_size = sizeof(u8),//每个寄存器占的数据位数
    .reg_cache_default = uda1341_reg,//默认值
    .reg_cache_step = 1,
    .read  = uda1341_read_reg_cache,//读寄存器的函数
    .write = uda1341_write_reg,  /* 写寄存器 */
    };

    (1)读寄存器(支持寄存器的读操作的编解码芯片可用读寄存器函数)

    /*
     * The codec has no support for reading its registers except for peak level...
     */

    对于uda1341智能在cache中读出来
    static inline unsigned int uda1341_read_reg_cache(struct snd_soc_codec *codec,
    unsigned int reg)
    {
    u8 *cache = codec->reg_cache;//对于uda1341智能在cache中读出来


    if (reg >= UDA1341_REG_NUM)//寄存器的个数大于某个值,返回-1
    return -1;
    return cache[reg];
    }
    (2)写寄存器

    static int uda1341_write_reg(struct snd_soc_codec *codec, unsigned int reg,
    unsigned int value)
    {
    u8 *cache = codec->reg_cache;//把写寄存器的值写到cache里面去(备份下来,因为uda1341不允许读寄存器操作)


        /* 先保存 */
    if (reg >= UDA1341_REG_NUM)//寄存器个数为12
    return -1;
    cache[reg] = value;//保存值


        /* 再写入硬件 */
        
        /* 对于EA(扩展寄存器),需要调用2次l3_write */先把EA地址值作为数据发送出去,再把ED作为数据值发送出去
        if ((reg >= UDA1341_EA000) && (reg <= UDA1341_EA110))
        { //左边的参数是地址(data0),右边的参数是数据  (uda1341_reg_addr[reg]对应的是扩展地址),最高两位或上1
            l3_write(UDA1341_DATA0_ADDR, uda1341_reg_addr[reg] | UDA1341_EXTADDR_PREFIX);
            l3_write(UDA1341_DATA0_ADDR, value | UDA1341_EXTDATA_PREFIX);
        }
        else
        {
            l3_write(uda1341_reg_addr[reg], value | uda1341_data_bit[reg]);//我们访问某个寄存器的时候,数据值要或上某一些位,才能定位到uda1341的某一类下的某个寄存器
        }


        return 0;
    }

    dai部分

    static struct snd_soc_dai_driver uda1341_dai = {
    .ops = &uda1341_dai_ops,
    };

    static const struct snd_soc_dai_ops uda1341_dai_ops = {
    .hw_params  = uda1341_hw_params,
    };


    5、uda1341硬件分析

    (1)2440通过3条线连接uda1341,想写uda1341的寄存器,肯定需要地址和数据,L3MODE等于0时表示线L3DATA线上传输的是地址,为1时传输的是数据。L3CLOCK表示每一个时钟 传输1位。


    (2)看芯片手册(UDA1341TS.pdf)有多少个寄存器

    数据位7~2代表设备地址(表示uda1341),数据位0和1表示地址


    (3)L3接口

    先发出地址,再发出数据。地址里bit7~bit2用于表示uda1341,数据位0和1表示访问哪类寄存器,有3类


    data0类能访问多少个寄存器

    可认为第1、2、3三行代表的是寄存器,第4、5行不是寄存器,是扩展地址,发出的数据是data0这1类,并且前面两个数据是11的话,后面的3位表示扩展地址。


    想访问某个扩展地址,如访问EA,先是地址,后是数据(数据是扩展寄存器的地址(高两位是1))

    下面的1、2、3、4、5、6、7代表寄存器,对于data0这1类有9个寄存器



    data1这1类只有1个

    status这1类,先发出地址,再发出数据,如果数据的最高位是0,选第1行数据,否则选第2行数据。


    所以uda1341会根据第2个周期的某些位来分辨访问哪些寄存器,共有3类12个寄存器。

  • 相关阅读:
    spring 的简单了解
    leetcode 刷题锻炼算法思维
    REDIS学习笔记
    mark:如何使用FileZilla连接虚拟机上的Fedora
    尝试在virtualbox fedora21 下安装additions和mount share folder
    字符集与Mysql字符集处理(二)
    字符集与Mysql字符集处理(一)
    MYSQL开发性能研究——INSERT,REPLACE,INSERT-UPDATE性能比较
    MYSQL开发性能研究——批量插入的优化措施
    Marven笔记贴
  • 原文地址:https://www.cnblogs.com/alan666/p/8311867.html
Copyright © 2011-2022 走看看