本章节文章我会详细记录一款开发环境没有的摄像头传感器,从硬件接线到驱动改写再到点亮的全流程记录,后续会继续更新调优部分。这次使用的soc平台是Sigmastar的SSD2355芯片,摄像头传感器是SmartSens的SC200PC传感器,这是一款小型MIPI接口摄像头,主要用于个人网络摄像头,平板及笔记本的摄像头,最大图像传输速率规格为1920 (H) × 1080 (V) @ 60 fps 10-bit,分辨率为200万。在开始之前我简单罗列了一下步骤提纲,让心里有一个概念该如何去切入,主要分为以下四个部分,我们会逐个分析。
<1> 硬件bring up
根据原理图首先确认硬件连接,比对开发板和自己PCB板的硬件设计是否一致,根据模块规格书确定接线。
<2> 内核设备树修改确认
根据开发文档摄像头移植那里所写的,看一下设备树配置是否都能对的上重点就看CSI和sensorif的配置,尤其注意线序的配置。
<3> 驱动改写
这部分最为重要,需要替换掉全部寄存器配置以及上下电时序
<4> 出流程序的编写
写一个VIF的程序去让摄像头出流,再通过指令抓取RAW原始图像,即可证明摄像头是否已经被点亮。
目录
[1. 基础信息与依赖引入](#1. 基础信息与依赖引入)
[2. 宏定义与配置参数](#2. 宏定义与配置参数)
[3. 数据结构定义](#3. 数据结构定义)
[4. 寄存器配置表](#4. 寄存器配置表)
[5. 核心功能函数](#5. 核心功能函数)
[6. 驱动入口与框架绑定](#6. 驱动入口与框架绑定)
[(1)Power ON/OFF函数](#(1)Power ON/OFF函数)
[(6)镜像 / 倒置设置函数](#(6)镜像 / 倒置设置函数)
[第一步:从数据手册提取 3 个 "必须找到" 的核心参数](#第一步:从数据手册提取 3 个 “必须找到” 的核心参数)
[第二步:用手册参数计算 VTS(两种方式,结果一致)](#第二步:用手册参数计算 VTS(两种方式,结果一致))
一、实物硬件接线验证
在准备开始点MIPI摄像头之前,除了硬件设备相信我们应该也拿到了如下类似的文件资料,比如厂商开发板的原理图资料,根据开发板设计的自己公司的PCB主板原理图资料,摄像头模组规格书资料,包括原厂的移植参考**(原厂文档最重要)**,那么对于第一步,上述这些就已足够。

我们先来看一下二者的原理图对比,根据确认硬件部门完全是按照开发板的原理图进行设计的,保险起见还是核对一下GPIO口的连接是否一致(上面为开发板原理图,下面为PCB板原理图)虽然已经保持一致,但是重复检验心里更踏实,也是一个好习惯。


根据原厂平台的文档说明sensor 与 SoC 之间的接线逻辑主要包含两部分,一个是基础接线一个是MIPI 接口接线,在这里面的CCI接口其实也就是我们的I2C接口,我们控制摄像头的寄存器都是通过I2C进行控制的(这里SR0_I2C0_SCL和SR0_I2C0_SDA对应的GPIO口就是B00和B01)。根据原理图的注释和文档的具体说明我们能清楚的发现在这个FPC接口上,连接的是一个有两个数据通道和一个时钟通道的摄像头,也就是snr0


接下来我们再来看看摄像头模组的规格书,这里我们也看到有两组data线和一组时钟线没有问题,这里注意再核对一下前面的NO号的位置,结合上面我们自己的原理图,经过查验都是对的上的,也就是说在原理图中CH0N/P和CH2N/P是两组data线,中间的CH1N/P是时钟线(这个细节很重要,后面涉及到设备树引脚组的线序改动)。

到此位置,我们已经验证接线没有问题,这下我们就可以放心的把摄像头模组FPC线插到板子上去了。
二、dts设备树修改
外部硬件接线没有问题,这时候我们就可以看内部的接线配置了,由于按照开发板设计,所以拿到的开发环境的设备树配置不需要进行很大的改动,只需要小修小改即可。
前面提到sensor和soc芯片之间的三类接线,"基础接线","CCI接口线","CSI接口线"。需要在dts里做好配置,内核会根据dts的配置,初始化好soc的这三类pad。"基础接线"和"CCI接口线"通过sensorif节点配置,"CSI接口线"通过csi接口配置。dts配置只是决定了初始化哪些snr pad的哪些lane,没有数量限制。Comake_Pi_D1开发板通用的配置如下
初始化snr0的2lane和snr2的1lane:
csi: csi {
compatible = "sstar,csi";
io_phy_addr = <0x1f000000>;
banks = <0x153C>,<0x153D>,<0x153E>,<0x1538>,<0x153A>,<0x153B>;
atop_banks = <0x153F>;
clkgen_banks = <0x1038>;
interrupts= <GIC_SPI INT_IRQ_MIPI_CSI2 IRQ_TYPE_LEVEL_HIGH>;
status = "okay";
/* Config max lane number */
csi_sr0_lane_num = <2>;
csi_sr2_lane_num = <1>;
/* Config lane selection */
csi_sr0_lane_select = <0 1 2>;
csi_sr2_lane_select = <0 1>;
/* Config lane P/N swap */
csi_sr0_lane_pn_swap = <0 0 0>;
csi_sr2_lane_pn_swap = <0 0>;
};
sensorif: sensorif {
compatible = "sstar,sensorif";
status = "okay";
clocks = <&CLK_sr00_mclk>, <&CLK_sr01_mclk>;
/* Config sensor 0 pad mux */
snr_sr0_mipi_mode = <5>;
snr_sr0_mipi_rst_mode = <2>;
snr_sr0_mipi_pdn_mode = <0>;
snr_sr0_mipi_mclk_mode = <2>;
snr_sr0_rst_gpio = <73>;
snr_sr0_pdn_gpio = <77>;
...
/* Config sensor 2 pad mux */
snr_sr2_mipi_mode = <5>;
snr_sr2_rst_gpio = <77>;
snr_sr2_mipi_pdn_mode = <0>;
snr_sr2_mipi_mclk_mode = <2>;
/* Config mclk 37.125MHz supported */
snr_sr0_mclk_37p125 = <1>;
snr_sr2_mclk_37p125 = <1>;
/* Config CCI interface */
snr0_mipi_i2c = <0>;
snr2_mipi_i2c = <1>;
};
在这里面我们重点看一下CSI线序的选择,这里也是最容易踩坑的,在设备树的设计中CLK对应0号、data0对应1号、data1对应2号,所以这里我们需要做出调整,实际上根据原理图和模组规格书,我们需要转换一下0和1的位置才是符合我们实际线序情况的。
实例:
然后我们来看一下sensorif节点,这里我做了一点修改我将snr_sr0_mipi_mode改为1了原本是5,因为我们只有一组时钟线,而且是单sensor这样修改更贴合实际情况。经过核对rst引脚号就是73,此处pdn没用上,但我还是按照原理图改为69号了。这里需要结合官方提供的hw_checklist的ARMTmux子表进行修改
实例:
随后再看一下I2C的配置是否正确GPIO口是不是对的上,是不是用的I2C0控制器,此处设备树在pcupid-comake-pi-d1-dual-sensor-padmux.dtsi
包括具体的I2C0节点设置的是不是也符合要求,这里我将频率改低了从200改为100了,因为I2C在摄像头传感器这里只涉及到配置的读写控制,并不涉及大量的数据交互,所以将频率改低没有问题。此处设备树在pcupid.dtsi
到此为止,设备树的修改也完成了,我们可以进行驱动程序的改写了
三、移植编写摄像头驱动方法
(1)拿到数据手册和初始化序列表
目前我们逐个验证了硬件的接线以及内部soc的设备树配置,现在我们可以正式开始驱动程序的改写。由于在开发环境中没有找到该款摄像头的驱动适配,所以我们找一个里面原本就有的且相近的驱动代码,在它的基础之上进行全部替换,总之就是两个字换瓤。
在开始之前,我们要找摄像头模组厂商要一下传感器的datasheet以及初始化序列表,这个初始化序列表很重要很重要,只有摄像头厂能提供,我们必须拿到它,在这里表中我们重点关注一下是多少bit位的,分辨率是多少,帧率是多少,频率是多少,lane数是多少,我们要将其记住,这是改写驱动要重点注意的地方。(cleaned_0x02_SC200PC_1928x1088_24Minput_mipi 2lane_10bit_720Mbps_30fps)
//Preview Type:0:DVP Raw 10 bit// 1:Raw 8 bit// 2:YUV422// 3:RAW16
//Preview Type:4:RGB565// 5:Pixart SPI// 6:MIPI 10bit// 7:MIPI 12bit// 8: MTK SPI
//port 0:MIPI// 1:Parallel// 2:MTK// 3:SPI// 4:TEST// 5: HISPI// 6 : Z2P/Z4P
//I2C Mode :0:Normal 8Addr,8Data// 1:Samsung 8 Addr,8Data// 2:Micron 8 Addr,16Data
//I2C Mode :3:Stmicro 16Addr,8Data//4:Micron2 16 Addr,16Data
//Out Format :0:YCbYCr/RG_GB// 1:YCrYCb/GR_BG// 2:CbYCrY/GB_RG// 3:CrYCbY/BG_GR
//MCLK Speed :0:6M//1:8M//2:10M//3:11.4M//4:12M//5:12.5M//6:13.5M//7:15M//8:18M//9:24M
//pin :BIT0 pwdn// BIT1:reset
//avdd 0:2.8V// 1:2.5V// 2:1.8V
//dovdd 0:2.8V// 1:2.5V// 2:1.8V
//dvdd 0:1.8V// 1:1.5V// 2:1.2V
[DataBase]
DBName=Dothinkey
[Vendor]
VendorName=SmartSens
[Sensor]
SensorName=SC200PC_60fps
width=1928
height=1088
port=0
type=6
pin=3
SlaveID=0x6c
mode=3
FlagReg=0x36ff
FlagMask=0xff
FlagData=0x00
FlagReg1=0x36ff
FlagMask1=0xff
FlagData1=0x00
outformat=3
mclk=24
avdd=2.800000
dovdd=1.800000
dvdd=1.200000
Ext0=0
Ext1=0
Ext2=0
AFVCC=2.44500
VPP=0.000000
[ParaList]
0x0103,0x01,
0x301f,0x02,
0x3200,0x00,
0x3201,0x00,
0x3202,0x00,
0x3203,0x00,
0x3204,0x07,
0x3205,0x8f,
0x3206,0x04,
0x3207,0x47,
0x3208,0x07,
0x3209,0x88,
0x320a,0x04,
0x320b,0x40,
0x320c,0x07,
0x320d,0x80,
0x3210,0x00,
0x3211,0x04,
0x3212,0x00,
0x3213,0x04,
0x3253,0x60,
0x325f,0x80,
0x32d1,0x70,
0x3301,0x07,
0x3306,0x30,
0x3308,0x10,
0x330b,0x78,
0x330e,0x28,
0x331e,0x21,
0x331f,0x21,
0x3333,0x10,
0x3334,0x40,
0x3347,0x05,
0x334c,0x08,
0x335d,0x60,
0x3364,0x56,
0x3390,0x08,
0x3391,0x38,
0x3393,0x0e,
0x3394,0x10,
0x33ad,0x1c,
0x33b1,0x80,
0x33b2,0x58,
0x33b3,0x08,
0x349f,0x02,
0x34a6,0x18,
0x34a7,0x38,
0x34a8,0x07,
0x34a9,0x06,
0x3619,0x20,
0x361a,0x91,
0x3633,0x48,
0x3637,0x49,
0x3638,0xa1,
0x3660,0x80,
0x3661,0x86,
0x3662,0x8e,
0x3667,0x38,
0x3668,0x78,
0x3670,0x65,
0x3671,0x45,
0x3672,0x45,
0x3680,0x46,
0x3681,0x66,
0x3682,0x88,
0x3683,0x39,
0x3684,0x39,
0x3685,0x39,
0x36c0,0x08,
0x36c1,0x18,
0x36c8,0x18,
0x36c9,0x78,
0x36ca,0x08,
0x36cb,0x78,
0x3718,0x04,
0x3723,0x20,
0x3724,0xe1,
0x3770,0x03,
0x3771,0x03,
0x3772,0x03,
0x37c0,0x08,
0x37c1,0x78,
0x37ed,0x89,
0x37f9,0x00,
0x37fa,0x0c,
0x37fb,0xca,
0x3901,0x08,
0x3902,0xc0,
0x3903,0x40,
0x3908,0x40,
0x3909,0x01,
0x390a,0x81,
0x3929,0x18,
0x3933,0x80,
0x3934,0x01,
0x3937,0x75,
0x3939,0x0f,
0x393a,0xfe,
0x39dd,0x06,
0x3e00,0x00,
0x3e01,0x95,
0x3e02,0x80,
0x3e03,0x0b,
0x3e09,0x10,
0x3f09,0x0e,
0x4407,0x0c,
0x4509,0x1e,
0x450d,0x01,
0x450f,0x06,
0x5784,0x0c,
0x5785,0x04,
0x578d,0x40,
0x57ac,0x00,
0x57ad,0x00,
0x0100,0x01,
(2)总览分析平台原来的驱动程序
我们首先来看一下原来平台移植好的驱动程序都是怎么写的,分为哪些部分,我们都要修改哪些部分,这里附上我们将要修改的摄像头驱动程序drv_ss_sc200ai_mipi.c,我们先来拆解分析都包含些什么东西。
1. 基础信息与依赖引入
- 版权与声明:驱动开头通常包含版权信息、保密协议等(如代码中的 SigmaStar 版权声明),明确知识产权归属。
- 头文件依赖 :引入驱动开发所需的公共接口和框架头文件(如代码中的
drv_sensor_common.h、sensor_i2c_api.h),用于复用传感器通用逻辑、I2C 通信等基础功能。
2. 宏定义与配置参数
这是驱动的 "配置核心",定义摄像头硬件特性和运行参数,包括:
- 硬件接口参数 :MIPI 通道数(
SENSOR_MIPI_LANE_NUM)、I2C 地址(SENSOR_I2C_ADDR)、I2C 速率(SENSOR_I2C_SPEED)、数据格式(SENSOR_I2C_FMT)等,描述传感器与主控的物理连接方式。- 时序与帧率参数 :行周期(
Preview_line_period)、垂直总行数(vts_30fps)、最大 / 最小帧率(Preview_MAX_FPS/Preview_MIN_FPS)等,控制图像输出的时序逻辑。- 图像属性参数 :分辨率(如
1920x1080)、Bayer 格式(SENSOR_BAYERID)、数据精度(SENSOR_DATAPREC)、HDR 模式(SENSOR_HDR_MODE)等,定义输出图像的格式和特性。- 自动曝光(AE)参数 :增益范围(
SENSOR_MAX_GAIN/SENSOR_MIN_GAIN)、增益 / 快门延迟帧数(SENSOR_GAIN_DELAY_FRAME_COUNT)等,用于自动曝光控制。
3. 数据结构定义
用于存储摄像头运行状态和配置信息,便于驱动管理:
- 分辨率信息结构体 :如
sc200ai_mipi_linear(线性模式)和sc200ai_mipi_hdr(HDR 模式),存储不同模式下的输出宽高、帧率范围、裁剪参数等。- 传感器参数结构体 :如
sc200ai_params,包含时钟配置、曝光参数(垂直总行数、帧率)、寄存器缓存(增益、曝光寄存器值)、初始化状态等,用于实时维护传感器运行状态。
4. 寄存器配置表
摄像头硬件功能通过寄存器控制,驱动需定义关键寄存器操作序列:
- 初始化寄存器表 :如
Sensor_init_table_2M30fps(线性模式)和Sensor_init_table_HDR(HDR 模式),包含上电后需配置的寄存器地址、值及延迟指令,用于传感器初始化(如时钟、模式切换)。- 功能寄存器表 :如
gain_reg(增益控制)、expo_reg(曝光控制)、vts_reg(垂直时序控制)、mirror_reg(镜像 / 翻转控制)等,用于具体功能的寄存器映射。
5. 核心功能函数
实现对摄像头的具体控制逻辑,是驱动的 "业务核心":
- 电源管理 :如
pCus_poweron(上电、复位、时钟初始化)和pCus_poweroff(下电、关闭时钟),控制传感器的电源状态。- 初始化函数 :如
pCus_init_mipi2lane_linear_2M30fps(线性模式初始化)和pCus_init_mipi2lane_HDR(HDR 模式初始化),加载对应模式的寄存器表,完成传感器启动配置。- 分辨率与帧率控制 :如
pCus_SetVideoRes(切换分辨率)、pCus_SetFPS(设置帧率),通过修改时序参数(如垂直总行数)调整输出帧率。- 自动曝光与增益控制 :如
pCus_SetAEGain(设置增益)、pCus_SetAEUSecs(设置曝光时间),通过配置增益和曝光寄存器实现自动曝光调节。- 方向控制 :如
pCus_SetOrien(设置镜像 / 翻转),通过修改mirror_reg寄存器控制图像方向。- MIPI 接口配置 :如
pCus_PowerOn_InitChipRX,配置 CSI 时钟(CUS_CSI_CLK_216M)、MIPI 通道数等,确保 MIPI 总线通信正常。- 状态通知 :如
pCus_AEStatusNotify,在帧活跃时更新寄存器(如生效增益、曝光参数),保证参数实时性。
6. 驱动入口与框架绑定
通过驱动入口宏(如代码中的SENSOR_DRV_ENTRY_IMPL_BEGIN_EX(SC200ai))将上述功能函数绑定到传感器驱动框架,使驱动能被系统识别和调用,完成驱动的注册与初始化。
原版驱动程序:
/* SigmaStar trade secret */
/* Copyright (c) [2019~2020] SigmaStar Technology.
All rights reserved.
Unless otherwise stipulated in writing, any and all information contained
herein regardless in any format shall remain the sole proprietary of
SigmaStar and be kept in strict confidence
(SigmaStar Confidential Information) by the recipient.
Any unauthorized act including without limitation unauthorized disclosure,
copying, use, reproduction, sale, distribution, modification, disassembling,
reverse engineering and compiling of the contents of SigmaStar Confidential
Information is unlawful and strictly prohibited. SigmaStar hereby reserves the
rights to any and all damages, losses, costs and expenses resulting therefrom.
*/
/* Sensor Porting on Master V4
Porting owner :Jilly
Date :23/09/19
Build on :Master_V4 i6c
Verified on :not yet
Remark :NA
*/
#ifdef __cplusplus
extern "C"
{
#endif
#include <drv_sensor_common.h>
#include <sensor_i2c_api.h>
#include <drv_sensor.h>
#ifdef __cplusplus
}
#endif
SENSOR_DRV_ENTRY_IMPL_BEGIN_EX(SC200ai);
#ifndef ARRAY_SIZE
#define ARRAY_SIZE CAM_OS_ARRAY_SIZE
#endif
#define SENSOR_PAD_GROUP_SET CUS_SENSOR_PAD_GROUP_A
#define SENSOR_CHANNEL_NUM (0)
#define SENSOR_CHANNEL_MODE_LINEAR CUS_SENSOR_CHANNEL_MODE_REALTIME_NORMAL
#define SENSOR_CHANNEL_MODE_SONY CUS_SENSOR_CHANNEL_MODE_RAW_STORE_HDR
//============================================
//MIPI config begin.
#define SENSOR_MIPI_LANE_NUM (2)
//MIPI config end.
//============================================
//#undef SENSOR_DBG
#define SENSOR_DBG 0
#define SENSOR_HDR_MODE CUS_HDR_MODE_VC
//#define SENSOR_ISP_TYPE ISP_EXT //ISP_EXT, ISP_SOC
#define SENSOR_IFBUS_TYPE CUS_SENIF_BUS_MIPI //CFG //CUS_SENIF_BUS_PARL, CUS_SENIF_BUS_MIPI
//#define SENSOR_MIPI_HSYNC_MODE PACKET_HEADER_EDGE1
//#define SENSOR_MIPI_HSYNC_MODE_HDR PACKET_FOOTER_EDGE
#define SENSOR_DATAPREC CUS_DATAPRECISION_10 //CFG //CUS_DATAPRECISION_8, CUS_DATAPRECISION_10
#define SENSOR_DATAPREC_HDR CUS_DATAPRECISION_10
//#define SENSOR_DATAMODE CUS_SEN_10TO12_9000 //CFG
#define SENSOR_BAYERID CUS_BAYER_BG //CFG //CUS_BAYER_GB, CUS_BAYER_GR, CUS_BAYER_BG, CUS_BAYER_RG
#define SENSOR_BAYERID_HDR CUS_BAYER_BG//CUS_BAYER_GR
#define SENSOR_RGBIRID CUS_RGBIR_NONE
#define SENSOR_ORIT CUS_ORIT_M0F0 //CUS_ORIT_M0F0, CUS_ORIT_M1F0, CUS_ORIT_M0F1, CUS_ORIT_M1F1,
#define Preview_MCLK_SPEED CUS_CMU_CLK_27MHZ //CFG //CUS_CMU_CLK_12M, CUS_CMU_CLK_16M, CUS_CMU_CLK_24M, CUS_CMU_CLK_27M
#define Preview_MCLK_SPEED_HDR CUS_CMU_CLK_27MHZ
#define Preview_line_period 29630
#define vts_30fps 1125//29630
#define Preview_line_period_HDR 14815 // 1000000000 / 30 / 1125 / 2 = 14815
#define vts_30fps_HDR 2250
#define Preview_MAX_FPS 30 //fastest preview FPS
#define Preview_MIN_FPS 3 //slowest preview FPS
#define Preview_CROP_START_X 0 //CROP_START_X
#define Preview_CROP_START_Y 0 //CROP_START_Y
#define SENSOR_I2C_ADDR 0x60 //I2C slave address
#define SENSOR_I2C_SPEED 200000 //I2C speed, 60000~320000
#define SENSOR_I2C_LEGACY I2C_NORMAL_MODE //usally set CUS_I2C_NORMAL_MODE, if use old OVT I2C protocol=> set CUS_I2C_LEGACY_MODE
#define SENSOR_I2C_FMT I2C_FMT_A16D8 //CUS_I2C_FMT_A8D8, CUS_I2C_FMT_A8D16, CUS_I2C_FMT_A16D8, CUS_I2C_FMT_A16D16
//#define SENSOR_PWDN_POL CUS_CLK_POL_NEG // if PWDN pin High can makes sensor in power down, set CUS_CLK_POL_POS
//#define SENSOR_RST_POL CUS_CLK_POL_NEG // if RESET pin High can makes sensor in reset state, set CUS_CLK_POL_NEG
// VSYNC/HSYNC POL can be found in data sheet timing diagram,
// Notice: the initial setting may contain VSYNC/HSYNC POL inverse settings so that condition is different.
//#define SENSOR_VSYNC_POL CUS_CLK_POL_NEG // if VSYNC pin High and data bus have data, set CUS_CLK_POL_POS
//#define SENSOR_HSYNC_POL CUS_CLK_POL_NEG // if HSYNC pin High and data bus have data, set CUS_CLK_POL_POS
//#define SENSOR_PCLK_POL CUS_CLK_POL_POS // depend on sensor setting, sometimes need to try CUS_CLK_POL_POS or CUS_CLK_POL_NEG
//AE info
#define SENSOR_MAX_GAIN (53975*3175 / 100000) //sensor again 15.875 dgain=31.5
#define SENSOR_MIN_GAIN (1)
#define SENSOR_GAIN_DELAY_FRAME_COUNT (2)
#define SENSOR_SHUTTER_DELAY_FRAME_COUNT (2)
#define SENSOR_GAIN_CTRL_NUM (1)
#define SENSOR_SHUTTER_CTRL_NUM (1)
#define SENSOR_SHUTTER_CTRL_NUM_HDR (2)
////////////////////////////////////
// Image Info //
////////////////////////////////////
static struct { // LINEAR
// Modify it based on number of support resolution
enum {LINEAR_RES_1 = 0, LINEAR_RES_END}mode;
// Sensor Output Image info
struct _senout{
s32 width, height, min_fps, max_fps;
}senout;
// VIF Get Image Info
struct _sensif{
s32 crop_start_X, crop_start_y, preview_w, preview_h;
}senif;
// Show resolution string
struct _senstr{
const char* strResDesc;
}senstr;
}sc200ai_mipi_linear[] = {
{LINEAR_RES_1, {1920, 1080, 3, 30}, {0, 0, 1920, 1080}, {"1920x1080@30fps"}},};
static struct { // HDR
// Modify it based on number of support resolution
enum {HDR_RES_1 = 0, HDR_RES_END}mode;
// Sensor Output Image info
struct _hsenout{
s32 width, height, min_fps, max_fps;
}senout;
// VIF Get Image Info
struct _hsensif{
s32 crop_start_X, crop_start_y, preview_w, preview_h;
}senif;
// Show resolution string
struct _hsenstr{
const char* strResDesc;
}senstr;
}sc200ai_mipi_hdr[] = {
{HDR_RES_1, {1920, 1080, 3, 30}, {0, 0, 1920, 1080}, {"1920x1080@30fps_HDR"}}, // Modify it
};
#if defined (SENSOR_MODULE_VERSION)
#define TO_STR_NATIVE(e) #e
#define TO_STR_PROXY(m, e) m(e)
#define MACRO_TO_STRING(e) TO_STR_PROXY(TO_STR_NATIVE, e)
static char *sensor_module_version = MACRO_TO_STRING(SENSOR_MODULE_VERSION);
module_param(sensor_module_version, charp, S_IRUGO);
#endif
//static int cus_camsensor_release_handle(ss_cus_sensor *handle);
static int pCus_SetAEGain(ss_cus_sensor *handle, u32 gain);
static int pCus_SetAEUSecs(ss_cus_sensor *handle, u32 us);
static int pCus_SetFPS(ss_cus_sensor *handle, u32 fps);
static int pCus_SetOrien(ss_cus_sensor *handle, CUS_CAMSENSOR_ORIT orit);
CUS_MCLK_FREQ UseParaMclk(void);
typedef struct {
struct {
u16 pre_div0;
u16 div124;
u16 div_cnt7b;
u16 sdiv0;
u16 mipi_div0;
u16 r_divp;
u16 sdiv1;
u16 r_seld5;
u16 r_sclk_dac;
u16 sys_sel;
u16 pdac_sel;
u16 adac_sel;
u16 pre_div_sp;
u16 r_div_sp;
u16 div_cnt5b;
u16 sdiv_sp;
u16 div12_sp;
u16 mipi_lane_sel;
u16 div_dac;
} clk_tree;
struct {
u32 sclk;
u32 hts;
u32 vts;
u32 preview_fps;
u32 fps;
u32 max_short_exp;
u32 line;
} expo;
struct {
bool bVideoMode;
u16 res_idx;
CUS_CAMSENSOR_ORIT orit;
} res;
I2C_ARRAY tVts_reg[2];
I2C_ARRAY tGain_reg[4];
I2C_ARRAY tGain_reg_HDR_SEF[4];
I2C_ARRAY tExpo_reg[3];
I2C_ARRAY tExpo_reg_HDR_SEF[3];
I2C_ARRAY tMax_short_exp_reg[2];
I2C_ARRAY tMirror_reg[1];
I2C_ARRAY tTemperature_reg_1[1];
int sen_init;
int still_min_fps;
int video_min_fps;
bool orient_dirty;
CUS_CAMSENSOR_ORIT orit;
bool reg_dirty;
bool temperature_reg_1_dirty;
} sc200ai_params;
// set sensor ID address and data,
#if 0
const static I2C_ARRAY Sensor_id_table[] =
{
{0x3107, 0xCB},
{0x3108, 0x1C},
};
#endif
const static I2C_ARRAY Sensor_init_table_2M30fps[] =
{
{0x0103,0x01},
{0x0100,0x00},
{0x36e9,0x80},
{0x36f9,0x80},
{0x301f,0x03},
{0x3243,0x01},
{0x3248,0x02},
{0x3249,0x09},
{0x3253,0x08},
{0x3271,0x0a},
{0x3301,0x20},
{0x3304,0x40},
{0x3306,0x32},
{0x330b,0x88},
{0x330f,0x02},
{0x331e,0x39},
{0x3333,0x10},
{0x3621,0xe8},
{0x3622,0x16},
{0x3637,0x1b},
{0x363a,0x1f},
{0x363b,0xc6},
{0x363c,0x0e},
{0x3670,0x0a},
{0x3674,0x82},
{0x3675,0x76},
{0x3676,0x78},
{0x367c,0x48},
{0x367d,0x58},
{0x3690,0x34},
{0x3691,0x33},
{0x3692,0x44},
{0x369c,0x40},
{0x369d,0x48},
{0x3901,0x02},
{0x3904,0x04},
{0x3908,0x41},
{0x391d,0x14},
{0x391f,0x18},
{0x3e01,0x8c},
{0x3e02,0x20},
{0x3e16,0x00},
{0x3e17,0x80},
{0x3f09,0x48},
{0x5787,0x10},
{0x5788,0x06},
{0x578a,0x10},
{0x578b,0x06},
{0x5790,0x10},
{0x5791,0x10},
{0x5792,0x00},
{0x5793,0x10},
{0x5794,0x10},
{0x5795,0x00},
{0x5799,0x00},
{0x57c7,0x10},
{0x57c8,0x06},
{0x57ca,0x10},
{0x57cb,0x06},
{0x57d1,0x10},
{0x57d4,0x10},
{0x57d9,0x00},
{0x59e0,0x60},
{0x59e1,0x08},
{0x59e2,0x3f},
{0x59e3,0x18},
{0x59e4,0x18},
{0x59e5,0x3f},
{0x59e6,0x06},
{0x59e7,0x02},
{0x59e8,0x38},
{0x59e9,0x10},
{0x59ea,0x0c},
{0x59eb,0x10},
{0x59ec,0x04},
{0x59ed,0x02},
{0x59ee,0xa0},
{0x59ef,0x08},
{0x59f4,0x18},
{0x59f5,0x10},
{0x59f6,0x0c},
{0x59f7,0x10},
{0x59f8,0x06},
{0x59f9,0x02},
{0x59fa,0x18},
{0x59fb,0x10},
{0x59fc,0x0c},
{0x59fd,0x10},
{0x59fe,0x04},
{0x59ff,0x02},
{0x36e9,0x20},
{0x36f9,0x27},
{0x0100,0x01},
{0xffff,0x0a},/////delay 10ms
};
const static I2C_ARRAY Sensor_init_table_HDR[] =
{
{0x0103,0x01},
{0x0100,0x00},
{0x36e9,0x80},
{0x36f9,0x80},
{0x301f,0x02},
{0x320e,0x08},
{0x320f,0xca},
{0x3220,0x53},
{0x3243,0x01},
{0x3248,0x02},
{0x3249,0x09},
{0x3250,0x3f},
{0x3253,0x08},
{0x3271,0x0a},
{0x3301,0x06},
{0x3302,0x0c},
{0x3303,0x08},
{0x3304,0x60},
{0x3306,0x30},
{0x3308,0x10},
{0x3309,0x70},
{0x330b,0x80},
{0x330d,0x16},
{0x330e,0x1c},
{0x330f,0x02},
{0x3310,0x02},
{0x331c,0x04},
{0x331e,0x51},
{0x331f,0x61},
{0x3320,0x07},
{0x3333,0x10},
{0x334c,0x08},
{0x3356,0x09},
{0x3364,0x17},
{0x3390,0x08},
{0x3391,0x18},
{0x3392,0x38},
{0x3393,0x06},
{0x3394,0x06},
{0x3395,0x06},
{0x3396,0x08},
{0x3397,0x18},
{0x3398,0x38},
{0x3399,0x06},
{0x339a,0x0a},
{0x339b,0x10},
{0x339c,0x20},
{0x33ac,0x08},
{0x33ae,0x10},
{0x33af,0x19},
{0x3621,0xe8},
{0x3622,0x16},
{0x3630,0xa0},
{0x3637,0x36},
{0x363a,0x1f},
{0x363b,0xc6},
{0x363c,0x0e},
{0x3670,0x0a},
{0x3674,0x82},
{0x3675,0x76},
{0x3676,0x78},
{0x367c,0x48},
{0x367d,0x58},
{0x3690,0x34},
{0x3691,0x33},
{0x3692,0x44},
{0x369c,0x40},
{0x369d,0x48},
{0x36eb,0x0c},
{0x36ec,0x0c},
{0x36fd,0x14},
{0x3901,0x02},
{0x3904,0x04},
{0x3908,0x41},
{0x391f,0x10},
{0x3e00,0x01},
{0x3e01,0x06},
{0x3e02,0x00},
{0x3e04,0x10},
{0x3e05,0x60},
{0x3e06,0x00},
{0x3e07,0x80},
{0x3e08,0x03},
{0x3e09,0x40},
{0x3e10,0x00},
{0x3e11,0x80},
{0x3e12,0x03},
{0x3e13,0x40},
{0x3e16,0x00},
{0x3e17,0x80},
{0x3e23,0x00},
{0x3e24,0x88},
{0x3f09,0x48},
{0x4816,0xb1},
{0x4819,0x09},
{0x481b,0x05},
{0x481d,0x14},
{0x481f,0x04},
{0x4821,0x0a},
{0x4823,0x05},
{0x4825,0x04},
{0x4827,0x05},
{0x4829,0x08},
{0x5787,0x10},
{0x5788,0x06},
{0x578a,0x10},
{0x578b,0x06},
{0x5790,0x10},
{0x5791,0x10},
{0x5792,0x00},
{0x5793,0x10},
{0x5794,0x10},
{0x5795,0x00},
{0x5799,0x00},
{0x57c7,0x10},
{0x57c8,0x06},
{0x57ca,0x10},
{0x57cb,0x06},
{0x57d1,0x10},
{0x57d4,0x10},
{0x57d9,0x00},
{0x59e0,0x60},
{0x59e1,0x08},
{0x59e2,0x3f},
{0x59e3,0x18},
{0x59e4,0x18},
{0x59e5,0x3f},
{0x59e6,0x06},
{0x59e7,0x02},
{0x59e8,0x38},
{0x59e9,0x10},
{0x59ea,0x0c},
{0x59eb,0x10},
{0x59ec,0x04},
{0x59ed,0x02},
{0x59ee,0xa0},
{0x59ef,0x08},
{0x59f4,0x18},
{0x59f5,0x10},
{0x59f6,0x0c},
{0x59f7,0x10},
{0x59f8,0x06},
{0x59f9,0x02},
{0x59fa,0x18},
{0x59fb,0x10},
{0x59fc,0x0c},
{0x59fd,0x10},
{0x59fe,0x04},
{0x59ff,0x02},
{0x36e9,0x20},
{0x36f9,0x24},
{0x0100,0x01},
{0xffff,0x0a},/////delay 10ms
};
const static I2C_ARRAY mirror_reg[] =
{
{0x3221, 0x00}, // mirror[2:1], flip[6:5]
};
typedef struct {
short reg;
char startbit;
char stopbit;
} COLLECT_REG_SET;
const static I2C_ARRAY gain_reg[] = {
{0x3e06, 0x00},
{0x3e07, 0x80},
{0x3e08, 0x03},
{0x3e09, 0x40}, //low bit, 0x10 - 0x3e0, step 1/64
};
const static I2C_ARRAY gain_reg_HDR_SEF[] = {
{0x3e10, 0x00},
{0x3e11, 0x80},
{0x3e12, 0x03},
{0x3e13, 0x40}, //low bit, 0x10 - 0x3e0, step 1/16
};
const static I2C_ARRAY expo_reg[] = { // max expo line vts*2-6!
{0x3e00, 0x00},//expo [20:17]
{0x3e01, 0x8c}, // expo[16:8]
{0x3e02, 0x40}, // expo[7:0], [3:0] fraction of line
};
const static I2C_ARRAY expo_reg_HDR_SEF[] = {
{0x3e22, 0x00}, // expo[3:0]
{0x3e04, 0x21}, // expo[7:0]
{0x3e05, 0x00}, // expo[7:4]
};
const static I2C_ARRAY vts_reg[] = {
{0x320e, 0x04},
{0x320f, 0x65}
};
const static I2C_ARRAY max_short_exp_reg[] = {
{0x3e23, 0x00},
{0x3e24, 0x88}
};
const static I2C_ARRAY temperature_reg_1[] = {
{0x5799, 0x00},
};
/////////// function definition ///////////////////
#if SENSOR_DBG == 1
//#define SENSOR_DMSG(args...) LOGD(args)
//#define SENSOR_DMSG(args...) LOGE(args)
#define SENSOR_DMSG(args...) SENSOR_DMSG(args)
#elif SENSOR_DBG == 0
//#define SENSOR_DMSG(args...)
#endif
#undef SENSOR_NAME
#define SENSOR_NAME sc200ai
#define SensorReg_Read(_reg,_data) (handle->i2c_bus->i2c_rx(handle->i2c_bus, &(handle->i2c_cfg),_reg,_data))
#define SensorReg_Write(_reg,_data) (handle->i2c_bus->i2c_tx(handle->i2c_bus, &(handle->i2c_cfg),_reg,_data))
#define SensorRegArrayW(_reg,_len) (handle->i2c_bus->i2c_array_tx(handle->i2c_bus, &(handle->i2c_cfg),(_reg),(_len)))
#define SensorRegArrayR(_reg,_len) (handle->i2c_bus->i2c_array_rx(handle->i2c_bus, &(handle->i2c_cfg),(_reg),(_len)))
static void pCus_PowerOn_InitChipRX(ss_cus_sensor *handle, u32 idx)
{
ISensorIfAPI *sensor_if = handle->sensor_if_api;
sensor_if->SetIOPad(idx, handle->sif_bus, handle->interface_attr.attr_mipi.mipi_lane_num);
sensor_if->SetCSI_Clk(idx, CUS_CSI_CLK_216M);
sensor_if->SetCSI_Lane(idx, handle->interface_attr.attr_mipi.mipi_lane_num, 1);
sensor_if->SetCSI_LongPacketType(idx, 0, 0x1C00, 0);
if (handle->interface_attr.attr_mipi.mipi_hdr_mode != CUS_HDR_MODE_NONE) {
sensor_if->SetCSI_hdr_mode(idx, handle->interface_attr.attr_mipi.mipi_hdr_mode, 2);
}
}
static int pCus_sensor_GetAEInfo(ss_cus_sensor *handle, CUS_SENSOR_AE_INFO_t *info)
{
info->u8AEGainDelay = handle->sensor_ae_info_cfg.u8AEGainDelay;
info->u8AEShutterDelay = handle->sensor_ae_info_cfg.u8AEShutterDelay;
info->u8AEGainCtrlNum = handle->sensor_ae_info_cfg.u8AEGainCtrlNum;
info->u8AEShutterCtrlNum = handle->sensor_ae_info_cfg.u8AEShutterCtrlNum;
info->u32AEGain_min = handle->sensor_ae_info_cfg.u32AEGain_min;
info->u32AEGain_max = handle->sensor_ae_info_cfg.u32AEGain_max;
if(handle->interface_attr.attr_mipi.mipi_hdr_mode == CUS_HDR_MODE_NONE)
{
info->u32AEShutter_min = Preview_line_period;
info->u32AEShutter_step = Preview_line_period;
info->u32AEShutter_max = handle->sensor_ae_info_cfg.u32AEShutter_max;
}
else
{
info->u32AEShutter_min = Preview_line_period_HDR*2;
info->u32AEShutter_step = Preview_line_period_HDR*2;
info->u32AEShutter_max = handle->sensor_ae_info_cfg.u32AEShutter_max;
}
return SUCCESS;
}
/////////////////// sensor hardware dependent //////////////
static int pCus_poweron(ss_cus_sensor *handle, u32 idx)
{
ISensorIfAPI *sensor_if = handle->sensor_if_api;
SENSOR_DMSG("[%s] ", __FUNCTION__);
//ISP_config_io(handle);
SENSOR_DMSG("[%s] reset low\n", __FUNCTION__);
sensor_if->Reset(idx, CUS_CLK_POL_NEG);
SENSOR_USLEEP(1000);
SENSOR_DMSG("[%s] power low\n", __FUNCTION__);
sensor_if->PowerOff(idx, CUS_CLK_POL_NEG);
SENSOR_USLEEP(1000);
//Sensor power on sequence
//Configuration Chip RX
pCus_PowerOn_InitChipRX(handle, idx);
sensor_if->MCLK(idx, 1, handle->mclk);
// power -> high, reset -> high
SENSOR_DMSG("[%s] power high\n", __FUNCTION__);
sensor_if->PowerOff(idx, CUS_CLK_POL_POS);
SENSOR_USLEEP(1000);
SENSOR_DMSG("[%s] reset high\n", __FUNCTION__);
sensor_if->Reset(idx, CUS_CLK_POL_NEG);
SENSOR_USLEEP(5000);
return SUCCESS;
}
static int pCus_poweroff(ss_cus_sensor *handle, u32 idx)
{
// power/reset low
ISensorIfAPI *sensor_if = handle->sensor_if_api;
sc200ai_params *params = (sc200ai_params *)handle->private_data;
SENSOR_DMSG("[%s] power low\n", __FUNCTION__);
sensor_if->PowerOff(idx, CUS_CLK_POL_NEG);
//handle->i2c_bus->i2c_close(handle->i2c_bus);
CamOsMsSleep(1);
//Set_csi_if(0, 0);
sensor_if->SetCSI_Clk(idx, CUS_CSI_CLK_DISABLE);
if (handle->interface_attr.attr_mipi.mipi_hdr_mode != CUS_HDR_MODE_NONE) {
sensor_if->SetCSI_hdr_mode(idx, handle->interface_attr.attr_mipi.mipi_hdr_mode, 0);
}
sensor_if->MCLK(idx, 0, handle->mclk);
params->orit = CUS_ORIT_M0F0;
return SUCCESS;
}
/////////////////// image function /////////////////////////
//Get and check sensor ID
//if i2c error or sensor id does not match then return FAIL
#if 0
static int pCus_GetSensorID(ss_cus_sensor *handle, u32 *id)
{
int i,n;
int table_length= ARRAY_SIZE(Sensor_id_table);
I2C_ARRAY id_from_sensor[ARRAY_SIZE(Sensor_id_table)];
SENSOR_DMSG("\n\n[%s]", __FUNCTION__);
for(n=0;n<table_length;++n)
{
id_from_sensor[n].reg = Sensor_id_table[n].reg;
id_from_sensor[n].data = 0;
}
*id =0;
if(table_length>8) table_length=8;
SENSOR_DMSG("\n\n[%s]", __FUNCTION__);
for(n=0;n<4;++n) //retry , until I2C success
{
if(n>2) return FAIL;
if( SensorRegArrayR((I2C_ARRAY*)id_from_sensor,table_length) == SUCCESS) //read sensor ID from I2C
break;
else
continue;
}
//convert sensor id to u32 format
for(i=0;i<table_length;++i)
{
if( id_from_sensor[i].data != Sensor_id_table[i].data )
return FAIL;
*id = ((*id)+ id_from_sensor[i].data)<<8;
}
*id >>= 8;
SENSOR_DMSG("[%s]sc200ai Read sensor id, get 0x%x Success\n", __FUNCTION__, (int)*id);
return SUCCESS;
}
#endif
static int sc200ai_SetPatternMode(ss_cus_sensor *handle,u32 mode)
{
SENSOR_DMSG("\n\n[%s], mode=%d \n", __FUNCTION__, mode);
return SUCCESS;
}
static int pCus_SetFPS(ss_cus_sensor *handle, u32 fps);
//static int pCus_SetAEGain_cal(ss_cus_sensor *handle, u32 gain);
static int pCus_AEStatusNotify(ss_cus_sensor *handle, u32 idx, CUS_CAMSENSOR_AE_STATUS_NOTIFY status);
static int pCus_init_mipi2lane_linear_2M30fps(ss_cus_sensor *handle)
{
sc200ai_params *params = (sc200ai_params *)handle->private_data;
//SENSOR_DMSG("\n\n[%s]", __FUNCTION__);
int i,cnt;
//ISensorIfAPI *sensor_if = (handle->sensor_if_api);
for(i=0;i< ARRAY_SIZE(Sensor_init_table_2M30fps);i++)
{
if(Sensor_init_table_2M30fps[i].reg==0xffff)
{
SENSOR_MSLEEP(Sensor_init_table_2M30fps[i].data);
}
else
{
cnt = 0;
while(SensorReg_Write(Sensor_init_table_2M30fps[i].reg, Sensor_init_table_2M30fps[i].data) != SUCCESS)
{
cnt++;
SENSOR_DMSG("Sensor_init_table -> Retry %d...\n",cnt);
if(cnt>=10)
{
SENSOR_DMSG("[%s:%d]Sensor init fail!!\n", __FUNCTION__, __LINE__);
return FAIL;
}
SENSOR_MSLEEP(10);
}
}
}
pCus_SetOrien(handle, params->orit);
params->tVts_reg[0].data = (params->expo.vts >> 8) & 0x00ff;
params->tVts_reg[1].data = (params->expo.vts >> 0) & 0x00ff;
return SUCCESS;
}
static int pCus_init_mipi2lane_HDR(ss_cus_sensor *handle)
{
sc200ai_params *params = (sc200ai_params *)handle->private_data;
int i,cnt=0;
for(i=0;i< ARRAY_SIZE(Sensor_init_table_HDR);i++)
{
if(Sensor_init_table_HDR[i].reg==0xffff)
{
SENSOR_MSLEEP(Sensor_init_table_HDR[i].data);
}
else
{
cnt = 0;
SENSOR_DMSG("reg = %x, data = %x\n", Sensor_init_table_HDR[i].reg, Sensor_init_table_HDR[i].data);
while(SensorReg_Write(Sensor_init_table_HDR[i].reg,Sensor_init_table_HDR[i].data) != SUCCESS)
{
cnt++;
SENSOR_DMSG("Sensor_init_table_HDR -> Retry %d...\n",cnt);
if(cnt>=10)
{
//printf("[%s:%d]Sensor init fail!!\n", __FUNCTION__, __LINE__);
return FAIL;
}
SENSOR_MSLEEP(10);
}
}
}
pCus_SetOrien(handle, params->orit);
params->tVts_reg[0].data = (params->expo.vts >> 8) & 0x00ff;
params->tVts_reg[1].data = (params->expo.vts >> 0) & 0x00ff;
return SUCCESS;
}
static int pCus_GetVideoResNum( ss_cus_sensor *handle, u32 *ulres_num)
{
*ulres_num = handle->video_res_supported.num_res;
return SUCCESS;
}
static int pCus_GetVideoRes(ss_cus_sensor *handle, u32 res_idx, cus_camsensor_res **res)
{
u32 num_res = handle->video_res_supported.num_res;
if (res_idx >= num_res) {
return FAIL;
}
*res = &handle->video_res_supported.res[res_idx];
return SUCCESS;
}
static int pCus_GetCurVideoRes(ss_cus_sensor *handle, u32 *cur_idx, cus_camsensor_res **res)
{
u32 num_res = handle->video_res_supported.num_res;
*cur_idx = handle->video_res_supported.ulcur_res;
if (*cur_idx >= num_res) {
return FAIL;
}
*res = &handle->video_res_supported.res[*cur_idx];
return SUCCESS;
}
static int pCus_SetVideoRes(ss_cus_sensor *handle, u32 res_idx)
{
u32 num_res = handle->video_res_supported.num_res;
sc200ai_params *params = (sc200ai_params *)handle->private_data;
if (res_idx >= num_res) {
return FAIL;
}
switch (res_idx) {
case 0: //"1920x1080@30fps"
handle->video_res_supported.ulcur_res = 0;
handle->pCus_sensor_init = pCus_init_mipi2lane_linear_2M30fps;
params->expo.vts = vts_30fps;
params->expo.fps = 30;
break;
default:
break;
}
return SUCCESS;
}
static int pCus_SetVideoRes_HDR(ss_cus_sensor *handle, u32 res_idx)
{
u32 num_res = handle->video_res_supported.num_res;
sc200ai_params *params = (sc200ai_params *)handle->private_data;
if (res_idx >= num_res) {
return FAIL;
}
switch (res_idx) {
case 0:
handle->video_res_supported.ulcur_res = 0;
handle->pCus_sensor_init = pCus_init_mipi2lane_HDR;
params->expo.vts = vts_30fps_HDR;
params->expo.fps = 30;
params->expo.max_short_exp=135;
break;
default:
break;
}
return SUCCESS;
}
static int pCus_GetOrien(ss_cus_sensor *handle, CUS_CAMSENSOR_ORIT *orit) {
char sen_data;
sc200ai_params *params = (sc200ai_params *)handle->private_data;
sen_data = params->tMirror_reg[0].data;
SENSOR_DMSG("mirror:%x\r\n", sen_data);
switch(sen_data) {
case 0x00:
*orit = CUS_ORIT_M0F0;
break;
case 0x06:
*orit = CUS_ORIT_M1F0;
break;
case 0x60:
*orit = CUS_ORIT_M0F1;
break;
case 0x66:
*orit = CUS_ORIT_M1F1;
break;
}
return SUCCESS;
}
static int pCus_SetOrien(ss_cus_sensor *handle, CUS_CAMSENSOR_ORIT orit)
{
sc200ai_params *params = (sc200ai_params *)handle->private_data;
switch(orit) {
case CUS_ORIT_M0F0:
params->tMirror_reg[0].data = 0;
params->orient_dirty = true;
break;
case CUS_ORIT_M1F0:
params->tMirror_reg[0].data = 6;
params->orient_dirty = true;
break;
case CUS_ORIT_M0F1:
params->tMirror_reg[0].data = 0x60;
params->orient_dirty = true;
break;
case CUS_ORIT_M1F1:
params->tMirror_reg[0].data = 0x66;
params->orient_dirty = true;
break;
default :
break;
}
params->orit = orit;
return SUCCESS;
}
static int pCus_GetFPS(ss_cus_sensor *handle)
{
sc200ai_params *params = (sc200ai_params *)handle->private_data;
u32 max_fps = handle->video_res_supported.res[handle->video_res_supported.ulcur_res].u16max_fps;
u32 tVts = (params->tVts_reg[0].data << 8) | (params->tVts_reg[1].data << 0);
if (params->expo.fps >= 1000)
params->expo.preview_fps = (vts_30fps*max_fps*1000)/tVts;
else
params->expo.preview_fps = (vts_30fps*max_fps)/tVts;
return params->expo.preview_fps;
}
static int pCus_SetFPS(ss_cus_sensor *handle, u32 fps)
{
u32 vts=0;
sc200ai_params *params = (sc200ai_params *)handle->private_data;
u32 max_fps = handle->video_res_supported.res[handle->video_res_supported.ulcur_res].u16max_fps;
u32 min_fps = handle->video_res_supported.res[handle->video_res_supported.ulcur_res].u16min_fps;
if(fps>=min_fps && fps <= max_fps){
params->expo.fps = fps;
params->expo.vts= (vts_30fps*max_fps)/fps;
}else if((fps >= (min_fps*1000)) && (fps <= (max_fps*1000))){
params->expo.fps = fps;
params->expo.vts= (vts_30fps*(max_fps*1000))/fps;
}else{
SENSOR_DMSG("[%s] FPS %d out of range.\n",__FUNCTION__,fps);
return FAIL;
}
if(params->expo.line > 2* (params->expo.vts) -10){
vts = (params->expo.line + 11)/2;
}else{
vts = params->expo.vts;
}
params->tVts_reg[0].data = (vts >> 8) & 0x00ff;
params->tVts_reg[1].data = (vts >> 0) & 0x00ff;
params->reg_dirty = true;
return SUCCESS;
}
static int pCus_SetFPS_HDR_SEF(ss_cus_sensor *handle, u32 fps)
{
sc200ai_params *params = (sc200ai_params *)handle->private_data;
u32 max_fps = handle->video_res_supported.res[handle->video_res_supported.ulcur_res].u16max_fps;
u32 min_fps = handle->video_res_supported.res[handle->video_res_supported.ulcur_res].u16min_fps;
if(fps>=min_fps && fps <= max_fps){
params->expo.fps = fps;
params->expo.vts= (vts_30fps_HDR*max_fps)/fps;
}else if((fps >= (min_fps*1000)) && (fps <= (max_fps*1000))){
params->expo.fps = fps;
params->expo.vts= (vts_30fps_HDR*(max_fps*1000))/fps;
}else{
SENSOR_DMSG("[%s] FPS %d out of range.\n",__FUNCTION__,fps);
return FAIL;
}
params->expo.max_short_exp = (((params->expo.vts)/17 - 1)>>1) << 1;
params->tMax_short_exp_reg[0].data = (params->expo.max_short_exp >> 8) & 0x00ff;
params->tMax_short_exp_reg[1].data = (params->expo.max_short_exp >> 0) & 0x00ff;
params->tVts_reg[0].data = (params->expo.vts >> 8) & 0x00ff;
params->tVts_reg[1].data = (params->expo.vts >> 0) & 0x00ff;
params->reg_dirty = true;
return SUCCESS;
}
static int pCus_SetFPS_hdr_lef(ss_cus_sensor *handle, u32 fps)
{
sc200ai_params *params = (sc200ai_params *)handle->private_data;
u32 max_fps = handle->video_res_supported.res[handle->video_res_supported.ulcur_res].u16max_fps;
u32 min_fps = handle->video_res_supported.res[handle->video_res_supported.ulcur_res].u16min_fps;
if(fps>=min_fps && fps <= max_fps){
params->expo.fps = fps;
params->expo.vts= (vts_30fps_HDR*max_fps)/fps;
}else if((fps >= (min_fps*1000)) && (fps <= (max_fps*1000))){
params->expo.fps = fps;
params->expo.vts= (vts_30fps_HDR*(max_fps*1000))/fps;
}else{
SENSOR_DMSG("[%s] FPS %d out of range.\n",__FUNCTION__,fps);
return FAIL;
}
params->expo.max_short_exp = (((params->expo.vts)/17 - 1)>>1) << 1;
params->tMax_short_exp_reg[0].data = (params->expo.max_short_exp >> 8) & 0x00ff;
params->tMax_short_exp_reg[1].data = (params->expo.max_short_exp >> 0) & 0x00ff;
params->tVts_reg[0].data = (params->expo.vts >> 8) & 0x00ff;
params->tVts_reg[1].data = (params->expo.vts >> 0) & 0x00ff;
params->reg_dirty = true;
return SUCCESS;
}
///////////////////////////////////////////////////////////////////////
// auto exposure
///////////////////////////////////////////////////////////////////////
// unit: micro seconds
//AE status notification
static int pCus_AEStatusNotify(ss_cus_sensor *handle, u32 idx, CUS_CAMSENSOR_AE_STATUS_NOTIFY status){
sc200ai_params *params = (sc200ai_params *)handle->private_data;
switch(status)
{
case CUS_FRAME_INACTIVE:
break;
case CUS_FRAME_ACTIVE:
if(params->orient_dirty)
{
SensorRegArrayW((I2C_ARRAY*)params->tMirror_reg, ARRAY_SIZE(mirror_reg));
params->orient_dirty = false;
}
if(params->reg_dirty)
{
SensorRegArrayW((I2C_ARRAY*)params->tExpo_reg, ARRAY_SIZE(expo_reg));
SensorRegArrayW((I2C_ARRAY*)params->tGain_reg, ARRAY_SIZE(gain_reg));
SensorRegArrayW((I2C_ARRAY*)params->tVts_reg, ARRAY_SIZE(vts_reg));
params->reg_dirty = false;
}
if(params->temperature_reg_1_dirty) {
SensorRegArrayW((I2C_ARRAY*)params->tTemperature_reg_1, ARRAY_SIZE(temperature_reg_1));
params->temperature_reg_1_dirty = false;
}
break;
default :
break;
}
return SUCCESS;
}
static int pCus_AEStatusNotifyHDR_LEF(ss_cus_sensor *handle, u32 idx, CUS_CAMSENSOR_AE_STATUS_NOTIFY status)
{
switch(status)
{
case CUS_FRAME_INACTIVE:
break;
case CUS_FRAME_ACTIVE:
break;
default :
break;
}
return SUCCESS;
}
static int pCus_AEStatusNotifyHDR_SEF(ss_cus_sensor *handle, u32 idx, CUS_CAMSENSOR_AE_STATUS_NOTIFY status)
{
sc200ai_params *params = (sc200ai_params *)handle->private_data;
switch(status)
{
case CUS_FRAME_INACTIVE:
break;
case CUS_FRAME_ACTIVE:
if(params->orient_dirty)
{
SensorRegArrayW((I2C_ARRAY*)params->tMirror_reg, ARRAY_SIZE(mirror_reg));
params->orient_dirty = false;
}
if(params->reg_dirty)
{
SensorRegArrayW((I2C_ARRAY*)params->tExpo_reg, ARRAY_SIZE(expo_reg));
SensorRegArrayW((I2C_ARRAY*)params->tGain_reg, ARRAY_SIZE(gain_reg));
SensorRegArrayW((I2C_ARRAY*)params->tExpo_reg_HDR_SEF, ARRAY_SIZE(expo_reg_HDR_SEF));
SensorRegArrayW((I2C_ARRAY*)params->tGain_reg_HDR_SEF, ARRAY_SIZE(gain_reg_HDR_SEF));
SensorRegArrayW((I2C_ARRAY*)params->tVts_reg, ARRAY_SIZE(vts_reg));
SensorRegArrayW((I2C_ARRAY*)params->tMax_short_exp_reg, ARRAY_SIZE(max_short_exp_reg));
params->reg_dirty = false;
}
if(params->temperature_reg_1_dirty) {
SensorRegArrayW((I2C_ARRAY*)params->tTemperature_reg_1, ARRAY_SIZE(temperature_reg_1));
params->temperature_reg_1_dirty = false;
}
break;
default :
break;
}
return SUCCESS;
}
static int pCus_SetAEUSecsHDR_LEF(ss_cus_sensor *handle, u32 us)
{
int i;
u32 half_lines = 0,dou_lines = 0;
sc200ai_params *params = (sc200ai_params *)handle->private_data;
I2C_ARRAY expo_reg_temp[] = { // max expo line vts-4!
{0x3e00, 0x00},//expo [20:17]
{0x3e01, 0x00}, // expo[16:8]
{0x3e02, 0x10}, // expo[7:0], [3:0] fraction of line
};
memcpy(expo_reg_temp, params->tExpo_reg, sizeof(expo_reg));
dou_lines = (1000*us)/(Preview_line_period_HDR*2); // Preview_line_period in ns
half_lines = 4*dou_lines;
if(half_lines<=4) half_lines=4;
if (half_lines > 2 * (params->expo.vts-params->expo.max_short_exp-10)) {
half_lines = 2 * (params->expo.vts-params->expo.max_short_exp-10);
}
// SENSOR_DMSG("[%s] us %ld, half_lines %ld, vts %ld\n", __FUNCTION__, us, half_lines, params->expo.vts);
half_lines = half_lines<<4;
params->tExpo_reg[0].data = (half_lines>>16) & 0x0f;
params->tExpo_reg[1].data = (half_lines>>8) & 0xff;
params->tExpo_reg[2].data = (half_lines>>0) & 0xf0;
for (i = 0; i < ARRAY_SIZE(expo_reg); i++)
{
if (params->tExpo_reg[i].data != expo_reg_temp[i].data)
{
params->reg_dirty = true;
break;
}
}
return SUCCESS;
}
static int pCus_GetAEUSecsHDR_SEF(ss_cus_sensor *handle, u32 *us) {
int rc=0;
u32 lines = 0;
sc200ai_params *params = (sc200ai_params *)handle->private_data;
lines |= (u32)(params->tExpo_reg_HDR_SEF[0].data&0x0f)<<16;
lines |= (u32)(params->tExpo_reg_HDR_SEF[1].data&0xff)<<8;
lines |= (u32)(params->tExpo_reg_HDR_SEF[2].data&0xf0)<<0;
lines >>= 4;
*us = (lines*Preview_line_period_HDR)/1000/2; //return us
SENSOR_DMSG("[%s] sensor expo lines/us %d, %dus\n", __FUNCTION__, lines, *us);
return rc;
}
static int pCus_SetAEUSecsHDR_SEF(ss_cus_sensor *handle, u32 us)
{
int i;
u32 half_lines = 0,dou_lines = 0;
sc200ai_params *params = (sc200ai_params *)handle->private_data;
I2C_ARRAY expo_reg_temp[] = {
{0x3e22, 0x00}, // expo[3:0]
{0x3e04, 0x21}, // expo[7:0]
{0x3e05, 0x00}, // expo[7:4]
};
memcpy(expo_reg_temp, params->tExpo_reg_HDR_SEF, sizeof(expo_reg_HDR_SEF));
dou_lines = (1000*us)/(Preview_line_period_HDR*2); // Preview_line_period in ns
half_lines = 4*dou_lines;
if(half_lines<=4) half_lines=4;
if (half_lines > 2 * (params->expo.max_short_exp-10)) {
half_lines = 2 * (params->expo.max_short_exp-10);
}
half_lines = half_lines<<4;
params->tExpo_reg_HDR_SEF[0].data = (half_lines>>16) & 0x0f;
params->tExpo_reg_HDR_SEF[1].data = (half_lines>>8) & 0xff;
params->tExpo_reg_HDR_SEF[2].data = (half_lines>>0) & 0xf0;
for (i = 0; i < ARRAY_SIZE(expo_reg_HDR_SEF); i++)
{
if (params->tExpo_reg_HDR_SEF[i].data != expo_reg_temp[i].data)
{
params->reg_dirty = true;
break;
}
}
return SUCCESS;
}
static int pCus_GetAEUSecs(ss_cus_sensor *handle, u32 *us) {
int rc=0;
u32 lines = 0;
sc200ai_params *params = (sc200ai_params *)handle->private_data;
lines |= (u32)(params->tExpo_reg[0].data&0x0f)<<16;
lines |= (u32)(params->tExpo_reg[1].data&0xff)<<8;
lines |= (u32)(params->tExpo_reg[2].data&0xf0)<<0;
lines >>= 4;
*us = (lines*Preview_line_period)/1000/2; //return us
SENSOR_DMSG("[%s] sensor expo lines/us %d, %dus\n", __FUNCTION__, lines, *us);
return rc;
}
static int pCus_SetAEUSecs(ss_cus_sensor *handle, u32 us) {
int i;
u32 half_lines = 0,vts = 0;
sc200ai_params *params = (sc200ai_params *)handle->private_data;
I2C_ARRAY expo_reg_temp[] = { // max expo line vts-4!
{0x3e00, 0x00},//expo [20:17]
{0x3e01, 0x00}, // expo[16:8]
{0x3e02, 0x10}, // expo[7:0], [3:0] fraction of line
};
memcpy(expo_reg_temp, params->tExpo_reg, sizeof(expo_reg));
half_lines = (1000*us*2)/Preview_line_period; // Preview_line_period in ns
if(half_lines <= 1) half_lines=1;
if (half_lines > 2 * (params->expo.vts)-10) {
vts = (half_lines+11)/2;
}
else
vts=params->expo.vts;
params->expo.line = half_lines;
SENSOR_DMSG("[%s] us %ld, half_lines %ld, vts %ld\n", __FUNCTION__, us, half_lines, params->expo.vts);
half_lines = half_lines<<4;
params->tExpo_reg[0].data = (half_lines>>16) & 0x0f;
params->tExpo_reg[1].data = (half_lines>>8) & 0xff;
params->tExpo_reg[2].data = (half_lines>>0) & 0xf0;
params->tVts_reg[0].data = (vts >> 8) & 0x00ff;
params->tVts_reg[1].data = (vts >> 0) & 0x00ff;
for (i = 0; i < ARRAY_SIZE(expo_reg); i++)
{
if (params->tExpo_reg[i].data != expo_reg_temp[i].data)
{
params->reg_dirty = true;
break;
}
}
return SUCCESS;
}
// Gain: 1x = 1024
static int pCus_GetAEGain(ss_cus_sensor *handle, u32* gain) {
int rc = 0;
return rc;
}
#if 0
static int pCus_SetAEGain_cal(ss_cus_sensor *handle, u32 gain) {
return SUCCESS;
}
#endif
static int pCus_SetAEGain(ss_cus_sensor *handle, u32 gain) {
sc200ai_params *params = (sc200ai_params *)handle->private_data;
u8 i=0 , Coarse_gain = 1,DIG_gain=1;
u32 Dcg_gainx100 = 100, ANA_Fine_gainx64 = 1;
u8 Coarse_gain_reg = 0,DIG_gain_reg=0, ANA_Fine_gain_reg= 0x20,DIG_Fine_gain_reg=0x80;
I2C_ARRAY gain_reg_temp[] = {
{0x3e06, 0x00},
{0x3e07, 0x00|0x80},
{0x3e08, 0x00|0x03},
{0x3e09, 0x40},
};
I2C_ARRAY temperature_reg_1_temp[] ={
{0x5799, 0x00},
};
memcpy(gain_reg_temp, params->tGain_reg, sizeof(gain_reg));
memcpy(temperature_reg_1_temp, params->tTemperature_reg_1, sizeof(temperature_reg_1_temp));
if (gain <= 1024) {
gain = 1024;
} else if (gain > SENSOR_MAX_GAIN*1024) {
gain = SENSOR_MAX_GAIN*1024;
}
if (gain < 2 * 1024) // start again
{
Dcg_gainx100 = 100; Coarse_gain = 1; DIG_gain=1;
Coarse_gain_reg = 0x03; DIG_gain_reg=0x0; DIG_Fine_gain_reg=0x80;
}
else if (gain <= 3456)
{
Dcg_gainx100 = 100; Coarse_gain = 2; DIG_gain=1;
Coarse_gain_reg = 0x07; DIG_gain_reg=0x0; DIG_Fine_gain_reg=0x80;
}
else if (gain <= 6908)
{
Dcg_gainx100 = 340; Coarse_gain = 1; DIG_gain=1;
Coarse_gain_reg = 0x23; DIG_gain_reg=0x0; DIG_Fine_gain_reg=0x80;
}
else if (gain <= 13817)
{
Dcg_gainx100 = 340; Coarse_gain = 2; DIG_gain=1;
Coarse_gain_reg = 0x27; DIG_gain_reg=0x0; DIG_Fine_gain_reg=0x80;
}
else if (gain <= 27635)
{
Dcg_gainx100 = 340; Coarse_gain = 4; DIG_gain=1;
Coarse_gain_reg = 0x2f; DIG_gain_reg=0x0; DIG_Fine_gain_reg=0x80;
}
else if (gain <= 55270) // end again
{
Dcg_gainx100 = 340; Coarse_gain = 8; DIG_gain=1;
Coarse_gain_reg = 0x3f; DIG_gain_reg=0x0; DIG_Fine_gain_reg=0x80;
}
#if 1
else if (gain < 55270 * 2) // start dgain
{
Dcg_gainx100 = 340; Coarse_gain = 8; DIG_gain=1; ANA_Fine_gainx64=127;
Coarse_gain_reg = 0x3f; DIG_gain_reg=0x0; ANA_Fine_gain_reg=0x7f;
}
else if (gain < 55270 * 4)
{
Dcg_gainx100 = 340; Coarse_gain = 8; DIG_gain=2; ANA_Fine_gainx64=127;
Coarse_gain_reg = 0x3f; DIG_gain_reg=0x1; ANA_Fine_gain_reg=0x7f;
}
else if (gain < 55270 * 8)
{
Dcg_gainx100 = 340; Coarse_gain = 8; DIG_gain=4; ANA_Fine_gainx64=127;
Coarse_gain_reg = 0x3f; DIG_gain_reg=0x3; ANA_Fine_gain_reg=0x7f;
}
else if (gain < 55270 * 16)
{
Dcg_gainx100 = 340; Coarse_gain = 8; DIG_gain=8; ANA_Fine_gainx64=127;
Coarse_gain_reg = 0x3f; DIG_gain_reg=0x7; ANA_Fine_gain_reg=0x7f;
}
else if (gain <= 1754822)
{
Dcg_gainx100 = 340; Coarse_gain = 8; DIG_gain=16; ANA_Fine_gainx64=127;
Coarse_gain_reg = 0x3f; DIG_gain_reg=0xF; ANA_Fine_gain_reg=0x7f;
}
#endif
if(gain < 3456)
{
ANA_Fine_gain_reg = (u8)(100 * gain / (Dcg_gainx100 * Coarse_gain) / 16);
}else if(gain == 3456) // || gain == 3456)
{
ANA_Fine_gain_reg = 0x6C;
}
else if(gain < 55270)
{
ANA_Fine_gain_reg = (u8)(100 * gain / (Dcg_gainx100 * Coarse_gain) / 16);
}else{
DIG_Fine_gain_reg = (u8)(800 * gain /(Dcg_gainx100 * Coarse_gain * DIG_gain) / ANA_Fine_gainx64);
}
params->tGain_reg[3].data = ANA_Fine_gain_reg; // 0x3e09
params->tGain_reg[2].data = Coarse_gain_reg; // 0x3e08
params->tGain_reg[1].data = DIG_Fine_gain_reg; // 0x3e07
params->tGain_reg[0].data = DIG_gain_reg & 0xF; // 0x3e06
for (i = 0; i < ARRAY_SIZE(params->tGain_reg); i++)
{
if (params->tGain_reg[i].data != gain_reg_temp[i].data)
{
params->reg_dirty = true;
break;
}
}
// highTemp dpc
if (gain >= 30 * 1024) {
params->tTemperature_reg_1[0].data = 0x07;
} else if (gain <= 20 * 1024) {
params->tTemperature_reg_1[0].data = 0x00;
}
for (i = 0; i < ARRAY_SIZE(temperature_reg_1_temp); i++) {
if (params->tTemperature_reg_1[i].data != temperature_reg_1_temp[i].data) {
params->temperature_reg_1_dirty = true;
break;
}
}
return SUCCESS;
}
static int pCus_SetAEGainHDR_SEF(ss_cus_sensor *handle, u32 gain) {
sc200ai_params *params = (sc200ai_params *)handle->private_data;
u8 i=0 , Coarse_gain = 1,DIG_gain=1;
u32 Dcg_gainx100 = 1, ANA_Fine_gainx64 = 1;
u8 Coarse_gain_reg = 0,DIG_gain_reg=0, ANA_Fine_gain_reg= 0x20,DIG_Fine_gain_reg=0x80;
I2C_ARRAY gain_reg_temp[] = {
{0x3e10, 0x00},
{0x3e11, 0x80},
{0x3e12, 0x00|0x03},
{0x3e13, 0x40},
};
I2C_ARRAY temperature_reg_1_temp[] ={
{0x5799, 0x00},
};
memcpy(gain_reg_temp, params->tGain_reg_HDR_SEF, sizeof(gain_reg_HDR_SEF));
memcpy(temperature_reg_1_temp, params->tTemperature_reg_1, sizeof(temperature_reg_1_temp));
if (gain <= 1024) {
gain = 1024;
} else if (gain > SENSOR_MAX_GAIN*1024) {
gain = SENSOR_MAX_GAIN*1024;
}
if (gain < 2 * 1024) // start again
{
Dcg_gainx100 = 100; Coarse_gain = 1; DIG_gain=1;
Coarse_gain_reg = 0x03; DIG_gain_reg=0x0; DIG_Fine_gain_reg=0x80;
}
else if (gain <= 3456)
{
Dcg_gainx100 = 100; Coarse_gain = 2; DIG_gain=1;
Coarse_gain_reg = 0x07; DIG_gain_reg=0x0; DIG_Fine_gain_reg=0x80;
}
else if (gain <= 6908)
{
Dcg_gainx100 = 340; Coarse_gain = 1; DIG_gain=1;
Coarse_gain_reg = 0x23; DIG_gain_reg=0x0; DIG_Fine_gain_reg=0x80;
}
else if (gain <= 13817)
{
Dcg_gainx100 = 340; Coarse_gain = 2; DIG_gain=1;
Coarse_gain_reg = 0x27; DIG_gain_reg=0x0; DIG_Fine_gain_reg=0x80;
}
else if (gain <= 27635)
{
Dcg_gainx100 = 340; Coarse_gain = 4; DIG_gain=1;
Coarse_gain_reg = 0x2f; DIG_gain_reg=0x0; DIG_Fine_gain_reg=0x80;
}
else if (gain <= 55270) // end again
{
Dcg_gainx100 = 340; Coarse_gain = 8; DIG_gain=1;
Coarse_gain_reg = 0x3f; DIG_gain_reg=0x0; DIG_Fine_gain_reg=0x80;
}
#if 1
else if (gain < 55270 * 2) // start dgain
{
Dcg_gainx100 = 340; Coarse_gain = 8; DIG_gain=1; ANA_Fine_gainx64=127;
Coarse_gain_reg = 0x3f; DIG_gain_reg=0x0; ANA_Fine_gain_reg=0x7f;
}
else if (gain < 55270 * 4)
{
Dcg_gainx100 = 340; Coarse_gain = 8; DIG_gain=2; ANA_Fine_gainx64=127;
Coarse_gain_reg = 0x3f; DIG_gain_reg=0x1; ANA_Fine_gain_reg=0x7f;
}
else if (gain < 55270 * 8)
{
Dcg_gainx100 = 340; Coarse_gain = 8; DIG_gain=4; ANA_Fine_gainx64=127;
Coarse_gain_reg = 0x3f; DIG_gain_reg=0x3; ANA_Fine_gain_reg=0x7f;
}
else if (gain < 55270 * 16)
{
Dcg_gainx100 = 340; Coarse_gain = 8; DIG_gain=8; ANA_Fine_gainx64=127;
Coarse_gain_reg = 0x3f; DIG_gain_reg=0x7; ANA_Fine_gain_reg=0x7f;
}
else if (gain <= 1754822)
{
Dcg_gainx100 = 340; Coarse_gain = 8; DIG_gain=16; ANA_Fine_gainx64=127;
Coarse_gain_reg = 0x3f; DIG_gain_reg=0xF; ANA_Fine_gain_reg=0x7f;
}
#endif
if(gain < 3456)
{
//ANA_Fine_gainx64 = 1000 * 100 * gain / (Dcg_gainx100 * Coarse_gain) / 1024;
ANA_Fine_gain_reg = (u8)(100 * gain / (Dcg_gainx100 * Coarse_gain) / 16);
}else if(gain == 3456) // || gain == 3456)
{
ANA_Fine_gain_reg = 0x6C;
}
else if(gain < 55270)
{
ANA_Fine_gain_reg = (u8)(100 * gain / (Dcg_gainx100 * Coarse_gain) / 16);
}else{
DIG_Fine_gain_reg = (u8)(800 * gain /(Dcg_gainx100 * Coarse_gain * DIG_gain) / ANA_Fine_gainx64);
}
params->tGain_reg_HDR_SEF[3].data = ANA_Fine_gain_reg;
params->tGain_reg_HDR_SEF[2].data = Coarse_gain_reg;
params->tGain_reg_HDR_SEF[1].data = DIG_Fine_gain_reg;
params->tGain_reg_HDR_SEF[0].data = DIG_gain_reg & 0xF;
//printk("[%s] gain_reg : %x ,%x ,%x, %x\n\n", __FUNCTION__,gain_reg_HDR_SEF[3].data,gain_reg_HDR_SEF[2].data,gain_reg_HDR_SEF[1].data,gain_reg_HDR_SEF[0].data);
for (i = 0; i < ARRAY_SIZE(params->tGain_reg_HDR_SEF); i++)
{
if (params->tGain_reg_HDR_SEF[i].data != gain_reg_temp[i].data)
{
params->reg_dirty = true;
break;
}
}
// highTemp dpc
if (gain >= 30 * 1024) {
params->tTemperature_reg_1[0].data = 0x07;
} else if (gain <= 20 * 1024) {
params->tTemperature_reg_1[0].data = 0x00;
}
for (i = 0; i < ARRAY_SIZE(temperature_reg_1_temp); i++) {
if (params->tTemperature_reg_1[i].data != temperature_reg_1_temp[i].data) {
params->temperature_reg_1_dirty = true;
break;
}
}
return SUCCESS;
}
#if 0
static int pCus_GetAEMinMaxUSecs(ss_cus_sensor *handle, u32 *min, u32 *max) {
*min = 1;
*max = 1000000/Preview_MIN_FPS;
return SUCCESS;
}
static int pCus_GetAEMinMaxGain(ss_cus_sensor *handle, u32 *min, u32 *max) {
*min = 1024; // 1232 x1.2 Gain
*max = SENSOR_MAX_GAIN*1024;
return SUCCESS;
}
static int sc200ai_GetShutterInfo(struct __ss_cus_sensor* handle,CUS_SHUTTER_INFO *info)
{
info->max = 1000000000/Preview_MIN_FPS;
info->min = Preview_line_period;// /2;
info->step = Preview_line_period /2;
return SUCCESS;
}
static int pCus_GetShutterInfo_hdr_lef(struct __ss_cus_sensor* handle,CUS_SHUTTER_INFO *info)
{
info->max = 1000000000/Preview_MIN_FPS;
info->min = Preview_line_period_HDR*2;
info->step = Preview_line_period_HDR*2;
return SUCCESS;
}
static int pCus_GetShutterInfo_hdr_sef(struct __ss_cus_sensor* handle,CUS_SHUTTER_INFO *info)
{
info->max = 1000000000/(Preview_MIN_FPS*17);
info->min = Preview_line_period_HDR*2;
info->step = Preview_line_period_HDR*2;
return SUCCESS;
}
#endif
static int pCus_poweron_hdr_lef(ss_cus_sensor *handle, u32 idx)
{
return SUCCESS;
}
static int pCus_poweroff_hdr_lef(ss_cus_sensor *handle, u32 idx)
{
return SUCCESS;
}
#if 0
static int pCus_GetSensorID_hdr_lef(ss_cus_sensor *handle, u32 *id)
{
*id = 0;
return SUCCESS;
}
#endif
static int pCus_init_hdr_lef(ss_cus_sensor *handle)
{
return SUCCESS;
}
static int pCus_GetFPS_hdr_lef(ss_cus_sensor *handle)
{
sc200ai_params *params = (sc200ai_params *)handle->private_data;
u32 max_fps = handle->video_res_supported.res[handle->video_res_supported.ulcur_res].u16max_fps;
u32 tVts = (params->tVts_reg[0].data << 8) | (params->tVts_reg[1].data << 0);
if (params->expo.fps >= 1000)
params->expo.preview_fps = (vts_30fps_HDR*max_fps*1000)/tVts;
else
params->expo.preview_fps = (vts_30fps_HDR*max_fps)/tVts;
return params->expo.preview_fps;
}
#if 0
static int pCus_setCaliData_gain_linearity_hdr_lef(ss_cus_sensor* handle, CUS_GAIN_GAP_ARRAY* pArray, u32 num)
{
return SUCCESS;
}
static int pCus_SetAEGain_cal_hdr_lef(ss_cus_sensor *handle, u32 gain)
{
return SUCCESS;
}
static int pCus_setCaliData_gain_linearity(ss_cus_sensor* handle, CUS_GAIN_GAP_ARRAY* pArray, u32 num) {
return SUCCESS;
}
#endif
#define CMDID_I2C_READ (0x01)
#define CMDID_I2C_WRITE (0x02)
static int pCus_sensor_CustDefineFunction(ss_cus_sensor* handle,u32 cmd_id, void *param) {
if(param == NULL || handle == NULL)
{
SENSOR_EMSG("param/handle data NULL \n");
return FAIL;
}
switch(cmd_id)
{
case CMDID_I2C_READ:
{
I2C_ARRAY *reg = (I2C_ARRAY *)param;
SensorReg_Read(reg->reg, ®->data);
SENSOR_EMSG("reg %x, read data %x \n", reg->reg, reg->data);
break;
}
case CMDID_I2C_WRITE:
{
I2C_ARRAY *reg = (I2C_ARRAY *)param;
SENSOR_EMSG("reg %x, write data %x \n", reg->reg, reg->data);
SensorReg_Write(reg->reg, reg->data);
break;
}
default:
SENSOR_EMSG("cmd id %d err \n", cmd_id);
break;
}
return SUCCESS;
}
int cus_camsensor_init_handle_linear(ss_cus_sensor* drv_handle) {
ss_cus_sensor *handle = drv_handle;
sc200ai_params *params;
int res;
if (!handle) {
SENSOR_DMSG("[%s] not enough memory!\n", __FUNCTION__);
return FAIL;
}
SENSOR_DMSG("[%s]", __FUNCTION__);
//private data allocation & init
if (handle->private_data == NULL) {
SENSOR_EMSG("[%s] Private data is empty!\n", __FUNCTION__);
return FAIL;
}
params = (sc200ai_params *)handle->private_data;
memcpy(params->tVts_reg, vts_reg, sizeof(vts_reg));
memcpy(params->tGain_reg, gain_reg, sizeof(gain_reg));
memcpy(params->tGain_reg_HDR_SEF, gain_reg_HDR_SEF, sizeof(gain_reg_HDR_SEF));
memcpy(params->tExpo_reg, expo_reg, sizeof(expo_reg));
memcpy(params->tExpo_reg_HDR_SEF, expo_reg_HDR_SEF, sizeof(expo_reg_HDR_SEF));
memcpy(params->tMax_short_exp_reg, max_short_exp_reg, sizeof(max_short_exp_reg));
memcpy(params->tMirror_reg, mirror_reg, sizeof(mirror_reg));
memcpy(params->tTemperature_reg_1, temperature_reg_1, sizeof(temperature_reg_1));
////////////////////////////////////
// sensor model ID //
////////////////////////////////////
sprintf(handle->strSensorStreamName,"sc200ai_MIPI_Linear");
////////////////////////////////////
// sensor interface info //
////////////////////////////////////
//SENSOR_DMSG("[%s] entering function with id %d\n", __FUNCTION__, id);
//handle->isp_type = SENSOR_ISP_TYPE; //ISP_SOC;
//handle->data_fmt = SENSOR_DATAFMT; //CUS_DATAFMT_YUV;
handle->sif_bus = SENSOR_IFBUS_TYPE;//CUS_SENIF_BUS_PARL;
handle->data_prec = SENSOR_DATAPREC; //CUS_DATAPRECISION_8;
//handle->data_mode = SENSOR_DATAMODE;
handle->bayer_id = SENSOR_BAYERID; //CUS_BAYER_GB;
handle->RGBIR_id = SENSOR_RGBIRID;
params->orit = SENSOR_ORIT; //CUS_ORIT_M1F1;
handle->interface_attr.attr_mipi.mipi_lane_num = SENSOR_MIPI_LANE_NUM;
handle->interface_attr.attr_mipi.mipi_data_format = CUS_SEN_INPUT_FORMAT_RGB; // RGB pattern.
handle->interface_attr.attr_mipi.mipi_yuv_order = 0; //don't care in RGB pattern.
//handle->interface_attr.attr_mipi.mipi_hsync_mode = SENSOR_MIPI_HSYNC_MODE;
handle->interface_attr.attr_mipi.mipi_hdr_mode = CUS_HDR_MODE_NONE;
handle->interface_attr.attr_mipi.mipi_hdr_virtual_channel_num = 0; //Short frame
////////////////////////////////////
// resolution capability //
////////////////////////////////////
handle->video_res_supported.ulcur_res = 0;
for (res = 0; res < LINEAR_RES_END; res++) {
handle->video_res_supported.num_res = res+1;
handle->video_res_supported.res[res].u16width = sc200ai_mipi_linear[res].senif.preview_w;
handle->video_res_supported.res[res].u16height = sc200ai_mipi_linear[res].senif.preview_h;
handle->video_res_supported.res[res].u16max_fps = sc200ai_mipi_linear[res].senout.max_fps;
handle->video_res_supported.res[res].u16min_fps = sc200ai_mipi_linear[res].senout.min_fps;
handle->video_res_supported.res[res].u16crop_start_x = sc200ai_mipi_linear[res].senif.crop_start_X;
handle->video_res_supported.res[res].u16crop_start_y = sc200ai_mipi_linear[res].senif.crop_start_y;
handle->video_res_supported.res[res].u16OutputWidth = sc200ai_mipi_linear[res].senout.width;
handle->video_res_supported.res[res].u16OutputHeight = sc200ai_mipi_linear[res].senout.height;
sprintf(handle->video_res_supported.res[res].strResDesc, sc200ai_mipi_linear[res].senstr.strResDesc);
}
handle->sensor_ae_info_cfg.u8AEGainDelay = SENSOR_GAIN_DELAY_FRAME_COUNT;
handle->sensor_ae_info_cfg.u8AEShutterDelay = SENSOR_SHUTTER_DELAY_FRAME_COUNT;
handle->sensor_ae_info_cfg.u8AEGainCtrlNum = SENSOR_GAIN_CTRL_NUM;
handle->sensor_ae_info_cfg.u8AEShutterCtrlNum = SENSOR_SHUTTER_CTRL_NUM;
handle->sensor_ae_info_cfg.u32AEGain_min = SENSOR_MIN_GAIN*1024;
handle->sensor_ae_info_cfg.u32AEGain_max = SENSOR_MAX_GAIN*1024;
handle->sensor_ae_info_cfg.u32AEShutter_min = Preview_line_period*1;//2
handle->sensor_ae_info_cfg.u32AEShutter_max = 1000000000/Preview_MIN_FPS;
handle->sensor_ae_info_cfg.u32AEShutter_step = Preview_line_period*1;//2
// i2c
handle->i2c_cfg.mode = SENSOR_I2C_LEGACY; //(CUS_ISP_I2C_MODE) FALSE;
handle->i2c_cfg.fmt = SENSOR_I2C_FMT; //CUS_I2C_FMT_A16D16;
handle->i2c_cfg.address = SENSOR_I2C_ADDR; //0x5a;
handle->i2c_cfg.speed = SENSOR_I2C_SPEED; //320000;
// mclk
handle->mclk = Preview_MCLK_SPEED;
//polarity
/////////////////////////////////////////////////////
//handle->pwdn_POLARITY = SENSOR_PWDN_POL; //CUS_CLK_POL_NEG;
//handle->reset_POLARITY = SENSOR_RST_POL; //CUS_CLK_POL_NEG;
//handle->VSYNC_POLARITY = SENSOR_VSYNC_POL; //CUS_CLK_POL_POS;
//handle->HSYNC_POLARITY = SENSOR_HSYNC_POL; //CUS_CLK_POL_POS;
//handle->PCLK_POLARITY = SENSOR_PCLK_POL; //CUS_CLK_POL_POS); // use '!' to clear board latch error
/////////////////////////////////////////////////////
////////////////////////////////////////////////////
// AE parameters
////////////////////////////////////////////////////
//handle->ae_gain_delay = 2;
//handle->ae_shutter_delay = 2;
//handle->ae_gain_ctrl_num = 1;
//handle->ae_shutter_ctrl_num = 1;
//handle->pCus_sensor_release = cus_camsensor_release_handle;
handle->pCus_sensor_init = pCus_init_mipi2lane_linear_2M30fps;
handle->pCus_sensor_poweron = pCus_poweron ;
handle->pCus_sensor_poweroff = pCus_poweroff;
// Normal
//handle->pCus_sensor_GetSensorID = pCus_GetSensorID ;
handle->pCus_sensor_GetVideoResNum = pCus_GetVideoResNum;
handle->pCus_sensor_GetVideoRes = pCus_GetVideoRes;
handle->pCus_sensor_GetCurVideoRes = pCus_GetCurVideoRes;
handle->pCus_sensor_SetVideoRes = pCus_SetVideoRes;
handle->pCus_sensor_GetOrien = pCus_GetOrien ;
handle->pCus_sensor_SetOrien = pCus_SetOrien ;
handle->pCus_sensor_GetFPS = pCus_GetFPS ;
handle->pCus_sensor_SetFPS = pCus_SetFPS ;
//handle->pCus_sensor_GetSensorCap = pCus_GetSensorCap;
handle->pCus_sensor_SetPatternMode = sc200ai_SetPatternMode;
///////////////////////////////////////////////////////
// AE
///////////////////////////////////////////////////////
// unit: micro seconds
handle->pCus_sensor_AEStatusNotify = pCus_AEStatusNotify;
handle->pCus_sensor_GetAEUSecs = pCus_GetAEUSecs;
handle->pCus_sensor_SetAEUSecs = pCus_SetAEUSecs;
handle->pCus_sensor_GetAEGain = pCus_GetAEGain;
handle->pCus_sensor_SetAEGain = pCus_SetAEGain;
handle->pCus_sensor_GetAEInfo = pCus_sensor_GetAEInfo;
//handle->pCus_sensor_GetAEMinMaxGain = pCus_GetAEMinMaxGain;
//handle->pCus_sensor_GetAEMinMaxUSecs= pCus_GetAEMinMaxUSecs;
handle->pCus_sensor_CustDefineFunction = pCus_sensor_CustDefineFunction;
//sensor calibration
//handle->pCus_sensor_SetAEGain_cal = pCus_SetAEGain_cal;
//handle->pCus_sensor_setCaliData_gain_linearity=pCus_setCaliData_gain_linearity;
//handle->pCus_sensor_GetShutterInfo = sc200ai_GetShutterInfo;
params->expo.vts=vts_30fps;
params->expo.fps = 30;
params->expo.line= 1000;
params->reg_dirty = false;
params->temperature_reg_1_dirty = false;
params->orient_dirty = false;
return SUCCESS;
}
int cus_camsensor_init_handle_hdr_sef(ss_cus_sensor* drv_handle)
{
ss_cus_sensor *handle = drv_handle;
sc200ai_params *params = NULL;
int res;
cus_camsensor_init_handle_linear(drv_handle);
params = (sc200ai_params *)handle->private_data;
sprintf(handle->strSensorStreamName,"sc200ai_MIPI_HDR_SEF");
handle->bayer_id = SENSOR_BAYERID_HDR;
handle->RGBIR_id = SENSOR_RGBIRID;
handle->data_prec = SENSOR_DATAPREC_HDR;
handle->interface_attr.attr_mipi.mipi_lane_num = SENSOR_MIPI_LANE_NUM;
//handle->interface_attr.attr_mipi.mipi_hsync_mode = SENSOR_MIPI_HSYNC_MODE_HDR;
handle->interface_attr.attr_mipi.mipi_hdr_mode = SENSOR_HDR_MODE;
////////////////////////////////////
// resolution capability //
////////////////////////////////////
handle->video_res_supported.ulcur_res = 0;
for (res = 0; res < HDR_RES_END; res++) {
handle->video_res_supported.num_res = res+1;
handle->video_res_supported.res[res].u16width = sc200ai_mipi_hdr[res].senif.preview_w;
handle->video_res_supported.res[res].u16height = sc200ai_mipi_hdr[res].senif.preview_h;
handle->video_res_supported.res[res].u16max_fps = sc200ai_mipi_hdr[res].senout.max_fps;
handle->video_res_supported.res[res].u16min_fps = sc200ai_mipi_hdr[res].senout.min_fps;
handle->video_res_supported.res[res].u16crop_start_x = sc200ai_mipi_hdr[res].senif.crop_start_X;
handle->video_res_supported.res[res].u16crop_start_y = sc200ai_mipi_hdr[res].senif.crop_start_y;
handle->video_res_supported.res[res].u16OutputWidth = sc200ai_mipi_hdr[res].senout.width;
handle->video_res_supported.res[res].u16OutputHeight = sc200ai_mipi_hdr[res].senout.height;
sprintf(handle->video_res_supported.res[res].strResDesc, sc200ai_mipi_hdr[res].senstr.strResDesc);
}
handle->sensor_ae_info_cfg.u8AEGainDelay = SENSOR_GAIN_DELAY_FRAME_COUNT;
handle->sensor_ae_info_cfg.u8AEShutterDelay = SENSOR_SHUTTER_DELAY_FRAME_COUNT;
handle->sensor_ae_info_cfg.u8AEGainCtrlNum = SENSOR_GAIN_CTRL_NUM;
handle->sensor_ae_info_cfg.u8AEShutterCtrlNum = SENSOR_SHUTTER_CTRL_NUM_HDR;
handle->sensor_ae_info_cfg.u32AEGain_min = SENSOR_MIN_GAIN*1024;
handle->sensor_ae_info_cfg.u32AEGain_max = SENSOR_MAX_GAIN*1024;
handle->sensor_ae_info_cfg.u32AEShutter_min = Preview_line_period_HDR*2;
handle->sensor_ae_info_cfg.u32AEShutter_max = 1000000000/(Preview_MIN_FPS*17);
handle->sensor_ae_info_cfg.u32AEShutter_step = Preview_line_period_HDR*2;
handle->pCus_sensor_SetVideoRes = pCus_SetVideoRes_HDR;
handle->mclk = Preview_MCLK_SPEED_HDR;
handle->pCus_sensor_init = pCus_init_mipi2lane_HDR;
handle->pCus_sensor_SetFPS = pCus_SetFPS_HDR_SEF; //TBD
handle->pCus_sensor_GetOrien = pCus_GetOrien;
handle->pCus_sensor_SetOrien = pCus_SetOrien;
handle->pCus_sensor_AEStatusNotify = pCus_AEStatusNotifyHDR_SEF;
handle->pCus_sensor_GetAEUSecs = pCus_GetAEUSecsHDR_SEF;
handle->pCus_sensor_SetAEUSecs = pCus_SetAEUSecsHDR_SEF;
handle->pCus_sensor_GetAEGain = pCus_GetAEGain;
handle->pCus_sensor_SetAEGain = pCus_SetAEGainHDR_SEF;
handle->pCus_sensor_GetAEInfo = pCus_sensor_GetAEInfo;
//handle->pCus_sensor_GetShutterInfo = pCus_GetShutterInfo_hdr_sef;
params->expo.vts = vts_30fps_HDR;
params->expo.fps = 30;
params->expo.max_short_exp=135;
handle->interface_attr.attr_mipi.mipi_hdr_virtual_channel_num = 1; //Short frame
//handle->ae_gain_delay = 2;
//handle->ae_shutter_delay = 2;
//handle->ae_gain_ctrl_num = 1;
//handle->ae_shutter_ctrl_num = 2;
return SUCCESS;
}
int cus_camsensor_init_handle_hdr_lef(ss_cus_sensor* drv_handle)
{
ss_cus_sensor *handle = drv_handle;
sc200ai_params *params;
int res;
if (!handle) {
SENSOR_DMSG("[%s] not enough memory!\n", __FUNCTION__);
return FAIL;
}
SENSOR_DMSG("[%s]", __FUNCTION__);
//private data allocation & init
if (handle->private_data == NULL) {
SENSOR_EMSG("[%s] Private data is empty!\n", __FUNCTION__);
return FAIL;
}
params = (sc200ai_params *)handle->private_data;
memcpy(params->tVts_reg, vts_reg, sizeof(vts_reg));
memcpy(params->tGain_reg, gain_reg, sizeof(gain_reg));
memcpy(params->tGain_reg_HDR_SEF, gain_reg_HDR_SEF, sizeof(gain_reg_HDR_SEF));
memcpy(params->tExpo_reg, expo_reg, sizeof(expo_reg));
memcpy(params->tExpo_reg_HDR_SEF, expo_reg_HDR_SEF, sizeof(expo_reg_HDR_SEF));
memcpy(params->tMax_short_exp_reg, max_short_exp_reg, sizeof(max_short_exp_reg));
memcpy(params->tMirror_reg, mirror_reg, sizeof(mirror_reg));
memcpy(params->tTemperature_reg_1, temperature_reg_1, sizeof(temperature_reg_1));
////////////////////////////////////
// sensor model ID //
////////////////////////////////////
sprintf(handle->strSensorStreamName,"SC200ai_MIPI_HDR_LEF");
////////////////////////////////////
// sensor interface info //
////////////////////////////////////
//SENSOR_DMSG("[%s] entering function with id %d\n", __FUNCTION__, id);
//handle->isp_type = SENSOR_ISP_TYPE; //ISP_SOC;
//handle->data_fmt = SENSOR_DATAFMT; //CUS_DATAFMT_YUV;
handle->sif_bus = SENSOR_IFBUS_TYPE;//CUS_SENIF_BUS_PARL;
handle->data_prec = SENSOR_DATAPREC_HDR; //CUS_DATAPRECISION_8;
//handle->data_mode = SENSOR_DATAMODE;
handle->bayer_id = SENSOR_BAYERID_HDR; //CUS_BAYER_GB;
handle->RGBIR_id = SENSOR_RGBIRID;
params->orit = SENSOR_ORIT; //CUS_ORIT_M1F1;
handle->interface_attr.attr_mipi.mipi_lane_num = SENSOR_MIPI_LANE_NUM;
handle->interface_attr.attr_mipi.mipi_data_format = CUS_SEN_INPUT_FORMAT_RGB; // RGB pattern.
handle->interface_attr.attr_mipi.mipi_yuv_order = 0; //don't care in RGB pattern.
//handle->interface_attr.attr_mipi.mipi_hsync_mode = SENSOR_MIPI_HSYNC_MODE_HDR;
handle->interface_attr.attr_mipi.mipi_hdr_mode = SENSOR_HDR_MODE;
handle->interface_attr.attr_mipi.mipi_hdr_virtual_channel_num = 0; //Long frame
////////////////////////////////////
// resolution capability //
////////////////////////////////////
handle->video_res_supported.ulcur_res = 0;
for (res = 0; res < HDR_RES_END; res++) {
handle->video_res_supported.num_res = res+1;
handle->video_res_supported.res[res].u16width = sc200ai_mipi_hdr[res].senif.preview_w;
handle->video_res_supported.res[res].u16height = sc200ai_mipi_hdr[res].senif.preview_h;
handle->video_res_supported.res[res].u16max_fps = sc200ai_mipi_hdr[res].senout.max_fps;
handle->video_res_supported.res[res].u16min_fps = sc200ai_mipi_hdr[res].senout.min_fps;
handle->video_res_supported.res[res].u16crop_start_x = sc200ai_mipi_hdr[res].senif.crop_start_X;
handle->video_res_supported.res[res].u16crop_start_y = sc200ai_mipi_hdr[res].senif.crop_start_y;
handle->video_res_supported.res[res].u16OutputWidth = sc200ai_mipi_hdr[res].senout.width;
handle->video_res_supported.res[res].u16OutputHeight = sc200ai_mipi_hdr[res].senout.height;
sprintf(handle->video_res_supported.res[res].strResDesc, sc200ai_mipi_hdr[res].senstr.strResDesc);
}
handle->sensor_ae_info_cfg.u8AEGainDelay = SENSOR_GAIN_DELAY_FRAME_COUNT;
handle->sensor_ae_info_cfg.u8AEShutterDelay = SENSOR_SHUTTER_DELAY_FRAME_COUNT;
handle->sensor_ae_info_cfg.u8AEGainCtrlNum = SENSOR_GAIN_CTRL_NUM;
handle->sensor_ae_info_cfg.u8AEShutterCtrlNum = SENSOR_SHUTTER_CTRL_NUM_HDR;
handle->sensor_ae_info_cfg.u32AEGain_min = SENSOR_MIN_GAIN*1024;
handle->sensor_ae_info_cfg.u32AEGain_max = SENSOR_MAX_GAIN*1024;
handle->sensor_ae_info_cfg.u32AEShutter_min = Preview_line_period_HDR*2;
handle->sensor_ae_info_cfg.u32AEShutter_max = 1000000000/Preview_MIN_FPS;
handle->sensor_ae_info_cfg.u32AEShutter_step = Preview_line_period_HDR*2;
// i2c
handle->i2c_cfg.mode = SENSOR_I2C_LEGACY; //(CUS_ISP_I2C_MODE) FALSE;
handle->i2c_cfg.fmt = SENSOR_I2C_FMT; //CUS_I2C_FMT_A16D16;
handle->i2c_cfg.address = SENSOR_I2C_ADDR; //0x5a;
handle->i2c_cfg.speed = SENSOR_I2C_SPEED; //320000;
// mclk
handle->mclk = Preview_MCLK_SPEED_HDR;
//polarity
/////////////////////////////////////////////////////
//handle->pwdn_POLARITY = SENSOR_PWDN_POL; //CUS_CLK_POL_NEG;
//handle->reset_POLARITY = SENSOR_RST_POL; //CUS_CLK_POL_NEG;
//handle->VSYNC_POLARITY = SENSOR_VSYNC_POL; //CUS_CLK_POL_POS;
//handle->HSYNC_POLARITY = SENSOR_HSYNC_POL; //CUS_CLK_POL_POS;
//handle->PCLK_POLARITY = SENSOR_PCLK_POL; //CUS_CLK_POL_POS); // use '!' to clear board latch error
/////////////////////////////////////////////////////
////////////////////////////////////////////////////
// AE parameters
////////////////////////////////////////////////////
//handle->ae_gain_delay = 2;//0;//1;
//handle->ae_shutter_delay = 2;//1;//2;
//handle->ae_gain_ctrl_num = 1;
//handle->ae_shutter_ctrl_num = 2;
//LOGD("[%s:%d]\n", __FUNCTION__, __LINE__);
//handle->pCus_sensor_release = cus_camsensor_release_handle;
handle->pCus_sensor_init = pCus_init_hdr_lef;
handle->pCus_sensor_poweron = pCus_poweron_hdr_lef;
handle->pCus_sensor_poweroff = pCus_poweroff_hdr_lef;
// Normal
//handle->pCus_sensor_GetSensorID = pCus_GetSensorID_hdr_lef;
handle->pCus_sensor_GetVideoResNum = NULL;
handle->pCus_sensor_GetVideoRes = NULL;
handle->pCus_sensor_GetCurVideoRes = NULL;
handle->pCus_sensor_SetVideoRes = NULL;
handle->pCus_sensor_GetOrien = pCus_GetOrien;
handle->pCus_sensor_SetOrien = pCus_SetOrien;
handle->pCus_sensor_GetFPS = pCus_GetFPS_hdr_lef;
handle->pCus_sensor_SetFPS = pCus_SetFPS_hdr_lef;
handle->pCus_sensor_SetPatternMode = sc200ai_SetPatternMode;
///////////////////////////////////////////////////////
// AE
///////////////////////////////////////////////////////
// unit: micro seconds
handle->pCus_sensor_AEStatusNotify = pCus_AEStatusNotifyHDR_LEF;
handle->pCus_sensor_GetAEUSecs = pCus_GetAEUSecs;
handle->pCus_sensor_SetAEUSecs = pCus_SetAEUSecsHDR_LEF;
handle->pCus_sensor_GetAEGain = pCus_GetAEGain;
handle->pCus_sensor_SetAEGain = pCus_SetAEGain;
handle->pCus_sensor_GetAEInfo = pCus_sensor_GetAEInfo;
//handle->pCus_sensor_GetAEMinMaxGain = pCus_GetAEMinMaxGain;
//sensor calibration
//handle->pCus_sensor_SetAEGain_cal = pCus_SetAEGain_cal_hdr_lef;
//handle->pCus_sensor_setCaliData_gain_linearity= pCus_setCaliData_gain_linearity_hdr_lef;
//handle->pCus_sensor_GetShutterInfo = pCus_GetShutterInfo_hdr_lef;
params->expo.vts = vts_30fps_HDR;
params->expo.fps = 30;
params->reg_dirty = false;
params->temperature_reg_1_dirty = false;
params->orient_dirty = false;
return SUCCESS;
}
#if 0
static int cus_camsensor_release_handle(ss_cus_sensor *handle) {
return SUCCESS;
}
#endif
SENSOR_DRV_ENTRY_IMPL_END_EX( SC200ai,
cus_camsensor_init_handle_linear,
cus_camsensor_init_handle_hdr_sef,
cus_camsensor_init_handle_hdr_lef,
sc200ai_params
);
总之写一个摄像头的驱动程序就分三步配置参数定义硬件特性 、寄存器表控制硬件行为、**功能函数实现逻辑控制、**最终通过驱动入口与系统框架对接,实现对摄像头的完整管理。
(3)对照数据手册和平台移植说明进行改写替换
梳理完大体框架,接下来就对照平台要求和传感器数据手册进行替换即可
1.重要宏配置改写与结构体修改
MCLK 速度(Preview_MCLK_SPEED 系列)
- 依据:数据手册 1.7 节明确 PLL 输入时钟(EXTCLK)范围为 6--40MHz,24MHz 属于该区间且是常用稳定频率,兼顾兼容性和性能。
- 逻辑:普通模式与 HDR 模式共用 24MHz,避免时钟切换导致的稳定性问题,简化驱动时序管理。

行周期与 VTS(Preview_line_period、vts_30fps 系列)
-
依据(此处有更正):
数据手册表2-10默认VTS为0x04B0(十进制1200),该值对应传感器60fps帧率; 60fps下通过公式"1e9/(帧率×VTS)"计算出行周期≈13889ns,与传感器时序逻辑一致; 30fps下需将VTS调整为2400(60fps VTS的2倍),此时行周期仍为13889ns(硬件固定),与传感器时序逻辑一致。 -
HDR 模式特殊处理:Preview_line_period_HDR 设为 14075ns(半行时间),完全匹配数据手册 2.3.2 节 "AE 调节步长为半行" 的要求,确保 HDR 模式下长短曝光的精准控制。


帧率范围(Preview_MAX_FPS/Preview_MIN_FPS)
- 依据:数据手册核心参数标注最大传输速率为 1920×1080@60fps,同时支持低功耗场景,故设 3fps 为最低值,覆盖常规预览和节能场景。
裁剪偏移宏:贴合传感器有效像素区域
- 依据:数据手册表 2-9 "输出窗口寄存器" 明确,列起始(3210/3211)和行起始(3212/3213)默认值为 0x0008,对应十进制 8。
- 逻辑:直接沿用手册默认配置,确保驱动读取的是传感器有效成像区域,避免边缘无效像素影响画面质量。

I2C 配置宏:适配传感器通信协议
- I2C 地址(SENSOR_I2C_ADDR=0x6C)
- 依据:数据手册表 1-3,SID 引脚低电平时 I2C 设备地址为 7'h36(7 位地址),转换为 8 位写地址需左移 1 位(加 0),即 0x6C。
- 通信参数(SENSOR_I2C_SPEED/FMT)
- 速度设为 100KHz:兼顾兼容性(默认支持 Standard-mode),虽手册支持 400KHz Fast-mode,但 100KHz 更稳定,适配多数主控的默认 I2C 配置。
- 格式设为 I2C_FMT_A16D8:数据手册 1.4 节明确传感器寄存器为 16 位地址、8 位数据,该格式直接匹配通信协议,避免地址 / 数据解析错误。

AE 相关宏:遵循手册增益 / 曝光控制规则
- 增益范围(SENSOR_MAX_GAIN/SENSOR_MIN_GAIN)
- 依据:数据手册 "产品特性" 标注 15.5x 模拟增益,表 2-5/2-6 显示数字增益最大 16x,两者相乘得 248x,故最大增益设为 248;最小增益 1x 为传感器默认初始状态。
- 延迟帧数(SENSOR_GAIN_DELAY_FRAME_COUNT 等)
- 依据:数据手册 2.3.2 节明确 "曝光时间及增益若在第 N 帧写入,第 N+2 帧生效",故延迟帧数统一设为 2,确保驱动层调用时的时序同步。
- 快门控制通道数(SENSOR_SHUTTER_CTRL_NUM 系列)
- 普通模式设为 1:线性模式仅需单通道控制曝光时间,符合数据手册 2.3 节 "AEC 单通道调节" 逻辑。
- HDR 模式设为 2:数据手册 2.2 节 "行交叠 HDR" 需分别控制长曝光、短曝光两个通道,故通道数设为 2,适配 HDR 模式的双曝光机制。


修改如下:

//sc200pc
#define Preview_MCLK_SPEED CUS_CMU_CLK_24MHZ //CFG //CUS_CMU_CLK_12M, CUS_CMU_CLK_16M, CUS_CMU_CLK_24M, CUS_CMU_CLK_27M
#define Preview_MCLK_SPEED_HDR CUS_CMU_CLK_24MHZ
#define Preview_line_period 28150 // 按30fps、VTS=1168计算:1e9/(30×1168)≈28150 ns
#define vts_30fps 1168 // sc200pc默认VTS(0x04B0)
#define Preview_line_period_HDR 14075 // 半行时间=28150/2≈14075 ns(与数据手册2.3.2节"半行调节步长"匹配)
#define vts_30fps_HDR 1168
#define Preview_MAX_FPS 60 //fastest preview FPS
#define Preview_MIN_FPS 3 //slowest preview FPS
#define Preview_CROP_START_X 8 // sc200pc有效区域偏移(列起始8)
#define Preview_CROP_START_Y 8 // 行起始8(数据手册表2-9)
//sc200pc
#define SENSOR_I2C_ADDR 0x6C //I2C slave address
#define SENSOR_I2C_SPEED 100000 // sc200pc支持Fast-mode(400KHz)
#define SENSOR_I2C_LEGACY I2C_NORMAL_MODE //usally set CUS_I2C_NORMAL_MODE, if use old OVT I2C protocol=> set CUS_I2C_LEGACY_MODE
#define SENSOR_I2C_FMT I2C_FMT_A16D8 //CUS_I2C_FMT_A8D8, CUS_I2C_FMT_A8D16, CUS_I2C_FMT_A16D8, CUS_I2C_FMT_A16D16
//AE info
//sc200pc
#define SENSOR_MAX_GAIN (248) // SC200PC:15.5x模拟增益 × 16x数字增益 = 248x总最大增益(手册产品特性+表2-5/2-6)
#define SENSOR_MIN_GAIN (1) // 最小增益1x(默认不变)
#define SENSOR_GAIN_DELAY_FRAME_COUNT (2) // 增益写入后第N+2帧生效(手册2.3.2节明确要求)
#define SENSOR_SHUTTER_DELAY_FRAME_COUNT (2) // 快门写入后第N+2帧生效(手册2.3.2节明确要求)
#define SENSOR_GAIN_CTRL_NUM (1) // 增益控制通道数(模拟+数字统一控制,默认不变)
#define SENSOR_SHUTTER_CTRL_NUM (1) // 线性模式快门控制通道数(默认不变)
#define SENSOR_SHUTTER_CTRL_NUM_HDR (2) // HDR模式快门通道数(长/短曝光双通道,默认不变)
还记得上面发的初始化序列表吗?里面写的传感器分辨率信息结构体需要小改一下即可

2.核心寄存器数据修改
传感器身份识别(Sensor_id_table)
- 作用:存储 SC200PC 的 ID 寄存器地址和对应值,用于初始化时校验传感器型号是否匹配。
- 修改逻辑:若更换传感器型号,需更新此处的 ID 高位(0x3107)和低位(0x3108)对应的数值,确保与新传感器的 ID 一致。

模式初始化配置(2M 30fps 与 HDR 模式)
这部分是核心,针对不同工作模式(普通 200 万像素 30fps 模式、HDR 模式)配置基础寄存器,确保传感器按预期输出图像。
(1)普通模式(Sensor_init_table_2M30fps)
- 逻辑 :针对 200 万像素、30fps 的线性输出模式,配置全套基础参数,包括:
- 复位与初始化(如 0x0103 软复位);
- 输出窗口尺寸(如 0x3208-0x320b 设置 1920x1080 分辨率);
- 帧率与帧长(隐含在 VTS 寄存器配置中);
- 增益、曝光、降噪、BLC(黑电平校正)等图像质量参数。
- 修改场景:若需调整分辨率(如改为 1080P)、帧率(如 60fps),需修改对应窗口尺寸寄存器(0x3208-0x3213)和帧长(VTS)相关寄存器。
(2)HDR 模式(Sensor_init_table_HDR)
- 逻辑 :在普通模式基础上,针对 HDR(高动态范围)的 "行交叠曝光" 特性增加专属配置:
- HDR 使能:通过 0x3281 寄存器开启 HDR 模式;
- 长短曝光通道分离:用 0x4814(长曝光)和 0x4851(短曝光)指定不同的 VC 通道,避免数据冲突;
- 长短曝光参数独立配置:分别设置长曝光(0x3E00-0x3E02)和短曝光(0x3E22-0x3E05)的初始值,以及最大短曝光限制(0x3E23-0x3E24),防止曝光时间重叠;
- 兼容基础配置:MIPI 通道(2-lane)、输出窗口(1920x1080)等与普通模式保持一致,确保硬件接口兼容。
- 修改场景:若 HDR 效果不佳(如过曝 / 欠曝),需调整最大短曝光值(0x3E24)或长短曝光的初始比例。
功能开关控制(镜像 / 倒置、温度补偿)
-
镜像 / 倒置(mirror_reg):
- 通过 0x3221 寄存器的位控制:bit [2:1] 控制镜像(0x06 开启),bit [6:5] 控制倒置(0x60 开启),默认关闭(0x00)。
- 修改逻辑:根据镜头安装方向(如倒装需倒置),修改对应位的值即可。
-
温度补偿(temperature_reg_1):
- 通过 0x5799 寄存器控制,默认 0x00(关闭),高增益场景下可改为 0x07(开启),补偿温度变化对图像的影响。
- 修改逻辑:根据实际环境温度波动情况,决定是否开启补偿。

核心参数调节(增益、曝光、帧长)
这些数组定义了可动态调节的关键参数的寄存器地址,方便后续通过程序修改(如自动曝光 AE、自动增益 AGC)。
-
增益(gain_reg 与 gain_reg_HDR_SEF):
- 普通模式:通过 0x3e06-0x3e09 分别控制数字增益、数字精细增益、模拟增益、模拟精细增益(初始 1.0x)。
- HDR 模式:短曝光通道单独用 0x3e10-0x3e13 配置增益,确保长短曝光的增益匹配。
- 修改逻辑:按寄存器位分配规则(如模拟增益 0x00=1x,0x01=1.2x 等),更新对应值以调节亮度。

-
曝光(expo_reg 与 expo_reg_HDR_SEF):
- 普通模式:曝光值由 3 个寄存器(0x3e00-0x3e02)拼接(高 4 位 + 中 8 位 + 低 4 位),初始为最小 2 半行。
- HDR 模式:短曝光由 0x3e22-0x3e05 拼接,独立控制。
- 修改逻辑:根据光线强度,按位分配曝光时间到对应寄存器(最大不超过 2*VTS-7,避免帧重叠)。

-
帧长(vts_reg):
- 由 0x320e(高 7 位)和 0x320f(低 8 位)定义每帧行数(VTS),30fps 默认 1168 行(0x04B0)。
- 修改逻辑:VTS 越大,帧率越低(帧率 = 时钟频率 / VTS),需根据需求平衡帧率和曝光范围。

修改如下:
// set sensor ID address and data
//sc200pc
#if 1
const static I2C_ARRAY Sensor_id_table[] =
{
{0x3107, 0x0B}, // sc200pc ID高位
{0x3108, 0x71}, // sc200pc ID低位
};
#endif
//sc200pc
const static I2C_ARRAY Sensor_init_table_2M30fps[] =
{
{0x0103, 0x01},
{0x301f, 0x02},
{0x3200, 0x00},
{0x3201, 0x00},
{0x3202, 0x00},
{0x3203, 0x00},
{0x3204, 0x07},
{0x3205, 0x8f},
{0x3206, 0x04},
{0x3207, 0x47},
{0x3208, 0x07},
{0x3209, 0x88},
{0x320a, 0x04},
{0x320b, 0x40},
{0x320c, 0x07},
{0x320d, 0x80},
{0x3210, 0x00},
{0x3211, 0x04},
{0x3212, 0x00},
{0x3213, 0x04},
{0x3253, 0x60},
{0x325f, 0x80},
{0x32d1, 0x70},
{0x3301, 0x07},
{0x3306, 0x30},
{0x3308, 0x10},
{0x330b, 0x78},
{0x330e, 0x28},
{0x331e, 0x21},
{0x331f, 0x21},
{0x3333, 0x10},
{0x3334, 0x40},
{0x3347, 0x05},
{0x334c, 0x08},
{0x335d, 0x60},
{0x3364, 0x56},
{0x3390, 0x08},
{0x3391, 0x38},
{0x3393, 0x0e},
{0x3394, 0x10},
{0x33ad, 0x1c},
{0x33b1, 0x80},
{0x33b2, 0x58},
{0x33b3, 0x08},
{0x349f, 0x02},
{0x34a6, 0x18},
{0x34a7, 0x38},
{0x34a8, 0x07},
{0x34a9, 0x06},
{0x3619, 0x20},
{0x361a, 0x91},
{0x3633, 0x48},
{0x3637, 0x49},
{0x3638, 0xa1},
{0x3660, 0x80},
{0x3661, 0x86},
{0x3662, 0x8e},
{0x3667, 0x38},
{0x3668, 0x78},
{0x3670, 0x65},
{0x3671, 0x45},
{0x3672, 0x45},
{0x3680, 0x46},
{0x3681, 0x66},
{0x3682, 0x88},
{0x3683, 0x39},
{0x3684, 0x39},
{0x3685, 0x39},
{0x36c0, 0x08},
{0x36c1, 0x18},
{0x36c8, 0x18},
{0x36c9, 0x78},
{0x36ca, 0x08},
{0x36cb, 0x78},
{0x3718, 0x04},
{0x3723, 0x20},
{0x3724, 0xe1},
{0x3770, 0x03},
{0x3771, 0x03},
{0x3772, 0x03},
{0x37c0, 0x08},
{0x37c1, 0x78},
{0x37ed, 0x89},
{0x37f9, 0x00},
{0x37fa, 0x0c},
{0x37fb, 0xca},
{0x3901, 0x08},
{0x3902, 0xc0},
{0x3903, 0x40},
{0x3908, 0x40},
{0x3909, 0x01},
{0x390a, 0x81},
{0x3929, 0x18},
{0x3933, 0x80},
{0x3934, 0x01},
{0x3937, 0x75},
{0x3939, 0x0f},
{0x393a, 0xfe},
{0x39dd, 0x06},
{0x3e00, 0x00},
{0x3e01, 0x95},
{0x3e02, 0x80},
{0x3e03, 0x0b},
{0x3e09, 0x10},
{0x3f09, 0x0e},
{0x4407, 0x0c},
{0x4509, 0x1e},
{0x450d, 0x01},
{0x450f, 0x06},
{0x5784, 0x0c},
{0x5785, 0x04},
{0x578d, 0x40},
{0x57ac, 0x00},
{0x57ad, 0x00},
{0x0100, 0x01},
};
//sc200pc
const static I2C_ARRAY Sensor_init_table_HDR[] =
{
// 1. 基础复位与睡眠模式配置(与线性模式保持一致)
{0x0103, 0x01}, // 软复位(SC200PC 标准软复位命令)
{0x0100, 0x00}, // 进入睡眠模式(后续再退出,确保配置稳定)
{0xFFFF, 0x02}, // 延迟 2ms(等待复位完成)
// 2. MIPI 基础配置(与线性模式一致,2-lane + 10-bit RAW)
{0x3018, 0x3B}, // MIPI 2-lane 模式(bit[7:5]=0x1)
{0x3031, 0x0A}, // MIPI 10-bit RAW 格式(bit[4:0]=0xA)
{0x3752, 0x0D}, // MIPI 驱动能力默认配置
{0x3750, 0x0D}, // Data Lane 0 延时默认
{0x3751, 0x00}, // Data Lane 1 + Clock Lane 延时默认
// 3. HDR 专属核心配置(SC200PC 行交叠 HDR 必需)
{0x3281, 0x01}, // 启用 HDR 模式(bit[0]=1)
{0x4814, 0x2A}, // 长曝光 VC 通道=0(bit[7:6]=0x0)
{0x4851, 0x6B}, // 短曝光 VC 通道=1(bit[7:6]=0x1)
{0x4853, 0xFC}, // 启用短曝光 VC 通道(bit[1]=0)
{0x3E23, 0x00}, // 最大短曝光高字节(默认 0x00)
{0x3E24, 0x20}, // 最大短曝光低字节(默认 0x20,32 半行)
// 4. 帧长(VTS)与帧率配置(HDR 模式 30fps,VTS=0x0900=2304 行)
{0x320E, 0x09}, // VTS 高 7 位(0x0900 的高 7 位=0x09)
{0x320F, 0x00}, // VTS 低 8 位(0x0900 的低 8 位=0x00)
{0x3230, 0x00}, // RB Rows=0(HDR 模式无需额外等待行)
{0x3231, 0x00},
// 5. 输出窗口配置(与线性模式一致,1920x1080,偏移 8x8)
{0x3208, 0x07}, // 窗口宽度高字节(1920=0x0780 → 0x07)
{0x3209, 0x80}, // 窗口宽度低字节(0x80)
{0x320A, 0x04}, // 窗口高度高字节(1080=0x0438 → 0x04)
{0x320B, 0x38}, // 窗口高度低字节(0x38)
{0x3210, 0x00}, // 列起始高字节(8=0x0008 → 0x00)
{0x3211, 0x08}, // 列起始低字节(0x08)
{0x3212, 0x00}, // 行起始高字节(0x00)
{0x3213, 0x08}, // 行起始低字节(0x08)
// 6. 增益配置(长曝光+短曝光均初始化为 1.0x)
// 长曝光增益(Normal 模式相同)
{0x3E06, 0x00}, // 长曝光 DIG GAIN=1x
{0x3E07, 0x80}, // 长曝光 DIG FINE GAIN=1x
{0x3E08, 0x00}, // 长曝光 ANA GAIN=1x
{0x3E09, 0x10}, // 长曝光 ANA FINE GAIN=1x
// 短曝光增益(HDR 专属)
{0x3E10, 0x00}, // 短曝光 DIG GAIN=1x
{0x3E11, 0x80}, // 短曝光 DIG FINE GAIN=1x
{0x3E12, 0x00}, // 短曝光 ANA GAIN=1x
{0x3E13, 0x10}, // 短曝光 ANA FINE GAIN=1x
// 7. 曝光配置(初始最小曝光,长曝光=2 半行,短曝光=2 半行)
{0x3E00, 0x00}, // 长曝光高 4 位
{0x3E01, 0x00}, // 长曝光中 8 位
{0x3E02, 0x10}, // 长曝光低 4 位(总=2 半行)
{0x3E22, 0x00}, // 短曝光低 4 位
{0x3E04, 0x02}, // 短曝光中 4 位
{0x3E05, 0x00}, // 短曝光高 4 位(总=2 半行)
// 8. 其他辅助配置(降噪、BLC 等,与线性模式保持一致)
{0x3301, 0x07}, // 模拟前端配置默认
{0x3333, 0x10}, // 降噪配置默认
{0x3902, 0xC0}, // BLC 自动使能
{0x3908, 0x40}, // BLC 最小值默认
// 9. 退出睡眠模式+稳定延迟
{0x0100, 0x01}, // 退出睡眠模式,开始输出图像
{0xFFFF, 0x0A}, // 延迟 10ms(确保 HDR 配置生效)
};
//sc200pc
// 镜像/倒置控制(参考表2-8):bit[2:1]镜像,bit[6:5]倒置,默认关闭
const static I2C_ARRAY mirror_reg[] =
{
{0x3221, 0x00}, // 保持默认:镜像关、倒置关(需开启时改对应位,如镜像开=0x06,倒置开=0x60)
};
typedef struct {
short reg;
char startbit;
char stopbit;
} COLLECT_REG_SET;
//sc200pc
// 普通模式/长曝光增益寄存器(参考表2-4、2-5):初始1.0x增益
const static I2C_ARRAY gain_reg[] = {
{0x3e06, 0x00}, // DIG GAIN(数字增益):0x00=1x
{0x3e07, 0x80}, // DIG FINE GAIN(数字精细增益):0x80=1x
{0x3e08, 0x00}, // ANA GAIN(模拟增益):0x00=1x
{0x3e09, 0x10}, // ANA FINE GAIN(模拟精细增益):0x10=1x(表2-5默认)
};
//sc200pc
// HDR短曝光增益寄存器(参考表2-4、2-5):初始1.0x增益
const static I2C_ARRAY gain_reg_HDR_SEF[] = {
{0x3e10, 0x00}, // HDR短曝光 DIG GAIN:0x00=1x
{0x3e11, 0x80}, // HDR短曝光 DIG FINE GAIN:0x80=1x
{0x3e12, 0x00}, // HDR短曝光 ANA GAIN:0x00=1x
{0x3e13, 0x10}, // HDR短曝光 ANA FINE GAIN:0x10=1x
};
//sc200pc
// 普通模式曝光寄存器(参考表2-3):初始最小曝光(2半行)
const static I2C_ARRAY expo_reg[] = { // max expo line = 2*VTS -7(数据手册标注)
{0x3e00, 0x00},// expo[20:17]:高4位
{0x3e01, 0x00}, // expo[16:8]:中8位
{0x3e02, 0x10}, // expo[7:4]:低4位(总曝光=2半行,对应最小曝光)
};
//sc200pc
// HDR短曝光曝光寄存器(参考表2-3):初始最小曝光(2半行)
const static I2C_ARRAY expo_reg_HDR_SEF[] = {
{0x3e22, 0x00}, // expo[3:0]:低4位
{0x3e04, 0x02}, // expo[7:4]:中4位
{0x3e05, 0x00}, // 保留位(默认0x00)
};
//sc200pc
// 帧长(VTS)寄存器(参考表2-10):默认30fps时VTS=0x04B0(1168行)
const static I2C_ARRAY vts_reg[] = {
{0x320e, 0x04}, // VTS高7位(0x04B0的高7位=0x04)
{0x320f, 0xB0}, // VTS低8位(0x04B0的低8位=0xB0)
};
//sc200pc
// 最大短曝光寄存器(参考表2-2):默认0x0020(32半行)
const static I2C_ARRAY max_short_exp_reg[] = {
{0x3e23, 0x00}, // 最大短曝光高8位
{0x3e24, 0x20}, // 最大短曝光低8位(数据手册默认值)
};
//sc200pc
// 温度补偿寄存器(参考数据手册5799地址配置):默认关闭补偿
const static I2C_ARRAY temperature_reg_1[] = {
{0x5799, 0x00}, // 保持默认:无温度补偿(高增益时可改0x07开启)
};
3.核心功能函数修改
(1)Power ON/OFF函数
在验证并点亮摄像头阶段,上下电函数无疑是最为重要的,如果上下电函数没写好没写对,首先摄像头的I2C就没办法通信,没办法通信也就没办法写寄存器配置,摄像头自然亮不了。所以我们先重点关注传感器的上下电时序

我们可以看到一个先后的顺序,在时钟开启后DOVDD、DVDD、AVDD可以同时上电也可以有略微的先后,然后拉高复位引脚正式进入工作的状态,重点注意T4>4ms。当知道了这个顺序之后,我们再来看看平台的控制函数,通过平台封装的函数可以对电源以及复位引脚进行控制。

修改如下:
static int pCus_poweron(ss_cus_sensor *handle, u32 idx)
{
printk(KERN_EMERG "%s start!!!!!!!!!!", __FUNCTION__);
ISensorIfAPI *sensor_if = handle->sensor_if_api;
SENSOR_DMSG("[%s] ", __FUNCTION__);
//RST拉低(进入复位):调用Reset(POS)→输出低电平(符合官方POS=低电平)
SENSOR_DMSG("[%s] reset low (active)\n", __FUNCTION__);
sensor_if->Reset(idx, CUS_CLK_POL_NEG); // POS→低电平(复位有效)
SENSOR_USLEEP(1000); // 复位稳定延时
sensor_if->PowerOff(idx, CUS_CLK_POL_POS );
// 配置MIPI IO Pad(lane数从配置获取,保持不变)
sensor_if->SetIOPad(idx, handle->sif_bus, handle->interface_attr.attr_mipi.mipi_lane_num);
// CSI时钟172MHz
sensor_if->SetCSI_Clk(idx, CUS_CSI_CLK_172M);
// 配置MIPI lane数+虚拟通道数
sensor_if->SetCSI_Lane(idx, handle->interface_attr.attr_mipi.mipi_lane_num, 1);
// MIPI长包类型
sensor_if->SetCSI_LongPacketType(idx, 0, 0x1C00, 0);
// 开启MCLK(EXTCLK需在电源前就绪,SC200PC要求)
sensor_if->MCLK(idx, 1, handle->mclk);
SENSOR_DMSG("[%s] MCLK enabled (EXTCLK ready)\n", __FUNCTION__);
SENSOR_USLEEP(1000); // MCLK稳定延时
//PDN拉低(开启电源):调用PowerOff→输出低电平
SENSOR_DMSG("[%s] power high (on)\n", __FUNCTION__);
sensor_if->PowerOff(idx, !CUS_CLK_POL_POS); // NEG→低电平(退出掉电)
CamOsMsSleep(1);
//RST拉高(退出复位)
SENSOR_DMSG("[%s] reset high (release)\n", __FUNCTION__);
sensor_if->Reset(idx, !CUS_CLK_POL_NEG);
CamOsMsSleep(1);
SENSOR_DMSG("[%s] poweron success\n", __FUNCTION__);
return SUCCESS;
}
这里尤其看仔细了别把复位函数sensor_if->Reset的赋值搞反了,如果弄反了复位不正确就会导致I2C通信不上,之前我误以为是摄像头模组损坏导致I2C不响应,但实际上是复位不正确,后来通过抓波才发现复位引脚不正确,应该是从低变高随后就一直是高电平了,以下是我当时记录的不正确的波形(绿色线就是rst线,粉色线是mclk)。
下电时序也是同理

修改如下:
static int pCus_poweroff(ss_cus_sensor *handle, u32 idx)
{
printk(KERN_EMERG "%s poweroff start!!!!!!!!!!", __FUNCTION__);
ISensorIfAPI *sensor_if = handle->sensor_if_api;
SENSOR_DMSG("[%s] ", __FUNCTION__);
SENSOR_DMSG("[%s] power low\n", __FUNCTION__);
sensor_if->PowerOff(idx, CUS_CLK_POL_NEG);
sensor_if->Reset(idx, CUS_CLK_POL_NEG);
CamOsMsSleep(1);
sensor_if->SetCSI_Clk(idx, CUS_CSI_CLK_DISABLE);
sensor_if->MCLK(idx, 0, handle->mclk);
printk(KERN_EMERG "%s poweroff finish!!!!!!!!!!", __FUNCTION__);
return SUCCESS;
}
(2)普通模式初始化函数
这里让SC200PC 进入 "200 万像素 + 30fps+MIPI 2-lane" 的常规线性工作模式完成传感器从硬件复位到参数配置的全流程,为后续 AE/AGC 等动态调节打下基础,适用于常规预览、普通拍照等无需 HDR 的基础成像场景。
static int pCus_init_mipi2lane_linear_2M30fps(ss_cus_sensor *handle)
{
printk(KERN_EMERG "%s pCus_init_mipi2lane_linear_2M30fps start!!!!!!!!!!", __FUNCTION__);
sc200ai_params *params = (sc200ai_params *)handle->private_data;
//SENSOR_DMSG("\n\n[%s]", __FUNCTION__);
int i,cnt;
//ISensorIfAPI *sensor_if = (handle->sensor_if_api);
for(i=0;i< ARRAY_SIZE(Sensor_init_table_2M30fps);i++)
{
if(Sensor_init_table_2M30fps[i].reg==0xffff)
{
SENSOR_MSLEEP(Sensor_init_table_2M30fps[i].data);
}
else
{
cnt = 0;
while(SensorReg_Write(Sensor_init_table_2M30fps[i].reg, Sensor_init_table_2M30fps[i].data) != SUCCESS)
{
cnt++;
SENSOR_DMSG("Sensor_init_table -> Retry %d...\n",cnt);
if(cnt>=10)
{
SENSOR_DMSG("[%s:%d]Sensor init fail!!\n", __FUNCTION__, __LINE__);
return FAIL;
}
SENSOR_MSLEEP(10);
}
}
}
pCus_SetOrien(handle, params->orit);
params->tVts_reg[0].data = (params->expo.vts >> 8) & 0x00ff;
params->tVts_reg[1].data = (params->expo.vts >> 0) & 0x00ff;
printk(KERN_EMERG "%s pCus_init_mipi2lane_linear_2M30fps finish!!!!!!!!!!", __FUNCTION__);
return SUCCESS;
}
(3)帧率设置函数
static int pCus_SetFPS(ss_cus_sensor *handle, u32 fps)
{
printk(KERN_EMERG "%s pCus_SetFPS start!!!!!!!!!!", __FUNCTION__);
u32 vts=0;
sc200ai_params *params = (sc200ai_params *)handle->private_data;
u32 max_fps = handle->video_res_supported.res[handle->video_res_supported.ulcur_res].u16max_fps;
u32 min_fps = handle->video_res_supported.res[handle->video_res_supported.ulcur_res].u16min_fps;
if(fps>=min_fps && fps <= max_fps){
params->expo.fps = fps;
params->expo.vts= (vts_30fps*max_fps)/fps;
}else if((fps >= (min_fps*1000)) && (fps <= (max_fps*1000))){
params->expo.fps = fps;
params->expo.vts= (vts_30fps*(max_fps*1000))/fps;
}else{
SENSOR_DMSG("[%s] FPS %d out of range.\n",__FUNCTION__,fps);
return FAIL;
}
if(params->expo.line > 2* (params->expo.vts) -10){
vts = (params->expo.line + 11)/2;
}else{
vts = params->expo.vts;
}
params->tVts_reg[0].data = (vts >> 8) & 0x00ff;
params->tVts_reg[1].data = (vts >> 0) & 0x00ff;
params->reg_dirty = true;
printk(KERN_EMERG "%s pCus_SetFPS finish!!!!!!!!!!", __FUNCTION__);
return SUCCESS;
}
(4)增益设置函数
static int pCus_SetAEGain(ss_cus_sensor *handle, u32 gain) {
printk(KERN_EMERG "%s pCus_SetAEGain start!!!!!!!!!!", __FUNCTION__);
sc200ai_params *params = (sc200ai_params *)handle->private_data;
u8 i=0;
u32 target_gain = gain; // 目标增益(1x=1024)
u32 gain_x_1024 = target_gain; // 用1024倍整数替代浮点(1x=1024,2x=2048...)
// 寄存器临时缓存
I2C_ARRAY gain_reg_temp[] = {0};
I2C_ARRAY temperature_reg_1_temp[] = {{0x5799, 0x00}};
memcpy(gain_reg_temp, params->tGain_reg, sizeof(gain_reg));
memcpy(temperature_reg_1_temp, params->tTemperature_reg_1, sizeof(temperature_reg_1_temp));
// 1. 增益范围限制(1x~248x → 1024~248×1024)
if (target_gain <= 1024) target_gain = 1024;
else if (target_gain > SENSOR_MAX_GAIN * 1024) target_gain = SENSOR_MAX_GAIN * 1024;
gain_x_1024 = target_gain;
// 2. 初始化增益寄存器(默认1x)
u8 ana_gain = 0x00; // 0x3e08: ANA GAIN(1x)
u8 ana_fine = 0x10; // 0x3e09: ANA FINE GAIN(1x)
u8 dig_gain = 0x00; // 0x3e06: DIG GAIN(1x)
u8 dig_fine = 0x80; // 0x3e07: DIG FINE GAIN(1x)
// 3. 优先调节模拟增益(低噪声),再调节数字增益(整数运算替代浮点)
u32 ana_max_1024 = 15 * 1024 + (0.5 * 1024); // 15.5x → 15872(15×1024+512)
if (gain_x_1024 <= ana_max_1024) {
// 仅用模拟增益:1x~15.5x(1024~15872)
if (gain_x_1024 <= 2048) { // ≤2x
ana_gain = 0x00;
ana_fine = 0x10 + ((gain_x_1024 - 1024) * 64) / 1024; // 步长1/64
} else if (gain_x_1024 <= 4096) { // 2x~4x
ana_gain = 0x01;
ana_fine = 0x10 + ((gain_x_1024 - 2048) * 64) / 1024;
} else if (gain_x_1024 <= 8192) { // 4x~8x
ana_gain = 0x03;
ana_fine = 0x10 + ((gain_x_1024 - 4096) * 64) / 1024;
} else { // 8x~15.5x
ana_gain = 0x07;
ana_fine = 0x10 + ((gain_x_1024 - 8192) * 64) / 1024;
if (ana_fine > 0x1F) ana_fine = 0x1F; // 最大15.5x
}
} else {
// 模拟增益到顶(15.5x=15872),开启数字增益
ana_gain = 0x07;
ana_fine = 0x1F; // 15.5x
u32 dig_gain_x_1024 = (gain_x_1024 * 1024) / ana_max_1024; // 数字增益需求(1x~16x)
// 数字增益换算:基础增益(1/2/4/8/16x)+ 精细调节
if (dig_gain_x_1024 <= 2048) { // ≤2x
dig_gain = 0x00;
dig_fine = 0x80 + ((dig_gain_x_1024 - 1024) * 128) / 1024; // 步长1/128
} else if (dig_gain_x_1024 <= 4096) { // 2x~4x
dig_gain = 0x01;
dig_fine = 0x80 + ((dig_gain_x_1024 - 2048) * 128) / 1024;
} else if (dig_gain_x_1024 <= 8192) { // 4x~8x
dig_gain = 0x03;
dig_fine = 0x80 + ((dig_gain_x_1024 - 4096) * 128) / 1024;
} else if (dig_gain_x_1024 <= 16384) { // 8x~16x
dig_gain = 0x07;
dig_fine = 0x80 + ((dig_gain_x_1024 - 8192) * 128) / 1024;
} else { // 最大16x
dig_gain = 0x0F;
dig_fine = 0x7F;
}
}
// 4. 写入寄存器(对应tGain_reg数组顺序:0=DIG,1=DIG_FINE,2=ANA,3=ANA_FINE)
params->tGain_reg[0].data = dig_gain & 0x0F; // 0x3e06
params->tGain_reg[1].data = dig_fine; // 0x3e07
params->tGain_reg[2].data = ana_gain; // 0x3e08
params->tGain_reg[3].data = ana_fine; // 0x3e09
// 5. 标记寄存器变更
for (i = 0; i < ARRAY_SIZE(params->tGain_reg); i++) {
if (params->tGain_reg[i].data != gain_reg_temp[i].data) {
params->reg_dirty = true;
break;
}
}
// 6. 高温降噪(gain >=30x → 30×1024=30720)
if (gain_x_1024 >= 30 * 1024) {
params->tTemperature_reg_1[0].data = 0x07;
} else if (gain_x_1024 <= 20 * 1024) {
params->tTemperature_reg_1[0].data = 0x00;
}
for (i = 0; i < ARRAY_SIZE(temperature_reg_1_temp); i++) {
if (params->tTemperature_reg_1[i].data != temperature_reg_1_temp[i].data) {
params->temperature_reg_1_dirty = true;
break;
}
}
printk(KERN_EMERG "%s pCus_SetAEGain finish!!!!!!!!!!", __FUNCTION__);
return SUCCESS;
}
(5)曝光设置函数
static int pCus_SetAEUSecs(ss_cus_sensor *handle, u32 us) {
printk(KERN_EMERG "%s pCus_SetAEUSecs start!!!!!!!!!!", __FUNCTION__);
int i;
u32 half_lines = 0,vts = 0;
sc200ai_params *params = (sc200ai_params *)handle->private_data;
I2C_ARRAY expo_reg_temp[] = { // max expo line vts-4!
{0x3e00, 0x00},//expo [20:17]
{0x3e01, 0x00}, // expo[16:8]
{0x3e02, 0x10}, // expo[7:0], [3:0] fraction of line
};
memcpy(expo_reg_temp, params->tExpo_reg, sizeof(expo_reg));
half_lines = (1000*us*2)/Preview_line_period; // Preview_line_period in ns
if(half_lines <= 1) half_lines=1;
if (half_lines > 2 * (params->expo.vts)-10) {
vts = (half_lines+11)/2;
}
else
vts=params->expo.vts;
params->expo.line = half_lines;
// SENSOR_DMSG("[%s] us %ld, half_lines %ld, vts %ld\n", __FUNCTION__, us, half_lines, params->expo.vts);
SENSOR_DMSG("[%s] us %u, half_lines %u, vts %u\n", __FUNCTION__, us, half_lines, params->expo.vts);
half_lines = half_lines<<4;
params->tExpo_reg[0].data = (half_lines>>16) & 0x0f;
params->tExpo_reg[1].data = (half_lines>>8) & 0xff;
params->tExpo_reg[2].data = (half_lines>>0) & 0xf0;
params->tVts_reg[0].data = (vts >> 8) & 0x00ff;
params->tVts_reg[1].data = (vts >> 0) & 0x00ff;
for (i = 0; i < ARRAY_SIZE(expo_reg); i++)
{
if (params->tExpo_reg[i].data != expo_reg_temp[i].data)
{
params->reg_dirty = true;
break;
}
}
printk(KERN_EMERG "%s pCus_SetAEUSecs finish!!!!!!!!!!", __FUNCTION__);
return SUCCESS;
}
(6)镜像 / 倒置设置函数
static int pCus_SetOrien(ss_cus_sensor *handle, CUS_CAMSENSOR_ORIT orit)
{
printk(KERN_EMERG "%s pCus_SetOrien start!!!!!!!!!!", __FUNCTION__);
sc200ai_params *params = (sc200ai_params *)handle->private_data;
switch(orit) {
case CUS_ORIT_M0F0:
params->tMirror_reg[0].data = 0;
params->orient_dirty = true;
break;
case CUS_ORIT_M1F0:
params->tMirror_reg[0].data = 6;
params->orient_dirty = true;
break;
case CUS_ORIT_M0F1:
params->tMirror_reg[0].data = 0x60;
params->orient_dirty = true;
break;
case CUS_ORIT_M1F1:
params->tMirror_reg[0].data = 0x66;
params->orient_dirty = true;
break;
default :
break;
}
params->orit = orit;
printk(KERN_EMERG "%s pCus_SetOrien finish!!!!!!!!!!", __FUNCTION__);
return SUCCESS;
}
(7)分辨率切换函数
static int pCus_SetVideoRes(ss_cus_sensor *handle, u32 res_idx)
{
printk(KERN_EMERG "%s pCus_SetVideoRes start!!!!!!!!!!", __FUNCTION__);
u32 num_res = handle->video_res_supported.num_res;
sc200ai_params *params = (sc200ai_params *)handle->private_data;
if (res_idx >= num_res) {
return FAIL;
}
switch (res_idx) {
case 0: //"1920x1080@30fps"
handle->video_res_supported.ulcur_res = 0;
handle->pCus_sensor_init = pCus_init_mipi2lane_linear_2M30fps;
params->expo.vts = vts_30fps;
params->expo.fps = 30;
break;
default:
break;
}
printk(KERN_EMERG "%s pCus_SetVideoRes finish!!!!!!!!!!", __FUNCTION__);
return SUCCESS;
}
到此为止我们对于核心功能函数的替换修改也完成了,根据平台的说明在最后将这些函数一股脑地注册进平台回调,我们的摄像头就可以出流了!!

四、出视频流应用程序编写
到此时我们完成了对驱动的编写,通过编译我们得到了.ko驱动文件,此时我们要通过平台的VIF功能模块编写一个应用程序让摄像头成功出流得到raw原始图像。这里注意平台的MI架构文档说明是怎么做的,跟着来就行了。

代码流程:
| 阶段 | 核心操作 | 目的 |
|---|---|---|
| 初始化准备 | 头文件引入、变量 / 结构体定义、参数赋值 | 为配置提供基础数据 |
| 系统启动 | MI_SYS_Init(0) | 初始化 SDK 核心资源 |
| Sensor 配置 | 禁用→关闭平面模式→启用 | 让 Sensor 输出合规的 MIPI 数据 |
| VIF 配置 | 创建设备组→配置设备属性→启用设备→配置端口→启用端口→设置缓存深度 | 让 VIF 能接收 / 输出 Sensor 数据 |
| 运行 / 释放 | 死循环保持运行(资源释放仅为理论流程) | 持续接收数据,保证链路稳定 |
应用程序:
#include <stdio.h>
#include "mi_sys.h"
#include "mi_vif.h"
#include "mi_sensor.h"
#include "mi_vif_datatype.h"
#include <string.h>
int main(int argc, char **argv)
{
MI_SNR_PADID eSnrPadId = 0;
MI_VIF_GROUP GroupId = 0;
MI_S32 s32Ret = 0;
MI_VIF_DEV vifDev =0;
MI_VIF_PORT vifPort = 0;
MI_SYS_ChnPort_t stChnPort;
MI_VIF_GroupAttr_t stGroupAttr;
MI_VIF_DevAttr_t stVifDevAttr;
MI_VIF_OutputPortAttr_t stVifPortInfo;
memset(&stGroupAttr, 0x0, sizeof(MI_VIF_GroupAttr_t));
stGroupAttr.eIntfMode = E_MI_VIF_MODE_MIPI;
memset(&stVifDevAttr, 0x0, sizeof(MI_VIF_DevAttr_t));
stVifDevAttr.stInputRect.u16X = 0;
stVifDevAttr.stInputRect.u16Y = 0;
stVifDevAttr.stInputRect.u16Width = 1920;
stVifDevAttr.stInputRect.u16Height = 1080;
stVifDevAttr.eInputPixel = (MI_SYS_PixelFormat_e)35;
memset(&stVifPortInfo, 0, sizeof(MI_VIF_OutputPortAttr_t));
stVifPortInfo.stCapRect.u16X = 0;
stVifPortInfo.stCapRect.u16Y = 0;
stVifPortInfo.stCapRect.u16Width = 1920;
stVifPortInfo.stCapRect.u16Height = 1080;
stVifPortInfo.stDestSize.u16Width = 1920;
stVifPortInfo.stDestSize.u16Height = 1080;
stVifPortInfo.ePixFormat = (MI_SYS_PixelFormat_e)35;
memset(&stChnPort, 0x0, sizeof(MI_SYS_ChnPort_t));
stChnPort.eModId=E_MI_MODULE_ID_VIF;
stChnPort.u32DevId=vifDev;
stChnPort.u32ChnId=0;
stChnPort.u32PortId=vifPort;
MI_SYS_Init(0);
s32Ret = MI_SNR_Disable(eSnrPadId);
if(s32Ret != MI_SUCCESS)
{
printf("disable sensor:%d failed\n", eSnrPadId);
goto EXIT;
}
else
{
printf("disable sensor:%d success\n", eSnrPadId);
}
s32Ret = MI_SNR_SetPlaneMode(eSnrPadId, FALSE);
if(s32Ret != MI_SUCCESS)
{
printf("set plane mode :%d failed\n", eSnrPadId);
goto EXIT;
}
else
{
printf("set plane mode:%d success\n", eSnrPadId);
}
s32Ret = MI_SNR_Enable(eSnrPadId);
if(s32Ret != MI_SUCCESS)
{
printf("enable sensor:%d failed\n", eSnrPadId);
goto EXIT;
}
else
{
printf("enable sensor:%d success\n", eSnrPadId);
}
s32Ret = MI_VIF_CreateDevGroup(GroupId, &stGroupAttr);
if(s32Ret != MI_SUCCESS)
{
printf("create group id:%d failed\n", GroupId);
goto EXIT;
}
else
{
printf("create group id:%d success\n", GroupId);
}
s32Ret = MI_VIF_SetDevAttr(vifDev, &stVifDevAttr);
if(s32Ret != MI_SUCCESS)
{
printf("set vif dev:%d failed\n",vifDev);
goto EXIT;
}
else
{
printf("set vif dev:%d success\n",vifDev);
}
s32Ret = MI_VIF_EnableDev(vifDev);
if(s32Ret != MI_SUCCESS)
{
printf("enable vif dev:%d failed\n",vifDev);
goto EXIT;
}
else
{
printf("enable vif dev:%d success\n",vifDev);
}
s32Ret = MI_VIF_SetOutputPortAttr(vifDev, vifPort, &stVifPortInfo);
if(s32Ret != MI_SUCCESS)
{
printf("set vif dev:%d port:%d failed\n",vifDev,vifPort);
goto EXIT;
}
else
{
printf("set vif dev:%d port:%d success\n",vifDev,vifPort);
}
s32Ret = MI_VIF_EnableOutputPort(vifDev, vifPort);
if(s32Ret != MI_SUCCESS)
{
printf("enable vif dev:%d port:%d failed\n",vifDev,vifPort);
goto EXIT;
}
else
{
printf("enable vif dev:%d port:%d success\n",vifDev,vifPort);
}
s32Ret = MI_SYS_SetChnOutputPortDepth(0, &stChnPort, 1, 4);
if(s32Ret != MI_SUCCESS)
{
printf("set vif port depth failed\n");
goto EXIT;
}
else
{
printf("set vif port depth success\n");
}
while(1){}
s32Ret = MI_VIF_DisableOutputPort(vifDev, vifPort);
if(s32Ret != MI_SUCCESS)
{
printf("disable vif dev:%d port:%d failed\n",vifDev,vifPort);
goto EXIT;
}
s32Ret = MI_VIF_DisableDev(vifDev);
if(s32Ret != MI_SUCCESS)
{
printf("disable vif dev:%d failed\n",vifDev);
goto EXIT;
}
s32Ret = MI_VIF_DestroyDevGroup(GroupId);
if(s32Ret != MI_SUCCESS)
{
printf("destroy vif group:%d failed\n",GroupId);
goto EXIT;
}
s32Ret = MI_SNR_Disable(eSnrPadId);
if(s32Ret != MI_SUCCESS)
{
printf("enable sensor:%d failed\n", eSnrPadId);
goto EXIT;
}
MI_SYS_Exit(0);
EXIT:
return s32Ret;
}
核心注意点:
此时注意最重要的一点,在设置设备属性的时候我们要设置正确的像素格式,查看传感器datasheet我们发现颜色格式是GBRG(从上面第一行的左一开始数)是标准的GB 型 Bayer 拜耳阵列
进入到代码中的MI_SYS_PixelFormat_e结构体,我们需要设置正确的枚举数值,看下官方是如何定义的
typedef enum { E_MI_SYS_DATA_PRECISION_8BPP, E_MI_SYS_DATA_PRECISION_10BPP, E_MI_SYS_DATA_PRECISION_12BPP, E_MI_SYS_DATA_PRECISION_14BPP, E_MI_SYS_DATA_PRECISION_16BPP, E_MI_SYS_DATA_PRECISION_MAX, } MI_SYS_DataPrecision_e; typedef enum { E_MI_SYS_PIXEL_BAYERID_RG, E_MI_SYS_PIXEL_BAYERID_GR, E_MI_SYS_PIXEL_BAYERID_BG, E_MI_SYS_PIXEL_BAYERID_GB, E_MI_SYS_PIXEL_RGBIR_R0, E_MI_SYS_PIXEL_RGBIR_G0, E_MI_SYS_PIXEL_RGBIR_B0, E_MI_SYS_PIXEL_RGBIR_G1, E_MI_SYS_PIXEL_RGBIR_G2, E_MI_SYS_PIXEL_RGBIR_I0, E_MI_SYS_PIXEL_RGBIR_G3, E_MI_SYS_PIXEL_RGBIR_I1, E_MI_SYS_PIXEL_BAYERID_MAX, } MI_SYS_BayerId_e; typedef enum { E_MI_SYS_PIXEL_FRAME_YUV422_YUYV = 0, E_MI_SYS_PIXEL_FRAME_ARGB8888, E_MI_SYS_PIXEL_FRAME_ABGR8888, E_MI_SYS_PIXEL_FRAME_BGRA8888, E_MI_SYS_PIXEL_FRAME_RGB565, E_MI_SYS_PIXEL_FRAME_ARGB1555, E_MI_SYS_PIXEL_FRAME_ARGB4444, E_MI_SYS_PIXEL_FRAME_I2, E_MI_SYS_PIXEL_FRAME_I4, E_MI_SYS_PIXEL_FRAME_I8, E_MI_SYS_PIXEL_FRAME_YUV_SEMIPLANAR_422, E_MI_SYS_PIXEL_FRAME_YUV_SEMIPLANAR_420, E_MI_SYS_PIXEL_FRAME_YUV_SEMIPLANAR_420_NV21, E_MI_SYS_PIXEL_FRAME_YUV_TILE_420, E_MI_SYS_PIXEL_FRAME_YUV422_UYVY, E_MI_SYS_PIXEL_FRAME_YUV422_YVYU, E_MI_SYS_PIXEL_FRAME_YUV422_VYUY, E_MI_SYS_PIXEL_FRAME_YUV422_PLANAR, E_MI_SYS_PIXEL_FRAME_YUV420_PLANAR, E_MI_SYS_PIXEL_FRAME_FBC_420, E_MI_SYS_PIXEL_FRAME_RGB_BAYER_BASE, E_MI_SYS_PIXEL_FRAME_RGB_BAYER_NUM = E_MI_SYS_PIXEL_FRAME_RGB_BAYER_BASE + E_MI_SYS_DATA_PRECISION_MAX * E_MI_SYS_PIXEL_BAYERID_MAX - 1, E_MI_SYS_PIXEL_FRAME_RGB888, E_MI_SYS_PIXEL_FRAME_BGR888, E_MI_SYS_PIXEL_FRAME_GRAY8, E_MI_SYS_PIXEL_FRAME_RGB101010, E_MI_SYS_PIXEL_FRAME_RGB888_PLANAR, E_MI_SYS_PIXEL_FRAME_FORMAT_MAX, } MI_SYS_PixelFormat_e;这三个枚举是厂商 MI SDK 定义的像素格式相关标准枚举,用于统一描述图像数据的 "位深、排列方式、整体格式",是 Sensor/VIF/ISP 等模块之间数据格式匹配的核心依据:
枚举名 核心作用 MI_SYS_DataPrecision_e定义像素的位深(每个像素占多少位),如 8BPP(8 位)、10BPP(10 位),是 Bayer 格式的 "精度维度" MI_SYS_BayerId_e定义 Bayer(拜耳)阵列的排列方式(传感器原始图像的基础排列),如 RG/GR/BG/GB,是 Bayer 格式的 "排列维度" MI_SYS_PixelFormat_e所有像素格式的总枚举,包含 YUV、RGB、Bayer 等所有类型,是跨模块数据格式交互的 "统一标识" 在
MI_SYS_PixelFormat_e中,Bayer 格式并非直接枚举,而是通过 "基础值 + 位深索引 + 排列索引" 计算得出,核心规则:plaintext
Bayer格式枚举值 = E_MI_SYS_PIXEL_FRAME_RGB_BAYER_BASE + (位深索引 × BayerId类型数) + 排列索引确定各关键值的枚举索引
- E_MI_SYS_PIXEL_FRAME_RGB_BAYER_BASE :Bayer 格式的起始值,数
MI_SYS_PixelFormat_e的枚举项可得其值为20(前 20 个是 YUV/RGB 等非 Bayer 格式);- 位深索引 (
MI_SYS_DataPrecision_e):8BPP=0、10BPP=1、12BPP=2...;- BayerId 类型数 :
MI_SYS_BayerId_e中有效排列类型为 12 种(0~11);- 排列索引 (
MI_SYS_BayerId_e):RG=0、GR=1、BG=2、GB=3...。场景 1:8 位位深(8BPP)
值 = 20 + (0 × 12) + 3 = 23对应代码配置:
stVifDevAttr.eInputPixel = (MI_SYS_PixelFormat_e)23;场景 2:10 位位深(10BPP,传感器更常用)
值 = 20 + (1 × 12) + 3 = 35对应代码配置:
stVifDevAttr.eInputPixel = (MI_SYS_PixelFormat_e)35;由于我的摄像头默认是10bit的,所以这里我就选择枚举值35
现象展示:
我们先加载驱动,发现能加载成功

然后我们跑一下这个VIF demo程序,此时由于终端占用,我们打开电脑的shell的adb工具另开一个交互终端,输入echo dump 0 0 /tmp > /proc/mi_modules/mi_vif/mi_vif抓取raw图像并保存到板端tmp目录,再通过adb pull将图片传输到电脑桌面上。

然后我们利用7yuv软件打开这个raw图片进行查看,输入正确的格式设置即可查看图像(灰色是正常的因为是raw图像)
展示图像:

五、根据实际RAW图像再修改驱动
虽然出现了图像,但是我明明设置的是1920x1080的画面,但是出来只有四分之一或者二分之一,很明显画面的大小不对,经过很细节的检查发现是**VTS的问题,**VTS我原本设置的为1168,但实际上这是不对的!应该是2400!
VTS(Vertical Total Size)中文叫垂直总帧数 / 帧长,是图像传感器 / 视频传输中描述「一帧图像在垂直方向的总行数」的关键参数我们来看一下官方的说法。


结合自己的传感器数据手册,重新梳理VTS的设置
第一步:从数据手册提取 3 个 "必须找到" 的核心参数
数据手册(Sensor 规格书 / 寄存器手册)里,和 VTS 相关的核心参数有 3 个,优先找这些(按优先级排序):
| 参数名称 | 手册里的标注方式 | 作用 |
|---|---|---|
| 1. 目标帧率对应的推荐 VTS | 如 "1080P@30fps VTS (0x320E/0x320F):2400" | 手册直接给出的实测推荐值(优先级最高,不用计算,直接用) |
| 2. 每行耗时 H_time | 如 "Line Period:13889ns" | 硬件固定的每行耗时(由 PCLK/HTS 决定,手册会直接标或可推导) |
| 3. PCLK+HTS | 如 "PCLK:144MHz;HTS (0x3208/0x3209):2000" | 像素时钟 + 水平总像素(若没标 H_time,可通过\(H_{time}=HTS/PCLK\) |
第二步:用手册参数计算 VTS(两种方式,结果一致)
结合你 30fps 的需求,用手册参数代入公式计算,验证 2400 的合理性:
方式 1:用手册标注的 H_time 计算(最直接)
如果手册里标了 "1080P 模式下 Line Period(H_time)=13889ns",代入 FPS 调节核心公式:

→ 这就是我 30fps 对应的 VTS 值,和实际生效的 2400 完全匹配
方式 2:用手册的 PCLK+HTS 计算(若没标 H_time)
如果手册里没直接标 H_time,但标了:
- PCLK(像素时钟)=144MHz(144×10⁶ Hz);
- HTS(水平总像素)=2000(1920 有效像素 + 80 消隐)。
先算 H_time:

再代入 VTS 公式,结果还是≈2400
修正后的驱动宏定义代码:
// ========== 修正后(适配30fps VTS=2400) ==========
#define Preview_line_period 13889 // 30fps、VTS=2400时每行耗时:1e9/(30×2400)≈13889 ns
#define vts_30fps 2400 // sc200pc 30fps正确VTS值(替代原1168,画面正常)
#define Preview_line_period_HDR 6944 // HDR半行时间=13889/2≈6944 ns(匹配手册半行调节步长)
#define vts_30fps_HDR 2400 // HDR模式30fps同样使用正确VTS=2400
#define Preview_MAX_FPS 60 // 最大预览帧率(60fps对应VTS=1200,传感器默认值)
#define Preview_MIN_FPS 3 // 最小预览帧率(无需调整)
#define Preview_CROP_START_X 8 // sc200pc有效区域列偏移(数据手册表2-9,不变)
#define Preview_CROP_START_Y 8 // sc200pc有效区域行偏移(数据手册表2-9,不变)
修正后跑了一个屏幕实时预览sensor画面的demo来验证,没有问题画面正常了,至此完成移植








