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

Commit 688272a0 authored by Oleg Perelet's avatar Oleg Perelet
Browse files

msm: kgsl: Use IDLE interrupt to switch to NAP state



Replace busy waiting loop to interrupt driven.
Current code starts probing for GPU idle after
RB_INT interrupt. This prevents CPU
from clock gating for 150-400uS per frame
and burns battery.

This change utilizes the RBBM_GPU_IDLE interrupt from
GPU. The GPU triggers this interrupt when it is
entering IDLE, so there's no need for busy
probing loop.

Change-Id: Ib00780ff426927a6e88013804d9b08a63e06affd
Signed-off-by: default avatarOleg Perelet <operelet@codeaurora.org>
parent 7ca51912
Loading
Loading
Loading
Loading
+17 −3
Original line number Diff line number Diff line
@@ -3129,6 +3129,20 @@ done:
	adreno_dispatcher_irq_fault(device);
}

static void a3xx_gpu_idle_callback(struct adreno_device *adreno_dev,
					int irq)
{
	struct kgsl_device *device = &adreno_dev->dev;

	if (device->requested_state == KGSL_STATE_NONE) {
		kgsl_pwrctrl_request_state(device, KGSL_STATE_NAP);
		queue_work(device->work_queue, &device->idle_check_ws);
	}

	mod_timer_pending(&device->idle_timer,
		jiffies + device->pwrctrl.interval_timeout);
}

/*
 * a3xx_cp_callback() - CP interrupt handler
 * @adreno_dev: Adreno device pointer
@@ -3141,7 +3155,6 @@ static void a3xx_cp_callback(struct adreno_device *adreno_dev, int irq)
{
	struct kgsl_device *device = &adreno_dev->dev;

	device->pwrctrl.irq_last = 1;
	queue_work(device->work_queue, &device->ts_expired_ws);
	adreno_dispatcher_schedule(device);
}
@@ -3416,7 +3429,8 @@ static uint64_t a3xx_perfcounter_read(struct adreno_device *adreno_dev,
#define A3XX_IRQ_CALLBACK(_c) { .func = _c }

#define A3XX_INT_MASK \
	((1 << A3XX_INT_RBBM_AHB_ERROR) |        \
	((1 << A3XX_INT_RBBM_GPU_IDLE) |         \
	 (1 << A3XX_INT_RBBM_AHB_ERROR) |        \
	 (1 << A3XX_INT_RBBM_ATB_BUS_OVERFLOW) | \
	 (1 << A3XX_INT_CP_T0_PACKET_IN_IB) |    \
	 (1 << A3XX_INT_CP_OPCODE_ERROR) |       \
@@ -3432,7 +3446,7 @@ static uint64_t a3xx_perfcounter_read(struct adreno_device *adreno_dev,
static struct {
	void (*func)(struct adreno_device *, int);
} a3xx_irq_funcs[] = {
	A3XX_IRQ_CALLBACK(NULL),               /* 0 - RBBM_GPU_IDLE */
	A3XX_IRQ_CALLBACK(a3xx_gpu_idle_callback), /* 0 - RBBM_GPU_IDLE */
	A3XX_IRQ_CALLBACK(a3xx_err_callback),  /* 1 - RBBM_AHB_ERROR */
	A3XX_IRQ_CALLBACK(a3xx_err_callback),  /* 2 - RBBM_REG_TIMEOUT */
	A3XX_IRQ_CALLBACK(a3xx_err_callback),  /* 3 - RBBM_ME_MS_TIMEOUT */
+0 −1
Original line number Diff line number Diff line
@@ -1181,7 +1181,6 @@ int adreno_ringbuffer_submitcmd(struct adreno_device *adreno_dev,
#endif

done:
	device->pwrctrl.irq_last = 0;
	kgsl_trace_issueibcmds(device, context->id, cmdbatch,
		cmdbatch->timestamp, cmdbatch->flags, ret,
		drawctxt->type);
+5 −38
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@
#include <mach/msm_iomap.h>
#include <mach/msm_bus.h>
#include <linux/ktime.h>
#include <linux/delay.h>

#include "kgsl.h"
#include "kgsl_pwrscale.h"
@@ -34,16 +33,6 @@
#define UPDATE_BUSY_VAL		1000000
#define UPDATE_BUSY		50

/*
 * Expected delay for post-interrupt processing on A3xx.
 * The delay may be longer, gradually increase the delay
 * to compensate.  If the GPU isn't done by max delay,
 * it's working on something other than just the final
 * command sequence so stop waiting for it to be idle.
 */
#define INIT_UDELAY		200
#define MAX_UDELAY		2000

struct clk_pair {
	const char *name;
	uint map;
@@ -1169,8 +1158,6 @@ void kgsl_pwrctrl_close(struct kgsl_device *device)
 */
void kgsl_idle_check(struct work_struct *work)
{
	int delay = INIT_UDELAY;
	int requested_state;
	struct kgsl_device *device = container_of(work, struct kgsl_device,
							idle_check_ws);
	WARN_ON(device == NULL);
@@ -1184,29 +1171,11 @@ void kgsl_idle_check(struct work_struct *work)
	if (device->state == KGSL_STATE_ACTIVE
		   || device->state ==  KGSL_STATE_NAP) {
		/*
		 * If no user is explicitly trying to use the GPU
		 * (active_cnt is zero), then loop with increasing delay,
		 * waiting for the GPU to become idle.
		 */
		while (!atomic_read(&device->active_cnt) &&
			(delay < MAX_UDELAY)) {
			requested_state = device->requested_state;
			if (!kgsl_pwrctrl_sleep(device))
				break;
			/*
			 * If no new commands have been issued since the
			 * last interrupt, stay in this loop waiting for
			 * the GPU to become idle.
		 * If no user is explicitly trying to use the GPU,
		 * try to sleep.
		*/
			if (!device->pwrctrl.irq_last)
				break;
			kgsl_pwrctrl_request_state(device, requested_state);
			mutex_unlock(&device->mutex);
			udelay(delay);
			delay *= 2;
			mutex_lock(&device->mutex);
		}

		if (!atomic_read(&device->active_cnt))
			kgsl_pwrctrl_sleep(device);

		kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
		if (device->state == KGSL_STATE_ACTIVE) {
@@ -1223,8 +1192,6 @@ void kgsl_idle_check(struct work_struct *work)
				kgsl_pwrctrl_busy_time(device, true);
				device->pwrctrl.clk_stats.no_nap_cnt = 0;
			}
		} else {
			device->pwrctrl.irq_last = 0;
		}
	}

+0 −1
Original line number Diff line number Diff line
@@ -93,7 +93,6 @@ struct kgsl_pwrctrl {
	struct pm_qos_request pm_qos_req_dma;
	unsigned int pm_qos_latency;
	unsigned int step_mul;
	unsigned int irq_last;
};

void kgsl_pwrctrl_irq(struct kgsl_device *device, int state);