#include <linux/clk/tegra.h>
#include <linux/tegra-powergate.h>
#include <linux/irqchip/tegra.h>
+#include <linux/sched.h>
#include <mach/pm_domains.h>
struct mutex open_lock;
int refcount;
int video_initialized;
+ int video_refcnt;
#if defined(CONFIG_TEGRA_NVAVP_AUDIO)
int audio_initialized;
+ int audio_refcnt;
struct work_struct app_notify_work;
#endif
struct work_struct clock_disable_work;
#if defined(CONFIG_TEGRA_NVAVP_AUDIO)
struct miscdevice audio_misc_dev;
#endif
+ struct task_struct *init_task;
};
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)
{
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);
__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++;
}
}
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__);
}
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");
}
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;
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);
err_exit:
mutex_unlock(&channel_info->pushbuffer_lock);
+ nvavp_runtime_put(nvavp);
return 0;
}
{
int ret = 0;
+ nvavp->init_task = current;
+
ret = nvavp_os_init(nvavp);
if (ret) {
dev_err(&nvavp->nvhost_dev->dev,
#endif
err_exit:
+ nvavp->init_task = NULL;
return ret;
}
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);
/* 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,
ret = nvavp_init(nvavp, channel_id);
- if (!ret)
+ if (!ret) {
nvavp->refcount++;
+ if (IS_VIDEO_CHANNEL_ID(channel_id))
+ nvavp->video_refcnt++;
+ if (IS_AUDIO_CHANNEL_ID(channel_id))
+ nvavp->audio_refcnt++;
+ }
clientctx->nvavp = nvavp;
}
#endif
-static int tegra_nvavp_release(struct inode *inode, struct file *filp)
+static int tegra_nvavp_release(struct inode *inode, struct file *filp, int channel_id)
{
struct nvavp_clientctx *clientctx = filp->private_data;
struct nvavp_info *nvavp = clientctx->nvavp;
if (!nvavp->refcount)
nvavp_uninit(nvavp);
+ if (IS_VIDEO_CHANNEL_ID(channel_id))
+ nvavp->video_refcnt--;
+ if (IS_AUDIO_CHANNEL_ID(channel_id))
+ nvavp->audio_refcnt--;
+
out:
nvmap_client_put(clientctx->nvmap);
mutex_unlock(&nvavp->open_lock);
return ret;
}
+static int tegra_nvavp_video_release(struct inode *inode, struct file *filp)
+{
+ return tegra_nvavp_release(inode, filp, NVAVP_VIDEO_CHANNEL);
+}
+
+#if defined(CONFIG_TEGRA_NVAVP_AUDIO)
+static int tegra_nvavp_audio_release(struct inode *inode, struct file *filp)
+{
+ return tegra_nvavp_release(inode, filp, NVAVP_AUDIO_CHANNEL);
+}
+#endif
+
static long tegra_nvavp_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
static const struct file_operations tegra_video_nvavp_fops = {
.owner = THIS_MODULE,
.open = tegra_nvavp_video_open,
- .release = tegra_nvavp_release,
+ .release = tegra_nvavp_video_release,
.unlocked_ioctl = tegra_nvavp_ioctl,
};
static const struct file_operations tegra_audio_nvavp_fops = {
.owner = THIS_MODULE,
.open = tegra_nvavp_audio_open,
- .release = tegra_nvavp_release,
+ .release = tegra_nvavp_audio_release,
.unlocked_ioctl = tegra_nvavp_ioctl,
};
#endif
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);
}
#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)
}
}
- /* Partition vde has to be left on before suspend for the
- * device to wakeup on resume
- */
- 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) {
+ if (nvavp->video_refcnt)
nvavp_init(nvavp, NVAVP_VIDEO_CHANNEL);
#if defined(CONFIG_TEGRA_NVAVP_AUDIO)
+ if (nvavp->audio_refcnt)
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);
+
+ /* Partition vde has to be left on before suspend for the
+ * device to wakeup on resume
+ */
+ nvavp_unpowergate_vde(nvavp);
+
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,
};
#define NVAVP_PM_OPS (&nvavp_pm_ops)