From: Bhawanpreet Lakha <[email protected]>

Add KUnit tests for the multi-stream transport validation path in
dm_encoder_helper_atomic_check(). A shared scaffold builds a fully faked
DRM atomic state (private objs, connectors, MST payload list) plus a fake
DC link so the PBN divider is non-zero, exercising the topology-state
lookup, color-depth/PBN compute, VCPI slot allocation, and both the
topology-error and negative-vcpi_slots failure branches.

Assisted-by: Copilot:Claude-Opus-4.8
Reviewed-by: Alex Hung <[email protected]>
Signed-off-by: Bhawanpreet Lakha <[email protected]>
Signed-off-by: Ivan Lipski <[email protected]>
---
 .../tests/amdgpu_dm_connector_test.c          | 252 ++++++++++++++++++
 1 file changed, 252 insertions(+)

diff --git 
a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c
index a54fd9529dc9e..d209e26b684eb 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c
@@ -29,6 +29,7 @@
 #include "amdgpu_dm_backlight.h"
 #include "include/grph_object_id.h"
 #include "amdgpu_dm_kunit_test_helpers.h"
+#include "inc/link_service.h"
 
 /* Tests for get_subconnector_type() */
 
@@ -4394,6 +4395,252 @@ static void 
dm_test_atomic_check_non_mst_returns_zero(struct kunit *test)
                                               &ctx->dm_state->base), 0);
 }
 
+/**
+ * dm_test_atomic_check_mst_no_change_returns_zero - Test MST no-change 
short-circuit
+ * @test: The KUnit test context
+ *
+ * An MST connector (mst_output_port set) whose CRTC state reports neither a
+ * connectors nor a mode change hits the ``return 0`` before any topology state
+ * is fetched.
+ */
+static void dm_test_atomic_check_mst_no_change_returns_zero(struct kunit *test)
+{
+       struct dm_test_atomic_check_ctx *ctx =
+               dm_test_atomic_check_ctx_alloc(test, 
DRM_MODE_CONNECTOR_DisplayPort);
+       struct drm_dp_mst_port *mst_port;
+       struct amdgpu_dm_connector *mst_root;
+
+       mst_port = kunit_kzalloc(test, sizeof(*mst_port), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, mst_port);
+       mst_root = kunit_kzalloc(test, sizeof(*mst_root), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, mst_root);
+
+       ctx->aconnector->mst_output_port = mst_port;
+       ctx->aconnector->mst_root = mst_root;
+       ctx->crtc_state->connectors_changed = false;
+       ctx->crtc_state->mode_changed = false;
+
+       KUNIT_EXPECT_EQ(test,
+               dm_encoder_helper_atomic_check(&ctx->aenc->base,
+                                              ctx->crtc_state,
+                                              &ctx->dm_state->base), 0);
+}
+
+
+static uint32_t dm_test_atomic_check_dp_link_bandwidth_kbps(
+       const struct dc_link *link,
+       const struct dc_link_settings *link_settings)
+{
+       return 4320000;
+}
+
+static const struct dc_link_settings 
*dm_test_atomic_check_dp_get_verified_link_cap(
+       const struct dc_link *link)
+{
+       return &link->verified_link_cap;
+}
+
+/* Fake fully-wired MST atomic state for the deep 
dm_encoder_helper_atomic_check() path. */
+struct dm_test_mst_scaffold {
+       struct drm_atomic_commit *state;
+       struct drm_dp_mst_topology_state *mst_state;
+       struct drm_dp_mst_atomic_payload *payload;
+       struct drm_dp_mst_port *mst_port;
+       struct amdgpu_dm_connector *mst_root;
+       struct __drm_private_objs_state *priv_objs;
+};
+
+/*
+ * dm_test_atomic_check_mst_scaffold - build a fake MST atomic state.
+ * @map_success: when true the topology manager maps to a valid topology state
+ *               and a fake DC link/link-service is wired so the PBN divider is
+ *               non-zero; when false the manager maps to an error pointer so
+ *               drm_atomic_get_mst_topology_state() reports failure.
+ */
+static void dm_test_atomic_check_mst_scaffold(struct kunit *test,
+               struct dm_test_atomic_check_ctx *ctx,
+               struct dm_test_mst_scaffold *s,
+               bool map_success)
+{
+       struct drm_modeset_acquire_ctx *acquire_ctx;
+       struct link_service *link_srv;
+       struct dc *dc;
+       struct dc_link *link;
+       struct drm_connector *port_conn;
+       struct drm_crtc *crtc;
+       struct drm_connector_state *port_conn_state;
+       struct __drm_connnectors_state *conns;
+
+       memset(s, 0, sizeof(*s));
+
+       s->mst_root = kunit_kzalloc(test, sizeof(*s->mst_root), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, s->mst_root);
+       s->mst_root->mst_mgr.dev = ctx->drm;
+
+       s->mst_port = kunit_kzalloc(test, sizeof(*s->mst_port), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, s->mst_port);
+
+       ctx->aconnector->mst_root = s->mst_root;
+       ctx->aconnector->mst_output_port = s->mst_port;
+
+       acquire_ctx = kunit_kzalloc(test, sizeof(*acquire_ctx), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, acquire_ctx);
+
+       s->state = kunit_kzalloc(test, sizeof(*s->state), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, s->state);
+       s->state->dev = ctx->drm;
+       s->state->acquire_ctx = acquire_ctx;
+
+       s->priv_objs = kunit_kzalloc(test, sizeof(*s->priv_objs), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, s->priv_objs);
+       s->priv_objs[0].ptr = &s->mst_root->mst_mgr.base;
+       s->state->num_private_objs = 1;
+       s->state->private_objs = s->priv_objs;
+
+       ctx->crtc_state->state = s->state;
+       ctx->crtc_state->connectors_changed = true;
+
+       if (!map_success) {
+               s->priv_objs[0].new_state = ERR_PTR(-ENOMEM);
+               return;
+       }
+
+       /* Fake DC link so dm_mst_get_pbn_divider() yields a non-zero divider. 
*/
+       link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL);
+       dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
+       link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, link_srv);
+       KUNIT_ASSERT_NOT_NULL(test, dc);
+       KUNIT_ASSERT_NOT_NULL(test, link);
+       link_srv->dp_get_verified_link_cap =
+               dm_test_atomic_check_dp_get_verified_link_cap;
+       link_srv->dp_link_bandwidth_kbps =
+               dm_test_atomic_check_dp_link_bandwidth_kbps;
+       dc->link_srv = link_srv;
+       link->dc = dc;
+       s->mst_root->dc_link = link;
+
+       s->mst_state = kunit_kzalloc(test, sizeof(*s->mst_state), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, s->mst_state);
+       INIT_LIST_HEAD(&s->mst_state->payloads);
+       s->priv_objs[0].new_state = &s->mst_state->base;
+
+       /* Pre-seed a payload so find_time_slots() skips the port kref alloc 
path. */
+       s->payload = kunit_kzalloc(test, sizeof(*s->payload), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, s->payload);
+       s->payload->port = s->mst_port;
+       list_add(&s->payload->next, &s->mst_state->payloads);
+
+       /* Wire a connector+crtc so find_time_slots() can resolve the crtc 
mask. */
+       port_conn = kunit_kzalloc(test, sizeof(*port_conn), GFP_KERNEL);
+       crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL);
+       port_conn_state = kunit_kzalloc(test, sizeof(*port_conn_state), 
GFP_KERNEL);
+       conns = kunit_kzalloc(test, sizeof(*conns), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, port_conn);
+       KUNIT_ASSERT_NOT_NULL(test, crtc);
+       KUNIT_ASSERT_NOT_NULL(test, port_conn_state);
+       KUNIT_ASSERT_NOT_NULL(test, conns);
+       port_conn->index = 0;
+       crtc->index = 0;
+       port_conn_state->crtc = crtc;
+       s->mst_port->connector = port_conn;
+       conns[0].new_state = port_conn_state;
+       s->state->num_connector = 1;
+       s->state->connectors = conns;
+}
+
+/**
+ * dm_test_atomic_check_mst_finds_vcpi_slots - Test the MST time-slot 
allocation path
+ * @test: The KUnit test context
+ *
+ * With a fully wired MST atomic state and connectors_changed set, the check
+ * computes the color depth/PBN, allocates VCPI time slots and returns 0.
+ */
+static void dm_test_atomic_check_mst_finds_vcpi_slots(struct kunit *test)
+{
+       struct dm_test_atomic_check_ctx *ctx =
+               dm_test_atomic_check_ctx_alloc(test, 
DRM_MODE_CONNECTOR_DisplayPort);
+       struct dm_test_mst_scaffold s;
+
+       dm_test_atomic_check_mst_scaffold(test, ctx, &s, true);
+       s.state->duplicated = false;
+       ctx->dm_state->base.max_requested_bpc = 8;
+
+       KUNIT_EXPECT_EQ(test,
+               dm_encoder_helper_atomic_check(&ctx->aenc->base,
+                                              ctx->crtc_state,
+                                              &ctx->dm_state->base), 0);
+       KUNIT_EXPECT_GE(test, ctx->dm_state->vcpi_slots, 0);
+}
+
+/**
+ * dm_test_atomic_check_mst_duplicated_skips_pbn - Test the duplicated-state 
fast path
+ * @test: The KUnit test context
+ *
+ * When the atomic state is duplicated the color depth/PBN recompute block is
+ * skipped, but time slots are still allocated and the check returns 0.
+ */
+static void dm_test_atomic_check_mst_duplicated_skips_pbn(struct kunit *test)
+{
+       struct dm_test_atomic_check_ctx *ctx =
+               dm_test_atomic_check_ctx_alloc(test, 
DRM_MODE_CONNECTOR_DisplayPort);
+       struct dm_test_mst_scaffold s;
+
+       dm_test_atomic_check_mst_scaffold(test, ctx, &s, true);
+       s.state->duplicated = true;
+       ctx->dm_state->pbn = 0;
+
+       KUNIT_EXPECT_EQ(test,
+               dm_encoder_helper_atomic_check(&ctx->aenc->base,
+                                              ctx->crtc_state,
+                                              &ctx->dm_state->base), 0);
+}
+
+/**
+ * dm_test_atomic_check_mst_topology_err_propagates - Test topology-state 
error propagation
+ * @test: The KUnit test context
+ *
+ * When drm_atomic_get_mst_topology_state() returns an error pointer, the check
+ * propagates the error code back to the caller.
+ */
+static void dm_test_atomic_check_mst_topology_err_propagates(struct kunit 
*test)
+{
+       struct dm_test_atomic_check_ctx *ctx =
+               dm_test_atomic_check_ctx_alloc(test, 
DRM_MODE_CONNECTOR_DisplayPort);
+       struct dm_test_mst_scaffold s;
+
+       dm_test_atomic_check_mst_scaffold(test, ctx, &s, false);
+
+       KUNIT_EXPECT_EQ(test,
+               dm_encoder_helper_atomic_check(&ctx->aenc->base,
+                                              ctx->crtc_state,
+                                              &ctx->dm_state->base), -ENOMEM);
+}
+
+/**
+ * dm_test_atomic_check_mst_vcpi_error_propagates - Test VCPI allocation error 
propagation
+ * @test: The KUnit test context
+ *
+ * A pre-existing payload marked for deletion makes 
drm_dp_atomic_find_time_slots()
+ * fail; the negative vcpi_slots result is propagated back to the caller.
+ */
+static void dm_test_atomic_check_mst_vcpi_error_propagates(struct kunit *test)
+{
+       struct dm_test_atomic_check_ctx *ctx =
+               dm_test_atomic_check_ctx_alloc(test, 
DRM_MODE_CONNECTOR_DisplayPort);
+       struct dm_test_mst_scaffold s;
+
+       dm_test_atomic_check_mst_scaffold(test, ctx, &s, true);
+       s.state->duplicated = true;
+       ctx->dm_state->pbn = 100;
+       s.payload->delete = true;
+
+       KUNIT_EXPECT_EQ(test,
+               dm_encoder_helper_atomic_check(&ctx->aenc->base,
+                                              ctx->crtc_state,
+                                              &ctx->dm_state->base), -EINVAL);
+}
+
 /* Tests for hdmi_cec_unset_edid() */
 
 /**
@@ -5611,6 +5858,11 @@ static struct kunit_case amdgpu_dm_connector_tests[] = {
        KUNIT_CASE(dm_test_atomic_check_edp_native_keeps_scaling),
        KUNIT_CASE(dm_test_atomic_check_lvds_non_native_enables_scaling),
        KUNIT_CASE(dm_test_atomic_check_non_mst_returns_zero),
+       KUNIT_CASE(dm_test_atomic_check_mst_no_change_returns_zero),
+       KUNIT_CASE(dm_test_atomic_check_mst_finds_vcpi_slots),
+       KUNIT_CASE(dm_test_atomic_check_mst_duplicated_skips_pbn),
+       KUNIT_CASE(dm_test_atomic_check_mst_topology_err_propagates),
+       KUNIT_CASE(dm_test_atomic_check_mst_vcpi_error_propagates),
        /* hdmi_cec_unset_edid */
        KUNIT_CASE(dm_test_hdmi_cec_unset_edid_no_notifier),
        /* create_eml_sink */
-- 
2.43.0

Reply via email to