From 3ae1686d04bac7fa5ecc040aafb6778112d5ed7e Mon Sep 17 00:00:00 2001 From: Thomas Glanzmann Date: Thu, 12 Nov 2026 14:25:21 +0100 Subject: [PATCH 1/2] drm/apple: afk: T8142: Bind powerlog-service on firmware 27 The M5's DCP announces powerlog-service on ep:20 with only ep-has-desc-mgr and interface-id. AFK matches such a service by its announced name, but only for the H17G method profile, and a T8142 on firmware 27 runs the default profile. So on 27.0 and 27.2 the service is never bound ("AFK[ep:20]: failed to extract init props: -2"), and the mNits reports it carries, the loader-level hint that dcp_backlight_takeover() takes on firmware 27, could never arrive. Fall back to the announced name for a T8142 on firmware 27 as well: the H17P-generation processor (hw.firmware_compat) that probe admits as firmware 27. The service-rebind host test's apple_dcp gets that field. On 27.2 the service now binds on channel 1. That firmware sent no report in the first minutes after boot, so the backlight still starts from the fallback level there. Signed-off-by: Thomas Glanzmann --- drivers/gpu/drm/apple/afk.c | 11 ++++++++--- tools/testing/selftests/drivers/gpu/apple-dcp/edid.py | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/apple/afk.c b/drivers/gpu/drm/apple/afk.c index a5c271254437..4ea4e00f0a45 100644 --- a/drivers/gpu/drm/apple/afk.c +++ b/drivers/gpu/drm/apple/afk.c @@ -1561,11 +1561,16 @@ static void afk_recv_handle_init(struct apple_dcp_afkep *ep, u32 channel, (ep->dcp->fw_compat == DCP_FIRMWARE_H17P || ep->dcp->fw_compat == DCP_FIRMWARE_V_27_0), ep->dcp->fw_compat == DCP_FIRMWARE_V_27_0); if (ret == -ENOENT && - ep->dcp->hw.iomfb_method_profile == DCP_IOMFB_METHODS_H17G) { + (ep->dcp->hw.iomfb_method_profile == DCP_IOMFB_METHODS_H17G || + (ep->dcp->fw_compat == DCP_FIRMWARE_V_27_0 && + ep->dcp->hw.firmware_compat == DCP_FIRMWARE_H17P))) { /* * M5 (H17G) announces powerlog-service with only - * ep-has-desc-mgr and interface-id; match it by the - * announced name, as for props-less announces. + * ep-has-desc-mgr and interface-id, on 26.x and on + * firmware 27 alike (the T8142, an H17P-generation + * processor, is the one that runs firmware 27); match + * it by the announced name, as for props-less + * announces. */ epic_name = NULL; epic_class = NULL; diff --git a/tools/testing/selftests/drivers/gpu/apple-dcp/edid.py b/tools/testing/selftests/drivers/gpu/apple-dcp/edid.py index 522740ab2bd3..602cf729aea4 100644 --- a/tools/testing/selftests/drivers/gpu/apple-dcp/edid.py +++ b/tools/testing/selftests/drivers/gpu/apple-dcp/edid.py @@ -98,7 +98,7 @@ def function(file,signature): struct apple_dcp { void *dev;int fw_compat;struct apple_dcp_afkep *dcpavservep;struct apple_connector *connector; bool external_physically_absent,hpd_mutex,quiescing,typec_cable_connected;u64 typec_generation;unsigned int nr_modes; - struct { int iomfb_method_profile; bool fw_14_7_endpoints; unsigned int num_dptx_ports; } hw; bool ext_dead,external_native; + struct { int iomfb_method_profile,firmware_compat; bool fw_14_7_endpoints; unsigned int num_dptx_ports; } hw; bool ext_dead,external_native; struct { bool lock,enabled;u64 epoch;int enable_completion;struct apple_epic_service *service,*opened; u64 opened_epoch,close_gen,terminated_epoch,close_epoch;bool opening;struct work_struct close_wq; } dcpavserv; }; From 55d5f6d05618a3ef4219d3cf54f8189b67a2b4a0 Mon Sep 17 00:00:00 2001 From: Thomas Glanzmann Date: Thu, 12 Nov 2026 14:28:33 +0100 Subject: [PATCH 2/2] drm/apple: dcp: Send a present without a surface at the completed level On firmware 27 the first present of a new KMS client (modetest, kmscube) fails with "present dropped: backlight prepare -61" and "swap failed!", and modetest -v then sees no page flip. With the panel backlight active, iomfb_apply_backlight_h17p() drops every present that dcp_backlight_prepare() refuses. dcp_bl_prepare() refuses a present without a surface with -ENODATA, because such a present cannot apply a new level, so the whole present is dropped rather than only the level. Treat a present without a surface as one with nothing to reserve: it carries the last completed level and is sent, and a pending level waits, still dirty, for the next present with a surface. Only the H17P/firmware 27 path asks without a surface; the M4 and 14.7 callers always pass one. Signed-off-by: Thomas Glanzmann --- drivers/gpu/drm/apple/dcp_backlight.c | 11 ++++++++-- .../drm/apple/iomfb_v14_7_backlight_test.c | 20 +++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp_backlight.c b/drivers/gpu/drm/apple/dcp_backlight.c index 00f29157241f..c029cca6d300 100644 --- a/drivers/gpu/drm/apple/dcp_backlight.c +++ b/drivers/gpu/drm/apple/dcp_backlight.c @@ -429,9 +429,16 @@ int dcp_backlight_prepare(struct apple_dcp *dcp, bool have_surface, spin_lock_irqsave(&dcp->backlight.lock, flags); ret = dcp_bl_prepare(&dcp->backlight.state, have_surface, present); - /* No reservation: carry the last completed level on an ordinary present. */ - if (ret == -EALREADY) + /* + * No reservation: carry the last completed level on an ordinary + * present, and on one without a surface, which cannot apply a new + * level. A pending level waits for the next present with a surface. + */ + if (ret == -EALREADY || + (ret == -ENODATA && !have_surface && dcp->backlight.state.ready)) { present->nits = dcp->backlight.state.actual; + ret = -EALREADY; + } spin_unlock_irqrestore(&dcp->backlight.lock, flags); return ret; } diff --git a/drivers/gpu/drm/apple/iomfb_v14_7_backlight_test.c b/drivers/gpu/drm/apple/iomfb_v14_7_backlight_test.c index 3c1fc2f45d53..a84e45338793 100644 --- a/drivers/gpu/drm/apple/iomfb_v14_7_backlight_test.c +++ b/drivers/gpu/drm/apple/iomfb_v14_7_backlight_test.c @@ -554,6 +554,25 @@ static void v14_bl_t6030_work(struct kunit *test) KUNIT_EXPECT_TRUE(test, dcp_backlight_pending(f.dcp)); } +/* + * A present without a surface cannot apply a new level: it carries the last + * completed one and reserves nothing, so it is still sent, and the request + * waits for the next present with a surface. + */ +static void v14_bl_surfaceless_present(struct kunit *test) +{ + struct v14_bl_fixture f = v14_bl_fixture(test, &dcp_v14_board_t6030, true); + struct dcp_backlight_present p = { .nits = U32_MAX }; + + v14_bl_request(test, f.dcp, 250); + KUNIT_EXPECT_EQ(test, dcp_backlight_prepare(f.dcp, false, &p), -EALREADY); + KUNIT_EXPECT_EQ(test, p.nits, DCP_V14_NITS_DEFAULT); + KUNIT_EXPECT_TRUE(test, f.dcp->backlight.state.dirty); + KUNIT_EXPECT_FALSE(test, f.dcp->backlight.state.in_flight); + KUNIT_ASSERT_EQ(test, dcp_backlight_prepare(f.dcp, true, &p), 0); + KUNIT_EXPECT_EQ(test, p.nits, 250U); +} + static struct kunit_case v14_backlight_cases[] = { KUNIT_CASE(v14_bl_binary64), KUNIT_CASE(v14_bl_level_block), @@ -562,6 +581,7 @@ static struct kunit_case v14_backlight_cases[] = { KUNIT_CASE(v14_bl_gate_transitions), KUNIT_CASE(v14_bl_plan), KUNIT_CASE(v14_bl_policy), + KUNIT_CASE(v14_bl_surfaceless_present), KUNIT_CASE(v14_bl_other_boards_untouched), KUNIT_CASE(v14_bl_external_untouched), KUNIT_CASE(v14_bl_t6030_swap_levels),