From: Paulo Zanoni <[email protected]>

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 <[email protected]>
---
 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 --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);
-- 
1.7.11.2

_______________________________________________
Intel-gfx mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

Reply via email to