Linux音频ALSA机制学习笔记



首先是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;

}


<>routepath


snd_soc_dapm_route的定义,sinkdest目的端,controlbridge桥梁作用,sourcesrc源端,当control


NULL时默认route是通,control时,必须打通controlroute才通。具体如何实现后面接着看。


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:对于destmixer类的widget,routecontrol namedestwidget下的kcontrolname


2:对于destmux类的widget,routecontron namedestwidgetenum下的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;

}


//通过sourcesinkname来遍历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;


//这里已经找到了srcsinkwidget,申请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;

}

//crotrolNULLpath默认为连接状态

/*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;

//关于muxswitchmixerwidgetdapm_connect_muxdapm_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里面的链表,namewidget里面的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数组里面的名字来匹配的,这与之前的routecontrol命名规则也是匹配的

/*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:

case snd_soc_dapm_mixer:

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来设置pathconnet状态

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链表来找到pathnamekcontrolname匹配的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;

}


//看看muxwidgetupdate 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;

//判断2name都必须存在

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 */

//pathnameenumtext数组其中一个名字必须匹配,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设备。

首先看下widgetevent触发的调用流程:

<----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_uppower_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_checkwidget注册时就赋值了通常为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;

}


//对于需要powerwidget加入up_list,其他的加入down_list

//有需要是在这里可以打印up_list或者down_listwidget

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



你可能感兴趣的:(驱动总结)