summaryrefslogtreecommitdiff
path: root/drivers/video/tegra/camera/camera.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/tegra/camera/camera.c')
-rw-r--r--drivers/video/tegra/camera/camera.c339
1 files changed, 339 insertions, 0 deletions
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