Linux内核显示驱动KMS驱动与设备树配置实战 - 第1节

1.1 Linux 图形显示栈架构

Linux 图形显示栈是一个多层协作的软件体系,从用户态的图形应用程序到底层的显示硬件,每一层都承担着特定的职责。理解整个栈的层次结构,是掌握 KMS/DRM 子系统的基础。

核心认知: Linux 图形栈的设计哲学是“分层解耦”——应用层不关心硬件细节,内核层只负责资源调度与硬件控制,硬件层专注像素输出。KMS/DRM 正是连接用户态与硬件的关键内核桥梁。
Linux 图形显示栈架构 用户态应用层 GUI 桌面 (GNOME/KDE) | 游戏 (Steam/Proton) | 视频播放器 (VLC/MPV) | 合成器 (Wayland Compositor / Xorg) 图形库与协议层 Wayland / X11 协议 | EGL / OpenGL ES / Vulkan 图形库 用户态 DRM 库 (libdrm) 提供 ioctl 封装、内存管理 (GEM)、模式设置 (KMS) 接口 内核 KMS/DRM 子系统 CRTC / Encoder / Connector / Plane / FB | GEM / DMA-BUF | 驱动框架 硬件层 (GPU / Display Controller / PHY / 显示器) 用户空间 内核空间
关键洞察: 在嵌入式显示驱动开发中,我们主要工作在内核 KMS/DRM 子系统层和硬件层。用户态的 libdrm 和图形库通常由 SoC 厂商或开源社区提供,但驱动开发者需要深刻理解它们与内核的交互协议(ioctl)。

1.2 KMS 与 DRM 的关系

DRM (Direct Rendering Manager) 和 KMS (Kernel Mode Setting) 是 Linux 图形子系统中两个紧密关联但职责不同的组件。很多初学者容易混淆它们,理解其关系是入门的关键。

维度 DRM (Direct Rendering Manager) KMS (Kernel Mode Setting)
核心职责 图形缓冲区管理、渲染同步、GPU 命令调度 显示模式设置、分辨率/刷新率控制、显示管线配置
主要功能 GEM 对象管理、DMA-BUF 共享、Fence 同步、GPU 上下文 CRTC/Encoder/Connector/Plane 配置、VBLANK 控制、DPMS
用户态接口 drmIoctl() — DRM_IOCTL_GEM_* 系列 drmModeSetCrtc() — DRM_IOCTL_MODE_* 系列
硬件依赖 需要 GPU 硬件支持(至少 2D 加速) 需要 Display Controller 硬件支持
历史渊源 最初为 DRI (Direct Rendering Infrastructure) 而生 从 DRM 中分离,替代 UMS (User Mode Setting)
DRM 子系统内部结构:KMS 是 DRM 的核心子模块 DRM 子系统 (Direct Rendering Manager) KMS (Kernel Mode Setting) 显示模式设置核心 CRTC 管理 Encoder / Connector 配置 Plane 叠加与合成 VBLANK / DPMS 控制 EDID / 显示参数解析 DRM 其他核心模块 GEM (Graphics Execution Manager) DMA-BUF 缓冲区共享 Fence / Sync 同步机制 GPU 命令提交与调度 驱动私有 ioctl 扩展 KMS 是 DRM 的子集,但却是显示驱动开发中最常打交道的部分
常见误区: 很多开发者认为 KMS 是一个独立的子系统。实际上,KMS 是 DRM 框架内的一组 ioctl 命令和内核对象(CRTC/Encoder/Connector/Plane)。在 Linux 内核源码中,KMS 相关代码位于 drivers/gpu/drm/ 目录下,与 DRM 核心代码交织在一起。驱动开发时,我们通常说“实现一个 DRM 驱动”,但实际工作量的 70% 以上是 KMS 相关的显示管线配置。

1.3 KMS 核心概念详解

KMS 的核心抽象模型由四个基本对象组成:CRTC、Encoder、Connector 和 Plane。它们共同构成了从帧缓冲区到显示器的完整数据通路。理解这四个对象及其关系,是编写 KMS 驱动的第一步。

1.3.1 四大核心对象一览

对象 英文全称 硬件对应 核心职责
CRTC Cathode Ray Tube Controller 显示控制器 (Display Controller) 扫描输出、时序生成、帧缓冲读取、VBLANK 中断
Encoder Encoder 显示信号编码器 (LVDS/HDMI/DP 编码器) 像素数据编码为特定接口信号、时钟调整
Connector Connector 物理连接器 (HDMI/DP/VGA 接口) 物理连接检测、EDID 读取、热插拔事件
Plane Plane 硬件图层 (Overlay/Sprite) 独立帧缓冲、缩放、色彩转换、图层叠加
KMS 显示管线:四大核心对象的数据流 Framebuffer 像素数据缓冲区 Plane 图层叠加 / 缩放 支持多个 Plane 叠加 CRTC 时序生成 / 扫描 VBLANK 中断 Encoder 信号编码 / 时钟调整 LVDS / HDMI / DP Connector 物理连接 / EDID 热插拔检测 显示器 最终显示输出 CRTC 输出像素流到 Encoder KMS 显示管线数据流总结 Framebuffer → Plane(s) → CRTC → Encoder → Connector → 显示器 每个 CRTC 可以绑定多个 Plane(图层叠加) 每个 CRTC 连接一个 Encoder,每个 Encoder 连接一个 Connector 一个 Connector 对应一个物理显示器接口

1.3.2 CRTC — 显示控制器的核心

CRTC 是 KMS 中最重要的对象,它模拟了传统 CRT 显示器的扫描控制逻辑。在现代 LCD 显示器中,CRTC 对应 SoC 内部的 Display Controller 或 Video Timing Controller。

CRTC 的关键能力:
  • 时序生成: 产生 H-sync、V-sync、DE (Data Enable)、像素时钟等时序信号
  • 帧缓冲读取: 从内存中指定的 Framebuffer 地址读取像素数据
  • 扫描输出: 按行/帧顺序将像素数据推送到显示管线
  • VBLANK 控制: 在帧消隐期产生中断,用于同步渲染和避免撕裂
  • 模式设置: 配置分辨率、刷新率、像素格式等显示参数

在设备树中,CRTC 通常对应一个显示控制器节点。例如,在 i.MX8M Plus 平台上,LCDIF 控制器就是一个 CRTC 实例:

/* 设备树中 CRTC 对应的显示控制器节点示例 */
&lcdif {
    compatible = "fsl,imx8mp-lcdif";
    reg = <0x32e00000 0x10000>;
    interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&clk IMX8MP_CLK_MEDIA_DISP1_PIX>,
             <&clk IMX8MP_CLK_MEDIA_DISP1_APB>;
    clock-names = "pix", "axi";
    assigned-clocks = <&clk IMX8MP_CLK_MEDIA_DISP1_PIX>;
    assigned-clock-parents = <&clk IMX8MP_CLK_VIDEO_PLL1_OUT>;
    status = "disabled";
};

1.3.3 Encoder — 信号编码与转换

Encoder 负责将 CRTC 输出的并行像素数据转换为特定显示接口所需的串行信号格式。不同的显示接口(HDMI、DP、LVDS、MIPI DSI)需要不同的 Encoder 硬件。

Encoder 类型 输出接口 典型硬件 驱动中的实现
LVDS Encoder LVDS 差分对 i.MX8 LVDS 桥接器 drm_encoder_init() + DRM_MODE_ENCODER_LVDS
HDMI Encoder TMDS 信号 Synopsys DesignWare HDMI drm_encoder_init() + DRM_MODE_ENCODER_TMDS
DP Encoder DisplayPort 差分对 Cadence DP TX drm_encoder_init() + DRM_MODE_ENCODER_DP
MIPI DSI Encoder MIPI D-PHY 差分对 Rockchip DSI 控制器 drm_encoder_init() + DRM_MODE_ENCODER_DSI

1.3.4 Connector — 物理连接与检测

Connector 代表物理显示接口,负责检测显示器是否连接、读取显示器的 EDID 信息(包含支持的分辨率、刷新率等)、处理热插拔事件。

开发要点: Connector 的 EDID 解析是驱动开发中的常见难点。如果显示器无法正确识别,首先要检查 I2C 总线是否正常、EDID 读取时序是否正确。可以使用 drm_edid_read() 和 drm_add_edid_modes() 标准接口来简化开发。

1.3.5 Plane — 硬件图层与合成

Plane 是 KMS 中相对较新的概念,它代表一个独立的硬件图层。每个 Plane 可以有自己的 Framebuffer、位置、大小、缩放比例和色彩格式。CRTC 会将所有绑定的 Plane 按照 Z-order 顺序合成最终的显示画面。

Plane 分为两种类型:

  • Primary Plane: 每个 CRTC 必须有一个 Primary Plane,它是显示画面的基础层
  • Overlay Plane: 可选的叠加层,用于实现视频播放、UI 悬浮层、光标等效果
/* 驱动中创建 Plane 的典型代码片段 */
static int driver_plane_init(struct drm_device *drm)
{
    struct drm_plane *plane;
    int ret;

    /* 分配并初始化一个 Plane */
    plane = kzalloc(sizeof(*plane), GFP_KERNEL);
    if (!plane)
        return -ENOMEM;

    /* 使用通用 Plane 辅助函数初始化 */
    ret = drm_universal_plane_init(drm, plane, 0,
                                   &driver_plane_funcs,
                                   driver_formats,
                                   ARRAY_SIZE(driver_formats),
                                   NULL, DRM_PLANE_TYPE_PRIMARY, NULL);
    if (ret) {
        kfree(plane);
        return ret;
    }

    /* 注册 Plane 的 atomic 更新回调 */
    drm_plane_helper_add(plane, &driver_plane_helper_funcs);

    return 0;
}

1.4 KMS 对象之间的连接关系

在 KMS 驱动中,四大对象不是孤立的,它们通过特定的拓扑结构连接在一起,形成完整的显示管线。理解这个拓扑是配置设备树和编写驱动的基础。

KMS 对象拓扑:一对多与多对一关系 CRTC 0 显示控制器 0 CRTC 1 显示控制器 1 Encoder 0 LVDS 编码器 Encoder 1 HDMI 编码器 Connector 0 LVDS 面板接口 Connector 1 HDMI 接口 KMS 对象连接规则 每个 CRTC 只能连接一个 Encoder,每个 Encoder 只能连接一个 Connector ← 上一章 📖 返回目录 下一章 →