widget控件
所谓widget,其实可以理解为是kcontrol进一步升级和包装也是指音频系统中的某些部件,如mixer,mux,我们可以定义虚拟输入输出引脚、电源供应器等widget,例如playback stream widget。widget把kcontrol与动态电源管理有机结合,还具有音频路径连接功能,一个widget它可以相邻widget有一定的动态连接关系。在DAPM框架中,widget用结构体snd_soc_dapm_widget描述(部分):
struct snd_soc_dapm_widget { enum snd_soc_dapm_type id; const char *name; /* widget name */ ... /* dapm control */ int reg; /* negative reg = no direct dapm */ unsigned char shift; /* bits to shift */ unsigned int value; /* widget current value */ unsigned int mask; /* non-shifted mask */ ... int (*power_check)(struct snd_soc_dapm_widget *w); int (*event)(struct snd_soc_dapm_widget*, struct snd_kcontrol *, int); /* kcontrols that relate to this widget */ int num_kcontrols; const struct snd_kcontrol_new *kcontrol_news; struct snd_kcontrol **kcontrols; /* widget input and outputs */ struct list_head sources; struct list_head sinks; ... };
下面介绍各成员:
.id:该widget类型值,例如snd_soc_dapm_output,snd_soc_dapm_mixer等等 .name:该widget的名字 .sname:代表该widget所在stream比如对snd_soc_dapm_dai_in类型的widget,使用此字段 .codec .platform:指向该widget所属的codec和platform .list:在系统中注册的一切widget都会通过该list,链接代表声卡snd_soc_card结构的widgets链表头字段中 .dapm:snd_soc_dapm_context结构指针,ASoC将系统分为多个部分dapm域,每个widget属于某个dapm相同的域代表相同的偏置电压供电策略,如相同的域codec中的widget通常位于同一个dapm平台上的域widget它可能位于另一个位置dapm域中 .priv:有些widget可能需要一些专有数据,可以用这个字段来保存,比如snd_soc_dapm_dai_in类型的widget,会使用该字段来记住与之相关联的snd_soc_dai结构指针 .regulator:对于snd_soc_dapm_regulator_supply类型的widget,与之相关的字段指向regulator结构指针 .params:目前对于snd_soc_dapm_dai_link类型的widget,指向该dai配置信息 snd_soc_pcm_stream结构 .reg .shift .mask:这三个字段用于控制widget电源状态对应于控制信息所在的寄存器地址、位移值和屏蔽值 .value .on_val .off_val:电源状态的当前值,开关时对应的值 .power .invert:用于指示该widget目前是否处于上电状态,invert则用于表明power字段是否需要逻辑反转 .active .connected:分别表示该widget当与相邻状态相邻时,是否处于激活状态和连接状态widget有连接关系时,connected位置为1,否则位置为0 .new:我们定义的widget(snd_soc_dapm_widget结构),在注册到声卡中时需要进行实例化,该字段用来表示该widget是否实例化 .ext:表示该widget目前是否有外部连接,如连接mic,耳机、喇叭等 .force:该位置设置后,将被忽略widget当前状态强制更新到新的电源状态 .ignore_suspend new_power .power_checked:这些与电源管理相关的字段 .subseq:该widget为防止上下电过程中出现排序号,目前在上下电队列中pop-pop声,DAPM会给每个widget合理分配上下电顺序 .power_check:用于检查该widget回调函数指针应上电或下电吗? .event_flags:字段是一个位或字段,每个位代表widget会关注某个DAPM事件通知。只有关注的通知才会发送到widget回调函数中的事件处理 .event:DAPM事件处理回调函数指针 .num_kcontrols .kcontrol_news .kcontrols:这三个字段用于描述widget所包含的kcontrol例如一个控件mixer或者一个控件mux控件 .sources sinks:两个链表字段,两个widget如果有连接,将通过一个连接snd_soc_dapm_path连接结构,sources链表用于链接所有输入path,sinks链表用于链接所有输出path .power_list:每次更新整个dapm在电源状态下,所有的电源都会按照一定的算法扫描widget,然后需要改变电源状态widget将该字段链接到上电或下电的链表中,扫描后,dapm该系统将通过这两个链表执行相应的上下电操作 .dirty:链表字段,widget状态变化后,dapm该系统将使用该字段widget加入到一个dirty链表中,以后会对的dirty扫描链表以更新整个路径 .inputs:该widget连接到输入端的所有有效路径中的路径数 .outputs:该widget连接到输出端的所有有效路径的路径数 .clk:对于snd_soc_dapm_clock_supply类型的widget,指相关的clk结构体(指针)
widget的种类
在DAPM在框架中,各种不同的框架widget分为不同类型,可选类型定义为枚举:
/* dapm widget types */ enum snd_soc_dapm_type {...}
让我们逐一解释这些widget的种类:
snd_soc_dapm_input 该widget输入引脚对应。 snd_soc_dapm_output 该widget对应输出引脚。 snd_soc_dapm_mux 该widget对应一个mux控件。 snd_soc_dapm_virt_mux 该widget对应虚拟的mux控件。 snd_soc_dapm_value_mux 该widget对应一个value类型的mux控件。 snd_soc_dapm_mixer 该widget对应一个mixer控件。 snd_soc_dapm_mixer_named_ctl 该widget对应一个mixer控制,但对应kcontrol不会添加名称widget前缀名称。 snd_soc_dapm_pga 该widget对应一个pga控件(可编程增益控件)。 snd_soc_dapm_out_drv 该widget输出驱动控制器对应 snd_soc_dapm_adc 该widget对应一个ADC snd_soc_dapm_dac 该widget对应一个DAC snd_soc_dapm_micbias 该widget对应麦克风偏置电压控制 snd_soc_dapm_mic 该widget对应麦克风 snd_soc_dapm_hp 该widget对应耳机 snd_soc_dapm_spk 该widget对应一个扬声器 snd_soc_dapm_line 该widget对应线路输入 snd_soc_dapm_switch 该widget对应模拟开关 snd_soc_dapm_vmid 该widget对应一个codec的vmid偏置电压 snd_soc_dapm_pre machine级别的专用widget,会先于其它widget执行检查操作 snd_soc_dapm_post machine级别的专用widget,会后于其它widget执行检查操作 snd_soc_dapm_supply 对应一个电源或时钟源 snd_soc_dapm_regulator_supply 对应外部regulator稳压器 snd_soc_dapm_clock_supply 对应外部时钟源 snd_soc_dapm_aif_in 对应数字音频输入接口,如I2S接口的输入端 snd_soc_dapm_aif_out 对应数字音频输出接口,如I2S接口的输出端 snd_soc_dapm_siggen 对应信号发生器 snd_soc_dapm_dai_in 对应一个platform或codec域的输入DAI结构 snd_soc_dapm_dai_out 对应一个platform或codec域的输出DAI结构 snd_soc_dapm_dai_link 链接一对输入/输出DAI结构
widget的定义
和普通的kcontrol一样,DAPM框架为我们定义各种各样的辅助宏提供了大量的定义widget这些宏定义是基于widget根据它们的电源所在的域,分为几个域,它们是:
label>codec域 比如VREF和VMID等提供参考电压的widget,这些widget通常在codec的probe/remove回调中进行控制,当然,在工作中如果没有音频流时,也可以适当地进行控制它们的开启与关闭。
位于该域上的widget通常是针对平台或板子的一些需要物理连接的输入/输出接口,例如耳机、扬声器、麦克风,因为这些接口在每块板子上都可能不一样,所以通常它们是在machine驱动中进行定义和控制,并且也可以由用户空间的应用程序通过某种方式来控制它们的打开和关闭。
一般是指codec内部的mixer、mux等控制音频路径的widget,这些widget可以根据用户空间的设定连接关系,自动设定他们的电源状态。
是指那些需要处理音频数据流的widget,例如ADC、DAC等等。
举个例子
#define SND_SOC_DAPM_MIXER(wname, wreg, wshift, winvert, \
wcontrols, wncontrols)\
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
可以发现这个widget的reg和shift字段是需要赋值的,说明这个widget是有相应的电源控制寄存器的,DAPM框架在扫描和更新音频路径时,会利用这些寄存器来控制widget的电源状态,使得它们的供电状态是按需分配的,需要的时候(在有效的音频路径上)上电,不需要的时候(不再有效的音频路径上)下电。这些widget需要完成和之前介绍的mixer、mux等控件同样的功能,实际上,这是通过它们包含的kcontrol控件来完成的,这些kcontrol我们需要在定义widget前先定义好,然后通过wcontrols和num_kcontrols参数传递给这些辅助定义宏。 如果需要自定义这些widget的dapm事件处理回调函数,也可以使用下面这些带“_E”后缀的版本:
-
SND_SOC_DAPM_PGA_E
-
SND_SOC_DAPM_OUT_DRV_E
-
SND_SOC_DAPM_MIXER_E
-
SND_SOC_DAPM_MIXER_NAMED_CTL_E
-
SND_SOC_DAPM_SWITCH_E
-
SND_SOC_DAPM_MUX_E
-
SND_SOC_DAPM_VIRT_MUX_E
还有另外三种widget没有提供显式的定义方法,它们的种类id分别是:
-
snd_soc_dapm_dai_in
-
snd_soc_dapm_dai_out
-
snd_soc_dapm_dai_link
每个codec有多个dai,而cpu(通常就是指某个soc cpu芯片)也会有多个dai,dai注册时,dapm系统会为每个dai创建一个snd_soc_dapm_dai_in或snd_soc_dapm_dai_out类型的widget,通常,这两种widget会和codec中具有相同的stream name的widget进行连接。另外一种情况,当系统中具有多个音频处理器(比如多个codec)时,他们之间可能会通过某两个dai进行连接,当machine驱动确认有这种配置时(通过判断dai_links结构中的param字段),会为他们建立一个dai link把他们绑定在一起,因为有连接关系,两个音频处理器之间的widget的电源状态就可以互相传递。