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_PIXELchannel0 packed:像素 alpha
channel0 planar NV12/NV21:
GLOBAL/0xFF
im_ipu_premultiply_t 的 channel0_premultiplied 和background_premultiplied 分别对应两路的 PREM 位:
true:输入已经预乘,硬件不再乘 alphafalse:输入未预乘,硬件执行预乘
预乘标志必须与 buffer 内容一致,否则叠图结果会偏暗。
1.4 OSD blend equation
im_ipu_osdm_t 对应 OSD_CH0_PARA.OSDM,支持:
IM_IPU_OSDM_SOURCE_OVERIM_IPU_OSDM_SOURCEIM_IPU_OSDM_DESTINATIONIM_IPU_OSDM_CLEARIM_IPU_OSDM_DESTINATION_OVERIM_IPU_OSDM_SOURCE_INIM_IPU_OSDM_DESTINATION_INIM_IPU_OSDM_SOURCE_OUTIM_IPU_OSDM_DESTINATION_OUTIM_IPU_OSDM_SOURCE_ATOPIM_IPU_OSDM_DESTINATION_ATOPIM_IPU_OSDM_XOR
零初始化对应 SOURCE_OVER。
1.5 图像格式和字节序
格式是否可用取决于所在路径,不能只看枚举是否存在:
| 格式 | OSD 背景 | channel0 | CSC 输入 | 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 包含:
formatorderwidthheight
im_ipu_frame_t 包含:
background:背景 Y/UV planechannel0:channel0 Y/UV planeoutput:输出格式、order 和 Y/UV/V plane
1.7 im_ipu_output_t
PSRAM 路由:
| 字段 | 说明 |
|---|---|
format | CSC 模式输出格式;OSD 模式实际跟随背景格式 |
order | packed 输出字节序 |
y | Y、packed 或 HSV 的 S plane |
uv | NV12/NV21 UV 或 HSV 的 H plane |
v | HSV 的 V plane |
uv.addr == NULL 或 uv.stride_bytes == 0 时,MID 在 Y 平面后推导 UV;
HSV 的 V 平面同理在 H 平面后推导。
DPU 路由只使用 format/order,不要求输出地址。
1.8 im_ipu_config_t
| 字段 | 说明 |
|---|---|
mode | OSD 或 CSC |
refresh | 单帧或连续 |
route | PSRAM 或 DPU |
background | 背景格式、order、尺寸 |
background_alpha | 背景 alpha 覆盖 |
foreground | OSD 层顺序 |
premultiply | 两路输入预乘标志 |
channel0 | channel0 开关、图像、位置、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。流程:
- 清零 MID 单实例。
- 初始化 mutex 和 frame waiter。
- 调用
hal_ipu_init()初始化时钟、硬件状态和 HAL context。 - 注册 MID 的 HAL callback。
- 申请
IRQ_IPU并使能 SoC IRQ。 - 返回不透明 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 清空。
有效实例的清理顺序:
- stop 并清除连续帧状态。
- 关闭 CPU 侧
IRQ_IPU投递,避免 HAL 拆除期间仍有中断进入。 - 清除 HAL callback。
- 调用
hal_ipu_deinit(),关闭 IPU 外设中断源并释放时钟。 - 释放
IRQ_IPU。 - 释放 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 都会:
检查 handle/config/frame。
校验配置。
检查实例处于 opened 且没有
needs_stop。比较
config和frame->output与上次保存值。配置变化时要求当前没有 in-flight 帧,否则返回
IM_ERR_BUSY。校验当前 frame 的地址、stride、格式和几何。
clean 输入 plane,invalidate PSRAM 输出。
清除旧的 OUT_END/SIZE_ERR 锁存。
首次 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_ERROR | SIZE_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);
停止流程:
- 关闭连续帧双缓冲。
- 调用
hal_ipu_stop()。 - 清除所有 buffer ready 位和 IRQ 锁存。
- 清空 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_READYwait_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;
}
}