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

Reply via email to