#ifdef CONFIG_DEBUG_FS
 static void a3xx_show(struct msm_gpu *gpu, struct seq_file *m)
 {
+       struct drm_device *dev = gpu->dev;
        int i;
 
        adreno_show(gpu, m);
+
+       mutex_lock(&dev->struct_mutex);
+
+       gpu->funcs->pm_resume(gpu);
+
        seq_printf(m, "status:   %08x\n",
                        gpu_read(gpu, REG_A3XX_RBBM_STATUS));
 
                        seq_printf(m, "IO:R %08x %08x\n", addr<<2, val);
                }
        }
+
+       gpu->funcs->pm_suspend(gpu);
+
+       mutex_unlock(&dev->struct_mutex);
 }
 #endif
 
 
 
 int msm_gpu_pm_resume(struct msm_gpu *gpu)
 {
+       struct drm_device *dev = gpu->dev;
        int ret;
 
-       DBG("%s", gpu->name);
+       DBG("%s: active_cnt=%d", gpu->name, gpu->active_cnt);
+
+       WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+
+       if (gpu->active_cnt++ > 0)
+               return 0;
+
+       if (WARN_ON(gpu->active_cnt <= 0))
+               return -EINVAL;
 
        ret = enable_pwrrail(gpu);
        if (ret)
 
 int msm_gpu_pm_suspend(struct msm_gpu *gpu)
 {
+       struct drm_device *dev = gpu->dev;
        int ret;
 
-       DBG("%s", gpu->name);
+       DBG("%s: active_cnt=%d", gpu->name, gpu->active_cnt);
+
+       WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+
+       if (--gpu->active_cnt > 0)
+               return 0;
+
+       if (WARN_ON(gpu->active_cnt < 0))
+               return -EINVAL;
 
        ret = disable_axi(gpu);
        if (ret)
        return 0;
 }
 
+/*
+ * Inactivity detection (for suspend):
+ */
+
+static void inactive_worker(struct work_struct *work)
+{
+       struct msm_gpu *gpu = container_of(work, struct msm_gpu, inactive_work);
+       struct drm_device *dev = gpu->dev;
+
+       if (gpu->inactive)
+               return;
+
+       DBG("%s: inactive!\n", gpu->name);
+       mutex_lock(&dev->struct_mutex);
+       if (!(msm_gpu_active(gpu) || gpu->inactive)) {
+               disable_axi(gpu);
+               disable_clk(gpu);
+               gpu->inactive = true;
+       }
+       mutex_unlock(&dev->struct_mutex);
+}
+
+static void inactive_handler(unsigned long data)
+{
+       struct msm_gpu *gpu = (struct msm_gpu *)data;
+       struct msm_drm_private *priv = gpu->dev->dev_private;
+
+       queue_work(priv->wq, &gpu->inactive_work);
+}
+
+/* cancel inactive timer and make sure we are awake: */
+static void inactive_cancel(struct msm_gpu *gpu)
+{
+       DBG("%s", gpu->name);
+       del_timer(&gpu->inactive_timer);
+       if (gpu->inactive) {
+               enable_clk(gpu);
+               enable_axi(gpu);
+               gpu->inactive = false;
+       }
+}
+
+static void inactive_start(struct msm_gpu *gpu)
+{
+       DBG("%s", gpu->name);
+       mod_timer(&gpu->inactive_timer,
+                       round_jiffies_up(jiffies + DRM_MSM_INACTIVE_JIFFIES));
+}
+
 /*
  * Hangcheck detection for locked gpu:
  */
        dev_err(dev->dev, "%s: hangcheck recover!\n", gpu->name);
 
        mutex_lock(&dev->struct_mutex);
-       gpu->funcs->recover(gpu);
+       if (msm_gpu_active(gpu)) {
+               inactive_cancel(gpu);
+               gpu->funcs->recover(gpu);
+       }
        mutex_unlock(&dev->struct_mutex);
 
        msm_gpu_retire(gpu);
        }
 
        mutex_unlock(&dev->struct_mutex);
+
+       if (!msm_gpu_active(gpu))
+               inactive_start(gpu);
 }
 
 /* call from irq handler to schedule work to retire bo's */
 
        gpu->submitted_fence = submit->fence;
 
+       inactive_cancel(gpu);
+
        ret = gpu->funcs->submit(gpu, submit, ctx);
        priv->lastctx = ctx;
 
        gpu->dev = drm;
        gpu->funcs = funcs;
        gpu->name = name;
+       gpu->inactive = true;
 
        INIT_LIST_HEAD(&gpu->active_list);
        INIT_WORK(&gpu->retire_work, retire_worker);
+       INIT_WORK(&gpu->inactive_work, inactive_worker);
        INIT_WORK(&gpu->recover_work, recover_worker);
 
+       setup_timer(&gpu->inactive_timer, inactive_handler,
+                       (unsigned long)gpu);
        setup_timer(&gpu->hangcheck_timer, hangcheck_handler,
                        (unsigned long)gpu);
 
 
 
        uint32_t submitted_fence;
 
+       /* is gpu powered/active? */
+       int active_cnt;
+       bool inactive;
+
        /* worker for handling active-list retiring: */
        struct work_struct retire_work;
 
        uint32_t bsc;
 #endif
 
-       /* Hang Detction: */
+       /* Hang and Inactivity Detection:
+        */
+#define DRM_MSM_INACTIVE_PERIOD   66 /* in ms (roughly four frames) */
+#define DRM_MSM_INACTIVE_JIFFIES  msecs_to_jiffies(DRM_MSM_INACTIVE_PERIOD)
+       struct timer_list inactive_timer;
+       struct work_struct inactive_work;
 #define DRM_MSM_HANGCHECK_PERIOD 500 /* in ms */
 #define DRM_MSM_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_MSM_HANGCHECK_PERIOD)
        struct timer_list hangcheck_timer;
        struct work_struct recover_work;
 };
 
+static inline bool msm_gpu_active(struct msm_gpu *gpu)
+{
+       return gpu->submitted_fence > gpu->funcs->last_fence(gpu);
+}
+
 static inline void gpu_write(struct msm_gpu *gpu, u32 reg, u32 data)
 {
        msm_writel(data, gpu->mmio + (reg << 2));