4. Rockchip DRM 驱动框架:rockchip_drm_drv.c 与组件注册初始化流程
本章深入分析 Rockchip DRM 驱动的核心入口文件 rockchip_drm_drv.c,解析其如何通过 Linux DRM 子系统框架完成平台驱动注册、组件绑定以及显示子系统的初始化流程。理解这一流程是后续开发 VOP、HDMI、MIPI DSI 等具体驱动的基础。
4.1 框架总体架构与分层
Rockchip DRM 驱动遵循标准的 Linux DRM/KMS 架构,并在其基础上增加了 Rockchip 特有的组件管理逻辑。整体架构分为三层:
| 层级 | 核心文件/模块 | 职责描述 |
|---|---|---|
| DRM 核心层 | drm_drv.c, drm_framebuffer.c |
提供 DRM 设备注册、File Operations、IOCTL 分发、FB 管理。 |
| Rockchip 平台层 | rockchip_drm_drv.c |
平台驱动入口、组件聚合、私有数据管理、CRTC/Encoder/Connector 的注册协调。 |
| 硬件驱动层 | rockchip_vop.c, dw_hdmi-rockchip.c, rockchip_dsi.c |
具体 IP 核驱动,实现 VOP、HDMI、DSI 等硬件控制。 |
关键理解: Rockchip 平台层起到“胶水”作用,它不直接操作硬件寄存器,而是负责将各个硬件驱动组件(VOP、HDMI 等)通过 DRM 框架的组件(Component)机制绑定在一起,形成一个完整的显示输出链路。
4.2 平台驱动入口与初始化流程
rockchip_drm_drv.c 的初始化从 module_platform_driver() 宏展开开始,其核心流程如下:
- 平台驱动注册:
rockchip_drm_platform_driver结构体被注册到内核,匹配设备树中的rockchip,rk3399-drm或rockchip,rk3568-drm等 compatible 字符串。 - Probe 函数调用:当匹配成功时,
rockchip_drm_platform_probe()被调用。 - 组件主设备注册:调用
component_master_add_with_match(),将当前设备注册为组件主设备,并传入匹配表。 - 组件绑定:当所有子组件(VOP、HDMI 等)都完成 probe 并调用
component_add()后,主设备的bind()回调被触发。 - DRM 设备初始化:在
bind()回调中,调用drm_dev_alloc()分配 DRM 设备,并注册mode_config函数指针。 - 子组件加载:遍历组件列表,调用各子组件的
bind()函数,完成 CRTC、Encoder、Connector 的创建。 - DRM 设备注册:调用
drm_dev_register(),对外暴露 /dev/dri/card0 设备节点。
初始化流程图: 以下 SVG 展示了从内核模块加载到 DRM 设备注册的完整调用链。
4.3 组件注册与匹配机制
Rockchip 驱动使用内核的 Component Helper API 来管理主设备与子设备之间的依赖关系。这种机制解决了设备树中多个节点需要协同工作的问题。
4.3.1 组件匹配表构建
在 rockchip_drm_drv.c 中,通过 component_match_add() 函数构建匹配表。匹配依据通常是设备树节点的 compatible 属性或 port 端点。
// rockchip_drm_drv.c 中的匹配表构建示例
static int rockchip_drm_platform_probe(struct platform_device *pdev)
{
struct component_match *match = NULL;
struct device_node *np = pdev->dev.of_node;
struct device_node *port;
// 遍历设备树中的端口节点,为每个端口添加组件匹配
for_each_child_of_node(np, port) {
// 根据端口号匹配对应的 VOP 或 HDMI 节点
component_match_add(&pdev->dev, &match,
rockchip_drm_match_by_port, port);
}
// 注册为主设备,传入匹配表
return component_master_add_with_match(&pdev->dev,
&rockchip_drm_master_ops, match);
}
4.3.2 子组件注册
每个硬件驱动(如 VOP)在其 probe 函数中调用 component_add() 将自己注册为子组件:
// rockchip_vop.c 中的组件注册
static int vop_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct vop *vop;
int ret;
// ... 分配和初始化 vop 结构体 ...
// 注册为 DRM 子组件
ret = component_add(dev, &vop_component_ops);
if (ret)
goto err_disable_pm;
return 0;
}
4.4 rockchip_drm_bind() 核心实现
当所有子组件就绪后,rockchip_drm_bind() 被调用。这是整个显示驱动初始化的核心函数,其关键步骤包括:
| 步骤 | 函数调用 | 说明 |
|---|---|---|
| 1 | drm_dev_alloc() |
分配 drm_device 结构体,设置 driver 指针。 |
| 2 | drm_mode_config_init() |
初始化 mode_config,设置 min_width/height、max_width/height 等限制。 |
| 3 | rockchip_drm_modeset_init() |
遍历组件列表,依次调用各组件 bind(),创建 CRTC、Encoder、Connector。 |
| 4 | drm_vblank_init() |
初始化 VBlank 支持,注册 IRQ 处理函数。 |
| 5 | drm_kms_helper_poll_init() |
启动输出轮询(用于热插拔检测)。 |
| 6 | drm_dev_register() |
注册 DRM 设备,创建 /dev/dri/card0。 |
注意事项:
rockchip_drm_modeset_init() 中的组件绑定顺序至关重要。通常 VOP(CRTC)必须先于 HDMI/DSI(Encoder/Connector)绑定,因为 Encoder 需要引用 CRTC 的管道信息。如果顺序错误,可能导致空指针异常或显示链路无法建立。
4.5 组件绑定顺序与依赖关系
以下 SVG 展示了 Rockchip 显示子系统中各组件的依赖关系与绑定顺序:
4.6 关键数据结构与私有数据
rockchip_drm_drv.c 定义了一个全局私有数据结构 rockchip_drm_private,用于管理整个显示子系统的状态:
struct rockchip_drm_private {
struct drm_device *drm;
struct device *dev;
// CRTC 列表
struct list_head crtc_list;
// VOP 相关
struct vop **vop;
int num_vop;
// 内存管理
struct drm_gem_object *(*gem_create_object)(struct drm_device *drm,
unsigned int size);
// IOMMU 相关
struct iommu_domain *domain;
struct device *iommu_dev;
};
调试技巧: 在
这有助于定位组件绑定失败或顺序错误的问题。
rockchip_drm_bind() 函数中添加 dev_info() 打印,可以跟踪组件绑定进度。例如:dev_info(dev, "Binding component: %s\n", component->name);这有助于定位组件绑定失败或顺序错误的问题。
4.7 本章小结
- 入口文件:
rockchip_drm_drv.c是 Rockchip DRM 驱动的总控中心,负责平台驱动注册和组件聚合。 - 组件机制:使用内核 Component Helper API 管理主-子设备依赖,确保 VOP、HDMI 等模块按序初始化。
- 绑定流程:从
probe()到bind(),再到drm_dev_register(),形成完整的驱动加载链路。 - 依赖顺序:CRTC (VOP) → Encoder (HDMI/DSI) → Connector,任何一环缺失都会导致显示链路不完整。