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 v3 1/2] drm/amd/display: keep custom brightness curve in userspace domain
Date: Sat, 24 Oct 2026 01:33:38 +0000 [thread overview]
Message-ID: <ba2699e51bc48bc673e3bac85a6788734d61cc92.1792805565.git.arc@empyreal.works> (raw)
In-Reply-To: <cover.1792805565.git.arc@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. AUX panels with a
custom curve lose their lowest levels the same way.
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.
Since commit "drm/amd/display: map PWM brightness through custom
backlight curve", PWM panels no longer reach this helper. AUX panels
still do through convert_brightness_from_user(), and stable kernels
predating the power module still use it 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
Assisted-by: LLM
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 27546febf..cd8be50bb 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
@@ -122,10 +122,10 @@ 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 u32 backlight_signal_to_luminance(const struct amdgpu_dm_backlight_caps *caps,
@@ -192,8 +192,7 @@ static u32 convert_brightness_to_millipercent(const struct amdgpu_dm_backlight_c
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;
@@ -251,7 +250,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));
}
@@ -266,7 +265,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 9ad888d3a..32ba2f8ef 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
@@ -68,8 +68,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 5221050de..5ff074497 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
@@ -1054,7 +1054,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);
@@ -1078,7 +1078,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);
@@ -1121,14 +1121,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);
@@ -1167,13 +1167,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);
@@ -1211,7 +1211,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
@@ -1254,7 +1254,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);
@@ -1292,7 +1292,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);
@@ -1330,7 +1330,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);
--
2.56.0
next prev parent reply other threads:[~2026-10-24 1:33 UTC|newest]
Thread overview: 10+ 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 ` [PATCH 1/4] drm/amd/display: keep custom brightness curve in userspace domain Andrei Rusu de Castro
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:33 ` [PATCH 4/4] drm/amd/display: test power module brightness input domain Andrei Rusu de Castro
[not found] ` <cover.1788388049.git.arc@empyreal.works>
[not found] ` <8ec5dc8922407924f771d353cb7f2dd1846553ae.1788388049.git.arc@empyreal.works>
2026-10-12 20:06 ` [PATCH v2 1/4] drm/amd/display: keep custom brightness curve in userspace domain Mario Limonciello
2026-10-24 1:33 ` Andrei Rusu de Castro
2026-10-24 1:33 ` [PATCH v3 0/2] " Andrei Rusu de Castro
2026-10-24 1:33 ` Andrei Rusu de Castro [this message]
2026-10-24 1:33 ` [PATCH v3 2/2] drm/amd/display: test custom brightness with non-zero minimum 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=ba2699e51bc48bc673e3bac85a6788734d61cc92.1792805565.git.arc@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
all inboxes | Powered by JetHome®