summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@arm.linux.org.uk>2015-07-31 14:24:55 +0100
committerRussell King <rmk+kernel@armlinux.org.uk>2016-08-08 11:03:33 +0100
commita09a4e88711b1a8bb8cb1bb825cb85a1941795c2 (patch)
tree6e0e7d46a41bbba9f0baeeec9bd9468c3ddc9429
parenta0fb5e7bff0aea17038553daec796881e3872fd7 (diff)
drm: etnaviv: initial suspend/resume support
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_gpu.c65
1 files changed, 55 insertions, 10 deletions
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c
index 0870ab01332a..8948ab07ff36 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c
@@ -1530,7 +1530,6 @@ static int etnaviv_gpu_hw_suspend(struct etnaviv_gpu *gpu)
return etnaviv_gpu_clk_disable(gpu);
}
-#ifdef CONFIG_PM
static int etnaviv_gpu_hw_resume(struct etnaviv_gpu *gpu)
{
int ret;
@@ -1549,7 +1548,6 @@ static int etnaviv_gpu_hw_resume(struct etnaviv_gpu *gpu)
return 0;
}
-#endif
static int
etnaviv_gpu_cooling_get_max_state(struct thermal_cooling_device *cdev,
@@ -1786,26 +1784,32 @@ static int etnaviv_gpu_platform_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM
-static int etnaviv_gpu_rpm_suspend(struct device *dev)
+static bool etnaviv_gpu_is_idle(struct etnaviv_gpu *gpu, u32 mask)
+{
+ u32 idle;
+
+ mask = mask & gpu->idle_mask;
+ idle = gpu_read(gpu, VIVS_HI_IDLE_STATE) & mask;
+
+ return idle == mask;
+}
+
+static int __maybe_unused etnaviv_gpu_rpm_suspend(struct device *dev)
{
struct etnaviv_gpu *gpu = dev_get_drvdata(dev);
- u32 idle, mask;
/* If we have outstanding fences, we're not idle */
if (gpu->completed_fence != gpu->active_fence)
return -EBUSY;
/* Check whether the hardware (except FE) is idle */
- mask = gpu->idle_mask & ~VIVS_HI_IDLE_STATE_FE;
- idle = gpu_read(gpu, VIVS_HI_IDLE_STATE) & mask;
- if (idle != mask)
+ if (!etnaviv_gpu_is_idle(gpu, ~VIVS_HI_IDLE_STATE_FE))
return -EBUSY;
return etnaviv_gpu_hw_suspend(gpu);
}
-static int etnaviv_gpu_rpm_resume(struct device *dev)
+static int __maybe_unused etnaviv_gpu_rpm_resume(struct device *dev)
{
struct etnaviv_gpu *gpu = dev_get_drvdata(dev);
int ret;
@@ -1825,11 +1829,52 @@ static int etnaviv_gpu_rpm_resume(struct device *dev)
return 0;
}
-#endif
+
+static int __maybe_unused etnaviv_gpu_suspend(struct device *dev)
+{
+ struct etnaviv_gpu *gpu = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (!pm_runtime_suspended(dev) && gpu->drm) {
+ /*
+ * Wait for the GPU to finish processing any commands
+ * userspace has submitted before trying to suspend it; we
+ * can't suspend the GPU with an incomplete command stream.
+ * Note that this does not wait for the fences to be retired.
+ */
+ ret = wait_event_timeout(gpu->fence_event,
+ gpu->completed_fence == gpu->active_fence,
+ msecs_to_jiffies(5000));
+ if (ret == 0) {
+ dev_crit(dev,
+ "timeout while waiting for GPU to idle - rendering operations will be lost, suspending anyway\n");
+ } else {
+ /* Check whether the hardware (except FE) is idle */
+ if (!etnaviv_gpu_is_idle(gpu, ~VIVS_HI_IDLE_STATE_FE))
+ dev_crit(dev,
+ "GPU is not idle - rendering operations will be lost, suspending anyway");
+ }
+
+ ret = etnaviv_gpu_hw_suspend(gpu);
+ }
+ return ret;
+}
+
+static int __maybe_unused etnaviv_gpu_resume(struct device *dev)
+{
+ struct etnaviv_gpu *gpu = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (!pm_runtime_suspended(dev) && gpu->drm)
+ ret = etnaviv_gpu_rpm_resume(dev);
+
+ return ret;
+}
static const struct dev_pm_ops etnaviv_gpu_pm_ops = {
SET_RUNTIME_PM_OPS(etnaviv_gpu_rpm_suspend, etnaviv_gpu_rpm_resume,
NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(etnaviv_gpu_suspend, etnaviv_gpu_resume)
};
struct platform_driver etnaviv_gpu_driver = {