【Android Camera】Qualcomm Camera马达驱动代码分析

前言:

主要给大家介绍基于高通平台的camera马达驱动的编写过程,此过程会涉及到手机对焦功能实现的原理以及如何通过I2C子系统实现数据传输等过程。


一、手机自动对焦原理

手机自动对焦功能是通过将摄像头锁入音圈马达来实现的,音圈马达简称(VCM),它主要有线圈,磁铁组和弹片构成,线圈通过上下两个弹片固定在磁铁组成,当给线圈通电时,线圈会产生磁场,线圈磁场和磁石组相互作用,线圈会向上移动,而锁在线圈里的摄像头便一起移动,当断电时,线圈在弹片弹力下返回,这样就实现了自动对焦功能。


二、代码分析

 这里重点是讲解一下关于高通平台camera的马达驱动,具体文件路径:kernel\drivers\media\platform\msm\camera_v2\sensor\actuatormsm_actuator.c;


1.和大部分驱动程序一样,高通平台端的马达的入口函数也是module_init);

module_init(msm_actuator_init_module);

msm_actuator_init_module)函数主要是实现了两个功能:一个是通过platform_driver_register函数注册驱动msm_actuator_platform_driver(),还有一个是将msm_actuator_i2c_driver挂载i2c总线上;

static struct platform_driver msm_actuator_platform_driver = {

 .probe = msm_actuator_platform_probe, //注册成功后就会调用这个探测函数(重要)

.driver = {

.name = "qcom,actuator",  

  .owner = THIS_MODULE,

.of_match_table = msm_actuator_dt_match,    //通过设备树的compatible匹配资源,具体见下图

}

}


这里主要是大概讲解一下关于msm_actuator_platform_probe函数功能实现:

staticint32_t msm_actuator_platform_probe(structplatform_device *pdev) {

··············

 rc =of_property_read_u32((&pdev->dev)->of_node, "cell-index",&pdev->id); 

//获取设备资源信息CDBG("cell-index %d, rc %d\n", pdev->id, rc);

··············

/*初始化msm_actuator_t*/

msm_actuator_t->act_v4l2_subdev_ops= &msm_actuator_subdev_ops;

msm_actuator_t->actuator_mutex= &msm_actuator_mutex; //互斥锁

msm_actuator_t->cam_name= pdev->id;

/* Setplatform device handle */ msm_actuator_t->pdev = pdev;

/* 设置设备类型为平台设备 */

 msm_actuator_t->act_device_type = MSM_CAMERA_PLATFORM_DEVICE;msm_actuator_t->i2c_client.i2c_func_tbl = &msm_sensor_cci_func_tbl;msm_actuator_t->i2c_client.cci_client = kzalloc(sizeof( structmsm_camera_cci_client), GFP_KERNEL);

··············

/*马达是通过i2c通信控制*/

cci_client= msm_actuator_t->i2c_client.cci_client;

cci_client->cci_subdev= msm_cci_get_subdev();

cci_client->cci_i2c_master= msm_actuator_t->cci_master; 

/*初始化马达的从设备以及绑定函数操作集*/

v4l2_subdev_init(&msm_actuator_t->msm_sd.sd, msm_actuator_t->act_v4l2_subdev_ops);v4l2_set_subdevdata(&msm_actuator_t->msm_sd.sd, msm_actuator_t);msm_actuator_t->msm_sd.sd.internal_ops = &msm_actuator_internal_ops;msm_actuator_t->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;snprintf(msm_actuator_t->msm_sd.sd.name,ARRAY_SIZE(msm_actuator_t->msm_sd.sd.name), "msm_actuator");media_entity_init(&msm_actuator_t->msm_sd.sd.entity, 0, NULL, 0);msm_actuator_t->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;msm_actuator_t->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_ACTUATOR;msm_actuator_t->msm_sd.close_seq = MSM_SD_CLOSE_2ND_CATEGORY | 0x2;

/*注册设备以及设置状态*/

msm_sd_register(&msm_actuator_t->msm_sd);msm_actuator_t->actuator_state = ACT_DISABLE_STATE;  //disable msm_cam_copy_v4l2_subdev_fops(&msm_actuator_v4l2_subdev_fops);

························· }

 

staticstruct msm_actuator msm_vcm_actuator_table = {

.act_type =ACTUATOR_VCM, //类型:音圈马达

.func_tbl ={

.actuator_init_step_table= msm_actuator_init_step_table, //设置初始化步进表

.actuator_move_focus= msm_actuator_move_focus, //发送马达移动信息

.actuator_write_focus= msm_actuator_write_focus, //发送马达移动距离

.actuator_set_default_focus= msm_actuator_set_default_focus,//设置默认对焦

       .actuator_init_focus = msm_actuator_init_focus,

 .actuator_parse_i2c_params=msm_actuator_parse_i2c_params,

 .actuator_set_position=msm_actuator_set_position,

.actuator_park_lens= msm_actuator_park_lens, },

};


msm_actuator_init_step_table()函数主要是初始化step_table
1、先通过ADC多少位确定max_code_size,例如10位的就是1024;
2、a_ctrl->step_position_table = NULL;清空位置表并重新设置;
3、cur_code =set_info->af_tuning_params.initial_code;设置最初的编码;
4、其他
msm_actuator_move_focus:主要是计算出位置,然后通过i2c发送命令移动焦距:
rc = a_ctrl->i2c_client.i2c_func_tbl->i2c_write_table_w_microdelay(
&a_ctrl->i2c_client, &reg_setting);
msm_actuator_write_focus:
Write code based on damping_code_step in a loop
msm_actuator_set_default_focus设置默认的焦距:
rc = a_ctrl->func_tbl->actuator_move_focus(a_ctrl, move_params);


三、大致的工作流程

1、首先是上电:CFG_ACTUATOR_POWERUP(msm_actuator_power_up(a_ctrl))----->
2、接着是初始化:CFG_ACTUATOR_INIT(msm_actuator_init(a_ctrl))----->
3、获取设备信息:CFG_GET_ACTUATOR_INFO()-----> 
4、设置参数:CFG_SET_ACTUATOR_INFO(msm_actuator_set_param(a_ctrl, &cdata->cfg.set_info))----->
5、设置默认对焦位置CFG_SET_DEFAULT_FOCUS(actuator_set_default_focus)----->
6、设置对焦位置CFG_SET_POSITION(actuator_set_position)----->
7、移动镜片对焦CFG_MOVE_FOCUS(actuator_move_focus)----->
8、最后关闭后下电CFG_ACTUATOR_POWERDOWN(msm_actuator_power_down)结束


四、分析msm_actuator_parse_i2c_params函数功能作用:


下面msm_actuator_parse_i2c_params函数具体主要是实现了i2c数据的解析,例如要发送的DAC数据和往具体地址的寄存器地址,具体见如下代码分析:

static void msm_actuator_parse_i2c_params(struct msm_actuator_ctrl_t*a_ctrl,

       int16_t next_lens_position,uint32_t hw_params, uint16_t delay)

{

       structmsm_actuator_reg_params_t *write_arr = NULL;

       uint32_t hw_dword = hw_params;

/*一般byte1是地址,byte2是data,但是这里默认只支持16的,32位以上原则上是不支持的*/

       uint16_t i2c_byte1 = 0, i2c_byte2 =0;  

uint16_t value = 0;
uint32_t size = 0, i = 0;
struct msm_camera_i2c_reg_array *i2c_tbl = NULL;
    •••••••••••••••
size = a_ctrl->reg_tbl_size;      //xxx_actuator.h里面配置table size
write_arr = a_ctrl->reg_tbl;
i2c_tbl = a_ctrl->i2c_reg_tbl;


for (i = 0; i < size; i++) {
/* check that the index into i2c_tbl cannot grow larger that
the allocated size of i2c_tbl */
if ((a_ctrl->total_steps + 1) < (a_ctrl->i2c_tbl_index))
break;
/*先是判断是否属于DAC类型,如果不属于,则有:
i2c_byte1 = write_arr[i].reg_addr;
i2c_byte2 = (hw_dword & write_arr[i].hw_mask) >>write_arr[i].hw_shift;
这个说明不属于DAC所以直接简第一位进行地址,第二位通过上面运算得到数据value作为byte2
如果属于DAC类型,value = (next_lens_position <<
write_arr[i].data_shift) | ((hw_dword & write_arr[i].hw_mask) >> write_arr[i].hw_shift);
然后判断是其寄存器地址是否等于0xFFF,如果不等于0xFFFF,则说明不是具体的某个寄存器,则:
i2c_byte1 = write_arr[i].reg_addr; i2c_byte2 = value;
如果DAC的寄存器不止一个的时候,i2c_byte2 = value & 0xFF;先存放到i2c_tbl中,然后第二个(貌似这里最多都只支持两个集训器)
如果寄存器的地址就是0xFFFF的时候,i2c_byte1 = (value & 0xFF00) >> 8;i2c_byte2 = value & 0xFF;高八位作为地址,第八位作为数据传输
代码实现见如下:*/
if (write_arr[i].reg_write_type == MSM_ACTUATOR_WRITE_DAC) {
value = (next_lens_position <<
write_arr[i].data_shift) |
((hw_dword & write_arr[i].hw_mask) >>
write_arr[i].hw_shift);
if (write_arr[i].reg_addr != 0xFFFF) {
i2c_byte1 = write_arr[i].reg_addr;
i2c_byte2 = value;
if (size != (i+1)) {
i2c_byte2 = value & 0xFF;
CDBG("byte1:0x%x, byte2:0x%x\n",i2c_byte1, i2c_byte2);
i2c_tbl[a_ctrl->i2c_tbl_index].reg_addr = i2c_byte1;
i2c_tbl[a_ctrl->i2c_tbl_index].reg_data = i2c_byte2;

i2c_tbl[a_ctrl->i2c_tbl_index].delay = 0;
a_ctrl->i2c_tbl_index++;i++;
i2c_byte1 = write_arr[i].reg_addr;
i2c_byte2 = (value & 0xFF00) >> 8;}
} else {
i2c_byte1 = (value & 0xFF00) >> 8;
i2c_byte2 = value & 0xFF;}
} else {
i2c_byte1 = write_arr[i].reg_addr;
i2c_byte2 = (hw_dword & write_arr[i].hw_mask) >>
write_arr[i].hw_shift;}
CDBG("i2c_byte1:0x%x, i2c_byte2:0x%x\n", i2c_byte1, i2c_byte2);
i2c_tbl[a_ctrl->i2c_tbl_index].reg_addr = i2c_byte1;
i2c_tbl[a_ctrl->i2c_tbl_index].reg_data = i2c_byte2;
i2c_tbl[a_ctrl->i2c_tbl_index].delay = delay;
a_ctrl->i2c_tbl_index++;}
CDBG("Exit\n");
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值