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)。
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。驱动通过实现这些对象的回调函数来控制显示时序、像素时钟、色彩空间和图层合成。
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 配置代码 ... */
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显示提交流程如下:
- 初始化阶段:DRM Core加载驱动,注册
drm_device;KMS探测Connector和Encoder,读取EDID获取显示模式;GEM初始化显存分配器。 - 缓冲区分配:用户空间通过
DRM_IOCTL_MODE_CREATE_DUMB(或GEM)创建帧缓冲,驱动分配物理连续内存并返回句柄。 - FB绑定:用户空间通过
DRM_IOCTL_MODE_ADDFB将dumb buffer句柄注册为KMS的FB对象,指定像素格式和步长。 - 模式设置:用户空间通过
DRM_IOCTL_MODE_SETCRTC或DRM_IOCTL_MODE_ATOMIC将FB绑定到CRTC,设置显示分辨率和刷新率。 - Page Flip:用户空间通过
DRM_IOCTL_MODE_PAGE_FLIP提交新帧,驱动在VBlank中断中切换显示缓冲区地址,实现无撕裂显示。
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叠加,实现硬件光标和视频叠加 |
simple_display_pipe辅助框架入手。该框架封装了CRTC、Encoder和Connector的通用实现,驱动只需提供mode_valid、enable和disable三个回调,大幅降低移植工作量。