3. SA8155 DRM驱动初始化:驱动入口函数、platform_driver注册、设备树匹配、组件绑定流程

SA8155 DRM驱动的初始化是整个显示子系统的启动起点。本章深入剖析驱动从内核入口到组件绑定完成的完整路径,涵盖 module_init、platform_driver_register、设备树匹配表 以及 component framework 绑定流程。理解这一过程是掌握后续CRTC、Encoder、Connector初始化的前提。

核心要点: SA8155 DRM驱动采用 component 框架管理多个显示硬件模块(DPU、DSI、HDMI TX等)的绑定顺序,确保所有子设备就绪后才触发DRM设备的最终注册。

3.1 驱动入口函数与module_init

SA8155 DRM驱动遵循标准Linux内核驱动模型,使用 module_platform_driver 宏简化注册过程。入口函数定义在 msm_drv.c 中:

/* msm_drv.c */
static struct platform_driver msm_drm_platform_driver = {
    .probe  = msm_drm_probe,
    .remove = msm_drm_remove,
    .shutdown = msm_drm_shutdown,
    .driver = {
        .name = "msm_drm",
        .of_match_table = msm_drm_dt_match,
        .pm = &msm_drm_pm_ops,
    },
};

static int __init msm_drm_init(void)
{
    DBG("msm_drm_init");
    return platform_driver_register(&msm_drm_platform_driver);
}
module_init(msm_drm_init);
入口组件 作用 所在文件
module_init(msm_drm_init) 内核启动时调用,注册platform驱动 msm_drv.c
platform_driver_register 将驱动注册到platform总线 msm_drv.c
msm_drm_probe 设备树匹配成功后调用,启动组件绑定 msm_drv.c
msm_drm_dt_match 设备树兼容性匹配表 msm_drv.c
驱动入口流程图: 从 module_init 到 platform_driver_register,再到设备树匹配和 probe 的完整调用链。
驱动入口与platform_driver注册流程 module_init platform_driver_register 设备树匹配 msm_drm_probe component_master_add_with_match 匹配表: msm_drm_dt_match 兼容性: "qcom,sa8155-dpu"

3.2 platform_driver注册与设备树匹配

设备树匹配表 msm_drm_dt_match 定义了驱动支持的硬件兼容性字符串。SA8155平台对应的匹配条目如下:

/* msm_drv.c */
static const struct of_device_id msm_drm_dt_match[] = {
    { .compatible = "qcom,sa8155-dpu", .data = &sa8155_dpu_cfg  },
    { .compatible = "qcom,sa8155-dsi", .data = &sa8155_dsi_cfg  },
    { .compatible = "qcom,sa8155-hdmi", .data = &sa8155_hdmi_cfg },
    { .compatible = "qcom,sa8155-mdss", .data = &sa8155_mdss_cfg },
    { /* end of table */ }
};
MODULE_DEVICE_TABLE(of, msm_drm_dt_match);

设备树节点示例(sa8155.dtsi):

/* sa8155.dtsi */
mdss: mdss@ae00000 {
    compatible = "qcom,sa8155-mdss";
    reg = <0x0ae00000 0x1000>;
    #address-cells = <1>;
    #size-cells = <1>;
    ranges;

    dpu: dpu@ae01000 {
        compatible = "qcom,sa8155-dpu";
        reg = <0x0ae01000 0x80000>;
        interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>;
        clocks = <&gcc MDSS_AHB_CLK>,
                 <&gcc MDSS_AXI_CLK>;
    };

    dsi0: dsi@ae94000 {
        compatible = "qcom,sa8155-dsi";
        reg = <0x0ae94000 0x400>;
        clocks = <&gcc MDSS_BYTE0_CLK>;
    };
};
设备树节点 compatible 对应驱动模块 功能
mdss qcom,sa8155-mdss MDSS主控制器 显示子系统顶层管理
dpu qcom,sa8155-dpu DPU CRTC 显示处理单元,图层合成
dsi0 qcom,sa8155-dsi DSI Encoder MIPI DSI接口输出
hdmi qcom,sa8155-hdmi HDMI Encoder HDMI接口输出
匹配优先级: 设备树匹配按照 of_match_table 顺序进行。SA8155平台中,mdss 节点作为顶层容器先被匹配,随后其子节点(dpu、dsi、hdmi)由各自的platform驱动独立匹配。DRM master驱动只绑定 mdss 节点。

3.3 组件绑定流程:component framework

SA8155 DRM驱动使用 component 框架实现多设备绑定。核心思想是:DRM master驱动等待所有子组件(DPU、DSI、HDMI等)注册完成后,才调用 bind 回调完成DRM设备初始化。

组件绑定流程分为三个角色:

  • Master(主控): msm_drm_probe 中调用 component_master_add_with_match 注册master。
  • Component(组件): 各子设备(dpu、dsi、hdmi)的probe函数中调用 component_add 注册自身。
  • Match(匹配): 通过 component_match_add 添加匹配规则,将子设备与master关联。
/* msm_drv.c - 组件绑定核心代码 */
static int msm_drm_probe(struct platform_device *pdev)
{
    struct component_match *match = NULL;
    int ret;

    /* 添加子组件匹配:DPU、DSI、HDMI */
    component_match_add(&pdev->dev, &match, compare_of, dpu_node);
    component_match_add(&pdev->dev, &match, compare_of, dsi_node);
    component_match_add(&pdev->dev, &match, compare_of, hdmi_node);

    /* 注册master,等待所有组件就绪 */
    ret = component_master_add_with_match(&pdev->dev, &msm_drm_comp_ops, match);
    if (ret)
        dev_err(&pdev->dev, "failed to add component master\n");

    return ret;
}

/* master bind回调:所有组件就绪后调用 */
static int msm_drm_bind(struct device *dev)
{
    struct drm_device *ddev;
    int ret;

    ddev = drm_dev_alloc(&msm_drm_driver, dev);
    if (IS_ERR(ddev))
        return PTR_ERR(ddev);

    /* 初始化各个子模块:kms、crtc、encoder、connector */
    ret = msm_drm_kms_init(ddev);
    if (ret)
        goto err_kms;

    ret = drm_dev_register(ddev, 0);
    if (ret)
        goto err_register;

    return 0;

err_register:
    msm_drm_kms_fini(ddev);
err_kms:
    drm_dev_put(ddev);
    return ret;
}
SA8155 DRM组件绑定流程 DRM Master (mdss) DPU Component DSI Component HDMI Component component_add() component_add() component_add() 所有组件就绪 → bind回调 drm_dev_register()

3.4 组件绑定关键数据结构

组件绑定过程中,msm_drm_comp_ops 定义了master的bind/unbind回调:

/* msm_drv.c */
static const struct component_master_ops msm_drm_comp_ops = {
    .bind   = msm_drm_bind,
    .unbind = msm_drm_unbind,
};

子组件通过 component_add 注册,并提供 component_ops:

/* dpu_kms.c */
static const struct component_ops dpu_component_ops = {
    .bind   = dpu_bind,
    .unbind = dpu_unbind,
};

static int dpu_kms_probe(struct platform_device *pdev)
{
    /* 初始化DPU硬件资源 */
    return component_add(&pdev->dev, &dpu_component_ops);
}
数据结构/函数 作用 调用时机
component_master_ops 定义master的bind/unbind回调 所有组件就绪后调用bind
component_ops 定义子组件的bind/unbind回调 组件注册时提供
component_match_add 添加组件匹配规则 master probe时
component_master_add_with_match 注册master并关联匹配规则 master probe时
component_add 子组件注册到component框架 子设备probe时
绑定顺序保证: component框架确保 msm_drm_bind 在所有子组件的 bind 回调执行完毕后才被调用。这意味着DPU、DSI、HDMI的硬件初始化在DRM设备注册之前完成,避免了竞态条件。

3.5 初始化流程总结

SA8155 DRM驱动初始化完整流程可归纳为以下步骤:

  1. module_init:调用 platform_driver_register 注册platform驱动。
  2. 设备树匹配:内核根据 of_match_table 匹配 mdss 节点,调用 msm_drm_probe。
  3. 组件匹配注册:在probe中通过 component_match_add 添加DPU、DSI、HDMI子组件匹配。
  4. 子设备probe:各子设备(dpu、dsi、hdmi)的platform驱动独立probe,调用 component_add 注册自身。
  5. master bind:所有子组件注册完成后,component框架调用 msm_drm_bind。
  6. DRM设备注册:在bind中完成KMS初始化、CRTC/Encoder/Connector创建,最终调用 drm_dev_register。
调试建议: 如果DRM设备注册失败,首先检查 /sys/kernel/debug/component 下的组件注册状态。使用 echo 0x1f > /sys/module/drm/parameters/debug 可开启DRM核心调试日志,观察bind/unbind调用顺序。