From 3256815a24eee5fb67ebf77f159d5093703b4eb9 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 |   52 +++++++++++++++++++---------------
 5 files changed, 35 insertions(+), 24 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index ecc4fbe..095611d 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -286,6 +286,7 @@ typedef struct drm_i915_private {
 	unsigned int int_crt_support:1;
 	unsigned int lvds_use_ssc:1;
 	unsigned int edp_support:1;
+	unsigned int display_clock_mode:1;
 	int lvds_ssc_freq;
 	int edp_bpp;
 
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index b463a0b..f95162c 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -2282,6 +2282,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 70c9d4b..ef3c174 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,
 			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 4c18514..f33c4fb 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 e96f4ec..444e532 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -2905,6 +2905,7 @@ static void ironlake_update_pch_refclk(struct drm_device *dev)
 	struct intel_output *intel_output;
 	struct drm_encoder *has_edp_encoder = NULL;
 	u32 temp;
+	int num_connectors = 0;
 	bool has_lvds = false;
 
 	/* We need to take the global config into account */
@@ -2924,6 +2925,7 @@ static void ironlake_update_pch_refclk(struct drm_device *dev)
 				has_edp_encoder = encoder;
 				break;
 			}
+			num_connectors++;
 		}
 	}
 
@@ -2933,37 +2935,41 @@ static void ironlake_update_pch_refclk(struct drm_device *dev)
 	 * ignoring this setting.
 	 */
 	temp = I915_READ(PCH_DREF_CONTROL);
-	/* Always enable nonspread source */
-	temp &= ~DREF_NONSPREAD_SOURCE_MASK;
-	temp |= DREF_NONSPREAD_SOURCE_ENABLE;
-	I915_WRITE(PCH_DREF_CONTROL, temp);
-	POSTING_READ(PCH_DREF_CONTROL);
 
+	/* 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_SSC_SOURCE_MASK;
-	temp |= DREF_SSC_SOURCE_ENABLE;
-	I915_WRITE(PCH_DREF_CONTROL, temp);
-	POSTING_READ(PCH_DREF_CONTROL);
+	temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
 
-	udelay(200);
+	if (!num_connectors)
+		goto out;
 
-	if (has_edp_encoder) {
-		if (dev_priv->lvds_use_ssc) {
+	if ((has_lvds || has_edp_encoder) && dev_priv->lvds_use_ssc) {
+		temp |= DREF_SSC_SOURCE_ENABLE;
+		if (has_edp_encoder)
+			temp |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD;
+		if (!dev_priv->display_clock_mode)
 			temp |= DREF_SSC1_ENABLE;
-			I915_WRITE(PCH_DREF_CONTROL, temp);
-			POSTING_READ(PCH_DREF_CONTROL);
-
-			udelay(200);
+		num_connectors--;
+	}
 
-			temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
-			temp |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD;
-			I915_WRITE(PCH_DREF_CONTROL, temp);
-			POSTING_READ(PCH_DREF_CONTROL);
-		} else {
+	/* 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 && !dev_priv->lvds_use_ssc)
 			temp |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD;
-			I915_WRITE(PCH_DREF_CONTROL, temp);
-			POSTING_READ(PCH_DREF_CONTROL);
-		}
 	}
+
+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

