3. KMS 对象模型:CRTC、Encoder、Connector、Plane、Bridge 的概念与拓扑关系
KMS(Kernel Mode Setting)是 DRM 驱动中负责显示模式配置与画面输出的核心子系统。要理解 KMS 的工作方式,必须首先掌握其对象模型——即构成显示管线的五个基本对象:CRTC、Encoder、Connector、Plane 和 Bridge。这些对象通过明确的拓扑关系连接在一起,形成从帧缓冲区到显示设备的完整数据通路。
3.1 五大核心对象概述
下表列出了 KMS 对象模型中每个核心对象的职责、内核数据结构以及典型实例:
| 对象 | 内核结构体 | 核心职责 | 典型实例 |
|---|---|---|---|
| CRTC | struct drm_crtc |
管理显示时序、扫描输出、VBlank 中断、色彩转换矩阵 | SoC 内置显示控制器、GPU 显示管道 |
| Encoder | struct drm_encoder |
将像素数据编码为特定接口信号(如 HDMI、LVDS、eDP) | IT66121 HDMI 发送器、ANX7625 DP 桥片 |
| Connector | struct drm_connector |
代表物理连接器,检测热插拔、读取 EDID、报告物理状态 | HDMI Type-A、DisplayPort、VGA、eDP 面板接口 |
| Plane | struct drm_plane |
管理帧缓冲区与显示层的合成(叠加、缩放、裁剪) | 主平面(Primary)、光标平面(Cursor)、叠加平面(Overlay) |
| Bridge | struct drm_bridge |
插入在 Encoder 与 Connector 之间的中间处理单元 | MIPI DSI 转 eDP 桥片、色彩转换芯片、信号调理器 |
3.2 对象拓扑关系
KMS 对象之间的连接关系遵循严格的单向数据流。下图展示了从帧缓冲区到显示设备的完整拓扑结构:
- 一个 CRTC 可以连接多个 Plane(通常至少一个主平面 + 一个光标平面)。
- 一个 CRTC 连接一个 Encoder,但一个 Encoder 可以被多个 CRTC 共享(取决于硬件)。
- 一个 Encoder 连接一个 Connector,中间可插入零个或多个 Bridge。
- 一个 Connector 只能连接一个 Encoder/Bridge,但一个物理连接器可能对应多个虚拟 Connector(如 DP MST)。
3.3 Plane:显示层的核心
Plane 是 KMS 对象模型中与帧缓冲区直接交互的组件。每个 Plane 对应一个独立的显示层,支持不同的像素格式和合成方式。
3.3.1 Plane 的类型
| 类型 | 内核常量 | 典型用途 | 数量限制 |
|---|---|---|---|
| 主平面 (Primary) | DRM_PLANE_TYPE_PRIMARY |
显示主内容,通常为全屏帧缓冲区 | 每个 CRTC 至少 1 个 |
| 光标平面 (Cursor) | DRM_PLANE_TYPE_CURSOR |
硬件鼠标光标,支持快速移动和热更新 | 每个 CRTC 最多 1 个 |
| 叠加平面 (Overlay) | DRM_PLANE_TYPE_OVERLAY |
视频叠加、UI 层、画中画 | 取决于硬件能力 |
3.3.2 Plane 的关键属性
- 帧缓冲区绑定:每个 Plane 在某一时刻只能绑定一个
struct drm_framebuffer,但可以通过drm_atomic_set_fb_for_plane()原子切换。 - 源裁剪与目标缩放:Plane 支持
src_x/src_y/src_w/src_h(源裁剪)和crtc_x/crtc_y/crtc_w/crtc_h(目标位置与缩放)。 - 像素格式:通过
drm_plane_create_format_modifiers()声明支持的格式列表,如 ARGB8888、NV12、YUV420 等。 - 色彩属性:支持
COLOR_ENCODING和COLOR_RANGE属性,用于 YUV 到 RGB 的转换控制。
drm_universal_plane_init() 正确初始化类型,并在 atomic_check 回调中验证源裁剪和目标缩放是否在硬件限制范围内。错误的 Plane 配置会导致画面撕裂或显示异常。
3.4 CRTC:显示时序与 VBlank 的掌控者
CRTC(Cathode Ray Tube Controller,历史名称沿用)是 KMS 对象模型中负责生成显示时序的核心对象。它控制着每一帧的扫描过程,并产生 VBlank 中断信号。
3.4.1 CRTC 的核心职责
- 模式设置:通过
drm_display_mode结构体配置分辨率、刷新率、行场同步参数。 - VBlank 管理:在垂直消隐期触发中断,用于帧同步、统计帧率、驱动色彩校准。
- 色彩管理:支持
drm_crtc_enable_color_mgmt()注册 degamma、CTM(色彩变换矩阵)、gamma 校正。 - 扫描输出:从绑定的 Plane 中读取像素数据,按时序输出到 Encoder。
3.4.2 CRTC 与 VBlank 的关系
VBlank 是 CRTC 驱动中最重要的中断事件之一。下图展示了 CRTC 扫描时序与 VBlank 窗口的关系:
3.5 Encoder 与 Bridge:信号转换链路
Encoder 和 Bridge 共同构成了从 CRTC 到 Connector 的信号转换链路。Encoder 通常负责协议级别的编码,而 Bridge 则处理更细粒度的信号调理。
3.5.1 Encoder 的类型
| Encoder 类型 | 输出接口 | 典型驱动 |
|---|---|---|
DRM_MODE_ENCODER_DAC |
模拟 VGA | i915、radeon |
DRM_MODE_ENCODER_TMDS |
HDMI / DVI | dw-hdmi、tda998x |
DRM_MODE_ENCODER_LVDS |
LVDS 面板 | imx-ldb、tilcdc |
DRM_MODE_ENCODER_DP |
DisplayPort | analogix_dp、cdns-dp |
DRM_MODE_ENCODER_DSI |
MIPI DSI | sun6i-mipi-dsi、dw-mipi-dsi |
3.5.2 Bridge 的链式处理
Bridge 是 DRM 子系统中相对较新的抽象,用于处理 Encoder 与 Connector 之间的中间处理步骤。典型的 Bridge 处理流程包括:
- 信号格式转换:如 MIPI DSI 转 eDP、RGB 并行转 LVDS。
- 色彩空间转换:如 BT.601 转 BT.709、YUV 转 RGB。
- 信号调理:如预加重、摆幅调整、去抖动。
- 电源管理:桥片的上电/下电序列控制。
- 使用
drm_bridge_add()注册桥片,通过drm_bridge_attach()连接到 Encoder。 - 实现
struct drm_bridge_funcs中的mode_set、enable、disable回调。 - 如果桥片支持色彩处理,可以在
atomic_check中验证色彩属性。 - 多个 Bridge 可以链式连接,形成处理流水线。
3.6 Connector:物理世界的接口
Connector 是 KMS 对象模型中唯一与物理世界直接交互的对象。它负责检测显示设备的连接状态、读取 EDID 信息、报告物理属性。
3.6.1 Connector 的状态机
Connector 的状态通过 drm_connector_status 枚举表示:
| 状态 | 含义 | 典型触发条件 |
|---|---|---|
connector_status_connected |
显示设备已连接 | 热插拔检测 (HPD) 引脚拉高 |
connector_status_disconnected |
显示设备未连接 | HPD 引脚拉低、EDID 读取失败 |
connector_status_unknown |
无法确定状态 | 硬件不支持热插拔检测 |
3.6.2 EDID 与显示能力
Connector 通过 drm_get_edid() 读取显示设备的 EDID(Extended Display Identification Data),获取以下信息:
- 支持的分辨率与刷新率:包含标准时序和详细时序描述。
- 物理尺寸:屏幕的对角线尺寸(厘米)。
- 色彩特性:色域、伽马值、色彩深度。
- 接口类型:HDMI、DP、DVI 等。
cat /sys/class/drm/card0-HDMI-A-1/edid | edid-decode 可以查看 EDID 的详细内容。如果 Connector 状态异常,首先检查 EDID 读取是否成功。
3.7 对象拓扑的完整示例
以下是一个典型的嵌入式设备 KMS 对象拓扑示例,展示了从帧缓冲区到 eDP 面板的完整链路:
// 伪代码:KMS 对象拓扑初始化
struct drm_device *dev;
struct drm_crtc *crtc;
struct drm_plane *primary, *cursor;
struct drm_encoder *encoder;
struct drm_bridge *bridge;
struct drm_connector *connector;
// 1. 创建 Plane
primary = drm_universal_plane_init(dev, ... , DRM_PLANE_TYPE_PRIMARY, ...);
cursor = drm_universal_plane_init(dev, ... , DRM_PLANE_TYPE_CURSOR, ...);
// 2. 创建 CRTC,绑定 Plane
crtc = drm_crtc_init_with_planes(dev, ... , primary, cursor, ...);
drm_crtc_enable_color_mgmt(crtc, degamma_lut_size, has_ctm, gamma_lut_size);
// 3. 创建 Encoder
encoder = drm_encoder_init(dev, ... , DRM_MODE_ENCODER_DP, ...);
// 4. 创建 Bridge 并连接到 Encoder
bridge = devm_drm_bridge_add(dev, ...);
drm_bridge_attach(encoder, bridge, NULL, 0);
// 5. 创建 Connector,连接到 Bridge
connector = drm_connector_init(dev, ... , DRM_MODE_CONNECTOR_eDP, ...);
drm_connector_attach_encoder(connector, encoder); // 或通过 Bridge 连接
// 6. 建立 CRTC 到 Encoder 的链接
drm_mode_config_reset(dev);
drm_client_modeset_probe() 或用户空间的 modetest 工具验证对象拓扑是否正确。使用 find . -name "*.ko" | xargs modprobe 加载驱动后,执行 modetest -M <driver_name> 查看对象列表。