diff options
Diffstat (limited to 'drivers/media/platform/tegra/ar1335.c')
-rw-r--r-- | drivers/media/platform/tegra/ar1335.c | 1257 |
1 files changed, 1257 insertions, 0 deletions
diff --git a/drivers/media/platform/tegra/ar1335.c b/drivers/media/platform/tegra/ar1335.c new file mode 100644 index 000000000000..d178fb459702 --- /dev/null +++ b/drivers/media/platform/tegra/ar1335.c @@ -0,0 +1,1257 @@ +/* + * ar1335.c - ar1335 sensor driver + * + * Copyright (c) 2014-2015, NVIDIA CORPORATION, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/delay.h> +#include <linux/fs.h> +#include <linux/i2c.h> +#include <linux/clk.h> +#include <linux/miscdevice.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/regulator/consumer.h> +#include <linux/regmap.h> +#include <media/ar1335.h> +#include <linux/gpio.h> +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> + +#include "nvc_utilities.h" + +struct ar1335_reg { + u16 addr; + u16 val; +}; + +struct ar1335_info { + struct miscdevice miscdev_info; + int mode; + struct ar1335_power_rail power; + struct ar1335_sensordata sensor_data; + struct i2c_client *i2c_client; + struct ar1335_platform_data *pdata; + struct clk *mclk; + struct regmap *regmap; + struct dentry *debugdir; + atomic_t in_use; +}; + +static const struct regmap_config sensor_regmap_config = { + .reg_bits = 16, + .val_bits = 16, + .cache_type = REGCACHE_RBTREE, +}; + +#define AR1335_TABLE_WAIT_MS 0 +#define AR1335_TABLE_END 1 +#define AR1335_MAX_RETRIES 3 +#define AR1335_WAIT_MS 100 + +#define MAX_BUFFER_SIZE 32 +#define AR1335_FRAME_LENGTH_ADDR 0x300A +#define AR1335_COARSE_TIME_ADDR 0x3012 +#define AR1335_GAIN_ADDR 0x305E + +static struct ar1335_reg mode_4208x3120[] = { + {0x3042, 0x1004}, + {0x30D2, 0x0120}, + {0x30D4, 0x0000}, + {0x3090, 0x0000}, + {0x30FC, 0x0060}, + {0x30FE, 0x0060}, + {0x31E0, 0x0781}, + {0x3180, 0x9434}, + {0x317C, 0xEFF4}, + {0x30EE, 0x4140}, + {0x3F2C, 0x4428}, + + {0x3D00, 0x0446}, + {0x3D02, 0x4C66}, + {0x3D04, 0xFFFF}, + {0x3D06, 0xFFFF}, + {0x3D08, 0x5E40}, + {0x3D0A, 0x1146}, + {0x3D0C, 0x5D41}, + {0x3D0E, 0x1088}, + {0x3D10, 0x8342}, + {0x3D12, 0x00C0}, + {0x3D14, 0x5580}, + {0x3D16, 0x5B83}, + {0x3D18, 0x6084}, + {0x3D1A, 0x5A8D}, + {0x3D1C, 0x00C0}, + {0x3D1E, 0x8342}, + {0x3D20, 0x925A}, + {0x3D22, 0x8664}, + {0x3D24, 0x1030}, + {0x3D26, 0x801C}, + {0x3D28, 0x00A0}, + {0x3D2A, 0x56B0}, + {0x3D2C, 0x5788}, + {0x3D2E, 0x5150}, + {0x3D30, 0x824D}, + {0x3D32, 0x8D58}, + {0x3D34, 0x58D2}, + {0x3D36, 0x438A}, + {0x3D38, 0x4592}, + {0x3D3A, 0x458A}, + {0x3D3C, 0x439D}, + {0x3D3E, 0x51CA}, + {0x3D40, 0x5182}, + {0x3D42, 0x100C}, + {0x3D44, 0x9259}, + {0x3D46, 0x5982}, + {0x3D48, 0x5FF7}, + {0x3D4A, 0x6182}, + {0x3D4C, 0x6283}, + {0x3D4E, 0x4281}, + {0x3D50, 0x10C0}, + {0x3D52, 0x6498}, + {0x3D54, 0x4281}, + {0x3D56, 0x41FF}, + {0x3D58, 0xFFB8}, + {0x3D5A, 0x4081}, + {0x3D5C, 0x4080}, + {0x3D5E, 0x4180}, + {0x3D60, 0x4280}, + {0x3D62, 0x438D}, + {0x3D64, 0x44BA}, + {0x3D66, 0x4488}, + {0x3D68, 0x4380}, + {0x3D6A, 0x4241}, + {0x3D6C, 0x8140}, + {0x3D6E, 0x8240}, + {0x3D70, 0x8041}, + {0x3D72, 0x8042}, + {0x3D74, 0x8043}, + {0x3D76, 0x8D44}, + {0x3D78, 0xBA44}, + {0x3D7A, 0x875E}, + {0x3D7C, 0x4354}, + {0x3D7E, 0x4241}, + {0x3D80, 0x8140}, + {0x3D82, 0x8120}, + {0x3D84, 0x2881}, + {0x3D86, 0x6026}, + {0x3D88, 0x8055}, + {0x3D8A, 0x8070}, + {0x3D8C, 0x8040}, + {0x3D8E, 0x4C81}, + {0x3D90, 0x45C3}, + {0x3D92, 0x4581}, + {0x3D94, 0x4C40}, + {0x3D96, 0x8070}, + {0x3D98, 0x8040}, + {0x3D9A, 0x4C85}, + {0x3D9C, 0x6CA8}, + {0x3D9E, 0x6C8C}, + {0x3DA0, 0x000E}, + {0x3DA2, 0xBE44}, + {0x3DA4, 0x8844}, + {0x3DA6, 0xBC78}, + {0x3DA8, 0x0900}, + {0x3DAA, 0x8904}, + {0x3DAC, 0x8080}, + {0x3DAE, 0x0240}, + {0x3DB0, 0x8609}, + {0x3DB2, 0x008E}, + {0x3DB4, 0x0900}, + {0x3DB6, 0x8002}, + {0x3DB8, 0x4080}, + {0x3DBA, 0x0480}, + {0x3DBC, 0x887C}, + {0x3DBE, 0xAA86}, + {0x3DC0, 0x0900}, + {0x3DC2, 0x877A}, + {0x3DC4, 0x000E}, + {0x3DC6, 0xC379}, + {0x3DC8, 0x4C40}, + {0x3DCA, 0xBF70}, + {0x3DCC, 0x5E40}, + {0x3DCE, 0x114E}, + {0x3DD0, 0x5D41}, + {0x3DD2, 0x5383}, + {0x3DD4, 0x4200}, + {0x3DD6, 0xC055}, + {0x3DD8, 0xA400}, + {0x3DDA, 0xC083}, + {0x3DDC, 0x4288}, + {0x3DDE, 0x6083}, + {0x3DE0, 0x5B80}, + {0x3DE2, 0x5A64}, + {0x3DE4, 0x1030}, + {0x3DE6, 0x801C}, + {0x3DE8, 0x00A5}, + {0x3DEA, 0x5697}, + {0x3DEC, 0x57A5}, + {0x3DEE, 0x5180}, + {0x3DF0, 0x505A}, + {0x3DF2, 0x814D}, + {0x3DF4, 0x8358}, + {0x3DF6, 0x8058}, + {0x3DF8, 0xA943}, + {0x3DFA, 0x8345}, + {0x3DFC, 0xB045}, + {0x3DFE, 0x8343}, + {0x3E00, 0xA351}, + {0x3E02, 0xE251}, + {0x3E04, 0x8C59}, + {0x3E06, 0x8059}, + {0x3E08, 0x8A5F}, + {0x3E0A, 0xEC7C}, + {0x3E0C, 0xCC84}, + {0x3E0E, 0x6182}, + {0x3E10, 0x6283}, + {0x3E12, 0x4283}, + {0x3E14, 0x10CC}, + {0x3E16, 0x6496}, + {0x3E18, 0x4281}, + {0x3E1A, 0x41BB}, + {0x3E1C, 0x4082}, + {0x3E1E, 0x407E}, + {0x3E20, 0xCC41}, + {0x3E22, 0x8042}, + {0x3E24, 0x8043}, + {0x3E26, 0x8300}, + {0x3E28, 0xC088}, + {0x3E2A, 0x44BA}, + {0x3E2C, 0x4488}, + {0x3E2E, 0x00C8}, + {0x3E30, 0x8042}, + {0x3E32, 0x4181}, + {0x3E34, 0x4082}, + {0x3E36, 0x4080}, + {0x3E38, 0x4180}, + {0x3E3A, 0x4280}, + {0x3E3C, 0x4383}, + {0x3E3E, 0x00C0}, + {0x3E40, 0x8844}, + {0x3E42, 0xBA44}, + {0x3E44, 0x8800}, + {0x3E46, 0xC880}, + {0x3E48, 0x4241}, + {0x3E4A, 0x8240}, + {0x3E4C, 0x8140}, + {0x3E4E, 0x8041}, + {0x3E50, 0x8042}, + {0x3E52, 0x8043}, + {0x3E54, 0x8300}, + {0x3E56, 0xC088}, + {0x3E58, 0x44BA}, + {0x3E5A, 0x4488}, + {0x3E5C, 0x00C8}, + {0x3E5E, 0x8042}, + {0x3E60, 0x4181}, + {0x3E62, 0x4082}, + {0x3E64, 0x4080}, + {0x3E66, 0x4180}, + {0x3E68, 0x4280}, + {0x3E6A, 0x4383}, + {0x3E6C, 0x00C0}, + {0x3E6E, 0x8844}, + {0x3E70, 0xBA44}, + {0x3E72, 0x8800}, + {0x3E74, 0xC880}, + {0x3E76, 0x4241}, + {0x3E78, 0x8140}, + {0x3E7A, 0x9F5E}, + {0x3E7C, 0x8A54}, + {0x3E7E, 0x8620}, + {0x3E80, 0x2881}, + {0x3E82, 0x6026}, + {0x3E84, 0x8055}, + {0x3E86, 0x8070}, + {0x3E88, 0x0000}, + {0x3E8A, 0x0000}, + {0x3E8C, 0x0000}, + {0x3E8E, 0x0000}, + {0x3E90, 0x0000}, + {0x3E92, 0x0000}, + {0x3E94, 0x0000}, + {0x3E96, 0x0000}, + {0x3E98, 0x0000}, + {0x3E9A, 0x0000}, + {0x3E9C, 0x0000}, + {0x3E9E, 0x0000}, + {0x3EA0, 0x0000}, + {0x3EA2, 0x0000}, + {0x3EA4, 0x0000}, + {0x3EA6, 0x0000}, + {0x3EA8, 0x0000}, + {0x3EAA, 0x0000}, + {0x3EAC, 0x0000}, + {0x3EAE, 0x0000}, + {0x3EB0, 0x0000}, + {0x3EB2, 0x0000}, + {0x3EB4, 0x0000}, + + {0x3EB6, 0x004D}, + {0x3EB8, 0x0F0C}, + {0x3EBC, 0xAA06}, + {0x3EC0, 0x1E02}, + {0x3EC2, 0x7700}, + {0x3EC4, 0x1308}, + {0x3EC6, 0xEA44}, + {0x3EC8, 0x0F0F}, + {0x3ECA, 0x0F4A}, + {0x3ECC, 0x0706}, + {0x3ECE, 0x443B}, + {0x3ED0, 0x12F0}, + {0x3ED2, 0x0039}, + {0x3ED4, 0x862F}, + {0x3ED6, 0x4080}, + {0x3ED8, 0x0523}, + {0x3EDA, 0xF896}, + {0x3EDC, 0x5096}, + {0x3EDE, 0x5005}, + {0x316A, 0x8200}, + {0x316E, 0x8200}, + {0x316C, 0x8200}, + {0x3EF0, 0x4D4D}, + {0x3EF2, 0x0101}, + {0x3EF6, 0x0307}, + {0x3EFA, 0x0F0F}, + {0x3EFC, 0x0F0F}, + {0x3EFE, 0x0F0F}, + + {0x31B0, 0x005C}, + {0x31B2, 0x002D}, + {0x31B4, 0x2412}, + {0x31B6, 0x142A}, + {0x31B8, 0x2413}, + {0x31BA, 0x1C70}, + {0x31BC, 0x868B}, + {0x31AE, 0x0204}, + + {0x0300, 0x0005}, + {0x0302, 0x0001}, + {0x0304, 0x0101}, + {0x0306, 0x2E2E}, + {0x0308, 0x000A}, + {0x030A, 0x0001}, + {0x0112, 0x0A0A}, + {0x3016, 0x0101}, + {AR1335_TABLE_WAIT_MS, AR1335_WAIT_MS}, + + {0x0344, 0x0010}, + {0x0348, 0x107F}, + {0x0346, 0x0010}, + {0x034A, 0x0C3F}, + {0x034C, 0x1070}, + {0x034E, 0x0C30}, + {0x3040, 0x0041}, + {0x0112, 0x0A0A}, + {0x0112, 0x0A0A}, + {0x3172, 0x0206}, + {0x317A, 0x416E}, + {0x3F3C, 0x0003}, + {0x0342, 0x1240}, + {0x0340, 0x0C4E}, + {0x0202, 0x0C44}, + + {0x31E0, 0x0781}, + {0x3F00, 0x004F}, + {0x3F02, 0x0125}, + {0x3F04, 0x0020}, + {0x3F06, 0x0040}, + {0x3F08, 0x0070}, + {0x3F0A, 0x0101}, + {0x3F0C, 0x0302}, + {0x3F1E, 0x0022}, + {0x3F1A, 0x01FF}, + {0x3F14, 0x0101}, + {0x3F44, 0x0707}, + {0x3F18, 0x011E}, + {0x3F12, 0x0303}, + {0x3F42, 0x1511}, + {0x3F16, 0x011E}, + {0x3F40, 0x1511}, + + {0x3F3C, 0x0003}, + {0x301A, 0x021C}, + {AR1335_TABLE_END, 0x00} +}; + +enum { + AR1335_MODE_4208X3120, +}; + +static struct ar1335_reg *mode_table[] = { + [AR1335_MODE_4208X3120] = mode_4208x3120, +}; + +static inline void +msleep_range(unsigned int delay_base) +{ + usleep_range(delay_base*1000, delay_base*1000+500); +} + +static inline void +ar1335_get_frame_length_regs(struct ar1335_reg *regs, u32 frame_length) +{ + regs->addr = AR1335_FRAME_LENGTH_ADDR; + regs->val = frame_length & 0xffff; +} + +static inline void +ar1335_get_coarse_time_regs(struct ar1335_reg *regs, u32 coarse_time) +{ + regs->addr = AR1335_COARSE_TIME_ADDR; + regs->val = coarse_time & 0xffff; +} + +static inline void +ar1335_get_gain_reg(struct ar1335_reg *regs, u16 gain) +{ + regs->addr = AR1335_GAIN_ADDR; + regs->val = gain; +} + +static inline int +ar1335_read_reg(struct ar1335_info *info, u16 addr, u16 *val) +{ + return regmap_read(info->regmap, addr, (unsigned int *) val); +} + +static int +ar1335_write_reg(struct ar1335_info *info, u16 addr, u16 val) +{ + int err; + + err = regmap_write(info->regmap, addr, val); + + if (err) + pr_err("%s:i2c write failed, %x = %x\n", + __func__, addr, val); + + return err; +} + +static int +ar1335_write_table(struct ar1335_info *info, + const struct ar1335_reg table[], + const struct ar1335_reg override_list[], + int num_override_regs) +{ + int err; + const struct ar1335_reg *next; + int i; + u16 val; + + for (next = table; next->addr != AR1335_TABLE_END; next++) { + if (next->addr == AR1335_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 = ar1335_write_reg(info, next->addr, val); + if (err) { + pr_err("%s:ar1335_write_table:%d", __func__, err); + return err; + } + } + return 0; +} + +static int ar1335_get_flash_cap(struct ar1335_info *info) +{ + struct ar1335_flash_control *fctl; + + dev_dbg(&info->i2c_client->dev, "%s: %p\n", __func__, info->pdata); + if (info->pdata) { + fctl = &info->pdata->flash_cap; + dev_dbg(&info->i2c_client->dev, + "edg: %x, st: %x, rpt: %x, dl: %x\n", + fctl->edge_trig_en, + fctl->start_edge, + fctl->repeat, + fctl->delay_frm); + + if (fctl->enable) + return 0; + } + return -ENODEV; +} + +static inline int ar1335_set_flash_control( + struct ar1335_info *info, struct ar1335_flash_control *fc) +{ + dev_dbg(&info->i2c_client->dev, "%s\n", __func__); + return ar1335_write_reg(info, 0x0802, 0x01); +} + +static int +ar1335_set_mode(struct ar1335_info *info, struct ar1335_mode *mode) +{ + int sensor_mode; + int err; + struct ar1335_reg reg_list[8]; + + pr_info("%s: xres %u yres %u framelength %u coarsetime %u gain %u\n", + __func__, mode->xres, mode->yres, mode->frame_length, + mode->coarse_time, mode->gain); + + if (mode->xres == 4208 && mode->yres == 3120) { + sensor_mode = AR1335_MODE_4208X3120; + } 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. */ + ar1335_get_frame_length_regs(reg_list, mode->frame_length); + ar1335_get_coarse_time_regs(reg_list + 1, mode->coarse_time); + ar1335_get_gain_reg(reg_list + 2, mode->gain); + + err = ar1335_write_table(info, + mode_table[sensor_mode], + reg_list, 3); + if (err) + return err; + info->mode = sensor_mode; + pr_info("[AR1335]: stream on.\n"); + return 0; +} + +static int +ar1335_get_status(struct ar1335_info *info, u8 *dev_status) +{ + *dev_status = 0; + return 0; +} + +static int +ar1335_set_frame_length(struct ar1335_info *info, u32 frame_length, + bool group_hold) +{ + struct ar1335_reg reg_list[2]; + int i = 0; + int ret; + + ar1335_get_frame_length_regs(reg_list, frame_length); + + if (group_hold) { + ret = ar1335_write_reg(info, 0x0104, 0x01); + if (ret) + return ret; + } + + for (i = 0; i < 1; i++) { + ret = ar1335_write_reg(info, reg_list[i].addr, + reg_list[i].val); + if (ret) + return ret; + } + + if (group_hold) { + ret = ar1335_write_reg(info, 0x0104, 0x0); + if (ret) + return ret; + } + + return 0; +} + +static int +ar1335_set_coarse_time(struct ar1335_info *info, u32 coarse_time, + bool group_hold) +{ + int ret; + + struct ar1335_reg reg_list[2]; + int i = 0; + + ar1335_get_coarse_time_regs(reg_list, coarse_time); + + if (group_hold) { + ret = ar1335_write_reg(info, 0x104, 0x01); + if (ret) + return ret; + } + + for (i = 0; i < 1; i++) { + ret = ar1335_write_reg(info, reg_list[i].addr, + reg_list[i].val); + if (ret) + return ret; + } + + if (group_hold) { + ret = ar1335_write_reg(info, 0x104, 0x0); + if (ret) + return ret; + } + return 0; +} + +static int +ar1335_set_gain(struct ar1335_info *info, u16 gain, bool group_hold) +{ + int ret; + struct ar1335_reg reg_list; + + ar1335_get_gain_reg(®_list, gain); + + if (group_hold) { + ret = ar1335_write_reg(info, 0x104, 0x1); + if (ret) + return ret; + } + + ret = ar1335_write_reg(info, reg_list.addr, reg_list.val); + if (ret) + return ret; + + if (group_hold) { + ret = ar1335_write_reg(info, 0x104, 0x0); + if (ret) + return ret; + } + return 0; +} + +static int +ar1335_set_group_hold(struct ar1335_info *info, struct ar1335_ae *ae) +{ + int ret; + int count = 0; + bool group_hold_enabled = false; + + if (ae->gain_enable) + count++; + if (ae->coarse_time_enable) + count++; + if (ae->frame_length_enable) + count++; + if (count >= 2) + group_hold_enabled = true; + + if (group_hold_enabled) { + ret = ar1335_write_reg(info, 0x104, 0x1); + if (ret) + return ret; + } + + if (ae->gain_enable) + ar1335_set_gain(info, ae->gain, true); + if (ae->coarse_time_enable) + ar1335_set_coarse_time(info, ae->coarse_time, true); + if (ae->frame_length_enable) + ar1335_set_frame_length(info, ae->frame_length, true); + + if (group_hold_enabled) { + ret = ar1335_write_reg(info, 0x104, 0x0); + if (ret) + return ret; + } + + return 0; +} + +static int ar1335_get_sensor_id(struct ar1335_info *info) +{ + int ret = 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 |= ar1335_write_reg(info, 0x3B02, 0x00); + ret |= ar1335_write_reg(info, 0x3B00, 0x01); + for (i = 0; i < 9; i++) { + ret |= ar1335_read_reg(info, 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 void ar1335_mclk_disable(struct ar1335_info *info) +{ + dev_dbg(&info->i2c_client->dev, "%s: disable MCLK\n", __func__); + clk_disable_unprepare(info->mclk); +} + +static int ar1335_mclk_enable(struct ar1335_info *info) +{ + int err; + unsigned long mclk_init_rate = 24000000; + + dev_dbg(&info->i2c_client->dev, "%s: enable MCLK with %lu Hz\n", + __func__, mclk_init_rate); + + err = clk_set_rate(info->mclk, mclk_init_rate); + if (!err) + err = clk_prepare_enable(info->mclk); + return err; +} + +static long +ar1335_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int err = 0; + struct ar1335_info *info = file->private_data; + + switch (cmd) { + case AR1335_IOCTL_SET_POWER: + if (!info->pdata) + break; + if (arg && info->pdata->power_on) { + err = ar1335_mclk_enable(info); + if (!err) + err = info->pdata->power_on(&info->power); + if (err < 0) + ar1335_mclk_disable(info); + } + if (!arg && info->pdata->power_off) { + info->pdata->power_off(&info->power); + ar1335_mclk_disable(info); + } + break; + case AR1335_IOCTL_SET_MODE: + { + struct ar1335_mode mode; + if (copy_from_user(&mode, (const void __user *)arg, + sizeof(struct ar1335_mode))) { + pr_err("%s:Failed to get mode from user.\n", __func__); + return -EFAULT; + } + return ar1335_set_mode(info, &mode); + } + case AR1335_IOCTL_SET_FRAME_LENGTH: + return ar1335_set_frame_length(info, (u32)arg, true); + case AR1335_IOCTL_SET_COARSE_TIME: + return ar1335_set_coarse_time(info, (u32)arg, true); + case AR1335_IOCTL_SET_GAIN: + return ar1335_set_gain(info, (u16)arg, true); + case AR1335_IOCTL_GET_STATUS: + { + u8 status; + + err = ar1335_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 AR1335_IOCTL_GET_SENSORDATA: + { + err = ar1335_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 ar1335_sensordata))) { + pr_info("%s:Failed to copy fuse id to user space\n", + __func__); + return -EFAULT; + } + return 0; + } + case AR1335_IOCTL_SET_GROUP_HOLD: + { + struct ar1335_ae ae; + if (copy_from_user(&ae, (const void __user *)arg, + sizeof(struct ar1335_ae))) { + pr_info("%s:fail group hold\n", __func__); + return -EFAULT; + } + return ar1335_set_group_hold(info, &ae); + } + case AR1335_IOCTL_SET_FLASH_MODE: + { + struct ar1335_flash_control values; + + dev_dbg(&info->i2c_client->dev, + "AR1335_IOCTL_SET_FLASH_MODE\n"); + if (copy_from_user(&values, + (const void __user *)arg, + sizeof(struct ar1335_flash_control))) { + err = -EFAULT; + break; + } + err = ar1335_set_flash_control(info, &values); + break; + } + case AR1335_IOCTL_GET_FLASH_CAP: + err = ar1335_get_flash_cap(info); + break; + default: + pr_err("%s:unknown cmd.\n", __func__); + err = -EINVAL; + } + + return err; +} + +static int ar1335_debugfs_show(struct seq_file *s, void *unused) +{ + struct ar1335_info *dev = s->private; + + dev_dbg(&dev->i2c_client->dev, "%s: ++\n", __func__); + + return 0; +} + +static ssize_t ar1335_debugfs_write( + struct file *file, + char const __user *buf, + size_t count, + loff_t *offset) +{ + struct ar1335_info *dev = + ((struct seq_file *)file->private_data)->private; + struct i2c_client *i2c_client = dev->i2c_client; + int ret = 0; + char buffer[MAX_BUFFER_SIZE]; + u32 address; + u32 data; + u16 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 |= ar1335_write_reg(dev, address, data); +read: + ret |= ar1335_read_reg(dev, 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 ar1335_debugfs_open(struct inode *inode, struct file *file) +{ + struct ar1335_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, ar1335_debugfs_show, inode->i_private); +} + +static const struct file_operations ar1335_debugfs_fops = { + .open = ar1335_debugfs_open, + .read = seq_read, + .write = ar1335_debugfs_write, + .llseek = seq_lseek, + .release = single_release, +}; + +static void ar1335_remove_debugfs(struct ar1335_info *dev) +{ + struct i2c_client *i2c_client = dev->i2c_client; + + dev_dbg(&i2c_client->dev, "%s: ++\n", __func__); + + debugfs_remove_recursive(dev->debugdir); + dev->debugdir = NULL; +} + +static void ar1335_create_debugfs(struct ar1335_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, + &ar1335_debugfs_fops); + if (!ret) + goto remove_debugfs; + + return; +remove_debugfs: + dev_err(&i2c_client->dev, "couldn't create debugfs\n"); + ar1335_remove_debugfs(dev); +} + +static int ar1335_power_on(struct ar1335_power_rail *pw) +{ + int err; + struct ar1335_info *info = container_of(pw, struct ar1335_info, power); + + if (unlikely(WARN_ON(!pw || !pw->iovdd || !pw->avdd))) + return -EFAULT; + + gpio_set_value(info->pdata->reset_gpio, 0); + gpio_set_value(info->pdata->af_gpio, 1); + gpio_set_value(info->pdata->cam1_gpio, 0); + usleep_range(10, 20); + + err = regulator_enable(pw->avdd); + if (err) + goto ar1335_avdd_fail; + + err = regulator_enable(pw->iovdd); + if (err) + goto ar1335_iovdd_fail; + + usleep_range(1, 2); + gpio_set_value(info->pdata->reset_gpio, 1); + gpio_set_value(info->pdata->cam1_gpio, 1); + + usleep_range(300, 310); + + return 1; + + +ar1335_iovdd_fail: + regulator_disable(pw->avdd); + +ar1335_avdd_fail: + pr_err("%s failed.\n", __func__); + return -ENODEV; +} + +static int ar1335_power_off(struct ar1335_power_rail *pw) +{ + struct ar1335_info *info = container_of(pw, struct ar1335_info, power); + + if (unlikely(WARN_ON(!pw || !pw->iovdd || !pw->avdd))) + return -EFAULT; + + usleep_range(1, 2); + gpio_set_value(info->pdata->cam1_gpio, 0); + usleep_range(1, 2); + + regulator_disable(pw->iovdd); + regulator_disable(pw->avdd); + + return 0; +} + +static int +ar1335_open(struct inode *inode, struct file *file) +{ + struct miscdevice *miscdev = file->private_data; + struct ar1335_info *info; + + info = container_of(miscdev, struct ar1335_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; + + return 0; +} + +static int +ar1335_release(struct inode *inode, struct file *file) +{ + struct ar1335_info *info = file->private_data; + + file->private_data = NULL; + + /* warn if device is already released */ + WARN_ON(!atomic_xchg(&info->in_use, 0)); + return 0; +} + +static int ar1335_power_put(struct ar1335_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 ar1335_regulator_get(struct ar1335_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 ar1335_power_get(struct ar1335_info *info) +{ + struct ar1335_power_rail *pw = &info->power; + int err = 0; + + err |= ar1335_regulator_get(info, &pw->avdd, "vana"); /* ananlog 2.7v */ + err |= ar1335_regulator_get(info, &pw->dvdd, "vdig_csi"); /* dig 1.2v */ + err |= ar1335_regulator_get(info, &pw->iovdd, "vif"); /* IO 1.8v */ + + return err; +} + +static const struct file_operations ar1335_fileops = { + .owner = THIS_MODULE, + .open = ar1335_open, + .unlocked_ioctl = ar1335_ioctl, + .release = ar1335_release, +}; + +static struct miscdevice ar1335_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "ar1335", + .fops = &ar1335_fileops, +}; + +static struct of_device_id ar1335_of_match[] = { + { .compatible = "nvidia,ar1335", }, + { }, +}; + +MODULE_DEVICE_TABLE(of, ar1335_of_match); + +static struct ar1335_platform_data *ar1335_parse_dt(struct i2c_client *client) +{ + struct device_node *np = client->dev.of_node; + struct ar1335_platform_data *board_info_pdata; + const struct of_device_id *match; + + match = of_match_device(ar1335_of_match, &client->dev); + if (!match) { + dev_err(&client->dev, "Failed to find matching dt id\n"); + return NULL; + } + + board_info_pdata = devm_kzalloc(&client->dev, sizeof(*board_info_pdata), + GFP_KERNEL); + if (!board_info_pdata) { + dev_err(&client->dev, "Failed to allocate pdata\n"); + return NULL; + } + + board_info_pdata->cam1_gpio = of_get_named_gpio(np, "cam1-gpios", 0); + board_info_pdata->reset_gpio = of_get_named_gpio(np, "reset-gpios", 0); + board_info_pdata->af_gpio = of_get_named_gpio(np, "af-gpios", 0); + + board_info_pdata->power_on = ar1335_power_on; + board_info_pdata->power_off = ar1335_power_off; + + return board_info_pdata; +} + +static int +ar1335_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ar1335_info *info; + int err; + const char *mclk_name; + + pr_err("[AR1335]: probing sensor.\n"); + + info = devm_kzalloc(&client->dev, + sizeof(struct ar1335_info), GFP_KERNEL); + if (!info) { + pr_err("%s:Unable to allocate memory!\n", __func__); + return -ENOMEM; + } + + info->regmap = devm_regmap_init_i2c(client, &sensor_regmap_config); + if (IS_ERR(info->regmap)) { + dev_err(&client->dev, + "regmap init failed: %ld\n", PTR_ERR(info->regmap)); + return -ENODEV; + } + + if (client->dev.of_node) + info->pdata = ar1335_parse_dt(client); + else + info->pdata = client->dev.platform_data; + + if (!info->pdata) { + pr_err("[AR1335]:%s:Unable to get platform data\n", __func__); + return -EFAULT; + } + + info->i2c_client = client; + atomic_set(&info->in_use, 0); + info->mode = -1; + + mclk_name = info->pdata->mclk_name ? + info->pdata->mclk_name : "default_mclk"; + info->mclk = devm_clk_get(&client->dev, mclk_name); + if (IS_ERR(info->mclk)) { + dev_err(&client->dev, "%s: unable to get clock %s\n", + __func__, mclk_name); + return PTR_ERR(info->mclk); + } + + ar1335_power_get(info); + + memcpy(&info->miscdev_info, + &ar1335_device, + sizeof(struct miscdevice)); + + err = misc_register(&info->miscdev_info); + if (err) { + pr_err("%s:Unable to register misc device!\n", __func__); + goto ar1335_probe_fail; + } + + i2c_set_clientdata(client, info); + /* create debugfs interface */ + ar1335_create_debugfs(info); + pr_err("[AR1335]: end of probing sensor.\n"); + return 0; + +ar1335_probe_fail: + ar1335_power_put(&info->power); + + return err; +} + +static int +ar1335_remove(struct i2c_client *client) +{ + struct ar1335_info *info; + info = i2c_get_clientdata(client); + misc_deregister(&ar1335_device); + + ar1335_power_put(&info->power); + + ar1335_remove_debugfs(info); + return 0; +} + +static const struct i2c_device_id ar1335_id[] = { + { "ar1335", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, ar1335_id); + +static struct i2c_driver ar1335_i2c_driver = { + .driver = { + .name = "ar1335", + .owner = THIS_MODULE, + }, + .probe = ar1335_probe, + .remove = ar1335_remove, + .id_table = ar1335_id, +}; + +static int __init ar1335_init(void) +{ + pr_info("[AR1335] sensor driver loading\n"); + return i2c_add_driver(&ar1335_i2c_driver); +} + +static void __exit ar1335_exit(void) +{ + i2c_del_driver(&ar1335_i2c_driver); +} + +module_init(ar1335_init); +module_exit(ar1335_exit); |