2. GPU驱动框架概述:Linux DRM子系统架构
在开始实际的GPU驱动移植之前,必须理解承载GPU驱动的软件框架——Linux DRM (Direct Rendering Manager) 子系统。DRM是Linux内核中负责图形显示与GPU管理的核心框架,它向上提供统一的用户空间API,向下抽象各类GPU硬件差异。本章将系统梳理DRM子系统的整体架构,并深入剖析其三大核心组件:GEM、KMS与Framebuffer。
2.1 DRM子系统整体架构
DRM子系统采用分层设计,从用户态到硬件共分为四层。每一层职责清晰,通过标准接口进行交互。
架构要点: DRM的核心设计哲学是“内核管理资源,用户态管理渲染”。内核负责内存分配、模式设置、同步等关键资源管理,用户态驱动(如Mesa)负责OpenGL/Vulkan命令生成与提交。
2.2 GEM — 图形执行管理器
GEM (Graphics Execution Manager) 是DRM框架中负责GPU缓冲区对象(BO)管理的核心组件。它提供了一套标准化的内存分配、共享、映射与同步机制。
2.2.1 GEM核心概念
| 概念 | 说明 | 关键数据结构/函数 |
|---|---|---|
| GEM对象 (BO) | GPU可访问的内存缓冲区,包含元数据与物理内存页 | struct drm_gem_object |
| 句柄 (Handle) | 用户空间通过整数句柄引用GEM对象,类似文件描述符 | drm_gem_handle_create() |
| 全局名称 (Global Name) | 用于跨进程共享GEM对象的全局标识符 | drm_gem_flink() / drm_gem_open() |
| DMA-BUF | 现代跨设备共享机制,基于dma-buf框架 | drm_gem_prime_export() |
| CPU映射 | 将GEM对象映射到用户空间虚拟地址空间 | drm_gem_mmap() |
2.2.2 GEM对象生命周期
- 创建: 驱动实现
gem_create_object()回调,分配GEM对象与物理内存 - 句柄导出: 内核生成句柄返回用户空间,用户通过句柄操作对象
- 共享: 通过flink或PRIME机制在进程/设备间共享
- 映射: 用户调用mmap将GEM对象映射到自身地址空间
- 销毁: 引用计数归零时,驱动释放内存
移植要点: 在移植GPU驱动时,必须实现
struct drm_gem_object_funcs 中的回调函数,包括 free、open、close、print_info、export、vmap 等。这些回调决定了GEM对象的行为。
2.3 KMS — 内核模式设置
KMS (Kernel Mode Setting) 是DRM框架中负责显示输出管理的组件。它将显示管线的配置权从用户空间移入内核,确保模式切换的原子性与安全性。
2.3.1 KMS核心对象模型
| 对象 | 角色 | 对应硬件 | 关键属性 |
|---|---|---|---|
| CRTC | 显示控制器,生成像素时钟与同步信号 | LCD控制器 / HDMI TX内部时序发生器 | 分辨率、刷新率、扫描模式、背景色 |
| Encoder | 编码器,将像素数据转换为特定接口信号 | HDMI编码器 / LVDS发送器 / eDP桥接芯片 | 编码类型、支持的连接器类型 |
| Connector | 连接器,代表物理显示接口 | HDMI端口 / DP端口 / VGA接口 / DSI接口 | 物理连接状态、EDID数据、支持的显示模式 |
| Plane | 硬件图层,支持叠加合成 | GPU的Overlay / 主图层 / 光标图层 | Z序、透明度、颜色格式、裁剪区域 |
| Bridge | 桥接芯片,连接Encoder与Connector | HDMI转DP / LVDS转eDP / 电平转换芯片 | 输入输出格式、I2C配置、电源控制 |
2.3.2 KMS管线连接关系
一个完整的显示管线从Framebuffer开始,经过Plane合成、CRTC时序生成、Encoder编码、Bridge桥接,最终到达Connector输出。驱动必须正确建立这些对象之间的连接关系。
2.4 Framebuffer — 帧缓冲区
Framebuffer是DRM中表示显示帧的缓冲区对象。它封装了像素数据的格式、尺寸、行宽和内存地址,是KMS管线中数据流的起点。
2.4.1 Framebuffer与GEM的关系
Framebuffer建立在GEM对象之上,一个Framebuffer可以引用一个或多个GEM对象(多平面格式如NV12需要两个平面)。
// Framebuffer核心数据结构
struct drm_framebuffer {
struct drm_device *dev;
struct list_head head;
struct drm_mode_object base;
const struct drm_framebuffer_funcs *funcs;
unsigned int pitches[4]; // 每行字节数
unsigned int offsets[4]; // 平面偏移
uint64_t modifier; // 内存布局修饰符
unsigned int width; // 像素宽度
unsigned int height; // 像素高度
int flags;
int hot_y;
struct drm_gem_object *obj[4]; // 引用的GEM对象
};
2.4.2 Framebuffer创建流程
| 步骤 | 用户空间操作 | 内核处理 |
|---|---|---|
| 1 | 调用 drmIoctl(DRM_IOCTL_MODE_ADDFB2) |
验证参数合法性,分配 drm_framebuffer 结构体 |
| 2 | 传递GEM句柄、宽度、高度、像素格式 | 通过句柄查找GEM对象,增加引用计数 |
| 3 | 指定修饰符(modifier)描述内存布局 | 调用驱动 fb_create 回调进行硬件校验 |
| 4 | 获取Framebuffer ID用于后续显示操作 | 注册到DRM设备,返回用户空间ID |
2.4.3 常见像素格式与修饰符
| 格式 | 描述 | 每像素位数 | 典型应用 |
|---|---|---|---|
DRM_FORMAT_XRGB8888 |
32位RGB,无Alpha | 32 | 桌面显示、UI |
DRM_FORMAT_ARGB8888 |
32位RGBA,有Alpha | 32 | 窗口合成、透明叠加 |
DRM_FORMAT_NV12 |
YUV 4:2:0 半平面 | 12 | 视频播放、摄像头预览 |
DRM_FORMAT_RGB565 |
16位RGB | 16 | 低端嵌入式设备、帧率优先 |
常见陷阱: 在移植过程中,Framebuffer的
pitches(行宽)计算必须与硬件DMA引擎对齐。许多GPU要求行宽是16字节或64字节对齐,否则会导致显示错位或性能下降。务必查阅硬件手册确认对齐要求。
2.5 DRM驱动核心数据结构
每个DRM驱动都需要实现一个 struct drm_driver 实例,这是驱动与DRM核心交互的契约。
// DRM驱动核心结构体(简化)
static struct drm_driver mygpu_drm_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_RENDER,
.gem_create_object = mygpu_gem_create_object, // GEM对象创建
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
.gem_prime_import = mygpu_gem_prime_import,
.gem_prime_import_sg_table = mygpu_gem_prime_import_sg_table,
.dumb_create = mygpu_dumb_create, // 创建 dumb buffer
.dumb_map_offset = mygpu_dumb_map_offset,
.dumb_destroy = mygpu_dumb_destroy,
.fops = &mygpu_fops, // 文件操作
.name = "mygpu",
.desc = "My GPU DRM Driver",
.date = "20250101",
.major = 1,
.minor = 0,
};
2.6 本章小结
本章建立了DRM子系统的整体认知框架:
- 架构分层: 用户空间 → 系统调用 → DRM核心 → 驱动适配 → 硬件抽象
- GEM: 管理GPU缓冲区对象,提供分配、共享、映射、同步机制
- KMS: 管理显示管线,包含CRTC、Encoder、Connector、Plane、Bridge对象模型
- Framebuffer: 基于GEM的帧缓冲区,定义像素格式与内存布局