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 四个核心对象,驱动开发者只需实现对应的回调函数即可完成适配。
DRM 三层架构 用户空间层 libdrm · Mesa 3D · Weston / KWin · 应用 内核核心层 (drm_core) 设备注册 · 对象管理 · ioctl 分发 · 事件通知 硬件驱动层 GPU 驱动 · Encoder 驱动 · Bridge 驱动 · PHY 配置

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;
};

驱动加载流程:

  1. 注册 DRM 设备 — 调用 drm_dev_alloc() 分配 drm_device。
  2. 初始化模式配置 — 调用 drm_mode_config_init()。
  3. 创建 CRTC — 调用 drm_crtc_init() 并实现 mode_set 回调。
  4. 创建 Encoder — 调用 drm_encoder_init() 并指定 encoder_type。
  5. 创建 Connector — 调用 drm_connector_init() 并实现 get_modes 回调。
  6. 创建 Plane — 调用 drm_universal_plane_init()。
  7. 注册设备 — 调用 drm_dev_register() 暴露给用户空间。
DRM 驱动核心对象关系 drm_device drm_mode_config CRTC Encoder Connector Plane 管线连接关系 CRTC → Encoder → Connector → 显示器;Plane 叠加在 CRTC 之上

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, ...) → 设置显示模式
用户空间 ioctl 调用流程 显示应用 libdrm DRM 内核 ① open("/dev/dri/card0") ② drmModeGetResources() ③ drmModeGetEncoder() ④ drmModeGetConnector() ⑤ drmModeSetCrtc() ioctl 数据流向 应用 → libdrm 封装 → 内核 drm_ioctl() → 驱动回调 → 硬件寄存器操作 → 结果返回用户空间
注意事项: 用户空间不能直接访问 encoder 硬件寄存器,所有操作必须通过 KMS ioctl 间接完成。Encoder 驱动在内核空间负责将 KMS 请求转换为硬件时序与控制信号。