Camera OTP内置校准方法
本文章主要HI556进行的内置校准总结整理而来.
约定: 注解及/**/中为对相应操作的注解
概要:添加一个Sensor的OTP驱动主要需要完成Kernel, Vendor层的工作.如下:
一,Kernel部分:
1,Kernel部分dts文件主要完成,包括OTP的上电,对memory map表的配置,eeprom的指定等.
2,另根据sensor的OTP读写情况,是否添加读取OTP读取C代码.
二,Vendor部分:
1,Vendor部分主要完成对从kernel读取到的数据的格式化校准工作.
2,需要根据模组厂商提供的OTP数据对读到的数据进行格式化校准,从而得到最终需要的数据填入平台端提供的接口,以供平台校准写入Sensor进行内置校准.或写入平台端进行平台校准.
3,打开Sensor中配置OTP.
具体步骤参考
一 OTP数据的读取.
在进行OTP数据校准之前,首先需要读取得到模组厂商写入模组内部的OTP数据,这部分工作主要在kernel中进行.需要完成的工作为:
1, dts文件的配置.主要包括memory map 表的配置填写,eeprom上下电的配置,eeprom name的填写以及i2c地址的正确填写.
A.map表的配置
map表是反应数据在OTP中的存储及对OTP进行读写操作的直观表现.在内核代码msm_eeprom.c函数msm_eeprom_parse_memory_map中会去获取dts里配置的memory map情况,然后按照获取到的memory情况在函数read_eeprom_memory中去读取eeprom的值并存入buffer中.所以对OTPmap表的配置就直接影响到能否读到OTP数据以及读到的数据是否正确.
/*指定总共有一个block*/
qcom,num-blocks = <1>;
/*配置block0相关的参数*/
qcom,page0 = <0 0x0100 2 0x01 1 1>;
qcom,poll0 = <0 0x0 2 0x0 1 0>;
qcom,mem0 = <0x150C 0x401 2 0x0 1 0>; /*OTP中的数据大小配置,将用于申请对应大小的内存*/
注: 内核说明文档对对dts文件map表的配置做了详细介绍,具体阅读下边文档:
kernel/msm-3.18/Documentation/devicetree/bindings/media/video/msm-eeprom.txt
B. eeprom上下电的配置.
eeprom在进行读写前后内核代码中回去进行上下电的操作,而因为eeprom不论是内置在sensor还是外挂的memory都是直接接在sensor上的,与sensor共用I2C总线,共用电源系统,所以只需要原封不动的按照sensor的供电配置就可以.
主要电源配置:
cam_vdig-supply = <&pm8916_l2>; /*数字电,根据硬件设置*/
cam_vio-supply = <&pm8916_l6>; /*IO电,根据硬件设置*/
cam_vana-supply = <&pm8916_l10>; /*模拟电,根据硬件设置*/
/*为以上供电电源指定name*/
qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
qcom,cam-custom-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
/*为以上电源指定电压*/
qcom,cam-vreg-type = <0 0 0>;
qcom,cam-vreg-min-voltage = <1200000 0 2800000>;
qcom,cam-vreg-max-voltage = <1200000 0 2850000>;
/*以下为GPIO控制的硬件设置参数, gpios为的使用情况*/
gpios = <&msm_gpio 26 0>, /*gpio26为clk*/
<&msm_gpio 17 0>,
<&msm_gpio 34 0>;
qcom,gpio-reset = <1>; /*gpio17为reset*/ /*clk在此处不许配置*/
qcom,gpio-standby = <2>; /*gpio34为standby*/
qcom,gpio-req-tbl-num = <0 1 2>; /*内核代码将根据这个tbl_num来判断有几个gpio*/
qcom,gpio-req-tbl-flags = <1 0 0>;
qcom,gpio-req-tbl-label = "CAMIF_MCLK",/*gpio lab,在gpio_request时候使用*/
"CAM_RESET1",
"CAM_STANDBY";
注:这里的几路电源都是PMIC的LDO供电,不同项目硬件连接不用,可能是外接LOD采用gpio来进行控制,那么就需要在下边gpio这里添加供电情况.
C. I2C地址及name的指定
/*0x50为这颗sensor的I2C地址,指定eeprom_name为hi556_DMEGC */
eeprom1: qcom,eeprom@0x50 {
cell-index = <0>;
reg = <0x50>;
compatible = "qcom,eeprom";
qcom,eeprom-name = "hi556_DMEGC";
qcom,slave-addr = <0x50>
注:内置OTP的I2C地址与sensor的地址相同,如果是外挂的OTP则不同,另外这里指定的name需要与用户空间,也就是vendor下指定的name相同.
D. 在对应的Camera的dts节点中指定eeprom.
qcom,eeprom-src = <&eeprom0 &eeprom1>;
注:因为这里同一个sensor存在一供与二供,所以这里兼通一供二供OTP数据在这里有两个,分别为一供和二供.
具体代码提交为:
--- a/arch/arm/boot/dts/qcom/msm8909-camera-sensor-cdp.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8909-camera-sensor-cdp.dtsi
@@ -85,6 +85,57 @@
qcom,clock-rates = <24000000 0>;
};
+ eeprom1: qcom,eeprom@0x50 {
+ cell-index = <0>;
+ reg = <0x50>;
+ compatible = "qcom,eeprom";
+ qcom,eeprom-name = "hi556_DMEGC";
+ qcom,slave-addr = <0x50>;
+ qcom,cci-master = <0>;
+ qcom,num-blocks = <1>;
+
+ qcom,page0 = <0 0x0100 2 0x01 1 1>;
+ qcom,poll0 = <0 0x0 2 0x0 1 0>;
+ //qcom,mem0 = <0x36 0x401 2 0x0 1 0>;
+ qcom,mem0 = <0x150C 0x401 2 0x0 1 0>;
+
+ cam_vdig-supply = <&pm8916_l2>;
+ cam_vio-supply = <&pm8916_l6>;
+ cam_vana-supply = <&pm8916_l10>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
+ qcom,cam-custom-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
+ qcom,cam-vreg-type = <0 0 0>;
+ qcom,cam-vreg-min-voltage = <1200000 0 2800000>;
+ qcom,cam-vreg-max-voltage = <1200000 0 2850000>;
+ qcom,cam-vreg-op-mode = <200000 0 80000>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk0_default &cam_sensor_rear_default>;
+ pinctrl-1 = <&cam_sensor_mclk0_sleep &cam_sensor_rear_sleep>;
+ gpios = <&msm_gpio 26 0>,
+ <&msm_gpio 17 0>,
+ <&msm_gpio 34 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET1",
+ "CAM_STANDBY";
+ qcom,cam-power-seq-type = "sensor_vreg", "sensor_vreg",
+ "sensor_vreg", "sensor_clk", "sensor_gpio", "sensor_gpio",
+ "sensor_gpio", "sensor_gpio";
+ qcom,cam-power-seq-val = "cam_vio", "cam_vana", "cam_vdig", "sensor_cam_mclk",
+ "sensor_gpio_standby", "sensor_gpio_standby", "sensor_gpio_reset", "sensor_gpio_reset";
+ qcom,cam-power-seq-cfg-val = <1 1 1 24000000 0 1 0 1>;
+ qcom,cam-power-seq-delay = <0 0 0 10 5 1 1 5>;
+ status = "ok";
+ clocks = <&clock_gcc clk_mclk0_clk_src>,
+ <&clock_gcc clk_gcc_camss_mclk0_clk>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+
+
qcom,camera@0 {
cell-index = <0>;
compatible = "qcom,camera";
@@ -93,7 +144,7 @@
qcom,csid-sd-index = <0>;
qcom,mount-angle = <90>;
qcom,led-flash-src = <&led_flash0>;
- qcom,eeprom-src = <&eeprom0>;
+ qcom,eeprom-src = <&eeprom0 &eeprom1>;
cam_vdig-supply = <&pm8916_l2>;
2, 一般的OTP在读写前需要写一些命令进去才能开始正常的读写操作,比如需要设置输出使能等操作,这部分操作一般模组厂商给的OTP_Guide中会有介绍.需要阅读模组厂商给的OTP手册,根据手册来进行读取.这部分操作可以写在OTP的map表中,通过map表配置系统原有的方式读写,也可单独拿出来写入,在本例中单独拿出来在一个文件中进行来读写,在msm_eeprom中调用读函数,实际平台中原来的读写操作并没有读到数据.具体见下:
kernel/msm-3.18/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom_insensor_hi556.c
#include "msm_sd.h"
#include "msm_eeprom.h"
#include "msm_cci.h"
#include "msm_camera_io_util.h"
#include "msm_camera_i2c_mux.h"
#include
DEFINE_MSM_MUTEX(msm_eeprom_insensor_hi556_mutex);
struct sensor_otp_reg_t {
uint32_t addr;
uint32_t data;
};
/*读写前需要提前设置的一些参数,是否需要及需要写入什么参数跟模组厂商联系*/
struct sensor_otp_reg_t hi556_init_otp[] = {
{0x0e00, 0x0102}, //tg_pmem_sckpw/sdly
{0x0e02, 0x0102}, //tg_dmem_sckpw/sdly
{0x0e0c, 0x0100}, //tg_pmem_rom_dly
{0x27fe, 0xe000}, // firmware start address-ROM
{0x0b0e, 0x8600}, // BGR enable
{0x0d04, 0x0100}, // STRB(OTP Busy) output enable
{0x0d02, 0x0707}, // STRB(OTP Busy) output drivability
{0x0f30, 0x6e25}, // Analog PLL setting
{0x0f32, 0x7067}, // Analog CLKGEN setting
{0x0f02, 0x0106}, // PLL enable
{0x0a04, 0x0000}, // mipi disable
{0x0e0a, 0x0001}, // TG PMEM CEN anable
{0x004a, 0x0100}, // TG MCU enable
{0x003e, 0x1000}, // ROM OTP Continuous W/R mode enable
{0x0a00, 0x0100}, // Stream On
};
/*这里是兼容一供与二供做了下边这个宏来对一供二供进行区分*/
#define OTP_ALL_SIZE_JSL 5364 /*一供*/
#define OTP_ALL_SIZE_DMEGC 5388 /*二供*/
#define NAME_JSL "hi556_JSL"
#define NAME_DMEGC "hi556_DMEGC"
int hi556_insensor_read_otp_info(struct msm_eeprom_ctrl_t *e_ctrl, char *otp_name)
{
int rc = 0;
uint16_t data = 0;
uint16_t j;
uint8_t *memptr;
uint16_t otp_size = 0;
if (!e_ctrl) {
pr_err("%s e_ctrl is NULL\n", __func__);
return -EINVAL;
}
printk("%s : %d E\n", __func__, __LINE__);
/*指针指向在解析完OTP memory size后申请的内存buffer的地址*/
memptr = e_ctrl->cal_data.mapdata;
e_ctrl->i2c_client.addr_type = 2;
/*写入以上数据,不同sensor根据具体情况而定是否写及写入什么值*/
for (j = 0; j < sizeof(hi556_init_otp)/sizeof(struct sensor_otp_reg_t); j++) {
rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_write(&(e_ctrl->i2c_client), hi556_init_otp[j].addr,hi556_init_otp[j].data, 2);
if (rc < 0) {
pr_err("%s: hi556 otp init function failed\n", __func__);
return rc;
}
}
/*读写前根据OTP Guide进行的写操作*/
rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_write(&(e_ctrl->i2c_client), 0x0a02, 0x01, 1);
rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_write(&(e_ctrl->i2c_client), 0x0a00, 0x00, 1);
msleep(10);
rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_write(&(e_ctrl->i2c_client), 0x0f02, 0x00, 1);
rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_write(&(e_ctrl->i2c_client), 0x011a, 0x01, 1);
rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_write(&(e_ctrl->i2c_client), 0x011b, 0x09, 1);
rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_write(&(e_ctrl->i2c_client), 0x0d04, 0x01, 1);
rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_write(&(e_ctrl->i2c_client), 0x0d00, 0x07, 1);
rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_write(&(e_ctrl->i2c_client), 0x003e, 0x10, 1);
rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_write(&(e_ctrl->i2c_client), 0x0a00, 0x01, 1);
rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_write(&(e_ctrl->i2c_client), 0x10a, (0x401>>8) & 0xff, 1);
rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_write(&(e_ctrl->i2c_client), 0x10b, 0x401 & 0xff, 1);
rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_write(&(e_ctrl->i2c_client), 0x102, 0x01, 1);
/*为一供二供兼容做的判断*/
if(!strcmp(otp_name, NAME_JSL)){
otp_size = OTP_ALL_SIZE_JSL;
}else if(!strcmp(otp_name, NAME_DMEGC)){
otp_size = OTP_ALL_SIZE_DMEGC;
}
/*真正去循环读取OTP数据,根据OTP Guide知从0x108去读*/
for (j=0; j < otp_size; j++) {
rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_read(&(e_ctrl->i2c_client), 0x108, &data, 1);
/*将读入的数据存入buffer中*/
memptr[j] = data;
}
/*读完后写入其他数据的操作结束读操作*/
rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_write(&(e_ctrl->i2c_client), 0x0a00, 0x00, 1);
msleep(10);
rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_write(&(e_ctrl->i2c_client), 0x003f, 0x00, 1);
rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_write(&(e_ctrl->i2c_client), 0x0a00, 0x01, 1);
if (rc < 0) {
pr_err("%s: hi556 read otp info failed\n", __func__);
pr_err("%s : %d rc:%d \n", __func__, __LINE__,rc);
return rc;
}
printk("%s : %d \n", __func__, __LINE__);
return rc;
}
在配置好内核读写操作等可打开msm_eeprom.c中的log,从而判断读到的数据是否正确.
二,用户空间即vendor中的校准实现.
在kernel空间实现来对模组烧录OTP数据的读取工作,但真正的校准还没有进行,需要在vendor中去进行.在开机过程中sensor从vendor发起probe后会去ioctrl到内核空间,从而拿取到OTP的数据,需要做的是根据拿取到的数据作校准工作再回写如sensor(自校准),如果是采用平台校准则需要填写入平台端对应结构体,及将Golden模组对应的的数据写入chromatix文件中.
代码目录: vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/sensors/eeprom_libs/hi556_DMEGC/hi556_DMEGC_eeprom.c
1在OTP的vendor驱动中需要实现填充eeprom_lib_func_t结构体,其在open EEPROM库的时候打开,本例程中如下:
static eeprom_lib_func_t hi556_DMEGC_eeprom_lib_func_ptr = { /*实现结构体*/
.get_calibration_items = hi556_DMEGC_eeprom_get_calibration_items, /*函数指针必填,用于获取校准选项*/
.format_calibration_data = hi556_DMEGC_eeprom_format_calibration_data, /*必填,用于对读到的OTP数据进行计算等操作*/
.do_af_calibration = NULL,/*根据校准选项情况填写,如果是in_sensor,也就是自校准则为NULL,否则为平台校准填写平台端校准函数*/
.do_wbc_calibration = NULL, /*根据校准选项情况填写,如果是in_sensor,也就是自校准则为NULL,否则为平台校准填写平台端校准函数*/
.do_lsc_calibration = eeprom_lensshading_calibration, /*此项目LSC采用来平台校准,所以此处填写来平台端的校准函数*/
.do_dpc_calibration = NULL, /*根据校准选项情况填写,如果是in_sensor,也就是自校准则为NULL,否则为平台校准填写平台端校准函数*/
.get_dpc_calibration_info = NULL, /*根据校准选项情况填写,如果是in_sensor,也就是自校准则为NULL,否则为平台校准填写平台端校准函数*/
.get_raw_data = hi556_DMEGC_get_raw_data, /*根据校准选项情况填写,如果是in_sensor,也就是自校准这里必须填写用于获取校准数据的函数,此函数获取用于校准的存于数组的数据,否则为平台校准则填入NULL*/
};
2,实现校准选项函数. get_calibration_items
void hi556_DMEGC_eeprom_get_calibration_items(void *e_ctrl)
{
sensor_eeprom_data_t *ectrl = (sensor_eeprom_data_t *)e_ctrl;
eeprom_calib_items_t *e_items = &(ectrl->eeprom_data.items);
e_items->is_wbc = awb_present ? TRUE: FALSE; /*因为我们是AWB自校准,只是这里写成来这样,实际代码运行这里就是FALSE,因为是自校准不需要平台端做处理*/
e_items->is_afc = FALSE; /*没有做afc校准所以填FALSE*/
e_items->is_lsc = lsc_present ? TRUE: FALSE; /*有LSC,但会做判断,如果数据校验失败则不做LSC校准,所以这里并没有直接填TRUE或FALSE*/
e_items->is_dpc = FALSE;/*没有做dpc故填FALSE*/
e_items->is_insensor = insensor_present? TRUE: FALSE;/*sensor自校准责在此处填入TRUE,在xxx_session_init的时候会判断是否为in_sensor,如果是会将get_raw_data 获取的数组数据IOCTRL到内核写入sensor,实际代码运行这里就是TRUE*/
}
3,实现校准数据的函数. format_calibration_data
void hi556_DMEGC_eeprom_format_calibration_data(void *e_ctrl)
{
sensor_eeprom_data_t * ctrl = (sensor_eeprom_data_t *)e_ctrl;
module_info_t *module_info;
unsigned short rc = SENSOR_FAILURE;
unsigned char flag;
int CHECKDATA = 0;
char value[PROPERTY_VALUE_MAX];
DEBUG_INFO("Enter");
DEBUG_INFO("Total bytes in OTP buffer: %d", ctrl->eeprom_params.num_bytes);
/*判断拿到的buffer是否为空*/
if (!ctrl->eeprom_params.buffer || !ctrl->eeprom_params.num_bytes) {
DEBUG_INFO("Buff pointer %p buffer size %d", ctrl->eeprom_params.buffer,
ctrl->eeprom_params.num_bytes);
return;
}
/*这个g_reg_setting 结构体中存将用于写入sensor的校准数据,及数据类型等信息*/
g_reg_setting.addr_type = MSM_CAMERA_I2C_WORD_ADDR;
g_reg_setting.data_type = MSM_CAMERA_I2C_BYTE_DATA;
g_reg_setting.reg_setting = &g_reg_array[0];
g_reg_setting.size = 0;
g_reg_setting.delay = 0;
hi556_DMEGC_eeprom_format_moduleinfo(ctrl); /*获取模组信息*/
hi556_DMEGC_eeprom_format_wbdata(ctrl); /*格式化buffer中的AWB的数据,填充在g_reg_setting.reg_setting = &g_reg_array[0]; 数组中,见下方*/
hi556_DMEGC_eeprom_format_lscdata(ctrl); /*格式化buffer中的LSC的数据,用于平台端校准,见下方*/
/* For MMIGROUP TO CHECK EEPROM DATA */
if ((HI556_MI_CHECKDATA < 0) || (HI556_AWB_CHECKDATA < 0)) {
CHECKDATA = -1; // error
SERR("checkdata:mi=%d, awb=%d", HI556_MI_CHECKDATA, HI556_AWB_CHECKDATA);
SERR("eeprom data is not correct, please check with sensor vendor!!!");
}
}
4,实现AWB校准数据函数.
static void hi556_DMEGC_eeprom_format_wbdata(sensor_eeprom_data_t *e_ctrl)
{
awb_data_t *wb;
int r_Gain=0x100,g_Gain=0x100,b_Gain=0x100;
float R_Gain = 1.0,G_Gain = 1.0,B_Gain = 1.0;
int RG_golden,BG_golden,RG,BG;
DEBUG_INFO("Enter");
/*根据模组厂商拿到的OTP表对拿到的数据做check,及判断group*/
if (e_ctrl->eeprom_params.buffer[AWB_GROUD3_FLAG_OFFSET] == VALID_FLAG) {
if(!(hi556_DMEGC_checksum(e_ctrl->eeprom_params.buffer, AWB_GROUD3_FLAG_OFFSET , AWB_GROUD3_LAST_BYTE, AWB_GROUD3_CRC_OFFSET))){
SERR("failed:invalid AWB groud3 data. insensor AWB is not turn on");
HI556_AWB_CHECKDATA = -1;
return;
}
wb = (awb_data_t *)(e_ctrl->eeprom_params.buffer + AWB_GROUD3_OFFSET);
} else if (e_ctrl->eeprom_params.buffer[AWB_GROUD2_FLAG_OFFSET] == VALID_FLAG) {
if(!(hi556_DMEGC_checksum(e_ctrl->eeprom_params.buffer, AWB_GROUD2_FLAG_OFFSET , AWB_GROUD2_LAST_BYTE, AWB_GROUD2_CRC_OFFSET))){
SERR("failed:invalid AWB groud2 data. insensor AWB is not turn on");
HI556_AWB_CHECKDATA = -1;
return;
}
wb = (awb_data_t *)(e_ctrl->eeprom_params.buffer + AWB_GROUD2_OFFSET);
} else if (e_ctrl->eeprom_params.buffer[AWB_GROUD1_FLAG_OFFSET] == VALID_FLAG) {
if(!(hi556_DMEGC_checksum(e_ctrl->eeprom_params.buffer, AWB_GROUD1_FLAG_OFFSET , AWB_GROUD1_LAST_BYTE, AWB_GROUD1_CRC_OFFSET))){
SERR("failed:invalid AWB groud1 data. insensor AWB is not turn on");
HI556_AWB_CHECKDATA = -1;
return;
}
wb = (awb_data_t *)(e_ctrl->eeprom_params.buffer + AWB_GROUD1_OFFSET);
} else {
SERR("AWB : empty or invalid data");
HI556_AWB_CHECKDATA = -1;
return;
}
/*从数据中拿到模组的RG RG 及Golden的RG及BG的值*/
RG = ((wb->rg_high << 8) | wb->rg_low);
BG = ((wb->bg_high << 8) | wb->bg_low);
RG_golden = ((wb->rg_golden_high << 8) | wb->rg_golden_low);
BG_golden = ((wb->bg_golden_high << 8) | wb->bg_golden_low);
DEBUG_INFO("RG= 0x%x ,BG= 0x%x,RG_golden= 0x%x,BG_golden= 0x%x\n",RG,BG,RG_golden,BG_golden);
/*下边是根据拿到的模组RG BG值及Golden的RG BG值进行计算,判断需要增大还是减小对应的Gain,这个算法逻辑比较简单,看一下就能理解,一般in_sensor的AWB也都是采用这个逻辑进行,这里就不解释了*/
R_Gain = (float) RG_golden / RG;
B_Gain = (float) BG_golden / BG;
DEBUG_INFO("R_gain= %f ,G_gain= %f,B_gain= %f\n",R_Gain,G_Gain,B_Gain);
if(R_Gain < B_Gain)
{
if(R_Gain < 1.0)
{
B_Gain = B_Gain/R_Gain;
G_Gain = G_Gain/R_Gain;
R_Gain = 1.0;
}
}
else{
if(B_Gain < 1.0)
{
R_Gain = R_Gain/B_Gain;
G_Gain = G_Gain/B_Gain;
B_Gain = 1.0;
}
}
DEBUG_INFO("R_gain= %f ,G_gain= %f,B_gain= %f\n",R_Gain,G_Gain,B_Gain);
/*通过以上判断计算得到校准后需要的R,B,G的值,并还原为int类型*/
r_Gain = (int) (256 * R_Gain);
g_Gain = (int) (256 * G_Gain);
b_Gain = (int) (256 * B_Gain);
DEBUG_INFO("r_Gain= %d ,g_Gain= %d,b_Gain= %d\n",r_Gain,g_Gain,b_Gain);
DEBUG_INFO("r_Gain= 0x%x ,g_Gain= 0x%x,b_Gain= 0x%x\n",r_Gain,g_Gain,b_Gain);
/*最后将R,B,G计算校准后的值写入数组中,数组将在调用 get_raw_data得到并最终将写入sensor中(自校准),数组的地址为sensor用于对应AWB校准的地址(宏定义了其地址,如果不知可联系模组或sensor厂商)*/
g_reg_array[g_reg_setting.size].reg_addr = AWB_GGAIN1_REG_H;
g_reg_array[g_reg_setting.size].reg_data = g_Gain >> 8;
g_reg_setting.size++;
g_reg_array[g_reg_setting.size].reg_addr = AWB_GGAIN1_REG_L;
g_reg_array[g_reg_setting.size].reg_data = g_Gain & 0xff;
g_reg_setting.size++;
g_reg_array[g_reg_setting.size].reg_addr = AWB_GGAIN2_REG_H;
g_reg_array[g_reg_setting.size].reg_data = g_Gain >> 8;
g_reg_setting.size++;
g_reg_array[g_reg_setting.size].reg_addr = AWB_GGAIN2_REG_L;
g_reg_array[g_reg_setting.size].reg_data = g_Gain & 0xff;
g_reg_setting.size++;
g_reg_array[g_reg_setting.size].reg_addr = AWB_RGAIN_REG_H;
g_reg_array[g_reg_setting.size].reg_data = r_Gain >> 8;
g_reg_setting.size++;
g_reg_array[g_reg_setting.size].reg_addr = AWB_RGAIN_REG_L;
g_reg_array[g_reg_setting.size].reg_data = r_Gain & 0xff;
g_reg_setting.size++;
g_reg_array[g_reg_setting.size].reg_addr = AWB_BGAIN_REG_H;
g_reg_array[g_reg_setting.size].reg_data = b_Gain >> 8;
g_reg_setting.size++;
g_reg_array[g_reg_setting.size].reg_addr = AWB_BGAIN_REG_L;
g_reg_array[g_reg_setting.size].reg_data = b_Gain & 0xff;
g_reg_setting.size++;
}
5,LSC校准函数的实现.
static void hi556_DMEGC_eeprom_format_lscdata(sensor_eeprom_data_t *e_ctrl)
{
unsigned char flag;
lsc_data_t *LSC; /*定义来一个结构体来映射buffer的数据*/
int t;
/*先判断LSC数据的有效group及check是否有效*/
if (e_ctrl->eeprom_params.buffer[LSC_GROUP3_FLAG_OFFSET] == VALID_FLAG) {
if(!(hi556_DMEGC_checksum(e_ctrl->eeprom_params.buffer, LSC_GROUP3_FLAG_OFFSET , LSC_GROUP3_LAST_BYTE, LSC_GROUP3_CRC_OFFSET))){
SERR("failed:invalid LSC groud3 data. empty or invalid data");
HI556_MI_CHECKDATA = -1;
return;
}
LSC = (lsc_data_t *)(e_ctrl->eeprom_params.buffer + LSC_GROUP3_OFFSET);
} else if (e_ctrl->eeprom_params.buffer[LSC_GROUP2_FLAG_OFFSET] == VALID_FLAG) {
if(!(hi556_DMEGC_checksum(e_ctrl->eeprom_params.buffer, LSC_GROUP2_FLAG_OFFSET , LSC_GROUP2_LAST_BYTE, LSC_GROUP2_CRC_OFFSET))){
SERR("failed:invalid LSC groud2 data. empty or invalid data");
HI556_MI_CHECKDATA = -1;
return;
}
LSC = (lsc_data_t *)(e_ctrl->eeprom_params.buffer + LSC_GROUP2_OFFSET);
} else if (e_ctrl->eeprom_params.buffer[LSC_GROUP1_FLAG_OFFSET] == VALID_FLAG) {
if(!(hi556_DMEGC_checksum(e_ctrl->eeprom_params.buffer, LSC_GROUP1_FLAG_OFFSET , LSC_GROUP1_LAST_BYTE, LSC_GROUP1_CRC_OFFSET))){
SERR("failed:invalid LSC groud1 data. empty or invalid data");
HI556_MI_CHECKDATA = -1;
return;
}
LSC = (lsc_data_t *)(e_ctrl->eeprom_params.buffer + LSC_GROUP1_OFFSET);
} else {
SERR("LSC : empty or invalid data");
return;
}
lsc_present=TRUE; /*用于在校准选项函数中判断使用是否进行lsc的平台校准*/
float r_gain[MESH_ROLLOFF_SIZE];
float gr_gain[MESH_ROLLOFF_SIZE];
float gb_gain[MESH_ROLLOFF_SIZE];
float b_gain[MESH_ROLLOFF_SIZE];
int r_value,rg_value,bg_value,b_value;
int i = 0;
int j = 0;
int r_value,rg_value,bg_value,b_value;
int i = 0;
/*LSC在平台端数据为17*13的一个阵列,所以这里是一个(LSC_SIZE = 17 * 13)循环*/
for(i = 0 ; i < LSC_SIZE; i++)
{
/*首先根据OTP的map表从OTP中获取到LSC的数据.LSC为结构体,其为eeprom中lsc数据的映射*/
r_value=LSC[i].R_avg_low | (LSC[i].R_avg_high<<8);
rg_value=LSC[i].Gr_avg_low | (LSC[i].Gr_avg_high<<8);
bg_value=LSC[i].Gb_avg_low | (LSC[i].Gb_avg_high<<8);
b_value=LSC[i].B_avg_low | (LSC[i].B_avg_high<<8);
/*将LSC的数据写入到平台校准数组中,具体需要去看结构体*/
e_ctrl->eeprom_data.lsc.lsc_calib[ROLLOFF_TL84_LIGHT].r_gain[i] =
e_ctrl->eeprom_data.lsc.lsc_calib[ROLLOFF_A_LIGHT].r_gain[i] =
e_ctrl->eeprom_data.lsc.lsc_calib[ROLLOFF_D65_LIGHT].r_gain[i] =
(float)r_value;
e_ctrl->eeprom_data.lsc.lsc_calib[ROLLOFF_TL84_LIGHT].gr_gain[i] =
e_ctrl->eeprom_data.lsc.lsc_calib[ROLLOFF_A_LIGHT].gr_gain[i] =
e_ctrl->eeprom_data.lsc.lsc_calib[ROLLOFF_D65_LIGHT].gr_gain[i] =
(float)rg_value;
e_ctrl->eeprom_data.lsc.lsc_calib[ROLLOFF_TL84_LIGHT].gb_gain[i] =
e_ctrl->eeprom_data.lsc.lsc_calib[ROLLOFF_A_LIGHT].gb_gain[i] =
e_ctrl->eeprom_data.lsc.lsc_calib[ROLLOFF_D65_LIGHT].gb_gain[i] =
(float)bg_value;
e_ctrl->eeprom_data.lsc.lsc_calib[ROLLOFF_TL84_LIGHT].b_gain[i] =
e_ctrl->eeprom_data.lsc.lsc_calib[ROLLOFF_A_LIGHT].b_gain[i] =
e_ctrl->eeprom_data.lsc.lsc_calib[ROLLOFF_D65_LIGHT].b_gain[i] =
(float)b_value;
}
/*最后写入每一个光源校准的size大小*/
e_ctrl->eeprom_data.lsc.lsc_calib[ROLLOFF_TL84_LIGHT].mesh_rolloff_table_size=
e_ctrl->eeprom_data.lsc.lsc_calib[ROLLOFF_A_LIGHT].mesh_rolloff_table_size=
e_ctrl->eeprom_data.lsc.lsc_calib[ROLLOFF_D65_LIGHT].mesh_rolloff_table_size=
MESH_ROLLOFF_SIZE; /*17 * 13*/
}
6,在sensor驱动代码中指定eeprom
--- a/mm-camera2/media-controller/modules/sensors/sensor_libs/hi556_DMEGC/hi556_DMEGC_lib.c
+++ b/mm-camera2/media-controller/modules/sensors/sensor_libs/hi556_DMEGC/hi556_DMEGC_lib.c
@@ -542,7 +542,7 @@ static sensor_lib_t sensor_lib_ptr = {
/* sensor init params */
.sensor_init_params = &sensor_init_params,
/* sensor eeprom name */
- //.eeprom_name = "sunny_h9p5",
+ .eeprom_name = "hi556_DMEGC",/*指定加载的eeprom*/
/* sensor actuator name */
//.actuator_name = "dw9714",
/* sensor output settings */