【Android Camera】Camera OTP内置校准方法

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 */

 

你可能感兴趣的:(【Android Camera】Camera OTP内置校准方法)