2. KMS核心概念:CRTC、Encoder、Connector、Plane、Framebuffer的对象模型与生命周期

KMS(Kernel Mode Setting)是DRM子系统中负责显示输出配置的核心框架。要理解GPU如何将渲染好的画面最终呈现在显示器上,必须掌握KMS的五大核心对象:CRTC、Encoder、Connector、Plane和Framebuffer。它们共同构成了从内存中的像素数据到物理显示设备的完整数据管道。

关键认知:KMS对象模型本质上是一个显示管线的抽象。Framebuffer是数据源,Plane负责图层合成,CRTC负责时序生成,Encoder负责信号编码,Connector负责物理连接。理解这个管线的数据流方向,是驱动开发的基础。

2.1 五大核心对象概述

下表总结了每个KMS对象的角色、核心数据结构以及在显示管线中的位置:

对象 核心数据结构 角色描述 管线位置
Framebuffer struct drm_framebuffer 内存中的像素数据缓冲区,包含颜色格式、宽高、步幅等信息 数据源(最上游)
Plane struct drm_plane 硬件图层,负责将Framebuffer的内容进行缩放、裁剪、旋转后合成到CRTC 图层合成层
CRTC struct drm_crtc 显示控制器,生成像素时钟、行/场同步信号,驱动显示时序 时序生成与扫描输出
Encoder struct drm_encoder 信号编码器,将CRTC输出的并行像素数据转换为特定接口协议(如HDMI、DP、LVDS) 信号编码转换
Connector struct drm_connector 物理连接器,代表显示设备接口(如HDMI端口、DP端口),负责EDID读取和热插拔检测 物理连接(最下游)
开发提示:在驱动初始化时,这五个对象的创建顺序通常是:Framebuffer(由用户态创建)→ Plane → CRTC → Encoder → Connector。但它们的数据流方向是相反的:Framebuffer → Plane → CRTC → Encoder → Connector。

2.2 KMS显示管线数据流

下面的SVG图展示了从Framebuffer到Connector的完整数据流,以及每个对象在管线中的职责边界:

KMS显示管线数据流与对象模型 Framebuffer 像素数据缓冲区 (drm_framebuffer) Plane 图层合成/缩放 (drm_plane) CRTC 时序生成/扫描 (drm_crtc) Encoder 信号编码转换 (drm_encoder) Connector 物理接口 (drm_connector) 像素数据 合成后帧 时序信号 编码后信号 数据流方向: Framebuffer → Plane → CRTC → Encoder → Connector(内存 → 显示控制器 → 物理接口 → 显示器) 生命周期管理要点 • Framebuffer:由用户态通过 drmModeAddFB() 创建,引用计数管理,所有Plane释放后自动销毁 • Plane/CRTC/Encoder/Connector:驱动初始化时创建,驱动卸载时销毁 • 热插拔事件:Connector可动态添加/移除,触发 drm_connector_register()/unregister() • Atomic Commit:所有对象的状态通过 drm_atomic_state 统一提交,保证状态切换的原子性

2.3 各对象的生命周期详解

2.3.1 Framebuffer:短暂的数据载体

Framebuffer是KMS对象中生命周期最短的。它由用户态应用程序通过drmModeAddFB()或drmModeAddFB2()创建,关联到一块已经分配好的GPU内存(GEM对象)。当用户态程序销毁Framebuffer或关闭DRM文件描述符时,内核会释放其引用。只有当所有引用它的Plane都停止使用后,Framebuffer才会被真正销毁。

// 用户态创建Framebuffer示例
struct drm_mode_fb_cmd2 fb_cmd = {
    .width = 1920,
    .height = 1080,
    .pixel_format = DRM_FORMAT_XRGB8888,
    .handles = { gem_handle },
    .pitches = { 1920 * 4 },
};
int fb_id;
int ret = drmIoctl(fd, DRM_IOCTL_MODE_ADDFB2, &fb_cmd);
if (ret == 0) {
    fb_id = fb_cmd.fb_id;
}

2.3.2 Plane:硬件图层单元

Plane代表GPU中的硬件图层(Overlay或Primary)。每个Plane可以绑定一个Framebuffer,并配置其在屏幕上的显示位置、缩放比例、透明度等属性。Plane的生命周期与驱动绑定:在bind()回调中创建,在unbind()中销毁。Plane分为三种类型:

  • DRM_PLANE_TYPE_PRIMARY:主平面,每个CRTC必须关联一个,用于显示桌面背景或主应用窗口。
  • DRM_PLANE_TYPE_OVERLAY:叠加平面,用于视频播放、UI叠加层等,支持硬件缩放和颜色空间转换。
  • DRM_PLANE_TYPE_CURSOR:光标平面,专用于鼠标指针,通常尺寸固定(如64x64)。

2.3.3 CRTC:显示时序引擎

CRTC是KMS中最核心的对象,它负责生成显示时序(像素时钟、行同步、场同步、消隐区间等)。每个CRTC必须绑定至少一个Encoder才能输出信号。CRTC的状态包括:

  • 激活状态:是否正在扫描输出。
  • 显示模式:分辨率、刷新率、时序参数(drm_display_mode)。
  • 关联的Plane列表:当前参与合成的所有Plane。
常见陷阱:CRTC的扫描输出与GPU的渲染完成是异步的。驱动开发者必须正确处理VBlank(垂直消隐)中断,确保在CRTC扫描到有效区域时,Framebuffer的内容已经完成渲染。否则会出现撕裂(Tearing)现象。

2.3.4 Encoder:信号编码桥接

Encoder负责将CRTC输出的并行RGB/YCbCr像素数据转换为特定显示接口的串行信号。常见的Encoder类型包括:

Encoder类型 对应接口 编码方式
DRM_MODE_ENCODER_TMDS HDMI / DVI TMDS(最小化传输差分信号)
DRM_MODE_ENCODER_DP DisplayPort 微包化差分信号
DRM_MODE_ENCODER_LVDS LVDS(笔记本内屏) 低压差分信号
DRM_MODE_ENCODER_DSI MIPI DSI(手机/平板) 串行显示接口

2.3.5 Connector:物理接口与探测

Connector是KMS对象中最靠近硬件的部分,它代表一个物理显示接口。Connector的生命周期相对动态:

  • 初始化时:驱动枚举所有物理接口,创建对应的Connector对象。
  • 热插拔时:通过drm_connector_register()和drm_connector_unregister()动态管理。
  • 探测(Probe):每次状态变化时,驱动会读取显示器的EDID(扩展显示标识数据),获取支持的分辨率列表。

2.4 对象间的绑定关系

KMS对象之间通过引用指针和状态链表建立连接。下面的SVG图展示了典型的绑定拓扑:

KMS对象绑定关系拓扑 Plane组 Primary Plane (必选) Overlay Plane (可选,可多个) Cursor Plane (可选) CRTC 显示控制器 (drm_crtc) Encoder 信号编码器 (drm_encoder) Connector 物理接口 绑定规则: CRTC通过 crtc->primary 和 crtc->cursor 直接引用Plane;Overlay Plane通过状态链表关联。 CRTC通过 crtc->encoder 绑定Encoder;Encoder通过 encoder->connector 绑定Connector。

2.5 对象生命周期管理最佳实践

驱动开发清单:
  1. 初始化阶段:在bind()中按顺序创建CRTC → Encoder → Connector → Plane。使用drmm_crtc_alloc_with_planes()等辅助函数简化内存管理。
  2. 状态管理:使用drm_atomic_state进行事务性提交,避免部分更新导致的状态不一致。
  3. 热插拔处理:在connector->funcs->detect()中实现EDID读取和状态更新,通过drm_kms_helper_hotplug_event()通知用户态。
  4. 销毁顺序:在unbind()中先销毁Plane,再销毁CRTC/Encoder/Connector。Framebuffer由用户态负责释放。
  5. 引用计数:所有KMS对象都使用kref引用计数,确保在最后一个引用释放前不会被销毁。

2.6 多平面合成(Multi-plane Composition)

现代GPU支持多个硬件平面同时合成,这是实现高性能显示的关键。多平面合成的核心优势在于:

  • 减少GPU渲染负载:视频播放层、UI层、光标层各自独立更新,无需在GPU中合成完整帧。
  • 降低内存带宽:每个平面只更新变化的部分,无需重新传输整个帧缓冲区。
  • 支持硬件缩放:Overlay Plane通常支持独立的缩放系数,适合视频播放时的全屏切换。
// 多平面合成的Atomic Commit示例(用户态)
struct drm_mode_atomic atomic = {0};
uint32_t plane_ids[3] = { primary_id, overlay_id, cursor_id };
uint32_t fb_ids[3] = { primary_fb, overlay_fb, cursor_fb };

// 配置每个Plane的显示位置
struct drm_plane_state plane_states[3] = {
    { .crtc_id = crtc_id, .fb_id = primary_fb, .src_x = 0, .src_y = 0,
      .src_w = 1920 << 16, .src_h = 1080 << 16,
      .crtc_x = 0, .crtc_y = 0, .crtc_w = 1920, .crtc_h = 1080 },
    { .crtc_id = crtc_id, .fb_id = overlay_fb, .src_x = 0, .src_y = 0,
      .src_w = 1280 << 16, .src_h = 720 << 16,
      .crtc_x = 100, .crtc_y = 50, .crtc_w = 1280, .crtc_h = 720 },
    { .crtc_id = crtc_id, .fb_id = cursor_fb, .src_x = 0, .src_y = 0,
      .src_w = 64 << 16, .src_h = 64 << 16,
      .crtc_x = 800, .crtc_y = 600, .crtc_w = 64, .crtc_h = 64 }
};

// 提交原子更新
atomic.flags = DRM_MODE_ATOMIC_ALLOW_MODESET;
atomic.count_plane_deps = 0;
atomic.objects = (uint64_t)plane_ids;
atomic.plane_mask = (1 << 3) - 1;
// ... 填充其他字段后调用 drmIoctl(fd, DRM_IOCTL_MODE_ATOMIC, &atomic)
性能优化建议:在多平面合成场景中,优先使用硬件光标平面(Cursor Plane)处理鼠标指针,避免主平面重绘。对于视频播放,使用Overlay Plane并启用硬件缩放,可以显著降低GPU的2D渲染负载。

2.7 本章小结

KMS的五大核心对象构成了一个清晰的分层显示管线:

  • Framebuffer:数据源,生命周期由用户态控制。
  • Plane:图层合成单元,支持多平面并行处理。
  • CRTC:时序引擎,驱动整个显示流程。
  • Encoder:信号编码桥接,适配不同物理接口。
  • Connector:物理接口,负责与显示设备通信。