【DAPDM 四】--- dapm机制深入分析(下篇)

  • 四、dapm机制深入分析
    • 4.2 dapm触发依据
    • 4.3 dapm_power_widgets()
    • 4.4 dapm_seq_run()

四、dapm机制深入分析

dapm触发时的入口函数是dapm_power_widgets,稍后详细分析这个函数,
这里仅说其作用:
检查每个dapm widget,如果该widget处在一条complete paths中,则power up这个widget,否则power down。

4.2 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

4.3 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的widgetsLIST_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_listlist_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状态,如果当前状态和设置状态一致,那么显然不用重复设置widgetif (w->power == power)continue;//将widget插入到up_list或down_list中if (power)dapm_seq_insert(w, &up_list, dapm_up_seq);elsedapm_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;}}/* If we're changing to all on or all off then prepare */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);}//先power down链表down_list上的widgets,接着power up链表up_list上的widgets//按照这样的次序,目的是避免产生pop音//dapm_seq_run核心函数,见其详细分析/* Power down widgets first; try to avoid amplifying pops. */dapm_seq_run(codec, &down_list, event, dapm_down_seq);/* Now power up. */dapm_seq_run(codec, &up_list, event, dapm_up_seq);/* If we just powered the last thing off drop to standby bias */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 we just powered up then move to active bias */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;
}

4.4 dapm_seq_run()

/* Apply a DAPM power sequence.** We walk over a pre-sorted list of widgets to apply power to.  In* order to minimise the number of writes to the device required* multiple widgets will be updated in a single write where possible.* Currently anything that requires more than a single write is not* handled.*/
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;//创建pending链表LIST_HEAD(pending);int cur_sort = -1;int cur_reg = SND_SOC_NOPM;int ret;//遍历list(即up_list或down_list),根据成员power_list找到挂到list上的每一个widgetlist_for_each_entry_safe(w, n, list, power_list) {ret = 0;/* Do we need to apply any queued changes? */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) {//为什么类型为pre/post的widget只执行event回调函数?看看它们的原型就明白了。//#define SND_SOC_DAPM_PRE(wname, wevent),显然这些widget只含有stream name和event回调函数。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:/* No register support currently *///这里就比较奇怪了,input/output/hp/mic/line/spk这些widgets也只有stream name和event,//但是仍然调用dapm_generic_apply_power试图控制widget的开关?根据这里的注释,应该是预留的。ret = dapm_generic_apply_power(w);break;default:/* Queue it up for application *///遇到非以上类型的widget,则插入到pending链表,进一步调用dapm_seq_run_coalesced处理。//这里设计很巧妙!下面详细解析这点。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处理是不同的。

/* Do we need to apply any queued changes? */
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;
}
/* Queue it up for application */
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机制就毫无压力了。

【DAPDM 四】--- dapm机制深入分析(下篇)相关推荐

  1. 【DAPDM 四】--- dapm机制深入分析(上篇)

    [DAPDM 四]--- dapm机制深入分析 四.dapm机制深入分析 4.1 DAPM 模块重要函数 4.1.1 dapm_update_bits() 4.1.2 snd_soc_update_b ...

  2. DAPM之四:dapm机制深入分析(下)

    dapm触发时的入口函数是dapm_power_widgets,稍后详细分析这个函数,这里仅说其作用:检查每个dapm widget,如果该widget处在一条complete paths中,则pow ...

  3. 转帖-MySQL Innodb日志机制深入分析

    为什么80%的码农都做不了架构师?>>>    MySQL Innodb日志机制深入分析 http://blog.csdn.net/yunhua_lee/article/detail ...

  4. Linux kernel 同步机制(下篇)

    之前的文章 Linux kernel同步机制 在上一部分,我们讨论了最基本常见的几类同步机制,这一部分我们将讨论相对复杂的几种同步机制,尤其是读写信号量和RCU,在操作系统内核中有相当广泛的应用. 读 ...

  5. python的回收机制_Python的垃圾回收机制深入分析

    一.概述: Python的GC模块主要运用了"引用计数"(reference counting)来跟踪和回收垃圾.在引用计数的基础上,还可以通过"标记-清除"( ...

  6. MySQL Innodb日志机制深入分析

    1.1. Log & Checkpoint Innodb的事务日志是指Redo log,简称Log,保存在日志文件ib_logfile*里面.Innodb还有另外一个日志Undo log,但U ...

  7. v8学习笔记(四) 对象机制

    v8对象机制 1.概述 v8中每一个API对象都对应一个内部实现对象(堆对象) 2.对象创建过程 (1)v8::internal::Factory类:创建各种内部对象(v8::internal::) ...

  8. import java.io 包下载_Go 包管理机制深入分析

    前言 随着 Go 语言的深入使用,其依赖管理机制也一直是各位 Gopher 热衷于探讨的话题.Go 语言的源码依赖可通过 go get 命令来获取,但自动化程度不高,于是官方提供了 Dep 这样的自动 ...

  9. url过滤怎么解除_Shiro-实战(四)---过滤器机制

    1 简介 Shiro使用了与Servlet一样的Filter接口进行扩展 1.1 NameableFilter NameableFilter给Filter起个名字,如果没有设置默认就是FilterNa ...

最新文章

  1. Scenario 7 – HP C7000 VC FlexFabric Tunneled VLANs and SUS A/A vSphere
  2. 初次树莓派遇到的一些小问题
  3. 日常问题——hadoop启动后发现namenode没有启动,但是排除了格式化过度的问题
  4. 微信小程序中app.js文件、组件、api
  5. ndarray是什么_python数据分析用什么软件?
  6. k8s核心技术-Controller(Deployment)_概述和应用场景---K8S_Google工作笔记0028
  7. PyCharm——Youki觉得好用的快捷键~
  8. Cinema 4D* 中令人惊叹的体积效果
  9. [推荐书籍]Flex 4 in Action MEAP Edition
  10. ubuntu20安装teamview15
  11. segger公司调试cortex-m内核出现hardfault的方法
  12. 51单片机入门教程(2)——实现流水灯
  13. SQLException: #22001你知道这个错误码吗
  14. 用微前端框架qiankun配置项目的实战
  15. Python新手入门英文
  16. 十大跨平台移动应用开发工具
  17. SAP采购订单行项目中的免费和发票收据的同步逻辑
  18. 中国计算机用户特刊,高考特刊|大佬的高考史,是互联网的凡尔赛史
  19. 图解 K8S(07):调度利器之亲和与反亲和(服务容灾)
  20. 天猫店铺基础知识分享

热门文章

  1. 目的:使用CUDA环境变量CUDA_VISIBLE_DEVICES来限定CUDA程序所能使用的GPU设备
  2. ggplot2-条形图和折线图
  3. s4 android4.4.2,三星Galaxy S4(GT-I9500)开始Android 4.4.2升级
  4. linux静默安装oracle11g数据库教程
  5. Java中xmp标签的作用_html 中 xmp标记
  6. Catalan数的分析和应用
  7. Android StatusBar 更改
  8. oracle.简单查询、排序、限定查询、单行函数
  9. 发票核验API 分享推荐
  10. CSS-属性选择器的用法详解