该小节我们讲解route与path添加过程分析。会使用一些例子,把这个过程讲解清楚。
打开源码文件rt5651.c,根据之前的分析我们知道:
static const struct snd_soc_dapm_route rt5651_dapm_routes[] = {
......
{"IN1P", NULL, "LDO"},
{"RECMIXL", "BST1 Switch", "BST1"},
{"Stereo1 ADC L1 Mux", "DD MIX", "DD MIXL"},
......
}
static struct snd_soc_codec_driver soc_codec_dev_rt5651 = {
.dapm_routes = rt5651_dapm_routes,
}
snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5651,rt5651_dai, ARRAY_SIZE(rt5651_dai));
/*machin之后最终导致该函数被调用*/
static int snd_soc_instantiate_card(struct snd_soc_card *card)
soc_probe_link_components(card, i, order);
soc_probe_component(card, component);
snd_soc_dapm_add_routes(dapm, component->dapm_routes,component->num_dapm_routes);
snd_soc_dapm_add_route(dapm, route);
在继续分析snd_soc_dapm_add_route函数之前,我们先猜测一下。 大家肯定注意到了很多的snd_soc_dapm_route,都使用了NULL,如:
{"IN1P", NULL, "LDO"},
如果不是为NULL的
{"RECMIXR", "INR1 Switch", "INR1 VOL"},//MIX类型
{"Stereo1 ADC L1 Mux", "DD MIX", "DD MIXL"},//MUX类型
实际只有两种类型,及MIX类型与MUX类型(从命名我们可以看出)。左边的为sink widget,中间为kcontrol(如果不为NULL),右边为soure widget。
其中,通过上小节的学习我们知道kcontrol分为3种: 1.sink,NULL,soure:其表示他们肯定是连接在一起的,由他们构造出来的path,其成员connect一直为1。 2.sink widget为mixer,如:
{"RECMIXR", "INR1 Switch", "INR1 VOL"},
{"RECMIXR", "BST3 Switch", "BST3"},
{"RECMIXR", "BST2 Switch", "BST2"},
{"RECMIXR", "BST1 Switch", "BST1"},//MIX类型
其描述如下 可以看到这四个ptch他们的source是不一样的,但是他们的sink是一样的,都为RECMIL。从上我们知道一共需要4个kcontrol,四个为通道选择的kcontrol,分别对应寄存器MXC的3,2,1,5,位。并且这些kcontrol还会对RECMXL的寄存器MX3B进行设定。他们共同组成一个mixer widger。
3.sink widget为为MUX,即如sink为mux widget类型:
其中MUX widget包含了MUX本身信息,以及一个snd_kcontrol,我们设置snd_kcontrol为不同的值时,其导通不同的ptch。
从上面我们总结出了3中route,那么他是怎么转换真path的呢?
情景分析我们现在继续分析snd_soc_dapm_add_route(dapm, route);函数:
static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,const struct snd_soc_dapm_route *route)
/*通过名字寻找card->widgets链表中的所有的widgets,如果名字相同,代表找到了对应的widgets*/
list_for_each_entry(w, &dapm->card->widgets, list) {
widgets[SND_SOC_DAPM_DIR_IN] = wsource;
widgets[SND_SOC_DAPM_DIR_OUT] = wsink;
/*如果control=null*/
if (control == NULL) {
/*设置connect为1*/
path->connect = 1;
我们先做个简单的总结:
route{"sink","kcontrol","source"}
转化为path的过程: 1.找到sink,kcontrol,source 2.构造path。 3.如果需要设置connect则设置connect。 我们称kcontrol为NULL的path称为static path。如果kcontrol不为NULL则称为动态path。对于动态路径:
static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink,const char *control,int (*connected)(struct snd_soc_dapm_widget *source,struct snd_soc_dapm_widget *sink))
if (control == NULL) { //静态路径
path->connect = 1;
}
else
{
//根据不同的id去设置相应的状态,通过读取寄存器,再设置connect的状态
switch (wsource->id) {
case snd_soc_dapm_demux:
ret = dapm_connect_mux(dapm, path, control, wsource);
if (ret)
goto err;
break;
default:
break;
}
switch (wsink->id) {
case snd_soc_dapm_mux:
ret = dapm_connect_mux(dapm, path, control, wsink);
if (ret != 0)
goto err;
break;
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
case snd_soc_dapm_mixer_named_ctl:
ret = dapm_connect_mixer(dapm, path, control);
if (ret != 0)
goto err;
break;
default:
break;
}
}
其会根据寄存器实际的值,然后设置connect的值,如dapm_connect_mixer函数:
/* connect mixer widget to its interconnecting audio paths */
static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm,struct snd_soc_dapm_path *path, const char *control_name)
dapm_set_mixer_path_status(path, i);
soc_dapm_read(p->sink->dapm, reg, &val);
p->connect = !!val;
我们再次回到snd_soc_dapm_add_path函数:
static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink,const char *control,int (*connected)(struct snd_soc_dapm_widget *source,struct snd_soc_dapm_widget *sink))
/*把path放入链表*/
list_add(&path->list, &dapm->card->paths);
list_add(&path->list_node[dir], &widgets[dir]->edges[dir]);
添加到里面去,就可以通过dapm->card->paths查询所有的path了,其最终会形成多条complite path,这个在后续为大家讲解。
从上面的分析,我们知道snd_soc_dapm_add_route其没有设置kcontrol,其设置是在snd_soc_dapm_new_control_unlocked函数中,把widget添加到dapm->card->widgets,然后在通过dapm_create_or_share_kcontrol把widget中的kcontrol添加到card->controls之中,