drm/amd/display: add CRC IRQ handler KUnit coverage

[WHAT]
Expose amdgpu_dm_crtc_handle_crc_irq() for KUnit and add tests for the
incomplete-CRTC early returns, the disabled-source exit, the initial
two-frame skip window, the DPRX post-skip exit, and the failed
dc_stream_get_crc() path that stops before delivering a DRM CRC entry.

These tests reuse the fake DC fixture introduced with the CRC configure
coverage.

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:07:21 -06:00 committed by Alex Deucher
parent f22a403ff5
commit 1cc234930e
2 changed files with 133 additions and 0 deletions

View File

@ -874,6 +874,7 @@ void amdgpu_dm_crtc_handle_crc_irq(struct drm_crtc *crtc)
drm_crtc_accurate_vblank_count(crtc), crcs);
}
}
EXPORT_IF_KUNIT(amdgpu_dm_crtc_handle_crc_irq);
#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
void amdgpu_dm_crtc_handle_crc_window_irq(struct drm_crtc *crtc)

View File

@ -673,6 +673,132 @@ static void dm_test_crtc_set_crc_source_dprx_no_connector(struct kunit *test)
AMDGPU_DM_PIPE_CRC_SOURCE_NONE);
}
/**
* dm_test_crtc_handle_crc_irq_early_returns() - Test null/missing state exits.
* @test: KUnit test context.
*
* Verifies that the CRC IRQ handler safely ignores incomplete CRTC objects.
*/
static void dm_test_crtc_handle_crc_irq_early_returns(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;
amdgpu_dm_crtc_handle_crc_irq(NULL);
amdgpu_dm_crtc_handle_crc_irq(&acrtc->base);
dm_state = kunit_kzalloc(test, sizeof(*dm_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, dm_state);
acrtc->base.state = &dm_state->base;
amdgpu_dm_crtc_handle_crc_irq(&acrtc->base);
KUNIT_EXPECT_EQ(test, dm_state->crc_skip_count, 0);
}
/**
* dm_test_crtc_handle_crc_irq_disabled_source() - Test disabled source exit.
* @test: KUnit test context.
*
* Verifies that a present stream does not advance the skip counter when CRC
* capture is disabled.
*/
static void dm_test_crtc_handle_crc_irq_disabled_source(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;
dm_state = kunit_kzalloc(test, sizeof(*dm_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, dm_state);
dm_state->stream = dm_kunit_alloc_stream(test, NULL);
acrtc->base.state = &dm_state->base;
acrtc->dm_irq_params.crc_src = AMDGPU_DM_PIPE_CRC_SOURCE_NONE;
amdgpu_dm_crtc_handle_crc_irq(&acrtc->base);
KUNIT_EXPECT_EQ(test, dm_state->crc_skip_count, 0);
}
/**
* dm_test_crtc_handle_crc_irq_skips_initial_frames() - Test initial skip logic.
* @test: KUnit test context.
*
* Verifies that the first two enabled CRC IRQs only increment crc_skip_count,
* avoiding the later DC CRC read path.
*/
static void dm_test_crtc_handle_crc_irq_skips_initial_frames(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;
dm_state = kunit_kzalloc(test, sizeof(*dm_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, dm_state);
dm_state->stream = dm_kunit_alloc_stream(test, NULL);
acrtc->base.state = &dm_state->base;
acrtc->dm_irq_params.crc_src = AMDGPU_DM_PIPE_CRC_SOURCE_CRTC;
amdgpu_dm_crtc_handle_crc_irq(&acrtc->base);
KUNIT_EXPECT_EQ(test, dm_state->crc_skip_count, 1);
amdgpu_dm_crtc_handle_crc_irq(&acrtc->base);
KUNIT_EXPECT_EQ(test, dm_state->crc_skip_count, 2);
}
/**
* dm_test_crtc_handle_crc_irq_dprx_after_skip() - Test DPRX post-skip exit.
* @test: KUnit test context.
*
* Verifies that enabled non-CRTC CRC sources do not call into DC CRC reads
* after the initial skip window.
*/
static void dm_test_crtc_handle_crc_irq_dprx_after_skip(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;
fixture = dm_test_alloc_crc_dc_fixture(test, adev);
fixture->dm_state->crc_skip_count = 2;
acrtc->base.state = &fixture->dm_state->base;
acrtc->dm_irq_params.crc_src = AMDGPU_DM_PIPE_CRC_SOURCE_DPRX;
dm_test_crc_dc_ctx = fixture;
amdgpu_dm_crtc_handle_crc_irq(&acrtc->base);
dm_test_crc_dc_ctx = NULL;
KUNIT_EXPECT_FALSE(test, fixture->get_crc_called);
KUNIT_EXPECT_EQ(test, fixture->dm_state->crc_skip_count, 2);
}
/**
* dm_test_crtc_handle_crc_irq_get_crc_fails() - Test failed DC CRC read.
* @test: KUnit test context.
*
* Verifies that the IRQ handler exits after dc_stream_get_crc() returns false,
* before attempting to deliver a DRM CRC entry.
*/
static void dm_test_crtc_handle_crc_irq_get_crc_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;
fixture = dm_test_alloc_crc_dc_fixture(test, adev);
fixture->dm_state->crc_skip_count = 2;
fixture->get_crc_return = false;
acrtc->base.state = &fixture->dm_state->base;
acrtc->dm_irq_params.crc_src = AMDGPU_DM_PIPE_CRC_SOURCE_CRTC;
dm_test_crc_dc_ctx = fixture;
amdgpu_dm_crtc_handle_crc_irq(&acrtc->base);
dm_test_crc_dc_ctx = NULL;
KUNIT_EXPECT_TRUE(test, fixture->get_crc_called);
KUNIT_EXPECT_EQ(test, fixture->dm_state->crc_skip_count, 2);
}
/**
* dm_test_need_dp_aux() - Test dm_need_dp_aux().
* @test: KUnit test context.
@ -818,6 +944,12 @@ static struct kunit_case dm_crc_test_cases[] = {
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),
/* amdgpu_dm_crtc_handle_crc_irq() */
KUNIT_CASE(dm_test_crtc_handle_crc_irq_early_returns),
KUNIT_CASE(dm_test_crtc_handle_crc_irq_disabled_source),
KUNIT_CASE(dm_test_crtc_handle_crc_irq_skips_initial_frames),
KUNIT_CASE(dm_test_crtc_handle_crc_irq_dprx_after_skip),
KUNIT_CASE(dm_test_crtc_handle_crc_irq_get_crc_fails),
/* dm_need_dp_aux() */
KUNIT_CASE(dm_test_need_dp_aux),
/* dm_crc_source_should_start_dprx() */