From f42da96525af43345cf0324c55503a3a13bd4e4a Mon Sep 17 00:00:00 2001 From: Nine Feng Date: Mon, 19 Oct 2015 14:54:36 +0800 Subject: [PATCH] media: soc_camera: add ov13860 v4l2 driver Bug 1674463 Change-Id: Ic7aa8c864aae5bb21882bbcaffd0ca561200734d Signed-off-by: Nine Feng Reviewed-on: http://git-master/r/819465 GVS: Gerrit_Virtual_Submit Reviewed-by: David Wang (SW-TEGRA) Reviewed-by: Arun Kannan Reviewed-by: Kamal Balagopalan --- drivers/media/i2c/soc_camera/Kconfig | 6 + drivers/media/i2c/soc_camera/Makefile | 1 + .../media/i2c/soc_camera/ov13860_mode_tbls.h | 327 +++++ drivers/media/i2c/soc_camera/ov13860_v4l2.c | 1219 +++++++++++++++++ include/media/camera_common.h | 1 + include/media/ov13860.h | 126 ++ 6 files changed, 1680 insertions(+) create mode 100644 drivers/media/i2c/soc_camera/ov13860_mode_tbls.h create mode 100644 drivers/media/i2c/soc_camera/ov13860_v4l2.c create mode 100644 include/media/ov13860.h diff --git a/drivers/media/i2c/soc_camera/Kconfig b/drivers/media/i2c/soc_camera/Kconfig index 38d41303590..f0ed930f335 100644 --- a/drivers/media/i2c/soc_camera/Kconfig +++ b/drivers/media/i2c/soc_camera/Kconfig @@ -122,6 +122,12 @@ config SOC_CAMERA_OV9740 help This is a ov9740 camera driver +config SOC_CAMERA_OV13860 + tristate "ov13860 sensor support" + depends on SOC_CAMERA && I2C + help + This is a V4L2 camera driver for the OmniVision OV13860 sensor + config SOC_CAMERA_RJ54N1 tristate "rj54n1cb0c support" depends on SOC_CAMERA && I2C diff --git a/drivers/media/i2c/soc_camera/Makefile b/drivers/media/i2c/soc_camera/Makefile index f124a930256..c30056f7e0d 100644 --- a/drivers/media/i2c/soc_camera/Makefile +++ b/drivers/media/i2c/soc_camera/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_SOC_CAMERA_OV6650) += ov6650.o obj-$(CONFIG_SOC_CAMERA_OV772X) += ov772x.o obj-$(CONFIG_SOC_CAMERA_OV9640) += ov9640.o obj-$(CONFIG_SOC_CAMERA_OV9740) += ov9740.o +obj-$(CONFIG_SOC_CAMERA_OV13860) += ov13860_v4l2.o obj-$(CONFIG_SOC_CAMERA_RJ54N1) += rj54n1cb0c.o obj-$(CONFIG_SOC_CAMERA_TW9910) += tw9910.o diff --git a/drivers/media/i2c/soc_camera/ov13860_mode_tbls.h b/drivers/media/i2c/soc_camera/ov13860_mode_tbls.h new file mode 100644 index 00000000000..81ae6a8f9c0 --- /dev/null +++ b/drivers/media/i2c/soc_camera/ov13860_mode_tbls.h @@ -0,0 +1,327 @@ +/* + * ov13860.c - ov13860 sensor driver + * + * Copyright (c) 2015, NVIDIA CORPORATION, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#ifndef __OV13860_I2C_TABLES__ +#define __OV13860_I2C_TABLES__ + +#define OV13860_TABLE_WAIT_MS 0 +#define OV13860_TABLE_END 1 +#define OV13860_MAX_RETRIES 3 +#define OV13860_WAIT_MS 3 + +#define ov13860_reg struct reg_8 + +static ov13860_reg ov13860_start[] = { + { 0x0100, 0x01 }, + { OV13860_TABLE_END, 0x00 } +}; + +static ov13860_reg ov13860_stop[] = { + { 0x0100, 0x00 }, + { OV13860_TABLE_END, 0x00 } +}; + +static ov13860_reg tp_colorbars[] = { + /* {0x5280, 0x82}, Color square mode */ + {0x5280, 0x80}, /* Color bar mode */ + {OV13860_TABLE_END, 0x00} +}; + +/* 1.2GHz */ +static ov13860_reg mode_4224x3120[] = { + /* Reset */ + {0x301e, 0x00}, + {OV13860_TABLE_WAIT_MS, 1}, + {0x0103, 0x01}, + {OV13860_TABLE_WAIT_MS, 1}, + + {0x0302, 0x32}, + {0x0303, 0x00}, + {0x030e, 0x02}, + {0x030f, 0x03}, + {0x0312, 0x03}, + {0x0313, 0x03}, + {0x031e, 0x01}, + {0x3010, 0x01}, + {0x3012, 0x41}, + {0x340c, 0xff}, + {0x340d, 0xff}, + {0x3501, 0x0d}, + {0x3502, 0x88}, + {0x3503, 0x00}, + {0x3507, 0x00}, + {0x3508, 0x00}, + {0x3509, 0x12}, + {0x350a, 0x00}, + {0x350b, 0xff}, + {0x350f, 0x10}, + {0x3541, 0x02}, + {0x3542, 0x00}, + {0x3543, 0x00}, + {0x3547, 0x00}, + {0x3548, 0x00}, + {0x3549, 0x12}, + {0x354b, 0x10}, + {0x354f, 0x10}, + {0x3600, 0x41}, + {0x3601, 0xd4}, + {0x3603, 0x97}, + {0x3604, 0x08}, + {0x360a, 0x35}, + {0x360c, 0xa0}, + {0x360d, 0x53}, + {0x3618, 0x0c}, + {0x3620, 0x55}, + {0x3622, 0x8c}, + {0x3623, 0x30}, + {0x3628, 0xc1}, + {0x3660, 0xc0}, + {0x3662, 0x00}, + {0x3663, 0x00}, + {0x3664, 0x04}, + {0x366b, 0x00}, + {0x3701, 0x20}, + {0x3702, 0x20}, + {0x3703, 0x2b}, + {0x3704, 0x56}, + {0x3705, 0x07}, + {0x3706, 0x3f}, + {0x3708, 0x3c}, + {0x3709, 0x18}, + {0x370a, 0x23}, + {0x370e, 0x32}, + {0x3710, 0x10}, + {0x3712, 0x12}, + {0x3714, 0x00}, + {0x3717, 0x00}, + {0x3719, 0x03}, + {0x3720, 0x18}, + {0x3721, 0x10}, + {0x3726, 0x22}, + {0x3727, 0x44}, + {0x3728, 0x40}, + {0x3729, 0x00}, + {0x372a, 0x20}, + {0x372b, 0x00}, + {0x372e, 0x2b}, + {0x372f, 0xa0}, + {0x3730, 0x00}, + {0x3731, 0x00}, + {0x3732, 0x00}, + {0x3733, 0x00}, + {0x3734, 0x01}, + {0x3735, 0x70}, + {0x3736, 0x00}, + {0x3737, 0xf0}, + {0x373c, 0x00}, + {0x373d, 0x10}, + {0x373e, 0x01}, + {0x373f, 0x16}, + {0x3740, 0x01}, + {0x3741, 0x1b}, + {0x3742, 0x01}, + {0x3743, 0x17}, + {0x3749, 0x11}, + {0x3744, 0x02}, + {0x3745, 0x37}, + {0x3746, 0x02}, + {0x3747, 0x33}, + {0x3748, 0x50}, + {0x374a, 0xd8}, + {0x3760, 0xd1}, + {0x3761, 0x31}, + {0x3762, 0x53}, + {0x3763, 0x14}, + {0x3767, 0x24}, + {0x3768, 0x0c}, + {0x3769, 0x24}, + {0x376c, 0x43}, + {0x376d, 0x01}, + {0x376e, 0x53}, + {0x378c, 0x1f}, + {0x378d, 0x13}, + {0x378f, 0x88}, + {0x3790, 0x5a}, + {0x3791, 0x5a}, + {0x3792, 0x21}, + {0x3794, 0x71}, + {0x3796, 0x01}, + {0x379f, 0x3e}, + {0x37a0, 0x44}, + {0x37a1, 0x00}, + {0x37a2, 0x44}, + {0x37a3, 0x41}, + {0x37a4, 0x88}, + {0x37a5, 0xa9}, + {0x37b3, 0xdc}, + {0x37b4, 0x0e}, + {0x37b7, 0x84}, + {0x37b8, 0x02}, + {0x37b9, 0x08}, + {0x3842, 0x00}, + {0x3800, 0x00}, + {0x3801, 0x14}, + {0x3802, 0x00}, + {0x3803, 0x0c}, + {0x3804, 0x10}, + {0x3805, 0x8b}, + {0x3806, 0x0c}, + {0x3807, 0x43}, + {0x3808, 0x10}, + {0x3809, 0x80}, + {0x380a, 0x0c}, + {0x380b, 0x30}, + {0x380c, 0x11}, + {0x380d, 0xd0}, + {0x380e, 0x0d}, + {0x380f, 0xa8}, + {0x3810, 0x00}, + {0x3811, 0x04}, + {0x3813, 0x04}, + {0x3814, 0x11}, + {0x3815, 0x11}, + {0x3820, 0x00}, + {0x3821, 0x04}, + {0x382a, 0x04}, + {0x3835, 0x04}, + {0x3836, 0x0c}, + {0x3837, 0x02}, + {0x382f, 0x84}, + {0x383c, 0x88}, + {0x383d, 0xff}, + {0x3845, 0x10}, + {0x3d85, 0x16}, + {0x3d8c, 0x79}, + {0x3d8d, 0x7f}, + {0x4000, 0x17}, + {0x4008, 0x00}, + {0x4009, 0x13}, + {0x400f, 0x80}, + {0x4011, 0xfb}, + {0x4017, 0x08}, + {0x401a, 0x0e}, + {0x4019, 0x18}, + {0x4020, 0x08}, + {0x4022, 0x08}, + {0x4024, 0x08}, + {0x4026, 0x08}, + {0x4028, 0x08}, + {0x402a, 0x08}, + {0x402c, 0x08}, + {0x402e, 0x08}, + {0x4030, 0x08}, + {0x4032, 0x08}, + {0x4034, 0x08}, + {0x4036, 0x08}, + {0x4038, 0x08}, + {0x403a, 0x08}, + {0x403c, 0x08}, + {0x403e, 0x08}, + {0x4051, 0x03}, + {0x4052, 0x00}, + {0x4053, 0x80}, + {0x4054, 0x00}, + {0x4055, 0x80}, + {0x4056, 0x00}, + {0x4057, 0x80}, + {0x4058, 0x00}, + {0x4059, 0x80}, + {0x4066, 0x04}, + {0x4202, 0x00}, + {0x4203, 0x00}, + {0x4d00, 0x05}, + {0x4d01, 0x03}, + {0x4d02, 0xca}, + {0x4d03, 0xd7}, + {0x4d04, 0xae}, + {0x4d05, 0x13}, + {0x4813, 0x10}, + {0x4815, 0x40}, + {0x4837, 0x0d}, + {0x486e, 0x03}, + {0x4b01, 0x80}, + {0x4b06, 0x00}, + {0x4c01, 0xdf}, + {0x5000, 0x99}, + {0x5001, 0x40}, + {0x5002, 0x04}, + {0x5003, 0x00}, + {0x5004, 0x80}, + {0x5005, 0x00}, + {0x501d, 0x00}, + {0x501f, 0x06}, + {0x5021, 0x00}, + {0x5022, 0x13}, + {0x5058, 0x00}, + {0x5200, 0x00}, + {0x5201, 0x80}, + {0x5204, 0x01}, + {0x5205, 0x00}, + {0x5209, 0x00}, + {0x520a, 0x80}, + {0x520b, 0x04}, + {0x520c, 0x01}, + {0x520e, 0x34}, + {0x5210, 0x10}, + {0x5211, 0xa0}, + {0x5280, 0x00}, + {0x5292, 0x00}, + {0x5c80, 0x00}, + {0x5c81, 0x10}, + {0x5c82, 0x09}, + {0x5c83, 0x5f}, + {0x5d00, 0x00}, + {0x4001, 0x60}, + {0x560f, 0xfc}, + {0x5610, 0xf0}, + {0x5611, 0x10}, + {0x562f, 0xfc}, + {0x5630, 0xf0}, + {0x5631, 0x10}, + {0x564f, 0xfc}, + {0x5650, 0xf0}, + {0x5651, 0x10}, + {0x566f, 0xfc}, + {0x5670, 0xf0}, + {0x5671, 0x10}, + + {OV13860_TABLE_END, 0x00} +}; + +enum { + OV13860_MODE_4224X3120, + + OV13860_MODE_START_STREAM, + OV13860_MODE_STOP_STREAM, + OV13860_MODE_TEST_PATTERN, +}; + +static ov13860_reg *mode_table[] = { + [OV13860_MODE_4224X3120] = mode_4224x3120, + + [OV13860_MODE_START_STREAM] = ov13860_start, + [OV13860_MODE_STOP_STREAM] = ov13860_stop, + [OV13860_MODE_TEST_PATTERN] = tp_colorbars, +}; + +static const struct camera_common_frmfmt ov13860_frmfmt[] = { + {{4224, 3120}, 0, OV13860_MODE_4224X3120}, +}; +#endif /* __OV13860_I2C_TABLES__ */ diff --git a/drivers/media/i2c/soc_camera/ov13860_v4l2.c b/drivers/media/i2c/soc_camera/ov13860_v4l2.c new file mode 100644 index 00000000000..bcef412ca39 --- /dev/null +++ b/drivers/media/i2c/soc_camera/ov13860_v4l2.c @@ -0,0 +1,1219 @@ +/* + * ov13860.c - ov13860 sensor driver + * + * Copyright (c) 2013-2015, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "ov13860_mode_tbls.h" + +#define OV13860_MAX_COARSE_DIFF 8 + +#define OV13860_GAIN_SHIFT 8 +#define OV13860_MIN_GAIN (1 << OV13860_GAIN_SHIFT) +#define OV13860_MAX_GAIN \ + ((15 << OV13860_GAIN_SHIFT) | (1 << (OV13860_GAIN_SHIFT - 1))) +#define OV13860_MIN_FRAME_LENGTH (0x0DA8) +#define OV13860_MAX_FRAME_LENGTH (0x7fff) +#define OV13860_MIN_EXPOSURE_COARSE (0x8) +#define OV13860_MAX_EXPOSURE_COARSE \ + (OV13860_MAX_FRAME_LENGTH-OV13860_MAX_COARSE_DIFF) + +#define OV13860_DEFAULT_GAIN OV13860_MIN_GAIN +#define OV13860_DEFAULT_FRAME_LENGTH OV13860_MIN_FRAME_LENGTH +#define OV13860_DEFAULT_EXPOSURE_COARSE \ + (OV13860_DEFAULT_FRAME_LENGTH-OV13860_MAX_COARSE_DIFF) + +#define OV13860_DEFAULT_MODE OV13860_MODE_4224X3120 +#define OV13860_DEFAULT_HDR_MODE OV13860_MODE_4224X3120_HDR +#define OV13860_DEFAULT_WIDTH 4224 +#define OV13860_DEFAULT_HEIGHT 3120 +#define OV13860_DEFAULT_DATAFMT V4L2_MBUS_FMT_SRGGB10_1X10 +#define OV13860_DEFAULT_CLK_FREQ 24000000 + +struct ov13860 { + struct camera_common_power_rail power; + int num_ctrls; + struct v4l2_ctrl_handler ctrl_handler; + struct camera_common_eeprom_data eeprom[OV13860_EEPROM_NUM_BLOCKS]; + u8 eeprom_buf[OV13860_EEPROM_SIZE]; + struct i2c_client *i2c_client; + struct v4l2_subdev *subdev; + + struct regmap *regmap; + struct camera_common_data *s_data; + struct camera_common_pdata *pdata; + struct v4l2_ctrl *ctrls[]; +}; + +static const struct regmap_config sensor_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, +}; + +u16 ov13860_to_gain(u32 rep, int shift) +{ + u16 gain; + int gain_int; + int gain_dec; + int min_int = (1 << shift); + + if (rep < OV13860_MIN_GAIN) + rep = OV13860_MIN_GAIN; + else if (rep > OV13860_MAX_GAIN) + rep = OV13860_MAX_GAIN; + + /* shift indicates number of least significant bits + * used for decimal representation of gain */ + gain_int = (int)(rep >> shift); + gain_dec = (int)(rep & ~(0xffff << shift)); + + /* derived from formulat gain = (x * 16 + 0.5) */ + gain = ((gain_int * min_int + gain_dec) * 32 + min_int) / (2 * min_int); + + return gain; +} + +static int ov13860_g_volatile_ctrl(struct v4l2_ctrl *ctrl); +static int ov13860_s_ctrl(struct v4l2_ctrl *ctrl); + +static const struct v4l2_ctrl_ops ov13860_ctrl_ops = { + .g_volatile_ctrl = ov13860_g_volatile_ctrl, + .s_ctrl = ov13860_s_ctrl, +}; + +static struct v4l2_ctrl_config ctrl_config_list[] = { +/* Do not change the name field for the controls! */ + { + .ops = &ov13860_ctrl_ops, + .id = V4L2_CID_GAIN, + .name = "Gain", + .type = V4L2_CTRL_TYPE_INTEGER, + .flags = V4L2_CTRL_FLAG_SLIDER, + .min = OV13860_MIN_GAIN, + .max = OV13860_MAX_GAIN, + .def = OV13860_DEFAULT_GAIN, + .step = 1, + }, + { + .ops = &ov13860_ctrl_ops, + .id = V4L2_CID_FRAME_LENGTH, + .name = "Frame Length", + .type = V4L2_CTRL_TYPE_INTEGER, + .flags = V4L2_CTRL_FLAG_SLIDER, + .min = OV13860_MIN_FRAME_LENGTH, + .max = OV13860_MAX_FRAME_LENGTH, + .def = OV13860_DEFAULT_FRAME_LENGTH, + .step = 1, + }, + { + .ops = &ov13860_ctrl_ops, + .id = V4L2_CID_COARSE_TIME, + .name = "Coarse Time", + .type = V4L2_CTRL_TYPE_INTEGER, + .flags = V4L2_CTRL_FLAG_SLIDER, + .min = OV13860_MIN_EXPOSURE_COARSE, + .max = OV13860_MAX_EXPOSURE_COARSE, + .def = OV13860_DEFAULT_EXPOSURE_COARSE, + .step = 1, + }, + { + .ops = &ov13860_ctrl_ops, + .id = V4L2_CID_COARSE_TIME_SHORT, + .name = "Coarse Time Short", + .type = V4L2_CTRL_TYPE_INTEGER, + .flags = V4L2_CTRL_FLAG_SLIDER, + .min = OV13860_MIN_EXPOSURE_COARSE, + .max = OV13860_MAX_EXPOSURE_COARSE, + .def = OV13860_DEFAULT_EXPOSURE_COARSE, + .step = 1, + }, + { + .ops = &ov13860_ctrl_ops, + .id = V4L2_CID_GROUP_HOLD, + .name = "Group Hold", + .type = V4L2_CTRL_TYPE_INTEGER_MENU, + .min = 0, + .max = ARRAY_SIZE(switch_ctrl_qmenu) - 1, + .menu_skip_mask = 0, + .def = 0, + .qmenu_int = switch_ctrl_qmenu, + }, + { + .ops = &ov13860_ctrl_ops, + .id = V4L2_CID_HDR_EN, + .name = "HDR enable", + .type = V4L2_CTRL_TYPE_INTEGER_MENU, + .min = 0, + .max = ARRAY_SIZE(switch_ctrl_qmenu) - 1, + .menu_skip_mask = 0, + .def = 0, + .qmenu_int = switch_ctrl_qmenu, + }, + { + .ops = &ov13860_ctrl_ops, + .id = V4L2_CID_EEPROM_DATA, + .name = "EEPROM Data", + .type = V4L2_CTRL_TYPE_STRING, + .flags = V4L2_CTRL_FLAG_VOLATILE, + .min = 0, + .max = OV13860_EEPROM_STR_SIZE, + .step = 2, + }, + { + .ops = &ov13860_ctrl_ops, + .id = V4L2_CID_OTP_DATA, + .name = "OTP Data", + .type = V4L2_CTRL_TYPE_STRING, + .flags = V4L2_CTRL_FLAG_READ_ONLY, + .min = 0, + .max = OV13860_OTP_STR_SIZE, + .step = 2, + }, + { + .ops = &ov13860_ctrl_ops, + .id = V4L2_CID_FUSE_ID, + .name = "Fuse ID", + .type = V4L2_CTRL_TYPE_STRING, + .flags = V4L2_CTRL_FLAG_READ_ONLY, + .min = 0, + .max = OV13860_FUSE_ID_STR_SIZE, + .step = 2, + }, +}; + +static inline void ov13860_get_frame_length_regs(ov13860_reg *regs, + u16 frame_length) +{ + regs->addr = OV13860_FRAME_LENGTH_ADDR_MSB; + regs->val = (frame_length >> 8) & 0xff; + (regs + 1)->addr = OV13860_FRAME_LENGTH_ADDR_LSB; + (regs + 1)->val = (frame_length) & 0xff; + (regs + 2)->addr = OV13860_TABLE_END; + (regs + 2)->val = 0; +} + +static inline void ov13860_get_coarse_time_regs(ov13860_reg *regs, + u16 coarse_time) +{ + regs->addr = OV13860_COARSE_TIME_ADDR_MSB; + regs->val = (coarse_time >> 8) & 0xff; + (regs + 1)->addr = OV13860_COARSE_TIME_ADDR_LSB; + (regs + 1)->val = (coarse_time) & 0xff; + (regs + 2)->addr = OV13860_TABLE_END; + (regs + 2)->val = 0; +} + +static inline void ov13860_get_coarse_time_short_regs(ov13860_reg *regs, + u16 coarse_time) +{ + regs->addr = OV13860_COARSE_TIME_SHORT_ADDR_MSB; + regs->val = (coarse_time >> 8) & 0xff; + (regs + 1)->addr = OV13860_COARSE_TIME_SHORT_ADDR_LSB; + (regs + 1)->val = (coarse_time) & 0xff; + (regs + 2)->addr = OV13860_TABLE_END; + (regs + 2)->val = 0; +} + +static inline void ov13860_get_gain_reg(ov13860_reg *regs, + u16 gain) +{ + regs->addr = OV13860_GAIN_ADDR_MSB; + regs->val = (gain >> 8) & 0xff; + (regs + 1)->addr = OV13860_GAIN_ADDR_LSB; + (regs + 1)->val = (gain) & 0xff; + (regs + 2)->addr = OV13860_TABLE_END; + (regs + 2)->val = 0; +} + +static inline void ov13860_get_gain_short_reg(ov13860_reg *regs, + u16 gain) +{ + regs->addr = OV13860_GAIN_SHORT_ADDR_MSB; + regs->val = (gain >> 8) & 0xff; + (regs + 1)->addr = OV13860_GAIN_SHORT_ADDR_LSB; + (regs + 1)->val = (gain) & 0xff; +} + +static int test_mode; +module_param(test_mode, int, 0644); + +static inline int ov13860_read_reg(struct camera_common_data *s_data, + u16 addr, u8 *val) +{ + struct ov13860 *priv = (struct ov13860 *)s_data->priv; + return regmap_read(priv->regmap, addr, (unsigned int *) val); +} + +static int ov13860_write_reg(struct camera_common_data *s_data, + u16 addr, u8 val) +{ + int err; + struct ov13860 *priv = (struct ov13860 *)s_data->priv; + + err = regmap_write(priv->regmap, addr, val); + if (err) + pr_err("%s:i2c write failed, %x = %x\n", + __func__, addr, val); + + return err; +} + +static int ov13860_write_table(struct ov13860 *priv, + const ov13860_reg table[]) +{ + return regmap_util_write_table_8(priv->regmap, + table, + NULL, 0, + OV13860_TABLE_WAIT_MS, + OV13860_TABLE_END); +} + +static int ov13860_power_on(struct camera_common_data *s_data) +{ + int err = 0; + struct ov13860 *priv = (struct ov13860 *)s_data->priv; + struct camera_common_power_rail *pw = &priv->power; + + dev_dbg(&priv->i2c_client->dev, "%s: power on\n", __func__); + + if (priv->pdata->power_on) { + err = priv->pdata->power_on(pw); + if (err) + pr_err("%s failed.\n", __func__); + else + pw->state = SWITCH_ON; + return err; + } + + /* sleep calls in the sequence below are for internal device + * signal propagation as specified by sensor vendor */ + if (pw->pwdn_gpio) + gpio_set_value(pw->pwdn_gpio, 0); + if (pw->reset_gpio) + gpio_set_value(pw->reset_gpio, 0); + usleep_range(10, 20); + + if (pw->avdd) + err = regulator_enable(pw->avdd); + if (err) + goto ov13860_avdd_fail; + + if (pw->iovdd) + err = regulator_enable(pw->iovdd); + if (err) + goto ov13860_iovdd_fail; + + usleep_range(10, 20); + if (pw->pwdn_gpio) + gpio_set_value(pw->pwdn_gpio, 1); + if (pw->reset_gpio) + gpio_set_value(pw->reset_gpio, 1); + + usleep_range(1000, 2000); + pw->state = SWITCH_ON; + return 0; + +ov13860_iovdd_fail: + regulator_disable(pw->avdd); + +ov13860_avdd_fail: + pr_err("%s failed.\n", __func__); + return -ENODEV; +} + +static int ov13860_power_off(struct camera_common_data *s_data) +{ + int err = 0; + struct ov13860 *priv = (struct ov13860 *)s_data->priv; + struct camera_common_power_rail *pw = &priv->power; + + dev_dbg(&priv->i2c_client->dev, "%s: power off\n", __func__); + + if (priv->pdata->power_off) { + err = priv->pdata->power_off(pw); + if (err) { + pr_err("%s failed.\n", __func__); + return err; + } else { + goto power_off_done; + } + } + + /* sleep calls in the sequence below are for internal device + * signal propagation as specified by sensor vendor */ + usleep_range(20, 25); + if (pw->reset_gpio) + gpio_set_value(pw->reset_gpio, 0); + if (pw->pwdn_gpio) + gpio_set_value(pw->pwdn_gpio, 0); + + if (pw->iovdd) + regulator_disable(pw->iovdd); + if (pw->avdd) + regulator_disable(pw->avdd); + +power_off_done: + pw->state = SWITCH_OFF; + return 0; +} + +static int ov13860_power_put(struct ov13860 *priv) +{ + struct camera_common_power_rail *pw = &priv->power; + if (unlikely(!pw)) + return -EFAULT; + + if (likely(pw->avdd)) + regulator_put(pw->avdd); + + if (likely(pw->iovdd)) + regulator_put(pw->iovdd); + + if (likely(pw->dvdd)) + regulator_put(pw->dvdd); + + pw->avdd = NULL; + pw->iovdd = NULL; + pw->dvdd = NULL; + + return 0; +} + +static int ov13860_power_get(struct ov13860 *priv) +{ + struct camera_common_power_rail *pw = &priv->power; + struct camera_common_pdata *pdata = priv->pdata; + const char *mclk_name; + int err = 0; + + mclk_name = priv->pdata->mclk_name ? + priv->pdata->mclk_name : "cam_mclk1"; + pw->mclk = devm_clk_get(&priv->i2c_client->dev, mclk_name); + if (IS_ERR(pw->mclk)) { + dev_err(&priv->i2c_client->dev, + "unable to get clock %s\n", mclk_name); + return PTR_ERR(pw->mclk); + } + + /* ananlog 2.7v */ + err |= camera_common_regulator_get(priv->i2c_client, + &pw->avdd, pdata->regulators.avdd); + /* digital 1.2v */ + err |= camera_common_regulator_get(priv->i2c_client, + &pw->dvdd, pdata->regulators.dvdd); + /* IO 1.8v */ + err |= camera_common_regulator_get(priv->i2c_client, + &pw->iovdd, pdata->regulators.iovdd); + + if (!err) { + pw->reset_gpio = pdata->reset_gpio; + pw->pwdn_gpio = pdata->pwdn_gpio; + } + + pw->state = SWITCH_OFF; + return err; +} + +static int ov13860_set_gain(struct ov13860 *priv, s32 val); +static int ov13860_set_frame_length(struct ov13860 *priv, s32 val); +static int ov13860_set_coarse_time(struct ov13860 *priv, s32 val); +static int ov13860_set_coarse_time_short(struct ov13860 *priv, s32 val); + +static int ov13860_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct camera_common_data *s_data = to_camera_common_data(client); + struct ov13860 *priv = (struct ov13860 *)s_data->priv; + struct v4l2_control control; + int err; + + if (!enable) + return ov13860_write_table(priv, + mode_table[OV13860_MODE_STOP_STREAM]); + + err = ov13860_write_table(priv, mode_table[s_data->mode]); + if (err) + goto exit; + + /* write list of override regs for the asking frame length, + * coarse integration time, and gain. Failures to write + * overrides are non-fatal. */ + control.id = V4L2_CID_GAIN; + err = v4l2_g_ctrl(&priv->ctrl_handler, &control); + err |= ov13860_set_gain(priv, control.value); + if (err) + dev_dbg(&client->dev, "%s: error gain override\n", __func__); + + control.id = V4L2_CID_FRAME_LENGTH; + err = v4l2_g_ctrl(&priv->ctrl_handler, &control); + err |= ov13860_set_frame_length(priv, control.value); + if (err) + dev_dbg(&client->dev, + "%s: error frame length override\n", __func__); + + control.id = V4L2_CID_COARSE_TIME; + err = v4l2_g_ctrl(&priv->ctrl_handler, &control); + err |= ov13860_set_coarse_time(priv, control.value); + if (err) + dev_dbg(&client->dev, + "%s: error coarse time override\n", __func__); + + control.id = V4L2_CID_COARSE_TIME_SHORT; + err = v4l2_g_ctrl(&priv->ctrl_handler, &control); + err |= ov13860_set_coarse_time_short(priv, control.value); + if (err) + dev_dbg(&client->dev, + "%s: error coarse time short override\n", __func__); + + err = ov13860_write_table(priv, mode_table[OV13860_MODE_START_STREAM]); + if (err) + goto exit; + + if (test_mode) + err = ov13860_write_table(priv, + mode_table[OV13860_MODE_TEST_PATTERN]); + + return 0; +exit: + dev_dbg(&client->dev, "%s: error setting stream\n", __func__); + return err; +} + +static struct v4l2_subdev_video_ops ov13860_subdev_video_ops = { + .s_stream = ov13860_s_stream, + .s_mbus_fmt = camera_common_s_fmt, + .g_mbus_fmt = camera_common_g_fmt, + .try_mbus_fmt = camera_common_try_fmt, + .enum_mbus_fmt = camera_common_enum_fmt, + .g_mbus_config = camera_common_g_mbus_config, +}; + +static struct v4l2_subdev_core_ops ov13860_subdev_core_ops = { + .g_chip_ident = camera_common_g_chip_ident, + .s_power = camera_common_s_power, +}; + +static struct v4l2_subdev_ops ov13860_subdev_ops = { + .core = &ov13860_subdev_core_ops, + .video = &ov13860_subdev_video_ops, +}; + +static struct of_device_id ov13860_of_match[] = { + { .compatible = "nvidia,ov13860", }, + { }, +}; + +static struct camera_common_sensor_ops ov13860_common_ops = { + .power_on = ov13860_power_on, + .power_off = ov13860_power_off, + .write_reg = ov13860_write_reg, + .read_reg = ov13860_read_reg, +}; + +static int ov13860_set_group_hold(struct ov13860 *priv, s32 val) +{ + int err; + int gh_en = switch_ctrl_qmenu[val]; + + if (gh_en == SWITCH_ON) { + /* group hold start */ + err = ov13860_write_reg(priv->s_data, + OV13860_GROUP_HOLD_ADDR, 0x00); + if (err) + goto fail; + } else if (gh_en == SWITCH_OFF) { + /* group hold end */ + err = ov13860_write_reg(priv->s_data, + OV13860_GROUP_HOLD_ADDR, 0x10); + if (err) + goto fail; + /* enable manual launch */ + err = ov13860_write_reg(priv->s_data, + OV13860_GROUP_HOLD_LAUNCH_ADDR, 0x00); + if (err) + goto fail; + /* quick launch */ + err = ov13860_write_reg(priv->s_data, + OV13860_GROUP_HOLD_ADDR, 0xE0); + if (err) + goto fail; + } + + return 0; + +fail: + dev_dbg(&priv->i2c_client->dev, + "%s: Group hold control error\n", __func__); + return err; +} + +static int ov13860_set_gain(struct ov13860 *priv, s32 val) +{ + ov13860_reg reg_list[3]; + int err; + u16 gain; + int i = 0; + + /* max_gain 15.5x ---> 0x350A=0x00, 0x350B=0xF8 */ + /* min_gain 1.0x ---> 0x350A=0x00, 0x350B=0x10 */ + /* translate value */ + gain = ov13860_to_gain((u32)val, OV13860_GAIN_SHIFT); + + dev_dbg(&priv->i2c_client->dev, + "%s: gain: %d\n", __func__, gain); + + ov13860_get_gain_reg(reg_list, gain); + err = ov13860_write_table(priv, reg_list); + if (err) + goto fail; + + return 0; + +fail: + dev_dbg(&priv->i2c_client->dev, + "%s: GAIN control error\n", __func__); + return err; +} + +static int ov13860_set_frame_length(struct ov13860 *priv, s32 val) +{ + ov13860_reg reg_list[3]; + int err; + u16 frame_length; + int i = 0; + + frame_length = (u16)val; + + dev_dbg(&priv->i2c_client->dev, + "%s: frame_length: %d\n", __func__, frame_length); + + ov13860_get_frame_length_regs(reg_list, frame_length); + err = ov13860_write_table(priv, reg_list); + if (err) + goto fail; + + return 0; + +fail: + dev_dbg(&priv->i2c_client->dev, + "%s: FRAME_LENGTH control error\n", __func__); + return err; +} + +static int ov13860_set_coarse_time(struct ov13860 *priv, s32 val) +{ + ov13860_reg reg_list[3]; + int err; + u16 coarse_time; + int i = 0; + + coarse_time = (u16)val; + + dev_dbg(&priv->i2c_client->dev, + "%s: coarse_time: %d\n", __func__, coarse_time); + + ov13860_get_coarse_time_regs(reg_list, coarse_time); + err = ov13860_write_table(priv, reg_list); + if (err) + goto fail; + + return 0; + +fail: + dev_dbg(&priv->i2c_client->dev, + "%s: COARSE_TIME control error\n", __func__); + return err; +} + +static int ov13860_set_coarse_time_short(struct ov13860 *priv, s32 val) +{ + ov13860_reg reg_list[3]; + int err; + struct v4l2_control hdr_control; + int hdr_en; + u16 coarse_time_short; + int i = 0; + + /* check hdr enable ctrl */ + hdr_control.id = V4L2_CID_HDR_EN; + + err = v4l2_g_ctrl(&priv->ctrl_handler, &hdr_control); + if (err < 0) { + dev_err(&priv->i2c_client->dev, + "could not find device ctrl.\n"); + return err; + } + + hdr_en = switch_ctrl_qmenu[hdr_control.value]; + if (hdr_en == SWITCH_OFF) + return 0; + + coarse_time_short = (u16)val; + + dev_dbg(&priv->i2c_client->dev, + "%s: coarse_time_short: %d\n", __func__, coarse_time_short); + + ov13860_get_coarse_time_short_regs(reg_list, coarse_time_short); + err = ov13860_write_table(priv, reg_list); + if (err) + goto fail; + + return 0; + +fail: + dev_dbg(&priv->i2c_client->dev, + "%s: COARSE_TIME_SHORT control error\n", __func__); + return err; +} + +static int ov13860_eeprom_device_release(struct ov13860 *priv) +{ + int i; + + for (i = 0; i < OV13860_EEPROM_NUM_BLOCKS; i++) { + if (priv->eeprom[i].i2c_client != NULL) { + i2c_unregister_device(priv->eeprom[i].i2c_client); + priv->eeprom[i].i2c_client = NULL; + } + } + + return 0; +} + +static int ov13860_eeprom_device_init(struct ov13860 *priv) +{ + char *dev_name = "eeprom_ov13860"; + static struct regmap_config eeprom_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + }; + int i; + int err; + struct v4l2_ctrl *ctrl; + + ctrl = v4l2_ctrl_find(&priv->ctrl_handler, V4L2_CID_EEPROM_DATA); + if (!priv->pdata->has_eeprom) { + ctrl->flags = V4L2_CTRL_FLAG_DISABLED; + return 0; + } + + for (i = 0; i < OV13860_EEPROM_NUM_BLOCKS; i++) { + priv->eeprom[i].adap = i2c_get_adapter( + priv->i2c_client->adapter->nr); + memset(&priv->eeprom[i].brd, 0, sizeof(priv->eeprom[i].brd)); + strncpy(priv->eeprom[i].brd.type, dev_name, + sizeof(priv->eeprom[i].brd.type)); + priv->eeprom[i].brd.addr = priv->pdata->eeprom_base_addr + i; + priv->eeprom[i].i2c_client = i2c_new_device( + priv->eeprom[i].adap, &priv->eeprom[i].brd); + + priv->eeprom[i].regmap = devm_regmap_init_i2c( + priv->eeprom[i].i2c_client, &eeprom_regmap_config); + if (IS_ERR(priv->eeprom[i].regmap)) { + err = PTR_ERR(priv->eeprom[i].regmap); + ov13860_eeprom_device_release(priv); + ctrl->flags = V4L2_CTRL_FLAG_DISABLED; + return err; + } + } + + return 0; +} + +static int ov13860_read_eeprom(struct ov13860 *priv, + struct v4l2_ctrl *ctrl) +{ + int err, i; + + for (i = 0; i < OV13860_EEPROM_NUM_BLOCKS; i++) { + err = regmap_bulk_read(priv->eeprom[i].regmap, 0, + &priv->eeprom_buf[i * OV13860_EEPROM_BLOCK_SIZE], + OV13860_EEPROM_BLOCK_SIZE); + if (err) + return err; + } + + for (i = 0; i < OV13860_EEPROM_SIZE; i++) + sprintf(&ctrl->string[i*2], "%02x", + priv->eeprom_buf[i]); + return 0; +} + +static int ov13860_write_eeprom(struct ov13860 *priv, + char *string) +{ + int err; + int i; + u8 curr[3]; + unsigned long data; + + for (i = 0; i < OV13860_EEPROM_SIZE; i++) { + curr[0] = string[i*2]; + curr[1] = string[i*2+1]; + curr[2] = '\0'; + + err = kstrtol(curr, 16, &data); + if (err) { + dev_err(&priv->i2c_client->dev, + "invalid eeprom string\n"); + return -EINVAL; + } + + priv->eeprom_buf[i] = (u8)data; + err = regmap_write(priv->eeprom[i >> 8].regmap, + i & 0xFF, (u8)data); + if (err) + return err; + msleep(20); + } + return 0; +} + +static int ov13860_read_otp(struct ov13860 *priv, u8 *buf, + u16 addr, int size) +{ + int err; + + /* PLL setting */ + err = ov13860_write_reg(priv->s_data, OV13860_ISP_CTRL_ADDR, 0x01); + if (err) + return err; + /* Start streaming before write or read */ + err = ov13860_write_reg(priv->s_data, 0x0100, 0x01); + if (err) + return err; + msleep(20); + + /* By default otp loading works in auto mode, but we can switch to + manual mode through OV13860_OTP_MODE_CTRL_ADDR[6] and the start + addr and end addr of manual mode can be configured by registers + accordingly + */ + + /* Loading enable */ + err = ov13860_write_reg(priv->s_data, OV13860_OTP_LOAD_CTRL_ADDR, 0x01); + if (err) + return err; + + msleep(20); + err = regmap_bulk_read(priv->regmap, addr, buf, size); + if (err) + return err; + + return 0; +} + +static int ov13860_otp_setup(struct ov13860 *priv) +{ + int err; + int i; + struct v4l2_ctrl *ctrl; + u8 otp_buf[OV13860_OTP_SIZE]; + + err = camera_common_s_power(priv->subdev, true); + if (err) + return -ENODEV; + + ov13860_read_otp(priv, &otp_buf[0], + OV13860_OTP_SRAM_START_ADDR, + OV13860_OTP_SIZE); + + ctrl = v4l2_ctrl_find(&priv->ctrl_handler, V4L2_CID_OTP_DATA); + if (!ctrl) { + dev_err(&priv->i2c_client->dev, + "could not find device ctrl.\n"); + return -EINVAL; + } + + for (i = 0; i < OV13860_OTP_SIZE; i++) + sprintf(&ctrl->string[i*2], "%02x", + otp_buf[i]); + ctrl->cur.string = ctrl->string; + + err = camera_common_s_power(priv->subdev, false); + if (err) + return -ENODEV; + + return 0; +} + +static int ov13860_fuse_id_setup(struct ov13860 *priv) +{ + int err; + int i; + struct v4l2_ctrl *ctrl; + u8 fuse_id[OV13860_FUSE_ID_SIZE]; + + err = camera_common_s_power(priv->subdev, true); + if (err) + return -ENODEV; + + /* Here we read ID from OTP */ + /* Actually, it can also be read from registers */ + /* Bit[23:0] = [0x380A] | [0x380B] | [0x380C] */ + ov13860_read_otp(priv, &fuse_id[0], + OV13860_FUSE_ID_OTP_BASE_ADDR, + OV13860_FUSE_ID_SIZE); + + ctrl = v4l2_ctrl_find(&priv->ctrl_handler, V4L2_CID_FUSE_ID); + if (!ctrl) { + dev_err(&priv->i2c_client->dev, + "could not find device ctrl.\n"); + return -EINVAL; + } + + for (i = 0; i < OV13860_FUSE_ID_SIZE; i++) + sprintf(&ctrl->string[i*2], "%02x", + fuse_id[i]); + ctrl->cur.string = ctrl->string; + + err = camera_common_s_power(priv->subdev, false); + if (err) + return -ENODEV; + + return 0; + + return 0; +} + +static int ov13860_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov13860 *priv = + container_of(ctrl->handler, struct ov13860, ctrl_handler); + int err = 0; + + if (priv->power.state == SWITCH_OFF) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EEPROM_DATA: + err = ov13860_read_eeprom(priv, ctrl); + if (err) + return err; + break; + default: + pr_err("%s: unknown ctrl id.\n", __func__); + return -EINVAL; + } + + return err; +} + +static int ov13860_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov13860 *priv = + container_of(ctrl->handler, struct ov13860, ctrl_handler); + int err = 0; + + if (priv->power.state == SWITCH_OFF) + return 0; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + err = ov13860_set_gain(priv, ctrl->val); + break; + case V4L2_CID_FRAME_LENGTH: + err = ov13860_set_frame_length(priv, ctrl->val); + break; + case V4L2_CID_COARSE_TIME: + err = ov13860_set_coarse_time(priv, ctrl->val); + break; + case V4L2_CID_COARSE_TIME_SHORT: + err = ov13860_set_coarse_time_short(priv, ctrl->val); + break; + case V4L2_CID_GROUP_HOLD: + err = ov13860_set_group_hold(priv, ctrl->val); + break; + case V4L2_CID_EEPROM_DATA: + if (!ctrl->string[0]) + break; + err = ov13860_write_eeprom(priv, ctrl->string); + if (err) + return err; + break; + case V4L2_CID_HDR_EN: + break; + default: + pr_err("%s: unknown ctrl id.\n", __func__); + return -EINVAL; + } + + return err; +} + +static int ov13860_ctrls_init(struct ov13860 *priv) +{ + struct i2c_client *client = priv->i2c_client; + struct v4l2_ctrl *ctrl; + int num_ctrls; + int err; + int i; + + dev_dbg(&client->dev, "%s++\n", __func__); + + num_ctrls = ARRAY_SIZE(ctrl_config_list); + v4l2_ctrl_handler_init(&priv->ctrl_handler, num_ctrls); + + for (i = 0; i < num_ctrls; i++) { + ctrl = v4l2_ctrl_new_custom(&priv->ctrl_handler, + &ctrl_config_list[i], NULL); + if (ctrl == NULL) { + dev_err(&client->dev, "Failed to init %s ctrl\n", + ctrl_config_list[i].name); + continue; + } + + if (ctrl_config_list[i].type == V4L2_CTRL_TYPE_STRING && + ctrl_config_list[i].flags & V4L2_CTRL_FLAG_READ_ONLY) { + ctrl->string = devm_kzalloc(&client->dev, + ctrl_config_list[i].max + 1, GFP_KERNEL); + if (!ctrl->string) { + dev_err(&client->dev, + "Failed to allocate otp data\n"); + return -ENOMEM; + } + } + priv->ctrls[i] = ctrl; + } + + priv->num_ctrls = num_ctrls; + priv->subdev->ctrl_handler = &priv->ctrl_handler; + if (priv->ctrl_handler.error) { + dev_err(&client->dev, "Error %d adding controls\n", + priv->ctrl_handler.error); + err = priv->ctrl_handler.error; + goto error; + } + + err = v4l2_ctrl_handler_setup(&priv->ctrl_handler); + if (err) { + dev_err(&client->dev, + "Error %d setting default controls\n", err); + goto error; + } + + err = ov13860_otp_setup(priv); + if (err) { + dev_err(&client->dev, + "Error %d reading otp data\n", err); + goto error; + } + + err = ov13860_fuse_id_setup(priv); + if (err) { + dev_err(&client->dev, + "Error %d reading fuse id data\n", err); + goto error; + } + + return 0; + +error: + v4l2_ctrl_handler_free(&priv->ctrl_handler); + return err; +} + +MODULE_DEVICE_TABLE(of, ov13860_of_match); + +static struct camera_common_pdata *ov13860_parse_dt(struct i2c_client *client) +{ + struct device_node *np = client->dev.of_node; + struct camera_common_pdata *board_priv_pdata; + const struct of_device_id *match; + + match = of_match_device(ov13860_of_match, &client->dev); + if (!match) { + dev_err(&client->dev, "Failed to find matching dt id\n"); + return NULL; + } + + board_priv_pdata = devm_kzalloc(&client->dev, + sizeof(*board_priv_pdata), GFP_KERNEL); + if (!board_priv_pdata) { + dev_err(&client->dev, "Failed to allocate pdata\n"); + return NULL; + } + + of_property_read_string(np, "mclk", &board_priv_pdata->mclk_name); + board_priv_pdata->pwdn_gpio = of_get_named_gpio(np, "pwdn-gpios", 0); + board_priv_pdata->reset_gpio = of_get_named_gpio(np, "reset-gpios", 0); + + of_property_read_string(np, "avdd-reg", + &board_priv_pdata->regulators.avdd); + of_property_read_string(np, "dvdd-reg", + &board_priv_pdata->regulators.dvdd); + of_property_read_string(np, "iovdd-reg", + &board_priv_pdata->regulators.iovdd); + of_property_read_u32(np, "eeprom-addr", + &board_priv_pdata->eeprom_base_addr); + + board_priv_pdata->has_eeprom = of_property_read_bool(np, "has-eeprom"); + + return board_priv_pdata; +} + +static int ov13860_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct camera_common_data *common_data; + struct ov13860 *priv; + struct soc_camera_subdev_desc *ssdd; + struct tegra_camera_platform_data *ov13860_camera_data; + char node_name[16]; + int err; + + pr_info("[OV13860]: probing v4l2 sensor.\n"); + + common_data = devm_kzalloc(&client->dev, + sizeof(struct camera_common_data), GFP_KERNEL); + if (!common_data) { + dev_err(&client->dev, "unable to allocate memory!\n"); + return -ENOMEM; + } + + priv = devm_kzalloc(&client->dev, + sizeof(struct ov13860) + sizeof(struct v4l2_ctrl *) * + ARRAY_SIZE(ctrl_config_list), + GFP_KERNEL); + if (!priv) { + dev_err(&client->dev, "unable to allocate memory!\n"); + return -ENOMEM; + } + + priv->regmap = devm_regmap_init_i2c(client, &sensor_regmap_config); + if (IS_ERR(priv->regmap)) { + dev_err(&client->dev, + "regmap init failed: %ld\n", PTR_ERR(priv->regmap)); + return -ENODEV; + } + + ssdd = soc_camera_i2c_to_desc(client); + ov13860_camera_data = (struct tegra_camera_platform_data *) + ssdd->drv_priv; + if (!ov13860_camera_data) { + dev_err(&client->dev, "unable to find iclink module name\n"); + return -EFAULT; + } + + snprintf(node_name, sizeof(node_name), "ov13860_%02x", client->addr); + dev_dbg(&client->dev, "%s: dt node name %s\n", __func__, node_name); + client->dev.of_node = of_find_node_by_name(NULL, node_name); + + if (client->dev.of_node) + priv->pdata = ov13860_parse_dt(client); + else + priv->pdata = ssdd->dev_priv; + + if (!priv->pdata) { + dev_err(&client->dev, "unable to get platform data\n"); + return -EFAULT; + } + + common_data->ops = &ov13860_common_ops; + common_data->ctrl_handler = &priv->ctrl_handler; + common_data->i2c_client = client; + common_data->frmfmt = &ov13860_frmfmt[0]; + common_data->colorfmt = camera_common_find_datafmt( + OV13860_DEFAULT_DATAFMT); + common_data->power = &priv->power; + common_data->priv = (void *)priv; + common_data->numfmts = ARRAY_SIZE(ov13860_frmfmt); + common_data->def_mode = OV13860_DEFAULT_MODE; + common_data->def_width = OV13860_DEFAULT_WIDTH; + common_data->def_height = OV13860_DEFAULT_HEIGHT; + common_data->def_clk_freq = OV13860_DEFAULT_CLK_FREQ; + common_data->csi_port = (int)ov13860_camera_data->port; + common_data->numlanes = ov13860_camera_data->lanes; + + priv->i2c_client = client; + priv->s_data = common_data; + priv->subdev = &common_data->subdev; + + err = ov13860_power_get(priv); + if (err) + return err; + + camera_common_create_debugfs(common_data, node_name); + + v4l2_i2c_subdev_init(&common_data->subdev, client, &ov13860_subdev_ops); + + err = ov13860_ctrls_init(priv); + if (err) + return err; + + /* eeprom interface */ + err = ov13860_eeprom_device_init(priv); + if (err) + dev_err(&client->dev, + "Failed to allocate eeprom register map: %d\n", err); + + return 0; +} + +static int +ov13860_remove(struct i2c_client *client) +{ + struct soc_camera_subdev_desc *ssdd; + struct camera_common_data *s_data = to_camera_common_data(client); + struct ov13860 *priv = (struct ov13860 *)s_data->priv; + + ssdd = soc_camera_i2c_to_desc(client); + if (ssdd->free_bus) + ssdd->free_bus(ssdd); + + v4l2_ctrl_handler_free(&priv->ctrl_handler); + ov13860_power_put(priv); + camera_common_remove_debugfs(s_data); + + return 0; +} + +static const struct i2c_device_id ov13860_id[] = { + { "ov13860_v4l2", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, ov13860_id); + +static struct i2c_driver ov13860_i2c_driver = { + .driver = { + .name = "ov13860_v4l2", + .owner = THIS_MODULE, + }, + .probe = ov13860_probe, + .remove = ov13860_remove, + .id_table = ov13860_id, +}; + +module_i2c_driver(ov13860_i2c_driver); + +MODULE_DESCRIPTION("SoC Camera driver for Omnivison OV13860"); +MODULE_AUTHOR("NVIDIA Corporation"); +MODULE_LICENSE("GPL v2"); diff --git a/include/media/camera_common.h b/include/media/camera_common.h index 9f34b54a840..663471501ac 100644 --- a/include/media/camera_common.h +++ b/include/media/camera_common.h @@ -86,6 +86,7 @@ struct camera_common_pdata { unsigned int pwdn_gpio; unsigned int reset_gpio; unsigned int af_gpio; + unsigned int eeprom_base_addr; bool ext_reg; int (*power_on)(struct camera_common_power_rail *pw); int (*power_off)(struct camera_common_power_rail *pw); diff --git a/include/media/ov13860.h b/include/media/ov13860.h new file mode 100644 index 00000000000..78c1a16e8b4 --- /dev/null +++ b/include/media/ov13860.h @@ -0,0 +1,126 @@ +/** + * Copyright (c) 2014-2015, NVIDIA Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __OV13860_H__ +#define __OV13860_H__ + +#include /* For IOCTL macros */ +#include +#include + +#define OV13860_IOCTL_SET_MODE _IOW('o', 1, \ + struct ov13860_mode) +#define OV13860_IOCTL_GET_STATUS _IOR('o', 2, __u8) +#define OV13860_IOCTL_SET_FRAME_LENGTH _IOW('o', 3, __u32) +#define OV13860_IOCTL_SET_COARSE_TIME _IOW('o', 4, __u32) +#define OV13860_IOCTL_SET_GAIN _IOW('o', 5, __u16) +#define OV13860_IOCTL_GET_SENSORDATA _IOR('o', 6, \ + struct ov13860_sensordata) +#define OV13860_IOCTL_SET_GROUP_HOLD _IOW('o', 7, struct ov13860_ae) +#define OV13860_IOCTL_SET_HDR_COARSE_TIME _IOW('o', 8, struct ov13860_hdr) +#define OV13860_IOCTL_SET_POWER _IOW('o', 20, __u32) + +#define OV13860_EEPROM_SIZE 1024 +#define OV13860_EEPROM_STR_SIZE (OV13860_EEPROM_SIZE * 2) +#define OV13860_EEPROM_BLOCK_SIZE (1 << 8) +#define OV13860_EEPROM_NUM_BLOCKS \ + (OV13860_EEPROM_SIZE / OV13860_EEPROM_BLOCK_SIZE) + +#define OV13860_ISP_CTRL_ADDR 0x5000 +#define OV13860_OTP_PROGRAME_CTRL_ADDR 0x3D80 +#define OV13860_OTP_LOAD_CTRL_ADDR 0x3D81 +#define OV13860_OTP_MODE_CTRL_ADDR 0x3D84 +#define OV13860_OTP_PROGRAME_START_ADDR_MSB 0x3D88 +#define OV13860_OTP_PROGRAME_START_ADDR_LSB 0x3D89 +#define OV13860_OTP_PROGRAME_END_ADDR_MSB 0x3D8A +#define OV13860_OTP_PROGRAME_END_ADDR_LSB 0x3D8B +#define OV13860_OTP_SIZE 0xA00 +#define OV13860_OTP_SRAM_START_ADDR 0x7000 +#define OV13860_OTP_STR_SIZE (OV13860_OTP_SIZE * 2) + +#define OV13860_FUSE_ID_OTP_BASE_ADDR 0x7000 +#define OV13860_FUSE_ID_SIZE 3 +#define OV13860_FUSE_ID_STR_SIZE (OV13860_FUSE_ID_SIZE * 2) + +#define OV13860_FRAME_LENGTH_ADDR_MSB 0x380E +#define OV13860_FRAME_LENGTH_ADDR_LSB 0x380F +#define OV13860_COARSE_TIME_ADDR_MSB 0x3501 +#define OV13860_COARSE_TIME_ADDR_LSB 0x3502 +#define OV13860_COARSE_TIME_SHORT_ADDR_MSB 0x3507 +#define OV13860_COARSE_TIME_SHORT_ADDR_LSB 0x3508 +#define OV13860_GAIN_ADDR_MSB 0x350A +#define OV13860_GAIN_ADDR_LSB 0x350B +#define OV13860_GAIN_SHORT_ADDR_MSB 0x350E +#define OV13860_GAIN_SHORT_ADDR_LSB 0x350F +#define OV13860_GROUP_HOLD_ADDR 0x3208 +#define OV13860_GROUP_HOLD_LAUNCH_ADDR 0x320B + +struct ov13860_mode { + __u32 xres; + __u32 yres; + __u32 frame_length; + __u32 coarse_time; + __u32 coarse_time_short; + __u16 gain; + __u8 hdr_en; +}; + +struct ov13860_hdr { + __u32 coarse_time_long; + __u32 coarse_time_short; +}; + +struct ov13860_ae { + __u32 frame_length; + __u8 frame_length_enable; + __u32 coarse_time; + __u32 coarse_time_short; + __u8 coarse_time_enable; + __s32 gain; + __u8 gain_enable; +}; + +struct ov13860_sensordata { + __u32 fuse_id_size; + __u8 fuse_id[OV13860_FUSE_ID_SIZE]; +}; + +#ifdef __KERNEL__ +struct ov13860_power_rail { + struct regulator *dvdd; + struct regulator *avdd; + struct regulator *iovdd; + struct regulator *ext_reg1; + struct regulator *ext_reg2; + struct clk *mclk; + unsigned int pwdn_gpio; + unsigned int cam1_gpio; + unsigned int reset_gpio; + unsigned int af_gpio; +}; + +struct ov13860_platform_data { + const char *mclk_name; /* NULL for default default_mclk */ + unsigned int cam1_gpio; + unsigned int reset_gpio; + unsigned int af_gpio; + bool ext_reg; + int (*power_on)(struct ov13860_power_rail *pw); + int (*power_off)(struct ov13860_power_rail *pw); +}; +#endif /* __KERNEL__ */ + +#endif /* __OV13860_H__ */ -- 2.39.2