4. 设备树与音频设备:设备树节点定义、compatible属性、reg与interrupts属性解析

在嵌入式 Linux 音频驱动开发中,设备树(Device Tree)是连接硬件描述与驱动代码的核心桥梁。本章将深入解析音频设备在设备树中的节点定义方式,重点讲解 compatible、reg 和 interrupts 三个关键属性的语义与用法,帮助你在实际项目中准确描述音频硬件资源。

核心认知:设备树不是驱动代码,而是硬件拓扑的静态描述。驱动通过匹配设备树节点来获取硬件资源(寄存器地址、中断号、DMA通道等),从而实现与具体硬件平台的解耦。

4.1 音频设备树节点基础结构

一个典型的音频设备节点通常包含以下结构:

/ {
    sound {
        compatible = "vendor,audio-codec";
        reg = <0x001a0000 0x1000>;
        interrupts = <0 42 4>;
        clocks = <&clkc 15>;
        #sound-dai-cells = <0>;
    };
};

每个节点由 节点名、属性 和 子节点 组成。节点名通常反映设备类型(如 i2s、codec、dma),而 compatible 属性是驱动匹配的关键。

命名规范:节点名应使用通用名称(如 audio-codec),而非具体型号(如 wm8960)。具体型号信息应放在 compatible 属性中,以保持设备树的通用性。

4.2 compatible 属性:驱动匹配的钥匙

compatible 属性是设备树中最关键的属性之一,它定义了设备与驱动之间的匹配关系。该属性是一个字符串列表,格式为 "厂商,型号",驱动通过 of_match_table 进行匹配。

属性字段 含义 示例
厂商前缀 设备制造商标识,通常使用标准前缀 ti(TI)、nxp(NXP)、realtek
型号标识 具体芯片或IP核型号 tlv320aic3x、wm8960、ak4458
兼容性列表 可指定多个兼容型号,从最具体到最通用 "ti,tlv320aic3106", "ti,tlv320aic3x"

驱动中的匹配表定义示例:

static const struct of_device_id audio_codec_of_match[] = {
    { .compatible = "ti,tlv320aic3106", },
    { .compatible = "ti,tlv320aic3x", },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, audio_codec_of_match);
匹配优先级:设备树中的 compatible 字符串列表按优先级从高到低排列。驱动匹配时,会遍历设备树中的字符串,与 of_match_table 中的条目逐一比较,先匹配到的优先。因此,应将最具体的型号放在最前面。

4.3 reg 属性:寄存器地址与内存映射

reg 属性用于描述设备在系统总线上的地址空间,包括起始地址和长度。对于音频设备,通常用于指定 I2S 控制器、音频编解码器或 DMA 控制器的寄存器基址。

reg 属性的格式为 <地址 长度> 对,可包含多个地址段:

reg = <0x001a0000 0x1000>;  // 单个地址段
reg = <0x001a0000 0x1000      // 第一个地址段:控制寄存器
       0x001b0000 0x0200>;    // 第二个地址段:数据缓冲区

在驱动中,通过 platform_get_resource() 或 of_iomap() 获取并映射这些地址:

struct resource *res;
void __iomem *base;

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
    return PTR_ERR(base);
reg 索引 典型用途 获取方式
0 主控制寄存器基址 platform_get_resource(pdev, IORESOURCE_MEM, 0)
1 FIFO 或数据缓冲区 platform_get_resource(pdev, IORESOURCE_MEM, 1)
2 辅助功能寄存器(如时钟控制) platform_get_resource(pdev, IORESOURCE_MEM, 2)
地址对齐:音频设备的寄存器地址通常要求 4 字节对齐。在设备树中指定 reg 时,应确保起始地址和长度符合硬件手册要求,否则 devm_ioremap_resource() 会返回错误。

4.4 interrupts 属性:中断资源描述

interrupts 属性用于描述设备使用的中断资源,包括中断控制器、中断号和触发类型。其格式依赖于中断控制器的 #interrupt-cells 属性。

对于 ARM GIC(Generic Interrupt Controller),通常使用 3 个 cell:

interrupts = <0 42 4>;
// 第1个cell: 中断类型 (0=SPI, 1=PPI)
// 第2个cell: 中断号 (SPI: 32-1019, PPI: 16-31)
// 第3个cell: 触发类型 (1=上升沿, 2=下降沿, 4=高电平, 8=低电平)

驱动中获取中断号的典型方式:

int irq;

irq = platform_get_irq(pdev, 0);
if (irq < 0)
    return irq;

ret = devm_request_irq(&pdev->dev, irq, audio_isr,
                        IRQF_TRIGGER_HIGH, "audio-codec", dev);
中断类型 cell 值 说明 典型中断号范围
SPI(共享外设中断) 0 多个设备可共享的中断线 32 ~ 1019
PPI(私有外设中断) 1 每个 CPU 核心私有的中断 16 ~ 31
中断号偏移:在设备树中,SPI 中断号通常从 0 开始编号,但 GIC 内部会将 SPI 中断号加上 32。例如,设备树中写 interrupts = <0 42 4>,实际 Linux 内核看到的中断号是 42 + 32 = 74。驱动开发时,应使用 platform_get_irq() 返回的原始值,无需手动偏移。

4.5 音频设备树完整示例:I2S 控制器 + 编解码器

以下是一个完整的音频子系统设备树示例,展示了 I2S 控制器与音频编解码器的节点定义:

/ {
    sound_card {
        compatible = "simple-audio-card";
        simple-audio-card,name = "MyAudioCard";
        simple-audio-card,format = "i2s";
        simple-audio-card,bitclock-master = <&codec>;
        simple-audio-card,frame-master = <&codec>;

        cpu {
            sound-dai = <&i2s0>;
        };

        codec {
            sound-dai = <&codec>;
        };
    };

    i2s0: i2s@001a0000 {
        compatible = "vendor,i2s-controller";
        reg = <0x001a0000 0x1000>;
        interrupts = <0 45 4>;
        clocks = <&clkc 15>, <&clkc 16>;
        clock-names = "bus", "mclk";
        dmas = <&dma 2>, <&dma 3>;
        dma-names = "tx", "rx";
        #sound-dai-cells = <0>;
    };

    codec: audio-codec@001b0000 {
        compatible = "ti,tlv320aic3106", "ti,tlv320aic3x";
        reg = <0x001b0000 0x0200>;
        interrupts = <0 46 4>;
        clocks = <&clkc 17>;
        clock-names = "mclk";
        #sound-dai-cells = <0>;
    };
};
属性复用:音频设备节点中常包含 clocks、dmas 等额外属性,这些属性与 reg、interrupts 共同构成完整的硬件资源描述。驱动通过 of_parse_phandle() 和 of_property_read_u32() 等 API 解析这些属性。

4.6 设备树资源解析流程 SVG 图

下图展示了音频驱动从设备树节点解析硬件资源的完整流程:

音频驱动设备树资源解析流程 设备树节点 sound@001a0000 compatible 匹配 of_match_table reg 解析 platform_get_resource interrupts 解析 platform_get_irq clocks 解析 of_parse_phandle dmas 解析 of_dma_request_slave 硬件资源汇总 寄存器基址 | 中断号 | 时钟句柄 | DMA 通道 驱动 probe 初始化 ioremap | request_irq | clk_prepare_enable

4.7 常见问题与避坑指南

  • compatible 字符串拼写错误:驱动匹配失败是最常见的问题。务必核对设备树中的 compatible 字符串与驱动 of_match_table 中的字符串完全一致(包括大小写)。
  • reg 地址范围重叠:多个设备节点不应使用重叠的 reg 地址范围,否则会导致 ioremap 失败。使用 devm_ioremap_resource() 可自动检测地址冲突。
  • 中断号配置错误:对于 GIC 控制器,SPI 中断号在设备树中从 0 开始,但内核内部会加 32。不要手动调整中断号,始终使用 platform_get_irq() 获取。
  • 缺少 #interrupt-cells:中断控制器节点必须定义 #interrupt-cells 属性,否则设备节点的 interrupts 属性无法被正确解析。
  • 音频设备特有属性遗漏:音频驱动常需要 #sound-dai-cells、clocks、dmas 等属性,这些属性在设备树中缺失会导致 sound card 注册失败。
调试技巧:使用 dtc -I dtb -O dts 反编译设备树二进制文件,检查节点属性是否正确。在驱动中通过 dev_info() 打印解析到的资源值,与设备树中的定义进行比对。

4.8 本章小结

设备树是音频驱动开发中不可或缺的硬件描述语言。通过本章的学习,你应掌握:

  • 音频设备树节点的基本结构,包括节点名、属性和子节点的组织方式。
  • compatible 属性的匹配机制,以及如何编写驱动匹配表。
  • reg 属性的地址空间描述方法,以及驱动中获取寄存器基址的 API。
  • interrupts 属性的中断资源描述,包括 GIC 的中断类型和触发方式。
  • 完整的音频设备树示例,以及常见问题的排查方法。