summaryrefslogtreecommitdiffstats
path: root/drivers/staging/media/atomisp/i2c/imx/ad5816g.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/media/atomisp/i2c/imx/ad5816g.c')
-rw-r--r--drivers/staging/media/atomisp/i2c/imx/ad5816g.c225
1 files changed, 225 insertions, 0 deletions
diff --git a/drivers/staging/media/atomisp/i2c/imx/ad5816g.c b/drivers/staging/media/atomisp/i2c/imx/ad5816g.c
new file mode 100644
index 0000000..d68ebb4
--- /dev/null
+++ b/drivers/staging/media/atomisp/i2c/imx/ad5816g.c
@@ -0,0 +1,225 @@
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/kmod.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <media/v4l2-device.h>
+
+#include "ad5816g.h"
+
+struct ad5816g_device ad5816g_dev;
+
+static int ad5816g_i2c_rd8(struct i2c_client *client, u8 reg, u8 *val)
+{
+ struct i2c_msg msg[2];
+ u8 buf[2];
+ buf[0] = reg;
+ buf[1] = 0;
+
+ msg[0].addr = AD5816G_VCM_ADDR;
+ msg[0].flags = 0;
+ msg[0].len = 1;
+ msg[0].buf = &buf[0];
+
+ msg[1].addr = AD5816G_VCM_ADDR;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = 1;
+ msg[1].buf = &buf[1];
+ *val = 0;
+ if (i2c_transfer(client->adapter, msg, 2) != 2)
+ return -EIO;
+ *val = buf[1];
+ return 0;
+}
+
+static int ad5816g_i2c_wr8(struct i2c_client *client, u8 reg, u8 val)
+{
+ struct i2c_msg msg;
+ u8 buf[2];
+ buf[0] = reg;
+ buf[1] = val;
+ msg.addr = AD5816G_VCM_ADDR;
+ msg.flags = 0;
+ msg.len = 2;
+ msg.buf = &buf[0];
+ if (i2c_transfer(client->adapter, &msg, 1) != 1)
+ return -EIO;
+ return 0;
+}
+
+static int ad5816g_i2c_wr16(struct i2c_client *client, u8 reg, u16 val)
+{
+ struct i2c_msg msg;
+ u8 buf[3];
+ buf[0] = reg;
+ buf[1] = (u8)(val >> 8);
+ buf[2] = (u8)(val & 0xff);
+ msg.addr = AD5816G_VCM_ADDR;
+ msg.flags = 0;
+ msg.len = 3;
+ msg.buf = &buf[0];
+ if (i2c_transfer(client->adapter, &msg, 1) != 1)
+ return -EIO;
+ return 0;
+}
+
+static int ad5816g_set_arc_mode(struct i2c_client *client)
+{
+ int ret;
+
+ ret = ad5816g_i2c_wr8(client, AD5816G_CONTROL, AD5816G_ARC_EN);
+ if (ret)
+ return ret;
+
+ ret = ad5816g_i2c_wr8(client, AD5816G_MODE,
+ AD5816G_MODE_2_5M_SWITCH_CLOCK);
+ if (ret)
+ return ret;
+
+ ret = ad5816g_i2c_wr8(client, AD5816G_VCM_FREQ, AD5816G_DEF_FREQ);
+ return ret;
+}
+
+int ad5816g_vcm_power_up(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+ u8 ad5816g_id;
+
+ /* Enable power */
+ ret = ad5816g_dev.platform_data->power_ctrl(sd, 1);
+ if (ret)
+ return ret;
+ /* waiting time AD5816G(vcm) - t1 + t2
+ * t1(1ms) -Time from VDD high to first i2c cmd
+ * t2(100us) - exit power-down mode time
+ */
+ usleep_range(1100, 2200);
+ /* Detect device */
+ ret = ad5816g_i2c_rd8(client, AD5816G_IC_INFO, &ad5816g_id);
+ if (ret < 0)
+ goto fail_powerdown;
+ if (ad5816g_id != AD5816G_ID) {
+ ret = -ENXIO;
+ goto fail_powerdown;
+ }
+ ret = ad5816g_set_arc_mode(client);
+ if (ret)
+ return ret;
+
+ /* set the VCM_THRESHOLD */
+ ret = ad5816g_i2c_wr8(client, AD5816G_VCM_THRESHOLD,
+ AD5816G_DEF_THRESHOLD);
+
+ return ret;
+
+fail_powerdown:
+ ad5816g_dev.platform_data->power_ctrl(sd, 0);
+ return ret;
+}
+
+int ad5816g_vcm_power_down(struct v4l2_subdev *sd)
+{
+ return ad5816g_dev.platform_data->power_ctrl(sd, 0);
+}
+
+
+int ad5816g_t_focus_vcm(struct v4l2_subdev *sd, u16 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u16 data = val & VCM_CODE_MASK;
+
+ return ad5816g_i2c_wr16(client, AD5816G_VCM_CODE_MSB, data);
+}
+
+int ad5816g_t_focus_abs(struct v4l2_subdev *sd, s32 value)
+{
+ int ret;
+
+ value = clamp(value, 0, AD5816G_MAX_FOCUS_POS);
+ ret = ad5816g_t_focus_vcm(sd, value);
+ if (ret == 0) {
+ ad5816g_dev.number_of_steps = value - ad5816g_dev.focus;
+ ad5816g_dev.focus = value;
+ getnstimeofday(&(ad5816g_dev.timestamp_t_focus_abs));
+ }
+
+ return ret;
+}
+
+int ad5816g_t_focus_rel(struct v4l2_subdev *sd, s32 value)
+{
+
+ return ad5816g_t_focus_abs(sd, ad5816g_dev.focus + value);
+}
+
+int ad5816g_q_focus_status(struct v4l2_subdev *sd, s32 *value)
+{
+ u32 status = 0;
+ struct timespec temptime;
+ const struct timespec timedelay = {
+ 0,
+ min_t(u32, abs(ad5816g_dev.number_of_steps) * DELAY_PER_STEP_NS,
+ DELAY_MAX_PER_STEP_NS),
+ };
+
+ ktime_get_ts(&temptime);
+
+ temptime = timespec_sub(temptime, (ad5816g_dev.timestamp_t_focus_abs));
+
+ if (timespec_compare(&temptime, &timedelay) <= 0) {
+ status |= ATOMISP_FOCUS_STATUS_MOVING;
+ status |= ATOMISP_FOCUS_HP_IN_PROGRESS;
+ } else {
+ status |= ATOMISP_FOCUS_STATUS_ACCEPTS_NEW_MOVE;
+ status |= ATOMISP_FOCUS_HP_COMPLETE;
+ }
+ *value = status;
+
+ return 0;
+}
+
+int ad5816g_q_focus_abs(struct v4l2_subdev *sd, s32 *value)
+{
+ s32 val;
+
+ ad5816g_q_focus_status(sd, &val);
+
+ if (val & ATOMISP_FOCUS_STATUS_MOVING)
+ *value = ad5816g_dev.focus - ad5816g_dev.number_of_steps;
+ else
+ *value = ad5816g_dev.focus;
+
+ return 0;
+}
+
+int ad5816g_t_vcm_slew(struct v4l2_subdev *sd, s32 value)
+{
+ return 0;
+}
+
+int ad5816g_t_vcm_timing(struct v4l2_subdev *sd, s32 value)
+{
+ return 0;
+}
+
+int ad5816g_vcm_init(struct v4l2_subdev *sd)
+{
+ ad5816g_dev.platform_data = camera_get_af_platform_data();
+ return (NULL == ad5816g_dev.platform_data) ? -ENODEV : 0;
+
+}
+
+
OpenPOWER on IntegriCloud