4. DRM 驱动初始化流程:platform_driver 注册、component 框架、bind/unbind 回调分析

瑞芯微平台 DRM 驱动的初始化并非简单的 probe 完成即结束,而是通过 platform_driver 标准模型触发 probe,再借助 component 框架 实现跨驱动(VOP、HDMI、DSI、DP 等)的绑定与解绑。本章深入拆解这一初始化链条,重点分析 bind/unbind 回调在显示控制器(VOP)与各个显示接口驱动之间的协作机制。

核心要点: component 框架解决了 DRM 子设备(CRTC、Encoder、Connector)的异步 probe 与有序绑定问题,是瑞芯微多芯片显示方案稳定初始化的基石。

4.1 platform_driver 注册与 probe 触发

瑞芯微 DRM 驱动(如 rockchip_drm_driver.c)首先注册为一个标准的 platform_driver:

static struct platform_driver rockchip_drm_platform_driver = {
    .probe  = rockchip_drm_platform_probe,
    .remove = rockchip_drm_platform_remove,
    .driver = {
        .name = "rockchip-drm",
        .of_match_table = rockchip_drm_dt_ids,
        .pm = &rockchip_drm_pm_ops,
    },
};

module_platform_driver(rockchip_drm_platform_driver);

当设备树节点 rockchip-drm 与驱动匹配成功后,内核调用 rockchip_drm_platform_probe。该函数的核心任务不是立即创建 DRM 设备,而是初始化 component 框架的 master:

static int rockchip_drm_platform_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct component_match *match = NULL;
    int ret;

    /* 收集所有子组件(VOP、HDMI、DSI 等)到 match 列表中 */
    ret = rockchip_drm_match_add(dev, &match);
    if (ret)
        return ret;

    /* 注册 component master,等待所有子组件 probe 完成后触发 bind */
    return component_master_add_with_match(dev, &rockchip_drm_master_ops, match);
}
关键设计: probe 函数不直接操作 DRM 核心对象,而是将初始化延迟到所有子组件就绪后,由 component 框架统一调度 bind 回调。这种设计避免了驱动加载顺序的不确定性。

4.2 component 框架:从分散到聚合

component 框架是 Linux 内核为解决“多个驱动模块需要按特定顺序组合成一个完整功能单元”而设计的轻量级绑定机制。在瑞芯微 DRM 场景中,它管理以下角色:

角色 对应驱动模块 DRM 子对象
Master rockchip-drm (主驱动) DRM device 整体
Component rockchip-vop CRTC (显示控制器)
Component dw-hdmi-rockchip Encoder + Connector
Component rockchip-dsi Encoder + Connector
Component rockchip-dp Encoder + Connector

每个子设备驱动在 probe 时调用 component_add() 将自己注册为 component:

static int vop_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;

    /* VOP 自身初始化:寄存器映射、时钟、中断等 */
    // ...

    /* 将自己添加到 component 框架 */
    return component_add(dev, &vop_component_ops);
}

当 master 和所有 component 都完成注册后,component 框架自动调用 master 的 bind 回调。

常见问题: 如果某个子设备驱动 probe 失败或 probe 顺序异常,component 框架会一直等待,导致 DRM 设备无法完成 bind。调试时应检查 /sys/kernel/debug/component 下的状态文件,确认哪些组件已就绪。

4.3 bind 回调:DRM 设备的组装工厂

rockchip_drm_master_ops 中的 bind 函数是 DRM 初始化的真正入口。它负责创建 DRM device、绑定所有子组件、注册最终设备:

static int rockchip_drm_bind(struct device *dev)
{
    struct drm_device *drm_dev;
    int ret;

    /* 1. 分配 DRM device */
    drm_dev = drm_dev_alloc(&rockchip_drm_driver, dev);
    if (IS_ERR(drm_dev))
        return PTR_ERR(drm_dev);

    dev_set_drvdata(dev, drm_dev);

    /* 2. 绑定所有子组件(CRTC、Encoder、Connector) */
    ret = component_bind_all(dev, drm_dev);
    if (ret)
        goto err_free;

    /* 3. 注册 DRM device,暴露 /dev/dri/card0 */
    ret = drm_dev_register(drm_dev, 0);
    if (ret)
        goto err_unbind;

    /* 4. 创建并注册 framebuffer 设备 */
    rockchip_drm_fbdev_init(drm_dev);

    drm_kms_helper_poll_init(drm_dev);

    return 0;

err_unbind:
    component_unbind_all(dev, drm_dev);
err_free:
    drm_dev_put(drm_dev);
    return ret;
}

下图展示了从 platform_driver probe 到 DRM 设备注册的完整流程:

DRM 驱动初始化流程:platform_driver → component → bind 1. platform_driver_register 2. rockchip_drm_platform_probe 3. component_master_add_with_match VOP component_add HDMI component_add DSI component_add DP component_add 4. component_bind_all 5. drm_dev_register 6. /dev/dri/card0 就绪 说明 所有子组件(绿色)必须全部完成 component_add,master 的 bind 回调才会被触发。 bind 回调内部通过 component_bind_all 依次调用每个子组件的 bind 函数,完成 CRTC/Encoder/Connector 的创建。

4.4 unbind 回调:有序拆卸

与 bind 对应,unbind 回调负责在驱动卸载或系统休眠时有序释放资源:

static void rockchip_drm_unbind(struct device *dev)
{
    struct drm_device *drm_dev = dev_get_drvdata(dev);

    drm_kms_helper_poll_fini(drm_dev);
    rockchip_drm_fbdev_fini(drm_dev);
    drm_dev_unregister(drm_dev);

    /* 依次解绑所有子组件 */
    component_unbind_all(dev, drm_dev);

    drm_dev_put(drm_dev);
}

unbind 的执行顺序与 bind 相反:先销毁 DRM 核心对象,再解绑子组件。每个子组件的 unbind 回调负责释放自身资源(如关闭显示时序、断开 HDMI 信号等)。

调试技巧: 在 rockchip_drm_unbind 中增加 dev_info 打印,可以清晰追踪卸载路径。如果系统休眠后唤醒出现显示异常,重点检查 unbind 是否完整执行,以及子组件是否在 unbind 后正确恢复状态。

4.5 子组件 bind/unbind 回调实现示例(VOP)

以 VOP(显示控制器)为例,其 component 回调函数集定义如下:

static const struct component_ops vop_component_ops = {
    .bind   = vop_bind,
    .unbind = vop_unbind,
};

vop_bind 函数负责创建 DRM CRTC 对象:

static int vop_bind(struct device *dev, struct device *master, void *data)
{
    struct drm_device *drm_dev = data;
    struct vop *vop = dev_get_drvdata(dev);
    int ret;

    /* 初始化 VOP 硬件:时钟使能、寄存器配置 */
    ret = vop_initial(vop);
    if (ret)
        return ret;

    /* 创建 DRM CRTC,关联 VOP 硬件操作函数集 */
    ret = vop_create_crtc(vop, drm_dev);
    if (ret)
        goto err_disable;

    return 0;

err_disable:
    vop_disable(vop);
    return ret;
}

vop_unbind 函数负责销毁 CRTC 并关闭硬件:

static void vop_unbind(struct device *dev, struct device *master, void *data)
{
    struct drm_device *drm_dev = data;
    struct vop *vop = dev_get_drvdata(dev);

    vop_destroy_crtc(vop, drm_dev);
    vop_disable(vop);
}
避坑指南: 在 vop_bind 中如果 vop_create_crtc 失败,必须调用 vop_disable 回滚硬件状态,否则下次 bind 时硬件可能处于异常状态。同理,unbind 中 vop_destroy_crtc 必须在 vop_disable 之前调用,确保 DRM 核心先解除对 CRTC 的引用。

4.6 初始化流程总结与调试检查清单

步骤 关键函数 调试检查点
1. platform_driver 注册 module_platform_driver 检查 /sys/bus/platform/drivers/rockchip-drm 是否存在
2. probe 收集组件 rockchip_drm_match_add 确认设备树中所有显示子节点 status = "okay"
3. component 注册 component_add (各子驱动) 查看 /sys/kernel/debug/component 状态
4. master bind rockchip_drm_bind 打印 bind 函数入口/出口,确认无错误返回
5. 子组件 bind vop_bind / dw_hdmi_bind 检查 CRTC/Encoder/Connector 创建日志
6. DRM 设备注册 drm_dev_register 确认 /dev/dri/card0 生成,dmesg 无错误
核心结论: 瑞芯微 DRM 驱动初始化通过 component 框架实现了“先分散 probe,后统一 bind”的优雅模式。理解这一流程,是定位显示驱动加载失败、子设备绑定超时、CRTC 未创建等问题的前提。在后续章节中,我们将基于这一初始化框架,深入分析 VOP 的 CRTC 实现细节与显示时序配置。