From: Andrei Rusu de Castro <arc@empyreal.works>
To: amd-gfx@lists.freedesktop.org
Cc: harry.wentland@amd.com, sunpeng.li@amd.com, siqueira@igalia.com,
alexander.deucher@amd.com, christian.koenig@amd.com,
airlied@gmail.com, simona@ffwll.ch, alex.hung@amd.com,
roman.li@amd.com, mario.limonciello@amd.com,
dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org,
chen-yu.chen@amd.com, ray.wu@amd.com, stable@vger.kernel.org
Subject: [PATCH 1/4] drm/amd/display: keep custom brightness curve in userspace domain
Date: Wed, 02 Sep 2026 12:31:17 +0000 [thread overview]
Message-ID: <20260902-brightness-1-fd468d7a@empyreal.works> (raw)
In-Reply-To: <20260902-brightness-cover-abf809f2@empyreal.works>
convert_brightness_from_user() expects convert_custom_brightness() to
reshape a value in the userspace [0..max] domain before the caller maps
it once into the firmware [min..max] domain.
Commit 8dbd72cb7900 ("drm/amd/display: Export full brightness range to
userspace") instead made convert_custom_brightness() call
scale_fw_to_input(min, max, ...). That helper adds min and scales by the
firmware span, entering the firmware domain before the caller applies
the same span and minimum again.
With the default PWM range, min is 3084 and max is 65535. A zero request
through any custom curve consequently returns 6023 instead of 3084, so
the darkest 2939 firmware levels are unreachable.
Keep the curve result in [0..max]. scale_fw_to_input() then becomes the
inverse of scale_input_to_fw(), and the caller remains the sole owner of
the userspace-to-firmware conversion. The linear path is unchanged.
A later patch removes this helper from the PWM power-module path because
the power module owns that curve. AUX panels continue to use it, and
stable kernels predating the power-module refactor still require this
correction on PWM panels.
Commit 6fd83a1c2cde ("drm/amd/display: Scale custom brightness curve from
full range") fixed the corresponding input-side domain error. This
completes the output side of the same conversion.
Source and arithmetic analysis identified the duplicate
domain conversion. KUnit coverage for a non-zero firmware minimum is
added separately.
Fixes: 8dbd72cb7900 ("drm/amd/display: Export full brightness range to userspace")
Cc: stable@vger.kernel.org # 6.17.x: 6fd83a1c2cde: drm/amd/display: Scale custom brightness curve from full range
Cc: stable@vger.kernel.org # 6.17.x
Cc: stable@vger.kernel.org # before 7.3 this code is in amdgpu_dm.c; drop the header and test hunks
Signed-off-by: Andrei Rusu de Castro <arc@empyreal.works>
---
.../display/amdgpu_dm/amdgpu_dm_backlight.c | 13 ++++----
.../display/amdgpu_dm/amdgpu_dm_backlight.h | 3 +-
.../tests/amdgpu_dm_backlight_test.c | 30 +++++++++----------
3 files changed, 22 insertions(+), 24 deletions(-)
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 e61bbc310f33..424b33573a73 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
@@ -112,16 +112,15 @@ static inline u32 scale_input_to_fw(int max, u64 input)
return DIV_ROUND_CLOSEST_ULL(input * AMDGPU_MAX_BL_LEVEL, max);
}
-/* Rescale from [0..AMDGPU_MAX_BL_LEVEL] to [min..max] */
-static inline u32 scale_fw_to_input(int min, int max, u64 input)
+/* Rescale the firmware curve's [0..AMDGPU_MAX_BL_LEVEL] back to userspace [0..max]. */
+static inline u32 scale_fw_to_input(int max, u64 input)
{
- return min + DIV_ROUND_CLOSEST_ULL(input * (max - min), AMDGPU_MAX_BL_LEVEL);
+ return DIV_ROUND_CLOSEST_ULL(input * max, AMDGPU_MAX_BL_LEVEL);
}
STATIC_IFN_KUNIT
void convert_custom_brightness(const struct amdgpu_dm_backlight_caps *caps,
- unsigned int min, unsigned int max,
- uint32_t *user_brightness)
+ unsigned int max, uint32_t *user_brightness)
{
u32 brightness = scale_input_to_fw(max, *user_brightness);
u8 lower_signal, upper_signal, upper_lum, lower_lum, lum;
@@ -179,7 +178,7 @@ void convert_custom_brightness(const struct amdgpu_dm_backlight_caps *caps,
(brightness - lower_signal),
upper_signal - lower_signal);
scale:
- *user_brightness = scale_fw_to_input(min, max,
+ *user_brightness = scale_fw_to_input(max,
DIV_ROUND_CLOSEST(lum * brightness, 101));
}
@@ -194,7 +193,7 @@ u32 convert_brightness_from_user(const struct amdgpu_dm_backlight_caps *caps,
if (!get_brightness_range(caps, &min, &max))
return brightness;
- convert_custom_brightness(caps, min, max, &brightness);
+ convert_custom_brightness(caps, max, &brightness);
/* Rescale 0..max to min..max */
return min + DIV_ROUND_CLOSEST_ULL((u64)(max - min) * brightness, max);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.h
index 07b75064847c..90bed0ea5d00 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.h
@@ -67,8 +67,7 @@ ssize_t panel_power_savings_store(struct device *device,
int get_brightness_range(const struct amdgpu_dm_backlight_caps *caps,
unsigned int *min, unsigned int *max);
void convert_custom_brightness(const struct amdgpu_dm_backlight_caps *caps,
- unsigned int min, unsigned int max,
- uint32_t *user_brightness);
+ unsigned int max, uint32_t *user_brightness);
u32 convert_brightness_from_user(const struct amdgpu_dm_backlight_caps *caps,
uint32_t brightness);
u32 convert_brightness_to_user(const struct amdgpu_dm_backlight_caps *caps,
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_backlight_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_backlight_test.c
index 7ca17f803f9d..1fb171fdbc3c 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_backlight_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_backlight_test.c
@@ -1033,7 +1033,7 @@ static void dm_test_custom_brightness_no_data_points(struct kunit *test)
caps.data_points = 0;
- convert_custom_brightness(&caps, 3084, 65535, &brightness);
+ convert_custom_brightness(&caps, 65535, &brightness);
/* No data points → no-op */
KUNIT_EXPECT_EQ(test, brightness, saved);
@@ -1057,7 +1057,7 @@ static void dm_test_custom_brightness_debug_mask_disables(struct kunit *test)
/* Set the disable flag */
amdgpu_dm_set_dc_debug_mask(amdgpu_dm_get_dc_debug_mask() | DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE);
- convert_custom_brightness(&caps, 3084, 65535, &brightness);
+ convert_custom_brightness(&caps, 65535, &brightness);
/* Should be no-op due to debug mask */
KUNIT_EXPECT_EQ(test, brightness, saved);
@@ -1100,14 +1100,14 @@ static void dm_test_custom_brightness_exact_match(struct kunit *test)
*/
brightness = 32896;
- convert_custom_brightness(&caps, min, max, &brightness);
+ convert_custom_brightness(&caps, max, &brightness);
/*
* Exact match: lum=50, brightness_scaled=128
- * result = scale_fw_to_input(min, max, DIV_ROUND_CLOSEST(50*128, 101))
- * = scale_fw_to_input(0, 65535, DIV_ROUND_CLOSEST(6400, 101))
- * = scale_fw_to_input(0, 65535, 63)
- * = 0 + DIV_ROUND_CLOSEST(63 * 65535, 255) = 16191 (approx)
+ * result = scale_fw_to_input(max, DIV_ROUND_CLOSEST(50*128, 101))
+ * = scale_fw_to_input(65535, DIV_ROUND_CLOSEST(6400, 101))
+ * = scale_fw_to_input(65535, 63)
+ * = DIV_ROUND_CLOSEST(63 * 65535, 255) = 16191 (approx)
*/
KUNIT_EXPECT_TRUE(test, brightness != 32896);
KUNIT_EXPECT_TRUE(test, brightness < 32896);
@@ -1146,13 +1146,13 @@ static void dm_test_custom_brightness_below_first(struct kunit *test)
*/
brightness = 12850;
- convert_custom_brightness(&caps, min, max, &brightness);
+ convert_custom_brightness(&caps, max, &brightness);
/*
* Below first data point: lum = DIV_ROUND_CLOSEST(40 * 50, 100) = 20
- * Then: scale_fw_to_input(0, 65535, DIV_ROUND_CLOSEST(20 * 50, 101))
- * = scale_fw_to_input(0, 65535, DIV_ROUND_CLOSEST(1000, 101))
- * = scale_fw_to_input(0, 65535, 10)
+ * Then: scale_fw_to_input(65535, DIV_ROUND_CLOSEST(20 * 50, 101))
+ * = scale_fw_to_input(65535, DIV_ROUND_CLOSEST(1000, 101))
+ * = scale_fw_to_input(65535, 10)
* The output should be significantly less than input.
*/
KUNIT_EXPECT_TRUE(test, brightness < 12850);
@@ -1190,7 +1190,7 @@ static void dm_test_custom_brightness_interpolation(struct kunit *test)
*/
brightness = 32125;
- convert_custom_brightness(&caps, min, max, &brightness);
+ convert_custom_brightness(&caps, max, &brightness);
/*
* The function should interpolate between data points and produce
@@ -1233,7 +1233,7 @@ static void dm_test_custom_brightness_above_last(struct kunit *test)
*/
brightness = 56533;
- convert_custom_brightness(&caps, min, max, &brightness);
+ convert_custom_brightness(&caps, max, &brightness);
/* Output should differ from input (remapped via curve) */
KUNIT_EXPECT_TRUE(test, brightness != 56533);
@@ -1271,7 +1271,7 @@ static void dm_test_custom_brightness_single_data_point(struct kunit *test)
*/
brightness = 16448;
- convert_custom_brightness(&caps, min, max, &brightness);
+ convert_custom_brightness(&caps, max, &brightness);
KUNIT_EXPECT_TRUE(test, brightness < 16448);
@@ -1309,7 +1309,7 @@ static void dm_test_custom_brightness_lower_lum_zero(struct kunit *test)
*/
brightness = 32125;
- convert_custom_brightness(&caps, min, max, &brightness);
+ convert_custom_brightness(&caps, max, &brightness);
/* Should remap; result should differ from input */
KUNIT_EXPECT_TRUE(test, brightness != 32125);
next prev parent reply other threads:[~2026-09-03 7:34 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-02 12:31 [PATCH 0/4] drm/amd/display: fix brightness ownership through power module Andrei Rusu de Castro
2026-09-02 12:31 ` Andrei Rusu de Castro [this message]
2026-09-02 12:31 ` [PATCH 2/4] drm/amd/display: test custom brightness with non-zero minimum Andrei Rusu de Castro
2026-09-02 12:32 ` [PATCH 3/4] drm/amd/display: pass userspace brightness to power module Andrei Rusu de Castro
2026-09-02 12:57 ` sashiko-bot
2026-09-02 12:33 ` [PATCH 4/4] drm/amd/display: test power module brightness input domain Andrei Rusu de Castro
2026-09-02 22:31 ` [PATCH v2 0/4] drm/amd/display: fix brightness ownership through power module Andrei Rusu de Castro
2026-09-02 22:31 ` [PATCH v2 1/4] drm/amd/display: keep custom brightness curve in userspace domain Andrei Rusu de Castro
2026-09-02 22:31 ` [PATCH v2 2/4] drm/amd/display: test custom brightness with non-zero minimum Andrei Rusu de Castro
2026-09-02 22:31 ` [PATCH v2 3/4] drm/amd/display: pass userspace brightness to power module Andrei Rusu de Castro
2026-09-03 7:50 ` sashiko-bot
2026-09-02 22:32 ` [PATCH v2 4/4] drm/amd/display: test power module brightness input domain Andrei Rusu de Castro
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260902-brightness-1-fd468d7a@empyreal.works \
--to=arc@empyreal.works \
--cc=airlied@gmail.com \
--cc=alex.hung@amd.com \
--cc=alexander.deucher@amd.com \
--cc=amd-gfx@lists.freedesktop.org \
--cc=chen-yu.chen@amd.com \
--cc=christian.koenig@amd.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=harry.wentland@amd.com \
--cc=linux-kernel@vger.kernel.org \
--cc=mario.limonciello@amd.com \
--cc=ray.wu@amd.com \
--cc=roman.li@amd.com \
--cc=simona@ffwll.ch \
--cc=siqueira@igalia.com \
--cc=stable@vger.kernel.org \
--cc=sunpeng.li@amd.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox