高通camera马达驱动代码分析

xiaozhu12371237 2016-10-07 06:26:00
加精

一、手机自动对焦原理
手机自动对焦功能是通过将摄像头锁入音圈马达来实现的,音圈马达简称(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, ®_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");
}
...全文
6295 8 打赏 收藏 举报
写回复
8 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
Winston_Jory 2017-04-19
  • 打赏
  • 举报
回复
http://blog.csdn.net/weijory/article/details/52711540
工业人工智能 2017-03-14
  • 打赏
  • 举报
回复
这是真正的好文章。
细水长流- 2016-10-12
  • 打赏
  • 举报
回复
棒!
line_us 2016-10-12
  • 打赏
  • 举报
回复
好厉害的样子
aierda 2016-10-11
  • 打赏
  • 举报
回复
感谢楼主分享,学习了
moyang628 2016-10-11
  • 打赏
  • 举报
回复
感谢分享
nettman 2016-10-11
  • 打赏
  • 举报
回复
感谢楼主分享,学习了
一苇渡江694 2016-10-11
  • 打赏
  • 举报
回复
相关推荐
发帖
Qualcomm开发

700

社区成员

为移动开发者提供丰富的解决方案、全面的技术下载。本版以游戏、多媒体、高效能等三个技术为核心,为开发者营造一个轻松、高效的学习交流平台。
社区管理员
  • Qualcomm开发
  • 霍大神
加入社区
帖子事件
创建了帖子
2016-10-07 06:26
社区公告
暂无公告