From bd675aa1a397792128800ac6176c34eea389a127 Mon Sep 17 00:00:00 2001 From: "dalong.zhang" Date: Sun, 9 Oct 2016 16:38:35 +0800 Subject: [PATCH] camera: rockchip: porting ov8858&ov4689&ov2710 driver Change-Id: Ia9a0a39d347a9fd506f493c6639785267233e0ac Signed-off-by: dalong.zhang --- drivers/media/i2c/soc_camera/Kconfig | 2 + drivers/media/i2c/soc_camera/Makefile | 1 + drivers/media/i2c/soc_camera/rockchip/Kconfig | 27 + .../media/i2c/soc_camera/rockchip/Makefile | 4 + .../rockchip/aptina_camera_module.c | 1428 +++++++++++ .../rockchip/aptina_camera_module.h | 278 +++ .../rockchip/imx323_v4l2-i2c-subdev.c | 613 +++++ .../soc_camera/rockchip/imx_camera_module.c | 1108 +++++++++ .../soc_camera/rockchip/imx_camera_module.h | 278 +++ .../rockchip/ov2710_v4l2-i2c-subdev.c | 915 +++++++ .../rockchip/ov4689_v4l2-i2c-subdev.c | 949 +++++++ .../rockchip/ov8858_v4l2-i2c-subdev.c | 2175 +++++++++++++++++ .../soc_camera/rockchip/ov_camera_module.c | 1127 +++++++++ .../soc_camera/rockchip/ov_camera_module.h | 285 +++ .../soc_camera/rockchip/rk_camera_module.c | 1630 ++++++++++++ .../rockchip/rk_camera_module_version.h | 32 + 16 files changed, 10852 insertions(+) create mode 100644 drivers/media/i2c/soc_camera/rockchip/Kconfig create mode 100644 drivers/media/i2c/soc_camera/rockchip/Makefile create mode 100644 drivers/media/i2c/soc_camera/rockchip/aptina_camera_module.c create mode 100644 drivers/media/i2c/soc_camera/rockchip/aptina_camera_module.h create mode 100644 drivers/media/i2c/soc_camera/rockchip/imx323_v4l2-i2c-subdev.c create mode 100644 drivers/media/i2c/soc_camera/rockchip/imx_camera_module.c create mode 100644 drivers/media/i2c/soc_camera/rockchip/imx_camera_module.h create mode 100644 drivers/media/i2c/soc_camera/rockchip/ov2710_v4l2-i2c-subdev.c create mode 100644 drivers/media/i2c/soc_camera/rockchip/ov4689_v4l2-i2c-subdev.c create mode 100644 drivers/media/i2c/soc_camera/rockchip/ov8858_v4l2-i2c-subdev.c create mode 100644 drivers/media/i2c/soc_camera/rockchip/ov_camera_module.c create mode 100644 drivers/media/i2c/soc_camera/rockchip/ov_camera_module.h create mode 100644 drivers/media/i2c/soc_camera/rockchip/rk_camera_module.c create mode 100644 drivers/media/i2c/soc_camera/rockchip/rk_camera_module_version.h diff --git a/drivers/media/i2c/soc_camera/Kconfig b/drivers/media/i2c/soc_camera/Kconfig index 23d352f0adf0..de785db82a74 100644 --- a/drivers/media/i2c/soc_camera/Kconfig +++ b/drivers/media/i2c/soc_camera/Kconfig @@ -85,3 +85,5 @@ config SOC_CAMERA_TW9910 depends on SOC_CAMERA && I2C help This is a tw9910 video driver + +source "drivers/media/i2c/soc_camera/rockchip/Kconfig" \ No newline at end of file diff --git a/drivers/media/i2c/soc_camera/Makefile b/drivers/media/i2c/soc_camera/Makefile index d0421feaa796..b1394116d2aa 100644 --- a/drivers/media/i2c/soc_camera/Makefile +++ b/drivers/media/i2c/soc_camera/Makefile @@ -12,3 +12,4 @@ obj-$(CONFIG_SOC_CAMERA_OV9640) += ov9640.o obj-$(CONFIG_SOC_CAMERA_OV9740) += ov9740.o obj-$(CONFIG_SOC_CAMERA_RJ54N1) += rj54n1cb0c.o obj-$(CONFIG_SOC_CAMERA_TW9910) += tw9910.o +obj-y += rockchip/ diff --git a/drivers/media/i2c/soc_camera/rockchip/Kconfig b/drivers/media/i2c/soc_camera/rockchip/Kconfig new file mode 100644 index 000000000000..168328941f0d --- /dev/null +++ b/drivers/media/i2c/soc_camera/rockchip/Kconfig @@ -0,0 +1,27 @@ +config VIDEO_ov8858 + tristate "ov8858 driver adapt to rockchip cif isp platform" + depends on VIDEO_V4L2 && VIDEO_RK_CIF_ISP10 && I2C + default n + ---help--- + This is ov8858 camera driver adapt to rockchip cif isp platform. + +config VIDEO_ov2710 + tristate "ov2710 driver adapt to rockchip cif isp platform" + depends on VIDEO_V4L2 && VIDEO_RK_CIF_ISP10 && I2C + default n + ---help--- + This is ov2710 camera driver adapt to rockchip cif isp platform. + +config VIDEO_ov4689 + tristate "ov4689 driver adapt to rockchip cif isp platform" + depends on VIDEO_V4L2 && VIDEO_RK_CIF_ISP10 && I2C + default n + ---help--- + This is ov8858 camera driver adapt to rockchip cif isp platform. + +config VIDEO_imx323 + tristate "imx323 driver adapt to rockchip cif isp platform" + depends on VIDEO_V4L2 && VIDEO_RK_CIF_ISP10 && I2C + default n + ---help--- + This is imx323 camera driver adapt to rockchip cif isp platform. diff --git a/drivers/media/i2c/soc_camera/rockchip/Makefile b/drivers/media/i2c/soc_camera/rockchip/Makefile new file mode 100644 index 000000000000..3c89fb64e3e5 --- /dev/null +++ b/drivers/media/i2c/soc_camera/rockchip/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_VIDEO_ov8858) += ov_camera_module.o rk_camera_module.o ov8858_v4l2-i2c-subdev.o +obj-$(CONFIG_VIDEO_ov2710) += ov_camera_module.o rk_camera_module.o ov2710_v4l2-i2c-subdev.o +obj-$(CONFIG_VIDEO_ov4689) += ov_camera_module.o rk_camera_module.o ov4689_v4l2-i2c-subdev.o +obj-$(CONFIG_VIDEO_imx323) += imx_camera_module.o rk_camera_module.o imx323_v4l2-i2c-subdev.o diff --git a/drivers/media/i2c/soc_camera/rockchip/aptina_camera_module.c b/drivers/media/i2c/soc_camera/rockchip/aptina_camera_module.c new file mode 100644 index 000000000000..9782de363a8b --- /dev/null +++ b/drivers/media/i2c/soc_camera/rockchip/aptina_camera_module.c @@ -0,0 +1,1428 @@ +/* + * aptina_camera_module.c + * + * Generic galaxycore sensor driver + * + * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd. + * + * Copyright (C) 2012-2014 Intel Mobile Communications GmbH + * + * Copyright (C) 2008 Texas Instruments. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "aptina_camera_module.h" + +#define I2C_M_WR 0 +#define I2C_MSG_MAX 300 +#define I2C_DATA_MAX (I2C_MSG_MAX * 3) + +/* ======================================================================== */ + +struct aptina_camera_module *to_aptina_camera_module(struct v4l2_subdev *sd) +{ + return container_of(sd, struct aptina_camera_module, sd); +} + +/* ======================================================================== */ + +void aptina_camera_module_reset( + struct aptina_camera_module *cam_mod) +{ + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + + cam_mod->inited = false; + cam_mod->active_config = NULL; + cam_mod->update_config = true; + cam_mod->frm_fmt_valid = false; + cam_mod->frm_intrvl_valid = false; + cam_mod->exp_config.auto_exp = false; + cam_mod->exp_config.auto_gain = false; + cam_mod->wb_config.auto_wb = false; + cam_mod->hflip = false; + cam_mod->vflip = false; + cam_mod->auto_adjust_fps = true; + cam_mod->rotation = 0; + cam_mod->ctrl_updt = 0; + cam_mod->state = APTINA_CAMERA_MODULE_POWER_OFF; + cam_mod->state_before_suspend = APTINA_CAMERA_MODULE_POWER_OFF; +} + +/* ======================================================================== */ + +int aptina_read_i2c_reg( + struct v4l2_subdev *sd, + u16 data_length, + u16 reg, + u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + struct i2c_msg msg[1]; + unsigned char data[2] = { 0, 0}; + + if (!client->adapter) { + pltfrm_camera_module_pr_err(sd, "client->adapter NULL\n"); + return -ENODEV; + } + + msg->addr = client->addr; + msg->flags = I2C_M_WR; + msg->len = 2; + msg->buf = data; + + /* High byte goes out first */ + data[0] = (u8)((reg >> 8) & 0xff); + data[1] = (u8)(reg & 0xff); + + ret = i2c_transfer(client->adapter, msg, 1); + if (ret >= 0) { + mdelay(3); + msg->flags = I2C_M_RD; + msg->len = data_length; + i2c_transfer(client->adapter, msg, 1); + } + if (ret >= 0) { + *val = 0; + /* High byte comes first */ + if (data_length == 1) + *val = data[0]; + else if (data_length == 2) + *val = data[1] + (data[0] << 8); + else + ; + + return 0; + } + pltfrm_camera_module_pr_err(sd, + "i2c read from offset 0x%08x failed with error %d\n", reg, ret); + return ret; +} + +/* ======================================================================== */ + +int aptina_write_i2c_reg( + struct v4l2_subdev *sd, + u16 reg, u16 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + struct i2c_msg msg[1]; + unsigned char data[4] = {0, 0, 0, 0}; + int retries; + + if (!client->adapter) { + pltfrm_camera_module_pr_err(sd, "client->adapter NULL\n"); + return -ENODEV; + } + + for (retries = 0; retries < 5; retries++) { + msg->addr = client->addr; + msg->flags = I2C_M_WR; + msg->len = 4; + msg->buf = data; + + /* high byte goes out first */ + data[0] = (u8)((reg >> 8) & 0xff); + data[1] = (u8)(reg & 0xff); + data[2] = (u8)((val >> 8) & 0xff); + data[3] = (u8)(val & 0xff); + + ret = i2c_transfer(client->adapter, msg, 1); + usleep_range(20, 50); + + if (ret == 1) + return 0; + + pltfrm_camera_module_pr_debug(sd, + "retrying I2C... %d\n", retries); + retries++; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(20)); + } + + pltfrm_camera_module_pr_err(sd, + "i2c write to offset 0x%08x failed with error %d\n", reg, ret); + return ret; +} + +/* ======================================================================== */ + +int aptina_write_reglist( + struct v4l2_subdev *sd, + const struct pltfrm_camera_module_reg reglist[], + int len) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + unsigned int k = 0, j = 0; + int i = 0; + struct i2c_msg *msg; + unsigned char *data; + unsigned int max_entries = len; + + msg = kmalloc((sizeof(struct i2c_msg) * I2C_MSG_MAX), GFP_KERNEL); + if (!msg) + return -ENOMEM; + data = kmalloc((sizeof(unsigned char) * I2C_DATA_MAX), GFP_KERNEL); + if (!data) { + kfree(msg); + return -ENOMEM; + } + + for (i = 0; i < max_entries; i++) { + switch (reglist[i].flag) { + case PLTFRM_CAMERA_MODULE_REG_TYPE_DATA: + (msg + j)->addr = client->addr; + (msg + j)->flags = I2C_M_WR; + (msg + j)->len = 4; + (msg + j)->buf = (data + k); + + data[k + 0] = (u8)((reglist[i].reg >> 8) & 0xFF); + data[k + 1] = (u8)(reglist[i].reg & 0xFF); + data[k + 2] = (u8)((reglist[i].val >> 8) & 0xFF); + data[k + 3] = (u8)(reglist[i].val & 0xFF); + + k = k + 4; + j++; + if (j == (I2C_MSG_MAX - 1)) { + /* Bulk I2C transfer */ + pltfrm_camera_module_pr_err(sd, + "messages transfers 1 0x%p msg %d bytes %d\n", + msg, j, k); + ret = i2c_transfer(client->adapter, msg, j); + if (ret < 0) { + pltfrm_camera_module_pr_err(sd, + "i2c transfer returned with err %d\n", + ret); + kfree(msg); + kfree(data); + return ret; + } + j = 0; + k = 0; + pltfrm_camera_module_pr_debug(sd, + "i2c_transfer return %d\n", ret); + } + break; + case PLTFRM_CAMERA_MODULE_REG_TYPE_TIMEOUT: + if (j > 0) { + /* Bulk I2C transfer */ + pltfrm_camera_module_pr_debug(sd, + "messages transfers 1 0x%p msg %d bytes %d\n", + msg, j, k); + ret = i2c_transfer(client->adapter, msg, j); + if (ret < 0) { + pltfrm_camera_module_pr_debug(sd, + "i2c transfer returned with err %d\n", + ret); + kfree(msg); + kfree(data); + return ret; + } + pltfrm_camera_module_pr_debug(sd, + "i2c_transfer return %d\n", ret); + } + mdelay(reglist[i].val); + j = 0; + k = 0; + break; + default: + pltfrm_camera_module_pr_debug(sd, "unknown command\n"); + kfree(msg); + kfree(data); + return -1; + } + } + + if (j != 0) { /*Remaining I2C message*/ + pltfrm_camera_module_pr_debug(sd, + "messages transfers 1 0x%p msg %d bytes %d\n", + msg, j, k); + ret = i2c_transfer(client->adapter, msg, j); + if (ret < 0) { + pltfrm_camera_module_pr_err(sd, + "i2c transfer returned with err %d\n", ret); + kfree(msg); + kfree(data); + return ret; + } + pltfrm_camera_module_pr_debug(sd, + "i2c_transfer return %d\n", ret); + } + + kfree(msg); + kfree(data); + return 0; +} + +static void aptina_camera_module_set_active_config( + struct aptina_camera_module *cam_mod, + struct aptina_camera_module_config *new_config) +{ + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + + if (IS_ERR_OR_NULL(new_config)) { + cam_mod->active_config = new_config; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "no active config\n"); + } else { + cam_mod->ctrl_updt &= APTINA_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP | + APTINA_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN | + APTINA_CAMERA_MODULE_CTRL_UPDT_AUTO_WB; + if (new_config->auto_exp_enabled != + cam_mod->exp_config.auto_exp) { + cam_mod->ctrl_updt |= + APTINA_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP; + cam_mod->exp_config.auto_exp = + new_config->auto_exp_enabled; + } + if (new_config->auto_gain_enabled != + cam_mod->exp_config.auto_gain) { + cam_mod->ctrl_updt |= + APTINA_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN; + cam_mod->exp_config.auto_gain = + new_config->auto_gain_enabled; + } + if (new_config->auto_wb_enabled != + cam_mod->wb_config.auto_wb) { + cam_mod->ctrl_updt |= + APTINA_CAMERA_MODULE_CTRL_UPDT_AUTO_WB; + cam_mod->wb_config.auto_wb = + new_config->auto_wb_enabled; + } + if (new_config != cam_mod->active_config) { + cam_mod->update_config = true; + cam_mod->active_config = new_config; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "activating config '%s'\n", + new_config->name); + } + } +} + +/* ======================================================================== */ + +static struct aptina_camera_module_config *aptina_camera_module_find_config( + struct aptina_camera_module *cam_mod, + struct v4l2_mbus_framefmt *fmt, + struct v4l2_subdev_frame_interval *frm_intrvl) +{ + u32 i; + unsigned long gcdiv; + struct v4l2_subdev_frame_interval norm_interval; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + if (!IS_ERR_OR_NULL(fmt)) + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "%dx%d, fmt code 0x%04x\n", + fmt->width, fmt->height, fmt->code); + + if (!IS_ERR_OR_NULL(frm_intrvl)) + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "frame interval %d/%d\n", + frm_intrvl->interval.numerator, + frm_intrvl->interval.denominator); + + for (i = 0; i < cam_mod->custom.num_configs; i++) { + if (!IS_ERR_OR_NULL(frm_intrvl)) { + gcdiv = gcd(cam_mod->custom.configs[i]. + frm_intrvl.interval.numerator, + cam_mod->custom.configs[i]. + frm_intrvl.interval.denominator); + norm_interval.interval.numerator = + cam_mod->custom.configs[i]. + frm_intrvl.interval.numerator / gcdiv; + norm_interval.interval.denominator = + cam_mod->custom.configs[i]. + frm_intrvl.interval.denominator / gcdiv; + if ((frm_intrvl->interval.numerator != + norm_interval.interval.numerator) || + (frm_intrvl->interval.denominator != + norm_interval.interval.denominator)) + continue; + } + if (!IS_ERR_OR_NULL(fmt)) { + if ((cam_mod->custom.configs[i].frm_fmt.width != + fmt->width) || + (cam_mod->custom.configs[i].frm_fmt.height != + fmt->height) || + (cam_mod->custom.configs[i].frm_fmt.code != + fmt->code)) { + continue; + } + } + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "found matching config %s\n", + cam_mod->custom.configs[i].name); + return &cam_mod->custom.configs[i]; + } + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "no matching config found\n"); + + return ERR_PTR(-EINVAL); +} + +/* ======================================================================== */ + +static int aptina_camera_module_write_config( + struct aptina_camera_module *cam_mod) +{ + int ret = 0; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + + if (IS_ERR_OR_NULL(cam_mod->active_config)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "no active sensor configuration"); + ret = -EFAULT; + goto err; + } + + ret = aptina_write_reglist(&cam_mod->sd, + cam_mod->active_config->reg_table, + cam_mod->active_config->reg_table_num_entries); + if (IS_ERR_VALUE(ret)) + goto err; + ret = pltfrm_camera_module_patch_config(&cam_mod->sd, + &cam_mod->frm_fmt, + &cam_mod->frm_intrvl); + if (IS_ERR_VALUE(ret)) + goto err; + + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; +} + +static int aptina_camera_module_attach( + struct aptina_camera_module *cam_mod) +{ + int ret = 0; + struct aptina_camera_module_custom_config *custom; + + custom = &cam_mod->custom; + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + if (custom->check_camera_id) { + aptina_camera_module_s_power(&cam_mod->sd, 1); + ret = custom->check_camera_id(cam_mod); + aptina_camera_module_s_power(&cam_mod->sd, 0); + if (ret != 0) + goto err; + } + + return 0; + +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + aptina_camera_module_release(cam_mod); + return ret; +} + +/* ======================================================================== */ + +int aptina_camera_module_try_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct aptina_camera_module *cam_mod = to_aptina_camera_module(sd); + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "%dx%d, fmt code 0x%04x\n", + fmt->width, fmt->height, fmt->code); + + if (IS_ERR_OR_NULL( + aptina_camera_module_find_config( + cam_mod, fmt, NULL))) { + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "format not supported\n"); + return -EINVAL; + } + pltfrm_camera_module_pr_debug(&cam_mod->sd, "format supported\n"); + + return 0; +} + +/* ======================================================================== */ + +int aptina_camera_module_s_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct aptina_camera_module *cam_mod = to_aptina_camera_module(sd); + struct v4l2_mbus_framefmt *fmt = &format->format; + int ret = 0; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "%dx%d, fmt code 0x%04x\n", + fmt->width, fmt->height, fmt->code); + + if (IS_ERR_OR_NULL( + aptina_camera_module_find_config( + cam_mod, fmt, NULL))) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "format %dx%d, code 0x%04x, not supported\n", + fmt->width, fmt->height, fmt->code); + ret = -EINVAL; + goto err; + } + cam_mod->frm_fmt_valid = true; + cam_mod->frm_fmt = *fmt; + if (cam_mod->frm_intrvl_valid) { + aptina_camera_module_set_active_config(cam_mod, + aptina_camera_module_find_config(cam_mod, + fmt, &cam_mod->frm_intrvl)); + } + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; +} + +/* ======================================================================== */ + +int aptina_camera_module_g_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct aptina_camera_module *cam_mod = to_aptina_camera_module(sd); + struct v4l2_mbus_framefmt *fmt = &format->format; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + + if (cam_mod->active_config) { + fmt->code = cam_mod->active_config->frm_fmt.code; + fmt->width = cam_mod->active_config->frm_fmt.width; + fmt->height = cam_mod->active_config->frm_fmt.height; + return 0; + } + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "no active config\n"); + + return -1; +} + +/* ======================================================================== */ + +int aptina_camera_module_s_frame_interval( + struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *interval) +{ + struct aptina_camera_module *cam_mod = to_aptina_camera_module(sd); + unsigned long gcdiv; + struct v4l2_subdev_frame_interval norm_interval; + int ret = 0; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + if ((interval->interval.denominator == 0) || + (interval->interval.numerator == 0)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "invalid frame interval %d/%d\n", + interval->interval.numerator, + interval->interval.denominator); + ret = -EINVAL; + goto err; + } + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "%d/%d (%dfps)\n", + interval->interval.numerator, interval->interval.denominator, + (interval->interval.denominator + + (interval->interval.numerator >> 1)) / + interval->interval.numerator); + + /* normalize interval */ + gcdiv = gcd(interval->interval.numerator, + interval->interval.denominator); + norm_interval.interval.numerator = + interval->interval.numerator / gcdiv; + norm_interval.interval.denominator = + interval->interval.denominator / gcdiv; + + if (IS_ERR_OR_NULL(aptina_camera_module_find_config(cam_mod, + NULL, &norm_interval))) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "frame interval %d/%d not supported\n", + interval->interval.numerator, + interval->interval.denominator); + ret = -EINVAL; + goto err; + } + cam_mod->frm_intrvl_valid = true; + cam_mod->frm_intrvl = norm_interval; + if (cam_mod->frm_fmt_valid) { + aptina_camera_module_set_active_config(cam_mod, + aptina_camera_module_find_config(cam_mod, + &cam_mod->frm_fmt, interval)); + } + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; +} + +/* ======================================================================== */ + +int aptina_camera_module_s_stream(struct v4l2_subdev *sd, int enable) +{ + int ret = 0; + struct aptina_camera_module *cam_mod = to_aptina_camera_module(sd); + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "%d\n", enable); + + if (enable) { + if (cam_mod->state == APTINA_CAMERA_MODULE_STREAMING) + return 0; + if (IS_ERR_OR_NULL(cam_mod->active_config)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "no active sensor configuration, cannot start streaming\n"); + ret = -EFAULT; + goto err; + } + if (cam_mod->state != APTINA_CAMERA_MODULE_SW_STANDBY) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "sensor is not powered on (in state %d), cannot start streaming\n", + cam_mod->state); + ret = -EINVAL; + goto err; + } + + if (!IS_ERR_OR_NULL(cam_mod->custom.set_flip)) + cam_mod->custom.set_flip(cam_mod); + + if (cam_mod->update_config) + ret = aptina_camera_module_write_config(cam_mod); + if (IS_ERR_VALUE(ret)) + goto err; + + ret = cam_mod->custom.start_streaming(cam_mod); + if (IS_ERR_VALUE(ret)) + goto err; + cam_mod->update_config = false; + cam_mod->ctrl_updt = 0; + mdelay(cam_mod->custom.power_up_delays_ms[2]); + cam_mod->state = APTINA_CAMERA_MODULE_STREAMING; + } else { + int pclk; + int wait_ms; + struct isp_supplemental_sensor_mode_data timings; + + if (cam_mod->state != APTINA_CAMERA_MODULE_STREAMING) + return 0; + ret = cam_mod->custom.stop_streaming(cam_mod); + if (IS_ERR_VALUE(ret)) + goto err; + + cam_mod->state = APTINA_CAMERA_MODULE_SW_STANDBY; + + ret = aptina_camera_module_ioctl(sd, RK_VIDIOC_SENSOR_MODE_DATA, + &timings); + if (IS_ERR_VALUE(ret)) + goto err; + pclk = timings.vt_pix_clk_freq_hz / 1000; + if (!pclk) + goto err; + wait_ms = (timings.line_length_pck + * timings.frame_length_lines) / pclk; + /* wait for a frame period to make sure that there is + *no pending frame left. + */ + msleep(wait_ms + 1); + } + + cam_mod->state_before_suspend = cam_mod->state; + + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; +} + +/* ======================================================================== */ + +int aptina_camera_module_s_power(struct v4l2_subdev *sd, int on) +{ + int ret = 0; + struct aptina_camera_module *cam_mod = to_aptina_camera_module(sd); + struct v4l2_subdev *af_ctrl; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "%d\n", on); + + if (on) { + if (cam_mod->state == APTINA_CAMERA_MODULE_POWER_OFF) { + ret = pltfrm_camera_module_s_power(&cam_mod->sd, 1); + if (!IS_ERR_VALUE(ret)) { + mdelay(cam_mod->custom.power_up_delays_ms[0]); + cam_mod->state = + APTINA_CAMERA_MODULE_HW_STANDBY; + } + } + if (cam_mod->state == APTINA_CAMERA_MODULE_HW_STANDBY) { + ret = pltfrm_camera_module_set_pin_state(&cam_mod->sd, + PLTFRM_CAMERA_MODULE_PIN_PWR, + PLTFRM_CAMERA_MODULE_PIN_STATE_ACTIVE); + msleep(20); + + ret = pltfrm_camera_module_set_pin_state( + &cam_mod->sd, + PLTFRM_CAMERA_MODULE_PIN_PD, + PLTFRM_CAMERA_MODULE_PIN_STATE_ACTIVE); + msleep(20); + + ret = pltfrm_camera_module_set_pin_state( + &cam_mod->sd, + PLTFRM_CAMERA_MODULE_PIN_RESET, + PLTFRM_CAMERA_MODULE_PIN_STATE_ACTIVE); + msleep(20); + + ret = pltfrm_camera_module_set_pin_state( + &cam_mod->sd, + PLTFRM_CAMERA_MODULE_PIN_RESET, + PLTFRM_CAMERA_MODULE_PIN_STATE_INACTIVE); + msleep(20); + + ret = pltfrm_camera_module_set_pin_state( + &cam_mod->sd, + PLTFRM_CAMERA_MODULE_PIN_RESET, + PLTFRM_CAMERA_MODULE_PIN_STATE_ACTIVE); + msleep(20); + + ret = pltfrm_camera_module_set_pin_state( + &cam_mod->sd, + PLTFRM_CAMERA_MODULE_PIN_PD, + PLTFRM_CAMERA_MODULE_PIN_STATE_INACTIVE); + msleep(20); + ret = pltfrm_camera_module_set_pin_state( + &cam_mod->sd, + PLTFRM_CAMERA_MODULE_PIN_PD, + PLTFRM_CAMERA_MODULE_PIN_STATE_ACTIVE); + msleep(20); + + if (!IS_ERR_VALUE(ret)) { + mdelay(cam_mod->custom.power_up_delays_ms[1]); + cam_mod->state = + APTINA_CAMERA_MODULE_SW_STANDBY; + + #if 1 + if (!IS_ERR_OR_NULL( + cam_mod->custom.init_common) && + cam_mod->custom.init_common( + cam_mod)) + usleep_range(1000, 1500); + #endif + + af_ctrl = pltfrm_camera_module_get_af_ctrl(sd); + if (!IS_ERR_OR_NULL(af_ctrl)) { + v4l2_subdev_call(af_ctrl, + core, init, 0); + } + } + } + } else { + if (cam_mod->state == APTINA_CAMERA_MODULE_STREAMING) { + ret = aptina_camera_module_s_stream(sd, 0); + if (!IS_ERR_VALUE(ret)) + cam_mod->state = + APTINA_CAMERA_MODULE_SW_STANDBY; + } + if (cam_mod->state == APTINA_CAMERA_MODULE_SW_STANDBY) { + ret = pltfrm_camera_module_set_pin_state( + &cam_mod->sd, + PLTFRM_CAMERA_MODULE_PIN_PD, + PLTFRM_CAMERA_MODULE_PIN_STATE_INACTIVE); + + ret = pltfrm_camera_module_set_pin_state( + &cam_mod->sd, + PLTFRM_CAMERA_MODULE_PIN_RESET, + PLTFRM_CAMERA_MODULE_PIN_STATE_INACTIVE); + + ret = pltfrm_camera_module_set_pin_state( + &cam_mod->sd, + PLTFRM_CAMERA_MODULE_PIN_PWR, + PLTFRM_CAMERA_MODULE_PIN_STATE_INACTIVE); + + if (!IS_ERR_VALUE(ret)) + cam_mod->state = + APTINA_CAMERA_MODULE_HW_STANDBY; + } + if (cam_mod->state == APTINA_CAMERA_MODULE_HW_STANDBY) { + ret = pltfrm_camera_module_s_power(&cam_mod->sd, 0); + if (!IS_ERR_VALUE(ret)) { + cam_mod->state = APTINA_CAMERA_MODULE_POWER_OFF; + aptina_camera_module_reset(cam_mod); + } + } + } + + cam_mod->state_before_suspend = cam_mod->state; + + if (IS_ERR_VALUE(ret)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "%s failed, camera left in state %d\n", + on ? "on" : "off", cam_mod->state); + goto err; + } else + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "camera powered %s\n", on ? "on" : "off"); + + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; +} + +/* ======================================================================== */ + +int aptina_camera_module_g_ctrl(struct v4l2_subdev *sd, + struct v4l2_control *ctrl) +{ + struct aptina_camera_module *cam_mod = to_aptina_camera_module(sd); + int ret; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, " id 0x%x\n", ctrl->id); + + if (ctrl->id == V4L2_CID_FLASH_LED_MODE) { + ctrl->value = cam_mod->exp_config.flash_mode; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_FLASH_LED_MODE %d\n", + ctrl->value); + return 0; + } + + if (IS_ERR_OR_NULL(cam_mod->active_config)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "no active configuration\n"); + return -EFAULT; + } + + if (ctrl->id == RK_V4L2_CID_VBLANKING) { + ctrl->value = cam_mod->active_config->v_blanking_time_us; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "RK_V4L2_CID_VBLANKING %d\n", + ctrl->value); + return 0; + } + +/* + *if (ctrl->id == RK_V4L2_CID_IGNORE_MEASUREMENT_CHECK) { + * ctrl->value = cam_mod->active_config->ignore_measurement_check; + * pltfrm_camera_module_pr_debug( + * &cam_mod->sd, + * "CIF_ISP20_CID_IGNORE_MEASUREMENT_CHECK %d\n", + * ctrl->value); + * return 0; + * } + */ + + if ((cam_mod->state != APTINA_CAMERA_MODULE_SW_STANDBY) && + (cam_mod->state != APTINA_CAMERA_MODULE_STREAMING)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "cannot get controls when camera is off\n"); + return -EFAULT; + } + + if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE) { + struct v4l2_subdev *af_ctrl; + + af_ctrl = + pltfrm_camera_module_get_af_ctrl(sd); + if (!IS_ERR_OR_NULL(af_ctrl)) { + ret = v4l2_subdev_call(af_ctrl, core, g_ctrl, ctrl); + return ret; + } + } + + if (!IS_ERR_OR_NULL(cam_mod->custom.g_ctrl)) { + ret = cam_mod->custom.g_ctrl(cam_mod, ctrl->id); + if (IS_ERR_VALUE(ret)) + return ret; + } + + switch (ctrl->id) { + case V4L2_CID_GAIN: + ctrl->value = cam_mod->exp_config.gain; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_GAIN %d\n", + ctrl->value); + break; + case V4L2_CID_EXPOSURE: + ctrl->value = cam_mod->exp_config.exp_time; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_EXPOSURE %d\n", + ctrl->value); + break; + case V4L2_CID_WHITE_BALANCE_TEMPERATURE: + ctrl->value = cam_mod->wb_config.temperature; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_WHITE_BALANCE_TEMPERATURE %d\n", + ctrl->value); + break; + case V4L2_CID_AUTOGAIN: + ctrl->value = cam_mod->exp_config.auto_gain; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_AUTOGAIN %d\n", + ctrl->value); + break; + case V4L2_CID_EXPOSURE_AUTO: + ctrl->value = cam_mod->exp_config.auto_exp; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_EXPOSURE_AUTO %d\n", + ctrl->value); + break; + case V4L2_CID_AUTO_WHITE_BALANCE: + ctrl->value = cam_mod->wb_config.auto_wb; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_AUTO_WHITE_BALANCE %d\n", + ctrl->value); + break; + case V4L2_CID_FOCUS_ABSOLUTE: + ctrl->value = cam_mod->af_config.abs_pos; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_FOCUS_ABSOLUTE %d\n", + ctrl->value); + break; + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + /* TBD */ + /* fallthrough */ + default: + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "failed, unknown ctrl %d\n", ctrl->id); + return -EINVAL; + } + + return 0; +} + +/* ======================================================================== */ + +static int flash_light_ctrl( + struct v4l2_subdev *sd, + struct aptina_camera_module *cam_mod, + int value) +{ + return 0; +} + +/* ======================================================================== */ + +int aptina_camera_module_s_ext_ctrls( + struct v4l2_subdev *sd, + struct v4l2_ext_controls *ctrls) +{ + int i; + int ctrl_cnt = 0; + struct aptina_camera_module *cam_mod = to_aptina_camera_module(sd); + int ret = 0; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + + if (ctrls->count == 0) + return -EINVAL; + + for (i = 0; i < ctrls->count; i++) { + struct v4l2_ext_control *ctrl; + u32 ctrl_updt = 0; + + ctrl = &ctrls->controls[i]; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + ctrl_updt = APTINA_CAMERA_MODULE_CTRL_UPDT_GAIN; + cam_mod->exp_config.gain = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_GAIN %d\n", + ctrl->value); + break; + case RK_V4L2_CID_GAIN_PERCENT: + ctrl_updt = APTINA_CAMERA_MODULE_CTRL_UPDT_GAIN; + cam_mod->exp_config.gain_percent = ctrl->value; + break; + case V4L2_CID_FLASH_LED_MODE: + ret = flash_light_ctrl(sd, cam_mod, ctrl->value); + if (ret == 0) { + cam_mod->exp_config.flash_mode = ctrl->value; + pltfrm_camera_module_pr_debug( + &cam_mod->sd, + "V4L2_CID_FLASH_LED_MODE %d\n", + ctrl->value); + } + break; + case V4L2_CID_EXPOSURE: + ctrl_updt = APTINA_CAMERA_MODULE_CTRL_UPDT_EXP_TIME; + cam_mod->exp_config.exp_time = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_EXPOSURE %d\n", + ctrl->value); + break; + case V4L2_CID_WHITE_BALANCE_TEMPERATURE: + ctrl_updt = + APTINA_CAMERA_MODULE_CTRL_UPDT_WB_TEMPERATURE; + cam_mod->wb_config.temperature = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_WHITE_BALANCE_TEMPERATURE %d\n", + ctrl->value); + break; + case V4L2_CID_AUTOGAIN: + ctrl_updt = APTINA_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN; + cam_mod->exp_config.auto_gain = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_AUTOGAIN %d\n", + ctrl->value); + break; + case V4L2_CID_EXPOSURE_AUTO: + ctrl_updt = APTINA_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP; + cam_mod->exp_config.auto_exp = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_EXPOSURE_AUTO %d\n", + ctrl->value); + break; + case V4L2_CID_AUTO_WHITE_BALANCE: + ctrl_updt = APTINA_CAMERA_MODULE_CTRL_UPDT_AUTO_WB; + cam_mod->wb_config.auto_wb = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_AUTO_WHITE_BALANCE %d\n", + ctrl->value); + break; + case RK_V4L2_CID_AUTO_FPS: + cam_mod->auto_adjust_fps = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "RK_V4L2_CID_AUTO_FPS %d\n", + ctrl->value); + break; + case V4L2_CID_FOCUS_ABSOLUTE: + { + struct v4l2_subdev *af_ctrl; + + af_ctrl = pltfrm_camera_module_get_af_ctrl(sd); + if (!IS_ERR_OR_NULL(af_ctrl)) { + struct v4l2_control single_ctrl; + + single_ctrl.id = + V4L2_CID_FOCUS_ABSOLUTE; + single_ctrl.value = ctrl->value; + ret = v4l2_subdev_call(af_ctrl, + core, s_ctrl, &single_ctrl); + return ret; + } + } + ctrl_updt = + APTINA_CAMERA_MODULE_CTRL_UPDT_FOCUS_ABSOLUTE; + cam_mod->af_config.abs_pos = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_FOCUS_ABSOLUTE %d\n", + ctrl->value); + break; + case V4L2_CID_HFLIP: + if (ctrl->value) + cam_mod->hflip = true; + else + cam_mod->hflip = false; + break; + case V4L2_CID_VFLIP: + if (ctrl->value) + cam_mod->vflip = true; + else + cam_mod->vflip = false; + break; + default: + pltfrm_camera_module_pr_warn(&cam_mod->sd, + "ignoring unknown ctrl 0x%x\n", ctrl->id); + break; + } + + if (cam_mod->state != APTINA_CAMERA_MODULE_SW_STANDBY && + cam_mod->state != APTINA_CAMERA_MODULE_STREAMING) + cam_mod->ctrl_updt |= ctrl_updt; + else if (ctrl_updt) + ctrl_cnt++; + } + + /* if camera module is already streaming, write through */ + if (ctrl_cnt && + (cam_mod->state == APTINA_CAMERA_MODULE_STREAMING || + cam_mod->state == APTINA_CAMERA_MODULE_SW_STANDBY)) { + struct aptina_camera_module_ext_ctrls aptina_ctrls; + + aptina_ctrls.ctrls = + (struct aptina_camera_module_ext_ctrl *) + kmalloc(ctrl_cnt * sizeof(struct aptina_camera_module_ext_ctrl), + GFP_KERNEL); + + if (aptina_ctrls.ctrls) { + for (i = 0; i < ctrl_cnt; i++) { + aptina_ctrls.ctrls[i].id = + ctrls->controls[i].id; + aptina_ctrls.ctrls[i].value = + ctrls->controls[i].value; + } + + aptina_ctrls.count = ctrl_cnt; + + ret = cam_mod->custom.s_ext_ctrls(cam_mod, + &aptina_ctrls); + + kfree(aptina_ctrls.ctrls); + } else { + ret = -ENOMEM; + } + + if (IS_ERR_VALUE(ret)) + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "failed with error %d\n", ret); + } + + return ret; +} + +/* ======================================================================== */ + +int aptina_camera_module_s_ctrl( + struct v4l2_subdev *sd, + struct v4l2_control *ctrl) +{ + struct aptina_camera_module *cam_mod = to_aptina_camera_module(sd); + struct v4l2_ext_control ext_ctrl[1]; + struct v4l2_ext_controls ext_ctrls; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "0x%x 0x%x\n", ctrl->id, ctrl->value); + + ext_ctrl[0].id = ctrl->id; + ext_ctrl[0].value = ctrl->value; + + ext_ctrls.count = 1; + ext_ctrls.controls = ext_ctrl; + + return aptina_camera_module_s_ext_ctrls(sd, &ext_ctrls); +} + +/* ======================================================================== */ + +long aptina_camera_module_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, + void *arg) +{ + struct aptina_camera_module *cam_mod = to_aptina_camera_module(sd); + int ret; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + + if (cmd == RK_VIDIOC_SENSOR_MODE_DATA) { + struct aptina_camera_module_timings aptina_timings; + struct isp_supplemental_sensor_mode_data *timings = + (struct isp_supplemental_sensor_mode_data *)arg; + + if (cam_mod->custom.g_timings) + ret = cam_mod->custom.g_timings(cam_mod, + &aptina_timings); + else + ret = -EPERM; + + if (IS_ERR_VALUE(ret)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; + } + + timings->sensor_output_width = + aptina_timings.sensor_output_width; + timings->sensor_output_height = + aptina_timings.sensor_output_height; + timings->crop_horizontal_start = + aptina_timings.crop_horizontal_start; + timings->crop_vertical_start = + aptina_timings.crop_vertical_start; + timings->crop_horizontal_end = + aptina_timings.crop_horizontal_end; + timings->crop_vertical_end = + aptina_timings.crop_vertical_end; + timings->line_length_pck = + aptina_timings.line_length_pck; + timings->frame_length_lines = + aptina_timings.frame_length_lines; + timings->vt_pix_clk_freq_hz = + aptina_timings.vt_pix_clk_freq_hz; + timings->binning_factor_x = + aptina_timings.binning_factor_x; + timings->binning_factor_y = + aptina_timings.binning_factor_y; + timings->coarse_integration_time_max_margin = + aptina_timings.coarse_integration_time_max_margin; + timings->coarse_integration_time_min = + aptina_timings.coarse_integration_time_min; + timings->fine_integration_time_max_margin = + aptina_timings.fine_integration_time_max_margin; + timings->fine_integration_time_min = + aptina_timings.fine_integration_time_min; + + if (cam_mod->custom.g_exposure_valid_frame) + timings->exposure_valid_frame = + cam_mod->custom.g_exposure_valid_frame(cam_mod); + + /* + *timings->exp_time = cam_mod->exp_config.exp_time; + *timings->gain = cam_mod->exp_config.gain; + */ + + return ret; + } else if (cmd == PLTFRM_CIFCAM_G_ITF_CFG) { + struct pltfrm_cam_itf *itf_cfg = (struct pltfrm_cam_itf *)arg; + struct aptina_camera_module_config *config; + + if (cam_mod->custom.num_configs <= 0) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "cam_mod->custom.num_configs is NULL, Get interface config failed!\n"); + return -EINVAL; + } + + if (IS_ERR_OR_NULL(cam_mod->active_config)) + config = &cam_mod->custom.configs[0]; + else + config = cam_mod->active_config; + + *itf_cfg = config->itf_cfg; + pltfrm_camera_module_ioctl(sd, PLTFRM_CIFCAM_G_ITF_CFG, arg); + return 0; + } else if (cmd == PLTFRM_CIFCAM_ATTACH) { + aptina_camera_module_init(cam_mod, &cam_mod->custom); + pltfrm_camera_module_ioctl(sd, cmd, arg); + return aptina_camera_module_attach(cam_mod); + } + + ret = pltfrm_camera_module_ioctl(sd, cmd, arg); + return ret; +} + +/* Reconfigure bayer order according to xml's definition */ +static int aptina_camera_module_s_flip(struct aptina_camera_module *cam_mod) +{ + int i, mode = 0; + static int flip_mode; + + if (!cam_mod->custom.configs->bayer_order_alter_enble) { + aptina_camera_module_pr_info(cam_mod, + "not need to change bayer order!"); + return 0; + } + + mode = pltfrm_camera_module_get_flip_mirror(&cam_mod->sd); + + aptina_camera_module_pr_debug(cam_mod, + "%s(%d): flip_mode is %d, mode is %d.\n", + __func__, + __LINE__, + flip_mode, + mode); + + if (mode != flip_mode) { + /* Reconfigure the compatible bayer order */ + switch (mode) { + case 0: + /* Change all the bayter order of registered configs */ + for (i = 0; i < cam_mod->custom.num_configs; i++) + cam_mod->custom.configs[i].frm_fmt.code = + MEDIA_BUS_FMT_SGRBG10_1X10; + break; + + case 1: + for (i = 0; i < cam_mod->custom.num_configs; i++) + cam_mod->custom.configs[i].frm_fmt.code = + MEDIA_BUS_FMT_SRGGB10_1X10; + break; + + case 2: + for (i = 0; i < cam_mod->custom.num_configs; i++) + cam_mod->custom.configs[i].frm_fmt.code = + MEDIA_BUS_FMT_SBGGR10_1X10; + break; + + case 3: + for (i = 0; i < cam_mod->custom.num_configs; i++) + cam_mod->custom.configs[i].frm_fmt.code = + MEDIA_BUS_FMT_SGBRG10_1X10; + break; + + default: + break; + }; + } + + flip_mode = mode; + + return 0; +} + +int aptina_camera_module_get_flip_mirror( + struct aptina_camera_module *cam_mod) +{ + return pltfrm_camera_module_get_flip_mirror(&cam_mod->sd); +} + +int aptina_camera_module_enum_frameintervals( + struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *file) +{ + struct aptina_camera_module *cam_mod = to_aptina_camera_module(sd); + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "%d\n", file->index); + + if (file->index >= cam_mod->custom.num_configs) + return -EINVAL; + + aptina_camera_module_s_flip(cam_mod); + file->code = + cam_mod->custom.configs[file->index].frm_fmt.code; + file->width = cam_mod->custom.configs[file->index].frm_fmt.width; + file->height = cam_mod->custom.configs[file->index].frm_fmt.height; + file->interval.numerator = cam_mod->custom. + configs[file->index].frm_intrvl.interval.numerator; + file->interval.denominator = cam_mod->custom. + configs[file->index].frm_intrvl.interval.denominator; + return 0; +} + +/* ======================================================================== */ + +int aptina_camera_module_write_reglist( + struct aptina_camera_module *cam_mod, + const struct aptina_camera_module_reg reglist[], + int len) +{ + return aptina_write_reglist(&cam_mod->sd, reglist, len); +} + +/* ======================================================================== */ + +int aptina_camera_module_write_reg( + struct aptina_camera_module *cam_mod, + u16 reg, + u16 val) +{ + return aptina_write_i2c_reg(&cam_mod->sd, reg, val); +} + +/* ======================================================================== */ + +int aptina_camera_module_read_reg( + struct aptina_camera_module *cam_mod, + u16 data_length, + u16 reg, + u32 *val) +{ + return aptina_read_i2c_reg(&cam_mod->sd, + data_length, reg, val); +} + +/* ======================================================================== */ + +int aptina_camera_module_read_reg_table( + struct aptina_camera_module *cam_mod, + u16 reg, + u32 *val) +{ + int i; + + if (cam_mod->state == APTINA_CAMERA_MODULE_STREAMING) + return aptina_read_i2c_reg(&cam_mod->sd, + 1, reg, val); + + if (!IS_ERR_OR_NULL(cam_mod->active_config)) { + for ( + i = cam_mod->active_config->reg_table_num_entries - 1; + i > 0; + i--) { + if (cam_mod->active_config->reg_table[i].reg == reg) { + *val = cam_mod->active_config->reg_table[i].val; + return 0; + } + } + } + + if (cam_mod->state == APTINA_CAMERA_MODULE_SW_STANDBY) + return aptina_read_i2c_reg(&cam_mod->sd, + 1, reg, val); + + return -EFAULT; +} + +/* ======================================================================== */ + +int aptina_camera_module_init(struct aptina_camera_module *cam_mod, + struct aptina_camera_module_custom_config *custom) +{ + int ret = 0; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + + aptina_camera_module_reset(cam_mod); + + if (IS_ERR_OR_NULL(custom->start_streaming) || + IS_ERR_OR_NULL(custom->stop_streaming) || + IS_ERR_OR_NULL(custom->s_ctrl) || + IS_ERR_OR_NULL(custom->g_ctrl)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "mandatory callback function is missing\n"); + ret = -EINVAL; + goto err; + } + + ret = pltfrm_camera_module_init(&cam_mod->sd, &cam_mod->pltfm_data); + if (IS_ERR_VALUE(ret)) + goto err; + + ret = pltfrm_camera_module_set_pin_state(&cam_mod->sd, + PLTFRM_CAMERA_MODULE_PIN_PD, + PLTFRM_CAMERA_MODULE_PIN_STATE_INACTIVE); + ret = pltfrm_camera_module_set_pin_state(&cam_mod->sd, + PLTFRM_CAMERA_MODULE_PIN_RESET, + PLTFRM_CAMERA_MODULE_PIN_STATE_ACTIVE); + if (IS_ERR_VALUE(ret)) { + aptina_camera_module_release(cam_mod); + goto err; + } +/* + *if (custom->check_camera_id) { + * aptina_camera_module_s_power(&cam_mod->sd, 1); + * ret = (custom->check_camera_id)(cam_mod); + * aptina_camera_module_s_power(&cam_mod->sd, 0); + *} + * + *if (IS_ERR_VALUE(ret)) { + * aptina_camera_module_release(cam_mod); + * goto err; + *} + */ + + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; +} + +void aptina_camera_module_release(struct aptina_camera_module *cam_mod) +{ + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + + cam_mod->custom.configs = NULL; + + pltfrm_camera_module_release(&cam_mod->sd); + v4l2_device_unregister_subdev(&cam_mod->sd); +} diff --git a/drivers/media/i2c/soc_camera/rockchip/aptina_camera_module.h b/drivers/media/i2c/soc_camera/rockchip/aptina_camera_module.h new file mode 100644 index 000000000000..5bd1b92e6c79 --- /dev/null +++ b/drivers/media/i2c/soc_camera/rockchip/aptina_camera_module.h @@ -0,0 +1,278 @@ +/* + * aptina_camera_module.h + * + * Generic galaxycore sensor driver + * + * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd. + * + * Copyright (C) 2012-2014 Intel Mobile Communications GmbH + * + * Copyright (C) 2008 Texas Instruments. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + */ + +#ifndef APTINA_CAMERA_MODULE_H +#define APTINA_CAMERA_MODULE_H +#include +#include +#include +/* + * TODO: references to v4l2 should be reomved from here and go into a + * platform dependent wrapper + */ + +#define APTINA_CAMERA_MODULE_REG_TYPE_DATA \ + PLTFRM_CAMERA_MODULE_REG_TYPE_DATA +#define APTINA_CAMERA_MODULE_REG_TYPE_TIMEOUT \ + PLTFRM_CAMERA_MODULE_REG_TYPE_TIMEOUT +#define aptina_camera_module_csi_config +#define aptina_camera_module_reg pltfrm_camera_module_reg +#define APTINA_FLIP_BIT_MASK 0x2 +#define APTINA_MIRROR_BIT_MASK 0x1 + +#define APTINA_CAMERA_MODULE_CTRL_UPDT_GAIN 0x01 +#define APTINA_CAMERA_MODULE_CTRL_UPDT_EXP_TIME 0x02 +#define APTINA_CAMERA_MODULE_CTRL_UPDT_WB_TEMPERATURE 0x04 +#define APTINA_CAMERA_MODULE_CTRL_UPDT_AUTO_WB 0x08 +#define APTINA_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN 0x10 +#define APTINA_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP 0x20 +#define APTINA_CAMERA_MODULE_CTRL_UPDT_FOCUS_ABSOLUTE 0x40 +#define APTINA_CAMERA_MODULE_CTRL_UPDT_PRESET_WB 0x80 + +enum aptina_camera_module_state { + APTINA_CAMERA_MODULE_POWER_OFF = 0, + APTINA_CAMERA_MODULE_HW_STANDBY = 1, + APTINA_CAMERA_MODULE_SW_STANDBY = 2, + APTINA_CAMERA_MODULE_STREAMING = 3 +}; + +struct aptina_camera_module; + +struct aptina_camera_module_timings { + /* public */ + u32 coarse_integration_time_min; + u32 coarse_integration_time_max_margin; + u32 fine_integration_time_min; + u32 fine_integration_time_max_margin; + u32 frame_length_lines; + u32 line_length_pck; + u32 vt_pix_clk_freq_hz; + u32 sensor_output_width; + u32 sensor_output_height; + u32 crop_horizontal_start; /* Sensor crop start cord. (x0,y0) */ + u32 crop_vertical_start; + u32 crop_horizontal_end; /* Sensor crop end cord. (x1,y1) */ + u32 crop_vertical_end; + u8 binning_factor_x; + u8 binning_factor_y; +}; + +struct aptina_camera_module_config { + const char *name; + struct v4l2_mbus_framefmt frm_fmt; + struct v4l2_subdev_frame_interval frm_intrvl; + bool auto_exp_enabled; + bool auto_gain_enabled; + bool auto_wb_enabled; + struct aptina_camera_module_reg *reg_table; + u32 reg_table_num_entries; + u32 bayer_order_alter_enble; + u32 v_blanking_time_us; + u32 line_length_pck; + u32 frame_length_lines; + struct aptina_camera_module_timings timings; + bool soft_reset; + bool ignore_measurement_check; + struct pltfrm_cam_itf itf_cfg; +}; + +struct aptina_camera_module_exp_config { + u32 exp_time; + bool auto_exp; + u16 gain; + u16 gain_percent; + bool auto_gain; + enum v4l2_flash_led_mode flash_mode; +}; + +struct aptina_camera_module_wb_config { + u32 temperature; + u32 preset_id; + bool auto_wb; +}; + +struct aptina_camera_module_af_config { + u32 abs_pos; + u32 rel_pos; +}; + +struct aptina_camera_module_ext_ctrl { + /* public */ + u32 id; + u32 value; + __u32 reserved2[1]; +}; + +struct aptina_camera_module_ext_ctrls { + /* public */ + u32 count; + struct aptina_camera_module_ext_ctrl *ctrls; +}; + +/* + * start_streaming: (mandatory) will be called when sensor should be + * put into streaming mode right after the base config has been + * written to the sensor. After a successful call of this function + * the sensor should start delivering frame data. + * + * stop_streaming: (mandatory) will be called when sensor should stop + * delivering data. After a successful call of this function the + * sensor should not deliver any more frame data. + * + * check_camera_id: (optional) will be called when the sensor is + * powered on. If provided should check the sensor ID/version + * required by the custom driver. Register access should be + * possible when this function is invoked. + * + * s_ctrl: (mandatory) will be called at the successful end of + * aptina_camera_module_s_ctrl with the ctrl_id as argument. + * + * priv: (optional) for private data used by the custom driver. + */ +struct aptina_camera_module_custom_config { + int (*start_streaming)(struct aptina_camera_module *cam_mod); + int (*stop_streaming)(struct aptina_camera_module *cam_mod); + int (*check_camera_id)(struct aptina_camera_module *cam_mod); + int (*s_ctrl)(struct aptina_camera_module *cam_mod, u32 ctrl_id); + int (*g_ctrl)(struct aptina_camera_module *cam_mod, u32 ctrl_id); + int (*g_timings)(struct aptina_camera_module *cam_mod, + struct aptina_camera_module_timings *timings); + int (*s_ext_ctrls)(struct aptina_camera_module *cam_mod, + struct aptina_camera_module_ext_ctrls *ctrls); + int (*set_flip)(struct aptina_camera_module *cam_mod); + struct aptina_camera_module_config *configs; + int (*init_common)(struct aptina_camera_module *cam_mod); + int (*g_exposure_valid_frame)(struct aptina_camera_module *cam_mod); + u32 num_configs; + u32 power_up_delays_ms[3]; + void *priv; + struct aptina_camera_module_timings timings; +}; + +struct aptina_camera_module { + /* public */ + struct v4l2_subdev sd; + struct v4l2_mbus_framefmt frm_fmt; + struct v4l2_subdev_frame_interval frm_intrvl; + struct aptina_camera_module_exp_config exp_config; + struct aptina_camera_module_wb_config wb_config; + struct aptina_camera_module_af_config af_config; + struct aptina_camera_module_custom_config custom; + enum aptina_camera_module_state state; + enum aptina_camera_module_state state_before_suspend; + struct aptina_camera_module_config *active_config; + u32 ctrl_updt; + u32 vts_min; + bool auto_adjust_fps; + bool update_config; + bool frm_fmt_valid; + bool frm_intrvl_valid; + bool hflip; + bool vflip; + u32 rotation; + void *pltfm_data; + bool inited; +}; + +#define aptina_camera_module_pr_info(cam_mod, fmt, arg...) \ + pltfrm_camera_module_pr_info(&cam_mod->sd, fmt, ## arg) +#define aptina_camera_module_pr_debug(cam_mod, fmt, arg...) \ + pltfrm_camera_module_pr_debug(&cam_mod->sd, fmt, ## arg) +#define aptina_camera_module_pr_warn(cam_mod, fmt, arg...) \ + pltfrm_camera_module_pr_warn(&cam_mod->sd, fmt, ## arg) +#define aptina_camera_module_pr_err(cam_mod, fmt, arg...) \ + pltfrm_camera_module_pr_err(&cam_mod->sd, fmt, ## arg) + +int aptina_camera_module_write_reglist( + struct aptina_camera_module *cam_mod, + const struct aptina_camera_module_reg reglist[], + int len); + +int aptina_camera_module_write_reg( + struct aptina_camera_module *cam_mod, + u16 reg, + u16 val); + +int aptina_camera_module_read_reg( + struct aptina_camera_module *cam_mod, + u16 data_length, + u16 reg, + u32 *val); + +int aptina_camera_module_read_reg_table( + struct aptina_camera_module *cam_mod, + u16 reg, + u32 *val); + +int aptina_camera_module_try_fmt( + struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt); + +int aptina_camera_module_s_fmt( + struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format); + +int aptina_camera_module_g_fmt( + struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format); + +int aptina_camera_module_s_frame_interval( + struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *interval); + +int aptina_camera_module_s_stream( + struct v4l2_subdev *sd, + int enable); + +int aptina_camera_module_s_power( + struct v4l2_subdev *sd, + int on); + +int aptina_camera_module_g_ctrl( + struct v4l2_subdev *sd, + struct v4l2_control *ctrl); + +int aptina_camera_module_s_ctrl( + struct v4l2_subdev *sd, + struct v4l2_control *ctrl); + +int aptina_camera_module_s_ext_ctrls( + struct v4l2_subdev *sd, + struct v4l2_ext_controls *ctrls); + +int aptina_camera_module_enum_frameintervals( + struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *file); + +int aptina_camera_module_init( + struct aptina_camera_module *cam_mod, + struct aptina_camera_module_custom_config *custom); + +void aptina_camera_module_release( + struct aptina_camera_module *cam_mod); + +long aptina_camera_module_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, + void *arg); + +int aptina_camera_module_get_flip_mirror( + struct aptina_camera_module *cam_mod); +#endif + diff --git a/drivers/media/i2c/soc_camera/rockchip/imx323_v4l2-i2c-subdev.c b/drivers/media/i2c/soc_camera/rockchip/imx323_v4l2-i2c-subdev.c new file mode 100644 index 000000000000..4df5f00e34a3 --- /dev/null +++ b/drivers/media/i2c/soc_camera/rockchip/imx323_v4l2-i2c-subdev.c @@ -0,0 +1,613 @@ +/* + * IMX323 sensor driver + * + * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd. + * + * Copyright (C) 2012-2014 Intel Mobile Communications GmbH + * + * Copyright (C) 2008 Texas Instruments. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "imx_camera_module.h" + +#define IMX323_DRIVER_NAME "imx323" + +#define IMX323_AEC_PK_GAIN_REG 0x301e + +#define IMX323_AEC_PK_EXPO_HIGH_REG 0x0202 +#define IMX323_AEC_PK_EXPO_LOW_REG 0x0203 + +#define IMX323_FETCH_HIGH_BYTE_EXP(VAL) ((VAL >> 8) & 0xFF) +#define IMX323_FETCH_LOW_BYTE_EXP(VAL) (VAL & 0xFF) + +#define IMX323_PID_ADDR 0x0112 +#define IMX323_PID_MAGIC 0xa + +#define IMX323_TIMING_VTS_HIGH_REG 0x0340 +#define IMX323_TIMING_VTS_LOW_REG 0x0341 +#define IMX323_TIMING_HTS_HIGH_REG 0x0342 +#define IMX323_TIMING_HTS_LOW_REG 0x0343 + +#define IMX323_INTEGRATION_TIME_MARGIN 8 +#define IMX323_FINE_INTG_TIME_MIN 0 +#define IMX323_FINE_INTG_TIME_MAX_MARGIN 0 +#define IMX323_COARSE_INTG_TIME_MIN 16 +#define IMX323_COARSE_INTG_TIME_MAX_MARGIN 4 + +#define IMX323_ORIENTATION_REG 0x0101 +#define IMX323_ORIENTATION_H 0x1 +#define IMX323_ORIENTATION_V 0x2 + +#define IMX323_EXT_CLK 37125000 + +static struct imx_camera_module imx323; +static struct imx_camera_module_custom_config imx323_custom_config; + +/* ======================================================================== */ +/* Base sensor configs */ +/* ======================================================================== */ + +/* MCLK:37.125MHz 1920x1080 30fps DVP_12_DATA PCLK:74.25MHz */ +static struct imx_camera_module_reg imx323_init_tab_1920_1080_30fps[] = { + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x0100, 0x00}, + {IMX_CAMERA_MODULE_REG_TYPE_TIMEOUT, 0x0000, 0x01}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x0009, 0xf0}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x0112, 0x0c}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x0113, 0x0c}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x0340, 0x04}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x0341, 0x65}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x0342, 0x04}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x0343, 0x4c}, + {IMX_CAMERA_MODULE_REG_TYPE_TIMEOUT, 0x0000, 0x01}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x3000, 0x31}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x3002, 0x0f}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x3011, 0x00}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x3012, 0x82}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x3013, 0x40}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x3016, 0x3c}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x301a, 0xc9}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x301c, 0x50}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x301f, 0x73}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x3021, 0x00}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x3022, 0x40}, + {IMX_CAMERA_MODULE_REG_TYPE_TIMEOUT, 0x0000, 0x01}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x3027, 0x20}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x302c, 0x00}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x303f, 0x0a}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x304f, 0x47}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x3054, 0x11}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x307a, 0x00}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x307b, 0x00}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x3098, 0x26}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x3099, 0x02}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x309a, 0x26}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x309b, 0x02}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x30ce, 0x16}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x30cf, 0x82}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x30d0, 0x00}, + {IMX_CAMERA_MODULE_REG_TYPE_DATA, 0x3117, 0x0d} +}; + +/* ======================================================================== */ + +static struct imx_camera_module_config imx323_configs[] = { + { + .name = "1920x1080_30fps", + .frm_fmt = { + .width = 2200, + .height = 1125, + .code = MEDIA_BUS_FMT_SGBRG12_1X12 + }, + .frm_intrvl = { + .interval = { + .numerator = 1, + .denominator = 30 + } + }, + .auto_exp_enabled = false, + .auto_gain_enabled = false, + .auto_wb_enabled = false, + .reg_table = (void *)imx323_init_tab_1920_1080_30fps, + .reg_table_num_entries = + sizeof(imx323_init_tab_1920_1080_30fps) / + sizeof(imx323_init_tab_1920_1080_30fps[0]), + .v_blanking_time_us = 5000, + PLTFRM_CAM_ITF_DVP_CFG( + PLTFRM_CAM_ITF_BT601_12, + PLTFRM_CAM_SIGNAL_HIGH_LEVEL, + PLTFRM_CAM_SIGNAL_HIGH_LEVEL, + PLTFRM_CAM_SDR_NEG_EDG, + IMX323_EXT_CLK) + } +}; + +/*--------------------------------------------------------------------------*/ + +static int imx323_g_VTS(struct imx_camera_module *cam_mod, u32 *vts) +{ + u32 msb, lsb; + int ret; + + ret = imx_camera_module_read_reg_table( + cam_mod, + IMX323_TIMING_VTS_HIGH_REG, + &msb); + if (IS_ERR_VALUE(ret)) + goto err; + + ret = imx_camera_module_read_reg_table( + cam_mod, + IMX323_TIMING_VTS_LOW_REG, + &lsb); + if (IS_ERR_VALUE(ret)) + goto err; + + *vts = (msb << 8) | lsb; + + return 0; +err: + imx_camera_module_pr_err(cam_mod, "failed with error (%d)\n", ret); + return ret; +} + +/*--------------------------------------------------------------------------*/ + +static int imx323_auto_adjust_fps(struct imx_camera_module *cam_mod, + u32 exp_time) +{ + int ret; + u32 vts; + + if ((cam_mod->exp_config.exp_time + IMX323_COARSE_INTG_TIME_MAX_MARGIN) + > cam_mod->vts_min) + vts = cam_mod->exp_config.exp_time + + IMX323_COARSE_INTG_TIME_MAX_MARGIN; + else + vts = cam_mod->vts_min; + ret = imx_camera_module_write_reg(cam_mod, + IMX323_TIMING_VTS_LOW_REG, vts & 0xFF); + ret |= imx_camera_module_write_reg(cam_mod, + IMX323_TIMING_VTS_HIGH_REG, (vts >> 8) & 0xFF); + + if (IS_ERR_VALUE(ret)) + imx_camera_module_pr_err(cam_mod, + "failed with error (%d)\n", ret); + else + imx_camera_module_pr_debug(cam_mod, + "updated vts = %d,vts_min=%d\n", vts, cam_mod->vts_min); + + return ret; +} + +/*--------------------------------------------------------------------------*/ + +static int imx323_write_aec(struct imx_camera_module *cam_mod) +{ + int ret = 0; + + imx_camera_module_pr_debug(cam_mod, + "exp_time = %d, gain = %d, flash_mode = %d\n", + cam_mod->exp_config.exp_time, + cam_mod->exp_config.gain, + cam_mod->exp_config.flash_mode); + + /* + * if the sensor is already streaming, write to shadow registers, + * if the sensor is in SW standby, write to active registers, + * if the sensor is off/registers are not writeable, do nothing + */ + if ((cam_mod->state == IMX_CAMERA_MODULE_SW_STANDBY) || + (cam_mod->state == IMX_CAMERA_MODULE_STREAMING)) { + u32 a_gain = cam_mod->exp_config.gain; + u32 exp_time = cam_mod->exp_config.exp_time; + + a_gain = a_gain * cam_mod->exp_config.gain_percent / 100; + + if (!IS_ERR_VALUE(ret) && cam_mod->auto_adjust_fps) + ret = imx323_auto_adjust_fps(cam_mod, + cam_mod->exp_config.exp_time); + + /* Gain */ + ret = imx_camera_module_write_reg(cam_mod, + IMX323_AEC_PK_GAIN_REG, a_gain); + + /* Integration Time */ + ret = imx_camera_module_write_reg(cam_mod, + IMX323_AEC_PK_EXPO_HIGH_REG, + IMX323_FETCH_HIGH_BYTE_EXP(exp_time)); + ret |= imx_camera_module_write_reg(cam_mod, + IMX323_AEC_PK_EXPO_LOW_REG, + IMX323_FETCH_LOW_BYTE_EXP(exp_time)); + } + + if (IS_ERR_VALUE(ret)) + imx_camera_module_pr_err(cam_mod, + "failed with error (%d)\n", ret); + return ret; +} + +/*--------------------------------------------------------------------------*/ + +static int imx323_g_ctrl(struct imx_camera_module *cam_mod, u32 ctrl_id) +{ + int ret = 0; + + imx_camera_module_pr_debug(cam_mod, "\n"); + + switch (ctrl_id) { + case V4L2_CID_GAIN: + case V4L2_CID_EXPOSURE: + case V4L2_CID_FLASH_LED_MODE: + /* nothing to be done here */ + break; + default: + ret = -EINVAL; + break; + } + + if (IS_ERR_VALUE(ret)) + imx_camera_module_pr_debug(cam_mod, + "failed with error (%d)\n", ret); + return ret; +} + +/*--------------------------------------------------------------------------*/ + +static int imx323_filltimings(struct imx_camera_module_custom_config *custom) +{ + int i, j; + struct imx_camera_module_config *config; + struct imx_camera_module_timings *timings; + struct imx_camera_module_reg *reg_table; + int reg_table_num_entries; + + for (i = 0; i < custom->num_configs; i++) { + config = &custom->configs[i]; + reg_table = config->reg_table; + reg_table_num_entries = config->reg_table_num_entries; + timings = &config->timings; + + for (j = 0; j < reg_table_num_entries; j++) { + switch (reg_table[j].reg) { + case IMX323_TIMING_VTS_HIGH_REG: + timings->frame_length_lines = + reg_table[j].val << 8; + break; + case IMX323_TIMING_VTS_LOW_REG: + timings->frame_length_lines |= reg_table[j].val; + break; + case IMX323_TIMING_HTS_HIGH_REG: + timings->line_length_pck = + (reg_table[j].val << 8); + break; + case IMX323_TIMING_HTS_LOW_REG: + timings->line_length_pck |= reg_table[j].val; + break; + } + } + + timings->exp_time >>= 4; + timings->line_length_pck = timings->line_length_pck * 2; + timings->vt_pix_clk_freq_hz = + config->frm_intrvl.interval.denominator + * timings->frame_length_lines + * timings->line_length_pck; + + timings->coarse_integration_time_min = + IMX323_COARSE_INTG_TIME_MIN; + timings->coarse_integration_time_max_margin = + IMX323_COARSE_INTG_TIME_MAX_MARGIN; + + /* IMX Sensor do not use fine integration time. */ + timings->fine_integration_time_min = IMX323_FINE_INTG_TIME_MIN; + timings->fine_integration_time_max_margin = + IMX323_FINE_INTG_TIME_MAX_MARGIN; + } + + return 0; +} + +static int imx323_g_timings(struct imx_camera_module *cam_mod, + struct imx_camera_module_timings *timings) +{ + int ret = 0; + unsigned int vts; + + if (IS_ERR_OR_NULL(cam_mod->active_config)) + goto err; + + *timings = cam_mod->active_config->timings; + + vts = (!cam_mod->vts_cur) ? + timings->frame_length_lines : + cam_mod->vts_cur; + + if (cam_mod->frm_intrvl_valid) + timings->vt_pix_clk_freq_hz = + cam_mod->frm_intrvl.interval.denominator + * vts + * timings->line_length_pck; + else + timings->vt_pix_clk_freq_hz = + cam_mod->active_config->frm_intrvl.interval.denominator + * vts + * timings->line_length_pck; + + return ret; +err: + imx_camera_module_pr_err(cam_mod, "failed with error (%d)\n", ret); + return ret; +} + +/*--------------------------------------------------------------------------*/ + +static int imx323_set_flip(struct imx_camera_module *cam_mod) +{ + int mode = 0; + u16 orientation = 0; + + mode = imx_camera_module_get_flip_mirror(cam_mod); + if (mode == -1) { + imx_camera_module_pr_info(cam_mod, + "dts don't set flip, return!\n"); + return 0; + } + + if (!IS_ERR_OR_NULL(cam_mod->active_config)) { + if (mode == IMX_FLIP_BIT_MASK) + orientation = IMX323_ORIENTATION_V; + else if (mode == IMX_MIRROR_BIT_MASK) + orientation = IMX323_ORIENTATION_H; + else if (mode == (IMX_MIRROR_BIT_MASK | IMX_FLIP_BIT_MASK)) + orientation = IMX323_ORIENTATION_H | + IMX323_ORIENTATION_V; + else + orientation = 0; + } + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +static int imx323_s_ctrl(struct imx_camera_module *cam_mod, u32 ctrl_id) +{ + int ret = 0; + + imx_camera_module_pr_debug(cam_mod, "\n"); + + switch (ctrl_id) { + case V4L2_CID_GAIN: + case V4L2_CID_EXPOSURE: + ret = imx323_write_aec(cam_mod); + break; + case V4L2_CID_FLASH_LED_MODE: + /* nothing to be done here */ + break; + default: + ret = -EINVAL; + break; + } + + if (IS_ERR_VALUE(ret)) + imx_camera_module_pr_debug(cam_mod, + "failed with error (%d) 0x%x\n", + ret, ctrl_id); + return ret; +} + +/*--------------------------------------------------------------------------*/ + +static int imx323_s_ext_ctrls(struct imx_camera_module *cam_mod, + struct imx_camera_module_ext_ctrls *ctrls) +{ + int ret = 0; + + /* Handles only exposure and gain together special case. */ + if (ctrls->count == 1) + ret = imx323_s_ctrl(cam_mod, ctrls->ctrls[0].id); + else if ((ctrls->count == 3) && + ((ctrls->ctrls[0].id == V4L2_CID_GAIN && + ctrls->ctrls[1].id == V4L2_CID_EXPOSURE) || + (ctrls->ctrls[1].id == V4L2_CID_GAIN && + ctrls->ctrls[0].id == V4L2_CID_EXPOSURE))) + ret = imx323_write_aec(cam_mod); + else + ret = -EINVAL; + + if (IS_ERR_VALUE(ret)) + imx_camera_module_pr_debug(cam_mod, + "failed with error (%d)\n", ret); + return ret; +} + +/*--------------------------------------------------------------------------*/ + +static int imx323_start_streaming(struct imx_camera_module *cam_mod) +{ + int ret = 0; + + imx_camera_module_pr_debug(cam_mod, + "active config=%s\n", cam_mod->active_config->name); + + ret = imx323_g_VTS(cam_mod, &cam_mod->vts_min); + if (IS_ERR_VALUE(ret)) + goto err; + + if (IS_ERR_VALUE(imx_camera_module_write_reg(cam_mod, 0x0100, 1))) + goto err; + + msleep(25); + + return 0; +err: + imx_camera_module_pr_err(cam_mod, "failed with error (%d)\n", + ret); + return ret; +} + +/*--------------------------------------------------------------------------*/ + +static int imx323_stop_streaming(struct imx_camera_module *cam_mod) +{ + int ret = 0; + + imx_camera_module_pr_debug(cam_mod, "\n"); + + ret = imx_camera_module_write_reg(cam_mod, 0x0100, 0); + if (IS_ERR_VALUE(ret)) + goto err; + + msleep(25); + + return 0; +err: + imx_camera_module_pr_err(cam_mod, "failed with error (%d)\n", ret); + return ret; +} + +/*--------------------------------------------------------------------------*/ +static int imx323_check_camera_id(struct imx_camera_module *cam_mod) +{ + u32 pid; + int ret = 0; + + imx_camera_module_pr_debug(cam_mod, "\n"); + + ret |= imx_camera_module_read_reg(cam_mod, 1, IMX323_PID_ADDR, &pid); + if (IS_ERR_VALUE(ret)) { + imx_camera_module_pr_err(cam_mod, + "register read failed, camera module powered off?\n"); + goto err; + } + + if (pid == IMX323_PID_MAGIC) { + imx_camera_module_pr_debug(cam_mod, + "successfully detected camera ID 0x%02x\n", + pid); + } else { + imx_camera_module_pr_err(cam_mod, + "wrong camera ID, expected 0x%02x, detected 0x%02x\n", + IMX323_PID_MAGIC, pid); + ret = -EINVAL; + goto err; + } + + return 0; +err: + imx_camera_module_pr_err(cam_mod, "failed with error (%d)\n", ret); + return ret; +} + +/* ======================================================================== */ +/* This part is platform dependent */ +/* ======================================================================== */ + +static struct v4l2_subdev_core_ops imx323_camera_module_core_ops = { + .g_ctrl = imx_camera_module_g_ctrl, + .s_ctrl = imx_camera_module_s_ctrl, + .s_ext_ctrls = imx_camera_module_s_ext_ctrls, + .s_power = imx_camera_module_s_power, + .ioctl = imx_camera_module_ioctl +}; + +static struct v4l2_subdev_video_ops imx323_camera_module_video_ops = { + .s_frame_interval = imx_camera_module_s_frame_interval, + .s_stream = imx_camera_module_s_stream +}; + +static struct v4l2_subdev_pad_ops imx323_camera_module_pad_ops = { + .enum_frame_interval = imx_camera_module_enum_frameintervals, + .get_fmt = imx_camera_module_g_fmt, + .set_fmt = imx_camera_module_s_fmt, +}; + +static struct v4l2_subdev_ops imx323_camera_module_ops = { + .core = &imx323_camera_module_core_ops, + .video = &imx323_camera_module_video_ops, + .pad = &imx323_camera_module_pad_ops +}; + +static struct imx_camera_module_custom_config imx323_custom_config = { + .start_streaming = imx323_start_streaming, + .stop_streaming = imx323_stop_streaming, + .s_ctrl = imx323_s_ctrl, + .s_ext_ctrls = imx323_s_ext_ctrls, + .g_ctrl = imx323_g_ctrl, + .g_timings = imx323_g_timings, + .check_camera_id = imx323_check_camera_id, + .set_flip = imx323_set_flip, + .configs = imx323_configs, + .num_configs = ARRAY_SIZE(imx323_configs), + .power_up_delays_ms = {5, 20, 0} +}; + +static int imx323_probe( + struct i2c_client *client, + const struct i2c_device_id *id) +{ + dev_info(&client->dev, "probing...\n"); + + imx323_filltimings(&imx323_custom_config); + v4l2_i2c_subdev_init(&imx323.sd, client, &imx323_camera_module_ops); + imx323.custom = imx323_custom_config; + + dev_info(&client->dev, "probing successful\n"); + return 0; +} + +static int imx323_remove(struct i2c_client *client) +{ + struct imx_camera_module *cam_mod = i2c_get_clientdata(client); + + dev_info(&client->dev, "removing device...\n"); + + if (!client->adapter) + return -ENODEV; /* our client isn't attached */ + + imx_camera_module_release(cam_mod); + + dev_info(&client->dev, "removed\n"); + return 0; +} + +static const struct i2c_device_id imx323_id[] = { + { IMX323_DRIVER_NAME, 0 }, + { } +}; + +static const struct of_device_id imx323_of_match[] = { + {.compatible = "sony,imx323-v4l2-i2c-subdev"}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, imx323_id); + +static struct i2c_driver imx323_i2c_driver = { + .driver = { + .name = IMX323_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = imx323_of_match + }, + .probe = imx323_probe, + .remove = imx323_remove, + .id_table = imx323_id, +}; + +module_i2c_driver(imx323_i2c_driver); + +MODULE_DESCRIPTION("SoC Camera driver for IMX323"); +MODULE_AUTHOR("George"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/soc_camera/rockchip/imx_camera_module.c b/drivers/media/i2c/soc_camera/rockchip/imx_camera_module.c new file mode 100644 index 000000000000..34317d5fd30a --- /dev/null +++ b/drivers/media/i2c/soc_camera/rockchip/imx_camera_module.c @@ -0,0 +1,1108 @@ +/* + * imx_camera_module.c + * + * Generic sony sensor driver + * + * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd. + * + * Copyright (C) 2012-2014 Intel Mobile Communications GmbH + * + * Copyright (C) 2008 Texas Instruments. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "imx_camera_module.h" + +static struct imx_camera_module *to_imx_camera_module(struct v4l2_subdev *sd) +{ + return container_of(sd, struct imx_camera_module, sd); +} + +/* ======================================================================== */ + +static void imx_camera_module_reset( + struct imx_camera_module *cam_mod) +{ + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + + cam_mod->inited = false; + cam_mod->active_config = NULL; + cam_mod->update_config = true; + cam_mod->frm_fmt_valid = false; + cam_mod->frm_intrvl_valid = false; + cam_mod->exp_config.auto_exp = false; + cam_mod->exp_config.auto_gain = false; + cam_mod->wb_config.auto_wb = false; + cam_mod->hflip = false; + cam_mod->vflip = false; + cam_mod->auto_adjust_fps = true; + cam_mod->rotation = 0; + cam_mod->ctrl_updt = 0; + cam_mod->state = IMX_CAMERA_MODULE_POWER_OFF; + cam_mod->state_before_suspend = IMX_CAMERA_MODULE_POWER_OFF; + cam_mod->exp_config.exp_time = 0; + cam_mod->exp_config.gain = 0; + cam_mod->vts_cur = 0; +} + +/* ======================================================================== */ + +static void imx_camera_module_set_active_config( + struct imx_camera_module *cam_mod, + struct imx_camera_module_config *new_config) +{ + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + + if (IS_ERR_OR_NULL(new_config)) { + cam_mod->active_config = new_config; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "no active config\n"); + } else { + cam_mod->ctrl_updt &= IMX_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP | + IMX_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN | + IMX_CAMERA_MODULE_CTRL_UPDT_AUTO_WB; + if (new_config->auto_exp_enabled != + cam_mod->exp_config.auto_exp) { + cam_mod->ctrl_updt |= + IMX_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP; + cam_mod->exp_config.auto_exp = + new_config->auto_exp_enabled; + } + if (new_config->auto_gain_enabled != + cam_mod->exp_config.auto_gain) { + cam_mod->ctrl_updt |= + IMX_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN; + cam_mod->exp_config.auto_gain = + new_config->auto_gain_enabled; + } + if (new_config->auto_wb_enabled != + cam_mod->wb_config.auto_wb) { + cam_mod->ctrl_updt |= + IMX_CAMERA_MODULE_CTRL_UPDT_AUTO_WB; + cam_mod->wb_config.auto_wb = + new_config->auto_wb_enabled; + } + if (new_config != cam_mod->active_config) { + cam_mod->update_config = true; + cam_mod->active_config = new_config; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "activating config '%s'\n", + cam_mod->active_config->name); + } + } +} + +/* ======================================================================== */ + +static struct imx_camera_module_config *imx_camera_module_find_config( + struct imx_camera_module *cam_mod, + struct v4l2_mbus_framefmt *fmt, + struct v4l2_subdev_frame_interval *frm_intrvl) +{ + u32 i; + unsigned long gcdiv; + struct v4l2_subdev_frame_interval norm_interval; + + if (!IS_ERR_OR_NULL(fmt)) + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "%dx%d, fmt code 0x%04x\n", + fmt->width, fmt->height, fmt->code); + + if (!IS_ERR_OR_NULL(frm_intrvl)) + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "frame interval %d/%d\n", + frm_intrvl->interval.numerator, + frm_intrvl->interval.denominator); + + for (i = 0; i < cam_mod->custom.num_configs; i++) { + if (!IS_ERR_OR_NULL(frm_intrvl)) { + gcdiv = gcd(cam_mod->custom.configs[i]. + frm_intrvl.interval.numerator, + cam_mod->custom.configs[i]. + frm_intrvl.interval.denominator); + norm_interval.interval.numerator = + cam_mod->custom.configs[i]. + frm_intrvl.interval.numerator / gcdiv; + norm_interval.interval.denominator = + cam_mod->custom.configs[i]. + frm_intrvl.interval.denominator / gcdiv; + if ((frm_intrvl->interval.numerator != + norm_interval.interval.numerator) || + (frm_intrvl->interval.denominator != + norm_interval.interval.denominator)) + continue; + } + if (!IS_ERR_OR_NULL(fmt)) { + if ((cam_mod->custom.configs[i].frm_fmt.width != + fmt->width) || + (cam_mod->custom.configs[i].frm_fmt.height != + fmt->height) || + (cam_mod->custom.configs[i].frm_fmt.code != + fmt->code)) { + continue; + } + } + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "found matching config %s\n", + cam_mod->custom.configs[i].name); + return &cam_mod->custom.configs[i]; + } + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "no matching config found\n"); + + return ERR_PTR(-EINVAL); +} + +/* ======================================================================== */ + +static int imx_camera_module_write_config( + struct imx_camera_module *cam_mod) +{ + int ret = 0; + struct imx_camera_module_reg *reg_table; + u32 reg_table_num_entries; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + + if (IS_ERR_OR_NULL(cam_mod->active_config)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "no active sensor configuration"); + ret = -EFAULT; + goto err; + } + + if (cam_mod->inited == false) { + cam_mod->active_config->soft_reset = true; + reg_table = cam_mod->active_config->reg_table; + reg_table_num_entries = + cam_mod->active_config->reg_table_num_entries; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "write config %s\n", + cam_mod->active_config->name); + } + + if (!IS_ERR_OR_NULL(cam_mod->custom.set_flip)) + cam_mod->custom.set_flip(cam_mod); + + ret = pltfrm_camera_module_write_reglist(&cam_mod->sd, + reg_table, reg_table_num_entries); + + if (IS_ERR_VALUE(ret)) + goto err; + ret = pltfrm_camera_module_patch_config(&cam_mod->sd, + &cam_mod->frm_fmt, + &cam_mod->frm_intrvl); + if (IS_ERR_VALUE(ret)) + goto err; + + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; +} + +static int imx_camera_module_attach( + struct imx_camera_module *cam_mod) +{ + int ret = 0; + struct imx_camera_module_custom_config *custom; + + custom = &cam_mod->custom; + + if (custom->check_camera_id) { + imx_camera_module_s_power(&cam_mod->sd, 1); + ret = custom->check_camera_id(cam_mod); + imx_camera_module_s_power(&cam_mod->sd, 0); + if (ret != 0) + goto err; + } + + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + imx_camera_module_release(cam_mod); + return ret; +} + +/* ======================================================================== */ + +int imx_camera_module_try_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct imx_camera_module *cam_mod = to_imx_camera_module(sd); + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "%dx%d, fmt code 0x%04x\n", + fmt->width, fmt->height, fmt->code); + + if (IS_ERR_OR_NULL(imx_camera_module_find_config(cam_mod, fmt, NULL))) { + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "format not supported\n"); + return -EINVAL; + } + pltfrm_camera_module_pr_debug(&cam_mod->sd, "format supported\n"); + + return 0; +} + +/* ======================================================================== */ + +int imx_camera_module_s_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct imx_camera_module *cam_mod = to_imx_camera_module(sd); + struct v4l2_mbus_framefmt *fmt = &format->format; + int ret = 0; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "%dx%d, fmt code 0x%04x\n", + fmt->width, fmt->height, fmt->code); + + if (IS_ERR_OR_NULL(imx_camera_module_find_config(cam_mod, fmt, NULL))) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "format %dx%d, code 0x%04x, not supported\n", + fmt->width, fmt->height, fmt->code); + ret = -EINVAL; + goto err; + } + cam_mod->frm_fmt_valid = true; + cam_mod->frm_fmt = *fmt; + if (cam_mod->frm_intrvl_valid) { + imx_camera_module_set_active_config(cam_mod, + imx_camera_module_find_config(cam_mod, + fmt, &cam_mod->frm_intrvl)); + } + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; +} + +/* ======================================================================== */ + +int imx_camera_module_g_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct imx_camera_module *cam_mod = to_imx_camera_module(sd); + struct v4l2_mbus_framefmt *fmt = &format->format; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + + if (cam_mod->active_config) { + fmt->code = cam_mod->active_config->frm_fmt.code; + fmt->width = cam_mod->active_config->frm_fmt.width; + fmt->height = cam_mod->active_config->frm_fmt.height; + return 0; + } + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "no active config\n"); + + return -1; +} + +/* ======================================================================== */ + +int imx_camera_module_s_frame_interval( + struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *interval) +{ + struct imx_camera_module *cam_mod = to_imx_camera_module(sd); + unsigned long gcdiv; + struct v4l2_subdev_frame_interval norm_interval; + int ret = 0; + + if ((interval->interval.denominator == 0) || + (interval->interval.numerator == 0)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "invalid frame interval %d/%d\n", + interval->interval.numerator, + interval->interval.denominator); + ret = -EINVAL; + goto err; + } + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "%d/%d (%dfps)\n", + interval->interval.numerator, interval->interval.denominator, + (interval->interval.denominator + + (interval->interval.numerator >> 1)) / + interval->interval.numerator); + + /* normalize interval */ + gcdiv = gcd(interval->interval.numerator, + interval->interval.denominator); + norm_interval.interval.numerator = + interval->interval.numerator / gcdiv; + norm_interval.interval.denominator = + interval->interval.denominator / gcdiv; + + if (IS_ERR_OR_NULL(imx_camera_module_find_config(cam_mod, + NULL, &norm_interval))) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "frame interval %d/%d not supported\n", + interval->interval.numerator, + interval->interval.denominator); + ret = -EINVAL; + goto err; + } + cam_mod->frm_intrvl_valid = true; + cam_mod->frm_intrvl = norm_interval; + if (cam_mod->frm_fmt_valid) { + imx_camera_module_set_active_config(cam_mod, + imx_camera_module_find_config(cam_mod, + &cam_mod->frm_fmt, interval)); + } + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; +} + +/* ======================================================================== */ + +int imx_camera_module_s_stream(struct v4l2_subdev *sd, int enable) +{ + int ret = 0; + struct imx_camera_module *cam_mod = to_imx_camera_module(sd); + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "%d\n", enable); + + if (enable) { + if (cam_mod->state == IMX_CAMERA_MODULE_STREAMING) + return 0; + if (IS_ERR_OR_NULL(cam_mod->active_config)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "no active sensor configuration, cannot start streaming\n"); + ret = -EFAULT; + goto err; + } + if (cam_mod->state != IMX_CAMERA_MODULE_SW_STANDBY) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "sensor is not powered on (in state %d), cannot start streaming\n", + cam_mod->state); + ret = -EINVAL; + goto err; + } + if (cam_mod->update_config) + ret = imx_camera_module_write_config(cam_mod); + if (IS_ERR_VALUE(ret)) + goto err; + + ret = cam_mod->custom.start_streaming(cam_mod); + if (IS_ERR_VALUE(ret)) + goto err; + if (!cam_mod->inited && cam_mod->update_config) + cam_mod->inited = true; + cam_mod->update_config = false; + cam_mod->ctrl_updt = 0; + mdelay(cam_mod->custom.power_up_delays_ms[2]); + cam_mod->state = IMX_CAMERA_MODULE_STREAMING; + } else { + int pclk; + int wait_ms; + struct isp_supplemental_sensor_mode_data timings; + + if (cam_mod->state != IMX_CAMERA_MODULE_STREAMING) + return 0; + ret = cam_mod->custom.stop_streaming(cam_mod); + if (IS_ERR_VALUE(ret)) + goto err; + + ret = imx_camera_module_ioctl(sd, + RK_VIDIOC_SENSOR_MODE_DATA, + &timings); + + cam_mod->state = IMX_CAMERA_MODULE_SW_STANDBY; + + if (IS_ERR_VALUE(ret)) + goto err; + + pclk = timings.vt_pix_clk_freq_hz / 1000; + + if (!pclk) + goto err; + + wait_ms = + (timings.line_length_pck * + timings.frame_length_lines) / + pclk; + + /* + * wait for a frame period to make sure that there is + * no pending frame left. + */ + + msleep(wait_ms + 1); + } + + cam_mod->state_before_suspend = cam_mod->state; + + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; +} + +/* ======================================================================== */ + +int imx_camera_module_s_power(struct v4l2_subdev *sd, int on) +{ + int ret = 0; + struct imx_camera_module *cam_mod = to_imx_camera_module(sd); + struct v4l2_subdev *af_ctrl; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "%d state:%d\n", on, cam_mod->state); + + if (on) { + if (cam_mod->state == IMX_CAMERA_MODULE_POWER_OFF) { + ret = pltfrm_camera_module_s_power(&cam_mod->sd, 1); + if (!IS_ERR_VALUE(ret)) { + mdelay(cam_mod->custom.power_up_delays_ms[0]); + cam_mod->state = IMX_CAMERA_MODULE_HW_STANDBY; + } + } + if (cam_mod->state == IMX_CAMERA_MODULE_HW_STANDBY) { + ret = pltfrm_camera_module_set_pin_state(&cam_mod->sd, + PLTFRM_CAMERA_MODULE_PIN_PD, + PLTFRM_CAMERA_MODULE_PIN_STATE_INACTIVE); + if (!IS_ERR_VALUE(ret)) { + mdelay(cam_mod->custom.power_up_delays_ms[1]); + cam_mod->state = IMX_CAMERA_MODULE_SW_STANDBY; + if (!IS_ERR_OR_NULL( + cam_mod->custom.init_common) && + cam_mod->custom.init_common( + cam_mod)) + usleep_range(1000, 1500); + + af_ctrl = pltfrm_camera_module_get_af_ctrl(sd); + if (!IS_ERR_OR_NULL(af_ctrl)) { + v4l2_subdev_call(af_ctrl, + core, init, 0); + } + } + } + } else { + if (cam_mod->state == IMX_CAMERA_MODULE_STREAMING) { + ret = imx_camera_module_s_stream(sd, 0); + if (!IS_ERR_VALUE(ret)) + cam_mod->state = IMX_CAMERA_MODULE_SW_STANDBY; + } + if (cam_mod->state == IMX_CAMERA_MODULE_SW_STANDBY) { + ret = pltfrm_camera_module_set_pin_state( + &cam_mod->sd, + PLTFRM_CAMERA_MODULE_PIN_PD, + PLTFRM_CAMERA_MODULE_PIN_STATE_ACTIVE); + if (!IS_ERR_VALUE(ret)) + cam_mod->state = IMX_CAMERA_MODULE_HW_STANDBY; + } + if (cam_mod->state == IMX_CAMERA_MODULE_HW_STANDBY) { + ret = pltfrm_camera_module_s_power(&cam_mod->sd, 0); + if (!IS_ERR_VALUE(ret)) { + cam_mod->state = IMX_CAMERA_MODULE_POWER_OFF; + imx_camera_module_reset(cam_mod); + } + } + } + + cam_mod->state_before_suspend = cam_mod->state; + + if (IS_ERR_VALUE(ret)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "%s failed, camera left in state %d\n", + on ? "on" : "off", cam_mod->state); + goto err; + } else + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "camera powered %s\n", on ? "on" : "off"); + + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; +} + +/* ======================================================================== */ + +int imx_camera_module_g_ctrl(struct v4l2_subdev *sd, + struct v4l2_control *ctrl) +{ + struct imx_camera_module *cam_mod = to_imx_camera_module(sd); + int ret; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, " id 0x%x\n", ctrl->id); + + if (ctrl->id == V4L2_CID_FLASH_LED_MODE) { + ctrl->value = cam_mod->exp_config.flash_mode; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_FLASH_LED_MODE %d\n", + ctrl->value); + return 0; + } + + if (IS_ERR_OR_NULL(cam_mod->active_config)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "no active configuration\n"); + return -EFAULT; + } + + if (ctrl->id == RK_V4L2_CID_VBLANKING) { + ctrl->value = cam_mod->active_config->v_blanking_time_us; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "RK_V4L2_CID_VBLANKING %d\n", + ctrl->value); + return 0; + } + + if ((cam_mod->state != IMX_CAMERA_MODULE_SW_STANDBY) && + (cam_mod->state != IMX_CAMERA_MODULE_STREAMING)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "cannot get controls when camera is off\n"); + return -EFAULT; + } + + if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE) { + struct v4l2_subdev *af_ctrl; + + af_ctrl = pltfrm_camera_module_get_af_ctrl(sd); + if (!IS_ERR_OR_NULL(af_ctrl)) { + ret = v4l2_subdev_call(af_ctrl, core, g_ctrl, ctrl); + return ret; + } + } + + if (!IS_ERR_OR_NULL(cam_mod->custom.g_ctrl)) { + ret = cam_mod->custom.g_ctrl(cam_mod, ctrl->id); + if (IS_ERR_VALUE(ret)) + return ret; + } + + switch (ctrl->id) { + case V4L2_CID_GAIN: + ctrl->value = cam_mod->exp_config.gain; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_GAIN %d\n", + ctrl->value); + break; + case V4L2_CID_EXPOSURE: + ctrl->value = cam_mod->exp_config.exp_time; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_EXPOSURE %d\n", + ctrl->value); + break; + case V4L2_CID_WHITE_BALANCE_TEMPERATURE: + ctrl->value = cam_mod->wb_config.temperature; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_WHITE_BALANCE_TEMPERATURE %d\n", + ctrl->value); + break; + case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: + ctrl->value = cam_mod->wb_config.preset_id; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE %d\n", + ctrl->value); + break; + case V4L2_CID_AUTOGAIN: + ctrl->value = cam_mod->exp_config.auto_gain; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_AUTOGAIN %d\n", + ctrl->value); + break; + case V4L2_CID_EXPOSURE_AUTO: + ctrl->value = cam_mod->exp_config.auto_exp; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_EXPOSURE_AUTO %d\n", + ctrl->value); + break; + case V4L2_CID_AUTO_WHITE_BALANCE: + ctrl->value = cam_mod->wb_config.auto_wb; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_AUTO_WHITE_BALANCE %d\n", + ctrl->value); + break; + case V4L2_CID_FOCUS_ABSOLUTE: + ctrl->value = cam_mod->af_config.abs_pos; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_FOCUS_ABSOLUTE %d\n", + ctrl->value); + break; + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + /* TBD */ + /* fallthrough */ + default: + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "failed, unknown ctrl %d\n", ctrl->id); + return -EINVAL; + } + + return 0; +} + +static int flash_light_ctrl( + struct v4l2_subdev *sd, + struct imx_camera_module *cam_mod, + int value) +{ + return 0; +} + +/* ======================================================================== */ + +int imx_camera_module_s_ext_ctrls( + struct v4l2_subdev *sd, + struct v4l2_ext_controls *ctrls) +{ + int i; + int ctrl_cnt = 0; + struct imx_camera_module *cam_mod = to_imx_camera_module(sd); + int ret = 0; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + if (ctrls->count == 0) + return -EINVAL; + + for (i = 0; i < ctrls->count; i++) { + struct v4l2_ext_control *ctrl; + u32 ctrl_updt = 0; + + ctrl = &ctrls->controls[i]; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + ctrl_updt = IMX_CAMERA_MODULE_CTRL_UPDT_GAIN; + cam_mod->exp_config.gain = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_GAIN %d\n", + ctrl->value); + break; + case RK_V4L2_CID_GAIN_PERCENT: + ctrl_updt = IMX_CAMERA_MODULE_CTRL_UPDT_GAIN; + cam_mod->exp_config.gain_percent = ctrl->value; + break; + case V4L2_CID_FLASH_LED_MODE: + ret = flash_light_ctrl(sd, cam_mod, ctrl->value); + if (ret == 0) { + cam_mod->exp_config.flash_mode = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_FLASH_LED_MODE %d\n", + ctrl->value); + } + break; + case V4L2_CID_EXPOSURE: + ctrl_updt = IMX_CAMERA_MODULE_CTRL_UPDT_EXP_TIME; + cam_mod->exp_config.exp_time = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_EXPOSURE %d\n", + ctrl->value); + break; + case V4L2_CID_WHITE_BALANCE_TEMPERATURE: + ctrl_updt = IMX_CAMERA_MODULE_CTRL_UPDT_WB_TEMPERATURE; + cam_mod->wb_config.temperature = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_WHITE_BALANCE_TEMPERATURE %d\n", + ctrl->value); + break; + case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: + ctrl_updt = IMX_CAMERA_MODULE_CTRL_UPDT_PRESET_WB; + cam_mod->wb_config.preset_id = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE %d\n", + ctrl->value); + break; + case V4L2_CID_AUTOGAIN: + ctrl_updt = IMX_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN; + cam_mod->exp_config.auto_gain = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_AUTOGAIN %d\n", + ctrl->value); + break; + case V4L2_CID_EXPOSURE_AUTO: + ctrl_updt = IMX_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP; + cam_mod->exp_config.auto_exp = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_EXPOSURE_AUTO %d\n", + ctrl->value); + break; + case V4L2_CID_AUTO_WHITE_BALANCE: + ctrl_updt = IMX_CAMERA_MODULE_CTRL_UPDT_AUTO_WB; + cam_mod->wb_config.auto_wb = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_AUTO_WHITE_BALANCE %d\n", + ctrl->value); + break; + case RK_V4L2_CID_AUTO_FPS: + cam_mod->auto_adjust_fps = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "RK_V4L2_CID_AUTO_FPS %d\n", + ctrl->value); + break; + case V4L2_CID_FOCUS_ABSOLUTE: + { + struct v4l2_subdev *af_ctrl; + + af_ctrl = pltfrm_camera_module_get_af_ctrl(sd); + if (!IS_ERR_OR_NULL(af_ctrl)) { + struct v4l2_control single_ctrl; + + single_ctrl.id = + V4L2_CID_FOCUS_ABSOLUTE; + single_ctrl.value = ctrl->value; + ret = v4l2_subdev_call(af_ctrl, + core, s_ctrl, &single_ctrl); + return ret; + } + } + ctrl_updt = + IMX_CAMERA_MODULE_CTRL_UPDT_FOCUS_ABSOLUTE; + cam_mod->af_config.abs_pos = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_FOCUS_ABSOLUTE %d\n", + ctrl->value); + break; + case V4L2_CID_HFLIP: + if (ctrl->value) + cam_mod->hflip = true; + else + cam_mod->hflip = false; + break; + case V4L2_CID_VFLIP: + if (ctrl->value) + cam_mod->vflip = true; + else + cam_mod->vflip = false; + break; + default: + pltfrm_camera_module_pr_warn(&cam_mod->sd, + "ignoring unknown ctrl 0x%x\n", ctrl->id); + break; + } + + if (cam_mod->state != IMX_CAMERA_MODULE_SW_STANDBY && + cam_mod->state != IMX_CAMERA_MODULE_STREAMING) + cam_mod->ctrl_updt |= ctrl_updt; + else if (ctrl_updt) + ctrl_cnt++; + } + + /* if camera module is already streaming, write through */ + if (ctrl_cnt && + (cam_mod->state == IMX_CAMERA_MODULE_STREAMING || + cam_mod->state == IMX_CAMERA_MODULE_SW_STANDBY)) { + struct imx_camera_module_ext_ctrls imx_ctrls; + + imx_ctrls.ctrls = + (struct imx_camera_module_ext_ctrl *) + kmalloc(ctrl_cnt * sizeof(struct imx_camera_module_ext_ctrl), + GFP_KERNEL); + + if (imx_ctrls.ctrls) { + for (i = 0; i < ctrl_cnt; i++) { + imx_ctrls.ctrls[i].id = ctrls->controls[i].id; + imx_ctrls.ctrls[i].value = + ctrls->controls[i].value; + } + + imx_ctrls.count = ctrl_cnt; + + ret = cam_mod->custom.s_ext_ctrls(cam_mod, &imx_ctrls); + + kfree(imx_ctrls.ctrls); + } else { + ret = -ENOMEM; + } + + if (IS_ERR_VALUE(ret)) + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "failed with error %d\n", ret); + } + + return ret; +} + +/* ======================================================================== */ + +int imx_camera_module_s_ctrl( + struct v4l2_subdev *sd, + struct v4l2_control *ctrl) +{ + struct imx_camera_module *cam_mod = to_imx_camera_module(sd); + struct v4l2_ext_control ext_ctrl[1]; + struct v4l2_ext_controls ext_ctrls; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "0x%x 0x%x\n", ctrl->id, ctrl->value); + + ext_ctrl[0].id = ctrl->id; + ext_ctrl[0].value = ctrl->value; + + ext_ctrls.count = 1; + ext_ctrls.controls = ext_ctrl; + + return imx_camera_module_s_ext_ctrls(sd, &ext_ctrls); +} + +/* ======================================================================== */ + +long imx_camera_module_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, + void *arg) +{ + struct imx_camera_module *cam_mod = to_imx_camera_module(sd); + int ret; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "cmd: 0x%x\n", cmd); + + if (cmd == RK_VIDIOC_SENSOR_MODE_DATA) { + struct imx_camera_module_timings imx_timings; + struct isp_supplemental_sensor_mode_data *timings = + (struct isp_supplemental_sensor_mode_data *)arg; + + if (cam_mod->custom.g_timings) + ret = cam_mod->custom.g_timings(cam_mod, &imx_timings); + else + ret = -EPERM; + + if (IS_ERR_VALUE(ret)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; + } + + timings->sensor_output_width = + imx_timings.sensor_output_width; + timings->sensor_output_height = + imx_timings.sensor_output_height; + timings->crop_horizontal_start = + imx_timings.crop_horizontal_start; + timings->crop_vertical_start = + imx_timings.crop_vertical_start; + timings->crop_horizontal_end = + imx_timings.crop_horizontal_end; + timings->crop_vertical_end = imx_timings.crop_vertical_end; + timings->line_length_pck = imx_timings.line_length_pck; + timings->frame_length_lines = imx_timings.frame_length_lines; + timings->vt_pix_clk_freq_hz = imx_timings.vt_pix_clk_freq_hz; + timings->binning_factor_x = imx_timings.binning_factor_x; + timings->binning_factor_y = imx_timings.binning_factor_y; + timings->coarse_integration_time_max_margin = + imx_timings.coarse_integration_time_max_margin; + timings->coarse_integration_time_min = + imx_timings.coarse_integration_time_min; + timings->fine_integration_time_max_margin = + imx_timings.fine_integration_time_max_margin; + timings->fine_integration_time_min = + imx_timings.fine_integration_time_min; + + if (cam_mod->custom.g_exposure_valid_frame) + timings->exposure_valid_frame = + cam_mod->custom.g_exposure_valid_frame(cam_mod); + timings->exp_time = cam_mod->exp_config.exp_time; + timings->gain = cam_mod->exp_config.gain; + + if (cam_mod->exp_config.exp_time) + timings->exp_time = cam_mod->exp_config.exp_time; + else + timings->exp_time = imx_timings.exp_time; + + if (cam_mod->exp_config.gain) + timings->gain = cam_mod->exp_config.gain; + else + timings->gain = imx_timings.gain; + return ret; + } else if (cmd == PLTFRM_CIFCAM_G_ITF_CFG) { + struct pltfrm_cam_itf *itf_cfg = (struct pltfrm_cam_itf *)arg; + struct imx_camera_module_config *config; + + if (cam_mod->custom.num_configs <= 0) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "cam_mod->custom.num_configs is NULL, Get interface config failed!\n"); + return -EINVAL; + } + + if (IS_ERR_OR_NULL(cam_mod->active_config)) + config = &cam_mod->custom.configs[0]; + else + config = cam_mod->active_config; + + *itf_cfg = config->itf_cfg; + + pltfrm_camera_module_ioctl(sd, PLTFRM_CIFCAM_G_ITF_CFG, arg); + return 0; + } else if (cmd == PLTFRM_CIFCAM_ATTACH) { + imx_camera_module_init(cam_mod, &cam_mod->custom); + pltfrm_camera_module_ioctl(sd, cmd, arg); + return imx_camera_module_attach(cam_mod); + } + + ret = pltfrm_camera_module_ioctl(sd, cmd, arg); + return ret; +} + +/* ======================================================================== */ + +int imx_camera_module_get_flip_mirror( + struct imx_camera_module *cam_mod) +{ + return pltfrm_camera_module_get_flip_mirror(&cam_mod->sd); +} + +/* ======================================================================== */ + +int imx_camera_module_enum_frameintervals( + struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct imx_camera_module *cam_mod = to_imx_camera_module(sd); + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "%d\n", fie->index); + + if (fie->index >= cam_mod->custom.num_configs) + return -EINVAL; + fie->code = + cam_mod->custom.configs[fie->index].frm_fmt.code; + fie->width = + cam_mod->custom.configs[fie->index].frm_fmt.width; + fie->height = + cam_mod->custom.configs[fie->index].frm_fmt.height; + fie->interval.numerator = cam_mod->custom. + configs[fie->index].frm_intrvl.interval.numerator; + fie->interval.denominator = cam_mod->custom. + configs[fie->index].frm_intrvl.interval.denominator; + return 0; +} + +/* ======================================================================== */ + +int imx_camera_module_write_reglist( + struct imx_camera_module *cam_mod, + const struct imx_camera_module_reg reglist[], + int len) +{ + return pltfrm_camera_module_write_reglist(&cam_mod->sd, reglist, len); +} + +/* ======================================================================== */ + +int imx_camera_module_write_reg( + struct imx_camera_module *cam_mod, + u16 reg, + u8 val) +{ + return pltfrm_camera_module_write_reg(&cam_mod->sd, reg, val); +} + +/* ======================================================================== */ + +int imx_camera_module_read_reg( + struct imx_camera_module *cam_mod, + u16 data_length, + u16 reg, + u32 *val) +{ + return pltfrm_camera_module_read_reg(&cam_mod->sd, + data_length, reg, val); +} + +/* ======================================================================== */ + +int imx_camera_module_read_reg_table( + struct imx_camera_module *cam_mod, + u16 reg, + u32 *val) +{ + int i; + + if (cam_mod->state == IMX_CAMERA_MODULE_STREAMING) + return pltfrm_camera_module_read_reg(&cam_mod->sd, + 1, reg, val); + + if (!IS_ERR_OR_NULL(cam_mod->active_config)) { + for ( + i = cam_mod->active_config->reg_table_num_entries - 1; + i > 0; + i--) { + if (cam_mod->active_config->reg_table[i].reg == reg) { + *val = cam_mod->active_config->reg_table[i].val; + return 0; + } + } + } + + if (cam_mod->state == IMX_CAMERA_MODULE_SW_STANDBY) + return pltfrm_camera_module_read_reg(&cam_mod->sd, + 1, reg, val); + + return -EFAULT; +} + +/* ======================================================================== */ + +int imx_camera_module_init(struct imx_camera_module *cam_mod, + struct imx_camera_module_custom_config *custom) +{ + int ret = 0; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + + imx_camera_module_reset(cam_mod); + + if (IS_ERR_OR_NULL(custom->start_streaming) || + IS_ERR_OR_NULL(custom->stop_streaming) || + IS_ERR_OR_NULL(custom->s_ctrl) || + IS_ERR_OR_NULL(custom->g_ctrl)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "mandatory callback function is missing\n"); + ret = -EINVAL; + goto err; + } + + ret = pltfrm_camera_module_init(&cam_mod->sd, &cam_mod->pltfm_data); + if (IS_ERR_VALUE(ret)) + goto err; + + ret = pltfrm_camera_module_set_pin_state(&cam_mod->sd, + PLTFRM_CAMERA_MODULE_PIN_PD, + PLTFRM_CAMERA_MODULE_PIN_STATE_INACTIVE); + ret = pltfrm_camera_module_set_pin_state(&cam_mod->sd, + PLTFRM_CAMERA_MODULE_PIN_RESET, + PLTFRM_CAMERA_MODULE_PIN_STATE_ACTIVE); + if (IS_ERR_VALUE(ret)) { + imx_camera_module_release(cam_mod); + goto err; + } + + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; +} + +void imx_camera_module_release(struct imx_camera_module *cam_mod) +{ + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + + cam_mod->custom.configs = NULL; + + pltfrm_camera_module_release(&cam_mod->sd); + v4l2_device_unregister_subdev(&cam_mod->sd); +} diff --git a/drivers/media/i2c/soc_camera/rockchip/imx_camera_module.h b/drivers/media/i2c/soc_camera/rockchip/imx_camera_module.h new file mode 100644 index 000000000000..787beecd0a5d --- /dev/null +++ b/drivers/media/i2c/soc_camera/rockchip/imx_camera_module.h @@ -0,0 +1,278 @@ +/* + * imx_camera_module.h + * + * Generic sony sensor driver + * + * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd. + * + * Copyright (C) 2012-2014 Intel Mobile Communications GmbH + * + * Copyright (C) 2008 Texas Instruments. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + */ + +#ifndef IMX_CAMERA_MODULE_H +#define IMX_CAMERA_MODULE_H +#include +#include +#include +/* + * TODO: references to v4l2 should be reomved from here and go into a + * platform dependent wrapper + */ + +#define IMX_CAMERA_MODULE_REG_TYPE_DATA PLTFRM_CAMERA_MODULE_REG_TYPE_DATA +#define IMX_CAMERA_MODULE_REG_TYPE_TIMEOUT PLTFRM_CAMERA_MODULE_REG_TYPE_TIMEOUT +#define imx_camera_module_csi_config +#define imx_camera_module_reg pltfrm_camera_module_reg + +#define IMX_FLIP_BIT_MASK 0x2 +#define IMX_MIRROR_BIT_MASK 0x1 + +#define IMX_CAMERA_MODULE_CTRL_UPDT_GAIN 0x01 +#define IMX_CAMERA_MODULE_CTRL_UPDT_EXP_TIME 0x02 +#define IMX_CAMERA_MODULE_CTRL_UPDT_WB_TEMPERATURE 0x04 +#define IMX_CAMERA_MODULE_CTRL_UPDT_AUTO_WB 0x08 +#define IMX_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN 0x10 +#define IMX_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP 0x20 +#define IMX_CAMERA_MODULE_CTRL_UPDT_FOCUS_ABSOLUTE 0x40 +#define IMX_CAMERA_MODULE_CTRL_UPDT_PRESET_WB 0x80 + +enum imx_camera_module_state { + IMX_CAMERA_MODULE_POWER_OFF = 0, + IMX_CAMERA_MODULE_HW_STANDBY = 1, + IMX_CAMERA_MODULE_SW_STANDBY = 2, + IMX_CAMERA_MODULE_STREAMING = 3 +}; + +struct imx_camera_module; + +struct imx_camera_module_timings { + u32 coarse_integration_time_min; + u32 coarse_integration_time_max_margin; + u32 fine_integration_time_min; + u32 fine_integration_time_max_margin; + u32 frame_length_lines; + u32 line_length_pck; + u32 vt_pix_clk_freq_hz; + u32 sensor_output_width; + u32 sensor_output_height; + u32 crop_horizontal_start; + u32 crop_vertical_start; + u32 crop_horizontal_end; + u32 crop_vertical_end; + u8 binning_factor_x; + u8 binning_factor_y; + u32 exp_time; + u32 gain; +}; + +struct imx_camera_module_config { + const char *name; + struct v4l2_mbus_framefmt frm_fmt; + struct v4l2_subdev_frame_interval frm_intrvl; + bool auto_exp_enabled; + bool auto_gain_enabled; + bool auto_wb_enabled; + struct imx_camera_module_reg *reg_table; + u32 reg_table_num_entries; + u32 reg_diff_table_num_entries; + u32 v_blanking_time_us; + u32 line_length_pck; + u32 frame_length_lines; + struct imx_camera_module_timings timings; + bool soft_reset; + bool ignore_measurement_check; + struct pltfrm_cam_itf itf_cfg; +}; + +struct imx_camera_module_exp_config { + u32 exp_time; + bool auto_exp; + u16 gain; + u16 gain_percent; + bool auto_gain; + enum v4l2_flash_led_mode flash_mode; +}; + +struct imx_camera_module_wb_config { + u32 temperature; + u32 preset_id; + bool auto_wb; +}; + +struct imx_camera_module_af_config { + u32 abs_pos; + u32 rel_pos; +}; + +struct imx_camera_module_ext_ctrl { + /* public */ + u32 id; + u32 value; + __u32 reserved2[1]; +}; + +struct imx_camera_module_ext_ctrls { + /* public */ + u32 count; + struct imx_camera_module_ext_ctrl *ctrls; +}; + +/* + * start_streaming: (mandatory) will be called when sensor should be + * put into streaming mode right after the base config has been + * written to the sensor. After a successful call of this function + * the sensor should start delivering frame data. + * + * stop_streaming: (mandatory) will be called when sensor should stop + * delivering data. After a successful call of this function the + * sensor should not deliver any more frame data. + * + * check_camera_id: (optional) will be called when the sensor is + * powered on. If provided should check the sensor ID/version + * required by the custom driver. Register access should be + * possible when this function is invoked. + * + * s_ctrl: (mandatory) will be called at the successful end of + * imx_camera_module_s_ctrl with the ctrl_id as argument. + * + * priv: (optional) for private data used by the custom driver. + */ +struct imx_camera_module_custom_config { + int (*start_streaming)(struct imx_camera_module *cam_mod); + int (*stop_streaming)(struct imx_camera_module *cam_mod); + int (*check_camera_id)(struct imx_camera_module *cam_mod); + int (*s_ctrl)(struct imx_camera_module *cam_mod, u32 ctrl_id); + int (*g_ctrl)(struct imx_camera_module *cam_mod, u32 ctrl_id); + int (*g_timings)(struct imx_camera_module *cam_mod, + struct imx_camera_module_timings *timings); + int (*g_exposure_valid_frame)(struct imx_camera_module *cam_mod); + int (*s_ext_ctrls)(struct imx_camera_module *cam_mod, + struct imx_camera_module_ext_ctrls *ctrls); + int (*set_flip)(struct imx_camera_module *cam_mod); + int (*init_common)(struct imx_camera_module *cam_mod); + struct imx_camera_module_config *configs; + u32 num_configs; + u32 power_up_delays_ms[3]; + void *priv; +}; + +struct imx_camera_module_otp_work { + struct work_struct work; + struct workqueue_struct *wq; + void *cam_mod; +}; + +struct imx_camera_module { + /* public */ + struct v4l2_subdev sd; + struct v4l2_mbus_framefmt frm_fmt; + struct v4l2_subdev_frame_interval frm_intrvl; + struct imx_camera_module_exp_config exp_config; + struct imx_camera_module_wb_config wb_config; + struct imx_camera_module_af_config af_config; + struct imx_camera_module_custom_config custom; + enum imx_camera_module_state state; + enum imx_camera_module_state state_before_suspend; + struct imx_camera_module_config *active_config; + struct imx_camera_module_otp_work otp_work; + u32 ctrl_updt; + u32 vts_cur; + u32 vts_min; + bool auto_adjust_fps; + bool update_config; + bool frm_fmt_valid; + bool frm_intrvl_valid; + bool hflip; + bool vflip; + u32 rotation; + void *pltfm_data; + bool inited; +}; + +#define imx_camera_module_pr_info(cam_mod, fmt, arg...) \ + pltfrm_camera_module_pr_info(&cam_mod->sd, fmt, ## arg) +#define imx_camera_module_pr_debug(cam_mod, fmt, arg...) \ + pltfrm_camera_module_pr_debug(&cam_mod->sd, fmt, ## arg) +#define imx_camera_module_pr_warn(cam_mod, fmt, arg...) \ + pltfrm_camera_module_pr_warn(&cam_mod->sd, fmt, ## arg) +#define imx_camera_module_pr_err(cam_mod, fmt, arg...) \ + pltfrm_camera_module_pr_err(&cam_mod->sd, fmt, ## arg) + +int imx_camera_module_write_reglist( + struct imx_camera_module *cam_mod, + const struct imx_camera_module_reg reglist[], + int len); + +int imx_camera_module_write_reg( + struct imx_camera_module *cam_mod, + u16 reg, + u8 val); + +int imx_camera_module_read_reg( + struct imx_camera_module *cam_mod, + u16 data_length, + u16 reg, + u32 *val); + +int imx_camera_module_read_reg_table( + struct imx_camera_module *cam_mod, + u16 reg, + u32 *val); + +int imx_camera_module_s_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format); + +int imx_camera_module_g_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format); + +int imx_camera_module_s_frame_interval( + struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *interval); + +int imx_camera_module_s_stream( + struct v4l2_subdev *sd, + int enable); + +int imx_camera_module_s_power( + struct v4l2_subdev *sd, + int on); + +int imx_camera_module_g_ctrl( + struct v4l2_subdev *sd, + struct v4l2_control *ctrl); + +int imx_camera_module_s_ctrl( + struct v4l2_subdev *sd, + struct v4l2_control *ctrl); + +int imx_camera_module_s_ext_ctrls( + struct v4l2_subdev *sd, + struct v4l2_ext_controls *ctrls); + +int imx_camera_module_enum_frameintervals( + struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie); + +int imx_camera_module_init( + struct imx_camera_module *cam_mod, + struct imx_camera_module_custom_config *custom); + +void imx_camera_module_release( + struct imx_camera_module *cam_mod); + +long imx_camera_module_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, + void *arg); + +int imx_camera_module_get_flip_mirror( + struct imx_camera_module *cam_mod); +#endif diff --git a/drivers/media/i2c/soc_camera/rockchip/ov2710_v4l2-i2c-subdev.c b/drivers/media/i2c/soc_camera/rockchip/ov2710_v4l2-i2c-subdev.c new file mode 100644 index 000000000000..c749fceb5a62 --- /dev/null +++ b/drivers/media/i2c/soc_camera/rockchip/ov2710_v4l2-i2c-subdev.c @@ -0,0 +1,915 @@ +/* + * drivers/media/i2c/soc_camera/xgold/ov2710.c + * + * ov2710 sensor driver + * + * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd. + * + * Copyright (C) 2012-2014 Intel Mobile Communications GmbH + * + * Copyright (C) 2008 Texas Instruments. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + * Note: + * + * v0.1.0: + * 1. Initialize version; + * 2. Stream on sensor in configuration, + * and stream off sensor after 1frame; + * 3. Stream delay time is define in power_up_delays_ms[2]; + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ov_camera_module.h" + +#define OV2710_DRIVER_NAME "ov2710" + +#define OV2710_FETCH_LSB_GAIN(VAL) (VAL & 0x00FF) /* gain[7:0] */ +#define OV2710_FETCH_MSB_GAIN(VAL) ((VAL >> 8) & 0x1) /* gain[10:8] */ +#define OV2710_AEC_PK_LONG_GAIN_HIGH_REG 0x350a /* Bit 8 */ +#define OV2710_AEC_PK_LONG_GAIN_LOW_REG 0x350b /* Bits 0 -7 */ + +#define OV2710_AEC_PK_LONG_EXPO_3RD_REG 0x3500 /* Exposure Bits 16-19 */ +#define OV2710_AEC_PK_LONG_EXPO_2ND_REG 0x3501 /* Exposure Bits 8-15 */ +#define OV2710_AEC_PK_LONG_EXPO_1ST_REG 0x3502 /* Exposure Bits 0-7 */ + +#define OV2710_AEC_GROUP_UPDATE_ADDRESS 0x3212 +#define OV2710_AEC_GROUP_UPDATE_START_DATA 0x00 +#define OV2710_AEC_GROUP_UPDATE_END_DATA 0x10 +#define OV2710_AEC_GROUP_UPDATE_END_LAUNCH 0xA0 + +#define OV2710_FETCH_3RD_BYTE_EXP(VAL) (((VAL) >> 12) & 0xF) /* 4 Bits */ +#define OV2710_FETCH_2ND_BYTE_EXP(VAL) (((VAL) >> 4) & 0xFF) /* 8 Bits */ +#define OV2710_FETCH_1ST_BYTE_EXP(VAL) (((VAL) & 0x0F) << 4) /* 4 Bits */ + +#define OV2710_PIDH_ADDR 0x300A +#define OV2710_PIDL_ADDR 0x300B + +/* High byte of product ID */ +#define OV2710_PIDH_MAGIC 0x27 +/* Low byte of product ID */ +#define OV2710_PIDL_MAGIC 0x10 + +#define OV2710_EXT_CLK 24000000 +#define OV2710_PLL_PREDIV0_REG 0x3088 +#define OV2710_PLL_PREDIV_REG 0x3080 +#define OV2710_PLL_MUL_HIGH_REG 0x3081 +#define OV2710_PLL_MUL_LOW_REG 0x3082 +#define OV2710_PLL_SPDIV_REG 0x3086 +#define OV2710_PLL_DIVSYS_REG 0x3084 +#define OV2710_TIMING_VTS_HIGH_REG 0x380e +#define OV2710_TIMING_VTS_LOW_REG 0x380f +#define OV2710_TIMING_HTS_HIGH_REG 0x380c +#define OV2710_TIMING_HTS_LOW_REG 0x380d +#define OV2710_FINE_INTG_TIME_MIN 0 +#define OV2710_FINE_INTG_TIME_MAX_MARGIN 0 +#define OV2710_COARSE_INTG_TIME_MIN 1 +#define OV2710_COARSE_INTG_TIME_MAX_MARGIN 4 +#define OV2710_TIMING_X_INC 0x3814 +#define OV2710_TIMING_Y_INC 0x3815 +#define OV2710_HORIZONTAL_START_HIGH_REG 0x3800 +#define OV2710_HORIZONTAL_START_LOW_REG 0x3801 +#define OV2710_VERTICAL_START_HIGH_REG 0x3802 +#define OV2710_VERTICAL_START_LOW_REG 0x3803 +#define OV2710_HORIZONTAL_END_HIGH_REG 0x3804 +#define OV2710_HORIZONTAL_END_LOW_REG 0x3805 +#define OV2710_VERTICAL_END_HIGH_REG 0x3806 +#define OV2710_VERTICAL_END_LOW_REG 0x3807 +#define OV2710_HORIZONTAL_OUTPUT_SIZE_HIGH_REG 0x3808 +#define OV2710_HORIZONTAL_OUTPUT_SIZE_LOW_REG 0x3809 +#define OV2710_VERTICAL_OUTPUT_SIZE_HIGH_REG 0x380a +#define OV2710_VERTICAL_OUTPUT_SIZE_LOW_REG 0x380b +#define OV2710_H_WIN_OFF_HIGH_REG 0x3810 +#define OV2710_H_WIN_OFF_LOW_REG 0x3811 +#define OV2710_V_WIN_OFF_HIGH_REG 0x3812 +#define OV2710_V_WIN_OFF_LOW_REG 0x3813 + +#define OV2710_ANA_ARRAR01 0x3621 +#define OV2710_TIMING_CONCTROL_VH 0x3803 +#define OV2710_TIMING_CONCTROL18 0x3818 + +/* ======================================================================== */ +/* Base sensor configs */ +/* ======================================================================== */ +/* MCLK:24MHz 1920x1080 30fps mipi 1lane 800Mbps/lane */ +static struct ov_camera_module_reg ov2710_init_tab_1920_1080_30fps[] = { +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3008, 0x82}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3008, 0x42}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4201, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4202, 0x0f}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3103, 0x93}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3017, 0x7f}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3018, 0xfc}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3706, 0x61}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3712, 0x0c}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3630, 0x6d}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3800, 0x01}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3801, 0xb4}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3802, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3803, 0x0a}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3818, 0x80}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3804, 0x07}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3805, 0x80}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3806, 0x04}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3807, 0x38}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3808, 0x07}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3809, 0x80}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380a, 0x04}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380b, 0x38}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3810, 0x10}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3811, 0x06}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3812, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3813, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3621, 0x04}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3604, 0x60}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3603, 0xa7}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3631, 0x26}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3600, 0x04}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3620, 0x37}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3623, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3702, 0x9e}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3703, 0x5c}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3704, 0x40}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370d, 0x0f}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3713, 0x9f}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3714, 0x4c}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3710, 0x9e}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3801, 0xc4}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3605, 0x05}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3606, 0x3f}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x302d, 0x90}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370b, 0x40}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3716, 0x31}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3707, 0x52}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380d, 0x74}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5181, 0x20}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x518f, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4301, 0xff}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4303, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3a00, 0x78}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x300f, 0x88}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3011, 0x28}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3a1a, 0x06}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3a18, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3a19, 0x7a}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3a13, 0x54}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382e, 0x0f}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x381a, 0x1a}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x401d, 0x02}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5688, 0x03}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5684, 0x07}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5685, 0xa0}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5686, 0x04}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5687, 0x43}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3011, 0x0a}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x300f, 0x8a}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3017, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3018, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x300e, 0x04}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4801, 0x0f}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x300f, 0xc3}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3a0f, 0x40}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3a10, 0x38}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3a1b, 0x48}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3a1e, 0x30}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3a11, 0x90}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3a1f, 0x10}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380c, 0x09},/* HTS H */ +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380d, 0x74},/* HTS L */ +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380e, 0x04},/* VTS H */ +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380f, 0x50},/* VTS L */ +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3500, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3501, 0x28}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3502, 0x90}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3503, 0x07}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x350a, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x350b, 0x1f}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5000, 0x5f}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5001, 0x4e}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3406, 0x01}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3400, 0x04}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3401, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3402, 0x04}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3403, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3404, 0x04}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3405, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4800, 0x24}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4201, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4202, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3008, 0x02}, +{OV_CAMERA_MODULE_REG_TYPE_TIMEOUT, 0x0000, 40}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3008, 0x42}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4201, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4202, 0x0f} +}; + +/* ======================================================================== */ +static struct ov_camera_module_config ov2710_configs[] = { + { + .name = "1920x1080_30fps", + .frm_fmt = { + .width = 1920, + .height = 1080, + .code = MEDIA_BUS_FMT_SBGGR10_1X10 + }, + .frm_intrvl = { + .interval = { + .numerator = 1, + .denominator = 30 + } + }, + .auto_exp_enabled = false, + .auto_gain_enabled = false, + .auto_wb_enabled = false, + .reg_table = (void *)ov2710_init_tab_1920_1080_30fps, + .reg_table_num_entries = + sizeof(ov2710_init_tab_1920_1080_30fps) / + sizeof(ov2710_init_tab_1920_1080_30fps[0]), + .v_blanking_time_us = 3078, + .ignore_measurement_check = 1, + PLTFRM_CAM_ITF_MIPI_CFG(0, 1, 800, 24000000) + } +}; + +/*--------------------------------------------------------------------------*/ +static int ov2710_set_flip( + struct ov_camera_module *cam_mod, + struct pltfrm_camera_module_reg reglist[], + int len) +{ + int i, mode = 0; + u16 match_reg[3]; + + mode = ov_camera_module_get_flip_mirror(cam_mod); + + if (mode == -1) { + ov_camera_module_pr_debug( + cam_mod, + "dts don't set flip, return!\n"); + return 0; + } + + if (mode == OV_FLIP_BIT_MASK) { + match_reg[0] = 0x04; + match_reg[1] = 0x09; + match_reg[2] = 0xa0; + } else if (mode == OV_MIRROR_BIT_MASK) { + match_reg[0] = 0x14; + match_reg[1] = 0x0a; + match_reg[2] = 0xc0; + } else if (mode == (OV_MIRROR_BIT_MASK | + OV_FLIP_BIT_MASK)) { + match_reg[0] = 0x14; + match_reg[1] = 0x09; + match_reg[2] = 0xe0; + } else { + match_reg[0] = 0x04; + match_reg[1] = 0x0a; + match_reg[2] = 0x80; + } + + for (i = len; i > 0; i--) { + if (reglist[i].reg == OV2710_ANA_ARRAR01) + reglist[i].val = match_reg[0]; + else if (reglist[i].reg == OV2710_TIMING_CONCTROL_VH) + reglist[i].val = match_reg[1]; + else if (reglist[i].reg == OV2710_TIMING_CONCTROL18) + reglist[i].val = match_reg[2]; + } + + return 0; +} + +/*--------------------------------------------------------------------------*/ +static int OV2710_g_VTS(struct ov_camera_module *cam_mod, u32 *vts) +{ + u32 msb, lsb; + int ret; + + ret = ov_camera_module_read_reg_table( + cam_mod, + OV2710_TIMING_VTS_HIGH_REG, + &msb); + if (IS_ERR_VALUE(ret)) + goto err; + + ret = ov_camera_module_read_reg_table( + cam_mod, + OV2710_TIMING_VTS_LOW_REG, + &lsb); + if (IS_ERR_VALUE(ret)) + goto err; + + *vts = (msb << 8) | lsb; + + return 0; +err: + ov_camera_module_pr_err(cam_mod, + "failed with error (%d)\n", ret); + return ret; +} + +static int OV2710_auto_adjust_fps(struct ov_camera_module *cam_mod, + u32 exp_time) +{ + int ret; + u32 vts; + + if ((cam_mod->exp_config.exp_time + OV2710_COARSE_INTG_TIME_MAX_MARGIN) + > cam_mod->vts_min) + vts = cam_mod->exp_config.exp_time + + OV2710_COARSE_INTG_TIME_MAX_MARGIN; + else + vts = cam_mod->vts_min; + + /* + * if (cam_mod->fps_ctrl > 0 && cam_mod->fps_ctrl < 100) + * vts = vts * 100 / cam_mod->fps_ctrl; + */ + + if (vts > 0xfff) + vts = 0xfff; + else + vts = vts;/* VTS value is 0x380e[3:0]/380f[7:0] */ + + ret = ov_camera_module_write_reg(cam_mod, + OV2710_TIMING_VTS_LOW_REG, + vts & 0xFF); + ret |= ov_camera_module_write_reg(cam_mod, + OV2710_TIMING_VTS_HIGH_REG, + (vts >> 8) & 0x0F); + + if (IS_ERR_VALUE(ret)) + ov_camera_module_pr_err(cam_mod, + "failed with error (%d)\n", ret); + else + ov_camera_module_pr_debug(cam_mod, + "updated vts = 0x%x,vts_min=0x%x\n", + vts, cam_mod->vts_min); + + return ret; +} + +static int ov2710_write_aec(struct ov_camera_module *cam_mod) +{ + int ret = 0; + + ov_camera_module_pr_debug(cam_mod, + "exp_time = %d lines, gain = %d, flash_mode = %d\n", + cam_mod->exp_config.exp_time, + cam_mod->exp_config.gain, + cam_mod->exp_config.flash_mode); + + /* + * if the sensor is already streaming, write to shadow registers, + * if the sensor is in SW standby, write to active registers, + * if the sensor is off/registers are not writeable, do nothing + */ + if ((cam_mod->state == OV_CAMERA_MODULE_SW_STANDBY) || + (cam_mod->state == OV_CAMERA_MODULE_STREAMING)) { + u32 a_gain = cam_mod->exp_config.gain; + u32 exp_time = cam_mod->exp_config.exp_time; + + a_gain = a_gain * cam_mod->exp_config.gain_percent / 100; + + if (cam_mod->state == OV_CAMERA_MODULE_STREAMING) + ret = ov_camera_module_write_reg(cam_mod, + OV2710_AEC_GROUP_UPDATE_ADDRESS, + OV2710_AEC_GROUP_UPDATE_START_DATA); + if (!IS_ERR_VALUE(ret) && cam_mod->auto_adjust_fps) + ret = OV2710_auto_adjust_fps(cam_mod, + cam_mod->exp_config.exp_time); + ret |= ov_camera_module_write_reg(cam_mod, + OV2710_AEC_PK_LONG_GAIN_HIGH_REG, + OV2710_FETCH_MSB_GAIN(a_gain)); + ret |= ov_camera_module_write_reg(cam_mod, + OV2710_AEC_PK_LONG_GAIN_LOW_REG, + OV2710_FETCH_LSB_GAIN(a_gain)); + ret = ov_camera_module_write_reg(cam_mod, + OV2710_AEC_PK_LONG_EXPO_3RD_REG, + OV2710_FETCH_3RD_BYTE_EXP(exp_time)); + ret |= ov_camera_module_write_reg(cam_mod, + OV2710_AEC_PK_LONG_EXPO_2ND_REG, + OV2710_FETCH_2ND_BYTE_EXP(exp_time)); + ret |= ov_camera_module_write_reg(cam_mod, + OV2710_AEC_PK_LONG_EXPO_1ST_REG, + OV2710_FETCH_1ST_BYTE_EXP(exp_time)); + if (cam_mod->state == OV_CAMERA_MODULE_STREAMING) { + ret = ov_camera_module_write_reg(cam_mod, + OV2710_AEC_GROUP_UPDATE_ADDRESS, + OV2710_AEC_GROUP_UPDATE_END_DATA); + ret = ov_camera_module_write_reg(cam_mod, + OV2710_AEC_GROUP_UPDATE_ADDRESS, + OV2710_AEC_GROUP_UPDATE_END_LAUNCH); + } + } + + if (IS_ERR_VALUE(ret)) + ov_camera_module_pr_err(cam_mod, + "failed with error (%d)\n", ret); + return ret; +} + +static int ov2710_g_ctrl(struct ov_camera_module *cam_mod, u32 ctrl_id) +{ + int ret = 0; + + ov_camera_module_pr_debug(cam_mod, "\n"); + + switch (ctrl_id) { + case V4L2_CID_GAIN: + case V4L2_CID_EXPOSURE: + case V4L2_CID_FLASH_LED_MODE: + /* nothing to be done here */ + break; + default: + ret = -EINVAL; + break; + } + + if (IS_ERR_VALUE(ret)) + ov_camera_module_pr_debug(cam_mod, + "failed with error (%d)\n", ret); + return ret; +} + +/*--------------------------------------------------------------------------*/ + +static int ov2710_filltimings(struct ov_camera_module_custom_config *custom) +{ + int i, j; + u32 win_h_off = 0, win_v_off = 0; + struct ov_camera_module_config *config; + struct ov_camera_module_timings *timings; + struct ov_camera_module_reg *reg_table; + int reg_table_num_entries; + + for (i = 0; i < custom->num_configs; i++) { + config = &custom->configs[i]; + reg_table = config->reg_table; + reg_table_num_entries = config->reg_table_num_entries; + timings = &config->timings; + + memset(timings, 0x00, sizeof(*timings)); + for (j = 0; j < reg_table_num_entries; j++) { + switch (reg_table[j].reg) { + case OV2710_TIMING_VTS_HIGH_REG: + timings->frame_length_lines = + ((reg_table[j].val << 8) | + (timings->frame_length_lines & 0xff)); + break; + case OV2710_TIMING_VTS_LOW_REG: + timings->frame_length_lines = + (reg_table[j].val | + (timings->frame_length_lines & 0xff00)); + break; + case OV2710_TIMING_HTS_HIGH_REG: + timings->line_length_pck = + ((reg_table[j].val << 8) | + timings->line_length_pck); + break; + case OV2710_TIMING_HTS_LOW_REG: + timings->line_length_pck = + (reg_table[j].val | + (timings->line_length_pck & 0xff00)); + break; + case OV2710_TIMING_X_INC: + timings->binning_factor_x = + ((reg_table[j].val >> 4) + 1) / 2; + if (timings->binning_factor_x == 0) + timings->binning_factor_x = 1; + break; + case OV2710_TIMING_Y_INC: + timings->binning_factor_y = + ((reg_table[j].val >> 4) + 1) / 2; + if (timings->binning_factor_y == 0) + timings->binning_factor_y = 1; + break; + case OV2710_HORIZONTAL_START_HIGH_REG: + timings->crop_horizontal_start = + ((reg_table[j].val << 8) | + (timings->crop_horizontal_start & + 0xff)); + break; + case OV2710_HORIZONTAL_START_LOW_REG: + timings->crop_horizontal_start = + (reg_table[j].val | + (timings->crop_horizontal_start & + 0xff00)); + break; + case OV2710_VERTICAL_START_HIGH_REG: + timings->crop_vertical_start = + ((reg_table[j].val << 8) | + (timings->crop_vertical_start & 0xff)); + break; + case OV2710_VERTICAL_START_LOW_REG: + timings->crop_vertical_start = + ((reg_table[j].val) | + (timings->crop_vertical_start & + 0xff00)); + break; + case OV2710_HORIZONTAL_END_HIGH_REG: + timings->crop_horizontal_end = + ((reg_table[j].val << 8) | + (timings->crop_horizontal_end & 0xff)); + break; + case OV2710_HORIZONTAL_END_LOW_REG: + timings->crop_horizontal_end = + (reg_table[j].val | + (timings->crop_horizontal_end & + 0xff00)); + break; + case OV2710_VERTICAL_END_HIGH_REG: + timings->crop_vertical_end = + ((reg_table[j].val << 8) | + (timings->crop_vertical_end & 0xff)); + break; + case OV2710_VERTICAL_END_LOW_REG: + timings->crop_vertical_end = + (reg_table[j].val | + (timings->crop_vertical_end & 0xff00)); + break; + case OV2710_H_WIN_OFF_HIGH_REG: + win_h_off = (reg_table[j].val & 0xf) << 8; + break; + case OV2710_H_WIN_OFF_LOW_REG: + win_h_off |= (reg_table[j].val & 0xff); + break; + case OV2710_V_WIN_OFF_HIGH_REG: + win_v_off = (reg_table[j].val & 0xf) << 8; + break; + case OV2710_V_WIN_OFF_LOW_REG: + win_v_off |= (reg_table[j].val & 0xff); + break; + case OV2710_HORIZONTAL_OUTPUT_SIZE_HIGH_REG: + timings->sensor_output_width = + ((reg_table[j].val << 8) | + (timings->sensor_output_width & 0xff)); + break; + case OV2710_HORIZONTAL_OUTPUT_SIZE_LOW_REG: + timings->sensor_output_width = + (reg_table[j].val | + (timings->sensor_output_width & + 0xff00)); + break; + case OV2710_VERTICAL_OUTPUT_SIZE_HIGH_REG: + timings->sensor_output_height = + ((reg_table[j].val << 8) | + (timings->sensor_output_height & 0xff)); + break; + case OV2710_VERTICAL_OUTPUT_SIZE_LOW_REG: + timings->sensor_output_height = + (reg_table[j].val | + (timings->sensor_output_height & + 0xff00)); + break; + } + } + + timings->crop_horizontal_start += win_h_off; + timings->crop_horizontal_end -= win_h_off; + timings->crop_vertical_start += win_v_off; + timings->crop_vertical_end -= win_v_off; + + timings->exp_time >>= 4; + timings->vt_pix_clk_freq_hz = + config->frm_intrvl.interval.denominator * + timings->frame_length_lines * + timings->line_length_pck; + + timings->coarse_integration_time_min = + OV2710_COARSE_INTG_TIME_MIN; + timings->coarse_integration_time_max_margin = + OV2710_COARSE_INTG_TIME_MAX_MARGIN; + + /* OV Sensor do not use fine integration time. */ + timings->fine_integration_time_min = + OV2710_FINE_INTG_TIME_MIN; + timings->fine_integration_time_max_margin = + OV2710_FINE_INTG_TIME_MAX_MARGIN; + } + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +static int ov2710_g_timings(struct ov_camera_module *cam_mod, + struct ov_camera_module_timings *timings) +{ + int ret = 0; + unsigned int vts; + + if (IS_ERR_OR_NULL(cam_mod->active_config)) + goto err; + + *timings = cam_mod->active_config->timings; + + vts = (!cam_mod->vts_cur) ? + timings->frame_length_lines : + cam_mod->vts_cur; + if (cam_mod->frm_intrvl_valid) + timings->vt_pix_clk_freq_hz = + cam_mod->frm_intrvl.interval.denominator + * vts + * timings->line_length_pck; + else + timings->vt_pix_clk_freq_hz = + cam_mod->active_config->frm_intrvl.interval.denominator * + vts * timings->line_length_pck; + + return ret; +err: + ov_camera_module_pr_err(cam_mod, + "failed with error (%d)\n", ret); + return ret; +} + +/*--------------------------------------------------------------------------*/ + +static int ov2710_s_ctrl(struct ov_camera_module *cam_mod, u32 ctrl_id) +{ + int ret = 0; + + ov_camera_module_pr_debug(cam_mod, "\n"); + + switch (ctrl_id) { + case V4L2_CID_GAIN: + case V4L2_CID_EXPOSURE: + ret = ov2710_write_aec(cam_mod); + break; + case V4L2_CID_FLASH_LED_MODE: + /* nothing to be done here */ + break; + case V4L2_CID_FOCUS_ABSOLUTE: + /* todo*/ + break; + /* + * case RK_V4L2_CID_FPS_CTRL: + * if (cam_mod->auto_adjust_fps) + * ret = OV2710_auto_adjust_fps( + * cam_mod, + * cam_mod->exp_config.exp_time); + * break; + */ + default: + ret = -EINVAL; + break; + } + + if (IS_ERR_VALUE(ret)) + ov_camera_module_pr_err(cam_mod, + "failed with error (%d)\n", ret); + return ret; +} + +/*--------------------------------------------------------------------------*/ + +static int ov2710_s_ext_ctrls(struct ov_camera_module *cam_mod, + struct ov_camera_module_ext_ctrls *ctrls) +{ + int ret = 0; + + /* Handles only exposure and gain together special case. */ + if (ctrls->count == 1) + ret = ov2710_s_ctrl(cam_mod, ctrls->ctrls[0].id); + else if ((ctrls->count == 3) && + (ctrls->ctrls[0].id == V4L2_CID_GAIN || + ctrls->ctrls[0].id == V4L2_CID_EXPOSURE || + ctrls->ctrls[1].id == V4L2_CID_GAIN || + ctrls->ctrls[1].id == V4L2_CID_EXPOSURE)) + ret = ov2710_write_aec(cam_mod); + else + ret = -EINVAL; + + if (IS_ERR_VALUE(ret)) + ov_camera_module_pr_debug(cam_mod, + "failed with error (%d)\n", ret); + + return ret; +} + +/*--------------------------------------------------------------------------*/ + +static int ov2710_start_streaming(struct ov_camera_module *cam_mod) +{ + int ret = 0; + + ov_camera_module_pr_debug(cam_mod, + "active config=%s\n", cam_mod->active_config->name); + + ret = OV2710_g_VTS(cam_mod, &cam_mod->vts_min); + if (IS_ERR_VALUE(ret)) + goto err; + ov_camera_module_pr_debug(cam_mod, "=====streaming on ===\n"); + ret = ov_camera_module_write_reg(cam_mod, 0x3008, 0x02); + ret |= ov_camera_module_write_reg(cam_mod, 0x4201, 0x00); + ret |= ov_camera_module_write_reg(cam_mod, 0x4202, 0x00); + + if (IS_ERR_VALUE(ret)) + goto err; + + return 0; +err: + ov_camera_module_pr_err(cam_mod, "failed with error (%d)\n", + ret); + return ret; +} + +/*--------------------------------------------------------------------------*/ + +static int ov2710_stop_streaming(struct ov_camera_module *cam_mod) +{ + int ret = 0; + + ov_camera_module_pr_debug(cam_mod, "\n"); + ret = ov_camera_module_write_reg(cam_mod, 0x3008, 0x42); + ret |= ov_camera_module_write_reg(cam_mod, 0x4201, 0x00); + ret |= ov_camera_module_write_reg(cam_mod, 0x4202, 0x0f); + + if (IS_ERR_VALUE(ret)) + goto err; + + return 0; +err: + ov_camera_module_pr_err(cam_mod, "failed with error (%d)\n", ret); + return ret; +} + +/*--------------------------------------------------------------------------*/ + +static int ov2710_check_camera_id(struct ov_camera_module *cam_mod) +{ + u32 pidh, pidl; + int ret = 0; + + ov_camera_module_pr_debug(cam_mod, "\n"); + + ret |= ov_camera_module_read_reg(cam_mod, 1, OV2710_PIDH_ADDR, &pidh); + ret |= ov_camera_module_read_reg(cam_mod, 1, OV2710_PIDL_ADDR, &pidl); + if (IS_ERR_VALUE(ret)) { + ov_camera_module_pr_err(cam_mod, + "register read failed, camera module powered off?\n"); + goto err; + } + + if ((pidh == OV2710_PIDH_MAGIC) && (pidl == OV2710_PIDL_MAGIC)) + ov_camera_module_pr_debug(cam_mod, + "successfully detected camera ID 0x%02x%02x\n", + pidh, pidl); + else { + ov_camera_module_pr_err(cam_mod, + "wrong camera ID, expected 0x%02x%02x, detected 0x%02x%02x\n", + OV2710_PIDH_MAGIC, OV2710_PIDL_MAGIC, pidh, pidl); + ret = -EINVAL; + goto err; + } + + return 0; +err: + ov_camera_module_pr_err(cam_mod, "failed with error (%d)\n", ret); + return ret; +} + +/* ======================================================================== */ +int ov_camera_2710_module_s_ctrl( + struct v4l2_subdev *sd, + struct v4l2_control *ctrl) +{ + return 0; +} + +/* ======================================================================== */ + +int ov_camera_2710_module_s_ext_ctrls( + struct v4l2_subdev *sd, + struct v4l2_ext_controls *ctrls) +{ + return 0; +} + +long ov_camera_2710_module_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, + void *arg) +{ + return 0; +} + +/* ======================================================================== */ +/* This part is platform dependent */ +/* ======================================================================== */ + +static struct v4l2_subdev_core_ops ov2710_camera_module_core_ops = { + .g_ctrl = ov_camera_module_g_ctrl, + .s_ctrl = ov_camera_module_s_ctrl, + .s_ext_ctrls = ov_camera_module_s_ext_ctrls, + .s_power = ov_camera_module_s_power, + .ioctl = ov_camera_module_ioctl +}; + +static struct v4l2_subdev_video_ops ov2710_camera_module_video_ops = { + .s_frame_interval = ov_camera_module_s_frame_interval, + .s_stream = ov_camera_module_s_stream +}; + +static struct v4l2_subdev_pad_ops ov2710_camera_module_pad_ops = { + .enum_frame_interval = ov_camera_module_enum_frameintervals, + .get_fmt = ov_camera_module_g_fmt, + .set_fmt = ov_camera_module_s_fmt, +}; + +static struct v4l2_subdev_ops ov2710_camera_module_ops = { + .core = &ov2710_camera_module_core_ops, + .video = &ov2710_camera_module_video_ops, + .pad = &ov2710_camera_module_pad_ops +}; + +static struct ov_camera_module ov2710; + +static struct ov_camera_module_custom_config ov2710_custom_config = { + .start_streaming = ov2710_start_streaming, + .stop_streaming = ov2710_stop_streaming, + .s_ctrl = ov2710_s_ctrl, + .g_ctrl = ov2710_g_ctrl, + .s_ext_ctrls = ov2710_s_ext_ctrls, + .g_timings = ov2710_g_timings, + .set_flip = ov2710_set_flip, + .check_camera_id = ov2710_check_camera_id, + .configs = ov2710_configs, + .num_configs = ARRAY_SIZE(ov2710_configs), + .power_up_delays_ms = {5, 30, 30} +}; + +static int ov2710_probe( + struct i2c_client *client, + const struct i2c_device_id *id) +{ + dev_info(&client->dev, "probing...\n"); + + ov2710_filltimings(&ov2710_custom_config); + v4l2_i2c_subdev_init(&ov2710.sd, client, + &ov2710_camera_module_ops); + + ov2710.custom = ov2710_custom_config; + + dev_info(&client->dev, "probing successful\n"); + return 0; +} + +/* ======================================================================== */ + +static int ov2710_remove( + struct i2c_client *client) +{ + struct ov_camera_module *cam_mod = i2c_get_clientdata(client); + + dev_info(&client->dev, "removing device...\n"); + + if (!client->adapter) + return -ENODEV; /* our client isn't attached */ + + ov_camera_module_release(cam_mod); + + dev_info(&client->dev, "removed\n"); + return 0; +} + +static const struct i2c_device_id ov2710_id[] = { + { OV2710_DRIVER_NAME, 0 }, + { } +}; + +static const struct of_device_id ov2710_of_match[] = { + {.compatible = "omnivision,ov2710-v4l2-i2c-subdev"}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, ov2710_id); + +static struct i2c_driver ov2710_i2c_driver = { + .driver = { + .name = OV2710_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = ov2710_of_match + }, + .probe = ov2710_probe, + .remove = ov2710_remove, + .id_table = ov2710_id, +}; + +module_i2c_driver(ov2710_i2c_driver); + +MODULE_DESCRIPTION("SoC Camera driver for ov2710"); +MODULE_AUTHOR("Eike Grimpe"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/media/i2c/soc_camera/rockchip/ov4689_v4l2-i2c-subdev.c b/drivers/media/i2c/soc_camera/rockchip/ov4689_v4l2-i2c-subdev.c new file mode 100644 index 000000000000..ad22b0413837 --- /dev/null +++ b/drivers/media/i2c/soc_camera/rockchip/ov4689_v4l2-i2c-subdev.c @@ -0,0 +1,949 @@ +/* + * ov4689 sensor driver + * + * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd. + * + * Copyright (C) 2012-2014 Intel Mobile Communications GmbH + * + * Copyright (C) 2008 Texas Instruments. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ov_camera_module.h" + +#define ov4689_DRIVER_NAME "ov4689" + +#define ov4689_AEC_PK_LONG_GAIN_HIGH_REG 0x3508 /* Bit 6-13 */ +#define ov4689_AEC_PK_LONG_GAIN_LOW_REG 0x3509 /* Bits 0 -5 */ +#define ov4689_FETCH_LSB_GAIN(VAL) ((VAL) & 0x00ff) +#define ov4689_FETCH_MSB_GAIN(VAL) (((VAL) >> 8) & 0xff) + +#define ov4689_AEC_PK_LONG_EXPO_3RD_REG 0x3500 /* Exposure Bits 16-19 */ +#define ov4689_AEC_PK_LONG_EXPO_2ND_REG 0x3501 /* Exposure Bits 8-15 */ +#define ov4689_AEC_PK_LONG_EXPO_1ST_REG 0x3502 /* Exposure Bits 0-7 */ +#define ov4689_FETCH_3RD_BYTE_EXP(VAL) (((VAL) >> 12) & 0xF) /* 4 Bits */ +#define ov4689_FETCH_2ND_BYTE_EXP(VAL) (((VAL) >> 4) & 0xFF) /* 8 Bits */ +#define ov4689_FETCH_1ST_BYTE_EXP(VAL) (((VAL) & 0x0F) << 4) /* 4 Bits */ + +#define ov4689_AEC_GROUP_UPDATE_ADDRESS 0x3208 +#define ov4689_AEC_GROUP_UPDATE_START_DATA 0x00 +#define ov4689_AEC_GROUP_UPDATE_END_DATA 0x10 +#define ov4689_AEC_GROUP_UPDATE_END_LAUNCH 0xA0 + +#define ov4689_PIDH_ADDR 0x300A +#define ov4689_PIDL_ADDR 0x300B + +#define ov4689_PIDH_MAGIC 0x46 +#define ov4689_PIDL_MAGIC 0x88 + +#define ov4689_TIMING_VTS_HIGH_REG 0x380e +#define ov4689_TIMING_VTS_LOW_REG 0x380f +#define ov4689_TIMING_HTS_HIGH_REG 0x380c +#define ov4689_TIMING_HTS_LOW_REG 0x380d +#define ov4689_INTEGRATION_TIME_MARGIN 8 +#define ov4689_FINE_INTG_TIME_MIN 0 +#define ov4689_FINE_INTG_TIME_MAX_MARGIN 0 +#define ov4689_COARSE_INTG_TIME_MIN 16 +#define ov4689_COARSE_INTG_TIME_MAX_MARGIN 4 +#define ov4689_TIMING_X_INC 0x3814 +#define ov4689_TIMING_Y_INC 0x3815 +#define ov4689_HORIZONTAL_START_HIGH_REG 0x3800 +#define ov4689_HORIZONTAL_START_LOW_REG 0x3801 +#define ov4689_VERTICAL_START_HIGH_REG 0x3802 +#define ov4689_VERTICAL_START_LOW_REG 0x3803 +#define ov4689_HORIZONTAL_END_HIGH_REG 0x3804 +#define ov4689_HORIZONTAL_END_LOW_REG 0x3805 +#define ov4689_VERTICAL_END_HIGH_REG 0x3806 +#define ov4689_VERTICAL_END_LOW_REG 0x3807 +#define ov4689_HORIZONTAL_OUTPUT_SIZE_HIGH_REG 0x3808 +#define ov4689_HORIZONTAL_OUTPUT_SIZE_LOW_REG 0x3809 +#define ov4689_VERTICAL_OUTPUT_SIZE_HIGH_REG 0x380a +#define ov4689_VERTICAL_OUTPUT_SIZE_LOW_REG 0x380b +#define ov4689_FLIP_REG 0x3820 +#define ov4689_MIRROR_REG 0x3821 + +#define ov4689_EXT_CLK 24000000 + +#define ov4689_FULL_SIZE_RESOLUTION_WIDTH 2688 +#define ov4689_BINING_SIZE_RESOLUTION_WIDTH 1280 + +static struct ov_camera_module ov4689; +static struct ov_camera_module_custom_config ov4689_custom_config; + +/* ======================================================================== */ +/* Base sensor configs */ +/* ======================================================================== */ + +/* MCLK:24MHz 2688x1520 30fps mipi 4lane 1008Mbps/lane */ +static struct ov_camera_module_reg ov4689_init_tab_2688_1520_30fps[] = { +/* global setting */ +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0103, 0x01}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3638, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0300, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0302, 0x2a}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0303, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0304, 0x03}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030b, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030d, 0x1e}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030e, 0x04}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030f, 0x01}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0312, 0x01}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x031e, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3000, 0x20}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3002, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3018, 0x32}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3020, 0x93}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3021, 0x03}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3022, 0x01}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3031, 0x0a}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x303f, 0x0c}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3305, 0xf1}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3307, 0x04}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3309, 0x29}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3500, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3501, 0x60}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3502, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3503, 0x04}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3504, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3505, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3506, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3507, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3508, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3509, 0x80}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x350a, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x350b, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x350c, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x350d, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x350e, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x350f, 0x80}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3510, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3511, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3512, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3513, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3514, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3515, 0x80}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3516, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3517, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3518, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3519, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x351a, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x351b, 0x80}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x351c, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x351d, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x351e, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x351f, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3520, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3521, 0x80}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3522, 0x08}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3524, 0x08}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3526, 0x08}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3528, 0x08}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x352a, 0x08}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3602, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3603, 0x40}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3604, 0x02}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3605, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3606, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3607, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3609, 0x12}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360a, 0x40}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360c, 0x08}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360f, 0xe5}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3608, 0x8f}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3611, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3613, 0xf7}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3616, 0x58}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3619, 0x99}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361b, 0x60}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361c, 0x7a}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361e, 0x79}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361f, 0x02}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3632, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3633, 0x10}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3634, 0x10}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3635, 0x10}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3636, 0x15}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3646, 0x86}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x364a, 0x0b}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3700, 0x17}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3701, 0x22}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3703, 0x10}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370a, 0x37}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3705, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3706, 0x63}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3709, 0x3c}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370b, 0x01}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370c, 0x30}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3710, 0x24}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3711, 0x0c}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3716, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3720, 0x28}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3729, 0x7b}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372a, 0x84}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372b, 0xbd}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372c, 0xbc}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372e, 0x52}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x373c, 0x0e}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x373e, 0x33}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3743, 0x10}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3744, 0x88}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3745, 0xc0}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x374a, 0x43}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x374c, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x374e, 0x23}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3751, 0x7b}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3752, 0x84}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3753, 0xbd}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3754, 0xbc}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3756, 0x52}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375c, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3760, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3761, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3762, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3763, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3764, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3767, 0x04}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3768, 0x04}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3769, 0x08}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x376a, 0x08}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x376b, 0x20}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x376c, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x376d, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x376e, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3773, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3774, 0x51}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3776, 0xbd}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3777, 0xbd } +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3781, 0x18}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3783, 0x25}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3798, 0x1b}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3800, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3801, 0x08}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3802, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3803, 0x04}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3804, 0x0a}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3805, 0x97}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3806, 0x05}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3807, 0xfb}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3808, 0x0a}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3809, 0x80}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380a, 0x05}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380b, 0xf0}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380c, 0x0a}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380d, 0x18}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380e, 0x06}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380f, 0x12}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3810, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3811, 0x08}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3812, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3813, 0x04}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3814, 0x01}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3815, 0x01}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3819, 0x01}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3820, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3821, 0x06}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3829, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382a, 0x01}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382b, 0x01}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382d, 0x7f}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3830, 0x04}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3836, 0x01}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3837, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3841, 0x02}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3846, 0x08}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3847, 0x07}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3d85, 0x36}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3d8c, 0x71}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3d8d, 0xcb}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3f0a, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4000, 0xf1}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4001, 0x40}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4002, 0x04}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4003, 0x14}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x400e, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4011, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x401a, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x401b, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x401c, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x401d, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x401f, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4020, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4021, 0x10}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4022, 0x07}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4023, 0xcf}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4024, 0x09}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4025, 0x60}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4026, 0x09}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4027, 0x6f}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4028, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4029, 0x02}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402a, 0x06}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402b, 0x04}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402c, 0x02}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402d, 0x02}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402e, 0x0e}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402f, 0x04}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4302, 0xff}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4303, 0xff}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4304, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4305, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4306, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4308, 0x02}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4500, 0x6c}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4501, 0xc4}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4502, 0x40}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4503, 0x01}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4601, 0xa7}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4800, 0x04}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4813, 0x08}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x481f, 0x40}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4829, 0x78}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4837, 0x10}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4b00, 0x2a}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4b0d, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d00, 0x04}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d01, 0x42}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d02, 0xd1}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d03, 0x93}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d04, 0xf5}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d05, 0xc1}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5000, 0xf3}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5001, 0x11}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5004, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x500a, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x500b, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5032, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5040, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5050, 0x0c}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5500, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5501, 0x10}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5502, 0x01}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5503, 0x0f}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x8000, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x8001, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x8002, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x8003, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x8004, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x8005, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x8006, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x8007, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x8008, 0x00}, +{OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3638, 0x00}, +/* {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0100, 0x01} */ +}; + +static struct ov_camera_module_config ov4689_configs[] = { + { + .name = "2688x1520_30fps", + .frm_fmt = { + .width = 2688, + .height = 1520, + .code = MEDIA_BUS_FMT_SBGGR10_1X10 + }, + .frm_intrvl = { + .interval = { + .numerator = 1, + .denominator = 30 + } + }, + .auto_exp_enabled = false, + .auto_gain_enabled = false, + .auto_wb_enabled = false, + .reg_table = (void *)ov4689_init_tab_2688_1520_30fps, + .reg_table_num_entries = + sizeof(ov4689_init_tab_2688_1520_30fps) / + sizeof(ov4689_init_tab_2688_1520_30fps[0]), + .v_blanking_time_us = 5000, + PLTFRM_CAM_ITF_MIPI_CFG(0, 2, 999, ov4689_EXT_CLK) + } +}; + +static int ov4689_g_VTS(struct ov_camera_module *cam_mod, u32 *vts) +{ + u32 msb, lsb; + int ret; + + ret = ov_camera_module_read_reg_table( + cam_mod, + ov4689_TIMING_VTS_HIGH_REG, + &msb); + if (IS_ERR_VALUE(ret)) + goto err; + + ret = ov_camera_module_read_reg_table( + cam_mod, + ov4689_TIMING_VTS_LOW_REG, + &lsb); + if (IS_ERR_VALUE(ret)) + goto err; + + *vts = (msb << 8) | lsb; + + return 0; +err: + ov_camera_module_pr_err(cam_mod, "failed with error (%d)\n", ret); + return ret; +} + +static int ov4689_auto_adjust_fps(struct ov_camera_module *cam_mod, + u32 exp_time) +{ + int ret; + u32 vts; + + if ((cam_mod->exp_config.exp_time + + ov4689_COARSE_INTG_TIME_MAX_MARGIN) + > cam_mod->vts_min) + vts = cam_mod->exp_config.exp_time + + ov4689_COARSE_INTG_TIME_MAX_MARGIN; + else + vts = cam_mod->vts_min; + ret = ov_camera_module_write_reg(cam_mod, + ov4689_TIMING_VTS_LOW_REG, vts & 0xFF); + ret |= ov_camera_module_write_reg(cam_mod, + ov4689_TIMING_VTS_HIGH_REG, + (vts >> 8) & 0xFF); + + if (IS_ERR_VALUE(ret)) + ov_camera_module_pr_err(cam_mod, + "failed with error (%d)\n", ret); + else + ov_camera_module_pr_debug(cam_mod, + "updated vts = %d,vts_min=%d\n", + vts, cam_mod->vts_min); + + return ret; +} + +static int ov4689_write_aec(struct ov_camera_module *cam_mod) +{ + int ret = 0; + + ov_camera_module_pr_debug(cam_mod, + "exp_time = %d, gain = %d, flash_mode = %d\n", + cam_mod->exp_config.exp_time, + cam_mod->exp_config.gain, + cam_mod->exp_config.flash_mode); + + /* + * if the sensor is already streaming, write to shadow registers, + * if the sensor is in SW standby, write to active registers, + * if the sensor is off/registers are not writeable, do nothing + */ + if ((cam_mod->state == OV_CAMERA_MODULE_SW_STANDBY) || + (cam_mod->state == OV_CAMERA_MODULE_STREAMING)) { + u32 a_gain = cam_mod->exp_config.gain; + u32 exp_time = cam_mod->exp_config.exp_time; + + a_gain = a_gain * cam_mod->exp_config.gain_percent / 100; + + if (cam_mod->state == OV_CAMERA_MODULE_STREAMING) + ret = ov_camera_module_write_reg(cam_mod, + ov4689_AEC_GROUP_UPDATE_ADDRESS, + ov4689_AEC_GROUP_UPDATE_START_DATA); + if (!IS_ERR_VALUE(ret) && cam_mod->auto_adjust_fps) + ret = ov4689_auto_adjust_fps(cam_mod, + cam_mod->exp_config.exp_time); + ret |= ov_camera_module_write_reg(cam_mod, + ov4689_AEC_PK_LONG_GAIN_HIGH_REG, + ov4689_FETCH_MSB_GAIN(a_gain)); + ret |= ov_camera_module_write_reg(cam_mod, + ov4689_AEC_PK_LONG_GAIN_LOW_REG, + ov4689_FETCH_LSB_GAIN(a_gain)); + ret = ov_camera_module_write_reg(cam_mod, + ov4689_AEC_PK_LONG_EXPO_3RD_REG, + ov4689_FETCH_3RD_BYTE_EXP(exp_time)); + ret |= ov_camera_module_write_reg(cam_mod, + ov4689_AEC_PK_LONG_EXPO_2ND_REG, + ov4689_FETCH_2ND_BYTE_EXP(exp_time)); + ret |= ov_camera_module_write_reg(cam_mod, + ov4689_AEC_PK_LONG_EXPO_1ST_REG, + ov4689_FETCH_1ST_BYTE_EXP(exp_time)); + if (cam_mod->state == OV_CAMERA_MODULE_STREAMING) { + ret = ov_camera_module_write_reg(cam_mod, + ov4689_AEC_GROUP_UPDATE_ADDRESS, + ov4689_AEC_GROUP_UPDATE_END_DATA); + ret = ov_camera_module_write_reg(cam_mod, + ov4689_AEC_GROUP_UPDATE_ADDRESS, + ov4689_AEC_GROUP_UPDATE_END_LAUNCH); + } + } + + if (IS_ERR_VALUE(ret)) + ov_camera_module_pr_err(cam_mod, + "failed with error (%d)\n", ret); + return ret; +} + +/*--------------------------------------------------------------------------*/ + +static int ov4689_g_ctrl(struct ov_camera_module *cam_mod, u32 ctrl_id) +{ + int ret = 0; + + ov_camera_module_pr_debug(cam_mod, "\n"); + + switch (ctrl_id) { + case V4L2_CID_GAIN: + case V4L2_CID_EXPOSURE: + case V4L2_CID_FLASH_LED_MODE: + /* nothing to be done here */ + break; + default: + ret = -EINVAL; + break; + } + + if (IS_ERR_VALUE(ret)) + ov_camera_module_pr_debug(cam_mod, + "failed with error (%d)\n", ret); + return ret; +} + +static int ov4689_filltimings(struct ov_camera_module_custom_config *custom) +{ + int i, j; + struct ov_camera_module_config *config; + struct ov_camera_module_timings *timings; + struct ov_camera_module_reg *reg_table; + int reg_table_num_entries; + + for (i = 0; i < custom->num_configs; i++) { + config = &custom->configs[i]; + reg_table = config->reg_table; + reg_table_num_entries = config->reg_table_num_entries; + timings = &config->timings; + + for (j = 0; j < reg_table_num_entries; j++) { + switch (reg_table[j].reg) { + case ov4689_TIMING_VTS_HIGH_REG: + timings->frame_length_lines = + reg_table[j].val << 8; + break; + case ov4689_TIMING_VTS_LOW_REG: + timings->frame_length_lines |= reg_table[j].val; + break; + case ov4689_TIMING_HTS_HIGH_REG: + timings->line_length_pck = + (reg_table[j].val << 8); + break; + case ov4689_TIMING_HTS_LOW_REG: + timings->line_length_pck |= reg_table[j].val; + break; + case ov4689_TIMING_X_INC: + timings->binning_factor_x = + ((reg_table[j].val >> 4) + 1) / 2; + if (timings->binning_factor_x == 0) + timings->binning_factor_x = 1; + break; + case ov4689_TIMING_Y_INC: + timings->binning_factor_y = + ((reg_table[j].val >> 4) + 1) / 2; + if (timings->binning_factor_y == 0) + timings->binning_factor_y = 1; + break; + case ov4689_HORIZONTAL_START_HIGH_REG: + timings->crop_horizontal_start = + reg_table[j].val << 8; + break; + case ov4689_HORIZONTAL_START_LOW_REG: + timings->crop_horizontal_start |= + reg_table[j].val; + break; + case ov4689_VERTICAL_START_HIGH_REG: + timings->crop_vertical_start = + reg_table[j].val << 8; + break; + case ov4689_VERTICAL_START_LOW_REG: + timings->crop_vertical_start |= + reg_table[j].val; + break; + case ov4689_HORIZONTAL_END_HIGH_REG: + timings->crop_horizontal_end = + reg_table[j].val << 8; + break; + case ov4689_HORIZONTAL_END_LOW_REG: + timings->crop_horizontal_end |= + reg_table[j].val; + break; + case ov4689_VERTICAL_END_HIGH_REG: + timings->crop_vertical_end = + reg_table[j].val << 8; + break; + case ov4689_VERTICAL_END_LOW_REG: + timings->crop_vertical_end |= reg_table[j].val; + break; + case ov4689_HORIZONTAL_OUTPUT_SIZE_HIGH_REG: + timings->sensor_output_width = + reg_table[j].val << 8; + break; + case ov4689_HORIZONTAL_OUTPUT_SIZE_LOW_REG: + timings->sensor_output_width |= + reg_table[j].val; + break; + case ov4689_VERTICAL_OUTPUT_SIZE_HIGH_REG: + timings->sensor_output_height = + reg_table[j].val << 8; + break; + case ov4689_VERTICAL_OUTPUT_SIZE_LOW_REG: + timings->sensor_output_height |= + reg_table[j].val; + break; + } + } + + timings->vt_pix_clk_freq_hz = + config->frm_intrvl.interval.denominator * + timings->frame_length_lines * + timings->line_length_pck; + + timings->coarse_integration_time_min = + ov4689_COARSE_INTG_TIME_MIN; + timings->coarse_integration_time_max_margin = + ov4689_COARSE_INTG_TIME_MAX_MARGIN; + + /* OV Sensor do not use fine integration time. */ + timings->fine_integration_time_min = ov4689_FINE_INTG_TIME_MIN; + timings->fine_integration_time_max_margin = + ov4689_FINE_INTG_TIME_MAX_MARGIN; + } + + return 0; +} + +static int ov4689_g_timings(struct ov_camera_module *cam_mod, + struct ov_camera_module_timings *timings) +{ + int ret = 0; + + if (IS_ERR_OR_NULL(cam_mod->active_config)) + goto err; + + *timings = cam_mod->active_config->timings; + + if (cam_mod->frm_intrvl_valid) + timings->vt_pix_clk_freq_hz = + cam_mod->frm_intrvl.interval.denominator + * timings->frame_length_lines + * timings->line_length_pck; + else + timings->vt_pix_clk_freq_hz = + cam_mod->active_config->frm_intrvl.interval.denominator + * timings->frame_length_lines + * timings->line_length_pck; + + return ret; +err: + ov_camera_module_pr_err(cam_mod, "failed with error (%d)\n", ret); + return ret; +} + +/*--------------------------------------------------------------------------*/ + +static int ov4689_set_flip( + struct ov_camera_module *cam_mod, + struct pltfrm_camera_module_reg reglist[], + int len) +{ + int i, mode = 0; + u16 match_reg[2]; + + mode = ov_camera_module_get_flip_mirror(cam_mod); + if (mode == -1) { + ov_camera_module_pr_info(cam_mod, + "dts don't set flip, return!\n"); + return 0; + } + + if (!IS_ERR_OR_NULL(cam_mod->active_config)) { + switch (cam_mod->active_config->frm_fmt.width) { + case ov4689_FULL_SIZE_RESOLUTION_WIDTH: + if (mode == OV_FLIP_BIT_MASK) { + match_reg[0] = 0x06; + match_reg[1] = 0x00; + } else if (mode == OV_MIRROR_BIT_MASK) { + match_reg[0] = 0x00; + match_reg[1] = 0x06; + } else if (mode == (OV_MIRROR_BIT_MASK | + OV_FLIP_BIT_MASK)) { + match_reg[0] = 0x06; + match_reg[1] = 0x06; + } else { + match_reg[0] = 0x00; + match_reg[1] = 0x00; + } + break; + case ov4689_BINING_SIZE_RESOLUTION_WIDTH: + if (mode == OV_FLIP_BIT_MASK) { + match_reg[0] = 0x16; + match_reg[1] = 0x01; + } else if (mode == OV_MIRROR_BIT_MASK) { + match_reg[0] = 0x16; + match_reg[1] = 0x07; + } else if (mode == (OV_MIRROR_BIT_MASK | + OV_FLIP_BIT_MASK)) { + match_reg[0] = 0x16; + match_reg[1] = 0x07; + } else { + match_reg[0] = 0x10; + match_reg[1] = 0x01; + } + break; + default: + return 0; + } + + for (i = len; i > 0; i--) { + if (reglist[i].reg == ov4689_FLIP_REG) + reglist[i].val = match_reg[0]; + else if (reglist[i].reg == ov4689_MIRROR_REG) + reglist[i].val = match_reg[1]; + } + } + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +static int ov4689_s_ctrl(struct ov_camera_module *cam_mod, u32 ctrl_id) +{ + int ret = 0; + + ov_camera_module_pr_debug(cam_mod, "\n"); + + switch (ctrl_id) { + case V4L2_CID_GAIN: + case V4L2_CID_EXPOSURE: + ret = ov4689_write_aec(cam_mod); + break; + case V4L2_CID_FLASH_LED_MODE: + /* nothing to be done here */ + break; + default: + ret = -EINVAL; + break; + } + + if (IS_ERR_VALUE(ret)) + ov_camera_module_pr_debug(cam_mod, + "failed with error (%d) 0x%x\n", ret, ctrl_id); + return ret; +} + +/*--------------------------------------------------------------------------*/ + +static int ov4689_s_ext_ctrls(struct ov_camera_module *cam_mod, + struct ov_camera_module_ext_ctrls *ctrls) +{ + int ret = 0; + + /* Handles only exposure and gain together special case. */ + if (ctrls->count == 1) + ret = ov4689_s_ctrl(cam_mod, ctrls->ctrls[0].id); + else if ((ctrls->count == 3) && + ((ctrls->ctrls[0].id == V4L2_CID_GAIN && + ctrls->ctrls[1].id == V4L2_CID_EXPOSURE) || + (ctrls->ctrls[1].id == V4L2_CID_GAIN && + ctrls->ctrls[0].id == V4L2_CID_EXPOSURE))) + ret = ov4689_write_aec(cam_mod); + else + ret = -EINVAL; + + if (IS_ERR_VALUE(ret)) + ov_camera_module_pr_debug(cam_mod, + "failed with error (%d)\n", ret); + return ret; +} + +/*--------------------------------------------------------------------------*/ + +static int ov4689_start_streaming(struct ov_camera_module *cam_mod) +{ + int ret = 0; + + ov_camera_module_pr_debug(cam_mod, + "active config=%s\n", cam_mod->active_config->name); + + ret = ov4689_g_VTS(cam_mod, &cam_mod->vts_min); + if (IS_ERR_VALUE(ret)) + goto err; + + if (IS_ERR_VALUE(ov_camera_module_write_reg(cam_mod, 0x0100, 1))) + goto err; + + msleep(25); + + return 0; +err: + ov_camera_module_pr_err(cam_mod, "failed with error (%d)\n", + ret); + return ret; +} + +/*--------------------------------------------------------------------------*/ + +static int ov4689_stop_streaming(struct ov_camera_module *cam_mod) +{ + int ret = 0; + + ov_camera_module_pr_debug(cam_mod, "\n"); + + ret = ov_camera_module_write_reg(cam_mod, 0x0100, 0); + if (IS_ERR_VALUE(ret)) + goto err; + + msleep(25); + + return 0; +err: + ov_camera_module_pr_err(cam_mod, "failed with error (%d)\n", ret); + return ret; +} + +/*--------------------------------------------------------------------------*/ +static int ov4689_check_camera_id(struct ov_camera_module *cam_mod) +{ + u32 pidh, pidl; + int ret = 0; + + ov_camera_module_pr_debug(cam_mod, "\n"); + + ret |= ov_camera_module_read_reg(cam_mod, 1, ov4689_PIDH_ADDR, &pidh); + ret |= ov_camera_module_read_reg(cam_mod, 1, ov4689_PIDL_ADDR, &pidl); + if (IS_ERR_VALUE(ret)) { + ov_camera_module_pr_err(cam_mod, + "register read failed, camera module powered off?\n"); + goto err; + } + + if ((pidh == ov4689_PIDH_MAGIC) && (pidl == ov4689_PIDL_MAGIC)) + ov_camera_module_pr_debug(cam_mod, + "successfully detected camera ID 0x%02x%02x\n", + pidh, pidl); + else { + ov_camera_module_pr_err(cam_mod, + "wrong camera ID, expected 0x%02x%02x, detected 0x%02x%02x\n", + ov4689_PIDH_MAGIC, ov4689_PIDL_MAGIC, pidh, pidl); + ret = -EINVAL; + goto err; + } + + return 0; +err: + ov_camera_module_pr_err(cam_mod, "failed with error (%d)\n", ret); + return ret; +} + +/* ======================================================================== */ +/* This part is platform dependent */ +/* ======================================================================== */ + +static struct v4l2_subdev_core_ops ov4689_camera_module_core_ops = { + .g_ctrl = ov_camera_module_g_ctrl, + .s_ctrl = ov_camera_module_s_ctrl, + .s_ext_ctrls = ov_camera_module_s_ext_ctrls, + .s_power = ov_camera_module_s_power, + .ioctl = ov_camera_module_ioctl +}; + +static struct v4l2_subdev_video_ops ov4689_camera_module_video_ops = { + .s_frame_interval = ov_camera_module_s_frame_interval, + .s_stream = ov_camera_module_s_stream +}; + +static struct v4l2_subdev_pad_ops ov4689_camera_module_pad_ops = { + .enum_frame_interval = ov_camera_module_enum_frameintervals, + .get_fmt = ov_camera_module_g_fmt, + .set_fmt = ov_camera_module_s_fmt, +}; + +static struct v4l2_subdev_ops ov4689_camera_module_ops = { + .core = &ov4689_camera_module_core_ops, + .video = &ov4689_camera_module_video_ops, + .pad = &ov4689_camera_module_pad_ops +}; + +static struct ov_camera_module_custom_config ov4689_custom_config = { + .start_streaming = ov4689_start_streaming, + .stop_streaming = ov4689_stop_streaming, + .s_ctrl = ov4689_s_ctrl, + .s_ext_ctrls = ov4689_s_ext_ctrls, + .g_ctrl = ov4689_g_ctrl, + .g_timings = ov4689_g_timings, + .check_camera_id = ov4689_check_camera_id, + .set_flip = ov4689_set_flip, + .configs = ov4689_configs, + .num_configs = ARRAY_SIZE(ov4689_configs), + .power_up_delays_ms = {5, 20, 0} +}; + +static int ov4689_probe( + struct i2c_client *client, + const struct i2c_device_id *id) +{ + dev_info(&client->dev, "probing...\n"); + + ov4689_filltimings(&ov4689_custom_config); + v4l2_i2c_subdev_init(&ov4689.sd, client, &ov4689_camera_module_ops); + + ov4689.custom = ov4689_custom_config; + + dev_info(&client->dev, "probing successful\n"); + return 0; +} + +static int ov4689_remove(struct i2c_client *client) +{ + struct ov_camera_module *cam_mod = i2c_get_clientdata(client); + + dev_info(&client->dev, "removing device...\n"); + + if (!client->adapter) + return -ENODEV; /* our client isn't attached */ + + ov_camera_module_release(cam_mod); + + dev_info(&client->dev, "removed\n"); + return 0; +} + +static const struct i2c_device_id ov4689_id[] = { + { ov4689_DRIVER_NAME, 0 }, + { } +}; + +static const struct of_device_id ov4689_of_match[] = { + {.compatible = "omnivision,ov4689-v4l2-i2c-subdev"}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, ov4689_id); + +static struct i2c_driver ov4689_i2c_driver = { + .driver = { + .name = ov4689_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = ov4689_of_match + }, + .probe = ov4689_probe, + .remove = ov4689_remove, + .id_table = ov4689_id, +}; + +module_i2c_driver(ov4689_i2c_driver); + +MODULE_DESCRIPTION("SoC Camera driver for ov4689"); +MODULE_AUTHOR("George"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/soc_camera/rockchip/ov8858_v4l2-i2c-subdev.c b/drivers/media/i2c/soc_camera/rockchip/ov8858_v4l2-i2c-subdev.c new file mode 100644 index 000000000000..51fee5e516d1 --- /dev/null +++ b/drivers/media/i2c/soc_camera/rockchip/ov8858_v4l2-i2c-subdev.c @@ -0,0 +1,2175 @@ +/* + * drivers/media/i2c/soc_camera/xgold/ov8858.c + * + * ov8858 sensor driver + * + * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd. + * + * Copyright (C) 2012-2014 Intel Mobile Communications GmbH + * + * Copyright (C) 2008 Texas Instruments. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + * Note: + * 07/01/2014: new implementation using v4l2-subdev + * instead of v4l2-int-device. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ov_camera_module.h" + +#define ov8858_DRIVER_NAME "ov8858" + +#define ov8858_FETCH_LSB_GAIN(VAL) ((VAL) & 0x00ff) +#define ov8858_FETCH_MSB_GAIN(VAL) (((VAL) >> 8) & 0xff) +#define ov8858_AEC_PK_LONG_GAIN_HIGH_REG 0x3508 /* Bit 6-13 */ +#define ov8858_AEC_PK_LONG_GAIN_LOW_REG 0x3509 /* Bits 0 -5 */ + +#define ov8858_AEC_PK_LONG_EXPO_3RD_REG 0x3500 /* Exposure Bits 16-19 */ +#define ov8858_AEC_PK_LONG_EXPO_2ND_REG 0x3501 /* Exposure Bits 8-15 */ +#define ov8858_AEC_PK_LONG_EXPO_1ST_REG 0x3502 /* Exposure Bits 0-7 */ + +#define ov8858_AEC_GROUP_UPDATE_ADDRESS 0x3208 +#define ov8858_AEC_GROUP_UPDATE_START_DATA 0x00 +#define ov8858_AEC_GROUP_UPDATE_END_DATA 0x10 +#define ov8858_AEC_GROUP_UPDATE_END_LAUNCH 0xA0 + +#define ov8858_FETCH_3RD_BYTE_EXP(VAL) (((VAL) >> 16) & 0xF) /* 4 Bits */ +#define ov8858_FETCH_2ND_BYTE_EXP(VAL) (((VAL) >> 8) & 0xFF) /* 8 Bits */ +#define ov8858_FETCH_1ST_BYTE_EXP(VAL) ((VAL) & 0xFF) /* 8 Bits */ + +#define ov8858_PIDH_ADDR 0x300B +#define ov8858_PIDL_ADDR 0x300C + +#define ov8858_TIMING_VTS_HIGH_REG 0x380e +#define ov8858_TIMING_VTS_LOW_REG 0x380f +#define ov8858_TIMING_HTS_HIGH_REG 0x380c +#define ov8858_TIMING_HTS_LOW_REG 0x380d +#define ov8858_INTEGRATION_TIME_MARGIN 8 +#define ov8858_FINE_INTG_TIME_MIN 0 +#define ov8858_FINE_INTG_TIME_MAX_MARGIN 0 +#define ov8858_COARSE_INTG_TIME_MIN 16 +#define ov8858_COARSE_INTG_TIME_MAX_MARGIN 4 +#define ov8858_TIMING_X_INC 0x3814 +#define ov8858_TIMING_Y_INC 0x3815 +#define ov8858_HORIZONTAL_START_HIGH_REG 0x3800 +#define ov8858_HORIZONTAL_START_LOW_REG 0x3801 +#define ov8858_VERTICAL_START_HIGH_REG 0x3802 +#define ov8858_VERTICAL_START_LOW_REG 0x3803 +#define ov8858_HORIZONTAL_END_HIGH_REG 0x3804 +#define ov8858_HORIZONTAL_END_LOW_REG 0x3805 +#define ov8858_VERTICAL_END_HIGH_REG 0x3806 +#define ov8858_VERTICAL_END_LOW_REG 0x3807 +#define ov8858_HORIZONTAL_OUTPUT_SIZE_HIGH_REG 0x3808 +#define ov8858_HORIZONTAL_OUTPUT_SIZE_LOW_REG 0x3809 +#define ov8858_VERTICAL_OUTPUT_SIZE_HIGH_REG 0x380a +#define ov8858_VERTICAL_OUTPUT_SIZE_LOW_REG 0x380b +#define ov8858_FLIP_REG 0x3820 +#define ov8858_MIRROR_REG 0x3821 + +#define ov8858_EXT_CLK 26000000 + +#define ov8858_FULL_SIZE_RESOLUTION_WIDTH 3264 +#define ov8858_BINING_SIZE_RESOLUTION_WIDTH 1632 +#define ov8858_VIDEO_SIZE_RESOLUTION_WIDTH 3200 + +#define ov8858_EXP_VALID_FRAMES 4 +/* High byte of product ID */ +#define ov8858_PIDH_MAGIC 0x88 +/* Low byte of product ID */ +#define ov8858_PIDL_MAGIC 0x58 + +bool is_R1A_module; + +#define BG_RATIO_TYPICAL 0x129 +#define RG_RATIO_TYPICAL 0x11f + +struct ov8858_otp_struct { + int otp_en; + int flag; + int module_integrator_id; + int lens_id; + int production_year; + int production_month; + int production_day; + int rg_ratio; + int bg_ratio; + int lenc[240]; + int checksum; + int VCM_start; + int VCM_end; + int VCM_dir; + int R_gain; + int G_gain; + int B_gain; +}; + +static struct ov8858_otp_struct *otp_ptr; +static struct ov_camera_module ov8858; +static struct ov_camera_module_custom_config ov8858_custom_config; + +/* ======================================================================== */ +/* Base sensor configs */ +/* ======================================================================== */ + +/* MCLK:26MHz 3264x2448 18fps mipi 4lane 481Mbps/lane */ +static struct ov_camera_module_reg + ov8858_init_tab_3264_2448_18fps[] = { + /* global setting */ + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0103, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0100, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0300, 0x05}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0301, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0302, 0x94}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0303, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0304, 0x03}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0305, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0306, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030a, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030b, 0x05}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030c, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030e, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030d, 0x6f}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030f, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0312, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x031e, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3033, 0x24}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3600, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3601, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3602, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3603, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3604, 0x22}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3605, 0x20}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3606, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3607, 0x20}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3608, 0x11}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3609, 0x28}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360a, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360b, 0x05}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360c, 0xd4}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360d, 0x40}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360e, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360f, 0x20}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3610, 0x07}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3611, 0x20}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3612, 0x88}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3613, 0x80}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3614, 0x58}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3615, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3616, 0x4a}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3617, 0x40}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3618, 0x5a}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3619, 0x70}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361a, 0x99}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361b, 0x0a}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361c, 0x07}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361d, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361e, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361f, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3638, 0xff}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3633, 0x0f}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3634, 0x0f}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3635, 0x0f}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3636, 0x12}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3645, 0x13}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3646, 0x83}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x364a, 0x07}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3015, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3018, 0x72}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3020, 0x93}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3022, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3031, 0x0a}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3034, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3106, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3305, 0xf1}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3308, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3309, 0x28}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330a, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330b, 0x20}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330c, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330d, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330e, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330f, 0x40}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3307, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3500, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3501, 0x4d}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3502, 0x40}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3503, 0x80}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3505, 0x80}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3508, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3509, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x350c, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x350d, 0x80}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3510, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3511, 0x02}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3512, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3700, 0x30}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3701, 0x18}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3702, 0x50}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3703, 0x32}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3704, 0x28}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3705, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3706, 0x82}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3707, 0x08}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3708, 0x48}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3709, 0x66}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370a, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370b, 0x82}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370c, 0x07}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3718, 0x14}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3719, 0x31}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3712, 0x44}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3714, 0x24}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x371e, 0x31}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x371f, 0x7f}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3720, 0x0a}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3721, 0x0a}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3724, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3725, 0x02}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3726, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3728, 0x0a}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3729, 0x03}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372a, 0x06}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372b, 0xa6}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372c, 0xa6}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372d, 0xa6}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372e, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372f, 0x20}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3730, 0x02}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3731, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3732, 0x28}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3733, 0x10}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3734, 0x40}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3736, 0x30}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x373a, 0x0a}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x373b, 0x0b}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x373c, 0x14}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x373e, 0x06}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3750, 0x0a}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3751, 0x0e}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3755, 0x10}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3758, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3759, 0x4c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375a, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375b, 0x26}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375c, 0x20}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375d, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375e, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375f, 0x28}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3768, 0x22}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3769, 0x44}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x376a, 0x44}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3761, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3762, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3763, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3766, 0xff}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x376b, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3772, 0x46}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3773, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3774, 0x2c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3775, 0x13}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3776, 0x08}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3777, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3778, 0x17}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a0, 0x88}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a1, 0x7a}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a2, 0x7a}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a3, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a4, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a5, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a6, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a7, 0x88}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a8, 0x98}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a9, 0x98}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3760, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x376f, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37aa, 0x88}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37ab, 0x5c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37ac, 0x5c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37ad, 0x55}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37ae, 0x19}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37af, 0x19}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b0, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b1, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b2, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b3, 0x84}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b4, 0x84}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b5, 0x60}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b6, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b7, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b8, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b9, 0xff}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3800, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3801, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3802, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3803, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3804, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3805, 0xd3}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3806, 0x09}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3807, 0xa3}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3808, 0x06}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3809, 0x60}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380a, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380b, 0xc8}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380c, 0x0b}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380d, 0x64}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380e, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380f, 0xdc}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3810, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3811, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3813, 0x02}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3814, 0x03}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3815, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3820, 0x06}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3821, 0x40}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382a, 0x03}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382b, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3830, 0x08}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3836, 0x02}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3837, 0x18}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3841, 0xff}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3846, 0x48}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3d85, 0x16}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3d8c, 0x73}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3d8d, 0xde}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3f08, 0x10}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3f0a, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4000, 0xf1}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4001, 0x10}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4005, 0x10}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4002, 0x27}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4009, 0x81}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x400b, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4011, 0x20}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x401b, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x401d, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4020, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4021, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4022, 0x06}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4023, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4024, 0x0f}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4025, 0x2a}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4026, 0x0f}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4027, 0x2b}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4028, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4029, 0x02}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402a, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402b, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402c, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402d, 0x02}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402e, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402f, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x401f, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4034, 0x3f}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x403d, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4300, 0xff}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4301, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4302, 0x0f}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4316, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4500, 0x58}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4503, 0x18}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4600, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4601, 0xcb}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x481f, 0x32}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4821, 0x5f}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4837, 0x21}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4850, 0x10}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4851, 0x32}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4b00, 0x2a}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4b0d, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d00, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d01, 0x18}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d02, 0xc3}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d03, 0xff}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d04, 0xff}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d05, 0xff}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5000, 0xfe}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5001, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5002, 0x08}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5003, 0x20}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5046, 0x12}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5780, 0x3e}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5781, 0x0f}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5782, 0x44}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5783, 0x02}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5784, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5785, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5786, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5787, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5788, 0x02}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5789, 0x0f}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x578a, 0xfd}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x578b, 0xf5}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x578c, 0xf5}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x578d, 0x03}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x578e, 0x08}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x578f, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5790, 0x08}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5791, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5792, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5793, 0x52}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5794, 0xa3}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5871, 0x0d}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5870, 0x18}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x586e, 0x10}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x586f, 0x08}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x58f8, 0x3d}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5901, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5b00, 0x02}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5b01, 0x10}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5b02, 0x03}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5b03, 0xcf}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5b05, 0x6c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5e00, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5e01, 0x41}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382d, 0x7f}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4825, 0x3a}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4826, 0x40}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4808, 0x25}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3763, 0x18}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3768, 0xcc}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x470b, 0x28}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4202, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x400d, 0x10}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4040, 0x07}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x403e, 0x08}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4041, 0xc6}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3007, 0x80}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x400a, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4500, 0x58}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3021, 0x23}, + /* @@ SIZE_3264X2448_15FPS_MIPI_4LANE based on R2A_EN_AM22 version db */ + /* ;;pll2_VCO= 721.5, SYSclk=144.3, Dac_clk =360.75 */ + /* ;;pll1_VCO= 962, pll_MIPIclk=481Mbps */ + /* mipi_Pclk =60.125, Sclk1=120.25 */ + /* ;;fps=15, 10bit,4lane, Vblanking=7.251ms */ + /* update on 2015/06/25.*/ + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3808, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3809, 0xc0}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380a, 0x09}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380b, 0x90}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380c, 0x0d}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380d, 0xa0}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380e, 0x0a}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380f, 0xbc}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3814, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3821, 0x46}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382a, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382d, 0xff}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3830, 0x06}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3836, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3f0a, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4001, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4022, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4023, 0x60}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4025, 0x36}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4027, 0x37}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402b, 0x08}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402f, 0x08}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4500, 0x58}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4600, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4601, 0x97} +}; + +static const struct ov_camera_module_reg + ov8858_init_tab_1632_1224_30fps_R1A[] = { + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0103, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0100, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0100, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0100, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0100, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0302, 0x1e}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0303, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0304, 0x03}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030e, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030f, 0x09}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0312, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x031e, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3600, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3601, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3602, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3603, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3604, 0x22}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3605, 0x30}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3606, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3607, 0x20}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3608, 0x11}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3609, 0x28}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360a, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360b, 0x06}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360c, 0xdc}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360d, 0x40}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360e, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360f, 0x20}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3610, 0x07}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3611, 0x20}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3612, 0x88}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3613, 0x80}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3614, 0x58}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3615, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3616, 0x4a}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3617, 0xb0}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3618, 0x56}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3619, 0x70}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361a, 0x99}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361b, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361c, 0x07}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361d, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361e, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361f, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3638, 0xff}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3633, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3634, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3635, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3636, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3645, 0x13}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3646, 0x83}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x364a, 0x07}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3015, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3018, 0x32}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3020, 0x93}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3022, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3031, 0x0a}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3034, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3106, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3305, 0xf1}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3308, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3309, 0x28}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330a, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330b, 0x20}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330c, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330d, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330e, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330f, 0x40}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3307, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3500, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3501, 0x4d}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3502, 0x40}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3503, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3505, 0x80}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3508, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3509, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x350c, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x350d, 0x80}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3510, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3511, 0x02}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3512, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3700, 0x18}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3701, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3702, 0x28}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3703, 0x19}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3704, 0x14}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3705, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3706, 0x35}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3707, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3708, 0x24}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3709, 0x33}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370a, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370b, 0xb5}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370c, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3718, 0x12}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3719, 0x31}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3712, 0x42}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3714, 0x24}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x371e, 0x19}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x371f, 0x40}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3720, 0x05}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3721, 0x05}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3724, 0x06}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3725, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3726, 0x06}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3728, 0x05}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3729, 0x02}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372a, 0x03}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372b, 0x53}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372c, 0xa3}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372d, 0x53}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372e, 0x06}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372f, 0x10}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3730, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3731, 0x06}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3732, 0x14}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3733, 0x10}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3734, 0x40}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3736, 0x20}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x373a, 0x05}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x373b, 0x06}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x373c, 0x0a}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x373e, 0x03}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3755, 0x10}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3758, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3759, 0x4c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375a, 0x06}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375b, 0x13}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375c, 0x20}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375d, 0x02}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375e, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375f, 0x14}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3768, 0x22}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3769, 0x44}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x376a, 0x44}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3761, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3762, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3763, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3766, 0xff}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x376b, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3772, 0x23}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3773, 0x02}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3774, 0x16}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3775, 0x12}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3776, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3777, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3778, 0x1b}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a0, 0x44}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a1, 0x3d}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a2, 0x3d}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a3, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a4, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a5, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a6, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a7, 0x44}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a8, 0x4c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a9, 0x4c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3760, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x376f, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37aa, 0x44}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37ab, 0x2e}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37ac, 0x2e}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37ad, 0x33}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37ae, 0x0d}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37af, 0x0d}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b0, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b1, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b2, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b3, 0x42}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b4, 0x42}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b5, 0x33}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b6, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b7, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b8, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b9, 0xff}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3800, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3801, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3802, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3803, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3804, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3805, 0xd3}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3806, 0x09}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3807, 0xa3}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3808, 0x06}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3809, 0x60}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380a, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380b, 0xc8}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380c, 0x07}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380d, 0x88}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380e, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380f, 0xdc}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3810, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3811, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3813, 0x02}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3814, 0x03}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3815, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3820, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3821, 0x67}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382a, 0x03}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382b, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3830, 0x08}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3836, 0x02}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3837, 0x18}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3841, 0xff}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3846, 0x48}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3d85, 0x14}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3f08, 0x08}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3f0a, 0x80}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4000, 0xf1}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4001, 0x10}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4005, 0x10}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4002, 0x27}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4009, 0x81}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x400b, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x401b, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x401d, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4020, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4021, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4022, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4023, 0xb9}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4024, 0x05}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4025, 0x2a}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4026, 0x05}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4027, 0x2b}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4028, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4029, 0x02}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402a, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402b, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402c, 0x02}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402d, 0x02}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402e, 0x08}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402f, 0x02}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x401f, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4034, 0x3f}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x403d, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4300, 0xff}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4301, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4302, 0x0f}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4316, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4500, 0x38}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4503, 0x18}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4600, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4601, 0xcb}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x481f, 0x32}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4837, 0x16}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4850, 0x10}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4851, 0x32}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4b00, 0x2a}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4b0d, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d00, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d01, 0x18}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d02, 0xc3}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d03, 0xff}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d04, 0xff}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d05, 0xff}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5000, 0x7e}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5001, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5002, 0x08}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5003, 0x20}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5046, 0x12}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5901, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5e00, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5e01, 0x41}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382d, 0x7f}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4825, 0x3a}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4826, 0x40}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4808, 0x25}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030e, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030f, 0x09}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0312, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3015, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3501, 0x4d}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3502, 0x40}, + /* {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3508, 0x04}, */ + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3706, 0x35}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370a, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370b, 0xb5}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3778, 0x1b}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3808, 0x06}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3809, 0x60}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380a, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380b, 0xc8}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380c, 0x07}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380d, 0x88}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380e, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380f, 0xdc}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3814, 0x03}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3821, 0x67}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382a, 0x03}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3830, 0x08}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3836, 0x02}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3f0a, 0x80}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4001, 0x10}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4022, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4023, 0xb9}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4024, 0x05}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4025, 0x2a}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4026, 0x05}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4027, 0x2b}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402b, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402e, 0x08}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4500, 0x38}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4600, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4601, 0xcb}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382d, 0x7f} +}; + +static const struct ov_camera_module_reg + ov8858_init_tab_3264_2448_15fps_R1A[] = { + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0103, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0100, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0100, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0100, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0100, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0302, 0x1e}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0303, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0304, 0x03}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030e, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030f, 0x09}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0312, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x031e, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3600, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3601, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3602, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3603, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3604, 0x22}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3605, 0x30}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3606, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3607, 0x20}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3608, 0x11}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3609, 0x28}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360a, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360b, 0x06}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360c, 0xdc}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360d, 0x40}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360e, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x360f, 0x20}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3610, 0x07}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3611, 0x20}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3612, 0x88}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3613, 0x80}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3614, 0x58}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3615, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3616, 0x4a}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3617, 0xb0}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3618, 0x56}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3619, 0x70}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361a, 0x99}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361b, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361c, 0x07}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361d, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361e, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x361f, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3638, 0xff}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3633, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3634, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3635, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3636, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3645, 0x13}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3646, 0x83}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x364a, 0x07}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3015, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3018, 0x32}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3020, 0x93}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3022, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3031, 0x0a}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3034, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3106, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3305, 0xf1}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3308, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3309, 0x28}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330a, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330b, 0x20}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330c, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330d, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330e, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x330f, 0x40}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3307, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3500, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3501, 0x4d}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3502, 0x40}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3503, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3505, 0x80}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3508, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3509, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x350c, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x350d, 0x80}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3510, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3511, 0x02}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3512, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3700, 0x18}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3701, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3702, 0x28}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3703, 0x19}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3704, 0x14}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3705, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3706, 0x35}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3707, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3708, 0x24}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3709, 0x33}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370a, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370b, 0xb5}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370c, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3718, 0x12}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3719, 0x31}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3712, 0x42}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3714, 0x24}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x371e, 0x19}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x371f, 0x40}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3720, 0x05}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3721, 0x05}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3724, 0x06}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3725, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3726, 0x06}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3728, 0x05}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3729, 0x02}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372a, 0x03}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372b, 0x53}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372c, 0xa3}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372d, 0x53}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372e, 0x06}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x372f, 0x10}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3730, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3731, 0x06}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3732, 0x14}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3733, 0x10}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3734, 0x40}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3736, 0x20}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x373a, 0x05}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x373b, 0x06}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x373c, 0x0a}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x373e, 0x03}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3755, 0x10}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3758, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3759, 0x4c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375a, 0x06}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375b, 0x13}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375c, 0x20}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375d, 0x02}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375e, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x375f, 0x14}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3768, 0x22}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3769, 0x44}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x376a, 0x44}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3761, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3762, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3763, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3766, 0xff}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x376b, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3772, 0x23}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3773, 0x02}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3774, 0x16}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3775, 0x12}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3776, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3777, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3778, 0x1b}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a0, 0x44}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a1, 0x3d}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a2, 0x3d}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a3, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a4, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a5, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a6, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a7, 0x44}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a8, 0x4c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37a9, 0x4c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3760, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x376f, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37aa, 0x44}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37ab, 0x2e}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37ac, 0x2e}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37ad, 0x33}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37ae, 0x0d}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37af, 0x0d}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b0, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b1, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b2, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b3, 0x42}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b4, 0x42}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b5, 0x33}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b6, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b7, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b8, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x37b9, 0xff}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3800, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3801, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3802, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3803, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3804, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3805, 0xd3}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3806, 0x09}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3807, 0xa3}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3808, 0x06}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3809, 0x60}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380a, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380b, 0xc8}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380c, 0x07}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380d, 0x88}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380e, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380f, 0xdc}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3810, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3811, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3813, 0x02}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3814, 0x03}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3815, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3820, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3821, 0x67}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382a, 0x03}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382b, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3830, 0x08}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3836, 0x02}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3837, 0x18}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3841, 0xff}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3846, 0x48}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3d85, 0x14}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3f08, 0x08}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3f0a, 0x80}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4000, 0xf1}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4001, 0x10}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4005, 0x10}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4002, 0x27}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4009, 0x81}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x400b, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x401b, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x401d, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4020, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4021, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4022, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4023, 0xb9}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4024, 0x05}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4025, 0x2a}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4026, 0x05}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4027, 0x2b}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4028, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4029, 0x02}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402a, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402b, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402c, 0x02}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402d, 0x02}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402e, 0x08}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402f, 0x02}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x401f, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4034, 0x3f}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x403d, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4300, 0xff}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4301, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4302, 0x0f}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4316, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4500, 0x38}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4503, 0x18}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4600, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4601, 0xcb}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x481f, 0x32}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4837, 0x16}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4850, 0x10}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4851, 0x32}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4b00, 0x2a}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4b0d, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d00, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d01, 0x18}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d02, 0xc3}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d03, 0xff}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d04, 0xff}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4d05, 0xff}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5000, 0x7e}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5001, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5002, 0x08}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5003, 0x20}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5046, 0x12}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5901, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5e00, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x5e01, 0x41}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382d, 0x7f}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4825, 0x3a}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4826, 0x40}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4808, 0x25}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030e, 0x02}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x030f, 0x04}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x0312, 0x03}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3015, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3501, 0x9a}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3502, 0x20}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3706, 0x6a}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370a, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x370b, 0x6a}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3778, 0x32}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3808, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3809, 0xc0}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380a, 0x09}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380b, 0x90}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380c, 0x07}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380d, 0x94}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380e, 0x09}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x380f, 0xaa}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3814, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3821, 0x46}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382a, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3830, 0x06}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3836, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x3f0a, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4001, 0x00}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4022, 0x0b}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4023, 0xc3}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4024, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4025, 0x36}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4026, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4027, 0x37}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402b, 0x08}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x402e, 0x0c}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4500, 0x58}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4600, 0x01}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x4601, 0x97}, + {OV_CAMERA_MODULE_REG_TYPE_DATA, 0x382d, 0xff} +}; + +/* ======================================================================== */ +static struct ov_camera_module_config ov8858_configs[] = { +{ + .name = "3264x2448_18fps", + .frm_fmt = { + .width = 3264, + .height = 2448, + .code = MEDIA_BUS_FMT_SBGGR10_1X10 + }, + .frm_intrvl = { + .interval = { + .numerator = 1, + .denominator = 18 + } + }, + .auto_exp_enabled = false, + .auto_gain_enabled = false, + .auto_wb_enabled = false, + .reg_table = (void *)ov8858_init_tab_3264_2448_18fps, + .reg_table_num_entries = + sizeof(ov8858_init_tab_3264_2448_18fps) / + sizeof(ov8858_init_tab_3264_2448_18fps[0]), + .reg_diff_table = NULL, + .reg_diff_table_num_entries = 0, + .v_blanking_time_us = 7251, + PLTFRM_CAM_ITF_MIPI_CFG(0, 4, 481, 24000000) + } +}; + +static struct ov_camera_module_config ov8858_configs_R1A[] = { + { + .name = "1632x1224_30fps", + .frm_fmt = { + .width = 1632, + .height = 1224, + .code = MEDIA_BUS_FMT_SBGGR10_1X10 + }, + .frm_intrvl = { + .interval = { + .numerator = 1, + .denominator = 30 + } + }, + .auto_exp_enabled = false, + .auto_gain_enabled = false, + .auto_wb_enabled = false, + .reg_table = (void *)ov8858_init_tab_1632_1224_30fps_R1A, + .reg_table_num_entries = + sizeof(ov8858_init_tab_1632_1224_30fps_R1A) / + sizeof(ov8858_init_tab_1632_1224_30fps_R1A[0]), + .v_blanking_time_us = 6579, + PLTFRM_CAM_ITF_MIPI_CFG(0, 2, 720, 24000000) + }, + { + .name = "3264x2448_15fps", + .frm_fmt = { + .width = 3264, + .height = 2448, + .code = MEDIA_BUS_FMT_SBGGR10_1X10 + }, + .frm_intrvl = { + .interval = { + .numerator = 1, + .denominator = 15 + } + }, + .auto_exp_enabled = false, + .auto_gain_enabled = false, + .auto_wb_enabled = false, + .reg_table = (void *)ov8858_init_tab_3264_2448_15fps_R1A, + .reg_table_num_entries = + sizeof(ov8858_init_tab_3264_2448_15fps_R1A) / + sizeof(ov8858_init_tab_3264_2448_15fps_R1A[0]), + .v_blanking_time_us = 6579, + PLTFRM_CAM_ITF_MIPI_CFG(0, 2, 720, 24000000) + } +}; + +/*--------------------------------------------------------------------------*/ + +static int ov8858_g_VTS(struct ov_camera_module *cam_mod, u32 *vts) +{ + u32 msb, lsb; + int ret; + + ret = ov_camera_module_read_reg_table( + cam_mod, + ov8858_TIMING_VTS_HIGH_REG, + &msb); + if (IS_ERR_VALUE(ret)) + goto err; + + ret = ov_camera_module_read_reg_table( + cam_mod, + ov8858_TIMING_VTS_LOW_REG, + &lsb); + if (IS_ERR_VALUE(ret)) + goto err; + + *vts = (msb << 8) | lsb; + cam_mod->vts_cur = *vts; + + return 0; +err: + ov_camera_module_pr_err(cam_mod, + "failed with error (%d)\n", ret); + return ret; +} + +/*--------------------------------------------------------------------------*/ + +static int ov8858_auto_adjust_fps(struct ov_camera_module *cam_mod, + u32 exp_time) +{ + int ret; + u32 vts; + + if ((cam_mod->exp_config.exp_time + ov8858_COARSE_INTG_TIME_MAX_MARGIN) + > cam_mod->vts_min) + vts = cam_mod->exp_config.exp_time + + ov8858_COARSE_INTG_TIME_MAX_MARGIN; + else + vts = cam_mod->vts_min; + ret = ov_camera_module_write_reg(cam_mod, + ov8858_TIMING_VTS_LOW_REG, + vts & 0xFF); + ret |= ov_camera_module_write_reg(cam_mod, + ov8858_TIMING_VTS_HIGH_REG, + (vts >> 8) & 0xFF); + + if (IS_ERR_VALUE(ret)) { + ov_camera_module_pr_err(cam_mod, + "failed with error (%d)\n", ret); + } else { + ov_camera_module_pr_debug(cam_mod, + "updated vts = %d,vts_min=%d\n", vts, cam_mod->vts_min); + cam_mod->vts_cur = vts; + } + + return ret; +} + +/*--------------------------------------------------------------------------*/ +static int ov8858_write_aec(struct ov_camera_module *cam_mod) +{ + int ret = 0; + + ov_camera_module_pr_debug(cam_mod, + "exp_time = %d, gain = %d, flash_mode = %d\n", + cam_mod->exp_config.exp_time, + cam_mod->exp_config.gain, + cam_mod->exp_config.flash_mode); + + /* + * if the sensor is already streaming, write to shadow registers, + * if the sensor is in SW standby, write to active registers, + * if the sensor is off/registers are not writeable, do nothing + */ + if ((cam_mod->state == OV_CAMERA_MODULE_SW_STANDBY) || + (cam_mod->state == OV_CAMERA_MODULE_STREAMING)) { + u32 a_gain = cam_mod->exp_config.gain; + u32 exp_time; + + a_gain = a_gain > 0x7ff ? 0x7ff : a_gain; + a_gain = a_gain * cam_mod->exp_config.gain_percent / 100; + exp_time = cam_mod->exp_config.exp_time << 4; + if (cam_mod->state == OV_CAMERA_MODULE_STREAMING) + ret = ov_camera_module_write_reg(cam_mod, + ov8858_AEC_GROUP_UPDATE_ADDRESS, + ov8858_AEC_GROUP_UPDATE_START_DATA); + if (!IS_ERR_VALUE(ret) && cam_mod->auto_adjust_fps) + ret = ov8858_auto_adjust_fps(cam_mod, + cam_mod->exp_config.exp_time); + ret |= ov_camera_module_write_reg(cam_mod, + ov8858_AEC_PK_LONG_GAIN_HIGH_REG, + ov8858_FETCH_MSB_GAIN(a_gain)); + ret |= ov_camera_module_write_reg(cam_mod, + ov8858_AEC_PK_LONG_GAIN_LOW_REG, + ov8858_FETCH_LSB_GAIN(a_gain)); + ret = ov_camera_module_write_reg(cam_mod, + ov8858_AEC_PK_LONG_EXPO_3RD_REG, + ov8858_FETCH_3RD_BYTE_EXP(exp_time)); + ret |= ov_camera_module_write_reg(cam_mod, + ov8858_AEC_PK_LONG_EXPO_2ND_REG, + ov8858_FETCH_2ND_BYTE_EXP(exp_time)); + ret |= ov_camera_module_write_reg(cam_mod, + ov8858_AEC_PK_LONG_EXPO_1ST_REG, + ov8858_FETCH_1ST_BYTE_EXP(exp_time)); + if (cam_mod->state == OV_CAMERA_MODULE_STREAMING) { + ret = ov_camera_module_write_reg(cam_mod, + ov8858_AEC_GROUP_UPDATE_ADDRESS, + ov8858_AEC_GROUP_UPDATE_END_DATA); + ret = ov_camera_module_write_reg(cam_mod, + ov8858_AEC_GROUP_UPDATE_ADDRESS, + ov8858_AEC_GROUP_UPDATE_END_LAUNCH); + } + } + + if (IS_ERR_VALUE(ret)) + ov_camera_module_pr_err(cam_mod, + "failed with error (%d)\n", ret); + return ret; +} + +/*--------------------------------------------------------------------------*/ + +static int ov8858_g_ctrl(struct ov_camera_module *cam_mod, u32 ctrl_id) +{ + int ret = 0; + + ov_camera_module_pr_debug(cam_mod, "\n"); + + switch (ctrl_id) { + case V4L2_CID_GAIN: + case V4L2_CID_EXPOSURE: + case V4L2_CID_FLASH_LED_MODE: + /* nothing to be done here */ + break; + default: + ret = -EINVAL; + break; + } + + if (IS_ERR_VALUE(ret)) + ov_camera_module_pr_debug(cam_mod, + "failed with error (%d)\n", ret); + return ret; +} + +/*--------------------------------------------------------------------------*/ +static int ov8858_filltimings(struct ov_camera_module_custom_config *custom) +{ + int i, j; + struct ov_camera_module_config *config; + struct ov_camera_module_timings *timings; + struct ov_camera_module_reg *reg_table; + int reg_table_num_entries; + + for (i = 0; i < custom->num_configs; i++) { + config = &custom->configs[i]; + reg_table = config->reg_table; + reg_table_num_entries = config->reg_table_num_entries; + timings = &config->timings; + + memset(timings, 0x00, sizeof(*timings)); + for (j = 0; j < reg_table_num_entries; j++) { + switch (reg_table[j].reg) { + case ov8858_TIMING_VTS_HIGH_REG: + timings->frame_length_lines = + ((reg_table[j].val << 8) | + (timings->frame_length_lines & 0xff)); + break; + case ov8858_TIMING_VTS_LOW_REG: + timings->frame_length_lines = + (reg_table[j].val | + (timings->frame_length_lines & 0xff00)); + break; + case ov8858_TIMING_HTS_HIGH_REG: + timings->line_length_pck = + ((reg_table[j].val << 8) | + timings->line_length_pck); + break; + case ov8858_TIMING_HTS_LOW_REG: + timings->line_length_pck = + (reg_table[j].val | + (timings->line_length_pck & 0xff00)); + break; + case ov8858_TIMING_X_INC: + timings->binning_factor_x = + ((reg_table[j].val >> 4) + 1) / 2; + if (timings->binning_factor_x == 0) + timings->binning_factor_x = 1; + break; + case ov8858_TIMING_Y_INC: + timings->binning_factor_y = + ((reg_table[j].val >> 4) + 1) / 2; + if (timings->binning_factor_y == 0) + timings->binning_factor_y = 1; + break; + case ov8858_HORIZONTAL_START_HIGH_REG: + timings->crop_horizontal_start = + ((reg_table[j].val << 8) | + (timings->crop_horizontal_start & + 0xff)); + break; + case ov8858_HORIZONTAL_START_LOW_REG: + timings->crop_horizontal_start = + (reg_table[j].val | + (timings->crop_horizontal_start & + 0xff00)); + break; + case ov8858_VERTICAL_START_HIGH_REG: + timings->crop_vertical_start = + ((reg_table[j].val << 8) | + (timings->crop_vertical_start & 0xff)); + break; + case ov8858_VERTICAL_START_LOW_REG: + timings->crop_vertical_start = + ((reg_table[j].val) | + (timings->crop_vertical_start & + 0xff00)); + break; + case ov8858_HORIZONTAL_END_HIGH_REG: + timings->crop_horizontal_end = + ((reg_table[j].val << 8) | + (timings->crop_horizontal_end & 0xff)); + break; + case ov8858_HORIZONTAL_END_LOW_REG: + timings->crop_horizontal_end = + (reg_table[j].val | + (timings->crop_horizontal_end & + 0xff00)); + break; + case ov8858_VERTICAL_END_HIGH_REG: + timings->crop_vertical_end = + ((reg_table[j].val << 8) | + (timings->crop_vertical_end & 0xff)); + break; + case ov8858_VERTICAL_END_LOW_REG: + timings->crop_vertical_end = + (reg_table[j].val | + (timings->crop_vertical_end & 0xff00)); + break; + case ov8858_HORIZONTAL_OUTPUT_SIZE_HIGH_REG: + timings->sensor_output_width = + ((reg_table[j].val << 8) | + (timings->sensor_output_width & 0xff)); + break; + case ov8858_HORIZONTAL_OUTPUT_SIZE_LOW_REG: + timings->sensor_output_width = + (reg_table[j].val | + (timings->sensor_output_width & + 0xff00)); + break; + case ov8858_VERTICAL_OUTPUT_SIZE_HIGH_REG: + timings->sensor_output_height = + ((reg_table[j].val << 8) | + (timings->sensor_output_height & 0xff)); + break; + case ov8858_VERTICAL_OUTPUT_SIZE_LOW_REG: + timings->sensor_output_height = + (reg_table[j].val | + (timings->sensor_output_height & + 0xff00)); + break; + case ov8858_AEC_PK_LONG_EXPO_1ST_REG: + timings->exp_time = + ((reg_table[j].val) | + (timings->exp_time & 0xffff00)); + break; + case ov8858_AEC_PK_LONG_EXPO_2ND_REG: + timings->exp_time = + ((reg_table[j].val << 8) | + (timings->exp_time & 0x00ff00)); + break; + case ov8858_AEC_PK_LONG_EXPO_3RD_REG: + timings->exp_time = + (((reg_table[j].val & 0x0f) << 16) | + (timings->exp_time & 0xff0000)); + break; + case ov8858_AEC_PK_LONG_GAIN_LOW_REG: + timings->gain = + (reg_table[j].val | + (timings->gain & 0x0700)); + break; + case ov8858_AEC_PK_LONG_GAIN_HIGH_REG: + timings->gain = + (((reg_table[j].val & 0x07) << 8) | + (timings->gain & 0xff)); + break; + } + } + + timings->exp_time >>= 4; + timings->vt_pix_clk_freq_hz = + config->frm_intrvl.interval.denominator + * timings->frame_length_lines + * timings->line_length_pck; + + timings->coarse_integration_time_min = + ov8858_COARSE_INTG_TIME_MIN; + timings->coarse_integration_time_max_margin = + ov8858_COARSE_INTG_TIME_MAX_MARGIN; + + /* OV Sensor do not use fine integration time. */ + timings->fine_integration_time_min = + ov8858_FINE_INTG_TIME_MIN; + timings->fine_integration_time_max_margin = + ov8858_FINE_INTG_TIME_MAX_MARGIN; + } + + return 0; +} + +static int ov8858_g_timings(struct ov_camera_module *cam_mod, + struct ov_camera_module_timings *timings) +{ + int ret = 0; + unsigned int vts; + + if (IS_ERR_OR_NULL(cam_mod->active_config)) + goto err; + + *timings = cam_mod->active_config->timings; + + vts = (!cam_mod->vts_cur) ? + timings->frame_length_lines : + cam_mod->vts_cur; + if (cam_mod->frm_intrvl_valid) + timings->vt_pix_clk_freq_hz = + cam_mod->frm_intrvl.interval.denominator * + vts * timings->line_length_pck; + else + timings->vt_pix_clk_freq_hz = + cam_mod->active_config->frm_intrvl.interval.denominator * + vts * timings->line_length_pck; + + return ret; +err: + ov_camera_module_pr_err(cam_mod, + "failed with error (%d)\n", ret); + return ret; +} + +/*--------------------------------------------------------------------------*/ + +static int ov8858_s_ctrl(struct ov_camera_module *cam_mod, u32 ctrl_id) +{ + int ret = 0; + + ov_camera_module_pr_debug(cam_mod, "\n"); + + switch (ctrl_id) { + case V4L2_CID_GAIN: + case V4L2_CID_EXPOSURE: + ret = ov8858_write_aec(cam_mod); + break; + case V4L2_CID_FLASH_LED_MODE: + /* nothing to be done here */ + break; + default: + ret = -EINVAL; + break; + } + + if (IS_ERR_VALUE(ret)) + ov_camera_module_pr_debug(cam_mod, + "failed with error (%d) 0x%x\n", ret, ctrl_id); + return ret; +} + +/*--------------------------------------------------------------------------*/ + +static int ov8858_s_ext_ctrls(struct ov_camera_module *cam_mod, + struct ov_camera_module_ext_ctrls *ctrls) +{ + int ret = 0; + + /* Handles only exposure and gain together special case. */ + if (ctrls->count == 1) + ret = ov8858_s_ctrl(cam_mod, ctrls->ctrls[0].id); + else if ((ctrls->count == 3) && + ((ctrls->ctrls[0].id == V4L2_CID_GAIN && + ctrls->ctrls[1].id == V4L2_CID_EXPOSURE) || + (ctrls->ctrls[1].id == V4L2_CID_GAIN && + ctrls->ctrls[0].id == V4L2_CID_EXPOSURE))) + ret = ov8858_write_aec(cam_mod); + else + ret = -EINVAL; + + if (IS_ERR_VALUE(ret)) + ov_camera_module_pr_debug(cam_mod, + "failed with error (%d)\n", ret); + return ret; +} + +/*--------------------------------------------------------------------------*/ +int update_awb_gain(struct ov_camera_module *cam_mod) +{ + int R_gain = otp_ptr->R_gain; + int G_gain = otp_ptr->G_gain; + int B_gain = otp_ptr->B_gain; + int ret = 0; + + if (R_gain > 0x400) { + ret |= ov_camera_module_write_reg(cam_mod, 0x5032, + R_gain >> 8); + ret |= ov_camera_module_write_reg(cam_mod, 0x5033, + R_gain & 0x00ff); + } + if (G_gain > 0x400) { + ret |= ov_camera_module_write_reg(cam_mod, 0x5034, + G_gain >> 8); + ret |= ov_camera_module_write_reg(cam_mod, 0x5035, + G_gain & 0x00ff); + } + if (B_gain > 0x400) { + ret |= ov_camera_module_write_reg(cam_mod, 0x5036, + B_gain >> 8); + ret |= ov_camera_module_write_reg(cam_mod, 0x5037, + B_gain & 0x00ff); + } + + if (IS_ERR_VALUE(ret)) + ov_camera_module_pr_err(cam_mod, "otp awb gain apply failed\n"); + else + ov_camera_module_pr_debug(cam_mod, + "ov8858_update:%s, rgain:%x ggain %x bgain %x\n", + __func__, R_gain, G_gain, B_gain); + + return ret; +} + +int update_lenc(struct ov_camera_module *cam_mod) +{ + int i, temp = 0, ret = 0; + + ret |= ov_camera_module_read_reg(cam_mod, 1, 0x5000, &temp); + temp = 0x80 | temp; + ret |= ov_camera_module_write_reg(cam_mod, 0x5000, temp); + + if (IS_ERR_VALUE(ret)) { + ov_camera_module_pr_err(cam_mod, + "otp lenc apply failed at beginning\n"); + return ret; + } + + for (i = 0; i < 240; i++) { + ret |= ov_camera_module_write_reg(cam_mod, (0x5800 + i), + otp_ptr->lenc[i]); + if (IS_ERR_VALUE(ret)) { + ov_camera_module_pr_err(cam_mod, + "otp lenc apply failed during procesing\n"); + break; + } + } + + return ret; +} + +static int ov8858_start_streaming(struct ov_camera_module *cam_mod) +{ + int ret = 0; + + ov_camera_module_pr_debug(cam_mod, + "active config=%s\n", cam_mod->active_config->name); + + if (otp_ptr && otp_ptr->otp_en == 1 && + cam_mod->update_config && + cam_mod->active_config->soft_reset) { + ov_camera_module_pr_debug(cam_mod, + "apply otp data for R2A module...\n"); + update_awb_gain(cam_mod); + update_lenc(cam_mod); + } + ret = ov8858_g_VTS(cam_mod, &cam_mod->vts_min); + if (IS_ERR_VALUE(ret)) + goto err; + + if (IS_ERR_VALUE(ov_camera_module_write_reg(cam_mod, 0x0100, 1))) + goto err; + + msleep(25); + + return 0; +err: + ov_camera_module_pr_err(cam_mod, "failed with error (%d)\n", + ret); + return ret; +} + +/*--------------------------------------------------------------------------*/ + +static int ov8858_stop_streaming(struct ov_camera_module *cam_mod) +{ + int ret = 0; + + ov_camera_module_pr_debug(cam_mod, "\n"); + + ret = ov_camera_module_write_reg(cam_mod, 0x0100, 0); + if (IS_ERR_VALUE(ret)) + goto err; + + msleep(25); + + return 0; +err: + ov_camera_module_pr_err(cam_mod, "failed with error (%d)\n", ret); + return ret; +} + +/*--------------------------------------------------------------------------*/ +/* + * call this function after ov8858 initialization + * return value: 0 update success + * 1, no OTP + */ +int __ov8858_read_otp_wb(struct ov_camera_module *cam_mod) +{ + int otp_flag = 0; + int addr = 0; + int ret = 0; + int temp1 = 0, temp2 = 0; + + ret = ov_camera_module_read_reg(cam_mod, 1, 0x7010, &otp_flag); + if (IS_ERR_VALUE(ret)) { + ov_camera_module_pr_err(cam_mod, + "otp wb flag read failed\n"); + return ret; + } + ov_camera_module_pr_debug(cam_mod, "otp wb flag is %0x\n", otp_flag); + + if ((otp_flag & 0xc0) == 0x40) + addr = 0x7011; /* set addr to group 1 base */ + else if ((otp_flag & 0x30) == 0x10) + addr = 0x7019; /* set addr to group 2 base */ + + if (addr != 0) { + ov_camera_module_pr_debug(cam_mod, + "otp wb addr is %0x\n", addr); + otp_ptr->flag = 0xc0; + ret |= ov_camera_module_read_reg(cam_mod, 1, addr, + &otp_ptr->module_integrator_id); + ret |= ov_camera_module_read_reg(cam_mod, 1, (addr + 1), + &otp_ptr->lens_id); + ret |= ov_camera_module_read_reg(cam_mod, 1, (addr + 2), + &otp_ptr->production_year); + ret |= ov_camera_module_read_reg(cam_mod, 1, (addr + 3), + &otp_ptr->production_month); + ret |= ov_camera_module_read_reg(cam_mod, 1, (addr + 4), + &otp_ptr->production_day); + ret |= ov_camera_module_read_reg(cam_mod, 1, + (addr + 5), &temp1); + ret |= ov_camera_module_read_reg(cam_mod, 1, + (addr + 7), &temp2); + otp_ptr->rg_ratio = (temp1 << 2) + (temp2 >> 6 & 0x03); + ret |= ov_camera_module_read_reg(cam_mod, 1, + (addr + 6), &temp1); + otp_ptr->bg_ratio = (temp1 << 2) + (temp2 >> 4 & 0x03); + + if (IS_ERR_VALUE(ret)) { + ov_camera_module_pr_err(cam_mod, + "otp wb data read failed\n"); + return 1; + } + + } else { + ov_camera_module_pr_debug(cam_mod, + "no valid module info and awb data in otp\n"); + otp_ptr->flag = 0; + otp_ptr->module_integrator_id = 0; + otp_ptr->lens_id = 0; + otp_ptr->production_year = 0; + otp_ptr->production_month = 0; + otp_ptr->production_day = 0; + otp_ptr->rg_ratio = 0; + otp_ptr->bg_ratio = 0; + return 1; + } + + otp_ptr->R_gain = (RG_RATIO_TYPICAL * 1000) / otp_ptr->rg_ratio; + otp_ptr->B_gain = (BG_RATIO_TYPICAL * 1000) / otp_ptr->bg_ratio; + otp_ptr->G_gain = 1000; + + if (otp_ptr->R_gain < 1000 || otp_ptr->B_gain < 1000) { + if (otp_ptr->R_gain < otp_ptr->B_gain) + temp1 = otp_ptr->R_gain; + else + temp1 = otp_ptr->B_gain; + } else { + temp1 = otp_ptr->G_gain; + } + + otp_ptr->R_gain = 0x400 * otp_ptr->R_gain / temp1; + otp_ptr->B_gain = 0x400 * otp_ptr->B_gain / temp1; + otp_ptr->G_gain = 0x400 * otp_ptr->G_gain / temp1; + + return ret; +} + +int __ov8858_read_otp_vcm(struct ov_camera_module *cam_mod) +{ + int otp_flag = 0; + int addr = 0; + int ret = 0; + int temp1 = 0, temp2 = 0; + + ret = ov_camera_module_read_reg(cam_mod, 1, 0x7021, &otp_flag); + if (IS_ERR_VALUE(ret)) { + ov_camera_module_pr_err(cam_mod, + "otp vcm flag read failed\n"); + return ret; + } + ov_camera_module_pr_debug(cam_mod, "otp vcm flag is %0x\n", otp_flag); + + if ((otp_flag & 0xc0) == 0x40) + addr = 0x7022; /* set addr to group 1 base */ + else if ((otp_flag & 0x30) == 0x10) + addr = 0x7025; /* set addr to group 2 base */ + + if (addr != 0) { + ov_camera_module_pr_debug(cam_mod, + "otp vcm addr is %0x\n", addr); + otp_ptr->flag |= 0x20; + ret |= ov_camera_module_read_reg(cam_mod, 1, addr, &temp1); + ret |= ov_camera_module_read_reg(cam_mod, 1, + (addr + 2), &temp2); + otp_ptr->VCM_start = (temp1 << 2) | (temp1 >> 6 & 0x03); + ret |= ov_camera_module_read_reg(cam_mod, 1, + (addr + 1), &temp1); + otp_ptr->VCM_end = (temp1 << 2) + (temp2 >> 4 & 0x03); + otp_ptr->VCM_dir = (temp2 >> 2 & 0x03); + + if (IS_ERR_VALUE(ret)) + ov_camera_module_pr_err(cam_mod, + "otp vcm read fail!\n"); + } else { + ov_camera_module_pr_debug(cam_mod, + "no valid vcm data in otp\n"); + otp_ptr->VCM_end = 0; + otp_ptr->VCM_end = 0; + otp_ptr->VCM_dir = 0; + return 1; + } + + return ret; +} + +int __ov8858_read_otp_lenc(struct ov_camera_module *cam_mod) +{ + int otp_flag = 0; + int addr = 0; + int ret = 0, i = 0; + int checksum = 0; + + ret = ov_camera_module_read_reg(cam_mod, 1, 0x7028, &otp_flag); + if (IS_ERR_VALUE(ret)) { + ov_camera_module_pr_err(cam_mod, + "otp lenc flag read failed\n"); + return ret; + } + + ov_camera_module_pr_debug(cam_mod, + "otp lenc flag is 0x%0x\n", otp_flag); + + if ((otp_flag & 0xc0) == 0x40) + addr = 0x7029; /* set addr to group 1 base */ + else if ((otp_flag & 0x30) == 0x10) + addr = 0x711a; /* set addr to group 2 base */ + + if (addr != 0) { + for (i = 0; i < 240; i++) { + ret |= ov_camera_module_read_reg(cam_mod, 1, (addr + i), + &otp_ptr->lenc[i]); + if (IS_ERR_VALUE(ret)) { + ov_camera_module_pr_err(cam_mod, + "otp lenc data read failed\n"); + return ret; + } + checksum += otp_ptr->lenc[i]; + } + + checksum = (checksum) % 255 + 1; + ret |= ov_camera_module_read_reg(cam_mod, 1, (addr + 240), + &otp_ptr->checksum); + if (IS_ERR_VALUE(ret)) { + ov_camera_module_pr_err(cam_mod, + "otp lenc checksum read failed\n"); + return ret; + } + + if (otp_ptr->checksum == checksum) { + otp_ptr->flag |= 0x10; + } else { + ov_camera_module_pr_err(cam_mod, + "otp lenc checksum no match!\n"); + return 1; + } + } else { + for (i = 0; i < 240; i++) { + ov_camera_module_pr_debug(cam_mod, + "no valid lenc data in otp\n"); + otp_ptr->lenc[i] = 0; + } + return 1; + } + + return ret; +} + +static int ov8858_otp_read(struct ov_camera_module *cam_mod) +{ + int ret = 0; + int temp = 0; + int reg = 0; + int otp_ret = 0; + struct i2c_client *client = v4l2_get_subdevdata(&cam_mod->sd); + + ov_camera_module_pr_debug(cam_mod, "\n"); + + if (otp_ptr) { + ov_camera_module_pr_debug(cam_mod, "OTP data loaded already\n"); + return 0; + } + + otp_ptr = kzalloc(sizeof(*otp_ptr), GFP_KERNEL); + if (!otp_ptr) { + ov_camera_module_pr_err(cam_mod, "otp alloc fail!\n"); + return -ENOMEM; + } + + ov_camera_module_write_reg(cam_mod, 0x0100, 1); + + /* set 0x5002[3] to "0" before ops */ + ret = ov_camera_module_read_reg(cam_mod, 1, 0x5002, &temp); + if (IS_ERR_VALUE(ret)) { + ov_camera_module_pr_err(cam_mod, + "otp read failed at phase1\n"); + return ret; + } + + ov_camera_module_pr_debug(cam_mod, "otp read phase1 pass\n"); + + ret |= ov_camera_module_write_reg(cam_mod, 0x5002, + ((temp & (~0x08)))); + + ret |= ov_camera_module_write_reg(cam_mod, 0x3d84, 0xc0); + /* otp start addr */ + ret |= ov_camera_module_write_reg(cam_mod, 0x3d88, 0x70); + ret |= ov_camera_module_write_reg(cam_mod, 0x3d89, 0x10); + /* otp end addr */ + ret |= ov_camera_module_write_reg(cam_mod, 0x3d8A, 0x72); + ret |= ov_camera_module_write_reg(cam_mod, 0x3d8B, 0x0a); + /* load otp into buffer */ + ret |= ov_camera_module_write_reg(cam_mod, 0x3d81, 0x01); + if (IS_ERR_VALUE(ret)) { + ov_camera_module_pr_err(cam_mod, + "otp read failed at phase2\n"); + return ret; + } + + ov_camera_module_pr_debug(cam_mod, "otp read phase2 pass\n"); + + usleep_range(10000, 15000); + + /* otp wb read */ + otp_ret = __ov8858_read_otp_wb(cam_mod); + + if (otp_ret) + ov_camera_module_pr_err(cam_mod, "fail to read OTP wb!\n"); + else + ov_camera_module_pr_debug(cam_mod, "read otp wb success\n"); + + /* otp vcm read */ + otp_ret = __ov8858_read_otp_vcm(cam_mod); + + if (otp_ret) + ov_camera_module_pr_err(cam_mod, "fail to read OTP vcm!\n"); + else + ov_camera_module_pr_debug(cam_mod, "read otp vcm success\n"); + + /* otp lenc read */ + otp_ret = __ov8858_read_otp_lenc(cam_mod); + + if (otp_ret) { + ov_camera_module_pr_err(cam_mod, "fail to read OTP lenc!\n"); + goto readotperr; + } else { + ov_camera_module_pr_debug(cam_mod, "read otp lenc success\n"); + } + /* clear data afer read */ + for (reg = 0x7010; reg <= 0x720a; reg++) { + ret |= ov_camera_module_write_reg(cam_mod, reg, 0); + if (IS_ERR_VALUE(ret)) { + ov_camera_module_pr_err(cam_mod, + "otp clear data failed at phase3\n"); + return ret; + } + } + +readotperr: + /* set 0x5002[3] to "1" after ops */ + ret = ov_camera_module_read_reg(cam_mod, 1, 0x5002, &temp); + if (IS_ERR_VALUE(ret)) { + ov_camera_module_pr_err(cam_mod, + "otp exit failed at phase 4\n"); + return ret; + } + + ov_camera_module_pr_debug(cam_mod, "0x5002 is 0x%0x\n", temp); + + ret |= ov_camera_module_write_reg(cam_mod, 0x5002, + (0x08 | (temp & (~0x08)))); + ov_camera_module_write_reg(cam_mod, 0x0100, 0); + if (otp_ret) { + otp_ptr->otp_en = 0; + ov_camera_module_pr_err( + cam_mod, + "get otp data failed\n"); + } else { + otp_ptr->otp_en = 1; + dev_info(&client->dev, "get otp data success\n"); + } + return ret; +} + +/*--------------------------------------------------------------------------*/ + +static int ov8858_check_camera_id(struct ov_camera_module *cam_mod) +{ + u32 pidh, pidl, vid = 0; + int ret = 0; + struct i2c_client *client; + + ov_camera_module_pr_debug(cam_mod, "\n"); + + ret |= ov_camera_module_read_reg(cam_mod, 1, ov8858_PIDH_ADDR, &pidh); + ret |= ov_camera_module_read_reg(cam_mod, 1, ov8858_PIDL_ADDR, &pidl); + if (IS_ERR_VALUE(ret)) { + ov_camera_module_pr_err(cam_mod, + "register read failed, camera module powered off?\n"); + goto err; + } + + if ((pidh == ov8858_PIDH_MAGIC) && (pidl == ov8858_PIDL_MAGIC)) + ov_camera_module_pr_debug(cam_mod, + "successfully detected camera ID 0x%02x%02x\n", + pidh, pidl); + else { + ov_camera_module_pr_err(cam_mod, + "wrong camera ID, expected 0x%02x%02x, detected 0x%02x%02x\n", + ov8858_PIDH_MAGIC, ov8858_PIDL_MAGIC, pidh, pidl); + ret = -EINVAL; + goto err; + } + + ret |= ov_camera_module_read_reg(cam_mod, 1, 0x302a, &vid); + if (IS_ERR_VALUE(ret)) { + ov_camera_module_pr_err(cam_mod, + "register read failed, camera module powered off?\n"); + goto err; + } else + ov_camera_module_pr_debug(cam_mod, + "ov8858 chip version is 0x%2x\n", vid); + + client = v4l2_get_subdevdata(&cam_mod->sd); + if ((vid == 0xb1) || (vid == 0xb0)) { + ov8858_custom_config.configs = ov8858_configs_R1A; + ov8858_custom_config.num_configs = + sizeof(ov8858_configs_R1A) + / sizeof(ov8858_configs_R1A[0]); + dev_info(&client->dev, "init setting for r1a\n"); + } else { + dev_info(&client->dev, "init setting for r2a\n"); + } + + ov8858_filltimings(&ov8858_custom_config); + cam_mod->custom = ov8858_custom_config; + + ov_camera_module_pr_debug(cam_mod, + "ov8858 chip version is 0x%2x,module config no. is %d\n", + vid, + cam_mod->custom.num_configs); + return 0; +err: + ov_camera_module_pr_err(cam_mod, "failed with error (%d)\n", ret); + return ret; +} + +static int ov8858_g_exposure_valid_frame(struct ov_camera_module *cam_mod) +{ + return ov8858_EXP_VALID_FRAMES; +} + +/* ======================================================================== */ +/* This part is platform dependent */ +/* ======================================================================== */ + +static struct v4l2_subdev_core_ops ov8858_camera_module_core_ops = { + .g_ctrl = ov_camera_module_g_ctrl, + .s_ctrl = ov_camera_module_s_ctrl, + .s_ext_ctrls = ov_camera_module_s_ext_ctrls, + .s_power = ov_camera_module_s_power, + .ioctl = ov_camera_module_ioctl +}; + +static struct v4l2_subdev_video_ops ov8858_camera_module_video_ops = { + .s_frame_interval = ov_camera_module_s_frame_interval, + .s_stream = ov_camera_module_s_stream +}; + +static struct v4l2_subdev_pad_ops ov8858_camera_module_pad_ops = { + .enum_frame_interval = ov_camera_module_enum_frameintervals, + .get_fmt = ov_camera_module_g_fmt, + .set_fmt = ov_camera_module_s_fmt, +}; + +static struct v4l2_subdev_ops ov8858_camera_module_ops = { + .core = &ov8858_camera_module_core_ops, + .video = &ov8858_camera_module_video_ops, + .pad = &ov8858_camera_module_pad_ops +}; + +static struct ov_camera_module_custom_config ov8858_custom_config = { + .start_streaming = ov8858_start_streaming, + .stop_streaming = ov8858_stop_streaming, + .s_ctrl = ov8858_s_ctrl, + .s_ext_ctrls = ov8858_s_ext_ctrls, + .g_ctrl = ov8858_g_ctrl, + .g_timings = ov8858_g_timings, + .check_camera_id = ov8858_check_camera_id, + .g_exposure_valid_frame = ov8858_g_exposure_valid_frame, + .read_otp = ov8858_otp_read, + .configs = ov8858_configs, + .num_configs = ARRAY_SIZE(ov8858_configs), + .power_up_delays_ms = {5, 20, 0} +}; + +static int ov8858_probe( + struct i2c_client *client, + const struct i2c_device_id *id) +{ + dev_info(&client->dev, "probing...\n"); + ov8858_filltimings(&ov8858_custom_config); + v4l2_i2c_subdev_init(&ov8858.sd, client, &ov8858_camera_module_ops); + ov8858.custom = ov8858_custom_config; + + dev_info(&client->dev, "probing successful\n"); + + return 0; +} + +static int ov8858_remove(struct i2c_client *client) +{ + struct ov_camera_module *cam_mod = i2c_get_clientdata(client); + + dev_info(&client->dev, "removing device...\n"); + + if (!client->adapter) + return -ENODEV; /* our client isn't attached */ + + ov_camera_module_release(cam_mod); + + kfree(otp_ptr); + dev_info(&client->dev, "removed\n"); + return 0; +} + +static const struct i2c_device_id ov8858_id[] = { + { ov8858_DRIVER_NAME, 0 }, + { } +}; + +static const struct of_device_id ov8858_of_match[] = { + {.compatible = "omnivision,ov8858-v4l2-i2c-subdev"}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, ov8858_id); + +static struct i2c_driver ov8858_i2c_driver = { + .driver = { + .name = ov8858_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = ov8858_of_match + }, + .probe = ov8858_probe, + .remove = ov8858_remove, + .id_table = ov8858_id, +}; + +module_i2c_driver(ov8858_i2c_driver); + +MODULE_DESCRIPTION("SoC Camera driver for ov8858"); +MODULE_AUTHOR("George"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/soc_camera/rockchip/ov_camera_module.c b/drivers/media/i2c/soc_camera/rockchip/ov_camera_module.c new file mode 100644 index 000000000000..ef404b840310 --- /dev/null +++ b/drivers/media/i2c/soc_camera/rockchip/ov_camera_module.c @@ -0,0 +1,1127 @@ +/* + * ov_camera_module.c + * + * Generic omnivision sensor driver + * + * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd. + * + * Copyright (C) 2012-2014 Intel Mobile Communications GmbH + * + * Copyright (C) 2008 Texas Instruments. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ov_camera_module.h" + +static struct ov_camera_module *to_ov_camera_module(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ov_camera_module, sd); +} + +/* ======================================================================== */ + +static void ov_camera_module_reset( + struct ov_camera_module *cam_mod) +{ + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + + cam_mod->inited = false; + cam_mod->active_config = NULL; + cam_mod->update_config = true; + cam_mod->frm_fmt_valid = false; + cam_mod->frm_intrvl_valid = false; + cam_mod->exp_config.auto_exp = false; + cam_mod->exp_config.auto_gain = false; + cam_mod->wb_config.auto_wb = false; + cam_mod->hflip = false; + cam_mod->vflip = false; + cam_mod->auto_adjust_fps = true; + cam_mod->rotation = 0; + cam_mod->ctrl_updt = 0; + cam_mod->state = OV_CAMERA_MODULE_POWER_OFF; + cam_mod->state_before_suspend = OV_CAMERA_MODULE_POWER_OFF; + + cam_mod->exp_config.exp_time = 0; + cam_mod->exp_config.gain = 0; + cam_mod->vts_cur = 0; +} + +/* ======================================================================== */ + +static void ov_camera_module_set_active_config( + struct ov_camera_module *cam_mod, + struct ov_camera_module_config *new_config) +{ + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + + if (IS_ERR_OR_NULL(new_config)) { + cam_mod->active_config = new_config; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "no active config\n"); + } else { + cam_mod->ctrl_updt &= OV_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP | + OV_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN | + OV_CAMERA_MODULE_CTRL_UPDT_AUTO_WB; + if (new_config->auto_exp_enabled != + cam_mod->exp_config.auto_exp) { + cam_mod->ctrl_updt |= + OV_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP; + cam_mod->exp_config.auto_exp = + new_config->auto_exp_enabled; + } + if (new_config->auto_gain_enabled != + cam_mod->exp_config.auto_gain) { + cam_mod->ctrl_updt |= + OV_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN; + cam_mod->exp_config.auto_gain = + new_config->auto_gain_enabled; + } + if (new_config->auto_wb_enabled != + cam_mod->wb_config.auto_wb) { + cam_mod->ctrl_updt |= + OV_CAMERA_MODULE_CTRL_UPDT_AUTO_WB; + cam_mod->wb_config.auto_wb = + new_config->auto_wb_enabled; + } + if (new_config != cam_mod->active_config) { + cam_mod->update_config = true; + cam_mod->active_config = new_config; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "activating config '%s'\n", + cam_mod->active_config->name); + } + } +} + +/* ======================================================================== */ + +static struct ov_camera_module_config *ov_camera_module_find_config( + struct ov_camera_module *cam_mod, + struct v4l2_mbus_framefmt *fmt, + struct v4l2_subdev_frame_interval *frm_intrvl) +{ + u32 i; + unsigned long gcdiv; + struct v4l2_subdev_frame_interval norm_interval; + + if (!IS_ERR_OR_NULL(fmt)) + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "%dx%d, fmt code 0x%04x\n", + fmt->width, fmt->height, fmt->code); + + if (!IS_ERR_OR_NULL(frm_intrvl)) + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "frame interval %d/%d\n", + frm_intrvl->interval.numerator, + frm_intrvl->interval.denominator); + + for (i = 0; i < cam_mod->custom.num_configs; i++) { + if (!IS_ERR_OR_NULL(frm_intrvl)) { + gcdiv = gcd(cam_mod->custom.configs[i]. + frm_intrvl.interval.numerator, + cam_mod->custom.configs[i]. + frm_intrvl.interval.denominator); + norm_interval.interval.numerator = + cam_mod->custom.configs[i]. + frm_intrvl.interval.numerator / gcdiv; + norm_interval.interval.denominator = + cam_mod->custom.configs[i]. + frm_intrvl.interval.denominator / gcdiv; + if ((frm_intrvl->interval.numerator != + norm_interval.interval.numerator) || + (frm_intrvl->interval.denominator != + norm_interval.interval.denominator)) + continue; + } + if (!IS_ERR_OR_NULL(fmt)) { + if ((cam_mod->custom.configs[i].frm_fmt.width != + fmt->width) || + (cam_mod->custom.configs[i].frm_fmt.height != + fmt->height) || + (cam_mod->custom.configs[i].frm_fmt.code != + fmt->code)) { + continue; + } + } + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "found matching config %s\n", + cam_mod->custom.configs[i].name); + return &cam_mod->custom.configs[i]; + } + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "no matching config found\n"); + + return ERR_PTR(-EINVAL); +} + +/* ======================================================================== */ + +static int ov_camera_module_write_config( + struct ov_camera_module *cam_mod) +{ + int ret = 0; + struct ov_camera_module_reg *reg_table; + u32 reg_table_num_entries; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + + if (IS_ERR_OR_NULL(cam_mod->active_config)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "no active sensor configuration"); + ret = -EFAULT; + goto err; + } + + if (!cam_mod->inited) { + cam_mod->active_config->soft_reset = true; + reg_table = cam_mod->active_config->reg_table; + reg_table_num_entries = + cam_mod->active_config->reg_table_num_entries; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "write config %s\n", + cam_mod->active_config->name); + } else { + if (cam_mod->active_config->reg_diff_table && + cam_mod->active_config->reg_diff_table_num_entries) { + cam_mod->active_config->soft_reset = false; + reg_table = cam_mod->active_config->reg_diff_table; + reg_table_num_entries = + cam_mod->active_config->reg_diff_table_num_entries; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "write config %s%s\n", + cam_mod->active_config->name, "_diff"); + } else { + cam_mod->active_config->soft_reset = true; + reg_table = cam_mod->active_config->reg_table; + reg_table_num_entries = + cam_mod->active_config->reg_table_num_entries; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "write config %s\n", + cam_mod->active_config->name); + } + } + + if (!IS_ERR_OR_NULL(cam_mod->custom.set_flip)) + cam_mod->custom.set_flip(cam_mod, + reg_table, reg_table_num_entries); + + ret = pltfrm_camera_module_write_reglist(&cam_mod->sd, + reg_table, reg_table_num_entries); + if (IS_ERR_VALUE(ret)) + goto err; + ret = pltfrm_camera_module_patch_config(&cam_mod->sd, + &cam_mod->frm_fmt, + &cam_mod->frm_intrvl); + if (IS_ERR_VALUE(ret)) + goto err; + + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; +} + +static int ov_camera_module_attach( + struct ov_camera_module *cam_mod) +{ + int ret = 0; + struct ov_camera_module_custom_config *custom; + + custom = &cam_mod->custom; + + if (custom->check_camera_id) { + ov_camera_module_s_power(&cam_mod->sd, 1); + ret = custom->check_camera_id(cam_mod); + ov_camera_module_s_power(&cam_mod->sd, 0); + if (ret != 0) + goto err; + } + + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + ov_camera_module_release(cam_mod); + return ret; +} + +/* ======================================================================== */ + +int ov_camera_module_try_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct ov_camera_module *cam_mod = to_ov_camera_module(sd); + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "%dx%d, fmt code 0x%04x\n", + fmt->width, fmt->height, fmt->code); + + if (IS_ERR_OR_NULL(ov_camera_module_find_config(cam_mod, fmt, NULL))) { + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "format not supported\n"); + return -EINVAL; + } + pltfrm_camera_module_pr_debug(&cam_mod->sd, "format supported\n"); + + return 0; +} + +/* ======================================================================== */ + +int ov_camera_module_s_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct ov_camera_module *cam_mod = to_ov_camera_module(sd); + struct v4l2_mbus_framefmt *fmt = &format->format; + int ret = 0; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "%dx%d, fmt code 0x%04x\n", + fmt->width, fmt->height, fmt->code); + + if (IS_ERR_OR_NULL(ov_camera_module_find_config(cam_mod, fmt, NULL))) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "format %dx%d, code 0x%04x, not supported\n", + fmt->width, fmt->height, fmt->code); + ret = -EINVAL; + goto err; + } + cam_mod->frm_fmt_valid = true; + cam_mod->frm_fmt = *fmt; + if (cam_mod->frm_intrvl_valid) { + ov_camera_module_set_active_config(cam_mod, + ov_camera_module_find_config(cam_mod, + fmt, &cam_mod->frm_intrvl)); + } + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; +} + +/* ======================================================================== */ + +int ov_camera_module_g_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct ov_camera_module *cam_mod = to_ov_camera_module(sd); + struct v4l2_mbus_framefmt *fmt = &format->format; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + + if (cam_mod->active_config) { + fmt->code = cam_mod->active_config->frm_fmt.code; + fmt->width = cam_mod->active_config->frm_fmt.width; + fmt->height = cam_mod->active_config->frm_fmt.height; + return 0; + } + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "no active config\n"); + + return -1; +} + +/* ======================================================================== */ + +int ov_camera_module_s_frame_interval( + struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *interval) +{ + struct ov_camera_module *cam_mod = to_ov_camera_module(sd); + unsigned long gcdiv; + struct v4l2_subdev_frame_interval norm_interval; + int ret = 0; + + if ((interval->interval.denominator == 0) || + (interval->interval.numerator == 0)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "invalid frame interval %d/%d\n", + interval->interval.numerator, + interval->interval.denominator); + ret = -EINVAL; + goto err; + } + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "%d/%d (%dfps)\n", + interval->interval.numerator, interval->interval.denominator, + (interval->interval.denominator + + (interval->interval.numerator >> 1)) / + interval->interval.numerator); + + /* normalize interval */ + gcdiv = gcd(interval->interval.numerator, + interval->interval.denominator); + norm_interval.interval.numerator = + interval->interval.numerator / gcdiv; + norm_interval.interval.denominator = + interval->interval.denominator / gcdiv; + + if (IS_ERR_OR_NULL(ov_camera_module_find_config(cam_mod, + NULL, &norm_interval))) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "frame interval %d/%d not supported\n", + interval->interval.numerator, + interval->interval.denominator); + ret = -EINVAL; + goto err; + } + cam_mod->frm_intrvl_valid = true; + cam_mod->frm_intrvl = norm_interval; + if (cam_mod->frm_fmt_valid) { + ov_camera_module_set_active_config(cam_mod, + ov_camera_module_find_config(cam_mod, + &cam_mod->frm_fmt, interval)); + } + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; +} + +/* ======================================================================== */ + +int ov_camera_module_s_stream(struct v4l2_subdev *sd, int enable) +{ + int ret = 0; + struct ov_camera_module *cam_mod = to_ov_camera_module(sd); + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "%d\n", enable); + + if (enable) { + if (cam_mod->state == OV_CAMERA_MODULE_STREAMING) + return 0; + if (IS_ERR_OR_NULL(cam_mod->active_config)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "no active sensor configuration, cannot start streaming\n"); + ret = -EFAULT; + goto err; + } + if (cam_mod->state != OV_CAMERA_MODULE_SW_STANDBY) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "sensor is not powered on (in state %d), cannot start streaming\n", + cam_mod->state); + ret = -EINVAL; + goto err; + } + if (cam_mod->update_config) + ret = ov_camera_module_write_config(cam_mod); + if (IS_ERR_VALUE(ret)) + goto err; + + ret = cam_mod->custom.start_streaming(cam_mod); + if (IS_ERR_VALUE(ret)) + goto err; + if (!cam_mod->inited && cam_mod->update_config) + cam_mod->inited = true; + cam_mod->update_config = false; + cam_mod->ctrl_updt = 0; + mdelay(cam_mod->custom.power_up_delays_ms[2]); + cam_mod->state = OV_CAMERA_MODULE_STREAMING; + } else { + int pclk; + int wait_ms; + struct isp_supplemental_sensor_mode_data timings; + + if (cam_mod->state != OV_CAMERA_MODULE_STREAMING) + return 0; + ret = cam_mod->custom.stop_streaming(cam_mod); + if (IS_ERR_VALUE(ret)) + goto err; + + ret = ov_camera_module_ioctl(sd, + RK_VIDIOC_SENSOR_MODE_DATA, + &timings); + + cam_mod->state = OV_CAMERA_MODULE_SW_STANDBY; + + if (IS_ERR_VALUE(ret)) + goto err; + + pclk = timings.vt_pix_clk_freq_hz / 1000; + + if (!pclk) + goto err; + + wait_ms = + (timings.line_length_pck * + timings.frame_length_lines) / + pclk; + + /* + * wait for a frame period to make sure that there is + * no pending frame left. + */ + + msleep(wait_ms + 1); + } + + cam_mod->state_before_suspend = cam_mod->state; + + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; +} + +/* ======================================================================== */ + +int ov_camera_module_s_power(struct v4l2_subdev *sd, int on) +{ + int ret = 0; + struct ov_camera_module *cam_mod = to_ov_camera_module(sd); + struct v4l2_subdev *af_ctrl; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "%d\n", on); + + if (on) { + if (cam_mod->state == OV_CAMERA_MODULE_POWER_OFF) { + ret = pltfrm_camera_module_s_power(&cam_mod->sd, 1); + if (!IS_ERR_VALUE(ret)) { + mdelay(cam_mod->custom.power_up_delays_ms[0]); + cam_mod->state = OV_CAMERA_MODULE_HW_STANDBY; + } + } + if (cam_mod->state == OV_CAMERA_MODULE_HW_STANDBY) { + ret = pltfrm_camera_module_set_pin_state(&cam_mod->sd, + PLTFRM_CAMERA_MODULE_PIN_PD, + PLTFRM_CAMERA_MODULE_PIN_STATE_INACTIVE); + if (!IS_ERR_VALUE(ret)) { + mdelay(cam_mod->custom.power_up_delays_ms[1]); + cam_mod->state = OV_CAMERA_MODULE_SW_STANDBY; + if (!IS_ERR_OR_NULL( + cam_mod->custom.init_common) && + cam_mod->custom.init_common( + cam_mod)) + usleep_range(1000, 1500); + + af_ctrl = pltfrm_camera_module_get_af_ctrl(sd); + if (!IS_ERR_OR_NULL(af_ctrl)) { + v4l2_subdev_call(af_ctrl, + core, init, 0); + } + } + } + if (cam_mod->update_config) { + ov_camera_module_write_config(cam_mod); + cam_mod->update_config = false; + } + } else { + if (cam_mod->state == OV_CAMERA_MODULE_STREAMING) { + ret = ov_camera_module_s_stream(sd, 0); + if (!IS_ERR_VALUE(ret)) + cam_mod->state = OV_CAMERA_MODULE_SW_STANDBY; + } + if (cam_mod->state == OV_CAMERA_MODULE_SW_STANDBY) { + ret = pltfrm_camera_module_set_pin_state( + &cam_mod->sd, + PLTFRM_CAMERA_MODULE_PIN_PD, + PLTFRM_CAMERA_MODULE_PIN_STATE_ACTIVE); + + if (!IS_ERR_VALUE(ret)) + cam_mod->state = OV_CAMERA_MODULE_HW_STANDBY; + } + if (cam_mod->state == OV_CAMERA_MODULE_HW_STANDBY) { + ret = pltfrm_camera_module_s_power(&cam_mod->sd, 0); + if (!IS_ERR_VALUE(ret)) { + cam_mod->state = OV_CAMERA_MODULE_POWER_OFF; + ov_camera_module_reset(cam_mod); + } + } + } + + cam_mod->state_before_suspend = cam_mod->state; + + if (IS_ERR_VALUE(ret)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "%s failed, camera left in state %d\n", + on ? "on" : "off", cam_mod->state); + goto err; + } else + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "camera powered %s\n", on ? "on" : "off"); + + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; +} + +/* ======================================================================== */ + +int ov_camera_module_g_ctrl(struct v4l2_subdev *sd, + struct v4l2_control *ctrl) +{ + struct ov_camera_module *cam_mod = to_ov_camera_module(sd); + int ret; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, " id 0x%x\n", ctrl->id); + + if (ctrl->id == V4L2_CID_FLASH_LED_MODE) { + ctrl->value = cam_mod->exp_config.flash_mode; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_FLASH_LED_MODE %d\n", + ctrl->value); + return 0; + } + + if (IS_ERR_OR_NULL(cam_mod->active_config)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "no active configuration\n"); + return -EFAULT; + } + + if (ctrl->id == RK_V4L2_CID_VBLANKING) { + ctrl->value = cam_mod->active_config->v_blanking_time_us; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "RK_V4L2_CID_VBLANKING %d\n", + ctrl->value); + return 0; + } + + if ((cam_mod->state != OV_CAMERA_MODULE_SW_STANDBY) && + (cam_mod->state != OV_CAMERA_MODULE_STREAMING)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "cannot get controls when camera is off\n"); + return -EFAULT; + } + + if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE) { + struct v4l2_subdev *af_ctrl; + + af_ctrl = pltfrm_camera_module_get_af_ctrl(sd); + if (!IS_ERR_OR_NULL(af_ctrl)) { + ret = v4l2_subdev_call(af_ctrl, core, g_ctrl, ctrl); + return ret; + } + } + + if (!IS_ERR_OR_NULL(cam_mod->custom.g_ctrl)) { + ret = cam_mod->custom.g_ctrl(cam_mod, ctrl->id); + if (IS_ERR_VALUE(ret)) + return ret; + } + + switch (ctrl->id) { + case V4L2_CID_GAIN: + ctrl->value = cam_mod->exp_config.gain; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_GAIN %d\n", + ctrl->value); + break; + case V4L2_CID_EXPOSURE: + ctrl->value = cam_mod->exp_config.exp_time; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_EXPOSURE %d\n", + ctrl->value); + break; + case V4L2_CID_WHITE_BALANCE_TEMPERATURE: + ctrl->value = cam_mod->wb_config.temperature; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_WHITE_BALANCE_TEMPERATURE %d\n", + ctrl->value); + break; + case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: + ctrl->value = cam_mod->wb_config.preset_id; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE %d\n", + ctrl->value); + break; + case V4L2_CID_AUTOGAIN: + ctrl->value = cam_mod->exp_config.auto_gain; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_AUTOGAIN %d\n", + ctrl->value); + break; + case V4L2_CID_EXPOSURE_AUTO: + ctrl->value = cam_mod->exp_config.auto_exp; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_EXPOSURE_AUTO %d\n", + ctrl->value); + break; + case V4L2_CID_AUTO_WHITE_BALANCE: + ctrl->value = cam_mod->wb_config.auto_wb; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_AUTO_WHITE_BALANCE %d\n", + ctrl->value); + break; + case V4L2_CID_FOCUS_ABSOLUTE: + ctrl->value = cam_mod->af_config.abs_pos; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_FOCUS_ABSOLUTE %d\n", + ctrl->value); + break; + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + /* TBD */ + /* fallthrough */ + default: + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "failed, unknown ctrl %d\n", ctrl->id); + return -EINVAL; + } + + return 0; +} + +static int flash_light_ctrl( + struct v4l2_subdev *sd, + struct ov_camera_module *cam_mod, + int value) +{ + return 0; +} + +/* ======================================================================== */ + +int ov_camera_module_s_ext_ctrls( + struct v4l2_subdev *sd, + struct v4l2_ext_controls *ctrls) +{ + int i; + int ctrl_cnt = 0; + struct ov_camera_module *cam_mod = to_ov_camera_module(sd); + int ret = 0; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + if (ctrls->count == 0) + return -EINVAL; + + for (i = 0; i < ctrls->count; i++) { + struct v4l2_ext_control *ctrl; + u32 ctrl_updt = 0; + + ctrl = &ctrls->controls[i]; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + ctrl_updt = OV_CAMERA_MODULE_CTRL_UPDT_GAIN; + cam_mod->exp_config.gain = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_GAIN %d\n", + ctrl->value); + break; + case RK_V4L2_CID_GAIN_PERCENT: + ctrl_updt = OV_CAMERA_MODULE_CTRL_UPDT_GAIN; + cam_mod->exp_config.gain_percent = ctrl->value; + break; + case V4L2_CID_FLASH_LED_MODE: + ret = flash_light_ctrl(sd, cam_mod, ctrl->value); + if (ret == 0) { + cam_mod->exp_config.flash_mode = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_FLASH_LED_MODE %d\n", + ctrl->value); + } + break; + case V4L2_CID_EXPOSURE: + ctrl_updt = OV_CAMERA_MODULE_CTRL_UPDT_EXP_TIME; + cam_mod->exp_config.exp_time = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_EXPOSURE %d\n", + ctrl->value); + break; + case V4L2_CID_WHITE_BALANCE_TEMPERATURE: + ctrl_updt = OV_CAMERA_MODULE_CTRL_UPDT_WB_TEMPERATURE; + cam_mod->wb_config.temperature = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_WHITE_BALANCE_TEMPERATURE %d\n", + ctrl->value); + break; + case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: + ctrl_updt = OV_CAMERA_MODULE_CTRL_UPDT_PRESET_WB; + cam_mod->wb_config.preset_id = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE %d\n", + ctrl->value); + break; + case V4L2_CID_AUTOGAIN: + ctrl_updt = OV_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN; + cam_mod->exp_config.auto_gain = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_AUTOGAIN %d\n", + ctrl->value); + break; + case V4L2_CID_EXPOSURE_AUTO: + ctrl_updt = OV_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP; + cam_mod->exp_config.auto_exp = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_EXPOSURE_AUTO %d\n", + ctrl->value); + break; + case V4L2_CID_AUTO_WHITE_BALANCE: + ctrl_updt = OV_CAMERA_MODULE_CTRL_UPDT_AUTO_WB; + cam_mod->wb_config.auto_wb = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_AUTO_WHITE_BALANCE %d\n", + ctrl->value); + break; + case RK_V4L2_CID_AUTO_FPS: + cam_mod->auto_adjust_fps = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "RK_V4L2_CID_AUTO_FPS %d\n", + ctrl->value); + break; + case V4L2_CID_FOCUS_ABSOLUTE: + { + struct v4l2_subdev *af_ctrl; + + af_ctrl = pltfrm_camera_module_get_af_ctrl(sd); + if (!IS_ERR_OR_NULL(af_ctrl)) { + struct v4l2_control single_ctrl; + + single_ctrl.id = + V4L2_CID_FOCUS_ABSOLUTE; + single_ctrl.value = ctrl->value; + ret = v4l2_subdev_call(af_ctrl, + core, s_ctrl, &single_ctrl); + return ret; + } + } + ctrl_updt = + OV_CAMERA_MODULE_CTRL_UPDT_FOCUS_ABSOLUTE; + cam_mod->af_config.abs_pos = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_FOCUS_ABSOLUTE %d\n", + ctrl->value); + break; + case V4L2_CID_HFLIP: + if (ctrl->value) + cam_mod->hflip = true; + else + cam_mod->hflip = false; + break; + case V4L2_CID_VFLIP: + if (ctrl->value) + cam_mod->vflip = true; + else + cam_mod->vflip = false; + break; + default: + pltfrm_camera_module_pr_warn(&cam_mod->sd, + "ignoring unknown ctrl 0x%x\n", ctrl->id); + break; + } + + if (cam_mod->state != OV_CAMERA_MODULE_SW_STANDBY && + cam_mod->state != OV_CAMERA_MODULE_STREAMING) + cam_mod->ctrl_updt |= ctrl_updt; + else if (ctrl_updt) + ctrl_cnt++; + } + + /* if camera module is already streaming, write through */ + if (ctrl_cnt && + (cam_mod->state == OV_CAMERA_MODULE_STREAMING || + cam_mod->state == OV_CAMERA_MODULE_SW_STANDBY)) { + struct ov_camera_module_ext_ctrls ov_ctrls; + + ov_ctrls.ctrls = + (struct ov_camera_module_ext_ctrl *) + kmalloc(ctrl_cnt * sizeof(struct ov_camera_module_ext_ctrl), + GFP_KERNEL); + + if (ov_ctrls.ctrls) { + for (i = 0; i < ctrl_cnt; i++) { + ov_ctrls.ctrls[i].id = ctrls->controls[i].id; + ov_ctrls.ctrls[i].value = + ctrls->controls[i].value; + } + + ov_ctrls.count = ctrl_cnt; + + ret = cam_mod->custom.s_ext_ctrls(cam_mod, &ov_ctrls); + + kfree(ov_ctrls.ctrls); + } else { + ret = -ENOMEM; + } + if (IS_ERR_VALUE(ret)) + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "failed with error %d\n", ret); + } + + return ret; +} + +/* ======================================================================== */ + +int ov_camera_module_s_ctrl( + struct v4l2_subdev *sd, + struct v4l2_control *ctrl) +{ + struct ov_camera_module *cam_mod = to_ov_camera_module(sd); + struct v4l2_ext_control ext_ctrl[1]; + struct v4l2_ext_controls ext_ctrls; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "0x%x 0x%x\n", ctrl->id, ctrl->value); + + ext_ctrl[0].id = ctrl->id; + ext_ctrl[0].value = ctrl->value; + + ext_ctrls.count = 1; + ext_ctrls.controls = ext_ctrl; + + return ov_camera_module_s_ext_ctrls(sd, &ext_ctrls); +} + +/* ======================================================================== */ + +long ov_camera_module_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, + void *arg) +{ + struct ov_camera_module *cam_mod = to_ov_camera_module(sd); + int ret; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "cmd: 0x%x\n", cmd); + + if (cmd == RK_VIDIOC_SENSOR_MODE_DATA) { + struct ov_camera_module_timings ov_timings; + struct isp_supplemental_sensor_mode_data *timings = + (struct isp_supplemental_sensor_mode_data *)arg; + + if (cam_mod->custom.g_timings) + ret = cam_mod->custom.g_timings(cam_mod, &ov_timings); + else + ret = -EPERM; + + if (IS_ERR_VALUE(ret)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; + } + + timings->sensor_output_width = ov_timings.sensor_output_width; + timings->sensor_output_height = ov_timings.sensor_output_height; + timings->crop_horizontal_start = + ov_timings.crop_horizontal_start; + timings->crop_vertical_start = ov_timings.crop_vertical_start; + timings->crop_horizontal_end = ov_timings.crop_horizontal_end; + timings->crop_vertical_end = ov_timings.crop_vertical_end; + timings->line_length_pck = ov_timings.line_length_pck; + timings->frame_length_lines = ov_timings.frame_length_lines; + timings->vt_pix_clk_freq_hz = ov_timings.vt_pix_clk_freq_hz; + timings->binning_factor_x = ov_timings.binning_factor_x; + timings->binning_factor_y = ov_timings.binning_factor_y; + timings->coarse_integration_time_max_margin = + ov_timings.coarse_integration_time_max_margin; + timings->coarse_integration_time_min = + ov_timings.coarse_integration_time_min; + timings->fine_integration_time_max_margin = + ov_timings.fine_integration_time_max_margin; + timings->fine_integration_time_min = + ov_timings.fine_integration_time_min; + + if (cam_mod->custom.g_exposure_valid_frame) + timings->exposure_valid_frame = + cam_mod->custom.g_exposure_valid_frame(cam_mod); + if (cam_mod->exp_config.exp_time) + timings->exp_time = cam_mod->exp_config.exp_time; + else + timings->exp_time = ov_timings.exp_time; + if (cam_mod->exp_config.gain) + timings->gain = cam_mod->exp_config.gain; + else + timings->gain = ov_timings.gain; + return ret; + } else if (cmd == PLTFRM_CIFCAM_G_ITF_CFG) { + struct pltfrm_cam_itf *itf_cfg = (struct pltfrm_cam_itf *)arg; + struct ov_camera_module_config *config; + + if (cam_mod->custom.num_configs <= 0) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "cam_mod->custom.num_configs is NULL, Get interface config failed!\n"); + return -EINVAL; + } + + if (IS_ERR_OR_NULL(cam_mod->active_config)) + config = &cam_mod->custom.configs[0]; + else + config = cam_mod->active_config; + + *itf_cfg = config->itf_cfg; + + pltfrm_camera_module_ioctl(sd, PLTFRM_CIFCAM_G_ITF_CFG, arg); + return 0; + } else if (cmd == PLTFRM_CIFCAM_ATTACH) { + ov_camera_module_init(cam_mod, &cam_mod->custom); + pltfrm_camera_module_ioctl(sd, cmd, arg); + return ov_camera_module_attach(cam_mod); + } + + ret = pltfrm_camera_module_ioctl(sd, cmd, arg); + return ret; +} + +/* ======================================================================== */ + +int ov_camera_module_get_flip_mirror( + struct ov_camera_module *cam_mod) +{ + return pltfrm_camera_module_get_flip_mirror(&cam_mod->sd); +} + +/* ======================================================================== */ + +int ov_camera_module_enum_frameintervals( + struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct ov_camera_module *cam_mod = to_ov_camera_module(sd); + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "%d\n", fie->index); + + if (fie->index >= cam_mod->custom.num_configs) + return -EINVAL; + fie->code = + cam_mod->custom.configs[fie->index].frm_fmt.code; + fie->width = + cam_mod->custom.configs[fie->index].frm_fmt.width; + fie->height = + cam_mod->custom.configs[fie->index].frm_fmt.height; + fie->interval.numerator = cam_mod->custom. + configs[fie->index].frm_intrvl.interval.numerator; + fie->interval.denominator = cam_mod->custom. + configs[fie->index].frm_intrvl.interval.denominator; + return 0; +} + +/* ======================================================================== */ + +int ov_camera_module_write_reglist( + struct ov_camera_module *cam_mod, + const struct ov_camera_module_reg reglist[], + int len) +{ + return pltfrm_camera_module_write_reglist(&cam_mod->sd, reglist, len); +} + +/* ======================================================================== */ + +int ov_camera_module_write_reg( + struct ov_camera_module *cam_mod, + u16 reg, + u8 val) +{ + return pltfrm_camera_module_write_reg(&cam_mod->sd, reg, val); +} + +/* ======================================================================== */ + +int ov_camera_module_read_reg( + struct ov_camera_module *cam_mod, + u16 data_length, + u16 reg, + u32 *val) +{ + return pltfrm_camera_module_read_reg(&cam_mod->sd, + data_length, reg, val); +} + +/* ======================================================================== */ + +int ov_camera_module_read_reg_table( + struct ov_camera_module *cam_mod, + u16 reg, + u32 *val) +{ + int i; + + if (cam_mod->state == OV_CAMERA_MODULE_STREAMING) + return pltfrm_camera_module_read_reg(&cam_mod->sd, + 1, reg, val); + + if (!IS_ERR_OR_NULL(cam_mod->active_config)) { + for ( + i = cam_mod->active_config->reg_table_num_entries - 1; + i > 0; + i--) { + if (cam_mod->active_config->reg_table[i].reg == reg) { + *val = cam_mod->active_config->reg_table[i].val; + return 0; + } + } + } + + if (cam_mod->state == OV_CAMERA_MODULE_SW_STANDBY) + return pltfrm_camera_module_read_reg(&cam_mod->sd, + 1, reg, val); + + return -EFAULT; +} + +int ov_camera_module_init(struct ov_camera_module *cam_mod, + struct ov_camera_module_custom_config *custom) +{ + int ret = 0; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + + ov_camera_module_reset(cam_mod); + + if (IS_ERR_OR_NULL(custom->start_streaming) || + IS_ERR_OR_NULL(custom->stop_streaming) || + IS_ERR_OR_NULL(custom->s_ctrl) || + IS_ERR_OR_NULL(custom->g_ctrl)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "mandatory callback function is missing\n"); + ret = -EINVAL; + goto err; + } + + ret = pltfrm_camera_module_init(&cam_mod->sd, &cam_mod->pltfm_data); + if (IS_ERR_VALUE(ret)) + goto err; + + ret = pltfrm_camera_module_set_pin_state(&cam_mod->sd, + PLTFRM_CAMERA_MODULE_PIN_PD, + PLTFRM_CAMERA_MODULE_PIN_STATE_INACTIVE); + ret = pltfrm_camera_module_set_pin_state(&cam_mod->sd, + PLTFRM_CAMERA_MODULE_PIN_RESET, + PLTFRM_CAMERA_MODULE_PIN_STATE_ACTIVE); + if (IS_ERR_VALUE(ret)) { + ov_camera_module_release(cam_mod); + goto err; + } + + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; +} + +void ov_camera_module_release(struct ov_camera_module *cam_mod) +{ + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + + if (cam_mod->otp_work.wq) { + flush_workqueue(cam_mod->otp_work.wq); + destroy_workqueue(cam_mod->otp_work.wq); + cam_mod->otp_work.wq = NULL; + } + + cam_mod->custom.configs = NULL; + + pltfrm_camera_module_release(&cam_mod->sd); + v4l2_device_unregister_subdev(&cam_mod->sd); +} diff --git a/drivers/media/i2c/soc_camera/rockchip/ov_camera_module.h b/drivers/media/i2c/soc_camera/rockchip/ov_camera_module.h new file mode 100644 index 000000000000..25af7f037c98 --- /dev/null +++ b/drivers/media/i2c/soc_camera/rockchip/ov_camera_module.h @@ -0,0 +1,285 @@ +/* + * ov_camera_module.h + * + * Generic omnivision sensor driver + * + * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd. + * + * Copyright (C) 2012-2014 Intel Mobile Communications GmbH + * + * Copyright (C) 2008 Texas Instruments. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + */ + +#ifndef OV_CAMERA_MODULE_H +#define OV_CAMERA_MODULE_H +#include +#include +#include + +/* + * TODO: references to v4l2 should be reomved from here and go into a + * platform dependent wrapper + */ + +#define OV_CAMERA_MODULE_REG_TYPE_DATA PLTFRM_CAMERA_MODULE_REG_TYPE_DATA +#define OV_CAMERA_MODULE_REG_TYPE_TIMEOUT PLTFRM_CAMERA_MODULE_REG_TYPE_TIMEOUT +#define ov_camera_module_csi_config +#define ov_camera_module_reg pltfrm_camera_module_reg +#define OV_FLIP_BIT_MASK 0x2 +#define OV_MIRROR_BIT_MASK 0x1 + +#define OV_CAMERA_MODULE_CTRL_UPDT_GAIN 0x01 +#define OV_CAMERA_MODULE_CTRL_UPDT_EXP_TIME 0x02 +#define OV_CAMERA_MODULE_CTRL_UPDT_WB_TEMPERATURE 0x04 +#define OV_CAMERA_MODULE_CTRL_UPDT_AUTO_WB 0x08 +#define OV_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN 0x10 +#define OV_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP 0x20 +#define OV_CAMERA_MODULE_CTRL_UPDT_FOCUS_ABSOLUTE 0x40 +#define OV_CAMERA_MODULE_CTRL_UPDT_PRESET_WB 0x80 + +enum ov_camera_module_state { + OV_CAMERA_MODULE_POWER_OFF = 0, + OV_CAMERA_MODULE_HW_STANDBY = 1, + OV_CAMERA_MODULE_SW_STANDBY = 2, + OV_CAMERA_MODULE_STREAMING = 3 +}; + +struct ov_camera_module; + +struct ov_camera_module_timings { + /* public */ + u32 coarse_integration_time_min; + u32 coarse_integration_time_max_margin; + u32 fine_integration_time_min; + u32 fine_integration_time_max_margin; + u32 frame_length_lines; + u32 line_length_pck; + u32 vt_pix_clk_freq_hz; + u32 sensor_output_width; + u32 sensor_output_height; + u32 crop_horizontal_start; /* Sensor crop start cord. (x0,y0) */ + u32 crop_vertical_start; + u32 crop_horizontal_end; /* Sensor crop end cord. (x1,y1) */ + u32 crop_vertical_end; + u8 binning_factor_x; + u8 binning_factor_y; + u32 exp_time; + u32 gain; +}; + +struct ov_camera_module_config { + const char *name; + struct v4l2_mbus_framefmt frm_fmt; + struct v4l2_subdev_frame_interval frm_intrvl; + bool auto_exp_enabled; + bool auto_gain_enabled; + bool auto_wb_enabled; + struct ov_camera_module_reg *reg_table; + u32 reg_table_num_entries; + struct ov_camera_module_reg *reg_diff_table; + u32 reg_diff_table_num_entries; + u32 v_blanking_time_us; + u32 line_length_pck; + u32 frame_length_lines; + struct ov_camera_module_timings timings; + bool soft_reset; + bool ignore_measurement_check; + + struct pltfrm_cam_itf itf_cfg; +}; + +struct ov_camera_module_exp_config { + s32 exp_time; + bool auto_exp; + u16 gain; + u16 gain_percent; + bool auto_gain; + enum v4l2_flash_led_mode flash_mode; +}; + +struct ov_camera_module_wb_config { + u32 temperature; + u32 preset_id; + bool auto_wb; +}; + +struct ov_camera_module_af_config { + u32 abs_pos; + u32 rel_pos; +}; + +struct ov_camera_module_ext_ctrl { + /* public */ + u32 id; + u32 value; + __u32 reserved2[1]; +}; + +struct ov_camera_module_ext_ctrls { + /* public */ + u32 count; + struct ov_camera_module_ext_ctrl *ctrls; +}; + +/* + * start_streaming: (mandatory) will be called when sensor should be + * put into streaming mode right after the base config has been + * written to the sensor. After a successful call of this function + * the sensor should start delivering frame data. + * + * stop_streaming: (mandatory) will be called when sensor should stop + * delivering data. After a successful call of this function the + * sensor should not deliver any more frame data. + * + * check_camera_id: (optional) will be called when the sensor is + * powered on. If provided should check the sensor ID/version + * required by the custom driver. Register access should be + * possible when this function is invoked. + * + * s_ctrl: (mandatory) will be called at the successful end of + * ov_camera_module_s_ctrl with the ctrl_id as argument. + * + * priv: (optional) for private data used by the custom driver. + */ +struct ov_camera_module_custom_config { + int (*start_streaming)(struct ov_camera_module *cam_mod); + int (*stop_streaming)(struct ov_camera_module *cam_mod); + int (*check_camera_id)(struct ov_camera_module *cam_mod); + int (*s_ctrl)(struct ov_camera_module *cam_mod, u32 ctrl_id); + int (*g_ctrl)(struct ov_camera_module *cam_mod, u32 ctrl_id); + int (*g_timings)(struct ov_camera_module *cam_mod, + struct ov_camera_module_timings *timings); + int (*g_exposure_valid_frame)(struct ov_camera_module *cam_mod); + int (*s_ext_ctrls)(struct ov_camera_module *cam_mod, + struct ov_camera_module_ext_ctrls *ctrls); + int (*set_flip)( + struct ov_camera_module *cam_mod, + struct pltfrm_camera_module_reg reglist[], + int len); + int (*init_common)(struct ov_camera_module *cam_mod); + int (*read_otp)(struct ov_camera_module *cam_mod); + struct ov_camera_module_config *configs; + u32 num_configs; + u32 power_up_delays_ms[3]; + void *priv; +}; + +struct ov_camera_module_otp_work { + struct work_struct work; + struct workqueue_struct *wq; + void *cam_mod; +}; + +struct ov_camera_module { + /* public */ + struct v4l2_subdev sd; + struct v4l2_mbus_framefmt frm_fmt; + struct v4l2_subdev_frame_interval frm_intrvl; + struct ov_camera_module_exp_config exp_config; + struct ov_camera_module_wb_config wb_config; + struct ov_camera_module_af_config af_config; + struct ov_camera_module_custom_config custom; + enum ov_camera_module_state state; + enum ov_camera_module_state state_before_suspend; + struct ov_camera_module_config *active_config; + struct ov_camera_module_otp_work otp_work; + u32 ctrl_updt; + u32 vts_cur; + u32 vts_min; + bool auto_adjust_fps; + bool update_config; + bool frm_fmt_valid; + bool frm_intrvl_valid; + bool hflip; + bool vflip; + u32 rotation; + void *pltfm_data; + bool inited; +}; + +#define ov_camera_module_pr_info(cam_mod, fmt, arg...) \ + pltfrm_camera_module_pr_info(&(cam_mod)->sd, fmt, ## arg) +#define ov_camera_module_pr_debug(cam_mod, fmt, arg...) \ + pltfrm_camera_module_pr_debug(&(cam_mod)->sd, fmt, ## arg) +#define ov_camera_module_pr_warn(cam_mod, fmt, arg...) \ + pltfrm_camera_module_pr_warn(&(cam_mod)->sd, fmt, ## arg) +#define ov_camera_module_pr_err(cam_mod, fmt, arg...) \ + pltfrm_camera_module_pr_err(&(cam_mod)->sd, fmt, ## arg) + +int ov_camera_module_write_reglist( + struct ov_camera_module *cam_mod, + const struct ov_camera_module_reg reglist[], + int len); + +int ov_camera_module_write_reg( + struct ov_camera_module *cam_mod, + u16 reg, + u8 val); + +int ov_camera_module_read_reg( + struct ov_camera_module *cam_mod, + u16 data_length, + u16 reg, + u32 *val); + +int ov_camera_module_read_reg_table( + struct ov_camera_module *cam_mod, + u16 reg, + u32 *val); + +int ov_camera_module_s_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format); + +int ov_camera_module_g_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format); + +int ov_camera_module_s_frame_interval( + struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *interval); + +int ov_camera_module_s_stream( + struct v4l2_subdev *sd, + int enable); + +int ov_camera_module_s_power( + struct v4l2_subdev *sd, + int on); + +int ov_camera_module_g_ctrl( + struct v4l2_subdev *sd, + struct v4l2_control *ctrl); + +int ov_camera_module_s_ctrl( + struct v4l2_subdev *sd, + struct v4l2_control *ctrl); + +int ov_camera_module_s_ext_ctrls( + struct v4l2_subdev *sd, + struct v4l2_ext_controls *ctrls); + +int ov_camera_module_enum_frameintervals( + struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie); + +int ov_camera_module_init( + struct ov_camera_module *cam_mod, + struct ov_camera_module_custom_config *custom); + +void ov_camera_module_release( + struct ov_camera_module *cam_mod); + +long ov_camera_module_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, + void *arg); + +int ov_camera_module_get_flip_mirror( + struct ov_camera_module *cam_mod); +#endif diff --git a/drivers/media/i2c/soc_camera/rockchip/rk_camera_module.c b/drivers/media/i2c/soc_camera/rockchip/rk_camera_module.c new file mode 100644 index 000000000000..80f3330c7046 --- /dev/null +++ b/drivers/media/i2c/soc_camera/rockchip/rk_camera_module.c @@ -0,0 +1,1630 @@ +/* + * rk_camera_module.c + * (Based on Intel driver for sofiaxxx) + * + * Copyright (C) 2015 Intel Mobile Communications GmbH + * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef CONFIG_OF +#error "this file requires device tree support" +#endif + +#ifndef SOFIA_3G_CAMERA_MODULE_H +#define SOFIA_3G_CAMERA_MODULE_H + +#include +#include +#ifdef CONFIG_PLATFORM_DEVICE_PM +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define OF_OV_GPIO_PD "rockchip,pd-gpio" +#define OF_OV_GPIO_PWR "rockchip,pwr-gpio" +#define OF_OV_GPIO_FLASH "rockchip,flash-gpio" +#define OF_OV_GPIO_TORCH "rockchip,torch-gpio" +#define OF_OV_GPIO_RESET "rockchip,rst-gpio" + +#define OF_CAMERA_MODULE_NAME "rockchip,camera-module-name" +#define OF_CAMERA_MODULE_LEN_NAME "rockchip,camera-module-len-name" +#define OF_CAMERA_MODULE_FOV_H "rockchip,camera-module-fov-h" +#define OF_CAMERA_MODULE_FOV_V "rockchip,camera-module-fov-v" +#define OF_CAMERA_MODULE_ORIENTATION "rockchip,camera-module-orientation" +#define OF_CAMERA_MODULE_FOCAL_LENGTH "rockchip,camera-module-focal-length" +#define OF_CAMERA_MODULE_FOCUS_DISTANCE "rockchip,camera-module-focus-distance" +#define OF_CAMERA_MODULE_IQ_MIRROR "rockchip,camera-module-iq-mirror" +#define OF_CAMERA_MODULE_IQ_FLIP "rockchip,camera-module-iq-flip" +#define OF_CAMERA_MODULE_FLIP "rockchip,camera-module-flip" +#define OF_CAMERA_MODULE_MIRROR "rockchip,camera-module-mirror" +#define OF_CAMERA_FLASH_SUPPORT "rockchip,camera-module-flash-support" +#define OF_CAMERA_FLASH_EXP_PERCENT "rockchip,camera-module-flash-exp-percent" +#define OF_CAMERA_FLASH_TURN_ON_TIME "rockchip,camera-module-flash-turn-on-time" +#define OF_CAMERA_FLASH_ON_TIMEOUT "rockchip,camera-module-flash-on-timeout" +#define OF_CAMERA_MODULE_DEFRECT0 "rockchip,camera-module-defrect0" +#define OF_CAMERA_MODULE_DEFRECT1 "rockchip,camera-module-defrect1" +#define OF_CAMERA_MODULE_DEFRECT2 "rockchip,camera-module-defrect2" +#define OF_CAMERA_MODULE_DEFRECT3 "rockchip,camera-module-defrect3" +#define OF_CAMERA_MODULE_MIPI_DPHY_INDEX "rockchip,camera-module-mipi-dphy-index" + +#define OF_CAMERA_MODULE_REGULATORS "rockchip,camera-module-regulator-names" +#define OF_CAMERA_MODULE_REGULATOR_VOLTAGES "rockchip,camera-module-regulator-voltages" +#define OF_CAMERA_MODULE_MCLK_NAME "rockchip,camera-module-mclk-name" +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" + +const char *PLTFRM_CAMERA_MODULE_PIN_PD = OF_OV_GPIO_PD; +const char *PLTFRM_CAMERA_MODULE_PIN_PWR = OF_OV_GPIO_PWR; +const char *PLTFRM_CAMERA_MODULE_PIN_FLASH = OF_OV_GPIO_FLASH; +const char *PLTFRM_CAMERA_MODULE_PIN_TORCH = OF_OV_GPIO_TORCH; +const char *PLTFRM_CAMERA_MODULE_PIN_RESET = OF_OV_GPIO_RESET; + +#define I2C_M_WR 0 +#define I2C_MSG_MAX 300 +#define I2C_DATA_MAX (I2C_MSG_MAX * 3) + +struct pltfrm_camera_module_gpio { + int pltfrm_gpio; + const char *label; + enum of_gpio_flags active_low; +}; + +struct pltfrm_camera_module_regulator { + struct regulator *regulator; + unsigned int min_uV; + unsigned int max_uV; +}; + +struct pltfrm_camera_module_regulators { + unsigned int cnt; + struct pltfrm_camera_module_regulator *regulator; +}; + +struct pltfrm_camera_module_fl { + const char *flash_driver_name; + /* flash ,torch */ + char fl_init_status; + struct pltfrm_camera_module_gpio *fl_flash; + struct pltfrm_camera_module_gpio *fl_torch; + struct v4l2_subdev *flsh_ctrl; +}; + +struct pltfrm_camera_module_info_s { + const char *module_name; + const char *len_name; + const char *fov_h; + const char *fov_v; + const char *focal_length; + const char *focus_distance; + int facing; + int orientation; + bool iq_mirror; + bool iq_flip; + int flip; + int mirror; + int flash_support; + int flash_exp_percent; + int flash_turn_on_time; + int flash_on_timeout; +}; + +struct pltfrm_camera_module_mipi { + unsigned int dphy_index; +}; + +struct pltfrm_camera_module_itf { + union { + struct pltfrm_camera_module_mipi mipi; + } itf; +}; + +struct pltfrm_camera_module_data { + struct pltfrm_camera_module_gpio gpios[6]; + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; + struct v4l2_subdev *af_ctrl; + /* move to struct pltfrm_camera_module_fl */ + /* const char *flash_driver_name; */ + struct pltfrm_camera_module_fl fl_ctrl; + + struct pltfrm_camera_module_info_s info; + struct pltfrm_camera_module_itf itf; + struct pltfrm_cam_defrect defrects[4]; + struct clk *mclk; + struct pltfrm_soc_cfg *soc_cfg; + struct pltfrm_camera_module_regulators regulators; + + void *priv; +}; + +/* ======================================================================== */ + +static int pltfrm_camera_module_set_pinctrl_state( + struct v4l2_subdev *sd, + struct pinctrl_state *state) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct pltfrm_camera_module_data *pdata = + dev_get_platdata(&client->dev); + int ret = 0; + + if (!IS_ERR_OR_NULL(state)) { + ret = pinctrl_select_state(pdata->pinctrl, state); + if (IS_ERR_VALUE(ret)) + pltfrm_camera_module_pr_debug(sd, + "could not set pins\n"); + } + + return ret; +} + +static int pltfrm_camera_module_init_gpio( + struct v4l2_subdev *sd) +{ + int ret = 0; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct pltfrm_camera_module_data *pdata = + dev_get_platdata(&client->dev); + int i = 0; + + ret = pltfrm_camera_module_set_pinctrl_state(sd, pdata->pins_default); + if (IS_ERR_VALUE(ret)) + goto err; + + for (i = 0; i < ARRAY_SIZE(pdata->gpios); i++) { + if (gpio_is_valid(pdata->gpios[i].pltfrm_gpio)) { + if (pdata->gpios[i].label == + PLTFRM_CAMERA_MODULE_PIN_FLASH || + pdata->gpios[i].label == + PLTFRM_CAMERA_MODULE_PIN_TORCH) { + if (pdata->fl_ctrl.fl_init_status && + pdata->fl_ctrl.fl_flash->pltfrm_gpio == + pdata->fl_ctrl.fl_torch->pltfrm_gpio) { + pltfrm_camera_module_pr_info( + sd, + "fl gpio has been inited, continue!\n"); + continue; + } + pdata->fl_ctrl.fl_init_status = 1; + } + + pltfrm_camera_module_pr_debug( + sd, + "requesting GPIO #%d ('%s')\n", + pdata->gpios[i].pltfrm_gpio, + pdata->gpios[i].label); + ret = gpio_request_one( + pdata->gpios[i].pltfrm_gpio, + GPIOF_DIR_OUT, + pdata->gpios[i].label); + if (IS_ERR_VALUE(ret)) { + if ((pdata->gpios[i].label == + PLTFRM_CAMERA_MODULE_PIN_RESET) || + (pdata->gpios[i].label == + PLTFRM_CAMERA_MODULE_PIN_PWR)) { + pltfrm_camera_module_pr_warn(sd, + "GPIO #%d ('%s') may be reused!\n", + pdata->gpios[i].pltfrm_gpio, + pdata->gpios[i].label); + } else { + pltfrm_camera_module_pr_err(sd, + "failed to request GPIO #%d ('%s')\n", + pdata->gpios[i].pltfrm_gpio, + pdata->gpios[i].label); + goto err; + } + } + + if (pdata->gpios[i].label == + PLTFRM_CAMERA_MODULE_PIN_PD) + ret = pltfrm_camera_module_set_pin_state(sd, + pdata->gpios[i].label, + PLTFRM_CAMERA_MODULE_PIN_STATE_INACTIVE); + else if (pdata->gpios[i].label == + PLTFRM_CAMERA_MODULE_PIN_RESET) + ret = pltfrm_camera_module_set_pin_state(sd, + pdata->gpios[i].label, + PLTFRM_CAMERA_MODULE_PIN_STATE_ACTIVE); + else + ret = pltfrm_camera_module_set_pin_state(sd, + pdata->gpios[i].label, + PLTFRM_CAMERA_MODULE_PIN_STATE_INACTIVE + ); + } + } + return 0; +err: + pltfrm_camera_module_pr_err(sd, "failed with error %d\n", ret); + for (; i < ARRAY_SIZE(pdata->gpios); i++) + pdata->gpios[i].pltfrm_gpio = -1; + + return ret; +} + +static struct pltfrm_camera_module_data *pltfrm_camera_module_get_data( + struct v4l2_subdev *sd) +{ + int ret = 0; + int elem_size, elem_index; + const char *str = ""; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct device_node *np = of_node_get(client->dev.of_node); + struct device_node *af_ctrl_node = NULL; + struct i2c_client *af_ctrl_client = NULL; + struct device_node *fl_ctrl_node = NULL; + struct i2c_client *fl_ctrl_client = NULL; + struct pltfrm_camera_module_data *pdata = NULL; + struct property *prop; + + pltfrm_camera_module_pr_debug(sd, "\n"); + + pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); + if (IS_ERR_OR_NULL(pdata)) { + ret = -ENOMEM; + goto err; + } + + client->dev.platform_data = pdata; + + ret = of_property_read_string(np, OF_CAMERA_MODULE_MCLK_NAME, &str); + if (ret) { + pltfrm_camera_module_pr_err(sd, + "cannot not get camera-module-mclk-name property of node %s\n", + np->name); + ret = -ENODEV; + goto err; + } + + pdata->mclk = devm_clk_get(&client->dev, str); + if (IS_ERR_OR_NULL(pdata->mclk)) { + pltfrm_camera_module_pr_err(sd, + "cannot not get %s property of node %s\n", + str, np->name); + ret = -ENODEV; + goto err; + } + + ret = of_property_read_string(np, + "rockchip,camera-module-facing", &str); + if (ret) { + pltfrm_camera_module_pr_err(sd, + "cannot not get camera-module-facing property of node %s\n", + np->name); + } else { + pltfrm_camera_module_pr_debug(sd, + "camera module camera-module-facing driver is %s\n", + str); + } + pdata->info.facing = -1; + if (!strcmp(str, "back")) + pdata->info.facing = 0; + else if (!strcmp(str, "front")) + pdata->info.facing = 1; + + ret = of_property_read_string(np, "rockchip,flash-driver", + &pdata->fl_ctrl.flash_driver_name); + if (ret) { + pltfrm_camera_module_pr_debug(sd, + "cannot not get flash-driver property of node %s\n", + np->name); + pdata->fl_ctrl.flash_driver_name = "0"; + } else { + pltfrm_camera_module_pr_info(sd, + "camera module flash driver is %s\n", + (char *)pdata->fl_ctrl.flash_driver_name); + + /*parse flash node info*/ + fl_ctrl_node = of_parse_phandle(np, "rockchip,fl-ctrl", 0); + if (!IS_ERR_OR_NULL(fl_ctrl_node)) { + fl_ctrl_client = + of_find_i2c_device_by_node(fl_ctrl_node); + of_node_put(fl_ctrl_node); + if (IS_ERR_OR_NULL(fl_ctrl_client)) { + pltfrm_camera_module_pr_err(sd, + "cannot not get node\n"); + ret = -EFAULT; + goto err; + } + pdata->fl_ctrl.flsh_ctrl = + i2c_get_clientdata(fl_ctrl_client); + if (IS_ERR_OR_NULL(pdata->fl_ctrl.flsh_ctrl)) { + pltfrm_camera_module_pr_warn(sd, + "cannot not get camera i2c client, maybe not yet created, deferring device probing...\n"); + ret = -EPROBE_DEFER; + goto err; + } + pltfrm_camera_module_pr_info(sd, + "camera module has flash driver %s\n", + pltfrm_dev_string(pdata->fl_ctrl.flsh_ctrl)); + } + } + + af_ctrl_node = of_parse_phandle(np, "rockchip,af-ctrl", 0); + if (!IS_ERR_OR_NULL(af_ctrl_node)) { + af_ctrl_client = of_find_i2c_device_by_node(af_ctrl_node); + of_node_put(af_ctrl_node); + if (IS_ERR_OR_NULL(af_ctrl_client)) { + pltfrm_camera_module_pr_err(sd, + "cannot not get node\n"); + ret = -EFAULT; + goto err; + } + pdata->af_ctrl = i2c_get_clientdata(af_ctrl_client); + if (IS_ERR_OR_NULL(pdata->af_ctrl)) { + pltfrm_camera_module_pr_warn(sd, + "cannot not get camera i2c client, maybe not yet created, deferring device probing...\n"); + ret = -EPROBE_DEFER; + goto err; + } + pltfrm_camera_module_pr_info(sd, + "camera module has auto focus controller %s\n", + pltfrm_dev_string(pdata->af_ctrl)); + } + + pdata->pinctrl = devm_pinctrl_get(&client->dev); + if (!IS_ERR(pdata->pinctrl)) { + pdata->pins_default = pinctrl_lookup_state( + pdata->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(pdata->pins_default)) + pltfrm_camera_module_pr_warn(sd, + "could not get default pinstate\n"); + + pdata->pins_sleep = pinctrl_lookup_state( + pdata->pinctrl, OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(pdata->pins_sleep)) + pltfrm_camera_module_pr_warn(sd, + "could not get sleep pinstate\n"); + } + + elem_size = of_property_count_elems_of_size( + np, + OF_CAMERA_MODULE_REGULATOR_VOLTAGES, + sizeof(u32)); + prop = of_find_property( + np, + OF_CAMERA_MODULE_REGULATORS, + NULL); + if (!IS_ERR_VALUE(elem_size) && + !IS_ERR_OR_NULL(prop)) { + struct pltfrm_camera_module_regulator *regulator; + + pdata->regulators.regulator = devm_kzalloc(&client->dev, + elem_size * + sizeof(struct pltfrm_camera_module_regulator), + GFP_KERNEL); + if (!pdata->regulators.regulator) { + pltfrm_camera_module_pr_err(sd, + "could not malloc pltfrm_camera_module_regulator\n"); + goto err; + } + pdata->regulators.cnt = elem_size; + str = NULL; + elem_index = 0; + regulator = pdata->regulators.regulator; + do { + str = of_prop_next_string(prop, str); + if (!str) { + pltfrm_camera_module_pr_err(sd, + "%s is not match %s in dts\n", + OF_CAMERA_MODULE_REGULATORS, + OF_CAMERA_MODULE_REGULATOR_VOLTAGES); + break; + } + regulator->regulator = + devm_regulator_get_optional(&client->dev, str); + if (IS_ERR(regulator->regulator)) + pltfrm_camera_module_pr_err(sd, + "devm_regulator_get %s failed\n", + str); + of_property_read_u32_index( + np, + OF_CAMERA_MODULE_REGULATOR_VOLTAGES, + elem_index++, + ®ulator->min_uV); + regulator->max_uV = regulator->min_uV; + regulator++; + } while (--elem_size); + } + pdata->gpios[0].label = PLTFRM_CAMERA_MODULE_PIN_PD; + pdata->gpios[0].pltfrm_gpio = of_get_named_gpio_flags( + np, + pdata->gpios[0].label, + 0, + &pdata->gpios[0].active_low); + + pdata->gpios[1].label = PLTFRM_CAMERA_MODULE_PIN_PWR; + pdata->gpios[1].pltfrm_gpio = of_get_named_gpio_flags( + np, + pdata->gpios[1].label, + 0, + &pdata->gpios[1].active_low); + + pdata->gpios[2].label = PLTFRM_CAMERA_MODULE_PIN_FLASH; + pdata->gpios[2].pltfrm_gpio = of_get_named_gpio_flags( + np, + pdata->gpios[2].label, + 0, + &pdata->gpios[2].active_low); + + /*set fl_ctrl flash reference*/ + pdata->fl_ctrl.fl_flash = &pdata->gpios[2]; + + pdata->gpios[3].label = PLTFRM_CAMERA_MODULE_PIN_TORCH; + pdata->gpios[3].pltfrm_gpio = of_get_named_gpio_flags( + np, + pdata->gpios[3].label, + 0, + &pdata->gpios[3].active_low); + + /*set fl_ctrl torch reference*/ + pdata->fl_ctrl.fl_torch = &pdata->gpios[3]; + + pdata->gpios[4].label = PLTFRM_CAMERA_MODULE_PIN_RESET; + pdata->gpios[4].pltfrm_gpio = of_get_named_gpio_flags( + np, + pdata->gpios[4].label, + 0, + &pdata->gpios[4].active_low); + + ret = of_property_read_string(np, OF_CAMERA_MODULE_NAME, + &pdata->info.module_name); + ret |= of_property_read_string(np, OF_CAMERA_MODULE_LEN_NAME, + &pdata->info.len_name); + if (ret) { + pltfrm_camera_module_pr_err( + sd, + "could not get module %s and %s from dts!\n", + OF_CAMERA_MODULE_NAME, + OF_CAMERA_MODULE_LEN_NAME); + } + + if (of_property_read_u32( + np, + OF_CAMERA_MODULE_ORIENTATION, + (unsigned int *)&pdata->info.orientation)) { + pdata->info.orientation = -1; + pltfrm_camera_module_pr_err( + sd, + "could not get module %s from dts!\n", + OF_CAMERA_MODULE_ORIENTATION); + } + + if (of_property_read_u32( + np, + OF_CAMERA_MODULE_FLIP, + (unsigned int *)&pdata->info.flip)) { + pdata->info.flip = -1; + pltfrm_camera_module_pr_err( + sd, + "could not get module %s from dts!\n", + OF_CAMERA_MODULE_FLIP); + } + + if (of_property_read_u32( + np, + OF_CAMERA_MODULE_MIRROR, + (unsigned int *)&pdata->info.mirror)) { + pdata->info.mirror = -1; + pltfrm_camera_module_pr_err( + sd, + "could not get module %s from dts!\n", + OF_CAMERA_MODULE_MIRROR); + } + + if (of_property_read_u32( + np, + OF_CAMERA_FLASH_SUPPORT, + (unsigned int *)&pdata->info.flash_support)) { + pdata->info.flash_support = -1; + pltfrm_camera_module_pr_err( + sd, + "could not get module %s from dts!\n", + OF_CAMERA_FLASH_SUPPORT); + } + + ret = of_property_read_string(np, OF_CAMERA_MODULE_FOV_H, + &pdata->info.fov_h); + ret |= of_property_read_string(np, OF_CAMERA_MODULE_FOV_V, + &pdata->info.fov_v); + if (ret) { + pltfrm_camera_module_pr_debug( + sd, + "could not get module %s and %s from dts!", + OF_CAMERA_MODULE_FOV_H, + OF_CAMERA_MODULE_FOV_V); + } + + ret = of_property_read_string(np, OF_CAMERA_MODULE_FOCAL_LENGTH, + &pdata->info.focal_length); + if (ret) { + pltfrm_camera_module_pr_debug( + sd, + "could not get %s from dts!\n", + OF_CAMERA_MODULE_FOCAL_LENGTH); + } + ret = of_property_read_string(np, OF_CAMERA_MODULE_FOCUS_DISTANCE, + &pdata->info.focus_distance); + if (ret) { + pltfrm_camera_module_pr_debug( + sd, + "could not get %s from dts!\n", + OF_CAMERA_MODULE_FOCUS_DISTANCE); + } + + ret = 0; + of_property_read_u32(np, OF_CAMERA_MODULE_IQ_MIRROR, &ret); + pdata->info.iq_mirror = (ret == 0) ? false : true; + + ret = 0; + of_property_read_u32(np, OF_CAMERA_MODULE_IQ_FLIP, &ret); + pdata->info.iq_flip = (ret == 0) ? false : true; + + if (of_property_read_u32( + np, + OF_CAMERA_FLASH_EXP_PERCENT, + (unsigned int *)&pdata->info.flash_exp_percent)) { + pdata->info.flash_exp_percent = -1; + pltfrm_camera_module_pr_debug( + sd, + "could not get module %s from dts!\n", + OF_CAMERA_FLASH_EXP_PERCENT); + } + + if (of_property_read_u32( + np, + OF_CAMERA_FLASH_TURN_ON_TIME, + (unsigned int *)&pdata->info.flash_turn_on_time)) { + pdata->info.flash_turn_on_time = -1; + pltfrm_camera_module_pr_debug( + sd, + "could not get module %s from dts!\n", + OF_CAMERA_FLASH_TURN_ON_TIME); + } + + if (of_property_read_u32( + np, + OF_CAMERA_FLASH_ON_TIMEOUT, + (unsigned int *)&pdata->info.flash_on_timeout)) { + pdata->info.flash_on_timeout = -1; + pltfrm_camera_module_pr_debug( + sd, + "could not get module %s from dts!\n", + OF_CAMERA_FLASH_ON_TIMEOUT); + } + + of_property_read_u32_array( + np, + OF_CAMERA_MODULE_DEFRECT0, + (unsigned int *)&pdata->defrects[0], + 6); + of_property_read_u32_array( + np, + OF_CAMERA_MODULE_DEFRECT1, + (unsigned int *)&pdata->defrects[1], + 6); + of_property_read_u32_array( + np, + OF_CAMERA_MODULE_DEFRECT2, + (unsigned int *)&pdata->defrects[2], + 6); + of_property_read_u32_array( + np, + OF_CAMERA_MODULE_DEFRECT3, + (unsigned int *)&pdata->defrects[3], + 6); + + if (of_property_read_u32( + np, + OF_CAMERA_MODULE_MIPI_DPHY_INDEX, + (unsigned int *)&pdata->itf.itf.mipi.dphy_index)) { + pdata->itf.itf.mipi.dphy_index = -1; + pltfrm_camera_module_pr_debug( + sd, + "could not get module %s from dts!\n", + OF_CAMERA_MODULE_MIPI_DPHY_INDEX); + } + + of_node_put(np); + return pdata; +err: + pltfrm_camera_module_pr_err(sd, "failed with error %d\n", ret); + if (!IS_ERR_OR_NULL(pdata->regulators.regulator)) { + devm_kfree( + &client->dev, + pdata->regulators.regulator); + pdata->regulators.regulator = NULL; + } + + if (!IS_ERR_OR_NULL(pdata->mclk)) { + devm_clk_put(&client->dev, pdata->mclk); + pdata->mclk = NULL; + } + if (!IS_ERR_OR_NULL(pdata)) { + devm_kfree(&client->dev, pdata); + pdata = NULL; + } + of_node_put(np); + return ERR_PTR(ret); +} + +static int pltfrm_camera_module_pix_frmt2code( + const char *pix_frmt) +{ + if (strcmp(pix_frmt, "BAYER_BGGR8") == 0) + return MEDIA_BUS_FMT_SBGGR8_1X8; + if (strcmp(pix_frmt, "BAYER_GBRG8") == 0) + return MEDIA_BUS_FMT_SGBRG8_1X8; + if (strcmp(pix_frmt, "BAYER_GRBG8") == 0) + return MEDIA_BUS_FMT_SGRBG8_1X8; + if (strcmp(pix_frmt, "BAYER_RGGB8") == 0) + return MEDIA_BUS_FMT_SRGGB8_1X8; + if (strcmp(pix_frmt, "BAYER_BGGR10") == 0) + return MEDIA_BUS_FMT_SBGGR10_1X10; + if (strcmp(pix_frmt, "BAYER_GBRG10") == 0) + return MEDIA_BUS_FMT_SGBRG10_1X10; + if (strcmp(pix_frmt, "BAYER_GRBG10") == 0) + return MEDIA_BUS_FMT_SGRBG10_1X10; + if (strcmp(pix_frmt, "BAYER_RGGB10") == 0) + return MEDIA_BUS_FMT_SRGGB10_1X10; + if (strcmp(pix_frmt, "BAYER_BGGR12") == 0) + return MEDIA_BUS_FMT_SBGGR12_1X12; + if (strcmp(pix_frmt, "BAYER_GBRG12") == 0) + return MEDIA_BUS_FMT_SGBRG12_1X12; + if (strcmp(pix_frmt, "BAYER_GRBG12") == 0) + return MEDIA_BUS_FMT_SGRBG12_1X12; + if (strcmp(pix_frmt, "BAYER_RGGB12") == 0) + return MEDIA_BUS_FMT_SRGGB12_1X12; + if (strcmp(pix_frmt, "YUYV8") == 0) + return MEDIA_BUS_FMT_YUYV8_2X8; + if (strcmp(pix_frmt, "YUYV10") == 0) + return MEDIA_BUS_FMT_YUYV10_2X10; + if (strcmp(pix_frmt, "UYUV8") == 0) + return MEDIA_BUS_FMT_UYVY8_2X8; + return -EINVAL; +} + +static int pltfrm_camera_module_config_matches( + struct v4l2_subdev *sd, + struct device_node *config, + struct v4l2_mbus_framefmt *frm_fmt, + struct v4l2_subdev_frame_interval *frm_intrvl) +{ + int ret = 0; + struct property *prop; + const char *of_pix_fmt; + bool match = true; + u32 min, min2, max, max2; + u32 numerator, denominator; + + pltfrm_camera_module_pr_debug(sd, + "pix_frm %d, %dx%d@%d/%dfps, config %s\n", + frm_fmt->code, frm_fmt->width, frm_fmt->height, + frm_intrvl->interval.denominator, + frm_intrvl->interval.numerator, + config->name); + + /* check pixel format */ + of_property_for_each_string(config, "rockchip,frm-pixel-format", + prop, of_pix_fmt) { + if (pltfrm_camera_module_pix_frmt2code(of_pix_fmt) == + frm_fmt->code) { + match = true; + break; + } + } + + if (!match) + return 0; + + /* check frame width */ + ret = of_property_read_u32(config, "rockchip,frm-width", &min); + if (ret == -EINVAL) { + ret = of_property_read_u32_index(config, + "rockchip,frm-width-range", 0, &min); + if (ret == -EINVAL) { + min = 0; + max = UINT_MAX; + } else if (IS_ERR_VALUE(ret)) { + pltfrm_camera_module_pr_err(sd, + "malformed property 'rockchip,frm-width-range'\n"); + goto err; + } else { + ret = of_property_read_u32_index(config, + "rockchip,frm-width-range", 1, &max); + if (IS_ERR_VALUE(ret)) { + pltfrm_camera_module_pr_err(sd, + "malformed property 'rockchip,frm-width-range'\n"); + goto err; + } + } + } else if (IS_ERR_VALUE(ret)) { + pltfrm_camera_module_pr_err(sd, + "malformed property 'rockchip,frm-width'\n"); + goto err; + } else { + max = min; + } + if ((frm_fmt->width < min) || (frm_fmt->width > max)) + return 0; + + /* check frame height */ + ret = of_property_read_u32(config, "rockchip,frm-height", &min); + if (ret == -EINVAL) { + ret = of_property_read_u32_index(config, + "rockchip,frm-height-range", 0, &min); + if (ret == -EINVAL) { + min = 0; + max = UINT_MAX; + } else if (IS_ERR_VALUE(ret)) { + pltfrm_camera_module_pr_err(sd, + "malformed property 'rockchip,frm-height-range'\n"); + goto err; + } else { + ret = of_property_read_u32_index(config, + "rockchip,frm-height-range", 1, &max); + if (IS_ERR_VALUE(ret)) { + pltfrm_camera_module_pr_err(sd, + "malformed property 'rockchip,frm-height-range'\n"); + goto err; + } + } + } else if (IS_ERR_VALUE(ret)) { + pltfrm_camera_module_pr_err(sd, + "malformed property 'rockchip,frm-height'\n"); + goto err; + } else { + max = min; + } + if ((frm_fmt->height < min) || (frm_fmt->height > max)) + return 0; + + /* check frame interval */ + ret = of_property_read_u32_index(config, + "rockchip,frm-interval", 0, &min); + if (ret == -EINVAL) { + ret = of_property_read_u32_index(config, + "rockchip,frm-interval-range", 0, &min); + if (ret == -EINVAL) { + min = 0; + max = UINT_MAX; + } else if (IS_ERR_VALUE(ret)) { + pltfrm_camera_module_pr_err(sd, + "malformed property 'rockchip,frm-interval-range'\n"); + goto err; + } else { + ret |= of_property_read_u32_index(config, + "rockchip,frm-interval-range", 1, &min2); + ret |= of_property_read_u32_index(config, + "rockchip,frm-interval-range", 2, &max); + ret |= of_property_read_u32_index(config, + "rockchip,frm-interval-range", 3, &max2); + if (IS_ERR_VALUE(ret)) { + pltfrm_camera_module_pr_err(sd, + "malformed property 'rockchip,frm-interval-range'\n"); + goto err; + } + } + } else if (IS_ERR_VALUE(ret)) { + pltfrm_camera_module_pr_err(sd, + "malformed property 'rockchip,frm-interval'\n"); + goto err; + } else { + ret = of_property_read_u32_index(config, + "rockchip,frm-interval", 1, &min2); + if (IS_ERR_VALUE(ret)) { + pltfrm_camera_module_pr_err(sd, + "malformed property 'rockchip,frm-interval'\n"); + goto err; + } + max = min; + max2 = min2; + } + + /* normalize frame intervals */ + denominator = lcm(min2, frm_intrvl->interval.denominator); + denominator = lcm(max2, denominator); + numerator = denominator / frm_intrvl->interval.denominator * + frm_intrvl->interval.numerator; + + min = denominator / min2 * min; + max = denominator / max2 * max; + + if ((numerator < min) || (numerator > max)) + return 0; + + return 1; +err: + pltfrm_camera_module_pr_err(sd, + "failed with error %d\n", ret); + return ret; +} + +static int pltfrm_camera_module_write_reglist_node( + struct v4l2_subdev *sd, + struct device_node *config_node) +{ + struct property *reg_table_prop; + struct pltfrm_camera_module_reg *reg_table = NULL; + u32 reg_table_num_entries; + u32 i = 0; + int ret = 0; + + reg_table_prop = of_find_property(config_node, "rockchip,reg-table", + ®_table_num_entries); + if (!IS_ERR_OR_NULL(reg_table_prop)) { + if (((reg_table_num_entries / 12) == 0) || + (reg_table_num_entries % 3)) { + pltfrm_camera_module_pr_err(sd, + "wrong register format in %s, must be 'type, address, value' per register\n", + config_node->name); + ret = -EINVAL; + goto err; + } + + reg_table_num_entries /= 12; + reg_table = (struct pltfrm_camera_module_reg *) + kmalloc(reg_table_num_entries * + sizeof(struct pltfrm_camera_module_reg), + GFP_KERNEL); + if (IS_ERR_OR_NULL(reg_table)) { + pltfrm_camera_module_pr_err(sd, + "memory allocation failed\n"); + ret = -ENOMEM; + goto err; + } + + pltfrm_camera_module_pr_debug(sd, + "patching config with %s (%d registers)\n", + config_node->name, reg_table_num_entries); + for (i = 0; i < reg_table_num_entries; i++) { + u32 val; + + ret |= of_property_read_u32_index( + config_node, "rockchip,reg-table", + 3 * i, &val); + reg_table[i].flag = val; + ret |= of_property_read_u32_index( + config_node, "rockchip,reg-table", + 3 * i + 1, &val); + reg_table[i].reg = val; + ret |= of_property_read_u32_index( + config_node, "rockchip,reg-table", + 3 * i + 2, &val); + reg_table[i].val = val; + if (IS_ERR_VALUE(ret)) { + pltfrm_camera_module_pr_err(sd, + "error while reading property %s at index %d\n", + "rockchip,reg-table", i); + goto err; + } + } + ret = pltfrm_camera_module_write_reglist( + sd, reg_table, reg_table_num_entries); + if (IS_ERR_VALUE(ret)) + goto err; + kfree(reg_table); + reg_table = NULL; + } + return 0; +err: + pltfrm_camera_module_pr_err(sd, + "failed with error %d\n", ret); + kfree(reg_table); + return ret; +} + +/* ======================================================================== */ + +const char *pltfrm_dev_string( + struct v4l2_subdev *sd) +{ + struct i2c_client *client; + + if (IS_ERR_OR_NULL(sd)) + return ""; + client = v4l2_get_subdevdata(sd); + if (IS_ERR_OR_NULL(client)) + return ""; + return dev_driver_string(&client->dev); +} + +int pltfrm_camera_module_read_reg( + struct v4l2_subdev *sd, + u16 data_length, + u16 reg, + u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + struct i2c_msg msg[1]; + unsigned char data[4] = { 0, 0, 0, 0 }; + + if (!client->adapter) { + pltfrm_camera_module_pr_err(sd, "client->adapter NULL\n"); + return -ENODEV; + } + + msg->addr = client->addr; + msg->flags = I2C_M_WR; + msg->len = 2; + msg->buf = data; + + /* High byte goes out first */ + data[0] = (u8)(reg >> 8); + data[1] = (u8)(reg & 0xff); + + ret = i2c_transfer(client->adapter, msg, 1); + if (ret >= 0) { + mdelay(3); + msg->flags = I2C_M_RD; + msg->len = data_length; + i2c_transfer(client->adapter, msg, 1); + } + if (ret >= 0) { + *val = 0; + /* High byte comes first */ + if (data_length == 1) + *val = data[0]; + else if (data_length == 2) + *val = data[1] + (data[0] << 8); + else + *val = data[3] + (data[2] << 8) + + (data[1] << 16) + (data[0] << 24); + return 0; + } + pltfrm_camera_module_pr_err(sd, + "i2c read from offset 0x%08x failed with error %d\n", reg, ret); + return ret; +} + +/* ======================================================================== */ + +int pltfrm_camera_module_write_reg( + struct v4l2_subdev *sd, + u16 reg, u8 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + struct i2c_msg msg[1]; + unsigned char data[3]; + int retries; + + if (!client->adapter) { + pltfrm_camera_module_pr_err(sd, "client->adapter NULL\n"); + return -ENODEV; + } + + for (retries = 0; retries < 5; retries++) { + msg->addr = client->addr; + msg->flags = I2C_M_WR; + msg->len = 3; + msg->buf = data; + + /* high byte goes out first */ + data[0] = (u8)(reg >> 8); + data[1] = (u8)(reg & 0xff); + data[2] = val; + + ret = i2c_transfer(client->adapter, msg, 1); + if (ret == 1) { + pltfrm_camera_module_pr_debug(sd, + "i2c write to offset 0x%08x success\n", reg); + return 0; + } + + pltfrm_camera_module_pr_debug(sd, + "retrying I2C... %d\n", retries); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(20)); + } + pltfrm_camera_module_pr_err(sd, + "i2c write to offset 0x%08x failed with error %d\n", reg, ret); + return ret; +} + +/* ======================================================================== */ +int pltfrm_camera_module_write_reglist( + struct v4l2_subdev *sd, + const struct pltfrm_camera_module_reg reglist[], + int len) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + unsigned int k = 0, j = 0; + int i = 0; + struct i2c_msg *msg; + unsigned char *data; + unsigned int max_entries = len; + + msg = kmalloc((sizeof(struct i2c_msg) * I2C_MSG_MAX), + GFP_KERNEL); + if (!msg) + return -ENOMEM; + data = kmalloc((sizeof(unsigned char) * I2C_DATA_MAX), + GFP_KERNEL); + if (!data) { + kfree(msg); + return -ENOMEM; + } + + for (i = 0; i < max_entries; i++) { + switch (reglist[i].flag) { + case PLTFRM_CAMERA_MODULE_REG_TYPE_DATA: + (msg + j)->addr = client->addr; + (msg + j)->flags = I2C_M_WR; + (msg + j)->len = 3; + (msg + j)->buf = (data + k); + + data[k + 0] = (u8)((reglist[i].reg & 0xFF00) >> 8); + data[k + 1] = (u8)(reglist[i].reg & 0xFF); + data[k + 2] = (u8)(reglist[i].val & 0xFF); + k = k + 3; + j++; + if (j == (I2C_MSG_MAX - 1)) { + /* Bulk I2C transfer */ + pltfrm_camera_module_pr_debug(sd, + "messages transfers 1 0x%p msg %d bytes %d\n", + msg, j, k); + ret = i2c_transfer(client->adapter, msg, j); + if (ret < 0) { + pltfrm_camera_module_pr_err(sd, + "i2c transfer returned with err %d\n", + ret); + kfree(msg); + kfree(data); + return ret; + } + j = 0; + k = 0; + pltfrm_camera_module_pr_debug(sd, + "i2c_transfer return %d\n", ret); + } + break; + case PLTFRM_CAMERA_MODULE_REG_TYPE_TIMEOUT: + if (j > 0) { + /* Bulk I2C transfer */ + pltfrm_camera_module_pr_debug(sd, + "messages transfers 1 0x%p msg %d bytes %d\n", + msg, j, k); + ret = i2c_transfer(client->adapter, msg, j); + if (ret < 0) { + pltfrm_camera_module_pr_debug(sd, + "i2c transfer returned with err %d\n", + ret); + kfree(msg); + kfree(data); + return ret; + } + pltfrm_camera_module_pr_debug(sd, + "i2c_transfer return %d\n", ret); + } + mdelay(reglist[i].val); + j = 0; + k = 0; + break; + default: + pltfrm_camera_module_pr_debug(sd, "unknown command\n"); + kfree(msg); + kfree(data); + return -1; + } + } + + if (j != 0) { /* Remaining I2C message */ + pltfrm_camera_module_pr_debug(sd, + "messages transfers 1 0x%p msg %d bytes %d\n", + msg, j, k); + ret = i2c_transfer(client->adapter, msg, j); + if (ret < 0) { + pltfrm_camera_module_pr_err(sd, + "i2c transfer returned with err %d\n", ret); + kfree(msg); + kfree(data); + return ret; + } + pltfrm_camera_module_pr_debug(sd, + "i2c_transfer return %d\n", ret); + } + + kfree(msg); + kfree(data); + return 0; +} + +static int pltfrm_camera_module_init_pm( + struct v4l2_subdev *sd, + struct pltfrm_soc_cfg *soc_cfg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct pltfrm_camera_module_data *pdata = + dev_get_platdata(&client->dev); + + pdata->soc_cfg = soc_cfg; + return 0; +} + +int pltfrm_camera_module_set_pm_state( + struct v4l2_subdev *sd, + int on) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct pltfrm_camera_module_data *pdata = + dev_get_platdata(&client->dev); + struct pltfrm_soc_cfg *soc_cfg = + pdata->soc_cfg; + struct pltfrm_soc_mclk_para mclk_para; + struct pltfrm_soc_cfg_para cfg_para; + struct pltfrm_cam_itf itf_cfg; + unsigned int i; + + if (on) { + if (IS_ERR_OR_NULL(soc_cfg)) { + pltfrm_camera_module_pr_err(sd, + "set_pm_state failed! soc_cfg is %p!\n", + soc_cfg); + return -EINVAL; + } + + if (pdata->regulators.regulator) { + for (i = 0; i < pdata->regulators.cnt; i++) { + struct pltfrm_camera_module_regulator + *regulator; + + regulator = pdata->regulators.regulator + i; + if (IS_ERR(regulator->regulator)) + continue; + regulator_set_voltage( + regulator->regulator, + regulator->min_uV, + regulator->max_uV); + if (regulator_enable(regulator->regulator)) + pltfrm_camera_module_pr_err(sd, + "regulator_enable failed!\n"); + } + } + + pltfrm_camera_module_set_pin_state( + sd, + PLTFRM_CAMERA_MODULE_PIN_PWR, + PLTFRM_CAMERA_MODULE_PIN_STATE_ACTIVE); + + pltfrm_camera_module_set_pin_state( + sd, + PLTFRM_CAMERA_MODULE_PIN_RESET, + PLTFRM_CAMERA_MODULE_PIN_STATE_ACTIVE); + usleep_range(100, 300); + pltfrm_camera_module_set_pin_state( + sd, + PLTFRM_CAMERA_MODULE_PIN_RESET, + PLTFRM_CAMERA_MODULE_PIN_STATE_INACTIVE); + + mclk_para.io_voltage = PLTFRM_IO_1V8; + mclk_para.drv_strength = PLTFRM_DRV_STRENGTH_2; + cfg_para.cmd = PLTFRM_MCLK_CFG; + cfg_para.cfg_para = (void *)&mclk_para; + soc_cfg->soc_cfg(&cfg_para); + + if (v4l2_subdev_call(sd, + core, + ioctl, + PLTFRM_CIFCAM_G_ITF_CFG, + (void *)&itf_cfg) == 0) { + clk_set_rate(pdata->mclk, itf_cfg.mclk_hz); + } else { + pltfrm_camera_module_pr_err(sd, + "PLTFRM_CIFCAM_G_ITF_CFG failed, mclk set 24m default.\n"); + clk_set_rate(pdata->mclk, 24000000); + } + clk_prepare_enable(pdata->mclk); + } else { + clk_disable_unprepare(pdata->mclk); + + pltfrm_camera_module_set_pin_state( + sd, + PLTFRM_CAMERA_MODULE_PIN_PWR, + PLTFRM_CAMERA_MODULE_PIN_STATE_INACTIVE); + + if (pdata->regulators.regulator) { + for (i = 0; i < pdata->regulators.cnt; i++) { + struct pltfrm_camera_module_regulator + *regulator; + + regulator = pdata->regulators.regulator + i; + if (IS_ERR(regulator->regulator)) + continue; + regulator_disable(regulator->regulator); + } + } + } + + return 0; +} + +int pltfrm_camera_module_set_pin_state( + struct v4l2_subdev *sd, + const char *pin, + enum pltfrm_camera_module_pin_state state) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct pltfrm_camera_module_data *pdata = + dev_get_platdata(&client->dev); + int gpio_val; + int i; + + for (i = 0; i < ARRAY_SIZE(pdata->gpios); i++) { + if (pin == pdata->gpios[i].label) { + if (!gpio_is_valid(pdata->gpios[i].pltfrm_gpio)) + return 0; + if (state == PLTFRM_CAMERA_MODULE_PIN_STATE_ACTIVE) + gpio_val = (pdata->gpios[i].active_low == + OF_GPIO_ACTIVE_LOW) ? 0 : 1; + else + gpio_val = (pdata->gpios[i].active_low == + OF_GPIO_ACTIVE_LOW) ? 1 : 0; + gpio_set_value(pdata->gpios[i].pltfrm_gpio, gpio_val); + pltfrm_camera_module_pr_debug(sd, + "set GPIO #%d ('%s') to %s\n", + pdata->gpios[i].pltfrm_gpio, + pdata->gpios[i].label, + gpio_val ? "HIGH" : "LOW"); + + return 0; + } + } + + pltfrm_camera_module_pr_err(sd, + "unknown pin '%s'\n", + pin); + return -EINVAL; +} + +int pltfrm_camera_module_get_pin_state( + struct v4l2_subdev *sd, + const char *pin) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct pltfrm_camera_module_data *pdata = + dev_get_platdata(&client->dev); + int gpio_val; + int i; + + for (i = 0; i < ARRAY_SIZE(pdata->gpios); i++) { + if (pin == pdata->gpios[i].label) { + if (!gpio_is_valid(pdata->gpios[i].pltfrm_gpio)) + return 0; + gpio_val = gpio_get_value(pdata->gpios[i].pltfrm_gpio); + pltfrm_camera_module_pr_debug( + sd, + "get GPIO #%d ('%s') is %s\n", + pdata->gpios[i].pltfrm_gpio, + pdata->gpios[i].label, + gpio_val ? "HIGH" : "LOW"); + + return gpio_val; + } + } + + pltfrm_camera_module_pr_err( + sd, + "unknown pin '%s'\n", + pin); + return -EINVAL; +} + +int pltfrm_camera_module_s_power( + struct v4l2_subdev *sd, + int on) +{ + int ret; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct pltfrm_camera_module_data *pdata = + dev_get_platdata(&client->dev); + + pltfrm_camera_module_pr_debug(sd, "%s\n", on ? "on" : "off"); + + if (on) { + /* Enable clock and voltage to Secondary Camera Sensor */ + ret = pltfrm_camera_module_set_pm_state(sd, on); + if (IS_ERR_VALUE(ret)) + pltfrm_camera_module_pr_err(sd, + "set PM state failed (%d), could not power on camera\n", + ret); + else { + pltfrm_camera_module_pr_debug(sd, + "set PM state to %d successful, camera module is on\n", + on); + ret = pltfrm_camera_module_set_pinctrl_state( + sd, pdata->pins_default); + } + } else { + /* Disable clock and voltage to Secondary Camera Sensor */ + ret = pltfrm_camera_module_set_pinctrl_state( + sd, pdata->pins_sleep); + if (!IS_ERR_VALUE(ret)) { + ret = pltfrm_camera_module_set_pm_state( + sd, on); + if (IS_ERR_VALUE(ret)) + pltfrm_camera_module_pr_err(sd, + "set PM state failed (%d), could not power off camera\n", + ret); + else + pltfrm_camera_module_pr_debug(sd, + "set PM state to %d successful, camera module is off\n", + on); + } + } + return ret; +} + +struct v4l2_subdev *pltfrm_camera_module_get_af_ctrl( + struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct pltfrm_camera_module_data *pdata = + dev_get_platdata(&client->dev); + + return pdata->af_ctrl; +} + +char *pltfrm_camera_module_get_flash_driver_name( + struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct pltfrm_camera_module_data *pdata = + dev_get_platdata(&client->dev); + + return (char *)pdata->fl_ctrl.flash_driver_name; +} + +struct v4l2_subdev *pltfrm_camera_module_get_fl_ctrl( + struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct pltfrm_camera_module_data *pdata = + dev_get_platdata(&client->dev); + + return pdata->fl_ctrl.flsh_ctrl; +} + +int pltfrm_camera_module_patch_config( + struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *frm_fmt, + struct v4l2_subdev_frame_interval *frm_intrvl) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct device_node *parent_node = of_node_get(client->dev.of_node); + struct device_node *child_node = NULL, *prev_node = NULL; + int ret = 0; + + pltfrm_camera_module_pr_debug(sd, "pix_fmt %d, %dx%d@%d/%dfps\n", + frm_fmt->code, frm_fmt->width, frm_fmt->height, + frm_intrvl->interval.denominator, + frm_intrvl->interval.numerator); + + while (!IS_ERR_OR_NULL(child_node = + of_get_next_child(parent_node, prev_node))) { + if (strncasecmp(child_node->name, + "rockchip,camera-module-config", + strlen("rockchip,camera-module-config")) == 0) { + ret = pltfrm_camera_module_config_matches( + sd, child_node, frm_fmt, frm_intrvl); + if (IS_ERR_VALUE(ret)) + goto err; + if (ret) { + ret = + pltfrm_camera_module_write_reglist_node( + sd, child_node); + if (!IS_ERR_VALUE(ret)) + goto err; + } + } + of_node_put(prev_node); + prev_node = child_node; + } + of_node_put(prev_node); + of_node_put(parent_node); + + return 0; +err: + pltfrm_camera_module_pr_err(sd, + "failed with error %d\n", ret); + of_node_put(prev_node); + of_node_put(child_node); + of_node_put(parent_node); + return ret; +} + +int pltfrm_camera_module_init( + struct v4l2_subdev *sd, + void **pldata) +{ + int ret = 0; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct pltfrm_camera_module_data *pdata; + + pltfrm_camera_module_pr_debug(sd, "\n"); + + pdata = pltfrm_camera_module_get_data(sd); + if (IS_ERR_OR_NULL(pdata)) { + pltfrm_camera_module_pr_err(sd, + "unable to get platform data\n"); + return -EFAULT; + } + + ret = pltfrm_camera_module_init_gpio(sd); + if (IS_ERR_VALUE(ret)) { + pltfrm_camera_module_pr_err(sd, + "GPIO initialization failed (%d)\n", ret); + } + + if (IS_ERR_VALUE(ret)) + devm_kfree(&client->dev, pdata); + else + *(struct pltfrm_camera_module_data **)pldata = pdata; + + return ret; +} + +void pltfrm_camera_module_release( + struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct pltfrm_camera_module_data *pdata = + dev_get_platdata(&client->dev); + int i; + + /* GPIOs also needs to be freed for other sensors to use */ + for (i = 0; i < ARRAY_SIZE(pdata->gpios); i++) { + if (gpio_is_valid(pdata->gpios[i].pltfrm_gpio)) { + pltfrm_camera_module_pr_debug(sd, + "free GPIO #%d ('%s')\n", + pdata->gpios[i].pltfrm_gpio, + pdata->gpios[i].label); + gpio_free( + pdata->gpios[i].pltfrm_gpio); + } + } + for (i = 0; i < pdata->regulators.cnt; i++) { + if (!IS_ERR(pdata->regulators.regulator[i].regulator)) + devm_regulator_put( + pdata->regulators.regulator[i].regulator); + } + if (pdata->pinctrl) + devm_pinctrl_put(pdata->pinctrl); +} + +/* ======================================================================== */ +long pltfrm_camera_module_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, + void *arg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct pltfrm_camera_module_data *pdata = + dev_get_platdata(&client->dev); + int ret = 0; + + pltfrm_camera_module_pr_debug(sd, "cmd: 0x%x\n", cmd); + + if (cmd == RK_VIDIOC_CAMERA_MODULEINFO) { + struct camera_module_info_s *p_camera_module = + (struct camera_module_info_s *)arg; + + /* + * strlcpy((char *)p_camera_module->sensor_name, + * (char *)client->driver->driver.name, + * sizeof(p_camera_module->sensor_name)); + */ + + if (pdata->info.module_name) + strcpy((char *)p_camera_module->module_name, + pdata->info.module_name); + else + strcpy((char *)p_camera_module->module_name, "(null)"); + if (pdata->info.len_name) + strcpy((char *)p_camera_module->len_name, + pdata->info.len_name); + else + strcpy((char *)p_camera_module->len_name, "(null)"); + if (pdata->info.fov_h) + strcpy((char *)p_camera_module->fov_h, + pdata->info.fov_h); + else + strcpy((char *)p_camera_module->fov_h, "(null)"); + if (pdata->info.fov_v) + strcpy((char *)p_camera_module->fov_v, + pdata->info.fov_v); + else + strcpy((char *)p_camera_module->fov_v, "(null)"); + if (pdata->info.focal_length) + strcpy((char *)p_camera_module->focal_length, + pdata->info.focal_length); + else + strcpy((char *)p_camera_module->focal_length, "(null)"); + if (pdata->info.focus_distance) + strcpy((char *)p_camera_module->focus_distance, + pdata->info.focus_distance); + else + strcpy((char *)p_camera_module->focus_distance, + "(null)"); + + p_camera_module->facing = pdata->info.facing; + p_camera_module->orientation = pdata->info.orientation; + p_camera_module->iq_mirror = pdata->info.iq_mirror; + p_camera_module->iq_flip = pdata->info.iq_flip; + p_camera_module->flash_support = pdata->info.flash_support; + p_camera_module->flash_exp_percent = + pdata->info.flash_exp_percent; + + return 0; + } else if (cmd == PLTFRM_CIFCAM_G_DEFRECT) { + struct pltfrm_cam_defrect *defrect = + (struct pltfrm_cam_defrect *)arg; + unsigned int i; + + for (i = 0; i < 4; i++) { + if ((pdata->defrects[i].width == defrect->width) && + (pdata->defrects[i].height == defrect->height)) + defrect->defrect = pdata->defrects[i].defrect; + } + return 0; + } else if (cmd == PLTFRM_CIFCAM_G_ITF_CFG) { + struct pltfrm_cam_itf *itf_cfg = (struct pltfrm_cam_itf *)arg; + + if (PLTFRM_CAM_ITF_IS_MIPI(itf_cfg->type)) + itf_cfg->cfg.mipi.dphy_index = + pdata->itf.itf.mipi.dphy_index; + + return 0; + } else if (cmd == PLTFRM_CIFCAM_ATTACH) { + return pltfrm_camera_module_init_pm(sd, + (struct pltfrm_soc_cfg *)arg); + } + + return ret; +} + +int pltfrm_camera_module_get_flip_mirror( + struct v4l2_subdev *sd) +{ + int mode = 0; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct pltfrm_camera_module_data *pdata = + dev_get_platdata(&client->dev); + + if ((pdata->info.flip == -1) && pdata->info.mirror == -1) + return -1; + + if (pdata->info.flip) + mode |= 0x02; + else + mode &= ~0x02; + + if (pdata->info.mirror) + mode |= 0x01; + else + mode &= ~0x01; + + return mode; +} +#endif + diff --git a/drivers/media/i2c/soc_camera/rockchip/rk_camera_module_version.h b/drivers/media/i2c/soc_camera/rockchip/rk_camera_module_version.h new file mode 100644 index 000000000000..4e52a333e819 --- /dev/null +++ b/drivers/media/i2c/soc_camera/rockchip/rk_camera_module_version.h @@ -0,0 +1,32 @@ +/* + * rk_camera_module.h + * (Based on Intel driver for sofiaxxx) + * + * Copyright (C) 2015 Intel Mobile Communications GmbH + * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef _RK_CAMERA_MODULE_VERSION_H_ +#define _RK_CAMERA_MODULE_VERSION_H_ +#include + +/* + * CIF DRIVER VERSION NOTE + * + * v0.1.0: + * 1. Initialize version; + * 2. Update sensor configuration after power up sensor in + * ov_camera_module_s_power. + * Because mipi datalane may be no still on LP11 state when + * sensor configuration; + * + */ + +#define CONFIG_RK_CAMERA_MODULE_VERSION KERNEL_VERSION(0, 1, 0) + +#endif -- 2.34.1