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


【高通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.c

static 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.c

case 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 数据。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

"小夜猫&小懒虫&小财迷"的男人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值