2. DRM 核心概念:CRTC、Encoder、Connector、Plane、Framebuffer、Mode Object 与 Property
DRM (Direct Rendering Manager) 是 Linux 内核中负责图形显示和渲染管理的核心框架。在瑞芯微平台(如 RK3568、RK3588)的显示驱动开发中,理解 DRM 的六大核心对象及其关系是进行驱动移植、显示优化和多屏调试的基础。本章将逐一剖析这些对象,并揭示它们如何协同工作,最终将帧缓冲区中的像素数据呈现到物理显示器上。
2.1 核心对象全景图
在深入每个对象之前,我们先通过一张架构图建立整体认知。下图展示了 DRM 显示管道中各个核心对象的连接关系与数据流向。
2.2 Framebuffer(帧缓冲区)
Framebuffer 是 DRM 中最基础的内存对象,它代表一块用于存储像素数据的物理连续或非连续内存区域。用户态应用程序(如 Wayland Compositor、Xorg)将渲染完成的图像数据写入 Framebuffer,然后由显示硬件读取并输出。
2.2.1 关键属性
| 属性 | 描述 | 瑞芯微平台注意事项 |
|---|---|---|
| width / height | 帧缓冲区的像素宽度和高度 | 需对齐到 16 或 32 像素边界 |
| pitch | 每行像素的字节数(stride) | RK3588 要求 pitch 为 64 字节对齐 |
| depth / bpp | 颜色深度(如 32bpp) | 支持 ARGB8888、XRGB8888 等格式 |
| handles / offsets | GEM 句柄数组及每平面的偏移 | 多平面格式(如 NV12)需注意 offset 计算 |
2.2.2 创建流程
// 用户态通过 DRM_IOCTL_MODE_ADDFB2 创建 Framebuffer
struct drm_mode_fb_cmd2 fb_cmd = {
.width = 1920,
.height = 1080,
.pixel_format = DRM_FORMAT_XRGB8888,
.handles[0] = gem_handle, // 从 GEM 分配的内存句柄
.pitches[0] = 1920 * 4, // 每行字节数
};
int ret = drmIoctl(fd, DRM_IOCTL_MODE_ADDFB2, &fb_cmd);
// fb_cmd.fb_id 即为新创建的 Framebuffer ID
2.3 Plane(硬件图层)
Plane 代表显示控制器中的一个硬件图层。现代 SoC(如 RK3588)通常支持多个硬件图层(如主图层、光标图层、视频图层),每个 Plane 可以独立配置位置、大小、透明度、Z-order 和色彩格式。Plane 是 DRM 实现合成(Composition)的关键。
2.3.1 Plane 类型
| 类型 | 枚举值 | 用途 | 瑞芯微典型数量 |
|---|---|---|---|
| Primary Plane | DRM_PLANE_TYPE_PRIMARY | 主显示图层,通常用于桌面背景或主应用 | 1 个 |
| Cursor Plane | DRM_PLANE_TYPE_CURSOR | 硬件光标层,支持小尺寸、高刷新率更新 | 1 个 |
| Overlay Plane | DRM_PLANE_TYPE_OVERLAY | 叠加层,用于视频播放、UI 弹窗等 | 2~4 个 |
2.3.2 Plane 属性与合成
// 通过 drmModeObjectGetProperties 获取 Plane 的 Property
drmModeObjectProperties *props = drmModeObjectGetProperties(fd, plane_id, DRM_MODE_OBJECT_PLANE);
for (int i = 0; i < props->count_props; i++) {
drmModePropertyRes *prop = drmModeGetProperty(fd, props->props[i]);
printf("Property: %s, Value: %llu\n", prop->name, props->prop_values[i]);
drmModeFreeProperty(prop);
}
drmModeFreeObjectProperties(props);
2.4 CRTC(显示控制器)
CRTC (Cathode Ray Tube Controller,历史名称沿用至今) 是 DRM 显示管道的核心调度器。它负责从 Framebuffer 读取像素数据,生成显示时序信号(行同步、场同步、像素时钟),并将数据流发送给 Encoder。在瑞芯微平台上,CRTC 通常对应 VOP (Video Output Processor) 模块。
2.4.1 CRTC 核心职责
- 时序生成: 根据 Mode (显示模式) 生成精确的 Hsync、Vsync、DE 信号。
- 扫描控制: 控制从 Framebuffer 读取像素的起始地址和扫描顺序。
- 色彩管理: 应用 Gamma/LUT 校正、色彩矩阵转换。
- VBlank 管理: 提供垂直消隐区中断,用于帧同步和 Page Flip。
2.4.2 瑞芯微 VOP 与 CRTC 映射
| 芯片型号 | VOP 模块 | CRTC 数量 | 最大分辨率 |
|---|---|---|---|
| RK3568 | VOP2 | 2 (CRTC-0, CRTC-1) | 4K@60 |
| RK3588 | VOP3 | 3 (CRTC-0 ~ CRTC-2) | 8K@60 |
| RK3399 | VOPB / VOPL | 2 | 4K@30 |
2.5 Encoder(编码器)
Encoder 负责将 CRTC 输出的并行像素数据转换为特定显示接口所需的串行信号格式。常见的 Encoder 类型包括 HDMI Encoder、DP Encoder、MIPI DSI Encoder、LVDS Encoder 等。在瑞芯微平台中,Encoder 通常由 SoC 内部的显示接口控制器(如 HDMI TX Controller)实现。
2.5.1 Encoder 与 CRTC 的绑定
// 内核驱动中,Encoder 通过 drm_encoder_init 注册,并关联可能的 CRTC 掩码
struct drm_encoder *encoder;
drm_encoder_init(drm_dev, encoder, &rk_encoder_funcs, DRM_MODE_ENCODER_TMDS, NULL);
encoder->possible_crtcs = drm_crtc_mask(crtc); // 绑定到特定 CRTC
2.6 Connector(连接器)
Connector 代表物理显示接口,如 HDMI 端口、DP 端口、eDP 接口、MIPI DSI 接口等。Connector 负责检测显示器是否连接(Hot Plug Detect)、读取显示器的 EDID 数据(获取支持的分辨率列表)、以及管理物理连接状态。
2.6.1 Connector 状态与 EDID
- connected: 检测到显示器,可通过 EDID 获取显示模式列表。
- disconnected: 无显示器连接,驱动应关闭输出以节省功耗。
- unknown: 状态未知,驱动需轮询检测。
// 用户态获取 Connector 信息
drmModeConnector *conn = drmModeGetConnector(fd, connector_id);
if (conn->connection == DRM_MODE_CONNECTED) {
printf("Connector %d connected, %d modes available\n", connector_id, conn->count_modes);
for (int i = 0; i < conn->count_modes; i++) {
drmModeModeInfo *mode = &conn->modes[i];
printf(" Mode: %s, %dx%d@%dHz\n", mode->name, mode->hdisplay, mode->vdisplay, mode->vrefresh);
}
}
drmModeFreeConnector(conn);
2.7 Mode Object 与 Property
Mode Object 是 DRM 中所有显示对象的基类。CRTC、Encoder、Connector、Plane、Framebuffer 都是 Mode Object 的子类。每个 Mode Object 都有一个唯一的 ID 和类型标识。
Property 是 DRM 实现可配置性的核心机制。每个 Property 都有一个名称、一个类型(如 range、enum、blob、object)和一个值。用户态可以通过 Property 来配置显示管道的各种参数,而无需直接操作内核结构体。
2.7.1 常用 Property 示例
| Property 名称 | 所属对象 | 类型 | 说明 |
|---|---|---|---|
| CRTC_ID | Plane / Connector | object | 绑定到指定的 CRTC |
| FB_ID | Plane | object | 指定 Plane 使用的 Framebuffer |
| alpha | Plane | range | 图层透明度 (0~65535) |
| zpos | Plane | range | Z-order 排序值 |
| mode_id | Connector | blob | 显示模式 ID |
| scaling_mode | Connector | enum | 缩放模式 (None/Full/Aspect) |
2.7.2 Property 的原子操作
// 使用 Atomic API 一次性提交多个 Property 更新
struct drm_mode_atomic atomic = {0};
atomic.flags = DRM_MODE_ATOMIC_ALLOW_MODESET;
atomic.count_objs = 2;
uint32_t objs[2] = {plane_id, crtc_id};
uint32_t count_props[2] = {2, 1};
uint32_t props[3] = {prop_fb_id, prop_crtc_id, prop_active};
uint64_t values[3] = {new_fb_id, crtc_id, 1};
atomic.objs_ptr = (uint64_t)objs;
atomic.count_props_ptr = (uint64_t)count_props;
atomic.props_ptr = (uint64_t)props;
atomic.values_ptr = (uint64_t)values;
int ret = drmIoctl(fd, DRM_IOCTL_MODE_ATOMIC, &atomic);
2.8 对象间的关系总结
为了帮助记忆,下表总结了六大核心对象之间的关系与约束:
| 关系 | 描述 | 约束条件 |
|---|---|---|
| Framebuffer → Plane | Plane 引用 Framebuffer 作为像素数据源 | Framebuffer 格式必须与 Plane 支持的格式匹配 |
| Plane → CRTC | Plane 必须绑定到一个 CRTC 才能参与显示 | 一个 CRTC 可绑定多个 Plane,但 Primary Plane 必须存在 |
| CRTC → Encoder | CRTC 将像素流发送给 Encoder | 一个 CRTC 只能连接一个 Encoder |
| Encoder → Connector | Encoder 将编码后的信号发送给 Connector | 一个 Encoder 可连接多个 Connector(如 HDMI+DP 复用) |
| Connector → Display | Connector 通过物理线缆连接显示器 | Connector 状态决定显示链路是否可用 |
2.9 本章小结
- Framebuffer 是像素数据的载体,由用户态分配并填充。
- Plane 是硬件图层,支持独立的位置、大小和透明度配置。
- CRTC 是显示时序控制器,负责从 Framebuffer 读取数据并生成同步信号。
- Encoder 将并行数据转换为串行接口信号(HDMI/DP/MIPI)。
- Connector 是物理接口,负责检测显示器并读取 EDID。
- Mode Object 是 DRM 对象的统一抽象,Property 提供了灵活的配置接口。
- 原子 API (Atomic Modeset) 通过 Property 集合实现无闪烁的显示模式切换。