diff options
Diffstat (limited to 'drivers/staging/media/atomisp/i2c/imx/ad5816g.c')
-rw-r--r-- | drivers/staging/media/atomisp/i2c/imx/ad5816g.c | 225 |
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; + +} + + |