summaryrefslogtreecommitdiff
path: root/drivers/media/platform/tegra/nvavp/nvavp_dev.c
diff options
context:
space:
mode:
authorPrashant Gaikwad <pgaikwad@nvidia.com>2013-10-30 11:42:45 +0530
committerBharat Nihalani <bnihalani@nvidia.com>2013-10-30 05:33:28 -0700
commit6918965ec9dbf422ddd6468c0cabd4b5e6a82a01 (patch)
treec031021f0c7209032965f381e93143183bdb86ea /drivers/media/platform/tegra/nvavp/nvavp_dev.c
parent61a6503c93e57e4cd54e57239f5dbd08baff4a38 (diff)
media: nvavp: uninit nvavp in runtime PM
If we enter LP0 from cpuidle all the context of nvavp will be reset as VDD_CORE is turned off in LP0. Save and restore the context of nvavp in runtime suspend and resume. If nvavp is idle for 2s then it will get suspended and context is saved. If some request or remote weakeup is received for it then it is restored. Also, add current task checks to avoid deadlocks in reentrant calls. Task1 nvavp_open nvavp_init pm_runtime_get_sync runtime_resume nvavp_init (DEADLOCK) Bug 1254633 Change-Id: I7c3690624c371c2c111ad3d6f4b41b41d5e7aa97 Signed-off-by: Prashant Gaikwad <pgaikwad@nvidia.com> Reviewed-on: http://git-master/r/304210 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
Diffstat (limited to 'drivers/media/platform/tegra/nvavp/nvavp_dev.c')
-rw-r--r--drivers/media/platform/tegra/nvavp/nvavp_dev.c80
1 files changed, 68 insertions, 12 deletions
diff --git a/drivers/media/platform/tegra/nvavp/nvavp_dev.c b/drivers/media/platform/tegra/nvavp/nvavp_dev.c
index 1ed26cc145b9..885a7d114628 100644
--- a/drivers/media/platform/tegra/nvavp/nvavp_dev.c
+++ b/drivers/media/platform/tegra/nvavp/nvavp_dev.c
@@ -40,6 +40,7 @@
#include <linux/clk/tegra.h>
#include <linux/tegra-powergate.h>
#include <linux/irqchip/tegra.h>
+#include <linux/sched.h>
#include <mach/pm_domains.h>
@@ -153,6 +154,7 @@ struct nvavp_info {
#if defined(CONFIG_TEGRA_NVAVP_AUDIO)
struct miscdevice audio_misc_dev;
#endif
+ struct task_struct *init_task;
};
struct nvavp_clientctx {
@@ -166,6 +168,22 @@ struct nvavp_clientctx {
u32 clk_reqs;
};
+static int nvavp_runtime_get(struct nvavp_info *nvavp)
+{
+ if (nvavp->init_task != current)
+ pm_runtime_get_sync(&nvavp->nvhost_dev->dev);
+ else
+ pm_runtime_get_noresume(&nvavp->nvhost_dev->dev);
+
+ return 0;
+}
+
+static void nvavp_runtime_put(struct nvavp_info *nvavp)
+{
+ pm_runtime_mark_last_busy(&nvavp->nvhost_dev->dev);
+ pm_runtime_put_autosuspend(&nvavp->nvhost_dev->dev);
+}
+
#if defined(CONFIG_TEGRA_NVAVP_AUDIO)
static int nvavp_get_audio_init_status(struct nvavp_info *nvavp)
{
@@ -309,8 +327,9 @@ static int nvavp_unpowergate_vde(struct nvavp_info *nvavp)
static void nvavp_clks_enable(struct nvavp_info *nvavp)
{
- if (nvavp->clk_enabled++ == 0) {
- pm_runtime_get_sync(&nvavp->nvhost_dev->dev);
+ if (nvavp->clk_enabled == 0) {
+ nvavp_runtime_get(nvavp);
+ nvavp->clk_enabled++;
nvhost_module_busy_ext(nvavp->nvhost_dev);
clk_prepare_enable(nvavp->bsev_clk);
clk_prepare_enable(nvavp->vde_clk);
@@ -321,6 +340,8 @@ static void nvavp_clks_enable(struct nvavp_info *nvavp)
__func__, nvavp->sclk_rate);
dev_dbg(&nvavp->nvhost_dev->dev, "%s: setting emc_clk to %lu\n",
__func__, nvavp->emc_clk_rate);
+ } else {
+ nvavp->clk_enabled++;
}
}
@@ -336,7 +357,7 @@ static void nvavp_clks_disable(struct nvavp_info *nvavp)
clk_set_rate(nvavp->sclk, 0);
nvavp_powergate_vde(nvavp);
nvhost_module_idle_ext(nvavp->nvhost_dev);
- pm_runtime_put(&nvavp->nvhost_dev->dev);
+ nvavp_runtime_put(nvavp);
dev_dbg(&nvavp->nvhost_dev->dev, "%s: resetting emc_clk "
"and sclk\n", __func__);
}
@@ -405,7 +426,7 @@ static int nvavp_service(struct nvavp_info *nvavp)
if (inbox & NVE276_OS_INTERRUPT_AUDIO_IDLE) {
if (audio_enabled) {
audio_enabled = false;
- pm_runtime_put(&nvavp->nvhost_dev->dev);
+ nvavp_runtime_put(nvavp);
}
pr_debug("nvavp_service NVE276_OS_INTERRUPT_AUDIO_IDLE\n");
}
@@ -617,6 +638,7 @@ static int nvavp_pushbuffer_update(struct nvavp_info *nvavp, u32 phys_addr,
u32 index, value = -1;
int ret = 0;
+ nvavp_runtime_get(nvavp);
channel_info = nvavp_get_channel_info(nvavp, channel_id);
control = channel_info->os_control;
@@ -706,7 +728,7 @@ static int nvavp_pushbuffer_update(struct nvavp_info *nvavp, u32 phys_addr,
if (IS_AUDIO_CHANNEL_ID(channel_id)) {
pr_debug("Wake up Audio Channel\n");
if (!audio_enabled) {
- pm_runtime_get_sync(&nvavp->nvhost_dev->dev);
+ nvavp_runtime_get(nvavp);
audio_enabled = true;
}
ret = nvavp_outbox_write(0xA0000002);
@@ -723,6 +745,7 @@ static int nvavp_pushbuffer_update(struct nvavp_info *nvavp, u32 phys_addr,
err_exit:
mutex_unlock(&channel_info->pushbuffer_lock);
+ nvavp_runtime_put(nvavp);
return 0;
}
@@ -984,6 +1007,8 @@ static int nvavp_init(struct nvavp_info *nvavp, int channel_id)
{
int ret = 0;
+ nvavp->init_task = current;
+
ret = nvavp_os_init(nvavp);
if (ret) {
dev_err(&nvavp->nvhost_dev->dev,
@@ -1015,6 +1040,7 @@ static int nvavp_init(struct nvavp_info *nvavp, int channel_id)
#endif
err_exit:
+ nvavp->init_task = NULL;
return ret;
}
@@ -1042,6 +1068,8 @@ static void nvavp_uninit(struct nvavp_info *nvavp)
if (!video_initialized && !audio_initialized)
return;
+ nvavp->init_task = current;
+
if (video_initialized) {
pr_debug("nvavp_uninit nvavp->video_initialized\n");
cancel_work_sync(&nvavp->clock_disable_work);
@@ -1080,6 +1108,8 @@ static void nvavp_uninit(struct nvavp_info *nvavp)
/* write a 1 to the intr_clr field to clear the interrupt */
reg = TIMER_PCR_INTR;
writel(reg, IO_ADDRESS(TEGRA_TMR2_BASE + TIMER_PCR));
+
+ nvavp->init_task = NULL;
}
static int nvavp_set_clock_ioctl(struct file *filp, unsigned int cmd,
@@ -1790,6 +1820,8 @@ static int tegra_nvavp_probe(struct platform_device *ndev)
platform_set_drvdata(ndev, nvavp);
tegra_pd_add_device(&ndev->dev);
+ pm_runtime_use_autosuspend(&ndev->dev);
+ pm_runtime_set_autosuspend_delay(&ndev->dev, 2000);
pm_runtime_enable(&ndev->dev);
ret = device_create_file(&ndev->dev, &dev_attr_boost_sclk);
@@ -1867,14 +1899,12 @@ static int tegra_nvavp_remove(struct platform_device *ndev)
}
#ifdef CONFIG_PM
-static int tegra_nvavp_suspend(struct device *dev)
+static int tegra_nvavp_runtime_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct nvavp_info *nvavp = platform_get_drvdata(pdev);
int ret = 0;
- mutex_lock(&nvavp->open_lock);
-
if (nvavp->refcount) {
if (!nvavp->clk_enabled) {
#if defined(CONFIG_TEGRA_NVAVP_AUDIO)
@@ -1896,29 +1926,55 @@ static int tegra_nvavp_suspend(struct device *dev)
*/
nvavp_unpowergate_vde(nvavp);
- mutex_unlock(&nvavp->open_lock);
return ret;
}
-static int tegra_nvavp_resume(struct device *dev)
+static int tegra_nvavp_runtime_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct nvavp_info *nvavp = platform_get_drvdata(pdev);
- mutex_lock(&nvavp->open_lock);
-
if (nvavp->refcount) {
nvavp_init(nvavp, NVAVP_VIDEO_CHANNEL);
#if defined(CONFIG_TEGRA_NVAVP_AUDIO)
nvavp_init(nvavp, NVAVP_AUDIO_CHANNEL);
#endif
}
+
+ return 0;
+}
+
+static int tegra_nvavp_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct nvavp_info *nvavp = platform_get_drvdata(pdev);
+
+ mutex_lock(&nvavp->open_lock);
+
+ tegra_nvavp_runtime_suspend(dev);
+
+ mutex_unlock(&nvavp->open_lock);
+
+ return 0;
+}
+
+static int tegra_nvavp_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct nvavp_info *nvavp = platform_get_drvdata(pdev);
+
+ mutex_lock(&nvavp->open_lock);
+
+ tegra_nvavp_runtime_resume(dev);
+
mutex_unlock(&nvavp->open_lock);
return 0;
}
static const struct dev_pm_ops nvavp_pm_ops = {
+ .runtime_suspend = tegra_nvavp_runtime_suspend,
+ .runtime_resume = tegra_nvavp_runtime_resume,
.suspend = tegra_nvavp_suspend,
.resume = tegra_nvavp_resume,
};