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。
显示通路的对象关系可以概括为:Framebuffer → Plane → CRTC → Encoder → Connector → 屏幕。Property 贯穿所有对象,Mode 属于 Connector,ModeSet 是把这条链路一次性配置生效的动作。
DRM/KMS 对象链路与 ModeSet 提交流程 Framebuffer 像素格式 / pitch Plane 缩放 / 裁剪 / zpos CRTC 时序 / 扫描输出 Encoder RGB / DSI / HDMI Connector EDID / 连接状态 Property:CRTC_X / CRTC_Y / zpos / alpha / rotation / mode_id / DPMS 所有对象通过 Property 描述一次原子变更,由 Atomic Commit 统一提交 1. 获取资源 GetResources / GetConnector 2. 选择 Mode EDID / preferred mode 3. 创建 FB drmModeAddFB2 4. Atomic Commit Plane + CRTC + Connector 瑞芯微 VOP 中:Plane 对应 Win,CRTC 对应 VOP 通道,Encoder 对应 VOP 输出接口,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 通常包含以下步骤:

  1. 打开 /dev/dri/card0,调用 drmModeGetResources 获取 CRTC、Encoder、Connector、FB 列表。
  2. 遍历 Connector,读取 connection 状态与 modes 列表,选择 preferred mode 或用户指定模式。
  3. 根据 Connector 的 encoder_id 找到 Encoder,再找到其 crtc_id,确定可用 CRTC。
  4. 用 drmModeAddFB2 把 GEM buffer 包装成 Framebuffer,指定格式如 DRM_FORMAT_XRGB8888。
  5. 构造 Atomic 请求:设置 Plane 的 FB_ID、CRTC_ID、SRC_W/H、CRTC_X/Y/W/H;设置 CRTC 的 MODE_ID、ACTIVE;设置 Connector 的 CRTC_ID。
  6. 调用 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。

调试时优先确认三件事:Connector 是否 connected、Mode 是否在 modes 列表中、CRTC 是否已被其他 Connector 占用。很多“黑屏”并非驱动问题,而是对象状态不匹配。

四、常见误区与复盘

误区 现象 正确理解
把 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,都能快速定位问题所在。