diff mbox

[2/2] drm/i915/dp: Enable Upfront link training for typeC DP support on BXT

Message ID 1442313301-7053-3-git-send-email-durgadoss.r@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

durgadoss.r@intel.com Sept. 15, 2015, 10:35 a.m. UTC
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@intel.com>
---
 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 mbox

Patch

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);