Skip to content

Commit 2474038

Browse files
committed
thunderbolt: Improve redrive mode handling
When USB-C monitor is connected directly to Intel Barlow Ridge host, it goes into "redrive" mode that basically routes the DisplayPort signals directly from the GPU to the USB-C monitor without any tunneling needed. However, the host router must be powered on for this to work. Aaron reported that there are a couple of cases where this will not work with the current code: - Booting with USB-C monitor plugged in. - Plugging in USB-C monitor when the host router is in sleep state (runtime suspended). - Plugging in USB-C device while the system is in system sleep state. In all these cases once the host router is runtime suspended the picture on the connected USB-C display disappears too. This is certainly not what the user expected. For this reason improve the redrive mode handling to keep the host router from runtime suspending when detect that any of the above cases is happening. Fixes: a75e068 ("thunderbolt: Keep the domain powered when USB4 port is in redrive mode") Reported-by: Aaron Rainbolt <[email protected]> Closes: https://lore.kernel.org/linux-usb/20241009220118.70bfedd0@kf-ir16/ Cc: [email protected] Signed-off-by: Mika Westerberg <[email protected]>
1 parent e34f171 commit 2474038

File tree

1 file changed

+41
-0
lines changed
  • drivers/thunderbolt

1 file changed

+41
-0
lines changed

drivers/thunderbolt/tb.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2059,6 +2059,37 @@ static void tb_exit_redrive(struct tb_port *port)
20592059
}
20602060
}
20612061

2062+
static void tb_switch_enter_redrive(struct tb_switch *sw)
2063+
{
2064+
struct tb_port *port;
2065+
2066+
tb_switch_for_each_port(sw, port)
2067+
tb_enter_redrive(port);
2068+
}
2069+
2070+
/*
2071+
* Called during system and runtime suspend to forcefully exit redrive
2072+
* mode without querying whether the resource is available.
2073+
*/
2074+
static void tb_switch_exit_redrive(struct tb_switch *sw)
2075+
{
2076+
struct tb_port *port;
2077+
2078+
if (!(sw->quirks & QUIRK_KEEP_POWER_IN_DP_REDRIVE))
2079+
return;
2080+
2081+
tb_switch_for_each_port(sw, port) {
2082+
if (!tb_port_is_dpin(port))
2083+
continue;
2084+
2085+
if (port->redrive) {
2086+
port->redrive = false;
2087+
pm_runtime_put(&sw->dev);
2088+
tb_port_dbg(port, "exit redrive mode\n");
2089+
}
2090+
}
2091+
}
2092+
20622093
static void tb_dp_resource_unavailable(struct tb *tb, struct tb_port *port)
20632094
{
20642095
struct tb_port *in, *out;
@@ -2909,6 +2940,7 @@ static int tb_start(struct tb *tb, bool reset)
29092940
tb_create_usb3_tunnels(tb->root_switch);
29102941
/* Add DP IN resources for the root switch */
29112942
tb_add_dp_resources(tb->root_switch);
2943+
tb_switch_enter_redrive(tb->root_switch);
29122944
/* Make the discovered switches available to the userspace */
29132945
device_for_each_child(&tb->root_switch->dev, NULL,
29142946
tb_scan_finalize_switch);
@@ -2924,6 +2956,7 @@ static int tb_suspend_noirq(struct tb *tb)
29242956

29252957
tb_dbg(tb, "suspending...\n");
29262958
tb_disconnect_and_release_dp(tb);
2959+
tb_switch_exit_redrive(tb->root_switch);
29272960
tb_switch_suspend(tb->root_switch, false);
29282961
tcm->hotplug_active = false; /* signal tb_handle_hotplug to quit */
29292962
tb_dbg(tb, "suspend finished\n");
@@ -3016,6 +3049,7 @@ static int tb_resume_noirq(struct tb *tb)
30163049
tb_dbg(tb, "tunnels restarted, sleeping for 100ms\n");
30173050
msleep(100);
30183051
}
3052+
tb_switch_enter_redrive(tb->root_switch);
30193053
/* Allow tb_handle_hotplug to progress events */
30203054
tcm->hotplug_active = true;
30213055
tb_dbg(tb, "resume finished\n");
@@ -3079,6 +3113,12 @@ static int tb_runtime_suspend(struct tb *tb)
30793113
struct tb_cm *tcm = tb_priv(tb);
30803114

30813115
mutex_lock(&tb->lock);
3116+
/*
3117+
* The below call only releases DP resources to allow exiting and
3118+
* re-entering redrive mode.
3119+
*/
3120+
tb_disconnect_and_release_dp(tb);
3121+
tb_switch_exit_redrive(tb->root_switch);
30823122
tb_switch_suspend(tb->root_switch, true);
30833123
tcm->hotplug_active = false;
30843124
mutex_unlock(&tb->lock);
@@ -3110,6 +3150,7 @@ static int tb_runtime_resume(struct tb *tb)
31103150
tb_restore_children(tb->root_switch);
31113151
list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list)
31123152
tb_tunnel_restart(tunnel);
3153+
tb_switch_enter_redrive(tb->root_switch);
31133154
tcm->hotplug_active = true;
31143155
mutex_unlock(&tb->lock);
31153156

0 commit comments

Comments
 (0)