As long as we calculate and program secondary PLL values, we can enable
our reduced refresh code to automatically downclock the display when
it's not active.

Signed-off-by: Jesse Barnes <[email protected]>
---
 drivers/gpu/drm/i915/i915_reg.h      |   10 +
 drivers/gpu/drm/i915/intel_display.c |  475 +++++++++++++++++++---------------
 drivers/gpu/drm/i915/intel_dp.c      |   54 +++--
 drivers/gpu/drm/i915/intel_drv.h     |    3 +-
 4 files changed, 310 insertions(+), 232 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 2fceb96..f327a70 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -2251,6 +2251,7 @@
 #define   PIPECONF_PROGRESSIVE (0 << 21)
 #define   PIPECONF_INTERLACE_W_FIELD_INDICATION        (6 << 21)
 #define   PIPECONF_INTERLACE_FIELD_0_ONLY              (7 << 21)
+#define   PIPECONF_POWERSAVE   (1<<20)
 #define   PIPECONF_CXSR_DOWNCLOCK      (1<<16)
 #define   PIPECONF_BPP_MASK    (0x000000e0)
 #define   PIPECONF_BPP_8       (0<<5)
@@ -2958,6 +2959,15 @@
 #define TRANSA_DP_LINK_M2       0xe0048
 #define TRANSA_DP_LINK_N2       0xe004c
 
+#define TRANS_DATA_M1(pipe) _PIPE(pipe, TRANSA_DATA_M1, TRANSB_DATA_M1)
+#define TRANS_DATA_N1(pipe) _PIPE(pipe, TRANSA_DATA_N1, TRANSB_DATA_N1)
+#define TRANS_DATA_M2(pipe) _PIPE(pipe, TRANSA_DATA_M2, TRANSB_DATA_M2)
+#define TRANS_DATA_N2(pipe) _PIPE(pipe, TRANSA_DATA_N2, TRANSB_DATA_N2)
+#define TRANS_DP_LINK_M1(pipe) _PIPE(pipe, TRANSA_DP_LINK_M1, 
TRANSB_DP_LINK_M1)
+#define TRANS_DP_LINK_N1(pipe) _PIPE(pipe, TRANSA_DP_LINK_N1, 
TRANSB_DP_LINK_N1)
+#define TRANS_DP_LINK_M2(pipe) _PIPE(pipe, TRANSA_DP_LINK_M2, 
TRANSB_DP_LINK_M2)
+#define TRANS_DP_LINK_N2(pipe) _PIPE(pipe, TRANSA_DP_LINK_N2, 
TRANSB_DP_LINK_N2)
+
 #define TRANS_HTOTAL_B          0xe1000
 #define TRANS_HBLANK_B          0xe1004
 #define TRANS_HSYNC_B           0xe1008
diff --git a/drivers/gpu/drm/i915/intel_display.c 
b/drivers/gpu/drm/i915/intel_display.c
index aa1579b..678921f 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -39,7 +39,13 @@
 
 #include "drm_crtc_helper.h"
 
-#define HAS_eDP (intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP))
+#define HAS_eDP(crtc) (intel_pipe_has_type((crtc), INTEL_OUTPUT_EDP))
+#define HAS_DP(crtc) (intel_pipe_has_type((crtc), INTEL_OUTPUT_DISPLAYPORT))
+#define HAS_LVDS(crtc) (intel_pipe_has_type((crtc), INTEL_OUTPUT_LVDS))
+#define HAS_TVOUT(crtc) (intel_pipe_has_type((crtc), INTEL_OUTPUT_TVOUT))
+#define HAS_SDVO(crtc) (intel_pipe_has_type((crtc), INTEL_OUTPUT_SDVO))
+#define HAS_HDMI(crtc) (intel_pipe_has_type((crtc), INTEL_OUTPUT_HDMI))
+#define HAS_ANALOG(crtc) (intel_pipe_has_type((crtc), INTEL_OUTPUT_ANALOG))
 
 bool intel_pipe_has_type (struct drm_crtc *crtc, int type);
 static void intel_update_watermarks(struct drm_device *dev);
@@ -649,7 +655,7 @@ static const intel_limit_t *intel_ironlake_limit(struct 
drm_crtc *crtc,
        struct drm_i915_private *dev_priv = dev->dev_private;
        const intel_limit_t *limit;
 
-       if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+       if (HAS_LVDS(crtc)) {
                if ((I915_READ(PCH_LVDS) & LVDS_CLKB_POWER_MASK) ==
                    LVDS_CLKB_POWER_UP) {
                        /* LVDS dual channel */
@@ -663,8 +669,7 @@ static const intel_limit_t *intel_ironlake_limit(struct 
drm_crtc *crtc,
                        else
                                limit = &intel_limits_ironlake_single_lvds;
                }
-       } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT) ||
-                       HAS_eDP)
+       } else if (HAS_DP(crtc) || HAS_eDP(crtc))
                limit = &intel_limits_ironlake_display_port;
        else
                limit = &intel_limits_ironlake_dac;
@@ -678,7 +683,7 @@ static const intel_limit_t *intel_g4x_limit(struct drm_crtc 
*crtc)
        struct drm_i915_private *dev_priv = dev->dev_private;
        const intel_limit_t *limit;
 
-       if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+       if (HAS_LVDS(crtc)) {
                if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) ==
                    LVDS_CLKB_POWER_UP)
                        /* LVDS with dual channel */
@@ -686,12 +691,11 @@ static const intel_limit_t *intel_g4x_limit(struct 
drm_crtc *crtc)
                else
                        /* LVDS with dual channel */
                        limit = &intel_limits_g4x_single_channel_lvds;
-       } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_HDMI) ||
-                  intel_pipe_has_type(crtc, INTEL_OUTPUT_ANALOG)) {
+       } else if (HAS_HDMI(crtc) || HAS_ANALOG(crtc)) {
                limit = &intel_limits_g4x_hdmi;
-       } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_SDVO)) {
+       } else if (HAS_SDVO(crtc)) {
                limit = &intel_limits_g4x_sdvo;
-       } else if (intel_pipe_has_type (crtc, INTEL_OUTPUT_DISPLAYPORT)) {
+       } else if (HAS_DP(crtc)) {
                limit = &intel_limits_g4x_display_port;
        } else /* The option is for other outputs */
                limit = &intel_limits_i9xx_sdvo;
@@ -709,17 +713,17 @@ static const intel_limit_t *intel_limit(struct drm_crtc 
*crtc, int refclk)
        else if (IS_G4X(dev)) {
                limit = intel_g4x_limit(crtc);
        } else if (IS_PINEVIEW(dev)) {
-               if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
+               if (HAS_LVDS(crtc))
                        limit = &intel_limits_pineview_lvds;
                else
                        limit = &intel_limits_pineview_sdvo;
        } else if (!IS_GEN2(dev)) {
-               if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
+               if (HAS_LVDS(crtc))
                        limit = &intel_limits_i9xx_lvds;
                else
                        limit = &intel_limits_i9xx_sdvo;
        } else {
-               if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
+               if (HAS_LVDS(crtc))
                        limit = &intel_limits_i8xx_lvds;
                else
                        limit = &intel_limits_i8xx_dvo;
@@ -809,8 +813,7 @@ intel_find_best_PLL(const intel_limit_t *limit, struct 
drm_crtc *crtc,
        intel_clock_t clock;
        int err = target;
 
-       if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) &&
-           (I915_READ(LVDS)) != 0) {
+       if (HAS_LVDS(crtc) && (I915_READ(LVDS)) != 0) {
                /*
                 * For LVDS, if the panel is on, just rely on its current
                 * settings for dual-channel.  We haven't figured out how to
@@ -875,7 +878,7 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct 
drm_crtc *crtc,
        int err_most = (target >> 8) + (target >> 9);
        found = false;
 
-       if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+       if (HAS_LVDS(crtc)) {
                int lvds_reg;
 
                if (HAS_PCH_SPLIT(dev))
@@ -2039,7 +2042,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
        intel_crtc->active = true;
        intel_update_watermarks(dev);
 
-       if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+       if (HAS_LVDS(crtc)) {
                temp = I915_READ(PCH_LVDS);
                if ((temp & LVDS_PORT_EN) == 0)
                        I915_WRITE(PCH_LVDS, temp | LVDS_PORT_EN);
@@ -2049,7 +2052,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
 
        /* Enable panel fitting for LVDS */
        if (dev_priv->pch_pf_size &&
-           (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) || HAS_eDP)) {
+           (HAS_LVDS(crtc) || HAS_eDP(crtc))) {
                /* Force use of hard-coded filter coefficients
                 * as some pre-programmed values are broken,
                 * e.g. x201.
@@ -2116,8 +2119,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
        intel_fdi_normal_train(crtc);
 
        /* For PCH DP, enable TRANS_DP_CTL */
-       if (HAS_PCH_CPT(dev) &&
-           intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) {
+       if (HAS_PCH_CPT(dev) && HAS_DP(crtc)) {
                reg = TRANS_DP_CTL(pipe);
                temp = I915_READ(reg);
                temp &= ~(TRANS_DP_PORT_SEL_MASK |
@@ -2256,7 +2258,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
        POSTING_READ(reg);
        udelay(100);
 
-       if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+       if (HAS_LVDS(crtc)) {
                temp = I915_READ(PCH_LVDS);
                if (temp & LVDS_PORT_EN) {
                        I915_WRITE(PCH_LVDS, temp & ~LVDS_PORT_EN);
@@ -3815,6 +3817,144 @@ static void intel_update_watermarks(struct drm_device 
*dev)
                                    sr_hdisplay, sr_htotal, pixel_size);
 }
 
+static int intel_find_clock(struct drm_crtc *crtc, int refclk, int 
target_clock,
+                           intel_clock_t *result)
+{
+       struct drm_device *dev = crtc->dev;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       int pipe = intel_crtc->pipe;
+       const intel_limit_t *limit;
+       bool ok;
+
+       /*
+        * Returns a set of divisors for the desired target clock with the given
+        * refclk, or FALSE.  The returned values represent the clock equation:
+        * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2.
+        */
+       limit = intel_limit(crtc, refclk);
+       ok = limit->find_pll(limit, crtc, target_clock, refclk, result);
+       if (!ok) {
+               drm_vblank_post_modeset(dev, pipe);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/*
+ * SDVO TV has fixed PLL values depend on its clock range,
+ * this mirrors vbios setting.
+ */
+static void intel_sdvo_tv_adjust(struct drm_display_mode *mode, intel_clock_t 
*clock)
+{
+       if (mode->clock >= 100000 && mode->clock < 140500) {
+               clock->p1 = 2;
+               clock->p2 = 10;
+               clock->n = 3;
+               clock->m1 = 16;
+               clock->m2 = 8;
+       } else if (mode->clock >= 140500 && mode->clock <= 200000) {
+               clock->p1 = 1;
+               clock->p2 = 10;
+               clock->n = 6;
+               clock->m1 = 12;
+               clock->m2 = 8;
+       }
+}
+
+static int intel_calc_fdi_m_n(struct drm_crtc *crtc,
+                             int target_clock,
+                             int pixel_multiplier,
+                             struct intel_encoder *edp_encoder,
+                             struct fdi_m_n *m_n)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       int pipe = intel_crtc->pipe;
+       int lane = 0, link_bw, bpp;
+       u32 temp;
+
+       /* CPU eDP doesn't require FDI link, so just set DP M/N
+          according to current link config */
+       if (edp_encoder && !intel_encoder_is_pch_edp(&edp_encoder->base)) {
+               intel_edp_link_config(edp_encoder, &lane, &link_bw);
+       } else {
+               /* FDI is a binary signal running at ~2.7GHz, encoding
+                * each output octet as 10 bits. The actual frequency
+                * is stored as a divider into a 100MHz clock, and the
+                * mode pixel clock is stored in units of 1KHz.
+                * Hence the bw of each lane in terms of the mode signal
+                * is:
+                */
+               link_bw = intel_fdi_link_freq(dev) * MHz(100)/KHz(1)/10;
+       }
+
+       /* determine panel color depth */
+       temp = I915_READ(PIPECONF(pipe));
+       temp &= ~PIPE_BPC_MASK;
+       if (HAS_LVDS(crtc)) {
+               /* the BPC will be 6 if it is 18-bit LVDS panel */
+               if ((I915_READ(PCH_LVDS) & LVDS_A3_POWER_MASK) == 
LVDS_A3_POWER_UP)
+                       temp |= PIPE_8BPC;
+               else
+                       temp |= PIPE_6BPC;
+       } else if (edp_encoder) {
+               switch (dev_priv->edp.bpp/3) {
+               case 8:
+                       temp |= PIPE_8BPC;
+                       break;
+               case 10:
+                       temp |= PIPE_10BPC;
+                       break;
+               case 6:
+                       temp |= PIPE_6BPC;
+                       break;
+               case 12:
+                       temp |= PIPE_12BPC;
+                       break;
+               }
+       } else
+               temp |= PIPE_8BPC;
+       I915_WRITE(PIPECONF(pipe), temp);
+
+       switch (temp & PIPE_BPC_MASK) {
+       case PIPE_8BPC:
+               bpp = 24;
+               break;
+       case PIPE_10BPC:
+               bpp = 30;
+               break;
+       case PIPE_6BPC:
+               bpp = 18;
+               break;
+       case PIPE_12BPC:
+               bpp = 36;
+               break;
+       default:
+               DRM_ERROR("unknown pipe bpc value\n");
+               bpp = 24;
+       }
+
+       if (!lane) {
+               /*
+                * Account for spread spectrum to avoid
+                * oversubscribing the link. Max center spread
+                * is 2.5%; use 5% for safety's sake.
+                */
+               u32 bps = target_clock * bpp * 21 / 20;
+               lane = bps / (link_bw * 8) + 1;
+       }
+
+       intel_crtc->fdi_lanes = lane;
+
+       if (pixel_multiplier > 1)
+               link_bw *= pixel_multiplier;
+       ironlake_compute_m_n(bpp, lane, target_clock, link_bw, m_n);
+
+       return 0;
+}
+
 static int intel_crtc_mode_set(struct drm_crtc *crtc,
                               struct drm_display_mode *mode,
                               struct drm_display_mode *adjusted_mode,
@@ -3830,54 +3970,29 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
        int refclk, num_connectors = 0;
        intel_clock_t clock, reduced_clock;
        u32 dpll, fp = 0, fp2 = 0, dspcntr, pipeconf;
-       bool ok, has_reduced_clock = false, is_sdvo = false, is_dvo = false;
-       bool is_crt = false, is_lvds = false, is_tv = false, is_dp = false;
+       bool has_reduced_clock = false;
        struct intel_encoder *has_edp_encoder = NULL;
        struct drm_mode_config *mode_config = &dev->mode_config;
        struct intel_encoder *encoder;
-       const intel_limit_t *limit;
        int ret;
-       struct fdi_m_n m_n = {0};
+       struct fdi_m_n m_n = {0}, reduced_m_n = {0};
        u32 reg, temp;
-       int target_clock;
+       int target_clock, pixel_multiplier;
 
        drm_vblank_pre_modeset(dev, pipe);
 
-       list_for_each_entry(encoder, &mode_config->encoder_list, base.head) {
-               if (encoder->base.crtc != crtc)
-                       continue;
-
-               switch (encoder->type) {
-               case INTEL_OUTPUT_LVDS:
-                       is_lvds = true;
-                       break;
-               case INTEL_OUTPUT_SDVO:
-               case INTEL_OUTPUT_HDMI:
-                       is_sdvo = true;
-                       if (encoder->needs_tv_clock)
-                               is_tv = true;
-                       break;
-               case INTEL_OUTPUT_DVO:
-                       is_dvo = true;
-                       break;
-               case INTEL_OUTPUT_TVOUT:
-                       is_tv = true;
-                       break;
-               case INTEL_OUTPUT_ANALOG:
-                       is_crt = true;
-                       break;
-               case INTEL_OUTPUT_DISPLAYPORT:
-                       is_dp = true;
-                       break;
-               case INTEL_OUTPUT_EDP:
-                       has_edp_encoder = encoder;
-                       break;
+       /*
+        * We need to do things differently if this crtc is driving more than
+        * one output
+        */
+       list_for_each_entry(encoder, &mode_config->encoder_list, base.head)
+               if (encoder->base.crtc == crtc) {
+                       num_connectors++;
+                       if (encoder->type == INTEL_OUTPUT_EDP)
+                               has_edp_encoder = encoder;
                }
 
-               num_connectors++;
-       }
-
-       if (is_lvds && dev_priv->lvds_use_ssc && num_connectors < 2) {
+       if (HAS_LVDS(crtc) && dev_priv->lvds_use_ssc && num_connectors < 2) {
                refclk = dev_priv->lvds_ssc_freq * 1000;
                DRM_DEBUG_KMS("using SSC reference clock of %d MHz\n",
                              refclk / 1000);
@@ -3890,28 +4005,32 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                refclk = 48000;
        }
 
-       /*
-        * Returns a set of divisors for the desired target clock with the given
-        * refclk, or FALSE.  The returned values represent the clock equation:
-        * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2.
-        */
-       limit = intel_limit(crtc, refclk);
-       ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, &clock);
-       if (!ok) {
-               DRM_ERROR("Couldn't find PLL settings for mode!\n");
-               drm_vblank_post_modeset(dev, pipe);
-               return -EINVAL;
-       }
 
        /* Ensure that the cursor is valid for the new mode before changing... 
*/
        intel_crtc_update_cursor(crtc, true);
 
-       if ((is_lvds || has_edp_encoder) && dev_priv->panel_downclock_avail) {
-               has_reduced_clock = limit->find_pll(limit, crtc,
-                                                   dev_priv->panel_downclock,
-                                                   refclk,
-                                                   &reduced_clock);
-               if (has_reduced_clock && (clock.p != reduced_clock.p)) {
+       /* [e]DP over FDI requires target mode clock
+          instead of link clock */
+       if (HAS_DP(crtc) || HAS_eDP(crtc))
+               target_clock = mode->clock;
+       else
+               target_clock = adjusted_mode->clock;
+       pixel_multiplier = intel_mode_get_pixel_multiplier(adjusted_mode);
+
+       ret = intel_find_clock(crtc, refclk, target_clock, &clock);
+       if (ret) {
+               DRM_ERROR("Couldn't find PLL settings for mode: refclk %d, 
target %d\n", refclk, target_clock);
+               return ret;
+       }
+
+       /* Find a PLL values for a reduced refresh if possible */
+       if ((HAS_LVDS(crtc) || has_edp_encoder) &&
+           dev_priv->panel_downclock_avail) {
+               ret = intel_find_clock(crtc, refclk, dev_priv->panel_downclock,
+                                      &reduced_clock);
+               if (ret)
+                       DRM_ERROR("Couldn't find PLL settings for reduced 
refresh mode: refclk %d, target %d\n", refclk, adjusted_mode->clock);
+               else if (clock.p != reduced_clock.p) {
                        /*
                         * If the different P is found, it means that we can't
                         * switch the display clock by using the FP0/FP1.
@@ -3920,118 +4039,21 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                         */
                        DRM_DEBUG_KMS("Different P is found for "
                                      "LVDS clock/downclock\n");
-                       has_reduced_clock = 0;
-               }
-       }
-       /* SDVO TV has fixed PLL values depend on its clock range,
-          this mirrors vbios setting. */
-       if (is_sdvo && is_tv) {
-               if (adjusted_mode->clock >= 100000
-                   && adjusted_mode->clock < 140500) {
-                       clock.p1 = 2;
-                       clock.p2 = 10;
-                       clock.n = 3;
-                       clock.m1 = 16;
-                       clock.m2 = 8;
-               } else if (adjusted_mode->clock >= 140500
-                          && adjusted_mode->clock <= 200000) {
-                       clock.p1 = 1;
-                       clock.p2 = 10;
-                       clock.n = 6;
-                       clock.m1 = 12;
-                       clock.m2 = 8;
-               }
-       }
-
-       /* FDI link */
-       if (HAS_PCH_SPLIT(dev)) {
-               int pixel_multiplier = 
intel_mode_get_pixel_multiplier(adjusted_mode);
-               int lane = 0, link_bw, bpp;
-               /* CPU eDP doesn't require FDI link, so just set DP M/N
-                  according to current link config */
-               if (has_edp_encoder && 
!intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
-                       target_clock = mode->clock;
-                       intel_edp_link_config(has_edp_encoder,
-                                             &lane, &link_bw);
-               } else {
-                       /* [e]DP over FDI requires target mode clock
-                          instead of link clock */
-                       if (is_dp || 
intel_encoder_is_pch_edp(&has_edp_encoder->base))
-                               target_clock = mode->clock;
-                       else
-                               target_clock = adjusted_mode->clock;
-
-                       /* FDI is a binary signal running at ~2.7GHz, encoding
-                        * each output octet as 10 bits. The actual frequency
-                        * is stored as a divider into a 100MHz clock, and the
-                        * mode pixel clock is stored in units of 1KHz.
-                        * Hence the bw of each lane in terms of the mode signal
-                        * is:
-                        */
-                       link_bw = intel_fdi_link_freq(dev) * MHz(100)/KHz(1)/10;
-               }
-
-               /* determine panel color depth */
-               temp = I915_READ(PIPECONF(pipe));
-               temp &= ~PIPE_BPC_MASK;
-               if (is_lvds) {
-                       /* the BPC will be 6 if it is 18-bit LVDS panel */
-                       if ((I915_READ(PCH_LVDS) & LVDS_A3_POWER_MASK) == 
LVDS_A3_POWER_UP)
-                               temp |= PIPE_8BPC;
-                       else
-                               temp |= PIPE_6BPC;
-               } else if (has_edp_encoder) {
-                       switch (dev_priv->edp.bpp/3) {
-                       case 8:
-                               temp |= PIPE_8BPC;
-                               break;
-                       case 10:
-                               temp |= PIPE_10BPC;
-                               break;
-                       case 6:
-                               temp |= PIPE_6BPC;
-                               break;
-                       case 12:
-                               temp |= PIPE_12BPC;
-                               break;
-                       }
+                       has_reduced_clock = false;
                } else
-                       temp |= PIPE_8BPC;
-               I915_WRITE(PIPECONF(pipe), temp);
-
-               switch (temp & PIPE_BPC_MASK) {
-               case PIPE_8BPC:
-                       bpp = 24;
-                       break;
-               case PIPE_10BPC:
-                       bpp = 30;
-                       break;
-               case PIPE_6BPC:
-                       bpp = 18;
-                       break;
-               case PIPE_12BPC:
-                       bpp = 36;
-                       break;
-               default:
-                       DRM_ERROR("unknown pipe bpc value\n");
-                       bpp = 24;
-               }
-
-               if (!lane) {
-                       /* 
-                        * Account for spread spectrum to avoid
-                        * oversubscribing the link. Max center spread
-                        * is 2.5%; use 5% for safety's sake.
-                        */
-                       u32 bps = target_clock * bpp * 21 / 20;
-                       lane = bps / (link_bw * 8) + 1;
-               }
+                       has_reduced_clock = true;
+       }
 
-               intel_crtc->fdi_lanes = lane;
+       if (HAS_SDVO(crtc) && HAS_TVOUT(crtc))
+               intel_sdvo_tv_adjust(adjusted_mode, &clock);
 
-               if (pixel_multiplier > 1)
-                       link_bw *= pixel_multiplier;
-               ironlake_compute_m_n(bpp, lane, target_clock, link_bw, &m_n);
+       if (HAS_PCH_SPLIT(dev)) {
+               intel_calc_fdi_m_n(crtc, target_clock, pixel_multiplier,
+                                  has_edp_encoder, &m_n);
+               if (has_reduced_clock)
+                       intel_calc_fdi_m_n(crtc, dev_priv->panel_downclock,
+                                          pixel_multiplier, has_edp_encoder,
+                                          &reduced_m_n);
        }
 
        /* Ironlake: try to setup display ref clock before DPLL
@@ -4096,12 +4118,12 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
        if (HAS_PCH_SPLIT(dev)) {
                int factor = 21;
 
-               if (is_lvds) {
+               if (HAS_LVDS(crtc)) {
                        if ((dev_priv->lvds_use_ssc &&
                             dev_priv->lvds_ssc_freq == 100) ||
                            (I915_READ(PCH_LVDS) & LVDS_CLKB_POWER_MASK) == 
LVDS_CLKB_POWER_UP)
                                factor = 25;
-               } else if (is_sdvo && is_tv)
+               } else if (HAS_SDVO(crtc) && HAS_TVOUT(crtc))
                        factor = 20;
 
                if (clock.m1 < factor * clock.n)
@@ -4113,11 +4135,11 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                dpll = DPLL_VGA_MODE_DIS;
 
        if (!IS_GEN2(dev)) {
-               if (is_lvds)
+               if (HAS_LVDS(crtc))
                        dpll |= DPLLB_MODE_LVDS;
                else
                        dpll |= DPLLB_MODE_DAC_SERIAL;
-               if (is_sdvo) {
+               if (HAS_SDVO(crtc)) {
                        int pixel_multiplier = 
intel_mode_get_pixel_multiplier(adjusted_mode);
                        if (pixel_multiplier > 1) {
                                if (IS_I945G(dev) || IS_I945GM(dev) || 
IS_G33(dev))
@@ -4127,7 +4149,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                        }
                        dpll |= DPLL_DVO_HIGH_SPEED;
                }
-               if (is_dp || intel_encoder_is_pch_edp(&has_edp_encoder->base))
+               if (HAS_DP(crtc) || 
intel_encoder_is_pch_edp(&has_edp_encoder->base))
                        dpll |= DPLL_DVO_HIGH_SPEED;
 
                /* compute bitmask from p1 value */
@@ -4158,7 +4180,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev))
                        dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT);
        } else {
-               if (is_lvds) {
+               if (HAS_LVDS(crtc)) {
                        dpll |= (1 << (clock.p1 - 1)) << 
DPLL_FPA01_P1_POST_DIV_SHIFT;
                } else {
                        if (clock.p1 == 2)
@@ -4170,13 +4192,13 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                }
        }
 
-       if (is_sdvo && is_tv)
+       if (HAS_SDVO(crtc) && HAS_TVOUT(crtc))
                dpll |= PLL_REF_INPUT_TVCLKINBC;
-       else if (is_tv)
+       else if (HAS_TVOUT(crtc))
                /* XXX: just matching BIOS for now */
                /*      dpll |= PLL_REF_INPUT_TVCLKINBC; */
                dpll |= 3;
-       else if (is_lvds && dev_priv->lvds_use_ssc && num_connectors < 2)
+       else if (HAS_LVDS(crtc) && dev_priv->lvds_use_ssc && num_connectors < 2)
                dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN;
        else
                dpll |= PLL_REF_INPUT_DREFCLK;
@@ -4252,7 +4274,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
         * This is an exception to the general rule that mode_set doesn't turn
         * things on.
         */
-       if (is_lvds) {
+       if (HAS_LVDS(crtc)) {
                reg = LVDS;
                if (HAS_PCH_SPLIT(dev))
                        reg = PCH_LVDS;
@@ -4298,14 +4320,15 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
        if (HAS_PCH_SPLIT(dev)) {
                pipeconf &= ~PIPECONF_DITHER_EN;
                pipeconf &= ~PIPECONF_DITHER_TYPE_MASK;
-               if (dev_priv->lvds_dither && (is_lvds || has_edp_encoder)) {
+               if (dev_priv->lvds_dither && (HAS_LVDS(crtc) || 
has_edp_encoder)) {
                        pipeconf |= PIPECONF_DITHER_EN;
                        pipeconf |= PIPECONF_DITHER_TYPE_ST1;
                }
        }
 
-       if (is_dp || intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
-               intel_dp_set_m_n(crtc, mode, adjusted_mode);
+       if (HAS_DP(crtc) || intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
+               intel_dp_set_m_n(crtc, mode, adjusted_mode,
+                                has_reduced_clock ? dev_priv->panel_downclock 
: 0);
        } else if (HAS_PCH_SPLIT(dev)) {
                /* For non-DP output, clear any trans DP clock recovery 
setting.*/
                if (pipe == 0) {
@@ -4330,7 +4353,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
 
                if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) {
                        temp = 0;
-                       if (is_sdvo) {
+                       if (HAS_SDVO(crtc)) {
                                temp = 
intel_mode_get_pixel_multiplier(adjusted_mode);
                                if (temp > 1)
                                        temp = (temp - 1) << 
DPLL_MD_UDI_MULTIPLIER_SHIFT;
@@ -4349,7 +4372,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
        }
 
        intel_crtc->lowfreq_avail = false;
-       if ((is_lvds || has_edp_encoder) && has_reduced_clock && 
i915_powersave) {
+       if ((HAS_LVDS(crtc) || has_edp_encoder) && has_reduced_clock && 
i915_powersave) {
                I915_WRITE(fp_reg + 4, fp2);
                intel_crtc->lowfreq_avail = true;
                if (HAS_PIPE_CXSR(dev)) {
@@ -4414,11 +4437,14 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                I915_WRITE(PIPE_LINK_M1(pipe), m_n.link_m);
                I915_WRITE(PIPE_LINK_N1(pipe), m_n.link_n);
 
-               I915_WRITE(PIPE_DATA_M2(pipe), TU_SIZE(m_n.tu) | m_n.gmch_m);
-               I915_WRITE(PIPE_DATA_N2(pipe), m_n.gmch_n);
-               I915_WRITE(PIPE_LINK_M2(pipe), m_n.link_m);
-               I915_WRITE(PIPE_LINK_N2(pipe), m_n.link_n);
-
+               if (has_reduced_clock) {
+                       I915_WRITE(PIPE_DATA_M2(pipe),
+                                  TU_SIZE(reduced_m_n.tu) |
+                                  reduced_m_n.gmch_m);
+                       I915_WRITE(PIPE_DATA_N2(pipe), reduced_m_n.gmch_n);
+                       I915_WRITE(PIPE_LINK_M2(pipe), reduced_m_n.link_m);
+                       I915_WRITE(PIPE_LINK_N2(pipe), reduced_m_n.link_n);
+               }
                if (has_edp_encoder && 
!intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
                        ironlake_set_pll_edp(crtc, adjusted_mode->clock);
                }
@@ -5047,12 +5073,23 @@ static void intel_increase_pllclock(struct drm_crtc 
*crtc)
        if (!dev_priv->panel_downclock_avail)
                return;
 
-       if (HAS_PCH_SPLIT(dev))
-               dpll_reg = PCH_DPLL(pipe);
+       if (HAS_PCH_SPLIT(dev)) {
+               int pipeconf_reg = PIPECONF(pipe);
+               u32 pipeconf;
+
+               return;
+
+               DRM_DEBUG_DRIVER("upclocking panel\n");
+               pipeconf = I915_READ(pipeconf_reg);
+               pipeconf &= ~PIPECONF_POWERSAVE;
+               I915_WRITE(pipeconf_reg, pipeconf);
+               POSTING_READ(pipeconf_reg);
+               goto out;
+       }
 
        dpll = I915_READ(dpll_reg);
        if (!HAS_PIPE_CXSR(dev) && (dpll & DISPLAY_RATE_SELECT_FPA1)) {
-               DRM_ERROR("upclocking LVDS\n");
+               DRM_DEBUG_DRIVER("upclocking panel\n");
 
                dpll &= ~DISPLAY_RATE_SELECT_FPA1;
                I915_WRITE(dpll_reg, dpll);
@@ -5061,9 +5098,10 @@ static void intel_increase_pllclock(struct drm_crtc 
*crtc)
 
                dpll = I915_READ(dpll_reg);
                if (dpll & DISPLAY_RATE_SELECT_FPA1)
-                       DRM_ERROR("failed to upclock LVDS!\n");
+                       DRM_ERROR("failed to upclock panel!\n");
        }
 
+out:
        /* Schedule downclock */
        mod_timer(&intel_crtc->idle_timer, jiffies +
                  msecs_to_jiffies(CRTC_IDLE_TIMEOUT));
@@ -5081,15 +5119,26 @@ static void intel_decrease_pllclock(struct drm_crtc 
*crtc)
        if (!dev_priv->panel_downclock_avail)
                return;
 
-       if (HAS_PCH_SPLIT(dev))
-               dpll_reg = PCH_DPLL(pipe);
+       if (HAS_PCH_SPLIT(dev)) {
+               int pipeconf_reg = PIPECONF(pipe);
+               u32 pipeconf;
+
+               return;
+
+               DRM_DEBUG_DRIVER("downclocking panel\n");
+               pipeconf = I915_READ(pipeconf_reg);
+               pipeconf |= PIPECONF_POWERSAVE;
+               I915_WRITE(pipeconf_reg, pipeconf);
+               POSTING_READ(pipeconf_reg);
+               return;
+       }
 
        /*
         * Since this is called by a timer, we should never get here in
         * the manual case.
         */
        if (!HAS_PIPE_CXSR(dev) && intel_crtc->lowfreq_avail) {
-               DRM_ERROR("downclocking LVDS\n");
+               DRM_DEBUG_DRIVER("downclocking panel\n");
 
                dpll |= DISPLAY_RATE_SELECT_FPA1;
                I915_WRITE(dpll_reg, dpll);
@@ -5097,7 +5146,7 @@ static void intel_decrease_pllclock(struct drm_crtc *crtc)
                intel_wait_for_vblank(dev, pipe);
                dpll = I915_READ(dpll_reg);
                if (!(dpll & DISPLAY_RATE_SELECT_FPA1))
-                       DRM_ERROR("failed to downclock LVDS!\n");
+                       DRM_ERROR("failed to downclock panel!\n");
        }
 
 }
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index feb0c23..d1cbad5 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -676,7 +676,8 @@ intel_dp_compute_m_n(int bpp,
 
 void
 intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
-                struct drm_display_mode *adjusted_mode)
+                struct drm_display_mode *adjusted_mode,
+                int reduced_pixel_clock)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_mode_config *mode_config = &dev->mode_config;
@@ -684,7 +685,8 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct 
drm_display_mode *mode,
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        int lane_count = 4, bpp = 24;
-       struct intel_dp_m_n m_n;
+       struct intel_dp_m_n m_n, reduced_m_n;
+       int pipe = intel_crtc->pipe;
 
        /*
         * Find the lane count in the intel_encoder private
@@ -711,24 +713,40 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct 
drm_display_mode *mode,
         * the number of bytes_per_pixel post-LUT, which we always
         * set up for 8-bits of R/G/B, or 3 bytes total.
         */
-       intel_dp_compute_m_n(bpp, lane_count,
-                            mode->clock, adjusted_mode->clock, &m_n);
+       intel_dp_compute_m_n(bpp, lane_count, mode->clock, adjusted_mode->clock,
+                            &m_n);
+       if (reduced_pixel_clock)
+               intel_dp_compute_m_n(bpp, lane_count, reduced_pixel_clock,
+                                    adjusted_mode->clock, &reduced_m_n);
 
        if (HAS_PCH_SPLIT(dev)) {
-               if (intel_crtc->pipe == 0) {
-                       I915_WRITE(TRANSA_DATA_M1,
-                                  ((m_n.tu - 1) << 
PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
-                                  m_n.gmch_m);
-                       I915_WRITE(TRANSA_DATA_N1, m_n.gmch_n);
-                       I915_WRITE(TRANSA_DP_LINK_M1, m_n.link_m);
-                       I915_WRITE(TRANSA_DP_LINK_N1, m_n.link_n);
-               } else {
-                       I915_WRITE(TRANSB_DATA_M1,
-                                  ((m_n.tu - 1) << 
PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
-                                  m_n.gmch_m);
-                       I915_WRITE(TRANSB_DATA_N1, m_n.gmch_n);
-                       I915_WRITE(TRANSB_DP_LINK_M1, m_n.link_m);
-                       I915_WRITE(TRANSB_DP_LINK_N1, m_n.link_n);
+               int trans_m1, trans_n1, trans_m2, trans_n2;
+               int trans_dp_m1, trans_dp_n1, trans_dp_m2, trans_dp_n2;
+
+               trans_m1 = TRANS_DATA_M1(pipe);
+               trans_n1 = TRANS_DATA_N1(pipe);
+               trans_m2 = TRANS_DATA_M2(pipe);
+               trans_n2 = TRANS_DATA_N2(pipe);
+
+               trans_dp_m1 = TRANS_DP_LINK_M1(pipe);
+               trans_dp_n1 = TRANS_DP_LINK_N1(pipe);
+               trans_dp_m2 = TRANS_DP_LINK_M2(pipe);
+               trans_dp_n2 = TRANS_DP_LINK_N2(pipe);
+
+               I915_WRITE(trans_m1,
+                          ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
+                          m_n.gmch_m);
+               I915_WRITE(trans_n1, m_n.gmch_n);
+               I915_WRITE(trans_dp_m1, m_n.link_m);
+               I915_WRITE(trans_dp_n1, m_n.link_n);
+
+               if (reduced_pixel_clock) {
+                       I915_WRITE(trans_m2,
+                                  ((reduced_m_n.tu - 1) << 
PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
+                                  reduced_m_n.gmch_m);
+                       I915_WRITE(trans_n2, reduced_m_n.gmch_n);
+                       I915_WRITE(trans_dp_m2, reduced_m_n.link_m);
+                       I915_WRITE(trans_dp_n2, reduced_m_n.link_n);
                }
        } else {
                if (intel_crtc->pipe == 0) {
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index d782ad9..a8313d2 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -242,7 +242,8 @@ extern bool intel_lvds_init(struct drm_device *dev);
 extern void intel_dp_init(struct drm_device *dev, int dp_reg);
 void
 intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
-                struct drm_display_mode *adjusted_mode);
+                struct drm_display_mode *adjusted_mode,
+                int reduced_pixel_clock);
 extern bool intel_dpd_is_edp(struct drm_device *dev);
 extern void intel_edp_link_config (struct intel_encoder *, int *, int *);
 extern bool intel_encoder_is_pch_edp(struct drm_encoder *encoder);
-- 
1.7.0.4

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

Reply via email to