2. KMS(Kernel Mode Setting)基础:KMS与UMS的区别、KMS初始化流程、modeset操作的基本步骤
2.1 KMS 与 UMS:两种显示模式设置机制
在 Linux DRM 子系统中,Mode Setting(模式设置)负责配置显示控制器的时序、分辨率、像素时钟以及帧缓冲区的格式。历史上存在两种实现路径:UMS(User Mode Setting) 和 KMS(Kernel Mode Setting)。理解两者的差异是掌握现代 DRM 驱动的基础。
| 对比维度 | UMS(User Mode Setting) | KMS(Kernel Mode Setting) |
|---|---|---|
| 控制主体 | 用户空间的 X Server 或 Wayland compositor | 内核空间的 DRM 驱动 |
| 模式切换路径 | 用户态通过 ioctl 直接操作硬件寄存器 | 用户态通过 DRM ioctl 请求内核执行模式设置 |
| 多进程支持 | 弱,通常需要独占显卡 | 强,内核统一管理资源,支持 VT 切换 |
| 电源管理 | 用户态自行处理,容易遗漏 | 内核统一管理,suspend/resume 自动恢复 |
| 安全性 | 用户态可直接访问硬件,存在风险 | 内核态校验参数,防止非法操作 |
| 现代嵌入式趋势 | 已淘汰,仅遗留代码中存在 | 标准方案,所有新驱动均采用 |
关键结论:KMS 将模式设置的控制权收归内核,使得显示状态切换、VT 切换、电源管理恢复等操作变得原子化且安全。嵌入式开发者只需关注 KMS 路径。
2.2 KMS 初始化流程:从驱动加载到设备就绪
KMS 初始化是 DRM 驱动加载的核心阶段,涉及多个内核组件的协同。以下流程以嵌入式平台(如 i.MX、Rockchip、Allwinner)为背景,描述从 probe() 到 drm_dev_register() 的完整路径。
- 平台驱动 probe() 触发:Device Tree 匹配后,调用驱动的
probe()函数。 - DRM 设备分配:调用
drm_dev_alloc()分配struct drm_device。 - DRM 驱动私有数据初始化:填充
struct drm_driver中的 fops、gem 回调、prime 回调等。 - KMS 组件注册:调用
drm_mode_config_init()初始化模式配置。 - CRTC/Encoder/Connector/Plane 创建:根据硬件能力创建 KMS 对象并注册。
- fbdev 兼容层(可选):调用
drm_fbdev_generic_setup()提供早期控制台。 - DRM 设备注册:调用
drm_dev_register()暴露设备节点/dev/dri/card0。
嵌入式要点:步骤⑤是驱动开发的核心工作量所在。CRTC 需要绑定硬件显示控制器,Encoder 对应 RGB/LVDS/HDMI 发送器,Connector 代表物理接口,Plane 负责图层叠加。Device Tree 中的
ports 节点正是用来描述这些对象之间的连接关系。
2.3 Modeset 操作的基本步骤:用户空间的视角
当 KMS 初始化完成后,用户空间应用程序(如 Weston、kmscube、modetest)通过标准 DRM ioctl 执行 modeset。一个完整的 modeset 操作包含以下原子步骤:
| 步骤 | DRM ioctl / 操作 | 说明 |
|---|---|---|
| 1. 打开设备 | open("/dev/dri/card0") |
获取 DRM 文件描述符 |
| 2. 获取资源 | drmModeGetResources() |
获取 CRTC、Encoder、Connector、FB 的 ID 列表 |
| 3. 查找 Connector | drmModeGetConnector() |
遍历 Connector,找到已连接的接口 |
| 4. 获取 Mode | connector->modes[] |
从 Connector 的 modes 列表中选择合适的分辨率 |
| 5. 创建 Framebuffer | drmModeAddFB() / drmModeAddFB2() |
注册内存缓冲区为 DRM framebuffer |
| 6. 设置 CRTC | drmModeSetCrtc() |
将 CRTC 与 Connector、Mode、FB 绑定,激活显示 |
| 7. Page Flip(可选) | drmModePageFlip() |
无撕裂切换帧缓冲区,用于动画/视频 |
常见陷阱:步骤⑥
drmModeSetCrtc() 会立即生效,但不会等待 VBlank。如果需要在垂直消隐期切换帧缓冲区以避免撕裂,必须使用 drmModePageFlip() 并配合 DRM_EVENT 等待。在嵌入式平台上,Page Flip 的完成依赖于 CRTC 的 VBlank 中断,请确保中断正确注册。
2.4 KMS 对象模型:CRTC、Encoder、Connector、Plane
KMS 将显示硬件抽象为四个核心对象,它们通过 Modeset 对象图 相互连接。理解这个模型是编写 Device Tree 绑定的前提。
| KMS 对象 | 硬件对应 | 主要职责 | Device Tree 节点示例 |
|---|---|---|---|
| CRTC | 显示控制器(Display Controller) | 生成时序信号、扫描帧缓冲区、产生 VBlank | display-controller@... |
| Encoder | 显示信号发送器(LVDS/HDMI/DSI 发送器) | 将 CRTC 输出的并行数据编码为串行信号 | lcdif@... / hdmi@... |
| Connector | 物理接口(HDMI 座、LVDS 插座、eDP 接口) | 检测显示器连接状态、获取 EDID、提供可用 modes | hdmi-connector / lvds-connector |
| Plane | 硬件图层(Overlay / Primary / Cursor) | 管理帧缓冲区与 CRTC 的叠加关系 | 通常由驱动内部创建,不直接对应 DT 节点 |
核心记忆点:KMS 初始化流程是“自底向上”的硬件发现过程,而 modeset 操作是“自顶向下”的配置过程。Device Tree 的作用正是在初始化阶段告诉驱动:CRTC 连接了哪个 Encoder,Encoder 又连接了哪个 Connector。这个连接关系通过
ports 和 endpoint 节点描述,我们将在后续章节详细展开。