<p dir="ltr"><br>
On 11 Dec 2015 7:09 PM, "Durgadoss R" <<a href="mailto:durgadoss.r@intel.com">durgadoss.r@intel.com</a>> wrote:<br>
><br>
> To support USB type C alternate DP mode, the display driver needs to<br>
> know the number of lanes required by the DP panel as well as number<br>
> of lanes that can be supported by the type-C cable. Sometimes, the<br>
> type-C cable may limit the bandwidth even if Panel can support<br>
> more lanes. To address these scenarios, the display driver will<br>
> start link training with max lanes, and if that fails, the driver<br>
> falls back to x2 lanes; and repeats this procedure for all<br>
> bandwidth/lane configurations.<br>
><br>
> * Since link training is done before modeset only the port<br>
>   (and not pipe/planes) and its associated PLLs are enabled.<br>
> * On DP hotplug: Directly start link training on the crtc<br>
>   associated with the DP encoder.<br>
> * On Connected boot scenarios: When booted with an LFP and a DP,<br>
>   typically, BIOS brings up DP. In these cases, we disable the<br>
>   crtc, do upfront link training and then re-enable it back.<br>
> * All local changes made for upfront link training are reset<br>
>   to their previous values once it is done; so that the<br>
>   subsequent modeset is not aware of these changes.</p>
<p dir="ltr">This is just an aside but do we know if other OSes just always link train at plug time.</p>
<p dir="ltr">I do wonder if we should make an OS level decision to require driver do this always.</p>
<p dir="ltr">Dave.</p>
<p dir="ltr">><br>
> Signed-off-by: Durgadoss R <<a href="mailto:durgadoss.r@intel.com">durgadoss.r@intel.com</a>><br>
> ---<br>
>  drivers/gpu/drm/i915/intel_ddi.c | 81 ++++++++++++++++++++++++++++++++++++++++<br>
>  drivers/gpu/drm/i915/intel_dp.c  | 77 +++++++++++++++++++++++++++++++++++++-<br>
>  drivers/gpu/drm/i915/intel_drv.h |  2 +<br>
>  3 files changed, 159 insertions(+), 1 deletion(-)<br>
><br>
> diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c<br>
> index 632044a..43efc26 100644<br>
> --- a/drivers/gpu/drm/i915/intel_ddi.c<br>
> +++ b/drivers/gpu/drm/i915/intel_ddi.c<br>
> @@ -3274,6 +3274,87 @@ intel_ddi_init_hdmi_connector(struct intel_digital_port *intel_dig_port)<br>
>         return connector;<br>
>  }<br>
><br>
> +bool intel_ddi_upfront_link_train(struct intel_dp *intel_dp,<br>
> +                               struct intel_crtc *crtc)<br>
> +{<br>
> +       struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);<br>
> +       struct intel_connector *connector = intel_dp->attached_connector;<br>
> +       struct intel_encoder *encoder = connector->encoder;<br>
> +       struct drm_device *dev = encoder->base.dev;<br>
> +       struct drm_i915_private *dev_priv = dev->dev_private;<br>
> +       struct intel_shared_dpll *pll;<br>
> +       struct drm_crtc *drm_crtc = NULL;<br>
> +       struct intel_crtc_state *tmp_crtc_config;<br>
> +       struct intel_dpll_hw_state tmp_dpll_hw_state;<br>
> +       uint8_t link_bw, lane_count;<br>
> +<br>
> +       if (!crtc) {<br>
> +               drm_crtc = intel_get_unused_crtc(&encoder->base);<br>
> +               if (!drm_crtc) {<br>
> +                       DRM_ERROR("No crtc for upfront link training\n");<br>
> +                       return false;<br>
> +               }<br>
> +               encoder->base.crtc = drm_crtc;<br>
> +               crtc = to_intel_crtc(drm_crtc);<br>
> +       }<br>
> +<br>
> +       /* Initialize with Max Link rate & lane count supported by panel */<br>
> +       link_bw =  intel_dp->dpcd[DP_MAX_LINK_RATE];<br>
> +       lane_count = drm_dp_max_lane_count(intel_dp->dpcd);<br>
> +<br>
> +       /* Save the crtc->config */<br>
> +       tmp_crtc_config = crtc->config;<br>
> +       tmp_dpll_hw_state = crtc->config->dpll_hw_state;<br>
> +<br>
> +       /* Select the shared DPLL to use for this port */<br>
> +       intel_get_ddi_pll(dev_priv, dig_port->port, crtc->config);<br>
> +       pll = intel_crtc_to_shared_dpll(crtc);<br>
> +       if (!pll) {<br>
> +               DRM_ERROR("Could not get shared DPLL\n");<br>
> +               goto exit;<br>
> +       }<br>
> +       DRM_DEBUG_KMS("Using %s for pipe %c\n", pll->name, pipe_name(crtc->pipe));<br>
> +<br>
> +       do {<br>
> +               crtc->config->port_clock = drm_dp_bw_code_to_link_rate(link_bw);<br>
> +               crtc->config->lane_count = lane_count;<br>
> +               if (!intel_ddi_pll_select(crtc, crtc->config, encoder, false))<br>
> +                       goto exit;<br>
> +<br>
> +               pll->config.crtc_mask |= (1 << crtc->pipe);<br>
> +               pll->config.hw_state = crtc->config->dpll_hw_state;<br>
> +<br>
> +               /* Enable PLL followed by port */<br>
> +               intel_enable_shared_dpll(crtc);<br>
> +               encoder->pre_enable(encoder);<br>
> +<br>
> +               /* Check if link training passed; if so update DPCD */<br>
> +               if (intel_dp->train_set_valid)<br>
> +                       intel_dp_update_dpcd_params(intel_dp);<br>
> +<br>
> +               /* Disable port followed by PLL for next retry/clean up */<br>
> +               encoder->post_disable(encoder);<br>
> +               intel_disable_shared_dpll(crtc);<br>
> +<br>
> +       } while (!intel_dp->train_set_valid &&<br>
> +               !intel_dp_get_link_retry_params(&lane_count, &link_bw));<br>
> +<br>
> +       /* Reset pll state as before */<br>
> +       pll->config.crtc_mask &= ~(1 << crtc->pipe);<br>
> +       pll->config.hw_state = tmp_dpll_hw_state;<br>
> +<br>
> +exit:<br>
> +       /* Reset local associations made */<br>
> +       if (drm_crtc)<br>
> +               encoder->base.crtc = NULL;<br>
> +       crtc->config = tmp_crtc_config;<br>
> +<br>
> +       DRM_DEBUG_KMS("Upfront link train %s: lanes:%d bw:%d\n",<br>
> +       intel_dp->train_set_valid ? "Passed" : "Failed", lane_count, link_bw);<br>
> +<br>
> +       return intel_dp->train_set_valid;<br>
> +}<br>
> +<br>
>  void intel_ddi_init(struct drm_device *dev, enum port port)<br>
>  {<br>
>         struct drm_i915_private *dev_priv = dev->dev_private;<br>
> diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c<br>
> index d8f7830..47b6266 100644<br>
> --- a/drivers/gpu/drm/i915/intel_dp.c<br>
> +++ b/drivers/gpu/drm/i915/intel_dp.c<br>
> @@ -4622,6 +4622,71 @@ intel_dp_unset_edid(struct intel_dp *intel_dp)<br>
>         intel_dp->has_audio = false;<br>
>  }<br>
><br>
> +static void intel_dp_upfront_dpms_off(struct drm_connector *connector,<br>
> +                               struct drm_modeset_acquire_ctx *ctx)<br>
> +{<br>
> +       struct intel_dp *intel_dp = intel_attached_dp(connector);<br>
> +       struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);<br>
> +       struct drm_crtc *crtc = intel_dig_port->base.base.crtc;<br>
> +       struct intel_load_detect_pipe tmp;<br>
> +<br>
> +       if (intel_get_load_detect_pipe(connector, NULL, &tmp, ctx)) {<br>
> +               crtc->acquire_ctx = ctx;<br>
> +               tmp.dpms_mode = DRM_MODE_DPMS_OFF;<br>
> +               intel_release_load_detect_pipe(connector, &tmp, ctx);<br>
> +       }<br>
> +}<br>
> +<br>
> +static void intel_dp_upfront_dpms_on(struct drm_connector *connector,<br>
> +                               struct drm_modeset_acquire_ctx *ctx)<br>
> +{<br>
> +       struct intel_load_detect_pipe tmp;<br>
> +<br>
> +       intel_get_load_detect_pipe(connector, NULL, &tmp, ctx);<br>
> +<br>
> +       drm_modeset_drop_locks(ctx);<br>
> +       drm_modeset_acquire_fini(ctx);<br>
> +}<br>
> +<br>
> +static bool intel_dp_upfront_link_train(struct drm_connector *connector)<br>
> +{<br>
> +       struct intel_dp *intel_dp = intel_attached_dp(connector);<br>
> +       struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);<br>
> +       struct intel_encoder *intel_encoder = &intel_dig_port->base;<br>
> +       struct drm_device *dev = intel_encoder->base.dev;<br>
> +       struct drm_crtc *crtc = intel_dig_port->base.base.crtc;<br>
> +       struct intel_crtc *intel_crtc = crtc ? to_intel_crtc(crtc) : NULL;<br>
> +       struct drm_modeset_acquire_ctx ctx, *old_ctx = NULL;<br>
> +       bool ret = true, need_dpms_on = false;<br>
> +<br>
> +       if (!IS_BROXTON(dev))<br>
> +               return true;<br>
> +       /*<br>
> +        * On hotplug cases, we call _upfront_link_train directly.<br>
> +        * In connected boot scenarios (boot with {MIPI/eDP} + DP),<br>
> +        * BIOS typically brings up DP. Hence, we disable the crtc<br>
> +        * to do _upfront_link_training and then re-enable it back.<br>
> +        */<br>
> +       if (crtc && crtc->enabled && intel_crtc->active) {<br>
> +               DRM_DEBUG_KMS("Disabling pipe %c\n", pipe_name(intel_crtc->pipe));<br>
> +               old_ctx = crtc->acquire_ctx;<br>
> +               drm_modeset_acquire_init(&ctx, 0);<br>
> +               intel_dp_upfront_dpms_off(connector, &ctx);<br>
> +               need_dpms_on = true;<br>
> +       }<br>
> +<br>
> +       if (HAS_DDI(dev))<br>
> +               ret = intel_ddi_upfront_link_train(intel_dp, intel_crtc);<br>
> +               /* Other platforms upfront link train call goes here..*/<br>
> +<br>
> +       if (need_dpms_on) {<br>
> +               intel_dp_upfront_dpms_on(connector, &ctx);<br>
> +               crtc->acquire_ctx = old_ctx;<br>
> +       }<br>
> +       return ret;<br>
> +}<br>
> +<br>
> +<br>
>  static enum drm_connector_status<br>
>  intel_dp_detect(struct drm_connector *connector, bool force)<br>
>  {<br>
> @@ -4631,7 +4696,7 @@ intel_dp_detect(struct drm_connector *connector, bool force)<br>
>         struct drm_device *dev = connector->dev;<br>
>         enum drm_connector_status status;<br>
>         enum intel_display_power_domain power_domain;<br>
> -       bool ret;<br>
> +       bool ret, do_upfront_link_train;<br>
>         u8 sink_irq_vector;<br>
><br>
>         DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",<br>
> @@ -4705,6 +4770,16 @@ intel_dp_detect(struct drm_connector *connector, bool force)<br>
>                         DRM_DEBUG_DRIVER("CP or sink specific irq unhandled\n");<br>
>         }<br>
><br>
> +       /* Do not do upfront link train, if it is a compliance request */<br>
> +       do_upfront_link_train =<br>
> +               intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT &&<br>
> +               intel_dp->compliance_test_type != DP_TEST_LINK_TRAINING;<br>
> +<br>
> +       if (do_upfront_link_train) {<br>
> +               ret = intel_dp_upfront_link_train(connector);<br>
> +               if (!ret)<br>
> +                       status = connector_status_disconnected;<br>
> +       }<br>
>  out:<br>
>         intel_display_power_put(to_i915(dev), power_domain);<br>
>         return status;<br>
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h<br>
> index 5912955..3cfab20 100644<br>
> --- a/drivers/gpu/drm/i915/intel_drv.h<br>
> +++ b/drivers/gpu/drm/i915/intel_drv.h<br>
> @@ -1025,6 +1025,8 @@ void intel_ddi_clock_get(struct intel_encoder *encoder,<br>
>                          struct intel_crtc_state *pipe_config);<br>
>  void intel_ddi_set_vc_payload_alloc(struct drm_crtc *crtc, bool state);<br>
>  uint32_t ddi_signal_levels(struct intel_dp *intel_dp);<br>
> +bool intel_ddi_upfront_link_train(struct intel_dp *intel_dp,<br>
> +                               struct intel_crtc *crtc);<br>
><br>
>  /* intel_frontbuffer.c */<br>
>  void intel_fb_obj_invalidate(struct drm_i915_gem_object *obj,<br>
> --<br>
> 1.9.1<br>
><br>
> _______________________________________________<br>
> Intel-gfx mailing list<br>
> <a href="mailto:Intel-gfx@lists.freedesktop.org">Intel-gfx@lists.freedesktop.org</a><br>
> <a href="http://lists.freedesktop.org/mailman/listinfo/intel-gfx">http://lists.freedesktop.org/mailman/listinfo/intel-gfx</a><br>
</p>