4. DRM 驱动初始化流程:platform_driver 注册、component 框架、bind/unbind 回调分析
瑞芯微平台 DRM 驱动的初始化并非简单的 probe 完成即结束,而是通过 platform_driver 标准模型触发 probe,再借助 component 框架 实现跨驱动(VOP、HDMI、DSI、DP 等)的绑定与解绑。本章深入拆解这一初始化链条,重点分析 bind/unbind 回调在显示控制器(VOP)与各个显示接口驱动之间的协作机制。
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);
}
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 回调。
/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 设备注册的完整流程:
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_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 无错误 |