【高通SDM660平台 Android(安卓)10.0】(11) --- Eeprom lib 与 Kernel eeprom代码分析
【高通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 做上电初始化操作:
主要工作如下:
- 获取上层下发的上电时序,保存在 power_setting_array 中,对应在lib.h 中的
power_setting_array
- 获取上层下发的地址映射表,对应在lib.h 中的 mem_map_array
- 上电
- 对eeprom lib.h 中配置的地址进行 循环映射,将内容保存在
e_ctrl->cal_data.mapdata
中 - 下电
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 数据。
更多相关文章
- 【Android】Intent中使用Extra传递数据
- Android(安卓)JSON数据与实体类之间的相互转化-------GSON实现
- android实现接通和挂断电话
- Android.mk 代码注释
- Android开发常用经典代码段集锦
- android 使用SharedPreferences保存list数据
- Android(安卓)积累一些RxJava2.0有关的知识
- android中SharedPerferences保存数据
- android的sd卡上创建目录不显示