drm/i915: use VDD AUX override to make panel power sequencing look better
authorJesse Barnes <jbarnes@virtuousgeek.org>
Tue, 25 Jan 2011 01:10:54 +0000 (17:10 -0800)
committerChris Wilson <chris@chris-wilson.co.uk>
Tue, 25 Jan 2011 11:22:11 +0000 (11:22 +0000)
Rather than power cycling the panel when there are no bits to display,
use the VDD AUX bit to power the panel up just enough for DP AUX
transactions to work.  This prevents a bit of unnecessary ugliness as
mode sets occur on the panel.

Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/intel_dp.c

index 33b5134fc017d4f0607224dd24b238c30d3ffabd..a39f8d254502db348830dad896b75666484c0761 100644 (file)
@@ -655,6 +655,7 @@ typedef struct drm_i915_private {
        unsigned int lvds_border_bits;
        /* Panel fitter placement and size for Ironlake+ */
        u32 pch_pf_pos, pch_pf_size;
+       int panel_t3, panel_t12;
 
        struct drm_crtc *plane_to_crtc_mapping[2];
        struct drm_crtc *pipe_to_crtc_mapping[2];
index ad6925ea2b5130df23585e5156e26350a8bd5d96..ac261155b2f754c7daba7d32c54fe8226a11cf93 100644 (file)
@@ -813,6 +813,40 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
        }
 }
 
+static void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp)
+{
+       struct drm_device *dev = intel_dp->base.base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 pp;
+
+       /*
+        * If the panel wasn't on, make sure there's not a currently
+        * active PP sequence before enabling AUX VDD.
+        */
+       if (!(I915_READ(PCH_PP_STATUS) & PP_ON))
+               msleep(dev_priv->panel_t3);
+
+       pp = I915_READ(PCH_PP_CONTROL);
+       pp |= EDP_FORCE_VDD;
+       I915_WRITE(PCH_PP_CONTROL, pp);
+       POSTING_READ(PCH_PP_CONTROL);
+}
+
+static void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp)
+{
+       struct drm_device *dev = intel_dp->base.base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 pp;
+
+       pp = I915_READ(PCH_PP_CONTROL);
+       pp &= ~EDP_FORCE_VDD;
+       I915_WRITE(PCH_PP_CONTROL, pp);
+       POSTING_READ(PCH_PP_CONTROL);
+
+       /* Make sure sequencer is idle before allowing subsequent activity */
+       msleep(dev_priv->panel_t12);
+}
+
 /* Returns true if the panel was already on when called */
 static bool ironlake_edp_panel_on (struct intel_dp *intel_dp)
 {
@@ -935,7 +969,7 @@ static void intel_dp_prepare(struct drm_encoder *encoder)
 
        if (is_edp(intel_dp)) {
                ironlake_edp_backlight_off(dev);
-               ironlake_edp_panel_on(intel_dp);
+               ironlake_edp_panel_off(dev);
                if (!is_pch_edp(intel_dp))
                        ironlake_edp_pll_on(encoder);
                else
@@ -949,10 +983,15 @@ static void intel_dp_commit(struct drm_encoder *encoder)
        struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
        struct drm_device *dev = encoder->dev;
 
+       if (is_edp(intel_dp))
+               ironlake_edp_panel_vdd_on(intel_dp);
+
        intel_dp_start_link_train(intel_dp);
 
-       if (is_edp(intel_dp))
+       if (is_edp(intel_dp)) {
                ironlake_edp_panel_on(intel_dp);
+               ironlake_edp_panel_vdd_off(intel_dp);
+       }
 
        intel_dp_complete_link_train(intel_dp);
 
@@ -978,9 +1017,13 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode)
                        ironlake_edp_pll_off(encoder);
        } else {
                if (is_edp(intel_dp))
-                       ironlake_edp_panel_on(intel_dp);
+                       ironlake_edp_panel_vdd_on(intel_dp);
                if (!(dp_reg & DP_PORT_EN)) {
                        intel_dp_start_link_train(intel_dp);
+                       if (is_edp(intel_dp)) {
+                               ironlake_edp_panel_on(intel_dp);
+                               ironlake_edp_panel_vdd_off(intel_dp);
+                       }
                        intel_dp_complete_link_train(intel_dp);
                }
                if (is_edp(intel_dp))
@@ -1872,9 +1915,18 @@ intel_dp_init(struct drm_device *dev, int output_reg)
        /* Cache some DPCD data in the eDP case */
        if (is_edp(intel_dp)) {
                int ret;
-               bool was_on;
+               u32 pp_on, pp_div;
+
+               pp_on = I915_READ(PCH_PP_ON_DELAYS);
+               pp_div = I915_READ(PCH_PP_DIVISOR);
 
-               was_on = ironlake_edp_panel_on(intel_dp);
+               /* Get T3 & T12 values (note: VESA not bspec terminology) */
+               dev_priv->panel_t3 = (pp_on & 0x1fff0000) >> 16;
+               dev_priv->panel_t3 /= 10; /* t3 in 100us units */
+               dev_priv->panel_t12 = pp_div & 0xf;
+               dev_priv->panel_t12 *= 100; /* t12 in 100ms units */
+
+               ironlake_edp_panel_vdd_on(intel_dp);
                ret = intel_dp_aux_native_read(intel_dp, DP_DPCD_REV,
                                               intel_dp->dpcd,
                                               sizeof(intel_dp->dpcd));
@@ -1885,8 +1937,7 @@ intel_dp_init(struct drm_device *dev, int output_reg)
                } else {
                        DRM_ERROR("failed to retrieve link info\n");
                }
-               if (!was_on)
-                       ironlake_edp_panel_off(dev);
+               ironlake_edp_panel_vdd_off(intel_dp);
        }
 
        intel_encoder->hot_plug = intel_dp_hot_plug;