From: Alex Hung <[email protected]> [WHAT] Add KUnit tests for is_dsc_need_re_compute() covering a non MST branch, a legacy hub without DSC support, a state with no stream on the link, a mode change, an unchanged stream, a removed stream, a stream without a connector and a connector without a CRTC.
[HOW] Build a DC state carrying streams on the link under test together with a matching drm_atomic_commit, then vary the topology, the connector to CRTC mapping and the mode changed flag to select each path. 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 | 3 +- .../display/amdgpu_dm/amdgpu_dm_mst_types.h | 2 + .../tests/amdgpu_dm_mst_types_test.c | 228 ++++++++++++++++++ 3 files changed, 232 insertions(+), 1 deletion(-) 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 f5085a0036af..fc71f8e567c9 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 @@ -1485,7 +1485,7 @@ static int compute_mst_dsc_configs_for_link(struct drm_atomic_commit *state, return 0; } -static bool is_dsc_need_re_compute( +STATIC_IFN_KUNIT bool is_dsc_need_re_compute( struct drm_atomic_commit *state, struct dc_state *dc_state, struct dc_link *dc_link) @@ -1616,6 +1616,7 @@ static bool is_dsc_need_re_compute( return is_dsc_need_re_compute; } +EXPORT_IF_KUNIT(is_dsc_need_re_compute); int compute_mst_dsc_configs_for_state(struct drm_atomic_commit *state, struct dc_state *dc_state, 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 b1d7a21a37e3..90efb1cf90e1 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 @@ -113,6 +113,8 @@ 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 is_dsc_need_re_compute(struct drm_atomic_commit *state, struct dc_state *dc_state, + struct dc_link *dc_link); 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 d91863d5416d..9af4fd84e69c 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 @@ -3276,6 +3276,225 @@ static void dm_mst_test_dsc_precompute_needed(struct kunit *test) KUNIT_EXPECT_EQ(test, is_dsc_precompute_needed(ctx.state), param->needed); } +/* Tests for is_dsc_need_re_compute */ + +struct dm_mst_test_recompute_ctx { + struct amdgpu_device *adev; + struct drm_atomic_commit *state; + struct amdgpu_dm_connector *aconnector; + struct drm_connector_state *conn_state; + struct drm_crtc_state *crtc_state; + struct dc_state *dc_state; + struct dc_link *link; + struct dc *dc; +}; + +/* + * One MST connector on one CRTC, with an empty new dc_state and an empty + * current dc_state. Tests populate the streams they need and adjust the CRTC + * state flags to select the branch under test. + */ +static void dm_mst_test_init_recompute_ctx(struct kunit *test, + struct dm_mst_test_recompute_ctx *ctx) +{ + struct drm_crtc *crtc; + + ctx->adev = dm_kunit_alloc_adev(test); + ctx->adev->ddev.mode_config.num_crtc = 1; + + ctx->dc = dm_kunit_alloc_dc_with_ctx(test); + ctx->dc->current_state = dm_kunit_alloc_dc_state(test); + ctx->dc_state = dm_kunit_alloc_dc_state(test); + + ctx->link = dm_kunit_alloc_link(test); + ctx->link->dc = ctx->dc; + ctx->link->type = dc_connection_mst_branch; + + ctx->aconnector = dm_kunit_alloc_connector(test, ctx->adev, ctx->link); + ctx->conn_state = kunit_kzalloc(test, sizeof(*ctx->conn_state), GFP_KERNEL); + crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL); + ctx->crtc_state = kunit_kzalloc(test, sizeof(*ctx->crtc_state), GFP_KERNEL); + ctx->state = kunit_kzalloc(test, sizeof(*ctx->state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->conn_state); + KUNIT_ASSERT_NOT_NULL(test, crtc); + KUNIT_ASSERT_NOT_NULL(test, ctx->crtc_state); + KUNIT_ASSERT_NOT_NULL(test, ctx->state); + + ctx->state->dev = &ctx->adev->ddev; + ctx->state->crtcs = kunit_kzalloc(test, 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); + + crtc->dev = &ctx->adev->ddev; + ctx->crtc_state->crtc = crtc; + ctx->state->crtcs[0].ptr = crtc; + ctx->state->crtcs[0].new_state = ctx->crtc_state; + ctx->state->crtcs[0].old_state = ctx->crtc_state; + + ctx->conn_state->connector = &ctx->aconnector->base; + ctx->conn_state->crtc = crtc; + ctx->state->connectors[0].ptr = &ctx->aconnector->base; + ctx->state->connectors[0].new_state = ctx->conn_state; + ctx->state->num_connector = 1; +} + +static struct dc_stream_state *dm_mst_test_add_link_stream(struct kunit *test, + struct dc_state *dc_state, + struct dc_link *link, + struct amdgpu_dm_connector *aconnector) +{ + struct dc_stream_state *stream = dm_kunit_alloc_stream(test, link); + + KUNIT_ASSERT_LT(test, dc_state->stream_count, MAX_PIPES); + stream->dm_stream_context = aconnector; + dc_state->streams[dc_state->stream_count++] = stream; + + return stream; +} + +/** + * dm_mst_test_recompute_not_mst_branch - only MST branches are recomputed + * @test: KUnit test context + */ +static void dm_mst_test_recompute_not_mst_branch(struct kunit *test) +{ + struct dm_mst_test_recompute_ctx ctx; + + dm_mst_test_init_recompute_ctx(test, &ctx); + ctx.link->type = dc_connection_single; + + KUNIT_EXPECT_FALSE(test, is_dsc_need_re_compute(ctx.state, ctx.dc_state, ctx.link)); +} + +/** + * dm_mst_test_recompute_legacy_hub_without_dsc - old hubs without DSC are skipped + * @test: KUnit test context + * + * A hub matching the no-virtual-DPCD workaround but reporting neither DSC nor + * DSC passthrough support cannot use MST DSC at all. + */ +static void dm_mst_test_recompute_legacy_hub_without_dsc(struct kunit *test) +{ + struct dm_mst_test_recompute_ctx ctx; + + dm_mst_test_init_recompute_ctx(test, &ctx); + dm_mst_test_arm_dsc_aux_workaround(ctx.link); + + KUNIT_EXPECT_FALSE(test, is_dsc_need_re_compute(ctx.state, ctx.dc_state, ctx.link)); +} + +/** + * dm_mst_test_recompute_no_stream_on_link - no stream on the link, nothing to do + * @test: KUnit test context + * + * The new state only drives another link, so this hub keeps its current DSC + * configuration. + */ +static void dm_mst_test_recompute_no_stream_on_link(struct kunit *test) +{ + struct dm_mst_test_recompute_ctx ctx; + struct dc_link *other_link; + + dm_mst_test_init_recompute_ctx(test, &ctx); + other_link = dm_kunit_alloc_link(test); + dm_mst_test_add_link_stream(test, ctx.dc_state, other_link, ctx.aconnector); + + KUNIT_EXPECT_FALSE(test, is_dsc_need_re_compute(ctx.state, ctx.dc_state, ctx.link)); +} + +/** + * dm_mst_test_recompute_on_mode_change - a modeset on the link forces a recompute + * @test: KUnit test context + */ +static void dm_mst_test_recompute_on_mode_change(struct kunit *test) +{ + struct dm_mst_test_recompute_ctx ctx; + + dm_mst_test_init_recompute_ctx(test, &ctx); + dm_mst_test_add_link_stream(test, ctx.dc_state, ctx.link, ctx.aconnector); + ctx.crtc_state->enable = true; + ctx.crtc_state->active = true; + ctx.crtc_state->mode_changed = true; + + KUNIT_EXPECT_TRUE(test, is_dsc_need_re_compute(ctx.state, ctx.dc_state, ctx.link)); +} + +/** + * dm_mst_test_recompute_unchanged_stream - an untouched stream needs no recompute + * @test: KUnit test context + * + * The same stream is present in both the new and the current state and its + * CRTC reports no change, so the existing DSC configuration still applies. + */ +static void dm_mst_test_recompute_unchanged_stream(struct kunit *test) +{ + struct dm_mst_test_recompute_ctx ctx; + + dm_mst_test_init_recompute_ctx(test, &ctx); + dm_mst_test_add_link_stream(test, ctx.dc_state, ctx.link, ctx.aconnector); + dm_mst_test_add_link_stream(test, ctx.dc->current_state, ctx.link, ctx.aconnector); + ctx.crtc_state->enable = true; + ctx.crtc_state->active = true; + + KUNIT_EXPECT_FALSE(test, is_dsc_need_re_compute(ctx.state, ctx.dc_state, ctx.link)); +} + +/** + * dm_mst_test_recompute_stream_removed - dropping a stream forces a recompute + * @test: KUnit test context + * + * A stream that is on the link in the current state but absent from the new + * state frees up bandwidth, so the remaining streams must be recomputed. + */ +static void dm_mst_test_recompute_stream_removed(struct kunit *test) +{ + struct dm_mst_test_recompute_ctx ctx; + struct amdgpu_dm_connector *gone; + + dm_mst_test_init_recompute_ctx(test, &ctx); + gone = dm_kunit_alloc_connector(test, ctx.adev, ctx.link); + dm_mst_test_add_link_stream(test, ctx.dc_state, ctx.link, ctx.aconnector); + dm_mst_test_add_link_stream(test, ctx.dc->current_state, ctx.link, gone); + ctx.crtc_state->enable = true; + ctx.crtc_state->active = true; + + KUNIT_EXPECT_TRUE(test, is_dsc_need_re_compute(ctx.state, ctx.dc_state, ctx.link)); +} + +/** + * dm_mst_test_recompute_stream_without_connector - streams need a DM connector + * @test: KUnit test context + * + * A stream on the link whose DM context is not set cannot be attributed to a + * connector, so it does not count towards the streams on the link. + */ +static void dm_mst_test_recompute_stream_without_connector(struct kunit *test) +{ + struct dm_mst_test_recompute_ctx ctx; + + dm_mst_test_init_recompute_ctx(test, &ctx); + dm_mst_test_add_link_stream(test, ctx.dc_state, ctx.link, NULL); + + KUNIT_EXPECT_FALSE(test, is_dsc_need_re_compute(ctx.state, ctx.dc_state, ctx.link)); +} + +/** + * dm_mst_test_recompute_connector_without_crtc - a disabled connector is skipped + * @test: KUnit test context + */ +static void dm_mst_test_recompute_connector_without_crtc(struct kunit *test) +{ + struct dm_mst_test_recompute_ctx ctx; + + dm_mst_test_init_recompute_ctx(test, &ctx); + dm_mst_test_add_link_stream(test, ctx.dc_state, ctx.link, ctx.aconnector); + dm_mst_test_add_link_stream(test, ctx.dc->current_state, ctx.link, ctx.aconnector); + ctx.conn_state->crtc = NULL; + + KUNIT_EXPECT_FALSE(test, is_dsc_need_re_compute(ctx.state, ctx.dc_state, ctx.link)); +} + static struct kunit_case dm_mst_types_test_cases[] = { /* needs_dsc_aux_workaround tests */ KUNIT_CASE(dm_mst_test_needs_dsc_aux_workaround_match), @@ -3393,6 +3612,15 @@ static struct kunit_case dm_mst_types_test_cases[] = { 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), + /* is_dsc_need_re_compute tests */ + KUNIT_CASE(dm_mst_test_recompute_not_mst_branch), + KUNIT_CASE(dm_mst_test_recompute_legacy_hub_without_dsc), + KUNIT_CASE(dm_mst_test_recompute_no_stream_on_link), + KUNIT_CASE(dm_mst_test_recompute_on_mode_change), + KUNIT_CASE(dm_mst_test_recompute_unchanged_stream), + KUNIT_CASE(dm_mst_test_recompute_stream_removed), + KUNIT_CASE(dm_mst_test_recompute_stream_without_connector), + KUNIT_CASE(dm_mst_test_recompute_connector_without_crtc), {} }; -- 2.43.0
