Skip to content

drm/apple: Cap desktop 26.x panel EDR at the panel's potential headroom - #986

Merged
RyanTheTide merged 1 commit into
integration/aurorafrom
fix/dcp-backlight-floor
Nov 5, 2026
Merged

RyanTheTide merged 1 commit into
integration/aurorafrom
fix/dcp-backlight-floor

Conversation

@RyanTheTide

@RyanTheTide RyanTheTide commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Problem

On the J616s (M4 Pro, internal panel) in GNOME's BT.2100 mode, stepping the brightness down through the low end with the keyboard made the panel dip darker and come back brighter on each key press, and it kept flickering for a few seconds after the last press. macOS shows nothing like this.

Cause

dcp_edr_prepare() gave the panel EDR headroom peak / SDR white with no limit. At GNOME's lowest level (6 nits), that's a headroom of about 266. Every swap asked for the panel peak and scaled the picture down by the same factor.

  • The ramp was long and stalled. Reaching that headroom took about 120 one-step swaps. A still desktop sends about one swap a second, so the ramp crawled on for seconds. The OSD redraws from each later key press ran another stretch of it.
  • The requested level reversed during each step. The backlight fade lowers SDR white faster than one step per swap raises the headroom. The panel level requested in each swap therefore went down and up within a single key press. The firmware eases the panel level over several frames but applies the EDR scale at once, so each reversal showed as a dip followed by a recovery.

A kprobe on dcp_peer_swap_submit_26_6 on the J616s, reading the swap's brightness, limit, EDR scale and headroom fields, recorded one 35 to 6 nit key step:

nits 1599 1527 1495 1566 1599 1559 1599 1551 1494 1428 1495 1565 ... 948 992 891 932 ... 1599
H    47   49   52   54   55   58   59   62   65   68   71   75  ... 135 142 149 155 ... 266.7  (last step 4.5 s after the write)

Fix

macOS limits the headroom to the panel's potential headroom, which the ADT carries as EDRPotentialHeadroom in the /backlight node. The fix has three parts:

  • Read the potential headroom. It's read with the panel's other ADT properties, through the existing phram ADT reader.
  • Cap the headroom there. Below the SDR white at which the peak would need more, the headroom stays at the cap, and a brightness change moves only the panel level.
  • Hold the panel level during downward fades. Above the cap's SDR white, a swap that lowers SDR white while the headroom is still below its target now raises the headroom just enough to hold the panel level. The level requested never moves against a fade. The macOS one-step-per-swap ramp for EDR content starting is unchanged, and it continues once SDR white settles.

Without the ADT property, the headroom isn't capped, as before. The pure helpers are in dcp-edr.h.

Tests

  • KUnit (apple-dcp-backlight) on a synthetic panel:
    • dcp_edr_ceiling and dcp_edr_target bounds.
    • Fades down and up, with and without a cap, never moving the panel level against the fade.
    • Swaps at a steady level after a fade change nothing.
    • The start ramp still goes one step per swap.
    • The old stepping (uncapped, no hold) fails the fade cases.
  • Host selftest: the desktop-adt-identity fixture gains a /backlight node, including a nested decoy node.
  • Host model of the traced key step:
    • Old code: 24 reversals in the panel level.
    • New code: 0 reversals. It settles in the fade's last frame and sends nothing further at the floor.

Results

  • KUnit apple-dcp-backlight, arm64 qemu, with DRM_APPLE=y, so all of appledrm builds: 25/25 pass.
  • apple-dcp host selftests (run.py): 83/85, and desktop-adt-identity passes. The two failures predate this change, and they're on the integration/aurora tip too. Since drm/apple: fade the panel backlight on DAC firmware #983 (DAC backlight fade), the panel-takeover-registration and desktop-brightness-notifications fixtures lack brightness.fade_work, fading, level and system_freezable_wq. GitHub host-tests CI didn't run on this PR.

Validation on the J616s

Pending. The machine is in use; it needs a kernel install and a reboot.

🤖 Generated with Claude Code

On the J616s in GNOME's BT.2100 mode, stepping the brightness down
through the low end made the panel dip darker and then come back
brighter on every key press, and kept flickering for seconds after the
last one.

dcp_edr_prepare() gave the panel headroom peak / SDR white with no
limit. At the lowest GNOME level (6 nits) that is a headroom of 266:
every swap asked for the panel peak and scaled the picture by 1/266.
Reaching it took about 120 one-step swaps, and a still desktop sends
about one swap a second, so the ramp crawled on for seconds and each OSD
redraw of a later key press ran another stretch of it. Meanwhile the
backlight fade lowers SDR white faster than one step a swap raises the
headroom, so the requested panel level went down and up again within
one key press: traced on the J616s for one 35 to 6 nit step, it went
1599, 1527, 1495, 1566, 1599, 1559, ... 891, and back up to 1599 over
the next 4.5 s. The firmware eases the panel level over several frames
but applies the EDR scale at once, so each reversal shows as a dip and
a recovery.

macOS limits the headroom to the panel's potential headroom, which the
ADT carries as EDRPotentialHeadroom in the /backlight node. Read it with
the panel's other ADT properties and cap the headroom there. Below the
SDR white at which the peak needs more than that, the headroom stays at
the cap, and a brightness change moves only the panel level.

Above it, a swap that lowers SDR white while the headroom is still
below its target now raises the headroom just enough to hold the panel
level, so the requested level never moves against a fade. The macOS
ramp for EDR content starting, one step per swap, is unchanged and
continues once SDR white settles. Without the ADT property, the
headroom is not capped, as before.

The KUnit cases model the swaps of a backlight fade and check that the
panel level never moves against it, with and without a cap, that swaps
at a steady level change nothing, and the start ramp. The host
selftest's ADT fixture gains a /backlight node.

Assisted-by: LLM
Signed-off-by: Ryan Murray <81541646+RyanTheTide@users.noreply.github.com>
@RyanTheTide

Copy link
Copy Markdown
Contributor Author

Merge train (MTL, 06/11). Gates passed on test tree 0412501d (integration/aurora 2661d71 + this batch):

  • Signed-off-by from the author on every commit. Adversarial review done.
  • W=1 on the touched objects: 0 new warnings. KUnit: drm/apple 112/113 (1 skip), hid 19/19, SEP 158/163 (the 4 known apple_sep_host_store failures, profile tests all pass). dt_binding_check clean. apple-dcp host selftests 86/86, run-host-tests at base parity (with Merge train: G17 hang confinement (#928) plus its host-test fixture #989's fixture).
  • Regression boots of 7.3.0-aurora-mtl1106a, compared with the fleet16/fleet17 rows: M2, M1 Pro, J700 and J616s all clean (0 err-level lines, 0 AVCs, displays, Wi-Fi, BT, Touch ID, camera, speakers at minimal volume with speakerguardd, s2idle, perf at parity).
  • drm/apple: Cap desktop 26.x panel EDR at the panel's potential headroom #986 on the J616s: boot logs "panel EDR headroom up to 16.000"; a 35->6 step sampled at 20 ms (twice) shows no reversals in actual_brightness or SMC PDBR, and the panel power stays flat at the floor.

@RyanTheTide
RyanTheTide merged commit 11eccbe into integration/aurora Nov 5, 2026
@RyanTheTide
RyanTheTide deleted the fix/dcp-backlight-floor branch November 5, 2026 17:35
@RyanTheTide

Copy link
Copy Markdown
Contributor Author

Validation on the J616s (M4 Pro), GNOME 51 in BT.2100 mode

A kprobe on dcp_peer_swap_submit_26_6 recorded each swap's panel brightness, limit, EDR scale and headroom. A uinput device pressed the brightness keys, so GNOME made each change the same way it does from the keyboard. The script ran 25 presses down past the minimum, then 25 up.

7.3.0 at 2661d71 (before this PR) 7.3.0 at 33bec40 (with this PR)
Step down to the minimum (35 → 6 nits) panel level reverses 14 times monotonic
Presses past the minimum (no new level from GNOME) each press raises the panel level one ramp step nothing sent
After the last press ramp runs 1.6 s, then stalls short of its target settles 0.26 s after the press (end of the fade)
Full ladder 600 → 6 → 600 monotonic, apart from ±1 nit rounding at the panel peak (also present before)

SELinux enforcing with 0 AVCs and 0 failed units. Boot warnings match the base, apart from two that don't come from this change: the AVD firmware (the one-shot test kit didn't ship it) and an intermittent PCIe link message that also appears on base boots.

🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant