[Intel-gfx] [PATCH 3/5] drm/i915/vlv: suspend/resume fixes for VLV/BYT

Jesse Barnes jbarnes at virtuousgeek.org
Tue Oct 15 01:07:47 CEST 2013


We were missing a few bits around power well handling and Gunit
save/restore.  The code added should be sufficient for runtime D3 as
well (though that requires additional changes to how we handle
save/restore of state).

Signed-off-by: Jesse Barnes <jbarnes at virtuousgeek.org>
---
 drivers/gpu/drm/i915/i915_drv.c     |  31 +++++++++
 drivers/gpu/drm/i915/i915_drv.h     |  11 +++
 drivers/gpu/drm/i915/i915_gem.c     |   5 +-
 drivers/gpu/drm/i915/i915_reg.h     |   8 +++
 drivers/gpu/drm/i915/intel_uncore.c | 133 ++++++++++++++++++++++++++++++++----
 5 files changed, 174 insertions(+), 14 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 82a1d53..01ff272 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -459,6 +459,21 @@ bool i915_semaphore_is_enabled(struct drm_device *dev)
 	return 1;
 }
 
+/*
+ * On VLV/BYT, transitioning from D0 to a Dx state requires the following
+ * steps:
+ *   1) force enable gfx clocks with GTLC survivability reg
+ *   2) take force wake ref for register saving (really necessary?)
+ *   3) save Gunit regs
+ *   4) drop gfx freq to minimum
+ *   5) drop force wake ref (not needed if we don't need #2)
+ *   6) clear allow wake bit (prevents further forcewake requests)
+ *   7) power gate render, media, and display power wells
+ *   8) release gfx clocks
+ * Along with the usual steps of idling the GPU, dealing with display state,
+ * etc.
+ */
+
 static int i915_drm_freeze(struct drm_device *dev)
 {
 	struct drm_i915_private *dev_priv = dev->dev_private;
@@ -471,6 +486,7 @@ static int i915_drm_freeze(struct drm_device *dev)
 
 	/* We do a lot of poking in a lot of registers, make sure they work
 	 * properly. */
+	intel_uncore_prepare_suspend(dev);
 	hsw_disable_package_c8(dev_priv);
 	intel_set_power_well(dev, true);
 
@@ -515,6 +531,8 @@ static int i915_drm_freeze(struct drm_device *dev)
 	intel_fbdev_set_suspend(dev, FBINFO_STATE_SUSPENDED);
 	console_unlock();
 
+	intel_uncore_suspend(dev);
+
 	return 0;
 }
 
@@ -578,6 +596,17 @@ static void intel_resume_hotplug(struct drm_device *dev)
 	drm_helper_hpd_irq_event(dev);
 }
 
+/*
+ * On VLV/BYT, the resume steps from a Dx state are as follows:
+ *  1) force gfx clocks on
+ *  2) ungate render, media, display power wells
+ *  3) restore gunit regs
+ *  4) set allow wake bit in wake control
+ *  5) take force wake ref on render & media
+ *  6) re-enable RC6
+ *  7) drop force wake ref
+ *  8) release gfx clocks
+ */
 static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
 {
 	struct drm_i915_private *dev_priv = dev->dev_private;
@@ -587,6 +616,8 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
 
 	intel_uncore_sanitize(dev);
 
+	intel_uncore_resume(dev);
+
 	if (drm_core_check_feature(dev, DRIVER_MODESET) &&
 	    restore_gtt_mappings) {
 		mutex_lock(&dev->struct_mutex);
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 4e97840..85cd5be 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -409,6 +409,9 @@ struct intel_uncore_funcs {
 				  enum intel_display_power_domain domain);
 	void (*display_power_put)(struct drm_i915_private *dev_priv,
 				  enum intel_display_power_domain domain);
+	void (*prepare_suspend)(struct drm_i915_private *dev_priv);
+	void (*suspend)(struct drm_i915_private *dev_priv);
+	void (*resume)(struct drm_i915_private *dev_priv);
 };
 
 struct intel_uncore {
@@ -842,6 +845,11 @@ struct i915_suspend_saved_registers {
 	u32 savePIPEB_LINK_N1;
 	u32 saveMCHBAR_RENDER_STANDBY;
 	u32 savePCH_PORT_HOTPLUG;
+	u32 saveGUNIT_Control;
+	u32 saveGUNIT_Control2;
+	u32 saveGUNIT_CZClockGatingDisable1;
+	u32 saveGUNIT_CZClockGatingDisable2;
+	u32 saveDPIO_CFG_DATA;
 };
 
 struct intel_gen6_power_mgmt {
@@ -1824,6 +1832,9 @@ extern void intel_pm_init(struct drm_device *dev);
 extern void intel_hpd_init(struct drm_device *dev);
 extern void intel_pm_init(struct drm_device *dev);
 
+extern void intel_uncore_prepare_suspend(struct drm_device *dev);
+extern void intel_uncore_suspend(struct drm_device *dev);
+extern void intel_uncore_resume(struct drm_device *dev);
 extern void intel_uncore_sanitize(struct drm_device *dev);
 extern void intel_uncore_early_sanitize(struct drm_device *dev);
 extern void intel_uncore_init(struct drm_device *dev);
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index eb29a23..bd189d6 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -4470,8 +4470,9 @@ int i915_gem_init(struct drm_device *dev)
 
 	if (IS_VALLEYVIEW(dev)) {
 		/* VLVA0 (potential hack), BIOS isn't actually waking us */
-		I915_WRITE(VLV_GTLC_WAKE_CTRL, 1);
-		if (wait_for((I915_READ(VLV_GTLC_PW_STATUS) & 1) == 1, 10))
+		I915_WRITE(VLV_GTLC_WAKE_CTRL, VLV_ALLOW_WAKE_REQ);
+		if (wait_for((I915_READ(VLV_GTLC_PW_STATUS) &
+			      VLV_ALLOW_WAKE_REQ), 10))
 			DRM_DEBUG_DRIVER("allow wake ack timed out\n");
 	}
 
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index b64b1a6..cbf3e48 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -791,8 +791,11 @@
 #define IIR		0x020a4
 #define IMR		0x020a8
 #define ISR		0x020ac
+#define VLV_GUNIT_CONTROL	(VLV_DISPLAY_BASE + 0x2030)
+#define VLV_GUNIT_CONTROL2	(VLV_DISPLAY_BASE + 0x2034)
 #define VLV_GUNIT_CLOCK_GATE	(VLV_DISPLAY_BASE + 0x2060)
 #define   GCFG_DIS		(1<<8)
+#define VLV_GUNIT_CLOCK_GATE2	(VLV_DISPLAY_BASE + 0x2064)
 #define VLV_IIR_RW	(VLV_DISPLAY_BASE + 0x2084)
 #define VLV_IER		(VLV_DISPLAY_BASE + 0x20a0)
 #define VLV_IIR		(VLV_DISPLAY_BASE + 0x20a4)
@@ -4644,7 +4647,12 @@
 #define  FORCEWAKE_ACK_HSW			0x130044
 #define  FORCEWAKE_ACK				0x130090
 #define  VLV_GTLC_WAKE_CTRL			0x130090
+#define   VLV_ALLOW_WAKE_REQ			(1<<0)
 #define  VLV_GTLC_PW_STATUS			0x130094
+#define   VLV_ALLOW_WAKE_ACK			(1<<0)
+#define VLV_GTLC_SURVIVABILITY_REG		0x130098
+#define   VLV_GFX_CLK_STATUS			(1<<3)
+#define   VLV_GFX_CLK_FORCE_ON			(1<<2)
 #define  FORCEWAKE_MT				0xa188 /* multi-threaded */
 #define   FORCEWAKE_KERNEL			0x1
 #define   FORCEWAKE_USER			0x2
diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c
index ef5d7fd..b126f5a 100644
--- a/drivers/gpu/drm/i915/intel_uncore.c
+++ b/drivers/gpu/drm/i915/intel_uncore.c
@@ -419,10 +419,110 @@ static void vlv_set_media_power(struct drm_i915_private *dev_priv, bool enable)
 	__vlv_set_power_well(dev_priv, MEDIA_PWRGT, enable);
 }
 
+static void vlv_set_gfx_clock(struct drm_i915_private *dev_priv, bool enable)
+{
+	u32 reg;
+
+	if (!IS_VALLEYVIEW(dev_priv->dev))
+		return;
+
+	reg = I915_READ(VLV_GTLC_SURVIVABILITY_REG);
+	if (enable)
+		reg |= VLV_GFX_CLK_FORCE_ON;
+	else
+		reg &= ~VLV_GFX_CLK_FORCE_ON;
+	I915_WRITE(VLV_GTLC_SURVIVABILITY_REG, reg);
+	if (wait_for_atomic(((VLV_GFX_CLK_STATUS &
+			      I915_READ(VLV_GTLC_SURVIVABILITY_REG)) != 0),
+			    100)) {
+		dev_err(&dev_priv->dev->pdev->dev,
+			"GFX_CLK_ON timed out, suspend might fail\n");
+	}
+}
+
+static void vlv_gunit_save(struct drm_i915_private *dev_priv)
+{
+	dev_priv->regfile.saveGUNIT_Control = I915_READ(VLV_GUNIT_CONTROL);
+	dev_priv->regfile.saveGUNIT_Control2 = I915_READ(VLV_GUNIT_CONTROL2);
+	dev_priv->regfile.saveGUNIT_CZClockGatingDisable1 =
+		I915_READ(VLV_GUNIT_CLOCK_GATE);
+	dev_priv->regfile.saveGUNIT_CZClockGatingDisable2 =
+		I915_READ(VLV_GUNIT_CLOCK_GATE2);
+	dev_priv->regfile.saveDPIO_CFG_DATA = I915_READ(DPIO_CTL);
+}
+
+static void vlv_gunit_restore(struct drm_i915_private *dev_priv)
+{
+	I915_WRITE(VLV_GUNIT_CONTROL, dev_priv->regfile.saveGUNIT_Control);
+	I915_WRITE(VLV_GUNIT_CONTROL2, dev_priv->regfile.saveGUNIT_Control2);
+	I915_WRITE(VLV_GUNIT_CLOCK_GATE,
+		   dev_priv->regfile.saveGUNIT_CZClockGatingDisable1);
+	I915_WRITE(VLV_GUNIT_CLOCK_GATE2,
+		   dev_priv->regfile.saveGUNIT_CZClockGatingDisable2);
+	I915_WRITE(DPIO_CTL, dev_priv->regfile.saveDPIO_CFG_DATA);
+}
+
+static void vlv_uncore_prepare_suspend(struct drm_i915_private *dev_priv)
+{
+	vlv_set_gfx_clock(dev_priv, true);
+	vlv_gunit_save(dev_priv);
+}
+
+static void vlv_uncore_suspend(struct drm_i915_private *dev_priv)
+{
+	u32 reg;
+
+	mutex_lock(&dev_priv->rps.hw_lock);
+	valleyview_set_rps(dev_priv->dev, dev_priv->rps.min_delay);
+	mutex_unlock(&dev_priv->rps.hw_lock);
+
+	reg = I915_READ(VLV_GTLC_WAKE_CTRL);
+	reg &= ~VLV_ALLOW_WAKE_REQ;
+	I915_WRITE(VLV_GTLC_WAKE_CTRL, reg);
+	if (wait_for_atomic(!(I915_READ(VLV_GTLC_PW_STATUS) &
+			      VLV_ALLOW_WAKE_ACK), 100)) {
+		dev_err(&dev_priv->dev->pdev->dev,
+			"ALLOW_WAKE_SET timed out, suspend might fail\n");
+	}
+
+	intel_set_power_well(dev_priv->dev, false);
+
+	vlv_set_display_power(dev_priv, false);
+	vlv_set_render_power(dev_priv, false);
+	vlv_set_media_power(dev_priv, false);
+
+	vlv_set_gfx_clock(dev_priv, false);
+}
+
+static void vlv_uncore_resume(struct drm_i915_private *dev_priv)
+{
+	u32 reg;
+
+	vlv_gunit_restore(dev_priv);
+
+	reg = I915_READ(VLV_GTLC_WAKE_CTRL);
+	reg |= VLV_ALLOW_WAKE_REQ;
+	I915_WRITE(VLV_GTLC_WAKE_CTRL, reg);
+	if (wait_for_atomic((0 != (I915_READ(VLV_GTLC_PW_STATUS) &
+				   VLV_ALLOW_WAKE_ACK)), 100)) {
+		dev_err(&dev_priv->dev->pdev->dev,
+			"ALLOW_WAKE_SET timed out, resume might fail\n");
+	}
+
+	vlv_set_gfx_clock(dev_priv, false);
+}
+
 void intel_uncore_early_sanitize(struct drm_device *dev)
 {
 	struct drm_i915_private *dev_priv = dev->dev_private;
 
+	if (IS_VALLEYVIEW(dev)) {
+		vlv_set_gfx_clock(dev_priv, true);
+		vlv_set_display_power(dev_priv, true);
+		vlv_set_render_power(dev_priv, true);
+		vlv_set_media_power(dev_priv, true);
+	}
+
 	if (HAS_FPGA_DBG_UNCLAIMED(dev))
 		__raw_i915_write32(dev_priv, FPGA_DBG, FPGA_DBG_RM_NOCLAIM);
 }
@@ -441,6 +541,9 @@ void intel_uncore_init(struct drm_device *dev)
 		dev_priv->uncore.funcs.display_power_enabled = vlv_display_power_enabled;
 		dev_priv->uncore.funcs.display_power_get = vlv_display_power_get;
 		dev_priv->uncore.funcs.display_power_put = vlv_display_power_put;
+		dev_priv->uncore.funcs.prepare_suspend = vlv_uncore_prepare_suspend;
+		dev_priv->uncore.funcs.suspend = vlv_uncore_suspend;
+		dev_priv->uncore.funcs.resume = vlv_uncore_resume;
 	} else if (IS_HASWELL(dev)) {
 		dev_priv->uncore.funcs.force_wake_get = __gen6_gt_force_wake_mt_get;
 		dev_priv->uncore.funcs.force_wake_put = __gen6_gt_force_wake_mt_put;
@@ -512,26 +615,32 @@ static void intel_uncore_forcewake_reset(struct drm_device *dev)
 
 void intel_uncore_sanitize(struct drm_device *dev)
 {
-	struct drm_i915_private *dev_priv = dev->dev_private;
-	u32 reg_val;
-
 	intel_uncore_forcewake_reset(dev);
 
 	/* BIOS often leaves RC6 enabled, but disable it for hw init */
 	intel_disable_gt_powersave(dev);
+}
 
-	/* Turn off power gate, require especially for the BIOS less system */
-	if (IS_VALLEYVIEW(dev)) {
-
-		mutex_lock(&dev_priv->rps.hw_lock);
-		reg_val = vlv_punit_read(dev_priv, PUNIT_REG_PWRGT_STATUS);
 
-		if (reg_val & (RENDER_PWRGT | MEDIA_PWRGT | DISP2D_PWRGT))
-			vlv_punit_write(dev_priv, PUNIT_REG_PWRGT_CTRL, 0x0);
+void intel_uncore_prepare_suspend(struct drm_device *dev)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	if (dev_priv->uncore.funcs.prepare_suspend)
+		dev_priv->uncore.funcs.prepare_suspend(dev_priv);
+}
 
-		mutex_unlock(&dev_priv->rps.hw_lock);
+void intel_uncore_suspend(struct drm_device *dev)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	if (dev_priv->uncore.funcs.suspend)
+		dev_priv->uncore.funcs.suspend(dev_priv);
+}
 
-	}
+void intel_uncore_resume(struct drm_device *dev)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	if (dev_priv->uncore.funcs.resume)
+		dev_priv->uncore.funcs.resume(dev_priv);
 }
 
 /*
-- 
1.8.3.1




More information about the Intel-gfx mailing list