summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Hung <alex.hung@amd.com>2026-06-24 23:34:18 -0600
committerAlex Deucher <alexander.deucher@amd.com>2026-07-14 19:12:36 -0400
commitf5e5e134dcf010d2ae45a579fe7470bfa357a2c2 (patch)
tree9afa57bd722e41f33f575b63c97eb0d4ace67bf1
parent0984a22c9ffc241bf8190d64bf3d26d6f098e73c (diff)
downloadlinux-f5e5e134dcf010d2ae45a579fe7470bfa357a2c2.tar.gz
linux-f5e5e134dcf010d2ae45a579fe7470bfa357a2c2.zip
drm/amd/display: Test I2C, stubs and MST early returns
[WHAT] Add KUnit coverage for dm_helpers_submit_i2c, dm_helper_dmub_aux_transfer_sync, the empty stub helpers, the MST null-connector early returns, dm_helpers_dmub_outbox_interrupt_control, dm_helpers_mst_enable_stream_features and dm_helpers_enable_periodic_detection. Assisted-by: Copilot:Claude-Opus-4.8 Reviewed-by: Bhawanpreet Lakha <bhawanpreet.lakha@amd.com> Signed-off-by: Alex Hung <alex.hung@amd.com> Signed-off-by: George Zhang <george.zhang@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c18
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_helpers_test.c541
2 files changed, 559 insertions, 0 deletions
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
index f7ba7eab69e2..ff23aee54c16 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
@@ -296,6 +296,7 @@ void dm_helpers_dp_update_branch_info(
struct dc_context *ctx,
const struct dc_link *link)
{}
+EXPORT_IF_KUNIT(dm_helpers_dp_update_branch_info);
static void dm_helpers_construct_old_payload(
struct drm_dp_mst_topology_mgr *mgr,
@@ -380,6 +381,7 @@ bool dm_helpers_dp_mst_write_payload_allocation_table(
return true;
}
+EXPORT_IF_KUNIT(dm_helpers_dp_mst_write_payload_allocation_table);
/*
* poll pending down reply
@@ -388,6 +390,7 @@ void dm_helpers_dp_mst_poll_pending_down_reply(
struct dc_context *ctx,
const struct dc_link *link)
{}
+EXPORT_IF_KUNIT(dm_helpers_dp_mst_poll_pending_down_reply);
/*
* Clear payload allocation table before enable MST DP link.
@@ -396,6 +399,7 @@ void dm_helpers_dp_mst_clear_payload_allocation_table(
struct dc_context *ctx,
const struct dc_link *link)
{}
+EXPORT_IF_KUNIT(dm_helpers_dp_mst_clear_payload_allocation_table);
/*
* Polls for ACT (allocation change trigger) handled and sends
@@ -426,6 +430,7 @@ enum act_return_status dm_helpers_dp_mst_poll_for_allocation_change_trigger(
return ACT_SUCCESS;
}
+EXPORT_IF_KUNIT(dm_helpers_dp_mst_poll_for_allocation_change_trigger);
void dm_helpers_dp_mst_send_payload_allocation(
struct dc_context *ctx,
@@ -460,6 +465,7 @@ void dm_helpers_dp_mst_send_payload_allocation(
clr_flag, false);
}
}
+EXPORT_IF_KUNIT(dm_helpers_dp_mst_send_payload_allocation);
void dm_helpers_dp_mst_update_mst_mgr_for_deallocation(
struct dc_context *ctx,
@@ -488,6 +494,7 @@ void dm_helpers_dp_mst_update_mst_mgr_for_deallocation(
amdgpu_dm_set_mst_status(&aconnector->mst_status, set_flag, true);
amdgpu_dm_set_mst_status(&aconnector->mst_status, clr_flag, false);
}
+EXPORT_IF_KUNIT(dm_helpers_dp_mst_update_mst_mgr_for_deallocation);
void dm_dtn_log_begin(struct dc_context *ctx,
struct dc_log_buffer_ctx *log_ctx)
@@ -707,6 +714,7 @@ bool dm_helpers_submit_i2c(
return result;
}
+EXPORT_IF_KUNIT(dm_helpers_submit_i2c);
bool dm_helpers_execute_fused_io(
struct dc_context *ctx,
@@ -1023,6 +1031,7 @@ bool dm_helpers_is_dp_sink_present(struct dc_link *link)
mutex_unlock(&aconnector->dm_dp_aux.aux.hw_mutex);
return dp_sink_present;
}
+EXPORT_IF_KUNIT(dm_helpers_is_dp_sink_present);
STATIC_IFN_KUNIT int
dm_helpers_probe_acpi_edid(void *data, u8 *buf, unsigned int block, size_t len)
@@ -1312,6 +1321,7 @@ int dm_helper_dmub_aux_transfer_sync(
return amdgpu_dm_process_dmub_aux_transfer_sync(ctx, link->link_index, payload,
operation_result);
}
+EXPORT_IF_KUNIT(dm_helper_dmub_aux_transfer_sync);
int dm_helpers_dmub_set_config_sync(struct dc_context *ctx,
const struct dc_link *link,
@@ -1326,18 +1336,21 @@ void dm_set_dcn_clocks(struct dc_context *ctx, struct dc_clocks *clks)
{
/* TODO: something */
}
+EXPORT_IF_KUNIT(dm_set_dcn_clocks);
void dm_helpers_dmu_timeout(struct dc_context *ctx)
{
// TODO:
//amdgpu_device_gpu_recover(dc_context->driver-context, NULL);
}
+EXPORT_IF_KUNIT(dm_helpers_dmu_timeout);
void dm_helpers_smu_timeout(struct dc_context *ctx, unsigned int msg_id, unsigned int param, unsigned int timeout_us)
{
// TODO:
//amdgpu_device_gpu_recover(dc_context->driver-context, NULL);
}
+EXPORT_IF_KUNIT(dm_helpers_smu_timeout);
void dm_helpers_init_panel_settings(
struct dc_context *ctx,
@@ -1410,6 +1423,7 @@ bool dm_helpers_dmub_outbox_interrupt_control(struct dc_context *ctx, bool enabl
enable ? "en" : "dis", ret);
return ret;
}
+EXPORT_IF_KUNIT(dm_helpers_dmub_outbox_interrupt_control);
void dm_helpers_mst_enable_stream_features(const struct dc_stream_state *stream)
{
@@ -1435,6 +1449,7 @@ void dm_helpers_mst_enable_stream_features(const struct dc_stream_state *stream)
&new_downspread.raw,
sizeof(new_downspread));
}
+EXPORT_IF_KUNIT(dm_helpers_mst_enable_stream_features);
bool dm_helpers_dp_handle_test_pattern_request(
struct dc_context *ctx,
@@ -1578,6 +1593,7 @@ void dm_set_phyd32clk(struct dc_context *ctx, int freq_khz)
{
// TODO
}
+EXPORT_IF_KUNIT(dm_set_phyd32clk);
void dm_helpers_enable_periodic_detection(struct dc_context *ctx, bool enable)
{
@@ -1589,6 +1605,7 @@ void dm_helpers_enable_periodic_detection(struct dc_context *ctx, bool enable)
schedule_work(&adev->dm.idle_workqueue->work);
}
}
+EXPORT_IF_KUNIT(dm_helpers_enable_periodic_detection);
void dm_helpers_dp_mst_update_branch_bandwidth(
struct dc_context *ctx,
@@ -1596,6 +1613,7 @@ void dm_helpers_dp_mst_update_branch_bandwidth(
{
// TODO
}
+EXPORT_IF_KUNIT(dm_helpers_dp_mst_update_branch_bandwidth);
STATIC_IFN_KUNIT const uint32_t dm_freesync_pcon_whitelist[] = {
DP_BRANCH_DEVICE_ID_0060AD,
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_helpers_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_helpers_test.c
index 599de8240bee..29feb8c3940b 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_helpers_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_helpers_test.c
@@ -1421,6 +1421,14 @@ static ssize_t dm_test_synaptics_aux_transfer(struct drm_dp_aux *aux,
return msg->size;
}
+static struct dm_test_synaptics_aux *dm_test_current_aux_recorder;
+
+static ssize_t dm_test_current_aux_transfer(struct drm_dp_aux *aux,
+ struct drm_dp_aux_msg *msg)
+{
+ return dm_test_synaptics_aux_transfer(&dm_test_current_aux_recorder->aux, msg);
+}
+
static struct dm_test_synaptics_aux *dm_test_alloc_synaptics_aux_with_dev(struct kunit *test,
struct drm_device *drm_dev)
{
@@ -2022,6 +2030,507 @@ static void dm_test_fill_mst_payload_table_empty(struct kunit *test)
KUNIT_EXPECT_EQ(test, table.stream_allocations[0].slot_count, 12);
}
+/* Tests for dm_helpers_submit_i2c() */
+
+/**
+ * dm_test_submit_i2c_null_priv - Test i2c submit returns false without connector
+ * @test: The KUnit test context
+ */
+static void dm_test_submit_i2c_null_priv(struct kunit *test)
+{
+ struct dc_link *link;
+ struct i2c_command cmd = {0};
+
+ link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, link);
+
+ KUNIT_EXPECT_FALSE(test, dm_helpers_submit_i2c(NULL, link, &cmd));
+}
+
+struct dm_test_i2c_adapter {
+ struct i2c_adapter base;
+ struct kunit *test;
+ struct i2c_payload *expected_payloads;
+ int expected_num;
+ int ret;
+ int calls;
+};
+
+static void dm_test_i2c_lock_bus(struct i2c_adapter *adapter,
+ unsigned int flags)
+{
+ rt_mutex_lock(&adapter->bus_lock);
+}
+
+static int dm_test_i2c_trylock_bus(struct i2c_adapter *adapter,
+ unsigned int flags)
+{
+ return rt_mutex_trylock(&adapter->bus_lock);
+}
+
+static void dm_test_i2c_unlock_bus(struct i2c_adapter *adapter,
+ unsigned int flags)
+{
+ rt_mutex_unlock(&adapter->bus_lock);
+}
+
+static const struct i2c_lock_operations dm_test_i2c_lock_ops = {
+ .lock_bus = dm_test_i2c_lock_bus,
+ .trylock_bus = dm_test_i2c_trylock_bus,
+ .unlock_bus = dm_test_i2c_unlock_bus,
+};
+
+static int dm_test_i2c_master_xfer(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs,
+ int num)
+{
+ struct dm_test_i2c_adapter *fake;
+ int i;
+
+ fake = container_of(adapter, struct dm_test_i2c_adapter, base);
+ fake->calls++;
+
+ KUNIT_EXPECT_EQ(fake->test, num, fake->expected_num);
+
+ for (i = 0; i < num; i++) {
+ KUNIT_EXPECT_EQ(fake->test, msgs[i].flags,
+ fake->expected_payloads[i].write ? 0 : I2C_M_RD);
+ KUNIT_EXPECT_EQ(fake->test, msgs[i].addr,
+ (u16)fake->expected_payloads[i].address);
+ KUNIT_EXPECT_EQ(fake->test, msgs[i].len,
+ (u16)fake->expected_payloads[i].length);
+ KUNIT_EXPECT_PTR_EQ(fake->test, msgs[i].buf,
+ fake->expected_payloads[i].data);
+ }
+
+ return fake->ret;
+}
+
+static const struct i2c_algorithm dm_test_i2c_algorithm = {
+ .master_xfer = dm_test_i2c_master_xfer,
+};
+
+static void dm_test_submit_i2c_transfer(struct kunit *test,
+ bool full_transfer)
+{
+ struct amdgpu_dm_connector *aconnector;
+ struct dm_test_i2c_adapter *fake;
+ struct dc_link *link;
+ u8 write_data[2] = { 0x12, 0x34 };
+ u8 read_data[3] = { 0 };
+ struct i2c_payload payloads[] = {
+ { true, 0x50, sizeof(write_data), write_data },
+ { false, 0x51, sizeof(read_data), read_data },
+ };
+ struct i2c_command cmd = {
+ .payloads = payloads,
+ .number_of_payloads = ARRAY_SIZE(payloads),
+ };
+
+ link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, link);
+ aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, aconnector);
+ fake = kunit_kzalloc(test, sizeof(*fake), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, fake);
+
+ fake->test = test;
+ fake->expected_payloads = payloads;
+ fake->expected_num = ARRAY_SIZE(payloads);
+ fake->ret = full_transfer ? ARRAY_SIZE(payloads) : 1;
+ fake->base.algo = &dm_test_i2c_algorithm;
+ fake->base.lock_ops = &dm_test_i2c_lock_ops;
+ rt_mutex_init(&fake->base.bus_lock);
+ rt_mutex_init(&fake->base.mux_lock);
+
+ aconnector->i2c = (struct amdgpu_i2c_adapter *)fake;
+ link->priv = aconnector;
+
+ KUNIT_EXPECT_EQ(test, dm_helpers_submit_i2c(NULL, link, &cmd), full_transfer);
+ KUNIT_EXPECT_EQ(test, fake->calls, 1);
+}
+
+/**
+ * dm_test_submit_i2c_success - Test payloads are mapped to i2c_msg array
+ * @test: The KUnit test context
+ */
+static void dm_test_submit_i2c_success(struct kunit *test)
+{
+ dm_test_submit_i2c_transfer(test, true);
+}
+
+/**
+ * dm_test_submit_i2c_partial_transfer - Test short i2c transfer returns false
+ * @test: The KUnit test context
+ */
+static void dm_test_submit_i2c_partial_transfer(struct kunit *test)
+{
+ dm_test_submit_i2c_transfer(test, false);
+}
+
+/* Tests for dm_helper_dmub_aux_transfer_sync() */
+
+/**
+ * dm_test_dmub_aux_transfer_sync_hpd_discon - Test aux transfer with HPD disconnected
+ * @test: The KUnit test context
+ */
+static void dm_test_dmub_aux_transfer_sync_hpd_discon(struct kunit *test)
+{
+ struct dc_link *link;
+ struct dc_context *ctx;
+ struct aux_payload payload = {0};
+ enum aux_return_code_type result = AUX_RET_SUCCESS;
+ int ret;
+
+ link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, link);
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ link->hpd_status = false;
+
+ ret = dm_helper_dmub_aux_transfer_sync(ctx, link, &payload, &result);
+
+ KUNIT_EXPECT_EQ(test, ret, -1);
+ KUNIT_EXPECT_EQ(test, (int)result, (int)AUX_RET_ERROR_HPD_DISCON);
+}
+
+/* Tests for empty stub functions (must not crash) */
+
+/**
+ * dm_test_dp_update_branch_info_no_crash - Test empty stub does not crash
+ * @test: The KUnit test context
+ */
+static void dm_test_dp_update_branch_info_no_crash(struct kunit *test)
+{
+ dm_helpers_dp_update_branch_info(NULL, NULL);
+ KUNIT_EXPECT_TRUE(test, true);
+}
+
+/**
+ * dm_test_mst_poll_pending_down_reply_no_crash - Test empty stub does not crash
+ * @test: The KUnit test context
+ */
+static void dm_test_mst_poll_pending_down_reply_no_crash(struct kunit *test)
+{
+ dm_helpers_dp_mst_poll_pending_down_reply(NULL, NULL);
+ KUNIT_EXPECT_TRUE(test, true);
+}
+
+/**
+ * dm_test_mst_clear_payload_alloc_table_no_crash - Test empty stub does not crash
+ * @test: The KUnit test context
+ */
+static void dm_test_mst_clear_payload_alloc_table_no_crash(struct kunit *test)
+{
+ dm_helpers_dp_mst_clear_payload_allocation_table(NULL, NULL);
+ KUNIT_EXPECT_TRUE(test, true);
+}
+
+/**
+ * dm_test_set_dcn_clocks_no_crash - Test empty stub does not crash
+ * @test: The KUnit test context
+ */
+static void dm_test_set_dcn_clocks_no_crash(struct kunit *test)
+{
+ dm_set_dcn_clocks(NULL, NULL);
+ KUNIT_EXPECT_TRUE(test, true);
+}
+
+/**
+ * dm_test_dmu_timeout_no_crash - Test empty stub does not crash
+ * @test: The KUnit test context
+ */
+static void dm_test_dmu_timeout_no_crash(struct kunit *test)
+{
+ dm_helpers_dmu_timeout(NULL);
+ KUNIT_EXPECT_TRUE(test, true);
+}
+
+/**
+ * dm_test_smu_timeout_no_crash - Test empty stub does not crash
+ * @test: The KUnit test context
+ */
+static void dm_test_smu_timeout_no_crash(struct kunit *test)
+{
+ dm_helpers_smu_timeout(NULL, 0, 0, 0);
+ KUNIT_EXPECT_TRUE(test, true);
+}
+
+/**
+ * dm_test_set_phyd32clk_no_crash - Test empty stub does not crash
+ * @test: The KUnit test context
+ */
+static void dm_test_set_phyd32clk_no_crash(struct kunit *test)
+{
+ dm_set_phyd32clk(NULL, 0);
+ KUNIT_EXPECT_TRUE(test, true);
+}
+
+/**
+ * dm_test_mst_update_branch_bandwidth_no_crash - Test empty stub does not crash
+ * @test: The KUnit test context
+ */
+static void dm_test_mst_update_branch_bandwidth_no_crash(struct kunit *test)
+{
+ dm_helpers_dp_mst_update_branch_bandwidth(NULL, NULL);
+ KUNIT_EXPECT_TRUE(test, true);
+}
+
+/* Tests for MST functions null-connector early returns */
+
+/**
+ * dm_test_mst_write_payload_alloc_table_null_ctx - Test null connector returns false
+ * @test: The KUnit test context
+ */
+static void dm_test_mst_write_payload_alloc_table_null_ctx(struct kunit *test)
+{
+ struct dc_stream_state *stream;
+
+ stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, stream);
+
+ /* dm_stream_context is NULL → aconnector is NULL → return false */
+ KUNIT_EXPECT_FALSE(test,
+ dm_helpers_dp_mst_write_payload_allocation_table(NULL, stream, NULL, true));
+}
+
+/**
+ * dm_test_mst_poll_for_act_null_ctx - Test null connector returns ACT_FAILED
+ * @test: The KUnit test context
+ */
+static void dm_test_mst_poll_for_act_null_ctx(struct kunit *test)
+{
+ struct dc_stream_state *stream;
+
+ stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, stream);
+
+ KUNIT_EXPECT_EQ(test,
+ (int)dm_helpers_dp_mst_poll_for_allocation_change_trigger(NULL, stream),
+ (int)ACT_FAILED);
+}
+
+/**
+ * dm_test_mst_send_payload_alloc_null_ctx - Test null connector does not crash
+ * @test: The KUnit test context
+ */
+static void dm_test_mst_send_payload_alloc_null_ctx(struct kunit *test)
+{
+ struct dc_stream_state *stream;
+
+ stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, stream);
+
+ /* Should early-return without crash */
+ dm_helpers_dp_mst_send_payload_allocation(NULL, stream);
+ KUNIT_EXPECT_TRUE(test, true);
+}
+
+/**
+ * dm_test_mst_update_mgr_dealloc_null_ctx - Test null connector does not crash
+ * @test: The KUnit test context
+ */
+static void dm_test_mst_update_mgr_dealloc_null_ctx(struct kunit *test)
+{
+ struct dc_stream_state *stream;
+
+ stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, stream);
+
+ dm_helpers_dp_mst_update_mst_mgr_for_deallocation(NULL, stream);
+ KUNIT_EXPECT_TRUE(test, true);
+}
+
+/**
+ * dm_test_is_dp_sink_present_null_priv - Test null connector returns true
+ * @test: The KUnit test context
+ */
+static void dm_test_is_dp_sink_present_null_priv(struct kunit *test)
+{
+ struct dc_link *link;
+
+ link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, link);
+
+ /* NULL priv → DRM_ERROR + return true */
+ KUNIT_EXPECT_TRUE(test, dm_helpers_is_dp_sink_present(link));
+}
+
+/* Tests for dm_helpers_dmub_outbox_interrupt_control() */
+
+/**
+ * dm_test_dmub_outbox_interrupt_control_null_dc - Test outbox irq control with NULL dc
+ * @test: The KUnit test context
+ *
+ * dc_interrupt_set() is NULL-safe and returns false when dc is NULL, so the
+ * helper returns false without touching real interrupt hardware.
+ */
+static void dm_test_dmub_outbox_interrupt_control_null_dc(struct kunit *test)
+{
+ struct dc_context *ctx;
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ /* ctx->dc is NULL → dc_interrupt_set returns false */
+ KUNIT_EXPECT_FALSE(test, dm_helpers_dmub_outbox_interrupt_control(ctx, true));
+ KUNIT_EXPECT_FALSE(test, dm_helpers_dmub_outbox_interrupt_control(ctx, false));
+}
+
+/* Tests for dm_helpers_mst_enable_stream_features() */
+
+/**
+ * dm_test_mst_enable_stream_features_aux_disabled - Test early return when aux disabled
+ * @test: The KUnit test context
+ */
+static void dm_test_mst_enable_stream_features_aux_disabled(struct kunit *test)
+{
+ struct dc_stream_state *stream;
+ struct dc_link *link;
+
+ link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, link);
+ stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, stream);
+
+ stream->link = link;
+ link->aux_access_disabled = true;
+
+ /* aux_access_disabled → early return without DPCD access */
+ dm_helpers_mst_enable_stream_features(stream);
+ KUNIT_EXPECT_TRUE(test, true);
+}
+
+/**
+ * dm_test_mst_enable_stream_features_writes_downspread - Test MSA ignore write
+ * @test: The KUnit test context
+ */
+static void dm_test_mst_enable_stream_features_writes_downspread(struct kunit *test)
+{
+ struct dm_test_synaptics_aux *fixture;
+ struct amdgpu_dm_connector *aconnector;
+ struct dc_stream_state *stream;
+ struct amdgpu_device *adev;
+ struct dc_link *link;
+
+ adev = dm_kunit_alloc_adev(test);
+ fixture = dm_test_alloc_synaptics_aux_with_dev(test, &adev->ddev);
+ aconnector = dm_kunit_alloc_connector(test, adev, NULL);
+ link = dm_kunit_alloc_link(test);
+ stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, stream);
+
+ dm_test_current_aux_recorder = fixture;
+ aconnector->dm_dp_aux.aux.drm_dev = &adev->ddev;
+ aconnector->dm_dp_aux.aux.transfer = dm_test_current_aux_transfer;
+ drm_dp_aux_init(&aconnector->dm_dp_aux.aux);
+ link->priv = aconnector;
+ stream->link = link;
+ stream->ignore_msa_timing_param = true;
+
+ dm_helpers_mst_enable_stream_features(stream);
+
+ KUNIT_EXPECT_EQ(test, fixture->downspread_reads, 1U);
+ KUNIT_EXPECT_EQ(test, fixture->downspread_writes, 1U);
+ KUNIT_EXPECT_EQ(test, fixture->last_dpcd_write_address,
+ DP_DOWNSPREAD_CTRL);
+ KUNIT_EXPECT_EQ(test, fixture->dpcd_write_value, (u8)BIT(7));
+}
+
+/* Tests for dm_helpers_enable_periodic_detection() */
+
+struct dm_test_idle_work {
+ struct idle_workqueue base;
+ bool ran;
+};
+
+static void dm_test_idle_work_func(struct work_struct *work)
+{
+ struct dm_test_idle_work *idle_work;
+
+ idle_work = container_of(work, struct dm_test_idle_work, base.work);
+ idle_work->ran = true;
+}
+
+/**
+ * dm_test_enable_periodic_detection_no_workqueue - Test no-op without idle workqueue
+ * @test: The KUnit test context
+ */
+static void dm_test_enable_periodic_detection_no_workqueue(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct dc_context *ctx;
+
+ adev = dm_kunit_alloc_adev(test);
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+ ctx->driver_context = adev;
+
+ /* adev->dm.idle_workqueue is NULL → no-op, no crash */
+ dm_helpers_enable_periodic_detection(ctx, true);
+ KUNIT_EXPECT_TRUE(test, true);
+}
+
+/**
+ * dm_test_enable_periodic_detection_updates_enable - Test idle work enable flag
+ * @test: The KUnit test context
+ *
+ * Keeps idle_workqueue->running set so the helper only updates the enable flag
+ * and does not queue the idle worker.
+ */
+static void dm_test_enable_periodic_detection_updates_enable(struct kunit *test)
+{
+ struct idle_workqueue *idle_work;
+ struct amdgpu_device *adev;
+ struct dc_context *ctx;
+
+ adev = dm_kunit_alloc_adev(test);
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+ idle_work = kunit_kzalloc(test, sizeof(*idle_work), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, idle_work);
+
+ ctx->driver_context = adev;
+ adev->dm.idle_workqueue = idle_work;
+ idle_work->running = true;
+
+ dm_helpers_enable_periodic_detection(ctx, true);
+ KUNIT_EXPECT_TRUE(test, idle_work->enable);
+
+ dm_helpers_enable_periodic_detection(ctx, false);
+ KUNIT_EXPECT_FALSE(test, idle_work->enable);
+}
+
+/**
+ * dm_test_enable_periodic_detection_schedules_work - Test headless schedule path
+ * @test: The KUnit test context
+ */
+static void dm_test_enable_periodic_detection_schedules_work(struct kunit *test)
+{
+ struct dm_test_idle_work *idle_work;
+ struct amdgpu_device *adev;
+ struct dc_context *ctx;
+
+ adev = dm_kunit_alloc_adev(test);
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+ idle_work = kunit_kzalloc(test, sizeof(*idle_work), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, idle_work);
+
+ ctx->driver_context = adev;
+ adev->dm.ddev = &adev->ddev;
+ adev->dm.idle_workqueue = &idle_work->base;
+ INIT_WORK(&idle_work->base.work, dm_test_idle_work_func);
+
+ dm_helpers_enable_periodic_detection(ctx, true);
+ flush_work(&idle_work->base.work);
+
+ KUNIT_EXPECT_TRUE(test, idle_work->base.enable);
+ KUNIT_EXPECT_TRUE(test, idle_work->ran);
+}
+
static struct kunit_case amdgpu_dm_helpers_test_cases[] = {
/* edid_extract_panel_id */
KUNIT_CASE(dm_test_edid_extract_panel_id_basic),
@@ -2127,6 +2636,38 @@ static struct kunit_case amdgpu_dm_helpers_test_cases[] = {
KUNIT_CASE(dm_test_fill_mst_payload_table_enable),
KUNIT_CASE(dm_test_fill_mst_payload_table_disable),
KUNIT_CASE(dm_test_fill_mst_payload_table_empty),
+ /* dm_helpers_submit_i2c */
+ KUNIT_CASE(dm_test_submit_i2c_null_priv),
+ KUNIT_CASE(dm_test_submit_i2c_success),
+ KUNIT_CASE(dm_test_submit_i2c_partial_transfer),
+ /* dm_helper_dmub_aux_transfer_sync */
+ KUNIT_CASE(dm_test_dmub_aux_transfer_sync_hpd_discon),
+ /* Empty stub functions */
+ KUNIT_CASE(dm_test_dp_update_branch_info_no_crash),
+ KUNIT_CASE(dm_test_mst_poll_pending_down_reply_no_crash),
+ KUNIT_CASE(dm_test_mst_clear_payload_alloc_table_no_crash),
+ KUNIT_CASE(dm_test_set_dcn_clocks_no_crash),
+ KUNIT_CASE(dm_test_dmu_timeout_no_crash),
+ KUNIT_CASE(dm_test_smu_timeout_no_crash),
+ KUNIT_CASE(dm_test_set_phyd32clk_no_crash),
+ KUNIT_CASE(dm_test_mst_update_branch_bandwidth_no_crash),
+ /* MST null-connector early returns */
+ KUNIT_CASE(dm_test_mst_write_payload_alloc_table_null_ctx),
+ KUNIT_CASE(dm_test_mst_poll_for_act_null_ctx),
+ /* dm_helpers_dp_mst_poll_for_allocation_change_trigger success/fail */
+ KUNIT_CASE(dm_test_mst_send_payload_alloc_null_ctx),
+ KUNIT_CASE(dm_test_mst_update_mgr_dealloc_null_ctx),
+ /* dm_helpers_is_dp_sink_present */
+ KUNIT_CASE(dm_test_is_dp_sink_present_null_priv),
+ /* dm_helpers_dmub_outbox_interrupt_control */
+ KUNIT_CASE(dm_test_dmub_outbox_interrupt_control_null_dc),
+ /* dm_helpers_mst_enable_stream_features */
+ KUNIT_CASE(dm_test_mst_enable_stream_features_aux_disabled),
+ KUNIT_CASE(dm_test_mst_enable_stream_features_writes_downspread),
+ /* dm_helpers_enable_periodic_detection */
+ KUNIT_CASE(dm_test_enable_periodic_detection_no_workqueue),
+ KUNIT_CASE(dm_test_enable_periodic_detection_updates_enable),
+ KUNIT_CASE(dm_test_enable_periodic_detection_schedules_work),
{}
};