video: tegra: host: Idle before suspend
Arto Merilainen [Wed, 19 Dec 2012 10:24:27 +0000 (12:24 +0200)]
In certain cases the device may be deinitialized before calling
idle() for the last time which causes invalid memory accesses. This
patch reorders device suspend and device deinitialization sequences
to be performed after calling the idle function.

Bug 1195805

Change-Id: Iad553575fbea26fa325ca93d98c7278aacd7ae90
Signed-off-by: Arto Merilainen <amerilainen@nvidia.com>
Reviewed-on: http://git-master/r/173590
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Terje Bergstrom <tbergstrom@nvidia.com>
GVS: Gerrit_Virtual_Submit

drivers/video/tegra/host/nvhost_acm.c
drivers/video/tegra/host/nvhost_intr.c

index 0ea1b40..f67f9f0 100644 (file)
@@ -283,20 +283,39 @@ void nvhost_module_idle_mult(struct platform_device *dev, int refs)
        bool kick = false;
 
        mutex_lock(&pdata->lock);
+
        pdata->refcount -= refs;
-       if (pdata->refcount == 0) {
-               if (nvhost_module_powered(dev))
-                       schedule_clockgating_locked(dev);
-               kick = true;
+
+       /* submits on the fly -> exit */
+       if (pdata->refcount)
+               goto out;
+
+       if (pdata->idle) {
+               /* give a reference for idle(). otherwise the dev can be
+                * clockgated */
+               pdata->refcount++;
+               mutex_unlock(&pdata->lock);
+               pdata->idle(dev);
+               mutex_lock(&pdata->lock);
+               pdata->refcount--;
        }
+
+       /* check that we don't have any new submits on the channel */
+       if (pdata->refcount)
+               goto out;
+
+       /* no new submits. just schedule clock gating */
+       kick = true;
+       if (nvhost_module_powered(dev))
+               schedule_clockgating_locked(dev);
+
+out:
        mutex_unlock(&pdata->lock);
 
-       if (kick) {
+       /* wake up a waiting thread if we actually went to idle state */
+       if (kick)
                wake_up(&pdata->idle_wq);
 
-               if (pdata->idle)
-                       pdata->idle(dev);
-       }
 }
 
 int nvhost_module_get_rate(struct platform_device *dev, unsigned long *rate,
index dfe09c7..0fc7cdf 100644 (file)
@@ -129,8 +129,8 @@ static void action_submit_complete(struct nvhost_waitlist *waiter)
        struct nvhost_channel *channel = waiter->data;
        int nr_completed = waiter->count;
 
-       nvhost_cdma_update(&channel->cdma);
        nvhost_module_idle_mult(channel->dev, nr_completed);
+       nvhost_cdma_update(&channel->cdma);
 
        /*  Add nr_completed to trace */
        trace_nvhost_channel_submit_complete(channel->dev->name,