Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,22 @@
CONFIG_KUNIT=y
CONFIG_PCI=y
CONFIG_DRM=y
CONFIG_DRM_AMDGPU=y
CONFIG_DRM_AMD_DC=y
CONFIG_DEBUG_FS=y
CONFIG_DRM_AMD_DC_KUNIT_TEST=y
CONFIG_FW_LOADER=y
CONFIG_DRM_KUNIT_TEST=y
CONFIG_DRM_KUNIT_TEST_HELPERS=y
CONFIG_DRM_KMS_HELPER=y
CONFIG_DRM_TTM=y
CONFIG_HWMON=y
CONFIG_I2C=y
CONFIG_POWER_SUPPLY=y
CONFIG_CRC16=y
# GCOV Coverage - see tools/testing/kunit/configs/coverage_uml.config
CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
CONFIG_GCOV=y
@@ -0,0 +1,18 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for amdgpu_dm KUnit tests.
ccflags-y += -I$(src)/..
ccflags-y += -I$(src)/../..
ccflags-y += -I$(src)/../../include
ccflags-y += -I$(src)/../../modules/inc
ccflags-y += -I$(src)/../../dc
ccflags-y += -I$(src)/../../../amdgpu
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_crc_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_hdcp_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_color_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_colorop_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_psr_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_replay_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_ism_test.o
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,239 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT
/*
* KUnit tests for amdgpu_dm_colorop.c
*
* Copyright 2026 Advanced Micro Devices, Inc.
*/
#include <kunit/test.h>
#include <drm/drm_colorop.h>
#include <drm/drm_kunit_helpers.h>
#include "amdgpu_dm_colorop.h"
/* Tests for amdgpu_dm_supported_degam_tfs */
static void dm_test_supported_degam_tfs_has_srgb_eotf(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_degam_tfs &
BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF));
}
static void dm_test_supported_degam_tfs_has_pq125_eotf(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_degam_tfs &
BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF));
}
static void dm_test_supported_degam_tfs_has_bt2020_inv_oetf(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_degam_tfs &
BIT(DRM_COLOROP_1D_CURVE_BT2020_INV_OETF));
}
static void dm_test_supported_degam_tfs_has_gamma22(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_degam_tfs &
BIT(DRM_COLOROP_1D_CURVE_GAMMA22));
}
static void dm_test_supported_degam_tfs_no_extra_bits(struct kunit *test)
{
u64 expected = BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) |
BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF) |
BIT(DRM_COLOROP_1D_CURVE_BT2020_INV_OETF) |
BIT(DRM_COLOROP_1D_CURVE_GAMMA22);
KUNIT_EXPECT_EQ(test, amdgpu_dm_supported_degam_tfs, expected);
}
/* Tests for amdgpu_dm_supported_shaper_tfs */
static void dm_test_supported_shaper_tfs_has_srgb_inv_eotf(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_shaper_tfs &
BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF));
}
static void dm_test_supported_shaper_tfs_has_pq125_inv_eotf(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_shaper_tfs &
BIT(DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF));
}
static void dm_test_supported_shaper_tfs_has_bt2020_oetf(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_shaper_tfs &
BIT(DRM_COLOROP_1D_CURVE_BT2020_OETF));
}
static void dm_test_supported_shaper_tfs_has_gamma22_inv(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_shaper_tfs &
BIT(DRM_COLOROP_1D_CURVE_GAMMA22_INV));
}
static void dm_test_supported_shaper_tfs_no_extra_bits(struct kunit *test)
{
u64 expected = BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF) |
BIT(DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF) |
BIT(DRM_COLOROP_1D_CURVE_BT2020_OETF) |
BIT(DRM_COLOROP_1D_CURVE_GAMMA22_INV);
KUNIT_EXPECT_EQ(test, amdgpu_dm_supported_shaper_tfs, expected);
}
/* Tests for amdgpu_dm_supported_blnd_tfs */
static void dm_test_supported_blnd_tfs_has_srgb_eotf(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_blnd_tfs &
BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF));
}
static void dm_test_supported_blnd_tfs_has_pq125_eotf(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_blnd_tfs &
BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF));
}
static void dm_test_supported_blnd_tfs_has_bt2020_inv_oetf(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_blnd_tfs &
BIT(DRM_COLOROP_1D_CURVE_BT2020_INV_OETF));
}
static void dm_test_supported_blnd_tfs_has_gamma22(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_blnd_tfs &
BIT(DRM_COLOROP_1D_CURVE_GAMMA22));
}
static void dm_test_supported_blnd_tfs_no_extra_bits(struct kunit *test)
{
u64 expected = BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) |
BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF) |
BIT(DRM_COLOROP_1D_CURVE_BT2020_INV_OETF) |
BIT(DRM_COLOROP_1D_CURVE_GAMMA22);
KUNIT_EXPECT_EQ(test, amdgpu_dm_supported_blnd_tfs, expected);
}
/* degam and blnd should support the same set of EOTF curves */
static void dm_test_degam_and_blnd_tfs_match(struct kunit *test)
{
KUNIT_EXPECT_EQ(test, amdgpu_dm_supported_degam_tfs,
amdgpu_dm_supported_blnd_tfs);
}
/* Tests for amdgpu_dm_initialize_default_pipeline */
static void kunit_colorop_pipeline_destroy(void *drm)
{
drm_colorop_pipeline_destroy((struct drm_device *)drm);
}
/**
* dm_test_initialize_default_pipeline() - Verify amdgpu_dm_build_default_pipeline()
* produces the expected colorop chain with all ops bypassable.
* @test: KUnit test context.
*/
static void dm_test_initialize_default_pipeline(struct kunit *test)
{
static const enum drm_colorop_type expected[] = {
DRM_COLOROP_1D_CURVE, /* degam TF */
DRM_COLOROP_MULTIPLIER,
DRM_COLOROP_CTM_3X4,
DRM_COLOROP_1D_CURVE, /* shaper TF */
DRM_COLOROP_1D_LUT, /* shaper LUT */
DRM_COLOROP_3D_LUT,
DRM_COLOROP_1D_CURVE, /* blnd TF */
DRM_COLOROP_1D_LUT, /* blnd LUT */
};
struct device *dev;
struct drm_device *drm;
struct drm_plane *plane;
struct drm_prop_enum_list list = {};
struct drm_colorop *op, *first = NULL;
int i = 0;
int ret;
dev = drm_kunit_helper_alloc_device(test);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
/*
* Allocate a plain drm_device (not an amdgpu_device) — sufficient
* because amdgpu_dm_build_default_pipeline() only needs the DRM
* mode-config infrastructure, not the amdgpu device wrapper.
*/
drm = __drm_kunit_helper_alloc_drm_device(test, dev,
sizeof(*drm), 0, 0);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm);
plane = drm_kunit_helper_create_primary_plane(test, drm,
NULL, NULL, NULL, 0, NULL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane);
/*
* Destroy the pipeline before the DRM device is cleaned up so
* that the colorop objects (kzalloc'd inside the function) are
* freed while the device is still valid.
*/
kunit_add_action(test, kunit_colorop_pipeline_destroy, drm);
ret = amdgpu_dm_build_default_pipeline(drm, plane, true, &list);
KUNIT_ASSERT_EQ(test, ret, 0);
kfree(list.name);
drm_for_each_colorop(op, drm) {
if (op->base.id == (uint32_t)list.type) {
first = op;
break;
}
}
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, first);
for (op = first; op; op = op->next, i++) {
KUNIT_ASSERT_LT(test, i, (int)ARRAY_SIZE(expected));
KUNIT_EXPECT_EQ(test, op->type, expected[i]);
KUNIT_EXPECT_NOT_NULL(test, op->bypass_property);
}
KUNIT_EXPECT_EQ(test, i, (int)ARRAY_SIZE(expected));
}
static struct kunit_case dm_colorop_test_cases[] = {
/* degam TFs */
KUNIT_CASE(dm_test_supported_degam_tfs_has_srgb_eotf),
KUNIT_CASE(dm_test_supported_degam_tfs_has_pq125_eotf),
KUNIT_CASE(dm_test_supported_degam_tfs_has_bt2020_inv_oetf),
KUNIT_CASE(dm_test_supported_degam_tfs_has_gamma22),
KUNIT_CASE(dm_test_supported_degam_tfs_no_extra_bits),
/* shaper TFs */
KUNIT_CASE(dm_test_supported_shaper_tfs_has_srgb_inv_eotf),
KUNIT_CASE(dm_test_supported_shaper_tfs_has_pq125_inv_eotf),
KUNIT_CASE(dm_test_supported_shaper_tfs_has_bt2020_oetf),
KUNIT_CASE(dm_test_supported_shaper_tfs_has_gamma22_inv),
KUNIT_CASE(dm_test_supported_shaper_tfs_no_extra_bits),
/* blnd TFs */
KUNIT_CASE(dm_test_supported_blnd_tfs_has_srgb_eotf),
KUNIT_CASE(dm_test_supported_blnd_tfs_has_pq125_eotf),
KUNIT_CASE(dm_test_supported_blnd_tfs_has_bt2020_inv_oetf),
KUNIT_CASE(dm_test_supported_blnd_tfs_has_gamma22),
KUNIT_CASE(dm_test_supported_blnd_tfs_no_extra_bits),
/* cross-check */
KUNIT_CASE(dm_test_degam_and_blnd_tfs_match),
/* amdgpu_dm_initialize_default_pipeline */
KUNIT_CASE(dm_test_initialize_default_pipeline),
{}
};
static struct kunit_suite dm_colorop_test_suite = {
.name = "amdgpu_dm_colorop",
.test_cases = dm_colorop_test_cases,
};
kunit_test_suite(dm_colorop_test_suite);
MODULE_LICENSE("Dual MIT/GPL");
MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_colorop");
MODULE_AUTHOR("AMD");
@@ -0,0 +1,121 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT
/*
* KUnit tests for amdgpu_dm_crc.c
*
* Copyright 2026 Advanced Micro Devices, Inc.
*/
#include <kunit/test.h>
#include "amdgpu_dm_crc.h"
static void dm_test_parse_crc_source_none(struct kunit *test)
{
KUNIT_EXPECT_EQ(test, AMDGPU_DM_PIPE_CRC_SOURCE_NONE, dm_parse_crc_source("none"));
KUNIT_EXPECT_EQ(test, AMDGPU_DM_PIPE_CRC_SOURCE_NONE, dm_parse_crc_source(NULL));
}
static void dm_test_parse_crc_source_crtc(struct kunit *test)
{
KUNIT_EXPECT_EQ(test, AMDGPU_DM_PIPE_CRC_SOURCE_CRTC, dm_parse_crc_source("crtc"));
KUNIT_EXPECT_EQ(test, AMDGPU_DM_PIPE_CRC_SOURCE_CRTC, dm_parse_crc_source("auto"));
}
static void dm_test_parse_crc_source_dprx(struct kunit *test)
{
KUNIT_EXPECT_EQ(test, AMDGPU_DM_PIPE_CRC_SOURCE_DPRX, dm_parse_crc_source("dprx"));
}
static void dm_test_parse_crc_source_crtc_dither(struct kunit *test)
{
KUNIT_EXPECT_EQ(test, AMDGPU_DM_PIPE_CRC_SOURCE_CRTC_DITHER,
dm_parse_crc_source("crtc dither"));
}
static void dm_test_parse_crc_source_dprx_dither(struct kunit *test)
{
KUNIT_EXPECT_EQ(test, AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER,
dm_parse_crc_source("dprx dither"));
}
static void dm_test_parse_crc_source_invalid(struct kunit *test)
{
KUNIT_EXPECT_EQ(test, AMDGPU_DM_PIPE_CRC_SOURCE_INVALID,
dm_parse_crc_source("invalid"));
KUNIT_EXPECT_EQ(test, AMDGPU_DM_PIPE_CRC_SOURCE_INVALID,
dm_parse_crc_source("unknown"));
KUNIT_EXPECT_EQ(test, AMDGPU_DM_PIPE_CRC_SOURCE_INVALID,
dm_parse_crc_source(""));
}
static void dm_test_is_crc_source_crtc(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, dm_is_crc_source_crtc(AMDGPU_DM_PIPE_CRC_SOURCE_CRTC));
KUNIT_EXPECT_TRUE(test, dm_is_crc_source_crtc(AMDGPU_DM_PIPE_CRC_SOURCE_CRTC_DITHER));
KUNIT_EXPECT_FALSE(test, dm_is_crc_source_crtc(AMDGPU_DM_PIPE_CRC_SOURCE_NONE));
KUNIT_EXPECT_FALSE(test, dm_is_crc_source_crtc(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX));
KUNIT_EXPECT_FALSE(test, dm_is_crc_source_crtc(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER));
KUNIT_EXPECT_FALSE(test, dm_is_crc_source_crtc(AMDGPU_DM_PIPE_CRC_SOURCE_INVALID));
}
static void dm_test_is_crc_source_dprx(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, dm_is_crc_source_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX));
KUNIT_EXPECT_TRUE(test, dm_is_crc_source_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER));
KUNIT_EXPECT_FALSE(test, dm_is_crc_source_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_NONE));
KUNIT_EXPECT_FALSE(test, dm_is_crc_source_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_CRTC));
KUNIT_EXPECT_FALSE(test, dm_is_crc_source_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_CRTC_DITHER));
KUNIT_EXPECT_FALSE(test, dm_is_crc_source_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_INVALID));
}
static void dm_test_need_crc_dither(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, dm_need_crc_dither(AMDGPU_DM_PIPE_CRC_SOURCE_NONE));
KUNIT_EXPECT_TRUE(test, dm_need_crc_dither(AMDGPU_DM_PIPE_CRC_SOURCE_CRTC_DITHER));
KUNIT_EXPECT_TRUE(test, dm_need_crc_dither(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER));
KUNIT_EXPECT_FALSE(test, dm_need_crc_dither(AMDGPU_DM_PIPE_CRC_SOURCE_CRTC));
KUNIT_EXPECT_FALSE(test, dm_need_crc_dither(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX));
KUNIT_EXPECT_FALSE(test, dm_need_crc_dither(AMDGPU_DM_PIPE_CRC_SOURCE_INVALID));
}
static void dm_test_is_valid_crc_source(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, amdgpu_dm_is_valid_crc_source(AMDGPU_DM_PIPE_CRC_SOURCE_CRTC));
KUNIT_EXPECT_TRUE(test, amdgpu_dm_is_valid_crc_source(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX));
KUNIT_EXPECT_TRUE(test,
amdgpu_dm_is_valid_crc_source(AMDGPU_DM_PIPE_CRC_SOURCE_CRTC_DITHER));
KUNIT_EXPECT_TRUE(test,
amdgpu_dm_is_valid_crc_source(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER));
KUNIT_EXPECT_FALSE(test, amdgpu_dm_is_valid_crc_source(AMDGPU_DM_PIPE_CRC_SOURCE_NONE));
KUNIT_EXPECT_FALSE(test, amdgpu_dm_is_valid_crc_source(AMDGPU_DM_PIPE_CRC_SOURCE_MAX));
KUNIT_EXPECT_FALSE(test, amdgpu_dm_is_valid_crc_source(AMDGPU_DM_PIPE_CRC_SOURCE_INVALID));
}
static struct kunit_case dm_crc_test_cases[] = {
KUNIT_CASE(dm_test_parse_crc_source_none),
KUNIT_CASE(dm_test_parse_crc_source_crtc),
KUNIT_CASE(dm_test_parse_crc_source_dprx),
KUNIT_CASE(dm_test_parse_crc_source_crtc_dither),
KUNIT_CASE(dm_test_parse_crc_source_dprx_dither),
KUNIT_CASE(dm_test_parse_crc_source_invalid),
KUNIT_CASE(dm_test_is_crc_source_crtc),
KUNIT_CASE(dm_test_is_crc_source_dprx),
KUNIT_CASE(dm_test_need_crc_dither),
KUNIT_CASE(dm_test_is_valid_crc_source),
{}
};
static struct kunit_suite dm_crc_test_suite = {
.name = "amdgpu_dm_crc",
.test_cases = dm_crc_test_cases,
};
kunit_test_suite(dm_crc_test_suite);
MODULE_LICENSE("Dual MIT/GPL");
MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_crc");
MODULE_AUTHOR("AMD");
@@ -0,0 +1,175 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT
/*
* KUnit tests for amdgpu_dm_hdcp.c
*
* Copyright 2026 Advanced Micro Devices, Inc.
*/
#include <kunit/test.h>
#include <linux/workqueue.h>
#include "amdgpu_dm_hdcp.h"
static void dummy_work_fn(struct work_struct *work) {}
/* Tests for process_output() */
/*
* Helper: allocate and initialise a minimal hdcp_workqueue sufficient for
* process_output() testing. Only the three delayed works accessed by
* process_output() are initialised; everything else is zeroed.
*/
static struct hdcp_workqueue *alloc_test_workqueue(struct kunit *test)
{
struct hdcp_workqueue *work;
work = kunit_kzalloc(test, sizeof(*work), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, work);
INIT_DELAYED_WORK(&work->callback_dwork, dummy_work_fn);
INIT_DELAYED_WORK(&work->watchdog_timer_dwork, dummy_work_fn);
INIT_DELAYED_WORK(&work->property_validate_dwork, dummy_work_fn);
return work;
}
/*
* process_output() always schedules property_validate_dwork with delay=0,
* which queues the work item directly (bypassing the timer). Use
* work_pending() rather than delayed_work_pending() to detect this.
*/
static void dm_test_process_output_property_validate_always_scheduled(struct kunit *test)
{
struct hdcp_workqueue *work = alloc_test_workqueue(test);
/* No flags set: only property_validate_dwork should be enqueued */
process_output(work);
KUNIT_EXPECT_TRUE(test, work_pending(&work->property_validate_dwork.work));
KUNIT_EXPECT_FALSE(test, delayed_work_pending(&work->callback_dwork));
KUNIT_EXPECT_FALSE(test, delayed_work_pending(&work->watchdog_timer_dwork));
cancel_delayed_work_sync(&work->property_validate_dwork);
}
/*
* output.callback_needed=true must schedule callback_dwork.
*/
static void dm_test_process_output_callback_needed(struct kunit *test)
{
struct hdcp_workqueue *work = alloc_test_workqueue(test);
work->output.callback_needed = true;
work->output.callback_delay = 500;
process_output(work);
KUNIT_EXPECT_TRUE(test, delayed_work_pending(&work->callback_dwork));
cancel_delayed_work_sync(&work->callback_dwork);
cancel_delayed_work_sync(&work->property_validate_dwork);
}
/*
* output.callback_stop=true must cancel a previously scheduled callback_dwork.
*/
static void dm_test_process_output_callback_stop(struct kunit *test)
{
struct hdcp_workqueue *work = alloc_test_workqueue(test);
/* Pre-schedule callback_dwork with a long delay so it won't fire. */
schedule_delayed_work(&work->callback_dwork, msecs_to_jiffies(10000));
KUNIT_ASSERT_TRUE(test, delayed_work_pending(&work->callback_dwork));
work->output.callback_stop = true;
process_output(work);
KUNIT_EXPECT_FALSE(test, delayed_work_pending(&work->callback_dwork));
cancel_delayed_work_sync(&work->property_validate_dwork);
}
/*
* output.watchdog_timer_needed=true must schedule watchdog_timer_dwork.
*/
static void dm_test_process_output_watchdog_needed(struct kunit *test)
{
struct hdcp_workqueue *work = alloc_test_workqueue(test);
work->output.watchdog_timer_needed = true;
work->output.watchdog_timer_delay = 1000;
process_output(work);
KUNIT_EXPECT_TRUE(test, delayed_work_pending(&work->watchdog_timer_dwork));
cancel_delayed_work_sync(&work->watchdog_timer_dwork);
cancel_delayed_work_sync(&work->property_validate_dwork);
}
/*
* output.watchdog_timer_stop=true must cancel a previously scheduled
* watchdog_timer_dwork.
*/
static void dm_test_process_output_watchdog_stop(struct kunit *test)
{
struct hdcp_workqueue *work = alloc_test_workqueue(test);
/* Pre-schedule watchdog_timer_dwork with a long delay. */
schedule_delayed_work(&work->watchdog_timer_dwork, msecs_to_jiffies(10000));
KUNIT_ASSERT_TRUE(test, delayed_work_pending(&work->watchdog_timer_dwork));
work->output.watchdog_timer_stop = true;
process_output(work);
KUNIT_EXPECT_FALSE(test, delayed_work_pending(&work->watchdog_timer_dwork));
cancel_delayed_work_sync(&work->property_validate_dwork);
}
/*
* Both callback_needed and watchdog_timer_needed set: both dworks are
* scheduled independently.
*/
static void dm_test_process_output_callback_and_watchdog_needed(struct kunit *test)
{
struct hdcp_workqueue *work = alloc_test_workqueue(test);
work->output.callback_needed = true;
work->output.callback_delay = 200;
work->output.watchdog_timer_needed = true;
work->output.watchdog_timer_delay = 800;
process_output(work);
KUNIT_EXPECT_TRUE(test, delayed_work_pending(&work->callback_dwork));
KUNIT_EXPECT_TRUE(test, delayed_work_pending(&work->watchdog_timer_dwork));
cancel_delayed_work_sync(&work->callback_dwork);
cancel_delayed_work_sync(&work->watchdog_timer_dwork);
cancel_delayed_work_sync(&work->property_validate_dwork);
}
/* End of tests for process_output() */
static struct kunit_case dm_hdcp_test_cases[] = {
KUNIT_CASE(dm_test_process_output_property_validate_always_scheduled),
KUNIT_CASE(dm_test_process_output_callback_needed),
KUNIT_CASE(dm_test_process_output_callback_stop),
KUNIT_CASE(dm_test_process_output_watchdog_needed),
KUNIT_CASE(dm_test_process_output_watchdog_stop),
KUNIT_CASE(dm_test_process_output_callback_and_watchdog_needed),
{}
};
static struct kunit_suite dm_hdcp_test_suite = {
.name = "amdgpu_dm_hdcp",
.test_cases = dm_hdcp_test_cases,
};
kunit_test_suite(dm_hdcp_test_suite);
MODULE_LICENSE("Dual MIT/GPL");
MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_hdcp");
MODULE_AUTHOR("AMD");
@@ -0,0 +1,938 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT
/*
* KUnit tests for amdgpu_dm_ism.c
*
* Copyright 2026 Advanced Micro Devices, Inc.
*/
#include <kunit/test.h>
#include "dc.h"
#include "amdgpu_dm_ism.h"
/*
* Helper: allocate and zero-initialise a dc_stream_state for timing tests.
* Only the timing sub-struct is accessed by the functions under test.
*/
static struct dc_stream_state *alloc_test_stream(struct kunit *test)
{
struct dc_stream_state *stream;
stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, stream);
return stream;
}
/*
* Helper: allocate and zero-initialise an ISM instance.
*/
static struct amdgpu_dm_ism *alloc_test_ism(struct kunit *test)
{
struct amdgpu_dm_ism *ism;
ism = kunit_kzalloc(test, sizeof(*ism), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ism);
return ism;
}
/* ===== Tests for dm_ism_next_state — FULL_POWER_RUNNING transitions ===== */
static void dm_test_ism_next_state_running_enter_idle(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_FULL_POWER_RUNNING,
DM_ISM_EVENT_ENTER_IDLE_REQUESTED, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_HYSTERESIS_WAITING);
}
static void dm_test_ism_next_state_running_begin_cursor(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_FULL_POWER_RUNNING,
DM_ISM_EVENT_BEGIN_CURSOR_UPDATE, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_FULL_POWER_BUSY);
}
static void dm_test_ism_next_state_running_invalid(struct kunit *test)
{
enum amdgpu_dm_ism_state next = DM_ISM_NUM_STATES;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_FULL_POWER_RUNNING,
DM_ISM_EVENT_EXIT_IDLE_REQUESTED, &next);
KUNIT_EXPECT_FALSE(test, ok);
/* next should remain untouched on invalid transition */
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_NUM_STATES);
}
/* ===== Tests for dm_ism_next_state — FULL_POWER_BUSY transitions ===== */
static void dm_test_ism_next_state_busy_enter_idle(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_FULL_POWER_BUSY,
DM_ISM_EVENT_ENTER_IDLE_REQUESTED, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_HYSTERESIS_BUSY);
}
static void dm_test_ism_next_state_busy_end_cursor(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_FULL_POWER_BUSY,
DM_ISM_EVENT_END_CURSOR_UPDATE, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_FULL_POWER_RUNNING);
}
/* ===== Tests for dm_ism_next_state — HYSTERESIS_WAITING transitions ===== */
static void dm_test_ism_next_state_hyst_wait_exit_idle(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_HYSTERESIS_WAITING,
DM_ISM_EVENT_EXIT_IDLE_REQUESTED, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_TIMER_ABORTED);
}
static void dm_test_ism_next_state_hyst_wait_begin_cursor(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_HYSTERESIS_WAITING,
DM_ISM_EVENT_BEGIN_CURSOR_UPDATE, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_HYSTERESIS_BUSY);
}
static void dm_test_ism_next_state_hyst_wait_timer(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_HYSTERESIS_WAITING,
DM_ISM_EVENT_TIMER_ELAPSED, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_OPTIMIZED_IDLE);
}
static void dm_test_ism_next_state_hyst_wait_immediate(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_HYSTERESIS_WAITING,
DM_ISM_EVENT_IMMEDIATE, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_OPTIMIZED_IDLE);
}
/* ===== Tests for dm_ism_next_state — HYSTERESIS_BUSY transitions ===== */
static void dm_test_ism_next_state_hyst_busy_exit_idle(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_HYSTERESIS_BUSY,
DM_ISM_EVENT_EXIT_IDLE_REQUESTED, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_FULL_POWER_BUSY);
}
static void dm_test_ism_next_state_hyst_busy_end_cursor(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_HYSTERESIS_BUSY,
DM_ISM_EVENT_END_CURSOR_UPDATE, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_HYSTERESIS_WAITING);
}
/* ===== Tests for dm_ism_next_state — OPTIMIZED_IDLE transitions ===== */
static void dm_test_ism_next_state_opt_idle_exit(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_OPTIMIZED_IDLE,
DM_ISM_EVENT_EXIT_IDLE_REQUESTED, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_FULL_POWER_RUNNING);
}
static void dm_test_ism_next_state_opt_idle_begin_cursor(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_OPTIMIZED_IDLE,
DM_ISM_EVENT_BEGIN_CURSOR_UPDATE, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_HYSTERESIS_BUSY);
}
static void dm_test_ism_next_state_opt_idle_sso_timer(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_OPTIMIZED_IDLE,
DM_ISM_EVENT_SSO_TIMER_ELAPSED, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_OPTIMIZED_IDLE_SSO);
}
static void dm_test_ism_next_state_opt_idle_immediate(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_OPTIMIZED_IDLE,
DM_ISM_EVENT_IMMEDIATE, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_OPTIMIZED_IDLE_SSO);
}
/* ===== Tests for dm_ism_next_state — OPTIMIZED_IDLE_SSO transitions ===== */
static void dm_test_ism_next_state_opt_idle_sso_exit(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_OPTIMIZED_IDLE_SSO,
DM_ISM_EVENT_EXIT_IDLE_REQUESTED, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_FULL_POWER_RUNNING);
}
static void dm_test_ism_next_state_opt_idle_sso_cursor(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_OPTIMIZED_IDLE_SSO,
DM_ISM_EVENT_BEGIN_CURSOR_UPDATE, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_HYSTERESIS_BUSY);
}
/* ===== Tests for dm_ism_next_state — TIMER_ABORTED transitions ===== */
static void dm_test_ism_next_state_aborted_immediate(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_TIMER_ABORTED,
DM_ISM_EVENT_IMMEDIATE, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_FULL_POWER_RUNNING);
}
static void dm_test_ism_next_state_aborted_invalid(struct kunit *test)
{
enum amdgpu_dm_ism_state next = DM_ISM_NUM_STATES;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_TIMER_ABORTED,
DM_ISM_EVENT_ENTER_IDLE_REQUESTED, &next);
KUNIT_EXPECT_FALSE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_NUM_STATES);
}
/* ===== Tests for dm_ism_get_sso_delay ===== */
static void dm_test_ism_sso_delay_null_stream(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
ism->config.sso_num_frames = 5;
KUNIT_EXPECT_EQ(test, dm_ism_get_sso_delay(ism, NULL), (uint64_t)0);
}
static void dm_test_ism_sso_delay_zero_frames(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
struct dc_stream_state *stream = alloc_test_stream(test);
stream->timing.v_total = 1125;
stream->timing.h_total = 2200;
stream->timing.pix_clk_100hz = 1485000;
ism->config.sso_num_frames = 0;
KUNIT_EXPECT_EQ(test, dm_ism_get_sso_delay(ism, stream), (uint64_t)0);
}
static void dm_test_ism_sso_delay_1080p60_3frames(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
struct dc_stream_state *stream = alloc_test_stream(test);
uint64_t expected_one_frame_ns, expected;
/*
* 1080p@60Hz: v_total=1125, h_total=2200, pix_clk=148.5MHz
* pix_clk_100hz = 1485000
* one_frame_ns = (1125 * 2200 * 10000000) / 1485000 = 16666666 ns
*/
stream->timing.v_total = 1125;
stream->timing.h_total = 2200;
stream->timing.pix_clk_100hz = 1485000;
ism->config.sso_num_frames = 3;
expected_one_frame_ns = div64_u64((uint64_t)1125 * 2200 * 10000000ULL,
1485000);
expected = 3 * expected_one_frame_ns;
KUNIT_EXPECT_EQ(test, dm_ism_get_sso_delay(ism, stream), expected);
}
static void dm_test_ism_sso_delay_4k60_1frame(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
struct dc_stream_state *stream = alloc_test_stream(test);
uint64_t expected_one_frame_ns;
/*
* 4K@60Hz: v_total=2250, h_total=4400, pix_clk=594MHz
* pix_clk_100hz = 5940000
*/
stream->timing.v_total = 2250;
stream->timing.h_total = 4400;
stream->timing.pix_clk_100hz = 5940000;
ism->config.sso_num_frames = 1;
expected_one_frame_ns = div64_u64((uint64_t)2250 * 4400 * 10000000ULL,
5940000);
KUNIT_EXPECT_EQ(test, dm_ism_get_sso_delay(ism, stream),
expected_one_frame_ns);
}
/* ===== Tests for dm_ism_get_idle_allow_delay ===== */
static void dm_test_ism_idle_delay_null_stream(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
ism->config.filter_num_frames = 5;
ism->config.filter_entry_count = 3;
ism->config.activation_num_delay_frames = 10;
KUNIT_EXPECT_EQ(test, dm_ism_get_idle_allow_delay(ism, NULL),
(uint64_t)0);
}
static void dm_test_ism_idle_delay_zero_filter_frames(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
struct dc_stream_state *stream = alloc_test_stream(test);
stream->timing.v_total = 1125;
stream->timing.h_total = 2200;
stream->timing.pix_clk_100hz = 1485000;
ism->config.filter_num_frames = 0;
KUNIT_EXPECT_EQ(test, dm_ism_get_idle_allow_delay(ism, stream),
(uint64_t)0);
}
static void dm_test_ism_idle_delay_zero_entry_count(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
struct dc_stream_state *stream = alloc_test_stream(test);
stream->timing.v_total = 1125;
stream->timing.h_total = 2200;
stream->timing.pix_clk_100hz = 1485000;
ism->config.filter_num_frames = 5;
ism->config.filter_entry_count = 0;
KUNIT_EXPECT_EQ(test, dm_ism_get_idle_allow_delay(ism, stream),
(uint64_t)0);
}
static void dm_test_ism_idle_delay_zero_delay_frames(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
struct dc_stream_state *stream = alloc_test_stream(test);
stream->timing.v_total = 1125;
stream->timing.h_total = 2200;
stream->timing.pix_clk_100hz = 1485000;
ism->config.filter_num_frames = 5;
ism->config.filter_entry_count = 3;
ism->config.activation_num_delay_frames = 0;
KUNIT_EXPECT_EQ(test, dm_ism_get_idle_allow_delay(ism, stream),
(uint64_t)0);
}
static void dm_test_ism_idle_delay_no_short_idles(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
struct dc_stream_state *stream = alloc_test_stream(test);
uint64_t one_frame_ns;
/*
* All history records have long durations (well above the
* short_idle_ns threshold), so no delay should be applied.
*/
stream->timing.v_total = 1125;
stream->timing.h_total = 2200;
stream->timing.pix_clk_100hz = 1485000;
one_frame_ns = div64_u64((uint64_t)1125 * 2200 * 10000000ULL,
1485000);
ism->config.filter_num_frames = 5;
ism->config.filter_entry_count = 3;
ism->config.activation_num_delay_frames = 10;
ism->config.filter_history_size = 8;
ism->config.filter_old_history_threshold = 0;
/* Fill history with long idle durations */
for (int i = 0; i < 8; i++) {
ism->records[i].duration_ns = one_frame_ns * 100;
ism->records[i].timestamp_ns = 0;
}
ism->next_record_idx = 8;
KUNIT_EXPECT_EQ(test, dm_ism_get_idle_allow_delay(ism, stream),
(uint64_t)0);
}
static void dm_test_ism_idle_delay_enough_short_idles(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
struct dc_stream_state *stream = alloc_test_stream(test);
uint64_t one_frame_ns, expected;
/*
* Fill history with short idle durations that meet the threshold.
* filter_entry_count=3, so 3 short idles should trigger the delay.
*/
stream->timing.v_total = 1125;
stream->timing.h_total = 2200;
stream->timing.pix_clk_100hz = 1485000;
one_frame_ns = div64_u64((uint64_t)1125 * 2200 * 10000000ULL,
1485000);
ism->config.filter_num_frames = 5;
ism->config.filter_entry_count = 3;
ism->config.activation_num_delay_frames = 10;
ism->config.filter_history_size = 8;
ism->config.filter_old_history_threshold = 0;
/* Fill history with short idle durations (1 frame each) */
for (int i = 0; i < 8; i++) {
ism->records[i].duration_ns = one_frame_ns;
ism->records[i].timestamp_ns = 0;
}
ism->next_record_idx = 8;
expected = 10 * one_frame_ns;
KUNIT_EXPECT_EQ(test, dm_ism_get_idle_allow_delay(ism, stream),
expected);
}
static void dm_test_ism_idle_delay_wraps_around_buffer(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
struct dc_stream_state *stream = alloc_test_stream(test);
uint64_t one_frame_ns, expected;
/*
* Test the circular buffer wraparound: next_record_idx at 2 means
* the most recent records are at indices 1, 0, 15, 14, ...
*/
stream->timing.v_total = 1125;
stream->timing.h_total = 2200;
stream->timing.pix_clk_100hz = 1485000;
one_frame_ns = div64_u64((uint64_t)1125 * 2200 * 10000000ULL,
1485000);
ism->config.filter_num_frames = 5;
ism->config.filter_entry_count = 3;
ism->config.activation_num_delay_frames = 10;
ism->config.filter_history_size = 8;
ism->config.filter_old_history_threshold = 0;
/* Fill entire buffer with short idles */
for (int i = 0; i < AMDGPU_DM_IDLE_HIST_LEN; i++) {
ism->records[i].duration_ns = one_frame_ns;
ism->records[i].timestamp_ns = 0;
}
/* Position next_record_idx at 2 to test wraparound */
ism->next_record_idx = 2;
expected = 10 * one_frame_ns;
KUNIT_EXPECT_EQ(test, dm_ism_get_idle_allow_delay(ism, stream),
expected);
}
static void dm_test_ism_idle_delay_old_history_cutoff(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
struct dc_stream_state *stream = alloc_test_stream(test);
uint64_t one_frame_ns;
/*
* Test old_history_threshold: only recent entries within the
* threshold should be counted. Set up 2 recent short idles but
* require 3 — older entries are outside the threshold.
*/
stream->timing.v_total = 1125;
stream->timing.h_total = 2200;
stream->timing.pix_clk_100hz = 1485000;
one_frame_ns = div64_u64((uint64_t)1125 * 2200 * 10000000ULL,
1485000);
ism->config.filter_num_frames = 5;
ism->config.filter_entry_count = 3;
ism->config.activation_num_delay_frames = 10;
ism->config.filter_history_size = 8;
/* Threshold: entries older than 20 frames are ignored */
ism->config.filter_old_history_threshold = 20;
ism->last_idle_timestamp_ns = one_frame_ns * 100;
/* 2 recent short idles (within threshold) */
ism->records[6].duration_ns = one_frame_ns;
ism->records[6].timestamp_ns = one_frame_ns * 95;
ism->records[7].duration_ns = one_frame_ns;
ism->records[7].timestamp_ns = one_frame_ns * 98;
/* Older entries outside the threshold with long durations */
for (int i = 0; i < 6; i++) {
ism->records[i].duration_ns = one_frame_ns * 100;
ism->records[i].timestamp_ns = one_frame_ns * 10;
}
ism->next_record_idx = 8;
/*
* Only 2 short idles within threshold, but 3 required —
* should return 0 (no delay).
*/
KUNIT_EXPECT_EQ(test, dm_ism_get_idle_allow_delay(ism, stream),
(uint64_t)0);
}
static void dm_test_ism_idle_delay_mixed_durations(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
struct dc_stream_state *stream = alloc_test_stream(test);
uint64_t one_frame_ns;
/*
* Mix of short and long idle durations. Only 2 short idles
* in 8 entries, but filter_entry_count=3, so no delay.
*/
stream->timing.v_total = 1125;
stream->timing.h_total = 2200;
stream->timing.pix_clk_100hz = 1485000;
one_frame_ns = div64_u64((uint64_t)1125 * 2200 * 10000000ULL,
1485000);
ism->config.filter_num_frames = 5;
ism->config.filter_entry_count = 3;
ism->config.activation_num_delay_frames = 10;
ism->config.filter_history_size = 8;
ism->config.filter_old_history_threshold = 0;
/* 2 short idles, 6 long idles */
for (int i = 0; i < 8; i++) {
if (i == 6 || i == 7)
ism->records[i].duration_ns = one_frame_ns;
else
ism->records[i].duration_ns = one_frame_ns * 100;
ism->records[i].timestamp_ns = 0;
}
ism->next_record_idx = 8;
KUNIT_EXPECT_EQ(test, dm_ism_get_idle_allow_delay(ism, stream),
(uint64_t)0);
}
/**
* dm_test_ism_idle_delay_entry_count_exceeds_history_size - entry_count > history_size sets delay
* @test: KUnit test context
*/
static void dm_test_ism_idle_delay_entry_count_exceeds_history_size(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
struct dc_stream_state *stream = alloc_test_stream(test);
uint64_t one_frame_ns, expected;
/*
* filter_entry_count (5) > filter_history_size (3), so history_size
* is determined by filter_entry_count via max(). All 5 records are
* short idles, triggering the delay.
*/
stream->timing.v_total = 1125;
stream->timing.h_total = 2200;
stream->timing.pix_clk_100hz = 1485000;
one_frame_ns = div64_u64((uint64_t)1125 * 2200 * 10000000ULL,
1485000);
ism->config.filter_num_frames = 5;
ism->config.filter_entry_count = 5;
ism->config.filter_history_size = 3;
ism->config.activation_num_delay_frames = 10;
ism->config.filter_old_history_threshold = 0;
for (int i = 0; i < 5; i++) {
ism->records[i].duration_ns = one_frame_ns;
ism->records[i].timestamp_ns = 0;
}
ism->next_record_idx = 5;
expected = 10 * one_frame_ns;
KUNIT_EXPECT_EQ(test, dm_ism_get_idle_allow_delay(ism, stream), expected);
}
/* ===== Tests for amdgpu_dm_ism_init ===== */
/**
* dm_test_ism_init_sets_initial_state - all ISM fields initialized to expected values
* @test: KUnit test context
*/
static void dm_test_ism_init_sets_initial_state(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
struct amdgpu_dm_ism_config config = {
.filter_num_frames = 5,
.filter_entry_count = 3,
.activation_num_delay_frames = 10,
.filter_history_size = 8,
.filter_old_history_threshold = 20,
.sso_num_frames = 2,
};
amdgpu_dm_ism_init(ism, &config);
KUNIT_EXPECT_EQ(test, (int)ism->current_state,
(int)DM_ISM_STATE_FULL_POWER_RUNNING);
KUNIT_EXPECT_EQ(test, (int)ism->previous_state,
(int)DM_ISM_STATE_FULL_POWER_RUNNING);
KUNIT_EXPECT_EQ(test, ism->next_record_idx, 0);
KUNIT_EXPECT_EQ(test, ism->last_idle_timestamp_ns, (uint64_t)0);
KUNIT_EXPECT_EQ(test, ism->config.filter_num_frames,
config.filter_num_frames);
KUNIT_EXPECT_EQ(test, ism->config.filter_entry_count,
config.filter_entry_count);
KUNIT_EXPECT_EQ(test, ism->config.activation_num_delay_frames,
config.activation_num_delay_frames);
KUNIT_EXPECT_EQ(test, ism->config.sso_num_frames, config.sso_num_frames);
}
/* ===== Tests for amdgpu_dm_ism_fini ===== */
/**
* dm_test_ism_fini_after_init - fini cancels never-scheduled work without error
* @test: KUnit test context
*/
static void dm_test_ism_fini_after_init(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
struct amdgpu_dm_ism_config config = {
.filter_num_frames = 5,
.filter_entry_count = 3,
.activation_num_delay_frames = 10,
.sso_num_frames = 2,
};
amdgpu_dm_ism_init(ism, &config);
/* Work was never scheduled; cancel_delayed_work_sync is a no-op. */
amdgpu_dm_ism_fini(ism);
/* FSM state is untouched by fini */
KUNIT_EXPECT_EQ(test, (int)ism->current_state,
(int)DM_ISM_STATE_FULL_POWER_RUNNING);
}
/* ===== Tests for dm_ism_set_last_idle_ts ===== */
/**
* dm_test_ism_set_last_idle_ts_updates_timestamp - last_idle_timestamp_ns updated to current time
* @test: KUnit test context
*/
static void dm_test_ism_set_last_idle_ts_updates_timestamp(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
uint64_t before;
ism->last_idle_timestamp_ns = 0;
before = ktime_get_ns();
dm_ism_set_last_idle_ts(ism);
KUNIT_EXPECT_GE(test, ism->last_idle_timestamp_ns, before);
}
/* ===== Tests for dm_ism_insert_record ===== */
/**
* dm_test_ism_insert_record_basic - record inserted with correct index and duration
* @test: KUnit test context
*/
static void dm_test_ism_insert_record_basic(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
ism->last_idle_timestamp_ns = 0;
ism->next_record_idx = 0;
dm_ism_insert_record(ism);
KUNIT_EXPECT_EQ(test, ism->next_record_idx, 1);
KUNIT_EXPECT_GT(test, ism->records[0].timestamp_ns, (uint64_t)0);
/* duration = timestamp - last_idle_timestamp_ns (0) */
KUNIT_EXPECT_EQ(test, ism->records[0].duration_ns,
ism->records[0].timestamp_ns);
}
/**
* dm_test_ism_insert_record_wraps_around - out-of-bounds index wraps to slot 0
* @test: KUnit test context
*/
static void dm_test_ism_insert_record_wraps_around(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
ism->last_idle_timestamp_ns = 0;
/* Out-of-bounds index triggers reset to 0 */
ism->next_record_idx = AMDGPU_DM_IDLE_HIST_LEN;
dm_ism_insert_record(ism);
KUNIT_EXPECT_EQ(test, ism->next_record_idx, 1);
KUNIT_EXPECT_GT(test, ism->records[0].timestamp_ns, (uint64_t)0);
}
/* ===== Tests for dm_ism_trigger_event ===== */
/**
* dm_test_ism_trigger_event_valid_transition - valid event advances current and previous state
* @test: KUnit test context
*/
static void dm_test_ism_trigger_event_valid_transition(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
bool ok;
ism->current_state = DM_ISM_STATE_FULL_POWER_RUNNING;
ism->previous_state = DM_ISM_STATE_FULL_POWER_RUNNING;
ok = dm_ism_trigger_event(ism, DM_ISM_EVENT_ENTER_IDLE_REQUESTED);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)ism->current_state,
(int)DM_ISM_STATE_HYSTERESIS_WAITING);
KUNIT_EXPECT_EQ(test, (int)ism->previous_state,
(int)DM_ISM_STATE_FULL_POWER_RUNNING);
}
/**
* dm_test_ism_trigger_event_invalid_transition - invalid event leaves state unchanged
* @test: KUnit test context
*/
static void dm_test_ism_trigger_event_invalid_transition(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
bool ok;
ism->current_state = DM_ISM_STATE_FULL_POWER_RUNNING;
ism->previous_state = DM_ISM_STATE_FULL_POWER_RUNNING;
/* EXIT_IDLE_REQUESTED is not valid from FULL_POWER_RUNNING */
ok = dm_ism_trigger_event(ism, DM_ISM_EVENT_EXIT_IDLE_REQUESTED);
KUNIT_EXPECT_FALSE(test, ok);
/* State must remain unchanged on invalid transition */
KUNIT_EXPECT_EQ(test, (int)ism->current_state,
(int)DM_ISM_STATE_FULL_POWER_RUNNING);
KUNIT_EXPECT_EQ(test, (int)ism->previous_state,
(int)DM_ISM_STATE_FULL_POWER_RUNNING);
}
/* ===== Tests for dm_ism_dispatch_next_event ===== */
/**
* dm_test_dispatch_next_event_hyst_wait_no_delay - zero delay_ns: IMMEDIATE in HYSTERESIS_WAITING
* @test: KUnit test context
*/
static void dm_test_dispatch_next_event_hyst_wait_no_delay(struct kunit *test)
{
enum amdgpu_dm_ism_event result;
result = dm_ism_dispatch_next_event(DM_ISM_STATE_HYSTERESIS_WAITING,
0, 0);
KUNIT_EXPECT_EQ(test, (int)result, (int)DM_ISM_EVENT_IMMEDIATE);
}
/**
* dm_test_dispatch_next_event_hyst_wait_with_delay - delay_ns > 0, no IMMEDIATE event returned
* @test: KUnit test context
*/
static void dm_test_dispatch_next_event_hyst_wait_with_delay(struct kunit *test)
{
enum amdgpu_dm_ism_event result;
result = dm_ism_dispatch_next_event(DM_ISM_STATE_HYSTERESIS_WAITING,
1000000, 0);
KUNIT_EXPECT_EQ(test, (int)result, (int)DM_ISM_NUM_EVENTS);
}
/**
* dm_test_dispatch_next_event_opt_idle_no_sso_delay - sso_delay_ns == 0 triggers IMMEDIATE event
* @test: KUnit test context
*/
static void dm_test_dispatch_next_event_opt_idle_no_sso_delay(struct kunit *test)
{
enum amdgpu_dm_ism_event result;
result = dm_ism_dispatch_next_event(DM_ISM_STATE_OPTIMIZED_IDLE,
0, 0);
KUNIT_EXPECT_EQ(test, (int)result, (int)DM_ISM_EVENT_IMMEDIATE);
}
/**
* dm_test_dispatch_next_event_opt_idle_with_sso_delay - sso_delay_ns > 0, SSO timer, no IMMEDIATE
* @test: KUnit test context
*/
static void dm_test_dispatch_next_event_opt_idle_with_sso_delay(struct kunit *test)
{
enum amdgpu_dm_ism_event result;
result = dm_ism_dispatch_next_event(DM_ISM_STATE_OPTIMIZED_IDLE,
0, 1000000);
KUNIT_EXPECT_EQ(test, (int)result, (int)DM_ISM_NUM_EVENTS);
}
/**
* dm_test_dispatch_next_event_timer_aborted - TIMER_ABORTED always returns IMMEDIATE
* @test: KUnit test context
*/
static void dm_test_dispatch_next_event_timer_aborted(struct kunit *test)
{
enum amdgpu_dm_ism_event result;
result = dm_ism_dispatch_next_event(DM_ISM_STATE_TIMER_ABORTED,
0, 0);
KUNIT_EXPECT_EQ(test, (int)result, (int)DM_ISM_EVENT_IMMEDIATE);
}
/**
* dm_test_dispatch_next_event_no_action_state - other states return DM_ISM_NUM_EVENTS
* @test: KUnit test context
*/
static void dm_test_dispatch_next_event_no_action_state(struct kunit *test)
{
enum amdgpu_dm_ism_event result;
result = dm_ism_dispatch_next_event(DM_ISM_STATE_FULL_POWER_RUNNING,
0, 0);
KUNIT_EXPECT_EQ(test, (int)result, (int)DM_ISM_NUM_EVENTS);
}
static struct kunit_case dm_ism_test_cases[] = {
/* dm_ism_next_state — FULL_POWER_RUNNING */
KUNIT_CASE(dm_test_ism_next_state_running_enter_idle),
KUNIT_CASE(dm_test_ism_next_state_running_begin_cursor),
KUNIT_CASE(dm_test_ism_next_state_running_invalid),
/* dm_ism_next_state — FULL_POWER_BUSY */
KUNIT_CASE(dm_test_ism_next_state_busy_enter_idle),
KUNIT_CASE(dm_test_ism_next_state_busy_end_cursor),
/* dm_ism_next_state — HYSTERESIS_WAITING */
KUNIT_CASE(dm_test_ism_next_state_hyst_wait_exit_idle),
KUNIT_CASE(dm_test_ism_next_state_hyst_wait_begin_cursor),
KUNIT_CASE(dm_test_ism_next_state_hyst_wait_timer),
KUNIT_CASE(dm_test_ism_next_state_hyst_wait_immediate),
/* dm_ism_next_state — HYSTERESIS_BUSY */
KUNIT_CASE(dm_test_ism_next_state_hyst_busy_exit_idle),
KUNIT_CASE(dm_test_ism_next_state_hyst_busy_end_cursor),
/* dm_ism_next_state — OPTIMIZED_IDLE */
KUNIT_CASE(dm_test_ism_next_state_opt_idle_exit),
KUNIT_CASE(dm_test_ism_next_state_opt_idle_begin_cursor),
KUNIT_CASE(dm_test_ism_next_state_opt_idle_sso_timer),
KUNIT_CASE(dm_test_ism_next_state_opt_idle_immediate),
/* dm_ism_next_state — OPTIMIZED_IDLE_SSO */
KUNIT_CASE(dm_test_ism_next_state_opt_idle_sso_exit),
KUNIT_CASE(dm_test_ism_next_state_opt_idle_sso_cursor),
/* dm_ism_next_state — TIMER_ABORTED */
KUNIT_CASE(dm_test_ism_next_state_aborted_immediate),
KUNIT_CASE(dm_test_ism_next_state_aborted_invalid),
/* dm_ism_get_sso_delay */
KUNIT_CASE(dm_test_ism_sso_delay_null_stream),
KUNIT_CASE(dm_test_ism_sso_delay_zero_frames),
KUNIT_CASE(dm_test_ism_sso_delay_1080p60_3frames),
KUNIT_CASE(dm_test_ism_sso_delay_4k60_1frame),
/* dm_ism_get_idle_allow_delay */
KUNIT_CASE(dm_test_ism_idle_delay_null_stream),
KUNIT_CASE(dm_test_ism_idle_delay_zero_filter_frames),
KUNIT_CASE(dm_test_ism_idle_delay_zero_entry_count),
KUNIT_CASE(dm_test_ism_idle_delay_zero_delay_frames),
KUNIT_CASE(dm_test_ism_idle_delay_no_short_idles),
KUNIT_CASE(dm_test_ism_idle_delay_enough_short_idles),
KUNIT_CASE(dm_test_ism_idle_delay_wraps_around_buffer),
KUNIT_CASE(dm_test_ism_idle_delay_old_history_cutoff),
KUNIT_CASE(dm_test_ism_idle_delay_mixed_durations),
KUNIT_CASE(dm_test_ism_idle_delay_entry_count_exceeds_history_size),
/* amdgpu_dm_ism_init */
KUNIT_CASE(dm_test_ism_init_sets_initial_state),
/* amdgpu_dm_ism_fini */
KUNIT_CASE(dm_test_ism_fini_after_init),
/* dm_ism_set_last_idle_ts */
KUNIT_CASE(dm_test_ism_set_last_idle_ts_updates_timestamp),
/* dm_ism_insert_record */
KUNIT_CASE(dm_test_ism_insert_record_basic),
KUNIT_CASE(dm_test_ism_insert_record_wraps_around),
/* dm_ism_trigger_event */
KUNIT_CASE(dm_test_ism_trigger_event_valid_transition),
KUNIT_CASE(dm_test_ism_trigger_event_invalid_transition),
/* dm_ism_dispatch_next_event */
KUNIT_CASE(dm_test_dispatch_next_event_hyst_wait_no_delay),
KUNIT_CASE(dm_test_dispatch_next_event_hyst_wait_with_delay),
KUNIT_CASE(dm_test_dispatch_next_event_opt_idle_no_sso_delay),
KUNIT_CASE(dm_test_dispatch_next_event_opt_idle_with_sso_delay),
KUNIT_CASE(dm_test_dispatch_next_event_timer_aborted),
KUNIT_CASE(dm_test_dispatch_next_event_no_action_state),
{}
};
static struct kunit_suite dm_ism_test_suite = {
.name = "amdgpu_dm_ism",
.test_cases = dm_ism_test_cases,
};
kunit_test_suite(dm_ism_test_suite);
MODULE_LICENSE("Dual MIT/GPL");
MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_ism");
MODULE_AUTHOR("AMD");
@@ -0,0 +1,291 @@
// 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 "amdgpu_dm_psr.h"
/*
* 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() */
/* Tests for amdgpu_dm_psr_set_event() — early-exit validation guards */
static void dm_test_psr_set_event_null_stream(struct kunit *test)
{
/* NULL stream → immediate false, dm is not accessed */
KUNIT_EXPECT_FALSE(test, amdgpu_dm_psr_set_event(NULL, NULL, true, psr_event_vsync, false));
}
static void dm_test_psr_set_event_null_link(struct kunit *test)
{
struct dc_stream_state *stream;
stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, stream);
/* stream->link remains NULL from kzalloc */
KUNIT_EXPECT_FALSE(test, amdgpu_dm_psr_set_event(NULL, stream, true, psr_event_vsync, false));
}
static void dm_test_psr_set_event_psr_not_enabled(struct kunit *test)
{
struct dc_stream_state *stream;
struct dc_link *link;
stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, stream);
link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, link);
stream->link = link;
/* link->psr_settings.psr_feature_enabled remains false from kzalloc */
KUNIT_EXPECT_FALSE(test, amdgpu_dm_psr_set_event(NULL, stream, true, psr_event_vsync, false));
}
/* End of tests for amdgpu_dm_psr_set_event() */
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),
KUNIT_CASE(dm_test_psr_set_event_null_stream),
KUNIT_CASE(dm_test_psr_set_event_null_link),
KUNIT_CASE(dm_test_psr_set_event_psr_not_enabled),
{}
};
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");
@@ -0,0 +1,206 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT
/*
* KUnit tests for amdgpu_dm_replay.c
*
* Copyright 2026 Advanced Micro Devices, Inc.
*/
#include <kunit/test.h>
#include "dc.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);
/*
* Helper: allocate a dc_link, amdgpu_dm_connector, and dm_connector_state
* wired up so that to_dm_connector_state(aconnector->base.state) works.
*/
struct replay_test_ctx {
struct dc_link *link;
struct amdgpu_dm_connector *aconnector;
struct dm_connector_state *dm_state;
};
static struct replay_test_ctx *alloc_replay_ctx(struct kunit *test)
{
struct replay_test_ctx *ctx;
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ctx);
ctx->link = kunit_kzalloc(test, sizeof(*ctx->link), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ctx->link);
ctx->aconnector = kunit_kzalloc(test, sizeof(*ctx->aconnector), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ctx->aconnector);
ctx->dm_state = kunit_kzalloc(test, sizeof(*ctx->dm_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ctx->dm_state);
/* Wire connector state so to_dm_connector_state() works */
ctx->aconnector->base.state = &ctx->dm_state->base;
return ctx;
}
/*
* Helper: set all conditions for replay support to pass so individual
* tests can disable one condition at a time.
*/
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->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;
ctx->link->dpcd_caps.pr_info.pixel_deviation_per_line = 1;
ctx->link->dpcd_caps.pr_info.max_deviation_line = 1;
}
/* Tests for amdgpu_dm_link_supports_replay() — all caps met */
static void dm_test_replay_supports_all_caps(struct kunit *test)
{
struct replay_test_ctx *ctx = alloc_replay_ctx(test);
set_all_replay_caps(ctx);
KUNIT_EXPECT_TRUE(test,
amdgpu_dm_link_supports_replay(ctx->link, ctx->aconnector));
}
/* Tests for amdgpu_dm_link_supports_replay() — freesync not capable */
static void dm_test_replay_no_freesync(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_link_supports_replay(ctx->link, ctx->aconnector));
}
/* Tests for amdgpu_dm_link_supports_replay() — no replay mode in VSDB */
static void dm_test_replay_no_vsdb_replay_mode(struct kunit *test)
{
struct replay_test_ctx *ctx = alloc_replay_ctx(test);
set_all_replay_caps(ctx);
ctx->aconnector->vsdb_info.replay_mode = false;
KUNIT_EXPECT_FALSE(test,
amdgpu_dm_link_supports_replay(ctx->link, ctx->aconnector));
}
/* Tests for amdgpu_dm_link_supports_replay() — eDP revision too low */
static void dm_test_replay_edp_rev_too_low(struct kunit *test)
{
struct replay_test_ctx *ctx = alloc_replay_ctx(test);
set_all_replay_caps(ctx);
ctx->link->dpcd_caps.edp_rev = EDP_REVISION_12;
KUNIT_EXPECT_FALSE(test,
amdgpu_dm_link_supports_replay(ctx->link, ctx->aconnector));
}
/* Tests for amdgpu_dm_link_supports_replay() — no ALPM AUX wake cap */
static void dm_test_replay_no_alpm_aux_wake(struct kunit *test)
{
struct replay_test_ctx *ctx = alloc_replay_ctx(test);
set_all_replay_caps(ctx);
ctx->link->dpcd_caps.alpm_caps.bits.AUX_WAKE_ALPM_CAP = 0;
KUNIT_EXPECT_FALSE(test,
amdgpu_dm_link_supports_replay(ctx->link, ctx->aconnector));
}
/* Tests for amdgpu_dm_link_supports_replay() — no adaptive sync SDP */
static void dm_test_replay_no_adaptive_sync_sdp(struct kunit *test)
{
struct replay_test_ctx *ctx = alloc_replay_ctx(test);
set_all_replay_caps(ctx);
ctx->link->dpcd_caps.adaptive_sync_caps.dp_adap_sync_caps.bits.ADAPTIVE_SYNC_SDP_SUPPORT = 0;
KUNIT_EXPECT_FALSE(test,
amdgpu_dm_link_supports_replay(ctx->link, ctx->aconnector));
}
/* Tests for amdgpu_dm_link_supports_replay() — zero pixel deviation */
static void dm_test_replay_zero_pixel_deviation(struct kunit *test)
{
struct replay_test_ctx *ctx = alloc_replay_ctx(test);
set_all_replay_caps(ctx);
ctx->link->dpcd_caps.pr_info.pixel_deviation_per_line = 0;
KUNIT_EXPECT_FALSE(test,
amdgpu_dm_link_supports_replay(ctx->link, ctx->aconnector));
}
/* Tests for amdgpu_dm_link_supports_replay() — zero max deviation line */
static void dm_test_replay_zero_max_deviation_line(struct kunit *test)
{
struct replay_test_ctx *ctx = alloc_replay_ctx(test);
set_all_replay_caps(ctx);
ctx->link->dpcd_caps.pr_info.max_deviation_line = 0;
KUNIT_EXPECT_FALSE(test,
amdgpu_dm_link_supports_replay(ctx->link, ctx->aconnector));
}
/* Tests for amdgpu_dm_link_supports_replay() — both deviation fields zero */
static void dm_test_replay_both_deviations_zero(struct kunit *test)
{
struct replay_test_ctx *ctx = alloc_replay_ctx(test);
set_all_replay_caps(ctx);
ctx->link->dpcd_caps.pr_info.pixel_deviation_per_line = 0;
ctx->link->dpcd_caps.pr_info.max_deviation_line = 0;
KUNIT_EXPECT_FALSE(test,
amdgpu_dm_link_supports_replay(ctx->link, ctx->aconnector));
}
/* End of tests for amdgpu_dm_link_supports_replay() */
static struct kunit_case dm_replay_test_cases[] = {
KUNIT_CASE(dm_test_replay_supports_all_caps),
KUNIT_CASE(dm_test_replay_no_freesync),
KUNIT_CASE(dm_test_replay_no_vsdb_replay_mode),
KUNIT_CASE(dm_test_replay_edp_rev_too_low),
KUNIT_CASE(dm_test_replay_no_alpm_aux_wake),
KUNIT_CASE(dm_test_replay_no_adaptive_sync_sdp),
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),
{}
};
static struct kunit_suite dm_replay_test_suite = {
.name = "amdgpu_dm_replay",
.test_cases = dm_replay_test_cases,
};
kunit_test_suite(dm_replay_test_suite);
MODULE_LICENSE("Dual MIT/GPL");
MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_replay");
MODULE_AUTHOR("AMD");