[PATCH 14/41] drm/amd/display: Add KUnit tests for more crtc functions
<[email protected]> Fri, 31 Jul 2026 17:12:35 -0400
| Newsgroups | org.freedesktop.lists.amd-gfx |
|---|---|
| Message-ID | <[email protected]> |
From: Bhawanpreet Lakha <[email protected]> Expand KUnit coverage for amdgpu_dm_crtc.c with tests for functions that are easy to exercise in isolation: - amdgpu_dm_crtc_set_static_screen_optimze(): the !allow_sr_entry early return. - amdgpu_dm_crtc_enable_vblank(): rejection with -EINVAL when enabling vblank on an unconfigured CRTC. - amdgpu_dm_crtc_update_crtc_active_planes(): the no-stream branch that resets active_planes to zero. - amdgpu_dm_crtc_duplicate_state(): DM-specific fields are carried over. - amdgpu_dm_crtc_reset_state(): a fresh state is allocated and installed. - amdgpu_dm_crtc_destroy_state(): a stream-less state is freed cleanly. Expose amdgpu_dm_crtc_destroy_state(), amdgpu_dm_crtc_duplicate_state(), amdgpu_dm_crtc_reset_state() and amdgpu_dm_crtc_update_crtc_active_planes() to the tests via STATIC_IFN_KUNIT/EXPORT_IF_KUNIT. Assisted-by: Copilot:Claude-Opus-4.8 Reviewed-by: Alex Hung <[email protected]> Signed-off-by: Bhawanpreet Lakha <[email protected]> Signed-off-by: Roman Li <[email protected]> --- .../amd/display/amdgpu_dm/amdgpu_dm_crtc.c | 14 +- .../amd/display/amdgpu_dm/amdgpu_dm_crtc.h | 6 + .../amdgpu_dm/tests/amdgpu_dm_crtc_test.c | 177 ++++++++++++++++++ 3 files changed, 193 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c index 079d4ccc88da..ce42102109bb 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c @@ -134,6 +134,7 @@ void amdgpu_dm_crtc_set_static_screen_optimze( amdgpu_dm_psr_set_event(dm, stream, set_vsync_event, psr_event_vsync, set_vsync_event); } +EXPORT_IF_KUNIT(amdgpu_dm_crtc_set_static_screen_optimze); bool amdgpu_dm_is_headless(struct amdgpu_device *adev) { @@ -393,13 +394,14 @@ int amdgpu_dm_crtc_enable_vblank(struct drm_crtc *crtc) { return amdgpu_dm_crtc_set_vblank(crtc, true); } +EXPORT_IF_KUNIT(amdgpu_dm_crtc_enable_vblank); void amdgpu_dm_crtc_disable_vblank(struct drm_crtc *crtc) { amdgpu_dm_crtc_set_vblank(crtc, false); } -static void amdgpu_dm_crtc_destroy_state(struct drm_crtc *crtc, +STATIC_IFN_KUNIT void amdgpu_dm_crtc_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *state) { struct dm_crtc_state *cur = to_dm_crtc_state(state); @@ -414,8 +416,9 @@ static void amdgpu_dm_crtc_destroy_state(struct drm_crtc *crtc, kfree(state); } +EXPORT_IF_KUNIT(amdgpu_dm_crtc_destroy_state); -static struct drm_crtc_state *amdgpu_dm_crtc_duplicate_state(struct drm_crtc *crtc) +STATIC_IFN_KUNIT struct drm_crtc_state *amdgpu_dm_crtc_duplicate_state(struct drm_crtc *crtc) { struct dm_crtc_state *state, *cur; @@ -450,6 +453,7 @@ static struct drm_crtc_state *amdgpu_dm_crtc_duplicate_state(struct drm_crtc *cr return &state->base; } +EXPORT_IF_KUNIT(amdgpu_dm_crtc_duplicate_state); static void amdgpu_dm_crtc_destroy(struct drm_crtc *crtc) { @@ -463,7 +467,7 @@ static void amdgpu_dm_crtc_destroy(struct drm_crtc *crtc) kfree(crtc); } -static void amdgpu_dm_crtc_reset_state(struct drm_crtc *crtc) +STATIC_IFN_KUNIT void amdgpu_dm_crtc_reset_state(struct drm_crtc *crtc) { struct dm_crtc_state *state; @@ -476,6 +480,7 @@ static void amdgpu_dm_crtc_reset_state(struct drm_crtc *crtc) __drm_atomic_helper_crtc_reset(crtc, &state->base); } +EXPORT_IF_KUNIT(amdgpu_dm_crtc_reset_state); #ifdef CONFIG_DEBUG_FS static int amdgpu_dm_crtc_late_register(struct drm_crtc *crtc) @@ -610,7 +615,7 @@ static int amdgpu_dm_crtc_count_crtc_active_planes(struct drm_crtc_state *new_cr return num_active; } -static void amdgpu_dm_crtc_update_crtc_active_planes(struct drm_crtc *crtc, +STATIC_IFN_KUNIT void amdgpu_dm_crtc_update_crtc_active_planes(struct drm_crtc *crtc, struct drm_crtc_state *new_crtc_state) { struct dm_crtc_state *dm_new_crtc_state = @@ -624,6 +629,7 @@ static void amdgpu_dm_crtc_update_crtc_active_planes(struct drm_crtc *crtc, dm_new_crtc_state->active_planes = amdgpu_dm_crtc_count_crtc_active_planes(new_crtc_state); } +EXPORT_IF_KUNIT(amdgpu_dm_crtc_update_crtc_active_planes); STATIC_IFN_KUNIT bool amdgpu_dm_crtc_helper_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.h index d8b004f613ab..ed6e8021d69e 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.h @@ -46,6 +46,12 @@ bool amdgpu_dm_crtc_vrr_active_irq(struct amdgpu_crtc *acrtc); bool amdgpu_dm_crtc_helper_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); +void amdgpu_dm_crtc_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state); +struct drm_crtc_state *amdgpu_dm_crtc_duplicate_state(struct drm_crtc *crtc); +void amdgpu_dm_crtc_reset_state(struct drm_crtc *crtc); +void amdgpu_dm_crtc_update_crtc_active_planes(struct drm_crtc *crtc, + struct drm_crtc_state *new_crtc_state); #endif bool amdgpu_dm_crtc_vrr_active(const struct dm_crtc_state *dm_state); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crtc_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crtc_test.c index ac21bbec92ab..ce20692959d0 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crtc_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crtc_test.c @@ -456,6 +456,171 @@ static void dm_test_idle_create_workqueue(struct kunit *test) kfree(idle_work); } +/* Tests for amdgpu_dm_crtc_set_static_screen_optimze() */ + +/** + * dm_test_crtc_set_static_screen_optimze_no_sr_entry - Test early return when SR entry disallowed + * @test: The KUnit test context + * + * When self-refresh entry is not allowed the function must return immediately + * without touching replay or PSR events, regardless of the requested SSO state. + */ +static void dm_test_crtc_set_static_screen_optimze_no_sr_entry(struct kunit *test) +{ + struct amdgpu_display_manager *dm; + struct dc_link *link; + struct dc_stream_state *stream; + + dm = kunit_kzalloc(test, sizeof(*dm), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dm); + + link = dm_kunit_alloc_link(test); + stream = dm_kunit_alloc_stream(test, link); + + /* allow_sr_entry == false -> returns before any event is set. */ + amdgpu_dm_crtc_set_static_screen_optimze(dm, stream, true, false); + amdgpu_dm_crtc_set_static_screen_optimze(dm, stream, false, false); +} + +/* Tests for amdgpu_dm_crtc_enable_vblank() */ + +/** + * dm_test_crtc_enable_vblank_rejects_unconfigured - Test vblank enable on disabled CRTC + * @test: The KUnit test context + * + * Enabling vblank on a CRTC that is not enabled must be rejected with -EINVAL + * before any interrupt state is touched. + */ +static void dm_test_crtc_enable_vblank_rejects_unconfigured(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct amdgpu_crtc *acrtc; + + acrtc = kunit_kzalloc(test, sizeof(*acrtc), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acrtc); + + acrtc->base.dev = &adev->ddev; + acrtc->base.enabled = false; + + KUNIT_EXPECT_EQ(test, amdgpu_dm_crtc_enable_vblank(&acrtc->base), -EINVAL); +} + +/* Tests for amdgpu_dm_crtc_update_crtc_active_planes() */ + +/** + * dm_test_crtc_update_active_planes_no_stream - Test active plane reset without a stream + * @test: The KUnit test context + * + * Without a DC stream attached the active plane count must be reset to zero + * and the plane-counting path must be skipped. + */ +static void dm_test_crtc_update_active_planes_no_stream(struct kunit *test) +{ + struct dm_crtc_state *dm_state; + + dm_state = kunit_kzalloc(test, sizeof(*dm_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dm_state); + + dm_state->stream = NULL; + dm_state->active_planes = 5; + + amdgpu_dm_crtc_update_crtc_active_planes(NULL, &dm_state->base); + + KUNIT_EXPECT_EQ(test, dm_state->active_planes, 0); +} + +/* Tests for amdgpu_dm_crtc_duplicate_state() */ + +/** + * dm_test_crtc_duplicate_state_copies_fields - Test duplicated state carries DM fields + * @test: The KUnit test context + * + * Duplicating a CRTC state without a stream must produce a new state that + * carries over the DM-specific fields from the current state. + */ +static void dm_test_crtc_duplicate_state_copies_fields(struct kunit *test) +{ + struct drm_crtc *crtc; + struct dm_crtc_state *cur; + struct drm_crtc_state *dup; + struct dm_crtc_state *dm_dup; + + crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc); + cur = kunit_kzalloc(test, sizeof(*cur), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cur); + + cur->abm_level = 3; + cur->active_planes = 2; + cur->vrr_supported = true; + cur->cm_has_degamma = true; + cur->cm_is_degamma_srgb = true; + cur->crc_skip_count = 7; + cur->mpo_requested = true; + crtc->state = &cur->base; + + dup = amdgpu_dm_crtc_duplicate_state(crtc); + KUNIT_ASSERT_NOT_NULL(test, dup); + + dm_dup = to_dm_crtc_state(dup); + KUNIT_EXPECT_EQ(test, dm_dup->abm_level, 3); + KUNIT_EXPECT_EQ(test, dm_dup->active_planes, 2); + KUNIT_EXPECT_TRUE(test, dm_dup->vrr_supported); + KUNIT_EXPECT_TRUE(test, dm_dup->cm_has_degamma); + KUNIT_EXPECT_TRUE(test, dm_dup->cm_is_degamma_srgb); + KUNIT_EXPECT_EQ(test, dm_dup->crc_skip_count, 7); + KUNIT_EXPECT_TRUE(test, dm_dup->mpo_requested); + + amdgpu_dm_crtc_destroy_state(crtc, dup); +} + +/* Tests for amdgpu_dm_crtc_reset_state() */ + +/** + * dm_test_crtc_reset_state_allocates_state - Test reset installs a fresh state + * @test: The KUnit test context + * + * Resetting a CRTC with no existing state must allocate and install a new + * drm_crtc_state. + */ +static void dm_test_crtc_reset_state_allocates_state(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct drm_crtc *crtc; + + crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc); + crtc->dev = &adev->ddev; + crtc->state = NULL; + + amdgpu_dm_crtc_reset_state(crtc); + + KUNIT_EXPECT_NOT_NULL(test, crtc->state); + + if (crtc->state) + amdgpu_dm_crtc_destroy_state(crtc, crtc->state); +} + +/* Tests for amdgpu_dm_crtc_destroy_state() */ + +/** + * dm_test_crtc_destroy_state_no_stream - Test destroy frees a stream-less state + * @test: The KUnit test context + * + * Destroying a CRTC state with no stream attached must free the state without + * attempting to release a DC stream. + */ +static void dm_test_crtc_destroy_state_no_stream(struct kunit *test) +{ + struct dm_crtc_state *dm_state; + + /* destroy_state kfree()s the state, so use a plain (unmanaged) alloc. */ + dm_state = kzalloc_obj(*dm_state, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dm_state); + + amdgpu_dm_crtc_destroy_state(NULL, &dm_state->base); +} + static struct kunit_case amdgpu_dm_crtc_tests[] = { /* amdgpu_dm_crtc_modeset_required */ KUNIT_CASE(dm_test_crtc_modeset_required_active_mode_changed), @@ -488,6 +653,18 @@ static struct kunit_case amdgpu_dm_crtc_tests[] = { KUNIT_CASE(dm_test_crtc_set_vupdate_irq_no_otg), /* idle_create_workqueue */ KUNIT_CASE(dm_test_idle_create_workqueue), + /* amdgpu_dm_crtc_set_static_screen_optimze */ + KUNIT_CASE(dm_test_crtc_set_static_screen_optimze_no_sr_entry), + /* amdgpu_dm_crtc_enable_vblank */ + KUNIT_CASE(dm_test_crtc_enable_vblank_rejects_unconfigured), + /* amdgpu_dm_crtc_update_crtc_active_planes */ + KUNIT_CASE(dm_test_crtc_update_active_planes_no_stream), + /* amdgpu_dm_crtc_duplicate_state */ + KUNIT_CASE(dm_test_crtc_duplicate_state_copies_fields), + /* amdgpu_dm_crtc_reset_state */ + KUNIT_CASE(dm_test_crtc_reset_state_allocates_state), + /* amdgpu_dm_crtc_destroy_state */ + KUNIT_CASE(dm_test_crtc_destroy_state_no_stream), {} }; -- 2.34.1