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.
Add a quirk to skip the link status check for sinks with unreliable
link status reporting. The quirk is matched on the Chrontel IEEE OUI
(F0:02:2B) and the device ID "CH7511" on any platform.
While at it, clear the DPCD quirks mask before scanning the quirk
table, so that quirks of a previously connected sink do not stick to
a new sink plugged into the same connector on re-detection.
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 <[email protected]>
Signed-off-by: Patrik Jakobsson <[email protected]>
Signed-off-by: bitey <[email protected]>
---
Changes in v5:
- Rename the quirk to QUIRK_DP_LINK_STATUS_UNRELIABLE and drop the
commit subject mention of the interlane align bit, since the quirk
now covers the whole link status (Sashiko)
- Skip the link status check entirely for quirked sinks instead of
synthesizing link status bits (Sashiko)
- Clear intel_dp->quirks.mask before scanning the quirk table to
prevent stale quirks from a previously connected sink and repeated
quirk messages on connector re-detection (Sashiko)
- Use drm_info_once() for the quirk message (Sashiko)
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
Changes in v3:
- 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 | 12 ++++++++++
drivers/gpu/drm/i915/display/intel_quirks.c | 23 +++++++++++++++++++
drivers/gpu/drm/i915/display/intel_quirks.h | 1 +
3 files changed, 36 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..0fdc5a4 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,17 @@ intel_dp_needs_link_retrain(struct intel_dp *intel_dp)
if (link_recovery_autoretrain_pending(link_training))
return true;
+ /*
+ * The Chrontel CH7511 DP-to-LVDS bridge does not maintain the DPCD
+ * link status fields after the training pattern is disabled: the
+ * interlane alignment and per-lane status bits all read back as zero
+ * even though the link is up. Skip the link status check for such
+ * sinks, otherwise the bogus status triggers an endless retrain
+ * loop.
+ */
+ if (intel_has_dpcd_quirk(intel_dp, QUIRK_DP_LINK_STATUS_UNRELIABLE))
+ return false;
+
/* 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..27141ad 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_dp_link_status_unreliable(struct intel_dp *intel_dp)
+{
+ struct intel_display *display = to_intel_display(intel_dp);
+
+ intel_set_dpcd_quirk(intel_dp, QUIRK_DP_LINK_STATUS_UNRELIABLE);
+ drm_info_once(display->drm, "Applying DP link status unreliable quirk
(Chrontel CH7511)\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_dp_link_status_unreliable,
+ },
};
void intel_init_quirks(struct intel_display *display)
@@ -320,6 +337,12 @@ void intel_init_dpcd_quirks(struct intel_dp *intel_dp,
struct pci_dev *d = to_pci_dev(display->drm->dev);
int i;
+ /*
+ * Reset the mask so that quirks from a previously connected sink
+ * do not stick to a new sink plugged into the same connector.
+ */
+ intel_dp->quirks.mask = 0;
+
for (i = 0; i < ARRAY_SIZE(intel_dpcd_quirks); i++) {
const struct intel_dpcd_quirk *q = &intel_dpcd_quirks[i];
diff --git a/drivers/gpu/drm/i915/display/intel_quirks.h
b/drivers/gpu/drm/i915/display/intel_quirks.h
index 970a4fe..483aa1f 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_DP_LINK_STATUS_UNRELIABLE,
};
void intel_init_quirks(struct intel_display *display);
--
2.56.0