\rol %s\\n\,
widget->name); ret = -ENOMEM; break; } widget++; }
......
return ret; }
该函数只是简单的一个循环,为传入的widget模板数组依次调用
snd_soc_dapm_new_control函数,实际的工作由snd_soc_dapm_new_control完成,继续进入该函数,看看它做了那些工作。
我们之前已经说过,驱动中定义的snd_soc_dapm_widget数组,只是作为一个模板,所以,snd_soc_dapm_new_control所做的第一件事,就是为该widget重新分配内存,并把模板的内容拷贝过来:
[cpp] view plaincopy
static struct snd_soc_dapm_widget *
snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_widget *widget) {
struct snd_soc_dapm_widget *w; int ret;
if ((w = dapm_cnew_widget(widget)) == NULL) return NULL;
由dapm_cnew_widget完成内存申请和拷贝模板的动作。接下来,根据widget的类型做不同的处理: [cpp] view plaincopy
switch (w->id) {
case snd_soc_dapm_regulator_supply:
w->regulator = devm_regulator_get(dapm->dev, w->name);
......
if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) {
ret = regulator_allow_bypass(w->regulator, true);
......
41
}
break;
case snd_soc_dapm_clock_supply: #ifdef CONFIG_CLKDEV_LOOKUP
w->clk = devm_clk_get(dapm->dev, w->name); ...... #else
return NULL; #endif
break; default:
break; }
对于snd_soc_dapm_regulator_supply类型的widget,根据widget的名称获取对应的regulator结构,对于snd_soc_dapm_clock_supply类型的widget,根据widget的名称,获取对应的clock结构。接下来,根据需要,在widget的名称前加入必要的前缀:
[cpp] view plaincopy widget的power_check回调函数 widget类型 power_check回调函数 mixer类: snd_soc_dapm_switch dapm_generic_check_power snd_soc_dapm_mixer snd_soc_dapm_mixer_named_ctl mux类: snd_soc_dapm_mux snd_soc_dapm_mux snd_soc_dapm_mux snd_soc_dapm_dai_out snd_soc_dapm_dai_in 端点类: snd_soc_dapm_adc snd_soc_dapm_aif_out snd_soc_dapm_dac snd_soc_dapm_aif_in snd_soc_dapm_pga snd_soc_dapm_out_drv snd_soc_dapm_input snd_soc_dapm_output snd_soc_dapm_micbias dapm_generic_check_power dapm_adc_check_power dapm_dac_check_power dapm_generic_check_power 42
snd_soc_dapm_spk snd_soc_dapm_hp snd_soc_dapm_mic snd_soc_dapm_line snd_soc_dapm_dai_link 电源/时钟/影子widget: snd_soc_dapm_supply snd_soc_dapm_regulator_supply dapm_supply_check_power snd_soc_dapm_clock_supply snd_soc_dapm_kcontrol dapm_always_on_check_power 其它类型 if (dapm->codec && dapm->codec->name_prefix) w->name = kasprintf(GFP_KERNEL, \,
dapm->codec->name_prefix, widget->name); else
w->name = kasprintf(GFP_KERNEL, \, widget->name); 然后,为不同类型的widget设置合适的power_check电源状态回调函数,widget类型和对应的power_check回调函数设置如下表所示:
当音频路径发生变化时,power_check回调会被调用,用于检查该widget的电源状态是否需要更新。power_check设置完成后,需要设置widget所属的codec、platform和dapm context,几个用于音频路径的链表也需要初始化,然后,把该widget加入到声卡的widgets链表中: [cpp] view plaincopy w->dapm = dapm;
w->codec = dapm->codec;
w->platform = dapm->platform; INIT_LIST_HEAD(&w->sources); INIT_LIST_HEAD(&w->sinks); INIT_LIST_HEAD(&w->list); INIT_LIST_HEAD(&w->dirty);
list_add(&w->list, &dapm->card->widgets); 几个链表的作用如下:
sources 用于链接所有连接到该widget输入端的snd_soc_path结构 sinks 用于链接所有连接到该widget输出端的snd_soc_path结构 list 用于链接到声卡的widgets链表
dirty 用于链接到声卡的dapm_dirty链表 最后,把widget设置为connect状态: [cpp] view plaincopy
/* machine layer set ups unconnected pins and insertions */ w->connected = 1; return w;
43
connected字段代表着引脚的连接状态,目前,只有以下这些widget使用connected字段:
snd_soc_dapm_output snd_soc_dapm_input snd_soc_dapm_hp snd_soc_dapm_spk snd_soc_dapm_line snd_soc_dapm_vmid snd_soc_dapm_mic snd_soc_dapm_siggen
驱动程序可以使用以下这些api来设置引脚的连接状态: snd_soc_dapm_enable_pin
snd_soc_dapm_force_enable_pin snd_soc_dapm_disable_pin snd_soc_dapm_nc_pin
到此,widget已经被正确地创建并初始化,而且被挂在声卡的widgets链表中,以后我们就可以通过声卡的widgets链表来遍历所有的widget,再次强调一下snd_soc_dapm_new_controls函数所完成的主要功能:
为widget分配内存,并拷贝参数中传入的在驱动中定义好的模板 设置power_check回调函数
把widget挂在声卡的widgets链表中 为widget建立dapm kcontrol
定义一个widget,我们需要指定两个很重要的内容:一个是用于控制widget的电源状态的reg/shift等寄存器信息,另一个是用于控制音频路径切换的dapm kcontrol信息,这些dapm kcontrol有它们自己的reg/shift寄存器信息用于切换widget的路径连接方式。前一节的内容中,我们只是创建了widget的实例,并把它们注册到声卡的widgts链表中,但是到目前为止,包含在widget中的dapm kcontrol并没有建立起来,dapm框架在声卡的初始化阶段,等所有的widget(包括machine、platform、codec)都创建好之后,通过
snd_soc_dapm_new_widgets函数,创建widget内包含的dapm kcontrol,并初始化widget的初始电源状态和音频路径的初始连接状态。我们看看声卡的初始化函数,都有那些初始化与dapm有关: [cpp] view plaincopy
static int snd_soc_instantiate_card(struct snd_soc_card *card) {
......
/* card bind complete so register a sound card */
ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
card->owner, 0, &card->snd_card); ......
44
card->dapm.bias_level = SND_SOC_BIAS_OFF; card->dapm.dev = card->dev; card->dapm.card = card;
list_add(&card->dapm.list, &card->dapm_list);
#ifdef CONFIG_DEBUG_FS
snd_soc_dapm_debugfs_init(&card->dapm, card->debugfs_card_root); #endif
......
if (card->dapm_widgets) /* 创建machine级别的widget */
snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets,
card->num_dapm_widgets); ......
snd_soc_dapm_link_dai_widgets(card); /* 连接dai widget */
if (card->controls) /* 建立machine级别的普通kcontrol控件 */
snd_soc_add_card_controls(card, card->controls, card->num_controls);
if (card->dapm_routes) /* 注册machine级别的路径连接信息 */
snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,
card->num_dapm_routes); ......
if (card->fully_routed) /* 如果该标志被置位,自动把codec中没有路径连接信息的引脚设置为无用widget */ list_for_each_entry(codec, &card->codec_dev_list, card_list)
snd_soc_dapm_auto_nc_codec_pins(codec);
snd_soc_dapm_new_widgets(card); /*初始化widget包含的dapm kcontrol、电源状态和连接状态*/
ret = snd_card_register(card->snd_card); ......
card->instantiated = 1;
45