跳到主要内容

异构快启 WDR 配置指南

前言

文档简介

介绍快启 WDR 的使用方法,方便开发人员使用。

目标读者

Camera 模块的驱动开发/维护人员。

适用范围

适用产品列表
芯片型号系统版本驱动文件目录
V861/V838/V881 LinuxLinux-6.6-xuantiebsp/drivers/vin
V861/V838/V881 RTOSFreeRTOSrtos/lichee/rtos-hal/hal/source/vin
V85X LinuxLinux-4.9drivers/media/platform/sunxi-vin/
V85X RTOSmelis-v3.0rtos-hal/hal/source/vin

专有名词

  • RTOS 系统:FreeRTOSMelis 实时系统
  • 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 来讲解。

  1. 可以参考 RTOSsensor 驱动 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

  1. 添加 Sensor 静态全局变量,以便可以保存 sensorWDR 模式来控制曝光增益。
static int sensor_wdr_mode[2];
  1. 函数 sensor_get_formatsensor_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;
......
}
  1. 设置曝光增益函数 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;
......
}
  1. 设置 MIPI 的接收 WDR 模式,一般有三种模式--DOLVC 和普通模式,函数为 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;
}
  1. Linux 系统 sensor 驱动改动点同常电,没有特殊配置。

配置修改

RTOS 系统配置改动

  1. 修改 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,
},
......
};
  1. 配置 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.cvin_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]

  1. RTOS 系统 menuconfig 配置修改

V861/V838/V881 执行 mrtos menuconfig

FreeRTOS 系统 menuconfig 选项配置

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

Melis 系统 menuconfig 选项配置

isp support number(即 CONFIG_ISP_NUMBER)可以配置范围为1-3,不同模式配置不一样:
(1) 单目 WDR 或者单目线性,配置为1。
(2) 双目线性,配置为2。
(3) 单目 WDR +单目线性或者双目 WDR,配置为3。

  1. 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 系统配置改动

  1. 修改 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 系统无法启动。

  1. 设置 TWI 延时注册

设置 Linux 系统延时注册的原因是 RTOS 系统还在使用该设备,如果这时 Linux 系统注册了该设备的中断函数并操作了该设备的硬件寄存器或者时钟,会导致 RTOS 系统无法使用到该设备,所以需要在 Linux 系统设置设备的延时注册。
根据硬件连接到 sensortwi 号(双目则需要设置两个 twi 延时注册)

V861/V838/V881board.dts 设置它延时注册,设置 rproc-name6010000.e907_rproc,以 twi1 为例,如下所示:

&twi1 {
clock-frequency = <400000>;
......
rproc-name = "6010000.e907_rproc";
status = "okay";
};

V85Xboard.dts 设置它延时注册,设置 rproc-namee907_rproc@0,以 twi1 为例,如下所示:

&twi1 {
clock-frequency = <400000>;
rproc-name = "e907_rproc@0";
......
status = "okay";
};

Linux 设置 twi 延时注册后,该 twi 将无法使用直到 Melis 系统通过 rpmsg_notify("twi1", NULL, 0) 通知到 Linux 系统为止。

  1. 设置 TDM / ISP / scaler / vinc 延时注册

由于不同的方案配置 TDM / ISP / scaler / vinc 的设备号不一致,如下:

  • 单目在线线性: isp0scaler0scaler4scaler8scaler12vinc0vinc4vinc8vinc12
  • 单目在线 WDR: tdm0isp0scaler0scaler4scaler8scaler12vinc0vinc4vinc8vinc12
  • 双目离线线性: tdm0isp0isp1scaler0scaler1scaler4scaler5scaler8scaler9scaler12scaler13vinc0vinc1vinc4vinc5vinc8vinc9vinc12vinc13
  • 单目离线 WDR +单目离线线性或者双目离线 WDR: tdm0isp0isp2scaler0scaler2scaler4scaler6scaler8scaler10scaler12scaler14vinc0vinc2vinc4vinc6vinc8vinc10vinc12vinc14

V861/V838/V881 设置延时注册为 delay_init = <1>,单目在线为例,如下:

tdm0: tdm@5908000 {
work_mode = <0x1>;
rpmsg-ser-name = "6010000.e907_rproc";
delay_init = <1>;
};
isp00:isp@5900000 {
work_mode = <0x1>;
rpbuf = <&rpbuf_controller0>;
isp-region = <&isp_dram_reserved>;
rpmsg-ser-name = "6010000.e907_rproc";
delay_init = <1>;
};
scaler00:scaler@5910000 {
work_mode = <0x1>;
status = "okay";
rpmsg-ser-name = "6010000.e907_rproc";
delay_init = <1>;
};
scaler10:scaler@5910400 {
work_mode = <0x1>;
status = "okay";
rpmsg-ser-name = "6010000.e907_rproc";
delay_init = <1>;
};
scaler20:scaler@5910800 {
work_mode = <0x1>;
status = "okay";
rpmsg-ser-name = "6010000.e907_rproc";
delay_init = <1>;
};
scaler30:scaler@5910c00 {
work_mode = <0x1>;
status = "okay";
rpmsg-ser-name = "6010000.e907_rproc";
delay_init = <1>;
};
vinc00:vinc@5830000 {
vinc0_csi_sel = <0>;
vinc0_mipi_sel = <0>;
vinc0_isp_sel = <0>;
vinc0_isp_tx_ch = <0>;
vinc0_tdm_rx_sel = <0>;
vinc0_vipp_sel = <0>;
vinc0_rear_sensor_sel = <0>;
vinc0_front_sensor_sel = <0>;
vinc0_sensor_list = <0>;
rpmsg-ser-name = "6010000.e907_rproc";
delay_init = <1>;
work_mode = <0x1>;
status = "okay";
};
vinc10:vinc@5831000 {
vinc4_csi_sel = <0>;
vinc4_mipi_sel = <0>;
vinc4_isp_sel = <0>;
vinc4_isp_tx_ch = <0>;
vinc4_tdm_rx_sel = <0>;
vinc4_vipp_sel = <4>;
vinc4_rear_sensor_sel = <0>;
vinc4_front_sensor_sel = <0>;
vinc4_sensor_list = <0>;
rpmsg-ser-name = "6010000.e907_rproc";
delay_init = <1>;
work_mode = <0x1>;
status = "okay";
};
vinc20:vinc@5832000 {
vinc8_csi_sel = <0>;
vinc8_mipi_sel = <0>;
vinc8_isp_sel = <0>;
vinc8_isp_tx_ch = <0>;
vinc8_tdm_rx_sel = <0>;
vinc8_vipp_sel = <8>;
vinc8_rear_sensor_sel = <0>;
vinc8_front_sensor_sel = <0>;
vinc8_sensor_list = <0>;
rpmsg-ser-name = "6010000.e907_rproc";
delay_init = <1>;
work_mode = <0x1>;
status = "okay";
};
vinc30:vinc@5833000 {
vinc12_csi_sel = <0>;
vinc12_mipi_sel = <0>;
vinc12_isp_sel = <0>;
vinc12_isp_tx_ch = <0>;
vinc12_tdm_rx_sel = <0>;
vinc12_vipp_sel = <12>;
vinc12_rear_sensor_sel = <0>;
vinc12_front_sensor_sel = <0>;
vinc12_sensor_list = <0>;
rpmsg-ser-name = "6010000.e907_rproc";
delay_init = <1>;
work_mode = <0x1>;
status = "okay";
};

V85X 设置延时注册为 delay_init = <1>,单目在线为例,如下:

isp00:isp@0 {
work_mode = <0>;
rpbuf = <&rpbuf_controller0>;
iommus = <&mmu_aw 4 0>;
isp-region = <&isp_reserved>;
delay_init = <1>;
};
scaler00:scaler@0 {
work_mode = <0>;
iommus = <&mmu_aw 1 0>;
delay_init = <1>;
};
scaler20:scaler@8 {
work_mode = <0>;
iommus = <&mmu_aw 1 0>;
delay_init = <1>;
};
scaler10:scaler@4 {
work_mode = <0>;
iommus = <&mmu_aw 1 0>;
delay_init = <1>;
};
scaler30:scaler@12 {
work_mode = <0>;
iommus = <&mmu_aw 1 0>;
delay_init = <1>;
};
vinc00:vinc@0 {
vinc0_csi_sel = <0>;
vinc0_mipi_sel = <0>;
vinc0_isp_sel = <0>;
vinc0_isp_tx_ch = <0>;
vinc0_tdm_rx_sel = <0>;
vinc0_rear_sensor_sel = <0>;
vinc0_front_sensor_sel = <0>;
vinc0_sensor_list = <0>;
work_mode = <0x0>;
iommus = <&mmu_aw 1 0>;
delay_init = <1>;
status = "okay";
};
vinc10:vinc@4 {
vinc4_csi_sel = <0>;
vinc4_mipi_sel = <0>;
vinc4_isp_sel = <0>;
vinc4_isp_tx_ch = <0>;
vinc4_tdm_rx_sel = <0>;
vinc4_rear_sensor_sel = <0>;
vinc4_front_sensor_sel = <0>;
vinc4_sensor_list = <0>;
work_mode = <0x0>;
iommus = <&mmu_aw 1 0>;
delay_init = <1>;
status = "okay";
};
vinc20:vinc@8 {
vinc8_csi_sel = <0>;
vinc8_mipi_sel = <0x0>;
vinc8_isp_sel = <0>;
vinc8_isp_tx_ch = <0>;
vinc8_tdm_rx_sel = <0>;
vinc8_rear_sensor_sel = <0>;
vinc8_front_sensor_sel = <0>;
vinc8_sensor_list = <0>;
work_mode = <0x0>;
iommus = <&mmu_aw 1 0>;
delay_init = <1>;
status = "okay";
};
vinc30:vinc@12 {
vinc12_csi_sel = <0>;
vinc12_mipi_sel = <0x0>;
vinc12_isp_sel = <0>;
vinc12_isp_tx_ch = <0>;
vinc12_tdm_rx_sel = <0>;
vinc12_rear_sensor_sel = <0>;
vinc12_front_sensor_sel = <0>;
vinc12_sensor_list = <0>;
work_mode = <0x0>;
iommus = <&mmu_aw 1 0>;
delay_init = <1>;
status = "okay";
};
  • 其中 work_mode 为0表示在线模式,为1表示离线模式。
  • TDM / ISP / scaler / vinc 设备号配置了延时注册后,Linux 系统将不能使用它们直到 RTOS 系统通过 rpmsg_notify("tdmx", NULL, 0) / rpmsg_notify("ispx", NULL, 0) / rpmsg_notify("scalerx", NULL, 0) / rpmsg_notify("vincx", NULL, 0) 通知到 Linux 系统为止,可以参考 Melis 系统配置改动章节的第4小点。
  • 配置了 scaler0-3 延时注册只需要 Melis 系统配置 scaler0 通知 rpmsg_notify("scaler0", NULL, 0) 即可,同理配置 scaler4-7 / scaler8-11 / scaler12-15 只需要 Melis 系统配置 scaler4 / scaler8 / scaler4 / scaler12 通知即可。
  1. Linux 系统 menuconfig 配置修改

执行 m kernel_menuconfig,依次进入 V4L platform devices,如下图所示:

v861

v861 Linux 系统 menuconfig 选项配置

v85x

Linux 系统 menuconfig 选项配置

开启如下配置:

  • use isp for time sharing multiplex
  • tdm reduces buf size by compression
  • ISP WDR module

WDR 开启方式

冷启动 Melis 和 Linux 开启 WDR

通过配置 flash 分区 SENSOR_ISP_CONFIG_S 结构的参数 wdr_mode,配置为1表示打开 WDR,配置为0表示关闭 WDR

V861/V838/V881 可以参考 platform/allwinner/multimedia/rt_media/sun252iw1/api_adapter/AW_VideoInput_API.c

V85X 可以参考 external/fast-user-adapter/rt_media/api_adapter/ 目录文件

AW_VideoInput_API.c 函数 isp_config_to_flash,如下所示:

void isp_config_to_flash(void)
{
SENSOR_ISP_CONFIG_S *sensor_isp_cfg0, *sensor_isp_cfg1;
int fd = 0;
struct write4k_op_t write_4k;
fd = open("/dev/mtd0", O_RDWR);
if (fd < 0) {
aw_loge("open mtd error");
return 0;
}

sensor_isp_cfg0 = malloc(sizeof(SENSOR_ISP_CONFIG_S));
memset(sensor_isp_cfg0, 0, sizeof(SENSOR_ISP_CONFIG_S));
sensor_isp_cfg0->sign = SENSOR_0_SIGN;
sensor_isp_cfg0->crc = 0;
sensor_isp_cfg0->ver = 0;
sensor_isp_cfg0->light_enable = 0;
sensor_isp_cfg0->adc_mode = 0;
sensor_isp_cfg0->light_def = 900;
sensor_isp_cfg0->ircut_state = 0;
sensor_isp_cfg0->ir_mode = 0;
sensor_isp_cfg0->lv_liner_def = 200;
sensor_isp_cfg0->lv_hdr_def = 0;
sensor_isp_cfg0->width = 0;
sensor_isp_cfg0->height = 0;
sensor_isp_cfg0->mirror = 0;
sensor_isp_cfg0->filp = 0;
sensor_isp_cfg0->fps = 15;
sensor_isp_cfg0->wdr_mode = 1;
sensor_isp_cfg0->flicker_mode = 0;
sensor_isp_cfg0->venc_format = 0;
sensor_isp_cfg0->sensor_deinit = 0;
sensor_isp_cfg0->get_yuv_en = 0;
sensor_isp_cfg0->lightadc_debug_en = 0;
sensor_isp_cfg0->light_sensor_en = 1;

write_4k.start = ISP0_PARAM_OFFSET*512;
write_4k.buf = malloc(sizeof(SENSOR_ISP_CONFIG_S));
memset(write_4k.buf, 0, sizeof(SENSOR_ISP_CONFIG_S));
memcpy((void *)write_4k.buf, (void *)sensor_isp_cfg0, sizeof(SENSOR_ISP_CONFIG_S));
write_4k.len = sizeof(SENSOR_ISP_CONFIG_S);
ioctl(fd, MEMWRITE_4K, &write_4k);
free(sensor_isp_cfg0);
......
}

该配置控制的是冷启动后出图的 WDR 模式;

通过 rt-media 用户层接口调用

设置通路配置接口 AWVideoInput_Configure(channelId_0, &config_0) 时,通过设置 config_0.enable_wdr,配置为1表示打开 WDR,配置为0表示关闭 WDR

V861/V838/V881 可以参考 platform/allwinner/multimedia/rt_media/sun252iw1/demo/demo_video_in.c

V85X 可以参考 external/fast-user-adapter/rt_media/demo/demo_video_in.c

创建通路0的实现,如下所示:

  ......
config_0.enable_wdr = 1;
if (AWVideoInput_Configure(channelId_0, &config_0) {
aw_loge("config err, exit!");
goto _exit;
}

该配置控制的不是冷启动出图,而且冷启动出图后,关闭出图,再次启动出图时通过该接口配置开启 WDR

通过 MPP 组件调用

如果 Tina 异构快启 SDK 选择的 MPP 组件的话,那么在 MPP 组件启动后,可以通过 MPP 组件配置 WDR 出图模式,接口为 configViAttr

V861/V838/V881 可以参考 platform/allwinner/eyesee-mpp/middleware/sun252iw1/sample/sample_virvi 目录的 sample_virvi.csample_virvi.conf

V85X 可以参考 external/eyesee-mpp/middleware/sun8iw21/sample/sample_virvi/ 目录的 sample_virvi.csample_virvi.conf

sample_virvi.cpViAttr->wdr_mode 配置为1表示开启 WDR,配置为0表示关闭 WDR,如下所示:

static void configViAttr(VI_ATTR_S *pViAttr, SampleVirViConfig *pConfig)
{
pViAttr->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
pViAttr->memtype = V4L2_MEMORY_MMAP;
pViAttr->format.pixelformat = map_PIXEL_FORMAT_E_to_V4L2_PIX_FMT(pConfig->PicFormat);
pViAttr->format.field = V4L2_FIELD_NONE;
pViAttr->format.colorspace = pConfig->mColorSpace;
pViAttr->format.width = pConfig->PicWidth;
pViAttr->format.height = pConfig->PicHeight;
pViAttr->nbufs = 3;
pViAttr->nplanes = 2;
pViAttr->fps = pConfig->FrameRate;
pViAttr->use_current_win = 0;
pViAttr->wdr_mode = pConfig->mEnableWDRMode;
pViAttr->capturemode = V4L2_MODE_VIDEO; /* V4L2_MODE_VIDEO; V4L2_MODE_IMAGE; V4L2_MODE_PREVIEW */
pViAttr->drop_frame_num = pConfig->mViDropFrmCnt; // drop 2 second video data, default=0
}

sample_virvi.conf 是配置文件,通过配置设置到 pViAttr->wdr_mode,参数是 enable_wdr_mode_x,配置为1表示打开 WDR,配置为0表示关闭 WDR,如下所示:

dev_num_0 = 0
isp_dev_num_0 = 0
pic_width_0 = 1920
pic_height_0 = 1080
frame_rate_0 = 20
pic_format_0 = "nv21"
color_space_0 = "rec709_part_range"
enable_wdr_mode_0 = 1
drop_frm_num_0 = 0

查看 WDR 生效的方式

可以通过在 Linux 系统串口通过命令 cat vi 节点查看,命令如下:

mount -t debugfs none /sys/kernel/debug
cat /sys/kernel/debug/mpp/vi

vi 节点信息

其中参数 isp_mode 表示当前 sensor 出图的模式:

  • isp_mode: NORMAL,表示 sensor 当前出图是线性模式
  • isp_mode: DOL_WDR,表示 sensor 当前出图是 DOL_WDR 模式

预留内存修改

ISP 预留内存修改

  1. board.dts 内存预留

通过 board.dts 预留内存,在 RTOS 系统启动后,CSI & ISP 硬件配置起来的时候使用到这块内存,当 Melis 系统 CSI & ISP 出图结束,切换到 Linux 系统时,即 Melis 系统调用 rpmsg_notify("isp0", NULL, 0) 通知到 Linux 系统的时候,这块内存会被释放。
默认预留是10M空间,如果是单目 WDR +单目线性或者双目 WDR 的话,10M空间可能不够,需要根据分辨率多预留几M空间。

V861/V838/V881 board.dts 配置预留内存,默认预留10M给 FreeRTOS 系统的 CSI & ISP 使用,如下所示:

reserved-memory {
......
isp_dram_reserved: {
reg = <0x0 0x414c6000 0x0 0xA00000>;
};
......
}

V85X board.dts 配置预留内存,默认预留10M给 Melis 系统的 CSI & ISP 使用,如下所示:

reserved-memory {
......
isp_reserved: isp_reserved {
reg = <0x0 0x43200000 0x0 0x00a00000>;
};
......
}

值得注意的是这10M内存其实包含两部分:

  • 地址范围 0x43200000-0x43BFDFFFMelis 系统 CSI & ISP 出图使用的内存。如果内存不足可以根据系统整体内存进行修改。
  • 地址范围 0x43BFE000-0x43BC0000:分配给 flash 分区读取 ISP 配置并保存区域,总共8kB。不可修改部分,如果 isp_reserved 被修改,那么需要单独在 board.dts0x43BFE000-0x43BC0000 这8kB单独进行预留。
  1. RTOS 系统 CSI & ISP 内存修改

V85X 修改方式:

上图所说地址范围 0x43200000-0x43BFDFFF 是预留给 Melis 系统的 CSI & ISP 使用,在 lichee/rtos-hal/hal/source/vin/vin.h 修改被定义,如下所示:

#define MEMRESERVE 0x43200000
//0x2000 reserved for boot0 read flash and write to it
#define MEMRESERVE_SIZE (0xa00000 - 0x2000)

可以看到预留的内存是 0x43200000,与 board.dtsisp_reserved 预留的内存是一一对应的,MEMRESERVE_SIZE 是预留内存的大小,总共10M-8k,减掉8k的原因是地址范围 0x43BFE000-0x43BC0000 是分配给 flash 分区读取 ISP 配置保存区域。
如果 board.dtsisp_reserved 预留内存被修改,那么 Melis 系统的 vin.h 中需要修改 MEMRESERVE 以及 MEMRESERVE_SIZE

V861 修改方式

位置: rtos/lichee/rtos/projects/v861_e907/perf2_fastboot/defconfig

CONFIG_ARCH_START_ADDRESS=0x41000000
CONFIG_ARCH_MEM_LENGTH=0x900000

CONFIG_ISP_MEMRESERVE_ADDR=0x414c6000
CONFIG_ISP_MEMRESERVE_LEN=0xa00000

或者 mrtos menuconfig 修改

E907 内存修改

ISP 内存修改

RTOS 系统固件预留内存修改

如果使用单目 WDR +单目线性,双目 WDR,双目摄像头不同的场景,由于 sensor 驱动的增加和效果头文件的增加会导致 Melis 系统固件增大,原本在 board.dts 预留的空间可能不够,需要进行修改。

V861/V838/V881 FreeRTOS 系统固件预留内存修改

  1. board.dts 修改

预留内存为 e907_fw,如下所示:

e907_mem_fw: e907_mem_fw@42400000 {
/* boot0 & uboot0 load elf addr */
reg = <0x0 0x42400000 0x0 0x200000>;
};

V85X Melis 系统固件预留内存修改

  1. board.dts 修改
    预留内存为 e907_fw,如下所示:
reserved-memory {
e907_fw: e907_fw {
reg = <0x0 0x43080000 0x0 0x00180000>;
};
}
  1. boot0 修改

V861 boot 固件预留内存修改 brandy/brandy-2.0/spl/include/configs/sun252iw1p1.h

#define SDRAM_OFFSET(x)                   ((phys_addr_t)0x40000000 + (x))
#define CONFIG_RTOS_LOAD_ADDR SDRAM_OFFSET(0x02400000)

V85X boot 固件预留内存修改

lichee/brandy-2.0/spl/board/sun8iw21p1/commonfastboot.mk

#E907
CFG_RISCV_E907=y
CFG_SUNXI_ELF=y
CFG_MELISELF_LOAD_ADDR=0x43080000

RTOS 系统 DRAM 预留内存修改

V861/V838/V881 FreeRTOS 系统预留的 DRAM 空间为4M,如果不够,则需要进行修改。

  1. V861/V838/V881 修改 board.dts
e907_dram_reserved: e907_dram@41000000 {
reg = <0x0 0x41000000 0x0 0x900000>;
no-map;
};
  1. FreeRTOS 系统 menuconfig 修改 执行 mrtos menuconfig,进入 Architecture Options,如下图所示

FreeRTOS 系统 menuconfig DRAM 选项配置

V85X Melis 系统给 Melis 系统预留的 DRAM 空间为4M,如果不够,则需要进行修改。

  1. board.dts 修改
    预留内存为 e907_dram,如下所示:
e907_dram: riscv_memserve {
reg = <0x0 0x43c00000 0x0 0x00400000>;
no-map;
};

......
&e907_rproc {
memory-region = <&e907_dram>, <&vdev0buffer>,
<&vdev0vring0>, <&vdev0vring1>, <&rv_share_irq_table>;
memory-mappings =
/* DA len PA */
/* DDR for e907 */
< 0x43c00000 0x00400000 0x43c00000 >;

// iommus = <&mmu_aw 5 1>;
fw-region = <&e907_fw>;
firmware-name = "melis-elf";
share-irq = "e907";
status = "okay";
};
  1. Melis 系统 menuconfig 修改
    修改 Melis 系统的 DRAM 配置,执行 mmelis menuconfig,进入 Platform Setup,如下图所示:

Melis 系统 menuconfig DRAM 选项配置

需要物理和虚拟起始地址和内存大小:

  • Physical base address of Dram0x43c00000
  • Virtual base address of Dram0x43c00000
  • Capacity of Dram0x0400000
  1. Melis 系统编译链接脚本修改
    修改编译链接脚本中的地址,文件位于 lichee/melis-v3.0/source/projects/xxx/kernel.lds,如下所示:
MEMORY
{
/*DRAM_KERNEL: 4M */
DRAM_SEG_KRN (rwx) : ORIGIN = 0x43c00000, LENGTH = 0x00400000
}