diff mbox

[2/2] drm/i915: add support for reduced pixel clocks on ILK+

Message ID 1294702736-2790-3-git-send-email-jbarnes@virtuousgeek.org (mailing list archive)
State New, archived
Headers show

Commit Message

Jesse Barnes Jan. 10, 2011, 11:38 p.m. UTC
None
diff mbox

Patch

diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 2fceb96..f327a70 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -2251,6 +2251,7 @@ 
 #define   PIPECONF_PROGRESSIVE	(0 << 21)
 #define   PIPECONF_INTERLACE_W_FIELD_INDICATION	(6 << 21)
 #define   PIPECONF_INTERLACE_FIELD_0_ONLY		(7 << 21)
+#define   PIPECONF_POWERSAVE	(1<<20)
 #define   PIPECONF_CXSR_DOWNCLOCK	(1<<16)
 #define   PIPECONF_BPP_MASK	(0x000000e0)
 #define   PIPECONF_BPP_8	(0<<5)
@@ -2958,6 +2959,15 @@ 
 #define TRANSA_DP_LINK_M2       0xe0048
 #define TRANSA_DP_LINK_N2       0xe004c
 
+#define TRANS_DATA_M1(pipe) _PIPE(pipe, TRANSA_DATA_M1, TRANSB_DATA_M1)
+#define TRANS_DATA_N1(pipe) _PIPE(pipe, TRANSA_DATA_N1, TRANSB_DATA_N1)
+#define TRANS_DATA_M2(pipe) _PIPE(pipe, TRANSA_DATA_M2, TRANSB_DATA_M2)
+#define TRANS_DATA_N2(pipe) _PIPE(pipe, TRANSA_DATA_N2, TRANSB_DATA_N2)
+#define TRANS_DP_LINK_M1(pipe) _PIPE(pipe, TRANSA_DP_LINK_M1, TRANSB_DP_LINK_M1)
+#define TRANS_DP_LINK_N1(pipe) _PIPE(pipe, TRANSA_DP_LINK_N1, TRANSB_DP_LINK_N1)
+#define TRANS_DP_LINK_M2(pipe) _PIPE(pipe, TRANSA_DP_LINK_M2, TRANSB_DP_LINK_M2)
+#define TRANS_DP_LINK_N2(pipe) _PIPE(pipe, TRANSA_DP_LINK_N2, TRANSB_DP_LINK_N2)
+
 #define TRANS_HTOTAL_B          0xe1000
 #define TRANS_HBLANK_B          0xe1004
 #define TRANS_HSYNC_B           0xe1008
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index aa1579b..678921f 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -39,7 +39,13 @@ 
 
 #include "drm_crtc_helper.h"
 
-#define HAS_eDP (intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP))
+#define HAS_eDP(crtc) (intel_pipe_has_type((crtc), INTEL_OUTPUT_EDP))
+#define HAS_DP(crtc) (intel_pipe_has_type((crtc), INTEL_OUTPUT_DISPLAYPORT))
+#define HAS_LVDS(crtc) (intel_pipe_has_type((crtc), INTEL_OUTPUT_LVDS))
+#define HAS_TVOUT(crtc) (intel_pipe_has_type((crtc), INTEL_OUTPUT_TVOUT))
+#define HAS_SDVO(crtc) (intel_pipe_has_type((crtc), INTEL_OUTPUT_SDVO))
+#define HAS_HDMI(crtc) (intel_pipe_has_type((crtc), INTEL_OUTPUT_HDMI))
+#define HAS_ANALOG(crtc) (intel_pipe_has_type((crtc), INTEL_OUTPUT_ANALOG))
 
 bool intel_pipe_has_type (struct drm_crtc *crtc, int type);
 static void intel_update_watermarks(struct drm_device *dev);
@@ -649,7 +655,7 @@  static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc,
 	struct drm_i915_private *dev_priv = dev->dev_private;
 	const intel_limit_t *limit;
 
-	if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+	if (HAS_LVDS(crtc)) {
 		if ((I915_READ(PCH_LVDS) & LVDS_CLKB_POWER_MASK) ==
 		    LVDS_CLKB_POWER_UP) {
 			/* LVDS dual channel */
@@ -663,8 +669,7 @@  static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc,
 			else
 				limit = &intel_limits_ironlake_single_lvds;
 		}
-	} else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT) ||
-			HAS_eDP)
+	} else if (HAS_DP(crtc) || HAS_eDP(crtc))
 		limit = &intel_limits_ironlake_display_port;
 	else
 		limit = &intel_limits_ironlake_dac;
@@ -678,7 +683,7 @@  static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc)
 	struct drm_i915_private *dev_priv = dev->dev_private;
 	const intel_limit_t *limit;
 
-	if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+	if (HAS_LVDS(crtc)) {
 		if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) ==
 		    LVDS_CLKB_POWER_UP)
 			/* LVDS with dual channel */
@@ -686,12 +691,11 @@  static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc)
 		else
 			/* LVDS with dual channel */
 			limit = &intel_limits_g4x_single_channel_lvds;
-	} else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_HDMI) ||
-		   intel_pipe_has_type(crtc, INTEL_OUTPUT_ANALOG)) {
+	} else if (HAS_HDMI(crtc) || HAS_ANALOG(crtc)) {
 		limit = &intel_limits_g4x_hdmi;
-	} else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_SDVO)) {
+	} else if (HAS_SDVO(crtc)) {
 		limit = &intel_limits_g4x_sdvo;
-	} else if (intel_pipe_has_type (crtc, INTEL_OUTPUT_DISPLAYPORT)) {
+	} else if (HAS_DP(crtc)) {
 		limit = &intel_limits_g4x_display_port;
 	} else /* The option is for other outputs */
 		limit = &intel_limits_i9xx_sdvo;
@@ -709,17 +713,17 @@  static const intel_limit_t *intel_limit(struct drm_crtc *crtc, int refclk)
 	else if (IS_G4X(dev)) {
 		limit = intel_g4x_limit(crtc);
 	} else if (IS_PINEVIEW(dev)) {
-		if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
+		if (HAS_LVDS(crtc))
 			limit = &intel_limits_pineview_lvds;
 		else
 			limit = &intel_limits_pineview_sdvo;
 	} else if (!IS_GEN2(dev)) {
-		if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
+		if (HAS_LVDS(crtc))
 			limit = &intel_limits_i9xx_lvds;
 		else
 			limit = &intel_limits_i9xx_sdvo;
 	} else {
-		if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
+		if (HAS_LVDS(crtc))
 			limit = &intel_limits_i8xx_lvds;
 		else
 			limit = &intel_limits_i8xx_dvo;
@@ -809,8 +813,7 @@  intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
 	intel_clock_t clock;
 	int err = target;
 
-	if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) &&
-	    (I915_READ(LVDS)) != 0) {
+	if (HAS_LVDS(crtc) && (I915_READ(LVDS)) != 0) {
 		/*
 		 * For LVDS, if the panel is on, just rely on its current
 		 * settings for dual-channel.  We haven't figured out how to
@@ -875,7 +878,7 @@  intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
 	int err_most = (target >> 8) + (target >> 9);
 	found = false;
 
-	if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+	if (HAS_LVDS(crtc)) {
 		int lvds_reg;
 
 		if (HAS_PCH_SPLIT(dev))
@@ -2039,7 +2042,7 @@  static void ironlake_crtc_enable(struct drm_crtc *crtc)
 	intel_crtc->active = true;
 	intel_update_watermarks(dev);
 
-	if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+	if (HAS_LVDS(crtc)) {
 		temp = I915_READ(PCH_LVDS);
 		if ((temp & LVDS_PORT_EN) == 0)
 			I915_WRITE(PCH_LVDS, temp | LVDS_PORT_EN);
@@ -2049,7 +2052,7 @@  static void ironlake_crtc_enable(struct drm_crtc *crtc)
 
 	/* Enable panel fitting for LVDS */
 	if (dev_priv->pch_pf_size &&
-	    (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) || HAS_eDP)) {
+	    (HAS_LVDS(crtc) || HAS_eDP(crtc))) {
 		/* Force use of hard-coded filter coefficients
 		 * as some pre-programmed values are broken,
 		 * e.g. x201.
@@ -2116,8 +2119,7 @@  static void ironlake_crtc_enable(struct drm_crtc *crtc)
 	intel_fdi_normal_train(crtc);
 
 	/* For PCH DP, enable TRANS_DP_CTL */
-	if (HAS_PCH_CPT(dev) &&
-	    intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) {
+	if (HAS_PCH_CPT(dev) && HAS_DP(crtc)) {
 		reg = TRANS_DP_CTL(pipe);
 		temp = I915_READ(reg);
 		temp &= ~(TRANS_DP_PORT_SEL_MASK |
@@ -2256,7 +2258,7 @@  static void ironlake_crtc_disable(struct drm_crtc *crtc)
 	POSTING_READ(reg);
 	udelay(100);
 
-	if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+	if (HAS_LVDS(crtc)) {
 		temp = I915_READ(PCH_LVDS);
 		if (temp & LVDS_PORT_EN) {
 			I915_WRITE(PCH_LVDS, temp & ~LVDS_PORT_EN);
@@ -3815,6 +3817,144 @@  static void intel_update_watermarks(struct drm_device *dev)
 				    sr_hdisplay, sr_htotal, pixel_size);
 }
 
+static int intel_find_clock(struct drm_crtc *crtc, int refclk, int target_clock,
+			    intel_clock_t *result)
+{
+	struct drm_device *dev = crtc->dev;
+	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+	int pipe = intel_crtc->pipe;
+	const intel_limit_t *limit;
+	bool ok;
+
+	/*
+	 * Returns a set of divisors for the desired target clock with the given
+	 * refclk, or FALSE.  The returned values represent the clock equation:
+	 * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2.
+	 */
+	limit = intel_limit(crtc, refclk);
+	ok = limit->find_pll(limit, crtc, target_clock, refclk, result);
+	if (!ok) {
+		drm_vblank_post_modeset(dev, pipe);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * SDVO TV has fixed PLL values depend on its clock range,
+ * this mirrors vbios setting.
+ */
+static void intel_sdvo_tv_adjust(struct drm_display_mode *mode, intel_clock_t *clock)
+{
+	if (mode->clock >= 100000 && mode->clock < 140500) {
+		clock->p1 = 2;
+		clock->p2 = 10;
+		clock->n = 3;
+		clock->m1 = 16;
+		clock->m2 = 8;
+	} else if (mode->clock >= 140500 && mode->clock <= 200000) {
+		clock->p1 = 1;
+		clock->p2 = 10;
+		clock->n = 6;
+		clock->m1 = 12;
+		clock->m2 = 8;
+	}
+}
+
+static int intel_calc_fdi_m_n(struct drm_crtc *crtc,
+			      int target_clock,
+			      int pixel_multiplier,
+			      struct intel_encoder *edp_encoder,
+			      struct fdi_m_n *m_n)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+	int pipe = intel_crtc->pipe;
+	int lane = 0, link_bw, bpp;
+	u32 temp;
+
+	/* CPU eDP doesn't require FDI link, so just set DP M/N
+	   according to current link config */
+	if (edp_encoder && !intel_encoder_is_pch_edp(&edp_encoder->base)) {
+		intel_edp_link_config(edp_encoder, &lane, &link_bw);
+	} else {
+		/* FDI is a binary signal running at ~2.7GHz, encoding
+		 * each output octet as 10 bits. The actual frequency
+		 * is stored as a divider into a 100MHz clock, and the
+		 * mode pixel clock is stored in units of 1KHz.
+		 * Hence the bw of each lane in terms of the mode signal
+		 * is:
+		 */
+		link_bw = intel_fdi_link_freq(dev) * MHz(100)/KHz(1)/10;
+	}
+
+	/* determine panel color depth */
+	temp = I915_READ(PIPECONF(pipe));
+	temp &= ~PIPE_BPC_MASK;
+	if (HAS_LVDS(crtc)) {
+		/* the BPC will be 6 if it is 18-bit LVDS panel */
+		if ((I915_READ(PCH_LVDS) & LVDS_A3_POWER_MASK) == LVDS_A3_POWER_UP)
+			temp |= PIPE_8BPC;
+		else
+			temp |= PIPE_6BPC;
+	} else if (edp_encoder) {
+		switch (dev_priv->edp.bpp/3) {
+		case 8:
+			temp |= PIPE_8BPC;
+			break;
+		case 10:
+			temp |= PIPE_10BPC;
+			break;
+		case 6:
+			temp |= PIPE_6BPC;
+			break;
+		case 12:
+			temp |= PIPE_12BPC;
+			break;
+		}
+	} else
+		temp |= PIPE_8BPC;
+	I915_WRITE(PIPECONF(pipe), temp);
+
+	switch (temp & PIPE_BPC_MASK) {
+	case PIPE_8BPC:
+		bpp = 24;
+		break;
+	case PIPE_10BPC:
+		bpp = 30;
+		break;
+	case PIPE_6BPC:
+		bpp = 18;
+		break;
+	case PIPE_12BPC:
+		bpp = 36;
+		break;
+	default:
+		DRM_ERROR("unknown pipe bpc value\n");
+		bpp = 24;
+	}
+
+	if (!lane) {
+		/*
+		 * Account for spread spectrum to avoid
+		 * oversubscribing the link. Max center spread
+		 * is 2.5%; use 5% for safety's sake.
+		 */
+		u32 bps = target_clock * bpp * 21 / 20;
+		lane = bps / (link_bw * 8) + 1;
+	}
+
+	intel_crtc->fdi_lanes = lane;
+
+	if (pixel_multiplier > 1)
+		link_bw *= pixel_multiplier;
+	ironlake_compute_m_n(bpp, lane, target_clock, link_bw, m_n);
+
+	return 0;
+}
+
 static int intel_crtc_mode_set(struct drm_crtc *crtc,
 			       struct drm_display_mode *mode,
 			       struct drm_display_mode *adjusted_mode,
@@ -3830,54 +3970,29 @@  static int intel_crtc_mode_set(struct drm_crtc *crtc,
 	int refclk, num_connectors = 0;
 	intel_clock_t clock, reduced_clock;
 	u32 dpll, fp = 0, fp2 = 0, dspcntr, pipeconf;
-	bool ok, has_reduced_clock = false, is_sdvo = false, is_dvo = false;
-	bool is_crt = false, is_lvds = false, is_tv = false, is_dp = false;
+	bool has_reduced_clock = false;
 	struct intel_encoder *has_edp_encoder = NULL;
 	struct drm_mode_config *mode_config = &dev->mode_config;
 	struct intel_encoder *encoder;
-	const intel_limit_t *limit;
 	int ret;
-	struct fdi_m_n m_n = {0};
+	struct fdi_m_n m_n = {0}, reduced_m_n = {0};
 	u32 reg, temp;
-	int target_clock;
+	int target_clock, pixel_multiplier;
 
 	drm_vblank_pre_modeset(dev, pipe);
 
-	list_for_each_entry(encoder, &mode_config->encoder_list, base.head) {
-		if (encoder->base.crtc != crtc)
-			continue;
-
-		switch (encoder->type) {
-		case INTEL_OUTPUT_LVDS:
-			is_lvds = true;
-			break;
-		case INTEL_OUTPUT_SDVO:
-		case INTEL_OUTPUT_HDMI:
-			is_sdvo = true;
-			if (encoder->needs_tv_clock)
-				is_tv = true;
-			break;
-		case INTEL_OUTPUT_DVO:
-			is_dvo = true;
-			break;
-		case INTEL_OUTPUT_TVOUT:
-			is_tv = true;
-			break;
-		case INTEL_OUTPUT_ANALOG:
-			is_crt = true;
-			break;
-		case INTEL_OUTPUT_DISPLAYPORT:
-			is_dp = true;
-			break;
-		case INTEL_OUTPUT_EDP:
-			has_edp_encoder = encoder;
-			break;
+	/*
+	 * We need to do things differently if this crtc is driving more than
+	 * one output
+	 */
+	list_for_each_entry(encoder, &mode_config->encoder_list, base.head)
+		if (encoder->base.crtc == crtc) {
+			num_connectors++;
+			if (encoder->type == INTEL_OUTPUT_EDP)
+				has_edp_encoder = encoder;
 		}
 
-		num_connectors++;
-	}
-
-	if (is_lvds && dev_priv->lvds_use_ssc && num_connectors < 2) {
+	if (HAS_LVDS(crtc) && dev_priv->lvds_use_ssc && num_connectors < 2) {
 		refclk = dev_priv->lvds_ssc_freq * 1000;
 		DRM_DEBUG_KMS("using SSC reference clock of %d MHz\n",
 			      refclk / 1000);
@@ -3890,28 +4005,32 @@  static int intel_crtc_mode_set(struct drm_crtc *crtc,
 		refclk = 48000;
 	}
 
-	/*
-	 * Returns a set of divisors for the desired target clock with the given
-	 * refclk, or FALSE.  The returned values represent the clock equation:
-	 * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2.
-	 */
-	limit = intel_limit(crtc, refclk);
-	ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, &clock);
-	if (!ok) {
-		DRM_ERROR("Couldn't find PLL settings for mode!\n");
-		drm_vblank_post_modeset(dev, pipe);
-		return -EINVAL;
-	}
 
 	/* Ensure that the cursor is valid for the new mode before changing... */
 	intel_crtc_update_cursor(crtc, true);
 
-	if ((is_lvds || has_edp_encoder) && dev_priv->panel_downclock_avail) {
-		has_reduced_clock = limit->find_pll(limit, crtc,
-						    dev_priv->panel_downclock,
-						    refclk,
-						    &reduced_clock);
-		if (has_reduced_clock && (clock.p != reduced_clock.p)) {
+	/* [e]DP over FDI requires target mode clock
+	   instead of link clock */
+	if (HAS_DP(crtc) || HAS_eDP(crtc))
+		target_clock = mode->clock;
+	else
+		target_clock = adjusted_mode->clock;
+	pixel_multiplier = intel_mode_get_pixel_multiplier(adjusted_mode);
+
+	ret = intel_find_clock(crtc, refclk, target_clock, &clock);
+	if (ret) {
+		DRM_ERROR("Couldn't find PLL settings for mode: refclk %d, target %d\n", refclk, target_clock);
+		return ret;
+	}
+
+	/* Find a PLL values for a reduced refresh if possible */
+	if ((HAS_LVDS(crtc) || has_edp_encoder) &&
+	    dev_priv->panel_downclock_avail) {
+		ret = intel_find_clock(crtc, refclk, dev_priv->panel_downclock,
+				       &reduced_clock);
+		if (ret)
+			DRM_ERROR("Couldn't find PLL settings for reduced refresh mode: refclk %d, target %d\n", refclk, adjusted_mode->clock);
+		else if (clock.p != reduced_clock.p) {
 			/*
 			 * If the different P is found, it means that we can't
 			 * switch the display clock by using the FP0/FP1.
@@ -3920,118 +4039,21 @@  static int intel_crtc_mode_set(struct drm_crtc *crtc,
 			 */
 			DRM_DEBUG_KMS("Different P is found for "
 				      "LVDS clock/downclock\n");
-			has_reduced_clock = 0;
-		}
-	}
-	/* SDVO TV has fixed PLL values depend on its clock range,
-	   this mirrors vbios setting. */
-	if (is_sdvo && is_tv) {
-		if (adjusted_mode->clock >= 100000
-		    && adjusted_mode->clock < 140500) {
-			clock.p1 = 2;
-			clock.p2 = 10;
-			clock.n = 3;
-			clock.m1 = 16;
-			clock.m2 = 8;
-		} else if (adjusted_mode->clock >= 140500
-			   && adjusted_mode->clock <= 200000) {
-			clock.p1 = 1;
-			clock.p2 = 10;
-			clock.n = 6;
-			clock.m1 = 12;
-			clock.m2 = 8;
-		}
-	}
-
-	/* FDI link */
-	if (HAS_PCH_SPLIT(dev)) {
-		int pixel_multiplier = intel_mode_get_pixel_multiplier(adjusted_mode);
-		int lane = 0, link_bw, bpp;
-		/* CPU eDP doesn't require FDI link, so just set DP M/N
-		   according to current link config */
-		if (has_edp_encoder && !intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
-			target_clock = mode->clock;
-			intel_edp_link_config(has_edp_encoder,
-					      &lane, &link_bw);
-		} else {
-			/* [e]DP over FDI requires target mode clock
-			   instead of link clock */
-			if (is_dp || intel_encoder_is_pch_edp(&has_edp_encoder->base))
-				target_clock = mode->clock;
-			else
-				target_clock = adjusted_mode->clock;
-
-			/* FDI is a binary signal running at ~2.7GHz, encoding
-			 * each output octet as 10 bits. The actual frequency
-			 * is stored as a divider into a 100MHz clock, and the
-			 * mode pixel clock is stored in units of 1KHz.
-			 * Hence the bw of each lane in terms of the mode signal
-			 * is:
-			 */
-			link_bw = intel_fdi_link_freq(dev) * MHz(100)/KHz(1)/10;
-		}
-
-		/* determine panel color depth */
-		temp = I915_READ(PIPECONF(pipe));
-		temp &= ~PIPE_BPC_MASK;
-		if (is_lvds) {
-			/* the BPC will be 6 if it is 18-bit LVDS panel */
-			if ((I915_READ(PCH_LVDS) & LVDS_A3_POWER_MASK) == LVDS_A3_POWER_UP)
-				temp |= PIPE_8BPC;
-			else
-				temp |= PIPE_6BPC;
-		} else if (has_edp_encoder) {
-			switch (dev_priv->edp.bpp/3) {
-			case 8:
-				temp |= PIPE_8BPC;
-				break;
-			case 10:
-				temp |= PIPE_10BPC;
-				break;
-			case 6:
-				temp |= PIPE_6BPC;
-				break;
-			case 12:
-				temp |= PIPE_12BPC;
-				break;
-			}
+			has_reduced_clock = false;
 		} else
-			temp |= PIPE_8BPC;
-		I915_WRITE(PIPECONF(pipe), temp);
-
-		switch (temp & PIPE_BPC_MASK) {
-		case PIPE_8BPC:
-			bpp = 24;
-			break;
-		case PIPE_10BPC:
-			bpp = 30;
-			break;
-		case PIPE_6BPC:
-			bpp = 18;
-			break;
-		case PIPE_12BPC:
-			bpp = 36;
-			break;
-		default:
-			DRM_ERROR("unknown pipe bpc value\n");
-			bpp = 24;
-		}
-
-		if (!lane) {
-			/* 
-			 * Account for spread spectrum to avoid
-			 * oversubscribing the link. Max center spread
-			 * is 2.5%; use 5% for safety's sake.
-			 */
-			u32 bps = target_clock * bpp * 21 / 20;
-			lane = bps / (link_bw * 8) + 1;
-		}
+			has_reduced_clock = true;
+	}
 
-		intel_crtc->fdi_lanes = lane;
+	if (HAS_SDVO(crtc) && HAS_TVOUT(crtc))
+		intel_sdvo_tv_adjust(adjusted_mode, &clock);
 
-		if (pixel_multiplier > 1)
-			link_bw *= pixel_multiplier;
-		ironlake_compute_m_n(bpp, lane, target_clock, link_bw, &m_n);
+	if (HAS_PCH_SPLIT(dev)) {
+		intel_calc_fdi_m_n(crtc, target_clock, pixel_multiplier,
+				   has_edp_encoder, &m_n);
+		if (has_reduced_clock)
+			intel_calc_fdi_m_n(crtc, dev_priv->panel_downclock,
+					   pixel_multiplier, has_edp_encoder,
+					   &reduced_m_n);
 	}
 
 	/* Ironlake: try to setup display ref clock before DPLL
@@ -4096,12 +4118,12 @@  static int intel_crtc_mode_set(struct drm_crtc *crtc,
 	if (HAS_PCH_SPLIT(dev)) {
 		int factor = 21;
 
-		if (is_lvds) {
+		if (HAS_LVDS(crtc)) {
 			if ((dev_priv->lvds_use_ssc &&
 			     dev_priv->lvds_ssc_freq == 100) ||
 			    (I915_READ(PCH_LVDS) & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP)
 				factor = 25;
-		} else if (is_sdvo && is_tv)
+		} else if (HAS_SDVO(crtc) && HAS_TVOUT(crtc))
 			factor = 20;
 
 		if (clock.m1 < factor * clock.n)
@@ -4113,11 +4135,11 @@  static int intel_crtc_mode_set(struct drm_crtc *crtc,
 		dpll = DPLL_VGA_MODE_DIS;
 
 	if (!IS_GEN2(dev)) {
-		if (is_lvds)
+		if (HAS_LVDS(crtc))
 			dpll |= DPLLB_MODE_LVDS;
 		else
 			dpll |= DPLLB_MODE_DAC_SERIAL;
-		if (is_sdvo) {
+		if (HAS_SDVO(crtc)) {
 			int pixel_multiplier = intel_mode_get_pixel_multiplier(adjusted_mode);
 			if (pixel_multiplier > 1) {
 				if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))
@@ -4127,7 +4149,7 @@  static int intel_crtc_mode_set(struct drm_crtc *crtc,
 			}
 			dpll |= DPLL_DVO_HIGH_SPEED;
 		}
-		if (is_dp || intel_encoder_is_pch_edp(&has_edp_encoder->base))
+		if (HAS_DP(crtc) || intel_encoder_is_pch_edp(&has_edp_encoder->base))
 			dpll |= DPLL_DVO_HIGH_SPEED;
 
 		/* compute bitmask from p1 value */
@@ -4158,7 +4180,7 @@  static int intel_crtc_mode_set(struct drm_crtc *crtc,
 		if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev))
 			dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT);
 	} else {
-		if (is_lvds) {
+		if (HAS_LVDS(crtc)) {
 			dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT;
 		} else {
 			if (clock.p1 == 2)
@@ -4170,13 +4192,13 @@  static int intel_crtc_mode_set(struct drm_crtc *crtc,
 		}
 	}
 
-	if (is_sdvo && is_tv)
+	if (HAS_SDVO(crtc) && HAS_TVOUT(crtc))
 		dpll |= PLL_REF_INPUT_TVCLKINBC;
-	else if (is_tv)
+	else if (HAS_TVOUT(crtc))
 		/* XXX: just matching BIOS for now */
 		/*	dpll |= PLL_REF_INPUT_TVCLKINBC; */
 		dpll |= 3;
-	else if (is_lvds && dev_priv->lvds_use_ssc && num_connectors < 2)
+	else if (HAS_LVDS(crtc) && dev_priv->lvds_use_ssc && num_connectors < 2)
 		dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN;
 	else
 		dpll |= PLL_REF_INPUT_DREFCLK;
@@ -4252,7 +4274,7 @@  static int intel_crtc_mode_set(struct drm_crtc *crtc,
 	 * This is an exception to the general rule that mode_set doesn't turn
 	 * things on.
 	 */
-	if (is_lvds) {
+	if (HAS_LVDS(crtc)) {
 		reg = LVDS;
 		if (HAS_PCH_SPLIT(dev))
 			reg = PCH_LVDS;
@@ -4298,14 +4320,15 @@  static int intel_crtc_mode_set(struct drm_crtc *crtc,
 	if (HAS_PCH_SPLIT(dev)) {
 		pipeconf &= ~PIPECONF_DITHER_EN;
 		pipeconf &= ~PIPECONF_DITHER_TYPE_MASK;
-		if (dev_priv->lvds_dither && (is_lvds || has_edp_encoder)) {
+		if (dev_priv->lvds_dither && (HAS_LVDS(crtc) || has_edp_encoder)) {
 			pipeconf |= PIPECONF_DITHER_EN;
 			pipeconf |= PIPECONF_DITHER_TYPE_ST1;
 		}
 	}
 
-	if (is_dp || intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
-		intel_dp_set_m_n(crtc, mode, adjusted_mode);
+	if (HAS_DP(crtc) || intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
+		intel_dp_set_m_n(crtc, mode, adjusted_mode,
+				 has_reduced_clock ? dev_priv->panel_downclock : 0);
 	} else if (HAS_PCH_SPLIT(dev)) {
 		/* For non-DP output, clear any trans DP clock recovery setting.*/
 		if (pipe == 0) {
@@ -4330,7 +4353,7 @@  static int intel_crtc_mode_set(struct drm_crtc *crtc,
 
 		if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) {
 			temp = 0;
-			if (is_sdvo) {
+			if (HAS_SDVO(crtc)) {
 				temp = intel_mode_get_pixel_multiplier(adjusted_mode);
 				if (temp > 1)
 					temp = (temp - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT;
@@ -4349,7 +4372,7 @@  static int intel_crtc_mode_set(struct drm_crtc *crtc,
 	}
 
 	intel_crtc->lowfreq_avail = false;
-	if ((is_lvds || has_edp_encoder) && has_reduced_clock && i915_powersave) {
+	if ((HAS_LVDS(crtc) || has_edp_encoder) && has_reduced_clock && i915_powersave) {
 		I915_WRITE(fp_reg + 4, fp2);
 		intel_crtc->lowfreq_avail = true;
 		if (HAS_PIPE_CXSR(dev)) {
@@ -4414,11 +4437,14 @@  static int intel_crtc_mode_set(struct drm_crtc *crtc,
 		I915_WRITE(PIPE_LINK_M1(pipe), m_n.link_m);
 		I915_WRITE(PIPE_LINK_N1(pipe), m_n.link_n);
 
-		I915_WRITE(PIPE_DATA_M2(pipe), TU_SIZE(m_n.tu) | m_n.gmch_m);
-		I915_WRITE(PIPE_DATA_N2(pipe), m_n.gmch_n);
-		I915_WRITE(PIPE_LINK_M2(pipe), m_n.link_m);
-		I915_WRITE(PIPE_LINK_N2(pipe), m_n.link_n);
-
+		if (has_reduced_clock) {
+			I915_WRITE(PIPE_DATA_M2(pipe),
+				   TU_SIZE(reduced_m_n.tu) |
+				   reduced_m_n.gmch_m);
+			I915_WRITE(PIPE_DATA_N2(pipe), reduced_m_n.gmch_n);
+			I915_WRITE(PIPE_LINK_M2(pipe), reduced_m_n.link_m);
+			I915_WRITE(PIPE_LINK_N2(pipe), reduced_m_n.link_n);
+		}
 		if (has_edp_encoder && !intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
 			ironlake_set_pll_edp(crtc, adjusted_mode->clock);
 		}
@@ -5047,12 +5073,23 @@  static void intel_increase_pllclock(struct drm_crtc *crtc)
 	if (!dev_priv->panel_downclock_avail)
 		return;
 
-	if (HAS_PCH_SPLIT(dev))
-		dpll_reg = PCH_DPLL(pipe);
+	if (HAS_PCH_SPLIT(dev)) {
+		int pipeconf_reg = PIPECONF(pipe);
+		u32 pipeconf;
+
+		return;
+
+		DRM_DEBUG_DRIVER("upclocking panel\n");
+		pipeconf = I915_READ(pipeconf_reg);
+		pipeconf &= ~PIPECONF_POWERSAVE;
+		I915_WRITE(pipeconf_reg, pipeconf);
+		POSTING_READ(pipeconf_reg);
+		goto out;
+	}
 
 	dpll = I915_READ(dpll_reg);
 	if (!HAS_PIPE_CXSR(dev) && (dpll & DISPLAY_RATE_SELECT_FPA1)) {
-		DRM_ERROR("upclocking LVDS\n");
+		DRM_DEBUG_DRIVER("upclocking panel\n");
 
 		dpll &= ~DISPLAY_RATE_SELECT_FPA1;
 		I915_WRITE(dpll_reg, dpll);
@@ -5061,9 +5098,10 @@  static void intel_increase_pllclock(struct drm_crtc *crtc)
 
 		dpll = I915_READ(dpll_reg);
 		if (dpll & DISPLAY_RATE_SELECT_FPA1)
-			DRM_ERROR("failed to upclock LVDS!\n");
+			DRM_ERROR("failed to upclock panel!\n");
 	}
 
+out:
 	/* Schedule downclock */
 	mod_timer(&intel_crtc->idle_timer, jiffies +
 		  msecs_to_jiffies(CRTC_IDLE_TIMEOUT));
@@ -5081,15 +5119,26 @@  static void intel_decrease_pllclock(struct drm_crtc *crtc)
 	if (!dev_priv->panel_downclock_avail)
 		return;
 
-	if (HAS_PCH_SPLIT(dev))
-		dpll_reg = PCH_DPLL(pipe);
+	if (HAS_PCH_SPLIT(dev)) {
+		int pipeconf_reg = PIPECONF(pipe);
+		u32 pipeconf;
+
+		return;
+
+		DRM_DEBUG_DRIVER("downclocking panel\n");
+		pipeconf = I915_READ(pipeconf_reg);
+		pipeconf |= PIPECONF_POWERSAVE;
+		I915_WRITE(pipeconf_reg, pipeconf);
+		POSTING_READ(pipeconf_reg);
+		return;
+	}
 
 	/*
 	 * Since this is called by a timer, we should never get here in
 	 * the manual case.
 	 */
 	if (!HAS_PIPE_CXSR(dev) && intel_crtc->lowfreq_avail) {
-		DRM_ERROR("downclocking LVDS\n");
+		DRM_DEBUG_DRIVER("downclocking panel\n");
 
 		dpll |= DISPLAY_RATE_SELECT_FPA1;
 		I915_WRITE(dpll_reg, dpll);
@@ -5097,7 +5146,7 @@  static void intel_decrease_pllclock(struct drm_crtc *crtc)
 		intel_wait_for_vblank(dev, pipe);
 		dpll = I915_READ(dpll_reg);
 		if (!(dpll & DISPLAY_RATE_SELECT_FPA1))
-			DRM_ERROR("failed to downclock LVDS!\n");
+			DRM_ERROR("failed to downclock panel!\n");
 	}
 
 }
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index feb0c23..d1cbad5 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -676,7 +676,8 @@  intel_dp_compute_m_n(int bpp,
 
 void
 intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
-		 struct drm_display_mode *adjusted_mode)
+		 struct drm_display_mode *adjusted_mode,
+		 int reduced_pixel_clock)
 {
 	struct drm_device *dev = crtc->dev;
 	struct drm_mode_config *mode_config = &dev->mode_config;
@@ -684,7 +685,8 @@  intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
 	struct drm_i915_private *dev_priv = dev->dev_private;
 	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 	int lane_count = 4, bpp = 24;
-	struct intel_dp_m_n m_n;
+	struct intel_dp_m_n m_n, reduced_m_n;
+	int pipe = intel_crtc->pipe;
 
 	/*
 	 * Find the lane count in the intel_encoder private
@@ -711,24 +713,40 @@  intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
 	 * the number of bytes_per_pixel post-LUT, which we always
 	 * set up for 8-bits of R/G/B, or 3 bytes total.
 	 */
-	intel_dp_compute_m_n(bpp, lane_count,
-			     mode->clock, adjusted_mode->clock, &m_n);
+	intel_dp_compute_m_n(bpp, lane_count, mode->clock, adjusted_mode->clock,
+			     &m_n);
+	if (reduced_pixel_clock)
+		intel_dp_compute_m_n(bpp, lane_count, reduced_pixel_clock,
+				     adjusted_mode->clock, &reduced_m_n);
 
 	if (HAS_PCH_SPLIT(dev)) {
-		if (intel_crtc->pipe == 0) {
-			I915_WRITE(TRANSA_DATA_M1,
-				   ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
-				   m_n.gmch_m);
-			I915_WRITE(TRANSA_DATA_N1, m_n.gmch_n);
-			I915_WRITE(TRANSA_DP_LINK_M1, m_n.link_m);
-			I915_WRITE(TRANSA_DP_LINK_N1, m_n.link_n);
-		} else {
-			I915_WRITE(TRANSB_DATA_M1,
-				   ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
-				   m_n.gmch_m);
-			I915_WRITE(TRANSB_DATA_N1, m_n.gmch_n);
-			I915_WRITE(TRANSB_DP_LINK_M1, m_n.link_m);
-			I915_WRITE(TRANSB_DP_LINK_N1, m_n.link_n);
+		int trans_m1, trans_n1, trans_m2, trans_n2;
+		int trans_dp_m1, trans_dp_n1, trans_dp_m2, trans_dp_n2;
+
+		trans_m1 = TRANS_DATA_M1(pipe);
+		trans_n1 = TRANS_DATA_N1(pipe);
+		trans_m2 = TRANS_DATA_M2(pipe);
+		trans_n2 = TRANS_DATA_N2(pipe);
+
+		trans_dp_m1 = TRANS_DP_LINK_M1(pipe);
+		trans_dp_n1 = TRANS_DP_LINK_N1(pipe);
+		trans_dp_m2 = TRANS_DP_LINK_M2(pipe);
+		trans_dp_n2 = TRANS_DP_LINK_N2(pipe);
+
+		I915_WRITE(trans_m1,
+			   ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
+			   m_n.gmch_m);
+		I915_WRITE(trans_n1, m_n.gmch_n);
+		I915_WRITE(trans_dp_m1, m_n.link_m);
+		I915_WRITE(trans_dp_n1, m_n.link_n);
+
+		if (reduced_pixel_clock) {
+			I915_WRITE(trans_m2,
+				   ((reduced_m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
+				   reduced_m_n.gmch_m);
+			I915_WRITE(trans_n2, reduced_m_n.gmch_n);
+			I915_WRITE(trans_dp_m2, reduced_m_n.link_m);
+			I915_WRITE(trans_dp_n2, reduced_m_n.link_n);
 		}
 	} else {
 		if (intel_crtc->pipe == 0) {
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index d782ad9..a8313d2 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -242,7 +242,8 @@  extern bool intel_lvds_init(struct drm_device *dev);
 extern void intel_dp_init(struct drm_device *dev, int dp_reg);
 void
 intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
-		 struct drm_display_mode *adjusted_mode);
+		 struct drm_display_mode *adjusted_mode,
+		 int reduced_pixel_clock);
 extern bool intel_dpd_is_edp(struct drm_device *dev);
 extern void intel_edp_link_config (struct intel_encoder *, int *, int *);
 extern bool intel_encoder_is_pch_edp(struct drm_encoder *encoder);