4. RK3399 DRM 驱动框架:核心对象关系

在 RK3399 多屏异显场景中,理解 DRM (Direct Rendering Manager) 框架的对象关系是配置 DMA-BUF 的基础。DRM 子系统采用面向对象的设计,将显示流水线抽象为五个核心对象:drm_device、crtc、encoder、connector 和 plane。这些对象通过特定的拓扑结构组合,形成完整的显示通道。

核心概念: DRM 框架借鉴了传统显示硬件的管线模型,将软件对象与硬件模块一一映射。每个显示通道(Pipe)由一组 CRTC、Encoder、Connector 和 Plane 对象组成,而 drm_device 则是整个显示子系统的容器。

4.1 对象层次与职责

下表总结了 DRM 五大核心对象的职责及其在 RK3399 平台上的典型映射:

DRM 对象 职责描述 RK3399 硬件映射 关键数据结构
drm_device DRM 设备顶层抽象,管理所有子对象、内存管理、文件操作 对应 VOP (Video Output Processor) 整体 struct drm_device
crtc 显示控制器,负责扫描输出、时序生成、图层合成 VOP0 / VOP1 / VOP2 (lite) struct drm_crtc
encoder 编码器,将 CRTC 输出的并行信号转换为外部接口协议 HDMI TX / DP / MIPI DSI / eDP struct drm_encoder
connector 物理连接器,检测显示器热插拔、读取 EDID、管理显示模式 HDMI 接口 / DP 接口 / MIPI 接口 struct drm_connector
plane 硬件图层(Overlay),管理帧缓冲与显示区域的映射 VOP 的 Win0/Win1/Win2/Win3 图层 struct drm_plane

4.2 对象关系拓扑

DRM 对象之间通过“绑定”关系形成显示管线。一个典型的显示通道从 plane 开始,经过 crtc 合成,通过 encoder 编码,最终由 connector 输出到显示器。下图展示了 RK3399 双屏异显场景下的对象关系:

RK3399 DRM 对象关系拓扑图 RK3399 DRM 对象关系拓扑(双屏异显) drm_device (VOP 整体) 显示通道 0 (VOP0 - 主屏) plane.0 Win0 (主图层) plane.1 Win1 (叠加层) crtc.0 VOP0 显示控制器 encoder.0 HDMI TX 编码器 encoder.1 eDP 编码器 connector.0 HDMI-A-1 物理接口 显示通道 1 (VOP1 - 副屏) plane.2 Win0 (主图层) plane.3 Win2 (叠加层) crtc.1 VOP1 显示控制器 encoder.2 MIPI DSI 编码器 encoder.3 DP 编码器 connector.1 MIPI-DSI-1 物理接口 Plane CRTC Encoder Connector
关键观察: 在 RK3399 上,一个 CRTC 可以绑定多个 Encoder(如 VOP0 同时连接 HDMI 和 eDP),但同一时刻只有一个 Encoder 被激活。这种 1:N 的绑定关系为多屏异显提供了灵活的拓扑选择。

4.3 对象绑定规则与数据流

DRM 对象的绑定遵循严格的层次规则,数据流从 drm_framebuffer 开始,经过 plane 映射,最终由 connector 输出。以下是对象绑定的核心规则:

  1. Plane → CRTC 绑定: 每个 Plane 必须绑定到一个 CRTC 才能参与显示。Plane 负责将 DMA-BUF 映射的帧缓冲内容输出到 CRTC 的扫描线。
  2. CRTC → Encoder 绑定: CRTC 通过 possible_encoders 掩码声明可连接的 Encoder。RK3399 的 VOP0 通常连接 HDMI 和 eDP 编码器。
  3. Encoder → Connector 绑定: Encoder 通过 possible_clones 和 possible_crtcs 掩码确定可用的 Connector。每个 Connector 在初始化时绑定一个默认 Encoder。
  4. Connector → 显示模式: Connector 通过读取 EDID 或固定时序确定显示分辨率、刷新率等参数,传递给 CRTC 生成时序。

数据流方向如下:

用户空间 (DMA-BUF fd)
    ↓
drm_framebuffer (GEM 对象 + 像素格式)
    ↓
drm_plane (裁剪/缩放/旋转)
    ↓
drm_crtc (时序生成 + 图层合成)
    ↓
drm_encoder (信号编码)
    ↓
drm_connector (物理输出)
    ↓
显示器

4.4 RK3399 特有的对象配置

RK3399 的 DRM 驱动(rockchip_drm_vop.c)在对象初始化时做了以下特殊配置:

配置项 VOP0 (主屏) VOP1 (副屏) 说明
CRTC 数量 1 (crtc.0) 1 (crtc.1) 每个 VOP 只有一个 CRTC
Plane 数量 4 (Win0~Win3) 4 (Win0~Win3) 支持 4 个硬件图层
最大分辨率 4096x2160 2560x1600 VOP1 为 lite 版本
支持格式 RGB888/YUV420/NV12 RGB888/YUV420 VOP1 不支持 NV16
DMA-BUF 支持 是 是 通过 drm_gem_prime_import
避坑提示: 在多屏异显配置中,务必确认每个 Plane 的 possible_crtcs 掩码。RK3399 的 VOP0 和 VOP1 的 Plane 不能跨 CRTC 使用。如果尝试将 VOP0 的 Plane 绑定到 VOP1 的 CRTC,驱动会返回 -EINVAL。

4.5 对象初始化流程(驱动视角)

在 RK3399 DRM 驱动的 bind() 函数中,对象的创建和绑定顺序如下:

// 伪代码:RK3399 DRM 对象初始化流程
static int vop_bind(struct device *dev, struct drm_device *drm_dev) {
    struct vop *vop = dev_get_drvdata(dev);
    
    // 1. 创建 CRTC 对象
    drm_crtc_init_with_planes(drm_dev, &vop->crtc, 
                              &vop->win[0]->plane,  // 主 Plane
                              NULL,                  // 无 cursor plane
                              &vop_crtc_funcs, NULL);
    
    // 2. 创建 Plane 对象(每个 Win 对应一个 Plane)
    for (i = 0; i < VOP_WIN_MAX; i++) {
        drm_universal_plane_init(drm_dev, &vop->win[i]->plane,
                                 possible_crtcs_mask,  // 只能绑定本 VOP 的 CRTC
                                 &vop_plane_funcs,
                                 formats, num_formats,
                                 NULL, type, NULL);
    }
    
    // 3. 创建 Encoder 对象(由外部桥接芯片驱动完成)
    // 例如 inno_hdmi_probe() 中调用 drm_encoder_init()
    
    // 4. 创建 Connector 对象(由外部桥接芯片驱动完成)
    // 例如 inno_hdmi_connector_init() 中调用 drm_connector_init()
    
    // 5. 建立绑定关系(通过可能的掩码)
    // crtc->possible_encoders = BIT(encoder_index);
    // encoder->possible_crtcs = BIT(crtc_index);
}
实践要点: 在 DMA-BUF 配置中,用户空间通过 drmModeAtomicCommit 或 drmModeSetCrtc 建立对象之间的运行时绑定。驱动层通过 struct drm_crtc_state 和 struct drm_plane_state 维护每个对象的当前状态,确保 DMA-BUF 帧缓冲在正确的显示通道上呈现。

4.6 对象关系与 DMA-BUF 的关联

DMA-BUF 在多屏异显中的流转与 DRM 对象关系紧密相关:

  • Plane 是 DMA-BUF 的消费者: 每个 Plane 通过 drm_gem_prime_import 导入 DMA-BUF fd,将其映射为 drm_framebuffer 的 GEM 对象。
  • CRTC 是合成调度器: CRTC 的 atomic_flush 回调负责将多个 Plane 的 DMA-BUF 内容合成到最终的扫描输出。
  • Encoder/Connector 是输出通道: 它们不直接操作 DMA-BUF,但决定了 DMA-BUF 内容最终输出到哪个物理显示器。
  • drm_device 是内存管理器: 通过 struct drm_gem_object 和 struct drm_prime_file_private 管理所有 DMA-BUF 的导入/导出操作。
进阶理解: 在 RK3399 双屏异显场景中,两个显示通道(VOP0 和 VOP1)各自拥有独立的 CRTC 和 Plane 集合。DMA-BUF 可以同时被两个通道的 Plane 引用(通过增加引用计数),实现同一帧缓冲在多个屏幕上显示。但要注意,VOP0 和 VOP1 的 DMA 引擎是独立的,需要确保内存带宽足够支撑双通道同时读取。