summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Hung <alex.hung@amd.com>2026-08-13 21:00:45 -0600
committerAlex Deucher <alexander.deucher@amd.com>2026-09-10 11:59:45 -0400
commit519ba75dcd1a4061909d2bf74230b84136dae2cc (patch)
treec175bfd83086017562a3ca344360e80272c8aa7a
parent767ae341b68193fda5fdbc510b2d77e3e8938039 (diff)
downloadlinux-next-519ba75dcd1a4061909d2bf74230b84136dae2cc.tar.gz
linux-next-519ba75dcd1a4061909d2bf74230b84136dae2cc.zip
drm/amd/display: Test self refresh entry
[Why] Add KUnit tests for amdgpu_dm_enable_self_refresh(), covering the full update and unsupported link blocks, the skip count gating, the damage settle window, and a Replay capable link. [How] The power module stays NULL, which every mod_power entry point treats as a no-op, so the event updates can be reached without a live power module. Assisted-by: Copilot:Claude-Opus-5 GPT-5.6 Sol Reviewed-by: Bhawanpreet Lakha <bhawanpreet.lakha@amd.com> Signed-off-by: Alex Hung <alex.hung@amd.com> Signed-off-by: Ray Wu <ray.wu@amd.com> Tested-by: Dan Wheeler <daniel.wheeler@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c9
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h3
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c148
3 files changed, 156 insertions, 4 deletions
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 0ec8ef881fc1..b31f4e090790 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -3722,10 +3722,10 @@ STATIC_IFN_KUNIT void amdgpu_dm_update_cursor(struct drm_plane *plane,
}
EXPORT_IF_KUNIT(amdgpu_dm_update_cursor);
-static void amdgpu_dm_enable_self_refresh(struct amdgpu_display_manager *dm,
- struct amdgpu_crtc *acrtc_attach,
- const struct dm_crtc_state *acrtc_state,
- const u64 current_ts)
+STATIC_IFN_KUNIT void amdgpu_dm_enable_self_refresh(struct amdgpu_display_manager *dm,
+ struct amdgpu_crtc *acrtc_attach,
+ const struct dm_crtc_state *acrtc_state,
+ const u64 current_ts)
{
struct psr_settings *psr = &acrtc_state->stream->link->psr_settings;
struct replay_settings *pr = &acrtc_state->stream->link->replay_settings;
@@ -3761,6 +3761,7 @@ static void amdgpu_dm_enable_self_refresh(struct amdgpu_display_manager *dm,
acrtc_attach->dm_irq_params.allow_sr_entry = false;
}
}
+EXPORT_IF_KUNIT(amdgpu_dm_enable_self_refresh);
STATIC_IFN_KUNIT void dm_arm_vblank_event(struct amdgpu_crtc *acrtc,
struct dm_crtc_state *acrtc_state,
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
index ad72dc182e93..9399f71c528e 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
@@ -1266,6 +1266,9 @@ int dm_update_mst_vcpi_slots_for_dsc(struct drm_atomic_commit *state, struct dc_
struct dsc_mst_fairness_vars *vars);
void manage_dm_interrupts(struct amdgpu_device *adev, struct amdgpu_crtc *acrtc,
struct dm_crtc_state *acrtc_state);
+void amdgpu_dm_enable_self_refresh(struct amdgpu_display_manager *dm,
+ struct amdgpu_crtc *acrtc_attach,
+ const struct dm_crtc_state *acrtc_state, const u64 current_ts);
int load_dmcu_fw(struct amdgpu_device *adev);
int dm_sw_init(struct amdgpu_ip_block *ip_block);
int dm_late_init(struct amdgpu_ip_block *ip_block);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c
index 4daa187fbd0a..3c975ca2710d 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c
@@ -3803,6 +3803,147 @@ static void dm_test_plane_info_layer_and_blending(struct kunit *test)
KUNIT_EXPECT_EQ(test, ctx->plane_info.global_alpha_value, 0x7f);
}
+/* Tests for amdgpu_dm_enable_self_refresh() */
+
+struct dm_test_sr_ctx {
+ struct amdgpu_display_manager *dm;
+ struct amdgpu_crtc *acrtc;
+ struct dm_crtc_state *acrtc_state;
+ struct amdgpu_dm_connector *aconn;
+ struct dc_link *link;
+};
+
+/*
+ * A fast-update CRTC whose stream has a self-refresh capable link. The power
+ * module stays NULL, which every mod_power entry point treats as a no-op.
+ */
+static struct dm_test_sr_ctx *dm_test_sr_ctx_alloc(struct kunit *test)
+{
+ struct dm_test_sr_ctx *ctx;
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ ctx->dm = dm_kunit_alloc_dm(test);
+ ctx->acrtc = kunit_kzalloc(test, sizeof(*ctx->acrtc), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, ctx->acrtc);
+ ctx->acrtc_state = kunit_kzalloc(test, sizeof(*ctx->acrtc_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, ctx->acrtc_state);
+ ctx->aconn = kunit_kzalloc(test, sizeof(*ctx->aconn), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, ctx->aconn);
+
+ ctx->link = dm_kunit_alloc_link(test);
+ ctx->acrtc_state->stream = dm_kunit_alloc_stream(test, ctx->link);
+ ctx->acrtc_state->stream->dm_stream_context = ctx->aconn;
+ ctx->acrtc_state->update_type = UPDATE_TYPE_FAST;
+
+ return ctx;
+}
+
+static void dm_test_enable_sr(struct dm_test_sr_ctx *ctx, u64 current_ts)
+{
+ amdgpu_dm_enable_self_refresh(ctx->dm, ctx->acrtc, ctx->acrtc_state,
+ current_ts);
+}
+
+/**
+ * dm_test_self_refresh_full_update - Test a full update blocks self refresh
+ * @test: The KUnit test context
+ */
+static void dm_test_self_refresh_full_update(struct kunit *test)
+{
+ struct dm_test_sr_ctx *ctx = dm_test_sr_ctx_alloc(test);
+
+ ctx->acrtc_state->update_type = UPDATE_TYPE_FULL;
+ ctx->link->psr_settings.psr_feature_enabled = true;
+ ctx->acrtc->dm_irq_params.allow_sr_entry = true;
+
+ dm_test_enable_sr(ctx, 0);
+
+ KUNIT_EXPECT_FALSE(test, ctx->acrtc->dm_irq_params.allow_sr_entry);
+}
+
+/**
+ * dm_test_self_refresh_unsupported_link - Test a link without PSR or Replay
+ * @test: The KUnit test context
+ */
+static void dm_test_self_refresh_unsupported_link(struct kunit *test)
+{
+ struct dm_test_sr_ctx *ctx = dm_test_sr_ctx_alloc(test);
+
+ ctx->acrtc->dm_irq_params.allow_sr_entry = true;
+
+ dm_test_enable_sr(ctx, 0);
+
+ KUNIT_EXPECT_FALSE(test, ctx->acrtc->dm_irq_params.allow_sr_entry);
+}
+
+/**
+ * dm_test_self_refresh_decrements_skip_count - Test the skip count gates entry
+ * @test: The KUnit test context
+ */
+static void dm_test_self_refresh_decrements_skip_count(struct kunit *test)
+{
+ struct dm_test_sr_ctx *ctx = dm_test_sr_ctx_alloc(test);
+
+ ctx->link->psr_settings.psr_feature_enabled = true;
+ ctx->aconn->sr_skip_count = 2;
+
+ dm_test_enable_sr(ctx, 0);
+
+ KUNIT_EXPECT_EQ(test, ctx->aconn->sr_skip_count, 1);
+ KUNIT_EXPECT_FALSE(test, ctx->acrtc->dm_irq_params.allow_sr_entry);
+}
+
+/**
+ * dm_test_self_refresh_allows_entry - Test a drained skip count allows entry
+ * @test: The KUnit test context
+ */
+static void dm_test_self_refresh_allows_entry(struct kunit *test)
+{
+ struct dm_test_sr_ctx *ctx = dm_test_sr_ctx_alloc(test);
+
+ ctx->link->psr_settings.psr_feature_enabled = true;
+ ctx->aconn->sr_skip_count = 1;
+
+ /* Well past the 500ms settle window, so the events are cleared. */
+ dm_test_enable_sr(ctx, 2ULL * NSEC_PER_SEC);
+
+ KUNIT_EXPECT_EQ(test, ctx->aconn->sr_skip_count, 0);
+ KUNIT_EXPECT_TRUE(test, ctx->acrtc->dm_irq_params.allow_sr_entry);
+}
+
+/**
+ * dm_test_self_refresh_within_settle_window - Test a recent damage change holds off
+ * @test: The KUnit test context
+ */
+static void dm_test_self_refresh_within_settle_window(struct kunit *test)
+{
+ struct dm_test_sr_ctx *ctx = dm_test_sr_ctx_alloc(test);
+
+ ctx->link->psr_settings.psr_feature_enabled = true;
+ ctx->link->psr_settings.psr_dirty_rects_change_timestamp_ns = 1;
+
+ dm_test_enable_sr(ctx, 2);
+
+ KUNIT_EXPECT_TRUE(test, ctx->acrtc->dm_irq_params.allow_sr_entry);
+}
+
+/**
+ * dm_test_self_refresh_replay_link - Test a Replay capable link takes the same path
+ * @test: The KUnit test context
+ */
+static void dm_test_self_refresh_replay_link(struct kunit *test)
+{
+ struct dm_test_sr_ctx *ctx = dm_test_sr_ctx_alloc(test);
+
+ ctx->link->replay_settings.replay_feature_enabled = true;
+
+ dm_test_enable_sr(ctx, 2ULL * NSEC_PER_SEC);
+
+ KUNIT_EXPECT_TRUE(test, ctx->acrtc->dm_irq_params.allow_sr_entry);
+}
+
/* Tests for manage_dm_interrupts() */
struct dm_test_irq_mgmt_ctx {
@@ -4836,6 +4977,13 @@ static struct kunit_case amdgpu_dm_tests[] = {
KUNIT_CASE(dm_test_plane_info_bad_color_encoding),
KUNIT_CASE(dm_test_plane_info_rotations),
KUNIT_CASE(dm_test_plane_info_layer_and_blending),
+ /* amdgpu_dm_enable_self_refresh */
+ KUNIT_CASE(dm_test_self_refresh_full_update),
+ KUNIT_CASE(dm_test_self_refresh_unsupported_link),
+ KUNIT_CASE(dm_test_self_refresh_decrements_skip_count),
+ KUNIT_CASE(dm_test_self_refresh_allows_entry),
+ KUNIT_CASE(dm_test_self_refresh_within_settle_window),
+ KUNIT_CASE(dm_test_self_refresh_replay_link),
/* manage_dm_interrupts */
KUNIT_CASE(dm_test_manage_interrupts_offdelay),
KUNIT_CASE(dm_test_manage_interrupts_offdelay_fallback),