drm/amd/display: add CRC configure KUnit coverage

[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 <bhawanpreet.lakha@amd.com>
Signed-off-by: Alex Hung <alex.hung@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
Tested-by: Dan Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
Alex Hung 2026-07-06 21:03:00 -06:00 committed by Alex Deucher
parent e714db7ff3
commit e03f5358ad
2 changed files with 344 additions and 0 deletions

View File

@ -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)
{

View File

@ -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() */