3. Linux DRM/KMS框架概述

DRM (Direct Rendering Manager) 与 KMS (Kernel Mode Setting) 是 Linux 图形显示子系统的核心基础设施。对于 MIPI DSI 显示驱动开发者而言,理解 DRM/KMS 的架构分层与组件协作关系,是编写高质量驱动的前提。本章从 DRM 核心架构出发,逐步拆解 KMS 的四大组件——CRTC、Encoder、Connector、Plane,并串联成完整的显示流水线模型。

3.1 DRM核心架构

DRM 最初是为 GPU 设计的图形内存管理框架,后来演变为涵盖显示输出、模式设置、内存管理、同步对象等功能的统一子系统。其核心架构分为三层:

  • DRM Core:提供设备注册、文件操作接口、IOCTL 分发、内存管理(GEM/DRM_MM)、同步对象(Fence/Syncobj)等基础服务。
  • KMS 组件:负责显示模式设置与输出管理,包括 CRTC、Encoder、Connector、Plane、Bridge 等对象。
  • 驱动层:实现具体的硬件操作回调,如寄存器读写、时钟配置、DSI 命令传输等。
关键概念: DRM 设备通过 /dev/dri/card0 暴露给用户空间,用户态程序(如 Weston、Xorg)通过 IOCTL 与内核交互。KMS 操作通过 drmModeSetCrtc、drmModeAtomicCommit 等接口完成。

以下 SVG 展示了 DRM 核心架构的分层关系:

DRM 核心架构分层 用户空间 Weston / Xorg / 自定义应用 DRM Core 设备注册 / IOCTL 分发 / GEM 内存管理 / Fence 同步 KMS 组件 CRTC / Encoder / Connector / Plane / Bridge 驱动层 硬件操作回调 / 寄存器读写 / DSI 命令传输

3.2 KMS 组件详解

KMS 将显示输出抽象为四个核心对象,每个对象承担特定的职责。理解这些对象的角色与连接关系,是配置显示流水线的基础。

组件 角色 关键操作 MIPI DSI 场景
CRTC 显示控制器,负责扫描输出、时序生成、帧缓冲读取 模式设置、Gamma 校正、VBLANK 控制 配置 DSI 视频模式时序或命令模式刷新
Encoder 编码器,将 CRTC 输出的像素数据转换为物理接口信号 编码格式转换、时钟生成、数据通道映射 DSI 主机控制器作为 Encoder,打包像素为 DSI 包
Connector 连接器,代表物理显示接口与显示设备的连接 EDID 读取、热插拔检测、连接状态管理 DSI 面板作为 Connector,提供面板参数与初始化序列
Plane 平面/图层,提供独立帧缓冲与合成能力 缩放、裁剪、Alpha 混合、Z-order 控制 主平面用于 UI 渲染,叠加平面用于视频播放
实战提示: 在 MIPI DSI 驱动中,Encoder 通常对应 DSI 主机控制器(如 Synopsys DW-MIPI-DSI),Connector 对应 DSI 面板驱动。CRTC 则与 SoC 的显示控制器(如 Rockchip VOP、Allwinner DE2)绑定。

3.3 显示流水线模型

显示流水线描述了从帧缓冲到物理显示设备的完整数据流路径。在 DRM/KMS 中,流水线由 Plane → CRTC → Encoder → Connector 串联而成。每个组件负责数据流的一个处理阶段。

以下 SVG 展示了典型的显示流水线模型:

DRM/KMS 显示流水线模型 Plane 帧缓冲 缩放/裁剪/Alpha 像素数据 CRTC 显示控制器 时序生成/扫描 像素流 Encoder DSI 主机控制器 打包/时钟/通道 DSI 包 Connector DSI 面板 初始化/参数/状态 数据流方向: 帧缓冲 → Plane(合成)→ CRTC(时序生成)→ Encoder(DSI 打包)→ Connector(面板显示) 每个阶段都可以通过 DRM 的 Atomic API 进行原子化配置,避免撕裂与闪烁。

3.4 组件连接与对象树

在 DRM 内部,KMS 组件通过 drm_encoder、drm_connector、drm_crtc、drm_plane 结构体表示。组件之间的连接关系通过指针与链表维护:

  • CRTC ↔ Encoder:CRTC 通过 encoder->crtc 指向绑定的 CRTC,一个 CRTC 可连接多个 Encoder(但同一时刻只能激活一个)。
  • Encoder ↔ Connector:Encoder 通过 connector->encoder_ids 数组关联可能的 Encoder,Connector 通过 connector->encoder 指向当前绑定的 Encoder。
  • CRTC ↔ Plane:Plane 通过 plane->crtc 指向绑定的 CRTC,一个 CRTC 可关联多个 Plane(主平面 + 叠加平面)。
驱动开发要点: 在 probe 阶段,驱动需要创建并注册这些对象,然后通过 drm_mode_config_reset 初始化默认状态。对于 MIPI DSI,通常使用 drm_mipi_dsi_device 注册面板,并在 panel 驱动中创建 Connector。

3.5 原子化显示更新

现代 DRM 驱动推荐使用 Atomic API 进行显示更新。原子操作允许在一次事务中同时修改多个组件的状态,确保显示更新的一致性:

// 原子提交示例
struct drm_atomic_state *state;
state = drm_atomic_state_alloc(dev);
if (!state)
    return -ENOMEM;

// 获取 CRTC 状态
struct drm_crtc_state *crtc_state;
crtc_state = drm_atomic_get_crtc_state(state, crtc);
if (IS_ERR(crtc_state)) {
    drm_atomic_state_put(state);
    return PTR_ERR(crtc_state);
}

// 设置模式
ret = drm_atomic_set_mode_prop_for_crtc(crtc_state, mode);
if (ret) {
    drm_atomic_state_put(state);
    return ret;
}

// 提交
ret = drm_atomic_commit(state);
drm_atomic_state_put(state);
性能陷阱: 原子提交失败时,整个事务回滚,不会产生部分更新导致的撕裂。但频繁的原子提交会增加内核态开销。对于 MIPI DSI 面板,建议在 VBLANK 期间提交更新,避免与面板刷新冲突。

3.6 本章小结