From: Alex Hung <[email protected]>

[WHAT]
Add KUnit coverage for dm_helpers_construct_old_payload, the DP MST
payload allocation, poll-for-ACT and deallocation success paths, the
full dm_helpers_dp_write_dsc_enable matrix and
dm_helpers_dp_handle_test_pattern_request.

Assisted-by: Copilot:Claude-Opus-4.8
Reviewed-by: Bhawanpreet Lakha <[email protected]>
Signed-off-by: Alex Hung <[email protected]>
Signed-off-by: George Zhang <[email protected]>
---
 .../amd/display/amdgpu_dm/amdgpu_dm_helpers.c |   4 +-
 .../amd/display/amdgpu_dm/amdgpu_dm_helpers.h |   4 +
 .../amdgpu_dm/tests/amdgpu_dm_helpers_test.c  | 806 ++++++++++++++++++
 3 files changed, 813 insertions(+), 1 deletion(-)

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 48ed6d8d9580..d177489a2684 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
@@ -298,7 +298,7 @@ void dm_helpers_dp_update_branch_info(
 {}
 EXPORT_IF_KUNIT(dm_helpers_dp_update_branch_info);

-static void dm_helpers_construct_old_payload(
+STATIC_IFN_KUNIT void dm_helpers_construct_old_payload(
                        struct drm_dp_mst_topology_mgr *mgr,
                        struct drm_dp_mst_topology_state *mst_state,
                        struct drm_dp_mst_atomic_payload *new_payload,
@@ -329,6 +329,7 @@ static void dm_helpers_construct_old_payload(
        old_payload->time_slots = allocated_time_slots;
        old_payload->pbn = allocated_time_slots * pbn_per_slot;
 }
+EXPORT_IF_KUNIT(dm_helpers_construct_old_payload);

 /*
  * Writes payload allocation table in immediate downstream device.
@@ -1588,6 +1589,7 @@ bool dm_helpers_dp_handle_test_pattern_request(

        return false;
 }
+EXPORT_IF_KUNIT(dm_helpers_dp_handle_test_pattern_request);

 void dm_set_phyd32clk(struct dc_context *ctx, int freq_khz)
 {
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.h 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.h
index e256e1e12422..0eab820757c1 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.h
@@ -33,6 +33,10 @@ void fill_dc_mst_payload_table_from_drm(struct dc_link *link,
                                         bool enable,
                                         struct drm_dp_mst_atomic_payload 
*target_payload,
                                         struct 
dc_dp_mst_stream_allocation_table *table);
+void dm_helpers_construct_old_payload(struct drm_dp_mst_topology_mgr *mgr,
+                                     struct drm_dp_mst_topology_state 
*mst_state,
+                                     struct drm_dp_mst_atomic_payload 
*new_payload,
+                                     struct drm_dp_mst_atomic_payload 
*old_payload);
 bool dm_helpers_dp_write_dsc_enable(struct dc_context *ctx,
                                    const struct dc_stream_state *stream,
                                    bool enable);
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 bde697092032..058e1ad15dfe 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
@@ -2936,6 +2936,784 @@ static void dm_test_mccs_vcp_set_i2c_failure(struct 
kunit *test)
        KUNIT_EXPECT_EQ(test, fake->reads, 0U);
 }

+/* Tests for dm_helpers_construct_old_payload() */
+
+/**
+ * dm_test_construct_old_payload_empty_list - Test PBN/time-slot calc, empty 
list
+ * @test: The KUnit test context
+ *
+ * With no other payloads, next_payload_vc_start stays at mgr->next_start_slot,
+ * so allocated time_slots = next_start_slot - vc_start_slot.
+ */
+static void dm_test_construct_old_payload_empty_list(struct kunit *test)
+{
+       struct drm_dp_mst_topology_mgr *mgr;
+       struct drm_dp_mst_topology_state *mst_state;
+       struct drm_dp_mst_atomic_payload *new_payload;
+       struct drm_dp_mst_atomic_payload *old_payload;
+
+       mgr = kunit_kzalloc(test, sizeof(*mgr), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, mgr);
+       mst_state = kunit_kzalloc(test, sizeof(*mst_state), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, mst_state);
+       new_payload = kunit_kzalloc(test, sizeof(*new_payload), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, new_payload);
+       old_payload = kunit_kzalloc(test, sizeof(*old_payload), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, old_payload);
+
+       INIT_LIST_HEAD(&mst_state->payloads);
+       mgr->next_start_slot = 10;
+       mst_state->pbn_div.full = 5 << 12;      /* dfixed_trunc → 5 PBN/slot */
+       new_payload->vc_start_slot = 3;
+
+       dm_helpers_construct_old_payload(mgr, mst_state, new_payload,
+                                        old_payload);
+
+       /* 10 - 3 = 7 slots, 7 * 5 = 35 PBN */
+       KUNIT_EXPECT_EQ(test, old_payload->time_slots, 7);
+       KUNIT_EXPECT_EQ(test, old_payload->pbn, 35);
+}
+
+/**
+ * dm_test_construct_old_payload_intervening - Test calc with an intervening 
payload
+ * @test: The KUnit test context
+ *
+ * A payload whose vc_start_slot falls between the new payload and the 
manager's
+ * next_start_slot narrows the allocated time-slot window.
+ */
+static void dm_test_construct_old_payload_intervening(struct kunit *test)
+{
+       struct drm_dp_mst_topology_mgr *mgr;
+       struct drm_dp_mst_topology_state *mst_state;
+       struct drm_dp_mst_atomic_payload *new_payload;
+       struct drm_dp_mst_atomic_payload *other_payload;
+       struct drm_dp_mst_atomic_payload *old_payload;
+
+       mgr = kunit_kzalloc(test, sizeof(*mgr), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, mgr);
+       mst_state = kunit_kzalloc(test, sizeof(*mst_state), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, mst_state);
+       new_payload = kunit_kzalloc(test, sizeof(*new_payload), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, new_payload);
+       other_payload = kunit_kzalloc(test, sizeof(*other_payload), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, other_payload);
+       old_payload = kunit_kzalloc(test, sizeof(*old_payload), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, old_payload);
+
+       INIT_LIST_HEAD(&mst_state->payloads);
+       mgr->next_start_slot = 10;
+       mst_state->pbn_div.full = 5 << 12;
+       new_payload->vc_start_slot = 3;
+
+       /* other payload at slot 6 (between 3 and 10) narrows window to 6 */
+       other_payload->vc_start_slot = 6;
+       list_add_tail(&other_payload->next, &mst_state->payloads);
+
+       dm_helpers_construct_old_payload(mgr, mst_state, new_payload,
+                                        old_payload);
+
+       /* 6 - 3 = 3 slots, 3 * 5 = 15 PBN */
+       KUNIT_EXPECT_EQ(test, old_payload->time_slots, 3);
+       KUNIT_EXPECT_EQ(test, old_payload->pbn, 15);
+}
+
+/* Tests for dm_helpers_dp_mst_write_payload_allocation_table() success path */
+
+/**
+ * dm_test_write_payload_alloc_table_success - Exercise the MST success path
+ * @test: The KUnit test context
+ * @enable: true for the add-payload path, false for the remove-payload path
+ *
+ * Builds a minimal MST topology-state fixture so the helper traverses past the
+ * early NULL checks and runs the real DRM MST payload helpers
+ * (drm_dp_add_payload_part1 / drm_dp_remove_payload_part1). With
+ * mgr->mst_primary == NULL the topology walk fails gracefully, so no remote
+ * DPCD/AUX traffic is generated, and the helper still returns true.
+ */
+static void dm_test_write_payload_alloc_table_success(struct kunit *test,
+                                                     bool enable)
+{
+       struct amdgpu_device *adev;
+       struct amdgpu_dm_connector *aconnector;
+       struct drm_dp_mst_topology_mgr *mgr;
+       struct drm_dp_mst_topology_state *mst_state;
+       struct drm_dp_mst_atomic_payload *payload;
+       struct drm_dp_mst_port *port;
+       struct dc_stream_state *stream;
+       struct dc_link *link;
+       struct dc_dp_mst_stream_allocation_table table = { 0 };
+       bool ret;
+
+       adev = dm_kunit_alloc_adev(test);
+       KUNIT_ASSERT_NOT_NULL(test, adev);
+
+       aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, aconnector);
+       mst_state = kunit_kzalloc(test, sizeof(*mst_state), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, mst_state);
+       payload = kunit_kzalloc(test, sizeof(*payload), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, payload);
+       port = kunit_kzalloc(test, sizeof(*port), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, port);
+       stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, stream);
+       link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, link);
+
+       /* aconnector acts as its own MST root for this fixture */
+       aconnector->mst_root = aconnector;
+       aconnector->mst_output_port = port;
+
+       mgr = &aconnector->mst_mgr;
+       mutex_init(&mgr->lock);
+       mgr->dev = &adev->ddev;
+       mgr->mst_primary = NULL;                /* topology walk fails 
gracefully */
+       mgr->base.state = &mst_state->base;
+
+       INIT_LIST_HEAD(&mst_state->payloads);
+       mst_state->pbn_div.full = 5 << 12;      /* dfixed_trunc → 5 PBN/slot */
+
+       /* payload found by drm_atomic_get_mst_payload_state via matching port 
*/
+       payload->port = port;
+       payload->vcpi = 1;
+       payload->vc_start_slot = 1;
+       payload->time_slots = 2;
+       list_add_tail(&payload->next, &mst_state->payloads);
+
+       /* pre-existing HW allocation so the disable path finds a VCPI to clear 
*/
+       link->mst_stream_alloc_table.stream_count = 1;
+       link->mst_stream_alloc_table.stream_allocations[0].vcp_id = 1;
+       link->mst_stream_alloc_table.stream_allocations[0].slot_count = 2;
+
+       stream->dm_stream_context = aconnector;
+       stream->link = link;
+
+       ret = dm_helpers_dp_mst_write_payload_allocation_table(NULL, stream,
+                                                              &table, enable);
+
+       KUNIT_EXPECT_TRUE(test, ret);
+
+       if (enable)
+               /* add path keeps the old entry and appends the new payload */
+               KUNIT_EXPECT_EQ(test, table.stream_count, 2);
+       else
+               /* remove path clears the only entry */
+               KUNIT_EXPECT_EQ(test, table.stream_count, 0);
+}
+
+/**
+ * dm_test_write_payload_alloc_table_enable - Test add-payload success path
+ * @test: The KUnit test context
+ */
+static void dm_test_write_payload_alloc_table_enable(struct kunit *test)
+{
+       dm_test_write_payload_alloc_table_success(test, true);
+}
+
+/**
+ * dm_test_write_payload_alloc_table_disable - Test remove-payload success path
+ * @test: The KUnit test context
+ */
+static void dm_test_write_payload_alloc_table_disable(struct kunit *test)
+{
+       dm_test_write_payload_alloc_table_success(test, false);
+}
+
+/* Tests for dm_helpers_dp_mst_poll_for_allocation_change_trigger() */
+
+/*
+ * Stub AUX transfer that ACKs a DPCD read of the payload-table update status
+ * with the ACT-handled bit set, so drm_dp_check_act_status() returns 0.
+ */
+static ssize_t dm_test_act_aux_transfer_handled(struct drm_dp_aux *aux,
+                                               struct drm_dp_aux_msg *msg)
+{
+       if ((msg->request & ~DP_AUX_I2C_MOT) == DP_AUX_NATIVE_READ) {
+               memset(msg->buffer, 0, msg->size);
+               if (msg->size > 0)
+                       ((u8 *)msg->buffer)[0] = DP_PAYLOAD_ACT_HANDLED;
+       }
+       msg->reply = DP_AUX_NATIVE_REPLY_ACK;
+       return msg->size;
+}
+
+/*
+ * Stub AUX transfer that fails every transaction, so the ACT status read
+ * returns an error and drm_dp_check_act_status() returns non-zero.
+ */
+static ssize_t dm_test_act_aux_transfer_fail(struct drm_dp_aux *aux,
+                                            struct drm_dp_aux_msg *msg)
+{
+       return -EIO;
+}
+
+/**
+ * dm_test_mst_start_top_mgr_set_mst_fail - Test MST start failure path
+ * @test: The KUnit test context
+ *
+ * With a connector-backed link and a failing AUX channel, the non-boot
+ * path calls drm_dp_mst_topology_mgr_set_mst(true), which fails to read the
+ * DPCD caps and returns a negative error, so the helper returns false.
+ */
+static void dm_test_mst_start_top_mgr_set_mst_fail(struct kunit *test)
+{
+       struct amdgpu_device *adev;
+       struct amdgpu_dm_connector *aconnector;
+       struct drm_dp_aux *aux;
+       struct dc_link *link;
+
+       adev = dm_kunit_alloc_adev(test);
+       KUNIT_ASSERT_NOT_NULL(test, adev);
+
+       aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, aconnector);
+       aux = kunit_kzalloc(test, sizeof(*aux), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, aux);
+       link = dm_kunit_alloc_link(test);
+
+       aux->drm_dev = &adev->ddev;
+       aux->transfer = dm_test_act_aux_transfer_fail;
+       drm_dp_aux_init(aux);
+
+       mutex_init(&aconnector->mst_mgr.lock);
+       aconnector->mst_mgr.dev = &adev->ddev;
+       aconnector->mst_mgr.aux = aux;
+       link->priv = aconnector;
+
+       KUNIT_EXPECT_FALSE(test, dm_helpers_dp_mst_start_top_mgr(NULL, link, 
false));
+}
+
+/**
+ * dm_test_mst_stop_top_mgr_active - Test MST stop on an active topology 
manager
+ * @test: The KUnit test context
+ *
+ * With mst_state set, the helper calls drm_dp_mst_topology_mgr_set_mst(false)
+ * to disable MST and clears the link lane count. The helper always returns
+ * false.
+ */
+static void dm_test_mst_stop_top_mgr_active(struct kunit *test)
+{
+       struct amdgpu_device *adev;
+       struct amdgpu_dm_connector *aconnector;
+       struct drm_dp_aux *aux;
+       struct dc_link *link;
+
+       adev = dm_kunit_alloc_adev(test);
+       KUNIT_ASSERT_NOT_NULL(test, adev);
+
+       aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, aconnector);
+       aux = kunit_kzalloc(test, sizeof(*aux), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, aux);
+       link = dm_kunit_alloc_link(test);
+
+       aux->drm_dev = &adev->ddev;
+       aux->transfer = dm_test_act_aux_transfer_handled;
+       drm_dp_aux_init(aux);
+
+       mutex_init(&aconnector->mst_mgr.lock);
+       aconnector->mst_mgr.dev = &adev->ddev;
+       aconnector->mst_mgr.aux = aux;
+       aconnector->mst_mgr.mst_state = true;
+       link->cur_link_settings.lane_count = 4;
+       link->priv = aconnector;
+
+       KUNIT_EXPECT_FALSE(test, dm_helpers_dp_mst_stop_top_mgr(NULL, link));
+       KUNIT_EXPECT_EQ(test, link->cur_link_settings.lane_count, 0);
+}
+
+/**
+ * dm_test_poll_for_act_no_mst_state - Test ACT poll bails when MST not started
+ * @test: The KUnit test context
+ *
+ * With mst_root set but mst_mgr->mst_state false, the helper returns
+ * ACT_FAILED before touching the AUX channel.
+ */
+static void dm_test_poll_for_act_no_mst_state(struct kunit *test)
+{
+       struct amdgpu_dm_connector *aconnector;
+       struct dc_stream_state *stream;
+
+       aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, aconnector);
+       stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, stream);
+
+       aconnector->mst_root = aconnector;
+       aconnector->mst_mgr.mst_state = false;
+       stream->dm_stream_context = aconnector;
+
+       KUNIT_EXPECT_EQ(test,
+                       
(int)dm_helpers_dp_mst_poll_for_allocation_change_trigger(NULL, stream),
+                       (int)ACT_FAILED);
+}
+
+/**
+ * dm_test_poll_for_act_success - Test ACT poll success path
+ * @test: The KUnit test context
+ *
+ * With MST started and the AUX channel reporting ACT handled,
+ * drm_dp_check_act_status() returns 0 and the helper returns ACT_SUCCESS.
+ */
+static void dm_test_poll_for_act_success(struct kunit *test)
+{
+       struct amdgpu_device *adev;
+       struct amdgpu_dm_connector *aconnector;
+       struct drm_dp_aux *aux;
+       struct dc_stream_state *stream;
+
+       adev = dm_kunit_alloc_adev(test);
+       KUNIT_ASSERT_NOT_NULL(test, adev);
+
+       aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, aconnector);
+       aux = kunit_kzalloc(test, sizeof(*aux), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, aux);
+       stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, stream);
+
+       aux->drm_dev = &adev->ddev;
+       aux->transfer = dm_test_act_aux_transfer_handled;
+       drm_dp_aux_init(aux);
+
+       aconnector->mst_root = aconnector;
+       aconnector->mst_mgr.mst_state = true;
+       aconnector->mst_mgr.aux = aux;
+       stream->dm_stream_context = aconnector;
+
+       KUNIT_EXPECT_EQ(test,
+                       
(int)dm_helpers_dp_mst_poll_for_allocation_change_trigger(NULL, stream),
+                       (int)ACT_SUCCESS);
+}
+
+/**
+ * dm_test_poll_for_act_status_failed - Test ACT poll failure path
+ * @test: The KUnit test context
+ *
+ * With MST started but the AUX channel failing, drm_dp_check_act_status()
+ * returns non-zero and the helper returns ACT_FAILED.
+ */
+static void dm_test_poll_for_act_status_failed(struct kunit *test)
+{
+       struct amdgpu_device *adev;
+       struct amdgpu_dm_connector *aconnector;
+       struct drm_dp_aux *aux;
+       struct dc_stream_state *stream;
+
+       adev = dm_kunit_alloc_adev(test);
+       KUNIT_ASSERT_NOT_NULL(test, adev);
+
+       aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, aconnector);
+       aux = kunit_kzalloc(test, sizeof(*aux), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, aux);
+       stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, stream);
+
+       aux->drm_dev = &adev->ddev;
+       aux->transfer = dm_test_act_aux_transfer_fail;
+       drm_dp_aux_init(aux);
+
+       aconnector->mst_root = aconnector;
+       aconnector->mst_mgr.mst_state = true;
+       aconnector->mst_mgr.aux = aux;
+       stream->dm_stream_context = aconnector;
+
+       KUNIT_EXPECT_EQ(test,
+                       
(int)dm_helpers_dp_mst_poll_for_allocation_change_trigger(NULL, stream),
+                       (int)ACT_FAILED);
+}
+
+/* Tests for dm_helpers_dp_mst_send_payload_allocation() */
+
+/**
+ * dm_test_mst_send_payload_alloc_part2_fail - Exercise the failure branch
+ * @test: The KUnit test context
+ *
+ * Builds a minimal MST topology-state fixture so the helper runs past the 
early
+ * NULL checks and calls drm_dp_add_payload_part2(). The payload's allocation
+ * status is left at its default (not DRM_DP_MST_PAYLOAD_ALLOCATION_DFP), so
+ * drm_dp_add_payload_part2() returns -EIO without any remote DPCD/AUX traffic.
+ * The non-zero return drives the failure branch, which clears the
+ * MST_ALLOCATE_NEW_PAYLOAD status bit.
+ */
+static void dm_test_mst_send_payload_alloc_part2_fail(struct kunit *test)
+{
+       struct amdgpu_device *adev;
+       struct amdgpu_dm_connector *aconnector;
+       struct drm_dp_mst_topology_mgr *mgr;
+       struct drm_dp_mst_topology_state *mst_state;
+       struct drm_dp_mst_atomic_payload *payload;
+       struct drm_dp_mst_port *port;
+       struct dc_stream_state *stream;
+
+       adev = dm_kunit_alloc_adev(test);
+       KUNIT_ASSERT_NOT_NULL(test, adev);
+
+       aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, aconnector);
+       mst_state = kunit_kzalloc(test, sizeof(*mst_state), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, mst_state);
+       payload = kunit_kzalloc(test, sizeof(*payload), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, payload);
+       port = kunit_kzalloc(test, sizeof(*port), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, port);
+       stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, stream);
+
+       /* aconnector acts as its own MST root for this fixture */
+       aconnector->mst_root = aconnector;
+       aconnector->mst_output_port = port;
+       /* drm_dp_add_payload_part2() logs port->connector->name on failure */
+       port->connector = &aconnector->base;
+
+       mgr = &aconnector->mst_mgr;
+       mgr->dev = &adev->ddev;
+       mgr->base.state = &mst_state->base;
+
+       INIT_LIST_HEAD(&mst_state->payloads);
+
+       /* payload found by drm_atomic_get_mst_payload_state via matching port 
*/
+       payload->port = port;
+       list_add_tail(&payload->next, &mst_state->payloads);
+
+       /* pre-set the bit so we can observe it being cleared on failure */
+       aconnector->mst_status = MST_ALLOCATE_NEW_PAYLOAD;
+
+       stream->dm_stream_context = aconnector;
+       dm_helpers_dp_mst_send_payload_allocation(NULL, stream);
+
+       KUNIT_EXPECT_EQ(test,
+                       aconnector->mst_status & MST_ALLOCATE_NEW_PAYLOAD, 0);
+}
+
+/* Tests for dm_helpers_dp_mst_update_mst_mgr_for_deallocation() */
+
+/**
+ * dm_test_mst_update_mgr_dealloc_success - Exercise the deallocation path
+ * @test: The KUnit test context
+ *
+ * Builds a minimal MST topology-state fixture so the helper runs past the 
early
+ * NULL checks through dm_helpers_construct_old_payload() and
+ * drm_dp_remove_payload_part2(), both of which are pure list/slot math with no
+ * remote DPCD/AUX traffic. Afterwards MST_CLEAR_ALLOCATED_PAYLOAD must be set,
+ * MST_ALLOCATE_NEW_PAYLOAD cleared, and the payload's slot released.
+ */
+static void dm_test_mst_update_mgr_dealloc_success(struct kunit *test)
+{
+       struct amdgpu_dm_connector *aconnector;
+       struct drm_dp_mst_topology_mgr *mgr;
+       struct drm_dp_mst_topology_state *mst_state;
+       struct drm_dp_mst_atomic_payload *payload;
+       struct drm_dp_mst_port *port;
+       struct dc_stream_state *stream;
+
+       aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, aconnector);
+       mst_state = kunit_kzalloc(test, sizeof(*mst_state), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, mst_state);
+       payload = kunit_kzalloc(test, sizeof(*payload), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, payload);
+       port = kunit_kzalloc(test, sizeof(*port), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, port);
+       stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, stream);
+
+       /* aconnector acts as its own MST root for this fixture */
+       aconnector->mst_root = aconnector;
+       aconnector->mst_output_port = port;
+
+       mgr = &aconnector->mst_mgr;
+       mgr->base.state = &mst_state->base;
+       mgr->next_start_slot = 10;
+       mgr->payload_count = 1;
+
+       INIT_LIST_HEAD(&mst_state->payloads);
+       mst_state->pbn_div.full = 5 << 12;      /* dfixed_trunc → 5 PBN/slot */
+
+       /* payload found by drm_atomic_get_mst_payload_state via matching port 
*/
+       payload->port = port;
+       payload->vc_start_slot = 3;
+       payload->time_slots = 7;
+       list_add_tail(&payload->next, &mst_state->payloads);
+
+       /* pre-set the bit so we can observe it being cleared */
+       aconnector->mst_status = MST_ALLOCATE_NEW_PAYLOAD;
+
+       stream->dm_stream_context = aconnector;
+       dm_helpers_dp_mst_update_mst_mgr_for_deallocation(NULL, stream);
+
+       KUNIT_EXPECT_EQ(test,
+                       aconnector->mst_status & MST_CLEAR_ALLOCATED_PAYLOAD,
+                       (int)MST_CLEAR_ALLOCATED_PAYLOAD);
+       KUNIT_EXPECT_EQ(test,
+                       aconnector->mst_status & MST_ALLOCATE_NEW_PAYLOAD, 0);
+       /* drm_dp_remove_payload_part2() releases the payload's slot */
+       KUNIT_EXPECT_EQ(test, payload->vc_start_slot, -1);
+}
+
+/* Tests for dm_helpers_dp_write_dsc_enable() */
+
+/**
+ * dm_test_dp_write_dsc_enable_mst_no_aux - Test MST early return without 
dsc_aux
+ * @test: The KUnit test context
+ */
+static void dm_test_dp_write_dsc_enable_mst_no_aux(struct kunit *test)
+{
+       struct amdgpu_dm_connector *aconnector;
+       struct dc_stream_state *stream;
+
+       aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, aconnector);
+       stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, stream);
+
+       stream->dm_stream_context = aconnector;
+       stream->signal = SIGNAL_TYPE_DISPLAY_PORT_MST;
+       aconnector->dsc_aux = NULL;
+
+       /* MST signal with NULL dsc_aux → return false */
+       KUNIT_EXPECT_FALSE(test, dm_helpers_dp_write_dsc_enable(NULL, stream, 
true));
+}
+
+/**
+ * dm_test_dp_write_dsc_enable_non_dp - Test non-DP signal returns false
+ * @test: The KUnit test context
+ *
+ * For an HDMI signal neither the MST nor the DP/eDP block runs, so ret stays 
0.
+ */
+static void dm_test_dp_write_dsc_enable_non_dp(struct kunit *test)
+{
+       struct amdgpu_dm_connector *aconnector;
+       struct dc_stream_state *stream;
+
+       aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, aconnector);
+       stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, stream);
+
+       stream->dm_stream_context = aconnector;
+       stream->signal = SIGNAL_TYPE_HDMI_TYPE_A;
+
+       /* Non-DP/MST signal → no DPCD write, ret stays 0 (false) */
+       KUNIT_EXPECT_FALSE(test, dm_helpers_dp_write_dsc_enable(NULL, stream, 
true));
+}
+
+struct dm_test_dsc_aux_pair {
+       struct dm_test_synaptics_aux *main;
+       struct dm_test_synaptics_aux *passthrough;
+};
+
+static struct amdgpu_dm_connector *dm_test_alloc_dsc_connector(struct kunit 
*test,
+                                                      struct dc_link *link)
+{
+       struct amdgpu_dm_connector *aconnector;
+
+       aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, aconnector);
+       aconnector->dc_link = link;
+
+       return aconnector;
+}
+
+static struct dc_stream_state *dm_test_alloc_dsc_stream(struct kunit *test,
+                                                      struct dc_link *link,
+                                                      enum signal_type signal)
+{
+       struct dc_stream_state *stream;
+       struct dc_sink *sink;
+
+       stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, stream);
+       sink = kunit_kzalloc(test, sizeof(*sink), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, sink);
+
+       stream->link = link;
+       stream->signal = signal;
+       stream->sink = sink;
+       sink->link = link;
+
+       return stream;
+}
+
+static struct dm_test_dsc_aux_pair dm_test_dp_write_dsc_enable_mst(struct 
kunit *test,
+                                                          bool enable,
+                                                          bool passthrough)
+{
+       struct dm_test_dsc_aux_pair aux_pair;
+       struct amdgpu_dm_connector *aconnector;
+       struct drm_dp_mst_port *port;
+       struct dc_stream_state *stream;
+       struct dc_link *link;
+       bool ret;
+
+       link = dm_kunit_alloc_link(test);
+       aconnector = dm_test_alloc_dsc_connector(test, link);
+       stream = dm_test_alloc_dsc_stream(test, link, 
SIGNAL_TYPE_DISPLAY_PORT_MST);
+       port = kunit_kzalloc(test, sizeof(*port), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, port);
+       aux_pair.main = dm_test_alloc_synaptics_aux_with_dev(test, NULL);
+       aux_pair.passthrough = dm_test_alloc_synaptics_aux_with_dev(test, NULL);
+
+       stream->dm_stream_context = aconnector;
+       aconnector->dsc_aux = &aux_pair.main->aux;
+       aconnector->mst_output_port = port;
+       if (passthrough)
+               port->passthrough_aux = &aux_pair.passthrough->aux;
+
+       ret = dm_helpers_dp_write_dsc_enable(NULL, stream, enable);
+
+       KUNIT_EXPECT_TRUE(test, ret);
+       KUNIT_EXPECT_EQ(test, aux_pair.main->dsc_enable_writes, 1U);
+       KUNIT_EXPECT_EQ(test, aux_pair.main->dsc_enable_values[0], enable ? 1 : 
0);
+       KUNIT_EXPECT_EQ(test, aux_pair.passthrough->dsc_enable_writes,
+                        passthrough ? 1U : 0U);
+       if (passthrough)
+               KUNIT_EXPECT_EQ(test, 
aux_pair.passthrough->dsc_enable_values[0], enable ? 2 : 0);
+
+       return aux_pair;
+}
+
+/**
+ * dm_test_dp_write_dsc_enable_mst_enable_decode_only - Test MST enable 
decoding write
+ * @test: The KUnit test context
+ */
+static void dm_test_dp_write_dsc_enable_mst_enable_decode_only(struct kunit 
*test)
+{
+       dm_test_dp_write_dsc_enable_mst(test, true, false);
+}
+
+/**
+ * dm_test_dp_write_dsc_enable_mst_enable_passthrough - Test MST enable 
passthrough write
+ * @test: The KUnit test context
+ */
+static void dm_test_dp_write_dsc_enable_mst_enable_passthrough(struct kunit 
*test)
+{
+       dm_test_dp_write_dsc_enable_mst(test, true, true);
+}
+
+/**
+ * dm_test_dp_write_dsc_enable_mst_disable_decode_only - Test MST disable 
decoding write
+ * @test: The KUnit test context
+ */
+static void dm_test_dp_write_dsc_enable_mst_disable_decode_only(struct kunit 
*test)
+{
+       dm_test_dp_write_dsc_enable_mst(test, false, false);
+}
+
+/**
+ * dm_test_dp_write_dsc_enable_mst_disable_passthrough - Test MST disable 
passthrough write
+ * @test: The KUnit test context
+ */
+static void dm_test_dp_write_dsc_enable_mst_disable_passthrough(struct kunit 
*test)
+{
+       dm_test_dp_write_dsc_enable_mst(test, false, true);
+}
+
+static void dm_test_dp_write_dsc_enable_sst(struct kunit *test,
+                                          enum display_dongle_type dongle_type,
+                                          bool enable,
+                                          u8 expected_value)
+{
+       struct dm_test_synaptics_aux *fixture;
+       struct amdgpu_dm_connector *aconnector;
+       struct dc_stream_state *stream;
+       struct dc_link *link;
+       bool ret;
+
+       link = dm_kunit_alloc_link(test);
+       aconnector = dm_test_alloc_dsc_connector(test, link);
+       stream = dm_test_alloc_dsc_stream(test, link, SIGNAL_TYPE_DISPLAY_PORT);
+       fixture = dm_test_alloc_synaptics_aux_with_dev(test, NULL);
+
+       stream->dm_stream_context = aconnector;
+       link->priv = aconnector;
+       link->dpcd_caps.dongle_type = dongle_type;
+       dm_test_current_aux_recorder = fixture;
+       aconnector->dm_dp_aux.aux.transfer = dm_test_current_aux_transfer;
+       drm_dp_aux_init(&aconnector->dm_dp_aux.aux);
+
+       ret = dm_helpers_dp_write_dsc_enable(NULL, stream, enable);
+
+       KUNIT_EXPECT_TRUE(test, ret);
+       KUNIT_EXPECT_EQ(test, fixture->dsc_enable_writes, 1U);
+       KUNIT_EXPECT_EQ(test, fixture->dsc_enable_values[0], expected_value);
+}
+
+/**
+ * dm_test_dp_write_dsc_enable_sst_rx_enable - Test SST RX enable DPCD write
+ * @test: The KUnit test context
+ */
+static void dm_test_dp_write_dsc_enable_sst_rx_enable(struct kunit *test)
+{
+       dm_test_dp_write_dsc_enable_sst(test, DISPLAY_DONGLE_NONE, true, 1);
+}
+
+/**
+ * dm_test_dp_write_dsc_enable_sst_rx_disable - Test SST RX disable DPCD write
+ * @test: The KUnit test context
+ */
+static void dm_test_dp_write_dsc_enable_sst_rx_disable(struct kunit *test)
+{
+       dm_test_dp_write_dsc_enable_sst(test, DISPLAY_DONGLE_NONE, false, 0);
+}
+
+/**
+ * dm_test_dp_write_dsc_enable_pcon_enable - Test DP-HDMI PCON enable DPCD 
write
+ * @test: The KUnit test context
+ */
+static void dm_test_dp_write_dsc_enable_pcon_enable(struct kunit *test)
+{
+       dm_test_dp_write_dsc_enable_sst(test, DISPLAY_DONGLE_DP_HDMI_CONVERTER, 
true, 1);
+}
+
+/**
+ * dm_test_dp_write_dsc_enable_pcon_disable - Test DP-HDMI PCON disable DPCD 
write
+ * @test: The KUnit test context
+ */
+static void dm_test_dp_write_dsc_enable_pcon_disable(struct kunit *test)
+{
+       dm_test_dp_write_dsc_enable_sst(test, DISPLAY_DONGLE_DP_HDMI_CONVERTER, 
false, 0);
+}
+
+/* Tests for dm_helpers_dp_handle_test_pattern_request() */
+
+/**
+ * dm_test_dp_handle_test_pattern_no_pipe - Test no matching pipe returns false
+ * @test: The KUnit test context
+ */
+static void dm_test_dp_handle_test_pattern_no_pipe(struct kunit *test)
+{
+       union link_test_pattern test_pattern = {0};
+       union test_misc test_params = {0};
+       struct amdgpu_dm_connector *aconnector;
+       struct amdgpu_device *adev;
+       struct dc_context *ctx;
+       struct dc_state *state;
+       struct dc_link *link;
+       struct dc *dc;
+
+       adev = dm_kunit_alloc_adev(test);
+       ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, ctx);
+       dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, dc);
+       state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, state);
+       link = dm_kunit_alloc_link(test);
+       aconnector = dm_kunit_alloc_connector(test, adev, link);
+
+       ctx->dc = dc;
+       dc->current_state = state;
+       link->dc = dc;
+       link->priv = aconnector;
+
+       KUNIT_EXPECT_FALSE(test,
+                       dm_helpers_dp_handle_test_pattern_request(ctx, link,
+                                                              test_pattern,
+                                                              test_params));
+}
+
 static struct kunit_case amdgpu_dm_helpers_test_cases[] = {
        /* edid_extract_panel_id */
        KUNIT_CASE(dm_test_edid_extract_panel_id_basic),
@@ -3020,6 +3798,8 @@ static struct kunit_case amdgpu_dm_helpers_test_cases[] = 
{
        KUNIT_CASE(dm_test_mst_start_top_mgr_null_priv),
        KUNIT_CASE(dm_test_mst_stop_top_mgr_null_priv),
        KUNIT_CASE(dm_test_mst_start_top_mgr_boot),
+       KUNIT_CASE(dm_test_mst_start_top_mgr_set_mst_fail),
+       KUNIT_CASE(dm_test_mst_stop_top_mgr_active),
        /* dm_helpers_dp_write_hblank_reduction */
        KUNIT_CASE(dm_test_dp_write_hblank_reduction_false),
        /* get_dsc_max_slices */
@@ -3056,12 +3836,22 @@ static struct kunit_case amdgpu_dm_helpers_test_cases[] 
= {
        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),
+       /* dm_helpers_dp_mst_write_payload_allocation_table success path */
+       KUNIT_CASE(dm_test_write_payload_alloc_table_enable),
+       KUNIT_CASE(dm_test_write_payload_alloc_table_disable),
        /* 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_poll_for_act_no_mst_state),
+       KUNIT_CASE(dm_test_poll_for_act_success),
+       KUNIT_CASE(dm_test_poll_for_act_status_failed),
        KUNIT_CASE(dm_test_mst_send_payload_alloc_null_ctx),
        KUNIT_CASE(dm_test_mst_update_mgr_dealloc_null_ctx),
+       /* dm_helpers_dp_mst_send_payload_allocation failure path */
+       KUNIT_CASE(dm_test_mst_send_payload_alloc_part2_fail),
+       /* dm_helpers_dp_mst_update_mst_mgr_for_deallocation success path */
+       KUNIT_CASE(dm_test_mst_update_mgr_dealloc_success),
        /* dm_helpers_is_dp_sink_present */
        KUNIT_CASE(dm_test_is_dp_sink_present_null_priv),
        /* dm_helpers_dmub_outbox_interrupt_control */
@@ -3087,6 +3877,22 @@ static struct kunit_case amdgpu_dm_helpers_test_cases[] 
= {
        KUNIT_CASE(dm_test_mccs_vcp_set_null_link),
        KUNIT_CASE(dm_test_mccs_vcp_set_i2c_packet),
        KUNIT_CASE(dm_test_mccs_vcp_set_i2c_failure),
+       /* dm_helpers_construct_old_payload */
+       KUNIT_CASE(dm_test_construct_old_payload_empty_list),
+       KUNIT_CASE(dm_test_construct_old_payload_intervening),
+       /* dm_helpers_dp_write_dsc_enable */
+       KUNIT_CASE(dm_test_dp_write_dsc_enable_mst_no_aux),
+       KUNIT_CASE(dm_test_dp_write_dsc_enable_non_dp),
+       KUNIT_CASE(dm_test_dp_write_dsc_enable_mst_enable_decode_only),
+       KUNIT_CASE(dm_test_dp_write_dsc_enable_mst_enable_passthrough),
+       KUNIT_CASE(dm_test_dp_write_dsc_enable_mst_disable_decode_only),
+       KUNIT_CASE(dm_test_dp_write_dsc_enable_mst_disable_passthrough),
+       KUNIT_CASE(dm_test_dp_write_dsc_enable_sst_rx_enable),
+       KUNIT_CASE(dm_test_dp_write_dsc_enable_sst_rx_disable),
+       KUNIT_CASE(dm_test_dp_write_dsc_enable_pcon_enable),
+       KUNIT_CASE(dm_test_dp_write_dsc_enable_pcon_disable),
+       /* dm_helpers_dp_handle_test_pattern_request */
+       KUNIT_CASE(dm_test_dp_handle_test_pattern_no_pipe),
        {}
 };

--
2.55.0

Reply via email to