Linux音频ALSA机制学习笔记<二>
首先是dapm是什么?就是音频电源动态管理。相信电源管理大家都不会陌生。dapm设计的目的就是只有需要时才打开必要的部件(widget),不需要时则关闭部件, 达到省电的目的。ALSA通过kcontrol来切换音频通道,当playback或者capture时会更新通道激活下的widget power,这个是由内核event统一完成的,无须上层干预。widget定义widgets
首先是dapm是什么?就是音频电源动态管理。相信电源管理大家都不会陌生。dapm设计的目的就是只有需要时才打开
必要的部件(widget),不需要时则关闭部件,达到省电的目的。ALSA通过kcontrol来切换音频通道,当playback或
者capture时会更新通道激活下的widgetpower,这个是由内核event统一完成的,无须上层干预。
<一>widget
定义widget
staticconst struct snd_soc_dapm_widget tabla_dapm_widgets[] = {
----------
SND_SOC_DAPM_OUTPUT("EAR"),
SND_SOC_DAPM_PGA_E("EARPA", SND_SOC_NOPM, 0, 0, NULL,
0,tabla_ear_pa_event, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_MIXER("DAC1",SND_SOC_NOPM, 0, 0, dac1_switch,
ARRAY_SIZE(dac1_switch)),
-----------
}
add widget
int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm,
conststruct snd_soc_dapm_widget *widget,
intnum)
{
inti, ret;
for(i = 0; i < num; i++) {
ret= snd_soc_dapm_new_control(dapm, widget);
if(ret < 0) {
dev_err(dapm->dev,
"ASoC:Failed to create DAPM control %s: %d\n",
widget->name,ret);
returnret;
}
widget++;
}
return0;
}
对widget初始化,之前看见的widget里面的很多的链表都是在这里初始化的,dapm机制链表很多很复杂,power_check
在updatepower时会调到。
intsnd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
conststruct snd_soc_dapm_widget *widget)
{
structsnd_soc_dapm_widget *w;
size_tname_len;
if((w = dapm_cnew_widget(widget)) == NULL)
return-ENOMEM;
name_len= strlen(widget->name) + 1;
if(dapm->codec && dapm->codec->name_prefix)
name_len+= 1 + strlen(dapm->codec->name_prefix);
w->name= kmalloc(name_len, GFP_KERNEL);
if(w->name == NULL) {
kfree(w);
return-ENOMEM;
}
if(dapm->codec && dapm->codec->name_prefix)
snprintf(w->name,name_len, "%s %s",
dapm->codec->name_prefix,widget->name);
else
snprintf(w->name,name_len, "%s", widget->name);
switch(w->id) {
casesnd_soc_dapm_switch:
casesnd_soc_dapm_mixer:
casesnd_soc_dapm_mixer_named_ctl:
w->power_check= dapm_generic_check_power;
break;
casesnd_soc_dapm_mux:
casesnd_soc_dapm_virt_mux:
casesnd_soc_dapm_value_mux:
w->power_check= dapm_generic_check_power;
break;
casesnd_soc_dapm_adc:
casesnd_soc_dapm_aif_out:
w->power_check= dapm_adc_check_power;
break;
casesnd_soc_dapm_dac:
casesnd_soc_dapm_aif_in:
w->power_check= dapm_dac_check_power;
break;
casesnd_soc_dapm_pga:
casesnd_soc_dapm_out_drv:
casesnd_soc_dapm_input:
casesnd_soc_dapm_output:
casesnd_soc_dapm_micbias:
casesnd_soc_dapm_spk:
casesnd_soc_dapm_hp:
casesnd_soc_dapm_mic:
casesnd_soc_dapm_line:
w->power_check= dapm_generic_check_power;
break;
casesnd_soc_dapm_supply:
w->power_check= dapm_supply_check_power;
break;
default:
w->power_check= dapm_always_on_check_power;
break;
}
dapm->n_widgets++;
w->dapm= dapm;
w->codec= dapm->codec;
w->platform= dapm->platform;
w->dai= dapm->dai;
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);
/*machine layer set ups unconnected pins and insertions */
w->connected= 1;
return0;
}
<二>route与path
对snd_soc_dapm_route的定义,sink为dest目的端,control为bridge桥梁作用,source为src源端,当control
为NULL时默认route是通的,有control时,必须打通control,route才通。具体如何实现后面接着看。
structsnd_soc_dapm_route {
constchar *sink;
constchar *control;
constchar *source;
};
staticconst struct snd_soc_dapm_route audio_map[] = {
----------
{"SLIMTX1", NULL, "SLIM TX1 MUX"},
{"SLIMTX3 MUX", "DEC3", "DEC3 MUX"},
{"SLIMTX3 MUX", "RMIX1", "RX1 MIX1"},
{"HPHL_PA_MIXER","AUX_PGA_L Switch", "AUX_PGA_Left"},
-----------
}
形成以下路径:
SLIMTX1 <----------- SLIM TX1 MUX
SLIMTX3 MUX <------------ DEC3 <---------- DEC3 MUX
SLIMTX3 MUX <------------ RMIX1 <-------- RX1 MIX1
SLIMTX3 MUX <------------ RMIX2 <-------- RX2 MIX2
HPHL_PA_MIXER<--------- AUX_PGA_L Switch <-------- AUX_PGA_Left
再看下control的命名规则:
1:对于dest为mixer类的widget,routecontrol name为destwidget下的kcontrolname。
2:对于dest为mux类的widget,routecontron name为destwidget下enum下的text数组里面任一个。
其实name是根据codec内部Signal-pathDiagram来命名的,软件path通路与codec内部Signal-path是对应的。
intsnd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_route *route, int num)
{
inti, ret;
for(i = 0; i < num; i++) {
ret= snd_soc_dapm_add_route(dapm, route);
if(ret < 0) {
dev_err(dapm->dev,"Failed to add route %s->%s\n",
route->source,route->sink);
returnret;
}
route++;
}
return0;
}
snd_soc_dapm_add_route函数:
staticint snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_route *route)
{
structsnd_soc_dapm_path *path;
structsnd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;
structsnd_soc_dapm_widget *wtsource = NULL, *wtsink = NULL;
constchar *sink;
constchar *control = route->control;
constchar *source;
charprefixed_sink[80];
charprefixed_source[80];
intret = 0;
//这里我们走的是else
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;
}
//通过source与sink的name来遍历widgets链表,来找到与name匹配的widget,当任何一个都找不到时就return,
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;
//这里已经找到了src与sinkwidget,申请snd_soc_dapm_path内存。
path= kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);
if(!path)
return-ENOMEM;
//初始化path
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;
}
//当crotrol为NULL,path默认为连接状态
/*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;
return0;
}
/*connect dynamic paths */
switch(wsink->id) {
//widget为以下类型时默认是连通的状态
casesnd_soc_dapm_adc:
casesnd_soc_dapm_dac:
casesnd_soc_dapm_pga:
casesnd_soc_dapm_out_drv:
casesnd_soc_dapm_input:
casesnd_soc_dapm_output:
casesnd_soc_dapm_siggen:
casesnd_soc_dapm_micbias:
casesnd_soc_dapm_vmid:
casesnd_soc_dapm_pre:
casesnd_soc_dapm_post:
casesnd_soc_dapm_supply:
casesnd_soc_dapm_aif_in:
casesnd_soc_dapm_aif_out:
list_add(&path->list,&dapm->card->paths);
list_add(&path->list_sink,&wsink->sources);
list_add(&path->list_source,&wsource->sinks);
path->connect= 1;
return0;
//关于mux、switch、mixer类widget,dapm_connect_mux与dapm_connect_mixer下面再分析
casesnd_soc_dapm_mux:
casesnd_soc_dapm_virt_mux:
casesnd_soc_dapm_value_mux:
ret= dapm_connect_mux(dapm, wsource, wsink, path, control,
&wsink->kcontrol_news[0]);
if(ret != 0)
gotoerr;
break;
casesnd_soc_dapm_switch:
casesnd_soc_dapm_mixer:
casesnd_soc_dapm_mixer_named_ctl:
ret= dapm_connect_mixer(dapm, wsource, wsink, path, control);
if(ret != 0)
gotoerr;
break;
//以下默认为不连通状态
casesnd_soc_dapm_hp:
casesnd_soc_dapm_mic:
casesnd_soc_dapm_line:
casesnd_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;
return0;
}
return0;
err:
dev_warn(dapm->dev,"asoc: no dapm match for %s --> %s --> %s\n",
source, control, sink);
kfree(path);
returnret;
}
//通过route里面的name来匹配dest里面的kcontrolname,这里用到了上面的routecontrol name的匹配,若找到则初
//始化path里面的链表,name为widget里面的kcontrolname同时也为route里面的controlname。
/*connect mixer widget to its interconnecting audio paths */
staticint dapm_connect_mixer(struct snd_soc_dapm_context *dapm,
structsnd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
structsnd_soc_dapm_path *path, const char *control_name)
{
inti;
/*search for mixer kcontrol */
for(i = 0; i < dest->num_kcontrols; i++) {
if(!strcmp(control_name, dest->kcontrol_news[i].name)) {
list_add(&path->list,&dapm->card->paths);
list_add(&path->list_sink,&dest->sources);
list_add(&path->list_source,&src->sinks);
path->name= dest->kcontrol_news[i].name;
dapm_set_path_status(dest,path, i);
return0;
}
}
return-ENODEV;
}
//而muxwidget则是通过enum下的text数组里面的名字来匹配的,这与之前的route的control命名规则也是匹配的
/*connect mux widget to its interconnecting audio paths */
staticint dapm_connect_mux(struct snd_soc_dapm_context *dapm,
structsnd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
structsnd_soc_dapm_path *path, const char *control_name,
conststruct snd_kcontrol_new *kcontrol)
{
structsoc_enum *e = (struct soc_enum *)kcontrol->private_value;
inti;
for(i = 0; i < e->max; i++) {
if(!(strcmp(control_name, snd_soc_get_enum_text(e, i)))) {
list_add(&path->list,&dapm->card->paths);
list_add(&path->list_sink,&dest->sources);
list_add(&path->list_source,&src->sinks);
path->name= (char*)snd_soc_get_enum_text(e, i);
dapm_set_path_status(dest,path, 0);
return0;
}
}
return-ENODEV;
}
//设置初始化的codecpath状态,
/*set up initial codec paths */
staticvoid dapm_set_path_status(struct snd_soc_dapm_widget *w,
structsnd_soc_dapm_path *p, int i)
{
switch(w->id) {
casesnd_soc_dapm_switch:
casesnd_soc_dapm_mixer_named_ctl: {
intval;
structsoc_mixer_control *mc = (struct soc_mixer_control *)
w->kcontrol_news[i].private_value;
unsignedint reg = mc->reg;
unsignedint shift = mc->shift;
intmax = mc->max;
unsignedint mask = (1 << fls(max)) - 1;
unsignedint invert = mc->invert;
val= soc_widget_read(w, reg);
val= (val >> shift) & mask;
if((invert && !val) || (!invert && val))
p->connect= 1;
else
p->connect= 0;
}
break;
casesnd_soc_dapm_mux: {
structsoc_enum *e = (struct soc_enum *)
w->kcontrol_news[i].private_value;
intval, item, bitmask;
for(bitmask = 1; bitmask < e->max; bitmask <<= 1)
;
val= soc_widget_read(w, e->reg);
item= (val >> e->shift_l) & (bitmask - 1);
p->connect= 0;
for(i = 0; i < e->max; i++) {
if(!(strcmp(p->name, snd_soc_get_enum_text(e, i))) && item== i)
p->connect= 1;
}
}
break;
casesnd_soc_dapm_virt_mux: {
structsoc_enum *e = (struct soc_enum *)
w->kcontrol_news[i].private_value;
p->connect= 0;
/*since a virtual mux has no backing registers to
* decide which path to connect, it will try to match
* with the first enumeration. This is to ensure
* that the default mux choice (the first) will be
* correctly powered up during initialization.
*/
if(!strcmp(p->name, snd_soc_get_enum_text(e, 0)))
p->connect= 1;
}
break;
casesnd_soc_dapm_value_mux: {
structsoc_enum *e = (struct soc_enum *)
w->kcontrol_news[i].private_value;
intval, item;
val= soc_widget_read(w, e->reg);
val= (val >> e->shift_l) & e->mask;
for(item = 0; item < e->max; item++) {
if(val == e->values[item])
break;
}
p->connect= 0;
for(i = 0; i < e->max; i++) {
if(!(strcmp(p->name, snd_soc_get_enum_text(e, i))) && item== i)
p->connect= 1;
}
}
break;
/*does not affect routing - always connected */
casesnd_soc_dapm_pga:
casesnd_soc_dapm_out_drv:
casesnd_soc_dapm_output:
casesnd_soc_dapm_adc:
casesnd_soc_dapm_input:
casesnd_soc_dapm_siggen:
casesnd_soc_dapm_dac:
casesnd_soc_dapm_micbias:
casesnd_soc_dapm_vmid:
casesnd_soc_dapm_supply:
casesnd_soc_dapm_aif_in:
casesnd_soc_dapm_aif_out:
casesnd_soc_dapm_hp:
casesnd_soc_dapm_mic:
casesnd_soc_dapm_spk:
casesnd_soc_dapm_line:
p->connect= 1;
break;
/*does affect routing - dynamically connected */
casesnd_soc_dapm_pre:
casesnd_soc_dapm_post:
p->connect= 0;
break;
}
}
<三>DAPM-动态电源管理
先具体来分析下kcontrolput函数,下面以mixer类型的put函数分析
intsnd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
structsnd_ctl_elem_value *ucontrol)
{
structsnd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
structsnd_soc_dapm_widget *widget = wlist->widgets[0];
structsnd_soc_codec *codec = widget->codec;
structsoc_mixer_control *mc =
(structsoc_mixer_control *)kcontrol->private_value;
unsignedint reg = mc->reg;
unsignedint shift = mc->shift;
intmax = mc->max;
unsignedint mask = (1 << fls(max)) - 1;
unsignedint invert = mc->invert;
unsignedint val;
intconnect, change;
structsnd_soc_dapm_update update;
intwi;
val= (ucontrol->value.integer.value[0] & mask);//获取上层ioctl发送value也就item的值
if(invert)
val= max - val;
mask= mask << shift;
val= val << shift;
//通道val来设置path的connet状态
if(val)
/*new connection */
connect= invert ? 0 : 1;
else
/*old connection must be powered down */
connect= invert ? 1 : 0;
mutex_lock(&codec->mutex);
change= snd_soc_test_bits(widget->codec, reg, mask, val);
if(change) {
for(wi = 0; wi < wlist->num_widgets; wi++) {
widget= wlist->widgets[wi];
widget->value= val;
update.kcontrol= kcontrol;
update.widget= widget;
update.reg= reg;
update.mask= mask;
update.val= val;
widget->dapm->update= &update;
//下面再仔细分析updatepower
snd_soc_dapm_mixer_update_power(widget,kcontrol, connect);
widget->dapm->update= NULL;
}
}
mutex_unlock(&codec->mutex);
return0;
}
/*test and update the power status of a mixer or switch widget */
intsnd_soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
struct snd_kcontrol *kcontrol, int connect)
{
structsnd_soc_dapm_path *path;
intfound = 0;
if(widget->id != snd_soc_dapm_mixer &&
widget->id != snd_soc_dapm_mixer_named_ctl &&
widget->id != snd_soc_dapm_switch)
return-ENODEV;
//通过遍历snd_soc_card下面paths链表来找到pathname与kcontrolname匹配的path,同时设置path状态
/*find dapm widget path assoc with kcontrol */
list_for_each_entry(path,&widget->dapm->card->paths, list) {
if(path->kcontrol != kcontrol)
continue;
/*found, now check type */
found= 1;
path->connect= connect;
dapm_mark_dirty(path->source,"mixer connection");
}
if(found) {
if (widget->platform) {
soc_dpcm_runtime_update(widget);
} else {
dapm_mark_dirty(widget,"mixer update");
dapm_power_widgets(widget->dapm,SND_SOC_DAPM_STREAM_NOP);
}
}
return0;
}
//看看mux类widgetupdate power
intsnd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
struct snd_kcontrol *kcontrol, int change,
int mux, struct soc_enum *e)
{
structsnd_soc_dapm_path *path;
intfound = 0;
if(widget->id != snd_soc_dapm_mux &&
widget->id != snd_soc_dapm_virt_mux &&
widget->id != snd_soc_dapm_value_mux)
return-ENODEV;
if(!change)
return0;
/*find dapm widget path assoc with kcontrol */
list_for_each_entry(path,&widget->dapm->card->paths, list) {
//匹配kcontrol指针
if(path->kcontrol != kcontrol)
continue;
//判断2个name都必须存在
if(!path->name || !snd_soc_get_enum_text(e, mux))
continue;
//以上2个都pass
found= 1;
/*we now need to match the string in the enum to the path */
//pathname与enumtext数组其中一个名字必须匹配,path通路打通名字不匹配则关闭该path
//实例:'SLIMTX3 MUX':0:RMIX3连通'SLIMTX3 MUX':0:ZERO关闭
if(!(strcmp(path->name, snd_soc_get_enum_text(e, mux)))) {
path->connect= 1; /* new connection */
dapm_mark_dirty(path->source,"mux connection");
}else {
if(path->connect)
dapm_mark_dirty(path->source,
"muxdisconnection");
path->connect= 0; /* old connection must be powered down */
}
}
if(found) {
if(widget->platform) {
soc_dpcm_runtime_update(widget);
}else {
dapm_mark_dirty(widget, "mux change");
dapm_power_widgets(widget->dapm,
SND_SOC_DAPM_STREAM_NOP);
}
}
return0;
}
到现在为止kcontrol控制path->connect更新完成了,下面看看如何更新完整的path,以及如何对widget上下电
在上层完成所有kcontrol发送后就打开pcm设备。
首先看下widget的event触发的调用流程:
<----snd_pcm_playback_ioctl1
<-----snd_pcm_common_ioctl1
<-----snd_pcm_action_single
<-----snd_pcm_do_prepare
<-----soc_dpcm_fe_dai_prepare
<-----soc_pcm_prepare
<-----snd_soc_dapm_stream_event
<-----soc_dapm_stream_event
<-----dapm_power_widgets
<-----dapm_seq_run
<-----dapm_seq_run_coalesced
<-----dapm_seq_check_event
<------power_up或power_down链表中的widget接收event事件
/*
*Scan each dapm widget for complete audio path.
*A complete path is a route that has valid endpoints i.e.:-
*/
staticint dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
{
structsnd_soc_card *card = dapm->card;
structsnd_soc_dapm_widget *w;
structsnd_soc_dapm_context *d;
LIST_HEAD(up_list);
LIST_HEAD(down_list);
LIST_HEAD(async_domain);
enumsnd_soc_bias_level bias;
trace_snd_soc_dapm_start(card);
mutex_lock(&card->dapm_power_mutex);
list_for_each_entry(d,&card->dapm_list, list) {
if(d->n_widgets || d->codec == NULL) {
if(d->idle_bias_off)
d->target_bias_level= SND_SOC_BIAS_OFF;
else
d->target_bias_level= SND_SOC_BIAS_STANDBY;
}
}
memset(&card->dapm_stats,0, sizeof(card->dapm_stats));
list_for_each_entry(w,&card->widgets, list) {
w->power_checked= false;
w->inputs= -1;
w->outputs= -1;
}
/*Check which widgets we need to power and store them in
* lists indicating if they should be powered up or down. We
* only check widgets that have been flagged as dirty but note
* that new widgets may be added to the dirty list while we
* iterate.
*/
list_for_each_entry(w,&card->dapm_dirty, dirty) {
dapm_power_one_widget(w,&up_list, &down_list);
}
list_for_each_entry(w,&card->widgets, list) {
list_del_init(&w->dirty);
if(w->power) {
d= w->dapm;
/*Supplies and micbiases only bring the
* context up to STANDBY as unless something
* else is active and passing audio they
* generally don't require full power.
*/
switch(w->id) {
casesnd_soc_dapm_supply:
casesnd_soc_dapm_micbias:
if(d->target_bias_level < SND_SOC_BIAS_STANDBY)
d->target_bias_level= SND_SOC_BIAS_STANDBY;
break;
default:
d->target_bias_level= SND_SOC_BIAS_ON;
break;
}
}
}
/*If there are no DAPM widgets then try to figure out power from the
* event type.
*/
if(!dapm->n_widgets) {
switch(event) {
caseSND_SOC_DAPM_STREAM_START:
caseSND_SOC_DAPM_STREAM_RESUME:
dapm->target_bias_level= SND_SOC_BIAS_ON;
break;
caseSND_SOC_DAPM_STREAM_STOP:
if(dapm->codec && dapm->codec->active)
dapm->target_bias_level= SND_SOC_BIAS_ON;
else
dapm->target_bias_level= SND_SOC_BIAS_STANDBY;
break;
caseSND_SOC_DAPM_STREAM_SUSPEND:
dapm->target_bias_level= SND_SOC_BIAS_STANDBY;
break;
caseSND_SOC_DAPM_STREAM_NOP:
dapm->target_bias_level= dapm->bias_level;
break;
default:
break;
}
}
/*Force all contexts in the card to the same bias state if
* they're not ground referenced.
*/
bias= SND_SOC_BIAS_OFF;
list_for_each_entry(d,&card->dapm_list, list)
if(d->target_bias_level > bias)
bias= d->target_bias_level;
list_for_each_entry(d,&card->dapm_list, list)
if(!d->idle_bias_off)
d->target_bias_level= bias;
trace_snd_soc_dapm_walk_done(card);
/*Run all the bias changes in parallel */
list_for_each_entry(d,&dapm->card->dapm_list, list)
async_schedule_domain(dapm_pre_sequence_async,d,
&async_domain);
async_synchronize_full_domain(&async_domain);
/*Power down widgets first; try to avoid amplifying pops. */
dapm_seq_run(dapm,&down_list, event, false);
dapm_widget_update(dapm);
/*Now power up. */
dapm_seq_run(dapm,&up_list, event, true);
/*Run all the bias changes in parallel */
list_for_each_entry(d,&dapm->card->dapm_list, list)
async_schedule_domain(dapm_post_sequence_async,d,
&async_domain);
async_synchronize_full_domain(&async_domain);
pop_dbg(dapm->dev,card->pop_time,
"DAPMsequencing finished, waiting %dms\n", card->pop_time);
pop_wait(card->pop_time);
mutex_unlock(&card->dapm_power_mutex);
trace_snd_soc_dapm_done(card);
return0;
}
staticvoid dapm_power_one_widget(struct snd_soc_dapm_widget *w,
struct list_head *up_list,
struct list_head *down_list)
{
intpower;
switch(w->id) {
casesnd_soc_dapm_pre:
dapm_seq_insert(w,down_list, false);
break;
casesnd_soc_dapm_post:
dapm_seq_insert(w,up_list, true);
break;
default:
power= dapm_widget_power_check(w);
dapm_widget_set_power(w,power, up_list, down_list);
break;
}
}
staticint dapm_widget_power_check(struct snd_soc_dapm_widget *w)
{
if(w->power_checked)
returnw->new_power;
if(w->force)
w->new_power= 1;
else
w->new_power= w->power_check(w);//这里的power_check在widget注册时就赋值了通常为dapm_generic_check_power
w->power_checked= true;
returnw->new_power;
}
/*Generic check to see if a widget should be powered.
*/
staticint dapm_generic_check_power(struct snd_soc_dapm_widget *w)
{
intin, out;
DAPM_UPDATE_STAT(w,power_checks);
//判断该widget是否连接input端点
in= is_connected_input_ep(w, NULL);
dapm_clear_walk(w->dapm);
//判断该widget是否连接output端点
out= is_connected_output_ep(w, NULL);
dapm_clear_walk(w->dapm);
//处于完整通道上的widgetreturn 1
returnout != 0 && in != 0;
}
/*
*Recursively check for a completed path to an active or physicallyconnected
*input widget. Returns number of complete paths.
*/
staticint is_connected_input_ep(struct snd_soc_dapm_widget *widget,
structsnd_soc_dapm_widget_list **list)
{
structsnd_soc_dapm_path *path;
intcon = 0;
if(widget->inputs >= 0)
returnwidget->inputs;
DAPM_UPDATE_STAT(widget,path_checks);
if(widget->id == snd_soc_dapm_supply)
return0;
/*active stream ? */
switch(widget->id) {
casesnd_soc_dapm_dac:
casesnd_soc_dapm_aif_in:
if(widget->active) {
widget->inputs= snd_soc_dapm_suspend_check(widget);
returnwidget->inputs;
}
default:
break;
}
if(widget->connected) {
//处于输入端点则return
/*connected pin ? */
if(widget->id == snd_soc_dapm_input && !widget->ext) {
widget->inputs= snd_soc_dapm_suspend_check(widget);
returnwidget->inputs;
}
/*connected VMID/Bias for lower pops */
if(widget->id == snd_soc_dapm_vmid) {
widget->inputs= snd_soc_dapm_suspend_check(widget);
returnwidget->inputs;
}
/*connected jack ? */
if(widget->id == snd_soc_dapm_mic ||
(widget->id == snd_soc_dapm_line &&
!list_empty(&widget->sinks))) {
widget->inputs= snd_soc_dapm_suspend_check(widget);
returnwidget->inputs;
}
/*signal generator */
if(widget->id == snd_soc_dapm_siggen) {
widget->inputs= snd_soc_dapm_suspend_check(widget);
returnwidget->inputs;
}
}
//遍历sources链表path,若该path存在source同时处于连通状态则递归调用该函数,直到找到widget处于input端点
//或者path不连接状态return0;
list_for_each_entry(path,&widget->sources, list_sink) {
DAPM_UPDATE_STAT(widget,neighbour_checks);
if(path->source)
dev_vdbg(widget->dapm->dev,"%c : %s <- %s <- %s : %c\n",
path->source&& path->connect ? '*' : ' ',
widget->name,path->name, path->source->name,
path->weak? 'w': ' ');
if(path->weak)
continue;
if(path->walked)
continue;
if(path->source && path->connect) {
path->walked= 1;
/*do we need to add this widget to the list ? */
if(list) {
interr;
err= dapm_list_add_widget(list, path->sink);
if(err < 0) {
dev_err(widget->dapm->dev,"could not add widget %s\n",
widget->name);
returncon;
}
}
con+= is_connected_input_ep(path->source, list);
}
}
widget->inputs= con;
returncon;
}
staticvoid dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power,
struct list_head *up_list,
struct list_head *down_list)
{
structsnd_soc_dapm_path *path;
if(w->power == power)
return;
trace_snd_soc_dapm_widget_power(w,power);
/*If we changed our power state perhaps our neigbours changed
* also.
*/
list_for_each_entry(path,&w->sources, list_sink) {
if(path->source) {
dapm_widget_set_peer_power(path->source,power,
path->connect);
}
}
switch(w->id) {
casesnd_soc_dapm_supply:
/*Supplies can't affect their outputs, only their inputs */
break;
default:
list_for_each_entry(path,&w->sinks, list_source) {
if(path->sink) {
dapm_widget_set_peer_power(path->sink,power,
path->connect);
}
}
break;
}
//对于需要power的widget加入up_list,其他的加入down_list
//有需要是在这里可以打印up_list或者down_list的widget
if(power) {
dapm_seq_insert(w,up_list, true);
dev_dbg(w->dapm->dev,
"dapm:power up widget %s\n", w->name);
}else {
dapm_seq_insert(w,down_list, false);
dev_dbg(w->dapm->dev,
"dapm:power down widget %s\n", w->name);
}
w->power= power;
}
添加的打印的dmesg:
<7>[ 44.593994] tabla_codec tabla_codec: dapm: power up widget SLIM RX2
<7>[ 44.594055] tabla_codec tabla_codec: dapm: power up widget SLIM RX1
<7>[ 44.594116] tabla_codec tabla_codec: dapm: power up widget RX5 MIX1INP1
<7>[ 44.594177] tabla_codec tabla_codec: dapm: power up widget RX3 MIX1INP1
<7>[ 44.594268] tabla_codec tabla_codec: dapm: power up widget RX5 MIX1
<7>[ 44.594329] tabla_codec tabla_codec: dapm: power up widget RX3 MIX1
<7>[ 44.594390] tabla_codec tabla_codec: dapm: power up widget COMP2_CLK
<7>[ 44.594482] tabla_codec tabla_codec: dapm: power up widget RX6 DSMMUX
<7>[ 44.594543] tabla_codec tabla_codec: dapm: power up widget LINEOUT2DAC
<7>[ 44.594665] tabla_codec tabla_codec: dapm: power up widget RX3 MIX2
<7>[ 44.594787] tabla_codec tabla_codec: dapm: power up widget LINEOUT4DAC
<7>[ 44.594879] tabla_codec tabla_codec: dapm: power up widget RX_BIAS
<7>[ 44.594940] tabla_codec tabla_codec: dapm: power up widgetLINEOUT2_PA_MIXER
<7>[ 44.595062] tabla_codec tabla_codec: dapm: power up widget LINEOUT1DAC
<7>[ 44.595123] tabla_codec tabla_codec: dapm: power up widget RX4 DSMMUX
<7>[ 44.595214] tabla_codec tabla_codec: dapm: power up widgetLINEOUT4_PA_MIXER
<7>[ 44.595825] tabla_codec tabla_codec: dapm: power up widget MCLK
<7>[ 44.595886] tabla_codec tabla_codec: dapm: power up widget LINEOUT2PA
<7>[ 44.595947] tabla_codec tabla_codec: dapm: power up widgetLINEOUT1_PA_MIXER
<7>[ 44.596008] tabla_codec tabla_codec: dapm: power up widget LINEOUT3DAC
更多推荐
所有评论(0)