Linux DRM KMS显示控制器驱动开发与适配 - 第2节

2.1 DRM 架构概览

DRM(Direct Rendering Manager)是 Linux 内核中负责图形显示与渲染管理的核心框架。它向上提供统一的用户空间接口(通过 /dev/dri/card0 等设备节点),向下抽象各类显示控制器硬件。KMS(Kernel Mode Setting)是 DRM 框架中专门负责显示模式设置与输出管理的子系统。

核心设计思想: DRM 将显示输出链路抽象为若干逻辑组件,每个组件对应硬件中的特定功能单元。驱动开发者只需实现这些组件的回调函数,即可完成显示控制器的适配。
DRM/KMS 软件架构分层 用户空间 (libdrm / Mesa / Weston / Xorg) 系统调用接口 (ioctl) DRM 核心框架 (drm_core) drm_device / drm_driver / drm_mode_config KMS 组件抽象层 Framebuffer | Plane | CRTC | Encoder | Connector 显示控制器驱动 (如 i915 / rockchip / msm) 硬件 (GPU / Display Controller / PHY / Panel)

2.2 五大核心组件详解

KMS 将显示输出链路抽象为五个核心组件,它们共同构成一条完整的显示管线。每个组件在 drm_mode_config 结构中注册,并通过 ID 相互关联。

组件 内核结构体 硬件对应 核心职责
Framebuffer drm_framebuffer 显存中的帧缓冲区 管理像素数据的内存对象,定义格式、宽高、步幅
Plane drm_plane 硬件图层(Overlay / Primary / Cursor) 将 Framebuffer 的内容叠加到 CRTC 输出上,支持缩放与裁剪
CRTC drm_crtc 显示控制器核心(Timing Generator) 生成像素时钟与同步信号,驱动扫描输出
Encoder drm_encoder 显示信号编码器(如 HDMI TX、DP 编码器) 将 CRTC 输出的并行像素数据编码为串行链路信号
Connector drm_connector 物理连接器(HDMI、DP、eDP、DSI 接口) 检测显示器热插拔,读取 EDID,提供可用模式列表

2.2.1 Framebuffer(帧缓冲区)

Framebuffer 是 DRM 中最基础的内存对象,它封装了一块用于存储像素数据的 DMA 缓冲区。用户空间通过 drm_mode_addfb2 创建 Framebuffer,内核驱动负责将其绑定到具体的显存地址。

struct drm_framebuffer {
    struct drm_device *dev;
    struct drm_mode_object base;
    const struct drm_framebuffer_funcs *funcs;
    unsigned int width;          // 像素宽度
    unsigned int height;         // 像素高度
    unsigned int pitches[4];     // 每行字节数(支持多平面)
    unsigned int offsets[4];     // 平面偏移
    uint32_t format;             // 像素格式(如 DRM_FORMAT_XRGB8888)
    uint64_t modifier;           // 内存布局修饰符(如 AFBC、线性)
    struct drm_gem_object *obj[4];
};
适配要点: 驱动需实现 drm_framebuffer_funcs 中的 create_handle 和 destroy 回调。对于支持 AFBC 压缩格式的硬件,务必正确设置 modifier 字段。

2.2.2 Plane(硬件图层)

Plane 是 KMS 中负责图层合成的组件。每个 Plane 对应一个硬件图层,支持独立的 Framebuffer 绑定、位置调整、缩放和透明度混合。现代 SoC 显示控制器通常包含 3 类 Plane:

  • Primary Plane: 主图层,必须存在,通常用于显示桌面背景或主应用界面。
  • Overlay Plane: 叠加图层,用于视频播放、UI 弹窗等,支持硬件 Alpha 混合。
  • Cursor Plane: 光标图层,硬件光标,支持快速移动而不重绘整个画面。
struct drm_plane {
    struct drm_device *dev;
    struct drm_crtc *crtc;           // 当前绑定的 CRTC
    struct drm_crtc *possible_crtcs; // 可绑定的 CRTC 位掩码
    uint32_t type;                   // DRM_PLANE_TYPE_PRIMARY / OVERLAY / CURSOR
    unsigned int format_count;
    const uint32_t *format_list;     // 支持的像素格式列表
    struct drm_plane_state *state;   // 当前状态
};

2.2.3 CRTC(显示控制器核心)

CRTC(Cathode Ray Tube Controller,历史名称沿用)是显示管线的核心调度器。它负责从绑定的 Plane 读取像素数据,生成符合显示器要求的时序信号(Hsync、Vsync、像素时钟),并驱动 Encoder 输出。

CRTC 回调 功能说明 适配注意事项
mode_valid 验证显示模式是否被硬件支持 检查时钟频率、分辨率是否在硬件限制内
mode_set 配置 CRTC 的时序寄存器 设置 Htotal、Vtotal、时钟分频等参数
enable 启动 CRTC 输出 使能像素时钟、释放复位、启动扫描
disable 停止 CRTC 输出 关闭时钟、进入低功耗模式
atomic_flush 原子提交所有 Plane 状态变更 在 VBlank 期间一次性更新寄存器

2.2.4 Encoder(信号编码器)

Encoder 将 CRTC 输出的并行 RGB/YCbCr 数据转换为特定显示接口所需的串行信号。常见的 Encoder 类型包括:

  • TMDS Encoder: 用于 HDMI/DVI,将像素数据编码为 TMDS 差分信号。
  • DisplayPort Encoder: 将数据打包为 DP 微包,支持多流传输(MST)。
  • DSI Encoder: 用于 MIPI DSI 接口,输出串行命令/像素流。
  • LVDS Encoder: 用于传统 LCD 屏,输出低压差分信号。
struct drm_encoder {
    struct drm_device *dev;
    struct drm_crtc *crtc;           // 当前绑定的 CRTC
    unsigned int possible_crtcs;     // 可绑定的 CRTC 位掩码
    unsigned int possible_clones;    // 可克隆的 Encoder 位掩码
    enum drm_encoder_type encoder_type; // DRM_MODE_ENCODER_TMDS / DSI / LVDS / DP
};

2.2.5 Connector(物理连接器)

Connector 是显示输出链路的最末端,直接对应物理接口。它负责检测显示器连接状态、读取 EDID 数据、提供可用显示模式列表,并管理电源控制(如 DPMS)。

常见适配陷阱: Connector 的 get_modes 回调必须正确解析 EDID。如果硬件不支持 EDID(如 DSI 固定分辨率屏),驱动应通过 drm_add_modes_noedid 添加固定模式,并调用 drm_set_preferred_mode 设置首选分辨率。
struct drm_connector {
    struct drm_device *dev;
    struct drm_encoder *encoder;     // 当前绑定的 Encoder
    enum drm_connector_status status; // connected / disconnected / unknown
    struct edid *edid_blob_ptr;      // 缓存的 EDID 数据
    struct drm_display_mode *modes;  // 可用模式链表
    int (*get_modes)(struct drm_connector *connector);
    int (*detect)(struct drm_connector *connector, bool force);
};

2.3 组件连接关系与显示管线

五个组件按照 Framebuffer → Plane → CRTC → Encoder → Connector 的顺序串联,形成完整的显示管线。用户空间通过 Atomic Commit 或 Legacy SetConfig 接口配置这条管线。

KMS 显示管线数据流 Framebuffer 像素数据 Plane 图层合成 CRTC 时序生成 Encoder 信号编码 Connector 物理接口 Atomic Commit 原子提交 用户空间一次性提交所有组件状态,驱动在 VBlank 期间原子切换

2.4 组件注册与初始化流程

在显示控制器驱动的 bind 或 probe 函数中,需要按以下顺序注册 KMS 组件:

  1. 初始化 drm_mode_config: 调用 drm_mode_config_init,设置 min_width/height、max_width/height 等硬件限制。
  2. 注册 CRTC: 调用 drm_crtc_init_with_planes,绑定 Primary 和 Cursor Plane。
  3. 注册 Plane: 调用 drm_universal_plane_init,指定支持的像素格式和类型。
  4. 注册 Encoder: 调用 drm_encoder_init,设置 possible_crtcs 位掩码。
  5. 注册 Connector: 调用 drm_connector_init,指定 Connector 类型和 drm_connector_funcs。
  6. 建立连接: 调用 drm_connector_attach_encoder 将 Connector 绑定到 Encoder。
  7. 注册模式配置: 调用 drm_mode_config_reset 初始化默认状态。
static int my_display_bind(struct drm_device *drm)
{
    // 1. 初始化 mode_config
    drm_mode_config_init(drm);
    drm->mode_config.min_width = 320;
    drm->mode_config.max_width = 4096;
    drm->mode_config.min_height = 240;
    drm->mode_config.max_height = 2160;

    // 2. 注册 Plane
    drm_universal_plane_init(drm, &primary_plane, 0xFF,
                             &primary_plane_funcs, formats, num_formats,
                             DRM_PLANE_TYPE_PRIMARY, NULL);

    // 3. 注册 CRTC
    drm_crtc_init_with_planes(drm, &crtc, &primary_plane, &cursor_plane,
                              &crtc_funcs, NULL);

    // 4. 注册 Encoder
    drm_encoder_init(drm, &encoder, &encoder_funcs,
                     DRM_MODE_ENCODER_TMDS, NULL);

    // 5. 注册 Connector
    drm_connector_init(drm, &connector, &connector_funcs,
                       DRM_MODE_CONNECTOR_HDMIA);

    // 6. 建立连接
    drm_connector_attach_encoder(&connector, &encoder);

    // 7. 重置配置
    drm_mode_config_reset(drm);

    return 0;
}

2.5 组件间的状态依赖关系

KMS 组件之间存在严格的状态依赖链。在进行 Atomic Check 时,DRM 核心会验证以下约束:

依赖关系 约束说明 验证函数
Plane → CRTC Plane 只能绑定到 possible_crtcs 位掩码允许的 CRTC drm_atomic_plane_check
CRTC → Encoder CRTC 的输出必须连接到兼容的 Encoder drm_atomic_encoder_check
Encoder → Connector Encoder 必须与 Connector 建立绑定关系 drm_atomic_connector_check
Framebuffer → Plane Framebuffer 的格式必须被 Plane 支持 drm_plane_check_pixel_format
Connector → 显示模式 Connector 必须提供至少一个有效显示模式 drm_connector_mode_valid
驱动适配关键: 在实现 atomic_check 回调时,必须遍历所有组件状态,确保硬件约束不被违反。例如,某些 SoC 的 Overlay Plane 不支持与 Primary Plane 使用不同的像素时钟,此时需要在 check 阶段拒绝此类配置。