包含寄存器地址和PAGE详细信息,以利于供应商核对otp信息
kernel\msm-3.18\drivers\media\platform\msm\camera_v2\sensor\eeprom\msm_eeprom.c +msm_eeprom_platform_probe +1728
int i,page = 5;
*********************
以下读取Module info && awb 数据
*********************
for (j = 0; j < e_ctrl->cal_data.num_data; j++)
CDBG("memory_data[%d] = address[0x%x] = 0x%X\n", j, (0x0A04+j),
e_ctrl->cal_data.mapdata[j]);
*********************
以下读取 LSC 数据
*********************
#define START_ADDRESS_OTP 0x0A28
#define START_OTP_YUEQIU 0x0A04
#define END_OTP_YUEQIU (0x0A43 + 1)
//int i,page = 5;
i = START_ADDRESS_OTP;
for (j = 0; j < e_ctrl->cal_data.num_data; j++, i++)
{
if(END_OTP_YUEQIU == i)
{
i = START_OTP_YUEQIU;
printk("PAGE %d\n", page++);
}
CDBG("memory_data[%d] = address[0x%x] = 0x%X\n", j, i,
e_ctrl->cal_data.mapdata[j]);
}
/*******打印整个群晖摄像头s5k5e8 otp 数据*********/
static int dump_memory;
int i, page = 5;
#define START_ADDRESS_OTP 0x0a04
#define START_OTP_YUEQIU 0x0a04
#define END_OTP_YUEQIU (0x0a43 + 1)
printk("PAGE 4\n");
i = START_ADDRESS_OTP;
dump_memory++;
if(3 == dump_memory)
{
for (j = 0; j < e_ctrl->cal_data.num_data; j++, i++)
{
if(END_OTP_YUEQIU == i)
{
i = START_OTP_YUEQIU;
printk("PAGE %d\n", page++);
}
printk("memory_data[%d] = address[0x%x] = 0x%X\n", j, i,
e_ctrl->cal_data.mapdata[j]);
}
}
vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/sensors/eeprom/libs/qunhui_s5k5e8/qunhui_s5k5e8_eeprom.c
/*============================================================================
Copyright (c) 2016 Qualcomm Technologies, Inc. All Rights Reserved.
Qualcomm Technologies Proprietary and Confidential.
============================================================================*/
#include
#include
#include
#include "../eeprom_util/eeprom.h"
#include "eeprom_lib.h"
#include "eeprom_util.h"
#include "debug_lib.h"
#include "sensor_lib.h"
#include "actuator_driver.h"
#undef CDBG
#define EEPROM_DEBUG
#ifdef EEPROM_DEBUG
#define CDBG(fmt, args...) \
ALOGE("s5k5e8_eeprom %s:%d "fmt"\n", __func__, __LINE__, ##args)
#else
#define CDBG(fmt, args...) do{}while(0)
#endif
#define AWB_REG_SIZE 8 //手册里面此部分寄存器有8个
#define OTP_VALID 1
#define OTP_INVALID 0
#define GAIN_GREEN1_ADDR 0x020E //这部分寄存器的值问供应商提供,或者手册里有
#define GAIN_BLUE_ADDR 0x0212
#define GAIN_RED_ADDR 0x0210
#define GAIN_GREEN2_ADDR 0x0214
#define RG_TYPICAL_QUNHUI 0x8B //经典值,可以从芯片寄存器里读取,因为是固定的,也可以自己指定
#define BG_TYPICAL_QUNHUI 0x7D
#define QUNHUI_MODULE_ID 0x44
#define PRIMAX_S5K5E8_OTP_DEBUG
struct otp_struct {
uint16_t module_integrator_id;
uint16_t production_year;
uint16_t production_month;
uint16_t production_day;
uint16_t lens_id;
uint16_t vcm_id;
uint16_t R_Gr_ratio;
uint16_t B_Gr_ratio;
} otp;
static uint16_t rg_typical, bg_typical = 0;
static uint16_t use_group = 1;
static uint16_t lsc_info[360];
//数组大小的计算千万小心仔细,很容易算错,这个数组是回写 sensor 寄存器
//camera_i2c_reg_array 属于 camera_i2c_reg_setting 元素
struct camera_i2c_reg_array g_reg_array[AWB_REG_SIZE + 144*6 + 2 + 1];
struct camera_i2c_reg_setting g_reg_setting;
/** qunhui_s5k5e8_get_calibration_items:
* @e_ctrl: point to sensor_eeprom_data_t of the eeprom device
*
* Get calibration capabilities and mode items.
*
* This function executes in eeprom module context
*
* Return: void.
**/
static void qunhui_s5k5e8_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);
//is_insensor 如果传感器模块本身支持EEPROM配置,则将此标记设置为TRUE。外部EEPROM均不可用
e_items->is_insensor = TRUE;
//如果支持AF校准,将此标记设置为TRUE
e_items->is_afc = FALSE;//摄像头因为没有对焦功能,所以这个标志位无关紧要
//如果支持白平衡校准,将此标记设置为TRUE
e_items->is_wbc = FALSE;//摄像头本身支持白平衡校准,就设置为 false
//如果支持镜头阴影校准,将此标记设置为TRUE
e_items->is_lsc = FALSE;//摄像头本身支持LSC校准,就设置为 false
}
//叫供应商提供以下函数
void qunhui_s5k5e8_awb_updata(uint16_t r_gain,uint16_t b_gain)
{
float R_ration=(float)rg_typical/(float)r_gain;
float B_ration=(float)bg_typical/(float)b_gain;
uint16_t G_gain,R_gain,B_gain;
uint16_t gain_default=0x0100;
if (R_ration>=1)
{
if (B_ration>=1)
{
G_gain=gain_default ;
R_gain=gain_default*R_ration;
B_gain=gain_default*B_ration;
}
else
{
B_gain=gain_default;
G_gain=gain_default/B_ration;
R_gain=gain_default*R_ration/B_ration;
}
}
else
{
if (B_ration>=1)
{
R_gain=gain_default;
G_gain=gain_default/R_ration;
B_gain=gain_default*B_ration/R_ration;
}
else if (R_ration>=B_ration)
{
B_gain=gain_default;
G_gain=gain_default/B_ration;
R_gain=gain_default*R_ration/B_ration;
}
else
{
R_gain=gain_default;
G_gain=gain_default/R_ration;
B_gain=gain_default*B_ration/R_ration;
}
}
g_reg_array[g_reg_setting.size].reg_addr = GAIN_GREEN1_ADDR;
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 = GAIN_GREEN1_ADDR+1;
g_reg_array[g_reg_setting.size].reg_data = G_gain & 0x00ff;
g_reg_setting.size++;
g_reg_array[g_reg_setting.size].reg_addr = GAIN_RED_ADDR;
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 = GAIN_RED_ADDR+1;
g_reg_array[g_reg_setting.size].reg_data = R_gain & 0x00ff;
g_reg_setting.size++;
g_reg_array[g_reg_setting.size].reg_addr = GAIN_BLUE_ADDR;
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 = GAIN_BLUE_ADDR+1;
g_reg_array[g_reg_setting.size].reg_data = B_gain & 0x00ff;
g_reg_setting.size++;
g_reg_array[g_reg_setting.size].reg_addr = GAIN_GREEN2_ADDR;
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 = GAIN_GREEN2_ADDR+1;
g_reg_array[g_reg_setting.size].reg_data = G_gain & 0x00ff;
g_reg_setting.size++;
}
//供应商提供的解码函数
// I2CMODE_MICRON2 (16 Bit Address,8 Bit Data)
/************************************************************************/
/* 解码OTP至SRAM格式 */
/************************************************************************/
void DePrOTPLSCData_5E8(uint16_t *OTPBuff, uint16_t *SRAMBuff)
{
int i = 0, j=0;
for(i = 0; i < 360; i++)
ALOGE("OTPBuff[%d]: %d \n", i, OTPBuff[i]);
for (i=0; i<360; i=i+5)
{
SRAMBuff[j++] = (OTPBuff[i] & 0xf0) >>4;
SRAMBuff[j++] = ((OTPBuff[i] & 0x0f) <<4) | ((OTPBuff[i+1] & 0xf0) >>4);
SRAMBuff[j++] = ((OTPBuff[i+1] & 0x0f) <<4) | ((OTPBuff[i+2] & 0xf0) >>4);
SRAMBuff[j++] = OTPBuff[i+2] & 0x0f;
SRAMBuff[j++] = OTPBuff[i+3];
SRAMBuff[j++] = OTPBuff[i+4];
}
}
/************************************************************************/
/* 加载SRAM到 Sensor */
/************************************************************************/
void ApplyLSC(void){
uint16_t sramBuff[432];
int counter = 0x00;
int i;
DePrOTPLSCData_5E8(lsc_info, sramBuff);
/* 起初这个是没有注释掉的,导致摄像头报错:media server died, camera closed
应该是和 xxx_lib.h 里面重复掉了 0x0100 是流控制寄存器,写1:stream 写0:software standby
g_reg_array[g_reg_setting.size].reg_addr = 0x0100;
g_reg_array[g_reg_setting.size].reg_data = 0x01;
g_reg_setting.size++;
*/
/* LSC 自动加载控制寄存器,写00:自动加载 写1:不是自动加载*/
g_reg_array[g_reg_setting.size].reg_addr = 0x3400;
g_reg_array[g_reg_setting.size].reg_data = 0x00;
g_reg_setting.size++;
//寄存器[3]: seed date update start enable
//寄存器[2]: gain caculate start enable
//寄存器[1]:gain caculate auto-start enable
g_reg_array[g_reg_setting.size].reg_addr = 0x3457;
g_reg_array[g_reg_setting.size].reg_data = 0x04;
g_reg_setting.size++;
/*
//bFX2WriteSensor(nI2C, 0x0100,0x00, I2CMODE_STMICRO);
//bFX2WriteSensor(nI2C, 0x3400,0x00, I2CMODE_STMICRO);
//Sleep(100);
// 这个寄存器三星没有开放,三星建议,不要设置
g_reg_array[g_reg_setting.size].reg_addr = 0x3b4c;
g_reg_array[g_reg_setting.size].reg_data = 0x00;
g_reg_setting.size++;
//bFX2WriteSensor(nI2C, 0x3457,0x04, I2CMODE_STMICRO);
//bFX2WriteSensor(nI2C, 0x3b4c,0x00, I2CMODE_STMICRO);
*/
for(i = 0; i < 432; i++)
ALOGE("sramBuff[%d]: %d \n", i, sramBuff[i]);
for (i=0; i<432; )
{
/*
0x3415 [7:4]Reserved R [3:0] GAS_write_data[19:16]
0x3416 [7:0]GAS_write_data[15:8]
0x3417 [7:0]GAS_write_data[7:0]
0x3418 [7:0]GAS_address
0x3419 [7:4]Reserved [3:0]GAS_command 4=write 8=read
*/
g_reg_array[g_reg_setting.size].reg_addr = 0x3419;
g_reg_array[g_reg_setting.size].reg_data = 0x00;
g_reg_setting.size++;
g_reg_array[g_reg_setting.size].reg_addr = 0x3415;
g_reg_array[g_reg_setting.size].reg_data = sramBuff[i];
g_reg_setting.size++;
g_reg_array[g_reg_setting.size].reg_addr = 0x3416;
g_reg_array[g_reg_setting.size].reg_data = sramBuff[i + 1];
g_reg_setting.size++;
g_reg_array[g_reg_setting.size].reg_addr = 0x3417;
g_reg_array[g_reg_setting.size].reg_data = sramBuff[i + 2];
g_reg_setting.size++;
g_reg_array[g_reg_setting.size].reg_addr = 0x3418;
g_reg_array[g_reg_setting.size].reg_data = counter++;
g_reg_setting.size++;
g_reg_array[g_reg_setting.size].reg_addr = 0x3419;
g_reg_array[g_reg_setting.size].reg_data = 0x04;
g_reg_setting.size++;
i=i+3;
}
//bFX2WriteSensor(nI2C, 0x3457,0x0C, I2CMODE_STMICRO);
//bFX2WriteSensor(nI2C, 0x0100,0x01, I2CMODE_STMICRO);
g_reg_array[g_reg_setting.size].reg_addr = 0x3457;
g_reg_array[g_reg_setting.size].reg_data = 0x0c;
g_reg_setting.size++;
/*
g_reg_array[g_reg_setting.size].reg_addr = 0x0100;
g_reg_array[g_reg_setting.size].reg_data = 0x00;
g_reg_setting.size++;
*/
}
void qunhui_s5k5e8_calib_sensor(sensor_eeprom_data_t *e_ctrl)
{
CDBG("[S5K5E8OTP] Enter %s", __func__);
/*****************************
注* 先判断Group2 Flag是否为 1 ? {如果为1 即读取Group2 数据}, {不为1则读取Group1数据}
*****************************/
unsigned int base=0, i;
unsigned int awb_base=16;
unsigned int lsc_base=16;
unsigned int uiInformation_Group = 0;
unsigned int uiAWB_Group = 0;
unsigned int uiLSC_Group = 0;
#define INFORMATION_GROUP_OFFSET 8
#define AWB_GROUP_OFFSET 10
#define LSC_GROUP_OFFSET 362
/************ Information *****************/
if (e_ctrl->eeprom_params.buffer[0 + INFORMATION_GROUP_OFFSET] == 0x01) {
uiInformation_Group = 2;
base = 8;
ALOGE("qunhui s5k5e8_eeprom MODULE ID use group 2 \n");
}else if (e_ctrl->eeprom_params.buffer[0] == 0x01) {
uiInformation_Group = 1;
base = 0;
ALOGE("qunhui s5k5e8_eeprom MODULE ID use group 1 \n");
}else {
uiInformation_Group = 0;
ALOGE("qunhui s5k5e8_eeprom MODULE ID error: %s:%d \n", __func__, __LINE__);
}
/************ AWB *****************/
if (e_ctrl->eeprom_params.buffer[16 + AWB_GROUP_OFFSET] == 0x01) {
uiAWB_Group = 2;
awb_base = 8 + 8 + 10 + 1;
ALOGE("qunhui s5k5e8_eeprom awb use group 2 \n");
}
else if (e_ctrl->eeprom_params.buffer[16] == 0x01){
uiAWB_Group = 1;
awb_base = 16 + 1;
ALOGE("qunhui s5k5e8_eeprom awb use group 1 \n");
}
else {
uiAWB_Group = 0;
ALOGE("qunhui s5k5e8_eeprom AWB error: %s:%d \n", __func__, __LINE__);
}
/************ LSC *****************/
if (e_ctrl->eeprom_params.buffer[36 + LSC_GROUP_OFFSET] == 0x01) {//****** Page 10 address
uiLSC_Group = 2;
lsc_base = 36 + 362 + 1;//计算开始地址要小心,和下面的读取拷贝要一致
ALOGE("qunhui s5k5e8_eeprom lsc use group 2 \n");
}
else if (e_ctrl->eeprom_params.buffer[36] == 0x01) {//****** Page 4 address
uiLSC_Group = 1;
lsc_base = 36 + 1;
ALOGE("qunhui s5k5e8_eeprom lsc use group 1 \n");
}
else {
uiLSC_Group = 0;
ALOGE("qunhui s5k5e8_eeprom LSC error: %s:%d \n", __func__, __LINE__);
}
//int flg_otp_lsc = 1;
//int i=0;
CDBG("grop flag = 0x%x otp.module_id= 0x%x\n",e_ctrl->eeprom_params.buffer[base], e_ctrl->eeprom_params.buffer[base+1] & 0x1f);
otp.module_integrator_id = e_ctrl->eeprom_params.buffer[base+1] & 0x1f;
otp.lens_id = e_ctrl->eeprom_params.buffer[base+2];
otp.vcm_id= e_ctrl->eeprom_params.buffer[base+3];
otp.production_year = e_ctrl->eeprom_params.buffer[base+4];
otp.production_month = e_ctrl->eeprom_params.buffer[base+5];
otp.production_day = e_ctrl->eeprom_params.buffer[base+6];
otp.R_Gr_ratio = (e_ctrl->eeprom_params.buffer[awb_base]) | (e_ctrl->eeprom_params.buffer[awb_base + 1] << 8);
otp.B_Gr_ratio = (e_ctrl->eeprom_params.buffer[awb_base + 2]) | (e_ctrl->eeprom_params.buffer[awb_base + 3] << 8);
rg_typical = (e_ctrl->eeprom_params.buffer[awb_base + 4]) | (e_ctrl->eeprom_params.buffer[awb_base + 5] << 8);
bg_typical = (e_ctrl->eeprom_params.buffer[awb_base + 6]) | (e_ctrl->eeprom_params.buffer[awb_base + 7] << 8);
//flg_otp_lsc = OTP_VALID;
CDBG("[S5K5E8OTP] otp.module_integrator_id= 0x%x\n", otp.module_integrator_id);
CDBG("[S5K5E8OTP] otp.lens_id= 0x%x\n", otp.vcm_id);
CDBG("[S5K5E8OTP] otp.R_Gr_ratio= 0x%x\n", otp.R_Gr_ratio);
CDBG("[S5K5E8OTP] otp.B_Gr_ratio= 0x%x\n", otp.B_Gr_ratio);
CDBG("[S5K5E8OTP] rg_typical= 0x%x\n", rg_typical);
CDBG("[S5K5E8OTP] bg_typical= 0x%x\n", bg_typical);
if(otp.R_Gr_ratio == 0 || otp.B_Gr_ratio == 0)
{
CDBG("[S5K5E8OTP] module OTP wb data unvalid !\n");
return ;
}
qunhui_s5k5e8_awb_updata(otp.R_Gr_ratio,otp.B_Gr_ratio);
//memcpy 用起来要小心,源数据的数据类型和目的地址的数据类型要千万注意,还有要拷贝的字节数要写正确,不要自己写数字,不然,很容易出现拷贝不全
//memcpy(lsc_info, &e_ctrl->eeprom_params.buffer[lsc_base], 360);
for(i = 0; i < 360; i++)
lsc_info[i] = e_ctrl->eeprom_params.buffer[lsc_base + i];//和前面的开始地址要一致
ApplyLSC();
//qunhui_s5k5e8_lsc_updata(flg_otp_lsc);
}
/** qunhui_s5k5e8_format_calibration_data:
* @e_ctrl: point to sensor_eeprom_data_t of the eeprom device
*
* Format all the data structure of calibration
*
* This function executes in eeprom module context and generate
* all the calibration registers setting of the sensor.
*
* Return: void.
**/
void qunhui_s5k5e8_format_calibration_data(void *e_ctrl) {
CDBG("Enter %s", __func__);
sensor_eeprom_data_t *ectrl = (sensor_eeprom_data_t *)e_ctrl;
g_reg_setting.addr_type = CAMERA_I2C_WORD_ADDR;
g_reg_setting.data_type = CAMERA_I2C_BYTE_DATA;
g_reg_setting.reg_setting = &g_reg_array[0];
g_reg_setting.size = 0;
g_reg_setting.delay = 0;
qunhui_s5k5e8_calib_sensor(ectrl);
CDBG("Exit %s", __func__);
}
/** qunhui_s5k5e8_get_raw_data:
* @e_ctrl: point to sensor_eeprom_data_t of the eeprom device
* @data: point to the destination msm_camera_i2c_reg_setting
*
* Get the all the calibration registers setting of the sensor
*
* This function executes in eeprom module context.
*
* Return: void.
**/
static int qunhui_s5k5e8_get_raw_data(void *e_ctrl, void *data) {
if (e_ctrl && data)
memcpy(data, &g_reg_setting, sizeof(g_reg_setting));
else
CDBG("failed Null pointer");
return 0;
}
static eeprom_lib_func_t qunhui_s5k5e8_lib_func_ptr = {
.get_calibration_items = qunhui_s5k5e8_get_calibration_items,
.format_calibration_data = qunhui_s5k5e8_format_calibration_data,
.do_af_calibration = NULL,
.do_wbc_calibration = NULL,
.do_lsc_calibration = NULL,
.get_raw_data = qunhui_s5k5e8_get_raw_data,
//以下的上下电没有用到,因为在kernel 的dtsi 文件里面已经配置了上下电
.eeprom_info =
{
.power_setting_array =
{
.power_setting_a =
{
{
.seq_type = CAMERA_POW_SEQ_GPIO,
.seq_val = CAMERA_GPIO_RESET,
.config_val = GPIO_OUT_LOW,
.delay = 1,
},
{
.seq_type = CAMERA_POW_SEQ_GPIO,
.seq_val = CAMERA_GPIO_VANA,
.config_val = GPIO_OUT_HIGH,
.delay = 1,
},
{
.seq_type = CAMERA_POW_SEQ_VREG,
.seq_val = CAMERA_VDIG,
.config_val = 1,
.delay = 1,
},
{
.seq_type = CAMERA_POW_SEQ_VREG,
.seq_val = CAMERA_VIO,
.config_val = 1,
.delay = 0,
},
/*
{
.seq_type = CAMERA_POW_SEQ_VREG,
.seq_val = CAMERA_VAF,
.config_val = 1,
.delay = 5,
},
*/
{
.seq_type = CAMERA_POW_SEQ_CLK,
.seq_val = CAMERA_MCLK,
.config_val = 24000000,
.delay = 1,
},
{
.seq_type = CAMERA_POW_SEQ_GPIO,
.seq_val = CAMERA_GPIO_RESET,
.config_val = GPIO_OUT_HIGH,
.delay = 11,
},
},
.size = 6,
.power_down_setting_a =
{
{
.seq_type = CAMERA_POW_SEQ_CLK,
.seq_val = CAMERA_MCLK,
.config_val = 0,
.delay = 1,
},
{
.seq_type = CAMERA_POW_SEQ_GPIO,
.seq_val = CAMERA_GPIO_RESET,
.config_val = GPIO_OUT_LOW,
.delay = 1,
},
/*
{
.seq_type = CAMERA_POW_SEQ_VREG,
.seq_val = CAMERA_VAF,
.config_val = 1,
.delay = 0,
},
*/
{
.seq_type = CAMERA_POW_SEQ_VREG,
.seq_val = CAMERA_VIO,
.config_val = 0,
.delay = 0,
},
{
.seq_type = CAMERA_POW_SEQ_VREG,
.seq_val = CAMERA_VDIG,
.config_val = 0,
.delay = 1,
},
{
.seq_type = CAMERA_POW_SEQ_GPIO,
.seq_val = CAMERA_GPIO_VANA,
.config_val = GPIO_OUT_LOW,
.delay = 1,
},
},
.size_down = 5,
},
.i2c_freq_mode = SENSOR_I2C_MODE_STANDARD,
.mem_map_array =
{
.memory_map =
{
{
.slave_addr = 0x20,
.mem_settings =
{
{ 0x0A04, CAMERA_I2C_WORD_ADDR,
64, CAMERA_I2C_BYTE_DATA, CAMERA_I2C_OP_READ, 1 },
},
.memory_map_size = 1,
},
},
.size_map_array = 1,
},
},
};
/** qunhui_s5k5e8_eeprom_open_lib:
*
* Get the funtion pointer of this lib.
*
* This function executes in eeprom module context.
*
* Return: eeprom_lib_func_t point to the function pointer.
**/
void* qunhui_s5k5e8_eeprom_open_lib(void) {
return &qunhui_s5k5e8_lib_func_ptr;
}