From e375e4369cbfdcb7ff94d67f6bd71ad0eb6c4fa2 Mon Sep 17 00:00:00 2001
From: Jesse Barnes <jbarnes@virtuousgeek.org>
Date: Wed, 3 Aug 2011 12:36:11 -0700
Subject: [PATCH 2/2] drm/i915: enable/disable spread spectrum as needed on mode set

This resurrects Chris's old code to better manage the PCH reference
clock.  With some previous mode setting bugs fixed, it appears work work
now.

Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
---
 drivers/gpu/drm/i915/i915_drv.h      |    1 +
 drivers/gpu/drm/i915/i915_reg.h      |    1 +
 drivers/gpu/drm/i915/intel_bios.c    |    1 +
 drivers/gpu/drm/i915/intel_bios.h    |    4 ++-
 drivers/gpu/drm/i915/intel_display.c |   63 +++++++++++++++++++---------------
 5 files changed, 41 insertions(+), 29 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 456f404..cce6512 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -344,6 +344,7 @@ typedef struct drm_i915_private {
 	unsigned int lvds_vbt:1;
 	unsigned int int_crt_support:1;
 	unsigned int lvds_use_ssc:1;
+	unsigned int display_clock_mode:1;
 	int lvds_ssc_freq;
 	struct {
 		int rate;
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index e7cb5df..1c67bcc 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -2917,6 +2917,7 @@
 #define  DREF_NONSPREAD_SOURCE_MASK		(3<<9)
 #define  DREF_SUPERSPREAD_SOURCE_DISABLE        (0<<7)
 #define  DREF_SUPERSPREAD_SOURCE_ENABLE         (2<<7)
+#define  DREF_SUPERSPREAD_SOURCE_MASK		(3<<7)
 #define  DREF_SSC4_DOWNSPREAD                   (0<<6)
 #define  DREF_SSC4_CENTERSPREAD                 (1<<6)
 #define  DREF_SSC1_DISABLE                      (0<<1)
diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c
index 0b44956..2e9cee0 100644
--- a/drivers/gpu/drm/i915/intel_bios.c
+++ b/drivers/gpu/drm/i915/intel_bios.c
@@ -270,6 +270,7 @@ parse_general_features(struct drm_i915_private *dev_priv,
 			dev_priv->lvds_ssc_freq = general->ssc_freq ? 100 : 120;
 		else
 			dev_priv->lvds_ssc_freq = general->ssc_freq ? 100 : 96;
+		dev_priv->display_clock_mode = general->display_clock_mode;
 	}
 }
 
diff --git a/drivers/gpu/drm/i915/intel_bios.h b/drivers/gpu/drm/i915/intel_bios.h
index 5f8e4ed..02b1b62 100644
--- a/drivers/gpu/drm/i915/intel_bios.h
+++ b/drivers/gpu/drm/i915/intel_bios.h
@@ -120,7 +120,9 @@ struct bdb_general_features {
 	u8 ssc_freq:1;
 	u8 enable_lfp_on_override:1;
 	u8 disable_ssc_ddt:1;
-	u8 rsvd8:3; /* finish byte */
+	u8 rsvd7:1;
+	u8 display_clock_mode:1;
+	u8 rsvd8:1; /* finish byte */
 
         /* bits 3 */
 	u8 disable_smooth_vision:1;
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index f5550e3..8f0ab2a 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -3980,6 +3980,7 @@ static void ironlake_update_pch_refclk(struct drm_device *dev)
 	struct intel_encoder *encoder;
 	struct intel_encoder *has_edp_encoder = NULL;
 	u32 temp;
+	int num_connectors = 0;
 	bool has_lvds = false;
 
 	/* We need to take the global config into account */
@@ -3999,6 +4000,7 @@ static void ironlake_update_pch_refclk(struct drm_device *dev)
 				has_edp_encoder = encoder;
 				break;
 			}
+			num_connectors++;
 		}
 	}
 
@@ -4008,43 +4010,48 @@ static void ironlake_update_pch_refclk(struct drm_device *dev)
 	 * ignoring this setting.
 	 */
 	temp = I915_READ(PCH_DREF_CONTROL);
-	/* Always enable nonspread source */
+
+	/* First clear the current state for output switching */
+	temp &= ~DREF_SSC1_ENABLE;
+	temp &= ~DREF_SSC4_ENABLE;
+	temp &= ~DREF_SUPERSPREAD_SOURCE_MASK;
 	temp &= ~DREF_NONSPREAD_SOURCE_MASK;
-	temp |= DREF_NONSPREAD_SOURCE_ENABLE;
 	temp &= ~DREF_SSC_SOURCE_MASK;
-	temp |= DREF_SSC_SOURCE_ENABLE;
-	I915_WRITE(PCH_DREF_CONTROL, temp);
-
-	POSTING_READ(PCH_DREF_CONTROL);
-	udelay(200);
+	temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
 
-	if (has_edp_encoder) {
-		if (intel_panel_use_ssc(dev_priv)) {
-			temp |= DREF_SSC1_ENABLE;
-			I915_WRITE(PCH_DREF_CONTROL, temp);
-
-			POSTING_READ(PCH_DREF_CONTROL);
-			udelay(200);
-		}
-		temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
+	if (!num_connectors)
+		goto out;
 
-		/* Enable CPU source on CPU attached eDP */
-		if (!intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
-			if (intel_panel_use_ssc(dev_priv))
+	if ((has_lvds || has_edp_encoder) &&
+	    intel_panel_use_ssc(dev_priv)) {
+		temp |= DREF_SSC_SOURCE_ENABLE;
+		if (has_edp_encoder) {
+			if (!intel_encoder_is_pch_edp(&has_edp_encoder->base))
 				temp |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD;
 			else
-				temp |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD;
-		} else {
-			/* Enable SSC on PCH eDP if needed */
-			if (intel_panel_use_ssc(dev_priv)) {
-				DRM_ERROR("enabling SSC on PCH\n");
 				temp |= DREF_SUPERSPREAD_SOURCE_ENABLE;
-			}
 		}
-		I915_WRITE(PCH_DREF_CONTROL, temp);
-		POSTING_READ(PCH_DREF_CONTROL);
-		udelay(200);
+		if (!dev_priv->display_clock_mode)
+			temp |= DREF_SSC1_ENABLE;
+		num_connectors--;
+	}
+
+	/* Unhandled outputs need non-SSC clock */
+	if (num_connectors) {
+		if (dev_priv->display_clock_mode)
+			temp |= DREF_NONSPREAD_CK505_ENABLE;
+		else
+			temp |= DREF_NONSPREAD_SOURCE_ENABLE;
+		if (has_edp_encoder &&
+		    !intel_encoder_is_pch_edp(&has_edp_encoder->base) &&
+		    !intel_panel_use_ssc(dev_priv))
+			temp |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD;
 	}
+
+out:
+	I915_WRITE(PCH_DREF_CONTROL, temp);
+	POSTING_READ(PCH_DREF_CONTROL);
+	udelay(200);
 }
 
 static int intel_crtc_mode_set(struct drm_crtc *crtc,
-- 
1.7.0.4

