msm: camera: Actuator changes for 8960
1) Added actuator framework with common functions abstracted out
2) Added actuator as a sub device in media controller
3) coupled sensor with its specific actuator in board file
4) Added new actuator control structure and new enum for communication
between user space and kernel space
5) Added common logging module with different logging levels
Signed-off-by: Sreesudhan Ramakrish Ramkumar <srramku@codeaurora.org>
Conflicts:
arch/arm/configs/msm8960_defconfig
diff --git a/arch/arm/configs/msm8960_defconfig b/arch/arm/configs/msm8960_defconfig
index 9e6856b..c781cbe 100644
--- a/arch/arm/configs/msm8960_defconfig
+++ b/arch/arm/configs/msm8960_defconfig
@@ -265,6 +265,7 @@
CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
CONFIG_USB_VIDEO_CLASS=y
CONFIG_MSM_CAMERA_V4L2=y
+CONFIG_MSM_ACTUATOR=y
CONFIG_MSM_CAMERA_SENSOR=y
CONFIG_MSM_CAMERA_FLASH_SC628A=y
CONFIG_MSM_GEMINI=y
diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h
index f752bf5..6063a41 100644
--- a/arch/arm/mach-msm/include/mach/board.h
+++ b/arch/arm/mach-msm/include/mach/board.h
@@ -186,6 +186,13 @@
uint8_t cam_gpio_tbl_size;
};
+struct msm_actuator_info {
+ struct i2c_board_info const *board_info;
+ int bus_id;
+ int vcm_pwd;
+ int vcm_enable;
+};
+
struct msm_camera_sensor_info {
const char *sensor_name;
int sensor_reset_enable;
@@ -206,6 +213,7 @@
char *eeprom_data;
struct msm_camera_gpio_conf *gpio_conf;
enum msm_camera_type camera_type;
+ struct msm_actuator_info *actuator_info;
};
int __init msm_get_cam_resources(struct msm_camera_sensor_info *);
diff --git a/arch/arm/mach-msm/include/mach/camera.h b/arch/arm/mach-msm/include/mach/camera.h
index 5b0e019..59bde6d 100644
--- a/arch/arm/mach-msm/include/mach/camera.h
+++ b/arch/arm/mach-msm/include/mach/camera.h
@@ -314,6 +314,14 @@
enum msm_st_frame_packing s_video_packing;
enum msm_st_frame_packing s_snap_packing;
};
+
+struct msm_actuator_ctrl {
+ int (*a_init_table)(void);
+ int (*a_power_down)(void);
+ int (*a_create_subdevice)(void *, void *);
+ int (*a_config)(void __user *);
+};
+
struct msm_strobe_flash_ctrl {
int (*strobe_flash_init)
(struct msm_camera_sensor_strobe_flash_data *);
@@ -379,6 +387,7 @@
struct msm_camvpe_fn vpefn;
struct msm_sensor_ctrl sctrl;
struct msm_strobe_flash_ctrl sfctrl;
+ struct msm_actuator_ctrl actctrl;
struct wake_lock wake_lock;
struct platform_device *pdev;
int16_t ignore_qcmd_type;
diff --git a/drivers/media/video/msm/Kconfig b/drivers/media/video/msm/Kconfig
index 7034556..56fd9db 100644
--- a/drivers/media/video/msm/Kconfig
+++ b/drivers/media/video/msm/Kconfig
@@ -172,6 +172,10 @@
bool "Qualcomm MSM camera sensor support"
depends on MSM_CAMERA
+config MSM_ACTUATOR
+ bool "Qualcomm MSM actuator support"
+ depends on MSM_CAMERA
+
config MSM_GEMINI
tristate "Qualcomm MSM Gemini Jpeg Engine support"
depends on MSM_CAMERA && (ARCH_MSM7X30 || ARCH_MSM8X60 || ARCH_MSM8960)
diff --git a/drivers/media/video/msm/Makefile b/drivers/media/video/msm/Makefile
index c50a620..35795fc 100644
--- a/drivers/media/video/msm/Makefile
+++ b/drivers/media/video/msm/Makefile
@@ -5,7 +5,7 @@
ifeq ($(CONFIG_MSM_CAMERA_V4L2),y)
obj-$(CONFIG_MSM_CAMERA) += msm_isp.o msm.o msm_mem.o msm_mctl.o msm_mctl_buf.o
- obj-$(CONFIG_MSM_CAMERA) += io/ sensors/
+ obj-$(CONFIG_MSM_CAMERA) += io/ sensors/ actuators/
else
obj-$(CONFIG_MSM_CAMERA) += msm_camera.o
endif
diff --git a/drivers/media/video/msm/actuators/Makefile b/drivers/media/video/msm/actuators/Makefile
new file mode 100644
index 0000000..70a3a19
--- /dev/null
+++ b/drivers/media/video/msm/actuators/Makefile
@@ -0,0 +1,4 @@
+GCC_VERSION := $(shell $(CONFIG_SHELL) $(PWD)/scripts/gcc-version.sh $(CROSS_COMPILE)gcc)
+EXTRA_CFLAGS += -Idrivers/media/video/msm
+EXTRA_CFLAGS += -Idrivers/media/video/msm/io
+obj-$(CONFIG_MSM_ACTUATOR) += msm_actuator.o
diff --git a/drivers/media/video/msm/actuators/msm_actuator.c b/drivers/media/video/msm/actuators/msm_actuator.c
new file mode 100644
index 0000000..b466903
--- /dev/null
+++ b/drivers/media/video/msm/actuators/msm_actuator.c
@@ -0,0 +1,585 @@
+/* Copyright (c) 2011, Code Aurora Forum. 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.
+ */
+
+#include "msm_actuator.h"
+#include "msm_logging.h"
+
+LDECVAR(a_profstarttime);
+LDECVAR(a_profendtime);
+
+static int msm_actuator_debug_init(struct msm_actuator_ctrl_t *a_ctrl);
+
+int32_t 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)
+{
+ int32_t rc = 0;
+ int16_t next_lens_pos = 0;
+ uint16_t damping_code_step = 0;
+ uint16_t wait_time = 0;
+
+ 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))) {
+ rc = a_ctrl->func_tbl.
+ actuator_i2c_write(a_ctrl, next_lens_pos);
+ curr_lens_pos = next_lens_pos;
+ usleep(wait_time);
+ }
+
+ if (curr_lens_pos != code_boundary) {
+ rc = a_ctrl->func_tbl.
+ actuator_i2c_write(a_ctrl, code_boundary);
+ usleep(wait_time);
+ }
+ return rc;
+}
+
+
+int32_t msm_actuator_move_focus(
+ struct msm_actuator_ctrl_t *a_ctrl,
+ int dir,
+ int32_t num_steps)
+{
+ int32_t rc = 0;
+ int8_t sign_dir = 0;
+ uint16_t curr_scene = 0;
+ uint16_t scenario_size = 0;
+ uint16_t index = 0;
+ uint16_t step_boundary = 0;
+ uint16_t target_step_pos = 0;
+ uint16_t target_lens_pos = 0;
+ int16_t dest_step_pos = 0;
+ uint16_t curr_lens_pos = 0;
+ LINFO("%s called, dir %d, num_steps %d\n",
+ __func__,
+ dir,
+ num_steps);
+
+ /* Determine sign direction */
+ if (dir == MOVE_NEAR)
+ sign_dir = 1;
+ else if (dir == MOVE_FAR)
+ sign_dir = -1;
+ else {
+ pr_err("Illegal focus direction\n");
+ rc = -EINVAL;
+ return rc;
+ }
+
+ /* Determine destination step position */
+ dest_step_pos = a_ctrl->curr_step_pos +
+ (sign_dir * num_steps);
+
+ if (dest_step_pos < 0)
+ dest_step_pos = 0;
+ else if (dest_step_pos > a_ctrl->total_steps)
+ dest_step_pos = a_ctrl->total_steps;
+
+ if (dest_step_pos == a_ctrl->curr_step_pos)
+ return rc;
+
+ /* Determine scenario */
+ scenario_size = a_ctrl->scenario_size[dir];
+ for (index = 0; index < scenario_size; index++) {
+ if (num_steps <= a_ctrl->ringing_scenario[dir][index]) {
+ curr_scene = index;
+ break;
+ }
+ }
+
+ curr_lens_pos = a_ctrl->step_position_table[a_ctrl->curr_step_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];
+ curr_lens_pos = a_ctrl->func_tbl.
+ actuator_write_focus(
+ a_ctrl,
+ curr_lens_pos,
+ &(a_ctrl->damping[dir]\
+ [a_ctrl->curr_region_index].
+ ringing_params[curr_scene]),
+ sign_dir,
+ target_lens_pos);
+
+ } else {
+ target_step_pos = step_boundary;
+ target_lens_pos =
+ a_ctrl->step_position_table[target_step_pos];
+ curr_lens_pos = a_ctrl->func_tbl.
+ actuator_write_focus(
+ a_ctrl,
+ curr_lens_pos,
+ &(a_ctrl->damping[dir]\
+ [a_ctrl->curr_region_index].
+ ringing_params[curr_scene]),
+ sign_dir,
+ target_lens_pos);
+
+ a_ctrl->curr_region_index += sign_dir;
+ }
+ a_ctrl->curr_step_pos = target_step_pos;
+ }
+
+ return rc;
+}
+
+int32_t msm_actuator_init_table(
+ struct msm_actuator_ctrl_t *a_ctrl)
+{
+ int16_t code_per_step = 0;
+ int32_t rc = 0;
+ int16_t cur_code = 0;
+ int16_t step_index = 0, region_index = 0;
+ uint16_t step_boundary = 0;
+ LINFO("%s called\n", __func__);
+
+ if (a_ctrl->func_tbl.actuator_set_params)
+ a_ctrl->func_tbl.actuator_set_params(a_ctrl);
+
+ /* Fill step position table */
+ a_ctrl->step_position_table =
+ kmalloc(sizeof(uint16_t) * (a_ctrl->total_steps + 1),
+ GFP_KERNEL);
+ cur_code = a_ctrl->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;
+ a_ctrl->step_position_table[step_index] = cur_code;
+ }
+ }
+
+ LINFO("step position table\n");
+ for (step_index = 0; step_index < a_ctrl->total_steps; step_index++) {
+ LINFO("spt i %d, val %d\n",
+ step_index,
+ a_ctrl->step_position_table[step_index]);
+ }
+
+ a_ctrl->curr_step_pos = 0;
+ a_ctrl->curr_region_index = 0;
+
+ return rc;
+}
+
+int32_t msm_actuator_set_default_focus(
+ struct msm_actuator_ctrl_t *a_ctrl)
+{
+ int32_t rc = 0;
+ LINFO("%s called\n", __func__);
+
+ if (!a_ctrl->step_position_table)
+ a_ctrl->func_tbl.actuator_init_table(a_ctrl);
+
+ if (a_ctrl->curr_step_pos != 0)
+ rc = a_ctrl->func_tbl.actuator_move_focus(a_ctrl, MOVE_FAR,
+ a_ctrl->curr_step_pos);
+ else if (a_ctrl->func_tbl.actuator_init_focus)
+ rc = a_ctrl->func_tbl.actuator_init_focus(a_ctrl);
+ return rc;
+}
+
+int32_t msm_actuator_af_power_down(struct msm_actuator_ctrl_t *a_ctrl)
+{
+ int32_t rc = 0;
+ LINFO("%s called\n", __func__);
+
+ if (a_ctrl->step_position_table[a_ctrl->curr_step_pos] !=
+ a_ctrl->initial_code) {
+ rc = a_ctrl->func_tbl.actuator_set_default_focus(a_ctrl);
+ LINFO("%s after msm_actuator_set_default_focus\n", __func__);
+ }
+ kfree(a_ctrl->step_position_table);
+ kfree(a_ctrl->debugfs_data);
+ return rc;
+}
+
+int32_t msm_actuator_config(
+ struct msm_actuator_ctrl_t *a_ctrl,
+ void __user *argp)
+{
+ struct msm_actuator_cfg_data cdata;
+ int32_t rc = 0;
+ if (copy_from_user(&cdata,
+ (void *)argp,
+ sizeof(struct msm_actuator_cfg_data)))
+ return -EFAULT;
+ mutex_lock(a_ctrl->actuator_mutex);
+ LINFO("%s called, type %d\n", __func__, cdata.cfgtype);
+ switch (cdata.cfgtype) {
+ case CFG_SET_ACTUATOR_INFO:
+ a_ctrl->total_steps = cdata.cfg.info.total_steps;
+ a_ctrl->gross_steps = cdata.cfg.info.gross_steps;
+ a_ctrl->fine_steps = cdata.cfg.info.fine_steps;
+ rc = a_ctrl->func_tbl.actuator_init_table(a_ctrl);
+ if (rc < 0)
+ LERROR("%s init table failed %d\n", __func__, rc);
+ break;
+
+ case CFG_SET_DEFAULT_FOCUS:
+ rc = a_ctrl->func_tbl.actuator_set_default_focus(a_ctrl);
+ if (rc < 0)
+ LERROR("%s move focus failed %d\n", __func__, rc);
+ break;
+
+ case CFG_MOVE_FOCUS:
+ rc = a_ctrl->func_tbl.actuator_move_focus(a_ctrl,
+ cdata.cfg.move.dir,
+ cdata.cfg.move.num_steps);
+ if (rc < 0)
+ LERROR("%s move focus failed %d\n", __func__, rc);
+ break;
+
+ default:
+ break;
+ }
+ mutex_unlock(a_ctrl->actuator_mutex);
+ return rc;
+}
+
+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;
+ LINFO("%s called\n", __func__);
+
+ 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);
+ i2c_set_clientdata(client, (void *)&act_ctrl_t->actuator_ext_ctrl);
+ LINFO("%s client = %x act ctrl t = %x\n",
+ __func__,
+ (unsigned int) client,
+ (unsigned int)&act_ctrl_t->actuator_ext_ctrl);
+ act_ctrl_t->i2c_client.client = client;
+ if (act_ctrl_t->i2c_addr != 0)
+ act_ctrl_t->i2c_client.client->addr =
+ act_ctrl_t->i2c_addr;
+
+ /* act_ctrl_t->func_tbl.actuator_init_table(act_ctrl_t); */
+ LINFO("%s succeeded\n", __func__);
+ return rc;
+
+probe_failure:
+ pr_err("%s failed! rc = %d\n", __func__, rc);
+ return rc;
+}
+
+int32_t msm_actuator_create_subdevice(struct msm_actuator_ctrl_t *a_ctrl,
+ struct i2c_board_info const *board_info,
+ struct v4l2_subdev *sdev)
+{
+ int32_t rc = 0;
+
+ LINFO("%s called\n", __func__);
+
+ /* Store the sub device in actuator structure */
+ a_ctrl->sdev = sdev;
+
+ /* Assign name for sub device */
+ snprintf(sdev->name, sizeof(sdev->name), "%s", board_info->type);
+
+ /* Initialize sub device */
+ v4l2_i2c_subdev_init(sdev,
+ a_ctrl->i2c_client.client,
+ a_ctrl->act_v4l2_subdev_ops);
+
+ /* Initialize debugfs */
+ if (a_ctrl->debugfs_flag) {
+ msm_actuator_debug_init(a_ctrl);
+ a_ctrl->debugfs_flag = 0;
+ }
+ return rc;
+}
+
+static int msm_actuator_set_linear_total_step(void *data, u64 val)
+{
+ struct msm_actuator_ctrl_t *a_ctrl = (struct msm_actuator_ctrl_t *)data;
+
+ /* Update total steps */
+ a_ctrl->total_steps = val;
+
+ /* Update last region's macro boundary in region params table */
+ a_ctrl->region_params[a_ctrl->region_size-1].step_bound[MOVE_NEAR] =
+ val;
+
+ /* Call init table to re-create step position table
+ based on updated total steps */
+ a_ctrl->func_tbl.actuator_init_table(a_ctrl);
+
+ LINFO("%s called, total steps = %d\n",
+ __func__,
+ a_ctrl->total_steps);
+
+ return 0;
+}
+
+static int msm_actuator_af_linearity_test(void *data, u64 *val)
+{
+ int16_t index = 0;
+ struct msm_actuator_ctrl_t *a_ctrl = (struct msm_actuator_ctrl_t *)data;
+
+ a_ctrl->func_tbl.actuator_set_default_focus(a_ctrl);
+ msleep(1000);
+
+ for (index = 0; index < a_ctrl->total_steps; index++) {
+ a_ctrl->func_tbl.actuator_move_focus(a_ctrl, MOVE_NEAR, 1);
+ LINFO("debugfs, new loc %d\n", index);
+ msleep(1000);
+ }
+
+ LINFO("debugfs moved to macro boundary\n");
+
+ for (index = 0; index < a_ctrl->total_steps; index++) {
+ a_ctrl->func_tbl.actuator_move_focus(a_ctrl, MOVE_FAR, 1);
+ LINFO("debugfs, new loc %d\n", index);
+ msleep(1000);
+ }
+
+ return 0;
+}
+
+static int msm_actuator_af_ring_config(void *data, u64 val)
+{
+ struct msm_actuator_ctrl_t *a_ctrl = (struct msm_actuator_ctrl_t *)data;
+ LINFO("%s called val %llu\n",
+ __func__,
+ val);
+ a_ctrl->debugfs_data->step_boundary = val & 0xFF;
+ a_ctrl->debugfs_data->step_value = (val >> 8) & 0xFF;
+ a_ctrl->debugfs_data->step_dir = (val >> 16) & 0x01;
+ LINFO("%s called dir %d, val %d, bound %d\n",
+ __func__,
+ a_ctrl->debugfs_data->step_dir,
+ a_ctrl->debugfs_data->step_value,
+ a_ctrl->debugfs_data->step_boundary);
+
+ return 0;
+}
+
+static int msm_actuator_af_ring(void *data, u64 *val)
+{
+ int i = 0;
+ int dir = MOVE_NEAR;
+ struct msm_actuator_ctrl_t *a_ctrl = (struct msm_actuator_ctrl_t *)data;
+ LINFO("%s called\n", __func__);
+ if (a_ctrl->debugfs_data->step_dir == 1)
+ dir = MOVE_FAR;
+
+ LINFO("%s called curr step %d step val %d, dir %d, bound %d\n",
+ __func__,
+ a_ctrl->curr_step_pos,
+ a_ctrl->debugfs_data->step_value,
+ a_ctrl->debugfs_data->step_dir,
+ a_ctrl->debugfs_data->step_boundary);
+
+ for (i = 0;
+ i < a_ctrl->debugfs_data->step_boundary;
+ i += a_ctrl->debugfs_data->step_value) {
+ LINFO("==================================================\n");
+ LINFO("i %d\n", i);
+ /* Measure start time */
+ LPROFSTART(a_profstarttime);
+
+ msm_actuator_move_focus(a_ctrl,
+ dir,
+ a_ctrl->debugfs_data->step_value);
+
+ /* Measure end time */
+ LPROFEND(a_profstarttime, a_profendtime);
+ LINFO("==================================================\n");
+ LINFO("%s called curr step %d\n",
+ __func__,
+ a_ctrl->curr_step_pos);
+
+ msleep(1000);
+ }
+ *val = a_ctrl->curr_step_pos;
+ return 0;
+}
+
+static int msm_actuator_set_af_codestep(void *data, u64 val)
+{
+ struct msm_actuator_ctrl_t *a_ctrl = (struct msm_actuator_ctrl_t *)data;
+ LINFO("%s called, val %lld\n", __func__, val);
+
+ /* Update last region's macro boundary in region params table */
+ a_ctrl->region_params[a_ctrl->region_size-1].code_per_step = val;
+
+ /* Call init table to re-create step position table
+ based on updated total steps */
+ a_ctrl->func_tbl.actuator_init_table(a_ctrl);
+
+ LINFO("%s called, code per step = %d\n",
+ __func__,
+ a_ctrl->region_params[a_ctrl->region_size-1].code_per_step);
+ return 0;
+}
+
+static int msm_actuator_get_af_codestep(void *data, u64 *val)
+{
+ struct msm_actuator_ctrl_t *a_ctrl = (struct msm_actuator_ctrl_t *)data;
+ LINFO("%s called\n", __func__);
+
+ *val = a_ctrl->region_params[a_ctrl->region_size-1].code_per_step;
+ return 0;
+}
+
+static int msm_actuator_call_init_table(void *data, u64 *val)
+{
+ struct msm_actuator_ctrl_t *a_ctrl = (struct msm_actuator_ctrl_t *)data;
+ LINFO("%s called\n", __func__);
+ a_ctrl->func_tbl.actuator_init_table(a_ctrl);
+ LINFO("%s end\n", __func__);
+ return 0;
+}
+
+static int msm_actuator_call_default_focus(void *data, u64 *val)
+{
+ struct msm_actuator_ctrl_t *a_ctrl = (struct msm_actuator_ctrl_t *)data;
+ LINFO("%s called\n", __func__);
+ a_ctrl->func_tbl.actuator_set_default_focus(a_ctrl);
+ msleep(3000);
+ LINFO("%s end\n", __func__);
+ return 0;
+}
+
+static int msm_actuator_getcurstep(void *data, u64 *val)
+{
+ struct msm_actuator_ctrl_t *a_ctrl = (struct msm_actuator_ctrl_t *)data;
+ LINFO("%s called, cur steps %d\n",
+ __func__,
+ a_ctrl->curr_step_pos);
+ *val = a_ctrl->curr_step_pos;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(af_linear,
+ msm_actuator_af_linearity_test,
+ msm_actuator_set_linear_total_step,
+ "%llu\n");
+
+DEFINE_SIMPLE_ATTRIBUTE(af_ring,
+ msm_actuator_af_ring,
+ msm_actuator_af_ring_config,
+ "%llu\n");
+
+DEFINE_SIMPLE_ATTRIBUTE(af_codeperstep,
+ msm_actuator_get_af_codestep,
+ msm_actuator_set_af_codestep,
+ "%llu\n");
+
+DEFINE_SIMPLE_ATTRIBUTE(af_init,
+ msm_actuator_call_init_table,
+ NULL,
+ "%llu\n");
+
+DEFINE_SIMPLE_ATTRIBUTE(af_default,
+ msm_actuator_call_default_focus,
+ NULL,
+ "%llu\n");
+
+DEFINE_SIMPLE_ATTRIBUTE(af_getcurstep,
+ msm_actuator_getcurstep,
+ NULL,
+ "%llu\n");
+
+static int msm_actuator_debug_init(struct msm_actuator_ctrl_t *a_ctrl)
+{
+ struct dentry *debugfs_base = debugfs_create_dir("msm_actuator", NULL);
+ if (!debugfs_base)
+ return -ENOMEM;
+
+ a_ctrl->debugfs_data = kmalloc(sizeof(struct debugfs_params_t),
+ GFP_KERNEL);
+ if (!a_ctrl->debugfs_data)
+ return -ENOMEM;
+
+ a_ctrl->debugfs_data->step_value = 4;
+ a_ctrl->debugfs_data->step_boundary = a_ctrl->total_steps;
+
+ if (!debugfs_create_file("af_linear",
+ S_IRUGO | S_IWUSR,
+ debugfs_base,
+ (void *)a_ctrl,
+ &af_linear))
+ return -ENOMEM;
+
+ if (!debugfs_create_file("af_ring",
+ S_IRUGO | S_IWUSR,
+ debugfs_base,
+ (void *)a_ctrl,
+ &af_ring))
+ return -ENOMEM;
+
+ if (!debugfs_create_file("af_codeperstep",
+ S_IRUGO | S_IWUSR,
+ debugfs_base,
+ (void *)a_ctrl,
+ &af_codeperstep))
+ return -ENOMEM;
+
+ if (!debugfs_create_file("af_init",
+ S_IRUGO | S_IWUSR,
+ debugfs_base,
+ (void *)a_ctrl,
+ &af_init))
+ return -ENOMEM;
+
+ if (!debugfs_create_file("af_default",
+ S_IRUGO | S_IWUSR,
+ debugfs_base,
+ (void *)a_ctrl,
+ &af_default))
+ return -ENOMEM;
+
+ if (!debugfs_create_file("af_getcurstep",
+ S_IRUGO | S_IWUSR,
+ debugfs_base,
+ (void *)a_ctrl,
+ &af_getcurstep))
+ return -ENOMEM;
+ return 0;
+}
diff --git a/drivers/media/video/msm/actuators/msm_actuator.h b/drivers/media/video/msm/actuators/msm_actuator.h
new file mode 100644
index 0000000..2ea5571
--- /dev/null
+++ b/drivers/media/video/msm/actuators/msm_actuator.h
@@ -0,0 +1,119 @@
+/* Copyright (c) 2011, Code Aurora Forum. 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/debugfs.h>
+#include <mach/camera.h>
+#include <media/v4l2-subdev.h>
+#include <media/msm_camera.h>
+#include "msm_camera_i2c.h"
+
+struct msm_actuator_ctrl_t;
+
+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 {
+ uint16_t damping_step;
+ uint16_t damping_delay;
+ void *hw_params;
+};
+
+struct damping_t {
+ struct damping_params_t *ringing_params;
+};
+
+struct debugfs_params_t {
+ uint8_t step_dir;
+ uint16_t step_value;
+ uint16_t step_boundary;
+};
+
+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_table)(struct msm_actuator_ctrl_t *);
+ int32_t (*actuator_init_focus)(struct msm_actuator_ctrl_t *);
+ int32_t (*actuator_set_default_focus) (struct msm_actuator_ctrl_t *);
+ int32_t (*actuator_set_params)(struct msm_actuator_ctrl_t *);
+ int32_t (*actuator_move_focus) (struct msm_actuator_ctrl_t *,
+ int,
+ int32_t);
+ int (*actuator_power_down) (struct msm_actuator_ctrl_t *);
+ int32_t (*actuator_config)(void __user *);
+ int32_t (*actuator_i2c_write)(struct msm_actuator_ctrl_t *,
+ int16_t);
+ int32_t (*actuator_write_focus)(struct msm_actuator_ctrl_t *,
+ uint16_t,
+ struct damping_params_t *,
+ int8_t,
+ int16_t);
+};
+
+struct msm_actuator_ctrl_t {
+ uint32_t i2c_addr;
+ struct i2c_driver *i2c_driver;
+ struct msm_camera_i2c_client i2c_client;
+ struct mutex *actuator_mutex;
+ struct msm_actuator_ctrl actuator_ext_ctrl;
+ struct msm_actuator_func_tbl func_tbl;
+ struct debugfs_params_t *debugfs_data;
+ struct v4l2_subdev *sdev;
+ struct v4l2_subdev_ops *act_v4l2_subdev_ops;
+
+ uint8_t debugfs_flag;
+ int16_t initial_code;
+ int16_t curr_step_pos;
+ uint16_t curr_region_index;
+ uint32_t total_steps;
+ uint16_t gross_steps;
+ uint16_t fine_steps;
+ uint16_t *step_position_table;
+ uint16_t *ringing_scenario[2];
+ uint16_t scenario_size[2];
+ struct region_params_t *region_params;
+ uint16_t region_size;
+ struct damping_t *damping[2];
+ void *user_data;
+};
+
+int32_t msm_actuator_i2c_write_b_af(struct msm_actuator_ctrl_t *a_ctrl,
+ uint8_t msb,
+ uint8_t lsb);
+int32_t msm_actuator_config(struct msm_actuator_ctrl_t *a_ctrl,
+ void __user *cfg_data);
+int32_t msm_actuator_move_focus(struct msm_actuator_ctrl_t *a_ctrl,
+ int direction,
+ int32_t num_steps);
+int32_t msm_actuator_init_table(struct msm_actuator_ctrl_t *a_ctrl);
+int32_t msm_actuator_set_default_focus(struct msm_actuator_ctrl_t *a_ctrl);
+int32_t msm_actuator_af_power_down(struct msm_actuator_ctrl_t *a_ctrl);
+int32_t msm_actuator_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id);
+int32_t 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);
+int32_t msm_actuator_create_subdevice(struct msm_actuator_ctrl_t *a_ctrl,
+ struct i2c_board_info const *board_info,
+ struct v4l2_subdev *sdev);
+
+#endif
diff --git a/drivers/media/video/msm/msm.c b/drivers/media/video/msm/msm.c
index da21481..b079d8f 100644
--- a/drivers/media/video/msm/msm.c
+++ b/drivers/media/video/msm/msm.c
@@ -2266,6 +2266,31 @@
}
return 0;
}
+
+static int msm_actuator_probe(struct msm_actuator_info *actuator_info,
+ struct v4l2_subdev *act_sdev,
+ struct msm_actuator_ctrl *actctrl)
+{
+ int rc = 0;
+ struct i2c_adapter *adapter = NULL;
+ void *act_client = NULL;
+ struct msm_actuator_ctrl *a_ext_ctrl = NULL;
+
+ if (!actuator_info)
+ return rc;
+
+ adapter = i2c_get_adapter(actuator_info->bus_id);
+
+ act_client = i2c_new_device(adapter, actuator_info->board_info);
+
+ a_ext_ctrl = (struct msm_actuator_ctrl *)i2c_get_clientdata(act_client);
+
+ *actctrl = *a_ext_ctrl;
+ a_ext_ctrl->a_create_subdevice((void *)actuator_info->board_info,
+ (void *)act_sdev);
+ return rc;
+}
+
static int msm_sync_init(struct msm_sync *sync,
struct platform_device *pdev)
{
@@ -2277,6 +2302,7 @@
sync->pdev = pdev;
sync->opencnt = 0;
+
mutex_init(&sync->lock);
D("%s: initialized %s\n", __func__, sync->sdata->sensor_name);
return rc;
@@ -2295,6 +2321,8 @@
struct msm_cam_v4l2_device *pcam;
struct v4l2_subdev *sdev = NULL;
struct msm_sensor_ctrl *sctrl = NULL;
+ struct v4l2_subdev *act_sdev = NULL;
+ struct msm_actuator_ctrl *actctrl = NULL;
D("%s for %s\n", __func__, pdev->name);
@@ -2327,16 +2355,35 @@
}
rc = sensor_probe(sdata, sdev, sctrl);
-
- msm_camio_probe_off(pdev);
if (rc < 0) {
pr_err("%s: failed to detect %s\n",
__func__,
- sdata->sensor_name);
+ sdata->sensor_name);
+ msm_camio_probe_off(pdev);
+ kzfree(sdev);
kzfree(pcam);
return rc;
}
+ pcam->mctl.act_sdev = kzalloc(sizeof(struct v4l2_subdev),
+ GFP_KERNEL);
+ if (!pcam->mctl.act_sdev) {
+ pr_err("%s: could not allocate mem for actuator v4l2_subdev\n",
+ __func__);
+ msm_camio_probe_off(pdev);
+ kzfree(sdev);
+ kfree(pcam);
+ return -ENOMEM;
+ }
+
+ act_sdev = pcam->mctl.act_sdev;
+ actctrl = &pcam->mctl.sync.actctrl;
+
+ msm_actuator_probe(sdata->actuator_info,
+ act_sdev, actctrl);
+
+ msm_camio_probe_off(pdev);
+
/* setup a manager object*/
rc = msm_sync_init(&pcam->mctl.sync, pdev);
if (rc < 0)
@@ -2391,6 +2438,17 @@
*/
/* register the subdevice, must be done for callbacks */
rc = v4l2_device_register_subdev(&pcam->v4l2_dev, sdev);
+ if (rc < 0) {
+ D("%s sensor sub device register failed\n",
+ __func__);
+ goto failure;
+ }
+ rc = v4l2_device_register_subdev(&pcam->v4l2_dev, act_sdev);
+ if (rc < 0) {
+ D("%s actuator sub device register failed\n",
+ __func__);
+ goto failure;
+ }
pcam->vnode_id = vnode_count++;
return rc;
diff --git a/drivers/media/video/msm/msm.h b/drivers/media/video/msm/msm.h
index f5d66459..c2df360 100644
--- a/drivers/media/video/msm/msm.h
+++ b/drivers/media/video/msm/msm.h
@@ -193,6 +193,7 @@
struct v4l2_subdev *flash_sdev; /* vpe sub device : VPE */
struct msm_cam_config_dev *config_device;
struct v4l2_subdev *ispif_sdev; /* ispif sub device */
+ struct v4l2_subdev *act_sdev; /* actuator sub device */
struct pm_qos_request_list pm_qos_req_list;
struct msm_mctl_pp_info pp_info;
diff --git a/drivers/media/video/msm/msm_logging.h b/drivers/media/video/msm/msm_logging.h
new file mode 100644
index 0000000..5e1547c
--- /dev/null
+++ b/drivers/media/video/msm/msm_logging.h
@@ -0,0 +1,110 @@
+/* Copyright (c) 2011, Code Aurora Forum. 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_LOGGING_H
+#define __MSM_LOGGING_H
+
+#include <linux/time.h>
+
+#ifdef MSM_LOG_LEVEL_ERROR
+#undef MSM_LOG_LEVEL_ERROR
+#endif
+
+#ifdef MSM_LOG_LEVEL_PROFILE
+#undef MSM_LOG_LEVEL_PROFILE
+#endif
+
+#ifdef MSM_LOG_LEVEL_ALL
+#undef MSM_LOG_LEVEL_ALL
+#endif
+
+/* MSM_LOG_LEVEL_ERROR - prints only error logs
+ * MSM_LOG_LEVEL_PROFILE - prints all profile and error logs
+ * MSM_LOG_LEVEL_ALL - prints all logs */
+
+#define MSM_LOG_LEVEL_ERROR
+/* #define MSM_LOG_LEVEL_PROFILE */
+/* #define MSM_LOG_LEVEL_ALL */
+
+#ifdef LINFO
+#undef LINFO
+#endif
+
+#ifdef LERROR
+#undef LERROR
+#endif
+
+#ifdef LPROFSTART
+#undef LPROFSTART
+#endif
+
+#ifdef LPROFEND
+#undef LPROFEND
+#endif
+
+#if defined(MSM_LOG_LEVEL_ERROR)
+#define LDECVAR(VAR)
+#define LINFO(fmt, args...) pr_debug(fmt, ##args)
+#define LERROR(fmt, args...) pr_err(fmt, ##args)
+#define LPROFSTART(START) do {} while (0)
+#define LPROFEND(START, END) do {} while (0)
+#elif defined(MSM_LOG_LEVEL_PROFILE)
+#define LDECVAR(VAR) \
+ static struct timespec VAR
+#define LINFO(fmt, args...) do {} while (0)
+#define LERROR(fmt, args...) pr_err(fmt, ##args)
+#define LPROFSTART(START) \
+ START = current_kernel_time()
+#define LPROFEND(START, END) \
+ do { \
+ END = current_kernel_time(); \
+ pr_info("profile start time in ns: %lu\n", \
+ (START.tv_sec * NSEC_PER_SEC) + \
+ START.tv_nsec); \
+ pr_info("profile end time in ns: %lu\n", \
+ (END.tv_sec * NSEC_PER_SEC) + \
+ END.tv_nsec); \
+ pr_info("profile diff time in ns: %lu\n", \
+ ((END.tv_sec - START.tv_sec) * NSEC_PER_SEC) + \
+ (END.tv_nsec - START.tv_nsec)); \
+ START = END; \
+ } while (0)
+
+#elif defined(MSM_LOG_LEVEL_ALL)
+#define LDECVAR(VAR) \
+ static struct timespec VAR
+#define LINFO(fmt, args...) pr_debug(fmt, ##args)
+#define LERROR(fmt, args...) pr_err(fmt, ##args)
+#define LPROFSTART(START) \
+ START = current_kernel_time()
+#define LPROFEND(START, END) \
+ do { \
+ END = current_kernel_time(); \
+ pr_info("profile start time in ns: %lu\n", \
+ (START.tv_sec * NSEC_PER_SEC) + \
+ START.tv_nsec); \
+ pr_info("profile end time in ns: %lu\n", \
+ (END.tv_sec * NSEC_PER_SEC) + \
+ END.tv_nsec); \
+ pr_info("profile diff time in ns: %lu\n", \
+ ((END.tv_sec - START.tv_sec) * NSEC_PER_SEC) + \
+ (END.tv_nsec - START.tv_nsec)); \
+ START = END; \
+ } while (0)
+#else
+#define LDECVAR(VAR)
+#define LINFO(fmt, args...) do {} while (0)
+#define LERROR(fmt, args...) do {} while (0)
+#define LPROFSTART(START) do {} while (0)
+#define LPROFEND(START, END) do {} while (0)
+#endif
+#endif
diff --git a/drivers/media/video/msm/msm_mctl.c b/drivers/media/video/msm/msm_mctl.c
index 344ea73..c9132cb 100644
--- a/drivers/media/video/msm/msm_mctl.c
+++ b/drivers/media/video/msm/msm_mctl.c
@@ -301,6 +301,10 @@
rc = p_mctl->sync.sctrl.s_config(argp);
break;
+ case MSM_CAM_IOCTL_ACTUATOR_IO_CFG:
+ rc = p_mctl->sync.actctrl.a_config(argp);
+ break;
+
case MSM_CAM_IOCTL_FLASH_CTRL: {
struct flash_ctrl_data flash_info;
if (copy_from_user(&flash_info, argp, sizeof(flash_info))) {
@@ -409,6 +413,9 @@
if (p_mctl->isp_sdev && p_mctl->isp_sdev->isp_release)
p_mctl->isp_sdev->isp_release(&p_mctl->sync);
+ if (p_mctl->sync.actctrl.a_power_down)
+ p_mctl->sync.actctrl.a_power_down();
+
if (p_mctl->sync.sctrl.s_release)
p_mctl->sync.sctrl.s_release();
diff --git a/include/media/msm_camera.h b/include/media/msm_camera.h
index 9984aa4..3a8c026 100644
--- a/include/media/msm_camera.h
+++ b/include/media/msm_camera.h
@@ -141,6 +141,9 @@
#define MSM_CAM_IOCTL_SET_MEM_MAP_INFO \
_IOR(MSM_CAM_IOCTL_MAGIC, 42, struct msm_mem_map_info *)
+#define MSM_CAM_IOCTL_ACTUATOR_IO_CFG \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 43, struct msm_actuator_cfg_data *)
+
#define MSM_CAMERA_LED_OFF 0
#define MSM_CAMERA_LED_LOW 1
#define MSM_CAMERA_LED_HIGH 2
@@ -637,7 +640,8 @@
#define CFG_GET_CALIB_DATA 31
#define CFG_GET_OUTPUT_INFO 32
#define CFG_GET_EEPROM_DATA 33
-#define CFG_MAX 34
+#define CFG_SET_ACTUATOR_INFO 34
+#define CFG_MAX 35
#define MOVE_NEAR 0
@@ -802,6 +806,25 @@
} cfg;
};
+struct msm_actuator_move_params_t {
+ int8_t dir;
+ int32_t num_steps;
+};
+
+struct msm_actuator_set_info_t {
+ uint32_t total_steps;
+ uint16_t gross_steps;
+ uint16_t fine_steps;
+};
+
+struct msm_actuator_cfg_data {
+ int cfgtype;
+ union {
+ struct msm_actuator_move_params_t move;
+ struct msm_actuator_set_info_t info;
+ } cfg;
+};
+
struct sensor_large_data {
int cfgtype;
union {