From: Alex Hung <[email protected]> [WHAT] Add KUnit tests for schedule_dc_vmin_vmax(), which had no coverage. Both callers are exercised: dm_crtc_high_irq() on an AI+ family and the DCE path of dm_vupdate_high_irq() on a pre-DCE12 family.
[HOW] Add a dm_test_setup_vmin_vmax_crtc() fixture building a CRTC with an active VRR stream and a vmin/vmax work queue. Each test flushes the queue and checks the worker applied the adjust and dropped its stream reference. 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 | 114 ++++++++++++++++++ 1 file changed, 114 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 4cb52093987f3..b398be878c932 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 @@ -3450,6 +3450,83 @@ static void dm_test_vupdate_high_irq_dcn_no_active_planes(struct kunit *test) KUNIT_EXPECT_EQ(test, acrtc->pflip_status, AMDGPU_FLIP_NONE); } +/* + * Build a CRTC with an active VRR stream on a device whose vmin/vmax updates + * are offloaded to adev->dm.vmin_vmax_wq, seeded with the given timing adjust. + * Caller sets adev->family to select the DCE or DCN path, then invokes the + * handler and flushes the queue. + */ +static struct amdgpu_crtc *dm_test_setup_vmin_vmax_crtc(struct kunit *test, + unsigned int v_total_min, + unsigned int v_total_max) +{ + struct dc_stream_state *stream; + struct amdgpu_device *adev; + struct amdgpu_crtc *acrtc; + struct dc_link *link; + struct dc *dc; + + adev = dm_kunit_alloc_adev(test); + mutex_init(&adev->dm.dc_lock); + KUNIT_ASSERT_EQ(test, drm_vblank_init(&adev->ddev, 1), 0); + KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0); + + dc = dm_kunit_alloc_dc_with_ctx(test); + dc->current_state = kunit_kzalloc(test, sizeof(*dc->current_state), + GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc->current_state); + adev->dm.dc = dc; + + link = dm_kunit_alloc_link(test); + stream = dm_kunit_alloc_stream(test, link); + + acrtc = dm_test_add_crtc(test, adev); + /* VRR active so the !vrr_active vblank handler is skipped. */ + acrtc->dm_irq_params.freesync_config.state = VRR_STATE_ACTIVE_VARIABLE; + acrtc->dm_irq_params.stream = stream; + acrtc->dm_irq_params.vrr_params.adjust.v_total_min = v_total_min; + acrtc->dm_irq_params.vrr_params.adjust.v_total_max = v_total_max; + + return acrtc; +} + +/** + * dm_test_vupdate_high_irq_dce_vrr_btr - Test DCE vupdate BTR handling + * @test: The KUnit test context + * + * On DCE (zero DCE IP version) with VRR active and a pre-DCE12 family, vupdate + * runs the below-the-range section: it handles the vblank and offloads the + * vmin/vmax update, which the worker then applies to the stream. + */ +static void dm_test_vupdate_high_irq_dce_vrr_btr(struct kunit *test) +{ + struct common_irq_params params = { 0 }; + struct dc_stream_state *stream; + struct amdgpu_device *adev; + struct amdgpu_crtc *acrtc; + + acrtc = dm_test_setup_vmin_vmax_crtc(test, 2000, 2200); + adev = drm_to_adev(acrtc->base.dev); + stream = acrtc->dm_irq_params.stream; + /* Pre-AI family so the BTR block inside the DCE path runs. */ + adev->family = AMDGPU_FAMILY_SI; + + adev->ddev.vblank[0].time = ktime_get(); + + params.adev = adev; + params.irq_src = (enum dc_irq_source)IRQ_TYPE_VUPDATE; + + dm_vupdate_high_irq(¶ms); + + /* The offloaded worker applies the adjust and drops its stream ref. */ + flush_workqueue(adev->dm.vmin_vmax_wq); + KUNIT_EXPECT_EQ(test, stream->adjust.v_total_min, 2000); + KUNIT_EXPECT_EQ(test, stream->adjust.v_total_max, 2200); + KUNIT_EXPECT_EQ(test, kref_read(&stream->refcount), 1); + + amdgpu_dm_irq_fini(adev); +} + /** * dm_test_crtc_high_irq_no_crtc - Test crtc high IRQ with no CRTC * @test: The KUnit test context @@ -3619,6 +3696,41 @@ static void dm_test_crtc_high_irq_writeback_completes(struct kunit *test) KUNIT_EXPECT_FALSE(test, acrtc->wb_frame_done); } +/** + * dm_test_crtc_high_irq_schedules_vmin_vmax - Test the freesync BTR update + * @test: The KUnit test context + * + * On an AI+ family with an active VRR stream, the handler must run the freesync + * v-update and offload the vmin/vmax programming, which the worker then applies + * to the stream. + */ +static void dm_test_crtc_high_irq_schedules_vmin_vmax(struct kunit *test) +{ + struct common_irq_params params = { 0 }; + struct dc_stream_state *stream; + struct amdgpu_device *adev; + struct amdgpu_crtc *acrtc; + + acrtc = dm_test_setup_vmin_vmax_crtc(test, 1000, 1100); + adev = drm_to_adev(acrtc->base.dev); + stream = acrtc->dm_irq_params.stream; + adev->family = AMDGPU_FAMILY_AI; + acrtc->dm_irq_params.vrr_params.supported = true; + + params.adev = adev; + params.irq_src = (enum dc_irq_source)IRQ_TYPE_VBLANK; + + dm_crtc_high_irq(¶ms); + + /* The offloaded worker applies the adjust and drops its stream ref. */ + flush_workqueue(adev->dm.vmin_vmax_wq); + KUNIT_EXPECT_EQ(test, stream->adjust.v_total_min, 1000); + KUNIT_EXPECT_EQ(test, stream->adjust.v_total_max, 1100); + KUNIT_EXPECT_EQ(test, kref_read(&stream->refcount), 1); + + amdgpu_dm_irq_fini(adev); +} + /* Tests for dm_handle_hpd_work() */ /** @@ -4246,11 +4358,13 @@ static struct kunit_case amdgpu_dm_irq_tests[] = { KUNIT_CASE(dm_test_vupdate_high_irq_no_crtc), KUNIT_CASE(dm_test_vupdate_high_irq_dcn_completes_flip), KUNIT_CASE(dm_test_vupdate_high_irq_dcn_no_active_planes), + KUNIT_CASE(dm_test_vupdate_high_irq_dce_vrr_btr), 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), + KUNIT_CASE(dm_test_crtc_high_irq_schedules_vmin_vmax), /* dm_handle_hpd_work */ KUNIT_CASE(dm_test_handle_hpd_work_out_of_range), /* dm_dmub_outbox1_low_irq */ -- 2.43.0
