snd_soc_dapm_sync(&card->dapm); ...... return 0; }
正如我添加的注释中所示,在完成machine级别的widget和route处理之后,调用的snd_soc_dapm_new_widgets函数,来为所有已经注册的widget初始化他们所包含的dapm kcontrol,并初始化widget的电源状态和路径连接状态。下面我们看看snd_soc_dapm_new_widgets函数的工作过程。 snd_soc_dapm_new_widgets函数
该函数通过声卡的widgets链表,遍历所有已经注册了的widget,其中的new字段用于判断该widget是否已经执行过snd_soc_dapm_new_widgets函数,如果num_kcontrols字段有数值,表明该widget包含有若干个dapm kcontrol,那么就需要为这些kcontrol分配一个指针数组,并把数组的首地址赋值给widget的kcontrols字段,该数组存放着指向这些kcontrol的指针,当然现在这些都是空指针,因为实际的kcontrol现在还没有被创建: [cpp] view plaincopy
int snd_soc_dapm_new_widgets(struct snd_soc_card *card) {
......
list_for_each_entry(w, &card->widgets, list) {
if (w->new) continue;
if (w->num_kcontrols) {
w->kcontrols = kzalloc(w->num_kcontrols *
sizeof(struct snd_kcontrol *),
GFP_KERNEL); ...... }
接着,对几种能影响音频路径的widget,创建并初始化它们所包含的dapm kcontrol:
[cpp] view plaincopy switch(w->id) {
case snd_soc_dapm_switch: case snd_soc_dapm_mixer:
case snd_soc_dapm_mixer_named_ctl: dapm_new_mixer(w); break;
case snd_soc_dapm_mux:
case snd_soc_dapm_virt_mux:
46
case snd_soc_dapm_value_mux: dapm_new_mux(w); break;
case snd_soc_dapm_pga:
case snd_soc_dapm_out_drv: dapm_new_pga(w); break; default:
break; }
需要用到的创建函数分别是:
dapm_new_mixer() 对于mixer类型,用该函数创建dapm kcontrol; dapm_new_mux() 对于mux类型,用该函数创建dapm kcontrol; dapm_new_pga() 对于pga类型,用该函数创建dapm kcontrol;
然后,根据widget寄存器的当前值,初始化widget的电源状态,并设置到power字段中:
[cpp] view plaincopy
/* Read the initial power state from the device */ if (w->reg >= 0) {
val = soc_widget_read(w, w->reg) >> w->shift; val &= w->mask;
if (val == w->on_val) w->power = 1; }
接着,设置new字段,表明该widget已经初始化完成,我们还要吧该widget加入到声卡的dapm_dirty链表中,表明该widget的状态发生了变化,稍后在合适的时刻,dapm框架会扫描dapm_dirty链表,统一处理所有已经变化的widget。为什么要统一处理?因为dapm要控制各种widget的上下电顺序,同时也是为了减少寄存器的读写次数(多个widget可能使用同一个寄存器): [cpp] view plaincopy w->new = 1;
dapm_mark_dirty(w, \); dapm_debugfs_add_widget(w);
最后,通过dapm_power_widgets函数,统一处理所有位于dapm_dirty链表上的widget的状态改变: [cpp] view plaincopy
dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP); ...... return 0;
dapm mixer kcontrol
47
上一节中,我们提到,对于mixer类型的dapm kcontrol,我们会使用dapm_new_mixer来完成具体的创建工作,先看代码后分析: [cpp] view plaincopy
static int dapm_new_mixer(struct snd_soc_dapm_widget *w) {
int i, ret;
struct snd_soc_dapm_path *path;
/* add kcontrol */
(1) for (i = 0; i < w->num_kcontrols; i++) { /* match name */
(2) list_for_each_entry(path, &w->sources, list_sink) {
/* mixer/mux paths name must match control name */
(3) if (path->name != (char *)w->kcontrol_news[i].name)
continue;
(4) if (w->kcontrols[i]) {
dapm_kcontrol_add_path(w->kcontrols[i], path);
continue; }
(5) ret = dapm_create_or_share_mixmux_kcontrol(w, i);
if (ret < 0)
return ret;
(6) dapm_kcontrol_add_path(w->kcontrols[i], path); } }
return 0; }
(1) 因为一个mixer是由多个kcontrol组成的,每个kcontrol控制着mixer的一个输入端的开启和关闭,所以,该函数会根据kcontrol的数量做循环,逐个建立对应的kcontrol。
48
(2)(3) 之前多次提到,widget之间使用snd_soc_path进行连接,widget的sources链表保存着所有和输入端连接的snd_soc_path结构,所以我们可以用kcontrol模板中指定的名字来匹配对应的snd_soc_path结构。
(4) 因为一个输入脚可能会连接多个输入源,所以可能在上一个输入源的path关联时已经创建了这个kcontrol,所以这里判断kcontrols指针数组中对应索引中的指针值,如果已经赋值,说明kcontrol已经在之前创建好了,所以我们只要简单地把连接该输入端的path加入到kcontrol的path_list链表中,并且增加一个虚拟的影子widget,该影子widget连接和输入端对应的源widget,因为使用了
kcontrol本身的reg/shift等寄存器信息,所以实际上控制的是该kcontrol的开和关,这个影子widget只有在kcontrol的autodisable字段被设置的情况下才会被创建,该特性使得source的关闭时,与之连接的mixer的输入端也可以自动关闭,这个特性通过dapm_kcontrol_add_path来实现这一点: [cpp] view plaincopy
static void dapm_kcontrol_add_path(const struct snd_kcontrol *kcontrol,
struct snd_soc_dapm_path *path) {
struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);
/* 把kcontrol连接的path加入到paths链表中 */
/* paths链表所在的dapm_kcontrol_data结构会保存在kcontrol的private_data字段中 */
list_add_tail(&path->list_kcontrol, &data->paths);
if (data->widget) {
snd_soc_dapm_add_path(data->widget->dapm, data->widget,
path->source, NULL, NULL); } }
(5) 如果kcontrol之前没有被创建,则通过
dapm_create_or_share_mixmux_kcontrol创建这个输入端的kcontrol,同理,kcontrol对应的影子widget也会通过dapm_kcontrol_add_path判断是否需要创建。
dapm mux kcontrol
因为一个widget最多只会包含一个mux类型的damp kcontrol,所以他的创建方法稍有不同,dapm框架使用dapm_new_mux函数来创建mux类型的dapm kcontrol:
[cpp] view plaincopy
static int dapm_new_mux(struct snd_soc_dapm_widget *w) {
49
struct snd_soc_dapm_context *dapm = w->dapm; struct snd_soc_dapm_path *path; int ret;
(1) if (w->num_kcontrols != 1) { dev_err(dapm->dev,
\trols\\n\,
w->name); return -EINVAL; }
if (list_empty(&w->sources)) { dev_err(dapm->dev, \, w->name); return -EINVAL; }
(2) ret = dapm_create_or_share_mixmux_kcontrol(w, 0); if (ret < 0)
return ret;
(3) list_for_each_entry(path, &w->sources, list_sink) dapm_kcontrol_add_path(w->kcontrols[0], path); return 0; }
(1) 对于mux类型的widget,因为只会有一个kcontrol,所以在这里做一下判断。
(2) 同样地,和mixer类型一样,也使用
dapm_create_or_share_mixmux_kcontrol来创建这个kcontrol。
(3) 对每个输入端所连接的path都加入dapm_kcontrol_data结构的paths链表中,并且创建一个影子widget,用于支持autodisable特性。 dapm pga kcontrol
目前对于pga类型的widget,kcontrol的创建函数是个空函数,所以我们不用太关注它:
[cpp] view plaincopy
static int dapm_new_pga(struct snd_soc_dapm_widget *w) {
if (w->num_kcontrols)
dev_err(w->dapm->dev,
\\, w->name);
return 0;
50