drm/i915: the PCH reference clocks are global, so don't clobber unconditionally
authorJesse Barnes <jbarnes@virtuousgeek.org>
Fri, 4 Feb 2011 21:57:30 +0000 (13:57 -0800)
committerChris Wilson <chris@chris-wilson.co.uk>
Mon, 7 Feb 2011 12:06:14 +0000 (12:06 +0000)
The PCH can drive several reference clocks simultaneously, and needs to
with multiple display configurations.  So we can't just clobber the
existing state everytime we set a mode, we need to take into account
what the other CRTCs are doing at the time.

Doing so fixes an issue where you'd lose the LVDS display at boot if you
had an LVDS+DP config.

[updated: init bools and check CRTC status correctly]
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
drivers/gpu/drm/i915/intel_display.c

index 84f3054a066f035ade356eba2134cef188b4ef2c..cc431f4581c3faba0327f82671b30d76bbe9accd 100644 (file)
@@ -4463,6 +4463,81 @@ static inline bool intel_panel_use_ssc(struct drm_i915_private *dev_priv)
        return dev_priv->lvds_use_ssc && i915_panel_use_ssc;
 }
 
+static void intel_update_dref(struct drm_i915_private *dev_priv)
+{
+       struct drm_device *dev = dev_priv->dev;
+       struct drm_mode_config *mode_config = &dev->mode_config;
+       struct intel_encoder *encoder;
+       struct drm_crtc *crtc;
+       u32 temp;
+       bool lvds_on = false, edp_on = false, pch_edp_on = false, other_on = false;
+
+       list_for_each_entry(encoder, &mode_config->encoder_list, base.head) {
+               crtc = encoder->base.crtc;
+
+               if (!crtc || !crtc->enabled)
+                       continue;
+
+               switch (encoder->type) {
+               case INTEL_OUTPUT_LVDS:
+                       lvds_on = true;
+                       break;
+               case INTEL_OUTPUT_EDP:
+                       edp_on = true;
+                       if (!pch_edp_on)
+                               pch_edp_on = intel_encoder_is_pch_edp(&encoder->base);
+                       break;
+               default:
+                       other_on = true;
+                       break;
+               }
+       }
+
+       /*XXX BIOS treats 16:31 as a mask for 0:15 */
+
+       temp = I915_READ(PCH_DREF_CONTROL);
+
+       /* First clear the current state for output switching */
+       temp &= ~DREF_SSC1_ENABLE;
+       temp &= ~DREF_SSC4_ENABLE;
+       temp &= ~DREF_SUPERSPREAD_SOURCE_MASK;
+       temp &= ~DREF_NONSPREAD_SOURCE_MASK;
+       temp &= ~DREF_SSC_SOURCE_MASK;
+       temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
+       I915_WRITE(PCH_DREF_CONTROL, temp);
+
+       POSTING_READ(PCH_DREF_CONTROL);
+       udelay(200);
+
+       if ((lvds_on || edp_on) && intel_panel_use_ssc(dev_priv)) {
+               temp |= DREF_SSC_SOURCE_ENABLE;
+               if (edp_on) {
+                       if (!pch_edp_on) {
+                               /* Enable CPU source on CPU attached eDP */
+                               temp |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD;
+                       } else {
+                               /* Enable SSC on PCH eDP if needed */
+                               temp |= DREF_SUPERSPREAD_SOURCE_ENABLE;
+                       }
+                       I915_WRITE(PCH_DREF_CONTROL, temp);
+               }
+               if (!dev_priv->display_clock_mode)
+                       temp |= DREF_SSC1_ENABLE;
+       }
+
+       if (other_on && dev_priv->display_clock_mode)
+               temp |= DREF_NONSPREAD_CK505_ENABLE;
+       else if (other_on) {
+               temp |= DREF_NONSPREAD_SOURCE_ENABLE;
+               if (edp_on && !pch_edp_on)
+                       temp |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD;
+       }
+
+       I915_WRITE(PCH_DREF_CONTROL, temp);
+       POSTING_READ(PCH_DREF_CONTROL);
+       udelay(200);
+}
+
 static int intel_crtc_mode_set(struct drm_crtc *crtc,
                               struct drm_display_mode *mode,
                               struct drm_display_mode *adjusted_mode,
@@ -4688,52 +4763,8 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
         * PCH B stepping, previous chipset stepping should be
         * ignoring this setting.
         */
-       if (HAS_PCH_SPLIT(dev)) {
-               /*XXX BIOS treats 16:31 as a mask for 0:15 */
-
-               temp = I915_READ(PCH_DREF_CONTROL);
-
-               /* First clear the current state for output switching */
-               temp &= ~DREF_SSC1_ENABLE;
-               temp &= ~DREF_SSC4_ENABLE;
-               temp &= ~DREF_SUPERSPREAD_SOURCE_MASK;
-               temp &= ~DREF_NONSPREAD_SOURCE_MASK;
-               temp &= ~DREF_SSC_SOURCE_MASK;
-               temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
-               I915_WRITE(PCH_DREF_CONTROL, temp);
-
-               POSTING_READ(PCH_DREF_CONTROL);
-               udelay(200);
-
-               if ((is_lvds || has_edp_encoder) &&
-                   intel_panel_use_ssc(dev_priv)) {
-                       temp |= DREF_SSC_SOURCE_ENABLE;
-                       if (has_edp_encoder) {
-                               if (!intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
-                                       /* Enable CPU source on CPU attached eDP */
-                                       temp |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD;
-                               } else {
-                                       /* Enable SSC on PCH eDP if needed */
-                                       temp |= DREF_SUPERSPREAD_SOURCE_ENABLE;
-                               }
-                               I915_WRITE(PCH_DREF_CONTROL, temp);
-                       }
-                       if (!dev_priv->display_clock_mode)
-                               temp |= DREF_SSC1_ENABLE;
-               } else {
-                       if (dev_priv->display_clock_mode)
-                               temp |= DREF_NONSPREAD_CK505_ENABLE;
-                       else
-                               temp |= DREF_NONSPREAD_SOURCE_ENABLE;
-                       if (has_edp_encoder &&
-                           !intel_encoder_is_pch_edp(&has_edp_encoder->base))
-                               temp |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD;
-               }
-
-               I915_WRITE(PCH_DREF_CONTROL, temp);
-               POSTING_READ(PCH_DREF_CONTROL);
-               udelay(200);
-       }
+       if (HAS_PCH_SPLIT(dev))
+               intel_update_dref(dev_priv);
 
        if (IS_PINEVIEW(dev)) {
                fp = (1 << clock.n) << 16 | clock.m1 << 8 | clock.m2;