首页 > 代码库 > Linux音频ALSA机制学习笔记<二>

Linux音频ALSA机制学习笔记<二>


首先是dapm是什么?就是音频电源动态管理。相信电源管理大家都不会陌生。dapm设计的目的就是只有需要时才打开必要的部件
(widget),不需要时则关闭部件, 达到省电的目的。ALSA通过kcontrol来切换音频通道,当playback或者capture时会更新通道激活下
的widget power,这个是由内核event统一完成的,无须上层干预。
<一>widget
定义widget
static const struct snd_soc_dapm_widget tabla_dapm_widgets[] = {
----------
SND_SOC_DAPM_OUTPUT("EAR"),
SND_SOC_DAPM_PGA_E("EAR PA", 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,
const struct snd_soc_dapm_widget *widget,
int num)
{
int i, 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);
return ret;
}
widget++;
}r
eturn 0;
}
对widget初始化,之前看见的widget里面的很多的链表都是在这里初始化的,dapm机制链表很多很复杂 ,power_check在update power时
会调到。
int snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_widget *widget)
{
struct snd_soc_dapm_widget *w;
size_t name_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) {
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
case snd_soc_dapm_mixer_named_ctl:
w->power_check = dapm_generic_check_power;
break;
case snd_soc_dapm_mux:
case snd_soc_dapm_virt_mux:
case snd_soc_dapm_value_mux:
w->power_check = dapm_generic_check_power;
break;
case snd_soc_dapm_adc:
case snd_soc_dapm_aif_out:
w->power_check = dapm_adc_check_power;
break;
case snd_soc_dapm_dac:
case snd_soc_dapm_aif_in:
w->power_check = dapm_dac_check_power;
break;
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_micbias:
case snd_soc_dapm_spk:
case snd_soc_dapm_hp:
case snd_soc_dapm_mic:
case snd_soc_dapm_line:
w->power_check = dapm_generic_check_power;
break;
case snd_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;
return 0;
}
<二>route与path
对 snd_soc_dapm_route的定义, sink为dest目的端, control为bridge 桥梁作用, source为src源端,当 control为NULL时默认route是通
的,有control时,必须打通control,route才通。具体如何实现后面接着看。
struct snd_soc_dapm_route {
const char *sink;
const char *control;
const char *source;
};
static const struct snd_soc_dapm_route audio_map[] = {
----------
{"SLIM TX1", NULL, "SLIM TX1 MUX"},
{"SLIM TX3 MUX", "DEC3", "DEC3 MUX"},
{"SLIM TX3 MUX", "RMIX1", "RX1 MIX1"},
{"HPHL_PA_MIXER", "AUX_PGA_L Switch", "AUX_PGA_Left"},
-----------
}
形成以下路径:
SLIM TX1 <----------- SLIM TX1 MUX
SLIM TX3 MUX <------------ DEC3 <---------- DEC3 MUX
SLIM TX3 MUX <------------ RMIX1 <-------- RX1 MIX1
SLIM TX3 MUX <------------ RMIX2 <-------- RX2 MIX2
HPHL_PA_MIXER <--------- AUX_PGA_L Switch <-------- AUX_PGA_Left
再看下control的命名规则:
1:对于dest为mixer类的widget,route control name为dest widget下的kcontrol name。
2:对于dest为mux类的widget,route contron name为dest widget下enum下的text数组里面任一个。
其实name是根据codec内部Signal-path Diagram来命名的,软件path通路与codec内部Signal-path是对应的。
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);
if (ret < 0) {
dev_err(dapm->dev, "Failed to add route %s->%s\n",
route->source, route->sink);
return ret;
} route++;
}
return 0;
}
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;
//这里我们走的是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 与 sink widget,申请 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;
return 0;
}
/* connect dynamic paths */
switch (wsink->id) {
//widget为以下类型时默认是连通的状态
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_aif_in:
case snd_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;
return 0;
//关于mux、switch、mixer类widget, dapm_connect_mux 与 dapm_connect_mixer下面再分析
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;
}r
eturn 0;
err:
dev_warn(dapm->dev, "asoc: no dapm match for %s --> %s --> %s\n",
source, control, sink);
kfree(path);
return ret;
}
//通过route里面的name来匹配dest里面的kcontrol name,这里用到了上面的route control name的匹配,若找到则初始化path里面的链表,
name为widget里面的kcontrol name同时也为route里面的control name。
/* connect mixer widget to its interconnecting audio paths */
static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
struct snd_soc_dapm_path *path, const char *control_name)
{
int i;
/* 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);
return 0;
}
}r
eturn -ENODEV;
}
//而mux widget则是通过enum下的text数组里面的名字来匹配的,这与之前的route的control命名规则也是匹配的
/* connect mux widget to its interconnecting audio paths */
static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
struct snd_soc_dapm_path *path, const char *control_name,
const struct snd_kcontrol_new *kcontrol)
{
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
int i;
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);
return 0;
}
}
return -ENODEV;
} //设





codec path状


/* set up initial codec paths */
static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
struct snd_soc_dapm_path *p, int i)
{
switch (w->id) {
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
case snd_soc_dapm_mixer_named_ctl: {
int val;
struct soc_mixer_control *mc = (struct soc_mixer_control *)
w->kcontrol_news[i].private_value;
unsigned int reg = mc->reg;
unsigned int shift = mc->shift;
int max = mc->max;
unsigned int mask = (1 << fls(max)) - 1;
unsigned int 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;
case snd_soc_dapm_mux: {
struct soc_enum *e = (struct soc_enum *)
w->kcontrol_news[i].private_value;
int val, 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;
case snd_soc_dapm_virt_mux: {
struct soc_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;
case snd_soc_dapm_value_mux: {
struct soc_enum *e = (struct soc_enum *)
w->kcontrol_news[i].private_value;
int val, 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 */
case snd_soc_dapm_pga:
case snd_soc_dapm_out_drv:
case snd_soc_dapm_output:
case snd_soc_dapm_adc:
case snd_soc_dapm_input:
case snd_soc_dapm_siggen:
case snd_soc_dapm_dac:
case snd_soc_dapm_micbias:
case snd_soc_dapm_vmid:
case snd_soc_dapm_supply:
case snd_soc_dapm_aif_in:
case snd_soc_dapm_aif_out:
case snd_soc_dapm_hp:
case snd_soc_dapm_mic:
case snd_soc_dapm_spk:
case snd_soc_dapm_line:
p->connect = 1;
break;
/* does affect routing - dynamically connected */
case snd_soc_dapm_pre:
case snd_soc_dapm_post:
p->connect = 0;
break;
}
}
<三>DAPM-动态电源管理
先具体来分析下kcontrol put函数,下面以mixer类型的put函数分析
int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct snd_soc_codec *codec = widget->codec;
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg;
unsigned int shift = mc->shift;
int max = mc->max;
unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
unsigned int val;
int connect, change;
struct snd_soc_dapm_update update;
int wi;
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 = http://www.mamicode.com/val;
update.kcontrol = kcontrol;
update.widget = widget;
update.reg = reg;
update.mask = mask;
update.val = val;
widget->dapm->update = &update;
//下面再仔细分析 update power
snd_soc_dapm_mixer_update_power(widget, kcontrol, connect);
widget->dapm->update = NULL;
}
}
mutex_unlock(&codec->mutex);
return 0;
}
/* test and update the power status of a mixer or switch widget */
int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
struct snd_kcontrol *kcontrol, int connect)
{
struct snd_soc_dapm_path *path;
int found = 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链表来找到path name与kcontrol name匹配的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);
}
}
return 0;
}
//看看mux类widget update power
int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
struct snd_kcontrol *kcontrol, int change,
int mux, struct soc_enum *e)
{
struct snd_soc_dapm_path *path;
int found = 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)
return 0;
/* 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 */
//path name与enum text数组其中一个名字必须匹配,path通路打通名字不匹配则关闭该path
//实例:‘SLIM TX3 MUX‘:0:RMIX3 连通 ‘SLIM TX3 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,
"mux disconnection");
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);
}
}
return 0;
}
到现在为止 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.:-
*/
static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
{
struct snd_soc_card *card = dapm->card;
struct snd_soc_dapm_widget *w;
struct snd_soc_dapm_context *d;
LIST_HEAD(up_list);
LIST_HEAD(down_list);
LIST_HEAD(async_domain);
enum snd_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) {
case snd_soc_dapm_supply:
case snd_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) {
case SND_SOC_DAPM_STREAM_START:
case SND_SOC_DAPM_STREAM_RESUME:
dapm->target_bias_level = SND_SOC_BIAS_ON;
break;
case SND_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;
case SND_SOC_DAPM_STREAM_SUSPEND:
dapm->target_bias_level = SND_SOC_BIAS_STANDBY;
break;
case SND_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,
"DAPM sequencing finished, waiting %dms\n", card->pop_time);
pop_wait(card->pop_time);
mutex_unlock(&card->dapm_power_mutex);
trace_snd_soc_dapm_done(card);
return 0;
}
static void dapm_power_one_widget(struct snd_soc_dapm_widget *w,
struct list_head *up_list,
struct list_head *down_list)
{
int power;
switch (w->id) {
case snd_soc_dapm_pre:
dapm_seq_insert(w, down_list, false);
break;
case snd_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;
}
}
static int dapm_widget_power_check(struct snd_soc_dapm_widget *w)
{
if (w->power_checked)
return w->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;
return w->new_power;
}
/* Generic check to see if a widget should be powered.
*/
static int dapm_generic_check_power(struct snd_soc_dapm_widget *w)
{
int in, 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);
//处于完整通道上的widget return 1
return out != 0 && in != 0;
}
/*
* Recursively check for a completed path to an active or physically connected
* input widget. Returns number of complete paths.
*/
static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
struct snd_soc_dapm_widget_list **list)
{
struct snd_soc_dapm_path *path;
int con = 0;
if (widget->inputs >= 0)
return widget->inputs;
DAPM_UPDATE_STAT(widget, path_checks);
if (widget->id == snd_soc_dapm_supply)
return 0;
/* active stream ? */
switch (widget->id) {
case snd_soc_dapm_dac:
case snd_soc_dapm_aif_in:
if (widget->active) {
widget->inputs = snd_soc_dapm_suspend_check(widget);
return widget->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);
return widget->inputs;
}
/* connected VMID/Bias for lower pops */
if (widget->id == snd_soc_dapm_vmid) {
widget->inputs = snd_soc_dapm_suspend_check(widget);
return widget->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);
return widget->inputs;
}
/* signal generator */
if (widget->id == snd_soc_dapm_siggen) {
widget->inputs = snd_soc_dapm_suspend_check(widget);
return widget->inputs;
}
}
//遍历 sources链表path,若该path存在source同时处于连通状态则递归调用该函数,直到找到widget处于input 端点或者path不
连接状态return 0;
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) {
int err;
err = dapm_list_add_widget(list, path->sink);
if (err < 0) {
dev_err(widget->dapm->dev, "could not add widget %s\n",
widget->name);
return con;
}
}
con += is_connected_input_ep(path->source, list);
}
}
widget->inputs = con;
return con;
}
static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power,
struct list_head *up_list,
struct list_head *down_list)
{
struct snd_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) {
case snd_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 MIX1 INP1
<7>[ 44.594177] tabla_codec tabla_codec: dapm: power up widget RX3 MIX1 INP1
<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 DSM MUX
<7>[ 44.594543] tabla_codec tabla_codec: dapm: power up widget LINEOUT2 DAC
<7>[ 44.594665] tabla_codec tabla_codec: dapm: power up widget RX3 MIX2
<7>[ 44.594787] tabla_codec tabla_codec: dapm: power up widget LINEOUT4 DAC
<7>[ 44.594879] tabla_codec tabla_codec: dapm: power up widget RX_BIAS
<7>[ 44.594940] tabla_codec tabla_codec: dapm: power up widget LINEOUT2_PA_MIXER
<7>[ 44.595062] tabla_codec tabla_codec: dapm: power up widget LINEOUT1 DAC
<7>[ 44.595123] tabla_codec tabla_codec: dapm: power up widget RX4 DSM MUX
<7>[ 44.595214] tabla_codec tabla_codec: dapm: power up widget LINEOUT4_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 LINEOUT2 PA
<7>[ 44.595947] tabla_codec tabla_codec: dapm: power up widget LINEOUT1_PA_MIXER
<7>[ 44.596008] tabla_codec tabla_codec: dapm: power up widget LINEOUT3 DAC

Linux音频ALSA机制学习笔记<二>