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)电压。
- /*
- * Scan each dapm widget for complete audio path.
- * A complete path is a route that has valid endpoints i.e.:-
- *
- * o DAC to output pin.
- * o Input Pin to ADC.
- * o Input pin to Output pin (bypass, sidetone)
- * o DAC to ADC (loopback).
- */
- 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;
- //初始化两个链表up_list和down_list,up_list指向要power up的widgets,down_list指向要power down的widgets
- LIST_HEAD(up_list);
- LIST_HEAD(down_list);
- int ret = 0;
- int power;
- int sys_power = 0;
- /* Check which widgets we need to power and store them in
- * lists indicating if they should be powered up or down.
- */
- //遍历所有的dapm widgets,检查是否需要对widget开关;'开'则把该widget插入到up_list,'关'则插入到down_list
- list_for_each_entry(w, &codec->dapm_widgets, list) {
- switch (w->id) {
- case snd_soc_dapm_pre:
- //属machine specific pre widget,插入到down_list最前方
- dapm_seq_insert(w, &down_list, dapm_down_seq);
- break;
- case snd_soc_dapm_post:
- //属machine specific post widget,插入到up_list最后方
- dapm_seq_insert(w, &up_list, dapm_up_seq);
- break;
- default:
- //其他类型的widgets,则调用自身的power_check函数进行检查需要开关。
- //关于power_check,具体见《DAPM之五:dapm机制深入分析(上)》第四、第五小节。非常重要的一个函数。
- if (!w->power_check)
- continue;
- /* If we're suspending then pull down all the
- * power. */
- switch (event) {
- case SND_SOC_DAPM_STREAM_SUSPEND:
- //上面注释很清楚了,如果是suspend事件,则pull down所有widgets。
- power = 0;
- break;
- default:
- power = w->power_check(w);
- if (power)
- sys_power = 1;
- break;
- }
- //w->power保存widget当前的power状态,如果当前状态和设置状态一致,那么显然不用重复设置widget
- if (w->power == power)
- continue;
- //将widget插入到up_list或down_list中
- if (power)
- dapm_seq_insert(w, &up_list, dapm_up_seq);
- else
- dapm_seq_insert(w, &down_list, dapm_down_seq);
- //更新w->power的状态
- w->power = power;
- break;
- }
- }
- /* If there are no DAPM widgets then try to figure out power from the
- * event type.
- */
- 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;
- }