commit 6c95bd921c3b740217c9b6dd50d41eea51bfe828 Author: Jesse Barnes Date: Mon Sep 27 11:56:06 2010 -0700 drm/i915/dp: add HPD wait function for use with eDP We need to wait until a given panel's HPD bit is set before trying to perform AUX transactions on it, otherwise they may fail. Signed-off-by: Jesse Barnes diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 52af211..316f0e8 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -786,9 +786,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; @@ -805,10 +866,16 @@ static bool ironlake_edp_panel_on (struct drm_device *dev) pp |= POWER_TARGET_ON; I915_WRITE(PCH_PP_CONTROL, pp); + 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); @@ -842,16 +909,23 @@ static void ironlake_edp_panel_off (struct drm_device *dev) POSTING_READ(PCH_PP_CONTROL); } -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) @@ -923,8 +997,11 @@ static void intel_dp_prepare(struct drm_encoder *encoder) if (is_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); @@ -937,8 +1014,10 @@ static void intel_dp_commit(struct drm_encoder *encoder) intel_dp_start_link_train(intel_dp); - if (is_edp(intel_dp)) - ironlake_edp_panel_on(dev); + if (is_edp(intel_dp)) { + ironlake_edp_panel_on(intel_dp); + ironlake_edp_panel_vdd_off(dev); + } intel_dp_complete_link_train(intel_dp); @@ -967,7 +1046,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)) - ironlake_edp_panel_on(dev); + ironlake_edp_panel_on(intel_dp); intel_dp_complete_link_train(intel_dp); if (is_edp(intel_dp)) ironlake_edp_backlight_on(dev); @@ -1435,7 +1514,7 @@ ironlake_dp_detect(struct drm_connector *connector) /* Panel needs power for AUX to work */ if (is_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,