]> git.hungrycats.org Git - linux/commitdiff
drm/amd/display: Add more tests for HDCP
authorBhawanpreet Lakha <bhawanpreet.lakha@amd.com>
Tue, 23 Jun 2026 14:44:10 +0000 (10:44 -0400)
committerAlex Deucher <alexander.deucher@amd.com>
Wed, 15 Jul 2026 13:15:38 +0000 (09:15 -0400)
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 <alex.hung@amd.com>
Signed-off-by: Bhawanpreet Lakha <bhawanpreet.lakha@amd.com>
Signed-off-by: George Zhang <george.zhang@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h
drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_hdcp_test.c

index 5dbeb1e017d461da1732244e00560d039e3408bf..1ffe82f0a9f100cbc06c05c9934a9d2c7240beed 100644 (file)
@@ -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 @@ fail_alloc_context:
 
        return NULL;
 }
+EXPORT_IF_KUNIT(hdcp_create_workqueue);
 
index 3ba5823aed9f996675e456c54a642c9bd7f364f3..a82a20b805185e0fc4386e9d7e7367c45b963d54 100644 (file)
@@ -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_ */
index 619b4a80c82bcb506897d6dd410bec201ce0e72f..dbcff92672e690b6b125dde84f9aec81966a33d6 100644 (file)
@@ -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),
        {}
 };