From: Alex Hung Date: Thu, 14 May 2026 18:35:33 +0000 (-0600) Subject: drm/amd/display: Add KUnit tests for color LUT functions X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f602089acd9c55bdf4e04cf25462307c26eb9c8c;p=thirdparty%2Fkernel%2Fstable.git 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 Signed-off-by: Alex Hung Signed-off-by: Ray Wu Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- 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), {} };