4. KMS对象模型

在Linux DRM子系统中,KMS(Kernel Mode Setting)负责显示管线的配置与提交。理解KMS的关键,不是记住一堆结构体名字,而是建立一条“从显存像素到屏幕像素”的对象链路。这条链路由六类核心对象构成:Framebuffer、Plane、CRTC、Encoder、Connector,以及贯穿所有对象的Property机制。它们之间既有层级关系,也有动态绑定关系,最终通过Atomic Commit一次性提交。

KMS 对象模型与数据流 Framebuffer → Plane → CRTC → Encoder → Connector → 物理屏幕 Framebuffer 像素数据 + 格式 宽高 / pitch / modifier Plane 图层 / 缩放 / 裁剪 Primary / Overlay / Cursor CRTC 扫描输出引擎 时序 / 模式 / 刷新率 Encoder 信号编码 HDMI / DP / DSI / eDP Connector 物理接口 + EDID 连接状态 / 热插拔 物理显示设备 Monitor / Panel 绑定 合成 输出 驱动 传输 Property 机制(贯穿所有对象) 每个 KMS 对象都挂载一组 Property 标准 Property FB_ID / CRTC_ID / MODE_ID 驱动私有 Property 色彩 / 缩放 / 旋转 Atomic Commit 一次性提交 drm_atomic_commit() → 校验 → 生效 数据流方向:Framebuffer 提供像素 → Plane 描述图层 → CRTC 合成扫描 → Encoder 编码信号 → Connector 输出到屏幕

一、核心对象职责与层级关系

KMS对象模型可以按“数据流方向”来理解。最上游是Framebuffer,它是对一块显存缓冲区的抽象,记录了像素格式、宽高、pitch(行跨距)以及modifier(内存布局修饰符)。Framebuffer本身不参与显示时序,它只是“像素的来源”。

Plane是图层抽象,负责把Framebuffer的某个矩形区域映射到CRTC的某个位置,并支持缩放、裁剪、旋转和Alpha混合。一个CRTC可以绑定多个Plane,常见类型包括Primary(主图层)、Overlay(叠加层)和Cursor(光标层)。多平面合成正是通过多个Plane同时绑定到同一个CRTC来实现的。

CRTC(Cathode Ray Tube Controller,历史名称沿用至今)是扫描输出引擎,它从绑定的Plane中读取像素,按照当前模式(Mode)生成时序信号,并以固定刷新率输出。CRTC决定了分辨率、刷新率、时序参数(hdisplay、hsync_start、hsync_end、htotal等)。

Encoder负责把CRTC输出的像素流转换为特定接口的信号格式,例如HDMI TMDS、DisplayPort链路符号或DSI差分信号。Encoder是“信号编码器”,它不关心像素内容,只关心电气协议。

Connector是物理接口的抽象,代表一个可以插拔的显示端口。它维护连接状态(connected/disconnected)、EDID信息、支持的显示模式列表,以及DPMS电源状态。Connector通过best_encoder与某个Encoder绑定,最终把信号送到物理屏幕。

关键理解:Framebuffer是“画什么”,Plane是“怎么摆”,CRTC是“什么时候扫”,Encoder是“用什么协议发”,Connector是“从哪个口出去”。五者缺一不可,但并非固定一对一绑定,而是可以在运行时动态重配。

二、Property机制:KMS的“统一配置接口”

KMS最精妙的设计之一是Property机制。每个KMS对象(CRTC、Plane、Connector、Encoder、Framebuffer)都挂载了一组Property,每个Property有名称、类型(枚举、范围、位掩码、对象引用、Blob等)和当前值。用户空间通过drmModeGetProperty()枚举属性,通过Atomic IOCTL设置属性值。

Property分为两类:标准Property由DRM核心定义,如FB_ID、CRTC_ID、MODE_ID、SRC_X、SRC_Y、SRC_W、SRC_H、CRTC_X、CRTC_Y、CRTC_W、CRTC_H;驱动私有Property由具体GPU驱动注册,如色彩校正、缩放滤波、旋转角度、HDR元数据等。标准Property保证了跨驱动的最小可用性,私有Property则暴露硬件特有能力。

Atomic Commit是Property机制的“提交事务”。用户空间先创建一个atomic请求,逐个设置对象的Property,然后调用drmModeAtomicCommit()。内核在提交前会执行atomic_check,验证所有Property组合是否合法(例如Plane的缩放比例是否超出硬件限制、CRTC时序是否与Connector模式匹配)。只有全部通过,才会真正写入硬件寄存器。

// 典型 Atomic Commit 伪代码
drmModeAtomicReq *req = drmModeAtomicAlloc();

// 设置 Connector 的 CRTC_ID
drmModeAtomicAddProperty(req, conn_id, prop_crtc_id, crtc_id);

// 设置 CRTC 的 MODE_ID
drmModeAtomicAddProperty(req, crtc_id, prop_mode_id, mode_blob_id);

// 设置 Plane 的 FB_ID 与位置
drmModeAtomicAddProperty(req, plane_id, prop_fb_id, fb_id);
drmModeAtomicAddProperty(req, plane_id, prop_crtc_id, crtc_id);
drmModeAtomicAddProperty(req, plane_id, prop_src_x, 0);
drmModeAtomicAddProperty(req, plane_id, prop_src_y, 0);
drmModeAtomicAddProperty(req, plane_id, prop_src_w, width << 16);
drmModeAtomicAddProperty(req, plane_id, prop_src_h, height << 16);
drmModeAtomicAddProperty(req, plane_id, prop_crtc_x, 0);
drmModeAtomicAddProperty(req, plane_id, prop_crtc_y, 0);
drmModeAtomicAddProperty(req, plane_id, prop_crtc_w, width);
drmModeAtomicAddProperty(req, plane_id, prop_crtc_h, height);

// 提交并等待生效
drmModeAtomicCommit(fd, req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
drmModeAtomicFree(req);

三、对象绑定关系与典型场景

在单屏直显场景中,绑定关系通常为:一个Framebuffer绑定到一个Primary Plane,该Plane绑定到一个CRTC,CRTC通过Encoder连接到Connector,Connector输出到屏幕。当需要叠加UI层时,再增加一个Overlay Plane绑定到同一个CRTC,形成多平面合成。

在热插拔场景中,Connector的状态会发生变化。当用户插入HDMI线时,内核检测到HPD(Hot Plug Detect)中断,读取EDID,更新Connector的modes列表和connection状态。用户空间收到uevent后,重新枚举资源,选择合适的CRTC和Encoder,执行一次带ALLOW_MODESET的Atomic Commit,完成显示切换。

对象 核心职责 关键Property 典型数量
Framebuffer 像素数据与格式描述 宽高、格式、pitch、modifier 按需创建
Plane 图层合成与变换 FB_ID、CRTC_ID、SRC_*、CRTC_*、rotation 每CRTC多个
CRTC 扫描输出与时序生成 MODE_ID、ACTIVE、OUT_FENCE_PTR 通常1~4个
Encoder 信号编码与协议转换 可能的驱动私有属性 与Connector对应
Connector 物理接口与EDID管理 CRTC_ID、DPMS、link-status 按物理口数量

四、常见误区与排障复盘

误区一:认为Plane必须绑定CRTC才能存在。实际上Plane可以处于未绑定状态,只是不参与扫描输出。用户空间可以动态把Plane从CRTC上解绑,再绑定到另一个CRTC,实现图层迁移。
误区二:忽略Atomic Check的失败原因。Atomic Commit失败时,内核会返回-EINVAL,但具体原因需要开启DRM debug日志(drm.debug=0x1e)才能看到是哪个Property校验失败。常见原因包括:Plane缩放比例超限、CRTC时序与Connector模式不匹配、Framebuffer格式不被Plane支持。
排障建议:使用modetest -M <driver> -p可以打印当前所有KMS对象的Property及取值;使用modetest -M <driver> -a可以查看Atomic能力;使用drm_info可以以树形结构展示对象绑定关系。先确认对象拓扑,再检查Property取值,最后看Atomic Check日志,是最高效的排障路径。

理解KMS对象模型,本质上是在理解“显示配置的状态机”。每个对象都有自己的状态,Property是状态的载体,Atomic Commit是状态迁移的原子操作。掌握这条主线,后续的多平面合成、色彩管理、HDR输出和热插拔处理,都会变得有章可循。