https://bugs.kde.org/show_bug.cgi?id=523341
Bug ID: 523341
Summary: Backlight helper applies brightness on the wrong
device scale when spawned while the panel connector is
disabled (hybrid GPU, two backlight devices)
Classification: Plasma
Product: plasmashell
Version First 6.6.5
Reported In:
Platform: Ubuntu
OS: Linux
Status: REPORTED
Severity: normal
Priority: NOR
Component: Power management & brightness
Assignee: [email protected]
Reporter: [email protected]
Target Milestone: 1.0
DESCRIPTION
On a hybrid-GPU laptop (Intel Arrow Lake iGPU driving the internal panel via
i915, NVIDIA RTX 5070 Ti Mobile with driver 595.71.05 open) the kernel exposes
two backlight devices for the same internal panel:
- intel_backlight, type raw, max_brightness 496 (the real one; parent is the
i915 eDP connector)
- nvidia_0, type raw, max_brightness 100 (registered by the NVIDIA driver even
though the panel is muxed to the iGPU; its parent is the dGPU PCI device)
Every time the screen turns off on idle timeout and is woken by user input, the
panel comes back roughly 5x brighter than its set value, while the brightness
slider/OSD still shows the old (correct) value. Clicking the slider re-applies
the correct brightness.
I traced writes to /sys/class/backlight/*/brightness with a bpftrace kprobe on
kernfs_fop_write_iter during a live occurrence. The writer is powerdevil's
KAuth helper (backlighthelper), writing BOTH devices in lockstep.
ROOT CAUSE (daemon/controllers/backlighthelper_linux.cpp, Plasma/6.6 branch,
still present on current master including commit 90bebf783d "controllers:
remove brightness animations"):
1. The helper is DBus-activated per request, and each fresh helper process
re-enumerates /sys/class/backlight in its constructor.
2. The undim restore on wake-from-idle reaches the helper while the eDP DRM
connector is still disabled. In getBacklightTypeDevices(), the check of
<device>/device/enabled therefore leaves rawEnabled empty, and the code
falls through to the rawAll list.
3. rawAll is sorted with QDir::Name | QDir::Reversed (the comment says this is
to prioritize acpi_video1 over acpi_video0). Reverse-alphabetical order
puts nvidia_0 before intel_backlight, so m_devices.constFirst() is now
nvidia_0 with max 100. Note the client side of powerdevil detected the
devices earlier (while the connector was enabled), so it holds
intel_backlight (max 496) as the reference scale and sends raw values on
that scale.
4. writeBrightness() rescales the incoming value from the FIRST device's max
onto every device: new = value * device_max / first_max. With first_max
now 100 instead of 496, the panel's correct raw target (e.g. 21, which is
4% of 496) is reinterpreted as "21 percent": intel_backlight is written
21 * 496 / 100 = 104 (about 5x too bright) and nvidia_0 is written 21.
5. The client-side BacklightBrightness sees the sysfs change, classifies it as
"External brightness change observed", and keeps its own tracked value.
The slider therefore does not move, and clicking it re-applies the correct
brightness through a helper instance that (with the panel now enabled)
enumerates intel_backlight first again.
Two independently captured occurrences match this arithmetic exactly:
set 3% (raw 16) woke to 79/496 + 16/100; set 4% (raw 21) woke to 104/496 +
21/100 (both = the raw value as a percentage).
EVIDENCE
bpftrace (kprobe:kernfs_fop_write_iter, filtered to files named "brightness")
during the wake restore; note the same short-lived helper pid writing both
devices alternately:
23:32:50 backlighthelper(pid 18610) wrote intel_backlight/brightness
23:32:50 backlighthelper(pid 18610) wrote nvidia_0/brightness
23:32:50 backlighthelper(pid 18610) wrote intel_backlight/brightness
23:32:50 backlighthelper(pid 18610) wrote nvidia_0/brightness
[...repeats through the animation ramp...]
Sysfs state sampled every 2s (intel=brightness/actual_brightness on 0..496,
nvidia on 0..100, dbus = powerdevil's tracked value on 0..10000):
23:31:35 dimmed intel=7/7 nvidia=16 dbus_internal=400
23:32:35 screens off intel=7/0 nvidia=16 dbus_internal=400
23:32:50 woke by mouse intel=104/104 nvidia=21 dbus_internal=400 <- bug
23:33:29 slider click intel=21/21 nvidia=21 dbus_internal=400 <- fixed
journalctl --user -u plasma-powerdevil with org.kde.powerdevil.debug=true at
an earlier occurrence of the same bug (set 3%, raw 16):
org.kde.powerdevil: DimDisplay: restoring brightness on wake-up from idle
org.kde.powerdevil: Set screen brightness dimming ratio with ID "DimDisplay"
to 1
org.kde.powerdevil: [BacklightBrightness]: Udev device changed brightness to:
29 for ".../card2-eDP-1/intel_backlight"
org.kde.powerdevil: [BacklightBrightness]: External brightness change
observed: 29 / 496
[...ramp continues: 34, 39, 44, ... 79 = 16% of 496...]
STEPS TO REPRODUCE
1. Hybrid-GPU laptop where the internal panel is driven by the iGPU and the
NVIDIA driver additionally registers nvidia_0 (or any second raw backlight
device with a different max_brightness).
2. Set panel brightness to a low value, e.g. 3%.
3. Let the machine idle until the screen dims and then turns off
(for fast repro set [AC][Display] DimDisplayIdleTimeoutSec=60 and
TurnOffDisplayIdleTimeoutSec=120 in powerdevilrc).
4. Wake with mouse/keyboard.
OBSERVED RESULT
Panel lights up at (raw_value) percent instead of the set brightness, about 5x
too bright in my case. Slider still shows the set value.
EXPECTED RESULT
Panel restores the set brightness.
SOFTWARE/OS VERSIONS
Operating System: Ubuntu 26.04 LTS
KDE Plasma Version: 6.6.5
KDE Frameworks Version: 6.24.0
Qt Version: 6.10.2
Kernel Version: 7.0.0-28-generic (64-bit)
Graphics Platform: Wayland
GPUs: Intel Arrow Lake-S [8086:7d67] (i915) + NVIDIA GB205M RTX 5070 Ti Mobile
[10de:2f58] (nvidia 595.71.05 open kernel module)
ADDITIONAL INFORMATION
- The bug requires the helper to spawn while the panel connector is disabled;
that is exactly the state during the wake/undim restore, so it hits on
every single idle wake on affected hardware.
- Any brightness write in that window (not just undim) takes the same path.
- Relationship to existing reports (checked before filing, not duplicates):
- Bug 399646 (multi-GPU wrong backlight device, CONFIRMED): umbrella bug
about device selection. This report is a specific, line-level defect in
the "write to all raw devices with rescaling" approach added for that
bug around 5.25: the reference scale is re-derived per helper spawn and
flips when the panel connector is disabled. Keys/slider work fine here;
the corruption happens only on writes issued while the connector is off.
- Bug 510294 (panel brighter than the slider indicates after sleep/lock,
touching the slider fixes it): identical user-visible symptom, no root
cause identified there. Possibly the same underlying defect if those
machines also expose multiple raw backlight devices with different
max_brightness; the analysis here may explain it.
- Bug 461479 (backlight briefly flashes to full on wake with dim + screen
off configured, RESOLVED in ~6.4): same trigger combination but a
transient flash; this bug produces a persistent wrong level equal to
raw_value percent, still present in 6.6.5.
- Bug 522171 (DDC value not reapplied to external monitor after wake) and
bug 513809 (stale dimming multiplier on secondary monitors after sleep):
different mechanisms (DDC restore / dim state), not the raw-scale
mix-up described here.
- Suggested fixes: pin the device order/reference scale in the client (pass
the expected device or its max along with the value), or have the helper
remember/receive the reference device instead of re-deriving it per spawn,
or drop the "enabled" filter dependence for scale selection.
- Local workaround that fully fixes it: shadow nvidia_0's sysfs directory
with an empty read-only tmpfs mount so only one backlight device is
visible.
--
You are receiving this mail because:
You are watching all bug changes.