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
