[PATCH 05/34] drm/amd/display: Cover crtc destroy callback

Tom Chung <[email protected]> Wed, 5 Aug 2026 14:36:17 +0800
Newsgroups org.freedesktop.lists.amd-gfx
Message-ID <[email protected]>
From: Bhawanpreet Lakha <[email protected]>

[why]
The amdgpu_dm_crtc_destroy() .destroy callback, which runs
drm_crtc_cleanup() and frees the CRTC, was not exercised by the KUnit
suite.

[how]
Exposed amdgpu_dm_crtc_destroy() via STATIC_IFN_KUNIT/EXPORT_IF_KUNIT and a
KUnit-only declaration, and added dm_test_crtc_destroy_cleans_up_and_frees
which initializes a CRTC with a primary plane and verifies
mode_config.num_crtc drops from 1 to 0 after destroy. Registered the case
in amdgpu_dm_crtc_tests.

Reviewed-by: Alex Hung <[email protected]>
Signed-off-by: Bhawanpreet Lakha <[email protected]>
Signed-off-by: Tom Chung <[email protected]>
---
 .../amd/display/amdgpu_dm/amdgpu_dm_crtc.c    |  3 +-
 .../amd/display/amdgpu_dm/amdgpu_dm_crtc.h    |  1 +
 .../amdgpu_dm/tests/amdgpu_dm_crtc_test.c     | 43 +++++++++++++++++++
 3 files changed, 46 insertions(+), 1 deletion(-)

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 82a30097072e..470abe9cc8f1 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
@@ -459,7 +459,7 @@ STATIC_IFN_KUNIT struct drm_crtc_state *amdgpu_dm_crtc_duplicate_state(struct dr
 }
 EXPORT_IF_KUNIT(amdgpu_dm_crtc_duplicate_state);
 
-static void amdgpu_dm_crtc_destroy(struct drm_crtc *crtc)
+STATIC_IFN_KUNIT void amdgpu_dm_crtc_destroy(struct drm_crtc *crtc)
 {
 	/*
 	 * amdgpu_dm_ism_fini() is intentionally called in amdgpu_dm_fini().
@@ -470,6 +470,7 @@ static void amdgpu_dm_crtc_destroy(struct drm_crtc *crtc)
 	drm_crtc_cleanup(crtc);
 	kfree(crtc);
 }
+EXPORT_IF_KUNIT(amdgpu_dm_crtc_destroy);
 
 STATIC_IFN_KUNIT void amdgpu_dm_crtc_reset_state(struct drm_crtc *crtc)
 {
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 93c6d0d8d7fd..06bb22d4c3b7 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
@@ -49,6 +49,7 @@ bool amdgpu_dm_crtc_helper_mode_fixup(struct drm_crtc *crtc,
 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_destroy(struct drm_crtc *crtc);
 void amdgpu_dm_crtc_reset_state(struct drm_crtc *crtc);
 int amdgpu_dm_crtc_count_crtc_active_planes(struct drm_crtc_state *new_crtc_state);
 void amdgpu_dm_crtc_update_crtc_active_planes(struct drm_crtc *crtc,
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 a23281a33422..1af819113168 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
@@ -1463,6 +1463,47 @@ static void dm_test_crtc_duplicate_state_null_state_returns_null(struct kunit *t
 	KUNIT_EXPECT_NULL(test, amdgpu_dm_crtc_duplicate_state(crtc));
 }
 
+/* Tests for amdgpu_dm_crtc_destroy() */
+
+/**
+ * dm_test_crtc_destroy_cleans_up_and_frees - Test destroy tears down the CRTC
+ * @test: The KUnit test context
+ *
+ * amdgpu_dm_crtc_destroy() is the drm_crtc .destroy callback: it must run
+ * drm_crtc_cleanup() and free the CRTC. Initialise a CRTC with a primary plane
+ * so it is registered on the device (num_crtc == 1), then destroy it and verify
+ * the CRTC was unregistered (num_crtc back to 0). The CRTC is a plain (unmanaged)
+ * allocation because amdgpu_dm_crtc_destroy() kfree()s it.
+ */
+static void dm_test_crtc_destroy_cleans_up_and_frees(struct kunit *test)
+{
+	struct amdgpu_device *adev;
+	struct amdgpu_crtc *acrtc;
+	struct drm_plane *plane;
+	int ret;
+
+	adev = dm_kunit_alloc_adev(test);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+	plane = drm_kunit_helper_create_primary_plane(test, &adev->ddev,
+						      NULL, NULL, NULL, 0, NULL);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane);
+
+	/* amdgpu_dm_crtc_destroy() kfree()s the CRTC, so use a plain alloc. */
+	acrtc = kzalloc_obj(*acrtc, GFP_KERNEL);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acrtc);
+
+	ret = drm_crtc_init_with_planes(&adev->ddev, &acrtc->base, plane,
+					NULL, &dm_test_crtc_funcs, NULL);
+	KUNIT_ASSERT_EQ(test, ret, 0);
+	KUNIT_EXPECT_EQ(test, adev->ddev.mode_config.num_crtc, 1);
+
+	amdgpu_dm_crtc_destroy(&acrtc->base);
+
+	/* drm_crtc_cleanup() ran: the CRTC was unregistered from the device. */
+	KUNIT_EXPECT_EQ(test, adev->ddev.mode_config.num_crtc, 0);
+}
+
 /* Tests for amdgpu_dm_crtc_reset_state() */
 
 /**
@@ -1968,6 +2009,8 @@ static struct kunit_case amdgpu_dm_crtc_tests[] = {
 	KUNIT_CASE(dm_test_crtc_duplicate_state_copies_fields),
 	KUNIT_CASE(dm_test_crtc_duplicate_state_retains_stream),
 	KUNIT_CASE(dm_test_crtc_duplicate_state_null_state_returns_null),
+	/* amdgpu_dm_crtc_destroy */
+	KUNIT_CASE(dm_test_crtc_destroy_cleans_up_and_frees),
 	/* amdgpu_dm_crtc_reset_state */
 	KUNIT_CASE(dm_test_crtc_reset_state_allocates_state),
 	/* amdgpu_dm_crtc_destroy_state */
-- 
2.43.0