diff options
author | Lihui Cui <lcui@nvidia.com> | 2013-01-18 10:12:53 +0900 |
---|---|---|
committer | Riham Haidar <rhaidar@nvidia.com> | 2013-03-13 19:47:42 -0700 |
commit | 94001d3f174ea954d261dac864dd73136ebf07a7 (patch) | |
tree | 61d01f55beb815faa6f3c0105776bf0f39ef5c63 /drivers/media | |
parent | 18bc190fadec263698d2da035b40b8f8dc959c0b (diff) |
media: tegra: add ov5693 with ad5823 focuser
bug 1218661
Change-Id: Id6c8d697c8a9fa8f0d4414802ad866783c00d695
Signed-off-by: Lihui Cui <lcui@nvidia.com>
Reviewed-on: http://git-master/r/192227
GVS: Gerrit_Virtual_Submit
Reviewed-by: Dan Willemsen <dwillemsen@nvidia.com>
Diffstat (limited to 'drivers/media')
-rw-r--r-- | drivers/media/video/tegra/Kconfig | 14 | ||||
-rw-r--r-- | drivers/media/video/tegra/Makefile | 2 | ||||
-rw-r--r-- | drivers/media/video/tegra/ad5823.c | 241 | ||||
-rw-r--r-- | drivers/media/video/tegra/ov5693.c | 1989 |
4 files changed, 2246 insertions, 0 deletions
diff --git a/drivers/media/video/tegra/Kconfig b/drivers/media/video/tegra/Kconfig index 8d59c895a6e4..cdf2b9c38297 100644 --- a/drivers/media/video/tegra/Kconfig +++ b/drivers/media/video/tegra/Kconfig @@ -81,6 +81,13 @@ config VIDEO_OV9772 This is a driver for the OV9772 camera sensor for use with the tegra isp. +config VIDEO_OV5693 + tristate "OV5693 camera sensor support" + depends on I2C && ARCH_TEGRA + ---help--- + This is a driver for the OV5693 camera sensor + for use with the tegra isp. + config TORCH_SSL3250A tristate "SSL3250A flash/torch support" depends on I2C && ARCH_TEGRA @@ -120,6 +127,13 @@ config VIDEO_AD5820 This is a driver for the AD5820 focuser for use with the tegra isp. +config VIDEO_AD5823 + tristate "AD5823 focuser support" + depends on I2C && ARCH_TEGRA + ---help--- + This is a driver for the AD5823 focuser + for use with the tegra isp. + config VIDEO_AD5816 tristate "AD5816 focuser support" depends on I2C && ARCH_TEGRA diff --git a/drivers/media/video/tegra/Makefile b/drivers/media/video/tegra/Makefile index d643b3a71d4b..f117fa21c61a 100644 --- a/drivers/media/video/tegra/Makefile +++ b/drivers/media/video/tegra/Makefile @@ -25,3 +25,5 @@ obj-$(CONFIG_VIDEO_AD5820) += ad5820.o obj-$(CONFIG_VIDEO_AD5816) += ad5816.o obj-$(CONFIG_VIDEO_IMX091) += imx091.o obj-$(CONFIG_VIDEO_IMX132) += imx132.o +obj-$(CONFIG_VIDEO_OV5693) += ov5693.o +obj-$(CONFIG_VIDEO_AD5823) += ad5823.o diff --git a/drivers/media/video/tegra/ad5823.c b/drivers/media/video/tegra/ad5823.c new file mode 100644 index 000000000000..5666a103832b --- /dev/null +++ b/drivers/media/video/tegra/ad5823.c @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2013, 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 <http://www.gnu.org/licenses/>. + */ + +#include <linux/delay.h> +#include <linux/fs.h> +#include <linux/i2c.h> +#include <linux/miscdevice.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <media/ad5823.h> + +#define POS_LOW (32) +#define POS_HIGH (850) +#define SETTLETIME_MS (70) +#define FOCAL_LENGTH (4.507f) +#define FNUMBER (2.8f) + +#define AD5823_MAX_RETRIES (3) + +struct ad5823_info { + struct i2c_client *i2c_client; + struct regulator *regulator; + struct ad5823_config config; + struct ad5823_platform_data *pdata; + struct miscdevice miscdev; + struct regmap *regmap; +}; + +static int ad5823_set_position(struct ad5823_info *info, u32 position) +{ + int ret = 0; + + if (position < info->config.pos_low || + position > info->config.pos_high) { + dev_err(&info->i2c_client->dev, + "%s: position(%d) out of bound([%d, %d])\n", + __func__, position, info->config.pos_low, + info->config.pos_high); + if (position < info->config.pos_low) + position = info->config.pos_low; + else + position = info->config.pos_high; + } + + ret |= regmap_write(info->regmap, AD5823_MODE, 0); + ret |= regmap_write(info->regmap, AD5823_VCM_CODE_MSB, + ((position >> 8) & 0x3) | (1 << 2)); + ret |= regmap_write(info->regmap, AD5823_VCM_CODE_LSB, + position & 0xFF); + + return ret; +} + +static long ad5823_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct ad5823_info *info = file->private_data; + + switch (cmd) { + case AD5823_IOCTL_GET_CONFIG: + { + if (copy_to_user((void __user *) arg, + &info->config, + sizeof(info->config))) { + dev_err(&info->i2c_client->dev, "%s: 0x%x\n", + __func__, __LINE__); + return -EFAULT; + } + + break; + } + case AD5823_IOCTL_SET_POSITION: + return ad5823_set_position(info, (u32) arg); + default: + return -EINVAL; + } + + return 0; +} + +static int ad5823_open(struct inode *inode, struct file *file) +{ + int err = 0; + struct miscdevice *miscdev = file->private_data; + struct ad5823_info *info = dev_get_drvdata(miscdev->parent); + + if (info->regulator) + err = regulator_enable(info->regulator); + + if (info->pdata->power_on) + err = info->pdata->power_on(info->pdata); + + file->private_data = info; + msleep(100); + return err; +} + +int ad5823_release(struct inode *inode, struct file *file) +{ + struct ad5823_info *info = file->private_data; + + if (info->regulator) + regulator_disable(info->regulator); + file->private_data = NULL; + + if (info->pdata->power_off) + info->pdata->power_off(info->pdata); + + return 0; +} + + +static const struct file_operations ad5823_fileops = { + .owner = THIS_MODULE, + .open = ad5823_open, + .unlocked_ioctl = ad5823_ioctl, + .release = ad5823_release, +}; + +static struct miscdevice ad5823_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "focuser", + .fops = &ad5823_fileops, +}; + +static int ad5823_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err; + struct ad5823_info *info; + static struct regmap_config ad5823_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + }; + + + dev_dbg(&client->dev, "ad5823: probing sensor.\n"); + + info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); + if (!info) { + dev_err(&client->dev, "ad5823: Unable to allocate memory!\n"); + return -ENOMEM; + } + + ad5823_device.parent = &client->dev; + err = misc_register(&ad5823_device); + if (err) { + dev_err(&client->dev, "ad5823: Unable to register misc device!\n"); + goto ERROR_RET; + } + + if (client->dev.platform_data) { + info->pdata = client->dev.platform_data; + } else { + info->pdata = NULL; + dev_err(&client->dev, "ad5823: Platform data missing."); + err = EINVAL; + goto ERROR_RET; + } + + info->regulator = devm_regulator_get(&client->dev, "vdd"); + if (IS_ERR_OR_NULL(info->regulator)) { + dev_err(&client->dev, "unable to get regulator %s\n", + dev_name(&client->dev)); + info->regulator = NULL; + } else { + regulator_enable(info->regulator); + } + + info->regmap = devm_regmap_init_i2c(client, &ad5823_regmap_config); + if (IS_ERR(info->regmap)) { + err = PTR_ERR(info->regmap); + dev_err(&client->dev, + "Failed to allocate register map: %d\n", err); + goto ERROR_RET; + } + + + info->i2c_client = client; + info->config.settle_time = SETTLETIME_MS; + info->config.focal_length = FOCAL_LENGTH; + info->config.fnumber = FNUMBER; + info->config.pos_low = POS_LOW; + info->config.pos_high = POS_HIGH; + info->miscdev = ad5823_device; + + i2c_set_clientdata(client, info); + + return 0; + +ERROR_RET: + if (info->regulator) + regulator_disable(info->regulator); + + misc_deregister(&ad5823_device); + return err; +} + +static int ad5823_remove(struct i2c_client *client) +{ + struct ad5823_info *info; + info = i2c_get_clientdata(client); + misc_deregister(&ad5823_device); + return 0; +} + +static const struct i2c_device_id ad5823_id[] = { + { "ad5823", 0 }, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, ad5823_id); + +static struct i2c_driver ad5823_i2c_driver = { + .driver = { + .name = "ad5823", + .owner = THIS_MODULE, + }, + .probe = ad5823_probe, + .remove = ad5823_remove, + .id_table = ad5823_id, +}; + +module_i2c_driver(ad5823_i2c_driver); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/tegra/ov5693.c b/drivers/media/video/tegra/ov5693.c new file mode 100644 index 000000000000..27e852bed07b --- /dev/null +++ b/drivers/media/video/tegra/ov5693.c @@ -0,0 +1,1989 @@ +/* + * Copyright (c) 2013, 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 <http://www.gnu.org/licenses/>. + */ + +#include <linux/delay.h> +#include <linux/fs.h> +#include <linux/i2c.h> +#include <linux/miscdevice.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/regulator/consumer.h> +#include <linux/gpio.h> +#include <linux/module.h> +#include <linux/regmap.h> + +#include <media/ov5693.h> + +#define OV5693_ID 0x5693 +#define OV5693_SENSOR_TYPE NVC_IMAGER_TYPE_RAW +#define OV5693_RES_CHG_WAIT_TIME_MS 100 +#define OV5693_SIZEOF_I2C_BUF 16 +#define OV5693_TABLE_WAIT_MS 0 +#define OV5693_TABLE_END 1 +#define OV5693_TABLE_RESET 2 +#define OV5693_TABLE_RESET_TIMEOUT 50 +#define OV5693_LENS_MAX_APERTURE 0 /* _INT2FLOAT_DIVISOR */ +#define OV5693_LENS_FNUMBER 0 /* _INT2FLOAT_DIVISOR */ +#define OV5693_LENS_FOCAL_LENGTH 6120 /* _INT2FLOAT_DIVISOR */ +#define OV5693_LENS_VIEW_ANGLE_H 60000 /* _INT2FLOAT_DIVISOR */ +#define OV5693_LENS_VIEW_ANGLE_V 60000 /* _INT2FLOAT_DIVISOR */ + +static struct nvc_gpio_init ov5693_gpio[] = { + { OV5693_GPIO_TYPE_PWRDN, GPIOF_OUT_INIT_LOW, "pwrdn", false, true, }, +}; + +struct ov5693_info { + atomic_t in_use; + struct i2c_client *i2c_client; + struct ov5693_platform_data *pdata; + struct miscdevice miscdev; + int pwr_api; + int pwr_dev; + struct nvc_gpio gpio[ARRAY_SIZE(ov5693_gpio)]; + struct ov5693_power_rail regulators; + bool power_on; + u32 mode_index; + bool mode_valid; + bool mode_enable; + unsigned test_pattern; + struct nvc_imager_static_nvc sdata; + u8 bin_en; + struct ov5693_fuseid fuseid; + struct regmap *regmap; +}; + +struct ov5693_reg { + u16 addr; + u16 val; +}; + +struct ov5693_mode_data { + struct nvc_imager_mode sensor_mode; + struct nvc_imager_dynamic_nvc sensor_dnvc; + struct ov5693_reg *p_mode_i2c; +}; + +static struct ov5693_platform_data ov5693_dflt_pdata = { + .cfg = 0, + .num = 0, + .dev_name = "camera", +}; + +/* + * NOTE: static vs dynamic + * If a member in the nvc_imager_static_nvc structure is not actually + * static data, then leave blank and add the parameter to the parameter + * read function that dynamically reads the data. The NVC user driver + * will call the parameter read for the data if the member data is 0. + * If the dynamic data becomes static during probe (a one time read + * such as device ID) then add the dynamic read to the _sdata_init + * function. + */ +static struct nvc_imager_static_nvc ov5693_dflt_sdata = { + .api_version = NVC_IMAGER_API_STATIC_VER, + .sensor_type = OV5693_SENSOR_TYPE, + .bits_per_pixel = 10, + .sensor_id = OV5693_ID, + .sensor_id_minor = 0, + .focal_len = OV5693_LENS_FOCAL_LENGTH, + .max_aperture = OV5693_LENS_MAX_APERTURE, + .fnumber = OV5693_LENS_FNUMBER, + .view_angle_h = OV5693_LENS_VIEW_ANGLE_H, + .view_angle_v = OV5693_LENS_VIEW_ANGLE_V, + .res_chg_wait_time = OV5693_RES_CHG_WAIT_TIME_MS, +}; + +static const struct ov5693_reg ov5693_2592x1944_i2c[] = { + {OV5693_TABLE_RESET, 0},/* Including sw reset */ + {0x3001, 0x0a}, + {0x3002, 0x80}, + {0x3006, 0x00}, + {0x3011, 0x21}, + {0x3012, 0x09}, + {0x3013, 0x10}, + {0x3014, 0x00}, + {0x3015, 0x08}, + {0x3016, 0xf0}, + {0x3017, 0xf0}, + {0x3018, 0xf0}, + {0x301b, 0xb4}, + {0x301d, 0x02}, + {0x3021, 0x00}, + {0x3022, 0x01}, + {0x3028, 0x44}, + {0x3090, 0x02}, + {0x3091, 0x0e}, + {0x3092, 0x00}, + {0x3093, 0x00}, + {0x3098, 0x03}, + {0x3099, 0x1e}, + {0x309a, 0x02}, + {0x309b, 0x01}, + {0x309c, 0x00}, + {0x30a0, 0xd2}, + {0x30a2, 0x01}, + {0x30b2, 0x00}, + {0x30b3, 0x68}, + {0x30b4, 0x03}, + {0x30b5, 0x04}, + {0x30b6, 0x01}, + {0x3104, 0x21}, + {0x3106, 0x00}, + {0x3400, 0x04}, + {0x3401, 0x00}, + {0x3402, 0x04}, + {0x3403, 0x00}, + {0x3404, 0x04}, + {0x3405, 0x00}, + {0x3406, 0x01}, + {0x3500, 0x00}, + {0x3501, 0x7b}, + {0x3502, 0x00}, + {0x3503, 0x07}, + {0x3504, 0x00}, + {0x3505, 0x00}, + {0x3506, 0x00}, + {0x3507, 0x02}, + {0x3508, 0x00}, + {0x3509, 0x08}, + {0x350a, 0x00}, + {0x350b, 0x40}, + {0x3601, 0x0a}, + {0x3602, 0x18}, + {0x3612, 0x80}, + {0x3620, 0x54}, + {0x3621, 0xc7}, + {0x3622, 0x0f}, + {0x3625, 0x10}, + {0x3630, 0x55}, + {0x3631, 0xf4}, + {0x3632, 0x00}, + {0x3633, 0x34}, + {0x3634, 0x02}, + {0x364d, 0x0d}, + {0x364f, 0xdd}, + {0x3660, 0x04}, + {0x3662, 0x10}, + {0x3663, 0xf1}, + {0x3665, 0x00}, + {0x3666, 0x20}, + {0x3667, 0x00}, + {0x366a, 0x80}, + {0x3680, 0xe0}, + {0x3681, 0x00}, + {0x3700, 0x42}, + {0x3701, 0x14}, + {0x3702, 0xa0}, + {0x3703, 0xd8}, + {0x3704, 0x78}, + {0x3705, 0x02}, + {0x3708, 0xe2}, + {0x3709, 0xc3}, + {0x370a, 0x00}, + {0x370b, 0x20}, + {0x370c, 0x0c}, + {0x370d, 0x11}, + {0x370e, 0x00}, + {0x370f, 0x40}, + {0x3710, 0x00}, + {0x371a, 0x1c}, + {0x371b, 0x05}, + {0x371c, 0x01}, + {0x371e, 0xa1}, + {0x371f, 0x0c}, + {0x3721, 0x00}, + {0x3726, 0x00}, + {0x372a, 0x01}, + {0x3730, 0x10}, + {0x3738, 0x22}, + {0x3739, 0xe5}, + {0x373a, 0x50}, + {0x373b, 0x02}, + {0x373c, 0x41}, + {0x373f, 0x02}, + {0x3740, 0x42}, + {0x3741, 0x02}, + {0x3742, 0x18}, + {0x3743, 0x01}, + {0x3744, 0x02}, + {0x3747, 0x10}, + {0x374c, 0x04}, + {0x3751, 0xf0}, + {0x3752, 0x00}, + {0x3753, 0x00}, + {0x3754, 0xc0}, + {0x3755, 0x00}, + {0x3756, 0x1a}, + {0x3758, 0x00}, + {0x3759, 0x0f}, + {0x376b, 0x44}, + {0x375c, 0x04}, + {0x3776, 0x00}, + {0x377f, 0x08}, + {0x3780, 0x22}, + {0x3781, 0x0c}, + {0x3784, 0x2c}, + {0x3785, 0x1e}, + {0x378f, 0xf5}, + {0x3791, 0xb0}, + {0x3795, 0x00}, + {0x3796, 0x64}, + {0x3797, 0x11}, + {0x3798, 0x30}, + {0x3799, 0x41}, + {0x379a, 0x07}, + {0x379b, 0xb0}, + {0x379c, 0x0c}, + {0x37c5, 0x00}, + {0x37c6, 0x00}, + {0x37c7, 0x00}, + {0x37c9, 0x00}, + {0x37ca, 0x00}, + {0x37cb, 0x00}, + {0x37de, 0x00}, + {0x37df, 0x00}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x0a}, + {0x3805, 0x3f}, + {0x3806, 0x07}, + {0x3807, 0xa3}, + {0x3808, 0x0a}, + {0x3809, 0x20}, + {0x380a, 0x07}, + {0x380b, 0x98}, + {0x380c, 0x0a}, + {0x380d, 0x80}, + {0x380e, 0x07}, + {0x380f, 0xc0}, + {0x3810, 0x00}, + {0x3811, 0x02}, + {0x3812, 0x00}, + {0x3813, 0x02}, + {0x3814, 0x11}, + {0x3815, 0x11}, + {0x3820, 0x00}, + {0x3821, 0x1e}, + {0x3823, 0x00}, + {0x3824, 0x00}, + {0x3825, 0x00}, + {0x3826, 0x00}, + {0x3827, 0x00}, + {0x382a, 0x04}, + {0x3a04, 0x06}, + {0x3a05, 0x14}, + {0x3a06, 0x00}, + {0x3a07, 0xfe}, + {0x3b00, 0x00}, + {0x3b02, 0x00}, + {0x3b03, 0x00}, + {0x3b04, 0x00}, + {0x3b05, 0x00}, + {0x3d00, 0x00}, + {0x3d01, 0x00}, + {0x3d02, 0x00}, + {0x3d03, 0x00}, + {0x3d04, 0x00}, + {0x3d05, 0x00}, + {0x3d06, 0x00}, + {0x3d07, 0x00}, + {0x3d08, 0x00}, + {0x3d09, 0x00}, + {0x3d0a, 0x00}, + {0x3d0b, 0x00}, + {0x3d0c, 0x00}, + {0x3d0d, 0x00}, + {0x3d0e, 0x00}, + {0x3d0f, 0x00}, + {0x3d80, 0x00}, + {0x3d81, 0x00}, + {0x3d84, 0x00}, + {0x3e07, 0x20}, + {0x4000, 0x08}, + {0x4001, 0x04}, + {0x4002, 0x45}, + {0x4004, 0x08}, + {0x4005, 0x18}, + {0x4006, 0x20}, + {0x4008, 0x24}, + {0x4009, 0x10}, + {0x400c, 0x00}, + {0x400d, 0x00}, + {0x4058, 0x00}, + {0x4101, 0xb2}, + {0x4303, 0x00}, + {0x4304, 0x08}, + {0x4307, 0x30}, + {0x4311, 0x04}, + {0x4315, 0x01}, + {0x4511, 0x05}, + {0x4512, 0x01}, + {0x4806, 0x00}, + {0x4816, 0x52}, + {0x481f, 0x30}, + {0x4826, 0x2c}, + {0x4831, 0x64}, + {0x4d00, 0x04}, + {0x4d01, 0x71}, + {0x4d02, 0xfd}, + {0x4d03, 0xf5}, + {0x4d04, 0x0c}, + {0x4d05, 0xcc}, + {0x4837, 0x09}, + {0x5000, 0x06}, + {0x5001, 0x01}, + {0x5002, 0x00}, + {0x5003, 0x20}, + {0x5046, 0x0a}, + {0x5013, 0x00}, + {0x5046, 0x0a}, + {0x5780, 0x1c}, + {0x5786, 0x20}, + {0x5787, 0x10}, + {0x5788, 0x18}, + {0x578a, 0x04}, + {0x578b, 0x02}, + {0x578c, 0x02}, + {0x578e, 0x06}, + {0x578f, 0x02}, + {0x5790, 0x02}, + {0x5791, 0xff}, + {0x5842, 0x01}, + {0x5843, 0x2b}, + {0x5844, 0x01}, + {0x5845, 0x92}, + {0x5846, 0x01}, + {0x5847, 0x8f}, + {0x5848, 0x01}, + {0x5849, 0x0c}, + {0x5e00, 0x00}, + {0x5e10, 0x0c}, + {0x0100, 0x01}, + + {OV5693_TABLE_END, 0x0000} +}; + +static const struct ov5693_reg ov5693_1296x972_i2c[] = { + {OV5693_TABLE_RESET, 0},/* Including sw reset */ + {0x0103, 0x01}, + {0x3001, 0x0a}, + {0x3002, 0x80}, + {0x3006, 0x00}, + {0x3011, 0x21}, + {0x3012, 0x09}, + {0x3013, 0x10}, + {0x3014, 0x00}, + {0x3015, 0x08}, + {0x3016, 0xf0}, + {0x3017, 0xf0}, + {0x3018, 0xf0}, + {0x301b, 0xb4}, + {0x301d, 0x02}, + {0x3021, 0x00}, + {0x3022, 0x01}, + {0x3028, 0x44}, + {0x3098, 0x03}, + {0x3099, 0x1e}, + {0x309a, 0x02}, + {0x309b, 0x01}, + {0x309c, 0x00}, + {0x30a0, 0xd2}, + {0x30a2, 0x01}, + {0x30b2, 0x00}, + {0x30b3, 0x64}, + {0x30b4, 0x03}, + {0x30b5, 0x04}, + {0x30b6, 0x01}, + {0x3104, 0x21}, + {0x3106, 0x00}, + {0x3400, 0x04}, + {0x3401, 0x00}, + {0x3402, 0x04}, + {0x3403, 0x00}, + {0x3404, 0x04}, + {0x3405, 0x00}, + {0x3406, 0x01}, + {0x3500, 0x00}, + {0x3501, 0x7b}, + {0x3502, 0x00}, + {0x3503, 0x07}, + {0x3504, 0x00}, + {0x3505, 0x00}, + {0x3506, 0x00}, + {0x3507, 0x02}, + {0x3508, 0x00}, + {0x3509, 0x08}, + {0x350a, 0x00}, + {0x350b, 0x40}, + {0x3601, 0x0a}, + {0x3602, 0x38}, + {0x3612, 0x80}, + {0x3620, 0x54}, + {0x3621, 0xc7}, + {0x3622, 0x0f}, + {0x3625, 0x10}, + {0x3630, 0x55}, + {0x3631, 0xf4}, + {0x3632, 0x00}, + {0x3633, 0x34}, + {0x3634, 0x02}, + {0x364d, 0x0d}, + {0x364f, 0xdd}, + {0x3660, 0x04}, + {0x3662, 0x10}, + {0x3663, 0xf1}, + {0x3665, 0x00}, + {0x3666, 0x20}, + {0x3667, 0x00}, + {0x366a, 0x80}, + {0x3680, 0xe0}, + {0x3681, 0x00}, + {0x3700, 0x42}, + {0x3701, 0x14}, + {0x3702, 0xa0}, + {0x3703, 0xd8}, + {0x3704, 0x78}, + {0x3705, 0x02}, + {0x3708, 0xe6}, + {0x3709, 0xc3}, + {0x370a, 0x00}, + {0x370b, 0x20}, + {0x370c, 0x0c}, + {0x370d, 0x11}, + {0x370e, 0x00}, + {0x370f, 0x40}, + {0x3710, 0x00}, + {0x371a, 0x1c}, + {0x371b, 0x05}, + {0x371c, 0x01}, + {0x371e, 0xa1}, + {0x371f, 0x0c}, + {0x3721, 0x00}, + {0x3724, 0x10}, + {0x3726, 0x00}, + {0x372a, 0x01}, + {0x3730, 0x10}, + {0x3738, 0x22}, + {0x3739, 0xe5}, + {0x373a, 0x50}, + {0x373b, 0x02}, + {0x373c, 0x41}, + {0x373f, 0x02}, + {0x3740, 0x42}, + {0x3741, 0x02}, + {0x3742, 0x18}, + {0x3743, 0x01}, + {0x3744, 0x02}, + {0x3747, 0x10}, + {0x374c, 0x04}, + {0x3751, 0xf0}, + {0x3752, 0x00}, + {0x3753, 0x00}, + {0x3754, 0xc0}, + {0x3755, 0x00}, + {0x3756, 0x1a}, + {0x3758, 0x00}, + {0x3759, 0x0f}, + {0x376b, 0x44}, + {0x375c, 0x04}, + {0x3774, 0x10}, + {0x3776, 0x00}, + {0x377f, 0x08}, + {0x3780, 0x22}, + {0x3781, 0x0c}, + {0x3784, 0x2c}, + {0x3785, 0x1e}, + {0x378f, 0xf5}, + {0x3791, 0xb0}, + {0x3795, 0x00}, + {0x3796, 0x64}, + {0x3797, 0x11}, + {0x3798, 0x30}, + {0x3799, 0x41}, + {0x379a, 0x07}, + {0x379b, 0xb0}, + {0x379c, 0x0c}, + {0x37c5, 0x00}, + {0x37c6, 0x00}, + {0x37c7, 0x00}, + {0x37c9, 0x00}, + {0x37ca, 0x00}, + {0x37cb, 0x00}, + {0x37de, 0x00}, + {0x37df, 0x00}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x0a}, + {0x3805, 0x3f}, + {0x3806, 0x07}, + {0x3807, 0xa3}, + {0x3808, 0x05}, + {0x3809, 0x10}, + {0x380a, 0x03}, + {0x380b, 0xcc}, + {0x380c, 0x0a}, + {0x380d, 0x80}, + {0x380e, 0x07}, + {0x380f, 0xc0}, + {0x3810, 0x00}, + {0x3811, 0x02}, + {0x3812, 0x00}, + {0x3813, 0x02}, + {0x3814, 0x31}, + {0x3815, 0x31}, + {0x3820, 0x01}, + {0x3821, 0x1f}, + {0x3823, 0x00}, + {0x3824, 0x00}, + {0x3825, 0x00}, + {0x3826, 0x00}, + {0x3827, 0x00}, + {0x382a, 0x04}, + {0x3a04, 0x06}, + {0x3a05, 0x14}, + {0x3a06, 0x00}, + {0x3a07, 0xfe}, + {0x3b00, 0x00}, + {0x3b02, 0x00}, + {0x3b03, 0x00}, + {0x3b04, 0x00}, + {0x3b05, 0x00}, + {0x3e07, 0x20}, + {0x4000, 0x08}, + {0x4001, 0x04}, + {0x4002, 0x45}, + {0x4004, 0x08}, + {0x4005, 0x18}, + {0x4006, 0x20}, + {0x4008, 0x24}, + {0x4009, 0x40}, + {0x400c, 0x00}, + {0x400d, 0x00}, + {0x4058, 0x00}, + {0x404e, 0x37}, + {0x404f, 0x8f}, + {0x4058, 0x00}, + {0x4101, 0xb2}, + {0x4303, 0x00}, + {0x4304, 0x08}, + {0x4307, 0x30}, + {0x4311, 0x04}, + {0x4315, 0x01}, + {0x4511, 0x05}, + {0x4512, 0x01}, + {0x4806, 0x00}, + {0x4816, 0x52}, + {0x481f, 0x30}, + {0x4826, 0x2c}, + {0x4831, 0x64}, + {0x4d00, 0x04}, + {0x4d01, 0x71}, + {0x4d02, 0xfd}, + {0x4d03, 0xf5}, + {0x4d04, 0x0c}, + {0x4d05, 0xcc}, + {0x4837, 0x0a}, + {0x5000, 0x06}, + {0x5001, 0x01}, + {0x5002, 0x00}, + {0x5003, 0x20}, + {0x5046, 0x0a}, + {0x5013, 0x00}, + {0x5046, 0x0a}, + {0x5780, 0x1c}, + {0x5786, 0x20}, + {0x5787, 0x10}, + {0x5788, 0x18}, + {0x578a, 0x04}, + {0x578b, 0x02}, + {0x578c, 0x02}, + {0x578e, 0x06}, + {0x578f, 0x02}, + {0x5790, 0x02}, + {0x5791, 0xff}, + {0x5842, 0x01}, + {0x5843, 0x2b}, + {0x5844, 0x01}, + {0x5845, 0x92}, + {0x5846, 0x01}, + {0x5847, 0x8f}, + {0x5848, 0x01}, + {0x5849, 0x0c}, + {0x5e00, 0x00}, + {0x5e10, 0x0c}, + {0x0100, 0x01}, + {OV5693_TABLE_END, 0x0000} +}; + +static const struct ov5693_reg ov5693_1920x1080_i2c[] = { + {OV5693_TABLE_RESET, 0x0},/*, 0xIncluding, 0xsw, 0xreset, 0x*/ + {0x3001, 0x0a}, + {0x3002, 0x80}, + {0x3006, 0x00}, + {0x3011, 0x21}, + {0x3012, 0x09}, + {0x3013, 0x10}, + {0x3014, 0x00}, + {0x3015, 0x08}, + {0x3016, 0xf0}, + {0x3017, 0xf0}, + {0x3018, 0xf0}, + {0x301b, 0xb4}, + {0x301d, 0x02}, + {0x3021, 0x00}, + {0x3022, 0x01}, + {0x3028, 0x44}, + {0x3098, 0x03}, + {0x3099, 0x1e}, + {0x309a, 0x02}, + {0x309b, 0x01}, + {0x309c, 0x00}, + {0x30a0, 0xd2}, + {0x30a2, 0x01}, + {0x30b2, 0x00}, + {0x30b3, 0x64}, + {0x30b4, 0x03}, + {0x30b5, 0x04}, + {0x30b6, 0x01}, + {0x3104, 0x21}, + {0x3106, 0x00}, + {0x3400, 0x04}, + {0x3401, 0x00}, + {0x3402, 0x04}, + {0x3403, 0x00}, + {0x3404, 0x04}, + {0x3405, 0x00}, + {0x3406, 0x01}, + {0x3500, 0x00}, + {0x3501, 0x7b}, + {0x3502, 0x00}, + {0x3503, 0x07}, + {0x3504, 0x00}, + {0x3505, 0x00}, + {0x3506, 0x00}, + {0x3507, 0x02}, + {0x3508, 0x00}, + {0x3509, 0x08}, + {0x350a, 0x00}, + {0x350b, 0x40}, + {0x3601, 0x0a}, + {0x3602, 0x38}, + {0x3612, 0x80}, + {0x3620, 0x54}, + {0x3621, 0xc7}, + {0x3622, 0x0f}, + {0x3625, 0x10}, + {0x3630, 0x55}, + {0x3631, 0xf4}, + {0x3632, 0x00}, + {0x3633, 0x34}, + {0x3634, 0x02}, + {0x364d, 0x0d}, + {0x364f, 0xdd}, + {0x3660, 0x04}, + {0x3662, 0x10}, + {0x3663, 0xf1}, + {0x3665, 0x00}, + {0x3666, 0x20}, + {0x3667, 0x00}, + {0x366a, 0x80}, + {0x3680, 0xe0}, + {0x3681, 0x00}, + {0x3700, 0x42}, + {0x3701, 0x14}, + {0x3702, 0xa0}, + {0x3703, 0xd8}, + {0x3704, 0x78}, + {0x3705, 0x02}, + {0x3708, 0xe2}, + {0x3709, 0xc3}, + {0x370a, 0x00}, + {0x370b, 0x20}, + {0x370c, 0x0c}, + {0x370d, 0x11}, + {0x370e, 0x00}, + {0x370f, 0x40}, + {0x3710, 0x00}, + {0x371a, 0x1c}, + {0x371b, 0x05}, + {0x371c, 0x01}, + {0x371e, 0xa1}, + {0x371f, 0x0c}, + {0x3721, 0x00}, + {0x3724, 0x10}, + {0x3726, 0x00}, + {0x372a, 0x01}, + {0x3730, 0x10}, + {0x3738, 0x22}, + {0x3739, 0xe5}, + {0x373a, 0x50}, + {0x373b, 0x02}, + {0x373c, 0x41}, + {0x373f, 0x02}, + {0x3740, 0x42}, + {0x3741, 0x02}, + {0x3742, 0x18}, + {0x3743, 0x01}, + {0x3744, 0x02}, + {0x3747, 0x10}, + {0x374c, 0x04}, + {0x3751, 0xf0}, + {0x3752, 0x00}, + {0x3753, 0x00}, + {0x3754, 0xc0}, + {0x3755, 0x00}, + {0x3756, 0x1a}, + {0x3758, 0x00}, + {0x3759, 0x0f}, + {0x376b, 0x44}, + {0x375c, 0x04}, + {0x3774, 0x10}, + {0x3776, 0x00}, + {0x377f, 0x08}, + {0x3780, 0x22}, + {0x3781, 0x0c}, + {0x3784, 0x2c}, + {0x3785, 0x1e}, + {0x378f, 0xf5}, + {0x3791, 0xb0}, + {0x3795, 0x00}, + {0x3796, 0x64}, + {0x3797, 0x11}, + {0x3798, 0x30}, + {0x3799, 0x41}, + {0x379a, 0x07}, + {0x379b, 0xb0}, + {0x379c, 0x0c}, + {0x37c5, 0x00}, + {0x37c6, 0x00}, + {0x37c7, 0x00}, + {0x37c9, 0x00}, + {0x37ca, 0x00}, + {0x37cb, 0x00}, + {0x37de, 0x00}, + {0x37df, 0x00}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0xf8}, + {0x3804, 0x0a}, + {0x3805, 0x3f}, + {0x3806, 0x06}, + {0x3807, 0xab}, + {0x3808, 0x07}, + {0x3809, 0x80}, + {0x380a, 0x04}, + {0x380b, 0x38}, + {0x380c, 0x0a}, + {0x380d, 0x80}, + {0x380e, 0x07}, + {0x380f, 0xc0}, + {0x3810, 0x00}, + {0x3811, 0x02}, + {0x3812, 0x00}, + {0x3813, 0x02}, + {0x3814, 0x11}, + {0x3815, 0x11}, + {0x3820, 0x00}, + {0x3821, 0x1e}, + {0x3823, 0x00}, + {0x3824, 0x00}, + {0x3825, 0x00}, + {0x3826, 0x00}, + {0x3827, 0x00}, + {0x382a, 0x04}, + {0x3a04, 0x06}, + {0x3a05, 0x14}, + {0x3a06, 0x00}, + {0x3a07, 0xfe}, + {0x3b00, 0x00}, + {0x3b02, 0x00}, + {0x3b03, 0x00}, + {0x3b04, 0x00}, + {0x3b05, 0x00}, + {0x3e07, 0x20}, + {0x4000, 0x08}, + {0x4001, 0x04}, + {0x4002, 0x45}, + {0x4004, 0x08}, + {0x4005, 0x18}, + {0x4006, 0x20}, + {0x4008, 0x24}, + {0x4009, 0x40}, + {0x400c, 0x00}, + {0x400d, 0x00}, + {0x4058, 0x00}, + {0x404e, 0x37}, + {0x404f, 0x8f}, + {0x4058, 0x00}, + {0x4101, 0xb2}, + {0x4303, 0x00}, + {0x4304, 0x08}, + {0x4307, 0x30}, + {0x4311, 0x04}, + {0x4315, 0x01}, + {0x4511, 0x05}, + {0x4512, 0x01}, + {0x4806, 0x00}, + {0x4816, 0x52}, + {0x481f, 0x30}, + {0x4826, 0x2c}, + {0x4831, 0x64}, + {0x4d00, 0x04}, + {0x4d01, 0x71}, + {0x4d02, 0xfd}, + {0x4d03, 0xf5}, + {0x4d04, 0x0c}, + {0x4d05, 0xcc}, + {0x4837, 0x0a}, + {0x5000, 0x06}, + {0x5001, 0x01}, + {0x5002, 0x80}, + {0x5003, 0x20}, + {0x5046, 0x0a}, + {0x5013, 0x00}, + {0x5046, 0x0a}, + {0x5780, 0x1c}, + {0x5786, 0x20}, + {0x5787, 0x10}, + {0x5788, 0x18}, + {0x578a, 0x04}, + {0x578b, 0x02}, + {0x578c, 0x02}, + {0x578e, 0x06}, + {0x578f, 0x02}, + {0x5790, 0x02}, + {0x5791, 0xff}, + {0x5842, 0x01}, + {0x5843, 0x2b}, + {0x5844, 0x01}, + {0x5845, 0x92}, + {0x5846, 0x01}, + {0x5847, 0x8f}, + {0x5848, 0x01}, + {0x5849, 0x0c}, + {0x5e00, 0x00}, + {0x5e10, 0x0c}, + {0x0100, 0x01}, + {OV5693_TABLE_END, 0x0000} +}; + + +static const struct ov5693_reg ov5693_1280x720_120fps_i2c[] = { + {OV5693_TABLE_RESET, 0},/* Including sw reset */ + {0x3001, 0x0a}, + {0x3002, 0x80}, + {0x3006, 0x00}, + {0x3011, 0x21}, + {0x3012, 0x09}, + {0x3013, 0x10}, + {0x3014, 0x00}, + {0x3015, 0x08}, + {0x3016, 0xf0}, + {0x3017, 0xf0}, + {0x3018, 0xf0}, + {0x301b, 0xb4}, + {0x301d, 0x02}, + {0x3021, 0x00}, + {0x3022, 0x01}, + {0x3028, 0x44}, + {0x3098, 0x03}, + {0x3099, 0x1e}, + {0x309a, 0x02}, + {0x309b, 0x01}, + {0x309c, 0x00}, + {0x30a0, 0xd2}, + {0x30a2, 0x01}, + {0x30b2, 0x00}, + {0x30b3, 0x64}, + {0x30b4, 0x03}, + {0x30b5, 0x04}, + {0x30b6, 0x01}, + {0x3104, 0x21}, + {0x3106, 0x00}, + {0x3400, 0x04}, + {0x3401, 0x00}, + {0x3402, 0x04}, + {0x3403, 0x00}, + {0x3404, 0x04}, + {0x3405, 0x00}, + {0x3406, 0x01}, + {0x3500, 0x00}, + {0x3501, 0x2e}, + {0x3502, 0x80}, + {0x3503, 0x07}, + {0x3504, 0x00}, + {0x3505, 0x00}, + {0x3506, 0x00}, + {0x3507, 0x02}, + {0x3508, 0x00}, + {0x3509, 0x08}, + {0x350a, 0x00}, + {0x350b, 0x40}, + {0x3601, 0x0a}, + {0x3602, 0x38}, + {0x3612, 0x80}, + {0x3620, 0x54}, + {0x3621, 0xc7}, + {0x3622, 0x0f}, + {0x3625, 0x10}, + {0x3630, 0x55}, + {0x3631, 0xf4}, + {0x3632, 0x00}, + {0x3633, 0x34}, + {0x3634, 0x02}, + {0x364d, 0x0d}, + {0x364f, 0xdd}, + {0x3660, 0x04}, + {0x3662, 0x10}, + {0x3663, 0xf1}, + {0x3665, 0x00}, + {0x3666, 0x20}, + {0x3667, 0x00}, + {0x366a, 0x80}, + {0x3680, 0xe0}, + {0x3681, 0x00}, + {0x3700, 0x42}, + {0x3701, 0x14}, + {0x3702, 0xa0}, + {0x3703, 0xd8}, + {0x3704, 0x78}, + {0x3705, 0x02}, + {0x3708, 0xe6}, + {0x3709, 0xc7}, + {0x370a, 0x00}, + {0x370b, 0x20}, + {0x370c, 0x0c}, + {0x370d, 0x11}, + {0x370e, 0x00}, + {0x370f, 0x40}, + {0x3710, 0x00}, + {0x371a, 0x1c}, + {0x371b, 0x05}, + {0x371c, 0x01}, + {0x371e, 0xa1}, + {0x371f, 0x0c}, + {0x3721, 0x00}, + {0x3724, 0x10}, + {0x3726, 0x00}, + {0x372a, 0x01}, + {0x3730, 0x10}, + {0x3738, 0x22}, + {0x3739, 0xe5}, + {0x373a, 0x50}, + {0x373b, 0x02}, + {0x373c, 0x41}, + {0x373f, 0x02}, + {0x3740, 0x42}, + {0x3741, 0x02}, + {0x3742, 0x18}, + {0x3743, 0x01}, + {0x3744, 0x02}, + {0x3747, 0x10}, + {0x374c, 0x04}, + {0x3751, 0xf0}, + {0x3752, 0x00}, + {0x3753, 0x00}, + {0x3754, 0xc0}, + {0x3755, 0x00}, + {0x3756, 0x1a}, + {0x3758, 0x00}, + {0x3759, 0x0f}, + {0x376b, 0x44}, + {0x375c, 0x04}, + {0x3774, 0x10}, + {0x3776, 0x00}, + {0x377f, 0x08}, + {0x3780, 0x22}, + {0x3781, 0x0c}, + {0x3784, 0x2c}, + {0x3785, 0x1e}, + {0x378f, 0xf5}, + {0x3791, 0xb0}, + {0x3795, 0x00}, + {0x3796, 0x64}, + {0x3797, 0x11}, + {0x3798, 0x30}, + {0x3799, 0x41}, + {0x379a, 0x07}, + {0x379b, 0xb0}, + {0x379c, 0x0c}, + {0x37c5, 0x00}, + {0x37c6, 0x00}, + {0x37c7, 0x00}, + {0x37c9, 0x00}, + {0x37ca, 0x00}, + {0x37cb, 0x00}, + {0x37de, 0x00}, + {0x37df, 0x00}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0xf4}, + {0x3804, 0x0a}, + {0x3805, 0x3f}, + {0x3806, 0x06}, + {0x3807, 0xab}, + {0x3808, 0x05}, + {0x3809, 0x00}, + {0x380a, 0x02}, + {0x380b, 0xd0}, + {0x380c, 0x06}, + {0x380d, 0xd8}, + {0x380e, 0x02}, + {0x380f, 0xf8}, + {0x3810, 0x00}, + {0x3811, 0x02}, + {0x3812, 0x00}, + {0x3813, 0x02}, + {0x3814, 0x31}, + {0x3815, 0x31}, + {0x3820, 0x04}, + {0x3821, 0x1f}, + {0x3823, 0x00}, + {0x3824, 0x00}, + {0x3825, 0x00}, + {0x3826, 0x00}, + {0x3827, 0x00}, + {0x382a, 0x04}, + {0x3a04, 0x06}, + {0x3a05, 0x14}, + {0x3a06, 0x00}, + {0x3a07, 0xfe}, + {0x3b00, 0x00}, + {0x3b02, 0x00}, + {0x3b03, 0x00}, + {0x3b04, 0x00}, + {0x3b05, 0x00}, + {0x3e07, 0x20}, + {0x4000, 0x08}, + {0x4001, 0x04}, + {0x4002, 0x45}, + {0x4004, 0x08}, + {0x4005, 0x18}, + {0x4006, 0x20}, + {0x4008, 0x24}, + {0x4009, 0x40}, + {0x400c, 0x00}, + {0x400d, 0x00}, + {0x4058, 0x00}, + {0x404e, 0x37}, + {0x404f, 0x8f}, + {0x4058, 0x00}, + {0x4101, 0xb2}, + {0x4303, 0x00}, + {0x4304, 0x08}, + {0x4307, 0x30}, + {0x4311, 0x04}, + {0x4315, 0x01}, + {0x4511, 0x05}, + {0x4512, 0x01}, + {0x4806, 0x00}, + {0x4816, 0x52}, + {0x481f, 0x30}, + {0x4826, 0x2c}, + {0x4831, 0x64}, + {0x4d00, 0x04}, + {0x4d01, 0x71}, + {0x4d02, 0xfd}, + {0x4d03, 0xf5}, + {0x4d04, 0x0c}, + {0x4d05, 0xcc}, + {0x4837, 0x0a}, + {0x5000, 0x06}, + {0x5001, 0x01}, + {0x5002, 0x00}, + {0x5003, 0x20}, + {0x5046, 0x0a}, + {0x5013, 0x00}, + {0x5046, 0x0a}, + {0x5780, 0x1c}, + {0x5786, 0x20}, + {0x5787, 0x10}, + {0x5788, 0x18}, + {0x578a, 0x04}, + {0x578b, 0x02}, + {0x578c, 0x02}, + {0x578e, 0x06}, + {0x578f, 0x02}, + {0x5790, 0x02}, + {0x5791, 0xff}, + {0x5842, 0x01}, + {0x5843, 0x2b}, + {0x5844, 0x01}, + {0x5845, 0x92}, + {0x5846, 0x01}, + {0x5847, 0x8f}, + {0x5848, 0x01}, + {0x5849, 0x0c}, + {0x5e00, 0x00}, + {0x5e10, 0x0c}, + {0x0100, 0x01}, + {0x350b, 0xF8}, + {OV5693_TABLE_END, 0x0000} +}; + +enum { + OV5693_MODE_2592x1944 = 0, + OV5693_MODE_1920x1080, + OV5693_MODE_1296x972, + OV5693_MODE_1280x720_120FPS, +}; + +static const struct ov5693_reg *mode_table[] = { + [OV5693_MODE_2592x1944] = ov5693_2592x1944_i2c, + [OV5693_MODE_1920x1080] = ov5693_1920x1080_i2c, + [OV5693_MODE_1296x972] = ov5693_1296x972_i2c, + [OV5693_MODE_1280x720_120FPS] = ov5693_1280x720_120fps_i2c, +}; + +static int ov5693_i2c_rd8(struct ov5693_info *info, u16 reg, u8 *val) +{ + unsigned int data; + int ret = regmap_read(info->regmap, reg, &data); + *val = data; + + return ret; +} + +static int ov5693_i2c_wr_table(struct ov5693_info *info, + const struct ov5693_reg table[]) +{ + int err; + int buf_count = 0; + const struct ov5693_reg *next, *n_next; + u16 i2c_reg = 0; + u8 i2c_buf[OV5693_SIZEOF_I2C_BUF]; + + u8 *b_ptr = i2c_buf; + u8 reset_status = 1; + u8 reset_tries_left = OV5693_TABLE_RESET_TIMEOUT; + + for (next = table; next->addr != OV5693_TABLE_END; next++) { + if (next->addr == OV5693_TABLE_WAIT_MS) { + msleep(next->val); + continue; + } else if (next->addr == OV5693_TABLE_RESET) { + err = regmap_write(info->regmap, 0x0103, 0x01); + if (err) + return err; + while (reset_status) { + usleep_range(200, 300); + if (reset_tries_left < 1) + return -EIO; + err = ov5693_i2c_rd8(info, 0x0103, + &reset_status); + if (err) + return err; + reset_status &= 0x01; + reset_tries_left -= 1; + } + continue; + } + + if (buf_count == 0) { + b_ptr = i2c_buf; + i2c_reg = next->addr; + } + + *b_ptr++ = next->val; + buf_count++; + n_next = next + 1; + if (n_next->addr == next->addr + 1 && + n_next->addr != OV5693_TABLE_WAIT_MS && + buf_count < OV5693_SIZEOF_I2C_BUF && + n_next->addr != OV5693_TABLE_RESET && + n_next->addr != OV5693_TABLE_END) + continue; + + err = regmap_bulk_write(info->regmap, i2c_reg, + i2c_buf, buf_count); + if (err) + return err; + + buf_count = 0; + } + + return 0; +} + + +static inline int ov5693_frame_length_reg(struct ov5693_reg *regs, + u32 frame_length) +{ + regs->addr = 0x380E; + regs->val = (frame_length >> 8) & 0xff; + (regs + 1)->addr = 0x380F; + (regs + 1)->val = (frame_length) & 0xff; + + return 2; +} + +static inline int ov5693_coarse_time_reg(struct ov5693_reg *regs, + u32 coarse_time) +{ + regs->addr = 0x3500; + regs->val = (coarse_time >> 12) & 0xff; + (regs + 1)->addr = 0x3501; + (regs + 1)->val = (coarse_time >> 4) & 0xff; + (regs + 2)->addr = 0x3502; + (regs + 2)->val = (coarse_time & 0xf) << 4; + + return 3; +} + +#define OV5693_ENTER_GROUP_HOLD(group_hold) \ + do { \ + if (group_hold) { \ + reg_list[offset].addr = 0x3208; \ + reg_list[offset].val = 0x01;\ + offset++; \ + } \ + } while (0) + +#define OV5693_LEAVE_GROUP_HOLD(group_hold) \ + do { \ + if (group_hold) { \ + reg_list[offset].addr = 0x3208; \ + reg_list[offset].val = 0x11;\ + offset++; \ + reg_list[offset].addr = 0x3208; \ + reg_list[offset].val = 0xe1;\ + offset++; \ + } \ + } while (0) + +static int ov5693_set_frame_length(struct ov5693_info *info, + u32 frame_length, bool group_hold) +{ + struct ov5693_reg reg_list[9]; + int err = 0; + int offset = 0; + + OV5693_ENTER_GROUP_HOLD(group_hold); + offset += ov5693_frame_length_reg(reg_list + offset, frame_length); + OV5693_LEAVE_GROUP_HOLD(group_hold); + + reg_list[offset].addr = OV5693_TABLE_END; + offset++; + + err = ov5693_i2c_wr_table(info, reg_list); + + return err; +} + +static int ov5693_set_coarse_time(struct ov5693_info *info, + u32 coarse_time, bool group_hold) +{ + struct ov5693_reg reg_list[16]; + int err = 0; + int offset = 0; + + OV5693_ENTER_GROUP_HOLD(group_hold); + offset += ov5693_coarse_time_reg(reg_list + offset, coarse_time); + OV5693_LEAVE_GROUP_HOLD(group_hold); + + reg_list[offset].addr = OV5693_TABLE_END; + offset++; + + err = ov5693_i2c_wr_table(info, reg_list); + + return err; +} + +static inline int ov5693_gain_reg(struct ov5693_reg *regs, u32 gain) +{ + (regs)->addr = 0x350B; + (regs)->val = gain; + + return 1; +} + +static int ov5693_bin_wr(struct ov5693_info *info, u8 enable) +{ + int err = 0; + + if (enable == info->bin_en) + return 0; + + if (!err) + info->bin_en = enable; + dev_dbg(&info->i2c_client->dev, "%s bin_en=%x err=%d\n", + __func__, info->bin_en, err); + return err; +} + +static int ov5693_exposure_wr(struct ov5693_info *info, + struct ov5693_mode *mode) +{ + struct ov5693_reg reg_list[16]; + int err = 0; + int offset = 0; + bool group_hold = true; /* To use GROUP_HOLD macros */ + + OV5693_ENTER_GROUP_HOLD(group_hold); + offset += ov5693_coarse_time_reg(reg_list + offset, mode->coarse_time); + offset += ov5693_gain_reg(reg_list + offset, mode->gain); + OV5693_LEAVE_GROUP_HOLD(group_hold); + + reg_list[offset].addr = OV5693_TABLE_END; + err = ov5693_i2c_wr_table(info, reg_list); + + return err; +} + + +static int ov5693_set_gain(struct ov5693_info *info, u32 gain, bool group_hold) +{ + struct ov5693_reg reg_list[9]; + int err = 0; + int offset = 0; + + OV5693_ENTER_GROUP_HOLD(group_hold); + offset += ov5693_gain_reg(reg_list + offset, gain); + OV5693_LEAVE_GROUP_HOLD(group_hold); + + reg_list[offset].addr = OV5693_TABLE_END; + offset++; + + err = ov5693_i2c_wr_table(info, reg_list); + + return err; +} + +static int ov5693_set_group_hold(struct ov5693_info *info, + struct ov5693_ae *ae) +{ + int err = 0; + struct ov5693_reg reg_list[16]; + int offset = 0; + bool group_hold = true; /* To use GROUP_HOLD macros */ + + OV5693_ENTER_GROUP_HOLD(group_hold); + if (ae->gain_enable) + offset += ov5693_gain_reg(reg_list + offset, + ae->gain); + if (ae->frame_length_enable) + offset += ov5693_frame_length_reg(reg_list + offset, + ae->frame_length); + if (ae->coarse_time_enable) + offset += ov5693_coarse_time_reg(reg_list + offset, + ae->coarse_time); + OV5693_LEAVE_GROUP_HOLD(group_hold); + + reg_list[offset].addr = OV5693_TABLE_END; + err |= ov5693_i2c_wr_table(info, reg_list); + + return err; +} + +static int ov5693_gpio_rd(struct ov5693_info *info, + enum ov5693_gpio_type type) +{ + int val = -EINVAL; + + if (info->gpio[type].gpio) { + val = gpio_get_value_cansleep(info->gpio[type].gpio); + dev_dbg(&info->i2c_client->dev, "%s %u %d\n", __func__, + info->gpio[type].gpio, val); + if (!info->gpio[type].active_high) + val = !val; + val &= 1; + } + return val; /* return read value or error */ +} + +static int ov5693_gpio_wr(struct ov5693_info *info, + enum ov5693_gpio_type type, + int val) /* val: 0=deassert, 1=assert */ +{ + int err = -EINVAL; + + if (info->gpio[type].gpio) { + if (!info->gpio[type].active_high) + val = !val; + val &= 1; + err = val; + gpio_set_value_cansleep(info->gpio[type].gpio, val); + dev_dbg(&info->i2c_client->dev, "%s %u %d\n", __func__, + info->gpio[type].gpio, val); + } + return err; /* return value written or error */ +} + +static void ov5693_gpio_pwrdn(struct ov5693_info *info, int val) +{ + int prev_val; + + prev_val = ov5693_gpio_rd(info, OV5693_GPIO_TYPE_PWRDN); + if ((prev_val < 0) || (val == prev_val)) + return; + + ov5693_gpio_wr(info, OV5693_GPIO_TYPE_PWRDN, val); + if (!val && prev_val) + /* if transition from assert to deassert then delay for I2C */ + msleep(50); +} + +static void ov5693_gpio_exit(struct ov5693_info *info) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(ov5693_gpio); i++) { + if (info->gpio[i].gpio && info->gpio[i].own) + gpio_free(info->gpio[i].gpio); + } +} + +static void ov5693_gpio_init(struct ov5693_info *info) +{ + char label[32]; + unsigned long flags; + unsigned type; + unsigned i; + unsigned j; + int err; + + if (!info->pdata->gpio_count || !info->pdata->gpio) + return; + + for (i = 0; i < ARRAY_SIZE(ov5693_gpio); i++) { + type = ov5693_gpio[i].gpio_type; + for (j = 0; j < info->pdata->gpio_count; j++) { + if (type == info->pdata->gpio[j].gpio_type) + break; + } + if (j == info->pdata->gpio_count) + continue; + + info->gpio[type].gpio = info->pdata->gpio[j].gpio; + if (ov5693_gpio[i].use_flags) { + flags = ov5693_gpio[i].flags; + info->gpio[type].active_high = + ov5693_gpio[i].active_high; + } else { + info->gpio[type].active_high = + info->pdata->gpio[j].active_high; + if (info->gpio[type].active_high) + flags = GPIOF_OUT_INIT_LOW; + else + flags = GPIOF_OUT_INIT_HIGH; + } + if (!info->pdata->gpio[j].init_en) + continue; + + snprintf(label, sizeof(label), "ov5693_%u_%s", + info->pdata->num, ov5693_gpio[i].label); + err = gpio_request_one(info->gpio[type].gpio, flags, label); + if (err) { + dev_err(&info->i2c_client->dev, + "%s ERR %s %u\n", __func__, label, + info->gpio[type].gpio); + } else { + info->gpio[type].own = true; + dev_dbg(&info->i2c_client->dev, + "%s %s %u\n", __func__, label, + info->gpio[type].gpio); + } + } +} + +static int ov5693_power_off(struct ov5693_info *info) +{ + struct ov5693_power_rail *pw = &info->regulators; + int err; + + if (false == info->power_on) + return 0; + + if (info->pdata && info->pdata->power_off) { + err = info->pdata->power_off(pw); + if (0 > err) + return err; + info->power_on = false; + ov5693_gpio_pwrdn(info, 1); + } else { + dev_err(&info->i2c_client->dev, + "%s ERR: has no power_off function\n", __func__); + err = -EINVAL; + } + return err; +} + +static int ov5693_power_on(struct ov5693_info *info, bool standby) +{ + struct ov5693_power_rail *pw = &info->regulators; + int err; + + if (true == info->power_on) + return 0; + + if (info->pdata && info->pdata->power_on) { + err = info->pdata->power_on(pw); + if (0 > err) + return err; + info->power_on = true; + ov5693_gpio_pwrdn(info, standby ? 1 : 0); + msleep(100); + } else { + dev_err(&info->i2c_client->dev, + "%s ERR: has no power_on function\n", __func__); + err = -EINVAL; + } + return err; +} + +static int ov5693_pm_wr(struct ov5693_info *info, int pwr) +{ + int err = 0; + + if ((info->pdata->cfg & (NVC_CFG_OFF2STDBY | NVC_CFG_BOOT_INIT)) && + (pwr == NVC_PWR_OFF || + pwr == NVC_PWR_STDBY_OFF)) + pwr = NVC_PWR_STDBY; + if (pwr == info->pwr_dev) + return 0; + + switch (pwr) { + case NVC_PWR_OFF_FORCE: + case NVC_PWR_OFF: + case NVC_PWR_STDBY_OFF: + err = ov5693_power_off(info); + info->mode_valid = false; + info->bin_en = 0; + break; + + case NVC_PWR_STDBY: + err = ov5693_power_on(info, true); + break; + + case NVC_PWR_COMM: + case NVC_PWR_ON: + err = ov5693_power_on(info, false); + break; + + default: + err = -EINVAL; + break; + } + + if (err < 0) { + dev_err(&info->i2c_client->dev, "%s err %d\n", __func__, err); + pwr = NVC_PWR_ERR; + } + info->pwr_dev = pwr; + dev_dbg(&info->i2c_client->dev, "%s pwr_dev=%d\n", + __func__, info->pwr_dev); + if (err > 0) + return 0; + + return err; +} + +static int ov5693_pm_dev_wr(struct ov5693_info *info, int pwr) +{ + if (info->mode_enable) + pwr = NVC_PWR_ON; + if (pwr < info->pwr_api) + pwr = info->pwr_api; + return ov5693_pm_wr(info, pwr); +} + +static void ov5693_pm_exit(struct ov5693_info *info) +{ + ov5693_pm_wr(info, NVC_PWR_OFF_FORCE); + + ov5693_gpio_exit(info); +} + +static void ov5693_regulator_get(struct ov5693_info *info, + struct regulator **vreg, + const char vreg_name[]) +{ + struct regulator *reg = NULL; + int err = 0; + + reg = devm_regulator_get(&info->i2c_client->dev, vreg_name); + if (IS_ERR(reg)) { + dev_err(&info->i2c_client->dev, "%s %s ERR: %d\n", + __func__, vreg_name, (int)reg); + err = PTR_ERR(reg); + reg = NULL; + } else { + dev_dbg(&info->i2c_client->dev, "%s: %s\n", + __func__, vreg_name); + } + + *vreg = reg; +} + +static void ov5693_pm_init(struct ov5693_info *info) +{ + struct ov5693_power_rail *pw = &info->regulators; + + ov5693_gpio_init(info); + + ov5693_regulator_get(info, &pw->dvdd, "dvdd"); + + ov5693_regulator_get(info, &pw->avdd, "avdd"); + + ov5693_regulator_get(info, &pw->dovdd, "dovdd"); + info->power_on = false; +} + +static int ov5693_mode_able(struct ov5693_info *info, bool mode_enable) +{ + u8 val; + int err; + + if (mode_enable) + val = 0x01; + else + val = 0x00; + err = regmap_write(info->regmap, 0x0100, val); + if (!err) { + info->mode_enable = mode_enable; + dev_dbg(&info->i2c_client->dev, "%s streaming=%x\n", + __func__, info->mode_enable); + if (!mode_enable) + ov5693_pm_dev_wr(info, NVC_PWR_STDBY); + } + return err; +} + +static int ov5693_mode_wr_full(struct ov5693_info *info, u32 mode_index) +{ + int err; + + ov5693_pm_dev_wr(info, NVC_PWR_ON); + ov5693_bin_wr(info, 0); + err = ov5693_i2c_wr_table(info, mode_table[mode_index]); + if (!err) { + dev_dbg(&info->i2c_client->dev, + "init done(mode=%d)!!!\n", mode_index); + info->mode_index = mode_index; + info->mode_valid = true; + } else { + dev_dbg(&info->i2c_client->dev, + "init error(mode=%d)!!!\n", mode_index); + info->mode_valid = false; + } + return err; +} + + +static int ov5693_set_mode(struct ov5693_info *info, + struct ov5693_mode *mode) +{ + u32 mode_index = 0; + int err = 0; + + if (!mode->res_x && !mode->res_y) { + if (mode->frame_length || mode->coarse_time || mode->gain) { + /* write exposure only */ + err = ov5693_exposure_wr(info, mode); + return err; + } else { + /* turn off streaming */ + err = ov5693_mode_able(info, false); + return err; + } + } + + if (mode->res_x == 2592 && mode->res_y == 1944) + mode_index = OV5693_MODE_2592x1944; + else if (mode->res_x == 1296 && mode->res_y == 972) + mode_index = OV5693_MODE_1296x972; + else if (mode->res_x == 1920 && mode->res_y == 1080) + mode_index = OV5693_MODE_1920x1080; + else if (mode->res_x == 1280 && mode->res_y == 720) + mode_index = OV5693_MODE_1280x720_120FPS; + + if (!info->mode_valid || (info->mode_index != mode_index)) + err = ov5693_mode_wr_full(info, mode_index); + else + dev_dbg(&info->i2c_client->dev, "%s short mode\n", __func__); + dev_dbg(&info->i2c_client->dev, "%s: mode #: %d\n", + __func__, mode_index); + dev_dbg(&info->i2c_client->dev, "%s: AE: %d, %d, %d\n", + __func__, mode->frame_length, + mode->coarse_time, mode->gain); + err |= ov5693_exposure_wr(info, mode); + if (err < 0) { + info->mode_valid = false; + dev_err(&info->i2c_client->dev, + "%s set_mode error\n", __func__); + goto ov5693_mode_wr_err; + } + + return 0; + +ov5693_mode_wr_err: + if (!info->mode_enable) + ov5693_pm_dev_wr(info, NVC_PWR_OFF); + return err; +} + +static int ov5693_get_fuse_id(struct ov5693_info *info) +{ + ov5693_i2c_rd8(info, 0x300A, &info->fuseid.id[0]); + ov5693_i2c_rd8(info, 0x300B, &info->fuseid.id[0]); + info->fuseid.size = 2; + dev_dbg(&info->i2c_client->dev, "ov5693 fuse_id: %x,%x\n", + info->fuseid.id[0], info->fuseid.id[1]); + return 0; +} + +static long ov5693_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct ov5693_info *info = file->private_data; + int err; + + switch (cmd) { + case OV5693_IOCTL_SET_MODE: + { + struct ov5693_mode mode; + if (copy_from_user(&mode, + (const void __user *)arg, + sizeof(struct ov5693_mode))) { + dev_err(&info->i2c_client->dev, + "%s:Failed to get mode from user.\n", + __func__); + return -EFAULT; + } + return ov5693_set_mode(info, &mode); + } + case OV5693_IOCTL_GET_STATUS: { + u8 status = 0; + if (copy_to_user((void __user *)arg, &status, sizeof(status))) { + dev_err(&info->i2c_client->dev, + "%s:Failed to copy status to user.\n", + __func__); + return -EFAULT; + } + return 0; + } + + case OV5693_IOCTL_SET_GROUP_HOLD: { + struct ov5693_ae ae; + if (copy_from_user(&ae, (const void __user *)arg, + sizeof(struct ov5693_ae))) { + dev_dbg(&info->i2c_client->dev, + "%s:fail group hold\n", __func__); + return -EFAULT; + } + + return ov5693_set_group_hold(info, &ae); + } + + case OV5693_IOCTL_SET_FRAME_LENGTH: + return ov5693_set_frame_length(info, (u32)arg, true); + + case OV5693_IOCTL_SET_COARSE_TIME: + return ov5693_set_coarse_time(info, (u32)arg, true); + + case OV5693_IOCTL_SET_GAIN: + return ov5693_set_gain(info, (u32)arg, true); + + case OV5693_IOCTL_GET_FUSEID: + { + err = ov5693_get_fuse_id(info); + + if (err) { + dev_err(&info->i2c_client->dev, "%s:Failed to get fuse id info.\n", + __func__); + return err; + } + if (copy_to_user((void __user *)arg, + &info->fuseid, + sizeof(struct ov5693_fuseid))) { + dev_dbg(&info->i2c_client->dev, "%s:Fail copy fuse id to user space\n", + __func__); + return -EFAULT; + } + return 0; + } + default: + dev_err(&info->i2c_client->dev, "%s unsupported ioctl: %x\n", + __func__, cmd); + } + return -EINVAL; +} + +static void ov5693_sdata_init(struct ov5693_info *info) +{ + memcpy(&info->sdata, &ov5693_dflt_sdata, sizeof(info->sdata)); + if (info->pdata->lens_focal_length) + info->sdata.focal_len = info->pdata->lens_focal_length; + if (info->pdata->lens_max_aperture) + info->sdata.max_aperture = info->pdata->lens_max_aperture; + if (info->pdata->lens_fnumber) + info->sdata.fnumber = info->pdata->lens_fnumber; + if (info->pdata->lens_view_angle_h) + info->sdata.view_angle_h = info->pdata->lens_view_angle_h; + if (info->pdata->lens_view_angle_v) + info->sdata.view_angle_v = info->pdata->lens_view_angle_v; +} + +static int ov5693_open(struct inode *inode, struct file *file) +{ + int err; + struct miscdevice *miscdev = file->private_data; + struct ov5693_info *info = dev_get_drvdata(miscdev->parent); + + if (atomic_xchg(&info->in_use, 1)) + return -EBUSY; + + file->private_data = info; + dev_dbg(&info->i2c_client->dev, "%s\n", __func__); + + err = ov5693_power_on(info, false); + return err; +} + +int ov5693_release(struct inode *inode, struct file *file) +{ + struct ov5693_info *info = file->private_data; + + dev_dbg(&info->i2c_client->dev, "%s\n", __func__); + ov5693_pm_wr(info, NVC_PWR_OFF); + file->private_data = NULL; + WARN_ON(!atomic_xchg(&info->in_use, 0)); + return 0; +} + +static const struct file_operations ov5693_fileops = { + .owner = THIS_MODULE, + .open = ov5693_open, + .unlocked_ioctl = ov5693_ioctl, + .release = ov5693_release, +}; + +static void ov5693_del(struct ov5693_info *info) +{ + ov5693_pm_exit(info); + synchronize_rcu(); +} + +static int ov5693_remove(struct i2c_client *client) +{ + struct ov5693_info *info = i2c_get_clientdata(client); + + dev_dbg(&info->i2c_client->dev, "%s\n", __func__); + misc_deregister(&info->miscdev); + ov5693_del(info); + return 0; +} + +static int ov5693_probe( + struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ov5693_info *info; + char dname[16]; + unsigned long clock_probe_rate; + int err; + static struct regmap_config ad5823_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + }; + + + dev_dbg(&client->dev, "%s\n", __func__); + info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); + if (info == NULL) { + dev_err(&client->dev, "%s: kzalloc error\n", __func__); + return -ENOMEM; + } + + info->i2c_client = client; + if (client->dev.platform_data) + info->pdata = client->dev.platform_data; + else { + info->pdata = &ov5693_dflt_pdata; + dev_dbg(&client->dev, + "%s No platform data. Using defaults.\n", + __func__); + } + + info->regmap = devm_regmap_init_i2c(client, &ad5823_regmap_config); + if (IS_ERR(info->regmap)) { + err = PTR_ERR(info->regmap); + dev_err(&client->dev, + "Failed to allocate register map: %d\n", err); + return err; + } + + + i2c_set_clientdata(client, info); + ov5693_pm_init(info); + ov5693_sdata_init(info); + if (info->pdata->cfg & (NVC_CFG_NODEV | NVC_CFG_BOOT_INIT)) { + if (info->pdata->probe_clock) { + clock_probe_rate = 6000; /* initial_clcok*/ + clock_probe_rate *= 1000; + info->pdata->probe_clock(clock_probe_rate); + } + ov5693_pm_dev_wr(info, NVC_PWR_COMM); + ov5693_pm_dev_wr(info, NVC_PWR_OFF); + if (info->pdata->probe_clock) + info->pdata->probe_clock(0); + } + if (info->pdata->dev_name != NULL) + strcpy(dname, info->pdata->dev_name); + else + strcpy(dname, "ov5693"); + if (info->pdata->num) + snprintf(dname, sizeof(dname), "%s.%u", + dname, info->pdata->num); + info->miscdev.name = dname; + info->miscdev.fops = &ov5693_fileops; + info->miscdev.minor = MISC_DYNAMIC_MINOR; + info->miscdev.parent = &client->dev; + if (misc_register(&info->miscdev)) { + dev_err(&client->dev, "%s unable to register misc device %s\n", + __func__, dname); + ov5693_del(info); + return -ENODEV; + } + + dev_dbg(&client->dev, "ov5693 sensor driver loading done\n"); + return 0; +} + +static const struct i2c_device_id ov5693_id[] = { + { "ov5693", 0 }, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, ov5693_id); + +static struct i2c_driver ov5693_i2c_driver = { + .driver = { + .name = "ov5693", + .owner = THIS_MODULE, + }, + .id_table = ov5693_id, + .probe = ov5693_probe, + .remove = ov5693_remove, +}; + +module_i2c_driver(ov5693_i2c_driver); +MODULE_LICENSE("GPL v2"); |