37cc0db
From 5fc9d375bac02e054fcaaf14a06bebc6c7118008 Mon Sep 17 00:00:00 2001
37cc0db
From: "cpaul@redhat.com" <cpaul@redhat.com>
37cc0db
Date: Tue, 12 Jul 2016 13:36:03 -0400
37cc0db
Subject: [PATCH] drm/i915/skl: Add support for the SAGV, fix underrun hangs
37cc0db
MIME-Version: 1.0
37cc0db
Content-Type: text/plain; charset=UTF-8
37cc0db
Content-Transfer-Encoding: 8bit
37cc0db
37cc0db
Since the watermark calculations for Skylake are still broken, we're apt
37cc0db
to hitting underruns very easily under multi-monitor configurations.
37cc0db
While it would be lovely if this was fixed, it's not. Another problem
37cc0db
that's been coming from this however, is the mysterious issue of
37cc0db
underruns causing full system hangs. An easy way to reproduce this with
37cc0db
a skylake system:
37cc0db
37cc0db
- Get a laptop with a skylake GPU, and hook up two external monitors to
37cc0db
  it
37cc0db
- Move the cursor from the built-in LCD to one of the external displays
37cc0db
  as quickly as you can
37cc0db
- You'll get a few pipe underruns, and eventually the entire system will
37cc0db
  just freeze.
37cc0db
37cc0db
After doing a lot of investigation and reading through the bspec, I
37cc0db
found the existence of the SAGV, which is responsible for adjusting the
37cc0db
system agent voltage and clock frequencies depending on how much power
37cc0db
we need. According to the bspec:
37cc0db
37cc0db
"The display engine access to system memory is blocked during the
37cc0db
 adjustment time. SAGV defaults to enabled. Software must use the
37cc0db
 GT-driver pcode mailbox to disable SAGV when the display engine is not
37cc0db
 able to tolerate the blocking time."
37cc0db
37cc0db
The rest of the bspec goes on to explain that software can simply leave
37cc0db
the SAGV enabled, and disable it when we use interlaced pipes/have more
37cc0db
then one pipe active.
37cc0db
37cc0db
Sure enough, with this patchset the system hangs resulting from pipe
37cc0db
underruns on Skylake have completely vanished on my T460s. Additionally,
37cc0db
the bspec mentions turning off the SAGV	with more then one pipe enabled
37cc0db
as a workaround for display underruns. While this patch doesn't entirely
37cc0db
fix that, it looks like it does improve the situation a little bit so
37cc0db
it's likely this is going to be required to make watermarks on Skylake
37cc0db
fully functional.
37cc0db
37cc0db
Changes since v2:
37cc0db
 - Really apply minor style nitpicks to patch this time
37cc0db
Changes since v1:
37cc0db
 - Added comments about this probably being one of the requirements to
37cc0db
   fixing Skylake's watermark issues
37cc0db
 - Minor style nitpicks from Matt Roper
37cc0db
 - Disable these functions on Broxton, since it doesn't have an SAGV
37cc0db
37cc0db
Cc: Matt Roper <matthew.d.roper@intel.com>
37cc0db
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
37cc0db
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
37cc0db
Signed-off-by: Lyude <cpaul@redhat.com>
37cc0db
Reviewed-by: Matt Roper <matthew.d.roper@intel.com>
37cc0db
---
37cc0db
 drivers/gpu/drm/i915/i915_drv.h |   2 +
37cc0db
 drivers/gpu/drm/i915/i915_reg.h |   5 ++
37cc0db
 drivers/gpu/drm/i915/intel_pm.c | 110 ++++++++++++++++++++++++++++++++++++++++
37cc0db
 3 files changed, 117 insertions(+)
37cc0db
37cc0db
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
37cc0db
index 608f8e44f353..274b57ac1a91 100644
37cc0db
--- a/drivers/gpu/drm/i915/i915_drv.h
37cc0db
+++ b/drivers/gpu/drm/i915/i915_drv.h
37cc0db
@@ -1942,6 +1942,8 @@ struct drm_i915_private {
37cc0db
 	struct i915_suspend_saved_registers regfile;
37cc0db
 	struct vlv_s0ix_state vlv_s0ix_state;
37cc0db
 
37cc0db
+	bool skl_sagv_enabled;
37cc0db
+
37cc0db
 	struct {
37cc0db
 		/*
37cc0db
 		 * Raw watermark latency values:
37cc0db
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
37cc0db
index b407411e31ba..9472949e8442 100644
37cc0db
--- a/drivers/gpu/drm/i915/i915_reg.h
37cc0db
+++ b/drivers/gpu/drm/i915/i915_reg.h
37cc0db
@@ -7094,6 +7094,11 @@ enum skl_disp_power_wells {
37cc0db
 #define   HSW_PCODE_DE_WRITE_FREQ_REQ		0x17
37cc0db
 #define   DISPLAY_IPS_CONTROL			0x19
37cc0db
 #define	  HSW_PCODE_DYNAMIC_DUTY_CYCLE_CONTROL	0x1A
37cc0db
+#define   GEN9_PCODE_SAGV_CONTROL		0x21
37cc0db
+#define     GEN9_SAGV_DISABLE			0x0
37cc0db
+#define     GEN9_SAGV_LOW_FREQ			0x1
37cc0db
+#define     GEN9_SAGV_HIGH_FREQ			0x2
37cc0db
+#define     GEN9_SAGV_DYNAMIC_FREQ              0x3
37cc0db
 #define GEN6_PCODE_DATA				_MMIO(0x138128)
37cc0db
 #define   GEN6_PCODE_FREQ_IA_RATIO_SHIFT	8
37cc0db
 #define   GEN6_PCODE_FREQ_RING_RATIO_SHIFT	16
37cc0db
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
37cc0db
index f764d284e6a0..439a38b08760 100644
37cc0db
--- a/drivers/gpu/drm/i915/intel_pm.c
37cc0db
+++ b/drivers/gpu/drm/i915/intel_pm.c
37cc0db
@@ -2847,6 +2847,109 @@ skl_wm_plane_id(const struct intel_plane *plane)
37cc0db
 }
37cc0db
 
37cc0db
 static void
37cc0db
+skl_sagv_get_hw_state(struct drm_i915_private *dev_priv)
37cc0db
+{
37cc0db
+	u32 temp;
37cc0db
+	int ret;
37cc0db
+
37cc0db
+	if (IS_BROXTON(dev_priv))
37cc0db
+		return;
37cc0db
+
37cc0db
+	mutex_lock(&dev_priv->rps.hw_lock);
37cc0db
+	ret = sandybridge_pcode_read(dev_priv, GEN9_PCODE_SAGV_CONTROL, &temp);
37cc0db
+	mutex_unlock(&dev_priv->rps.hw_lock);
37cc0db
+
37cc0db
+	if (!ret) {
37cc0db
+		dev_priv->skl_sagv_enabled = !!(temp & GEN9_SAGV_DYNAMIC_FREQ);
37cc0db
+	} else {
37cc0db
+		/*
37cc0db
+		 * If for some reason we can't access the SAGV state, follow
37cc0db
+		 * the bspec and assume it's enabled
37cc0db
+		 */
37cc0db
+		DRM_ERROR("Failed to get SAGV state, assuming enabled\n");
37cc0db
+		dev_priv->skl_sagv_enabled = true;
37cc0db
+	}
37cc0db
+}
37cc0db
+
37cc0db
+/*
37cc0db
+ * SAGV dynamically adjusts the system agent voltage and clock frequencies
37cc0db
+ * depending on power and performance requirements. The display engine access
37cc0db
+ * to system memory is blocked during the adjustment time. Having this enabled
37cc0db
+ * in multi-pipe configurations can cause issues (such as underruns causing
37cc0db
+ * full system hangs), and the bspec also suggests that software disable it
37cc0db
+ * when more then one pipe is enabled.
37cc0db
+ */
37cc0db
+static int
37cc0db
+skl_enable_sagv(struct drm_i915_private *dev_priv)
37cc0db
+{
37cc0db
+	int ret;
37cc0db
+
37cc0db
+	if (IS_BROXTON(dev_priv))
37cc0db
+		return 0;
37cc0db
+	if (dev_priv->skl_sagv_enabled)
37cc0db
+		return 0;
37cc0db
+
37cc0db
+	mutex_lock(&dev_priv->rps.hw_lock);
37cc0db
+	DRM_DEBUG_KMS("Enabling the SAGV\n");
37cc0db
+
37cc0db
+	ret = sandybridge_pcode_write(dev_priv, GEN9_PCODE_SAGV_CONTROL,
37cc0db
+				      GEN9_SAGV_DYNAMIC_FREQ);
37cc0db
+	if (!ret)
37cc0db
+		dev_priv->skl_sagv_enabled = true;
37cc0db
+	else
37cc0db
+		DRM_ERROR("Failed to enable the SAGV\n");
37cc0db
+
37cc0db
+	/* We don't need to wait for SAGV when enabling */
37cc0db
+	mutex_unlock(&dev_priv->rps.hw_lock);
37cc0db
+	return ret;
37cc0db
+}
37cc0db
+
37cc0db
+static int
37cc0db
+skl_disable_sagv(struct drm_i915_private *dev_priv)
37cc0db
+{
37cc0db
+	int ret = 0;
37cc0db
+	unsigned long timeout;
37cc0db
+	u32 temp;
37cc0db
+
37cc0db
+	if (IS_BROXTON(dev_priv))
37cc0db
+		return 0;
37cc0db
+	if (!dev_priv->skl_sagv_enabled)
37cc0db
+		return 0;
37cc0db
+
37cc0db
+	mutex_lock(&dev_priv->rps.hw_lock);
37cc0db
+	DRM_DEBUG_KMS("Disabling the SAGV\n");
37cc0db
+
37cc0db
+	/* bspec says to keep retrying for at least 1 ms */
37cc0db
+	timeout = jiffies + msecs_to_jiffies(1);
37cc0db
+	do {
37cc0db
+		ret = sandybridge_pcode_write(dev_priv, GEN9_PCODE_SAGV_CONTROL,
37cc0db
+					      GEN9_SAGV_DISABLE);
37cc0db
+		if (ret) {
37cc0db
+			DRM_ERROR("Failed to disable the SAGV\n");
37cc0db
+			goto out;
37cc0db
+		}
37cc0db
+
37cc0db
+		ret = sandybridge_pcode_read(dev_priv, GEN9_PCODE_SAGV_CONTROL,
37cc0db
+					     &temp);
37cc0db
+		if (ret) {
37cc0db
+			DRM_ERROR("Failed to check the status of the SAGV\n");
37cc0db
+			goto out;
37cc0db
+		}
37cc0db
+	} while (!(temp & 0x1) && jiffies < timeout);
37cc0db
+
37cc0db
+	if (temp & 0x1) {
37cc0db
+		dev_priv->skl_sagv_enabled = false;
37cc0db
+	} else {
37cc0db
+		ret = -1;
37cc0db
+		DRM_ERROR("Request to disable SAGV timed out\n");
37cc0db
+	}
37cc0db
+
37cc0db
+out:
37cc0db
+	mutex_unlock(&dev_priv->rps.hw_lock);
37cc0db
+	return ret;
37cc0db
+}
37cc0db
+
37cc0db
+static void
37cc0db
 skl_ddb_get_pipe_allocation_limits(struct drm_device *dev,
37cc0db
 				   const struct intel_crtc_state *cstate,
37cc0db
 				   struct skl_ddb_entry *alloc, /* out */
37cc0db
@@ -3525,6 +3628,11 @@ static void skl_write_wm_values(struct drm_i915_private *dev_priv,
37cc0db
 	struct drm_device *dev = dev_priv->dev;
37cc0db
 	struct intel_crtc *crtc;
37cc0db
 
37cc0db
+	if (dev_priv->active_crtcs == 1)
37cc0db
+		skl_enable_sagv(dev_priv);
37cc0db
+	else
37cc0db
+		skl_disable_sagv(dev_priv);
37cc0db
+
37cc0db
 	for_each_intel_crtc(dev, crtc) {
37cc0db
 		int i, level, max_level = ilk_wm_max_level(dev);
37cc0db
 		enum pipe pipe = crtc->pipe;
37cc0db
@@ -4072,6 +4180,8 @@ void skl_wm_get_hw_state(struct drm_device *dev)
37cc0db
 				skl_plane_relative_data_rate(cstate, pstate, 1);
37cc0db
 		}
37cc0db
 	}
37cc0db
+
37cc0db
+	skl_sagv_get_hw_state(dev_priv);
37cc0db
 }
37cc0db
 
37cc0db
 static void ilk_pipe_wm_get_hw_state(struct drm_crtc *crtc)
37cc0db
-- 
37cc0db
2.7.4
37cc0db