diff options
| author | Alex Hung <alex.hung@amd.com> | 2026-06-15 18:54:53 -0600 |
|---|---|---|
| committer | Alex Deucher <alexander.deucher@amd.com> | 2026-07-01 11:37:06 -0400 |
| commit | d99024d7d243aecec42b98ac000e720499cb3e92 (patch) | |
| tree | a9378b60ba7b39e2e2687f650d5bef4e0fae977d /drivers/gpu/drm/amd/display/amdgpu_dm | |
| parent | 682710244fa3176f642eecd4c1a27e69be2e3a7f (diff) | |
| download | linux-next-d99024d7d243aecec42b98ac000e720499cb3e92.tar.gz linux-next-d99024d7d243aecec42b98ac000e720499cb3e92.zip | |
drm/amd/display: Add KUnit tests for amdgpu_dm_replay
[WHAT]
Add KUnit coverage for amdgpu_dm_set_replay_caps(),
amdgpu_dm_link_setup_replay(), and amdgpu_dm_replay_set_event()
including happy-path tests that exercise the configuration logic,
coasting vtotal calculations, and early-return when replay events
are already in the desired state.
Assisted-by: Copilot:Claude-Opus-4.6 GPT-5.5
Reviewed-by: Bhawanpreet Lakha <bhawanpreet.lakha@amd.com>
Signed-off-by: Alex Hung <alex.hung@amd.com>
Signed-off-by: George Zhang <george.zhang@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Diffstat (limited to 'drivers/gpu/drm/amd/display/amdgpu_dm')
| -rw-r--r-- | drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.c | 3 | ||||
| -rw-r--r-- | drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_replay_test.c | 437 |
2 files changed, 436 insertions, 4 deletions
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.c index f3cea2aba901..42e17119461d 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.c @@ -128,6 +128,7 @@ bool amdgpu_dm_set_replay_caps(struct dc_link *link, struct amdgpu_dm_connector return true; } +EXPORT_IF_KUNIT(amdgpu_dm_set_replay_caps); /* * amdgpu_dm_link_setup_replay() - config replay settings @@ -166,6 +167,7 @@ bool amdgpu_dm_link_setup_replay(struct dc_stream_state *stream, static_coasting_vtotal); return true; } +EXPORT_IF_KUNIT(amdgpu_dm_link_setup_replay); /* * amdgpu_dm_replay_set_event() - set or clear replay event for a stream @@ -205,3 +207,4 @@ bool amdgpu_dm_replay_set_event(struct amdgpu_display_manager *dm, return mod_power_set_replay_event(dm->power_module, stream, set_event, event, wait_for_disable); } +EXPORT_IF_KUNIT(amdgpu_dm_replay_set_event); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_replay_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_replay_test.c index 28ff8bbcc0f7..68f2f4d70407 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_replay_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_replay_test.c @@ -8,12 +8,12 @@ #include <kunit/test.h> #include "dc.h" +#include "dc_dmub_srv.h" #include "amdgpu_mode.h" #include "amdgpu_dm.h" - -/* Extern declaration for the function under test */ -extern bool amdgpu_dm_link_supports_replay(struct dc_link *link, - struct amdgpu_dm_connector *aconnector); +#include "amdgpu_dm_replay.h" +#include "modules/power/power_helpers.h" +#include "dmub/dmub_srv.h" /* * Helper: allocate a dc_link, amdgpu_dm_connector, and dm_connector_state @@ -23,6 +23,9 @@ struct replay_test_ctx { struct dc_link *link; struct amdgpu_dm_connector *aconnector; struct dm_connector_state *dm_state; + struct dc *dc; + struct dc_context *dc_ctx; + struct dc_stream_state *stream; }; static struct replay_test_ctx *alloc_replay_ctx(struct kunit *test) @@ -41,8 +44,21 @@ static struct replay_test_ctx *alloc_replay_ctx(struct kunit *test) ctx->dm_state = kunit_kzalloc(test, sizeof(*ctx->dm_state), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, ctx->dm_state); + ctx->dc = kunit_kzalloc(test, sizeof(*ctx->dc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->dc); + + ctx->dc_ctx = kunit_kzalloc(test, sizeof(*ctx->dc_ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->dc_ctx); + + ctx->stream = kunit_kzalloc(test, sizeof(*ctx->stream), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->stream); + /* Wire connector state so to_dm_connector_state() works */ ctx->aconnector->base.state = &ctx->dm_state->base; + ctx->link->ctx = ctx->dc_ctx; + ctx->dc_ctx->dc = ctx->dc; + ctx->dc->ctx = ctx->dc_ctx; + ctx->stream->link = ctx->link; return ctx; } @@ -55,6 +71,7 @@ static void set_all_replay_caps(struct replay_test_ctx *ctx) { ctx->dm_state->freesync_capable = true; ctx->aconnector->vsdb_info.replay_mode = true; + ctx->link->connector_signal = SIGNAL_TYPE_EDP; ctx->link->dpcd_caps.edp_rev = EDP_REVISION_13; ctx->link->dpcd_caps.alpm_caps.bits.AUX_WAKE_ALPM_CAP = 1; ctx->link->dpcd_caps.adaptive_sync_caps.dp_adap_sync_caps.bits.ADAPTIVE_SYNC_SDP_SUPPORT = 1; @@ -181,7 +198,398 @@ static void dm_test_replay_both_deviations_zero(struct kunit *test) /* End of tests for amdgpu_dm_link_supports_replay() */ +/* Tests for amdgpu_dm_set_replay_caps() */ + +/** + * dm_test_replay_set_caps_already_supported - Verify cached Replay support + * @test: KUnit test context + * + * When replay_supported is already set, amdgpu_dm_set_replay_caps() should + * return true without revalidating the link capabilities. + */ +static void dm_test_replay_set_caps_already_supported(struct kunit *test) +{ + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + + ctx->link->replay_settings.config.replay_supported = true; + + KUNIT_EXPECT_TRUE(test, amdgpu_dm_set_replay_caps(ctx->link, ctx->aconnector)); +} + +/** + * dm_test_replay_set_caps_non_embedded_signal - Verify non-eDP rejection + * @test: KUnit test context + * + * When the link signal is not embedded, amdgpu_dm_set_replay_caps() should + * reject Replay even if the sink capability fields are otherwise valid. + */ +static void dm_test_replay_set_caps_non_embedded_signal(struct kunit *test) +{ + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + + set_all_replay_caps(ctx); + ctx->link->connector_signal = SIGNAL_TYPE_DISPLAY_PORT; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_set_replay_caps(ctx->link, ctx->aconnector)); +} + +/** + * dm_test_replay_set_caps_disallowed_by_panel - Verify panel policy rejection + * @test: KUnit test context + * + * When the panel configuration disallows Replay, amdgpu_dm_set_replay_caps() + * should return false before accepting the capability set. + */ +static void dm_test_replay_set_caps_disallowed_by_panel(struct kunit *test) +{ + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + + set_all_replay_caps(ctx); + ctx->link->panel_config.psr.disallow_replay = true; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_set_replay_caps(ctx->link, ctx->aconnector)); +} + +/** + * dm_test_replay_set_caps_link_not_supported - Verify capability rejection + * @test: KUnit test context + * + * When amdgpu_dm_link_supports_replay() rejects the link, the higher-level + * Replay setup helper should also return false. + */ +static void dm_test_replay_set_caps_link_not_supported(struct kunit *test) +{ + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + + set_all_replay_caps(ctx); + ctx->dm_state->freesync_capable = false; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_set_replay_caps(ctx->link, ctx->aconnector)); +} + +/** + * dm_test_replay_set_caps_missing_dmub_srv - Verify missing DMUB rejection + * @test: KUnit test context + * + * When the link and connector support Replay but no DMUB service is available, + * amdgpu_dm_set_replay_caps() should return false. + */ +static void dm_test_replay_set_caps_missing_dmub_srv(struct kunit *test) +{ + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + + set_all_replay_caps(ctx); + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_set_replay_caps(ctx->link, ctx->aconnector)); +} + +/** + * dm_test_replay_set_caps_success - Verify successful Replay configuration + * @test: KUnit test context + * + * When all prerequisites are met (embedded signal, panel allows replay, link + * supports replay, DMUB present with replay support), amdgpu_dm_set_replay_caps() + * should configure the link replay settings and return true. + */ +static void dm_test_replay_set_caps_success(struct kunit *test) +{ + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + struct dc_dmub_srv *dmub_srv; + struct dmub_srv *dmub; + + set_all_replay_caps(ctx); + + dmub_srv = kunit_kzalloc(test, sizeof(*dmub_srv), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dmub_srv); + + dmub = kunit_kzalloc(test, sizeof(*dmub), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dmub); + + dmub->feature_caps.replay_supported = 1; + dmub_srv->dmub = dmub; + ctx->dc_ctx->dmub_srv = dmub_srv; + + KUNIT_EXPECT_TRUE(test, amdgpu_dm_set_replay_caps(ctx->link, ctx->aconnector)); + KUNIT_EXPECT_TRUE(test, ctx->link->replay_settings.config.replay_supported); +} + +/* Tests for amdgpu_dm_link_setup_replay() */ + +/** + * dm_test_replay_link_setup_null_stream - Verify NULL stream rejection + * @test: KUnit test context + * + * amdgpu_dm_link_setup_replay() should return false when no stream is provided. + */ +static void dm_test_replay_link_setup_null_stream(struct kunit *test) +{ + struct mod_vrr_params vrr_params = { 0 }; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_link_setup_replay(NULL, &vrr_params)); +} + +/** + * dm_test_replay_link_setup_null_link - Verify NULL stream link rejection + * @test: KUnit test context + * + * amdgpu_dm_link_setup_replay() should return false when the stream has no + * associated link. + */ +static void dm_test_replay_link_setup_null_link(struct kunit *test) +{ + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + struct mod_vrr_params vrr_params = { 0 }; + + ctx->stream->link = NULL; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_link_setup_replay(ctx->stream, &vrr_params)); +} + +/** + * dm_test_replay_link_setup_null_vrr_params - Verify NULL VRR params rejection + * @test: KUnit test context + * + * amdgpu_dm_link_setup_replay() should return false when VRR parameters are + * not supplied. + */ +static void dm_test_replay_link_setup_null_vrr_params(struct kunit *test) +{ + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_link_setup_replay(ctx->stream, NULL)); +} + +/** + * dm_test_replay_link_setup_not_supported - Verify unsupported Replay rejection + * @test: KUnit test context + * + * amdgpu_dm_link_setup_replay() should return false when Replay is not marked + * supported on the link configuration. + */ +static void dm_test_replay_link_setup_not_supported(struct kunit *test) +{ + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + struct mod_vrr_params vrr_params = { 0 }; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_link_setup_replay(ctx->stream, &vrr_params)); +} + +/** + * dm_test_replay_link_setup_already_enabled - Verify enabled Replay success + * @test: KUnit test context + * + * When Replay is already enabled, amdgpu_dm_link_setup_replay() should return + * true without recalculating coasting vtotal state. + */ +static void dm_test_replay_link_setup_already_enabled(struct kunit *test) +{ + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + struct mod_vrr_params vrr_params = { 0 }; + + ctx->link->replay_settings.config.replay_supported = true; + ctx->link->replay_settings.replay_feature_enabled = true; + + KUNIT_EXPECT_TRUE(test, amdgpu_dm_link_setup_replay(ctx->stream, &vrr_params)); +} + +/** + * dm_test_replay_link_setup_success - Verify coasting vtotal configuration + * @test: KUnit test context + * + * When Replay is supported but not yet enabled, amdgpu_dm_link_setup_replay() + * should calculate the link-off frame count and set the coasting vtotal values, + * then return true. + */ +static void dm_test_replay_link_setup_success(struct kunit *test) +{ + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + struct mod_vrr_params vrr_params = { 0 }; + + ctx->link->replay_settings.config.replay_supported = true; + ctx->link->replay_settings.config.replay_version = DC_FREESYNC_REPLAY; + + /* Set timing so calculate_replay_link_off_frame_count computes */ + ctx->stream->timing.v_total = 1125; + ctx->stream->timing.h_total = 2200; + ctx->stream->timing.pix_clk_100hz = 1485000; + ctx->link->dpcd_caps.pr_info.pixel_deviation_per_line = 4; + ctx->link->dpcd_caps.pr_info.max_deviation_line = 10; + + /* min_refresh_in_uhz = 0 makes calc return v_total directly */ + vrr_params.min_refresh_in_uhz = 0; + + KUNIT_EXPECT_TRUE(test, amdgpu_dm_link_setup_replay(ctx->stream, &vrr_params)); + + /* Verify coasting vtotal was set */ + KUNIT_EXPECT_EQ(test, + ctx->link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_NOM], + (uint32_t)1125); + KUNIT_EXPECT_EQ(test, + ctx->link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_STATIC], + (uint32_t)1125); + + /* Verify link_off_frame_count was calculated: 2200*10/(4*1125) = 4 */ + KUNIT_EXPECT_EQ(test, + ctx->link->replay_settings.link_off_frame_count, + (uint32_t)4); +} + +/* Tests for amdgpu_dm_replay_set_event() */ + +/** + * dm_test_replay_set_event_null_stream - Verify NULL stream rejection + * @test: KUnit test context + * + * amdgpu_dm_replay_set_event() should return false when no stream is provided. + */ +static void dm_test_replay_set_event_null_stream(struct kunit *test) +{ + struct amdgpu_display_manager *dm; + + dm = kunit_kzalloc(test, sizeof(*dm), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm); + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_replay_set_event(dm, NULL, true, + replay_event_vsync, false)); +} + +/** + * dm_test_replay_set_event_null_link - Verify NULL stream link rejection + * @test: KUnit test context + * + * amdgpu_dm_replay_set_event() should return false when the stream has no + * associated link. + */ +static void dm_test_replay_set_event_null_link(struct kunit *test) +{ + struct amdgpu_display_manager *dm; + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + + dm = kunit_kzalloc(test, sizeof(*dm), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm); + + ctx->stream->link = NULL; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_replay_set_event(dm, ctx->stream, true, + replay_event_vsync, false)); +} + +/** + * dm_test_replay_set_event_feature_disabled - Verify disabled Replay rejection + * @test: KUnit test context + * + * amdgpu_dm_replay_set_event() should return false when Replay is not enabled + * on the stream link. + */ +static void dm_test_replay_set_event_feature_disabled(struct kunit *test) +{ + struct amdgpu_display_manager *dm; + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + + dm = kunit_kzalloc(test, sizeof(*dm), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm); + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_replay_set_event(dm, ctx->stream, true, + replay_event_vsync, false)); +} + +/** + * dm_test_replay_set_event_missing_power_module - Verify missing power rejection + * @test: KUnit test context + * + * When Replay is enabled but no power module is available, the event helper + * should return false after failing to read the current Replay events. + */ +static void dm_test_replay_set_event_missing_power_module(struct kunit *test) +{ + struct amdgpu_display_manager *dm; + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + + dm = kunit_kzalloc(test, sizeof(*dm), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm); + + ctx->link->replay_settings.replay_feature_enabled = true; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_replay_set_event(dm, ctx->stream, true, + replay_event_vsync, false)); +} + +/** + * dm_test_replay_set_event_already_set - Verify no-op when event already active + * @test: KUnit test context + * + * When the requested event is already in the desired state, the function should + * return true without calling mod_power_set_replay_event(). + */ +static void dm_test_replay_set_event_already_set(struct kunit *test) +{ + struct amdgpu_display_manager *dm; + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + struct core_power *core_power; + struct power_entity *map; + + dm = kunit_kzalloc(test, sizeof(*dm), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm); + + core_power = kunit_kzalloc(test, sizeof(*core_power), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, core_power); + + map = kunit_kzalloc(test, sizeof(*map), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, map); + + /* Wire the power module so mod_power_get_replay_event() succeeds */ + map->stream = ctx->stream; + map->replay_events = replay_event_vsync; + core_power->map = map; + core_power->num_entities = 1; + dm->power_module = &core_power->mod_public; + + ctx->link->replay_settings.replay_feature_enabled = true; + + /* Event already set — should return true without calling set */ + KUNIT_EXPECT_TRUE(test, amdgpu_dm_replay_set_event(dm, ctx->stream, true, + replay_event_vsync, false)); +} + +/** + * dm_test_replay_set_event_already_clear - Verify no-op when event already cleared + * @test: KUnit test context + * + * When clearing an event that is not currently active, the function should + * return true without calling mod_power_set_replay_event(). + */ +static void dm_test_replay_set_event_already_clear(struct kunit *test) +{ + struct amdgpu_display_manager *dm; + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + struct core_power *core_power; + struct power_entity *map; + + dm = kunit_kzalloc(test, sizeof(*dm), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm); + + core_power = kunit_kzalloc(test, sizeof(*core_power), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, core_power); + + map = kunit_kzalloc(test, sizeof(*map), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, map); + + /* Wire the power module — replay_events has NO vsync bit */ + map->stream = ctx->stream; + map->replay_events = 0; + core_power->map = map; + core_power->num_entities = 1; + dm->power_module = &core_power->mod_public; + + ctx->link->replay_settings.replay_feature_enabled = true; + + /* Clearing an event that's already clear — should return true */ + KUNIT_EXPECT_TRUE(test, amdgpu_dm_replay_set_event(dm, ctx->stream, false, + replay_event_vsync, false)); +} + static struct kunit_case dm_replay_test_cases[] = { + /* amdgpu_dm_link_supports_replay */ KUNIT_CASE(dm_test_replay_supports_all_caps), KUNIT_CASE(dm_test_replay_no_freesync), KUNIT_CASE(dm_test_replay_no_vsdb_replay_mode), @@ -191,6 +599,27 @@ static struct kunit_case dm_replay_test_cases[] = { KUNIT_CASE(dm_test_replay_zero_pixel_deviation), KUNIT_CASE(dm_test_replay_zero_max_deviation_line), KUNIT_CASE(dm_test_replay_both_deviations_zero), + /* amdgpu_dm_set_replay_caps */ + KUNIT_CASE(dm_test_replay_set_caps_already_supported), + KUNIT_CASE(dm_test_replay_set_caps_non_embedded_signal), + KUNIT_CASE(dm_test_replay_set_caps_disallowed_by_panel), + KUNIT_CASE(dm_test_replay_set_caps_link_not_supported), + KUNIT_CASE(dm_test_replay_set_caps_missing_dmub_srv), + KUNIT_CASE(dm_test_replay_set_caps_success), + /* amdgpu_dm_link_setup_replay */ + KUNIT_CASE(dm_test_replay_link_setup_null_stream), + KUNIT_CASE(dm_test_replay_link_setup_null_link), + KUNIT_CASE(dm_test_replay_link_setup_null_vrr_params), + KUNIT_CASE(dm_test_replay_link_setup_not_supported), + KUNIT_CASE(dm_test_replay_link_setup_already_enabled), + KUNIT_CASE(dm_test_replay_link_setup_success), + /* amdgpu_dm_replay_set_event */ + KUNIT_CASE(dm_test_replay_set_event_null_stream), + KUNIT_CASE(dm_test_replay_set_event_null_link), + KUNIT_CASE(dm_test_replay_set_event_feature_disabled), + KUNIT_CASE(dm_test_replay_set_event_missing_power_module), + KUNIT_CASE(dm_test_replay_set_event_already_set), + KUNIT_CASE(dm_test_replay_set_event_already_clear), {} }; |
