drm/amd/display: Add color transfer-function tests

[WHY]
The color transfer-function calculation helpers were not covered by
the amdgpu_dm_color KUnit suite. They rely on DAL fixed-point math
and run under the KUnit UML build without native floating point.

[HOW]
Expose the five static helpers to KUnit and add direct coverage for
the legacy, 16-bit, and 32-bit input and output transfer-function
paths, including sRGB, linear, and ROM-backed variants.

Assisted-by: Copilot:GPT-5.6-Sol
Reviewed-by: Bhawanpreet Lakha <bhawanpreet.lakha@amd.com>
Signed-off-by: Alex Hung <alex.hung@amd.com>
Signed-off-by: Fangzhi Zuo <jerry.zuo@amd.com>
Tested-by: Dan Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
Alex Hung
2026-07-14 15:03:05 -06:00
committed by Alex Deucher
parent 36e7f55ec8
commit bfab0b0efd
3 changed files with 184 additions and 13 deletions

View File

@@ -618,9 +618,10 @@ EXPORT_IF_KUNIT(__drm_ctm_3x4_to_dc_matrix);
* Returns:
* 0 in case of success, -ENOMEM if fails
*/
static int __set_legacy_tf(struct dc_transfer_func *func,
const struct drm_color_lut *lut, uint32_t lut_size,
bool has_rom)
STATIC_IFN_KUNIT int
__set_legacy_tf(struct dc_transfer_func *func,
const struct drm_color_lut *lut, uint32_t lut_size,
bool has_rom)
{
struct dc_gamma *gamma = NULL;
struct calculate_buffer cal_buffer = {0};
@@ -645,6 +646,7 @@ static int __set_legacy_tf(struct dc_transfer_func *func,
return res ? 0 : -ENOMEM;
}
EXPORT_IF_KUNIT(__set_legacy_tf);
/**
* __set_output_tf - calculates the output transfer function based on expected input space.
@@ -656,9 +658,10 @@ static int __set_legacy_tf(struct dc_transfer_func *func,
* Returns:
* 0 in case of success. -ENOMEM if fails.
*/
static int __set_output_tf(struct dc_transfer_func *func,
const struct drm_color_lut *lut, uint32_t lut_size,
bool has_rom)
STATIC_IFN_KUNIT int
__set_output_tf(struct dc_transfer_func *func,
const struct drm_color_lut *lut, uint32_t lut_size,
bool has_rom)
{
struct dc_gamma *gamma = NULL;
struct calculate_buffer cal_buffer = {0};
@@ -703,6 +706,7 @@ static int __set_output_tf(struct dc_transfer_func *func,
return res ? 0 : -ENOMEM;
}
EXPORT_IF_KUNIT(__set_output_tf);
/**
* __set_output_tf_32 - calculates the output transfer function based on expected input space.
@@ -714,9 +718,10 @@ static int __set_output_tf(struct dc_transfer_func *func,
* Returns:
* 0 in case of success. -ENOMEM if fails.
*/
static int __set_output_tf_32(struct dc_transfer_func *func,
const struct drm_color_lut32 *lut, uint32_t lut_size,
bool has_rom)
STATIC_IFN_KUNIT int
__set_output_tf_32(struct dc_transfer_func *func,
const struct drm_color_lut32 *lut, uint32_t lut_size,
bool has_rom)
{
struct dc_gamma *gamma = NULL;
struct calculate_buffer cal_buffer = {0};
@@ -759,6 +764,7 @@ static int __set_output_tf_32(struct dc_transfer_func *func,
return res ? 0 : -ENOMEM;
}
EXPORT_IF_KUNIT(__set_output_tf_32);
STATIC_IFN_KUNIT void __set_tf_bypass(struct dc_transfer_func *tf)
{
@@ -820,8 +826,9 @@ EXPORT_IF_KUNIT(amdgpu_dm_set_atomic_regamma);
* Returns:
* 0 in case of success. -ENOMEM if fails.
*/
static int __set_input_tf(struct dc_color_caps *caps, struct dc_transfer_func *func,
const struct drm_color_lut *lut, uint32_t lut_size)
STATIC_IFN_KUNIT int __set_input_tf(struct dc_color_caps *caps,
struct dc_transfer_func *func,
const struct drm_color_lut *lut, uint32_t lut_size)
{
struct dc_gamma *gamma = NULL;
bool res;
@@ -844,6 +851,7 @@ static int __set_input_tf(struct dc_color_caps *caps, struct dc_transfer_func *f
return res ? 0 : -ENOMEM;
}
EXPORT_IF_KUNIT(__set_input_tf);
/**
* __set_input_tf_32 - calculates the input transfer function based on expected
@@ -856,8 +864,9 @@ static int __set_input_tf(struct dc_color_caps *caps, struct dc_transfer_func *f
* Returns:
* 0 in case of success. -ENOMEM if fails.
*/
static int __set_input_tf_32(struct dc_color_caps *caps, struct dc_transfer_func *func,
const struct drm_color_lut32 *lut, uint32_t lut_size)
STATIC_IFN_KUNIT int __set_input_tf_32(struct dc_color_caps *caps,
struct dc_transfer_func *func,
const struct drm_color_lut32 *lut, uint32_t lut_size)
{
struct dc_gamma *gamma = NULL;
bool res;
@@ -880,6 +889,7 @@ static int __set_input_tf_32(struct dc_color_caps *caps, struct dc_transfer_func
return res ? 0 : -ENOMEM;
}
EXPORT_IF_KUNIT(__set_input_tf_32);
STATIC_IFN_KUNIT
enum dc_transfer_func_predefined

View File

@@ -64,6 +64,20 @@ void __drm_ctm_to_dc_matrix(const struct drm_color_ctm *ctm,
struct fixed31_32 *matrix);
void __drm_ctm_3x4_to_dc_matrix(const struct drm_color_ctm_3x4 *ctm,
struct fixed31_32 *matrix);
int __set_legacy_tf(struct dc_transfer_func *func,
const struct drm_color_lut *lut, uint32_t lut_size,
bool has_rom);
int __set_output_tf(struct dc_transfer_func *func,
const struct drm_color_lut *lut, uint32_t lut_size,
bool has_rom);
int __set_output_tf_32(struct dc_transfer_func *func,
const struct drm_color_lut32 *lut, uint32_t lut_size,
bool has_rom);
struct dc_color_caps;
int __set_input_tf(struct dc_color_caps *caps, struct dc_transfer_func *func,
const struct drm_color_lut *lut, uint32_t lut_size);
int __set_input_tf_32(struct dc_color_caps *caps, struct dc_transfer_func *func,
const struct drm_color_lut32 *lut, uint32_t lut_size);
enum dc_transfer_func_predefined
amdgpu_tf_to_dc_tf(enum amdgpu_transfer_function tf);
enum dc_transfer_func_predefined

View File

@@ -1375,6 +1375,146 @@ static void dm_test_set_tf_distributed_points_pq(struct kunit *test)
KUNIT_EXPECT_EQ(test, tf->sdr_ref_white_level, 80U);
}
/**
* dm_test_set_legacy_tf_identity - Legacy identity LUT uses the sRGB ROM path
* @test: KUnit test context
*/
static void dm_test_set_legacy_tf_identity(struct kunit *test)
{
struct drm_color_lut *lut;
struct dc_transfer_func *tf;
int i;
tf = kunit_kzalloc(test, sizeof(*tf), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, tf);
lut = kunit_kcalloc(test, MAX_COLOR_LEGACY_LUT_ENTRIES, sizeof(*lut), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, lut);
for (i = 0; i < MAX_COLOR_LEGACY_LUT_ENTRIES; i++) {
u16 value = i * MAX_DRM_LUT_VALUE / (MAX_COLOR_LEGACY_LUT_ENTRIES - 1);
lut[i].red = value;
lut[i].green = value;
lut[i].blue = value;
}
tf->type = TF_TYPE_PREDEFINED;
tf->tf = TRANSFER_FUNCTION_SRGB;
KUNIT_EXPECT_EQ(test,
__set_legacy_tf(tf, lut, MAX_COLOR_LEGACY_LUT_ENTRIES, true),
0);
}
/**
* dm_test_set_output_tf_linear - Linear output without a LUT calculates degamma
* @test: KUnit test context
*/
static void dm_test_set_output_tf_linear(struct kunit *test)
{
struct dc_transfer_func *tf;
tf = kunit_kzalloc(test, sizeof(*tf), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, tf);
tf->type = TF_TYPE_PREDEFINED;
tf->tf = TRANSFER_FUNCTION_LINEAR;
KUNIT_EXPECT_EQ(test, __set_output_tf(tf, NULL, 0, false), 0);
}
/**
* dm_test_set_output_tf_32_srgb_rom - sRGB output uses the no-LUT ROM path
* @test: KUnit test context
*/
static void dm_test_set_output_tf_32_srgb_rom(struct kunit *test)
{
struct dc_transfer_func *tf;
tf = kunit_kzalloc(test, sizeof(*tf), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, tf);
tf->type = TF_TYPE_PREDEFINED;
tf->tf = TRANSFER_FUNCTION_SRGB;
KUNIT_EXPECT_EQ(test, __set_output_tf_32(tf, NULL, 0, true), 0);
}
/**
* dm_test_set_input_tf_srgb - Predefined sRGB input needs no generated curve
* @test: KUnit test context
*/
static void dm_test_set_input_tf_srgb(struct kunit *test)
{
struct dc_transfer_func *tf;
tf = kunit_kzalloc(test, sizeof(*tf), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, tf);
tf->type = TF_TYPE_PREDEFINED;
tf->tf = TRANSFER_FUNCTION_SRGB;
KUNIT_EXPECT_EQ(test, __set_input_tf(NULL, tf, NULL, 0), 0);
}
/**
* dm_test_set_input_tf_32_srgb - 32-bit input wrapper accepts predefined sRGB
* @test: KUnit test context
*/
static void dm_test_set_input_tf_32_srgb(struct kunit *test)
{
struct dc_transfer_func *tf;
tf = kunit_kzalloc(test, sizeof(*tf), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, tf);
tf->type = TF_TYPE_PREDEFINED;
tf->tf = TRANSFER_FUNCTION_SRGB;
KUNIT_EXPECT_EQ(test, __set_input_tf_32(NULL, tf, NULL, 0), 0);
}
/**
* dm_test_set_transfer_funcs_with_luts - LUT-backed transfer functions succeed
* @test: KUnit test context
*/
static void dm_test_set_transfer_funcs_with_luts(struct kunit *test)
{
struct drm_color_lut32 *lut32;
struct drm_color_lut *lut;
struct dc_transfer_func *tf;
lut = kunit_kcalloc(test, MAX_COLOR_LUT_ENTRIES, sizeof(*lut), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, lut);
lut32 = kunit_kcalloc(test, MAX_COLOR_LUT_ENTRIES, sizeof(*lut32), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, lut32);
tf = kunit_kzalloc(test, sizeof(*tf), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, tf);
tf->type = TF_TYPE_DISTRIBUTED_POINTS;
tf->tf = TRANSFER_FUNCTION_LINEAR;
KUNIT_EXPECT_EQ(test,
__set_output_tf(tf, lut, MAX_COLOR_LUT_ENTRIES, false),
0);
memset(tf, 0, sizeof(*tf));
tf->type = TF_TYPE_DISTRIBUTED_POINTS;
tf->tf = TRANSFER_FUNCTION_LINEAR;
KUNIT_EXPECT_EQ(test,
__set_output_tf_32(tf, lut32, MAX_COLOR_LUT_ENTRIES, false),
0);
memset(tf, 0, sizeof(*tf));
tf->type = TF_TYPE_DISTRIBUTED_POINTS;
tf->tf = TRANSFER_FUNCTION_SRGB;
KUNIT_EXPECT_EQ(test,
__set_input_tf(NULL, tf, lut, MAX_COLOR_LUT_ENTRIES),
0);
memset(tf, 0, sizeof(*tf));
tf->type = TF_TYPE_DISTRIBUTED_POINTS;
tf->tf = TRANSFER_FUNCTION_SRGB;
KUNIT_EXPECT_EQ(test,
__set_input_tf_32(NULL, tf, lut32, MAX_COLOR_LUT_ENTRIES),
0);
}
/**
* dm_test_set_atomic_regamma_bypass - No LUT and linear TF: must take bypass path
* @test: KUnit test context
@@ -2384,6 +2524,13 @@ static struct kunit_case dm_color_test_cases[] = {
/* __set_tf_distributed_points */
KUNIT_CASE(dm_test_set_tf_distributed_points_srgb),
KUNIT_CASE(dm_test_set_tf_distributed_points_pq),
/* Transfer-function calculation helpers */
KUNIT_CASE(dm_test_set_legacy_tf_identity),
KUNIT_CASE(dm_test_set_output_tf_linear),
KUNIT_CASE(dm_test_set_output_tf_32_srgb_rom),
KUNIT_CASE(dm_test_set_input_tf_srgb),
KUNIT_CASE(dm_test_set_input_tf_32_srgb),
KUNIT_CASE(dm_test_set_transfer_funcs_with_luts),
/* amdgpu_dm_set_atomic_regamma */
KUNIT_CASE(dm_test_set_atomic_regamma_bypass),
/* amdgpu_dm_atomic_shaper_lut */