Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2421545
  • 博文数量: 401
  • 博客积分: 12926
  • 博客等级: 上将
  • 技术积分: 4567
  • 用 户 组: 普通用户
  • 注册时间: 2009-02-22 14:51
文章分类

全部博文(401)

文章存档

2015年(16)

2014年(4)

2013年(12)

2012年(82)

2011年(98)

2010年(112)

2009年(77)

分类: LINUX

2012-05-30 17:17:32

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;

  1. amixer-应用层[alsa_amixer cset name='Left Output Mixer Left Input Mixer Switch' 1]      
  2.   |->snd_ctl_ioctl-系统调用      
  3.        |->snd_ctl_elem_write_user-内核钩子函数      
  4.             |->snd_ctl_elem_wirte-      
  5.                  |->snd_ctl_find_id-遍历kcontrol链表找到name字段匹配的kctl      
  6.                  |->kctl->put()-调用kctl的成员函数put()      
  7.                       |->snd_soc_dapm_put_volsw    
  8.                            |->dapm_mixer_update_power    
  9.                                 |->更新path->connect状态  
  10.                                 |->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处理。

  1. snd_pcm_prepare  
  2.   |->soc_pcm_prepare  
  3.        |->处理platform、codec-dai、cpu-dai的prepare回调函数  
  4.        |->snd_soc_dapm_stream_event  
  5.             |->遍历codec每个dapm widget,如果该widget的stream name与传递进来的stream参数相匹配,如果匹配则置widget->active为真  
  6.             |->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)电压。

  1. /* 
  2.  * Scan each dapm widget for complete audio path. 
  3.  * A complete path is a route that has valid endpoints i.e.:- 
  4.  * 
  5.  *  o DAC to output pin. 
  6.  *  o Input Pin to ADC. 
  7.  *  o Input pin to Output pin (bypass, sidetone) 
  8.  *  o DAC to ADC (loopback). 
  9.  */  
  10. static int dapm_power_widgets(struct snd_soc_codec *codec, int event)  
  11. {  
  12.     struct snd_soc_device *socdev = codec->socdev;  
  13.     struct snd_soc_dapm_widget *w;  
  14.       
  15.     //初始化两个链表up_list和down_list,up_list指向要power up的widgets,down_list指向要power down的widgets  
  16.     LIST_HEAD(up_list);  
  17.     LIST_HEAD(down_list);  
  18.     int ret = 0;  
  19.     int power;  
  20.     int sys_power = 0;  
  21.   
  22.     /* Check which widgets we need to power and store them in 
  23.      * lists indicating if they should be powered up or down. 
  24.      */  
  25.     //遍历所有的dapm widgets,检查是否需要对widget开关;'开'则把该widget插入到up_list,'关'则插入到down_list  
  26.     list_for_each_entry(w, &codec->dapm_widgets, list) {  
  27.         switch (w->id) {  
  28.         case snd_soc_dapm_pre:  
  29.             //属machine specific pre widget,插入到down_list最前方  
  30.             dapm_seq_insert(w, &down_list, dapm_down_seq);  
  31.             break;  
  32.         case snd_soc_dapm_post:  
  33.             //属machine specific post widget,插入到up_list最后方  
  34.             dapm_seq_insert(w, &up_list, dapm_up_seq);  
  35.             break;  
  36.   
  37.         default:  
  38.             //其他类型的widgets,则调用自身的power_check函数进行检查需要开关。  
  39.             //关于power_check,具体见《DAPM之五:dapm机制深入分析(上)》第四、第五小节。非常重要的一个函数。  
  40.               
  41.             if (!w->power_check)  
  42.                 continue;  
  43.   
  44.             /* If we're suspending then pull down all the  
  45.              * power. */  
  46.             switch (event) {  
  47.             case SND_SOC_DAPM_STREAM_SUSPEND:  
  48.                 //上面注释很清楚了,如果是suspend事件,则pull down所有widgets。  
  49.                 power = 0;  
  50.                 break;  
  51.   
  52.             default:  
  53.                 power = w->power_check(w);  
  54.                 if (power)  
  55.                     sys_power = 1;  
  56.                 break;  
  57.             }  
  58.   
  59.             //w->power保存widget当前的power状态,如果当前状态和设置状态一致,那么显然不用重复设置widget  
  60.             if (w->power == power)  
  61.                 continue;  
  62.                   
  63.             //将widget插入到up_list或down_list中  
  64.             if (power)  
  65.                 dapm_seq_insert(w, &up_list, dapm_up_seq);  
  66.             else  
  67.                 dapm_seq_insert(w, &down_list, dapm_down_seq);  
  68.   
  69.             //更新w->power的状态  
  70.             w->power = power;  
  71.             break;  
  72.         }  
  73.     }  
  74.   
  75.     /* If there are no DAPM widgets then try to figure out power from the 
  76.      * event type. 
  77.      */  
  78.     if (list_empty(&codec->dapm_widgets)) {  
  79.         switch (event) {  
  80.         case SND_SOC_DAPM_STREAM_START:  
  81.         case SND_SOC_DAPM_STREAM_RESUME:  
  82.             sys_power = 1;  
  83.             break;  
  84.         case SND_SOC_DAPM_STREAM_SUSPEND:  
  85.             sys_power = 0;  
  86.             break;  
  87.         case SND_SOC_DAPM_STREAM_NOP:  
  88.             sys_power = codec->bias_level != SND_SOC_BIAS_STANDBY;  
  89.             break;  
  90.         default:  
  91.             break;  
  92.         }  
  93.     }  
  94.   
  95.     /* If we're changing to all on or all off then prepare */  
  96.     if ((sys_power && codec->bias_level == SND_SOC_BIAS_STANDBY) ||  
  97.         (!sys_power && codec->bias_level == SND_SOC_BIAS_ON)) {  
  98.         ret = snd_soc_dapm_set_bias_level(socdev,  
  99.                           SND_SOC_BIAS_PREPARE);  
  100.         if (ret != 0)  
  101.             pr_err("Failed to prepare bias: %d\n", ret);  
  102.     }  
  103.       
  104.     //先power down链表down_list上的widgets,接着power up链表up_list上的widgets  
  105.     //按照这样的次序,目的是避免产生pop音  
  106.     //dapm_seq_run核心函数,见其详细分析  
  107.   
  108.     /* Power down widgets first; try to avoid amplifying pops. */  
  109.     dapm_seq_run(codec, &down_list, event, dapm_down_seq);  
  110.   
  111.     /* Now power up. */  
  112.     dapm_seq_run(codec, &up_list, event, dapm_up_seq);  
  113.   
  114.     /* If we just powered the last thing off drop to standby bias */  
  115.     if (codec->bias_level == SND_SOC_BIAS_PREPARE && !sys_power) {  
  116.         ret = snd_soc_dapm_set_bias_level(socdev,  
  117.                           SND_SOC_BIAS_STANDBY);  
  118.         if (ret != 0)  
  119.             pr_err("Failed to apply standby bias: %d\n", ret);  
  120.     }  
  121.   
  122.     /* If we just powered up then move to active bias */  
  123.     if (codec->bias_level == SND_SOC_BIAS_PREPARE && sys_power) {  
  124.         ret = snd_soc_dapm_set_bias_level(socdev,  
  125.                           SND_SOC_BIAS_ON);  
  126.         if (ret != 0)  
  127.             pr_err("Failed to apply active bias: %d\n", ret);  
  128.     }  
  129.   
  130.     pop_dbg(codec->pop_time, "DAPM sequencing finished, waiting %dms\n",  
  131.         codec->pop_time);  
  132.   
  133.     return 0;  
  134. }  

dapm_seq_run分析

  1. /* Apply a DAPM power sequence. 
  2.  * 
  3.  * We walk over a pre-sorted list of widgets to apply power to.  In 
  4.  * order to minimise the number of writes to the device required 
  5.  * multiple widgets will be updated in a single write where possible. 
  6.  * Currently anything that requires more than a single write is not 
  7.  * handled. 
  8.  */  
  9. static void dapm_seq_run(struct snd_soc_codec *codec, struct list_head *list,  
  10.              int event, int sort[])  
  11. {  
  12.     struct snd_soc_dapm_widget *w, *n;  
  13.     //创建pending链表  
  14.     LIST_HEAD(pending);  
  15.     int cur_sort = -1;  
  16.     int cur_reg = SND_SOC_NOPM;  
  17.     int ret;  
  18.   
  19.     //遍历list(即up_list或down_list),根据成员power_list找到挂到list上的每一个widget  
  20.     list_for_each_entry_safe(w, n, list, power_list) {  
  21.         ret = 0;  
  22.   
  23.         /* Do we need to apply any queued changes? */  
  24.         if (sort[w->id] != cur_sort || w->reg != cur_reg) {  
  25.             if (!list_empty(&pending))  
  26.                 dapm_seq_run_coalesced(codec, &pending);  
  27.   
  28.             INIT_LIST_HEAD(&pending);  
  29.             cur_sort = -1;  
  30.             cur_reg = SND_SOC_NOPM;  
  31.         }  
  32.   
  33.         switch (w->id) {  
  34.         //为什么类型为pre/post的widget只执行event回调函数?看看它们的原型就明白了。  
  35.         //#define SND_SOC_DAPM_PRE(wname, wevent),显然这些widget只含有stream name和event回调函数。  
  36.         case snd_soc_dapm_pre:  
  37.             if (!w->event)  
  38.                 list_for_each_entry_safe_continue(w, n, list,  
  39.                                   power_list);  
  40.   
  41.             if (event == SND_SOC_DAPM_STREAM_START)  
  42.                 ret = w->event(w,  
  43.                            NULL, SND_SOC_DAPM_PRE_PMU);  
  44.             else if (event == SND_SOC_DAPM_STREAM_STOP)  
  45.                 ret = w->event(w,  
  46.                            NULL, SND_SOC_DAPM_PRE_PMD);  
  47.             break;  
  48.   
  49.         case snd_soc_dapm_post:  
  50.             if (!w->event)  
  51.                 list_for_each_entry_safe_continue(w, n, list,  
  52.                                   power_list);  
  53.   
  54.             if (event == SND_SOC_DAPM_STREAM_START)  
  55.                 ret = w->event(w,  
  56.                            NULL, SND_SOC_DAPM_POST_PMU);  
  57.             else if (event == SND_SOC_DAPM_STREAM_STOP)  
  58.                 ret = w->event(w,  
  59.                            NULL, SND_SOC_DAPM_POST_PMD);  
  60.             break;  
  61.   
  62.         case snd_soc_dapm_input:  
  63.         case snd_soc_dapm_output:  
  64.         case snd_soc_dapm_hp:  
  65.         case snd_soc_dapm_mic:  
  66.         case snd_soc_dapm_line:  
  67.         case snd_soc_dapm_spk:  
  68.             /* No register support currently */  
  69.             //这里就比较奇怪了,input/output/hp/mic/line/spk这些widgets也只有stream name和event,  
  70.             //但是仍然调用dapm_generic_apply_power试图控制widget的开关?根据这里的注释,应该是预留的。  
  71.             ret = dapm_generic_apply_power(w);  
  72.             break;  
  73.   
  74.     default:  
  75.         /* Queue it up for application */  
  76.         //遇到非以上类型的widget,则插入到pending链表,进一步调用dapm_seq_run_coalesced处理。  
  77.         //这里设计很巧妙!下面详细解析这点。  
  78.         cur_sort = sort[w->id];  
  79.         cur_reg = w->reg;  
  80.         list_move(&w->power_list, &pending);  
  81.         break;  
  82.         }  
  83.   
  84.         if (ret < 0)  
  85.             pr_err("Failed to apply widget power: %d\n",  
  86.                    ret);  
  87.     }  
  88.   
  89.     if (!list_empty(&pending))  
  90.         dapm_seq_run_coalesced(codec, &pending);  
  91. }  
从dapm_seq_run的分析,我们可以看出,mixer/mux类型的widgets处理是不同的。
  1. /* Do we need to apply any queued changes? */  
  2. if (sort[w->id] != cur_sort || w->reg != cur_reg) {  
  3.     if (!list_empty(&pending))  
  4.         dapm_seq_run_coalesced(codec, &pending);  
  5.   
  6.     INIT_LIST_HEAD(&pending);  
  7.     cur_sort = -1;  
  8.     cur_reg = SND_SOC_NOPM;  
  9. }  
  1. /* Queue it up for application */  
  2. cur_sort = sort[w->id];  
  3. cur_reg = w->reg;  
  4. 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机制就毫无压力了。
阅读(4980) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~