When a connector has DRM_FORCE_ON (set via video=CONNECTOR:e),
check_connector_changed() calls the driver's detect() callback.
Well-behaved drivers (e.g. NVIDIA's open kernel module) check
connector->force and return connector_status_connected regardless
of actual hardware state. This means the epoch counter never
increments and no uevent reaches userspace.

However, the HPD interrupt itself indicates a physical state
change on the link. A common scenario is an HDMI-connected TV
switching to built-in apps (Netflix, etc.), which causes the TV's
HDMI receiver to stop listening to TMDS signals. When the TV
returns to the HDMI input, the GPU needs to re-negotiate the link,
but no component in the stack detects this because the forced
status masks the physical change.

Add an epoch counter increment for force-enabled connectors after
detect() returns. This ensures the hotplug uevent reaches
userspace compositors so they can trigger a modeset for link
re-training.

Note that detect() is still called normally before the epoch
bump. This preserves driver-side effects like scrambler
re-initialization on vc4/i915. This approach differs from a
prior patch [1] which skipped detect() entirely for forced
connectors and was rejected due to regression concerns on
scrambler re-init.

The software polling path (output_poll_execute) already skips
forced connectors with an explicit guard. This patch addresses
only the hardware HPD IRQ path, which was previously unguarded.

[1] 
https://patchwork.kernel.org/project/dri-devel/patch/[email protected]/

Tested with:
- GPU: NVIDIA RTX 5070 Ti (nvidia-open 595.58.03)
- Display: Samsung 75" 4K TV via HDMI-A-1 (force-enabled)
- Kernel: 6.19.10 (CachyOS)
- Compositor: KWin 6.6.3 (Wayland)

Signed-off-by: Miguel Sedek <[email protected]>
---
 drm_probe_helper.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/drm_probe_helper.c b/drm_probe_helper.c
index 92f2176..b7d60e2 100644
--- a/drm_probe_helper.c
+++ b/drm_probe_helper.c
@@ -984,6 +984,23 @@ static bool check_connector_changed(struct drm_connector 
*connector)
        old_epoch_counter = connector->epoch_counter;
        connector->status = drm_helper_probe_detect(connector, NULL, false);
 
+       /*
+        * For force-enabled connectors, the driver's detect() returns the
+        * forced status, so the epoch counter never increments. But an HPD
+        * event on a forced connector still indicates a physical link state
+        * change (e.g. a TV switching inputs). Signal the change to
+        * userspace so the compositor can trigger a modeset for link
+        * re-training. Note: detect() is still called above so driver-side
+        * effects (scrambler re-init, etc.) are preserved.
+        */
+       if (connector->force && old_epoch_counter == connector->epoch_counter) {
+               connector->epoch_counter += 1;
+               drm_dbg_kms(dev,
+                           "[CONNECTOR:%d:%s] HPD on force-enabled connector, 
signaling link re-train\n",
+                           connector->base.id, connector->name);
+               return true;
+       }
+
        if (old_epoch_counter == connector->epoch_counter) {
                drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Same epoch counter %llu\n",
                            connector->base.id,
-- 
2.53.0

Reply via email to