[Intel-gfx] [PATCH 3/3] drm/i915/tgl: Select master trasconder for MST stream

José Roberto de Souza jose.souza at intel.com
Wed Nov 20 23:48:53 UTC 2019


On TGL the blending of all the streams have moved from DDI to
transcoder, so now every transcoder working over the same MST port must
send its stream to a master transcoder and master will send to DDI
respecting the time slots.

A previous approach was using the lowest pipe/transcoder as master
transcoder but as the comment in skl_commit_modeset_enables() states,
that is not always true.

So here promoting the first pipe/transcoder of the stream as master.
That caused several other problems as during the commit phase the
state computed should not be changed.

So the master transcoder is store into intel_dp and the modeset in
slave pipes/transcoders is forced using mst_master_trans_pending.

BSpec: 50493
BSpec: 49190
Cc: Ville Syrjälä <ville.syrjala at linux.intel.com>
Cc: Lucas De Marchi <lucas.demarchi at intel.com>
Signed-off-by: José Roberto de Souza <jose.souza at intel.com>
---
 drivers/gpu/drm/i915/display/intel_ddi.c      |   9 +-
 drivers/gpu/drm/i915/display/intel_display.c  |  58 +++++++++-
 .../drm/i915/display/intel_display_types.h    |   3 +
 drivers/gpu/drm/i915/display/intel_dp.c       |   1 +
 drivers/gpu/drm/i915/display/intel_dp_mst.c   | 109 +++++++++++++++++-
 drivers/gpu/drm/i915/display/intel_dp_mst.h   |   2 +
 6 files changed, 175 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c
index a976606d21c7..5d076c84f253 100644
--- a/drivers/gpu/drm/i915/display/intel_ddi.c
+++ b/drivers/gpu/drm/i915/display/intel_ddi.c
@@ -35,6 +35,7 @@
 #include "intel_display_types.h"
 #include "intel_dp.h"
 #include "intel_dp_link_training.h"
+#include "intel_dp_mst.h"
 #include "intel_dpio_phy.h"
 #include "intel_dsi.h"
 #include "intel_fifo_underrun.h"
@@ -1903,8 +1904,12 @@ intel_ddi_transcoder_func_reg_val_get(const struct intel_crtc_state *crtc_state)
 		temp |= TRANS_DDI_MODE_SELECT_DP_MST;
 		temp |= DDI_PORT_WIDTH(crtc_state->lane_count);
 
-		if (INTEL_GEN(dev_priv) >= 12)
-			temp |= TRANS_DDI_MST_TRANSPORT_SELECT(crtc_state->cpu_transcoder);
+		if (INTEL_GEN(dev_priv) >= 12) {
+			enum transcoder master;
+
+			master = intel_dp_mst_master_trans_get(encoder);
+			temp |= TRANS_DDI_MST_TRANSPORT_SELECT(master);
+		}
 	} else {
 		temp |= TRANS_DDI_MODE_SELECT_DP_SST;
 		temp |= DDI_PORT_WIDTH(crtc_state->lane_count);
diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
index 801b975c7d39..35a59108194e 100644
--- a/drivers/gpu/drm/i915/display/intel_display.c
+++ b/drivers/gpu/drm/i915/display/intel_display.c
@@ -46,6 +46,7 @@
 #include "display/intel_crt.h"
 #include "display/intel_ddi.h"
 #include "display/intel_dp.h"
+#include "display/intel_dp_mst.h"
 #include "display/intel_dsi.h"
 #include "display/intel_dvo.h"
 #include "display/intel_gmbus.h"
@@ -5365,6 +5366,36 @@ intel_get_crtc_new_encoder(const struct intel_atomic_state *state,
 	return encoder;
 }
 
+/*
+ * Finds the encoder associated with the given CRTC. This can only be
+ * used when we know that the CRTC isn't feeding multiple encoders!
+ */
+static struct intel_encoder *
+intel_get_crtc_old_encoder(const struct intel_atomic_state *state,
+			   const struct intel_crtc_state *crtc_state)
+{
+	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+	const struct drm_connector_state *connector_state;
+	const struct drm_connector *connector;
+	struct intel_encoder *encoder = NULL;
+	int num_encoders = 0;
+	int i;
+
+	for_each_old_connector_in_state(&state->base, connector,
+					connector_state, i) {
+		if (connector_state->crtc != &crtc->base)
+			continue;
+
+		encoder = to_intel_encoder(connector_state->best_encoder);
+		num_encoders++;
+	}
+
+	WARN(num_encoders != 1, "%d encoders for pipe %c\n",
+	     num_encoders, pipe_name(crtc->pipe));
+
+	return encoder;
+}
+
 /*
  * Enable PCH resources required for PCH ports:
  *   - PCH PLLs
@@ -13365,6 +13396,12 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config,
 #undef PIPE_CONF_CHECK_COLOR_LUT
 #undef PIPE_CONF_QUIRK
 
+	if (fastset && pipe_config->mst_master_trans_pending) {
+		DRM_DEBUG_KMS("[CRTC:%d:%s] fastset mismatch in mst_master_trans\n",
+			      crtc->base.base.id, crtc->base.name);
+		ret = false;
+	}
+
 	return ret;
 }
 
@@ -14449,22 +14486,35 @@ static void intel_commit_modeset_disables(struct intel_atomic_state *state)
 	struct intel_crtc *crtc;
 	int i;
 
-	/* Only disable port sync slaves */
+	/* Only disable port sync and MST slaves */
 	for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state,
 					    new_crtc_state, i) {
 		if (!needs_modeset(new_crtc_state) || !crtc->active)
 			continue;
 
+		if (!is_trans_port_sync_mode(new_crtc_state) &&
+		    !intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_DP_MST))
+			continue;
+
 		/* In case of Transcoder port Sync master slave CRTCs can be
 		 * assigned in any order and we need to make sure that
 		 * slave CRTCs are disabled first and then master CRTC since
 		 * Slave vblanks are masked till Master Vblanks.
 		 */
-		if (!is_trans_port_sync_mode(old_crtc_state))
-			continue;
-		if (is_trans_port_sync_master(old_crtc_state))
+		if (is_trans_port_sync_mode(new_crtc_state) &&
+		    is_trans_port_sync_master(new_crtc_state))
 			continue;
 
+		if (intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_DP_MST)) {
+			struct intel_encoder *encoder;
+
+			encoder = intel_get_crtc_old_encoder(state,
+							     old_crtc_state);
+			if (intel_dp_mst_master_trans_get(encoder) ==
+			    old_crtc_state->cpu_transcoder)
+				continue;
+		}
+
 		intel_pre_plane_update(old_crtc_state, new_crtc_state);
 		intel_old_crtc_state_disables(state, old_crtc_state,
 					      new_crtc_state, crtc);
diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h
index 83ea04149b77..23d747cdca64 100644
--- a/drivers/gpu/drm/i915/display/intel_display_types.h
+++ b/drivers/gpu/drm/i915/display/intel_display_types.h
@@ -1052,6 +1052,8 @@ struct intel_crtc_state {
 	/* Pointer to master transcoder in case of tiled displays */
 	enum transcoder master_transcoder;
 
+	bool mst_master_trans_pending;
+
 	/* Bitmask to indicate slaves attached */
 	u8 sync_mode_slaves_mask;
 };
@@ -1284,6 +1286,7 @@ struct intel_dp {
 	bool can_mst; /* this port supports mst */
 	bool is_mst;
 	int active_mst_links;
+	enum transcoder mst_master_transcoder; /* Only used in TGL+ */
 
 	/*
 	 * DP_TP_* registers may be either on port or transcoder register space.
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
index 3123958e2081..ceff6901451a 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.c
+++ b/drivers/gpu/drm/i915/display/intel_dp.c
@@ -7424,6 +7424,7 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
 	intel_dp->reset_link_params = true;
 	intel_dp->pps_pipe = INVALID_PIPE;
 	intel_dp->active_pipe = INVALID_PIPE;
+	intel_dp->mst_master_transcoder = INVALID_TRANSCODER;
 
 	/* Preserve the current hw state. */
 	intel_dp->DP = I915_READ(intel_dp->output_reg);
diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c
index f8a350359346..38739d565751 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_mst.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c
@@ -157,6 +157,95 @@ static int intel_dp_mst_compute_config(struct intel_encoder *encoder,
 	return 0;
 }
 
+static int
+intel_dp_mst_atomic_master_trans_check(struct drm_connector *connector,
+				       struct drm_atomic_state *state)
+{
+	struct drm_i915_private *dev_priv = to_i915(connector->dev);
+	struct intel_connector *intel_conn = to_intel_connector(connector);
+	struct drm_connector_state *new_conn_state, *old_conn_state;
+	struct drm_connector_list_iter conn_list_iter;
+	struct intel_crtc_state *intel_crtc_state;
+	struct drm_crtc_state *crtc_state;
+	struct drm_connector *conn_iter;
+
+	if (INTEL_GEN(dev_priv) < 12)
+		return 0;
+
+	new_conn_state = drm_atomic_get_new_connector_state(state, connector);
+	old_conn_state = drm_atomic_get_old_connector_state(state, connector);
+
+	if (!old_conn_state->crtc && !new_conn_state->crtc)
+		return 0;
+
+	/*
+	 * 3 cases that needs be handled here:
+	 * - connector going from disabled to enabled
+	 * - connector going from enabled to disabled:
+	 * if this transcoder was the master, all slaves needs a modeset
+	 * - connector going from enabled to enabled but it needs a modeset:
+	 * if this transcoder was the master, all slaves also needs a modeset
+	 */
+
+	/* disabled -> enabled */
+	if (!old_conn_state->crtc && new_conn_state->crtc) {
+		crtc_state = drm_atomic_get_new_crtc_state(state, new_conn_state->crtc);
+		intel_crtc_state = to_intel_crtc_state(crtc_state);
+		intel_crtc_state->mst_master_trans_pending = true;
+		return 0;
+	}
+
+	/* enabled -> enabled(modeset)? */
+	if (new_conn_state->crtc) {
+		crtc_state = drm_atomic_get_new_crtc_state(state, new_conn_state->crtc);
+		if (!drm_atomic_crtc_needs_modeset(crtc_state))
+			return 0;
+	}
+
+	/* handling enabled -> enabled(modeset) and enabled -> disabled */
+	crtc_state = drm_atomic_get_old_crtc_state(state, old_conn_state->crtc);
+	intel_crtc_state = to_intel_crtc_state(crtc_state);
+
+	/* If not master, nothing else needs to be handled */
+	if (intel_conn->mst_port->mst_master_transcoder !=
+	    intel_crtc_state->cpu_transcoder)
+		return 0;
+
+	/* Is master, mark all other CRTCs as needing a modeset */
+	drm_connector_list_iter_begin(state->dev, &conn_list_iter);
+	drm_for_each_connector_iter(conn_iter, &conn_list_iter) {
+		struct intel_connector *intel_conn_iter;
+		struct drm_connector_state *conn_iter_state;
+
+		intel_conn_iter = to_intel_connector(conn_iter);
+		if (intel_conn_iter->mst_port != intel_conn->mst_port)
+			continue;
+
+		conn_iter_state = drm_atomic_get_connector_state(state,
+								 conn_iter);
+		if (IS_ERR(conn_iter_state)) {
+			drm_connector_list_iter_end(&conn_list_iter);
+			return PTR_ERR(conn_iter_state);
+		}
+
+		if (!conn_iter_state->crtc)
+			continue;
+
+		crtc_state = drm_atomic_get_crtc_state(state, conn_iter_state->crtc);
+		if (IS_ERR(crtc_state)) {
+			drm_connector_list_iter_end(&conn_list_iter);
+			return PTR_ERR(conn_iter_state);
+		}
+
+		intel_crtc_state = to_intel_crtc_state(crtc_state);
+		intel_crtc_state->mst_master_trans_pending = true;
+		crtc_state->mode_changed = true;
+	}
+	drm_connector_list_iter_end(&conn_list_iter);
+
+	return 0;
+}
+
 static int
 intel_dp_mst_atomic_check(struct drm_connector *connector,
 			  struct drm_atomic_state *state)
@@ -175,6 +264,10 @@ intel_dp_mst_atomic_check(struct drm_connector *connector,
 	if (ret)
 		return ret;
 
+	ret = intel_dp_mst_atomic_master_trans_check(connector, state);
+	if (ret)
+		return ret;
+
 	if (!old_conn_state->crtc)
 		return 0;
 
@@ -259,6 +352,7 @@ static void intel_mst_post_disable_dp(struct intel_encoder *encoder,
 		intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_OFF);
 		intel_dig_port->base.post_disable(&intel_dig_port->base,
 						  old_crtc_state, NULL);
+		intel_dp->mst_master_transcoder = INVALID_TRANSCODER;
 	}
 
 	DRM_DEBUG_KMS("active links %d\n", intel_dp->active_mst_links);
@@ -314,8 +408,11 @@ static void intel_mst_pre_enable_dp(struct intel_encoder *encoder,
 
 	DRM_DEBUG_KMS("active links %d\n", intel_dp->active_mst_links);
 
-	if (first_mst_stream)
+	if (first_mst_stream) {
+		WARN_ON(intel_dp->mst_master_transcoder != INVALID_TRANSCODER);
+		intel_dp->mst_master_transcoder = pipe_config->cpu_transcoder;
 		intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
+	}
 
 	drm_dp_send_power_updown_phy(&intel_dp->mst_mgr, connector->port, true);
 
@@ -717,3 +814,13 @@ intel_dp_mst_encoder_cleanup(struct intel_digital_port *intel_dig_port)
 	drm_dp_mst_topology_mgr_destroy(&intel_dp->mst_mgr);
 	/* encoders will get killed by normal cleanup */
 }
+
+enum transcoder
+intel_dp_mst_master_trans_get(struct intel_encoder *encoder)
+{
+	struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+	struct intel_dp *intel_dp = &intel_mst->primary->dp;
+
+	WARN_ON(intel_dp->mst_master_transcoder == INVALID_TRANSCODER);
+	return intel_dp->mst_master_transcoder;
+}
diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.h b/drivers/gpu/drm/i915/display/intel_dp_mst.h
index f660ad80db04..e6f28a517182 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_mst.h
+++ b/drivers/gpu/drm/i915/display/intel_dp_mst.h
@@ -7,9 +7,11 @@
 #define __INTEL_DP_MST_H__
 
 struct intel_digital_port;
+struct intel_encoder;
 
 int intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port, int conn_id);
 void intel_dp_mst_encoder_cleanup(struct intel_digital_port *intel_dig_port);
 int intel_dp_mst_encoder_active_links(struct intel_digital_port *intel_dig_port);
+enum transcoder intel_dp_mst_master_trans_get(struct intel_encoder *encoder);
 
 #endif /* __INTEL_DP_MST_H__ */
-- 
2.24.0



More information about the Intel-gfx mailing list