Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 37d77c3a authored by Rob Clark's avatar Rob Clark
Browse files

drm/msm: crank down gpu when inactive



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.

Signed-off-by: default avatarRob Clark <robdclark@gmail.com>
parent 0963756f
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -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

+6 −1
Original line number Diff line number Diff line
@@ -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)
+82 −3
Original line number Diff line number Diff line
@@ -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);
	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);

+15 −1
Original line number Diff line number Diff line
@@ -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));