From: Bhawanpreet Lakha <[email protected]>

Adds a few more test cases to the hdcp code

  - process_output(): make sure work gets requeued when callback and
    watchdog are stopped but also asked to run again
  - event_property_update(): bail out when the connector is NULL
  - hdcp_handle_cpirq(): schedules the work and picks the right link
  - hdcp_update_display(): schedules property validation on enable,
    resets status and cancels validation on disable
  - hdcp_create_workqueue(): returns NULL when max links is zero

Assisted-by: Copilot:Claude-Opus-4.8
Reviewed-by: Alex Hung <[email protected]>
Signed-off-by: Bhawanpreet Lakha <[email protected]>
Signed-off-by: George Zhang <[email protected]>
---
 .../amd/display/amdgpu_dm/amdgpu_dm_hdcp.c    |  48 ++--
 .../amd/display/amdgpu_dm/amdgpu_dm_hdcp.h    |   5 +
 .../amdgpu_dm/tests/amdgpu_dm_hdcp_test.c     | 240 ++++++++++++++++++
 3 files changed, 276 insertions(+), 17 deletions(-)

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 039f6516085c..8f7585d3efa4 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
@@ -258,6 +258,30 @@ static void link_lock(struct hdcp_workqueue *work, bool 
lock)
        }
 }

+STATIC_IFN_KUNIT
+void hdcp_update_display_encryption_control(struct hdcp_workqueue *hdcp_work,
+                                           struct hdcp_workqueue *hdcp_w,
+                                           unsigned int conn_index,
+                                           bool enable_encryption)
+{
+       if (enable_encryption) {
+               /* Explicitly set the saved SRM as sysfs call will be after we 
already enabled hdcp
+                * (s3 resume case)
+                */
+               if (hdcp_work->srm_size > 0)
+                       psp_set_srm(hdcp_work->hdcp.config.psp.handle, 
hdcp_work->srm,
+                                   hdcp_work->srm_size,
+                                   &hdcp_work->srm_version);
+
+               schedule_delayed_work(&hdcp_w->property_validate_dwork,
+                                     
msecs_to_jiffies(DRM_HDCP_CHECK_PERIOD_MS));
+       } else {
+               hdcp_w->encryption_status[conn_index] = 
MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;
+               cancel_delayed_work(&hdcp_w->property_validate_dwork);
+       }
+}
+EXPORT_IF_KUNIT(hdcp_update_display_encryption_control);
+
 void hdcp_update_display(struct hdcp_workqueue *hdcp_work,
                         unsigned int link_index,
                         struct amdgpu_dm_connector *aconnector,
@@ -281,22 +305,8 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work,
                        dc->debug.hdcp_lc_force_fw_enable,
                        dc->debug.hdcp_lc_enable_sw_fallback,
                        &link_adjust, &display_adjust);
-
-       if (enable_encryption) {
-               /* Explicitly set the saved SRM as sysfs call will be after we 
already enabled hdcp
-                * (s3 resume case)
-                */
-               if (hdcp_work->srm_size > 0)
-                       psp_set_srm(hdcp_work->hdcp.config.psp.handle, 
hdcp_work->srm,
-                                   hdcp_work->srm_size,
-                                   &hdcp_work->srm_version);
-
-               schedule_delayed_work(&hdcp_w->property_validate_dwork,
-                                     
msecs_to_jiffies(DRM_HDCP_CHECK_PERIOD_MS));
-       } else {
-               hdcp_w->encryption_status[conn_index] = 
MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;
-               cancel_delayed_work(&hdcp_w->property_validate_dwork);
-       }
+       hdcp_update_display_encryption_control(hdcp_work, hdcp_w, conn_index,
+                                             enable_encryption);

        mod_hdcp_update_display(&hdcp_w->hdcp, conn_index, &link_adjust, 
&display_adjust, &hdcp_w->output);

@@ -363,6 +373,7 @@ void hdcp_handle_cpirq(struct hdcp_workqueue *hdcp_work, 
unsigned int link_index

        schedule_work(&hdcp_w->cpirq_work);
 }
+EXPORT_IF_KUNIT(hdcp_handle_cpirq);

 static void event_callback(struct work_struct *work)
 {
@@ -381,7 +392,8 @@ static void event_callback(struct work_struct *work)
        process_output(hdcp_work);
 }

-static void event_property_update(struct work_struct *work)
+STATIC_IFN_KUNIT
+void event_property_update(struct work_struct *work)
 {
        struct hdcp_workqueue *hdcp_work = container_of(work, struct 
hdcp_workqueue,
                                                        property_update_work);
@@ -440,6 +452,7 @@ static void event_property_update(struct work_struct *work)
                drm_modeset_unlock(&dev->mode_config.connection_mutex);
        }
 }
+EXPORT_IF_KUNIT(event_property_update);

 static void event_property_validate(struct work_struct *work)
 {
@@ -872,4 +885,5 @@ struct hdcp_workqueue *hdcp_create_workqueue(struct 
amdgpu_device *adev,

        return NULL;
 }
+EXPORT_IF_KUNIT(hdcp_create_workqueue);

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 3ba5823aed9f..a82a20b80518 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
@@ -108,6 +108,11 @@ void hdcp_get_link_display_adjustments(
        bool hdcp_lc_enable_sw_fallback,
        struct mod_hdcp_link_adjustment *link_adjust,
        struct mod_hdcp_display_adjustment *display_adjust);
+void hdcp_update_display_encryption_control(struct hdcp_workqueue *hdcp_work,
+                                           struct hdcp_workqueue *hdcp_w,
+                                           unsigned int conn_index,
+                                           bool enable_encryption);
+void event_property_update(struct work_struct *work);
 #endif

 #endif /* AMDGPU_DM_AMDGPU_DM_HDCP_H_ */
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
index 619b4a80c82b..dbcff92672e6 100644
--- 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
@@ -402,8 +402,236 @@ static void 
dm_test_process_output_callback_and_watchdog_needed(struct kunit *te
        cancel_delayed_work_sync(&work->watchdog_timer_dwork);
        cancel_delayed_work_sync(&work->property_validate_dwork);
 }
+
+/**
+ * dm_test_process_output_callback_stop_and_needed_requeues - stop+needed 
requeues callback work
+ * @test: KUnit test context
+ *
+ * When callback_stop and callback_needed are both set, process_output()
+ * should cancel the previous callback_dwork and queue it again.
+ */
+static void dm_test_process_output_callback_stop_and_needed_requeues(struct 
kunit *test)
+{
+       struct hdcp_workqueue *work = alloc_test_workqueue(test);
+
+       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;
+       work->output.callback_needed = true;
+       work->output.callback_delay = 300;
+
+       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);
+}
+
+/**
+ * dm_test_process_output_watchdog_stop_and_needed_requeues - stop+needed 
requeues watchdog work
+ * @test: KUnit test context
+ *
+ * When watchdog_timer_stop and watchdog_timer_needed are both set,
+ * process_output() should cancel previous watchdog work and queue it again.
+ */
+static void dm_test_process_output_watchdog_stop_and_needed_requeues(struct 
kunit *test)
+{
+       struct hdcp_workqueue *work = alloc_test_workqueue(test);
+
+       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;
+       work->output.watchdog_timer_needed = true;
+       work->output.watchdog_timer_delay = 700;
+
+       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);
+}
 /* End of tests for process_output() */

+/* Tests for event_property_update() */
+
+/**
+ * alloc_test_workqueue_for_property_update - allocate minimal workqueue for 
callback tests
+ * @test: KUnit test context for managed allocation
+ *
+ * Allocates a minimal hdcp_workqueue with property_update_work initialised
+ * so event_property_update() can resolve container_of() safely.
+ */
+static struct hdcp_workqueue *alloc_test_workqueue_for_property_update(struct 
kunit *test)
+{
+       struct hdcp_workqueue *work;
+
+       work = kunit_kzalloc(test, sizeof(*work), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, work);
+
+       INIT_WORK(&work->property_update_work, dummy_work_fn);
+
+       return work;
+}
+
+/**
+ * dm_test_event_property_update_skips_null_connector - null connector is 
ignored
+ * @test: KUnit test context
+ *
+ * If aconnector entry is NULL, event_property_update() should skip it
+ * without modifying encryption_status.
+ */
+static void dm_test_event_property_update_skips_null_connector(struct kunit 
*test)
+{
+       struct hdcp_workqueue *work = 
alloc_test_workqueue_for_property_update(test);
+       enum mod_hdcp_encryption_status before;
+
+       work->encryption_status[0] = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON;
+       before = work->encryption_status[0];
+
+       event_property_update(&work->property_update_work);
+
+       KUNIT_EXPECT_EQ(test, work->encryption_status[0], before);
+}
+
+/* End of tests for event_property_update() */
+
+/* Tests for hdcp_handle_cpirq() */
+
+/**
+ * dm_test_hdcp_handle_cpirq_schedules_work - cpirq handler queues cpirq_work
+ * @test: KUnit test context
+ *
+ * hdcp_handle_cpirq() should schedule cpirq_work for the selected link.
+ */
+static void dm_test_hdcp_handle_cpirq_schedules_work(struct kunit *test)
+{
+       struct hdcp_workqueue *work;
+
+       work = kunit_kzalloc(test, sizeof(*work), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, work);
+
+       INIT_WORK(&work->cpirq_work, dummy_work_fn);
+
+       hdcp_handle_cpirq(work, 0);
+
+       KUNIT_EXPECT_TRUE(test, work_pending(&work->cpirq_work));
+
+       cancel_work_sync(&work->cpirq_work);
+}
+
+/**
+ * dm_test_hdcp_handle_cpirq_selects_link_index - only selected link work is 
queued
+ * @test: KUnit test context
+ *
+ * hdcp_handle_cpirq() should schedule cpirq_work for the selected index
+ * and not queue unrelated links.
+ */
+static void dm_test_hdcp_handle_cpirq_selects_link_index(struct kunit *test)
+{
+       struct hdcp_workqueue *work;
+
+       work = kunit_kcalloc(test, 2, sizeof(*work), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, work);
+
+       INIT_WORK(&work[0].cpirq_work, dummy_work_fn);
+       INIT_WORK(&work[1].cpirq_work, dummy_work_fn);
+
+       hdcp_handle_cpirq(work, 1);
+
+       KUNIT_EXPECT_FALSE(test, work_pending(&work[0].cpirq_work));
+       KUNIT_EXPECT_TRUE(test, work_pending(&work[1].cpirq_work));
+
+       cancel_work_sync(&work[1].cpirq_work);
+}
+
+/* End of tests for hdcp_handle_cpirq() */
+
+/* Tests for hdcp_update_display() helper logic */
+
+/**
+ * dm_test_hdcp_update_display_enable_schedules_property_validate - enable 
path queues validate work
+ * @test: KUnit test context
+ *
+ * hdcp_update_display() should schedule property_validate_dwork when
+ * encryption is enabled.
+ */
+static void 
dm_test_hdcp_update_display_enable_schedules_property_validate(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->property_validate_dwork, dummy_work_fn);
+       work->srm_size = 0;
+
+       hdcp_update_display_encryption_control(work, work, 0, true);
+
+       KUNIT_EXPECT_TRUE(test, 
delayed_work_pending(&work->property_validate_dwork));
+
+       cancel_delayed_work_sync(&work->property_validate_dwork);
+}
+
+/**
+ * dm_test_hdcp_update_display_disable_resets_status_and_cancels_validate - 
disable path state update
+ * @test: KUnit test context
+ *
+ * hdcp_update_display() should set encryption_status to HDCP_OFF and
+ * cancel property_validate_dwork when encryption is disabled.
+ */
+static void 
dm_test_hdcp_update_display_disable_resets_status_and_cancels_validate(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->property_validate_dwork, dummy_work_fn);
+       schedule_delayed_work(&work->property_validate_dwork, 
msecs_to_jiffies(10000));
+       KUNIT_ASSERT_TRUE(test, 
delayed_work_pending(&work->property_validate_dwork));
+
+       work->encryption_status[3] = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON;
+
+       hdcp_update_display_encryption_control(work, work, 3, false);
+
+       KUNIT_EXPECT_EQ(test, work->encryption_status[3],
+                       MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF);
+       KUNIT_EXPECT_FALSE(test, 
delayed_work_pending(&work->property_validate_dwork));
+}
+
+/* End of tests for hdcp_update_display() helper logic */
+
+/* Tests for hdcp_create_workqueue() */
+
+/**
+ * dm_test_hdcp_create_workqueue_zero_max_links_returns_null - zero-link 
creation fails early
+ * @test: KUnit test context
+ *
+ * When dc->caps.max_links is zero, hdcp_create_workqueue() should fail
+ * the initial allocation path and return NULL.
+ */
+static void dm_test_hdcp_create_workqueue_zero_max_links_returns_null(struct 
kunit *test)
+{
+       struct cp_psp cp_psp = {0};
+       struct dc *dc;
+       struct hdcp_workqueue *work;
+
+       dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, dc);
+
+       dc->caps.max_links = 0;
+
+       work = hdcp_create_workqueue(NULL, &cp_psp, dc);
+
+       KUNIT_EXPECT_PTR_EQ(test, work, NULL);
+}
+
+/* End of tests for hdcp_create_workqueue() */
+
 static struct kunit_case dm_hdcp_test_cases[] = {
        /* hdcp_get_content_protection_from_status() */
        KUNIT_CASE(dm_test_hdcp_get_cp_disabled_returns_desired),
@@ -423,6 +651,18 @@ static struct kunit_case dm_hdcp_test_cases[] = {
        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),
+       KUNIT_CASE(dm_test_process_output_callback_stop_and_needed_requeues),
+       KUNIT_CASE(dm_test_process_output_watchdog_stop_and_needed_requeues),
+       /* event_property_update() */
+       KUNIT_CASE(dm_test_event_property_update_skips_null_connector),
+       /* hdcp_handle_cpirq() */
+       KUNIT_CASE(dm_test_hdcp_handle_cpirq_schedules_work),
+       KUNIT_CASE(dm_test_hdcp_handle_cpirq_selects_link_index),
+       /* hdcp_update_display() helper logic */
+       
KUNIT_CASE(dm_test_hdcp_update_display_enable_schedules_property_validate),
+       
KUNIT_CASE(dm_test_hdcp_update_display_disable_resets_status_and_cancels_validate),
+       /* hdcp_create_workqueue() */
+       KUNIT_CASE(dm_test_hdcp_create_workqueue_zero_max_links_returns_null),
        {}
 };

--
2.55.0

Reply via email to