summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorJihoon Bang <jbang@nvidia.com>2012-12-27 10:31:01 -0800
committerRiham Haidar <rhaidar@nvidia.com>2013-01-16 14:14:19 -0800
commite503a08b809844b53b7737e504e9f376f4a8a1eb (patch)
tree6f915ed814ae4cf23bdc2fa919bf68201b774455 /drivers
parent41f8b7c8e183feeb509bf8193a2362e8a1e1a144 (diff)
media: video: tegra: camera: move tegra_camera
Move tegra_camera from platform_driver to part of nvhost vi driver. Split tegra_camera.c file into multiple files incluidng camera.c, camera_power.c, camera_emc.c and camera_clk.c according to functionality. tegra_camera is registered/unregistered in nvhost vi driver. Bug 1189789 Change-Id: I0ff6b4c0032b33b1030f94ac1b1675d1fff53bab Signed-off-by: Jihoon Bang <jbang@nvidia.com> Reviewed-on: http://git-master/r/174508 Reviewed-by: Terje Bergstrom <tbergstrom@nvidia.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/media/video/tegra/Kconfig9
-rw-r--r--drivers/media/video/tegra/Makefile2
-rw-r--r--drivers/media/video/tegra/ov5650.c2
-rw-r--r--drivers/media/video/tegra/tegra_camera.c697
-rw-r--r--drivers/video/tegra/Kconfig8
-rw-r--r--drivers/video/tegra/Makefile1
-rw-r--r--drivers/video/tegra/camera/Makefile7
-rw-r--r--drivers/video/tegra/camera/camera.c339
-rw-r--r--drivers/video/tegra/camera/camera_clk.c170
-rw-r--r--drivers/video/tegra/camera/camera_clk.h25
-rw-r--r--drivers/video/tegra/camera/camera_emc.c36
-rw-r--r--drivers/video/tegra/camera/camera_emc.h24
-rw-r--r--drivers/video/tegra/camera/camera_power.c84
-rw-r--r--drivers/video/tegra/camera/camera_power.h26
-rw-r--r--drivers/video/tegra/camera/camera_priv_defs.h91
-rw-r--r--drivers/video/tegra/host/vi/Makefile1
-rw-r--r--drivers/video/tegra/host/vi/vi.c75
-rw-r--r--drivers/video/tegra/host/vi/vi.h31
18 files changed, 912 insertions, 716 deletions
diff --git a/drivers/media/video/tegra/Kconfig b/drivers/media/video/tegra/Kconfig
index 9bde7622531b..8d59c895a6e4 100644
--- a/drivers/media/video/tegra/Kconfig
+++ b/drivers/media/video/tegra/Kconfig
@@ -2,15 +2,6 @@ source "drivers/media/video/tegra/avp/Kconfig"
source "drivers/media/video/tegra/mediaserver/Kconfig"
source "drivers/media/video/tegra/nvavp/Kconfig"
-config TEGRA_CAMERA
- bool "Enable support for tegra camera/isp hardware"
- depends on ARCH_TEGRA
- default y
- help
- Enables support for the Tegra camera interface
-
- If unsure, say Y
-
config TEGRA_DTV
bool "Enable support for tegra dtv interface"
depends on ARCH_TEGRA
diff --git a/drivers/media/video/tegra/Makefile b/drivers/media/video/tegra/Makefile
index 75ba99f8eacf..d643b3a71d4b 100644
--- a/drivers/media/video/tegra/Makefile
+++ b/drivers/media/video/tegra/Makefile
@@ -8,7 +8,6 @@ obj-y += avp/
obj-$(CONFIG_TEGRA_MEDIASERVER) += mediaserver/
obj-$(CONFIG_TEGRA_NVAVP) += nvavp/
obj-$(CONFIG_TEGRA_DTV) += tegra_dtv.o
-obj-$(CONFIG_TEGRA_CAMERA) += tegra_camera.o
obj-$(CONFIG_VIDEO_AR0832) += ar0832_main.o
obj-$(CONFIG_VIDEO_OV5650) += ov5650.o
obj-$(CONFIG_VIDEO_OV5640) += ov5640.o
@@ -26,4 +25,3 @@ obj-$(CONFIG_VIDEO_AD5820) += ad5820.o
obj-$(CONFIG_VIDEO_AD5816) += ad5816.o
obj-$(CONFIG_VIDEO_IMX091) += imx091.o
obj-$(CONFIG_VIDEO_IMX132) += imx132.o
-
diff --git a/drivers/media/video/tegra/ov5650.c b/drivers/media/video/tegra/ov5650.c
index f54531792d4b..1ef23a403486 100644
--- a/drivers/media/video/tegra/ov5650.c
+++ b/drivers/media/video/tegra/ov5650.c
@@ -22,7 +22,7 @@
#include <linux/module.h>
#include <media/ov5650.h>
-#include <media/tegra_camera.h>
+#include <video/tegra_camera.h>
#define SIZEOF_I2C_TRANSBUF 32
diff --git a/drivers/media/video/tegra/tegra_camera.c b/drivers/media/video/tegra/tegra_camera.c
deleted file mode 100644
index f548e5240597..000000000000
--- a/drivers/media/video/tegra/tegra_camera.c
+++ /dev/null
@@ -1,697 +0,0 @@
-/*
- * drivers/media/video/tegra/tegra_camera.c
- *
- * Copyright (C) 2010 Google, Inc.
- * Copyright (C) 2012 Nvidia Corp
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that 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.
- *
- */
-
-#include <linux/miscdevice.h>
-#include <linux/platform_device.h>
-#include <linux/ioctl.h>
-#include <linux/fs.h>
-#include <linux/device.h>
-#include <linux/regulator/consumer.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-#include <linux/uaccess.h>
-#include <linux/delay.h>
-#include <linux/export.h>
-#include <linux/slab.h>
-
-#include <mach/iomap.h>
-#include <mach/clk.h>
-#include <mach/powergate.h>
-#include <mach/mc.h>
-
-#ifdef CONFIG_ARCH_TEGRA_11x_SOC
-#include <mach/isomgr.h>
-#endif
-
-#include <media/tegra_camera.h>
-
-/* Eventually this should handle all clock and reset calls for the isp, vi,
- * vi_sensor, and csi modules, replacing nvrm and nvos completely for camera
- */
-#define TEGRA_CAMERA_NAME "tegra_camera"
-
-struct tegra_camera_dev {
- struct device *dev;
- struct miscdevice misc_dev;
- struct clk *isp_clk;
- struct clk *vi_clk;
- struct clk *vi_sensor_clk;
- struct clk *csus_clk;
- struct clk *csi_clk;
- struct clk *emc_clk;
-#ifdef CONFIG_ARCH_TEGRA_11x_SOC
- struct clk *pll_d2_clk;
- struct clk *cilab_clk;
- struct clk *cilcd_clk;
- struct clk *cile_clk;
-#endif
- struct regulator *reg;
- struct tegra_camera_clk_info info;
- struct mutex tegra_camera_lock;
- atomic_t in_use;
- int power_on;
-#ifdef CONFIG_ARCH_TEGRA_11x_SOC
- tegra_isomgr_handle isomgr_handle;
-#endif
-};
-
-struct tegra_camera_block {
- int (*enable) (struct tegra_camera_dev *dev);
- int (*disable) (struct tegra_camera_dev *dev);
- bool is_enabled;
-};
-
-static int tegra_camera_enable_clk(struct tegra_camera_dev *dev)
-{
- clk_prepare_enable(dev->vi_clk);
- clk_prepare_enable(dev->vi_sensor_clk);
- clk_prepare_enable(dev->csus_clk);
- clk_prepare_enable(dev->isp_clk);
- clk_prepare_enable(dev->csi_clk);
-
-#ifdef CONFIG_ARCH_TEGRA_11x_SOC
- clk_prepare_enable(dev->cilab_clk);
- clk_prepare_enable(dev->cilcd_clk);
- clk_prepare_enable(dev->cile_clk);
-#endif
- return 0;
-}
-
-static int tegra_camera_disable_clk(struct tegra_camera_dev *dev)
-{
- clk_disable_unprepare(dev->csi_clk);
- clk_disable_unprepare(dev->isp_clk);
- clk_disable_unprepare(dev->csus_clk);
- clk_disable_unprepare(dev->vi_sensor_clk);
- clk_disable_unprepare(dev->vi_clk);
-
-#ifdef CONFIG_ARCH_TEGRA_11x_SOC
- clk_disable_unprepare(dev->cilab_clk);
- clk_disable_unprepare(dev->cilcd_clk);
- clk_disable_unprepare(dev->cile_clk);
-#endif
-
- return 0;
-}
-
-static int tegra_camera_enable_emc(struct tegra_camera_dev *dev)
-{
- int ret = tegra_emc_disable_eack();
- clk_prepare_enable(dev->emc_clk);
-#ifdef CONFIG_ARCH_TEGRA_2x_SOC
- clk_set_rate(dev->emc_clk, 300000000);
-#endif
- return ret;
-}
-
-static int tegra_camera_disable_emc(struct tegra_camera_dev *dev)
-{
- clk_disable_unprepare(dev->emc_clk);
- return tegra_emc_enable_eack();
-}
-
-static int tegra_camera_clk_set_rate(struct tegra_camera_dev *dev)
-{
- struct clk *clk, *clk_parent;
- struct tegra_camera_clk_info *info = &dev->info;
- unsigned long parent_rate, parent_div_rate, parent_div_rate_pre;
-
- if (!info) {
- dev_err(dev->dev,
- "%s: no clock info %d\n",
- __func__, info->id);
- return -EINVAL;
- }
-
- if (info->id != TEGRA_CAMERA_MODULE_VI &&
- info->id != TEGRA_CAMERA_MODULE_EMC) {
- dev_err(dev->dev,
- "%s: set rate only aplies to vi module %d\n",
- __func__, info->id);
- return -EINVAL;
- }
-
- switch (info->clk_id) {
- case TEGRA_CAMERA_VI_CLK:
- clk = dev->vi_clk;
- break;
- case TEGRA_CAMERA_VI_SENSOR_CLK:
- clk = dev->vi_sensor_clk;
- break;
- case TEGRA_CAMERA_EMC_CLK:
- clk = dev->emc_clk;
-#ifndef CONFIG_ARCH_TEGRA_2x_SOC
- {
- /*
- * User space assumes that HW emc controller is 4
- * byte-wide DDR controller.
- * Emc bandwidth needs to be calcaluated using input emc
- * freq first, and then real emc freq will
- * be calculated using tegra_emc API.
- * tegra_emc_bw_to_freq_req takes HW difference
- * into consideration.
- * bw param in tegra_emc_bw_to_freq_req() is in KHz.
- */
- unsigned long bw = (info->rate * 8) >> 10;
-#ifdef CONFIG_ARCH_TEGRA_11x_SOC
- int ret = 0;
-#endif
- dev_dbg(dev->dev, "%s: emc_clk rate=%lu\n",
- __func__, info->rate);
- clk_set_rate(dev->emc_clk,
- tegra_emc_bw_to_freq_req(bw) << 10);
-#ifdef CONFIG_ARCH_TEGRA_11x_SOC
- /*
- * There is no way to figure out what latency
- * can be tolerated in VI without reading VI
- * registers for now. 3 usec is minimum time
- * to switch PLL source. Let's put 4 usec as
- * latency for now.
- */
- ret = tegra_isomgr_reserve(dev->isomgr_handle,
- bw, /* KB/sec */
- 4); /* usec */
- if (!ret)
- return -ENOMEM;
-
- ret = tegra_isomgr_realize(dev->isomgr_handle);
- if (!ret)
- return -ENOMEM;
-#endif
- }
-#endif
- goto set_rate_end;
- default:
- dev_err(dev->dev,
- "%s: invalid clk id for set rate %d\n",
- __func__, info->clk_id);
- return -EINVAL;
- }
-
- clk_parent = clk_get_parent(clk);
- parent_rate = clk_get_rate(clk_parent);
- dev_dbg(dev->dev, "%s: clk_id=%d, parent_rate=%lu, clk_rate=%lu\n",
- __func__, info->clk_id, parent_rate, info->rate);
- parent_div_rate = parent_rate;
- parent_div_rate_pre = parent_rate;
-
- /*
- * The requested clock rate from user space should be respected.
- * This loop is to search the clock rate that is higher than requested
- * clock.
- */
- while (parent_div_rate >= info->rate) {
- parent_div_rate_pre = parent_div_rate;
- parent_div_rate = clk_round_rate(clk, parent_div_rate-1);
- }
-
- dev_dbg(dev->dev, "%s: set_rate=%lu",
- __func__, parent_div_rate_pre);
-
- clk_set_rate(clk, parent_div_rate_pre);
-
- if (info->clk_id == TEGRA_CAMERA_VI_CLK) {
-#ifdef CONFIG_ARCH_TEGRA_2x_SOC
- {
- u32 val;
- void __iomem *apb_misc =
- IO_ADDRESS(TEGRA_APB_MISC_BASE);
- val = readl(apb_misc + 0x42c);
- writel(val | 0x1, apb_misc + 0x42c);
- }
-#endif
-
- if (info->flag == TEGRA_CAMERA_ENABLE_PD2VI_CLK) {
-#ifdef CONFIG_ARCH_TEGRA_11x_SOC
- tegra_clk_cfg_ex(dev->pll_d2_clk,
- TEGRA_CLK_PLLD_CSI_OUT_ENB, 1);
-#else
- /*
- * bit 25: 0 = pd2vi_Clk, 1 = vi_sensor_clk
- * bit 24: 0 = internal clock, 1 = external clock(pd2vi_clk)
- */
- tegra_clk_cfg_ex(clk, TEGRA_CLK_VI_INP_SEL, 2);
-#endif
- }
-#ifdef CONFIG_ARCH_TEGRA_11x_SOC
- else {
- tegra_clk_cfg_ex(dev->pll_d2_clk,
- TEGRA_CLK_PLLD_CSI_OUT_ENB, 0);
- }
-#endif
- }
-
-set_rate_end:
- info->rate = clk_get_rate(clk);
- dev_dbg(dev->dev, "%s: get_rate=%lu",
- __func__, info->rate);
- return 0;
-
-}
-
-static int tegra_camera_power_on(struct tegra_camera_dev *dev)
-{
- int ret = 0;
- dev_dbg(dev->dev, "%s++\n", __func__);
-
- /* Enable external power */
- if (dev->reg) {
- ret = regulator_enable(dev->reg);
- if (ret) {
- dev_err(dev->dev,
- "%s: enable csi regulator failed.\n",
- __func__);
- return ret;
- }
- }
-#ifndef CONFIG_ARCH_TEGRA_2x_SOC
- /* Unpowergate VE */
- ret = tegra_unpowergate_partition(TEGRA_POWERGATE_VENC);
- if (ret)
- dev_err(dev->dev,
- "%s: unpowergate failed.\n",
- __func__);
-#endif
- dev->power_on = 1;
- return ret;
-}
-
-static int tegra_camera_power_off(struct tegra_camera_dev *dev)
-{
- int ret = 0;
-
- dev_dbg(dev->dev, "%s++\n", __func__);
-
-#ifndef CONFIG_ARCH_TEGRA_2x_SOC
- /* Powergate VE */
- ret = tegra_powergate_partition(TEGRA_POWERGATE_VENC);
- if (ret)
- dev_err(dev->dev,
- "%s: powergate failed.\n",
- __func__);
-#endif
- /* Disable external power */
- if (dev->reg) {
- ret = regulator_disable(dev->reg);
- if (ret) {
- dev_err(dev->dev,
- "%s: disable csi regulator failed.\n",
- __func__);
- return ret;
- }
- }
- dev->power_on = 0;
- return ret;
-}
-
-static long tegra_camera_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- uint id;
- struct tegra_camera_dev *dev = file->private_data;
-
- /* first element of arg must be u32 with id of module to talk to */
- if (copy_from_user(&id, (const void __user *)arg, sizeof(uint))) {
- dev_err(dev->dev,
- "%s: Failed to copy arg from user", __func__);
- return -EFAULT;
- }
-
- if (id >= TEGRA_CAMERA_MODULE_MAX) {
- dev_err(dev->dev,
- "%s: Invalid id to tegra isp ioctl%d\n",
- __func__, id);
- return -EINVAL;
- }
-
- switch (cmd) {
- /*
- * Clock enable/disable and reset should be handled in kernel.
- * In order to support legacy code in user space, we don't remove
- * these IOCTL.
- */
- case TEGRA_CAMERA_IOCTL_ENABLE:
- case TEGRA_CAMERA_IOCTL_DISABLE:
- case TEGRA_CAMERA_IOCTL_RESET:
- return 0;
- case TEGRA_CAMERA_IOCTL_CLK_SET_RATE:
- {
- int ret;
-
- if (copy_from_user(&dev->info, (const void __user *)arg,
- sizeof(struct tegra_camera_clk_info))) {
- dev_err(dev->dev,
- "%s: Failed to copy arg from user\n", __func__);
- return -EFAULT;
- }
- ret = tegra_camera_clk_set_rate(dev);
- if (ret)
- return ret;
- if (copy_to_user((void __user *)arg, &dev->info,
- sizeof(struct tegra_camera_clk_info))) {
- dev_err(dev->dev,
- "%s: Failed to copy arg to user\n", __func__);
- return -EFAULT;
- }
- return 0;
- }
- default:
- dev_err(dev->dev,
- "%s: Unknown tegra_camera ioctl.\n", __func__);
- return -EINVAL;
- }
- return 0;
-}
-
-static int tegra_camera_open(struct inode *inode, struct file *file)
-{
- struct miscdevice *miscdev = file->private_data;
- struct tegra_camera_dev *dev = container_of(miscdev,
- struct tegra_camera_dev,
- misc_dev);
- int ret = 0;
-
- dev_info(dev->dev, "%s\n", __func__);
-
- if (atomic_xchg(&dev->in_use, 1))
- return -EBUSY;
-
- file->private_data = dev;
-
- mutex_lock(&dev->tegra_camera_lock);
- /* turn on CSI regulator */
- ret = tegra_camera_power_on(dev);
- if (ret)
- goto power_on_err;
- /* set EMC request */
- ret = tegra_camera_enable_emc(dev);
- if (ret)
- goto enable_emc_err;
- /* enable camera HW clock */
- ret = tegra_camera_enable_clk(dev);
- if (ret)
- goto enable_clk_err;
-
- mutex_unlock(&dev->tegra_camera_lock);
-
- return 0;
-
-enable_clk_err:
- tegra_camera_disable_emc(dev);
-enable_emc_err:
- tegra_camera_power_off(dev);
-power_on_err:
- mutex_unlock(&dev->tegra_camera_lock);
- return ret;
-}
-
-static int tegra_camera_release(struct inode *inode, struct file *file)
-{
- int ret = 0;
- struct tegra_camera_dev *dev = file->private_data;
-
- dev_info(dev->dev, "%s\n", __func__);
-
-
- mutex_lock(&dev->tegra_camera_lock);
- /* disable HW clock */
- ret = tegra_camera_disable_clk(dev);
- if (ret)
- goto release_exit;
- /* nullify EMC request */
- ret = tegra_camera_disable_emc(dev);
- if (ret)
- goto release_exit;
- /* turn off CSI regulator */
- ret = tegra_camera_power_off(dev);
- if (ret)
- goto release_exit;
-
-release_exit:
- mutex_unlock(&dev->tegra_camera_lock);
- WARN_ON(!atomic_xchg(&dev->in_use, 0));
- return 0;
-}
-
-static const struct file_operations tegra_camera_fops = {
- .owner = THIS_MODULE,
- .open = tegra_camera_open,
- .unlocked_ioctl = tegra_camera_ioctl,
- .release = tegra_camera_release,
-};
-
-static int tegra_camera_clk_get(struct platform_device *pdev, const char *name,
- struct clk **clk)
-{
- *clk = clk_get(&pdev->dev, name);
- if (IS_ERR_OR_NULL(*clk)) {
- dev_err(&pdev->dev, "%s: unable to get clock for %s\n",
- __func__, name);
- *clk = NULL;
- return PTR_ERR(*clk);
- }
- return 0;
-}
-
-static int tegra_camera_probe(struct platform_device *pdev)
-{
- int err;
- struct tegra_camera_dev *dev;
-
- dev_info(&pdev->dev, "%s\n", __func__);
- dev = devm_kzalloc(&pdev->dev, sizeof(struct tegra_camera_dev),
- GFP_KERNEL);
- if (!dev) {
- err = -ENOMEM;
- dev_err(&pdev->dev, "%s: unable to allocate memory\n",
- __func__);
- goto alloc_err;
- }
-
- mutex_init(&dev->tegra_camera_lock);
-
- /* Powergate VE when boot */
- mutex_lock(&dev->tegra_camera_lock);
-#ifndef CONFIG_ARCH_TEGRA_2x_SOC
- err = tegra_powergate_partition(TEGRA_POWERGATE_VENC);
- if (err)
- dev_err(&pdev->dev, "%s: powergate failed.\n", __func__);
-#endif
- mutex_unlock(&dev->tegra_camera_lock);
-
- dev->dev = &pdev->dev;
-
- /* Get regulator pointer */
-#ifdef CONFIG_ARCH_TEGRA_2x_SOC
- dev->reg = regulator_get(&pdev->dev, "vcsi");
-#else
- dev->reg = regulator_get(&pdev->dev, "avdd_dsi_csi");
-#endif
- if (IS_ERR_OR_NULL(dev->reg)) {
- if (dev->reg == ERR_PTR(-ENODEV)) {
- dev->reg = NULL;
- dev_info(&pdev->dev, "%s: no regulator device, overriding\n",
- __func__);
- } else {
- dev_err(&pdev->dev, "%s: couldn't get regulator\n",
- __func__);
- return PTR_ERR(dev->reg);
- }
- }
-
- dev->misc_dev.minor = MISC_DYNAMIC_MINOR;
- dev->misc_dev.name = TEGRA_CAMERA_NAME;
- dev->misc_dev.fops = &tegra_camera_fops;
- dev->misc_dev.parent = &pdev->dev;
-
- err = misc_register(&dev->misc_dev);
- if (err) {
- dev_err(&pdev->dev, "%s: Unable to register misc device!\n",
- TEGRA_CAMERA_NAME);
- goto misc_register_err;
- }
-
- err = tegra_camera_clk_get(pdev, "isp", &dev->isp_clk);
- if (err)
- goto isp_clk_get_err;
- err = tegra_camera_clk_get(pdev, "vi", &dev->vi_clk);
- if (err)
- goto vi_clk_get_err;
- err = tegra_camera_clk_get(pdev, "vi_sensor", &dev->vi_sensor_clk);
- if (err)
- goto vi_sensor_clk_get_err;
- err = tegra_camera_clk_get(pdev, "csus", &dev->csus_clk);
- if (err)
- goto csus_clk_get_err;
- err = tegra_camera_clk_get(pdev, "csi", &dev->csi_clk);
- if (err)
- goto csi_clk_get_err;
- err = tegra_camera_clk_get(pdev, "emc", &dev->emc_clk);
- if (err)
- goto emc_clk_get_err;
-#ifdef CONFIG_ARCH_TEGRA_11x_SOC
- err = tegra_camera_clk_get(pdev, "cilab", &dev->cilab_clk);
- if (err)
- goto cilab_clk_get_err;
- err = tegra_camera_clk_get(pdev, "cilcd", &dev->cilcd_clk);
- if (err)
- goto cilcd_clk_get_err;
- err = tegra_camera_clk_get(pdev, "cile", &dev->cile_clk);
- if (err)
- goto cile_clk_get_err;
- err = tegra_camera_clk_get(pdev, "pll_d2", &dev->pll_d2_clk);
- if (err)
- goto pll_d2_clk_get_err;
-#endif
-
-#ifdef CONFIG_ARCH_TEGRA_11x_SOC
- /*
- * Dedicated bw is what VI could ask for at most.
- * Assume that preview has 3M@30fps and video has 8M@30fps
- * Total = 3M * 30fps * 2Bpp + 8M * 30fps * 1.5Bpp
- * = 540MBps
- * This number can be changed if VI requests more than this.
- *
- */
- dev->isomgr_handle = tegra_isomgr_register(TEGRA_ISO_CLIENT_VI_0,
- 540000, /* dedicated bw, KBps*/
- NULL, /* tegra_isomgr_renegotiate */
- NULL); /* *priv */
- if (!dev->isomgr_handle)
- goto isomgr_reg_err;
-#endif
-
- /* dev is set in order to restore in _remove */
- platform_set_drvdata(pdev, dev);
-
- return 0;
-
-#ifdef CONFIG_ARCH_TEGRA_11x_SOC
-isomgr_reg_err:
- clk_put(dev->pll_d2_clk);
-pll_d2_clk_get_err:
- clk_put(dev->cile_clk);
-cile_clk_get_err:
- clk_put(dev->cilcd_clk);
-cilcd_clk_get_err:
- clk_put(dev->cilab_clk);
-cilab_clk_get_err:
- clk_put(dev->emc_clk);
-#endif
-emc_clk_get_err:
- clk_put(dev->csi_clk);
-csi_clk_get_err:
- clk_put(dev->csus_clk);
-csus_clk_get_err:
- clk_put(dev->vi_sensor_clk);
-vi_sensor_clk_get_err:
- clk_put(dev->vi_clk);
-vi_clk_get_err:
- clk_put(dev->isp_clk);
-isp_clk_get_err:
- misc_deregister(&dev->misc_dev);
-misc_register_err:
- regulator_put(dev->reg);
-alloc_err:
- return err;
-}
-
-static int tegra_camera_remove(struct platform_device *pdev)
-{
- struct tegra_camera_dev *dev = platform_get_drvdata(pdev);
-
-
- clk_put(dev->isp_clk);
- clk_put(dev->vi_clk);
- clk_put(dev->vi_sensor_clk);
- clk_put(dev->csus_clk);
- clk_put(dev->csi_clk);
-#ifdef CONFIG_ARCH_TEGRA_11x_SOC
- clk_put(dev->cilab_clk);
- clk_put(dev->cilcd_clk);
- clk_put(dev->cile_clk);
-#endif
-
-#ifdef CONFIG_ARCH_TEGRA_11x_SOC
- /*
- * Return memory bandwidth to isomgr.
- * If bandwidth is zero, then latency will be ignored
- * in tegra_isomgr_reserve().
- */
- {
- int ret = 0;
- ret = tegra_isomgr_reserve(dev->isomgr_handle,
- 0, /* KB/sec */
- 0); /* usec */
- if (!ret)
- return -ENOMEM;
-
- tegra_isomgr_unregister(dev->isomgr_handle);
- dev->isomgr_handle = NULL;
- }
-#endif
-
- misc_deregister(&dev->misc_dev);
- regulator_put(dev->reg);
- mutex_destroy(&dev->tegra_camera_lock);
-
- return 0;
-}
-
-static int tegra_camera_suspend(struct platform_device *pdev, pm_message_t state)
-{
- struct tegra_camera_dev *dev = platform_get_drvdata(pdev);
- int ret = 0;
-
- mutex_lock(&dev->tegra_camera_lock);
- if (dev->power_on) {
- ret = -EBUSY;
- dev_err(&pdev->dev,
- "tegra_camera cannot suspend, "
- "application is holding on to camera. \n");
- }
- mutex_unlock(&dev->tegra_camera_lock);
-
- return ret;
-}
-
-static int tegra_camera_resume(struct platform_device *pdev)
-{
- return 0;
-}
-
-static struct platform_driver tegra_camera_driver = {
- .probe = tegra_camera_probe,
- .remove = tegra_camera_remove,
- .suspend = tegra_camera_suspend,
- .resume = tegra_camera_resume,
- .driver = { .name = TEGRA_CAMERA_NAME }
-};
-
-static int __init tegra_camera_init(void)
-{
- return platform_driver_register(&tegra_camera_driver);
-}
-
-static void __exit tegra_camera_exit(void)
-{
- platform_driver_unregister(&tegra_camera_driver);
-}
-
-module_init(tegra_camera_init);
-module_exit(tegra_camera_exit);
-
diff --git a/drivers/video/tegra/Kconfig b/drivers/video/tegra/Kconfig
index 4e88419c02e7..9eaaa33d69a5 100644
--- a/drivers/video/tegra/Kconfig
+++ b/drivers/video/tegra/Kconfig
@@ -219,3 +219,11 @@ config TEGRA_HDMI_74MHZ_LIMIT
endif
+config TEGRA_CAMERA
+ bool "Enable support for tegra camera/isp hardware"
+ depends on ARCH_TEGRA
+ default y
+ help
+ Enables support for the Tegra camera interface
+
+ If unsure, say Y
diff --git a/drivers/video/tegra/Makefile b/drivers/video/tegra/Makefile
index ce581d2c81a3..1db5abcfe52e 100644
--- a/drivers/video/tegra/Makefile
+++ b/drivers/video/tegra/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_TEGRA_GRHOST) += host/
obj-$(CONFIG_TEGRA_DC) += dc/
obj-$(CONFIG_FB_TEGRA) += fb.o
obj-$(CONFIG_TEGRA_NVMAP) += nvmap/
+obj-$(CONFIG_TEGRA_CAMERA) += camera/
diff --git a/drivers/video/tegra/camera/Makefile b/drivers/video/tegra/camera/Makefile
new file mode 100644
index 000000000000..1f3930d6d997
--- /dev/null
+++ b/drivers/video/tegra/camera/Makefile
@@ -0,0 +1,7 @@
+GCOV_PROFILE := y
+EXTRA_CFLAGS += -Idrivers/video/tegra/host
+
+obj-y += camera.o
+obj-y += camera_power.o
+obj-y += camera_emc.o
+obj-y += camera_clk.o
diff --git a/drivers/video/tegra/camera/camera.c b/drivers/video/tegra/camera/camera.c
new file mode 100644
index 000000000000..9264d8fd0e5c
--- /dev/null
+++ b/drivers/video/tegra/camera/camera.c
@@ -0,0 +1,339 @@
+/*
+ * drivers/video/tegra/camera/camera.c
+ *
+ * Copyright (C) 2013 Nvidia Corp
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ */
+
+#include "camera_priv_defs.h"
+#include "camera_clk.h"
+#include "camera_power.h"
+#include "camera_emc.h"
+
+#define TEGRA_CAMERA_NAME "tegra_camera"
+
+static struct camera_clk tegra_camera_clk[] = {
+ { CAMERA_ISP_CLK, "isp"},
+ { CAMERA_VI_CLK, "vi"},
+ { CAMERA_VI_SENSOR_CLK, "vi_sensor"},
+ { CAMERA_CSUS_CLK, "csus"},
+ { CAMERA_CSI_CLK, "csi"},
+ { CAMERA_EMC_CLK, "emc"},
+#ifdef CONFIG_ARCH_TEGRA_11x_SOC
+ { CAMERA_CILAB_CLK, "cilab"},
+ { CAMERA_CILCD_CLK, "cilcd"},
+ { CAMERA_CILE_CLK, "cile"},
+ { CAMERA_PLL_D2_CLK, "pll_d2"}
+#endif
+};
+
+static long tegra_camera_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ uint id;
+ struct tegra_camera *camera = file->private_data;
+
+ /* first element of arg must be u32 with id of module to talk to */
+ if (copy_from_user(&id, (const void __user *)arg, sizeof(uint))) {
+ dev_err(camera->dev,
+ "%s: Failed to copy arg from user", __func__);
+ return -EFAULT;
+ }
+
+ if (id >= TEGRA_CAMERA_MODULE_MAX) {
+ dev_err(camera->dev,
+ "%s: Invalid id to tegra isp ioctl%d\n",
+ __func__, id);
+ return -EINVAL;
+ }
+
+ switch (cmd) {
+ /*
+ * Clock enable/disable and reset should be handled in kernel.
+ * In order to support legacy code in user space, we don't remove
+ * these IOCTL.
+ */
+ case TEGRA_CAMERA_IOCTL_ENABLE:
+ case TEGRA_CAMERA_IOCTL_DISABLE:
+ case TEGRA_CAMERA_IOCTL_RESET:
+ return 0;
+ case TEGRA_CAMERA_IOCTL_CLK_SET_RATE:
+ {
+ int ret;
+
+ if (copy_from_user(&camera->info, (const void __user *)arg,
+ sizeof(struct tegra_camera_clk_info))) {
+ dev_err(camera->dev,
+ "%s: Failed to copy arg from user\n", __func__);
+ return -EFAULT;
+ }
+ ret = tegra_camera_clk_set_rate(camera);
+ if (ret)
+ return ret;
+ if (copy_to_user((void __user *)arg, &camera->info,
+ sizeof(struct tegra_camera_clk_info))) {
+ dev_err(camera->dev,
+ "%s: Failed to copy arg to user\n", __func__);
+ return -EFAULT;
+ }
+ return 0;
+ }
+ default:
+ dev_err(camera->dev,
+ "%s: Unknown tegra_camera ioctl.\n", __func__);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int tegra_camera_open(struct inode *inode, struct file *file)
+{
+ int ret;
+ struct miscdevice *miscdev = file->private_data;
+ struct tegra_camera *camera = container_of(miscdev,
+ struct tegra_camera,
+ misc_dev);
+
+ dev_info(camera->dev, "%s: ++\n", __func__);
+
+ if (atomic_xchg(&camera->in_use, 1))
+ return -EBUSY;
+
+ file->private_data = camera;
+
+ mutex_lock(&camera->tegra_camera_lock);
+
+ /* turn on CSI regulator */
+ ret = tegra_camera_power_on(camera);
+ if (ret)
+ goto power_on_fail;
+ /* set EMC request */
+ ret = tegra_camera_enable_emc(camera);
+ if (ret)
+ goto enable_emc_fail;
+ /* enable camera HW clock */
+ ret = tegra_camera_enable_clk(camera);
+ if (ret)
+ goto enable_clk_fail;
+
+ mutex_unlock(&camera->tegra_camera_lock);
+
+ return 0;
+
+enable_clk_fail:
+ tegra_camera_disable_emc(camera);
+enable_emc_fail:
+ tegra_camera_power_off(camera);
+power_on_fail:
+ mutex_unlock(&camera->tegra_camera_lock);
+ return ret;
+}
+
+static int tegra_camera_release(struct inode *inode, struct file *file)
+{
+ int ret = 0;
+ struct tegra_camera *camera = file->private_data;
+
+ dev_info(camera->dev, "%s++\n", __func__);
+
+ mutex_lock(&camera->tegra_camera_lock);
+ /* disable HW clock */
+ ret = tegra_camera_disable_clk(camera);
+ if (ret)
+ goto release_exit;
+ /* nullify EMC request */
+ ret = tegra_camera_disable_emc(camera);
+ if (ret)
+ goto release_exit;
+ /* turn off CSI regulator */
+ ret = tegra_camera_power_off(camera);
+ if (ret)
+ goto release_exit;
+
+release_exit:
+ mutex_unlock(&camera->tegra_camera_lock);
+ WARN_ON(!atomic_xchg(&camera->in_use, 0));
+ return 0;
+}
+
+static const struct file_operations tegra_camera_fops = {
+ .owner = THIS_MODULE,
+ .open = tegra_camera_open,
+ .unlocked_ioctl = tegra_camera_ioctl,
+ .release = tegra_camera_release,
+};
+
+static int tegra_camera_clk_get(struct platform_device *ndev, const char *name,
+ struct clk **clk)
+{
+ *clk = clk_get(&ndev->dev, name);
+ if (IS_ERR_OR_NULL(*clk)) {
+ dev_err(&ndev->dev, "%s: unable to get clock for %s\n",
+ __func__, name);
+ *clk = NULL;
+ return PTR_ERR(*clk);
+ }
+ return 0;
+}
+
+struct tegra_camera *tegra_camera_register(struct platform_device *ndev)
+{
+ struct tegra_camera *camera = NULL;
+ int ret = 0;
+ int i;
+
+ dev_info(&ndev->dev, "%s: ++\n", __func__);
+
+ camera = kzalloc(sizeof(struct tegra_camera), GFP_KERNEL);
+ if (!camera) {
+ dev_err(&ndev->dev, "can't allocate memory for tegra_camera\n");
+ return camera;
+ }
+
+ mutex_init(&camera->tegra_camera_lock);
+
+ /* Powergate VE when boot */
+ mutex_lock(&camera->tegra_camera_lock);
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ ret = tegra_camera_powergate_init(camera);
+ if (ret)
+ goto regulator_fail;
+#endif
+ mutex_unlock(&camera->tegra_camera_lock);
+
+ camera->dev = &ndev->dev;
+
+ /* Get regulator pointer */
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ camera->reg = regulator_get(&ndev->dev, "vcsi");
+#else
+ camera->reg = regulator_get(&ndev->dev, "avdd_dsi_csi");
+#endif
+
+ if (IS_ERR_OR_NULL(camera->reg)) {
+ ret = -ENODEV;
+ if (camera->reg == ERR_PTR(-ENODEV)) {
+ camera->reg = NULL;
+ dev_info(&ndev->dev,
+ "%s: no regulator device, overriding\n",
+ __func__);
+ } else {
+ dev_err(&ndev->dev, "%s: couldn't get regulator\n",
+ __func__);
+ goto regulator_fail;
+ }
+ }
+
+ camera->misc_dev.minor = MISC_DYNAMIC_MINOR;
+ camera->misc_dev.name = TEGRA_CAMERA_NAME;
+ camera->misc_dev.fops = &tegra_camera_fops;
+ camera->misc_dev.parent = &ndev->dev;
+ ret = misc_register(&camera->misc_dev);
+ if (ret) {
+ dev_err(&ndev->dev, "%s: unable to register misc device!\n",
+ TEGRA_CAMERA_NAME);
+ goto misc_register_fail;
+ }
+
+ for (i = 0; i < CAMERA_CLK_MAX; i++) {
+ ret = tegra_camera_clk_get(ndev, tegra_camera_clk[i].name,
+ &camera->clk[tegra_camera_clk[i].index]);
+ if (ret)
+ goto clk_get_fail;
+ }
+
+#ifdef CONFIG_ARCH_TEGRA_11x_SOC
+ /*
+ * Dedicated bw is what VI could ask for at most.
+ * Assume that preview has 3M@30fps and video has 8M@30fps
+ * Total = 3M * 30fps * 2Bpp + 8M * 30fps * 1.5Bpp
+ * = 540MBps
+ * This number can be changed if VI requests more than this.
+ *
+ */
+ camera->isomgr_handle = tegra_isomgr_register(TEGRA_ISO_CLIENT_VI_0,
+ 540000, /* dedicated bw, KBps*/
+ NULL, /* tegra_isomgr_renegotiate */
+ NULL); /* *priv */
+ if (!camera->isomgr_handle) {
+ dev_err(&ndev->dev, "%s: unable to register isomgr\n",
+ __func__);
+ goto clk_get_fail;
+ }
+#endif
+
+ return camera;
+
+clk_get_fail:
+ for (; i > 0; i--)
+ clk_put(camera->clk[i-1]);
+ misc_deregister(&camera->misc_dev);
+misc_register_fail:
+ regulator_put(camera->reg);
+regulator_fail:
+ kfree(camera);
+ camera = NULL;
+ return camera;
+}
+
+int tegra_camera_unregister(struct tegra_camera *camera)
+{
+ dev_info(camera->dev, "%s: ++\n", __func__);
+
+#ifdef CONFIG_ARCH_TEGRA_11x_SOC
+ /*
+ * Return memory bandwidth to isomgr.
+ * If bandwidth is zero, then latency will be ignored
+ * in tegra_isomgr_reserve().
+ */
+ {
+ int ret = 0;
+
+ ret = tegra_isomgr_reserve(camera->isomgr_handle,
+ 0, /* KB/sec */
+ 0); /* usec */
+ if (!ret)
+ return -ENOMEM;
+
+ tegra_isomgr_unregister(camera->isomgr_handle);
+ camera->isomgr_handle = NULL;
+ }
+#endif
+ kfree(camera);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+int tegra_camera_suspend(struct tegra_camera *camera)
+{
+ int ret = 0;
+
+ dev_dbg(camera->dev, "%s: ++\n", __func__);
+ mutex_lock(&camera->tegra_camera_lock);
+ if (camera->power_on) {
+ ret = -EBUSY;
+ dev_err(camera->dev,
+ "tegra_camera cannot suspend, "
+ "application is holding on to camera.\n");
+ }
+ mutex_unlock(&camera->tegra_camera_lock);
+
+ return ret;
+}
+
+int tegra_camera_resume(struct tegra_camera *camera)
+{
+ dev_info(camera->dev, "%s: ++\n", __func__);
+ return 0;
+}
+#endif
diff --git a/drivers/video/tegra/camera/camera_clk.c b/drivers/video/tegra/camera/camera_clk.c
new file mode 100644
index 000000000000..d3668c9a8356
--- /dev/null
+++ b/drivers/video/tegra/camera/camera_clk.c
@@ -0,0 +1,170 @@
+/*
+ * drivers/video/tegra/camera/camera_clk.c
+ *
+ * Copyright (C) 2013 Nvidia Corp
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ */
+
+#include "camera_clk.h"
+
+int tegra_camera_enable_clk(struct tegra_camera *camera)
+{
+ int i;
+ for (i = 0; i < CAMERA_CLK_MAX; i++)
+ clk_prepare_enable(camera->clk[i]);
+ return 0;
+}
+
+int tegra_camera_disable_clk(struct tegra_camera *camera)
+{
+ int i;
+ for (i = CAMERA_CLK_MAX; i > 0; i--)
+ clk_prepare_enable(camera->clk[i-1]);
+ return 0;
+}
+
+int tegra_camera_clk_set_rate(struct tegra_camera *camera)
+{
+ struct clk *clk, *clk_parent;
+ struct tegra_camera_clk_info *info = &camera->info;
+ unsigned long parent_rate, parent_div_rate, parent_div_rate_pre;
+
+ if (!info) {
+ dev_err(camera->dev,
+ "%s: no clock info %d\n",
+ __func__, info->id);
+ return -EINVAL;
+ }
+
+ if (info->id != TEGRA_CAMERA_MODULE_VI &&
+ info->id != TEGRA_CAMERA_MODULE_EMC) {
+ dev_err(camera->dev,
+ "%s: set rate only aplies to vi module %d\n",
+ __func__, info->id);
+ return -EINVAL;
+ }
+
+ switch (info->clk_id) {
+ case TEGRA_CAMERA_VI_CLK:
+ clk = camera->clk[CAMERA_VI_CLK];
+ break;
+ case TEGRA_CAMERA_VI_SENSOR_CLK:
+ clk = camera->clk[CAMERA_VI_SENSOR_CLK];
+ break;
+ case TEGRA_CAMERA_EMC_CLK:
+ clk = camera->clk[CAMERA_EMC_CLK];
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ {
+ /*
+ * User space assumes that HW emc controller is 4
+ * byte-wide DDR controller.
+ * Emc bandwidth needs to be calcaluated using input emc
+ * freq first, and then real emc freq will
+ * be calculated using tegra_emc API.
+ * tegra_emc_bw_to_freq_req takes HW difference
+ * into consideration.
+ * bw param in tegra_emc_bw_to_freq_req() is in KHz.
+ */
+ unsigned long bw = (info->rate * 8) >> 10;
+#ifdef CONFIG_ARCH_TEGRA_11x_SOC
+ int ret = 0;
+#endif
+ dev_dbg(camera->dev, "%s: emc_clk rate=%lu\n",
+ __func__, info->rate);
+ clk_set_rate(clk,
+ tegra_emc_bw_to_freq_req(bw) << 10);
+#ifdef CONFIG_ARCH_TEGRA_11x_SOC
+ /*
+ * There is no way to figure out what latency
+ * can be tolerated in VI without reading VI
+ * registers for now. 3 usec is minimum time
+ * to switch PLL source. Let's put 4 usec as
+ * latency for now.
+ */
+ ret = tegra_isomgr_reserve(camera->isomgr_handle,
+ bw, /* KB/sec */
+ 4); /* usec */
+ if (!ret)
+ return -ENOMEM;
+
+ ret = tegra_isomgr_realize(camera->isomgr_handle);
+ if (!ret)
+ return -ENOMEM;
+#endif
+ }
+#endif
+ goto set_rate_end;
+ default:
+ dev_err(camera->dev,
+ "%s: invalid clk id for set rate %d\n",
+ __func__, info->clk_id);
+ return -EINVAL;
+ }
+
+ clk_parent = clk_get_parent(clk);
+ parent_rate = clk_get_rate(clk_parent);
+ dev_dbg(camera->dev, "%s: clk_id=%d, parent_rate=%lu, clk_rate=%lu\n",
+ __func__, info->clk_id, parent_rate, info->rate);
+ parent_div_rate = parent_rate;
+ parent_div_rate_pre = parent_rate;
+
+ /*
+ * The requested clock rate from user space should be respected.
+ * This loop is to search the clock rate that is higher than requested
+ * clock.
+ */
+ while (parent_div_rate >= info->rate) {
+ parent_div_rate_pre = parent_div_rate;
+ parent_div_rate = clk_round_rate(clk, parent_div_rate-1);
+ }
+
+ dev_dbg(camera->dev, "%s: set_rate=%lu",
+ __func__, parent_div_rate_pre);
+
+ clk_set_rate(clk, parent_div_rate_pre);
+
+ if (info->clk_id == TEGRA_CAMERA_VI_CLK) {
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ {
+ u32 val;
+ void __iomem *apb_misc =
+ IO_ADDRESS(TEGRA_APB_MISC_BASE);
+ val = readl(apb_misc + 0x42c);
+ writel(val | 0x1, apb_misc + 0x42c);
+ }
+#endif
+ if (info->flag == TEGRA_CAMERA_ENABLE_PD2VI_CLK) {
+#ifdef CONFIG_ARCH_TEGRA_11x_SOC
+ tegra_clk_cfg_ex(camera->clk[CAMERA_PLL_D2_CLK],
+ TEGRA_CLK_PLLD_CSI_OUT_ENB, 1);
+#else
+ /*
+ * bit 25: 0 = pd2vi_Clk, 1 = vi_sensor_clk
+ * bit 24: 0 = internal clock, 1 = external clock(pd2vi_clk)
+ */
+ tegra_clk_cfg_ex(clk, TEGRA_CLK_VI_INP_SEL, 2);
+#endif
+ }
+#ifdef CONFIG_ARCH_TEGRA_11x_SOC
+ else {
+ tegra_clk_cfg_ex(camera->clk[CAMERA_PLL_D2_CLK],
+ TEGRA_CLK_PLLD_CSI_OUT_ENB, 0);
+ }
+#endif
+ }
+
+set_rate_end:
+ info->rate = clk_get_rate(clk);
+ dev_dbg(camera->dev, "%s: get_rate=%lu",
+ __func__, info->rate);
+ return 0;
+}
diff --git a/drivers/video/tegra/camera/camera_clk.h b/drivers/video/tegra/camera/camera_clk.h
new file mode 100644
index 000000000000..19fc9db47f47
--- /dev/null
+++ b/drivers/video/tegra/camera/camera_clk.h
@@ -0,0 +1,25 @@
+/*
+ * drivers/video/tegra/camera/camera_clk.h
+ *
+ * Copyright (C) 2013 Nvidia Corp
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ */
+
+#ifndef __DRIVERS_VIDEO_TEGRA_CAMERA_CAMERA_CLK_H
+#define __DRIVERS_VIDEO_TEGRA_CAMERA_CAMERA_CLK_H
+#include "camera_priv_defs.h"
+
+int tegra_camera_enable_clk(struct tegra_camera *camera);
+int tegra_camera_disable_clk(struct tegra_camera *camera);
+int tegra_camera_clk_set_rate(struct tegra_camera *camera);
+
+#endif
diff --git a/drivers/video/tegra/camera/camera_emc.c b/drivers/video/tegra/camera/camera_emc.c
new file mode 100644
index 000000000000..44208c104193
--- /dev/null
+++ b/drivers/video/tegra/camera/camera_emc.c
@@ -0,0 +1,36 @@
+/*
+ * drivers/video/tegra/camera/camera_emc.c
+ *
+ * Copyright (C) 2013 Nvidia Corp
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ */
+
+#include "camera_emc.h"
+
+int tegra_camera_enable_emc(struct tegra_camera *camera)
+{
+ int ret = tegra_emc_disable_eack();
+
+ dev_dbg(camera->dev, "%s++\n", __func__);
+ clk_prepare_enable(camera->clk[CAMERA_EMC_CLK]);
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ clk_set_rate(camera->clk[TEGRA_CAMERA_EMC_CLK], 300000000);
+#endif
+ return ret;
+}
+
+int tegra_camera_disable_emc(struct tegra_camera *camera)
+{
+ dev_dbg(camera->dev, "%s++\n", __func__);
+ clk_disable_unprepare(camera->clk[CAMERA_EMC_CLK]);
+ return tegra_emc_enable_eack();
+}
diff --git a/drivers/video/tegra/camera/camera_emc.h b/drivers/video/tegra/camera/camera_emc.h
new file mode 100644
index 000000000000..8ee7d05711ef
--- /dev/null
+++ b/drivers/video/tegra/camera/camera_emc.h
@@ -0,0 +1,24 @@
+/*
+ * drivers/video/tegra/camera/camera_emc.h
+ *
+ * Copyright (C) 2013 Nvidia Corp
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ */
+
+#ifndef __DRIVERS_VIDEO_TEGRA_CAMERA_CAMERA_EMC_H
+#define __DRIVERS_VIDEO_TEGRA_CAMERA_CAMERA_EMC_H
+#include "camera_priv_defs.h"
+
+int tegra_camera_enable_emc(struct tegra_camera *camera);
+int tegra_camera_disable_emc(struct tegra_camera *camera);
+
+#endif
diff --git a/drivers/video/tegra/camera/camera_power.c b/drivers/video/tegra/camera/camera_power.c
new file mode 100644
index 000000000000..8a6d8af4c174
--- /dev/null
+++ b/drivers/video/tegra/camera/camera_power.c
@@ -0,0 +1,84 @@
+/*
+ * drivers/video/tegra/camera/camera_power.c
+ *
+ * Copyright (C) 2013 Nvidia Corp
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ */
+
+#include "camera_power.h"
+
+int tegra_camera_power_on(struct tegra_camera *camera)
+{
+ int ret = 0;
+ dev_dbg(camera->dev, "%s++\n", __func__);
+
+ /* Enable external power */
+ if (camera->reg) {
+ ret = regulator_enable(camera->reg);
+ if (ret) {
+ dev_err(camera->dev,
+ "%s: enable csi regulator failed.\n",
+ __func__);
+ return ret;
+ }
+ }
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ /* Unpowergate VE */
+ ret = tegra_unpowergate_partition(TEGRA_POWERGATE_VENC);
+ if (ret)
+ dev_err(camera->dev,
+ "%s: unpowergate failed.\n",
+ __func__);
+#endif
+ camera->power_on = 1;
+ return ret;
+}
+
+int tegra_camera_power_off(struct tegra_camera *camera)
+{
+ int ret = 0;
+
+ dev_dbg(camera->dev, "%s++\n", __func__);
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ /* Powergate VE */
+ ret = tegra_powergate_partition(TEGRA_POWERGATE_VENC);
+ if (ret)
+ dev_err(camera->dev,
+ "%s: powergate failed.\n",
+ __func__);
+#endif
+ /* Disable external power */
+ if (camera->reg) {
+ ret = regulator_disable(camera->reg);
+ if (ret) {
+ dev_err(camera->dev,
+ "%s: disable csi regulator failed.\n",
+ __func__);
+ return ret;
+ }
+ }
+ camera->power_on = 0;
+ return ret;
+}
+
+int tegra_camera_powergate_init(struct tegra_camera *camera)
+{
+ int ret = 0;
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ ret = tegra_powergate_partition(TEGRA_POWERGATE_VENC);
+ if (ret)
+ dev_err(camera->dev, "%s: powergate failed.\n", __func__);
+#endif
+ return ret;
+}
diff --git a/drivers/video/tegra/camera/camera_power.h b/drivers/video/tegra/camera/camera_power.h
new file mode 100644
index 000000000000..4628501d2064
--- /dev/null
+++ b/drivers/video/tegra/camera/camera_power.h
@@ -0,0 +1,26 @@
+/*
+ * drivers/video/tegra/camera/camera_power.h
+ *
+ * Copyright (C) 2013 Nvidia Corp
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ */
+
+#ifndef __DRIVERS_VIDEO_TEGRA_CAMERA_CAMERA_POWER_H
+#define __DRIVERS_VIDEO_TEGRA_CAMERA_CAMERA_POWER_H
+#include "camera_priv_defs.h"
+
+int tegra_camera_power_on(struct tegra_camera *camera);
+int tegra_camera_power_off(struct tegra_camera *camera);
+int tegra_camera_powergate_init(struct tegra_camera *camera);
+
+#endif
+
diff --git a/drivers/video/tegra/camera/camera_priv_defs.h b/drivers/video/tegra/camera/camera_priv_defs.h
new file mode 100644
index 000000000000..7e4ecefdf199
--- /dev/null
+++ b/drivers/video/tegra/camera/camera_priv_defs.h
@@ -0,0 +1,91 @@
+/*
+ * drivers/video/tegra/camera/camera_priv_defs.h
+ *
+ * Copyright (C) 2013 Nvidia Corp
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ */
+
+#ifndef __DRIVERS_VIDEO_TEGRA_CAMERA_CAMERA_PRIV_DEFS_H
+#define __DRIVERS_VIDEO_TEGRA_CAMERA_CAMERA_PRIV_DEFS_H
+
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/ioctl.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+
+#include <mach/powergate.h>
+#include <mach/clk.h>
+#include <mach/mc.h>
+#include <mach/iomap.h>
+#ifdef CONFIG_ARCH_TEGRA_11x_SOC
+#include <mach/isomgr.h>
+#endif
+
+#include <video/tegra_camera.h>
+
+
+/*
+ * CAMERA_*_CLK is only for internal driver use.
+ * TEGRA_CAMERA_*_CLK is enum used between driver and user space.
+ * TEGRA_CAMERA_*_CLK is defined in tegra_camera.h
+ */
+enum {
+ CAMERA_VI_CLK,
+ CAMERA_VI_SENSOR_CLK,
+ CAMERA_EMC_CLK,
+ CAMERA_ISP_CLK,
+ CAMERA_CSUS_CLK,
+ CAMERA_CSI_CLK,
+#ifdef CONFIG_ARCH_TEGRA_11x_SOC
+ CAMERA_CILAB_CLK,
+ CAMERA_CILCD_CLK,
+ CAMERA_CILE_CLK,
+ CAMERA_PLL_D2_CLK,
+#endif
+ CAMERA_CLK_MAX,
+};
+
+struct tegra_camera {
+ struct device *dev;
+ struct miscdevice misc_dev;
+ struct clk *clk[CAMERA_CLK_MAX];
+ struct regulator *reg;
+ struct tegra_camera_clk_info info;
+ struct mutex tegra_camera_lock;
+ atomic_t in_use;
+ int power_on;
+#ifdef CONFIG_ARCH_TEGRA_11x_SOC
+ tegra_isomgr_handle isomgr_handle;
+#endif
+};
+
+struct camera_clk {
+ int index;
+ char *name;
+};
+
+struct tegra_camera *tegra_camera_register(struct platform_device *ndev);
+int tegra_camera_unregister(struct tegra_camera *camera);
+#ifdef CONFIG_PM
+int tegra_camera_suspend(struct tegra_camera *camera);
+int tegra_camera_resume(struct tegra_camera *camera);
+#endif
+
+#endif
diff --git a/drivers/video/tegra/host/vi/Makefile b/drivers/video/tegra/host/vi/Makefile
index 8c130e49814d..666cdfc03321 100644
--- a/drivers/video/tegra/host/vi/Makefile
+++ b/drivers/video/tegra/host/vi/Makefile
@@ -1,5 +1,6 @@
GCOV_PROFILE := y
EXTRA_CFLAGS += -Idrivers/video/tegra/host
+EXTRA_CFLAGS += -Idrivers/video/tegra/camera
nvhost-vi-objs = \
vi.o
diff --git a/drivers/video/tegra/host/vi/vi.c b/drivers/video/tegra/host/vi/vi.c
index 8e0b342b5f85..9a571a7e3908 100644
--- a/drivers/video/tegra/host/vi/vi.c
+++ b/drivers/video/tegra/host/vi/vi.c
@@ -25,38 +25,99 @@
#include "dev.h"
#include "bus_client.h"
+#include "vi.h"
static int __devinit vi_probe(struct platform_device *dev)
{
int err = 0;
+ struct vi *tegra_vi;
struct nvhost_device_data *pdata =
(struct nvhost_device_data *)dev->dev.platform_data;
+ dev_info(&dev->dev, "%s: ++\n", __func__);
+ tegra_vi = kzalloc(sizeof(struct vi), GFP_KERNEL);
+ if (!tegra_vi) {
+ dev_err(&dev->dev, "can't allocate memory for vi\n");
+ return -ENOMEM;
+ }
+
+ tegra_vi->ndev = dev;
+ pdata->private_data = tegra_vi;
+
+#ifdef CONFIG_TEGRA_CAMERA
+ tegra_vi->camera = tegra_camera_register(dev);
+ if (!tegra_vi->camera) {
+ dev_err(&dev->dev, "%s: can't register tegra_camera\n",
+ __func__);
+ goto camera_register_fail;
+ }
+#endif
pdata->pdev = dev;
platform_set_drvdata(dev, pdata);
-
err = nvhost_client_device_get_resources(dev);
- if (err)
- return err;
-
+ if (err) {
+ goto camera_register_fail;
+ }
return nvhost_client_device_init(dev);
+
+camera_register_fail:
+ kfree(tegra_vi);
+ return err;
}
static int __exit vi_remove(struct platform_device *dev)
{
- /* Add clean-up */
+#ifdef CONFIG_TEGRA_CAMERA
+ int err = 0;
+ struct nvhost_device_data *pdata =
+ (struct nvhost_device_data *)dev->dev.platform_data;
+ struct vi *tegra_vi = (struct vi *)pdata->private_data;
+#endif
+
+ dev_info(&dev->dev, "%s: ++\n", __func__);
+
+#ifdef CONFIG_TEGRA_CAMERA
+ err = tegra_camera_unregister(tegra_vi->camera);
+ if (err)
+ return err;
+#endif
+
return 0;
}
#ifdef CONFIG_PM
static int vi_suspend(struct platform_device *dev, pm_message_t state)
{
+#ifdef CONFIG_TEGRA_CAMERA
+ struct nvhost_device_data *pdata =
+ (struct nvhost_device_data *)dev->dev.platform_data;
+ struct vi *tegra_vi = (struct vi *)pdata->private_data;
+#endif
+
+ dev_info(&dev->dev, "%s: ++\n", __func__);
+
+#ifdef CONFIG_TEGRA_CAMERA
+ tegra_camera_suspend(tegra_vi->camera);
+#endif
+
return nvhost_client_device_suspend(dev);
}
static int vi_resume(struct platform_device *dev)
{
- dev_info(&dev->dev, "resuming\n");
+#ifdef CONFIG_TEGRA_CAMERA
+ struct nvhost_device_data *pdata =
+ (struct nvhost_device_data *)dev->dev.platform_data;
+
+ struct vi *tegra_vi = (struct vi *)pdata->private_data;
+#endif
+
+ dev_info(&dev->dev, "%s: ++\n", __func__);
+
+#ifdef CONFIG_TEGRA_CAMERA
+ tegra_camera_resume(tegra_vi->camera);
+#endif
+
return 0;
}
#endif
@@ -84,5 +145,5 @@ static void __exit vi_exit(void)
platform_driver_unregister(&vi_driver);
}
-module_init(vi_init);
+late_initcall(vi_init);
module_exit(vi_exit);
diff --git a/drivers/video/tegra/host/vi/vi.h b/drivers/video/tegra/host/vi/vi.h
new file mode 100644
index 000000000000..152043d2cef3
--- /dev/null
+++ b/drivers/video/tegra/host/vi/vi.h
@@ -0,0 +1,31 @@
+/*
+ * drivers/video/tegra/host/vi/vi.h
+ *
+ * Tegra Graphics Host VI
+ *
+ * Copyright (c) 2012, NVIDIA Corporation.
+ *
+ * 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 __NVHOST_VI_H__
+#define __NVHOST_VI_H__
+
+#include "camera_priv_defs.h"
+
+struct vi {
+ struct tegra_camera *camera;
+ struct platform_device *ndev;
+};
+
+#endif