异构快启 WDR 配置指南
前言
文档简介
介绍快启 WDR 的使用方法,方便开发人员使用。
目标读者
Camera 模块的驱动开发/维护人员。
适用范围
适用产品列表
:::
| 芯片型号 | 系统版本 | 驱动文件目录 |
|---|---|---|
| V861/V838/V881 Linux | Linux-6.6-xuantie | bsp/drivers/vin |
| V861/V838/V881 RTOS | FreeRTOS | rtos/lichee/rtos-hal/hal/source/vin |
| V85X Linux | Linux-4.9 | drivers/media/platform/sunxi-vin/ |
| V85X RTOS | melis-v3.0 | rtos-hal/hal/source/vin |
专有名词
RTOS系统:FreeRTOS或Melis实时系统CSI(Camera Serial Interface):图像串行接口,一般指串行数字接口ISP(Image Signal Processor):图像处理器VIPP(Video Input Post Processor):视频输入后置处理器,一般做scale处理MIPI(Mobile Industry Processor Interface):高速接收接口TDM(Time Divison Multiplexing):分时复用控制器,其中rx表示接受sensor数据,tx表示发送数据给ISP处理VINC(Video Input Control):视频输出DMA接收WDR(Wide Dynamic Range Imaging):宽动态范围成像,行业也称为HDR(High Dynamic Range Imaging)高动态范围成像MPP(Media Process Platform):媒体处理平台
Sensor 驱动修改点
RTOS 系统 Sensor 驱动修改点
下面是基于 gc4663 来讲解。
- 可以参考
RTOS的sensor驱动gc4663_mipi.c
V861/V838/V881 代码位置: rtos/lichee/rtos-hal/hal/source/vin/modules/sensor/gc4663_mipi.c
V85X 代码位置: rtos-hal/hal/source/vin/modules/sensor/gc4663_mipi.c
- 添加
Sensor静态全局变量,以便可以保存sensor的WDR模式来控制曝光增益。
static int sensor_wdr_mode[2];
- 函数
sensor_get_format和sensor_get_switch_format在获取sensor格式后,需要给sensor_wdr_mode赋值,以便可以控制不同的曝光增益。
static struct sensor_format_struct *sensor_get_format(int id, int isp_id)
{
......
sensor_wdr_mode[id] = sensor_format->wdr_mode;
......
}
static struct sensor_format_struct *sensor_get_switch_format(int id, int isp_id)
{
......
sensor_wdr_mode[id] = sensor_format->wdr_mode;
......
}
- 设置曝光增益函数
sensor_s_exp_gain和设置mbus函数sensor_g_mbus_config需要根据sensor_wdr_mode来区分线性和WDR。
static int sensor_s_exp_gain(int id, struct sensor_exp_gain *exp_gain)
{
int isp_wdr_mode;
if (sensor_wdr_mode[id])
isp_wdr_mode = ISP_DOL_WDR_MODE;
else
isp_wdr_mode = ISP_NORMAL_MODE;
......
}
static int sensor_g_mbus_config(int id, struct v4l2_mbus_config *cfg, struct
mbus_framefmt_res *res)
{
int isp_wdr_mode;
if (sensor_wdr_mode[id])
isp_wdr_mode = ISP_DOL_WDR_MODE;
else
isp_wdr_mode = ISP_NORMAL_MODE;
res->res_wdr_mode = isp_wdr_mode;
......
}
- 设置
MIPI的接收WDR模式,一般有三种模式--DOL、VC和普通模式,函数为sensor_g_mbus_config。
static int sensor_g_mbus_config(int id, struct v4l2_mbus_config *cfg, struct
mbus_framefmt_res *res)
{
......
if (isp_wdr_mode == ISP_DOL_WDR_MODE) {
res->res_combo_mode = MIPI_VC_WDR_MODE;
}
......
return 0;
}
Linux系统sensor驱动改动点同常电,没有特殊配置。
配置修改
RTOS 系统配置改动
- 修改
CSI&ISP的频率
注意不是 WDR 模式都需要修改,规格超过设置的频率才需要修改,默认频率是 324M。
CSI & ISP 频率计算公式如下,设置的频率需要取 max(csi_clk, isp_clk)。
csi_clk计算公式:
帧率 x (vts) x (hts) x 1(WDR则为2) / 8 / 1(双pixel则为2) / 1000000,向上取整,单位为MH;
isp_clk计算公式:
帧率 x (vts) x (hts) x 1.2 / 1000000,向上取整,单位为MH;
V861/V838/V881 文件位置:
rtos/lichee/rtos-hal/hal/source/vin/platform/vin_config_sun252iw1.c
参数是 vind_default_clk[VIN_TOP_CLK].frequency,如下所示
struct vin_clk_info vind_default_clk[VIN_TOP_CLK] = {
[VIN_TOP_CLK] = {
.clock_id = CLK_CSI,
.type = HAL_SUNXI_CCU,
.frequency = 324000000,
},
[VIN_TOP_CLK_SRC] = {
.clock_id = CLK_PLL_CSI_4X,
.type = HAL_SUNXI_CCU,
.frequency = 1296000000,
},
[VIN_TOP_CLK_SRC1] = {
.clock_id = CLK_PLL_PERI_300M,
.type = NOT_USE_THIS_CLK,
},
};
V85X 文件位置: lichee/rtos-hal/hal/source/vin/platform/vin_config_sun8iw21p1.c,
参数是 vind_default_clk[VIN_TOP_CLK].frequency,如下所示:
struct vin_clk_info vind_default_clk[VIN_MAX_CLK] = {
[VIN_TOP_CLK] = {
.clock = HAL_CLK_PERIPH_CSI_TOP,
.frequency = 300000000,
},
......
};
- 配置
TDM模块
由于 WDR 需要使用到 TDM 模块,所以需要把 TDM 连接到 pipeline 中。
V861/V838/V881 文件位置: rtos/lichee/rtos-hal/hal/source/vin/platform/vin_config_sun252iw1.c
V85X 文件位置: lichee/rtos-hal/hal/source/vin/platform/vin_config_sun8iw21p1.c
参数是 tdm_rx_sel,如下所示:
struct vin_core global_video[VIN_MAX_VIDEO] = {
[0] = {
......
.tdm_rx_sel = 0,
......
},
[1] = {
......
.tdm_rx_sel = 1,
......
},
}
注意:
(1) 上面代码所示 tdm_rx_sel 不是固定为0/1,需要根据实际 pipeline 选择。
(2) vin_config_sun252iw1.c 和 vin_config_sun8iw21p1.c 文件中通过 CONFIG_ISP_NUMBER 来区分单双目数据,如下:
#if (CONFIG_ISP_NUMBER == 1)
......//单目配置
#else /* CONFIG_ISP_NUMBER == 2/3 */
......//双目配置
#endif
(3) 单目配置
单目 WDR 或者单目线性--配置 global_video[0]。
(4) 双目配置
双目线性--配置 global_video[0] 和 global_video[1]
单目 WDR +单目线性或者双目 WDR --配置 global_video[0] 和 global_video[2]
RTOS系统menuconfig配置修改
V861/V838/V881 执行 mrtos menuconfig

V85X 执行 mmelis menuconfig,依次进入 VIN Devices 选项,如下图所示:

isp support number(即 CONFIG_ISP_NUMBER)可以配置范围为1-3,不同模式配置不一样:
(1) 单目 WDR 或者单目线性,配置为1。
(2) 双目 线性,配置为2。
(3) 单目 WDR +单目线性或者双目 WDR,配置为3。
RTOS系统main.c版型相关主文件修改
V861/V838/V881 此文件为 FreeRTOS 系统启动后运行指令函数集合,文件位置 rtos/lichee/rtos/projects/v861_e907/版型型号/src/main.c,主函数为 cpu0_app_entry,如下所示:
void cpu0_app_entry(void *param)
{
(void)param;
#ifdef CONFIG_COMPONENTS_PM
pm_init(1, NULL);
#endif
#ifdef CONFIG_COMPONENTS_OPENAMP
void *thread;
thread = hal_thread_create(openamp_init_thread, NULL,
"amp_init", 8 * 1024, HAL_THREAD_PRIORITY_SYS);
if (thread != NULL)
hal_thread_start(thread);
#endif
#ifdef CONFIG_COMPONENTS_TCPIP
//tcpip stack init
cmd_tcpip_init();
#endif
#if defined(CONFIG_COMPONENT_CLI) && !defined(CONFIG_UART_MULTI_CONSOLE_AS_MAIN)
vCommandConsoleStart(0x1000, HAL_THREAD_PRIORITY_CLI, NULL);
#endif
#ifdef CONFIG_PRELOAD_COMPENENTS
extern void flash_load_data_async(void);
flash_load_data_async();
#endif
#ifdef CONFIG_DRIVERS_VIN
int ret;
ret = csi_init(0, NULL);
if (ret) {
rpmsg_notify("rt-media", NULL, 0);
printf("csi init fail!\n");
} else {
#if CONFIG_ISP_NUMBER >= 2
rpmsg_notify("tdm0", NULL, 0);
#endif
rpmsg_notify("twi1", NULL, 0);
rpmsg_notify("isp0", NULL, 0);
rpmsg_notify("scaler0", NULL, 0);
rpmsg_notify("scaler4", NULL, 0);
rpmsg_notify("scaler8", NULL, 0);
rpmsg_notify("scaler12", NULL, 0);
rpmsg_notify("vinc0", NULL, 0);
rpmsg_notify("vinc4", NULL, 0);
rpmsg_notify("vinc8", NULL, 0);
rpmsg_notify("vinc12", NULL, 0);
#if CONFIG_ISP_NUMBER >= 2
rpmsg_notify("isp1", NULL, 0);
rpmsg_notify("vinc1", NULL, 0);
rpmsg_notify("vinc5", NULL, 0);
rpmsg_notify("vinc9", NULL, 0);
rpmsg_notify("vinc13", NULL, 0);
#endif
#if CONFIG_ISP_NUMBER >= 3
rpmsg_notify("isp2", NULL, 0);
rpmsg_notify("vinc2", NULL, 0);
rpmsg_notify("vinc6", NULL, 0);
rpmsg_notify("vinc10", NULL, 0);
rpmsg_notify("vinc14", NULL, 0);
#endif
#if CONFIG_ISP_NUMBER >= 4
rpmsg_notify("isp3", NULL, 0);
rpmsg_notify("vinc3", NULL, 0);
rpmsg_notify("vinc7", NULL, 0);
rpmsg_notify("vinc11", NULL, 0);
rpmsg_notify("vinc15", NULL, 0);
#endif
printf("csi init success!\n");
}
#endif
#ifdef CONFIG_COMMAND_AUTO_START_MEMTESTER
void *autotest_thread;
autotest_thread = hal_thread_create(auto_memtester_thread, NULL,
"auto_memtester", 8 * 1024, HAL_THREAD_PRIORITY_SYS);
if (autotest_thread != NULL)
hal_thread_start(autotest_thread);
#endif
vTaskDelete(NULL);
}
V85X 此文件为 Melis 系统启动后运行指令函数集合,文件位置 lichee/melis-v3.0/source/projects/v851_e907/版型选项/src/main.c,主函数为 app_entry,如下所示:
#include <stdio.h>
#include <hal_timer.h>
#include <openamp/sunxi_helper/openamp.h>
extern int csi_init(int argc, const char **argv);
extern int msh_exec(char *cmd, int length);
int app_entry(void *param)
{
#ifdef CONFIG_DRIVERS_VIN
int ret;
ret = csi_init(0, NULL);
if (ret) {
rpmsg_notify("rt-media", NULL, 0);
printf("csi init fail!\n");
}
#if 1
rpmsg_notify("twi0", NULL, 0);
rpmsg_notify("twi1", NULL, 0);
rpmsg_notify("tdm0", NULL, 0);
rpmsg_notify("isp0", NULL, 0);
rpmsg_notify("isp1", NULL, 0);
rpmsg_notify("scaler0", NULL, 0);
rpmsg_notify("scaler4", NULL, 0);
rpmsg_notify("scaler8", NULL, 0);
rpmsg_notify("scaler12", NULL, 0);
rpmsg_notify("vinc0", NULL, 0);
rpmsg_notify("vinc1", NULL, 0);
rpmsg_notify("vinc4", NULL, 0);
rpmsg_notify("vinc5", NULL, 0);
rpmsg_notify("vinc8", NULL, 0);
rpmsg_notify("vinc9", NULL, 0);
rpmsg_notify("vinc12", NULL, 0);
rpmsg_notify("vinc13", NULL, 0);
#endif
#else
hal_msleep(200);
rpmsg_notify("rt-media", NULL, 0);
#endif
msh_exec("dmesg", strlen("dmesg"));
return 0;
}
其中主要配置不同项为 rpmsg_notify,该函数的意思是发生消息给 Linux 系统,告知 Linux 系统该硬件模块 RTOS 系统已经使用完毕,Linux 系统可以注册该硬件模块的中断函数并使用此硬件模块,当然需要 Linux 系统通过函数 rpmsg_notify_add 创建 name 相同的消息回调才会被调用。
即发送方 RTOS 系统,发送函数 rpmsg_notify,接收方 Linux 系统,创建回调函数 rpmsg_notify_add 并接收,通过两个函数的参数 name 一一对应。
有四种调用组合:
(1)单目线性
rpmsg_notify("twi1, NULL, 0);
rpmsg_notify("isp0", NULL, 0);
rpmsg_notify("scaler0", NULL, 0);
rpmsg_notify("scaler4", NULL, 0);
rpmsg_notify("scaler8", NULL, 0);
rpmsg_notify("scaler12", NULL, 0);
rpmsg_notify("vinc0", NULL, 0);
rpmsg_notify("vinc4", NULL, 0);
rpmsg_notify("vinc8", NULL, 0);
rpmsg_notify("vinc12", NULL, 0);
说明:
twi表示sensor使用到的twi号,其他的均属于CSI&ISP硬件模块。- 这里通过
rpmsg_notify调用的函数需要在Linux系统的board.dts同步配置,Linux系统的模块才可以收到该消息。board.dts同步配置的是延时注册,见Linux系统配置改动章节。 vincx表示Linux系统的videox节点,Linux系统收到该消息回调的时候,会注册中断和使能iommu内存,并且调用rt-media回调函数接口告知rt-media可以开始配置videox出图。
(2) 单目 WDR
rpmsg_notify("twi1, NULL, 0);
rpmsg_notify("tdm0", NULL, 0);
rpmsg_notify("isp0", NULL, 0);
rpmsg_notify("scaler0", NULL, 0);
rpmsg_notify("scaler4", NULL, 0);
rpmsg_notify("scaler8", NULL, 0);
rpmsg_notify("scaler12", NULL, 0);
rpmsg_notify("vinc0", NULL, 0);
rpmsg_notify("vinc4", NULL, 0);
rpmsg_notify("vinc8", NULL, 0);
rpmsg_notify("vinc12", NULL, 0);
(3)双目线性
rpmsg_notify("twi0", NULL, 0);
rpmsg_notify("twi1", NULL, 0);
rpmsg_notify("tdm0", NULL, 0);
rpmsg_notify("isp0", NULL, 0);
rpmsg_notify("isp1", NULL, 0);
rpmsg_notify("scaler0", NULL, 0);
rpmsg_notify("scaler4", NULL, 0);
rpmsg_notify("scaler8", NULL, 0);
rpmsg_notify("scaler12", NULL, 0);
rpmsg_notify("vinc0", NULL, 0);
rpmsg_notify("vinc1", NULL, 0);
rpmsg_notify("vinc4", NULL, 0);
rpmsg_notify("vinc5", NULL, 0);
rpmsg_notify("vinc8", NULL, 0);
rpmsg_notify("vinc9", NULL, 0);
rpmsg_notify("vinc12", NULL, 0);
rpmsg_notify("vinc13", NULL, 0);
(4)单目 WDR +单目线性或者双目 WDR
rpmsg_notify("twi0", NULL, 0);
rpmsg_notify("twi1", NULL, 0);
rpmsg_notify("tdm0", NULL, 0);
rpmsg_notify("isp0", NULL, 0);
rpmsg_notify("isp2", NULL, 0);
rpmsg_notify("scaler0", NULL, 0);
rpmsg_notify("scaler4", NULL, 0);
rpmsg_notify("scaler8", NULL, 0);
rpmsg_notify("scaler12", NULL, 0);
rpmsg_notify("vinc0", NULL, 0);
rpmsg_notify("vinc2", NULL, 0);
rpmsg_notify("vinc4", NULL, 0);
rpmsg_notify("vinc6", NULL, 0);
rpmsg_notify("vinc8", NULL, 0);
rpmsg_notify("vinc10", NULL, 0);
rpmsg_notify("vinc12", NULL, 0);
rpmsg_notify("vinc14", NULL, 0);
Linux 系统配置改动
- 修改
CSI&ISP的频率
V861/V838/V881 可以根据 FreeRTOS 系统计算出来的时钟频率,设置到 board.dts,如下所示:
&vind0 {
csi_top = <324000000>;
......
}
V85X 可以根据 Melis 系统计算出来的时钟频率,设置到 board.dts,如下所示:
vind0:vind@0 {
vind0_clk = <300000000>;
......
}
注意:
RTOS 系统设置的 CSI & ISP 频率需要与 Linux 系统设置的 CSI & ISP 频率一样,否则可能会导致 Linux 系统无法启动。
- 设置
TWI延时注册
设置 Linux 系统延时注册的原因是 RTOS 系统还在使用该设备,如果这时 Linux 系统注册了该设备的中断函数并操作了该设备的硬件寄存器或者时钟,会导致 RTOS 系统无法使用到该设备,所以需要在 Linux 系统设置设备的延时注册。
根据硬件连接到 sensor 的 twi 号(双目则需要设置两个 twi 延时注册)
V861/V838/V881 在 board.dts 设置它延时注册,设置 rproc-name 为 6010000.e907_rproc,以 twi1 为例,如下所示:
&twi1 {
clock-frequency = <400000>;
......
rproc-name = "6010000.e907_rproc";
status = "okay";
};
V85X 在 board.dts 设置它延时注册,设置 rproc-name 为 e907_rproc@0,以 twi1 为例,如下所示:
&twi1 {
clock-frequency = <400000>;
rproc-name = "e907_rproc@0";
......
status = "okay";
};
Linux 设置 twi 延时注册后,该 twi 将无法使用直到 Melis 系统通过 rpmsg_notify("twi1", NULL, 0) 通知到 Linux 系统为止。
- 设置
TDM/ISP/scaler/vinc延时注册
由于不同的方案配置 TDM / ISP / scaler / vinc 的设备号不一致,如下:
- 单目在线线性:
isp0,scaler0,scaler4,scaler8,scaler12,vinc0,vinc4,vinc8,vinc12 - 单目在线
WDR:tdm0,isp0,scaler0,scaler4,scaler8,scaler12,vinc0,vinc4,vinc8,vinc12 - 双目离线线性:
tdm0,isp0,isp1,scaler0,scaler1,scaler4,scaler5,scaler8,scaler9,scaler12,scaler13,vinc0,vinc1,vinc4,vinc5,vinc8,vinc9,vinc12,vinc13 - 单目离线
WDR+单目离线线性或者双目离线WDR:tdm0,isp0,isp2,scaler0,scaler2,scaler4,scaler6,scaler8,scaler10