From patchwork Wed Apr 6 11:54:01 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: durgadoss.r@intel.com X-Patchwork-Id: 8760951 Return-Path: X-Original-To: patchwork-intel-gfx@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 210649F3D1 for ; Wed, 6 Apr 2016 11:13:46 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id D6A9520155 for ; Wed, 6 Apr 2016 11:13:44 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id 7648F2022A for ; Wed, 6 Apr 2016 11:13:43 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 150266E8CF; Wed, 6 Apr 2016 11:13:43 +0000 (UTC) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from mga14.intel.com (mga14.intel.com [192.55.52.115]) by gabe.freedesktop.org (Postfix) with ESMTP id 3FA806E8CF for ; Wed, 6 Apr 2016 11:13:42 +0000 (UTC) Received: from fmsmga004.fm.intel.com ([10.253.24.48]) by fmsmga103.fm.intel.com with ESMTP; 06 Apr 2016 04:13:42 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.24,447,1455004800"; d="scan'208";a="80073320" Received: from dr3-desktop.iind.intel.com ([10.223.26.125]) by fmsmga004.fm.intel.com with ESMTP; 06 Apr 2016 04:13:40 -0700 From: Durgadoss R To: intel-gfx@lists.freedesktop.org Date: Wed, 6 Apr 2016 17:24:01 +0530 Message-Id: <1459943641-12179-5-git-send-email-durgadoss.r@intel.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1459943641-12179-1-git-send-email-durgadoss.r@intel.com> References: <1459943641-12179-1-git-send-email-durgadoss.r@intel.com> Cc: ander.conselvan.de.oliveira@intel.com Subject: [Intel-gfx] [PATCHv3 4/4] drm/i915/dp: Enable Upfront link training for typeC DP support on BXT X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" X-Spam-Status: No, score=-5.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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. * 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 and then do upfront link training; and let the subsequent modeset re-enable the crtc. * All local changes made for upfront link training are reset to their previous values once it is done; so that the subsequent modeset is not aware of these changes. Changes since v2: * Rebased on top of latest dpll_mgr.c code and latest HPD related clean ups. * Corrected return values from upfront (Ander) * Corrected atomic locking for upfront in intel_dp.c (Ville) Changes since v1: * all pll related functions inside ddi.c Signed-off-by: Durgadoss R --- drivers/gpu/drm/i915/intel_ddi.c | 46 ++++++++++ drivers/gpu/drm/i915/intel_dp.c | 180 ++++++++++++++++++++++++++++++++++++++- drivers/gpu/drm/i915/intel_drv.h | 5 ++ 3 files changed, 229 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 1e08385..0add10a 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -2087,6 +2087,52 @@ intel_ddi_init_hdmi_connector(struct intel_digital_port *intel_dig_port) return connector; } +int intel_ddi_upfront_link_train(struct intel_dp *intel_dp, + struct intel_crtc *crtc) +{ + struct intel_connector *connector = intel_dp->attached_connector; + struct intel_encoder *encoder = connector->encoder; + struct intel_shared_dpll *pll = NULL; + uint8_t link_bw, lane_count; + + if (WARN_ON(!crtc)) + return -EINVAL; + + /* Initialize with Max Link rate & lane count supported by panel */ + link_bw = intel_dp->dpcd[DP_MAX_LINK_RATE]; + lane_count = drm_dp_max_lane_count(intel_dp->dpcd); + + do { + crtc->config->port_clock = drm_dp_bw_code_to_link_rate(link_bw); + crtc->config->lane_count = lane_count; + + pll = intel_get_shared_dpll(crtc, crtc->config, encoder); + if (!pll) { + DRM_ERROR("Could not get shared DPLL\n"); + return -EINVAL; + } + + /* Enable PLL followed by port */ + intel_enable_shared_dpll(crtc); + encoder->pre_enable(encoder); + + /* Check if link training passed; if so update DPCD */ + if (intel_dp->train_set_valid) + intel_dp_update_dpcd_params(intel_dp); + + /* Disable port followed by PLL for next retry/clean up */ + encoder->post_disable(encoder); + intel_disable_shared_dpll(crtc); + + } while (!intel_dp->train_set_valid && + intel_dp_get_link_retry_params(intel_dp, &lane_count, &link_bw)); + + DRM_DEBUG_KMS("Upfront link train %s: lanes:%d bw:%d\n", + intel_dp->train_set_valid ? "Passed" : "Failed", lane_count, link_bw); + + return intel_dp->train_set_valid ? 0 : -1; +} + void intel_ddi_init(struct drm_device *dev, enum port port) { struct drm_i915_private *dev_priv = dev->dev_private; diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index da0c3d2..b8a42b8 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1595,6 +1595,41 @@ void intel_dp_set_link_params(struct intel_dp *intel_dp, intel_dp->lane_count = pipe_config->lane_count; } +void intel_dp_update_dpcd_params(struct intel_dp *intel_dp) +{ + 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; + + intel_dp->dpcd[DP_MAX_LINK_RATE] = + drm_dp_link_rate_to_bw_code(intel_dp->link_rate); +} + +bool intel_dp_get_link_retry_params(struct intel_dp *intel_dp, + uint8_t *lane_count, uint8_t *link_bw) +{ + /* + * As per DP1.3 Spec, retry all link rates for a particular + * lane count value, before reducing number of lanes. + */ + if (*link_bw == DP_LINK_BW_5_4) { + *link_bw = DP_LINK_BW_2_7; + } else if (*link_bw == DP_LINK_BW_2_7) { + *link_bw = DP_LINK_BW_1_62; + } else if (*lane_count == 4) { + *lane_count = 2; + *link_bw = intel_dp_max_link_bw(intel_dp); + } else if (*lane_count == 2) { + *lane_count = 1; + *link_bw = intel_dp_max_link_bw(intel_dp); + } else { + /* Tried all combinations, so exit */ + return false; + } + + return true; +} + static void intel_dp_prepare(struct intel_encoder *encoder) { struct drm_device *dev = encoder->base.dev; @@ -4578,6 +4613,135 @@ intel_dp_unset_edid(struct intel_dp *intel_dp) intel_dp->has_audio = false; } +static struct intel_crtc_state *intel_dp_upfront_get_crtc_state( + struct intel_crtc *crtc, + struct drm_modeset_acquire_ctx *ctx) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_atomic_state *state; + struct intel_crtc_state *crtc_state; + + state = drm_atomic_state_alloc(dev); + if (!state) + return ERR_PTR(-ENOMEM); + + state->acquire_ctx = ctx; + + crtc_state = intel_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) { + drm_atomic_state_free(state); + state = NULL; + } + + return crtc_state; +} + +static int intel_dp_upfront_commit(struct intel_crtc *crtc, + struct drm_modeset_acquire_ctx *ctx) +{ + int ret; + struct intel_crtc_state *crtc_state; + enum pipe pipe = crtc->pipe; + + crtc_state = intel_dp_upfront_get_crtc_state(crtc, ctx); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + DRM_DEBUG_KMS("Disabling crtc %c for upfront link train\n", pipe_name(pipe)); + + crtc_state->base.active = false; + ret = drm_atomic_commit(crtc_state->base.state); + if (ret) { + drm_atomic_state_free(crtc_state->base.state); + crtc_state->base.state = NULL; + } + return ret; +} + +static int intel_dp_upfront_link_train(struct drm_connector *connector) +{ + struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + struct intel_encoder *intel_encoder = &intel_dig_port->base; + struct drm_device *dev = intel_encoder->base.dev; + struct drm_mode_config *config = &dev->mode_config; + struct drm_modeset_acquire_ctx ctx; + struct intel_crtc_state *crtc_state, *tmp_crtc_config; + struct intel_crtc *intel_crtc; + struct drm_crtc *crtc = NULL; + bool crtc_enabled = false; + int ret, status = 0; + + if (!IS_BROXTON(dev)) + return true; + + drm_modeset_acquire_init(&ctx, 0); +retry: + ret = drm_modeset_lock(&config->connection_mutex, &ctx); + if (ret) + goto exit_fail; + + if (connector->state->crtc) { + crtc = connector->state->crtc; + + ret = drm_modeset_lock(&crtc->mutex, &ctx); + if (ret) + goto exit_fail; + + crtc_enabled = true; + } else { + crtc = intel_get_unused_crtc(&intel_encoder->base, &ctx); + if (IS_ERR_OR_NULL(crtc)) { + ret = PTR_ERR_OR_ZERO(crtc); + DRM_DEBUG_KMS("No pipe available for upfront link train:%d\n", ret); + goto exit_fail; + } + intel_encoder->base.crtc = crtc; + } + + intel_crtc = to_intel_crtc(crtc); + DRM_DEBUG_KMS("Using pipe %c for upfront link training\n", + pipe_name(intel_crtc->pipe)); + + ret = drm_modeset_lock(&crtc->primary->mutex, &ctx); + if (ret) + goto exit_fail; + + if (crtc_enabled) { + ret = intel_dp_upfront_commit(intel_crtc, &ctx); + if (ret) + goto exit_fail; + } + + crtc_state = intel_dp_upfront_get_crtc_state(intel_crtc, &ctx); + if (IS_ERR(crtc_state)) { + ret = PTR_ERR(crtc_state); + goto exit_fail; + } + + /* Save the existing config */ + tmp_crtc_config = intel_crtc->config; + intel_crtc->config = crtc_state; + + if (HAS_DDI(dev)) + status = intel_ddi_upfront_link_train(intel_dp, intel_crtc); + /* Other platforms upfront link train call goes here..*/ + + /* Restore the saved config */ + intel_crtc->config = tmp_crtc_config; + intel_encoder->base.crtc = crtc_enabled ? crtc : NULL; + drm_atomic_state_free(crtc_state->base.state); + +exit_fail: + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry; + } + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + return status; +} + static void intel_dp_long_pulse(struct intel_connector *intel_connector) { @@ -4588,7 +4752,7 @@ intel_dp_long_pulse(struct intel_connector *intel_connector) struct drm_device *dev = connector->dev; enum drm_connector_status status; enum intel_display_power_domain power_domain; - bool ret; + bool ret, do_upfront_link_train; u8 sink_irq_vector; power_domain = intel_display_port_aux_power_domain(intel_encoder); @@ -4634,7 +4798,11 @@ intel_dp_long_pulse(struct intel_connector *intel_connector) drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); intel_dp_check_link_status(intel_dp); drm_modeset_unlock(&dev->mode_config.connection_mutex); - goto out; + /* + * If we are here, we have (re)read DPCD above and hence + * need to do upfront link train again. + */ + goto upfront; } /* @@ -4664,6 +4832,14 @@ intel_dp_long_pulse(struct intel_connector *intel_connector) DRM_DEBUG_DRIVER("CP or sink specific irq unhandled\n"); } +upfront: + /* Do not do upfront link train, if it is a compliance request */ + do_upfront_link_train = + intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT && + intel_dp->compliance_test_type != DP_TEST_LINK_TRAINING; + + if (do_upfront_link_train && intel_dp_upfront_link_train(connector)) + status = connector_status_disconnected; out: if (status != connector_status_connected) { intel_dp_unset_edid(intel_dp); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 3873af5..ecd5fd3 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -1077,6 +1077,8 @@ void intel_ddi_clock_get(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config); void intel_ddi_set_vc_payload_alloc(struct drm_crtc *crtc, bool state); uint32_t ddi_signal_levels(struct intel_dp *intel_dp); +int intel_ddi_upfront_link_train(struct intel_dp *intel_dp, + struct intel_crtc *crtc); /* intel_frontbuffer.c */ void intel_fb_obj_invalidate(struct drm_i915_gem_object *obj, @@ -1275,6 +1277,9 @@ bool intel_dp_init_connector(struct intel_digital_port *intel_dig_port, struct intel_connector *intel_connector); void intel_dp_set_link_params(struct intel_dp *intel_dp, const struct intel_crtc_state *pipe_config); +void intel_dp_update_dpcd_params(struct intel_dp *intel_dp); +bool intel_dp_get_link_retry_params(struct intel_dp *intel_dp, + uint8_t *lane_count, uint8_t *link_bw); void intel_dp_start_link_train(struct intel_dp *intel_dp); void intel_dp_stop_link_train(struct intel_dp *intel_dp); void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode);