目录
1. snd_soc_dapm_new_controls直接注册
3.1 soc_probe_link_components函数
在前面的文章《如何定义各种widget》介绍了各种widget的定义以及一种注册widget的方法,今天我们着重介绍widget的注册。widget本质上也是kcontrol,只不过添加了电源管理的部分。
1. snd_soc_dapm_new_controls直接注册
我们以sound\soc\codecs\wm8960.c为例。
static const struct snd_soc_dapm_widget wm8960_dapm_widgets[] = {
......
};
snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets,
ARRAY_SIZE(wm8960_dapm_widgets));
snd_soc_dapm_new_controls
snd_soc_dapm_new_control_unlocked
list_add_tail(&w->list, &dapm->card->widgets);
可以知道其最终会被添加到card->widgets链表之中。
2. snd_soc_register_codec间接注册
以sound\soc\codecs\es8328.c为例。
static const struct snd_soc_codec_driver es8328_codec_driver = {
.......
.component_driver = {
.controls = es8328_snd_controls,
.num_controls = ARRAY_SIZE(es8328_snd_controls),
.dapm_widgets = es8328_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(es8328_dapm_widgets),
.dapm_routes = es8328_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(es8328_dapm_routes),
},
};
return snd_soc_register_codec(dev,
&es8328_codec_driver, &es8328_dai, 1);
snd_soc_register_codec(dev, &es8328_codec_driver, &es8328_dai, 1);
ret = snd_soc_component_initialize(&codec->component,
&codec_drv->component_driver, dev); ----> component->driver = driver;
ret = snd_soc_register_dais(&codec->component, dai_drv, num_dai, false);
component->dai_drv = dai_drv;
dai = soc_add_dai(component, dai_drv + i,count == 1 && legacy_dai_naming);
struct snd_soc_dai *dai;
dai->component = component;
dai->driver = dai_drv;
list_add(&dai->list, &component->dai_list);
snd_soc_component_add_unlocked(&codec->component);
list_add(&component->list, &component_list);
最终es8328_codec_driver中的component_driver被幅值给dai->component的component_driver。这里面既包含kcontrol又包含widget。这个dai->component又最终被什么调用呢。
snd_soc_register_card
snd_soc_instantiate_card
struct snd_soc_pcm_runtime *rtd;
soc_bind_dai_link
struct snd_soc_pcm_runtime *rtd;
rtd = soc_new_pcm_runtime(card, dai_link);
snd_soc_rtdcom_add(rtd, rtd->cpu_dai->component);
snd_soc_rtdcom_add(rtd, codec_dais[i]->component);
snd_soc_rtdcom_add(rtd, component);
soc_add_pcm_runtime(card, rtd);
list_add_tail(&rtd->list, &card->rtd_list);
soc_probe_link_components
soc_probe_component
snd_soc_dapm_new_controls
snd_soc_dapm_new_control_unlocked
list_add_tail(&w->list, &dapm->card->widgets);
snd_soc_dapm_new_dai_widgets
snd_soc_dapm_new_control_unlocked
list_add_tail(&w->list, &dapm->card->widgets);
soc_probe_link_dais
snd_soc_dapm_new_pcm
snd_soc_dapm_new_control_unlocked
list_add_tail(&w->list, &dapm->card->widgets);
可以知道其最终会被添加到card->widgets链表之中。
大家肯定有一个疑问,普通的kcontrol和DAPM的kcontrol给应用程序的访问接口都是一样的,那么很显然DAPM的kcontrol(widgets中的kcontrol)最终都会放到card->controls(普通kcontrol)链表之中。
但是上面的分析,其并没有处理widgets中的kcontrol,把其放入到card->controls链表之中,那么肯定还存在其他的操作:
snd_soc_register_card
snd_soc_instantiate_card
snd_soc_dapm_new_widgets
3. 具体分析几个点
3.1 soc_probe_link_components函数
soc_probe_link_components
soc_probe_component
snd_soc_dapm_new_controls
snd_soc_dapm_new_control_unlocked
struct snd_soc_dapm_widget *
snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_widget *widget)
{
......
if ((w = dapm_cnew_widget(widget)) == NULL)
return NULL;
......
switch (w->id) {
case snd_soc_dapm_mic:
w->is_ep = SND_SOC_DAPM_EP_SOURCE;
w->power_check = dapm_generic_check_power;
break;
case snd_soc_dapm_input:
if (!dapm->card->fully_routed)
w->is_ep = SND_SOC_DAPM_EP_SOURCE;
w->power_check = dapm_generic_check_power;
break;
.......
}
w->dapm = dapm;
INIT_LIST_HEAD(&w->list);
INIT_LIST_HEAD(&w->dirty);
list_add_tail(&w->list, &dapm->card->widgets);
snd_soc_dapm_for_each_direction(dir) {
INIT_LIST_HEAD(&w->edges[dir]);
w->endpoints[dir] = -1;
}
/* machine layer sets up unconnected pins and insertions */
w->connected = 1;
return w;
}
soc_probe_link_components函数会对每一个widgets设置他的power_check = dapm_generic_check_power,该函数用来判断是否需要上电(上电条件:1.位于complete path上。2.有APP在使用声卡)。
3.2 snd_soc_dapm_new_widgets
int snd_soc_dapm_new_widgets(struct snd_soc_card *card)
{
struct snd_soc_dapm_widget *w;
unsigned int val;
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
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);
if (!w->kcontrols) {
mutex_unlock(&card->dapm_mutex);
return -ENOMEM;
}
}
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_demux:
dapm_new_mux(w);
break;
case snd_soc_dapm_pga:
case snd_soc_dapm_out_drv:
dapm_new_pga(w);
break;
case snd_soc_dapm_dai_link:
dapm_new_dai_link(w);
break;
default:
break;
}
/* Read the initial power state from the device */
if (w->reg >= 0) {
soc_dapm_read(w->dapm, w->reg, &val);
val = val >> w->shift;
val &= w->mask;
if (val == w->on_val)
w->power = 1;
}
w->new = 1;
dapm_mark_dirty(w, "new widget");
dapm_debugfs_add_widget(w);
}
dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP);
mutex_unlock(&card->dapm_mutex);
return 0;
}
3.2.1 mixer
可以看到通过三个开关,每个开关只要连接了都会进入mixer。我们想象一下三个开关对应的kcontrol我们在什么时候会使用到他,如果他的kcontrol_new位于某个path上,则使用kcontrol_new构造出son_kcontrol放入到card->controls链表中。
snd_soc_dapm_new_widgets(card);
case snd_soc_dapm_mixer://如果为混合器
dapm_new_mixer(w);
/*看一下widget中的kcontrol是否存在于一个path中*/
snd_soc_dapm_widget_for_each_source_path(w, path) {
/* mixer/mux paths name must match control name */
if (path->name != (char *)w->kcontrol_news[i].name)
continue
if (!w->kcontrols[i]) {
ret = dapm_create_or_share_kcontrol(w, i);
ret = snd_ctl_add(card, kcontrol);
list_add_tail(&kcontrol->list, &card->controls);
可以看到,其最终添加到了声卡的card->controls链表。
3.2.2 mux
其上这种情况,我们只需要一种寄存器若干位表示他就可以了,可以看出,其选择和之前的mixer相反,为一对对,mixer为多对1。其上这种情况,只需要一个kcontrol即可,mux连接是用值来确定的,如寄存器的值。mixer值通过字符串(name)。一个mux widgets包含了一个mux本身与一个snd_kcontrol_new。snd_kcontrol_new有多个取值,比如lift。right等。
如上图,两条绿线代表两条路径path1,与path1,那么他们之中的kcontrol为同一个kcontrol。
我们猜测一下,对mux widgets其会怎么做:
1.snd_kcontrol_new会转换为snd_kcontrol放入到链表之中,最后在放入到声卡的card->controls链表。
2.对于path1,path1他们指向同一个kcontrol,并且他们的sink(目的地)相同。
如下,我们任意查看一个:
static const struct snd_kcontrol_new rt5651_inl1_mux =
SOC_DAPM_ENUM("INL1 source", rt5651_inl_enum);
SND_SOC_DAPM_MUX("INL1 Mux", SND_SOC_NOPM, 0, 0, &rt5651_inl1_mux),
snd_soc_dapm_new_widgets(card);
case snd_soc_dapm_mux:
dapm_new_mux(w);
ret = dapm_create_or_share_kcontrol(w, 0);
ret = snd_ctl_add(card, kcontrol);
list_add_tail(&kcontrol->list, &card->controls);
可以看到其分析和之前是类似的。下面是个小中结:DAPM的kcontrol注册过程,
DAPM的kcontrol注册过程
a. 对于普通的snd_kcontrol:
snd_soc_add_controls : snd_kcontrol_new构造出snd_kcontrol, 放入card->controls链表
b. 对于DAPM的snd_kcontrol, 分2步:
b.1 snd_soc_dapm_new_controls // 把widget放入card->widgets链表
b.2 在注册machine驱动时, 导致如下调用:
soc_probe_dai_link > soc_post_component_init > snd_soc_dapm_new_widgets
snd_soc_dapm_new_widgets:
对于每一个widget, 设置它的power_check函数(用来判断该widget是否应该上电)
对于mixer widget, 取出其中的snd_kcontrol_new构造出snd_kcontrol, 放入card->controls链表
对于mux widget,它只有一个snd_kcontrol_new, 构造出snd_kcontrol, 放入card->controls链表