3. DRM基础概念:DRM架构概览、核心组件

DRM(Direct Rendering Manager)是Linux内核中现代图形显示子系统的核心框架,它取代了传统的Framebuffer架构,为GPU、显示控制器和内存管理提供了统一的抽象层。理解DRM的架构和核心组件,是从Framebuffer向DRM移植适配的关键第一步。

3.1 DRM架构概览

DRM架构采用分层设计,从上到下依次为:用户空间应用程序、libdrm库、DRM内核驱动层以及硬件设备层。内核中的DRM子系统由三个核心模块组成:DRM Core、KMS(Kernel Mode Setting)和GEM(Graphics Execution Manager)。

DRM整体架构分层图 DRM 整体架构分层图 用户空间 (User Space) 应用程序 (Weston / Xorg / Wayland Compositor / 裸DRM应用) libdrm 用户空间库 内核空间 (Kernel Space) DRM Core 设备管理 / ioctl分发 KMS 显示模式设置 / 平面合成 GEM 显存管理 / Buffer对象 硬件抽象层 (GPU / Display Controller / Encoder / Connector) 硬件设备 (LCD / HDMI / eDP / VGA 等显示面板)
移植要点:从Framebuffer迁移到DRM时,最直观的变化是驱动需要同时实现KMS和GEM两个子系统。Framebuffer驱动仅需提供简单的fb_ops回调,而DRM驱动则需要注册drm_driver结构体,并实现KMS的mode_config_funcs和GEM的gem_create_object等接口。

3.2 核心组件详解

3.2.1 DRM Core

DRM Core是整个DRM框架的基础设施,负责设备注册、文件操作接口、ioctl调度、缓冲区管理以及驱动生命周期管理。它向上对用户空间暴露/dev/dri/card0设备节点,向下通过drm_driver结构体与具体硬件驱动交互。

组件 职责 关键数据结构/接口
设备管理 PCI/platform设备注册、中断处理、电源管理 drm_driver, drm_device
文件操作 open/release/ioctl/mmap 回调分发 drm_fops, drm_ioctl_desc
ioctl调度 根据ioctl编号路由到对应处理函数 drm_ioctl_t, DRM_IOCTL_DEF
对象管理 DRM对象(FB、CRTC、PLANE等)的ID分配和查找 drm_mode_object, idr机制

3.2.2 KMS (Kernel Mode Setting)

KMS是DRM中负责显示输出管理的子系统,它替代了传统Framebuffer中fb_set_par和fb_blank等分散的回调。KMS将显示管线抽象为五个核心对象:CRTC、Encoder、Connector、Plane和FB。驱动通过实现这些对象的回调函数来控制显示时序、像素时钟、色彩空间和图层合成。

KMS核心对象关系图 KMS 核心对象关系图 Plane (平面) 图像层 / 缩放 / 旋转 CRTC 时序生成 / 扫描输出 Encoder (编码器) 信号编码 / 协议转换 Connector (连接器) 物理接口 / EDID读取 FB (帧缓冲) 内存中的像素数据 数据流方向:FB → Plane → CRTC → Encoder → Connector → 显示面板 一个CRTC可绑定多个Plane实现硬件图层合成,一个Encoder可连接多个Connector
移植对比:Framebuffer驱动中,显示时序和分辨率通常通过固定的fb_var_screeninfo结构体配置;而在DRM/KMS中,驱动需要实现crtc_helper_funcs中的mode_set和atomic_flush回调,支持动态模式切换和原子提交。

3.2.3 GEM (Graphics Execution Manager)

GEM是DRM的显存管理子系统,负责分配、释放和管理GPU/显示控制器使用的缓冲区对象(BO)。GEM提供了基于句柄(handle)和文件描述符(FD)的共享机制,支持CPU映射和DMA-BUF导出。对于没有GPU的嵌入式平台,DRM提供了dumb buffer机制作为GEM的简化实现。

概念 说明 典型API
GEM BO 显存缓冲区对象,通过句柄在进程内引用 drm_gem_object_init, drm_gem_handle_create
GEM handle 用户空间可见的整数ID,用于操作BO DRM_IOCTL_GEM_OPEN, DRM_IOCTL_GEM_CLOSE
GEM FD (prime) 跨进程/跨设备共享BO的文件描述符 drm_gem_prime_export, drm_gem_prime_import
dumb buffer 无需GPU的简单连续内存分配,适用于纯显示控制器 DRM_IOCTL_MODE_CREATE_DUMB, DRM_IOCTL_MODE_MAP_DUMB

3.2.4 Dumb Buffer

Dumb buffer是DRM为简单显示控制器(如嵌入式LCD控制器)提供的轻量级显存分配方案。它不需要GPU或复杂的显存管理器,驱动只需实现drm_driver.dumb_create、drm_driver.dumb_destroy和drm_driver.dumb_map_offset三个回调。用户空间通过ioctl创建dumb buffer后,可以直接mmap到用户空间进行像素填充,然后通过KMS的page flip提交显示。

/* dumb buffer 典型使用流程(用户空间) */
int fd = open("/dev/dri/card0", O_RDWR);

/* 1. 创建 dumb buffer */
struct drm_mode_create_dumb create = {
    .width = 1024,
    .height = 600,
    .bpp = 32,
};
ioctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create);

/* 2. 获取 mmap 偏移 */
struct drm_mode_map_dumb map = {
    .handle = create.handle,
};
ioctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map);

/* 3. mmap 到用户空间 */
uint32_t *fb_ptr = mmap(NULL, create.size, PROT_READ | PROT_WRITE,
                        MAP_SHARED, fd, map.offset);

/* 4. 填充像素数据(例如填充为红色) */
for (int i = 0; i < 1024 * 600; i++)
    fb_ptr[i] = 0x00FF0000;  /* ARGB: 红色 */

/* 5. 通过 KMS 提交显示(需要先设置 CRTC 和 Connector) */
/* ... 省略 KMS 配置代码 ... */
移植陷阱:从Framebuffer移植到DRM时,不要直接将fb_info中的screen_base映射地址传递给DRM的FB对象。DRM要求所有显示缓冲区必须通过GEM或dumb buffer机制分配,否则KMS的page flip和atomic操作会因缺少正确的DMA地址而失败。正确的做法是:在dumb_create回调中分配连续物理内存,并通过drm_gem_dumb_destroy和drm_gem_dumb_map_offset完成生命周期管理。

3.3 核心组件协作流程

一个典型的DRM显示提交流程如下:

  1. 初始化阶段:DRM Core加载驱动,注册drm_device;KMS探测Connector和Encoder,读取EDID获取显示模式;GEM初始化显存分配器。
  2. 缓冲区分配:用户空间通过DRM_IOCTL_MODE_CREATE_DUMB(或GEM)创建帧缓冲,驱动分配物理连续内存并返回句柄。
  3. FB绑定:用户空间通过DRM_IOCTL_MODE_ADDFB将dumb buffer句柄注册为KMS的FB对象,指定像素格式和步长。
  4. 模式设置:用户空间通过DRM_IOCTL_MODE_SETCRTC或DRM_IOCTL_MODE_ATOMIC将FB绑定到CRTC,设置显示分辨率和刷新率。
  5. Page Flip:用户空间通过DRM_IOCTL_MODE_PAGE_FLIP提交新帧,驱动在VBlank中断中切换显示缓冲区地址,实现无撕裂显示。
DRM显示提交流程图 DRM 显示提交流程图 1. 创建Dumb 分配显存 2. AddFB 注册帧缓冲 3. SetCRTC 绑定CRTC 4. PageFlip 提交新帧 5. VBlank 切换显示 关键说明 • 步骤1-3为初始化阶段,通常在应用程序启动时执行一次 • 步骤4-5为渲染循环,每帧重复执行 • 实际项目中通常使用双缓冲(Double Buffer)或三缓冲(Triple Buffer) • Page Flip支持阻塞和非阻塞两种模式,非阻塞模式需配合VBlank事件等待 • 原子提交(Atomic API)可在一个ioctl中同时完成SetCRTC和PageFlip

3.4 从Framebuffer到DRM的组件映射

为了帮助开发者快速理解DRM与Framebuffer的对应关系,下表列出了关键概念的映射:

Framebuffer概念 DRM对应组件 差异说明
fb_info drm_device + drm_framebuffer DRM将设备信息和帧缓冲分离管理
fb_ops.fb_set_par KMS CRTC mode_set 回调 DRM支持原子模式切换,可同时设置多个CRTC
fb_ops.fb_blank KMS DPMS 属性控制 DRM通过Connector的DPMS属性实现电源管理
fb_fix_screeninfo.smem_start GEM BO 的物理地址 / dumb buffer offset DRM通过GEM句柄间接管理,不直接暴露物理地址
fb_var_screeninfo drm_display_mode DRM支持EDID自动解析和模式列表
fb_info.screen_base dumb buffer mmap 后的用户空间地址 DRM要求通过ioctl创建buffer后再mmap
无(Framebuffer不支持) Plane(硬件图层) DRM支持多个Plane叠加,实现硬件光标和视频叠加
移植建议:对于嵌入式平台上的简单LCD控制器,建议从dumb buffer + KMS的simple_display_pipe辅助框架入手。该框架封装了CRTC、Encoder和Connector的通用实现,驱动只需提供mode_valid、enable和disable三个回调,大幅降低移植工作量。