238 lines
5.4 KiB
C
238 lines
5.4 KiB
C
#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>
|
|
#ifndef CONFIG_GMIN_INTEL_MID /* FIXME! for non-gmin*/
|
|
#include <media/v4l2-chip-ident.h>
|
|
#endif
|
|
#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);
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
int dw9714_vcm_init(struct v4l2_subdev *sd)
|
|
{
|
|
|
|
/* set VCM to home position and vcm mode to direct*/
|
|
dw9714_dev.vcm_mode = DW9714_DIRECT;
|
|
dw9714_dev.vcm_settings.update = false;
|
|
dw9714_dev.platform_data = camera_get_af_platform_data();
|
|
return (NULL == dw9714_dev.platform_data) ? -ENODEV : 0;
|
|
|
|
}
|
|
|