--- /dev/null
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * KUnit tests for amdgpu_dm_psr.c
+ *
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ */
+
+#include <kunit/test.h>
+
+#include "dc.h"
+
+/* Extern declaration for the function under test */
+extern void amdgpu_dm_psr_fill_caps(struct dc_link *link,
+ struct psr_caps *caps);
+
+/*
+ * Helper: allocate and zero-initialise a dc_link sufficient for
+ * amdgpu_dm_psr_fill_caps() testing. The function only accesses
+ * embedded members (dpcd_caps, psr_settings) so no pointer fields
+ * need to be wired up.
+ */
+static struct dc_link *alloc_test_link(struct kunit *test)
+{
+ struct dc_link *link;
+
+ link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, link);
+
+ return link;
+}
+
+/* Tests for amdgpu_dm_psr_fill_caps() — PSR version mapping */
+
+static void dm_test_psr_fill_caps_version_1(struct kunit *test)
+{
+ struct dc_link *link = alloc_test_link(test);
+ struct psr_caps caps;
+
+ memset(&caps, 0, sizeof(caps));
+ link->psr_settings.psr_version = DC_PSR_VERSION_1;
+
+ amdgpu_dm_psr_fill_caps(link, &caps);
+
+ KUNIT_EXPECT_EQ(test, (int)caps.psr_version, 1);
+}
+
+static void dm_test_psr_fill_caps_version_su1(struct kunit *test)
+{
+ struct dc_link *link = alloc_test_link(test);
+ struct psr_caps caps;
+
+ memset(&caps, 0, sizeof(caps));
+ link->psr_settings.psr_version = DC_PSR_VERSION_SU_1;
+
+ amdgpu_dm_psr_fill_caps(link, &caps);
+
+ KUNIT_EXPECT_EQ(test, (int)caps.psr_version, 2);
+}
+
+static void dm_test_psr_fill_caps_version_unsupported(struct kunit *test)
+{
+ struct dc_link *link = alloc_test_link(test);
+ struct psr_caps caps;
+
+ memset(&caps, 0, sizeof(caps));
+ link->psr_settings.psr_version = DC_PSR_VERSION_UNSUPPORTED;
+
+ amdgpu_dm_psr_fill_caps(link, &caps);
+
+ /*
+ * Neither DC_PSR_VERSION_1 nor DC_PSR_VERSION_SU_1,
+ * so psr_version stays at its zero-initialised value.
+ */
+ KUNIT_EXPECT_EQ(test, (int)caps.psr_version, 0);
+}
+
+/* Tests for amdgpu_dm_psr_fill_caps() — RFB setup time */
+
+static void dm_test_psr_fill_caps_setup_time_zero(struct kunit *test)
+{
+ struct dc_link *link = alloc_test_link(test);
+ struct psr_caps caps;
+
+ memset(&caps, 0, sizeof(caps));
+ /* PSR_SETUP_TIME = 0 → (6 - 0) * 55 = 330 */
+ link->dpcd_caps.psr_info.psr_dpcd_caps.bits.PSR_SETUP_TIME = 0;
+
+ amdgpu_dm_psr_fill_caps(link, &caps);
+
+ KUNIT_EXPECT_EQ(test, caps.psr_rfb_setup_time, 330U);
+}
+
+static void dm_test_psr_fill_caps_setup_time_mid(struct kunit *test)
+{
+ struct dc_link *link = alloc_test_link(test);
+ struct psr_caps caps;
+
+ memset(&caps, 0, sizeof(caps));
+ /* PSR_SETUP_TIME = 3 → (6 - 3) * 55 = 165 */
+ link->dpcd_caps.psr_info.psr_dpcd_caps.bits.PSR_SETUP_TIME = 3;
+
+ amdgpu_dm_psr_fill_caps(link, &caps);
+
+ KUNIT_EXPECT_EQ(test, caps.psr_rfb_setup_time, 165U);
+}
+
+static void dm_test_psr_fill_caps_setup_time_max(struct kunit *test)
+{
+ struct dc_link *link = alloc_test_link(test);
+ struct psr_caps caps;
+
+ memset(&caps, 0, sizeof(caps));
+ /* PSR_SETUP_TIME = 6 → (6 - 6) * 55 = 0 */
+ link->dpcd_caps.psr_info.psr_dpcd_caps.bits.PSR_SETUP_TIME = 6;
+
+ amdgpu_dm_psr_fill_caps(link, &caps);
+
+ KUNIT_EXPECT_EQ(test, caps.psr_rfb_setup_time, 0U);
+}
+
+/* Tests for amdgpu_dm_psr_fill_caps() — link training flag */
+
+static void dm_test_psr_fill_caps_link_training_required(struct kunit *test)
+{
+ struct dc_link *link = alloc_test_link(test);
+ struct psr_caps caps;
+
+ memset(&caps, 0, sizeof(caps));
+ link->dpcd_caps.psr_info.psr_dpcd_caps.bits.LINK_TRAINING_ON_EXIT_NOT_REQUIRED = 0;
+
+ amdgpu_dm_psr_fill_caps(link, &caps);
+
+ KUNIT_EXPECT_TRUE(test, caps.psr_exit_link_training_required);
+}
+
+static void dm_test_psr_fill_caps_link_training_not_required(struct kunit *test)
+{
+ struct dc_link *link = alloc_test_link(test);
+ struct psr_caps caps;
+
+ memset(&caps, 0, sizeof(caps));
+ link->dpcd_caps.psr_info.psr_dpcd_caps.bits.LINK_TRAINING_ON_EXIT_NOT_REQUIRED = 1;
+
+ amdgpu_dm_psr_fill_caps(link, &caps);
+
+ KUNIT_EXPECT_FALSE(test, caps.psr_exit_link_training_required);
+}
+
+/* Tests for amdgpu_dm_psr_fill_caps() — DPCD field passthrough */
+
+static void dm_test_psr_fill_caps_dpcd_fields(struct kunit *test)
+{
+ struct dc_link *link = alloc_test_link(test);
+ struct psr_caps caps;
+
+ memset(&caps, 0, sizeof(caps));
+
+ link->dpcd_caps.edp_rev = 0x14;
+ link->dpcd_caps.psr_info.psr_version = 2;
+ link->dpcd_caps.psr_info.psr_dpcd_caps.bits.SU_GRANULARITY_REQUIRED = 1;
+ link->dpcd_caps.psr_info.psr_dpcd_caps.bits.Y_COORDINATE_REQUIRED = 1;
+ link->dpcd_caps.psr_info.psr2_su_y_granularity_cap = 4;
+ link->dpcd_caps.alpm_caps.bits.AUX_WAKE_ALPM_CAP = 1;
+ link->dpcd_caps.alpm_caps.bits.PM_STATE_2A_SUPPORT = 1;
+
+ amdgpu_dm_psr_fill_caps(link, &caps);
+
+ KUNIT_EXPECT_EQ(test, (int)caps.edp_revision, 0x14);
+ KUNIT_EXPECT_EQ(test, (int)caps.support_ver, 2);
+ KUNIT_EXPECT_TRUE(test, caps.su_granularity_required);
+ KUNIT_EXPECT_TRUE(test, caps.y_coordinate_required);
+ KUNIT_EXPECT_EQ(test, (int)caps.su_y_granularity, 4);
+ KUNIT_EXPECT_TRUE(test, caps.alpm_cap);
+ KUNIT_EXPECT_TRUE(test, caps.standby_support);
+}
+
+static void dm_test_psr_fill_caps_dpcd_fields_unset(struct kunit *test)
+{
+ struct dc_link *link = alloc_test_link(test);
+ struct psr_caps caps;
+
+ memset(&caps, 0xFF, sizeof(caps));
+
+ /* All dpcd_caps fields are zero from kzalloc */
+ amdgpu_dm_psr_fill_caps(link, &caps);
+
+ KUNIT_EXPECT_EQ(test, (int)caps.edp_revision, 0);
+ KUNIT_EXPECT_EQ(test, (int)caps.support_ver, 0);
+ KUNIT_EXPECT_FALSE(test, caps.su_granularity_required);
+ KUNIT_EXPECT_FALSE(test, caps.y_coordinate_required);
+ KUNIT_EXPECT_EQ(test, (int)caps.su_y_granularity, 0);
+ KUNIT_EXPECT_FALSE(test, caps.alpm_cap);
+ KUNIT_EXPECT_FALSE(test, caps.standby_support);
+}
+
+/* Tests for amdgpu_dm_psr_fill_caps() — rate control and power opts */
+
+static void dm_test_psr_fill_caps_rate_control_always_zero(struct kunit *test)
+{
+ struct dc_link *link = alloc_test_link(test);
+ struct psr_caps caps;
+
+ /* Pre-fill caps with non-zero to verify overwrite */
+ memset(&caps, 0xFF, sizeof(caps));
+
+ amdgpu_dm_psr_fill_caps(link, &caps);
+
+ KUNIT_EXPECT_EQ(test, (int)caps.rate_control_caps, 0);
+}
+
+static void dm_test_psr_fill_caps_power_opts_z10_always_set(struct kunit *test)
+{
+ struct dc_link *link = alloc_test_link(test);
+ struct psr_caps caps;
+
+ memset(&caps, 0, sizeof(caps));
+
+ amdgpu_dm_psr_fill_caps(link, &caps);
+
+ /*
+ * psr_power_opt_z10_static_screen is always added to power_opts
+ * regardless of amdgpu_dc_feature_mask.
+ */
+ KUNIT_EXPECT_TRUE(test,
+ (caps.psr_power_opt_flag &
+ psr_power_opt_z10_static_screen) != 0);
+}
+/* End of tests for amdgpu_dm_psr_fill_caps() */
+
+static struct kunit_case dm_psr_test_cases[] = {
+ KUNIT_CASE(dm_test_psr_fill_caps_version_1),
+ KUNIT_CASE(dm_test_psr_fill_caps_version_su1),
+ KUNIT_CASE(dm_test_psr_fill_caps_version_unsupported),
+ KUNIT_CASE(dm_test_psr_fill_caps_setup_time_zero),
+ KUNIT_CASE(dm_test_psr_fill_caps_setup_time_mid),
+ KUNIT_CASE(dm_test_psr_fill_caps_setup_time_max),
+ KUNIT_CASE(dm_test_psr_fill_caps_link_training_required),
+ KUNIT_CASE(dm_test_psr_fill_caps_link_training_not_required),
+ KUNIT_CASE(dm_test_psr_fill_caps_dpcd_fields),
+ KUNIT_CASE(dm_test_psr_fill_caps_dpcd_fields_unset),
+ KUNIT_CASE(dm_test_psr_fill_caps_rate_control_always_zero),
+ KUNIT_CASE(dm_test_psr_fill_caps_power_opts_z10_always_set),
+ {}
+};
+
+static struct kunit_suite dm_psr_test_suite = {
+ .name = "amdgpu_dm_psr",
+ .test_cases = dm_psr_test_cases,
+};
+
+kunit_test_suite(dm_psr_test_suite);
+
+MODULE_LICENSE("Dual MIT/GPL");
+MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_psr");
+MODULE_AUTHOR("AMD");