From: Alex Hung <[email protected]>

[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 <[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      | 132 ++++++++++++++++++
 2 files changed, 133 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 0e896aab098a..078d2b589ec4 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
@@ -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)
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 96bdb83317ec..29a513dab9cd 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
@@ -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() */
-- 
2.43.0

Reply via email to