From d3f6486fd3fddea01310e834b7e261e339de7da4 Mon Sep 17 00:00:00 2001 From: Satya Benson Date: Fri, 30 Oct 2026 21:18:41 -0400 Subject: [PATCH 1/3] drm/apple: accept automatic PMGR activity in J613 coldplug Use the PMGR provider active-state predicate when checking inherited display power. DISP_CPU can report ACTUAL=4 with AUTO_ENABLE and TARGET=15 after automatic clock gating. Keep the checks read-only and preserve all other handoff requirements. Test all actual, target and automatic-state combinations and the captured J613 state through the production helper. Signed-off-by: Satya Benson --- drivers/gpu/drm/apple/j613_dcp_activate.c | 9 ++++- tools/asahi/run-host-tests.py | 1 + tools/asahi/test-j613-dcp-power.py | 45 +++++++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 tools/asahi/test-j613-dcp-power.py diff --git a/drivers/gpu/drm/apple/j613_dcp_activate.c b/drivers/gpu/drm/apple/j613_dcp_activate.c index 6e20a5afc914c..6d0a262069469 100644 --- a/drivers/gpu/drm/apple/j613_dcp_activate.c +++ b/drivers/gpu/drm/apple/j613_dcp_activate.c @@ -40,6 +40,13 @@ bool apple_t8122_25g83_board(void) return ok; } +/* Match the PMGR provider: automatic domains can be clock-gated while active. */ +static bool j613_power_state_active(u32 state) +{ + return FIELD_GET(GENMASK(7, 4), state) == 15 || + ((state & BIT(28)) && FIELD_GET(GENMASK(3, 0), state) == 15); +} + /* These are DT domain nodes, carrying the PMGR offsets, not copied registers. */ static int j613_power_ready(void) { @@ -82,7 +89,7 @@ static int j613_power_ready(void) return -ENOMEM; state = readl(pmgr + offset); iounmap(pmgr); - if (FIELD_GET(GENMASK(7, 4), state) != 15) + if (!j613_power_state_active(state)) return -EBUSY; } return 0; diff --git a/tools/asahi/run-host-tests.py b/tools/asahi/run-host-tests.py index 5c5ca93c68594..00b9d65f68575 100755 --- a/tools/asahi/run-host-tests.py +++ b/tools/asahi/run-host-tests.py @@ -53,6 +53,7 @@ def mutations(flag, names, base, expect=KILL): # test file -> [(arguments, expectation)]. '{out}' is a fresh directory. ASAHI_TESTS = { + 'test-j613-dcp-power.py': [([], PASS)], 'aop-als-channel-tests.py': [([], PASS)], 'mtp-pm-tests.py': [(out(), PASS), (out() + ['--old-transport', '4db3a5a22c43'], PASS)], diff --git a/tools/asahi/test-j613-dcp-power.py b/tools/asahi/test-j613-dcp-power.py new file mode 100644 index 0000000000000..4e93bcc8b1bdb --- /dev/null +++ b/tools/asahi/test-j613-dcp-power.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only OR MIT +"""Compile the production J613 PMGR predicate and test all actual/target/auto states.""" +from pathlib import Path +import os +import subprocess +import tempfile + +root = Path(__file__).resolve().parents[2] +source = (root / "drivers/gpu/drm/apple/j613_dcp_activate.c").read_text() +start = source.index("static bool j613_power_state_active(") +end = source.index("\n}\n", start) + 3 +stub = r""" +#include +#include +#include +typedef uint32_t u32; +#define BIT(n) (1U << (n)) +#define GENMASK(a,b) (((1U << ((a)-(b)+1))-1) << (b)) +#define FIELD_GET(mask,v) (((v) & (mask)) >> __builtin_ctz(mask)) +""" +main = r""" +int main(void) { + for (u32 actual = 0; actual < 16; actual++) + for (u32 target = 0; target < 16; target++) + for (u32 automatic = 0; automatic < 2; automatic++) { + u32 word = (actual << 4) | target | (automatic << 28); + bool expected = actual == 15 || (target == 15 && automatic); + if (j613_power_state_active(word) != expected) return 1; + } + if (!j613_power_state_active(0x1404024f)) return 2; + if (j613_power_state_active(0x0404024f)) return 3; + if (j613_power_state_active(0x14040240)) return 4; + puts("PASS: 512 PMGR combinations and captured-state regression controls"); + return 0; +} +""" +with tempfile.TemporaryDirectory() as temporary: + path = Path(temporary) / "power.c" + path.write_text(stub + source[start:end] + main) + for flags in ([], ["-O2"]): + exe = str(path.with_suffix("")) + subprocess.run([os.environ.get("CC", "cc"), "-Wall", "-Wextra", "-Werror", + *flags, str(path), "-o", exe], check=True) + subprocess.run([exe], check=True) From 31a2f5de22bbcf15668f864183b2c37fec43913b Mon Sep 17 00:00:00 2001 From: Satya Benson Date: Fri, 30 Oct 2026 22:17:26 -0400 Subject: [PATCH 2/3] test: vary unrelated bits in J613 PMGR state words Cross all 512 ACTUAL/TARGET/AUTO combinations with zero, all-set, alternating and individual unrelated-bit patterns. This verifies that readiness ignores every bit outside the three PMGR fields. A temporary ACTUAL mask widened from GENMASK(7, 4) to GENMASK(8, 4) passes the old cases but fails the new ones at both -O0 and -O2. Make the unoptimized compiler flag explicit and report failing words. Signed-off-by: Satya Benson --- tools/asahi/test-j613-dcp-power.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/tools/asahi/test-j613-dcp-power.py b/tools/asahi/test-j613-dcp-power.py index 4e93bcc8b1bdb..3bc1a3379c253 100644 --- a/tools/asahi/test-j613-dcp-power.py +++ b/tools/asahi/test-j613-dcp-power.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: GPL-2.0-only OR MIT -"""Compile the production J613 PMGR predicate and test all actual/target/auto states.""" +"""Compile the production J613 PMGR predicate and test PMGR fields and unrelated bits.""" from pathlib import Path import os import subprocess @@ -21,24 +21,36 @@ """ main = r""" int main(void) { + /* Only ACTUAL[7:4], TARGET[3:0] and AUTO_ENABLE[28] affect readiness. */ + const u32 unrelated_mask = ~(GENMASK(7, 0) | BIT(28)); + u32 unrelated[32] = {0, unrelated_mask, + 0xaaaaaaaaU & unrelated_mask, + 0x55555555U & unrelated_mask}; + unsigned int count = 4; + for (unsigned int bit = 8; bit < 32; bit++) + if (bit != 28) unrelated[count++] = BIT(bit); + for (unsigned int i = 0; i < count; i++) for (u32 actual = 0; actual < 16; actual++) for (u32 target = 0; target < 16; target++) for (u32 automatic = 0; automatic < 2; automatic++) { - u32 word = (actual << 4) | target | (automatic << 28); + u32 word = unrelated[i] | (actual << 4) | target | (automatic << 28); bool expected = actual == 15 || (target == 15 && automatic); - if (j613_power_state_active(word) != expected) return 1; + if (j613_power_state_active(word) != expected) { + fprintf(stderr, "PMGR state 0x%08x: expected active=%d\n", word, expected); + return 1; + } } if (!j613_power_state_active(0x1404024f)) return 2; if (j613_power_state_active(0x0404024f)) return 3; if (j613_power_state_active(0x14040240)) return 4; - puts("PASS: 512 PMGR combinations and captured-state regression controls"); + puts("PASS: 13824 PMGR combinations and captured-state regression controls"); return 0; } """ with tempfile.TemporaryDirectory() as temporary: path = Path(temporary) / "power.c" path.write_text(stub + source[start:end] + main) - for flags in ([], ["-O2"]): + for flags in (["-O0"], ["-O2"]): exe = str(path.with_suffix("")) subprocess.run([os.environ.get("CC", "cc"), "-Wall", "-Wextra", "-Werror", *flags, str(path), "-o", exe], check=True) From 4b07a28daa8348cb232b860874fac0c8bf2513b6 Mon Sep 17 00:00:00 2001 From: Satya Benson Date: Wed, 4 Nov 2026 21:33:34 -0500 Subject: [PATCH 3/3] selftests: apple: accept the explicitly registered PMGR fixture Signed-off-by: Satya Benson --- tools/asahi/run-host-tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/asahi/run-host-tests.py b/tools/asahi/run-host-tests.py index 00b9d65f68575..540858e834a6f 100755 --- a/tools/asahi/run-host-tests.py +++ b/tools/asahi/run-host-tests.py @@ -189,7 +189,7 @@ def jobs(work, conf, dtc): if re.search(r'-tests\.(py|rs|sh)$', p.name) and p.name != Path(__file__).name} known = set(ASAHI_TESTS) | set(ASAHI_RUST) | set(ASAHI_SHELL) missing = sorted(found - known) - gone = sorted(known - found) + gone = sorted(name for name in known if not (ASAHI / name).is_file()) if missing or gone: for name in missing: print(f'not registered in {Path(__file__).name}: tools/asahi/{name}', file=sys.stderr)