From 0c7d455e598ee386ffa5ee877d77943a3259f5d6 Mon Sep 17 00:00:00 2001 From: Jake Day Date: Sun, 27 Jan 2019 10:51:49 -0500 Subject: [PATCH 04/11] cameras --- drivers/media/usb/uvc/uvc_driver.c | 40 + drivers/staging/Makefile | 1 + drivers/staging/ov5693/Kconfig | 10 + drivers/staging/ov5693/Makefile | 5 + drivers/staging/ov5693/ad5823.c | 218 +++++ drivers/staging/ov5693/ad5823.h | 90 ++ drivers/staging/ov5693/ov5693.c | 1461 ++++++++++++++++++++++++++++ drivers/staging/ov5693/ov5693.h | 848 ++++++++++++++++ 8 files changed, 2673 insertions(+) create mode 100644 drivers/staging/ov5693/Kconfig create mode 100644 drivers/staging/ov5693/Makefile create mode 100644 drivers/staging/ov5693/ad5823.c create mode 100644 drivers/staging/ov5693/ad5823.h create mode 100644 drivers/staging/ov5693/ov5693.c create mode 100644 drivers/staging/ov5693/ov5693.h diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index d46dc432456c..2552fb6fcfbe 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -2352,6 +2352,46 @@ static const struct uvc_device_info uvc_quirk_force_y8 = { * though they are compliant. */ static const struct usb_device_id uvc_ids[] = { + /* Microsoft Surface Pro 3 Front */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x045e, + .idProduct = 0x07be, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 1 }, + /* Microsoft Surface Pro 3 Rear */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x045e, + .idProduct = 0x07bf, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 1 }, + /* Microsoft Surface Pro 4 Cam */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x045e, + .idProduct = 0x090c, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 1 }, + /* Microsoft Surface Book Cam 1 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x045e, + .idProduct = 0x090b, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 1 }, + /* Microsoft Surface Book Cam 2 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x045e, + .idProduct = 0x091a, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 1 }, /* LogiLink Wireless Webcam */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index ab0cbe8815b1..96c4c669a93f 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -53,3 +53,4 @@ obj-$(CONFIG_SOC_MT7621) += mt7621-dts/ obj-$(CONFIG_STAGING_GASKET_FRAMEWORK) += gasket/ obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo/ obj-$(CONFIG_EROFS_FS) += erofs/ +obj-$(CONFIG_VIDEO_OV5693) += ov5693/ diff --git a/drivers/staging/ov5693/Kconfig b/drivers/staging/ov5693/Kconfig new file mode 100644 index 000000000000..96000f112c4d --- /dev/null +++ b/drivers/staging/ov5693/Kconfig @@ -0,0 +1,10 @@ +config VIDEO_OV5693 + tristate "Omnivision ov5693 sensor support" + depends on I2C && VIDEO_V4L2 + ---help--- + This is a Video4Linux2 sensor-level driver for the Micron + ov5693 5 Mpixel camera. + + ov5693 is video camera sensor. + + It currently only works with the atomisp driver. diff --git a/drivers/staging/ov5693/Makefile b/drivers/staging/ov5693/Makefile new file mode 100644 index 000000000000..d8a63faa591f --- /dev/null +++ b/drivers/staging/ov5693/Makefile @@ -0,0 +1,5 @@ +obj-$(CONFIG_VIDEO_OV5693) += ov569x.o + +ov569x-objs := ov5693.o ad5823.o + +ccflags-y += -Werror diff --git a/drivers/staging/ov5693/ad5823.c b/drivers/staging/ov5693/ad5823.c new file mode 100644 index 000000000000..7c34c36e77e5 --- /dev/null +++ b/drivers/staging/ov5693/ad5823.c @@ -0,0 +1,218 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ad5823.h" + +static struct ad5823_device ad5823_dev; +static int ad5823_i2c_write(struct i2c_client *client, u8 reg, u8 val) +{ + struct i2c_msg msg; + u8 buf[2]; + buf[0] = reg; + buf[1] = val; + msg.addr = AD5823_VCM_ADDR; + msg.flags = 0; + msg.len = AD5823_8BIT; + msg.buf = &buf[0]; + + if (i2c_transfer(client->adapter, &msg, 1) != 1) + return -EIO; + return 0; +} + +static int ad5823_i2c_read(struct i2c_client *client, u8 reg, u8 *val) +{ + struct i2c_msg msg[2]; + u8 buf[2]; + buf[0] = reg; + buf[1] = 0; + + msg[0].addr = AD5823_VCM_ADDR; + msg[0].flags = 0; + msg[0].len = AD5823_8BIT; + msg[0].buf = &buf[0]; + + msg[1].addr = AD5823_VCM_ADDR; + msg[1].flags = I2C_M_RD; + msg[1].len = AD5823_8BIT; + msg[1].buf = &buf[1]; + *val = 0; + if (i2c_transfer(client->adapter, msg, 2) != 2) + return -EIO; + *val = buf[1]; + return 0; +} + +int ad5823_vcm_power_up(struct v4l2_subdev *sd) +{ + int ret = -ENODEV; + + /* Enable power */ + if (ad5823_dev.platform_data) + ret = ad5823_dev.platform_data->power_ctrl(sd, 1); + /* + * waiting time requested by AD5823(vcm) + */ + usleep_range(1000, 2000); + return ret; +} + +int ad5823_vcm_power_down(struct v4l2_subdev *sd) +{ + int ret = -ENODEV; + + if (ad5823_dev.platform_data) + ret = ad5823_dev.platform_data->power_ctrl(sd, 0); + + return ret; +} + + +int ad5823_t_focus_vcm(struct v4l2_subdev *sd, u16 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = -EINVAL; + u8 vcm_code; + u8 vcm_mode_reg_val[4] = { + AD5823_ARC_RES0, + AD5823_ARC_RES1, + AD5823_ARC_RES2, + AD5823_ESRC + }; + + if (ad5823_dev.vcm_mode != AD5823_DIRECT) { + ret = ad5823_i2c_write(client, AD5823_REG_VCM_CODE_MSB, + AD5823_RING_CTRL_ENABLE); + if (ret) + return ret; + + ret = ad5823_i2c_write(client, AD5823_REG_MODE, + vcm_mode_reg_val[ad5823_dev.vcm_mode]); + if (ret) + return ret; + } else { + ret = ad5823_i2c_write(client, AD5823_REG_VCM_CODE_MSB, + AD5823_RING_CTRL_DISABLE); + if (ret) + return ret; + } + + ret = ad5823_i2c_read(client, AD5823_REG_VCM_CODE_MSB, &vcm_code); + if (ret) + return ret; + + /* set reg VCM_CODE_MSB Bit[1:0] */ + vcm_code = (vcm_code & VCM_CODE_MSB_MASK) | ((val >> 8) & ~VCM_CODE_MSB_MASK); + ret = ad5823_i2c_write(client, AD5823_REG_VCM_CODE_MSB, vcm_code); + if (ret) + return ret; + + /* set reg VCM_CODE_LSB Bit[7:0] */ + ret = ad5823_i2c_write(client, AD5823_REG_VCM_CODE_LSB, + (val & 0x0f)); + if (ret) + return ret; + + /* set required vcm move time */ + vcm_code = AD5823_RESONANCE_PERIOD / AD5823_RESONANCE_COEF + - AD5823_HIGH_FREQ_RANGE; + ret = ad5823_i2c_write(client, AD5823_REG_VCM_MOVE_TIME, vcm_code); + + return ret; +} + +int ad5823_t_focus_abs(struct v4l2_subdev *sd, s32 value) +{ + int ret; + + value = min(value, AD5823_MAX_FOCUS_POS); + ret = ad5823_t_focus_vcm(sd, AD5823_MAX_FOCUS_POS - value); + if (ret == 0) { + ad5823_dev.number_of_steps = value - ad5823_dev.focus; + ad5823_dev.focus = value; + ktime_get_ts(&ad5823_dev.timestamp_t_focus_abs); + } + + return ret; +} + +int ad5823_t_focus_rel(struct v4l2_subdev *sd, s32 value) +{ + return ad5823_t_focus_abs(sd, ad5823_dev.focus + value); +} + +int ad5823_q_focus_status(struct v4l2_subdev *sd, s32 *value) +{ + u32 status = 0; + struct timespec temptime; + const struct timespec timedelay = { + 0, + min_t(u32, abs(ad5823_dev.number_of_steps)*DELAY_PER_STEP_NS, + DELAY_MAX_PER_STEP_NS), + }; + + ktime_get_ts(&temptime); + + temptime = timespec_sub(temptime, (ad5823_dev.timestamp_t_focus_abs)); + + if (timespec_compare(&temptime, &timedelay) <= 0) + status = ATOMISP_FOCUS_STATUS_MOVING + | ATOMISP_FOCUS_HP_IN_PROGRESS; + else + status = ATOMISP_FOCUS_STATUS_ACCEPTS_NEW_MOVE + | ATOMISP_FOCUS_HP_COMPLETE; + + *value = status; + + return 0; +} + +int ad5823_q_focus_abs(struct v4l2_subdev *sd, s32 *value) +{ + s32 val; + + ad5823_q_focus_status(sd, &val); + + if (val & ATOMISP_FOCUS_STATUS_MOVING) + *value = ad5823_dev.focus - ad5823_dev.number_of_steps; + else + *value = ad5823_dev.focus ; + + return 0; +} + +int ad5823_t_vcm_slew(struct v4l2_subdev *sd, s32 value) +{ + return 0; +} + +int ad5823_t_vcm_timing(struct v4l2_subdev *sd, s32 value) +{ + return 0; +} + +int ad5823_vcm_init(struct v4l2_subdev *sd) +{ + /* set vcm mode to ARC RES0.5 */ + ad5823_dev.vcm_mode = AD5823_ARC_RES1; + ad5823_dev.platform_data = camera_get_af_platform_data(); + return ad5823_dev.platform_data ? 0 : -ENODEV; +} diff --git a/drivers/staging/ov5693/ad5823.h b/drivers/staging/ov5693/ad5823.h new file mode 100644 index 000000000000..8b046c31f3af --- /dev/null +++ b/drivers/staging/ov5693/ad5823.h @@ -0,0 +1,90 @@ +/* + * Support for AD5823 VCM. + * + * Copyright (c) 2013 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 __AD5823_H__ +#define __AD5823_H__ + +#include +#include + + +#define AD5823_VCM_ADDR 0x0c + +#define AD5823_REG_RESET 0x01 +#define AD5823_REG_MODE 0x02 +#define AD5823_REG_VCM_MOVE_TIME 0x03 +#define AD5823_REG_VCM_CODE_MSB 0x04 +#define AD5823_REG_VCM_CODE_LSB 0x05 +#define AD5823_REG_VCM_THRESHOLD_MSB 0x06 +#define AD5823_REG_VCM_THRESHOLD_LSB 0x07 + +#define AD5823_RING_CTRL_ENABLE 0x04 +#define AD5823_RING_CTRL_DISABLE 0x00 + +#define AD5823_RESONANCE_PERIOD 100000 +#define AD5823_RESONANCE_COEF 512 +#define AD5823_HIGH_FREQ_RANGE 0x80 + +#define VCM_CODE_MSB_MASK 0xfc + +enum ad5823_tok_type { + AD5823_8BIT = 0x0001, + AD5823_16BIT = 0x0002, +}; + +enum ad5823_vcm_mode { + AD5823_ARC_RES0 = 0x0, /* Actuator response control RES1 */ + AD5823_ARC_RES1 = 0x1, /* Actuator response control RES0.5 */ + AD5823_ARC_RES2 = 0x2, /* Actuator response control RES2 */ + AD5823_ESRC = 0x3, /* Enhanced slew rate control */ + AD5823_DIRECT = 0x4, /* Direct control */ +}; + +/* ad5823 device structure */ +struct ad5823_device { + struct timespec timestamp_t_focus_abs; + enum ad5823_vcm_mode vcm_mode; + s16 number_of_steps; + bool initialized; /* true if ad5823 is detected */ + s32 focus; /* Current focus value */ + struct timespec focus_time; /* Time when focus was last time set */ + __u8 buffer[4]; /* Used for i2c transactions */ + const struct camera_af_platform_data *platform_data; +}; + +#define AD5823_INVALID_CONFIG 0xffffffff +#define AD5823_MAX_FOCUS_POS 1023 + + +#define DELAY_PER_STEP_NS 1000000 +#define DELAY_MAX_PER_STEP_NS (1000000 * 1023) + +int ad5823_vcm_power_up(struct v4l2_subdev *sd); +int ad5823_vcm_power_down(struct v4l2_subdev *sd); +int ad5823_vcm_init(struct v4l2_subdev *sd); + +int ad5823_t_focus_vcm(struct v4l2_subdev *sd, u16 val); +int ad5823_t_focus_abs(struct v4l2_subdev *sd, s32 value); +int ad5823_t_focus_rel(struct v4l2_subdev *sd, s32 value); +int ad5823_q_focus_status(struct v4l2_subdev *sd, s32 *value); +int ad5823_q_focus_abs(struct v4l2_subdev *sd, s32 *value); + +#endif diff --git a/drivers/staging/ov5693/ov5693.c b/drivers/staging/ov5693/ov5693.c new file mode 100644 index 000000000000..51d218da3722 --- /dev/null +++ b/drivers/staging/ov5693/ov5693.c @@ -0,0 +1,1461 @@ +/* + * Support for OmniVision OV5693 5M HD camera sensor. + * + * Copyright (c) 2013 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 "ov5693.h" + +/* i2c read/write stuff */ +static int ov5693_read_reg(struct i2c_client *client, + u16 data_length, u16 reg, u16 *val) +{ + int err; + struct i2c_msg msg[2]; + unsigned char data[6]; + + if (!client->adapter) { + dev_err(&client->dev, "%s error, no client->adapter\n", + __func__); + return -ENODEV; + } + + if (data_length != OV5693_8BIT && data_length != OV5693_16BIT + && data_length != OV5693_32BIT) { + dev_err(&client->dev, "%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 = data; + + /* high byte goes out first */ + data[0] = (u8)(reg >> 8); + data[1] = (u8)(reg & 0xff); + + msg[1].addr = client->addr; + msg[1].len = data_length; + msg[1].flags = I2C_M_RD; + msg[1].buf = data; + + err = i2c_transfer(client->adapter, msg, 2); + if (err != 2) { + if (err >= 0) + err = -EIO; + dev_err(&client->dev, + "read from offset 0x%x error %d", reg, err); + return err; + } + + *val = 0; + /* high byte comes first */ + if (data_length == OV5693_8BIT) + *val = (u8)data[0]; + else if (data_length == OV5693_16BIT) + *val = be16_to_cpu(*(u16 *)&data[0]); + else + *val = be32_to_cpu(*(u32 *)&data[0]); + + return 0; +} + +static int ov5693_i2c_write(struct i2c_client *client, u16 len, u8 *data) +{ + struct i2c_msg msg; + const int num_msg = 1; + int ret; + + msg.addr = client->addr; + msg.flags = 0; + msg.len = len; + msg.buf = data; + ret = i2c_transfer(client->adapter, &msg, 1); + + return ret == num_msg ? 0 : -EIO; +} + +static int ov5693_write_reg(struct i2c_client *client, u16 data_length, + u16 reg, u16 val) +{ + int ret; + unsigned char data[4] = {0}; + u16 *wreg = (u16 *)data; + const u16 len = data_length + sizeof(u16); /* 16-bit address + data */ + + if (data_length != OV5693_8BIT && data_length != OV5693_16BIT) { + dev_err(&client->dev, + "%s error, invalid data_length\n", __func__); + return -EINVAL; + } + + /* high byte goes out first */ + *wreg = cpu_to_be16(reg); + + if (data_length == OV5693_8BIT) { + data[2] = (u8)(val); + } else { + /* OV5693_16BIT */ + u16 *wdata = (u16 *)&data[2]; + *wdata = cpu_to_be16(val); + } + + ret = ov5693_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; +} + +/* + * ov5693_write_reg_array - Initializes a list of OV5693 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(). + * + * __ov5693_flush_reg_array, __ov5693_buf_reg_array() and + * __ov5693_write_reg_is_consecutive() are internal functions to + * ov5693_write_reg_array_fast() and should be not used anywhere else. + * + */ +static int __ov5693_flush_reg_array(struct i2c_client *client, + struct ov5693_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 ov5693_i2c_write(client, size, (u8 *)&ctrl->buffer); +} + +static int __ov5693_buf_reg_array(struct i2c_client *client, + struct ov5693_write_ctrl *ctrl, + const struct ov5693_reg *next) +{ + int size; + u16 *data16; + + switch (next->type) { + case OV5693_8BIT: + size = 1; + ctrl->buffer.data[ctrl->index] = (u8)next->val; + break; + case OV5693_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; + + 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) >= OV5693_MAX_WRITE_BUF_SIZE) + return __ov5693_flush_reg_array(client, ctrl); + + return 0; +} + +static int __ov5693_write_reg_is_consecutive(struct i2c_client *client, + struct ov5693_write_ctrl *ctrl, + const struct ov5693_reg *next) +{ + if (ctrl->index == 0) + return 1; + + return ctrl->buffer.addr + ctrl->index == next->reg; +} + +static int ov5693_write_reg_array(struct i2c_client *client, + const struct ov5693_reg *reglist) +{ + const struct ov5693_reg *next = reglist; + struct ov5693_write_ctrl ctrl; + int err; + + ctrl.index = 0; + for (; next->type != OV5693_TOK_TERM; next++) { + switch (next->type & OV5693_TOK_MASK) { + case OV5693_TOK_DELAY: + err = __ov5693_flush_reg_array(client, &ctrl); + if (err) + return err; + usleep_range(next->val * 1000, (next->val + 1) * 1000); + break; + default: + /* + * If next address is not consecutive, data needs to be + * flushed before proceed. + */ + if (!__ov5693_write_reg_is_consecutive(client, &ctrl, + next)) { + err = __ov5693_flush_reg_array(client, &ctrl); + if (err) + return err; + } + err = __ov5693_buf_reg_array(client, &ctrl, next); + if (err) { + dev_err(&client->dev, "%s: write error, aborted\n", + __func__); + return err; + } + break; + } + } + + return __ov5693_flush_reg_array(client, &ctrl); +} +static int ov5693_g_focal(struct v4l2_subdev *sd, s32 *val) +{ + *val = (OV5693_FOCAL_LENGTH_NUM << 16) | OV5693_FOCAL_LENGTH_DEM; + return 0; +} + +static int ov5693_g_fnumber(struct v4l2_subdev *sd, s32 *val) +{ + /*const f number for ov5693*/ + *val = (OV5693_F_NUMBER_DEFAULT_NUM << 16) | OV5693_F_NUMBER_DEM; + return 0; +} + +static int ov5693_g_fnumber_range(struct v4l2_subdev *sd, s32 *val) +{ + *val = (OV5693_F_NUMBER_DEFAULT_NUM << 24) | + (OV5693_F_NUMBER_DEM << 16) | + (OV5693_F_NUMBER_DEFAULT_NUM << 8) | OV5693_F_NUMBER_DEM; + return 0; +} + + +static int ov5693_get_intg_factor(struct i2c_client *client, + struct camera_mipi_info *info, + const struct ov5693_resolution *res) +{ + struct atomisp_sensor_mode_data *buf = &info->data; + unsigned int pix_clk_freq_hz; + u16 reg_val; + int ret; + + if (info == NULL) + return -EINVAL; + + /* pixel clock calculattion */ + pix_clk_freq_hz = res->pix_clk_freq * 1000000; + + buf->vt_pix_clk_freq_mhz = pix_clk_freq_hz; + + /* get integration time */ + buf->coarse_integration_time_min = OV5693_COARSE_INTG_TIME_MIN; + buf->coarse_integration_time_max_margin = + OV5693_COARSE_INTG_TIME_MAX_MARGIN; + + buf->fine_integration_time_min = OV5693_FINE_INTG_TIME_MIN; + buf->fine_integration_time_max_margin = + OV5693_FINE_INTG_TIME_MAX_MARGIN; + + buf->fine_integration_time_def = OV5693_FINE_INTG_TIME_MIN; + buf->frame_length_lines = res->lines_per_frame; + buf->line_length_pck = res->pixels_per_line; + buf->read_mode = res->bin_mode; + + /* get the cropping and output resolution to ISP for this mode. */ + ret = ov5693_read_reg(client, OV5693_16BIT, + OV5693_H_CROP_START_H, ®_val); + if (ret) + return ret; + buf->crop_horizontal_start = reg_val; + + ret = ov5693_read_reg(client, OV5693_16BIT, + OV5693_V_CROP_START_H, ®_val); + if (ret) + return ret; + buf->crop_vertical_start = reg_val; + + ret = ov5693_read_reg(client, OV5693_16BIT, + OV5693_H_CROP_END_H, ®_val); + if (ret) + return ret; + buf->crop_horizontal_end = reg_val; + + ret = ov5693_read_reg(client, OV5693_16BIT, + OV5693_V_CROP_END_H, ®_val); + if (ret) + return ret; + buf->crop_vertical_end = reg_val; + + ret = ov5693_read_reg(client, OV5693_16BIT, + OV5693_H_OUTSIZE_H, ®_val); + if (ret) + return ret; + buf->output_width = reg_val; + + ret = ov5693_read_reg(client, OV5693_16BIT, + OV5693_V_OUTSIZE_H, ®_val); + if (ret) + return ret; + buf->output_height = reg_val; + + /* + * we can't return 0 for bin_factor, this is because camera + * HAL will use them as denominator, bin_factor = 0 will + * cause camera HAL crash. So we return bin_factor as this + * rules: + * [1]. res->bin_factor = 0, return 1 for bin_factor. + * [2]. res->bin_factor > 0, return res->bin_factor. + */ + buf->binning_factor_x = res->bin_factor_x ? + res->bin_factor_x : 1; + buf->binning_factor_y = res->bin_factor_y ? + res->bin_factor_y : 1; + return 0; +} + +static long __ov5693_set_exposure(struct v4l2_subdev *sd, int coarse_itg, + int gain, int digitgain) + +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u16 vts; + int ret; + + /* + * According to spec, the low 4 bits of exposure/gain reg are + * fraction bits, so need to take 4 bits left shift to align + * reg integer bits. + */ + coarse_itg <<= 4; + gain <<= 4; + + ret = ov5693_read_reg(client, OV5693_16BIT, + OV5693_VTS_H, &vts); + if (ret) + return ret; + + if (coarse_itg + OV5693_INTEGRATION_TIME_MARGIN >= vts) + vts = coarse_itg + OV5693_INTEGRATION_TIME_MARGIN; + + ret = ov5693_write_reg(client, OV5693_16BIT, OV5693_VTS_H, vts); + if (ret) + return ret; + + /* group hold start */ + ret = ov5693_write_reg(client, OV5693_8BIT, OV5693_GROUP_ACCESS, 0); + if (ret) + return ret; + + /* set exposure */ + ret = ov5693_write_reg(client, OV5693_8BIT, + OV5693_AEC_PK_EXPO_L, + coarse_itg & 0xff); + if (ret) + return ret; + + ret = ov5693_write_reg(client, OV5693_16BIT, + OV5693_AEC_PK_EXPO_H, + (coarse_itg >> 8) & 0xfff); + if (ret) + return ret; + + /* set analog gain */ + ret = ov5693_write_reg(client, OV5693_16BIT, + OV5693_AGC_ADJ_H, gain); + if (ret) + return ret; + + /* set digital gain */ + ret = ov5693_write_reg(client, OV5693_16BIT, + OV5693_MWB_GAIN_R_H, digitgain); + if (ret) + return ret; + + ret = ov5693_write_reg(client, OV5693_16BIT, + OV5693_MWB_GAIN_G_H, digitgain); + if (ret) + return ret; + + ret = ov5693_write_reg(client, OV5693_16BIT, + OV5693_MWB_GAIN_B_H, digitgain); + if (ret) + return ret; + + /* group hold end */ + ret = ov5693_write_reg(client, OV5693_8BIT, + OV5693_GROUP_ACCESS, 0x10); + if (ret) + return ret; + + /* group hold launch */ + ret = ov5693_write_reg(client, OV5693_8BIT, + OV5693_GROUP_ACCESS, 0xa0); + + return ret; +} + +static int ov5693_set_exposure(struct v4l2_subdev *sd, int exposure, + int gain, int digitgain) +{ + struct ov5693_device *dev = to_ov5693_sensor(sd); + int ret; + + mutex_lock(&dev->input_lock); + ret = __ov5693_set_exposure(sd, exposure, gain, digitgain); + mutex_unlock(&dev->input_lock); + + return ret; +} + +static long ov5693_s_exposure(struct v4l2_subdev *sd, + struct atomisp_exposure *exposure) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int exp = exposure->integration_time[0]; + int gain = exposure->gain[0]; + int digitgain = exposure->gain[1]; + + /* we should not accept the invalid value below. */ + if (gain == 0) { + dev_err(&client->dev, "%s: invalid value\n", __func__); + return -EINVAL; + } + + return ov5693_set_exposure(sd, exp, gain, digitgain); +} + +static long ov5693_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + + switch (cmd) { + case ATOMISP_IOC_S_EXPOSURE: + return ov5693_s_exposure(sd, arg); + default: + return -EINVAL; + } + 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 ov5693_q_exposure(struct v4l2_subdev *sd, s32 *value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u16 reg_v, reg_v2; + int ret; + + /* get exposure */ + ret = ov5693_read_reg(client, OV5693_8BIT, + OV5693_AEC_PK_EXPO_L, + ®_v); + if (ret) + goto err; + + ret = ov5693_read_reg(client, OV5693_8BIT, + OV5693_AEC_PK_EXPO_M, + ®_v2); + if (ret) + goto err; + + reg_v += reg_v2 << 8; + ret = ov5693_read_reg(client, OV5693_8BIT, + OV5693_AEC_PK_EXPO_H, + ®_v2); + if (ret) + goto err; + + *value = (reg_v + (((u32)reg_v2 << 16))) >> 4; +err: + return ret; +} + +/* + * This below focus func don't need input_lock mutex_lock + * since they are just called in v4l2 s_ctrl/g_ctrl framework + * where mutex input_lock have been done. + */ +int ov5693_t_focus_abs(struct v4l2_subdev *sd, s32 value) +{ + struct ov5693_device *dev = to_ov5693_sensor(sd); + int ret = 0; + + if (dev->vcm_driver && dev->vcm_driver->t_focus_abs) + ret = dev->vcm_driver->t_focus_abs(sd, value); + + return ret; +} + +int ov5693_t_focus_rel(struct v4l2_subdev *sd, s32 value) +{ + struct ov5693_device *dev = to_ov5693_sensor(sd); + int ret = 0; + + if (dev->vcm_driver && dev->vcm_driver->t_focus_rel) + ret = dev->vcm_driver->t_focus_rel(sd, value); + + return ret; +} + +int ov5693_q_focus_status(struct v4l2_subdev *sd, s32 *value) +{ + struct ov5693_device *dev = to_ov5693_sensor(sd); + int ret = 0; + + if (dev->vcm_driver && dev->vcm_driver->q_focus_status) + ret = dev->vcm_driver->q_focus_status(sd, value); + + return ret; +} + +int ov5693_q_focus_abs(struct v4l2_subdev *sd, s32 *value) +{ + struct ov5693_device *dev = to_ov5693_sensor(sd); + int ret = 0; + + if (dev->vcm_driver && dev->vcm_driver->q_focus_abs) + ret = dev->vcm_driver->q_focus_abs(sd, value); + + return ret; +} + +/* ov5693 control set/get */ +static int ov5693_g_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov5693_device *dev = container_of( + ctrl->handler, struct ov5693_device, ctrl_handler); + int ret = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE_ABSOLUTE: + ret = ov5693_q_exposure(&dev->sd, &ctrl->val); + break; + case V4L2_CID_FOCUS_ABSOLUTE: + ret = ov5693_q_focus_abs(&dev->sd, &ctrl->val); + break; + case V4L2_CID_FOCUS_STATUS: + ret = ov5693_q_focus_status(&dev->sd, &ctrl->val); + break; + case V4L2_CID_FOCAL_ABSOLUTE: + ret = ov5693_g_focal(&dev->sd, &ctrl->val); + break; + case V4L2_CID_FNUMBER_ABSOLUTE: + ret = ov5693_g_fnumber(&dev->sd, &ctrl->val); + break; + case V4L2_CID_FNUMBER_RANGE: + ret = ov5693_g_fnumber_range(&dev->sd, &ctrl->val); + break; + case V4L2_CID_BIN_FACTOR_HORZ: + ctrl->val = dev->ov5693_res[dev->fmt_idx].bin_factor_x; + break; + case V4L2_CID_BIN_FACTOR_VERT: + ctrl->val = dev->ov5693_res[dev->fmt_idx].bin_factor_y; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int ov5693_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov5693_device *dev = container_of( + ctrl->handler, struct ov5693_device, ctrl_handler); + int ret = 0; + + if (!ctrl) + return -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_RUN_MODE: + switch (ctrl->val) { + case ATOMISP_RUN_MODE_VIDEO: + dev->ov5693_res = ov5693_res_video; + dev->curr_res_num = N_RES_VIDEO; + break; + case ATOMISP_RUN_MODE_STILL_CAPTURE: + dev->ov5693_res = ov5693_res_still; + dev->curr_res_num = N_RES_STILL; + break; + default: + dev->ov5693_res = ov5693_res_preview; + dev->curr_res_num = N_RES_PREVIEW; + } + break; + case V4L2_CID_FOCUS_ABSOLUTE: + ret = ov5693_t_focus_abs(&dev->sd, ctrl->val); + break; + case V4L2_CID_FOCUS_RELATIVE: + ret = ov5693_t_focus_rel(&dev->sd, ctrl->val); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int ov5693_init(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov5693_device *dev = to_ov5693_sensor(sd); + int ret = 0; + + /* restore settings */ + dev->ov5693_res = ov5693_res_preview; + dev->curr_res_num = N_RES_PREVIEW; + + ret = ov5693_write_reg_array(client, ov5693_init_setting); + if (ret) + dev_err(&client->dev, "ov5693 write init setting reg err.\n"); + + return ret; +} + + +static int power_up(struct v4l2_subdev *sd) +{ + struct ov5693_device *dev = to_ov5693_sensor(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + + if (NULL == dev->platform_data) { + dev_err(&client->dev, + "no camera_sensor_platform_data"); + return -ENODEV; + } + + /* power control */ + ret = dev->platform_data->power_ctrl(sd, 1); + if (ret) + goto fail_power; + + /* gpio ctrl */ + ret = dev->platform_data->gpio_ctrl(sd, 1); + if (ret) + goto fail_power; + + /* flis clock control */ + ret = dev->platform_data->flisclk_ctrl(sd, 1); + if (ret) + goto fail_clk; + + /* according to DS, 20ms is needed between PWDN and i2c access */ + msleep(20); + + return 0; + +fail_clk: + dev->platform_data->gpio_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 ov5693_device *dev = to_ov5693_sensor(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + + if (NULL == dev->platform_data) { + dev_err(&client->dev, + "no camera_sensor_platform_data"); + return -ENODEV; + } + + 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.\n"); + + /* power control */ + ret = dev->platform_data->power_ctrl(sd, 0); + if (ret) + dev_err(&client->dev, "vprog failed.\n"); + + return ret; +} + +static int ov5693_s_power(struct v4l2_subdev *sd, int on) +{ + struct ov5693_device *dev = to_ov5693_sensor(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + + mutex_lock(&dev->input_lock); + if (on == 0) { + if (dev->vcm_driver && dev->vcm_driver->power_down) + ret = dev->vcm_driver->power_down(sd); + if (ret) + dev_err(&client->dev, "vcm power-down failed.\n"); + + ret = power_down(sd); + } else { + if (dev->vcm_driver && dev->vcm_driver->power_up) + ret = dev->vcm_driver->power_up(sd); + if (ret) + dev_err(&client->dev, "vcm power-up failed.\n"); + + ret = power_up(sd); + if (!ret) + ret = ov5693_init(sd); + } + mutex_unlock(&dev->input_lock); + return ret; +} + +/* + * 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. + */ +static int distance(struct ov5693_resolution *res, u32 w, u32 h) +{ + unsigned int w_ratio = ((res->width << RATIO_SHIFT_BITS)/w); + unsigned int h_ratio; + int match; + + if (h == 0) + return -1; + h_ratio = ((res->height << RATIO_SHIFT_BITS) / h); + if (h_ratio == 0) + return -1; + match = abs(((w_ratio << RATIO_SHIFT_BITS) / h_ratio) + - ((int)(1 << RATIO_SHIFT_BITS))); + + if ((w_ratio < (int)(1 << RATIO_SHIFT_BITS)) + || (h_ratio < (int)(1 << RATIO_SHIFT_BITS)) || + (match > LARGEST_ALLOWED_RATIO_MISMATCH)) + return -1; + + return w_ratio + h_ratio; +} + +/* Return the nearest higher resolution index */ +static int nearest_resolution_index(struct v4l2_subdev *sd, + int w, int h) +{ + struct ov5693_device *dev = to_ov5693_sensor(sd); + int i; + int idx = dev->curr_res_num-1; + int dist; + int min_dist = INT_MAX; + struct ov5693_resolution *tmp_res = NULL; + + for (i = 0; i < dev->curr_res_num; i++) { + tmp_res = &dev->ov5693_res[i]; + dist = distance(tmp_res, w, h); + if (dist == -1) + continue; + if (dist < min_dist) { + min_dist = dist; + idx = i; + } + } + + return idx; +} + +static int get_resolution_index(struct v4l2_subdev *sd, + int w, int h) +{ + struct ov5693_device *dev = to_ov5693_sensor(sd); + int i; + + for (i = 0; i < dev->curr_res_num; i++) { + if (w != dev->ov5693_res[i].width) + continue; + if (h != dev->ov5693_res[i].height) + continue; + + return i; + } + + return -1; +} + +static int __ov5693_try_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct ov5693_device *dev = to_ov5693_sensor(sd); + int idx; + + if (!fmt) + return -EINVAL; + + idx = nearest_resolution_index(sd, fmt->width, fmt->height); + fmt->width = dev->ov5693_res[idx].width; + fmt->height = dev->ov5693_res[idx].height; + fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10; + + return 0; +} + +static int ov5693_try_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct ov5693_device *dev = to_ov5693_sensor(sd); + int ret; + + mutex_lock(&dev->input_lock); + ret = __ov5693_try_mbus_fmt(sd, fmt); + mutex_unlock(&dev->input_lock); + + return ret; +} + +static int ov5693_s_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct ov5693_device *dev = to_ov5693_sensor(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct camera_mipi_info *ov5693_info = NULL; + int ret = 0; + + ov5693_info = v4l2_get_subdev_hostdata(sd); + if (ov5693_info == NULL) + return -EINVAL; + + mutex_lock(&dev->input_lock); + ret = __ov5693_try_mbus_fmt(sd, fmt); + if (ret == -1) { + dev_err(&client->dev, "try fmt fail\n"); + goto done; + } + + dev->fmt_idx = get_resolution_index(sd, fmt->width, fmt->height); + if (dev->fmt_idx == -1) { + dev_err(&client->dev, "get resolution fail\n"); + goto done; + } + + ret = ov5693_write_reg_array(client, dev->ov5693_res[dev->fmt_idx].regs); + if (ret) { + dev_err(&client->dev, "ov5693 write fmt register err.\n"); + goto done; + } + + ret = ov5693_get_intg_factor(client, ov5693_info, + &dev->ov5693_res[dev->fmt_idx]); + if (ret) + dev_err(&client->dev, "failed to get integration_factor\n"); + +done: + mutex_unlock(&dev->input_lock); + return ret; +} +static int ov5693_g_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct ov5693_device *dev = to_ov5693_sensor(sd); + + mutex_lock(&dev->input_lock); + fmt->width = dev->ov5693_res[dev->fmt_idx].width; + fmt->height = dev->ov5693_res[dev->fmt_idx].height; + fmt->code = V4L2_MBUS_FMT_SBGGR10_1X10; + mutex_unlock(&dev->input_lock); + + return 0; +} + +static int ov5693_detect(struct i2c_client *client) +{ + struct i2c_adapter *adapter = client->adapter; + int ret = 0; + u16 id; + u8 revision; + + if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) + return -ENODEV; + + ret = ov5693_read_reg(client, OV5693_16BIT, + OV5693_SC_CMMN_CHIP_ID, &id); + if (ret) { + dev_err(&client->dev, "read sensor_id err.\n"); + return -ENODEV; + } + + if (id != OV5693_ID) { + dev_err(&client->dev, "sensor ID error\n"); + return -ENODEV; + } + + ret = ov5693_read_reg(client, OV5693_8BIT, + OV5693_SC_CMMN_SUB_ID, &id); + revision = (u8)id & 0x0f; + + dev_dbg(&client->dev, "sensor_revision = 0x%x\n", revision); + dev_dbg(&client->dev, "detect ov5693 success\n"); + return ret; +} + +static int ov5693_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct ov5693_device *dev = to_ov5693_sensor(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + + mutex_lock(&dev->input_lock); + + ret = ov5693_write_reg(client, OV5693_8BIT, OV5693_SW_STREAM, + enable ? OV5693_START_STREAMING : + OV5693_STOP_STREAMING); + + mutex_unlock(&dev->input_lock); + return ret; +} + +/* ov5693 enum frame size, frame intervals */ +static int ov5693_enum_framesizes(struct v4l2_subdev *sd, + struct v4l2_frmsizeenum *fsize) +{ + struct ov5693_device *dev = to_ov5693_sensor(sd); + unsigned int index = fsize->index; + + mutex_lock(&dev->input_lock); + + if (index >= dev->curr_res_num) { + mutex_unlock(&dev->input_lock); + return -EINVAL; + } + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = dev->ov5693_res[index].width; + fsize->discrete.height = dev->ov5693_res[index].height; + + mutex_unlock(&dev->input_lock); + return 0; +} + +static int ov5693_enum_frameintervals(struct v4l2_subdev *sd, + struct v4l2_frmivalenum *fival) +{ + struct ov5693_device *dev = to_ov5693_sensor(sd); + unsigned int index = fival->index; + + mutex_lock(&dev->input_lock); + + if (index >= dev->curr_res_num) { + mutex_unlock(&dev->input_lock); + return -EINVAL; + } + + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->width = dev->ov5693_res[index].width; + fival->height = dev->ov5693_res[index].height; + fival->discrete.numerator = 1; + fival->discrete.denominator = dev->ov5693_res[index].fps; + + mutex_unlock(&dev->input_lock); + + return 0; +} + +static int ov5693_enum_mbus_fmt(struct v4l2_subdev *sd, + unsigned int index, + enum v4l2_mbus_pixelcode *code) +{ + *code = V4L2_MBUS_FMT_SBGGR10_1X10; + + return 0; +} + +static int ov5693_s_config(struct v4l2_subdev *sd, + int irq, void *platform_data) +{ + struct ov5693_device *dev = to_ov5693_sensor(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + + if (platform_data == NULL) + return -ENODEV; + + mutex_lock(&dev->input_lock); + + dev->platform_data = platform_data; + + ret = power_up(sd); + if (ret) { + dev_err(&client->dev, "ov5693 power-up err.\n"); + goto fail_power_on; + } + + ret = dev->platform_data->csi_cfg(sd, 1); + if (ret) + goto fail_csi_cfg; + + /* config & detect sensor */ + ret = ov5693_detect(client); + if (ret) { + dev_err(&client->dev, "ov5693_detect err s_config.\n"); + goto fail_csi_cfg; + } + + /* turn off sensor, after probed */ + ret = power_down(sd); + if (ret) { + dev_err(&client->dev, "ov5693 power-off err.\n"); + goto fail_csi_cfg; + } + mutex_unlock(&dev->input_lock); + + return 0; + +fail_csi_cfg: + dev->platform_data->csi_cfg(sd, 0); +fail_power_on: + power_down(sd); + dev_err(&client->dev, "sensor power-gating failed\n"); + mutex_unlock(&dev->input_lock); + return ret; +} + +static int ov5693_g_parm(struct v4l2_subdev *sd, + struct v4l2_streamparm *param) +{ + struct ov5693_device *dev = to_ov5693_sensor(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + dev_err(&client->dev, "unsupported buffer type.\n"); + return -EINVAL; + } + + memset(param, 0, sizeof(*param)); + param->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + mutex_lock(&dev->input_lock); + if (dev->fmt_idx >= 0 && dev->fmt_idx < dev->curr_res_num) { + param->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + param->parm.capture.timeperframe.numerator = 1; + param->parm.capture.capturemode = dev->run_mode->val; + param->parm.capture.timeperframe.denominator = + dev->ov5693_res[dev->fmt_idx].fps; + } + mutex_unlock(&dev->input_lock); + return 0; +} + +static int ov5693_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *interval) +{ + struct ov5693_device *dev = to_ov5693_sensor(sd); + + mutex_lock(&dev->input_lock); + interval->interval.numerator = 1; + interval->interval.denominator = dev->ov5693_res[dev->fmt_idx].fps; + mutex_unlock(&dev->input_lock); + + return 0; +} + +static int ov5693_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + code->code = V4L2_MBUS_FMT_SBGGR10_1X10; + return 0; +} + +static int ov5693_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct ov5693_device *dev = to_ov5693_sensor(sd); + int index = fse->index; + + mutex_lock(&dev->input_lock); + + if (index >= dev->curr_res_num) { + mutex_unlock(&dev->input_lock); + return -EINVAL; + } + + fse->min_width = dev->ov5693_res[index].width; + fse->min_height = dev->ov5693_res[index].height; + fse->max_width = dev->ov5693_res[index].width; + fse->max_height = dev->ov5693_res[index].height; + + mutex_unlock(&dev->input_lock); + return 0; +} + +static int ov5693_get_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct ov5693_device *dev = to_ov5693_sensor(sd); + struct v4l2_mbus_framefmt *format; + + mutex_lock(&dev->input_lock); + + switch (fmt->which) { + case V4L2_SUBDEV_FORMAT_TRY: + format = v4l2_subdev_get_try_format(fh, fmt->pad); + break; + case V4L2_SUBDEV_FORMAT_ACTIVE: + format = &dev->format; + break; + default: + format = NULL; + } + + mutex_unlock(&dev->input_lock); + + if (!format) + return -EINVAL; + + fmt->format = *format; + return 0; +} + +static int ov5693_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct ov5693_device *dev = to_ov5693_sensor(sd); + + mutex_lock(&dev->input_lock); + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + dev->format = fmt->format; + + mutex_unlock(&dev->input_lock); + return 0; +} + +static int ov5693_g_skip_frames(struct v4l2_subdev *sd, u32 *frames) +{ + struct ov5693_device *dev = to_ov5693_sensor(sd); + + mutex_lock(&dev->input_lock); + *frames = dev->ov5693_res[dev->fmt_idx].skip_frames; + mutex_unlock(&dev->input_lock); + + return 0; +} + +static const struct v4l2_ctrl_ops ctrl_ops = { + .s_ctrl = ov5693_s_ctrl, + .g_volatile_ctrl = ov5693_g_ctrl, +}; + +static const char * const ctrl_run_mode_menu[] = { + NULL, + "Video", + "Still capture", + "Continuous capture", + "Preview", +}; + +static const struct v4l2_ctrl_config ctrl_run_mode = { + .ops = &ctrl_ops, + .id = V4L2_CID_RUN_MODE, + .name = "run mode", + .type = V4L2_CTRL_TYPE_MENU, + .min = 1, + .def = 4, + .max = 4, + .qmenu = ctrl_run_mode_menu, +}; + +static const struct v4l2_ctrl_config ctrls[] = { + { + .ops = &ctrl_ops, + .id = V4L2_CID_EXPOSURE_ABSOLUTE, + .name = "absolute exposure", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0x0, + .max = 0xffff, + .step = 0x01, + .def = 0x00, + .flags = 0, + }, { + .ops = &ctrl_ops, + .id = V4L2_CID_FOCUS_ABSOLUTE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "focus move absolute", + .min = 0, + .max = OV5693_MAX_FOCUS_POS, + .step = 1, + .def = 0, + .flags = 0, + }, { + .ops = &ctrl_ops, + .id = V4L2_CID_FOCUS_RELATIVE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "focus move relative", + .min = OV5693_MAX_FOCUS_NEG, + .max = OV5693_MAX_FOCUS_POS, + .step = 1, + .def = 0, + .flags = 0, + }, { + .ops = &ctrl_ops, + .id = V4L2_CID_FOCUS_STATUS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "focus status", + .min = 0, + .max = 100, + .step = 1, + .def = 0, + .flags = 0, + }, { + .ops = &ctrl_ops, + .id = V4L2_CID_FOCAL_ABSOLUTE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "focal length", + .min = OV5693_FOCAL_LENGTH_DEFAULT, + .max = OV5693_FOCAL_LENGTH_DEFAULT, + .step = 0x01, + .def = OV5693_FOCAL_LENGTH_DEFAULT, + .flags = 0, + }, { + .ops = &ctrl_ops, + .id = V4L2_CID_FNUMBER_ABSOLUTE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "f-number", + .min = OV5693_F_NUMBER_DEFAULT, + .max = OV5693_F_NUMBER_DEFAULT, + .step = 0x01, + .def = OV5693_F_NUMBER_DEFAULT, + .flags = 0, + }, { + .ops = &ctrl_ops, + .id = V4L2_CID_FNUMBER_RANGE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "f-number range", + .min = OV5693_F_NUMBER_RANGE, + .max = OV5693_F_NUMBER_RANGE, + .step = 0x01, + .def = OV5693_F_NUMBER_RANGE, + .flags = 0, + }, { + .ops = &ctrl_ops, + .id = V4L2_CID_BIN_FACTOR_HORZ, + .name = "horizontal binning factor", + .type = V4L2_CTRL_TYPE_INTEGER, + .max = OV5693_BIN_FACTOR_MAX, + .step = 2, + .flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE, + }, { + .ops = &ctrl_ops, + .id = V4L2_CID_BIN_FACTOR_VERT, + .name = "vertical binning factor", + .type = V4L2_CTRL_TYPE_INTEGER, + .max = OV5693_BIN_FACTOR_MAX, + .step = 2, + .flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE, + } +}; + +static const struct v4l2_subdev_sensor_ops ov5693_sensor_ops = { + .g_skip_frames = ov5693_g_skip_frames, +}; + +static const struct v4l2_subdev_video_ops ov5693_video_ops = { + .s_stream = ov5693_s_stream, + .g_parm = ov5693_g_parm, + .enum_framesizes = ov5693_enum_framesizes, + .enum_frameintervals = ov5693_enum_frameintervals, + .enum_mbus_fmt = ov5693_enum_mbus_fmt, + .try_mbus_fmt = ov5693_try_mbus_fmt, + .g_mbus_fmt = ov5693_g_mbus_fmt, + .s_mbus_fmt = ov5693_s_mbus_fmt, + .g_frame_interval = ov5693_g_frame_interval, +}; + +static const struct v4l2_subdev_core_ops ov5693_core_ops = { + .s_power = ov5693_s_power, + .queryctrl = v4l2_subdev_queryctrl, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .ioctl = ov5693_ioctl, +}; + +static const struct v4l2_subdev_pad_ops ov5693_pad_ops = { + .enum_mbus_code = ov5693_enum_mbus_code, + .enum_frame_size = ov5693_enum_frame_size, + .get_fmt = ov5693_get_pad_format, + .set_fmt = ov5693_set_pad_format, +}; + +static const struct v4l2_subdev_ops ov5693_ops = { + .core = &ov5693_core_ops, + .video = &ov5693_video_ops, + .pad = &ov5693_pad_ops, + .sensor = &ov5693_sensor_ops, +}; + +static int ov5693_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov5693_device *dev = to_ov5693_sensor(sd); + dev_dbg(&client->dev, "ov5693_remove...\n"); + + dev->platform_data->csi_cfg(sd, 0); + + v4l2_device_unregister_subdev(sd); + media_entity_cleanup(&dev->sd.entity); + devm_kfree(&client->dev, dev); + + return 0; +} + +static int ov5693_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ov5693_device *dev; + int i; + int ret; + + dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) { + dev_err(&client->dev, "out of memory\n"); + return -ENOMEM; + } + + mutex_init(&dev->input_lock); + + /* + * Initialize related res members of dev. + */ + dev->fmt_idx = 0; + dev->ov5693_res = ov5693_res_preview; + dev->curr_res_num = N_RES_PREVIEW; + + v4l2_i2c_subdev_init(&(dev->sd), client, &ov5693_ops); + + if (client->dev.platform_data) { + ret = ov5693_s_config(&dev->sd, client->irq, + client->dev.platform_data); + if (ret) + goto out_free; + } + + dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + dev->pad.flags = MEDIA_PAD_FL_SOURCE; + dev->format.code = V4L2_MBUS_FMT_SBGGR10_1X10; + dev->sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; + dev->vcm_driver = &ov5693_vcm_ops; + + ret = v4l2_ctrl_handler_init(&dev->ctrl_handler, ARRAY_SIZE(ctrls) + 1); + if (ret) { + ov5693_remove(client); + return ret; + } + + dev->run_mode = v4l2_ctrl_new_custom(&dev->ctrl_handler, + &ctrl_run_mode, NULL); + + for (i = 0; i < ARRAY_SIZE(ctrls); i++) + v4l2_ctrl_new_custom(&dev->ctrl_handler, &ctrls[i], NULL); + + if (dev->ctrl_handler.error) { + ov5693_remove(client); + return dev->ctrl_handler.error; + } + + dev->ctrl_handler.lock = &dev->input_lock; + dev->sd.ctrl_handler = &dev->ctrl_handler; + v4l2_ctrl_handler_setup(&dev->ctrl_handler); + + ret = media_entity_init(&dev->sd.entity, 1, &dev->pad, 0); + if (ret) + ov5693_remove(client); + + /* vcm initialization */ + if (dev->vcm_driver && dev->vcm_driver->init) + ret = dev->vcm_driver->init(&dev->sd); + if (ret) { + dev_err(&client->dev, "vcm init failed.\n"); + ov5693_remove(client); + } + + return ret; +out_free: + v4l2_device_unregister_subdev(&dev->sd); + devm_kfree(&client->dev, dev); + return ret; +} + +MODULE_DEVICE_TABLE(i2c, ov5693_id); +static struct i2c_driver ov5693_driver = { + .driver = { + .owner = THIS_MODULE, + .name = OV5693_NAME, + }, + .probe = ov5693_probe, + .remove = ov5693_remove, + .id_table = ov5693_id, +}; + +module_i2c_driver(ov5693_driver); + +MODULE_DESCRIPTION("A low-level driver for OmniVision 5693 sensors"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/ov5693/ov5693.h b/drivers/staging/ov5693/ov5693.h new file mode 100644 index 000000000000..79aef69666e8 --- /dev/null +++ b/drivers/staging/ov5693/ov5693.h @@ -0,0 +1,848 @@ +/* + * Support for OmniVision OV5693 5M HD camera sensor. + * + * Copyright (c) 2013 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 __OV5693_H__ +#define __OV5693_H__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "ad5823.h" + +#define OV5693_NAME "ov5693" + +/* Defines for register writes and register array processing */ +#define I2C_MSG_LENGTH 0x2 + +#define OV5693_FOCAL_LENGTH_NUM 278 /*2.78mm*/ +#define OV5693_FOCAL_LENGTH_DEM 100 +#define OV5693_F_NUMBER_DEFAULT_NUM 26 +#define OV5693_F_NUMBER_DEM 10 + +#define OV5693_MAX_FOCUS_POS 1023 +#define OV5693_MAX_FOCUS_POS 1023 +#define OV5693_MAX_FOCUS_NEG (-1023) + +#define LARGEST_ALLOWED_RATIO_MISMATCH 800 +#define RATIO_SHIFT_BITS 13 + +/* + * focal length bits definition: + * bits 31-16: numerator, bits 15-0: denominator + */ +#define OV5693_FOCAL_LENGTH_DEFAULT 0x1160064 + +/* + * current f-number bits definition: + * bits 31-16: numerator, bits 15-0: denominator + */ +#define OV5693_F_NUMBER_DEFAULT 0x1a000a + +/* + * 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 OV5693_F_NUMBER_RANGE 0x1a0a1a0a +#define OV5693_ID 0x5690 + +#define OV5693_FINE_INTG_TIME_MIN 0 +#define OV5693_FINE_INTG_TIME_MAX_MARGIN 0 +#define OV5693_COARSE_INTG_TIME_MIN 1 +#define OV5693_COARSE_INTG_TIME_MAX_MARGIN (0xffff - 6) +#define OV5693_INTEGRATION_TIME_MARGIN 8 + +#define OV5693_BIN_FACTOR_MAX 2 + +/* + * OV5693 System control registers + */ +#define OV5693_SW_RESET 0x0103 +#define OV5693_SW_STREAM 0x0100 + +#define OV5693_SC_CMMN_CHIP_ID 0x300a +#define OV5693_SC_CMMN_SUB_ID 0x302a /* process, version*/ + +#define OV5693_AEC_PK_EXPO_H 0x3500 +#define OV5693_AEC_PK_EXPO_M 0x3501 +#define OV5693_AEC_PK_EXPO_L 0x3502 +#define OV5693_AGC_ADJ_H 0x350a +#define OV5693_VTS_H 0x380e +#define OV5693_GROUP_ACCESS 0x3208 + +#define OV5693_MWB_GAIN_R_H 0x3400 +#define OV5693_MWB_GAIN_G_H 0x3402 +#define OV5693_MWB_GAIN_B_H 0x3404 + +#define OV5693_H_CROP_START_H 0x3800 +#define OV5693_V_CROP_START_H 0x3802 +#define OV5693_H_CROP_END_H 0x3804 +#define OV5693_V_CROP_END_H 0x3806 +#define OV5693_H_OUTSIZE_H 0x3808 +#define OV5693_V_OUTSIZE_H 0x380a + +#define OV5693_START_STREAMING 0x01 +#define OV5693_STOP_STREAMING 0x00 + +struct ov5693_vcm { + int (*power_up)(struct v4l2_subdev *sd); + int (*power_down)(struct v4l2_subdev *sd); + int (*init)(struct v4l2_subdev *sd); + int (*t_focus_vcm)(struct v4l2_subdev *sd, u16 val); + int (*t_focus_abs)(struct v4l2_subdev *sd, s32 value); + int (*t_focus_rel)(struct v4l2_subdev *sd, s32 value); + int (*q_focus_status)(struct v4l2_subdev *sd, s32 *value); + int (*q_focus_abs)(struct v4l2_subdev *sd, s32 *value); +}; + +struct ov5693_resolution { + u8 *desc; + const struct ov5693_reg *regs; + int res; + int width; + int height; + int fps; + int pix_clk_freq; + u16 skip_frames; + u16 pixels_per_line; + u16 lines_per_frame; + u8 bin_factor_x; + u8 bin_factor_y; + u8 bin_mode; + bool used; +}; + +struct ov5693_control { + struct v4l2_queryctrl qc; + int (*query)(struct v4l2_subdev *sd, s32 *value); + int (*tweak)(struct v4l2_subdev *sd, s32 value); +}; + +/* + * ov5693 device structure. + */ +struct ov5693_device { + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_mbus_framefmt format; + struct mutex input_lock; + + struct camera_sensor_platform_data *platform_data; + struct ov5693_vcm *vcm_driver; + int fmt_idx; + u8 res; + u8 type; + + struct ov5693_resolution *ov5693_res; + int curr_res_num; + + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *run_mode; +}; + +enum ov5693_tok_type { + OV5693_8BIT = 0x0001, + OV5693_16BIT = 0x0002, + OV5693_32BIT = 0x0004, + OV5693_TOK_TERM = 0xf000, /* terminating token for reg list */ + OV5693_TOK_DELAY = 0xfe00, /* delay token for reg list */ + OV5693_TOK_MASK = 0xfff0 +}; + +/** + * struct ov5693_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 ov5693_reg { + enum ov5693_tok_type type; + u16 reg; + u32 val; /* @set value for read/mod/write, @mask */ +}; + +#define to_ov5693_sensor(x) container_of(x, struct ov5693_device, sd) + +#define OV5693_MAX_WRITE_BUF_SIZE 30 + +struct ov5693_write_buffer { + u16 addr; + u8 data[OV5693_MAX_WRITE_BUF_SIZE]; +}; + +struct ov5693_write_ctrl { + int index; + struct ov5693_write_buffer buffer; +}; + +static const struct i2c_device_id ov5693_id[] = { + {OV5693_NAME, 0}, + {} +}; + +/* ov5693 sensor initialization setting */ +static struct ov5693_reg const ov5693_init_setting[] = { + {OV5693_8BIT, 0x0103, 0x01}, + {OV5693_8BIT, 0x3001, 0x0a}, + {OV5693_8BIT, 0x3002, 0x80}, + {OV5693_8BIT, 0x3006, 0x00}, + {OV5693_8BIT, 0x3011, 0x21}, + {OV5693_8BIT, 0x3012, 0x09}, + {OV5693_8BIT, 0x3013, 0x10}, + {OV5693_8BIT, 0x3014, 0x00}, + {OV5693_8BIT, 0x3015, 0x08}, + {OV5693_8BIT, 0x3016, 0xf0}, + {OV5693_8BIT, 0x3017, 0xf0}, + {OV5693_8BIT, 0x3018, 0xf0}, + {OV5693_8BIT, 0x301b, 0xb4}, + {OV5693_8BIT, 0x301d, 0x02}, + {OV5693_8BIT, 0x3021, 0x00}, + {OV5693_8BIT, 0x3022, 0x01}, + {OV5693_8BIT, 0x3028, 0x44}, + {OV5693_8BIT, 0x3098, 0x02}, + {OV5693_8BIT, 0x3099, 0x19}, + {OV5693_8BIT, 0x309a, 0x02}, + {OV5693_8BIT, 0x309b, 0x01}, + {OV5693_8BIT, 0x309c, 0x00}, + {OV5693_8BIT, 0x30a0, 0xd2}, + {OV5693_8BIT, 0x30a2, 0x01}, + {OV5693_8BIT, 0x30b2, 0x00}, + {OV5693_8BIT, 0x30b3, 0x7d}, + {OV5693_8BIT, 0x30b4, 0x03}, + {OV5693_8BIT, 0x30b5, 0x04}, + {OV5693_8BIT, 0x30b6, 0x01}, + {OV5693_8BIT, 0x3104, 0x21}, + {OV5693_8BIT, 0x3106, 0x00}, + + /* Manual white balance */ + {OV5693_8BIT, 0x3400, 0x04}, + {OV5693_8BIT, 0x3401, 0x00}, + {OV5693_8BIT, 0x3402, 0x04}, + {OV5693_8BIT, 0x3403, 0x00}, + {OV5693_8BIT, 0x3404, 0x04}, + {OV5693_8BIT, 0x3405, 0x00}, + {OV5693_8BIT, 0x3406, 0x01}, + + /* Manual exposure control */ + {OV5693_8BIT, 0x3500, 0x00}, + {OV5693_8BIT, 0x3503, 0x07}, + {OV5693_8BIT, 0x3504, 0x00}, + {OV5693_8BIT, 0x3505, 0x00}, + {OV5693_8BIT, 0x3506, 0x00}, + {OV5693_8BIT, 0x3507, 0x02}, + {OV5693_8BIT, 0x3508, 0x00}, + + /* Manual gain control */ + {OV5693_8BIT, 0x3509, 0x10}, + {OV5693_8BIT, 0x350a, 0x00}, + {OV5693_8BIT, 0x350b, 0x40}, + + {OV5693_8BIT, 0x3601, 0x0a}, + {OV5693_8BIT, 0x3602, 0x38}, + {OV5693_8BIT, 0x3612, 0x80}, + {OV5693_8BIT, 0x3620, 0x54}, + {OV5693_8BIT, 0x3621, 0xc7}, + {OV5693_8BIT, 0x3622, 0x0f}, + {OV5693_8BIT, 0x3625, 0x10}, + {OV5693_8BIT, 0x3630, 0x55}, + {OV5693_8BIT, 0x3631, 0xf4}, + {OV5693_8BIT, 0x3632, 0x00}, + {OV5693_8BIT, 0x3633, 0x34}, + {OV5693_8BIT, 0x3634, 0x02}, + {OV5693_8BIT, 0x364d, 0x0d}, + {OV5693_8BIT, 0x364f, 0xdd}, + {OV5693_8BIT, 0x3660, 0x04}, + {OV5693_8BIT, 0x3662, 0x10}, + {OV5693_8BIT, 0x3663, 0xf1}, + {OV5693_8BIT, 0x3665, 0x00}, + {OV5693_8BIT, 0x3666, 0x20}, + {OV5693_8BIT, 0x3667, 0x00}, + {OV5693_8BIT, 0x366a, 0x80}, + {OV5693_8BIT, 0x3680, 0xe0}, + {OV5693_8BIT, 0x3681, 0x00}, + {OV5693_8BIT, 0x3700, 0x42}, + {OV5693_8BIT, 0x3701, 0x14}, + {OV5693_8BIT, 0x3702, 0xa0}, + {OV5693_8BIT, 0x3703, 0xd8}, + {OV5693_8BIT, 0x3704, 0x78}, + {OV5693_8BIT, 0x3705, 0x02}, + {OV5693_8BIT, 0x370a, 0x00}, + {OV5693_8BIT, 0x370b, 0x20}, + {OV5693_8BIT, 0x370c, 0x0c}, + {OV5693_8BIT, 0x370d, 0x11}, + {OV5693_8BIT, 0x370e, 0x00}, + {OV5693_8BIT, 0x370f, 0x40}, + {OV5693_8BIT, 0x3710, 0x00}, + {OV5693_8BIT, 0x371a, 0x1c}, + {OV5693_8BIT, 0x371b, 0x05}, + {OV5693_8BIT, 0x371c, 0x01}, + {OV5693_8BIT, 0x371e, 0xa1}, + {OV5693_8BIT, 0x371f, 0x0c}, + {OV5693_8BIT, 0x3721, 0x00}, + {OV5693_8BIT, 0x3724, 0x10}, + {OV5693_8BIT, 0x3726, 0x00}, + {OV5693_8BIT, 0x372a, 0x01}, + {OV5693_8BIT, 0x3730, 0x10}, + {OV5693_8BIT, 0x3738, 0x22}, + {OV5693_8BIT, 0x3739, 0xe5}, + {OV5693_8BIT, 0x373a, 0x50}, + {OV5693_8BIT, 0x373b, 0x02}, + {OV5693_8BIT, 0x373c, 0x41}, + {OV5693_8BIT, 0x373f, 0x02}, + {OV5693_8BIT, 0x3740, 0x42}, + {OV5693_8BIT, 0x3741, 0x02}, + {OV5693_8BIT, 0x3742, 0x18}, + {OV5693_8BIT, 0x3743, 0x01}, + {OV5693_8BIT, 0x3744, 0x02}, + {OV5693_8BIT, 0x3747, 0x10}, + {OV5693_8BIT, 0x374c, 0x04}, + {OV5693_8BIT, 0x3751, 0xf0}, + {OV5693_8BIT, 0x3752, 0x00}, + {OV5693_8BIT, 0x3753, 0x00}, + {OV5693_8BIT, 0x3754, 0xc0}, + {OV5693_8BIT, 0x3755, 0x00}, + {OV5693_8BIT, 0x3756, 0x1a}, + {OV5693_8BIT, 0x3758, 0x00}, + {OV5693_8BIT, 0x3759, 0x0f}, + {OV5693_8BIT, 0x376b, 0x44}, + {OV5693_8BIT, 0x375c, 0x04}, + {OV5693_8BIT, 0x3774, 0x10}, + {OV5693_8BIT, 0x3776, 0x00}, + {OV5693_8BIT, 0x377f, 0x08}, + {OV5693_8BIT, 0x3780, 0x22}, + {OV5693_8BIT, 0x3781, 0x0c}, + {OV5693_8BIT, 0x3784, 0x2c}, + {OV5693_8BIT, 0x3785, 0x1e}, + {OV5693_8BIT, 0x378f, 0xf5}, + {OV5693_8BIT, 0x3791, 0xb0}, + {OV5693_8BIT, 0x3795, 0x00}, + {OV5693_8BIT, 0x3796, 0x64}, + {OV5693_8BIT, 0x3797, 0x11}, + {OV5693_8BIT, 0x3798, 0x30}, + {OV5693_8BIT, 0x3799, 0x41}, + {OV5693_8BIT, 0x379a, 0x07}, + {OV5693_8BIT, 0x379b, 0xb0}, + {OV5693_8BIT, 0x379c, 0x0c}, + {OV5693_8BIT, 0x37c5, 0x00}, + {OV5693_8BIT, 0x37c6, 0x00}, + {OV5693_8BIT, 0x37c7, 0x00}, + {OV5693_8BIT, 0x37c9, 0x00}, + {OV5693_8BIT, 0x37ca, 0x00}, + {OV5693_8BIT, 0x37cb, 0x00}, + {OV5693_8BIT, 0x37de, 0x00}, + {OV5693_8BIT, 0x37df, 0x00}, + {OV5693_8BIT, 0x3800, 0x00}, + {OV5693_8BIT, 0x3801, 0x00}, + {OV5693_8BIT, 0x3802, 0x00}, + {OV5693_8BIT, 0x3804, 0x0a}, + {OV5693_8BIT, 0x3805, 0x3f}, + {OV5693_8BIT, 0x3810, 0x00}, + {OV5693_8BIT, 0x3812, 0x00}, + {OV5693_8BIT, 0x3823, 0x00}, + {OV5693_8BIT, 0x3824, 0x00}, + {OV5693_8BIT, 0x3825, 0x00}, + {OV5693_8BIT, 0x3826, 0x00}, + {OV5693_8BIT, 0x3827, 0x00}, + {OV5693_8BIT, 0x382a, 0x04}, + {OV5693_8BIT, 0x3a04, 0x06}, + {OV5693_8BIT, 0x3a05, 0x14}, + {OV5693_8BIT, 0x3a06, 0x00}, + {OV5693_8BIT, 0x3a07, 0xfe}, + {OV5693_8BIT, 0x3b00, 0x00}, + {OV5693_8BIT, 0x3b02, 0x00}, + {OV5693_8BIT, 0x3b03, 0x00}, + {OV5693_8BIT, 0x3b04, 0x00}, + {OV5693_8BIT, 0x3b05, 0x00}, + {OV5693_8BIT, 0x3e07, 0x20}, + {OV5693_8BIT, 0x4000, 0x08}, + {OV5693_8BIT, 0x4001, 0x04}, + {OV5693_8BIT, 0x4002, 0x45}, + {OV5693_8BIT, 0x4004, 0x08}, + {OV5693_8BIT, 0x4005, 0x18}, + {OV5693_8BIT, 0x4006, 0x20}, + {OV5693_8BIT, 0x4008, 0x24}, + {OV5693_8BIT, 0x4009, 0x10}, + {OV5693_8BIT, 0x400c, 0x00}, + {OV5693_8BIT, 0x400d, 0x00}, + {OV5693_8BIT, 0x4058, 0x00}, + {OV5693_8BIT, 0x404e, 0x37}, + {OV5693_8BIT, 0x404f, 0x8f}, + {OV5693_8BIT, 0x4058, 0x00}, + {OV5693_8BIT, 0x4101, 0xb2}, + {OV5693_8BIT, 0x4303, 0x00}, + {OV5693_8BIT, 0x4304, 0x08}, + {OV5693_8BIT, 0x4307, 0x30}, + {OV5693_8BIT, 0x4311, 0x04}, + {OV5693_8BIT, 0x4315, 0x01}, + {OV5693_8BIT, 0x4511, 0x05}, + {OV5693_8BIT, 0x4512, 0x01}, + {OV5693_8BIT, 0x4806, 0x00}, + {OV5693_8BIT, 0x4816, 0x52}, + {OV5693_8BIT, 0x481f, 0x30}, + {OV5693_8BIT, 0x4826, 0x2c}, + {OV5693_8BIT, 0x4831, 0x64}, + {OV5693_8BIT, 0x4d00, 0x04}, + {OV5693_8BIT, 0x4d01, 0x71}, + {OV5693_8BIT, 0x4d02, 0xfd}, + {OV5693_8BIT, 0x4d03, 0xf5}, + {OV5693_8BIT, 0x4d04, 0x0c}, + {OV5693_8BIT, 0x4d05, 0xcc}, + {OV5693_8BIT, 0x4837, 0x0a}, + {OV5693_8BIT, 0x5000, 0x06}, + {OV5693_8BIT, 0x5001, 0x01}, + {OV5693_8BIT, 0x5003, 0x20}, + {OV5693_8BIT, 0x5046, 0x0a}, + {OV5693_8BIT, 0x5013, 0x00}, + {OV5693_8BIT, 0x5046, 0x0a}, + {OV5693_8BIT, 0x5780, 0x1c}, + {OV5693_8BIT, 0x5786, 0x20}, + {OV5693_8BIT, 0x5787, 0x10}, + {OV5693_8BIT, 0x5788, 0x18}, + {OV5693_8BIT, 0x578a, 0x04}, + {OV5693_8BIT, 0x578b, 0x02}, + {OV5693_8BIT, 0x578c, 0x02}, + {OV5693_8BIT, 0x578e, 0x06}, + {OV5693_8BIT, 0x578f, 0x02}, + {OV5693_8BIT, 0x5790, 0x02}, + {OV5693_8BIT, 0x5791, 0xff}, + {OV5693_8BIT, 0x5842, 0x01}, + {OV5693_8BIT, 0x5843, 0x2b}, + {OV5693_8BIT, 0x5844, 0x01}, + {OV5693_8BIT, 0x5845, 0x92}, + {OV5693_8BIT, 0x5846, 0x01}, + {OV5693_8BIT, 0x5847, 0x8f}, + {OV5693_8BIT, 0x5848, 0x01}, + {OV5693_8BIT, 0x5849, 0x0c}, + {OV5693_8BIT, 0x5e00, 0x00}, + {OV5693_8BIT, 0x5e10, 0x0c}, + {OV5693_8BIT, 0x0100, 0x00}, + {OV5693_TOK_TERM, 0, 0} +}; + +/* + * Register settings for various resolution + */ + +/* +------------------------------------ +@@ FULL QSXGA (2592x1944) 15fps 33.33ms VBlank 2lane 10Bit +100 99 2592 1944 +100 98 1 0 +102 3601 bb8 ;Pather tool use only +c8 1 f2 ; New FPGA Board +c8 20 22 ; New FPGA Board +c8 10 42 ; MIPI DFGA CYCY3 Board use only +; +c8 f 32 ; input clock to 19.2MHz +; +; OV5690 setting version History +; +; +; V18b +*/ +static struct ov5693_reg const ov5693_5M_15fps[] = { + {OV5693_8BIT, 0x3501, 0x7b}, + {OV5693_8BIT, 0x3502, 0x00}, + {OV5693_8BIT, 0x3708, 0xe2}, + {OV5693_8BIT, 0x3709, 0xc3}, + {OV5693_8BIT, 0x3800, 0x00}, /* x_addr_start: 0 */ + {OV5693_8BIT, 0x3801, 0x00}, + {OV5693_8BIT, 0x3802, 0x00}, /* y_addr_start: 0 */ + {OV5693_8BIT, 0x3803, 0x00}, + {OV5693_8BIT, 0x3804, 0x0a}, /* x_addr_end: 2623 */ + {OV5693_8BIT, 0x3805, 0x3f}, + {OV5693_8BIT, 0x3806, 0x07}, /* y_addr_end: 1955 */ + {OV5693_8BIT, 0x3807, 0xa3}, + {OV5693_8BIT, 0x3808, 0x0a}, /* x output size: 2592 */ + {OV5693_8BIT, 0x3809, 0x20}, + {OV5693_8BIT, 0x380a, 0x07}, /* y output size: 1944 */ + {OV5693_8BIT, 0x380b, 0x98}, + {OV5693_8BIT, 0x380c, 0x0e}, /* total x output size: 3688 */ + {OV5693_8BIT, 0x380d, 0x68}, + {OV5693_8BIT, 0x380e, 0x0f}, /* total y output size: 3968 */ + {OV5693_8BIT, 0x380f, 0x80}, + {OV5693_8BIT, 0x3810, 0x00}, /* x offset: 16 */ + {OV5693_8BIT, 0x3811, 0x10}, + {OV5693_8BIT, 0x3812, 0x00}, /* y offset: 6 */ + {OV5693_8BIT, 0x3813, 0x06}, + {OV5693_8BIT, 0x3814, 0x11}, + {OV5693_8BIT, 0x3815, 0x11}, + {OV5693_8BIT, 0x3820, 0x00}, + {OV5693_8BIT, 0x3821, 0x1e}, + {OV5693_8BIT, 0x5002, 0x00}, + {OV5693_TOK_TERM, 0, 0} +}; + +/* +@@ OV5693 1940x1096 30fps 8.8ms VBlanking 2lane 10Bit(Scaling) +100 99 1940 1096 +100 98 1 0 +102 3601 bb8 ;Pather tool use only +102 40 0 ; HDR Mode off +c8 1 f2 ; New FPGA Board +c8 20 22 ; New FPGA Board +c8 10 42 ; MIPI DFGA CYCY3 Board use only +; +c8 f 32 ; input clock to 19.2MHz +; +; OV5690 setting version History +; +; +; V18b +*/ +static struct ov5693_reg const ov5693_1080p_30fps[] = { + {OV5693_8BIT, 0x3501, 0x7b}, + {OV5693_8BIT, 0x3502, 0x00}, + {OV5693_8BIT, 0x3708, 0xe2}, + {OV5693_8BIT, 0x3709, 0xc3}, + {OV5693_8BIT, 0x3800, 0x00}, /* x_addr_start: 0 */ + {OV5693_8BIT, 0x3801, 0x00}, + {OV5693_8BIT, 0x3802, 0x00}, /* y_addr_start: 240 */ + {OV5693_8BIT, 0x3803, 0xf0}, + {OV5693_8BIT, 0x3804, 0x0a}, /* x_addr_end: 2591 */ + {OV5693_8BIT, 0x3805, 0x1f}, + {OV5693_8BIT, 0x3806, 0x06}, /* y_addr_end: 1703 */ + {OV5693_8BIT, 0x3807, 0xa7}, + {OV5693_8BIT, 0x3808, 0x07}, /* x output size: 1940 */ + {OV5693_8BIT, 0x3809, 0x94}, + {OV5693_8BIT, 0x380a, 0x04}, /* y output size: 1096 */ + {OV5693_8BIT, 0x380b, 0x48}, + {OV5693_8BIT, 0x380c, 0x0e}, /* total x output size: 3688 */ + {OV5693_8BIT, 0x380d, 0x68}, + {OV5693_8BIT, 0x380e, 0x0b}, /* total y output size: 2984 */ + {OV5693_8BIT, 0x380f, 0xa8}, + {OV5693_8BIT, 0x3810, 0x00}, /* x offset: 2 */ + {OV5693_8BIT, 0x3811, 0x02}, + {OV5693_8BIT, 0x3812, 0x00}, /* y offset: 2 */ + {OV5693_8BIT, 0x3813, 0x02}, + {OV5693_8BIT, 0x3814, 0x11}, + {OV5693_8BIT, 0x3815, 0x11}, + {OV5693_8BIT, 0x3820, 0x00}, + {OV5693_8BIT, 0x3821, 0x1e}, + {OV5693_8BIT, 0x5002, 0x80}, + {OV5693_TOK_TERM, 0, 0} +}; + +/* + * 1296x736 30fps 8.8ms VBlanking 2lane 10Bit (Scaling) + */ +static struct ov5693_reg const ov5693_720p_30fps[] = { + {OV5693_8BIT, 0x3501, 0x3d}, + {OV5693_8BIT, 0x3502, 0x00}, + {OV5693_8BIT, 0x3708, 0xe6}, + {OV5693_8BIT, 0x3709, 0xc7}, + {OV5693_8BIT, 0x3800, 0x00}, /* x_addr_start: 0 */ + {OV5693_8BIT, 0x3801, 0x00}, + {OV5693_8BIT, 0x3802, 0x00}, /* y_addr_start: 0 */ + {OV5693_8BIT, 0x3803, 0x00}, + {OV5693_8BIT, 0x3804, 0x0a}, /* x_addr_end: 2623 */ + {OV5693_8BIT, 0x3805, 0x3f}, + {OV5693_8BIT, 0x3806, 0x07}, /* y_addr_end: 1955 */ + {OV5693_8BIT, 0x3807, 0xa3}, + {OV5693_8BIT, 0x3808, 0x05}, /* x output size: 1296 */ + {OV5693_8BIT, 0x3809, 0x10}, + {OV5693_8BIT, 0x380a, 0x02}, /* y output size: 736 */ + {OV5693_8BIT, 0x380b, 0xe0}, + {OV5693_8BIT, 0x380c, 0x0a}, /* total x output size: 2688 */ + {OV5693_8BIT, 0x380d, 0x80}, + {OV5693_8BIT, 0x380e, 0x07}, /* total y output size: 1984 */ + {OV5693_8BIT, 0x380f, 0xc0}, + {OV5693_8BIT, 0x3810, 0x00}, /* x offset: 8 */ + {OV5693_8BIT, 0x3811, 0x08}, + {OV5693_8BIT, 0x3812, 0x00}, /* y offset: 2 */ + {OV5693_8BIT, 0x3813, 0x02}, + {OV5693_8BIT, 0x3814, 0x31}, + {OV5693_8BIT, 0x3815, 0x31}, + {OV5693_8BIT, 0x3820, 0x04}, + {OV5693_8BIT, 0x3821, 0x1f}, + {OV5693_8BIT, 0x5002, 0x80}, + {OV5693_TOK_TERM, 0, 0} +}; + +/* + * 736x496 30fps 8.8ms VBlanking 2lane 10Bit (Scaling) + */ +static struct ov5693_reg const ov5693_480p_30fps[] = { + {OV5693_8BIT, 0x3501, 0x3d}, + {OV5693_8BIT, 0x3502, 0x00}, + {OV5693_8BIT, 0x3708, 0xe6}, + {OV5693_8BIT, 0x3709, 0xc7}, + {OV5693_8BIT, 0x3800, 0x00}, /* x_addr_start: 0 */ + {OV5693_8BIT, 0x3801, 0x00}, + {OV5693_8BIT, 0x3802, 0x00}, /* y_addr_start: 7 */ + {OV5693_8BIT, 0x3803, 0x07}, + {OV5693_8BIT, 0x3804, 0x0a}, /* x_addr_end: 2623 */ + {OV5693_8BIT, 0x3805, 0x3f}, + {OV5693_8BIT, 0x3806, 0x07}, /* y_addr_end: 1955 */ + {OV5693_8BIT, 0x3807, 0xa3}, + {OV5693_8BIT, 0x3808, 0x02}, /* x output size: 736 */ + {OV5693_8BIT, 0x3809, 0xe0}, + {OV5693_8BIT, 0x380a, 0x01}, /* y output size: 496 */ + {OV5693_8BIT, 0x380b, 0xf0}, + {OV5693_8BIT, 0x380c, 0x0a}, /* total x output size: 2688 */ + {OV5693_8BIT, 0x380d, 0x80}, + {OV5693_8BIT, 0x380e, 0x07}, /* total y output size: 1984 */ + {OV5693_8BIT, 0x380f, 0xc0}, + {OV5693_8BIT, 0x3810, 0x00}, /* x offset: 8 */ + {OV5693_8BIT, 0x3811, 0x08}, + {OV5693_8BIT, 0x3812, 0x00}, /* y offset: 2 */ + {OV5693_8BIT, 0x3813, 0x02}, + {OV5693_8BIT, 0x3814, 0x31}, + {OV5693_8BIT, 0x3815, 0x31}, + {OV5693_8BIT, 0x3820, 0x04}, + {OV5693_8BIT, 0x3821, 0x1f}, + {OV5693_8BIT, 0x5002, 0x80}, + {OV5693_TOK_TERM, 0, 0} +}; + +/* +@@ OV5693 656x496 30fps 17ms VBlanking 2lane 10Bit(Scaling) +100 99 656 496 +100 98 1 0 +102 3601 BB8 ;Pather tool use only +c8 1 f2 ; New FPGA Board +c8 20 22 ; New FPGA Board +; OV5690 setting version History +; +c8 f 32 ; input clock to 19.2MHz +; +; V18b +*/ +static struct ov5693_reg const ov5693_VGA_30fps[] = { + {OV5693_8BIT, 0x3501, 0x3d}, + {OV5693_8BIT, 0x3502, 0x00}, + {OV5693_8BIT, 0x3708, 0xe6}, + {OV5693_8BIT, 0x3709, 0xc7}, + {OV5693_8BIT, 0x3800, 0x00}, /* x_addr_start: 0 */ + {OV5693_8BIT, 0x3801, 0x00}, + {OV5693_8BIT, 0x3802, 0x00}, /* y_addr_start: 0 */ + {OV5693_8BIT, 0x3803, 0x00}, + {OV5693_8BIT, 0x3804, 0x0a}, /* x_addr_end: 2623 */ + {OV5693_8BIT, 0x3805, 0x3f}, + {OV5693_8BIT, 0x3806, 0x07}, /* y_addr_end: 1955 */ + {OV5693_8BIT, 0x3807, 0xa3}, + {OV5693_8BIT, 0x3808, 0x02}, /* x output size: 656 */ + {OV5693_8BIT, 0x3809, 0x90}, + {OV5693_8BIT, 0x380a, 0x01}, /* y output size: 496 */ + {OV5693_8BIT, 0x380b, 0xf0}, + {OV5693_8BIT, 0x380c, 0x0a}, /* total x output size: 2688 */ + {OV5693_8BIT, 0x380d, 0x80}, + {OV5693_8BIT, 0x380e, 0x07}, /* total y output size: 1984 */ + {OV5693_8BIT, 0x380f, 0xc0}, + {OV5693_8BIT, 0x3810, 0x00}, /* x offset: 13 */ + {OV5693_8BIT, 0x3811, 0x0d}, + {OV5693_8BIT, 0x3812, 0x00}, /* y offset: 3 */ + {OV5693_8BIT, 0x3813, 0x03}, + {OV5693_8BIT, 0x3814, 0x31}, + {OV5693_8BIT, 0x3815, 0x31}, + {OV5693_8BIT, 0x3820, 0x04}, + {OV5693_8BIT, 0x3821, 0x1f}, + {OV5693_8BIT, 0x5002, 0x80}, + {OV5693_TOK_TERM, 0, 0} +}; + +struct ov5693_resolution ov5693_res_preview[] = { + { + .desc = "ov5693_VGA_30fps", + .width = 656, + .height = 496, + .fps = 30, + .pix_clk_freq = 81, + .used = 0, + .pixels_per_line = 2688, + .lines_per_frame = 1984, + .bin_factor_x = 2, + .bin_factor_y = 2, + .bin_mode = 0, + .skip_frames = 3, + .regs = ov5693_VGA_30fps, + }, + { + .desc = "ov5693_1080P_30fps", + .width = 1940, + .height = 1096, + .fps = 30, + .pix_clk_freq = 81, + .used = 0, + .pixels_per_line = 3688, + .lines_per_frame = 2984, + .bin_factor_x = 0, + .bin_factor_y = 0, + .bin_mode = 0, + .skip_frames = 3, + .regs = ov5693_1080p_30fps, + }, + { + .desc = "ov5693_5M_15fps", + .width = 2592, + .height = 1944, + .fps = 15, + .pix_clk_freq = 81, + .used = 0, + .pixels_per_line = 3688, + .lines_per_frame = 3968, + .bin_factor_x = 0, + .bin_factor_y = 0, + .bin_mode = 0, + .skip_frames = 3, + .regs = ov5693_5M_15fps, + }, +}; +#define N_RES_PREVIEW (ARRAY_SIZE(ov5693_res_preview)) + +struct ov5693_resolution ov5693_res_still[] = { + { + .desc = "ov5693_VGA_30fps", + .width = 656, + .height = 496, + .fps = 30, + .pix_clk_freq = 81, + .used = 0, + .pixels_per_line = 2688, + .lines_per_frame = 1984, + .bin_factor_x = 2, + .bin_factor_y = 2, + .bin_mode = 0, + .skip_frames = 3, + .regs = ov5693_VGA_30fps, + }, + { + .desc = "ov5693_1080P_30fps", + .width = 1940, + .height = 1096, + .fps = 30, + .pix_clk_freq = 81, + .used = 0, + .pixels_per_line = 3688, + .lines_per_frame = 2984, + .bin_factor_x = 0, + .bin_factor_y = 0, + .bin_mode = 0, + .skip_frames = 3, + .regs = ov5693_1080p_30fps, + }, + { + .desc = "ov5693_5M_15fps", + .width = 2592, + .height = 1944, + .fps = 15, + .pix_clk_freq = 81, + .used = 0, + .pixels_per_line = 3688, + .lines_per_frame = 3968, + .bin_factor_x = 0, + .bin_factor_y = 0, + .bin_mode = 0, + .skip_frames = 3, + .regs = ov5693_5M_15fps, + }, +}; +#define N_RES_STILL (ARRAY_SIZE(ov5693_res_still)) + +struct ov5693_resolution ov5693_res_video[] = { + { + .desc = "ov5693_VGA_30fps", + .width = 656, + .height = 496, + .fps = 30, + .pix_clk_freq = 81, + .used = 0, + .pixels_per_line = 2688, + .lines_per_frame = 1984, + .bin_factor_x = 2, + .bin_factor_y = 2, + .bin_mode = 0, + .skip_frames = 3, + .regs = ov5693_VGA_30fps, + }, + { + .desc = "ov5693_480P_30fps", + .width = 736, + .height = 496, + .fps = 30, + .pix_clk_freq = 81, + .used = 0, + .pixels_per_line = 2688, + .lines_per_frame = 1984, + .bin_factor_x = 2, + .bin_factor_y = 2, + .bin_mode = 0, + .skip_frames = 1, + .regs = ov5693_480p_30fps, + }, + { + .desc = "ov5693_720p_30fps", + .width = 1296, + .height = 736, + .fps = 30, + .pix_clk_freq = 81, + .used = 0, + .pixels_per_line = 2688, + .lines_per_frame = 1984, + .bin_factor_x = 2, + .bin_factor_y = 2, + .bin_mode = 0, + .skip_frames = 1, + .regs = ov5693_720p_30fps, + }, + { + .desc = "ov5693_1080P_30fps", + .width = 1940, + .height = 1096, + .fps = 30, + .pix_clk_freq = 81, + .used = 0, + .pixels_per_line = 3688, + .lines_per_frame = 2984, + .bin_factor_x = 0, + .bin_factor_y = 0, + .bin_mode = 0, + .skip_frames = 3, + .regs = ov5693_1080p_30fps, + }, +}; +#define N_RES_VIDEO (ARRAY_SIZE(ov5693_res_video)) + +struct ov5693_vcm ov5693_vcm_ops = { + .power_up = ad5823_vcm_power_up, + .power_down = ad5823_vcm_power_down, + .init = ad5823_vcm_init, + .t_focus_vcm = ad5823_t_focus_vcm, + .t_focus_abs = ad5823_t_focus_abs, + .t_focus_rel = ad5823_t_focus_rel, + .q_focus_status = ad5823_q_focus_status, + .q_focus_abs = ad5823_q_focus_abs, +}; +#endif -- 2.17.1