On 6/10/2026 11:14 PM, Alexander Kaplan wrote:
The bpc computation for HDMI sinks behind a DP branch device
(intel_dp_hdmi_compute_bpc()) validates each bpc candidate against
the sink's TMDS character rate limits, even if the video will be
transmitted over an FRL link, where those limits don't apply.
This caps such sinks at 8bpc whenever a deep color mode's
TMDS-equivalent character rate exceeds the TMDS limit, although the
FRL link has plenty of bandwidth.
E.g. 4k60 RGB 10bpc corresponds to a ~742 MHz TMDS character rate,
above the typical 600 MHz limit, but only needs ~17.8 Gbps of e.g. a
48 Gbps FRL link.
Modes whose rate exceeds the TMDS limit already at 8bpc (e.g. 4k120)
are additionally forced from RGB to YCbCr 4:2:0 output.
If both the PCON and the sink support FRL, validate the required
bandwidth against the FRL bandwidth the link will be trained with
(the same min() of the PCON's and the sink's max FRL rate that
intel_dp_pcon_start_frl_training() uses) instead of the TMDS limits.
This mirrors how intel_dp_mode_valid_downstream() already validates
modes against the FRL bandwidth for such sinks.
Sinks without FRL support behind an FRL capable PCON keep using the
TMDS limits, since the PCON transmits to them in TMDS mode.
The sink's deep color EDID capabilities still apply via
intel_hdmi_bpc_possible(), and the DP link side limits are handled
separately, as before.
intel_dp_mode_valid_downstream() currently checks the FRL bandwidth
only against the PCON's limit and also skips the TMDS checks for
non-FRL sinks behind an FRL capable PCON.
Aligning it with the limit used here is left for a separate change.
Tested on PTL (xe) with a Synaptics VMM7100 PCON and an LG OLED G4:
4k60 goes from RGB 8bpc (dithered 6bpc pipe) to RGB 12bpc with HDR,
matching macOS (12bpc) and Windows (10bpc) on the same hardware.
Thanks for the patches, and for the testing on real PCON hardware.
Yeah, the PCON mode_valid/bpc path needs an overhaul here.
The fix is correct i.e. the compute_bpc shouldn't apply TMDS limits on
an FRL link.
But it covers only part of the problem:
- It fixes only intel_dp_hdmi_compute_bpc() and leaves
intel_dp_mode_valid_downstream() on the old PCON-only FRL check
(as you have also mentioned in the commit message).
- frl_required_bw() is uncompressed-only, so modes that would only
fit via PCON DSC (where both PCON and sink support it) still get
rejected.
I had sent a few patches to address these sometime ago, but didnt go
through [1] [2].
This patch overlaps with my earlier 2023 series [1] which fixes both
compute_bpc and mode_valid via a common wrapper.
I've since extended these with the PCON DSC bandwidth handling [3] and
I'm about to post the updated series once I'm done testing, I'll Cc you.
Since these touch the same paths, it'd be good to converge there rather
than land overlapping changes.
Feel free to take a look once it's out. A Tested-by from you on the PCON
hardware would be very welcome.
[1] https://patchwork.freedesktop.org/series/107550/
[2] https://patchwork.freedesktop.org/series/99311/
[3] https://patchwork.freedesktop.org/series/169372/ (trybot)
Thanks & Regards,
Ankit
Cc: Ankit Nautiyal <[email protected]>
Cc: Ville Syrjälä <[email protected]>
Signed-off-by: Alexander Kaplan <[email protected]>
---
drivers/gpu/drm/i915/display/intel_dp.c | 37 ++++++++++++++++++++++++-
1 file changed, 36 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c
b/drivers/gpu/drm/i915/display/intel_dp.c
index 2831b274d88a..511d99326af4 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.c
+++ b/drivers/gpu/drm/i915/display/intel_dp.c
@@ -124,6 +124,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);
/* Is link rate UHBR and thus 128b/132b? */
bool intel_dp_is_uhbr(const struct intel_crtc_state *crtc_state)
@@ -1347,6 +1348,40 @@ static int frl_required_bw(int clock, int bpc,
return clock * bpc * 3;
}
+static enum drm_mode_status
+intel_dp_hdmi_clock_valid(struct intel_dp *intel_dp,
+ int clock, int bpc,
+ enum intel_output_format sink_format,
+ bool respect_downstream_limits)
+{
+ int max_frl_bw;
+
+ if (!respect_downstream_limits)
+ return MODE_OK;
+
+ /* The FRL bandwidth the link will be trained with */
+ max_frl_bw = min(intel_dp->dfp.pcon_max_frl_bw,
+ intel_dp_hdmi_sink_max_frl(intel_dp));
+
+ /*
+ * If both the PCON and the sink support FRL, the PCON transmits
+ * to the sink in FRL mode, where the TMDS character rate limits
+ * don't apply.
+ */
+ if (max_frl_bw > 0) {
+ /* converting bw from Gbps to Kbps */
+ max_frl_bw = max_frl_bw * 1000000;
+
+ if (frl_required_bw(clock, bpc, sink_format) > max_frl_bw)
+ return MODE_CLOCK_HIGH;
+
+ return MODE_OK;
+ }
+
+ return intel_dp_tmds_clock_valid(intel_dp, clock, bpc,
+ sink_format,
respect_downstream_limits);
+}
+
static enum drm_mode_status
intel_dp_mode_valid_downstream(struct intel_connector *connector,
const struct drm_display_mode *mode,
@@ -1831,7 +1866,7 @@ 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_tmds_clock_valid(intel_dp, clock, bpc,
crtc_state->sink_format,
+ intel_dp_hdmi_clock_valid(intel_dp, clock, bpc,
crtc_state->sink_format,
respect_downstream_limits) ==
MODE_OK)
return bpc;
}