[PATCH 42/70] drm/amd/display: add CRC set-source KUnit coverage

Wayne Lin <[email protected]>
Newsgroups org.freedesktop.lists.amd-gfx
Message-ID <[email protected]>
From: Alex Hung <[email protected]>

[WHAT]
Expose amdgpu_dm_crtc_set_crc_source() for KUnit and add tests for the
invalid-source guard, the valid NONE no-stream exit, the pending-commit
wait/put path, and the DPRX connector-walk that returns -EINVAL when no
matching DP connector is attached.

Add connector test helpers (drm_connector_funcs plus a cleanup action)
to back the DPRX connector-walk test.

Assisted-by: Copilot:Claude-Opus-4.8 GPT-5.5
Reviewed-by: Bhawanpreet Lakha <[email protected]>
Signed-off-by: Alex Hung <[email protected]>
Signed-off-by: Wayne Lin <[email protected]>
---
 .../drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c |   1 +
 .../amdgpu_dm/tests/amdgpu_dm_crc_test.c      | 161 ++++++++++++++++++
 2 files changed, 162 insertions(+)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c
index 47beee584dbf..0e896aab098a 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c
@@ -816,6 +816,7 @@ int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name)
 
 	return ret;
 }
+EXPORT_IF_KUNIT(amdgpu_dm_crtc_set_crc_source);
 
 /**
  * amdgpu_dm_crtc_handle_crc_irq: Report to DRM the CRC on given CRTC.
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crc_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crc_test.c
index 383646ad7005..96bdb83317ec 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crc_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crc_test.c
@@ -7,7 +7,11 @@
 
 #include <kunit/test.h>
 
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_connector.h>
 #include <drm/drm_modeset_lock.h>
+#include <drm/drm_probe_helper.h>
 
 #include "dc.h"
 #include "core_types.h"
@@ -170,6 +174,19 @@ static struct amdgpu_crtc *dm_test_alloc_crc_crtc(struct kunit *test,
 	return acrtc;
 }
 
+static const struct drm_connector_funcs dm_test_crc_connector_funcs = {
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = drm_connector_cleanup,
+};
+
+static void dm_test_crc_connector_cleanup(void *data)
+{
+	drm_connector_cleanup(data);
+}
+
 static void dm_test_parse_crc_source_none(struct kunit *test)
 {
 	KUNIT_EXPECT_EQ(test, AMDGPU_DM_PIPE_CRC_SOURCE_NONE, dm_parse_crc_source("none"));
@@ -517,6 +534,145 @@ static void dm_test_crtc_configure_crc_source_none(struct kunit *test)
 	KUNIT_EXPECT_TRUE(test, fixture->bit_depth_reduction_called);
 }
 
+/**
+ * dm_test_crtc_set_crc_source_invalid() - Test invalid source guard.
+ * @test: KUnit test context.
+ *
+ * Verifies that amdgpu_dm_crtc_set_crc_source() rejects invalid source names
+ * before taking modeset locks, vblank references, or touching DC state.
+ */
+static void dm_test_crtc_set_crc_source_invalid(struct kunit *test)
+{
+	struct amdgpu_device *adev = dm_kunit_alloc_adev(test);
+	struct amdgpu_crtc *acrtc = dm_test_alloc_crc_crtc(test, adev);
+	int ret;
+
+	ret = amdgpu_dm_crtc_set_crc_source(&acrtc->base, "invalid");
+
+	KUNIT_EXPECT_EQ(test, ret, -EINVAL);
+}
+
+/**
+ * dm_test_crtc_set_crc_source_none_no_stream() - Test valid source no-stream exit.
+ * @test: KUnit test context.
+ *
+ * Verifies that a valid NONE request enters the set-source body, reads the
+ * current CRC state, and exits cleanly when configuration is deferred because
+ * no stream is attached.
+ */
+static void dm_test_crtc_set_crc_source_none_no_stream(struct kunit *test)
+{
+	struct amdgpu_device *adev = dm_kunit_alloc_adev(test);
+	struct amdgpu_crtc *acrtc = dm_test_alloc_crc_crtc(test, adev);
+	struct dm_crtc_state *dm_state;
+	int ret;
+
+	dm_state = kunit_kzalloc(test, sizeof(*dm_state), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, dm_state);
+	acrtc->base.state = &dm_state->base;
+	acrtc->dm_irq_params.crc_src = AMDGPU_DM_PIPE_CRC_SOURCE_NONE;
+
+	ret = amdgpu_dm_crtc_set_crc_source(&acrtc->base, "none");
+
+	KUNIT_EXPECT_EQ(test, ret, -EINVAL);
+	KUNIT_EXPECT_EQ(test, acrtc->dm_irq_params.crc_src,
+			AMDGPU_DM_PIPE_CRC_SOURCE_NONE);
+	KUNIT_EXPECT_EQ(test, dm_state->crc_skip_count, 0);
+}
+
+/**
+ * dm_test_crtc_set_crc_source_none_commit() - Test set-source with pending commit.
+ * @test: KUnit test context.
+ *
+ * Verifies that a pending CRTC commit is acquired and waited on (already
+ * completed here so the wait returns immediately), then released during
+ * cleanup. Configuration is still deferred because no stream is attached.
+ */
+static void dm_test_crtc_set_crc_source_none_commit(struct kunit *test)
+{
+	struct amdgpu_device *adev = dm_kunit_alloc_adev(test);
+	struct amdgpu_crtc *acrtc = dm_test_alloc_crc_crtc(test, adev);
+	struct dm_crtc_state *dm_state;
+	struct drm_crtc_commit *commit;
+	int ret;
+
+	dm_state = kunit_kzalloc(test, sizeof(*dm_state), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, dm_state);
+	acrtc->base.state = &dm_state->base;
+	acrtc->dm_irq_params.crc_src = AMDGPU_DM_PIPE_CRC_SOURCE_NONE;
+
+	commit = kunit_kzalloc(test, sizeof(*commit), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, commit);
+	kref_init(&commit->ref);
+	init_completion(&commit->hw_done);
+	/* Mark the commit finished so the wait returns immediately. */
+	complete_all(&commit->hw_done);
+	list_add_tail(&commit->commit_entry, &acrtc->base.commit_list);
+
+	ret = amdgpu_dm_crtc_set_crc_source(&acrtc->base, "none");
+
+	KUNIT_EXPECT_EQ(test, ret, -EINVAL);
+	KUNIT_EXPECT_EQ(test, dm_state->crc_skip_count, 0);
+}
+
+/**
+ * dm_test_crtc_set_crc_source_dprx_no_connector() - Test DPRX with no match.
+ * @test: KUnit test context.
+ *
+ * Verifies that requesting a DPRX source walks the connector list and returns
+ * -EINVAL when no matching DP connector is attached to the CRTC. A stateless
+ * connector and a writeback connector exercise both connector filter branches.
+ */
+static void dm_test_crtc_set_crc_source_dprx_no_connector(struct kunit *test)
+{
+	struct amdgpu_device *adev = dm_kunit_alloc_adev(test);
+	struct amdgpu_crtc *acrtc = dm_test_alloc_crc_crtc(test, adev);
+	struct drm_connector *dp_conn;
+	struct drm_connector *wb_conn;
+	struct dm_crtc_state *dm_state;
+	int ret;
+
+	dm_state = kunit_kzalloc(test, sizeof(*dm_state), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, dm_state);
+	acrtc->base.state = &dm_state->base;
+	acrtc->dm_irq_params.crc_src = AMDGPU_DM_PIPE_CRC_SOURCE_NONE;
+
+	/* Stateless DP connector: skipped by the !state filter. */
+	dp_conn = kunit_kzalloc(test, sizeof(*dp_conn), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, dp_conn);
+	KUNIT_ASSERT_EQ(test, drm_connector_init(&adev->ddev, dp_conn,
+						 &dm_test_crc_connector_funcs,
+						 DRM_MODE_CONNECTOR_DisplayPort), 0);
+	KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test,
+			dm_test_crc_connector_cleanup, dp_conn), 0);
+
+	/* Writeback connector bound to this CRTC: skipped by the WB filter. */
+	wb_conn = kunit_kzalloc(test, sizeof(*wb_conn), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, wb_conn);
+	KUNIT_ASSERT_EQ(test, drm_connector_init(&adev->ddev, wb_conn,
+						 &dm_test_crc_connector_funcs,
+						 DRM_MODE_CONNECTOR_WRITEBACK), 0);
+	KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test,
+			dm_test_crc_connector_cleanup, wb_conn), 0);
+	drm_atomic_helper_connector_reset(wb_conn);
+	KUNIT_ASSERT_NOT_NULL(test, wb_conn->state);
+	wb_conn->state->crtc = &acrtc->base;
+	/*
+	 * __drm_atomic_helper_connector_destroy_state() drops a connector
+	 * reference when state->crtc is set. Balance it here since the CRTC is
+	 * assigned directly rather than via drm_atomic_set_crtc_for_connector(),
+	 * otherwise cleanup would drop the connector to zero and schedule an
+	 * async free on the system workqueue.
+	 */
+	drm_connector_get(wb_conn);
+
+	ret = amdgpu_dm_crtc_set_crc_source(&acrtc->base, "dprx");
+
+	KUNIT_EXPECT_EQ(test, ret, -EINVAL);
+	KUNIT_EXPECT_EQ(test, acrtc->dm_irq_params.crc_src,
+			AMDGPU_DM_PIPE_CRC_SOURCE_NONE);
+}
+
 /**
  * dm_test_need_dp_aux() - Test dm_need_dp_aux().
  * @test: KUnit test context.
@@ -657,6 +813,11 @@ static struct kunit_case dm_crc_test_cases[] = {
 	KUNIT_CASE(dm_test_crtc_configure_crc_source_crtc_dcn36_poly),
 	KUNIT_CASE(dm_test_crtc_configure_crc_source_crtc_configure_fails),
 	KUNIT_CASE(dm_test_crtc_configure_crc_source_none),
+	/* amdgpu_dm_crtc_set_crc_source() */
+	KUNIT_CASE(dm_test_crtc_set_crc_source_invalid),
+	KUNIT_CASE(dm_test_crtc_set_crc_source_none_no_stream),
+	KUNIT_CASE(dm_test_crtc_set_crc_source_none_commit),
+	KUNIT_CASE(dm_test_crtc_set_crc_source_dprx_no_connector),
 	/* dm_need_dp_aux() */
 	KUNIT_CASE(dm_test_need_dp_aux),
 	/* dm_crc_source_should_start_dprx() */
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.