summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/media/video/tegra/tegra_camera.c298
-rw-r--r--include/media/tegra_camera.h1
2 files changed, 120 insertions, 179 deletions
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;
}
diff --git a/include/media/tegra_camera.h b/include/media/tegra_camera.h
index d7d08bd9a99b..8ee290758262 100644
--- a/include/media/tegra_camera.h
+++ b/include/media/tegra_camera.h
@@ -23,6 +23,7 @@ enum {
TEGRA_CAMERA_MODULE_ISP = 0,
TEGRA_CAMERA_MODULE_VI,
TEGRA_CAMERA_MODULE_CSI,
+ TEGRA_CAMERA_MODULE_MAX,
};
enum {