1. DRM 框架概述
DRM (Direct Rendering Manager) 是 Linux 内核中负责图形显示与渲染的核心框架。它向上提供统一的用户空间接口,向下抽象 GPU、显示控制器、编码器、连接器等硬件资源。理解 DRM 的架构组成是编写 encoder 驱动和适配 LVDS/HDMI 的基础。
1.1 DRM 架构组成
DRM 框架从逻辑上分为三层:
| 层次 | 组件 | 职责 |
|---|---|---|
| 用户空间 | libdrm / Mesa / Weston | 通过 ioctl 调用内核接口,管理显存分配、模式设置、页面翻转 |
| 内核核心层 | drm_core / drm_framebuffer | 提供设备注册、文件操作、对象管理、事件通知等基础服务 |
| 硬件驱动层 | GPU 驱动 / 桥接芯片驱动 | 实现具体的硬件初始化、寄存器配置、中断处理、电源管理 |
关键点: DRM 将显示管线抽象为 CRTC、Encoder、Connector、Plane 四个核心对象,驱动开发者只需实现对应的回调函数即可完成适配。
1.2 GEM 与 KMS
DRM 内部包含两个核心子系统:
- GEM (Graphics Execution Manager) — 负责显存管理与 GPU 命令提交。提供 buffer 分配、共享、映射、同步机制。
- KMS (Kernel Mode Setting) — 负责显示模式设置与输出管线控制。包括分辨率、刷新率、色彩空间、连接器状态等。
| 特性 | GEM | KMS |
|---|---|---|
| 核心功能 | 显存分配与 GPU 执行 | 显示模式与输出管线 |
| 关键对象 | GEM BO, fence, sync_file | CRTC, Encoder, Connector, Plane, FB |
| 用户空间接口 | DRM_IOCTL_GEM_* | DRM_IOCTL_MODE_* |
| 与 encoder 关系 | 无直接关系 | Encoder 是 KMS 管线核心节点 |
实践提示: 编写 encoder 驱动时主要与 KMS 子系统交互,GEM 通常由 GPU 驱动或显示控制器驱动管理。但 encoder 可能需要通过 GEM 获取 framebuffer 的物理地址用于时序生成。
1.3 DRM 驱动模型
DRM 驱动采用面向对象的设计,每个硬件组件对应一个内核对象,通过 drm_mode_object 基类统一管理。驱动模型的核心结构体如下:
// 核心驱动结构体
struct drm_driver {
int (*load)(struct drm_device *, unsigned long flags);
void (*unload)(struct drm_device *);
const struct file_operations *fops;
struct drm_mode_config_funcs *mode_config_funcs;
// ... 更多回调
};
// 模式配置结构体
struct drm_mode_config {
struct list_head crtc_list;
struct list_head encoder_list;
struct list_head connector_list;
struct list_head plane_list;
struct list_head fb_list;
int min_width, max_width;
int min_height, max_height;
};
驱动加载流程:
- 注册 DRM 设备 — 调用
drm_dev_alloc()分配drm_device。 - 初始化模式配置 — 调用
drm_mode_config_init()。 - 创建 CRTC — 调用
drm_crtc_init()并实现mode_set回调。 - 创建 Encoder — 调用
drm_encoder_init()并指定 encoder_type。 - 创建 Connector — 调用
drm_connector_init()并实现get_modes回调。 - 创建 Plane — 调用
drm_universal_plane_init()。 - 注册设备 — 调用
drm_dev_register()暴露给用户空间。
1.4 用户空间接口
用户空间通过 /dev/dri/card0 设备节点与 DRM 驱动通信,主要使用以下 ioctl 命令:
| 分类 | ioctl 命令 | 用途 |
|---|---|---|
| 查询 | DRM_IOCTL_MODE_GETRESOURCES | 获取 CRTC/Encoder/Connector/FB 资源 ID 列表 |
| 模式设置 | DRM_IOCTL_MODE_SETCRTC | 设置 CRTC 模式与 framebuffer 绑定 |
| Encoder 查询 | DRM_IOCTL_MODE_GETENCODER | 获取 encoder 当前状态与绑定的 CRTC/Connector |
| Connector 查询 | DRM_IOCTL_MODE_GETCONNECTOR | 获取连接器状态、EDID、支持的模式列表 |
| 页面翻转 | DRM_IOCTL_MODE_PAGE_FLIP | 无撕裂切换 framebuffer |
| GEM 操作 | DRM_IOCTL_GEM_OPEN / CLOSE | 打开/关闭 GEM 对象句柄 |
典型用户空间调用流程:
1. open("/dev/dri/card0") → 获取 fd
2. drmModeGetResources(fd) → 获取 encoder_id 和 connector_id
3. drmModeGetEncoder(fd, encoder_id) → 获取 encoder 信息
4. drmModeGetConnector(fd, connector_id) → 获取 EDID 与模式列表
5. drmModeSetCrtc(fd, crtc_id, fb_id, ...) → 设置显示模式
1. open("/dev/dri/card0") → 获取 fd
2. drmModeGetResources(fd) → 获取 encoder_id 和 connector_id
3. drmModeGetEncoder(fd, encoder_id) → 获取 encoder 信息
4. drmModeGetConnector(fd, connector_id) → 获取 EDID 与模式列表
5. drmModeSetCrtc(fd, crtc_id, fb_id, ...) → 设置显示模式
注意事项: 用户空间不能直接访问 encoder 硬件寄存器,所有操作必须通过 KMS ioctl 间接完成。Encoder 驱动在内核空间负责将 KMS 请求转换为硬件时序与控制信号。