From: Alex Hung <[email protected]> Expose process_output() as non-static when CONFIG_DRM_AMD_DC_KUNIT_TEST is enabled and add KUnit tests exercising its full branch logic:
- property_validate_dwork is always enqueued (delay=0) - callback_dwork is scheduled when callback_needed is set - callback_dwork is cancelled when callback_stop is set - watchdog_timer_dwork is scheduled when watchdog_timer_needed is set - watchdog_timer_dwork is cancelled when watchdog_timer_stop is set - Both dworks are scheduled independently when both flags are set Assisted-by: Copilot:Claude-Sonnet-4.6 Reviewed-by: Harry Wentland <[email protected]> Signed-off-by: Alex Hung <[email protected]> Signed-off-by: Ivan Lipski <[email protected]> --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h | 2 - .../amd/display/amdgpu_dm/amdgpu_dm_hdcp.c | 5 +- .../amd/display/amdgpu_dm/amdgpu_dm_hdcp.h | 13 +- .../drm/amd/display/amdgpu_dm/tests/Makefile | 5 + .../amdgpu_dm/tests/amdgpu_dm_hdcp_test.c | 175 ++++++++++++++++++ 5 files changed, 196 insertions(+), 4 deletions(-) create mode 100644 drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_hdcp_test.c 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 1e0ccf58cdb8..43056392a1f0 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h @@ -45,8 +45,6 @@ * in amdgpu_dm_kms.h file */ -#define AMDGPU_DM_MAX_DISPLAY_INDEX 31 - #define AMDGPU_DM_MAX_CRTC 6 #define AMDGPU_DM_MAX_NUM_EDP 2 diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c index a10401675f53..29e5bdb16b89 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c @@ -31,6 +31,7 @@ #include "dm_helpers.h" #include <drm/display/drm_hdcp_helper.h> #include "hdcp_psp.h" +#include "amdgpu_dm_kunit_helpers.h" /* * If the SRM version being loaded is less than or equal to the @@ -158,7 +159,8 @@ static int psp_set_srm(struct psp_context *psp, return 0; } -static void process_output(struct hdcp_workqueue *hdcp_work) +STATIC_IFN_KUNIT +void process_output(struct hdcp_workqueue *hdcp_work) { struct mod_hdcp_output output = hdcp_work->output; @@ -178,6 +180,7 @@ static void process_output(struct hdcp_workqueue *hdcp_work) schedule_delayed_work(&hdcp_work->property_validate_dwork, msecs_to_jiffies(0)); } +EXPORT_IF_KUNIT(process_output); static void link_lock(struct hdcp_workqueue *work, bool lock) { diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h index 4faa344f196e..4bb072cfac1e 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h @@ -31,12 +31,19 @@ #include "hdcp.h" #include "dc.h" #include "dm_cp_psp.h" -#include "amdgpu.h" + +/* + * Minimal declarations needed by this header. + * Full amdgpu/DM definitions come from amdgpu_dm.h included by each .c file. + */ +#define AMDGPU_DM_MAX_DISPLAY_INDEX 31 +struct amdgpu_dm_connector; struct mod_hdcp; struct mod_hdcp_link; struct mod_hdcp_display; struct cp_psp; +struct amdgpu_device; struct hdcp_workqueue { struct work_struct cpirq_work; @@ -87,4 +94,8 @@ void hdcp_destroy(struct kobject *kobj, struct hdcp_workqueue *work); struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev, struct cp_psp *cp_psp, struct dc *dc); +#ifdef CONFIG_DRM_AMD_DC_KUNIT_TEST +void process_output(struct hdcp_workqueue *hdcp_work); +#endif + #endif /* AMDGPU_DM_AMDGPU_DM_HDCP_H_ */ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/Makefile b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/Makefile index 1238d8832fa3..9669ea79a666 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/Makefile +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/Makefile @@ -3,5 +3,10 @@ # 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 obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_crc_test.o +obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_hdcp_test.o diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_hdcp_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_hdcp_test.c new file mode 100644 index 000000000000..d03b606d27bc --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_hdcp_test.c @@ -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"); -- 2.43.0
