转载:http://blog.csdn.net/sepnic/article/details/6334922
备注:仅作个人学习资料保存使用,请移步原作者链接进行讨论。
在用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的路径选择。要打开红色通路,则进行如下操作:
- alsa_amixer cset numid=3,iface=MIXER,name='Left Input PGA Switch' 1
- alsa_amixer cset numid=73,iface=MIXER,name='Left Input PGA LINPUT1 Switch' 1
- alsa_amixer cset numid=69,iface=MIXER,name='Left Input Mixer Input PGA Switch' 1
- alsa_amixer cset numid=58,iface=MIXER,name='Left Output Mixer Left Input Mixer Switch' 1
- alsa_amixer cset numid=43,iface=MIXER,name='LINEOUT1 Switch' 1
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的输入源选择:
- static const struct snd_kcontrol_new wm8900_loutmix_controls[] = {
- SOC_DAPM_SINGLE("LINPUT3 Bypass Switch", WM8900_REG_LOUTMIXCTL1, 7, 1, 0),
- SOC_DAPM_SINGLE("AUX Bypass Switch", WM8900_REG_AUXOUT_CTL, 7, 1, 0),
- SOC_DAPM_SINGLE("Left Input Mixer Switch", WM8900_REG_BYPASS1, 7, 1, 0),
- SOC_DAPM_SINGLE("Right Input Mixer Switch", WM8900_REG_BYPASS2, 3, 1, 0),
- SOC_DAPM_SINGLE("DACL Switch", WM8900_REG_LOUTMIXCTL1, 8, 1, 0),
- };
2、添加名为Left Output Mixer的widget:
- static const struct snd_soc_dapm_widget wm8900_dapm_widgets[] = {
- /* Externally visible pins */
- ......省略......
- /* Input */
- ......省略......
- SND_SOC_DAPM_MIXER("Left Input Mixer", WM8900_REG_POWER2, 5, 0,
- wm8900_linmix_controls,
- ARRAY_SIZE(wm8900_linmix_controls)),
- ......省略......
- /* Output */
- ......省略......
- SND_SOC_DAPM_MIXER("Left Output Mixer", WM8900_REG_POWER3, 3, 0,
- wm8900_loutmix_controls,
- ARRAY_SIZE(wm8900_loutmix_controls)),
- ......省略......
- };
【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、搭建音频路径:
- static const struct snd_soc_dapm_route audio_map[] = {
- /* Inputs */
- ......省略......
- /* Outputs */
- ......省略......
- {"Left Output Mixer", "Left Input Mixer Switch", "Left Input Mixer"},
- ......省略......
- };
前面的"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更清晰一点:
- e.g., from the WM8731 output mixer (wm8731.c)
- The WM8731 output mixer has 3 inputs (sources)
- 1. Line Bypass Input
- 2. DAC (HiFi playback)
- 3. Mic Sidetone Input
- Each input in this example has a kcontrol associated with it (defined in example
- above) and is connected to the output mixer via it's kcontrol name. We can now
- connect the destination widget (wrt audio signal) with it's source widgets.
- /* output mixer */
- {"Output Mixer", "Line Bypass Switch", "Line Input"},
- {"Output Mixer", "HiFi Playback Switch", "DAC"},
- {"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},
- So we have :-
- Destination Widget <=== Path Name <=== Source Widget
- Or:-
- Sink, Path, Source
- Or :-
- "Output Mixer" is connected to the "DAC" via the "HiFi Playback Switch".
- When there is no path name connecting widgets (e.g. a direct connection) we
- pass NULL for the path name.
注意:dapm kcontrol名称 = 目的对象sink名称 + 操作行为control名称,即'Left Output Mixer Left Input Mixer Switch',操作源对象source名称被忽略。之后深入源码分析。
4、将kcontrols、widgets和route串联起来:
- static int wm8900_add_widgets(struct snd_soc_codec *codec)
- {
- snd_soc_dapm_new_controls(codec, wm8900_dapm_widgets,
- ARRAY_SIZE(wm8900_dapm_widgets));
- snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
- snd_soc_dapm_new_widgets(codec);
- return 0;
- }
四、dapm kcontrol
好了,如果仅仅是为了实现audio paths的话,了解以上应该是足够的了。但人生的追求不应该那么简单,我们要从更根源处剖析问题,以后再审视相似的问题时,站的高度也不同。
snd_soc_dapm_route的定义:
- /*
- * DAPM audio route definition.