Re: [PATCH 4/7] drm/msm: crank down gpu when inactive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 03/10/2014 10:47 AM, Rob Clark wrote:
Shut down the clks when the gpu has nothing to do.  A short inactivity
timer is used to provide a low pass filter for power transitions.

Good luck.  Power management will take years off your life.

Signed-off-by: Rob Clark <robdclark@xxxxxxxxx>

Acked-by: Jordan Crouse <jcrouse@xxxxxxxxxxxxxx>
---
  drivers/gpu/drm/msm/adreno/a3xx_gpu.c | 10 +++++
  drivers/gpu/drm/msm/msm_drv.c         |  7 ++-
  drivers/gpu/drm/msm/msm_gpu.c         | 85 +++++++++++++++++++++++++++++++++--
  drivers/gpu/drm/msm/msm_gpu.h         | 16 ++++++-
  4 files changed, 113 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c
index 59ed762..e6cb2bc 100644
--- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c
@@ -395,9 +395,15 @@ static const unsigned int a3xx_registers[] = {
  #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));

@@ -413,6 +419,10 @@ static void a3xx_show(struct msm_gpu *gpu, struct seq_file *m)
  			seq_printf(m, "IO:R %08x %08x\n", addr<<2, val);
  		}
  	}
+
+	gpu->funcs->pm_suspend(gpu);
+
+	mutex_unlock(&dev->struct_mutex);
  }
  #endif

diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index e6adafc..e913efa 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -311,7 +311,6 @@ static void load_gpu(struct drm_device *dev)
  		gpu = NULL;
  		/* not fatal */
  	}
-	mutex_unlock(&dev->struct_mutex);

  	if (gpu) {
  		int ret;
@@ -321,10 +320,16 @@ static void load_gpu(struct drm_device *dev)
  			dev_err(dev->dev, "gpu hw init failed: %d\n", ret);
  			gpu->funcs->destroy(gpu);
  			gpu = NULL;
+		} else {
+			/* give inactive pm a chance to kick in: */
+			msm_gpu_retire(gpu);
  		}
+
  	}

  	priv->gpu = gpu;
+
+	mutex_unlock(&dev->struct_mutex);
  }

  static int msm_open(struct drm_device *dev, struct drm_file *file)
diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c
index 0cfe3f4..3e667ca 100644
--- a/drivers/gpu/drm/msm/msm_gpu.c
+++ b/drivers/gpu/drm/msm/msm_gpu.c
@@ -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)
@@ -195,6 +213,55 @@ int msm_gpu_pm_suspend(struct msm_gpu *gpu)
  }

  /*
+ * 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);

diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h
index 458db8c..fad2700 100644
--- a/drivers/gpu/drm/msm/msm_gpu.h
+++ b/drivers/gpu/drm/msm/msm_gpu.h
@@ -72,6 +72,10 @@ struct msm_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;

@@ -91,7 +95,12 @@ struct msm_gpu {
  	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;
@@ -99,6 +108,11 @@ struct msm_gpu {
  	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));



--
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
--
To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [Linux for Sparc]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux