From patchwork Wed Apr 7 08:15:58 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zhenyu Wang X-Patchwork-Id: 90971 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by demeter.kernel.org (8.14.3/8.14.3) with ESMTP id o378GcVg024269 for ; Wed, 7 Apr 2010 08:17:18 GMT Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 4D1F09F059; Wed, 7 Apr 2010 01:16:15 -0700 (PDT) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from orsmga101.jf.intel.com (mga06.intel.com [134.134.136.21]) by gabe.freedesktop.org (Postfix) with ESMTP id 1DCB09EF89 for ; Wed, 7 Apr 2010 01:16:08 -0700 (PDT) Received: from orsmga002.jf.intel.com ([10.7.209.21]) by orsmga101.jf.intel.com with ESMTP; 07 Apr 2010 01:15:43 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="4.51,377,1267430400"; d="scan'208";a="507032476" Received: from ubuntu-hp.sh.intel.com (HELO localhost.localdomain) ([10.239.36.35]) by orsmga002.jf.intel.com with ESMTP; 07 Apr 2010 01:16:01 -0700 From: Zhenyu Wang To: eric@anholt.net Date: Wed, 7 Apr 2010 16:15:58 +0800 Message-Id: <1270628158-29056-8-git-send-email-zhenyuw@linux.intel.com> X-Mailer: git-send-email 1.6.3.3 In-Reply-To: <1270628158-29056-1-git-send-email-zhenyuw@linux.intel.com> References: <1270628158-29056-1-git-send-email-zhenyuw@linux.intel.com> Cc: intel-gfx@lists.freedesktop.org Subject: [Intel-gfx] [PATCH 7/7] drm/i915: enable DP/eDP for Sandybridge/Cougarpoint X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.11 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: intel-gfx-bounces@lists.freedesktop.org Errors-To: intel-gfx-bounces@lists.freedesktop.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Wed, 07 Apr 2010 08:17:18 +0000 (UTC) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 3af745f..f329b97 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -999,6 +999,7 @@ extern void i8xx_disable_fbc(struct drm_device *dev); extern void g4x_disable_fbc(struct drm_device *dev); extern void intel_detect_pch (struct drm_device *dev); +extern int intel_trans_dp_port_sel (struct drm_crtc *crtc); /** * Lock test for when it's just for synchronization of ring access. diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d658d6e..4d46133 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1876,6 +1876,39 @@ static void ironlake_crtc_dpms(struct drm_crtc *crtc, int mode) /* wait one idle pattern time */ udelay(100); + /* For PCH DP, enable TRANS_DP_CTL */ + if (HAS_PCH_CPT(dev) && + intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) { + int trans_dp_ctl = (pipe == 0) ? TRANS_DP_CTL_A : TRANS_DP_CTL_B; + int reg; + + reg = I915_READ(trans_dp_ctl); + reg &= ~TRANS_DP_PORT_SEL_MASK; + reg = TRANS_DP_OUTPUT_ENABLE | + TRANS_DP_ENH_FRAMING | + TRANS_DP_VSYNC_ACTIVE_HIGH | + TRANS_DP_HSYNC_ACTIVE_HIGH; + + switch (intel_trans_dp_port_sel(crtc)) { + case PCH_DP_B: + reg |= TRANS_DP_PORT_SEL_B; + break; + case PCH_DP_C: + reg |= TRANS_DP_PORT_SEL_C; + break; + case PCH_DP_D: + reg |= TRANS_DP_PORT_SEL_D; + break; + default: + DRM_DEBUG_KMS("Wrong PCH DP port return. Guess port B\n"); + reg |= TRANS_DP_PORT_SEL_B; + break; + } + + I915_WRITE(trans_dp_ctl, reg); + POSTING_READ(trans_dp_ctl); + } + /* enable PCH transcoder */ temp = I915_READ(transconf_reg); /* @@ -2013,6 +2046,14 @@ static void ironlake_crtc_dpms(struct drm_crtc *crtc, int mode) udelay(100); if (HAS_PCH_CPT(dev)) { + /* disable TRANS_DP_CTL */ + int trans_dp_ctl = (pipe == 0) ? TRANS_DP_CTL_A : TRANS_DP_CTL_B; + int reg; + + reg = I915_READ(trans_dp_ctl); + reg &= ~(TRANS_DP_OUTPUT_ENABLE | TRANS_DP_PORT_SEL_MASK); + I915_WRITE(trans_dp_ctl, reg); + POSTING_READ(trans_dp_ctl); /* disable DPLL_SEL */ temp = I915_READ(PCH_DPLL_SEL); diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 0e15503..3e71200 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -222,19 +222,27 @@ intel_dp_aux_ch(struct intel_encoder *intel_encoder, uint32_t ctl; uint32_t status; uint32_t aux_clock_divider; - int try; + int try, precharge; /* The clock divider is based off the hrawclk, * and would like to run at 2MHz. So, take the * hrawclk value and divide by 2 and use that */ - if (IS_eDP(intel_encoder)) - aux_clock_divider = 225; /* eDP input clock at 450Mhz */ - else if (HAS_PCH_SPLIT(dev)) + if (IS_eDP(intel_encoder)) { + if (IS_GEN6(dev)) + aux_clock_divider = 200; /* SNB eDP input clock at 400Mhz */ + else + aux_clock_divider = 225; /* eDP input clock at 450Mhz */ + } else if (HAS_PCH_SPLIT(dev)) aux_clock_divider = 62; /* IRL input clock fixed at 125Mhz */ else aux_clock_divider = intel_hrawclk(dev) / 2; + if (IS_GEN6(dev)) + precharge = 3; + else + precharge = 5; + /* Must try at least 3 times according to DP spec */ for (try = 0; try < 5; try++) { /* Load the send data into the aux channel data registers */ @@ -247,7 +255,7 @@ intel_dp_aux_ch(struct intel_encoder *intel_encoder, ctl = (DP_AUX_CH_CTL_SEND_BUSY | DP_AUX_CH_CTL_TIME_OUT_400us | (send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) | - (5 << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) | + (precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) | (aux_clock_divider << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT) | DP_AUX_CH_CTL_DONE | DP_AUX_CH_CTL_TIME_OUT_ERROR | @@ -628,17 +636,22 @@ static void intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { + struct drm_device *dev = encoder->dev; struct intel_encoder *intel_encoder = enc_to_intel_encoder(encoder); struct intel_dp_priv *dp_priv = intel_encoder->dev_priv; struct drm_crtc *crtc = intel_encoder->enc.crtc; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - dp_priv->DP = (DP_LINK_TRAIN_OFF | - DP_VOLTAGE_0_4 | + dp_priv->DP = (DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0 | DP_SYNC_VS_HIGH | DP_SYNC_HS_HIGH); + if (HAS_PCH_CPT(dev) && !IS_eDP(intel_encoder)) + dp_priv->DP |= DP_LINK_TRAIN_OFF_CPT; + else + dp_priv->DP |= DP_LINK_TRAIN_OFF; + switch (dp_priv->lane_count) { case 1: dp_priv->DP |= DP_PORT_WIDTH_1; @@ -666,7 +679,8 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, dp_priv->DP |= DP_ENHANCED_FRAMING; } - if (intel_crtc->pipe == 1) + /* CPT DP's pipe select is decided in TRANS_DP_CTL */ + if (intel_crtc->pipe == 1 && !HAS_PCH_CPT(dev)) dp_priv->DP |= DP_PIPEB_SELECT; if (IS_eDP(intel_encoder)) { @@ -880,6 +894,58 @@ intel_dp_signal_levels(uint8_t train_set, int lane_count) return signal_levels; } +static int gen6_edp_levels[] = { + EDP_LINK_TRAIN_400MV_0DB_SNB_B, + EDP_LINK_TRAIN_400MV_6DB_SNB_B, + EDP_LINK_TRAIN_600MV_3_5DB_SNB_B, + EDP_LINK_TRAIN_800MV_0DB_SNB_B, +}; + +/* Gen6's DP voltage swing and pre-emphasis control */ +static uint32_t +intel_gen6_edp_signal_levels(uint8_t train_set) +{ + int v, p; + + switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) { + case DP_TRAIN_VOLTAGE_SWING_400: + default: + v = 0; + break; + case DP_TRAIN_VOLTAGE_SWING_600: + v = 0; + break; + case DP_TRAIN_VOLTAGE_SWING_800: + v = 3; + break; + case DP_TRAIN_VOLTAGE_SWING_1200: + v = 0; + DRM_DEBUG_KMS("Unsupported voltage swing 1200mV!\n"); + break; + } + switch (train_set & DP_TRAIN_PRE_EMPHASIS_MASK) { + case DP_TRAIN_PRE_EMPHASIS_0: + default: + p = 0; + break; + case DP_TRAIN_PRE_EMPHASIS_3_5: + p = 2; + break; + case DP_TRAIN_PRE_EMPHASIS_6: + p = 1; + break; + case DP_TRAIN_PRE_EMPHASIS_9_5: + p = 0; + DRM_DEBUG_KMS("Unsupported pre-emphasis 9.5dB\n"); + break; + } + if (v + p > 3) { + DRM_DEBUG_KMS("Unsupported voltage swing/pre-emphasis level\n"); + return gen6_edp_levels[0]; + } else + return gen6_edp_levels[v + p]; +} + static uint8_t intel_get_lane_status(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane) @@ -973,23 +1039,38 @@ intel_dp_link_train(struct intel_encoder *intel_encoder, uint32_t DP, bool channel_eq = false; bool first = true; int tries; + u32 reg; /* Write the link configuration data */ intel_dp_aux_native_write(intel_encoder, 0x100, link_configuration, DP_LINK_CONFIGURATION_SIZE); DP |= DP_PORT_EN; - DP &= ~DP_LINK_TRAIN_MASK; + if (HAS_PCH_CPT(dev) && !IS_eDP(intel_encoder)) + DP &= ~DP_LINK_TRAIN_MASK_CPT; + else + DP &= ~DP_LINK_TRAIN_MASK; memset(train_set, 0, 4); voltage = 0xff; tries = 0; clock_recovery = false; for (;;) { /* Use train_set[0] to set the voltage and pre emphasis values */ - uint32_t signal_levels = intel_dp_signal_levels(train_set[0], dp_priv->lane_count); - DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels; + uint32_t signal_levels; + if (IS_GEN6(dev) && IS_eDP(intel_encoder)) { + signal_levels = intel_gen6_edp_signal_levels(train_set[0]); + DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_SNB) | signal_levels; + } else { + signal_levels = intel_dp_signal_levels(train_set[0], dp_priv->lane_count); + DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels; + } - if (!intel_dp_set_link_train(intel_encoder, DP | DP_LINK_TRAIN_PAT_1, + if (HAS_PCH_CPT(dev) && !IS_eDP(intel_encoder)) + reg = DP | DP_LINK_TRAIN_PAT_1_CPT; + else + reg = DP | DP_LINK_TRAIN_PAT_1; + + if (!intel_dp_set_link_train(intel_encoder, reg, DP_TRAINING_PATTERN_1, train_set, first)) break; first = false; @@ -1029,11 +1110,23 @@ intel_dp_link_train(struct intel_encoder *intel_encoder, uint32_t DP, channel_eq = false; for (;;) { /* Use train_set[0] to set the voltage and pre emphasis values */ - uint32_t signal_levels = intel_dp_signal_levels(train_set[0], dp_priv->lane_count); - DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels; + uint32_t signal_levels; + + if (IS_GEN6(dev) && IS_eDP(intel_encoder)) { + signal_levels = intel_gen6_edp_signal_levels(train_set[0]); + DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_SNB) | signal_levels; + } else { + signal_levels = intel_dp_signal_levels(train_set[0], dp_priv->lane_count); + DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels; + } + + if (HAS_PCH_CPT(dev) && !IS_eDP(intel_encoder)) + reg = DP | DP_LINK_TRAIN_PAT_2_CPT; + else + reg = DP | DP_LINK_TRAIN_PAT_2; /* channel eq pattern */ - if (!intel_dp_set_link_train(intel_encoder, DP | DP_LINK_TRAIN_PAT_2, + if (!intel_dp_set_link_train(intel_encoder, reg, DP_TRAINING_PATTERN_2, train_set, false)) break; @@ -1056,7 +1149,12 @@ intel_dp_link_train(struct intel_encoder *intel_encoder, uint32_t DP, ++tries; } - I915_WRITE(dp_priv->output_reg, DP | DP_LINK_TRAIN_OFF); + if (HAS_PCH_CPT(dev) && !IS_eDP(intel_encoder)) + reg = DP | DP_LINK_TRAIN_OFF_CPT; + else + reg = DP | DP_LINK_TRAIN_OFF; + + I915_WRITE(dp_priv->output_reg, reg); POSTING_READ(dp_priv->output_reg); intel_dp_aux_native_write_1(intel_encoder, DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_DISABLE); @@ -1078,9 +1176,15 @@ intel_dp_link_down(struct intel_encoder *intel_encoder, uint32_t DP) udelay(100); } - DP &= ~DP_LINK_TRAIN_MASK; - I915_WRITE(dp_priv->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE); - POSTING_READ(dp_priv->output_reg); + if (HAS_PCH_CPT(dev) && !IS_eDP(intel_encoder)) { + DP &= ~DP_LINK_TRAIN_MASK_CPT; + I915_WRITE(dp_priv->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE_CPT); + POSTING_READ(dp_priv->output_reg); + } else { + DP &= ~DP_LINK_TRAIN_MASK; + I915_WRITE(dp_priv->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE); + POSTING_READ(dp_priv->output_reg); + } udelay(17000); @@ -1277,6 +1381,28 @@ intel_dp_hot_plug(struct intel_encoder *intel_encoder) intel_dp_check_link_status(intel_encoder); } +/* Return which DP Port should be selected for Transcoder DP control */ +int +intel_trans_dp_port_sel (struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_mode_config *mode_config = &dev->mode_config; + struct drm_encoder *encoder; + struct intel_encoder *intel_encoder = NULL; + + list_for_each_entry(encoder, &mode_config->encoder_list, head) { + if (!encoder || encoder->crtc != crtc) + continue; + + intel_encoder = enc_to_intel_encoder(encoder); + if (intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT) { + struct intel_dp_priv *dp_priv = intel_encoder->dev_priv; + return dp_priv->output_reg; + } + } + return -1; +} + void intel_dp_init(struct drm_device *dev, int output_reg) {