From 20cffb3e0c70b7cbae5e57c428a36bc32b28d2e2 Mon Sep 17 00:00:00 2001 From: Tuukka Toivonen Date: Fri, 9 Dec 2011 17:43:38 +0200 Subject: [PATCH] ov8830: add driver skeleton and platform data BZ: 16972 A driver is created by copying it from mt9e013 driver. In this patch it is not yet functional, only the labels have been renamed. Change-Id: I8de8f32fd795eefd19dcb15a2beb21ab1ee64e7f Signed-off-by: Tuukka Toivonen Reviewed-on: http://android.intel.com:8080/32612 Reviewed-by: Koski, Anttu Tested-by: Koski, Anttu Reviewed-by: buildbot Tested-by: buildbot --- arch/x86/platform/mrst/mrst.c | 91 +- drivers/media/video/Kconfig | 9 + drivers/media/video/Makefile | 1 + drivers/media/video/atomisp/Kconfig | 1 + drivers/media/video/ov8830.c | 1963 +++++++++++++++++++++++++++++++++++ drivers/media/video/ov8830.h | 1066 +++++++++++++++++++ 6 files changed, 3128 insertions(+), 3 deletions(-) create mode 100644 drivers/media/video/ov8830.c create mode 100644 drivers/media/video/ov8830.h diff --git a/arch/x86/platform/mrst/mrst.c b/arch/x86/platform/mrst/mrst.c index 3983ea8..36a792b 100644 --- a/arch/x86/platform/mrst/mrst.c +++ b/arch/x86/platform/mrst/mrst.c @@ -1294,6 +1294,77 @@ void *mt9e013_platform_data_init(void *info) } /* + * CLV PR0 primary camera sensor - OV8830 platform data + */ + +static int ov8830_gpio_ctrl(struct v4l2_subdev *sd, int flag) +{ + int ret; + + if (gp_camera0_reset < 0) { + ret = camera_sensor_gpio(-1, GP_CAMERA_0_RESET, + GPIOF_DIR_OUT, 1); + if (ret < 0) + return ret; + gp_camera0_reset = ret; + } + + if (flag) { + gpio_set_value(gp_camera0_reset, 0); + msleep(20); + gpio_set_value(gp_camera0_reset, 1); + } else { + gpio_set_value(gp_camera0_reset, 0); + } + + return 0; +} + +static int ov8830_flisclk_ctrl(struct v4l2_subdev *sd, int flag) +{ + return intel_scu_ipc_osc_clk(OSC_CLK_CAM0, flag); +} + +static int ov8830_power_ctrl(struct v4l2_subdev *sd, int flag) +{ + if (flag) { + if (!camera_vprog1_on) { + camera_vprog1_on = 1; + intel_scu_ipc_msic_vprog1(1); + } + } else { + if (camera_vprog1_on) { + camera_vprog1_on = 0; + intel_scu_ipc_msic_vprog1(0); + } + } + + return 0; +} + +static int ov8830_csi_configure(struct v4l2_subdev *sd, int flag) +{ + static const int LANES = 4; + return camera_sensor_csi(sd, ATOMISP_CAMERA_PORT_PRIMARY, LANES, + ATOMISP_INPUT_FORMAT_RAW_10, atomisp_bayer_order_grbg, flag); +} + +static struct camera_sensor_platform_data ov8830_sensor_platform_data = { + .gpio_ctrl = ov8830_gpio_ctrl, + .flisclk_ctrl = ov8830_flisclk_ctrl, + .power_ctrl = ov8830_power_ctrl, + .csi_cfg = ov8830_csi_configure, +}; + +void *ov8830_platform_data_init(void *info) +{ + gp_camera0_reset = -1; + gp_camera0_power_down = -1; + + return &ov8830_sensor_platform_data; +} + +/* * MFLD PR2 secondary camera sensor - MT9M114 platform data */ static int mt9m114_gpio_ctrl(struct v4l2_subdev *sd, int flag) @@ -1678,12 +1749,20 @@ static const struct devs_id __initconst device_ids[] = { */ {"lm3554", SFI_DEV_TYPE_I2C, 0, &no_platform_data}, {"mt9e013", SFI_DEV_TYPE_I2C, 0, &mt9e013_platform_data_init}, + {"ov8830", SFI_DEV_TYPE_I2C, 0, &ov8830_platform_data_init}, {"mt9m114", SFI_DEV_TYPE_I2C, 0, &mt9m114_platform_data_init}, {"mxt224", SFI_DEV_TYPE_I2C, 0, &atmel_mxt224_platform_data_init}, {"synaptics_3202", SFI_DEV_TYPE_I2C, 0, &s3202_platform_data_init}, {}, }; +static const struct intel_v4l2_subdev_id v4l2_ids_clv[] = { + {"ov8830", RAW_CAMERA, ATOMISP_CAMERA_PORT_PRIMARY}, + {"mt9m114", SOC_CAMERA, ATOMISP_CAMERA_PORT_SECONDARY}, + {"lm3554", LED_FLASH, -1}, + {}, +}; + static const struct intel_v4l2_subdev_id v4l2_ids_mfld[] = { {"mt9e013", RAW_CAMERA, ATOMISP_CAMERA_PORT_PRIMARY}, {"mt9m114", SOC_CAMERA, ATOMISP_CAMERA_PORT_SECONDARY}, @@ -1693,9 +1772,15 @@ static const struct intel_v4l2_subdev_id v4l2_ids_mfld[] = { static const struct intel_v4l2_subdev_id *get_v4l2_ids(int *n_subdev) { - if (n_subdev) - *n_subdev = ARRAY_SIZE(v4l2_ids_mfld); - return v4l2_ids_mfld; + if (mrst_identify_cpu() == MRST_CPU_CHIP_CLOVERVIEW) { + if (n_subdev) + *n_subdev = ARRAY_SIZE(v4l2_ids_clv); + return v4l2_ids_clv; + } else { + if (n_subdev) + *n_subdev = ARRAY_SIZE(v4l2_ids_mfld); + return v4l2_ids_mfld; + } } static struct atomisp_platform_data *v4l2_subdev_table_head; diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index bf54190..878d233 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -332,6 +332,15 @@ config VIDEO_MT9E013 It currently only works with the atomisp driver. +config VIDEO_OV8830 + tristate "Omnivision ov8830 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_ATOMISP + ---help--- + This is a Video4Linux2 sensor-level driver for the Omnivision + ov8830 8MP RAW sensor. + + It currently only works with the atomisp driver. + config VIDEO_MT9M114 tristate "Aptina mt9m114 sensor support" depends on I2C && VIDEO_V4L2 diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 7bbbb6a..7be7031 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -76,6 +76,7 @@ obj-$(CONFIG_VIDEO_LM3554) += lm3554.o obj-$(CONFIG_VIDEO_MT9E013) += mt9e013.o CFLAGS_mt9e013.o = -Werror +obj-$(CONFIG_VIDEO_OV8830) += ov8830.o obj-$(CONFIG_VIDEO_MT9M114) += mt9m114.o CFFLAGS_mt9m114.o = -Werror diff --git a/drivers/media/video/atomisp/Kconfig b/drivers/media/video/atomisp/Kconfig index 0af43bf..a98d2a6 100644 --- a/drivers/media/video/atomisp/Kconfig +++ b/drivers/media/video/atomisp/Kconfig @@ -8,6 +8,7 @@ config VIDEO_ATOMISP select VIDEO_LM3554 select VIDEO_MT9E013 select VIDEO_MT9M114 + select VIDEO_OV8830 default m diff --git a/drivers/media/video/ov8830.c b/drivers/media/video/ov8830.c new file mode 100644 index 0000000..6ba600a --- /dev/null +++ b/drivers/media/video/ov8830.c @@ -0,0 +1,1963 @@ +/* + * Support for OmniVision ov8830 1080p HD camera sensor. + * + * Copyright (c) 2011 Intel Corporation. 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 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ov8830.h" + +#define to_ov8830_sensor(sd) container_of(sd, struct ov8830_device, sd) + +#define HOME_POS 255 + + +/* divides a by b using half up rounding and div/0 prevention + * (result is 0 if b == 0) */ +#define divsave_rounded(a, b) (((b) != 0) ? (((a)+((b)>>1))/(b)) : (-1)) + +typedef unsigned int sensor_register; +struct sensor_mode_data { + sensor_register coarse_integration_time_min; + sensor_register coarse_integration_time_max_margin; + sensor_register fine_integration_time_min; + sensor_register fine_integration_time_max_margin; + sensor_register fine_integration_time_def; + sensor_register frame_length_lines; + sensor_register line_length_pck; + sensor_register read_mode; + int vt_pix_clk_freq_mhz; +}; + +/* + * TODO: use debug parameter to actually define when debug messages should + * be printed. + */ +static int debug; +static u16 real_model_id; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Enable debug messages"); + +struct ov8830_resolution ov8830_res_preview[] = { + { + .desc = "PREVIEW_30fps" , + .width = 820 , + .height = 616 , + .fps = 30 , + .used = 0 , + .pixels_per_line = 0x20F0, /* consistent with regs arrays */ + .lines_per_frame = 0x02F7, /* consistent with regs arrays */ + .regs = ov8830_PREVIEW_30fps , + }, + { + .desc = "WIDE_PREVIEW_30fps" , + .width = 1640 , + .height = 956 , + .fps = 30 , + .used = 0 , + .pixels_per_line = 0x16C2, /* consistent with regs arrays */ + .lines_per_frame = 0x044F, /* consistent with regs arrays */ + .regs = ov8830_WIDE_PREVIEW_30fps , + }, + +}; + +#define N_RES_PREVIEW (ARRAY_SIZE(ov8830_res_preview)) + +struct ov8830_resolution ov8830_res_still[] = { + { + .desc = "STILL_2M_15fps" , + .width = 1640 , + .height = 1232 , + .fps = 15 , + .used = 0 , + .pixels_per_line = 0x2460, /* consistent with regs arrays */ + .lines_per_frame = 0x0563, /* consistent with regs arrays */ + .regs = ov8830_STILL_2M_15fps , + }, + { + .desc = "STILL_6M_15fps" , + .width = 3280 , + .height = 1848 , + .fps = 15 , + .used = 0 , + .pixels_per_line = 0x191C, /* consistent with regs arrays */ + .lines_per_frame = 0x07C7, /* consistent with regs arrays */ + .regs = ov8830_STILL_6M_15fps , + }, + { + .desc = "STILL_8M_12fps" , + .width = 3280 , + .height = 2464 , + .fps = 12 , + .used = 0 , + .pixels_per_line = 0x17F8, /* consistent with regs arrays */ + .lines_per_frame = 0x0A2F, /* consistent with regs arrays */ + .regs = ov8830_STILL_8M_12fps , + }, +}; + +#define N_RES_STILL (ARRAY_SIZE(ov8830_res_still)) + +struct ov8830_resolution ov8830_res_video[] = { + { + .desc = "QCIF_strong_dvs_30fps" , + .width = 384 , + .height = 292 , + .fps = 30 , + .used = 0 , + .pixels_per_line = 0x3978, /* consistent with regs arrays */ + .lines_per_frame = 0x01B3, /* consistent with regs arrays */ + .regs = ov8830_QCIF_strong_dvs_30fps , + }, + { + .desc = "QVGA_strong_dvs_30fps" , + .width = 384 , + .height = 288 , + .fps = 30 , + .used = 0 , + .pixels_per_line = 0x3A00, /* consistent with regs arrays */ + .lines_per_frame = 0x01AF, /* consistent with regs arrays */ + .regs = ov8830_QVGA_strong_dvs_30fps , + }, + { + .desc = "VGA_strong_dvs_30fps" , + .width = 820 , + .height = 616 , + .fps = 30 , + .used = 0 , + .pixels_per_line = 0x20F0, /* consistent with regs arrays */ + .lines_per_frame = 0x02F7, /* consistent with regs arrays */ + .regs = ov8830_VGA_strong_dvs_30fps , + }, + { + .desc = "WVGA_strong_dvs_30fps" , + .width = 1640 , + .height = 1024 , + .fps = 30 , + .used = 0 , + .pixels_per_line = 0x156C, /* consistent with regs arrays */ + .lines_per_frame = 0x048F, /* consistent with regs arrays */ + .regs = ov8830_WVGA_strong_dvs_30fps , + }, + { + .desc = "720p_strong_dvs_30fps" , + .width = 1568 , + .height = 876 , + .fps = 30 , + .used = 0 , + .pixels_per_line = 0x188C, /* consistent with regs arrays */ + .lines_per_frame = 0x03FF, /* consistent with regs arrays */ + .regs = ov8830_720p_strong_dvs_30fps , + }, + { + .desc = "1080p_strong_dvs_30fps", + .width = 2336, + .height = 1308, + .fps = 30, + .used = 0, + .pixels_per_line = 0x113A, /* consistent with regs arrays */ + .lines_per_frame = 0x05AB, /* consistent with regs arrays */ + .regs = ov8830_1080p_strong_dvs_30fps, + }, +}; + +#define N_RES_VIDEO (ARRAY_SIZE(ov8830_res_video)) + +struct ov8830_resolution *ov8830_res = ov8830_res_preview; +static int N_RES = N_RES_PREVIEW; + +static int +ov8830_read_reg(struct i2c_client *client, u16 len, u16 reg, u16 *val) +{ + struct i2c_msg msg[2]; + u16 data[OV8830_SHORT_MAX]; + int err, i; + + if (!client->adapter) { + v4l2_err(client, "%s error, no client->adapter\n", __func__); + return -ENODEV; + } + + /* @len should be even when > 1 */ + if (len > OV8830_BYTE_MAX) { + v4l2_err(client, "%s error, invalid data length\n", __func__); + return -EINVAL; + } + + memset(msg, 0 , sizeof(msg)); + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = I2C_MSG_LENGTH; + msg[0].buf = (u8 *)data; + /* high byte goes first */ + data[0] = cpu_to_be16(reg); + + msg[1].addr = client->addr; + msg[1].len = len; + msg[1].flags = I2C_M_RD; + msg[1].buf = (u8 *)data; + + err = i2c_transfer(client->adapter, msg, 2); + if (err < 0) + goto error; + + /* high byte comes first */ + if (len == OV8830_8BIT) { + *val = (u8)data[0]; + } else { + /* 16-bit access is default when len > 1 */ + for (i = 0; i < (len >> 1); i++) + val[i] = be16_to_cpu(data[i]); + } + + return 0; + +error: + dev_err(&client->dev, "read from offset 0x%x error %d", reg, err); + return err; +} + +static int ov8830_i2c_write(struct i2c_client *client, u16 len, u8 *data) +{ + struct i2c_msg msg; + const int num_msg = 1; + int ret; + int retry = 0; + +again: + msg.addr = client->addr; + msg.flags = 0; + msg.len = len; + msg.buf = data; + + ret = i2c_transfer(client->adapter, &msg, 1); + + /* + * It is said that Rev 2 sensor needs some delay here otherwise + * registers do not seem to load correctly. But tests show that + * removing the delay would not cause any in-stablility issue and the + * delay will cause serious performance down, so, removed previous + * mdelay(1) here. + */ + + if (ret == num_msg) + return 0; + + if (retry <= I2C_RETRY_COUNT) { + dev_err(&client->dev, "retrying i2c write transfer... %d", + retry); + retry++; + msleep(20); + goto again; + } + + return ret; +} + +static int +ov8830_write_reg(struct i2c_client *client, u16 data_length, u16 reg, u16 val) +{ + int ret; + unsigned char data[4] = {0}; + u16 *wreg; + const u16 len = data_length + sizeof(u16); /* 16-bit address + data */ + + if (!client->adapter) { + v4l2_err(client, "%s error, no client->adapter\n", __func__); + return -ENODEV; + } + + if (data_length != OV8830_8BIT && data_length != OV8830_16BIT) { + v4l2_err(client, "%s error, invalid data_length\n", __func__); + return -EINVAL; + } + + /* high byte goes out first */ + wreg = (u16 *)data; + *wreg = cpu_to_be16(reg); + + if (data_length == OV8830_8BIT) { + data[2] = (u8)(val); + } else { + /* OV8830_16BIT */ + u16 *wdata = (u16 *)&data[2]; + *wdata = be16_to_cpu(val); + } + + ret = ov8830_i2c_write(client, len, data); + if (ret) + dev_err(&client->dev, + "write error: wrote 0x%x to offset 0x%x error %d", + val, reg, ret); + + return ret; +} + + +/** + * ov8830_rmw_reg - Read/Modify/Write a value to a register in the sensor + * device + * @client: i2c driver client structure + * @data_length: 8/16-bits length + * @reg: register address + * @mask: masked out bits + * @set: bits set + * + * Read/modify/write a value to a register in the sensor device. + * Returns zero if successful, or non-zero otherwise. + */ +static int ov8830_rmw_reg(struct i2c_client *client, u16 data_length, u16 reg, + u16 mask, u16 set) +{ + int err; + u16 val; + + /* Exit when no mask */ + if (mask == 0) + return 0; + + /* @mask must not exceed data length */ + if (data_length == OV8830_8BIT && mask & ~0xff) + return -EINVAL; + + err = ov8830_read_reg(client, data_length, reg, &val); + if (err) { + v4l2_err(client, "ov8830_rmw_reg error exit, read failed\n"); + return -EINVAL; + } + + val &= ~mask; + + /* + * Perform the OR function if the @set exists. + * Shift @set value to target bit location. @set should set only + * bits included in @mask. + * + * REVISIT: This function expects @set to be non-shifted. Its shift + * value is then defined to be equal to mask's LSB position. + * How about to inform values in their right offset position and avoid + * this unneeded shift operation? + */ + set <<= ffs(mask) - 1; + val |= set & mask; + + err = ov8830_write_reg(client, data_length, reg, val); + if (err) { + v4l2_err(client, "ov8830_rmw_reg error exit, write failed\n"); + return -EINVAL; + } + + return 0; +} + + +/* + * ov8830_write_reg_array - Initializes a list of MT9M114 registers + * @client: i2c driver client structure + * @reglist: list of registers to be written + * + * This function initializes a list of registers. When consecutive addresses + * are found in a row on the list, this function creates a buffer and sends + * consecutive data in a single i2c_transfer(). + * + * __ov8830_flush_reg_array, __ov8830_buf_reg_array() and + * __ov8830_write_reg_is_consecutive() are internal functions to + * ov8830_write_reg_array_fast() and should be not used anywhere else. + * + */ + +static int __ov8830_flush_reg_array(struct i2c_client *client, + struct ov8830_write_ctrl *ctrl) +{ + u16 size; + + if (ctrl->index == 0) + return 0; + + size = sizeof(u16) + ctrl->index; /* 16-bit address + data */ + ctrl->buffer.addr = cpu_to_be16(ctrl->buffer.addr); + ctrl->index = 0; + + return ov8830_i2c_write(client, size, (u8 *)&ctrl->buffer); +} + +static int __ov8830_buf_reg_array(struct i2c_client *client, + struct ov8830_write_ctrl *ctrl, + const struct ov8830_reg *next) +{ + int size; + u16 *data16; + + switch (next->type) { + case OV8830_8BIT: + size = 1; + ctrl->buffer.data[ctrl->index] = (u8)next->val; + break; + case OV8830_16BIT: + size = 2; + data16 = (u16 *)&ctrl->buffer.data[ctrl->index]; + *data16 = cpu_to_be16((u16)next->val); + break; + default: + return -EINVAL; + } + + /* When first item is added, we need to store its starting address */ + if (ctrl->index == 0) + ctrl->buffer.addr = next->reg.sreg; + + ctrl->index += size; + + /* + * Buffer cannot guarantee free space for u32? Better flush it to avoid + * possible lack of memory for next item. + */ + if (ctrl->index + sizeof(u16) >= OV8830_MAX_WRITE_BUF_SIZE) + __ov8830_flush_reg_array(client, ctrl); + + return 0; +} + +static int +__ov8830_write_reg_is_consecutive(struct i2c_client *client, + struct ov8830_write_ctrl *ctrl, + const struct ov8830_reg *next) +{ + if (ctrl->index == 0) + return 1; + + return ctrl->buffer.addr + ctrl->index == next->reg.sreg; +} + +static int ov8830_write_reg_array(struct i2c_client *client, + const struct ov8830_reg *reglist) +{ + const struct ov8830_reg *next = reglist; + struct ov8830_write_ctrl ctrl; + int err; + + ctrl.index = 0; + for (; next->type != OV8830_TOK_TERM; next++) { + switch (next->type & OV8830_TOK_MASK) { + case OV8830_TOK_DELAY: + err = __ov8830_flush_reg_array(client, &ctrl); + if (err) + return err; + msleep(next->val); + break; + + case OV8830_RMW: + err = __ov8830_flush_reg_array(client, &ctrl); + err |= ov8830_rmw_reg(client, + next->type & ~OV8830_RMW, + next->reg.sreg, next->val, + next->val2); + if (err) { + v4l2_err(client, "%s: rwm error, " + "aborted\n", __func__); + return err; + } + break; + + default: + /* + * If next address is not consecutive, data needs to be + * flushed before proceed. + */ + if (!__ov8830_write_reg_is_consecutive(client, &ctrl, + next)) { + err = __ov8830_flush_reg_array(client, &ctrl); + if (err) + return err; + } + err = __ov8830_buf_reg_array(client, &ctrl, next); + if (err) { + v4l2_err(client, "%s: write error, aborted\n", + __func__); + return err; + } + break; + } + } + + return __ov8830_flush_reg_array(client, &ctrl); +} + + +static int ov8830_t_focus_abs(struct v4l2_subdev *sd, s32 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov8830_device *dev = to_ov8830_sensor(sd); + int ret; + + value = min(value, OV8830_MAX_FOCUS_POS); + + ret = ov8830_write_reg(client, OV8830_16BIT, OV8830_VCM_CODE, + OV8830_MAX_FOCUS_POS - value); + if (ret == 0) { + dev->focus = value; + do_gettimeofday(&(dev->timestamp_t_focus_abs)); + } + return ret; +} + +static int ov8830_q_focus_abs(struct v4l2_subdev *sd, s32 *value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + u16 val; + + + + ret = ov8830_read_reg(client, OV8830_16BIT, + OV8830_VCM_CODE, &val); + *value = OV8830_MAX_FOCUS_POS - val; + + return ret; +} + +static int ov8830_t_focus_rel(struct v4l2_subdev *sd, s32 value) +{ + struct ov8830_device *dev = to_ov8830_sensor(sd); + return ov8830_t_focus_abs(sd, dev->focus + value); +} + +#define WAIT_FOR_VCM_MOTOR 60000 +static int ov8830_q_focus_status(struct v4l2_subdev *sd, s32 *value) +{ + u32 status = 0; + struct ov8830_device *dev = to_ov8830_sensor(sd); + struct timeval current_time; + bool stillmoving = false; + struct i2c_client *client = v4l2_get_subdevdata(sd); + + + do_gettimeofday(¤t_time); + if (current_time.tv_sec == (dev->timestamp_t_focus_abs).tv_sec) { + if (current_time.tv_usec < ((dev->timestamp_t_focus_abs).tv_usec+WAIT_FOR_VCM_MOTOR)) { + stillmoving = true; + } + } else { + if ((current_time.tv_usec+1000000) < ((dev->timestamp_t_focus_abs).tv_usec+WAIT_FOR_VCM_MOTOR)) { + /* assuming the delay betwee calls does not take more than a second. */ + stillmoving = true; + } + } + + if (stillmoving) { + status |= ATOMISP_FOCUS_STATUS_MOVING; + status |= ATOMISP_FOCUS_HP_IN_PROGRESS; + } else { + status |= ATOMISP_FOCUS_HP_COMPLETE; + status |= ATOMISP_FOCUS_STATUS_ACCEPTS_NEW_MOVE; + } + *value = status; + return 0; +} + +/* +static int ov8830_vcm_enable(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return ov8830_rmw_reg(client, OV8830_16BIT, OV8830_VCM_SLEW_STEP, + OV8830_VCM_ENABLE, 0x1); +} +*/ +static long ov8830_set_exposure(struct v4l2_subdev *sd, u16 coarse_itg, + u16 fine_itg, u16 gain) + +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + u16 frame_length; + struct ov8830_device *dev = to_ov8830_sensor(sd); + + if (ov8830_read_reg(client, OV8830_16BIT, + OV8830_FRAME_LENGTH_LINES, &frame_length)) + return -EINVAL; + + /* enable group hold */ + ret = ov8830_write_reg_array(client, ov8830_param_hold); + if (ret) + return ret; + + /* set coarse integration time */ + ret = ov8830_write_reg(client, OV8830_16BIT, + OV8830_COARSE_INTEGRATION_TIME, coarse_itg); + if (ret) + goto error; + + /* set fine integration time */ + ret = ov8830_write_reg(client, OV8830_16BIT, + OV8830_FINE_INTEGRATION_TIME, fine_itg); + if (ret) + goto error; + + /* set global gain */ + ret = ov8830_write_reg(client, OV8830_16BIT, + OV8830_GLOBAL_GAIN, gain); + + if (ret) + goto error; + dev->gain = gain; + dev->coarse_itg = coarse_itg; + dev->fine_itg = fine_itg; + +error: + /* disable group hold */ + ov8830_write_reg_array(client, ov8830_param_update); + return ret; +} + +static long ov8830_s_exposure(struct v4l2_subdev *sd, + struct atomisp_exposure *exposure) +{ + u16 coarse_itg, fine_itg, gain; + + coarse_itg = exposure->integration_time[0]; + fine_itg = exposure->integration_time[1]; + gain = exposure->gain[0]; + + return ov8830_set_exposure(sd, coarse_itg, fine_itg, gain); +} + +static int ov8830_read_reg_array(struct i2c_client *client, u16 size, u16 addr, + void *data) +{ + u8 *buf = data; + u16 index; + int ret = 0; + + for (index = 0; index + OV8830_BYTE_MAX <= size; + index += OV8830_BYTE_MAX) { + ret = ov8830_read_reg(client, OV8830_BYTE_MAX, + OV8830_OTP_START_ADDR + index, + (u16 *)&buf[index]); + if (ret) + return ret; + } + + if (size - index > 0) + ret = ov8830_read_reg(client, size - index, + OV8830_OTP_START_ADDR + index, + (u16 *)&buf[index]); + + return ret; +} + +static unsigned long +ov8830_otp_sum(struct v4l2_subdev *sd, u8 *buf, u16 start, u16 end) +{ + unsigned long sum = 0; + u16 i; + + for (i = start; i <= end; i++) + sum += buf[i]; + + return sum; +} + +static int ov8830_otp_checksum(struct v4l2_subdev *sd, u8 *buf, int list_len, + const struct ov8830_otp_checksum_format *list) +{ + unsigned long sum; + u8 checksum; + int i; + + for (i = 0; i < list_len; i++) { + sum = ov8830_otp_sum(sd, buf, list[i].start, list[i].end); + checksum = sum % OV8830_OTP_MOD_CHECKSUM; + if (buf[list[i].checksum] != checksum) + return -EINVAL; + } + + return 0; +} + +static int ov8830_otp_read(struct v4l2_subdev *sd, + const struct ov8830_reg *type, + void __user *data, u32 size) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + int retry = 100; + void *buf; + u16 ready; + + ret = ov8830_write_reg_array(client, type); + if (ret) { + v4l2_err(client, "%s: failed to prepare OTP memory\n", + __func__); + return ret; + } + + /* + * As we need to wait for sensor to prepare OTP memory, let's allocate + * buffer now to optimize time. + */ + buf = kmalloc(OV8830_OTP_DATA_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + do { + ret = ov8830_read_reg(client, OV8830_16BIT, + OV8830_OTP_READY_REG, &ready); + if (ret) { + v4l2_err(client, "%s: failed to read OTP memory " + "status\n", __func__); + goto out; + } + if (ready & OV8830_OTP_READY_REG_DONE) + break; + } while (--retry); + + if (!(ready & OV8830_OTP_READY_REG_OK)) { + v4l2_info(client, "%s: OTP memory was initialized with error\n", + __func__); + ret = -EIO; + goto out; + } + ret = ov8830_read_reg_array(client, OV8830_OTP_DATA_SIZE, + OV8830_OTP_START_ADDR, buf); + if (ret) { + v4l2_err(client, "%s: failed to read OTP data\n", __func__); + goto out; + } + if (OV8830_OTP_CHECKSUM) { + ret = ov8830_otp_checksum(sd, buf, + ARRAY_SIZE(ov8830_otp_checksum_list), + ov8830_otp_checksum_list); + if (ret) + goto out; + } + ret = copy_to_user(data, buf, size); + if (ret) { + v4l2_err(client, "%s: failed to copy OTP data to user\n", + __func__); + ret = -EFAULT; + } + +out: + kfree(buf); + return ret; +} + +static int ov8830_g_priv_int_data(struct v4l2_subdev *sd, + struct v4l2_private_int_data *priv) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov8830_device *dev = to_ov8830_sensor(sd); + u32 read_size = priv->size; + int ret; + + if (!dev->power) + return -EIO; + + if (dev->streaming) + return -EBUSY; + + if (!priv) + return -EINVAL; + + /* Return correct size */ + priv->size = OV8830_OTP_DATA_SIZE; + + /* No need to copy data if size is 0 */ + if (!read_size) + return 0; + + /* Correct read_size value only if bigger than maximum */ + if (read_size > OV8830_OTP_DATA_SIZE) + read_size = OV8830_OTP_DATA_SIZE; + + /* Try all banks, one by one, and return after first success */ + ret = ov8830_otp_read(sd, ov8830_otp_type30, priv->data, read_size); + if (!ret) + return 0; + ret = ov8830_otp_read(sd, ov8830_otp_type31, priv->data, read_size); + if (!ret) + return 0; + ret = ov8830_otp_read(sd, ov8830_otp_type32, priv->data, read_size); + if (!ret) + return 0; + + /* Driver has failed to find valid data */ + v4l2_info(client, "%s: sensor found no valid OTP data\n", __func__); + return ret; +} + +static long ov8830_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + switch (cmd) { + case ATOMISP_IOC_S_EXPOSURE: + return ov8830_s_exposure(sd, (struct atomisp_exposure *)arg); + case ATOMISP_IOC_G_SENSOR_PRIV_INT_DATA: + return ov8830_g_priv_int_data(sd, arg); + default: + return -EINVAL; + } + return 0; +} + +static int ov8830_init_registers(struct v4l2_subdev *sd) +{ + int ret; + struct i2c_client *client = v4l2_get_subdevdata(sd); + + ret = ov8830_write_reg_array(client, ov8830_reset_register); + ret |= ov8830_write_reg_array(client, ov8830_pll_timing); + ret |= ov8830_write_reg_array(client, ov8830_raw_10); + ret |= ov8830_write_reg_array(client, ov8830_mipi_config); + ret |= ov8830_write_reg_array(client, ov8830_recommended_settings); + ret |= ov8830_write_reg_array(client, ov8830_mipi_timing); + ret |= ov8830_write_reg_array(client, ov8830_scaler); + ret |= ov8830_write_reg_array(client, ov8830_init_vcm); + + return ret; +} + +static int ov8830_init(struct v4l2_subdev *sd, u32 val) +{ + int ret; + struct i2c_client *client = v4l2_get_subdevdata(sd); + + /* set inital registers */ + ret = ov8830_init_registers(sd); + + /*set VCM to home position */ + ret |= ov8830_t_focus_abs(sd, HOME_POS); + + /* Program shading table into sensor */ + ret |= ov8830_write_reg_array(client, ov8830_lens_shading); + + /* restore settings */ + ov8830_res = ov8830_res_preview; + N_RES = N_RES_PREVIEW; + + return ret; +} + +static void ov8830_uninit(struct v4l2_subdev *sd) +{ + struct ov8830_device *dev = to_ov8830_sensor(sd); + + dev->coarse_itg = 0; + dev->fine_itg = 0; + dev->gain = 0; + dev->focus = OV8830_INVALID_CONFIG; +} + +static int power_up(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov8830_device *dev = to_ov8830_sensor(sd); + int ret; + + /* power control */ + ret = dev->platform_data->power_ctrl(sd, 1); + if (ret) + goto fail_power; + + /* flis clock control */ + ret = dev->platform_data->flisclk_ctrl(sd, 1); + if (ret) + goto fail_clk; + + /* gpio ctrl */ + ret = dev->platform_data->gpio_ctrl(sd, 1); + if (ret) + dev_err(&client->dev, "gpio failed 1\n"); + msleep(20); + + return 0; + +fail_clk: + dev->platform_data->flisclk_ctrl(sd, 0); +fail_power: + dev->platform_data->power_ctrl(sd, 0); + dev_err(&client->dev, "sensor power-up failed\n"); + + return ret; +} + +static int power_down(struct v4l2_subdev *sd) +{ + struct ov8830_device *dev = to_ov8830_sensor(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + + ret = dev->platform_data->flisclk_ctrl(sd, 0); + if (ret) + dev_err(&client->dev, "flisclk failed\n"); + + /* gpio ctrl */ + ret = dev->platform_data->gpio_ctrl(sd, 0); + if (ret) + dev_err(&client->dev, "gpio failed 1\n"); + + /* power control */ + ret = dev->platform_data->power_ctrl(sd, 0); + if (ret) + dev_err(&client->dev, "vprog failed.\n"); + + return ret; +} + +static int ov8830_s_power(struct v4l2_subdev *sd, int on) +{ + struct ov8830_device *dev = to_ov8830_sensor(sd); + int ret; + + if (on == 0) { + ov8830_uninit(sd); + ret = power_down(sd); + dev->power = 0; + } else { + ret = power_up(sd); + if (!ret) { + dev->power = 1; + /* init motor initial position */ + return ov8830_init(sd, 0); + } + } + + return ret; +} + +static int ov8830_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!chip) + return -EINVAL; + + v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_OV8830, 0); + + return 0; +} + +static int ov8830_get_intg_factor(struct i2c_client *client, + struct camera_mipi_info *info, + const struct ov8830_reg *reglist) +{ + sensor_register vt_pix_clk_div; + sensor_register vt_sys_clk_div; + sensor_register pre_pll_clk_div; + sensor_register pll_multiplier; + sensor_register op_pix_clk_div; + sensor_register op_sys_clk_div; + + /* TODO: this should not be a constant but should be set by a call to + * MSIC's driver to get the ext_clk that MSIC supllies to the sensor. + */ + const int ext_clk_freq_mhz = 19200000; + struct sensor_mode_data buf; + const struct ov8830_reg *next = reglist; + int vt_pix_clk_freq_mhz; + u16 data[OV8830_SHORT_MAX]; + + sensor_register coarse_integration_time_min; + sensor_register coarse_integration_time_max_margin; + sensor_register fine_integration_time_min; + sensor_register fine_integration_time_max_margin; + sensor_register frame_length_lines; + sensor_register line_length_pck; + sensor_register read_mode; + + if (info == NULL) + return -EINVAL; + + memset(data, 0, OV8830_SHORT_MAX * sizeof(u16)); + if (ov8830_read_reg(client, 12, OV8830_VT_PIX_CLK_DIV, data)) + return -EINVAL; + vt_pix_clk_div = data[0]; + vt_sys_clk_div = data[1]; + pre_pll_clk_div = data[2]; + pll_multiplier = data[3]; + op_pix_clk_div = data[4]; + op_sys_clk_div = data[5]; + + memset(data, 0, OV8830_SHORT_MAX * sizeof(u16)); + if (ov8830_read_reg(client, 4, OV8830_FRAME_LENGTH_LINES, data)) + return -EINVAL; + frame_length_lines = data[0]; + line_length_pck = data[1]; + + memset(data, 0, OV8830_SHORT_MAX * sizeof(u16)); + if (ov8830_read_reg(client, 8, OV8830_COARSE_INTG_TIME_MIN, data)) + return -EINVAL; + coarse_integration_time_min = data[0]; + coarse_integration_time_max_margin = data[1]; + fine_integration_time_min = data[2]; + fine_integration_time_max_margin = data[3]; + + memset(data, 0, OV8830_SHORT_MAX * sizeof(u16)); + if (ov8830_read_reg(client, 2, OV8830_READ_MODE, data)) + return -EINVAL; + read_mode = data[0]; + + vt_pix_clk_freq_mhz = divsave_rounded(ext_clk_freq_mhz*pll_multiplier, + pre_pll_clk_div*vt_sys_clk_div*vt_pix_clk_div); + + memset(data, 0, OV8830_SHORT_MAX * sizeof(u16)); + if (ov8830_read_reg(client, 2, OV8830_FINE_INTEGRATION_TIME, data)) + return -EINVAL; + v4l2_info(client, "fine_integration_time_i2c: %d", data[0]); + + for (; next->type != OV8830_TOK_TERM; next++) { + if (next->type == OV8830_16BIT) { + if (next->reg.sreg == OV8830_FINE_INTEGRATION_TIME) { + buf.fine_integration_time_def = next->val; + break; + } + } + } + + /* something's wrong here, this mode does not have fine_igt set! */ + if (next->type == OV8830_TOK_TERM) + return -EINVAL; + + buf.coarse_integration_time_min = coarse_integration_time_min; + buf.coarse_integration_time_max_margin = coarse_integration_time_max_margin; + buf.fine_integration_time_min = fine_integration_time_min; + buf.fine_integration_time_max_margin = fine_integration_time_max_margin; + buf.vt_pix_clk_freq_mhz = vt_pix_clk_freq_mhz; + buf.line_length_pck = line_length_pck; + buf.frame_length_lines = frame_length_lines; + buf.read_mode = read_mode; + + memcpy(&info->data, &buf, sizeof(buf)); + + return 0; +} + +/* This returns the exposure time being used. This should only be used + for filling in EXIF data, not for actual image processing. */ +static int ov8830_q_exposure(struct v4l2_subdev *sd, s32 *value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u16 coarse; + int ret; + + /* the fine integration time is currently not calculated */ + ret = ov8830_read_reg(client, OV8830_16BIT, + OV8830_COARSE_INTEGRATION_TIME, &coarse); + *value = coarse; + + return ret; +} + +static int ov8830_test_pattern(struct v4l2_subdev *sd, s32 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return ov8830_write_reg(client, OV8830_16BIT, 0x3070, value); +} + +static int ov8830_v_flip(struct v4l2_subdev *sd, s32 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + + if (value > 1) + return -EINVAL; + + ret = ov8830_write_reg_array(client, ov8830_param_hold); + if (ret) + return ret; + ret = ov8830_rmw_reg(client, OV8830_16BIT & ~OV8830_RMW, + 0x3040, 0x8000, value); + if (ret) + return ret; + return ov8830_write_reg_array(client, ov8830_param_update); +} + + +static int ov8830_t_vcm_slew(struct v4l2_subdev *sd, s32 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (value > OV8830_VCM_SLEW_STEP_MAX) + return -EINVAL; + + return ov8830_rmw_reg(client, OV8830_16BIT, OV8830_VCM_SLEW_STEP, + OV8830_VCM_SLEW_STEP_MASK, value); +} + +static int ov8830_t_vcm_timing(struct v4l2_subdev *sd, s32 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + /* Max 16 bits */ + if (value > OV8830_VCM_SLEW_TIME_MAX) + return -EINVAL; + + return ov8830_write_reg(client, OV8830_16BIT, OV8830_VCM_SLEW_TIME, + value); +} + +static int ov8830_g_focal(struct v4l2_subdev *sd, s32 *val) +{ + *val = (OV8830_FOCAL_LENGTH_NUM << 16) | OV8830_FOCAL_LENGTH_DEM; + return 0; +} + +static int ov8830_g_fnumber(struct v4l2_subdev *sd, s32 *val) +{ + /*const f number for ov8830*/ + *val = (OV8830_F_NUMBER_DEFAULT_NUM << 16) | OV8830_F_NUMBER_DEM; + return 0; +} + +static int ov8830_g_fnumber_range(struct v4l2_subdev *sd, s32 *val) +{ + *val = (OV8830_F_NUMBER_DEFAULT_NUM << 24) | + (OV8830_F_NUMBER_DEM << 16) | + (OV8830_F_NUMBER_DEFAULT_NUM << 8) | OV8830_F_NUMBER_DEM; + return 0; +} + +struct ov8830_control ov8830_controls[] = { + { + .qc = { + .id = V4L2_CID_EXPOSURE_ABSOLUTE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x0, + .maximum = 0xffff, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + .query = ov8830_q_exposure, + }, + { + .qc = { + .id = V4L2_CID_TEST_PATTERN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Test pattern", + .minimum = 0, + .maximum = 0xffff, + .step = 1, + .default_value = 0, + }, + .tweak = ov8830_test_pattern, + }, + { + .qc = { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .tweak = ov8830_v_flip, + }, + { + .qc = { + .id = V4L2_CID_FOCUS_ABSOLUTE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "focus move absolute", + .minimum = 0, + .maximum = OV8830_MAX_FOCUS_POS, + .step = 1, + .default_value = 0, + .flags = 0, + }, + .tweak = ov8830_t_focus_abs, + .query = ov8830_q_focus_abs, + }, + { + .qc = { + .id = V4L2_CID_FOCUS_RELATIVE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "focus move relative", + .minimum = OV8830_MAX_FOCUS_NEG, + .maximum = OV8830_MAX_FOCUS_POS, + .step = 1, + .default_value = 0, + .flags = 0, + }, + .tweak = ov8830_t_focus_rel, + }, + { + .qc = { + .id = V4L2_CID_FOCUS_STATUS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "focus status", + .minimum = 0, + .maximum = 100, /* allow enum to grow in the future */ + .step = 1, + .default_value = 0, + .flags = 0, + }, + .query = ov8830_q_focus_status, + }, + { + .qc = { + .id = V4L2_CID_VCM_SLEW, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "vcm slew", + .minimum = 0, + .maximum = OV8830_VCM_SLEW_STEP_MAX, + .step = 1, + .default_value = 0, + .flags = 0, + }, + .tweak = ov8830_t_vcm_slew, + }, + { + .qc = { + .id = V4L2_CID_VCM_TIMEING, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "vcm step time", + .minimum = 0, + .maximum = OV8830_VCM_SLEW_TIME_MAX, + .step = 1, + .default_value = 0, + .flags = 0, + }, + .tweak = ov8830_t_vcm_timing, + }, + { + .qc = { + .id = V4L2_CID_FOCAL_ABSOLUTE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "focal length", + .minimum = OV8830_FOCAL_LENGTH_DEFAULT, + .maximum = OV8830_FOCAL_LENGTH_DEFAULT, + .step = 0x01, + .default_value = OV8830_FOCAL_LENGTH_DEFAULT, + .flags = 0, + }, + .query = ov8830_g_focal, + }, + { + .qc = { + .id = V4L2_CID_FNUMBER_ABSOLUTE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "f-number", + .minimum = OV8830_F_NUMBER_DEFAULT, + .maximum = OV8830_F_NUMBER_DEFAULT, + .step = 0x01, + .default_value = OV8830_F_NUMBER_DEFAULT, + .flags = 0, + }, + .query = ov8830_g_fnumber, + }, + { + .qc = { + .id = V4L2_CID_FNUMBER_RANGE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "f-number range", + .minimum = OV8830_F_NUMBER_RANGE, + .maximum = OV8830_F_NUMBER_RANGE, + .step = 0x01, + .default_value = OV8830_F_NUMBER_RANGE, + .flags = 0, + }, + .query = ov8830_g_fnumber_range, + } +}; +#define N_CONTROLS (ARRAY_SIZE(ov8830_controls)) + +static struct ov8830_control *ov8830_find_control(u32 id) +{ + int i; + + for (i = 0; i < N_CONTROLS; i++) + if (ov8830_controls[i].qc.id == id) + return &ov8830_controls[i]; + return NULL; +} + +static int ov8830_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + struct ov8830_control *ctrl = ov8830_find_control(qc->id); + + if (ctrl == NULL) + return -EINVAL; + + *qc = ctrl->qc; + + return 0; +} + +/* ov8830 control set/get */ +static int ov8830_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct ov8830_control *s_ctrl; + + if (!ctrl) + return -EINVAL; + + s_ctrl = ov8830_find_control(ctrl->id); + if ((s_ctrl == NULL) || (s_ctrl->query == NULL)) + return -EINVAL; + + return s_ctrl->query(sd, &ctrl->value); +} + +static int ov8830_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct ov8830_control *octrl = ov8830_find_control(ctrl->id); + + if ((octrl == NULL) || (octrl->tweak == NULL)) + return -EINVAL; + + return octrl->tweak(sd, ctrl->value); +} + +struct ov8830_format ov8830_formats[] = { + { + .desc = "RGB Bayer Format", + .regs = NULL, + }, +}; + +/* + * distance - calculate the distance + * @res: resolution + * @w: width + * @h: height + * + * Get the gap between resolution and w/h. + * res->width/height smaller than w/h wouldn't be considered. + * Returns the value of gap or -1 if fail. + */ +#define LARGEST_ALLOWED_RATIO_MISMATCH 140 /* tune this value so that the DVS resolutions get selected properly, but make sure 16:9 do not match 4:3*/ +static int distance(struct ov8830_resolution *res, u32 w, u32 h) +{ + unsigned int w_ratio = ((res->width<<13)/w); + unsigned int h_ratio = ((res->height<<13)/h); + int match = abs(((w_ratio<<13)/h_ratio) - ((int)8192)); + + if ((w_ratio < (int)8192) || (h_ratio < (int)8192) || (match > LARGEST_ALLOWED_RATIO_MISMATCH)) + return -1; + + return w_ratio + h_ratio; +} + +/* Return the nearest higher resolution index */ +static int nearest_resolution_index(int w, int h) +{ + int i; + int idx = -1; + int dist; + int min_dist = INT_MAX; + struct ov8830_resolution *tmp_res = NULL; + + for (i = 0; i < N_RES; i++) { + tmp_res = &ov8830_res[i]; + dist = distance(tmp_res, w, h); + if (dist == -1) + continue; + if (dist < min_dist) { + min_dist = dist; + idx = i; + } + } + + if (idx == -1) + return -1; + + return idx; +} + +static int get_resolution_index(int w, int h) +{ + int i; + + for (i = 0; i < N_RES; i++) { + if (w != ov8830_res[i].width) + continue; + if (h != ov8830_res[i].height) + continue; + /* Found it */ + return i; + } + return -1; +} + +static int ov8830_try_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + int idx; + + if (!fmt) + return -EINVAL; + + if ((fmt->width > OV8830_RES_WIDTH_MAX) || (fmt->height > OV8830_RES_HEIGHT_MAX)) { + fmt->width = OV8830_RES_WIDTH_MAX; + fmt->height = OV8830_RES_HEIGHT_MAX; + } else { + idx = nearest_resolution_index(fmt->width, fmt->height); + + /* + * nearest_resolution_index() doesn't return smaller resolutions. + * If it fails, it means the requested resolution is higher than we + * can support. Fallback to highest possible resolution in this case. + */ + if (idx == -1) + idx = N_RES - 1; + + fmt->width = ov8830_res[idx].width; + fmt->height = ov8830_res[idx].height; + } + + fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10; + + + return 0; +} + +static int ov8830_s_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct ov8830_device *dev = to_ov8830_sensor(sd); + const struct ov8830_reg *ov8830_def_reg; + struct camera_mipi_info *ov8830_info = NULL; + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + + ov8830_info = v4l2_get_subdev_hostdata(sd); + if (ov8830_info == NULL) + return -EINVAL; + + ret = ov8830_try_mbus_fmt(sd, fmt); + if (ret) { + v4l2_err(sd, "try fmt fail\n"); + return ret; + } + dev->fmt_idx = get_resolution_index(fmt->width, fmt->height); + + /* Sanity check */ + if (unlikely(dev->fmt_idx == -1)) { + v4l2_err(sd, "get resolution fail\n"); + return -EINVAL; + } + + ov8830_def_reg = ov8830_res[dev->fmt_idx].regs; + ret = ov8830_write_reg_array(client, ov8830_def_reg); + if (ret) + return -EINVAL; + + dev->fps = ov8830_res[dev->fmt_idx].fps; + dev->pixels_per_line = ov8830_res[dev->fmt_idx].pixels_per_line; + dev->lines_per_frame = ov8830_res[dev->fmt_idx].lines_per_frame; + + ret = ov8830_get_intg_factor(client, ov8830_info, ov8830_def_reg); + if (ret) { + v4l2_err(sd, "failed to get integration_factor\n"); + return -EINVAL; + } + + /* restore exposure, gain settings */ + if (dev->coarse_itg) + ov8830_set_exposure(sd, dev->coarse_itg, dev->fine_itg, + dev->gain); + + return 0; +} + +static int ov8830_g_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct ov8830_device *dev = to_ov8830_sensor(sd); + + if (!fmt) + return -EINVAL; + + fmt->width = ov8830_res[dev->fmt_idx].width; + fmt->height = ov8830_res[dev->fmt_idx].height; + fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10; + + return 0; +} + +static int ov8830_detect(struct i2c_client *client, u16 *id, u8 *revision) +{ + struct i2c_adapter *adapter = client->adapter; + u16 high, low; + + /* i2c check */ + if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) + return -ENODEV; + + /* check sensor chip ID */ + if (ov8830_read_reg(client, OV8830_8BIT, OV8830_SC_CMMN_CHIP_ID_H, + &high)) { + v4l2_err(client, "sensor_id_high = 0x%x\n", high); + return -ENODEV; + } + if (ov8830_read_reg(client, OV8830_8BIT, OV8830_SC_CMMN_CHIP_ID_L, + &low)) { + v4l2_err(client, "sensor_id_low = 0x%x\n", high); + return -ENODEV; + } + *id = (((u8) high) << 8) | (u8) low; + v4l2_info(client, "sensor_id = 0x%x\n", *id); + real_model_id = *id; + + if (*id != OV8830_ID) { + v4l2_err(client, "sensor ID error\n"); + return -ENODEV; + } + + v4l2_info(client, "detect ov8830 success\n"); + + /* REVISIT: HACK: Driver is currently forcing revision to 0 */ + *revision = 0; + + return 0; +} + +/* + * ov8830 stream on/off + */ +static int ov8830_s_stream(struct v4l2_subdev *sd, int enable) +{ + int ret; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov8830_device *dev = to_ov8830_sensor(sd); + u16 temp; + + if (enable) { + if (dev->sensor_revision <= 0x0) { + /* begin: vcm hack, needs to be removed when new camera module is availible */ + struct ov8830_reg ov8830_stream_enable[] = { + ov8830_streaming[0], + {OV8830_16BIT, {0x30F2}, 0x0000}, /* VCM_NEW_CODE */ + INIT_VCM_CONTROL, + {OV8830_16BIT, {0x30F2}, 0x0000}, /* VCM_NEW_CODE */ + {OV8830_TOK_DELAY, {0}, 100}, + {OV8830_TOK_TERM, {0}, 0} + }; + + ov8830_stream_enable[1].val = (OV8830_MAX_FOCUS_POS - dev->focus) + 1; + ov8830_stream_enable[3].val = (OV8830_MAX_FOCUS_POS - dev->focus); + + ret = ov8830_write_reg_array(client, ov8830_stream_enable); + + /* end: vcm hack, needs to be removed when new camera module is availible */ + } else { + ret = ov8830_write_reg_array(client, ov8830_streaming); + } + + if (ret != 0) { + v4l2_err(client, "write_reg_array err\n"); + return ret; + } + dev->streaming = 1; + } else { + + ret = ov8830_write_reg_array(client, ov8830_soft_standby); + if (ret != 0) { + v4l2_err(client, "write_reg_array err\n"); + return ret; + } + dev->streaming = 0; + } + + /* restore settings */ + ov8830_res = ov8830_res_preview; + N_RES = N_RES_PREVIEW; + + return 0; +} + +/* + * ov8830 enum frame size, frame intervals + */ +static int ov8830_enum_framesizes(struct v4l2_subdev *sd, + struct v4l2_frmsizeenum *fsize) +{ + unsigned int index = fsize->index; + + if (index >= N_RES) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = ov8830_res[index].width; + fsize->discrete.height = ov8830_res[index].height; + fsize->reserved[0] = ov8830_res[index].used; + + return 0; +} + +static int ov8830_enum_frameintervals(struct v4l2_subdev *sd, + struct v4l2_frmivalenum *fival) +{ + unsigned int index = fival->index; + + if (index >= N_RES) + return -EINVAL; + + /* since the isp will donwscale the resolution to the right size, find the nearest one that will allow the isp to do so + * important to ensure that the resolution requested is padded correctly by the requester, which is the atomisp driver in this case. + */ + index = nearest_resolution_index(fival->width, fival->height); + + if (-1 == index) + return -EINVAL; + + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; +/* fival->width = ov8830_res[index].width; + fival->height = ov8830_res[index].height; */ + fival->discrete.numerator = 1; + fival->discrete.denominator = ov8830_res[index].fps; + + return 0; +} + +static int ov8830_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index, + enum v4l2_mbus_pixelcode *code) +{ + *code = V4L2_MBUS_FMT_SGRBG10_1X10; + return 0; +} + +static int ov8830_s_config(struct v4l2_subdev *sd, + int irq, void *pdata) +{ + struct ov8830_device *dev = to_ov8830_sensor(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 sensor_revision; + u16 sensor_id; + int ret; + + if (pdata == NULL) + return -ENODEV; + + dev->platform_data = pdata; + + ret = ov8830_s_power(sd, 1); + if (ret) { + v4l2_err(client, "ov8830 power-up err.\n"); + return ret; + } + + ret = dev->platform_data->csi_cfg(sd, 1); + if (ret) + goto fail_csi_cfg; + + /* config & detect sensor */ + ret = ov8830_detect(client, &sensor_id, &sensor_revision); + if (ret) { + v4l2_err(client, "ov8830_detect err s_config.\n"); + goto fail_detect; + } + + dev->sensor_id = sensor_id; + dev->sensor_revision = sensor_revision; + + /* power off sensor */ + ret = ov8830_s_power(sd, 0); + if (ret) { + v4l2_err(client, "ov8830 power-down err.\n"); + return ret; + } + + return 0; + +fail_detect: + dev->platform_data->csi_cfg(sd, 0); +fail_csi_cfg: + ov8830_s_power(sd, 0); + dev_err(&client->dev, "sensor power-gating failed\n"); + return ret; +} + +static int +ov8830_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= MAX_FMTS) + return -EINVAL; + code->code = V4L2_MBUS_FMT_SGRBG10_1X10; + + return 0; +} + +static int +ov8830_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + int index = fse->index; + + if (index >= N_RES) + return -EINVAL; + + fse->min_width = ov8830_res[index].width; + fse->min_height = ov8830_res[index].height; + fse->max_width = ov8830_res[index].width; + fse->max_height = ov8830_res[index].height; + + return 0; +} + +static struct v4l2_mbus_framefmt * +__ov8830_get_pad_format(struct ov8830_device *sensor, + struct v4l2_subdev_fh *fh, unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd); + + if (pad != 0) { + v4l2_err(client, "%s err. pad %x\n", __func__, pad); + return NULL; + } + + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(fh, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &sensor->format; + default: + return NULL; + } +} + +static int +ov8830_get_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct ov8830_device *dev = to_ov8830_sensor(sd); + struct v4l2_mbus_framefmt *format = + __ov8830_get_pad_format(dev, fh, fmt->pad, fmt->which); + + if (format == NULL) + return -EINVAL; + fmt->format = *format; + + return 0; +} + +static int +ov8830_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct ov8830_device *dev = to_ov8830_sensor(sd); + struct v4l2_mbus_framefmt *format = + __ov8830_get_pad_format(dev, fh, fmt->pad, fmt->which); + + if (format == NULL) + return -EINVAL; + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + dev->format = fmt->format; + + return 0; +} + +static int +ov8830_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *param) +{ + struct ov8830_device *dev = to_ov8830_sensor(sd); + + dev->run_mode = param->parm.capture.capturemode; + + switch (dev->run_mode) { + case CI_MODE_VIDEO: + ov8830_res = ov8830_res_video; + N_RES = N_RES_VIDEO; + break; + case CI_MODE_STILL_CAPTURE: + ov8830_res = ov8830_res_still; + N_RES = N_RES_STILL; + break; + default: + ov8830_res = ov8830_res_preview; + N_RES = N_RES_PREVIEW; + } + return 0; +} + +int +ov8830_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *interval) +{ + struct ov8830_device *dev = to_ov8830_sensor(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + u16 lines_per_frame; + u8 fps; + /* + * if no specific information to calculate the fps, + * just used the value in sensor settings + */ + if (!dev->pixels_per_line || !dev->lines_per_frame) { + interval->interval.numerator = 1; + interval->interval.denominator = dev->fps; + return 0; + } + + /* + * DS: if coarse_integration_time is set larger than + * lines_per_frame the frame_size will be expanded to + * coarse_integration_time+1 + */ + if (dev->coarse_itg > dev->lines_per_frame) { + if (dev->coarse_itg == 0xFFFF) { + /* + * we can not add 1 according to ds, as this will + * cause over flow + */ + v4l2_warn(client, "%s: abnormal coarse_itg:0x%x\n", + __func__, dev->coarse_itg); + lines_per_frame = dev->coarse_itg; + } else + lines_per_frame = dev->coarse_itg + 1; + } else + lines_per_frame = dev->lines_per_frame; + + interval->interval.numerator = dev->pixels_per_line * + lines_per_frame; + interval->interval.denominator = OV8830_MCLK * 1000000; + + return 0; +} + +static const struct v4l2_subdev_video_ops ov8830_video_ops = { + .s_stream = ov8830_s_stream, + .enum_framesizes = ov8830_enum_framesizes, + .enum_frameintervals = ov8830_enum_frameintervals, + .enum_mbus_fmt = ov8830_enum_mbus_fmt, + .try_mbus_fmt = ov8830_try_mbus_fmt, + .g_mbus_fmt = ov8830_g_mbus_fmt, + .s_mbus_fmt = ov8830_s_mbus_fmt, + .s_parm = ov8830_s_parm, + .g_frame_interval = ov8830_g_frame_interval, +}; + +static const struct v4l2_subdev_core_ops ov8830_core_ops = { + .g_chip_ident = ov8830_g_chip_ident, + .s_config = ov8830_s_config, + .queryctrl = ov8830_queryctrl, + .g_ctrl = ov8830_g_ctrl, + .s_ctrl = ov8830_s_ctrl, + .s_power = ov8830_s_power, + .ioctl = ov8830_ioctl, + .init = ov8830_init, +}; + +/* REVISIT: Do we need pad operations? */ +static const struct v4l2_subdev_pad_ops ov8830_pad_ops = { + .enum_mbus_code = ov8830_enum_mbus_code, + .enum_frame_size = ov8830_enum_frame_size, + .get_fmt = ov8830_get_pad_format, + .set_fmt = ov8830_set_pad_format, +}; + +static const struct v4l2_subdev_ops ov8830_ops = { + .core = &ov8830_core_ops, + .video = &ov8830_video_ops, + .pad = &ov8830_pad_ops, +}; + +static const struct media_entity_operations ov8830_entity_ops = { + .set_power = v4l2_subdev_set_power, +}; + +static int ov8830_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov8830_device *dev = to_ov8830_sensor(sd); + + dev->platform_data->csi_cfg(sd, 0); + v4l2_device_unregister_subdev(sd); + kfree(dev); + + return 0; +} + +static int ov8830_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ov8830_device *dev; + int ret; + + /* allocate sensor device & init sub device */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + v4l2_err(client, "%s: out of memory\n", __func__); + return -ENOMEM; + } + + dev->fmt_idx = 0; + v4l2_i2c_subdev_init(&(dev->sd), client, &ov8830_ops); + + dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + dev->pad.flags = MEDIA_PAD_FLAG_OUTPUT; + dev->sd.entity.ops = &ov8830_entity_ops; + dev->format.code = V4L2_MBUS_FMT_SGRBG10_1X10; + + /* REVISIT: Do we need media controller? */ + ret = media_entity_init(&dev->sd.entity, 1, &dev->pad, 0); + if (ret) { + ov8830_remove(client); + return ret; + } + + return 0; +} + +static const struct i2c_device_id ov8830_id[] = { + {OV8830_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, ov8830_id); + +static struct v4l2_i2c_driver_data v4l2_i2c_data = { + .name = OV8830_NAME, + .probe = ov8830_probe, + .remove = ov8830_remove, + .id_table = ov8830_id, +}; + +MODULE_DESCRIPTION("A low-level driver for Omnivision OV8830 sensors"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/ov8830.h b/drivers/media/video/ov8830.h new file mode 100644 index 0000000..3053eb5 --- /dev/null +++ b/drivers/media/video/ov8830.h @@ -0,0 +1,1066 @@ +/* + * Support for Omnivision OV8830 camera sensor. + * Based on Aptina mt9e013 driver. + * + * Copyright (c) 2010 Intel Corporation. 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 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#ifndef __OV8830_H__ +#define __OV8830_H__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define OV8830_NAME "ov8830" +#define OV8830_ADDR 0x36 +#define OV8830_ID 0x4b00 + +#define LAST_REG_SETING {0xffff, 0xff} +#define is_last_reg_setting(item) ((item).reg == 0xffff) +#define I2C_MSG_LENGTH 0x2 + +#define OV8830_INVALID_CONFIG 0xffffffff + +#define OV8830_MAX_FOCUS_POS 255 +#define OV8830_MAX_FOCUS_NEG (-255) + +#define OV8830_INTG_UNIT_US 100 +#define OV8830_MCLK 192 + +#define OV8830_REG_BITS 16 +#define OV8830_REG_MASK 0xFFFF + +/* This should be added into include/linux/videodev2.h */ +#ifndef V4L2_IDENT_OV8830 +#define V4L2_IDENT_OV8830 8245 +#endif + +/* + * ov8830 System control registers + */ +#define OV8830_SC_CMMN_CHIP_ID_H 0x0000 +#define OV8830_SC_CMMN_CHIP_ID_L 0x0001 + +#define GROUPED_PARAMETER_UPDATE 0x0000 +#define GROUPED_PARAMETER_HOLD 0x0100 +#define OV8830_GROUPED_PARAMETER_HOLD 0x0104 + +#define OV8830_VT_PIX_CLK_DIV 0x0300 +#define OV8830_VT_SYS_CLK_DIV 0x0302 +#define OV8830_PRE_PLL_CLK_DIV 0x0304 +#define OV8830_PLL_MULTIPLIER 0x0306 +#define OV8830_OP_PIX_DIV 0x0308 +#define OV8830_OP_SYS_DIV 0x030A +#define OV8830_FRAME_LENGTH_LINES 0x0340 +#define OV8830_LINE_LENGTH_PCK 0x0342 +#define OV8830_COARSE_INTG_TIME_MIN 0x1004 +#define OV8830_COARSE_INTG_TIME_MAX 0x1006 +#define OV8830_FINE_INTG_TIME_MIN 0x1008 +#define OV8830_FINE_INTG_MIN_DEF 0x4FE +#define OV8830_FINE_INTG_TIME_MAX 0x100A +#define OV8830_FINE_INTG_MAX_DEF 0x3EE + +#define OV8830_READ_MODE 0x3040 + + +#define OV8830_COARSE_INTEGRATION_TIME 0x3012 +#define OV8830_FINE_INTEGRATION_TIME 0x3014 +#define OV8830_ROW_SPEED 0x3016 +#define OV8830_GLOBAL_GAIN 0x305e +#define OV8830_GLOBAL_GAIN_WR 0x1000 +#define OV8830_TEST_PATTERN_MODE 0x3070 +#define OV8830_VCM_SLEW_STEP 0x30F0 +#define OV8830_VCM_SLEW_STEP_MAX 0x7 +#define OV8830_VCM_SLEW_STEP_MASK 0x7 +#define OV8830_VCM_CODE 0x30F2 +#define OV8830_VCM_SLEW_TIME 0x30F4 +#define OV8830_VCM_SLEW_TIME_MAX 0xffff +#define OV8830_VCM_ENABLE 0x8000 + +/* ov8830 SCCB */ +#define OV8830_SCCB_CTRL 0x3100 +#define OV8830_AEC_PK_EXPO_H 0x3500 +#define OV8830_AEC_PK_EXPO_M 0x3501 +#define OV8830_AEC_PK_EXPO_L 0x3502 +#define OV8830_AEC_MANUAL_CTRL 0x3503 +#define OV8830_AGC_ADJ_H 0x3508 +#define OV8830_AGC_ADJ_L 0x3509 + +#define OV8830_FOCAL_LENGTH_NUM 439 /*4.39mm*/ +#define OV8830_FOCAL_LENGTH_DEM 100 +#define OV8830_F_NUMBER_DEFAULT_NUM 24 +#define OV8830_F_NUMBER_DEM 10 + +#define OV8830_X_ADDR_MIN 0X1180 +#define OV8830_Y_ADDR_MIN 0X1182 +#define OV8830_X_ADDR_MAX 0X1184 +#define OV8830_Y_ADDR_MAX 0X1186 + +#define OV8830_MIN_FRAME_LENGTH_LINES 0x1140 +#define OV8830_MAX_FRAME_LENGTH_LINES 0x1142 +#define OV8830_MIN_LINE_LENGTH_PCK 0x1144 +#define OV8830_MAX_LINE_LENGTH_PCK 0x1146 +#define OV8830_MIN_LINE_BLANKING_PCK 0x1148 +#define OV8830_MIN_FRAME_BLANKING_LINES 0x114A +#define OV8830_X_OUTPUT_SIZE 0x034C +#define OV8830_Y_OUTPUT_SIZE 0x034E + + +/* + * focal length bits definition: + * bits 31-16: numerator, bits 15-0: denominator + */ +#define OV8830_FOCAL_LENGTH_DEFAULT 0x1B70064 + +/* + * current f-number bits definition: + * bits 31-16: numerator, bits 15-0: denominator + */ +#define OV8830_F_NUMBER_DEFAULT 0x18000a + +/* + * f-number range bits definition: + * bits 31-24: max f-number numerator + * bits 23-16: max f-number denominator + * bits 15-8: min f-number numerator + * bits 7-0: min f-number denominator + */ +#define OV8830_F_NUMBER_RANGE 0x180a180a +#define OTPM_ADD_START_1 0x1000 +#define OTPM_DATA_LENGTH_1 0x0100 +#define OTPM_COUNT 0x200 + +/* Defines for register writes and register array processing */ +#define OV8830_BYTE_MAX 32 +#define OV8830_SHORT_MAX 16 +#define I2C_RETRY_COUNT 5 +#define OV8830_TOK_MASK 0xfff0 + +#define OV8830_STATUS_POWER_DOWN 0x0 +#define OV8830_STATUS_STANDBY 0x2 +#define OV8830_STATUS_ACTIVE 0x3 +#define OV8830_STATUS_VIEWFINDER 0x4 + +#define v4l2_format_capture_type_entry(_width, _height, \ + _pixelformat, _bytesperline, _colorspace) \ + {\ + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,\ + .fmt.pix.width = (_width),\ + .fmt.pix.height = (_height),\ + .fmt.pix.pixelformat = (_pixelformat),\ + .fmt.pix.bytesperline = (_bytesperline),\ + .fmt.pix.colorspace = (_colorspace),\ + .fmt.pix.sizeimage = (_height)*(_bytesperline),\ + } + +#define s_output_format_entry(_width, _height, _pixelformat, \ + _bytesperline, _colorspace, _fps) \ + {\ + .v4l2_fmt = v4l2_format_capture_type_entry(_width, \ + _height, _pixelformat, _bytesperline, \ + _colorspace),\ + .fps = (_fps),\ + } + +#define s_output_format_reg_entry(_width, _height, _pixelformat, \ + _bytesperline, _colorspace, _fps, _reg_setting) \ + {\ + .s_fmt = s_output_format_entry(_width, _height,\ + _pixelformat, _bytesperline, \ + _colorspace, _fps),\ + .reg_setting = (_reg_setting),\ + } + +struct s_ctrl_id { + struct v4l2_queryctrl qc; + int (*s_ctrl)(struct v4l2_subdev *sd, u32 val); + int (*g_ctrl)(struct v4l2_subdev *sd, u32 *val); +}; + +#define v4l2_queryctrl_entry_integer(_id, _name,\ + _minimum, _maximum, _step, \ + _default_value, _flags) \ + {\ + .id = (_id), \ + .type = V4L2_CTRL_TYPE_INTEGER, \ + .name = _name, \ + .minimum = (_minimum), \ + .maximum = (_maximum), \ + .step = (_step), \ + .default_value = (_default_value),\ + .flags = (_flags),\ + } +#define v4l2_queryctrl_entry_boolean(_id, _name,\ + _default_value, _flags) \ + {\ + .id = (_id), \ + .type = V4L2_CTRL_TYPE_BOOLEAN, \ + .name = _name, \ + .minimum = 0, \ + .maximum = 1, \ + .step = 1, \ + .default_value = (_default_value),\ + .flags = (_flags),\ + } + +#define s_ctrl_id_entry_integer(_id, _name, \ + _minimum, _maximum, _step, \ + _default_value, _flags, \ + _s_ctrl, _g_ctrl) \ + {\ + .qc = v4l2_queryctrl_entry_integer(_id, _name,\ + _minimum, _maximum, _step,\ + _default_value, _flags), \ + .s_ctrl = _s_ctrl, \ + .g_ctrl = _g_ctrl, \ + } + +#define s_ctrl_id_entry_boolean(_id, _name, \ + _default_value, _flags, \ + _s_ctrl, _g_ctrl) \ + {\ + .qc = v4l2_queryctrl_entry_boolean(_id, _name,\ + _default_value, _flags), \ + .s_ctrl = _s_ctrl, \ + .g_ctrl = _g_ctrl, \ + } + + +#define macro_string_entry(VAL) \ + { \ + .val = VAL, \ + .string = #VAL, \ + } + +enum ov8830_tok_type { + OV8830_8BIT = 0x0001, + OV8830_16BIT = 0x0002, + OV8830_RMW = 0x0010, + OV8830_TOK_TERM = 0xf000, /* terminating token for reg list */ + OV8830_TOK_DELAY = 0xfe00 /* delay token for reg list */ +}; + +/* + * If register address or register width is not 32 bit width, + * user needs to convert it manually + */ + +struct s_register_setting { + u32 reg; + u32 val; +}; + +struct s_output_format { + struct v4l2_format v4l2_fmt; + int fps; +}; + +/** + * struct ov8830_fwreg - Firmware burst command + * @type: FW burst or 8/16 bit register + * @addr: 16-bit offset to register or other values depending on type + * @val: data value for burst (or other commands) + * + * Define a structure for sensor register initialization values + */ +struct ov8830_fwreg { + enum ov8830_tok_type type; /* value, register or FW burst string */ + u16 addr; /* target address */ + u32 val[8]; +}; + +/** + * struct ov8830_reg - MI sensor register format + * @type: type of the register + * @reg: 16-bit offset to register + * @val: 8/16/32-bit register value + * + * Define a structure for sensor register initialization values + */ +struct ov8830_reg { + enum ov8830_tok_type type; + union { + u16 sreg; + struct ov8830_fwreg *fwreg; + } reg; + u32 val; /* @set value for read/mod/write, @mask */ + u32 val2; /* optional: for rmw, OR mask */ +}; + +/* Store macro values' debug names */ +struct macro_string { + u8 val; + char *string; +}; + +static inline const char * +macro_to_string(const struct macro_string *array, int size, u8 val) +{ + int i; + for (i = 0; i < size; i++) { + if (array[i].val == val) + return array[i].string; + } + return "Unknown VAL"; +} + +struct ov8830_control { + struct v4l2_queryctrl qc; + int (*query)(struct v4l2_subdev *sd, s32 *value); + int (*tweak)(struct v4l2_subdev *sd, s32 value); +}; + +struct ov8830_resolution { + u8 *desc; + int res; + int width; + int height; + int fps; + bool used; + unsigned short pixels_per_line; + unsigned short lines_per_frame; + const struct ov8830_reg *regs; +}; + +struct ov8830_format { + u8 *desc; + u32 pixelformat; + struct s_register_setting *regs; +}; + + +/* ov8830 device structure */ +struct ov8830_device { + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_mbus_framefmt format; + + struct camera_sensor_platform_data *platform_data; + int fmt_idx; + int status; + int streaming; + int power; + u8 res; + u8 type; + u16 sensor_id; + u8 sensor_revision; + u16 coarse_itg; + u16 fine_itg; + u16 gain; + u32 focus; + u16 pixels_per_line; + u16 lines_per_frame; + u8 fps; + int run_mode; + struct timeval timestamp_t_focus_abs; + +}; + +#define OV8830_MAX_WRITE_BUF_SIZE 128 +struct ov8830_write_buffer { + u16 addr; + u8 data[OV8830_MAX_WRITE_BUF_SIZE]; +}; + +struct ov8830_write_ctrl { + int index; + struct ov8830_write_buffer buffer; +}; + +#define OV8830_OTP_START_ADDR 0x3800 +#define OV8830_OTP_DATA_SIZE 456 +#define OV8830_OTP_READY_REG 0x304a +#define OV8830_OTP_READY_REG_DONE (1 << 5) +#define OV8830_OTP_READY_REG_OK (1 << 6) + +static const struct ov8830_reg ov8830_otp_type30[] = { + {OV8830_16BIT, {0x3134}, 0xcd95}, + {OV8830_16BIT, {0x304c}, 0x3000}, + {OV8830_16BIT, {0x304a}, 0x0010}, + {OV8830_TOK_TERM, {0}, 0} +}; + +static const struct ov8830_reg ov8830_otp_type31[] = { + {OV8830_16BIT, {0x3134}, 0xcd95}, + {OV8830_16BIT, {0x304c}, 0x3100}, + {OV8830_16BIT, {0x304a}, 0x0010}, + {OV8830_TOK_TERM, {0}, 0} +}; + +static const struct ov8830_reg ov8830_otp_type32[] = { + {OV8830_16BIT, {0x3134}, 0xcd95}, + {OV8830_16BIT, {0x304c}, 0x3200}, + {OV8830_16BIT, {0x304a}, 0x0010}, + {OV8830_TOK_TERM, {0}, 0} +}; + +#define OV8830_OTP_CHECKSUM 1 +#define OV8830_OTP_MOD_CHECKSUM 255 + +/* + * Checksum entries in OTP data: + * @start: start offset of checksum's input data + * @end: end offset of checksum's input data + * @checksum: offset where checksum is placed + */ +struct ov8830_otp_checksum_format { + u16 start; + u16 end; + u16 checksum; +}; + +static const struct ov8830_otp_checksum_format +ov8830_otp_checksum_list[] = { + {0x0004, 0x00d7, 0x00e1}, + {0x00d8, 0x00df, 0x00e0}, + {0x00e4, 0x01b7, 0x01c1}, + {0x01b8, 0x01bf, 0x01c0}, + {0x0000, 0x01c3, 0x01c4}, +}; + +#define MAX_FMTS 1 + +#define OV8830_RES_WIDTH_MAX 3280 +#define OV8830_RES_HEIGHT_MAX 2464 + +/* Recommended Settings 29 Mar 2011*/ +static const struct ov8830_reg ov8830_recommended_settings[] = { + {OV8830_16BIT, {0x3044}, 0x0590}, + {OV8830_16BIT, {0x306E}, 0xFC80}, + {OV8830_16BIT, {0x30B2}, 0xC000}, + {OV8830_16BIT, {0x30D6}, 0x0800}, + {OV8830_16BIT, {0x316C}, 0xB42F}, + {OV8830_16BIT, {0x316E}, 0x869A}, + {OV8830_16BIT, {0x3170}, 0x210E}, + {OV8830_16BIT, {0x317A}, 0x010E}, + {OV8830_16BIT, {0x31E0}, 0x1FB9}, + {OV8830_16BIT, {0x31E6}, 0x07FC}, + {OV8830_16BIT, {0x37C0}, 0x0000}, + {OV8830_16BIT, {0x37C2}, 0x0000}, + {OV8830_16BIT, {0x37C4}, 0x0000}, + {OV8830_16BIT, {0x37C6}, 0x0000}, + {OV8830_16BIT, {0x3E00}, 0x0011}, + {OV8830_16BIT, {0x3E02}, 0x8801}, + {OV8830_16BIT, {0x3E04}, 0x2801}, + {OV8830_16BIT, {0x3E06}, 0x8449}, + {OV8830_16BIT, {0x3E08}, 0x6841}, + {OV8830_16BIT, {0x3E0A}, 0x400C}, + {OV8830_16BIT, {0x3E0C}, 0x1001}, + {OV8830_16BIT, {0x3E0E}, 0x2603}, + {OV8830_16BIT, {0x3E10}, 0x4B41}, + {OV8830_16BIT, {0x3E12}, 0x4B24}, + {OV8830_16BIT, {0x3E14}, 0xA3CF}, + {OV8830_16BIT, {0x3E16}, 0x8802}, + {OV8830_16BIT, {0x3E18}, 0x84FF}, + {OV8830_16BIT, {0x3E1A}, 0x8601}, + {OV8830_16BIT, {0x3E1C}, 0x8401}, + {OV8830_16BIT, {0x3E1E}, 0x840A}, + {OV8830_16BIT, {0x3E20}, 0xFF00}, + {OV8830_16BIT, {0x3E22}, 0x8401}, + {OV8830_16BIT, {0x3E24}, 0x00FF}, + {OV8830_16BIT, {0x3E26}, 0x0088}, + {OV8830_16BIT, {0x3E28}, 0x2E8A}, + {OV8830_16BIT, {0x3E30}, 0x0000}, + {OV8830_16BIT, {0x3E32}, 0x8801}, + {OV8830_16BIT, {0x3E34}, 0x4029}, + {OV8830_16BIT, {0x3E36}, 0x00FF}, + {OV8830_16BIT, {0x3E38}, 0x8469}, + {OV8830_16BIT, {0x3E3A}, 0x00FF}, + {OV8830_16BIT, {0x3E3C}, 0x2801}, + {OV8830_16BIT, {0x3E3E}, 0x3E2A}, + {OV8830_16BIT, {0x3E40}, 0x1C01}, + {OV8830_16BIT, {0x3E42}, 0xFF84}, + {OV8830_16BIT, {0x3E44}, 0x8401}, + {OV8830_16BIT, {0x3E46}, 0x0C01}, + {OV8830_16BIT, {0x3E48}, 0x8401}, + {OV8830_16BIT, {0x3E4A}, 0x00FF}, + {OV8830_16BIT, {0x3E4C}, 0x8402}, + {OV8830_16BIT, {0x3E4E}, 0x8984}, + {OV8830_16BIT, {0x3E50}, 0x6628}, + {OV8830_16BIT, {0x3E52}, 0x8340}, + {OV8830_16BIT, {0x3E54}, 0x00FF}, + {OV8830_16BIT, {0x3E56}, 0x4A42}, + {OV8830_16BIT, {0x3E58}, 0x2703}, + {OV8830_16BIT, {0x3E5A}, 0x6752}, + {OV8830_16BIT, {0x3E5C}, 0x3F2A}, + {OV8830_16BIT, {0x3E5E}, 0x846A}, + {OV8830_16BIT, {0x3E60}, 0x4C01}, + {OV8830_16BIT, {0x3E62}, 0x8401}, + {OV8830_16BIT, {0x3E66}, 0x3901}, + {OV8830_16BIT, {0x3E90}, 0x2C01}, + {OV8830_16BIT, {0x3E98}, 0x2B02}, + {OV8830_16BIT, {0x3E92}, 0x2A04}, + {OV8830_16BIT, {0x3E94}, 0x2509}, + {OV8830_16BIT, {0x3E96}, 0x0000}, + {OV8830_16BIT, {0x3E9A}, 0x2905}, + {OV8830_16BIT, {0x3E9C}, 0x00FF}, + {OV8830_16BIT, {0x3ECC}, 0x00EB}, + {OV8830_16BIT, {0x3ED0}, 0x1E24}, + {OV8830_16BIT, {0x3ED4}, 0xAFC4}, + {OV8830_16BIT, {0x3ED6}, 0x909B}, + {OV8830_16BIT, {0x3EE0}, 0x2424}, + {OV8830_16BIT, {0x3EE2}, 0x9797}, + {OV8830_16BIT, {0x3EE4}, 0xC100}, + {OV8830_16BIT, {0x3EE6}, 0x0540}, + {OV8830_16BIT, {0x3174}, 0x8000}, + {OV8830_TOK_TERM, {0}, 0} +}; + +static const struct ov8830_reg ov8830_pll_timing[] = { + /* pixelrate into the isp = 153.600.000 Hz */ + {OV8830_16BIT, {0x0300}, 0x0004 }, /* vt_pix_clk_div= 4 internal pixel clock freq = 192.000.000 Hz */ + {OV8830_16BIT, {0x0302}, 0x0001 }, /* vt_sys_clk_div= 1 */ + {OV8830_16BIT, {0x0304}, 0x0002 }, /* pre_pll_clk_div= 2 PLL input clock freq = 9.600.000 Hz */ + {OV8830_16BIT, {0x0306}, 0x0050 }, /* pll_multiplier= 80 mipi bus speed = 768.000.000 Hz */ + {OV8830_16BIT, {0x0308}, 0x000A }, /* op_pix_clk_div= 10 output pixel clock freq = 76.800.000 Hz */ + {OV8830_16BIT, {0x030A}, 0x0001 }, /* op_sys_clk_div= 1 */ + {OV8830_16BIT, {0x3016}, 0x111 }, /* row_speed= 273 */ + {OV8830_TOK_DELAY, {0}, 1}, + {OV8830_TOK_TERM, {0}, 0} +}; + + +/*2-lane MIPI Interface Configuration*/ +static const struct ov8830_reg ov8830_mipi_config[] = { + {OV8830_16BIT+OV8830_RMW, {0x3064}, 0x0100, 0x0000}, + {OV8830_16BIT, {0x31AE}, 0x0202}, + {OV8830_16BIT, {0x31B8}, 0x03EF}, + /*{OV8830_16BIT, {0x31B8}, 0x2FEF}, */ + {OV8830_TOK_DELAY, {0}, 5}, + {OV8830_TOK_TERM, {0}, 0} +}; + +/* MIPI Timing Settings */ +static const struct ov8830_reg ov8830_mipi_timing[] = { + {OV8830_16BIT, {0x31B0}, 0x0083}, + {OV8830_16BIT, {0x31B2}, 0x004D}, + {OV8830_16BIT, {0x31B4}, 0x0E88}, + {OV8830_16BIT, {0x31B6}, 0x0D24}, + {OV8830_16BIT, {0x31B8}, 0x020E}, + {OV8830_16BIT, {0x31BA}, 0x0710}, + {OV8830_16BIT, {0x31BC}, 0x2A0D}, + {OV8830_16BIT, {0x31BE}, 0xC007}, + {OV8830_TOK_DELAY, {0}, 5}, + {OV8830_TOK_TERM, {0}, 0} +}; + +/* Start Streaming + * reset_register_restart_bad = 1 + * reset_register_mask_bad = 1 + * reset_register_lock_reg = 1 + * grouped_parameter_hold = 0 + * reset_register_stream = 1 */ + +static const struct ov8830_reg ov8830_start_streaming[] = { + {OV8830_16BIT+OV8830_RMW, {0x301A}, 0x0200, 0x1}, + {OV8830_16BIT+OV8830_RMW, {0x301A}, 0x0400, 0x1}, + {OV8830_16BIT+OV8830_RMW, {0x301A}, 0x8, 0x1}, + {OV8830_16BIT, {0x0104}, 0x0}, + {OV8830_16BIT+OV8830_RMW, {0x301A}, 0x4, 0x1}, + {OV8830_TOK_TERM, {0}, 0} +}; + +#define GROUPED_PARAMETER_HOLD_ENABLE {OV8830_8BIT, {0x0104}, 0x1} + +#define GROUPED_PARAMETER_HOLD_DISABLE {OV8830_8BIT, {0x0104}, 0x0} + +#define RESET_REGISTER {OV8830_16BIT, {0x301A}, 0x4A18} + +#define INIT_VCM_CONTROL {OV8830_16BIT, {0x30F0}, 0x8004} /* slew_rate[2:0] */ +static const struct ov8830_reg ov8830_init_vcm[] = { + INIT_VCM_CONTROL, /* VCM_CONTROL */ + {OV8830_16BIT, {0x30F2}, 0x0000}, /* VCM_NEW_CODE */ + {OV8830_16BIT, {0x30F4}, 0x0080}, /* VCM_STEP_TIME */ + {OV8830_TOK_TERM, {0}, 0} +}; +static const struct ov8830_reg ov8830_reset_register[] = { + {OV8830_16BIT, {0x301A}, 0x10}, /* Lock_Register, transition to standby at frame end */ + {OV8830_TOK_TERM, {0}, 0} +}; + +static const struct ov8830_reg ov8830_raw_10[] = { + {OV8830_16BIT, {0x0112}, 0x0A0A}, /* CCP_DATA_FORMAT, set to RAW10 mode */ + {OV8830_TOK_TERM, {0}, 0} +}; + +static const struct ov8830_reg ov8830_scaler[] = { + {OV8830_16BIT, {0x0400}, 0x0000}, /* SCALE_MODE: 0:disable */ + {OV8830_16BIT, {0x0404}, 0x0010}, /* SCALE_M = 16 */ + {OV8830_TOK_TERM, {0}, 0} +}; + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +/*****************************still15ok*****************************/ +static struct ov8830_reg const ov8830_STILL_8M_12fps[] = { + /* STILL 8M */ + GROUPED_PARAMETER_HOLD_ENABLE, + /* Output size */ + {OV8830_16BIT, {0x0344}, 0x0000 }, /* X_ADDR_START 0 */ + {OV8830_16BIT, {0x0348}, 0x0CCF }, /* X_ADDR_END 3279 */ + {OV8830_16BIT, {0x0346}, 0x0000 }, /* Y_ADDR_START 0 */ + {OV8830_16BIT, {0x034A}, 0x099F }, /* Y_ADDR_END 2463 */ + {OV8830_16BIT, {0x034C}, 0x0CD0 }, /* X_OUTPUT_SIZE 3280 */ + {OV8830_16BIT, {0x034E}, 0x09A0 }, /* Y_OUTPUT_SIZE 2464 */ + {OV8830_16BIT, {0x3040}, 0x0041 }, /* READ_MODE 65 */ + /* Timing Configuation */ + {OV8830_16BIT, {0x0342}, 0x17F8 }, /* line_length_pck 6136 */ + {OV8830_16BIT, {0x0340}, 0x0A2F }, /* frame_length_lines 2607 */ + {OV8830_16BIT, {0x0202}, 0x0A2F }, /* COARSE_INTEGRATION_TIME 2607 */ + {OV8830_16BIT, {0x3014}, 0x0D77 }, /* FINE_INTEGRATION_TIME 3348 */ + {OV8830_16BIT, {0x3010}, 0x0078 }, /* FINE_CORRECTION 120 */ + /* scaler */ + {OV8830_16BIT, {0x0400}, 0x0 }, /* SCALE_MODE 0 */ + {OV8830_16BIT, {0x0404}, 0x10 }, /* SCALE_M 16 */ + {OV8830_TOK_TERM, {0}, 0} +}; + +static struct ov8830_reg const ov8830_STILL_6M_15fps[] = { + /* STILL 6M */ + GROUPED_PARAMETER_HOLD_ENABLE, + /* Output size */ + {OV8830_16BIT, {0x0344}, 0x0000 }, /* X_ADDR_START 0 */ + {OV8830_16BIT, {0x0348}, 0x0CCF }, /* X_ADDR_END 3279 */ + {OV8830_16BIT, {0x0346}, 0x0134 }, /* Y_ADDR_START 308 */ + {OV8830_16BIT, {0x034A}, 0x086D }, /* Y_ADDR_END 2157 */ + {OV8830_16BIT, {0x034C}, 0x0CD0 }, /* X_OUTPUT_SIZE 3280 */ + {OV8830_16BIT, {0x034E}, 0x0738 }, /* Y_OUTPUT_SIZE 1848 */ + {OV8830_16BIT, {0x3040}, 0x0041 }, /* READ_MODE 65 */ + /* Timing Configuation */ + {OV8830_16BIT, {0x0342}, 0x191C }, /* line_length_pck 6428 */ + {OV8830_16BIT, {0x0340}, 0x07C7 }, /* frame_length_lines 1991 */ + {OV8830_16BIT, {0x0202}, 0x07C7 }, /* COARSE_INTEGRATION_TIME 1991 */ + {OV8830_16BIT, {0x3014}, 0x073C }, /* FINE_INTEGRATION_TIME 1852 */ + {OV8830_16BIT, {0x3010}, 0x0078 }, /* FINE_CORRECTION 120 */ + /* scaler */ + {OV8830_16BIT, {0x0400}, 0x0 }, /* SCALE_MODE 0 */ + {OV8830_16BIT, {0x0404}, 0x10 }, /* SCALE_M 16 */ + {OV8830_TOK_TERM, {0}, 0} +}; + +static struct ov8830_reg const ov8830_STILL_2M_15fps[] = { + /* STILL 2M */ + GROUPED_PARAMETER_HOLD_ENABLE, + /* Output size */ + {OV8830_16BIT, {0x0344}, 0x0000 }, /* X_ADDR_START 0 */ + {OV8830_16BIT, {0x0348}, 0x0CD1 }, /* X_ADDR_END 3281 */ + {OV8830_16BIT, {0x0346}, 0x0000 }, /* Y_ADDR_START 0 */ + {OV8830_16BIT, {0x034A}, 0x09A1 }, /* Y_ADDR_END 2465 */ + {OV8830_16BIT, {0x034C}, 0x0668 }, /* X_OUTPUT_SIZE 1640 */ + {OV8830_16BIT, {0x034E}, 0x04D0 }, /* Y_OUTPUT_SIZE 1232 */ + {OV8830_16BIT, {0x3040}, 0x04C3 }, /* READ_MODE 1219 */ + /* Timing Configuation */ + {OV8830_16BIT, {0x0342}, 0x2460 }, /* line_length_pck 9312 */ + {OV8830_16BIT, {0x0340}, 0x0563 }, /* frame_length_lines 1379 */ + {OV8830_16BIT, {0x0202}, 0x055F }, /* COARSE_INTEGRATION_TIME 1375 */ + {OV8830_16BIT, {0x3014}, 0x0846 }, /* FINE_INTEGRATION_TIME 2118 */ + {OV8830_16BIT, {0x3010}, 0x0130 }, /* FINE_CORRECTION 304 */ + /* scaler */ + {OV8830_16BIT, {0x0400}, 0x0 }, /* SCALE_MODE 0 */ + {OV8830_16BIT, {0x0404}, 0x10 }, /* SCALE_M 16 */ + {OV8830_TOK_TERM, {0}, 0} +}; + +/*****************************preview30ok********************************/ +static struct ov8830_reg const ov8830_PREVIEW_30fps[] = { + /* PREVIEW */ + GROUPED_PARAMETER_HOLD_ENABLE, + /* Output size */ + {OV8830_16BIT, {0x0344}, 0x0000 }, /* X_ADDR_START 0 */ + {OV8830_16BIT, {0x0348}, 0x0CC9 }, /* X_ADDR_END 3273 */ + {OV8830_16BIT, {0x0346}, 0x0000 }, /* Y_ADDR_START 0 */ + {OV8830_16BIT, {0x034A}, 0x0999 }, /* Y_ADDR_END 2457 */ + {OV8830_16BIT, {0x034C}, 0x0334 }, /* X_OUTPUT_SIZE 820 */ + {OV8830_16BIT, {0x034E}, 0x0268 }, /* Y_OUTPUT_SIZE 616 */ + {OV8830_16BIT, {0x3040}, 0x05C7 }, /* READ_MODE 5575 */ + /* Timing Configuation */ + {OV8830_16BIT, {0x0342}, 0x20F0 }, /* line_length_pck 8432 */ + {OV8830_16BIT, {0x0340}, 0x02F7 }, /* frame_length_lines 759 */ + {OV8830_16BIT, {0x0202}, 0x02F7 }, /* COARSE_INTEGRATION_TIME 759 */ + {OV8830_16BIT, {0x3014}, 0x0846 }, /* FINE_INTEGRATION_TIME 2118 */ + {OV8830_16BIT, {0x3010}, 0x0130 }, /* FINE_CORRECTION 304 */ + /* scaler */ + {OV8830_16BIT, {0x0400}, 0x0 }, /* SCALE_MODE 0 */ + {OV8830_16BIT, {0x0404}, 0x10 }, /* SCALE_M 16 */ + {OV8830_TOK_TERM, {0}, 0} +}; + +static struct ov8830_reg const ov8830_WIDE_PREVIEW_30fps[] = { + /* WIDE PREVIEW */ + GROUPED_PARAMETER_HOLD_ENABLE, + /* Output size */ + {OV8830_16BIT, {0x0344}, 0x0000 }, /* X_ADDR_START 0 */ + {OV8830_16BIT, {0x0348}, 0x0CD1 }, /* X_ADDR_END 3281 */ + {OV8830_16BIT, {0x0346}, 0x0114 }, /* Y_ADDR_START 276 */ + {OV8830_16BIT, {0x034A}, 0x088D }, /* Y_ADDR_END 2189 */ + {OV8830_16BIT, {0x034C}, 0x0668 }, /* X_OUTPUT_SIZE 1640 */ + {OV8830_16BIT, {0x034E}, 0x03BC }, /* Y_OUTPUT_SIZE 956 */ + {OV8830_16BIT, {0x3040}, 0x04C3 }, /* READ_MODE 1219 */ + /* Timing Configuation */ + {OV8830_16BIT, {0x0342}, 0x16C2 }, /* line_length_pck 5826 */ + {OV8830_16BIT, {0x0340}, 0x044F }, /* frame_length_lines 1103 */ + {OV8830_16BIT, {0x0202}, 0x044B }, /* COARSE_INTEGRATION_TIME 1099 */ + {OV8830_16BIT, {0x3014}, 0x0846 }, /* FINE_INTEGRATION_TIME 2118 */ + {OV8830_16BIT, {0x3010}, 0x0130 }, /* FINE_CORRECTION 304 */ + /* scaler */ + {OV8830_16BIT, {0x0400}, 0x0 }, /* SCALE_MODE 0 */ + {OV8830_16BIT, {0x0404}, 0x10 }, /* SCALE_M 16 */ + {OV8830_TOK_TERM, {0}, 0} +}; + +/*****************************video************************/ +static struct ov8830_reg const ov8830_1080p_strong_dvs_30fps[] = { + /* 1080p strong dvs */ + GROUPED_PARAMETER_HOLD_ENABLE, + /* Output size */ + {OV8830_16BIT, {0x0344}, 0x01D8 }, /* X_ADDR_START 472 */ + {OV8830_16BIT, {0x0348}, 0x0AF7 }, /* X_ADDR_END 2807 */ + {OV8830_16BIT, {0x0346}, 0x0242 }, /* Y_ADDR_START 578 */ + {OV8830_16BIT, {0x034A}, 0x075D }, /* Y_ADDR_END 1885 */ + {OV8830_16BIT, {0x034C}, 0x0920 }, /* X_OUTPUT_SIZE 2336 */ + {OV8830_16BIT, {0x034E}, 0x051C }, /* Y_OUTPUT_SIZE 1308 */ + {OV8830_16BIT, {0x3040}, 0x0041 }, /* READ_MODE 65 */ + /* Timing Configuation */ + {OV8830_16BIT, {0x0342}, 0x113A }, /* line_length_pck 4410 */ + {OV8830_16BIT, {0x0340}, 0x05AB }, /* frame_length_lines 1451 */ + {OV8830_16BIT, {0x0202}, 0x05AB }, /* COARSE_INTEGRATION_TIME 1451 */ + {OV8830_16BIT, {0x3014}, 0x0442 }, /* FINE_INTEGRATION_TIME 1090 */ + {OV8830_16BIT, {0x3010}, 0x0078 }, /* FINE_CORRECTION 120 */ + /* scaler */ + {OV8830_16BIT, {0x0400}, 0x0 }, /* SCALE_MODE 0 */ + {OV8830_16BIT, {0x0404}, 0x10 }, /* SCALE_M 16 */ + {OV8830_TOK_TERM, {0}, 0} +}; + +static struct ov8830_reg const ov8830_720p_strong_dvs_30fps[] = { + /* 720p strong dvs */ + GROUPED_PARAMETER_HOLD_ENABLE, + /* Output size */ + {OV8830_16BIT, {0x0344}, 0x0048 }, /* X_ADDR_START 72 */ + {OV8830_16BIT, {0x0348}, 0x0C89 }, /* X_ADDR_END 3209 */ + {OV8830_16BIT, {0x0346}, 0x0164 }, /* Y_ADDR_START 356 */ + {OV8830_16BIT, {0x034A}, 0x083D }, /* Y_ADDR_END 2109 */ + {OV8830_16BIT, {0x034C}, 0x0620 }, /* X_OUTPUT_SIZE 1568 */ + {OV8830_16BIT, {0x034E}, 0x036C }, /* Y_OUTPUT_SIZE 876 */ + {OV8830_16BIT, {0x3040}, 0x04C3 }, /* READ_MODE 5315 */ + /* Timing Configuation */ + {OV8830_16BIT, {0x0342}, 0x188C }, /* line_length_pck 6284 */ + {OV8830_16BIT, {0x0340}, 0x03FF }, /* frame_length_lines 1023 */ + {OV8830_16BIT, {0x0202}, 0x03FB }, /* COARSE_INTEGRATION_TIME 1019 */ + {OV8830_16BIT, {0x3014}, 0x0846 }, /* FINE_INTEGRATION_TIME 2118 */ + {OV8830_16BIT, {0x3010}, 0x0130 }, /* FINE_CORRECTION 304 */ + /* scaler */ + {OV8830_16BIT, {0x0400}, 0x2 }, /* SCALE_MODE 2 */ + {OV8830_16BIT, {0x0404}, 0x10 }, /* SCALE_M 16 */ + {OV8830_TOK_TERM, {0}, 0} +}; + +static struct ov8830_reg const ov8830_WVGA_strong_dvs_30fps[] = { + /* WVGA strong dvs */ + GROUPED_PARAMETER_HOLD_ENABLE, + /* Output size */ + {OV8830_16BIT, {0x0344}, 0x0000 }, /* X_ADDR_START 0 */ + {OV8830_16BIT, {0x0348}, 0x0CCD }, /* X_ADDR_END 3281 */ + {OV8830_16BIT, {0x0346}, 0x00D0 }, /* Y_ADDR_START 208 */ + {OV8830_16BIT, {0x034A}, 0x08CD }, /* Y_ADDR_END 2253 */ + {OV8830_16BIT, {0x034C}, 0x0668 }, /* X_OUTPUT_SIZE 1640 */ + {OV8830_16BIT, {0x034E}, 0x0400 }, /* Y_OUTPUT_SIZE 1024 */ + {OV8830_16BIT, {0x3040}, 0x04C3 }, /* READ_MODE 195 */ + /* Timing Configuation */ + {OV8830_16BIT, {0x0342}, 0x156C }, /* line_length_pck 5484 */ + {OV8830_16BIT, {0x0340}, 0x048F }, /* frame_length_lines 1167 */ + {OV8830_16BIT, {0x0202}, 0x048F }, /* COARSE_INTEGRATION_TIME 1167 */ + {OV8830_16BIT, {0x3014}, 0x0846 }, /* FINE_INTEGRATION_TIME 2118 */ + {OV8830_16BIT, {0x3010}, 0x0130 }, /* FINE_CORRECTION 304 */ + /* scaler */ + {OV8830_16BIT, {0x0400}, 0x0 }, /* SCALE_MODE 0 */ + {OV8830_16BIT, {0x0404}, 0x10 }, /* SCALE_M 16 */ + {OV8830_TOK_TERM, {0}, 0} +}; + +static struct ov8830_reg const ov8830_VGA_strong_dvs_30fps[] = { + /* VGA strong dvs */ + GROUPED_PARAMETER_HOLD_ENABLE, + /* Output size */ + {OV8830_16BIT, {0x0344}, 0x0000 }, /* X_ADDR_START 0 */ + {OV8830_16BIT, {0x0348}, 0x0CC9 }, /* X_ADDR_END 3273 */ + {OV8830_16BIT, {0x0346}, 0x0000 }, /* Y_ADDR_START 0 */ + {OV8830_16BIT, {0x034A}, 0x0999 }, /* Y_ADDR_END 2457 */ + {OV8830_16BIT, {0x034C}, 0x0334 }, /* X_OUTPUT_SIZE 820 */ + {OV8830_16BIT, {0x034E}, 0x0268 }, /* Y_OUTPUT_SIZE 616 */ + {OV8830_16BIT, {0x3040}, 0x05C7 }, /* READ_MODE 1479 */ + /* Timing Configuation */ + {OV8830_16BIT, {0x0342}, 0x20F0 }, /* line_length_pck 8432 */ + {OV8830_16BIT, {0x0340}, 0x02F7 }, /* frame_length_lines 759 */ + {OV8830_16BIT, {0x0202}, 0x02F7 }, /* COARSE_INTEGRATION_TIME 759 */ + {OV8830_16BIT, {0x3014}, 0x0846 }, /* FINE_INTEGRATION_TIME 2118 */ + {OV8830_16BIT, {0x3010}, 0x0130 }, /* FINE_CORRECTION 304 */ + /* scaler */ + {OV8830_16BIT, {0x0400}, 0x0 }, /* SCALE_MODE 0 */ + {OV8830_16BIT, {0x0404}, 0x10 }, /* SCALE_M 16 */ + {OV8830_TOK_TERM, {0}, 0} +}; + +#if 0 +static struct ov8830_reg const ov8830_QVGA_strong_dvs_30fps[] = { + /* QVGA strong dvs */ + GROUPED_PARAMETER_HOLD_ENABLE, + /* Output size */ + {OV8830_16BIT, {0x0344}, 0x0000 }, /* X_ADDR_START 0 */ + {OV8830_16BIT, {0x0348}, 0x0CC9 }, /* X_ADDR_END 3273 */ + {OV8830_16BIT, {0x0346}, 0x0000 }, /* Y_ADDR_START 0 */ + {OV8830_16BIT, {0x034A}, 0x0999 }, /* Y_ADDR_END 2457 */ + {OV8830_16BIT, {0x034C}, 0x019A }, /* X_OUTPUT_SIZE 410 */ + {OV8830_16BIT, {0x034E}, 0x0134 }, /* Y_OUTPUT_SIZE 308 */ + {OV8830_16BIT, {0x3040}, 0x05C7 }, /* READ_MODE 1479 */ + /* Timing Configuation */ + {OV8830_16BIT, {0x0342}, 0x20F0 }, /* line_length_pck 8432 */ + {OV8830_16BIT, {0x0340}, 0x02F7 }, /* frame_length_lines 759 */ + {OV8830_16BIT, {0x0202}, 0x02F7 }, /* COARSE_INTEGRATION_TIME 759 */ + {OV8830_16BIT, {0x3014}, 0x0846 }, /* FINE_INTEGRATION_TIME 2118 */ + {OV8830_16BIT, {0x3010}, 0x0130 }, /* FINE_CORRECTION 304 */ + /* scaler */ + {OV8830_16BIT, {0x0400}, 0x2 }, /* SCALE_MODE 0 */ + {OV8830_16BIT, {0x0404}, 0x20 }, /* SCALE_M 32 */ + {OV8830_TOK_TERM, {0}, 0} +}; + + +static struct ov8830_reg const ov8830_QCIF_strong_dvs_30fps[] = { + /* QCIF strong dvs */ + GROUPED_PARAMETER_HOLD_ENABLE, + /* Output size */ + {OV8830_16BIT, {0x0344}, 0x0000 }, /* X_ADDR_START 0 */ + {OV8830_16BIT, {0x0348}, 0x0CC9 }, /* X_ADDR_END 3273 */ + {OV8830_16BIT, {0x0346}, 0x0000 }, /* Y_ADDR_START 0 */ + {OV8830_16BIT, {0x034A}, 0x0999 }, /* Y_ADDR_END 2457 */ + {OV8830_16BIT, {0x034C}, 0x019A }, /* X_OUTPUT_SIZE 205 */ + {OV8830_16BIT, {0x034E}, 0x0134 }, /* Y_OUTPUT_SIZE 154 */ + {OV8830_16BIT, {0x3040}, 0x05C7 }, /* READ_MODE 1479 */ + /* Timing Configuation */ + {OV8830_16BIT, {0x0342}, 0x20F0 }, /* line_length_pck 8432 */ + {OV8830_16BIT, {0x0340}, 0x02F7 }, /* frame_length_lines 759 */ + {OV8830_16BIT, {0x0202}, 0x02F7 }, /* COARSE_INTEGRATION_TIME 759 */ + {OV8830_16BIT, {0x3014}, 0x0846 }, /* FINE_INTEGRATION_TIME 2118 */ + {OV8830_16BIT, {0x3010}, 0x0130 }, /* FINE_CORRECTION 304 */ + /* scaler */ + {OV8830_16BIT, {0x0400}, 0x2 }, /* SCALE_MODE 0 */ + {OV8830_16BIT, {0x0404}, 0x40 }, /* SCALE_M 64 */ + {OV8830_TOK_TERM, {0}, 0} +}; +#endif + +static struct ov8830_reg const ov8830_QVGA_strong_dvs_30fps[] = { + /* QVGA strong dvs */ + GROUPED_PARAMETER_HOLD_ENABLE, + /* Output size */ + {OV8830_16BIT, {0x0344}, 0x0368 }, /* X_ADDR_START 872 */ + {OV8830_16BIT, {0x0348}, 0x0961 }, /* X_ADDR_END 2401 */ + {OV8830_16BIT, {0x0346}, 0x0290 }, /* Y_ADDR_START 656 */ + {OV8830_16BIT, {0x034A}, 0x0709 }, /* Y_ADDR_END 1801 */ + {OV8830_16BIT, {0x034C}, 0x0180 }, /* X_OUTPUT_SIZE 384 */ + {OV8830_16BIT, {0x034E}, 0x0120 }, /* Y_OUTPUT_SIZE 288 */ + {OV8830_16BIT, {0x3040}, 0x05C7 }, /* READ_MODE 1479 */ + /* Timing Configuation */ + {OV8830_16BIT, {0x0342}, 0x3A00 }, /* line_length_pck 14848 */ + {OV8830_16BIT, {0x0340}, 0x01AF }, /* frame_length_lines 431 */ + {OV8830_16BIT, {0x0202}, 0x01AF }, /* COARSE_INTEGRATION_TIME 431 */ + {OV8830_16BIT, {0x3014}, 0x0846 }, /* FINE_INTEGRATION_TIME 2118 */ + {OV8830_16BIT, {0x3010}, 0x0130 }, /* FINE_CORRECTION 304 */ + /* scaler */ + {OV8830_16BIT, {0x0400}, 0x0 }, /* SCALE_MODE 0 */ + {OV8830_16BIT, {0x0404}, 0x40 }, /* SCALE_M 16 */ + {OV8830_TOK_TERM, {0}, 0} +}; + +static struct ov8830_reg const ov8830_QCIF_strong_dvs_30fps[] = { + /* QCIF strong dvs */ + GROUPED_PARAMETER_HOLD_ENABLE, + /* Output size */ + {OV8830_16BIT, {0x0344}, 0x0368 }, /* X_ADDR_START 872 */ + {OV8830_16BIT, {0x0348}, 0x0961 }, /* X_ADDR_END 2401 */ + {OV8830_16BIT, {0x0346}, 0x0288 }, /* Y_ADDR_START 648 */ + {OV8830_16BIT, {0x034A}, 0x0711 }, /* Y_ADDR_END 1809 */ + {OV8830_16BIT, {0x034C}, 0x0180 }, /* X_OUTPUT_SIZE 384 */ + {OV8830_16BIT, {0x034E}, 0x0124 }, /* Y_OUTPUT_SIZE 292 */ + {OV8830_16BIT, {0x3040}, 0x05C7 }, /* READ_MODE 1479 */ + /* Timing Configuation */ + {OV8830_16BIT, {0x0342}, 0x3978 }, /* line_length_pck 14712 */ + {OV8830_16BIT, {0x0340}, 0x01B3 }, /* frame_length_lines 435 */ + {OV8830_16BIT, {0x0202}, 0x01B3 }, /* COARSE_INTEGRATION_TIME 435 */ + {OV8830_16BIT, {0x3014}, 0x0846 }, /* FINE_INTEGRATION_TIME 2118 */ + {OV8830_16BIT, {0x3010}, 0x0130 }, /* FINE_CORRECTION 304 */ + /* scaler */ + {OV8830_16BIT, {0x0400}, 0x0 }, /* SCALE_MODE 0 */ + {OV8830_16BIT, {0x0404}, 0x10 }, /* SCALE_M 16 */ + {OV8830_TOK_TERM, {0}, 0} +}; + + + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +static const struct ov8830_reg ov8830_soft_standby[] = { + {OV8830_8BIT, {0x301C}, 0x00}, + {OV8830_TOK_TERM, {0}, 0} +}; + +static const struct ov8830_reg ov8830_streaming[] = { + {OV8830_8BIT, {0x301C}, 0x01}, + {OV8830_TOK_TERM, {0}, 0} +}; + +static const struct ov8830_reg ov8830_param_hold[] = { + {OV8830_8BIT, {0x0104}, 0x01}, /* GROUPED_PARAMETER_HOLD */ + {OV8830_TOK_TERM, {0}, 0} +}; + +static const struct ov8830_reg ov8830_param_update[] = { + {OV8830_8BIT, {0x0104}, 0x00}, /* GROUPED_PARAMETER_HOLD */ + {OV8830_TOK_TERM, {0}, 0} +}; + +static const struct ov8830_reg ov8830_lens_shading[] = { + {OV8830_16BIT | OV8830_RMW, {0x3780}, 0x8000, 0}, /* POLY_SC_ENABLE */ + {OV8830_16BIT, {0x3600}, 0x0430}, /* P_GR_P0Q0 */ + {OV8830_16BIT, {0x3602}, 0x1BEE}, /* P_GR_P0Q1 */ + {OV8830_16BIT, {0x3604}, 0x39F0}, /* P_GR_P0Q2 */ + {OV8830_16BIT, {0x3606}, 0xC7AD}, /* P_GR_P0Q3 */ + {OV8830_16BIT, {0x3608}, 0xC390}, /* P_GR_P0Q4 */ + {OV8830_16BIT, {0x360A}, 0x03D0}, /* P_RD_P0Q0 */ + {OV8830_16BIT, {0x360C}, 0xA0CE}, /* P_RD_P0Q1 */ + {OV8830_16BIT, {0x360E}, 0x2850}, /* P_RD_P0Q2 */ + {OV8830_16BIT, {0x3610}, 0x6A0E}, /* P_RD_P0Q3 */ + {OV8830_16BIT, {0x3612}, 0xAF30}, /* P_RD_P0Q4 */ + {OV8830_16BIT, {0x3614}, 0x03D0}, /* P_BL_P0Q0 */ + {OV8830_16BIT, {0x3616}, 0x36AE}, /* P_BL_P0Q1 */ + {OV8830_16BIT, {0x3618}, 0x5E6F}, /* P_BL_P0Q2 */ + {OV8830_16BIT, {0x361A}, 0xA22E}, /* P_BL_P0Q3 */ + {OV8830_16BIT, {0x361C}, 0xF6EF}, /* P_BL_P0Q4 */ + {OV8830_16BIT, {0x361E}, 0x02F0}, /* P_GB_P0Q0 */ + {OV8830_16BIT, {0x3620}, 0xA00E}, /* P_GB_P0Q1 */ + {OV8830_16BIT, {0x3622}, 0x3CD0}, /* P_GB_P0Q2 */ + {OV8830_16BIT, {0x3624}, 0x530E}, /* P_GB_P0Q3 */ + {OV8830_16BIT, {0x3626}, 0xCEF0}, /* P_GB_P0Q4 */ + {OV8830_16BIT, {0x3640}, 0xAB2D}, /* P_GR_P1Q0 */ + {OV8830_16BIT, {0x3642}, 0xB72E}, /* P_GR_P1Q1 */ + {OV8830_16BIT, {0x3644}, 0x988D}, /* P_GR_P1Q2 */ + {OV8830_16BIT, {0x3646}, 0x6E2E}, /* P_GR_P1Q3 */ + {OV8830_16BIT, {0x3648}, 0x53EE}, /* P_GR_P1Q4 */ + {OV8830_16BIT, {0x364A}, 0xDA2C}, /* P_RD_P1Q0 */ + {OV8830_16BIT, {0x364C}, 0x3E8D}, /* P_RD_P1Q1 */ + {OV8830_16BIT, {0x364E}, 0xAFAD}, /* P_RD_P1Q2 */ + {OV8830_16BIT, {0x3650}, 0x874E}, /* P_RD_P1Q3 */ + {OV8830_16BIT, {0x3652}, 0x5B4E}, /* P_RD_P1Q4 */ + {OV8830_16BIT, {0x3654}, 0x740D}, /* P_BL_P1Q0 */ + {OV8830_16BIT, {0x3656}, 0x310E}, /* P_BL_P1Q1 */ + {OV8830_16BIT, {0x3658}, 0x280B}, /* P_BL_P1Q2 */ + {OV8830_16BIT, {0x365A}, 0xE06E}, /* P_BL_P1Q3 */ + {OV8830_16BIT, {0x365C}, 0xEA0D}, /* P_BL_P1Q4 */ + {OV8830_16BIT, {0x365E}, 0x182D}, /* P_GB_P1Q0 */ + {OV8830_16BIT, {0x3660}, 0xAD0E}, /* P_GB_P1Q1 */ + {OV8830_16BIT, {0x3662}, 0x032E}, /* P_GB_P1Q2 */ + {OV8830_16BIT, {0x3664}, 0x7EEE}, /* P_GB_P1Q3 */ + {OV8830_16BIT, {0x3666}, 0xF34E}, /* P_GB_P1Q4 */ + {OV8830_16BIT, {0x3680}, 0x0E31}, /* P_GR_P2Q0 */ + {OV8830_16BIT, {0x3682}, 0x104F}, /* P_GR_P2Q1 */ + {OV8830_16BIT, {0x3684}, 0x92D3}, /* P_GR_P2Q2 */ + {OV8830_16BIT, {0x3686}, 0xA030}, /* P_GR_P2Q3 */ + {OV8830_16BIT, {0x3688}, 0x3873}, /* P_GR_P2Q4 */ + {OV8830_16BIT, {0x368A}, 0x1971}, /* P_RD_P2Q0 */ + {OV8830_16BIT, {0x368C}, 0x750C}, /* P_RD_P2Q1 */ + {OV8830_16BIT, {0x368E}, 0xFFF2}, /* P_RD_P2Q2 */ + {OV8830_16BIT, {0x3690}, 0xEDAF}, /* P_RD_P2Q3 */ + {OV8830_16BIT, {0x3692}, 0x1D73}, /* P_RD_P2Q4 */ + {OV8830_16BIT, {0x3694}, 0x0031}, /* P_BL_P2Q0 */ + {OV8830_16BIT, {0x3696}, 0x1A2F}, /* P_BL_P2Q1 */ + {OV8830_16BIT, {0x3698}, 0xF792}, /* P_BL_P2Q2 */ + {OV8830_16BIT, {0x369A}, 0x8530}, /* P_BL_P2Q3 */ + {OV8830_16BIT, {0x369C}, 0x1F73}, /* P_BL_P2Q4 */ + {OV8830_16BIT, {0x369E}, 0x08B1}, /* P_GB_P2Q0 */ + {OV8830_16BIT, {0x36A0}, 0x11AE}, /* P_GB_P2Q1 */ + {OV8830_16BIT, {0x36A2}, 0x9093}, /* P_GB_P2Q2 */ + {OV8830_16BIT, {0x36A4}, 0x9030}, /* P_GB_P2Q3 */ + {OV8830_16BIT, {0x36A6}, 0x36D3}, /* P_GB_P2Q4 */ + {OV8830_16BIT, {0x36C0}, 0x5F2D}, /* P_GR_P3Q0 */ + {OV8830_16BIT, {0x36C2}, 0x314F}, /* P_GR_P3Q1 */ + {OV8830_16BIT, {0x36C4}, 0x684E}, /* P_GR_P3Q2 */ + {OV8830_16BIT, {0x36C6}, 0x88B0}, /* P_GR_P3Q3 */ + {OV8830_16BIT, {0x36C8}, 0xDAF0}, /* P_GR_P3Q4 */ + {OV8830_16BIT, {0x36CA}, 0x636E}, /* P_RD_P3Q0 */ + {OV8830_16BIT, {0x36CC}, 0xAD0C}, /* P_RD_P3Q1 */ + {OV8830_16BIT, {0x36CE}, 0xEEEE}, /* P_RD_P3Q2 */ + {OV8830_16BIT, {0x36D0}, 0x500E}, /* P_RD_P3Q3 */ + {OV8830_16BIT, {0x36D2}, 0xDDCE}, /* P_RD_P3Q4 */ + {OV8830_16BIT, {0x36D4}, 0xA3AC}, /* P_BL_P3Q0 */ + {OV8830_16BIT, {0x36D6}, 0xC06E}, /* P_BL_P3Q1 */ + {OV8830_16BIT, {0x36D8}, 0xC04F}, /* P_BL_P3Q2 */ + {OV8830_16BIT, {0x36DA}, 0x49AF}, /* P_BL_P3Q3 */ + {OV8830_16BIT, {0x36DC}, 0x4830}, /* P_BL_P3Q4 */ + {OV8830_16BIT, {0x36DE}, 0x0F6B}, /* P_GB_P3Q0 */ + {OV8830_16BIT, {0x36E0}, 0x1DEF}, /* P_GB_P3Q1 */ + {OV8830_16BIT, {0x36E2}, 0x8730}, /* P_GB_P3Q2 */ + {OV8830_16BIT, {0x36E4}, 0x9E50}, /* P_GB_P3Q3 */ + {OV8830_16BIT, {0x36E6}, 0x7110}, /* P_GB_P3Q4 */ + {OV8830_16BIT, {0x3700}, 0xF4F1}, /* P_GR_P4Q0 */ + {OV8830_16BIT, {0x3702}, 0xF090}, /* P_GR_P4Q1 */ + {OV8830_16BIT, {0x3704}, 0x6493}, /* P_GR_P4Q2 */ + {OV8830_16BIT, {0x3706}, 0x5FB1}, /* P_GR_P4Q3 */ + {OV8830_16BIT, {0x3708}, 0xADB3}, /* P_GR_P4Q4 */ + {OV8830_16BIT, {0x370A}, 0xFEF1}, /* P_RD_P4Q0 */ + {OV8830_16BIT, {0x370C}, 0x134B}, /* P_RD_P4Q1 */ + {OV8830_16BIT, {0x370E}, 0x4D33}, /* P_RD_P4Q2 */ + {OV8830_16BIT, {0x3710}, 0x9B8E}, /* P_RD_P4Q3 */ + {OV8830_16BIT, {0x3712}, 0x88B3}, /* P_RD_P4Q4 */ + {OV8830_16BIT, {0x3714}, 0xEBB1}, /* P_BL_P4Q0 */ + {OV8830_16BIT, {0x3716}, 0x8131}, /* P_BL_P4Q1 */ + {OV8830_16BIT, {0x3718}, 0x5AD3}, /* P_BL_P4Q2 */ + {OV8830_16BIT, {0x371A}, 0x54F1}, /* P_BL_P4Q3 */ + {OV8830_16BIT, {0x371C}, 0xB193}, /* P_BL_P4Q4 */ + {OV8830_16BIT, {0x371E}, 0xE6D1}, /* P_GB_P4Q0 */ + {OV8830_16BIT, {0x3720}, 0xE0EC}, /* P_GB_P4Q1 */ + {OV8830_16BIT, {0x3722}, 0x6033}, /* P_GB_P4Q2 */ + {OV8830_16BIT, {0x3724}, 0x9DCE}, /* P_GB_P4Q3 */ + {OV8830_16BIT, {0x3726}, 0xA453}, /* P_GB_P4Q4 */ + {OV8830_16BIT, {0x3782}, 0x0614}, /* POLY_ORIGIN_C */ + {OV8830_16BIT, {0x3784}, 0x0494}, /* POLY_ORIGIN_R */ + {OV8830_16BIT, {0x37C0}, 0xC40A}, /* P_GR_Q5 */ + {OV8830_16BIT, {0x37C2}, 0xCE6A}, /* P_RD_Q5 */ + {OV8830_16BIT, {0x37C4}, 0xDBAA}, /* P_BL_Q5 */ + {OV8830_16BIT, {0x37C6}, 0xCCEA}, /* P_GB_Q5 */ + + /*STATE= Lens Correction Falloff, 70 */ + {OV8830_16BIT | OV8830_RMW, {0x3780}, 0x8000, 1}, /* POLY_SC_ENABLE */ + {OV8830_TOK_TERM, {0}, 0} +}; + +#endif -- 2.7.4