[PATCH 33/59] drm/amd/display: Test vblank event arming

Alex Hung <[email protected]>
Newsgroups org.freedesktop.lists.amd-gfx
Message-ID <[email protected]>
[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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.