diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 737d8a3..8a34dcc 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -363,6 +363,11 @@
 #define PUNIT_OPCODE_REG_READ			6
 #define PUNIT_OPCODE_REG_WRITE			7
 
+#define PUNIT_REG_DSPFREQ			0x36
+#define   DSPFREQSTAT_SHIFT			30
+#define   DSPFREQSTAT_MASK			(0x3 << DSPFREQSTAT_SHIFT)
+#define   DSPFREQGUAR_SHIFT			14
+#define   DSPFREQGUAR_MASK			(0x3 << DSPFREQGUAR_SHIFT)
 #define PUNIT_REG_PWRGT_CTRL			0x60
 #define PUNIT_REG_PWRGT_STATUS			0x61
 #define	  PUNIT_CLK_GATE			1
@@ -1453,6 +1458,8 @@
 #define   CZCLK_FREQ_MASK	0xf
 #define GMBUSFREQ_VLV		(VLV_DISPLAY_BASE + 0x6510)
 
+#define CZCLK_CDCLK_FREQ_RATIO	(VLV_DISPLAY_BASE + 0x6508)
+
 /*
  * Palette regs
  */
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index faa7548..85b1dbf 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -3894,6 +3894,176 @@ static void i9xx_pfit_enable(struct intel_crtc *crtc)
 	I915_WRITE(BCLRPAT(crtc->pipe), 0);
 }
 
+static int valleyview_get_vco(struct drm_i915_private *dev_priv)
+{
+	int vco;
+
+	switch (dev_priv->mem_freq) {
+	default:
+	case 800:
+		vco = 800;
+		break;
+	case 1066:
+		vco = 1600;
+		break;
+	case 1333:
+		vco = 2000;
+		break;
+	}
+
+	return vco;
+}
+
+/* Adjust CDclk dividers to allow high res or save power if possible */
+static void valleyview_set_cdclk(struct drm_device *dev, int cdclk)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	u32 val;
+
+	if (cdclk == 400) {
+		u32 divider, vco;
+
+		vco = valleyview_get_vco(dev_priv);
+		divider = (vco * 10) / cdclk;
+		divider = ((divider * 2) / 10) - 1;
+
+		mutex_lock(&dev_priv->dpio_lock);
+		/* adjust cdclk divider */
+		val = vlv_cck_read(dev_priv, 0x6b);
+		val &= ~0xf;
+		val |= divider;
+		vlv_cck_write(dev_priv, 0x6b, val);
+		mutex_unlock(&dev_priv->dpio_lock);
+	} else {
+		u32 cmd;
+
+		if (cdclk == 320)
+			cmd = 2;
+		else if (cdclk == 266)
+			cmd = 1;
+		else
+			cmd = 0;
+
+		mutex_lock(&dev_priv->rps.hw_lock);
+		val = vlv_punit_read(dev_priv, PUNIT_REG_DSPFREQ);
+		val &= ~DSPFREQGUAR_MASK;
+		val |= (cmd << DSPFREQGUAR_SHIFT);
+		vlv_punit_write(dev_priv, PUNIT_REG_DSPFREQ, val);
+		if (wait_for((vlv_punit_read(dev_priv, PUNIT_REG_DSPFREQ) &
+			      DSPFREQSTAT_MASK) == (cmd << DSPFREQSTAT_SHIFT),
+			     50)) {
+			DRM_ERROR("timed out waiting for CDclk change\n");
+		}
+		mutex_unlock(&dev_priv->rps.hw_lock);
+	}
+
+	mutex_lock(&dev_priv->dpio_lock);
+	/* adjust self-refresh exit latency value */
+	val = vlv_bunit_read(dev_priv, 0x11);
+	val &= ~0x7f;
+
+	/*
+	 * For high bandwidth configs, we set a higher latency in the bunit
+	 * so that the core display fetch happens in time to avoid underruns.
+	 */
+	if (cdclk == 400)
+		val |= 0x12;
+	else
+		val |= 0xc;
+	vlv_bunit_write(dev_priv, 0x11, val);
+	mutex_unlock(&dev_priv->dpio_lock);
+}
+
+static int valleyview_cur_cdclk(struct drm_i915_private *dev_priv)
+{
+	int cur_cdclk, vco;
+	int divider;
+
+	vco = valleyview_get_vco(dev_priv);
+
+	mutex_lock(&dev_priv->dpio_lock);
+	divider = vlv_cck_read(dev_priv, 0x6b);
+	mutex_unlock(&dev_priv->dpio_lock);
+
+	divider &= 0xf;
+	divider = ((divider + 1) * 10) / 2;
+
+	cur_cdclk = (vco * 10) / divider;
+
+	return cur_cdclk;
+}
+
+static int valleyview_calc_cdclk(struct drm_i915_private *dev_priv,
+				 int max_pixclk)
+{
+	int cur_cdclk;
+
+	cur_cdclk = valleyview_cur_cdclk(dev_priv);
+
+	/*
+	 * Really only a few cases to deal with, as only 4 CDclks are supported:
+	 *   200MHz
+	 *   267MHz
+	 *   320MHz
+	 *   400MHz
+	 * So we check to see whether we're above 90% of the lower bin and
+	 * adjust if needed.
+	 */
+	if (max_pixclk > 288000) {
+		return 400;
+	} else if (max_pixclk <= 288000 && max_pixclk > 240300) {
+		return 320;
+	} else
+		return 266;
+	/* Looks like the 200MHz CDclk freq doesn't work on some configs */
+}
+
+static int intel_mode_max_pixclk(struct drm_i915_private *dev_priv)
+{
+	struct drm_device *dev = dev_priv->dev;
+	struct intel_crtc *intel_crtc;
+	int max_pixclk = 0;
+
+	list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list,
+			    base.head) {
+		if (!intel_crtc->base.enabled)
+			continue;
+
+		if (max_pixclk < intel_crtc->config.adjusted_mode.clock)
+			max_pixclk = intel_crtc->config.adjusted_mode.clock;
+	}
+
+	return max_pixclk;
+}
+
+static void valleyview_modeset_global_pipes(struct drm_device *dev,
+					    unsigned *prepare_pipes)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_crtc *intel_crtc;
+	int max_pixclk = intel_mode_max_pixclk(dev_priv);
+	int cur_cdclk = valleyview_cur_cdclk(dev_priv);
+
+	if (valleyview_calc_cdclk(dev_priv, max_pixclk) == cur_cdclk)
+		return;
+
+	list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list,
+			    base.head)
+		if (intel_crtc->base.enabled)
+			*prepare_pipes |= (1 << intel_crtc->pipe);
+}
+
+static void valleyview_modeset_global_resources(struct drm_device *dev)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	int max_pixclk = intel_mode_max_pixclk(dev_priv);
+	int cur_cdclk = valleyview_cur_cdclk(dev_priv);
+	int req_cdclk = valleyview_calc_cdclk(dev_priv, max_pixclk);
+
+	if (req_cdclk != cur_cdclk)
+		valleyview_set_cdclk(dev, req_cdclk);
+}
+
 static void valleyview_crtc_enable(struct drm_crtc *crtc)
 {
 	struct drm_device *dev = crtc->dev;
@@ -10336,6 +10506,11 @@ static void intel_init_display(struct drm_device *dev)
 		}
 	} else if (IS_G4X(dev)) {
 		dev_priv->display.write_eld = g4x_write_eld;
+	} else if (IS_VALLEYVIEW(dev)) {
+		dev_priv->display.modeset_global_resources =
+			valleyview_modeset_global_resources;
+		dev_priv->display.modeset_global_pipes =
+			valleyview_modeset_global_pipes;
 	}
 
 	/* Default just returns -ENODEV to indicate unsupported */
