您的位置:首页 > 产品设计 > 产品经理

Asoc dapm(五) - dapm widget链表更新

2015-12-06 18:52 393 查看
有几个操作和dapm widget链表更新相关

1) 初始化的时候,snd_soc_instantiate_card里调用snd_soc_dapm_new_widgets,最终会调用dapm_power_widgets

2) 在用户空间通过tinymix设置路径,在SOC_DAPM_ENUM中的put或者get函数最终会调用dapm_power_widgets

3) 在用户空间通过tinyplay播放或者录音是的soc_pcm_prepare和soc_pcm_close,最终会调用dapm_power_widgets

以上3个地方最终会引发dapm widget链表的更新,都会调用一个很关键的函数dapm_power_widgets

以下几个操作会引起dapm widget链表更新

snd_soc_dapm_new_widgets

snd_soc_dapm_new_widgets在snd_soc_instantiate_card的时候会被调用,主要的作用是将card->widgets链表中widget的所有control添加到card->kcontrol链表中,最后会调用dapm_power_widgets

int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm)
{
struct snd_soc_dapm_widget *w;
unsigned int val;

mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);

list_for_each_entry(w, &dapm->card->widgets, list)
{
if (w->new)
continue;

if (w->num_kcontrols) {
w->kcontrols = kzalloc(w->num_kcontrols *
sizeof(struct snd_kcontrol *),
GFP_KERNEL);
if (!w->kcontrols) {
mutex_unlock(&dapm->card->dapm_mutex);
return -ENOMEM;
}
}

switch(w->id) {
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
case snd_soc_dapm_mixer_named_ctl:
dapm_new_mixer(w);
break;
case snd_soc_dapm_mux:
case snd_soc_dapm_virt_mux:
case snd_soc_dapm_value_mux:
dapm_new_mux(w);
break;
case snd_soc_dapm_pga:
case snd_soc_dapm_out_drv:
dapm_new_pga(w);
break;
default:
break;
}

/* Read the initial power state from the device */
if (w->reg >= 0) {
val = soc_widget_read(w, w->reg);
val &= 1 << w->shift;
if (w->invert)
val = !val;

if (val)
w->power = 1;
}

w->new = 1;

dapm_mark_dirty(w, "new widget");
dapm_debugfs_add_widget(w);
}

dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP);
mutex_unlock(&dapm->card->dapm_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets);


SOC_DAPM_ENUM

tinymix对kcontrol进行设置的时候,调用的就是SOC_DAPM_ENUM中的put或者get函数,最终会执行dapm_power_widgets

SOC_DAPM_ENUM

#define SOC_DAPM_ENUM(xname, xenum) \
{   .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = snd_soc_info_enum_double, \
.get = snd_soc_dapm_get_enum_double, \
.put = snd_soc_dapm_put_enum_double, \
.private_value = (unsigned long)&xenum }


snd_soc_dapm_put_enum_double

int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
kcontrol->private_value是struct soc_enum类型,包含此kcontrol对应寄存器及在寄存器中的偏移
ucontrol->vallue.enumerated.item[0]/[1] 用户空间对kcontrol对应寄存器设定的值
soc_dapm_mux_update_power(widget, kcontrol, mux, e);
}


soc_dapm_mux_update_power



在soc_dapm_mux_update_power中,

1) 在card->path链表中找到与要设置的kcontrol匹配的path

2) 令path->connect=1,并且dapm_mark_dirty(path->source, “mux connection”);将path->source对应的widget添加到card->dapm_dirty链表中

3) dapm_mark_dirty(widget, “mux change”);将此widget添加到card->dapm_dirty链表中

4) dapm_power_widgets

soc_pcm_prepare & soc_pcm_close

dapm_power_widgets

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);
ASYNC_DOMAIN_EXCLUSIVE(async_domain);
enum snd_soc_bias_level bias;

list_for_each_entry(d, &card->dapm_list, list) {
if (d->idle_bias_off)
d->target_bias_level = SND_SOC_BIAS_OFF;
else
d->target_bias_level = SND_SOC_BIAS_STANDBY;
}

dapm_reset(card);

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) {
switch (w->id) {
case snd_soc_dapm_pre:
case snd_soc_dapm_post:
/* These widgets always need to be powered */
break;
default:
list_del_init(&w->dirty);
break;
}

if (w->power) {
d = w->dapm;

switch (w->id) {
case snd_soc_dapm_siggen:
break;
case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply:
case snd_soc_dapm_clock_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;
}
}

}

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

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

/* do we need to notify any clients that DAPM event is complete */
list_for_each_entry(d, &card->dapm_list, list) {
if (d->stream_event)
d->stream_event(d, event);
}

pop_dbg(dapm->dev, card->pop_time,
"DAPM sequencing finished, waiting %dms\n", card->pop_time);
pop_wait(card->pop_time);
return 0;
}


1) dapm_reset

对card->widgets链表中所有的widget

w->power_checked = false;

w->inputs = -1;

w->outputs = -1;

2) dapm_power_one_widget

list_for_each_entry(w, &card->dapm_dirty, dirty) {
dapm_power_one_widget(w, &up_list, &down_list);
}

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;
}
}


将card->dapm_dirty链表中的widget按照不同的情况分别加入到up_list和down_list中。



dapm_seq_insert函数里面的dapm_seq_compare比较上电先后顺序,看上图,如果new widget->id < widget->id,则将new widget添加到widget之前。这样dapm_seq_insert就可按照一定的顺序将widget添加到power_up链表或者power_down链表中

3) dapm_seq_run(dapm, &down_list, event, false);

在下电链表中,调用各个widget的event回调函数来实现下电

4) dapm_widget_update

更新widget中kcontrol,这个kcontrol是触发本次状态改变的触发源

5) dapm_seq_run(dapm, &up_list, event, true);

在上电链表中,调用各个widget的event回调函数来实现上电

参考文章

ALSA声卡驱动中的DAPM详解之六:精髓所在,牵一发而动全身

ALSA声卡驱动中的DAPM详解之七:dapm事件机制(dapm event)

附上几篇文章的链接

1. Asoc dapm(一) - kcontrol

2. Asoc dapm(二) - kcontrol注册与使用

3. Asoc dapm(三) - dapm widgets & dapm kcontrol & dapm route

4. Asoc dapm(四) - dapm widgets & dapm route注册

5. Asoc dapm(五) - dapm widget链表更新
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: