summaryrefslogtreecommitdiff
path: root/drivers/media
diff options
context:
space:
mode:
authorRebecca Schultz Zavin <rebecca@android.com>2010-08-10 13:00:02 -0700
committerDan Willemsen <dwillemsen@nvidia.com>2010-10-13 14:17:42 -0700
commitdc1b8b5255e85f379723ad7b71825dcbcdfc41c6 (patch)
tree8c3d101dab4230db05ccd53f0e81d62fcf218f87 /drivers/media
parent10582e1d3c0ed704d87143329178f43742603795 (diff)
media: video: tegra: Move tegra_isp to tegra_camera and add functionality
The tegra_camera device has been extended to include all clock and regulator functionality needed by to support camera on tegra. Change-Id: Ie1611a79c24f7ebe3ae570d38a9f470683af91dd Signed-off-by: Rebecca Schultz Zavin <rebecca@android.com>
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/video/tegra/Makefile2
-rw-r--r--drivers/media/video/tegra/tegra_camera.c371
-rw-r--r--drivers/media/video/tegra/tegra_isp.c107
3 files changed, 372 insertions, 108 deletions
diff --git a/drivers/media/video/tegra/Makefile b/drivers/media/video/tegra/Makefile
index 14375785f061..1c096b91fbbb 100644
--- a/drivers/media/video/tegra/Makefile
+++ b/drivers/media/video/tegra/Makefile
@@ -1,4 +1,4 @@
#
# Makefile for the video capture/playback device drivers.
#
-obj-y += tegra_isp.o
+obj-y += tegra_camera.o
diff --git a/drivers/media/video/tegra/tegra_camera.c b/drivers/media/video/tegra/tegra_camera.c
new file mode 100644
index 000000000000..feeeeaa659b1
--- /dev/null
+++ b/drivers/media/video/tegra/tegra_camera.c
@@ -0,0 +1,371 @@
+/*
+ * drivers/media/video/tegra/isp.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * 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/regulator/consumer.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <mach/iomap.h>
+#include <mach/clk.h>
+
+#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"
+DEFINE_MUTEX(tegra_camera_lock);
+
+struct tegra_camera_block {
+ int (*enable) (void);
+ int (*disable) (void);
+ bool is_enabled;
+};
+
+
+static struct clk *isp_clk;
+static struct clk *vi_clk;
+static struct clk *vi_sensor_clk;
+static struct clk *csus_clk;
+static struct clk *csi_clk;
+static struct regulator *tegra_camera_regulator_csi;
+
+static int tegra_camera_enable_isp(void)
+{
+ return clk_enable(isp_clk);
+}
+
+static int tegra_camera_disable_isp(void)
+{
+ clk_disable(isp_clk);
+ return 0;
+}
+
+static int tegra_camera_enable_vi(void)
+{
+ clk_enable(vi_clk);
+ clk_enable(vi_sensor_clk);
+ clk_enable(csus_clk);
+ return 0;
+}
+
+static int tegra_camera_disable_vi(void)
+{
+ clk_disable(vi_clk);
+ clk_disable(vi_sensor_clk);
+ clk_disable(csus_clk);
+ return 0;
+}
+
+static int tegra_camera_enable_csi(void)
+{
+ int ret;
+
+ ret = regulator_enable(tegra_camera_regulator_csi);
+ if (ret)
+ return ret;
+ clk_enable(csi_clk);
+ return 0;
+}
+
+static int tegra_camera_disable_csi(void)
+{
+ int ret;
+
+ ret = regulator_disable(tegra_camera_regulator_csi);
+ if (ret)
+ return ret;
+ clk_disable(csi_clk);
+ return 0;
+}
+
+struct tegra_camera_block tegra_camera_block[] = {
+ [TEGRA_CAMERA_MODULE_ISP] = {tegra_camera_enable_isp,
+ tegra_camera_disable_isp, false},
+ [TEGRA_CAMERA_MODULE_VI] = {tegra_camera_enable_vi,
+ tegra_camera_disable_vi, false},
+ [TEGRA_CAMERA_MODULE_CSI] = {tegra_camera_enable_csi,
+ tegra_camera_disable_csi, false},
+};
+
+#define TEGRA_CAMERA_VI_CLK_SEL_INTERNAL 0
+#define TEGRA_CAMERA_VI_CLK_SEL_EXTERNAL (1<<24)
+#define TEGRA_CAMERA_PD2VI_CLK_SEL_VI_SENSOR_CLK (1<<25)
+#define TEGRA_CAMERA_PD2VI_CLK_SEL_PD2VI_CLK 0
+
+static int tegra_camera_clk_set_rate(struct tegra_camera_clk_info *info)
+{
+ u32 offset;
+ struct clk *clk;
+
+ if (info->id != TEGRA_CAMERA_MODULE_VI) {
+ pr_err("%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 = vi_clk;
+ offset = 0x148;
+ break;
+ case TEGRA_CAMERA_VI_SENSOR_CLK:
+ clk = vi_sensor_clk;
+ offset = 0x1a8;
+ break;
+ default:
+ pr_err("%s: invalid clk id for set rate %d\n", __func__,
+ info->clk_id);
+ return -EINVAL;
+ }
+
+ clk_set_rate(clk, info->rate);
+
+ if (info->clk_id == TEGRA_CAMERA_VI_CLK) {
+ u32 val;
+ void __iomem *car = IO_ADDRESS(TEGRA_CLK_RESET_BASE);
+ void __iomem *apb_misc = IO_ADDRESS(TEGRA_APB_MISC_BASE);
+
+ val = readl(car + offset);
+ val &= ~(TEGRA_CAMERA_VI_CLK_SEL_EXTERNAL |
+ TEGRA_CAMERA_PD2VI_CLK_SEL_VI_SENSOR_CLK);
+ writel(val, car + offset);
+
+ val = readl(apb_misc + 0x42c);
+ writel(val | 0x1, apb_misc + 0x42c);
+ }
+
+ info->rate = clk_get_rate(clk);
+ return 0;
+
+}
+static int tegra_camera_reset(uint id)
+{
+ struct clk *clk;
+
+ switch (id) {
+ case TEGRA_CAMERA_MODULE_VI:
+ clk = vi_clk;
+ break;
+ case TEGRA_CAMERA_MODULE_ISP:
+ clk = isp_clk;
+ break;
+ case TEGRA_CAMERA_MODULE_CSI:
+ clk = csi_clk;
+ break;
+ default:
+ return -EINVAL;
+ }
+ tegra_periph_reset_assert(clk);
+ udelay(10);
+ tegra_periph_reset_deassert(clk);
+
+ return 0;
+}
+
+static int tegra_camera_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ uint id;
+
+ /* 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))) {
+ pr_err("%s: Failed to copy arg from user", __func__);
+ return -EFAULT;
+ }
+
+ if (id >= ARRAY_SIZE(tegra_camera_block)) {
+ pr_err("%s: Invalid id to tegra isp ioctl%d\n", __func__, id);
+ return -EINVAL;
+ }
+
+ switch (cmd) {
+ case TEGRA_CAMERA_IOCTL_ENABLE:
+ {
+ int ret = 0;
+
+ mutex_lock(&tegra_camera_lock);
+ if (!tegra_camera_block[id].is_enabled) {
+ ret = tegra_camera_block[id].enable();
+ tegra_camera_block[id].is_enabled = true;
+ }
+ mutex_unlock(&tegra_camera_lock);
+ return ret;
+ }
+ case TEGRA_CAMERA_IOCTL_DISABLE:
+ {
+ int ret = 0;
+
+ mutex_lock(&tegra_camera_lock);
+ if (tegra_camera_block[id].is_enabled) {
+ ret = tegra_camera_block[id].disable();
+ tegra_camera_block[id].is_enabled = false;
+ }
+ mutex_unlock(&tegra_camera_lock);
+ return ret;
+ }
+ case TEGRA_CAMERA_IOCTL_CLK_SET_RATE:
+ {
+ struct tegra_camera_clk_info info;
+ int ret;
+
+ if (copy_from_user(&info, (const void __user *)arg,
+ sizeof(struct tegra_camera_clk_info))) {
+ pr_err("%s: Failed to copy arg from user\n", __func__);
+ return -EFAULT;
+ }
+ ret = tegra_camera_clk_set_rate(&info);
+ if (ret)
+ return ret;
+ if (copy_to_user((void __user *)arg, &info,
+ sizeof(struct tegra_camera_clk_info))) {
+ pr_err("%s: Failed to copy arg to user\n", __func__);
+ return -EFAULT;
+ }
+ return 0;
+ }
+ case TEGRA_CAMERA_IOCTL_RESET:
+ return tegra_camera_reset(id);
+ default:
+ pr_err("%s: Unknown tegra_camera ioctl.\n", TEGRA_CAMERA_NAME);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int tegra_camera_release(struct inode *inode, struct file *file)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tegra_camera_block); i++)
+ if (tegra_camera_block[i].is_enabled) {
+ tegra_camera_block[i].disable();
+ tegra_camera_block[i].is_enabled = false;
+ }
+
+ return 0;
+}
+
+static const struct file_operations tegra_camera_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = tegra_camera_ioctl,
+ .release = tegra_camera_release,
+};
+
+static struct miscdevice tegra_camera_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = TEGRA_CAMERA_NAME,
+ .fops = &tegra_camera_fops,
+};
+
+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)) {
+ pr_err("%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;
+
+ pr_info("%s: probe\n", TEGRA_CAMERA_NAME);
+ tegra_camera_regulator_csi = regulator_get(&pdev->dev, "vcsi");
+ if (IS_ERR_OR_NULL(tegra_camera_regulator_csi)) {
+ pr_err("%s: Couldn't get regulator vcsi\n", TEGRA_CAMERA_NAME);
+ return PTR_ERR(tegra_camera_regulator_csi);
+ }
+
+ err = misc_register(&tegra_camera_device);
+ if (err) {
+ pr_err("%s: Unable to register misc device!\n",
+ TEGRA_CAMERA_NAME);
+ goto misc_register_err;
+ }
+
+ err = tegra_camera_clk_get(pdev, "isp", &isp_clk);
+ if (err)
+ goto misc_register_err;
+ err = tegra_camera_clk_get(pdev, "vi", &vi_clk);
+ if (err)
+ goto vi_clk_get_err;
+ err = tegra_camera_clk_get(pdev, "vi_sensor", &vi_sensor_clk);
+ if (err)
+ goto vi_sensor_clk_get_err;
+ err = tegra_camera_clk_get(pdev, "csus", &csus_clk);
+ if (err)
+ goto csus_clk_get_err;
+ err = tegra_camera_clk_get(pdev, "csi", &csi_clk);
+ if (err)
+ goto csi_clk_get_err;
+
+ return 0;
+
+csi_clk_get_err:
+ clk_put(csus_clk);
+csus_clk_get_err:
+ clk_put(vi_sensor_clk);
+vi_sensor_clk_get_err:
+ clk_put(vi_clk);
+vi_clk_get_err:
+ clk_put(isp_clk);
+misc_register_err:
+ regulator_put(tegra_camera_regulator_csi);
+ return err;
+}
+
+static int tegra_camera_remove(struct platform_device *pdev)
+{
+ clk_put(isp_clk);
+ clk_put(vi_clk);
+ clk_put(vi_sensor_clk);
+ clk_put(csus_clk);
+ clk_put(csi_clk);
+
+ regulator_put(tegra_camera_regulator_csi);
+ misc_deregister(&tegra_camera_device);
+ return 0;
+}
+
+static struct platform_driver tegra_camera_driver = {
+ .probe = tegra_camera_probe,
+ .remove = tegra_camera_remove,
+ .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/media/video/tegra/tegra_isp.c b/drivers/media/video/tegra/tegra_isp.c
deleted file mode 100644
index f82f5d96e331..000000000000
--- a/drivers/media/video/tegra/tegra_isp.c
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * drivers/media/video/tegra/isp.c
- *
- * Copyright (C) 2010 Google, Inc.
- *
- * 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/regulator/consumer.h>
-#include <media/tegra_isp.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_ISP_NAME "tegra_isp"
-
-static struct regulator *tegra_isp_regulator_csi;
-
-static int tegra_isp_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- switch (cmd) {
- case TEGRA_ISP_IOCTL_CSI_POWER_ON:
- regulator_enable(tegra_isp_regulator_csi);
- break;
- case TEGRA_ISP_IOCTL_CSI_POWER_OFF:
- regulator_disable(tegra_isp_regulator_csi);
- break;
- default:
- pr_err("%s: Unknoown tegra_isp ioctl.\n", TEGRA_ISP_NAME);
- }
- return 0;
-}
-
-static const struct file_operations tegra_isp_fops = {
- .owner = THIS_MODULE,
- .ioctl = tegra_isp_ioctl,
-};
-
-static struct miscdevice tegra_isp_device = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = TEGRA_ISP_NAME,
- .fops = &tegra_isp_fops,
-};
-
-static int tegra_isp_probe(struct platform_device *pdev)
-{
- int err;
-
- pr_info("%s: probe\n", TEGRA_ISP_NAME);
- tegra_isp_regulator_csi = regulator_get(NULL, "vcsi");
- if (IS_ERR(tegra_isp_regulator_csi)) {
- pr_err("%s: Couldn't get regulator vcsi\n", TEGRA_ISP_NAME);
- return PTR_ERR(tegra_isp_regulator_csi);
- }
-
- err = misc_register(&tegra_isp_device);
- if (err) {
- pr_err("%s: Unable to register misc device!\n", TEGRA_ISP_NAME);
- regulator_put(tegra_isp_regulator_csi);
- return err;
- }
-
- /* XXX FIX ME -- SHOULDN'T BE ALWAYS ON! */
- regulator_enable(tegra_isp_regulator_csi);
- return 0;
-}
-
-static int tegra_isp_remove(struct platform_device *pdev)
-{
- /* XXX FIX ME -- SHOULDN'T BE ALWAYS ON! */
- regulator_disable(tegra_isp_regulator_csi);
- misc_deregister(&tegra_isp_device);
- return 0;
-}
-
-static struct platform_driver tegra_isp_driver = {
- .probe = tegra_isp_probe,
- .remove = tegra_isp_remove,
- .driver = { .name = TEGRA_ISP_NAME }
-};
-
-static int __init tegra_isp_init(void)
-{
- return platform_driver_register(&tegra_isp_driver);
-}
-
-static void __exit tegra_isp_exit(void)
-{
- platform_driver_unregister(&tegra_isp_driver);
-}
-
-module_init(tegra_isp_init);
-module_exit(tegra_isp_exit);
-