[PATCH 41/70] drm/amd/display: add CRC configure 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_configure_crc_source() for KUnit and add tests for
the CRTC-enable, disable (NONE), DPRX, DPRX-dither, DCN3.6 polynomial
select, and DC-configure-failure paths.

Introduce a small fake DC fixture (timing-generator and OPP callbacks
over an empty resource context) so the configure path can be exercised
without real hardware. The fixture is shared with the CRC IRQ tests.

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      | 343 ++++++++++++++++++
 2 files changed, 344 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 6600cc6ecf8e..47beee584dbf 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
@@ -638,6 +638,7 @@ int amdgpu_dm_crtc_configure_crc_source(struct drm_crtc *crtc,
 
 	return ret;
 }
+EXPORT_IF_KUNIT(amdgpu_dm_crtc_configure_crc_source);
 
 int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name)
 {
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 4fa0bd9669c4..383646ad7005 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
@@ -10,12 +10,150 @@
 #include <drm/drm_modeset_lock.h>
 
 #include "dc.h"
+#include "core_types.h"
+#include "logger_types.h"
+#include "opp.h"
+#include "timing_generator.h"
 #include "amdgpu.h"
 #include "amdgpu_mode.h"
 #include "amdgpu_dm.h"
 #include "amdgpu_dm_crc.h"
 #include "amdgpu_dm_kunit_test_helpers.h"
 
+struct dm_test_crc_dc_fixture {
+	struct dc *dc;
+	struct dc_context *dc_ctx;
+	struct dc_state *dc_state;
+	struct dc_stream_state *stream;
+	struct dc_link *link;
+	struct timing_generator *tg;
+	struct output_pixel_processor *opp;
+	struct dal_logger *logger;
+	struct dm_crtc_state *dm_state;
+	struct crc_params crc_params;
+	enum dc_dynamic_expansion dyn_expansion;
+	enum dc_dither_option dither_option;
+	uint32_t crc_r;
+	uint32_t crc_g;
+	uint32_t crc_b;
+	bool configure_crc_called;
+	bool dyn_expansion_called;
+	bool bit_depth_reduction_called;
+	bool configure_crc_return;
+	bool get_crc_called;
+	bool get_crc_return;
+};
+
+static struct dm_test_crc_dc_fixture *dm_test_crc_dc_ctx;
+
+static bool dm_test_configure_crc(struct timing_generator *tg,
+					  const struct crc_params *params)
+{
+	if (!dm_test_crc_dc_ctx)
+		return false;
+
+	dm_test_crc_dc_ctx->configure_crc_called = true;
+	dm_test_crc_dc_ctx->crc_params = *params;
+
+	return dm_test_crc_dc_ctx->configure_crc_return;
+}
+
+static bool dm_test_get_crc(struct timing_generator *tg, uint8_t idx,
+				    uint32_t *r_cr, uint32_t *g_y, uint32_t *b_cb)
+{
+	if (!dm_test_crc_dc_ctx)
+		return false;
+
+	dm_test_crc_dc_ctx->get_crc_called = true;
+	*r_cr = dm_test_crc_dc_ctx->crc_r;
+	*g_y = dm_test_crc_dc_ctx->crc_g;
+	*b_cb = dm_test_crc_dc_ctx->crc_b;
+
+	return dm_test_crc_dc_ctx->get_crc_return;
+}
+
+static void dm_test_opp_set_dyn_expansion(struct output_pixel_processor *opp,
+						  enum dc_color_space color_sp,
+						  enum dc_color_depth color_dpth,
+						  enum signal_type signal)
+{
+	if (!dm_test_crc_dc_ctx)
+		return;
+
+	dm_test_crc_dc_ctx->dyn_expansion_called = true;
+	dm_test_crc_dc_ctx->dyn_expansion = opp->dyn_expansion;
+}
+
+static void dm_test_opp_program_bit_depth_reduction(struct output_pixel_processor *opp,
+							    const struct bit_depth_reduction_params *params)
+{
+	if (!dm_test_crc_dc_ctx)
+		return;
+
+	dm_test_crc_dc_ctx->bit_depth_reduction_called = true;
+}
+
+static const struct timing_generator_funcs dm_test_tg_funcs = {
+	.configure_crc = dm_test_configure_crc,
+	.get_crc = dm_test_get_crc,
+};
+
+static const struct opp_funcs dm_test_opp_funcs = {
+	.opp_set_dyn_expansion = dm_test_opp_set_dyn_expansion,
+	.opp_program_bit_depth_reduction = dm_test_opp_program_bit_depth_reduction,
+};
+
+static struct dm_test_crc_dc_fixture *dm_test_alloc_crc_dc_fixture(struct kunit *test,
+								   struct amdgpu_device *adev)
+{
+	struct dm_test_crc_dc_fixture *fixture;
+	struct pipe_ctx *pipe;
+
+	fixture = kunit_kzalloc(test, sizeof(*fixture), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, fixture);
+
+	fixture->dm_state = kunit_kzalloc(test, sizeof(*fixture->dm_state), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, fixture->dm_state);
+	fixture->dc = kunit_kzalloc(test, sizeof(*fixture->dc), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, fixture->dc);
+	fixture->dc_ctx = kunit_kzalloc(test, sizeof(*fixture->dc_ctx), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, fixture->dc_ctx);
+	fixture->dc_state = kunit_kzalloc(test, sizeof(*fixture->dc_state), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, fixture->dc_state);
+	fixture->tg = kunit_kzalloc(test, sizeof(*fixture->tg), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, fixture->tg);
+	fixture->opp = kunit_kzalloc(test, sizeof(*fixture->opp), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, fixture->opp);
+	fixture->logger = kunit_kzalloc(test, sizeof(*fixture->logger), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, fixture->logger);
+	fixture->link = dm_kunit_alloc_link(test);
+	fixture->stream = dm_kunit_alloc_stream(test, fixture->link);
+
+	mutex_init(&adev->dm.dc_lock);
+	adev->dm.dc = fixture->dc;
+	fixture->dc->ctx = fixture->dc_ctx;
+	fixture->dc->current_state = fixture->dc_state;
+	fixture->dc_ctx->dc = fixture->dc;
+	fixture->dc_ctx->logger = fixture->logger;
+	fixture->link->dc = fixture->dc;
+	fixture->stream->ctx = fixture->dc_ctx;
+	fixture->stream->link = fixture->link;
+	fixture->stream->timing.h_addressable = 1920;
+	fixture->stream->timing.v_addressable = 1080;
+	fixture->configure_crc_return = true;
+	fixture->tg->funcs = &dm_test_tg_funcs;
+	fixture->opp->funcs = &dm_test_opp_funcs;
+	fixture->dm_state->stream = fixture->stream;
+
+	pipe = &fixture->dc_state->res_ctx.pipe_ctx[0];
+	pipe->stream = fixture->stream;
+	pipe->pipe_idx = 0;
+	pipe->stream_res.tg = fixture->tg;
+	pipe->stream_res.opp = fixture->opp;
+
+	return fixture;
+}
+
 static struct amdgpu_crtc *dm_test_alloc_crc_crtc(struct kunit *test,
 							 struct amdgpu_device *adev)
 {
@@ -182,6 +320,203 @@ static void dm_test_crtc_verify_crc_source_invalid(struct kunit *test)
 	KUNIT_EXPECT_EQ(test, values_cnt, 7);
 }
 
+/**
+ * dm_test_crtc_configure_crc_source_no_stream() - Test missing stream handling.
+ * @test: KUnit test context.
+ *
+ * Verifies that configuration is deferred/rejected before any DC access when
+ * the CRTC state does not have a stream.
+ */
+static void dm_test_crtc_configure_crc_source_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);
+
+	ret = amdgpu_dm_crtc_configure_crc_source(&acrtc->base, dm_state,
+						   AMDGPU_DM_PIPE_CRC_SOURCE_CRTC);
+
+	KUNIT_EXPECT_EQ(test, ret, -EINVAL);
+}
+
+/**
+ * dm_test_crtc_configure_crc_source_dprx() - Test DPRX configure path.
+ * @test: KUnit test context.
+ *
+ * Verifies that a DPRX source can be configured with an empty DC resource
+ * state, covering the non-CRTC path that only updates dither/dynamic expansion.
+ */
+static void dm_test_crtc_configure_crc_source_dprx(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_test_crc_dc_fixture *fixture;
+	int ret;
+
+	fixture = dm_test_alloc_crc_dc_fixture(test, adev);
+	dm_test_crc_dc_ctx = fixture;
+
+	ret = amdgpu_dm_crtc_configure_crc_source(&acrtc->base, fixture->dm_state,
+						   AMDGPU_DM_PIPE_CRC_SOURCE_DPRX);
+	dm_test_crc_dc_ctx = NULL;
+
+	KUNIT_EXPECT_EQ(test, ret, 0);
+	KUNIT_EXPECT_FALSE(test, fixture->configure_crc_called);
+	KUNIT_EXPECT_TRUE(test, fixture->dyn_expansion_called);
+	KUNIT_EXPECT_EQ(test, fixture->dyn_expansion, DYN_EXPANSION_DISABLE);
+	KUNIT_EXPECT_TRUE(test, fixture->bit_depth_reduction_called);
+}
+
+/**
+ * dm_test_crtc_configure_crc_source_dprx_dither() - Test DPRX dither path.
+ * @test: KUnit test context.
+ *
+ * Verifies that a DPRX dither source reaches the default dither/dynamic
+ * expansion path without requiring timing-generator callbacks.
+ */
+static void dm_test_crtc_configure_crc_source_dprx_dither(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_test_crc_dc_fixture *fixture;
+	int ret;
+
+	fixture = dm_test_alloc_crc_dc_fixture(test, adev);
+	dm_test_crc_dc_ctx = fixture;
+
+	ret = amdgpu_dm_crtc_configure_crc_source(&acrtc->base, fixture->dm_state,
+						   AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER);
+	dm_test_crc_dc_ctx = NULL;
+
+	KUNIT_EXPECT_EQ(test, ret, 0);
+	KUNIT_EXPECT_FALSE(test, fixture->configure_crc_called);
+	KUNIT_EXPECT_TRUE(test, fixture->dyn_expansion_called);
+	KUNIT_EXPECT_EQ(test, fixture->dyn_expansion, DYN_EXPANSION_AUTO);
+	KUNIT_EXPECT_TRUE(test, fixture->bit_depth_reduction_called);
+}
+
+/**
+ * dm_test_crtc_configure_crc_source_crtc() - Test CRTC enable path.
+ * @test: KUnit test context.
+ *
+ * Verifies that a CRTC source enables DC CRC capture and disables dither.
+ */
+static void dm_test_crtc_configure_crc_source_crtc(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_test_crc_dc_fixture *fixture;
+	int ret;
+
+	fixture = dm_test_alloc_crc_dc_fixture(test, adev);
+	dm_test_crc_dc_ctx = fixture;
+
+	ret = amdgpu_dm_crtc_configure_crc_source(&acrtc->base, fixture->dm_state,
+						   AMDGPU_DM_PIPE_CRC_SOURCE_CRTC);
+	dm_test_crc_dc_ctx = NULL;
+
+	KUNIT_EXPECT_EQ(test, ret, 0);
+	KUNIT_EXPECT_TRUE(test, fixture->configure_crc_called);
+	KUNIT_EXPECT_TRUE(test, fixture->crc_params.enable);
+	KUNIT_EXPECT_TRUE(test, fixture->crc_params.continuous_mode);
+	KUNIT_EXPECT_TRUE(test, fixture->crc_params.reset);
+	KUNIT_EXPECT_EQ(test, fixture->crc_params.windowa_x_end, 1920);
+	KUNIT_EXPECT_EQ(test, fixture->crc_params.windowa_y_end, 1080);
+	KUNIT_EXPECT_TRUE(test, fixture->dyn_expansion_called);
+	KUNIT_EXPECT_EQ(test, fixture->dyn_expansion, DYN_EXPANSION_DISABLE);
+	KUNIT_EXPECT_TRUE(test, fixture->bit_depth_reduction_called);
+}
+
+/**
+ * dm_test_crtc_configure_crc_source_crtc_dcn36_poly() - Test CRC poly select.
+ * @test: KUnit test context.
+ *
+ * Verifies that DCN3.6+ configurations use the CRTC-selected CRC polynomial.
+ */
+static void dm_test_crtc_configure_crc_source_crtc_dcn36_poly(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_test_crc_dc_fixture *fixture;
+	int ret;
+
+	fixture = dm_test_alloc_crc_dc_fixture(test, adev);
+	adev->ip_versions[DCE_HWIP][0] = IP_VERSION(3, 6, 0);
+	acrtc->dm_irq_params.crc_poly_mode = CRC_POLY_MODE_32;
+	dm_test_crc_dc_ctx = fixture;
+
+	ret = amdgpu_dm_crtc_configure_crc_source(&acrtc->base, fixture->dm_state,
+						   AMDGPU_DM_PIPE_CRC_SOURCE_CRTC);
+	dm_test_crc_dc_ctx = NULL;
+
+	KUNIT_EXPECT_EQ(test, ret, 0);
+	KUNIT_EXPECT_TRUE(test, fixture->configure_crc_called);
+	KUNIT_EXPECT_EQ(test, fixture->crc_params.crc_poly_mode, CRC_POLY_MODE_32);
+}
+
+/**
+ * dm_test_crtc_configure_crc_source_crtc_configure_fails() - Test failure path.
+ * @test: KUnit test context.
+ *
+ * Verifies that a DC CRC configuration failure is reported as -EINVAL and
+ * stops before dither/dynamic expansion programming.
+ */
+static void dm_test_crtc_configure_crc_source_crtc_configure_fails(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_test_crc_dc_fixture *fixture;
+	int ret;
+
+	fixture = dm_test_alloc_crc_dc_fixture(test, adev);
+	fixture->configure_crc_return = false;
+	dm_test_crc_dc_ctx = fixture;
+
+	ret = amdgpu_dm_crtc_configure_crc_source(&acrtc->base, fixture->dm_state,
+						   AMDGPU_DM_PIPE_CRC_SOURCE_CRTC);
+	dm_test_crc_dc_ctx = NULL;
+
+	KUNIT_EXPECT_EQ(test, ret, -EINVAL);
+	KUNIT_EXPECT_TRUE(test, fixture->configure_crc_called);
+	KUNIT_EXPECT_FALSE(test, fixture->dyn_expansion_called);
+	KUNIT_EXPECT_FALSE(test, fixture->bit_depth_reduction_called);
+}
+
+/**
+ * dm_test_crtc_configure_crc_source_none() - Test CRC disable path.
+ * @test: KUnit test context.
+ *
+ * Verifies that source NONE disables DC CRC capture and restores default
+ * dither/dynamic expansion.
+ */
+static void dm_test_crtc_configure_crc_source_none(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_test_crc_dc_fixture *fixture;
+	int ret;
+
+	fixture = dm_test_alloc_crc_dc_fixture(test, adev);
+	dm_test_crc_dc_ctx = fixture;
+
+	ret = amdgpu_dm_crtc_configure_crc_source(&acrtc->base, fixture->dm_state,
+						   AMDGPU_DM_PIPE_CRC_SOURCE_NONE);
+	dm_test_crc_dc_ctx = NULL;
+
+	KUNIT_EXPECT_EQ(test, ret, 0);
+	KUNIT_EXPECT_TRUE(test, fixture->configure_crc_called);
+	KUNIT_EXPECT_FALSE(test, fixture->crc_params.enable);
+	KUNIT_EXPECT_FALSE(test, fixture->crc_params.continuous_mode);
+	KUNIT_EXPECT_TRUE(test, fixture->crc_params.reset);
+	KUNIT_EXPECT_TRUE(test, fixture->dyn_expansion_called);
+	KUNIT_EXPECT_EQ(test, fixture->dyn_expansion, DYN_EXPANSION_AUTO);
+	KUNIT_EXPECT_TRUE(test, fixture->bit_depth_reduction_called);
+}
+
 /**
  * dm_test_need_dp_aux() - Test dm_need_dp_aux().
  * @test: KUnit test context.
@@ -314,6 +649,14 @@ static struct kunit_case dm_crc_test_cases[] = {
 	/* amdgpu_dm_crtc_verify_crc_source() */
 	KUNIT_CASE(dm_test_crtc_verify_crc_source_valid),
 	KUNIT_CASE(dm_test_crtc_verify_crc_source_invalid),
+	/* amdgpu_dm_crtc_configure_crc_source() */
+	KUNIT_CASE(dm_test_crtc_configure_crc_source_no_stream),
+	KUNIT_CASE(dm_test_crtc_configure_crc_source_dprx),
+	KUNIT_CASE(dm_test_crtc_configure_crc_source_dprx_dither),
+	KUNIT_CASE(dm_test_crtc_configure_crc_source_crtc),
+	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),
 	/* 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.