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
