[Intel-gfx] [PATCH v2 16/17] drm/i915/dp_mst: Add workaround for a DELL P2715Q payload allocation problem

Lyude Paul lyude at redhat.com
Tue Jan 31 23:43:41 UTC 2023


I mentioned this once already but: really, I am genuinely impressed at this! I
never expected to see this monitor ever work. Also, thank you a ton for adding
the payload table verification stuff here. For the whole series:

Reviewed-by: Lyude Paul <lyude at redhat.com>

On Tue, 2023-01-31 at 17:05 +0200, Imre Deak wrote:
> The DELL P2715Q monitor's MST hub has a payload allocation problem,
> where the VCPI used to reserve the last two time slots (at position
> 0x3e, 0x3f) in the hub's payload table, this VCPI can't be reused for
> later payload configurations.
> 
> The problem results at least in streams reusing older VCPIs to stay
> blank on the screen and the payload table containing bogus VCPIs
> (repeating the one earlier used to reserve the 0x3e, 0x3f slots) after
> the last reservered slot.
> 
> To work around the problem detect this monitor and the condition for the
> problem (when the last two slots get allocated in a commit), force a
> full modeset of the MST topology in the subsequent commit and reset the
> payload table during the latter commit after all payloads have been
> freed.
> 
> Cc: Ville Syrjälä <ville.syrjala at linux.intel.com>
> Cc: Lyude Paul <lyude at redhat.com>
> Signed-off-by: Imre Deak <imre.deak at intel.com>
> ---
>  drivers/gpu/drm/i915/display/intel_atomic.c   | 13 +++--
>  drivers/gpu/drm/i915/display/intel_atomic.h   |  3 +-
>  .../drm/i915/display/intel_display_types.h    |  1 +
>  drivers/gpu/drm/i915/display/intel_dp.c       |  5 +-
>  drivers/gpu/drm/i915/display/intel_dp_mst.c   | 48 +++++++++++++++++--
>  5 files changed, 61 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/display/intel_atomic.c b/drivers/gpu/drm/i915/display/intel_atomic.c
> index 0f1c5b9c9a826..04e5f0e0fffa6 100644
> --- a/drivers/gpu/drm/i915/display/intel_atomic.c
> +++ b/drivers/gpu/drm/i915/display/intel_atomic.c
> @@ -616,7 +616,8 @@ intel_atomic_get_crtc_state(struct drm_atomic_state *state,
>  }
>  
>  static int modeset_pipe(struct intel_atomic_state *state,
> -			struct intel_crtc *crtc, const char *reason)
> +			struct intel_crtc *crtc, const char *reason,
> +			bool allow_fastset)
>  {
>  	struct drm_i915_private *i915 = to_i915(state->base.dev);
>  	struct intel_crtc_state *crtc_state;
> @@ -629,6 +630,8 @@ static int modeset_pipe(struct intel_atomic_state *state,
>  		return PTR_ERR(crtc_state);
>  
>  	crtc_state->uapi.mode_changed = true;
> +	if (!allow_fastset)
> +		crtc_state->uapi.connectors_changed = true;
>  	crtc_state->update_pipe = false;
>  
>  	return intel_atomic_add_affected_planes(state, crtc);
> @@ -639,6 +642,7 @@ static int modeset_pipe(struct intel_atomic_state *state,
>   * @state: atomic state
>   * @connector: connector to add the state for
>   * @reason: the reason why the connector needs to be added
> + * @allow_fastset: allow a fastset
>   *
>   * Add the @connector to the atomic state with its CRTC state and force a modeset
>   * on the CRTC if any.
> @@ -648,7 +652,8 @@ static int modeset_pipe(struct intel_atomic_state *state,
>   * Returns 0 in case of success, a negative error code on failure.
>   */
>  int intel_atomic_modeset_connector(struct intel_atomic_state *state,
> -				   struct intel_connector *connector, const char *reason)
> +				   struct intel_connector *connector, const char *reason,
> +				   bool allow_fastset)
>  {
>  	struct drm_i915_private *i915 = to_i915(state->base.dev);
>  	struct drm_connector_state *conn_state;
> @@ -671,7 +676,7 @@ int intel_atomic_modeset_connector(struct intel_atomic_state *state,
>  	if (ret)
>  		return ret;
>  
> -	return modeset_pipe(state, crtc, reason);
> +	return modeset_pipe(state, crtc, reason, allow_fastset);
>  }
>  
>  /**
> @@ -700,7 +705,7 @@ int intel_atomic_modeset_pipe(struct intel_atomic_state *state,
>  	if (ret)
>  		return ret;
>  
> -	return modeset_pipe(state, crtc, reason);
> +	return modeset_pipe(state, crtc, reason, true);
>  }
>  
>  /**
> diff --git a/drivers/gpu/drm/i915/display/intel_atomic.h b/drivers/gpu/drm/i915/display/intel_atomic.h
> index 84295d388e3cb..7778aea8a09fe 100644
> --- a/drivers/gpu/drm/i915/display/intel_atomic.h
> +++ b/drivers/gpu/drm/i915/display/intel_atomic.h
> @@ -59,7 +59,8 @@ int intel_atomic_setup_scalers(struct drm_i915_private *dev_priv,
>  int intel_atomic_modeset_pipe(struct intel_atomic_state *state,
>  			      struct intel_crtc *crtc, const char *reason);
>  int intel_atomic_modeset_connector(struct intel_atomic_state *state,
> -				   struct intel_connector *connector, const char *reason);
> +				   struct intel_connector *connector, const char *reason,
> +				   bool allow_fastset);
>  int intel_atomic_modeset_all_pipes(struct intel_atomic_state *state,
>  				   const char *reason);
>  
> diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h
> index 9ccae7a460200..06d51d2b5e0d6 100644
> --- a/drivers/gpu/drm/i915/display/intel_display_types.h
> +++ b/drivers/gpu/drm/i915/display/intel_display_types.h
> @@ -1656,6 +1656,7 @@ struct intel_dp {
>  	bool has_audio;
>  	bool reset_link_params;
>  	bool use_max_params;
> +	bool mst_reset_payload_table;
>  	u8 dpcd[DP_RECEIVER_CAP_SIZE];
>  	u8 psr_dpcd[EDP_PSR_RECEIVER_CAP_SIZE];
>  	u8 downstream_ports[DP_MAX_DOWNSTREAM_PORTS];
> diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
> index d6b0ef38f6563..c157bcd976103 100644
> --- a/drivers/gpu/drm/i915/display/intel_dp.c
> +++ b/drivers/gpu/drm/i915/display/intel_dp.c
> @@ -4689,6 +4689,8 @@ intel_dp_detect(struct drm_connector *connector,
>  		memset(&intel_dp->compliance, 0, sizeof(intel_dp->compliance));
>  		memset(intel_dp->dsc_dpcd, 0, sizeof(intel_dp->dsc_dpcd));
>  
> +		intel_dp->mst_reset_payload_table = false;
> +
>  		if (intel_dp->is_mst) {
>  			drm_dbg_kms(&dev_priv->drm,
>  				    "MST device may have disappeared %d vs %d\n",
> @@ -4924,7 +4926,8 @@ static int intel_modeset_tile_group(struct intel_atomic_state *state,
>  			continue;
>  
>  		ret = intel_atomic_modeset_connector(state, connector,
> -						     "connector tile group");
> +						     "connector tile group",
> +						     true);
>  		if (ret)
>  			break;
>  	}
> diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c
> index 08222fc6c5ecd..a9bb339e41987 100644
> --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c
> +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c
> @@ -422,9 +422,10 @@ intel_dp_mst_atomic_master_trans_check(struct intel_connector *connector,
>  	struct drm_i915_private *dev_priv = to_i915(state->base.dev);
>  	struct drm_connector_list_iter connector_list_iter;
>  	struct intel_connector *connector_iter;
> +	bool reset_payload_table = connector->mst_port->mst_reset_payload_table;
>  	int ret = 0;
>  
> -	if (DISPLAY_VER(dev_priv) < 12)
> +	if (DISPLAY_VER(dev_priv) < 12 && !reset_payload_table)
>  		return  0;
>  
>  	if (!intel_connector_needs_modeset(state, &connector->base))
> @@ -437,7 +438,8 @@ intel_dp_mst_atomic_master_trans_check(struct intel_connector *connector,
>  			continue;
>  
>  		ret = intel_atomic_modeset_connector(state, connector_iter,
> -						     "MST master transcoder");
> +						     "MST master transcoder",
> +						     !reset_payload_table);
>  		if (ret)
>  			break;
>  	}
> @@ -531,6 +533,41 @@ static void intel_mst_disable_dp(struct intel_atomic_state *state,
>  	intel_audio_codec_disable(encoder, old_crtc_state, old_conn_state);
>  }
>  
> +static void detect_payload_allocation_bug(const struct drm_dp_mst_topology_state *mst_state,
> +					  const struct intel_connector *connector,
> +					  struct intel_dp *intel_dp)
> +{
> +	struct drm_i915_private *i915 = dp_to_i915(intel_dp);
> +
> +	if (!drm_dp_has_quirk(&intel_dp->desc, DP_DPCD_QUIRK_MST_PAYLOAD_TABLE_RESET_WA))
> +		return;
> +
> +	if (drm_dp_mst_allocated_time_slots(mst_state) < DP_PAYLOAD_TABLE_SIZE - 2)
> +		return;
> +
> +	drm_dbg(&i915->drm,
> +		"[CONNECTOR:%d:%s] Payload table allocation bug detected\n",
> +		connector->base.base.id, connector->base.name);
> +
> +	intel_dp->mst_reset_payload_table = true;
> +}
> +
> +static void payload_allocation_bug_wa(const struct intel_connector *connector,
> +				      struct intel_dp *intel_dp)
> +{
> +	struct drm_i915_private *i915 = dp_to_i915(intel_dp);
> +
> +	if (!intel_dp->mst_reset_payload_table)
> +		return;
> +
> +	drm_dbg(&i915->drm,
> +		"[CONNECTOR:%d:%s] Resetting payload table due to allocation bug\n",
> +		connector->base.base.id, connector->base.name);
> +
> +	drm_dp_mst_reset_payload_table(&intel_dp->mst_mgr);
> +	intel_dp->mst_reset_payload_table = false;
> +}
> +
>  static void intel_mst_post_disable_dp(struct intel_atomic_state *state,
>  				      struct intel_encoder *encoder,
>  				      const struct intel_crtc_state *old_crtc_state,
> @@ -594,10 +631,13 @@ static void intel_mst_post_disable_dp(struct intel_atomic_state *state,
>  
>  
>  	intel_mst->connector = NULL;
> -	if (last_mst_stream)
> +	if (last_mst_stream) {
>  		dig_port->base.post_disable(state, &dig_port->base,
>  						  old_crtc_state, NULL);
>  
> +		payload_allocation_bug_wa(connector, intel_dp);
> +	}
> +
>  	drm_dbg_kms(&dev_priv->drm, "active links %d\n",
>  		    intel_dp->active_mst_links);
>  }
> @@ -662,6 +702,8 @@ static void intel_mst_pre_enable_dp(struct intel_atomic_state *state,
>  		drm_err(&dev_priv->drm, "Failed to create MST payload for %s: %d\n",
>  			connector->base.name, ret);
>  
> +	detect_payload_allocation_bug(mst_state, connector, intel_dp);
> +
>  	/*
>  	 * Before Gen 12 this is not done as part of
>  	 * dig_port->base.pre_enable() and should be done here. For

-- 
Cheers,
 Lyude Paul (she/her)
 Software Engineer at Red Hat



More information about the Intel-gfx mailing list