The existing custom-curve tests use min_input_signal == 0. In that
configuration the firmware minimum is zero and an extra [0..max] to
[min..max] conversion is an identity, so the output-side double scaling
cannot be observed.

Add a curve using the default non-zero PWM minimum. Cover the helper's
userspace-domain result, zero and maximum endpoints, interpolation,
monotonicity, range bounds, zero round-trip, agreement with the linear
path at zero, and the unchanged no-curve path.

Six of the nine new cases fail against the pre-fix arithmetic. The other
three retain endpoint and linear-path invariants around the repair.

The cases were verified under UML KUnit.

Signed-off-by: Andrei Rusu de Castro <[email protected]>
---
 .../tests/amdgpu_dm_backlight_test.c          | 264 ++++++++++++++++++
 1 file changed, 264 insertions(+)

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 1fb171fdbc3c..a6fa052a8272 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
@@ -1398,6 +1398,261 @@ static void 
dm_test_brightness_from_user_with_curve(struct kunit *test)
        amdgpu_dm_set_dc_debug_mask(saved_mask);
 }
 
+/*
+ * The custom curve cases above all use min_input_signal == 0.  There the
+ * firmware minimum is zero and the [0..max] to [min..max] span is the 
identity,
+ * so an extra application of that mapping cannot be observed.  The cases below
+ * use the default firmware range instead, where min is 0x101 * 12 == 3084 and
+ * max is 0x101 * 255 == 65535.
+ */
+static void dm_test_curve_caps_init(struct amdgpu_dm_backlight_caps *caps)
+{
+       caps->aux_support = false;
+       caps->min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
+       caps->max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
+       caps->data_points = 3;
+       caps->luminance_data[0].input_signal = 50;
+       caps->luminance_data[0].luminance = 20;
+       caps->luminance_data[1].input_signal = 128;
+       caps->luminance_data[1].luminance = 50;
+       caps->luminance_data[2].input_signal = 255;
+       caps->luminance_data[2].luminance = 100;
+}
+
+/**
+ * dm_test_custom_brightness_user_domain - Curve output is a userspace value
+ * @test: The KUnit test context
+ *
+ * convert_custom_brightness() reshapes a value inside the userspace [0..max]
+ * domain.  Its caller owns the single conversion to the firmware [min..max]
+ * domain, so the firmware minimum must not appear in this result.
+ */
+static void dm_test_custom_brightness_user_domain(struct kunit *test)
+{
+       struct amdgpu_dm_backlight_caps caps = {};
+       uint saved_mask = amdgpu_dm_get_dc_debug_mask();
+       unsigned int min, max;
+       u32 brightness;
+
+       amdgpu_dm_set_dc_debug_mask(saved_mask & 
~DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE);
+       dm_test_curve_caps_init(&caps);
+       get_brightness_range(&caps, &min, &max);
+
+       /* Zero stays zero; it is not lifted to the firmware minimum here. */
+       brightness = 0;
+       convert_custom_brightness(&caps, max, &brightness);
+       KUNIT_EXPECT_EQ(test, brightness, (u32)0);
+
+       /* The top of the curve stays inside the userspace range. */
+       brightness = max;
+       convert_custom_brightness(&caps, max, &brightness);
+       KUNIT_EXPECT_LE(test, brightness, (u32)max);
+
+       amdgpu_dm_set_dc_debug_mask(saved_mask);
+}
+
+/**
+ * dm_test_curve_from_user_zero_is_min - Zero maps to the firmware minimum
+ * @test: The KUnit test context
+ *
+ * Zero is the darkest level userspace can ask for and must reach the darkest
+ * level the firmware accepts.
+ */
+static void dm_test_curve_from_user_zero_is_min(struct kunit *test)
+{
+       struct amdgpu_dm_backlight_caps caps = {};
+       uint saved_mask = amdgpu_dm_get_dc_debug_mask();
+       unsigned int min, max;
+
+       amdgpu_dm_set_dc_debug_mask(saved_mask & 
~DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE);
+       dm_test_curve_caps_init(&caps);
+       get_brightness_range(&caps, &min, &max);
+
+       KUNIT_EXPECT_EQ(test, convert_brightness_from_user(&caps, 0), (u32)min);
+
+       amdgpu_dm_set_dc_debug_mask(saved_mask);
+}
+
+/**
+ * dm_test_curve_from_user_matches_linear_at_zero - Curve keeps the lower 
endpoint
+ * @test: The KUnit test context
+ *
+ * The curve reshapes the interior of the range.  It does not move either
+ * endpoint, so the curved and linear paths must agree at zero.
+ */
+static void dm_test_curve_from_user_matches_linear_at_zero(struct kunit *test)
+{
+       struct amdgpu_dm_backlight_caps caps = {};
+       uint saved_mask = amdgpu_dm_get_dc_debug_mask();
+       u32 with_curve, without_curve;
+
+       dm_test_curve_caps_init(&caps);
+
+       amdgpu_dm_set_dc_debug_mask(saved_mask & 
~DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE);
+       with_curve = convert_brightness_from_user(&caps, 0);
+
+       amdgpu_dm_set_dc_debug_mask(saved_mask | 
DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE);
+       without_curve = convert_brightness_from_user(&caps, 0);
+
+       KUNIT_EXPECT_EQ(test, with_curve, without_curve);
+
+       amdgpu_dm_set_dc_debug_mask(saved_mask);
+}
+
+/**
+ * dm_test_curve_from_user_round_trip_zero - Zero survives a conversion round 
trip
+ * @test: The KUnit test context
+ */
+static void dm_test_curve_from_user_round_trip_zero(struct kunit *test)
+{
+       struct amdgpu_dm_backlight_caps caps = {};
+       uint saved_mask = amdgpu_dm_get_dc_debug_mask();
+       u32 level;
+
+       amdgpu_dm_set_dc_debug_mask(saved_mask & 
~DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE);
+       dm_test_curve_caps_init(&caps);
+
+       level = convert_brightness_from_user(&caps, 0);
+       KUNIT_EXPECT_EQ(test, convert_brightness_to_user(&caps, level), (u32)0);
+
+       amdgpu_dm_set_dc_debug_mask(saved_mask);
+}
+
+/**
+ * dm_test_curve_from_user_interpolation - Interpolated point with a non-zero 
min
+ * @test: The KUnit test context
+ */
+static void dm_test_curve_from_user_interpolation(struct kunit *test)
+{
+       struct amdgpu_dm_backlight_caps caps = {};
+       uint saved_mask = amdgpu_dm_get_dc_debug_mask();
+
+       amdgpu_dm_set_dc_debug_mask(saved_mask & 
~DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE);
+       dm_test_curve_caps_init(&caps);
+
+       /*
+        * scale_input_to_fw(65535, 25700) = DIV_ROUND_CLOSEST(25700 * 255, 
65535)
+        * = 100, which falls between the (50, 20) and (128, 50) points:
+        *   lum = 20 + DIV_ROUND_CLOSEST((50 - 20) * (100 - 50), 128 - 50) = 39
+        * The curved firmware level is DIV_ROUND_CLOSEST(39 * 100, 101) = 39,
+        * which is DIV_ROUND_CLOSEST(39 * 65535, 255) = 10023 in the userspace
+        * domain and 3084 + DIV_ROUND_CLOSEST(62451 * 10023, 65535) = 12635
+        * once converted to the firmware domain.
+        */
+       KUNIT_EXPECT_EQ(test, convert_brightness_from_user(&caps, 25700), 
(u32)12635);
+
+       amdgpu_dm_set_dc_debug_mask(saved_mask);
+}
+
+/**
+ * dm_test_curve_from_user_max - The top of the range stays inside the range
+ * @test: The KUnit test context
+ */
+static void dm_test_curve_from_user_max(struct kunit *test)
+{
+       struct amdgpu_dm_backlight_caps caps = {};
+       uint saved_mask = amdgpu_dm_get_dc_debug_mask();
+       unsigned int min, max;
+
+       amdgpu_dm_set_dc_debug_mask(saved_mask & 
~DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE);
+       dm_test_curve_caps_init(&caps);
+       get_brightness_range(&caps, &min, &max);
+
+       /*
+        * scale_input_to_fw(65535, 65535) = 255 matches the last point exactly,
+        * so lum = 100 and the curved firmware level is
+        * DIV_ROUND_CLOSEST(100 * 255, 101) = 252.  That is
+        * DIV_ROUND_CLOSEST(252 * 65535, 255) = 64764 in the userspace domain
+        * and 3084 + DIV_ROUND_CLOSEST(62451 * 64764, 65535) = 64800 in the
+        * firmware domain.
+        */
+       KUNIT_EXPECT_EQ(test, convert_brightness_from_user(&caps, max), 
(u32)64800);
+       KUNIT_EXPECT_LE(test, convert_brightness_from_user(&caps, max), 
(u32)max);
+
+       amdgpu_dm_set_dc_debug_mask(saved_mask);
+}
+
+/**
+ * dm_test_curve_from_user_monotonic - A rising curve gives a rising level
+ * @test: The KUnit test context
+ */
+static void dm_test_curve_from_user_monotonic(struct kunit *test)
+{
+       struct amdgpu_dm_backlight_caps caps = {};
+       uint saved_mask = amdgpu_dm_get_dc_debug_mask();
+       unsigned int min, max, i;
+       u32 previous = 0;
+
+       amdgpu_dm_set_dc_debug_mask(saved_mask & 
~DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE);
+       dm_test_curve_caps_init(&caps);
+       get_brightness_range(&caps, &min, &max);
+
+       for (i = 0; i <= max; i += 1023) {
+               u32 level = convert_brightness_from_user(&caps, i);
+
+               KUNIT_ASSERT_GE(test, level, previous);
+               previous = level;
+       }
+
+       KUNIT_EXPECT_GE(test, convert_brightness_from_user(&caps, max), 
previous);
+
+       amdgpu_dm_set_dc_debug_mask(saved_mask);
+}
+
+/**
+ * dm_test_curve_from_user_within_range - Curved levels never leave [min..max]
+ * @test: The KUnit test context
+ *
+ * The firmware level is programmed through a 16-bit path, so a converted value
+ * above max would wrap and darken the panel.
+ */
+static void dm_test_curve_from_user_within_range(struct kunit *test)
+{
+       struct amdgpu_dm_backlight_caps caps = {};
+       uint saved_mask = amdgpu_dm_get_dc_debug_mask();
+       unsigned int min, max, i;
+
+       amdgpu_dm_set_dc_debug_mask(saved_mask & 
~DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE);
+       dm_test_curve_caps_init(&caps);
+       get_brightness_range(&caps, &min, &max);
+
+       for (i = 0; i <= max; i += 1023) {
+               u32 level = convert_brightness_from_user(&caps, i);
+
+               KUNIT_ASSERT_GE(test, level, (u32)min);
+               KUNIT_ASSERT_LE(test, level, (u32)max);
+       }
+
+       KUNIT_EXPECT_LE(test, convert_brightness_from_user(&caps, max), 
(u32)max);
+
+       amdgpu_dm_set_dc_debug_mask(saved_mask);
+}
+
+/**
+ * dm_test_from_user_no_curve_unchanged - The linear path is untouched
+ * @test: The KUnit test context
+ *
+ * Without luminance data the conversion is the plain
+ * min + DIV_ROUND_CLOSEST((max - min) * brightness, max) mapping.
+ */
+static void dm_test_from_user_no_curve_unchanged(struct kunit *test)
+{
+       struct amdgpu_dm_backlight_caps caps = {};
+       unsigned int min, max;
+
+       caps.aux_support = false;
+       caps.min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
+       caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
+       caps.data_points = 0;
+
+       get_brightness_range(&caps, &min, &max);
+
+       KUNIT_EXPECT_EQ(test, convert_brightness_from_user(&caps, 0), 
(u32)3084);
+       KUNIT_EXPECT_EQ(test, convert_brightness_from_user(&caps, 16383), 
(u32)18696);
+       KUNIT_EXPECT_EQ(test, convert_brightness_from_user(&caps, 32767), 
(u32)34309);
+       KUNIT_EXPECT_EQ(test, convert_brightness_from_user(&caps, max), 
(u32)65535);
+}
+
 /**
  * dm_test_brightness_range_zero_signals - Test Brightness range with zero min 
and max signals
  * @test: The KUnit test context
@@ -1955,6 +2210,15 @@ static struct kunit_case dm_backlight_test_cases[] = {
        KUNIT_CASE(dm_test_brightness_to_user_above_max),
        KUNIT_CASE(dm_test_brightness_from_user_midrange),
        KUNIT_CASE(dm_test_brightness_from_user_with_curve),
+       KUNIT_CASE(dm_test_custom_brightness_user_domain),
+       KUNIT_CASE(dm_test_curve_from_user_zero_is_min),
+       KUNIT_CASE(dm_test_curve_from_user_matches_linear_at_zero),
+       KUNIT_CASE(dm_test_curve_from_user_round_trip_zero),
+       KUNIT_CASE(dm_test_curve_from_user_interpolation),
+       KUNIT_CASE(dm_test_curve_from_user_max),
+       KUNIT_CASE(dm_test_curve_from_user_monotonic),
+       KUNIT_CASE(dm_test_curve_from_user_within_range),
+       KUNIT_CASE(dm_test_from_user_no_curve_unchanged),
        KUNIT_CASE(dm_test_brightness_range_zero_signals),
        /* amdgpu_dm_backlight_fill_props */
        KUNIT_CASE(dm_test_backlight_fill_props_ac_linear),

Reply via email to