summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBhawanpreet Lakha <bhawanpreet.lakha@amd.com>2026-07-21 15:19:29 -0400
committerAlex Deucher <alexander.deucher@amd.com>2026-08-06 12:43:08 -0400
commitfd942d65f57185740208cf75bfc7be75bce4606e (patch)
tree7e490bc3d46e79801fcf23ee100e2008452bb361
parenta7cd055d44dc57ea37ce43f27d60b031b3018d26 (diff)
downloadlinux-next-fd942d65f57185740208cf75bfc7be75bce4606e.tar.gz
linux-next-fd942d65f57185740208cf75bfc7be75bce4606e.zip
drm/amd/display: Cover crtc set_vblank workqueue branch
Add dm_test_crtc_enable_vblank_queues_work and dm_test_crtc_disable_vblank_queues_work to cover the vblank_control_workqueue branch of amdgpu_dm_crtc_set_vblank(): - The enable test installs a real workqueue, retains the stream and queues the control worker, then drains it and checks the active vblank IRQ count was incremented. - The disable test drives the no-stream sub-branch (the stream-retain is skipped) and checks the worker decremented the count. Both seed the ISM so the queued worker takes no state-machine transition, keeping coverage on the vblank accounting; the ISM state machine itself is covered by the ISM tests. Assisted-by: Copilot:Claude-Opus-4.8 Reviewed-by: Alex Hung <alex.hung@amd.com> Signed-off-by: Bhawanpreet Lakha <bhawanpreet.lakha@amd.com> Signed-off-by: Roman Li <roman.li@amd.com> Tested-by: Dan Wheeler <daniel.wheeler@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crtc_test.c115
1 files changed, 115 insertions, 0 deletions
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crtc_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crtc_test.c
index 753150943fba..74d72289dff6 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crtc_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crtc_test.c
@@ -1063,6 +1063,50 @@ static void dm_test_crtc_enable_vblank_in_reset(struct kunit *test)
KUNIT_EXPECT_EQ(test, amdgpu_dm_crtc_enable_vblank(&acrtc->base), 0);
}
+/**
+ * dm_test_crtc_enable_vblank_queues_work - Test enable queues vblank control work
+ * @test: The KUnit test context
+ *
+ * With a vblank control workqueue installed, the enable path allocates a work
+ * item, retains the stream and queues the control worker. Draining the queue
+ * runs the worker, which bumps the active vblank IRQ count. The initial ISM
+ * state has no EXIT_IDLE_REQUESTED transition, so the worker only exercises the
+ * vblank accounting (the ISM state machine is covered by its own tests).
+ */
+static void dm_test_crtc_enable_vblank_queues_work(struct kunit *test)
+{
+ struct dm_crtc_state *acrtc_state;
+ struct amdgpu_device *adev;
+ struct amdgpu_crtc *acrtc;
+
+ /* DCE_VERSION_8_0 supports VRR -> the vupdate-irq branch is walked. */
+ acrtc = dm_test_crtc_setup_enable(test, &adev, DCE_VERSION_8_0);
+
+ /* OTG unassigned -> amdgpu_dm_crtc_set_vupdate_irq() returns 0 early. */
+ acrtc->otg_inst = -1;
+ acrtc_state = to_dm_crtc_state(acrtc->base.state);
+ acrtc_state->freesync_config.state = VRR_STATE_ACTIVE_VARIABLE;
+
+ adev->irq.installed = true;
+ dm_test_crtc_arm_irq_src(test, &adev->crtc_irq, 1);
+ dm_test_crtc_arm_irq_src(test, &adev->pageflip_irq, 1);
+
+ /* Real workqueue so the queue_work() branch runs the control worker. */
+ mutex_init(&adev->dm.dc_lock);
+ adev->dm.vblank_control_workqueue =
+ create_singlethread_workqueue("dm_test_vblank");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev->dm.vblank_control_workqueue);
+
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_crtc_enable_vblank(&acrtc->base), 0);
+
+ /* Drain the queued worker before the fixture is torn down, then tidy up. */
+ destroy_workqueue(adev->dm.vblank_control_workqueue);
+ adev->dm.vblank_control_workqueue = NULL;
+
+ /* The queued worker ran and accounted the active vblank IRQ. */
+ KUNIT_EXPECT_EQ(test, adev->dm.active_vblank_irq_count, 1);
+}
+
/* Tests for amdgpu_dm_crtc_update_crtc_active_planes() */
/**
@@ -1585,6 +1629,75 @@ static void dm_test_crtc_disable_vblank_vrr(struct kunit *test)
KUNIT_EXPECT_EQ(test, atomic_read(&adev->pageflip_irq.enabled_types[0]), 1);
}
+/**
+ * dm_test_crtc_disable_vblank_queues_work - Test disable queues work without a stream
+ * @test: The KUnit test context
+ *
+ * With a vblank control workqueue installed and a CRTC state carrying no
+ * stream, the disable path queues the control worker without retaining a
+ * stream. Draining the queue runs the worker, which drops the active vblank IRQ
+ * count. The ISM is seeded in a state with no ENTER_IDLE_REQUESTED transition
+ * so the worker only exercises the vblank accounting.
+ */
+static void dm_test_crtc_disable_vblank_queues_work(struct kunit *test)
+{
+ struct amdgpu_reset_domain *reset_domain;
+ struct dm_crtc_state *dm_state;
+ struct amdgpu_device *adev;
+ struct amdgpu_crtc *acrtc;
+
+ adev = dm_kunit_alloc_adev(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ adev->dm.dc = dm_kunit_alloc_dc_with_ctx(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev->dm.dc);
+ /* DCE_VERSION_6_0 has no VRR, so the vupdate-irq branch is skipped. */
+ adev->dm.dc->ctx->dce_version = DCE_VERSION_6_0;
+ adev->dm.active_vblank_irq_count = 2;
+
+ adev->mode_info.num_crtc = 1;
+ adev->irq.installed = true;
+
+ reset_domain = kunit_kzalloc(test, sizeof(*reset_domain), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, reset_domain);
+ adev->reset_domain = reset_domain;
+
+ /* Seed with 2 so amdgpu_irq_put() drops to a non-zero refcount. */
+ dm_test_crtc_arm_irq_src(test, &adev->crtc_irq, 2);
+ dm_test_crtc_arm_irq_src(test, &adev->pageflip_irq, 2);
+
+ acrtc = kunit_kzalloc(test, sizeof(*acrtc), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acrtc);
+ acrtc->base.dev = &adev->ddev;
+ acrtc->crtc_id = 0;
+ acrtc->otg_inst = -1;
+ /*
+ * Seed the ISM in a state where ENTER_IDLE_REQUESTED does not transition
+ * so the worker skips the ISM power-state dispatch and its timers.
+ */
+ acrtc->ism.current_state = DM_ISM_STATE_HYSTERESIS_WAITING;
+
+ /* CRTC state with no stream -> the stream-retain branch is skipped. */
+ dm_state = kunit_kzalloc(test, sizeof(*dm_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dm_state);
+ acrtc->base.state = &dm_state->base;
+
+ /* Real workqueue so the queue_work() branch runs the control worker. */
+ mutex_init(&adev->dm.dc_lock);
+ adev->dm.vblank_control_workqueue =
+ create_singlethread_workqueue("dm_test_vblank");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev->dm.vblank_control_workqueue);
+
+ amdgpu_dm_crtc_disable_vblank(&acrtc->base);
+
+ /* Drain the queued worker before the fixture is torn down, then tidy up. */
+ destroy_workqueue(adev->dm.vblank_control_workqueue);
+ adev->dm.vblank_control_workqueue = NULL;
+
+ /* The queued worker ran and decremented the active vblank IRQ count. */
+ KUNIT_EXPECT_EQ(test, adev->dm.active_vblank_irq_count, 1);
+}
+
static struct kunit_case amdgpu_dm_crtc_tests[] = {
/* amdgpu_dm_crtc_modeset_required */
KUNIT_CASE(dm_test_crtc_modeset_required_active_mode_changed),
@@ -1634,6 +1747,7 @@ static struct kunit_case amdgpu_dm_crtc_tests[] = {
KUNIT_CASE(dm_test_crtc_enable_vblank_vupdate_busy),
KUNIT_CASE(dm_test_crtc_enable_vblank_crtc_irq_error),
KUNIT_CASE(dm_test_crtc_enable_vblank_in_reset),
+ KUNIT_CASE(dm_test_crtc_enable_vblank_queues_work),
/* amdgpu_dm_crtc_update_crtc_active_planes */
KUNIT_CASE(dm_test_crtc_update_active_planes_no_stream),
/* amdgpu_dm_crtc_count_crtc_active_planes */
@@ -1656,6 +1770,7 @@ static struct kunit_case amdgpu_dm_crtc_tests[] = {
/* amdgpu_dm_crtc_disable_vblank */
KUNIT_CASE(dm_test_crtc_disable_vblank_no_irq_installed),
KUNIT_CASE(dm_test_crtc_disable_vblank_vrr),
+ KUNIT_CASE(dm_test_crtc_disable_vblank_queues_work),
{}
};