1. DRM 框架概述
DRM(Direct Rendering Manager)是 Linux 内核中负责图形显示与渲染管理的核心框架。它最初诞生于 1999 年,作为 XFree86 加速架构(DRI)的一部分,旨在为 3D 加速提供安全的用户空间内核接口。经过二十多年的演进,DRM 已从单一的渲染管理器发展为涵盖显示控制、内存管理、编解码加速的完整显示子系统,是当前 Linux 图形栈的基石。
1.1 DRM 在 Linux 图形栈中的位置
下图展示了 DRM 在 Linux 图形栈中的层级关系:
关键理解:DRM 是内核空间与用户空间图形栈之间的桥梁。用户空间的图形库(如 Mesa)通过 libdrm 封装的 ioctl 调用与 DRM 核心交互,而 DRM 核心则通过驱动层控制具体的显示硬件。Wayland/Weston 等现代显示服务器直接使用 DRM 的 KMS 接口进行页面合成与显示输出。
1.2 DRM 的发展历史
| 时间 | 里程碑 | 关键变化 |
|---|---|---|
| 1999 | DRI 项目启动 | XFree86 引入 DRI(Direct Rendering Infrastructure),DRM 作为内核组件诞生 |
| 2004 | DRM 进入主线内核 2.6.x | DRM 成为 Linux 内核官方子系统,支持 i810、radeon 等早期 GPU |
| 2008 | KMS 引入 | 内核模式设置(Kernel Mode Setting)合并,将显示模式管理从 X Server 移入内核 |
| 2012 | GEM 与 TTM 成熟 | 图形执行管理器(GEM)成为主流内存管理方案,TTM 用于专业显卡 |
| 2015 | Atomic API 稳定 | DRM 原子操作接口合并,支持无撕裂的原子化显示更新 |
| 2018+ | 显示子框架扩展 | DRM 桥接框架、MIPI DSI 支持、HDMI/DP 驱动框架持续完善 |
1.3 DRM 核心组件
DRM 框架由三个核心子系统构成,它们协同工作以实现完整的显示功能:
1.3.1 KMS — 内核模式设置
KMS(Kernel Mode Setting)负责显示管线的配置与管理,包括分辨率、刷新率、图层叠加、色彩空间等。KMS 将显示硬件抽象为以下对象:
- CRTC(Cathode Ray Tube Controller):显示控制器,负责从内存读取像素数据并生成视频时序信号。每个 CRTC 对应一个独立的显示通道。
- Encoder(编码器):将 CRTC 输出的像素流编码为特定接口格式(如 MIPI DSI、HDMI、DisplayPort)。
- Connector(连接器):代表物理显示接口(如 HDMI 插座、DSI 排线),负责检测显示器连接状态和 EDID 信息。
- Plane(图层):硬件叠加层,允许将多个帧缓冲叠加合成后输出,减少 CPU 合成负担。
- Bridge(桥接):用于连接不同显示接口的转换芯片(如 DSI 转 eDP、LVDS 转 HDMI)。
1.3.2 GEM — 图形执行管理器
GEM(Graphics Execution Manager)是 DRM 的内存管理子系统,负责管理 GPU 和显示控制器使用的缓冲区对象。GEM 提供以下核心能力:
- 缓冲区分配与释放:通过
drm_gem_object_init()创建 GEM 对象,支持连续内存、分散内存等多种分配策略。 - 内存共享与映射:支持通过
drm_gem_handle_create()导出句柄,实现进程间共享;通过mmap将 GPU 内存映射到用户空间。 - 同步与栅栏:提供
dma_fence机制,确保 GPU 渲染完成后再进行显示扫描。 - 驱动适配层:每个 DRM 驱动可以实现自己的 GEM 后端(如 i915 的 shmem、msm 的 ion 封装)。
GEM vs TTM:GEM 是 DRM 推荐的内存管理框架,适用于大多数嵌入式 GPU 和集成显卡。TTM(Translation Table Manager)是更底层的内存管理方案,主要用于需要复杂显存管理的独立显卡(如 AMDGPU、Nouveau)。现代驱动倾向于使用 GEM 加驱动私有后端的组合方式。
1.3.3 Framebuffer — 帧缓冲
Framebuffer 是 DRM 中表示显示缓冲区的基本对象。它封装了像素数据的格式、尺寸、行宽和内存地址。KMS 通过 Framebuffer 对象告诉 CRTC 从哪里读取显示数据。
| 属性 | 说明 | 典型值 |
|---|---|---|
| width / height | 帧缓冲的像素尺寸 | 1920 x 1080 |
| pixel_format | 像素格式(DRM 四码标识) | DRM_FORMAT_XRGB8888 |
| pitches[4] | 每行字节数(可能包含对齐填充) | 7680(1920 * 4) |
| offsets[4] | 各颜色平面的字节偏移 | 0(单平面格式) |
| modifier | 内存布局修饰符(压缩、分块等) | DRM_FORMAT_MOD_LINEAR |
1.4 DRM 驱动注册流程
一个典型的 DRM 驱动在 probe 阶段的注册流程如下:
- 分配 DRM 设备:调用
drm_dev_alloc()分配struct drm_device结构体。 - 设置驱动信息:填充
struct drm_driver,包括driver_features、fops、gem_create_object等回调。 - 注册 DRM 设备:调用
drm_dev_register()将设备注册到内核,创建设备节点/dev/dri/card0。 - 初始化 KMS 组件:调用
drm_mode_config_init(),注册 CRTC、Encoder、Connector、Plane。 - 初始化 GEM 后端:调用
drm_gem_object_init()或驱动私有的 GEM 初始化函数。 - 创建初始 Framebuffer:为启动 Logo 或控制台创建默认帧缓冲。
// DRM 驱动 probe 函数示例(简化)
static int my_drm_probe(struct platform_device *pdev)
{
struct drm_device *drm;
int ret;
// 1. 分配 DRM 设备
drm = drm_dev_alloc(&my_drm_driver, &pdev->dev);
if (IS_ERR(drm))
return PTR_ERR(drm);
platform_set_drvdata(pdev, drm);
// 2. 初始化模式配置
ret = drm_mode_config_init(drm);
if (ret)
goto err_free;
// 3. 初始化显示管线组件
ret = my_drm_crtc_init(drm);
if (ret)
goto err_config;
ret = my_drm_encoder_init(drm);
if (ret)
goto err_config;
ret = my_drm_connector_init(drm);
if (ret)
goto err_config;
// 4. 注册 DRM 设备
ret = drm_dev_register(drm, 0);
if (ret)
goto err_config;
drm_fbdev_generic_setup(drm, 32);
return 0;
err_config:
drm_mode_config_cleanup(drm);
err_free:
drm_dev_put(drm);
return ret;
}
注意事项:DRM 驱动的注册顺序非常重要。必须先初始化所有 KMS 组件(CRTC、Encoder、Connector),再调用
drm_dev_register()。如果组件未就绪就注册设备,用户空间可能在设备节点出现后立即尝试配置显示,导致竞态条件。
1.5 本章小结
DRM 框架是 Linux 显示子系统的核心,它通过 KMS、GEM、Framebuffer 三大组件实现了显示管线的完整管理。KMS 负责显示模式与图层配置,GEM 管理 GPU 与显示内存,Framebuffer 提供像素数据的标准表示。理解这三者的关系与协作方式,是后续学习 MIPI DSI 驱动开发和调试的基础。