diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/media/video/tegra/Kconfig | 9 | ||||
-rw-r--r-- | drivers/media/video/tegra/Makefile | 2 | ||||
-rw-r--r-- | drivers/media/video/tegra/ov5650.c | 2 | ||||
-rw-r--r-- | drivers/media/video/tegra/tegra_camera.c | 697 | ||||
-rw-r--r-- | drivers/video/tegra/Kconfig | 8 | ||||
-rw-r--r-- | drivers/video/tegra/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/tegra/camera/Makefile | 7 | ||||
-rw-r--r-- | drivers/video/tegra/camera/camera.c | 339 | ||||
-rw-r--r-- | drivers/video/tegra/camera/camera_clk.c | 170 | ||||
-rw-r--r-- | drivers/video/tegra/camera/camera_clk.h | 25 | ||||
-rw-r--r-- | drivers/video/tegra/camera/camera_emc.c | 36 | ||||
-rw-r--r-- | drivers/video/tegra/camera/camera_emc.h | 24 | ||||
-rw-r--r-- | drivers/video/tegra/camera/camera_power.c | 84 | ||||
-rw-r--r-- | drivers/video/tegra/camera/camera_power.h | 26 | ||||
-rw-r--r-- | drivers/video/tegra/camera/camera_priv_defs.h | 91 | ||||
-rw-r--r-- | drivers/video/tegra/host/vi/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/tegra/host/vi/vi.c | 75 | ||||
-rw-r--r-- | drivers/video/tegra/host/vi/vi.h | 31 |
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 |