From b1dcc099d97cedf827a61fa82a717f9fab12e2fd Mon Sep 17 00:00:00 2001 From: Jacob Crowther <3944151+jcrowthe@users.noreply.github.com> Date: Thu, 12 Nov 2026 23:26:53 -0700 Subject: [PATCH 1/4] arm64: dts: apple: t6022-j475d: enable DP alt mode on the rear USB-C ports On the Mac Studio (M2 Ultra) no display controller is wired to the four rear USB-C ports, so a USB-C DisplayPort display there never lights up. The dcpext1 routes in t602x-j474-j475.dtsi sit inside the APPLE_T602X_NON_ULTRA block together with the USB4/PCIe-C wiring, and t6022 does not define it. The die-0 half of the t6022 keeps the plain labels and the same rear-port wiring as the M2 Max Studio. macOS on a J475d drives a DP alt mode display on rear ports 1 and 2 from DCPEXT1 through the display crossbar (ADT display-crossbar0, "display-crossbar,t602x"), the same controller the non-Ultra boards use. Keep only the USB4 aliases, the PCIe-C tunnels and the non-Ultra display iommus under the guard. The dcpext1 display routes, dpaudio2, the ATC crossbars and the always-on ATC common power domains now apply to J475d too. t6022-jxxxd.dtsi points the display subsystem at the die-1 HDMI DART only, so add dispext1_dart to it on J475d for the framebuffers of the new pipe. J474s and J475c keep the same nodes and properties; only the position of the dcpext1 alias within /aliases changes. Signed-off-by: Jacob Crowther <3944151+jcrowthe@users.noreply.github.com> --- arch/arm64/boot/dts/apple/t6022-j475d.dts | 9 +++++++++ arch/arm64/boot/dts/apple/t602x-j474-j475.dtsi | 15 ++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/apple/t6022-j475d.dts b/arch/arm64/boot/dts/apple/t6022-j475d.dts index 6c09afcffc9a69..35f002d23d6e8d 100644 --- a/arch/arm64/boot/dts/apple/t6022-j475d.dts +++ b/arch/arm64/boot/dts/apple/t6022-j475d.dts @@ -45,6 +45,15 @@ power-domains = <&ps_dispext0_cpu0_die1>, <&ps_dptx_phy_ps_die1>; }; +/* + * HDMI scans out through dispext0 on die 1, and the rear USB-C ports through + * dcpext1/dispext1 on die 0 (see t602x-j474-j475.dtsi). + */ +&display { + iommus = <&dispext0_dart_die1 0 0x100 0x0 0x10 0x0>, + <&dispext1_dart 0 0x100 0x0 0x10 0x0>; +}; + &dcpext0_die1 { // J180 misses "function-dp2hdmi_pwr_en" dp2hdmi-pwren-gpios = <&smc_gpio 25 GPIO_ACTIVE_HIGH>; diff --git a/arch/arm64/boot/dts/apple/t602x-j474-j475.dtsi b/arch/arm64/boot/dts/apple/t602x-j474-j475.dtsi index 822760c80ae598..22827a0ffab38f 100644 --- a/arch/arm64/boot/dts/apple/t602x-j474-j475.dtsi +++ b/arch/arm64/boot/dts/apple/t602x-j474-j475.dtsi @@ -24,7 +24,6 @@ #ifdef APPLE_T602X_NON_ULTRA / { aliases { - dcpext1 = &dcpext1; /* Same Thunderbolt aliases as the 14/16-inch boards. This mini * brings up a fourth controller. */ @@ -67,6 +66,19 @@ iommus = <&dispext0_dart 0 0x100 0x0 0x10 0x0>, <&dispext1_dart 0 0x100 0x0 0x10 0x0>; }; +#endif + +/* + * dcpext1 on die 0 drives the rear USB-C ports through the display crossbar + * on all of these boards, M2 Ultra included: macOS routes a DP alt mode + * display on the J475d's rear ports to DCPEXT1 as well. Only the USB4 and + * PCIe-C wiring below stays non-Ultra. + */ +/ { + aliases { + dcpext1 = &dcpext1; + }; +}; &dispext1_dart { status = "okay"; @@ -229,6 +241,7 @@ apple,always-on; }; +#ifdef APPLE_T602X_NON_ULTRA &usb4_0_acio { apple,pcie-tunnel = <&usb4_0_pcie_tunnel>; }; From 5831614759c15d5908b93335edfe744e63f05c75 Mon Sep 17 00:00:00 2001 From: Jacob Crowther <3944151+jcrowthe@users.noreply.github.com> Date: Fri, 13 Nov 2026 00:06:57 -0700 Subject: [PATCH 2/4] thunderbolt: apple: qualify the M2 Ultra (t6022) for DP tunnels The M2 Ultra is two t6021 dies. Its die-0 ACIO, ATC and display crossbar are the ones the M2 Pro/Max laptops use, and the DCPs are the same apple,t6020-dcpext. With t6022 missing from apple_dpin_qualified_soc, a Thunderbolt display on a J475d enumerates, but the DP IN adapter never asks for a pipeline and the tunnel is torn down after the DPRX timeout: thunderbolt-apple-nhi f01f00000.nhi: 0:5 <-> 1:13 (DP): not active, tearing down Qualify t6022 with the same M2 (pre/post) flow. That flow still requires dual-stream connector wiring, so nothing changes until a board's DT describes it. Tested on a Mac Studio (M2 Ultra, J475d) with an LG 40WP95C on rear ports 1 and 2: 5120x2160@72, hotplug, and DPMS off/on. Signed-off-by: Jacob Crowther <3944151+jcrowthe@users.noreply.github.com> --- drivers/thunderbolt/apple-dpin-test.c | 3 ++- drivers/thunderbolt/apple.c | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/thunderbolt/apple-dpin-test.c b/drivers/thunderbolt/apple-dpin-test.c index c22da168bd8bc9..2417768ebd0ea9 100644 --- a/drivers/thunderbolt/apple-dpin-test.c +++ b/drivers/thunderbolt/apple-dpin-test.c @@ -355,7 +355,8 @@ static void apple_dpin_qualification_profiles(struct kunit *test) { "apple,t6021", &apple_dpin_m2, true, &apple_dpin_m2 }, { "apple,t6020", &apple_dpin_m2, false, &apple_dpin_disabled }, { "apple,t6021", &apple_dpin_m2, false, &apple_dpin_disabled }, - { "apple,t6022", &apple_dpin_m2, true, &apple_dpin_disabled }, + { "apple,t6022", &apple_dpin_m2, true, &apple_dpin_m2 }, + { "apple,t6022", &apple_dpin_m2, false, &apple_dpin_disabled }, { "apple,t6030", &apple_dpin_m3, true, &apple_dpin_m3 }, { "apple,t6031", &apple_dpin_m3, true, &apple_dpin_disabled }, { "apple,unknown", &apple_dpin_m1, true, &apple_dpin_disabled }, diff --git a/drivers/thunderbolt/apple.c b/drivers/thunderbolt/apple.c index c6625c5cbb5d3f..8e7df5aa30f874 100644 --- a/drivers/thunderbolt/apple.c +++ b/drivers/thunderbolt/apple.c @@ -375,6 +375,8 @@ static const struct of_device_id apple_dpin_qualified_soc[] = { { .compatible = "apple,t6001" }, { .compatible = "apple,t6020" }, { .compatible = "apple,t6021" }, + /* The M2 Ultra's die-0 ACIO, ATC and display crossbar are t6021's. */ + { .compatible = "apple,t6022" }, { .compatible = "apple,t6030" }, {}, }; From 5cb2ccf77011076388cbf5feaf0a75b1cdec5ad6 Mon Sep 17 00:00:00 2001 From: Jacob Crowther <3944151+jcrowthe@users.noreply.github.com> Date: Fri, 13 Nov 2026 00:06:57 -0700 Subject: [PATCH 3/4] phy: apple: atc: qualify the M2 Ultra and the fourth die-0 ATC for DP tunnels Qualify t6022 for the T602x tunnel clock sequence, as for the Thunderbolt side, and accept the ATC3 core at 0x1303000000. The 14/16-inch boards only have ATC0-2, so ATC3 was never listed, but the Mac mini and Mac Studio use it for their fourth rear port. The wiring gate still applies. Tested on a Mac Studio (M2 Ultra, J475d): a DP tunnel on rear port 1 (phy@1303000000) runs 5120x2160@72. Signed-off-by: Jacob Crowther <3944151+jcrowthe@users.noreply.github.com> --- drivers/phy/apple/atc-tunnel-test.c | 4 ++-- drivers/phy/apple/atc-tunnel.h | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/phy/apple/atc-tunnel-test.c b/drivers/phy/apple/atc-tunnel-test.c index 15f10557073328..4e1a33474ec5f3 100644 --- a/drivers/phy/apple/atc-tunnel-test.c +++ b/drivers/phy/apple/atc-tunnel-test.c @@ -12,7 +12,7 @@ static void atc_tunnel_soc_qualification(struct kunit *test) } cases[] = { { "apple,t6020", true }, { "apple,t6021", true }, - { "apple,t6022", false }, + { "apple,t6022", true }, { "apple,t8103", false }, { "apple,t6000", false }, { "apple,t6001", false }, @@ -47,7 +47,7 @@ static void atc_tunnel_core_qualification(struct kunit *test) { "apple,t6020-atcphy", 0xb03000000ULL, 0x7048, true }, { "apple,t6020-atcphy", 0xf03000000ULL, 0x7048, true }, { "apple,t6020-atcphy", 0x703000000ULL, 0x7047, false }, - { "apple,t6020-atcphy", 0x1303000000ULL, 0x4c000, false }, + { "apple,t6020-atcphy", 0x1303000000ULL, 0x4c000, true }, { "apple,t6020-atcphy", 0x2703000000ULL, 0x4c000, false }, { "apple,t8103-atcphy", 0x703000000ULL, 0x4c000, false }, }; diff --git a/drivers/phy/apple/atc-tunnel.h b/drivers/phy/apple/atc-tunnel.h index e06d10b4a3e3d4..2bc4dae3425a85 100644 --- a/drivers/phy/apple/atc-tunnel.h +++ b/drivers/phy/apple/atc-tunnel.h @@ -9,13 +9,15 @@ static inline bool apple_atc_t602x_qualified(const struct device_node *root) { return of_device_is_compatible(root, "apple,t6020") || - of_device_is_compatible(root, "apple,t6021"); + of_device_is_compatible(root, "apple,t6021") || + of_device_is_compatible(root, "apple,t6022"); } static inline bool apple_atc_t602x_core_valid(const struct device_node *np, u64 base, u64 size) { return of_device_is_compatible(np, "apple,t6020-atcphy") && - (base == 0x703000000ULL || base == 0xb03000000ULL || base == 0xf03000000ULL) && + (base == 0x703000000ULL || base == 0xb03000000ULL || base == 0xf03000000ULL || + base == 0x1303000000ULL) && size >= 0x7048; } From 5cb9b740287ac212c21b637629221ee739b049d7 Mon Sep 17 00:00:00 2001 From: Jacob Crowther <3944151+jcrowthe@users.noreply.github.com> Date: Fri, 13 Nov 2026 00:06:57 -0700 Subject: [PATCH 4/4] arm64: dts: apple: t6022-j475d: wire Thunderbolt DP tunnels on the rear ports On M2, Thunderbolt DP tunnels need dual-stream connector wiring: two DCPs per Type-C connector, each with typecN-dpin0/1 controls on cells 1 and 2 of the port's crossbar, as on the 14/16-inch boards. The J475d's HDMI runs from die 1, so die-0 dcpext0 is free. Enable it as a USB-C-only pipeline for the rear ports (crossbar state 0, dcpext1 state 2), give both die-0 DCPs the DPIN controls, add the second endpoint to each rear connector, and add dispext0_dart to the display iommus. m1n1 fills apple,firmware-compat only through the ADT-indexed dcpextN aliases. Without "dcpext0" here, probe fails with "Could not read 'apple,firmware-compat': -22" and the whole apple-drm aggregate stays down, HDMI included. Tested on a Mac Studio (M2 Ultra, J475d), together with the two previous patches: HDMI (dcpext4), a DP alt mode display on rear port 4 (dcpext0) and a Thunderbolt display on rear port 2 (dcpext1, dpin0) all run at once. Signed-off-by: Jacob Crowther <3944151+jcrowthe@users.noreply.github.com> --- arch/arm64/boot/dts/apple/t6022-j475d.dts | 202 ++++++++++++++++++++++ 1 file changed, 202 insertions(+) diff --git a/arch/arm64/boot/dts/apple/t6022-j475d.dts b/arch/arm64/boot/dts/apple/t6022-j475d.dts index 35f002d23d6e8d..724b5dbac699a2 100644 --- a/arch/arm64/boot/dts/apple/t6022-j475d.dts +++ b/arch/arm64/boot/dts/apple/t6022-j475d.dts @@ -51,9 +51,211 @@ */ &display { iommus = <&dispext0_dart_die1 0 0x100 0x0 0x10 0x0>, + <&dispext0_dart 0 0x100 0x0 0x10 0x0>, <&dispext1_dart 0 0x100 0x0 0x10 0x0>; }; +/* + * Thunderbolt DP tunnels on M2 need two pipelines per port with named DPIN + * crossbar controls, as on the 14/16-inch boards. HDMI uses die 1 here, so + * die-0 dcpext0 is free to be the second rear-port pipeline (crossbar state + * 0, dcpext1 state 2). + */ +/ { + aliases { + /* m1n1 fills apple,firmware-compat through the ADT-indexed alias */ + dcpext0 = &dcpext0; + }; +}; + +&dispext0_dart { + status = "okay"; +}; + +&dcpext0_dart { + status = "okay"; +}; + +&dcpext0_mbox { + status = "okay"; +}; + +&dpaudio1 { + status = "okay"; +}; + +&dcpext0 { + status = "okay"; + apple,connector-type = "USB-C"; + phys = <&atcphy0 PHY_TYPE_DP>, <&atcphy1 PHY_TYPE_DP>, + <&atcphy2 PHY_TYPE_DP>, <&atcphy3 PHY_TYPE_DP>; + phy-names = "typec0", "typec1", "typec2", "typec3"; + mux-controls = <&atcphy0_xbar 0>, + <&atcphy1_xbar 0>, + <&atcphy2_xbar 0>, + <&atcphy3_xbar 0>, + <&atcphy0_xbar 1>, + <&atcphy1_xbar 1>, + <&atcphy2_xbar 1>, + <&atcphy3_xbar 1>, + <&atcphy0_xbar 2>, + <&atcphy1_xbar 2>, + <&atcphy2_xbar 2>, + <&atcphy3_xbar 2>; + mux-control-names = "typec0", "typec1", "typec2", "typec3", + "typec0-dpin0", "typec1-dpin0", "typec2-dpin0", "typec3-dpin0", + "typec0-dpin1", "typec1-dpin1", "typec2-dpin1", "typec3-dpin1"; + apple,typec-mux-indices = <0 0 0 0>; + apple,typec-dptx-phys = <0 1 2 3>; + + typec-routes { + #address-cells = <1>; + #size-cells = <0>; + + route@0 { + reg = <0>; + mode-switch; + + port { + dcpext0_typec0: endpoint { + remote-endpoint = <&typec0_dcpext0>; + }; + }; + }; + + route@1 { + reg = <1>; + mode-switch; + + port { + dcpext0_typec1: endpoint { + remote-endpoint = <&typec1_dcpext0>; + }; + }; + }; + + route@2 { + reg = <2>; + mode-switch; + + port { + dcpext0_typec2: endpoint { + remote-endpoint = <&typec2_dcpext0>; + }; + }; + }; + + route@3 { + reg = <3>; + mode-switch; + + port { + dcpext0_typec3: endpoint { + remote-endpoint = <&typec3_dcpext0>; + }; + }; + }; + }; +}; + +&dcpext1 { + mux-controls = <&atcphy0_xbar 0>, + <&atcphy1_xbar 0>, + <&atcphy2_xbar 0>, + <&atcphy3_xbar 0>, + <&atcphy0_xbar 1>, + <&atcphy1_xbar 1>, + <&atcphy2_xbar 1>, + <&atcphy3_xbar 1>, + <&atcphy0_xbar 2>, + <&atcphy1_xbar 2>, + <&atcphy2_xbar 2>, + <&atcphy3_xbar 2>; + mux-control-names = "typec0", "typec1", "typec2", "typec3", + "typec0-dpin0", "typec1-dpin0", "typec2-dpin0", "typec3-dpin0", + "typec0-dpin1", "typec1-dpin1", "typec2-dpin1", "typec3-dpin1"; +}; + +&typec0 { + ports { + port@3 { + #address-cells = <1>; + #size-cells = <0>; + /delete-node/ endpoint; + + typec0_dcp: endpoint@0 { + reg = <0>; + remote-endpoint = <&dcpext1_typec0>; + }; + + typec0_dcpext0: endpoint@1 { + reg = <1>; + remote-endpoint = <&dcpext0_typec0>; + }; + }; + }; +}; + +&typec1 { + ports { + port@3 { + #address-cells = <1>; + #size-cells = <0>; + /delete-node/ endpoint; + + typec1_dcp: endpoint@0 { + reg = <0>; + remote-endpoint = <&dcpext1_typec1>; + }; + + typec1_dcpext0: endpoint@1 { + reg = <1>; + remote-endpoint = <&dcpext0_typec1>; + }; + }; + }; +}; + +&typec2 { + ports { + port@3 { + #address-cells = <1>; + #size-cells = <0>; + /delete-node/ endpoint; + + typec2_dcp: endpoint@0 { + reg = <0>; + remote-endpoint = <&dcpext1_typec2>; + }; + + typec2_dcpext0: endpoint@1 { + reg = <1>; + remote-endpoint = <&dcpext0_typec2>; + }; + }; + }; +}; + +&typec3 { + ports { + port@3 { + #address-cells = <1>; + #size-cells = <0>; + /delete-node/ endpoint; + + typec3_dcp: endpoint@0 { + reg = <0>; + remote-endpoint = <&dcpext1_typec3>; + }; + + typec3_dcpext0: endpoint@1 { + reg = <1>; + remote-endpoint = <&dcpext0_typec3>; + }; + }; + }; +}; + &dcpext0_die1 { // J180 misses "function-dp2hdmi_pwr_en" dp2hdmi-pwren-gpios = <&smc_gpio 25 GPIO_ACTIVE_HIGH>;