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
lmpx.com only provides a reader for public news (NNTP) servers. It is not
affiliated with the servers or forums shown here and is not responsible for
the content of articles, which is written by their respective authors.