2. KMS核心概念:CRTC、Encoder、Connector、Plane、Framebuffer的角色与关系

在DRM/KMS子系统中,显示管线的抽象是理解驱动架构的基石。KMS将复杂的显示硬件分解为五个核心对象:CRTC、Encoder、Connector、Plane和Framebuffer。它们共同构成了从内存像素到屏幕光信号的完整数据通路。

核心认知:KMS的五个对象并非物理硬件的简单映射,而是对显示流水线的一种逻辑分层。理解它们之间的“组合”与“连接”关系,是编写正确且高效的显示驱动的第一步。

2.1 五元组角色总览

下表从功能、数据流方向和典型硬件对应三个维度,快速建立对每个对象的初步认知。

对象 核心角色 数据流方向 典型硬件对应
Framebuffer 内存中的像素数据容器 内存 → 显示控制器 显存缓冲区、DMA缓冲区
Plane 独立的图像层,支持缩放与裁剪 Framebuffer → CRTC 硬件图层(Overlay/Primary/Cursor)
CRTC 显示时序生成器与扫描控制器 Plane → Encoder 显示控制器、视频定时发生器(VTG)
Encoder 像素数据编码与信号转换 CRTC → Connector HDMI编码器、LVDS发送器、DSI D-PHY
Connector 物理接口与显示设备探测 Encoder → 显示器 HDMI端口、VGA接口、eDP连接器
关键区别:Framebuffer是数据源,Plane是合成层,CRTC是时序引擎,Encoder是信号转换器,Connector是物理接口。这五个对象在驱动中通过drm_mode_object基类统一管理。

2.2 核心对象深度解析

2.2.1 Framebuffer(帧缓冲)

Framebuffer是内存中一块连续的物理内存区域,存储了待显示的像素数据。它由用户空间通过drmModeAddFB()创建,并关联一个drm_framebuffer对象。驱动需要提供drm_framebuffer_funcs回调,处理内存映射和销毁。

// Framebuffer 创建时的关键参数
struct drm_mode_fb_cmd2 {
    __u32 fb_id;           // 返回的FB ID
    __u32 width, height;   // 分辨率
    __u32 pixel_format;    // 像素格式,如 DRM_FORMAT_XRGB8888
    __u32 handles[4];      // GEM handle数组(每个plane一个)
    __u32 pitches[4];      // 每行字节数(stride)
    __u32 offsets[4];      // 每个plane的偏移
};

2.2.2 Plane(图层)

Plane代表一个独立的硬件合成层。每个Plane可以绑定一个Framebuffer,并独立设置位置(crtc_x/crtc_y)、大小(crtc_w/crtc_h)和源裁剪(src_x/src_y/src_w/src_h)。KMS定义了三种Plane类型:

  • DRM_PLANE_TYPE_PRIMARY:主图层,每个CRTC必须至少有一个,通常用于桌面背景。
  • DRM_PLANE_TYPE_OVERLAY:叠加层,用于视频播放、UI弹窗等,支持硬件缩放。
  • DRM_PLANE_TYPE_CURSOR:光标层,专用于鼠标指针,通常尺寸固定(如64x64)。
避坑提示:并非所有硬件都支持Overlay层的任意位置和大小。驱动在.atomic_check阶段必须验证Plane的配置是否在硬件能力范围内,否则可能导致合成失败或显示异常。

2.2.3 CRTC(显示控制器)

CRTC是显示管线的核心调度器。它负责生成行/场同步信号(HSYNC/VSYNC)、像素时钟,并控制从Plane读取数据到Encoder的时序。CRTC的状态包括:

  • 模式(Mode):分辨率、刷新率、时钟参数。
  • 使能状态(Active/Enabled):是否正在输出。
  • Gamma/LUT:色彩查找表,用于色彩校正。
  • 背景色:当所有Plane透明时的显示颜色。

2.2.4 Encoder(编码器)

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

  • TMDS:用于HDMI/DVI。
  • LVDS:用于嵌入式LCD屏。
  • DSI:用于MIPI DSI接口。
  • DisplayPort:用于DP/eDP。

Encoder通过possible_crtcs掩码声明它可以连接到哪些CRTC。

2.2.5 Connector(连接器)

Connector代表物理显示接口和连接的显示器。它负责:

  • 探测(Detect):通过.detect()回调检查显示器是否插入。
  • 获取EDID:读取显示器的能力数据(支持的分辨率、色彩空间等)。
  • 状态管理:维护连接状态(connected/disconnected)和当前模式。

2.3 对象关系与连接拓扑

下图展示了KMS五元组之间的数据流和连接关系。注意,一个系统中可能存在多个CRTC、多个Encoder和多个Connector,它们通过possible_clones和possible_crtcs等掩码形成灵活的拓扑。

KMS五元组关系拓扑图 KMS核心对象关系拓扑 Framebuffer 内存像素数据容器 Plane 独立图像合成层 CRTC 显示时序控制器 Encoder 信号编码转换器 Connector 物理接口与探测 绑定 合成输出 时序+像素 编码信号 拓扑说明: 一个CRTC可绑定多个Plane(主层+叠加层+光标层);一个Encoder可连接多个Connector(克隆模式); 一个Connector只能连接一个Encoder。实际拓扑通过possible_crtcs/possible_clones掩码配置。 显示器

2.4 对象间的关键约束

KMS对象之间的连接并非任意,而是受到硬件能力和驱动配置的严格约束。以下是驱动开发中必须处理的核心规则:

约束类型 描述 驱动实现位置
CRTC-Encoder兼容性 Encoder通过possible_crtcs掩码声明可连接的CRTC drm_encoder_init()时设置
Encoder-Connector连接 一个Connector只能连接一个Encoder,但一个Encoder可连接多个Connector(克隆) drm_connector_attach_encoder()
Plane-CRTC绑定 Plane通过possible_crtcs声明可绑定的CRTC drm_universal_plane_init()时设置
Framebuffer-Plane匹配 Framebuffer的像素格式必须被Plane支持 .atomic_check中验证
Z-order顺序 多个Plane的叠加顺序由zpos属性控制 用户空间设置或驱动默认

2.5 数据流全景:从Framebuffer到像素点亮

理解数据流是掌握KMS的关键。以下是一个典型的显示刷新流程:

  1. 用户空间提交:应用程序通过drmModeAtomicCommit()提交一个包含Framebuffer ID、Plane位置、CRTC模式等属性的原子请求。
  2. 驱动校验:内核调用.atomic_check回调,验证所有对象的状态组合是否合法(如分辨率是否超出Encoder能力、Plane是否重叠等)。
  3. 硬件编程:如果校验通过,驱动在.atomic_flush或.atomic_commit中编程硬件寄存器,配置CRTC时序、Plane源地址、Encoder模式等。
  4. DMA传输:CRTC通过DMA引擎从Framebuffer所在的内存地址读取像素数据,送入Plane合成器。
  5. 图层合成:硬件合成器按照Z-order将多个Plane的像素进行Alpha混合,生成最终的帧图像。
  6. 信号输出:合成后的像素数据传递给Encoder,Encoder将其转换为TMDS/LVDS/DSI等信号,通过Connector发送到显示器。
性能关键点:整个流程中,.atomic_check和.atomic_flush之间的时间窗口非常关键。驱动应尽量减少锁持有时间,避免在.atomic_flush中执行耗时操作(如等待VSYNC),否则会导致用户空间渲染线程阻塞。

2.6 驱动中的对象注册示例

以下代码片段展示了在驱动初始化时,如何创建并注册KMS五元组对象。注意对象之间的连接关系。

// 驱动probe函数中的典型KMS对象初始化流程
int my_display_init(struct drm_device *drm) {
    struct drm_plane *primary_plane;
    struct drm_crtc *crtc;
    struct drm_encoder *encoder;
    struct drm_connector *connector;

    // 1. 创建主Plane(支持XRGB8888格式)
    primary_plane = drm_universal_plane_init(drm, &my_plane_funcs,
                                             DRM_PLANE_TYPE_PRIMARY,
                                             &my_plane_helper_funcs,
                                             my_formats, ARRAY_SIZE(my_formats),
                                             NULL, NULL, NULL);

    // 2. 创建CRTC,关联主Plane
    crtc = drm_crtc_init_with_planes(drm, &my_crtc_funcs,
                                     primary_plane, NULL,  // cursor_plane = NULL
                                     &my_crtc_helper_funcs, NULL);

    // 3. 创建Encoder,设置possible_crtcs掩码(位0表示CRTC 0)
    encoder = drm_encoder_init(drm, &my_encoder_funcs,
                               DRM_MODE_ENCODER_TMDS, NULL);
    encoder->possible_crtcs = 1 << drm_crtc_index(crtc);

    // 4. 创建Connector,并关联Encoder
    connector = drm_connector_init(drm, &my_connector_funcs,
                                   DRM_MODE_CONNECTOR_HDMIA);
    drm_connector_attach_encoder(connector, encoder);

    // 5. 注册所有对象到KMS框架
    drm_mode_config_reset(drm);
    return 0;
}
常见错误:忘记设置possible_crtcs掩码会导致用户空间无法将CRTC与Encoder连接,表现为drmModeSetCrtc()返回-EINVAL。同样,Plane的possible_crtcs也必须正确设置,否则Plane无法绑定到CRTC。

2.7 总结:五元组协作模型

KMS的五元组设计体现了显示子系统的分层抽象:

  • 数据层(Framebuffer)与合成层(Plane)分离,使得内存管理和图像处理可以独立优化。
  • 时序控制(CRTC)与信号编码(Encoder)分离,允许同一CRTC驱动不同接口的Encoder。
  • 物理接口(Connector)作为最外层,负责与显示设备的交互和热插拔检测。