|
@@ -154,9 +154,18 @@ static int disable_axi(struct msm_gpu *gpu)
|
|
|
|
|
|
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)
|
|
@@ -175,9 +184,18 @@ int msm_gpu_pm_resume(struct msm_gpu *gpu)
|
|
|
|
|
|
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)
|
|
@@ -194,6 +212,55 @@ int msm_gpu_pm_suspend(struct msm_gpu *gpu)
|
|
|
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:
|
|
|
*/
|
|
@@ -206,7 +273,10 @@ static void recover_worker(struct work_struct *work)
|
|
|
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);
|
|
@@ -281,6 +351,9 @@ static void retire_worker(struct work_struct *work)
|
|
|
}
|
|
|
|
|
|
mutex_unlock(&dev->struct_mutex);
|
|
|
+
|
|
|
+ if (!msm_gpu_active(gpu))
|
|
|
+ inactive_start(gpu);
|
|
|
}
|
|
|
|
|
|
/* call from irq handler to schedule work to retire bo's */
|
|
@@ -302,6 +375,8 @@ int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
|
|
|
|
|
|
gpu->submitted_fence = submit->fence;
|
|
|
|
|
|
+ inactive_cancel(gpu);
|
|
|
+
|
|
|
ret = gpu->funcs->submit(gpu, submit, ctx);
|
|
|
priv->lastctx = ctx;
|
|
|
|
|
@@ -357,11 +432,15 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
|
|
|
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);
|
|
|
|