[WHAT]
Add KUnit tests for dm_arm_vblank_event() and
dm_arm_vblank_event_pre_programming() covering missing events, inactive
CRTCs, page-flip and cursor updates, and vblank reference acquisition.

[HOW]
Use one pending-event fixture. Initialize DRM vblank state only for the
pre-programming tests and release every acquired reference.

Assisted-by: Copilot:Claude-Opus-5 GPT-5.6-Sol
Reviewed-by: Bhawanpreet Lakha <[email protected]>
Signed-off-by: Alex Hung <[email protected]>
---
 .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c |  18 +-
 .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h |   6 +
 .../display/amdgpu_dm/tests/amdgpu_dm_test.c  | 185 ++++++++++++++++++
 3 files changed, 201 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 08f2cc7fec68..1eb2f0973aac 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -3723,10 +3723,10 @@ static void amdgpu_dm_enable_self_refresh(struct 
amdgpu_display_manager *dm,
        }
 }
 
-static void dm_arm_vblank_event(struct amdgpu_crtc *acrtc,
-                               struct dm_crtc_state *acrtc_state,
-                               bool pflip_update,
-                               bool cursor_update)
+STATIC_IFN_KUNIT void dm_arm_vblank_event(struct amdgpu_crtc *acrtc,
+                                         struct dm_crtc_state *acrtc_state,
+                                         bool pflip_update,
+                                         bool cursor_update)
 {
        assert_spin_locked(&acrtc->base.dev->event_lock);
 
@@ -3749,6 +3749,7 @@ static void dm_arm_vblank_event(struct amdgpu_crtc *acrtc,
                acrtc->base.state->event = NULL;
        }
 }
+EXPORT_IF_KUNIT(dm_arm_vblank_event);
 
 /**
  * dm_arm_vblank_event_pre_programming - Prepare for programming
@@ -3761,10 +3762,10 @@ static void dm_arm_vblank_event(struct amdgpu_crtc 
*acrtc,
  * be programmed. Do this before programming so the HW is not in any
  * idle-optimized state (such as PSR).
  */
-static void dm_arm_vblank_event_pre_programming(struct amdgpu_crtc *acrtc,
-                                               struct dm_crtc_state 
*acrtc_state,
-                                               bool pflip_update,
-                                               bool cursor_update)
+STATIC_IFN_KUNIT void dm_arm_vblank_event_pre_programming(struct amdgpu_crtc 
*acrtc,
+                                                         struct dm_crtc_state 
*acrtc_state,
+                                                         bool pflip_update,
+                                                         bool cursor_update)
 {
        assert_spin_locked(&acrtc->base.dev->event_lock);
 
@@ -3774,6 +3775,7 @@ static void dm_arm_vblank_event_pre_programming(struct 
amdgpu_crtc *acrtc,
        if (pflip_update || cursor_update)
                drm_crtc_vblank_get(&acrtc->base);
 }
+EXPORT_IF_KUNIT(dm_arm_vblank_event_pre_programming);
 
 static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state,
                                    struct drm_device *dev,
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
index ed2341b387d9..e4b67a0b98c3 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
@@ -1176,6 +1176,12 @@ void amdgpu_dm_commit_cursors(struct drm_atomic_commit 
*state);
 void amdgpu_dm_update_cursor(struct drm_plane *plane,
                             struct drm_plane_state *old_plane_state,
                             struct dc_stream_update *update);
+void dm_arm_vblank_event(struct amdgpu_crtc *acrtc,
+                        struct dm_crtc_state *acrtc_state,
+                        bool pflip_update, bool cursor_update);
+void dm_arm_vblank_event_pre_programming(struct amdgpu_crtc *acrtc,
+                                        struct dm_crtc_state *acrtc_state,
+                                        bool pflip_update, bool cursor_update);
 int add_affected_mst_dsc_crtcs(struct drm_atomic_commit *state,
                               struct drm_crtc *crtc);
 int dm_plane_layer_index_cmp(const void *a, const void *b);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c
index a2671ad90bfe..648db4a58509 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c
@@ -1552,6 +1552,183 @@ static void 
dm_test_update_cursor_disables_stream(struct kunit *test)
        KUNIT_EXPECT_NULL(test, update->cursor_attributes);
 }
 
+/* Tests for dm_arm_vblank_event() */
+
+struct dm_test_vblank_ctx {
+       struct amdgpu_device *adev;
+       struct amdgpu_crtc *acrtc;
+       struct dm_crtc_state *acrtc_state;
+       struct drm_pending_vblank_event *event;
+};
+
+/*
+ * A CRTC with one active plane and a pending vblank event. There is no
+ * initialised vblank, so drm_crtc_vblank_get() fails, which the function under
+ * test ignores.
+ */
+static struct dm_test_vblank_ctx *dm_test_vblank_ctx_alloc(struct kunit *test)
+{
+       struct dm_test_vblank_ctx *ctx;
+
+       ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+       ctx->adev = dm_kunit_alloc_adev(test);
+       ctx->acrtc = kunit_kzalloc(test, sizeof(*ctx->acrtc), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, ctx->acrtc);
+       ctx->acrtc_state = kunit_kzalloc(test, sizeof(*ctx->acrtc_state), 
GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, ctx->acrtc_state);
+       ctx->event = kunit_kzalloc(test, sizeof(*ctx->event), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, ctx->event);
+
+       ctx->acrtc->base.dev = &ctx->adev->ddev;
+       ctx->acrtc->base.state = &ctx->acrtc_state->base;
+       ctx->acrtc_state->base.event = ctx->event;
+       ctx->acrtc_state->active_planes = 1;
+
+       return ctx;
+}
+
+static void dm_test_arm_vblank(struct dm_test_vblank_ctx *ctx, bool 
pflip_update,
+                              bool cursor_update)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&ctx->adev->ddev.event_lock, flags);
+       dm_arm_vblank_event(ctx->acrtc, ctx->acrtc_state, pflip_update,
+                           cursor_update);
+       spin_unlock_irqrestore(&ctx->adev->ddev.event_lock, flags);
+}
+
+static void dm_test_arm_vblank_pre_programming(struct dm_test_vblank_ctx *ctx,
+                                              bool pflip_update, bool 
cursor_update)
+{
+       struct dm_crtc_state *state = ctx->acrtc_state;
+       struct amdgpu_crtc *acrtc = ctx->acrtc;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ctx->adev->ddev.event_lock, flags);
+       dm_arm_vblank_event_pre_programming(acrtc, state, pflip_update, 
cursor_update);
+       spin_unlock_irqrestore(&ctx->adev->ddev.event_lock, flags);
+}
+
+/**
+ * dm_test_arm_vblank_pre_programming_no_event - Test missing event takes no 
reference
+ * @test: The KUnit test context
+ */
+static void dm_test_arm_vblank_pre_programming_no_event(struct kunit *test)
+{
+       struct dm_test_vblank_ctx *ctx = dm_test_vblank_ctx_alloc(test);
+       struct drm_vblank_crtc *vblank;
+
+       KUNIT_ASSERT_EQ(test, drm_vblank_init(&ctx->adev->ddev, 1), 0);
+       vblank = drm_crtc_vblank_crtc(&ctx->acrtc->base);
+       ctx->acrtc_state->base.event = NULL;
+
+       dm_test_arm_vblank_pre_programming(ctx, true, false);
+
+       KUNIT_EXPECT_EQ(test, atomic_read(&vblank->refcount), 0);
+}
+
+/**
+ * dm_test_arm_vblank_pre_programming_no_planes - Test inactive CRTC takes no 
reference
+ * @test: The KUnit test context
+ */
+static void dm_test_arm_vblank_pre_programming_no_planes(struct kunit *test)
+{
+       struct dm_test_vblank_ctx *ctx = dm_test_vblank_ctx_alloc(test);
+       struct drm_vblank_crtc *vblank;
+
+       KUNIT_ASSERT_EQ(test, drm_vblank_init(&ctx->adev->ddev, 1), 0);
+       vblank = drm_crtc_vblank_crtc(&ctx->acrtc->base);
+       ctx->acrtc_state->active_planes = 0;
+
+       dm_test_arm_vblank_pre_programming(ctx, false, true);
+
+       KUNIT_EXPECT_EQ(test, atomic_read(&vblank->refcount), 0);
+}
+
+/**
+ * dm_test_arm_vblank_pre_programming_update - Test an update takes a vblank 
reference
+ * @test: The KUnit test context
+ */
+static void dm_test_arm_vblank_pre_programming_update(struct kunit *test)
+{
+       struct dm_test_vblank_ctx *ctx = dm_test_vblank_ctx_alloc(test);
+       struct drm_vblank_crtc *vblank;
+
+       KUNIT_ASSERT_EQ(test, drm_vblank_init(&ctx->adev->ddev, 1), 0);
+       vblank = drm_crtc_vblank_crtc(&ctx->acrtc->base);
+
+       dm_test_arm_vblank_pre_programming(ctx, true, false);
+
+       KUNIT_EXPECT_EQ(test, atomic_read(&vblank->refcount), 1);
+       drm_crtc_vblank_put(&ctx->acrtc->base);
+}
+
+/**
+ * dm_test_arm_vblank_event_no_event - Test a commit without an event is a 
no-op
+ * @test: The KUnit test context
+ */
+static void dm_test_arm_vblank_event_no_event(struct kunit *test)
+{
+       struct dm_test_vblank_ctx *ctx = dm_test_vblank_ctx_alloc(test);
+
+       ctx->acrtc_state->base.event = NULL;
+
+       dm_test_arm_vblank(ctx, true, false);
+
+       KUNIT_EXPECT_NULL(test, ctx->acrtc->event);
+}
+
+/**
+ * dm_test_arm_vblank_event_no_active_planes - Test an event is left armed 
without planes
+ * @test: The KUnit test context
+ */
+static void dm_test_arm_vblank_event_no_active_planes(struct kunit *test)
+{
+       struct dm_test_vblank_ctx *ctx = dm_test_vblank_ctx_alloc(test);
+
+       ctx->acrtc_state->active_planes = 0;
+
+       dm_test_arm_vblank(ctx, false, true);
+
+       KUNIT_EXPECT_NULL(test, ctx->acrtc->event);
+       KUNIT_EXPECT_PTR_EQ(test, ctx->acrtc_state->base.event, ctx->event);
+}
+
+/**
+ * dm_test_arm_vblank_event_pflip - Test a page flip arms the flip ISR
+ * @test: The KUnit test context
+ */
+static void dm_test_arm_vblank_event_pflip(struct kunit *test)
+{
+       struct dm_test_vblank_ctx *ctx = dm_test_vblank_ctx_alloc(test);
+
+       dm_test_arm_vblank(ctx, true, false);
+
+       KUNIT_EXPECT_PTR_EQ(test, ctx->acrtc->event, ctx->event);
+       KUNIT_EXPECT_NULL(test, ctx->acrtc_state->base.event);
+       KUNIT_EXPECT_EQ(test, (int)ctx->acrtc->pflip_status,
+                       (int)AMDGPU_FLIP_SUBMITTED);
+}
+
+/**
+ * dm_test_arm_vblank_event_cursor - Test a cursor update consumes the event
+ * @test: The KUnit test context
+ */
+static void dm_test_arm_vblank_event_cursor(struct kunit *test)
+{
+       struct dm_test_vblank_ctx *ctx = dm_test_vblank_ctx_alloc(test);
+
+       dm_test_arm_vblank(ctx, false, true);
+
+       KUNIT_EXPECT_PTR_EQ(test, ctx->acrtc->event, ctx->event);
+       KUNIT_EXPECT_NULL(test, ctx->acrtc_state->base.event);
+       KUNIT_EXPECT_EQ(test, (int)ctx->acrtc->pflip_status,
+                       (int)AMDGPU_FLIP_NONE);
+}
+
 static struct kunit_case amdgpu_dm_tests[] = {
        /* Simple DM callbacks */
        KUNIT_CASE(dm_test_wait_for_idle),
@@ -1637,6 +1814,14 @@ static struct kunit_case amdgpu_dm_tests[] = {
        KUNIT_CASE(dm_test_commit_cursors_updates_cursor),
        KUNIT_CASE(dm_test_update_cursor_no_framebuffer),
        KUNIT_CASE(dm_test_update_cursor_disables_stream),
+       /* dm_arm_vblank_event */
+       KUNIT_CASE(dm_test_arm_vblank_event_no_event),
+       KUNIT_CASE(dm_test_arm_vblank_event_no_active_planes),
+       KUNIT_CASE(dm_test_arm_vblank_event_pflip),
+       KUNIT_CASE(dm_test_arm_vblank_event_cursor),
+       KUNIT_CASE(dm_test_arm_vblank_pre_programming_no_event),
+       KUNIT_CASE(dm_test_arm_vblank_pre_programming_no_planes),
+       KUNIT_CASE(dm_test_arm_vblank_pre_programming_update),
        {}
 };
 
-- 
2.43.0

Reply via email to