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 33b5134..a39f8d2 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 ad6925e..ac26115 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;