msm: camera: Add actuator driver

Add actuator driver that is responsible to move the actuator lens
for auto focus functionality. Register platform driver and create
v4l2 subdevice during probing. Add config functions for power up,
power down and move focus.

Change-Id: Icabf5dbd88fa64ea0a007834ffdd7a2b81eae567
Signed-off-by: Sreesudhan Ramakrish Ramkumar <srramku@codeaurora.org>
This commit is contained in:
Sreesudhan Ramakrish Ramkumar 2013-01-26 21:26:13 -08:00 committed by Iliyan Malchev
parent db5ded04d5
commit b29ac877a7
9 changed files with 1117 additions and 16 deletions

View file

@ -11,6 +11,20 @@ Required properties:
- interrupt-names : should specify relevant names to each interrupts
property defined.
* Qualcomm MSM ACTUATOR
Required properties:
- cell-index : should contain unique identifier to differentiate
between multiple actuators
- reg : should contain i2c slave address of the actuator and length of
data field which is 0x0
- compatible :
- "qcom,actuator"
- qcom,cci-master : should contain i2c master id to be used for this camera
sensor
- 0 -> MASTER 0
- 1 -> MASTER 1
Example:
qcom,cci@0xfda0c000 {

View file

@ -17,9 +17,9 @@
#include <media/msm_cam_sensor.h>
#include <mach/board.h>
enum msm_sensor_device_type_t {
MSM_SENSOR_I2C_DEVICE,
MSM_SENSOR_PLATFORM_DEVICE,
enum msm_camera_device_type_t {
MSM_CAMERA_I2C_DEVICE,
MSM_CAMERA_PLATFORM_DEVICE,
};
enum msm_bus_perf_setting {

View file

@ -3,6 +3,6 @@ ccflags-y += -Idrivers/media/video/msmb/msm_vb2
ccflags-y += -Idrivers/media/video/msmb/camera
ccflags-y += -Idrivers/media/video/msmb/sensor/io
ccflags-y += -Idrivers/media/video/msmb/sensor/cci
obj-$(CONFIG_MSMB_CAMERA) += cci/ io/ csiphy/ csid/
obj-$(CONFIG_MSMB_CAMERA) += cci/ io/ csiphy/ csid/ actuator/
obj-$(CONFIG_MSM_CAMERA_SENSOR) += msm_sensor.o
obj-$(CONFIG_S5K3L1YX) += s5k3l1yx.o

View file

@ -0,0 +1,4 @@
ccflags-y += -Idrivers/media/video/msmb
ccflags-y += -Idrivers/media/video/msmb/sensor/io
ccflags-y += -Idrivers/media/video/msmb/sensor/cci
obj-$(CONFIG_MSMB_CAMERA) += msm_actuator.o

View file

@ -0,0 +1,858 @@
/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__
#include <linux/module.h>
#include "msm_sd.h"
#include "msm_actuator.h"
#include "msm_cci.h"
DEFINE_MSM_MUTEX(msm_actuator_mutex);
/*#define MSM_ACUTUATOR_DEBUG*/
#undef CDBG
#ifdef MSM_ACUTUATOR_DEBUG
#define CDBG(fmt, args...) pr_err(fmt, ##args)
#else
#define CDBG(fmt, args...) pr_debug(fmt, ##args)
#endif
static struct msm_actuator_ctrl_t msm_actuator_t;
static struct msm_actuator msm_vcm_actuator_table;
static struct msm_actuator msm_piezo_actuator_table;
static struct msm_actuator *actuators[] = {
&msm_vcm_actuator_table,
&msm_piezo_actuator_table,
};
static int32_t msm_actuator_piezo_set_default_focus(
struct msm_actuator_ctrl_t *a_ctrl,
struct msm_actuator_move_params_t *move_params)
{
int32_t rc = 0;
CDBG("Enter\n");
if (a_ctrl->curr_step_pos != 0) {
a_ctrl->i2c_tbl_index = 0;
a_ctrl->func_tbl->actuator_parse_i2c_params(a_ctrl,
a_ctrl->initial_code, 0, 0);
a_ctrl->func_tbl->actuator_parse_i2c_params(a_ctrl,
a_ctrl->initial_code, 0, 0);
rc = a_ctrl->i2c_client.i2c_func_tbl->
i2c_write_table_w_microdelay(
&a_ctrl->i2c_client, a_ctrl->i2c_reg_tbl,
a_ctrl->i2c_tbl_index, a_ctrl->i2c_data_type);
if (rc < 0) {
pr_err("%s: i2c write error:%d\n",
__func__, rc);
return rc;
}
a_ctrl->i2c_tbl_index = 0;
a_ctrl->curr_step_pos = 0;
}
CDBG("Exit\n");
return rc;
}
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)
{
struct msm_actuator_reg_params_t *write_arr = a_ctrl->reg_tbl;
uint32_t hw_dword = hw_params;
uint16_t i2c_byte1 = 0, i2c_byte2 = 0;
uint16_t value = 0;
uint32_t size = a_ctrl->reg_tbl_size, i = 0;
struct msm_camera_i2c_reg_tbl *i2c_tbl = a_ctrl->i2c_reg_tbl;
CDBG("Enter\n");
for (i = 0; i < size; i++) {
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");
}
static int32_t msm_actuator_init_focus(struct msm_actuator_ctrl_t *a_ctrl,
uint16_t size, enum msm_actuator_data_type type,
struct reg_settings_t *settings)
{
int32_t rc = -EFAULT;
int32_t i = 0;
CDBG("Enter\n");
for (i = 0; i < size; i++) {
switch (type) {
case MSM_ACTUATOR_BYTE_DATA:
rc = a_ctrl->i2c_client.i2c_func_tbl->i2c_write(
&a_ctrl->i2c_client,
settings[i].reg_addr,
settings[i].reg_data, MSM_CAMERA_I2C_BYTE_DATA);
break;
case MSM_ACTUATOR_WORD_DATA:
rc = a_ctrl->i2c_client.i2c_func_tbl->i2c_write(
&a_ctrl->i2c_client,
settings[i].reg_addr,
settings[i].reg_data, MSM_CAMERA_I2C_WORD_DATA);
break;
default:
pr_err("Unsupport data type: %d\n", type);
break;
}
if (rc < 0)
break;
}
a_ctrl->curr_step_pos = 0;
CDBG("Exit\n");
return rc;
}
static void msm_actuator_write_focus(
struct msm_actuator_ctrl_t *a_ctrl,
uint16_t curr_lens_pos,
struct damping_params_t *damping_params,
int8_t sign_direction,
int16_t code_boundary)
{
int16_t next_lens_pos = 0;
uint16_t damping_code_step = 0;
uint16_t wait_time = 0;
CDBG("Enter\n");
damping_code_step = damping_params->damping_step;
wait_time = damping_params->damping_delay;
/* Write code based on damping_code_step in a loop */
for (next_lens_pos =
curr_lens_pos + (sign_direction * damping_code_step);
(sign_direction * next_lens_pos) <=
(sign_direction * code_boundary);
next_lens_pos =
(next_lens_pos +
(sign_direction * damping_code_step))) {
a_ctrl->func_tbl->actuator_parse_i2c_params(a_ctrl,
next_lens_pos, damping_params->hw_params, wait_time);
curr_lens_pos = next_lens_pos;
}
if (curr_lens_pos != code_boundary) {
a_ctrl->func_tbl->actuator_parse_i2c_params(a_ctrl,
code_boundary, damping_params->hw_params, wait_time);
}
CDBG("Exit\n");
}
static int32_t msm_actuator_piezo_move_focus(
struct msm_actuator_ctrl_t *a_ctrl,
struct msm_actuator_move_params_t *move_params)
{
int32_t dest_step_position = move_params->dest_step_pos;
int32_t rc = 0;
int32_t num_steps = move_params->num_steps;
CDBG("Enter\n");
if (num_steps == 0)
return rc;
a_ctrl->i2c_tbl_index = 0;
a_ctrl->func_tbl->actuator_parse_i2c_params(a_ctrl,
(num_steps *
a_ctrl->region_params[0].code_per_step),
move_params->ringing_params[0].hw_params, 0);
rc = a_ctrl->i2c_client.i2c_func_tbl->i2c_write_table_w_microdelay(
&a_ctrl->i2c_client,
a_ctrl->i2c_reg_tbl, a_ctrl->i2c_tbl_index,
a_ctrl->i2c_data_type);
if (rc < 0) {
pr_err("i2c write error:%d\n", rc);
return rc;
}
a_ctrl->i2c_tbl_index = 0;
a_ctrl->curr_step_pos = dest_step_position;
CDBG("Exit\n");
return rc;
}
static int32_t msm_actuator_move_focus(
struct msm_actuator_ctrl_t *a_ctrl,
struct msm_actuator_move_params_t *move_params)
{
int32_t rc = 0;
int8_t sign_dir = move_params->sign_dir;
uint16_t step_boundary = 0;
uint16_t target_step_pos = 0;
uint16_t target_lens_pos = 0;
int16_t dest_step_pos = move_params->dest_step_pos;
uint16_t curr_lens_pos = 0;
int dir = move_params->dir;
int32_t num_steps = move_params->num_steps;
CDBG("called, dir %d, num_steps %d\n", dir, num_steps);
if (dest_step_pos == a_ctrl->curr_step_pos)
return rc;
curr_lens_pos = a_ctrl->step_position_table[a_ctrl->curr_step_pos];
a_ctrl->i2c_tbl_index = 0;
CDBG("curr_step_pos =%d dest_step_pos =%d curr_lens_pos=%d\n",
a_ctrl->curr_step_pos, dest_step_pos, curr_lens_pos);
while (a_ctrl->curr_step_pos != dest_step_pos) {
step_boundary =
a_ctrl->region_params[a_ctrl->curr_region_index].
step_bound[dir];
if ((dest_step_pos * sign_dir) <=
(step_boundary * sign_dir)) {
target_step_pos = dest_step_pos;
target_lens_pos =
a_ctrl->step_position_table[target_step_pos];
a_ctrl->func_tbl->actuator_write_focus(a_ctrl,
curr_lens_pos,
&(move_params->
ringing_params[a_ctrl->
curr_region_index]),
sign_dir,
target_lens_pos);
curr_lens_pos = target_lens_pos;
} else {
target_step_pos = step_boundary;
target_lens_pos =
a_ctrl->step_position_table[target_step_pos];
a_ctrl->func_tbl->actuator_write_focus(a_ctrl,
curr_lens_pos,
&(move_params->ringing_params[a_ctrl->
curr_region_index]),
sign_dir,
target_lens_pos);
curr_lens_pos = target_lens_pos;
a_ctrl->curr_region_index += sign_dir;
}
a_ctrl->curr_step_pos = target_step_pos;
}
rc = a_ctrl->i2c_client.i2c_func_tbl->i2c_write_table_w_microdelay(
&a_ctrl->i2c_client,
a_ctrl->i2c_reg_tbl, a_ctrl->i2c_tbl_index,
a_ctrl->i2c_data_type);
if (rc < 0) {
pr_err("i2c write error:%d\n", rc);
return rc;
}
a_ctrl->i2c_tbl_index = 0;
CDBG("Exit\n");
return rc;
}
static int32_t msm_actuator_init_step_table(struct msm_actuator_ctrl_t *a_ctrl,
struct msm_actuator_set_info_t *set_info)
{
int16_t code_per_step = 0;
int16_t cur_code = 0;
int16_t step_index = 0, region_index = 0;
uint16_t step_boundary = 0;
uint32_t max_code_size = 1;
uint16_t data_size = set_info->actuator_params.data_size;
CDBG("Enter\n");
for (; data_size > 0; data_size--)
max_code_size *= 2;
kfree(a_ctrl->step_position_table);
a_ctrl->step_position_table = NULL;
/* Fill step position table */
a_ctrl->step_position_table =
kmalloc(sizeof(uint16_t) *
(set_info->af_tuning_params.total_steps + 1), GFP_KERNEL);
if (a_ctrl->step_position_table == NULL)
return -ENOMEM;
cur_code = set_info->af_tuning_params.initial_code;
a_ctrl->step_position_table[step_index++] = cur_code;
for (region_index = 0;
region_index < a_ctrl->region_size;
region_index++) {
code_per_step =
a_ctrl->region_params[region_index].code_per_step;
step_boundary =
a_ctrl->region_params[region_index].
step_bound[MOVE_NEAR];
for (; step_index <= step_boundary;
step_index++) {
cur_code += code_per_step;
if (cur_code < max_code_size)
a_ctrl->step_position_table[step_index] =
cur_code;
else {
for (; step_index <
set_info->af_tuning_params.total_steps;
step_index++)
a_ctrl->
step_position_table[
step_index] =
max_code_size;
}
}
}
CDBG("Exit\n");
return 0;
}
static int32_t msm_actuator_set_default_focus(
struct msm_actuator_ctrl_t *a_ctrl,
struct msm_actuator_move_params_t *move_params)
{
int32_t rc = 0;
CDBG("Enter\n");
if (a_ctrl->curr_step_pos != 0)
rc = a_ctrl->func_tbl->actuator_move_focus(a_ctrl, move_params);
CDBG("Exit\n");
return rc;
}
static int32_t msm_actuator_power_down(struct msm_actuator_ctrl_t *a_ctrl)
{
int32_t rc = 0;
CDBG("Enter\n");
if (a_ctrl->vcm_enable) {
rc = gpio_direction_output(a_ctrl->vcm_pwd, 0);
if (!rc)
gpio_free(a_ctrl->vcm_pwd);
}
kfree(a_ctrl->step_position_table);
a_ctrl->step_position_table = NULL;
kfree(a_ctrl->i2c_reg_tbl);
a_ctrl->i2c_reg_tbl = NULL;
a_ctrl->i2c_tbl_index = 0;
CDBG("Exit\n");
return rc;
}
static int32_t msm_actuator_init(struct msm_actuator_ctrl_t *a_ctrl,
struct msm_actuator_set_info_t *set_info) {
struct reg_settings_t *init_settings = NULL;
int32_t rc = -EFAULT;
uint16_t i = 0;
struct msm_camera_cci_client *cci_client = NULL;
CDBG("Enter\n");
for (i = 0; i < ARRAY_SIZE(actuators); i++) {
if (set_info->actuator_params.act_type ==
actuators[i]->act_type) {
a_ctrl->func_tbl = &actuators[i]->func_tbl;
rc = 0;
}
}
if (rc < 0) {
pr_err("Actuator function table not found\n");
return rc;
}
a_ctrl->region_size = set_info->af_tuning_params.region_size;
if (a_ctrl->region_size > MAX_ACTUATOR_REGION) {
pr_err("MAX_ACTUATOR_REGION is exceeded.\n");
return -EFAULT;
}
a_ctrl->pwd_step = set_info->af_tuning_params.pwd_step;
a_ctrl->total_steps = set_info->af_tuning_params.total_steps;
if (copy_from_user(&a_ctrl->region_params,
(void *)set_info->af_tuning_params.region_params,
a_ctrl->region_size * sizeof(struct region_params_t)))
return -EFAULT;
if (a_ctrl->act_device_type == MSM_CAMERA_PLATFORM_DEVICE) {
cci_client = a_ctrl->i2c_client.cci_client;
cci_client->sid =
set_info->actuator_params.i2c_addr >> 1;
cci_client->retries = 3;
cci_client->id_map = 0;
cci_client->cci_i2c_master = a_ctrl->cci_master;
} else {
a_ctrl->i2c_client.client->addr =
set_info->actuator_params.i2c_addr;
}
a_ctrl->i2c_data_type = set_info->actuator_params.i2c_data_type;
a_ctrl->i2c_client.addr_type = set_info->actuator_params.i2c_addr_type;
a_ctrl->reg_tbl_size = set_info->actuator_params.reg_tbl_size;
if (a_ctrl->reg_tbl_size > MAX_ACTUATOR_REG_TBL_SIZE) {
pr_err("MAX_ACTUATOR_REG_TBL_SIZE is exceeded.\n");
return -EFAULT;
}
a_ctrl->i2c_reg_tbl =
kmalloc(sizeof(struct msm_camera_i2c_reg_tbl) *
(set_info->af_tuning_params.total_steps + 1), GFP_KERNEL);
if (!a_ctrl->i2c_reg_tbl) {
pr_err("kmalloc fail\n");
return -ENOMEM;
}
if (copy_from_user(&a_ctrl->reg_tbl,
(void *)set_info->actuator_params.reg_tbl_params,
a_ctrl->reg_tbl_size *
sizeof(struct msm_actuator_reg_params_t))) {
kfree(a_ctrl->i2c_reg_tbl);
return -EFAULT;
}
if (set_info->actuator_params.init_setting_size) {
if (a_ctrl->func_tbl->actuator_init_focus) {
init_settings = kmalloc(sizeof(struct reg_settings_t) *
(set_info->actuator_params.init_setting_size),
GFP_KERNEL);
if (init_settings == NULL) {
kfree(a_ctrl->i2c_reg_tbl);
pr_err("Error allocating memory for init_settings\n");
return -EFAULT;
}
if (copy_from_user(init_settings,
(void *)set_info->actuator_params.init_settings,
set_info->actuator_params.init_setting_size *
sizeof(struct reg_settings_t))) {
kfree(init_settings);
kfree(a_ctrl->i2c_reg_tbl);
pr_err("Error copying init_settings\n");
return -EFAULT;
}
rc = a_ctrl->func_tbl->actuator_init_focus(a_ctrl,
set_info->actuator_params.init_setting_size,
a_ctrl->i2c_data_type,
init_settings);
kfree(init_settings);
if (rc < 0) {
kfree(a_ctrl->i2c_reg_tbl);
pr_err("Error actuator_init_focus\n");
return -EFAULT;
}
}
}
a_ctrl->initial_code = set_info->af_tuning_params.initial_code;
if (a_ctrl->func_tbl->actuator_init_step_table)
rc = a_ctrl->func_tbl->
actuator_init_step_table(a_ctrl, set_info);
a_ctrl->curr_step_pos = 0;
a_ctrl->curr_region_index = 0;
CDBG("Exit\n");
return rc;
}
static int32_t msm_actuator_config(struct msm_actuator_ctrl_t *a_ctrl,
void __user *argp)
{
struct msm_actuator_cfg_data *cdata =
(struct msm_actuator_cfg_data *)argp;
int32_t rc = 0;
mutex_lock(a_ctrl->actuator_mutex);
CDBG("Enter\n");
CDBG("%s type %d\n", __func__, cdata->cfgtype);
switch (cdata->cfgtype) {
case CFG_GET_ACTUATOR_INFO:
cdata->is_af_supported = 1;
cdata->cfg.cam_name = a_ctrl->cam_name;
break;
case CFG_SET_ACTUATOR_INFO:
rc = msm_actuator_init(a_ctrl, &cdata->cfg.set_info);
if (rc < 0)
pr_err("init table failed %d\n", rc);
break;
case CFG_SET_DEFAULT_FOCUS:
rc = a_ctrl->func_tbl->actuator_set_default_focus(a_ctrl,
&cdata->cfg.move);
if (rc < 0)
pr_err("move focus failed %d\n", rc);
break;
case CFG_MOVE_FOCUS:
rc = a_ctrl->func_tbl->actuator_move_focus(a_ctrl,
&cdata->cfg.move);
if (rc < 0)
pr_err("move focus failed %d\n", rc);
break;
default:
break;
}
mutex_unlock(a_ctrl->actuator_mutex);
CDBG("Exit\n");
return rc;
}
static int32_t msm_actuator_get_subdev_id(struct msm_actuator_ctrl_t *a_ctrl,
void *arg)
{
uint32_t *subdev_id = (uint32_t *)arg;
CDBG("Enter\n");
if (!subdev_id) {
pr_err("failed\n");
return -EINVAL;
}
*subdev_id = a_ctrl->pdev->id;
CDBG("subdev_id %d\n", *subdev_id);
CDBG("Exit\n");
return 0;
}
static struct msm_camera_i2c_fn_t msm_sensor_cci_func_tbl = {
.i2c_read = msm_camera_cci_i2c_read,
.i2c_read_seq = msm_camera_cci_i2c_read_seq,
.i2c_write = msm_camera_cci_i2c_write,
.i2c_write_table = msm_camera_cci_i2c_write_table,
.i2c_write_seq_table = msm_camera_cci_i2c_write_seq_table,
.i2c_write_table_w_microdelay =
msm_camera_cci_i2c_write_table_w_microdelay,
.i2c_util = msm_sensor_cci_i2c_util,
};
static struct msm_camera_i2c_fn_t msm_sensor_qup_func_tbl = {
.i2c_read = msm_camera_qup_i2c_read,
.i2c_read_seq = msm_camera_qup_i2c_read_seq,
.i2c_write = msm_camera_qup_i2c_write,
.i2c_write_table = msm_camera_qup_i2c_write_table,
.i2c_write_seq_table = msm_camera_qup_i2c_write_seq_table,
.i2c_write_table_w_microdelay =
msm_camera_qup_i2c_write_table_w_microdelay,
};
static int msm_actuator_open(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh) {
int rc = 0;
struct msm_actuator_ctrl_t *a_ctrl = v4l2_get_subdevdata(sd);
CDBG("Enter\n");
if (!a_ctrl) {
pr_err("failed\n");
return -EINVAL;
}
if (a_ctrl->act_device_type == MSM_CAMERA_PLATFORM_DEVICE) {
rc = a_ctrl->i2c_client.i2c_func_tbl->i2c_util(
&a_ctrl->i2c_client, MSM_CCI_INIT);
if (rc < 0)
pr_err("cci_init failed\n");
}
CDBG("Exit\n");
return rc;
}
static int msm_actuator_close(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh) {
int rc = 0;
struct msm_actuator_ctrl_t *a_ctrl = v4l2_get_subdevdata(sd);
CDBG("Enter\n");
if (!a_ctrl) {
pr_err("failed\n");
return -EINVAL;
}
if (a_ctrl->act_device_type == MSM_CAMERA_PLATFORM_DEVICE) {
rc = a_ctrl->i2c_client.i2c_func_tbl->i2c_util(
&a_ctrl->i2c_client, MSM_CCI_RELEASE);
if (rc < 0)
pr_err("cci_init failed\n");
}
CDBG("Exit\n");
return rc;
}
static const struct v4l2_subdev_internal_ops msm_actuator_internal_ops = {
.open = msm_actuator_open,
.close = msm_actuator_close,
};
static int32_t msm_actuator_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int rc = 0;
struct msm_actuator_ctrl_t *act_ctrl_t = NULL;
CDBG("Enter\n");
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
pr_err("i2c_check_functionality failed\n");
goto probe_failure;
}
act_ctrl_t = (struct msm_actuator_ctrl_t *)(id->driver_data);
CDBG("client = %x\n", (unsigned int) client);
act_ctrl_t->i2c_client.client = client;
/* Set device type as I2C */
act_ctrl_t->act_device_type = MSM_CAMERA_I2C_DEVICE;
act_ctrl_t->i2c_client.i2c_func_tbl = &msm_sensor_qup_func_tbl;
/* Assign name for sub device */
snprintf(act_ctrl_t->msm_sd.sd.name, sizeof(act_ctrl_t->msm_sd.sd.name),
"%s", act_ctrl_t->i2c_driver->driver.name);
/* Initialize sub device */
v4l2_i2c_subdev_init(&act_ctrl_t->msm_sd.sd,
act_ctrl_t->i2c_client.client,
act_ctrl_t->act_v4l2_subdev_ops);
v4l2_set_subdevdata(&act_ctrl_t->msm_sd.sd, act_ctrl_t);
act_ctrl_t->msm_sd.sd.internal_ops = &msm_actuator_internal_ops;
act_ctrl_t->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
media_entity_init(&act_ctrl_t->msm_sd.sd.entity, 0, NULL, 0);
act_ctrl_t->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
act_ctrl_t->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_ACTUATOR;
msm_sd_register(&act_ctrl_t->msm_sd);
CDBG("succeeded\n");
CDBG("Exit\n");
probe_failure:
return rc;
}
static int32_t msm_actuator_platform_probe(struct platform_device *pdev)
{
int32_t rc = 0;
struct msm_camera_cci_client *cci_client = NULL;
CDBG("Enter\n");
if (!pdev->dev.of_node) {
pr_err("of_node NULL\n");
return -EINVAL;
}
rc = of_property_read_u32((&pdev->dev)->of_node, "cell-index",
&pdev->id);
CDBG("cell-index %d, rc %d\n", pdev->id, rc);
if (rc < 0) {
pr_err("failed rc %d\n", rc);
return rc;
}
rc = of_property_read_u32((&pdev->dev)->of_node, "qcom,cci-master",
&msm_actuator_t.cci_master);
CDBG("qcom,cci-master %d, rc %d\n", msm_actuator_t.cci_master, rc);
if (rc < 0) {
pr_err("failed rc %d\n", rc);
return rc;
}
msm_actuator_t.cam_name = pdev->id;
/* Set platform device handle */
msm_actuator_t.pdev = pdev;
/* Set device type as platform device */
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(
struct msm_camera_cci_client), GFP_KERNEL);
if (!msm_actuator_t.i2c_client.cci_client) {
pr_err("failed no memory\n");
return -ENOMEM;
}
cci_client = msm_actuator_t.i2c_client.cci_client;
cci_client->cci_subdev = msm_cci_get_subdev();
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_sd_register(&msm_actuator_t.msm_sd);
CDBG("Exit\n");
return rc;
}
static int32_t msm_actuator_power_up(struct msm_actuator_ctrl_t *a_ctrl)
{
int rc = 0;
CDBG("%s called\n", __func__);
CDBG("vcm info: %x %x\n", a_ctrl->vcm_pwd,
a_ctrl->vcm_enable);
if (a_ctrl->vcm_enable) {
rc = gpio_request(a_ctrl->vcm_pwd, "msm_actuator");
if (!rc) {
CDBG("Enable VCM PWD\n");
gpio_direction_output(a_ctrl->vcm_pwd, 1);
}
}
CDBG("Exit\n");
return rc;
}
static const struct i2c_device_id msm_actuator_i2c_id[] = {
{"msm_actuator", (kernel_ulong_t)&msm_actuator_t},
{ }
};
static struct i2c_driver msm_actuator_i2c_driver = {
.id_table = msm_actuator_i2c_id,
.probe = msm_actuator_i2c_probe,
.remove = __exit_p(msm_actuator_i2c_remove),
.driver = {
.name = "msm_actuator",
},
};
static const struct of_device_id msm_actuator_dt_match[] = {
{.compatible = "qcom,actuator", .data = &msm_actuator_t},
{}
};
MODULE_DEVICE_TABLE(of, msm_actuator_dt_match);
static struct platform_driver msm_actuator_platform_driver = {
.driver = {
.name = "qcom,actuator",
.owner = THIS_MODULE,
.of_match_table = msm_actuator_dt_match,
},
};
static int __init msm_actuator_init_module(void)
{
int32_t rc = 0;
CDBG("Enter\n");
rc = platform_driver_probe(msm_actuator_t.pdriver,
msm_actuator_platform_probe);
if (!rc)
return rc;
CDBG("%s:%d rc %d\n", __func__, __LINE__, rc);
return i2c_add_driver(msm_actuator_t.i2c_driver);
}
static long msm_actuator_subdev_ioctl(struct v4l2_subdev *sd,
unsigned int cmd, void *arg)
{
struct msm_actuator_ctrl_t *a_ctrl = v4l2_get_subdevdata(sd);
void __user *argp = (void __user *)arg;
CDBG("Enter\n");
CDBG("%s:%d a_ctrl %p argp %p\n", __func__, __LINE__, a_ctrl, argp);
switch (cmd) {
case VIDIOC_MSM_SENSOR_GET_SUBDEV_ID:
return msm_actuator_get_subdev_id(a_ctrl, argp);
case VIDIOC_MSM_ACTUATOR_CFG:
return msm_actuator_config(a_ctrl, argp);
default:
return -ENOIOCTLCMD;
}
}
static int32_t msm_actuator_power(struct v4l2_subdev *sd, int on)
{
int rc = 0;
struct msm_actuator_ctrl_t *a_ctrl = v4l2_get_subdevdata(sd);
CDBG("Enter\n");
mutex_lock(a_ctrl->actuator_mutex);
if (on)
rc = msm_actuator_power_up(a_ctrl);
else
rc = msm_actuator_power_down(a_ctrl);
mutex_unlock(a_ctrl->actuator_mutex);
CDBG("Exit\n");
return rc;
}
static struct v4l2_subdev_core_ops msm_actuator_subdev_core_ops = {
.ioctl = msm_actuator_subdev_ioctl,
.s_power = msm_actuator_power,
};
static struct v4l2_subdev_ops msm_actuator_subdev_ops = {
.core = &msm_actuator_subdev_core_ops,
};
static struct msm_actuator_ctrl_t msm_actuator_t = {
.i2c_driver = &msm_actuator_i2c_driver,
.pdriver = &msm_actuator_platform_driver,
.act_v4l2_subdev_ops = &msm_actuator_subdev_ops,
.curr_step_pos = 0,
.curr_region_index = 0,
.actuator_mutex = &msm_actuator_mutex,
};
static struct 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,
},
};
static struct msm_actuator msm_piezo_actuator_table = {
.act_type = ACTUATOR_PIEZO,
.func_tbl = {
.actuator_init_step_table = NULL,
.actuator_move_focus = msm_actuator_piezo_move_focus,
.actuator_write_focus = NULL,
.actuator_set_default_focus =
msm_actuator_piezo_set_default_focus,
.actuator_init_focus = msm_actuator_init_focus,
.actuator_parse_i2c_params = msm_actuator_parse_i2c_params,
},
};
module_init(msm_actuator_init_module);
MODULE_DESCRIPTION("MSM ACTUATOR");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,85 @@
/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef MSM_ACTUATOR_H
#define MSM_ACTUATOR_H
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <mach/camera2.h>
#include <media/v4l2-subdev.h>
#include <media/msmb_camera.h>
#include "msm_camera_i2c.h"
#define DEFINE_MSM_MUTEX(mutexname) \
static struct mutex mutexname = __MUTEX_INITIALIZER(mutexname)
struct msm_actuator_ctrl_t;
struct msm_actuator_func_tbl {
int32_t (*actuator_i2c_write_b_af)(struct msm_actuator_ctrl_t *,
uint8_t,
uint8_t);
int32_t (*actuator_init_step_table)(struct msm_actuator_ctrl_t *,
struct msm_actuator_set_info_t *);
int32_t (*actuator_init_focus)(struct msm_actuator_ctrl_t *,
uint16_t, enum msm_actuator_data_type, struct reg_settings_t *);
int32_t (*actuator_set_default_focus) (struct msm_actuator_ctrl_t *,
struct msm_actuator_move_params_t *);
int32_t (*actuator_move_focus) (struct msm_actuator_ctrl_t *,
struct msm_actuator_move_params_t *);
void (*actuator_parse_i2c_params)(struct msm_actuator_ctrl_t *,
int16_t, uint32_t, uint16_t);
void (*actuator_write_focus)(struct msm_actuator_ctrl_t *,
uint16_t,
struct damping_params_t *,
int8_t,
int16_t);
};
struct msm_actuator {
enum actuator_type act_type;
struct msm_actuator_func_tbl func_tbl;
};
struct msm_actuator_ctrl_t {
struct i2c_driver *i2c_driver;
struct platform_driver *pdriver;
struct platform_device *pdev;
struct msm_camera_i2c_client i2c_client;
enum msm_camera_device_type_t act_device_type;
struct msm_sd_subdev msm_sd;
enum af_camera_name cam_name;
struct mutex *actuator_mutex;
struct msm_actuator_func_tbl *func_tbl;
enum msm_actuator_data_type i2c_data_type;
struct v4l2_subdev sdev;
struct v4l2_subdev_ops *act_v4l2_subdev_ops;
int16_t curr_step_pos;
uint16_t curr_region_index;
uint16_t *step_position_table;
struct region_params_t region_params[MAX_ACTUATOR_REGION];
uint16_t reg_tbl_size;
struct msm_actuator_reg_params_t reg_tbl[MAX_ACTUATOR_REG_TBL_SIZE];
uint16_t region_size;
void *user_data;
uint32_t vcm_pwd;
uint32_t vcm_enable;
uint32_t total_steps;
uint16_t pwd_step;
uint16_t initial_code;
struct msm_camera_i2c_reg_tbl *i2c_reg_tbl;
uint16_t i2c_tbl_index;
enum cci_i2c_master_t cci_master;
};
#endif

View file

@ -642,7 +642,7 @@ static int32_t msm_sensor_get_dt_data(struct platform_device *pdev,
rc = of_property_read_u32(of_node, "qcom,cci-master",
&s_ctrl->cci_i2c_master);
CDBG("%s qcom,cci-master %d, rc %d\n", __func__, s_ctrl->cci_i2c_master
CDBG("%s qcom,cci-master %d, rc %d\n", __func__, s_ctrl->cci_i2c_master,
rc);
if (rc < 0) {
/* Set default master 0 */
@ -888,7 +888,7 @@ int32_t msm_sensor_power_up(struct msm_sensor_ctrl_t *s_ctrl)
}
}
if (s_ctrl->sensor_device_type == MSM_SENSOR_PLATFORM_DEVICE) {
if (s_ctrl->sensor_device_type == MSM_CAMERA_PLATFORM_DEVICE) {
rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_util(
s_ctrl->sensor_i2c_client, MSM_CCI_INIT);
if (rc < 0) {
@ -910,7 +910,7 @@ int32_t msm_sensor_power_up(struct msm_sensor_ctrl_t *s_ctrl)
return 0;
power_up_failed:
pr_err("%s:%d failed\n", __func__, __LINE__);
if (s_ctrl->sensor_device_type == MSM_SENSOR_PLATFORM_DEVICE) {
if (s_ctrl->sensor_device_type == MSM_CAMERA_PLATFORM_DEVICE) {
s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_util(
s_ctrl->sensor_i2c_client, MSM_CCI_RELEASE);
}
@ -970,7 +970,7 @@ int32_t msm_sensor_power_down(struct msm_sensor_ctrl_t *s_ctrl)
CDBG("%s:%d\n", __func__, __LINE__);
power_setting_array = &s_ctrl->power_setting_array;
if (s_ctrl->sensor_device_type == MSM_SENSOR_PLATFORM_DEVICE) {
if (s_ctrl->sensor_device_type == MSM_CAMERA_PLATFORM_DEVICE) {
s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_util(
s_ctrl->sensor_i2c_client, MSM_CCI_RELEASE);
}
@ -1392,7 +1392,8 @@ static struct msm_camera_i2c_fn_t msm_sensor_qup_func_tbl = {
msm_camera_qup_i2c_write_table_w_microdelay,
};
int32_t msm_sensor_platform_probe(struct platform_device *pdev, void *data)
int32_t msm_sensor_platform_probe(struct platform_device *pdev,
const void *data)
{
int32_t rc = 0;
struct msm_sensor_ctrl_t *s_ctrl =
@ -1410,7 +1411,7 @@ int32_t msm_sensor_platform_probe(struct platform_device *pdev, void *data)
return rc;
}
}
s_ctrl->sensor_device_type = MSM_SENSOR_PLATFORM_DEVICE;
s_ctrl->sensor_device_type = MSM_CAMERA_PLATFORM_DEVICE;
s_ctrl->sensor_i2c_client->cci_client = kzalloc(sizeof(
struct msm_camera_cci_client), GFP_KERNEL);
if (!s_ctrl->sensor_i2c_client->cci_client) {
@ -1489,7 +1490,7 @@ int32_t msm_sensor_i2c_probe(struct i2c_client *client,
return -EINVAL;
}
s_ctrl->sensor_device_type = MSM_SENSOR_I2C_DEVICE;
s_ctrl->sensor_device_type = MSM_CAMERA_I2C_DEVICE;
s_ctrl->sensordata = client->dev.platform_data;
if (s_ctrl->sensordata == NULL) {
pr_err("%s %s NULL sensor data\n", __func__, client->name);

View file

@ -47,7 +47,7 @@ struct msm_sensor_fn_t {
struct msm_sensor_ctrl_t {
struct platform_device *pdev;
enum msm_sensor_device_type_t sensor_device_type;
enum msm_camera_device_type_t sensor_device_type;
enum cci_i2c_master_t cci_i2c_master;
struct msm_camera_sensor_board_info *sensordata;
struct msm_sensor_power_setting_array power_setting_array;
@ -77,7 +77,7 @@ int32_t msm_sensor_power_down(struct msm_sensor_ctrl_t *s_ctrl);
int32_t msm_sensor_match_id(struct msm_sensor_ctrl_t *s_ctrl);
int32_t msm_sensor_platform_probe(struct platform_device *pdev,
void *data);
const void *data);
int32_t msm_sensor_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id);

View file

@ -1,5 +1,5 @@
#ifndef __LINUX_MSM_CAM_SENSOR_H
#define __LINUX_MSM_CAM_SENSOR_H
#ifndef _UAPI_MEDIA_MSM_CAM_SENSOR_H
#define _UAPI_MEDIA_MSM_CAM_SENSOR_H
#ifdef MSM_CAMERA_BIONIC
#include <sys/types.h>
@ -33,6 +33,17 @@
#define MAX_SENSOR_NAME 32
#define MAX_ACT_MOD_NAME_SIZE 32
#define MAX_ACT_NAME_SIZE 32
#define NUM_ACTUATOR_DIR 2
#define MAX_ACTUATOR_SCENARIO 8
#define MAX_ACTUATOR_REGION 5
#define MAX_ACTUATOR_INIT_SET 12
#define MAX_ACTUATOR_REG_TBL_SIZE 8
#define MOVE_NEAR 0
#define MOVE_FAR 1
enum msm_camera_i2c_reg_addr_type {
MSM_CAMERA_I2C_BYTE_ADDR = 1,
MSM_CAMERA_I2C_WORD_ADDR,
@ -283,6 +294,131 @@ enum msm_sensor_cfg_type_t {
CFG_GET_SENSOR_INIT_PARAMS,
};
enum msm_actuator_cfg_type_t {
CFG_GET_ACTUATOR_INFO,
CFG_SET_ACTUATOR_INFO,
CFG_SET_DEFAULT_FOCUS,
CFG_MOVE_FOCUS,
};
enum actuator_type {
ACTUATOR_VCM,
ACTUATOR_PIEZO,
};
enum msm_actuator_data_type {
MSM_ACTUATOR_BYTE_DATA = 1,
MSM_ACTUATOR_WORD_DATA,
};
enum msm_actuator_addr_type {
MSM_ACTUATOR_BYTE_ADDR = 1,
MSM_ACTUATOR_WORD_ADDR,
};
struct reg_settings_t {
uint16_t reg_addr;
uint16_t reg_data;
};
struct region_params_t {
/* [0] = ForwardDirection Macro boundary
[1] = ReverseDirection Inf boundary
*/
uint16_t step_bound[2];
uint16_t code_per_step;
};
struct damping_params_t {
uint32_t damping_step;
uint32_t damping_delay;
uint32_t hw_params;
};
struct msm_actuator_move_params_t {
int8_t dir;
int8_t sign_dir;
int16_t dest_step_pos;
int32_t num_steps;
struct damping_params_t *ringing_params;
};
struct msm_actuator_tuning_params_t {
int16_t initial_code;
uint16_t pwd_step;
uint16_t region_size;
uint32_t total_steps;
struct region_params_t *region_params;
};
struct msm_actuator_params_t {
enum actuator_type act_type;
uint8_t reg_tbl_size;
uint16_t data_size;
uint16_t init_setting_size;
uint32_t i2c_addr;
enum msm_actuator_addr_type i2c_addr_type;
enum msm_actuator_data_type i2c_data_type;
struct msm_actuator_reg_params_t *reg_tbl_params;
struct reg_settings_t *init_settings;
};
struct msm_actuator_set_info_t {
struct msm_actuator_params_t actuator_params;
struct msm_actuator_tuning_params_t af_tuning_params;
};
struct msm_actuator_get_info_t {
uint32_t focal_length_num;
uint32_t focal_length_den;
uint32_t f_number_num;
uint32_t f_number_den;
uint32_t f_pix_num;
uint32_t f_pix_den;
uint32_t total_f_dist_num;
uint32_t total_f_dist_den;
uint32_t hor_view_angle_num;
uint32_t hor_view_angle_den;
uint32_t ver_view_angle_num;
uint32_t ver_view_angle_den;
};
enum af_camera_name {
ACTUATOR_MAIN_CAM_0,
ACTUATOR_MAIN_CAM_1,
ACTUATOR_MAIN_CAM_2,
ACTUATOR_MAIN_CAM_3,
ACTUATOR_MAIN_CAM_4,
ACTUATOR_MAIN_CAM_5,
ACTUATOR_WEB_CAM_0,
ACTUATOR_WEB_CAM_1,
ACTUATOR_WEB_CAM_2,
};
struct msm_actuator_cfg_data {
int cfgtype;
uint8_t is_af_supported;
union {
struct msm_actuator_move_params_t move;
struct msm_actuator_set_info_t set_info;
struct msm_actuator_get_info_t get_info;
enum af_camera_name cam_name;
} cfg;
};
enum msm_actuator_write_type {
MSM_ACTUATOR_WRITE_HW_DAMP,
MSM_ACTUATOR_WRITE_DAC,
};
struct msm_actuator_reg_params_t {
enum msm_actuator_write_type reg_write_type;
uint32_t hw_mask;
uint16_t reg_addr;
uint16_t hw_shift;
uint16_t data_shift;
};
#define VIDIOC_MSM_SENSOR_CFG \
_IOWR('V', BASE_VIDIOC_PRIVATE + 1, struct sensorb_cfg_data)
@ -298,6 +434,9 @@ enum msm_sensor_cfg_type_t {
#define VIDIOC_MSM_CSID_IO_CFG \
_IOWR('V', BASE_VIDIOC_PRIVATE + 5, struct csiphy_cfg_data)
#define VIDIOC_MSM_ACTUATOR_CFG \
_IOWR('V', BASE_VIDIOC_PRIVATE + 6, struct msm_actuator_cfg_data)
#define MSM_V4L2_PIX_FMT_META v4l2_fourcc('M', 'E', 'T', 'A') /* META */
#endif /* __LINUX_MSM_CAM_SENSOR_H */
#endif /* _UAPI_MEDIA_MSM_CAM_SENSOR_H */