From patchwork Wed Oct 14 12:00:12 2015 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: 7392311 Return-Path: X-Original-To: patchwork-intel-gfx@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 2B096BEEA4 for ; Wed, 14 Oct 2015 11:31:55 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 06ADB20788 for ; Wed, 14 Oct 2015 11:31:54 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id B67C920632 for ; Wed, 14 Oct 2015 11:31:52 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 0A7166EC2B; Wed, 14 Oct 2015 04:31:52 -0700 (PDT) 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 C304472183 for ; Wed, 14 Oct 2015 04:31:50 -0700 (PDT) Received: from orsmga003.jf.intel.com ([10.7.209.27]) by fmsmga103.fm.intel.com with ESMTP; 14 Oct 2015 04:31:50 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.17,681,1437462000"; d="scan'208";a="664106006" Received: from dr3-desktop.iind.intel.com ([10.223.26.125]) by orsmga003.jf.intel.com with ESMTP; 14 Oct 2015 04:31:49 -0700 From: Durgadoss R To: intel-gfx@lists.freedesktop.org Date: Wed, 14 Oct 2015 17:30:12 +0530 Message-Id: <1444824013-23147-6-git-send-email-durgadoss.r@intel.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1444824013-23147-1-git-send-email-durgadoss.r@intel.com> References: <1444824013-23147-1-git-send-email-durgadoss.r@intel.com> Subject: [Intel-gfx] [RFCv2 DP-typeC 5/6] 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=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, T_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. * 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 --- drivers/gpu/drm/i915/intel_ddi.c | 152 +++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_dp.c | 41 ++++++++++- drivers/gpu/drm/i915/intel_drv.h | 2 + 3 files changed, 194 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 8e4ea36..b3a9bff 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -3209,6 +3209,158 @@ intel_ddi_init_hdmi_connector(struct intel_digital_port *intel_dig_port) return connector; } +bool intel_ddi_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 *tmp_encoder, *encoder = connector->encoder; + struct intel_shared_dpll *pll; + struct intel_crtc *tmp_crtc; + struct drm_crtc *tmp_drm_crtc; + uint8_t tmp_lane_count, tmp_link_bw; + bool ret, found, valid_crtc = false; + + /* For now, we have only SKL and BXT supporting type-C */ + if (!IS_BROXTON(dev) || !IS_SKYLAKE(dev)) + return true; + + 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; + + /* Make sure the new crtc will work with the encoder */ + if (drm_encoder_crtc_ok(&encoder->base, &crtc->base)) { + found = true; + + /* Save the existing values */ + tmp_encoder = connector->new_encoder; + tmp_crtc = encoder->new_crtc; + tmp_drm_crtc = encoder->base.crtc; + + connector->new_encoder = encoder; + encoder->new_crtc = crtc; + encoder->base.crtc = &crtc->base; + + break; + } + } + + if (!found) { + DRM_ERROR("Could not find crtc for upfront link training\n"); + return false; + } + +start_link_train: + DRM_DEBUG_KMS("upfront link train on pipe:%c\n", pipe_name(crtc->pipe)); + found = false; + + /* Save the existing lane_count and link_bw values */ + tmp_lane_count = intel_dp->lane_count; + tmp_link_bw = intel_dp->link_bw; + + /* 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 */ + intel_get_ddi_pll(dev_priv, dig_port->port, crtc->config); + pll = intel_crtc_to_shared_dpll(crtc); + if (!pll) { + DRM_ERROR("Could not get shared DPLL\n"); + goto exit; + } + + do { + /* Find port clock from link_bw */ + crtc->config->port_clock = + drm_dp_bw_code_to_link_rate(intel_dp->link_bw); + + ret = intel_ddi_pll_select(crtc, crtc->config, encoder, false); + if (!ret) { + DRM_ERROR("Could not select PLL\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_DEBUG_KMS("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: + /* Restore local associations made */ + if (!valid_crtc) { + connector->new_encoder = tmp_encoder; + encoder->new_crtc = tmp_crtc; + encoder->base.crtc = tmp_drm_crtc; + } + + if (found) + DRM_DEBUG_KMS("upfront link training passed. lanes:%d bw:%d\n", + intel_dp->lane_count, intel_dp->link_bw); + /* Restore lane_count and link_bw values */ + intel_dp->lane_count = tmp_lane_count; + intel_dp->link_bw = tmp_link_bw; + + return found; +} + 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 18bcfbe..8376b47 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -4785,6 +4785,35 @@ intel_dp_power_put(struct intel_dp *dp, intel_display_power_put(to_i915(encoder->base.dev), power_domain); } +static bool intel_dp_upfront_link_train(struct intel_dp *intel_dp) +{ + 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 = intel_encoder->base.dev; + struct intel_crtc *intel_crtc = crtc ? to_intel_crtc(crtc) : NULL; + + /* + * 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 (intel_encoder->connectors_active && crtc && crtc->enabled) { + DRM_DEBUG_KMS("Disabling crtc %c for upfront link training\n", + pipe_name(intel_crtc->pipe)); + intel_crtc_control(crtc, false); + } + + if (HAS_DDI(dev)) + return intel_ddi_upfront_link_train(dev, intel_dp, intel_crtc); + + /* Not a supported platform. Assume we don't need upfront_train */ + return true; +} + + static enum drm_connector_status intel_dp_detect(struct drm_connector *connector, bool force) { @@ -4794,7 +4823,7 @@ intel_dp_detect(struct drm_connector *connector, bool force) 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; DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", @@ -4852,6 +4881,16 @@ intel_dp_detect(struct drm_connector *connector, bool force) DRM_DEBUG_DRIVER("CP or sink specific irq unhandled\n"); } + /* 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) { + ret = intel_dp_upfront_link_train(intel_dp); + if (!ret) + status = connector_status_disconnected; + } out: intel_dp_power_put(intel_dp, power_domain); return status; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 5bcdd37..82af4e6 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -1007,6 +1007,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); +bool intel_ddi_upfront_link_train(struct drm_device *dev, + struct intel_dp *intel_dp, struct intel_crtc *crtc); /* intel_frontbuffer.c */ void intel_fb_obj_invalidate(struct drm_i915_gem_object *obj,