4. CRTC与PLANE的联动模型:硬件流水线视角下的CRTC与PLANE协作、主平面与覆盖平面
4.1 硬件流水线视角下的CRTC与PLANE协作
在DRM/KMS架构中,CRTC与PLANE的联动并非简单的“主从”关系,而是基于硬件显示流水线(Display Pipeline)的紧密协作。从硬件视角看,CRTC负责生成最终的像素时钟、同步信号(Hsync/Vsync)以及数据使能信号(DE),而PLANE则负责将帧缓冲区(Framebuffer)中的像素数据按图层顺序混合后送入CRTC。
核心概念: CRTC是显示流水线的“时序控制器”,PLANE是“图层混合器”。CRTC决定何时显示,PLANE决定显示什么内容。
下图展示了典型的硬件显示流水线中CRTC与PLANE的协作关系:
4.2 主平面与覆盖平面的职责划分
DRM/KMS将PLANE分为三种类型:主平面(Primary Plane)、覆盖平面(Overlay Plane)和光标平面(Cursor Plane)。其中,主平面与覆盖平面是多图层显示驱动的核心。
| 属性 | 主平面 (Primary) | 覆盖平面 (Overlay) | 光标平面 (Cursor) |
|---|---|---|---|
| 必要性 | 必须存在,每个CRTC至少一个 | 可选,硬件支持时可用 | 可选,通常硬件支持 |
| Z-order | 通常为底层(背景层) | 位于主平面之上 | 最顶层 |
| 典型用途 | 桌面背景、UI主画布 | 视频播放、画中画、游戏画面 | 鼠标指针 |
| 格式支持 | XRGB8888、ARGB8888等 | NV12、YUV420、P010等 | ARGB8888(小尺寸) |
| 尺寸限制 | 通常等于CRTC分辨率 | 可缩放、可裁剪 | 固定小尺寸(如64x64) |
| Alpha支持 | 全局Alpha或每像素Alpha | 支持Alpha混合 | 支持Alpha混合 |
驱动开发提示: 主平面是CRTC的“默认平面”,驱动初始化时必须注册。覆盖平面是性能优化的关键——将视频帧直接送到覆盖平面,可以避免CPU/GPU合成,降低功耗。
4.3 多图层混合与Z-order管理
当多个PLANE同时启用时,硬件需要按照Z-order(深度顺序)将各图层混合成最终画面。Z-order值越小,图层越靠近底层;值越大,越靠近顶层。
在驱动实现中,Z-order的管理通常通过硬件寄存器配置完成。驱动需要将PLANE的Z-order值写入硬件指定的寄存器,硬件在扫描输出时按此顺序进行Alpha混合。
// 示例:设置PLANE的Z-order(伪代码)
static void plane_set_zorder(struct drm_plane *plane, int zorder)
{
struct my_plane *my_plane = to_my_plane(plane);
struct my_device *dev = my_plane->dev;
// 将Z-order写入硬件寄存器
writel(zorder, dev->mmio + PLANE_ZORDER_REG(my_plane->id));
// 更新硬件混合顺序
writel(1, dev->mmio + MIXER_UPDATE_REG);
}
4.4 CRTC与PLANE的原子化联动
在DRM原子化(Atomic)框架中,CRTC与PLANE的联动通过drm_atomic_state对象统一管理。一次原子提交可以同时更新多个CRTC和多个PLANE的状态,确保画面切换无撕裂(tear-free)。
原子化联动的关键步骤:
- 用户空间通过
DRM_IOCTL_MODE_ATOMIC提交包含CRTC和PLANE属性的状态对象。 - 内核驱动校验所有属性的合法性(如分辨率匹配、格式支持、Z-order冲突等)。
- 驱动将校验通过的状态写入硬件寄存器,在Vblank期间一次性生效。
- 硬件在下一个Vblank开始使用新配置进行扫描输出。
下图展示了原子化联动的状态机:
4.5 驱动开发中的联动实现要点
在编写DRM/KMS驱动时,CRTC与PLANE的联动需要关注以下关键点:
- CRTC的
mode_set回调: 当CRTC的模式(分辨率、刷新率)改变时,需要重新配置所有关联PLANE的缩放和裁剪参数。 - PLANE的
update_plane回调: 更新PLANE的帧缓冲区地址、位置、尺寸时,必须确保CRTC处于开启状态,否则硬件可能无法正确锁存。 - Vblank同步: 所有PLANE的更新应在CRTC的Vblank期间完成,避免画面撕裂。使用
drm_crtc_vblank_get/put管理Vblank引用计数。 - 硬件限制检查: 不同硬件对PLANE的数量、尺寸对齐、格式组合有不同限制。驱动必须在
atomic_check中严格校验。
常见陷阱: 某些硬件要求主平面必须始终使能,即使上层覆盖平面完全遮挡了主平面。关闭主平面可能导致CRTC输出黑屏。驱动应确保主平面始终有一个有效的帧缓冲区。
4.6 实战案例:双图层视频播放场景
以典型的视频播放场景为例,展示CRTC与PLANE的联动配置:
| 组件 | 配置 | 说明 |
|---|---|---|
| CRTC | 分辨率1920x1080 @60Hz | 生成1080p60时序 |
| 主平面 | 帧缓冲区A:XRGB8888,1920x1080 | 显示播放器UI背景 |
| 覆盖平面 | 帧缓冲区B:NV12,1280x720 | 显示视频内容,硬件缩放至全屏 |
| 光标平面 | 帧缓冲区C:ARGB8888,64x64 | 鼠标指针 |
驱动在原子提交时需要完成以下操作:
// 原子提交伪代码
static int my_atomic_commit(struct drm_device *dev,
struct drm_atomic_state *state,
bool nonblock)
{
struct drm_crtc_state *crtc_state;
struct drm_plane_state *plane_state;
int i;
// 1. 校验所有状态
for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
// 检查CRTC分辨率与主平面尺寸匹配
if (crtc_state->mode.hdisplay != primary_plane->state->crtc_w)
return -EINVAL;
}
// 2. 配置硬件寄存器
for_each_new_plane_in_state(state, plane, plane_state, i) {
// 设置帧缓冲区地址
writel(plane_state->fb->pitches[0],
dev->mmio + PLANE_STRIDE_REG(plane->index));
writel(plane_state->crtc_x,
dev->mmio + PLANE_POS_X_REG(plane->index));
writel(plane_state->crtc_y,
dev->mmio + PLANE_POS_Y_REG(plane->index));
}
// 3. 触发CRTC更新
writel(1, dev->mmio + CRTC_UPDATE_REG);
return 0;
}
性能优化建议: 在视频播放场景中,将视频帧直接送到覆盖平面(NV12格式),可以避免CPU/GPU进行颜色空间转换和缩放,显著降低功耗。这是硬件合成(Hardware Composer)的核心优势。