[PATCH RFC 1/3] drm/amd/display: Repair or drop a bad ATIF backlight curve
From: Hugo Sales
Date: Wed Nov 04 2026 - 05:35:00 EST
The custom brightness curve code expects the input signals and
luminances of the ATIF curve to increase monotonically. They come from
firmware as u8 values, and nothing checks them
Check the curve once, in amdgpu_dm_validate_backlight_caps(), next to
the signal range check. If a signal falls, drop the curve and map
linearly. Cap a luminance over 100% at 100%, and raise a luminance that
falls back to the one before it. When the check changes the curve, warn
about a firmware bug
The curve code only caps its result at 100%. A point over 100% then
makes the curve reach full brightness before that point and stay flat up
to it. With the point capped, the curve reaches full brightness at the
point itself
Dropping the curve also changes ABM. The power module rejected a curve
whose signals fall and built its default quadratic LUT. With no points
it builds a linear LUT over the firmware's signal range and uses the
firmware's AC and DC levels, as it does when firmware reports no curve.
The power module still rejects a capped or raised luminance, as before
Signed-off-by: Hugo Sales <hugo@xxxxxxx>
---
The data_points clamp duplicates "drm/amdgpu: Clamp the ATIF backlight
curve to its 99 points", which fixes the overflow where the ATIF reply
is read:
https://lore.kernel.org/r/20261031153948.127016-1-hugo@xxxxxxx
This check stays correct whichever of the two lands first
The check keeps a repeated signal, which the mapping steps over as
before. It sits in amdgpu_dm rather than in
amdgpu_atif_query_backlight_caps() so KUnit on UML, which has no ACPI,
can reach it
---
.../amd/display/amdgpu_dm/amdgpu_dm_backlight.c | 44 ++++++++++++++++++++++
1 file changed, 44 insertions(+)
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c
index 358ba0c73d795..3a27fb956627a 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c
@@ -52,6 +52,48 @@
#include "dm_helpers.h"
#if defined(CONFIG_ACPI) || IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
+/*
+ * The brightness mapping needs signals that never fall from point to point
+ * and luminance that never falls or passes 100%. Drop a curve whose
+ * signals fall, and flatten the luminance of the others
+ */
+static void backlight_validate_custom_curve(struct amdgpu_display_manager *dm,
+ struct amdgpu_dm_backlight_caps *caps)
+{
+ struct amdgpu_dm_luminance_data *p = caps->luminance_data;
+ int over = 0, falling = 0;
+ int i;
+
+ if (caps->data_points > ARRAY_SIZE(caps->luminance_data))
+ caps->data_points = ARRAY_SIZE(caps->luminance_data);
+
+ for (i = 1; i < caps->data_points; i++) {
+ if (p[i].input_signal < p[i - 1].input_signal) {
+ drm_warn(adev_to_drm(dm->adev), FW_BUG
+ "ATIF: backlight curve signal %u at point %d is below %u, using a linear curve\n",
+ p[i].input_signal, i, p[i - 1].input_signal);
+ caps->data_points = 0;
+ return;
+ }
+ }
+
+ for (i = 0; i < caps->data_points; i++) {
+ if (p[i].luminance > 100) {
+ p[i].luminance = 100;
+ over++;
+ }
+ if (i && p[i].luminance < p[i - 1].luminance) {
+ p[i].luminance = p[i - 1].luminance;
+ falling++;
+ }
+ }
+
+ if (over || falling)
+ drm_warn(adev_to_drm(dm->adev), FW_BUG
+ "ATIF: backlight curve has %d points over 100%%, set to 100%%, and %d falling points, raised to the point before\n",
+ over, falling);
+}
+
STATIC_IFN_KUNIT
void amdgpu_dm_validate_backlight_caps(struct amdgpu_display_manager *dm, int bl_idx)
{
@@ -66,6 +108,8 @@ void amdgpu_dm_validate_backlight_caps(struct amdgpu_display_manager *dm, int bl
caps->min_input_signal, caps->max_input_signal);
caps->caps_valid = false;
}
+
+ backlight_validate_custom_curve(dm, caps);
}
EXPORT_IF_KUNIT(amdgpu_dm_validate_backlight_caps);
#endif
--
2.56.0