aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc
diff options
context:
space:
mode:
authorAlexei Shlychkov <x0177296@ti.com>2012-07-17 13:00:00 -0700
committerZiyann <jaraidaniel@gmail.com>2014-10-01 13:00:53 +0200
commit07ab3b66c987227954a9eb6d3a044804dd6e3cff (patch)
tree4009e3d18590e986f46aea9f17d5ef449a3b53f4 /drivers/misc
parent1951c714097434a8630c7e6e579dcbc6a0f23d71 (diff)
downloadkernel_samsung_tuna-07ab3b66c987227954a9eb6d3a044804dd6e3cff.zip
kernel_samsung_tuna-07ab3b66c987227954a9eb6d3a044804dd6e3cff.tar.gz
kernel_samsung_tuna-07ab3b66c987227954a9eb6d3a044804dd6e3cff.tar.bz2
gcx: fixed suspend synchronization.
It's possible for suspend to be called while the gcgpu is busy. Switching to power off in this state doesn't shutdown cleanly and the device can't sleep. Have suspend wait for gpu activity to complete. Change-Id: I280bda56a2017491aa5e812cbeed2bd3a625056a Signed-off-by: Craig Stout <craig.stout@ti.com>
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/gcx/gccore/gcmain.c19
-rw-r--r--drivers/misc/gcx/gccore/gcmain.h3
-rw-r--r--drivers/misc/gcx/gccore/gcqueue.c91
-rw-r--r--drivers/misc/gcx/gccore/gcqueue.h5
4 files changed, 76 insertions, 42 deletions
diff --git a/drivers/misc/gcx/gccore/gcmain.c b/drivers/misc/gcx/gccore/gcmain.c
index 19a61dc..03c01eb 100644
--- a/drivers/misc/gcx/gccore/gcmain.c
+++ b/drivers/misc/gcx/gccore/gcmain.c
@@ -813,7 +813,7 @@ static int gc_remove(struct platform_device *pdev)
static int gc_suspend(struct platform_device *pdev, pm_message_t s)
{
GCENTER(GCZONE_POWER);
- gcpwr_set(&g_context, GCPWR_OFF);
+ gcqueue_wait_idle(&g_context);
GCEXIT(GCZONE_POWER);
return 0;
}
@@ -843,28 +843,14 @@ static struct platform_driver plat_drv = {
#include <linux/earlysuspend.h>
static void gc_early_suspend(struct early_suspend *h)
{
- struct gccorecontext *gccorecontext = &g_context;
-
GCENTER(GCZONE_POWER);
-
- GCLOCK(&gccorecontext->mmucontextlock);
- gccorecontext->forceoff = true;
- gcpwr_set(gccorecontext, GCPWR_OFF);
- GCUNLOCK(&gccorecontext->mmucontextlock);
-
+ gcqueue_wait_idle(&g_context);
GCEXIT(GCZONE_POWER);
}
static void gc_late_resume(struct early_suspend *h)
{
- struct gccorecontext *gccorecontext = &g_context;
-
GCENTER(GCZONE_POWER);
-
- GCLOCK(&gccorecontext->mmucontextlock);
- gccorecontext->forceoff = false;
- GCUNLOCK(&gccorecontext->mmucontextlock);
-
GCEXIT(GCZONE_POWER);
}
@@ -961,7 +947,6 @@ static void gc_exit(struct gccorecontext *gccorecontext)
gcmmu_exit(gccorecontext);
/* Disable power. */
- gcpwr_set(gccorecontext, GCPWR_OFF);
pm_runtime_disable(gccorecontext->device);
if (gccorecontext->platdriver) {
diff --git a/drivers/misc/gcx/gccore/gcmain.h b/drivers/misc/gcx/gccore/gcmain.h
index 925dde6..462375a 100644
--- a/drivers/misc/gcx/gccore/gcmain.h
+++ b/drivers/misc/gcx/gccore/gcmain.h
@@ -66,7 +66,6 @@ struct gccorecontext {
/* Power mode flags. */
bool clockenabled;
bool pulseskipping;
- bool forceoff;
/* MMU and command buffer managers. */
struct gcmmu gcmmu;
@@ -80,6 +79,8 @@ struct gccorecontext {
int opp_count;
unsigned long *opp_freqs;
unsigned long cur_freq;
+
+ bool suspend_requested;
};
diff --git a/drivers/misc/gcx/gccore/gcqueue.c b/drivers/misc/gcx/gccore/gcqueue.c
index 725429d..9f0601e 100644
--- a/drivers/misc/gcx/gccore/gcqueue.c
+++ b/drivers/misc/gcx/gccore/gcqueue.c
@@ -67,7 +67,10 @@ GCDBG_FILTERDEF(queue, GCZONE_NONE,
/* GPU timeout in milliseconds. The timeout value controls when the power
* on the GPU is pulled if there is no activity in progress or scheduled. */
-#define GC_TIMEOUT 1000
+#define GC_THREAD_TIMEOUT 1000
+
+/* Time in milliseconds to wait for GPU to become idle. */
+#define GC_IDLE_TIMEOUT 100
/* The size of storage buffer. */
#define GC_STORAGE_SIZE ((GC_BUFFER_SIZE * GC_CMDBUF_FACTOR + \
@@ -488,7 +491,7 @@ static enum gcerror append_cmdbuf(
gcqueue->gcmoterminator = tailcmdbuf->gcmoterminator;
/* Start the GPU if not already running. */
- if (gcqueue->stopped) {
+ if (try_wait_for_completion(&gcqueue->stopped)) {
GCDBG(GCZONE_THREAD, "GPU is currently stopped - starting.\n");
/* Enable power to the chip. */
@@ -521,9 +524,6 @@ static enum gcerror append_cmdbuf(
GCSETFIELD(0, GCREG_CMD_BUFFER_CTRL,
PREFETCH, headcmdbuf->count));
- /* Set running state. */
- gcqueue->stopped = false;
-
/* Release the command buffer thread. */
complete(&gcqueue->ready);
}
@@ -571,13 +571,6 @@ static int gccmdthread(void *_gccorecontext)
timeout);
GCDBG(GCZONE_THREAD, "wait(ready) = %d.\n", signaled);
- /* Is termination requested? */
- if (try_wait_for_completion(&gcqueue->stop)) {
- GCDBG(GCZONE_THREAD,
- "terminating command queue thread.\n");
- break;
- }
-
/* Get triggered interrupts. */
ints2process = triggered = atomic_read(&gcqueue->triggered);
GCDBG(GCZONE_THREAD, "int = 0x%08X.\n", triggered);
@@ -779,19 +772,23 @@ static int gccmdthread(void *_gccorecontext)
continue;
}
- if (signaled) {
+ if (signaled && !gccorecontext->suspend_requested) {
/* The timeout value controls when the power
* on the GPU is pulled if there is no activity
* in progress or scheduled. */
- timeout = msecs_to_jiffies(GC_TIMEOUT);
+ timeout = msecs_to_jiffies(GC_THREAD_TIMEOUT);
} else {
GCLOCK(&gcqueue->queuelock);
- GCDBG(GCZONE_THREAD,
- "timedout while waiting for ready signal.\n");
+ if (gccorecontext->suspend_requested)
+ GCDBG(GCZONE_THREAD, "suspend requested\n");
+
+ if (!signaled)
+ GCDBG(GCZONE_THREAD,
+ "timedout while waiting for ready signal.\n");
if (gcqueue->gcmoterminator == NULL) {
- GCERR("unexpected condition.\n");
+ GCUNLOCK(&gcqueue->queuelock);
continue;
}
@@ -834,17 +831,22 @@ static int gccmdthread(void *_gccorecontext)
gcqueue->gcmoterminator = NULL;
/* Go to suspend. */
- gcpwr_set(gccorecontext,
- gccorecontext->forceoff
- ? GCPWR_OFF : GCPWR_LOW);
+ gcpwr_set(gccorecontext, GCPWR_OFF);
- /* Set running state. */
- gcqueue->stopped = true;
+ /* Set idle state. */
+ complete(&gcqueue->stopped);
/* Set timeout to infinity. */
timeout = MAX_SCHEDULE_TIMEOUT;
GCUNLOCK(&gcqueue->queuelock);
+
+ /* Is termination requested? */
+ if (try_wait_for_completion(&gcqueue->stop)) {
+ GCDBG(GCZONE_THREAD,
+ "terminating command queue thread.\n");
+ break;
+ }
}
}
@@ -921,7 +923,11 @@ enum gcerror gcqueue_start(struct gccorecontext *gccorecontext)
complete(&gcqueue->freeint);
/* Set GPU running state. */
- gcqueue->stopped = true;
+ init_completion(&gcqueue->stopped);
+ complete(&gcqueue->stopped);
+
+ /* Initialize sleep completion. */
+ init_completion(&gcqueue->sleep);
/* Initialize thread control completions. */
init_completion(&gcqueue->ready);
@@ -1580,3 +1586,42 @@ exit:
(gcerror == GCERR_NONE) ? "result" : "error", gcerror);
return gcerror;
}
+
+enum gcerror gcqueue_wait_idle(struct gccorecontext *gccorecontext)
+{
+ enum gcerror gcerror = GCERR_NONE;
+ struct gcqueue *gcqueue = &gccorecontext->gcqueue;
+ unsigned long timeout;
+ unsigned int count, limit;
+
+ GCENTER(GCZONE_THREAD);
+
+ /* indicate shutdown immediately */
+ gccorecontext->suspend_requested = true;
+ complete(&gcqueue->ready);
+
+ /* Convert timeout to jiffies. */
+ timeout = msecs_to_jiffies(GC_IDLE_TIMEOUT);
+
+ /* Compute the maximum number of attempts. */
+ limit = 5000 / GC_IDLE_TIMEOUT;
+
+ /* Wait for GPU to stop. */
+ count = 0;
+ while (!completion_done(&gcqueue->stopped)) {
+ /* Not stopped, sleep. */
+ wait_for_completion_timeout(&gcqueue->sleep, timeout);
+
+ /* Waiting too long? */
+ if (++count == limit) {
+ GCERR("wait for idle takes too long.\n");
+ gcerror = GCERR_TIMEOUT;
+ break;
+ }
+ }
+
+ gccorecontext->suspend_requested = false;
+
+ GCEXIT(GCZONE_THREAD);
+ return gcerror;
+}
diff --git a/drivers/misc/gcx/gccore/gcqueue.h b/drivers/misc/gcx/gccore/gcqueue.h
index 8b7e409..60e85fd 100644
--- a/drivers/misc/gcx/gccore/gcqueue.h
+++ b/drivers/misc/gcx/gccore/gcqueue.h
@@ -141,12 +141,13 @@ struct gcqueue {
struct gcmoterminator *gcmoterminator;
/* GPU running state. */
- bool stopped;
+ struct completion stopped;
/* Command buffer thread and thread control completions. */
struct task_struct *cmdthread;
struct completion ready;
struct completion stop;
+ struct completion sleep;
/* Stall completion; used to imitate synchronous behaviour. */
struct completion stall;
@@ -222,4 +223,6 @@ void gcqueue_free_cmdbuf(struct gcqueue *gcqueue,
enum gcerror gcqueue_alloc_int(struct gcqueue *gcqueue,
unsigned int *interrupt);
+enum gcerror gcqueue_wait_idle(struct gccorecontext *gccorecontext);
+
#endif