您的位置:首页 > 编程语言

qualcomm的camera马达驱动代码分析

2017-06-16 13:16 253 查看
前言:

主要给大家介绍基于高通平台的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, ®_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);

三、大致的工作流程<
4000
/p>

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”);

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息