summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJihoon Bang <jbang@nvidia.com>2011-10-19 10:00:23 -0700
committerSimone Willett <swillett@nvidia.com>2011-10-25 13:21:11 -0700
commit88a1aaf5b80f8a76d7f35977a189e641f8c7dcdf (patch)
treedb68ab88c15042dfab8b4efa2b939fe15ce224b2
parent2b19c77b495b19367a86fdd2c52a07f00b92687f (diff)
media: video: tegra: apply powergate based on chipset revision
Apply powergate differently depending on chip revision. Remove dependency of regulator and csi. Enable regulator when any client asks for power. Previously regulator was enabled only when csi was enabled. Bug 855758 Bug 878057 Change-Id: Ib231d1f1074aa1547c223e294fe9d72cf81f8f9e Reviewed-on: http://git-master/r/59369 Reviewed-by: Jihoon Bang <jbang@nvidia.com> Tested-by: Jihoon Bang <jbang@nvidia.com> Tested-by: Gerrit_Virtual_Submit Reviewed-by: Dan Willemsen <dwillemsen@nvidia.com>
-rw-r--r--drivers/media/video/tegra/tegra_camera.c348
1 files changed, 223 insertions, 125 deletions
diff --git a/drivers/media/video/tegra/tegra_camera.c b/drivers/media/video/tegra/tegra_camera.c
index 438e431fa033..309ddc4a47d2 100644
--- a/drivers/media/video/tegra/tegra_camera.c
+++ b/drivers/media/video/tegra/tegra_camera.c
@@ -18,6 +18,7 @@
#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>
@@ -33,69 +34,64 @@
* 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_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 regulator *reg;
+ struct tegra_camera_clk_info info;
+ struct mutex tegra_camera_lock;
+ int power_refcnt;
+};
struct tegra_camera_block {
- int (*enable) (void);
- int (*disable) (void);
+ int (*enable) (struct tegra_camera_dev *dev);
+ int (*disable) (struct tegra_camera_dev *dev);
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_powergate;
-
-static int tegra_camera_enable_isp(void)
+static int tegra_camera_enable_isp(struct tegra_camera_dev *dev)
{
- return clk_enable(isp_clk);
+ return clk_enable(dev->isp_clk);
}
-static int tegra_camera_disable_isp(void)
+static int tegra_camera_disable_isp(struct tegra_camera_dev *dev)
{
- clk_disable(isp_clk);
+ clk_disable(dev->isp_clk);
return 0;
}
-static int tegra_camera_enable_vi(void)
+static int tegra_camera_enable_vi(struct tegra_camera_dev *dev)
{
- clk_enable(vi_clk);
- clk_enable(vi_sensor_clk);
- clk_enable(csus_clk);
- return 0;
+ int ret = 0;
+
+ ret |= clk_enable(dev->vi_clk);
+ ret |= clk_enable(dev->vi_sensor_clk);
+ ret |= clk_enable(dev->csus_clk);
+ return ret;
}
-static int tegra_camera_disable_vi(void)
+static int tegra_camera_disable_vi(struct tegra_camera_dev *dev)
{
- clk_disable(vi_clk);
- clk_disable(vi_sensor_clk);
- clk_disable(csus_clk);
+ clk_disable(dev->vi_clk);
+ clk_disable(dev->vi_sensor_clk);
+ clk_disable(dev->csus_clk);
return 0;
}
-static int tegra_camera_enable_csi(void)
+static int tegra_camera_enable_csi(struct tegra_camera_dev *dev)
{
- int ret;
-
- ret = regulator_enable(tegra_camera_regulator_csi);
- if (ret)
- return ret;
- clk_enable(csi_clk);
- return 0;
+ return clk_enable(dev->csi_clk);
}
-static int tegra_camera_disable_csi(void)
+static int tegra_camera_disable_csi(struct tegra_camera_dev *dev)
{
- int ret;
-
- ret = regulator_disable(tegra_camera_regulator_csi);
- if (ret)
- return ret;
- clk_disable(csi_clk);
+ clk_disable(dev->csi_clk);
return 0;
}
@@ -113,29 +109,39 @@ struct tegra_camera_block tegra_camera_block[] = {
#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)
+static int tegra_camera_clk_set_rate(struct tegra_camera_dev *dev)
{
u32 offset;
struct clk *clk;
+ struct tegra_camera_clk_info *info = &dev->info;
+
+ if (!info) {
+ dev_err(dev->dev,
+ "%s: no clock info %d\n",
+ __func__, info->id);
+ return -EINVAL;
+ }
if (info->id != TEGRA_CAMERA_MODULE_VI) {
- pr_err("%s: Set rate only aplies to vi module %d\n", __func__,
- info->id);
+ 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 = vi_clk;
+ clk = dev->vi_clk;
offset = 0x148;
break;
case TEGRA_CAMERA_VI_SENSOR_CLK:
- clk = vi_sensor_clk;
+ clk = dev->vi_sensor_clk;
offset = 0x1a8;
break;
default:
- pr_err("%s: invalid clk id for set rate %d\n", __func__,
- info->clk_id);
+ dev_err(dev->dev,
+ "%s: invalid clk id for set rate %d\n",
+ __func__, info->clk_id);
return -EINVAL;
}
@@ -160,19 +166,19 @@ static int tegra_camera_clk_set_rate(struct tegra_camera_clk_info *info)
return 0;
}
-static int tegra_camera_reset(uint id)
+static int tegra_camera_reset(struct tegra_camera_dev *dev, uint id)
{
struct clk *clk;
switch (id) {
case TEGRA_CAMERA_MODULE_VI:
- clk = vi_clk;
+ clk = dev->vi_clk;
break;
case TEGRA_CAMERA_MODULE_ISP:
- clk = isp_clk;
+ clk = dev->isp_clk;
break;
case TEGRA_CAMERA_MODULE_CSI:
- clk = csi_clk;
+ clk = dev->csi_clk;
break;
default:
return -EINVAL;
@@ -184,19 +190,78 @@ static int tegra_camera_reset(uint id)
return 0;
}
+static int tegra_camera_power_on(struct tegra_camera_dev *dev)
+{
+ int ret = 0;
+
+ if (dev->power_refcnt++ == 0) {
+ /* 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
+ }
+
+ return ret;
+}
+
+static int tegra_camera_power_off(struct tegra_camera_dev *dev)
+{
+ int ret = 0;
+
+ if (--dev->power_refcnt == 0) {
+#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;
+ }
+ }
+ }
+ 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))) {
- pr_err("%s: Failed to copy arg from user", __func__);
+ dev_err(dev->dev,
+ "%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);
+ dev_err(dev->dev,
+ "%s: Invalid id to tegra isp ioctl%d\n",
+ __func__, id);
return -EINVAL;
}
@@ -205,94 +270,103 @@ static long tegra_camera_ioctl(struct file *file,
{
int ret = 0;
- mutex_lock(&tegra_camera_lock);
+ mutex_lock(&dev->tegra_camera_lock);
/* Unpowergate camera blocks (vi, csi and isp)
before enabling clocks */
- if (tegra_camera_powergate++ == 0) {
- ret = tegra_unpowergate_partition(TEGRA_POWERGATE_VENC);
- if (ret) {
- tegra_powergate_partition(TEGRA_POWERGATE_VENC);
- pr_err("%s: Unpowergating failed.\n", __func__);
- tegra_camera_powergate = 0;
- mutex_unlock(&tegra_camera_lock);
- return ret;
- }
+ ret = tegra_camera_power_on(dev);
+ if (ret) {
+ dev->power_refcnt = 0;
+ mutex_unlock(&dev->tegra_camera_lock);
+ return ret;
}
if (!tegra_camera_block[id].is_enabled) {
- ret = tegra_camera_block[id].enable();
+ ret = tegra_camera_block[id].enable(dev);
tegra_camera_block[id].is_enabled = true;
}
- mutex_unlock(&tegra_camera_lock);
+ mutex_unlock(&dev->tegra_camera_lock);
return ret;
}
case TEGRA_CAMERA_IOCTL_DISABLE:
{
int ret = 0;
- mutex_lock(&tegra_camera_lock);
+ mutex_lock(&dev->tegra_camera_lock);
if (tegra_camera_block[id].is_enabled) {
- ret = tegra_camera_block[id].disable();
+ ret = tegra_camera_block[id].disable(dev);
tegra_camera_block[id].is_enabled = false;
}
/* Powergate camera blocks (vi, csi and isp)
after disabling all the clocks */
if (!ret) {
- if (--tegra_camera_powergate == 0) {
- ret = tegra_powergate_partition(TEGRA_POWERGATE_VENC);
- if (ret)
- pr_err("%s: Powergating failed.\n", __func__);
- }
+ ret = tegra_camera_power_off(dev);
}
- mutex_unlock(&tegra_camera_lock);
+ mutex_unlock(&dev->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,
+ if (copy_from_user(&dev->info, (const void __user *)arg,
sizeof(struct tegra_camera_clk_info))) {
- pr_err("%s: Failed to copy arg from user\n", __func__);
+ dev_err(dev->dev,
+ "%s: Failed to copy arg from user\n", __func__);
return -EFAULT;
}
- ret = tegra_camera_clk_set_rate(&info);
+ ret = tegra_camera_clk_set_rate(dev);
if (ret)
return ret;
- if (copy_to_user((void __user *)arg, &info,
+ if (copy_to_user((void __user *)arg, &dev->info,
sizeof(struct tegra_camera_clk_info))) {
- pr_err("%s: Failed to copy arg to user\n", __func__);
+ dev_err(dev->dev,
+ "%s: Failed to copy arg to user\n", __func__);
return -EFAULT;
}
return 0;
}
case TEGRA_CAMERA_IOCTL_RESET:
- return tegra_camera_reset(id);
+ return tegra_camera_reset(dev, id);
default:
- pr_err("%s: Unknown tegra_camera ioctl.\n", TEGRA_CAMERA_NAME);
+ 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);
+ dev_info(dev->dev, "%s\n", __func__);
+ file->private_data = dev;
+
+ return 0;
+}
+
static int tegra_camera_release(struct inode *inode, struct file *file)
{
- int i, err = 0;
+ int i, err;
+ struct tegra_camera_dev *dev = file->private_data;
+ dev_info(dev->dev, "%s\n", __func__);
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].disable(dev);
tegra_camera_block[i].is_enabled = false;
}
+
/* If camera blocks are not powergated yet, do it now */
- if (tegra_camera_powergate > 0) {
- mutex_lock(&tegra_camera_lock);
- tegra_camera_powergate = 0;
+ if (dev->power_refcnt > 0) {
+ mutex_lock(&dev->tegra_camera_lock);
err = tegra_powergate_partition(TEGRA_POWERGATE_VENC);
if (err)
- pr_err("%s: Powergating failed.\n", __func__);
- mutex_unlock(&tegra_camera_lock);
+ dev_err(dev->dev, "%s: powergate failed.\n", __func__);
+ dev->power_refcnt = 0;
+ mutex_unlock(&dev->tegra_camera_lock);
}
return 0;
@@ -300,22 +374,18 @@ static int tegra_camera_release(struct inode *inode, struct file *file)
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 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);
+ dev_err(&pdev->dev, "%s: unable to get clock for %s\n",
+ __func__, name);
*clk = NULL;
return PTR_ERR(*clk);
}
@@ -325,74 +395,102 @@ static int tegra_camera_clk_get(struct platform_device *pdev, const char *name,
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;
+ }
- pr_info("%s: probe\n", TEGRA_CAMERA_NAME);
+ mutex_init(&dev->tegra_camera_lock);
- mutex_lock(&tegra_camera_lock);
- tegra_camera_powergate = 0;
+ /* Powergate VE when boot */
+ dev->power_refcnt = 0;
+ mutex_lock(&dev->tegra_camera_lock);
err = tegra_powergate_partition(TEGRA_POWERGATE_VENC);
if (err)
- pr_err("%s: Powergating failed.\n", __func__);
- mutex_unlock(&tegra_camera_lock);
+ dev_err(&pdev->dev, "%s: powergate failed.\n", __func__);
+ mutex_unlock(&dev->tegra_camera_lock);
+
+ dev->dev = &pdev->dev;
+ /* Get regulator pointer */
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
- tegra_camera_regulator_csi = regulator_get(&pdev->dev, "vcsi");
+ dev->reg = regulator_get(&pdev->dev, "vcsi");
#else
- tegra_camera_regulator_csi = regulator_get(&pdev->dev, "avdd_dsi_csi");
+ dev->reg = regulator_get(&pdev->dev, "avdd_dsi_csi");
#endif
- if (IS_ERR_OR_NULL(tegra_camera_regulator_csi)) {
- pr_err("%s: Couldn't get regulator\n", TEGRA_CAMERA_NAME);
- return PTR_ERR(tegra_camera_regulator_csi);
+ if (IS_ERR_OR_NULL(dev->reg)) {
+ dev_err(&pdev->dev, "%s: couldn't get regulator\n", __func__);
+ return PTR_ERR(dev->reg);
}
- err = misc_register(&tegra_camera_device);
+ 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) {
- pr_err("%s: Unable to register misc device!\n",
+ 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", &isp_clk);
+ err = tegra_camera_clk_get(pdev, "isp", &dev->isp_clk);
if (err)
goto misc_register_err;
- err = tegra_camera_clk_get(pdev, "vi", &vi_clk);
+ 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", &vi_sensor_clk);
+ 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", &csus_clk);
+ err = tegra_camera_clk_get(pdev, "csus", &dev->csus_clk);
if (err)
goto csus_clk_get_err;
- err = tegra_camera_clk_get(pdev, "csi", &csi_clk);
+ err = tegra_camera_clk_get(pdev, "csi", &dev->csi_clk);
if (err)
goto csi_clk_get_err;
+ /* dev is set in order to restore in _remove */
+ platform_set_drvdata(pdev, dev);
+
return 0;
csi_clk_get_err:
- clk_put(csus_clk);
+ clk_put(dev->csus_clk);
csus_clk_get_err:
- clk_put(vi_sensor_clk);
+ clk_put(dev->vi_sensor_clk);
vi_sensor_clk_get_err:
- clk_put(vi_clk);
+ clk_put(dev->vi_clk);
vi_clk_get_err:
- clk_put(isp_clk);
+ clk_put(dev->isp_clk);
misc_register_err:
- regulator_put(tegra_camera_regulator_csi);
+ regulator_put(dev->reg);
+alloc_err:
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);
+ 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);
+
+ misc_deregister(&dev->misc_dev);
+ regulator_put(dev->reg);
+ mutex_destroy(&dev->tegra_camera_lock);
+
return 0;
}