[Intel-gfx] [PATCH 4/4] drm/i915/vlv: modeset_global_* for VLV

Jesse Barnes jbarnes at virtuousgeek.org
Fri Nov 1 16:41:27 CET 2013


On VLV/BYT, we can adjust the CDclk frequency up or down based on the
max pixel clock we need to drive.  Lowering it can save power, while
raising it is necessary to support high resolution.

Add proper modeset_global_pipes and modeset_global_resources support to
perform this adjustment as necessary.

Signed-off-by: Jesse Barnes <jbarnes at virtuousgeek.org>
---
 drivers/gpu/drm/i915/i915_reg.h      |   2 +
 drivers/gpu/drm/i915/intel_display.c | 151 +++++++++++++++++++++++++++++++++++
 2 files changed, 153 insertions(+)

diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 737d8a3..9de4adb 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -1453,6 +1453,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..96f0ac6 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -3894,6 +3894,152 @@ 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, divider, vco;
+
+	vco = valleyview_get_vco(dev_priv);
+
+	divider = (vco * 10) / cdclk;
+
+	divider = ((divider * 2) / 10) - 1;
+
+	/* Maybe use punit for anything other than 400MHz cdclk? */
+
+	/* adjust czclk ratio */
+	mutex_lock(&dev_priv->dpio_lock);
+	val = vlv_cck_read(dev_priv, 0x6b);
+	val &= ~0xf;
+	val |= divider;
+	vlv_cck_write(dev_priv, 0x6b, val);
+
+	/* 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 lower CDclk freqs don'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 +10482,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 */
-- 
1.8.3.1




More information about the Intel-gfx mailing list