To support USB type C alternate DP mode, the display driver needs to know the number of lanes required by the DP panel as well as number of lanes that can be supported by the type-C cable. Sometimes, the type-C cable may limit the bandwidth even if Panel can support more lanes. To address these scenarios, the display driver will start link training with max lanes, and if that fails, the driver falls back to x2 lanes; and repeats this procedure for all bandwidth/lane configurations. * Since link training is done before modeset only the port (and not pipe/planes) and its associated PLLs are enabled. * Once link training is done, the port and its PLLs are disabled; so that the subsequent modeset is not aware of these changes. * On DP hotplug: Directly start link training on the crtc associated with the DP encoder. * On Connected boot scenarios: When booted with an LFP and a DP, typically, BIOS brings up DP. In these cases, we disable the crtc first and then start upfront link training. The crtc is re-enabled as part of a subsequent modeset. * For BXT, ddi->enable/disable for DP only enable/disable audio codec and hence are not included in upfront link training sequence. * As of now, this is tested only on BXT A1 platform, on kernel 4.2-rc2. Signed-off-by: Durgadoss R <durgadoss.r@xxxxxxxxx> --- drivers/gpu/drm/i915/intel_display.c | 130 +++++++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_dp.c | 22 ++++++ drivers/gpu/drm/i915/intel_drv.h | 2 + 3 files changed, 154 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e629a1b..bbcb5fa 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -15638,3 +15638,133 @@ void intel_modeset_preclose(struct drm_device *dev, struct drm_file *file) spin_unlock_irq(&dev->event_lock); } } + +bool bxt_upfront_link_train(struct drm_device *dev, + struct intel_dp *intel_dp, struct intel_crtc *crtc) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); + struct intel_connector *connector = intel_dp->attached_connector; + struct intel_encoder *encoder = connector->encoder; + struct intel_shared_dpll *pll; + bool ret, found, valid_crtc = false; + + if (!connector || !encoder) { + DRM_DEBUG_KMS("dp connector/encoder is NULL\n"); + return false; + } + + /* If we already have a crtc, start link training directly */ + if (crtc) { + valid_crtc = true; + goto start_link_train; + } + + /* Find an unused crtc and use it for link training */ + for_each_intel_crtc(dev, crtc) { + if (intel_crtc_active(&crtc->base)) + continue; + + connector->new_encoder = encoder; + encoder->new_crtc = crtc; + encoder->base.crtc = &crtc->base; + + /* Make sure the new crtc will work with the encoder */ + if (drm_encoder_crtc_ok(&encoder->base, + &crtc->base)) { + found = true; + break; + } + } + + if (!found) { + DRM_ERROR("Could not find crtc for upfront link training\n"); + return false; + } + +start_link_train: + + DRM_ERROR("upfront link training on pipe:%c\n", + pipe_name(crtc->pipe)); + found = false; + + /* Initialize with Max Link rate & lane count supported by panel */ + intel_dp->link_bw = intel_dp->dpcd[DP_MAX_LINK_RATE]; + intel_dp->lane_count = intel_dp->dpcd[DP_MAX_LANE_COUNT] & + DP_MAX_LANE_COUNT_MASK; + + /* Selects the shared DPLL to use for this port */ + bxt_get_ddi_pll(dev_priv, dig_port->port, crtc->config); + pll = intel_crtc_to_shared_dpll(crtc); + + do { + /* Find port clock from link_bw */ + crtc->config->port_clock = + drm_dp_bw_code_to_link_rate(intel_dp->link_bw); + + ret = bxt_ddi_pll_select(crtc, crtc->config, encoder, false); + if (!ret) { + DRM_ERROR("Could not find PLL..exiting\n"); + goto exit; + } + + pll->config.crtc_mask = (1 << crtc->pipe); + pll->config.hw_state = crtc->config->dpll_hw_state; + + DRM_DEBUG_KMS("Using shared_dpll:%d\n", crtc->config->shared_dpll); + + /* Enable PLL followed by port */ + intel_enable_shared_dpll(crtc); + encoder->pre_enable(encoder); + + /* Check if link training passed; if so update lane count */ + if (intel_dp->train_set_valid) { + intel_dp->dpcd[DP_MAX_LANE_COUNT] &= + ~DP_MAX_LANE_COUNT_MASK; + intel_dp->dpcd[DP_MAX_LANE_COUNT] |= + intel_dp->lane_count & DP_MAX_LANE_COUNT_MASK; + + found = true; + } + + /* Disable port followed by PLL for next retry/clean up */ + encoder->post_disable(encoder); + intel_disable_shared_dpll(crtc); + + if (found) + goto exit; + + DRM_ERROR("upfront link training failed. lanes:%d bw:%d\n", + intel_dp->lane_count, intel_dp->link_bw); + + /* Go down to the next level and retry link training */ + if (intel_dp->lane_count == 4) { + intel_dp->lane_count = 2; + } else if (intel_dp->lane_count == 2) { + intel_dp->lane_count = 1; + } else if (intel_dp->link_bw == DP_LINK_BW_5_4) { + intel_dp->link_bw = DP_LINK_BW_2_7; + intel_dp->lane_count = 4; + } else if (intel_dp->link_bw == DP_LINK_BW_2_7) { + intel_dp->link_bw = DP_LINK_BW_1_62; + intel_dp->lane_count = 4; + } else { + /* Tried all combinations, so exit */ + break; + } + + } while (1); + +exit: + /* Clear local associations made */ + if (!valid_crtc) { + connector->new_encoder = NULL; + encoder->new_crtc = NULL; + encoder->base.crtc = NULL; + } + + if (found) + DRM_ERROR("upfront link training passed. lanes:%d bw:%d\n", + intel_dp->lane_count, intel_dp->link_bw); + return found; +} diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 45ab25e..e103b67 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -4784,8 +4784,10 @@ intel_dp_detect(struct drm_connector *connector, bool force) { struct intel_dp *intel_dp = intel_attached_dp(connector); struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + struct drm_crtc *crtc = intel_dig_port->base.base.crtc; struct intel_encoder *intel_encoder = &intel_dig_port->base; struct drm_device *dev = connector->dev; + struct intel_crtc *intel_crtc = NULL; enum drm_connector_status status; enum intel_display_power_domain power_domain; bool ret; @@ -4830,6 +4832,26 @@ intel_dp_detect(struct drm_connector *connector, bool force) if (intel_encoder->type != INTEL_OUTPUT_EDP) intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT; + + /* + * On hotplug cases, we call _upfront_link_train directly. + * In connected boot scenarios (boot with {MIPI/eDP} + DP), + * BIOS typically brings up DP. Hence, we disable the crtc + * to do _upfront_link_training. It gets re-enabled as part of + * subsequent modeset. + */ + if (IS_BROXTON(dev) && intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT) { + if (intel_encoder->connectors_active && crtc && crtc->enabled) { + intel_crtc = to_intel_crtc(crtc); + DRM_ERROR("Disabling crtc %c for upfront link training\n", + pipe_name(intel_crtc->pipe)); + intel_crtc_control(crtc, false); + } + ret = bxt_upfront_link_train(dev, intel_dp, intel_crtc); + if (!ret) + goto out; + } + status = connector_status_connected; /* Try to read the source of the interrupt */ diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 30f91cf..55d6e12 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -1134,6 +1134,8 @@ bool bxt_ddi_pll_select(struct intel_crtc *intel_crtc, struct intel_crtc_state *crtc_state, struct intel_encoder *intel_encoder, bool find_pll); +bool bxt_upfront_link_train(struct drm_device *dev, + struct intel_dp *intel_dp, struct intel_crtc *crtc); void bxt_enable_dc9(struct drm_i915_private *dev_priv); void bxt_disable_dc9(struct drm_i915_private *dev_priv); void skl_init_cdclk(struct drm_i915_private *dev_priv); -- 1.9.1 _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/intel-gfx