From: Alex Hung <[email protected]>

[WHAT]
Add KUnit tests for the two writeback vblanks in dm_crtc_high_irq(): the
first disables frame capture and marks wb_frame_done, the second signals
the out fence and clears the pending state.

[HOW]
dc_stream_fc_disable_writeback() dereferences dc->res_pool before its
NULL-stream check, so supply a dc with a fake IRQ service. The
completion test queues a bare drm_writeback_job and holds a vblank
reference for the completion's drm_crtc_vblank_put().

Assisted-by: Copilot:Claude-Opus-5
Reviewed-by: Bhawanpreet Lakha <[email protected]>
Signed-off-by: Alex Hung <[email protected]>
Signed-off-by: Ivan Lipski <[email protected]>
---
 .../amdgpu_dm/tests/amdgpu_dm_irq_test.c      | 97 +++++++++++++++++++
 1 file changed, 97 insertions(+)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_irq_test.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_irq_test.c
index 0bf46e856f5a2..4cb52093987f3 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_irq_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_irq_test.c
@@ -10,6 +10,7 @@
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_vblank.h>
+#include <drm/drm_writeback.h>
 
 #include "dc.h"
 #include "inc/core_types.h"
@@ -3524,6 +3525,100 @@ static void 
dm_test_crtc_high_irq_vrr_ai_no_stream(struct kunit *test)
        KUNIT_EXPECT_EQ(test, acrtc->pflip_status, AMDGPU_FLIP_NONE);
 }
 
+/**
+ * dm_test_crtc_high_irq_writeback_disables - Test first writeback vblank
+ * @test: The KUnit test context
+ *
+ * The first vblank after a writeback is armed must disable frame capture so 
the
+ * hardware cannot overwrite the buffer, and defer signalling completion to the
+ * next vblank by marking wb_frame_done.
+ */
+static void dm_test_crtc_high_irq_writeback_disables(struct kunit *test)
+{
+       struct drm_writeback_connector *wb_conn;
+       struct common_irq_params params = { 0 };
+       struct amdgpu_device *adev;
+       struct amdgpu_crtc *acrtc;
+       struct dc *dc;
+
+       adev = dm_kunit_alloc_adev(test);
+       KUNIT_ASSERT_EQ(test, drm_vblank_init(&adev->ddev, 1), 0);
+
+       /* dc_stream_fc_disable_writeback() dereferences dc->res_pool. */
+       dc = dm_test_alloc_dc_with_irq_service(test,
+                                              
&dm_test_irq_service_funcs_dcn10);
+       adev->dm.dc = dc;
+       /* Pre-AI family returns right after CRC handling. */
+       adev->family = AMDGPU_FAMILY_SI;
+
+       acrtc = dm_test_add_crtc(test, adev);
+
+       wb_conn = kunit_kzalloc(test, sizeof(*wb_conn), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, wb_conn);
+       acrtc->wb_conn = wb_conn;
+       acrtc->wb_pending = true;
+
+       params.adev = adev;
+       params.irq_src = (enum dc_irq_source)IRQ_TYPE_VBLANK;
+
+       dm_crtc_high_irq(&params);
+
+       KUNIT_EXPECT_TRUE(test, acrtc->wb_frame_done);
+       KUNIT_EXPECT_TRUE(test, acrtc->wb_pending);
+}
+
+/**
+ * dm_test_crtc_high_irq_writeback_completes - Test second writeback vblank
+ * @test: The KUnit test context
+ *
+ * Once wb_frame_done is set the DMA has had a full frame period to flush, so
+ * the next vblank must signal the writeback out fence and clear the pending
+ * state.
+ */
+static void dm_test_crtc_high_irq_writeback_completes(struct kunit *test)
+{
+       struct drm_writeback_connector *wb_conn;
+       struct common_irq_params params = { 0 };
+       struct drm_writeback_job *job;
+       struct amdgpu_device *adev;
+       struct amdgpu_crtc *acrtc;
+
+       adev = dm_kunit_alloc_adev(test);
+       KUNIT_ASSERT_EQ(test, drm_vblank_init(&adev->ddev, 1), 0);
+       adev->family = AMDGPU_FAMILY_SI;
+
+       acrtc = dm_test_add_crtc(test, adev);
+
+       wb_conn = kunit_kzalloc(test, sizeof(*wb_conn), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, wb_conn);
+       spin_lock_init(&wb_conn->job_lock);
+       INIT_LIST_HEAD(&wb_conn->job_queue);
+
+       /*
+        * The job carries no framebuffer or fence, so the deferred DRM cleanup
+        * work only kfree()s it; it needs nothing from this test.
+        */
+       job = kzalloc_obj(*job, GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, job);
+       list_add_tail(&job->list_entry, &wb_conn->job_queue);
+
+       acrtc->wb_conn = wb_conn;
+       acrtc->wb_pending = true;
+       acrtc->wb_frame_done = true;
+
+       /* Balance the drm_crtc_vblank_put() done on completion. */
+       adev->ddev.vblank[0].enabled = true;
+       KUNIT_ASSERT_EQ(test, drm_crtc_vblank_get(&acrtc->base), 0);
+
+       params.adev = adev;
+       params.irq_src = (enum dc_irq_source)IRQ_TYPE_VBLANK;
+
+       dm_crtc_high_irq(&params);
+
+       KUNIT_EXPECT_FALSE(test, acrtc->wb_pending);
+       KUNIT_EXPECT_FALSE(test, acrtc->wb_frame_done);
+}
+
 /* Tests for dm_handle_hpd_work() */
 
 /**
@@ -4154,6 +4249,8 @@ static struct kunit_case amdgpu_dm_irq_tests[] = {
        KUNIT_CASE(dm_test_crtc_high_irq_no_crtc),
        KUNIT_CASE(dm_test_crtc_high_irq_vrr_pre_ai),
        KUNIT_CASE(dm_test_crtc_high_irq_vrr_ai_no_stream),
+       KUNIT_CASE(dm_test_crtc_high_irq_writeback_disables),
+       KUNIT_CASE(dm_test_crtc_high_irq_writeback_completes),
        /* dm_handle_hpd_work */
        KUNIT_CASE(dm_test_handle_hpd_work_out_of_range),
        /* dm_dmub_outbox1_low_irq */
-- 
2.43.0

Reply via email to