drm/amd/display: Extract HDCP testable helpers for KUnit

[WHAT]
Extract hdcp_get_content_protection_from_status() and
hdcp_get_link_display_adjustments() from event_property_update()
and hdcp_update_display() so the pure decision logic can be
KUnit-tested.

Also update function comments to kernel-doc formats.

Assisted-by: Copilot:Claude-Opus-4.6
Reviewed-by: Bhawanpreet Lakha <bhawanpreet.lakha@amd.com>
Signed-off-by: Alex Hung <alex.hung@amd.com>
Signed-off-by: Aurabindo Pillai <aurabindo.pillai@amd.com>
Tested-by: Dan Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
Alex Hung 2026-05-27 17:01:13 -06:00 committed by Alex Deucher
parent 29757b93d7
commit 569cc68d6a
3 changed files with 370 additions and 54 deletions

View File

@ -182,6 +182,70 @@ void process_output(struct hdcp_workqueue *hdcp_work)
}
EXPORT_IF_KUNIT(process_output);
STATIC_IFN_KUNIT
bool hdcp_get_content_protection_from_status(
unsigned int hdcp_content_type,
enum mod_hdcp_encryption_status encryption_status,
unsigned int *content_protection)
{
if (encryption_status == MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF) {
*content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
return true;
}
if (hdcp_content_type == DRM_MODE_HDCP_CONTENT_TYPE0 &&
encryption_status <= MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON) {
*content_protection = DRM_MODE_CONTENT_PROTECTION_ENABLED;
return true;
}
if (hdcp_content_type == DRM_MODE_HDCP_CONTENT_TYPE1 &&
encryption_status == MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON) {
*content_protection = DRM_MODE_CONTENT_PROTECTION_ENABLED;
return true;
}
return false;
}
EXPORT_IF_KUNIT(hdcp_get_content_protection_from_status);
STATIC_IFN_KUNIT
void hdcp_get_link_display_adjustments(
bool enable_encryption,
u8 content_type,
bool fused_io_supported,
bool hdcp_lc_force_fw_enable,
bool hdcp_lc_enable_sw_fallback,
struct mod_hdcp_link_adjustment *link_adjust,
struct mod_hdcp_display_adjustment *display_adjust)
{
memset(link_adjust, 0, sizeof(*link_adjust));
memset(display_adjust, 0, sizeof(*display_adjust));
if (!enable_encryption) {
display_adjust->disable =
MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION;
return;
}
display_adjust->disable = MOD_HDCP_DISPLAY_NOT_DISABLE;
link_adjust->auth_delay = 2;
link_adjust->retry_limit = MAX_NUM_OF_ATTEMPTS;
if (content_type == DRM_MODE_HDCP_CONTENT_TYPE0) {
link_adjust->hdcp2.force_type = MOD_HDCP_FORCE_TYPE_0;
} else if (content_type == DRM_MODE_HDCP_CONTENT_TYPE1) {
link_adjust->hdcp1.disable = 1;
link_adjust->hdcp2.force_type = MOD_HDCP_FORCE_TYPE_1;
}
link_adjust->hdcp2.use_fw_locality_check =
fused_io_supported || hdcp_lc_force_fw_enable;
link_adjust->hdcp2.use_sw_locality_fallback =
hdcp_lc_enable_sw_fallback;
}
EXPORT_IF_KUNIT(hdcp_get_link_display_adjustments);
static void link_lock(struct hdcp_workqueue *work, bool lock)
{
int i = 0;
@ -212,8 +276,11 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work,
drm_connector_put(&hdcp_w->aconnector[conn_index]->base);
hdcp_w->aconnector[conn_index] = aconnector;
memset(&link_adjust, 0, sizeof(link_adjust));
memset(&display_adjust, 0, sizeof(display_adjust));
hdcp_get_link_display_adjustments(enable_encryption, content_type,
dc->caps.fused_io_supported,
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
@ -224,25 +291,9 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work,
hdcp_work->srm_size,
&hdcp_work->srm_version);
display_adjust.disable = MOD_HDCP_DISPLAY_NOT_DISABLE;
link_adjust.auth_delay = 2;
link_adjust.retry_limit = MAX_NUM_OF_ATTEMPTS;
if (content_type == DRM_MODE_HDCP_CONTENT_TYPE0) {
link_adjust.hdcp2.force_type = MOD_HDCP_FORCE_TYPE_0;
} else if (content_type == DRM_MODE_HDCP_CONTENT_TYPE1) {
link_adjust.hdcp1.disable = 1;
link_adjust.hdcp2.force_type = MOD_HDCP_FORCE_TYPE_1;
}
link_adjust.hdcp2.use_fw_locality_check =
(dc->caps.fused_io_supported || dc->debug.hdcp_lc_force_fw_enable);
link_adjust.hdcp2.use_sw_locality_fallback = dc->debug.hdcp_lc_enable_sw_fallback;
schedule_delayed_work(&hdcp_w->property_validate_dwork,
msecs_to_jiffies(DRM_HDCP_CHECK_PERIOD_MS));
} else {
display_adjust.disable = MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION;
hdcp_w->encryption_status[conn_index] = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;
cancel_delayed_work(&hdcp_w->property_validate_dwork);
}
@ -336,6 +387,7 @@ static void event_property_update(struct work_struct *work)
property_update_work);
struct amdgpu_dm_connector *aconnector = NULL;
struct drm_device *dev;
unsigned int content_protection;
long ret;
unsigned int conn_index;
struct drm_connector *connector;
@ -375,26 +427,15 @@ static void event_property_update(struct work_struct *work)
MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;
}
}
if (hdcp_work->encryption_status[conn_index] !=
MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF) {
if (conn_state->hdcp_content_type ==
DRM_MODE_HDCP_CONTENT_TYPE0 &&
hdcp_work->encryption_status[conn_index] <=
MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON) {
if (hdcp_get_content_protection_from_status(conn_state->hdcp_content_type,
hdcp_work->encryption_status[conn_index],
&content_protection)) {
if (content_protection == DRM_MODE_CONTENT_PROTECTION_ENABLED)
DRM_DEBUG_DRIVER("[HDCP_DM] DRM_MODE_CONTENT_PROTECTION_ENABLED\n");
drm_hdcp_update_content_protection(connector,
DRM_MODE_CONTENT_PROTECTION_ENABLED);
} else if (conn_state->hdcp_content_type ==
DRM_MODE_HDCP_CONTENT_TYPE1 &&
hdcp_work->encryption_status[conn_index] ==
MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON) {
drm_hdcp_update_content_protection(connector,
DRM_MODE_CONTENT_PROTECTION_ENABLED);
}
} else {
DRM_DEBUG_DRIVER("[HDCP_DM] DRM_MODE_CONTENT_PROTECTION_DESIRED\n");
drm_hdcp_update_content_protection(connector,
DRM_MODE_CONTENT_PROTECTION_DESIRED);
else
DRM_DEBUG_DRIVER("[HDCP_DM] DRM_MODE_CONTENT_PROTECTION_DESIRED\n");
drm_hdcp_update_content_protection(connector, content_protection);
}
drm_modeset_unlock(&dev->mode_config.connection_mutex);
}

View File

@ -96,6 +96,18 @@ struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev, struct
#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
void process_output(struct hdcp_workqueue *hdcp_work);
bool hdcp_get_content_protection_from_status(
unsigned int hdcp_content_type,
enum mod_hdcp_encryption_status encryption_status,
unsigned int *content_protection);
void hdcp_get_link_display_adjustments(
bool enable_encryption,
u8 content_type,
bool fused_io_supported,
bool hdcp_lc_force_fw_enable,
bool hdcp_lc_enable_sw_fallback,
struct mod_hdcp_link_adjustment *link_adjust,
struct mod_hdcp_display_adjustment *display_adjust);
#endif
#endif /* AMDGPU_DM_AMDGPU_DM_HDCP_H_ */

View File

@ -12,11 +12,241 @@
static void dummy_work_fn(struct work_struct *work) {}
/* Tests for hdcp_get_content_protection_from_status() */
/**
* dm_test_hdcp_get_cp_disabled_returns_desired - HDCP off maps to DESIRED
* @test: KUnit test context
*
* When encryption status is HDCP_OFF, content_protection should be set
* to DESIRED and the function should return true to indicate an update.
*/
static void dm_test_hdcp_get_cp_disabled_returns_desired(struct kunit *test)
{
unsigned int content_protection = DRM_MODE_CONTENT_PROTECTION_UNDESIRED;
bool update;
update = hdcp_get_content_protection_from_status(
DRM_MODE_HDCP_CONTENT_TYPE0,
MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF,
&content_protection);
KUNIT_EXPECT_TRUE(test, update);
KUNIT_EXPECT_EQ(test, content_protection,
DRM_MODE_CONTENT_PROTECTION_DESIRED);
}
/**
* dm_test_hdcp_get_cp_type0_returns_enabled - TYPE0 with TYPE0_ON maps to ENABLED
* @test: KUnit test context
*
* When content type is TYPE0 and encryption status is at or below
* HDCP2_TYPE0_ON, content_protection should be set to ENABLED.
*/
static void dm_test_hdcp_get_cp_type0_returns_enabled(struct kunit *test)
{
unsigned int content_protection = DRM_MODE_CONTENT_PROTECTION_UNDESIRED;
bool update;
update = hdcp_get_content_protection_from_status(
DRM_MODE_HDCP_CONTENT_TYPE0,
MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON,
&content_protection);
KUNIT_EXPECT_TRUE(test, update);
KUNIT_EXPECT_EQ(test, content_protection,
DRM_MODE_CONTENT_PROTECTION_ENABLED);
}
/**
* dm_test_hdcp_get_cp_type1_returns_enabled - TYPE1 with TYPE1_ON maps to ENABLED
* @test: KUnit test context
*
* When content type is TYPE1 and encryption status is exactly
* HDCP2_TYPE1_ON, content_protection should be set to ENABLED.
*/
static void dm_test_hdcp_get_cp_type1_returns_enabled(struct kunit *test)
{
unsigned int content_protection = DRM_MODE_CONTENT_PROTECTION_UNDESIRED;
bool update;
update = hdcp_get_content_protection_from_status(
DRM_MODE_HDCP_CONTENT_TYPE1,
MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON,
&content_protection);
KUNIT_EXPECT_TRUE(test, update);
KUNIT_EXPECT_EQ(test, content_protection,
DRM_MODE_CONTENT_PROTECTION_ENABLED);
}
/**
* dm_test_hdcp_get_cp_type1_rejects_type0_status - TYPE1 rejects TYPE0_ON
* @test: KUnit test context
*
* When content type is TYPE1 but encryption status is only TYPE0_ON,
* the function should return false and leave content_protection unchanged.
*/
static void dm_test_hdcp_get_cp_type1_rejects_type0_status(struct kunit *test)
{
unsigned int content_protection = DRM_MODE_CONTENT_PROTECTION_UNDESIRED;
bool update;
update = hdcp_get_content_protection_from_status(
DRM_MODE_HDCP_CONTENT_TYPE1,
MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON,
&content_protection);
KUNIT_EXPECT_FALSE(test, update);
KUNIT_EXPECT_EQ(test, content_protection,
DRM_MODE_CONTENT_PROTECTION_UNDESIRED);
}
/**
* dm_test_hdcp_get_cp_type0_rejects_type1_status - TYPE0 rejects TYPE1_ON
* @test: KUnit test context
*
* When content type is TYPE0 but encryption status exceeds the TYPE0_ON
* boundary (TYPE1_ON), the function should return false.
*/
static void dm_test_hdcp_get_cp_type0_rejects_type1_status(struct kunit *test)
{
unsigned int content_protection = DRM_MODE_CONTENT_PROTECTION_UNDESIRED;
bool update;
update = hdcp_get_content_protection_from_status(
DRM_MODE_HDCP_CONTENT_TYPE0,
MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON,
&content_protection);
KUNIT_EXPECT_FALSE(test, update);
KUNIT_EXPECT_EQ(test, content_protection,
DRM_MODE_CONTENT_PROTECTION_UNDESIRED);
}
/* Tests for hdcp_get_link_display_adjustments() */
/**
* dm_test_hdcp_get_adjustments_disable_authentication - disable path zeroes adjustments
* @test: KUnit test context
*
* When enable_encryption is false, display_adjust should disable
* authentication and all link_adjust fields should remain zeroed.
*/
static void dm_test_hdcp_get_adjustments_disable_authentication(struct kunit *test)
{
struct mod_hdcp_link_adjustment link_adjust;
struct mod_hdcp_display_adjustment display_adjust;
unsigned int disable;
unsigned int hdcp1_disable;
unsigned int force_type;
hdcp_get_link_display_adjustments(false, DRM_MODE_HDCP_CONTENT_TYPE0,
false, false, false, &link_adjust, &display_adjust);
disable = display_adjust.disable;
hdcp1_disable = link_adjust.hdcp1.disable;
force_type = link_adjust.hdcp2.force_type;
KUNIT_EXPECT_EQ(test, disable,
MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION);
KUNIT_EXPECT_EQ(test, link_adjust.auth_delay, 0);
KUNIT_EXPECT_EQ(test, link_adjust.retry_limit, 0);
KUNIT_EXPECT_EQ(test, hdcp1_disable, 0);
KUNIT_EXPECT_EQ(test, force_type, 0);
}
/**
* dm_test_hdcp_get_adjustments_type0_policy - TYPE0 enables HDCP1 and forces TYPE0
* @test: KUnit test context
*
* When encryption is enabled with content TYPE0, hdcp1 should remain
* enabled, force_type should be TYPE_0, and sw_locality_fallback should
* be propagated from the input parameter.
*/
static void dm_test_hdcp_get_adjustments_type0_policy(struct kunit *test)
{
struct mod_hdcp_link_adjustment link_adjust;
struct mod_hdcp_display_adjustment display_adjust;
unsigned int disable;
unsigned int hdcp1_disable;
unsigned int force_type;
hdcp_get_link_display_adjustments(true, DRM_MODE_HDCP_CONTENT_TYPE0,
false, false, true, &link_adjust, &display_adjust);
disable = display_adjust.disable;
hdcp1_disable = link_adjust.hdcp1.disable;
force_type = link_adjust.hdcp2.force_type;
KUNIT_EXPECT_EQ(test, disable,
MOD_HDCP_DISPLAY_NOT_DISABLE);
KUNIT_EXPECT_EQ(test, link_adjust.auth_delay, 2);
KUNIT_EXPECT_EQ(test, link_adjust.retry_limit, MAX_NUM_OF_ATTEMPTS);
KUNIT_EXPECT_EQ(test, hdcp1_disable, 0);
KUNIT_EXPECT_EQ(test, force_type,
MOD_HDCP_FORCE_TYPE_0);
KUNIT_EXPECT_FALSE(test, link_adjust.hdcp2.use_fw_locality_check);
KUNIT_EXPECT_TRUE(test, link_adjust.hdcp2.use_sw_locality_fallback);
}
/**
* dm_test_hdcp_get_adjustments_type1_policy - TYPE1 disables HDCP1 and forces TYPE1
* @test: KUnit test context
*
* When encryption is enabled with content TYPE1, hdcp1 should be
* disabled, force_type should be TYPE_1, and fw_locality_check should
* be enabled when hdcp_lc_force_fw_enable is set.
*/
static void dm_test_hdcp_get_adjustments_type1_policy(struct kunit *test)
{
struct mod_hdcp_link_adjustment link_adjust;
struct mod_hdcp_display_adjustment display_adjust;
unsigned int disable;
unsigned int hdcp1_disable;
unsigned int force_type;
hdcp_get_link_display_adjustments(true, DRM_MODE_HDCP_CONTENT_TYPE1,
false, true, false, &link_adjust, &display_adjust);
disable = display_adjust.disable;
hdcp1_disable = link_adjust.hdcp1.disable;
force_type = link_adjust.hdcp2.force_type;
KUNIT_EXPECT_EQ(test, disable,
MOD_HDCP_DISPLAY_NOT_DISABLE);
KUNIT_EXPECT_EQ(test, link_adjust.auth_delay, 2);
KUNIT_EXPECT_EQ(test, link_adjust.retry_limit, MAX_NUM_OF_ATTEMPTS);
KUNIT_EXPECT_EQ(test, hdcp1_disable, 1);
KUNIT_EXPECT_EQ(test, force_type,
MOD_HDCP_FORCE_TYPE_1);
KUNIT_EXPECT_TRUE(test, link_adjust.hdcp2.use_fw_locality_check);
KUNIT_EXPECT_FALSE(test, link_adjust.hdcp2.use_sw_locality_fallback);
}
/**
* dm_test_hdcp_get_adjustments_fused_io_enables_fw_check - fused_io enables FW locality check
* @test: KUnit test context
*
* When fused_io_supported is true, use_fw_locality_check should be
* enabled regardless of hdcp_lc_force_fw_enable.
*/
static void dm_test_hdcp_get_adjustments_fused_io_enables_fw_check(struct kunit *test)
{
struct mod_hdcp_link_adjustment link_adjust;
struct mod_hdcp_display_adjustment display_adjust;
hdcp_get_link_display_adjustments(true, DRM_MODE_HDCP_CONTENT_TYPE0,
true, false, false, &link_adjust, &display_adjust);
KUNIT_EXPECT_TRUE(test, link_adjust.hdcp2.use_fw_locality_check);
}
/* Tests for process_output() */
/*
* Helper: allocate and initialise a minimal hdcp_workqueue sufficient for
* process_output() testing. Only the three delayed works accessed by
/**
* alloc_test_workqueue - allocate a minimal hdcp_workqueue for testing
* @test: KUnit test context for managed allocation
*
* Allocates and initialises 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)
@ -33,9 +263,12 @@ static struct hdcp_workqueue *alloc_test_workqueue(struct kunit *test)
return work;
}
/*
/**
* dm_test_process_output_property_validate_always_scheduled - validate_dwork always queued
* @test: KUnit test context
*
* process_output() always schedules property_validate_dwork with delay=0,
* which queues the work item directly (bypassing the timer). Use
* which queues the work item directly (bypassing the timer). Uses
* work_pending() rather than delayed_work_pending() to detect this.
*/
static void dm_test_process_output_property_validate_always_scheduled(struct kunit *test)
@ -52,8 +285,12 @@ static void dm_test_process_output_property_validate_always_scheduled(struct kun
cancel_delayed_work_sync(&work->property_validate_dwork);
}
/*
* output.callback_needed=true must schedule callback_dwork.
/**
* dm_test_process_output_callback_needed - callback_needed schedules callback_dwork
* @test: KUnit test context
*
* When output.callback_needed is true, process_output() must schedule
* callback_dwork with the specified delay.
*/
static void dm_test_process_output_callback_needed(struct kunit *test)
{
@ -70,8 +307,12 @@ static void dm_test_process_output_callback_needed(struct kunit *test)
cancel_delayed_work_sync(&work->property_validate_dwork);
}
/*
* output.callback_stop=true must cancel a previously scheduled callback_dwork.
/**
* dm_test_process_output_callback_stop - callback_stop cancels callback_dwork
* @test: KUnit test context
*
* When output.callback_stop is true, process_output() must cancel a
* previously scheduled callback_dwork.
*/
static void dm_test_process_output_callback_stop(struct kunit *test)
{
@ -90,8 +331,12 @@ static void dm_test_process_output_callback_stop(struct kunit *test)
cancel_delayed_work_sync(&work->property_validate_dwork);
}
/*
* output.watchdog_timer_needed=true must schedule watchdog_timer_dwork.
/**
* dm_test_process_output_watchdog_needed - watchdog_needed schedules watchdog_dwork
* @test: KUnit test context
*
* When output.watchdog_timer_needed is true, process_output() must
* schedule watchdog_timer_dwork with the specified delay.
*/
static void dm_test_process_output_watchdog_needed(struct kunit *test)
{
@ -108,9 +353,12 @@ static void dm_test_process_output_watchdog_needed(struct kunit *test)
cancel_delayed_work_sync(&work->property_validate_dwork);
}
/*
* output.watchdog_timer_stop=true must cancel a previously scheduled
* watchdog_timer_dwork.
/**
* dm_test_process_output_watchdog_stop - watchdog_stop cancels watchdog_dwork
* @test: KUnit test context
*
* When output.watchdog_timer_stop is true, process_output() must cancel
* a previously scheduled watchdog_timer_dwork.
*/
static void dm_test_process_output_watchdog_stop(struct kunit *test)
{
@ -129,9 +377,12 @@ static void dm_test_process_output_watchdog_stop(struct kunit *test)
cancel_delayed_work_sync(&work->property_validate_dwork);
}
/*
* Both callback_needed and watchdog_timer_needed set: both dworks are
* scheduled independently.
/**
* dm_test_process_output_callback_and_watchdog_needed - both dworks scheduled independently
* @test: KUnit test context
*
* When both callback_needed and watchdog_timer_needed are set,
* process_output() must schedule both dworks independently.
*/
static void dm_test_process_output_callback_and_watchdog_needed(struct kunit *test)
{
@ -154,6 +405,18 @@ static void dm_test_process_output_callback_and_watchdog_needed(struct kunit *te
/* End of tests for process_output() */
static struct kunit_case dm_hdcp_test_cases[] = {
/* hdcp_get_content_protection_from_status() */
KUNIT_CASE(dm_test_hdcp_get_cp_disabled_returns_desired),
KUNIT_CASE(dm_test_hdcp_get_cp_type0_returns_enabled),
KUNIT_CASE(dm_test_hdcp_get_cp_type1_returns_enabled),
KUNIT_CASE(dm_test_hdcp_get_cp_type1_rejects_type0_status),
KUNIT_CASE(dm_test_hdcp_get_cp_type0_rejects_type1_status),
/* hdcp_get_link_display_adjustments() */
KUNIT_CASE(dm_test_hdcp_get_adjustments_disable_authentication),
KUNIT_CASE(dm_test_hdcp_get_adjustments_type0_policy),
KUNIT_CASE(dm_test_hdcp_get_adjustments_type1_policy),
KUNIT_CASE(dm_test_hdcp_get_adjustments_fused_io_enables_fw_check),
/* process_output() */
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),