4. DAPM Route定义:source / control / sink 的含义

DAPM(Dynamic Audio Power Management)路由的核心是 snd_soc_dapm_route 结构体。该结构体用于描述音频数据在 Codec、CPU DAI、DMA 以及外部连接器之间的流动路径。理解 source、control 和 sink 三个字段的含义,是进行 DAPM 路由调试的基础。

4.1 snd_soc_dapm_route 结构体解析

在 Linux 内核源码 include/sound/soc-dapm.h 中,该结构体定义如下:

struct snd_soc_dapm_route {
    const char *sink;      /* 路径终点 widget 名称 */
    const char *control;   /* 可选:控制该路径的 mixer/mux 控件名称,NULL 表示直连 */
    const char *source;    /* 路径起点 widget 名称 */

    /* 注:实际内核中还有后续字段,但驱动开发中最常用的是以上三个 */
};

三个字段构成了一个有向边:音频数据从 source 流出,经过可选的 control 开关,最终流入 sink。

4.2 三字段含义详解

字段 含义 典型值举例 说明
source 音频路径的起点 Widget "DAC", "DMIC", "I2S Input" 数据生产者,可以是 ADC、DMA 缓冲区、外部引脚输入等
control 路径上的开关/混音器控件 "Left Mixer", "Mux Select", NULL 若为 NULL 表示直连;若指定控件名,则路径通断受该控件状态控制
sink 音频路径的终点 Widget "HP Out", "Speaker", "ADC" 数据消费者,可以是耳机放大器、扬声器、ADC 输入等
关键理解: source 和 sink 都是 DAPM widget 的名称字符串,必须与 snd_soc_dapm_widget 定义中的 .name 字段完全匹配(包括大小写)。control 字段对应 mixer 或 mux 类型的 widget 名称,用于实现条件路由。

4.3 路由方向与数据流

DAPM 路由是有向的。数据流方向固定为:

source  →  [control]  →  sink

在音频回放(playback)路径中,source 通常是 DAC 或 AIF,sink 是输出引脚。在录音(capture)路径中,source 是输入引脚或 MIC,sink 是 ADC 或 AIF。驱动开发者必须按照实际硬件信号流向填写路由表。

常见错误: 新手常将 playback 路径的 source 和 sink 写反,导致 DAPM 无法正确上电。例如将 "HP Out" 作为 source,"DAC" 作为 sink,这会导致音频路径无法建立。

4.4 路由定义示例:三字段组合

以下是一个典型 Codec 的路由表片段,展示了三种常见组合:

static const struct snd_soc_dapm_route audio_paths[] = {
    /* 1. 直连路径:control = NULL */
    { "HP Out", NULL, "HP Amp" },

    /* 2. 受 mixer 控件控制的路径 */
    { "Left Mixer", "Left DAC Switch", "Left DAC" },

    /* 3. 受 mux 控件控制的路径 */
    { "ADC Mux", "DMIC Select", "DMIC" },
};
行号 source control sink 含义
1 "HP Amp" NULL "HP Out" HP Amp 直连到 HP Out,只要 HP Amp 上电,路径即通
2 "Left DAC" "Left DAC Switch" "Left Mixer" Left DAC 通过 Left DAC Switch 控件连接到 Left Mixer,控件值为 1 时路径导通
3 "DMIC" "DMIC Select" "ADC Mux" DMIC 通过 DMIC Select mux 控件连接到 ADC Mux,mux 选择 DMIC 时路径导通

4.5 路由与 DAPM 上电/下电的关系

DAPM 引擎在构建完整音频路径时,会遍历所有注册的路由表。只有当一条路径上的所有 widget 都处于上电状态,且 control 控件处于开启状态时,该路径才被认为是激活的。DAPM 会沿着激活路径递归上电所有涉及的 widget。

调试要点: 如果某个 widget 无法上电,请检查其 source 或 sink 方向的路由是否定义正确。使用 /sys/kernel/debug/asoc/<card>/dapm 可以查看当前所有 widget 的状态和连接关系。

4.6 路由定义中的 control 字段深入

control 字段并非任意字符串,它必须对应一个已注册的 mixer 或 mux 控件的名称。具体规则如下:

  • Mixer 控件: control 字符串必须与 mixer widget 中 snd_kcontrol_new 定义的 .name 一致。例如 "Left DAC Switch" 对应一个开关型 mixer 控件。
  • Mux 控件: control 字符串必须与 mux widget 中 snd_kcontrol_new 定义的 .name 一致。例如 "DMIC Select" 对应一个枚举型 mux 控件。
  • NULL: 表示无控制条件,路径始终跟随 source widget 的上电状态。

当 control 不为 NULL 时,DAPM 会在运行时检查该控件的当前值。对于 mixer,值为非零时路径导通;对于 mux,值等于该路由对应的枚举索引时路径导通。

4.7 路由定义与 DAPM 图的关系

下图展示了一个简化的 Codec 内部路由拓扑,标注了 source、control、sink 三字段在 DAPM 图中的位置:

DAPM 路由三字段在音频路径中的位置 source "Left DAC" control "Left DAC Switch" sink "Left Mixer" 数据流 数据流 路由定义: { "Left Mixer", "Left DAC Switch", "Left DAC" } 含义:Left DAC 通过 Left DAC Switch 控件连接到 Left Mixer "HP Amp" NULL "HP Out" 直连路径:{ "HP Out", NULL, "HP Amp" }

4.8 路由注册与调试检查点

路由表通过 snd_soc_dapm_add_routes() 函数注册到 DAPM 核心:

static int my_codec_probe(struct snd_soc_component *component) {
    struct snd_soc_card *card = component->card;

    snd_soc_dapm_add_routes(card, audio_paths, ARRAY_SIZE(audio_paths));

    return 0;
}

调试时,可以通过以下方法验证路由是否正确注册:

  • debugfs 检查: 查看 /sys/kernel/debug/asoc/<card>/dapm 中每个 widget 的 connected 状态。
  • dmesg 日志: 开启 DAPM 调试日志 # echo 'file sound/soc/soc-dapm.c +p' > /sys/kernel/debug/dynamic_debug/control,观察路由查找过程。
  • 控件值验证: 使用 amixer cget 检查 control 字段对应的 mixer/mux 控件当前值是否符合预期。
最佳实践: 在编写路由表时,建议按照数据流方向分组注释,将 playback 路径和 capture 路径分开定义。每个路由定义前添加注释说明该路径的物理意义,便于后期调试和维护。