Repository navigation
thunderbolt: apple: HBR3 for a lone external monitor on the M4 (T8132), HBR2 when a second one comes - #1008
Merged
RyanTheTide merged 2 commits intoNov 6, 2026
Conversation
TB_HOST_DP_HBR2_MAX caps every DP tunnel from the host router's DP IN adapters at HBR2, because both DP IN streams of the PHY must run at one link rate. A single display does not need that cap: alone on the host router a DP tunnel can run at HBR3 x4 (25920 Mb/s), which carries modes that HBR2 x4 (17280 Mb/s) cannot. Rename the flag to TB_HOST_DP_SHARED_RATE and decide the cap where DP IN and DP OUT adapters are paired, under the domain lock: - when one DP tunnel will be up on the host router, it may offer up to HBR3; when more will, each new one offers HBR2 at most; - when another DP tunnel is wanted (a second DP OUT adapter became available) while one is up above HBR2, that one is torn down and set up again between the same adapters at HBR2 first; the new one then takes its rate. Its display sees an unplug and a plug. If it cannot be set up again, pairing runs once more, and at the next event; - a tunnel alone that has to be reduced takes the combination with the most bandwidth (HBR2 x4 rather than HBR3 x2). Held at HBR2, or next to another tunnel, it keeps the rate and gives up lanes as before. A host DP tunnel is also counted at its cap at most in the bandwidth left for the others. Once its DPRX is done, the bandwidth it takes is worked out from what its DP IN adapter reports, which the cap does not bound: held at HBR2 between adapters that take HBR3, it could be counted at HBR3 x4, and a second display next to it would then get HBR2 x2 rather than x4. Nothing is set up again during a system sleep transition: from its PREPARE event (a PM notifier ahead of all others) until it is over, POST callbacks and the rollback of a refused PREPARE included (the gate is reopened by a work item that takes the system sleep lock after the POST event); a re-link admitted before holds the transition back until it is done. The re-link, and the pairing that needs it, then wait: a work item on the freezable workqueue hands them to the domain's ordered workqueue once the transition is over, behind any DPRX or teardown work queued before. The request also stays pending for the resume and thaw replays, and a failed runtime resume of the domain is retried a few times. The KUnit tests reach the connection manager's DP bandwidth accounting on a domain set up without hardware, reading adapter registers through a static stub on tb_cfg_read(); tb_cm_init() is split out of tb_probe() for that. The DP repair host test models the connection manager's private data: add the new work item there, and take the work initializers from tb_cm_init(). Signed-off-by: Erik Bourget <erik@ebourget.net>
Under TB_HOST_DP_SHARED_RATE two DP tunnels from the host router run at HBR2. When one of them goes because its display or its router was unplugged, the one left stays at HBR2 although it could now run at HBR3. Set it up again at up to HBR3, between the same adapters, once a debounce of 2.5 s is over, from the re-link work on the domain's ordered workqueue (so that DPRX or teardown work queued before it has released its paths): - only after a hot unplug of a DP OUT adapter or a router that took DP tunnels from the host router, and only for the one left alone below the rate both its adapters take. DPRX failures, failed activations and teardown for system sleep never lead to it; - once per unplug: the tunnel set up again is not marked; - dropped when another DP tunnel is up or can be paired by then, so an unplug and replug, or a display that drops and asserts HPD again while it retrains, causes no re-link. Until it is set up again the tunnel keeps its HBR2 cap and is counted at HBR2. When another one is wanted during the debounce, the tunnel is held at HBR2, its estimated bandwidth is worked out again and the debounce is stopped, all before the new one is given any bandwidth, so the new one gets HBR2 x4 next to it; - when a second DP tunnel takes the rate back down within a minute of such a re-link, the next debounce is doubled, up to 40 s; - not during a system sleep transition: tunnels that go then mark nothing, and a re-link that is due waits for the transition to end; a marked tunnel is looked at again at resume and runtime resume; - if the tunnel cannot be set up at the higher rate, it is set up at HBR2 again at once; if it is set up but does not come up (DPRX), it is set up at HBR2 again once its DPRX work is over, with its adapters kept for pairing and no new re-link up. If neither works, pairing runs again, at HBR2, and at the next event. The KUnit tests run the marking and the pairing on such a domain too. The DP repair host test models the connection manager: add the new fields there, with no tunnel re-linked at a higher rate. Signed-off-by: Erik Bourget <erik@ebourget.net>
This was referenced Nov 6, 2026
Contributor
|
Merge train (MTR, 06/11): merging. An independent adversarial review found it gated:
Two small changes reach every Thunderbolt host: a PM notifier (lock only, no state change) and a KUnit-only Gates: test merge 2d3579ef = integration/aurora 11eccbe + #1005 #994 #930 #658 #992 #1006 #1007 #1008 #531 (#992 has since landed on its own); base 11eccbe.
Hardware (7.3.0-aurora-mtr1106a, one-shot boots, fleet recipe):
|
This was referenced Nov 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follows #955. There, every DP tunnel from a T8132 host router was capped at HBR2 (
TB_HOST_DP_HBR2_MAX), because both DP IN streams of the ATC PHY must run at one link rate and two 4K60 streams fit at HBR2 x4. A single monitor doesn't need the cap: alone, a tunnel can run at HBR3 x4 (25920 Mb/s instead of 17280), which carries uncompressed modes HBR2 x4 can't, those between 17.28 and 25.92 Gb/s. (3840x2160 at 120 Hz with DSC fits HBR2 x4 and doesn't need it.) This PR (#955's listed follow-up) lets a lone tunnel use HBR3 and re-links when the number of tunnels changes.TB_HOST_DP_SHARED_RATE. The connection manager decides the cap where DP IN and DP OUT adapters are paired, under the domain lock, and gives it to the tunnel before activation:The PHY needs no change: one stream starting at HBR3 runs alone on DP IN 0 (PCLK1) and on DP IN 1 (PCLK1 prerequisite plus PCLK2), and the one-rate check still refuses HBR2 next to HBR3. Neither does the DRM side: the external outputs' modes are filtered by the trained link (HBR3 x4: 25.92 Gb/s at 8b/10b), and the link rate change and the route release each send a hotplug, so the modes are probed again after a re-link.
KUnit (thunderbolt suite):
tb_test_dp_shared_rate_single(HBR3 alone, HBR2 for two, reduction alone vs shared),_second(a second tunnel triggers the re-link down; lanes first with two; a tunnel counts at its cap),_relink_up(debounce, once per unplug, dropped when another comes, back-off),_recover(HBR2 recovery after a failed re-link up),_sleep(no re-link during a transition; the gate stays closed through a refused PREPARE's rollback and through a stale reopen) and_retry(bounded retry when the domain doesn't runtime resume). Four scenario tests follow the tunnels on a dock's link (34650 Mb/s for DP):_lone(one monitor at HBR3 x4),_two(a second one arrives: both HBR2 x4),_leave(the second leaves; held and counted at HBR2 through the debounce, then HBR3 x4) and_flap(quick back-and-forth: no re-link within the debounce, a new debounce at each unplug, the doubled debounce after a quick re-link down). Three tests run the connection manager itself on a domain with no hardware, the adapter registers read through a KUnit static stub ontb_cfg_read(), the DP IN adapter reporting HBR3 x4 once its DPRX is done:_counted(the bandwidth accounting,tb_consumed_dp_bandwidth(), counts a tunnel capped at HBR2 at HBR2 x4, from its caps and from its allocated bandwidth),_debounce(a tunnel marked bytb_dp_host_tunnel_gone(); a second display arrives and pairing,tb_dp_host_rate_limit(), drops the re-link up, holds the tunnel at HBR2 and stops the debounce timer, after which the new one gets HBR2 x4; in both modes) and_debounce_deferred(the same with pairing deferred for system sleep: the re-link up is dropped, the re-link work stays armed).tb_cm_init()is split out oftb_probe()so that the tests set the connection manager up as it is set up for a real domain.Testing:
KUnit,
tools/testing/kunit/kunit.py run --arch=arm64withCONFIG_USB4_KUNIT_TEST,CONFIG_USB4_APPLE_DPIN_KUNIT_TESTand the Apple ATC PHY and DCP fabric suites: 269 tests, 267 passed, 2 skipped (unrelated: one needsCONFIG_UCLAMP_TASK, one is covered elsewhere). The thunderbolt suite passes 69/69, including the 13tb_test_dp_shared_rate_*cases, with no warnings in the kernel log. The first commit on its own: 261 tests, 259 passed, the same 2 skipped.The tests fail when the fix is taken out, one part at a time:
tb_dp_host_rate_limit():_debounceand_debounce_deferred;tb_dp_consumed_bandwidth():_counted,_debounceand_debounce_deferred;_countedand_debounce.tools/asahi/tb-dp-repair-tests.py: 16 cases pass, on both commits.scripts/checkpatch.pl --strict: clean on both commits.arm64 build of the J713 config: no new warnings in
drivers/thunderbolt.J713 with a CalDigit TS5 dock and two 3840x2160 monitors:
Open: once, a monitor replugged during the debounce had every bandwidth request of its new tunnel refused ("not enough bandwidth available"), and came up at 1920x1080 at 60 Hz; 3840x2160 at 60 Hz could be set right after. Two more replugs during the debounce, one with each monitor and with debug logging, went as described above (the new tunnel offered 18400 Mb/s and given 17500), so the cause isn't known yet.
This was developed and tested by Claude (Anthropic), working with me on the J713.