4. 设备树基础

在 DRM 驱动移植过程中,设备树是内核与硬件之间的“硬件说明书”。它把 SoC 内部控制器、外部显示桥、面板、背光、电源等连接关系,以可读的树形结构描述出来。驱动通过 of_* 接口读取这些描述,完成资源申请、时钟使能、中断注册和显示链路绑定。理解 DTS/DTSI 语法与显示相关属性,是后续调试链路能否走通的前提。

一、DTS 与 DTSI:源文件组织与编译关系

DTS(Device Tree Source)是具体板级的设备树源文件,DTSI 是被包含的公共描述文件,通常由 SoC 厂商提供,描述 CPU、总线、GPIO、时钟、中断控制器等 SoC 级资源。板级 DTS 通过 #include 引入 DTSI,再对节点进行覆盖或追加,形成最终设备树。编译流程为:DTS/DTSI 经 DTC 编译为 DTB,由 Bootloader 传给内核并展开为 /proc/device-tree 或 /sys/firmware/devicetree。

// 典型板级 DTS 结构
#include "soc.dtsi"
#include "display-panel.dtsi"

/ {
    model = "Demo Board V1";
    compatible = "vendor,demo-board", "vendor,soc";

    backlight: backlight {
        compatible = "pwm-backlight";
        pwms = <&pwm0 0 20000 0>;
        brightness-levels = <0 4 8 16 32 64 128 255>;
        default-brightness-level = <6>;
    };
};

DTSI 中常使用 /delete-node/、/delete-property/ 或标签覆盖来修改默认配置。板级文件应尽量只描述“与参考设计不同”的部分,避免整段复制,否则后续 SoC DTSI 升级会带来大量冲突。

二、节点与属性:设备树的基本语法单元

节点用 node-name@unit-address 表示,unit-address 通常取寄存器基地址,用于区分同一节点名下的多个实例。属性是键值对,值可以是字符串、u32 数组、字节序列或 phandle。常见标准属性包括:

  • compatible:驱动匹配的关键,格式为 "厂商,型号",可多个按优先级排列。
  • reg:寄存器地址与长度,格式为 <地址 长度>,地址和长度单元格数由父节点 #address-cells、#size-cells 决定。
  • status:"okay" 或 "disabled",用于使能/关闭节点。
  • interrupts、interrupt-parent:中断号与中断控制器引用。
  • clocks、clock-names:时钟引用与名称对应关系。
  • power-domains、resets:电源域与复位资源。
节点名描述“是什么”,unit-address 描述“在哪里”,compatible 描述“用什么驱动”,reg 描述“占哪段地址”。四者配合,驱动才能完成 probe。

三、phandle 引用:把分散的硬件连成链路

phandle 是设备树中节点的唯一数字标识。给节点加标签(label)后,其他节点可用 &label 引用它,DTC 编译时自动生成 phandle。显示链路中,CRTC、编码器、桥、面板、背光之间正是通过 phandle 建立连接。

panel: panel@0 {
    compatible = "vendor,panel-1080p";
    reg = <0>;
    backlight = <&backlight>;
    power-supply = <&vcc_lcd>;
    port {
        panel_in: endpoint {
            remote-endpoint = <&dsi_out>;
        };
    };
};

&dsi {
    status = "okay";
    port {
        dsi_out: endpoint {
            remote-endpoint = <&panel_in>;
        };
    };
};

驱动侧通过 of_parse_phandle()、of_find_node_by_phandle() 或 of_graph_get_remote_endpoint() 解析引用,从而拿到对端设备节点。若 phandle 指向的节点 status = "disabled",链路绑定会失败,这是显示不亮的常见原因之一。

四、地址与中断描述:reg、ranges 与 interrupts

地址描述依赖父节点的 #address-cells 和 #size-cells。例如 32 位地址、32 位长度时,reg = <0xfe0e0000 0x1000> 表示从 0xfe0e0000 起 4KB 寄存器空间。若存在地址转换,还需 ranges 描述子总线到父总线的映射。中断描述则通过 interrupt-parent 指定中断控制器,interrupts 给出中断号和触发类型,如 <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>。

属性作用显示场景示例
reg寄存器基址与长度DSI 控制器 0xfe0e0000,长度 0x1000
interrupts中断号与触发方式VBlank 中断、DSI 错误中断
clocks / clock-names时钟引用与命名"pclk"、"hsclk"、"dsi_phy"
physPHY 引用MIPI D-PHY / C-PHY
ports / endpoint显示链路连接CRTC → DSI → Panel

五、显示相关常用属性与链路绑定

DRM 显示子系统普遍使用 OF graph 描述链路:每个设备节点下用 port 和 endpoint 表示输入/输出端口,remote-endpoint 指向对端 endpoint。驱动通过 drm_of_find_panel_or_bridge() 找到面板或桥,再注册到 DRM 框架。常见显示属性还包括:

  • data-lanes:MIPI DSI 通道数,如 <1 2 3 4>。
  • lane-polarities:差分对极性翻转。
  • panel-timing 或 display-timings:像素时钟、H/V 前后沿、同步脉宽。
  • backlight:背光 phandle,供 panel 驱动调节亮度。
  • enable-gpios、reset-gpios:面板使能与复位引脚。
display-timings {
    native-mode = <&timing0>;
    timing0: timing0 {
        clock-frequency = <148500000>;
        hactive = <1920>;
        vactive = <1080>;
        hfront-porch = <88>;
        hback-porch = <148>;
        hsync-len = <44>;
        vfront-porch = <4>;
        vback-porch = <36>;
        vsync-len = <5>;
    };
};

六、误区与复盘:设备树调试的常见坑

第一,compatible 拼写错误或与驱动 of_match_table 不一致,导致驱动根本不 probe。第二,reg 地址与 SoC 手册不符,或 #address-cells 配置错误,导致地址解析偏移。第三,phandle 引用指向 disabled 节点,链路绑定失败。第四,status 未从 disabled 改为 okay,节点被内核忽略。第五,endpoint 的 remote-endpoint 未成对出现,OF graph 无法闭环。排查时可用 ls /proc/device-tree、cat /sys/kernel/debug/device_component 或 dmesg | grep -i drm 观察绑定结果。

建议在板级 DTS 中只覆盖必要属性,保持与 SoC DTSI 的差异最小;显示链路调试时,先确认设备树节点全部 okay,再检查 phandle 与 endpoint 是否闭环,最后核对时序参数与硬件手册是否一致。
设备树显示链路:DTS/DTSI → 节点属性 → phandle → OF graph 绑定 DTSI(SoC 公共) soc.dtsi / display.dtsi CPU、GIC、时钟、GPIO DTS(板级) board.dts 覆盖/追加显示节点 DTC 编译 dtc -I dts -O dtb 生成 .dtb 内核展开 /proc/device-tree of_* 接口解析 DRM 驱动 probe compatible 匹配 reg / clk / irq 申请 OF graph 绑定 CRTC reg / interrupts DSI 控制器 data-lanes / phys Panel display-timings Backlight pwms / brightness phandle remote-endpoint backlight 关键检查点: ① compatible 与驱动 of_match_table 一致 ② reg 地址与手册一致 ③ status = "okay" ④ phandle 指向的节点已使能 ⑤ endpoint 成对闭环 ⑥ 时序参数与面板规格一致

掌握以上语法与属性后,设备树就不再是“看不懂的文本”,而是可以按图索骥的硬件连接图。后续调试显示链路时,先看设备树节点是否完整、phandle 是否闭环、时序是否匹配,再结合驱动日志定位问题,效率会显著提升。