From 44da1b533e5abcca6e5fe5042443ef0e28c7c907 Mon Sep 17 00:00:00 2001 From: Rebecca Schultz Zavin Date: Mon, 1 Nov 2010 15:39:10 -0700 Subject: [PATCH] media: video: tegra: add basic camera/isp support The tegra_camera device includes all clock and regulator functionality needed to support camera on tegra. Change-Id: I281f1cb5f360276af832309e35dbe17c98b62bb3 Signed-off-by: Dima Zavin --- drivers/media/video/Kconfig | 1 + drivers/media/video/Makefile | 1 + drivers/media/video/tegra/Kconfig | 8 + drivers/media/video/tegra/Makefile | 1 + drivers/media/video/tegra/tegra_camera.c | 368 +++++++++++++++++++++++ include/media/tegra_camera.h | 38 +++ 6 files changed, 417 insertions(+) create mode 100644 drivers/media/video/tegra/Kconfig create mode 100644 drivers/media/video/tegra/Makefile create mode 100644 drivers/media/video/tegra/tegra_camera.c create mode 100644 include/media/tegra_camera.h diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index f6e4d0475351..e3b374110897 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -553,6 +553,7 @@ config VIDEO_VIVI source "drivers/media/video/davinci/Kconfig" source "drivers/media/video/omap/Kconfig" +source "drivers/media/video/tegra/Kconfig" source "drivers/media/video/bt8xx/Kconfig" diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 40f98fba5f88..399ff510d79c 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -179,6 +179,7 @@ obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o obj-y += davinci/ obj-$(CONFIG_ARCH_OMAP) += omap/ +obj-$(CONFIG_ARCH_TEGRA) += tegra/ EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core EXTRA_CFLAGS += -Idrivers/media/dvb/frontends diff --git a/drivers/media/video/tegra/Kconfig b/drivers/media/video/tegra/Kconfig new file mode 100644 index 000000000000..86c6aa2c466b --- /dev/null +++ b/drivers/media/video/tegra/Kconfig @@ -0,0 +1,8 @@ +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/media/video/tegra/Makefile b/drivers/media/video/tegra/Makefile new file mode 100644 index 000000000000..e5a074ea6608 --- /dev/null +++ b/drivers/media/video/tegra/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_TEGRA_CAMERA) += 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..f310d0f5619f --- /dev/null +++ b/drivers/media/video/tegra/tegra_camera.c @@ -0,0 +1,368 @@ +/* + * drivers/media/video/tegra/tegra_camera.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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* 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); + + writel(0x2, 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 long tegra_camera_ioctl(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, + .unlocked_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/include/media/tegra_camera.h b/include/media/tegra_camera.h new file mode 100644 index 000000000000..3c8ddcab26e3 --- /dev/null +++ b/include/media/tegra_camera.h @@ -0,0 +1,38 @@ +/* + * include/linux/tegra_camera.h + * + * 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. + * + */ + +enum { + TEGRA_CAMERA_MODULE_ISP = 0, + TEGRA_CAMERA_MODULE_VI, + TEGRA_CAMERA_MODULE_CSI, +}; + +enum { + TEGRA_CAMERA_VI_CLK, + TEGRA_CAMERA_VI_SENSOR_CLK, +}; + +struct tegra_camera_clk_info { + uint id; + uint clk_id; + unsigned long rate; +}; + +#define TEGRA_CAMERA_IOCTL_ENABLE _IOWR('i', 1, uint) +#define TEGRA_CAMERA_IOCTL_DISABLE _IOWR('i', 2, uint) +#define TEGRA_CAMERA_IOCTL_CLK_SET_RATE \ + _IOWR('i', 3, struct tegra_camera_clk_info) +#define TEGRA_CAMERA_IOCTL_RESET _IOWR('i', 4, uint) -- 2.34.1