PCM data flow - part 2: ASoC data structure

http://blog.csdn.net/azloong/article/details/17252551

ASoC:ALSA System on Chip,是建立在标准ALSA驱动之上,为了更好支持嵌入式系统和移动设备中的音频codec的一套软件体系,它依赖于标准ALSA驱动框架。内核文档Documentation/alsa/soc/overview.txt中详细介绍了ASoC的设计初衷,这里不一一引用,简单陈述如下:

·          独立的codec驱动,标准的ALSA驱动框架里面codec驱动往往与SoCCPU耦合过于紧密,不利于在多样化的平台/机器上移植复用;

·          方便codec与SoC通过音频总线PCM/I2S建立链接,通常在Machine驱动里面通过dai_link配置的codec_dai_name和cpu_dai_name来指定codec_dai和cpu_dai;

·          动态音频电源管理DAPM,使得codec任何时候都工作在最低功耗状态,同时负责音频路由的创建;这部分在我的另一个系列文章dapm里有详细的分析,是ASoC的重点和难点;

·          POPs和click音抑制弱化处理,在ASoC中通过正确的音频部件上下电次序来实现;

·          Machine驱动的特定控制,比如耳机、麦克风的插拔检测,外放功放的开关。


在概述中已经介绍了ASoC驱动的三大构成:Codec、Platform和Machine,下面列举各部分驱动包含的功能特性:

ASoC Codec Driver

·          Codec DAI和PCM的配置信息;

·          Codec的控制接口,I2C或者SPI等;

·          Mixer和其他音频控件;

·          Codec的音频操作接口,见snd_soc_dai_ops结构体定义;

·          DAPM描述信息;

·          DAPM事件处理句柄;

·          DAC数字静音控制。

ASoC Platform Driver

包括pcm_dma和cpu_dai两部分:

·          pcm_dma实现音频dma操作函数集,具体见snd_pcm_ops结构体定义;

·          cpu_dai实现数字音频接口的描述和配置、系统时钟配置、休眠唤醒等。

ASoC Machine Driver

·          作为链结Platform和Codec的载体,它必须定义dai_link为音频物理链路选定Platform和Codec;

·          处理机器特有的音频控件和音频事件,例如回放时打开外放功放。


以下UML类图标示着ASoC中重要的数据结构以及它们之间的联系,帮助理解整个ASoC系统。通过不同颜色标示数据结构对应的模块,其中soc-core和pcm_native是核心,platform_drv、codec_drv、cpu_dai_drv、machine_drv是我们要实现的alsa-driver,主要是一些结构体实例的创建和回调函数集的实现。

注:对于Linux内核来说,整理清楚模块中重要的数据结构,包括主要成员的作用以及各个数据结构之间的联系,那么就等于把握了该模块的脉络,剩下的只是细节。因此在各模块的分析之前,作者会把模块重要的数据结构先列出来,逐一介绍,然后才是源码分析。



snd_soc_pcm_runtime

整个ASoC都以snd_soc_pcm_runtime为桥梁来操作,可以这么理解:每一个音频物理链路对应一个dai_link,而每个dai_link都有着自身的设备私有数据,这些私有数据保存在snd_soc_pcm_runtime中。

  1. /* SoC machine DAI configuration, glues a codec and cpu DAI together */  
  2. struct snd_soc_pcm_runtime {  
  3.     struct device *dev;  
  4.     struct snd_soc_card *card;  
  5.     struct snd_soc_dai_link *dai_link;  
  6.     struct mutex pcm_mutex;  
  7.     enum snd_soc_pcm_subclass pcm_subclass;  
  8.     struct snd_pcm_ops ops;  
  9.   
  10.     unsigned int complete:1;  
  11.     unsigned int dev_registered:1;  
  12.   
  13.     long pmdown_time;  
  14.   
  15.     /* runtime devices */  
  16.     struct snd_pcm *pcm;  
  17.     struct snd_soc_codec *codec;  
  18.     struct snd_soc_platform *platform;  
  19.     struct snd_soc_dai *codec_dai;  
  20.     struct snd_soc_dai *cpu_dai;  
  21.   
  22.     struct delayed_work delayed_work;  
  23. };  

·          card:SoC card,在Machine中会把它作为名为"soc-audio"的platform_device的私有数据,主要包含dai_link的定义、Machine的休眠唤醒回调、Machine特定的dapm微件和路由、Machine特定的控件等;

·          dai_link:该snd_soc_pcm_runtime对应的音频物理链路,声明链路需要绑定的codec、codec_dai、cpu_dai、platform设备名称;

·          ops:pcm回调函数集,用于调用codec_dai、cpu_dai、platform的音频操作接口(hw_params/prepare/trigger/…),后面在soc_new_pcm()详细分析;

·          pcm:pcm中间层核心结构,保存多个snd_pcm_substream分别用于回放和录制,而每个snd_pcm_substream有着自身的pcm操作接口和运行期信息(snd_pcm_runtime);

·          codec:在codec_drv中通过snd_soc_register_codec()创建,可以认为它是对snd_soc_codec_driver的封装,通过它可以访问操作codec_drv;

·          platform:在platform_drv中通过snd_soc_register_platform()创建,可以认为它是对snd_soc_platform_driver的封装,通过它可以访问操作pcm_dma;

·          codec_dai:通过snd_soc_register_dais()创建,通过它可以访问操作codec_dai;

·          cpu_dai:在cpu_dai_drv中通过snd_soc_register_dai()创建,通过它可以访问操作cpu_dai。


snd_pcm_substream

pcm中间层核心结构,如果说snd_soc_pcm_runtime是ASoC桥梁的话,那么snd_pcm_substream就是pcm native的桥梁,它包含了pcm数据传输所需要的一切元素,如pcm operations、dma buffer、runtime information等。

  1. struct snd_pcm_substream {  
  2.     struct snd_pcm *pcm;  
  3.     struct snd_pcm_str *pstr;  
  4.     void *private_data;     /* copied from pcm->private_data */  
  5.     int number;  
  6.     char name[32];          /* substream name */  
  7.     int stream;         /* stream (direction) */  
  8.     struct pm_qos_request latency_pm_qos_req; /* pm_qos request */  
  9.     size_t buffer_bytes_max;    /* limit ring buffer size */  
  10.     struct snd_dma_buffer dma_buffer;  
  11.     unsigned int dma_buf_id;  
  12.     size_t dma_max;  
  13.     /* -- hardware operations -- */  
  14.     struct snd_pcm_ops *ops;  
  15.     /* -- runtime information -- */  
  16.     struct snd_pcm_runtime *runtime;  
  17.         /* -- timer section -- */  
  18.     struct snd_timer *timer;        /* timer */  
  19.     // ...省略...  
  20. };  

·          private_data:私有数据,是pcm->private_data的拷贝,事实上在soc-pcm中,把snd_soc_pcm_runtime赋给了它;因此对于ASoC来说,拿到了snd_pcm_substream就可以取得snd_soc_pcm_runtime,这样就得到整个asoc-driver所有设备实例(CODEC/DMA/I2S);

·          dma_buffer:用于保存pcm dma驱动分配的dma buffer相关信息,包括dma buffer的物理地址、虚拟地址、大小以及设备类型;其中dma buffer物理地址用于设定dma传输的源地址(对于回放来说)或者目的地址(对于录制来说),虚拟地址用于与用户态之间的音频数据拷贝;

·          ops:pcm操作函数集,用于调用codec_dai、cpu_dai、platform的音频操作接口(hw_params/prepare/trigger/…);通过它,pcm中间层可以启动dma搬运音频数据并启动I2S总线控制器把数据送到codec;

·          runtime:音频数据传输的运行期信息。


snd_pcm_runtime

该结构包含pcm数据传输的运行期信息,如硬件参数、软件参数、dma buffer相关信息等。由于该结构体比较大,就不全贴出来了,分段介绍:

·          dma buffer managementinformation:用于计算dma buffer的剩余空间、当前位置指针等;这些计算是pcm native的一个难点,后面详细分析

  1. snd_pcm_uframes_t avail_max;  
  2. snd_pcm_uframes_t hw_ptr_base;  /* Position at buffer restart */  
  3. snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time */  
  4. unsigned long hw_ptr_jiffies;   /* Time when hw_ptr is updated */  
  5. unsigned long hw_ptr_buffer_jiffies; /* buffer time in jiffies */  
  6. snd_pcm_sframes_t delay;    /* extra delay; typically FIFO size */  

·          hw parameters:包括pcm数据格式、采样率、声道数、周期大小、周期数、量化位数等参数;

  1. /* -- HW params -- */  
  2. snd_pcm_access_t access;    /* access mode */  
  3. snd_pcm_format_t format;    /* SNDRV_PCM_FORMAT_* */  
  4. snd_pcm_subformat_t subformat;  /* subformat */  
  5. unsigned int rate;      /* rate in Hz */  
  6. unsigned int channels;      /* channels */  
  7. snd_pcm_uframes_t period_size;  /* period size */  
  8. unsigned int periods;       /* periods */  
  9. snd_pcm_uframes_t buffer_size;  /* buffer size */  
  10. snd_pcm_uframes_t min_align;    /* Min alignment for the format */  
  11. size_t byte_align;  
  12. unsigned int frame_bits;  
  13. unsigned int sample_bits;  
  14. unsigned int info;  
  15. unsigned int rate_num;  
  16. unsigned int rate_den;  
  17. unsigned int no_period_wakeup: 1;  

·          sw parameters:主要留意start_threshold、stop_threshold和silence_threshold;

  1. /* -- SW params -- */  
  2. int tstamp_mode;        /* mmap timestamp is updated */  
  3. unsigned int period_step;  
  4. snd_pcm_uframes_t start_threshold;  
  5. snd_pcm_uframes_t stop_threshold;  
  6. snd_pcm_uframes_t silence_threshold; /* Silence filling happens when 
  7.                     noise is nearest than this */  
  8. snd_pcm_uframes_t silence_size; /* Silence filling size */  
  9. snd_pcm_uframes_t boundary; /* pointers wrap point */  
  10.   
  11. snd_pcm_uframes_t silence_start; /* starting pointer to silence area */  
  12. snd_pcm_uframes_t silence_filled; /* size filled with silence */  

·          dma buffer

  1. /* -- DMA -- */             
  2. unsigned char *dma_area;    /* DMA area */  
  3. dma_addr_t dma_addr;        /* physical bus address (not accessible from main CPU) */  
  4. size_t dma_bytes;       /* size of DMA area */  
  5.   
  6. struct snd_dma_buffer *dma_buffer_p;    /* allocated buffer */  

其中dma_buffer_p和snd_pcm_substream的dma_buffer字段是一致的,保存着pcm_dma驱动中分配的dma buffer相关信息。至于dma_area、dma_addr与dma_buffer_p的关系,看如下的函数就一目了然了:

  1. static inline void snd_pcm_set_runtime_buffer(struct snd_pcm_substream *substream,  
  2.                           struct snd_dma_buffer *bufp)  
  3. {  
  4.     struct snd_pcm_runtime *runtime = substream->runtime;  
  5.     if (bufp) {  
  6.         runtime->dma_buffer_p = bufp;  
  7.         runtime->dma_area = bufp->area;  
  8.         runtime->dma_addr = bufp->addr;  
  9.         runtime->dma_bytes = bufp->bytes;  
  10.     } else {  
  11.         runtime->dma_buffer_p = NULL;  
  12.         runtime->dma_area = NULL;  
  13.         runtime->dma_addr = 0;  
  14.         runtime->dma_bytes = 0;  
  15.     }  
  16. }  

dma_area正是dma buffer的虚拟地址,dma_addr是dma buffer的物理地址。


以上都是alsa-core的数据结构,而对于硬件设备驱动(CODEC/DMA/I2S)来说,我们可能更关心:

·          snd_soc_codec_driver:音频芯片描述及操作接口,如控件/微件/音频路由描述信息、时钟控制、IO控制等;

·          snd_soc_dai_driver:音频接口描述及操作接口,分为codec_dai和cpu_dai;

·          snd_soc_platform_driver:音频dma驱动描述及操作接口;

·          snd_soc_dai_link:音频链路描述及板级操作函数。

后面分析这些数据结构如何注册到soc-core中。


下面是goni_wm8994的UML类图,从这个类图中,我们可以看到goni_wm8994整个音频驱动的框架是怎样的。




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值