Repository navigation
Conversation
A Thunderbolt dock carries each display in its own DP tunnel, and the second one arrives on a USB-C port's DPIN1. The M1 Pro/Max laptop device tree only names each port's DP-alt control on dcpext0 and dcpext1, so apple-drm creates no tunnel connector and the second stream fails with "no Type-C connector for the DP tunnel": a dual-stream dock drives only one display. Name typecN-dpin0 and typecN-dpin1 on both external DCPs, as t602x-j414-j416.dtsi does for the M2 Pro/Max laptops. Cell 1 of each port's crossbar selects DPIN0 and cell 2 selects DPIN1. On its own this lets the second display connect but not stay up; t600x also needs the following drm/apple change to keep two tunnels at one link rate. Tested on a MacBook Pro 16" M1 Pro (j316s) with a Dell WD19TB dock and two 1920x1080@60 displays. Not tested on j314s, j314c or j316c. Assisted-by: Claude:claude-opus-5-5 Signed-off-by: Mark Zelinski <mark.zelinski890@gmail.com>
With both DP IN adapters of a port in use, as with a dual-stream dock, only one display stays lit on the M1 Pro/Max. On t600x, atc_tunnel_start_t8103() programs a single tunnel pixel clock selector for PCLK1, PCLK2 and DPRX. When the two streams train at different link rates (here HBR3 x4 for one display and HBR2 x2 for the other), whichever trains second reselects that clock under the first, and the first stream dies with a DPTX FIFO error. Report HBR2 as the maximum link rate for DP tunnels on t6000 and t6001, so both streams train at the same rate and share the clock. HBR2 x4 still covers 4K at 60 Hz with 8-bit colour. Direct DP-alt and HDMI outputs are unaffected. The t600x_tunnel_max_hbr2 module parameter (default on) restores HBR3 for single-display tunnel setups. Giving each DPIN its own selector (DPIN0 -> PCLK1, DPIN1 -> PCLK2, as atc_tunnel_set_t602x() does) was also tried on j316s. With DPRX on either DPIN, the second stream failed link training with firmware link fault 22, so the cap is the fix that works for now. Tested on a MacBook Pro 16" M1 Pro (j316s) with a Dell WD19TB dock: both displays light up together after a reboot and several replugs, with no DPTX FIFO errors. Not tested on M1 Max or M1 Ultra. Assisted-by: Claude:claude-opus-5-5 Signed-off-by: Mark Zelinski <mark.zelinski890@gmail.com>
|
Same failure on j314c (M1 Max 14", t6001), Microsoft Surface Thunderbolt 4 Dock, on unpatched aurora14 Here is a data point from the board this PR covers in code but lists as "not tested". This is the failure without the patch. I'm happy to test the PR branch. Setup
Behaviour
Log ( This repeats every time the second tunnel is attempted. Full logs are available on request. Both monitors are 4K60, so the HBR2 cap in this PR is fine for my case: one 4K60 stream fits in HBR2 x4. If there's a test build or a branch I can build, I'll report back with results, including whether the link-drop described above goes away once dpin1 has a connector. |
|
Thanks, this is very useful. The j314c is covered (it includes The branch is git clone --filter=blob:none --sparse -b t600x-dock-dual-stream https://github.com/kloploid/linux-aurora
cd linux-aurora
git sparse-checkout set arch/arm64/boot/dts/apple include/dt-bindings drivers/gpu/drm/apple
K=/usr/lib/modules/$(uname -r)/build
# 1. DTB (use the kernel's dtc; the system dtc rejects the float properties in t600x-die0.dtsi)
cpp -nostdinc -undef -D__DTS__ -x assembler-with-cpp -I include -I arch/arm64/boot/dts \
arch/arm64/boot/dts/apple/t6001-j314c.dts -o j314c.pre.dts
$K/scripts/dtc/dtc -q -I dts -O dtb -o t6001-j314c.dtb j314c.pre.dts
D=/usr/lib/modules/$(uname -r)/dtbs
sudo cp -a $D/t6001-j314c.dtb $D/t6001-j314c.dtb.orig
sudo install -m644 t6001-j314c.dtb $D/
sudo update-m1n1
# 2. appledrm with the HBR2 tunnel cap
make -C $K M=$PWD/drivers/gpu/drm/apple LLVM=1 -j$(nproc) modules
sudo install -Dm644 drivers/gpu/drm/apple/appledrm.ko /lib/modules/$(uname -r)/updates/appledrm.ko
sudo depmod -a
sudo mkinitcpio -p linux-aurora && sudo limine-mkinitcpio # if you boot a Limine UKIReboot, then plug in the dock. To undo, restore The logs that would help most:
The link drop isn't something I saw on the WD19TB. My guess is that it follows from the failed second tunnel, but I can't promise it goes away. Two 4K60 streams at HBR2 x4 come to about 34.6 Gb/s of a 40 Gb/s link, so the dock's USB and Ethernet will have less room. #154 changes how the connection manager splits that bandwidth. It's a separate |
|
Tested on M1 Max 14" (j314c), Result: dual-stream works after a replug, but not at boot. This is the first time I've had two monitors on this dock under Linux. The second stream trains at HBR2 and comes up at 3840x2160@60 next to the first one. Out of three plug events:
Replug 2 (works). The cap is applied, and dpin1 trains at HBR2: Boot (fails). dpin1's DPTX calls time out, teardown fails, and the whole Thunderbolt link drops and retrains. dpin0 then comes back on its own, but dpin1 doesn't: dpin1 was set up about 2 s after dpin0, while the USB4 PCIe tunnel was still coming up ("tunnel reset acknowledgment timed out" in the same second). That looks like a bring-up race rather than the clock problem. Replug 1 (fails). dpin1 is routed, but no EDID is read (3 fallback modes), and the 640x480 modeset fails at No DPTX FIFO errors appeared in any of these, or in my pre-test log. I'll report back on how long both monitors stay lit, and I can test #154 or a fix for the boot-time race if that helps. |
|
Thanks for testing so quickly. Your replug 2 confirms the main point of the PR on t600x: the second tunnel gets a connector, both streams train at HBR2 ( About the boot failure. I don't think the PCIe tunnel causes it.
That fits the t600x shared tunnel pixel clock from the PR description: bringing up dpin1 reprograms the selector that dpin0 is still training on. My j316s with a WD19TB gets dpin1 about 5 s after dpin0. The firmware drops its first attempt once and the retry sticks, but the link never goes down. A patch to test. Branch git fetch origin t600x-dpin-stagger && git switch t600x-dpin-stagger # in the clone from before
git sparse-checkout add drivers/thunderbolt
K=/usr/lib/modules/$(uname -r)/build
make -C $K M=$PWD/drivers/thunderbolt LLVM=1 -j$(nproc) modules
sudo install -Dm644 -t /lib/modules/$(uname -r)/updates/thunderbolt \
drivers/thunderbolt/thunderbolt.ko drivers/thunderbolt/thunderbolt_apple.ko
sudo depmod -a && sudo mkinitcpio -p linux-aurora && sudo limine-mkinitcpioInstall both What would tell us the most:
If the stagger helps, I'll send it as a separate PR on top of this one. If it doesn't, those logs should show what to try next. #154 probably won't change this, since both your tunnels already end up at HBR2 x4. |
|
Thanks for adding j314c. I tested Result: replug is fine, but boot still fails, and the stagger shows dpin1 isn't the trigger.
Cold boot. The stagger kicked in as intended, but the link dropped while dpin1 was still waiting. dpin1 was never routed to the DCP before the drop: The first sign of trouble is the Gen2/3 lane errors on both lanes, in the same second as dpin0's 4K60 modeset. USB on the dock starts failing (-71) right after, then the PCIe tunnel reset times out and the link goes down. dpin0's link fault 22 comes after the TB link is already down, so it looks like a result, not the cause. I saw the same Gen2/3 lane errors in my earlier dock-plug log with the #373 build. After the link retrained, dpin0 came back on its own, but there's no further dpin1 activity at all: no new "waiting" line and no handoff. The Z27k stayed dark until I replugged it. So separately from the drop, dpin1 doesn't seem to be re-requested after the link comes back. Replug (works). The Z27k came up first time, with no link fault and no TB drop. The no-EDID / This is one cold boot and one replug. I can do more boots, try other |
|
One more failure with the same Sequence (Hyprland idle: screen locked, then DPMS off and back on twice in quick succession):
There was no Thunderbolt link drop and no link fault at any point before the dock replug. dpin0 was unaffected throughout. Wake failure (21:05:25). This is the same Black screen after reconnecting the Z27k (21:24:55). Everything looks normal in the log, including The disable/re-enable at 21:28 picked a different timing ( Full |
|
I run a j314c (M1 Max) with an Apple Studio Display XDR over a Thunderbolt DP tunnel on Two things I would check on this hardware, since the XDR is the demanding end of what a t600x drives:
If you can point me at a branch with the DT change and the dptxep change applied, I'll build it into my local kernel and report the dmesg for plug, unplug and replug with the XDR. Notes on how this display behaves on the TB tunnel, including the mode and colour-mode tables decoded from DCP: https://github.com/briantoll/omarchy-thunderbolt-display. |
|
Tested on MacBook Pro 14" M1 Pro (j314s, t6000), Result: #373 alone was not enough with mismatched monitors. With #154 added, both run at full mode. #373 only: both DPINs get a connector (the That is the shared-clock failure the PR describes. The Dell's stream was torn down. The Dell also only offered modes up to 2560x1440@60 on the second tunnel, which is consistent with that tunnel getting too little bandwidth for HBR2 (the #154 issue). Your two-4K60 test probably didn't hit this because both sinks needed HBR2 anyway. #373 + #154: both tunnels get an even bandwidth share, both train at HBR2, and there are no FIFO errors: The Dell now runs at 2560x1440@154.85 and the ultrawide at 5120x1440@119.97, both on the dock with 0 FIFO errors after link-up. Suggestion: on t600x the cap might need to pin both tunnels to the same rate, not only cap them, or depend on #154. Otherwise a lower-bandwidth sink can still train below HBR2. One caveat: I also had a Hyprland rule asking for the Dell's 155Hz mode, and I didn't test whether a 60Hz mode with #154 would still train at HBR. Tester note: on Omarchy installs that boot a UKI through limine, I haven't tested boot with the dock attached or DPMS wake on this setup yet. |
|
@JarenKempton thanks for testing this. I've now tried #373 + #154 together on my setup, as you suggested. Setup: MacBook Pro 16" M1 Pro (j316s, t6000), Dell WD19TB, two 1920x1080@60 monitors on the dock (Lenovo T24i, Acer VG240Y). Two kernels built from the same tree:
Both kernels booted with the dock and both monitors already connected, then I compared kernel log snapshots. Result: #154 works with #373 on this setup, and nothing regresses. New with #154 ( The same on both kernels:
Caveats:
This covers the boot-with-dock-attached case you hadn't tested yet: it works with both builds. Update: dock replug and s2idle sleep on the same build Dock replug: OK. Both screens came back without me doing anything. The first re-enumeration failed on the PCIe side ( Wake from s2idle: dpin1 fails at first, then recovers by itself after HPD cycles. dpin0 (289c00000) came back straight away ( dpin1 (28cc00000) failed six modesets in a row over about 7 s. This is the same pipe-not-enabled failure @Dazzzz reported, but without the After that, the sink dropped HPD ( Totals for the boot: 13 FIFO errors (0 before sleep), 56 So on my setup, wake has the same dpin1 problem Dazzzz saw, but it heals on its own here. That might be because 1080p60 leaves more bandwidth headroom than 4K60. Full kernel logs are available if useful. |
Problem
On M1 Pro/Max laptops, a dual-stream Thunderbolt dock (Dell WD19TB) drives only one of its two displays.
atc_tunnel_start_t8103()programs one tunnel pixel-clock selector for PCLK1, PCLK2 and DPRX. Here one display trained at HBR3 x4 and the other at HBR2 x2. Whichever trained second reselected the shared clock, and the first stream died with a DPTX FIFO error.Changes
typecN-dpin0/typecN-dpin1on both external DCPs, the same ast602x-j414-j416.dtsi.appledrm.t600x_tunnel_max_hbr2(defaultY) turns the cap off.I also tried giving each DPIN its own selector (DPIN0→PCLK1, DPIN1→PCLK2, as on t602x). With DPRX following either DPIN, the second stream failed link training (
DPTXPort: firmware reports link fault 22,prepareLink() failed with 0xe00002ed). So the cap is the fix that works for now. Correct per-DPIN clocking on t600x is still an open question.Trade-off: on M1 Pro/Max, a single display over a Thunderbolt tunnel is also limited to HBR2 x4. Modes that need HBR3 over a tunnel lose them while the cap is on (for example, uncompressed 4K at 120 Hz), so
appledrm.t600x_tunnel_max_hbr2=0restores the old behaviour. The cap could later be limited to when a second DPIN is live, but the rate is chosen at training time, before that is known.Related: #154 (Thunderbolt bandwidth sharing between Apple host DP tunnels). The two PRs don't conflict, and I've tested them together (see Results). With mismatched monitors, this PR needs #154. @JarenKempton tested on j314s with a CalDigit TS4 and found that the cap is only a ceiling: with #373 alone, a starved second tunnel trained at HBR while the first ran at HBR2, and that brought back the FIFO error. With #154 added, both tunnels got an even bandwidth share, both trained at HBR2, and there were no FIFO errors. So the two should land together, or #154 first. On j314c, #154 also ends with both 5K tiles at HBR2 x4, which fits the shared-clock explanation: two tunnels at the same rate work.
Machine and build
apple,j316s/apple,t6000, Omarchy7.2.11-14-1-ARCH(packagelinux-aurora 7.2.11.aurora14-1), with the patched modules built from4fdd0fc5c9with clang (LLVM=1).linux-aurorapackages on aurora15:7.2.11-15-1.1-ARCH(t600x: drive both displays of a dual-stream Thunderbolt dock (j314/j316) #373 only) and7.2.11-15-1.2-ARCH(t600x: drive both displays of a dual-stream Thunderbolt dock (j314/j316) #373 + thunderbolt: share an Apple host link between DP tunnels that come up together #154)asahi,os-fw-version13.5,asahi,system-fw-version27.0Results
mux-controls/mux-control-nameson both DCPscheckpatch.pl --strict, W=1 build of dptxep.cdyndbgon dptxep.c)SET_LINK_RATE 0x14on both DCPs at boot, on wake and on replug: dpin0 HBR2 x4, dpin1 HBR2 x2sharing the path with 2 DP OUT adapters, offering 18000 Mb/sThe cap message is
dev_dbg. To see it, along withDPTXPort: SET_LINK_RATEand the tunnel clock rate, enable dynamic debug:On Omarchy installs that boot a UKI through limine,
appledrmis loaded from the UKI's embedded initramfs, so rebuilt modules needlimine-mkinitcpio(thanks @JarenKempton).Known issue: one display stays dark after s2idle wake (not caused by this PR, fix to follow separately)
I reproduced this twice on j316s + WD19TB. It's the same failure @Dazzzz saw after DPMS wake. On each wake, one DPIN of the pair loses its tunnel route: the dpin1 display the first time, the dpin0 display the second time. The other DPIN comes back normally at HBR2. Link rates were HBR2 on both sides throughout, so this isn't the shared-clock rate problem this PR fixes.
What the log shows for the stuck DPIN (second wake, dyndbg on):
The stuck DPIN is never routed again and never gets a
SET_LINK_RATE. It recovers only if that DPIN gets an HPD cycle (which happened by itself on the first wake) or the dock is replugged.dcp_poweron()goes on to a modeset after the 3 s IOMFB hotplug wait times out. For a tunnel route, it should probably tear the route down and attach it again instead. I'll send that as a separate PR.Remaining DCP firmware log lines, also present without these patches:
🤖 Generated with Claude Code