From 61a1fd0861701288b3bf49f021ee34242501b12a Mon Sep 17 00:00:00 2001 From: Jeff Queisser Date: Mon, 2 Nov 2026 14:27:25 -0800 Subject: [PATCH 1/2] drm/apple: dcp: Choose the firmware 27 method table by firmware build Firmware 27.2 renumbered the IOMFB AP methods: the IOMobileFramebufferAP methods moved up by 50, and set_create_DFB and vi_set_temperature_hint by one. The UPPipe methods are unchanged. A DCP running 27.2 that is sent a 27.0 tag panics ("link_rpc_lookup callback not found"); on T6050 the first one sent was A427. Keep a method table per 27.x build and choose it at probe from apple,firmware-compat ("27.0.0" or "27.2.0"). A firmware-compat with no table is refused at probe with a message naming it, where a 27.x string other than 27.0.0 was refused silently before. The 27.2 tag of dfb_ready_query has not been seen in a call, so the 27.2 table has no entry for it and boot_5() does not send it to a build whose table has none. Signed-off-by: Jeff Queisser Co-Authored-By: Claude Opus 5.5 [Rolando: resolved context conflicts with the afk_power_gen field and the panel_identity allocation that PR #1012 added next to the new lines; the 27.2 entry A478 (27.0 A428) is named iomfbep_set_brightness_correction, as PR #1012's 27.0 table names A428, since dcpep_pipe_set_428 is not in its method enum; left dfb_ready_query out of the 27.2 table, where it was A385 by inference, and made boot_5() skip a query that has no tag] Signed-off-by: Rolando Trevino --- drivers/gpu/drm/apple/dcp-internal.h | 2 + drivers/gpu/drm/apple/dcp.c | 12 +++- .../gpu/drm/apple/iomfb-peer-runtime-v27.inc | 4 +- drivers/gpu/drm/apple/iomfb.h | 3 + drivers/gpu/drm/apple/iomfb_template.c | 5 ++ drivers/gpu/drm/apple/iomfb_v27.c | 67 ++++++++++++++++++- 6 files changed, 89 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index ac355dcd2c5cc..178e29ac5e9e4 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -457,6 +457,8 @@ struct apple_dcp { enum dcp_firmware_version fw_compat; /* Bumped by every iomfb poweron; see afk_compact_slot(). */ u32 afk_power_gen; + /* Firmware 27.x: the method IDs of this build (iomfb_v27_methods()). */ + const struct dcp_method_entry *v27_methods; bool external; /* A desktop 26.x external processor (apple,t6041-dcpext) */ bool desktop_external; diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index bc452a840da83..632ac9f93de48 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -5060,8 +5060,11 @@ static enum dcp_firmware_version dcp_check_firmware_version(struct device *dev) if (hw->firmware_compat == DCP_FIRMWARE_V_27_0) { ret = dcp_read_fw_version(dev, "apple,firmware-compat", compat_str); - if (ret < 0 || strcmp(compat_str, "27.0.0")) + if (ret < 0 || !iomfb_v27_methods(compat_str)) { + dev_err(dev, "DCP firmware-compat %s has no 27.x method table\n", + ret < 0 ? "missing" : compat_str); return DCP_FIRMWARE_UNKNOWN; + } return DCP_FIRMWARE_V_27_0; } @@ -6174,6 +6177,13 @@ static int dcp_platform_probe_pipe(struct platform_device *pdev) return -ENOMEM; } } + if (fw_compat == DCP_FIRMWARE_V_27_0) { + char compat_str[DCP_FW_VERSION_STR_LEN]; + + /* Checked by dcp_check_firmware_version(). */ + dcp_read_fw_version(dev, "apple,firmware-compat", compat_str); + dcp->v27_methods = iomfb_v27_methods(compat_str); + } if (fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP) { dcp->desktop = devm_kzalloc(dev, sizeof(*dcp->desktop), GFP_KERNEL); if (!dcp->desktop) diff --git a/drivers/gpu/drm/apple/iomfb-peer-runtime-v27.inc b/drivers/gpu/drm/apple/iomfb-peer-runtime-v27.inc index 5d82c88266756..2faa735d84fd4 100644 --- a/drivers/gpu/drm/apple/iomfb-peer-runtime-v27.inc +++ b/drivers/gpu/drm/apple/iomfb-peer-runtime-v27.inc @@ -83,12 +83,12 @@ static void dcp_peer_started_on_owner(struct apple_dcp *owner, void *data, void dcp_peer_swap_armed_v27(owner, id); trace_iomfb_swap_submit(owner, id); trace_iomfb_swap_submit(peer, id); - dcp_push(owner, false, &dcp_methods[dcpep_swap_submit], sizeof(owner->present_v27), + dcp_push(owner, false, &owner->v27_methods[dcpep_swap_submit], sizeof(owner->present_v27), sizeof(struct dcp_swap_submit_resp_v27), &owner->present_v27, dcp_peer_submitted, owner); if (READ_ONCE(owner->crashed)) return; - dcp_push(peer, false, &dcp_methods[dcpep_swap_submit], sizeof(owner->peer_present_v27), + dcp_push(peer, false, &peer->v27_methods[dcpep_swap_submit], sizeof(owner->peer_present_v27), sizeof(struct dcp_swap_submit_resp_v27), &owner->peer_present_v27, dcp_peer_submitted, owner); if (READ_ONCE(peer->crashed)) diff --git a/drivers/gpu/drm/apple/iomfb.h b/drivers/gpu/drm/apple/iomfb.h index 1021825209fb3..31581bd6dff17 100644 --- a/drivers/gpu/drm/apple/iomfb.h +++ b/drivers/gpu/drm/apple/iomfb.h @@ -212,6 +212,9 @@ struct dcp_method_entry { char tag_h17g[4]; }; +/* The IOMFB method table of a firmware 27.x build, by its compat string; NULL if unknown. */ +const struct dcp_method_entry *iomfb_v27_methods(const char *compat); + #define IOMFB_MAX_CB (1000) bool iomfb_validate_callback_h17p(int tag, u32 in_len, u32 out_len); diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index 52235ecf334e3..6d30c09a3ab79 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -1706,6 +1706,11 @@ static void boot_5(struct apple_dcp *dcp, void *out, void *cookie) { #if DCP_FW_H17(DCP_FW_VER) /* H17P expects A380 right before set_display_refresh_properties */ + if (!dcp_methods[dcpep_dfb_ready_query].tag[0]) { + /* No tag is known for this firmware build: do not send one. */ + boot_5b(dcp, out, cookie); + return; + } dcp_dfb_ready_query(dcp, false, boot_5b, NULL); #else boot_5b(dcp, out, cookie); diff --git a/drivers/gpu/drm/apple/iomfb_v27.c b/drivers/gpu/drm/apple/iomfb_v27.c index 93d2fa72cc41b..3ff748d674374 100644 --- a/drivers/gpu/drm/apple/iomfb_v27.c +++ b/drivers/gpu/drm/apple/iomfb_v27.c @@ -69,7 +69,8 @@ static_assert(offsetof(struct dcp_swap_v27, brightness_update) == 0x354); static_assert(offsetof(struct dcp_swap_v27, brightness_nits) == 0x35e); static_assert(sizeof(struct dcp_swap_v27) == 0x6e0); -static const struct dcp_method_entry dcp_methods[dcpep_num_methods] = { +/* Firmware 27.0, the build Aurora's t6050-gpu table was written for. */ +static const struct dcp_method_entry dcp_methods_27_0[dcpep_num_methods] = { IOMFB_METHOD("A000", dcpep_late_init_signal), IOMFB_METHOD("A027", dcpep_setup_video_limits), IOMFB_METHOD("A136", iomfbep_a131_pmu_service_matched), @@ -113,6 +114,70 @@ static const struct dcp_method_entry dcp_methods[dcpep_num_methods] = { IOMFB_METHOD("A483", dcpep_register_dfb_surface), }; +/* + * Firmware 27.2 renumbered the AP methods: UnifiedPipeline2 grew into + * A400/A401, so IOMobileFramebufferAP moved up by 50 (start_signal A451, + * get_color_remap_mode A477), and set_create_DFB and vi_set_temperature_hint + * up by one. The UPPipe A0xx and A1xx IDs are unchanged. The firmware rejects + * an unknown tag with a panic ("link_rpc_lookup callback not found"), so + * dfb_ready_query, whose 27.2 tag has not been seen in a call, has no entry + * here and is not sent to this build (boot_5()). + */ +static const struct dcp_method_entry dcp_methods_27_2[dcpep_num_methods] = { + IOMFB_METHOD("A000", dcpep_late_init_signal), + IOMFB_METHOD("A027", dcpep_setup_video_limits), + IOMFB_METHOD("A136", iomfbep_a131_pmu_service_matched), + IOMFB_METHOD("A137", iomfbep_a132_backlight_service_matched), + IOMFB_METHOD("A390", dcpep_set_create_dfb), + IOMFB_METHOD("A391", iomfbep_a358_vi_set_temperature_hint), + IOMFB_METHOD("A451", dcpep_start_signal), + IOMFB_METHOD("A456", dcpep_swap_start), + IOMFB_METHOD("A458", dcpep_swap_submit), + IOMFB_METHOD("A461", dcpep_set_display_device), + IOMFB_METHOD("A462", dcpep_is_main_display), + IOMFB_METHOD("A463", dcpep_set_digital_out_mode), + IOMFB_METHOD("A473", iomfbep_set_matrix), + IOMFB_METHOD("A477", iomfbep_get_color_remap_mode), + IOMFB_METHOD("A500", dcpep_set_parameter_dcp), + IOMFB_METHOD("A504", dcpep_create_default_fb), + IOMFB_METHOD("A037", dcpep_get_dfb_compression_info), + IOMFB_METHOD("A036", dcpep_get_dfb_info), + IOMFB_METHOD("A103", dcpep_get_dfb_layout), + IOMFB_METHOD("A503", dcpep_get_dfb_state), + IOMFB_METHOD("A104", dcpep_set_dfb_dimensions), + IOMFB_METHOD("A105", dcpep_commit_dfb_info), + IOMFB_METHOD("A035", dcpep_dfb_query), + IOMFB_METHOD("A353", dcpep_pipe_query_353), + IOMFB_METHOD("A352", dcpep_pipe_set_352), + IOMFB_METHOD("A465", dcpep_pipe_cfg_415), + IOMFB_METHOD("A033", dcpep_pipe_cfg_031), + IOMFB_METHOD("A464", dcpep_pipe_cfg_414), + IOMFB_METHOD("A539", dcpep_pipe_query_478), + IOMFB_METHOD("A535", dcpep_pipe_query_474), + IOMFB_METHOD("A537", dcpep_pipe_set_476), + IOMFB_METHOD("A478", iomfbep_set_brightness_correction), + IOMFB_METHOD("A508", dcpep_enable_disable_video_power_savings), + IOMFB_METHOD("A516", dcpep_first_client_open), + IOMFB_METHOD("A517", iomfbep_last_client_close), + IOMFB_METHOD("A525", dcpep_set_display_refresh_properties), + IOMFB_METHOD("A528", dcpep_flush_supports_power), + IOMFB_METHOD("A529", iomfbep_abort_swaps_dcp), + IOMFB_METHOD("A534", dcpep_set_power_state), + IOMFB_METHOD("A533", dcpep_register_dfb_surface), +}; + +const struct dcp_method_entry *iomfb_v27_methods(const char *compat) +{ + if (!strcmp(compat, "27.0.0")) + return dcp_methods_27_0; + if (!strcmp(compat, "27.2.0")) + return dcp_methods_27_2; + return NULL; +} + +/* The template's calls resolve through the table probe chose for this build. */ +#define dcp_methods (dcp->v27_methods) + #define iomfb_modeset_h17p iomfb_modeset_v27 #define iomfb_flush_h17p iomfb_flush_v27 #define iomfb_poweron_h17p iomfb_poweron_v27 From bf43e8cefdf39886e9b7e4ae68a8781533ac6633 Mon Sep 17 00:00:00 2001 From: Rolando Trevino Date: Sat, 7 Nov 2026 12:42:27 -0500 Subject: [PATCH 2/2] drm/apple: Add the traced firmware 27.3 IOMFB method IDs The macOS 27.3 beta 1 stub (26C5035) carries DCP firmware AppleDCP-1317.60.10. A trace of the calls the macOS 27.3 display driver makes to the DCP of a J716s while it starts shows 20 of this driver's 41 methods, each with the request and reply sizes the driver uses. Three of them are one above their 27.2 IDs: set_display_refresh_properties A526, flush_supports_power A529 and register_dfb_surface A534. create_default_fb is still A504, so 27.3 has one more method between those. The same trace has dfb_ready_query at A385, right before set_display_refresh_properties. Add a 27.3 table with those 20 methods and nothing else. The trace has no display client, so it has no mode set, swap or power state call, and the other 21 methods stay out of the table until a trace shows them. A method without an ID would be sent with an empty tag, and the firmware answers an unknown tag with a panic. So check a 27.x table at probe: if it lacks the ID of any method the driver sends, refuse the firmware with a message that names the build, the number of missing methods and the first one. dfb_ready_query is the exception, since boot_5() already skips it for a build without one. The 27.0 and 27.2 tables pass. "27.3.0" now selects its own table and is refused by this check instead of as an unknown build, so the driver still does not start a 27.3 DCP. Signed-off-by: Rolando Trevino Assisted-by: LLM Claude Co-Authored-By: Claude Fable 5.2 --- drivers/gpu/drm/apple/dcp.c | 9 +++++ drivers/gpu/drm/apple/iomfb.h | 3 ++ drivers/gpu/drm/apple/iomfb_v27.c | 62 +++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+) diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 632ac9f93de48..3099b5fb3642a 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -5044,6 +5044,8 @@ static enum dcp_firmware_version dcp_check_firmware_version(struct device *dev) const struct dcp_v14_board *v14_board; char compat_str[DCP_FW_VERSION_STR_LEN]; char fw_str[DCP_FW_VERSION_STR_LEN]; + unsigned int num_missing; + const char *missing; int ret; /* @@ -5065,6 +5067,13 @@ static enum dcp_firmware_version dcp_check_firmware_version(struct device *dev) ret < 0 ? "missing" : compat_str); return DCP_FIRMWARE_UNKNOWN; } + missing = iomfb_v27_missing_method(iomfb_v27_methods(compat_str), + &num_missing); + if (missing) { + dev_err(dev, "DCP firmware-compat %s not started: no ID known for %u methods (first: %s)\n", + compat_str, num_missing, missing); + return DCP_FIRMWARE_UNKNOWN; + } return DCP_FIRMWARE_V_27_0; } diff --git a/drivers/gpu/drm/apple/iomfb.h b/drivers/gpu/drm/apple/iomfb.h index 31581bd6dff17..f41c6027e3e2a 100644 --- a/drivers/gpu/drm/apple/iomfb.h +++ b/drivers/gpu/drm/apple/iomfb.h @@ -214,6 +214,9 @@ struct dcp_method_entry { /* The IOMFB method table of a firmware 27.x build, by its compat string; NULL if unknown. */ const struct dcp_method_entry *iomfb_v27_methods(const char *compat); +/* The first method of a firmware 27.x table that has no ID, NULL if none. */ +const char *iomfb_v27_missing_method(const struct dcp_method_entry *methods, + unsigned int *count); #define IOMFB_MAX_CB (1000) diff --git a/drivers/gpu/drm/apple/iomfb_v27.c b/drivers/gpu/drm/apple/iomfb_v27.c index 3ff748d674374..2b527f2f2bf2f 100644 --- a/drivers/gpu/drm/apple/iomfb_v27.c +++ b/drivers/gpu/drm/apple/iomfb_v27.c @@ -166,15 +166,77 @@ static const struct dcp_method_entry dcp_methods_27_2[dcpep_num_methods] = { IOMFB_METHOD("A533", dcpep_register_dfb_surface), }; +/* + * Firmware 27.3: only the methods seen, with the request and reply sizes this + * driver uses, in a trace of the calls the macOS 27.3 display driver makes to + * the DCP of a J716s while it starts. Three are one above their 27.2 IDs + * (set_display_refresh_properties A526, flush_supports_power A529, + * register_dfb_surface A534) and create_default_fb is still A504, so 27.3 has + * one more method somewhere between those. That trace has no display client, + * so it has no mode set, swap or power state call, and the methods it does not + * show have no entry here. Until a trace shows them the table is incomplete + * and the driver does not start this firmware (iomfb_v27_missing_method()). + */ +static const struct dcp_method_entry dcp_methods_27_3[dcpep_num_methods] = { + IOMFB_METHOD("A000", dcpep_late_init_signal), + IOMFB_METHOD("A027", dcpep_setup_video_limits), + IOMFB_METHOD("A136", iomfbep_a131_pmu_service_matched), + IOMFB_METHOD("A137", iomfbep_a132_backlight_service_matched), + IOMFB_METHOD("A390", dcpep_set_create_dfb), + IOMFB_METHOD("A391", iomfbep_a358_vi_set_temperature_hint), + IOMFB_METHOD("A451", dcpep_start_signal), + IOMFB_METHOD("A504", dcpep_create_default_fb), + IOMFB_METHOD("A037", dcpep_get_dfb_compression_info), + IOMFB_METHOD("A036", dcpep_get_dfb_info), + IOMFB_METHOD("A103", dcpep_get_dfb_layout), + IOMFB_METHOD("A503", dcpep_get_dfb_state), + IOMFB_METHOD("A104", dcpep_set_dfb_dimensions), + IOMFB_METHOD("A105", dcpep_commit_dfb_info), + IOMFB_METHOD("A035", dcpep_dfb_query), + IOMFB_METHOD("A385", dcpep_dfb_ready_query), + IOMFB_METHOD("A353", dcpep_pipe_query_353), + IOMFB_METHOD("A526", dcpep_set_display_refresh_properties), + IOMFB_METHOD("A529", dcpep_flush_supports_power), + IOMFB_METHOD("A534", dcpep_register_dfb_surface), +}; + const struct dcp_method_entry *iomfb_v27_methods(const char *compat) { if (!strcmp(compat, "27.0.0")) return dcp_methods_27_0; if (!strcmp(compat, "27.2.0")) return dcp_methods_27_2; + if (!strcmp(compat, "27.3.0")) + return dcp_methods_27_3; return NULL; } +/* + * The driver sends every method of the 27.0 table. dfb_ready_query is the one + * it leaves out for a build whose table has no ID for it (boot_5()). Return + * the name of the first other method @methods has no ID for, with their number + * in @count, or NULL if the table is complete. A method without an ID would go + * out with an empty tag, which the firmware answers with a panic, so a build + * with an incomplete table must not be started. + */ +const char *iomfb_v27_missing_method(const struct dcp_method_entry *methods, + unsigned int *count) +{ + const char *first = NULL; + unsigned int i; + + *count = 0; + for (i = 0; i < dcpep_num_methods; i++) { + if (!dcp_methods_27_0[i].tag[0] || methods[i].tag[0] || + i == dcpep_dfb_ready_query) + continue; + if (!first) + first = dcp_methods_27_0[i].name; + (*count)++; + } + return first; +} + /* The template's calls resolve through the table probe chose for this build. */ #define dcp_methods (dcp->v27_methods)