API GUIDE / IPU / Middleware

G32S10 IPU Middleware 接口

Middleware 层接口:MID 将该选择编码为 HAL 的 osd_channel0.pad_sel/osd_sel。

1. 公开类型

1.1 模式、刷新和路由

类型/值含义
IM_IPU_MODE_OSD背景 + channel0 叠图
IM_IPU_MODE_CSC背景色彩空间转换
IM_IPU_REFRESH_SINGLE单帧,处理完成后需要重新启动
IM_IPU_REFRESH_CONTINUOUS连续刷新,后续 submit 更新输入地址
IM_IPU_ROUTE_PSRAM输出到 PSRAM
IM_IPU_ROUTE_DPU输出直通 DPU;CSC/直通路径只接受 RGB888

1.2 OSD 层顺序

值含义
IM_IPU_LAYER_CHANNEL0重叠区域 channel0 在上
IM_IPU_LAYER_BACKGROUND重叠区域背景在上

MID 将该选择编码为 HAL 的 osd_channel0.pad_sel/osd_sel。

1.3 Alpha 与预乘

im_ipu_alpha_t:

typedef struct {
    bool override;
    im_ipu_alpha_select_t select;
    uint8_t global_alpha;
} im_ipu_alpha_t;
select含义
IM_IPU_ALPHA_SELECT_PIXEL使用像素 alpha
IM_IPU_ALPHA_SELECT_GLOBAL只使用 global_alpha
IM_IPU_ALPHA_SELECT_PIXEL_X_GLOBAL像素 alpha × global alpha

override=false 时保持 MID 默认:

  • 背景:ALPHA_SELECT_PIXEL

  • channel0 packed:像素 alpha

  • channel0 planar NV12/NV21:GLOBAL/0xFF

im_ipu_premultiply_t 的 channel0_premultiplied 和background_premultiplied 分别对应两路的 PREM 位:

  • true:输入已经预乘,硬件不再乘 alpha

  • false:输入未预乘,硬件执行预乘

预乘标志必须与 buffer 内容一致,否则叠图结果会偏暗。

1.4 OSD blend equation

im_ipu_osdm_t 对应 OSD_CH0_PARA.OSDM,支持:

  • IM_IPU_OSDM_SOURCE_OVER

  • IM_IPU_OSDM_SOURCE

  • IM_IPU_OSDM_DESTINATION

  • IM_IPU_OSDM_CLEAR

  • IM_IPU_OSDM_DESTINATION_OVER

  • IM_IPU_OSDM_SOURCE_IN

  • IM_IPU_OSDM_DESTINATION_IN

  • IM_IPU_OSDM_SOURCE_OUT

  • IM_IPU_OSDM_DESTINATION_OUT

  • IM_IPU_OSDM_SOURCE_ATOP

  • IM_IPU_OSDM_DESTINATION_ATOP

  • IM_IPU_OSDM_XOR

零初始化对应 SOURCE_OVER。

1.5 图像格式和字节序

格式是否可用取决于所在路径,不能只看枚举是否存在:

格式OSD 背景channel0CSC 输入CSC 输出
ARGB8888是是否是
RGB888否是否是
NV12是是是是
NV21否是否是
YUV422是否是否
HSV否否否是
ARGB1555否是否否
2BIT否是否否

CSC 模式的 frame->output.format 只允许ARGB8888/RGB888/NV12/NV21/HSV。选择其他格式时,MID 在im_ipu_submit_frame() 中返回 IM_ERR_UNSUPPORTED,不会写入非法的D_FMT.OUT_FMT。OSD 模式不使用 frame->output.format 选择格式,输出跟随 背景格式。

packed 格式使用 im_ipu_order_t 选择字节序。2-bit 格式不使用 order。

1.6 Plane、图像和 frame

typedef struct {
    void *addr;
    uint32_t stride_bytes;
} im_ipu_plane_t;

地址和 stride 必须满足 8-byte 对齐。

im_ipu_image_t 包含:

  • format

  • order

  • width

  • height

im_ipu_frame_t 包含:

  • background:背景 Y/UV plane

  • channel0:channel0 Y/UV plane

  • output:输出格式、order 和 Y/UV/V plane

1.7 im_ipu_output_t

PSRAM 路由:

字段说明
formatCSC 模式输出格式;OSD 模式实际跟随背景格式
orderpacked 输出字节序
yY、packed 或 HSV 的 S plane
uvNV12/NV21 UV 或 HSV 的 H plane
vHSV 的 V plane

uv.addr == NULL 或 uv.stride_bytes == 0 时,MID 在 Y 平面后推导 UV; HSV 的 V 平面同理在 H 平面后推导。

DPU 路由只使用 format/order,不要求输出地址。

1.8 im_ipu_config_t

字段说明
modeOSD 或 CSC
refresh单帧或连续
routePSRAM 或 DPU
background背景格式、order、尺寸
background_alpha背景 alpha 覆盖
foregroundOSD 层顺序
premultiply两路输入预乘标志
channel0channel0 开关、图像、位置、2-bit、alpha、OSDM

MID 没有 struct_init(),调用方必须先清零结构体,再填写字段。

2. 生命周期和状态

2.1 im_ipu_init()

error_code_t im_ipu_init(im_ipu_t **handle_out);

要求 handle_out != NULL 且 *handle_out == NULL。流程:

  1. 清零 MID 单实例。
  2. 初始化 mutex 和 frame waiter。
  3. 调用 hal_ipu_init() 初始化时钟、硬件状态和 HAL context。
  4. 注册 MID 的 HAL callback。
  5. 申请 IRQ_IPU 并使能 SoC IRQ。
  6. 返回不透明 handle。

hal_ipu_init() 会关闭上一次运行遗留的扫描流,因此调用方不需要在 init 前手工 stop。它会关闭外设级 GLB_CTRL.IRQ_EN;真正开始帧处理时由 hal_ipu_start() 打开。SoC 级 IRQ_IPU 也只在 HAL 初始化并安装 callback 后 才允许使能,避免无主中断在初始化窗口内进入。

返回 IM_ERR_BUSY 表示实例已经被占用;其他失败会回滚已初始化的资源。

2.2 im_ipu_deinit()

error_code_t im_ipu_deinit(im_ipu_t **handle_out);

如果 handle 已为空或未打开,直接返回 IM_OK 并将 *handle_out 清空。

有效实例的清理顺序:

  1. stop 并清除连续帧状态。
  2. 关闭 CPU 侧 IRQ_IPU 投递,避免 HAL 拆除期间仍有中断进入。
  3. 清除 HAL callback。
  4. 调用 hal_ipu_deinit(),关闭 IPU 外设中断源并释放时钟。
  5. 释放 IRQ_IPU。
  6. 释放 waiter/mutex,清空 handle。

stop 错误优先返回;否则返回 HAL deinit 错误。

3. 帧提交

3.1 im_ipu_submit_frame()

error_code_t im_ipu_submit_frame(im_ipu_t *handle,
                                 const im_ipu_config_t *config,
                                 const im_ipu_frame_t *frame);

该函数非阻塞。每次 submit 都会:

  1. 检查 handle/config/frame。

  2. 校验配置。

  3. 检查实例处于 opened 且没有 needs_stop。

  4. 比较 config 和 frame->output 与上次保存值。

  5. 配置变化时要求当前没有 in-flight 帧,否则返回 IM_ERR_BUSY。

  6. 校验当前 frame 的地址、stride、格式和几何。

  7. clean 输入 plane,invalidate PSRAM 输出。

  8. 清除旧的 OUT_END/SIZE_ERR 锁存。

  9. 首次 submit 全量配置并启动;连续帧则更新下一帧地址。

配置和 frame->output 使用整体 memcmp 比较,因此结构体必须先清零,避免 padding 中的随机值导致误判为配置变化。

3.2 配置复用

以下任一变化会触发全量重配置:

  • im_ipu_config_t 任意字段变化

  • frame->output 任意字段变化

重配置只能在 in_flight == 0 时进行。连续帧中需要保持配置和输出描述稳定, 只更换输入地址。

3.3 im_ipu_wait_frame()

error_code_t im_ipu_wait_frame(im_ipu_t *handle,
                               im_ipu_frame_result_t *result_out,
                               uint32_t timeout_ms);

等待最早提交的 in-flight 帧。result_out:

结果含义
IM_IPU_FRAME_DONE正常完成
IM_IPU_FRAME_TIMEOUT等待超时,下一步必须 stop
IM_IPU_FRAME_HW_ERRORSIZE_ERR,下一步必须 stop

注意:timeout 和 hardware error 通过 result_out 返回时,函数本身仍返回IM_OK。没有 in-flight 帧或已经 needs_stop 时返回 IM_ERR_NOT_READY。

成功后 MID 会:

  • 消费一个已完成帧

  • 单帧模式清除 buffer ready 位

  • invalidate PSRAM 输出 cache

3.4 im_ipu_process_frame()

error_code_t im_ipu_process_frame(im_ipu_t *handle,
                                  const im_ipu_config_t *config,
                                  const im_ipu_frame_t *frame,
                                  im_ipu_frame_result_t *result_out,
                                  uint32_t timeout_ms);

等价于 submit_frame() 成功后立即 wait_frame()。result_out 必须非空。

4. 停止和错误恢复

4.1 im_ipu_stop()

error_code_t im_ipu_stop(im_ipu_t *handle, uint32_t timeout_ms);

停止流程:

  1. 关闭连续帧双缓冲。
  2. 调用 hal_ipu_stop()。
  3. 清除所有 buffer ready 位和 IRQ 锁存。
  4. 清空 in-flight、双缓冲和已绑定的 stride 状态,并清除 hw_configured。

上一次保存的 config/output 副本仍保留用于下一次比较,但下一次 submit 会因 hw_configured=false 重新执行全量配置和启动。

如果 hal_ipu_stop() 超时,MID 仍会清理自己的 in-flight/双缓冲状态并返回 IM_ERR_TIMEOUT。此时硬件是否真正 idle 需要调用方结合 HAL/LL 状态确认, 必要时重新 init 或整机恢复。

停止后下一次 submit 会重新做全量配置并启动。

4.2 超时和硬件错误

wait_frame() 检测到超时或 SIZE_ERR 后会将 MID 标记为 needs_stop。此后:

  • submit_frame() 返回 IM_ERR_NOT_READY
  • wait_frame() 返回 IM_ERR_NOT_READY
  • 必须先调用 im_ipu_stop()

这是 MID 强制要求的安全恢复路径。

5. OSD 模式约束

5.1 channel0 关闭

channel0.enable=false 时,MID 走背景直通/CSC 路径:

  • route == IM_IPU_ROUTE_PSRAM 且 output.format == background.format 时, 支持 ARGB8888、NV12、YUV422 大图数据搬运。

  • route == IM_IPU_ROUTE_DPU 时仍要求 RGB888,因此上述直通格式不能直接送 DPU。

  • packed 背景会使用 BGRA 兼容顺序

6. 典型用法

6.1 同步单帧

im_ipu_t *ipu = NULL;
im_ipu_config_t config;
im_ipu_frame_t frame;
im_ipu_frame_result_t result;
error_code_t ret;

memset(&config, 0, sizeof(config));
memset(&frame, 0, sizeof(frame));

config.mode = IM_IPU_MODE_OSD;
config.refresh = IM_IPU_REFRESH_SINGLE;
config.route = IM_IPU_ROUTE_PSRAM;
config.background.format = IM_IPU_FORMAT_NV12;
config.background.width = 640U;
config.background.height = 360U;
config.channel0.enable = true;
config.channel0.image.format = IM_IPU_FORMAT_ARGB8888;
config.channel0.image.order = IM_IPU_ORDER_ARGB;
config.channel0.image.width = 320U;
config.channel0.image.height = 180U;
config.channel0.xpos = 160U;
config.channel0.ypos = 180U;

/* frame 和 config 的 plane 地址、stride、output 按实际 buffer 填写 */

ret = im_ipu_init(&ipu);
if (ret == IM_OK) {
    ret = im_ipu_process_frame(ipu, &config, &frame, &result, 2000U);
}
(void)im_ipu_deinit(&ipu);

6.2 异步提交

ret = im_ipu_submit_frame(ipu, &config, &frame);
if (ret == IM_OK) {
    ret = im_ipu_wait_frame(ipu, &result, 2000U);
}

if ((ret == IM_OK) &&
    ((result == IM_IPU_FRAME_TIMEOUT) || (result == IM_IPU_FRAME_HW_ERROR))) {
    (void)im_ipu_stop(ipu, 1000U);
}

6.3 连续帧

ret = im_ipu_submit_frame(ipu, &config, &first_frame);
if (ret != IM_OK) {
    return ret;
}

for (i = 0U; i < frame_count; i++) {
    ret = im_ipu_wait_frame(ipu, &result, 2000U);
    if (ret != IM_OK) {
        break;
    }
    if (result != IM_IPU_FRAME_DONE) {
        (void)im_ipu_stop(ipu, 1000U);
        break;
    }

    /* 下一帧只更换输入地址;config/output 保持不变 */
    ret = im_ipu_submit_frame(ipu, &config, &next_frame);
    if (ret != IM_OK) {
        break;
    }
}