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

Reply via email to