]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
drm/amd/display: Add KUnit tests for color LUT functions
authorAlex Hung <alex.hung@amd.com>
Thu, 14 May 2026 18:35:33 +0000 (12:35 -0600)
committerAlex Deucher <alexander.deucher@amd.com>
Wed, 3 Jun 2026 17:40:08 +0000 (13:40 -0400)
[Why]
Add KUnit tests for three color management functions in
amdgpu_dm_color.c: amdgpu_dm_verify_lut_sizes,
amdgpu_dm_atomic_lut3d, and __set_colorop_3dlut.

[How]
Export amdgpu_dm_verify_lut_sizes with EXPORT_IF_KUNIT. Change
amdgpu_dm_atomic_lut3d and __set_colorop_3dlut from static to
STATIC_IFN_KUNIT and export them with EXPORT_IF_KUNIT. Add their
prototypes to amdgpu_dm_color.h inside the KUnit guard block.

Implement 14 test cases in amdgpu_dm_color_test.c:
- 8 tests for amdgpu_dm_verify_lut_sizes covering null LUTs,
  valid and invalid degamma/gamma sizes, both valid, and priority
- 3 tests for amdgpu_dm_atomic_lut3d covering zero size clearing
  initialized state, nonzero setting state bits and mode flags,
  and LUT data forwarding to tetrahedral_17
- 3 tests for __set_colorop_3dlut covering zero size returning
  -EINVAL and clearing initialized state, nonzero returning 0
  and setting state bits, and 32-bit LUT data forwarding to
  tetrahedral_17

Assisted-by: Copilot:Claude-Sonnet-4.6
Reviewed-by: Harry Wentland <harry.wentland@amd.com>
Signed-off-by: Alex Hung <alex.hung@amd.com>
Signed-off-by: Ray Wu <ray.wu@amd.com>
Tested-by: Daniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.h
drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_color_test.c

index 4339e84360a58e474251f9ae5defbd30af3af777..3b00d3b6a9c6eb084e161dc2777370ef40aeac0a 100644 (file)
@@ -1044,9 +1044,9 @@ EXPORT_IF_KUNIT(__drm_3dlut32_to_dc_3dlut);
  * Map user 3D LUT data to DC 3D LUT and all necessary bits to program it
  * on DCN accordingly.
  */
-static void amdgpu_dm_atomic_lut3d(const struct drm_color_lut *drm_lut3d,
-                                  uint32_t drm_lut3d_size,
-                                  struct dc_3dlut *lut)
+STATIC_IFN_KUNIT void amdgpu_dm_atomic_lut3d(const struct drm_color_lut *drm_lut3d,
+                                    uint32_t drm_lut3d_size,
+                                    struct dc_3dlut *lut)
 {
        if (!drm_lut3d_size) {
                lut->state.bits.initialized = 0;
@@ -1062,6 +1062,7 @@ static void amdgpu_dm_atomic_lut3d(const struct drm_color_lut *drm_lut3d,
                                        MAX_COLOR_3DLUT_BITDEPTH);
        }
 }
+EXPORT_IF_KUNIT(amdgpu_dm_atomic_lut3d);
 
 static int amdgpu_dm_atomic_shaper_lut(const struct drm_color_lut *shaper_lut,
                                       bool has_rom,
@@ -1198,6 +1199,7 @@ int amdgpu_dm_verify_lut_sizes(const struct drm_crtc_state *crtc_state)
 
        return 0;
 }
+EXPORT_IF_KUNIT(amdgpu_dm_verify_lut_sizes);
 
 /**
  * amdgpu_dm_check_crtc_color_mgmt: Check if DRM color props are programmable by DC.
@@ -1705,9 +1707,9 @@ __set_dm_plane_colorop_shaper(struct drm_plane_state *plane_state,
  * Returns:
  * 0 on success. -EINVAL if drm_lut3d_size is zero.
  */
-static int __set_colorop_3dlut(const struct drm_color_lut32 *drm_lut3d,
-                               uint32_t drm_lut3d_size,
-                               struct dc_3dlut *lut)
+STATIC_IFN_KUNIT int __set_colorop_3dlut(const struct drm_color_lut32 *drm_lut3d,
+                                        uint32_t drm_lut3d_size,
+                                        struct dc_3dlut *lut)
 {
        if (!drm_lut3d_size) {
                lut->state.bits.initialized = 0;
@@ -1724,6 +1726,7 @@ static int __set_colorop_3dlut(const struct drm_color_lut32 *drm_lut3d,
 
        return 0;
 }
+EXPORT_IF_KUNIT(__set_colorop_3dlut);
 
 static int
 __set_dm_plane_colorop_3dlut(struct drm_plane_state *plane_state,
index 19d3a13572f55fdc7cbd372bd27bf077139adbbf..6f4e97fe6694a997b8560c60c834aa1b0f7435bf 100644 (file)
@@ -84,6 +84,13 @@ void __drm_3dlut32_to_dc_3dlut(const struct drm_color_lut32 *lut,
                                struct tetrahedral_params *params,
                                bool use_tetrahedral_9,
                                int bit_depth);
+struct dc_3dlut;
+void amdgpu_dm_atomic_lut3d(const struct drm_color_lut *drm_lut3d,
+                            uint32_t drm_lut3d_size,
+                            struct dc_3dlut *lut);
+int __set_colorop_3dlut(const struct drm_color_lut32 *drm_lut3d,
+                       uint32_t drm_lut3d_size,
+                       struct dc_3dlut *lut);
 #endif
 
 #endif /* __AMDGPU_DM_COLOR_H__ */
index 377fa4342ca1fe2e80415af657fa078fbdf0b5ac..56eea0f5a75d6e8cb47cadead1108d9c3f5e389d 100644 (file)
@@ -991,6 +991,331 @@ static void dm_test_3dlut32_to_dc_3dlut_green_blue(struct kunit *test)
                        drm_color_lut32_extract(600000, 12));
 }
 
+/* ---- Tests for amdgpu_dm_verify_lut_sizes ---- */
+
+/**
+ * dm_test_make_lut_blob - Allocate a fake drm_property_blob for testing
+ * @test: KUnit test context
+ * @num_entries: number of LUT entries the blob will report
+ *
+ * Allocates a fake blob whose drm_color_lut_size() returns exactly
+ * @num_entries.  The data pointer is non-NULL so that
+ * __extract_blob_lut() returns a non-NULL lut pointer and the size
+ * check inside amdgpu_dm_verify_lut_sizes() is actually exercised.
+ *
+ * Return: pointer to the allocated blob
+ */
+static struct drm_property_blob *
+dm_test_make_lut_blob(struct kunit *test, uint32_t num_entries)
+{
+       struct drm_property_blob *blob;
+
+       blob = kunit_kzalloc(test, sizeof(*blob), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, blob);
+
+       blob->length = num_entries * sizeof(struct drm_color_lut);
+       blob->data = kunit_kcalloc(test, num_entries,
+                                  sizeof(struct drm_color_lut), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, blob->data);
+
+       return blob;
+}
+
+/**
+ * dm_test_verify_lut_sizes_null_luts - Both LUTs absent: must succeed
+ * @test: KUnit test context
+ */
+static void dm_test_verify_lut_sizes_null_luts(struct kunit *test)
+{
+       struct drm_crtc_state *state;
+
+       state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, state);
+
+       /* degamma_lut and gamma_lut are NULL (zeroed allocation) */
+       KUNIT_EXPECT_EQ(test, amdgpu_dm_verify_lut_sizes(state), 0);
+}
+
+/**
+ * dm_test_verify_lut_sizes_valid_degamma - Degamma LUT with the correct atomic size: must succeed
+ * @test: KUnit test context
+ */
+static void dm_test_verify_lut_sizes_valid_degamma(struct kunit *test)
+{
+       struct drm_crtc_state *state;
+
+       state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, state);
+
+       state->degamma_lut = dm_test_make_lut_blob(test, MAX_COLOR_LUT_ENTRIES);
+
+       KUNIT_EXPECT_EQ(test, amdgpu_dm_verify_lut_sizes(state), 0);
+}
+
+/**
+ * dm_test_verify_lut_sizes_invalid_degamma - Degamma LUT with a wrong size: must return -EINVAL
+ * @test: KUnit test context
+ */
+static void dm_test_verify_lut_sizes_invalid_degamma(struct kunit *test)
+{
+       struct drm_crtc_state *state;
+
+       state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, state);
+
+       /* Use an arbitrary size that is neither atomic nor legacy */
+       state->degamma_lut = dm_test_make_lut_blob(test, 128);
+
+       KUNIT_EXPECT_EQ(test, amdgpu_dm_verify_lut_sizes(state), -EINVAL);
+}
+
+/**
+ * dm_test_verify_lut_sizes_valid_gamma_atomic - Gamma LUT with correct atomic size: must succeed
+ * @test: KUnit test context
+ */
+static void dm_test_verify_lut_sizes_valid_gamma_atomic(struct kunit *test)
+{
+       struct drm_crtc_state *state;
+
+       state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, state);
+
+       state->gamma_lut = dm_test_make_lut_blob(test, MAX_COLOR_LUT_ENTRIES);
+
+       KUNIT_EXPECT_EQ(test, amdgpu_dm_verify_lut_sizes(state), 0);
+}
+
+/**
+ * dm_test_verify_lut_sizes_valid_gamma_legacy - Gamma LUT with legacy 256-entry size: must succeed
+ * @test: KUnit test context
+ */
+static void dm_test_verify_lut_sizes_valid_gamma_legacy(struct kunit *test)
+{
+       struct drm_crtc_state *state;
+
+       state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, state);
+
+       state->gamma_lut = dm_test_make_lut_blob(test, MAX_COLOR_LEGACY_LUT_ENTRIES);
+
+       KUNIT_EXPECT_EQ(test, amdgpu_dm_verify_lut_sizes(state), 0);
+}
+
+/**
+ * dm_test_verify_lut_sizes_invalid_gamma - Size is neither atomic nor legacy: must return -EINVAL
+ * @test: KUnit test context
+ */
+static void dm_test_verify_lut_sizes_invalid_gamma(struct kunit *test)
+{
+       struct drm_crtc_state *state;
+
+       state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, state);
+
+       state->gamma_lut = dm_test_make_lut_blob(test, 128);
+
+       KUNIT_EXPECT_EQ(test, amdgpu_dm_verify_lut_sizes(state), -EINVAL);
+}
+
+/**
+ * dm_test_verify_lut_sizes_both_valid - Both LUTs set to valid sizes: must succeed
+ * @test: KUnit test context
+ */
+static void dm_test_verify_lut_sizes_both_valid(struct kunit *test)
+{
+       struct drm_crtc_state *state;
+
+       state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, state);
+
+       state->degamma_lut = dm_test_make_lut_blob(test, MAX_COLOR_LUT_ENTRIES);
+       state->gamma_lut   = dm_test_make_lut_blob(test, MAX_COLOR_LUT_ENTRIES);
+
+       KUNIT_EXPECT_EQ(test, amdgpu_dm_verify_lut_sizes(state), 0);
+}
+
+/**
+ * dm_test_verify_lut_sizes_invalid_degamma_valid_gamma - Bad degamma overrides valid gamma: -EINVAL
+ * @test: KUnit test context
+ */
+static void dm_test_verify_lut_sizes_invalid_degamma_valid_gamma(struct kunit *test)
+{
+       struct drm_crtc_state *state;
+
+       state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, state);
+
+       state->degamma_lut = dm_test_make_lut_blob(test, 128);
+       state->gamma_lut   = dm_test_make_lut_blob(test, MAX_COLOR_LUT_ENTRIES);
+
+       KUNIT_EXPECT_EQ(test, amdgpu_dm_verify_lut_sizes(state), -EINVAL);
+}
+
+/* ---- Tests for amdgpu_dm_atomic_lut3d ---- */
+
+/**
+ * dm_test_atomic_lut3d_zero_size - Zero LUT size: initialized must be cleared, no LUT data written
+ * @test: KUnit test context
+ */
+static void dm_test_atomic_lut3d_zero_size(struct kunit *test)
+{
+       struct dc_3dlut *lut;
+       u32 initialized;
+
+       lut = kunit_kzalloc(test, sizeof(*lut), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, lut);
+
+       /* Pre-set initialized so we can confirm it is cleared */
+       lut->state.bits.initialized = 1;
+
+       amdgpu_dm_atomic_lut3d(NULL, 0, lut);
+
+       /* Copy bit-field: typeof cannot be applied to a bit-field */
+       initialized = lut->state.bits.initialized;
+       KUNIT_EXPECT_EQ(test, initialized, 0U);
+}
+
+/**
+ * dm_test_atomic_lut3d_nonzero_state_bits - Non-zero size: state bits and mode flags must be set
+ * @test: KUnit test context
+ */
+static void dm_test_atomic_lut3d_nonzero_state_bits(struct kunit *test)
+{
+       const uint32_t lut3d_size = 5;
+       struct drm_color_lut *lut_data;
+       struct dc_3dlut *lut;
+       u32 initialized;
+
+       lut_data = kunit_kcalloc(test, lut3d_size, sizeof(*lut_data), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, lut_data);
+
+       lut = kunit_kzalloc(test, sizeof(*lut), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, lut);
+
+       amdgpu_dm_atomic_lut3d(lut_data, lut3d_size, lut);
+
+       /* Copy bit-field: typeof cannot be applied to a bit-field */
+       initialized = lut->state.bits.initialized;
+       KUNIT_EXPECT_EQ(test, initialized, 1U);
+       KUNIT_EXPECT_FALSE(test, lut->lut_3d.use_tetrahedral_9);
+       KUNIT_EXPECT_TRUE(test, lut->lut_3d.use_12bits);
+}
+
+/**
+ * dm_test_atomic_lut3d_data_forwarded - Non-zero size: LUT data forwarded to tetrahedral_17
+ * @test: KUnit test context
+ */
+static void dm_test_atomic_lut3d_data_forwarded(struct kunit *test)
+{
+       const uint32_t lut3d_size = 5;
+       struct drm_color_lut *lut_data;
+       struct dc_3dlut *lut;
+
+       lut_data = kunit_kcalloc(test, lut3d_size, sizeof(*lut_data), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, lut_data);
+
+       lut = kunit_kzalloc(test, sizeof(*lut), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, lut);
+
+       lut_data[0].red   = 0xFFFF;
+       lut_data[0].green = 0x8000;
+       lut_data[0].blue  = 0x4000;
+
+       amdgpu_dm_atomic_lut3d(lut_data, lut3d_size, lut);
+
+       /*
+        * use_tetrahedral_9 == false → data goes into tetrahedral_17.
+        * lut[0] maps to lut0[0] (first element of the first group).
+        */
+       KUNIT_EXPECT_EQ(test, lut->lut_3d.tetrahedral_17.lut0[0].red,
+                       drm_color_lut_extract(0xFFFF, MAX_COLOR_3DLUT_BITDEPTH));
+       KUNIT_EXPECT_EQ(test, lut->lut_3d.tetrahedral_17.lut0[0].green,
+                       drm_color_lut_extract(0x8000, MAX_COLOR_3DLUT_BITDEPTH));
+       KUNIT_EXPECT_EQ(test, lut->lut_3d.tetrahedral_17.lut0[0].blue,
+                       drm_color_lut_extract(0x4000, MAX_COLOR_3DLUT_BITDEPTH));
+}
+
+/* ---- Tests for __set_colorop_3dlut ---- */
+
+/**
+ * dm_test_set_colorop_3dlut_zero_size - Zero LUT size: must return -EINVAL and clear initialized
+ * @test: KUnit test context
+ */
+static void dm_test_set_colorop_3dlut_zero_size(struct kunit *test)
+{
+       struct dc_3dlut *lut;
+       u32 initialized;
+
+       lut = kunit_kzalloc(test, sizeof(*lut), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, lut);
+
+       lut->state.bits.initialized = 1;
+
+       KUNIT_EXPECT_EQ(test, __set_colorop_3dlut(NULL, 0, lut), -EINVAL);
+       /* Copy bit-field: typeof cannot be applied to a bit-field */
+       initialized = lut->state.bits.initialized;
+       KUNIT_EXPECT_EQ(test, initialized, 0U);
+}
+
+/**
+ * dm_test_set_colorop_3dlut_nonzero_state_bits - Non-zero size: must return 0 and set state bits
+ * @test: KUnit test context
+ */
+static void dm_test_set_colorop_3dlut_nonzero_state_bits(struct kunit *test)
+{
+       const uint32_t lut3d_size = 5;
+       struct drm_color_lut32 *lut_data;
+       struct dc_3dlut *lut;
+       u32 initialized;
+
+       lut_data = kunit_kcalloc(test, lut3d_size, sizeof(*lut_data), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, lut_data);
+
+       lut = kunit_kzalloc(test, sizeof(*lut), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, lut);
+
+       KUNIT_EXPECT_EQ(test, __set_colorop_3dlut(lut_data, lut3d_size, lut), 0);
+       /* Copy bit-field: typeof cannot be applied to a bit-field */
+       initialized = lut->state.bits.initialized;
+       KUNIT_EXPECT_EQ(test, initialized, 1U);
+       KUNIT_EXPECT_FALSE(test, lut->lut_3d.use_tetrahedral_9);
+       KUNIT_EXPECT_TRUE(test, lut->lut_3d.use_12bits);
+}
+
+/**
+ * dm_test_set_colorop_3dlut_data_forwarded - Non-zero size: 32-bit data forwarded to tetrahedral_17
+ * @test: KUnit test context
+ */
+static void dm_test_set_colorop_3dlut_data_forwarded(struct kunit *test)
+{
+       const uint32_t lut3d_size = 5;
+       struct drm_color_lut32 *lut_data;
+       struct dc_3dlut *lut;
+
+       lut_data = kunit_kcalloc(test, lut3d_size, sizeof(*lut_data), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, lut_data);
+
+       lut = kunit_kzalloc(test, sizeof(*lut), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, lut);
+
+       lut_data[0].red   = 0xFFFFFFFF;
+       lut_data[0].green = 0x80000000;
+       lut_data[0].blue  = 0x40000000;
+
+       KUNIT_EXPECT_EQ(test, __set_colorop_3dlut(lut_data, lut3d_size, lut), 0);
+
+       /*
+        * use_tetrahedral_9 == false → data goes into tetrahedral_17.
+        * lut[0] maps to lut0[0].  Bit depth used by __set_colorop_3dlut is 12.
+        */
+       KUNIT_EXPECT_EQ(test, lut->lut_3d.tetrahedral_17.lut0[0].red,
+                       drm_color_lut32_extract(0xFFFFFFFF, 12));
+       KUNIT_EXPECT_EQ(test, lut->lut_3d.tetrahedral_17.lut0[0].green,
+                       drm_color_lut32_extract(0x80000000, 12));
+       KUNIT_EXPECT_EQ(test, lut->lut_3d.tetrahedral_17.lut0[0].blue,
+                       drm_color_lut32_extract(0x40000000, 12));
+}
+
 static struct kunit_case dm_color_test_cases[] = {
        /* amdgpu_dm_fixpt_from_s3132 */
        KUNIT_CASE(dm_test_fixpt_from_s3132_zero),
@@ -1056,6 +1381,23 @@ static struct kunit_case dm_color_test_cases[] = {
        KUNIT_CASE(dm_test_3dlut32_to_dc_3dlut_distribution),
        KUNIT_CASE(dm_test_3dlut32_to_dc_3dlut_tetrahedral_17),
        KUNIT_CASE(dm_test_3dlut32_to_dc_3dlut_green_blue),
+       /* amdgpu_dm_verify_lut_sizes */
+       KUNIT_CASE(dm_test_verify_lut_sizes_null_luts),
+       KUNIT_CASE(dm_test_verify_lut_sizes_valid_degamma),
+       KUNIT_CASE(dm_test_verify_lut_sizes_invalid_degamma),
+       KUNIT_CASE(dm_test_verify_lut_sizes_valid_gamma_atomic),
+       KUNIT_CASE(dm_test_verify_lut_sizes_valid_gamma_legacy),
+       KUNIT_CASE(dm_test_verify_lut_sizes_invalid_gamma),
+       KUNIT_CASE(dm_test_verify_lut_sizes_both_valid),
+       KUNIT_CASE(dm_test_verify_lut_sizes_invalid_degamma_valid_gamma),
+       /* amdgpu_dm_atomic_lut3d */
+       KUNIT_CASE(dm_test_atomic_lut3d_zero_size),
+       KUNIT_CASE(dm_test_atomic_lut3d_nonzero_state_bits),
+       KUNIT_CASE(dm_test_atomic_lut3d_data_forwarded),
+       /* __set_colorop_3dlut */
+       KUNIT_CASE(dm_test_set_colorop_3dlut_zero_size),
+       KUNIT_CASE(dm_test_set_colorop_3dlut_nonzero_state_bits),
+       KUNIT_CASE(dm_test_set_colorop_3dlut_data_forwarded),
        {}
 };