diff options
-rw-r--r-- | drivers/media/video/tegra/Kconfig | 7 | ||||
-rw-r--r-- | drivers/media/video/tegra/Makefile | 2 | ||||
-rw-r--r-- | drivers/media/video/tegra/ar0832_focuser.c | 230 | ||||
-rw-r--r-- | drivers/media/video/tegra/ar0832_main.c | 1171 | ||||
-rw-r--r-- | include/media/ar0832_focuser.h | 56 | ||||
-rw-r--r-- | include/media/ar0832_main.h | 91 |
6 files changed, 1557 insertions, 0 deletions
diff --git a/drivers/media/video/tegra/Kconfig b/drivers/media/video/tegra/Kconfig index afcbec6b25ab..5b6ffd50ad25 100644 --- a/drivers/media/video/tegra/Kconfig +++ b/drivers/media/video/tegra/Kconfig @@ -24,6 +24,13 @@ config VIDEO_OV2710 This is a driver for the Omnivision OV2710 camera sensor for use with the tegra isp. +config VIDEO_AR0832 + tristate "AR0832 camera sensor support" + depends on I2C && ARCH_TEGRA + ---help--- + This is a driver for the AR0832 camera sensor + for use with the tegra isp. + config VIDEO_SOC380 tristate "SOC380 camera sensor support" depends on I2C && ARCH_TEGRA diff --git a/drivers/media/video/tegra/Makefile b/drivers/media/video/tegra/Makefile index 872a3ce0d30a..c49c458f1760 100644 --- a/drivers/media/video/tegra/Makefile +++ b/drivers/media/video/tegra/Makefile @@ -4,6 +4,8 @@ obj-y += avp/ obj-$(CONFIG_TEGRA_MEDIASERVER) += mediaserver/ obj-$(CONFIG_TEGRA_CAMERA) += tegra_camera.o +obj-$(CONFIG_VIDEO_AR0832) += ar0832_main.o +obj-$(CONFIG_VIDEO_AR0832) += ar0832_focuser.o obj-$(CONFIG_VIDEO_OV5650) += ov5650.o obj-$(CONFIG_VIDEO_OV2710) += ov2710.o obj-$(CONFIG_VIDEO_SOC380) += soc380.o diff --git a/drivers/media/video/tegra/ar0832_focuser.c b/drivers/media/video/tegra/ar0832_focuser.c new file mode 100644 index 000000000000..26c03c981e3f --- /dev/null +++ b/drivers/media/video/tegra/ar0832_focuser.c @@ -0,0 +1,230 @@ +/* +* ar0832_focuser.c - focuser driver +* +* Copyright (c) 2011, NVIDIA, All Rights Reserved. +* +* This file is licensed under the terms of the GNU General Public License +* version 2. This program is licensed "as is" without any warranty of any +* kind, whether express or implied. +*/ + +#include <linux/delay.h> +#include <linux/fs.h> +#include <linux/i2c.h> +#include <linux/miscdevice.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <media/ar0832_focuser.h> + + +#define POS_LOW 50 +#define POS_HIGH 1000 +#define SETTLETIME_MS 100 +#define FOCAL_LENGTH (3.5f) +#define FNUMBER (2.8f) +#define FPOS_COUNT 1024 +DEFINE_MUTEX(star_focuser_lock); +#define DW9716_MAX_RETRIES (3) + +static int ar0832_focuser_write(struct i2c_client *client, u16 value) +{ + int count; + struct i2c_msg msg[1]; + unsigned char data[2]; + int retry = 0; + + if (!client->adapter) + return -ENODEV; + + data[0] = (u8) ((value >> 4) & 0x3F); + data[1] = (u8) ((value & 0xF) << 4); + /* Slew rate control (8 steps, 50us) */ + data[1] = (data[1] & 0xF0) | 0x05; + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = ARRAY_SIZE(data); + msg[0].buf = data; + + do { + count = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + if (count == ARRAY_SIZE(msg)) + return 0; + retry++; + pr_err("ar0832_focuser: i2c transfer failed, retrying %x\n", + value); + usleep_range(3000, 3500); + } while (retry <= DW9716_MAX_RETRIES); + return -EIO; +} + +static int ar0832_focuser_write_helper( + struct ar0832_focuser_info *info, u16 value) +{ + int ret; + switch (info->camera_mode) { + case MAIN: + case LEFT_ONLY: + ret = ar0832_focuser_write(info->i2c_client, value); + break; + case STEREO: + ret = ar0832_focuser_write(info->i2c_client, value); + ret = ar0832_focuser_write(info->i2c_client_right, value); + break; + case RIGHT_ONLY: + ret = ar0832_focuser_write(info->i2c_client_right, value); + break; + default: + return -1; + } + return ret; +} +static int ar0832_focuser_set_position( + struct ar0832_focuser_info *info, u32 position) +{ + if (position < info->config.pos_low || + position > info->config.pos_high) + return -EINVAL; + + return ar0832_focuser_write(info->i2c_client, position); +} + +static long ar0832_focuser_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct ar0832_focuser_info *info = file->private_data; + int ret; + switch (cmd) { + case AR0832_FOCUSER_IOCTL_GET_CONFIG: + { + if (copy_to_user((void __user *) arg, + &info->config, + sizeof(info->config))) { + pr_err("%s: 0x%x\n", __func__, __LINE__); + return -EFAULT; + } + + break; + } + case AR0832_FOCUSER_IOCTL_SET_POSITION: + mutex_lock(&star_focuser_lock); + ret = ar0832_focuser_set_position(info, (u32) arg); + mutex_unlock(&star_focuser_lock); + return ret; + case AR0832_FOCUSER_IOCTL_SET_MODE: + info->camera_mode = (enum StereoCameraMode)arg; + break; + default: + return -EINVAL; + } + + return 0; +} + +static struct ar0832_focuser_info *info; + +static int ar0832_focuser_open(struct inode *inode, struct file *file) +{ + pr_info("ar0832_focuser: open!\n"); + file->private_data = info; + return 0; +} + +int ar0832_focuser_release(struct inode *inode, struct file *file) +{ + pr_info("ar0832_focuser: release!\n"); + file->private_data = NULL; + return 0; +} + + +static const struct file_operations ar0832_focuser_fileops = { + .owner = THIS_MODULE, + .open = ar0832_focuser_open, + .unlocked_ioctl = ar0832_focuser_ioctl, + .release = ar0832_focuser_release, +}; + +static struct miscdevice ar0832_focuser_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "ar0832_focuser", + .fops = &ar0832_focuser_fileops, +}; + +static int ar0832_focuser_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err; + + pr_info("ar0832_focuser: probing sensor.\n"); + + info = kzalloc(sizeof(struct ar0832_focuser_info), GFP_KERNEL); + if (!info) { + pr_err("ar0832_focuser: Unable to allocate memory!\n"); + return -ENOMEM; + } + + err = misc_register(&ar0832_focuser_device); + if (err) { + pr_err("ar0832_focuser: Unable to register misc device!\n"); + kfree(info); + return err; + } + + info->regulator = 0; + info->i2c_client = client; + info->config.settle_time = SETTLETIME_MS; + /* FIX-ME */ + /* + focuser_info->config.focal_length = FOCAL_LENGTH; + focuser_info->config.fnumber = FNUMBER; + */ + info->config.pos_low = POS_LOW; + info->config.pos_high = POS_HIGH; + i2c_set_clientdata(client, info); + + return 0; +} + +static int ar0832_focuser_remove(struct i2c_client *client) +{ + struct ar0832_focuser_info *info; + info = i2c_get_clientdata(client); + misc_deregister(&ar0832_focuser_device); + kfree(info); + return 0; +} + +static const struct i2c_device_id ar0832_focuser_id[] = { + { "ar0832_focuser", 0 }, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, ar0832_focuser_id); + +static struct i2c_driver ar0832_focuser_i2c_driver = { + .driver = { + .name = "ar0832_focuser", + .owner = THIS_MODULE, + }, + .probe = ar0832_focuser_probe, + .remove = ar0832_focuser_remove, + .id_table = ar0832_focuser_id, +}; + +static int __init ar0832_focuser_init(void) +{ + pr_info("ar0832_focuser sensor driver loading\n"); + i2c_add_driver(&ar0832_focuser_i2c_driver); + + return 0; +} + +static void __exit ar0832_focuser_exit(void) +{ + i2c_del_driver(&ar0832_focuser_i2c_driver); +} + +module_init(ar0832_focuser_init); +module_exit(ar0832_focuser_exit); diff --git a/drivers/media/video/tegra/ar0832_main.c b/drivers/media/video/tegra/ar0832_main.c new file mode 100644 index 000000000000..c0802d56e636 --- /dev/null +++ b/drivers/media/video/tegra/ar0832_main.c @@ -0,0 +1,1171 @@ +/* +* ar0832_main.c - Aptina AR0832 8M Bayer type sensor driver +* +* Copyright (c) 2011, NVIDIA, All Rights Reserved. +* +* This file is licensed under the terms of the GNU General Public License +* version 2. This program is licensed "as is" without any warranty of any +* kind, whether express or implied. +*/ + +#include <linux/delay.h> +#include <linux/fs.h> +#include <linux/i2c.h> +#include <linux/miscdevice.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <media/ar0832_main.h> +#include <media/ar0832_focuser.h> +#include <mach/hardware.h> +#include <linux/gpio.h> + +DEFINE_MUTEX(ar0832_camera_lock); + +#define AR0832_RESET TEGRA_GPIO_PD2 +#define AR0832_PWRDN TEGRA_GPIO_PD5 +#define USE_I2C_DATA_2BYTES + +#define POS_LOW 50 +#define POS_HIGH 1000 +#define SETTLETIME_MS 100 +#define FOCAL_LENGTH (3.5f) +#define FNUMBER (2.8f) +#define FPOS_COUNT 1024 + +struct ar0832_info { + int mode; + struct i2c_client *i2c_client; + struct i2c_client *i2c_client_right; + struct ar0832_platform_data *pdata; +}; + +static struct ar0832_info *info; +static struct ar0832_focuser_info *focuser_info; + +/* stereo */ +static u16 DefaultImageWidth = 1200; +static u16 DefaultImageHeight = 680; +#define UpperByte16to8(x) ((u8)((x&0xFF00)>>8)) +#define LowerByte16to8(x) ((u8)(x&0x00FF)) + +#define ar0832_TABLE_WAIT_MS 0 +#define ar0832_TABLE_END 1 +#define ar0832_MAX_RETRIES 3 +#define USE_CLOCK_MAXIMUM 1 +#define USE_DIGITAL_GAIN 0 +#define USE_BINNING_MODE 1 + +static struct ar0832_reg mode_start[] = { + {ar0832_TABLE_END, 0x0000} +}; + +static struct ar0832_reg mode_3264X2448[] = { + {0x301A, 0x0058}, /* RESET_REGISTER */ + {0x301A, 0x0050}, /* RESET_REGISTER */ + {0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */ + {0x3064, 0x7800}, /* RESERVED_MFR_3064 */ + {0x31AE, 0x0202}, /* SERIAL_FORMAT */ + {0x31B0, 0x0083}, /* FRAME_PREAMBLE */ + {0x31B2, 0x004D}, /* LINE_PREAMBLE */ + {0x31B4, 0x0E77}, /* MIPI_TIMING_0 */ + {0x31B6, 0x0D20}, /* MIPI_TIMING_1 */ + {0x31B8, 0x020E}, /* MIPI_TIMING_2 */ + {0x31BA, 0x0710}, /* MIPI_TIMING_3 */ + {0x31BC, 0x2A0D}, /* MIPI_TIMING_4 */ + {ar0832_TABLE_WAIT_MS, 0x0005}, + {0x0112, 0x0A0A}, /* LG_CHANGE CCP_DATA_FORMAT */ + + {0x3044, 0x0590}, /* RESERVED_MFR_3044 */ + {0x306E, 0xFC80}, /* DATAPATH_SELECT */ + {0x30B2, 0xC000}, /* RESERVED_MFR_30B2 */ + {0x30D6, 0x0800}, /* RESERVED_MFR_30D6 */ + {0x316C, 0xB42F}, /* RESERVED_MFR_316C */ + {0x316E, 0x869A}, /* RESERVED_MFR_316E */ + {0x3170, 0x210E}, /* RESERVED_MFR_3170 */ + {0x317A, 0x010E}, /* RESERVED_MFR_317A */ + {0x31E0, 0x1FB9}, /* RESERVED_MFR_31E0 */ + {0x31E6, 0x07FC}, /* RESERVED_MFR_31E6 */ + {0x37C0, 0x0000}, /* P_GR_Q5 */ + {0x37C2, 0x0000}, /* P_RD_Q5 */ + {0x37C4, 0x0000}, /* P_BL_Q5 */ + {0x37C6, 0x0000}, /* P_GB_Q5 */ + {0x3E00, 0x0011}, /* RESERVED_MFR_3E00 */ + {0x3E02, 0x8801}, /* RESERVED_MFR_3E02 */ + {0x3E04, 0x2801}, /* RESERVED_MFR_3E04 */ + {0x3E06, 0x8449}, /* RESERVED_MFR_3E06 */ + {0x3E08, 0x6841}, /* RESERVED_MFR_3E08 */ + {0x3E0A, 0x400C}, /* RESERVED_MFR_3E0A */ + {0x3E0C, 0x1001}, /* RESERVED_MFR_3E0C */ + {0x3E0E, 0x2603}, /* RESERVED_MFR_3E0E */ + {0x3E10, 0x4B41}, /* RESERVED_MFR_3E10 */ + {0x3E12, 0x4B24}, /* RESERVED_MFR_3E12 */ + {0x3E14, 0xA3CF}, /* RESERVED_MFR_3E14 */ + {0x3E16, 0x8802}, /* RESERVED_MFR_3E16 */ + {0x3E18, 0x84FF}, /* LG_CHANGE 0x8401 RESERVED_MFR_3E18 */ + {0x3E1A, 0x8601}, /* RESERVED_MFR_3E1A */ + {0x3E1C, 0x8401}, /* RESERVED_MFR_3E1C */ + {0x3E1E, 0x840A}, /* RESERVED_MFR_3E1E */ + {0x3E20, 0xFF00}, /* RESERVED_MFR_3E20 */ + {0x3E22, 0x8401}, /* RESERVED_MFR_3E22 */ + {0x3E24, 0x00FF}, /* RESERVED_MFR_3E24 */ + {0x3E26, 0x0088}, /* RESERVED_MFR_3E26 */ + {0x3E28, 0x2E8A}, /* RESERVED_MFR_3E28 */ + {0x3E30, 0x0000}, /* RESERVED_MFR_3E30 */ + {0x3E32, 0x8801}, /* RESERVED_MFR_3E32 */ + {0x3E34, 0x4029}, /* RESERVED_MFR_3E34 */ + {0x3E36, 0x00FF}, /* RESERVED_MFR_3E36 */ + {0x3E38, 0x8469}, /* RESERVED_MFR_3E38 */ + {0x3E3A, 0x00FF}, /* RESERVED_MFR_3E3A */ + {0x3E3C, 0x2801}, /* RESERVED_MFR_3E3C */ + {0x3E3E, 0x3E2A}, /* RESERVED_MFR_3E3E */ + {0x3E40, 0x1C01}, /* RESERVED_MFR_3E40 */ + {0x3E42, 0xFF84}, /* RESERVED_MFR_3E42 */ + {0x3E44, 0x8401}, /* RESERVED_MFR_3E44 */ + {0x3E46, 0x0C01}, /* RESERVED_MFR_3E46 */ + {0x3E48, 0x8401}, /* RESERVED_MFR_3E48 */ + {0x3E4A, 0x00FF}, /* RESERVED_MFR_3E4A */ + {0x3E4C, 0x8402}, /* RESERVED_MFR_3E4C */ + {0x3E4E, 0x8984}, /* RESERVED_MFR_3E4E */ + {0x3E50, 0x6628}, /* RESERVED_MFR_3E50 */ + {0x3E52, 0x8340}, /* RESERVED_MFR_3E52 */ + {0x3E54, 0x00FF}, /* RESERVED_MFR_3E54 */ + {0x3E56, 0x4A42}, /* RESERVED_MFR_3E56 */ + {0x3E58, 0x2703}, /* RESERVED_MFR_3E58 */ + {0x3E5A, 0x6752}, /* RESERVED_MFR_3E5A */ + {0x3E5C, 0x3F2A}, /* RESERVED_MFR_3E5C */ + {0x3E5E, 0x846A}, /* RESERVED_MFR_3E5E */ + {0x3E60, 0x4C01}, /* RESERVED_MFR_3E60 */ + {0x3E62, 0x8401}, /* RESERVED_MFR_3E62 */ + {0x3E66, 0x3901}, /* RESERVED_MFR_3E66 */ + {0x3E90, 0x2C01}, /* RESERVED_MFR_3E90 */ + {0x3E98, 0x2B02}, /* RESERVED_MFR_3E98 */ + {0x3E92, 0x2A04}, /* RESERVED_MFR_3E92 */ + {0x3E94, 0x2509}, /* RESERVED_MFR_3E94 */ + {0x3E96, 0x0000}, /* RESERVED_MFR_3E96 */ + {0x3E9A, 0x2905}, /* RESERVED_MFR_3E9A */ + {0x3E9C, 0x00FF}, /* RESERVED_MFR_3E9C */ + {0x3ECC, 0x00EB}, /* RESERVED_MFR_3ECC */ + {0x3ED0, 0x1E24}, /* RESERVED_MFR_3ED0 */ + {0x3ED4, 0xAFC4}, /* RESERVED_MFR_3ED4 */ + {0x3ED6, 0x909B}, /* RESERVED_MFR_3ED6 */ + {0x3EE0, 0x2424}, /* RESERVED_MFR_3EE0 */ + {0x3EE2, 0x9797}, /* RESERVED_MFR_3EE2 */ + {0x3EE4, 0xC100}, /* RESERVED_MFR_3EE4 */ + {0x3EE6, 0x0540}, /* RESERVED_MFR_3EE6 */ + {0x3174, 0x8000}, /* RESERVED_MFR_3174 */ + {0x0300, 0x0004}, /* VT_PIX_CLK_DIV */ + {0x0302, 0x0001}, /* VT_SYS_CLK_DIV */ + {0x0304, 0x0002}, /* PRE_PLL_CLK_DIV */ + {0x0306, 0x0040}, /* PLL_MULTIPLIER */ + {0x0308, 0x000A}, /* OP_PIX_CLK_DIV */ + {0x030A, 0x0001}, /* OP_SYS_CLK_DIV */ + {0x3064, 0x7400}, /* RESERVED_MFR_3064 */ + {0x0344, 0x0004}, /* X_ADDR_START */ + {0x0348, 0x0CCB}, /* X_ADDR_END */ + {0x0346, 0x0004}, /* Y_ADDR_START */ + {0x034A, 0x099B}, /* Y_ADDR_END */ + {0x034C, 0x0CC8}, /* X_OUTPUT_SIZE */ + {0x034E, 0x0998}, /* Y_OUTPUT_SIZE */ + {0x3040, 0xC041}, /* READ_MODE */ + {0x306E, 0xFC80}, /* DATAPATH_SELECT */ + {0x0400, 0x0000}, /* SCALING_MODE */ + {0x0404, 0x0010}, /* SCALE_M */ + {0x3178, 0x0000}, /* RESERVED_MFR_3178 */ + {0x3ED0, 0x1E24}, /* RESERVED_MFR_3ED0 */ + {0x0400, 0x0000}, /* SCALING_MODE */ + {0x0404, 0x0010}, /* SCALE_M */ + {0x0342, 0x133C}, /* LINE_LENGTH_PCK */ + {0x0340, 0x0A27}, /* FRAME_LENGTH_LINES */ + {0x0202, 0x0A27}, /* COARSE_INTEGRATION_TIME */ + {0x3014, 0x09DC}, /* FINE_INTEGRATION_TIME_ */ + {0x3010, 0x0078}, /* FINE_CORRECTION */ + {0x301A, 0x8250}, /* RESET_REGISTER */ + {0x301A, 0x8650}, /* RESET_REGISTER */ + {0x301A, 0x8658}, /* RESET_REGISTER */ + /* gain */ + {0x3056, 0x10AA}, /* gain */ + {0x3058, 0x10AA}, /* gain */ + {0x305a, 0x10AA}, /* gain */ + {0x305c, 0x10AA}, /* gain */ + {0x0104, 0x0000}, /* GROUPED_PARAMETER_HOLD */ + {0x301A, 0x065C}, /* RESET_REGISTER */ + {ar0832_TABLE_END, 0x0000} +}; + +static struct ar0832_reg mode_2880X1620[] = { + {0x301A, 0x0058}, /* RESET_REGISTER */ + {0x301A, 0x0050}, /* RESET_REGISTER */ + {0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */ + {0x3064, 0x7800}, /* RESERVED_MFR_3064 */ + {0x31AE, 0x0202}, /* SERIAL_FORMAT */ + {0x31B0, 0x0083}, /* FRAME_PREAMBLE */ + {0x31B2, 0x004D}, /* LINE_PREAMBLE */ + {0x31B4, 0x0E77}, /* MIPI_TIMING_0 */ + {0x31B6, 0x0D20}, /* MIPI_TIMING_1 */ + {0x31B8, 0x020E}, /* MIPI_TIMING_2 */ + {0x31BA, 0x0710}, /* MIPI_TIMING_3 */ + {0x31BC, 0x2A0D}, /* MIPI_TIMING_4 */ + {ar0832_TABLE_WAIT_MS, 0x0005}, + {0x0112, 0x0A0A}, /* CCP_DATA_FORMAT */ + + {0x3044, 0x0590}, /* RESERVED_MFR_3044 */ + {0x306E, 0xFC80}, /* DATAPATH_SELECT */ + {0x30B2, 0xC000}, /* RESERVED_MFR_30B2 */ + {0x30D6, 0x0800}, /* RESERVED_MFR_30D6 */ + {0x316C, 0xB42F}, /* RESERVED_MFR_316C */ + {0x316E, 0x869A}, /* RESERVED_MFR_316E */ + {0x3170, 0x210E}, /* RESERVED_MFR_3170 */ + {0x317A, 0x010E}, /* RESERVED_MFR_317A */ + {0x31E0, 0x1FB9}, /* RESERVED_MFR_31E0 */ + {0x31E6, 0x07FC}, /* RESERVED_MFR_31E6 */ + {0x37C0, 0x0000}, /* P_GR_Q5 */ + {0x37C2, 0x0000}, /* P_RD_Q5 */ + {0x37C4, 0x0000}, /* P_BL_Q5 */ + {0x37C6, 0x0000}, /* P_GB_Q5 */ + {0x3E00, 0x0011}, /* RESERVED_MFR_3E00 */ + {0x3E02, 0x8801}, /* RESERVED_MFR_3E02 */ + {0x3E04, 0x2801}, /* RESERVED_MFR_3E04 */ + {0x3E06, 0x8449}, /* RESERVED_MFR_3E06 */ + {0x3E08, 0x6841}, /* RESERVED_MFR_3E08 */ + {0x3E0A, 0x400C}, /* RESERVED_MFR_3E0A */ + {0x3E0C, 0x1001}, /* RESERVED_MFR_3E0C */ + {0x3E0E, 0x2603}, /* RESERVED_MFR_3E0E */ + {0x3E10, 0x4B41}, /* RESERVED_MFR_3E10 */ + {0x3E12, 0x4B24}, /* RESERVED_MFR_3E12 */ + {0x3E14, 0xA3CF}, /* RESERVED_MFR_3E14 */ + {0x3E16, 0x8802}, /* RESERVED_MFR_3E16 */ + {0x3E18, 0x84FF}, /* RESERVED_MFR_3E18 */ + {0x3E1A, 0x8601}, /* RESERVED_MFR_3E1A */ + {0x3E1C, 0x8401}, /* RESERVED_MFR_3E1C */ + {0x3E1E, 0x840A}, /* RESERVED_MFR_3E1E */ + {0x3E20, 0xFF00}, /* RESERVED_MFR_3E20 */ + {0x3E22, 0x8401}, /* RESERVED_MFR_3E22 */ + {0x3E24, 0x00FF}, /* RESERVED_MFR_3E24 */ + {0x3E26, 0x0088}, /* RESERVED_MFR_3E26 */ + {0x3E28, 0x2E8A}, /* RESERVED_MFR_3E28 */ + {0x3E30, 0x0000}, /* RESERVED_MFR_3E30 */ + {0x3E32, 0x8801}, /* RESERVED_MFR_3E32 */ + {0x3E34, 0x4029}, /* RESERVED_MFR_3E34 */ + {0x3E36, 0x00FF}, /* RESERVED_MFR_3E36 */ + {0x3E38, 0x8469}, /* RESERVED_MFR_3E38 */ + {0x3E3A, 0x00FF}, /* RESERVED_MFR_3E3A */ + {0x3E3C, 0x2801}, /* RESERVED_MFR_3E3C */ + {0x3E3E, 0x3E2A}, /* RESERVED_MFR_3E3E */ + {0x3E40, 0x1C01}, /* RESERVED_MFR_3E40 */ + {0x3E42, 0xFF84}, /* RESERVED_MFR_3E42 */ + {0x3E44, 0x8401}, /* RESERVED_MFR_3E44 */ + {0x3E46, 0x0C01}, /* RESERVED_MFR_3E46 */ + {0x3E48, 0x8401}, /* RESERVED_MFR_3E48 */ + {0x3E4A, 0x00FF}, /* RESERVED_MFR_3E4A */ + {0x3E4C, 0x8402}, /* RESERVED_MFR_3E4C */ + {0x3E4E, 0x8984}, /* RESERVED_MFR_3E4E */ + {0x3E50, 0x6628}, /* RESERVED_MFR_3E50 */ + {0x3E52, 0x8340}, /* RESERVED_MFR_3E52 */ + {0x3E54, 0x00FF}, /* RESERVED_MFR_3E54 */ + {0x3E56, 0x4A42}, /* RESERVED_MFR_3E56 */ + {0x3E58, 0x2703}, /* RESERVED_MFR_3E58 */ + {0x3E5A, 0x6752}, /* RESERVED_MFR_3E5A */ + {0x3E5C, 0x3F2A}, /* RESERVED_MFR_3E5C */ + {0x3E5E, 0x846A}, /* RESERVED_MFR_3E5E */ + {0x3E60, 0x4C01}, /* RESERVED_MFR_3E60 */ + {0x3E62, 0x8401}, /* RESERVED_MFR_3E62 */ + {0x3E66, 0x3901}, /* RESERVED_MFR_3E66 */ + {0x3E90, 0x2C01}, /* RESERVED_MFR_3E90 */ + {0x3E98, 0x2B02}, /* RESERVED_MFR_3E98 */ + {0x3E92, 0x2A04}, /* RESERVED_MFR_3E92 */ + {0x3E94, 0x2509}, /* RESERVED_MFR_3E94 */ + {0x3E96, 0x0000}, /* RESERVED_MFR_3E96 */ + {0x3E9A, 0x2905}, /* RESERVED_MFR_3E9A */ + {0x3E9C, 0x00FF}, /* RESERVED_MFR_3E9C */ + {0x3ECC, 0x00EB}, /* RESERVED_MFR_3ECC */ + {0x3ED0, 0x1E24}, /* RESERVED_MFR_3ED0 */ + {0x3ED4, 0xAFC4}, /* RESERVED_MFR_3ED4 */ + {0x3ED6, 0x909B}, /* RESERVED_MFR_3ED6 */ + {0x3EE0, 0x2424}, /* RESERVED_MFR_3EE0 */ + {0x3EE2, 0x9797}, /* RESERVED_MFR_3EE2 */ + {0x3EE4, 0xC100}, /* RESERVED_MFR_3EE4 */ + {0x3EE6, 0x0540}, /* RESERVED_MFR_3EE6 */ + {0x3174, 0x8000}, /* RESERVED_MFR_3174 */ + {0x0300, 0x0004}, /* VT_PIX_CLK_DIV */ + {0x0302, 0x0001}, /* VT_SYS_CLK_DIV */ + {0x0304, 0x0002}, /* PRE_PLL_CLK_DIV */ + {0x0306, 0x0040}, /* PLL_MULTIPLIER */ + {0x0308, 0x000A}, /* OP_PIX_CLK_DIV */ + {0x030A, 0x0001}, /* OP_SYS_CLK_DIV */ + {0x3064, 0x7400}, /* RESERVED_MFR_3064 */ + {0x0344, 0x00C8}, /* X_ADDR_START */ + {0x0348, 0x0C07}, /* X_ADDR_END */ + {0x0346, 0x01A6}, /* Y_ADDR_START */ + {0x034A, 0x07F9}, /* Y_ADDR_END */ + {0x034C, 0x0B40}, /* X_OUTPUT_SIZE */ + {0x034E, 0x0654}, /* Y_OUTPUT_SIZE */ + {0x3040, 0xC041}, /* READ_MODE */ + {0x306E, 0xFC80}, /* DATAPATH_SELECT */ + {0x0400, 0x0000}, /* SCALING_MODE */ + {0x0404, 0x0010}, /* SCALE_M */ + {0x3178, 0x0000}, /* RESERVED_MFR_3178 */ + {0x3ED0, 0x1E24}, /* RESERVED_MFR_3ED0 */ + + {0x0342, 0x11B8}, /* LINE_LENGTH_PCK */ + {0x0340, 0x06E3}, /* FRAME_LENGTH_LINES */ + {0x0202, 0x06E3}, /* COARSE_INTEGRATION_TIME */ + {0x3014, 0x0BD8}, /* FINE_INTEGRATION_TIME_ */ + {0x3010, 0x0078}, /* FINE_CORRECTION */ + {0x301A, 0x8250}, /* RESET_REGISTER */ + {0x301A, 0x8650}, /* RESET_REGISTER */ + {0x301A, 0x8658}, /* RESET_REGISTER */ + /* gain */ + {0x3056, 0x10AA}, /* gain */ + {0x3058, 0x10AA}, /* gain */ + {0x305a, 0x10AA}, /* gain */ + {0x305c, 0x10AA}, /* gain */ + {0x0104, 0x0000}, /* GROUPED_PARAMETER_HOLD */ + {0x301A, 0x065C}, /* RESET_REGISTER */ + {ar0832_TABLE_END, 0x0000} +}; + +static struct ar0832_reg mode_1632X1224[] = { + {0x301A, 0x0058}, /* LG_CHANGE 0x0658 RESET_REGISTER */ + {0x301A, 0x0050}, /* LG_CHANGE 0x0650 RESET_REGISTER */ + + /* SC-CHANGE: to-do 8 bit write */ + {0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */ + + {0x3064, 0x7800}, /* LG_CHANGE 0x7400 RESERVED_MFR_3064 */ + {0x31AE, 0x0202}, /* SERIAL_FORMAT */ + {0x31B0, 0x0083}, /* FRAME_PREAMBLE */ + {0x31B2, 0x004D}, /* LINE_PREAMBLE */ + {0x31B4, 0x0E77}, /* MIPI_TIMING_0 */ + {0x31B6, 0x0D20}, /* MIPI_TIMING_1 */ + {0x31B8, 0x020E}, /* MIPI_TIMING_2 */ + {0x31BA, 0x0710}, /* MIPI_TIMING_3 */ + {0x31BC, 0x2A0D}, /* MIPI_TIMING_4 */ + {ar0832_TABLE_WAIT_MS, 0x0005}, + {0x0112, 0x0A0A}, /* LG_CHANGE CCP_DATA_FORMAT */ + {0x3044, 0x0590}, /* RESERVED_MFR_3044 */ + {0x306E, 0xFC80}, /* DATAPATH_SELECT */ + {0x30B2, 0xC000}, /* RESERVED_MFR_30B2 */ + {0x30D6, 0x0800}, /* RESERVED_MFR_30D6 */ + {0x316C, 0xB42F}, /* RESERVED_MFR_316C */ + {0x316E, 0x869A}, /* RESERVED_MFR_316E */ + {0x3170, 0x210E}, /* RESERVED_MFR_3170 */ + {0x317A, 0x010E}, /* RESERVED_MFR_317A */ + {0x31E0, 0x1FB9}, /* RESERVED_MFR_31E0 */ + {0x31E6, 0x07FC}, /* RESERVED_MFR_31E6 */ + {0x37C0, 0x0000}, /* P_GR_Q5 */ + {0x37C2, 0x0000}, /* P_RD_Q5 */ + {0x37C4, 0x0000}, /* P_BL_Q5 */ + {0x37C6, 0x0000}, /* P_GB_Q5 */ + {0x3E00, 0x0011}, /* RESERVED_MFR_3E00 */ + {0x3E02, 0x8801}, /* RESERVED_MFR_3E02 */ + {0x3E04, 0x2801}, /* RESERVED_MFR_3E04 */ + {0x3E06, 0x8449}, /* RESERVED_MFR_3E06 */ + {0x3E08, 0x6841}, /* RESERVED_MFR_3E08 */ + {0x3E0A, 0x400C}, /* RESERVED_MFR_3E0A */ + {0x3E0C, 0x1001}, /* RESERVED_MFR_3E0C */ + {0x3E0E, 0x2603}, /* RESERVED_MFR_3E0E */ + {0x3E10, 0x4B41}, /* RESERVED_MFR_3E10 */ + {0x3E12, 0x4B24}, /* RESERVED_MFR_3E12 */ + {0x3E14, 0xA3CF}, /* RESERVED_MFR_3E14 */ + {0x3E16, 0x8802}, /* RESERVED_MFR_3E16 */ + {0x3E18, 0x84FF}, /* LG_CHANGE 0x8401 RESERVED_MFR_3E18 */ + {0x3E1A, 0x8601}, /* RESERVED_MFR_3E1A */ + {0x3E1C, 0x8401}, /* RESERVED_MFR_3E1C */ + {0x3E1E, 0x840A}, /* RESERVED_MFR_3E1E */ + {0x3E20, 0xFF00}, /* RESERVED_MFR_3E20 */ + {0x3E22, 0x8401}, /* RESERVED_MFR_3E22 */ + {0x3E24, 0x00FF}, /* RESERVED_MFR_3E24 */ + {0x3E26, 0x0088}, /* RESERVED_MFR_3E26 */ + {0x3E28, 0x2E8A}, /* RESERVED_MFR_3E28 */ + {0x3E30, 0x0000}, /* RESERVED_MFR_3E30 */ + {0x3E32, 0x8801}, /* RESERVED_MFR_3E32 */ + {0x3E34, 0x4029}, /* RESERVED_MFR_3E34 */ + {0x3E36, 0x00FF}, /* RESERVED_MFR_3E36 */ + {0x3E38, 0x8469}, /* RESERVED_MFR_3E38 */ + {0x3E3A, 0x00FF}, /* RESERVED_MFR_3E3A */ + {0x3E3C, 0x2801}, /* RESERVED_MFR_3E3C */ + {0x3E3E, 0x3E2A}, /* RESERVED_MFR_3E3E */ + {0x3E40, 0x1C01}, /* RESERVED_MFR_3E40 */ + {0x3E42, 0xFF84}, /* RESERVED_MFR_3E42 */ + {0x3E44, 0x8401}, /* RESERVED_MFR_3E44 */ + {0x3E46, 0x0C01}, /* RESERVED_MFR_3E46 */ + {0x3E48, 0x8401}, /* RESERVED_MFR_3E48 */ + {0x3E4A, 0x00FF}, /* RESERVED_MFR_3E4A */ + {0x3E4C, 0x8402}, /* RESERVED_MFR_3E4C */ + {0x3E4E, 0x8984}, /* RESERVED_MFR_3E4E */ + {0x3E50, 0x6628}, /* RESERVED_MFR_3E50 */ + {0x3E52, 0x8340}, /* RESERVED_MFR_3E52 */ + {0x3E54, 0x00FF}, /* RESERVED_MFR_3E54 */ + {0x3E56, 0x4A42}, /* RESERVED_MFR_3E56 */ + {0x3E58, 0x2703}, /* RESERVED_MFR_3E58 */ + {0x3E5A, 0x6752}, /* RESERVED_MFR_3E5A */ + {0x3E5C, 0x3F2A}, /* RESERVED_MFR_3E5C */ + {0x3E5E, 0x846A}, /* RESERVED_MFR_3E5E */ + {0x3E60, 0x4C01}, /* RESERVED_MFR_3E60 */ + {0x3E62, 0x8401}, /* RESERVED_MFR_3E62 */ + {0x3E66, 0x3901}, /* RESERVED_MFR_3E66 */ + {0x3E90, 0x2C01}, /* RESERVED_MFR_3E90 */ + {0x3E98, 0x2B02}, /* RESERVED_MFR_3E98 */ + {0x3E92, 0x2A04}, /* RESERVED_MFR_3E92 */ + {0x3E94, 0x2509}, /* RESERVED_MFR_3E94 */ + {0x3E96, 0x0000}, /* RESERVED_MFR_3E96 */ + {0x3E9A, 0x2905}, /* RESERVED_MFR_3E9A */ + {0x3E9C, 0x00FF}, /* RESERVED_MFR_3E9C */ + {0x3ECC, 0x00EB}, /* RESERVED_MFR_3ECC */ + {0x3ED0, 0x1E24}, /* RESERVED_MFR_3ED0 */ + {0x3ED4, 0xAFC4}, /* RESERVED_MFR_3ED4 */ + {0x3ED6, 0x909B}, /* RESERVED_MFR_3ED6 */ + {0x3EE0, 0x2424}, /* RESERVED_MFR_3EE0 */ + {0x3EE2, 0x9797}, /* RESERVED_MFR_3EE2 */ + {0x3EE4, 0xC100}, /* RESERVED_MFR_3EE4 */ + {0x3EE6, 0x0540}, /* RESERVED_MFR_3EE6 */ + {0x3174, 0x8000}, /* RESERVED_MFR_3174 */ + {0x0300, 0x0004}, /* VT_PIX_CLK_DIV */ + {0x0302, 0x0001}, /* VT_SYS_CLK_DIV */ + {0x0304, 0x0002}, /* PRE_PLL_CLK_DIV */ + + {0x0306, 0x0040}, /* PLL_MULTIPLIER */ + + {0x0308, 0x000A}, /* OP_PIX_CLK_DIV */ + {0x030A, 0x0001}, /* OP_SYS_CLK_DIV */ + {ar0832_TABLE_WAIT_MS, 0x0001}, /* waitmsec 1 */ + + {0x3064, 0x7400}, /* RESERVED_MFR_3064 */ + + {0x0104, 0x0100}, /* GROUPED_PARAMETER_HOLD */ + + {0x0344, 0x0008}, /* X_ADDR_START */ + {0x0348, 0x0CC9}, /* X_ADDR_END */ + {0x0346, 0x0008}, /* Y_ADDR_START */ + {0x034A, 0x0999}, /* Y_ADDR_END */ + {0x034C, 0x0660}, /* X_OUTPUT_SIZE */ + {0x034E, 0x04C8}, /* Y_OUTPUT_SIZE */ + {0x3040, 0xC4C3}, /* READ_MODE */ + {0x306E, 0xFC80}, /* DATAPATH_SELECT */ + {0x3178, 0x0000}, /* RESERVED_MFR_3178 */ + {0x3ED0, 0x1E24}, /* RESERVED_MFR_3ED0 */ + {0x0400, 0x0002}, /* SCALING_MODE */ + {0x0404, 0x0010}, /* SCALE_M */ + {0x0342, 0x101A}, /* LINE_LENGTH_PCK */ + {0x0340, 0x0610}, /* FRAME_LENGTH_LINES */ + {0x0202, 0x0557}, /* COARSE_INTEGRATION_TIME */ + {0x3014, 0x0988}, /* FINE_INTEGRATION_TIME_ */ + {0x3010, 0x0130}, /* FINE_CORRECTION */ + {0x301A, 0x8250}, /* LGE_CHANGE 0x8650 RESET_REGISTER */ + {0x301A, 0x8650}, /* RESET_REGISTER */ + {0x301A, 0x8658}, /* RESET_REGISTER */ + + /* gain */ + {0x3056, 0x10AA}, /* gain */ + {0x3058, 0x10AA}, /* gain */ + {0x305a, 0x10AA}, /* gain */ + {0x305c, 0x10AA}, /* gain */ + + /* todo 8-bit write */ + {0x0104, 0x0000}, /* GROUPED_PARAMETER_HOLD */ + + {0x301A, 0x065C}, /* RESET_REGISTER */ + {ar0832_TABLE_END, 0x0000} +}; + +static struct ar0832_reg mode_end[] = { + {ar0832_TABLE_END, 0x0000} +}; + +enum { + ar0832_MODE_3264X2448, + ar0832_MODE_2880X1620, + ar0832_MODE_1632X1224, +}; + +static struct ar0832_reg *mode_table[] = { + [ar0832_MODE_3264X2448] = mode_3264X2448, + [ar0832_MODE_2880X1620] = mode_2880X1620, + [ar0832_MODE_1632X1224] = mode_1632X1224, +}; + + +/* 2 regs to program frame length */ +static inline void ar0832_get_frame_length_regs(struct ar0832_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 ar0832_get_coarse_time_regs(struct ar0832_reg *regs, + u32 coarse_time) +{ + regs->addr = 0x0202; + regs->val = (coarse_time >> 8) & 0xff; + (regs + 1)->addr = 0x0203; + (regs + 1)->val = (coarse_time) & 0xff; +} + +static inline void ar0832_get_focuser_vcm_control_regs(struct ar0832_reg *regs, + u16 value) +{ + regs->addr = 0x30F0; + regs->val = (value >> 8) & 0xff; + (regs + 1)->addr = 0x30F1; + (regs + 1)->val = (value) & 0xff; +} + +static inline void ar0832_get_focuser_vcm_step_time_regs + (struct ar0832_reg *regs, u16 value) +{ + regs->addr = 0x30F4; + regs->val = (value >> 8) & 0xff; + (regs + 1)->addr = 0x30F5; + (regs + 1)->val = (value) & 0xff; +} + +static inline void ar0832_get_focuser_data_regs(struct ar0832_reg *regs, + u16 value) +{ + regs->addr = 0x30F2; + regs->val = (value >> 8) & 0xff; + (regs + 1)->addr = 0x30F3; + (regs + 1)->val = (value) & 0xff; +} + + +static inline void ar0832_get_gain_reg(struct ar0832_reg *regs, u16 gain) +{ + regs->addr = 0x0205; + regs->val = gain; +} + +static int ar0832_write_reg(struct i2c_client *client, u16 addr, u8 val) +{ + int err; + struct i2c_msg msg; + unsigned char data[3]; + int retry = 0; + + 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; + + do { + err = i2c_transfer(client->adapter, &msg, 1); + if (err > 0) + return 0; + retry++; + pr_err("ar0832: i2c transfer failed, retrying %x %x\n", + addr, val); + /* + why usleep_range() instead of msleep() ? + Read Documentation/timers/timers-howto.txt + */ + usleep_range(3000, 3500); + } while (retry < ar0832_MAX_RETRIES); + + return err; +} + + +static int ar0832_2bytes_write_reg_helper( + struct ar0832_info *info, u16 addr, u16 val) +{ + int ret; + + pr_info("[%s] (0x%04x)(0x%04x)\n", __func__, addr, val); + ret = ar0832_write_reg(info->i2c_client, addr, (val >> 8 & 0xff)); + if (addr != 0x104) + ret = ar0832_write_reg(info->i2c_client, addr+1, (val & 0xff)); + else + pr_info("[%s] this global parameter hold\n", __func__); + return ret; +} + +static int ar0832_write_reg_helper(struct ar0832_info *info, u16 addr, u16 val) +{ + int ret; + + ret = ar0832_write_reg(info->i2c_client, addr, val); + return ret; +} + +static int ar0832_focuser_write(struct i2c_client *client, u16 value) +{ + int count; + struct i2c_msg msg[1]; + unsigned char data[2]; + int retry = 0; + + if (!client->adapter) + return -ENODEV; + + data[0] = (u8) ((value >> 4) & 0x3F); + data[1] = (u8) ((value & 0xF) << 4); + /* Slew rate control (8 steps, 50us) */ + data[1] = (data[1] & 0xF0) | 0x05; + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = ARRAY_SIZE(data); + msg[0].buf = data; + + do { + count = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + if (count == ARRAY_SIZE(msg)) + return 0; + retry++; + pr_err("ar0832_focuser: i2c transfer failed, retrying %x\n", + value); + usleep_range(3000, 3500); + } while (retry <= 3); + return -EIO; +} + +static int ar0832_focuser_write_helper( + struct ar0832_focuser_info *info, u16 value) +{ + int ret; + ret = ar0832_focuser_write(info->i2c_client, value); + return ret; +} + +static int ar0832_write_table(struct ar0832_info *info, + const struct ar0832_reg table[], + const struct ar0832_reg override_list[], + int num_override_regs) +{ + int err; + const struct ar0832_reg *next; + + for (next = table; next->addr != ar0832_TABLE_END; next++) { + if (next->addr == ar0832_TABLE_WAIT_MS) { + usleep_range(next->val*1000, next->val*1000 + 500); + continue; + } + err = ar0832_2bytes_write_reg_helper(info, next->addr, + next->val); + if (err) + return err; + } + return 0; +} + +static int ar0832_set_frame_length(struct ar0832_info *info, u32 frame_length) +{ + struct ar0832_reg reg_list[2]; + int i = 0; + int ret; + + pr_info("[%s] (0x%08x)\n", __func__, frame_length); + + ar0832_get_frame_length_regs(reg_list, frame_length); + ret = ar0832_write_reg_helper(info, 0x0104, 0x01); + if (ret) + return ret; + + for (i = 0; i < 2; i++) { + ret = ar0832_write_reg_helper(info, reg_list[i].addr, + reg_list[i].val); + if (ret) + return ret; + } + ret = ar0832_write_reg_helper(info, 0x0104, 0x00); + if (ret) + return ret; + + return 0; +} + +static int ar0832_set_coarse_time(struct ar0832_info *info, u32 coarse_time) +{ + int ret; + + struct ar0832_reg reg_list[2]; + int i = 0; + + pr_info("[%s] (0x%08x)\n", __func__, coarse_time); + ar0832_get_coarse_time_regs(reg_list, coarse_time); + ret = ar0832_write_reg_helper(info, 0x0104, 0x01); + if (ret) + return ret; + + for (i = 0; i < 2; i++) { + ret = ar0832_write_reg_helper(info, reg_list[i].addr, + reg_list[i].val); + if (ret) + return ret; + } + ret = ar0832_write_reg_helper(info, 0x0104, 0x00); + if (ret) + return ret; + + return 0; +} + +static int ar0832_set_gain(struct ar0832_info *info, __u16 gain) +{ + int ret = 0; + /*struct ar0832_reg reg_list_analog; +#if USE_DIGITAL_GAIN + struct ar0832_reg reg_list_digtal[8]; + u16 i; +#endif + + pr_info("[%s] (0x%08x)\n", __func__, gain->AnalogGain); + + ar0832_get_gain_reg(®_list_analog, gain->AnalogGain); + ret = ar0832_write_reg_helper(info, 0x0104, 0x01); + if (ret) + return ret; + ret = ar0832_write_reg_helper(info, reg_list_analog.addr, + reg_list_analog.val); + if (ret) + return ret; +#if USE_DIGITAL_GAIN + for(i=0; i<4; i++) { + reg_list_digtal[i*2].addr = 0x020E + i*2; + reg_list_digtal[i*2].val = gain->DigitalGain_Upper; + reg_list_digtal[i*2+1].addr = 0x020F + i*2; + reg_list_digtal[i*2+1].val = gain->DigitalGain_Lower; + ret = ar0832_write_reg_helper(info, reg_list_digtal[i*2].addr, + reg_list_digtal[i*2].val); + } + if (ret) + return ret; +#endif + ret = ar0832_write_reg_helper(info, 0x0104, 0x00); + if (ret) + return ret; +*/ + ret |= ar0832_write_reg_helper(info, 0x0104, 0x01); + /* Gain Registers Start */ + ret |= ar0832_write_reg_helper(info, 0x3056, gain); + ret |= ar0832_write_reg_helper(info, 0x3058, gain); + ret |= ar0832_write_reg_helper(info, 0x305A, gain); + ret |= ar0832_write_reg_helper(info, 0x305C, gain); + /* Gain register End */ + ret |= ar0832_write_reg_helper(info, 0x0104, 0x00); + + return ret; +} + +static int ar0832_set_mode(struct ar0832_info *info, struct ar0832_mode *mode) +{ + int sensor_mode; + int err; + int i; + int ret; + struct ar0832_reg reg_list[5]; + + if (mode->xres == 3264 && mode->yres == 2448) + sensor_mode = ar0832_MODE_3264X2448; + else if (mode->xres == 2880 && mode->yres == 1620) + sensor_mode = ar0832_MODE_2880X1620; + else if (mode->xres == 1632 && mode->yres == 1224) + sensor_mode = ar0832_MODE_1632X1224; + 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.*/ + err = ar0832_write_table(info, mode_start, NULL, 0); + if (err) + return err; + + err = ar0832_write_table(info, mode_table[sensor_mode], NULL, 0); + if (err) + return err; + + /* When we change the resolution */ +#if 0 + ar0832_get_frame_length_regs(reg_list, mode->frame_length); + for (i = 0; i < 2; i++) { + ret = ar0832_write_reg_helper(info, reg_list[i].addr, + reg_list[i].val); + if (ret) + return ret; + } + + ar0832_get_coarse_time_regs(reg_list + 2, mode->coarse_time); + for (i = 0; i < 2; i++) { + ret = ar0832_write_reg_helper(info, reg_list[i+2].addr, + reg_list[i+2].val); + if (ret) + return ret; + } + ret = ar0832_set_gain(info, &(mode->gain)); + if (ret) + return ret; +#endif + + err = ar0832_write_table(info, mode_end, NULL, 0); + if (err) + return err; + + info->mode = sensor_mode; + pr_info("%s: end\n", __func__); + return 0; +} + +static int ar0832_get_status(struct ar0832_info *info, u8 *status) +{ + int err = 0; + + *status = 0; + /* FixMe */ + /* + err = ar0832_read_reg(info->i2c_client, 0x001, status); + */ + pr_info("%s: %u %d\n", __func__, *status, err); + return err; +} +static int ar0832_set_region(struct ar0832_info *info, + struct ar0832_stereo_region *region) +{ + u16 image_width = region->image_end.x - region->image_start.x+1; + u16 image_height = region->image_end.y - region->image_start.y+1; + struct i2c_client *i2c_client; + pr_info("%s: %d\n", __func__, region->camer_index); + if (region->camer_index == 0) + i2c_client = info->i2c_client; + else if (region->camer_index == 1) + i2c_client = info->i2c_client_right; + else + return -1; + pr_info("%s: width = %d height = %d\n", __func__, image_width, + image_height); +#if 0 + ar0832_write_reg(i2c_client, 0x0104, 1); + ar0832_write_reg(i2c_client, 0x0346, + UpperByte16to8(region->image_start.y)); + /* Y_ADDR START LOWER BYTE */ + ar0832_write_reg(i2c_client, 0x0347, + LowerByte16to8(region->image_start.y)); + /* Y_OUT SIZE UPPER BYTE */ + ar0832_write_reg(i2c_client, 0x034E, + UpperByte16to8(DefaultImageHeight)); + /* Y_OUT SIZE LOWER BYTE */ + ar0832_write_reg(i2c_client, 0x034F, + LowerByte16to8(DefaultImageHeight)); + /* Y_ADDR_END UPPER BYTE */ + ar0832_write_reg(i2c_client, 0x034A, + UpperByte16to8(region->image_end.y)); + /* Y_ADDR_END LOWER BYTE */ + ar0832_write_reg(i2c_client, 0x034B, + LowerByte16to8(region->image_end.y)); + /* X_ADDR_START UPPER BYTE */ + ar0832_write_reg(i2c_client, 0x0344, + UpperByte16to8(region->image_start.x)); + /* X_ADDR START LOWER BYTE */ + ar0832_write_reg(i2c_client, 0x0345, + LowerByte16to8(region->image_start.x)); + /* X_SIZE UPPER BYTE */ + ar0832_write_reg(i2c_client, 0x034C, + UpperByte16to8(DefaultImageWidth)); + /* X_SIZE LOWER BYTE */ + ar0832_write_reg(i2c_client, 0x034D, + LowerByte16to8(DefaultImageWidth)); + /* X_ADDR_END UPPER BYTE */ + ar0832_write_reg(i2c_client, 0x0348, + UpperByte16to8(region->image_end.x)); + /* X_ADDR_END LOWER BYTE */ + ar0832_write_reg(i2c_client, 0x0349, + LowerByte16to8(region->image_end.x)); + ar0832_write_reg(i2c_client, 0x0104, 0); +#endif + return 0; +} + +static int ar0832_power_on(void) +{ + pr_info("%s: ++\n", __func__); + info->pdata->power_on(); + + return 0; +} + + +static int ar0832_focuser_set_config(struct ar0832_focuser_info *info) +{ + struct ar0832_reg reg_list[2]; + int ret; + int i = 0; + u8 vcm_slew = 5; + /* + bit15(0x80) means that VCM driver enable bit. + bit3(0x08) means that keep VCM(AF position) + while sensor is in soft standby mode during mode transitions. + */ + u16 vcm_control_data = (0x80 << 8 | (0x08 | (vcm_slew & 0x07))); + /* Gene's code for AF */ + u16 vcm_step_time = 4096; + + pr_info("[%s] vcm_control_data (0x%08x)\n", __func__, + vcm_control_data); + ar0832_get_focuser_vcm_control_regs(reg_list, vcm_control_data); + for (i = 0; i < 2; i++) { + ret = ar0832_focuser_write_helper(info, reg_list[i].val); + if (ret) + return ret; + } + + pr_info("[%s] vcm_control_data (0x%08x)\n", __func__, vcm_step_time); + ar0832_get_focuser_vcm_step_time_regs(reg_list, vcm_step_time); + for (i = 0; i < 2; i++) { + ret = ar0832_focuser_write_helper(info, reg_list[i].val); + if (ret) + return ret; + } + + return 0; +} + +static int ar0832_focuser_set_position(struct ar0832_focuser_info *info, u32 position) +{ + int ret; + struct ar0832_reg reg_list[2]; + int i = 0; + + if (position < info->config.pos_low || + position > info->config.pos_high) + return -EINVAL; + + pr_info("[%s] (0x%08x)\n", __func__, position); + ar0832_get_focuser_data_regs(reg_list, position); + + for (i = 0; i < 2; i++) { + ret = ar0832_focuser_write_helper(info, reg_list[i].val); + if (ret) + return ret; + } + return 0; +} + +static long ar0832_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int err; + struct ar0832_info *info = file->private_data; + + pr_info("ar0832_ioctl : cmd = %d\n", cmd); + + switch (cmd) { + case AR0832_IOCTL_SET_POWER_ON: + pr_info("AR0832_IOCTL_SET_POWER_ON\n"); + return ar0832_power_on(); + case AR0832_IOCTL_SET_MODE: + { + struct ar0832_mode mode; + pr_info("AR0832_IOCTL_SET_MODE\n"); + if (copy_from_user(&mode, + (const void __user *)arg, + sizeof(struct ar0832_mode))) { + pr_info("%s %d\n", __func__, __LINE__); + return -EFAULT; + } + mutex_lock(&ar0832_camera_lock); + err = ar0832_set_mode(info, &mode); + if (focuser_info->focuser_init_flag == false) { + ar0832_focuser_set_config(focuser_info); + focuser_info->focuser_init_flag = true; + } + mutex_unlock(&ar0832_camera_lock); + return err; + } + case AR0832_IOCTL_SET_FRAME_LENGTH: + mutex_lock(&ar0832_camera_lock); + err = ar0832_set_frame_length(info, (u32)arg); + mutex_unlock(&ar0832_camera_lock); + return err; + case AR0832_IOCTL_SET_COARSE_TIME: + mutex_lock(&ar0832_camera_lock); + err = ar0832_set_coarse_time(info, (u32)arg); + mutex_unlock(&ar0832_camera_lock); + return err; + case AR0832_IOCTL_SET_GAIN: + { + mutex_lock(&ar0832_camera_lock); + err = ar0832_set_gain(info, (u16)arg); + mutex_unlock(&ar0832_camera_lock); + return err; + } + case AR0832_IOCTL_GET_STATUS: + { + u8 status; + pr_info("AR0832_IOCTL_GET_STATUS\n"); + err = ar0832_get_status(info, &status); + if (err) + return err; + if (copy_to_user((void __user *)arg, &status, + 2)) { + pr_info("%s %d\n", __func__, __LINE__); + return -EFAULT; + } + return 0; + } + case AR0832_IOCTL_SET_SENSOR_REGION: + { + struct ar0832_stereo_region region; + pr_info("AR0832_IOCTL_SET_SENSOR_REGION\n"); + if (copy_from_user(®ion, + (const void __user *)arg, + sizeof(struct ar0832_stereo_region))) { + pr_info("%s %d\n", __func__, __LINE__); + return -EFAULT; + } + /* TO-DO */ + /* + mutex_lock(&ar0832_camera_lock); + err = ar0832_set_region(info, ®ion); + mutex_unlock(&ar0832_camera_lock); + */ + return err; + } + + case AR0832_FOCUSER_IOCTL_GET_CONFIG: + { + pr_info("%s AR0832_FOCUSER_IOCTL_GET_CONFIG\n", __func__); + if (copy_to_user((void __user *) arg, + &focuser_info->config, + sizeof(focuser_info->config))) { + pr_err("%s: 0x%x\n", __func__, __LINE__); + return -EFAULT; + } + return 0; + } + case AR0832_FOCUSER_IOCTL_SET_POSITION: + pr_info("%s AR0832_FOCUSER_IOCTL_SET_POSITION\n", __func__); + mutex_lock(&ar0832_camera_lock); + err = ar0832_focuser_set_position(focuser_info, (u32) arg); + mutex_unlock(&ar0832_camera_lock); + return err; + case AR0832_FOCUSER_IOCTL_SET_MODE: + pr_info("%s AR0832_FOCUSER_IOCTL_SET_MODE\n", __func__); + return 0; + + default: + pr_info("(error) %s NONE IOCTL\n", __func__); + return -EINVAL; + } + return 0; +} + +static int ar0832_open(struct inode *inode, struct file *file) +{ + pr_info("%s: ++\n", __func__); + file->private_data = info; + focuser_info->focuser_init_flag = false; + return 0; +} + +static int ar0832_release(struct inode *inode, struct file *file) +{ + pr_info("%s: ++\n", __func__); + + file->private_data = NULL; + info->pdata->power_off(); + return 0; +} + +static const struct file_operations ar0832_fileops = { + .owner = THIS_MODULE, + .open = ar0832_open, + .unlocked_ioctl = ar0832_ioctl, + .release = ar0832_release, +}; + +static struct miscdevice ar0832_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "ar0832", + .fops = &ar0832_fileops, + .mode = S_IRWXUGO +}; + +static int ar0832_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err; + pr_info("ar0832: probing sensor.(id:%s)\n", id->name); + + info = kzalloc(sizeof(struct ar0832_info), GFP_KERNEL); + focuser_info = kzalloc(sizeof(struct ar0832_focuser_info), GFP_KERNEL); + if (!info || !focuser_info) { + pr_err("ar0832: Unable to allocate memory!\n"); + return -ENOMEM; + } + + err = misc_register(&ar0832_device); + if (err) { + pr_err("ar0832: Unable to register misc device!\n"); + kfree(info); + kfree(focuser_info); + return err; + } + + /* sensor */ + info->pdata = client->dev.platform_data; + info->i2c_client = client; + + /* focuser */ + focuser_info->i2c_client = client; + focuser_info->config.settle_time = SETTLETIME_MS; + /* FIX-ME */ + /* + focuser_info->config.focal_length = FOCAL_LENGTH; + focuser_info->config.fnumber = FNUMBER; + */ + focuser_info->config.pos_low = POS_LOW; + focuser_info->config.pos_high = POS_HIGH; + + i2c_set_clientdata(client, info); + + return 0; +} + +static int ar0832_remove(struct i2c_client *client) +{ + misc_deregister(&ar0832_device); + kfree(info); + kfree(focuser_info); + return 0; +} + +static const struct i2c_device_id ar0832_id[] = { + { "ar0832", 0 }, +}; + +static struct i2c_driver ar0832_i2c_driver = { + .probe = ar0832_probe, + .remove = ar0832_remove, + .id_table = ar0832_id, + .driver = { + .name = "ar0832", + .owner = THIS_MODULE, + }, +}; + +static int __init ar0832_init(void) +{ + pr_info("%s: ++\n", __func__); + return i2c_add_driver(&ar0832_i2c_driver); +} + +static void __exit ar0832_exit(void) +{ + i2c_del_driver(&ar0832_i2c_driver); +} + +module_init(ar0832_init); +module_exit(ar0832_exit); diff --git a/include/media/ar0832_focuser.h b/include/media/ar0832_focuser.h new file mode 100644 index 000000000000..e1258629b89a --- /dev/null +++ b/include/media/ar0832_focuser.h @@ -0,0 +1,56 @@ +/* +* ar0832_focuser.h +* +* Copyright (c) 2011, NVIDIA, All Rights Reserved. +* +* This file is licensed under the terms of the GNU General Public License +* version 2. This program is licensed "as is" without any warranty of any +* kind, whether express or implied. +*/ + +#ifndef __AR0832_FOCUSER_H__ +#define __AR0832_FOCUSER_H__ + +#include <linux/ioctl.h> /* For IOCTL macros */ +#include <linux/i2c.h> + +#define AR0832_FOCUSER_IOCTL_GET_CONFIG _IOR('o', 12, struct ar0832_focuser_config) +#define AR0832_FOCUSER_IOCTL_SET_POSITION _IOW('o', 13, __u32) +#define AR0832_FOCUSER_IOCTL_SET_MODE _IOW('o', 14, __u32) + +struct ar0832_focuser_config { + __u32 settle_time; + __u32 actuator_range; + __u32 pos_low; + __u32 pos_high; + /* To-Do */ + /* + float focal_length; + float fnumber; + float max_aperture; + */ +}; + +enum StereoCameraMode { + MAIN = 0, + /* Sets the stereo camera to stereo mode. */ + STEREO = 1, + /* Only the sensor on the left is on. */ + LEFT_ONLY, + /* Only the sensor on the right is on. */ + RIGHT_ONLY, + /* Ignore -- Forces compilers to make 32-bit enums. */ + StereoCameraMode_Force32 = 0x7FFFFFFF +}; + +struct ar0832_focuser_info { + struct i2c_client *i2c_client; + struct i2c_client *i2c_client_right; + struct regulator *regulator; + struct ar0832_focuser_config config; + __u8 focuser_init_flag; + + enum StereoCameraMode camera_mode; +}; + +#endif /* __AR0832_FOCUSER_H__ */ diff --git a/include/media/ar0832_main.h b/include/media/ar0832_main.h new file mode 100644 index 000000000000..55ade14a4efa --- /dev/null +++ b/include/media/ar0832_main.h @@ -0,0 +1,91 @@ +/* +* ar0832_main.h +* +* Copyright (c) 2011, NVIDIA, All Rights Reserved. +* +* This file is licensed under the terms of the GNU General Public License +* version 2. This program is licensed "as is" without any warranty of any +* kind, whether express or implied. +*/ + +#ifndef __AR0832_MAIN_H__ +#define __AR0832_MAIN_H__ + +#include <linux/ioctl.h> /* For IOCTL macros */ + +#define AR0832_IOCTL_SET_MODE _IOW('o', 1, struct ar0832_mode) +#define AR0832_IOCTL_SET_FRAME_LENGTH _IOW('o', 2, __u32) +#define AR0832_IOCTL_SET_COARSE_TIME _IOW('o', 3, __u32) +#define AR0832_IOCTL_SET_GAIN _IOW('o', 4, struct ar0832_gain) +#define AR0832_IOCTL_GET_STATUS _IOR('o', 5, __u8) +#define AR0832_IOCTL_GET_OTP _IOR('o', 6, struct ar0832_otp_data) +#define AR0832_IOCTL_TEST_PATTERN _IOW('o', 7, enum ar0832_test_pattern) +#define AR0832_IOCTL_SET_POWER_ON _IOW('o', 10, __u32) +#define AR0832_IOCTL_SET_SENSOR_REGION _IOW('o', 11, struct ar0832_stereo_region) + + +enum ar0832_test_pattern { + TEST_PATTERN_NONE, + TEST_PATTERN_COLORBARS, + TEST_PATTERN_CHECKERBOARD +}; + +struct ar0832_otp_data { + /* Only the first 5 bytes are actually used. */ + __u8 sensor_serial_num[6]; + __u8 part_num[8]; + __u8 lens_id[1]; + __u8 manufacture_id[2]; + __u8 factory_id[2]; + __u8 manufacture_date[9]; + __u8 manufacture_line[2]; + + __u32 module_serial_num; + __u8 focuser_liftoff[2]; + __u8 focuser_macro[2]; + __u8 reserved1[12]; + __u8 shutter_cal[16]; + __u8 reserved2[183]; + + /* Big-endian. CRC16 over 0x00-0x41 (inclusive) */ + __u16 crc; + __u8 reserved3[3]; + __u8 auto_load[2]; +} __attribute__ ((packed)); + +struct ar0832_gain{ + __u16 AnalogGain; + __u16 DigitalGain_Upper; + __u16 DigitalGain_Lower; +}; + +struct ar0832_mode { + int xres; + int yres; + __u32 frame_length; + __u32 coarse_time; + struct ar0832_gain gain; +}; +struct ar0832_point{ + int x; + int y; +}; +struct ar0832_reg { + __u16 addr; + __u16 val; +}; +struct ar0832_stereo_region{ + int camer_index; + struct ar0832_point image_start; + struct ar0832_point image_end; +}; + +#ifdef __KERNEL__ +struct ar0832_platform_data { + int (*power_on)(void); + int (*power_off)(void); + +}; +#endif /* __KERNEL__ */ + +#endif |