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),
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 */
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 */
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),
{}
};