When I got a new camera module with LC898214, I intuitively thought that I could use LC898212 driver for test, but it’s not true, it didn’t work anyway. I feel a little upset because I have to tune a new AF driver just for testing the module. Fortunately, it’s very easy to design the driver with Onsemi FAE’s help.
Modify LC898212 driver for LC898214,
In “msm_LC898212_InitForQualcomm” function
uint16_t chip_id = 0;
uint16_t temp_id = 0, check_data = 0xFF, step = 0;
uint16_t macro_low, macro_hi, inf_low, inf_hi;
struct msm_camera_cci_client *cci_client = NULL;
a_ctrl->i2c_client.i2c_func_tbl->i2c_read(&a_ctrl->i2c_client, 0xF0,&chip_id, MSM_CAMERA_I2C_BYTE_DATA);
if(chip_id == 0x42)
{
printk("xiaolifeng@ find LC898214 success, chip_id = 0x%x\n", chip_id);
}
else
{
printk("xiaolifeng@ find LC898214 failure, chip_id = 0x%x\n", chip_id);
}
a_ctrl->i2c_client.i2c_func_tbl->i2c_write(&a_ctrl->i2c_client,0x87, 0x00, MSM_CAMERA_I2C_BYTE_DATA);
cci_client = a_ctrl->i2c_client.cci_client;
temp_id = cci_client->sid;
printk("xiaolifeng@ temp_id = 0x%x\n", temp_id);
cci_client->sid = 0xE7 >>1;
a_ctrl->i2c_client.i2c_func_tbl->i2c_read(&a_ctrl->i2c_client, 0x39,¯o_low, MSM_CAMERA_I2C_BYTE_DATA);
a_ctrl->i2c_client.i2c_func_tbl->i2c_read(&a_ctrl->i2c_client, 0x38,¯o_hi, MSM_CAMERA_I2C_BYTE_DATA);
a_ctrl->i2c_client.i2c_func_tbl->i2c_read(&a_ctrl->i2c_client, 0x3b,&inf_low, MSM_CAMERA_I2C_BYTE_DATA);
a_ctrl->i2c_client.i2c_func_tbl->i2c_read(&a_ctrl->i2c_client, 0x3a,&inf_hi, MSM_CAMERA_I2C_BYTE_DATA);
if(a_ctrl-> cci_master == 0)
{
g_macro_pos_0 = macro_low|macro_hi<<8;
g_inf_pos_0 = inf_low|inf_hi<<8;
}
else
{
g_macro_pos_1 = macro_low|macro_hi<<8;
g_inf_pos_1 = inf_low|inf_hi<<8;
}
printk("xiaolifeng@ Read macro&infinity position from EEprom:cci_master=> %d\n macro_low = 0x%x, macro_hi= 0x%x, inf_low = 0x%x, inf_hi = 0x%x \n macro_pos = 0x%x, inf_pos= 0x%x\n", a_ctrl-> cci_master, macro_low, macro_hi, inf_low, inf_hi, macro_low|macro_hi<<8, inf_low|inf_hi<<8);
cci_client->sid = temp_id;
a_ctrl->i2c_client.i2c_func_tbl->i2c_write(&a_ctrl->i2c_client,0xe0, 0x01, MSM_CAMERA_I2C_BYTE_DATA);
msleep(2);
while(1)
{
a_ctrl->i2c_client.i2c_func_tbl->i2c_read(&a_ctrl->i2c_client, 0xe0,&check_data, MSM_CAMERA_I2C_BYTE_DATA);
if(check_data == 0)
break;
step++;
if(step == 3)
{
printk("xiaolifeng@ Break check loop because of time out\n");
break;
}
}
Notice that LC898214 have built-in eeprom for storaging macro and infinity position. So after check ID of LC898214, we need to change i2c slave address to eeprom, that is why we can see such line: “cci_client->sid = 0xE7 >>1;”
Modify transformation function which change “code” range 0~1023 from user space into real vcm hardware register value.
int msm_LC898212_convert(uint16_t code,uint32_t subdev_id){
int HallPos;
int HallRange;
int PosRange;
if(subdev_id == 0) //0 means imx214 1means imx214_R
{
HallRange = g_macro_pos_0 + (0x10000 - g_inf_pos_0);
}
else
{
HallRange = g_macro_pos_1 + (0x10000 - g_inf_pos_1);
}
PosRange = abs_closeloop(tinfo.pos_high - tinfo.pos_low);
if(subdev_id == 0)
{
HallPos = ((tinfo.pos_high - code) * HallRange / PosRange) + g_inf_pos_0;
}
else
{
HallPos = ((tinfo.pos_high - code) * HallRange / PosRange) + g_inf_pos_1;
}
return HallPos & 0xFFFF;
}
how to understand these mapping codes?
For a given module, I read macro_pos is 0x2e07, inf_pos is 0xe8b1, its hardware range is 0~0x2e07 plus 0xe8b1~0xffff. We want to map the unique range to 0~1023 for user space application. Firstly, total hardware range is “HallRange = g_macro_pos_0 + (0x10000 - g_inf_pos_0);”, secondly, use HallRange divide PosRange which is 0~1023, then multiply “tinfo.pos_high – code” in order to change the value 1023 as micro and 0 as infinity. It is also unique too. Then add g_inf_pos_0, it probably beyond 0x10000, so finally use “HallPos & 0xFFFF” for changing value under 0x2e07, it is a trick, right?
Modify “moveto” function to make the motor run to the position we expect,
uint16_t msm_LC898212_moveto(struct msm_actuator_ctrl_t *a_ctrl,uint16_t SsSmvEnd ){
uint16_t check_data = 0xFF, step = 0;
a_ctrl->i2c_client.i2c_func_tbl->i2c_write(&a_ctrl->i2c_client,0xA0, SsSmvEnd, MSM_CAMERA_I2C_WORD_DATA);
msleep(5);
while(1)
{
a_ctrl->i2c_client.i2c_func_tbl->i2c_read(&a_ctrl->i2c_client, 0x8F,&check_data, MSM_CAMERA_I2C_BYTE_DATA);
if(check_data == 0)
break;
msleep(5);
step++;
check_data = 0xFF;
if(step == 3)
{
printk("xiaolifeng@ Break VCM moveto because of time out\n");
break;
}
}
return check_data;
}
0xA0 is the register for controlling the motor’s position, we’d better read 0x8F for checking if there are something wrong when it work.
Above are summary codes for back-up, other codes should be modified, such as reading the feedback of motor real position and judge whether difference between position we want it go and its real position are reliable. But I just want to make the new dual cameras work, so I am lazy and omit a lot of work.