4. drm_plane_state 结构体:平面状态、坐标、格式、Fence

drm_plane_state 是 DRM 原子提交中描述单个平面(Plane)瞬时状态的核⼼结构体。它记录了该平面在某一帧的所有配置参数,包括绑定的 Framebuffer、显示坐标、像素格式、裁剪区域以及同步 Fence。原子提交的核心工作就是构造、校验并提交这些状态对象。

4.1 结构体核心字段解析

以下表格列出了 drm_plane_state 中最关键的字段及其语义:

字段名 类型 说明
crtc struct drm_crtc * 该平面当前绑定的 CRTC,NULL 表示关闭
fb struct drm_framebuffer * 当前显示的 Framebuffer,包含像素数据与格式信息
fb_damage_clips struct drm_property_blob * 脏区域(Damage)提示,用于优化局部刷新
crtc_x, crtc_y int32_t 平面内容在 CRTC 坐标空间中的位置(左上角)
crtc_w, crtc_h uint32_t 平面在 CRTC 上的显示宽度和高度(缩放后尺寸)
src_x, src_y uint32_t 从 Framebuffer 中采样的起始坐标(16.16 定点数)
src_w, src_h uint32_t 从 Framebuffer 中采样的源尺寸(16.16 定点数)
rotation unsigned int 旋转和镜像标志位(DRM_MODE_ROTATE_* / REFLECT_*)
zpos unsigned int 平面 Z 序,值越小越靠近背景
color_encoding enum drm_color_encoding 颜色编码标准(BT.601 / BT.709 等)
color_range enum drm_color_range 颜色量化范围(有限 / 全范围)
fence struct dma_fence * 出站 Fence,表示 GPU 渲染完成信号
state struct drm_plane_state * 指向全局原子状态(用于状态所有权跟踪)
关键概念: src_* 字段使用 16.16 定点数格式(左移 16 位),允许亚像素精度的裁剪和缩放。驱动在 atomic_check 中必须验证这些坐标不超出 Framebuffer 边界。

4.2 坐标与裁剪模型

平面状态定义了从 Framebuffer 到 CRTC 显示区域的完整映射链路。下图展示了源裁剪、缩放和显示定位的关系:

平面坐标与裁剪映射模型 Framebuffer 源 src_x, src_y src_w, src_h (16.16 定点数) 裁剪窗口 缩放 CRTC 显示区域 crtc_x, crtc_y crtc_w, crtc_h (像素整数坐标) 平面显示位置 坐标映射流程 Framebuffer 裁剪窗口 (src) → 缩放 → CRTC 显示位置 (crtc) 驱动在 atomic_check 中验证 src 不越界、crtc 不超出 CRTC 尺寸
校验要点: 驱动在 atomic_check 回调中必须检查:
  • src_x + src_w 不超过 Framebuffer 宽度(左移 16 位后比较)
  • crtc_x + crtc_w 不超过 CRTC 模式设定的显示宽度
  • 缩放比例不超过硬件限制(如最大缩小 1/4 或最大放大 8 倍)

4.3 像素格式与颜色属性

Framebuffer 的像素格式由 drm_framebuffer 的 format 字段描述,而 drm_plane_state 通过 fb 指针间接引用。平面支持的格式列表在 drm_plane 的 format_types 数组中声明。

常见像素格式分类:

格式类型 示例 FOURCC 位深 典型用途
ARGB8888 DRM_FORMAT_ARGB8888 32 bpp UI 图层、光标平面
XRGB8888 DRM_FORMAT_XRGB8888 32 bpp 无透明通道的主平面
NV12 DRM_FORMAT_NV12 12 bpp 视频解码输出、摄像头预览
P010 DRM_FORMAT_P010 15 bpp HDR 视频、10-bit 精度
格式校验陷阱: 原子提交时,用户空间可能设置一个平面不支持的格式。驱动必须在 atomic_check 中遍历 plane->format_types 进行匹配,不匹配则返回 -EINVAL。此外,对于 NV12 等半平面格式,还需验证偏移量和行距(pitch)的对齐要求。

4.4 Fence 同步机制

drm_plane_state 中的 fence 字段用于实现 GPU 与 Display 之间的流水线同步。它本质上是一个 dma_fence,表示该平面的 Framebuffer 内容何时可以被 Display 硬件安全读取。

Fence 的工作流程如下:

  1. 出站 Fence(Out-fence): 用户空间在提交原子 commit 时,可以为每个平面关联一个 out-fence。当 Display 硬件完成对该平面的扫描输出后,该 fence 被 signal。
  2. 入站 Fence(In-fence): 用户空间可以传入一个 in-fence,表示 GPU 渲染尚未完成。Display 驱动必须等待该 fence signal 后才能开始扫描该平面。
  3. 驱动处理: 在 atomic_commit 的 commit_tail 阶段,驱动调用 drm_atomic_set_fence_for_plane() 将 in-fence 安装到 plane_state,并在硬件编程前等待。
Fence 同步流程 GPU 渲染 生成 Framebuffer in-fence 等待 GPU 完成 Display 控制器 扫描平面 等待 in-fence out-fence 扫描完成 signal 用户空间 等待 out-fence Fence 生命周期 in-fence:GPU→Display 同步,防止撕裂;out-fence:Display→CPU/GPU 同步,通知帧已消耗 驱动在 atomic_commit 中通过 drm_atomic_set_fence_for_plane() 管理
实现建议: 在 atomic_commit 的 prepare_fb 回调中,驱动应调用 dma_fence_get() 获取 in-fence 的引用,并在硬件编程前通过 dma_fence_wait() 或硬件等待机制阻塞。out-fence 则在 cleanup_fb 或中断处理中 signal。

4.5 状态校验流程示例

以下代码片段展示了驱动在 atomic_check 中如何校验 drm_plane_state 的坐标和格式:

static int my_plane_atomic_check(struct drm_plane *plane,
                                 struct drm_atomic_state *state)
{
    struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
    struct drm_crtc_state *crtc_state;
    struct drm_framebuffer *fb = new_state->fb;
    int ret;

    if (!fb || !new_state->crtc)
        return 0;  /* 平面关闭,跳过校验 */

    crtc_state = drm_atomic_get_new_crtc_state(state, new_state->crtc);

    /* 1. 校验像素格式是否在平面支持列表中 */
    ret = drm_plane_check_pixel_format(plane, fb->format->format);
    if (ret)
        return ret;

    /* 2. 校验源裁剪坐标不越界(16.16 定点数) */
    if (new_state->src_x + new_state->src_w > fb->width << 16 ||
        new_state->src_y + new_state->src_h > fb->height << 16)
        return -EINVAL;

    /* 3. 校验显示坐标不超出 CRTC 尺寸 */
    if (new_state->crtc_x + new_state->crtc_w > crtc_state->mode.hdisplay ||
        new_state->crtc_y + new_state->crtc_h > crtc_state->mode.vdisplay)
        return -EINVAL;

    /* 4. 校验缩放比例在硬件限制内 */
    if (new_state->crtc_w > (new_state->src_w >> 16) * 8 ||
        new_state->crtc_h > (new_state->src_h >> 16) * 8)
        return -EINVAL;

    return 0;
}
校验顺序建议: 先检查格式和基本参数,再检查坐标边界,最后检查硬件约束(缩放、对齐、带宽)。这样可以在早期快速拒绝无效请求,减少不必要的计算。

4.6 状态所有权与引用计数

drm_plane_state 的生命周期由 DRM 核心管理,但驱动需要理解以下所有权规则:

  • 分配: 驱动通过 drm_atomic_helper_plane_duplicate_state() 或自定义 atomic_duplicate_state 回调创建新状态。
  • 销毁: 原子提交完成后,旧状态通过 drm_atomic_helper_plane_destroy_state() 释放。
  • Framebuffer 引用: fb 字段在状态复制时自动增加引用计数,销毁时减少。驱动不应手动干预。
  • Fence 引用: fence 字段在设置时通过 dma_fence_get() 获取引用,在状态清理时通过 dma_fence_put() 释放。
常见错误: 驱动在 atomic_check 中修改 plane_state 后,必须确保所有修改在 atomic_commit 中可见。不要直接修改 plane->state,始终通过 drm_atomic_get_new_plane_state() 获取新状态。