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