3. DRM/KMS核心对象
在瑞芯微(Rockchip)Linux 平台上调试显示问题时,几乎所有现象最终都会落到 DRM/KMS 的对象模型上。KMS(Kernel Mode Setting)把显示管线抽象成一组可枚举、可配置、可提交的内核对象:Framebuffer、Plane、CRTC、Encoder、Connector、Property、Mode。理解它们之间的“谁连谁、谁驱动谁、谁在什么时候被提交”,是看懂 modetest 输出、drmModeGetResources 结果以及内核日志的前提。
一、核心对象与职责
可以把一条显示通路理解为“像素数据从内存出发,经过图层合成,被扫描控制器按时序读出,再编码成电信号,最后送到屏幕”。每个环节对应一个 KMS 对象:
- Framebuffer(FB):一块显存缓冲的抽象,记录像素格式、宽高、pitch 和 GEM 句柄。它只描述“数据长什么样”,不关心显示位置。
- Plane:图层。负责把某个 FB 的矩形区域按缩放、裁剪、格式转换后叠加到 CRTC 上。瑞芯微 VOP 通常有多个 Win(如 Smart0/1、Cluster0/1),对应多个 Plane。
- CRTC:显示控制器通道,负责扫描输出与时序生成。它决定分辨率、刷新率、同步极性,并把多个 Plane 合成后的画面按像素时钟送出。
- Encoder:把 CRTC 的并行像素流转换成具体接口信号,如 RGB、MIPI DSI、eDP、HDMI、LVDS。它负责“怎么送出去”。
- Connector:物理输出接口及其连接状态,如 HDMI-A-1、DSI-1、eDP-1。它负责“送到哪里”,并暴露 EDID、连接/断开、DPMS 等属性。
- Property:附加在对象上的可配置键值,如
CRTC_X、CRTC_Y、zpos、alpha、rotation。原子提交正是通过 Property 描述一次完整变更。 - Mode:Connector 上可用的显示时序,包含
hdisplay/vdisplay、clock、前后沿与同步宽度。ModeSet 就是把某个 Mode 应用到 CRTC 并点亮 Connector。
二、ModeSet 流程与原子提交
传统 drmModeSetCrtc 是“一次性设置 CRTC + FB + Connector + Mode”,但它无法精细控制 Plane 的叠加关系。现代驱动普遍使用 Atomic ModeSet:先创建 drmModeAtomicReq,把每个对象的 Property 变更加入请求,最后调用 drmModeAtomicCommit 一次性提交。内核会做原子性检查,全部通过才生效,否则整批回滚。
# 典型调试命令:查看当前资源与对象 ID
modetest -M rockchip -c # 查看 CRTC、Encoder、Connector 连接关系
modetest -M rockchip -p # 查看 Plane 与支持的格式
modetest -M rockchip -s 90:1920x1080 # 在 connector 90 上设置模式
# 内核日志中常见对象名
[drm] Initialized rockchip 1.0.0 for display-subsystem
rockchip-drm display-subsystem: [drm] Connector HDMI-A-1: connected
rockchip-drm display-subsystem: [drm] CRTC 0: mode 1920x1080@60
一次完整的 ModeSet 通常包含以下步骤:
- 打开
/dev/dri/card0,调用drmModeGetResources获取 CRTC、Encoder、Connector、FB 列表。 - 遍历 Connector,读取
connection状态与modes列表,选择 preferred mode 或用户指定模式。 - 根据 Connector 的
encoder_id找到 Encoder,再找到其crtc_id,确定可用 CRTC。 - 用
drmModeAddFB2把 GEM buffer 包装成 Framebuffer,指定格式如DRM_FORMAT_XRGB8888。 - 构造 Atomic 请求:设置 Plane 的
FB_ID、CRTC_ID、SRC_W/H、CRTC_X/Y/W/H;设置 CRTC 的MODE_ID、ACTIVE;设置 Connector 的CRTC_ID。 - 调用
drmModeAtomicCommit,带上DRM_MODE_ATOMIC_ALLOW_MODESET标志,等待内核完成时序切换。
三、典型场景:HDMI 热插拔后的模式切换
在瑞芯微平台上,HDMI 热插拔会触发 HPD 中断,内核读取 EDID 并更新 Connector 的 modes 列表。用户态(如 Weston、Xorg 或自研应用)收到 udev 事件后,重新枚举资源,选择新的 preferred mode,然后提交一次带 ALLOW_MODESET 的 Atomic Commit。此时 CRTC 会重新计算像素时钟与前后沿,Encoder 重新配置 TMDS 输出,Connector 更新 DPMS 状态。若 EDID 读取失败,Connector 可能显示 disconnected,此时任何 ModeSet 都会返回 -ENOTCONN。
四、常见误区与复盘
| 误区 | 现象 | 正确理解 |
|---|---|---|
| 把 Plane 当成“屏幕” | 设置 Plane 后屏幕无变化 | Plane 必须绑定到 CRTC 并参与 Commit 才会显示 |
| 忽略 Connector 状态 | Commit 返回 -ENOTCONN | 先检查 HPD/EDID,确认 connected 再设置 Mode |
| 认为 Mode 属于 CRTC | 找不到 mode 列表 | Mode 挂在 Connector 上,CRTC 通过 MODE_ID 引用 |
| 混用 Legacy 与 Atomic API | 属性设置不生效或冲突 | 同一路径统一使用 Atomic Commit,避免状态竞争 |
| 忘记设置 CRTC_ID | Plane 不显示 | Plane 的 CRTC_ID 必须指向目标 CRTC,否则处于未激活状态 |
复盘时建议按“对象链路”逐级排查:先看 Connector 是否 connected 且有 Mode,再看 Encoder 是否绑定 CRTC,再看 CRTC 是否 ACTIVE 且 MODE_ID 有效,最后看 Plane 的 FB_ID、CRTC_ID 与坐标是否合法。瑞芯微 VOP 的 Win 数量有限,多图层场景还要关注 zpos 与带宽限制,避免因叠加过多导致 Commit 失败。掌握这套对象模型后,无论是用 modetest 验证,还是阅读内核 rockchip_drm_vop.c,都能快速定位问题所在。