1. DRM框架概述

1.1 DRM发展历史

DRM(Direct Rendering Manager)是Linux内核中负责图形显示和渲染管理的核心框架。它的发展历程反映了Linux图形栈从简单到复杂、从封闭到开放的演进过程。

时间 版本/事件 里程碑意义
1999年 DRI项目启动 XFree86引入DRI(Direct Rendering Infrastructure),首次在Linux上实现直接渲染
2004年 Linux 2.6.9 DRM作为独立内核模块被合入主线,提供GPU内存管理和命令提交基础
2008年 KMS引入 Kernel Mode Setting(KMS)从用户态移入内核,DRM开始接管显示模式设置
2012年 Linux 3.3 Atomic Mode Set(原子模式设置)概念提出,DRM进入原子化时代
2015年 Linux 4.2 Atomic API正式稳定,DRM框架支持无撕裂、无闪烁的显示更新
2018年至今 持续演进 引入HDCP、HDR、VRR(可变刷新率)、DisplayPort MST等高级特性
关键转折点:KMS从用户态(X Server)移入内核态是DRM历史上最重要的决策。这一变化使得显示模式切换不再依赖X Server,嵌入式设备可以在没有完整桌面环境的情况下直接驱动显示硬件,为Android、Yocto等嵌入式系统铺平了道路。

1.2 DRM在Linux图形栈中的位置

DRM位于Linux图形栈的中间层,向上对接用户态图形库,向下控制具体显示硬件。理解它的位置对于驱动开发至关重要。

Linux图形栈分层架构 用户态应用层 Wayland Compositor / X Server / Android SurfaceFlinger / Qt / SDL 用户态图形库层 libdrm / Mesa 3D / EGL / OpenGL ES / Vulkan 内核态 DRM 核心层 DRM Core (drm.ko) / KMS (drm_kms_helper.ko) / GEM (drm_gem.ko) Atomic API / FBDev兼容层 / 属性系统 内核态 GPU/显示驱动层 i915 / amdgpu / msm / rockchip / sun4i / stm / vc4 硬件层 GPU / Display Controller / HDMI/DP/LVDS/MIPI DSI Transmitter / Panel DRM是内核态唯一的标准 显示驱动接口
嵌入式视角:在嵌入式Linux系统中,DRM是连接硬件和上层图形框架的桥梁。与桌面不同,嵌入式系统通常没有X Server,Wayland/Weston或Android的HWC(Hardware Composer)直接通过DRM的Atomic API与内核交互。这意味着嵌入式DRM驱动开发者需要同时理解硬件寄存器级操作和DRM框架的抽象层。

1.3 DRM核心组件概念介绍

DRM/KMS框架将显示硬件抽象为五个核心组件:Framebuffer、CRTC、Encoder、Connector和Plane。这五个组件构成了现代显示管线的完整数据流。

1.3.1 核心组件总览

组件 内核结构体 功能描述 硬件对应
Framebuffer struct drm_framebuffer 存储一帧完整的像素数据,包含颜色格式、分辨率、内存布局 显存中的帧缓冲区
CRTC struct drm_crtc 控制显示时序生成,负责从Framebuffer读取数据并生成视频信号 显示控制器(Display Controller)
Encoder struct drm_encoder 将CRTC输出的并行视频信号编码为特定接口的串行信号 HDMI/DVI/LVDS/MIPI DSI发送器
Connector struct drm_connector 代表物理显示接口,检测连接状态、读取EDID、管理显示模式 HDMI/DP/VGA物理接口
Plane struct drm_plane 硬件叠加层,支持独立缩放、旋转、颜色转换,实现多图层合成 硬件Overlay/合成器

1.3.2 显示管线数据流

五个组件按照固定的管线顺序协同工作,形成从内存到屏幕的完整数据通路:

DRM显示管线数据流 Framebuffer 帧缓冲区 Plane 硬件叠加层 CRTC 显示控制器 Encoder 信号编码器 Connector 物理接口 内存中的像素数据 图层合成与缩放 时序生成与扫描 信号编码转换 物理连接与检测 数据流方向:Framebuffer → Plane → CRTC → Encoder → Connector → 显示器 每个组件在驱动中对应一个struct,通过链表挂接到drm_device上

1.3.3 各组件详解

Framebuffer(帧缓冲区)

Framebuffer是显示数据的源头,它封装了内存中一帧图像的完整描述。驱动开发者需要关注:

  • 像素格式:ARGB8888、XRGB8888、RGB565、NV12(YUV)等
  • 内存布局:线性布局、Tiled布局、Swizzle布局
  • 多平面格式:NV12等YUV格式需要多个内存平面(plane)描述
  • DMA-BUF:现代DRM驱动通过DMA-BUF实现Framebuffer的跨设备共享
// Framebuffer创建示例(简化)
struct drm_framebuffer *fb;
struct drm_mode_fb_cmd2 r = {
    .width = 1920,
    .height = 1080,
    .pixel_format = DRM_FORMAT_XRGB8888,
    .handles[0] = gem_handle,
    .pitches[0] = 1920 * 4,
};
fb = drm_internal_framebuffer_create(dev, &r, NULL);
CRTC(显示控制器)

CRTC是显示管线的核心调度器,负责:

  • 时序生成:产生VSYNC、HSYNC、DE(数据使能)等同步信号
  • 扫描输出:按行/帧从Framebuffer读取像素数据
  • 模式设置:配置分辨率、刷新率、时钟频率
  • Gamma校正:内置LUT(查找表)进行颜色校正
嵌入式常见陷阱:很多嵌入式SoC的显示控制器支持多个CRTC,但每个CRTC只能连接一个Encoder。在Device Tree中,CRTC通常对应display-controller节点,其port子节点描述与Encoder的连接关系。错误的连接配置会导致显示无输出。
Encoder(编码器)

Encoder将CRTC输出的并行RGB/YCbCr信号转换为特定显示接口所需的串行信号:

Encoder类型 输出接口 典型芯片 嵌入式常见场景
DRM_MODE_ENCODER_TMDS HDMI/DVI IT66121, ADV7511 HDMI输出
DRM_MODE_ENCODER_LVDS LVDS SN75LVDS83 工业屏/车载屏
DRM_MODE_ENCODER_DSI MIPI DSI SoC内置DSI控制器 手机/平板/智能家居
DRM_MODE_ENCODER_DPI RGB并行 SoC内置RGB接口 小尺寸LCD屏
DRM_MODE_ENCODER_DP DisplayPort ANX7688, Parade PS8640 高分辨率/高刷新率
Connector(连接器)

Connector是DRM框架中与物理世界交互的接口,承担以下职责:

  • 热插拔检测:通过HPD(Hot Plug Detect)引脚检测显示器连接/断开
  • EDID读取:通过I2C读取显示器的EDID数据,获取支持的分辨率列表
  • 模式列表管理:维护显示器支持的所有显示模式(drm_display_mode)
  • 属性暴露:向用户态暴露连接状态、物理尺寸、宽高比等属性
// Connector初始化示例(嵌入式DSI屏)
static const struct drm_connector_funcs my_connector_funcs = {
    .detect = my_connector_detect,      // 检测连接状态
    .fill_modes = drm_helper_probe_single_connector_modes,
    .destroy = my_connector_destroy,
    .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
    .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};

// 对于固定连接的嵌入式屏,detect直接返回connected
static enum drm_connector_status my_connector_detect(struct drm_connector *connector, bool force)
{
    return connector_status_connected;  // 固定连接,始终返回已连接
}
Plane(硬件叠加层)

Plane是DRM框架中较晚引入但非常重要的组件,它代表了硬件的图层合成能力:

  • 主平面(Primary Plane):每个CRTC至少有一个主平面,是默认的显示层
  • 光标平面(Cursor Plane):专门用于鼠标光标,支持硬件光标
  • 叠加平面(Overlay Plane):额外的硬件层,支持独立缩放、Alpha混合、颜色键
Plane在嵌入式中的价值:嵌入式SoC通常有2-4个硬件叠加层。合理使用Plane可以实现视频播放、UI叠加、画中画等功能,而无需CPU参与图层合成。例如,在Rockchip RK3399上,通过配置Video Plane和UI Plane,可以实现4K视频播放与系统UI的无缝叠加,CPU占用率降低80%以上。

1.3.4 组件间的连接关系

在DRM框架中,五个组件通过可能的连接关系(possible clones/possible crtcs)形成拓扑结构。驱动初始化时需要明确声明这些连接关系:

// 驱动初始化时声明组件连接关系
static int my_display_init(struct drm_device *drm)
{
    struct drm_plane *primary_plane;
    struct drm_crtc *crtc;
    struct drm_encoder *encoder;
    struct drm_connector *connector;

    // 1. 创建Plane,声明它可以连接到哪些CRTC(位掩码)
    primary_plane = my_plane_init(drm, DRM_PLANE_TYPE_PRIMARY);
    primary_plane->possible_crtcs = 1 << 0;  // 只能连接到CRTC 0

    // 2. 创建CRTC,绑定Plane
    crtc = my_crtc_init(drm, primary_plane);

    // 3. 创建Encoder,声明它可以连接到哪些CRTC
    encoder = my_encoder_init(drm);
    encoder->possible_crtcs = 1 << 0;       // 只能连接到CRTC 0
    encoder->possible_clones = 0;            // 不支持克隆(多显复制)

    // 4. 创建Connector,绑定Encoder
    connector = my_connector_init(drm);
    drm_connector_attach_encoder(connector, encoder);

    return 0;
}

1.4 核心组件交互流程

当用户态应用请求显示一帧图像时,DRM框架内部经历以下完整流程:

  1. 分配Framebuffer:用户态通过drmModeAddFB2()创建Framebuffer,内核分配GEM对象并映射到DMA地址
  2. 提交Atomic请求:用户态构造Atomic请求,包含Plane与Framebuffer的绑定关系、CRTC模式、Connector状态等
  3. 校验(Check):内核调用各个组件的.atomic_check()回调,验证资源是否充足、状态是否合法
  4. 提交(Commit):通过.atomic_commit()将配置写入硬件寄存器,等待VSYNC信号完成切换
  5. 完成通知:硬件VSYNC中断触发后,内核发送DRM_EVENT_FLIP_COMPLETE事件通知用户态
Atomic显示更新完整流程 ① 分配FB drmModeAddFB2 ② Atomic请求 drmModeAtomicCommit ③ 校验 atomic_check ④ 提交硬件 atomic_commit ⑤ 完成 VSYNC事件 关键特性:Atomic模式保证所有配置同时生效,避免中间状态导致的屏幕撕裂(Tearing) 非Atomic模式(Legacy)下,每个组件单独配置,可能出现CRTC已切换但Plane未更新的中间态 VSYNC时序 VBlank VBlank VBlank VBlank VBlank

1.5 本章小结

DRM框架是嵌入式Linux显示驱动的基石,理解其核心组件是后续深入学习的前提:

  • 历史脉络:从DRI到KMS再到Atomic,DRM经历了20年的演进,每一次变革都解决了特定的显示痛点
  • 架构位置:DRM位于内核态,向上提供统一的/dev/dri/card0接口,向下适配各类显示硬件
  • 五大组件:Framebuffer(数据源)、Plane(图层合成)、CRTC(时序控制)、Encoder(信号编码)、Connector(物理接口)构成了完整的显示管线
  • Atomic机制:通过一次原子提交完成所有组件的状态切换,从根本上解决了显示撕裂和闪烁问题