[PATCH RFC 2/3] drm/amd/display: Add KUnit tests for the ATIF backlight curve check
From: Hugo Sales
Date: Wed Nov 04 2026 - 05:35:16 EST
Test the curve check in amdgpu_dm_validate_backlight_caps():
- the GV301QH curve stays as it is
- a luminance over 100% becomes 100%
- a falling luminance rises to the point before
- a curve whose signals fall gets dropped
- a repeated signal stays
- a curve with more points than luminance_data holds is cut to fit
Also check the brightness the PWM and AUX paths give on the checked
curves
Signed-off-by: Hugo Sales <hugo@xxxxxxx>
---
KUnit: dm_test_validate_custom_curve_unsorted and the luminance checks
in the falling test fail without patch 1
---
.../amdgpu_dm/tests/amdgpu_dm_backlight_test.c | 184 +++++++++++++++++++++
1 file changed, 184 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 b3e6a96320164..d76527e49acf2 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
@@ -1696,6 +1696,185 @@ static void dm_test_brightness_curve_pwm_luminance_over_100(struct kunit *test)
KUNIT_EXPECT_EQ(test, convert_brightness_from_user(&caps, 225000), 255000U);
}
+/* Tests for the custom curve check in amdgpu_dm_validate_backlight_caps() */
+
+static struct amdgpu_display_manager *alloc_validate_dm(struct kunit *test)
+{
+ struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
+
+ dm->adev = dm_kunit_alloc_adev(test);
+ /* drm_warn() prints the curve warnings against this device */
+ KUNIT_ASSERT_NOT_NULL(test, adev_to_drm(dm->adev)->dev);
+
+ return dm;
+}
+
+static struct amdgpu_dm_backlight_caps *
+validate_custom_curve(struct amdgpu_display_manager *dm,
+ const struct amdgpu_dm_luminance_data *points, int n)
+{
+ struct amdgpu_dm_backlight_caps *caps = &dm->backlight_caps[0];
+
+ set_custom_curve_caps(caps, points, n);
+ caps->caps_valid = true;
+ amdgpu_dm_validate_backlight_caps(dm, 0);
+
+ return caps;
+}
+
+/**
+ * dm_test_validate_custom_curve_gv301qh - Test a good ATIF curve is left alone
+ * @test: The KUnit test context
+ *
+ * The GV301QH curve rises and stays within 100%, so the check must keep
+ * every point as it is
+ */
+static void dm_test_validate_custom_curve_gv301qh(struct kunit *test)
+{
+ struct amdgpu_display_manager *dm = alloc_validate_dm(test);
+ struct amdgpu_dm_backlight_caps *caps = &dm->backlight_caps[0];
+
+ set_gv301qh_caps(caps, false);
+ caps->caps_valid = true;
+ amdgpu_dm_validate_backlight_caps(dm, 0);
+
+ KUNIT_EXPECT_TRUE(test, caps->caps_valid);
+ KUNIT_ASSERT_EQ(test, caps->data_points, ARRAY_SIZE(gv301qh_curve));
+ KUNIT_EXPECT_MEMEQ(test, caps->luminance_data, gv301qh_curve, sizeof(gv301qh_curve));
+}
+
+/**
+ * dm_test_validate_custom_curve_luminance_over_100 - Test ATIF luminance over 100% becomes 100%
+ * @test: The KUnit test context
+ *
+ * Points (100, 50), (200, 150) and (250, 120) become (100, 50), (200, 100)
+ * and (250, 100). Level 150 is then 50 + 50 * 50 / 100 = 75% and level 200
+ * is full brightness. Over AUX, 301176 of 512000 is just below signal 150,
+ * so it lands one below 75% of 1000..512000, at 384249
+ */
+static void dm_test_validate_custom_curve_luminance_over_100(struct kunit *test)
+{
+ static const struct amdgpu_dm_luminance_data points[] = {
+ { .input_signal = 100, .luminance = 50 },
+ { .input_signal = 200, .luminance = 150 },
+ { .input_signal = 250, .luminance = 120 },
+ };
+ struct amdgpu_display_manager *dm = alloc_validate_dm(test);
+ struct amdgpu_dm_backlight_caps *caps;
+
+ dm_test_enable_custom_curve(test);
+ caps = validate_custom_curve(dm, points, ARRAY_SIZE(points));
+
+ KUNIT_ASSERT_EQ(test, caps->data_points, 3);
+ KUNIT_EXPECT_EQ(test, caps->luminance_data[0].luminance, 50);
+ KUNIT_EXPECT_EQ(test, caps->luminance_data[1].luminance, 100);
+ KUNIT_EXPECT_EQ(test, caps->luminance_data[2].luminance, 100);
+
+ KUNIT_EXPECT_EQ(test, convert_brightness_from_user(caps, 150 * 0x101), 49151U);
+ KUNIT_EXPECT_EQ(test, convert_brightness_from_user(caps, 200 * 0x101), 65535U);
+ KUNIT_EXPECT_EQ(test, convert_brightness_to_millipercent(caps, 150 * 0x101), 75000U);
+ KUNIT_EXPECT_EQ(test, convert_brightness_to_millipercent(caps, 200 * 0x101), 100000U);
+
+ caps->aux_support = true;
+ caps->aux_min_input_signal = 1;
+ caps->aux_max_input_signal = 512;
+ KUNIT_EXPECT_EQ(test, convert_brightness_from_user(caps, 301176), 384249U);
+}
+
+/**
+ * dm_test_validate_custom_curve_falling - Test a falling ATIF point is raised
+ * @test: The KUnit test context
+ *
+ * In (50, 30), (100, 60), (150, 40) and (200, 80), the point at 150 is
+ * raised to 60%, so the curve is flat from 100 to 150. Level 125 is 60% on
+ * both paths, which over AUX is 307600 in 1000..512000
+ */
+static void dm_test_validate_custom_curve_falling(struct kunit *test)
+{
+ static const struct amdgpu_dm_luminance_data points[] = {
+ { .input_signal = 50, .luminance = 30 },
+ { .input_signal = 100, .luminance = 60 },
+ { .input_signal = 150, .luminance = 40 },
+ { .input_signal = 200, .luminance = 80 },
+ };
+ struct amdgpu_display_manager *dm = alloc_validate_dm(test);
+ struct amdgpu_dm_backlight_caps *caps;
+
+ dm_test_enable_custom_curve(test);
+ caps = validate_custom_curve(dm, points, ARRAY_SIZE(points));
+
+ KUNIT_ASSERT_EQ(test, caps->data_points, 4);
+ KUNIT_EXPECT_EQ(test, caps->luminance_data[1].luminance, 60);
+ KUNIT_EXPECT_EQ(test, caps->luminance_data[2].luminance, 60);
+ KUNIT_EXPECT_EQ(test, caps->luminance_data[3].luminance, 80);
+
+ KUNIT_EXPECT_EQ(test, convert_brightness_from_user(caps, 125 * 0x101), 39321U);
+ KUNIT_EXPECT_EQ(test, convert_brightness_to_millipercent(caps, 125 * 0x101), 60000U);
+
+ caps->aux_support = true;
+ caps->aux_min_input_signal = 1;
+ caps->aux_max_input_signal = 512;
+ KUNIT_EXPECT_EQ(test, convert_brightness_from_user(caps, 250980), 307600U);
+}
+
+/**
+ * dm_test_validate_custom_curve_unsorted - Test a curve whose signals fall is dropped
+ * @test: The KUnit test context
+ *
+ * A signal below the one before leaves no curve, so the mapping is
+ * linear. A repeated signal is a step: the curve stays, and level 100 is
+ * at the later point's 60%
+ */
+static void dm_test_validate_custom_curve_unsorted(struct kunit *test)
+{
+ static const struct amdgpu_dm_luminance_data falling[] = {
+ { .input_signal = 100, .luminance = 40 },
+ { .input_signal = 80, .luminance = 60 },
+ };
+ static const struct amdgpu_dm_luminance_data repeated[] = {
+ { .input_signal = 50, .luminance = 20 },
+ { .input_signal = 100, .luminance = 40 },
+ { .input_signal = 100, .luminance = 60 },
+ };
+ struct amdgpu_display_manager *dm = alloc_validate_dm(test);
+ struct amdgpu_dm_backlight_caps *caps;
+
+ dm_test_enable_custom_curve(test);
+
+ caps = validate_custom_curve(dm, falling, ARRAY_SIZE(falling));
+ KUNIT_EXPECT_EQ(test, caps->data_points, 0);
+ KUNIT_EXPECT_EQ(test, convert_brightness_from_user(caps, 32768), 32768U);
+
+ caps = validate_custom_curve(dm, repeated, ARRAY_SIZE(repeated));
+ KUNIT_EXPECT_EQ(test, caps->data_points, 3);
+ KUNIT_EXPECT_EQ(test, convert_brightness_to_millipercent(caps, 100 * 0x101), 60000U);
+}
+
+/**
+ * dm_test_validate_custom_curve_too_many_points - Test data_points is cut to the array
+ * @test: The KUnit test context
+ *
+ * data_points is a u8 from firmware, so it can claim more points than
+ * luminance_data holds. The check keeps the points that fit
+ */
+static void dm_test_validate_custom_curve_too_many_points(struct kunit *test)
+{
+ struct amdgpu_display_manager *dm = alloc_validate_dm(test);
+ struct amdgpu_dm_backlight_caps *caps = &dm->backlight_caps[0];
+ int i;
+
+ set_gv301qh_caps(caps, false);
+ for (i = 0; i < ARRAY_SIZE(caps->luminance_data); i++) {
+ caps->luminance_data[i].input_signal = 2 * i;
+ caps->luminance_data[i].luminance = i;
+ }
+ caps->data_points = 255;
+ caps->caps_valid = true;
+ amdgpu_dm_validate_backlight_caps(dm, 0);
+
+ KUNIT_EXPECT_EQ(test, caps->data_points, ARRAY_SIZE(caps->luminance_data));
+}
+
/**
* dm_test_brightness_range_zero_signals - Test Brightness range with zero min and max signals
* @test: The KUnit test context
@@ -2365,6 +2544,11 @@ static struct kunit_case dm_backlight_test_cases[] = {
KUNIT_CASE(dm_test_backlight_caps_aux_support_noop),
/* amdgpu_dm_validate_backlight_caps */
KUNIT_CASE_PARAM(dm_test_validate_backlight_caps, dm_validate_caps_gen_params),
+ KUNIT_CASE(dm_test_validate_custom_curve_gv301qh),
+ KUNIT_CASE(dm_test_validate_custom_curve_luminance_over_100),
+ KUNIT_CASE(dm_test_validate_custom_curve_falling),
+ KUNIT_CASE(dm_test_validate_custom_curve_unsorted),
+ KUNIT_CASE(dm_test_validate_custom_curve_too_many_points),
/* get_brightness_range */
KUNIT_CASE(dm_test_brightness_range_null_caps),
KUNIT_CASE(dm_test_brightness_range_pwm),
--
2.56.0