summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/msm/adreno/adreno_gpu.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/msm/adreno/adreno_gpu.c')
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_gpu.c59
1 files changed, 56 insertions, 3 deletions
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
index c0bf75b47ef6..5e10446f7798 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
@@ -60,6 +60,50 @@ const char *adreno_get_name(struct adreno_gpu *gpu)
return gpu->info->name;
}
+/*
+ * Hangcheck detection for locked gpu:
+ */
+
+static void recover_worker(struct work_struct *work)
+{
+ struct adreno_gpu *gpu = container_of(work, struct adreno_gpu, recover_work);
+ struct drm_device *dev = gpu->drm;
+
+ dev_err(dev->dev, "%s: hangcheck recover!\n", adreno_get_name(gpu));
+
+ mutex_lock(&dev->struct_mutex);
+ gpu->funcs->recover(gpu);
+ mutex_unlock(&dev->struct_mutex);
+
+ gpu->gem->gem_retire(gpu->gem->priv);
+}
+
+static void hangcheck_timer_reset(struct adreno_gpu *gpu)
+{
+ DBG("%s", adreno_get_name(gpu));
+ mod_timer(&gpu->hangcheck_timer,
+ round_jiffies_up(jiffies + DRM_MSM_HANGCHECK_JIFFIES));
+}
+
+static void hangcheck_handler(unsigned long data)
+{
+ struct adreno_gpu *gpu = (struct adreno_gpu *)data;
+ uint32_t fence = gpu->funcs->last_fence(gpu);
+
+ if (fence != gpu->hangcheck_fence) {
+ /* some progress has been made.. ya! */
+ gpu->hangcheck_fence = fence;
+ } else if (fence < gpu->submitted_fence) {
+ /* no progress and not done.. hung! */
+ gpu->hangcheck_fence = fence;
+ queue_work(gpu->shared_wq, &gpu->recover_work);
+ }
+
+ /* if still more pending work, reset the hangcheck timer: */
+ if (gpu->submitted_fence > gpu->hangcheck_fence)
+ hangcheck_timer_reset(gpu);
+}
+
int adreno_get_param(struct adreno_gpu *gpu, uint32_t param, uint64_t *value)
{
switch (param) {
@@ -120,7 +164,7 @@ void adreno_recover(struct adreno_gpu *gpu)
gpu->rb->cur = gpu->rb->start;
/* reset completed fence seqno, just discard anything pending: */
- gpu->memptrs->fence = gpu->base.submitted_fence;
+ gpu->memptrs->fence = gpu->submitted_fence;
gpu->funcs->pm_resume(gpu);
ret = gpu->funcs->hw_init(gpu);
@@ -196,6 +240,9 @@ int adreno_submit(struct adreno_gpu *gpu, struct adreno_submit *submit,
gpu->funcs->flush(gpu);
+ gpu->submitted_fence = submit->fence;
+ hangcheck_timer_reset(gpu);
+
return 0;
}
@@ -238,7 +285,7 @@ void adreno_show(struct adreno_gpu *gpu, struct seq_file *m)
gpu->rev.patchid);
seq_printf(m, "fence: %d/%d\n", gpu->memptrs->fence,
- gpu->base.submitted_fence);
+ gpu->submitted_fence);
seq_printf(m, "rptr: %d\n", gpu->memptrs->rptr);
seq_printf(m, "wptr: %d\n", gpu->memptrs->wptr);
seq_printf(m, "rb wptr: %d\n", get_wptr(gpu->rb));
@@ -268,7 +315,8 @@ static inline bool _rev_match(uint8_t entry, uint8_t id)
int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,
struct adreno_gpu *gpu, const struct adreno_gpu_funcs *funcs,
- struct adreno_gem *gem, struct adreno_rev rev)
+ struct adreno_gem *gem, struct workqueue_struct *wq,
+ struct adreno_rev rev)
{
int i, ret;
@@ -297,6 +345,7 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,
gpu->drm = drm;
gpu->funcs = funcs;
gpu->gem = gem;
+ gpu->shared_wq = wq;
gpu->rev = rev;
/* XXX: urgh - pointers to self are fun! */
@@ -368,6 +417,10 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,
return ret;
}
+ INIT_WORK(&gpu->recover_work, recover_worker);
+ setup_timer(&gpu->hangcheck_timer, hangcheck_handler,
+ (unsigned long)gpu);
+
return 0;
}