2. KMS核心概念:CRTC、Encoder、Connector、Plane、Framebuffer的角色与关系
在DRM/KMS子系统中,显示管线的抽象是理解驱动架构的基石。KMS将复杂的显示硬件分解为五个核心对象:CRTC、Encoder、Connector、Plane和Framebuffer。它们共同构成了从内存像素到屏幕光信号的完整数据通路。
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连接器 |
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)。
.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等掩码形成灵活的拓扑。
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的关键。以下是一个典型的显示刷新流程:
- 用户空间提交:应用程序通过
drmModeAtomicCommit()提交一个包含Framebuffer ID、Plane位置、CRTC模式等属性的原子请求。 - 驱动校验:内核调用
.atomic_check回调,验证所有对象的状态组合是否合法(如分辨率是否超出Encoder能力、Plane是否重叠等)。 - 硬件编程:如果校验通过,驱动在
.atomic_flush或.atomic_commit中编程硬件寄存器,配置CRTC时序、Plane源地址、Encoder模式等。 - DMA传输:CRTC通过DMA引擎从Framebuffer所在的内存地址读取像素数据,送入Plane合成器。
- 图层合成:硬件合成器按照Z-order将多个Plane的像素进行Alpha混合,生成最终的帧图像。
- 信号输出:合成后的像素数据传递给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)作为最外层,负责与显示设备的交互和热插拔检测。