On Wed, Jun 10, 2026 at 07:48:07PM +0200, Alexander Kaplan wrote: > Synaptics VMM7100 PCONs with branch firmware 7.1 deterministically > fail 8b/10b channel equalization at the 4-lane RBR link configuration > (see the preceding commit adding DP_DPCD_QUIRK_NO_LINK_RATE_RBR). > > The link config computation optimizes for the minimum link rate, so > it picks exactly this failing configuration for low resolution modes. > E.g. a 1080p60 boot greeter computes to 4x162000. > The resulting link training failure makes the fallback logic reduce > the link parameters, and since the rate is already at its minimum, > the lane count is halved to 2. > The reduced intel_dp->link.max_lane_count then sticks: these PCONs > hold HPD high and never raise the long HPD that would re-sync the > link parameters via intel_dp_reset_link_params(). > On the affected system the boot greeter thus permanently capped the > link at 2 lanes, limiting the subsequent 3840x2160@60 desktop mode to > 6 bpc dithered output instead of the deep color modes the setup is > capable of with 4 lanes. > > Skip RBR when computing the sink rates of a device with the > NO_LINK_RATE_RBR quirk, unless RBR is the only available rate. > Any mode that fits into the RBR link bandwidth also fits into HBR at > the same lane count, so no mode support is lost. > With the quirk applied the greeter trains 2x270000 and the 4k60 > desktop mode 4x810000, verified on PTL (xe) with an affected PCON.
Thanks for the tests and root causing of the issue. I think the right solution to avoid using a problematic link configuartion is to rely on the existing mechanism which is the link training fallback logic. A quirk in this patch would add another way, which is less generic and would potentially disable the link config on a non-affected device as well (I did read your test results above, but I still think it's possible that another device would use the same OUI/device ID without this issue). The existing link training fallback logic in the driver does have the problem that you describe above: after a link training failure with a given link config, a set of link configurations will be disabled for subsequent modesets, where these disabled link configs haven't been tried yet and so could still work fine. That issue should be fixed in the link training fallback logic, by disabling only the link configuration which has failed. That in turn needs more changes, which could be done after [1] is merged, the rationale for that and more details described in the cover letter of the same patchset. One way to force a (minimum) link rate (until the above issue is addressed in the fallback code) is to use the i915_dp_force_link_rate debugfs entry. [1] https://lore.kernel.org/all/[email protected] > Signed-off-by: Alexander Kaplan <[email protected]> > --- > drivers/gpu/drm/i915/display/intel_dp.c | 23 +++++++++++++++++------ > 1 file changed, 17 insertions(+), 6 deletions(-) > > diff --git a/drivers/gpu/drm/i915/display/intel_dp.c > b/drivers/gpu/drm/i915/display/intel_dp.c > index 85d3aa3b9894..dec68f07161e 100644 > --- a/drivers/gpu/drm/i915/display/intel_dp.c > +++ b/drivers/gpu/drm/i915/display/intel_dp.c > @@ -197,7 +197,8 @@ static void intel_dp_set_dpcd_sink_rates(struct intel_dp > *intel_dp) > static const int dp_rates[] = { > 162000, 270000, 540000, 810000 > }; > - int i, max_rate; > + int i, num_rates = 0; > + int max_rate; > int max_lttpr_rate; > > if (drm_dp_has_quirk(&intel_dp->desc, > DP_DPCD_QUIRK_CAN_DO_MAX_LINK_RATE_3_24_GBPS)) { > @@ -221,7 +222,17 @@ static void intel_dp_set_dpcd_sink_rates(struct intel_dp > *intel_dp) > for (i = 0; i < ARRAY_SIZE(dp_rates); i++) { > if (dp_rates[i] > max_rate) > break; > - intel_dp->sink_rates[i] = dp_rates[i]; > + > + /* > + * The quirked devices fail channel equalization at RBR, but > + * train reliably at all higher rates. Skip RBR, unless it's > + * the only available rate. > + */ > + if (dp_rates[i] == 162000 && max_rate >= 270000 && > + drm_dp_has_quirk(&intel_dp->desc, > DP_DPCD_QUIRK_NO_LINK_RATE_RBR)) > + continue; > + > + intel_dp->sink_rates[num_rates++] = dp_rates[i]; > } > > /* > @@ -252,14 +263,14 @@ static void intel_dp_set_dpcd_sink_rates(struct > intel_dp *intel_dp) > } > > if (uhbr_rates & DP_UHBR10) > - intel_dp->sink_rates[i++] = 1000000; > + intel_dp->sink_rates[num_rates++] = 1000000; > if (uhbr_rates & DP_UHBR13_5) > - intel_dp->sink_rates[i++] = 1350000; > + intel_dp->sink_rates[num_rates++] = 1350000; > if (uhbr_rates & DP_UHBR20) > - intel_dp->sink_rates[i++] = 2000000; > + intel_dp->sink_rates[num_rates++] = 2000000; > } > > - intel_dp->num_sink_rates = i; > + intel_dp->num_sink_rates = num_rates; > } > > static void intel_dp_set_sink_rates(struct intel_dp *intel_dp) > -- > 2.54.0 > >
