diff --git a/arch/arm64/boot/dts/apple/t6034-dcpext.dtsi b/arch/arm64/boot/dts/apple/t6034-dcpext.dtsi new file mode 100644 index 00000000000000..dec5e088fa9939 --- /dev/null +++ b/arch/arm64/boot/dts/apple/t6034-dcpext.dtsi @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: GPL-2.0+ OR MIT +/* + * T6034 external display processor dcpext0. + * + * The J516M ADT places dcpext0 exactly 0x8000_0000 below the panel's DCP + * block: coprocessor 0x3_06c0_0000, mailbox 0x3_06c0_8000, firmware DART + * 0x3_0530_c000 and display DART 0x3_0530_4000 (both on interrupt 1047), + * display pipe 0x3_0400_0000. Its preloaded 14.7 firmware is the panel's + * image (F493F03E-...), and its nub has the T6030 dcpext segment layout, so + * it runs on the T6030 dcpext handoff protocol: compatible falls back to + * apple,t6030-dcpext. + * + * Power: as for the panel, dispext0_fe is the child of dispext0_cpu on + * T6031/T6034, so the processor holds the front end (and with it all three + * states); its DART and mailbox hold dispext0_cpu, which is what m1n1's + * display DART handoff and the display gate check. + * + * Every node stays disabled. m1n1 describes the firmware memory and the + * display DART only with chosen asahi,t6034-dcpext=1 / asahi,t6034-dispext=1, + * and the display gate enables the nodes only when those markers are there. + */ + +/ { + aliases { + dcpext0 = &dcpext; + }; +}; + +&soc { + dcpext_dart: iommu@30530c000 { + compatible = "apple,t6031-dart", "apple,t8110-dart"; + reg = <0x3 0x0530c000 0x0 0x4000>; + interrupt-parent = <&aic>; + interrupts = ; + #iommu-cells = <1>; + apple,dma-range = <0x100 0x0 0x10 0x0>; + power-domains = <&ps_dispext0_cpu>; + status = "disabled"; + }; + + dcpext_mbox: mailbox@306c08000 { + compatible = "apple,t6031-asc-mailbox", "apple,asc-mailbox-v4"; + reg = <0x3 0x06c08000 0x0 0x4000>; + interrupt-parent = <&aic>; + /* ADT order 1069 1068 1071 1070, as the panel's 1030 1029 1032 1031 */ + interrupts = , + , + , + ; + interrupt-names = "send-empty", "send-not-empty", + "recv-empty", "recv-not-empty"; + #mbox-cells = <0>; + power-domains = <&ps_dispext0_cpu>; + resets = <&ps_dispext0_cpu>; + status = "disabled"; + }; + + /* ADT atcN-dpxbar (compatible atc-dpxbar,t602x), one per Type-C port. */ + atcphy0_xbar: mux@70304c000 { + compatible = "apple,t6031-display-crossbar", + "apple,t6020-display-crossbar"; + reg = <0x7 0x0304c000 0x0 0x4000>; + #mux-control-cells = <1>; + power-domains = <&ps_atc0_usb>; + }; + + atcphy1_xbar: mux@b0304c000 { + compatible = "apple,t6031-display-crossbar", + "apple,t6020-display-crossbar"; + reg = <0xb 0x0304c000 0x0 0x4000>; + #mux-control-cells = <1>; + power-domains = <&ps_atc1_usb>; + }; + + atcphy2_xbar: mux@f0304c000 { + compatible = "apple,t6031-display-crossbar", + "apple,t6020-display-crossbar"; + reg = <0xf 0x0304c000 0x0 0x4000>; + #mux-control-cells = <1>; + power-domains = <&ps_atc2_usb>; + }; + + dcpext: dcp@306c00000 { + compatible = "apple,t6034-dcpext", "apple,t6030-dcpext", + "apple,dcpext"; + reg = <0x3 0x06c00000 0x0 0x4000>; + reg-names = "coproc"; + mboxes = <&dcpext_mbox>; + mbox-names = "mbox"; + iommus = <&dcpext_dart 5>; + power-domains = <&ps_dispext0_fe>; + resets = <&ps_dispext0_cpu>; + /* + * The gate sets this too; naming it here gives the entry the + * phandle the gate requires (dtc emits one only if referenced). + */ + apple,pmp-report = <&pmp_report_dispext0>; + 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>, + <&atcphy0_xbar 1>, <&atcphy0_xbar 2>, + <&atcphy1_xbar 1>, <&atcphy1_xbar 2>, + <&atcphy2_xbar 1>, <&atcphy2_xbar 2>; + mux-control-names = "typec0", "typec1", "typec2", + "typec0-dpin0", "typec0-dpin1", + "typec1-dpin0", "typec1-dpin1", + "typec2-dpin0", "typec2-dpin1"; + apple,connector-type = "USB-C"; + /* display-crossbar0 upstream port: dispext0 */ + apple,dcp-index = <1>; + apple,typec-dptx-phys = <0 1 2>; + apple,typec-mux-indices = <0 0 0>; + #address-cells = <2>; + #size-cells = <2>; + ranges; + status = "disabled"; + + /* Enabled together with the processor by the display gate. */ + typec-routes { + #address-cells = <1>; + #size-cells = <0>; + + route@0 { + reg = <0>; + mode-switch; + status = "disabled"; + + port { + dcpext_typec0: endpoint { + remote-endpoint = <&typec0_dcpext>; + }; + }; + }; + + route@1 { + reg = <1>; + mode-switch; + status = "disabled"; + + port { + dcpext_typec1: endpoint { + remote-endpoint = <&typec1_dcpext>; + }; + }; + }; + + route@2 { + reg = <2>; + mode-switch; + status = "disabled"; + + port { + dcpext_typec2: endpoint { + remote-endpoint = <&typec2_dcpext>; + }; + }; + }; + }; + }; +}; + +/* The driver waits for the PMP to acknowledge this request before RUN. */ +&pmp_report_dispext0 { + apple,always-on; +}; + +&typec0 { + ports { + port@3 { + reg = <3>; + + typec0_dcpext: endpoint { + remote-endpoint = <&dcpext_typec0>; + }; + }; + }; +}; + +&typec1 { + ports { + port@3 { + reg = <3>; + + typec1_dcpext: endpoint { + remote-endpoint = <&dcpext_typec1>; + }; + }; + }; +}; + +&typec2 { + ports { + port@3 { + reg = <3>; + + typec2_dcpext: endpoint { + remote-endpoint = <&dcpext_typec2>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/apple/t6034-j516m.dts b/arch/arm64/boot/dts/apple/t6034-j516m.dts index 22ad5251eaecef..355a6ae7192432 100644 --- a/arch/arm64/boot/dts/apple/t6034-j516m.dts +++ b/arch/arm64/boot/dts/apple/t6034-j516m.dts @@ -10,7 +10,9 @@ /dts-v1/; #include "t6034.dtsi" +#include "t6034-dcp.dtsi" #include "t603x-j514-j516.dtsi" +#include "t6034-dcpext.dtsi" / { compatible = "apple,j516m", "apple,t6034", "apple,arm-platform"; @@ -29,6 +31,17 @@ brcm,board-type = "apple,jura"; }; +/* J516 panel dimensions, as on the J516C (same 16-inch chassis and panel). */ +&dcp { + panel { + compatible = "apple,panel-j516", "apple,panel-mini-led", "apple,panel"; + width-mm = <346>; + height-mm = <223>; + adj-height-mm = <216>; + apple,max-brightness = <500>; + }; +}; + &aop_audio { apple,chassis-name = "J516"; apple,machine-kind = "MacBook Pro"; diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index b6a5be040ef9c8..9c9740a53a5048 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -139,7 +139,8 @@ EXPORT_SYMBOL_GPL(apple_drm_t6031_link_device); int apple_drm_t6034_link_device(struct device *consumer) { - if (!of_machine_is_compatible("apple,j514m")) + if (!of_machine_is_compatible("apple,j514m") && + !of_machine_is_compatible("apple,j516m")) return -ENODEV; return apple_g15_link_device(consumer, "apple,t6034", "apple,agx-t6034", &apple_t6034_display); diff --git a/drivers/gpu/drm/apple/dcp-fabric.c b/drivers/gpu/drm/apple/dcp-fabric.c index 29321f8032a490..1963cbb0c9ccd9 100644 --- a/drivers/gpu/drm/apple/dcp-fabric.c +++ b/drivers/gpu/drm/apple/dcp-fabric.c @@ -211,7 +211,7 @@ bool dcp_typec_dual_stream(void) bool dcp_typec_ordered_dual_stream(void) { return dcp_fabric_ordered_dual_stream(dcp_typec_dual_stream(), - of_machine_is_compatible("apple,t6030")); + apple_dp_tunnel_m3_soc()); } /* @@ -257,7 +257,7 @@ static int dcp_route_clock_rate(struct apple_dcp_typec_route *route, { int ret; - if (of_machine_is_compatible("apple,t6030")) { + if (apple_dp_tunnel_m3_soc()) { typeof(apple_atc_dp_tunnel_rate_source) *fn; if (dcp_fabric_t6030_pclk(route->mux_index, dpin) < 0) @@ -902,7 +902,7 @@ static void dcp_fabric_snapshot_port(struct apple_dcp_typec_port *port, route->core.tunnel = route->tunnel; route->core.dpin = route->tunnel_dpin; route->core.tunnel_clock_blocked = dcp_fabric_tunnel_clock_blocked( - of_machine_is_compatible("apple,t6030"), route->mux_index); + apple_dp_tunnel_m3_soc(), route->mux_index); *tail = &route->core; tail = &route->core.next; } @@ -1763,7 +1763,7 @@ int dcp_direct_crossbar_link(struct apple_dcp *dcp, bool up) struct mux_control *xbar = NULL; int ret; - if (!of_machine_is_compatible("apple,t6030")) + if (!apple_dp_tunnel_m3_soc()) return 0; guard(mutex)(&dcp->tb_lock); route = dcp->active_typec_route; @@ -1805,7 +1805,7 @@ int dcp_tunnel_set_rate(struct apple_dcp *dcp, struct phy *phy, u32 unit, return -ENODEV; if (!dcp_fabric_callback_valid(route->tunnel_generation, dcp->tb_generation)) return -ESTALE; - if (of_machine_is_compatible("apple,t6030") && + if (apple_dp_tunnel_m3_soc() && (unit || dcp->dptx_dfp_port != 1 + route->tunnel_dpin || apple_dp_tunnel_t6030_pclk(route->mux_index, route->tunnel_dpin) < 0)) @@ -1903,7 +1903,7 @@ static struct mux_control * dcp_typec_tunnel_ctl(struct apple_dcp_typec_route *route, unsigned int dpin) { if (dpin > 1 || - (dcp_fabric_tunnel_clock_blocked(of_machine_is_compatible("apple,t6030"), + (dcp_fabric_tunnel_clock_blocked(apple_dp_tunnel_m3_soc(), route->mux_index) & BIT(dpin))) return NULL; if (route->dpin[dpin]) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 8cb040d16d552c..6cfe67892cdc9f 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -1382,6 +1382,66 @@ static int dcp_external_start_locked(struct apple_dcp *dcp) * that DART, and stream 0 among the display subsystem's iommus, so that * framebuffers are mapped for it as for the panel. */ +/* Display subsystems an external processor on the T6030 handoff can join. */ +static const struct of_device_id dcp_ext_display_ids[] = { + { .compatible = "apple,t6030-display-subsystem" }, + /* T6034: dcpext0 on the T6030 handoff protocol. */ + { .compatible = "apple,t6034-display-subsystem" }, + {}, +}; + +/* + * The external processor images the 14.7 external path speaks to: the + * T6030 image, and on T6034 the SoC's own image, which + * the panel's DCP already runs natively on the same 14.7 layouts. + */ +bool dcp_ext_firmware_admitted(const struct device_node *np) +{ + const char *uuid; + + if (of_property_read_string(np, "apple,firmware-uuid", &uuid)) + return false; + if (!strcmp(uuid, "DDF38191-93B3-324A-BC8F-643006F5AC82")) + return true; + return of_machine_is_compatible("apple,t6034") && + !strcmp(uuid, "F493F03E-CC40-3D89-9A08-5F259BA2B246"); +} + +static bool dcp_ext_domain_is(struct device_node *ps, const char *part) +{ + const char *label; + size_t len, plen = strlen(part); + + if (!ps || of_property_read_string(ps, "label", &label) || + !strstarts(label, "dispext")) + return false; + len = strlen(label); + return len > plen && !strcmp(label + len - plen, part); +} + +/* + * The CPU power domain of an external processor's pipe, dispext_cpu, + * with a reference. T6030 names it first in the processor's power-domains; + * on T6031/T6034 the processor holds dispext_fe, the + * child of dispext_cpu there, as disp_fe is of disp_cpu. + */ +struct device_node *dcp_ext_cpu_domain(const struct device_node *np) +{ + struct device_node *ps, *parent; + + ps = of_parse_phandle(np, "power-domains", 0); + if (dcp_ext_domain_is(ps, "_fe")) { + parent = of_parse_phandle(ps, "power-domains", 0); + of_node_put(ps); + ps = parent; + } + if (!dcp_ext_domain_is(ps, "_cpu")) { + of_node_put(ps); + return NULL; + } + return ps; +} + bool dcp_t6030_ext_native(const struct device_node *np) { struct of_phandle_args dart, args; @@ -1403,7 +1463,7 @@ bool dcp_t6030_ext_native(const struct device_node *np) of_node_put(piodma); if (dart.args_count != 1 || dart.args[0] != 4 || !of_device_is_available(dart.np)) goto out; - display = of_find_compatible_node(NULL, NULL, "apple,t6030-display-subsystem"); + display = of_find_matching_node(NULL, dcp_ext_display_ids); n = display && of_device_is_available(display) ? of_count_phandle_with_args(display, "iommus", "#iommu-cells") : 0; for (i = 0; i < n && !native; i++) { diff --git a/drivers/gpu/drm/apple/dcp.h b/drivers/gpu/drm/apple/dcp.h index baff04ee201e72..eb07e7911b823d 100644 --- a/drivers/gpu/drm/apple/dcp.h +++ b/drivers/gpu/drm/apple/dcp.h @@ -97,6 +97,8 @@ void dcp_send_message(struct apple_dcp *dcp, u8 endpoint, u64 message); int dcp_dptx_connect_oob(struct platform_device *pdev, u32 port); bool dcp_t6030_ext_native(const struct device_node *np); +bool dcp_ext_firmware_admitted(const struct device_node *np); +struct device_node *dcp_ext_cpu_domain(const struct device_node *np); void dcp_external_ready(struct apple_dcp *dcp); void dcp_external_retry(struct apple_dcp *dcp, const char *why, int error, unsigned int base_ms); diff --git a/drivers/gpu/drm/apple/dcpext_scanout.c b/drivers/gpu/drm/apple/dcpext_scanout.c index f3266931bb456d..85cbff316ab841 100644 --- a/drivers/gpu/drm/apple/dcpext_scanout.c +++ b/drivers/gpu/drm/apple/dcpext_scanout.c @@ -516,7 +516,7 @@ static int scanout_recheck_dart(struct device_node *dart, struct device_node *dc return -EINVAL; /* The display DART of this processor's own pipe shares its CPU domain. */ dart_pd = of_parse_phandle(dart, "power-domains", 0); - dcp_pd = of_parse_phandle(dcp, "power-domains", 0); + dcp_pd = dcp_ext_cpu_domain(dcp); of_node_put(dart_pd); of_node_put(dcp_pd); if (!dart_pd || dart_pd != dcp_pd) @@ -760,14 +760,11 @@ int dcpext_scanout_register(struct apple_dcp *dcp) { struct device_node *node; struct dcpext_scanout *scanout; - const char *uuid; int ret; if (!dcp->external || dcp->fw_compat != DCP_FIRMWARE_V_14_7) return -ENODEV; - if (dcpext_pageflip && - (of_property_read_string(dcp->dev->of_node, "apple,firmware-uuid", &uuid) || - strcmp(uuid, "DDF38191-93B3-324A-BC8F-643006F5AC82"))) + if (dcpext_pageflip && !dcp_ext_firmware_admitted(dcp->dev->of_node)) return -EOPNOTSUPP; node = of_get_child_by_name(dcp->dev->of_node, "scanout"); if (!scanout_node_verified(node)) { diff --git a/drivers/gpu/drm/apple/iomfb_v14_7.c b/drivers/gpu/drm/apple/iomfb_v14_7.c index 4f381ab769f315..4f1a9c529217f5 100644 --- a/drivers/gpu/drm/apple/iomfb_v14_7.c +++ b/drivers/gpu/drm/apple/iomfb_v14_7.c @@ -203,6 +203,26 @@ static const struct dcp_v14_board dcp_v14_board_j514m = { .log_bringup = true, }; +/* + * J516M (MacBook Pro 16", binned M3 Max): the T6034 DCP, image and handoff + * of the J514M in the J516C chassis, so the J516 panel: 3456x2234 with 74 + * notch rows above the 3456x2160 boot framebuffer. Panel controls stay off + * as on the J514M. + */ +static const struct dcp_v14_board dcp_v14_board_j516m = { + .name = "J516M", + .debugfs = "dcp-j516m", + .dcp_compatible = "apple,t6034-dcp", + .machine = "apple,j516m", + .soc = "apple,t6034", + .firmware_uuid = DCP_V14_J516C_FIRMWARE_UUID, + .handoff = "apple,t6034-handoff", + .panel_width = 3456, + .panel_height = 2234, + .promotion = true, + .log_bringup = true, +}; + static const struct dcp_v14_board *const dcp_v14_boards[] = { &dcp_v14_board_t6030, &dcp_v14_board_j613, @@ -210,6 +230,7 @@ static const struct dcp_v14_board *const dcp_v14_boards[] = { &dcp_v14_board_j516c, &dcp_v14_board_j514c, &dcp_v14_board_j514m, + &dcp_v14_board_j516m, }; struct dcp_v14_property { @@ -2045,8 +2066,8 @@ int iomfb_v14_7_external_prepare(struct apple_dcp *dcp) goto refuse; } /* The callbacks here are those of one firmware image. */ - if (of_property_read_string(dev->of_node, "apple,firmware-uuid", &uuid) || - strcmp(uuid, DCP_V14_FIRMWARE_UUID)) { + if (!dcp_ext_firmware_admitted(dev->of_node)) { + of_property_read_string(dev->of_node, "apple,firmware-uuid", &uuid); dev_err(dev, "external display processor firmware %s is not supported\n", uuid ?: "(unknown)"); ret = -ENODEV; @@ -2139,20 +2160,13 @@ static int dcpext_check_power(struct apple_dcp *dcp) struct device *dev = dcp->dev; struct device_node *ps, *entry; struct regmap *map; - const char *label; u32 offset, value; int ret; - ps = of_parse_phandle(dev->of_node, "power-domains", 0); - if (!ps) - return -ENODEV; /* The CPU domain of this pipe: dispext_cpu. */ - ret = of_property_read_string(ps, "label", &label); - if (ret || !strstarts(label, "dispext") || strlen(label) < 4 || - strcmp(label + strlen(label) - 4, "_cpu")) { - of_node_put(ps); + ps = dcp_ext_cpu_domain(dev->of_node); + if (!ps) return -EINVAL; - } ret = of_property_read_u32(ps, "reg", &offset); map = syscon_node_to_regmap(ps->parent); of_node_put(ps); diff --git a/drivers/gpu/drm/apple/v14-board-test.c b/drivers/gpu/drm/apple/v14-board-test.c index 23453f1368ee14..1b2cbd0cd8d879 100644 --- a/drivers/gpu/drm/apple/v14-board-test.c +++ b/drivers/gpu/drm/apple/v14-board-test.c @@ -125,10 +125,33 @@ static void v14_board_j514m(struct kunit *test) KUNIT_EXPECT_FALSE(test, dcp_v14_board_admits(b, T6030_DCP_IMAGE)); KUNIT_EXPECT_FALSE(test, b->panel_nits); KUNIT_EXPECT_NULL(test, board_for("apple,t6034-dcp", j514m_wrong_soc, &known)); - KUNIT_EXPECT_NULL(test, board_for("apple,t6034-dcp", j516m, &known)); + /* The J516M has a record of its own. */ + KUNIT_EXPECT_PTR_NE(test, b, board_for("apple,t6034-dcp", j516m, &known)); KUNIT_EXPECT_NULL(test, board_for("apple,t6031-dcp", j514m, &known)); } +/* The J516M: the J514M's T6034 DCP and image, with the J516 panel. */ +static void v14_board_j516m(struct kunit *test) +{ + const struct dcp_v14_board *b; + bool known; + + b = board_for("apple,t6034-dcp", j516m, &known); + KUNIT_ASSERT_NOT_NULL(test, b); + KUNIT_EXPECT_TRUE(test, known); + KUNIT_EXPECT_STREQ(test, b->name, "J516M"); + KUNIT_EXPECT_STREQ(test, b->debugfs, "dcp-j516m"); + KUNIT_EXPECT_STREQ(test, b->machine, "apple,j516m"); + KUNIT_EXPECT_STREQ(test, b->soc, "apple,t6034"); + KUNIT_EXPECT_STREQ(test, b->handoff, "apple,t6034-handoff"); + KUNIT_EXPECT_EQ(test, b->panel_width, 3456); + KUNIT_EXPECT_EQ(test, b->panel_height, 2234); + KUNIT_EXPECT_TRUE(test, dcp_v14_board_admits(b, J516C_DCP_IMAGE)); + KUNIT_EXPECT_FALSE(test, dcp_v14_board_admits(b, T6030_DCP_IMAGE)); + KUNIT_EXPECT_FALSE(test, b->panel_nits); + KUNIT_EXPECT_NULL(test, board_for("apple,t6031-dcp", j516m, &known)); +} + /* A T6031 DCP on a T6034 has no record: refused. */ static void v14_board_t6031_other_boards(struct kunit *test) { @@ -277,6 +300,7 @@ static struct kunit_case v14_board_cases[] = { KUNIT_CASE(v14_board_j516c), KUNIT_CASE(v14_board_j514c), KUNIT_CASE(v14_board_j514m), + KUNIT_CASE(v14_board_j516m), KUNIT_CASE(v14_board_t6031_other_boards), KUNIT_CASE(v14_board_j516c_image), KUNIT_CASE(v14_board_unchanged), diff --git a/drivers/gpu/drm/asahi/g15/boot.rs b/drivers/gpu/drm/asahi/g15/boot.rs index b0ea7bfc44aedf..2b4c22f9af0d0b 100644 --- a/drivers/gpu/drm/asahi/g15/boot.rs +++ b/drivers/gpu/drm/asahi/g15/boot.rs @@ -476,9 +476,11 @@ pub(crate) fn admit( && cfg.platform == hw::Platform::DeviceTree && glue::machine(kernel::c_str!("apple,j514c")) && glue::machine(kernel::c_str!("apple,t6031")); - let j514m_board = cfg.chip_id == 0x6034 + // T6034 boards: the J514M and the 16-inch J516M, which carries the same die. + let t6034_board = cfg.chip_id == 0x6034 && cfg.platform == hw::Platform::DeviceTree - && glue::machine(kernel::c_str!("apple,j514m")) + && (glue::machine(kernel::c_str!("apple,j514m")) + || glue::machine(kernel::c_str!("apple,j516m"))) && glue::machine(kernel::c_str!("apple,t6034")); if pmp { if !glue::board() { @@ -494,8 +496,8 @@ pub(crate) fn admit( } return Err(err); } - } else if (cfg.chip_id == 0x6034 && !j514m_board) - || (!glue::machine(cfg.machine) && !j514c_board) + } else if (cfg.chip_id == 0x6034 && !t6034_board) + || (!glue::machine(cfg.machine) && !j514c_board && !t6034_board) { dev_err!(dev, "G15: not a {} machine\n", cfg.name); return Err(ENODEV); @@ -511,7 +513,7 @@ pub(crate) fn admit( return Err(err); } } - if j514c_board || j514m_board { + if j514c_board || t6034_board { if let hw::Board::DeviceTree(reference) = cfg.board { dev_warn!( dev, diff --git a/drivers/gpu/drm/asahi/g15/hw/t6034.rs b/drivers/gpu/drm/asahi/g15/hw/t6034.rs index 1c0ec130cc177e..a26bf06ea88e02 100644 --- a/drivers/gpu/drm/asahi/g15/hw/t6034.rs +++ b/drivers/gpu/drm/asahi/g15/hw/t6034.rs @@ -85,6 +85,19 @@ pub(crate) const CONFIG: Config = Config { 0xe4, 0x1e, 0x5d, 0xaf, 0x86, 0x32, 0x51, 0x50, 0x77, 0xb5, 0x37, 0xca, 0x19, 0xe6, 0xd0, 0xdc, 0x77, 0x04, 0xb7, ], + }, FirmwareBuild { + // Measured on a J516M: the J514M's 14.7 image (RTKit 2419.140.12), + // byte for byte. Pinned to the J516M on its own row. + name: "J516M RTKit 2419.140.12", + machine: Some(kernel::c_str!("apple,j516m")), + text_size: 0x64000, + data_size: 0x140000, + marker: 0x5cf28, + text_sha256: [ + 0x4c, 0xa2, 0x4b, 0x65, 0x8f, 0x8e, 0xbe, 0x7c, 0x27, 0x37, 0x5f, 0xdf, 0x38, + 0xe4, 0x1e, 0x5d, 0xaf, 0x86, 0x32, 0x51, 0x50, 0x77, 0xb5, 0x37, 0xca, 0x19, + 0xe6, 0xd0, 0xdc, 0x77, 0x04, 0xb7, + ], }], machine: kernel::c_str!("apple,j514m"), platform: super::Platform::DeviceTree, diff --git a/drivers/mux/apple-display-crossbar.c b/drivers/mux/apple-display-crossbar.c index a02b8d9fe8a304..a3c5f3eaee4986 100644 --- a/drivers/mux/apple-display-crossbar.c +++ b/drivers/mux/apple-display-crossbar.c @@ -1003,7 +1003,7 @@ static int apple_dpxbar_probe(struct platform_device *pdev) spin_lock_init(&dpxbar->lock); dpxbar->dev = dev; - dpxbar->t6030_dpin = of_machine_is_compatible("apple,t6030"); + dpxbar->t6030_dpin = apple_dp_tunnel_m3_soc(); dpxbar->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(dpxbar->regs)) return PTR_ERR(dpxbar->regs); diff --git a/drivers/phy/apple/atc.c b/drivers/phy/apple/atc.c index 4ce4ad5062504f..4735e9f4b09606 100644 --- a/drivers/phy/apple/atc.c +++ b/drivers/phy/apple/atc.c @@ -3222,7 +3222,7 @@ static int atc_tunnel_set_t602x(struct apple_atcphy *atcphy, unsigned int dpin, /* T6030 (M3 Pro) uses T8122-generation PHYs for both DP IN clock outputs. */ static bool apple_atc_tunnel_is_t6030(struct apple_atcphy *atcphy) { - return of_machine_is_compatible("apple,t6030") && + return apple_dp_tunnel_m3_soc() && atcphy->hw->gen == ATCPHY_GENERATION_T8122; } @@ -3487,8 +3487,7 @@ static int atcphy_configure(struct apple_atcphy *atcphy, enum atcphy_mode mode) /* Keep failed transitions retryable even when the requested mode is unchanged. */ atcphy->mode_dirty = true; /* the mode change below may power the PHY down: stop the tunnel clock first */ - if (of_machine_is_compatible("apple,t6030") && - atcphy->hw->gen == ATCPHY_GENERATION_T8122) { + if (apple_atc_tunnel_is_t6030(atcphy)) { ret = atc_t8122_tunnel_stop(atcphy); if (ret) return ret; diff --git a/drivers/soc/apple/t6030-display-gate.c b/drivers/soc/apple/t6030-display-gate.c index 016abbcf6cf96f..2dd0b187213397 100644 --- a/drivers/soc/apple/t6030-display-gate.c +++ b/drivers/soc/apple/t6030-display-gate.c @@ -190,7 +190,8 @@ static bool gate_t6034_requested __initdata; static bool __init gate_t6034_default_board(void) { return of_machine_is_compatible("apple,t6034") && - of_machine_is_compatible("apple,j514m"); + (of_machine_is_compatible("apple,j514m") || + of_machine_is_compatible("apple,j516m")); } static bool __init gate_t6034_wanted(int sw) @@ -235,6 +236,7 @@ struct gate_soc { const char *fw_uuids; /* /chosen strings naming each firmware image */ bool quiet_without_nodes; /* no display nodes in the device tree is normal */ bool dcpext; /* the external display processors and their DARTs */ + bool ext_fe_first; /* dcpext -> dispextN_fe -> _cpu -> _sys (T6031/T6034) */ }; static const struct gate_soc gate_t6030 __initconst = { @@ -344,6 +346,13 @@ static const struct gate_soc gate_t6034 __initconst = { .pmp_ps_any_pmgr = true, .describe = true, .fw_uuids = "asahi,t6034-fw-uuids", + /* + * dcpext0 on the T6030 handoff protocol (markers + * apple,t6030-dcpext-memory-ready / -dispext-handoff from m1n1 with + * chosen asahi,t6034-dcpext / -dispext). Without them it stays off. + */ + .dcpext = true, + .ext_fe_first = true, }; static const struct gate_soc *const gate_socs[] __initconst = { @@ -1370,15 +1379,32 @@ static int __init gate_ext_resolve(struct gate_ext *ext) return -EINVAL; /* Do not enable a CPU whose power ownership is not fully described. */ - ext->cpu = gate_ps_parent(ext->dcp); - if (!gate_ps_dispext(ext->cpu, "cpu", &ext->index)) - return -EINVAL; - ext->fe = gate_ps_parent(ext->cpu); - if (!gate_ps_dispext(ext->fe, "fe", &ext->index)) - return -EINVAL; - ext->sys = gate_ps_parent(ext->fe); - if (!gate_ps_dispext(ext->sys, "sys", &ext->index)) - return -EINVAL; + if (gate_soc->ext_fe_first) { + /* + * T6031/T6034: dispextN_fe is the child of dispextN_cpu, as + * disp_fe is of disp_cpu, so the processor holds the front end + * and with it all three states. Its DART and mailbox hold the CPU. + */ + ext->fe = gate_ps_parent(ext->dcp); + if (!gate_ps_dispext(ext->fe, "fe", &ext->index)) + return -EINVAL; + ext->cpu = gate_ps_parent(ext->fe); + if (!gate_ps_dispext(ext->cpu, "cpu", &ext->index)) + return -EINVAL; + ext->sys = gate_ps_parent(ext->cpu); + if (!gate_ps_dispext(ext->sys, "sys", &ext->index)) + return -EINVAL; + } else { + ext->cpu = gate_ps_parent(ext->dcp); + if (!gate_ps_dispext(ext->cpu, "cpu", &ext->index)) + return -EINVAL; + ext->fe = gate_ps_parent(ext->cpu); + if (!gate_ps_dispext(ext->fe, "fe", &ext->index)) + return -EINVAL; + ext->sys = gate_ps_parent(ext->fe); + if (!gate_ps_dispext(ext->sys, "sys", &ext->index)) + return -EINVAL; + } for (i = 0; i < 2; i++) { domain = gate_ps_parent(i ? ext->mbox : ext->dart); of_node_put(domain); @@ -2060,7 +2086,7 @@ static void __init gate_run(void) } gate_t6034_requested = gate_t6034_wanted(gate_t6034_switch); if (gate_t6034_switch < 0) - gate_info("native display on by default on J514M; apple_t6034_display.enable=0 keeps the boot framebuffer\n"); + gate_info("native display on by default on J514M/J516M; apple_t6034_display.enable=0 keeps the boot framebuffer\n"); } if (!*gate_soc->requested) { diff --git a/drivers/thunderbolt/apple.c b/drivers/thunderbolt/apple.c index c6625c5cbb5d3f..d181aaf88423fb 100644 --- a/drivers/thunderbolt/apple.c +++ b/drivers/thunderbolt/apple.c @@ -151,6 +151,15 @@ static const struct apple_cio_hw apple_cio_t6030_hw = { .dp = &apple_dpin_m3, }; +/* + * T6031/T6034 (M3 Max): the ACIO bring-up stays the t8103 one it has used so + * far (the link already works with it); only the DP IN policy is the M3 one. + * DP tunnels still need the root to be on apple_dpin_qualified_soc. + */ +static const struct apple_cio_hw apple_cio_t6031_hw = { + .dp = &apple_dpin_m3, +}; + /* Type 7 uses its existing ready-state check and has no qualified DP route. */ static const struct apple_cio_hw apple_cio_t8142_hw = { .dp = &apple_dpin_disabled, @@ -376,6 +385,8 @@ static const struct of_device_id apple_dpin_qualified_soc[] = { { .compatible = "apple,t6020" }, { .compatible = "apple,t6021" }, { .compatible = "apple,t6030" }, + /* T6034: T8122-generation ATC PHYs and the M3 DP IN flow, as on T6030. */ + { .compatible = "apple,t6034" }, {}, }; @@ -3335,6 +3346,10 @@ static const struct of_device_id apple_acio_match[] = { .compatible = "apple,t6030-usb4-acio", .data = &apple_cio_t6030_hw, }, + { + .compatible = "apple,t6031-usb4-acio", + .data = &apple_cio_t6031_hw, + }, { .compatible = "apple,t6020-usb4-acio", .data = &apple_cio_t6020_hw, diff --git a/include/linux/soc/apple/dp-tunnel.h b/include/linux/soc/apple/dp-tunnel.h index 540d48fe8e0dcb..1f1c4903e4896d 100644 --- a/include/linux/soc/apple/dp-tunnel.h +++ b/include/linux/soc/apple/dp-tunnel.h @@ -180,6 +180,17 @@ static inline bool apple_dp_tunnel_route_wired(struct device_node *route, * T6030 old trees may omit DPIN names on their three-output T602X crossbar. * Consumers qualify their SoC flow separately and never select these muxes. */ +/* + * SoCs whose DP IN tunnels run the T6030 (M3) flow: T8122-generation ATC PHYs + * clocking both DP IN outputs, and a crossbar that routes every output itself. + * T6034 has the same ATC generation. + */ +static inline bool apple_dp_tunnel_m3_soc(void) +{ + return of_machine_is_compatible("apple,t6030") || + of_machine_is_compatible("apple,t6034"); +} + static inline bool apple_dp_tunnel_dual_stream(struct device_node *connector_np) { struct device_node *port __free(device_node) = NULL; diff --git a/tools/asahi/g15-display-order-tests.py b/tools/asahi/g15-display-order-tests.py index 6ff26493965c60..a1f7f8ea371381 100644 --- a/tools/asahi/g15-display-order-tests.py +++ b/tools/asahi/g15-display-order-tests.py @@ -221,9 +221,11 @@ def c_source(root): CHECK(publish(&d34) == 0 && d34.refs == 1); CHECK(prepare(&g34) == 0 && links_made == 1 && last_link.supplier == &d34); machine("apple,t6034", "apple,j516m"); - CHECK(prepare(&g34) == -ENODEV && links_made == 1); + CHECK(prepare(&g34) == 0 && links_made == 2 && last_link.supplier == &d34); + machine("apple,t6034", "apple,j999"); + CHECK(prepare(&g34) == -ENODEV && links_made == 2); machine("apple,t6031", "apple,j514m"); - CHECK(prepare(&g34) == -ENODEV && links_made == 1); + CHECK(prepare(&g34) == -ENODEV && links_made == 2); machine("apple,t6034", "apple,j514m"); withdraw(&d34); CHECK(!d34.refs && prepare(&g34) == -EPROBE_DEFER); @@ -243,7 +245,7 @@ def c_source(root): "t6031-reads-t6030-slot": ("&apple_t6031_display);", "&apple_t6030_display);"), "t6034-reads-t6031-slot": ("&apple_t6034_display);", "&apple_t6031_display);"), "t6034-published-as-t6031": ("return &apple_t6034_display;", "return &apple_t6031_display;"), - "t6034-no-board-check": ('if (!of_machine_is_compatible("apple,j514m"))', 'if (false)'), + "t6034-no-board-check": ('if (!of_machine_is_compatible("apple,j514m") &&\n\t !of_machine_is_compatible("apple,j516m"))', 'if (false)'), "t6031-published-as-t6030": ("return &apple_t6031_display;", "return &apple_t6030_display;"), "no-machine-check": ("!of_machine_is_compatible(machine) ||", ""), "withdraw-keeps-display": ("*slot = NULL;", ";"), diff --git a/tools/asahi/g15-t6034-profile-tests.py b/tools/asahi/g15-t6034-profile-tests.py index 4f73b5172aee76..16fde7183569a4 100644 --- a/tools/asahi/g15-t6034-profile-tests.py +++ b/tools/asahi/g15-t6034-profile-tests.py @@ -58,13 +58,17 @@ def item(text, marker): assert_eq!(c.gpu_revision,0x11); assert_eq!((c.hw_ids[0].version,c.hw_ids[0].counts),(0x07041100,0x0021040a)); // An empty table is the first diagnostic build. A completed profile may - // contain only the measured row for this exact board. - assert_eq!(c.firmware.len(),1); + // contain only the measured rows, each pinned to its exact board: the J514M, + // and the J516M, measured with the same image. + assert_eq!(c.firmware.len(),2); if !c.firmware.is_empty() { use lookup::{Measured,Verdict}; - assert_eq!(c.firmware.len(),1); let b=&c.firmware[0]; assert_eq!(b.machine.map(|m|m.to_bytes()),Some(b"apple,j514m".as_slice())); + let j516m=&c.firmware[1]; + assert_eq!(j516m.machine.map(|m|m.to_bytes()),Some(b"apple,j516m".as_slice())); + assert_eq!((j516m.text_size,j516m.data_size,j516m.marker,j516m.text_sha256), + (b.text_size,b.data_size,b.marker,b.text_sha256)); let exact=Measured {text_size:b.text_size,data_size:b.data_size,marker:b.marker,sha256:b.text_sha256}; let verdict=|measured:&Measured,board:&[u8]| { let qualified=c.firmware.iter().map(|b| { @@ -74,10 +78,13 @@ def item(text, marker): lookup::verdict(measured,qualified,0) }; assert_eq!(verdict(&exact,b"apple,j514m"),Verdict::Pinned(0)); - for board in [b"apple,j514c".as_slice(),b"apple,j516c",b"apple,j516m",b"apple,j514s",b"apple,j999"] { + assert_eq!(verdict(&exact,b"apple,j516m"),Verdict::Pinned(1)); + for board in [b"apple,j514c".as_slice(),b"apple,j516c",b"apple,j514s",b"apple,j999"] { assert_eq!(verdict(&exact,board),Verdict::Refused); } let mut changed=exact;changed.sha256[31]^=1; + assert_eq!(verdict(&changed,b"apple,j516m"),Verdict::Refused); + let mut changed=exact;changed.sha256[31]^=1; assert_eq!(verdict(&changed,b"apple,j514m"),Verdict::Refused); let mut changed=exact;changed.text_size+=0x4000; assert_eq!(verdict(&changed,b"apple,j514m"),Verdict::Refused); diff --git a/tools/asahi/t6030-clock-tests.py b/tools/asahi/t6030-clock-tests.py index 0b75c3ade6f5d6..459006c1a7bea7 100644 --- a/tools/asahi/t6030-clock-tests.py +++ b/tools/asahi/t6030-clock-tests.py @@ -10,7 +10,7 @@ import subprocess ROOT = Path(__file__).resolve().parents[2] FUNCTIONS = { - 'include/linux/soc/apple/dp-tunnel.h': ['apple_dp_tunnel_t6030_pclk'], + 'include/linux/soc/apple/dp-tunnel.h': ['apple_dp_tunnel_t6030_pclk', 'apple_dp_tunnel_m3_soc'], 'drivers/gpu/drm/apple/dcp-fabric-core.h': ['dcp_fabric_t6030_pclk'], 'drivers/gpu/drm/apple/dcp-fabric-session.h': ['dcp_fabric_callback_valid'], 'drivers/phy/apple/atc.c': [ diff --git a/tools/asahi/t6034-display-tests.py b/tools/asahi/t6034-display-tests.py index ddd1cf54cc4c39..f2b1f14205edac 100644 --- a/tools/asahi/t6034-display-tests.py +++ b/tools/asahi/t6034-display-tests.py @@ -41,7 +41,7 @@ def item(s, marker): const char *socs[]={"apple,t6034","apple,t6031","apple,t6030",NULL}; dcp="apple,t6034-dcp"; for(unsigned i=0;ifirmware_uuid);','return uuid != NULL;'), } diff --git a/tools/asahi/tunnel-route-tests.py b/tools/asahi/tunnel-route-tests.py index 5be05c91c156f6..4bf11b24f7d5af 100644 --- a/tools/asahi/tunnel-route-tests.py +++ b/tools/asahi/tunnel-route-tests.py @@ -11,7 +11,7 @@ ROOT = Path(__file__).resolve().parents[2] FUNCTIONS = { - 'include/linux/soc/apple/dp-tunnel.h': ['apple_dp_tunnel_t6030_pclk'], + 'include/linux/soc/apple/dp-tunnel.h': ['apple_dp_tunnel_t6030_pclk', 'apple_dp_tunnel_m3_soc'], 'drivers/gpu/drm/apple/dcp-fabric-core.c': ['dcp_fabric_tunnel_clock_blocked'], 'drivers/gpu/drm/apple/dcp-fabric.c': [ 'dcp_fabric_snapshot_port', 'dcp_typec_tunnel_ctl'], @@ -19,7 +19,7 @@ def function(source, name): - match = re.search(r'^(?:static\s+)?(?:inline\s+)?(?:int|void|u8|struct mux_control \*)\s*' + + match = re.search(r'^(?:static\s+)?(?:inline\s+)?(?:int|void|u8|bool|struct mux_control \*)\s*' + name + r'\([^;]*?\)\n\{', source, re.M | re.S) if not match: raise ValueError(name) diff --git a/tools/asahi/tunnel-route/fixture.c b/tools/asahi/tunnel-route/fixture.c index dd63853c3bd369..ef5058a3443b5c 100644 --- a/tools/asahi/tunnel-route/fixture.c +++ b/tools/asahi/tunnel-route/fixture.c @@ -55,6 +55,9 @@ struct apple_dcp_typec_port { static bool t6030; static bool of_machine_is_compatible(const char *compatible) { + /* apple_dp_tunnel_m3_soc() also asks for T6034, which no scenario here models. */ + if (!strcmp(compatible, "apple,t6034")) + return false; assert(!strcmp(compatible, "apple,t6030")); return t6030; }