[PATCH v4] drm/i915/display: Add quirk for interlane align bit on CH7511

From: bitey

Date: Sat Oct 31 2026 - 13:45:54 EST


When disabling pattern training after successful link training the
CH7511 bridge incorrectly clears the DPCD link status bits: not only
INTERLANE_ALIGN_DONE, but also the per-lane CR_DONE, CHANNEL_EQ_DONE
and SYMBOL_LOCKED bits. This is interpreted as a link failure when
rechecking the link status and triggers an endless loop of retraining
sequences.

This patch adds a quirk to pretend the bits are still set and no
retraining is needed. The quirk is matched on the Chrontel IEEE OUI
(F0:02:2B) and the device ID "CH7511" on any platform.

Tested on Ivy Bridge (HD Graphics 2500, device 0152) with a CH7511
DP-to-HDMI adapter on kernels 7.1-rc1, drm-tip and 7.3.

Fixes: 3b3be899fc81 ("drm/i915/dp: Recheck link state after modeset")
Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/work_items/15744
Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/work_items/15994
Co-developed-by: Patrik Jakobsson <pjakobsson@xxxxxxx>
Signed-off-by: Patrik Jakobsson <pjakobsson@xxxxxxx>
Signed-off-by: bitey <s.mavlenkov@xxxxxxx>
---
Changes in v4:
- Also force the per-lane status bits (CR_DONE / CHANNEL_EQ_DONE /
SYMBOL_LOCKED). On Ivy Bridge the CH7511 zeroes the entire lane
status registers after training (ln0_1 = ln2_3 = 0x00), so forcing
only the INTERLANE_ALIGN_DONE bit is not enough to stop the retrain
loop.
- Rebased on top of v7.3

v3 changes:
- Match on all devices with a CH7511, instead of only VLV
- Rebased on top of drm-tip
- Additional "Closes" tag
---
.../drm/i915/display/intel_dp_link_training.c | 17 +++++++++++++++++
drivers/gpu/drm/i915/display/intel_quirks.c | 17 +++++++++++++++++
drivers/gpu/drm/i915/display/intel_quirks.h | 1 +
3 files changed, 35 insertions(+)

diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c
index 9a692f4..a2d5b04 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c
@@ -43,6 +43,7 @@
#include "intel_hotplug.h"
#include "intel_modeset_lock.h"
#include "intel_panel.h"
+#include "intel_quirks.h"
#include "intel_psr.h"

/**
@@ -2515,6 +2516,22 @@ intel_dp_needs_link_retrain(struct intel_dp *intel_dp)
if (link_recovery_autoretrain_pending(link_training))
return true;

+ /*
+ * The CH7511 bridge incorrectly clears all link status bits
+ * (INTERLANE_ALIGN_DONE, per-lane CR/CEQ/SYMLOCK) when disabling
+ * the training pattern. Force them to indicate successful training.
+ */
+ if (intel_has_dpcd_quirk(intel_dp, QUIRK_IGNORE_INTERLANE_ALIGN_BIT)) {
+ u8 lane_eq = DP_LANE_CR_DONE | DP_LANE_CHANNEL_EQ_DONE |
+ DP_LANE_SYMBOL_LOCKED;
+ int i;
+
+ for (i = 0; i < (intel_dp->lane_count + 1) / 2; i++)
+ link_status[i] |= lane_eq | (lane_eq << 4);
+ link_status[DP_LANE_ALIGN_STATUS_UPDATED - DP_LANE0_1_STATUS] |=
+ DP_INTERLANE_ALIGN_DONE;
+ }
+
/* Retrain if link not ok */
return !intel_dp_link_ok(intel_dp, link_status) &&
!intel_psr_link_ok(intel_dp);
diff --git a/drivers/gpu/drm/i915/display/intel_quirks.c b/drivers/gpu/drm/i915/display/intel_quirks.c
index 7d7db77..359bca4 100644
--- a/drivers/gpu/drm/i915/display/intel_quirks.c
+++ b/drivers/gpu/drm/i915/display/intel_quirks.c
@@ -94,6 +94,14 @@ static void quirk_disable_edp_panel_replay(struct intel_dp *intel_dp)
drm_info(display->drm, "Applying disable Panel Replay quirk\n");
}

+static void quirk_ignore_interlane_align_bit(struct intel_dp *intel_dp)
+{
+ struct intel_display *display = to_intel_display(intel_dp);
+
+ intel_set_dpcd_quirk(intel_dp, QUIRK_IGNORE_INTERLANE_ALIGN_BIT);
+ drm_info(display->drm, "Applying ignore interlane align bit quirk\n");
+}
+
static void quirk_disable_psr2(struct intel_display *display)
{
intel_set_quirk(display, QUIRK_DISABLE_PSR2);
@@ -289,6 +297,15 @@ static const struct intel_dpcd_quirk intel_dpcd_quirks[] = {
.sink_oui = SINK_OUI(0x00, 0x22, 0xb9),
.hook = quirk_disable_edp_panel_replay,
},
+ /* Chrontel CH7511 DP-to-LVDS bridge */
+ {
+ .device = DEVICE_ID_ANY,
+ .subsystem_vendor = PCI_ANY_ID,
+ .subsystem_device = PCI_ANY_ID,
+ .sink_oui = SINK_OUI(0x2b, 0x02, 0xf0),
+ .sink_device_id = SINK_DEVICE_ID('C', 'H', '7', '5', '1', '1'),
+ .hook = quirk_ignore_interlane_align_bit,
+ },
};

void intel_init_quirks(struct intel_display *display)
diff --git a/drivers/gpu/drm/i915/display/intel_quirks.h b/drivers/gpu/drm/i915/display/intel_quirks.h
index 970a4fe..7fcc396 100644
--- a/drivers/gpu/drm/i915/display/intel_quirks.h
+++ b/drivers/gpu/drm/i915/display/intel_quirks.h
@@ -23,6 +23,7 @@ enum intel_quirk_id {
QUIRK_EDP_LIMIT_RATE_HBR2,
QUIRK_DISABLE_EDP_PANEL_REPLAY,
QUIRK_DISABLE_PSR2,
+ QUIRK_IGNORE_INTERLANE_ALIGN_BIT,
};

void intel_init_quirks(struct intel_display *display);
--
2.56.0