[PATCH 11/14] drm/i915/dp: Allow PCON DSC modes through FRL bw check

Ankit Nautiyal <[email protected]> Thu, 30 Jul 2026 16:22:23 +0530
Newsgroups org.freedesktop.lists.intel-gfx,org.freedesktop.lists.intel-xe
Message-ID <[email protected]>
intel_dp_frl_bw_valid() rejects any mode whose uncompressed FRL
bandwidth exceeds the PCON/sink max FRL bandwidth, even though we are
capable of enabling PCON DSC encoding at modeset time. As a result,
high-bandwidth HDMI2.1 modes (e.g. 4K120, 8K60) get pruned for sinks
behind a PCON, despite the PCON + sink being able to carry them with
DSC compression.

Now, that the HDMI DSC helpers no longer depend on intel_crtc_state,
reuse them from the FRL bandwidth check. When uncompressed transport does
not fit, compute the lowest compressed bpp allowed for the output format
and verify it:
 - fits the sink's per-scanline chunk-bytes limit and
 - resulets in the required FRL bandwidth within the PCON/sink limits.

This takes care of the existing FIXME in the FRL bandwidth check.

Signed-off-by: Ankit Nautiyal <[email protected]>
---
 drivers/gpu/drm/i915/display/intel_dp.c | 80 ++++++++++++++++++++++---
 1 file changed, 71 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
index 9396f118d154..ff74a3dd3524 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.c
+++ b/drivers/gpu/drm/i915/display/intel_dp.c
@@ -125,6 +125,7 @@ bool intel_dp_is_edp(struct intel_dp *intel_dp)
 
 static void intel_dp_unset_edid(struct intel_dp *intel_dp);
 static int intel_dp_hdmi_sink_max_frl(struct intel_dp *intel_dp);
+static bool intel_dp_is_hdmi_2_1_sink(struct intel_dp *intel_dp);
 
 /* Is link rate UHBR and thus 128b/132b? */
 bool intel_dp_is_uhbr(const struct intel_crtc_state *crtc_state)
@@ -1179,8 +1180,58 @@ static int frl_required_bw(int clock, int bpc,
 	return clock * bpc * 3;
 }
 
+static int frl_required_bw_dsc(int target_clock, int comp_bpp_x16)
+{
+	return DIV_ROUND_UP(target_clock * comp_bpp_x16, 16);
+}
+
+static bool
+intel_dp_pcon_dsc_fits_frl_bw(struct intel_dp *intel_dp,
+			      const struct drm_display_mode *mode,
+			      int bpc, enum intel_output_format sink_format,
+			      int max_frl_bw)
+{
+	struct intel_connector *connector = intel_dp->attached_connector;
+	const struct drm_display_info *info = &connector->base.display_info;
+	int min_dsc_bpp, max_dsc_bpp;
+	int num_slices, slice_width;
+	int hdmi_max_chunk_bytes;
+	int pcon_max_slices, pcon_max_slice_width;
+
+	/* PCON and HDMI sink must both support DSC 1.2 */
+	if (!info->hdmi.dsc_cap.v_1p2 ||
+	    !drm_dp_pcon_enc_is_dsc_1_2(intel_dp->pcon_dsc_dpcd))
+		return false;
+
+	pcon_max_slices = drm_dp_pcon_dsc_max_slices(intel_dp->pcon_dsc_dpcd);
+	pcon_max_slice_width = drm_dp_pcon_dsc_max_slice_width(intel_dp->pcon_dsc_dpcd);
+
+	num_slices = intel_hdmi_dsc_get_num_slices(mode, sink_format,
+						   pcon_max_slices,
+						   pcon_max_slice_width,
+						   info->hdmi.dsc_cap.max_slices,
+						   info->hdmi.dsc_cap.clk_per_slice);
+	if (!num_slices)
+		return false;
+
+	slice_width = DIV_ROUND_UP(mode->hdisplay, num_slices);
+
+	intel_hdmi_dsc_get_min_max_bpp(sink_format, bpc, info->hdmi.dsc_cap.all_bpp,
+				       &min_dsc_bpp, &max_dsc_bpp);
+
+	/* Use the lowest allowed compressed bpp for the best-case bandwidth */
+	hdmi_max_chunk_bytes = info->hdmi.dsc_cap.total_chunk_kbytes * 1024;
+	if (!intel_hdmi_dsc_bpp_fits_chunk_bytes(min_dsc_bpp, num_slices, slice_width,
+						 hdmi_max_chunk_bytes))
+		return false;
+
+	return frl_required_bw_dsc(mode->clock, min_dsc_bpp * 16) <= max_frl_bw;
+}
+
 static enum drm_mode_status
-intel_dp_frl_bw_valid(struct intel_dp *intel_dp, int target_clock,
+intel_dp_frl_bw_valid(struct intel_dp *intel_dp,
+		      const struct drm_display_mode *mode,
+		      int target_clock,
 		      int bpc, enum intel_output_format sink_format,
 		      bool respect_downstream_limits)
 {
@@ -1199,11 +1250,19 @@ intel_dp_frl_bw_valid(struct intel_dp *intel_dp, int target_clock,
 	/* converting bw from Gbps to Kbps*/
 	max_frl_bw = max_frl_bw * 1000000;
 
-	/* #FIXME check bandwidth with DSC if both PCON and HDMI sink support DSC */
-	if (target_bw > max_frl_bw)
-		return MODE_CLOCK_HIGH;
+	if (target_bw <= max_frl_bw)
+		return MODE_OK;
 
-	return MODE_OK;
+	/*
+	 * Uncompressed transport doesn't fit; if both PCON and HDMI sink
+	 * support DSC, check whether DSC compression brings it within limits.
+	 */
+	if (intel_dp_is_hdmi_2_1_sink(intel_dp) &&
+	    intel_dp_pcon_dsc_fits_frl_bw(intel_dp, mode, bpc, sink_format,
+					  max_frl_bw))
+		return MODE_OK;
+
+	return MODE_CLOCK_HIGH;
 }
 
 static bool
@@ -1215,13 +1274,14 @@ intel_dp_pcon_sink_support_frl(struct intel_dp *intel_dp)
 
 static enum drm_mode_status
 intel_dp_hdmi_bw_valid(struct intel_dp *intel_dp,
+		       const struct drm_display_mode *mode,
 		       int target_clock, int bpc,
 		       enum intel_output_format sink_format,
 		       bool respect_downstream_limits)
 {
 	if (intel_dp_pcon_sink_support_frl(intel_dp))
-		return intel_dp_frl_bw_valid(intel_dp, target_clock, bpc,
-					     sink_format,
+		return intel_dp_frl_bw_valid(intel_dp, mode,
+					     target_clock, bpc, sink_format,
 					     respect_downstream_limits);
 
 	return intel_dp_tmds_clock_valid(intel_dp, target_clock, bpc, sink_format,
@@ -1242,7 +1302,8 @@ intel_dp_mode_valid_downstream(struct intel_connector *connector,
 	    target_clock > intel_dp->dfp.max_dotclock)
 		return MODE_CLOCK_HIGH;
 
-	return intel_dp_hdmi_bw_valid(intel_dp, target_clock, bpc, sink_format, true);
+	return intel_dp_hdmi_bw_valid(intel_dp, mode, target_clock, bpc,
+				      sink_format, true);
 }
 
 static enum drm_mode_status
@@ -1662,7 +1723,8 @@ static int intel_dp_hdmi_compute_bpc(struct intel_dp *intel_dp,
 	for (; bpc >= 8; bpc -= 2) {
 		if (intel_hdmi_bpc_possible(crtc_state, bpc,
 					    intel_dp_has_hdmi_sink(intel_dp)) &&
-		    intel_dp_hdmi_bw_valid(intel_dp, clock, bpc, crtc_state->sink_format,
+		    intel_dp_hdmi_bw_valid(intel_dp, &crtc_state->hw.adjusted_mode,
+					   clock, bpc, crtc_state->sink_format,
 					   respect_downstream_limits) == MODE_OK)
 			return bpc;
 	}
-- 
2.50.1