summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Hung <alex.hung@amd.com>2026-05-14 12:35:33 -0600
committerAlex Deucher <alexander.deucher@amd.com>2026-06-03 13:40:08 -0400
commitf602089acd9c55bdf4e04cf25462307c26eb9c8c (patch)
tree07295b0e4b5c0494f779dd478f9d4de35ba24653
parentc2572d535be4847f8caba58b4ee8446b4ee15b57 (diff)
downloadlinux-f602089acd9c55bdf4e04cf25462307c26eb9c8c.tar.gz
linux-f602089acd9c55bdf4e04cf25462307c26eb9c8c.zip
drm/amd/display: Add KUnit tests for color LUT functions
[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>
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c15
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.h7
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_color_test.c342
3 files changed, 358 insertions, 6 deletions
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
index 4339e84360a5..3b00d3b6a9c6 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
@@ -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,
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.h
index 19d3a13572f5..6f4e97fe6694 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.h
@@ -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__ */
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_color_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_color_test.c
index 377fa4342ca1..56eea0f5a75d 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_color_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_color_test.c
@@ -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),
{}
};