- 模块概述
- 数据流架构图
- Meson Driver (meson_drv.c)
- CRTC 模块 (meson_crtc.c)
- Plane 模块
- Encoder 模块 (meson_encoder_hdmi.c)
- VPU 子系统模块
- AFBC 模块 (meson_osd_afbcd.c)
- RDMA 模块 (meson_rdma.c)
- 模块串联关系
- SoC 兼容性
- 参考文档
模块概述
本文档基于 Linux Mainline 5.18 内核源码,详细分析 Amlogic Meson 系列芯片(GXBB/GXL/GXM/G12A)的 DRM 显示驱动架构。该驱动位于 drivers/gpu/drm/meson/ 目录下。
驱动特性:
- 支持 Atomic KMS API
- 支持 Gamma 校正和 Color Management
- 支持 AFBC(ARM FrameBuffer Compression)硬件压缩
- 支持多种输出格式(CVBS/HDMI)
- 支持隔行扫描 (Interlace)
# 源代码文件列表
$ ls -la drivers/gpu/drm/meson/
meson_crtc.c # CRTC 实现
meson_plane.c # Primary Plane (OSD)
meson_overlay.c # Overlay Plane (Video)
meson_encoder_hdmi.c # HDMI Encoder
meson_encoder_cvbs.c # CVBS Encoder
meson_viu.c # Video Input Unit
meson_vpp.c # Video Post Processing
meson_venc.c # Video Encoder
meson_vclk.c # Video Clock
meson_osd_afbcd.c # AFBC Decoder
meson_rdma.c # Register DMA
meson_dw_hdmi.c # DesignWare HDMI Controller
数据流架构图
┌─────────────────────────────────────────────────────────────────────────────┐
│ Meson VPU 数据流架构 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌─────────────────────────────────────────────────┐ │
│ │ DDR │ │ VPU (Video Processing Unit) │ │
│ │ Memory │ │ ┌─────────┐ ┌─────────┐ ┌──────────────┐ │ │
│ │ │ │ │ VIU │───▶│ VPP │───▶│ VENC │ │ │
│ │ ┌─────┐ │ │ │(Video │ │(Video │ │ (Video │ │ │
│ │ │OSD1 │ │ │ │ Input) │ │ Post │ │ Encoder) │ │ │
│ │ └─────┘ │ │ └────┬────┘ └────┬────┘ └──────┬───────┘ │ │
│ │ ┌─────┐ │ │ │ │ │ │ │
│ │ │VD1 │ │────┼───────┴──────────────┘ │ │ │
│ │ └─────┘ │ │ ▼ │ │
│ └─────────┘ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ ENCI │ │ ENCP │ │ HDMI-TX │ │ │
│ ┌─────────┐ │ │ (Interlace│ │(Progressive)│ │ │ │ │
│ │ AFBC │───▶│ │ Encoder) │ │ Encoder) │ │ │ │ │
│ │ Decoder │ │ └────┬─────┘ └─────┬─────┘ └─────┬────┘ │ │
│ └─────────┘ │ │ │ │ │ │
│ │ ▼ ▼ ▼ │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ VDAC │ │ VDAC │ │ TMDS │ │ │
│ │ │(CVBS) │ │(Analog) │ │(HDMI) │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
DRM 组件对应关系:
┌────────────┬────────────┬────────────┬─────────────────────┐
│ DRM 组件 │ 硬件模块 │ 源码文件 │ 职责 │
├────────────┼────────────┼────────────┼─────────────────────┤
│ Plane │ OSD1/VD1 │ meson_plane │ 显存帧缓冲管理 │
│ Plane │ VD1 │meson_overlay│ 视频overlay │
│ CRTC │ VIU+VPP │ meson_crtc │ 扫描时序控制 │
│ Encoder │ VENC+TMDS │meson_encoder│ 信号编码输出 │
│ Connector │ HDMI PHY │meson_dw_hdmi│ 外部设备连接 │
└────────────┴────────────┴────────────┴─────────────────────┘
Meson Driver (meson_drv.c)
驱动入口和初始化流程:
// meson_drv.c - 驱动初始化流程
meson_drv_bind_master
├── 1. 设备资源映射
│ ├── drm_dev_alloc(&meson_driver, dev)
│ ├── devm_platform_ioremap_resource_byname(pdev, "vpu") // VPU 寄存器
│ └── platform_get_resource_byname(pdev, IORESOURCE_MEM, "hhi") // HHI 时钟寄存器
│
├── 2. Canvas 分配 (显存 DMA 通道)
│ ├── meson_canvas_alloc(priv->canvas, &priv->canvas_id_osd1)
│ ├── meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_0)
│ ├── meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_1)
│ └── meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_2)
│
├── 3. DRM Mode Config 初始化
│ └── drmm_mode_config_init(drm)
│ ├── max_width = 3840 // 最大分辨率支持
│ ├── max_height = 2160
│ └── funcs = &meson_mode_config_funcs
│
├── 4. VPU 子系统初始化
│ ├── meson_vpu_init(priv) // 全局 VPU 配置
│ ├── meson_venc_init(priv) // 视频编码器
│ ├── meson_vpp_init(priv) // 后处理
│ └── meson_viu_init(priv) // 视频输入
│
├── 5. DRM 组件创建
│ ├── meson_encoder_cvbs_init(priv) // CVBS 编码器
│ ├── meson_encoder_hdmi_init(priv) // HDMI 编码器
│ ├── meson_plane_create(priv) // Primary Plane (OSD)
│ ├── meson_overlay_create(priv) // Overlay Plane (Video)
│ └── meson_crtc_create(priv) // CRTC
│
└── 6. 注册
├── request_irq(vsync_irq, meson_irq, ...) // VSync 中断
├── drm_mode_config_reset(drm)
├── drm_kms_helper_poll_init(drm)
└── drm_dev_register(drm, 0)
meson_driver 特性配置:
static const struct drm_driver meson_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
// DRIVER_GEM: 支持 GEM 内存管理
// DRIVER_MODESET: 支持显示模式设置
// DRIVER_ATOMIC: 支持 Atomic KMS API
.DRM_GEM_CMA_DRIVER_OPS_WITH_DUMB_CREATE(meson_dumb_create),
// CMA (Contiguous Memory Allocator) 显存分配
.fops = &fops,
.name = "meson",
.desc = "Amlogic Meson DRM",
.major = 1,
.minor = 0,
};
CRTC 模块 (meson_crtc.c)
CRTC (CRT Controller) 是 DRM 核心组件,负责时序生成和帧同步。
关键数据结构
struct meson_crtc {
struct drm_crtc base; // DRM 标准 CRTC
struct drm_pending_vblank_event *event; // VBlank 事件
struct meson_drm *priv; // 私有数据指针
void (*enable_osd1)(struct meson_drm *priv); // OSD1 使能回调
void (*enable_vd1)(struct meson_drm *priv); // VD1 使能回调
void (*enable_osd1_afbc)(struct meson_drm *priv); // AFBC 使能回调
void (*disable_osd1_afbc)(struct meson_drm *priv);
unsigned int viu_offset; // VIU 寄存器偏移 (G12A 有偏移)
bool vsync_forced; // VSync 强制标志
bool vsync_disabled; // VSync 禁用标志
};
drm_crtc_funcs 实现
static const struct drm_crtc_funcs meson_crtc_funcs = {
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.destroy = drm_crtc_cleanup,
.page_flip = drm_atomic_helper_page_flip, // 页面翻转
.reset = drm_atomic_helper_crtc_reset,
.set_config = drm_atomic_helper_set_config,
.enable_vblank = meson_crtc_enable_vblank, // 自定义
.disable_vblank = meson_crtc_disable_vblank, // 自定义
};
VBlank 回调详解:
static int meson_crtc_enable_vblank(struct drm_crtc *crtc)
{
struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
struct meson_drm *priv = meson_crtc->priv;
meson_crtc->vsync_disabled = false;
meson_venc_enable_vsync(priv); // 启用 VSync 中断
return 0;
}
// 底层实现 - meson_venc.c
void meson_venc_enable_vsync(struct meson_drm *priv)
{
// 1. 启用 ENCI 逐行复位中断
writel_relaxed(VENC_INTCTRL_ENCI_LNRST_INT_EN,
priv->io_base + _REG(VENC_INTCTRL));
// 2. 启用 VENC 时钟
regmap_update_bits(priv->hhi, HHI_GCLK_MPEG2, BIT(25), BIT(25));
}
drm_crtc_helper_funcs 实现
// GXBB/GXL/GXM 使用这套
static const struct drm_crtc_helper_funcs meson_crtc_helper_funcs = {
.atomic_begin = meson_crtc_atomic_begin, // 原子操作开始
.atomic_flush = meson_crtc_atomic_flush, // 原子操作刷新
.atomic_enable = meson_crtc_atomic_enable, // CRTC 使能
.atomic_disable = meson_crtc_atomic_disable,// CRTC 禁用
};
// G12A 使用另一套 (寄存器布局不同)
static const struct drm_crtc_helper_funcs meson_g12a_crtc_helper_funcs = {
.atomic_begin = meson_crtc_atomic_begin,
.atomic_flush = meson_crtc_atomic_flush,
.atomic_enable = meson_g12a_crtc_atomic_enable,
.atomic_disable = meson_g12a_crtc_atomic_disable,
};
Helper 回调详解:
// 1. atomic_begin - 准备 VBlank 事件
static void meson_crtc_atomic_begin(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
if (crtc->state->event) {
WARN_ON(drm_crtc_vblank_get(crtc) != 0); // 获取 VBlank 引用
spin_lock_irqsave(&crtc->dev->event_lock, flags);
meson_crtc->event = crtc->state->event; // 保存事件
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
crtc->state->event = NULL;
}
}
// 2. atomic_flush - 标记提交标志
static void meson_crtc_atomic_flush(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
struct meson_drm *priv = meson_crtc->priv;
priv->viu.osd1_commit = true; // 标记 OSD1 寄存器待提交
priv->viu.vd1_commit = true; // 标记 VD1 寄存器待提交
}
// 3. atomic_enable - 使能 CRTC
static void meson_crtc_atomic_enable(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
struct meson_drm *priv = meson_crtc->priv;
// 设置后处理尺寸
writel(crtc_state->mode.hdisplay,
priv->io_base + _REG(VPP_POSTBLEND_H_SIZE));
// 使能后处理模块
writel_bits_relaxed(VPP_POSTBLEND_ENABLE, VPP_POSTBLEND_ENABLE,
priv->io_base + _REG(VPP_MISC));
// 启用 VBlank
drm_crtc_vblank_on(crtc);
}
// 4. atomic_disable - 禁用 CRTC
static void meson_crtc_atomic_disable(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
struct meson_drm *priv = meson_crtc->priv;
drm_crtc_vblank_off(crtc);
priv->viu.osd1_enabled = false;
priv->viu.vd1_enabled = false;
// 禁用所有混合层
writel_bits_relaxed(VPP_OSD1_POSTBLEND | VPP_VD1_POSTBLEND |
VPP_VD1_PREBLEND | VPP_POSTBLEND_ENABLE, 0,
priv->io_base + _REG(VPP_MISC));
}
VSync 中断处理
CRTC 最重要的职责之一是处理 VSync 中断,在正确的时机更新硬件寄存器:
void meson_crtc_irq(struct meson_drm *priv)
{
struct meson_crtc *meson_crtc = to_meson_crtc(priv->crtc);
// === OSD1 寄存器更新 (在 VSync 时) ===
if (priv->viu.osd1_enabled && priv->viu.osd1_commit) {
// 1. 写入 OSD1 控制寄存器
writel_relaxed(priv->viu.osd1_ctrl_stat,
priv->io_base + _REG(VIU_OSD1_CTRL_STAT));
// 2. 配置 Canvas DMA
meson_canvas_config(priv->canvas, priv->canvas_id_osd1,
priv->viu.osd1_addr, priv->viu.osd1_stride,
priv->viu.osd1_height,
MESON_CANVAS_WRAP_NONE,
MESON_CANVAS_BLKMODE_LINEAR, 0);
// 3. 处理 AFBC (如果启用)
if (priv->viu.osd1_afbcd) {
priv->afbcd.ops->reset(priv);
priv->afbcd.ops->setup(priv);
priv->afbcd.ops->enable(priv);
}
// 4. 使能 OSD1
if (meson_crtc->enable_osd1)
meson_crtc->enable_osd1(priv);
priv->viu.osd1_commit = false;
}
// === VD1 寄存器更新 ===
if (priv->viu.vd1_enabled && priv->viu.vd1_commit) {
// 配置视频通道寄存器...
meson_crtc->enable_vd1(priv);
priv->viu.vd1_commit = false;
}
// === VBlank 处理 ===
if (!meson_crtc->vsync_disabled) {
drm_crtc_handle_vblank(priv->crtc); // 更新 VBlank 时间戳
// 发送待处理的 VBlank 事件
if (meson_crtc->event) {
drm_crtc_send_vblank_event(priv->crtc, meson_crtc->event);
drm_crtc_vblank_put(priv->crtc);
meson_crtc->event = NULL;
}
}
}
Plane 模块
Meson DRM 有两种 Plane:Primary Plane (OSD) 和 Overlay Plane (Video)。
Primary Plane (meson_plane.c)
Primary Plane 负责 OSD (On-Screen Display) 渲染,处理 UI 和图形:
struct meson_plane {
struct drm_plane base;
struct meson_drm *priv;
bool enabled;
};
static const struct drm_plane_funcs meson_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = drm_plane_cleanup,
.reset = drm_atomic_helper_plane_reset,
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
.format_mod_supported = meson_plane_format_mod_supported,
};
static const struct drm_plane_helper_funcs meson_plane_helper_funcs = {
.atomic_check = meson_plane_atomic_check,
.atomic_disable= meson_plane_atomic_disable,
.atomic_update = meson_plane_atomic_update,
};
Plane 初始化:
int meson_plane_create(struct meson_drm *priv)
{
// 支持的像素格式
static const uint32_t supported_drm_formats[] = {
DRM_FORMAT_ARGB8888, DRM_FORMAT_ABGR8888,
DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888,
DRM_FORMAT_RGB888, DRM_FORMAT_RGB565,
};
// AFBC modifier 支持 (GXM/G12A)
static const uint64_t format_modifiers_afbc_g12a[] = {
DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
AFBC_FORMAT_MOD_SPARSE |
AFBC_FORMAT_MOD_SPLIT),
// ... 更多配置
DRM_FORMAT_MOD_LINEAR,
DRM_FORMAT_MOD_INVALID,
};
drm_universal_plane_init(priv->drm, plane, 0xFF,
&meson_plane_funcs,
supported_drm_formats,
ARRAY_SIZE(supported_drm_formats),
format_modifiers,
DRM_PLANE_TYPE_PRIMARY, // Primary Plane
"meson_primary_plane");
}
meson_plane_atomic_update 流程:
static void meson_plane_atomic_update(struct drm_plane *plane,
struct drm_atomic_state *state)
{
// 1. 检测是否使用 AFBC 压缩
if (fb->modifier & DRM_FORMAT_MOD_ARM_AFBC(...))
priv->viu.osd1_afbcd = true;
// 2. 配置 OSD1 控制寄存器
priv->viu.osd1_ctrl_stat = OSD_ENABLE |
(0xFF << OSD_GLOBAL_ALPHA_SHIFT) |
OSD_BLK0_ENABLE;
// 3. 设置 BLK0 指向 Canvas
priv->viu.osd1_blk0_cfg[0] = canvas_id_osd1 << OSD_CANVAS_SEL;
// 4. 根据格式配置颜色矩阵
switch (fb->format->format) {
case DRM_FORMAT_ARGB8888:
priv->viu.osd1_blk0_cfg[0] |= OSD_BLK_MODE_32 | OSD_COLOR_MATRIX_32_ARGB;
break;
// ...
}
// 5. 配置 Scaler (缩放)
if (src_w != dst_w || src_h != dst_h) {
// 水平和垂直 Scaler 配置
priv->viu.osd_sc_ctrl0 = SC_CTRL0_PATH_EN | SC_CTRL0_SEL_OSD1;
// 配置缩放参数...
}
// 6. 获取帧缓冲地址
gem = drm_fb_cma_get_gem_obj(fb, 0);
priv->viu.osd1_addr = gem->paddr;
priv->viu.osd1_stride = fb->pitches[0];
priv->viu.osd1_enabled = true;
}
Overlay Plane (meson_overlay.c)
Overlay Plane 负责视频解码输出,支持多种 YUV 格式:
static const uint32_t supported_drm_formats[] = {
DRM_FORMAT_YUYV, DRM_FORMAT_NV12, DRM_FORMAT_NV21,
DRM_FORMAT_YUV444, DRM_FORMAT_YUV422, DRM_FORMAT_YUV420,
DRM_FORMAT_YUV420_8BIT, DRM_FORMAT_YUV420_10BIT, // Amlogic FBC
};
static const uint64_t format_modifiers[] = {
DRM_FORMAT_MOD_AMLOGIC_FBC(AMLOGIC_FBC_LAYOUT_SCATTER,
AMLOGIC_FBC_OPTION_MEM_SAVING),
// ... 支持 Amlogic 自有 FBC 格式
DRM_FORMAT_MOD_LINEAR,
DRM_FORMAT_MOD_INVALID,
};
Overlay 与 Primary Plane 的关键区别:
| 特性 | Primary Plane (OSD) | Overlay Plane (Video) |
|---|---|---|
| 位置 | 最顶层 (zpos=1) | 底层 (zpos=0) |
| 格式 | RGB 系列 | YUV 系列 |
| Scaler | OSD Scaler | VD Scaler (支持 0.2x-5x) |
| AFBC | ARM AFBC | Amlogic FBC |
| 用途 | UI、菜单、文字 | 视频播放 |
Plane 与 CRTC 的绑定
int meson_crtc_create(struct meson_drm *priv)
{
// 创建 CRTC 时必须指定 primary plane
ret = drm_crtc_init_with_planes(priv->drm, crtc,
priv->primary_plane, // Primary Plane 绑定
NULL, // 无 cursor plane
&meson_crtc_funcs,
"meson_crtc");
// ...
}
Encoder 模块 (meson_encoder_hdmi.c)
Bridge 模式架构
Meson HDMI 使用 DRM Bridge 架构串联多个组件:
┌────────────────────────────────────────────────────────────────┐
│ HDMI Encoder Pipeline │
├────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐│
│ │ Encoder │───▶│ Meson │───▶│ DW HDMI │───▶│ Display ││
│ │ (TMDS) │ │ Bridge │ │ Bridge │ │Connector ││
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘│
│ │ │ │ │
│ drm_encoder meson_bridge dw_hdmi_bridge drm_connector
│ │
│ Pipeline 初始化顺序: │
│ 1. meson_encoder_hdmi_init() 创建 meson_bridge │
│ 2. of_drm_find_bridge() 查找 dw_hdmi_bridge │
│ 3. drm_bridge_attach() 连接 meson_bridge 和 dw_hdmi_bridge │
│ 4. drm_bridge_connector_init() 创建 connector │
│ │
└────────────────────────────────────────────────────────────────┘
drm_bridge_funcs 实现
static const struct drm_bridge_funcs meson_encoder_hdmi_bridge_funcs = {
.attach = meson_encoder_hdmi_attach,
.detach = meson_encoder_hdmi_detach,
.mode_valid = meson_encoder_hdmi_mode_valid, // 模式验证
.hpd_notify = meson_encoder_hdmi_hpd_notify, // 热插拔
.atomic_enable = meson_encoder_hdmi_atomic_enable, // 使能
.atomic_disable = meson_encoder_hdmi_atomic_disable, // 禁用
.atomic_get_input_bus_fmts = meson_encoder_hdmi_get_inp_bus_fmts,
.atomic_check = meson_encoder_hdmi_atomic_check, // HDR 检查
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_reset = drm_atomic_helper_bridge_reset,
};
atomic_enable 详解:
static void meson_encoder_hdmi_atomic_enable(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state)
{
struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge);
struct meson_drm *priv = encoder_hdmi->priv;
// 1. 获取显示模式
mode = &crtc_state->adjusted_mode;
vic = drm_match_cea_mode(mode);
// 2. 确定 YUV 格式
if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24) {
ycrcb_map = VPU_HDMI_OUTPUT_CRYCB; // YUV420
yuv420_mode = true;
}
// 3. 配置 VENC 时序
meson_venc_hdmi_mode_set(priv, vic, ycrcb_map, yuv420_mode, mode);
// 4. 配置时钟
meson_encoder_hdmi_set_vclk(encoder_hdmi, mode);
// 5. 配置输出格式
writel_relaxed(VPU_HDMI_FMT_CTRL, ...);
// 6. 使能编码器
if (priv->venc.hdmi_use_enci)
writel_relaxed(1, priv->io_base + _REG(ENCI_VIDEO_EN));
else
writel_relaxed(1, priv->io_base + _REG(ENCP_VIDEO_EN));
}
HDMI Pipeline 初始化
int meson_encoder_hdmi_init(struct meson_drm *priv)
{
// 1. 查找外部 HDMI PHY bridge (通过 device tree)
remote = of_graph_get_remote_node(priv->dev->of_node, 1, 0);
meson_encoder_hdmi->next_bridge = of_drm_find_bridge(remote);
// 2. 创建 Meson Bridge
meson_encoder_hdmi->bridge.funcs = &meson_encoder_hdmi_bridge_funcs;
meson_encoder_hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
drm_bridge_add(&meson_encoder_hdmi->bridge);
// 3. 创建 DRM Encoder
ret = drm_simple_encoder_init(priv->drm, &encoder->encoder,
DRM_MODE_ENCODER_TMDS);
encoder->encoder.possible_crtcs = BIT(0); // 只绑定 CRTC 0
// 4. 连接 Bridge
ret = drm_bridge_attach(&encoder->encoder, &encoder->bridge, NULL,
DRM_BRIDGE_ATTACH_NO_CONNECTOR);
// 5. 创建 Connector
encoder->connector = drm_bridge_connector_init(priv->drm, &encoder->encoder);
drm_connector_attach_encoder(encoder->connector, &encoder->encoder);
// 6. 配置 Connector 属性
drm_connector_attach_max_bpc_property(connector, 8, 8);
connector->ycbcr_420_allowed = true;
// 7. CEC 通知注册
cec_notifier = cec_notifier_conn_register(...);
}
VPU 子系统模块
VIU - Video Input Unit (meson_viu.c)
VIU 负责像素扫描和基本颜色空间转换:
/**
* DOC: Video Input Unit
*
* VIU Handles the Pixel scanout and the basic Colorspace conversions
* Features:
* - OSD1 RGB565/RGB888/xRGB8888 scanout
* - RGB conversion to x/cb/cr
* - Progressive or Interlace buffer scanout
* - OSD1 Commit on Vsync
* - HDR OSD matrix for GXL/GXM/G12A
*/
OSD 颜色空间转换矩阵:
// RGB709 to YUV709 limited range
static int RGB709_to_YUV709l_coeff[MATRIX_5X3_COEF_SIZE] = {
0, 0, 0, /* pre offset */
COEFF_NORM(0.181873), COEFF_NORM(0.611831), COEFF_NORM(0.061765),
COEFF_NORM(-0.100251), COEFF_NORM(-0.337249), COEFF_NORM(0.437500),
COEFF_NORM(0.437500), COEFF_NORM(-0.397384), COEFF_NORM(-0.040116),
0, 0, 0, /* 10'/11'/12' */
0, 0, 0, /* 20'/21'/22' */
64, 512, 512, /* offset */
0, 0, 0 /* mode, right_shift, clip_en */
};
G12A vs 旧版本 AFBC 路径:
void meson_viu_g12a_enable_osd1_afbc(struct meson_drm *priv)
{
// 启用 Mali AFBC 解包器
writel_bits_relaxed(VIU_OSD1_MALI_UNPACK_EN,
VIU_OSD1_MALI_UNPACK_EN,
priv->io_base + _REG(VIU_OSD1_MALI_UNPACK_CTRL));
// 选择 AFBCD 路径
writel_bits_relaxed(OSD_PATH_OSD_AXI_SEL_OSD1_AFBCD,
OSD_PATH_OSD_AXI_SEL_OSD1_AFBCD,
priv->io_base + _REG(OSD_PATH_MISC_CTRL));
}
VPP - Video Post Processing (meson_vpp.c)
VPP 负责混合、Scaler 和 CSC:
/**
* DOC: Video Post Processing
*
* VPP Handles all the Post Processing after the Scanout from the VIU
* Features:
* - Postblend, Blends the OSD1 only
* - Vertical OSD Scaler for OSD1 only
* - Intermediate FIFO with default Amlogic values
*/
VPP 数据流:
┌─────────────────────────────────────────────────────────────┐
│ VPP Pipeline │
├─────────────────────────────────────────────────────────────┤
│ │
│ OSD1 ──┐ │
│ │ ┌─────────────────────────────────────────┐ │
│ OSD2 ──┼───▶│ Blend Module │ │
│ │ │ ┌───────────┐ ┌──────────────┐ │ │
│ VD1 ──┼───▶│ │ Preblend │───▶│ Postblend │───▶ │ │
│ │ │ └───────────┘ └──────────────┘ │ │
│ VD2 ──┘ └─────────────────────────────────────────┘ │
│ │
│ │ │
│ ▼ │
│ ┌──────────────┐ │
│ │ Scaler │ │
│ │ (H/V) │ │
│ └──────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────┐ │
│ │ CSC Matrix │ │
│ │ (RGB↔YUV) │ │
│ └──────────────┘ │
│ │ │
│ ▼ │
│ VENC 输入 │
└─────────────────────────────────────────────────────────────┘
VENC - Video Encoder (meson_venc.c)
VENC 负责像素编码到不同输出格式:
/**
* DOC: Video Encoder
*
* VENC Handle the pixels encoding to the output formats.
* Paths:
*
* _____ _____ ____________________
* vd1---| |-| | | VENC /---------|----VDAC
* vd2---| VIU |-| VPP |-|-----ENCI/-ENCI_DVI-|-|
* osd1--| |-| | | \ | X--HDMI-TX
* osd2--|_____|-|_____| | |\-ENCP--ENCP_DVI-|-|
* | | |
* | \--ENCL-----------|----LVDS
* |____________________|
*/
VENC 时序模式:
| 模式 | 用途 | 编码器 | 分辨率 |
|---|---|---|---|
| ENCI | 隔行 (PAL/NTSC) | CVBS/HDMI | 480i/576i |
| ENCP | 逐行 | HDMI/Analog | 480p-1080p |
| ENCL | LCD | LVDS | 可变 |
VCLK - Video Clock (meson_vclk.c)
VCLK 负责时钟生成:
┌─────────────────────────────────────────────────────────────┐
│ Clock Architecture │
├─────────────────────────────────────────────────────────────┤
│ │
│ __________ _________ _____ │
│ | | | | | |--ENCI │
│ | HDMI PLL |-| PLL_DIV |--- VCLK--| |--ENCL │
│ |__________| |_________| \ | MUX |--ENCP │
│ --VCLK2-| |--VDAC │
│ |_____|--HDMI-TX │
│ │
│ 支持的像素时钟范围: 25MHz - 594MHz │
│ 支持 HDMI 2.0 (最高 6Gbps TMDS) │
└─────────────────────────────────────────────────────────────┘
AFBC 模块 (meson_osd_afbcd.c)
AFBC (ARM FrameBuffer Compression) 是 ARM 设计的帧缓冲压缩格式。
GXM vs G12A AFBC 差异
| 特性 | GXM AFBC | G12A Mali AFBC |
|---|---|---|
| 标准版本 | AFBC 1.0 | AFBC 1.2 |
| 兼容 GPU | Mali T820 | Mali G31/G52 |
| 格式支持 | RGB32 only | RGB + YUV |
| Superblock | 16x16 only | 16x16 + 32x8 |
| Tiled Header | 不支持 | 支持 |
| 独立通道 | 共享 | 4 通道独立 |
AFBC 与 Plane 的协作
// meson_plane_atomic_update() 中
if (fb->modifier & DRM_FORMAT_MOD_ARM_AFBC(...)) {
priv->viu.osd1_afbcd = true;
priv->afbcd.modifier = fb->modifier;
priv->afbcd.format = fb->format->format;
}
// meson_crtc_irq() 中
if (priv->viu.osd1_afbcd) {
// 启用 AFBC 解码器
meson_crtc->enable_osd1_afbc(priv);
// 配置解码参数
priv->afbcd.ops->setup(priv);
// 使能解码
priv->afbcd.ops->enable(priv);
}
G12A AFBC 解码流程:
// 1. 初始化 RDMA
static int meson_g12a_afbcd_init(struct meson_drm *priv)
{
ret = meson_rdma_init(priv); // 初始化 Register DMA
meson_rdma_setup(priv);
// 手动处理复位
}
// 2. 设置解码参数
static int meson_g12a_afbcd_setup(struct meson_drm *priv)
{
// 配置格式
format = meson_g12a_afbcd_pixel_fmt(priv->afbcd.modifier, ...);
meson_rdma_writel_sync(priv, format, VPU_MAFBC_FORMAT_SPECIFIER_S0);
// 配置缓冲区地址
meson_rdma_writel_sync(priv, priv->viu.osd1_addr, VPU_MAFBC_HEADER_BUF_ADDR_LOW_S0);
// 配置输出地址 (内部解码内存)
meson_rdma_writel_sync(priv, MESON_G12A_AFBCD_OUT_ADDR,
VPU_MAFBC_OUTPUT_BUF_ADDR_LOW_S0);
}
// 3. 使能解码
static int meson_g12a_afbcd_enable(struct meson_drm *priv)
{
meson_rdma_writel_sync(priv, VPU_MAFBC_IRQ_SURFACES_COMPLETED |
VPU_MAFBC_IRQ_CONFIGURATION_SWAPPED, ...);
meson_rdma_writel_sync(priv, VPU_MAFBC_S0_ENABLE, VPU_MAFBC_SURFACE_CFG);
meson_rdma_flush(priv); // 触发 RDMA
}
RDMA 模块 (meson_rdma.c)
RDMA (Register DMA) 用于在 VSync 时自动重放寄存器写入:
/**
* DOC: Register DMA
*
* The VPU embeds a "Register DMA" that can write a sequence of registers
* on the VPU AHB bus, either manually or triggered by an internal IRQ
* event like VSYNC or a line input counter.
*/
RDMA 工作流程:
// 1. 初始化 - 分配 DMA 缓冲区
int meson_rdma_init(struct meson_drm *priv)
{
priv->rdma.addr = dma_alloc_coherent(priv->dev, SZ_4K,
&priv->rdma.addr_dma, GFP_KERNEL);
priv->rdma.offset = 0;
// 配置 RDMA 控制器
writel_relaxed(RDMA_CTRL_SW_RESET, priv->io_base + _REG(RDMA_CTRL));
writel_relaxed(RDMA_DEFAULT_CONFIG, priv->io_base + _REG(RDMA_CTRL));
}
// 2. 写入寄存器到缓冲区
void meson_rdma_writel_sync(struct meson_drm *priv, uint32_t val, uint32_t reg)
{
// 添加到 DMA 缓冲区
priv->rdma.addr[priv->rdma.offset++] = reg;
priv->rdma.addr[priv->rdma.offset++] = val;
// 同时直接写入 (用于同步)
writel_relaxed(val, priv->io_base + _REG(reg));
}
// 3. 刷新 - 配置 RDMA 触发
void meson_rdma_flush(struct meson_drm *priv)
{
// 设置 DMA 缓冲区地址
writel(priv->rdma.addr_dma, priv->io_base + _REG(RDMA_AHB_START_ADDR_1));
writel(priv->rdma.addr_dma + (priv->rdma.offset * RDMA_DESC_SIZE) - 1,
priv->io_base + _REG(RDMA_AHB_END_ADDR_1));
// 配置为 VSYNC 触发
writel_bits_relaxed(RDMA_ACCESS_TRIGGER_CHAN1,
FIELD_PREP(RDMA_ACCESS_TRIGGER_CHAN1,
RDMA_ACCESS_TRIGGER_VSYNC),
priv->io_base + _REG(RDMA_ACCESS_AUTO));
priv->rdma.offset = 0;
}
模块串联关系
┌─────────────────────────────────────────────────────────────────────────────┐
│ Atomic Commit 流程 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. 用户空间提交 Atomic 请求 │
│ │ │
│ ▼ │
│ 2. drm_atomic_commit() │
│ │ │
│ ├──────────────────────────────────────┐ │
│ ▼ ▼ │
│ 3. Plane atomic_check() Encoder/Connector check() │
│ │ │
│ ├──────────────────────┐ │ │
│ ▼ ▼ │ │
│ 4. meson_plane_ meson_overlay_ │
│ atomic_check() atomic_check() │
│ │ │ │
│ │ │ │
│ ▼ ▼ │
│ 5. 配置存入 priv->viu 结构体 │
│ (osd1_ctrl_stat, osd1_blk0_cfg, vd1_if0_gen_reg...) │
│ │
│ │ │
│ ▼ │
│ 6. CRTC Helper: meson_crtc_atomic_begin() │
│ │ │
│ ▼ │
│ 7. CRTC Helper: meson_crtc_atomic_flush() │
│ 设置 osd1_commit=true, vd1_commit=true │
│ │ │
│ ▼ │
│ 8. CRTC Helper: meson_crtc_atomic_enable() │
│ │ │
│ ▼ │
│ 9. Encoder Bridge: meson_encoder_hdmi_atomic_enable() │
│ 配置 VENC 模式、VCLK 时钟 │
│ │
│ │ │
│ ▼ │
│ 10. 等待 VSync 中断 │
│ │ │
│ ▼ │
│ 11. meson_crtc_irq() │
│ - 写入 OSD1/VD1 寄存器 │
│ - 配置 Canvas DMA │
│ - 处理 AFBC │
│ - 发送 VBlank 事件 │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
SoC 兼容性
Meson DRM 通过 meson_vpu_is_compatible() 实现多 SoC 兼容:
enum vpu_compatible {
VPU_COMPATIBLE_GXBB = 0, // 第一代 (S905)
VPU_COMPATIBLE_GXL = 1, // 第二代 (S905X, S905D)
VPU_COMPATIBLE_GXM = 2, // 第二代 Plus (S912)
VPU_COMPATIBLE_G12A = 3, // 第三代 (S905X2, S905Y2, S922X)
};
SoC 差异总结:
| 特性 | GXBB | GXL/GXM | G12A |
|---|---|---|---|
| Primary Plane | ✓ | ✓ | ✓ |
| Overlay Plane | ✓ | ✓ | ✓ |
| AFBC (OSD) | ✗ | ✓ | ✓ |
| HDR OSD | ✗ | ✓ | ✓ |
| G12A OSD Blend | ✗ | ✗ | ✓ |
| Mali AFBC | ✗ | ✗ | ✓ |
| RDMA | ✗ | ✗ | ✓ |
| 4K60 输出 | ✗ | ✗ | ✓ |
参考文档
- Linux DRM/KMS 文档:
Documentation/gpu/drm-kms.rst - ARM AFBC 规范: ARM 官方文档
- Amlogic VPU 数据手册 (需 NDA)
感谢阅读!
