commit 9ed2312c678f41b7922fe74425ab589fe2ca287b Author: Jesse Barnes Date: Fri Oct 1 12:38:08 2010 -0700 drm/i915/dp: wait for HPD bit to set before using main eDP link DP spec wants this, might help avoid some of the questionable delays in this code. Signed-off-by: Jesse Barnes diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index ae4abf7..53917d7 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -764,9 +764,70 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, } } +/* Wait for HPD bit to set before trying to use AUX channel */ +static void ironlake_edp_clear_hpd(struct intel_dp *intel_dp) +{ + struct drm_device *dev = intel_dp->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t output_reg = intel_dp->output_reg; + uint32_t hpd_reg; + uint32_t hpd_mask; + + switch (output_reg) { + case DP_A: + hpd_reg = DIGITAL_PORT_HOTPLUG_CNTRL; + hpd_mask = DIGITAL_PORTA_LONG_PULSE_DETECT_MASK | + DIGITAL_PORTA_SHORT_PULSE_DETECT_MASK; + break; + case PCH_DP_D: + hpd_reg = PCH_PORT_HOTPLUG; + hpd_mask = PORTD_HOTPLUG_LONG_DETECT | + PORTD_HOTPLUG_SHORT_DETECT; + break; + default: + DRM_ERROR("waiting for HPD on non-eDP panel\n"); + return; + } + + /* Ack any existing status */ + I915_WRITE(hpd_reg, I915_READ(hpd_reg)); +} + +/* Wait for HPD bit to set before trying to use AUX channel */ +static void ironlake_edp_wait_hpd(struct intel_dp *intel_dp) +{ + struct drm_device *dev = intel_dp->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t output_reg = intel_dp->output_reg; + uint32_t hpd_reg; + uint32_t hpd_mask; + + switch (output_reg) { + case DP_A: + hpd_reg = DIGITAL_PORT_HOTPLUG_CNTRL; + hpd_mask = DIGITAL_PORTA_LONG_PULSE_DETECT_MASK | + DIGITAL_PORTA_SHORT_PULSE_DETECT_MASK; + break; + case PCH_DP_D: + hpd_reg = PCH_PORT_HOTPLUG; + hpd_mask = PORTD_HOTPLUG_LONG_DETECT | + PORTD_HOTPLUG_SHORT_DETECT; + break; + default: + DRM_ERROR("waiting for HPD on non-eDP panel\n"); + return; + } + + if (wait_for(I915_READ(hpd_reg) & hpd_mask, 5000)) + WARN(1, "panel HPD active wait timed out: 0x%08x\n", + I915_READ(hpd_reg)); + I915_WRITE(hpd_reg, I915_READ(hpd_reg)); +} + /* Returns true if the panel was already on when called */ -static bool ironlake_edp_panel_on (struct drm_device *dev) +static bool ironlake_edp_panel_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; @@ -788,10 +849,16 @@ static bool ironlake_edp_panel_on (struct drm_device *dev) */ msleep(300); + ironlake_edp_clear_hpd(intel_dp); + if (wait_for(I915_READ(PCH_PP_STATUS) & PP_ON, 5000)) DRM_ERROR("panel on wait timed out: 0x%08x\n", I915_READ(PCH_PP_STATUS)); + /* If VDD was forced on, HPD will have already been set */ + if (!(pp & EDP_FORCE_VDD)) + ironlake_edp_wait_hpd(intel_dp); + pp &= ~(PANEL_UNLOCK_REGS); pp |= PANEL_POWER_RESET; /* restore panel reset bit */ I915_WRITE(PCH_PP_CONTROL, pp); @@ -830,16 +897,23 @@ static void ironlake_edp_panel_off (struct drm_device *dev) msleep(300); } -static void ironlake_edp_panel_vdd_on(struct drm_device *dev) +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 (I915_READ(PCH_PP_CONTROL) & EDP_FORCE_VDD) + return; + + ironlake_edp_clear_hpd(intel_dp); + pp = I915_READ(PCH_PP_CONTROL); pp |= EDP_FORCE_VDD; I915_WRITE(PCH_PP_CONTROL, pp); POSTING_READ(PCH_PP_CONTROL); - msleep(300); + + ironlake_edp_wait_hpd(intel_dp); } static void ironlake_edp_panel_vdd_off(struct drm_device *dev) @@ -911,8 +985,11 @@ static void intel_dp_prepare(struct drm_encoder *encoder) if (IS_eDP(intel_dp) || IS_PCH_eDP(intel_dp)) { ironlake_edp_panel_off(dev); ironlake_edp_backlight_off(dev); - ironlake_edp_panel_vdd_on(dev); - ironlake_edp_pll_on(encoder); + ironlake_edp_panel_vdd_on(intel_dp); + if (!IS_PCH_eDP(intel_dp)) + ironlake_edp_pll_on(encoder); + else + ironlake_edp_pll_off(encoder); } if (dp_reg & DP_PORT_EN) intel_dp_link_down(intel_dp); @@ -926,7 +1003,7 @@ static void intel_dp_commit(struct drm_encoder *encoder) intel_dp_start_link_train(intel_dp); if (IS_eDP(intel_dp) || IS_PCH_eDP(intel_dp)) - ironlake_edp_panel_on(dev); + ironlake_edp_panel_on(intel_dp); intel_dp_complete_link_train(intel_dp); @@ -955,7 +1032,7 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode) if (!(dp_reg & DP_PORT_EN)) { intel_dp_start_link_train(intel_dp); if (IS_eDP(intel_dp) || IS_PCH_eDP(intel_dp)) - ironlake_edp_panel_on(dev); + ironlake_edp_panel_on(intel_dp); intel_dp_complete_link_train(intel_dp); if (IS_eDP(intel_dp) || IS_PCH_eDP(intel_dp)) ironlake_edp_backlight_on(dev); @@ -1423,7 +1500,7 @@ ironlake_dp_detect(struct drm_connector *connector) /* Panel needs power for AUX to work */ if (IS_eDP(intel_dp) || IS_PCH_eDP(intel_dp)) - ironlake_edp_panel_vdd_on(connector->dev); + ironlake_edp_panel_vdd_on(intel_dp); status = connector_status_disconnected; if (intel_dp_aux_native_read(intel_dp, 0x000, intel_dp->dpcd,