From 8bdfa0899cb39cfd234b48d2b464c6e1f2c7360d Mon Sep 17 00:00:00 2001 From: Ryan Murray <81541646+RyanTheTide@users.noreply.github.com> Date: Tue, 27 Oct 2026 19:33:55 +1100 Subject: [PATCH 1/2] drm/apple: guard T6041 external DCPs and power direct DP routes Validate each external processor register map and require a boot-loader handoff marker before touching its hardware. For desktop 26.x Type-C routes, power the selected DP PHY for the route lifetime and leave idle pipelines unassigned. The existing generic Type-C fabric carries the Alt Mode and HPD notifications. Assisted-by: LLM Signed-off-by: Ryan Murray --- drivers/gpu/drm/apple/dcp-fabric.c | 30 ++++++++++++-- drivers/gpu/drm/apple/dcp-fabric.h | 2 + drivers/gpu/drm/apple/dcp.c | 64 +++++++++++++++++++++++++----- 3 files changed, 82 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp-fabric.c b/drivers/gpu/drm/apple/dcp-fabric.c index b5e2eafb8c090..b8d8213ae4bbb 100644 --- a/drivers/gpu/drm/apple/dcp-fabric.c +++ b/drivers/gpu/drm/apple/dcp-fabric.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -102,9 +103,9 @@ static bool dcp_hdmi_borrowed(void *ctx); bool dcp_is_typec_output(struct apple_dcp *dcp) { - /* Desktop 26.x keeps its own Type-C route, outside this fabric. */ + /* Desktop 26.x only has a Type-C output while a route owns a port. */ if (dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP) - return false; + return !!dcp->active_typec_route; return dcp->active_typec_route || dcp->fixed_connector_type == DRM_MODE_CONNECTOR_USB; } @@ -367,6 +368,21 @@ static int dcp_typec_route_activate(struct apple_dcp_typec_route *route, return ret; } + if (dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP && + xbar == route->xbar) { + ret = phy_init(route->phy); + if (!ret) { + ret = phy_power_on(route->phy); + if (ret) + phy_exit(route->phy); + } + if (ret) { + mux_control_deselect(route->xbar); + return ret; + } + route->desktop_phy_up = true; + } + dcp_modes_begin_attachment(dcp); apple_connector_edid_set_live(dcp->connector, false); dcp->phy = route->phy; @@ -484,6 +500,11 @@ static int dcp_typec_route_deactivate(struct apple_dcp_typec_route *route) } if (dcp_is_external_h17p(dcp)) dpavservep_detach(dcp); + if (route->desktop_phy_up) { + phy_power_off(route->phy); + phy_exit(route->phy); + route->desktop_phy_up = false; + } route->selected = false; ended_attachment = dcp_modes_end_typec(dcp, route); if (dcp->active_typec_route == route) @@ -545,6 +566,9 @@ static int dcp_typec_route_deactivate(struct apple_dcp_typec_route *route) dcp->phy = dcp->typec_routes[0].phy; dcp->dptx_phy = dcp->typec_routes[0].dptx_phy; } + } else if (dcp->fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP) { + dcp->phy = NULL; + dcp->connector_type = DRM_MODE_CONNECTOR_USB; } else { /* Keep DPTX endpoint discovery working before a cable is attached. */ dcp->phy = dcp->typec_routes[0].phy; @@ -3593,7 +3617,7 @@ static int dcp_register_typec_routes_present(void *data) if (ret) return ret; - if (!dcp->phy) + if (!dcp->phy && dcp->fw_compat != DCP_FIRMWARE_V_26_6_DESKTOP) dcp->phy = route->phy; dcp->nr_typec_routes++; } diff --git a/drivers/gpu/drm/apple/dcp-fabric.h b/drivers/gpu/drm/apple/dcp-fabric.h index 869a2c3716cfe..96b187f2ca7dd 100644 --- a/drivers/gpu/drm/apple/dcp-fabric.h +++ b/drivers/gpu/drm/apple/dcp-fabric.h @@ -39,6 +39,8 @@ struct apple_dcp_typec_route { u32 dptx_phy; u32 mux_index; bool selected; + /* Desktop T6041 powers a direct DP PHY while this route owns it. */ + bool desktop_phy_up; /* crossbar output actually selected: xbar (dpphy) or a Thunderbolt dpin */ unsigned int tunnel_dpin; u64 tunnel_generation; diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 628c1b7e05660..28627f44f51dd 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -247,15 +247,16 @@ static bool dcp_desktop_external(struct apple_dcp *dcp) } /* - * The register map of the T6041 internal DCP, from the macOS ADT capture - * (/arm-io/dcp reg[0], /arm-io/disp0 reg[0..3]). The firmware maps these - * windows by index, so the device tree must describe exactly these. + * The T6041 display processors map these windows by index. Check each + * processor against its own register map before touching the coprocessor. */ -static const struct { +struct t6041_dcp_reg { const char *name; u64 start; u64 size; -} t6041_dcp_regs[] = { +}; + +static const struct t6041_dcp_reg t6041_dcp_regs[] = { { "coproc", 0x402e00000ULL, 0x4000 }, { "disp-0", 0x400000000ULL, 0x4000000 }, { "disp-1", 0x402240000ULL, 0x10000 }, @@ -263,17 +264,58 @@ static const struct { { "disp-3", 0x402800000ULL, 0x800000 }, }; +static const struct t6041_dcp_reg t6041_dcpext0_regs[] = { + { "coproc", 0x3a2e00000ULL, 0x4000 }, + { "disp-0", 0x3a0000000ULL, 0x4000000 }, + { "disp-1", 0x3a2240000ULL, 0x10000 }, + { "disp-2", 0x3a23a8000ULL, 0x4000 }, + { "disp-3", 0x3a2800000ULL, 0x800000 }, +}; + +static const struct t6041_dcp_reg t6041_dcpext1_regs[] = { + { "coproc", 0x602e00000ULL, 0x4000 }, + { "disp-0", 0x600000000ULL, 0x4000000 }, + { "disp-1", 0x602240000ULL, 0x10000 }, + { "disp-2", 0x6023a8000ULL, 0x4000 }, + { "disp-3", 0x602800000ULL, 0x800000 }, +}; + /* Refuse to touch the coprocessor on any mismatch with the captured ADT. */ static int dcp_validate_t6041_dt(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct device_node *piodma __free(device_node) = - of_get_child_by_name(dev->of_node, "piodma"); + of_get_child_by_name(dev->of_node, "piodma"); + const struct t6041_dcp_reg *regs = t6041_dcp_regs; + struct resource *coproc; + u32 handoff; int i; - if (!of_device_is_compatible(dev->of_node, "apple,t6041-dcp")) + if (!of_device_is_compatible(dev->of_node, "apple,t6041-dcp") && + !of_device_is_compatible(dev->of_node, "apple,t6041-dcpext")) return 0; + coproc = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!coproc) + return -ENODEV; + if (of_device_is_compatible(dev->of_node, "apple,t6041-dcpext")) { + /* Only the loader may grant the external firmware/DART handoff. */ + if (of_property_read_u32(dev->of_node, "apple,t6041-ext-handoff", + &handoff) || handoff != 1) + return dev_err_probe(dev, -ENODEV, + "T6041 external DCP has no validated handoff\n"); + if (coproc->start == t6041_dcpext0_regs[0].start) + regs = t6041_dcpext0_regs; + else if (coproc->start == t6041_dcpext1_regs[0].start) + regs = t6041_dcpext1_regs; + else + return dev_err_probe(dev, -ENODEV, + "Unknown T6041 external DCP register map\n"); + } else if (coproc->start != t6041_dcp_regs[0].start) { + return dev_err_probe(dev, -ENODEV, + "Unknown T6041 internal DCP register map\n"); + } + if (pdev->num_resources != ARRAY_SIZE(t6041_dcp_regs)) return dev_err_probe(dev, -ENODEV, "T6041 DCP: %u register windows, the ADT has %zu\n", @@ -282,13 +324,13 @@ static int dcp_validate_t6041_dt(struct device *dev) for (i = 0; i < ARRAY_SIZE(t6041_dcp_regs); i++) { struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, i); - if (!r || r->start != t6041_dcp_regs[i].start || - resource_size(r) != t6041_dcp_regs[i].size || + if (!r || r->start != regs[i].start || + resource_size(r) != regs[i].size || of_property_match_string(dev->of_node, "reg-names", - t6041_dcp_regs[i].name) != i) + regs[i].name) != i) return dev_err_probe(dev, -ENODEV, "T6041 DCP: register window %d (%s) differs from the ADT\n", - i, t6041_dcp_regs[i].name); + i, regs[i].name); } if (!of_property_present(dev->of_node, "iommus") || From e7724b540a6b8f48d82f4043d76351271e4e28b1 Mon Sep 17 00:00:00 2001 From: Ryan Murray <81541646+RyanTheTide@users.noreply.github.com> Date: Tue, 27 Oct 2026 19:33:55 +1100 Subject: [PATCH 2/2] arm64: dts: apple: describe J616s Type-C DisplayPort routes Connect dcpext1 to the three USB-C DPPHY and display crossbar outputs using the ADT UFP mapping and the existing DCP Type-C route graph. Keep the connector links disabled until the external firmware and DART handoff is available; the fixed HDMI route on dcpext0 remains separate. Document the loader marker in the DCP binding and validate the Type-C route example. Assisted-by: LLM Signed-off-by: Ryan Murray --- .../display/apple,dcp-typec-routes.yaml | 9 ++- .../bindings/display/apple,dcp.yaml | 31 +++++++- arch/arm64/boot/dts/apple/t6040-j616s.dts | 78 +++++++++++++++++++ 3 files changed, 116 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/display/apple,dcp-typec-routes.yaml b/Documentation/devicetree/bindings/display/apple,dcp-typec-routes.yaml index 7b2e28ac3dca7..39552406be0c6 100644 --- a/Documentation/devicetree/bindings/display/apple,dcp-typec-routes.yaml +++ b/Documentation/devicetree/bindings/display/apple,dcp-typec-routes.yaml @@ -37,6 +37,7 @@ select: enum: - apple,dcp - apple,dcpext + - apple,t6041-dcpext typec-routes: true required: - compatible @@ -144,8 +145,14 @@ examples: - | #include - dcp { + dcp@10000000 { compatible = "apple,t6020-dcpext", "apple,dcpext"; + reg = <0x10000000 0x4000>, <0x11000000 0x4000>; + reg-names = "coproc", "disp-0"; + mboxes = <&mbox>; + mbox-names = "mbox"; + iommus = <&dart 0>; + apple,bw-scratch = <&pmgr 0 0 0>; phys = <&atcphy0 PHY_TYPE_DP>; phy-names = "typec0"; mux-controls = <&atcphy0_xbar 0>, <&atcphy0_xbar 1>, diff --git a/Documentation/devicetree/bindings/display/apple,dcp.yaml b/Documentation/devicetree/bindings/display/apple,dcp.yaml index ee14690fb6319..26034c11f6092 100644 --- a/Documentation/devicetree/bindings/display/apple,dcp.yaml +++ b/Documentation/devicetree/bindings/display/apple,dcp.yaml @@ -222,6 +222,14 @@ properties: $ref: /schemas/types.yaml#/definitions/uint32 description: Die containing the DisplayPort transmitter. + apple,t6041-ext-handoff: + $ref: /schemas/types.yaml#/definitions/uint32 + const: 1 + description: > + Set by the boot loader only after validating and reserving a T6041 + external DCP's firmware and display DART mappings. The driver refuses + the external processor without this handoff. + apple,notch-height: $ref: /schemas/types.yaml#/definitions/uint32 description: Height in pixels reserved for the display notch. @@ -362,7 +370,10 @@ allOf: properties: compatible: contains: - const: apple,t6030-dcpext + enum: + - apple,t6020-dcpext + - apple,t6030-dcpext + - apple,t6041-dcpext required: - compatible else: @@ -391,6 +402,24 @@ allOf: then: required: - apple,bw-scratch + - if: + properties: + compatible: + contains: + const: apple,t6041-dcpext + required: + - compatible + then: + if: + properties: + status: + const: disabled + required: + - status + else: + required: + - apple,t6041-ext-handoff + - memory-region - if: not: properties: diff --git a/arch/arm64/boot/dts/apple/t6040-j616s.dts b/arch/arm64/boot/dts/apple/t6040-j616s.dts index 75e67c49002a4..9630d5545a43f 100644 --- a/arch/arm64/boot/dts/apple/t6040-j616s.dts +++ b/arch/arm64/boot/dts/apple/t6040-j616s.dts @@ -25,6 +25,8 @@ bluetooth0 = "/pcie@1cb0000000/pci@0,0/bluetooth@0,1"; /* used by the loader to hand over the display */ dcp = &dcp; + dcpext0 = &dcpext0; + dcpext1 = &dcpext1; disp0 = &display; disp0-piodma = &disp0_piodma; /* the boot loader finds the GPU node through this alias */ @@ -4150,6 +4152,14 @@ remote-endpoint = <&atcphy0_ss>; }; }; + + port@3 { + reg = <3>; + status = "disabled"; + typec0_display: endpoint { + remote-endpoint = <&dcpext1_typec0>; + }; + }; }; }; }; @@ -4186,6 +4196,14 @@ remote-endpoint = <&atcphy1_ss>; }; }; + + port@3 { + reg = <3>; + status = "disabled"; + typec1_display: endpoint { + remote-endpoint = <&dcpext1_typec1>; + }; + }; }; }; }; @@ -4237,6 +4255,14 @@ remote-endpoint = <&atcphy2_ss>; }; }; + + port@3 { + reg = <3>; + status = "disabled"; + typec2_display: endpoint { + remote-endpoint = <&dcpext1_typec2>; + }; + }; }; }; }; @@ -4395,6 +4421,58 @@ }; }; +/* + * ADT routes dcpext1 (UFP endpoint 1) to the three Type-C DPPHY outputs. + * The graph remains disabled until the loader hands the external firmware and + * display DARTs over and the Type-C DP mode switches can be activated. + */ +&dcpext1 { + apple,connector-type = "USB-C"; + phys = <&atcphy0 PHY_TYPE_DP>, <&atcphy1 PHY_TYPE_DP>, + <&atcphy2 PHY_TYPE_DP>; + phy-names = "typec0", "typec1", "typec2"; + mux-controls = <&atcphy0_xbar 0>, <&atcphy1_xbar 0>, + <&atcphy2_xbar 0>; + mux-control-names = "typec0", "typec1", "typec2"; + apple,typec-mux-indices = <2 2 2>; + apple,typec-dptx-phys = <0 1 2>; + + typec-routes { + #address-cells = <1>; + #size-cells = <0>; + + route@0 { + reg = <0>; + mode-switch; + port { + dcpext1_typec0: endpoint { + remote-endpoint = <&typec0_display>; + }; + }; + }; + + route@1 { + reg = <1>; + mode-switch; + port { + dcpext1_typec1: endpoint { + remote-endpoint = <&typec1_display>; + }; + }; + }; + + route@2 { + reg = <2>; + mode-switch; + port { + dcpext1_typec2: endpoint { + remote-endpoint = <&typec2_display>; + }; + }; + }; + }; +}; + /* Additional Abbey PMICs on the two independent NUB SPMI buses. */ &nub_spmi1 { status = "okay";