@@ -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));
@@ -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);
/*
@@ -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);