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子系统的核心功能,承担以下三项关键任务:

  1. 显示模式设置(Mode Setting):配置显示控制器的时序参数(分辨率、刷新率、像素时钟等),使显示接口输出正确的视频信号。
  2. 缓冲区管理(Buffer Management):管理显示帧缓冲区(Framebuffer)的创建、映射、切换与销毁,支持双缓冲、三缓冲等机制。
  3. 扫描输出控制(Scanout Control):控制CRTC从帧缓冲区读取像素数据,经过编码器与连接器输出到显示设备。
开发提示:在编写显示控制器驱动时,KMS部分的工作量通常占整个DRM驱动的60%~70%。正确实现KMS回调函数,是驱动能够正常显示画面的关键。

3.4 KMS与DRM的软件架构图

下图展示了KMS在DRM子系统中的位置及其与用户空间、硬件层的关系:

KMS与DRM软件架构关系图 KMS与DRM软件架构关系图 用户空间 (Userspace) 应用程序 / Wayland / Xorg / libdrm 系统调用 / ioctl 接口 DRM 核心框架 (drm_core) drm_device / drm_driver / drm_ioctl 分发 KMS 核心模块 drm_mode_config drm_crtc / drm_plane drm_encoder / drm_connector GEM 内存管理模块 drm_gem_object drm_framebuffer drm_dumb_buffer 硬件抽象层 (HAL) — 显示控制器驱动 drm_crtc_helper_funcs / drm_encoder_helper_funcs / drm_connector_helper_funcs 硬件层 (Hardware) 显示控制器 / HDMI / DP / LVDS / MIPI-DSI 物理接口 协作 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之间的连接关系。这比直接阅读源码更直观。