【高通SDM660平台 Android 10.0】--- Eeprom lib 与 Kernel eeprom代码分析

  • 一、 libmmcamera_rohm_brcg064gwz_3_eeprom.so 代码分析
    • 1.1 eeprom.c
    • 1.2 rohm_brcg064gwz_3_eeprom.h
    • 1.2 rohm_brcg064gwz_3_eeprom.c
  • 二、 EEPROM 初始化(CFG_EEPROM_INIT)
  • 三、获取 OTP 数据(CFG_EEPROM_READ_CAL_DATA)


《【高通SDM660平台】(1) — Camera 驱动 Bringup Guide》
《【高通SDM660平台】(2) — Camera Kernel 驱动层代码逻辑分析》
《【高通SDM660平台】(3) — Camera V4L2 驱动层分析 》
《【高通SDM660平台】(4) — Camera Init 初始化流程 》
《【高通SDM660平台】(5) — Camera Open 流程》
《【高通SDM660平台】(6) — Camera getParameters 及 setParameters 流程》
《【高通SDM660平台】(7) — Camera onPreview 代码流程》
《【高通SDM660平台】(8) — Camera MetaData介绍》
《【高通SDM660平台 Android 10.0】(9) — Qcom Camera Daemon 代码分析》
《【高通SDM660平台 Android 10.0】(10) — Camera Sensor lib 与 Kernel Camera Probe 代码分析》
《【高通SDM660平台 Android 10.0】(11) — Eeprom lib 与 Kernel eeprom代码分析》

《【高通SDM660平台】Camera Capture 流程》
《【高通SDM660平台】Camera mm-qcamera-app 代码分析》
《【高通SDM660平台 Android 10.0】 — 高通马达、eeprom、flash 等外设代码分析》



以 imx258 为例,它使用的eeprom 是 rohm_brcg064gwz_3。

先说下,做OTP 的目的。

大家都知道,生产任何东西的时候,不可能做到一模一样,多多少少都会有些误差。
同样,Camera 模组也是一样,在同一批Camera 有品质好的,也有品质一般的,甚至也有少部份品质差的。
一般数量是呈正态分布,也就是,可能 10%是品质好,80%是品质一般的,10%是品质差的。

对于用户来说,他肯定要买品质好。
但对于Camera 模组厂来说,只卖品质好的肯定不行,因为大多数产品还是品质一般的,
所以就可以利用otp,将品质一般的Camera 模组 ,校准到和品质好的效果一样,就样就 OK 了。

这就是 OTP的由来,将大多数品质一般的模组,通过otp 数据校准,使其效果达到和 品质好的模组一样。
所以, 模组产商会对每一颗模组进行校准,也就是说,每个Camera 模组之间的 otp 数据都是不一样的,它是唯一的。


一、 libmmcamera_rohm_brcg064gwz_3_eeprom.so 代码分析

1.1 eeprom.c

eeprom.c 是通用的eeprom 代码,它的相关代码,相信在前我们写的比较熟悉了
《【高通SDM660平台 Android 10.0】(9) — Qcom Camera Daemon 代码分析》

在该文件中,最重要的是 eeprom_process 函数。
它类似一个引导文件,通过它,最终还是会调用到 具体的camera eeprom 的代码中,因为每个eeprom 代码及数据处理方法都 是不一样的。

@ mm-camera/mm-camera2/media-controller/modules/sensors/eeprom/module/eeprom.cstatic int32_t eeprom_process(void *eeprom_ctrl, sensor_submodule_event_type_t event, void *data){  sensor_eeprom_data_t *e_ctrl = (sensor_eeprom_data_t *)eeprom_ctrl;  switch(event) {  case EEPROM_READ_DATA:      rc = eeprom_get_info(e_ctrl);      break;  case EEPROM_SET_FORMAT_DATA:      rc = eeprom_format_calibration_data(e_ctrl);      eeprom_af_add_margin(e_ctrl);      break;  case EEPROM_CALIBRATE_WB:    eeprom_do_wb_calibration(e_ctrl, data);    break;  case EEPROM_CALIBRATE_WB_GREEN:    eeprom_do_wb_green_calibration(e_ctrl, data);    break;  case EEPROM_CALIBRATE_LSC:    eeprom_do_lsc_calibration(e_ctrl, data);    break;  case EEPROM_CALIBRATE_FOCUS_DATA:      e_ctrl->eeprom_afchroma = *((eeprom_set_chroma_af_t *)data);      eeprom_do_af_calibration(e_ctrl);      break;  case EEPROM_GET_ISINSENSOR_CALIB: {      int32_t *is_insensor = (int32_t *)data;      if (e_ctrl->eeprom_lib.func_tbl &&         e_ctrl->eeprom_params.is_supported) {        if (e_ctrl->eeprom_lib.func_tbl->get_calibration_items != NULL)          e_ctrl->eeprom_lib.func_tbl->get_calibration_items(e_ctrl);        *is_insensor = e_ctrl->eeprom_data.items.is_insensor;      }  }    break;  case EEPROM_GET_ISOIS_CALIB:{      int32_t *is_ois = (int32_t *)data;      if (e_ctrl->eeprom_lib.func_tbl &&          e_ctrl->eeprom_params.is_supported) {        if (e_ctrl->eeprom_lib.func_tbl->get_calibration_items != NULL)          e_ctrl->eeprom_lib.func_tbl->get_calibration_items(e_ctrl);        *is_ois = e_ctrl->eeprom_data.items.is_ois;      }  }    break;  case EEPROM_GET_FORMATTED_DATA:    rc = eeprom_get_formatted_data(e_ctrl, data);    break;  case EEPROM_GET_RAW_DATA:    rc = eeprom_get_raw_data(e_ctrl, data);    break;  case EEPROM_GET_OIS_RAW_DATA:    rc = eeprom_get_ois_raw_data(e_ctrl, data);    break;  case EEPROM_GET_WB_GRGB:    if (e_ctrl->eeprom_data.wbc.gr_over_gb < 1)      *(float*)data = 1 / e_ctrl->eeprom_data.wbc.gr_over_gb;    else      *(float*)data = e_ctrl->eeprom_data.wbc.gr_over_gb;    break;  case EEPROM_GET_WB_CAL:    if (!e_ctrl->eeprom_data.items.is_insensor)      *(wbcalib_data_t**)data = &e_ctrl->eeprom_wbc_factor;    break;case EEPROM_GET_ISDPC_CALIB:{    int32_t *is_dpc = (int32_t *)data;    if (e_ctrl->eeprom_lib.func_tbl) {      if (e_ctrl->eeprom_lib.func_tbl->get_calibration_items != NULL){        e_ctrl->eeprom_lib.func_tbl->get_calibration_items(e_ctrl);        *is_dpc = e_ctrl->eeprom_data.items.is_dpc;      }    }    break;  }  case EEPROM_SET_CALIBRATE_DUALCAM_PARAM: {    cam_related_system_calibration_data_t *dual_data = (cam_related_system_calibration_data_t *)data;    rc = eeprom_do_dualcam_data_calibration(e_ctrl, dual_data);    break;  }  case EEPROM_DUMP_CALIB_DATA:  {    sensor_chromatix_params_t* chromatix_params;    chromatix_params = (sensor_chromatix_params_t*) data;    eeprom_dbg_data_dump(e_ctrl, chromatix_params, EEPROM_DUMP_CALIB);  }  break;  case EEPROM_INIT:    rc = eeprom_init(e_ctrl);   break;  }  return rc;}

1.2 rohm_brcg064gwz_3_eeprom.h

在 eeprom 头文件中,包含了该 eeprom 具体的参数,
注意,调试的时候一定要结合 eeprom datasheet,及 otp 表格 。

@ mm-camera/mm-camera2/media-controller/modules/sensors/eeprom/libs/rohm_brcg064gwz_3/rohm_brcg064gwz_3_eeprom.h#define WB_OFFSET            272// awb 白平衡数据 偏移地址#define AF_OFFSET            278// af 对焦数据 偏移地址#define MESH_HWROLLOFF_SIZE  (17*13)#define LSC_R_OFFSET         512// LSC Red 数据 偏移地址#define LSC_GR_OFFSET        (LSC_R_OFFSET+MESH_HWROLLOFF_SIZE*2)// LSC GR 数据 偏移地址#define LSC_GB_OFFSET        (LSC_GR_OFFSET+MESH_HWROLLOFF_SIZE*2)// LSC GB 数据 偏移地址#define LSC_B_OFFSET         (LSC_GB_OFFSET+MESH_HWROLLOFF_SIZE*2)// LSC Blue 数据 偏移地址#define PDGAIN_OFFSET        2816#define DCC_OFFSET           3706#define PDGAIN_WITDH         17#define PDGAIN_HEIGHT        13#define PDGAIN_LENGTH2D      (PDGAIN_HEIGHT * PDGAIN_WITDH)#define QVALUE 1024.0#define PAGE_EMPTY 0#define PAGE_NOT_EMPTY 1#define MAX_EMPTY_BYTES 8#define FAR_MARGIN (-0.074)#define NEAR_MARGIN (0.245)static unsigned int datapresent = 0;void brcg064gwz_3_get_calibration_items(void *e_ctrl);static void brcg064gwz_3_format_calibration_data(void *e_ctrl);static eeprom_lib_func_t brcg064gwz_3_lib_func_ptr = {  // 1. 该eeprom 对应的数据处理函数  .get_calibration_items = brcg064gwz_3_get_calibration_items,  .format_calibration_data = brcg064gwz_3_format_calibration_data,  .do_af_calibration = eeprom_autofocus_calibration,  .do_wbc_calibration = eeprom_whitebalance_calibration,  .do_lsc_calibration = eeprom_lensshading_calibration,  // 2. eeprom 上下电时序  .eeprom_info =  {    .power_setting_array =    {      .power_setting_a =      {        {          .seq_type = CAMERA_POW_SEQ_VREG,          .seq_val = CAMERA_VIO,          .config_val = 0,          .delay = 0,        },      },      .size = 1,      .power_down_setting_a =      {        {          .seq_type = CAMERA_POW_SEQ_VREG,          .seq_val = CAMERA_VIO,          .config_val = 0,          .delay = 0,        },      },      .size_down = 1,    },    // 3. eeprom 地址映射,有此地址比较复杂的,会在这里配置所有的地址    .i2c_freq_mode = SENSOR_I2C_MODE_FAST,    .mem_map_array =    {      .memory_map =      {        {          .slave_addr = 0xa0,          .mem_settings =          {            { 0x0, CAMERA_I2C_WORD_ADDR,              3840, CAMERA_I2C_BYTE_DATA, CAMERA_I2C_OP_READ, 0 },          },          .memory_map_size = 1,        },      },      .size_map_array = 1,    },  },};

1.2 rohm_brcg064gwz_3_eeprom.c

在该文件中,主要是就是对数据的转换,将 otp 中的数据,转化为实际能用的数据。
不同的芯片转换规则不一样,具体的转换规则,见eeprom datasheet.

static void brcg064gwz_3_format_calibration_data(void *e_ctrl) {  sensor_eeprom_data_t * ctrl = (sensor_eeprom_data_t *)e_ctrl;  unsigned char *buffer = ctrl->eeprom_params.buffer;  unsigned short crc = 0;  datapresent = 0;  SHIGH("OTP: total bytes: %d",ctrl->eeprom_params.num_bytes);  datapresent = 1;  brcg064gwz_3_format_pdafdata(ctrl);  brcg064gwz_3_format_wbdata(ctrl);  brcg064gwz_3_format_afdata(ctrl);  brcg064gwz_3_format_lscdata(ctrl);}

二、 EEPROM 初始化(CFG_EEPROM_INIT)

msm-qcom-daemon 中下发 CFG_EEPROM_INIT命令后,会

static int msm_eeprom_config(struct msm_eeprom_ctrl_t *e_ctrl, void *argp){CDBG("%s E\n", __func__);switch (cdata->cfgtype) {case CFG_EEPROM_INIT:if (e_ctrl->cal_data.num_data == 0) {rc = eeprom_init_config(e_ctrl, argp);}break;}

在 eeprom_init_config 对eerpom 做上电初始化操作:
主要工作如下:

  1. 获取上层下发的上电时序,保存在 power_setting_array 中,对应在lib.h 中的 power_setting_array
  2. 获取上层下发的地址映射表,对应在lib.h 中的 mem_map_array
  3. 上电
  4. 对eeprom lib.h 中配置的地址进行 循环映射,将内容保存在 e_ctrl->cal_data.mapdata
  5. 下电
static int eeprom_init_config(struct msm_eeprom_ctrl_t *e_ctrl, void *argp){power_setting_array = kzalloc(sizeof(struct msm_sensor_power_setting_array), GFP_KERNEL);memory_map_arr = kzalloc(sizeof(struct msm_eeprom_memory_map_array), GFP_KERNEL);// 1. 获取上层下发的上电时序,保存在 power_setting_array 中,对应在lib.h 中的 power_setting_array copy_from_user(power_setting_array,(void __user *)cdata->cfg.eeprom_info.power_setting_array,sizeof(struct msm_sensor_power_setting_array));CDBG("%s:%d Size of power setting array: %d\n", __func__, __LINE__, power_setting_array->size);// 2. 获取上层下发的地址映射表,对应在lib.h 中的 mem_map_array copy_from_user(memory_map_arr,(void __user *)cdata->cfg.eeprom_info.mem_map_array,sizeof(struct msm_eeprom_memory_map_array));power_info = &(e_ctrl->eboard_info->power_info);power_info->power_setting = power_setting_array->power_setting_a;power_info->power_down_setting = power_setting_array->power_down_setting_a;power_info->power_setting_size = power_setting_array->size;power_info->power_down_setting_size = power_setting_array->size_down;if (e_ctrl->i2c_client.cci_client) {e_ctrl->i2c_client.cci_client->i2c_freq_mode = cdata->cfg.eeprom_info.i2c_freq_mode;}// 3. 上电/* Fill vreg power info and power up here */rc = msm_eeprom_power_up(e_ctrl, power_info);=============>+@ msm-4.14/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c+msm_camera_fill_vreg_params( power_info->cam_vreg, power_info->num_vreg,+power_info->power_setting, power_info->power_setting_size);+msm_camera_fill_vreg_params( power_info->cam_vreg, power_info->num_vreg,+power_info->power_down_setting, power_info->power_down_setting_size);+msm_camera_power_up(power_info, e_ctrl->eeprom_device_type, &e_ctrl->i2c_client);<=============// 4. 对eeprom lib.h 中配置的地址进行 循环映射,将内容保存在 e_ctrl->cal_data.mapdata 中rc = eeprom_parse_memory_map(e_ctrl, memory_map_arr);=============>+ memptr = e_ctrl->cal_data.mapdata;+ // 循环遍历+for (j = 0; j < eeprom_map_array->msm_size_of_max_mappings; j++) {+for (i = 0; i < eeprom_map->memory_map_size; i++) {+e_ctrl->i2c_client.i2c_func_tbl->i2c_read_seq(&(e_ctrl->i2c_client),+eeprom_map->mem_settings[i].reg_addr, memptr, eeprom_map->mem_settings[i].reg_data);+}+}+memptr = e_ctrl->cal_data.mapdata;+for (i = 0; i < e_ctrl->cal_data.num_data; i++)  // 打印 OTP 内容+CDBG("memory_data[%d] = 0x%X\n", i, memptr[i]);<=============// 5. 下电rc = msm_camera_power_down(power_info, e_ctrl->eeprom_device_type, &e_ctrl->i2c_client);return rc;}

三、获取 OTP 数据(CFG_EEPROM_READ_CAL_DATA)

@ msm-4.14/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.ccase CFG_EEPROM_READ_CAL_DATA:rc = eeprom_config_read_cal_data(e_ctrl, cdata);break;

eeprom_config_read_cal_data() 中,主要是获取 cal_data.mapdata 中的保存的 otp 数据

static int eeprom_config_read_cal_data(struct msm_eeprom_ctrl_t *e_ctrl, struct msm_eeprom_cfg_data *cdata){rc = copy_to_user((void __user *)cdata->cfg.read_data.dbuffer,e_ctrl->cal_data.mapdata, cdata->cfg.read_data.num_bytes);}

这样,就将kernel 中的 e_ctrl->cal_data.mapdata 中的 otp 数据通过copy_to_user 传递到了vendor 中了。

总结 eeprom 数据初始化过程为:

mm-qcamera-daemon 进程启动时,在main 函数中调用module_sensor_init 来初化Sensor,
从而调用到 module_sensor_init_eeprom 函数,在该函数中下加载 eeprom lib 为库,
下发 EEPROM_INIT 参数给eeprom上电,并读取eeprom 保存起来,
当真正要使用数据时,下发EEPROM_READ_DATA 参数来获取 eeprom 数据。

更多相关文章

  1. 【Android】Intent中使用Extra传递数据
  2. Android(安卓)JSON数据与实体类之间的相互转化-------GSON实现
  3. android实现接通和挂断电话
  4. Android.mk 代码注释
  5. Android开发常用经典代码段集锦
  6. android 使用SharedPreferences保存list数据
  7. Android(安卓)积累一些RxJava2.0有关的知识
  8. android中SharedPerferences保存数据
  9. android的sd卡上创建目录不显示

随机推荐

  1. Android(安卓)获取手机信息
  2. 修改不启动Launcher导致开机广播无法发出
  3. Android获取通话状态
  4. android shape 常用到属性的设置
  5. Android(安卓)双开沙箱 VirtualApp 源码
  6. Android通过App启动另一个APP
  7. Android(安卓)获取设备各种信息以及其它
  8. android 自定义对话框 背景透明
  9. Android之一种很有趣的界面跳动提示动画
  10. Android(安卓)wifi提示已连接但是无法访