drm/amd/display: Add KUnit tests for amdgpu_dm

Add KUnit tests for pure helper functions in amdgpu_dm.c.

Tests cover:
- dm_plane_layer_index_cmp(): equal, ascending, and descending
  layer_index ordering
- fill_plane_color_attributes(): RGB plus BT601/BT709/BT2020
  full- and limited-range YCbCr, and invalid encoding
- modereset_required(): active vs inactive stream states with
  and without a mode change
- dm_get_oriented_plane_size(): 0/90/180/270 degree rotations
- dm_get_plane_scale(): identity, rotated identity, and
  division-by-zero guard
- is_scaling_state_different(): identical state, scaling mode
  change, and underscan enable/border changes
- is_timing_unchanged_for_freesync(): NULL args, identical
  modes, VRR vtotal/vsync shift, and pixel clock change
- set_freesync_fixed_config(): fixed refresh-rate computation
- is_dc_timing_adjust_needed(): pending hw adjust, VRR
  active-fixed, VRR active-state toggle, and steady state
- set_multisync_trigger_params(): disabled trigger and
  rising/falling edge selection by vsync polarity
- set_master_stream(): highest refresh-rate selection and the
  default-to-first-stream case

Assisted-by: Copilot:Claude-Opus-4.8
Reviewed-by: Bhawanpreet Lakha <bhawanpreet.lakha@amd.com>
Signed-off-by: Alex Hung <alex.hung@amd.com>
Signed-off-by: Chenyu Chen <chen-yu.chen@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-04-30 15:46:34 -06:00 committed by Alex Deucher
parent fcf4919cd8
commit c26b643aa3
5 changed files with 979 additions and 15 deletions

View File

@ -70,6 +70,7 @@
#include "amdgpu_dm_audio.h"
#include "amdgpu_dm_dmub.h"
#include "amdgpu_dm_connector.h"
#include "amdgpu_dm_kunit_helpers.h"
#include "ivsrcid/ivsrcid_vislands30.h"
@ -146,7 +147,7 @@ static void dm_enable_per_frame_crtc_master_sync(struct dc_state *context);
static int amdgpu_dm_atomic_check(struct drm_device *dev,
struct drm_atomic_commit *state);
static bool
STATIC_IFN_KUNIT bool
is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state,
struct drm_crtc_state *new_crtc_state);
@ -247,8 +248,8 @@ static int dm_soft_reset(struct amdgpu_ip_block *ip_block)
return 0;
}
static inline bool is_dc_timing_adjust_needed(struct dm_crtc_state *old_state,
struct dm_crtc_state *new_state)
STATIC_IFN_KUNIT bool is_dc_timing_adjust_needed(struct dm_crtc_state *old_state,
struct dm_crtc_state *new_state)
{
if (new_state->stream->adjust.timing_adjust_pending)
return true;
@ -259,13 +260,14 @@ static inline bool is_dc_timing_adjust_needed(struct dm_crtc_state *old_state,
else
return false;
}
EXPORT_IF_KUNIT(is_dc_timing_adjust_needed);
/*
* DC will program planes with their z-order determined by their ordering
* in the dc_surface_updates array. This comparator is used to sort them
* by descending zpos.
*/
static int dm_plane_layer_index_cmp(const void *a, const void *b)
STATIC_IFN_KUNIT int dm_plane_layer_index_cmp(const void *a, const void *b)
{
const struct dc_surface_update *sa = (struct dc_surface_update *)a;
const struct dc_surface_update *sb = (struct dc_surface_update *)b;
@ -273,6 +275,7 @@ static int dm_plane_layer_index_cmp(const void *a, const void *b)
/* Sort by descending dc_plane layer_index (i.e. normalized_zpos) */
return sb->surface->layer_index - sa->surface->layer_index;
}
EXPORT_IF_KUNIT(dm_plane_layer_index_cmp);
/**
* update_planes_and_stream_adapter() - Send planes to be updated in DC
@ -2980,12 +2983,13 @@ static int dm_early_init(struct amdgpu_ip_block *ip_block)
return dm_init_microcode(adev);
}
static bool modereset_required(struct drm_crtc_state *crtc_state)
STATIC_IFN_KUNIT bool modereset_required(struct drm_crtc_state *crtc_state)
{
return !crtc_state->active && drm_atomic_crtc_needs_modeset(crtc_state);
}
EXPORT_IF_KUNIT(modereset_required);
static int
STATIC_IFN_KUNIT int
fill_plane_color_attributes(const struct drm_plane_state *plane_state,
const enum surface_pixel_format format,
enum dc_color_space *color_space)
@ -3032,6 +3036,7 @@ fill_plane_color_attributes(const struct drm_plane_state *plane_state,
return 0;
}
EXPORT_IF_KUNIT(fill_plane_color_attributes);
static int
fill_dc_plane_info_and_addr(struct amdgpu_device *adev,
@ -3600,7 +3605,7 @@ static void dm_update_pflip_irq_state(struct amdgpu_device *adev,
amdgpu_irq_update(adev, &adev->pageflip_irq, irq_type);
}
static bool
STATIC_IFN_KUNIT bool
is_scaling_state_different(const struct dm_connector_state *dm_state,
const struct dm_connector_state *old_dm_state)
{
@ -3617,6 +3622,7 @@ is_scaling_state_different(const struct dm_connector_state *dm_state,
return true;
return false;
}
EXPORT_IF_KUNIT(is_scaling_state_different);
static bool is_content_protection_different(struct drm_crtc_state *new_crtc_state,
struct drm_crtc_state *old_crtc_state,
@ -5079,7 +5085,7 @@ static int amdgpu_dm_atomic_setup_commit(struct drm_atomic_commit *state)
return 0;
}
static void set_multisync_trigger_params(
STATIC_IFN_KUNIT void set_multisync_trigger_params(
struct dc_stream_state *stream)
{
struct dc_stream_state *master = NULL;
@ -5092,9 +5098,10 @@ static void set_multisync_trigger_params(
stream->triggered_crtc_reset.delay = TRIGGER_DELAY_NEXT_PIXEL;
}
}
EXPORT_IF_KUNIT(set_multisync_trigger_params);
static void set_master_stream(struct dc_stream_state *stream_set[],
int stream_count)
STATIC_IFN_KUNIT void set_master_stream(struct dc_stream_state *stream_set[],
int stream_count)
{
int j, highest_rfr = 0, master_stream = 0;
@ -5115,6 +5122,7 @@ static void set_master_stream(struct dc_stream_state *stream_set[],
stream_set[j]->triggered_crtc_reset.event_source = stream_set[master_stream];
}
}
EXPORT_IF_KUNIT(set_master_stream);
static void dm_enable_per_frame_crtc_master_sync(struct dc_state *context)
{
@ -5560,7 +5568,7 @@ static void reset_freesync_config_for_crtc(
sizeof(new_crtc_state->vrr_infopacket));
}
static bool
STATIC_IFN_KUNIT bool
is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state,
struct drm_crtc_state *new_crtc_state)
{
@ -5589,8 +5597,9 @@ is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state,
return false;
}
EXPORT_IF_KUNIT(is_timing_unchanged_for_freesync);
static void set_freesync_fixed_config(struct dm_crtc_state *dm_new_crtc_state)
STATIC_IFN_KUNIT void set_freesync_fixed_config(struct dm_crtc_state *dm_new_crtc_state)
{
u64 num, den, res;
struct drm_crtc_state *new_crtc_state = &dm_new_crtc_state->base;
@ -5604,6 +5613,7 @@ static void set_freesync_fixed_config(struct dm_crtc_state *dm_new_crtc_state)
res = div_u64(num, den);
dm_new_crtc_state->freesync_config.fixed_refresh_in_uhz = res;
}
EXPORT_IF_KUNIT(set_freesync_fixed_config);
static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
struct drm_atomic_commit *state,
@ -6339,8 +6349,8 @@ static int dm_update_plane_state(struct dc *dc,
return ret;
}
static void dm_get_oriented_plane_size(struct drm_plane_state *plane_state,
int *src_w, int *src_h)
STATIC_IFN_KUNIT void dm_get_oriented_plane_size(struct drm_plane_state *plane_state,
int *src_w, int *src_h)
{
switch (plane_state->rotation & DRM_MODE_ROTATE_MASK) {
case DRM_MODE_ROTATE_90:
@ -6356,8 +6366,9 @@ static void dm_get_oriented_plane_size(struct drm_plane_state *plane_state,
break;
}
}
EXPORT_IF_KUNIT(dm_get_oriented_plane_size);
static void
STATIC_IFN_KUNIT void
dm_get_plane_scale(struct drm_plane_state *plane_state,
int *out_plane_scale_w, int *out_plane_scale_h)
{
@ -6367,6 +6378,7 @@ dm_get_plane_scale(struct drm_plane_state *plane_state,
*out_plane_scale_w = plane_src_w ? plane_state->crtc_w * 1000 / plane_src_w : 0;
*out_plane_scale_h = plane_src_h ? plane_state->crtc_h * 1000 / plane_src_h : 0;
}
EXPORT_IF_KUNIT(dm_get_plane_scale);
/*
* The normalized_zpos value cannot be used by this iterator directly. It's only

View File

@ -1126,4 +1126,25 @@ void amdgpu_dm_emulated_link_detect(struct dc_link *link);
void amdgpu_dm_apply_delay_after_dpcd_poweroff(struct amdgpu_device *adev,
struct dc_sink *sink);
#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
int dm_plane_layer_index_cmp(const void *a, const void *b);
int fill_plane_color_attributes(const struct drm_plane_state *plane_state,
const enum surface_pixel_format format,
enum dc_color_space *color_space);
bool modereset_required(struct drm_crtc_state *crtc_state);
void dm_get_oriented_plane_size(struct drm_plane_state *plane_state,
int *src_w, int *src_h);
void dm_get_plane_scale(struct drm_plane_state *plane_state,
int *out_plane_scale_w, int *out_plane_scale_h);
bool is_scaling_state_different(const struct dm_connector_state *dm_state,
const struct dm_connector_state *old_dm_state);
bool is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state,
struct drm_crtc_state *new_crtc_state);
void set_freesync_fixed_config(struct dm_crtc_state *dm_new_crtc_state);
bool is_dc_timing_adjust_needed(struct dm_crtc_state *old_state,
struct dm_crtc_state *new_state);
void set_multisync_trigger_params(struct dc_stream_state *stream);
void set_master_stream(struct dc_stream_state *stream_set[], int stream_count);
#endif
#endif /* __AMDGPU_DM_H__ */

View File

@ -10,6 +10,7 @@
#define STATIC_IFN_KUNIT
#define INLINE_IFN_KUNIT inline
#define EXPORT_IF_KUNIT(symbol) EXPORT_SYMBOL(symbol)
#else
#define STATIC_IFN_KUNIT static
#define INLINE_IFN_KUNIT

View File

@ -20,3 +20,4 @@ obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_ism_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_wb_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_mst_types_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_pp_smu_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_test.o

View File

@ -0,0 +1,929 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT
/*
* KUnit tests for amdgpu_dm.c
*
* Copyright 2026 Advanced Micro Devices, Inc.
*/
#include <kunit/test.h>
#include "dc.h"
#include "amdgpu_mode.h"
#include "amdgpu_dm.h"
/* Tests for dm_plane_layer_index_cmp() */
/**
* dm_test_plane_layer_index_cmp_equal - Test Plane layer index cmp equal
* @test: The KUnit test context
*/
static void dm_test_plane_layer_index_cmp_equal(struct kunit *test)
{
struct dc_plane_state *plane_a;
struct dc_plane_state *plane_b;
struct dc_surface_update sa, sb;
plane_a = kunit_kzalloc(test, sizeof(*plane_a), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane_a);
plane_b = kunit_kzalloc(test, sizeof(*plane_b), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane_b);
plane_a->layer_index = 5;
plane_b->layer_index = 5;
sa.surface = plane_a;
sb.surface = plane_b;
KUNIT_EXPECT_EQ(test, dm_plane_layer_index_cmp(&sa, &sb), 0);
}
/**
* dm_test_plane_layer_index_cmp_descending - Test Plane layer index cmp descending
* @test: The KUnit test context
*/
static void dm_test_plane_layer_index_cmp_descending(struct kunit *test)
{
struct dc_plane_state *plane_a;
struct dc_plane_state *plane_b;
struct dc_surface_update sa, sb;
plane_a = kunit_kzalloc(test, sizeof(*plane_a), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane_a);
plane_b = kunit_kzalloc(test, sizeof(*plane_b), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane_b);
plane_a->layer_index = 3;
plane_b->layer_index = 7;
sa.surface = plane_a;
sb.surface = plane_b;
/* b has higher index, so cmp(a,b) = b - a > 0 (b sorts first) */
KUNIT_EXPECT_GT(test, dm_plane_layer_index_cmp(&sa, &sb), 0);
}
/**
* dm_test_plane_layer_index_cmp_ascending - Test Plane layer index cmp ascending
* @test: The KUnit test context
*/
static void dm_test_plane_layer_index_cmp_ascending(struct kunit *test)
{
struct dc_plane_state *plane_a;
struct dc_plane_state *plane_b;
struct dc_surface_update sa, sb;
plane_a = kunit_kzalloc(test, sizeof(*plane_a), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane_a);
plane_b = kunit_kzalloc(test, sizeof(*plane_b), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane_b);
plane_a->layer_index = 9;
plane_b->layer_index = 2;
sa.surface = plane_a;
sb.surface = plane_b;
/* a has higher index, so cmp(a,b) = b - a < 0 (a sorts first) */
KUNIT_EXPECT_LT(test, dm_plane_layer_index_cmp(&sa, &sb), 0);
}
/* Tests for fill_plane_color_attributes() */
/**
* dm_test_fill_color_attr_rgb_format - Test Fill color attr rgb format
* @test: The KUnit test context
*/
static void dm_test_fill_color_attr_rgb_format(struct kunit *test)
{
struct drm_plane_state plane_state = { 0 };
enum dc_color_space color_space = COLOR_SPACE_UNKNOWN;
int ret;
/* RGB format: should return 0 and set SRGB regardless of encoding */
plane_state.color_encoding = DRM_COLOR_YCBCR_BT709;
plane_state.color_range = DRM_COLOR_YCBCR_FULL_RANGE;
ret = fill_plane_color_attributes(&plane_state,
SURFACE_PIXEL_FORMAT_GRPH_ARGB8888,
&color_space);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_EQ(test, (int)color_space, (int)COLOR_SPACE_SRGB);
}
/**
* dm_test_fill_color_attr_bt601_full - Test Fill color attr bt601 full
* @test: The KUnit test context
*/
static void dm_test_fill_color_attr_bt601_full(struct kunit *test)
{
struct drm_plane_state plane_state = { 0 };
enum dc_color_space color_space = COLOR_SPACE_UNKNOWN;
int ret;
plane_state.color_encoding = DRM_COLOR_YCBCR_BT601;
plane_state.color_range = DRM_COLOR_YCBCR_FULL_RANGE;
ret = fill_plane_color_attributes(&plane_state,
SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr,
&color_space);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_EQ(test, (int)color_space, (int)COLOR_SPACE_YCBCR601);
}
/**
* dm_test_fill_color_attr_bt601_limited - Test Fill color attr bt601 limited
* @test: The KUnit test context
*/
static void dm_test_fill_color_attr_bt601_limited(struct kunit *test)
{
struct drm_plane_state plane_state = { 0 };
enum dc_color_space color_space = COLOR_SPACE_UNKNOWN;
int ret;
plane_state.color_encoding = DRM_COLOR_YCBCR_BT601;
plane_state.color_range = DRM_COLOR_YCBCR_LIMITED_RANGE;
ret = fill_plane_color_attributes(&plane_state,
SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr,
&color_space);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_EQ(test, (int)color_space,
(int)COLOR_SPACE_YCBCR601_LIMITED);
}
/**
* dm_test_fill_color_attr_bt709_full - Test Fill color attr bt709 full
* @test: The KUnit test context
*/
static void dm_test_fill_color_attr_bt709_full(struct kunit *test)
{
struct drm_plane_state plane_state = { 0 };
enum dc_color_space color_space = COLOR_SPACE_UNKNOWN;
int ret;
plane_state.color_encoding = DRM_COLOR_YCBCR_BT709;
plane_state.color_range = DRM_COLOR_YCBCR_FULL_RANGE;
ret = fill_plane_color_attributes(&plane_state,
SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr,
&color_space);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_EQ(test, (int)color_space, (int)COLOR_SPACE_YCBCR709);
}
/**
* dm_test_fill_color_attr_bt709_limited - Test Fill color attr bt709 limited
* @test: The KUnit test context
*/
static void dm_test_fill_color_attr_bt709_limited(struct kunit *test)
{
struct drm_plane_state plane_state = { 0 };
enum dc_color_space color_space = COLOR_SPACE_UNKNOWN;
int ret;
plane_state.color_encoding = DRM_COLOR_YCBCR_BT709;
plane_state.color_range = DRM_COLOR_YCBCR_LIMITED_RANGE;
ret = fill_plane_color_attributes(&plane_state,
SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr,
&color_space);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_EQ(test, (int)color_space,
(int)COLOR_SPACE_YCBCR709_LIMITED);
}
/**
* dm_test_fill_color_attr_bt2020_full - Test Fill color attr bt2020 full
* @test: The KUnit test context
*/
static void dm_test_fill_color_attr_bt2020_full(struct kunit *test)
{
struct drm_plane_state plane_state = { 0 };
enum dc_color_space color_space = COLOR_SPACE_UNKNOWN;
int ret;
plane_state.color_encoding = DRM_COLOR_YCBCR_BT2020;
plane_state.color_range = DRM_COLOR_YCBCR_FULL_RANGE;
ret = fill_plane_color_attributes(&plane_state,
SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr,
&color_space);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_EQ(test, (int)color_space,
(int)COLOR_SPACE_2020_YCBCR_FULL);
}
/**
* dm_test_fill_color_attr_bt2020_limited - Test Fill color attr bt2020 limited
* @test: The KUnit test context
*/
static void dm_test_fill_color_attr_bt2020_limited(struct kunit *test)
{
struct drm_plane_state plane_state = { 0 };
enum dc_color_space color_space = COLOR_SPACE_UNKNOWN;
int ret;
plane_state.color_encoding = DRM_COLOR_YCBCR_BT2020;
plane_state.color_range = DRM_COLOR_YCBCR_LIMITED_RANGE;
ret = fill_plane_color_attributes(&plane_state,
SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr,
&color_space);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_EQ(test, (int)color_space,
(int)COLOR_SPACE_2020_YCBCR_LIMITED);
}
/**
* dm_test_fill_color_attr_invalid_encoding - Test Fill color attr invalid encoding
* @test: The KUnit test context
*/
static void dm_test_fill_color_attr_invalid_encoding(struct kunit *test)
{
struct drm_plane_state plane_state = { 0 };
enum dc_color_space color_space = COLOR_SPACE_UNKNOWN;
int ret;
plane_state.color_encoding = 99;
plane_state.color_range = DRM_COLOR_YCBCR_FULL_RANGE;
ret = fill_plane_color_attributes(&plane_state,
SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr,
&color_space);
KUNIT_EXPECT_EQ(test, ret, -EINVAL);
}
/* Tests for modereset_required() */
/**
* dm_test_modereset_required_when_inactive_and_modeset - Test Modereset required when inactive and modeset
* @test: The KUnit test context
*/
static void dm_test_modereset_required_when_inactive_and_modeset(struct kunit *test)
{
struct drm_crtc_state crtc_state = { 0 };
crtc_state.active = false;
crtc_state.mode_changed = true;
KUNIT_EXPECT_TRUE(test, modereset_required(&crtc_state));
}
/**
* dm_test_modereset_not_required_when_active_and_modeset - Test Modereset not required when active and modeset
* @test: The KUnit test context
*/
static void dm_test_modereset_not_required_when_active_and_modeset(struct kunit *test)
{
struct drm_crtc_state crtc_state = { 0 };
crtc_state.active = true;
crtc_state.mode_changed = true;
KUNIT_EXPECT_FALSE(test, modereset_required(&crtc_state));
}
/**
* dm_test_modereset_not_required_when_inactive_without_modeset - Test Modereset not required when inactive without modeset
* @test: The KUnit test context
*/
static void dm_test_modereset_not_required_when_inactive_without_modeset(struct kunit *test)
{
struct drm_crtc_state crtc_state = { 0 };
crtc_state.active = false;
crtc_state.mode_changed = false;
KUNIT_EXPECT_FALSE(test, modereset_required(&crtc_state));
}
/* Tests for dm_get_oriented_plane_size() */
/**
* dm_test_oriented_plane_size_rotate_0 - Test Oriented plane size rotate 0
* @test: The KUnit test context
*/
static void dm_test_oriented_plane_size_rotate_0(struct kunit *test)
{
struct drm_plane_state plane_state = { 0 };
int src_w = 0;
int src_h = 0;
plane_state.rotation = DRM_MODE_ROTATE_0;
plane_state.src_w = 1920 << 16;
plane_state.src_h = 1080 << 16;
dm_get_oriented_plane_size(&plane_state, &src_w, &src_h);
KUNIT_EXPECT_EQ(test, src_w, 1920);
KUNIT_EXPECT_EQ(test, src_h, 1080);
}
/**
* dm_test_oriented_plane_size_rotate_90 - Test Oriented plane size rotate 90
* @test: The KUnit test context
*/
static void dm_test_oriented_plane_size_rotate_90(struct kunit *test)
{
struct drm_plane_state plane_state = { 0 };
int src_w = 0;
int src_h = 0;
plane_state.rotation = DRM_MODE_ROTATE_90;
plane_state.src_w = 1920 << 16;
plane_state.src_h = 1080 << 16;
dm_get_oriented_plane_size(&plane_state, &src_w, &src_h);
KUNIT_EXPECT_EQ(test, src_w, 1080);
KUNIT_EXPECT_EQ(test, src_h, 1920);
}
/**
* dm_test_oriented_plane_size_rotate_180 - Test Oriented plane size rotate 180
* @test: The KUnit test context
*/
static void dm_test_oriented_plane_size_rotate_180(struct kunit *test)
{
struct drm_plane_state plane_state = { 0 };
int src_w = 0;
int src_h = 0;
plane_state.rotation = DRM_MODE_ROTATE_180;
plane_state.src_w = 1920 << 16;
plane_state.src_h = 1080 << 16;
dm_get_oriented_plane_size(&plane_state, &src_w, &src_h);
KUNIT_EXPECT_EQ(test, src_w, 1920);
KUNIT_EXPECT_EQ(test, src_h, 1080);
}
/**
* dm_test_oriented_plane_size_rotate_270 - Test Oriented plane size rotate 270
* @test: The KUnit test context
*/
static void dm_test_oriented_plane_size_rotate_270(struct kunit *test)
{
struct drm_plane_state plane_state = { 0 };
int src_w = 0;
int src_h = 0;
plane_state.rotation = DRM_MODE_ROTATE_270;
plane_state.src_w = 1920 << 16;
plane_state.src_h = 1080 << 16;
dm_get_oriented_plane_size(&plane_state, &src_w, &src_h);
KUNIT_EXPECT_EQ(test, src_w, 1080);
KUNIT_EXPECT_EQ(test, src_h, 1920);
}
/* Tests for dm_get_plane_scale() */
/**
* dm_test_get_plane_scale_identity - Test Get plane scale identity
* @test: The KUnit test context
*/
static void dm_test_get_plane_scale_identity(struct kunit *test)
{
struct drm_plane_state plane_state = { 0 };
int scale_w = 0;
int scale_h = 0;
plane_state.rotation = DRM_MODE_ROTATE_0;
plane_state.src_w = 1920 << 16;
plane_state.src_h = 1080 << 16;
plane_state.crtc_w = 1920;
plane_state.crtc_h = 1080;
dm_get_plane_scale(&plane_state, &scale_w, &scale_h);
KUNIT_EXPECT_EQ(test, scale_w, 1000);
KUNIT_EXPECT_EQ(test, scale_h, 1000);
}
/**
* dm_test_get_plane_scale_rotate_90_identity - Test Get plane scale rotate 90 identity
* @test: The KUnit test context
*/
static void dm_test_get_plane_scale_rotate_90_identity(struct kunit *test)
{
struct drm_plane_state plane_state = { 0 };
int scale_w = 0;
int scale_h = 0;
plane_state.rotation = DRM_MODE_ROTATE_90;
plane_state.src_w = 1920 << 16;
plane_state.src_h = 1080 << 16;
plane_state.crtc_w = 1080;
plane_state.crtc_h = 1920;
dm_get_plane_scale(&plane_state, &scale_w, &scale_h);
KUNIT_EXPECT_EQ(test, scale_w, 1000);
KUNIT_EXPECT_EQ(test, scale_h, 1000);
}
/**
* dm_test_get_plane_scale_zero_src_width - Test Get plane scale zero src width
* @test: The KUnit test context
*/
static void dm_test_get_plane_scale_zero_src_width(struct kunit *test)
{
struct drm_plane_state plane_state = { 0 };
int scale_w = 0;
int scale_h = 0;
plane_state.rotation = DRM_MODE_ROTATE_0;
plane_state.src_w = 0;
plane_state.src_h = 1080 << 16;
plane_state.crtc_w = 100;
plane_state.crtc_h = 200;
dm_get_plane_scale(&plane_state, &scale_w, &scale_h);
KUNIT_EXPECT_EQ(test, scale_w, 0);
KUNIT_EXPECT_EQ(test, scale_h, 185);
}
/* Tests for is_scaling_state_different() */
/**
* dm_test_scaling_state_same - Test identical scaling states compare equal
* @test: The KUnit test context
*/
static void dm_test_scaling_state_same(struct kunit *test)
{
struct dm_connector_state a = { 0 };
struct dm_connector_state b = { 0 };
a.scaling = RMX_FULL;
a.underscan_enable = false;
b = a;
KUNIT_EXPECT_FALSE(test, is_scaling_state_different(&a, &b));
}
/**
* dm_test_scaling_state_scaling_changed - Test differing scaling mode is detected
* @test: The KUnit test context
*/
static void dm_test_scaling_state_scaling_changed(struct kunit *test)
{
struct dm_connector_state a = { 0 };
struct dm_connector_state b = { 0 };
a.scaling = RMX_FULL;
b.scaling = RMX_CENTER;
KUNIT_EXPECT_TRUE(test, is_scaling_state_different(&a, &b));
}
/**
* dm_test_scaling_state_underscan_enabled - Test enabling underscan with borders differs
* @test: The KUnit test context
*/
static void dm_test_scaling_state_underscan_enabled(struct kunit *test)
{
struct dm_connector_state old_state = { 0 };
struct dm_connector_state new_state = { 0 };
/* new enables underscan with non-zero borders, old has it disabled */
new_state.underscan_enable = true;
new_state.underscan_hborder = 16;
new_state.underscan_vborder = 16;
old_state.underscan_enable = false;
KUNIT_EXPECT_TRUE(test, is_scaling_state_different(&new_state, &old_state));
}
/**
* dm_test_scaling_state_underscan_border_changed - Test changed underscan borders differ
* @test: The KUnit test context
*/
static void dm_test_scaling_state_underscan_border_changed(struct kunit *test)
{
struct dm_connector_state a = { 0 };
struct dm_connector_state b = { 0 };
a.underscan_enable = true;
a.underscan_hborder = 16;
a.underscan_vborder = 16;
b = a;
b.underscan_hborder = 32;
KUNIT_EXPECT_TRUE(test, is_scaling_state_different(&a, &b));
}
/* Tests for is_timing_unchanged_for_freesync() */
/**
* dm_test_timing_unchanged_null_args - Test NULL crtc states return false
* @test: The KUnit test context
*/
static void dm_test_timing_unchanged_null_args(struct kunit *test)
{
struct drm_crtc_state crtc_state = { 0 };
KUNIT_EXPECT_FALSE(test,
is_timing_unchanged_for_freesync(NULL, &crtc_state));
KUNIT_EXPECT_FALSE(test,
is_timing_unchanged_for_freesync(&crtc_state, NULL));
}
/**
* dm_test_timing_unchanged_identical_modes - Test identical modes are not "unchanged"
* @test: The KUnit test context
*
* The helper only returns true when vtotal/vsync shift (vrr) while the rest
* of the timing stays fixed, so identical modes must return false.
*/
static void dm_test_timing_unchanged_identical_modes(struct kunit *test)
{
struct drm_crtc_state old_state = { 0 };
struct drm_crtc_state new_state = { 0 };
old_state.mode.clock = 148500;
old_state.mode.hdisplay = 1920;
old_state.mode.vdisplay = 1080;
old_state.mode.htotal = 2200;
old_state.mode.vtotal = 1125;
new_state.mode = old_state.mode;
KUNIT_EXPECT_FALSE(test,
is_timing_unchanged_for_freesync(&old_state, &new_state));
}
/**
* dm_test_timing_unchanged_vrr_shift - Test vrr-style vtotal/vsync shift is detected
* @test: The KUnit test context
*/
static void dm_test_timing_unchanged_vrr_shift(struct kunit *test)
{
struct drm_crtc_state old_state = { 0 };
struct drm_crtc_state new_state = { 0 };
old_state.mode.clock = 148500;
old_state.mode.hdisplay = 1920;
old_state.mode.vdisplay = 1080;
old_state.mode.htotal = 2200;
old_state.mode.vtotal = 1125;
old_state.mode.hsync_start = 2008;
old_state.mode.vsync_start = 1084;
old_state.mode.hsync_end = 2052;
old_state.mode.vsync_end = 1089;
/* Same horizontal timing, vertical totals/sync shifted by 125 lines */
new_state.mode = old_state.mode;
new_state.mode.vtotal = 1250;
new_state.mode.vsync_start = 1209;
new_state.mode.vsync_end = 1214;
KUNIT_EXPECT_TRUE(test,
is_timing_unchanged_for_freesync(&old_state, &new_state));
}
/**
* dm_test_timing_unchanged_clock_changed - Test pixel clock change returns false
* @test: The KUnit test context
*/
static void dm_test_timing_unchanged_clock_changed(struct kunit *test)
{
struct drm_crtc_state old_state = { 0 };
struct drm_crtc_state new_state = { 0 };
old_state.mode.clock = 148500;
old_state.mode.htotal = 2200;
old_state.mode.vtotal = 1125;
old_state.mode.vsync_start = 1084;
old_state.mode.vsync_end = 1089;
new_state.mode = old_state.mode;
new_state.mode.clock = 297000;
new_state.mode.vtotal = 1250;
new_state.mode.vsync_start = 1209;
new_state.mode.vsync_end = 1214;
KUNIT_EXPECT_FALSE(test,
is_timing_unchanged_for_freesync(&old_state, &new_state));
}
/* Tests for set_freesync_fixed_config() */
/**
* dm_test_set_freesync_fixed_config_60hz - Test fixed refresh computed for 1080p60
* @test: The KUnit test context
*/
static void dm_test_set_freesync_fixed_config_60hz(struct kunit *test)
{
struct dm_crtc_state dm_crtc_state = { 0 };
dm_crtc_state.base.mode.clock = 148500;
dm_crtc_state.base.mode.htotal = 2200;
dm_crtc_state.base.mode.vtotal = 1125;
set_freesync_fixed_config(&dm_crtc_state);
KUNIT_EXPECT_EQ(test, (int)dm_crtc_state.freesync_config.state,
(int)VRR_STATE_ACTIVE_FIXED);
/* 148500 kHz / (2200 * 1125) = 60 Hz = 60000000 uHz */
KUNIT_EXPECT_EQ(test, dm_crtc_state.freesync_config.fixed_refresh_in_uhz,
60000000U);
}
/* Tests for is_dc_timing_adjust_needed() */
/**
* dm_test_dc_timing_adjust_pending - Test a pending hw timing adjust forces true
* @test: The KUnit test context
*/
static void dm_test_dc_timing_adjust_pending(struct kunit *test)
{
struct dm_crtc_state *old_state, *new_state;
struct dc_stream_state *stream;
old_state = kunit_kzalloc(test, sizeof(*old_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_state);
new_state = kunit_kzalloc(test, sizeof(*new_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_state);
stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
new_state->stream = stream;
stream->adjust.timing_adjust_pending = 1;
KUNIT_EXPECT_TRUE(test, is_dc_timing_adjust_needed(old_state, new_state));
}
/**
* dm_test_dc_timing_adjust_active_fixed - Test VRR active-fixed forces true
* @test: The KUnit test context
*/
static void dm_test_dc_timing_adjust_active_fixed(struct kunit *test)
{
struct dm_crtc_state *old_state, *new_state;
struct dc_stream_state *stream;
old_state = kunit_kzalloc(test, sizeof(*old_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_state);
new_state = kunit_kzalloc(test, sizeof(*new_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_state);
stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
new_state->stream = stream;
new_state->freesync_config.state = VRR_STATE_ACTIVE_FIXED;
KUNIT_EXPECT_TRUE(test, is_dc_timing_adjust_needed(old_state, new_state));
}
/**
* dm_test_dc_timing_adjust_vrr_toggle - Test a change in vrr active state forces true
* @test: The KUnit test context
*/
static void dm_test_dc_timing_adjust_vrr_toggle(struct kunit *test)
{
struct dm_crtc_state *old_state, *new_state;
struct dc_stream_state *stream;
old_state = kunit_kzalloc(test, sizeof(*old_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_state);
new_state = kunit_kzalloc(test, sizeof(*new_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_state);
stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
new_state->stream = stream;
old_state->freesync_config.state = VRR_STATE_ACTIVE_VARIABLE;
new_state->freesync_config.state = VRR_STATE_INACTIVE;
KUNIT_EXPECT_TRUE(test, is_dc_timing_adjust_needed(old_state, new_state));
}
/**
* dm_test_dc_timing_adjust_not_needed - Test steady-state timing needs no adjust
* @test: The KUnit test context
*/
static void dm_test_dc_timing_adjust_not_needed(struct kunit *test)
{
struct dm_crtc_state *old_state, *new_state;
struct dc_stream_state *stream;
old_state = kunit_kzalloc(test, sizeof(*old_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_state);
new_state = kunit_kzalloc(test, sizeof(*new_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_state);
stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
new_state->stream = stream;
old_state->freesync_config.state = VRR_STATE_INACTIVE;
new_state->freesync_config.state = VRR_STATE_INACTIVE;
KUNIT_EXPECT_FALSE(test, is_dc_timing_adjust_needed(old_state, new_state));
}
/* Tests for set_multisync_trigger_params() */
/**
* dm_test_multisync_trigger_disabled - Test disabled reset leaves params untouched
* @test: The KUnit test context
*/
static void dm_test_multisync_trigger_disabled(struct kunit *test)
{
struct dc_stream_state *stream;
stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
stream->triggered_crtc_reset.enabled = false;
stream->triggered_crtc_reset.event = CRTC_EVENT_VSYNC_FALLING;
stream->triggered_crtc_reset.delay = TRIGGER_DELAY_NEXT_LINE;
set_multisync_trigger_params(stream);
/* Nothing should change when the reset trigger is disabled */
KUNIT_EXPECT_EQ(test, (int)stream->triggered_crtc_reset.event,
(int)CRTC_EVENT_VSYNC_FALLING);
KUNIT_EXPECT_EQ(test, (int)stream->triggered_crtc_reset.delay,
(int)TRIGGER_DELAY_NEXT_LINE);
}
/**
* dm_test_multisync_trigger_rising - Test positive vsync polarity selects rising edge
* @test: The KUnit test context
*/
static void dm_test_multisync_trigger_rising(struct kunit *test)
{
struct dc_stream_state *stream;
struct dc_stream_state *master;
stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
master = kunit_kzalloc(test, sizeof(*master), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, master);
master->timing.flags.VSYNC_POSITIVE_POLARITY = 1;
stream->triggered_crtc_reset.enabled = true;
stream->triggered_crtc_reset.event_source = master;
set_multisync_trigger_params(stream);
KUNIT_EXPECT_EQ(test, (int)stream->triggered_crtc_reset.event,
(int)CRTC_EVENT_VSYNC_RISING);
KUNIT_EXPECT_EQ(test, (int)stream->triggered_crtc_reset.delay,
(int)TRIGGER_DELAY_NEXT_PIXEL);
}
/**
* dm_test_multisync_trigger_falling - Test negative vsync polarity selects falling edge
* @test: The KUnit test context
*/
static void dm_test_multisync_trigger_falling(struct kunit *test)
{
struct dc_stream_state *stream;
struct dc_stream_state *master;
stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
master = kunit_kzalloc(test, sizeof(*master), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, master);
master->timing.flags.VSYNC_POSITIVE_POLARITY = 0;
stream->triggered_crtc_reset.enabled = true;
stream->triggered_crtc_reset.event_source = master;
set_multisync_trigger_params(stream);
KUNIT_EXPECT_EQ(test, (int)stream->triggered_crtc_reset.event,
(int)CRTC_EVENT_VSYNC_FALLING);
KUNIT_EXPECT_EQ(test, (int)stream->triggered_crtc_reset.delay,
(int)TRIGGER_DELAY_NEXT_PIXEL);
}
/* Tests for set_master_stream() */
/**
* dm_test_master_stream_highest_refresh - Test highest refresh-rate stream becomes master
* @test: The KUnit test context
*/
static void dm_test_master_stream_highest_refresh(struct kunit *test)
{
struct dc_stream_state *stream0, *stream1;
struct dc_stream_state *stream_set[2];
stream0 = kunit_kzalloc(test, sizeof(*stream0), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream0);
stream1 = kunit_kzalloc(test, sizeof(*stream1), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream1);
stream_set[0] = stream0;
stream_set[1] = stream1;
/* stream0: 60Hz, stream1: 120Hz -> stream1 is master */
stream0->triggered_crtc_reset.enabled = true;
stream0->timing.pix_clk_100hz = 1485000;
stream0->timing.h_total = 2200;
stream0->timing.v_total = 1125;
stream1->triggered_crtc_reset.enabled = true;
stream1->timing.pix_clk_100hz = 2970000;
stream1->timing.h_total = 2200;
stream1->timing.v_total = 1125;
set_master_stream(stream_set, 2);
KUNIT_EXPECT_PTR_EQ(test, stream0->triggered_crtc_reset.event_source,
stream1);
KUNIT_EXPECT_PTR_EQ(test, stream1->triggered_crtc_reset.event_source,
stream1);
}
/**
* dm_test_master_stream_defaults_to_first - Test default master when none triggered
* @test: The KUnit test context
*
* When no stream has the reset trigger enabled, master_stream stays 0 and all
* streams point at the first stream as their event source.
*/
static void dm_test_master_stream_defaults_to_first(struct kunit *test)
{
struct dc_stream_state *stream0, *stream1;
struct dc_stream_state *stream_set[2];
stream0 = kunit_kzalloc(test, sizeof(*stream0), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream0);
stream1 = kunit_kzalloc(test, sizeof(*stream1), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream1);
stream_set[0] = stream0;
stream_set[1] = stream1;
set_master_stream(stream_set, 2);
KUNIT_EXPECT_PTR_EQ(test, stream0->triggered_crtc_reset.event_source,
stream0);
KUNIT_EXPECT_PTR_EQ(test, stream1->triggered_crtc_reset.event_source,
stream0);
}
static struct kunit_case amdgpu_dm_tests[] = {
/* dm_plane_layer_index_cmp */
KUNIT_CASE(dm_test_plane_layer_index_cmp_equal),
KUNIT_CASE(dm_test_plane_layer_index_cmp_descending),
KUNIT_CASE(dm_test_plane_layer_index_cmp_ascending),
/* fill_plane_color_attributes */
KUNIT_CASE(dm_test_fill_color_attr_rgb_format),
KUNIT_CASE(dm_test_fill_color_attr_bt601_full),
KUNIT_CASE(dm_test_fill_color_attr_bt601_limited),
KUNIT_CASE(dm_test_fill_color_attr_bt709_full),
KUNIT_CASE(dm_test_fill_color_attr_bt709_limited),
KUNIT_CASE(dm_test_fill_color_attr_bt2020_full),
KUNIT_CASE(dm_test_fill_color_attr_bt2020_limited),
KUNIT_CASE(dm_test_fill_color_attr_invalid_encoding),
/* modereset_required */
KUNIT_CASE(dm_test_modereset_required_when_inactive_and_modeset),
KUNIT_CASE(dm_test_modereset_not_required_when_active_and_modeset),
KUNIT_CASE(dm_test_modereset_not_required_when_inactive_without_modeset),
/* dm_get_oriented_plane_size */
KUNIT_CASE(dm_test_oriented_plane_size_rotate_0),
KUNIT_CASE(dm_test_oriented_plane_size_rotate_90),
KUNIT_CASE(dm_test_oriented_plane_size_rotate_180),
KUNIT_CASE(dm_test_oriented_plane_size_rotate_270),
/* dm_get_plane_scale */
KUNIT_CASE(dm_test_get_plane_scale_identity),
KUNIT_CASE(dm_test_get_plane_scale_rotate_90_identity),
KUNIT_CASE(dm_test_get_plane_scale_zero_src_width),
/* is_scaling_state_different */
KUNIT_CASE(dm_test_scaling_state_same),
KUNIT_CASE(dm_test_scaling_state_scaling_changed),
KUNIT_CASE(dm_test_scaling_state_underscan_enabled),
KUNIT_CASE(dm_test_scaling_state_underscan_border_changed),
/* is_timing_unchanged_for_freesync */
KUNIT_CASE(dm_test_timing_unchanged_null_args),
KUNIT_CASE(dm_test_timing_unchanged_identical_modes),
KUNIT_CASE(dm_test_timing_unchanged_vrr_shift),
KUNIT_CASE(dm_test_timing_unchanged_clock_changed),
/* set_freesync_fixed_config */
KUNIT_CASE(dm_test_set_freesync_fixed_config_60hz),
/* is_dc_timing_adjust_needed */
KUNIT_CASE(dm_test_dc_timing_adjust_pending),
KUNIT_CASE(dm_test_dc_timing_adjust_active_fixed),
KUNIT_CASE(dm_test_dc_timing_adjust_vrr_toggle),
KUNIT_CASE(dm_test_dc_timing_adjust_not_needed),
/* set_multisync_trigger_params */
KUNIT_CASE(dm_test_multisync_trigger_disabled),
KUNIT_CASE(dm_test_multisync_trigger_rising),
KUNIT_CASE(dm_test_multisync_trigger_falling),
/* set_master_stream */
KUNIT_CASE(dm_test_master_stream_highest_refresh),
KUNIT_CASE(dm_test_master_stream_defaults_to_first),
{}
};
static struct kunit_suite amdgpu_dm_test_suite = {
.name = "amdgpu_dm",
.test_cases = amdgpu_dm_tests,
};
kunit_test_suite(amdgpu_dm_test_suite);
MODULE_AUTHOR("AMD");
MODULE_DESCRIPTION("KUnit tests for amdgpu_dm");
MODULE_LICENSE("Dual MIT/GPL");