From 6be52085d3680d257443c14f24785f0f23a044b6 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 8d9df59..5df227a 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -319,6 +319,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 140f017..d9d2253 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -2610,6 +2610,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 96f75d7..19b1d15 100644
--- a/drivers/gpu/drm/i915/intel_bios.c
+++ b/drivers/gpu/drm/i915/intel_bios.c
@@ -280,6 +280,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 91fd523..0dc0cac 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -3286,6 +3286,7 @@ static void ironlake_update_pch_refclk(struct drm_device *dev)
 	struct intel_encoder *intel_encoder;
 	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 */
@@ -3305,6 +3306,7 @@ static void ironlake_update_pch_refclk(struct drm_device *dev)
 				has_edp_encoder = encoder;
 				break;
 			}
+			num_connectors++;
 		}
 	}
 
@@ -3314,37 +3316,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

