dapm触发时的入口函数是dapm_power_widgets,稍后详细分析这个函数,这里仅说其作用:检查每个dapm widget,如果该widget处在一条complete paths中,则power up这个widget,否则power down。
dapm触发
1、dapm widgets建立时,详见snd_soc_dapm_new_widgets;
2、上层通过alsa_amixer等工具改变codec音频路径时,此时与此相关的widgets状态要重置,详见dapm_mixer_update_power和dapm_mux_update_power;
- amixer-应用层[alsa_amixer cset name='Left Output Mixer Left Input Mixer Switch' 1]
- |->snd_ctl_ioctl-系统调用
- |->snd_ctl_elem_write_user-内核钩子函数
- |->snd_ctl_elem_wirte-
- |->snd_ctl_find_id-遍历kcontrol链表找到name字段匹配的kctl
- |->kctl->put()-调用kctl的成员函数put()
- |->snd_soc_dapm_put_volsw
- |->dapm_mixer_update_power
- |->更新path->connect状态
- |->dapm_power_widgets 触发dapm,重置相关的widgets
3、发生stream事件时,会触发snd_soc_dapm_stream_even。什么叫stream事件?准备或关闭一个pcm stream通道(snd_pcm_prepare/snd_pcm_close)这些都属于stream事件。另外suspend或resume时,也会触发snd_soc_dapm_stream_event处理。
- snd_pcm_prepare
- |->soc_pcm_prepare
- |->处理platform、codec-dai、cpu-dai的prepare回调函数
- |->snd_soc_dapm_stream_event
- |->遍历codec每个dapm widget,如果该widget的stream name与传递进来的stream参数相匹配,如果匹配则置widget->active为真
- |->dapm_power_widgets 触发dapm,重置相关的widgets
dapm_power_widgets分析
1、初始化两个链表up_list和down_list,如字面意思,up_list指向要power up的widgets,down_list指向要power down的widgets;
2、遍历所有widgets,检查是否需要对其进行power操作;要power up的则插入到up_list,要power down的则插入到down_list;
3、先power down down_list上widgets,再power up up_list上的widgets;
4、设置codec的偏置(bias)电压。
-
-
-
-
-
-
-
-
-
- static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
- {
- struct snd_soc_device *socdev = codec->socdev;
- struct snd_soc_dapm_widget *w;
-
-
- LIST_HEAD(up_list);
- LIST_HEAD(down_list);
- int ret = 0;
- int power;
- int sys_power = 0;
-
-
-
-
-
- list_for_each_entry(w, &codec->dapm_widgets, list) {
- switch (w->id) {
- case snd_soc_dapm_pre:
-
- dapm_seq_insert(w, &down_list, dapm_down_seq);
- break;
- case snd_soc_dapm_post:
-
- dapm_seq_insert(w, &up_list, dapm_up_seq);
- break;
-
- default:
-
-
-
- if (!w->power_check)
- continue;
-
-
-
- switch (event) {
- case SND_SOC_DAPM_STREAM_SUSPEND:
-
- power = 0;
- break;
-
- default:
- power = w->power_check(w);
- if (power)
- sys_power = 1;
- break;
- }
-
-
- if (w->power == power)
- continue;
-
-
- if (power)
- dapm_seq_insert(w, &up_list, dapm_up_seq);
- else
- dapm_seq_insert(w, &down_list, dapm_down_seq);
-
-
- w->power = power;
- break;
- }
- }
-
-
-
-
- if (list_empty(&codec->dapm_widgets)) {
- switch (event) {
- case SND_SOC_DAPM_STREAM_START:
- case SND_SOC_DAPM_STREAM_RESUME:
- sys_power = 1;
- break;
- case SND_SOC_DAPM_STREAM_SUSPEND:
- sys_power = 0;
- break;
- case SND_SOC_DAPM_STREAM_NOP:
- sys_power = codec->bias_level != SND_SOC_BIAS_STANDBY;
- break;
- default:
- break;
- }
- }
-
-
- if ((sys_power && codec->bias_level == SND_SOC_BIAS_STANDBY) ||
- (!sys_power && codec->bias_level == SND_SOC_BIAS_ON)) {
- ret = snd_soc_dapm_set_bias_level(socdev,
- SND_SOC_BIAS_PREPARE);
- if (ret != 0)
- pr_err("Failed to prepare bias: %d\n", ret);
- }
-
-
-
-
-
-
- dapm_seq_run(codec, &down_list, event, dapm_down_seq);
-
-
- dapm_seq_run(codec, &up_list, event, dapm_up_seq);
-
-
- if (codec->bias_level == SND_SOC_BIAS_PREPARE && !sys_power) {
- ret = snd_soc_dapm_set_bias_level(socdev,
- SND_SOC_BIAS_STANDBY);
- if (ret != 0)
- pr_err("Failed to apply standby bias: %d\n", ret);
- }
-
-
- if (codec->bias_level == SND_SOC_BIAS_PREPARE && sys_power) {
- ret = snd_soc_dapm_set_bias_level(socdev,
- SND_SOC_BIAS_ON);
- if (ret != 0)
- pr_err("Failed to apply active bias: %d\n", ret);
- }
-
- pop_dbg(codec->pop_time, "DAPM sequencing finished, waiting %dms\n",
- codec->pop_time);
-
- return 0;
- }
dapm_seq_run分析
-
-
-
-
-
-
-
-
- static void dapm_seq_run(struct snd_soc_codec *codec, struct list_head *list,
- int event, int sort[])
- {
- struct snd_soc_dapm_widget *w, *n;
-
- LIST_HEAD(pending);
- int cur_sort = -1;
- int cur_reg = SND_SOC_NOPM;
- int ret;
-
-
- list_for_each_entry_safe(w, n, list, power_list) {
- ret = 0;
-
-
- if (sort[w->id] != cur_sort || w->reg != cur_reg) {
- if (!list_empty(&pending))
- dapm_seq_run_coalesced(codec, &pending);
-
- INIT_LIST_HEAD(&pending);
- cur_sort = -1;
- cur_reg = SND_SOC_NOPM;
- }
-
- switch (w->id) {
-
-
- case snd_soc_dapm_pre:
- if (!w->event)
- list_for_each_entry_safe_continue(w, n, list,
- power_list);
-
- if (event == SND_SOC_DAPM_STREAM_START)
- ret = w->event(w,
- NULL, SND_SOC_DAPM_PRE_PMU);
- else if (event == SND_SOC_DAPM_STREAM_STOP)
- ret = w->event(w,
- NULL, SND_SOC_DAPM_PRE_PMD);
- break;
-
- case snd_soc_dapm_post:
- if (!w->event)
- list_for_each_entry_safe_continue(w, n, list,
- power_list);
-
- if (event == SND_SOC_DAPM_STREAM_START)
- ret = w->event(w,
- NULL, SND_SOC_DAPM_POST_PMU);
- else if (event == SND_SOC_DAPM_STREAM_STOP)
- ret = w->event(w,
- NULL, SND_SOC_DAPM_POST_PMD);
- break;
-
- case snd_soc_dapm_input:
- case snd_soc_dapm_output:
- case snd_soc_dapm_hp:
- case snd_soc_dapm_mic:
- case snd_soc_dapm_line:
- case snd_soc_dapm_spk:
-
-
-
- ret = dapm_generic_apply_power(w);
- break;
-
- default:
-
-
-
- cur_sort = sort[w->id];
- cur_reg = w->reg;
- list_move(&w->power_list, &pending);
- break;
- }
-
- if (ret < 0)
- pr_err("Failed to apply widget power: %d\n",
- ret);
- }
-
- if (!list_empty(&pending))
- dapm_seq_run_coalesced(codec, &pending);
- }
从dapm_seq_run的分析,我们可以看出,mixer/mux类型的widgets处理是不同的。
-
- if (sort[w->id] != cur_sort || w->reg != cur_reg) {
- if (!list_empty(&pending))
- dapm_seq_run_coalesced(codec, &pending);
-
- INIT_LIST_HEAD(&pending);
- cur_sort = -1;
- cur_reg = SND_SOC_NOPM;
- }
-
- cur_sort = sort[w->id];
- cur_reg = w->reg;
- list_move(&w->power_list, &pending);
结合这两段代码理解:遇到操作对象是同一个reg的widgets,则把他们放入pending链表中,随后调用dapm_seq_run_coalesced进行处理。这样做的意义何在?见dapm_seq_run注释:In order to minimise the number of writes to the device required multiple widgets will be updated in a single write where possible.保证了同reg但不同widgets的一次性读写。这设计是相当巧妙高效的。
dapm_seq_run_coalesced就不累述了,比较简单,对同reg但不同widgets进行一次读写。
总结:dapm机制分析到此结束了,这篇主要简单说了下其触发过程,同时分析两个主体函数。其实原理是简单的,复杂的地方都在于前期处理,这些在上篇已详细分析了。相信了解:
path的建立过程、根据path->list_source找到作为sink的widget、根据path->list_sink找到作为source的widget、endpoint的概念、complete path的概念、depop通电/断电次序,理解整个dpam机制就毫无压力了。