本文基于 linux-mainline-5.18/drivers/gpu/drm/meson/meson_plane.c 进行分析,聚焦 Meson DRM 中 primary plane 的检查、配置、使能和 AFBC 路径。
- Meson Plane 深度解析
Meson Plane 深度解析
1. 文件定位与职责
meson_plane.c 是 Meson DRM 驱动里 primary plane 的核心实现,主要完成三件事:
- 对 plane 原子状态做约束检查(缩放能力、坐标合法性)。
- 将 framebuffer 和目标显示参数转换为
priv->viu.*一组“待刷寄存器语义”的字段。 - 定义 plane 的格式/压缩格式能力,并在驱动初始化时注册 plane。
这个文件本身不直接把每个寄存器都立即写硬件,而是配合 Meson DRM 的整体提交路径完成一次 atomic commit 的生效。
2. 关键数据结构
核心私有结构很简单:
struct meson_plane {
struct drm_plane base;
struct meson_drm *priv;
bool enabled;
};
base:DRM 核心 plane 对象。priv:指向驱动总私有结构,后续大量字段通过priv->viu、priv->afbcd更新。enabled:记录 plane 是否已进入使能状态,避免重复做首次使能流程。
3. Plane 原子检查:meson_plane_atomic_check
该函数调用 drm_atomic_helper_check_plane_state() 做统一校验,参数里约束了缩放能力:
- 最小缩放比:
FRAC_16_16(1, 5),即最多放大到 5x(注释也说明了 upscaling up to 5x)。 - 最大缩放比:
DRM_PLANE_HELPER_NO_SCALING,表示不允许超出 helper 可接受上限。 - 同时要求和 CRTC 状态匹配,避免超出显示窗口。
简化理解:用户态提交 plane state 后,先在这里卡掉非法缩放和非法坐标,再进入 update。
4. Plane 更新主路径:meson_plane_atomic_update
这是全文件最核心函数,逻辑按“格式路径选择 -> 缩放参数 -> 窗口寄存器 -> 缓冲区地址 -> 使能状态”推进。
4.1 AFBC 路径选择
先判断当前 SoC 与 framebuffer modifier:
- SoC 需为
GXM或G12A。 - modifier 需匹配
DRM_FORMAT_MOD_ARM_AFBC(...)且位于MESON_MOD_AFBC_VALID_BITS范围。
满足则 priv->viu.osd1_afbcd = true,否则走线性(Linear)路径。
AFBC 与非 AFBC 下会设置不同端序/路径控制位:
- G12A AFBC:设置
MESON_G12A_AFBCD_OUT_ADDR、OSD_PENDING_STAT_CLEAN、VIU_OSD1_CFG_SYN_EN等。 - GXM AFBC:设置
OSD_DPATH_MALI_AFBCD。 - 非 AFBC:清除 AFBC 路径位并走普通 OSD canvas 读取。
4.2 像素格式与 alpha 处理
根据 fb->format->format 选择 OSD block mode 和 color matrix:
XRGB/ARGB8888->OSD_BLK_MODE_32,再区分 ARGB/ABGR matrix。RGB888->OSD_BLK_MODE_24。RGB565->OSD_BLK_MODE_16。
alpha 处理点:
XRGB/XBGR:没有有效 alpha,驱动置OSD_REPLACE_EN,用固定0xFF替代像素 alpha。ARGB/ABGR:清OSD_REPLACE_EN,使用 framebuffer 自带 alpha。
4.3 缩放器参数与隔行模式
代码先取源/目标尺寸:
src_w = fixed16_to_int(new_state->src_w);
src_h = fixed16_to_int(new_state->src_h);
dst_w = new_state->crtc_w;
dst_h = new_state->crtc_h;
然后算相位步进:
hf_phase_step = ((src_w << 18) / dst_w) << 6vf_phase_step = (src_h << 20) / dst_h
隔行输出(DRM_MODE_FLAG_INTERLACE)时,目标高度与 y 坐标会按 field 处理(减半),并启用 VSC_PROG_INTERLACE 相关参数。代码注释说明了一个关键点:隔行场切换可借助垂直 scaler 的配置完成。
缩放器使能策略:
- 垂直方向:
src_h != dst_h时启用。 - 水平方向:
src_w != dst_w时启用。 - 只要任一方向缩放,
osd_sc_ctrl0开启 scaler path。
4.4 源/目标窗口寄存器打包
源码明确了窗口寄存器格式:
(x2 << 16) | x1,其中 x2 是“排他上界”,所以通常写 (end - 1)。
因此你会看到:
- 源窗口来自
new_state->src.{x1,x2,y1,y2}(16.16 定点转整数)。 - 目标窗口来自
dest.{x1,x2,y1,y2}(CRTC 坐标)。
G12A 下还会更新 blend scope 与 blend size,保证后级混合单元范围一致。
4.5 Buffer 地址与 stride 更新
通过 drm_fb_cma_get_gem_obj(fb, 0) 获取 CMA GEM 对象后,填入:
priv->viu.osd1_addr = gem->paddrpriv->viu.osd1_stride = fb->pitches[0]priv->viu.osd1_width/height = fb->width/height
AFBC 下还会记录:
priv->afbcd.modifierpriv->afbcd.format
并在 G12A 计算 AFBCD 输出行跨度(meson_g12a_afbcd_line_stride()),写入 osd1_blk2_cfg4。
4.6 首次使能行为
首次打开 plane(!meson_plane->enabled)时:
- GXM/GXL 会先执行
meson_viu_osd1_reset(priv)。 - 再置
meson_plane->enabled = true和priv->viu.osd1_enabled = true。
这里的 reset 是很典型的硬件稳定性处理,避免前序状态残留。
5. Plane 关闭路径:meson_plane_atomic_disable
disable 顺序很清晰:
- 若 AFBCD ops 存在,先
reset再disable。 - 关闭 OSD1 后混合输入:
- G12A:清
OSD1_BLEND_SRC_CTRL对应位。 - 非 G12A:清
VPP_MISC的VPP_OSD1_POSTBLEND。
- G12A:清
- 清软件态:
meson_plane->enabled = falsepriv->viu.osd1_enabled = false
6. 格式与 modifier 协商机制
6.1 支持的像素格式
supported_drm_formats[] 提供 6 种 RGB 格式:
ARGB8888ABGR8888XRGB8888XBGR8888RGB888RGB565
6.2 SoC 相关 AFBC modifier 列表
format_modifiers_default:仅LINEAR。format_modifiers_afbc_gxm:16x16 + YTR/SPARSE/SPLIT 的组合。format_modifiers_afbc_g12a:支持 16x16 与 32x8 多组合(含对性能的约束注释)。
这体现了不同代际 Meson VPU 对 AFBC 的硬件能力差异。
6.3 format_mod_supported 判定流程
meson_plane_format_mod_supported() 的判定链:
INVALID直接拒绝。LINEAR直接接受。- 若 SoC 不是 GXM/G12A,拒绝 AFBC。
- modifier 若含非法位(超出
MESON_MOD_AFBC_VALID_BITS)拒绝。 - modifier 必须出现在
plane->modifiers列表中。 - 最后调用
priv->afbcd.ops->supported_fmt()做 AFBC 细粒度格式验证。
这套机制把“硬件代际能力 + 驱动声明能力 + AFBCD 子模块能力”三层都校验到了。
7. Plane 创建与注册:meson_plane_create
创建流程:
devm_kzalloc()申请struct meson_plane。- 根据 SoC 选择 modifier 表(default / gxm / g12a)。
- 调用
drm_universal_plane_init()注册为DRM_PLANE_TYPE_PRIMARY,名称meson_primary_plane。 drm_plane_helper_add()绑定 helper 回调:atomic_checkatomic_updateatomic_disable
- 设置不可变 zpos 为 1:
drm_plane_create_zpos_immutable_property(plane, 1)。 - 保存
priv->primary_plane = plane。
8. 关键实现细节总结
- Meson plane 是典型的“DRM atomic 状态 -> 私有寄存器语义缓存”的设计。
- AFBC 支持不是全平台统一能力,代码按 SoC 精细分支。
- 缩放器和隔行处理耦合较深,尤其垂直 scaler 在 interlace 下承担场切换相关功能。
- XRGB 与 ARGB 的 alpha 语义在硬件侧显式区分,避免 UI 合成透明度错误。
- 首次使能时的 OSD reset 是稳定性关键点。
9. 调试建议
- 先确认 userspace 传入的是
LINEAR还是AFBCmodifier。 - 若 AFBC 不生效,优先检查:
- SoC 兼容分支是否命中;
- modifier 是否在
plane->modifiers中; supported_fmt()返回值。
- 若出现缩放异常,核对:
src_w/src_h与crtc_w/crtc_h;hf_phase_step/vf_phase_step;- interlace 场景下
dest.y与dst_h的减半逻辑。
- 若首帧异常,关注
meson_viu_osd1_reset()是否被执行以及执行时机。
10. 跨文件时序图(meson_plane.c + meson_crtc.c + meson_viu.c)
下面把三个文件放在一条时序线上看,会更容易理解“为什么 atomic_update 里只写 priv->viu,而真正寄存器写在中断里做”。
10.1 Atomic 提交到 VSync 生效(OSD1)
Userspace
| drmModeAtomicCommit()
v
DRM Atomic Core
|-> meson_plane_atomic_check() [meson_plane.c]
|-> meson_plane_atomic_update() [meson_plane.c]
| - 计算格式/缩放/窗口/stride
| - 写入 priv->viu.osd1_* 缓存字段
|
|-> meson_crtc_atomic_begin() [meson_crtc.c]
| - 暂存 vblank event
|
|-> meson_crtc_atomic_flush() [meson_crtc.c]
| - priv->viu.osd1_commit = true
v
等待下一次 VSync IRQ
v
meson_crtc_irq() [meson_crtc.c]
|- if (osd1_enabled && osd1_commit):
| - writel(VIU_OSD1_CTRL_STAT/STAT2/BLK0_CFGx...)
| - writel(OSD scaler寄存器)
| - 非AFBC: meson_canvas_config(...)
| - enable_osd1()
| - AFBC时: afbcd reset/setup/enable
| - osd1_commit = false
|
|- drm_crtc_handle_vblank()
|- drm_crtc_send_vblank_event()
v
硬件在场同步边界完成切换,避免撕裂
关键结论:
meson_plane_atomic_update() 负责“准备参数”,meson_crtc_irq() 负责“在安全时刻落寄存器”。
10.2 AFBC 路径切换关键时序
meson_plane_atomic_update() [meson_plane.c]
|- 根据 SoC + fb->modifier 判定 osd1_afbcd
|- 设置 osd1_blk0_cfg/osd1_ctrl_stat2 等路径位
|- 记录 priv->afbcd.{modifier,format}
|- G12A: 计算 osd1_blk2_cfg4 (line stride)
v
meson_crtc_irq() [meson_crtc.c]
|- if osd1_afbcd == true:
| - enable_osd1_afbc() (函数指针, SoC相关)
| - G12A: meson_crtc_g12a_enable_osd1_afbc()
| -> meson_viu_g12a_enable_osd1_afbc() [meson_viu.c]
| - GXM : meson_viu_gxm_enable_osd1_afbc() [meson_viu.c]
| - priv->afbcd.ops->reset/setup/enable
|
|- else:
| - disable_osd1_afbc()
| - priv->afbcd.ops->reset/disable
v
OSD1 在 AFBC 与 Linear 两条读路径间切换
关键结论:
AFBC 不是单点开关,而是 plane 预配置 + crtc irq 生效 + viu 路径控制 + afbcd ops 解码模块配置共同完成。
10.3 CRTC enable/disable 与 VIU 初始化关系
Driver bind/init 阶段
-> meson_viu_init() [meson_viu.c]
- 关 OSD1/OSD2
- 初始化 FIFO / alpha replace / blend
- 关闭 AFBC 默认路径
- 清 osd1_enabled/osd1_commit
模式设置启用阶段
-> meson_crtc_atomic_enable() [meson_crtc.c]
或 meson_g12a_crtc_atomic_enable()
- 配置 VPP blend 输出尺寸
- drm_crtc_vblank_on()
提交显示更新
-> meson_plane_atomic_update() + meson_crtc_atomic_flush()
-> meson_crtc_irq() 在 VSync 应用新参数
关闭阶段
-> meson_crtc_atomic_disable()
- drm_crtc_vblank_off()
- 清 osd1/vd1 enabled/commit
- 关闭 postblend 路径
-> meson_plane_atomic_disable() (按提交状态触发)
- AFBCD reset/disable
关键结论:
meson_viu_init() 解决“初始硬件基线”,meson_crtc_* 解决“显示管线状态机”,meson_plane_* 解决“每帧图层参数”。
感谢阅读!
