3. KMS (Kernel Mode Setting) 核心:KMS的作用、与FBDEV的区别、Atomic Modeset与Legacy Modeset对比
3.1 KMS 的核心作用
KMS(Kernel Mode Setting)是 DRM 框架中负责显示模式配置与管理的核心子系统。它将显示控制权从用户空间(X Server、Wayland Compositor)转移到内核空间,确保模式切换(分辨率、刷新率、图层叠加)的原子性与安全性。
KMS 的主要职责包括:
- 显示模式管理:枚举显示器支持的时序参数(EDID 解析),设置分辨率、刷新率、色彩深度。
- 平面(Plane)管理:管理硬件图层(Primary、Overlay、Cursor),控制每个平面的缓冲区地址、裁剪区域、透明度。
- CRTC 控制:管理显示控制器(CRTC)的扫描时序、时钟源、帧缓冲区关联。
- 连接器(Connector)管理:检测显示器热插拔(Hotplug)、获取物理连接状态(HDMI/DP/MIPI DSI)。
- 内存管理:分配与释放帧缓冲区(FB)、处理 DMA-BUF 导入导出。
关键概念:KMS 将显示硬件抽象为
drm_framebuffer → drm_plane → drm_crtc → drm_encoder → drm_connector 五级管道,所有配置操作均通过 drm_mode_config 结构体协调。
3.2 KMS 与 FBDEV 的区别
FBDEV(Framebuffer Device)是 Linux 内核早期的显示框架,仅提供简单的帧缓冲区操作接口。KMS 在功能、性能、安全性上全面超越 FBDEV,是当前嵌入式与桌面显示的主流方案。
| 对比维度 | FBDEV | KMS (DRM) |
|---|---|---|
| 模式切换 | 通过 FBIOPUT_VSCREENINFO 切换,无校验,易撕裂 |
通过 drmModeSetCrtc 或原子提交,带完整校验与 VBlank 同步 |
| 多图层支持 | 仅单层(主帧缓冲),无硬件叠加 | 支持 Primary + Overlay + Cursor 多平面,硬件合成 |
| 热插拔检测 | 无原生支持,需轮询或外部机制 | 通过 drm_connector 的 HPD 中断,实时通知 |
| 内存管理 | 固定线性内存,无 DMA-BUF 支持 | 支持 GEM、DMA-BUF、显存分配与共享 |
| 多显示器 | 仅单显示输出,扩展需额外驱动 | 原生支持多 CRTC + 多 Connector,克隆/扩展/独立输出 |
| 用户空间接口 | /dev/fbX,mmap 直接操作 |
/dev/dri/cardX,通过 IOCTL 操作,更安全 |
| 性能 | CPU 频繁拷贝,功耗高 | 支持 Page Flip、零拷贝、GPU 合成 |
迁移建议:SA8155 平台已完全弃用 FBDEV,所有显示链路均基于 DRM/KMS。若遗留代码使用
fbdev 接口,需通过 modeset 库或直接调用 DRM IOCTL 重写。
3.3 Atomic Modeset 与 Legacy Modeset 对比
KMS 提供两种模式设置接口:Legacy Modeset(传统模式)和 Atomic Modeset(原子模式)。Atomic 是 DRM 框架的现代标准,SA8155 平台强制要求使用 Atomic API。
3.3.1 Legacy Modeset 接口
Legacy 接口通过独立的 IOCTL 调用分别设置 CRTC、Plane、Connector:
drmModeSetCrtc()— 设置 CRTC 与帧缓冲绑定drmModeSetPlane()— 设置平面位置与缓冲区drmModeConnectorSetProperty()— 设置连接器属性
缺点:非原子性,多次调用间可能出现中间状态(撕裂、闪烁);无回滚机制;需用户空间自行保证调用顺序。
3.3.2 Atomic Modeset 接口
Atomic 接口通过一次 drmModeAtomicCommit() 调用提交完整的状态变更:
- 构建
drmModeAtomicReq对象,添加所有需要变更的属性 - 内核校验整个状态的一致性(带宽、时钟、图层约束)
- 支持 TEST_ONLY 标志进行预校验,不实际生效
- 支持 NONBLOCK 异步提交,通过 event 通知完成
- 失败时自动回滚,不改变当前显示状态
SA8155 平台强制要求:所有显示配置必须使用 Atomic Modeset。Legacy 接口在 DPU 驱动中已被标记为
deprecated,调用会返回 -ENOTSUPP。
| 对比维度 | Legacy Modeset | Atomic Modeset |
|---|---|---|
| 调用方式 | 多次独立 IOCTL | 单次 drmModeAtomicCommit |
| 原子性 | 无,中间状态可见 | 全有或全无,无中间状态 |
| 回滚 | 需用户手动恢复 | 内核自动回滚 |
| 预校验 | 不支持 | 支持 DRM_MODE_ATOMIC_TEST_ONLY |
| 异步提交 | 不支持 | 支持 DRM_MODE_PAGE_FLIP_EVENT |
| 属性支持 | 有限(位置、大小、FB ID) | 所有 KMS 属性(alpha、zpos、color_encoding、scaling) |
| 性能 | 多次上下文切换,延迟高 | 单次调用,延迟低,适合高帧率 |
3.3.3 Atomic 提交流程示例
// 1. 创建 atomic request
drmModeAtomicReq *req = drmModeAtomicAlloc();
// 2. 添加属性变更
drmModeAtomicAddProperty(req, crtc_id, crtc_active_prop, 1);
drmModeAtomicAddProperty(req, crtc_id, mode_id_prop, mode_blob_id);
drmModeAtomicAddProperty(req, plane_id, src_x_prop, 0);
drmModeAtomicAddProperty(req, plane_id, src_y_prop, 0);
drmModeAtomicAddProperty(req, plane_id, crtc_id_prop, crtc_id);
drmModeAtomicAddProperty(req, conn_id, crtc_id_prop, crtc_id);
// 3. 提交(带预校验)
int ret = drmModeAtomicCommit(fd, req, DRM_MODE_ATOMIC_TEST_ONLY, NULL);
if (ret) {
// 校验失败,处理错误
drmModeAtomicFree(req);
return ret;
}
// 4. 实际提交(异步)
ret = drmModeAtomicCommit(fd, req, DRM_MODE_ATOMIC_NONBLOCK |
DRM_MODE_PAGE_FLIP_EVENT, &user_data);
drmModeAtomicFree(req);
调试陷阱:Atomic 提交失败时,通过
drmModeAtomicCommit 返回的 errno 判断原因。常见错误:-EINVAL(属性值非法)、-ENOSPC(带宽超限)、-EACCES(权限不足)。在 SA8155 上,dmesg 中 DPU 驱动会打印详细的校验失败日志。
3.4 SA8155 平台 KMS 调试要点
在 SA8155 平台上调试 KMS 时,重点关注以下工具与接口:
- modetest:libdrm 提供的测试工具,可枚举 Connector、CRTC、Plane,执行 Legacy 或 Atomic 模式设置。
- dmesg:DPU 驱动(msm_dpu)的调试日志,通过
drm.debug=0x1e内核参数开启。 - sysfs 接口:
/sys/kernel/debug/dri/0/下查看 KMS 状态、VBlank 计数、带宽使用。 - Atomic 校验日志:当 Atomic 提交失败时,驱动会在
dmesg中打印[dpu]前缀的详细原因。
快速验证命令:
modetest -M msm_dpu -a 以 Atomic 模式枚举所有 KMS 对象;modetest -M msm_dpu -s 42:1920x1080@60 -a 设置指定 Connector 的分辨率。