From c458d6187bc932347e69360e548df3a695f9a9d2 Mon Sep 17 00:00:00 2001 From: Jihoon Bang Date: Mon, 26 Mar 2012 14:24:22 -0700 Subject: media: video: tegra: tegra_camera: re-arch power and clock There are a couple of issues found in tegra_camera. 1. clock enable/disable is controlled by user space. -> If client process crashes, there is no way to disable clock. 2. power enable/disable is associated with clock enable. -> There is no reason to relate power with clock. -> There is only one regulator for this driver. -> As same as #1, it may leave power up when client process crashes. 3. driver allows multiple clients to access. -> This is not the case for this driver. This changes addresses the problems described above. Bug 948780 Change-Id: Ie534771327175f56cf0e138f1c07096ddba470a8 Signed-off-by: Jihoon Bang Reviewed-on: http://git-master/r/92386 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: David Schalig Reviewed-by: Dan Willemsen --- drivers/media/video/tegra/tegra_camera.c | 298 ++++++++++++------------------- 1 file changed, 119 insertions(+), 179 deletions(-) (limited to 'drivers') diff --git a/drivers/media/video/tegra/tegra_camera.c b/drivers/media/video/tegra/tegra_camera.c index e90b130bcd65..36ecde087bed 100644 --- a/drivers/media/video/tegra/tegra_camera.c +++ b/drivers/media/video/tegra/tegra_camera.c @@ -48,7 +48,8 @@ struct tegra_camera_dev { struct regulator *reg; struct tegra_camera_clk_info info; struct mutex tegra_camera_lock; - int power_refcnt; + atomic_t in_use; + int power_on; }; struct tegra_camera_block { @@ -57,52 +58,51 @@ struct tegra_camera_block { bool is_enabled; }; -static int tegra_camera_enable_isp(struct tegra_camera_dev *dev) +static int tegra_camera_enable_clk(struct tegra_camera_dev *dev) { - return clk_enable(dev->isp_clk); -} - -static int tegra_camera_disable_isp(struct tegra_camera_dev *dev) -{ - clk_disable(dev->isp_clk); + clk_enable(dev->vi_clk); + clk_enable(dev->vi_sensor_clk); + clk_enable(dev->csus_clk); + + tegra_periph_reset_assert(dev->vi_clk); + udelay(2); + tegra_periph_reset_deassert(dev->vi_clk); + + clk_enable(dev->isp_clk); + tegra_periph_reset_assert(dev->isp_clk); + udelay(2); + tegra_periph_reset_deassert(dev->isp_clk); + + clk_enable(dev->csi_clk); + tegra_periph_reset_assert(dev->csi_clk); + udelay(2); + tegra_periph_reset_deassert(dev->csi_clk); return 0; } -static int tegra_camera_enable_vi(struct tegra_camera_dev *dev) -{ - 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(struct tegra_camera_dev *dev) +static int tegra_camera_disable_clk(struct tegra_camera_dev *dev) { - clk_disable(dev->vi_clk); - clk_disable(dev->vi_sensor_clk); + clk_disable(dev->csi_clk); + tegra_periph_reset_assert(dev->csi_clk); + clk_disable(dev->isp_clk); + tegra_periph_reset_assert(dev->isp_clk); clk_disable(dev->csus_clk); - return 0; -} - -static int tegra_camera_enable_csi(struct tegra_camera_dev *dev) -{ - return clk_enable(dev->csi_clk); -} + clk_disable(dev->vi_sensor_clk); + clk_disable(dev->vi_clk); + tegra_periph_reset_assert(dev->vi_clk); -static int tegra_camera_disable_csi(struct tegra_camera_dev *dev) -{ - clk_disable(dev->csi_clk); return 0; } static int tegra_camera_enable_emc(struct tegra_camera_dev *dev) { - /* tegra_camera wasn't added as a user of emc_clk until 3x. - set to 150 MHz, will likely need to be increased as we support - sensors with higher framerates and resolutions. */ + /* + * tegra_camera wasn't added as a user of emc_clk until 3x. + * set to 150 MHz, will likely need to be increased as we support + * sensors with higher framerates and resolutions. + */ clk_enable(dev->emc_clk); + #ifdef CONFIG_ARCH_TEGRA_2x_SOC clk_set_rate(dev->emc_clk, 300000000); #else @@ -117,32 +117,6 @@ static int tegra_camera_disable_emc(struct tegra_camera_dev *dev) 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 bool tegra_camera_enabled(struct tegra_camera_dev *dev) -{ - bool ret = false; - - mutex_lock(&dev->tegra_camera_lock); - ret = tegra_camera_block[TEGRA_CAMERA_MODULE_ISP].is_enabled == true || - tegra_camera_block[TEGRA_CAMERA_MODULE_VI].is_enabled == true || - tegra_camera_block[TEGRA_CAMERA_MODULE_CSI].is_enabled == true; - mutex_unlock(&dev->tegra_camera_lock); - return ret; -} - static int tegra_camera_clk_set_rate(struct tegra_camera_dev *dev) { struct clk *clk, *clk_parent; @@ -221,55 +195,32 @@ static int tegra_camera_clk_set_rate(struct tegra_camera_dev *dev) return 0; } -static int tegra_camera_reset(struct tegra_camera_dev *dev, uint id) -{ - struct clk *clk; - - switch (id) { - case TEGRA_CAMERA_MODULE_VI: - clk = dev->vi_clk; - break; - case TEGRA_CAMERA_MODULE_ISP: - clk = dev->isp_clk; - break; - case TEGRA_CAMERA_MODULE_CSI: - clk = dev->csi_clk; - break; - default: - return -EINVAL; - } - tegra_periph_reset_assert(clk); - udelay(10); - tegra_periph_reset_deassert(clk); - - 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_dbg(dev->dev, "%s++\n", __func__); + + /* Enable external power */ + if (dev->reg) { + ret = regulator_enable(dev->reg); + if (ret) { dev_err(dev->dev, - "%s: unpowergate failed.\n", + "%s: enable csi regulator failed.\n", __func__); -#endif + 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; } @@ -277,26 +228,27 @@ static int tegra_camera_power_off(struct tegra_camera_dev *dev) { int ret = 0; - if (--dev->power_refcnt == 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) + /* 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: powergate failed.\n", + "%s: disable csi regulator 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; } } + dev->power_on = 0; return ret; } @@ -313,7 +265,7 @@ static long tegra_camera_ioctl(struct file *file, return -EFAULT; } - if (id >= ARRAY_SIZE(tegra_camera_block)) { + if (id >= TEGRA_CAMERA_MODULE_MAX) { dev_err(dev->dev, "%s: Invalid id to tegra isp ioctl%d\n", __func__, id); @@ -321,44 +273,15 @@ static long tegra_camera_ioctl(struct file *file, } 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: - { - int ret = 0; - - mutex_lock(&dev->tegra_camera_lock); - /* Unpowergate camera blocks (vi, csi and isp) - before enabling clocks */ - 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(dev); - tegra_camera_block[id].is_enabled = true; - } - mutex_unlock(&dev->tegra_camera_lock); - return ret; - } case TEGRA_CAMERA_IOCTL_DISABLE: - { - int ret = 0; - - mutex_lock(&dev->tegra_camera_lock); - if (tegra_camera_block[id].is_enabled) { - 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) { - ret = tegra_camera_power_off(dev); - } - mutex_unlock(&dev->tegra_camera_lock); - return ret; - } + case TEGRA_CAMERA_IOCTL_RESET: + return 0; case TEGRA_CAMERA_IOCTL_CLK_SET_RATE: { int ret; @@ -380,8 +303,6 @@ static long tegra_camera_ioctl(struct file *file, } return 0; } - case TEGRA_CAMERA_IOCTL_RESET: - return tegra_camera_reset(dev, id); default: dev_err(dev->dev, "%s: Unknown tegra_camera ioctl.\n", __func__); @@ -396,40 +317,58 @@ static int tegra_camera_open(struct inode *inode, struct file *file) struct tegra_camera_dev *dev = container_of(miscdev, struct tegra_camera_dev, misc_dev); + int ret = 0; + dev_info(dev->dev, "%s\n", __func__); - file->private_data = dev; - tegra_camera_enable_emc(dev); + if (atomic_xchg(&dev->in_use, 1)) + return -EBUSY; - return 0; + file->private_data = dev; + + mutex_lock(&dev->tegra_camera_lock); + /* turn on CSI regulator */ + ret = tegra_camera_power_on(dev); + if (ret) + goto open_exit; + /* set EMC request */ + ret = tegra_camera_enable_emc(dev); + if (ret) + goto open_exit; + /* enable camera HW clock */ + ret = tegra_camera_enable_clk(dev); + if (ret) + goto open_exit; +open_exit: + mutex_unlock(&dev->tegra_camera_lock); + return ret; } static int tegra_camera_release(struct inode *inode, struct file *file) { - int i, err; + int ret = 0; 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(dev); - tegra_camera_block[i].is_enabled = false; - } - /* If camera blocks are not powergated yet, do it now */ - if (dev->power_refcnt > 0) { - mutex_lock(&dev->tegra_camera_lock); -#ifndef CONFIG_ARCH_TEGRA_2x_SOC - err = tegra_powergate_partition(TEGRA_POWERGATE_VENC); - if (err) - dev_err(dev->dev, "%s: powergate failed.\n", __func__); -#endif - dev->power_refcnt = 0; - mutex_unlock(&dev->tegra_camera_lock); - } - - tegra_camera_disable_emc(dev); + 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 */ + 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; } @@ -472,7 +411,6 @@ static int tegra_camera_probe(struct platform_device *pdev) /* Powergate VE when boot */ mutex_lock(&dev->tegra_camera_lock); - dev->power_refcnt = 0; #ifndef CONFIG_ARCH_TEGRA_2x_SOC err = tegra_powergate_partition(TEGRA_POWERGATE_VENC); if (err) @@ -574,12 +512,14 @@ 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; - if (tegra_camera_enabled(dev)) { + 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; } -- cgit v1.2.3