blob: f968554543130bbd12831e965aa45d5db9df267c [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
#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 <asm/intel-mid.h>
#include "dw9714.h"
static struct dw9714_device dw9714_dev;
static int dw9714_i2c_write(struct i2c_client *client, u16 data)
{
struct i2c_msg msg;
const int num_msg = 1;
int ret;
u16 val;
val = cpu_to_be16(data);
msg.addr = DW9714_VCM_ADDR;
msg.flags = 0;
msg.len = DW9714_16BIT;
msg.buf = (u8 *)&val;
ret = i2c_transfer(client->adapter, &msg, 1);
return ret == num_msg ? 0 : -EIO;
}
int dw9714_vcm_power_up(struct v4l2_subdev *sd)
{
int ret;
/* Enable power */
ret = dw9714_dev.platform_data->power_ctrl(sd, 1);
/* waiting time requested by DW9714A(vcm) */
usleep_range(12000, 12500);
return ret;
}
int dw9714_vcm_power_down(struct v4l2_subdev *sd)
{
return dw9714_dev.platform_data->power_ctrl(sd, 0);
}
static int dw9714_t_focus_vcm(struct v4l2_subdev *sd, u16 val)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
int ret = -EINVAL;
u8 mclk = vcm_step_mclk(dw9714_dev.vcm_settings.step_setting);
u8 s = vcm_step_s(dw9714_dev.vcm_settings.step_setting);
/*
* For different mode, VCM_PROTECTION_OFF/ON required by the
* control procedure. For DW9714_DIRECT/DLC mode, slew value is
* VCM_DEFAULT_S(0).
*/
switch (dw9714_dev.vcm_mode) {
case DW9714_DIRECT:
if (dw9714_dev.vcm_settings.update) {
ret = dw9714_i2c_write(client, VCM_PROTECTION_OFF);
if (ret)
return ret;
ret = dw9714_i2c_write(client, DIRECT_VCM);
if (ret)
return ret;
ret = dw9714_i2c_write(client, VCM_PROTECTION_ON);
if (ret)
return ret;
dw9714_dev.vcm_settings.update = false;
}
ret = dw9714_i2c_write(client,
vcm_val(val, VCM_DEFAULT_S));
break;
case DW9714_LSC:
if (dw9714_dev.vcm_settings.update) {
ret = dw9714_i2c_write(client, VCM_PROTECTION_OFF);
if (ret)
return ret;
ret = dw9714_i2c_write(client,
vcm_dlc_mclk(DLC_DISABLE, mclk));
if (ret)
return ret;
ret = dw9714_i2c_write(client,
vcm_tsrc(dw9714_dev.vcm_settings.t_src));
if (ret)
return ret;
ret = dw9714_i2c_write(client, VCM_PROTECTION_ON);
if (ret)
return ret;
dw9714_dev.vcm_settings.update = false;
}
ret = dw9714_i2c_write(client, vcm_val(val, s));
break;
case DW9714_DLC:
if (dw9714_dev.vcm_settings.update) {
ret = dw9714_i2c_write(client, VCM_PROTECTION_OFF);
if (ret)
return ret;
ret = dw9714_i2c_write(client,
vcm_dlc_mclk(DLC_ENABLE, mclk));
if (ret)
return ret;
ret = dw9714_i2c_write(client,
vcm_tsrc(dw9714_dev.vcm_settings.t_src));
if (ret)
return ret;
ret = dw9714_i2c_write(client, VCM_PROTECTION_ON);
if (ret)
return ret;
dw9714_dev.vcm_settings.update = false;
}
ret = dw9714_i2c_write(client,
vcm_val(val, VCM_DEFAULT_S));
break;
}
return ret;
}
int dw9714_t_focus_abs(struct v4l2_subdev *sd, s32 value)
{
int ret;
value = clamp(value, 0, DW9714_MAX_FOCUS_POS);
ret = dw9714_t_focus_vcm(sd, value);
if (ret == 0) {
dw9714_dev.number_of_steps = value - dw9714_dev.focus;
dw9714_dev.focus = value;
getnstimeofday(&(dw9714_dev.timestamp_t_focus_abs));
}
return ret;
}
int dw9714_t_focus_abs_init(struct v4l2_subdev *sd)
{
int ret;
ret = dw9714_t_focus_vcm(sd, DW9714_DEFAULT_FOCUS_POS);
if (ret == 0) {
dw9714_dev.number_of_steps =
DW9714_DEFAULT_FOCUS_POS - dw9714_dev.focus;
dw9714_dev.focus = DW9714_DEFAULT_FOCUS_POS;
getnstimeofday(&(dw9714_dev.timestamp_t_focus_abs));
}
return ret;
}
int dw9714_t_focus_rel(struct v4l2_subdev *sd, s32 value)
{
return dw9714_t_focus_abs(sd, dw9714_dev.focus + value);
}
int dw9714_q_focus_status(struct v4l2_subdev *sd, s32 *value)
{
u32 status = 0;
struct timespec temptime;
const struct timespec timedelay = {
0,
min_t(u32, abs(dw9714_dev.number_of_steps)*DELAY_PER_STEP_NS,
DELAY_MAX_PER_STEP_NS),
};
ktime_get_ts(&temptime);
temptime = timespec_sub(temptime, (dw9714_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 dw9714_q_focus_abs(struct v4l2_subdev *sd, s32 *value)
{
s32 val;
dw9714_q_focus_status(sd, &val);
if (val & ATOMISP_FOCUS_STATUS_MOVING)
*value = dw9714_dev.focus - dw9714_dev.number_of_steps;
else
*value = dw9714_dev.focus;
return 0;
}
int dw9714_t_vcm_slew(struct v4l2_subdev *sd, s32 value)
{
dw9714_dev.vcm_settings.step_setting = value;
dw9714_dev.vcm_settings.update = true;
return 0;
}
int dw9714_t_vcm_timing(struct v4l2_subdev *sd, s32 value)
{
dw9714_dev.vcm_settings.t_src = value;
dw9714_dev.vcm_settings.update = true;
return 0;
}