Meson DRM 驱动架构详解

2025-10-27

模块概述

本文档基于 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)

感谢阅读!