4. KMS对象模型
在Linux DRM子系统中,KMS(Kernel Mode Setting)负责显示管线的配置与提交。理解KMS的关键,不是记住一堆结构体名字,而是建立一条“从显存像素到屏幕像素”的对象链路。这条链路由六类核心对象构成:Framebuffer、Plane、CRTC、Encoder、Connector,以及贯穿所有对象的Property机制。它们之间既有层级关系,也有动态绑定关系,最终通过Atomic Commit一次性提交。
一、核心对象职责与层级关系
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绑定,最终把信号送到物理屏幕。
二、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 | 按物理口数量 |
四、常见误区与排障复盘
-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输出和热插拔处理,都会变得有章可循。