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>
This commit is contained in:
Alex Hung
2026-06-15 18:54:53 -06:00
committed by Alex Deucher
parent 682710244f
commit d99024d7d2
2 changed files with 436 additions and 4 deletions

View File

@@ -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);

View File

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