From bc4e099caa85e8d4c3555851a0feb07058c4a1fe Mon Sep 17 00:00:00 2001 From: Ray Wu Date: Fri, 18 Sep 2026 11:05:46 +0800 Subject: [PATCH] drm/amd/display: map PWM brightness through custom backlight curve [Why/How] On the PWM path, convert the userspace brightness to an input signal and derive the target luminance from the custom backlight curve, then pass the resulting millipercent to the power module. This keeps PWM programming and backlight curve mapping in the power module, aligning the Linux path with the Windows behavior. Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/5723 Fixes: 3c108046e1d6 ("drm/amd/display: Add power module on Linux") Assisted-by: Cursor:Claude-Opus-4.8 Signed-off-by: Ray Wu Signed-off-by: Fangzhi Zuo Reviewed-by: ChiaHsuan (Tom) Chung Tested-by: Dan Wheeler Signed-off-by: Alex Deucher --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 2 + .../display/amdgpu_dm/amdgpu_dm_backlight.c | 92 ++++++++++++++++--- 2 files changed, 81 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 59f04a317665..de4af9adc633 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -449,6 +449,8 @@ VISIBLE_IF_KUNIT int amdgpu_dm_init_power_module(struct amdgpu_display_manager * init_data[i].use_custom_backlight_caps = false; init_data[i].custom_backlight_caps_config_no = 0; init_data[i].use_linear_backlight_curve = false; + if (amdgpu_dc_debug_mask & DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE) + init_data[i].use_linear_backlight_curve = true; init_data[i].def_varibright_enable = 0; init_data[i].varibright_level = 0; /* 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 bf89c619e12f..d0465bde2d0a 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 @@ -39,6 +39,7 @@ #include "modules/inc/mod_power.h" #include +#include #include #include #include @@ -127,6 +128,68 @@ static inline u32 scale_fw_to_input(int min, int max, u64 input) return min + DIV_ROUND_CLOSEST_ULL(input * (max - min), AMDGPU_MAX_BL_LEVEL); } +static u32 backlight_signal_to_luminance(const struct amdgpu_dm_backlight_caps *caps, + u32 signal) +{ + u32 lower_signal, upper_signal; + u32 lower_lum, upper_lum; + int i; + + if (!caps->data_points) + return DIV_ROUND_CLOSEST(signal * 100, AMDGPU_MAX_BL_LEVEL); + + if (signal < caps->luminance_data[0].input_signal) { + lower_signal = 0; + lower_lum = 0; + upper_signal = caps->luminance_data[0].input_signal; + upper_lum = caps->luminance_data[0].luminance; + } else if (signal >= + caps->luminance_data[caps->data_points - 1].input_signal) { + lower_signal = caps->luminance_data[caps->data_points - 1].input_signal; + lower_lum = caps->luminance_data[caps->data_points - 1].luminance; + upper_signal = caps->max_input_signal; + upper_lum = 100; + } else { + for (i = 1; + i < caps->data_points && + signal > caps->luminance_data[i].input_signal; + i++) + ; + + lower_signal = caps->luminance_data[i - 1].input_signal; + upper_signal = caps->luminance_data[i].input_signal; + lower_lum = caps->luminance_data[i - 1].luminance; + upper_lum = caps->luminance_data[i].luminance; + } + + if (upper_signal == lower_signal) + return upper_lum; + + return lower_lum + DIV_ROUND_CLOSEST((upper_lum - lower_lum) * + (signal - lower_signal), + upper_signal - lower_signal); +} + +static u32 convert_brightness_to_millipercent(const struct amdgpu_dm_backlight_caps *caps, + u32 user_brightness) +{ + unsigned int min, max; + u32 signal, luminance; + + if (!get_brightness_range(caps, &min, &max)) + return DIV_ROUND_CLOSEST_ULL((u64)user_brightness * 100000, + MAX_BACKLIGHT_LEVEL); + + user_brightness = clamp(user_brightness, min, max); + if (amdgpu_dc_debug_mask & DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE) + return DIV_ROUND_CLOSEST_ULL((u64)user_brightness * 100000, max); + + signal = scale_input_to_fw(max, user_brightness); + luminance = backlight_signal_to_luminance(caps, signal); + + return min_t(u32, luminance * 1000, 100000); +} + VISIBLE_IF_KUNIT void convert_custom_brightness(const struct amdgpu_dm_backlight_caps *caps, unsigned int min, unsigned int max, @@ -267,10 +330,10 @@ void amdgpu_dm_backlight_set_level(struct amdgpu_display_manager *dm, struct amdgpu_dm_backlight_caps *caps; struct dc_link *link; u32 brightness = 0; + u32 millipercent = 0; bool rc = false, reallow_idle = false; struct drm_connector *connector; struct dc_stream_state *stream; - unsigned int min, max; list_for_each_entry(connector, &dm->ddev->mode_config.connector_list, head) { struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); @@ -293,12 +356,21 @@ void amdgpu_dm_backlight_set_level(struct amdgpu_display_manager *dm, /* update scratch register */ if (bl_idx == 0) amdgpu_atombios_scratch_regs_set_backlight_level(dm->adev, dm->brightness[bl_idx]); - brightness = convert_brightness_from_user(caps, dm->brightness[bl_idx]); - link = (struct dc_link *)dm->backlight_link[bl_idx]; - /* Apply brightness quirk */ - if (caps->brightness_mask) - brightness |= caps->brightness_mask; + link = (struct dc_link *)dm->backlight_link[bl_idx]; + stream = dm_find_stream_with_link(dm, link); + if (!stream) + return; + + if (caps->aux_support) { + brightness = convert_brightness_from_user(caps, dm->brightness[bl_idx]); + /* Apply brightness quirk */ + if (caps->brightness_mask) + brightness |= caps->brightness_mask; + } else { + millipercent = convert_brightness_to_millipercent(caps, + user_brightness); + } if (trace_amdgpu_dm_brightness_enabled()) { trace_amdgpu_dm_brightness(__builtin_return_address(0), @@ -308,10 +380,6 @@ void amdgpu_dm_backlight_set_level(struct amdgpu_display_manager *dm, power_supply_is_system_supplied() > 0); } - stream = dm_find_stream_with_link(dm, link); - if (!stream) - return; - mutex_lock(&dm->dc_lock); if (dm->dc->caps.ips_support && dm->dc->ctx->dmub_srv->idle_allowed) { dc_allow_idle_optimizations(dm->dc, false); @@ -323,10 +391,8 @@ void amdgpu_dm_backlight_set_level(struct amdgpu_display_manager *dm, AUX_BL_DEFAULT_TRANSITION_TIME_MS, false, true); } else { /* power module uses millipercent */ - get_brightness_range(caps, &min, &max); - brightness = DIV_ROUND_CLOSEST(brightness * 100, (max - min)) * 1000; rc = mod_power_set_backlight_percent(dm->power_module, stream, - brightness, 0, false); + millipercent, 0, false); } /*