3. DRM 设备树节点结构:display-controller 核心属性

在 DRM 显示驱动框架中,display-controller 节点是设备树中描述显示控制器的核心节点。该节点向内核报告硬件控制器的寄存器基址、中断资源、总线连接关系以及兼容性信息。本节详细解析 display-controller 节点的规范定义、compatible、reg 和 interrupts 属性的标准写法与解析流程。

关键概念: display-controller 节点通常位于 SoC 的 axi/peripheral 总线节点下,是 DRM 驱动 probe 入口的硬件描述依据。一个完整的 display-controller 节点必须至少包含 compatible、reg 和 interrupts 三个属性,否则驱动可能无法正确匹配或申请资源。

3.1 display-controller 节点规范与标准结构

设备树规范(ePAPR / Devicetree Specification)并未强制要求节点名称必须为 "display-controller",但 DRM 子系统及主流 SoC 厂商(如 NXP、Rockchip、Allwinner、Mediatek)均采用 display-controller 作为标准节点名,以便于驱动通过 of_device_is_compatible() 或节点名称进行快速识别。

一个典型的 display-controller 节点结构如下:

display-controller@1e60000 {
    compatible = "vendor,display-controller-version", "vendor,display-controller";
    reg = <0x01e60000 0x10000>;
    interrupts = <0 45 4>;
    interrupt-parent = <&gic>;
    clocks = <&ccu CLK_DISPLAY>;
    resets = <&ccu RST_DISPLAY>;
    status = "okay";
};

节点名称中的 @1e60000 表示该控制器的第一个寄存器基地址,用于辅助地址匹配。节点内部属性按功能可分为三类:

属性类别 属性名 作用 是否必须
标识属性 compatible 驱动匹配与硬件版本识别 必须
资源属性 reg 寄存器地址空间描述 必须
资源属性 interrupts 中断号与触发类型 必须
辅助属性 clocks / resets 时钟与复位控制 按需
状态属性 status 设备启用/禁用 推荐
规范要点: display-controller 节点必须放置在正确的总线地址范围内,且 reg 属性中的地址应与节点名称中的 @address 保持一致。interrupt-parent 若未显式指定,则继承父节点的中断父控制器。
display-controller 节点结构 display-controller@1e60000 compatible reg interrupts clocks 属性说明 • compatible: "vendor,display-controller-version" → 驱动匹配字符串 • reg: <0x01e60000 0x10000> → 基地址 0x01e60000,长度 0x10000 • interrupts: <0 45 4> → SPI 中断 45,高电平触发 • clocks: 引用时钟控制器节点中的显示时钟

3.2 compatible 属性:驱动匹配与硬件版本识别

compatible 属性是设备树中最重要的标识属性,DRM 驱动通过 of_match_table 与 compatible 字符串进行匹配。display-controller 节点的 compatible 通常采用 “厂商,设备型号” 的命名规范,并支持向后兼容的 fallback 字符串。

标准格式:

compatible = "vendor,soc-display-ctrl-v2", "vendor,soc-display-ctrl";

匹配优先级:内核优先使用第一个字符串进行精确匹配;若失败,则依次尝试后续字符串。这种设计允许同一驱动支持多个硬件版本。

厂商前缀 示例 compatible 对应 DRM 驱动
rockchip "rockchip,rk3399-vop" rockchip_drm_vop
allwinner "allwinner,sun8i-de2" sun4i_drm
fsl "fsl,imx6q-ipu" imx_drm
mediatek "mediatek,mt8173-disp-ovl" mediatek_drm
amlogic "amlogic,meson-gxbb-dw-hdmi" meson_drm
常见错误: compatible 字符串中不要包含空格、特殊字符或版本号中的多余点号。例如 "vendor,display-ctrl v1" 是错误的,应写为 "vendor,display-ctrl-v1"。内核匹配是精确字符串比较,任何差异都会导致 probe 失败。
compatible 匹配流程 compatible 属性字符串 字符串 1: 精确匹配 字符串 2: fallback 匹配 字符串 N: 通用匹配 驱动 probe 成功

3.3 reg 属性:寄存器地址空间描述

reg 属性用于描述 display-controller 占用的寄存器地址空间。每个 reg 条目由 地址 和 长度 组成,格式由父节点的 #address-cells 和 #size-cells 决定。

标准格式:

reg = <0x01e60000 0x10000>;

其中:

  • 0x01e60000:寄存器基地址(物理地址)
  • 0x10000:寄存器地址空间长度(64KB)

对于包含多个独立寄存器区域的复杂控制器,reg 可以包含多个条目:

reg = <0x01e60000 0x10000>,
      <0x01e70000 0x1000>,
      <0x01e80000 0x2000>;

内核通过 platform_get_resource(pdev, IORESOURCE_MEM, index) 按索引依次获取每个 reg 区域,index 从 0 开始。

#address-cells #size-cells reg 条目格式 示例
1 1 <addr len> <0x1e60000 0x10000>
2 1 <high_addr low_addr len> <0x0 0x1e60000 0x10000>
1 2 <addr high_len low_len> <0x1e60000 0x0 0x10000>
实践建议: 对于 32 位 SoC,通常使用 #address-cells = <1> 和 #size-cells = <1>。对于 64 位 SoC(如 ARM64),#address-cells = <2> 是常见配置。驱动开发时应检查父节点的 cells 定义,避免 reg 解析错误。
reg 属性解析流程 reg = <addr len> addr: 基地址 len: 地址长度 ioremap 映射 驱动访问寄存器

3.4 interrupts 属性:中断资源描述

interrupts 属性定义 display-controller 使用的中断线。中断描述格式由中断控制器(如 GIC)的 #interrupt-cells 属性决定。ARM 平台最常用的中断控制器是 GIC(Generic Interrupt Controller),其 #interrupt-cells 通常为 3。

标准格式(GIC v2/v3):

interrupts = <0 45 4>;

三个字段的含义:

  • 字段 1(中断类型):0 = SPI(共享外设中断),1 = PPI(私有外设中断)
  • 字段 2(中断号):对于 SPI,中断号范围为 32 ~ 1019;对于 PPI,范围为 16 ~ 31
  • 字段 3(触发类型与极性):1 = 上升沿触发,2 = 下降沿触发,4 = 高电平触发,8 = 低电平触发

对于包含多个中断的 display-controller(如帧完成中断、错误中断、DMA 中断),可以定义多个中断:

interrupts = <0 45 4>,   /* 帧同步中断 */
             <0 46 4>,   /* 错误中断 */
             <0 47 4>;   /* DMA 完成中断 */

内核通过 platform_get_irq(pdev, index) 按索引获取每个中断号,index 从 0 开始。

中断类型 类型值 中断号范围 说明
SPI 0 32 ~ 1019 共享外设中断,多个 CPU 均可接收
PPI 1 16 ~ 31 私有外设中断,仅绑定到特定 CPU
注意: 中断号在设备树中通常是 硬件中断号,而非 Linux IRQ 号。GIC 驱动会在初始化时将硬件中断号映射到 Linux IRQ 空间。对于 SPI,Linux IRQ 号 = 硬件中断号 + 16(GIC 偏移)。驱动开发时直接使用 platform_get_irq 返回的 Linux IRQ 号即可。
interrupts 属性结构 interrupts = <0 45 4> 0: SPI 中断类型 45: 中断号 4: 高电平触发 中断触发类型编码 1: 上升沿 | 2: 下降沿 | 4: 高电平 | 8: 低电平

3.5 完整 display-controller 节点示例

以下是一个完整的 display-controller 节点示例,综合了上述三个核心属性及常用辅助属性:

display-controller@1e60000 {
    compatible = "vendor,display-ctrl-v2", "vendor,display-ctrl";
    reg = <0x01e60000 0x10000>,
          <0x01e70000 0x1000>;
    interrupts = <0 45 4>,
                 <0 46 4>;
    interrupt-parent = <&gic>;
    clocks = <&clkc DISPLAY_CLK>,
             <&clkc DISPLAY_AXI_CLK>;
    clock-names = "pclk", "axi";
    resets = <&rstc DISPLAY_RST>;
    status = "okay";

    ports {
        #address-cells = <1>;
        #size-cells = <0>;
        port@0 {
            reg = <0>;
            display_out: endpoint {
                remote-endpoint = <&hdmi_in>;
            };
        };
    };
};
调试技巧: 当 display-controller 节点 probe 失败时,首先检查 /sys/firmware/devicetree/base/ 下对应节点的属性值是否与设备树源文件一致。使用 dtc -I dtb -O dts 反编译 DTB 文件,验证 compatible、reg、interrupts 是否被正确编译。

通过本节的学习,你应该能够:

  • 理解 display-controller 节点的标准结构和命名规范
  • 正确编写 compatible 属性,支持多版本硬件匹配
  • 准确配置 reg 属性,描述寄存器地址空间
  • 合理设置 interrupts 属性,定义中断资源
  • 将三个核心属性组合成完整的 display-controller 节点