2. DRM基础概念
在Linux图形显示子系统中,DRM(Direct Rendering Manager)负责GPU资源管理与显存调度,而KMS(Kernel Mode Setting)是DRM内部专门负责显示模式设置与画面扫描输出的子系统。理解DRM/KMS,关键不是记住一堆缩写,而是建立“谁描述屏幕、谁描述时序、谁描述图层、谁描述显存”的对象模型。只有把CRTC、Encoder、Connector、Plane、Framebuffer这五个核心对象的关系理清,后续的色彩管理、VBlank同步、原子提交才有落脚点。
一、核心原理:从Framebuffer到屏幕的完整链路
KMS把显示输出抽象为一条从显存到物理接口的流水线。最底层是Framebuffer,它描述一块可以被扫描输出的像素缓冲区,包含宽、高、像素格式、pitch(行跨距)以及底层GEM对象。Plane负责把Framebuffer“贴”到显示管道的某个图层上,支持主图层、叠加图层和光标图层,并带有位置、缩放、旋转、Alpha混合等属性。CRTC是显示控制器,负责读取Plane合成后的像素,并按照指定时序产生扫描信号。Encoder把CRTC输出的像素流转换为特定接口协议,例如HDMI TMDS、DisplayPort链路或MIPI DSI。Connector则代表物理输出接口及其连接的显示设备,保存EDID、连接状态、支持的模式列表和DPMS电源状态。
这五个对象不是并列关系,而是“数据源—图层—时序控制器—协议编码—物理接口”的串联关系。Framebuffer提供像素,Plane决定像素如何进入合成,CRTC决定何时扫描,Encoder决定以什么电气协议输出,Connector决定输出到哪块屏幕。任何一个环节配置错误,都会表现为黑屏、花屏、分辨率异常或VBlank中断不触发。
二、方法流程:一次模式设置的典型路径
当用户空间调用 drmModeSetCrtc() 或提交 atomic commit 时,内核会按以下顺序校验并生效:首先根据 Connector 的 EDID 选择 drm_display_mode,然后确认该 Connector 能通过某个 Encoder 连接到目标 CRTC,接着检查 CRTC 上挂载的 Plane 是否满足格式与缩放能力,最后把 Framebuffer 的 GEM 句柄转换为扫描地址写入硬件寄存器。整个过程必须满足资源约束,否则返回 -EINVAL。
// 典型对象查找与模式设置伪代码
connector = drmModeGetConnector(fd, conn_id);
mode = pick_mode(connector->modes, connector->count_modes);
encoder = drmModeGetEncoder(fd, connector->encoder_id);
crtc_id = encoder->crtc_id;
drmModeSetCrtc(fd, crtc_id, fb_id, 0, 0,
&conn_id, 1, &mode);
// 原子提交方式
drmModeAtomicAddProperty(req, plane_id, "FB_ID", fb_id);
drmModeAtomicAddProperty(req, crtc_id, "MODE_ID", blob_id);
drmModeAtomicAddProperty(req, conn_id, "CRTC_ID", crtc_id);
drmModeAtomicCommit(fd, req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
三、案例场景:双屏异显与图层叠加
假设一台设备有两个 Connector:HDMI-A-1 和 eDP-1。系统启动后,内核为每个 Connector 创建独立的 CRTC 资源。用户空间希望 HDMI 输出 1920×1080 桌面,eDP 输出 2560×1600 主界面,同时在 eDP 上叠加一个视频层。此时需要为两个 CRTC 分别绑定 Plane:eDP 的 Primary Plane 承载桌面 Framebuffer,Overlay Plane 承载视频 Framebuffer;HDMI 的 Primary Plane 承载另一块 Framebuffer。两个 CRTC 各自产生 VBlank,色彩管理模块需要根据 Connector 类型分别加载不同的 Gamma LUT 和 CTM 矩阵。如果错误地把两个 Connector 绑定到同一个 CRTC,就会出现镜像而非异显。
四、误区与复盘:常见理解偏差
误区一:把 Connector 当成“屏幕本身”。Connector 只是接口抽象,拔插、EDID 读取、DPMS 都发生在这一层,但实际显示设备可能通过转换芯片连接,状态需要结合 /sys/class/drm/card0-HDMI-A-1/status 判断。
误区二:认为 Plane 一定属于某个 CRTC。Plane 通过 possible_crtcs 位掩码声明可挂载的 CRTC 集合,实际绑定关系在 commit 时确定,可以动态切换。
误区三:忽略 Framebuffer 与 GEM 的区别。Framebuffer 是扫描输出视图,GEM 是显存对象;同一个 GEM 可以创建多个不同格式或偏移的 Framebuffer。
五、核心对象速查清单
| 对象 | 职责 | 关键属性/接口 | 与色彩/VBlank的关系 |
|---|---|---|---|
| Framebuffer | 描述可扫描的像素缓冲 | width/height/pixel_format/pitch/handle | 决定像素格式与色彩空间输入 |
| Plane | 图层合成与定位 | crtc_id/fb_id/src_x/src_y/alpha/blend_mode | 参与图层混合,影响最终颜色 |
| CRTC | 时序控制与扫描输出 | mode/active/gamma_lut/ctm | 产生 VBlank,承载 Gamma/CTM |
| Encoder | 协议编码 | encoder_type/possible_crtcs/possible_clones | 决定输出接口类型与带宽 |
| Connector | 物理接口与设备状态 | connector_type/status/modes/dpms | EDID 决定模式,影响色彩能力上报 |
调试建议:使用 modetest -M <driver> 可以列出当前系统中所有 CRTC、Encoder、Connector、Plane 及其绑定关系;cat /sys/kernel/debug/dri/0/state 可查看原子状态。先确认对象拓扑,再排查色彩与同步问题,能显著减少盲目试错。