2. 设备树基础:语法、关键属性与编译

设备树(Device Tree,DT)是描述硬件拓扑与资源分配的一种结构化数据格式,在 ARM、RISC-V 等非 x86 平台中取代了传统的 board-*.c 硬编码方式。KMS 显示驱动依赖设备树来获取显示控制器、HDMI/DP 编码器、I2C 通道、时钟与复位信号等硬件信息。本章从语法、节点/属性、关键属性及编译工具四个维度展开。

学习目标:掌握设备树源文件(.dts)的基本结构,能够独立编写或修改与显示驱动相关的设备树节点,并熟练使用 dtc 工具进行编译与反编译。

2.1 设备树语法基础

设备树源文件采用树形结构,以 /dts-v1/; 开头,根节点 / 包含所有子节点。每个节点代表一个硬件设备或总线,节点内包含若干属性(property)描述资源。

/dts-v1/;

/ {
    model = "Example ARM Board";
    compatible = "vendor,board-name";
    #address-cells = <1>;
    #size-cells = <1>;

    soc {
        compatible = "simple-bus";
        #address-cells = <1>;
        #size-cells = <1>;
        ranges;

        display_controller: display@1e00000 {
            compatible = "vendor,display-controller";
            reg = <0x01e00000 0x1000>;
            interrupts = <0 42 4>;
            clocks = <&clk 5>;
            status = "okay";
        };
    };
};

关键语法规则:

  • 节点名称:格式为 node-name@unit-address,其中 unit-address 通常为寄存器基址。
  • 标签(label):如 display_controller:,用于在其它节点中引用。
  • 属性值:支持 <u32> 数组、"字符串"、[byte 序列]、&phandle 引用。
  • 包含文件:使用 #include "soc.dtsi" 引入 .dtsi 头文件,实现 SoC 级描述复用。
显示驱动常见节点层级:根 → soc → display-controller → hdmi-encoder → i2c-bus(用于 EDID 读取)→ backlight → panel。
设备树节点层级结构(显示驱动相关) / (根节点) soc display-controller hdmi-encoder i2c-bus (EDID) backlight 图例说明 蓝/绿节点:总线与 SoC 层级;红节点:显示控制器;紫节点:编码器; 橙节点:I2C 总线(用于读取 EDID);青节点:背光控制。 箭头表示父子包含关系,实际设备树中通过 phandle 或节点路径引用。

2.2 节点与属性

设备树中每个节点由一对花括号 { } 界定,内部包含属性与子节点。属性是键值对,值类型决定硬件资源的描述方式。

属性名 值类型 说明 显示驱动示例
compatible 字符串列表 设备驱动匹配标识,格式 "厂商,设备型号" "rockchip,rk3399-vop"
reg <u32> 数组 寄存器地址与长度,按 (address, length) 对排列 <0x1e00000 0x1000>
interrupts <u32> 数组 中断号、触发类型(依赖 interrupt-parent) <0 42 4>(SPI 42, 高电平触发)
clocks phandle + 索引 时钟源引用,可带 clock-names 辅助识别 <&clk 5>
status 字符串 设备启用状态:"okay" / "disabled" "okay"
pinctrl-* phandle 列表 引脚复用配置,通常配合 pinctrl-names <&pinctrl_lcd>
常见错误:忘记设置 #address-cells 和 #size-cells 会导致 reg 解析错误;interrupt-parent 缺失会导致中断无法路由到 GIC。

2.3 关键属性详解

2.3.1 compatible —— 驱动匹配的钥匙

内核通过 of_match_table 将设备树节点的 compatible 值与驱动结构体匹配。显示驱动中通常使用两级匹配:

static const struct of_device_id vop_dt_match[] = {
    { .compatible = "rockchip,rk3399-vop" },
    { .compatible = "rockchip,rk3288-vop" },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, vop_dt_match);

2.3.2 reg —— 地址空间描述

reg 属性由 #address-cells 和 #size-cells 决定每个地址/长度对的宽度。对于 32 位 SoC,通常为 <1 1>;对于 64 位 SoC,可能为 <2 2>。

display@1e00000 {
    reg = <0x01e00000 0x1000>;   /* 基址 0x01e00000,长度 0x1000 */
};

2.3.3 interrupts —— 中断资源

中断描述依赖 interrupt-parent 指向中断控制器节点。ARM GIC 中断类型:

  • SPI(Shared Peripheral Interrupt):类型值 0,用于外设,中断号范围 32~1019。
  • PPI(Private Peripheral Interrupt):类型值 1,用于 CPU 本地中断。
  • 触发类型:1=上升沿,2=下降沿,4=高电平,8=低电平。
interrupt-parent = <&gic>;
interrupts = <0 42 4>;  /* SPI 42, 高电平触发 */

2.3.4 clocks / resets —— 时钟与复位

显示驱动通常需要多个时钟源(像素时钟、总线时钟、AHB 时钟)和复位信号。使用 clock-names 和 reset-names 提高可读性:

clocks = <&cru PCLK_HDMI>, <&cru SCLK_HDMI>;
clock-names = "pclk", "sclk";
resets = <&cru SRST_HDMI>;
reset-names = "hdmi";

2.4 设备树编译与反编译

设备树编译器(dtc)将 .dts 源文件编译为 .dtb 二进制文件,也可将 .dtb 反编译为 .dts 用于调试。

命令 说明 示例
dtc -I dts -O dtb -o output.dtb input.dts 编译 .dts → .dtb dtc -I dts -O dtb -o rk3399.dtb rk3399.dts
dtc -I dtb -O dts -o output.dts input.dtb 反编译 .dtb → .dts dtc -I dtb -O dts -o rk3399.dts rk3399.dtb
dtc -I dtb -O dts -o output.dts input.dtb -@ 反编译时保留插件符号 用于调试 overlay 场景
fdtdump input.dtb 快速打印 .dtb 结构 fdtdump rk3399.dtb | head -50
实战技巧:在开发板上使用 dtc -I fs -O dts /sys/firmware/devicetree/base 可以从运行中的内核导出当前设备树,用于对比实际硬件配置与 .dts 源文件是否一致。
设备树编译与反编译流程 .dts 源文件 (文本, 可读) dtc 编译 .dtb 二进制 (二进制, 内核加载) dtc 反编译 .dts 反编译结果 (调试/验证用) 编译与反编译典型场景 • 开发阶段:修改 .dts → dtc 编译 → 替换 .dtb → 重启验证 • 调试阶段:从 /sys/firmware/devicetree/base 导出 → 反编译 → 对比预期配置

2.5 设备树包含与覆盖(Overlay)

实际项目中,SoC 厂商提供 .dtsi 头文件描述芯片内部外设,板级 .dts 文件通过 #include 引入并覆盖或添加节点。显示驱动常需要根据具体面板型号调整时序参数。

// rk3399.dtsi (SoC 级)
#include "rk3399-clocks.dtsi"
&display_controller {
    compatible = "rockchip,rk3399-vop";
    reg = <0x1e00000 0x1000>;
    interrupts = <0 42 4>;
    status = "disabled";   // 默认关闭
};

// my-board.dts (板级)
#include "rk3399.dtsi"
&display_controller {
    status = "okay";       // 板级启用
    rockchip,panel = <&panel>;
};

&i2c4 {
    panel: panel@36 {
        compatible = "boe,nv140fhm-n62";
        reg = <0x36>;
        enable-gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>;
    };
};
Overlay 机制:Linux 4.4+ 支持设备树覆盖(.dtbo),允许在运行时动态加载/卸载设备节点,常用于 FPGA 动态重配置或可插拔显示模块。

2.6 本章小结