Skip to content

Thunderbolt: M1/M2 Pro display, M1 PCIe and resume recovery - #8

Merged
RyanTheTide merged 90 commits into
aurora-silicon:aurora-wipfrom
iconidentify:tb-dp-tunnel-t8103
Oct 8, 2026
Merged

RyanTheTide merged 90 commits into
aurora-silicon:aurora-wipfrom
iconidentify:tb-dp-tunnel-t8103

Conversation

@iconidentify

@iconidentify iconidentify commented Sep 23, 2026 •

Copy link
Copy Markdown

This PR combines the M1 Thunderbolt display/PCIe series with the M2 Pro display work from #14 and the suspend recovery work from #25, and now also carries the open Thunderbolt display PRs #46, #64 and part of #50. It targets aurora-wip, merges cleanly into it, and keeps every original author.

Status (2026-10-01, head 947026902232):

Earlier status (2026-09-28): first-boot and cross-port USB2/display checks passed on the final USB2 follow-up. On 2026-09-28, 7.1.12-tbpr8-usb2fix2 brought up the TS4 USB2 devices on the first session after reboot and after subsequent manual hotplugs, including first attach on the other controller. The user reports everything working across the port moves. TS4 suspend-to-idle also passed at 32.1 s and 128.8 s, with USB/display functionality confirmed after each wake and Ethernet gateway traffic verified. Hotplug checks also passed functionally on the Kensington SD5560T and CalDigit TS3 Plus, as reported by the user and supported by device/display enumeration. Kensington long sleep remains to be checked on this kernel.

Earlier hardware results: on M1/j293, suspend-to-idle worked with the CalDigit TS3 Plus, CalDigit TS4 and Kensington SD5560T, displays included, at the revisions and sleep durations listed in the hardware matrix.

  • What was broken at c2514e492d2e: a TS3 Plus suspend hung the machine until it reset. The Kensington lost its link after every resume. Any sleep longer than about 40 s lost the dock.
  • What happens now: the dock stays connected through the sleep. Keyboard, mouse and every other USB device behind it come back without re-enumerating, and resume completes in about 0.9 s. External displays come back the way they do after a screen blank.
  • Known issue: see "Known issues" below.

USB2 first-session fix

The first TS4 attach on each Type-C port after boot could bring up USB3, display
and Ethernet while leaving USB2 keyboards and mice disconnected until replug.
Commit 0ab0ac187140 made the Apple DWC3 glue select USB2 host mode before
releasing the controller reset. The final fix leaves host-mode selection to the
xHCI root hub after reset; device-mode selection still runs before reset.

  • Fix: 154e31b133ab; KUnit helper tests: d9de49ad1c84.
  • On the final usb2fix2 kernel, the TS4 USB2 side enumerated 0.39 seconds
    after the first dock attach
    in the boot. The Magic Keyboard and both mice
    enumerated; the user confirmed USB and display operation. The external Sony
    display was active at 3840×2160 @ 120 Hz. There was one dock attach and no
    USB/dock disconnect before the initial capture.
  • Subsequent manual hotplug/cross-port checks passed: all 3/3 TS4 attaches in
    this boot brought up USB2, at +0.39 s, +0.41 s and +0.38 s. The initial
    controller was 502280000.usb; the final capture shows 382280000.usb, so
    both machine ports have first-session coverage. The keyboard, both mice and
    4K/120 Hz display are present after the moves, consistent with the user report.
  • The earlier experiment kernel also connected USB2 on the first session,
    0.40 seconds after attach; the final-kernel result above now supersedes it.
  • Fresh arm64 QEMU KUnit run on d9de49ad1c84: 91/91 passed, with lockdep and
    debug atomic-sleep enabled. This includes 19 Type-C PM tests omitted from the
    earlier 72-test run. Suites: DART 10, IOMFB state 7, AFK lifetime 6, DWC3 role
    choice 3, CD321x PM 19, Thunderbolt 46.
  • KUnit validates software decisions and state transitions. PHY timing,
    attached-cable role swaps, dock/display recovery, and long sleeps still need
    hardware validation. Device-to-host and host-to-device swaps are untested.
  • Focused review of the USB2 follow-up covered the xHCI root-hub HOST_SS-to-HOST
    fallback, reset ordering, retained device-mode selection, fixed-hub PHY setup,
    invalid-state rejection and error paths. No blocking issue was identified in
    these two commits. A fresh whole-series Sashiko review was not completed.
  • The device-to-host expectation in the fix commit message is not hardware
    evidence. In-place role swaps may follow different Type-C mux/PHY power
    sequencing; both role-swap directions remain a separate validation item.

Included support

Source Included work Source tip
Original #8 t8103/M1 DP tunnels, PCIe enumeration, IOMMU teardown, placeholder EDID retry 14cc7c907e6a
#14, carried through #25 T602X/j416s DP routing, DP IN handshake, DCP/AFK support, crossbar and PHY clock e9e52e6dc932
#25 Updated PHY teardown, ACIO reset retries and standby recovery ebbf4cb22add
#46 Independent dual dock DP streams on the M2 Pro/Max laptops, USB3 bandwidth accounting 3075f5503a32
#64 Thunderbolt display on t600x (M1 Pro/Max), late clear swap at power-off 5d524a478c92
#50 (2 of 18 commits) DP-N connector type, fixed dual-stream possible_crtcs 9dc79fb25c92

Suspend-to-idle with Thunderbolt docks (7 commits on c2514e492d2e)

On Apple silicon the host router, ACIO and the tunneled PCIe controller all stay powered in suspend-to-idle. The previous code treated the sleep as a power-down anyway. It stopped every tunnel, asked the routers to sleep and reset the dock's PCIe hierarchy, then rebuilt everything on resume. That caused the failures seen at c2514e492d2e:

  • The link died at a fixed deadline. Routers were asked to sleep but the link never went down. The Thunderbolt link then failed 40.8 s after suspend entry, in every one of ten cycles, on four kernels and both ports. That was the "late link drop" that c2514e492d2e recovered from, and it meant a suspend longer than about 40 s lost the dock while asleep.
  • The dock sometimes didn't come back after a long sleep. After the tunneled PCIe was held in reset for a long sleep, a Kensington's link trained and its bridges were restored, then every request to it timed out.
  • The PCI core restored the dock too late. It assumed the dock's functions had kept their state, skipped their noirq restore and readiness waits, and restored them late and in parallel. On a TS3 Plus, pciehp on the dock's occupied hotplug port could remove a device mid-resume.
  • Displays were reported as unplugged at every suspend. When the tunnel came back, the compositor had already switched the output off.

The seven commits:

  1. PCI: apple: resume tunneled functions as if from D3cold
    • When a tunnel really is stopped, the functions below it are marked powered off. The PCI core then restores them parent-first in noirq, waits for each secondary bus, and disconnects any subtree that doesn't come back.
    • A port that fails to resume marks its hierarchy disconnected instead of gating its clock under bound drivers.
  2. thunderbolt: keep routers awake across system sleep on Apple hosts
    • Adds QUIRK_NO_SYSTEM_SLEEP. Routers aren't asked to sleep when the host keeps the link up, which removes the 40.8 s link failure.
  3. thunderbolt: restore DP resources after resume when routers stayed awake
    • Routers that stay awake send no plug events, so the DP resources released at suspend are paired again at resume completion.
  4. thunderbolt: keep M1 PCIe tunnels up through suspend-to-idle
    • A healthy tunneled PCIe port is left running, and the Thunderbolt connection manager leaves kept tunnels alone.
    • The dock's endpoints keep their state: no xHCI reinit and no USB re-enumeration.
    • If the link was lost while asleep, resume falls back to the stop, reset and restart path.
  5. thunderbolt: keep DP tunnels through suspend-to-idle with kept tunnels
    • DP tunnels are kept too, so a display behind a dock is only powered down and up, as for DPMS, never reported as unplugged.
  6. drm/apple: keep the Type-C cable state across system sleep
    • Suspend no longer clears the Type-C cable state. Resume re-establishes DPTX before the modeset, as DPMS on does.
  7. drm/apple: recover Type-C outputs whose swaps DCP drops
    • DCP accepts a modeset even when the external pipe can't be enabled yet (a TV behind a DP-to-HDMI converter asserts HPD late after resume), then drops every swap, stalling the compositor on flip_done timeouts.
    • A 1 s swap watchdog on Type-C outputs completes the flip, invalidates the mode and re-applies the CRTC through the hotplug worker, a bounded number of times. A late HPD after a failed modeset also re-applies the mode.

Test parameters. Both are meant to be dropped before upstreaming:

  • pcie_apple.s2idle_keep_link=0 restores stop/reset/restart of the tunneled PCIe port;
  • thunderbolt_apple.router_sleep=1 restores the router sleep request.

Validation of the current source

  • The native ARM64 test Image and all 1,865 modules built. The final USB2 driver was rebuilt and installed for 7.1.12-tbpr8-usb2fix2; the installed module matches the saved final build artifact byte-for-byte.
  • The changed objects build clean with W=1; the pre-existing kernel-doc warnings in drivers/gpu/drm/apple/dcp.c are unchanged.
  • checkpatch.pl --strict on the seven patches: 0 errors, 0 checks. The warnings are the Co-Authored-By trailer form and two quoted kernel log lines.
  • The two USB2 patches also pass checkpatch.pl --strict with zero errors/checks and only the existing co-author trailer capitalization warnings; git diff --check passes.
  • KUnit was re-run on the USB2 follow-up (d9de49ad1c84): 91/91 pass under arm64 QEMU with lockdep, including the CD321x PM suite.
  • Final-kernel boot check (2026-09-28): 7.1.12-tbpr8-usb2fix2, boot 05145ba1-c851-4df1-becd-52bdf97372f0, TS4 on controller 502280000.usb. First-session USB2 enumeration and user-reported USB/display operation passed. The Ethernet driver reported a 1 Gbps link, but traffic was not tested. The installed final module remained intact after the boot cleanup service ran successfully.
  • Saved evidence includes the kernel journal, USB sysfs topology, PCI/network state and active display modes. Earlier hardware results below remain tied to their named revisions.

Final USB2 follow-up hardware results (M1/j293)

Kernel 7.1.12-tbpr8-usb2fix2, source d9de49ad1c84, 2026-09-28.
Boot ID 05145ba1-c851-4df1-becd-52bdf97372f0. Manual wake was used because the
machine's RTC has no wake alarm. Sleep durations were measured from the change
in CLOCK_BOOTTIME - CLOCK_MONOTONIC.

Setup Check Result
TS4, keyboard, two mice, external Sony display First boot and manual port moves Pass, 3/3 USB2 attaches in 0.38–0.41 s; both controllers covered
TS4 on 382280000.usb 32.1 s s2idle Pass, USB identities/addresses retained, no USB resets, 4K/120 Hz restored, Ethernet gateway 3/3
Same 128.8 s s2idle Functional pass, USB identities/addresses retained, downstream USB resets recovered automatically, 4K/120 Hz restored, Ethernet gateway 3/3
Kensington SD5560T Hotplug User-reported functional pass after a brief initial attach/disconnect/reconnect; USB input receivers and USB LAN enumerated, 4096×2160 @ 60 Hz modeset completed. Ethernet traffic not tested
CalDigit TS3 Plus Hotplug Pass, user reports normal operation; Keychron keyboard and other USB peripherals enumerated, Samsung 3840×2160 @ 60 Hz active, Ethernet gateway 3/3

Raw journals also retain display firmware diagnostics and Wi-Fi errors; these
results establish the tested dock functions, not an error-free system log.

Earlier hardware matrix (M1/j293)

"Head" in this historical table is d607bbfd1265, before the USB2 follow-up. "Rev A" is this series up to 003225676c30 (PCIe/Thunderbolt keep, without the DP and DCP commits). "Rev B" is up to 337af62b6e56. The Thunderbolt and PCIe sleep code is identical in Rev A, Rev B and the head except for the DP-tunnel keep in commit 5.

Dock Port Setup Sleep Result Build
CalDigit TS3 Plus front Samsung 4K (DP), keyboard, mouse, I210 Ethernet (igb), Samsung T7, card reader ~20 s Pass. Resume ~0.9 s, all 12 PCI functions and Ethernet back, display relit Head, Rev B
CalDigit TS3 Plus front same ~90 s Pass Rev B
Kensington SD5560T rear Sony TV via dock HDMI, keyboard/mouse, USB3 LAN ~20 s Pass. The first modeset raced the TV's HPD; the watchdog retrained it and the picture returned ~1 s after wake. No stall Head
Kensington SD5560T rear same ~20 s and ~90 s Pass. Resume ~0.9 s, no USB resets, USB3 tree kept, no link loss Rev A
Kensington SD5560T front same ~20 s Pass Rev A
CalDigit TS4 rear TV via DP-to-HDMI adapter, Magic Keyboard, two mice (one via monitor KVM), I225 Ethernet (igc) ~20 s Pass. USB2 devices stayed connected, Ethernet relinked, the watchdog retrained the display Head

At Rev B (without commit 7), the Kensington + TV case froze the compositor on flip_done timeouts. At c2514e492d2e, the TS3 Plus front-port display suspend hung the machine until it reset. Both are fixed by the commits above.

Known issues

  • Unplug diagnostics: manual TS4 disconnects emitted Pipehandler lock not acked / Failed to lock pipehandler from the PHY driver. Subsequent USB2/display attaches passed, with no kernel WARNING/Oops observed in the captured journal. The diagnostic cause is not yet triaged.
  • Kensington long sleep on the final head: still pending. The TS4 gap is now closed by the 128.8-second functional pass below; it does not establish Kensington behavior.
  • Downstream USB resets on long resume: the 128.8-second TS4 wake reset the downstream Genesys USB2 hub and attached keyboard, mouse and audio device (five reset events, including two for the audio device). Device addresses remained unchanged and input/display functionality recovered automatically; this was a functional pass, not a reset-free resume.

Hardware test progress on d9de49ad1c84

Use the installed 7.1.12-tbpr8-usb2fix2 kernel and record the dock, port, boot ID,
sleep duration, observed input/display/network health and kernel journal.

  1. Passed: TS4 first boot session, USB2 enumeration and user-reported USB/
    display operation on 2026-09-28, as recorded above.
  2. Passed: manual TS4 hotplug/cross-port checks, including first attach on
    the other controller. All three attaches passed USB2 enumeration, and the
    user reports working USB/display across the moves.
  3. Passed functionally: Kensington SD5560T and CalDigit TS3 Plus hotplug,
    with the user reporting normal operation on both. Logs confirm USB input
    device enumeration and completed display modesets. The current TS3 Plus
    session also passed an Ethernet gateway ping (3/3). Kensington Ethernet
    enumerated but traffic was not tested. These TB3 docks use their own PCIe
    USB controllers; their coverage comes from device/display evidence and the
    user report, not the TS4 attach checker.
  4. Passed on TS4: short 32.1 s and long 128.8 s suspend-to-idle. USB
    topology and device numbers were retained through both cycles; no USB/dock
    disconnect or new-device enumeration was logged during either cycle. The
    user confirmed input/display functionality, the external display returned
    at 3840×2160 @ 120 Hz, and Ethernet gateway ping passed 3/3 after each wake.
    Short resume had no USB reset events; long resume had the five downstream
    reset events noted above. No kernel WARNING/Oops/Call Trace was observed in
    either captured interval. Kensington long-sleep testing remains pending.

The attach checker validates enumeration events, not resume health. An INFO/SKIP
result is not a hardware pass, and an earlier attach PASS does not establish
successful recovery after sleep.

Earlier hardware results

  • c2514e492d2e: Kensington SD5560T rear, two USB-only suspends and one display suspend passed functionally, recovered by late link-loss notification. CalDigit TS3 Plus front-port display suspend failed: the machine hung after wake and reset.
  • 67761b89689e (TS4): passed boot, same-port and cross-port hotplug, Sony 3840×2160@120 through the TS4 DP-to-HDMI adapter, and wired connectivity. It exposed a suspend regression.
  • 44bcebd4fabe: passed one TS4 display/network suspend recovery cycle after the PCIe ownership/PM fix. Kensington suspend still failed at 44bcebd4fabe and 5eb7f83a961a.

🤖 Generated with Claude Code

https://claude.ai/code/session_01PA1D59hehGmwnpSxrYcDbk

@iconidentify
iconidentify changed the base branch from aurora-rc to aurora-wip September 23, 2026 06:40
A DP tunnel from the host router's DP IN adapter needs a pixel clock
from the ATC's AUSPLL. Type-C DP alt mode sets it up together with the
lanes; a tunnel has no lanes to set up, and the PHY stays in
Thunderbolt/USB4 mode.

Export apple_atc_dp_tunnel_rate(). It keeps the PHY blocks awake,
enables DPTX PCLK1 with a per-rate selector and programs a fixed AUSPLL
descriptor, without touching the lane mux. Rate 0 stops the clock: PLL
output drivers off, then an APB power-down command. The sleep/clamp
overrides and TX_DP_CTRL0 are left as they are; the next start programs
them again.

A start is refused while PLL outputs are live or a PLL request is
pending, and DP alt mode configuration is refused while the tunnel clock
runs, so the two never share the PLL. The clock is stopped before any
PHY mode change. Only t8103 is supported: the descriptor and selectors
are for that SoC.

Name the TX/RX sleep and clamp value fields next to their override
fields, and add include/linux/soc/apple/dp-tunnel.h for the functions
the Thunderbolt glue, appledrm, the ATC PHY and the display crossbar
call on each other. They are looked up with symbol_get(), so none of
these modules depends on another.

Based on Oliver Lukschander's t6020 tunnel clock work.

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
When DCP retrains a link through a Thunderbolt DP tunnel, the crossbar
connection has to follow it. Add apple_dpxbar_link_down() and
apple_dpxbar_link_up(). They gate and ungate the clocks and enables of
an already selected output. The mux selection is left alone; link_down()
also leaves the ATC output enable set, and link_up() re-asserts it. The
caller keeps the output selected around both; the selection is read
under the crossbar lock.

Also let the clock gates release before enabling the clocks behind them,
both when an output is selected and in link_up(), and warn if they do
not.

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
On Apple silicon the display engine is not wired to the host router's DP
IN adapters; the NHI glue has to route it there. Add an NHI op,
dp_tunnel_changed, called when a DP tunnel from a host DP IN adapter
comes up, before one is torn down, and when the connection manager stops
with such a tunnel in place. Every "down" is paired with an earlier
"up", so a tunnel released twice (at tb_stop() and by a late DPRX
failure) is only reported once.

System and runtime suspend release all DP tunnels through the normal
teardown, so the glue hears about them; they come back through plug
events after resume, which take the full path again.

Only hosts whose glue provides the op get the extra handling, and only
for their own DP IN adapters:
- after activation, pulse the DP IN adapter's HPD propagation bit
  (ADP_DP_CS_3 bit 10) and wait for ADP_DP_CS_2_HPD, at most 2 s under
  tb->lock;
- the DP IN video hop gets 5 NFC credits, including on USB4 routers;
- no LTTPR non-transparent mode on Titan Ridge DP OUT; the host DPTX
  trains the sink through the tunnel;
- the DP OUT adapter must not start link training on its own
  (ADP_DP_CS_3 bit 8) while the tunnel is up; the bit is restored on
  teardown, also when disabling the adapter fails.

The check is safe for routers without a domain, as built by the KUnit
tests.

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
Implement dp_tunnel_changed. It is only registered when
thunderbolt_apple.dp_display=1 is given and the machine is a t8103, so
the connection manager's Apple DP handling stays off otherwise.

When a DP tunnel from a host DP IN adapter comes up:
1. Map the adapter's register block non-posted; posted writes to it take
   an SError.
2. Acknowledge and enable its interrupts.
3. Ask appledrm to route a display pipeline to it. If appledrm is still
   probing (a dock present at boot), wait for it for up to 30 s.

The adapter's DPTX_INACTIVE handshake is run from DCP's link activation
(the adapter may only be woken while DCP drives the DPTX), within one
500 ms budget per call. Before a tunnel is torn down the adapter is put
to sleep and masked synchronously, and nothing touches it after that.

Each adapter has one work item on an ordered queue that brings the
display side in line with the tunnel state, so nothing is allocated on
the way down and whatever was handed to appledrm is always taken back.
When the ACIO is stopped, the display side is released before it powers
off. The parameter is read-only at runtime.

Not covered yet: the DP IN block is found at a fixed offset from the
ACIO "rc" region (there is no device tree node for it), its interrupts
have no handler, and suspend with an active tunnel has not been
validated.

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
Add apple_dcp_tb_dp_tunnel() for the Thunderbolt glue. It picks a free
display pipeline for the port and routes it to the crossbar's
dpin0/dpin1 output. It then out-of-band connects DPTX with the DFP port
(1/2) and the DP IN role (attribute bit 0), and reports the display as
attached.

During link setup:
- SetLinkRate starts or stops the ATC tunnel pixel clock.
- After DidChangeLinkConfiguration the crossbar connection is brought up
  and DP IN is activated. The first time this is the mux selection;
  after that only the clocks are brought back.
- Before WillChangeLinkConfiguration DP IN goes inactive and the
  crossbar clocks go down. Failures are logged and keep the crossbar
  down, so no stream is started without a clock.

Tunnel state, the clock and the crossbar are handled under one lock, so
teardown cannot race a link change, and DCP calls never block on the
mux. The lock (and hpd_mutex, which the tunnel path also reaches before
the DRM device binds) is initialized at probe. While a tunnel owns a
Type-C port, alt mode state for it is not acted on; the tunnel teardown
drops the recorded state and reconnects a fixed HDMI output if one is
live.

The Type-C DP alt mode path is unchanged: DFP port 0, role 0 and a
strict check of DCP's connection reply.

Tested on a MacBook Pro 13" M1 (j293) with a CalDigit TS3 Plus and a
Kensington SD5560T: 3840x2160 at 60 Hz over HBR2 and HBR3, unplug and
replug, monitor cable replug behind the dock, and a dock present at
boot.

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
@iconidentify

Copy link
Copy Markdown
Author

Updated the series after running two kernel review tools on it: the review-prompts /kreview protocol (one deep dive per commit) and sashiko (0 critical, 0 high, 15 medium, 26 low).

Fixed:

  • KUnit NULL dereference. tb_port_is_apple_host_dpin() dereferenced sw->tb, which the KUnit tests leave unset, so the Thunderbolt suite oopsed on every architecture. It went from 34/39 passing to 39/39 (arm64, QEMU).
  • Stale callback after teardown. Switching dp_display off at runtime, or a failed allocation, could skip the display teardown and leave appledrm holding a callback into thunderbolt_apple. Each adapter now has one embedded work item that always takes back what was handed out, and the parameter is read-only.
  • Quirks without the feature. The Apple DP handling in the connection manager ran even with the feature off. The NHI op is now only registered when dp_display=1 is set on t8103.
  • Late lock init. tb_lock (and hpd_mutex, which the tunnel path reaches at boot) were initialized at DRM bind, after the routes that use them were published. Both are now initialized at probe.
  • Loosened reply check. The DCP connection-reply check had been loosened for every output. It is strict again for everything except tunnels.
  • Unpaired notifications. The tunnel up/down notifications are now paired, so a late DPRX failure after tb_stop() doesn't report the same tunnel twice.
  • Shared PLL. DP alt-mode configuration and the tunnel clock no longer share the PLL.
  • Alt mode on tunnel ports. Alt-mode state on a port that a tunnel still owns is ignored, and the tunnel teardown drops what was recorded.
  • HDMI after teardown. A live fixed HDMI output is reconnected after tunnel teardown.
  • Crossbar locking. The crossbar selection is read under its lock.
  • Cleanups:
    • Named register fields instead of raw constants. For the ATC clock code the object code is unchanged.
    • The existing APB command helper is reused.
    • Kernel-doc for the new fields.
    • Documentation for thunderbolt_apple.dp_display.

Not changed: a few suggestions would change the register sequence that was validated on hardware:

  • the sleep/clamp override order;
  • leaving the overrides set after a stop;
  • the crossbar teardown order;
  • enabling DP IN interrupts before there is a handler.

Those are left as they are, with the reasoning in comments and commit messages. Suspend with an active tunnel is listed as not yet validated.

Every commit builds on its own with no warnings and passes checkpatch. These fixes are build- and KUnit-tested only; the previous revision is the one that was tested on the hardware listed in the description.

@iconidentify

Copy link
Copy Markdown
Author

Hardware retest of this revision (fead9c8) on the MacBook Pro 13" M1 (j293), with a CalDigit TS3 Plus on the second USB-C port (ACIO1). The tests before this update all ran on the first port.

  • Dock attached at boot: the display came up at 3840x2160 @ 60 Hz, and the compositor picked it up.
  • Unplug: the tunnel was torn down cleanly.
  • Replug: the display came back at 3840x2160 @ 60 Hz.

There were no warnings from the tunnel path.

t8103 boots with the Thunderbolt PCIe port held in reset. Bring the
port up when the tunnel activates, and do it before the port's IOMMU
probes: that IOMMU's invalidate command does not finish while the port
clock is still gated.

Program the port tunables and the reset table, release PERST, enable
the port clock, then program the root-port configuration tunables.
Link training stays in the host driver. If the port is already
running, the host does not replay the reset table. Endpoint memory on
t8103 is left posted.

The PCIe-C IOMMU uses the 64-stream USB4 register block: 16 KiB pages,
a 32-bit IOVA and a 36-bit physical address. The 42-bit four-level
geometry remains on T8110, which is the hardware that walks it.

An xHCI early handoff no longer faults when the capability length read
from a BAR is shorter than 16 bytes or not dword-aligned.

Tested on a MacBook Pro 13" M1 (j293) with a CalDigit TS3 Plus on
USB-1. The tunnel enumerated the dock bridges, four USB host
controllers and an I210. The USB devices and a gigabit link came up,
and the display on the same port still comes up.

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
@iconidentify iconidentify changed the title DisplayPort over Thunderbolt for M1 (t8103) DisplayPort and PCIe over Thunderbolt for M1 (t8103) Sep 23, 2026
Cable removal quiesces the PCIe tunnel and then removes the IOMMU.
The IOMMU's runtime resume and remove both issue a command, and that
command cannot complete once the port clock is gated. Every dock
unplug logged that timeout, then a follow-up quiesce skipped the port
and reported success.

Tell the IOMMU to stop issuing commands before the clock is turned
off, and let it issue them again only after the port is back up. An
acknowledgment timeout from a cable that is already gone is no longer
a failed quiesce when the local port has left the running state.

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
Some USB-C display adapters answer the first hotplug with a 128-byte
EDID whose product name is "Non-PnP" and whose preferred mode is
1024x768. The panel timings, including 3840x2160 at 120 Hz, show up
only after HPD drops and returns. macOS ends up with the panel mode.
The first Linux modeset was keeping the placeholder.

When a Type-C output copies that EDID, pulse HPD once. A second
placeholder is left as-is, so a display that really is that mode does
not loop. The pulse is cancelled when the cable goes away.

Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
oliverlukschander pushed a commit to oliverlukschander/asahi-j416s-display that referenced this pull request Sep 23, 2026
…kConfiguration

Found via aurora-silicon/linux#8, a real hardware-tested reference
implementation for a different SoC, built on Oliver's own earlier
t6020 tunnel-clock work. Reverts 0120's wrong-mental-model DP-AUX
approach entirely (phy-apple-atc.ko now byte-identical to 0119's
known-good hash) and 0118's speculative supportsHPD changes, keeping
only what the reference implementation confirms: attach a real PHY,
and defer crossbar activation until DCP has actually set a link rate.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
oliverlukschander pushed a commit to oliverlukschander/linux that referenced this pull request Sep 23, 2026
…figuration

Found via aurora-silicon#8, a real, hardware-tested (three docks,
multiple monitors) DisplayPort-over-Thunderbolt-tunnel implementation
for a different SoC, explicitly built on this project's own earlier
t6020 tunnel-clock work. It never touches DP AUX or any PLL-common
register (0120's whole approach was the wrong mental model); the real
difference is ordering: its Activate handler only wakes the DP IN
adapter, and defers crossbar mux selection to DidChangeLinkConfiguration,
gated on SetLinkRate having already started the tunnel pixel clock.

Our own dptxport_call_did_change_link_config() already has this exact
mechanism, already correctly gated on dptx->link_rate, with a comment
that already states the right idea -- but dptxport_call_activate() also,
unconditionally, brought up the crossbar and ACIO DPIN0 handshake
immediately, before any link rate existed. This routed a real analog
signal path through the crossbar with no pixel clock behind it every
time, all session -- consistent with DCP's own AUX probe finding
nothing coherent (INACTIVE_SINK_DETECTED, confirmed in the 0119 test)
and never proceeding to SET_LINK_RATE, so dptxport_tunnel_clock()
(already wired up correctly) never got a chance to run.

Remove the eager call from Activate. Revert 0118's GET_SUPPORTS_HPD/
supports_hpd changes (the reference implementation keeps supportsHPD
set for a tunnel route too; what actually distinguishes it is a
separate role bit this driver doesn't have yet, not supportsHPD).
Fully remove 0120's DP-AUX-enable additions -- phy-apple-atc.ko now
hashes byte-identical to 0119's already-known-good build, confirmed.
Keep 0118's PHY attachment and 0119's guard relaxation, both of which
match the reference implementation's own pattern.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
oliverlukschander pushed a commit to oliverlukschander/linux that referenced this pull request Sep 23, 2026
…nect

0119, 0121 and 0122 all reached the identical INACTIVE_SINK_DETECTED/
DPRX-timeout outcome tonight despite substantially different Activate/
crossbar ordering -- pointing at a signal we are not sending at all
rather than a timing issue reorderable on our end.

The reference implementation (aurora-silicon#8) packs a "role"
bit (0 = direct PHY, 1 = Thunderbolt/USB4 DP IN) into both
validate_connection's and connect's attributes field, alongside
supportsHPD. Our analog-DPIN path has never set it, always sending a
plain direct-PHY value even though this is a genuinely USB4-tunneled
connection. If DCP firmware's tolerance for a failed AUX probe depends
on this bit, that would explain the consistent outcome regardless of
ordering. Set it via dcp_is_usb4_output(), our existing equivalent of
the reference implementation's dedicated dptx_tunnel flag, and relax
validate_connection's reply check the same way theirs does (only the
role bit is allowed to differ in the echoed reply).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
oliverlukschander pushed a commit to oliverlukschander/linux that referenced this pull request Sep 23, 2026
The analog-DPIN USB4 connect path (dcp_dptx_connect()) has
unconditionally called phy_set_mode_ext(dp_route->phy, PHY_MODE_DP,
dcp->index) at connect time since candidate 0118, before DCP ever
gets to ACTIVATE/SET_LINK_RATE. This is a genuine Thunderbolt tunnel,
not a direct PHY connection -- both the aurora-silicon#8
reference implementation and this project's own tunnel-clock code
(apple_atc_right_usb4_tunnel_rate(), gated on
atcphy->mode == APPLE_ATCPHY_MODE_USB4) require the ATC PHY to stay
in USB4/TBT mode for the whole connection. Switching it to DP mode
reconfigures the SERDES lanes for direct DisplayPort signaling
instead of USB4 tunneling.

Traced while scoping a full PR#8 port: our own tunnel.c already polls
the generic, non-Apple-specific DP_COMMON_CAP_DPRX_DONE hardware bit
(tb_dp_dprx_start()/tb_dp_wait_dprx()) -- the same bit stuck at
DPRX=0 in every capture through candidate 0125. That bit is set by
the PHY hardware based on real AUX electrical activity, downstream of
DCP's software protocol. Forcing the tunnel's PHY out of USB4 mode
before that AUX/DPRX negotiation can complete is a plausible direct
cause, independent of anything DCP-protocol-level (which 0124 already
confirmed is clean end-to-end).

The dptxport[bind].atcphy assignment is kept -- it only tells DCP
firmware which PHY object to answer GET_MAX_LANE_COUNT against, and
is unrelated to the PHY's own runtime mode.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
oliverlukschander pushed a commit to oliverlukschander/asahi-j416s-display that referenced this pull request Sep 23, 2026
Found while scoping the aurora-silicon/linux#8 port: the analog-DPIN
connect path has switched the tunnel's ATC PHY into PHY_MODE_DP at
connect time since 0118, in every candidate since. Both the reference
PR and our own tunnel-clock code require the PHY to stay in USB4/TBT
mode for a genuine tunnel. Also traced DPRX detection to a generic,
non-Apple-specific mechanism in our own tunnel.c that polls a real
hardware bit downstream of DCP's protocol -- forcing the PHY out of
USB4 mode before that can complete is a plausible direct cause.
Tested as a quick, low-risk fix before the much larger architectural
port. Full reasoning in notes/2026-09-23-0126-stop-phy-mode-dp-switch.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
oliverlukschander pushed a commit to oliverlukschander/linux that referenced this pull request Sep 23, 2026
…t DP tunnel routing from aurora-silicon#8

Every candidate through 0126 relied on faking a USB4 tunnel route
through the Type-C alt-mode mux-state machinery (dcp_typec_route_set()
scoring a "usb4_xbar" candidate on a TBT-SVID mux notification) rather
than a genuine "a Thunderbolt DP tunnel just came up" trigger. DCP's
own AFK/EPIC protocol handling was already confirmed completely clean
(candidate 0124: zero retcode errors, zero reply mismatches) through
ACTIVATE, but nothing ever got DCP past that point to SET_LINK_RATE.

Ported the real mechanism from aurora-silicon#8 ("DisplayPort and
PCIe over Thunderbolt for M1 (t8103)", hardware-validated on a CalDigit
TS3 Plus, Kensington SD5560T and CalDigit TS4), adapted from t8103 to
T602X/j416s:

- New cross-module interface (include/linux/soc/apple/dp-tunnel.h):
  apple_dcp_tb_dp_tunnel(), apple_atc_dp_tunnel_rate(),
  apple_dpxbar_link_up/down(), found via symbol_get() so none of the
  four modules requires the others to be loaded.

- drivers/gpu/drm/apple/dcp.c: dcp_typec_route_activate()/deactivate()
  now take an explicit crossbar controller and defer a tunnel's mux
  selection to DidChangeLinkConfiguration instead of selecting
  immediately; dcp_typec_route_set() no longer creates tunnel routes
  from Type-C mux notifications, only tears down an alt-mode owner and
  ignores state while a tunnel owns the port; new
  apple_dcp_tb_dp_tunnel() entry point plus
  dcp_tunnel_crossbar_up/down()/set_rate()/dpin_activate() helpers
  (verbatim port, this side is SoC-agnostic); dcp_dptx_connect()
  collapsed from three USB4-specific branches (protocol-connect,
  analog-DPIN bind loop, lpdptxphy path) to the same single connect
  path used for a direct alt-mode PHY, differing only in
  dcp->dptx_dfp_port (0=dpphy, 1/2=dpin0/dpin1) and dcp->phy already
  being the route's own ATC PHY. Removed ~400 lines of superseded
  experimental scaffolding (dcp_usb4_arm_typec/auto_arm_work,
  dcp_usb4_protocol_connect, the analog-DPIN branch, lpdptxphy
  instantiation, and their module params).

- drivers/gpu/drm/apple/dptxep.c: SetLinkRate/Activate/Deactivate wired
  to the new dcp_tunnel_*() calls instead of the removed
  dptxport_tunnel_clock()/dptxport_native_dpin(); WillChange/DidChange
  crossbar hooks moved into the apcall dispatcher, matching the
  reference's own structure; the ATC PHY is no longer switched to
  PHY_MODE_DP for a tunnel (already fixed standalone in 0126, folded in
  here); dptxport_remote_target() drops the DPIN target field, using
  the same plain CORE|ATC|DIE|CONNECTED encoding as a direct PHY.

- drivers/thunderbolt/apple.c: the confirmed-ineffective dpin_aux
  "analog AUX serializer" mechanism (2026-09-21 candidates 0054-0056,
  reconfirmed 2026-09-23 candidate 0125 after everything else was
  independently fixed) is replaced by a new apple_dpin_ctx/
  apple_dpin_connect() mechanism, reusing this file's own confirmed-
  working DPTX_INACTIVE handshake (apple_dpin_handshake(), unchanged)
  generalized to whichever DP IN adapter (0 or 1) a tunnel actually
  lands on. Wired into the *existing* dp_tunnel_pre_activate/
  post_activate/deactivate hooks (already safely integrated into
  tunnel.c's tb_dp_activate() at the right lifecycle points) rather
  than adding the reference's own separate dp_tunnel_changed NHI hook,
  so this needed zero changes to shared Thunderbolt connection-manager
  code (drivers/thunderbolt/{tb,tunnel}.c are untouched, byte-identical
  thunderbolt.ko). The connector_np graph lookup (ACIO port@1 -> the
  Type-C PD controller's connector node) needed no device-tree change:
  confirmed present on this exact hardware by walking the live phandle
  before writing any code.

- drivers/phy/apple/atc.c: apple_atc_right_usb4_tunnel_rate() renamed
  to apple_atc_dp_tunnel_rate() to match the reference's cross-module
  symbol name, keeping this project's own T602X-specific
  implementation (the reference's own version hard-fails off
  t8103's fixed AUSPLL descriptor); now also accepts TBT mode, not
  just USB4.

- drivers/mux/apple-display-crossbar.c: apple_dpxbar_right_dpin0_bring_up()
  (kept, unchanged) generalized into apple_dpxbar_link_up()/link_down(),
  parameterized on whichever index/dispext is actually selected instead
  of always dpin0/source-2, for dcp_tunnel_crossbar_up/down()'s re-link
  path.

drivers/gpu/drm/apple/iomfb_template.c: one leftover reference to the
removed route->usb4_xbar field (right_frame_snapshot(), a diagnostic)
fixed to use route->active_xbar ?: route->xbar.

usb4_native_dpin/usb4_protocol_probe/dcp_usb4_native_route() are left
in place: still load-bearing in iomfb.c/iomfb_template.c/apple_drv.c
for USB4-tunnel-specific swap-completion and CRTC-candidate logic
outside this port's scope, and dcp_is_usb4_output() (which they
compose with) still correctly reflects route->tunnel under the new
mechanism.

Full reasoning, the two-agent scoping analysis, and per-file porting
checklists in the asahi-j416s-display repo (not committed here --
scratch analysis, not project history).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
oliverlukschander pushed a commit to oliverlukschander/asahi-j416s-display that referenced this pull request Sep 23, 2026
…ux#8

Full architectural port after 0126's quick fix landed a clean negative.
Every candidate through 0126 relied on faking a USB4 tunnel route
through Type-C alt-mode mux-state notifications, never a genuine
tunnel-came-up trigger -- DCP's own AFK protocol was already confirmed
clean end-to-end through ACTIVATE (0124), but nothing ever got it
further. Spans dcp.c/dptxep.c, drivers/thunderbolt/apple.c (reusing
this project's own existing, already-safe hooks instead of the
reference's separate NHI op -- zero shared Thunderbolt code touched),
atc.c and apple-display-crossbar.c. Full reasoning in
notes/2026-09-24-0127-port-thunderbolt-dp-tunnel-routing.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
oliverlukschander pushed a commit to oliverlukschander/linux that referenced this pull request Sep 24, 2026
Ported from aurora-silicon#8 (t8103, hardware-tested on three
docks/multiple monitors: CalDigit TS3 Plus, Kensington SD5560T, CalDigit
TS4). Its dp_tunnel_changed() pulses ADP_DP_CS_3_HPD_PROPAGATE on the DP
IN adapter right after tunnel activation and waits for
ADP_DP_CS_2_HPD -- an Apple host's DP IN adapter has no real physical DP
connector wired to it, so nothing else ever sets that bit, and every
downstream USB4 DP tunnel mechanism (AUX/DPCD, DPRX) is gated on it.

Found by comparing this project's own driver against that reference:
our port (0127) deliberately reused the existing dp_tunnel_pre/post_
activate/deactivate hooks instead of the reference's separate NHI op to
avoid touching shared tb.c/tunnel.c -- but in doing so, this one
specific register-level step from the same commit was never carried
over. Confirmed absent: zero references to ADP_DP_CS_3_HPD_PROPAGATE,
tb_dp_tunnel_notify, or an HPD-propagate pulse anywhere in this tree
before this commit.

This project's own diagnostic poller (apple_dp_aux_work(), drivers/
thunderbolt/apple.c) independently samples the DP IN adapter's CS0-CS13
registers every 500ms and logs any change; across all four dcpext1
failures so far (0128 x2, 0129, 0130 -- 96 samples total) it logs zero
changes, versus exactly one (the DPRX transition) on the one dcpext0
success (0127). That is consistent with the adapter's AUX engine never
being kicked into motion at all on this pipeline/port, which an
unpropagated HPD is a plausible, concrete explanation for.

Added tb_dp_apple_pulse_hpd(), called from tb_dp_activate() right
before this project's own dp_tunnel_post_activate hook (so the DCP glue
routes a display only after HPD has actually propagated, preserving the
reference's own ordering even though our hook lives one level deeper
than its dp_tunnel_changed callsite). Gated on tb_nhi_is_apple() (already
used elsewhere in this file) and tb_port_is_dpin(), both pre-existing
checks -- zero effect on non-Apple hosts or non-DP-IN tunnels. A failed
pulse is logged and the tunnel setup continues regardless, matching the
reference's own non-fatal handling.

Deliberately not ported this candidate: the reference's ADP_DP_CS_3_
NO_AUTO_LT hold-off on the DP OUT (hub) adapter, and its 5-NFC-credit
override for the DP IN video hop. Both are real, separate pieces of the
same hardware-tested commit, but this keeps the current test to one
variable; see notes/2026-09-24-0131-*.md for the reasoning and what
they would target if this alone is not enough.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
oliverlukschander pushed a commit to oliverlukschander/asahi-j416s-display that referenced this pull request Sep 24, 2026
…silicon/linux#8

Comprehensive comparison against the reference PR (t8103, hardware-tested
on three docks/multiple monitors), done at Oliver's request. The DCP-side
port (0127) already matches closely. But its Thunderbolt-side commit does
one more thing in generic tb.c/tunnel.c, gated behind an Apple-host check,
that never got carried over: pulsing ADP_DP_CS_3_HPD_PROPAGATE on the DP
IN adapter and waiting for ADP_DP_CS_2_HPD, because an Apple host's DP IN
adapter has no real physical connector wired to it. This lines up exactly
with the CS0-13-never-changes finding logged after 0130 -- an unpropagated
HPD directly explains registers that never move. Also corrects the
"currently installed" trailer, which had drifted after logging 0130's
confirmed result. Full comparison and reasoning in
notes/2026-09-24-0131-pulse-hpd-propagation-apple-host-dpin.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
oliverlukschander pushed a commit to oliverlukschander/linux that referenced this pull request Sep 24, 2026
…Apple host)

Direct follow-up to 0131. That candidate ported the HPD-propagate pulse
from aurora-silicon#8 alone: HPD now confirmed propagates ("Apple:
HPD propagated"), but DPRX still never asserts, and the failure shape is
otherwise identical (DEVICE_NOT_RESPONDING/DEVICE_NOT_STARTED at ~5s,
"DPRX timeout, keeping DP tunnel" at 12s). Ports the other two pieces of
the same hardware-tested commit (4a7fd72), held back from 0131 to keep
that a single-variable test:

- ADP_DP_CS_3_NO_AUTO_LT on the hub's DP OUT adapter while the tunnel is
  up, in tb_dp_activate(). Without it, the hub is free to run its own
  link training at the same time our host DPTX tries to train the sink
  through the tunnel -- a plausible, concrete reason AUX/DPCD would never
  produce a coherent reply even after HPD is asserted correctly.
- 5 NFC credits for the DP IN video hop, in tb_dp_init_video_credits()
  (this project's own captures have shown credits=1/credits=0 on this
  hop every run; the reference fixes it at 5 for any Apple host DP IN
  adapter).

Both gated on tb_nhi_is_apple() + tb_port_is_dpin() (the same checks
0131 already introduced), plus !tb_route() for the credits hop-iteration
case specifically (multiple hops are visited per path there, unlike the
tunnel-endpoint-only pulse/NO_AUTO_LT code, so the extra host-router
check guards against ever matching a downstream port by coincidence).
Zero effect on non-Apple hosts or non-DP-IN tunnels either way.

Full reasoning in notes/2026-09-24-0132-*.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
oliverlukschander pushed a commit to oliverlukschander/asahi-j416s-display that referenced this pull request Sep 24, 2026
…nfirmed HPD fix

0131's HPD-propagate pulse is confirmed working on real hardware ("Apple:
HPD propagated" fires correctly) but wasn't sufficient alone -- DPRX still
never asserted, monitor still dark. Ports the other two pieces of the same
hardware-tested aurora-silicon/linux#8 commit, held back from 0131
specifically to isolate the pulse as its own test: NO_AUTO_LT on the hub's
DP OUT adapter (stop it racing its own link training against our host's),
and 5 NFC credits for the DP IN video hop (ours has read 1/0 every run).
Full reasoning in notes/2026-09-24-0132-*.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
oliverlukschander pushed a commit to oliverlukschander/asahi-j416s-display that referenced this pull request Sep 24, 2026
… a correction

0132's HPD-propagate + NO_AUTO_LT changes applied cleanly but DPRX still
never asserted -- Oliver confirmed, monitor still dark. Also corrects the
record on 0132's video-hop credit change, which targeted port-level NFC
buffer bookkeeping (hop->nfc_credits) rather than the hop table's own
initial_credits field that apple_dp_dump_hop() actually prints -- harmless
but not the fix it was described as, and not relevant to AUX/DPRX anyway.

The full aurora-silicon/linux#8 Apple-host register comparison is now
exhausted without a picture. DCP's own ~5-second wait before DEVICE_NOT_
RESPONDING/DEVICE_NOT_STARTED has been unmoved by every host-side change
across eight connect attempts. 0133 is a pure diagnostic (zero behavior
change): dumps the ACIO analog block every 500ms instead of once at the
end, to finally see whether its internal state ever moves during DCP's
own attempt -- evidence this project has never actually gathered. Full
reasoning in notes/2026-09-24-0133-*.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
oliverlukschander pushed a commit to oliverlukschander/asahi-j416s-display that referenced this pull request Sep 24, 2026
…lved upstream for any Apple Silicon chip

The write-1-to-clear ack fired exactly as designed and had zero effect
(0x80000000 before and after); failure shape unchanged, Oliver confirmed
still no picture. That specific hypothesis is closed.

Web research (not previously done this project): Sven Peter's actual
upstream Asahi Linux Thunderbolt series (covering this exact SoC family,
t8103/t600x/t8112/t602x) explicitly states DisplayPort tunneling is not
yet implemented -- "requires additional work and reverse engineering
that is not done yet." The only place DP tunneling has ever been shown
working on real Apple Silicon hardware is aurora-silicon/linux#8, for
t8103 (M1) specifically, a different SoC generation than this machine.
Ruled out along the way: CONFIG_RESET_APPLE_CIO is enabled, this
project's own driver already correctly deasserts the ACIO reset
controller, and the ACIO's Cortex-M3 coprocessor is confirmed alive via
RTKit. This reframes the effort: not a missed foundational piece, but
genuinely unsolved territory for this chip generation. Full context in
notes/ACTION-LOG.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
oliverlukschander pushed a commit to oliverlukschander/asahi-j416s-display that referenced this pull request Sep 24, 2026
0136 installed and confirmed on hardware: the -22 crossbar failure is
gone (captures/2026-09-24-0136-boot-kernel.log shows a real "Switched
dpin0 to dispext0,0" enable, no crossbar-up failure, DPRX_DONE=1 reached,
SET_ACTIVE_LANE_COUNT 4 accepted) -- but dcp_dptx_connect still timed out
waiting for link configuration.

Root-caused that timeout: dptxport_call_set_active_lane_count() only
completed linkcfg_completion for a USB4 tunnel if dcp_usb4_drm_allowed()
(usb4_force_dptx) was true, a flag with no way to ever be set true since
commit 0dc9f50 removed the old manual-training sysfs knob that used to
set it, without removing this now-dead gate. The hardware-validated
aurora-silicon/linux#8 reference completes linkcfg_completion here
unconditionally. Every USB4-tunneled connect attempt since 0127 has been
structurally unable to complete this wait -- a fourth, independent defect
alongside the port/pipeline confound, the crossbar -22, and dcpext1's own
firmware-silence problem.

Fixed and built in linux-aurora-pr (branch j416s-usb4-dpin, commit
992ff65b5, not pushed -- no origin tracking ref for this branch, matching
every prior candidate). scripts/manage-0137.py stages the same module set
as 0135/0136 except a rebuilt appledrm.ko. check already run (no sudo);
install needs sudo and a reboot to test, not yet run.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
oliverlukschander pushed a commit to oliverlukschander/linux that referenced this pull request Sep 24, 2026
… count

dcp_dptx_connect()'s wait_for_completion_timeout() on linkcfg_completion
is now shared by both the alt-mode and USB4-tunnel connect paths (since
0dc9f50's port from aurora-silicon#8). For a USB4 tunnel,
dptxport_call_set_active_lane_count() only completed it if
dcp_usb4_drm_allowed() (usb4_force_dptx) was true -- a flag that used to
be a live, writable knob (module_param_cb usb4_dptx_train) for a manual
training workflow that 0dc9f50 removed wholesale when
apple_dcp_tb_dp_tunnel() replaced it with automatic tunnel detection.
usb4_force_dptx itself, and this gate, were left behind with no way left
to ever set it true.

Confirmed on real hardware (candidate 0136, right port, dcpext0 forced via
usb4_route_prefer_fixed_diag): request_display succeeds, the crossbar
selects cleanly (0136's own fix), DPRX_DONE=1 is reached, and
SET_ACTIVE_LANE_COUNT 4 is accepted -- but dcp_dptx_connect() still times
out 8s later, because linkcfg_completion is never completed (firmware
does not send FORCE_HOTPLUG_DETECT, the only other completion site, for a
tunnel). This has been happening on every candidate since 0127.

The reference completes linkcfg_completion here unconditionally whenever
lane_count > 0, with no such gate. usb4_lane_completion (also
USB4-specific, always completed alongside the gated linkcfg_completion
call) has no wait_for_completion() anywhere in this tree -- a fully dead
completion. Match the reference: complete linkcfg_completion
unconditionally, and remove the now-fully-dead usb4_force_dptx /
dcp_usb4_drm_allowed() / usb4_lane_completion.

Built and verified (make in src/appledrm/, vermagic 7.1.12-2.5-1-ARCH, no
new warnings).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
oliverlukschander pushed a commit to oliverlukschander/linux that referenced this pull request Sep 24, 2026
tb_dp_activate(tunnel, false) returns early on tb_dp_port_enable()
failure for either port. When a physical unplug tears down a DP tunnel,
tb_handle_hotplug() calls tb_sw_set_unplugged() on the departing switch
before tb_free_invalid_tunnels() -> tb_tunnel_deactivate() runs, so
tb_dp_port_enable(dst_port, false) hits tb_port_read/write's
"if (port->sw->is_unplugged) return -ENODEV" short-circuit -- a
deterministic, zero-I/O failure, not a hardware timing issue. That trips
"if (ret) return ret;" before ops->dp_tunnel_deactivate() ever runs.

On this hardware, ops->dp_tunnel_deactivate is apple_nhi_dp_tunnel_deactivate()
(drivers/thunderbolt/apple.c), which is the only place that clears
apple_dpin_ctx's "alive" latch. With it skipped, "handed" never clears
either, so a fresh dp_tunnel_post_activate() on a later replug hits
apple_dpin_work_fn()'s "if (c->handed) return;" guard and never re-enters
apple_dcp_tb_dp_tunnel() -- the physical tunnel reforms but DCP's own
connect flow is never re-armed. Confirmed: a live unplug/replug during
this session's testing produced a fresh Thunderbolt tunnel (new device
enumeration, "DP tunnel paths up") but zero new dcp_dptx_connect/
request_display/"display routed" log lines.

tb_tunnel_deactivate() (this file's only caller of tunnel->activate on
the deactivate path) already discards the return value entirely, so
nothing observes tb_dp_activate()'s return code when active=false.
Change both "if (ret) return ret;" checks to "if (ret && active)": the
activate (active=true) path is byte-for-byte unchanged, and on
deactivate this guarantees ops->dp_tunnel_deactivate() always runs
regardless of whether the departing port's register I/O could still
succeed.

The reference (aurora-silicon#8) doesn't have this gap: it notifies
through a dedicated, unconditional tb_dp_tunnel_notify() called before
any register I/O, not folded into the generic activate/deactivate
register-programming path this tree uses.

Built and verified (make in src/thunderbolt/, vermagic
7.1.12-2.5-1-ARCH, no new warnings; thunderbolt_apple.ko unchanged).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add a second mux-control entry (index 1) per Type-C PHY crossbar to
both dcpext0's and dcpext1's mux-controls/mux-control-names, named
"typecN-usb4" alongside the existing "typecN" (direct alt-mode) entry.
These are the devicetree handles the display crossbar driver resolves
by name to select a USB4/Thunderbolt DP tunnel route (dpin0/dpin1)
rather than a direct Type-C alt-mode route, on T602X (M2 Pro) j414/j416
machines.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
cb2206 and others added 5 commits October 1, 2026 07:38
… display

With only the SoC checks widened, a Thunderbolt display on the MacBook
Pro 14" M1 Pro (j314s) got as far as an active DP IN adapter. DCP then
stayed silent for 5 s after Activate, every DPTX call timed out, and it
reported link faults 22 and 24 without ever sending SET_LINK_RATE.

All three Type-C routes of j314s use dcpext1 (apple,typec-mux-indices =
<2 2 2>), while the DP IN output idles at dispext0. Point the output at
the pipeline that takes the tunnel and wake the ATC's DP clock path
before the DPTX connect, and put the output back at its idle source when
the route goes away without the crossbar having come up.

Both helpers return -EOPNOTSUPP where they do not apply, so t8103 and
T602X behave as before.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Christian Bartels <christian@thebartels.de>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

[Chris Kearney: ported onto aurora-silicon#8 on top of the dual DP
 tunnel series; the tunnel is prepared once its slot is taken.]
Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
The M1 Pro/Max ATC has the t8103 DP IN adapter registers, the same
crossbar and the same tunnel pixel clock sequence. Install the DP tunnel
hook there too, and clear the DP IN interrupt status the way t8103 does.

Tested on a MacBook Pro 14" M1 Pro (j314s) with an LG UltraFine 5K on
both left ports: 3840x2160@60 at cold boot, on hotplug and after
suspend-to-idle. t6001 is untested.

  apple-display-crossbar 70304c000.mux: dpin0: source preselected, MUX_CTRL=00002002
  phy-apple-atc 703000000.phy: DP tunnel open (CFG0=11833fef SLEEP_CTRL=15570cff TX_DP_CTRL0=0000e00d)
  apple-dcp 28cc00000.dcp: display routed to Thunderbolt DP tunnel dpin0
  apple-dcp 28cc00000.dcp: DPTXPort: SET_LINK_RATE 0x14
  apple-display-crossbar 70304c000.mux: Switched dpin0 to dispext1,0
  apple-dcp 28cc00000.dcp: dcp_hotplug() connected:1 valid_mode:0 nr_modes:5

The second DP tunnel of the display is still refused ("port already
routed"), so it runs as a single 4K stream, not as two 5K tiles.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Christian Bartels <christian@thebartels.de>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

[Chris Kearney: ported onto aurora-silicon#8, where dp_display
 defaults to on; t600x takes that default too.]
Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
iomfb_poweroff() waits 50 ms for the clear swap and sets dcp->crashed
when that times out. When a Thunderbolt display is unplugged, the
firmware is busy powering the external pipe down on its own and answers
the swap a little later. Nothing logs the timeout, but from then on
dcp_crtc_atomic_check() fails every commit with -EINVAL, so a replugged
display stays dark until reboot:

  [CRTC:73] atomic driver check failed

A real firmware crash is reported through the RTKit crash callback. Wait
500 ms, warn on a timeout and carry on with the power-off.

Seen on j314s with an LG UltraFine 5K: unplug, then replug. With this
change the unplug logs "dcp_poweroff() done" and the display comes back
on replug.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Christian Bartels <christian@thebartels.de>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

[Chris Kearney: ported onto aurora-silicon#8, where DjDeveloperr's
 "preserve Type-C routes across blanking and detach" already stopped
 setting crashed but returned early; this carries on with the power-off.
 This is also the DCP unplug wedge Oliver Lukschander reported on #8.]
Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
The per-port Type-C connectors carry DisplayPort (DP alt mode or a
Thunderbolt/USB4 DP tunnel), but were registered as
DRM_MODE_CONNECTOR_USB, which DRM reserves for USB display adapters
(gud, appletbdrm). Userspace keys on the type: systemd-logind only
counts VGA/DVI/DP/HDMI/TV connectors as external displays, so a
docked lid close suspended the machine instead of honoring
HandleLidSwitchDocked=ignore.

Register them as DisplayPort. The driver's internal "is Type-C" checks
use dcp->connector_type / fixed_connector_type and are unaffected.
Connectors are now named DP-N instead of USB-N.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: RJ Skerry-Ryan <rryan@alum.mit.edu>
A Type-C port's encoder spans every pipeline that can drive the port, and
routing narrows its possible_crtcs to the pipeline actually chosen,
relying on userspace to re-read them on the following hotplug.
Compositors that read possible_crtcs once when the connector appears
(aquamarine, used by Hyprland) never see that. With a USB4 dock carrying
two streams, whichever connector connects first takes the lowest free
CRTC. When that is the DPIN1 connector, it gets dcpext0's CRTC while the
stream is routed to dcpext1, every modeset fails the atomic check with
-EINVAL, and both dock displays stay dark until the dock is replugged
and happens to connect in the other order.

possible_crtcs is meant to be static. On the dual-stream machines
(J414s, J416c):

- Fix the DPIN1 connector to the lowest-indexed Type-C-only pipeline
  (dcpext1), where DPIN1 is always routed, and route DPIN1 only within
  the connector's possible_crtcs.
- Leave the primary connector spanning all its pipelines and stop
  narrowing or restoring possible_crtcs at runtime.
- Route direct DP-alt displays to the lowest free CRTC, which is what a
  compositor picks from the port's fixed possible_crtcs, instead of
  steering them away from dcpext0.

A direct DP-alt display now takes dcpext0 when it is free, so a
single-stream dock on another port lands on the next free pipeline.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: RJ Skerry-Ryan <rryan@alum.mit.edu>

[Chris Kearney: ported onto aurora-silicon#8. dcp_typec_dual_stream()
 is apple_dp_tunnel_t602x() here, so j414c and j416s are dual-stream too;
 they share the j414/j416 device tree and the dcpext0/dcpext1 layout.]
Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
@iconidentify

Copy link
Copy Markdown
Author

New head 947026902232: 13 commits on top of 3fcb3823b49e, bringing the open Thunderbolt display PRs into #8. Every commit keeps its author, and each ported one has a short note saying what changed in the port.

Two displays through one dock on every M2 Pro/Max laptop (DjDeveloperr's #46, 6 commits):

Thunderbolt display on M1 Pro/Max (@cb2206's #64, 5 commits): t600x runs the t8103 tunnel clock and crossbar flow, with the DP IN source preselected before DCP probes AUX. His clear-swap fix (wait 500 ms, warn, carry on with the power-off) replaces #46's early return. That's the DCP unplug wedge from Oliver's report.

From rryan's #50: Type-C connectors are DP-N instead of USB-N, so logind counts them and a docked lid close doesn't suspend. The second connector's possible_crtcs is fixed, because Hyprland/aquamarine reads it once. #50's PCIe-C cold init is not in yet: it overlaps #8's own PCIe tunnel work, and I'll go through it separately.

Tested: the head builds, and it boots on a J313 with dock displays on, both CIO controllers bound, DP-1/DP-2 connectors and no oopses, with no omarchy-m-test regressions. It hasn't run on a T602X or t600x machine in this combination yet:

iconidentify added a commit to iconidentify/aurora-linux that referenced this pull request Oct 1, 2026
Pin linux-aurora 7.1.12.aurora2-11.17, which adds to 11.16:
- two independent displays through one dock on every M2 Pro/Max laptop
  (DjDeveloperr's #46, ported onto aurora-silicon#8), with live
  USB3 bandwidth accounting so the second tunnel is admitted;
- Thunderbolt display on M1 Pro/Max (cb2206's #64), and a clear-swap
  timeout at power-off no longer wedges the display until reboot;
- Type-C display connectors reported as DisplayPort (DP-N), so a docked
  lid close does not suspend, and fixed possible_crtcs for the second
  stream, which Hyprland reads once (rryan's #50);
- Touch ID enrol and verify refused with EROFS instead of wedging the
  enclave when xART writes are off (aurora-silicon#69);
- EDIDs longer than the base block announces are accepted (#67).

Type-C display connectors are now named DP-N instead of USB-N. The other
packages are unchanged from 11.16.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FwdG63dRaMrRbYrUBkSa1i
Resolves the conflicts with the T8140 PCIe root-port bring-up (AsahiLinux#151).
Both sides only add: this branch's PCIe-C tunnel state and properties
(resets, apple,pciec-kernel-init, apple,tunable-*) and the T8140 PIODMA
bootstrap (apple,piodma, map_bus gating, Neo enumeration) are all kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FwdG63dRaMrRbYrUBkSa1i
@iconidentify

Copy link
Copy Markdown
Author

Merged aurora-wip 3bb0a6104a11 into this branch (99a554596d0a) to resolve the conflict with the T8140 PCIe root-port bring-up (#151). Both sides only added code, so everything is kept: the PCIe-C tunnel state and properties (resets, apple,pciec-kernel-init, apple,tunable-*) and the T8140 PIODMA bootstrap (apple,piodma, map_bus gating, Neo enumeration). The same merge is in the aurora-sep 11.20 release, which boot-tested clean in our lab.

Status of the two related PRs:

@oliverlukschander

Copy link
Copy Markdown

Rerun of 947026902232 on j416s with two displays on the same OWC Thunderbolt 5 hub:

  • a BenQ at 2560×1440 via a VMM7100, hub DP OUT 1:19
  • a BenQ via a USB-C video adapter, hub DP OUT 1:11

Works

  • Boot with both attached. 0:5 <-> 1:19 goes to DCP 289c00000 and 0:6 <-> 1:11 to
    315c00000. Both show a picture, with no bandwidth error. Taint S only, no WARN/BUG.
  • DPMS and output disable/enable. 3× each, per display, with the other display on: 12/12
    back, with no AUSPLL or pixel-clock lines. The AUSPLL fix still holds with the per-DP-IN clock.
  • s2idle with both displays. Both come back without a replug, which drm/apple: support independent dual dock DP streams on J414s #46 alone couldn't do.
    • router_sleep=1: pictures 3.2 s and 3.6 s after resume.
    • Default router_sleep=0: 10.1 s and 10.4 s, the known 8 s dcp_dptx_connect wait.
  • Unplug wedge. VMM7100 directly on the port (DP alt mode), unplug/replug 5×: 5/5
    dcp_poweroff() done and the picture back each time. clear swap timed out never fired. This
    used to wedge 2 times in 4. Thanks @cb2206.
  • Docked lid close. logind reports Docked=true with the DP-N connectors, so no suspend.

New: a hub replug can take 14 s instead of 2–3 s

This happened on 2 of 3 replugs:

  1. The first tunnel is 0:5 <-> 1:11. About 10 ms later it is torn down and rebuilt as
    0:5 <-> 1:19 plus 0:6 <-> 1:11.
  2. DCP 289c00000 has already started dcp_dptx_connect() for the torn-down tunnel. It logs
    link fault 22/24, then waits until timed out waiting for port 0 link configuration
    (10.25 s).
  3. Only then does replacing stale display route for new tunnel recover it. DCP 315c00000
    waits behind it.

Result: pictures 13.8–14.1 s after the Thunderbolt attach. In the replug where 1:19 came first,
they were 1.8 s and 3.0 s. I never saw the transient pairing with #46 alone. Pairing in tb.c is
first-come, so that may just be luck.

Why it can't recover sooner, from reading the code:

  • apple_dcp_tb_dp_tunnel() holds dcp_typec_fabric_lock across dcp_dptx_connect_oob()
    (dcp.c:764, 876).
  • The connect waits on linkcfg_completion for up to DPTX_TUNNEL_CONNECT_TIMEOUT (8 s,
    dcp.c:1524). Only SET_ACTIVE_LANE_COUNT or FORCE_HOTPLUG_DETECT complete it
    (dptxep.c:462, 777). A tunnel teardown doesn't.
  • dp_wq is ordered (apple.c:2353), so dpin1's handoff waits for dpin0's.

This is the same wait as the router_sleep=0 resume. Possible fixes, smallest first. I haven't
tried any of them:

  • Debounce the dpin handoff: delayed_work, ~150 ms in post_activate, 0 in deactivate.
  • Let apple_nhi_dp_tunnel_deactivate() abort a pending connect, e.g. complete
    linkcfg_completion with a cancel flag.
  • Don't hold the fabric lock across the connect wait, and don't serialize the two dpins. This
    would also cover the resume wait.

Two side effects of the first-come pairing

  • After both resumes, the tunnels came back as 0:5 <-> 1:11 and 0:6 <-> 1:19, so DP-3 and
    DP-6 swapped monitors. That's harmless here, because Omarchy matches displays by EDID.
  • The USB-C-adapter display offers at most 1920×1080 when its tunnel is 0:6, but 2560×1440 when
    it is 0:5. The VMM7100 display offers 2560×1440 on either.

router_sleep still defaults to 0 in this head. The two-display numbers above are one more data
point for the "no kept PCIe tunnel" default.

@cb2206

cb2206 commented Oct 2, 2026

Copy link
Copy Markdown

Run of 99a554596d0a on j314s (14" M1 Pro) with an LG UltraFine 5K on Thunderbolt, left port, as you asked for #64. All three cases work.

The kernel is built from a tree whose #8 Thunderbolt code is identical to 99a554596d0a, plus two local patches that aren't in #8: PCIe-C kernel init for t600x (pcie_apple.t600x_tunnel_kernel_init=1) and DCP DP audio. router_sleep is at its default N.

Boot with the LG attached

  • 3840×2160@60 on DP-1. The connector rename from USB-1 works.
  • The full LG USB tree comes up over the PCIe tunnel: hubs, USB Audio, camera, brightness controls. boltctl shows it authorized.
  • No DART faults and no AER errors. dpin1: DP tunnel setup failed: -16 shows up once. The LG only uses dpin0, and it's the same on my older test kernel.

Unplug and replug

  • Unplug: dcp_poweroff() done, DP tunnel clock stopped, dpin0: DP tunnel down. No clear-swap timeout. One tunnel reset acknowledgment timed out (tunstat 0x1) from pcie-apple, which is harmless.
  • Replug: cable state 1 at 54.06 s, dpin0: DP tunnel up at 55.48 s, PCIe link up at 55.68 s, modeset done at 61.48 s. So the picture is back about 7.4 s after the attach. That includes the ~6 s DCP connect wait, so there's no 14 s stall like on the hub replugs. USB tree and audio are back too.

s2idle, 22 s, LG attached

  • Both DCPs power off and on. The PCIe tunnel and the LG USB tree stay up through the sleep.
  • After suspend exit at 85.26 s, DCP 28cc00000 refuses the first three modesets with setmode failed / Modeset done, but pipe not enabled: fSoftPowerState=1, fDisplayPowerState=0, fHardPowerState=1, at 85.26, 86.13 and 87.35 s. The fourth, at 88.58 s, works (switch to normal mode succeeded). The picture is back 3.3 s after resume, with no replug.
  • The failed modesets didn't cause any visible problem. They look like the display power state lagging behind the first modesets after resume.

I'll close #64 now that its commits are in #8.

On the M2 Pro/Max 14" and 16" laptops (J414s/c, J416s/c) the Type-C
ports share the external pipelines: dcpext0 (which also drives HDMI)
and dcpext1, plus dcpext2/3 on the M2 Max boards.  Since "drm/apple:
Keep dual-stream Type-C possible_crtcs fixed", every port's connector
offers all of them.  Compositors read possible_crtcs once and pair
connectors with CRTCs themselves: aquamarine (Hyprland) walks the CRTCs
in index order and gives each to the first connected connector, in
connector order, that can use it.  The fabric instead hands out
pipelines in whatever order the Type-C and Thunderbolt events arrive.

That is a regression from the commit above on a J416c with two direct
DP-alt monitors at boot: when port 1's event was handled first, port 1
took dcpext0 and port 0 dcpext1, and Hyprland paired them the other way
round.  Each connector's modes were then checked against the other
monitor's list, native modes failed the atomic check with -EINVAL, and
only modes both monitors share could be set.  fbdev, which pairs every
connected display afresh on each hotplug, and a dock's DPIN0 stream
attaching after a direct display cross the same way.

Until a compositor owns the display, keep the routes equal to that
pairing computed from scratch: pipelines in CRTC index order, each to
the first stream that wants one and has it in its possible_crtcs, with
the primary connectors in port order ahead of the DPIN1 ones.  A hybrid
pipeline whose HDMI output is live is left to it, unless a tunnel holds
it.  A direct DP-alt stream wants a pipeline while its sink asserts
HPD, as only then can its connector read connected.  Only direct routes
move, each to exactly its planned pipeline.  A Thunderbolt tunnel never
moves once set up.  A direct stream planned onto a pipeline a tunnel
holds stays unrouted and reads disconnected, since putting it anywhere
else would have the compositor cross it with another display; it is
then left out and the pass re-run, so the streams after it are not
planned one pipeline off.

A move is a disconnect and reconnect: every route that moves is taken
down before any comes up, so two never share a pipeline, HPD is
replayed on the new pipeline, and the hotplugs make fbdev and userspace
re-probe.  The pass runs on every DP-alt and tunnel attach, detach and
HPD change, when HDMI is plugged in while a Type-C route has its
pipeline, once DRM is registered, and from postclose when a file that
was DRM master is closed; drm_release() has taken that file off the
file list by then.

A compositor owns the display while any open file has been DRM master:
it pairs its connectors when it starts and keeps that pairing across a
VT switch, where it drops master but keeps the device open.  Until the
last such file is closed routing stays as it was, the lowest free
pipeline on DP entry with preferred_route honoured, and nothing moves.
The check walks the file list under filelist_mutex, taken inside the
fabric lock; nothing takes the two the other way round.

cd321x does not report an unchanged DP state again, so a port left
without a pipeline used to stay dark for the session.  When a route
lets its pipeline go while a compositor owns the display, such a port
now goes back to the pipeline it last had if that is free, as the
compositor keeps a reconnected connector's CRTC, and otherwise takes the
lowest free one, the CRTC it gives a newly connected connector.

Hotplug and retrain work queued for a Type-C connector can run after
its route is gone and connector->dcp is NULL; check for that instead of
dereferencing it.

possible_crtcs stay static, since compositors read them only once, and
the DPIN1 connector stays fixed to dcpext1.  Single-stream machines take
none of the new paths.

Signed-off-by: Chris Kearney <chris@kearneymail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FwdG63dRaMrRbYrUBkSa1i
@iconidentify

Copy link
Copy Markdown
Author

Added 581d7e3, "drm/apple: route Type-C displays in compositor pairing order". It fixes a regression from "Keep dual-stream Type-C possible_crtcs fixed", which this branch carries: on J414/J416, two monitors plugged straight into two USB-C ports at boot could each be paired by Hyprland with the other's pipeline, leaving both on the one mode they share.

  • Until a compositor has opened the device as DRM master, the routes follow the pairing a compositor starting now would make: CRTCs in index order, each to the first wanting connector in connector order. That covers boot, plymouth quitting and logout. Only direct DP-alt routes move; tunnels never do.
  • While a compositor holds the device, including across VT switches, routing is unchanged from before. The one addition: a port left without a pipeline gets one when a pipeline frees up, because cd321x doesn't re-report an unchanged DP state.
  • Fixed possible_crtcs and DPIN1 on dcpext1 are kept. Single-stream machines take none of the new paths.
  • Simulation: we checked it against aquamarine's pairing over about 250k boot, hotplug, dock and HDMI sequences per machine. Without a dock it is never worse than before. The remaining dock-only limits are described in the commit.

It ships in iconidentify/aurora-linux 11.23. It's boot-tested for regressions on M1, but not yet run on M2 Pro/Max hardware, so J414/J416 test reports are very welcome.

@rryan

rryan commented Oct 3, 2026

Copy link
Copy Markdown

j416c (MacBook Pro 16" M2 Max) results for 581d7e36ea84

Setup: 581d7e36ea84 plus two local patches (below), production config, m1n1 1.6.1 with this tree's DT in boot.bin. CalDigit USB-C Pro Dock (Thunderbolt 3, Titan Ridge xHCI 8086:15f0), two 3840×2160@60 monitors on the dock, Hyprland.

Works

  • Boot with the dock attached: both dock displays and the internal panel come up. 0:5 and 0:6 route to dcpext0 and dcpext1 with no bandwidth error.
  • Hotplug, repeated many times: both displays come back every time.
  • Docked lid close: logind sees DP-N, so the machine stays awake (clamshell).
  • s2idle undocked; displays on all three outputs after wake.
  • With the two patches below: dock USB, Ethernet (r8152), SD reader and webcam over the PCIe tunnel, including replug and replug after s2idle.

Needed for PCIe-C on t602x (J414/J416) — branch: https://github.com/rryan/linux/tree/j416c-pr8

This replaces the cold init from #50. It reuses this PR's apple_pcie_tunnel_cold_init() and apple_pcie_tunnel_prepare() instead of a second copy.

  1. arm64: dts: apple: t602x-j414-j416: Enable PCIe-C kernel init
    • apple,pciec-kernel-init on apciec0..2, plus the dart-apciec0..2 apple,tunable values iBoot puts only in the live ADT.
    • resets = <&ps_atcN_pcie>. Without it, apple_cio_start() skips the PCIe-C reset. After a failed first tunnel activation, the port then stayed at link 0x81000200 (link didn't come up) on every replug until reboot. With the reset, replug recovers.
  2. PCI: apple: Cold-init t600x/t602x PCIe-C ports behind a module parameter
    • apple_pcie_tunnel_prepare() matches apple,t6000-pciec too, gated by pcie_apple.tunnel_kernel_init=1, since the PR's comment says T6020 PCIe-C may be m1n1's. t8103 stays unconditional.
    • oe-fabric is optional: t602x has no such region. apple_pcie_tunnel_init_resources() failed probe with PCIe-C oe-fabric region is required.
    • apple_pcie_resume_noirq() returns early when bus_stopped is set (the dock was lost during suspend), instead of resetting or starting the ports against a missing tunnel.

These have only been tested on j416c with this TB3 dock. cb2206 carries a similar local t600x_tunnel_kernel_init for j314s, so one shared opt-in may suit both.

Open issues seen

  • Every attach: the first 0:3 <-> 1:8 (PCI): activation failed, a reconnect, then success on the second attempt.
  • device links to tunneled native ports are missing! after each ACIO firmware restart.
  • s2idle: Bluetooth (BCM4388) times out after resume (hci0: command 0x0c01 tx timeout). Probably not this PR; noting it for completeness.
  • No wake from a dock USB keyboard while suspended (lid closed). Unclear whether that's expected on this platform yet.

Tested with the help of Claude Code.

@jacobragsdale

Copy link
Copy Markdown

j314s (14" M1 Pro) report with a TB4 dock-monitor, the Dell U4025QW, on sep-7.1.12.aurora2-11.25.1 (iconidentify/aurora-linux custom/sep 0cde04307749): iconidentify#13

  • Display: 5120x2160@60 over the DP tunnel (HBR3 x4, link 2x20 Gb/s) on all three ports, including the right (f01ac0000.cio). Attached at boot and hot plug/unplug/replug all clean. The apple_nhi_remove teardown hang seen on stock aurora2-7 is gone.
  • Dock USB: works over the USB3 tunnel without PCIe-C.
  • s2idle: recovers without a replug, but always by dropping and re-enumerating the router, not by restoring the tunnel.
    • router_sleep=N: 7.3 s to picture. DCP setmode failed, then tps6598x 0-003f: failed to configure Type-C connection: -11, then the link drops.
    • router_sleep=Y: 5.8 s. The router is already gone at resume, followed by tps6598x ... -67.
    • Logs for both are in the issue, for the M1/DP-only-dock router_sleep question.

Tested with the help of Claude Code.

@cb2206

cb2206 commented Oct 3, 2026

Copy link
Copy Markdown

@rryan thanks, I tried your 64937227f149 on j314s (14" M1 Pro, apple,t6000-pciec) with an LG UltraFine 5K on Thunderbolt 3, left port. Your commit works there unchanged, with the same t8103 cold-init sequence, so one shared opt-in covers t600x too.

Setup: #8 at 581d7e36ea84 (on the 11.22 tree), your 64937227 as is, pcie_apple.tunnel_kernel_init=1. For the comparison I kept my old local t600x sequence behind a test switch.

t8103 sequence (your commit as is) my separate T600x sequence
Boot with the LG attached cold init done, status 0x3 link 0xa9000200, Link up. 4K@60, LG USB tree (hubs, audio, camera, controls), no DART faults identical
Unplug / replug cold init again, Link up 1.3 s after cable attach, picture at +7.4 s, USB and audio back same (yesterday, on #8 99a554596d0a)
s2idle, 25 s PCIe tunnel and USB tree kept through the sleep, picture back at +3.3 s without a replug same (yesterday, on #8 99a554596d0a)

No hard reset or link failure with the t8103 order on t600x. So my separate T600x sequence (tunables before APPCLK, Intr2AXI pulse, modelled on #50) isn't needed, and I'll drop it. Your resume guard wasn't exercised: the tunnel survived the sleep.

What t600x still needs is the device tree, which j314s's DT doesn't have yet. I add it at runtime for now:

  • apple,pciec-kernel-init on the PCIe-C hosts.
  • The PCIe-C DART apple,tunable. I used exactly your J416c values. On t600x they're not read from the live ADT. They work, but someone with an M1 Pro/Max in macOS should confirm them with ioreg.
  • A DART apple,dma-range of <0x0 0x80000000 0x0 0x7fe00000>. The T600x ADT gives these DARTs vm-base 0, vm-size 0xffe00000 and a vm-reserve over the low 2 GiB (the MMIO windows), with no 1 TiB alias as on t602x. Without the limit, the driver builds a four-level table and the first xHCI DMA faults.
  • I haven't added your resets = <&ps_atcN_pcie>. Replug recovered without it here, but I didn't hit a failed first activation, which is where you needed it.

If that's useful, I can send a t600x DT commit like your 3be6b726 for the four t600x PCIe-C hosts with those three properties, plus the resets if they exist on t600x. jacobragsdale's j314s with a TB4 Dell dock-monitor would be a good second test for it.

@rryan

rryan commented Oct 4, 2026

Copy link
Copy Markdown

@rryan thanks, I tried your 64937227f149 on j314s (14" M1 Pro, apple,t6000-pciec) with an LG UltraFine 5K on Thunderbolt 3, left port. Your commit works there unchanged, with the same t8103 cold-init sequence, so one shared opt-in covers t600x too.

Great to hear! Feel free to open a PR against https://github.com/rryan/linux/tree/j416c-pr8 if you'd like.

@rryan

rryan commented Oct 4, 2026

Copy link
Copy Markdown

@rryan thanks, I tried your 64937227f149 on j314s (14" M1 Pro, apple,t6000-pciec) with an LG UltraFine 5K on Thunderbolt 3, left port. Your commit works there unchanged, with the same t8103 cold-init sequence, so one shared opt-in covers t600x too.

Great to hear! Feel free to open a PR against https://github.com/rryan/linux/tree/j416c-pr8 if you'd like.

Or actually, @iconidentify could you recommend the best path forward? I have added more commits to my branch that fix some more issues I am having but may be out of scope for this PR:

  • Wake from s2idle when a device is plugged into a USB hub, or when you hit a key or move a mouse attached to the USB hub connected through a PCIe-tunneled-over-USB-C dock. This lets me keep the macbook closed and use my external keyboard and mouse to wake it up from sleep.
  • Bluetooth wake up from sleep (I use a bluetooth mouse). Unrelated to PCIe

@cb2206

cb2206 commented Oct 4, 2026

Copy link
Copy Markdown

@rryan one thing I only saw after my last comment: iconidentify/aurora-linux 11.32+ already has PCIe-C cold init for T600x and T602x, from Wesley Grimes's work (iconidentify#10):

  • 77f974b24080: on by default; pcie_apple.tunnel_kernel_init=0 turns it off.
  • c439df001663: "Don't restart a quiesced PCIe-C host on resume", the same idea as your resume guard.
  • 9e3a5bfd295e / ae0b0636bde0: the t600x DART tunables and a 32-bit DART window.
  • 4f48cb1d768c: the t602x DART tunables.

Ports whose DART has no tunables are refused, so the device-tree values matter. 11.34 adds an ASPM L1 replug fix for these tunnels on top (iconidentify#15).

On j314s, 11.34 does everything your commit plus my runtime DT did: boot with the LG attached, replug including with idle USB, link 0xa9000200. So I won't send a PR to your branch for the t600x part.

One correction to my comment above: 11.32's t600x DART values aren't your J416c ones. They're 0x20c 0xff000007 0xec000007, 0x220, 0x224, only three entries, with apple,dma-range = <0 0 1 0>, a 4 GiB window. Those work here too. What matters for the xHCI DMA is the 32-bit window, not my 2 GiB value.

@cb2206

cb2206 commented Oct 4, 2026

Copy link
Copy Markdown

For anyone with an LG UltraFine 5K: it now runs at full 5120x2880@60 on a 14" M1 Pro, with iconidentify#22 on top of the Thunderbolt display code here.

  • The gap: the LG takes two DP streams, one per tile, and DCP only joins them when DP IN 1 goes to DPTX port 1 of the pipeline that drives DP IN 0, not to a second pipeline.
  • The change: cpufreq: apple: J700 T8140 cpufreq and SMC CPU thermal policy (reworked; PMP-v2 parked) #22 does that, but only when DCP reports a tiled sink through SetTiledDisplayHints, so docks with two separate monitors are unaffected.
  • Testing: done on j314s only. The t602x paths are written to match, but an M2 Pro/Max report with the LG would be welcome.

cb2206 pushed a commit to cb2206/linux that referenced this pull request Oct 5, 2026
A dock unplug while receiver discovery is pending cancels the worker and
releases its tunnel reference, but omits the callback that owns a domain
reference. The M3 Type-C worker then waits indefinitely in apple_nhi_remove,
blocking subsequent events on that port. Observed on J516S with a TS3 Plus.

Backport the serialized cancellation and callback lifetime fix from Aurora
PR aurora-silicon#8. Drain DPRX before tb_stop releases router ports, complete cancellation
once, and release worker references under the domain lock. The M3 tree's
existing host notification API is retained via a small DPRX-only helper.
Carry both upstream KUnit cases for pending and running cancellation.

Source: iconidentify@477d56b
Source: iconidentify@d0bc506
Related: iconidentify/m3-roadmap#59
Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
cb2206 pushed a commit to cb2206/linux that referenced this pull request Oct 5, 2026
…-20260927

Integrate September 27 M3 GPU, display and device fixes
@RyanTheTide
RyanTheTide merged commit b37df4b into aurora-silicon:aurora-wip Oct 8, 2026
RyanTheTide added a commit that referenced this pull request Oct 8, 2026
Preserve the original review commits and retain subsequent published repairs where superseded.

Contributor credit; Signed-off-by outstanding:
- 99a5545: Chris Kearney <303316+iconidentify@users.noreply.github.com>
- b4e5e4c: Chris Kearney <303316+iconidentify@users.noreply.github.com>
- 508384b: Chris Kearney <303316+iconidentify@users.noreply.github.com>
- 46b9e80: Oliver Lukschander <oliver.lukschander@golf.at>
- b9a6a94: Chris Kearney <303316+iconidentify@users.noreply.github.com>
- 749ac04: Chris Kearney <303316+iconidentify@users.noreply.github.com>
- d04e476: Chris Kearney <303316+iconidentify@users.noreply.github.com>
- 67fdc27: Chris Kearney <303316+iconidentify@users.noreply.github.com>
- cf5f7d1: Chris Kearney <303316+iconidentify@users.noreply.github.com>
- 8f576eb: Oliver Lukschander <oliver.lukschander@golf.at>
- 69893ec: Oliver Lukschander <oliver.lukschander@golf.at>
- 85d6d84: Oliver Lukschander <oliver.lukschander@golf.at>
- 85fbf65: Oliver Lukschander <oliver.lukschander@golf.at>
- e557722: Oliver Lukschander <oliver.lukschander@golf.at>
- 0daeb8e: Oliver Lukschander <oliver.lukschander@golf.at>
- cc895f2: Oliver Lukschander <oliver.lukschander@golf.at>
- f8cec39: Oliver Lukschander <oliver.lukschander@golf.at>
- 81eee7b: Oliver Lukschander <oliver.lukschander@golf.at>
- 45c34d5: Oliver Lukschander <oliver.lukschander@golf.at>
- 6f0468f: Oliver Lukschander <oliver.lukschander@golf.at>
Ryan integrates these contributions under DCO (b); no author signature is forged.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
RyanTheTide added a commit that referenced this pull request Oct 8, 2026
[follow-up to #8] drm/apple: expose and enforce the routed Type-C CRTC

Original contributor commits are preserved. Conflict resolutions retain the audited shared driver APIs and their integrated review fixes.

Contributor credit; Signed-off-by outstanding:
- 99a5545: Chris Kearney <303316+iconidentify@users.noreply.github.com>
- b4e5e4c: Chris Kearney <303316+iconidentify@users.noreply.github.com>
- 508384b: Chris Kearney <303316+iconidentify@users.noreply.github.com>
- 46b9e80: Oliver Lukschander <oliver.lukschander@golf.at>
- b9a6a94: Chris Kearney <303316+iconidentify@users.noreply.github.com>
- 749ac04: Chris Kearney <303316+iconidentify@users.noreply.github.com>
- d04e476: Chris Kearney <303316+iconidentify@users.noreply.github.com>
- 67fdc27: Chris Kearney <303316+iconidentify@users.noreply.github.com>
- cf5f7d1: Chris Kearney <303316+iconidentify@users.noreply.github.com>
- 8f576eb: Oliver Lukschander <oliver.lukschander@golf.at>
- 69893ec: Oliver Lukschander <oliver.lukschander@golf.at>
- 85d6d84: Oliver Lukschander <oliver.lukschander@golf.at>
- 85fbf65: Oliver Lukschander <oliver.lukschander@golf.at>
- e557722: Oliver Lukschander <oliver.lukschander@golf.at>
- 0daeb8e: Oliver Lukschander <oliver.lukschander@golf.at>
- cc895f2: Oliver Lukschander <oliver.lukschander@golf.at>
- f8cec39: Oliver Lukschander <oliver.lukschander@golf.at>
- 81eee7b: Oliver Lukschander <oliver.lukschander@golf.at>
- 45c34d5: Oliver Lukschander <oliver.lukschander@golf.at>
- 6f0468f: Oliver Lukschander <oliver.lukschander@golf.at>
Ryan integrates these contributions under DCO (b); no author signature is forged.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants