分类: LINUX
2014-03-14 19:09:36
在用alsa_amixer controls时,除了我们之前提到的snd_soc_add_controls添加的kcontrols外,还有一些多出来的controls。其实多出来的那些都是属于dapm kcontrol,主要用于切换音频路径。
一、AUDIO PATHS OVERVIEW
以标准内核2.6.32的wm8900 codec为例。先看AUDIO PATHS OVERVIEW,红色线路是LINPUT1(Left Input) -> LEFT INPUT PGA -> LEFT INPUT MIXER -> LEFTOUTPUT MIXER -> LINEOUT1L,表示从LINPUT输入的信号通过这条路径送到LINEOUT输出,再具现一点,就是录音信号直接放到SPK播出。在这条路径上,有三个带+号的圆圈,那就是多路混合器mixer,用于切换输入源或将多个输入源混合输出。
土黄色部分为LEFT INPUT PGA:可选择LINPUT1、LINPUT2和LINPUT3;
绿色部分是LEFT INPUT MIXER:可选择INPUTPGA、LINPUT2、LINPUT3和AUX/LCOM;
蓝色部分是LEFT OUTPUT MIXER:可选择INPUTMIXER、LINPUT3、AUX/LCOM和LEFT DAC等。
配置声音通路时,主要是对mixer做切换输入源操作。如要实现Playback,则需要打通DAC -> OUTPUT MIXER -> LINEOUT通路。
二、配置声音通路
这里先绕开代码,先用alsa_amixer实际操作切换声音路径(以上图的红色路径为例),有个直观印象。
~ # alsa_amixer controls
numid=1,iface=MIXER,name='Mic Bias Level'
......省略......
numid=68,iface=MIXER,name='Left Input Mixer AUX Switch'
numid=69,iface=MIXER,name='Left Input Mixer Input PGA Switch'
numid=66,iface=MIXER,name='Left Input Mixer LINPUT2 Switch'
numid=67,iface=MIXER,name='Left Input Mixer LINPUT3 Switch'
numid=73,iface=MIXER,name='Left Input PGA LINPUT1 Switch'
numid=74,iface=MIXER,name='Left Input PGA LINPUT2 Switch'
numid=75,iface=MIXER,name='Left Input PGA LINPUT3 Switch'
numid=3,iface=MIXER,name='Left Input PGA Switch'
numid=2,iface=MIXER,name='Left Input PGA Volume'
numid=4,iface=MIXER,name='Left Input PGA ZC Switch'
numid=57,iface=MIXER,name='Left Output Mixer AUX Bypass Switch'
numid=60,iface=MIXER,name='Left Output Mixer DACL Switch'
numid=56,iface=MIXER,name='Left Output Mixer LINPUT3 Bypass Switch'
numid=58,iface=MIXER,name='Left Output Mixer Left Input Mixer Switch'
numid=59,iface=MIXER,name='Left Output Mixer Right Input Mixer Switch'
......省略......
~ #
以上打印出所有的codec kcontrols,以之前对应的颜色区分了INPUT PGA、INPUT MIXER、OUTPUT MIXER三个mixer的路径选择。要打开红色通路,则进行如下操作:
numid3 : 使能Left Input PGA;
numid73: 令Left Input PGA选择LINPUT1输入源;
numid69: 令Left Input Mixer选择Left Input PGA输入源;
numid58: 令Left Output Mixer选择Left Input Mixer输入源;
numid43: 使能LINEOUT1。
【这里仅以最基本的alsa-util来配置回路,在实际应用中,可自己实现alsa mixer或编写asound.conf虚拟出不同的devices,前者较典型见Android2.2的ALSAControl.cpp,后者在之后的章节会提一下。我偏向于asound.conf实现,灵活性较高,不同的codec改动asound.conf就行了。】
三、AUDIO PATHS代码实现
知道声音通路如何配置后,回到驱动代码实现。音频路径功能与普通的snd_kcontrol差不多,但写法稍微复杂一点,以'Left Output Mixer Left Input Mixer Switch'为例说明。
1、实现Left Output Mixer的输入源选择:
2、添加名为Left Output Mixer的widget:
【MIXER:多个输入源混合成一个输出,用SND_SOC_DAPM_MIXER定义这个widget,类型为snd_soc_dapm_mixer;
MUX:多路选择器,多路输入,但只能选择一路作为输出,用SND_SOC_DAPM_MUX定义这个widget,类型为snd_soc_dapm_mux;
PGA:单路输入,单路输出,带gain调整的部件,用SND_SOC_DAPM_PGA定义这个widget,类型为snd_soc_dapm_pga。】
3、搭建音频路径:
前面的"Left Output Mixer"表示操作目的对象sink,对应widgets中的名为Left Output Mixer的widget;
中间的"Left Input Mixer Switch"表示操作行为control,对应wm8900_loutmix_controls中的名为Left Input Mixer Switch的kcontrol;
后面的"Left Input Mixer"表示操作源对象source,对应widgets中的名为Left Input Mixer的widget。
合起来的意思:通过动作"Left Input Mixer Switch"将输入源"Left Input Mixer"送到目的混合器"Left Output Mixer"输出。
这里我的解析有点拗口,直接看dapm.txt更清晰一点:
注意:dapm kcontrol名称 = 目的对象sink名称 + 操作行为control名称,即'Left Output Mixer Left Input Mixer Switch',操作源对象source名称被忽略。之后深入源码分析。
4、将kcontrols、widgets和route串联起来:
四、dapm kcontrol
好了,如果仅仅是为了实现audio paths的话,了解以上应该是足够的了。但人生的追求不应该那么简单,我们要从更根源处剖析问题,以后再审视相似的问题时,站的高度也不同。
snd_soc_dapm_route的定义:
sink为目的对象名称,control为操作行为名称,source为源对象名称。
为方便起见,先定义一些名词:source为输入源,在widgets中定义,如"Left Input Mixer";sources为输入源选择,如wm8900_loutmix_controls;control为操作行为,具体在sources数组中定义,如"Left Input Mixer Switch";sink为目的混合器,在widgets中定义,如"Left Output Mixer";route为音频路径,连接source、control和sink,如{"Left Output Mixer", "Left Input Mixer Switch", "Left Input Mixer"}。
1、snd_soc_dapm_new_controls()函数进行widget内存分配、链表初始化工作,比较简单,按下不表。
2、snd_soc_dapm_add_routes(),该函数的注释值得一看:snd_soc_dapm_add_routes - Add routes between DAPM widgets. Connects 2 dapm widgets together via a named audio path. The sink is the widget receiving the audio signal, whilst the source is the sender of the audio signal.
以MIXER类型snd_soc_dapm_mixer为例,简单介绍调用过程:
以上的核心部分是path的建立和链表插入操作,如果在sink->kcontrols中找不到name相匹配的kcontrol,则这个route无效,不创建path。这里path->name为找到的sink->kcontrol.name。
【其实dapm_set_path_status挺困惑的,path的connect理应包含两个操作:1是指定source的选择,2是sink本身的使能。但这里connect状态仅仅是检查source是否选择而已,不检查指定sink是否使能,看起来不太合理。后面会解决这个疑问。这里我理解可能还有些偏差或有遗漏的地方。】
3、snd_soc_dapm_new_widgets()
经过snd_soc_dapm_new_widgets(),终于为snd_soc_dapm_mixer类型的widget建立用于route切换的dapm kcontrol,使得alsa_amixer可以通过控制这些dapm kcontrol来达到音频通路切换的目的。
注:SND_SOC_DAPM_MIXER和SND_SOC_DAPM_MIXER_NAMED_CTL建立的widget仅体现在dapm kcontrol的名字上面,前者为sink->name + kcontrol->name,后者简单的为kcontrol->name。
4、snd_soc_dapm_path[补充]
以上的过程分析非常简略,其实一切都是围绕path展开的。可以把重点放在path的分析上面,搞懂path数据,基本就能理解这个dapm kcontrol的一切了。总的来说:
path->name = sink->kcontrol[i].name,上例是"Left Input Mixer Switch";
path->long_name = sink->name + sink->kcontrol[i].name,上例是"Left Output Mixer Left Input Mixer Switch";
path->source = source,上例是名为"Left Input Mixer"的widget;
path->sink = sink,上例是名为"Left Output Mixer"的widget;
path->connect:通道connect状态,根据sink->kcontrol[i]判断。
path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w, path->long_name);可见path->kcontrol对应sink->kcontrol[i],但名为path->long_name。因此上层可以通过path->long_name找到对应的sink->kcontrol[i]。
五、如何触发path connect
dapm kcontrol的触发很大程度上可以参考snd_kcontrol探究,但更为复杂。当上层触发dapm kcontrol时,会做两个重要动作:1是切换音频通路,这与普通的kcontrol做法基本一致;2是使能dapm widget(power up/down),这就是分歧之处。
回到<三、AUDIO PATHS代码实现>复习一下"Left Input Mixer Switch"的kcontrol写法:SOC_DAPM_SINGLE("Left Input Mixer Switch", WM8900_REG_BYPASS1, 7, 1, 0)。
1、SOC_DAPM_SINGLE宏定义:
SOC_SINGLE宏定义:
从这里就可以看出,dapm kcontrol跟普通的kcontrol不同之处,以put为例:
dapm kcontrol:.put = snd_soc_dapm_put_volsw
kcontrol:.put = snd_soc_put_volsw
2、snd_soc_dapm_put_volsw
这是底层方法差异,往上应该没必要说了,与snd_kcontrol探究一致。