DAPM之八:stream domain触发过程分析
来源:互联网 发布:淘宝现货是什么意思 编辑:程序博客网 时间:2024/06/01 08:44
这两天在查一个bug,结果bug没有完美解决,关于stream domain和stream event的触发过程倒是跟了个遍。记于此,也好慰告《DAPM之四:dapm widget events》大坑的在天之灵。
另外,以前的DAPM系列均基于Linux-2.6.32来分析的,目前我们使用Linux-3.4.5,dapm改动很大了。列举一点:
Linux-2.6.32时代,无论codec处在什么状态,系统休眠/唤醒时,codec驱动都可以进入其suspend/resume流程;
Linux-3.4.5时代,只要dapm模块发现codec内部还打开一条complete path(不知道complete path是什么东东的,请补习《DAPM之五:dapm机制深入分析(上)》第4节),那么系统休眠/唤醒时,codec驱动不会跑其suspend/resume流程。
这告诉我们:你丫不按标准来设计音频路径,乱开乱关的,就不让进suspen/resume的大门了!(呃,,,和某猥琐男在外乱搞结果被老婆撞见的情形好像)。大致就这样,具体原因还没有深究,有空再看。
注:这里所有的分析主要由Rambo童鞋进行,感谢!如下分析基于Linux-3.0.8,但基本流程都一样。
1.stream domain
我们先看看内核文档dapm.txt的描述:
4. Stream domain - DACs and ADCs. Enabled and disabled when stream playback/capture is started and stopped respectively. e.g. aplay, arecord.
2.1 Stream Domain Widgets-------------------------Stream Widgets relate to the stream power domain and only consist of ADCs(analog to digital converters) and DACs (digital to analog converters).Stream widgets have the following format:-SND_SOC_DAPM_DAC(name, stream name, reg, shift, invert),NOTE: the stream name must match the corresponding stream name in your codecsnd_soc_codec_dai.e.g. stream widgets for HiFi playback and captureSND_SOC_DAPM_DAC("HiFi DAC", "HiFi Playback", REG, 3, 1),SND_SOC_DAPM_ADC("HiFi ADC", "HiFi Capture", REG, 2, 1),
从中我们可以读到的信息有:
1、stream domain的触发依据有回放子流或录音子流的开始/停止;
2、stream domain widgets的编写,注意一点:widget的sname必须和snd_soc_codec_dai结构体定义的stream name一致。如:
SND_SOC_DAPM_AIF_IN("AIF1DACDAT", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),static struct snd_soc_dai_driver wm8994_dai[] = {{.name = "wm8994-aif1",.id = 1,.playback = {.stream_name = "AIF1 Playback",.channels_min = 1,.channels_max = 2,.rates = WM8994_RATES,.formats = WM8994_FORMATS,},.capture = {.stream_name = "AIF1 Capture",.channels_min = 1,.channels_max = 2,.rates = WM8994_RATES,.formats = WM8994_FORMATS, },.ops = &wm8994_aif1_dai_ops,},
那么当tinyplay启动一个回放子流的播放时,那么名字为AIF1DACDAT的widget就会响应,需要设置的寄存器就会被设置。其实不止那么简单,在dapm机制深入分析中,我已经说了:发生stream事件时,会触发snd_soc_dapm_stream_even()处理,然后会遍历试图找到一条complete path,如果找到,则这条complete path上面的所有widgets都会响应,widgets定义的寄存器会被设置,定义的event会被执行。
接着会实例分析下这个过程,同时不可避免的会有一些代码分析。其实我很讨厌代码分析,看别人博客如果有大量代码分析,我一般不会往下看了。但我阐述水平还没达到那种信手拈来飞花摘叶的境界,所以先说声见谅了。
2.stream domain触发流程
这次其实缘由于一个dapm widget event的跟踪,这个widget的代码如下:
static int late_enable_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event){struct snd_soc_codec *codec = w->codec;struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);dump_stack(); //打印调用stack,跟踪流程的利器switch (event) {case SND_SOC_DAPM_PRE_PMU:if (wm8994->aif1clk_enable) {snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1, WM8994_AIF1CLK_ENA_MASK, WM8994_AIF1CLK_ENA);wm8994->aif1clk_enable = 0;}if (wm8994->aif2clk_enable) {snd_soc_update_bits(codec, WM8994_AIF2_CLOCKING_1, WM8994_AIF2CLK_ENA_MASK, WM8994_AIF2CLK_ENA);wm8994->aif2clk_enable = 0;}break;}/* We may also have postponed startup of DSP, handle that. */wm8958_aif_ev(w, kcontrol, event);return 0;}static int late_disable_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event){struct snd_soc_codec *codec = w->codec;struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);dump_stack(); //打印调用stack,跟踪流程的利器switch (event) {case SND_SOC_DAPM_POST_PMD:printk("-->late_disable_ev SND_SOC_DAPM_POST_PMD\n");if (wm8994->aif1clk_disable) {snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1, WM8994_AIF1CLK_ENA_MASK, 0);wm8994->aif1clk_disable = 0;}if (wm8994->aif2clk_disable) {snd_soc_update_bits(codec, WM8994_AIF2_CLOCKING_1, WM8994_AIF2CLK_ENA_MASK, 0);wm8994->aif2clk_disable = 0;}break;}return 0;}static const struct snd_soc_dapm_widget wm8994_lateclk_revd_widgets[] = {SND_SOC_DAPM_SUPPLY("AIF1CLK", SND_SOC_NOPM, 0, 0, aif1clk_ev,SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),SND_SOC_DAPM_SUPPLY("AIF2CLK", SND_SOC_NOPM, 0, 0, aif2clk_ev,SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),SND_SOC_DAPM_PGA_E("Late DAC1L Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0,late_enable_ev, SND_SOC_DAPM_PRE_PMU),SND_SOC_DAPM_PGA_E("Late DAC1R Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0,late_enable_ev, SND_SOC_DAPM_PRE_PMU),SND_SOC_DAPM_PGA_E("Late DAC2L Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0,late_enable_ev, SND_SOC_DAPM_PRE_PMU),SND_SOC_DAPM_PGA_E("Late DAC2R Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0,late_enable_ev, SND_SOC_DAPM_PRE_PMU),SND_SOC_DAPM_POST("Late Disable PGA", late_disable_ev)};
代码有点长,不要恐惧,我当时只是想了解late_enable_ev和late_disable_ev这两个event是如何被调用的。进入系统后,我播放了一个按键音,其结果如下:
[ 301.866067] Backtrace: [ 301.868445] [<c0040d94>] (dump_backtrace+0x0/0x110) from [<c0640ce0>] (dump_stack+0x18/0x1c)[ 301.876867] r6:f2eac900 r5:f2f92200 r4:00000001 r3:f2f906a0[ 301.882473] [<c0640cc8>] (dump_stack+0x0/0x1c) from [<c04bfcd0>] (late_enable_ev+0x2c/0xd4)[ 301.890809] [<c04bfca4>] (late_enable_ev+0x0/0xd4) from [<c04b5980>] (dapm_seq_check_event.clone.14+0xcc/0x11c)[ 301.900849] r8:c08423e8 r7:ffffffff r6:00000001 r5:c07c183c r4:f2f92200[ 301.907335] r3:f2f906a0[ 301.909943] [<c04b58b4>] (dapm_seq_check_event.clone.14+0x0/0x11c) from [<c04b5a94>] (dapm_seq_run_coalesced+0xc4/0x198)[ 301.920788] r6:00000001 r5:f2eac994 r4:f2f92200[ 301.925369] [<c04b59d0>] (dapm_seq_run_coalesced+0x0/0x198) from [<c04b5bfc>] (dapm_seq_run.clone.15+0x94/0x478)[ 301.935527] [<c04b5b68>] (dapm_seq_run.clone.15+0x0/0x478) from [<c04b62c8>] (dapm_power_widgets+0x2e8/0x408)[ 301.945405] [<c04b5fe0>] (dapm_power_widgets+0x0/0x408) from [<c04b72e0>] (snd_soc_dapm_stream_event+0x80/0xf4)[ 301.955459] [<c04b7260>] (snd_soc_dapm_stream_event+0x0/0xf4) from [<c04b023c>] (soc_pcm_prepare+0x110/0x1cc)[ 301.965342] [<c04b012c>] (soc_pcm_prepare+0x0/0x1cc) from [<c0487c74>] (snd_pcm_do_prepare+0x1c/0x34)[ 301.974527] [<c0487c58>] (snd_pcm_do_prepare+0x0/0x34) from [<c048779c>] (snd_pcm_action_single+0x40/0x80)[ 301.984136] r4:c083e688 r3:00000001[ 301.987683] [<c048775c>] (snd_pcm_action_single+0x0/0x80) from [<c048a394>] (snd_pcm_action_nonatomic+0x70/0x88)[ 301.997835] r7:00020002 r6:c083e688 r5:00020002 r4:f2eacb00[ 302.003456] [<c048a324>] (snd_pcm_action_nonatomic+0x0/0x88) from [<c048b088>] (snd_pcm_common_ioctl1+0x904/0xdb8)[ 302.013776] r6:f2f7ebd4 r5:f2eacb00 r4:00000000 r3:00000000[ 302.019403] [<c048a784>] (snd_pcm_common_ioctl1+0x0/0xdb8) from [<c048b9a4>] (snd_pcm_playback_ioctl1+0x40/0x408)[ 302.029642] [<c048b964>] (snd_pcm_playback_ioctl1+0x0/0x408) from [<c048bda4>] (snd_pcm_playback_ioctl+0x38/0x3c)[ 302.039865] r6:f284cd28 r5:00000000 r4:00000000[ 302.044449] [<c048bd6c>] (snd_pcm_playback_ioctl+0x0/0x3c) from [<c00dd8f4>] (do_vfs_ioctl+0x88/0x50c)[ 302.053738] [<c00dd86c>] (do_vfs_ioctl+0x0/0x50c) from [<c00dddb8>] (sys_ioctl+0x40/0x68)[ 302.061875] r9:f25bc000 r8:c003d844 r7:0000002b r6:00004140 r5:00000000[ 302.068362] r4:e5b6aa80[ 302.070969] [<c00ddd78>] (sys_ioctl+0x0/0x68) from [<c003d6c0>] (ret_fast_syscall+0x0/0x30)[ 302.079295] r7:00000036 r6:414d7be8 r5:414d7b68 r4:4000ca78[ 302.089721] Backtrace: [ 302.092097] [<c0040d94>] (dump_backtrace+0x0/0x110) from [<c0640ce0>] (dump_stack+0x18/0x1c)[ 302.100518] r6:f2eac900 r5:f2f92280 r4:00000001 r3:f2f906c0[ 302.106127] [<c0640cc8>] (dump_stack+0x0/0x1c) from [<c04bfcd0>] (late_enable_ev+0x2c/0xd4)[ 302.114459] [<c04bfca4>] (late_enable_ev+0x0/0xd4) from [<c04b5980>] (dapm_seq_check_event.clone.14+0xcc/0x11c)[ 302.124506] r8:c08423e8 r7:ffffffff r6:00000001 r5:c07c183c r4:f2f92280[ 302.130988] r3:f2f906c0[ 302.133596] [<c04b58b4>] (dapm_seq_check_event.clone.14+0x0/0x11c) from [<c04b5a94>] (dapm_seq_run_coalesced+0xc4/0x198)[ 302.144438] r6:00000001 r5:f2eac994 r4:f2f92280[ 302.149021] [<c04b59d0>] (dapm_seq_run_coalesced+0x0/0x198) from [<c04b5bfc>] (dapm_seq_run.clone.15+0x94/0x478)[ 302.159178] [<c04b5b68>] (dapm_seq_run.clone.15+0x0/0x478) from [<c04b62c8>] (dapm_power_widgets+0x2e8/0x408)[ 302.169055] [<c04b5fe0>] (dapm_power_widgets+0x0/0x408) from [<c04b72e0>] (snd_soc_dapm_stream_event+0x80/0xf4)[ 302.179112] [<c04b7260>] (snd_soc_dapm_stream_event+0x0/0xf4) from [<c04b023c>] (soc_pcm_prepare+0x110/0x1cc)[ 302.188999] [<c04b012c>] (soc_pcm_prepare+0x0/0x1cc) from [<c0487c74>] (snd_pcm_do_prepare+0x1c/0x34)[ 302.198177] [<c0487c58>] (snd_pcm_do_prepare+0x0/0x34) from [<c048779c>] (snd_pcm_action_single+0x40/0x80)[ 302.207791] r4:c083e688 r3:00000001[ 302.211336] [<c048775c>] (snd_pcm_action_single+0x0/0x80) from [<c048a394>] (snd_pcm_action_nonatomic+0x70/0x88)[ 302.221484] r7:00020002 r6:c083e688 r5:00020002 r4:f2eacb00[ 302.227108] [<c048a324>] (snd_pcm_action_nonatomic+0x0/0x88) from [<c048b088>] (snd_pcm_common_ioctl1+0x904/0xdb8)[ 302.237436] r6:f2f7ebd4 r5:f2eacb00 r4:00000000 r3:00000000[ 302.243055] [<c048a784>] (snd_pcm_common_ioctl1+0x0/0xdb8) from [<c048b9a4>] (snd_pcm_playback_ioctl1+0x40/0x408)[ 302.253297] [<c048b964>] (snd_pcm_playback_ioctl1+0x0/0x408) from [<c048bda4>] (snd_pcm_playback_ioctl+0x38/0x3c)[ 302.263514] r6:f284cd28 r5:00000000 r4:00000000[ 302.268102] [<c048bd6c>] (snd_pcm_playback_ioctl+0x0/0x3c) from [<c00dd8f4>] (do_vfs_ioctl+0x88/0x50c)[ 302.277389] [<c00dd86c>] (do_vfs_ioctl+0x0/0x50c) from [<c00dddb8>] (sys_ioctl+0x40/0x68)[ 302.285530] r9:f25bc000 r8:c003d844 r7:0000002b r6:00004140 r5:00000000[ 302.292014] r4:e5b6aa80[ 302.294622] [<c00ddd78>] (sys_ioctl+0x0/0x68) from [<c003d6c0>] (ret_fast_syscall+0x0/0x30)[ 302.302951] r7:00000036 r6:414d7be8 r5:414d7b68 r4:4000ca78[ 302.486533] Backtrace: [ 302.488908] [<c0040d94>] (dump_backtrace+0x0/0x110) from [<c0640ce0>] (dump_stack+0x18/0x1c)[ 302.497327] r6:00000002 r5:f2f7ec00 r4:f2eac900 r3:00000013[ 302.502937] [<c0640cc8>] (dump_stack+0x0/0x1c) from [<c04bfc10>] (late_disable_ev+0x24/0xb8)[ 302.511359] [<c04bfbec>] (late_disable_ev+0x0/0xb8) from [<c04b5f8c>] (dapm_seq_run.clone.15+0x424/0x478)[ 302.520884] r6:c0841df0 r5:ffffffff r4:ffffffff r3:00000013[ 302.526507] [<c04b5b68>] (dapm_seq_run.clone.15+0x0/0x478) from [<c04b62c8>] (dapm_power_widgets+0x2e8/0x408)[ 302.536402] [<c04b5fe0>] (dapm_power_widgets+0x0/0x408) from [<c04b72e0>] (snd_soc_dapm_stream_event+0x80/0xf4)[ 302.546456] [<c04b7260>] (snd_soc_dapm_stream_event+0x0/0xf4) from [<c04b023c>] (soc_pcm_prepare+0x110/0x1cc)[ 302.556341] [<c04b012c>] (soc_pcm_prepare+0x0/0x1cc) from [<c0487c74>] (snd_pcm_do_prepare+0x1c/0x34)[ 302.565522] [<c0487c58>] (snd_pcm_do_prepare+0x0/0x34) from [<c048779c>] (snd_pcm_action_single+0x40/0x80)[ 302.575138] r4:c083e688 r3:00000001[ 302.578681] [<c048775c>] (snd_pcm_action_single+0x0/0x80) from [<c048a394>] (snd_pcm_action_nonatomic+0x70/0x88)[ 302.588829] r7:00020002 r6:c083e688 r5:00020002 r4:f2eacb00[ 302.594453] [<c048a324>] (snd_pcm_action_nonatomic+0x0/0x88) from [<c048b088>] (snd_pcm_common_ioctl1+0x904/0xdb8)[ 302.604776] r6:f2f7ebd4 r5:f2eacb00 r4:00000000 r3:00000000[ 302.610399] [<c048a784>] (snd_pcm_common_ioctl1+0x0/0xdb8) from [<c048b9a4>] (snd_pcm_playback_ioctl1+0x40/0x408)[ 302.620642] [<c048b964>] (snd_pcm_playback_ioctl1+0x0/0x408) from [<c048bda4>] (snd_pcm_playback_ioctl+0x38/0x3c)[ 302.630859] r6:f284cd28 r5:00000000 r4:00000000[ 302.635447] [<c048bd6c>] (snd_pcm_playback_ioctl+0x0/0x3c) from [<c00dd8f4>] (do_vfs_ioctl+0x88/0x50c)[ 302.644734] [<c00dd86c>] (do_vfs_ioctl+0x0/0x50c) from [<c00dddb8>] (sys_ioctl+0x40/0x68)[ 302.652875] r9:f25bc000 r8:c003d844 r7:0000002b r6:00004140 r5:00000000[ 302.659371] r4:e5b6aa80[ 302.661966] [<c00ddd78>] (sys_ioctl+0x0/0x68) from [<c003d6c0>] (ret_fast_syscall+0x0/0x30)[ 302.670293] r7:00000036 r6:414d7be8 r5:414d7b68 r4:4000ca78/ # / # / # [ 310.931244] Backtrace: [ 310.933638] [<c0040d94>] (dump_backtrace+0x0/0x110) from [<c0640ce0>] (dump_stack+0x18/0x1c)[ 310.942046] r6:00000008 r5:f2f7ec00 r4:f2eac900 r3:00000013[ 310.947659] [<c0640cc8>] (dump_stack+0x0/0x1c) from [<c04bfc10>] (late_disable_ev+0x24/0xb8)[ 310.956083] [<c04bfbec>] (late_disable_ev+0x0/0xb8) from [<c04b5e88>] (dapm_seq_run.clone.15+0x320/0x478)[ 310.965610] r6:c0841df0 r5:ffffffff r4:ffffffff r3:00000013[ 310.971225] [<c04b5b68>] (dapm_seq_run.clone.15+0x0/0x478) from [<c04b62c8>] (dapm_power_widgets+0x2e8/0x408)[ 310.981127] [<c04b5fe0>] (dapm_power_widgets+0x0/0x408) from [<c04b72e0>] (snd_soc_dapm_stream_event+0x80/0xf4)[ 310.991183] [<c04b7260>] (snd_soc_dapm_stream_event+0x0/0xf4) from [<c04b011c>] (close_delayed_work+0x44/0x54)[ 311.001143] [<c04b00d8>] (close_delayed_work+0x0/0x54) from [<c006c380>] (process_one_work+0x120/0x38c)[ 311.010495] r5:f3421400 r4:f341b180[ 311.014038] [<c006c260>] (process_one_work+0x0/0x38c) from [<c006e3e8>] (worker_thread+0x160/0x34c)[ 311.023070] [<c006e288>] (worker_thread+0x0/0x34c) from [<c0072654>] (kthread+0x90/0x94)[ 311.031136] [<c00725c4>] (kthread+0x0/0x94) from [<c005b3e0>] (do_exit+0x0/0x674)[ 311.038570] r6:c005b3e0 r5:c00725c4 r4:f3439ee0这Log比较长,耐心点分析:
1、播放开始时,调用流程:
soc_pcm_prepare->
snd_soc_dapm_stream_event->
dapm_power_widgets->
dapm_seq_run->
dapm_seq_run_coalesced->
dapm_seq_check_event->
late_enable_ev;
2、播放结束后,调用流程:
close_delayed_work->
snd_soc_dapm_stream_event->
dapm_power_widgets->
dapm_seq_run->
late_disable_ev。
可能这里有点困惑,close_delayed_work看起来是一个delayed work的处理句柄,那么又是谁调度了这个delayed work呢?答案是soc_codec_close。
下面抽丝剥茧对每个函数进行分析。我会尽可能注明代码的用意,而不是死板的逐行注释。其实更多时候,我们更需要知道的是what/why,而不是how。
3.源码分析
3.1 soc_dapm_stream_event
static void soc_dapm_stream_event(struct snd_soc_dapm_context *dapm,const char *stream, int event){struct snd_soc_dapm_widget *w;list_for_each_entry(w, &dapm->card->widgets, list){if (!w->sname || w->dapm != dapm)continue;dev_dbg(w->dapm->dev, "widget %s\n %s stream %s event %d\n",w->name, w->sname, stream, event);if (strstr(w->sname, stream)) {switch(event) {case SND_SOC_DAPM_STREAM_START:w->active = 1;break;case SND_SOC_DAPM_STREAM_STOP:w->active = 0;break;case SND_SOC_DAPM_STREAM_SUSPEND:case SND_SOC_DAPM_STREAM_RESUME:case SND_SOC_DAPM_STREAM_PAUSE_PUSH:case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:break;}}}dapm_power_widgets(dapm, event);}
这个函数是stream event处理的入口:
1、找到sname和stream name相匹配的widget--这种widget类型一般是DAC/ADC/AIF_IN/AIF_OUT,也只有这四种widgets会有定义sname,具体见soc-dapm.h宏定义;
2、如果音频子流是start状态,则置其active标志为1;如果音频子流是stop状态,则置active标志为0;这个active标志会在遍历complete path时用到,请留意这一点;
3、最后进入dapm_power_widgets处理。可见,stream event和amixer一样,都会触发对所有的dapm widgets的遍历、响应。
3.2 dapm_power_widgets
/* * 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_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);LIST_HEAD(async_domain);int power;trace_snd_soc_dapm_start(card);list_for_each_entry(d, &card->dapm_list, list)if (d->n_widgets || d->codec == NULL)d->dev_power = 0;// 遍历所有dapm widgets,寻找一条完整的音频路径complete audio path/* Check which widgets we need to power and store them in * lists indicating if they should be powered up or down. */list_for_each_entry(w, &card->widgets, list) {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:if (!w->power_check)continue;// 检查widget的force标志,当驱动对某pin强制enable时,那么该pin对应的widget会一直维持通电状态,不管是否它处在一个complete path里面if (!w->force)power = w->power_check(w); // 检查该widget是否位于complete path中,如果是,那么需要对它通电,设置通电标志power=1;否则设置通电标志power=0elsepower = 1;if (power)w->dapm->dev_power = 1;if (w->power == power)continue;trace_snd_soc_dapm_widget_power(w, power);if (power)dapm_seq_insert(w, &up_list, true); // 通电标志power=1,把该widget插入up_list链表,该链表上的所有widgets均需要power up的elsedapm_seq_insert(w, &down_list, false); // 通电标志power=0,把该widget插入down_list链表,该链表上的所有widgets均需要power down的w->power = power;break;}}/* If there are no DAPM widgets then try to figure out power from the * event type. */if (!dapm->n_widgets) {switch (event) {case SND_SOC_DAPM_STREAM_START:case SND_SOC_DAPM_STREAM_RESUME:dapm->dev_power = 1;break;case SND_SOC_DAPM_STREAM_STOP:dapm->dev_power = !!dapm->codec->active;break;case SND_SOC_DAPM_STREAM_SUSPEND:dapm->dev_power = 0;break;case SND_SOC_DAPM_STREAM_NOP:switch (dapm->bias_level) {case SND_SOC_BIAS_STANDBY:case SND_SOC_BIAS_OFF:dapm->dev_power = 0;break;default:dapm->dev_power = 1;break;}break;default:break;}}/* Force all contexts in the card to the same bias state */power = 0;list_for_each_entry(d, &card->dapm_list, list)if (d->dev_power)power = 1;list_for_each_entry(d, &card->dapm_list, list)d->dev_power = power;/* 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链表down_list上的widgets,接着power up链表up_list上的widgets;按照这样的次序,目的是避免产生pop音 /* Power down widgets first; try to avoid amplifying pops. */dapm_seq_run(dapm, &down_list, event, false);dapm_widget_update(dapm); // 这里设置widget的kcontrol,注意只有mux/mixer切换输入源才有效,普通的widgets直接返回/* 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);pop_dbg(dapm->dev, card->pop_time,"DAPM sequencing finished, waiting %dms\n", card->pop_time);pop_wait(card->pop_time);trace_snd_soc_dapm_done(card);return 0;}
该函数是dapm模块最核心的一个函数,它的作用:
1、遍历所有dapm widgets,寻找一条完整的音频路径complete audio path;所谓complete audio path,在这个函数的注释都说得很清楚了,就是如下四种情形:
* o DAC to output pin. * o Input Pin to ADC. * o Input pin to Output pin (bypass, sidetone) * o DAC to ADC (loopback).
complete path涉及到的概念,如input endpoint、output endpoint等请翻到dapm核心分析,里面有详尽的解释;还有如何查找complete path,之前也有解释,这里不累述了;
2、把complete path上面的所有widgets插入到up_list链表中,其他widgets插入到down_list链表中;
3、power down链表down_list上的widgets,然后power up链表up_list上的widgets。
注意:dapm_widget_update用于设置widget kcontrol寄存器,只在mixer/mux部件切换输入源source有效,具体见dapm_mux_update_power和dapm_mixer_update_power函数。如果有必要,在path domain之章会详细阐述这一点。
还有一些枝叶没有一一分析。
3.3 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_dapm_context *dapm, struct list_head *list, int event, bool power_up){struct snd_soc_dapm_widget *w, *n;LIST_HEAD(pending);int cur_sort = -1;int cur_subseq = -1;int cur_reg = SND_SOC_NOPM;struct snd_soc_dapm_context *cur_dapm = NULL;int ret, i;int *sort;if (power_up)sort = dapm_up_seq;elsesort = dapm_down_seq;list_for_each_entry_safe(w, n, list, power_list) {ret = 0;/* Do we need to apply any queued changes? */// 将pending链表上所有的widgets通电/断电,注意:该链表上的所有widgets要设置的寄存器(widget register)、通电次序(power sequence)均是一样的。在保证POPs有效处理基础上,实现寄存器的最少次数的读写if (sort[w->id] != cur_sort || w->reg != cur_reg || w->dapm != cur_dapm || w->subseq != cur_subseq) {if (!list_empty(&pending))dapm_seq_run_coalesced(cur_dapm, &pending);if (cur_dapm && cur_dapm->seq_notifier) {for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++)if (sort[i] == cur_sort)cur_dapm->seq_notifier(cur_dapm, i, cur_subseq);}INIT_LIST_HEAD(&pending); // 重新初始化pending链表,为下个widget准备cur_sort = -1;cur_subseq = -1;cur_reg = SND_SOC_NOPM;cur_dapm = NULL;} 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); // widget event句柄处理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;default:/* Queue it up for application */cur_sort = sort[w->id];cur_subseq = w->subseq;cur_reg = w->reg;cur_dapm = w->dapm;list_move(&w->power_list, &pending); // 将遍历到的widget移除到pending链表break;}if (ret < 0)dev_err(w->dapm->dev,"Failed to apply widget power: %d\n", ret);}if (!list_empty(&pending))dapm_seq_run_coalesced(cur_dapm, &pending);if (cur_dapm && cur_dapm->seq_notifier) {for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++)if (sort[i] == cur_sort)cur_dapm->seq_notifier(cur_dapm, i, cur_subseq);}}这个函数的注释写得很清楚,它遍历之前已排序好(dapm_seq_insert)的链表,把1)连续的、2)同一个widget register的、3)同一个power sequence的widgets送到pending链表上,然后调用dapm_seq_run_coalesced对该链表上的widgets进行设置。这样做的目的是尽可能减少对widget registers的读写。
留意power sequence,这是为了有效消除POPs而设置的,我们分别看看power up和power down情况下的次序是怎样的:
/* dapm power sequences - make this per codec in the future */static int dapm_up_seq[] = {[snd_soc_dapm_pre] = 0,[snd_soc_dapm_supply] = 1,[snd_soc_dapm_micbias] = 2,[snd_soc_dapm_aif_in] = 3,[snd_soc_dapm_aif_out] = 3,[snd_soc_dapm_mic] = 4,[snd_soc_dapm_mux] = 5,[snd_soc_dapm_virt_mux] = 5,[snd_soc_dapm_value_mux] = 5,[snd_soc_dapm_dac] = 6,[snd_soc_dapm_mixer] = 7,[snd_soc_dapm_mixer_named_ctl] = 7,[snd_soc_dapm_pga] = 8,[snd_soc_dapm_adc] = 9,[snd_soc_dapm_out_drv] = 10,[snd_soc_dapm_hp] = 10,[snd_soc_dapm_spk] = 10,[snd_soc_dapm_line] = 10,[snd_soc_dapm_post] = 11,};static int dapm_down_seq[] = {[snd_soc_dapm_pre] = 0,[snd_soc_dapm_adc] = 1,[snd_soc_dapm_hp] = 2,[snd_soc_dapm_spk] = 2,[snd_soc_dapm_line] = 2,[snd_soc_dapm_out_drv] = 2,[snd_soc_dapm_pga] = 4,[snd_soc_dapm_mixer_named_ctl] = 5,[snd_soc_dapm_mixer] = 5,[snd_soc_dapm_dac] = 6,[snd_soc_dapm_mic] = 7,[snd_soc_dapm_micbias] = 8,[snd_soc_dapm_mux] = 9,[snd_soc_dapm_virt_mux] = 9,[snd_soc_dapm_value_mux] = 9,[snd_soc_dapm_aif_in] = 10,[snd_soc_dapm_aif_out] = 10,[snd_soc_dapm_supply] = 11,[snd_soc_dapm_post] = 12,};可知:power up时,通电次序是从input endpoint到output endpoint的,power down时,则刚好相反。
3.4 dapm_seq_run_coalesced
/* Apply the coalesced changes from a DAPM sequence */static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm, struct list_head *pending){struct snd_soc_card *card = dapm->card;struct snd_soc_dapm_widget *w;int reg, power;unsigned int value = 0;unsigned int mask = 0;unsigned int cur_mask;reg = list_first_entry(pending, struct snd_soc_dapm_widget, power_list)->reg;// 从之前的分析可知,pending链表上的widgets均是对同一个register操作的,所以在这里遍历链表,整理每个widget要设置的值和掩码list_for_each_entry(w, pending, power_list) {cur_mask = 1 << w->shift;BUG_ON(reg != w->reg);if (w->invert)power = !w->power;elsepower = w->power;mask |= cur_mask;if (power)value |= cur_mask;pop_dbg(dapm->dev, card->pop_time,"pop test : Queue %s: reg=0x%x, 0x%x/0x%x\n",w->name, reg, value, mask);/* Check for events */dapm_seq_check_event(dapm, w, SND_SOC_DAPM_PRE_PMU); // widget pre event句柄处理dapm_seq_check_event(dapm, w, SND_SOC_DAPM_PRE_PMD);}if (reg >= 0) {pop_dbg(dapm->dev, card->pop_time,"pop test : Applying 0x%x/0x%x to %x in %dms\n",value, mask, reg, card->pop_time);pop_wait(card->pop_time);snd_soc_update_bits(dapm->codec, reg, mask, value); // 设置widget register}list_for_each_entry(w, pending, power_list) {dapm_seq_check_event(dapm, w, SND_SOC_DAPM_POST_PMU); // widget post event句柄处理dapm_seq_check_event(dapm, w, SND_SOC_DAPM_POST_PMD);}}因为pending链表上的widgets均是对同一个regiser进行操作的,所以整理每个widget需要设置的register bits,然后一次性设置widget register。留意:widget pre event是在widget register设置之前执行的,widget post event是在widget register设置之后执行的。
3.5 dapm_seq_check_event
static void dapm_seq_check_event(struct snd_soc_dapm_context *dapm, struct snd_soc_dapm_widget *w, int event){struct snd_soc_card *card = dapm->card;const char *ev_name;int power, ret;switch (event) {case SND_SOC_DAPM_PRE_PMU:ev_name = "PRE_PMU";power = 1;break;case SND_SOC_DAPM_POST_PMU:ev_name = "POST_PMU";power = 1;break;case SND_SOC_DAPM_PRE_PMD:ev_name = "PRE_PMD";power = 0;break;case SND_SOC_DAPM_POST_PMD:ev_name = "POST_PMD";power = 0;break;default:BUG();return;}if (w->power != power)return;if (w->event && (w->event_flags & event)) {pop_dbg(dapm->dev, card->pop_time, "pop test : %s %s\n",w->name, ev_name);trace_snd_soc_dapm_widget_event_start(w, event);ret = w->event(w, NULL, event);trace_snd_soc_dapm_widget_event_done(w, event);if (ret < 0)pr_err("%s: %s event failed: %d\n", ev_name, w->name, ret);}}这个真没什么可说的,进入widget event处理句柄。
4.个人对widget event的理解
通篇说了那么多widget event,还没有涉及一个关键问题:widget event究竟是什么,它为了什么而存在?
我们先看dapm.txt是如何描述的:
5 DAPM Widget Events====================Some widgets can register their interest with the DAPM core in PM events.e.g. A Speaker with an amplifier registers a widget so the amplifier can bepowered only when the spk is in use./* turn speaker amplifier on/off depending on use */static int corgi_amp_event(struct snd_soc_dapm_widget *w, int event){gpio_set_value(CORGI_GPIO_APM_ON, SND_SOC_DAPM_EVENT_ON(event));return 0;}/* corgi machine dapm widgets */static const struct snd_soc_dapm_widget wm8731_dapm_widgets =SND_SOC_DAPM_SPK("Ext Spk", corgi_amp_event);Please see soc-dapm.h for all other widgets that support events.这里面有个例子,当使用spk时,会调用这个event从而控制GPIO打开功放(amplifier)。
我个人的理解是:widget event是对部件(widget)开关的一种时序控制。比如说,某些DAC通电或断电前后,可能需要对其它寄存器进行一些设置,否则DAC工作会有问题。这就是所谓的硬件通断电时序,widget event就是为了迎合这种需要而设置的。
soc-dapm.h里面定义的event类型有:
/* dapm event types */#define SND_SOC_DAPM_PRE_PMU0x1 /* before widget power up */#define SND_SOC_DAPM_POST_PMU0x2/* after widget power up */#define SND_SOC_DAPM_PRE_PMD0x4 /* before widget power down */#define SND_SOC_DAPM_POST_PMD0x8/* after widget power down */#define SND_SOC_DAPM_PRE_REG0x10/* before audio path setup */#define SND_SOC_DAPM_POST_REG0x20/* after audio path setup */#define SND_SOC_DAPM_PRE_POST_PMD \(SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD)
- DAPM之八:stream domain触发过程分析
- DAPM之五:dapm机制深入分析(上)
- DAPM之六:dapm机制深入分析(下)
- DAPM之五:dapm机制深入分析(上)
- DAPM之六:dapm机制深入分析(下)
- DAPM之五:dapm机制深入分析(上)
- DAPM之四:dapm机制深入分析(上)
- DAPM之五:dapm机制深入分析(下)
- DAPM之四:dapm机制深入分析(上)
- DAPM之五:dapm机制深入分析(下)
- DAPM之三:dapm机制深入分析(上)
- DAPM之四:dapm机制深入分析(下)
- DAPM之五:dapm机制深入分析(上)
- DAPM之六:dapm机制深入分析(下)
- DAPM之五:dapm机制深入分析(上)
- DAPM之六:dapm机制深入分析(下)
- socket的编程过程(Internet Domain, stream type)
- OOA/D的统一构建(UP)过程之二:需求分析阶段之Domain model
- SilverLight Tools 安装错误“必须先安装与Silverlight Tools 4 语言版本相一致的....”解决方法
- 第十五周上机任务项目2-建立专门的链表类处理有关动态链表的操作
- VC程序把汉字写入文件
- flex注册监听器带额外参数的解决
- vs2008调试 Release 工程
- DAPM之八:stream domain触发过程分析
- ctags的应用
- 用issnode+IIS来托管NodeJs Server之二:移植
- nat 穿透原理
- 广州JAVA基础班新福利!精美教材免费送!
- android-vlc 精简
- MATLAB里面的filter和filtfilt的C语言源代码
- vs2003 序列化json