diff options
author | Riham Haidar <rhaidar@nvidia.com> | 2013-05-30 10:36:16 -0700 |
---|---|---|
committer | Riham Haidar <rhaidar@nvidia.com> | 2013-05-30 10:36:16 -0700 |
commit | d4dc214982830cb5afa6f281bc00f30612ba454a (patch) | |
tree | 86d2163e4638778dd15ee9bc3d5d5ad50d412321 /drivers/media | |
parent | c6ee9bbf8a901359881d40a6ad9902aae44f4044 (diff) | |
parent | 59f86ac32f66bfc9d7378db82c18fac621e61765 (diff) |
Merge remote-tracking branch 'origin/dev/svenkatarama/AOHDR_Merge' into promotion_build
Diffstat (limited to 'drivers/media')
-rw-r--r-- | drivers/media/video/tegra/Kconfig | 21 | ||||
-rw-r--r-- | drivers/media/video/tegra/Makefile | 3 | ||||
-rw-r--r-- | drivers/media/video/tegra/ar0833.c | 1266 | ||||
-rw-r--r-- | drivers/media/video/tegra/ar0833_mode_tbls.c | 565 | ||||
-rw-r--r-- | drivers/media/video/tegra/dw9718.c | 1077 | ||||
-rw-r--r-- | drivers/media/video/tegra/imx132.c | 23 | ||||
-rw-r--r-- | drivers/media/video/tegra/imx135.c | 2417 | ||||
-rw-r--r-- | drivers/media/video/tegra/max77665-flash.c | 679 | ||||
-rw-r--r-- | drivers/media/video/tegra/ov5693.c | 283 |
9 files changed, 5835 insertions, 499 deletions
diff --git a/drivers/media/video/tegra/Kconfig b/drivers/media/video/tegra/Kconfig index ff576d67e6a5..6c88a0660197 100644 --- a/drivers/media/video/tegra/Kconfig +++ b/drivers/media/video/tegra/Kconfig @@ -68,6 +68,13 @@ config VIDEO_IMX091 This is a driver for the IMX091 camera sensor for use with the tegra isp. +config VIDEO_IMX135 + tristate "IMX135 camera sensor support" + depends on I2C && ARCH_TEGRA + ---help--- + This is a driver for the IMX135 camera sensor + for use with the tegra isp. + config VIDEO_IMX132 tristate "IMX132 camera sensor support" depends on I2C && ARCH_TEGRA @@ -154,3 +161,17 @@ config VIDEO_AD5816 ---help--- This is a driver for the AD5816 focuser for use with the tegra isp. + +config VIDEO_AR0833 + tristate "AR0833 camera sensor support" + depends on I2C && ARCH_TEGRA + ---help--- + This is a driver for the AR0833 camera sensor + for use with the tegra isp. + +config VIDEO_DW9718 + tristate "DW9718 focuser support" + depends on I2C && ARCH_TEGRA + ---help--- + This is a driver for the DW9718 focuser + for use with the tegra isp. diff --git a/drivers/media/video/tegra/Makefile b/drivers/media/video/tegra/Makefile index 7ce7bb077f77..b76e27b1a50c 100644 --- a/drivers/media/video/tegra/Makefile +++ b/drivers/media/video/tegra/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_TEGRA_MEDIASERVER) += mediaserver/ obj-$(CONFIG_TEGRA_NVAVP) += nvavp/ obj-$(CONFIG_TEGRA_DTV) += tegra_dtv.o obj-$(CONFIG_VIDEO_AR0832) += ar0832_main.o +obj-$(CONFIG_VIDEO_AR0833) += ar0833.o obj-$(CONFIG_VIDEO_OV5650) += ov5650.o obj-$(CONFIG_VIDEO_OV5640) += ov5640.o obj-$(CONFIG_VIDEO_OV14810) += ov14810.o @@ -21,9 +22,11 @@ obj-$(CONFIG_TORCH_TPS61050) += tps61050.o obj-$(CONFIG_MAX77665_FLASH) += max77665-flash.o obj-$(CONFIG_TORCH_AS364X) += as364x.o obj-$(CONFIG_VIDEO_SH532U) += sh532u.o +obj-$(CONFIG_VIDEO_DW9718) += dw9718.o obj-$(CONFIG_VIDEO_AD5820) += ad5820.o obj-$(CONFIG_VIDEO_AD5816) += ad5816.o obj-$(CONFIG_VIDEO_IMX091) += imx091.o +obj-$(CONFIG_VIDEO_IMX135) += imx135.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/ar0833.c b/drivers/media/video/tegra/ar0833.c new file mode 100644 index 000000000000..701468fac6fd --- /dev/null +++ b/drivers/media/video/tegra/ar0833.c @@ -0,0 +1,1266 @@ +/* + * ar0833.c - ar0833 sensor driver + * + * 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/kernel.h> +#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/gpio.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/atomic.h> +#include <linux/regulator/consumer.h> +#include <linux/export.h> +#include <linux/module.h> + +#include <media/ar0833.h> + +#define SIZEOF_I2C_TRANSBUF 128 + +struct ar0833_reg { + u16 addr; + u16 val; +}; + +struct ar0833_reg_blob { + u16 addr; + u16 size; + u8 *data; +}; + +struct ar0833_info { + struct miscdevice miscdev_info; + struct ar0833_power_rail power; + struct ar0833_sensordata sensor_data; + struct i2c_client *i2c_client; + struct ar0833_platform_data *pdata; + atomic_t in_use; + const struct ar0833_reg *mode; +#ifdef CONFIG_DEBUG_FS + struct dentry *debugfs_root; + u32 debug_i2c_offset; + int enableDCBLC; +#endif + u8 i2c_trans_buf[SIZEOF_I2C_TRANSBUF]; +}; + +#define AR0833_TABLE_WAIT_MS 0xC000 +#define AR0833_TABLE_NOP 0xC001 +#define AR0833_TABLE_CALL 0xC002 +#define AR0833_TABLE_BLOB 0xC003 +#define AR0833_TABLE_END 0xC004 +#define AR0833_TABLE_8BIT 0x8000 + +#include "ar0833_mode_tbls.c" + +struct ar0833_mode_desc { + u16 xres; + u16 yres; + u8 hdr_en; + const struct ar0833_reg *mode_tbl; + struct ar0833_modeinfo mode_info; +}; + +static struct ar0833_mode_desc mode_table[] = { + { + .xres = 3264, + .yres = 2448, + .hdr_en = 0, + .mode_tbl = mode_3264x2448_30fps, + }, + { + .xres = 3264, + .yres = 2448, + .hdr_en = 1, + .mode_tbl = mode_3264x2448_HDR_30fps, + }, + { + .xres = 1920, + .yres = 1080, + .hdr_en = 0, + .mode_tbl = mode_1920x1080_30fps, + }, + { + .xres = 1920, + .yres = 1080, + .hdr_en = 1, + .mode_tbl = mode_1920x1080_HDR_30fps, + }, + { + .xres = 3264, + .yres = 1836, + .hdr_en = 0, + .mode_tbl = mode_3264x1836_30fps, + }, + { + .xres = 3264, + .yres = 1836, + .hdr_en = 1, + .mode_tbl = mode_3264x1836_HDR_30fps, + }, + { }, +}; + +static long ar0833_ioctl(struct file *file, + unsigned int cmd, unsigned long arg); + +static inline void ar0833_msleep(u32 t) +{ + usleep_range(t*1000, t*1000 + 500); +} + +static inline void ar0833_get_coarse_time_regs(struct ar0833_reg *regs, + u32 coarse_time) +{ + regs->addr = 0x0202; + regs->val = coarse_time & 0xFFFF; +} + +static inline void ar0833_get_coarse_time_short_regs(struct ar0833_reg *regs, + u32 coarse_time) +{ + regs->addr = 0x3088; + regs->val = coarse_time & 0xFFFF; +} + +static inline void ar0833_get_gain_reg(struct ar0833_reg *regs, u16 gain) +{ + regs->addr = 0x305E; + regs->val = gain & 0xFFFF; +} + +static int ar0833_read_reg(struct i2c_client *client, u16 addr, u8 *val) +{ + int err; + struct i2c_msg msg[2]; + unsigned char data[3]; + + if (!client->adapter) + return -ENODEV; + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 2; + msg[0].buf = data; + + /* high byte goes out first */ + data[0] = (u8) (addr >> 8); + data[1] = (u8) (addr & 0xff); + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 1; + msg[1].buf = data + 2; + + err = i2c_transfer(client->adapter, msg, 2); + + if (err != 2) + return -EINVAL; + + *val = data[2]; + + return 0; +} + +static int ar0833_write_bulk_reg(struct i2c_client *client, u8 *data, int len) +{ + int err; + struct i2c_msg msg; + + if (!client->adapter) + return -ENODEV; + + msg.addr = client->addr; + msg.flags = 0; + msg.len = len; + msg.buf = data; + + dev_dbg(&client->dev, + "%s {0x%04x,", __func__, (int)data[0] << 8 | data[1]); + for (err = 2; err < len; err++) + dev_dbg(&client->dev, " 0x%02x", data[err]); + dev_dbg(&client->dev, "},\n"); + + err = i2c_transfer(client->adapter, &msg, 1); + if (err == 1) + return 0; + + dev_err(&client->dev, "ar0833: i2c bulk transfer failed at %x\n", + (int)data[0] << 8 | data[1]); + + return err; +} + +static int ar0833_write_reg8(struct i2c_client *client, u16 addr, u8 val) +{ + unsigned char data[3]; + + if (!client->adapter) + return -ENODEV; + + data[0] = (u8) (addr >> 8); + data[1] = (u8) (addr & 0xff); + data[2] = (u8) (val & 0xff); + + dev_dbg(&client->dev, "0x%x = 0x%x\n", addr, val); + return ar0833_write_bulk_reg(client, data, sizeof(data)); +} + +static int ar0833_write_reg16(struct i2c_client *client, u16 addr, u16 val) +{ + unsigned char data[4]; + + if (!client->adapter) + return -ENODEV; + + data[0] = (u8) (addr >> 8); + data[1] = (u8) (addr & 0xff); + data[2] = (u8) (val >> 8); + data[3] = (u8) (val & 0xff); + + dev_dbg(&client->dev, "0x%x = 0x%x\n", addr, val); + return ar0833_write_bulk_reg(client, data, sizeof(data)); +} + +/* flush and reset buffer */ +static int ar0833_flash_buffer(struct ar0833_info *info, int *len) +{ + int err; + + if (!(*len)) + return 0; + + err = ar0833_write_bulk_reg(info->i2c_client, + info->i2c_trans_buf, *len); + + *len = 0; + return err; +} + +static int ar0833_write_blob( + struct ar0833_info *info, struct ar0833_reg_blob *pblob) +{ + u8 *pdata = pblob->data; + u16 addr = pblob->addr; + u16 size = pblob->size; + int err = 0; + u16 blk; + + dev_dbg(&info->i2c_client->dev, "ar0833_write_blob ++\n"); + while (size) { + blk = size > sizeof(info->i2c_trans_buf) - 2 ? + sizeof(info->i2c_trans_buf) - 2 : size; + info->i2c_trans_buf[0] = addr >> 8; + info->i2c_trans_buf[1] = (u8)addr; + memcpy(info->i2c_trans_buf + 2, pdata, blk); + err = ar0833_write_bulk_reg(info->i2c_client, + info->i2c_trans_buf, blk + 2); + if (err) + break; + + size -= blk; + pdata += blk; + addr += blk; + } + dev_dbg(&info->i2c_client->dev, "ar0833_write_blob -- %d\n", err); + return err; +} + +static int ar0833_write_table( + struct ar0833_info *info, + const struct ar0833_reg table[], + const struct ar0833_reg override_list[], + int num_override_regs) +{ + int err = 0; + const struct ar0833_reg *next, *n_next; + u8 *b_ptr = info->i2c_trans_buf; + unsigned int buf_filled = 0; + int i; + u16 val; + + dev_dbg(&info->i2c_client->dev, "%s ++\n", __func__); + for (next = table; !err; next++) { + switch (next->addr) { + case AR0833_TABLE_END: + dev_dbg(&info->i2c_client->dev, "ar0833_table_end\n"); + err = ar0833_flash_buffer(info, &buf_filled); + return err; + case AR0833_TABLE_NOP: + continue; + case AR0833_TABLE_WAIT_MS: + dev_dbg(&info->i2c_client->dev, + "ar0833_wait_ms %d\n", next->val); + err = ar0833_flash_buffer(info, &buf_filled); + if (err < 0) + return err; + ar0833_msleep(next->val); + continue; + case AR0833_TABLE_CALL: + err = ar0833_flash_buffer(info, &buf_filled); + if (next->val >= NUM_OF_SUBTBLS) { + dev_err(&info->i2c_client->dev, + "%s: invalid tbl index %d\n", + __func__, next->val); + return -EFAULT; + } + if (err < 0) + return err; + + err = ar0833_write_table( + info, sub_tbls[next->val], NULL, 0); + if (err < 0) + return err; + continue; + case AR0833_TABLE_BLOB: + err = ar0833_flash_buffer(info, &buf_filled); + if (next->val >= NUM_OF_SUBTBLS) { + dev_err(&info->i2c_client->dev, + "%s: invalid tbl index %d\n", + __func__, next->val); + return -EFAULT; + } + if (err < 0) + return err; + + err = ar0833_write_blob(info, sub_tbls[next->val]); + if (err < 0) + return err; + continue; + } + + val = next->val; + /* When an override list is passed in, replace the reg */ + /* value to write if the reg is in the list */ + if (override_list) + for (i = 0; i < num_override_regs; i++) + if (next->addr == override_list[i].addr) { + val = override_list[i].val; + break; + } + + if (!buf_filled) { + b_ptr = info->i2c_trans_buf; + *b_ptr++ = (next->addr & ~AR0833_TABLE_8BIT) >> 8; + *b_ptr++ = next->addr & 0xff; + buf_filled = 2; + } + if (!(next->addr & AR0833_TABLE_8BIT)) { + *b_ptr++ = (u8)(val >> 8); + buf_filled++; + } + *b_ptr++ = (u8)val; + buf_filled++; + + n_next = next + 1; + if (buf_filled < (sizeof(info->i2c_trans_buf) & 0xFFFE) && + n_next->addr == next->addr + 2) + continue; + err = ar0833_flash_buffer(info, &buf_filled); + if (err < 0) + return err; + } + dev_dbg(&info->i2c_client->dev, "%s --\n", __func__); + return 0; +} + +static int ar0833_set_coarse_time(struct ar0833_info *info, u32 coarse_time, + bool group_hold) +{ + int ret; + struct ar0833_reg reg_list; + + ar0833_get_coarse_time_regs(®_list, coarse_time); + + if (group_hold) { + ret = ar0833_write_reg16(info->i2c_client, 0x0104, 0x1); + if (ret) + return ret; + } + + ret = ar0833_write_reg16(info->i2c_client, reg_list.addr, + reg_list.val); + if (ret) + return ret; + + if (group_hold) { + ret = ar0833_write_reg16(info->i2c_client, 0x0104, 0x0); + if (ret) + return ret; + } + return ret; +} + +static int ar0833_set_hdr_coarse_time(struct ar0833_info *info, + struct ar0833_hdr *values, + bool group_hold) +{ + int ret; + struct ar0833_reg reg_list; + struct ar0833_reg reg_list_short; + + ar0833_get_coarse_time_regs(®_list, values->coarse_time_long); + ar0833_get_coarse_time_short_regs(®_list_short, + values->coarse_time_short); + + if (group_hold) { + ret = ar0833_write_reg16(info->i2c_client, 0x0104, 0x1); + if (ret) + return ret; + } + + ret = ar0833_write_reg16(info->i2c_client, reg_list.addr, + reg_list.val); + if (ret) + return ret; + + ret = ar0833_write_reg16(info->i2c_client, reg_list_short.addr, + reg_list_short.val); + if (ret) + return ret; + + if (group_hold) { + ret = ar0833_write_reg16(info->i2c_client, 0x0104, 0x0); + if (ret) + return ret; + } + return ret; +} + +static int ar0833_set_gain(struct ar0833_info *info, u16 gain, + bool group_hold) +{ + int ret; + struct ar0833_reg reg_list; + + ar0833_get_gain_reg(®_list, gain); + + if (group_hold) { + ret = ar0833_write_reg16(info->i2c_client, 0x0104, 0x1); + if (ret) + return ret; + } + + ret = ar0833_write_reg16(info->i2c_client, reg_list.addr, + reg_list.val); + if (ret) + return ret; + + if (group_hold) { + ret = ar0833_write_reg16(info->i2c_client, 0x0104, 0x0); + if (ret) + return ret; + } + return ret; +} + +static int +ar0833_set_group_hold(struct ar0833_info *info, struct ar0833_ae *ae) +{ + int ret; + int count = 0; + bool groupHoldEnabled = false; + struct ar0833_hdr values; + + values.coarse_time_long = ae->coarse_time; + values.coarse_time_short = ae->coarse_time_short; + + if (ae->gain_enable) + count++; + if (ae->coarse_time_enable) + count++; + if (count >= 1) + groupHoldEnabled = true; + + if (groupHoldEnabled) { + ret = ar0833_write_reg16(info->i2c_client, 0x104, 0x1); + if (ret) + return ret; + } + + if (ae->gain_enable) + ar0833_set_gain(info, ae->gain, false); + if (ae->coarse_time_enable) + ar0833_set_hdr_coarse_time(info, &values, false); + + if (groupHoldEnabled) { + ret = ar0833_write_reg16(info->i2c_client, 0x104, 0x0); + if (ret) + return ret; + } + + return 0; +} + +static int ar0833_get_status(struct ar0833_info *info, u8 *status) +{ + int err; + + err = ar0833_read_reg(info->i2c_client, 0x380e, status); + return err; +} + +static int ar0833_open(struct inode *inode, struct file *file) +{ + struct miscdevice *miscdev = file->private_data; + struct ar0833_info *info; + + info = container_of(miscdev, struct ar0833_info, miscdev_info); + /* check if the device is in use */ + if (atomic_xchg(&info->in_use, 1)) { + dev_info(&info->i2c_client->dev, "%s:BUSY!\n", __func__); + return -EBUSY; + } + + file->private_data = info; + + if (info->pdata && info->pdata->power_on) + info->pdata->power_on(&info->power); + else { + dev_err(&info->i2c_client->dev, + "%s:no valid power_on function.\n", __func__); + return -EEXIST; + } + return 0; +} + +int ar0833_release(struct inode *inode, struct file *file) +{ + struct ar0833_info *info = file->private_data; + + if (info->pdata && info->pdata->power_off) + info->pdata->power_off(&info->power); + file->private_data = NULL; + + /* warn if device is already released */ + WARN_ON(!atomic_xchg(&info->in_use, 0)); + return 0; +} + +static int ar0833_regulator_get(struct ar0833_info *info, + struct regulator **vreg, char vreg_name[]) +{ + struct regulator *reg = NULL; + int err = 0; + + reg = regulator_get(&info->i2c_client->dev, vreg_name); + if (unlikely(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; + return err; +} + +static int ar0833_power_get(struct ar0833_info *info) +{ + struct ar0833_power_rail *pw = &info->power; + + ar0833_regulator_get(info, &pw->avdd, "vana"); /* ananlog 2.7v */ + ar0833_regulator_get(info, &pw->dvdd, "vdig"); /* digital 1.2v */ + ar0833_regulator_get(info, &pw->iovdd, "vif"); /* interface 1.8v */ + + return 0; +} + +static const struct file_operations ar0833_fileops = { + .owner = THIS_MODULE, + .open = ar0833_open, + .unlocked_ioctl = ar0833_ioctl, + .release = ar0833_release, +}; + +static struct miscdevice ar0833_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "ar0833", + .fops = &ar0833_fileops, +}; + +#ifdef CONFIG_DEBUG_FS +int control_DC_BLC(struct ar0833_info *info) +{ + u8 reg0x3d0a; + u8 reg0x3d0b; + u8 reg0x4006; + + if (info->enableDCBLC) { + ar0833_write_reg8(info->i2c_client, 0x3d84, 0xdf); + ar0833_write_reg8(info->i2c_client, 0x3d81, 0x01); + ar0833_msleep(10); + + ar0833_read_reg(info->i2c_client, 0x3d0a, ®0x3d0a); + ar0833_read_reg(info->i2c_client, 0x3d0b, ®0x3d0b); + ar0833_write_reg8(info->i2c_client, 0x3d81, 0x00); + + if ((reg0x3d0b > 0x10 && reg0x3d0b < 0x20)) + reg0x4006 = reg0x3d0b; + else if ((reg0x3d0a > 0x10) && (reg0x3d0a < 0x20)) + reg0x4006 = reg0x3d0a; + else + reg0x4006 = 0x20; + + ar0833_write_reg8(info->i2c_client, 0x4006, reg0x4006); + dev_info(&info->i2c_client->dev, + "ar0833: %s: wrote the DC BLC commands\n", + __func__); + } else { + dev_info(&info->i2c_client->dev, + "ar0833: %s: DID NOT do the DC BLC commands\n", + __func__); + } + + return 0; +} + +static int ar0833_stats_show(struct seq_file *s, void *data) +{ + static struct ar0833_info *info; + + seq_printf(s, "%-20s : %-20s\n", "Name", "ar0833-debugfs-testing"); + seq_printf(s, "%-20s : 0x%X\n", "Current i2c-offset Addr", + info->debug_i2c_offset); + seq_printf(s, "%-20s : 0x%X\n", "DC BLC Enabled", + info->debug_i2c_offset); + return 0; +} + +static int ar0833_stats_open(struct inode *inode, struct file *file) +{ + return single_open(file, ar0833_stats_show, inode->i_private); +} + +static const struct file_operations ar0833_stats_fops = { + .open = ar0833_stats_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int debug_i2c_offset_w(void *data, u64 val) +{ + struct ar0833_info *info = (struct ar0833_info *)(data); + dev_info(&info->i2c_client->dev, + "ar0833:%s setting i2c offset to 0x%X\n", + __func__, (u32)val); + info->debug_i2c_offset = (u32)val; + dev_info(&info->i2c_client->dev, + "ar0833:%s new i2c offset is 0x%X\n", __func__, + info->debug_i2c_offset); + return 0; +} + +static int debug_i2c_offset_r(void *data, u64 *val) +{ + struct ar0833_info *info = (struct ar0833_info *)(data); + *val = (u64)info->debug_i2c_offset; + dev_info(&info->i2c_client->dev, + "ar0833:%s reading i2c offset is 0x%X\n", __func__, + info->debug_i2c_offset); + return 0; +} + +static int debug_i2c_read(void *data, u64 *val) +{ + struct ar0833_info *info = (struct ar0833_info *)(data); + u8 temp1 = 0; + u8 temp2 = 0; + dev_info(&info->i2c_client->dev, + "ar0833:%s reading offset 0x%X\n", __func__, + info->debug_i2c_offset); + if (ar0833_read_reg(info->i2c_client, + info->debug_i2c_offset, &temp1) + || ar0833_read_reg(info->i2c_client, + info->debug_i2c_offset+1, &temp2)) { + dev_err(&info->i2c_client->dev, + "ar0833:%s failed\n", __func__); + return -EIO; + } + dev_info(&info->i2c_client->dev, + "ar0833:%s read value is 0x%X\n", __func__, + temp1<<8 | temp2); + *val = (u64)(temp1<<8 | temp2); + return 0; +} + +static int debug_i2c_write(void *data, u64 val) +{ + struct ar0833_info *info = (struct ar0833_info *)(data); + dev_info(&info->i2c_client->dev, + "ar0833:%s writing 0x%X to offset 0x%X\n", __func__, + (u16)val, info->debug_i2c_offset); + if (ar0833_write_reg16(info->i2c_client, + info->debug_i2c_offset, (u16)val)) { + dev_err(&info->i2c_client->dev, "ar0833:%s failed\n", __func__); + return -EIO; + } + return 0; +} + +static int debug_dcblc_r(void *data, u64 *val) +{ + struct ar0833_info *info = (struct ar0833_info *)(data); + *val = (u64)info->enableDCBLC; + dev_info(&info->i2c_client->dev, + "ar0833:%s read DC BLC [%d]\n", __func__, + info->enableDCBLC); + + return 0; +} + +static int debug_dcblc_w(void *data, u64 val) +{ + struct ar0833_info *info = (struct ar0833_info *)(data); + if (val != 0) { + info->enableDCBLC = 1; + dev_info(&info->i2c_client->dev, + "ar0833:%s enabled DC BLC\n", __func__); + } else { + info->enableDCBLC = 0; + dev_info(&info->i2c_client->dev, + "ar0833:%s disabled DC BLC\n", __func__); + } + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(debug_dcblc_fops, debug_dcblc_r, + debug_dcblc_w, "0x%llx\n"); +DEFINE_SIMPLE_ATTRIBUTE(i2c_offset_fops, debug_i2c_offset_r, + debug_i2c_offset_w, "0x%llx\n"); +DEFINE_SIMPLE_ATTRIBUTE(i2c_read_fops, debug_i2c_read, + /*debug_i2c_dummy_w*/ NULL, "0x%llx\n"); +DEFINE_SIMPLE_ATTRIBUTE(i2c_write_fops, /*debug_i2c_dummy_r*/NULL, + debug_i2c_write, "0x%llx\n"); + +static int ar0833_debug_init(struct ar0833_info *info) +{ + dev_dbg(&info->i2c_client->dev, "%s", __func__); + + info->debugfs_root = debugfs_create_dir(ar0833_device.name, NULL); + + if (!info->debugfs_root) + goto err_out; + + if (!debugfs_create_file("stats", S_IRUGO, + info->debugfs_root, info, &ar0833_stats_fops)) + goto err_out; + + if (!debugfs_create_file("i2c_offset", S_IRUGO | S_IWUSR, + info->debugfs_root, info, &i2c_offset_fops)) + goto err_out; + + if (!debugfs_create_file("i2c_read", S_IRUGO, + info->debugfs_root, info, &i2c_read_fops)) + goto err_out; + + if (!debugfs_create_file("i2c_write", S_IWUSR, + info->debugfs_root, info, &i2c_write_fops)) + goto err_out; + + if (!debugfs_create_file("DCBLC", S_IRUGO | S_IWUSR, + info->debugfs_root, info, &debug_dcblc_fops)) + goto err_out; + + return 0; + +err_out: + dev_err(&info->i2c_client->dev, "ERROR:%s failed", __func__); + if (info->debugfs_root) + debugfs_remove_recursive(info->debugfs_root); + return -ENOMEM; +} +#endif + +static struct ar0833_modeinfo def_modeinfo = { + .xres = 3264, + .yres = 2448, + .hdr = 0, + .lanes = 4, + .line_len = 0x0f68, + .frame_len = 0x0a01, + .coarse_time = 2400, + .coarse_time_2nd = 300, + .xstart = 8, + .xend = 0x0cc7, + .ystart = 8, + .yend = 0x0997, + .xsize = 0x0cc0, + .ysize = 0x0990, + .gain = 0x1000, + .x_flip = 1, + .y_flip = 0, + .x_bin = 1, + .y_bin = 1, + .vt_pix_clk_div = 5, + .vt_sys_clk_div = 1, + .pre_pll_clk_div = 2, + .pll_multi = 0x40, + .op_pix_clk_div = 0x0a, + .op_sys_clk_div = 1, +}; + +static struct ar0833_mode_desc *ar0833_get_mode( + struct ar0833_info *info, struct ar0833_mode *mode) +{ + struct ar0833_mode_desc *mt = mode_table; + + while (mt->xres) { + if ((mt->xres == mode->xres) && + (mt->yres == mode->yres) && + (mt->hdr_en == mode->hdr_en)) + break; + mt++; + } + + if (!mt->xres) + mt = NULL; + return mt; +} + +static int ar0833_mode_info_init(struct ar0833_info *info) +{ + struct ar0833_mode_desc *md = mode_table; + const struct ar0833_reg *mt; + struct ar0833_modeinfo *mi; + + dev_dbg(&info->i2c_client->dev, "%s", __func__); + while (md->xres) { + mi = &md->mode_info; + mt = md->mode_tbl; + memcpy(mi, &def_modeinfo, sizeof(*mi)); + dev_dbg(&info->i2c_client->dev, "mode %d x %d %s", + md->xres, md->yres, md->hdr_en ? "HDR" : "REG"); + mi->xres = md->xres; + mi->yres = md->yres; + mi->hdr = md->hdr_en; + while (mt->addr != AR0833_TABLE_END) { + switch (mt->addr) { + case 0x0300: + mi->vt_pix_clk_div = mt->val; + break; + case 0x0302: + mi->vt_sys_clk_div = mt->val; + break; + case 0x0304: + mi->pre_pll_clk_div = mt->val; + break; + case 0x0306: + mi->pll_multi = mt->val; + break; + case 0x0308: + mi->op_pix_clk_div = mt->val; + break; + case 0x030a: + mi->op_sys_clk_div = mt->val; + break; + case 0x0202: + mi->coarse_time = mt->val; + break; + case 0x3088: + mi->coarse_time_2nd = mt->val; + break; + case 0x0340: + mi->frame_len = mt->val; + break; + case 0x0342: + mi->line_len = mt->val; + break; + case 0x0344: + mi->xstart = mt->val; + break; + case 0x0346: + mi->ystart = mt->val; + break; + case 0x0348: + mi->xend = mt->val; + break; + case 0x034a: + mi->yend = mt->val; + break; + case 0x034c: + mi->xsize = mt->val; + break; + case 0x034e: + mi->ysize = mt->val; + break; + case 0x305e: + mi->gain = mt->val; + break; + case 0x31ae: + mi->lanes = mt->val & 0x7; + break; + case 0x3040: + if (mt->val & 0x8000) + mi->y_flip = 1; + if (mt->val & 0x4000) + mi->x_flip = 1; + switch (mt->val & 0x1c0) { + case 1: + mi->x_bin = 1; + break; + case 3: + mi->x_bin = 2; + break; + case 7: + mi->x_bin = 4; + break; + default: + dev_warn(&info->i2c_client->dev, + "%s :Unrecognized x_odd_inc" + "setting in mode %d x %d %s," + " 0x3040 = 0x%x\n", + __func__, md->xres, md->yres, + md->hdr_en ? "HDR" : "REG", + mt->val); + break; + } + + switch (mt->val & 0x3f) { + case 1: + mi->y_bin = 1; + break; + case 3: + mi->y_bin = 2; + break; + case 7: + mi->y_bin = 4; + break; + case 15: + mi->y_bin = 8; + break; + case 31: + mi->y_bin = 16; + break; + case 63: + mi->y_bin = 32; + break; + default: + dev_warn(&info->i2c_client->dev, + "%s :Unrecognized y_odd_inc" + "setting in mode %d x %d %s," + " 0x3040 = 0x%x\n", + __func__, md->xres, md->yres, + md->hdr_en ? "HDR" : "REG", + mt->val); + break; + } + break; + }; + mt++; + }; + md++; + } + return 0; +} + +static struct ar0833_modeinfo *ar0833_get_mode_info( + struct ar0833_info *info, struct ar0833_modeinfo *mi) +{ + struct ar0833_mode mode; + struct ar0833_mode_desc *mode_desc; + + mode.xres = mi->xres; + mode.yres = mi->yres; + mode.hdr_en = mi->hdr; + mode_desc = ar0833_get_mode(info, &mode); + if (mode_desc == NULL) { + dev_err(&info->i2c_client->dev, + "%s: invalid params to get mode info %d %d %d\n", + __func__, mi->xres, mi->yres, mi->hdr); + return NULL; + } + + return &mode_desc->mode_info; +} + +static int ar0833_set_mode(struct ar0833_info *info, struct ar0833_mode *mode) +{ + struct ar0833_mode_desc *sensor_mode; + struct ar0833_reg reg_list[4]; + int err; + + dev_info(&info->i2c_client->dev, + "%s: xres %u yres %u hdr %d\n", + __func__, mode->xres, mode->yres, mode->hdr_en); + dev_info(&info->i2c_client->dev, + "framelength %u coarsetime %u gain %x\n", + mode->frame_length, mode->coarse_time, mode->gain); + + sensor_mode = ar0833_get_mode(info, mode); + if (sensor_mode == NULL) { + dev_err(&info->i2c_client->dev, + "%s: invalid params supplied to set mode %d %d %d\n", + __func__, mode->xres, mode->yres, mode->hdr_en); + return -EINVAL; + } + + if (mode->hdr_en == 1) /* if HDR is enabled */ + dev_info(&info->i2c_client->dev, "ar0833 HDR enabled\n"); + else + dev_info(&info->i2c_client->dev, "ar0833 HDR disabled\n"); + + memset(reg_list, 0, sizeof(reg_list)); + /* get a list of override regs for the asking frame length, */ + /* coarse integration time, and gain. */ + ar0833_get_coarse_time_regs(reg_list, mode->coarse_time); + ar0833_get_gain_reg(reg_list + 1, mode->gain); + if (mode->hdr_en == 1) /* if HDR is enabled */ + ar0833_get_coarse_time_short_regs( + reg_list + 2, mode->coarse_time_short); + + err = ar0833_write_table( + info, sensor_mode->mode_tbl, reg_list, mode->hdr_en ? 3 : 2); + if (err) + return err; + + info->mode = sensor_mode->mode_tbl; + +#ifdef CONFIG_DEBUG_FS + control_DC_BLC(info); +#endif + + return 0; +} + +static long ar0833_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int err = 0; + struct ar0833_info *info = file->private_data; + + switch (cmd) { + case AR0833_IOCTL_SET_MODE: + { + struct ar0833_mode mode; + + dev_dbg(&info->i2c_client->dev, "AR0833_IOCTL_SET_MODE\n"); + if (copy_from_user(&mode, (const void __user *)arg, + sizeof(struct ar0833_mode))) { + err = -EFAULT; + break; + } + err = ar0833_set_mode(info, &mode); + break; + } + case AR0833_IOCTL_SET_FRAME_LENGTH: + dev_dbg(&info->i2c_client->dev, + "AR0833_IOCTL_SET_FRAME_LENGTH %x\n", (u32)arg); + /* obsolete. we should not update frame length, + it is done by sensor automatically */ + break; + case AR0833_IOCTL_SET_COARSE_TIME: + dev_dbg(&info->i2c_client->dev, + "AR0833_IOCTL_SET_COARSE_TIME %x\n", (u32)arg); + err = ar0833_set_coarse_time(info, (u32)arg, true); + break; + case AR0833_IOCTL_SET_HDR_COARSE_TIME: + { + struct ar0833_hdr values; + + dev_dbg(&info->i2c_client->dev, + "AR0833_IOCTL_SET_HDR_COARSE_TIME\n"); + if (copy_from_user(&values, + (const void __user *)arg, + sizeof(struct ar0833_hdr))) { + err = -EFAULT; + break; + } + err = ar0833_set_hdr_coarse_time(info, &values, true); + break; + } + case AR0833_IOCTL_SET_GAIN: + dev_dbg(&info->i2c_client->dev, + "AR0833_IOCTL_SET_GAIN %x\n", (u32)arg); + err = ar0833_set_gain(info, (u16)arg, true); + break; + case AR0833_IOCTL_SET_GROUP_HOLD: + { + struct ar0833_ae ae; + if (copy_from_user(&ae, (const void __user *)arg, + sizeof(struct ar0833_ae))) { + dev_err(&info->i2c_client->dev, + "%s:fail group hold\n", __func__); + return -EFAULT; + } + return ar0833_set_group_hold(info, &ae); + } + case AR0833_IOCTL_GET_STATUS: + { + u8 status; + + dev_dbg(&info->i2c_client->dev, "AR0833_IOCTL_GET_STATUS\n"); + err = ar0833_get_status(info, &status); + if (err) + break; + if (copy_to_user((void __user *)arg, &status, 2)) { + err = -EFAULT; + break; + } + break; + } + case AR0833_IOCTL_GET_MODE: + { + struct ar0833_modeinfo mode_info, *mi; + + dev_dbg(&info->i2c_client->dev, "AR0833_IOCTL_GET_MODE\n"); + if (copy_from_user(&mode_info, + (const void __user *)arg, + sizeof(struct ar0833_mode))) { + err = -EFAULT; + break; + } + mi = ar0833_get_mode_info(info, &mode_info); + if (mi == NULL) + err = -EFAULT; + else { + if (copy_to_user((void __user *)arg, mi, sizeof(*mi))) { + err = -EFAULT; + break; + } + dev_dbg(&info->i2c_client->dev, "mode %d x %d %s:\n", + mi->xres, mi->yres, mi->hdr ? "HDR" : "REG"); + dev_dbg(&info->i2c_client->dev, + "line_len = %d\n", mi->line_len); + dev_dbg(&info->i2c_client->dev, + "frame_len = %d\n", mi->frame_len); + dev_dbg(&info->i2c_client->dev, + "xsize = %d\n", mi->xsize); + dev_dbg(&info->i2c_client->dev, + "ysize = %d\n", mi->ysize); + dev_dbg(&info->i2c_client->dev, + "vt_pix_clk_div = %d\n", mi->vt_pix_clk_div); + dev_dbg(&info->i2c_client->dev, + "vt_sys_clk_div = %d\n", mi->vt_sys_clk_div); + dev_dbg(&info->i2c_client->dev, + "pre_pll_clk_div = %d\n", mi->pre_pll_clk_div); + dev_dbg(&info->i2c_client->dev, + "pll_multi = %d\n", mi->pll_multi); + dev_dbg(&info->i2c_client->dev, + "op_pix_clk_div = %d\n", mi->op_pix_clk_div); + dev_dbg(&info->i2c_client->dev, + "op_sys_clk_div = %d\n", mi->op_sys_clk_div); + } + break; + } + default: + dev_dbg(&info->i2c_client->dev, "INVALID IOCTL\n"); + err = -EINVAL; + } + + if (err) + dev_dbg(&info->i2c_client->dev, + "%s - %x: ERR = %d\n", __func__, cmd, err); + return err; +} + +static int ar0833_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err; + struct ar0833_info *info; + dev_info(&client->dev, "ar0833: probing sensor.\n"); + + info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); + if (info == NULL) { + dev_err(&client->dev, "%s: kzalloc error\n", __func__); + return -ENOMEM; + } + + info->pdata = client->dev.platform_data; + info->i2c_client = client; + atomic_set(&info->in_use, 0); + info->mode = NULL; + + i2c_set_clientdata(client, info); + + ar0833_power_get(info); + ar0833_mode_info_init(info); + + memcpy(&info->miscdev_info, + &ar0833_device, + sizeof(struct miscdevice)); + + err = misc_register(&info->miscdev_info); + if (err) { + dev_err(&info->i2c_client->dev, + "ar0833: Unable to register misc device!\n"); + kfree(info); + return err; + } +#ifdef CONFIG_DEBUG_FS + ar0833_debug_init(info); + info->enableDCBLC = 0; +#endif + + return 0; +} + +static int ar0833_remove(struct i2c_client *client) +{ + struct ar0833_info *info; + info = i2c_get_clientdata(client); + misc_deregister(&info->miscdev_info); + kfree(info); + +#ifdef CONFIG_DEBUG_FS + if (info->debugfs_root) + debugfs_remove_recursive(info->debugfs_root); +#endif + + return 0; +} + +static const struct i2c_device_id ar0833_id[] = { + { "ar0833", 0 }, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, ar0833_id); + +static struct i2c_driver ar0833_i2c_driver = { + .driver = { + .name = "ar0833", + .owner = THIS_MODULE, + }, + .probe = ar0833_probe, + .remove = ar0833_remove, + .id_table = ar0833_id, +}; + +static int __init ar0833_init(void) +{ + pr_info("ar0833 sensor driver loading\n"); + return i2c_add_driver(&ar0833_i2c_driver); +} + +static void __exit ar0833_exit(void) +{ + i2c_del_driver(&ar0833_i2c_driver); +} + +module_init(ar0833_init); +module_exit(ar0833_exit); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/tegra/ar0833_mode_tbls.c b/drivers/media/video/tegra/ar0833_mode_tbls.c new file mode 100644 index 000000000000..39a3db3bb717 --- /dev/null +++ b/drivers/media/video/tegra/ar0833_mode_tbls.c @@ -0,0 +1,565 @@ +/* + * ar0833_mode_tbls.c - ar0833 sensor mode table + * + * 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/>. + */ + +static struct ar0833_reg Reset_tbl[] = { + {0x301A, 0x0019}, + {AR0833_TABLE_WAIT_MS, 1}, + /* Disable Streaming */ + {0x301A, 0x0218}, + {AR0833_TABLE_END, 0x0000}, +}; + +/*All image captures should be taken with these settings */ +static struct ar0833_reg Default_3P_tbl[] = { + {0x3042, 0x0000}, + {0x30C0, 0x1810}, + {0x30C8, 0x0018}, + {0x30D2, 0x0000}, + {0x30D4, 0xD030}, + {0x30D6, 0x2200}, + {0x30DA, 0x0080}, + {0x30DC, 0x0080}, + {0x30EE, 0x0340}, + {0x316A, 0x8800}, + {0x316C, 0x8200}, + {0x3172, 0x0286}, + {0x3174, 0x8000}, + {0x317C, 0xE103}, + {0x3180, 0xF0FF}, + {0x31E0, 0x0741}, + {0x3ECC, 0x0056}, + {0x3ED0, 0xA8AA}, + {0x3ED2, 0xAAA8}, + {0x3ED4, 0x8ACC}, + {0x3ED8, 0x7288}, + {0x3EDA, 0x77CA}, + {0x3EDE, 0x6664}, + {0x3EE0, 0x26D5}, + {0x3EE4, 0x1548}, + {0x3EE6, 0xB10C}, + {0x3EE8, 0x6E79}, + {0x3EFE, 0x77CC}, + {0x31E6, 0x0000}, + {0x3F00, 0x0028}, + {0x3F02, 0x0140}, + {0x3F04, 0x0002}, + {0x3F06, 0x0004}, + {0x3F08, 0x0008}, + {0x3F0A, 0x0B09}, + {0x3F0C, 0x0302}, + {0x3F10, 0x0505}, + {0x3F12, 0x0303}, + {0x3F14, 0x0101}, + {0x3F16, 0x2020}, + {0x3F18, 0x0404}, + {0x3F1A, 0x7070}, + {0x3F1C, 0x003A}, + {0x3F1E, 0x003C}, + {0x3F2C, 0x2210}, + {0x3F40, 0x2020}, + {0x3F42, 0x0808}, + {0x3F44, 0x0101}, + {AR0833_TABLE_END, 0x0000}, +}; + +u8 Sequencer_Adjustment_v7p2_data[] = { + 0x04, 0x70, 0xC8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x6F, 0x40, 0x14, + 0x0E, 0x23, 0x82, 0x41, 0x5C, 0x54, 0x6E, 0x42, 0x00, 0xC0, 0x5D, + 0x80, 0x5A, 0x80, 0x57, 0x84, 0x64, 0x80, 0x55, 0x86, 0x64, 0x80, + 0x65, 0x88, 0x65, 0x84, 0x58, 0x80, 0x00, 0xC0, 0x80, 0x30, 0x0C, + 0x84, 0x42, 0x82, 0x10, 0x30, 0xA6, 0x5B, 0x80, 0x63, 0x8B, 0x30, + 0x0C, 0xA5, 0x59, 0x84, 0x6C, 0x80, 0x6D, 0x81, 0x5F, 0x60, 0x61, + 0x10, 0x30, 0x88, 0x66, 0x83, 0x6E, 0x80, 0x64, 0x87, 0x64, 0x30, + 0x50, 0xDA, 0x6A, 0x83, 0x6B, 0xA6, 0x30, 0x94, 0x67, 0x84, 0x65, + 0x82, 0x4D, 0x83, 0x65, 0x30, 0x50, 0xA6, 0x58, 0x43, 0x06, 0x00, + 0x8D, 0x45, 0xA0, 0x45, 0x6A, 0x83, 0x6B, 0x06, 0x00, 0x81, 0x43, + 0x9C, 0x58, 0x84, 0x30, 0x90, 0x67, 0x64, 0x88, 0x64, 0x80, 0x65, + 0x88, 0x65, 0x82, 0x10, 0xC0, 0xEB, 0x10, 0xC0, 0x66, 0x85, 0x64, + 0x81, 0x17, 0x00, 0x80, 0x20, 0x0D, 0x80, 0x18, 0x0C, 0x80, 0x64, + 0x30, 0x60, 0x41, 0x82, 0x42, 0xB2, 0x42, 0x80, 0x40, 0x81, 0x40, + 0x80, 0x41, 0x80, 0x42, 0x80, 0x43, 0x83, 0x06, 0xC0, 0x88, 0x44, + 0x87, 0x6A, 0x83, 0x6B, 0x92, 0x44, 0x88, 0x06, 0xC8, 0x81, 0x41, + 0x85, 0x30, 0xA4, 0x67, 0x85, 0x65, 0x87, 0x65, 0x30, 0x60, 0x8D, + 0x42, 0x82, 0x40, 0x82, 0x40, 0x80, 0x41, 0x80, 0x42, 0x80, 0x43, + 0x83, 0x06, 0xC0, 0x88, 0x44, 0x9C, 0x44, 0x88, 0x06, 0xC8, 0x85, + 0x41, 0x6A, 0x83, 0x6B, 0xA0, 0x42, 0x82, 0x40, 0x6C, 0x3A, 0xA8, + 0x80, 0x28, 0x30, 0x70, 0x6F, 0x40, 0x14, 0x0E, 0x23, 0xC2, 0x41, + 0x82, 0x42, 0x00, 0xC0, 0x5D, 0x80, 0x5A, 0x80, 0x57, 0x84, 0x64, + 0x80, 0x55, 0x86, 0x64, 0x80, 0x65, 0x88, 0x65, 0x82, 0x54, 0x80, + 0x58, 0x80, 0x00, 0xC0, 0x86, 0x42, 0x82, 0x10, 0x30, 0x9C, 0x5C, + 0x80, 0x6E, 0x86, 0x5B, 0x80, 0x63, 0x9E, 0x59, 0x8C, 0x5E, 0x8A, + 0x6C, 0x80, 0x6D, 0x81, 0x5F, 0x60, 0x61, 0x88, 0x10, 0x30, 0x66, + 0x83, 0x6E, 0x80, 0x64, 0x87, 0x64, 0x30, 0x50, 0xD3, 0x6A, 0x6B, + 0xAD, 0x30, 0x94, 0x67, 0x84, 0x65, 0x82, 0x4D, 0x83, 0x65, 0x30, + 0x50, 0xA7, 0x43, 0x06, 0x00, 0x8D, 0x45, 0x9A, 0x6A, 0x6B, 0x45, + 0x85, 0x06, 0x00, 0x81, 0x43, 0x8A, 0x6F, 0x96, 0x30, 0x90, 0x67, + 0x64, 0x88, 0x64, 0x80, 0x65, 0x82, 0x10, 0xC0, 0x84, 0x65, 0xEF, + 0x10, 0xC0, 0x66, 0x85, 0x64, 0x81, 0x17, 0x00, 0x80, 0x20, 0x0D, + 0x80, 0x18, 0x0C, 0x80, 0x64, 0x30, 0x60, 0x41, 0x82, 0x42, 0xB2, + 0x42, 0x80, 0x40, 0x82, 0x40, 0x4C, 0x45, 0x92, 0x6A, 0x6B, 0x9B, + 0x45, 0x81, 0x4C, 0x40, 0x8C, 0x30, 0xA4, 0x67, 0x85, 0x65, 0x87, + 0x65, 0x30, 0x60, 0xD3, 0x6A, 0x6B, 0xAC, 0x6C, 0x32, 0xA8, 0x80, + 0x28, 0x30, 0x70, 0x00, 0x80, 0x40, 0x4C, 0xBD, 0x00, 0x0E, 0xBE, + 0x44, 0x88, 0x44, 0xBC, 0x78, 0x09, 0x00, 0x89, 0x04, 0x80, 0x80, + 0x02, 0x40, 0x86, 0x09, 0x00, 0x8E, 0x09, 0x00, 0x80, 0x02, 0x40, + 0x80, 0x04, 0x80, 0x88, 0x7D, 0x9E, 0x86, 0x09, 0x00, 0x87, 0x7A, + 0x00, 0x0E, 0xC3, 0x79, 0x4C, 0x40, 0xBF, 0x70, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +u8 Sequencer_iHDR_v12p12_data[] = { + 0x04, 0x87, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x6F, 0x40, 0x14, + 0x0E, 0x23, 0x82, 0x41, 0x5C, 0x54, 0x6E, 0x42, 0x00, 0xC0, 0x5D, + 0x80, 0x5A, 0x80, 0x57, 0x84, 0x64, 0x80, 0x55, 0x84, 0x00, 0xC4, + 0x10, 0x30, 0x64, 0x80, 0x65, 0x88, 0x65, 0x88, 0x30, 0x0C, 0xB0, + 0x5B, 0x80, 0x63, 0x8B, 0x30, 0x0C, 0xA2, 0x58, 0x87, 0x6C, 0x80, + 0x6D, 0x81, 0x5F, 0x60, 0x61, 0xA3, 0x59, 0x8C, 0x10, 0x30, 0x88, + 0x66, 0x83, 0x6E, 0x80, 0x64, 0x87, 0x64, 0x30, 0x50, 0xDA, 0x6A, + 0x83, 0x6B, 0xA6, 0x30, 0x94, 0x67, 0x84, 0x65, 0x82, 0x4D, 0x83, + 0x65, 0x30, 0x50, 0xA6, 0x58, 0x43, 0x06, 0x00, 0x8D, 0x45, 0xA0, + 0x45, 0x6A, 0x83, 0x6B, 0x06, 0x00, 0x81, 0x43, 0x9C, 0x58, 0x84, + 0x30, 0x90, 0x67, 0x64, 0x88, 0x64, 0x80, 0x65, 0x88, 0x65, 0x82, + 0x10, 0xC0, 0xC3, 0x10, 0xC0, 0x66, 0x85, 0x64, 0x81, 0x17, 0x00, + 0x80, 0x20, 0x0D, 0x80, 0x18, 0x0C, 0x80, 0x64, 0x30, 0x60, 0x41, + 0x82, 0x42, 0xB2, 0x42, 0x80, 0x40, 0x81, 0x40, 0x80, 0x41, 0x80, + 0x42, 0x80, 0x43, 0x83, 0x06, 0xC0, 0x88, 0x44, 0x87, 0x6A, 0x83, + 0x6B, 0x92, 0x44, 0x88, 0x06, 0xC8, 0x81, 0x41, 0x85, 0x30, 0xA4, + 0x67, 0x85, 0x65, 0x87, 0x65, 0x30, 0x60, 0x8D, 0x42, 0x82, 0x40, + 0x82, 0x40, 0x80, 0x41, 0x80, 0x42, 0x80, 0x43, 0x83, 0x06, 0xC0, + 0x88, 0x44, 0x9C, 0x44, 0x88, 0x06, 0xC8, 0x85, 0x41, 0x6A, 0x83, + 0x6B, 0xA0, 0x42, 0x82, 0x40, 0x6C, 0x3A, 0xA8, 0x80, 0x28, 0x30, + 0x40, 0x80, 0x41, 0x80, 0x42, 0x80, 0x43, 0x88, 0x06, 0xC0, 0x83, + 0x44, 0xA0, 0x44, 0x88, 0x06, 0xC8, 0x81, 0x41, 0xA6, 0x42, 0x82, + 0x40, 0x82, 0x40, 0x80, 0x41, 0x80, 0x42, 0x80, 0x43, 0x83, 0x06, + 0xC0, 0x88, 0x44, 0xA0, 0x44, 0x88, 0x06, 0xC8, 0x81, 0x41, 0xA6, + 0x42, 0x82, 0x40, 0x80, 0x70, 0x00, 0x6F, 0x40, 0x14, 0x0E, 0x23, + 0xC2, 0x41, 0x82, 0x42, 0x00, 0xC0, 0x5D, 0x80, 0x5A, 0x80, 0x57, + 0x84, 0x64, 0x80, 0x55, 0x86, 0x64, 0x80, 0x65, 0x88, 0x65, 0x82, + 0x54, 0x80, 0x58, 0x80, 0x00, 0xC0, 0x86, 0x42, 0x82, 0x10, 0x30, + 0x9C, 0x5C, 0x80, 0x6E, 0x86, 0x5B, 0x80, 0x63, 0x9E, 0x59, 0x8C, + 0x5E, 0x8A, 0x6C, 0x80, 0x6D, 0x81, 0x5F, 0x60, 0x61, 0x10, 0x30, + 0x88, 0x66, 0x83, 0x6E, 0x80, 0x64, 0x87, 0x64, 0x30, 0x50, 0xD3, + 0x6A, 0x6B, 0xAD, 0x30, 0x94, 0x67, 0x84, 0x65, 0x82, 0x4D, 0x83, + 0x65, 0x30, 0x50, 0xA7, 0x43, 0x06, 0x00, 0x8D, 0x45, 0x9A, 0x6A, + 0x6B, 0x45, 0x85, 0x06, 0x00, 0x81, 0x43, 0x8A, 0x6F, 0x96, 0x30, + 0x90, 0x67, 0x64, 0x88, 0x64, 0x80, 0x65, 0x82, 0x10, 0xC0, 0x84, + 0x65, 0xEF, 0x10, 0xC0, 0x66, 0x85, 0x64, 0x81, 0x17, 0x00, 0x80, + 0x20, 0x0D, 0x80, 0x18, 0x0C, 0x80, 0x64, 0x30, 0x60, 0x41, 0x82, + 0x42, 0xB2, 0x42, 0x80, 0x40, 0x82, 0x40, 0x4C, 0x45, 0x92, 0x6A, + 0x6B, 0x9B, 0x45, 0x81, 0x4C, 0x40, 0x8C, 0x30, 0xA4, 0x67, 0x85, + 0x65, 0x87, 0x65, 0x30, 0x60, 0xD3, 0x6A, 0x6B, 0xAC, 0x6C, 0x32, + 0xA8, 0x80, 0x28, 0x30, 0x70, 0x00, 0x80, 0x40, 0x4C, 0xBD, 0x00, + 0x0E, 0xBE, 0x44, 0x88, 0x44, 0xBC, 0x78, 0x00, 0x00, +}; + +static struct ar0833_reg_blob Sequencer_iHDR_v12p12 = { + .addr = 0x3D00, + .size = sizeof(Sequencer_iHDR_v12p12_data), + .data = Sequencer_iHDR_v12p12_data, +}; + +static struct ar0833_reg_blob Sequencer_Adjustment_v7p2 = { + .addr = 0x3D00, + .size = sizeof(Sequencer_Adjustment_v7p2_data), + .data = Sequencer_Adjustment_v7p2_data, +}; + +u8 Sequencer_iHDR_v7p2_data[] = { + 0x04, 0x88, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x6F, 0x40, 0x14, + 0x0E, 0x23, 0x82, 0x41, 0x5C, 0x54, 0x6E, 0x42, 0x00, 0xC0, 0x5D, + 0x80, 0x5A, 0x80, 0x57, 0x84, 0x64, 0x80, 0x55, 0x86, 0x64, 0x80, + 0x65, 0x88, 0x65, 0x84, 0x58, 0x80, 0x00, 0xC0, 0x80, 0x30, 0x0C, + 0x84, 0x42, 0x82, 0x10, 0x30, 0xA6, 0x5B, 0x80, 0x63, 0x8B, 0x30, + 0x0C, 0xA5, 0x59, 0x84, 0x6C, 0x80, 0x6D, 0x81, 0x5F, 0x60, 0x61, + 0x10, 0x30, 0x88, 0x66, 0x83, 0x6E, 0x80, 0x64, 0x87, 0x64, 0x30, + 0x50, 0xDA, 0x6A, 0x83, 0x6B, 0xA6, 0x30, 0x94, 0x67, 0x84, 0x65, + 0x82, 0x4D, 0x83, 0x65, 0x30, 0x50, 0xA6, 0x58, 0x43, 0x06, 0x00, + 0x8D, 0x45, 0xA0, 0x45, 0x6A, 0x83, 0x6B, 0x06, 0x00, 0x81, 0x43, + 0x9C, 0x58, 0x84, 0x30, 0x90, 0x67, 0x64, 0x88, 0x64, 0x80, 0x65, + 0x88, 0x65, 0x82, 0x10, 0xC0, 0xEB, 0x10, 0xC0, 0x66, 0x85, 0x64, + 0x81, 0x17, 0x00, 0x80, 0x20, 0x0D, 0x80, 0x18, 0x0C, 0x80, 0x64, + 0x30, 0x60, 0x41, 0x82, 0x42, 0xB2, 0x42, 0x80, 0x40, 0x81, 0x40, + 0x80, 0x41, 0x80, 0x42, 0x80, 0x43, 0x83, 0x06, 0xC0, 0x88, 0x44, + 0x87, 0x6A, 0x83, 0x6B, 0x92, 0x44, 0x88, 0x06, 0xC8, 0x81, 0x41, + 0x85, 0x30, 0xA4, 0x67, 0x85, 0x65, 0x87, 0x65, 0x30, 0x60, 0x8D, + 0x42, 0x82, 0x40, 0x82, 0x40, 0x80, 0x41, 0x80, 0x42, 0x80, 0x43, + 0x83, 0x06, 0xC0, 0x88, 0x44, 0x9C, 0x44, 0x88, 0x06, 0xC8, 0x85, + 0x41, 0x6A, 0x83, 0x6B, 0xA0, 0x42, 0x82, 0x40, 0x6C, 0x3A, 0xA8, + 0x80, 0x28, 0x30, 0x40, 0x80, 0x41, 0x80, 0x42, 0x80, 0x43, 0x88, + 0x06, 0xC0, 0x83, 0x44, 0xA0, 0x44, 0x88, 0x06, 0xC8, 0x81, 0x41, + 0xA6, 0x42, 0x82, 0x40, 0x82, 0x40, 0x80, 0x41, 0x80, 0x42, 0x80, + 0x43, 0x83, 0x06, 0xC0, 0x88, 0x44, 0xA0, 0x44, 0x88, 0x06, 0xC8, + 0x81, 0x41, 0xA6, 0x42, 0x82, 0x40, 0x80, 0x70, 0x6F, 0x40, 0x14, + 0x0E, 0x23, 0xC2, 0x41, 0x82, 0x42, 0x00, 0xC0, 0x5D, 0x80, 0x5A, + 0x80, 0x57, 0x84, 0x64, 0x80, 0x55, 0x86, 0x64, 0x80, 0x65, 0x88, + 0x65, 0x82, 0x54, 0x80, 0x58, 0x80, 0x00, 0xC0, 0x86, 0x42, 0x82, + 0x10, 0x30, 0x9C, 0x5C, 0x80, 0x6E, 0x86, 0x5B, 0x80, 0x63, 0x9E, + 0x59, 0x8C, 0x5E, 0x8A, 0x6C, 0x80, 0x6D, 0x81, 0x5F, 0x60, 0x61, + 0x10, 0x30, 0x88, 0x66, 0x83, 0x6E, 0x80, 0x64, 0x87, 0x64, 0x30, + 0x50, 0xD3, 0x6A, 0x6B, 0xAD, 0x30, 0x94, 0x67, 0x84, 0x65, 0x82, + 0x4D, 0x83, 0x65, 0x30, 0x50, 0xA7, 0x43, 0x06, 0x00, 0x8D, 0x45, + 0x9A, 0x6A, 0x6B, 0x45, 0x85, 0x06, 0x00, 0x81, 0x43, 0x8A, 0x6F, + 0x96, 0x30, 0x90, 0x67, 0x64, 0x88, 0x64, 0x80, 0x65, 0x82, 0x10, + 0xC0, 0x84, 0x65, 0xEF, 0x10, 0xC0, 0x66, 0x85, 0x64, 0x81, 0x17, + 0x00, 0x80, 0x20, 0x0D, 0x80, 0x18, 0x0C, 0x80, 0x64, 0x30, 0x60, + 0x41, 0x82, 0x42, 0xB2, 0x42, 0x80, 0x40, 0x82, 0x40, 0x4C, 0x45, + 0x92, 0x6A, 0x6B, 0x9B, 0x45, 0x81, 0x4C, 0x40, 0x8C, 0x30, 0xA4, + 0x67, 0x85, 0x65, 0x87, 0x65, 0x30, 0x60, 0xD3, 0x6A, 0x6B, 0xAC, + 0x6C, 0x32, 0xA8, 0x80, 0x28, 0x30, 0x70, 0x00, 0x80, 0x40, 0x4C, + 0xBD, 0x00, 0x0E, 0xBE, 0x44, 0x88, 0x44, 0xBC, 0x78, +}; + +static struct ar0833_reg_blob Sequencer_iHDR_v7p2 = { + .addr = 0x3D00, + .size = sizeof(Sequencer_iHDR_v7p2_data), + .data = Sequencer_iHDR_v7p2_data, +}; + +static struct ar0833_reg MipiTimings[] = { + {0x31B0, 0x0060}, + {0x31B2, 0x0042}, + {0x31B4, 0x4C36}, + {0x31B6, 0x5218}, + {0x31B8, 0x404A}, + {0x31BA, 0x028A}, + {0x31BC, 0x0008}, + {AR0833_TABLE_WAIT_MS, 1}, + {AR0833_TABLE_END, 0x0000}, +}; + +enum { + AR0833_TBL_RESET, + AR0833_TBL_DEF3P, + AR0833_TBL_SEQ_ADJ_V7P2, + AR0833_TBL_SEQ_HDR_V12P12, + AR0833_TBL_SEQ_HDR_V7P2, + AR0833_TBL_MIPITM, + NUM_OF_SUBTBLS, +}; + +static void *sub_tbls[] = { + [AR0833_TBL_RESET] = Reset_tbl, + [AR0833_TBL_DEF3P] = Default_3P_tbl, + [AR0833_TBL_SEQ_ADJ_V7P2] = &Sequencer_Adjustment_v7p2, + [AR0833_TBL_SEQ_HDR_V12P12] = &Sequencer_iHDR_v12p12, + [AR0833_TBL_SEQ_HDR_V7P2] = &Sequencer_iHDR_v7p2, + [AR0833_TBL_MIPITM] = MipiTimings, +}; + +/* + * Initialize + * XMCLK=24000000 + * PAUSE=1 + * STATE= Master Clock, 292800000 + * REG=0x301A, 0x0218, Disable Streaming + * LOAD=Default_3P + * LOAD=Sequencer_Adjustment_v7p2 + */ +static struct ar0833_reg mode_3264x2448_30fps[] = { + {AR0833_TABLE_CALL, AR0833_TBL_RESET}, + {AR0833_TABLE_CALL, AR0833_TBL_DEF3P}, + {AR0833_TABLE_WAIT_MS, 1}, + {AR0833_TABLE_BLOB, AR0833_TBL_SEQ_ADJ_V7P2}, + + /* PLL Configuration */ + {0x0300, 0x0005}, /*VT_PIX_CLK_DIV=5 */ + {0x0302, 0x0001}, /*VT_SYS_CLK_DIV=1 */ + {0x0304, 0x0004}, /*PRE_PLL_CLK_DIV=4 Note: 24MHz/4=6MHz */ + /*PLL_MULTIPLIER=122 Note: Running at 732MHz */ + {0x0306, 0x007A}, + {0x0308, 0x000A}, /*OP_PIX_CLK_DIV=10 */ + {0x030A, 0x0001}, /*OP_SYS_CLK_DIV=1 */ + {0x3064, 0x7800}, + {AR0833_TABLE_WAIT_MS, 1}, + + /* LOAD=MIPI Timings */ + {AR0833_TABLE_CALL, AR0833_TBL_MIPITM}, + + /* Frame Timing */ + {0x0340, 0x0A10}, /*FRAME_LENGTH_LINES */ + {0x0342, 0x0ECC}, /*LINE_LENGTH_PCK */ + {0x0344, 0x0008}, /*X_ADDR_START 8 */ + {0x0346, 0x0008}, /*Y_ADDR_START 8 */ + {0x0348, 0x0CC7}, /*X_ADDR_END 3271 */ + {0x034A, 0x0997}, /*Y_ADDR_END 2455 */ + {0x034C, 0x0CC0}, /*X_OUTPUT_SIZE 3264 */ + {0x034E, 0x0990}, /*Y_OUTPUT_SIZE 2448 */ + {0x3040, 0x4041}, /*X_ODD_INC & Y_ODD_INC */ + {0x0202, 0x0A01}, /*COARSE_INTEGRATION_TIME */ + {0x305E, 0x1000}, /* gain */ + /*Scaling Enabling: 0= disable, 1= x-dir, 2= xy-dir */ + {0x0400, 0x0000}, + {0x0402, 0x0000}, /*Co-Site Scaling */ + {0x0404, 0x0010}, /*Scale_M = 16 */ + {0x0408, 0x1010}, + {0x040A, 0x0210}, + {0x306E, 0x9080}, /*Data Path Select */ + {0x301A, 0x001C}, /* Enable Streaming */ + {AR0833_TABLE_END, 0x0000}, +}; + +static struct ar0833_reg mode_3264x2448_HDR_30fps[] = { + {AR0833_TABLE_CALL, AR0833_TBL_RESET}, + {AR0833_TABLE_CALL, AR0833_TBL_DEF3P}, + + {0x0300, 0x0004}, /* VT_PIX_CLK_DIV = 4 */ + {0x0304, 0x0004}, + {0x0306, 0x007A}, + + /* LOAD=MIPI Timings */ + {AR0833_TABLE_CALL, AR0833_TBL_MIPITM}, + + {0x0340, 2579}, /* FLL = 2579 */ + {0x0342, 4832}, /* LLPCK = 4832 */ + {0x0344, 0x0008}, /*X_ADDR_START */ + {0x0346, 0x0008}, /*Y_ADDR_START */ + {0x0348, 0x0CC7}, /*X_ADDR_END */ + {0x034A, 0x0997}, /*Y_ADDR_END */ + {0x034C, 0x0CC0}, /*X_OUTPUT_SIZE */ + {0x034E, 0x0990}, /*Y_OUTPUT_SIZE */ + {0x0202, 2400}, /* T1 Coarse Integration */ + {0x3088, 300}, /* T2 Coarse Integration */ + {0x305E, 0x1000}, /* gain */ + {0x3040, 0x4041}, /*Default read mode */ + + /* Disable Streaming */ + {0x301A, 0x0218}, + {AR0833_TABLE_WAIT_MS, 1}, + /*LOAD=Sequencer_iHDR_v7p2 */ + {AR0833_TABLE_BLOB, AR0833_TBL_SEQ_HDR_V12P12}, + /* Enable iHDR readout */ + {0x303E, 0x0001}, + /* Other Settings + BLC Options need to be set to not use auto calib in iHDR */ + {0x30C0, 0x2001}, + {0x30C8, 0x2020}, + /*Disable Defect Correction, check for 1DDC? -> REG= 0x31E0, 0x01F1 */ + {0x31E0, 0x0000}, + + /* No3 iHDR*/ + {0x316C, 0x8200}, + {0x3EFE, 0x2222}, + {0x3EE6, 0xB10C}, + {0x3ED8, 0x7288}, + + {0x301A, 0x001C}, /* Enable Streaming */ + {AR0833_TABLE_END, 0x0000}, +}; + +/* + * Initialize + * XMCLK=24000000 + * PAUSE=1 + * STATE= Master Clock, 292800000 + * REG=0x301A, 0x0218, Disable Streaming + * LOAD=Default_3P + * LOAD=Sequencer_Adjustment_v7p2 + */ +static struct ar0833_reg mode_1920x1080_HDR_30fps[] = { + {AR0833_TABLE_CALL, AR0833_TBL_RESET}, + {AR0833_TABLE_CALL, AR0833_TBL_DEF3P}, + + {0x0304, 0x0004}, + {0x0306, 0x007A}, + + /* LOAD=MIPI Timings */ + {AR0833_TABLE_CALL, AR0833_TBL_MIPITM}, + + {0x0340, 1990}, /* FLL = 2579 */ + {0x0342, 4832}, /* LLPCK = 4832v */ + {0x0344, 0x02A0}, /*X_ADDR_START 672 */ + {0x0346, 0x0130}, /*Y_ADDR_START 304 */ + {0x0348, 0x0A1F}, /*X_ADDR_END 2591 */ + {0x034A, 0x0567}, /*Y_ADDR_END 1751 */ + {0x034C, 0x0780}, /*X_OUTPUT_SIZE 1920 */ + {0x034E, 0x0438}, /*Y_OUTPUT_SIZE 1080 */ + {0x3040, 0x4041}, /*X_ODD_INC and Y_ODD_INC */ + {0x0202, 1952}, /* T1 Coarse Integration */ + {0x3088, 244}, /* T2 Coarse Integration */ + {0x305E, 0x1000}, /* gain */ + + /* Disable Streaming */ + {0x301A, 0x0218}, + {AR0833_TABLE_WAIT_MS, 1}, + {AR0833_TABLE_BLOB, AR0833_TBL_SEQ_HDR_V7P2}, + + {0x303E, 0x0001}, /* Enable iHDR readout */ + /* Other Settings + BLC Options need to be set to not use auto calib in iHDR */ + {0x30C0, 0x2001}, + {0x30C8, 0x2020}, + /*Disable Defect Correction, check for 1DDC? -> REG= 0x31E0, 0x01F1 */ + {0x31E0, 0x0000}, + /*BITFIELD=0x301A,0x0004,0 Disable Streaming */ + {0x316C, 0x8200}, /* auto switch Txlo at 2x */ + {0x3EFE, 0xC1C1}, /* Txlo Level: Txlo1=-12,Txlo0=+1 */ + {0x3EE6, 0xB10D}, /* Txlo polarity: Txlo1-,Txlo0+ */ + {0x3ED8, 0x7288}, /* Txlo booster setting */ + {0x301A, 0x001C}, /* Enable Streaming */ + {AR0833_TABLE_END, 0x0000}, +}; + +/* + * Initialize + * XMCLK=24000000 + * PAUSE=1 + * STATE= Master Clock, 292800000 + * REG=0x301A, 0x0218, Disable Streaming + * LOAD=Default_3P + * LOAD=Sequencer_Adjustment_v7p2 + */ +static struct ar0833_reg mode_3264x1836_HDR_30fps[] = { + {AR0833_TABLE_CALL, AR0833_TBL_RESET}, + {AR0833_TABLE_CALL, AR0833_TBL_DEF3P}, + + {0x0300, 0x0005}, /* VT_PIX_CLK_DIV = 5 */ + {0x0304, 0x0004}, + {0x0306, 0x007A}, + + /* LOAD=MIPI Timings */ + {AR0833_TABLE_CALL, AR0833_TBL_MIPITM}, + + {0x0340, 1990}, /* FLL = 2579 */ + {0x0342, 4832}, /* LLPCK = 4832 */ + {0x0344, 0x0008}, /*X_ADDR_START */ + {0x0346, 0x0130}, /*Y_ADDR_START */ + {0x0348, 0x0CC7}, /*X_ADDR_END */ + {0x034A, 0x085B}, /*Y_ADDR_END */ + {0x034C, 0x0CC0}, /*X_OUTPUT_SIZE */ + {0x034E, 0x072C}, /*Y_OUTPUT_SIZE */ + {0x3040, 0x4041}, /*Default read mode */ + {0x0202, 1952}, /* T1 Coarse Integration */ + {0x3088, 244}, /* T2 Coarse Integration */ + {0x305E, 0x1000}, /* gain */ + + /* Disable Streaming */ + {0x301A, 0x0218}, + {AR0833_TABLE_WAIT_MS, 1}, + /*LOAD=Sequencer_iHDR_v7p2 */ + {AR0833_TABLE_BLOB, AR0833_TBL_SEQ_HDR_V7P2}, + /* Enable iHDR readout */ + {0x303E, 0x0001}, + /* Other Settings + BLC Options need to be set to not use auto calib in iHDR */ + {0x30C0, 0x2001}, + {0x30C8, 0x2020}, + /*Disable Defect Correction, check for 1DDC? -> REG= 0x31E0, 0x01F1 */ + {0x31E0, 0x0000}, + {0x301A, 0x001C}, /* Enable Streaming */ + {AR0833_TABLE_END, 0x0000}, +}; + +/* + * Initialize + * XMCLK=24000000 + * PAUSE=1 + * STATE= Master Clock, 292800000 + * REG=0x301A, 0x0218, Disable Streaming + * LOAD=Default_3P + * LOAD=Sequencer_Adjustment_v7p2 + */ +static struct ar0833_reg mode_1920x1080_30fps[] = { + {AR0833_TABLE_CALL, AR0833_TBL_RESET}, + {AR0833_TABLE_CALL, AR0833_TBL_DEF3P}, + {AR0833_TABLE_WAIT_MS, 1}, + {AR0833_TABLE_BLOB, AR0833_TBL_SEQ_ADJ_V7P2}, + + /*PLL Configuration */ + {0x0300, 0x0005}, /*VT_PIX_CLK_DIV=5 */ + {0x0302, 0x0001}, /*VT_SYS_CLK_DIV=1 */ + {0x0304, 0x0004}, /*PRE_PLL_CLK_DIV=4 Note: 24MHz/4=6MHz */ + {0x0306, 0x007A}, /*PLL_MULTIPLIER=122 Note: Running at 732MHz */ + {0x0308, 0x000A}, /*OP_PIX_CLK_DIV=10 */ + {0x030A, 0x0001}, /*OP_SYS_CLK_DIV=1 */ + {0x3064, 0x7800}, + {AR0833_TABLE_WAIT_MS, 1}, + + /*LOAD=MIPI Timings */ + {AR0833_TABLE_CALL, AR0833_TBL_MIPITM}, + + {0x0340, 0x079D}, /*FRAME_LENGTH_LINES */ + {0x0342, 0x139E}, /*LINE_LENGTH_PCK */ + {0x0344, 0x0008}, /*X_ADDR_START */ + {0x0346, 0x0130}, /*Y_ADDR_START */ + {0x0348, 0x0CC7}, /*X_ADDR_END */ + {0x034A, 0x085B}, /*Y_ADDR_END */ + {0x034C, 0x0780}, /*X_OUTPUT_SIZE */ + {0x034E, 0x0438}, /*Y_OUTPUT_SIZE */ + {0x3040, 0x4041}, /*X_ODD_INC & Y_ODD_INC */ + {0x0202, 0x0700}, /*COARSE_INTEGRATION_TIME */ + {0x305E, 0x1000}, /* gain */ + /*Scaling Enabling: 0= disable, 1= x-dir, 2= xy-dir */ + {0x0400, 0x0002}, + {0x0402, 0x0000}, /*Co-Site Scaling */ + {0x0404, 0x001A}, /*Scale_M = 16 */ + {0x0408, 0x0B0C}, + {0x040A, 0x018C}, + {0x306E, 0x9090}, /*Data Path Select */ + {0x301A, 0x001C}, /* Enable Streaming */ + {AR0833_TABLE_END, 0x0000}, +}; + +/* + * Initialize + * XMCLK=24000000 + * PAUSE=1 + * STATE= Master Clock, 292800000 + * REG=0x301A, 0x0218, Disable Streaming + * LOAD=Default_3P + * LOAD=Sequencer_Adjustment_v7p2 + */ +static struct ar0833_reg mode_3264x1836_30fps[] = { + {AR0833_TABLE_CALL, AR0833_TBL_RESET}, + {AR0833_TABLE_CALL, AR0833_TBL_DEF3P}, + {AR0833_TABLE_WAIT_MS, 1}, + {AR0833_TABLE_BLOB, AR0833_TBL_SEQ_ADJ_V7P2}, + + /*PLL Configuration */ + {0x0300, 0x0005}, /*VT_PIX_CLK_DIV=5 */ + {0x0302, 0x0001}, /*VT_SYS_CLK_DIV=1 */ + {0x0304, 0x0004}, /*PRE_PLL_CLK_DIV=4 Note: 24MHz/4=6MHz */ + {0x0306, 0x007A}, /*PLL_MULTIPLIER=122 Note: Running at 732MHz */ + {0x0308, 0x000A}, /*OP_PIX_CLK_DIV=10 */ + {0x030A, 0x0001}, /*OP_SYS_CLK_DIV=1 */ + {0x3064, 0x7800}, + {AR0833_TABLE_WAIT_MS, 1}, + + {AR0833_TABLE_CALL, AR0833_TBL_MIPITM}, + + /*Frame Timing */ + {0x0340, 0x079D}, /*FRAME_LENGTH_LINES */ + {0x0342, 0x139E}, /*LINE_LENGTH_PCK */ + {0x0344, 0x0008}, /*X_ADDR_START */ + {0x0346, 0x0130}, /*Y_ADDR_START */ + {0x0348, 0x0CC7}, /*X_ADDR_END */ + {0x034A, 0x085B}, /*Y_ADDR_END */ + {0x034C, 0x0CC0}, /*X_OUTPUT_SIZE */ + {0x034E, 0x072C}, /*Y_OUTPUT_SIZE */ + {0x3040, 0x4041}, /*X_ODD_INC & Y_ODD_INC */ + {0x0202, 0x0700}, /*COARSE_INTEGRATION_TIME */ + {0x305E, 0x1000}, /* gain */ + /*Scaling Enabling: 0= disable, 1= x-dir, 2= xy-dir */ + {0x0400, 0x0000}, + {0x0402, 0x0000}, /*Co-Site Scaling */ + {0x0404, 0x0010}, /*Scale_M = 16 */ + {0x0408, 0x1010}, + {0x040A, 0x0210}, + {0x306E, 0x9080}, /*Data Path Select */ + {0x301A, 0x001C}, /* Enable Streaming */ + {AR0833_TABLE_END, 0x0000}, +}; diff --git a/drivers/media/video/tegra/dw9718.c b/drivers/media/video/tegra/dw9718.c new file mode 100644 index 000000000000..c2044ae3ac63 --- /dev/null +++ b/drivers/media/video/tegra/dw9718.c @@ -0,0 +1,1077 @@ +/* + * dw9718.c - dw9718 focuser driver + * + * 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/>. + */ +/* Implementation + * -------------- + * The board level details about the device need to be provided in the board + * file with the <device>_platform_data structure. + * Standard among NVC kernel drivers in this structure is: + * .cfg = Use the NVC_CFG_ defines that are in nvc.h. + * Descriptions of the configuration options are with the defines. + * This value is typically 0. + * .num = The number of the instance of the device. This should start at 1 and + * and increment for each device on the board. This number will be + * appended to the MISC driver name, Example: /dev/focuser.1 + * If not used or 0, then nothing is appended to the name. + * .sync = If there is a need to synchronize two devices, then this value is + * the number of the device instance (.num above) this device is to + * sync to. For example: + * Device 1 platform entries = + * .num = 1, + * .sync = 2, + * Device 2 platfrom entries = + * .num = 2, + * .sync = 1, + * The above example sync's device 1 and 2. + * To disable sync, set .sync = 0. Note that the .num = 0 device is not + * allowed to be synced to. + * This is typically used for stereo applications. + * .dev_name = The MISC driver name the device registers as. If not used, + * then the part number of the device is used for the driver name. + * If using the NVC user driver then use the name found in this + * driver under _default_pdata. + * .gpio_count = The ARRAY_SIZE of the nvc_gpio_pdata table. + * .gpio = A pointer to the nvc_gpio_pdata structure's platform GPIO data. + * The GPIO mechanism works by cross referencing the .gpio_type key + * among the nvc_gpio_pdata GPIO data and the driver's nvc_gpio_init + * GPIO data to build a GPIO table the driver can use. The GPIO's + * defined in the device header file's _gpio_type enum are the + * gpio_type keys for the nvc_gpio_pdata and nvc_gpio_init structures. + * These need to be present in the board file's nvc_gpio_pdata + * structure for the GPIO's that are used. + * The driver's GPIO logic uses assert/deassert throughout until the + * low level _gpio_wr/rd calls where the .assert_high is used to + * convert the value to the correct signal level. + * See the GPIO notes in nvc.h for additional information. + * + * The following is specific to NVC kernel focus drivers: + * .nvc = Pointer to the nvc_focus_nvc structure. This structure needs to + * be defined and populated if overriding the driver defaults. + * .cap = Pointer to the nvc_focus_cap structure. This structure needs to + * be defined and populated if overriding the driver defaults. + * + * The following is specific to this NVC kernel focus driver: + * .info = Pointer to the dw9718_pdata_info structure. This structure does + * not need to be defined and populated unless overriding ROM data. + * + * Power Requirements: + * The device's header file defines the voltage regulators needed with the + * enumeration <device>_vreg. The order these are enumerated is the order + * the regulators will be enabled when powering on the device. When the + * device is powered off the regulators are disabled in descending order. + * The <device>_vregs table in this driver uses the nvc_regulator_init + * structure to define the regulator ID strings that go with the regulators + * defined with <device>_vreg. These regulator ID strings (or supply names) + * will be used in the regulator_get function in the _vreg_init function. + * The board power file and <device>_vregs regulator ID strings must match. + */ + +#include <linux/fs.h> +#include <linux/seq_file.h> +#include <linux/debugfs.h> +#include <linux/i2c.h> +#include <linux/miscdevice.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/uaccess.h> +#include <linux/list.h> +#include <linux/regulator/consumer.h> +#include <linux/gpio.h> +#include <linux/module.h> +#include <media/dw9718.h> + +#define ENABLE_DEBUGFS_INTERFACE + +#define dw9718_ID 0x04 +#define dw9718_FOCAL_LENGTH_FLOAT (4.570f) +#define dw9718_FNUMBER_FLOAT (2.8f) +#define dw9718_FOCAL_LENGTH (0x40923D71) /* 4.570f */ +#define dw9718_FNUMBER (0x40333333) /* 2.8f */ +#define dw9718_SLEW_RATE 0x0060 +#define dw9718_ACTUATOR_RANGE 1023 +#define dw9718_SETTLETIME 30 +#define dw9718_FOCUS_MACRO 620 +#define dw9718_FOCUS_INFINITY 70 +#define dw9718_POS_LOW_DEFAULT 0 +#define dw9718_POS_HIGH_DEFAULT 1023 +#define dw9718_POS_CLAMP 0x03ff +/* Need to decide exact value of VCM_THRESHOLD and its use */ +/* define dw9718_VCM_THRESHOLD 20 */ + +struct dw9718_info { + atomic_t in_use; + struct i2c_client *i2c_client; + struct dw9718_platform_data *pdata; + struct miscdevice miscdev; + struct list_head list; + int pwr_dev; + struct dw9718_power_rail power; + int status; + u32 cur_pos; + u8 s_mode; + bool reset_flag; + struct dw9718_info *s_info; + struct nvc_focus_nvc nvc; + struct nvc_focus_cap cap; + struct nv_focuser_config nv_config; +}; + +/** + * The following are default values + */ +static struct nvc_focus_cap dw9718_default_cap = { + .version = NVC_FOCUS_CAP_VER2, + .slew_rate = dw9718_SLEW_RATE, + .actuator_range = dw9718_ACTUATOR_RANGE, + .settle_time = dw9718_SETTLETIME, + .focus_macro = dw9718_FOCUS_MACRO, + .focus_infinity = dw9718_FOCUS_INFINITY, + .focus_hyper = dw9718_FOCUS_INFINITY, +}; + +static struct nvc_focus_nvc dw9718_default_nvc = { + .focal_length = dw9718_FOCAL_LENGTH, + .fnumber = dw9718_FNUMBER, + .max_aperature = dw9718_FNUMBER, +}; + +static struct dw9718_platform_data dw9718_default_pdata = { + .cfg = 0, + .num = 0, + .sync = 0, + .dev_name = "focuser", +}; +static LIST_HEAD(dw9718_info_list); +static DEFINE_SPINLOCK(dw9718_spinlock); + +static int dw9718_i2c_wr8(struct dw9718_info *info, u8 reg, u8 val) +{ + struct i2c_msg msg; + u8 buf[2]; + buf[0] = reg; + buf[1] = val; + msg.addr = info->i2c_client->addr; + msg.flags = 0; + msg.len = 2; + msg.buf = &buf[0]; + if (i2c_transfer(info->i2c_client->adapter, &msg, 1) != 1) + return -EIO; + return 0; +} + +static int dw9718_i2c_wr16(struct dw9718_info *info, u8 reg, u16 val) +{ + struct i2c_msg msg; + u8 buf[3]; + buf[0] = reg; + buf[1] = (u8)(val >> 8); + buf[2] = (u8)(val & 0xff); + msg.addr = info->i2c_client->addr; + msg.flags = 0; + msg.len = 3; + msg.buf = &buf[0]; + if (i2c_transfer(info->i2c_client->adapter, &msg, 1) != 1) + return -EIO; + return 0; +} + +/** + * Below are device specific functions. + */ +static int dw9718_position_wr(struct dw9718_info *info, s32 position) +{ + int err; + + dev_dbg(&info->i2c_client->dev, "%s %d\n", __func__, position); + position &= dw9718_POS_CLAMP; + err = dw9718_i2c_wr16(info, DW9718_VCM_CODE_MSB, position); + if (!err) + info->cur_pos = position; + else + dev_err(&info->i2c_client->dev, "%s: ERROR set position %d", + __func__, position); + return err; +} + +int dw9718_set_arc_mode(struct dw9718_info *info) +{ + int err; + u32 sr = info->nv_config.slew_rate; + + dev_dbg(&info->i2c_client->dev, "%s %x\n", __func__, sr); + /* set ARC enable */ + err = dw9718_i2c_wr8(info, DW9718_CONTROL, (sr >> 16) & 0xFF); + if (err) { + dev_err(&info->i2c_client->dev, + "%s: CONTROL reg write failed\n", __func__); + goto set_arc_mode_done; + } + usleep_range(80, 100); + + /* set the ARC RES2 */ + err = dw9718_i2c_wr8(info, DW9718_SWITCH_MODE, (sr >> 8) & 0xFF); + if (err) { + dev_err(&info->i2c_client->dev, + "%s: MODE write failed\n", __func__); + goto set_arc_mode_done; + } + + err = dw9718_i2c_wr8(info, DW9718_SACT, sr & 0XFF); + if (err) { + dev_err(&info->i2c_client->dev, + "%s: RES write failed\n", __func__); + goto set_arc_mode_done; + } + + err = dw9718_position_wr(info, 0); + +set_arc_mode_done: + return err; +} + +static int dw9718_pm_wr(struct dw9718_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: + if (info->pdata && info->pdata->power_off) + info->pdata->power_off(&info->power); + break; + case NVC_PWR_STDBY_OFF: + case NVC_PWR_STDBY: + if (info->pdata && info->pdata->power_off) + info->pdata->power_off(&info->power); + break; + case NVC_PWR_COMM: + case NVC_PWR_ON: + if (info->pdata && info->pdata->power_on) + info->pdata->power_on(&info->power); + dw9718_set_arc_mode(info); + 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); + + return err; +} + +static int dw9718_power_put(struct dw9718_power_rail *pw) +{ + if (unlikely(!pw)) + return -EFAULT; + + if (likely(pw->vdd)) + regulator_put(pw->vdd); + + if (likely(pw->vdd_i2c)) + regulator_put(pw->vdd_i2c); + + pw->vdd = NULL; + pw->vdd_i2c = NULL; + + return 0; +} + +static int dw9718_regulator_get(struct dw9718_info *info, + struct regulator **vreg, char vreg_name[]) +{ + struct regulator *reg = NULL; + int err = 0; + + reg = regulator_get(&info->i2c_client->dev, vreg_name); + if (unlikely(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; + return err; +} + +static int dw9718_power_get(struct dw9718_info *info) +{ + struct dw9718_power_rail *pw = &info->power; + + dw9718_regulator_get(info, &pw->vdd, "vdd"); + dw9718_regulator_get(info, &pw->vdd_i2c, "vdd_i2c"); + + return 0; +} + +static int dw9718_pm_dev_wr(struct dw9718_info *info, int pwr) +{ + if (pwr < info->pwr_dev) + pwr = info->pwr_dev; + return dw9718_pm_wr(info, pwr); +} + +static void dw9718_pm_exit(struct dw9718_info *info) +{ + dw9718_pm_wr(info, NVC_PWR_OFF_FORCE); + dw9718_power_put(&info->power); +} + +static int dw9718_reset(struct dw9718_info *info, u32 level) +{ + int err = 0; + + if (level == NVC_RESET_SOFT) { + err = dw9718_i2c_wr8(info, DW9718_POWER_DN, 0x01); + usleep_range(200, 220); + err |= dw9718_i2c_wr8(info, DW9718_POWER_DN, 0x00); + usleep_range(100, 120); + } else + err = dw9718_pm_wr(info, NVC_PWR_OFF_FORCE); + + return err; +} + +static void dw9718_get_focuser_capabilities(struct dw9718_info *info) +{ + memset(&info->nv_config, 0, sizeof(info->nv_config)); + + dev_dbg(&info->i2c_client->dev, "%s\n", __func__); + info->nv_config.focal_length = info->nvc.focal_length; + info->nv_config.fnumber = info->nvc.fnumber; + info->nv_config.max_aperture = info->nvc.fnumber; + info->nv_config.range_ends_reversed = 0; + + info->nv_config.pos_working_low = info->cap.focus_infinity; + info->nv_config.pos_working_high = info->cap.focus_macro; + info->nv_config.pos_actual_low = dw9718_POS_LOW_DEFAULT; + info->nv_config.pos_actual_high = dw9718_POS_HIGH_DEFAULT; + + info->nv_config.slew_rate = info->cap.slew_rate; + info->nv_config.circle_of_confusion = -1; + info->nv_config.num_focuser_sets = 1; + info->nv_config.focuser_set[0].macro = info->cap.focus_macro; + info->nv_config.focuser_set[0].hyper = info->cap.focus_hyper; + info->nv_config.focuser_set[0].inf = info->cap.focus_infinity; + info->nv_config.focuser_set[0].settle_time = info->cap.settle_time; +} + +static int dw9718_set_focuser_capabilities(struct dw9718_info *info, + struct nvc_param *params) +{ + dev_dbg(&info->i2c_client->dev, "%s\n", __func__); + if (copy_from_user(&info->nv_config, + (const void __user *)params->p_value, + sizeof(struct nv_focuser_config))) { + dev_err(&info->i2c_client->dev, + "%s Error: copy_from_user bytes %d\n", + __func__, sizeof(struct nv_focuser_config)); + return -EFAULT; + } + + /* set pre-set value, as currently ODM sets incorrect value */ + info->cap.settle_time = dw9718_SETTLETIME; + + dev_dbg(&info->i2c_client->dev, + "%s: copy_from_user bytes %d info->cap.settle_time %d\n", + __func__, sizeof(struct nv_focuser_config), + info->cap.settle_time); + + return 0; +} + +static int dw9718_param_rd(struct dw9718_info *info, unsigned long arg) +{ + struct nvc_param params; + const void *data_ptr = NULL; + u32 data_size = 0; + + dev_dbg(&info->i2c_client->dev, "%s %lx\n", __func__, arg); + if (copy_from_user(¶ms, + (const void __user *)arg, + sizeof(struct nvc_param))) { + dev_err(&info->i2c_client->dev, "%s %d copy_from_user err\n", + __func__, __LINE__); + return -EFAULT; + } + if (info->s_mode == NVC_SYNC_SLAVE) + info = info->s_info; + switch (params.param) { + case NVC_PARAM_LOCUS: + data_ptr = &info->cur_pos; + data_size = sizeof(info->cur_pos); + dev_dbg(&info->i2c_client->dev, "%s LOCUS: %d\n", + __func__, info->cur_pos); + break; + case NVC_PARAM_FOCAL_LEN: + info->nvc.focal_length = dw9718_FOCAL_LENGTH; + data_ptr = &info->nvc.focal_length; + data_size = sizeof(info->nvc.focal_length); + break; + case NVC_PARAM_MAX_APERTURE: + data_ptr = &info->nvc.max_aperature; + data_size = sizeof(info->nvc.max_aperature); + dev_dbg(&info->i2c_client->dev, "%s MAX_APERTURE: %x\n", + __func__, info->nvc.max_aperature); + break; + case NVC_PARAM_FNUMBER: + data_ptr = &info->nvc.fnumber; + data_size = sizeof(info->nvc.fnumber); + dev_dbg(&info->i2c_client->dev, "%s FNUMBER: %u\n", + __func__, info->nvc.fnumber); + break; + case NVC_PARAM_CAPS: + /* send back just what's requested or our max size */ + dw9718_get_focuser_capabilities(info); + data_ptr = &info->nv_config; + data_size = sizeof(info->nv_config); + dev_err(&info->i2c_client->dev, "%s CAPS\n", __func__); + break; + case NVC_PARAM_STS: + data_ptr = &info->status; + data_size = sizeof(info->status); + dev_dbg(&info->i2c_client->dev, "%s\n", __func__); + break; + case NVC_PARAM_STEREO: + data_ptr = &info->s_mode; + data_size = sizeof(info->s_mode); + dev_err(&info->i2c_client->dev, "%s STEREO: %d\n", __func__, + info->s_mode); + break; + default: + dev_err(&info->i2c_client->dev, + "%s unsupported parameter: %d\n", + __func__, params.param); + return -EINVAL; + } + if (params.sizeofvalue < data_size) { + dev_err(&info->i2c_client->dev, + "%s data size mismatch %d != %d Param: %d\n", + __func__, params.sizeofvalue, data_size, params.param); + return -EINVAL; + } + if (copy_to_user((void __user *)params.p_value, data_ptr, data_size)) { + dev_err(&info->i2c_client->dev, "%s copy_to_user err line %d\n", + __func__, __LINE__); + return -EFAULT; + } + return 0; +} + +static int dw9718_param_wr_s(struct dw9718_info *info, + struct nvc_param *params, s32 s32val) +{ + int err = 0; + + switch (params->param) { + case NVC_PARAM_LOCUS: + dev_dbg(&info->i2c_client->dev, "%s LOCUS: %d\n", + __func__, s32val); + err = dw9718_position_wr(info, s32val); + break; + case NVC_PARAM_RESET: + err = dw9718_reset(info, s32val); + dev_dbg(&info->i2c_client->dev, "%s RESET\n", __func__); + break; + case NVC_PARAM_SELF_TEST: + dev_dbg(&info->i2c_client->dev, "%s SELF_TEST\n", __func__); + break; + default: + dev_dbg(&info->i2c_client->dev, + "%s unsupported parameter: %d\n", + __func__, params->param); + err = -EINVAL; + break; + } + + if (err) + dev_err(&info->i2c_client->dev, "ERROR! %d\n", err); + return err; +} + +static int dw9718_param_wr(struct dw9718_info *info, unsigned long arg) +{ + struct nvc_param params; + u8 u8val; + s32 s32val; + int err = 0; + if (copy_from_user(¶ms, (const void __user *)arg, + sizeof(struct nvc_param))) { + dev_err(&info->i2c_client->dev, + "%s copy_from_user err line %d\n", + __func__, __LINE__); + return -EFAULT; + } + if (copy_from_user(&s32val, + (const void __user *)params.p_value, sizeof(s32val))) { + dev_err(&info->i2c_client->dev, "%s %d copy_from_user err\n", + __func__, __LINE__); + return -EFAULT; + } + u8val = (u8)s32val; + /* parameters independent of sync mode */ + switch (params.param) { + case NVC_PARAM_STEREO: + dev_dbg(&info->i2c_client->dev, "%s STEREO: %d\n", + __func__, u8val); + if (u8val == info->s_mode) + return 0; + switch (u8val) { + case NVC_SYNC_OFF: + info->s_mode = u8val; + break; + case NVC_SYNC_MASTER: + info->s_mode = u8val; + break; + case NVC_SYNC_SLAVE: + if (info->s_info != NULL) { + /* default slave lens position */ + err = dw9718_position_wr(info->s_info, + info->s_info->cap.focus_infinity); + if (!err) { + info->s_mode = u8val; + info->s_info->s_mode = u8val; + } else { + if (info->s_mode != NVC_SYNC_STEREO) + dw9718_pm_wr(info->s_info, + NVC_PWR_OFF); + err = -EIO; + } + } else { + err = -EINVAL; + } + break; + case NVC_SYNC_STEREO: + if (info->s_info != NULL) { + /* sync power */ + info->s_info->pwr_dev = info->pwr_dev; + /* move slave lens to master position */ + err = dw9718_position_wr(info->s_info, + (s32)info->cur_pos); + if (!err) { + info->s_mode = u8val; + info->s_info->s_mode = u8val; + } else { + if (info->s_mode != NVC_SYNC_SLAVE) + dw9718_pm_wr(info->s_info, + NVC_PWR_OFF); + err = -EIO; + } + } else { + err = -EINVAL; + } + break; + default: + err = -EINVAL; + } + if (info->pdata->cfg & NVC_CFG_NOERR) + return 0; + return err; + + case NVC_PARAM_CAPS: + if (dw9718_set_focuser_capabilities(info, ¶ms)) { + dev_err(&info->i2c_client->dev, + "%s: Error: copy_from_user bytes %d\n", + __func__, params.sizeofvalue); + return -EFAULT; + } + return 0; + + default: + /* parameters dependent on sync mode */ + switch (info->s_mode) { + case NVC_SYNC_OFF: + case NVC_SYNC_MASTER: + return dw9718_param_wr_s(info, ¶ms, s32val); + case NVC_SYNC_SLAVE: + return dw9718_param_wr_s(info->s_info, ¶ms, s32val); + case NVC_SYNC_STEREO: + err = dw9718_param_wr_s(info, ¶ms, s32val); + if (!(info->pdata->cfg & NVC_CFG_SYNC_I2C_MUX)) + err |= dw9718_param_wr_s(info->s_info, + ¶ms, s32val); + return err; + default: + dev_err(&info->i2c_client->dev, "%s %d internal err\n", + __func__, __LINE__); + return -EINVAL; + } + } +} + +static long dw9718_ioctl(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + struct dw9718_info *info = file->private_data; + int pwr; + int err = 0; + switch (cmd) { + case NVC_IOCTL_PARAM_WR: + dw9718_pm_dev_wr(info, NVC_PWR_ON); + err = dw9718_param_wr(info, arg); + dw9718_pm_dev_wr(info, NVC_PWR_OFF); + return err; + case NVC_IOCTL_PARAM_RD: + dw9718_pm_dev_wr(info, NVC_PWR_ON); + err = dw9718_param_rd(info, arg); + dw9718_pm_dev_wr(info, NVC_PWR_OFF); + return err; + case NVC_IOCTL_PWR_WR: + /* This is a Guaranteed Level of Service (GLOS) call */ + pwr = (int)arg * 2; + dev_dbg(&info->i2c_client->dev, "%s PWR_WR: %d\n", + __func__, pwr); + err = dw9718_pm_dev_wr(info, pwr); + return err; + case NVC_IOCTL_PWR_RD: + if (info->s_mode == NVC_SYNC_SLAVE) + pwr = info->s_info->pwr_dev; + else + pwr = info->pwr_dev; + dev_dbg(&info->i2c_client->dev, "%s PWR_RD: %d\n", + __func__, pwr); + if (copy_to_user((void __user *)arg, + (const void *)&pwr, sizeof(pwr))) { + dev_err(&info->i2c_client->dev, + "%s copy_to_user err line %d\n", + __func__, __LINE__); + return -EFAULT; + } + return 0; + default: + dev_dbg(&info->i2c_client->dev, "%s unsupported ioctl: %x\n", + __func__, cmd); + } + return -EINVAL; +} + + +static void dw9718_sdata_init(struct dw9718_info *info) +{ + /* set defaults */ + memcpy(&info->nvc, &dw9718_default_nvc, sizeof(info->nvc)); + memcpy(&info->cap, &dw9718_default_cap, sizeof(info->cap)); + + /* set overrides if any */ + if (info->pdata->nvc) { + if (info->pdata->nvc->fnumber) + info->nvc.fnumber = info->pdata->nvc->fnumber; + if (info->pdata->nvc->focal_length) + info->nvc.focal_length = info->pdata->nvc->focal_length; + if (info->pdata->nvc->max_aperature) + info->nvc.max_aperature = + info->pdata->nvc->max_aperature; + } + + if (info->pdata->cap) { + if (info->pdata->cap->actuator_range) + info->cap.actuator_range = + info->pdata->cap->actuator_range; + if (info->pdata->cap->settle_time) + info->cap.settle_time = info->pdata->cap->settle_time; + if (info->pdata->cap->slew_rate) + info->cap.slew_rate = info->pdata->cap->slew_rate; + if (info->pdata->cap->focus_macro) + info->cap.focus_macro = info->pdata->cap->focus_macro; + if (info->pdata->cap->focus_hyper) + info->cap.focus_hyper = info->pdata->cap->focus_hyper; + if (info->pdata->cap->focus_infinity) + info->cap.focus_infinity = + info->pdata->cap->focus_infinity; + } +} + +static int dw9718_sync_en(unsigned num, unsigned sync) +{ + struct dw9718_info *master = NULL; + struct dw9718_info *slave = NULL; + struct dw9718_info *pos = NULL; + rcu_read_lock(); + list_for_each_entry_rcu(pos, &dw9718_info_list, list) { + if (pos->pdata->num == num) { + master = pos; + break; + } + } + pos = NULL; + list_for_each_entry_rcu(pos, &dw9718_info_list, list) { + if (pos->pdata->num == sync) { + slave = pos; + break; + } + } + rcu_read_unlock(); + if (master != NULL) + master->s_info = NULL; + if (slave != NULL) + slave->s_info = NULL; + if (!sync) + return 0; /* no err if sync disabled */ + if (num == sync) + return -EINVAL; /* err if sync instance is itself */ + if ((master != NULL) && (slave != NULL)) { + master->s_info = slave; + slave->s_info = master; + } + return 0; +} + +static int dw9718_sync_dis(struct dw9718_info *info) +{ + if (info->s_info != NULL) { + info->s_info->s_mode = 0; + info->s_info->s_info = NULL; + info->s_mode = 0; + info->s_info = NULL; + return 0; + } + return -EINVAL; +} + +static int dw9718_open(struct inode *inode, struct file *file) +{ + struct dw9718_info *info = NULL; + struct dw9718_info *pos = NULL; + int err; + rcu_read_lock(); + list_for_each_entry_rcu(pos, &dw9718_info_list, list) { + if (pos->miscdev.minor == iminor(inode)) { + info = pos; + break; + } + } + rcu_read_unlock(); + if (!info) + return -ENODEV; + err = dw9718_sync_en(info->pdata->num, info->pdata->sync); + if (err == -EINVAL) + dev_err(&info->i2c_client->dev, + "%s err: invalid num (%u) and sync (%u) instance\n", + __func__, info->pdata->num, info->pdata->sync); + if (atomic_xchg(&info->in_use, 1)) + return -EBUSY; + if (info->s_info != NULL) { + if (atomic_xchg(&info->s_info->in_use, 1)) + return -EBUSY; + } + file->private_data = info; + dw9718_pm_dev_wr(info, NVC_PWR_ON); + dw9718_position_wr(info, info->cap.focus_infinity); + dw9718_pm_dev_wr(info, NVC_PWR_OFF); + dev_dbg(&info->i2c_client->dev, "%s\n", __func__); + + return 0; +} + +static int dw9718_release(struct inode *inode, struct file *file) +{ + struct dw9718_info *info = file->private_data; + dev_dbg(&info->i2c_client->dev, "%s\n", __func__); + dw9718_pm_wr(info, NVC_PWR_OFF); + file->private_data = NULL; + WARN_ON(!atomic_xchg(&info->in_use, 0)); + if (info->s_info != NULL) + WARN_ON(!atomic_xchg(&info->s_info->in_use, 0)); + dw9718_sync_dis(info); + return 0; +} + +static const struct file_operations dw9718_fileops = { + .owner = THIS_MODULE, + .open = dw9718_open, + .unlocked_ioctl = dw9718_ioctl, + .release = dw9718_release, +}; + +static void dw9718_del(struct dw9718_info *info) +{ + dw9718_pm_exit(info); + if ((info->s_mode == NVC_SYNC_SLAVE) || + (info->s_mode == NVC_SYNC_STEREO)) + dw9718_pm_exit(info->s_info); + + dw9718_sync_dis(info); + spin_lock(&dw9718_spinlock); + list_del_rcu(&info->list); + spin_unlock(&dw9718_spinlock); + synchronize_rcu(); +} + +static int dw9718_remove(struct i2c_client *client) +{ + struct dw9718_info *info = i2c_get_clientdata(client); + dev_dbg(&info->i2c_client->dev, "%s\n", __func__); + misc_deregister(&info->miscdev); + dw9718_del(info); + return 0; +} + +static int nvc_debugfs_init(const char *dir_name, + struct dentry **d_entry, struct dentry **f_entry, void *info); + +static int dw9718_probe( + struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct dw9718_info *info; + char dname[16]; + + dev_dbg(&client->dev, "%s\n", __func__); + pr_info("dw9718: probing focuser.\n"); + + 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 = &dw9718_default_pdata; + dev_dbg(&client->dev, "%s No platform data. Using defaults.\n", + __func__); + } + + i2c_set_clientdata(client, info); + INIT_LIST_HEAD(&info->list); + spin_lock(&dw9718_spinlock); + list_add_rcu(&info->list, &dw9718_info_list); + spin_unlock(&dw9718_spinlock); + dw9718_power_get(info); + dw9718_sdata_init(info); + + if (info->pdata->dev_name != 0) + strcpy(dname, info->pdata->dev_name); + else + strcpy(dname, "dw9718"); + + if (info->pdata->num) + snprintf(dname, sizeof(dname), + "%s.%u", dname, info->pdata->num); + + info->miscdev.name = dname; + info->miscdev.fops = &dw9718_fileops; + info->miscdev.minor = MISC_DYNAMIC_MINOR; + if (misc_register(&info->miscdev)) { + dev_err(&client->dev, "%s unable to register misc device %s\n", + __func__, dname); + dw9718_del(info); + return -ENODEV; + } + + nvc_debugfs_init( + info->miscdev.this_device->kobj.name, NULL, NULL, info); + + return 0; +} + +#ifdef ENABLE_DEBUGFS_INTERFACE +static int nvc_status_show(struct seq_file *s, void *data) +{ + struct dw9718_info *k_info = s->private; + struct nv_focuser_config *pcfg = &k_info->nv_config; + struct nvc_focus_cap *pcap = &k_info->cap; + + pr_info("%s\n", __func__); + + seq_printf(s, "focuser status:\n" + " Limit = (%04d - %04d)\n" + " Range = (%04d - %04d)\n" + " Current Pos = %04d\n" + " Settle time = %04d\n" + " Macro = %04d\n" + " Infinity = %04d\n" + " Hyper = %04d\n" + " SlewRate = 0x%06x\n" + , + pcfg->pos_actual_low, pcfg->pos_actual_high, + pcfg->pos_working_low, pcfg->pos_working_high, + k_info->cur_pos, + pcap->settle_time, + pcap->focus_macro, + pcap->focus_infinity, + pcap->focus_hyper, + pcfg->slew_rate + ); + + return 0; +} + +static ssize_t nvc_attr_set(struct file *s, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct dw9718_info *k_info = + ((struct seq_file *)s->private_data)->private; + struct nv_focuser_config *pcfg = &k_info->nv_config; + char buf[24]; + int buf_size; + int err; + u32 val = 0; + + pr_info("%s (%d)\n", __func__, count); + + if (!user_buf || count <= 1) + return -EFAULT; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + if (sscanf(buf + 1, "0x%x", &val) == 1) + goto set_attr; + if (sscanf(buf + 1, "0X%x", &val) == 1) + goto set_attr; + if (sscanf(buf + 1, "%d", &val) == 1) + goto set_attr; + + pr_err("SYNTAX ERROR: %s\n", buf); + return -EFAULT; + +set_attr: + pr_info("new data = %x\n", val); + switch (buf[0]) { + case 'p': + pr_info("new pos = %d\n", val); + err = dw9718_position_wr(k_info, val); + if (err) + pr_err("ERROR set position %x\n", val); + break; + case 'h': + if (val <= pcfg->pos_working_low || val >= 1024) { + pr_info("new pos_high(%d) out of range\n", + val); + break; + } + pr_info("new pos_high = %d\n", val); + pcfg->pos_working_high = val; + break; + case 'l': + if (val >= pcfg->pos_working_high) { + pr_info("new pos_low(%d) out of range\n", + val); + break; + } + pr_info("new pos_low = %d\n", val); + pcfg->pos_working_low = val; + break; + case 'm': + pr_info("new vcm mode = %x\n", val); + pcfg->slew_rate = val; + dw9718_set_arc_mode(k_info); + break; + } + + return count; +} + +static int nvc_debugfs_open(struct inode *inode, struct file *file) +{ + return single_open(file, nvc_status_show, inode->i_private); +} + +static const struct file_operations nvc_debugfs_fops = { + .open = nvc_debugfs_open, + .read = seq_read, + .write = nvc_attr_set, + .llseek = seq_lseek, + .release = single_release, +}; + +static int nvc_debugfs_init(const char *dir_name, + struct dentry **d_entry, struct dentry **f_entry, void *info) +{ + struct dentry *dp, *fp; + + dp = debugfs_create_dir(dir_name, NULL); + if (dp == NULL) { + pr_info("%s: debugfs create dir failed\n", __func__); + return -ENOMEM; + } + + fp = debugfs_create_file("d", S_IRUGO|S_IWUSR, + dp, info, &nvc_debugfs_fops); + if (!fp) { + pr_info("%s: debugfs create file failed\n", __func__); + debugfs_remove_recursive(dp); + return -ENOMEM; + } + + if (d_entry) + *d_entry = dp; + if (f_entry) + *f_entry = fp; + return 0; +} +#else +static int nvc_debugfs_init(const char *dir_name, + struct dentry **d_entry, struct dentry **f_entry, void *info) +{ + return 0; +} +#endif + + +static const struct i2c_device_id dw9718_id[] = { + { "dw9718", 0 }, + { }, +}; + + +MODULE_DEVICE_TABLE(i2c, dw9718_id); + +static struct i2c_driver dw9718_i2c_driver = { + .driver = { + .name = "dw9718", + .owner = THIS_MODULE, + }, + .id_table = dw9718_id, + .probe = dw9718_probe, + .remove = dw9718_remove, +}; + +static int __init dw9718_init(void) +{ + return i2c_add_driver(&dw9718_i2c_driver); +} + +static void __exit dw9718_exit(void) +{ + i2c_del_driver(&dw9718_i2c_driver); +} + +module_init(dw9718_init); +module_exit(dw9718_exit); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/tegra/imx132.c b/drivers/media/video/tegra/imx132.c index 22a1ab7023b4..6e89577047c9 100644 --- a/drivers/media/video/tegra/imx132.c +++ b/drivers/media/video/tegra/imx132.c @@ -1,15 +1,19 @@ /* * imx132.c - imx132 sensor driver * - * Copyright (c) 2012-2013, NVIDIA Corporation. All Rights Reserved. - * - * Contributors: - * Krupal Divvela <kdivvela@nvidia.com> - * - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. + * Copyright (c) 2012 - 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> @@ -756,6 +760,7 @@ imx132_remove(struct i2c_client *client) static const struct i2c_device_id imx132_id[] = { { "imx132", 0 }, + { } }; MODULE_DEVICE_TABLE(i2c, imx132_id); diff --git a/drivers/media/video/tegra/imx135.c b/drivers/media/video/tegra/imx135.c new file mode 100644 index 000000000000..ca4d89417567 --- /dev/null +++ b/drivers/media/video/tegra/imx135.c @@ -0,0 +1,2417 @@ +/* + * imx135.c - imx135 sensor driver + * + * 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 <media/imx135.h> +#include <linux/gpio.h> +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> + +struct imx135_reg { + u16 addr; + u8 val; +}; + +struct imx135_info { + struct miscdevice miscdev_info; + int mode; + struct imx135_power_rail power; + struct imx135_sensordata sensor_data; + struct i2c_client *i2c_client; + struct imx135_platform_data *pdata; + struct mutex imx135_camera_lock; + struct dentry *debugdir; + atomic_t in_use; +}; + +#define IMX135_TABLE_WAIT_MS 0 +#define IMX135_TABLE_END 1 +#define IMX135_MAX_RETRIES 3 +#define IMX135_WAIT_MS 3 + +#define IMX135_4208x3120_HDR + +#ifdef IMX135_4208x3120_HDR +/* HDR */ +static struct imx135_reg mode_4208x3120[] = { + /* software reset */ + {0x0103, 0x01}, + /* global settings */ + {0x0101, 0x00}, + {0x0105, 0x01}, + {0x0110, 0x00}, + {0x0220, 0x01}, + {0x3302, 0x11}, + {0x3833, 0x20}, + {0x3873, 0x03}, + {0x3893, 0x00}, + {0x3906, 0x08}, + {0x3907, 0x01}, + {0x391B, 0x00}, + {0x3C09, 0x01}, + {0x600A, 0x00}, + {0x3008, 0xB0}, + {0x320A, 0x01}, + {0x320D, 0x10}, + {0x3216, 0x2E}, + {0x322C, 0x02}, + {0x3409, 0x0C}, + {0x340C, 0x2D}, + {0x3411, 0x39}, + {0x3414, 0x1E}, + {0x3427, 0x04}, + {0x3480, 0x1E}, + {0x3484, 0x1E}, + {0x3488, 0x1E}, + {0x348C, 0x1E}, + {0x3490, 0x1E}, + {0x3494, 0x1E}, + {0x3511, 0x8F}, + {0x364F, 0x2D}, + + /* Clock Setting */ + {0x011E, 0x18}, + {0x011F, 0x00}, + {0x0301, 0x05}, + {0x0303, 0x01}, + {0x0305, 0x0C}, + {0x0309, 0x05}, + {0x030B, 0x01}, + {0x030C, 0x01}, + {0x030D, 0xC2}, + {0x030E, 0x01}, + {0x3A06, 0x11}, + + /* Mode Settings */ + {0x0108, 0x03}, + {0x0112, 0x0E}, + {0x0113, 0x0A}, + {0x0381, 0x01}, + {0x0383, 0x01}, + {0x0385, 0x01}, + {0x0387, 0x01}, + {0x0390, 0x00}, + {0x0391, 0x11}, + {0x0392, 0x00}, + {0x0401, 0x00}, + {0x0404, 0x00}, + {0x0405, 0x10}, + {0x4082, 0x01}, + {0x4083, 0x01}, + {0x7006, 0x04}, + + /* Optinal/Function settings */ + {0x0700, 0x00}, + {0x3A63, 0x00}, + {0x4100, 0xF8}, + {0x4203, 0xFF}, + {0x4344, 0x00}, + {0x441C, 0x01}, + + /* Size Setting */ + {0x0340, 0x0C}, + {0x0341, 0xD0}, + {0x0342, 0x11}, + {0x0343, 0xDC}, + {0x0344, 0x00}, + {0x0345, 0x00}, + {0x0346, 0x00}, + {0x0347, 0x00}, + {0x0348, 0x10}, + {0x0349, 0x6F}, + {0x034A, 0x0C}, + {0x034B, 0x2F}, + {0x034C, 0x10}, + {0x034D, 0x70}, + {0x034E, 0x0C}, + {0x034F, 0x30}, + {0x0350, 0x00}, + {0x0351, 0x00}, + {0x0352, 0x00}, + {0x0353, 0x00}, + {0x0354, 0x10}, + {0x0355, 0x70}, + {0x0356, 0x0C}, + {0x0357, 0x30}, + {0x301D, 0x30}, + {0x3310, 0x10}, + {0x3311, 0x70}, + {0x3312, 0x0C}, + {0x3313, 0x30}, + {0x331C, 0x01}, + {0x331D, 0x68}, + {0x4084, 0x00}, + {0x4085, 0x00}, + {0x4086, 0x00}, + {0x4087, 0x00}, + {0x4400, 0x00}, + + /* Global Timing Setting */ + {0x0830, 0x87}, + {0x0831, 0x3F}, + {0x0832, 0x67}, + {0x0833, 0x3F}, + {0x0834, 0x3F}, + {0x0835, 0x4F}, + {0x0836, 0xDF}, + {0x0837, 0x47}, + {0x0839, 0x1F}, + {0x083A, 0x17}, + {0x083B, 0x02}, + + /* Integration Time Setting */ + {0x0202, 0x0C}, + {0x0203, 0xCC}, + + /* Gain Setting */ + {0x0205, 0x00}, + {0x020E, 0x01}, + {0x020F, 0x00}, + {0x0210, 0x01}, + {0x0211, 0x00}, + {0x0212, 0x01}, + {0x0213, 0x00}, + {0x0214, 0x01}, + {0x0215, 0x00}, + + /* HDR Setting */ + {0x0230, 0x00}, + {0x0231, 0x00}, + {0x0233, 0x00}, + {0x0234, 0x00}, + {0x0235, 0x40}, + {0x0238, 0x01}, + {0x0239, 0x04}, + {0x023B, 0x00}, + {0x023C, 0x01}, + {0x33B0, 0x10}, + {0x33B1, 0x70}, + {0x33B3, 0x01}, + {0x33B4, 0x01}, + {0x3800, 0x00}, + + {0x3A43, 0x01}, + /* stream on */ + {0x0100, 0x01}, + + {IMX135_TABLE_WAIT_MS, IMX135_WAIT_MS}, + {IMX135_TABLE_END, 0x00} +}; +#else +/* standard */ +static struct imx135_reg mode_4208x3120[] = { + /* software reset */ + {0x0103, 0x01}, + /* global settings */ + {0x0101, 0x00}, + {0x0105, 0x01}, + {0x0110, 0x00}, + {0x0220, 0x01}, + {0x3302, 0x11}, + {0x3833, 0x20}, + {0x3893, 0x00}, + {0x3906, 0x08}, + {0x3907, 0x01}, + {0x391B, 0x01}, + {0x3C09, 0x01}, + {0x600A, 0x00}, + {0x3008, 0xB0}, + {0x320A, 0x01}, + {0x320D, 0x10}, + {0x3216, 0x2E}, + {0x322C, 0x02}, + {0x3409, 0x0C}, + {0x340C, 0x2D}, + {0x3411, 0x39}, + {0x3414, 0x1E}, + {0x3427, 0x04}, + {0x3480, 0x1E}, + {0x3484, 0x1E}, + {0x3488, 0x1E}, + {0x348C, 0x1E}, + {0x3490, 0x1E}, + {0x3494, 0x1E}, + {0x3511, 0x8F}, + {0x364F, 0x2D}, + + /* Clock Setting */ + {0x011E, 0x18}, + {0x011F, 0x00}, + {0x0301, 0x05}, + {0x0303, 0x01}, + {0x0305, 0x0C}, + {0x0309, 0x05}, + {0x030B, 0x01}, + {0x030C, 0x01}, + {0x030D, 0xC2}, + {0x030E, 0x01}, + {0x3A06, 0x11}, + + /* Mode Settings */ + {0x0108, 0x03}, + {0x0112, 0x0A}, + {0x0113, 0x0A}, + {0x0381, 0x01}, + {0x0383, 0x01}, + {0x0385, 0x01}, + {0x0387, 0x01}, + {0x0390, 0x00}, + {0x0391, 0x11}, + {0x0392, 0x00}, + {0x0401, 0x00}, + {0x0404, 0x00}, + {0x0405, 0x10}, + {0x4082, 0x01}, + {0x4083, 0x01}, + {0x7006, 0x04}, + + /* Optinal/Function settings */ + {0x0700, 0x00}, + {0x3A63, 0x00}, + {0x4100, 0xF8}, + {0x4203, 0xFF}, + {0x4344, 0x00}, + {0x441C, 0x01}, + + /* Size Setting */ + {0x0340, 0x0C}, + {0x0341, 0xD0}, + {0x0342, 0x11}, + {0x0343, 0xDC}, + {0x0344, 0x00}, + {0x0345, 0x00}, + {0x0346, 0x00}, + {0x0347, 0x00}, + {0x0348, 0x10}, + {0x0349, 0x6F}, + {0x034A, 0x0C}, + {0x034B, 0x2F}, + {0x034C, 0x10}, + {0x034D, 0x70}, + {0x034E, 0x0C}, + {0x034F, 0x30}, + {0x0350, 0x00}, + {0x0351, 0x00}, + {0x0352, 0x00}, + {0x0353, 0x00}, + {0x0354, 0x10}, + {0x0355, 0x70}, + {0x0356, 0x0C}, + {0x0357, 0x30}, + {0x301D, 0x30}, + {0x3310, 0x10}, + {0x3311, 0x70}, + {0x3312, 0x0C}, + {0x3313, 0x30}, + {0x331C, 0x01}, + {0x331D, 0x68}, + {0x4084, 0x00}, + {0x4085, 0x00}, + {0x4086, 0x00}, + {0x4087, 0x00}, + {0x4400, 0x00}, + + /* Global Timing Setting */ + {0x0830, 0x87}, + {0x0831, 0x3F}, + {0x0832, 0x67}, + {0x0833, 0x3F}, + {0x0834, 0x3F}, + {0x0835, 0x4F}, + {0x0836, 0xDF}, + {0x0837, 0x47}, + {0x0839, 0x1F}, + {0x083A, 0x17}, + {0x083B, 0x02}, + + /* Integration Time Setting */ + {0x0202, 0x0C}, + {0x0203, 0xCC}, + + /* Gain Setting */ + {0x0205, 0x04}, + {0x020E, 0x01}, + {0x020F, 0x00}, + {0x0210, 0x01}, + {0x0211, 0x00}, + {0x0212, 0x01}, + {0x0213, 0x00}, + {0x0214, 0x01}, + {0x0215, 0x00}, + + /* HDR Setting */ + {0x0230, 0x00}, + {0x0231, 0x00}, + {0x0233, 0x00}, + {0x0234, 0x00}, + {0x0235, 0x40}, + {0x0238, 0x01}, + {0x0239, 0x04}, + {0x023B, 0x00}, + {0x023C, 0x01}, + {0x33B0, 0x04}, + {0x33B1, 0x00}, + {0x33B3, 0x00}, + {0x33B4, 0x01}, + {0x3800, 0x00}, + + {0x3A43, 0x01}, + /* stream on */ + {0x0100, 0x01}, + + {IMX135_TABLE_WAIT_MS, IMX135_WAIT_MS}, + {IMX135_TABLE_END, 0x00} +}; +#endif + +static struct imx135_reg mode_1920x1080[] = { + /* software reset */ + {0x0103, 0x01}, + /* global settings */ + {0x0101, 0x00}, + {0x0105, 0x01}, + {0x0110, 0x00}, + {0x0220, 0x01}, + {0x3302, 0x11}, + {0x3833, 0x20}, + {0x3893, 0x00}, + {0x3906, 0x08}, + {0x3907, 0x01}, + {0x391B, 0x01}, + {0x3C09, 0x01}, + {0x600A, 0x00}, + {0x3008, 0xB0}, + {0x320A, 0x01}, + {0x320D, 0x10}, + {0x3216, 0x2E}, + {0x322C, 0x02}, + {0x3409, 0x0C}, + {0x340C, 0x2D}, + {0x3411, 0x39}, + {0x3414, 0x1E}, + {0x3427, 0x04}, + {0x3480, 0x1E}, + {0x3484, 0x1E}, + {0x3488, 0x1E}, + {0x348C, 0x1E}, + {0x3490, 0x1E}, + {0x3494, 0x1E}, + {0x3511, 0x8F}, + {0x364F, 0x2D}, + + /* Clock Setting */ + {0x011E, 0x18}, + {0x011F, 0x00}, + {0x0301, 0x05}, + {0x0303, 0x01}, + {0x0305, 0x0C}, + {0x0309, 0x05}, + {0x030B, 0x02}, + {0x030C, 0x01}, + {0x030D, 0xC2}, + {0x030E, 0x01}, + {0x3A06, 0x12}, + + /* Mode Settings */ + {0x0108, 0x03}, + {0x0112, 0x0A}, + {0x0113, 0x0A}, + {0x0381, 0x01}, + {0x0383, 0x01}, + {0x0385, 0x01}, + {0x0387, 0x01}, + {0x0390, 0x01}, + {0x0391, 0x22}, + {0x0392, 0x00}, + {0x0401, 0x02}, + {0x0404, 0x00}, + {0x0405, 0x11}, + {0x4082, 0x00}, + {0x4083, 0x00}, + {0x7006, 0x04}, + + /* Optinal/Function settings */ + {0x0700, 0x00}, + {0x3A63, 0x00}, + {0x4100, 0xF8}, + {0x4203, 0xFF}, + {0x4344, 0x00}, + {0x441C, 0x01}, + + /* Size Setting */ + {0x0340, 0x0A}, + {0x0341, 0x40}, + {0x0342, 0x11}, + {0x0343, 0xDC}, + {0x0344, 0x00}, + {0x0345, 0x40}, + {0x0346, 0x01}, + {0x0347, 0x9C}, + {0x0348, 0x10}, + {0x0349, 0x2F}, + {0x034A, 0x0A}, + {0x034B, 0x93}, + {0x034C, 0x07}, + {0x034D, 0x80}, + {0x034E, 0x04}, + {0x034F, 0x38}, + {0x0350, 0x00}, + {0x0351, 0x00}, + {0x0352, 0x00}, + {0x0353, 0x00}, + {0x0354, 0x07}, + {0x0355, 0xF8}, + {0x0356, 0x04}, + {0x0357, 0x7C}, + {0x301D, 0x30}, + {0x3310, 0x07}, + {0x3311, 0x80}, + {0x3312, 0x04}, + {0x3313, 0x38}, + {0x331C, 0x00}, + {0x331D, 0xD2}, + {0x4084, 0x07}, + {0x4085, 0x80}, + {0x4086, 0x04}, + {0x4087, 0x38}, + {0x4400, 0x00}, + + /* Global Timing Setting */ + {0x0830, 0x67}, + {0x0831, 0x27}, + {0x0832, 0x47}, + {0x0833, 0x27}, + {0x0834, 0x27}, + {0x0835, 0x1F}, + {0x0836, 0x87}, + {0x0837, 0x2F}, + {0x0839, 0x1F}, + {0x083A, 0x17}, + {0x083B, 0x02}, + + /* Integration Time Setting */ + {0x0202, 0x0A}, + {0x0203, 0x3C}, + + /* Gain Setting */ + {0x0205, 0x00}, + {0x020E, 0x01}, + {0x020F, 0x00}, + {0x0210, 0x01}, + {0x0211, 0x00}, + {0x0212, 0x01}, + {0x0213, 0x00}, + {0x0214, 0x01}, + {0x0215, 0x00}, + + /* HDR Setting */ + {0x0230, 0x00}, + {0x0231, 0x00}, + {0x0233, 0x00}, + {0x0234, 0x00}, + {0x0235, 0x40}, + {0x0238, 0x01}, + {0x0239, 0x04}, + {0x023B, 0x00}, + {0x023C, 0x01}, + {0x33B0, 0x04}, + {0x33B1, 0x00}, + {0x33B3, 0x00}, + {0x33B4, 0x01}, + {0x3800, 0x00}, + + {0x3A43, 0x01}, + /* stream on */ + {0x0100, 0x01}, + + {IMX135_TABLE_WAIT_MS, IMX135_WAIT_MS}, + {IMX135_TABLE_END, 0x00} +}; + +#ifdef IMX135_1280x720_90_FPS +/* 720p 90fps */ +static struct imx135_reg mode_1280x720[] = { + /* software reset */ + {0x0103, 0x01}, + /* global settings */ + {0x0101, 0x00}, + {0x0105, 0x01}, + {0x0110, 0x00}, + {0x0220, 0x01}, + {0x3302, 0x11}, + {0x3833, 0x20}, + {0x3893, 0x00}, + {0x3906, 0x08}, + {0x3907, 0x01}, + {0x391B, 0x01}, + {0x3C09, 0x01}, + {0x600A, 0x00}, + {0x3008, 0xB0}, + {0x320A, 0x01}, + {0x320D, 0x10}, + {0x3216, 0x2E}, + {0x322C, 0x02}, + {0x3409, 0x0C}, + {0x340C, 0x2D}, + {0x3411, 0x39}, + {0x3414, 0x1E}, + {0x3427, 0x04}, + {0x3480, 0x1E}, + {0x3484, 0x1E}, + {0x3488, 0x1E}, + {0x348C, 0x1E}, + {0x3490, 0x1E}, + {0x3494, 0x1E}, + {0x3511, 0x8F}, + {0x364F, 0x2D}, + + /* Clock Setting */ + {0x011E, 0x18}, + {0x011F, 0x00}, + {0x0301, 0x05}, + {0x0303, 0x01}, + {0x0305, 0x0C}, + {0x0309, 0x05}, + {0x030B, 0x02}, + {0x030C, 0x01}, + {0x030D, 0xC2}, + {0x030E, 0x01}, + {0x3A06, 0x12}, + + /* Mode Settings */ + {0x0108, 0x03}, + {0x0112, 0x0A}, + {0x0113, 0x0A}, + {0x0381, 0x01}, + {0x0383, 0x01}, + {0x0385, 0x01}, + {0x0387, 0x01}, + {0x0390, 0x01}, + {0x0391, 0x22}, + {0x0392, 0x00}, + {0x0401, 0x00}, + {0x0404, 0x00}, + {0x0405, 0x10}, + {0x4082, 0x01}, + {0x4083, 0x01}, + {0x7006, 0x04}, + + /* Optinal/Function settings */ + {0x0700, 0x00}, + {0x3A63, 0x00}, + {0x4100, 0xF8}, + {0x4203, 0xFF}, + {0x4344, 0x00}, + {0x441C, 0x01}, + + /* Size Setting */ + {0x0340, 0x03}, + {0x0341, 0x6A}, + {0x0342, 0x11}, + {0x0343, 0xDC}, + {0x0344, 0x03}, + {0x0345, 0x38}, + {0x0346, 0x03}, + {0x0347, 0x48}, + {0x0348, 0x0D}, + {0x0349, 0x37}, + {0x034A, 0x08}, + {0x034B, 0xE7}, + {0x034C, 0x05}, + {0x034D, 0x00}, + {0x034E, 0x02}, + {0x034F, 0xD0}, + {0x0350, 0x00}, + {0x0351, 0x00}, + {0x0352, 0x00}, + {0x0353, 0x00}, + {0x0354, 0x05}, + {0x0355, 0x00}, + {0x0356, 0x02}, + {0x0357, 0xD0}, + {0x301D, 0x30}, + {0x3310, 0x05}, + {0x3311, 0x00}, + {0x3312, 0x02}, + {0x3313, 0xD0}, + {0x331C, 0x00}, + {0x331D, 0x10}, + {0x4084, 0x00}, + {0x4085, 0x00}, + {0x4086, 0x00}, + {0x4087, 0x00}, + {0x4400, 0x00}, + + /* Global Timing Setting */ + {0x0830, 0x67}, + {0x0831, 0x27}, + {0x0832, 0x47}, + {0x0833, 0x27}, + {0x0834, 0x27}, + {0x0835, 0x1F}, + {0x0836, 0x87}, + {0x0837, 0x2F}, + {0x0839, 0x1F}, + {0x083A, 0x17}, + {0x083B, 0x02}, + + /* Integration Time Setting */ + {0x0202, 0x03}, + {0x0203, 0x66}, + + /* Gain Setting */ + {0x0205, 0x00}, + {0x020E, 0x01}, + {0x020F, 0x00}, + {0x0210, 0x01}, + {0x0211, 0x00}, + {0x0212, 0x01}, + {0x0213, 0x00}, + {0x0214, 0x01}, + {0x0215, 0x00}, + + /* HDR Setting */ + {0x0230, 0x00}, + {0x0231, 0x00}, + {0x0233, 0x00}, + {0x0234, 0x00}, + {0x0235, 0x40}, + {0x0238, 0x01}, + {0x0239, 0x04}, + {0x023B, 0x00}, + {0x023C, 0x01}, + {0x33B0, 0x04}, + {0x33B1, 0x00}, + {0x33B3, 0x00}, + {0x33B4, 0x01}, + {0x3800, 0x00}, + + {0x3A43, 0x01}, + /* stream on */ + {0x0100, 0x01}, + + {IMX135_TABLE_WAIT_MS, IMX135_WAIT_MS}, + {IMX135_TABLE_END, 0x00} +}; +#else +/* 720p 30fps */ +static struct imx135_reg mode_1280x720[] = { + /* software reset */ + {0x0103, 0x01}, + /* global settings */ + {0x0101, 0x00}, + {0x0105, 0x01}, + {0x0110, 0x00}, + {0x0220, 0x01}, + {0x3302, 0x11}, + {0x3833, 0x20}, + {0x3893, 0x00}, + {0x3906, 0x08}, + {0x3907, 0x01}, + {0x391B, 0x01}, + {0x3C09, 0x01}, + {0x600A, 0x00}, + {0x3008, 0xB0}, + {0x320A, 0x01}, + {0x320D, 0x10}, + {0x3216, 0x2E}, + {0x322C, 0x02}, + {0x3409, 0x0C}, + {0x340C, 0x2D}, + {0x3411, 0x39}, + {0x3414, 0x1E}, + {0x3427, 0x04}, + {0x3480, 0x1E}, + {0x3484, 0x1E}, + {0x3488, 0x1E}, + {0x348C, 0x1E}, + {0x3490, 0x1E}, + {0x3494, 0x1E}, + {0x3511, 0x8F}, + {0x364F, 0x2D}, + + /* Clock Setting */ + {0x011E, 0x18}, + {0x011F, 0x00}, + {0x0301, 0x05}, + {0x0303, 0x01}, + {0x0305, 0x0C}, + {0x0309, 0x05}, + {0x030B, 0x02}, + {0x030C, 0x01}, + {0x030D, 0xC2}, + {0x030E, 0x01}, + {0x3A06, 0x12}, + + /* Mode Settings */ + {0x0108, 0x03}, + {0x0112, 0x0A}, + {0x0113, 0x0A}, + {0x0381, 0x01}, + {0x0383, 0x01}, + {0x0385, 0x01}, + {0x0387, 0x01}, + {0x0390, 0x01}, + {0x0391, 0x22}, + {0x0392, 0x00}, + {0x0401, 0x02}, + {0x0404, 0x00}, + {0x0405, 0x1A}, + {0x4082, 0x00}, + {0x4083, 0x00}, + {0x7006, 0x04}, + + /* Optinal/Function settings */ + {0x0700, 0x00}, + {0x3A63, 0x00}, + {0x4100, 0xF8}, + {0x4203, 0xFF}, + {0x4344, 0x00}, + {0x441C, 0x01}, + + /* Size Setting */ + {0x0340, 0x0A}, + {0x0341, 0x40}, + {0x0342, 0x11}, + {0x0343, 0xDC}, + {0x0344, 0x00}, + {0x0345, 0x18}, + {0x0346, 0x01}, + {0x0347, 0x88}, + {0x0348, 0x10}, + {0x0349, 0x57}, + {0x034A, 0x0A}, + {0x034B, 0xAB}, + {0x034C, 0x05}, + {0x034D, 0x00}, + {0x034E, 0x02}, + {0x034F, 0xD0}, + {0x0350, 0x00}, + {0x0351, 0x00}, + {0x0352, 0x00}, + {0x0353, 0x00}, + {0x0354, 0x08}, + {0x0355, 0x20}, + {0x0356, 0x04}, + {0x0357, 0x92}, + {0x301D, 0x30}, + {0x3310, 0x05}, + {0x3311, 0x00}, + {0x3312, 0x02}, + {0x3313, 0xD0}, + {0x331C, 0x02}, + {0x331D, 0x18}, + {0x4084, 0x05}, + {0x4085, 0x00}, + {0x4086, 0x02}, + {0x4087, 0xD0}, + {0x4400, 0x00}, + + /* Global Timing Setting */ + {0x0830, 0x67}, + {0x0831, 0x27}, + {0x0832, 0x47}, + {0x0833, 0x27}, + {0x0834, 0x27}, + {0x0835, 0x1F}, + {0x0836, 0x87}, + {0x0837, 0x2F}, + {0x0839, 0x1F}, + {0x083A, 0x17}, + {0x083B, 0x02}, + + /* Integration Time Setting */ + {0x0202, 0x0A}, + {0x0203, 0x3C}, + + /* Gain Setting */ + {0x0205, 0x00}, + {0x020E, 0x01}, + {0x020F, 0x00}, + {0x0210, 0x01}, + {0x0211, 0x00}, + {0x0212, 0x01}, + {0x0213, 0x00}, + {0x0214, 0x01}, + {0x0215, 0x00}, + + /* HDR Setting */ + {0x0230, 0x00}, + {0x0231, 0x00}, + {0x0233, 0x00}, + {0x0234, 0x00}, + {0x0235, 0x40}, + {0x0238, 0x01}, + {0x0239, 0x04}, + {0x023B, 0x00}, + {0x023C, 0x01}, + {0x33B0, 0x04}, + {0x33B1, 0x00}, + {0x33B3, 0x00}, + {0x33B4, 0x01}, + {0x3800, 0x00}, + + {0x3A43, 0x01}, + /* stream on */ + {0x0100, 0x01}, + + {IMX135_TABLE_WAIT_MS, IMX135_WAIT_MS}, + {IMX135_TABLE_END, 0x00} +}; +#endif + +static struct imx135_reg mode_2616x1472[] = { + /* software reset */ + {0x0103, 0x01}, + /* global settings */ + {0x0101, 0x00}, + {0x0105, 0x01}, + {0x0110, 0x00}, + {0x0220, 0x01}, + {0x3302, 0x11}, + {0x3833, 0x20}, + {0x3873, 0x03}, + {0x3893, 0x00}, + {0x3906, 0x08}, + {0x3907, 0x01}, + {0x391B, 0x00}, + {0x3C09, 0x01}, + {0x600A, 0x00}, + {0x3008, 0xB0}, + {0x320A, 0x01}, + {0x320D, 0x10}, + {0x3216, 0x2E}, + {0x322C, 0x02}, + {0x3409, 0x0C}, + {0x340C, 0x2D}, + {0x3411, 0x39}, + {0x3414, 0x1E}, + {0x3427, 0x04}, + {0x3480, 0x1E}, + {0x3484, 0x1E}, + {0x3488, 0x1E}, + {0x348C, 0x1E}, + {0x3490, 0x1E}, + {0x3494, 0x1E}, + {0x3511, 0x8F}, + {0x364F, 0x2D}, + + /* Clock Setting */ + {0x011E, 0x18}, + {0x011F, 0x00}, + {0x0301, 0x05}, + {0x0303, 0x01}, + {0x0305, 0x0C}, + {0x0309, 0x05}, + {0x030B, 0x01}, + {0x030C, 0x01}, + {0x030D, 0xC2}, + {0x030E, 0x01}, + {0x3A06, 0x11}, + + /* Mode Settings */ + {0x0108, 0x03}, + {0x0112, 0x0E}, + {0x0113, 0x0A}, + {0x0381, 0x01}, + {0x0383, 0x01}, + {0x0385, 0x01}, + {0x0387, 0x01}, + {0x0390, 0x00}, + {0x0391, 0x11}, + {0x0392, 0x00}, + {0x0401, 0x00}, + {0x0404, 0x00}, + {0x0405, 0x10}, + {0x4082, 0x01}, + {0x4083, 0x01}, + {0x7006, 0x04}, + + /* Optinal/Function settings */ + {0x0700, 0x00}, + {0x3A63, 0x00}, + {0x4100, 0xF8}, + {0x4203, 0xFF}, + {0x4344, 0x00}, + {0x441C, 0x01}, + + /* Size Setting */ + {0x0340, 0x0A}, + {0x0341, 0x40}, + {0x0342, 0x11}, + {0x0343, 0xDC}, + {0x0344, 0x03}, + {0x0345, 0x1C}, + {0x0346, 0x03}, + {0x0347, 0x38}, + {0x0348, 0x0D}, + {0x0349, 0x53}, + {0x034A, 0x08}, + {0x034B, 0xF7}, + {0x034C, 0x0A}, + {0x034D, 0x38}, + {0x034E, 0x05}, + {0x034F, 0xC0}, + {0x0350, 0x00}, + {0x0351, 0x00}, + {0x0352, 0x00}, + {0x0353, 0x00}, + {0x0354, 0x0A}, + {0x0355, 0x38}, + {0x0356, 0x05}, + {0x0357, 0xC0}, + {0x301D, 0x30}, + {0x3310, 0x0A}, + {0x3311, 0x38}, + {0x3312, 0x05}, + {0x3313, 0xC0}, + {0x331C, 0x08}, + {0x331D, 0xD4}, + {0x4084, 0x00}, + {0x4085, 0x00}, + {0x4086, 0x00}, + {0x4087, 0x00}, + {0x4400, 0x00}, + + /* Global Timing Setting */ + {0x0830, 0x87}, + {0x0831, 0x3F}, + {0x0832, 0x67}, + {0x0833, 0x3F}, + {0x0834, 0x3F}, + {0x0835, 0x4F}, + {0x0836, 0xDF}, + {0x0837, 0x47}, + {0x0839, 0x1F}, + {0x083A, 0x17}, + {0x083B, 0x02}, + + /* Integration Time Setting */ + {0x0202, 0x0A}, + {0x0203, 0x3C}, + + /* Gain Setting */ + {0x0205, 0x00}, + {0x020E, 0x01}, + {0x020F, 0x00}, + {0x0210, 0x01}, + {0x0211, 0x00}, + {0x0212, 0x01}, + {0x0213, 0x00}, + {0x0214, 0x01}, + {0x0215, 0x00}, + + /* HDR Setting */ + {0x0230, 0x00}, + {0x0231, 0x00}, + {0x0233, 0x00}, + {0x0234, 0x00}, + {0x0235, 0x40}, + {0x0238, 0x01}, + {0x0239, 0x04}, + {0x023B, 0x00}, + {0x023C, 0x01}, + {0x33B0, 0x0A}, + {0x33B1, 0x38}, + {0x33B3, 0x01}, + {0x33B4, 0x01}, + {0x3800, 0x00}, + + {0x3A43, 0x01}, + /* stream on */ + {0x0100, 0x01}, + + {IMX135_TABLE_WAIT_MS, IMX135_WAIT_MS}, + {IMX135_TABLE_END, 0x00} +}; + +static struct imx135_reg mode_3896x2192[] = { + /* software reset */ + {0x0103, 0x01}, + /* global settings */ + {0x0101, 0x00}, + {0x0105, 0x01}, + {0x0110, 0x00}, + {0x0220, 0x01}, + {0x3302, 0x11}, + {0x3833, 0x20}, + {0x3873, 0x03}, + {0x3893, 0x00}, + {0x3906, 0x08}, + {0x3907, 0x01}, + {0x391B, 0x00}, + {0x3C09, 0x01}, + {0x600A, 0x00}, + {0x3008, 0xB0}, + {0x320A, 0x01}, + {0x320D, 0x10}, + {0x3216, 0x2E}, + {0x322C, 0x02}, + {0x3409, 0x0C}, + {0x340C, 0x2D}, + {0x3411, 0x39}, + {0x3414, 0x1E}, + {0x3427, 0x04}, + {0x3480, 0x1E}, + {0x3484, 0x1E}, + {0x3488, 0x1E}, + {0x348C, 0x1E}, + {0x3490, 0x1E}, + {0x3494, 0x1E}, + {0x3511, 0x8F}, + {0x364F, 0x2D}, + + /* Clock Setting */ + {0x011E, 0x18}, + {0x011F, 0x00}, + {0x0301, 0x05}, + {0x0303, 0x01}, + {0x0305, 0x0C}, + {0x0309, 0x05}, + {0x030B, 0x01}, + {0x030C, 0x01}, + {0x030D, 0xC2}, + {0x030E, 0x01}, + {0x3A06, 0x11}, + + /* Mode Settings */ + {0x0108, 0x03}, + {0x0112, 0x0E}, + {0x0113, 0x0A}, + {0x0381, 0x01}, + {0x0383, 0x01}, + {0x0385, 0x01}, + {0x0387, 0x01}, + {0x0390, 0x00}, + {0x0391, 0x11}, + {0x0392, 0x00}, + {0x0401, 0x00}, + {0x0404, 0x00}, + {0x0405, 0x10}, + {0x4082, 0x01}, + {0x4083, 0x01}, + {0x7006, 0x04}, + + /* Optinal/Function settings */ + {0x0700, 0x00}, + {0x3A63, 0x00}, + {0x4100, 0xF8}, + {0x4203, 0xFF}, + {0x4344, 0x00}, + {0x441C, 0x01}, + + /* Size Setting */ + {0x0340, 0x0A}, + {0x0341, 0x40}, + {0x0342, 0x11}, + {0x0343, 0xDC}, + {0x0344, 0x00}, + {0x0345, 0x9C}, + {0x0346, 0x01}, + {0x0347, 0xD0}, + {0x0348, 0x0F}, + {0x0349, 0xD3}, + {0x034A, 0x0A}, + {0x034B, 0x5F}, + {0x034C, 0x0F}, + {0x034D, 0x38}, + {0x034E, 0x08}, + {0x034F, 0x90}, + {0x0350, 0x00}, + {0x0351, 0x00}, + {0x0352, 0x00}, + {0x0353, 0x00}, + {0x0354, 0x0F}, + {0x0355, 0x38}, + {0x0356, 0x08}, + {0x0357, 0x90}, + {0x301D, 0x30}, + {0x3310, 0x0F}, + {0x3311, 0x38}, + {0x3312, 0x08}, + {0x3313, 0x90}, + {0x331C, 0x0F}, + {0x331D, 0x32}, + {0x4084, 0x00}, + {0x4085, 0x00}, + {0x4086, 0x00}, + {0x4087, 0x00}, + {0x4400, 0x00}, + + /* Global Timing Setting */ + {0x0830, 0x87}, + {0x0831, 0x3F}, + {0x0832, 0x67}, + {0x0833, 0x3F}, + {0x0834, 0x3F}, + {0x0835, 0x4F}, + {0x0836, 0xDF}, + {0x0837, 0x47}, + {0x0839, 0x1F}, + {0x083A, 0x17}, + {0x083B, 0x02}, + + /* Integration Time Setting */ + {0x0202, 0x0A}, + {0x0203, 0x3C}, + + /* Gain Setting */ + {0x0205, 0x00}, + {0x020E, 0x01}, + {0x020F, 0x00}, + {0x0210, 0x01}, + {0x0211, 0x00}, + {0x0212, 0x01}, + {0x0213, 0x00}, + {0x0214, 0x01}, + {0x0215, 0x00}, + + /* HDR Setting */ + {0x0230, 0x00}, + {0x0231, 0x00}, + {0x0233, 0x00}, + {0x0234, 0x00}, + {0x0235, 0x40}, + {0x0238, 0x01}, + {0x0239, 0x04}, + {0x023B, 0x00}, + {0x023C, 0x01}, + {0x33B0, 0x0F}, + {0x33B1, 0x38}, + {0x33B3, 0x01}, + {0x33B4, 0x01}, + {0x3800, 0x00}, + + {0x3A43, 0x01}, + /* stream on */ + {0x0100, 0x01}, + + {IMX135_TABLE_WAIT_MS, IMX135_WAIT_MS}, + {IMX135_TABLE_END, 0x00} +}; + +static struct imx135_reg mode_quality_hdr[] = { + /* defect correction */ + {0x380A, 0x00}, + {0x380B, 0x00}, + {0x4103, 0x00}, + /* color artifact */ + {0x4243, 0x9A}, + {0x4330, 0x01}, + {0x4331, 0x90}, + {0x4332, 0x02}, + {0x4333, 0x58}, + {0x4334, 0x03}, + {0x4335, 0x20}, + {0x4336, 0x03}, + {0x4337, 0x84}, + {0x433C, 0x01}, + {0x4340, 0x02}, + {0x4341, 0x58}, + {0x4342, 0x03}, + {0x4343, 0x52}, + /* moire reduction */ + {0x4364, 0x0B}, + {0x4368, 0x00}, + {0x4369, 0x0F}, + {0x436A, 0x03}, + {0x436B, 0xA8}, + {0x436C, 0x00}, + {0x436D, 0x00}, + {0x436E, 0x00}, + {0x436F, 0x06}, + /* CNR parameter */ + {0x4281, 0x21}, + {0x4282, 0x18}, + {0x4283, 0x04}, + {0x4284, 0x08}, + {0x4287, 0x7F}, + {0x4288, 0x08}, + {0x428B, 0x7F}, + {0x428C, 0x08}, + {0x428F, 0x7F}, + {0x4297, 0x00}, + {0x4298, 0x7E}, + {0x4299, 0x7E}, + {0x429A, 0x7E}, + {0x42A4, 0xFB}, + {0x42A5, 0x7E}, + {0x42A6, 0xDF}, + {0x42A7, 0xB7}, + {0x42AF, 0x03}, + + /* ARNR Parameter Settings */ + {0x4207, 0x03}, + {0x4216, 0x08}, + {0x4217, 0x08}, + + /* DLC parameter */ + {0x4218, 0x00}, + {0x421B, 0x20}, + {0x421F, 0x04}, + {0x4222, 0x02}, + {0x4223, 0x22}, + {0x422E, 0x54}, + {0x422F, 0xFB}, + {0x4230, 0xFF}, + {0x4231, 0xFE}, + {0x4232, 0xFF}, + {0x4235, 0x58}, + {0x4236, 0xF7}, + {0x4237, 0xFD}, + {0x4239, 0x4E}, + {0x423A, 0xFC}, + {0x423B, 0xFD}, + + /* HDR Setting */ + {0x4300, 0x00}, + {0x4316, 0x12}, + {0x4317, 0x22}, + {0x4318, 0x00}, + {0x4319, 0x00}, + {0x431A, 0x00}, + {0x4324, 0x03}, + {0x4325, 0x20}, + {0x4326, 0x03}, + {0x4327, 0x84}, + {0x4328, 0x03}, + {0x4329, 0x20}, + {0x432A, 0x03}, + {0x432B, 0x20}, + {0x432C, 0x01}, + {0x432D, 0x01}, + {0x4338, 0x02}, + {0x4339, 0x00}, + {0x433A, 0x00}, + {0x433B, 0x02}, + {0x435A, 0x03}, + {0x435B, 0x84}, + {0x435E, 0x01}, + {0x435F, 0xFF}, + {0x4360, 0x01}, + {0x4361, 0xF4}, + {0x4362, 0x03}, + {0x4363, 0x84}, + {0x437B, 0x01}, + {0x4401, 0x3F}, + {0x4402, 0xFF}, + {0x4404, 0x13}, + {0x4405, 0x26}, + {0x4406, 0x07}, + {0x4408, 0x20}, + {0x4409, 0xE5}, + {0x440A, 0xFB}, + {0x440C, 0xF6}, + {0x440D, 0xEA}, + {0x440E, 0x20}, + {0x4410, 0x00}, + {0x4411, 0x00}, + {0x4412, 0x3F}, + {0x4413, 0xFF}, + {0x4414, 0x1F}, + {0x4415, 0xFF}, + {0x4416, 0x20}, + {0x4417, 0x00}, + {0x4418, 0x1F}, + {0x4419, 0xFF}, + {0x441A, 0x20}, + {0x441B, 0x00}, + {0x441D, 0x40}, + {0x441E, 0x1E}, + {0x441F, 0x38}, + {0x4420, 0x01}, + {0x4444, 0x00}, + {0x4445, 0x00}, + {0x4446, 0x1D}, + {0x4447, 0xF9}, + {0x4452, 0x00}, + {0x4453, 0xA0}, + {0x4454, 0x08}, + {0x4455, 0x00}, + {0x4456, 0x0F}, + {0x4457, 0xFF}, + {0x4458, 0x18}, + {0x4459, 0x18}, + {0x445A, 0x3F}, + {0x445B, 0x3A}, + {0x445C, 0x00}, + {0x445D, 0x28}, + {0x445E, 0x01}, + {0x445F, 0x90}, + {0x4460, 0x00}, + {0x4461, 0x60}, + {0x4462, 0x00}, + {0x4463, 0x00}, + {0x4464, 0x00}, + {0x4465, 0x00}, + {0x446C, 0x01}, + {0x446D, 0x00}, + {0x446E, 0x00}, + + /* LSC setting */ + {0x452A, 0x02}, + + /* White balance */ + {0x0712, 0x01}, + {0x0713, 0x00}, + {0x0714, 0x01}, + {0x0715, 0x00}, + {0x0716, 0x01}, + {0x0717, 0x00}, + {0x0718, 0x01}, + {0x0719, 0x00}, + + /* Shading */ + {0x4500, 0x1F}, + + {IMX135_TABLE_WAIT_MS, IMX135_WAIT_MS}, + {IMX135_TABLE_END, 0x00} +}; + +static struct imx135_reg mode_quality[] = { + /* defect correction */ + {0x380A, 0x00}, + {0x380B, 0x00}, + {0x4103, 0x00}, + /* color artifact */ + {0x4243, 0x9A}, + {0x4330, 0x01}, + {0x4331, 0x90}, + {0x4332, 0x02}, + {0x4333, 0x58}, + {0x4334, 0x03}, + {0x4335, 0x20}, + {0x4336, 0x03}, + {0x4337, 0x84}, + {0x433C, 0x01}, + {0x4340, 0x02}, + {0x4341, 0x58}, + {0x4342, 0x03}, + {0x4343, 0x52}, + /* moire reduction */ + {0x4364, 0x0B}, + {0x4368, 0x00}, + {0x4369, 0x0F}, + {0x436A, 0x03}, + {0x436B, 0xA8}, + {0x436C, 0x00}, + {0x436D, 0x00}, + {0x436E, 0x00}, + {0x436F, 0x06}, + /* CNR parameter */ + {0x4281, 0x21}, + {0x4282, 0x18}, + {0x4283, 0x04}, + {0x4284, 0x08}, + {0x4287, 0x7F}, + {0x4288, 0x08}, + {0x428B, 0x7F}, + {0x428C, 0x08}, + {0x428F, 0x7F}, + {0x4297, 0x00}, + {0x4298, 0x7E}, + {0x4299, 0x7E}, + {0x429A, 0x7E}, + {0x42A4, 0xFB}, + {0x42A5, 0x7E}, + {0x42A6, 0xDF}, + {0x42A7, 0xB7}, + {0x42AF, 0x03}, + + /* ARNR Parameter Settings */ + {0x4207, 0x03}, + {0x4216, 0x08}, + {0x4217, 0x08}, + + /* DLC parameter */ + {0x4218, 0x00}, + {0x421B, 0x20}, + {0x421F, 0x04}, + {0x4222, 0x02}, + {0x4223, 0x22}, + {0x422E, 0x54}, + {0x422F, 0xFB}, + {0x4230, 0xFF}, + {0x4231, 0xFE}, + {0x4232, 0xFF}, + {0x4235, 0x58}, + {0x4236, 0xF7}, + {0x4237, 0xFD}, + {0x4239, 0x4E}, + {0x423A, 0xFC}, + {0x423B, 0xFD}, + + /* HDR Setting */ + {0x4300, 0x00}, + {0x4316, 0x12}, + {0x4317, 0x22}, + {0x4318, 0x00}, + {0x4319, 0x00}, + {0x431A, 0x00}, + {0x4324, 0x03}, + {0x4325, 0x20}, + {0x4326, 0x03}, + {0x4327, 0x84}, + {0x4328, 0x03}, + {0x4329, 0x20}, + {0x432A, 0x03}, + {0x432B, 0x20}, + {0x432C, 0x01}, + {0x432D, 0x01}, + {0x4338, 0x02}, + {0x4339, 0x00}, + {0x433A, 0x00}, + {0x433B, 0x02}, + {0x435A, 0x03}, + {0x435B, 0x84}, + {0x435E, 0x01}, + {0x435F, 0xFF}, + {0x4360, 0x01}, + {0x4361, 0xF4}, + {0x4362, 0x03}, + {0x4363, 0x84}, + {0x437B, 0x01}, + {0x4401, 0x3F}, + {0x4402, 0xFF}, + {0x4404, 0x13}, + {0x4405, 0x26}, + {0x4406, 0x07}, + {0x4408, 0x20}, + {0x4409, 0xE5}, + {0x440A, 0xFB}, + {0x440C, 0xF6}, + {0x440D, 0xEA}, + {0x440E, 0x20}, + {0x4410, 0x00}, + {0x4411, 0x00}, + {0x4412, 0x3F}, + {0x4413, 0xFF}, + {0x4414, 0x1F}, + {0x4415, 0xFF}, + {0x4416, 0x20}, + {0x4417, 0x00}, + {0x4418, 0x1F}, + {0x4419, 0xFF}, + {0x441A, 0x20}, + {0x441B, 0x00}, + {0x441D, 0x40}, + {0x441E, 0x1E}, + {0x441F, 0x38}, + {0x4420, 0x01}, + {0x4444, 0x00}, + {0x4445, 0x00}, + {0x4446, 0x1D}, + {0x4447, 0xF9}, + {0x4452, 0x00}, + {0x4453, 0xA0}, + {0x4454, 0x08}, + {0x4455, 0x00}, + {0x4456, 0x0F}, + {0x4457, 0xFF}, + {0x4458, 0x18}, + {0x4459, 0x18}, + {0x445A, 0x3F}, + {0x445B, 0x3A}, + {0x445C, 0x00}, + {0x445D, 0x28}, + {0x445E, 0x01}, + {0x445F, 0x90}, + {0x4460, 0x00}, + {0x4461, 0x60}, + {0x4462, 0x00}, + {0x4463, 0x00}, + {0x4464, 0x00}, + {0x4465, 0x00}, + {0x446C, 0x01}, + {0x446D, 0x00}, + {0x446E, 0x00}, + + /* LSC setting */ + {0x452A, 0x02}, + + /* White balance */ + {0x0712, 0x01}, + {0x0713, 0x00}, + {0x0714, 0x01}, + {0x0715, 0x00}, + {0x0716, 0x01}, + {0x0717, 0x00}, + {0x0718, 0x01}, + {0x0719, 0x00}, + + /* Shading */ + {0x4500, 0x1F}, + + {IMX135_TABLE_WAIT_MS, IMX135_WAIT_MS}, + {IMX135_TABLE_END, 0x00} +}; + +enum { + IMX135_MODE_4208X3120, + IMX135_MODE_1920X1080, + IMX135_MODE_1280X720, + IMX135_MODE_2616X1472, + IMX135_MODE_3896X2192, + IMX135_MODE_QUALITY_HDR, + IMX135_MODE_QUALITY, +}; + +static struct imx135_reg *mode_table[] = { + [IMX135_MODE_4208X3120] = mode_4208x3120, + [IMX135_MODE_1920X1080] = mode_1920x1080, + [IMX135_MODE_1280X720] = mode_1280x720, + [IMX135_MODE_2616X1472] = mode_2616x1472, + [IMX135_MODE_3896X2192] = mode_3896x2192, + [IMX135_MODE_QUALITY_HDR] = mode_quality_hdr, + [IMX135_MODE_QUALITY] = mode_quality, +}; + +static inline void +msleep_range(unsigned int delay_base) +{ + usleep_range(delay_base*1000, delay_base*1000+500); +} + +static inline void +imx135_get_frame_length_regs(struct imx135_reg *regs, u32 frame_length) +{ + regs->addr = 0x0340; + regs->val = (frame_length >> 8) & 0xff; + (regs + 1)->addr = 0x0341; + (regs + 1)->val = (frame_length) & 0xff; +} + +static inline void +imx135_get_coarse_time_regs(struct imx135_reg *regs, u32 coarse_time) +{ + regs->addr = 0x202; + regs->val = (coarse_time >> 8) & 0xff; + (regs + 1)->addr = 0x203; + (regs + 1)->val = (coarse_time) & 0xff; +} + +static inline void +imx135_get_coarse_time_short_regs(struct imx135_reg *regs, u32 coarse_time) +{ + regs->addr = 0x230; + regs->val = (coarse_time >> 8) & 0xff; + (regs + 1)->addr = 0x231; + (regs + 1)->val = (coarse_time) & 0xff; +} + +static inline void +imx135_get_gain_reg(struct imx135_reg *regs, u16 gain) +{ + regs->addr = 0x205; + regs->val = gain; +} + +static inline void +imx135_get_gain_short_reg(struct imx135_reg *regs, u16 gain) +{ + regs->addr = 0x233; + regs->val = gain; +} + +static int +imx135_read_reg(struct i2c_client *client, u16 addr, u8 *val) +{ + int err; + struct i2c_msg msg[2]; + unsigned char data[3]; + + if (!client->adapter) + return -ENODEV; + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 2; + msg[0].buf = data; + + /* high byte goes out first */ + data[0] = (u8) (addr >> 8); + data[1] = (u8) (addr & 0xff); + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 1; + msg[1].buf = data + 2; + + err = i2c_transfer(client->adapter, msg, 2); + + if (err != 2) + return -EINVAL; + + *val = data[2]; + return 0; +} + +static int +imx135_write_reg(struct i2c_client *client, u16 addr, u8 val) +{ + int err; + struct i2c_msg msg; + unsigned char data[3]; + + if (!client->adapter) + return -ENODEV; + + data[0] = (u8) (addr >> 8); + data[1] = (u8) (addr & 0xff); + data[2] = (u8) (val & 0xff); + + msg.addr = client->addr; + msg.flags = 0; + msg.len = 3; + msg.buf = data; + + err = i2c_transfer(client->adapter, &msg, 1); + if (err == 1) + return 0; + + pr_err("%s:i2c write failed, %x = %x\n", + __func__, addr, val); + + return err; +} + +static int +imx135_write_table(struct i2c_client *client, + const struct imx135_reg table[], + const struct imx135_reg override_list[], + int num_override_regs) +{ + int err; + const struct imx135_reg *next; + int i; + u16 val; + + for (next = table; next->addr != IMX135_TABLE_END; next++) { + if (next->addr == IMX135_TABLE_WAIT_MS) { + msleep_range(next->val); + continue; + } + + val = next->val; + + /* When an override list is passed in, replace the reg */ + /* value to write if the reg is in the list */ + if (override_list) { + for (i = 0; i < num_override_regs; i++) { + if (next->addr == override_list[i].addr) { + val = override_list[i].val; + break; + } + } + } + + err = imx135_write_reg(client, next->addr, val); + if (err) { + pr_err("%s:imx135_write_table:%d", __func__, err); + return err; + } + } + return 0; +} + +static int +imx135_set_mode(struct imx135_info *info, struct imx135_mode *mode) +{ + int sensor_mode; + u8 quality_hdr; + int err; + struct imx135_reg reg_list[8]; + + pr_info("%s: xres %u yres %u framelength %u coarsetime %u gain %u, hdr %d\n", + __func__, mode->xres, mode->yres, mode->frame_length, + mode->coarse_time, mode->gain, mode->hdr_en); + + if (mode->xres == 4208 && mode->yres == 3120) { + sensor_mode = IMX135_MODE_4208X3120; + quality_hdr = 1; + } else if (mode->xres == 1920 && mode->yres == 1080) { + sensor_mode = IMX135_MODE_1920X1080; + quality_hdr = 0; + } else if (mode->xres == 1280 && mode->yres == 720) { + sensor_mode = IMX135_MODE_1280X720; + quality_hdr = 0; + } else if (mode->xres == 2616 && mode->yres == 1472) { + sensor_mode = IMX135_MODE_2616X1472; + quality_hdr = 1; + } else if (mode->xres == 3896 && mode->yres == 2192) { + sensor_mode = IMX135_MODE_3896X2192; + quality_hdr = 1; + } else { + pr_err("%s: invalid resolution supplied to set mode %d %d\n", + __func__, mode->xres, mode->yres); + return -EINVAL; + } + + /* get a list of override regs for the asking frame length, */ + /* coarse integration time, and gain. */ + imx135_get_frame_length_regs(reg_list, mode->frame_length); + imx135_get_coarse_time_regs(reg_list + 2, mode->coarse_time); + imx135_get_gain_reg(reg_list + 4, mode->gain); + if (mode->hdr_en == 1) /* if HDR is enabled */ + { + imx135_get_gain_short_reg(reg_list + 5, mode->gain); + imx135_get_coarse_time_short_regs( + reg_list + 6, mode->coarse_time_short); + } + + err = imx135_write_table(info->i2c_client, + mode_table[sensor_mode], + reg_list, mode->hdr_en ? 8 : 5); + if (err) + return err; + if (quality_hdr) + err = imx135_write_table(info->i2c_client, + mode_table[IMX135_MODE_QUALITY_HDR], + reg_list, 0); + else + err = imx135_write_table(info->i2c_client, + mode_table[IMX135_MODE_QUALITY], + reg_list, 0); + if (err) + return err; + + info->mode = sensor_mode; + pr_info("[IMX135]: stream on.\n"); + return 0; +} + +static int +imx135_get_status(struct imx135_info *info, u8 *dev_status) +{ + *dev_status = 0; + return 0; +} + +static int +imx135_set_frame_length(struct imx135_info *info, u32 frame_length, + bool group_hold) +{ + struct imx135_reg reg_list[2]; + int i = 0; + int ret; + + imx135_get_frame_length_regs(reg_list, frame_length); + + if (group_hold) { + ret = imx135_write_reg(info->i2c_client, 0x0104, 0x01); + if (ret) + return ret; + } + + for (i = 0; i < 2; i++) { + ret = imx135_write_reg(info->i2c_client, reg_list[i].addr, + reg_list[i].val); + if (ret) + return ret; + } + + if (group_hold) { + ret = imx135_write_reg(info->i2c_client, 0x0104, 0x0); + if (ret) + return ret; + } + + return 0; +} + +static int +imx135_set_coarse_time(struct imx135_info *info, u32 coarse_time, + bool group_hold) +{ + int ret; + + struct imx135_reg reg_list[2]; + int i = 0; + + imx135_get_coarse_time_regs(reg_list, coarse_time); + + if (group_hold) { + ret = imx135_write_reg(info->i2c_client, 0x104, 0x01); + if (ret) + return ret; + } + + for (i = 0; i < 2; i++) { + ret = imx135_write_reg(info->i2c_client, reg_list[i].addr, + reg_list[i].val); + if (ret) + return ret; + } + + if (group_hold) { + ret = imx135_write_reg(info->i2c_client, 0x104, 0x0); + if (ret) + return ret; + } + return 0; +} + +static int +imx135_set_gain(struct imx135_info *info, u16 gain, bool group_hold) +{ + int ret; + struct imx135_reg reg_list; + + imx135_get_gain_reg(®_list, gain); + + if (group_hold) { + ret = imx135_write_reg(info->i2c_client, 0x104, 0x1); + if (ret) + return ret; + } + + ret = imx135_write_reg(info->i2c_client, reg_list.addr, reg_list.val); + /* writing second gain register for HDR */ + ret = imx135_write_reg(info->i2c_client, 0x233, reg_list.val); + if (ret) + return ret; + + if (group_hold) { + ret = imx135_write_reg(info->i2c_client, 0x104, 0x0); + if (ret) + return ret; + } + return 0; +} + +static int +imx135_set_hdr_coarse_time(struct imx135_info *info, struct imx135_hdr *values) +{ + struct imx135_reg reg_list[2]; + struct imx135_reg reg_list_short[2]; + int ret, i = 0; + + /* get long and short coarse time registers */ + imx135_get_coarse_time_regs(reg_list, values->coarse_time_long); + imx135_get_coarse_time_short_regs(reg_list_short, + values->coarse_time_short); + /* set to direct mode */ + ret = imx135_write_reg(info->i2c_client, 0x238, 0x1); + if (ret) + return ret; + /* set group hold */ + ret = imx135_write_reg(info->i2c_client, 0x104, 0x1); + if (ret) + return ret; + /* writing long exposure */ + for (i = 0; i < 2; i++) { + ret = imx135_write_reg(info->i2c_client, reg_list[i].addr, + reg_list[i].val); + if (ret) + return ret; + } + /* writing short exposure */ + for (i = 0; i < 2; i++) { + ret = imx135_write_reg(info->i2c_client, reg_list_short[i].addr, + reg_list_short[i].val); + if (ret) + return ret; + } + ret = imx135_write_reg(info->i2c_client, 0x104, 0x0); + if (ret) + return ret; + + return 0; +} + +static int +imx135_set_group_hold(struct imx135_info *info, struct imx135_ae *ae) +{ + int ret; + int count = 0; + bool groupHoldEnabled = false; + struct imx135_hdr values; + + values.coarse_time_long = ae->coarse_time; + values.coarse_time_short = ae->coarse_time_short; + + if (ae->gain_enable) + count++; + if (ae->coarse_time_enable) + count++; + if (ae->frame_length_enable) + count++; + if (count >= 2) + groupHoldEnabled = true; + + if (groupHoldEnabled) { + ret = imx135_write_reg(info->i2c_client, 0x104, 0x1); + if (ret) + return ret; + } + + if (ae->gain_enable) + imx135_set_gain(info, ae->gain, false); + if (ae->coarse_time_enable) + imx135_set_hdr_coarse_time(info, &values); + if (ae->frame_length_enable) + imx135_set_frame_length(info, ae->frame_length, false); + + if (groupHoldEnabled) { + ret = imx135_write_reg(info->i2c_client, 0x104, 0x0); + if (ret) + return ret; + } + + return 0; +} + +static int imx135_get_sensor_id(struct imx135_info *info) +{ + int ret = 0; + int i; + u8 bak = 0; + + pr_info("%s\n", __func__); + if (info->sensor_data.fuse_id_size) + return 0; + + /* Note 1: If the sensor does not have power at this point + Need to supply the power, e.g. by calling power on function */ + + ret |= imx135_write_reg(info->i2c_client, 0x3B02, 0x00); + ret |= imx135_write_reg(info->i2c_client, 0x3B00, 0x01); + for (i = 0; i < 9 ; i++) { + ret |= imx135_read_reg(info->i2c_client, 0x3B24 + i, &bak); + info->sensor_data.fuse_id[i] = bak; + } + + if (!ret) + info->sensor_data.fuse_id_size = i; + + /* Note 2: Need to clean up any action carried out in Note 1 */ + + return ret; +} + +static long +imx135_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int err; + struct imx135_info *info = file->private_data; + + switch (cmd) { + case IMX135_IOCTL_SET_MODE: + { + struct imx135_mode mode; + if (copy_from_user(&mode, (const void __user *)arg, + sizeof(struct imx135_mode))) { + pr_err("%s:Failed to get mode from user.\n", __func__); + return -EFAULT; + } + return imx135_set_mode(info, &mode); + } + case IMX135_IOCTL_SET_FRAME_LENGTH: + return imx135_set_frame_length(info, (u32)arg, true); + case IMX135_IOCTL_SET_COARSE_TIME: + return imx135_set_coarse_time(info, (u32)arg, true); + case IMX135_IOCTL_SET_GAIN: + return imx135_set_gain(info, (u16)arg, true); + case IMX135_IOCTL_GET_STATUS: + { + u8 status; + + err = imx135_get_status(info, &status); + if (err) + return err; + if (copy_to_user((void __user *)arg, &status, 1)) { + pr_err("%s:Failed to copy status to user\n", __func__); + return -EFAULT; + } + return 0; + } + case IMX135_IOCTL_GET_SENSORDATA: + { + err = imx135_get_sensor_id(info); + + if (err) { + pr_err("%s:Failed to get fuse id info.\n", __func__); + return err; + } + if (copy_to_user((void __user *)arg, &info->sensor_data, + sizeof(struct imx135_sensordata))) { + pr_info("%s:Failed to copy fuse id to user space\n", + __func__); + return -EFAULT; + } + return 0; + } + case IMX135_IOCTL_SET_GROUP_HOLD: + { + struct imx135_ae ae; + if (copy_from_user(&ae, (const void __user *)arg, + sizeof(struct imx135_ae))) { + pr_info("%s:fail group hold\n", __func__); + return -EFAULT; + } + return imx135_set_group_hold(info, &ae); + } + case IMX135_IOCTL_SET_HDR_COARSE_TIME: + { + struct imx135_hdr values; + + dev_dbg(&info->i2c_client->dev, + "IMX135_IOCTL_SET_HDR_COARSE_TIME\n"); + if (copy_from_user(&values, + (const void __user *)arg, + sizeof(struct imx135_hdr))) { + err = -EFAULT; + break; + } + err = imx135_set_hdr_coarse_time(info, &values); + break; + } + default: + pr_err("%s:unknown cmd.\n", __func__); + return -EINVAL; + } + return 0; +} + +static int imx135_debugfs_show(struct seq_file *s, void *unused) +{ + struct imx135_info *dev = s->private; + + dev_dbg(&dev->i2c_client->dev, "%s: ++\n", __func__); + + mutex_lock(&dev->imx135_camera_lock); + mutex_unlock(&dev->imx135_camera_lock); + + return 0; +} + +static ssize_t imx135_debugfs_write( + struct file *file, + char const __user *buf, + size_t count, + loff_t *offset) +{ + struct imx135_info *dev = + ((struct seq_file *)file->private_data)->private; + struct i2c_client *i2c_client = dev->i2c_client; + int ret = 0; + char buffer[24]; + u32 address; + u32 data; + u8 readback; + + dev_dbg(&i2c_client->dev, "%s: ++\n", __func__); + + if (copy_from_user(&buffer, buf, sizeof(buffer))) + goto debugfs_write_fail; + + if (sscanf(buf, "0x%x 0x%x", &address, &data) == 2) + goto set_attr; + if (sscanf(buf, "0X%x 0X%x", &address, &data) == 2) + goto set_attr; + if (sscanf(buf, "%d %d", &address, &data) == 2) + goto set_attr; + + if (sscanf(buf, "0x%x 0x%x", &address, &data) == 1) + goto read; + if (sscanf(buf, "0X%x 0X%x", &address, &data) == 1) + goto read; + if (sscanf(buf, "%d %d", &address, &data) == 1) + goto read; + + dev_err(&i2c_client->dev, "SYNTAX ERROR: %s\n", buf); + return -EFAULT; + +set_attr: + dev_info(&i2c_client->dev, + "new address = %x, data = %x\n", address, data); + ret |= imx135_write_reg(i2c_client, address, data); +read: + ret |= imx135_read_reg(i2c_client, address, &readback); + dev_dbg(&i2c_client->dev, + "wrote to address 0x%x with value 0x%x\n", + address, readback); + + if (ret) + goto debugfs_write_fail; + + return count; + +debugfs_write_fail: + dev_err(&i2c_client->dev, + "%s: test pattern write failed\n", __func__); + return -EFAULT; +} + +static int imx135_debugfs_open(struct inode *inode, struct file *file) +{ + struct imx135_info *dev = inode->i_private; + struct i2c_client *i2c_client = dev->i2c_client; + + dev_dbg(&i2c_client->dev, "%s: ++\n", __func__); + + return single_open(file, imx135_debugfs_show, inode->i_private); +} + +static const struct file_operations imx135_debugfs_fops = { + .open = imx135_debugfs_open, + .read = seq_read, + .write = imx135_debugfs_write, + .llseek = seq_lseek, + .release = single_release, +}; + +static void imx135_remove_debugfs(struct imx135_info *dev) +{ + struct i2c_client *i2c_client = dev->i2c_client; + + dev_dbg(&i2c_client->dev, "%s: ++\n", __func__); + + if (dev->debugdir) + debugfs_remove_recursive(dev->debugdir); + dev->debugdir = NULL; +} + +static void imx135_create_debugfs(struct imx135_info *dev) +{ + struct dentry *ret; + struct i2c_client *i2c_client = dev->i2c_client; + + dev_dbg(&i2c_client->dev, "%s\n", __func__); + + dev->debugdir = + debugfs_create_dir(dev->miscdev_info.this_device->kobj.name, + NULL); + if (!dev->debugdir) + goto remove_debugfs; + + ret = debugfs_create_file("d", + S_IWUSR | S_IRUGO, + dev->debugdir, dev, + &imx135_debugfs_fops); + if (!ret) + goto remove_debugfs; + + return; +remove_debugfs: + dev_err(&i2c_client->dev, "couldn't create debugfs\n"); + imx135_remove_debugfs(dev); +} + +static int +imx135_open(struct inode *inode, struct file *file) +{ + struct miscdevice *miscdev = file->private_data; + struct imx135_info *info; + + info = container_of(miscdev, struct imx135_info, miscdev_info); + /* check if the device is in use */ + if (atomic_xchg(&info->in_use, 1)) { + pr_info("%s:BUSY!\n", __func__); + return -EBUSY; + } + + file->private_data = info; + + if (info->pdata && info->pdata->power_on) + info->pdata->power_on(&info->power); + else{ + pr_err("%s:no valid power_on function.\n", __func__); + return -EEXIST; + } + + return 0; +} + +static int +imx135_release(struct inode *inode, struct file *file) +{ + struct imx135_info *info = file->private_data; + + if (info->pdata && info->pdata->power_off) + info->pdata->power_off(&info->power); + file->private_data = NULL; + + /* warn if device is already released */ + WARN_ON(!atomic_xchg(&info->in_use, 0)); + return 0; +} + +static int imx135_power_put(struct imx135_power_rail *pw) +{ + 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 imx135_regulator_get(struct imx135_info *info, + struct regulator **vreg, char vreg_name[]) +{ + struct regulator *reg = NULL; + int err = 0; + + reg = regulator_get(&info->i2c_client->dev, vreg_name); + if (unlikely(IS_ERR_OR_NULL(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; + return err; +} + +static int imx135_power_get(struct imx135_info *info) +{ + struct imx135_power_rail *pw = &info->power; + + imx135_regulator_get(info, &pw->avdd, "vana"); /* ananlog 2.7v */ + imx135_regulator_get(info, &pw->dvdd, "vdig"); /* digital 1.2v */ + imx135_regulator_get(info, &pw->iovdd, "vif"); /* interface 1.8v */ + + return 0; +} + +static const struct file_operations imx135_fileops = { + .owner = THIS_MODULE, + .open = imx135_open, + .unlocked_ioctl = imx135_ioctl, + .release = imx135_release, +}; + +static struct miscdevice imx135_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "imx135", + .fops = &imx135_fileops, +}; + + +static int +imx135_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct imx135_info *info; + int err; + + pr_info("[IMX135]: probing sensor.\n"); + + info = devm_kzalloc(&client->dev, + sizeof(struct imx135_info), GFP_KERNEL); + if (!info) { + pr_err("%s:Unable to allocate memory!\n", __func__); + return -ENOMEM; + } + + info->pdata = client->dev.platform_data; + info->i2c_client = client; + atomic_set(&info->in_use, 0); + info->mode = -1; + + imx135_power_get(info); + + memcpy(&info->miscdev_info, + &imx135_device, + sizeof(struct miscdevice)); + + err = misc_register(&info->miscdev_info); + if (err) { + pr_err("%s:Unable to register misc device!\n", __func__); + goto imx135_probe_fail; + } + + i2c_set_clientdata(client, info); + /* create debugfs interface */ + imx135_create_debugfs(info); + return 0; + +imx135_probe_fail: + imx135_power_put(&info->power); + + return err; +} + +static int +imx135_remove(struct i2c_client *client) +{ + struct imx135_info *info; + info = i2c_get_clientdata(client); + misc_deregister(&imx135_device); + + imx135_power_put(&info->power); + + imx135_remove_debugfs(info); + return 0; +} + +static const struct i2c_device_id imx135_id[] = { + { "imx135", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, imx135_id); + +static struct i2c_driver imx135_i2c_driver = { + .driver = { + .name = "imx135", + .owner = THIS_MODULE, + }, + .probe = imx135_probe, + .remove = imx135_remove, + .id_table = imx135_id, +}; + +static int __init imx135_init(void) +{ + pr_info("[IMX135] sensor driver loading\n"); + return i2c_add_driver(&imx135_i2c_driver); +} + +static void __exit imx135_exit(void) +{ + i2c_del_driver(&imx135_i2c_driver); +} + +module_init(imx135_init); +module_exit(imx135_exit); diff --git a/drivers/media/video/tegra/max77665-flash.c b/drivers/media/video/tegra/max77665-flash.c index 15cf8897dfc0..2feee300eb42 100644 --- a/drivers/media/video/tegra/max77665-flash.c +++ b/drivers/media/video/tegra/max77665-flash.c @@ -1,7 +1,7 @@ /* * MAX77665_F.c - MAX77665_F flash/torch kernel driver * - * Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2012 - 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, @@ -65,7 +65,6 @@ #include <linux/slab.h> #include <linux/delay.h> #include <linux/uaccess.h> -#include <linux/list.h> #include <linux/regulator/consumer.h> #include <linux/gpio.h> #include <linux/seq_file.h> @@ -172,6 +171,8 @@ #define MAX77665_F_MAX_FLASH_LEVEL ((1 << 6) + 1) #define MAX77665_F_MAX_TORCH_LEVEL ((1 << 4) + 1) +#define MAX77665_F_LEVEL_OFF 0xFFFF + #define MAX77665_F_MAX_FLASH_CURRENT(x) \ DIV_ROUND_UP(((x) * MAX77665_F_MAX_FLASH_LEVEL), 1000) #define MAX77665_F_MAX_TORCH_CURRENT(x) \ @@ -180,6 +181,9 @@ #define SUSTAINTIME_DEF 558 #define DEFAULT_FLASH_TMR_DUR ((SUSTAINTIME_DEF * 10 - 1) / 625) +#define MAX77665_F_FLASH_TIMER_NUM 16 +#define MAX77665_F_TORCH_TIMER_NUM 17 + /* minimium debounce time 600uS */ #define RECHARGEFACTOR_DEF 600 @@ -187,11 +191,27 @@ #define MAXFLASH_MODE_TORCH 1 #define MAXFLASH_MODE_FLASH 2 -#define max77665_f_max_flash_cap_size (sizeof(u32) \ - + (sizeof(struct nvc_torch_level_info) \ - * (MAX77665_F_MAX_FLASH_LEVEL))) -#define max77665_f_max_torch_cap_size (sizeof(u32) \ - + (sizeof(s32) * (MAX77665_F_MAX_TORCH_LEVEL))) +#define max77665_f_flash_cap_size \ + (sizeof(struct nvc_torch_flash_capabilities_v1) \ + + sizeof(struct nvc_torch_lumi_level_v1) \ + * MAX77665_F_MAX_FLASH_LEVEL) +#define max77665_f_flash_timeout_size \ + (sizeof(struct nvc_torch_timer_capabilities_v1) \ + + sizeof(struct nvc_torch_timeout_v1) \ + * MAX77665_F_FLASH_TIMER_NUM) +#define max77665_f_max_flash_cap_size (max77665_f_flash_cap_size * 2 \ + + max77665_f_flash_timeout_size * 2) + +#define max77665_f_torch_cap_size \ + (sizeof(struct nvc_torch_torch_capabilities_v1) \ + + sizeof(struct nvc_torch_lumi_level_v1) \ + * MAX77665_F_MAX_TORCH_LEVEL) +#define max77665_f_torch_timeout_size \ + (sizeof(struct nvc_torch_timer_capabilities_v1) \ + + sizeof(struct nvc_torch_timeout_v1) \ + * MAX77665_F_TORCH_TIMER_NUM) +#define max77665_f_max_torch_cap_size (max77665_f_torch_timeout_size * 2\ + + max77665_f_torch_timeout_size * 2) #define GET_CURRENT_BY_INDEX(c) ((c) * 125 / 8) /* mul 15.625 mA */ #define GET_INDEX_BY_CURRENT(c) ((c) * 8 / 125) /* div by 15.625 mA */ @@ -230,13 +250,14 @@ struct max77665_f_info { struct device *dev; struct miscdevice miscdev; struct dentry *d_max77665_f; - struct list_head list; - struct max77665_f_info *s_info; struct mutex mutex; struct max77665_f_power_rail pwr_rail; struct max77665_f_platform_data *pdata; - struct nvc_torch_flash_capabilities *flash_cap; - struct nvc_torch_torch_capabilities *torch_cap; + struct nvc_torch_capability_query query; + struct nvc_torch_flash_capabilities_v1 *flash_cap[2]; + struct nvc_torch_timer_capabilities_v1 *flash_timeouts[2]; + struct nvc_torch_torch_capabilities_v1 *torch_cap[2]; + struct nvc_torch_timer_capabilities_v1 *torch_timeouts[2]; struct max77665_f_config config; struct max77665_f_reg_cache regs; struct max77665_f_state_regs states; @@ -246,13 +267,10 @@ struct max77665_f_info { atomic_t in_use; int flash_cap_size; int torch_cap_size; - int pwr_api; - int pwr_dev; - int sustainTime; + int pwr_state; u8 fled_settings; u8 op_mode; u8 power_is_on; - u8 s_mode; u8 ftimer_mode; u8 ttimer_mode; u8 new_ftimer; @@ -336,8 +354,15 @@ static struct max77665_f_platform_data max77665_f_default_pdata = { .pinstate = {0x0000, 0x0000}, }; -static LIST_HEAD(max77665_f_info_list); -static DEFINE_SPINLOCK(max77665_f_spinlock); +/* torch timer duration settings in uS */ +#define MAX77665_F_TORCH_TIMER_FOREVER 0xFFFFFFFF +static u32 max77665_f_torch_timer[] = { + 262000, 524000, 786000, 1048000, + 1572000, 2096000, 2620000, 3144000, + 4193000, 5242000, 6291000, 7340000, + 9437000, 11534000, 13631000, 15728000, + MAX77665_F_TORCH_TIMER_FOREVER +}; static void max77665_f_throttle(unsigned int new_state, void *priv_data); @@ -467,8 +492,10 @@ static int max77665_f_set_leds(struct max77665_f_info *info, u8 mask, u8 curr1, u8 curr2) { int err = 0; - u16 f_levels = info->flash_cap->numberoflevels - 2; - u16 t_levels = info->torch_cap->numberoflevels - 2; + u32 f_levels1 = info->flash_cap[0]->numberoflevels - 2; + u32 f_levels2 = info->flash_cap[1]->numberoflevels - 2; + u32 t_levels1 = info->torch_cap[0]->numberoflevels - 2; + u32 t_levels2 = info->torch_cap[1]->numberoflevels - 2; u8 fled_en = 0; u8 t_curr = 0; u8 regs[6]; @@ -490,13 +517,13 @@ static int max77665_f_set_leds(struct max77665_f_info *info, if (mask & 1) { if (info->op_mode == MAXFLASH_MODE_FLASH) { - if (curr1 > f_levels) - curr1 = f_levels; + if (curr1 > f_levels1) + curr1 = f_levels1; fled_en |= (info->fled_settings & LED1_FLASH_TRIG_MASK); regs[0] = curr1; } else { - if (curr1 > t_levels) - curr1 = t_levels; + if (curr1 > t_levels1) + curr1 = t_levels1; fled_en |= (info->fled_settings & LED1_TORCH_TRIG_MASK); t_curr = curr1; } @@ -504,13 +531,13 @@ static int max77665_f_set_leds(struct max77665_f_info *info, if (mask & 2) { if (info->op_mode == MAXFLASH_MODE_FLASH) { - if (curr2 > f_levels) - curr2 = f_levels; + if (curr2 > f_levels2) + curr2 = f_levels2; fled_en |= (info->fled_settings & LED2_FLASH_TRIG_MASK); regs[1] = curr2; } else { - if (curr2 > t_levels) - curr2 = t_levels; + if (curr2 > t_levels2) + curr2 = t_levels2; fled_en |= (info->fled_settings & LED2_TORCH_TRIG_MASK); t_curr |= curr2 << 4; } @@ -778,10 +805,12 @@ static int max77665_f_update_settings(struct max77665_f_info *info) static int max77665_f_configure(struct max77665_f_info *info, bool update) { struct max77665_f_config *pcfg = &info->config; - struct nvc_torch_flash_capabilities *pfcap = info->flash_cap; - struct nvc_torch_torch_capabilities *ptcap = info->torch_cap; - int val; - int i; + struct nvc_torch_capability_query *pqry = &info->query; + struct nvc_torch_flash_capabilities_v1 *pfcap = NULL; + struct nvc_torch_torch_capabilities_v1 *ptcap = NULL; + struct nvc_torch_timer_capabilities_v1 *ptmcap = NULL; + struct nvc_torch_lumi_level_v1 *plvls = NULL; + int val, i, j; if (pcfg->max_peak_current_mA > max77665_f_caps.max_peak_curr_mA || !pcfg->max_peak_current_mA) { @@ -792,9 +821,14 @@ static int max77665_f_configure(struct max77665_f_info *info, bool update) pcfg->max_peak_current_mA = max77665_f_caps.max_peak_curr_mA; } + /* number of leds enabled */ i = 1; - if ((info->config.led_mask & 3) == 3) + /* in synchronize mode, both leds are considered as 1 */ + if (!pcfg->synchronized_led && (info->config.led_mask & 3) == 3) i = 2; + pqry->flash_num = i; + pqry->torch_num = i; + val = pcfg->max_peak_current_mA * i; if (val > max77665_f_caps.max_total_current_mA) val = max77665_f_caps.max_total_current_mA; @@ -814,37 +848,80 @@ static int max77665_f_configure(struct max77665_f_info *info, bool update) max77665_f_caps.max_torch_curr_mA; } - pfcap->levels[0].guidenum = 0; - pfcap->levels[0].sustaintime = 0xFFFFFFFF; - pfcap->levels[0].rechargefactor = 0; - val = max77665_f_caps.curr_step_uA; - for (i = 1; i < MAX77665_F_MAX_FLASH_LEVEL; i++) { - pfcap->levels[i].guidenum = val * i / 1000; /* mA */ - if (pfcap->levels[i].guidenum > - pcfg->max_peak_current_mA) { - pfcap->levels[i].guidenum = 0; - break; + pqry->version = NVC_TORCH_CAPABILITY_VER_1; + pqry->led_attr = 0; + for (i = 0; i < pqry->flash_num; i++) { + pfcap = info->flash_cap[i]; + pfcap->version = NVC_TORCH_CAPABILITY_VER_1; + pfcap->led_idx = i; + pfcap->attribute = 0; + pfcap->numberoflevels = pcfg->led_config[i].flash_levels + 1; + pfcap->granularity = pcfg->led_config[i].granularity; + pfcap->timeout_num = MAX77665_F_FLASH_TIMER_NUM; + ptmcap = info->flash_timeouts[i]; + pfcap->timeout_off = (void *)ptmcap - (void *)pfcap; + pfcap->flash_torch_ratio = + pcfg->led_config[i].flash_torch_ratio; + dev_dbg(info->dev, + "%s flash#%d, attr: %x, levels: %d, g: %d, ratio: %d\n", + __func__, pfcap->led_idx, pfcap->attribute, + pfcap->numberoflevels, pfcap->granularity, + pfcap->flash_torch_ratio); + + plvls = pcfg->led_config[i].lumi_levels; + pfcap->levels[0].guidenum = MAX77665_F_LEVEL_OFF; + pfcap->levels[0].luminance = 0; + for (j = 1; j < pfcap->numberoflevels; j++) { + pfcap->levels[j].guidenum = plvls[j - 1].guidenum; + pfcap->levels[j].luminance = plvls[j - 1].luminance; + dev_dbg(info->dev, "%02d - %d\n", + pfcap->levels[j].guidenum, + pfcap->levels[j].luminance); + } + + ptmcap->timeout_num = pfcap->timeout_num; + for (j = 0; j < ptmcap->timeout_num; j++) { + ptmcap->timeouts[j].timeout = 62500 * (j + 1); + dev_dbg(info->dev, "t: %02d - %d uS\n", j, + ptmcap->timeouts[j].timeout); } - pfcap->levels[i].sustaintime = info->sustainTime; - pfcap->levels[i].rechargefactor = RECHARGEFACTOR_DEF; } - info->flash_cap_size = (sizeof(u32) + - (sizeof(struct nvc_torch_level_info) * i)); - pfcap->numberoflevels = i; - - ptcap->guidenum[0] = 0; - for (i = 1; i < MAX77665_F_MAX_TORCH_LEVEL; i++) { - ptcap->guidenum[i] = pfcap->levels[i].guidenum; - if (ptcap->guidenum[i] > pcfg->max_torch_current_mA) { - ptcap->guidenum[i] = 0; - break; + + for (i = 0; i < pqry->torch_num; i++) { + ptcap = info->torch_cap[i]; + ptcap->version = NVC_TORCH_CAPABILITY_VER_1; + ptcap->led_idx = i; + ptcap->attribute = 0; + ptcap->numberoflevels = pcfg->led_config[i].flash_levels + 1; + ptcap->granularity = pcfg->led_config[i].granularity; + ptcap->timeout_num = MAX77665_F_TORCH_TIMER_NUM; + ptmcap = info->torch_timeouts[i]; + ptcap->timeout_off = (void *)ptmcap - (void *)ptcap; + dev_dbg(info->dev, "torch#%d, attr: %x, levels: %d, g: %d\n", + ptcap->led_idx, ptcap->attribute, + ptcap->numberoflevels, ptcap->granularity); + + plvls = pcfg->led_config[i].lumi_levels; + ptcap->levels[0].guidenum = MAX77665_F_LEVEL_OFF; + ptcap->levels[0].luminance = 0; + for (j = 1; j < ptcap->numberoflevels; j++) { + ptcap->levels[j].guidenum = plvls[j - 1].guidenum; + ptcap->levels[j].luminance = plvls[j - 1].luminance; + dev_dbg(info->dev, "%02d - %d\n", + ptcap->levels[j].guidenum, + ptcap->levels[j].luminance); + } + + ptmcap->timeout_num = ptcap->timeout_num; + for (j = 0; j < ptmcap->timeout_num; j++) { + ptmcap->timeouts[j].timeout = max77665_f_torch_timer[j]; + dev_dbg(info->dev, "t: %02d - %d uS\n", j, + ptmcap->timeouts[j].timeout); } } - info->torch_cap_size = (sizeof(u32) + (sizeof(s32) * i)); - ptcap->numberoflevels = i; - if (update && (info->pwr_dev == NVC_PWR_COMM || - info->pwr_dev == NVC_PWR_ON)) + if (update && (info->pwr_state == NVC_PWR_COMM || + info->pwr_state == NVC_PWR_ON)) return max77665_f_update_settings(info); return 0; @@ -1018,25 +1095,25 @@ max77665_f_poweron_vio_fail: return err; } -static int max77665_f_power(struct max77665_f_info *info, int pwr) +static int max77665_f_power_set(struct max77665_f_info *info, int pwr) { int err = 0; - if (pwr == info->pwr_dev) + if (pwr == info->pwr_state) return 0; switch (pwr) { case NVC_PWR_OFF: max77665_f_enter_offmode(info, true); if ((info->pdata->cfg & NVC_CFG_OFF2STDBY) || - (info->pdata->cfg & NVC_CFG_BOOT_INIT)) + (info->pdata->cfg & NVC_CFG_BOOT_INIT)) pwr = NVC_PWR_STDBY; else err = max77665_f_power_off(info); break; case NVC_PWR_STDBY_OFF: if ((info->pdata->cfg & NVC_CFG_OFF2STDBY) || - (info->pdata->cfg & NVC_CFG_BOOT_INIT)) + (info->pdata->cfg & NVC_CFG_BOOT_INIT)) pwr = NVC_PWR_STDBY; else err = max77665_f_power_on(info); @@ -1060,54 +1137,28 @@ static int max77665_f_power(struct max77665_f_info *info, int pwr) dev_err(info->dev, "%s error\n", __func__); pwr = NVC_PWR_ERR; } - info->pwr_dev = pwr; + info->pwr_state = pwr; if (err > 0) return 0; return err; } -static int max77665_f_power_sync(struct max77665_f_info *info, int pwr) -{ - int err1 = 0; - int err2 = 0; - - if ((info->s_mode == NVC_SYNC_OFF) || - (info->s_mode == NVC_SYNC_MASTER) || - (info->s_mode == NVC_SYNC_STEREO)) - err1 = max77665_f_power(info, pwr); - if ((info->s_mode == NVC_SYNC_SLAVE) || - (info->s_mode == NVC_SYNC_STEREO)) - err2 = max77665_f_power(info->s_info, pwr); - return err1 | err2; -} - -static int max77665_f_power_user_set(struct max77665_f_info *info, int pwr) +static inline int max77665_f_power_user_set( + struct max77665_f_info *info, int pwr) { int err = 0; if (!pwr || (pwr > NVC_PWR_ON)) return 0; - if (pwr > info->pwr_dev) - err = max77665_f_power_sync(info, pwr); - if (!err) - info->pwr_api = pwr; - else - info->pwr_api = NVC_PWR_ERR; + err = max77665_f_power_set(info, pwr); if (info->pdata->cfg & NVC_CFG_NOERR) return 0; return err; } -static int max77665_f_dev_power_set(struct max77665_f_info *info, int pwr) -{ - if (pwr < info->pwr_api) - pwr = info->pwr_api; - return max77665_f_power(info, pwr); -} - static int max77665_f_get_param(struct max77665_f_info *info, long arg) { struct nvc_param params; @@ -1116,43 +1167,70 @@ static int max77665_f_get_param(struct max77665_f_info *info, long arg) u32 data_size = 0; u8 reg; - if (copy_from_user(¶ms, - (const void __user *)arg, + if (copy_from_user(¶ms, (const void __user *)arg, sizeof(struct nvc_param))) { dev_err(info->dev, "%s %d copy_from_user err\n", __func__, __LINE__); return -EINVAL; } - if (info->s_mode == NVC_SYNC_SLAVE) - info = info->s_info; switch (params.param) { - case NVC_PARAM_FLASH_CAPS: - dev_dbg(info->dev, "%s FLASH_CAPS\n", __func__); - data_ptr = info->flash_cap; + case NVC_PARAM_TORCH_QUERY: + dev_dbg(info->dev, "%s QUERY\n", __func__); + data_ptr = &info->query; + data_size = sizeof(info->query); + break; + case NVC_PARAM_FLASH_EXT_CAPS: + dev_dbg(info->dev, "%s EXT_FLASH_CAPS %d\n", + __func__, params.variant); + if (params.variant >= info->query.flash_num) { + dev_err(info->dev, "%s unsupported flash index.\n", + __func__); + return -EINVAL; + } + data_ptr = info->flash_cap[params.variant]; data_size = info->flash_cap_size; break; - + case NVC_PARAM_TORCH_EXT_CAPS: + dev_dbg(info->dev, "%s EXT_TORCH_CAPS %d\n", + __func__, params.variant); + if (params.variant >= info->query.torch_num) { + dev_err(info->dev, "%s unsupported torch index.\n", + __func__); + return -EINVAL; + } + data_ptr = info->torch_cap[params.variant]; + data_size = info->torch_cap_size; + break; case NVC_PARAM_FLASH_LEVEL: - reg = info->regs.led1_curr; - data_ptr = &info->flash_cap->levels[reg].guidenum; - data_size = sizeof(info->flash_cap->levels[reg].guidenum); + if (params.variant >= info->query.flash_num) { + dev_err(info->dev, + "%s unsupported flash index.\n", __func__); + return -EINVAL; + } + if (params.variant > 0) + reg = info->regs.led2_curr; + else + reg = info->regs.led1_curr; + data_ptr = ® + data_size = sizeof(reg); dev_dbg(info->dev, "%s FLASH_LEVEL %d\n", __func__, reg); break; - - case NVC_PARAM_TORCH_CAPS: - dev_dbg(info->dev, "%s TORCH_CAPS\n", __func__); - data_ptr = info->torch_cap; - data_size = info->torch_cap_size; - break; - case NVC_PARAM_TORCH_LEVEL: - reg = info->regs.led1_curr; - data_ptr = &info->torch_cap->guidenum[reg]; - data_size = sizeof(info->torch_cap->guidenum[reg]); + reg = info->regs.led_tcurr; + if (params.variant >= info->query.torch_num) { + dev_err(info->dev, "%s unsupported torch index.\n", + __func__); + return -EINVAL; + } + if (params.variant > 0) + reg >>= 4; + else + reg &= 0x0F; + data_ptr = ® + data_size = sizeof(reg); dev_dbg(info->dev, "%s TORCH_LEVEL %d\n", __func__, reg); break; - case NVC_PARAM_FLASH_PIN_STATE: pinstate = info->pdata->pinstate; if (info->op_mode != MAXFLASH_MODE_FLASH) @@ -1164,28 +1242,21 @@ static int max77665_f_get_param(struct max77665_f_info *info, long arg) data_ptr = &pinstate; data_size = sizeof(pinstate); break; - - case NVC_PARAM_STEREO: - dev_dbg(info->dev, "%s STEREO: %d\n", __func__, info->s_mode); - data_ptr = &info->s_mode; - data_size = sizeof(info->s_mode); - break; - default: dev_err(info->dev, "%s unsupported parameter: %d\n", __func__, params.param); return -EINVAL; } + dev_dbg(info->dev, "%s data size user %d vs local %d\n", + __func__, params.sizeofvalue, data_size); if (params.sizeofvalue < data_size) { - dev_err(info->dev, "%s data size mismatch %d != %d\n", - __func__, params.sizeofvalue, data_size); + dev_err(info->dev, "%s data size mismatch\n", __func__); return -EINVAL; } if (copy_to_user((void __user *)params.p_value, - data_ptr, - data_size)) { + data_ptr, data_size)) { dev_err(info->dev, "%s copy_to_user err line %d\n", __func__, __LINE__); return -EFAULT; @@ -1194,160 +1265,118 @@ static int max77665_f_get_param(struct max77665_f_info *info, long arg) return 0; } -static int max77665_f_param_update(struct max77665_f_info *info, +static int max77665_f_get_levels(struct max77665_f_info *info, struct nvc_param *params, - u8 val) + bool flash_mode, + struct nvc_torch_set_level_v1 *plevels) { - int err; - - switch (params->param) { - case NVC_PARAM_FLASH_LEVEL: - dev_dbg(info->dev, "%s FLASH_LEVEL: %d\n", __func__, val); - if (val) { - info->new_ftimer = DEFAULT_FLASH_TMR_DUR; - info->op_mode = MAXFLASH_MODE_FLASH; - val--; - } else - info->op_mode = MAXFLASH_MODE_NONE; + struct nvc_torch_timer_capabilities_v1 *p_tm; + u8 op_mode; - max77665_f_dev_power_set(info, NVC_PWR_ON); - err = max77665_f_set_leds(info, - info->config.led_mask, val, val); - /*turn pwr off if no flash && no pwr_api*/ - if (info->op_mode == MAXFLASH_MODE_NONE) - max77665_f_dev_power_set(info, NVC_PWR_OFF); - return err; + if (copy_from_user(plevels, (const void __user *)params->p_value, + sizeof(*plevels))) { + dev_err(info->dev, "%s %d copy_from_user err\n", + __func__, __LINE__); + return -EINVAL; + } - case NVC_PARAM_TORCH_LEVEL: - dev_dbg(info->dev, "%s TORCH_LEVEL: %d\n", __func__, val); - if (val) { - info->op_mode = MAXFLASH_MODE_TORCH; - val--; - } else - info->op_mode = MAXFLASH_MODE_NONE; + if (flash_mode) { + dev_dbg(info->dev, "%s FLASH_LEVEL: %d %d %d\n", + __func__, plevels->ledmask, + plevels->levels[0], plevels->levels[1]); + p_tm = info->flash_timeouts[0]; + op_mode = MAXFLASH_MODE_FLASH; + } else { + dev_dbg(info->dev, "%s TORCH_LEVEL: %d %d %d\n", + __func__, plevels->ledmask, + plevels->levels[0], plevels->levels[1]); + p_tm = info->torch_timeouts[0]; + op_mode = MAXFLASH_MODE_TORCH; + } - max77665_f_dev_power_set(info, NVC_PWR_ON); - err = max77665_f_set_leds(info, - info->config.led_mask, val, val); - /*turn pwr off if no flash && no pwr_api*/ - if (info->op_mode == MAXFLASH_MODE_NONE) - max77665_f_dev_power_set(info, NVC_PWR_OFF); - return err; + if (plevels->timeout) { + u16 i; + for (i = 0; i < p_tm->timeout_num; i++) { + plevels->timeout = i; + if (plevels->timeout == p_tm->timeouts[i].timeout) + break; + } + } else + plevels->timeout = p_tm->timeout_num - 1; - case NVC_PARAM_FLASH_PIN_STATE: - dev_dbg(info->dev, "%s FLASH_PIN_STATE: %d\n", - __func__, val); - return max77665_f_strobe(info, val); + if (plevels->levels[0] == MAX77665_F_LEVEL_OFF) + plevels->ledmask &= ~1; + if (plevels->levels[1] == MAX77665_F_LEVEL_OFF) + plevels->ledmask &= ~2; + plevels->ledmask &= info->config.led_mask; - default: - dev_err(info->dev, "%s unsupported parameter: %d\n", - __func__, params->param); - return -EINVAL; + if (!plevels->ledmask) + info->op_mode = MAXFLASH_MODE_NONE; + else { + info->op_mode = op_mode; + if (info->config.synchronized_led) { + plevels->ledmask = 3; + plevels->levels[1] = plevels->levels[0]; + } } + + dev_dbg(info->dev, "Return: %d - %d %d %d\n", info->op_mode, + plevels->ledmask, plevels->levels[0], plevels->levels[1]); + return 0; } static int max77665_f_set_param(struct max77665_f_info *info, long arg) { struct nvc_param params; + struct nvc_torch_set_level_v1 led_levels; + u8 curr1; + u8 curr2; u8 val; int err = 0; - if (copy_from_user(¶ms, - (const void __user *)arg, - sizeof(struct nvc_param))) { - dev_err(info->dev, "%s %d copy_from_user err\n", - __func__, __LINE__); - return -EINVAL; - } - - if (copy_from_user(&val, (const void __user *)params.p_value, - sizeof(val))) { + if (copy_from_user( + ¶ms, (const void __user *)arg, sizeof(struct nvc_param))) { dev_err(info->dev, "%s %d copy_from_user err\n", __func__, __LINE__); return -EINVAL; } - /* parameters independent of sync mode */ switch (params.param) { - case NVC_PARAM_STEREO: - dev_dbg(info->dev, "%s STEREO: %d\n", __func__, (int)val); - if (val == info->s_mode) - return 0; - - switch (val) { - case NVC_SYNC_OFF: - info->s_mode = val; - if (info->s_info != NULL) { - info->s_info->s_mode = val; - max77665_f_power(info->s_info, NVC_PWR_OFF); - } - break; - - case NVC_SYNC_MASTER: - info->s_mode = val; - if (info->s_info != NULL) - info->s_info->s_mode = val; - break; - - case NVC_SYNC_SLAVE: - case NVC_SYNC_STEREO: - if (info->s_info != NULL) { - /* sync power */ - info->s_info->pwr_api = info->pwr_api; - err = max77665_f_power(info->s_info, - info->pwr_dev); - if (!err) { - info->s_mode = val; - info->s_info->s_mode = val; - } else { - max77665_f_power(info->s_info, - NVC_PWR_OFF); - err = -EIO; - } - } else { - err = -EINVAL; - } - break; - - default: - err = -EINVAL; - } - if (info->pdata->cfg & NVC_CFG_NOERR) - return 0; - + case NVC_PARAM_FLASH_LEVEL: + max77665_f_get_levels(info, ¶ms, true, &led_levels); + info->new_ftimer = led_levels.timeout & 0X0F; + curr1 = led_levels.levels[0]; + curr2 = led_levels.levels[1]; + err = max77665_f_set_leds(info, + led_levels.ledmask, curr1, curr2); return err; - - default: - /* parameters dependent on sync mode */ - switch (info->s_mode) { - case NVC_SYNC_OFF: - case NVC_SYNC_MASTER: - return max77665_f_param_update(info, ¶ms, val); - - case NVC_SYNC_SLAVE: - return max77665_f_param_update(info->s_info, - ¶ms, - val); - - case NVC_SYNC_STEREO: - err = max77665_f_param_update(info, ¶ms, val); - if (!(info->pdata->cfg & NVC_CFG_SYNC_I2C_MUX)) - err |= max77665_f_param_update(info->s_info, - ¶ms, - val); - return err; - - default: - dev_err(info->dev, "%s %d internal err\n", - __func__, __LINE__); + case NVC_PARAM_TORCH_LEVEL: + max77665_f_get_levels(info, ¶ms, false, &led_levels); + info->new_ftimer = led_levels.timeout & 0X0F; + curr1 = led_levels.levels[0]; + curr2 = led_levels.levels[1]; + err = max77665_f_set_leds(info, + led_levels.ledmask, curr1, curr2); + return err; + case NVC_PARAM_FLASH_PIN_STATE: + if (copy_from_user(&val, (const void __user *)params.p_value, + sizeof(val))) { + dev_err(info->dev, "%s %d copy_from_user err\n", + __func__, __LINE__); return -EINVAL; } + dev_dbg(info->dev, "%s FLASH_PIN_STATE: %d\n", + __func__, val); + return max77665_f_strobe(info, val); + default: + dev_err(info->dev, "%s unsupported parameter: %d\n", + __func__, params.param); + return -EINVAL; } } -static long max77665_f_ioctl(struct file *file, - unsigned int cmd, - unsigned long arg) +static long max77665_f_ioctl( + struct file *file, unsigned int cmd, unsigned long arg) { struct max77665_f_info *info = file->private_data; int pwr; @@ -1355,31 +1384,24 @@ static long max77665_f_ioctl(struct file *file, switch (cmd) { case NVC_IOCTL_PARAM_WR: return max77665_f_set_param(info, arg); - case NVC_IOCTL_PARAM_RD: return max77665_f_get_param(info, arg); - case NVC_IOCTL_PWR_WR: /* This is a Guaranteed Level of Service (GLOS) call */ pwr = (int)arg * 2; dev_dbg(info->dev, "%s PWR_WR: %d\n", __func__, pwr); return max77665_f_power_user_set(info, pwr); - case NVC_IOCTL_PWR_RD: - if (info->s_mode == NVC_SYNC_SLAVE) - pwr = info->s_info->pwr_api / 2; - else - pwr = info->pwr_api / 2; + pwr = info->pwr_state / 2; dev_dbg(info->dev, "%s PWR_RD: %d\n", __func__, pwr); - if (copy_to_user((void __user *)arg, (const void *)&pwr, - sizeof(pwr))) { + if (copy_to_user( + (void __user *)arg, (const void *)&pwr, sizeof(pwr))) { dev_err(info->dev, "%s copy_to_user err line %d\n", __func__, __LINE__); return -EFAULT; } return 0; - default: dev_err(info->dev, "%s unsupported ioctl: %x\n", __func__, cmd); @@ -1387,88 +1409,18 @@ static long max77665_f_ioctl(struct file *file, } } -static int max77665_f_sync_enable(int dev1, int dev2) -{ - struct max77665_f_info *sync1 = NULL; - struct max77665_f_info *sync2 = NULL; - struct max77665_f_info *pos = NULL; - - rcu_read_lock(); - list_for_each_entry_rcu(pos, &max77665_f_info_list, list) { - if (pos->pdata->num == dev1) { - sync1 = pos; - break; - } - } - pos = NULL; - list_for_each_entry_rcu(pos, &max77665_f_info_list, list) { - if (pos->pdata->num == dev2) { - sync2 = pos; - break; - } - } - rcu_read_unlock(); - if (sync1 != NULL) - sync1->s_info = NULL; - if (sync2 != NULL) - sync2->s_info = NULL; - if (!dev1 && !dev2) - return 0; /* no err if default instance 0's used */ - - if (dev1 == dev2) - return -EINVAL; /* err if sync instance is itself */ - - if ((sync1 != NULL) && (sync2 != NULL)) { - sync1->s_info = sync2; - sync2->s_info = sync1; - } - - return 0; -} - -static int max77665_f_sync_disable(struct max77665_f_info *info) -{ - if (info->s_info != NULL) { - info->s_info->s_mode = 0; - info->s_info->s_info = NULL; - info->s_mode = 0; - info->s_info = NULL; - return 0; - } - - return -EINVAL; -} - static int max77665_f_open(struct inode *inode, struct file *file) { - struct max77665_f_info *info = NULL; - struct max77665_f_info *pos = NULL; - int err; + struct miscdevice *miscdev = file->private_data; + struct max77665_f_info *info; - rcu_read_lock(); - list_for_each_entry_rcu(pos, &max77665_f_info_list, list) { - if (pos->miscdev.minor == iminor(inode)) { - info = pos; - break; - } - } - rcu_read_unlock(); + info = container_of(miscdev, struct max77665_f_info, miscdev); if (!info) return -ENODEV; - err = max77665_f_sync_enable(info->pdata->num, info->pdata->sync); - if (err == -EINVAL) - dev_err(info->dev, - "%s err: invalid num (%u) and sync (%u) instance\n", - __func__, info->pdata->num, info->pdata->sync); if (atomic_xchg(&info->in_use, 1)) return -EBUSY; - if (info->s_info != NULL) { - if (atomic_xchg(&info->s_info->in_use, 1)) - return -EBUSY; - } - file->private_data = info; dev_dbg(info->dev, "%s\n", __func__); return 0; @@ -1479,12 +1431,9 @@ static int max77665_f_release(struct inode *inode, struct file *file) struct max77665_f_info *info = file->private_data; dev_dbg(info->dev, "%s\n", __func__); - max77665_f_power_sync(info, NVC_PWR_OFF); + max77665_f_power_set(info, NVC_PWR_OFF); file->private_data = NULL; WARN_ON(!atomic_xchg(&info->in_use, 0)); - if (info->s_info != NULL) - WARN_ON(!atomic_xchg(&info->s_info->in_use, 0)); - max77665_f_sync_disable(info); return 0; } @@ -1546,13 +1495,8 @@ static const struct file_operations max77665_f_fileops = { static void max77665_f_del(struct max77665_f_info *info) { - max77665_f_power_sync(info, NVC_PWR_OFF); + max77665_f_power_set(info, NVC_PWR_OFF); max77665_f_power_put(&info->pwr_rail); - max77665_f_sync_disable(info); - spin_lock(&max77665_f_spinlock); - list_del_rcu(&info->list); - spin_unlock(&max77665_f_spinlock); - synchronize_rcu(); } static int max77665_f_remove(struct platform_device *pdev) @@ -1570,6 +1514,38 @@ static int max77665_f_remove(struct platform_device *pdev) static int max77665_f_debugfs_init(struct max77665_f_info *info); +static void max77665_f_caps_layout(struct max77665_f_info *info) +{ +#define MAX77665_FLASH_CAP_TIMEOUT_SIZE \ + (max77665_f_flash_cap_size + max77665_f_flash_timeout_size) +#define MAX77665_TORCH_CAP_TIMEOUT_SIZE \ + (max77665_f_torch_cap_size + max77665_f_torch_timeout_size) + void *start_ptr = (void *)info + sizeof(*info); + + info->flash_cap[0] = start_ptr; + info->flash_timeouts[0] = start_ptr + max77665_f_flash_cap_size; + + start_ptr += MAX77665_FLASH_CAP_TIMEOUT_SIZE; + info->flash_cap[1] = start_ptr; + info->flash_timeouts[1] = start_ptr + max77665_f_flash_cap_size; + + info->flash_cap_size = MAX77665_FLASH_CAP_TIMEOUT_SIZE; + + start_ptr += MAX77665_FLASH_CAP_TIMEOUT_SIZE; + info->torch_cap[0] = start_ptr; + info->torch_timeouts[0] = start_ptr + max77665_f_torch_cap_size; + + start_ptr += MAX77665_TORCH_CAP_TIMEOUT_SIZE; + info->torch_cap[1] = start_ptr; + info->torch_timeouts[1] = start_ptr + max77665_f_torch_cap_size; + + info->torch_cap_size = MAX77665_TORCH_CAP_TIMEOUT_SIZE; + dev_dbg(info->dev, "%s: %d(%d + %d), %d(%d + %d)\n", __func__, + info->flash_cap_size, max77665_f_flash_cap_size, + max77665_f_flash_timeout_size, info->torch_cap_size, + max77665_f_torch_cap_size, max77665_f_torch_timeout_size); +} + static int max77665_f_probe(struct platform_device *pdev) { struct max77665_f_info *info; @@ -1603,10 +1579,7 @@ static int max77665_f_probe(struct platform_device *pdev) } else dev_warn(&pdev->dev, "%s NO platform data\n", __func__); - info->flash_cap = (void *)info + sizeof(*info); - info->torch_cap = (void *)info->flash_cap + - max77665_f_max_flash_cap_size; - info->sustainTime = SUSTAINTIME_DEF; + max77665_f_caps_layout(info); max77665_f_update_config(info); @@ -1619,10 +1592,6 @@ static int max77665_f_probe(struct platform_device *pdev) dev_set_drvdata(info->dev, info); mutex_init(&info->mutex); - INIT_LIST_HEAD(&info->list); - spin_lock(&max77665_f_spinlock); - list_add_rcu(&info->list, &max77665_f_info_list); - spin_unlock(&max77665_f_spinlock); if (info->pdata->dev_name != NULL) strcpy(dname, info->pdata->dev_name); @@ -1664,7 +1633,7 @@ static int max77665_f_status_show(struct seq_file *s, void *data) " PinState Values = 0x%04x\n" " Max_Peak_Current = %dmA\n" , - info->pwr_dev, + info->pwr_state, info->config.led_mask, info->regs.led1_curr, info->regs.led2_curr, @@ -1732,9 +1701,9 @@ set_attr: break; case 'p': if (val) - max77665_f_power(info, NVC_PWR_ON); + max77665_f_power_set(info, NVC_PWR_ON); else - max77665_f_power(info, NVC_PWR_OFF); + max77665_f_power_set(info, NVC_PWR_OFF); break; case 'k': if (val & 0xffff) diff --git a/drivers/media/video/tegra/ov5693.c b/drivers/media/video/tegra/ov5693.c index f63d5ef365da..8c8797f9a871 100644 --- a/drivers/media/video/tegra/ov5693.c +++ b/drivers/media/video/tegra/ov5693.c @@ -110,8 +110,9 @@ static struct nvc_imager_static_nvc ov5693_dflt_sdata = { .res_chg_wait_time = OV5693_RES_CHG_WAIT_TIME_MS, }; -static const struct ov5693_reg ov5693_2592x1944_i2c[] = { +static const struct ov5693_reg ov5693_2592x1944_hdr_i2c[] = { {OV5693_TABLE_RESET, 0},/* Including sw reset */ + {0x3001, 0x0a}, {0x3002, 0x80}, {0x3006, 0x00}, @@ -128,10 +129,6 @@ static const struct ov5693_reg ov5693_2592x1944_i2c[] = { {0x3021, 0x00}, {0x3022, 0x01}, {0x3028, 0x44}, - {0x3090, 0x02}, - {0x3091, 0x0e}, - {0x3092, 0x00}, - {0x3093, 0x00}, {0x3098, 0x03}, {0x3099, 0x1e}, {0x309a, 0x02}, @@ -154,19 +151,19 @@ static const struct ov5693_reg ov5693_2592x1944_i2c[] = { {0x3405, 0x00}, {0x3406, 0x01}, {0x3500, 0x00}, - {0x3501, 0x7b}, - {0x3502, 0x00}, + {0x3501, 0x9a}, + {0x3502, 0x80}, {0x3503, 0x07}, {0x3504, 0x00}, {0x3505, 0x00}, {0x3506, 0x00}, - {0x3507, 0x02}, - {0x3508, 0x00}, - {0x3509, 0x08}, + {0x3507, 0x01}, + {0x3508, 0x80}, + {0x3509, 0x10}, {0x350a, 0x00}, {0x350b, 0x40}, {0x3601, 0x0a}, - {0x3602, 0x18}, + {0x3602, 0x38}, {0x3612, 0x80}, {0x3620, 0x54}, {0x3621, 0xc7}, @@ -209,6 +206,7 @@ static const struct ov5693_reg ov5693_2592x1944_i2c[] = { {0x371e, 0xa1}, {0x371f, 0x0c}, {0x3721, 0x00}, + {0x3724, 0x10}, {0x3726, 0x00}, {0x372a, 0x01}, {0x3730, 0x10}, @@ -235,6 +233,7 @@ static const struct ov5693_reg ov5693_2592x1944_i2c[] = { {0x3759, 0x0f}, {0x376b, 0x44}, {0x375c, 0x04}, + {0x3774, 0x10}, {0x3776, 0x00}, {0x377f, 0x08}, {0x3780, 0x22}, @@ -271,10 +270,10 @@ static const struct ov5693_reg ov5693_2592x1944_i2c[] = { {0x3809, 0x20}, {0x380a, 0x07}, {0x380b, 0x98}, - {0x380c, 0x0a}, - {0x380d, 0x80}, - {0x380e, 0x07}, - {0x380f, 0xc0}, + {0x380c, 0x0e}, + {0x380d, 0x40}, + {0x380e, 0x09}, + {0x380f, 0xb4}, {0x3810, 0x00}, {0x3811, 0x02}, {0x3812, 0x00}, @@ -282,15 +281,15 @@ static const struct ov5693_reg ov5693_2592x1944_i2c[] = { {0x3814, 0x11}, {0x3815, 0x11}, {0x3820, 0x00}, - {0x3821, 0x1e}, + {0x3821, 0x9e}, {0x3823, 0x00}, {0x3824, 0x00}, {0x3825, 0x00}, {0x3826, 0x00}, {0x3827, 0x00}, {0x382a, 0x04}, - {0x3a04, 0x06}, - {0x3a05, 0x14}, + {0x3a04, 0x09}, + {0x3a05, 0xa9}, {0x3a06, 0x00}, {0x3a07, 0xfe}, {0x3b00, 0x00}, @@ -298,25 +297,6 @@ static const struct ov5693_reg ov5693_2592x1944_i2c[] = { {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}, @@ -325,10 +305,13 @@ static const struct ov5693_reg ov5693_2592x1944_i2c[] = { {0x4005, 0x18}, {0x4006, 0x20}, {0x4008, 0x24}, - {0x4009, 0x10}, + {0x4009, 0x40}, {0x400c, 0x00}, {0x400d, 0x00}, {0x4058, 0x00}, + {0x404e, 0x37}, + {0x404f, 0x8f}, + {0x4058, 0x00}, {0x4101, 0xb2}, {0x4303, 0x00}, {0x4304, 0x08}, @@ -382,9 +365,9 @@ static const struct ov5693_reg ov5693_2592x1944_i2c[] = { {OV5693_TABLE_END, 0x0000} }; -static const struct ov5693_reg ov5693_1296x972_i2c[] = { +static const struct ov5693_reg ov5693_1296x972_hdr_i2c[] = { {OV5693_TABLE_RESET, 0},/* Including sw reset */ - {0x0103, 0x01}, + {0x3001, 0x0a}, {0x3002, 0x80}, {0x3006, 0x00}, @@ -423,15 +406,15 @@ static const struct ov5693_reg ov5693_1296x972_i2c[] = { {0x3405, 0x00}, {0x3406, 0x01}, {0x3500, 0x00}, - {0x3501, 0x7b}, + {0x3501, 0x72}, {0x3502, 0x00}, {0x3503, 0x07}, {0x3504, 0x00}, {0x3505, 0x00}, {0x3506, 0x00}, - {0x3507, 0x02}, - {0x3508, 0x00}, - {0x3509, 0x08}, + {0x3507, 0x01}, + {0x3508, 0x80}, + {0x3509, 0x10}, {0x350a, 0x00}, {0x350b, 0x40}, {0x3601, 0x0a}, @@ -463,7 +446,7 @@ static const struct ov5693_reg ov5693_1296x972_i2c[] = { {0x3703, 0xd8}, {0x3704, 0x78}, {0x3705, 0x02}, - {0x3708, 0xe6}, + {0x3708, 0xe2}, {0x3709, 0xc3}, {0x370a, 0x00}, {0x370b, 0x20}, @@ -530,38 +513,38 @@ static const struct ov5693_reg ov5693_1296x972_i2c[] = { {0x37cb, 0x00}, {0x37de, 0x00}, {0x37df, 0x00}, - {0x3800, 0x00}, - {0x3801, 0x00}, - {0x3802, 0x00}, - {0x3803, 0x00}, - {0x3804, 0x0a}, - {0x3805, 0x3f}, - {0x3806, 0x07}, - {0x3807, 0xa3}, + {0x3800, 0x02}, + {0x3801, 0xa0}, + {0x3802, 0x01}, + {0x3803, 0xe8}, + {0x3804, 0x07}, + {0x3805, 0xb7}, + {0x3806, 0x05}, + {0x3807, 0xb9}, {0x3808, 0x05}, {0x3809, 0x10}, {0x380a, 0x03}, {0x380b, 0xcc}, - {0x380c, 0x0a}, - {0x380d, 0x80}, + {0x380c, 0x0b}, + {0x380d, 0x40}, {0x380e, 0x07}, - {0x380f, 0xc0}, + {0x380f, 0x3a}, {0x3810, 0x00}, {0x3811, 0x02}, {0x3812, 0x00}, {0x3813, 0x02}, - {0x3814, 0x31}, - {0x3815, 0x31}, - {0x3820, 0x01}, - {0x3821, 0x1f}, + {0x3814, 0x11}, + {0x3815, 0x11}, + {0x3820, 0x00}, + {0x3821, 0x9e}, {0x3823, 0x00}, {0x3824, 0x00}, {0x3825, 0x00}, {0x3826, 0x00}, {0x3827, 0x00}, {0x382a, 0x04}, - {0x3a04, 0x06}, - {0x3a05, 0x14}, + {0x3a04, 0x09}, + {0x3a05, 0xa9}, {0x3a06, 0x00}, {0x3a07, 0xfe}, {0x3b00, 0x00}, @@ -633,11 +616,13 @@ static const struct ov5693_reg ov5693_1296x972_i2c[] = { {0x5e00, 0x00}, {0x5e10, 0x0c}, {0x0100, 0x01}, + {OV5693_TABLE_END, 0x0000} }; -static const struct ov5693_reg ov5693_1920x1080_i2c[] = { +static const struct ov5693_reg ov5693_1920x1080_hdr_i2c[] = { {OV5693_TABLE_RESET, 0x0},/*, 0xIncluding, 0xsw, 0xreset, 0x*/ + {0x3001, 0x0a}, {0x3002, 0x80}, {0x3006, 0x00}, @@ -676,15 +661,15 @@ static const struct ov5693_reg ov5693_1920x1080_i2c[] = { {0x3405, 0x00}, {0x3406, 0x01}, {0x3500, 0x00}, - {0x3501, 0x7b}, + {0x3501, 0x72}, {0x3502, 0x00}, {0x3503, 0x07}, {0x3504, 0x00}, {0x3505, 0x00}, {0x3506, 0x00}, - {0x3507, 0x02}, - {0x3508, 0x00}, - {0x3509, 0x08}, + {0x3507, 0x01}, + {0x3508, 0x80}, + {0x3509, 0x10}, {0x350a, 0x00}, {0x350b, 0x40}, {0x3601, 0x0a}, @@ -783,22 +768,22 @@ static const struct ov5693_reg ov5693_1920x1080_i2c[] = { {0x37cb, 0x00}, {0x37de, 0x00}, {0x37df, 0x00}, - {0x3800, 0x00}, - {0x3801, 0x00}, - {0x3802, 0x00}, - {0x3803, 0xf8}, - {0x3804, 0x0a}, - {0x3805, 0x3f}, - {0x3806, 0x06}, - {0x3807, 0xab}, + {0x3800, 0x01}, + {0x3801, 0x70}, + {0x3802, 0x01}, + {0x3803, 0xbc}, + {0x3804, 0x09}, + {0x3805, 0x0f}, + {0x3806, 0x05}, + {0x3807, 0xff}, {0x3808, 0x07}, {0x3809, 0x80}, {0x380a, 0x04}, {0x380b, 0x38}, - {0x380c, 0x0a}, - {0x380d, 0x80}, + {0x380c, 0x0b}, + {0x380d, 0x40}, {0x380e, 0x07}, - {0x380f, 0xc0}, + {0x380f, 0x3a}, {0x3810, 0x00}, {0x3811, 0x02}, {0x3812, 0x00}, @@ -806,15 +791,15 @@ static const struct ov5693_reg ov5693_1920x1080_i2c[] = { {0x3814, 0x11}, {0x3815, 0x11}, {0x3820, 0x00}, - {0x3821, 0x1e}, + {0x3821, 0x9e}, {0x3823, 0x00}, {0x3824, 0x00}, {0x3825, 0x00}, {0x3826, 0x00}, {0x3827, 0x00}, {0x382a, 0x04}, - {0x3a04, 0x06}, - {0x3a05, 0x14}, + {0x3a04, 0x09}, + {0x3a05, 0xa9}, {0x3a06, 0x00}, {0x3a07, 0xfe}, {0x3b00, 0x00}, @@ -859,7 +844,7 @@ static const struct ov5693_reg ov5693_1920x1080_i2c[] = { {0x4837, 0x0a}, {0x5000, 0x06}, {0x5001, 0x01}, - {0x5002, 0x80}, + {0x5002, 0x00}, {0x5003, 0x20}, {0x5046, 0x0a}, {0x5013, 0x00}, @@ -890,8 +875,9 @@ static const struct ov5693_reg ov5693_1920x1080_i2c[] = { }; -static const struct ov5693_reg ov5693_1280x720_120fps_i2c[] = { +static const struct ov5693_reg ov5693_1280x720_hdr_i2c[] = { {OV5693_TABLE_RESET, 0},/* Including sw reset */ + {0x3001, 0x0a}, {0x3002, 0x80}, {0x3006, 0x00}, @@ -930,15 +916,15 @@ static const struct ov5693_reg ov5693_1280x720_120fps_i2c[] = { {0x3405, 0x00}, {0x3406, 0x01}, {0x3500, 0x00}, - {0x3501, 0x2e}, - {0x3502, 0x80}, + {0x3501, 0x39}, + {0x3502, 0x00}, {0x3503, 0x07}, {0x3504, 0x00}, {0x3505, 0x00}, {0x3506, 0x00}, - {0x3507, 0x02}, - {0x3508, 0x00}, - {0x3509, 0x08}, + {0x3507, 0x01}, + {0x3508, 0x80}, + {0x3509, 0x10}, {0x350a, 0x00}, {0x350b, 0x40}, {0x3601, 0x0a}, @@ -970,8 +956,8 @@ static const struct ov5693_reg ov5693_1280x720_120fps_i2c[] = { {0x3703, 0xd8}, {0x3704, 0x78}, {0x3705, 0x02}, - {0x3708, 0xe6}, - {0x3709, 0xc7}, + {0x3708, 0xe2}, + {0x3709, 0xc3}, {0x370a, 0x00}, {0x370b, 0x20}, {0x370c, 0x0c}, @@ -1037,38 +1023,38 @@ static const struct ov5693_reg ov5693_1280x720_120fps_i2c[] = { {0x37cb, 0x00}, {0x37de, 0x00}, {0x37df, 0x00}, - {0x3800, 0x00}, - {0x3801, 0x00}, - {0x3802, 0x00}, - {0x3803, 0xf4}, - {0x3804, 0x0a}, - {0x3805, 0x3f}, - {0x3806, 0x06}, - {0x3807, 0xab}, + {0x3800, 0x02}, + {0x3801, 0xa8}, + {0x3802, 0x02}, + {0x3803, 0x68}, + {0x3804, 0x07}, + {0x3805, 0xb7}, + {0x3806, 0x05}, + {0x3807, 0x3b}, {0x3808, 0x05}, {0x3809, 0x00}, {0x380a, 0x02}, {0x380b, 0xd0}, - {0x380c, 0x06}, - {0x380d, 0xd8}, - {0x380e, 0x02}, - {0x380f, 0xf8}, + {0x380c, 0x0b}, + {0x380d, 0x40}, + {0x380e, 0x03}, + {0x380f, 0x9e}, {0x3810, 0x00}, {0x3811, 0x02}, {0x3812, 0x00}, {0x3813, 0x02}, - {0x3814, 0x31}, - {0x3815, 0x31}, - {0x3820, 0x04}, - {0x3821, 0x1f}, + {0x3814, 0x11}, + {0x3815, 0x11}, + {0x3820, 0x00}, + {0x3821, 0x9e}, {0x3823, 0x00}, {0x3824, 0x00}, {0x3825, 0x00}, {0x3826, 0x00}, {0x3827, 0x00}, {0x382a, 0x04}, - {0x3a04, 0x06}, - {0x3a05, 0x14}, + {0x3a04, 0x09}, + {0x3a05, 0xa9}, {0x3a06, 0x00}, {0x3a07, 0xfe}, {0x3b00, 0x00}, @@ -1140,22 +1126,22 @@ static const struct ov5693_reg ov5693_1280x720_120fps_i2c[] = { {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, + OV5693_MODE_2592x1944_HDR = 0, + OV5693_MODE_1920x1080_HDR, + OV5693_MODE_1296x972_HDR, + OV5693_MODE_1280x720_HDR, }; 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, + [OV5693_MODE_2592x1944_HDR] = ov5693_2592x1944_hdr_i2c, + [OV5693_MODE_1920x1080_HDR] = ov5693_1920x1080_hdr_i2c, + [OV5693_MODE_1296x972_HDR] = ov5693_1296x972_hdr_i2c, + [OV5693_MODE_1280x720_HDR] = ov5693_1280x720_hdr_i2c, }; static int ov5693_i2c_rd8(struct ov5693_info *info, u16 reg, u8 *val) @@ -1241,8 +1227,9 @@ static inline int ov5693_frame_length_reg(struct ov5693_reg *regs, } static inline int ov5693_coarse_time_reg(struct ov5693_reg *regs, - u32 coarse_time) + u32 coarse_time, u32 coarse_time_short) { + int ret = 0; regs->addr = 0x3500; regs->val = (coarse_time >> 12) & 0xff; (regs + 1)->addr = 0x3501; @@ -1250,7 +1237,20 @@ static inline int ov5693_coarse_time_reg(struct ov5693_reg *regs, (regs + 2)->addr = 0x3502; (regs + 2)->val = (coarse_time & 0xf) << 4; - return 3; + ret += 3; + + if (coarse_time_short != OV5693_INVALID_COARSE_TIME) { + regs->addr = 0x3506; + regs->val = (coarse_time_short >> 12) & 0xff; + (regs + 1)->addr = 0x3507; + (regs + 1)->val = (coarse_time_short >> 4) & 0xff; + (regs + 2)->addr = 0x3508; + (regs + 2)->val = (coarse_time_short & 0xf) << 4; + + ret += 3; + } + + return ret; } #define OV5693_ENTER_GROUP_HOLD(group_hold) \ @@ -1294,14 +1294,17 @@ static int ov5693_set_frame_length(struct ov5693_info *info, } static int ov5693_set_coarse_time(struct ov5693_info *info, - u32 coarse_time, bool group_hold) + u32 coarse_time, u32 coarse_time_short, + 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); + offset += ov5693_coarse_time_reg(reg_list + offset, + coarse_time, + coarse_time_short); OV5693_LEAVE_GROUP_HOLD(group_hold); reg_list[offset].addr = OV5693_TABLE_END; @@ -1343,7 +1346,9 @@ static int ov5693_exposure_wr(struct ov5693_info *info, 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_coarse_time_reg(reg_list + offset, + mode->coarse_time, + mode->coarse_time_short); offset += ov5693_gain_reg(reg_list + offset, mode->gain); OV5693_LEAVE_GROUP_HOLD(group_hold); @@ -1389,7 +1394,7 @@ static int ov5693_set_group_hold(struct ov5693_info *info, ae->frame_length); if (ae->coarse_time_enable) offset += ov5693_coarse_time_reg(reg_list + offset, - ae->coarse_time); + ae->coarse_time, OV5693_INVALID_COARSE_TIME); OV5693_LEAVE_GROUP_HOLD(group_hold); reg_list[offset].addr = OV5693_TABLE_END; @@ -1406,7 +1411,7 @@ static int ov5693_gpio_rd(struct ov5693_info *info, 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); + info->gpio[type].gpio, val); if (!info->gpio[type].active_high) val = !val; val &= 1; @@ -1427,7 +1432,7 @@ static int ov5693_gpio_wr(struct ov5693_info *info, 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); + info->gpio[type].gpio, val); } return err; /* return value written or error */ } @@ -1712,23 +1717,19 @@ static int ov5693_set_mode(struct ov5693_info *info, } if (mode->res_x == 2592 && mode->res_y == 1944) - mode_index = OV5693_MODE_2592x1944; + mode_index = OV5693_MODE_2592x1944_HDR; else if (mode->res_x == 1296 && mode->res_y == 972) - mode_index = OV5693_MODE_1296x972; + mode_index = OV5693_MODE_1296x972_HDR; else if (mode->res_x == 1920 && mode->res_y == 1080) - mode_index = OV5693_MODE_1920x1080; + mode_index = OV5693_MODE_1920x1080_HDR; else if (mode->res_x == 1280 && mode->res_y == 720) - mode_index = OV5693_MODE_1280x720_120FPS; + mode_index = OV5693_MODE_1280x720_HDR; 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; @@ -1801,7 +1802,19 @@ static long ov5693_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return ov5693_set_frame_length(info, (u32)arg, true); case OV5693_IOCTL_SET_COARSE_TIME: - return ov5693_set_coarse_time(info, (u32)arg, true); + return ov5693_set_coarse_time(info, (u32)arg, + OV5693_INVALID_COARSE_TIME, true); + + case OV5693_IOCTL_SET_HDR_COARSE_TIME: + { + struct ov5693_hdr *hdrcoarse = (struct ov5693_hdr *)arg; + int ret = ov5693_set_coarse_time(info, + hdrcoarse->coarse_time_long, + hdrcoarse->coarse_time_short, + false); + return ret; + } + case OV5693_IOCTL_SET_GAIN: return ov5693_set_gain(info, (u32)arg, true); |