Since commit d422a66847a9 ("drm/amd/display: map PWM brightness through
custom backlight curve"), convert_brightness_to_millipercent() rounds the
16-bit user level to an 8-bit signal and the luminance to a whole
percent, so the power module gets at most 101 levelsInterpolate on the unrounded user level and round once, to millipercent. The curve and its segments stay the same, and a result over 100% is still capped Signed-off-by: Hugo Sales <[email protected]> --- On the GV301QH curve, minimum 12 and 49 points, the 62452 levels from user_min to user_max gave 99 different millipercent values and now give 62112 The curve's first point is 2% at signal 14, and below it the curve is a line from 0, so the minimum, signal 12, should get 2% * 12 / 14, which is 1714 millipercent. It now gets exactly that instead of 2000, and every other level also gets the curve's value instead of the nearest whole percent Since patches 2 and 3 the power module's LUT keeps those steps. On the same curve the levels reach the panel as 57821 different PWM values instead of 99 backlight_custom_curve_scale() maps 0..user_max to 0..output_max, with the curve reaching 100% at signal_max. The two weights of the interpolation can't go negative. A luminance is a u8, so before the cap the result is at most 2.55 times output_max. The divisor can pass 32 bits, hence mul_u64_add_u64_div_u64() With no data points the mapping stays linear. Above the last point the curve still runs to 100% at max_input_signal, and points above max_input_signal are still interpolated as before. Past a last point above max_input_signal the curve runs on to 100% at signal 255. Before, the divisor wrapped there and the level stayed near that point's luminance. The top level still gives 100% when a point at max_input_signal claims less KUnit: the PWM tests from patch 5 fail without this patch and pass with it --- .../amd/display/amdgpu_dm/amdgpu_dm_backlight.c | 79 ++++++++++++---------- 1 file changed, 42 insertions(+), 37 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 3b59699e74287..900c17e0285c9 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 <linux/backlight.h> +#include <linux/math64.h> #include <linux/minmax.h> #include <linux/power_supply.h> #include <drm/drm_edid.h> @@ -128,53 +129,59 @@ static inline u32 scale_fw_to_input(int user_max, u64 input) return DIV_ROUND_CLOSEST_ULL(input * user_max, AMDGPU_MAX_BL_LEVEL); } -static u32 backlight_signal_to_luminance(const struct amdgpu_dm_backlight_caps *caps, - u32 signal) +/* + * Map user level 0..user_max to 0..output_max through the custom curve, + * which ends at 100% at signal_max, or at AMDGPU_MAX_BL_LEVEL when the + * last point lies past signal_max. A point at signal s sits at user + * level s * user_max / AMDGPU_MAX_BL_LEVEL, so compare + * user * AMDGPU_MAX_BL_LEVEL against s * user_max rather than rounding the + * user level to 8 bits + */ +static u32 backlight_custom_curve_scale(const struct amdgpu_dm_backlight_caps *caps, + u32 user, u32 user_max, u32 signal_max, + u32 output_max) { - u32 lower_signal, upper_signal; - u32 lower_lum, upper_lum; + u64 x, x0 = 0, x1, num, den; + u32 l0 = 0, l1 = 100; int i; + user = min(user, user_max); 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; + return DIV_ROUND_CLOSEST_ULL((u64)user * output_max, user_max); + + x = (u64)user * AMDGPU_MAX_BL_LEVEL; + for (i = 0; i < caps->data_points; i++) { + u64 xi = (u64)caps->luminance_data[i].input_signal * user_max; + + if (x < xi) + break; + x0 = xi; + l0 = caps->luminance_data[i].luminance; + } + + if (i < caps->data_points) { + x1 = (u64)caps->luminance_data[i].input_signal * user_max; + l1 = caps->luminance_data[i].luminance; } 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; + x1 = (u64)signal_max * user_max; + if (x0 > x1) + x1 = (u64)AMDGPU_MAX_BL_LEVEL * user_max; + if (x >= x1) + return output_max; } - if (upper_signal == lower_signal) - return upper_lum; + /* x0 <= x < x1, so neither weight wraps */ + num = (u64)l0 * (x1 - x) + (u64)l1 * (x - x0); + den = 100 * (x1 - x0); - return lower_lum + DIV_ROUND_CLOSEST((upper_lum - lower_lum) * - (signal - lower_signal), - upper_signal - lower_signal); + return min_t(u64, mul_u64_add_u64_div_u64(output_max, num, den / 2, den), + output_max); } static u32 convert_brightness_to_millipercent(const struct amdgpu_dm_backlight_caps *caps, u32 user_brightness) { unsigned int user_min, user_max; - u32 signal, luminance; if (!get_brightness_range(caps, &user_min, &user_max)) return DIV_ROUND_CLOSEST_ULL((u64)user_brightness * 100000, @@ -184,10 +191,8 @@ static u32 convert_brightness_to_millipercent(const struct amdgpu_dm_backlight_c if (amdgpu_dc_debug_mask & DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE) return DIV_ROUND_CLOSEST_ULL((u64)user_brightness * 100000, user_max); - signal = scale_input_to_fw(user_max, user_brightness); - luminance = backlight_signal_to_luminance(caps, signal); - - return min_t(u32, luminance * 1000, 100000); + return backlight_custom_curve_scale(caps, user_brightness, user_max, + caps->max_input_signal, 100000); } STATIC_IFN_KUNIT -- 2.56.0
