Kernel: add ov4689 kernel driver
Hu He [Tue, 25 Nov 2014 22:59:07 +0000 (14:59 -0800)]
Bug 1600299

Change-Id: I63f4d597bcc8b960407a7291fde6aa2ddb5bec25
Signed-off-by: Ming Wong <miwong@nvidia.com>
Reviewed-on: http://git-master/r/674099
Reviewed-by: Winnie Hsu <whsu@nvidia.com>
GVS: Gerrit_Virtual_Submit

arch/arm/boot/dts/tegra124-jetson_tk1-pm375-000-c00-00.dts
arch/arm/boot/dts/tegra124-platforms/tegra124-pm359-camera-a00.dtsi
arch/arm/configs/tegra12_defconfig
arch/arm/mach-tegra/board-ardbeg-sensors.c
drivers/media/platform/tegra/Kconfig
drivers/media/platform/tegra/Makefile
drivers/media/platform/tegra/ov4689.c [new file with mode: 0644]
drivers/media/platform/tegra/ov4689_tables.h [new file with mode: 0644]
include/media/ov4689.h [new file with mode: 0644]

index 87dbc0f..9d3559b 100644 (file)
                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>;
index 566180d..e84ffcc 100644 (file)
@@ -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
                                        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 {
                                        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
+                                       >;
+                       };
                };
        };
 };
index 8c86912..d028d4a 100644 (file)
@@ -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
index d89c2e1..c2fd850 100644 (file)
@@ -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},
        {},
 };
 
index 2b95077..1bcd293 100644 (file)
@@ -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
index 7a3b491..6c205b2 100644 (file)
@@ -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 (file)
index 0000000..435322f
--- /dev/null
@@ -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 (file)
index 0000000..7233d60
--- /dev/null
@@ -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 (file)
index 0000000..fa60d6c
--- /dev/null
@@ -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__ */