3. KMS与DRM的关系:KMS作为DRM子系统的核心功能
在Linux图形显示栈中,DRM(Direct Rendering Manager)是内核层负责图形输出与渲染管理的框架,而KMS(Kernel Mode Setting)则是DRM子系统中最核心、最基础的功能模块。理解二者之间的关系,是掌握显示控制器驱动适配的前提。
3.1 DRM的整体架构与KMS的定位
DRM子系统从功能上可划分为两大模块:
- KMS(Kernel Mode Setting):负责显示模式设置、缓冲区管理、扫描输出控制,是显示控制器驱动的核心。
- GEM(Graphics Execution Manager):负责显存分配、GPU命令提交与同步,是渲染加速的基础。
KMS与GEM共同构成DRM框架,但KMS直接面向显示控制器硬件,GEM则面向GPU引擎。在嵌入式或非GPU平台上,KMS可以独立存在,而GEM则不是必须的。
核心观点:KMS是DRM的“显示输出”部分,GEM是DRM的“渲染加速”部分。KMS负责“看得见”的屏幕管理,GEM负责“算得快”的图形处理。
3.2 KMS与DRM的层次关系
从内核源码与驱动开发的角度,KMS与DRM的关系可以用下表清晰描述:
| 维度 | DRM(整体框架) | KMS(核心功能) |
|---|---|---|
| 定义 | 内核图形子系统,管理显示输出与GPU渲染 | DRM子系统中负责显示模式设置与缓冲区管理的功能集合 |
| 核心对象 | drm_device, drm_file, drm_driver | drm_crtc, drm_plane, drm_encoder, drm_connector, drm_framebuffer |
| 用户空间接口 | /dev/dri/card0, libdrm, ioctl | DRM_IOCTL_MODE_* 系列 ioctl |
| 硬件抽象 | GPU、显示控制器、编解码器 | 显示控制器(CRTC)、显示接口(Encoder/Connector) |
| 驱动注册 | drm_driver 结构体注册 | drm_mode_config_funcs 回调实现 |
| 依赖关系 | KMS + GEM 共同组成完整DRM | KMS 必须运行在 DRM 框架之上 |
3.3 KMS在DRM中的核心职责
KMS作为DRM子系统的核心功能,承担以下三项关键任务:
- 显示模式设置(Mode Setting):配置显示控制器的时序参数(分辨率、刷新率、像素时钟等),使显示接口输出正确的视频信号。
- 缓冲区管理(Buffer Management):管理显示帧缓冲区(Framebuffer)的创建、映射、切换与销毁,支持双缓冲、三缓冲等机制。
- 扫描输出控制(Scanout Control):控制CRTC从帧缓冲区读取像素数据,经过编码器与连接器输出到显示设备。
开发提示:在编写显示控制器驱动时,KMS部分的工作量通常占整个DRM驱动的60%~70%。正确实现KMS回调函数,是驱动能够正常显示画面的关键。
3.4 KMS与DRM的软件架构图
下图展示了KMS在DRM子系统中的位置及其与用户空间、硬件层的关系:
3.5 KMS对象模型与DRM框架的映射
KMS在DRM框架中通过一组标准对象来抽象显示控制器的硬件功能。这些对象之间的关系构成了KMS的“显示管线”:
- drm_crtc:代表显示控制器核心,负责从帧缓冲区读取数据并生成视频时序信号。
- drm_plane:代表显示图层,支持硬件叠加、缩放、旋转等操作。
- drm_encoder:代表显示编码器,将CRTC输出的数字信号转换为特定接口格式(如HDMI、DP、LVDS)。
- drm_connector:代表物理连接器,检测显示设备是否连接、读取EDID信息。
- drm_framebuffer:代表帧缓冲区,存储待显示的像素数据。
常见误区:初学者容易将KMS与DRM混为一谈。实际上,KMS只是DRM的一部分,DRM还包含GEM内存管理、fence同步、dma-buf共享等机制。在编写显示控制器驱动时,如果只涉及显示输出而不需要GPU渲染,可以只实现KMS部分,不实现GEM。
3.6 KMS初始化在DRM驱动中的入口
在DRM驱动中,KMS的初始化通常通过以下步骤完成:
// 1. 在 drm_driver 结构体中注册 KMS 回调
static struct drm_driver my_drm_driver = {
.driver_features = DRIVER_MODESET | DRIVER_GEM,
.fops = &my_drm_fops,
.name = "my_display",
.desc = "My Display Controller Driver",
.date = "20250101",
.major = 1,
.minor = 0,
};
// 2. 在 probe 函数中初始化 KMS 配置
static int my_display_probe(struct platform_device *pdev)
{
struct drm_device *drm;
int ret;
drm = drm_dev_alloc(&my_drm_driver, &pdev->dev);
if (IS_ERR(drm))
return PTR_ERR(drm);
// 初始化模式配置
ret = drm_mode_config_init(drm);
if (ret)
goto err_free;
// 设置最小/最大分辨率等参数
drm->mode_config.min_width = 640;
drm->mode_config.max_width = 4096;
drm->mode_config.min_height = 480;
drm->mode_config.max_height = 2160;
// 注册 CRTC、Encoder、Connector 等 KMS 对象
ret = my_display_kms_init(drm);
if (ret)
goto err_config;
// 注册 DRM 设备
ret = drm_dev_register(drm, 0);
if (ret)
goto err_kms;
platform_set_drvdata(pdev, drm);
return 0;
err_kms:
my_display_kms_fini(drm);
err_config:
drm_mode_config_cleanup(drm);
err_free:
drm_dev_put(drm);
return ret;
}
3.7 KMS与DRM的关系总结
下表从驱动开发者的视角,总结了KMS与DRM的关键差异与协作关系:
| 对比项 | DRM(整体) | KMS(核心功能) |
|---|---|---|
| 驱动注册 | drm_dev_alloc + drm_dev_register | drm_mode_config_init + 对象注册 |
| 核心数据结构 | drm_device, drm_driver | drm_mode_config, drm_crtc, drm_plane |
| 用户空间接口 | /dev/dri/card0, 通用ioctl | DRM_IOCTL_MODE_GETCRTC, DRM_IOCTL_MODE_SETCRTC 等 |
| 硬件依赖 | 需要GPU或显示控制器 | 只需要显示控制器 |
| 驱动开发重点 | 实现drm_driver回调 | 实现drm_crtc_helper_funcs等辅助回调 |
| 调试工具 | drm_info, modetest, libdrm测试 | modetest -M 设备名, cat /sys/kernel/debug/dri/*/state |
学习建议:在开始编写显示控制器驱动之前,建议先通过
modetest 工具观察现有KMS驱动的行为,理解CRTC、Encoder、Connector之间的连接关系。这比直接阅读源码更直观。