mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Duff OMelia <duff@omelia.org>
To: westeri@kernel.org, andreas.noever@gmail.com, YehezkelShB@gmail.com
Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org,
	imre.deak@intel.com, Duff OMelia <duff@omelia.org>
Subject: [RFC PATCH] thunderbolt: Tunnel only the first DP OUT of Apple Studio Displays
Date: Fri,  6 Nov 2026 10:44:45 -0500	[thread overview]
Message-ID: <20261106154445.517940-1-duff@omelia.org> (raw)

An Apple Studio Display presents its 5K panel as a 2x1 tile over two DP
OUT adapters. The first tile's connector offers the full 5120x2880 mode,
which the xe driver on my host drives over a single stream. The second
tile's connector offers only 2560x2880, and nothing uses it there.

tb_find_dp_out() treats both adapters alike, so it tunnels both when DP
IN adapters are free. The second tunnel sits idle but holds a DP IN
adapter, and with it one of the host's display outputs. A Dell XPS 16
(DA16260, Panther Lake) has three external outputs, so two Studio
Displays take all of them. A third is authorized and its USB devices
work, but it stays dark:

  DP-1  enabled   5120x2880       first display, first tile
  DP-2  disabled  2560x2880 only  first display, second tile
  DP-3  enabled   5120x2880       second display

Plug order doesn't help, since whichever display connects first takes
two outputs. The hardware isn't the limit: with Thunderbolt turned off
in the BIOS, all three run at 5120x2880@60 in DisplayPort alt mode.

Skip every DP OUT adapter but the first on these displays: vendor 0x1,
device 0x801f (2022 model) and 0x8025 (2026 model). All three displays
then run at 5120x2880@60, each on its own output.

Commit 49521be4809d ("drm/amd/display: hide Apple Studio Display
secondary tile") reached the same conclusion for amdgpu, and its panel
IDs include both models here (0xae3a and 0xae46). It hides the tile from
userspace, but this driver sets up the tunnel, so it can't free the
output.

Signed-off-by: Duff OMelia <duff@omelia.org>
---
This is an RFC because I don't think a device list is the right final
form, and I'd like your view on a better one:

- Not every host drives these displays over one stream. Bernd Behler's
  report [1] has a 2022 model on Arrow Lake with i915 driven as two real
  tiles, with both tunnels carrying video, and hosts without DSC
  presumably need both streams too. This patch skips the second tunnel
  whatever the host, so it could break those setups. I have neither to
  test. On my host, the tunnels show about 17 Gb/s consumed on the 2022
  model and 22.5 Gb/s on the 2026 model. Uncompressed 5K at 60 Hz needs
  at least 21 Gb/s, so I read the 2022 figure as DSC, but I haven't
  confirmed that from the xe side.
- The driver could leave the second tile untunneled until the GPU needs
  it, or tear down an idle tile tunnel when another DP OUT needs a DP
  IN. I don't know whether it can tell those cases apart.
- If a device list is acceptable, quirks.c looks like a better home.
- The Studio Display XDR (device 0x8024) isn't covered, and I don't have
  one. Dennis Wang's report [2] shows the same two-sink pattern on Panther
  Lake, and says masking the second sink with video=DP-2:d gives a clean
  single-stream boot, so a general fix might help there too.
  49521be4809d also lists panel ID 0xae42, which I can't identify.

There's no Fixes tag, because I can't point to a commit that broke this.

Testing

Hardware: XPS 16 DA16260 (Core Ultra X9 378H, xe driver) with two 2022
displays and one 2026 display each directly on the laptop, and a CalDigit
TS4 behind the 2026 display.

Kernels: run on Omarchy's 7.2.8 and 7.2.15, which carry Thunderbolt
backports. Not run on mainline. This diff is against thunderbolt.git next,
where tb.c matches 7.4-rc1.

Built against this tree with W=1 (x86_64 defconfig plus USB4=m, gcc
16.2.1), with no new warnings. checkpatch --strict has no complaints
about the code.

Passed, with all three displays back at 5K each time: reboot, cold boot,
lid-closed boot, s2idle, and unplugging everything and replugging into
different ports. The displays have been stable in normal use since.

Debug log with the patch:

  thunderbolt 0000:00:0d.3: 701:10: DP OUT in use
  thunderbolt 0000:00:0d.3: 701:11: skipping spare DP OUT tile

AI assistance

I asked Claude Code (Claude Opus 5.5) to get three displays and the
TS4's Ethernet working at once. It traced the problem through the driver
source and kernel logs, wrote this patch, and drafted this email. The
testing above was on my own hardware.

[1] https://lore.kernel.org/linux-usb/CAM=hwCxiJwODObeut=JcCA8jbu4C-XVEKJpR-y2MnDJibBQyOw@mail.gmail.com/
[2] https://lore.kernel.org/linux-usb/CAOX-mXs0ZjpiLtviUDqHLrzWh2Dyz+Y9gZUcenW_yV4iQUa=7g@mail.gmail.com/

 drivers/thunderbolt/tb.c | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index 57c11ba..b015a71 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -1862,6 +1862,27 @@ out:
 	return tb_find_unused_port(sw, TB_TYPE_PCIE_DOWN);
 }
 
+/*
+ * A Studio Display has a DP OUT adapter for each half of its panel, but a
+ * host can drive the whole 5K panel over the first one alone. Tunneling the
+ * second only takes a DP IN adapter that another display could have used.
+ */
+static bool tb_dp_out_is_spare_tile(struct tb_port *port)
+{
+	struct tb_switch *sw = port->sw;
+	struct tb_port *p;
+
+	if (sw->vendor != 0x1 || (sw->device != 0x801f && sw->device != 0x8025))
+		return false;
+
+	tb_switch_for_each_port(sw, p) {
+		if (tb_port_is_dpout(p))
+			return p != port;
+	}
+
+	return false;
+}
+
 static struct tb_port *tb_find_dp_out(struct tb *tb, struct tb_port *in)
 {
 	struct tb_port *host_port, *port;
@@ -1879,6 +1900,11 @@ static struct tb_port *tb_find_dp_out(struct tb *tb, struct tb_port *in)
 			continue;
 		}
 
+		if (tb_dp_out_is_spare_tile(port)) {
+			tb_port_dbg(port, "skipping spare DP OUT tile\n");
+			continue;
+		}
+
 		/* Needs to be on different routers */
 		if (in->sw == port->sw) {
 			tb_port_dbg(port, "skipping DP OUT on same router\n");

base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4

                 reply	other threads:[~2026-11-06 15:45 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261106154445.517940-1-duff@omelia.org \
    --to=duff@omelia.org \
    --cc=YehezkelShB@gmail.com \
    --cc=andreas.noever@gmail.com \
    --cc=imre.deak@intel.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=westeri@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®