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的协作关系:

CRTC与PLANE硬件流水线协作图 CRTC与PLANE硬件流水线协作模型 主平面 (Primary Plane) 帧缓冲区A / 桌面背景 覆盖平面 (Overlay Plane) 帧缓冲区B / 视频窗口 光标平面 (Cursor Plane) 帧缓冲区C / 鼠标指针 图层混合器 Alpha混合 / Z-order CRTC 时序生成 / 扫描输出 协作流程 各PLANE独立获取像素数据 → 混合器按Z-order合成 → CRTC按时序逐行扫描输出到Encoder

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混合示意图 多图层Z-order混合流程 主平面 (Z=0) — 桌面背景 / 基础UI 覆盖平面1 (Z=1) — 视频播放窗口 覆盖平面2 (Z=2) — 字幕/OSD 光标平面 (Z=3) — 鼠标指针 混合方向:从底到顶 上层覆盖下层

在驱动实现中,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)。

原子化联动的关键步骤:
  1. 用户空间通过DRM_IOCTL_MODE_ATOMIC提交包含CRTC和PLANE属性的状态对象。
  2. 内核驱动校验所有属性的合法性(如分辨率匹配、格式支持、Z-order冲突等)。
  3. 驱动将校验通过的状态写入硬件寄存器,在Vblank期间一次性生效。
  4. 硬件在下一个Vblank开始使用新配置进行扫描输出。

下图展示了原子化联动的状态机:

CRTC与PLANE原子化联动状态机 CRTC与PLANE原子化联动状态机 空闲 (Idle) 等待用户空间请求 校验 (Check) 验证属性合法性 提交 (Commit) 写入硬件寄存器 生效 (Active) 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)的核心优势。