diff options
-rw-r--r-- | arch/arm/boot/dts/tegra124-jetson_tk1-pm375-000-c00-00.dts | 13 | ||||
-rw-r--r-- | arch/arm/boot/dts/tegra124-platforms/tegra124-pm359-camera-a00.dtsi | 46 | ||||
-rw-r--r-- | arch/arm/configs/tegra12_defconfig | 1 | ||||
-rw-r--r-- | arch/arm/mach-tegra/board-ardbeg-sensors.c | 44 | ||||
-rw-r--r-- | drivers/media/platform/tegra/Kconfig | 7 | ||||
-rw-r--r-- | drivers/media/platform/tegra/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/platform/tegra/ov4689.c | 993 | ||||
-rw-r--r-- | drivers/media/platform/tegra/ov4689_tables.h | 565 | ||||
-rw-r--r-- | include/media/ov4689.h | 77 |
9 files changed, 1745 insertions, 2 deletions
diff --git a/arch/arm/boot/dts/tegra124-jetson_tk1-pm375-000-c00-00.dts b/arch/arm/boot/dts/tegra124-jetson_tk1-pm375-000-c00-00.dts index 87dbc0fc389e..9d3559b4e0dd 100644 --- a/arch/arm/boot/dts/tegra124-jetson_tk1-pm375-000-c00-00.dts +++ b/arch/arm/boot/dts/tegra124-jetson_tk1-pm375-000-c00-00.dts @@ -67,6 +67,19 @@ reg = <0x0 0x80000000 0x0 0x80000000>; }; + camera-pcl { + profiles { + ov4689@2_0036 { + use_of_node = "yes"; + reset-gpios = <&gpio TEGRA_GPIO(BB, 3) 0>; + cam1-gpios = <&gpio TEGRA_GPIO(BB, 5) 0>; + }; + }; + dpd { + default-enable; + }; + }; + spi@7000d400 { status = "okay"; spi-max-frequency = <25000000>; diff --git a/arch/arm/boot/dts/tegra124-platforms/tegra124-pm359-camera-a00.dtsi b/arch/arm/boot/dts/tegra124-platforms/tegra124-pm359-camera-a00.dtsi index 566180d4cdab..e84ffcc1c87d 100644 --- a/arch/arm/boot/dts/tegra124-platforms/tegra124-pm359-camera-a00.dtsi +++ b/arch/arm/boot/dts/tegra124-platforms/tegra124-pm359-camera-a00.dtsi @@ -1,7 +1,7 @@ /* * arch/arm/boot/dts/tegra124-platforms/tegra124-pm359-camera-a00.dtsi * - * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * 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 of the GNU General Public License as published by @@ -71,6 +71,15 @@ platformdata = "ardbeg_ar0330_front_pdata"; }; }; + module5: module5@modules { + compatible = "sensor,rear"; + badge_info = "e1633_ov4689"; + + sensor { + profile = <&ov4689_1>; + platformdata = "ardbeg_ov4689_pdata"; + }; + }; }; profiles { imx135_1: imx135@2_0010 { @@ -295,6 +304,41 @@ CAMERA_END >; }; + ov4689_1: ov4689@2_0036 { + index = <6>; + chipname = "pcl_OV4689"; + type = "sensor"; + guid = "s_OV4689"; + position = <0>; + bustype = "i2c"; + busnum = <2>; + addr = <0x36>; + datalen = <2>; + pinmuxgrp = <0xFFFF>; + gpios = <3>; + regulators = "vana", "vdig", "vif"; + clocks = "mclk"; + drivername = "ov4689"; + detect = <0x0002 0x300A 0xFFFF 0x4688>; + devid = <0x4689>; + poweron = < + CAMERA_IND_CLK_SET(10000) + CAMERA_GPIO_CLR(221) + CAMERA_GPIO_CLR(219) + CAMERA_WAITMS(40) + CAMERA_REGULATOR_ON(2) + CAMERA_GPIO_SET(221) + CAMERA_GPIO_SET(219) + CAMERA_WAITMS(10) + CAMERA_END + >; + poweroff = < + CAMERA_GPIO_CLR(221) + CAMERA_IND_CLK_CLR + CAMERA_WAITUS(10) + CAMERA_END + >; + }; }; }; }; diff --git a/arch/arm/configs/tegra12_defconfig b/arch/arm/configs/tegra12_defconfig index 8c8691235c6d..d028d4a23ad5 100644 --- a/arch/arm/configs/tegra12_defconfig +++ b/arch/arm/configs/tegra12_defconfig @@ -346,6 +346,7 @@ CONFIG_VIDEO_AR0261=y CONFIG_VIDEO_AR0330=y CONFIG_VIDEO_IMX132=y CONFIG_VIDEO_OV9772=y +CONFIG_VIDEO_OV4689=y CONFIG_TORCH_SSL3250A=y CONFIG_MAX77665_FLASH=y CONFIG_TORCH_MAX77387=y diff --git a/arch/arm/mach-tegra/board-ardbeg-sensors.c b/arch/arm/mach-tegra/board-ardbeg-sensors.c index d89c2e1970f4..c2fd85030283 100644 --- a/arch/arm/mach-tegra/board-ardbeg-sensors.c +++ b/arch/arm/mach-tegra/board-ardbeg-sensors.c @@ -1,7 +1,7 @@ /* * arch/arm/mach-tegra/board-ardbeg-sensors.c * - * Copyright (c) 2013-2014, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2013-2015, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -39,6 +39,7 @@ #include <media/ad5823.h> #include <media/max77387.h> +#include <media/ov4689.h> #include <linux/platform_device.h> #include <media/soc_camera.h> #include <media/soc_camera_platform.h> @@ -464,6 +465,34 @@ struct ar0330_platform_data ardbeg_ar0330_data = { .dev_name = "ar0330", }; +static int ardbeg_ov4689_power_on(struct ov4689_power_rail *pw) +{ + pr_info("%s: ++\n", __func__); + /* disable CSIA/B IOs DPD mode to turn on camera for ardbeg */ + tegra_io_dpd_disable(&csia_io); + tegra_io_dpd_disable(&csib_io); + + gpio_set_value(TEGRA_GPIO_PBB5, 0); + usleep_range(10, 20); + gpio_set_value(TEGRA_GPIO_PBB5, 1); + usleep_range(820, 1000); + + return 1; +} + +static int ardbeg_ov4689_power_off(struct ov4689_power_rail *pw) +{ + pr_info("%s: ++\n", __func__); + + gpio_set_value(TEGRA_GPIO_PBB5, 0); + + /* put CSIA/B IOs into DPD mode to save additional power for ardbeg */ + tegra_io_dpd_enable(&csia_io); + tegra_io_dpd_enable(&csib_io); + + return 0; +} + static int ardbeg_ar0261_power_on(struct ar0261_power_rail *pw) { int err; @@ -753,6 +782,18 @@ struct imx179_platform_data ardbeg_imx179_data = { .power_off = ardbeg_imx179_power_off, }; +struct ov4689_platform_data ardbeg_ov4689_data = { + .flash_cap = { + .enable = 0, + .edge_trig_en = 1, + .start_edge = 0, + .repeat = 1, + .delay_frm = 0, + }, + .power_on = ardbeg_ov4689_power_on, + .power_off = ardbeg_ov4689_power_off, +}; + static int ardbeg_dw9718_power_on(struct dw9718_power_rail *pw) { int err; @@ -1285,6 +1326,7 @@ static struct camera_data_blob ardbeg_camera_lut[] = { {"ardbeg_ov5693f_pdata", &ardbeg_ov5693_front_pdata}, {"ardbeg_ar0330_pdata", &ardbeg_ar0330_data}, {"ardbeg_ar0330_front_pdata", &ardbeg_ar0330_front_data}, + {"ardbeg_ov4689_pdata", &ardbeg_ov4689_data}, {}, }; diff --git a/drivers/media/platform/tegra/Kconfig b/drivers/media/platform/tegra/Kconfig index 2b95077a2f2b..1bcd293e6260 100644 --- a/drivers/media/platform/tegra/Kconfig +++ b/drivers/media/platform/tegra/Kconfig @@ -127,6 +127,13 @@ config VIDEO_OV7695 This is a driver for the OV7695 YUV camera sensor device This sensor has external ISP, it isn't using tegra ISP. +config VIDEO_OV4689 + tristate "OV4689 camera sensor support" + depends on I2C && ARCH_TEGRA + ---help--- + This is a driver for the OV4689 camera sensor + for use with the tegra isp. + config TORCH_SSL3250A tristate "SSL3250A flash/torch support" depends on I2C && ARCH_TEGRA diff --git a/drivers/media/platform/tegra/Makefile b/drivers/media/platform/tegra/Makefile index 7a3b491cd7db..6c205b2ed0e1 100644 --- a/drivers/media/platform/tegra/Makefile +++ b/drivers/media/platform/tegra/Makefile @@ -43,4 +43,5 @@ obj-$(CONFIG_VIDEO_OV5693) += ov5693.o obj-$(CONFIG_VIDEO_AD5823) += ad5823.o obj-$(CONFIG_VIDEO_OV7695) += ov7695.o obj-$(CONFIG_VIDEO_MT9M114) += mt9m114.o +obj-$(CONFIG_VIDEO_OV4689) += ov4689.o obj-$(CONFIG_VIDEO_CAMERA) += camera.o diff --git a/drivers/media/platform/tegra/ov4689.c b/drivers/media/platform/tegra/ov4689.c new file mode 100644 index 000000000000..435322f69cc5 --- /dev/null +++ b/drivers/media/platform/tegra/ov4689.c @@ -0,0 +1,993 @@ +/* + * ov4682.c - ov4682 sensor driver + * + * Copyright (c) 2015, NVIDIA CORPORATION, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <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/ov4689.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" + +#include "ov4689_tables.h" + +#define MAX_BUFFER_SIZE 32 + +/* Frame length: R0x380e~R0x380f */ +#define OV4689_NUM_BYTES_FL (2) +/* Coarse time: R0x3500~R0x3502 */ +#define OV4689_NUM_BYTES_CT (3) +/* Gain: R0x3508~R0x3509 */ +#define OV4689_NUM_BYTES_GAIN (2) +/* Num of regs in override list */ +#define OV4689_NUM_BYTES_OVERRIDES (OV4689_NUM_BYTES_FL + \ + OV4689_NUM_BYTES_CT + \ + OV4689_NUM_BYTES_GAIN) + +#define OV4689_SUPPORT_SENSORID (1) +#define OV4689_SUPPORT_EEPROM (0) +#define OV4689_SUPPORT_DEBUGFS (1) + +struct ov4689_info { + struct miscdevice miscdev_info; + int mode; + struct ov4689_power_rail power; + struct ov4689_sensordata sensor_data; + struct i2c_client *i2c_client; + struct ov4689_platform_data *pdata; + struct clk *mclk; + struct regmap *regmap; +#if OV4689_SUPPORT_DEBUGFS + struct dentry *debugdir; +#endif + atomic_t in_use; +#if OV4689_SUPPORT_EEPROM + struct ov4689_eeprom_data eeprom[ov4689_EEPROM_NUM_BLOCKS]; + u8 eeprom_buf[ov4689_EEPROM_SIZE]; +#endif +}; + +static const struct regmap_config sensor_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, +}; + +static inline void +msleep_range(unsigned int delay_base) +{ + usleep_range(delay_base*1000, delay_base*1000+500); +} + +static inline void ov4689_get_frame_length_regs(struct ov4689_reg *regs, + u32 frame_length) +{ + /* 2 registers for FL, i.e., 2-byte FL */ + regs->addr = 0x380e; + regs->val = (frame_length >> 8) & 0xff; + (regs + 1)->addr = 0x380f; + (regs + 1)->val = (frame_length) & 0xff; +} + +static inline void ov4689_get_coarse_time_regs(struct ov4689_reg *regs, + u32 coarse_time) +{ + /* 3 registers for CT, i.e., 3-byte CT */ + regs->addr = 0x3500; + regs->val = (coarse_time >> 12) & 0xff; + (regs + 1)->addr = 0x3501; + (regs + 1)->val = (coarse_time >> 4) & 0xff; + (regs + 2)->addr = 0x3502; + (regs + 2)->val = (coarse_time & 0xf) << 4; +} + +static inline void ov4689_get_gain_reg(struct ov4689_reg *regs, + u16 gain) +{ + /* 2 register for gain, i.e., 2-byte gain */ + regs->addr = 0x3508; + regs->val = (gain >> 8) & 0xff; + (regs + 1)->addr = 0x3509; + (regs + 1)->val = gain & 0xff; +} + +static inline int ov4689_read_reg(struct ov4689_info *info, + u16 addr, u8 *val) +{ + return regmap_read(info->regmap, addr, (unsigned int *) val); +} + +static int ov4689_write_reg(struct ov4689_info *info, u16 addr, u8 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 ov4689_write_table(struct ov4689_info *info, + const struct ov4689_reg table[], + const struct ov4689_reg override_list[], + int num_override_regs) +{ + int err; + const struct ov4689_reg *next; + int i; + u16 val; + + for (next = table; next->addr != OV4689_TABLE_END; next++) { + if (next->addr == OV4689_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 = ov4689_write_reg(info, next->addr, val); + if (err) { + pr_err("%s:ov4689_write_table:%d", __func__, err); + return err; + } + } + return 0; +} + +static int ov4689_set_mode(struct ov4689_info *info, struct ov4689_mode *mode) +{ + int sensor_mode; + int err; + struct ov4689_reg reg_list[OV4689_NUM_BYTES_OVERRIDES]; + + if ((mode->xres == 2688 && mode->yres == 1520) || + (mode->xres == 2688 && mode->yres == 1504)) { + sensor_mode = OV4689_MODE_2688x1520; + pr_info("ov4689_set_mode 2688x1520\n"); + } else if (mode->xres == 1920 && mode->yres == 1080) { + sensor_mode = OV4689_MODE_1920x1080; + pr_info("ov4689_set_mode 1920x1080\n"); + } else { + pr_err("There is no this resolution no support %dX%d!!", + mode->xres, mode->yres); + return 1; + } + + /* get a list of override regs for the asking frame length, */ + /* coarse integration time, and gain. */ + ov4689_get_frame_length_regs(reg_list, mode->frame_length); + ov4689_get_coarse_time_regs(reg_list + OV4689_NUM_BYTES_FL, + mode->coarse_time); + ov4689_get_gain_reg(reg_list + OV4689_NUM_BYTES_FL + + OV4689_NUM_BYTES_CT, mode->gain); + + err = ov4689_write_table(info, mode_table[sensor_mode], + reg_list, OV4689_NUM_BYTES_OVERRIDES); + + info->mode = sensor_mode; + + pr_info("[ov4689]: stream on.\n"); + return 0; +} + +static int ov4689_get_status(struct ov4689_info *info, u8 *dev_status) +{ + *dev_status = 0; + return 0; +} + +static int ov4689_set_frame_length(struct ov4689_info *info, u32 frame_length, + bool group_hold) +{ + struct ov4689_reg reg_list[OV4689_NUM_BYTES_FL]; + int i = 0; + int ret; + + ov4689_get_frame_length_regs(reg_list, frame_length); + + if (group_hold) { + ret = ov4689_write_reg(info, 0x3208, 0x00); + if (ret) + return ret; + } + + for (i = 0; i < OV4689_NUM_BYTES_FL; i++) { + ret = ov4689_write_reg(info, reg_list[i].addr, + reg_list[i].val); + if (ret) + return ret; + } + + if (group_hold) { + ret = ov4689_write_reg(info, 0x3208, 0x10); + if (ret) + return ret; + ret = ov4689_write_reg(info, 0x3208, 0xA0); + if (ret) + return ret; + } + + return 0; +} + +static int ov4689_set_coarse_time(struct ov4689_info *info, u32 coarse_time, + bool group_hold) +{ + int ret; + + struct ov4689_reg reg_list[OV4689_NUM_BYTES_CT]; + int i = 0; + + ov4689_get_coarse_time_regs(reg_list, coarse_time); + + if (group_hold) { + ret = ov4689_write_reg(info, 0x3208, 0x00); + if (ret) + return ret; + } + + for (i = 0; i < OV4689_NUM_BYTES_CT; i++) { + ret = ov4689_write_reg(info, reg_list[i].addr, + reg_list[i].val); + if (ret) + return ret; + } + + if (group_hold) { + ret = ov4689_write_reg(info, 0x3208, 0x10); + if (ret) + return ret; + ret = ov4689_write_reg(info, 0x3208, 0xA0); + if (ret) + return ret; + } + return 0; +} + +static int ov4689_set_gain(struct ov4689_info *info, u16 gain, + bool group_hold) +{ + int ret, i; + struct ov4689_reg reg_list[OV4689_NUM_BYTES_GAIN]; + + ov4689_get_gain_reg(reg_list, gain); + + if (group_hold) { + ret = ov4689_write_reg(info, 0x3208, 0x00); + if (ret) + return ret; + } + + /* writing 1-byte gain */ + for (i = 0; i < OV4689_NUM_BYTES_GAIN; i++) { + ret = ov4689_write_reg(info, reg_list[i].addr, + reg_list[i].val); + if (ret) + return ret; + } + + if (group_hold) { + ret = ov4689_write_reg(info, 0x3208, 0x10); + if (ret) + return ret; + ret = ov4689_write_reg(info, 0x3208, 0xA0); + if (ret) + return ret; + } + return 0; +} + +static int ov4689_set_group_hold(struct ov4689_info *info, + + struct ov4689_grouphold *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 = ov4689_write_reg(info, 0x3208, 0x00); + if (ret) + return ret; + } + + if (ae->gain_enable) + ov4689_set_gain(info, ae->gain, false); + if (ae->coarse_time_enable) + ov4689_set_coarse_time(info, ae->coarse_time, false); + if (ae->frame_length_enable) + ov4689_set_frame_length(info, ae->frame_length, false); + + if (group_hold_enabled) { + ret = ov4689_write_reg(info, 0x3208, 0x10); + if (ret) + return ret; + ret = ov4689_write_reg(info, 0x3208, 0xA0); + if (ret) + return ret; + } + + return 0; +} + +#if OV4689_SUPPORT_SENSORID +static int ov4689_get_sensor_id(struct ov4689_info *info) +{ + int ret = 0; + int i; + u8 bak = 0; + + pr_info("%s\n", __func__); + if (info->sensor_data.fuse_id_size) + return 0; + + /* select bank 31 */ + ov4689_write_reg(info, 0x3d84, 31); + for (i = 0; i < 8; i++) { + ret |= ov4689_read_reg(info, 0x300A + i, &bak); + info->sensor_data.fuse_id[i] = bak; + } + + if (!ret) + info->sensor_data.fuse_id_size = 2; + + return ret; +} +#endif + +static void ov4689_mclk_disable(struct ov4689_info *info) +{ + dev_dbg(&info->i2c_client->dev, "%s: disable MCLK\n", __func__); + clk_disable_unprepare(info->mclk); +} + +static int ov4689_mclk_enable(struct ov4689_info *info) +{ + int err; + unsigned long mclk_init_rate = 24000000; + + dev_info(&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; +} + +#if OV4689_SUPPORT_EEPROM +static int ov4689_eeprom_device_release(struct ov4689_info *info) +{ + int i; + + for (i = 0; i < ov4689_EEPROM_NUM_BLOCKS; i++) { + if (info->eeprom[i].i2c_client != NULL) { + i2c_unregister_device(info->eeprom[i].i2c_client); + info->eeprom[i].i2c_client = NULL; + } + } + + return 0; +} + +static int ov4689_eeprom_device_init(struct ov4689_info *info) +{ + char *dev_name = "eeprom_ov4689"; + static struct regmap_config eeprom_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + }; + int i; + int err; + + for (i = 0; i < ov4689_EEPROM_NUM_BLOCKS; i++) { + info->eeprom[i].adap = i2c_get_adapter( + info->i2c_client->adapter->nr); + memset(&info->eeprom[i].brd, 0, sizeof(info->eeprom[i].brd)); + strncpy(info->eeprom[i].brd.type, dev_name, + sizeof(info->eeprom[i].brd.type)); + info->eeprom[i].brd.addr = ov4689_EEPROM_ADDRESS + i; + info->eeprom[i].i2c_client = i2c_new_device( + info->eeprom[i].adap, &info->eeprom[i].brd); + + info->eeprom[i].regmap = devm_regmap_init_i2c( + info->eeprom[i].i2c_client, &eeprom_regmap_config); + if (IS_ERR(info->eeprom[i].regmap)) { + err = PTR_ERR(info->eeprom[i].regmap); + ov4689_eeprom_device_release(info); + return err; + } + } + + return 0; +} + +static int ov4689_read_eeprom(struct ov4689_info *info, + u8 reg, u16 length, u8 *buf) +{ + return regmap_raw_read(info->eeprom[0].regmap, reg, &buf[reg], length); +} + +static int ov4689_write_eeprom(struct ov4689_info *info, + u16 addr, u8 val) +{ + return regmap_write(info->eeprom[addr >> 8].regmap, addr & 0xFF, val); +} +#endif + +static long OV4689_IOCTL(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int err = 0; + struct ov4689_info *info = file->private_data; + + switch (_IOC_NR(cmd)) { + case _IOC_NR(OV4689_IOCTL_SET_POWER): + if (!info->pdata) + break; + if (arg && info->pdata->power_on) { + err = ov4689_mclk_enable(info); + if (!err) + err = info->pdata->power_on(&info->power); + if (err < 0) + ov4689_mclk_disable(info); + } + if (!arg && info->pdata->power_off) { + info->pdata->power_off(&info->power); + ov4689_mclk_disable(info); + } + break; + case _IOC_NR(OV4689_IOCTL_SET_MODE): + { + struct ov4689_mode mode; + if (copy_from_user(&mode, (const void __user *)arg, + sizeof(struct ov4689_mode))) { + pr_err("%s:Failed to get mode from user.\n", __func__); + return -EFAULT; + } + return ov4689_set_mode(info, &mode); + } + case _IOC_NR(OV4689_IOCTL_SET_FRAME_LENGTH): + return ov4689_set_frame_length(info, (u32)arg, true); + case _IOC_NR(OV4689_IOCTL_SET_COARSE_TIME): + return ov4689_set_coarse_time(info, (u32)arg, true); + case _IOC_NR(OV4689_IOCTL_SET_GAIN): + return ov4689_set_gain(info, (u16)arg, true); + case _IOC_NR(OV4689_IOCTL_GET_STATUS): + { + u8 status; + + err = ov4689_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; + } +#if OV4689_SUPPORT_SENSORID + case _IOC_NR(OV4689_IOCTL_GET_SENSORDATA): + { + err = ov4689_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 ov4689_sensordata))) { + pr_info("%s:Failed to copy fuse id to user space\n", + __func__); + return -EFAULT; + } + return 0; + } +#endif + case _IOC_NR(OV4689_IOCTL_SET_GROUP_HOLD): + { + struct ov4689_grouphold grouphold; + if (copy_from_user(&grouphold, (const void __user *)arg, + sizeof(struct ov4689_grouphold))) { + pr_info("%s:fail group hold\n", __func__); + return -EFAULT; + } + return ov4689_set_group_hold(info, &grouphold); + } +#if OV4689_SUPPORT_EEPROM + /* there is actually one of them really in use + NVC_IOCTL_GET_EEPROM_DATA + or + OV4689_IOCTL_GET_SENSORDATA (legacy?) + */ + case _IOC_NR(NVC_IOCTL_GET_EEPROM_DATA): + { + ov4689_read_eeprom(info, + 0, + ov4689_EEPROM_SIZE, + info->eeprom_buf); + + if (copy_to_user((void __user *)arg, + info->eeprom_buf, ov4689_EEPROM_SIZE)) { + dev_err(&info->i2c_client->dev, + "%s:Failed to copy status to user\n", + __func__); + return -EFAULT; + } + return 0; + } + + case _IOC_NR(NVC_IOCTL_SET_EEPROM_DATA): + { + int i; + if (copy_from_user(info->eeprom_buf, + (const void __user *)arg, ov4689_EEPROM_SIZE)) { + dev_err(&info->i2c_client->dev, + "%s:Failed to read from user buffer\n", + __func__); + return -EFAULT; + } + for (i = 0; i < ov4689_EEPROM_SIZE; i++) { + ov4689_write_eeprom(info, + i, + info->eeprom_buf[i]); + msleep(20); + } + return 0; + } +#endif + + default: + pr_err("%s:unknown cmd.\n", __func__); + err = -EINVAL; + } + + return err; +} + +#if OV4689_SUPPORT_DEBUGFS +static int ov4689_debugfs_show(struct seq_file *s, void *unused) +{ + struct ov4689_info *dev = s->private; + + dev_dbg(&dev->i2c_client->dev, "%s: ++\n", __func__); + + return 0; +} + +static ssize_t ov4689_debugfs_write(struct file *file, char const __user *buf, + size_t count, loff_t *offset) +{ + struct ov4689_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; + 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 |= ov4689_write_reg(dev, address, data); +read: + ret |= ov4689_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 ov4689_debugfs_open(struct inode *inode, struct file *file) +{ + struct ov4689_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, ov4689_debugfs_show, inode->i_private); +} + +static const struct file_operations ov4689_debugfs_fops = { + .open = ov4689_debugfs_open, + .read = seq_read, + .write = ov4689_debugfs_write, + .llseek = seq_lseek, + .release = single_release, +}; + +static void ov4689_remove_debugfs(struct ov4689_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 ov4689_create_debugfs(struct ov4689_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, + &ov4689_debugfs_fops); + if (!ret) + goto remove_debugfs; + + return; +remove_debugfs: + dev_err(&i2c_client->dev, "couldn't create debugfs\n"); + ov4689_remove_debugfs(dev); +} +#endif + +static int ov4689_power_on(struct ov4689_power_rail *pw) +{ + struct ov4689_info *info = container_of(pw, struct ov4689_info, power); + + gpio_set_value(info->pdata->reset_gpio, 0); + usleep_range(10, 20); + gpio_set_value(info->pdata->reset_gpio, 1); + usleep_range(1000, 1100); + + return 0; +} + +static int ov4689_power_off(struct ov4689_power_rail *pw) +{ + struct ov4689_info *info = container_of(pw, struct ov4689_info, power); + + gpio_set_value(info->pdata->reset_gpio, 0); + return 0; +} + +static int ov4689_open(struct inode *inode, struct file *file) +{ + struct miscdevice *miscdev = file->private_data; + struct ov4689_info *info; + + info = container_of(miscdev, struct ov4689_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 ov4689_release(struct inode *inode, struct file *file) +{ + struct ov4689_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 ov4689_power_put(struct ov4689_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 ov4689_regulator_get(struct ov4689_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 ov4689_power_get(struct ov4689_info *info) +{ + struct ov4689_power_rail *pw = &info->power; + int err = 0; + + err |= ov4689_regulator_get(info, &pw->avdd, "vana"); /* ananlog 2.7v */ + err |= ov4689_regulator_get(info, &pw->dvdd, "vdig"); /* digital 1.2v */ + err |= ov4689_regulator_get(info, &pw->iovdd, "vif"); /* IO 1.8v */ + + return err; +} + +static const struct file_operations ov4689_fileops = { + .owner = THIS_MODULE, + .open = ov4689_open, + .unlocked_ioctl = OV4689_IOCTL, +#ifdef CONFIG_COMPAT + .compat_ioctl = OV4689_IOCTL, +#endif + .release = ov4689_release, +}; + +static struct miscdevice ov4689_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "ov4689", + .fops = &ov4689_fileops, +}; + +static struct ov4689_platform_data *ov4689_parse_dt(struct i2c_client *client) +{ + struct device_node *np = client->dev.of_node; + struct ov4689_platform_data *board_info_pdata; + + 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; + } + + of_property_read_string(np, "clocks", &board_info_pdata->mclk_name); + board_info_pdata->reset_gpio = of_get_named_gpio(np, "reset-gpios", 0); + + board_info_pdata->power_on = ov4689_power_on; + board_info_pdata->power_off = ov4689_power_off; + + return board_info_pdata; +} + +static int ov4689_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ov4689_info *info; + int err; + const char *mclk_name; + + pr_info("[ov4689]: probing sensor.\n"); + + info = devm_kzalloc(&client->dev, + sizeof(struct ov4689_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 = ov4689_parse_dt(client); + else + info->pdata = client->dev.platform_data; + + if (!info->pdata) { + pr_err("[ov4689]:%s:Unable to get platform data\n", __func__); + return -EFAULT; + } + + gpio_request_one(info->pdata->reset_gpio, GPIOF_OUT_INIT_LOW, + "reset_gpio"); + + 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); + } + + ov4689_power_get(info); + + memcpy(&info->miscdev_info, + &ov4689_device, + sizeof(struct miscdevice)); + + err = misc_register(&info->miscdev_info); + if (err) { + pr_err("%s:Unable to register misc device!\n", __func__); + goto ov4689_probe_fail; + } + + i2c_set_clientdata(client, info); + +#if OV4689_SUPPORT_EEPROM + /* eeprom interface */ + err = ov4689_eeprom_device_init(info); + if (err) { + dev_err(&client->dev, + "Failed to allocate eeprom register map: %d\n", err); + return err; + } +#endif + +#if OV4689_SUPPORT_DEBUGFS + /* create debugfs interface */ + ov4689_create_debugfs(info); +#endif + return 0; + +ov4689_probe_fail: + ov4689_power_put(&info->power); + + return err; +} + +static int ov4689_remove(struct i2c_client *client) +{ + struct ov4689_info *info; + info = i2c_get_clientdata(client); + misc_deregister(&ov4689_device); + + ov4689_power_put(&info->power); + +#if OV4689_SUPPORT_DEBUGFS + ov4689_remove_debugfs(info); +#endif + +#if OV4689_SUPPORT_EEPROM + ov4689_eeprom_device_release(info); +#endif + return 0; +} + +static const struct i2c_device_id ov4689_id[] = { + { "ov4689", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, ov4689_id); + +static struct i2c_driver ov4689_i2c_driver = { + .driver = { + .name = "ov4689", + .owner = THIS_MODULE, + }, + .probe = ov4689_probe, + .remove = ov4689_remove, + .id_table = ov4689_id, +}; + + +static int __init +ov4689_init(void) +{ + pr_info("[ov4689] sensor driver loading\n"); + return i2c_add_driver(&ov4689_i2c_driver); +} + +static void __exit +ov4689_exit(void) +{ + i2c_del_driver(&ov4689_i2c_driver); +} + +module_init(ov4689_init); +module_exit(ov4689_exit); diff --git a/drivers/media/platform/tegra/ov4689_tables.h b/drivers/media/platform/tegra/ov4689_tables.h new file mode 100644 index 000000000000..7233d60ad311 --- /dev/null +++ b/drivers/media/platform/tegra/ov4689_tables.h @@ -0,0 +1,565 @@ +/* + * ov4689_tables.h + * + * Copyright (c) 2015, NVIDIA CORPORATION, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef OV4689_I2C_TABLES +#define OV4689_I2C_TABLES + +struct ov4689_reg { + u16 addr; + u16 val; +}; + +#define OV4689_TABLE_WAIT_MS 0 +#define OV4689_TABLE_END 1 + +static struct ov4689_reg ov4689_mode_2688x1520_initial[] = { + {0x0103, 0x01}, + {0x3638, 0x00}, + {0x0300, 0x00}, + /* 1008Mbps MIPI_CLK */ + {0x0302, 0x2a}, + {0x0304, 0x03}, + {0x030b, 0x00}, + {0x030d, 0x1e}, + {0x030e, 0x04}, + {0x030f, 0x01}, + {0x0312, 0x01}, + {0x031e, 0x00}, + {0x3000, 0x20}, + {0x3002, 0x00}, + {0x3018, 0x72}, + {0x3020, 0x93}, + {0x3021, 0x03}, + {0x3022, 0x01}, + {0x3031, 0x0a}, + {0x303f, 0x0c}, + {0x3305, 0xf1}, + {0x3307, 0x04}, + {0x3309, 0x29}, + {0x3500, 0x00}, + {0x3501, 0x60}, + {0x3502, 0x00}, + {0x3503, 0x04}, + {0x3504, 0x00}, + {0x3505, 0x00}, + {0x3506, 0x00}, + {0x3507, 0x00}, + {0x3508, 0x00}, + {0x3509, 0x80}, + {0x350a, 0x00}, + {0x350b, 0x00}, + {0x350c, 0x00}, + {0x350d, 0x00}, + {0x350e, 0x00}, + {0x350f, 0x80}, + {0x3510, 0x00}, + {0x3511, 0x00}, + {0x3512, 0x00}, + {0x3513, 0x00}, + {0x3514, 0x00}, + {0x3515, 0x80}, + {0x3516, 0x00}, + {0x3517, 0x00}, + {0x3518, 0x00}, + {0x3519, 0x00}, + {0x351a, 0x00}, + {0x351b, 0x80}, + {0x351c, 0x00}, + {0x351d, 0x00}, + {0x351e, 0x00}, + {0x351f, 0x00}, + {0x3520, 0x00}, + {0x3521, 0x80}, + {0x3522, 0x08}, + {0x3524, 0x08}, + {0x3526, 0x08}, + {0x3528, 0x08}, + {0x352a, 0x08}, + {0x3602, 0x00}, + {0x3603, 0x40}, + {0x3604, 0x02}, + {0x3605, 0x00}, + {0x3606, 0x00}, + {0x3607, 0x00}, + {0x3609, 0x12}, + {0x360a, 0x40}, + {0x360c, 0x08}, + {0x360f, 0xe5}, + {0x3608, 0x8f}, + {0x3611, 0x00}, + {0x3613, 0xf7}, + {0x3616, 0x58}, + {0x3619, 0x99}, + {0x361b, 0x60}, + {0x361c, 0x7a}, + {0x361e, 0x79}, + {0x361f, 0x02}, + {0x3632, 0x00}, + {0x3633, 0x10}, + {0x3634, 0x10}, + {0x3635, 0x10}, + {0x3636, 0x15}, + {0x3646, 0x86}, + {0x364a, 0x0b}, + {0x3700, 0x17}, + {0x3701, 0x22}, + {0x3703, 0x10}, + {0x370a, 0x37}, + {0x3705, 0x00}, + {0x3706, 0x63}, + {0x3709, 0x3c}, + {0x370b, 0x01}, + {0x370c, 0x30}, + {0x3710, 0x24}, + {0x3711, 0x0c}, + {0x3716, 0x00}, + {0x3720, 0x28}, + {0x3729, 0x7b}, + {0x372a, 0x84}, + {0x372b, 0xbd}, + {0x372c, 0xbc}, + {0x372e, 0x52}, + {0x373c, 0x0e}, + {0x373e, 0x33}, + {0x3743, 0x10}, + {0x3744, 0x88}, + {0x3745, 0xc0}, + {0x374a, 0x43}, + {0x374c, 0x00}, + {0x374e, 0x23}, + {0x3751, 0x7b}, + {0x3752, 0x84}, + {0x3753, 0xbd}, + {0x3754, 0xbc}, + {0x3756, 0x52}, + {0x375c, 0x00}, + {0x3760, 0x00}, + {0x3761, 0x00}, + {0x3762, 0x00}, + {0x3763, 0x00}, + {0x3764, 0x00}, + {0x3767, 0x04}, + {0x3768, 0x04}, + {0x3769, 0x08}, + {0x376a, 0x08}, + {0x376b, 0x20}, + {0x376c, 0x00}, + {0x376d, 0x00}, + {0x376e, 0x00}, + {0x3773, 0x00}, + {0x3774, 0x51}, + {0x3776, 0xbd}, + {0x3777, 0xbd}, + {0x3781, 0x18}, + {0x3783, 0x25}, + {0x3798, 0x1b}, + {0x3800, 0x00}, + {0x3801, 0x08}, + {0x3802, 0x00}, + {0x3803, 0x04}, + {0x3804, 0x0a}, + {0x3805, 0x97}, + {0x3806, 0x05}, + {0x3807, 0xfb}, + {0x3808, 0x0a}, + {0x3809, 0x80}, + {0x380a, 0x05}, + {0x380b, 0xf0}, + {0x380c, 0x03}, + {0x380d, 0x5c}, + {0x380e, 0x06}, + {0x380f, 0x12}, + {0x3810, 0x00}, + {0x3811, 0x08}, + {0x3812, 0x00}, + {0x3813, 0x04}, + {0x3814, 0x01}, + {0x3815, 0x01}, + {0x3819, 0x01}, + {0x3820, 0x00}, + {0x3821, 0x06}, + {0x3829, 0x00}, + {0x382a, 0x01}, + {0x382b, 0x01}, + {0x382d, 0x7f}, + {0x3830, 0x04}, + {0x3836, 0x01}, + {0x3837, 0x00}, + {0x3841, 0x02}, + {0x3846, 0x08}, + {0x3847, 0x07}, + {0x3d85, 0x36}, + {0x3d8c, 0x71}, + {0x3d8d, 0xcb}, + {0x3f0a, 0x00}, + {0x4000, 0xf1}, + {0x4001, 0x40}, + {0x4002, 0x04}, + {0x4003, 0x14}, + {0x400e, 0x00}, + {0x4011, 0x00}, + {0x401a, 0x00}, + {0x401b, 0x00}, + {0x401c, 0x00}, + {0x401d, 0x00}, + {0x401f, 0x00}, + {0x4020, 0x00}, + {0x4021, 0x10}, + {0x4022, 0x07}, + {0x4023, 0xcf}, + {0x4024, 0x09}, + {0x4025, 0x60}, + {0x4026, 0x09}, + {0x4027, 0x6f}, + {0x4028, 0x00}, + {0x4029, 0x02}, + {0x402a, 0x06}, + {0x402b, 0x04}, + {0x402c, 0x02}, + {0x402d, 0x02}, + {0x402e, 0x0e}, + {0x402f, 0x04}, + {0x4302, 0xff}, + {0x4303, 0xff}, + {0x4304, 0x00}, + {0x4305, 0x00}, + {0x4306, 0x00}, + {0x4308, 0x02}, + {0x4500, 0x6c}, + {0x4501, 0xc4}, + {0x4502, 0x40}, + {0x4503, 0x01}, + {0x4600, 0x00}, + {0x4601, 0xA7}, + /* bit[5]=0 as MIPI continuous clock mode */ + {0x4800, 0x04}, + {0x4813, 0x08}, + {0x481f, 0x40}, + {0x4829, 0x78}, + /* global timing for 360Mbps */ + {0x4837, 0x10}, + {0x4b00, 0x2a}, + {0x4b0d, 0x00}, + {0x4d00, 0x04}, + {0x4d01, 0x42}, + {0x4d02, 0xd1}, + {0x4d03, 0x93}, + {0x4d04, 0xf5}, + {0x4d05, 0xc1}, + {0x5000, 0xf3}, + {0x5001, 0x11}, + {0x5004, 0x00}, + {0x500a, 0x00}, + {0x500b, 0x00}, + {0x5032, 0x00}, + {0x5040, 0x00}, + {0x5050, 0x0c}, + {0x5500, 0x00}, + {0x5501, 0x10}, + {0x5502, 0x01}, + {0x5503, 0x0f}, + {0x8000, 0x00}, + {0x8001, 0x00}, + {0x8002, 0x00}, + {0x8003, 0x00}, + {0x8004, 0x00}, + {0x8005, 0x00}, + {0x8006, 0x00}, + {0x8007, 0x00}, + {0x8008, 0x00}, + {0x3638, 0x00}, + + {0x0100, 0x01}, + + {OV4689_TABLE_END, OV4689_TABLE_END} + +}; + +static struct ov4689_reg ov4689_mode_1920x1080_initial[] = { + {0x0103, 0x01}, + {0x3638, 0x00}, + {0x0300, 0x00}, + /* 792 Mbps MIPI_CLK */ + {0x0302, 0x21}, + {0x0304, 0x03}, + {0x030b, 0x00}, + {0x030d, 0x1e}, + {0x030e, 0x04}, + {0x030f, 0x01}, + {0x0312, 0x01}, + {0x031e, 0x00}, + {0x3000, 0x20}, + {0x3002, 0x00}, + {0x3018, 0x72}, + {0x3020, 0x93}, + {0x3021, 0x03}, + {0x3022, 0x01}, + {0x3031, 0x0a}, + {0x303f, 0x0c}, + {0x3305, 0xf1}, + {0x3307, 0x04}, + {0x3309, 0x29}, + {0x3500, 0x01}, + {0x3501, 0x21}, + {0x3502, 0x40}, + {0x3503, 0x04}, + {0x3504, 0x00}, + {0x3505, 0x00}, + {0x3506, 0x00}, + {0x3507, 0x00}, + {0x3508, 0x00}, + {0x3509, 0x80}, + {0x350a, 0x00}, + {0x350b, 0x00}, + {0x350c, 0x00}, + {0x350d, 0x00}, + {0x350e, 0x00}, + {0x350f, 0x80}, + {0x3510, 0x00}, + {0x3511, 0x00}, + {0x3512, 0x00}, + {0x3513, 0x00}, + {0x3514, 0x00}, + {0x3515, 0x80}, + {0x3516, 0x00}, + {0x3517, 0x00}, + {0x3518, 0x00}, + {0x3519, 0x00}, + {0x351a, 0x00}, + {0x351b, 0x80}, + {0x351c, 0x00}, + {0x351d, 0x00}, + {0x351e, 0x00}, + {0x351f, 0x00}, + {0x3520, 0x00}, + {0x3521, 0x80}, + {0x3522, 0x08}, + {0x3524, 0x08}, + {0x3526, 0x08}, + {0x3528, 0x08}, + {0x352a, 0x08}, + {0x3602, 0x00}, + {0x3603, 0x40}, + {0x3604, 0x02}, + {0x3605, 0x00}, + {0x3606, 0x00}, + {0x3607, 0x00}, + {0x3609, 0x12}, + {0x360a, 0x40}, + {0x360c, 0x08}, + {0x360f, 0xe5}, + {0x3608, 0x8f}, + {0x3611, 0x00}, + {0x3613, 0xf7}, + {0x3616, 0x58}, + {0x3619, 0x99}, + {0x361b, 0x60}, + {0x361c, 0x7a}, + {0x361e, 0x79}, + {0x361f, 0x02}, + {0x3632, 0x00}, + {0x3633, 0x10}, + {0x3634, 0x10}, + {0x3635, 0x10}, + {0x3636, 0x15}, + {0x3646, 0x86}, + {0x364a, 0x0b}, + {0x3700, 0x17}, + {0x3701, 0x22}, + {0x3703, 0x10}, + {0x370a, 0x37}, + {0x3705, 0x00}, + {0x3706, 0x63}, + {0x3709, 0x3c}, + {0x370b, 0x01}, + {0x370c, 0x30}, + {0x3710, 0x24}, + {0x3711, 0x0c}, + {0x3716, 0x00}, + {0x3720, 0x28}, + {0x3729, 0x7b}, + {0x372a, 0x84}, + {0x372b, 0xbd}, + {0x372c, 0xbc}, + {0x372e, 0x52}, + {0x373c, 0x0e}, + {0x373e, 0x33}, + {0x3743, 0x10}, + {0x3744, 0x88}, + {0x3745, 0xc0}, + {0x374a, 0x43}, + {0x374c, 0x00}, + {0x374e, 0x23}, + {0x3751, 0x7b}, + {0x3752, 0x84}, + {0x3753, 0xbd}, + {0x3754, 0xbc}, + {0x3756, 0x52}, + {0x375c, 0x00}, + {0x3760, 0x00}, + {0x3761, 0x00}, + {0x3762, 0x00}, + {0x3763, 0x00}, + {0x3764, 0x00}, + {0x3767, 0x04}, + {0x3768, 0x04}, + {0x3769, 0x08}, + {0x376a, 0x08}, + {0x376b, 0x20}, + {0x376c, 0x00}, + {0x376d, 0x00}, + {0x376e, 0x00}, + {0x3773, 0x00}, + {0x3774, 0x51}, + {0x3776, 0xbd}, + {0x3777, 0xbd}, + {0x3781, 0x18}, + {0x3783, 0x25}, + {0x3798, 0x1b}, + {0x3800, 0x01}, + {0x3801, 0x88}, + {0x3802, 0x00}, + {0x3803, 0xe0}, + {0x3804, 0x09}, + {0x3805, 0x17}, + {0x3806, 0x05}, + {0x3807, 0x1f}, + {0x3808, 0x07}, + {0x3809, 0x80}, + {0x380a, 0x04}, + {0x380b, 0x38}, + {0x380c, 0x03}, + {0x380d, 0x5c}, + {0x380e, 0x12}, + {0x380f, 0x1e}, + {0x3810, 0x00}, + {0x3811, 0x08}, + {0x3812, 0x00}, + {0x3813, 0x04}, + {0x3814, 0x01}, + {0x3815, 0x01}, + {0x3819, 0x01}, + {0x3820, 0x00}, + {0x3821, 0x06}, + {0x3829, 0x00}, + {0x382a, 0x01}, + {0x382b, 0x01}, + {0x382d, 0x7f}, + {0x3830, 0x04}, + {0x3836, 0x01}, + {0x3837, 0x00}, + {0x3841, 0x02}, + {0x3846, 0x08}, + {0x3847, 0x07}, + {0x3d85, 0x36}, + {0x3d8c, 0x71}, + {0x3d8d, 0xcb}, + {0x3f0a, 0x00}, + {0x4000, 0xf1}, + {0x4001, 0x40}, + {0x4002, 0x04}, + {0x4003, 0x14}, + {0x400e, 0x00}, + {0x4011, 0x00}, + {0x401a, 0x00}, + {0x401b, 0x00}, + {0x401c, 0x00}, + {0x401d, 0x00}, + {0x401f, 0x00}, + {0x4020, 0x00}, + {0x4021, 0x10}, + {0x4022, 0x06}, + {0x4023, 0x13}, + {0x4024, 0x07}, + {0x4025, 0x40}, + {0x4026, 0x07}, + {0x4027, 0x50}, + {0x4028, 0x00}, + {0x4029, 0x02}, + {0x402a, 0x06}, + {0x402b, 0x04}, + {0x402c, 0x02}, + {0x402d, 0x02}, + {0x402e, 0x0e}, + {0x402f, 0x04}, + {0x4302, 0xff}, + {0x4303, 0xff}, + {0x4304, 0x00}, + {0x4305, 0x00}, + {0x4306, 0x00}, + {0x4308, 0x02}, + {0x4500, 0x6c}, + {0x4501, 0xc4}, + {0x4502, 0x40}, + {0x4503, 0x01}, + {0x4600, 0x00}, + {0x4601, 0x77}, + /* bit[5]=0 as MIPI continuous clock mode */ + {0x4800, 0x04}, + {0x4813, 0x08}, + {0x481f, 0x40}, + {0x4829, 0x78}, + {0x4837, 0x14}, + {0x4b00, 0x2a}, + {0x4b0d, 0x00}, + {0x4d00, 0x04}, + {0x4d01, 0x42}, + {0x4d02, 0xd1}, + {0x4d03, 0x93}, + {0x4d04, 0xf5}, + {0x4d05, 0xc1}, + {0x5000, 0xf3}, + {0x5001, 0x11}, + {0x5004, 0x00}, + {0x500a, 0x00}, + {0x500b, 0x00}, + {0x5032, 0x00}, + {0x5040, 0x00}, + {0x5050, 0x0c}, + {0x5500, 0x00}, + {0x5501, 0x10}, + {0x5502, 0x01}, + {0x5503, 0x0f}, + {0x8000, 0x00}, + {0x8001, 0x00}, + {0x8002, 0x00}, + {0x8003, 0x00}, + {0x8004, 0x00}, + {0x8005, 0x00}, + {0x8006, 0x00}, + {0x8007, 0x00}, + {0x8008, 0x00}, + {0x3638, 0x00}, + + {0x0100, 0x01}, + + + {OV4689_TABLE_END, OV4689_TABLE_END} +}; + +enum { + OV4689_MODE_2688x1520, + OV4689_MODE_1920x1080, +}; + +static struct ov4689_reg *mode_table[] = { + [OV4689_MODE_2688x1520] = ov4689_mode_2688x1520_initial, + [OV4689_MODE_1920x1080] = ov4689_mode_1920x1080_initial, +}; + +#endif diff --git a/include/media/ov4689.h b/include/media/ov4689.h new file mode 100644 index 000000000000..fa60d6cab65d --- /dev/null +++ b/include/media/ov4689.h @@ -0,0 +1,77 @@ +/* + * ov4689.h - ov4689 sensor driver + * + * Copyright (c) 2015 NVIDIA Corporation. All rights reserved. + * + * Contributors: + * Jerry Chang <jerchang@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. + */ + +#ifndef __OV4689_H__ +#define __OV4689_H__ + +#include <linux/ioctl.h> + +#define OV4689_IOCTL_SET_MODE _IOW('o', 1, struct ov4689_mode) +#define OV4689_IOCTL_SET_FRAME_LENGTH _IOW('o', 2, __u32) +#define OV4689_IOCTL_SET_COARSE_TIME _IOW('o', 3, __u32) +#define OV4689_IOCTL_SET_GAIN _IOW('o', 4, __u16) +#define OV4689_IOCTL_GET_STATUS _IOR('o', 5, __u8) +#define OV4689_IOCTL_SET_GROUP_HOLD _IOW('o', 6, struct ov4689_grouphold) +#define OV4689_IOCTL_GET_SENSORDATA _IOR('o', 7, struct ov4689_sensordata) +#define OV4689_IOCTL_SET_FLASH _IOW('o', 8, struct ov4689_flash_control) +#define OV4689_IOCTL_SET_POWER _IOW('o', 20, __u32) + +struct ov4689_sensordata { + __u32 fuse_id_size; + __u8 fuse_id[16]; +}; + +struct ov4689_mode { + __u32 xres; + __u32 yres; + __u32 fps; + __u32 frame_length; + __u32 coarse_time; + __u16 gain; +}; + +struct ov4689_grouphold { + __u32 frame_length; + __u8 frame_length_enable; + __u32 coarse_time; + __u8 coarse_time_enable; + __s32 gain; + __u8 gain_enable; +}; + +struct ov4689_flash_control { + u8 enable; + u8 edge_trig_en; + u8 start_edge; + u8 repeat; + u16 delay_frm; +}; + +#ifdef __KERNEL__ +struct ov4689_power_rail { + struct regulator *dvdd; + struct regulator *avdd; + struct regulator *iovdd; + struct regulator *vif; +}; + +struct ov4689_platform_data { + struct ov4689_flash_control flash_cap; + int (*power_on)(struct ov4689_power_rail *pw); + int (*power_off)(struct ov4689_power_rail *pw); + const char *mclk_name; + unsigned int reset_gpio; +}; +#endif /* __KERNEL__ */ + +#endif /* __OV4689_H__ */ |