本篇博客主要分析如何将audio_paths添加到内核中
首先看一下,audio_paths的定义:
static const struct snd_soc_dapm_route audio_paths[] = { { "Left Boost Mixer", "LINPUT1 Switch", "LINPUT1" }, { "Left Boost Mixer", "LINPUT2 Switch", "LINPUT2" }, { "Left Boost Mixer", "LINPUT3 Switch", "LINPUT3" }, { "Left Input Mixer", "Boost Switch", "Left Boost Mixer", }, { "Left Input Mixer", NULL, "LINPUT1", }, /* Really Boost Switch */ { "Left Input Mixer", NULL, "LINPUT2" }, { "Left Input Mixer", NULL, "LINPUT3" },
.....
};
在代码中,使用函数snd_soc_dapm_add_routes将route添加到内核中,如下所示:
snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths)); /** * snd_soc_dapm_add_routes - Add routes between DAPM widgets * @dapm: DAPM context * @route: audio routes * @num: number of routes * * Connects 2 dapm widgets together via a named audio path. The sink is * the widget receiving the audio signal, whilst the source is the sender * of the audio signal. * * Returns 0 for success else error. On error all resources can be freed * with a call to snd_soc_card_free(). */ int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route, int num) { int i, ret; for (i = 0; i < num; i++) { ret = snd_soc_dapm_add_route(dapm, route); ..... route++; } return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes);
这个函数的关键就是snd_soc_dapm_add_route,在分析该函数之前,我们首先看一下route的种类:
1)常规route
{"sink" NULL "source"}
表示由route构造出来的path->connect = 1
2)sink widget是Mixer
{"Mixer", name1, "source1"} {"Mixer", name2, "source2"} 即: source1----->name1------>Mixer source2----->name2------>Mixer
name1和name2代表两个snd_kcontrol_new,这两个kcontrol在Mixer widget中,该wiget含有:
a.Mixer本身的信息
b.2个snd_kcontrol_new: name1和name2
可以通过操作某个kcontrol来打开某条path
3)sink widget是Mux
{"Mux" value1, "source1"} {"Mux" value2, "source2"} source1 ---------value1 \ \ \-----------Mux source2 --------- value2
Mux widget含有:
a.Mux本身信息
b.1个snd_kcontrol_new,它有两种取值:value和value2
这三种route是如何转换成path的呢?
猜测:
1)首先要找出source widget和sink widget,至于kcontrol,这个地方不会去找。为什么?
上篇博客中已经介绍,widget中kcontrol的创建是在machine驱动中,而这个地方只是codec驱动,还没有创建kcontrol,因此它在这个地方是不会去找的。
2)构造path
path->source = wsource; path->sink = wsink;
3)设置
path->connect的值:
a.如果kcontrol = NULL,则它是一条static path,此时path->connect=1.
b.如果kcontrol !=NULL,则它是一条dynamic path,它的path->connect是如何设置呢?
这个地方是根据widget sink的类型决定的,
b.1 sink widget为ADC、DAC等的path,该path一般来说都是直连的(无开关)
snd_soc_dapm_adc snd_soc_dapm_dac snd_soc_dapm_pga snd_soc_dapm_out_drv snd_soc_dapm_input snd_soc_dapm_output snd_soc_dapm_siggen snd_soc_dapm_micbias snd_soc_dapm_vmid snd_soc_dapm_pre snd_soc_dapm_post snd_soc_dapm_supply snd_soc_dapm_regulator_supply snd_soc_dapm_aif_in snd_soc_dapm_aif_out snd_soc_dapm_dai //将path放入dapm->card->paths链表中 list_add(&path->list, &dapm->card->paths); //将path的sink放入到widget的sources链表中。如何理解: //在widget中,source就是source,sink就是sink。而在audio_routes,即形成的path中,格式是sink---->source. //因此path中的sink刚好对应的是widget中的source list_add(&path->list_sink, &wsink->sources); list_add(&path->list_source, &wsource->sinks); path->connect = 1;
b.2 对于sink widget是Mixer的path,path->connect 需要根据寄存器的值进行确定
case snd_soc_dapm_switch: case snd_soc_dapm_mixer: case snd_soc_dapm_mixer_named_ctl: ret = dapm_connect_mixer(dapm, wsource, wsink, path, control); dapm_set_path_status
b.3 对于sink widget是Mux的path,path->connect需要根据寄存器的值进行确定
case snd_soc_dapm_mux: case snd_soc_dapm_virt_mux: case snd_soc_dapm_value_mux: ret = dapm_connect_mux(dapm, wsource, wsink, path, control,&wsink->kcontrol_news[0]); dapm_set_path_status//读寄存器,通过读出来的值再去判断该path是否连通
b.4 对于sink widget可以动态拔插的headphone、mic、line、speaker,它默认将path->connect设置为0。以后再根据实际情况,将path->connect设置为1.
比如说,插上耳机,对应的path设置为1
case snd_soc_dapm_hp: case snd_soc_dapm_mic: case snd_soc_dapm_line: case snd_soc_dapm_spk: list_add(&path->list, &dapm->card->paths); list_add(&path->list_sink, &wsink->sources); list_add(&path->list_source, &wsource->sinks); path->connect = 0;
snd_soc_dapm_add_routes会导致path的创建,以及链表关系的形成,path->connect值的设置。但是注意一点:
Mux和Mixer中snd_kcontrol此时还没有被创建,snd_kcontrol的创建是在函数snd_soc_dapm_new_widgets中。
附:snd_soc_dapm_add_route源码
static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route) { struct snd_soc_dapm_path *path; struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w; struct snd_soc_dapm_widget *wtsource = NULL, *wtsink = NULL; const char *sink; const char *control = route->control; const char *source; char prefixed_sink[80]; char prefixed_source[80]; int ret = 0; if (dapm->codec && dapm->codec->name_prefix) { snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s", dapm->codec->name_prefix, route->sink); sink = prefixed_sink; snprintf(prefixed_source, sizeof(prefixed_source), "%s %s", dapm->codec->name_prefix, route->source); source = prefixed_source; } else { sink = route->sink; source = route->source; } /* * find src and dest widgets over all widgets but favor a widget from * current DAPM context */ list_for_each_entry(w, &dapm->card->widgets, list) { if (!wsink && !(strcmp(w->name, sink))) { wtsink = w; if (w->dapm == dapm) wsink = w; continue; } if (!wsource && !(strcmp(w->name, source))) { wtsource = w; if (w->dapm == dapm) wsource = w; } } /* use widget from another DAPM context if not found from this */ if (!wsink) wsink = wtsink; if (!wsource) wsource = wtsource; if (wsource == NULL || wsink == NULL) return -ENODEV; path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL); if (!path) return -ENOMEM; path->source = wsource; path->sink = wsink; path->connected = route->connected; INIT_LIST_HEAD(&path->list); INIT_LIST_HEAD(&path->list_source); INIT_LIST_HEAD(&path->list_sink); /* check for external widgets */ if (wsink->id == snd_soc_dapm_input) { if (wsource->id == snd_soc_dapm_micbias || wsource->id == snd_soc_dapm_mic || wsource->id == snd_soc_dapm_line || wsource->id == snd_soc_dapm_output) wsink->ext = 1; } if (wsource->id == snd_soc_dapm_output) { if (wsink->id == snd_soc_dapm_spk || wsink->id == snd_soc_dapm_hp || wsink->id == snd_soc_dapm_line || wsink->id == snd_soc_dapm_input) wsource->ext = 1; } /* connect static paths */ if (control == NULL) { list_add(&path->list, &dapm->card->paths); list_add(&path->list_sink, &wsink->sources); list_add(&path->list_source, &wsource->sinks); path->connect = 1; return 0; } /* connect dynamic paths */ switch (wsink->id) { case snd_soc_dapm_adc: case snd_soc_dapm_dac: case snd_soc_dapm_pga: case snd_soc_dapm_out_drv: case snd_soc_dapm_input: case snd_soc_dapm_output: case snd_soc_dapm_siggen: case snd_soc_dapm_micbias: case snd_soc_dapm_vmid: case snd_soc_dapm_pre: case snd_soc_dapm_post: case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: case snd_soc_dapm_aif_in: case snd_soc_dapm_aif_out: case snd_soc_dapm_dai: list_add(&path->list, &dapm->card->paths); list_add(&path->list_sink, &wsink->sources); list_add(&path->list_source, &wsource->sinks); path->connect = 1; return 0; case snd_soc_dapm_mux: case snd_soc_dapm_virt_mux: case snd_soc_dapm_value_mux: ret = dapm_connect_mux(dapm, wsource, wsink, path, control, &wsink->kcontrol_news[0]); 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, wsource, wsink, path, control); if (ret != 0) goto err; break; case snd_soc_dapm_hp: case snd_soc_dapm_mic: case snd_soc_dapm_line: case snd_soc_dapm_spk: list_add(&path->list, &dapm->card->paths); list_add(&path->list_sink, &wsink->sources); list_add(&path->list_source, &wsource->sinks); path->connect = 0; return 0; } return 0; err: dev_warn(dapm->dev, "asoc: no dapm match for %s --> %s --> %s\n", source, control, sink); kfree(path); return ret; }View Code