From: Alex Hung <[email protected]>

[WHAT]
Add KUnit tests for log_dsc_params(), for
find_crtc_index_in_state_by_stream() with a matching and a missing
stream, and for is_dsc_precompute_needed() covering an empty state, a
stream without a link, a non MST link, an MST link whose CRTC is not
in the state and an MST link that needs the precompute pass.

[HOW]
Hand build a drm_atomic_commit with CRTC states and DC stream states so
the stream to CRTC lookup can be driven directly, and reuse it for a
table driven is_dsc_precompute_needed() test that varies the link
signal type and whether the CRTC is present.

Assisted-by: Copilot:Claude-Opus-5
Reviewed-by: Bhawanpreet Lakha <[email protected]>
Signed-off-by: Alex Hung <[email protected]>
Signed-off-by: Chenyu Chen <[email protected]>
---
 .../display/amdgpu_dm/amdgpu_dm_mst_types.c   |  11 +-
 .../display/amdgpu_dm/amdgpu_dm_mst_types.h   |   4 +
 .../tests/amdgpu_dm_mst_types_test.c          | 174 ++++++++++++++++++
 3 files changed, 185 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
index 4c6e7c28e4f8..f5085a0036af 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
@@ -1303,7 +1303,7 @@ static void build_frl_mst_dsc_params(struct 
amdgpu_dm_connector *aconnector,
        }
 }
 
-static void log_dsc_params(int count, struct dsc_mst_fairness_vars *vars, int 
k)
+STATIC_IFN_KUNIT void log_dsc_params(int count, struct dsc_mst_fairness_vars 
*vars, int k)
 {
        int i;
 
@@ -1311,6 +1311,7 @@ static void log_dsc_params(int count, struct 
dsc_mst_fairness_vars *vars, int k)
                DRM_DEBUG_DRIVER("MST_DSC DSC params: stream #%d --- 
dsc_enabled = %d, bpp_x16 = %d, pbn = %d\n",
                                 i, vars[i + k].dsc_enabled, vars[i + 
k].bpp_x16, vars[i + k].pbn);
 }
+EXPORT_IF_KUNIT(log_dsc_params);
 
 static int compute_mst_dsc_configs_for_link(struct drm_atomic_commit *state,
                                            struct dc_state *dc_state,
@@ -1739,8 +1740,8 @@ static int pre_compute_mst_dsc_configs_for_state(struct 
drm_atomic_commit *state
        return ret;
 }
 
-static int find_crtc_index_in_state_by_stream(struct drm_atomic_commit *state,
-                                             struct dc_stream_state *stream)
+STATIC_IFN_KUNIT int find_crtc_index_in_state_by_stream(struct 
drm_atomic_commit *state,
+                                                       struct dc_stream_state 
*stream)
 {
        int i;
        struct drm_crtc *crtc;
@@ -1754,6 +1755,7 @@ static int find_crtc_index_in_state_by_stream(struct 
drm_atomic_commit *state,
        }
        return -1;
 }
+EXPORT_IF_KUNIT(find_crtc_index_in_state_by_stream);
 
 static bool is_link_to_dschub(struct dc_link *dc_link)
 {
@@ -1770,7 +1772,7 @@ static bool is_link_to_dschub(struct dc_link *dc_link)
        return true;
 }
 
-static bool is_dsc_precompute_needed(struct drm_atomic_commit *state)
+STATIC_IFN_KUNIT bool is_dsc_precompute_needed(struct drm_atomic_commit *state)
 {
        int i;
        struct drm_crtc *crtc;
@@ -1790,6 +1792,7 @@ static bool is_dsc_precompute_needed(struct 
drm_atomic_commit *state)
        }
        return ret;
 }
+EXPORT_IF_KUNIT(is_dsc_precompute_needed);
 
 int pre_validate_dsc(struct drm_atomic_commit *state,
                     struct dm_atomic_state **dm_state_ptr,
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h
index e1a25afb570c..b1d7a21a37e3 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h
@@ -109,6 +109,10 @@ bool is_synaptics_cascaded_panamera(struct dc_link *link, 
struct drm_dp_mst_port
 bool dp_get_link_current_set_bw(struct drm_dp_aux *aux, uint32_t *cur_link_bw);
 bool get_conv_frl_bw(struct amdgpu_dm_connector *aconnector,
                     uint32_t *bw_in_kbps, uint32_t *dsc_bw_in_kbps);
+void log_dsc_params(int count, struct dsc_mst_fairness_vars *vars, int k);
+int find_crtc_index_in_state_by_stream(struct drm_atomic_commit *state,
+                                      struct dc_stream_state *stream);
+bool is_dsc_precompute_needed(struct drm_atomic_commit *state);
 bool validate_dsc_caps_on_connector(struct amdgpu_dm_connector *aconnector);
 bool retrieve_downstream_port_device(struct amdgpu_dm_connector *aconnector);
 bool retrieve_branch_specific_data(struct amdgpu_dm_connector *aconnector);
diff --git 
a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c
index 0e5dd4ca9aed..d91863d5416d 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c
@@ -3109,6 +3109,173 @@ static void dm_mst_test_conv_frl_bw_bottleneck(struct 
kunit *test)
        KUNIT_EXPECT_EQ(test, dsc_bw_in_kbps, 6000U);
 }
 
+/* Tests for log_dsc_params */
+
+/**
+ * dm_mst_test_log_dsc_params - logging the fairness vars leaves them untouched
+ * @test: KUnit test context
+ *
+ * log_dsc_params() only traces, so the only observable contract is that it
+ * walks @count entries starting at @k without modifying them.
+ */
+static void dm_mst_test_log_dsc_params(struct kunit *test)
+{
+       struct dsc_mst_fairness_vars vars[3] = {
+               { .pbn = 100, .dsc_enabled = false, .bpp_x16 = 160 },
+               { .pbn = 200, .dsc_enabled = true, .bpp_x16 = 192 },
+               { .pbn = 300, .dsc_enabled = true, .bpp_x16 = 256 },
+       };
+
+       log_dsc_params(2, vars, 1);
+
+       KUNIT_EXPECT_EQ(test, vars[1].pbn, 200);
+       KUNIT_EXPECT_EQ(test, vars[2].bpp_x16, 256);
+}
+
+/* Tests for find_crtc_index_in_state_by_stream and is_dsc_precompute_needed */
+
+struct dm_mst_test_crtc_state_ctx {
+       struct drm_atomic_commit *state;
+       struct drm_crtc *crtcs;
+       struct dm_crtc_state *crtc_states;
+       struct drm_connector *connector;
+       struct drm_connector_state *conn_state;
+};
+
+/*
+ * Hand-build an atomic state with @num_crtc CRTCs, each carrying a DM CRTC
+ * state. drm_atomic_state_alloc() would need a fully registered mode config,
+ * and the helpers under test only walk the crtcs/connectors arrays.
+ */
+static void dm_mst_test_init_crtc_state_ctx(struct kunit *test,
+                                           struct dm_mst_test_crtc_state_ctx 
*ctx,
+                                           unsigned int num_crtc)
+{
+       struct amdgpu_device *adev;
+       unsigned int i;
+
+       adev = dm_kunit_alloc_adev(test);
+       adev->ddev.mode_config.num_crtc = num_crtc;
+
+       ctx->state = kunit_kzalloc(test, sizeof(*ctx->state), GFP_KERNEL);
+       ctx->crtcs = kunit_kcalloc(test, num_crtc, sizeof(*ctx->crtcs), 
GFP_KERNEL);
+       ctx->crtc_states = kunit_kcalloc(test, num_crtc, 
sizeof(*ctx->crtc_states), GFP_KERNEL);
+       ctx->connector = kunit_kzalloc(test, sizeof(*ctx->connector), 
GFP_KERNEL);
+       ctx->conn_state = kunit_kzalloc(test, sizeof(*ctx->conn_state), 
GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, ctx->state);
+       KUNIT_ASSERT_NOT_NULL(test, ctx->crtcs);
+       KUNIT_ASSERT_NOT_NULL(test, ctx->crtc_states);
+       KUNIT_ASSERT_NOT_NULL(test, ctx->connector);
+       KUNIT_ASSERT_NOT_NULL(test, ctx->conn_state);
+
+       ctx->state->dev = &adev->ddev;
+       ctx->state->crtcs = kunit_kcalloc(test, num_crtc, 
sizeof(*ctx->state->crtcs), GFP_KERNEL);
+       ctx->state->connectors = kunit_kzalloc(test, 
sizeof(*ctx->state->connectors), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, ctx->state->crtcs);
+       KUNIT_ASSERT_NOT_NULL(test, ctx->state->connectors);
+
+       for (i = 0; i < num_crtc; i++) {
+               ctx->crtcs[i].dev = &adev->ddev;
+               ctx->crtc_states[i].base.crtc = &ctx->crtcs[i];
+               ctx->state->crtcs[i].ptr = &ctx->crtcs[i];
+               ctx->state->crtcs[i].new_state = &ctx->crtc_states[i].base;
+               ctx->state->crtcs[i].old_state = &ctx->crtc_states[i].base;
+       }
+}
+
+/* Attach the context's connector to @crtc so the CRTC looks driven. */
+static void dm_mst_test_attach_connector(struct dm_mst_test_crtc_state_ctx 
*ctx,
+                                        struct drm_crtc *crtc)
+{
+       ctx->conn_state->crtc = crtc;
+       ctx->state->connectors[0].ptr = ctx->connector;
+       ctx->state->connectors[0].new_state = ctx->conn_state;
+       ctx->state->num_connector = 1;
+}
+
+/**
+ * dm_mst_test_find_crtc_index_matches - the CRTC driving a stream is found
+ * @test: KUnit test context
+ */
+static void dm_mst_test_find_crtc_index_matches(struct kunit *test)
+{
+       struct dm_mst_test_crtc_state_ctx ctx;
+       struct dc_stream_state *stream;
+
+       dm_mst_test_init_crtc_state_ctx(test, &ctx, 3);
+       stream = dm_kunit_alloc_stream(test, NULL);
+       ctx.crtc_states[2].stream = stream;
+
+       KUNIT_EXPECT_EQ(test, find_crtc_index_in_state_by_stream(ctx.state, 
stream), 2);
+}
+
+/**
+ * dm_mst_test_find_crtc_index_no_match - an unknown stream yields -1
+ * @test: KUnit test context
+ */
+static void dm_mst_test_find_crtc_index_no_match(struct kunit *test)
+{
+       struct dm_mst_test_crtc_state_ctx ctx;
+       struct dc_stream_state *stream;
+
+       dm_mst_test_init_crtc_state_ctx(test, &ctx, 2);
+       stream = dm_kunit_alloc_stream(test, NULL);
+
+       KUNIT_EXPECT_EQ(test, find_crtc_index_in_state_by_stream(ctx.state, 
stream), -1);
+}
+
+struct dm_mst_precompute_param {
+       const char *name;
+       bool connector_attached;
+       bool has_stream;
+       enum dc_connection_type link_type;
+       bool dsc_support;
+       bool dsc_passthrough;
+       bool needed;
+};
+
+static const struct dm_mst_precompute_param dm_mst_precompute_params[] = {
+       { "no_connector", false, true, dc_connection_mst_branch, true, false, 
false },
+       { "dsc_hub", true, true, dc_connection_mst_branch, true, false, true },
+       { "dsc_passthrough_hub", true, true, dc_connection_mst_branch, false, 
true, true },
+       { "sst_link", true, true, dc_connection_single, true, false, false },
+       { "mst_without_dsc", true, true, dc_connection_mst_branch, false, 
false, false },
+       { "no_stream", true, false, dc_connection_mst_branch, true, false, 
false },
+};
+
+KUNIT_ARRAY_PARAM_DESC(dm_mst_precompute, dm_mst_precompute_params, name);
+
+/**
+ * dm_mst_test_dsc_precompute_needed - precompute needs a driven DSC MST hub
+ * @test: KUnit test context
+ *
+ * A DSC or DSC passthrough capable MST branch that a connector in the state
+ * drives needs its bandwidth precomputed. A CRTC no connector drives aborts
+ * the scan, and SST links or MST branches without DSC never need it.
+ */
+static void dm_mst_test_dsc_precompute_needed(struct kunit *test)
+{
+       const struct dm_mst_precompute_param *param = test->param_value;
+       struct dm_mst_test_crtc_state_ctx ctx;
+       struct dc_link *link;
+
+       dm_mst_test_init_crtc_state_ctx(test, &ctx, 1);
+       if (param->connector_attached)
+               dm_mst_test_attach_connector(&ctx, &ctx.crtcs[0]);
+
+       if (param->has_stream) {
+               link = dm_kunit_alloc_link(test);
+               link->type = param->link_type;
+               
link->dpcd_caps.dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_SUPPORT =
+                       param->dsc_support;
+               
link->dpcd_caps.dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_PASSTHROUGH_SUPPORT
 =
+                       param->dsc_passthrough;
+               ctx.crtc_states[0].stream = dm_kunit_alloc_stream(test, link);
+       }
+
+       KUNIT_EXPECT_EQ(test, is_dsc_precompute_needed(ctx.state), 
param->needed);
+}
+
 static struct kunit_case dm_mst_types_test_cases[] = {
        /* needs_dsc_aux_workaround tests */
        KUNIT_CASE(dm_mst_test_needs_dsc_aux_workaround_match),
@@ -3219,6 +3386,13 @@ static struct kunit_case dm_mst_types_test_cases[] = {
        KUNIT_CASE(dm_mst_test_conv_frl_bw_not_hdmi_port),
        KUNIT_CASE(dm_mst_test_conv_frl_bw_sink_without_frl),
        KUNIT_CASE(dm_mst_test_conv_frl_bw_bottleneck),
+       /* log_dsc_params tests */
+       KUNIT_CASE(dm_mst_test_log_dsc_params),
+       /* find_crtc_index_in_state_by_stream tests */
+       KUNIT_CASE(dm_mst_test_find_crtc_index_matches),
+       KUNIT_CASE(dm_mst_test_find_crtc_index_no_match),
+       /* is_dsc_precompute_needed tests */
+       KUNIT_CASE_PARAM(dm_mst_test_dsc_precompute_needed, 
dm_mst_precompute_gen_params),
        {}
 };
 
-- 
2.43.0

Reply via email to