diff mbox

[RFC,05/30] drm/i915: implement intel_ddi_pll_mode_set

Message ID 1346191621-12996-6-git-send-email-przanoni@gmail.com (mailing list archive)
State RFC
Headers show

Commit Message

Paulo Zanoni Aug. 28, 2012, 10:06 p.m. UTC
From: Paulo Zanoni <paulo.r.zanoni@intel.com>

Previously we were setting the PLLs at encoder->mode_set. This was a
problem because there's no way to fail and return error codes from
encoder->mode_set.

So now we follow the example of the previous gens and try to set the
PLLs inside ironlake_crtc_mode_set, properly failing when we need to
fail.

In addition to moving the DDI PLL code from hsw_fdi_link_train and
intel_ddi_mode_set to the new intel_ddi_pll_mode_set, we now try to
use WR PLL 2 when it's available.

Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
---
 drivers/gpu/drm/i915/intel_ddi.c     | 168 +++++++++++++++++++++++++----------
 drivers/gpu/drm/i915/intel_display.c |   4 +
 drivers/gpu/drm/i915/intel_drv.h     |   1 +
 3 files changed, 128 insertions(+), 45 deletions(-)
diff mbox

Patch

diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
index e3dac45..a29c1e7 100644
--- a/drivers/gpu/drm/i915/intel_ddi.c
+++ b/drivers/gpu/drm/i915/intel_ddi.c
@@ -136,15 +136,6 @@  void hsw_fdi_link_train(struct drm_crtc *crtc)
 	int pipe = intel_crtc->pipe;
 	u32 reg, temp, i;
 
-	/* Configure CPU PLL, wait for warmup */
-	I915_WRITE(SPLL_CTL,
-			SPLL_PLL_ENABLE |
-			SPLL_PLL_FREQ_1350MHz |
-			SPLL_PLL_SCC);
-
-	/* Use SPLL to drive the output when in FDI mode */
-	I915_WRITE(PORT_CLK_SEL(PORT_E),
-			PORT_CLK_SEL_SPLL);
 	I915_WRITE(PIPE_CLK_SEL(pipe),
 			PIPE_CLK_SEL_PORT(PORT_E));
 
@@ -645,6 +636,128 @@  static const struct wrpll_tmds_clock wrpll_tmds_clock_table[] = {
 	{298000,	2,	21,	19},
 };
 
+bool intel_ddi_pll_mode_set(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_encoder *intel_encoder;
+	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+	bool is_hdmi = false, is_crt = false;
+	int port, i, to_use, num_encoders = 0;
+	bool wrpll_used[] = {false, false};
+	bool spll_used = false;
+	uint32_t wrpll_reg[] = {WRPLL_CTL1, WRPLL_CTL2};
+	uint32_t wrpll_sel[] = {PORT_CLK_SEL_WRPLL1, PORT_CLK_SEL_WRPLL2};
+	uint32_t temp;
+
+	for_each_encoder_on_crtc(dev, crtc, intel_encoder) {
+		struct intel_hdmi *intel_hdmi;
+
+		switch (intel_encoder->type) {
+		case INTEL_OUTPUT_HDMI:
+			is_hdmi = true;
+			intel_hdmi = enc_to_intel_hdmi(&intel_encoder->base);
+			port = intel_hdmi->ddi_port;
+			break;
+		case INTEL_OUTPUT_ANALOG:
+			is_crt = true;
+			port = PORT_E;
+			break;
+		default:
+			WARN(1, "Invalid encoder type %d on crtc for pipe %d\n",
+			     intel_encoder->type, intel_crtc->pipe);
+			return false;
+		}
+
+		num_encoders++;
+	}
+
+	if (num_encoders != 1) {
+		WARN(1, "%d encoders on crtc for pipe %d\n", num_encoders,
+		     intel_crtc->pipe);
+		return false;
+	}
+
+	for (i = PORT_A; i <= PORT_E; i++) {
+		if (i == port)
+			continue;
+
+		switch (I915_READ(PORT_CLK_SEL(i))) {
+		case PORT_CLK_SEL_WRPLL1:
+			wrpll_used[0] = true;
+			break;
+		case PORT_CLK_SEL_WRPLL2:
+			wrpll_used[1] = true;
+			break;
+		case PORT_CLK_SEL_SPLL:
+			spll_used = true;
+			break;
+		}
+	}
+
+	if (is_hdmi) {
+		int p, n2, r2;
+
+		for (i = 0; i < ARRAY_SIZE(wrpll_reg); i++)
+			if (!wrpll_used[i])
+				break;
+		if (i == ARRAY_SIZE(wrpll_reg)) {
+			DRM_ERROR("No WRPLL available\n");
+			return false;
+		}
+		to_use = i;
+
+		temp = I915_READ(wrpll_reg[to_use]);
+		if (temp & WRPLL_PLL_ENABLE) {
+			WARN(1, "WR PLL is enabled\n");
+			temp &= ~WRPLL_PLL_ENABLE;
+			I915_WRITE(wrpll_reg[to_use], temp);
+			POSTING_READ(wrpll_reg[to_use]);
+		}
+
+		for (i = 0; i < ARRAY_SIZE(wrpll_tmds_clock_table); i++)
+			if (crtc->mode.clock <= wrpll_tmds_clock_table[i].clock)
+				break;
+		if (i == ARRAY_SIZE(wrpll_tmds_clock_table))
+			i--;
+
+		p = wrpll_tmds_clock_table[i].p;
+		n2 = wrpll_tmds_clock_table[i].n2;
+		r2 = wrpll_tmds_clock_table[i].r2;
+
+		if (wrpll_tmds_clock_table[i].clock != crtc->mode.clock)
+			DRM_INFO("WRPLL: using %dKHz settings on %dKHz mode\n",
+				 wrpll_tmds_clock_table[i].clock,
+				 crtc->mode.clock);
+
+		DRM_DEBUG_KMS("WRPLL %d: %dKHz with p=%d, n2=%d r2=%d\n",
+			      to_use +1, crtc->mode.clock, p, n2, r2);
+
+		I915_WRITE(wrpll_reg[to_use],
+			   WRPLL_PLL_ENABLE | WRPLL_PLL_SELECT_LCPLL_2700 |
+			   WRPLL_DIVIDER_REFERENCE(r2) |
+			   WRPLL_DIVIDER_FEEDBACK(n2) |
+			   WRPLL_DIVIDER_POST(p));
+
+		udelay(20);
+
+		I915_WRITE(PORT_CLK_SEL(port), wrpll_sel[to_use]);
+
+	} else if (is_crt) {
+		if (spll_used) {
+			DRM_ERROR("SPLL not available\n");
+			return false;
+		}
+
+		I915_WRITE(SPLL_CTL, SPLL_PLL_ENABLE | SPLL_PLL_FREQ_1350MHz |
+				     SPLL_PLL_SCC);
+
+		I915_WRITE(PORT_CLK_SEL(port), PORT_CLK_SEL_SPLL);
+	}
+
+	return true;
+}
+
 void intel_ddi_mode_set(struct drm_encoder *encoder,
 				struct drm_display_mode *mode,
 				struct drm_display_mode *adjusted_mode)
@@ -656,48 +769,13 @@  void intel_ddi_mode_set(struct drm_encoder *encoder,
 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
 	int port = intel_hdmi->ddi_port;
 	int pipe = intel_crtc->pipe;
-	int p, n2, r2;
-	u32 temp, i;
+	u32 temp;
 
 	/* On Haswell, we need to enable the clocks and prepare DDI function to
 	 * work in HDMI mode for this pipe.
 	 */
 	DRM_DEBUG_KMS("Preparing HDMI DDI mode for Haswell on port %c, pipe %c\n", port_name(port), pipe_name(pipe));
 
-	for (i = 0; i < ARRAY_SIZE(wrpll_tmds_clock_table); i++)
-		if (crtc->mode.clock <= wrpll_tmds_clock_table[i].clock)
-			break;
-
-	if (i == ARRAY_SIZE(wrpll_tmds_clock_table))
-		i--;
-
-	p = wrpll_tmds_clock_table[i].p;
-	n2 = wrpll_tmds_clock_table[i].n2;
-	r2 = wrpll_tmds_clock_table[i].r2;
-
-	if (wrpll_tmds_clock_table[i].clock != crtc->mode.clock)
-		DRM_INFO("WR PLL: using settings for %dKHz on %dKHz mode\n",
-			 wrpll_tmds_clock_table[i].clock, crtc->mode.clock);
-
-	DRM_DEBUG_KMS("WR PLL: %dKHz refresh rate with p=%d, n2=%d r2=%d\n",
-		      crtc->mode.clock, p, n2, r2);
-
-	/* Configure WR PLL 1, program the correct divider values for
-	 * the desired frequency and wait for warmup */
-	I915_WRITE(WRPLL_CTL1,
-			WRPLL_PLL_ENABLE |
-			WRPLL_PLL_SELECT_LCPLL_2700 |
-			WRPLL_DIVIDER_REFERENCE(r2) |
-			WRPLL_DIVIDER_FEEDBACK(n2) |
-			WRPLL_DIVIDER_POST(p));
-
-	udelay(20);
-
-	/* Use WRPLL1 clock to drive the output to the port, and tell the pipe to use
-	 * this port for connection.
-	 */
-	I915_WRITE(PORT_CLK_SEL(port),
-			PORT_CLK_SEL_WRPLL1);
 	I915_WRITE(PIPE_CLK_SEL(pipe),
 			PIPE_CLK_SEL_PORT(port));
 
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 33eebcb..cd506bb 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -4627,6 +4627,10 @@  static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
 		num_connectors++;
 	}
 
+	if (IS_HASWELL(dev))
+		if (!intel_ddi_pll_mode_set(crtc))
+			return -EINVAL;
+
 	refclk = ironlake_get_refclk(crtc);
 
 	/*
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index b99af38..aa62439 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -522,6 +522,7 @@  extern void gen6_gt_check_fifodbg(struct drm_i915_private *dev_priv);
 extern void ironlake_teardown_rc6(struct drm_device *dev);
 
 extern void intel_ddi_dpms(struct drm_encoder *encoder, int mode);
+extern bool intel_ddi_pll_mode_set(struct drm_crtc *crtc);
 extern void intel_ddi_mode_set(struct drm_encoder *encoder,
 				struct drm_display_mode *mode,
 				struct drm_display_mode *adjusted_mode);