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 <[email protected]>
Signed-off-by: Patrik Jakobsson <[email protected]>
Signed-off-by: bitey <[email protected]>
---
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

Reply via email to