diff mbox series

drm/i915: Init LCPLL on HSW/BDW if the BIOS did not

Message ID 20200303171808.7247-1-ville.syrjala@linux.intel.com (mailing list archive)
State New, archived
Headers show
Series drm/i915: Init LCPLL on HSW/BDW if the BIOS did not | expand

Commit Message

Ville Syrjälä March 3, 2020, 5:18 p.m. UTC
From: Ville Syrjälä <ville.syrjala@linux.intel.com>

There are some rare BDW machines where the BIOS apparently does
not enable LCPLL/CDLCK. Let's try to do that ourselves.

Or maybe we should treat this case as "don't use the display engine!"
kind of knob?

Cc: Chris Wilson <chris@chris-wilson.co.uk>
Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
---
 .../drm/i915/display/intel_display_power.c    | 58 ++++++++++++-------
 1 file changed, 36 insertions(+), 22 deletions(-)
diff mbox series

Patch

diff --git a/drivers/gpu/drm/i915/display/intel_display_power.c b/drivers/gpu/drm/i915/display/intel_display_power.c
index 246e406bb385..e7641e57cebd 100644
--- a/drivers/gpu/drm/i915/display/intel_display_power.c
+++ b/drivers/gpu/drm/i915/display/intel_display_power.c
@@ -4538,24 +4538,14 @@  static void icl_mbus_init(struct drm_i915_private *dev_priv)
 	}
 }
 
-static void hsw_assert_cdclk(struct drm_i915_private *dev_priv)
+static bool hsw_lcpll_ok(u32 lcpll_ctl)
 {
-	u32 val = intel_de_read(dev_priv, LCPLL_CTL);
-
-	/*
-	 * The LCPLL register should be turned on by the BIOS. For now
-	 * let's just check its state and print errors in case
-	 * something is wrong.  Don't even try to turn it on.
-	 */
-
-	if (val & LCPLL_CD_SOURCE_FCLK)
-		drm_err(&dev_priv->drm, "CDCLK source is not LCPLL\n");
-
-	if (val & LCPLL_PLL_DISABLE)
-		drm_err(&dev_priv->drm, "LCPLL is disabled\n");
-
-	if ((val & LCPLL_REF_MASK) != LCPLL_REF_NON_SSC)
-		drm_err(&dev_priv->drm, "LCPLL not using non-SSC reference\n");
+	return (lcpll_ctl & (LCPLL_PLL_DISABLE | LCPLL_PLL_LOCK |
+			     LCPLL_CD_CLOCK_DISABLE |
+			     LCPLL_ROOT_CD_CLOCK_DISABLE |
+			     LCPLL_CD2X_CLOCK_DISABLE |
+			     LCPLL_POWER_DOWN_ALLOW |
+			     LCPLL_CD_SOURCE_FCLK)) == LCPLL_PLL_LOCK;
 }
 
 static void assert_can_disable_lcpll(struct drm_i915_private *dev_priv)
@@ -4632,8 +4622,6 @@  static void hsw_disable_lcpll(struct drm_i915_private *dev_priv,
 {
 	u32 val;
 
-	assert_can_disable_lcpll(dev_priv);
-
 	val = intel_de_read(dev_priv, LCPLL_CTL);
 
 	if (switch_to_fclk) {
@@ -4681,8 +4669,7 @@  static void hsw_restore_lcpll(struct drm_i915_private *dev_priv)
 
 	val = intel_de_read(dev_priv, LCPLL_CTL);
 
-	if ((val & (LCPLL_PLL_LOCK | LCPLL_PLL_DISABLE | LCPLL_CD_SOURCE_FCLK |
-		    LCPLL_POWER_DOWN_ALLOW)) == LCPLL_PLL_LOCK)
+	if (hsw_lcpll_ok(val))
 		return;
 
 	/*
@@ -4720,6 +4707,11 @@  static void hsw_restore_lcpll(struct drm_i915_private *dev_priv)
 				"Switching back to LCPLL failed\n");
 	}
 
+	val &= ~(LCPLL_CD_CLOCK_DISABLE |
+		 LCPLL_ROOT_CD_CLOCK_DISABLE |
+		 LCPLL_CD2X_CLOCK_DISABLE);
+	intel_de_write(dev_priv, LCPLL_CTL, val);
+
 	intel_uncore_forcewake_put(&dev_priv->uncore, FORCEWAKE_ALL);
 
 	intel_update_cdclk(dev_priv);
@@ -4762,6 +4754,7 @@  static void hsw_enable_pc8(struct drm_i915_private *dev_priv)
 	}
 
 	lpt_disable_clkout_dp(dev_priv);
+	assert_can_disable_lcpll(dev_priv);
 	hsw_disable_lcpll(dev_priv, true, true);
 }
 
@@ -4781,6 +4774,27 @@  static void hsw_disable_pc8(struct drm_i915_private *dev_priv)
 	}
 }
 
+static void hsw_lcpll_init(struct drm_i915_private *dev_priv)
+{
+	u32 val;
+
+	/*
+	 * The LCPLL register should be turned on by the BIOS.
+	 * Some machines apparently don't, so we're going to
+	 * try to fix it if somehting is not correct.
+	 */
+	val = intel_de_read(dev_priv, LCPLL_CTL);
+	if (hsw_lcpll_ok(val))
+		return;
+
+	drm_dbg(&dev_priv->drm,
+		"Reinitializing misconfigured LCPLL/CDCLK (LCPLL_CTL=0x%08x)\n",
+		val);
+
+	hsw_disable_lcpll(dev_priv, true, true);
+	hsw_restore_lcpll(dev_priv);
+}
+
 static void intel_pch_reset_handshake(struct drm_i915_private *dev_priv,
 				      bool enable)
 {
@@ -5307,8 +5321,8 @@  void intel_power_domains_init_hw(struct drm_i915_private *i915, bool resume)
 		assert_ved_power_gated(i915);
 		assert_isp_power_gated(i915);
 	} else if (IS_BROADWELL(i915) || IS_HASWELL(i915)) {
-		hsw_assert_cdclk(i915);
 		intel_pch_reset_handshake(i915, !HAS_PCH_NOP(i915));
+		hsw_lcpll_init(i915);
 	} else if (IS_IVYBRIDGE(i915)) {
 		intel_pch_reset_handshake(i915, !HAS_PCH_NOP(i915));
 	}