]> rtime.felk.cvut.cz Git - sojka/nv-tegra/linux-3.10.git/commitdiff
media: nvavp: uninit nvavp in runtime PM
authorPrashant Gaikwad <pgaikwad@nvidia.com>
Tue, 12 Nov 2013 09:26:26 +0000 (14:56 +0530)
committerBharat Nihalani <bnihalani@nvidia.com>
Thu, 14 Nov 2013 08:49:27 +0000 (00:49 -0800)
This change fixes regression observed in
Bug 1401797 : sanity regression

Add separate reference counts for audio and video channels.
If any of those channels are not opened from userspace then
do init/uninit those in runtime PM.

Also, do not init AVP in system resume, it will be done by
runtime PM.

Bug 1254633 : Original bug

Change-Id: I06a3ad0e3709424202ea068b25fcf6f91e86c533
Reviewed-on: http://git-master/r/329633
Tested-by: Antti Miettinen <amiettinen@nvidia.com>
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
drivers/media/platform/tegra/nvavp/nvavp_dev.c

index 1ed26cc145b904f5b02c29da6b6e7dd48dde71ff..740bf57839a293b7b18fed6922edf8033d1d4ada 100644 (file)
@@ -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>
 
@@ -129,8 +130,10 @@ struct nvavp_info {
        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;
@@ -153,6 +156,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 +170,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 +329,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 +342,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 +359,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 +428,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 +640,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 +730,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 +747,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 +1009,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 +1042,7 @@ static int nvavp_init(struct nvavp_info *nvavp, int channel_id)
 #endif
 
 err_exit:
+       nvavp->init_task = NULL;
        return ret;
 }
 
@@ -1042,6 +1070,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 +1110,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,
@@ -1459,8 +1491,13 @@ static int tegra_nvavp_open(struct inode *inode, struct file *filp, int channel_
 
        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;
 
@@ -1485,7 +1522,7 @@ static int tegra_nvavp_audio_open(struct inode *inode, struct file *filp)
 }
 #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;
@@ -1513,6 +1550,11 @@ static int tegra_nvavp_release(struct inode *inode, struct file *filp)
        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);
@@ -1520,6 +1562,18 @@ out:
        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)
 {
@@ -1568,7 +1622,7 @@ static long tegra_nvavp_ioctl(struct file *filp, unsigned int cmd,
 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,
 };
 
@@ -1576,7 +1630,7 @@ static const struct file_operations tegra_video_nvavp_fops = {
 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
@@ -1790,6 +1844,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 +1923,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)
@@ -1891,36 +1945,47 @@ static int tegra_nvavp_suspend(struct device *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 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)