Intel-GFX Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2] drm/i915/tc: Recover AUX channel after external TC port mode change
@ 2026-05-05  3:35 ChunAn Wu
  2026-05-05  5:12 ` ✗ i915.CI.BAT: failure for drm/i915/tc: Recover AUX channel after external TC port mode change (rev2) Patchwork
                   ` (3 more replies)
  0 siblings, 4 replies; 8+ messages in thread
From: ChunAn Wu @ 2026-05-05  3:35 UTC (permalink / raw)
  To: Jani Nikula, Rodrigo Vivi, Joonas Lahtinen, Tvrtko Ursulin,
	David Airlie, Simona Vetter
  Cc: intel-gfx, intel-xe, dri-devel, linux-kernel

When Fn+F4 triggers mirror mode, the BIOS/EC reconfigures a TC port
mode (e.g. DP-Alt to TBT-Alt via UCSI) without generating HPD. The
driver's cached TC mode becomes stale, so intel_dp_aux_xfer() uses
the wrong power domain and IO routing, and AUX transactions fail.

This occurs when the USB-C-to-HDMI dongle does not support TBT, so
the mode switch creates a mismatch. It also occurs with slow monitors
whose delayed HPD/DDC/EDID recovery causes the Type-C layer to settle
on a stale tbt-alt state before the dongle finishes re-negotiation.

Add intel_tc_port_aux_recover() to detect hardware/cached TC mode
divergence and reset the PHY. Wrap it with
intel_dp_aux_xfer_with_recovery() to retry with corrected settings.

Tested on Panther Lake and Lunar Lake with a BenQ HDMI monitor via
USB-C-to-HDMI dongle.

Signed-off-by: ChunAn Wu <an.wu@canonical.com>
---
 drivers/gpu/drm/i915/display/intel_dp_aux.c | 41 +++++++++--
 drivers/gpu/drm/i915/display/intel_tc.c     | 75 +++++++++++++++++++++
 drivers/gpu/drm/i915/display/intel_tc.h     |  1 +
 3 files changed, 113 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux.c b/drivers/gpu/drm/i915/display/intel_dp_aux.c
index b20ec3e589fa..d496b2231656 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_aux.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_aux.c
@@ -481,6 +481,39 @@ static u32 intel_dp_aux_xfer_flags(const struct drm_dp_aux_msg *msg)
 	return 0;
 }
 
+/**
+ * intel_dp_aux_xfer_with_recovery - AUX transfer with TC port recovery
+ * @intel_dp: the DP port
+ * @send: buffer of bytes to send
+ * @send_bytes: number of bytes to send
+ * @recv: buffer to store received reply
+ * @recv_size: maximum number of bytes to receive
+ * @aux_send_ctl_flags: extra flags for the AUX send control register
+ *
+ * Wrapper around intel_dp_aux_xfer() that attempts to recover from an
+ * external TC port mode change (e.g., dp-alt -> tbt-alt via hotkey BIOS
+ * action) when the initial AUX transfer fails. On failure, recovery is
+ * attempted once via intel_tc_port_aux_recover() before retrying.
+ *
+ * Returns: number of received bytes on success, negative error code on failure.
+ */
+static int
+intel_dp_aux_xfer_with_recovery(struct intel_dp *intel_dp,
+				const u8 *send, int send_bytes,
+				u8 *recv, int recv_size,
+				u32 aux_send_ctl_flags)
+{
+	struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
+	int ret;
+
+	ret = intel_dp_aux_xfer(intel_dp, send, send_bytes, recv, recv_size,
+				aux_send_ctl_flags);
+	if (ret < 0 && intel_tc_port_aux_recover(dig_port))
+		ret = intel_dp_aux_xfer(intel_dp, send, send_bytes, recv,
+					recv_size, aux_send_ctl_flags);
+	return ret;
+}
+
 static ssize_t
 intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
 {
@@ -508,8 +541,8 @@ intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
 		if (msg->buffer)
 			memcpy(txbuf + HEADER_SIZE, msg->buffer, msg->size);
 
-		ret = intel_dp_aux_xfer(intel_dp, txbuf, txsize,
-					rxbuf, rxsize, flags);
+		ret = intel_dp_aux_xfer_with_recovery(intel_dp, txbuf, txsize,
+						      rxbuf, rxsize, flags);
 		if (ret > 0) {
 			msg->reply = rxbuf[0] >> 4;
 
@@ -531,8 +564,8 @@ intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
 		if (drm_WARN_ON(display->drm, rxsize > 20))
 			return -E2BIG;
 
-		ret = intel_dp_aux_xfer(intel_dp, txbuf, txsize,
-					rxbuf, rxsize, flags);
+		ret = intel_dp_aux_xfer_with_recovery(intel_dp, txbuf, txsize,
+						      rxbuf, rxsize, flags);
 		if (ret > 0) {
 			msg->reply = rxbuf[0] >> 4;
 			/*
diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c
index a21dd4e3fe4c..1b23161e4eea 100644
--- a/drivers/gpu/drm/i915/display/intel_tc.c
+++ b/drivers/gpu/drm/i915/display/intel_tc.c
@@ -1861,6 +1861,81 @@ void intel_tc_port_link_cancel_reset_work(struct intel_digital_port *dig_port)
 	cancel_delayed_work(&tc->link_reset_work);
 }
 
+/**
+ * intel_tc_port_aux_recover - Recover AUX channel after external TC mode change
+ * @dig_port: digital port
+ *
+ * When firmware causes a TC port mode change (e.g., dp-alt -> disconnect ->
+ * tbt-alt) during a hotkey-triggered display mode switch, AUX transactions
+ * using the stale power domain and IO flags will fail with timeouts or errors.
+ * This happens because:
+ *
+ *   1. The display driver holds the TC link in dp-alt mode (link_refcount > 0)
+ *   2. Firmware (e.g., via HP WMI hotkey BIOS action) reconfigures the TC port
+ *      mode externally without notifying the display driver
+ *   3. tc->mode stays as TC_PORT_DP_ALT while HW transitions to a different
+ *      mode, invalidating the AUX power domain and IO flags used by
+ *      intel_dp_aux_xfer()
+ *
+ * This function detects the discrepancy between tc->mode and actual HW state,
+ * and re-synchronizes them via a TC PHY reset. After recovery, AUX retries
+ * will use the correct power domain and control flags.
+ *
+ * The link_refcount is temporarily cleared to allow intel_tc_port_reset_mode()
+ * to proceed without the PHY-ownership assertion that fires when link_refcount
+ * is non-zero and firmware has already released PHY ownership.
+ *
+ * Must be called outside the TC port lock (tc->lock).
+ *
+ * Returns: %true if recovery was performed and AUX can be retried,
+ *          %false if recovery was not needed or not possible.
+ */
+bool intel_tc_port_aux_recover(struct intel_digital_port *dig_port)
+{
+	struct intel_tc_port *tc;
+	struct intel_display *display;
+	bool recovered = false;
+
+	if (!intel_encoder_is_tc(&dig_port->base))
+		return false;
+
+	tc = to_tc_port(dig_port);
+	display = to_intel_display(dig_port);
+
+	mutex_lock(&tc->lock);
+
+	/*
+	 * Recovery is only needed when the link is actively held AND the HW
+	 * TC mode has diverged from the driver's cached state.
+	 */
+	if (!tc->link_refcount || !intel_tc_port_needs_reset(tc))
+		goto out;
+
+	drm_dbg_kms(display->drm,
+		    "Port %s: AUX recover: external TC mode change detected (%s -> HW), reconnecting TC PHY\n",
+		    tc->port_name, tc_port_mode_name(tc->mode));
+
+	/*
+	 * Temporarily clear link_refcount so intel_tc_port_reset_mode() can
+	 * run the PHY disconnect/connect cycle without the ownership assertion
+	 * that fires when link_refcount > 0 and firmware has already released
+	 * PHY ownership externally.
+	 */
+	tc->link_refcount = 0;
+	intel_tc_port_reset_mode(tc, 1, false);
+	tc->link_refcount = 1;
+
+	recovered = tc->mode != TC_PORT_DISCONNECTED;
+	if (!recovered)
+		drm_warn(display->drm,
+			 "Port %s: AUX recover: failed to restore TC port mode\n",
+			 tc->port_name);
+
+out:
+	mutex_unlock(&tc->lock);
+	return recovered;
+}
+
 static void __intel_tc_port_lock(struct intel_tc_port *tc,
 				 int required_lanes)
 {
diff --git a/drivers/gpu/drm/i915/display/intel_tc.h b/drivers/gpu/drm/i915/display/intel_tc.h
index 6719aea5bd58..d44998a3081a 100644
--- a/drivers/gpu/drm/i915/display/intel_tc.h
+++ b/drivers/gpu/drm/i915/display/intel_tc.h
@@ -108,6 +108,7 @@ bool intel_tc_port_ref_held(struct intel_digital_port *dig_port);
 bool intel_tc_port_link_needs_reset(struct intel_digital_port *dig_port);
 bool intel_tc_port_link_reset(struct intel_digital_port *dig_port);
 void intel_tc_port_link_cancel_reset_work(struct intel_digital_port *dig_port);
+bool intel_tc_port_aux_recover(struct intel_digital_port *dig_port);
 
 int intel_tc_port_init(struct intel_digital_port *dig_port, bool is_legacy);
 void intel_tc_port_cleanup(struct intel_digital_port *dig_port);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* ✗ i915.CI.BAT: failure for drm/i915/tc: Recover AUX channel after external TC port mode change (rev2)
  2026-05-05  3:35 [PATCH v2] drm/i915/tc: Recover AUX channel after external TC port mode change ChunAn Wu
@ 2026-05-05  5:12 ` Patchwork
  2026-05-05  8:02 ` [PATCH v2] drm/i915/tc: Recover AUX channel after external TC port mode change Jani Nikula
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 8+ messages in thread
From: Patchwork @ 2026-05-05  5:12 UTC (permalink / raw)
  To: ChunAn Wu; +Cc: intel-gfx

[-- Attachment #1: Type: text/plain, Size: 4602 bytes --]

== Series Details ==

Series: drm/i915/tc: Recover AUX channel after external TC port mode change (rev2)
URL   : https://patchwork.freedesktop.org/series/165945/
State : failure

== Summary ==

CI Bug Log - changes from CI_DRM_18411 -> Patchwork_165945v2
====================================================

Summary
-------

  **FAILURE**

  Serious unknown changes coming with Patchwork_165945v2 absolutely need to be
  verified manually.
  
  If you think the reported changes have nothing to do with the changes
  introduced in Patchwork_165945v2, please notify your bug team (I915-ci-infra@lists.freedesktop.org) to allow them
  to document this new failure mode, which will reduce false positives in CI.

  External URL: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_165945v2/index.html

Participating hosts (41 -> 40)
------------------------------

  Missing    (1): bat-dg2-13 

Possible new issues
-------------------

  Here are the unknown changes that may have been introduced in Patchwork_165945v2:

### IGT changes ###

#### Possible regressions ####

  * igt@i915_selftest@live@requests:
    - bat-atsm-1:         [PASS][1] -> [INCOMPLETE][2]
   [1]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_18411/bat-atsm-1/igt@i915_selftest@live@requests.html
   [2]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_165945v2/bat-atsm-1/igt@i915_selftest@live@requests.html

  
Known issues
------------

  Here are the changes found in Patchwork_165945v2 that come from known issues:

### IGT changes ###

#### Issues hit ####

  * igt@i915_selftest@live@sanitycheck:
    - fi-kbl-7567u:       [PASS][3] -> [DMESG-WARN][4] ([i915#13735]) +65 other tests dmesg-warn
   [3]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_18411/fi-kbl-7567u/igt@i915_selftest@live@sanitycheck.html
   [4]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_165945v2/fi-kbl-7567u/igt@i915_selftest@live@sanitycheck.html

  * igt@i915_selftest@live@workarounds:
    - bat-arlh-3:         [PASS][5] -> [DMESG-FAIL][6] ([i915#12061]) +1 other test dmesg-fail
   [5]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_18411/bat-arlh-3/igt@i915_selftest@live@workarounds.html
   [6]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_165945v2/bat-arlh-3/igt@i915_selftest@live@workarounds.html

  * igt@kms_busy@basic@flip:
    - fi-kbl-7567u:       [PASS][7] -> [DMESG-WARN][8] ([i915#13735] / [i915#180])
   [7]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_18411/fi-kbl-7567u/igt@kms_busy@basic@flip.html
   [8]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_165945v2/fi-kbl-7567u/igt@kms_busy@basic@flip.html

  * igt@kms_pm_rpm@basic-pci-d3-state:
    - fi-kbl-7567u:       [PASS][9] -> [DMESG-WARN][10] ([i915#13735] / [i915#15673] / [i915#180]) +52 other tests dmesg-warn
   [9]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_18411/fi-kbl-7567u/igt@kms_pm_rpm@basic-pci-d3-state.html
   [10]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_165945v2/fi-kbl-7567u/igt@kms_pm_rpm@basic-pci-d3-state.html

  
#### Possible fixes ####

  * igt@i915_selftest@live@workarounds:
    - bat-dg2-14:         [DMESG-FAIL][11] ([i915#12061]) -> [PASS][12] +1 other test pass
   [11]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_18411/bat-dg2-14/igt@i915_selftest@live@workarounds.html
   [12]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_165945v2/bat-dg2-14/igt@i915_selftest@live@workarounds.html

  
#### Warnings ####

  * igt@i915_selftest@live:
    - bat-atsm-1:         [DMESG-FAIL][13] ([i915#12061]) -> [INCOMPLETE][14] ([i915#12061])
   [13]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_18411/bat-atsm-1/igt@i915_selftest@live.html
   [14]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_165945v2/bat-atsm-1/igt@i915_selftest@live.html

  
  [i915#12061]: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/12061
  [i915#13735]: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/13735
  [i915#15673]: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/15673
  [i915#180]: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/180


Build changes
-------------

  * Linux: CI_DRM_18411 -> Patchwork_165945v2

  CI-20190529: 20190529
  CI_DRM_18411: 94d56a898a2db27f841b17f6966a81ba502fe63c @ git://anongit.freedesktop.org/gfx-ci/linux
  IGT_8884: 2e185bca126c4d135fe7fe579d0a3a177f150813 @ https://gitlab.freedesktop.org/drm/igt-gpu-tools.git
  Patchwork_165945v2: 94d56a898a2db27f841b17f6966a81ba502fe63c @ git://anongit.freedesktop.org/gfx-ci/linux

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_165945v2/index.html

[-- Attachment #2: Type: text/html, Size: 5813 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2] drm/i915/tc: Recover AUX channel after external TC port mode change
  2026-05-05  3:35 [PATCH v2] drm/i915/tc: Recover AUX channel after external TC port mode change ChunAn Wu
  2026-05-05  5:12 ` ✗ i915.CI.BAT: failure for drm/i915/tc: Recover AUX channel after external TC port mode change (rev2) Patchwork
@ 2026-05-05  8:02 ` Jani Nikula
  2026-05-05  8:20   ` An Wu
  2026-05-05  8:34 ` Imre Deak
  2026-05-06 18:55 ` Ville Syrjälä
  3 siblings, 1 reply; 8+ messages in thread
From: Jani Nikula @ 2026-05-05  8:02 UTC (permalink / raw)
  To: ChunAn Wu, Rodrigo Vivi, Joonas Lahtinen, Tvrtko Ursulin,
	David Airlie, Simona Vetter
  Cc: intel-gfx, intel-xe, dri-devel, linux-kernel

On Tue, 05 May 2026, ChunAn Wu <an.wu@canonical.com> wrote:
> When Fn+F4 triggers mirror mode, the BIOS/EC reconfigures a TC port
> mode (e.g. DP-Alt to TBT-Alt via UCSI) without generating HPD. The
> driver's cached TC mode becomes stale, so intel_dp_aux_xfer() uses
> the wrong power domain and IO routing, and AUX transactions fail.
>
> This occurs when the USB-C-to-HDMI dongle does not support TBT, so
> the mode switch creates a mismatch. It also occurs with slow monitors
> whose delayed HPD/DDC/EDID recovery causes the Type-C layer to settle
> on a stale tbt-alt state before the dongle finishes re-negotiation.
>
> Add intel_tc_port_aux_recover() to detect hardware/cached TC mode
> divergence and reset the PHY. Wrap it with
> intel_dp_aux_xfer_with_recovery() to retry with corrected settings.
>
> Tested on Panther Lake and Lunar Lake with a BenQ HDMI monitor via
> USB-C-to-HDMI dongle.

What are the changes between v1 and v2?

>
> Signed-off-by: ChunAn Wu <an.wu@canonical.com>
> ---
>  drivers/gpu/drm/i915/display/intel_dp_aux.c | 41 +++++++++--
>  drivers/gpu/drm/i915/display/intel_tc.c     | 75 +++++++++++++++++++++
>  drivers/gpu/drm/i915/display/intel_tc.h     |  1 +
>  3 files changed, 113 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux.c b/drivers/gpu/drm/i915/display/intel_dp_aux.c
> index b20ec3e589fa..d496b2231656 100644
> --- a/drivers/gpu/drm/i915/display/intel_dp_aux.c
> +++ b/drivers/gpu/drm/i915/display/intel_dp_aux.c
> @@ -481,6 +481,39 @@ static u32 intel_dp_aux_xfer_flags(const struct drm_dp_aux_msg *msg)
>  	return 0;
>  }
>  
> +/**
> + * intel_dp_aux_xfer_with_recovery - AUX transfer with TC port recovery
> + * @intel_dp: the DP port
> + * @send: buffer of bytes to send
> + * @send_bytes: number of bytes to send
> + * @recv: buffer to store received reply
> + * @recv_size: maximum number of bytes to receive
> + * @aux_send_ctl_flags: extra flags for the AUX send control register
> + *
> + * Wrapper around intel_dp_aux_xfer() that attempts to recover from an
> + * external TC port mode change (e.g., dp-alt -> tbt-alt via hotkey BIOS
> + * action) when the initial AUX transfer fails. On failure, recovery is
> + * attempted once via intel_tc_port_aux_recover() before retrying.
> + *
> + * Returns: number of received bytes on success, negative error code on failure.
> + */
> +static int
> +intel_dp_aux_xfer_with_recovery(struct intel_dp *intel_dp,
> +				const u8 *send, int send_bytes,
> +				u8 *recv, int recv_size,
> +				u32 aux_send_ctl_flags)
> +{
> +	struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
> +	int ret;
> +
> +	ret = intel_dp_aux_xfer(intel_dp, send, send_bytes, recv, recv_size,
> +				aux_send_ctl_flags);
> +	if (ret < 0 && intel_tc_port_aux_recover(dig_port))
> +		ret = intel_dp_aux_xfer(intel_dp, send, send_bytes, recv,
> +					recv_size, aux_send_ctl_flags);
> +	return ret;
> +}
> +
>  static ssize_t
>  intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
>  {
> @@ -508,8 +541,8 @@ intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
>  		if (msg->buffer)
>  			memcpy(txbuf + HEADER_SIZE, msg->buffer, msg->size);
>  
> -		ret = intel_dp_aux_xfer(intel_dp, txbuf, txsize,
> -					rxbuf, rxsize, flags);
> +		ret = intel_dp_aux_xfer_with_recovery(intel_dp, txbuf, txsize,
> +						      rxbuf, rxsize, flags);
>  		if (ret > 0) {
>  			msg->reply = rxbuf[0] >> 4;
>  
> @@ -531,8 +564,8 @@ intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
>  		if (drm_WARN_ON(display->drm, rxsize > 20))
>  			return -E2BIG;
>  
> -		ret = intel_dp_aux_xfer(intel_dp, txbuf, txsize,
> -					rxbuf, rxsize, flags);
> +		ret = intel_dp_aux_xfer_with_recovery(intel_dp, txbuf, txsize,
> +						      rxbuf, rxsize, flags);
>  		if (ret > 0) {
>  			msg->reply = rxbuf[0] >> 4;
>  			/*
> diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c
> index a21dd4e3fe4c..1b23161e4eea 100644
> --- a/drivers/gpu/drm/i915/display/intel_tc.c
> +++ b/drivers/gpu/drm/i915/display/intel_tc.c
> @@ -1861,6 +1861,81 @@ void intel_tc_port_link_cancel_reset_work(struct intel_digital_port *dig_port)
>  	cancel_delayed_work(&tc->link_reset_work);
>  }
>  
> +/**
> + * intel_tc_port_aux_recover - Recover AUX channel after external TC mode change
> + * @dig_port: digital port
> + *
> + * When firmware causes a TC port mode change (e.g., dp-alt -> disconnect ->
> + * tbt-alt) during a hotkey-triggered display mode switch, AUX transactions
> + * using the stale power domain and IO flags will fail with timeouts or errors.
> + * This happens because:
> + *
> + *   1. The display driver holds the TC link in dp-alt mode (link_refcount > 0)
> + *   2. Firmware (e.g., via HP WMI hotkey BIOS action) reconfigures the TC port
> + *      mode externally without notifying the display driver
> + *   3. tc->mode stays as TC_PORT_DP_ALT while HW transitions to a different
> + *      mode, invalidating the AUX power domain and IO flags used by
> + *      intel_dp_aux_xfer()
> + *
> + * This function detects the discrepancy between tc->mode and actual HW state,
> + * and re-synchronizes them via a TC PHY reset. After recovery, AUX retries
> + * will use the correct power domain and control flags.
> + *
> + * The link_refcount is temporarily cleared to allow intel_tc_port_reset_mode()
> + * to proceed without the PHY-ownership assertion that fires when link_refcount
> + * is non-zero and firmware has already released PHY ownership.
> + *
> + * Must be called outside the TC port lock (tc->lock).
> + *
> + * Returns: %true if recovery was performed and AUX can be retried,
> + *          %false if recovery was not needed or not possible.
> + */
> +bool intel_tc_port_aux_recover(struct intel_digital_port *dig_port)
> +{
> +	struct intel_tc_port *tc;
> +	struct intel_display *display;
> +	bool recovered = false;
> +
> +	if (!intel_encoder_is_tc(&dig_port->base))
> +		return false;
> +
> +	tc = to_tc_port(dig_port);
> +	display = to_intel_display(dig_port);
> +
> +	mutex_lock(&tc->lock);
> +
> +	/*
> +	 * Recovery is only needed when the link is actively held AND the HW
> +	 * TC mode has diverged from the driver's cached state.
> +	 */
> +	if (!tc->link_refcount || !intel_tc_port_needs_reset(tc))
> +		goto out;
> +
> +	drm_dbg_kms(display->drm,
> +		    "Port %s: AUX recover: external TC mode change detected (%s -> HW), reconnecting TC PHY\n",
> +		    tc->port_name, tc_port_mode_name(tc->mode));
> +
> +	/*
> +	 * Temporarily clear link_refcount so intel_tc_port_reset_mode() can
> +	 * run the PHY disconnect/connect cycle without the ownership assertion
> +	 * that fires when link_refcount > 0 and firmware has already released
> +	 * PHY ownership externally.
> +	 */
> +	tc->link_refcount = 0;
> +	intel_tc_port_reset_mode(tc, 1, false);
> +	tc->link_refcount = 1;
> +
> +	recovered = tc->mode != TC_PORT_DISCONNECTED;
> +	if (!recovered)
> +		drm_warn(display->drm,
> +			 "Port %s: AUX recover: failed to restore TC port mode\n",
> +			 tc->port_name);
> +
> +out:
> +	mutex_unlock(&tc->lock);
> +	return recovered;
> +}
> +
>  static void __intel_tc_port_lock(struct intel_tc_port *tc,
>  				 int required_lanes)
>  {
> diff --git a/drivers/gpu/drm/i915/display/intel_tc.h b/drivers/gpu/drm/i915/display/intel_tc.h
> index 6719aea5bd58..d44998a3081a 100644
> --- a/drivers/gpu/drm/i915/display/intel_tc.h
> +++ b/drivers/gpu/drm/i915/display/intel_tc.h
> @@ -108,6 +108,7 @@ bool intel_tc_port_ref_held(struct intel_digital_port *dig_port);
>  bool intel_tc_port_link_needs_reset(struct intel_digital_port *dig_port);
>  bool intel_tc_port_link_reset(struct intel_digital_port *dig_port);
>  void intel_tc_port_link_cancel_reset_work(struct intel_digital_port *dig_port);
> +bool intel_tc_port_aux_recover(struct intel_digital_port *dig_port);
>  
>  int intel_tc_port_init(struct intel_digital_port *dig_port, bool is_legacy);
>  void intel_tc_port_cleanup(struct intel_digital_port *dig_port);

-- 
Jani Nikula, Intel

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2] drm/i915/tc: Recover AUX channel after external TC port mode change
  2026-05-05  8:02 ` [PATCH v2] drm/i915/tc: Recover AUX channel after external TC port mode change Jani Nikula
@ 2026-05-05  8:20   ` An Wu
  0 siblings, 0 replies; 8+ messages in thread
From: An Wu @ 2026-05-05  8:20 UTC (permalink / raw)
  To: Jani Nikula
  Cc: Rodrigo Vivi, Joonas Lahtinen, Tvrtko Ursulin, David Airlie,
	Simona Vetter, intel-gfx, intel-xe, dri-devel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 10263 bytes --]

Hi Jani
    The auto check bot told me the alignment is wrong. So I fixed the
alignment.

```
-:77: CHECK:PARENTHESIS_ALIGNMENT: Alignment should match open parenthesis
#77: FILE: drivers/gpu/drm/i915/display/intel_dp_aux.c:545:
+               ret = intel_dp_aux_xfer_with_recovery(intel_dp, txbuf,
txsize,
+                                                      rxbuf, rxsize,
flags);

-:88: CHECK:PARENTHESIS_ALIGNMENT: Alignment should match open parenthesis
#88: FILE: drivers/gpu/drm/i915/display/intel_dp_aux.c:568:
+               ret = intel_dp_aux_xfer_with_recovery(intel_dp, txbuf,
txsize,
+                                                      rxbuf, rxsize,
flags);
```

BR
    An

On Tue, May 5, 2026 at 4:02 PM Jani Nikula <jani.nikula@linux.intel.com>
wrote:

> On Tue, 05 May 2026, ChunAn Wu <an.wu@canonical.com> wrote:
> > When Fn+F4 triggers mirror mode, the BIOS/EC reconfigures a TC port
> > mode (e.g. DP-Alt to TBT-Alt via UCSI) without generating HPD. The
> > driver's cached TC mode becomes stale, so intel_dp_aux_xfer() uses
> > the wrong power domain and IO routing, and AUX transactions fail.
> >
> > This occurs when the USB-C-to-HDMI dongle does not support TBT, so
> > the mode switch creates a mismatch. It also occurs with slow monitors
> > whose delayed HPD/DDC/EDID recovery causes the Type-C layer to settle
> > on a stale tbt-alt state before the dongle finishes re-negotiation.
> >
> > Add intel_tc_port_aux_recover() to detect hardware/cached TC mode
> > divergence and reset the PHY. Wrap it with
> > intel_dp_aux_xfer_with_recovery() to retry with corrected settings.
> >
> > Tested on Panther Lake and Lunar Lake with a BenQ HDMI monitor via
> > USB-C-to-HDMI dongle.
>
> What are the changes between v1 and v2?
>
> >
> > Signed-off-by: ChunAn Wu <an.wu@canonical.com>
> > ---
> >  drivers/gpu/drm/i915/display/intel_dp_aux.c | 41 +++++++++--
> >  drivers/gpu/drm/i915/display/intel_tc.c     | 75 +++++++++++++++++++++
> >  drivers/gpu/drm/i915/display/intel_tc.h     |  1 +
> >  3 files changed, 113 insertions(+), 4 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux.c
> b/drivers/gpu/drm/i915/display/intel_dp_aux.c
> > index b20ec3e589fa..d496b2231656 100644
> > --- a/drivers/gpu/drm/i915/display/intel_dp_aux.c
> > +++ b/drivers/gpu/drm/i915/display/intel_dp_aux.c
> > @@ -481,6 +481,39 @@ static u32 intel_dp_aux_xfer_flags(const struct
> drm_dp_aux_msg *msg)
> >       return 0;
> >  }
> >
> > +/**
> > + * intel_dp_aux_xfer_with_recovery - AUX transfer with TC port recovery
> > + * @intel_dp: the DP port
> > + * @send: buffer of bytes to send
> > + * @send_bytes: number of bytes to send
> > + * @recv: buffer to store received reply
> > + * @recv_size: maximum number of bytes to receive
> > + * @aux_send_ctl_flags: extra flags for the AUX send control register
> > + *
> > + * Wrapper around intel_dp_aux_xfer() that attempts to recover from an
> > + * external TC port mode change (e.g., dp-alt -> tbt-alt via hotkey BIOS
> > + * action) when the initial AUX transfer fails. On failure, recovery is
> > + * attempted once via intel_tc_port_aux_recover() before retrying.
> > + *
> > + * Returns: number of received bytes on success, negative error code on
> failure.
> > + */
> > +static int
> > +intel_dp_aux_xfer_with_recovery(struct intel_dp *intel_dp,
> > +                             const u8 *send, int send_bytes,
> > +                             u8 *recv, int recv_size,
> > +                             u32 aux_send_ctl_flags)
> > +{
> > +     struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
> > +     int ret;
> > +
> > +     ret = intel_dp_aux_xfer(intel_dp, send, send_bytes, recv,
> recv_size,
> > +                             aux_send_ctl_flags);
> > +     if (ret < 0 && intel_tc_port_aux_recover(dig_port))
> > +             ret = intel_dp_aux_xfer(intel_dp, send, send_bytes, recv,
> > +                                     recv_size, aux_send_ctl_flags);
> > +     return ret;
> > +}
> > +
> >  static ssize_t
> >  intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg
> *msg)
> >  {
> > @@ -508,8 +541,8 @@ intel_dp_aux_transfer(struct drm_dp_aux *aux, struct
> drm_dp_aux_msg *msg)
> >               if (msg->buffer)
> >                       memcpy(txbuf + HEADER_SIZE, msg->buffer,
> msg->size);
> >
> > -             ret = intel_dp_aux_xfer(intel_dp, txbuf, txsize,
> > -                                     rxbuf, rxsize, flags);
> > +             ret = intel_dp_aux_xfer_with_recovery(intel_dp, txbuf,
> txsize,
> > +                                                   rxbuf, rxsize,
> flags);
> >               if (ret > 0) {
> >                       msg->reply = rxbuf[0] >> 4;
> >
> > @@ -531,8 +564,8 @@ intel_dp_aux_transfer(struct drm_dp_aux *aux, struct
> drm_dp_aux_msg *msg)
> >               if (drm_WARN_ON(display->drm, rxsize > 20))
> >                       return -E2BIG;
> >
> > -             ret = intel_dp_aux_xfer(intel_dp, txbuf, txsize,
> > -                                     rxbuf, rxsize, flags);
> > +             ret = intel_dp_aux_xfer_with_recovery(intel_dp, txbuf,
> txsize,
> > +                                                   rxbuf, rxsize,
> flags);
> >               if (ret > 0) {
> >                       msg->reply = rxbuf[0] >> 4;
> >                       /*
> > diff --git a/drivers/gpu/drm/i915/display/intel_tc.c
> b/drivers/gpu/drm/i915/display/intel_tc.c
> > index a21dd4e3fe4c..1b23161e4eea 100644
> > --- a/drivers/gpu/drm/i915/display/intel_tc.c
> > +++ b/drivers/gpu/drm/i915/display/intel_tc.c
> > @@ -1861,6 +1861,81 @@ void intel_tc_port_link_cancel_reset_work(struct
> intel_digital_port *dig_port)
> >       cancel_delayed_work(&tc->link_reset_work);
> >  }
> >
> > +/**
> > + * intel_tc_port_aux_recover - Recover AUX channel after external TC
> mode change
> > + * @dig_port: digital port
> > + *
> > + * When firmware causes a TC port mode change (e.g., dp-alt ->
> disconnect ->
> > + * tbt-alt) during a hotkey-triggered display mode switch, AUX
> transactions
> > + * using the stale power domain and IO flags will fail with timeouts or
> errors.
> > + * This happens because:
> > + *
> > + *   1. The display driver holds the TC link in dp-alt mode
> (link_refcount > 0)
> > + *   2. Firmware (e.g., via HP WMI hotkey BIOS action) reconfigures the
> TC port
> > + *      mode externally without notifying the display driver
> > + *   3. tc->mode stays as TC_PORT_DP_ALT while HW transitions to a
> different
> > + *      mode, invalidating the AUX power domain and IO flags used by
> > + *      intel_dp_aux_xfer()
> > + *
> > + * This function detects the discrepancy between tc->mode and actual HW
> state,
> > + * and re-synchronizes them via a TC PHY reset. After recovery, AUX
> retries
> > + * will use the correct power domain and control flags.
> > + *
> > + * The link_refcount is temporarily cleared to allow
> intel_tc_port_reset_mode()
> > + * to proceed without the PHY-ownership assertion that fires when
> link_refcount
> > + * is non-zero and firmware has already released PHY ownership.
> > + *
> > + * Must be called outside the TC port lock (tc->lock).
> > + *
> > + * Returns: %true if recovery was performed and AUX can be retried,
> > + *          %false if recovery was not needed or not possible.
> > + */
> > +bool intel_tc_port_aux_recover(struct intel_digital_port *dig_port)
> > +{
> > +     struct intel_tc_port *tc;
> > +     struct intel_display *display;
> > +     bool recovered = false;
> > +
> > +     if (!intel_encoder_is_tc(&dig_port->base))
> > +             return false;
> > +
> > +     tc = to_tc_port(dig_port);
> > +     display = to_intel_display(dig_port);
> > +
> > +     mutex_lock(&tc->lock);
> > +
> > +     /*
> > +      * Recovery is only needed when the link is actively held AND the
> HW
> > +      * TC mode has diverged from the driver's cached state.
> > +      */
> > +     if (!tc->link_refcount || !intel_tc_port_needs_reset(tc))
> > +             goto out;
> > +
> > +     drm_dbg_kms(display->drm,
> > +                 "Port %s: AUX recover: external TC mode change
> detected (%s -> HW), reconnecting TC PHY\n",
> > +                 tc->port_name, tc_port_mode_name(tc->mode));
> > +
> > +     /*
> > +      * Temporarily clear link_refcount so intel_tc_port_reset_mode()
> can
> > +      * run the PHY disconnect/connect cycle without the ownership
> assertion
> > +      * that fires when link_refcount > 0 and firmware has already
> released
> > +      * PHY ownership externally.
> > +      */
> > +     tc->link_refcount = 0;
> > +     intel_tc_port_reset_mode(tc, 1, false);
> > +     tc->link_refcount = 1;
> > +
> > +     recovered = tc->mode != TC_PORT_DISCONNECTED;
> > +     if (!recovered)
> > +             drm_warn(display->drm,
> > +                      "Port %s: AUX recover: failed to restore TC port
> mode\n",
> > +                      tc->port_name);
> > +
> > +out:
> > +     mutex_unlock(&tc->lock);
> > +     return recovered;
> > +}
> > +
> >  static void __intel_tc_port_lock(struct intel_tc_port *tc,
> >                                int required_lanes)
> >  {
> > diff --git a/drivers/gpu/drm/i915/display/intel_tc.h
> b/drivers/gpu/drm/i915/display/intel_tc.h
> > index 6719aea5bd58..d44998a3081a 100644
> > --- a/drivers/gpu/drm/i915/display/intel_tc.h
> > +++ b/drivers/gpu/drm/i915/display/intel_tc.h
> > @@ -108,6 +108,7 @@ bool intel_tc_port_ref_held(struct
> intel_digital_port *dig_port);
> >  bool intel_tc_port_link_needs_reset(struct intel_digital_port
> *dig_port);
> >  bool intel_tc_port_link_reset(struct intel_digital_port *dig_port);
> >  void intel_tc_port_link_cancel_reset_work(struct intel_digital_port
> *dig_port);
> > +bool intel_tc_port_aux_recover(struct intel_digital_port *dig_port);
> >
> >  int intel_tc_port_init(struct intel_digital_port *dig_port, bool
> is_legacy);
> >  void intel_tc_port_cleanup(struct intel_digital_port *dig_port);
>
> --
> Jani Nikula, Intel
>

[-- Attachment #2: Type: text/html, Size: 12400 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2] drm/i915/tc: Recover AUX channel after external TC port mode change
  2026-05-05  3:35 [PATCH v2] drm/i915/tc: Recover AUX channel after external TC port mode change ChunAn Wu
  2026-05-05  5:12 ` ✗ i915.CI.BAT: failure for drm/i915/tc: Recover AUX channel after external TC port mode change (rev2) Patchwork
  2026-05-05  8:02 ` [PATCH v2] drm/i915/tc: Recover AUX channel after external TC port mode change Jani Nikula
@ 2026-05-05  8:34 ` Imre Deak
  2026-05-05 10:11   ` An Wu
  2026-05-06 18:55 ` Ville Syrjälä
  3 siblings, 1 reply; 8+ messages in thread
From: Imre Deak @ 2026-05-05  8:34 UTC (permalink / raw)
  To: ChunAn Wu
  Cc: Jani Nikula, Rodrigo Vivi, Joonas Lahtinen, Tvrtko Ursulin,
	David Airlie, Simona Vetter, intel-gfx, intel-xe, dri-devel,
	linux-kernel

On Tue, May 05, 2026 at 11:35:57AM +0800, ChunAn Wu wrote:
> When Fn+F4 triggers mirror mode, the BIOS/EC reconfigures a TC port
> mode (e.g. DP-Alt to TBT-Alt via UCSI) without generating HPD. The
> driver's cached TC mode becomes stale, so intel_dp_aux_xfer() uses
> the wrong power domain and IO routing, and AUX transactions fail.
> 
> This occurs when the USB-C-to-HDMI dongle does not support TBT, so
> the mode switch creates a mismatch. It also occurs with slow monitors
> whose delayed HPD/DDC/EDID recovery causes the Type-C layer to settle
> on a stale tbt-alt state before the dongle finishes re-negotiation.
> 
> Add intel_tc_port_aux_recover() to detect hardware/cached TC mode
> divergence and reset the PHY. Wrap it with
> intel_dp_aux_xfer_with_recovery() to retry with corrected settings.
> 
> Tested on Panther Lake and Lunar Lake with a BenQ HDMI monitor via
> USB-C-to-HDMI dongle.
> 
> Signed-off-by: ChunAn Wu <an.wu@canonical.com>
> ---
>
> [...]
>
> +/**
> + * intel_tc_port_aux_recover - Recover AUX channel after external TC mode change
> + * @dig_port: digital port
> + *
> + * When firmware causes a TC port mode change (e.g., dp-alt -> disconnect ->
> + * tbt-alt) during a hotkey-triggered display mode switch, AUX transactions
> + * using the stale power domain and IO flags will fail with timeouts or errors.
> + * This happens because:
> + *
> + *   1. The display driver holds the TC link in dp-alt mode (link_refcount > 0)
> + *   2. Firmware (e.g., via HP WMI hotkey BIOS action) reconfigures the TC port
> + *      mode externally without notifying the display driver
> + *   3. tc->mode stays as TC_PORT_DP_ALT while HW transitions to a different
> + *      mode, invalidating the AUX power domain and IO flags used by
> + *      intel_dp_aux_xfer()
> + *
> + * This function detects the discrepancy between tc->mode and actual HW state,
> + * and re-synchronizes them via a TC PHY reset. After recovery, AUX retries
> + * will use the correct power domain and control flags.
> + *
> + * The link_refcount is temporarily cleared to allow intel_tc_port_reset_mode()
> + * to proceed without the PHY-ownership assertion that fires when link_refcount
> + * is non-zero and firmware has already released PHY ownership.
> + *
> + * Must be called outside the TC port lock (tc->lock).
> + *
> + * Returns: %true if recovery was performed and AUX can be retried,
> + *          %false if recovery was not needed or not possible.
> + */
> +bool intel_tc_port_aux_recover(struct intel_digital_port *dig_port)
> +{
> +	struct intel_tc_port *tc;
> +	struct intel_display *display;
> +	bool recovered = false;
> +
> +	if (!intel_encoder_is_tc(&dig_port->base))
> +		return false;
> +
> +	tc = to_tc_port(dig_port);
> +	display = to_intel_display(dig_port);
> +
> +	mutex_lock(&tc->lock);
> +
> +	/*
> +	 * Recovery is only needed when the link is actively held AND the HW
> +	 * TC mode has diverged from the driver's cached state.
> +	 */
> +	if (!tc->link_refcount || !intel_tc_port_needs_reset(tc))
> +		goto out;
> +
> +	drm_dbg_kms(display->drm,
> +		    "Port %s: AUX recover: external TC mode change detected (%s -> HW), reconnecting TC PHY\n",
> +		    tc->port_name, tc_port_mode_name(tc->mode));
> +
> +	/*
> +	 * Temporarily clear link_refcount so intel_tc_port_reset_mode() can
> +	 * run the PHY disconnect/connect cycle without the ownership assertion
> +	 * that fires when link_refcount > 0 and firmware has already released
> +	 * PHY ownership externally.
> +	 */
> +	tc->link_refcount = 0;

This looks wrong, NAK from my side:

A non-zero link_refcount guards an active output, which must maintain
the TypeC mode with which the output was enabled. So that TypeC mode
cannot be reset from under the active output as done in this patch.

The commit message fails to mention how the display driver is supposed
to notice that the user have pressed F4 and the firmware has
reconfigured things in the background. Assuming that the FW's
reconfiguration in the background is actually a valid thing to do, i.e.
doesn't result in getting the active TypeC port into a broken state, the
transition to the new TypeC mode should happen in the regular way: via
an HPD event, if neecessary using the
drm_connector_funcs::oob_hotplug_event interface, disabling any active
outputs and _then_ switching the driver's idea of the TypeC mode to
reflect the HW state.

> +	intel_tc_port_reset_mode(tc, 1, false);
> +	tc->link_refcount = 1;
> +
> +	recovered = tc->mode != TC_PORT_DISCONNECTED;
> +	if (!recovered)
> +		drm_warn(display->drm,
> +			 "Port %s: AUX recover: failed to restore TC port mode\n",
> +			 tc->port_name);
> +
> +out:
> +	mutex_unlock(&tc->lock);
> +	return recovered;
> +}
> +
>  static void __intel_tc_port_lock(struct intel_tc_port *tc,
>  				 int required_lanes)
>  {
> diff --git a/drivers/gpu/drm/i915/display/intel_tc.h b/drivers/gpu/drm/i915/display/intel_tc.h
> index 6719aea5bd58..d44998a3081a 100644
> --- a/drivers/gpu/drm/i915/display/intel_tc.h
> +++ b/drivers/gpu/drm/i915/display/intel_tc.h
> @@ -108,6 +108,7 @@ bool intel_tc_port_ref_held(struct intel_digital_port *dig_port);
>  bool intel_tc_port_link_needs_reset(struct intel_digital_port *dig_port);
>  bool intel_tc_port_link_reset(struct intel_digital_port *dig_port);
>  void intel_tc_port_link_cancel_reset_work(struct intel_digital_port *dig_port);
> +bool intel_tc_port_aux_recover(struct intel_digital_port *dig_port);
>  
>  int intel_tc_port_init(struct intel_digital_port *dig_port, bool is_legacy);
>  void intel_tc_port_cleanup(struct intel_digital_port *dig_port);
> -- 
> 2.34.1
> 

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2] drm/i915/tc: Recover AUX channel after external TC port mode change
  2026-05-05  8:34 ` Imre Deak
@ 2026-05-05 10:11   ` An Wu
  0 siblings, 0 replies; 8+ messages in thread
From: An Wu @ 2026-05-05 10:11 UTC (permalink / raw)
  To: imre.deak
  Cc: Jani Nikula, Rodrigo Vivi, Joonas Lahtinen, Tvrtko Ursulin,
	David Airlie, Simona Vetter, intel-gfx, intel-xe, dri-devel,
	linux-kernel

[-- Attachment #1: Type: text/plain, Size: 6570 bytes --]

Hi Imre
Thank you for pointing out my mistake and kindly providing clear guidance.
I’ll fix the issue, run some tests, and update the commit description
accordingly.
Thank you.

BR
    An

On Tue, May 5, 2026 at 4:35 PM Imre Deak <imre.deak@intel.com> wrote:

> On Tue, May 05, 2026 at 11:35:57AM +0800, ChunAn Wu wrote:
> > When Fn+F4 triggers mirror mode, the BIOS/EC reconfigures a TC port
> > mode (e.g. DP-Alt to TBT-Alt via UCSI) without generating HPD. The
> > driver's cached TC mode becomes stale, so intel_dp_aux_xfer() uses
> > the wrong power domain and IO routing, and AUX transactions fail.
> >
> > This occurs when the USB-C-to-HDMI dongle does not support TBT, so
> > the mode switch creates a mismatch. It also occurs with slow monitors
> > whose delayed HPD/DDC/EDID recovery causes the Type-C layer to settle
> > on a stale tbt-alt state before the dongle finishes re-negotiation.
> >
> > Add intel_tc_port_aux_recover() to detect hardware/cached TC mode
> > divergence and reset the PHY. Wrap it with
> > intel_dp_aux_xfer_with_recovery() to retry with corrected settings.
> >
> > Tested on Panther Lake and Lunar Lake with a BenQ HDMI monitor via
> > USB-C-to-HDMI dongle.
> >
> > Signed-off-by: ChunAn Wu <an.wu@canonical.com>
> > ---
> >
> > [...]
> >
> > +/**
> > + * intel_tc_port_aux_recover - Recover AUX channel after external TC
> mode change
> > + * @dig_port: digital port
> > + *
> > + * When firmware causes a TC port mode change (e.g., dp-alt ->
> disconnect ->
> > + * tbt-alt) during a hotkey-triggered display mode switch, AUX
> transactions
> > + * using the stale power domain and IO flags will fail with timeouts or
> errors.
> > + * This happens because:
> > + *
> > + *   1. The display driver holds the TC link in dp-alt mode
> (link_refcount > 0)
> > + *   2. Firmware (e.g., via HP WMI hotkey BIOS action) reconfigures the
> TC port
> > + *      mode externally without notifying the display driver
> > + *   3. tc->mode stays as TC_PORT_DP_ALT while HW transitions to a
> different
> > + *      mode, invalidating the AUX power domain and IO flags used by
> > + *      intel_dp_aux_xfer()
> > + *
> > + * This function detects the discrepancy between tc->mode and actual HW
> state,
> > + * and re-synchronizes them via a TC PHY reset. After recovery, AUX
> retries
> > + * will use the correct power domain and control flags.
> > + *
> > + * The link_refcount is temporarily cleared to allow
> intel_tc_port_reset_mode()
> > + * to proceed without the PHY-ownership assertion that fires when
> link_refcount
> > + * is non-zero and firmware has already released PHY ownership.
> > + *
> > + * Must be called outside the TC port lock (tc->lock).
> > + *
> > + * Returns: %true if recovery was performed and AUX can be retried,
> > + *          %false if recovery was not needed or not possible.
> > + */
> > +bool intel_tc_port_aux_recover(struct intel_digital_port *dig_port)
> > +{
> > +     struct intel_tc_port *tc;
> > +     struct intel_display *display;
> > +     bool recovered = false;
> > +
> > +     if (!intel_encoder_is_tc(&dig_port->base))
> > +             return false;
> > +
> > +     tc = to_tc_port(dig_port);
> > +     display = to_intel_display(dig_port);
> > +
> > +     mutex_lock(&tc->lock);
> > +
> > +     /*
> > +      * Recovery is only needed when the link is actively held AND the
> HW
> > +      * TC mode has diverged from the driver's cached state.
> > +      */
> > +     if (!tc->link_refcount || !intel_tc_port_needs_reset(tc))
> > +             goto out;
> > +
> > +     drm_dbg_kms(display->drm,
> > +                 "Port %s: AUX recover: external TC mode change
> detected (%s -> HW), reconnecting TC PHY\n",
> > +                 tc->port_name, tc_port_mode_name(tc->mode));
> > +
> > +     /*
> > +      * Temporarily clear link_refcount so intel_tc_port_reset_mode()
> can
> > +      * run the PHY disconnect/connect cycle without the ownership
> assertion
> > +      * that fires when link_refcount > 0 and firmware has already
> released
> > +      * PHY ownership externally.
> > +      */
> > +     tc->link_refcount = 0;
>
> This looks wrong, NAK from my side:
>
> A non-zero link_refcount guards an active output, which must maintain
> the TypeC mode with which the output was enabled. So that TypeC mode
> cannot be reset from under the active output as done in this patch.
>
> The commit message fails to mention how the display driver is supposed
> to notice that the user have pressed F4 and the firmware has
> reconfigured things in the background. Assuming that the FW's
> reconfiguration in the background is actually a valid thing to do, i.e.
> doesn't result in getting the active TypeC port into a broken state, the
> transition to the new TypeC mode should happen in the regular way: via
> an HPD event, if neecessary using the
> drm_connector_funcs::oob_hotplug_event interface, disabling any active
> outputs and _then_ switching the driver's idea of the TypeC mode to
> reflect the HW state.
>
> > +     intel_tc_port_reset_mode(tc, 1, false);
> > +     tc->link_refcount = 1;
> > +
> > +     recovered = tc->mode != TC_PORT_DISCONNECTED;
> > +     if (!recovered)
> > +             drm_warn(display->drm,
> > +                      "Port %s: AUX recover: failed to restore TC port
> mode\n",
> > +                      tc->port_name);
> > +
> > +out:
> > +     mutex_unlock(&tc->lock);
> > +     return recovered;
> > +}
> > +
> >  static void __intel_tc_port_lock(struct intel_tc_port *tc,
> >                                int required_lanes)
> >  {
> > diff --git a/drivers/gpu/drm/i915/display/intel_tc.h
> b/drivers/gpu/drm/i915/display/intel_tc.h
> > index 6719aea5bd58..d44998a3081a 100644
> > --- a/drivers/gpu/drm/i915/display/intel_tc.h
> > +++ b/drivers/gpu/drm/i915/display/intel_tc.h
> > @@ -108,6 +108,7 @@ bool intel_tc_port_ref_held(struct
> intel_digital_port *dig_port);
> >  bool intel_tc_port_link_needs_reset(struct intel_digital_port
> *dig_port);
> >  bool intel_tc_port_link_reset(struct intel_digital_port *dig_port);
> >  void intel_tc_port_link_cancel_reset_work(struct intel_digital_port
> *dig_port);
> > +bool intel_tc_port_aux_recover(struct intel_digital_port *dig_port);
> >
> >  int intel_tc_port_init(struct intel_digital_port *dig_port, bool
> is_legacy);
> >  void intel_tc_port_cleanup(struct intel_digital_port *dig_port);
> > --
> > 2.34.1
> >
>

[-- Attachment #2: Type: text/html, Size: 7842 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2] drm/i915/tc: Recover AUX channel after external TC port mode change
  2026-05-05  3:35 [PATCH v2] drm/i915/tc: Recover AUX channel after external TC port mode change ChunAn Wu
                   ` (2 preceding siblings ...)
  2026-05-05  8:34 ` Imre Deak
@ 2026-05-06 18:55 ` Ville Syrjälä
  2026-05-07  1:58   ` An Wu
  3 siblings, 1 reply; 8+ messages in thread
From: Ville Syrjälä @ 2026-05-06 18:55 UTC (permalink / raw)
  To: ChunAn Wu
  Cc: Jani Nikula, Rodrigo Vivi, Joonas Lahtinen, Tvrtko Ursulin,
	David Airlie, Simona Vetter, intel-gfx, intel-xe, dri-devel,
	linux-kernel

On Tue, May 05, 2026 at 11:35:57AM +0800, ChunAn Wu wrote:
> When Fn+F4 triggers mirror mode, the BIOS/EC reconfigures a TC port
> mode (e.g. DP-Alt to TBT-Alt via UCSI) without generating HPD. The
> driver's cached TC mode becomes stale, so intel_dp_aux_xfer() uses
> the wrong power domain and IO routing, and AUX transactions fail.
> 
> This occurs when the USB-C-to-HDMI dongle does not support TBT, so
> the mode switch creates a mismatch. It also occurs with slow monitors
> whose delayed HPD/DDC/EDID recovery causes the Type-C layer to settle
> on a stale tbt-alt state before the dongle finishes re-negotiation.
> 
> Add intel_tc_port_aux_recover() to detect hardware/cached TC mode
> divergence and reset the PHY. Wrap it with
> intel_dp_aux_xfer_with_recovery() to retry with corrected settings.
> 
> Tested on Panther Lake and Lunar Lake with a BenQ HDMI monitor via
> USB-C-to-HDMI dongle.

This sounds like a completely insane thing for the firmware to do.
It's not supposed to screw with the hardware while the driver is in
control.

Please file a bug in gitlab and also attach full acpidumps there so
that we can check if that's where the insanity lives. This is isn't
some Fujitsu-Siemens laptop is it? Those have historically known to
do stupid stuff like this...

> 
> Signed-off-by: ChunAn Wu <an.wu@canonical.com>
> ---
>  drivers/gpu/drm/i915/display/intel_dp_aux.c | 41 +++++++++--
>  drivers/gpu/drm/i915/display/intel_tc.c     | 75 +++++++++++++++++++++
>  drivers/gpu/drm/i915/display/intel_tc.h     |  1 +
>  3 files changed, 113 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux.c b/drivers/gpu/drm/i915/display/intel_dp_aux.c
> index b20ec3e589fa..d496b2231656 100644
> --- a/drivers/gpu/drm/i915/display/intel_dp_aux.c
> +++ b/drivers/gpu/drm/i915/display/intel_dp_aux.c
> @@ -481,6 +481,39 @@ static u32 intel_dp_aux_xfer_flags(const struct drm_dp_aux_msg *msg)
>  	return 0;
>  }
>  
> +/**
> + * intel_dp_aux_xfer_with_recovery - AUX transfer with TC port recovery
> + * @intel_dp: the DP port
> + * @send: buffer of bytes to send
> + * @send_bytes: number of bytes to send
> + * @recv: buffer to store received reply
> + * @recv_size: maximum number of bytes to receive
> + * @aux_send_ctl_flags: extra flags for the AUX send control register
> + *
> + * Wrapper around intel_dp_aux_xfer() that attempts to recover from an
> + * external TC port mode change (e.g., dp-alt -> tbt-alt via hotkey BIOS
> + * action) when the initial AUX transfer fails. On failure, recovery is
> + * attempted once via intel_tc_port_aux_recover() before retrying.
> + *
> + * Returns: number of received bytes on success, negative error code on failure.
> + */
> +static int
> +intel_dp_aux_xfer_with_recovery(struct intel_dp *intel_dp,
> +				const u8 *send, int send_bytes,
> +				u8 *recv, int recv_size,
> +				u32 aux_send_ctl_flags)
> +{
> +	struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
> +	int ret;
> +
> +	ret = intel_dp_aux_xfer(intel_dp, send, send_bytes, recv, recv_size,
> +				aux_send_ctl_flags);
> +	if (ret < 0 && intel_tc_port_aux_recover(dig_port))
> +		ret = intel_dp_aux_xfer(intel_dp, send, send_bytes, recv,
> +					recv_size, aux_send_ctl_flags);
> +	return ret;
> +}
> +
>  static ssize_t
>  intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
>  {
> @@ -508,8 +541,8 @@ intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
>  		if (msg->buffer)
>  			memcpy(txbuf + HEADER_SIZE, msg->buffer, msg->size);
>  
> -		ret = intel_dp_aux_xfer(intel_dp, txbuf, txsize,
> -					rxbuf, rxsize, flags);
> +		ret = intel_dp_aux_xfer_with_recovery(intel_dp, txbuf, txsize,
> +						      rxbuf, rxsize, flags);
>  		if (ret > 0) {
>  			msg->reply = rxbuf[0] >> 4;
>  
> @@ -531,8 +564,8 @@ intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
>  		if (drm_WARN_ON(display->drm, rxsize > 20))
>  			return -E2BIG;
>  
> -		ret = intel_dp_aux_xfer(intel_dp, txbuf, txsize,
> -					rxbuf, rxsize, flags);
> +		ret = intel_dp_aux_xfer_with_recovery(intel_dp, txbuf, txsize,
> +						      rxbuf, rxsize, flags);
>  		if (ret > 0) {
>  			msg->reply = rxbuf[0] >> 4;
>  			/*
> diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c
> index a21dd4e3fe4c..1b23161e4eea 100644
> --- a/drivers/gpu/drm/i915/display/intel_tc.c
> +++ b/drivers/gpu/drm/i915/display/intel_tc.c
> @@ -1861,6 +1861,81 @@ void intel_tc_port_link_cancel_reset_work(struct intel_digital_port *dig_port)
>  	cancel_delayed_work(&tc->link_reset_work);
>  }
>  
> +/**
> + * intel_tc_port_aux_recover - Recover AUX channel after external TC mode change
> + * @dig_port: digital port
> + *
> + * When firmware causes a TC port mode change (e.g., dp-alt -> disconnect ->
> + * tbt-alt) during a hotkey-triggered display mode switch, AUX transactions
> + * using the stale power domain and IO flags will fail with timeouts or errors.
> + * This happens because:
> + *
> + *   1. The display driver holds the TC link in dp-alt mode (link_refcount > 0)
> + *   2. Firmware (e.g., via HP WMI hotkey BIOS action) reconfigures the TC port
> + *      mode externally without notifying the display driver
> + *   3. tc->mode stays as TC_PORT_DP_ALT while HW transitions to a different
> + *      mode, invalidating the AUX power domain and IO flags used by
> + *      intel_dp_aux_xfer()
> + *
> + * This function detects the discrepancy between tc->mode and actual HW state,
> + * and re-synchronizes them via a TC PHY reset. After recovery, AUX retries
> + * will use the correct power domain and control flags.
> + *
> + * The link_refcount is temporarily cleared to allow intel_tc_port_reset_mode()
> + * to proceed without the PHY-ownership assertion that fires when link_refcount
> + * is non-zero and firmware has already released PHY ownership.
> + *
> + * Must be called outside the TC port lock (tc->lock).
> + *
> + * Returns: %true if recovery was performed and AUX can be retried,
> + *          %false if recovery was not needed or not possible.
> + */
> +bool intel_tc_port_aux_recover(struct intel_digital_port *dig_port)
> +{
> +	struct intel_tc_port *tc;
> +	struct intel_display *display;
> +	bool recovered = false;
> +
> +	if (!intel_encoder_is_tc(&dig_port->base))
> +		return false;
> +
> +	tc = to_tc_port(dig_port);
> +	display = to_intel_display(dig_port);
> +
> +	mutex_lock(&tc->lock);
> +
> +	/*
> +	 * Recovery is only needed when the link is actively held AND the HW
> +	 * TC mode has diverged from the driver's cached state.
> +	 */
> +	if (!tc->link_refcount || !intel_tc_port_needs_reset(tc))
> +		goto out;
> +
> +	drm_dbg_kms(display->drm,
> +		    "Port %s: AUX recover: external TC mode change detected (%s -> HW), reconnecting TC PHY\n",
> +		    tc->port_name, tc_port_mode_name(tc->mode));
> +
> +	/*
> +	 * Temporarily clear link_refcount so intel_tc_port_reset_mode() can
> +	 * run the PHY disconnect/connect cycle without the ownership assertion
> +	 * that fires when link_refcount > 0 and firmware has already released
> +	 * PHY ownership externally.
> +	 */
> +	tc->link_refcount = 0;
> +	intel_tc_port_reset_mode(tc, 1, false);
> +	tc->link_refcount = 1;
> +
> +	recovered = tc->mode != TC_PORT_DISCONNECTED;
> +	if (!recovered)
> +		drm_warn(display->drm,
> +			 "Port %s: AUX recover: failed to restore TC port mode\n",
> +			 tc->port_name);
> +
> +out:
> +	mutex_unlock(&tc->lock);
> +	return recovered;
> +}
> +
>  static void __intel_tc_port_lock(struct intel_tc_port *tc,
>  				 int required_lanes)
>  {
> diff --git a/drivers/gpu/drm/i915/display/intel_tc.h b/drivers/gpu/drm/i915/display/intel_tc.h
> index 6719aea5bd58..d44998a3081a 100644
> --- a/drivers/gpu/drm/i915/display/intel_tc.h
> +++ b/drivers/gpu/drm/i915/display/intel_tc.h
> @@ -108,6 +108,7 @@ bool intel_tc_port_ref_held(struct intel_digital_port *dig_port);
>  bool intel_tc_port_link_needs_reset(struct intel_digital_port *dig_port);
>  bool intel_tc_port_link_reset(struct intel_digital_port *dig_port);
>  void intel_tc_port_link_cancel_reset_work(struct intel_digital_port *dig_port);
> +bool intel_tc_port_aux_recover(struct intel_digital_port *dig_port);
>  
>  int intel_tc_port_init(struct intel_digital_port *dig_port, bool is_legacy);
>  void intel_tc_port_cleanup(struct intel_digital_port *dig_port);
> -- 
> 2.34.1

-- 
Ville Syrjälä
Intel

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2] drm/i915/tc: Recover AUX channel after external TC port mode change
  2026-05-06 18:55 ` Ville Syrjälä
@ 2026-05-07  1:58   ` An Wu
  0 siblings, 0 replies; 8+ messages in thread
From: An Wu @ 2026-05-07  1:58 UTC (permalink / raw)
  To: Ville Syrjälä
  Cc: Jani Nikula, Rodrigo Vivi, Joonas Lahtinen, Tvrtko Ursulin,
	David Airlie, Simona Vetter, intel-gfx, intel-xe, dri-devel,
	linux-kernel

[-- Attachment #1: Type: text/plain, Size: 10076 bytes --]

Hi Ville
I have filed a bug (
https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/7918)
Thank you for your comment.

BR
    An

On Thu, May 7, 2026 at 2:55 AM Ville Syrjälä <ville.syrjala@linux.intel.com>
wrote:

> On Tue, May 05, 2026 at 11:35:57AM +0800, ChunAn Wu wrote:
> > When Fn+F4 triggers mirror mode, the BIOS/EC reconfigures a TC port
> > mode (e.g. DP-Alt to TBT-Alt via UCSI) without generating HPD. The
> > driver's cached TC mode becomes stale, so intel_dp_aux_xfer() uses
> > the wrong power domain and IO routing, and AUX transactions fail.
> >
> > This occurs when the USB-C-to-HDMI dongle does not support TBT, so
> > the mode switch creates a mismatch. It also occurs with slow monitors
> > whose delayed HPD/DDC/EDID recovery causes the Type-C layer to settle
> > on a stale tbt-alt state before the dongle finishes re-negotiation.
> >
> > Add intel_tc_port_aux_recover() to detect hardware/cached TC mode
> > divergence and reset the PHY. Wrap it with
> > intel_dp_aux_xfer_with_recovery() to retry with corrected settings.
> >
> > Tested on Panther Lake and Lunar Lake with a BenQ HDMI monitor via
> > USB-C-to-HDMI dongle.
>
> This sounds like a completely insane thing for the firmware to do.
> It's not supposed to screw with the hardware while the driver is in
> control.
>
> Please file a bug in gitlab and also attach full acpidumps there so
> that we can check if that's where the insanity lives. This is isn't
> some Fujitsu-Siemens laptop is it? Those have historically known to
> do stupid stuff like this...
>
> >
> > Signed-off-by: ChunAn Wu <an.wu@canonical.com>
> > ---
> >  drivers/gpu/drm/i915/display/intel_dp_aux.c | 41 +++++++++--
> >  drivers/gpu/drm/i915/display/intel_tc.c     | 75 +++++++++++++++++++++
> >  drivers/gpu/drm/i915/display/intel_tc.h     |  1 +
> >  3 files changed, 113 insertions(+), 4 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux.c
> b/drivers/gpu/drm/i915/display/intel_dp_aux.c
> > index b20ec3e589fa..d496b2231656 100644
> > --- a/drivers/gpu/drm/i915/display/intel_dp_aux.c
> > +++ b/drivers/gpu/drm/i915/display/intel_dp_aux.c
> > @@ -481,6 +481,39 @@ static u32 intel_dp_aux_xfer_flags(const struct
> drm_dp_aux_msg *msg)
> >       return 0;
> >  }
> >
> > +/**
> > + * intel_dp_aux_xfer_with_recovery - AUX transfer with TC port recovery
> > + * @intel_dp: the DP port
> > + * @send: buffer of bytes to send
> > + * @send_bytes: number of bytes to send
> > + * @recv: buffer to store received reply
> > + * @recv_size: maximum number of bytes to receive
> > + * @aux_send_ctl_flags: extra flags for the AUX send control register
> > + *
> > + * Wrapper around intel_dp_aux_xfer() that attempts to recover from an
> > + * external TC port mode change (e.g., dp-alt -> tbt-alt via hotkey BIOS
> > + * action) when the initial AUX transfer fails. On failure, recovery is
> > + * attempted once via intel_tc_port_aux_recover() before retrying.
> > + *
> > + * Returns: number of received bytes on success, negative error code on
> failure.
> > + */
> > +static int
> > +intel_dp_aux_xfer_with_recovery(struct intel_dp *intel_dp,
> > +                             const u8 *send, int send_bytes,
> > +                             u8 *recv, int recv_size,
> > +                             u32 aux_send_ctl_flags)
> > +{
> > +     struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
> > +     int ret;
> > +
> > +     ret = intel_dp_aux_xfer(intel_dp, send, send_bytes, recv,
> recv_size,
> > +                             aux_send_ctl_flags);
> > +     if (ret < 0 && intel_tc_port_aux_recover(dig_port))
> > +             ret = intel_dp_aux_xfer(intel_dp, send, send_bytes, recv,
> > +                                     recv_size, aux_send_ctl_flags);
> > +     return ret;
> > +}
> > +
> >  static ssize_t
> >  intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg
> *msg)
> >  {
> > @@ -508,8 +541,8 @@ intel_dp_aux_transfer(struct drm_dp_aux *aux, struct
> drm_dp_aux_msg *msg)
> >               if (msg->buffer)
> >                       memcpy(txbuf + HEADER_SIZE, msg->buffer,
> msg->size);
> >
> > -             ret = intel_dp_aux_xfer(intel_dp, txbuf, txsize,
> > -                                     rxbuf, rxsize, flags);
> > +             ret = intel_dp_aux_xfer_with_recovery(intel_dp, txbuf,
> txsize,
> > +                                                   rxbuf, rxsize,
> flags);
> >               if (ret > 0) {
> >                       msg->reply = rxbuf[0] >> 4;
> >
> > @@ -531,8 +564,8 @@ intel_dp_aux_transfer(struct drm_dp_aux *aux, struct
> drm_dp_aux_msg *msg)
> >               if (drm_WARN_ON(display->drm, rxsize > 20))
> >                       return -E2BIG;
> >
> > -             ret = intel_dp_aux_xfer(intel_dp, txbuf, txsize,
> > -                                     rxbuf, rxsize, flags);
> > +             ret = intel_dp_aux_xfer_with_recovery(intel_dp, txbuf,
> txsize,
> > +                                                   rxbuf, rxsize,
> flags);
> >               if (ret > 0) {
> >                       msg->reply = rxbuf[0] >> 4;
> >                       /*
> > diff --git a/drivers/gpu/drm/i915/display/intel_tc.c
> b/drivers/gpu/drm/i915/display/intel_tc.c
> > index a21dd4e3fe4c..1b23161e4eea 100644
> > --- a/drivers/gpu/drm/i915/display/intel_tc.c
> > +++ b/drivers/gpu/drm/i915/display/intel_tc.c
> > @@ -1861,6 +1861,81 @@ void intel_tc_port_link_cancel_reset_work(struct
> intel_digital_port *dig_port)
> >       cancel_delayed_work(&tc->link_reset_work);
> >  }
> >
> > +/**
> > + * intel_tc_port_aux_recover - Recover AUX channel after external TC
> mode change
> > + * @dig_port: digital port
> > + *
> > + * When firmware causes a TC port mode change (e.g., dp-alt ->
> disconnect ->
> > + * tbt-alt) during a hotkey-triggered display mode switch, AUX
> transactions
> > + * using the stale power domain and IO flags will fail with timeouts or
> errors.
> > + * This happens because:
> > + *
> > + *   1. The display driver holds the TC link in dp-alt mode
> (link_refcount > 0)
> > + *   2. Firmware (e.g., via HP WMI hotkey BIOS action) reconfigures the
> TC port
> > + *      mode externally without notifying the display driver
> > + *   3. tc->mode stays as TC_PORT_DP_ALT while HW transitions to a
> different
> > + *      mode, invalidating the AUX power domain and IO flags used by
> > + *      intel_dp_aux_xfer()
> > + *
> > + * This function detects the discrepancy between tc->mode and actual HW
> state,
> > + * and re-synchronizes them via a TC PHY reset. After recovery, AUX
> retries
> > + * will use the correct power domain and control flags.
> > + *
> > + * The link_refcount is temporarily cleared to allow
> intel_tc_port_reset_mode()
> > + * to proceed without the PHY-ownership assertion that fires when
> link_refcount
> > + * is non-zero and firmware has already released PHY ownership.
> > + *
> > + * Must be called outside the TC port lock (tc->lock).
> > + *
> > + * Returns: %true if recovery was performed and AUX can be retried,
> > + *          %false if recovery was not needed or not possible.
> > + */
> > +bool intel_tc_port_aux_recover(struct intel_digital_port *dig_port)
> > +{
> > +     struct intel_tc_port *tc;
> > +     struct intel_display *display;
> > +     bool recovered = false;
> > +
> > +     if (!intel_encoder_is_tc(&dig_port->base))
> > +             return false;
> > +
> > +     tc = to_tc_port(dig_port);
> > +     display = to_intel_display(dig_port);
> > +
> > +     mutex_lock(&tc->lock);
> > +
> > +     /*
> > +      * Recovery is only needed when the link is actively held AND the
> HW
> > +      * TC mode has diverged from the driver's cached state.
> > +      */
> > +     if (!tc->link_refcount || !intel_tc_port_needs_reset(tc))
> > +             goto out;
> > +
> > +     drm_dbg_kms(display->drm,
> > +                 "Port %s: AUX recover: external TC mode change
> detected (%s -> HW), reconnecting TC PHY\n",
> > +                 tc->port_name, tc_port_mode_name(tc->mode));
> > +
> > +     /*
> > +      * Temporarily clear link_refcount so intel_tc_port_reset_mode()
> can
> > +      * run the PHY disconnect/connect cycle without the ownership
> assertion
> > +      * that fires when link_refcount > 0 and firmware has already
> released
> > +      * PHY ownership externally.
> > +      */
> > +     tc->link_refcount = 0;
> > +     intel_tc_port_reset_mode(tc, 1, false);
> > +     tc->link_refcount = 1;
> > +
> > +     recovered = tc->mode != TC_PORT_DISCONNECTED;
> > +     if (!recovered)
> > +             drm_warn(display->drm,
> > +                      "Port %s: AUX recover: failed to restore TC port
> mode\n",
> > +                      tc->port_name);
> > +
> > +out:
> > +     mutex_unlock(&tc->lock);
> > +     return recovered;
> > +}
> > +
> >  static void __intel_tc_port_lock(struct intel_tc_port *tc,
> >                                int required_lanes)
> >  {
> > diff --git a/drivers/gpu/drm/i915/display/intel_tc.h
> b/drivers/gpu/drm/i915/display/intel_tc.h
> > index 6719aea5bd58..d44998a3081a 100644
> > --- a/drivers/gpu/drm/i915/display/intel_tc.h
> > +++ b/drivers/gpu/drm/i915/display/intel_tc.h
> > @@ -108,6 +108,7 @@ bool intel_tc_port_ref_held(struct
> intel_digital_port *dig_port);
> >  bool intel_tc_port_link_needs_reset(struct intel_digital_port
> *dig_port);
> >  bool intel_tc_port_link_reset(struct intel_digital_port *dig_port);
> >  void intel_tc_port_link_cancel_reset_work(struct intel_digital_port
> *dig_port);
> > +bool intel_tc_port_aux_recover(struct intel_digital_port *dig_port);
> >
> >  int intel_tc_port_init(struct intel_digital_port *dig_port, bool
> is_legacy);
> >  void intel_tc_port_cleanup(struct intel_digital_port *dig_port);
> > --
> > 2.34.1
>
> --
> Ville Syrjälä
> Intel
>

[-- Attachment #2: Type: text/html, Size: 12159 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-05-07  1:59 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-05  3:35 [PATCH v2] drm/i915/tc: Recover AUX channel after external TC port mode change ChunAn Wu
2026-05-05  5:12 ` ✗ i915.CI.BAT: failure for drm/i915/tc: Recover AUX channel after external TC port mode change (rev2) Patchwork
2026-05-05  8:02 ` [PATCH v2] drm/i915/tc: Recover AUX channel after external TC port mode change Jani Nikula
2026-05-05  8:20   ` An Wu
2026-05-05  8:34 ` Imre Deak
2026-05-05 10:11   ` An Wu
2026-05-06 18:55 ` Ville Syrjälä
2026-05-07  1:58   ` An Wu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox