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

Jesse Barnes
2013-10-14T23:07:55Z

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@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