android Audio OverView之三(driver)

来源:互联网 发布:韦德生涯数据更新 编辑:程序博客网 时间:2024/05/17 01:26

下面进入Linux driver篇
LINUX Audio 架构

这里写图片描述

linux Audio driver分为 ALSA和 OSS架构,OSS架构目前以很少使用,原因是由于代码不利于维护。ALSA架构 分层清晰,代码可以重复利用(如codec driver)等一系列好处被广泛应用,其中 ALSA 又分为ASOC(cpu 内部无dsp) , dpcm(cpu 内部含有dsp)

ALSA(Advanced Linux Sound Architecture(高级Linux声音体系)的缩写)是为声卡提供驱动的Linux内核组件,以替代原先的OSS(开放声音系统)。ALSA除了像OSS那样提供一组内核驱动程序模块以外,还专门为简化应用程序的编写提供了相应的库函数,与OSS提供的基于ioctl的原始编程接口相比,ALSA函数库使用起来要更加方便一点。

当ALSA 驱动加载时会分别在文件系统下注册 /proc/asound/ /sys/class/sound节点
这里写图片描述

这里写图片描述

ALSA 驱动结构 – Platform
音频驱动主要有三部分组成:
1、 Platform:通常指某款SoC平台,如intel、qcom等等。Platform又可细分两部分:
1.1、CPU DAI:在嵌入式系统里面通常指CPU的I2S、PCM总线接口,负责将音频数据从AIF FIFO搬运到CODEC(Playback的情形,Capture则方向相反)。CPU DAI通过snd_soc_register_dai()来注册。注:DAI是Digital Audio Interface的缩写,分为CPU DAI和CODEC DAI,这两者通过I2S/PCM总线连接;AIF是Audio Interface的缩写,一般分为I2S和PCM接口。
1.2、DMA:负责将音频数据从userspace通过DMA操作搬运到AIF FIFO。DMA操作通过snd_soc_register_platform()来注册。值得留意的是:某些情形下是不需要DMA操作的,比如Modem和CODEC直连,因为Modem本身已经把数据送到它的PCM接口FIFO了,这时只需启动PCM接口把数据搬运到CODEC即可;在这种情形下,Machine驱动snd_soc_dai_link中需要指定.platform_name = “snd-soc-dummy”, 这是虚拟出来的DMA驱动。
ALSA 驱动结构 – Codec/Machine
1. CODEC:对于Playback来说,userspace送过来的PCM数据是经过抽样量化出来的数字信号,在codec经过DAC转换成模拟信号送到外放耳机输出,这样我们就可以听到声音了。Codec字面意思是编解码器,但芯片里面的部件很多,常见的有AIF(音频接口)、DAC、ADC、Mixer、PGA、Line-in、Line-out,有些高端的codec芯片还有EQ、DSP、SRC、DRC、AGC、Echo canceller、Noise suppression等功能。
2. Machine:指某一款机器,它把CPU DAI、CODEC DAI、MODEM DAI各个音频总线接口通过定义dai_link链结起来,然后注册snd_soc_card。和上面两个不一样,Platform和CODEC驱动一般是可以重用的,而Machine有它特定的硬件特性,几乎是不可重用的。所谓的硬件特性指:DAIs之间的链结;通过某个GPIO打开Amplifier;通过某个GPIO检测耳机插拔;使用某个时钟如MCLK/External OSC作为I2S、CODEC模块的基准时钟源等等

DPCM(dynamic PCM)
这里写图片描述

dpcm主要是为 内部有dsp的 芯片准备的。这里引出了 fe dai和be dai的概念。
fe dai(front end ):软件上的概念,通俗讲就是音频的软件流,如我们之前提到的,fasttrack,deepbuffer,等这些线程传递过来的软件流。
be dai(back end): 具体的 硬件 设备,这里 可一是speaker,bt ,headset, dmic等。
任何一个 fe dai都可以 通过内部 通路的切换连接到任意一个be dai上面,这也是dpcm的好处之一。
详细解释大家可以参考kernel中提供的文档
参照 kernel/Documentation/sound/alsa/DPCM.txt
如图:
这里写图片描述
dynamic =1,意思是支持dpcm动态匹配模式。
Fe dai codec_dai_name 定义为 dummy,是为了可以动态匹配BE dai,这一切都是在ASOC core 层完成的,驱动中只要定义好对应的匹配名字就可以。
这里写图片描述

Cpu dai widget 和 codec dai widget的连接贯穿数据流的流向,首要是要找到 snd_soc_dapm_dai_in,snd_soc_dapm_dai_out类型的widget

具体可以参考如下链接,虽然其讲的是非dpcm模式,但还是有一定的参考意义
http://blog.csdn.net/droidphone/article/details/14548631

ALSA driver 初始化流程
首先platform driver分别注册
1.snd_soc_register_component,在 ASOC 中创建 cpu_dai list /sound/soc/intel/pcm.c
2.snd_soc_register_codec ,在ASOC 中创建codec_dai list /sound/soc/codecs/wm5102.c
3. snd_soc_register_card,通过 late_initcall(snd_cht_driver_init) 调用 /sound/soc/intel/board/cht_bl_dpcm_wm5102.c

在snd_soc_register_card中调用snd_soc_instantiate_card,完成cpu_dai list 和 codec_dai list 中widget的绑定,建立/dev/snd/pcmCxDxp 建立 /sys/ /proc/文件节点。调用 cpu_dai->driver->probe. Codec_dai->driver->probe, 分别进行,codec 和 内部dsp的一些初始化操作。

打开PCM设备
当应用程序open设备文件/dev/snd/pcmCxDxp时,会进入soc-pcm.c open回调函数
这里写图片描述

上面的代码片段可知,dpcm模式和非dpcm模式是根据dynamic标志位获取的,而alsa的 open/read/write也是在alsa lib层实现的,驱动中只要实现对用的handle就可以了,可以看出alsa代码层次特别清晰,也是其广泛应用的原因之一。
在dpcm_fe_dai_open中根据cpu dai widget和 codec dai widget名字匹配上FE DAI 和 BE DAI,最终会调用驱动中snd_soc_dai_ops 中实现的函数,
以ops.hw_params为例,当用户空间设置 hw_params时,会分别调用, cpu_dai->driver->ops->hw_params
codec_dai->driver->ops->hw_params,从而将数据流连通起来

是谁触发了一次dapm
一个widget的状态改变如何传递到整个音频路径上的所有widget。这些过程总是需要一个起始点:是谁触动了dapm,使得它需要执行上述的扫描和上电过程?事实上,以下几种情况可以触发dapm发起一次扫描操作:
1.声卡初始化阶段,snd_soc_dapm_new_widgets函数创建widget包含的kcontrol后,会触发一次扫描操作。
2.用户空间的应用程序修改了widget中包含的dapm kcontrol的配置值时,会触发一次扫描操作。
3.pcm的打开或关闭,会通过音频流widget触发一次扫描操作。
驱动程序在改变了某个widget并把它加入到dapm_dirty链表后,主动调用snd_soc_dapm_sync函数触发扫描操作。

对于 intel 平台, tinymix –D 1 + kcontrol name

用户空间对kcontrol的修改,最终都会调用到kcontrol的put回调函数,(每一个kcontrol在驱动中定义的时候都有一个默认的put回调函数)
1. Open /dev/snd/Controlx
2. Tinymix –D + “Kcontrol name”
这里写图片描述
通过dapm_power_widgets函数,触发整个音频路径的扫描过程,所以,尽管我们只改变了mixer kcontrol中的一个输入端的连接状态,所有相关的widget的电源状态都会被重新设定,这一切,都是自动完成的,对用户空间的应用程序完全透明,实现了dapm的原本设计目标。

好了, hal层到此 位置。

参考链接
http://blog.csdn.net/droidphone/article/details/6271122
http://blog.csdn.net/azloong/article/details/14105023