4. SA8155 DRM驱动初始化:msm_drm_init流程、设备树(DTS)解析、MDSS驱动加载顺序

本章深入分析SA8155平台DRM子系统的启动流程,重点围绕msm_drm_init函数执行路径、设备树(DTS)解析机制以及MDSS(Mobile Display Subsystem)驱动的加载顺序展开。理解这一初始化链路是后续进行HDR显示配置与调试的基础。

核心要点:SA8155的DRM驱动初始化遵循“平台驱动绑定 → DTS解析 → 组件化注册 → CRTC/Encoder/Connector创建”的典型路径,其中MDSS作为顶层父设备,其加载顺序直接影响子模块的可用性。

4.1 msm_drm_init 主流程

msm_drm_init 是SA8155 DRM驱动的入口函数,位于 drivers/gpu/drm/msm/msm_drv.c。该函数负责完成DRM设备的注册、组件绑定以及核心对象的初始化。

以下为 msm_drm_init 的主要执行步骤:

  1. 平台驱动探测:当DTS中匹配到 qcom,mdss 节点时,触发 mdss_probe,进而调用 msm_drm_init。
  2. DRM设备分配:调用 drm_dev_alloc 分配 drm_device 结构体,并设置驱动私有数据。
  3. 组件绑定:通过 component_bind_all 绑定MDSS下的所有子组件(如DPU、DSI、HDMI等)。
  4. 模式配置初始化:调用 msm_drm_modeset_init,创建CRTC、Encoder、Connector和Plane。
  5. GPU初始化:如果平台包含GPU节点,则初始化GPU子系统。
  6. DRM设备注册:调用 drm_dev_register 将设备注册到内核,并创建对应的字符设备。
关键函数调用链:
msm_drm_init() → component_bind_all() → msm_drm_modeset_init() → drm_dev_register()
msm_drm_init 主流程 平台驱动探测 (mdss_probe) DRM设备分配 (drm_dev_alloc) 组件绑定 (component_bind_all) 模式配置初始化 (msm_drm_modeset_init) GPU初始化 (msm_gpu_init) DRM设备注册 (drm_dev_register) 说明:组件绑定失败将导致整个DRM设备初始化中止,需检查DTS中各个子节点的状态与依赖。

4.2 设备树(DTS)解析

SA8155平台使用设备树来描述硬件拓扑。DRM驱动通过解析DTS获取显示控制器的寄存器地址、中断号、时钟频率、接口类型等关键参数。核心节点位于 qcom,mdss 下。

以下为SA8155显示相关DTS节点的典型结构:

// SA8155 MDSS DTS 节点示例
mdss: mdss@ae00000 {
    compatible = "qcom,mdss";
    reg = <0x0ae00000 0x1000>;
    reg-names = "mdss_phys";

    clocks = <&gcc GCC_DISP_AHB_CLK>,
             <&gcc GCC_DISP_AXI_CLK>;
    clock-names = "iface", "bus";

    #address-cells = <1>;
    #size-cells = <1>;
    ranges;

    dpu: dpu@ae01000 {
        compatible = "qcom,sdm845-dpu";
        reg = <0x0ae01000 0x8f000>,
              <0x0aeb0000 0x2008>;
        reg-names = "mdp_phys", "vbif_phys";
        interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>;
        clocks = <&gcc GCC_DISP_MDP_CLK>;
        clock-names = "bus";
    };

    dsi0: dsi@ae94000 {
        compatible = "qcom,mdss-dsi-ctrl";
        reg = <0x0ae94000 0x400>;
        reg-names = "dsi_ctrl";
        clocks = <&gcc GCC_DISP_BYTE0_CLK>;
        clock-names = "byte";
        phys = <&dsi0_phy>;
        phy-names = "dsi";
    };

    hdmi: hdmi@ae90000 {
        compatible = "qcom,mdss-hdmi-tx";
        reg = <0x0ae90000 0x10000>;
        interrupts = <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>;
        hpd-gpios = <&tlmm 34 GPIO_ACTIVE_HIGH>;
    };
};

DTS解析的关键函数与对应关系如下表:

DTS节点 解析函数 获取的资源
mdss mdss_probe() 顶层寄存器、时钟、中断
dpu dpu_bind() MDP寄存器、VBIF、中断
dsi0 / dsi1 msm_dsi_host_init() DSI控制器寄存器、PHY、时钟
hdmi msm_hdmi_bind() HDMI TX寄存器、HPD GPIO
常见问题:DTS中 status = "disabled" 的节点不会被驱动探测。若DSI或HDMI接口未使能,需检查对应节点的status属性是否设置为 "okay"。
DTS 节点解析与驱动绑定关系 mdss (qcom,mdss) dpu (qcom,sdm845-dpu) dsi0 (qcom,mdss-dsi-ctrl) hdmi (qcom,mdss-hdmi-tx) DTS 解析要点 • 每个子节点必须包含 compatible 属性,驱动通过 of_match_table 匹配。 • reg 属性定义寄存器地址范围,驱动使用 devm_ioremap_resource 映射。 • interrupts 属性定义中断号,驱动使用 devm_request_irq 注册中断处理函数。 • clocks 与 clock-names 属性定义时钟源,驱动使用 clk_get 获取时钟句柄。

4.3 MDSS驱动加载顺序

MDSS(Mobile Display Subsystem)是SA8155显示子系统的顶层硬件模块,包含DPU、DSI、HDMI、DP等多个IP核。驱动的加载顺序遵循先父后子、先依赖后被依赖的原则。

加载顺序由 component 框架保证,具体流程如下:

  • 步骤1:平台总线匹配到 qcom,mdss 节点,执行 mdss_probe(),注册MDSS顶层设备。
  • 步骤2:MDSS驱动调用 of_platform_populate(),扫描DTS中子节点,触发子设备的平台驱动探测。
  • 步骤3:各子设备驱动(DPU、DSI、HDMI等)在 probe 中调用 component_add(),将自己注册为组件。
  • 步骤4:当所有必需的组件都添加完成后,component_master_ops.bind 回调被触发,执行 msm_drm_init 中的组件绑定。

下表总结了各子模块的加载顺序与依赖关系:

加载顺序 模块名称 依赖关系 初始化函数
1 MDSS (顶层) 无 mdss_probe()
2 DPU (显示处理单元) MDSS时钟、寄存器 dpu_bind()
3 DSI PHY DPU接口 msm_dsi_phy_probe()
4 DSI 控制器 DSI PHY msm_dsi_host_init()
5 HDMI TX DPU接口 msm_hdmi_bind()
6 DisplayPort DPU接口 msm_dp_bind()
调试技巧:在 /sys/kernel/debug/component 下可以查看当前系统中所有组件的注册状态。如果某个组件未注册,DRM初始化将卡在 component_bind_all 处。
MDSS 驱动加载顺序与组件依赖 ① MDSS 顶层 ② DPU 显示处理单元 ③ DSI PHY ④ DSI 控制器 ⑤ HDMI TX ⑥ DisplayPort 加载顺序关键点 • MDSS 顶层驱动必须先于所有子设备加载,提供总线时钟与寄存器访问基础。 • DPU 是显示处理核心,DSI/HDMI/DP 等接口模块依赖 DPU 提供的接口时序。 • 组件框架确保所有子设备就绪后,才执行 DRM 核心初始化,避免竞态条件。

4.4 组件化绑定与回调机制

SA8155 DRM驱动使用Linux内核的 component 框架来管理多个子设备之间的依赖关系。该框架通过 component_master 和 component 两个角色实现解耦。

关键数据结构与回调函数如下:

// 组件主设备结构体
static const struct component_master_ops msm_drm_ops = {
    .bind = msm_drm_bind,
    .unbind = msm_drm_unbind,
};

// 子组件添加示例 (DPU)
static const struct component_ops dpu_component_ops = {
    .bind   = dpu_bind,
    .unbind = dpu_unbind,
};

// 在 dpu_probe 中注册组件
static int dpu_probe(struct platform_device *pdev)
{
    // ... 初始化硬件资源 ...
    return component_add(&pdev->dev, &dpu_component_ops);
}

组件绑定流程的时序图如下:

组件绑定时序图 时间 mdss_probe() component_add() component_bind_all() 组件绑定注意事项 • 每个子设备必须在 probe 中调用 component_add,否则主设备无法完成绑定。 • 如果某个子设备 probe 失败,component_bind_all 将返回 -EPROBE_DEFER,等待重试。 • 可以通过 /sys/kernel/debug/component/ 查看组件注册状态,排查未注册的组件。

4.5 初始化过程中的常见问题与调试方法

在SA8155平台DRM驱动初始化过程中,开发者常遇到以下问题:

问题现象 可能原因 调试方法
drm_dev_register 失败 DTS中缺少必要的时钟或中断资源 检查 dmesg 中的 EPROBE_DEFER 信息
component_bind_all 超时 某个子设备未注册组件 查看 /sys/kernel/debug/component 确认组件状态
CRTC/Encoder 创建失败 DPU 初始化异常或接口类型不匹配 添加 drm.debug=0x1e 内核参数,查看DRM日志
显示无输出 DSI/HDMI 节点 status 为 disabled 检查DTS中对应节点的 status 属性
推荐调试命令:
# cat /sys/kernel/debug/component/devices — 查看所有注册的组件
# cat /sys/kernel/debug/dri/0/state — 查看DRM状态
# dmesg | grep msm — 过滤MSM驱动日志

通过以上分析,开发者应能清晰理解SA8155平台DRM驱动的初始化流程、DTS解析机制以及MDSS驱动的加载顺序。这些知识是后续进行HDR显示配置与调试的基础。