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>
This commit is contained in:
Alex Hung 2026-05-14 12:35:33 -06:00 committed by Alex Deucher
parent c2572d535b
commit f602089acd
3 changed files with 358 additions and 6 deletions

View 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,

View 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__ */

View 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),
{}
};