From: Alex Hung <[email protected]> [WHAT] Add the final batch of amdgpu_dm_irq KUnit tests covering the high-IRQ callbacks (dm_pflip_high_irq, dm_vupdate_high_irq, dm_crtc_high_irq), dm_handle_hpd_work, the DMUB outbox1 low IRQ, and the per-ASIC IRQ handler registration helpers (DCE110, DCN10 and outbox).
Assisted-by: Copilot:Claude-Opus-4.8 Reviewed-by: Bhawanpreet Lakha <[email protected]> Signed-off-by: Alex Hung <[email protected]> Signed-off-by: George Zhang <[email protected]> --- .../drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c | 18 +- .../drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h | 5 + .../amdgpu_dm/tests/amdgpu_dm_irq_test.c | 606 ++++++++++++++++++ 3 files changed, 624 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c index e34bd3bbc2b8..dab970e064ff 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c @@ -1776,7 +1776,7 @@ EXPORT_IF_KUNIT(amdgpu_dm_get_crtc_by_otg_inst); * Handles the pageflip interrupt by notifying all interested parties * that the pageflip has been completed. */ -static void dm_pflip_high_irq(void *interrupt_params) +STATIC_IFN_KUNIT void dm_pflip_high_irq(void *interrupt_params) { struct amdgpu_crtc *amdgpu_crtc; struct common_irq_params *irq_params = interrupt_params; @@ -1872,6 +1872,7 @@ static void dm_pflip_high_irq(void *interrupt_params) "crtc:%d[%p], pflip_stat:AMDGPU_FLIP_NONE, vrr[%d]-fp %d\n", amdgpu_crtc->crtc_id, amdgpu_crtc, vrr_active, (int)!e); } +EXPORT_IF_KUNIT(dm_pflip_high_irq); static void dm_handle_vmin_vmax_update(struct work_struct *offload_work) { @@ -1918,7 +1919,7 @@ static void schedule_dc_vmin_vmax(struct amdgpu_device *adev, queue_work(system_percpu_wq, &offload_work->work); } -static void dm_vupdate_high_irq(void *interrupt_params) +STATIC_IFN_KUNIT void dm_vupdate_high_irq(void *interrupt_params) { struct common_irq_params *irq_params = interrupt_params; struct amdgpu_device *adev = irq_params->adev; @@ -1981,6 +1982,7 @@ static void dm_vupdate_high_irq(void *interrupt_params) } } } +EXPORT_IF_KUNIT(dm_vupdate_high_irq); /** * dm_crtc_high_irq() - Handles CRTC interrupt @@ -1989,7 +1991,7 @@ static void dm_vupdate_high_irq(void *interrupt_params) * Handles the CRTC/VSYNC interrupt by notfying DRM's VBLANK * event handler. */ -static void dm_crtc_high_irq(void *interrupt_params) +STATIC_IFN_KUNIT void dm_crtc_high_irq(void *interrupt_params) { struct common_irq_params *irq_params = interrupt_params; struct amdgpu_device *adev = irq_params->adev; @@ -2088,6 +2090,7 @@ static void dm_crtc_high_irq(void *interrupt_params) spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags); } +EXPORT_IF_KUNIT(dm_crtc_high_irq); #if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) /** @@ -2112,7 +2115,7 @@ static void dm_dcn_vertical_interrupt0_high_irq(void *interrupt_params) } #endif /* CONFIG_DRM_AMD_SECURE_DISPLAY */ -static void dm_handle_hpd_work(struct work_struct *work) +STATIC_IFN_KUNIT void dm_handle_hpd_work(struct work_struct *work) { struct dmub_hpd_work *dmub_hpd_wrk; @@ -2132,6 +2135,7 @@ static void dm_handle_hpd_work(struct work_struct *work) kfree(dmub_hpd_wrk); } +EXPORT_IF_KUNIT(dm_handle_hpd_work); STATIC_IFN_KUNIT const char *dmub_notification_type_str(enum dmub_notification_type e) { @@ -2166,7 +2170,7 @@ EXPORT_IF_KUNIT(dmub_notification_type_str); * Handles the Outbox Interrupt * event handler. */ -static void dm_dmub_outbox1_low_irq(void *interrupt_params) +STATIC_IFN_KUNIT void dm_dmub_outbox1_low_irq(void *interrupt_params) { struct dmub_notification notify = {0}; struct common_irq_params *irq_params = interrupt_params; @@ -2229,6 +2233,7 @@ static void dm_dmub_outbox1_low_irq(void *interrupt_params) } while (notify.pending_notification); } } +EXPORT_IF_KUNIT(dm_dmub_outbox1_low_irq); /* Register IRQ sources and initialize IRQ callbacks */ int amdgpu_dm_dce110_register_irq_handlers(struct amdgpu_device *adev) @@ -2367,6 +2372,7 @@ int amdgpu_dm_dce110_register_irq_handlers(struct amdgpu_device *adev) return r; } +EXPORT_IF_KUNIT(amdgpu_dm_dce110_register_irq_handlers); /* Register IRQ sources and initialize IRQ callbacks */ int amdgpu_dm_dcn10_register_irq_handlers(struct amdgpu_device *adev) @@ -2548,6 +2554,7 @@ int amdgpu_dm_dcn10_register_irq_handlers(struct amdgpu_device *adev) return r; } +EXPORT_IF_KUNIT(amdgpu_dm_dcn10_register_irq_handlers); /* Register Outbox IRQ sources and initialize IRQ callbacks */ int amdgpu_dm_register_outbox_irq_handlers(struct amdgpu_device *adev) @@ -2585,3 +2592,4 @@ int amdgpu_dm_register_outbox_irq_handlers(struct amdgpu_device *adev) return 0; } +EXPORT_IF_KUNIT(amdgpu_dm_register_outbox_irq_handlers); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h index 7200d3754905..da698f224d85 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h @@ -173,6 +173,11 @@ void dmub_hpd_callback(struct amdgpu_device *adev, struct dmub_notification *notify); void dmub_hpd_sense_callback(struct amdgpu_device *adev, struct dmub_notification *notify); +void dm_pflip_high_irq(void *interrupt_params); +void dm_vupdate_high_irq(void *interrupt_params); +void dm_crtc_high_irq(void *interrupt_params); +void dm_handle_hpd_work(struct work_struct *work); +void dm_dmub_outbox1_low_irq(void *interrupt_params); #endif #endif /* __AMDGPU_DM_IRQ_H__ */ 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 eb74883cb6d9..c356c2c836b4 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 @@ -184,6 +184,24 @@ static bool dm_test_allow_hpd_rx_irq_true(const struct dc_link *link) } +static uint32_t dm_test_dmub_get_outbox0_wptr(struct dmub_srv *dmub) +{ + return 0; +} + +static uint32_t dm_test_dmub_get_outbox1_wptr(struct dmub_srv *dmub) +{ + return 0; +} + +static int dm_test_dmub_notify_count; + +static void dm_test_dmub_notify_callback(struct amdgpu_device *adev, + struct dmub_notification *notify) +{ + dm_test_dmub_notify_count++; +} + static struct dc *dm_test_alloc_dc_with_ctx(struct kunit *test) { struct dc_context *ctx; @@ -200,6 +218,37 @@ static struct dc *dm_test_alloc_dc_with_ctx(struct kunit *test) return dc; } +static enum dc_irq_source dm_test_to_dal_irq_source_dce110( + struct irq_service *irq_service, + uint32_t src_id, + uint32_t ext_id) +{ + switch (src_id) { + case VISLANDS30_IV_SRCID_D1_VERTICAL_INTERRUPT0: + return DC_IRQ_SOURCE_VBLANK1; + case VISLANDS30_IV_SRCID_D1_V_UPDATE_INT: + return DC_IRQ_SOURCE_VUPDATE1; + case VISLANDS30_IV_SRCID_D1_GRPH_PFLIP: + return DC_IRQ_SOURCE_PFLIP1; + case VISLANDS30_IV_SRCID_D1_GRPH_PFLIP + 2: + return DC_IRQ_SOURCE_PFLIP2; + case VISLANDS30_IV_SRCID_D1_GRPH_PFLIP + 4: + return DC_IRQ_SOURCE_PFLIP3; + case VISLANDS30_IV_SRCID_D1_GRPH_PFLIP + 6: + return DC_IRQ_SOURCE_PFLIP4; + case VISLANDS30_IV_SRCID_D1_GRPH_PFLIP + 8: + return DC_IRQ_SOURCE_PFLIP5; + case VISLANDS30_IV_SRCID_D1_GRPH_PFLIP + 10: + return DC_IRQ_SOURCE_PFLIP6; + default: + return DC_IRQ_SOURCE_INVALID; + } +} + +static const struct irq_service_funcs dm_test_irq_service_funcs_dce110 = { + .to_dal_irq_source = dm_test_to_dal_irq_source_dce110 +}; + static enum dc_irq_source dm_test_to_dal_irq_source_dcn10( struct irq_service *irq_service, uint32_t src_id, @@ -274,6 +323,32 @@ static struct dc *dm_test_alloc_dc_with_irq_service(struct kunit *test, return dc; } +static void dm_test_free_irq_sources(void *data) +{ + struct amdgpu_device *adev = data; + int i; + + for (i = 0; i < AMDGPU_IRQ_CLIENTID_MAX; i++) { + kfree(adev->irq.client[i].sources); + adev->irq.client[i].sources = NULL; + } + + kfree(adev->crtc_irq.enabled_types); + adev->crtc_irq.enabled_types = NULL; + kfree(adev->vline0_irq.enabled_types); + adev->vline0_irq.enabled_types = NULL; + kfree(adev->vupdate_irq.enabled_types); + adev->vupdate_irq.enabled_types = NULL; + kfree(adev->pageflip_irq.enabled_types); + adev->pageflip_irq.enabled_types = NULL; + kfree(adev->dmub_outbox_irq.enabled_types); + adev->dmub_outbox_irq.enabled_types = NULL; + kfree(adev->dmub_trace_irq.enabled_types); + adev->dmub_trace_irq.enabled_types = NULL; + kfree(adev->hpd_irq.enabled_types); + adev->hpd_irq.enabled_types = NULL; +} + static void dm_test_crtc_list_del(void *data) { struct amdgpu_crtc *acrtc = data; @@ -3217,6 +3292,516 @@ static void dm_test_register_hpd_handlers_dmub_outbox(struct kunit *test) amdgpu_dm_irq_fini(adev); } +/* Tests for CRTC/pflip/vupdate high IRQ callbacks */ + +/** + * dm_test_pflip_high_irq_no_crtc - Test pflip high IRQ with no matching CRTC + * @test: The KUnit test context + */ +static void dm_test_pflip_high_irq_no_crtc(struct kunit *test) +{ + struct common_irq_params params = { 0 }; + struct amdgpu_device *adev; + + adev = dm_kunit_alloc_adev(test); + params.adev = adev; + params.irq_src = (enum dc_irq_source)IRQ_TYPE_PFLIP; + + dm_pflip_high_irq(¶ms); +} + +/** + * dm_test_pflip_high_irq_not_submitted - Test pflip high IRQ early status exit + * @test: The KUnit test context + */ +static void dm_test_pflip_high_irq_not_submitted(struct kunit *test) +{ + struct common_irq_params params = { 0 }; + struct amdgpu_crtc *acrtc; + struct amdgpu_device *adev; + + adev = dm_kunit_alloc_adev(test); + acrtc = kunit_kzalloc(test, sizeof(*acrtc), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acrtc); + + INIT_LIST_HEAD(&acrtc->base.head); + acrtc->base.dev = &adev->ddev; + acrtc->otg_inst = 0; + acrtc->pflip_status = AMDGPU_FLIP_NONE; + list_add_tail(&acrtc->base.head, &adev->ddev.mode_config.crtc_list); + KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, + dm_test_crtc_list_del, acrtc), 0); + + params.adev = adev; + params.irq_src = (enum dc_irq_source)IRQ_TYPE_PFLIP; + + dm_pflip_high_irq(¶ms); + KUNIT_EXPECT_EQ(test, acrtc->pflip_status, AMDGPU_FLIP_NONE); +} + +/** + * dm_test_vupdate_high_irq_no_crtc - Test vupdate high IRQ with no CRTC + * @test: The KUnit test context + */ +static void dm_test_vupdate_high_irq_no_crtc(struct kunit *test) +{ + struct common_irq_params params = { 0 }; + struct amdgpu_device *adev; + + adev = dm_kunit_alloc_adev(test); + params.adev = adev; + params.irq_src = (enum dc_irq_source)IRQ_TYPE_VUPDATE; + + dm_vupdate_high_irq(¶ms); +} + +/** + * dm_test_crtc_high_irq_no_crtc - Test crtc high IRQ with no CRTC + * @test: The KUnit test context + */ +static void dm_test_crtc_high_irq_no_crtc(struct kunit *test) +{ + struct common_irq_params params = { 0 }; + struct amdgpu_device *adev; + + adev = dm_kunit_alloc_adev(test); + params.adev = adev; + params.irq_src = (enum dc_irq_source)IRQ_TYPE_VBLANK; + + dm_crtc_high_irq(¶ms); +} + +/** + * dm_test_crtc_high_irq_vrr_pre_ai - Test crtc high IRQ VRR path on pre-AI ASIC + * @test: The KUnit test context + * + * With a matching CRTC, no writeback, VRR active (so the !vrr_active vblank + * handler is skipped) and a pre-AI family, the handler runs through CRC + * handling and returns before the freesync section. + */ +static void dm_test_crtc_high_irq_vrr_pre_ai(struct kunit *test) +{ + struct common_irq_params params = { 0 }; + struct amdgpu_crtc *acrtc; + struct amdgpu_device *adev; + + adev = dm_kunit_alloc_adev(test); + acrtc = kunit_kzalloc(test, sizeof(*acrtc), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acrtc); + + INIT_LIST_HEAD(&acrtc->base.head); + acrtc->base.dev = &adev->ddev; + acrtc->otg_inst = 0; + /* VRR active so the !vrr_active vblank handler is skipped. */ + acrtc->dm_irq_params.freesync_config.state = VRR_STATE_ACTIVE_VARIABLE; + list_add_tail(&acrtc->base.head, &adev->ddev.mode_config.crtc_list); + KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, + dm_test_crtc_list_del, acrtc), 0); + + /* Pre-AI family returns right after CRC handling. */ + adev->family = AMDGPU_FAMILY_SI; + + params.adev = adev; + params.irq_src = (enum dc_irq_source)IRQ_TYPE_VBLANK; + + dm_crtc_high_irq(¶ms); +} + +/** + * dm_test_crtc_high_irq_vrr_ai_no_stream - Test crtc high IRQ AI path, no stream + * @test: The KUnit test context + * + * On an AI+ family the handler runs the post-CRC freesync section. With no + * stream and no pending flip, both inner blocks are skipped and the handler + * completes through the event-lock critical section, leaving pflip_status + * untouched. + */ +static void dm_test_crtc_high_irq_vrr_ai_no_stream(struct kunit *test) +{ + struct common_irq_params params = { 0 }; + struct amdgpu_crtc *acrtc; + struct amdgpu_device *adev; + + adev = dm_kunit_alloc_adev(test); + acrtc = kunit_kzalloc(test, sizeof(*acrtc), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acrtc); + + INIT_LIST_HEAD(&acrtc->base.head); + acrtc->base.dev = &adev->ddev; + acrtc->otg_inst = 0; + acrtc->pflip_status = AMDGPU_FLIP_NONE; + /* VRR active so the !vrr_active vblank handler is skipped. */ + acrtc->dm_irq_params.freesync_config.state = VRR_STATE_ACTIVE_VARIABLE; + list_add_tail(&acrtc->base.head, &adev->ddev.mode_config.crtc_list); + KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, + dm_test_crtc_list_del, acrtc), 0); + + /* AI+ family runs the freesync section; no stream skips it. */ + adev->family = AMDGPU_FAMILY_AI; + + params.adev = adev; + params.irq_src = (enum dc_irq_source)IRQ_TYPE_VBLANK; + + dm_crtc_high_irq(¶ms); + KUNIT_EXPECT_EQ(test, acrtc->pflip_status, AMDGPU_FLIP_NONE); +} + +/* Tests for dm_handle_hpd_work() */ + +/** + * dm_test_handle_hpd_work_out_of_range - Test HPD work frees unknown notification + * @test: The KUnit test context + */ +static void dm_test_handle_hpd_work_out_of_range(struct kunit *test) +{ + struct dmub_hpd_work *hpd_work; + struct amdgpu_device *adev; + + adev = dm_kunit_alloc_adev(test); + hpd_work = kzalloc(sizeof(*hpd_work), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hpd_work); + hpd_work->dmub_notify = kzalloc(sizeof(*hpd_work->dmub_notify), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hpd_work->dmub_notify); + hpd_work->dmub_notify->type = + (enum dmub_notification_type)ARRAY_SIZE(adev->dm.dmub_callback); + hpd_work->adev = adev; + INIT_WORK(&hpd_work->handle_hpd_work, dm_handle_hpd_work); + + dm_handle_hpd_work(&hpd_work->handle_hpd_work); +} + +/* Tests for dm_dmub_outbox1_low_irq() */ + +/** + * dm_test_dmub_outbox1_low_irq_empty - Test outbox low IRQ with empty trace queue + * @test: The KUnit test context + */ +static void dm_test_dmub_outbox1_low_irq_empty(struct kunit *test) +{ + struct common_irq_params params = { 0 }; + struct dc_dmub_srv *dc_dmub_srv; + struct amdgpu_device *adev; + struct dmub_srv *dmub; + struct dc *dc; + + adev = dm_kunit_alloc_adev(test); + dc = dm_test_alloc_dc_with_ctx(test); + dc_dmub_srv = kunit_kzalloc(test, sizeof(*dc_dmub_srv), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc_dmub_srv); + dmub = kunit_kzalloc(test, sizeof(*dmub), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dmub); + + dmub->hw_funcs.get_outbox0_wptr = dm_test_dmub_get_outbox0_wptr; + dc_dmub_srv->dmub = dmub; + dc->ctx->dmub_srv = dc_dmub_srv; + adev->dm.dc = dc; + params.adev = adev; + params.irq_src = DC_IRQ_SOURCE_DMCUB_OUTBOX; + + dm_dmub_outbox1_low_irq(¶ms); +} + +/* + * dm_test_alloc_adev_outbox_notify - Build an adev wired for DMUB outbox + * notification handling. + * + * Configures dc/dc_dmub_srv/dmub so that the trace queue is empty and + * dc_enable_dmub_notifications() returns true, allowing the notification + * handling block of dm_dmub_outbox1_low_irq() to execute. The outbox1 ring + * buffer is left empty, so each notification read returns + * DMUB_NOTIFICATION_NO_DATA with no pending notification. + */ +static struct amdgpu_device *dm_test_alloc_adev_outbox_notify(struct kunit *test) +{ + struct dc_dmub_srv *dc_dmub_srv; + struct amdgpu_device *adev; + struct dmub_srv *dmub; + struct dc *dc; + + adev = dm_kunit_alloc_adev(test); + dc = dm_test_alloc_dc_with_ctx(test); + dc_dmub_srv = kunit_kzalloc(test, sizeof(*dc_dmub_srv), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc_dmub_srv); + dmub = kunit_kzalloc(test, sizeof(*dmub), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dmub); + + dmub->hw_init = true; + dmub->hw_funcs.get_outbox0_wptr = dm_test_dmub_get_outbox0_wptr; + dmub->hw_funcs.get_outbox1_wptr = dm_test_dmub_get_outbox1_wptr; + dc_dmub_srv->dmub = dmub; + dc->ctx->dmub_srv = dc_dmub_srv; + + /* Make dc_enable_dmub_notifications() return true. */ + dc->caps.dmcub_support = true; + dc->ctx->asic_id.chip_family = AMDGPU_FAMILY_GC_11_0_1; + + adev->dm.dc = dc; + + return adev; +} + +/** + * dm_test_dmub_outbox1_low_irq_no_handler - Test notification with no handler + * @test: The KUnit test context + * + * Notifications are enabled but no callback is registered for the returned + * notification type, exercising the skip-with-warning path. + */ +static void dm_test_dmub_outbox1_low_irq_no_handler(struct kunit *test) +{ + struct common_irq_params params = { 0 }; + struct amdgpu_device *adev; + + adev = dm_test_alloc_adev_outbox_notify(test); + params.adev = adev; + params.irq_src = DC_IRQ_SOURCE_DMCUB_OUTBOX; + + dm_dmub_outbox1_low_irq(¶ms); +} + +/** + * dm_test_dmub_outbox1_low_irq_direct_callback - Test direct callback dispatch + * @test: The KUnit test context + * + * Notifications are enabled with a registered callback and thread offload + * disabled, so the callback is invoked directly from the IRQ handler. + */ +static void dm_test_dmub_outbox1_low_irq_direct_callback(struct kunit *test) +{ + struct common_irq_params params = { 0 }; + struct amdgpu_device *adev; + + adev = dm_test_alloc_adev_outbox_notify(test); + adev->dm.dmub_callback[DMUB_NOTIFICATION_NO_DATA] = dm_test_dmub_notify_callback; + adev->dm.dmub_thread_offload[DMUB_NOTIFICATION_NO_DATA] = false; + params.adev = adev; + params.irq_src = DC_IRQ_SOURCE_DMCUB_OUTBOX; + + dm_test_dmub_notify_count = 0; + + dm_dmub_outbox1_low_irq(¶ms); + + KUNIT_EXPECT_EQ(test, dm_test_dmub_notify_count, 1); +} + +/** + * dm_test_dmub_outbox1_low_irq_offload - Test offloaded callback dispatch + * @test: The KUnit test context + * + * Notifications are enabled with a registered callback and thread offload + * enabled, so the callback is dispatched via the delayed HPD work queue. + */ +static void dm_test_dmub_outbox1_low_irq_offload(struct kunit *test) +{ + struct common_irq_params params = { 0 }; + struct amdgpu_device *adev; + + adev = dm_test_alloc_adev_outbox_notify(test); + adev->dm.dmub_callback[DMUB_NOTIFICATION_NO_DATA] = dm_test_dmub_notify_callback; + adev->dm.dmub_thread_offload[DMUB_NOTIFICATION_NO_DATA] = true; + adev->dm.delayed_hpd_wq = create_singlethread_workqueue("dm_irq_test_outbox"); + KUNIT_ASSERT_NOT_NULL(test, adev->dm.delayed_hpd_wq); + params.adev = adev; + params.irq_src = DC_IRQ_SOURCE_DMCUB_OUTBOX; + + dm_test_dmub_notify_count = 0; + + dm_dmub_outbox1_low_irq(¶ms); + + flush_workqueue(adev->dm.delayed_hpd_wq); + destroy_workqueue(adev->dm.delayed_hpd_wq); + + KUNIT_EXPECT_EQ(test, dm_test_dmub_notify_count, 1); +} + + +/** + * dm_test_dce110_register_irq_handlers_rejects_uninitialized_sources - Test DCE110 error + * @test: The KUnit test context + */ +static void dm_test_dce110_register_irq_handlers_rejects_uninitialized_sources(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc *dc; + + adev = dm_kunit_alloc_adev(test); + dc = dm_test_alloc_dc_with_ctx(test); + adev->dm.dc = dc; + + KUNIT_EXPECT_EQ(test, amdgpu_dm_dce110_register_irq_handlers(adev), -EINVAL); +} + +/** + * dm_test_dce110_register_irq_handlers_one_crtc - Test DCE110 with 1 CRTC + * @test: The KUnit test context + * + * Exercises the VBLANK, VUPDATE, PFLIP and HPD for-loop bodies with a + * fake IRQ service that maps source IDs to DC IRQ sources. + */ +static void dm_test_dce110_register_irq_handlers_one_crtc(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc *dc; + + adev = dm_kunit_alloc_adev(test); + KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, dm_test_free_irq_sources, + adev), 0); + dc = dm_test_alloc_dc_with_irq_service(test, &dm_test_irq_service_funcs_dce110); + dc->ctx->dce_version = DCE_VERSION_11_0; + adev->dm.dc = dc; + adev->mode_info.num_crtc = 1; + adev->mode_info.num_hpd = 1; + amdgpu_dm_set_irq_funcs(adev); + KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0); + + KUNIT_EXPECT_EQ(test, amdgpu_dm_dce110_register_irq_handlers(adev), 0); + + /* Verify VBLANK params were populated */ + KUNIT_EXPECT_EQ(test, (int)adev->dm.vblank_params[0].irq_src, + (int)DC_IRQ_SOURCE_VBLANK1); + KUNIT_EXPECT_PTR_EQ(test, adev->dm.vblank_params[0].adev, adev); + + /* Verify VUPDATE params were populated (VRR supported on DCE 11) */ + KUNIT_EXPECT_EQ(test, (int)adev->dm.vupdate_params[0].irq_src, + (int)DC_IRQ_SOURCE_VUPDATE1); + + /* Verify PFLIP params were populated (6 fixed entries) */ + KUNIT_EXPECT_EQ(test, (int)adev->dm.pflip_params[0].irq_src, + (int)DC_IRQ_SOURCE_PFLIP1); + KUNIT_EXPECT_EQ(test, (int)adev->dm.pflip_params[5].irq_src, + (int)DC_IRQ_SOURCE_PFLIP6); + + amdgpu_dm_irq_fini(adev); +} + +/** + * dm_test_dcn10_register_irq_handlers_zero_crtc - Test DCN10 zero-CRTC registration + * @test: The KUnit test context + */ +static void dm_test_dcn10_register_irq_handlers_zero_crtc(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc *dc; + + adev = dm_kunit_alloc_adev(test); + KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, dm_test_free_irq_sources, + adev), 0); + dc = dm_test_alloc_dc_with_ctx(test); + adev->dm.dc = dc; + adev->mode_info.num_hpd = 1; + amdgpu_dm_set_irq_funcs(adev); + + KUNIT_EXPECT_EQ(test, amdgpu_dm_dcn10_register_irq_handlers(adev), 0); + KUNIT_ASSERT_NOT_NULL(test, adev->irq.client[SOC15_IH_CLIENTID_DCE].sources); + KUNIT_EXPECT_PTR_EQ(test, + adev->irq.client[SOC15_IH_CLIENTID_DCE].sources[DCN_1_0__SRCID__DC_HPD1_INT], + &adev->hpd_irq); +} + +/** + * dm_test_dcn10_register_irq_handlers_one_crtc - Test DCN10 with 1 CRTC + * @test: The KUnit test context + * + * Exercises the VSTARTUP, VUPDATE, and PFLIP for-loop bodies with a + * fake IRQ service. + */ +static void dm_test_dcn10_register_irq_handlers_one_crtc(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc *dc; + + adev = dm_kunit_alloc_adev(test); + KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, dm_test_free_irq_sources, + adev), 0); + dc = dm_test_alloc_dc_with_irq_service(test, &dm_test_irq_service_funcs_dcn10); + adev->dm.dc = dc; + adev->mode_info.num_crtc = 1; + adev->mode_info.num_hpd = 1; + dc->caps.max_otg_num = 1; + amdgpu_dm_set_irq_funcs(adev); + KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0); + + KUNIT_EXPECT_EQ(test, amdgpu_dm_dcn10_register_irq_handlers(adev), 0); + + /* Verify VBLANK params were populated */ + KUNIT_EXPECT_EQ(test, (int)adev->dm.vblank_params[0].irq_src, + (int)DC_IRQ_SOURCE_VBLANK1); + KUNIT_EXPECT_PTR_EQ(test, adev->dm.vblank_params[0].adev, adev); + + /* Verify VUPDATE params were populated */ + KUNIT_EXPECT_EQ(test, (int)adev->dm.vupdate_params[0].irq_src, + (int)DC_IRQ_SOURCE_VUPDATE1); + + /* Verify PFLIP params were populated */ + KUNIT_EXPECT_EQ(test, (int)adev->dm.pflip_params[0].irq_src, + (int)DC_IRQ_SOURCE_PFLIP1); + + amdgpu_dm_irq_fini(adev); +} + +/** + * dm_test_register_outbox_irq_handlers_without_dmub - Test outbox registration without DMUB + * @test: The KUnit test context + */ +static void dm_test_register_outbox_irq_handlers_without_dmub(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc *dc; + + adev = dm_kunit_alloc_adev(test); + KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, dm_test_free_irq_sources, + adev), 0); + dc = dm_test_alloc_dc_with_ctx(test); + adev->dm.dc = dc; + amdgpu_dm_set_irq_funcs(adev); + + KUNIT_EXPECT_EQ(test, amdgpu_dm_register_outbox_irq_handlers(adev), 0); + KUNIT_ASSERT_NOT_NULL(test, adev->irq.client[SOC15_IH_CLIENTID_DCE].sources); + KUNIT_EXPECT_PTR_EQ(test, + adev->irq.client[SOC15_IH_CLIENTID_DCE].sources[ + DCN_1_0__SRCID__DMCUB_OUTBOX_LOW_PRIORITY_READY_INT], + &adev->dmub_outbox_irq); +} + +/** + * dm_test_register_outbox_irq_handlers_with_dmub - Test outbox registration with DMUB + * @test: The KUnit test context + * + * Exercises the dc->ctx->dmub_srv branch which maps the outbox source and + * registers dm_dmub_outbox1_low_irq in the low IRQ context table. + */ +static void dm_test_register_outbox_irq_handlers_with_dmub(struct kunit *test) +{ + struct dc_dmub_srv *dc_dmub_srv; + struct amdgpu_device *adev; + struct list_head *hnd_list; + struct dc *dc; + + adev = dm_kunit_alloc_adev(test); + KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, dm_test_free_irq_sources, + adev), 0); + dc = dm_test_alloc_dc_with_irq_service(test, &dm_test_irq_service_funcs_dcn10); + dc_dmub_srv = kunit_kzalloc(test, sizeof(*dc_dmub_srv), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc_dmub_srv); + dc->ctx->dmub_srv = dc_dmub_srv; + adev->dm.dc = dc; + amdgpu_dm_set_irq_funcs(adev); + KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0); + + KUNIT_EXPECT_EQ(test, amdgpu_dm_register_outbox_irq_handlers(adev), 0); + + /* Verify the outbox IRQ params were populated */ + KUNIT_EXPECT_EQ(test, (int)adev->dm.dmub_outbox_params[0].irq_src, + (int)DC_IRQ_SOURCE_DMCUB_OUTBOX); + KUNIT_EXPECT_PTR_EQ(test, adev->dm.dmub_outbox_params[0].adev, adev); + + /* Verify a low-context handler was registered for the outbox source */ + hnd_list = &adev->dm.irq_handler_list_low_tab[DC_IRQ_SOURCE_DMCUB_OUTBOX]; + KUNIT_EXPECT_FALSE(test, list_empty(hnd_list)); + + amdgpu_dm_irq_fini(adev); +} + static struct kunit_case amdgpu_dm_irq_tests[] = { /* amdgpu_dm_hpd_to_dal_irq_source */ KUNIT_CASE(dm_test_hpd_to_dal_irq_source_hpd1), @@ -3352,6 +3937,27 @@ static struct kunit_case amdgpu_dm_irq_tests[] = { KUNIT_CASE(dm_test_register_hpd_handlers_invalid_hpd), KUNIT_CASE(dm_test_register_hpd_handlers_invalid_hpd_rx), KUNIT_CASE(dm_test_register_hpd_handlers_dmub_outbox), + /* pflip/vupdate/crtc high IRQ callbacks */ + KUNIT_CASE(dm_test_pflip_high_irq_no_crtc), + KUNIT_CASE(dm_test_pflip_high_irq_not_submitted), + KUNIT_CASE(dm_test_vupdate_high_irq_no_crtc), + 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), + /* dm_handle_hpd_work */ + KUNIT_CASE(dm_test_handle_hpd_work_out_of_range), + /* dm_dmub_outbox1_low_irq */ + KUNIT_CASE(dm_test_dmub_outbox1_low_irq_empty), + KUNIT_CASE(dm_test_dmub_outbox1_low_irq_no_handler), + KUNIT_CASE(dm_test_dmub_outbox1_low_irq_direct_callback), + KUNIT_CASE(dm_test_dmub_outbox1_low_irq_offload), + /* IRQ handler registration helpers */ + KUNIT_CASE(dm_test_dce110_register_irq_handlers_rejects_uninitialized_sources), + KUNIT_CASE(dm_test_dce110_register_irq_handlers_one_crtc), + KUNIT_CASE(dm_test_dcn10_register_irq_handlers_zero_crtc), + KUNIT_CASE(dm_test_dcn10_register_irq_handlers_one_crtc), + KUNIT_CASE(dm_test_register_outbox_irq_handlers_without_dmub), + KUNIT_CASE(dm_test_register_outbox_irq_handlers_with_dmub), {} }; -- 2.55.0
