Skip to content

DAPM widget 上电:谁判、何时判 ​

内核:NXP BSP Linux 4.9.88本文:普通音频 widget 的上电充要条件,以及 power_check 由谁、在何时调用


目录 ​


1. 本文要回答什么 ​

Linux 什么时候给 DAPM 图上的 widget 上电?w->power_check 被谁调用、在什么条件下跑起来?


2. 名词对齐 ​

词含义(本文用法)
widgetDAPM 节点:DAC / ADC / Mixer / 输出脚等,常和电源位绑定
path两 widget 之间的运行时边(snd_soc_dapm_path);connect 表示是否接通
端点(endpoint)行走时可当作「路径终点」的 widget:is_ep 标明 source/sink,且 connected
complete path从有效 source 端点沿 connect==1 的边走到有效 sink 端点的整条链
power_check每个 widget 上的函数指针:回答「我现在该不该有电」

创建 widget 时按类型挂上不同的 power_check(snd_soc_dapm_new_control_unlocked):Mixer / DAC / ADC / PGA / DAI 等用 dapm_generic_check_power;电源类用 dapm_supply_check_power;少数 always-on 用 dapm_always_on_check_power。


3. 上电充要条件:generic 路径 ​

普通音频块(Mixer、PGA、DAC、ADC、DAI in/out 等)走:

c
/* sound/soc/soc-dapm.c — dapm_generic_check_power */
in  = is_connected_input_ep(w, NULL, NULL);
out = is_connected_output_ep(w, NULL, NULL);
return out != 0 && in != 0;

充要条件(对 generic、且未 force):

  1. 从该 widget 往 输入方向 走,能到达至少一个有效 source 端点(in != 0);
  2. 从该 widget 往 输出方向 走,能到达至少一个有效 sink 端点(out != 0)。

两个方向都走通,说明这个 widget 落在至少一条 complete path 上,于是 new_power 为 1。

行走规则要点(is_connected_ep):

  • 只沿 path->connect == 1 的边前进(开关打开或常通边);
  • 跳过 weak、以及标记为 supply 的边(它们不计入这条「音频 complete path」);
  • 走到标记了对应 is_ep、并且 connected 的 widget,就当作端点停下。

内核注释 complete path 的典型形态(dapm_power_widgets 上方):

text
DAC → 输出脚
输入脚 → ADC
输入脚 → 输出脚(bypass)
DAC → ADC(loopback)

有效端点从哪来(播放直觉):

角色常见来源
sourcePCM START 后,DAI 的 playback widget 被标成 EP_SOURCE 且 active
sinkOUTPUT / HP / SPK 等输出类 widget,且未被 dapm_nc_pin 置成未连接

因此:只调音量、中间 Mixer Switch 断开 → 边 connect=0 → 中间 widget 的 in 或 out 为 0 → 不上电。流没起来(DAI 不是有效端点)同理。


4. power_check:谁调用 ​

w->power_check 没有独立的周期性轮询。唯一封装的入口:

c
static int dapm_widget_power_check(struct snd_soc_dapm_widget *w)
{
	if (w->power_checked)
		return w->new_power;

	if (w->force)
		w->new_power = 1;
	else
		w->new_power = w->power_check(w);   /* ★ 真正调用 */

	w->power_checked = true;
	return w->new_power;
}

谁调 dapm_widget_power_check:

调用者目的
dapm_power_one_widget给某个 dirty widget 算该上还是该下,再 dapm_widget_set_power
dapm_supply_check_powerSUPPLY 判断自己要不要上电时,递归问下游 sink:dapm_widget_power_check(path->sink)

真正扫描上电的是 dapm_power_widgets:只处理标脏的 widget,每个最多算一次该不该上电。进入时先清掉「已经算过」的标记。

text
dapm_power_widgets
  → dapm_reset()                    // 清 power_checked
  → 每个 dirty widget:
        dapm_power_one_widget(w)
          → dapm_widget_power_check(w)
                → w->power_check(w)   // generic / supply / …
          → dapm_widget_set_power(...)

5. 何时跑整次供电扫描 ​

所有「该不该上电」的重算,都汇聚到 dapm_power_widgets(card, event)。常见触发:

时机入口(简化)
PCM 流 START / STOP 等snd_soc_dapm_stream_event → soc_dapm_stream_event → dapm_power_widgets
拨 DAPM Mixer 开关snd_soc_dapm_put_volsw → soc_dapm_mixer_update_power → 改 path->connect 并 dirty → dapm_power_widgets
拨 DAPM MUXsoc_dapm_mux_update_power → 同上
显式同步snd_soc_dapm_sync / _unlocked(probe 结束、jack、改 pin 之后常见)
新建一批 widgetsnd_soc_dapm_new_widgets 末尾直接调用

只 dapm_enable_pin / 改 route 而不 sync 时,注释写明:需要随后的 snd_soc_dapm_sync 才会真正重算上电。模式是 事件驱动 + dirty 列表,不是定时扫全图。


6. 一次 dapm_power_widgets 里发生什么 ​

text
dapm_power_widgets(card, event)
  1. 按 idle_bias_off / suspend 定各 dapm context 的目标 bias 初值
  2. dapm_reset()
  3. 遍历 dapm_dirty → dapm_power_one_widget
        (算 new_power;与旧 power 不同则进 up_list / down_list,并可 dirty 邻居)
  4. 根据仍需上电的 widget 抬高 target_bias(多数音频块要 BIAS_ON)
  5. 统一 card 内 bias(非 idle_bias_off 时)
  6. 先跑断电序列 dapm_seq_run(..., false),再跑上电序列 dapm_seq_run(..., true)
        (写 widget 绑定的电源寄存器,触发 WILL_PMU / PMU 等事件)

注释强调:完整路径是「有有效端点的 route」;扫描的目的就是找出谁在 complete path 上,谁该进上电/断电列表。


7. 两条常见触发链 ​

7.1 开始播放 / 录音(流事件) ​

aplay / arecord 做 prepare 时,内核在 soc_pcm_prepare 里调用 snd_soc_dapm_stream_event(..., STREAM_START),模拟电路在这里上电。关掉设备时走 soc_pcm_close,发出 STREAM_STOP。open 和 trigger 不改 DAPM 供电。

播放关掉后默认再等 5000 毫秒才断电;录音马上断电。

核心:

c
/* soc_dapm_dai_stream_event:STREAM_START 时 */
w->active = 1;
w->is_ep = ep;          /* playback → EP_SOURCE;capture → EP_SINK */
dapm_mark_dirty(w, "stream event");

/* soc_dapm_stream_event 末尾 */
dapm_power_widgets(rtd->card, event);

对播放:DAI 成为有效 source 端点;若输出脚仍是有效 sink,且中间 Mixer 等边 connect==1,则 DAC / Output Mixer / PGA 等 in && out 成立 → 进入上电序列。

7.2 tinymix 拨 route 开关 ​

DAPM 的 put 改完寄存器后走 mixer 更新:

c
/* soc_dapm_mixer_update_power */
soc_dapm_connect_path(path, connect, "mixer update");
/* connect_path 内:path->connect = …;dirty source/sink;invalidate 缓存 */
dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP);

例如打开 Left Output Mixer PCM Playback Switch:对应 path 变为接通 → 若此时流已 START,complete path 被补全 → 沿线 widget 上电。


8. 特例:SUPPLY / force / always_on ​

power_check上电条件(摘要)
dapm_generic_check_powerin != 0 && out != 0(§3)
dapm_supply_check_power存在已接通且需要上电的下游 sink(电源跟着负载走)
dapm_always_on_check_power基本上看 w->connected
w->forcedapm_widget_power_check 短路为恒上电

WM8960 上的 MICB 一类 SUPPLY:麦克风通路上的 ADC/Input 需要电时,才会把偏置拉起来,而不是自己单独构成「DAC→喇叭」那种音频 complete path。


9. 小结 ​

  • 普通 widget 上电,必须同时能连到有效 source 端点与有效 sink 端点(落在 complete path 上)。
  • power_check 只经 dapm_widget_power_check 进入;由 dapm_power_one_widget(及 SUPPLY 递归)在 dapm_power_widgets 扫描 dirty 时调用。
  • 扫描由流事件、DAPM 开关/MUX、snd_soc_dapm_sync、新建 widget 等触发,属事件驱动。
  • 真正写电源寄存器发生在 dapm_seq_run 的上电/断电序列,不在 power_check 函数内部。

附录 A 源码索引 ​

位置内容
sound/soc/soc-dapm.c — dapm_generic_check_power / is_connected_ep上电判定与图行走
同文件 — dapm_widget_power_check / dapm_power_one_widget / dapm_power_widgets调用与扫描
同文件 — soc_dapm_mixer_update_power / soc_dapm_connect_path开关改 connect 后重算
同文件 — soc_dapm_stream_event / soc_dapm_dai_stream_event流 START/STOP 标端点
同文件 — snd_soc_dapm_sync显式触发供电扫描
sound/soc/soc-pcm.c — soc_pcm_prepare / soc_pcm_closeHiFi:STREAM_START / STREAM_STOP
include/sound/soc-dapm.h — snd_soc_dapm_widget / snd_soc_dapm_pathpower_check、connect、is_ep

基于 VitePress 构建