mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 10:31:33 -04:00
drm/amd/display: Test irq funcs/suspend/workqueue
[WHAT] Expand the amdgpu_dm_irq KUnit suite with the first batch of tests covering IRQ source-function setup, suspend/resume of the IRQ clients, and the HPD RX offload workqueue lifecycle. Assisted-by: Copilot:Claude-Opus-4.8 Reviewed-by: Bhawanpreet Lakha <bhawanpreet.lakha@amd.com> Signed-off-by: Alex Hung <alex.hung@amd.com> Signed-off-by: George Zhang <george.zhang@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
@@ -537,6 +537,7 @@ void amdgpu_dm_irq_suspend(struct amdgpu_device *adev)
|
||||
if (dev->mode_config.poll_enabled)
|
||||
drm_kms_helper_poll_disable(dev);
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_irq_suspend);
|
||||
|
||||
void amdgpu_dm_irq_resume_early(struct amdgpu_device *adev)
|
||||
{
|
||||
@@ -558,6 +559,7 @@ void amdgpu_dm_irq_resume_early(struct amdgpu_device *adev)
|
||||
|
||||
DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags);
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_irq_resume_early);
|
||||
|
||||
void amdgpu_dm_irq_resume_late(struct amdgpu_device *adev)
|
||||
{
|
||||
@@ -586,6 +588,7 @@ void amdgpu_dm_irq_resume_late(struct amdgpu_device *adev)
|
||||
if (dev->mode_config.poll_enabled)
|
||||
drm_kms_helper_poll_enable(dev);
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_irq_resume_late);
|
||||
|
||||
/*
|
||||
* amdgpu_dm_irq_schedule_work - schedule all work items registered for the
|
||||
@@ -900,6 +903,8 @@ void amdgpu_dm_set_irq_funcs(struct amdgpu_device *adev)
|
||||
adev->hpd_irq.num_types = adev->mode_info.num_hpd;
|
||||
adev->hpd_irq.funcs = &dm_hpd_irq_funcs;
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_set_irq_funcs);
|
||||
|
||||
void amdgpu_dm_outbox_init(struct amdgpu_device *adev)
|
||||
{
|
||||
dc_interrupt_set(adev->dm.dc,
|
||||
@@ -1187,6 +1192,7 @@ struct hpd_rx_irq_offload_work_queue *amdgpu_dm_hpd_rx_irq_create_workqueue(stru
|
||||
kfree(hpd_rx_offload_wq);
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_hpd_rx_irq_create_workqueue);
|
||||
|
||||
void amdgpu_dm_hpd_rx_irq_work_suspend(struct amdgpu_display_manager *dm)
|
||||
{
|
||||
@@ -1197,6 +1203,7 @@ void amdgpu_dm_hpd_rx_irq_work_suspend(struct amdgpu_display_manager *dm)
|
||||
flush_workqueue(dm->hpd_rx_offload_wq[i].wq);
|
||||
}
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_hpd_rx_irq_work_suspend);
|
||||
|
||||
STATIC_IFN_KUNIT bool are_sinks_equal(const struct dc_sink *sink1, const struct dc_sink *sink2)
|
||||
{
|
||||
|
||||
@@ -7,14 +7,23 @@
|
||||
|
||||
#include <kunit/test.h>
|
||||
#include <drm/drm_kunit_helpers.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
|
||||
#include "dc.h"
|
||||
#include "inc/core_types.h"
|
||||
#include "irq/irq_service.h"
|
||||
#include "amdgpu.h"
|
||||
#include "amdgpu_mode.h"
|
||||
#include "amdgpu_dm.h"
|
||||
#include "amdgpu_dm_irq.h"
|
||||
#include "amdgpu_dm_kunit_test_helpers.h"
|
||||
#include "dc_dmub_srv.h"
|
||||
#include "ivsrcid/ivsrcid_vislands30.h"
|
||||
#include "ivsrcid/dcn/irqsrcs_dcn_1_0.h"
|
||||
#include "link_service.h"
|
||||
#include "dmub/dmub_srv.h"
|
||||
#include "dal_asic_id.h"
|
||||
|
||||
static void dm_test_irq_handler(void *arg)
|
||||
{
|
||||
@@ -31,6 +40,22 @@ static void dm_test_crtc_list_del(void *data)
|
||||
list_del_init(&acrtc->base.head);
|
||||
}
|
||||
|
||||
struct dm_test_hpd_rx_wq_ctx {
|
||||
struct hpd_rx_irq_offload_work_queue *wq;
|
||||
int count;
|
||||
};
|
||||
|
||||
static void dm_test_destroy_hpd_rx_wq(void *data)
|
||||
{
|
||||
struct dm_test_hpd_rx_wq_ctx *ctx = data;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ctx->count; i++)
|
||||
if (ctx->wq[i].wq)
|
||||
destroy_workqueue(ctx->wq[i].wq);
|
||||
kfree(ctx->wq);
|
||||
}
|
||||
|
||||
/* Tests for amdgpu_dm_hpd_to_dal_irq_source() */
|
||||
|
||||
/**
|
||||
@@ -842,6 +867,318 @@ static void dm_test_get_crtc_by_otg_inst_empty_list(struct kunit *test)
|
||||
KUNIT_EXPECT_NULL(test, amdgpu_dm_get_crtc_by_otg_inst(adev, 0));
|
||||
}
|
||||
|
||||
/* Tests for amdgpu_dm_set_irq_funcs() */
|
||||
|
||||
/**
|
||||
* dm_test_set_irq_funcs - Test irq src funcs and counts are populated
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_set_irq_funcs(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_device *adev;
|
||||
|
||||
adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
|
||||
|
||||
adev->mode_info.num_crtc = 6;
|
||||
adev->mode_info.num_hpd = 4;
|
||||
|
||||
amdgpu_dm_set_irq_funcs(adev);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, adev->crtc_irq.num_types, 6);
|
||||
KUNIT_EXPECT_EQ(test, adev->vline0_irq.num_types, 6);
|
||||
KUNIT_EXPECT_EQ(test, adev->vupdate_irq.num_types, 6);
|
||||
KUNIT_EXPECT_EQ(test, adev->pageflip_irq.num_types, 6);
|
||||
KUNIT_EXPECT_EQ(test, adev->dmub_outbox_irq.num_types, 1);
|
||||
KUNIT_EXPECT_EQ(test, adev->dmub_trace_irq.num_types, 1);
|
||||
KUNIT_EXPECT_EQ(test, adev->hpd_irq.num_types, 4);
|
||||
|
||||
KUNIT_EXPECT_TRUE(test, adev->crtc_irq.funcs != NULL);
|
||||
KUNIT_EXPECT_TRUE(test, adev->vline0_irq.funcs != NULL);
|
||||
KUNIT_EXPECT_TRUE(test, adev->dmub_outbox_irq.funcs != NULL);
|
||||
KUNIT_EXPECT_TRUE(test, adev->vupdate_irq.funcs != NULL);
|
||||
KUNIT_EXPECT_TRUE(test, adev->dmub_trace_irq.funcs != NULL);
|
||||
KUNIT_EXPECT_TRUE(test, adev->pageflip_irq.funcs != NULL);
|
||||
KUNIT_EXPECT_TRUE(test, adev->hpd_irq.funcs != NULL);
|
||||
}
|
||||
|
||||
/* Tests for amdgpu_dm_irq_suspend()/resume_early()/resume_late() */
|
||||
|
||||
/**
|
||||
* dm_test_irq_suspend_empty - Test suspend walks empty handler tables safely
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_irq_suspend_empty(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_device *adev;
|
||||
int src;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0);
|
||||
|
||||
/*
|
||||
* With no registered handlers the HW dc_interrupt_set() calls are
|
||||
* skipped, so suspend must complete without touching the (absent) DC.
|
||||
*/
|
||||
amdgpu_dm_irq_suspend(adev);
|
||||
|
||||
for (src = 0; src < DAL_IRQ_SOURCES_NUMBER; src++) {
|
||||
KUNIT_EXPECT_TRUE(test, list_empty(&adev->dm.irq_handler_list_low_tab[src]));
|
||||
KUNIT_EXPECT_TRUE(test, list_empty(&adev->dm.irq_handler_list_high_tab[src]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_irq_resume_early_empty - Test early resume walks empty tables safely
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_irq_resume_early_empty(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_device *adev;
|
||||
int src;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0);
|
||||
|
||||
amdgpu_dm_irq_resume_early(adev);
|
||||
|
||||
for (src = 0; src < DAL_IRQ_SOURCES_NUMBER; src++)
|
||||
KUNIT_EXPECT_TRUE(test, list_empty(&adev->dm.irq_handler_list_high_tab[src]));
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_irq_resume_late_empty - Test late resume walks empty tables safely
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_irq_resume_late_empty(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_device *adev;
|
||||
int src;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0);
|
||||
|
||||
amdgpu_dm_irq_resume_late(adev);
|
||||
|
||||
for (src = 0; src < DAL_IRQ_SOURCES_NUMBER; src++)
|
||||
KUNIT_EXPECT_TRUE(test, list_empty(&adev->dm.irq_handler_list_low_tab[src]));
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_irq_suspend_registered - Test suspend reaches the dc_interrupt_set path
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* Registers a low-context HPD handler so the handler list is non-empty,
|
||||
* forcing amdgpu_dm_irq_suspend() to call dc_interrupt_set() (NULL-safe with
|
||||
* no DC) and flush_work() on the registered handler.
|
||||
*/
|
||||
static void dm_test_irq_suspend_registered(struct kunit *test)
|
||||
{
|
||||
struct dc_interrupt_params int_params = { 0 };
|
||||
struct amdgpu_device *adev;
|
||||
void *handler;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0);
|
||||
|
||||
int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT;
|
||||
int_params.irq_source = DC_IRQ_SOURCE_HPD1;
|
||||
handler = amdgpu_dm_irq_register_interrupt(adev, &int_params,
|
||||
dm_test_irq_handler, adev);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, handler);
|
||||
|
||||
amdgpu_dm_irq_suspend(adev);
|
||||
|
||||
amdgpu_dm_irq_unregister_interrupt(adev, DC_IRQ_SOURCE_HPD1,
|
||||
dm_test_irq_handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_irq_suspend_disables_polling - Test suspend disables KMS polling
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* With KMS polling active, suspend must take the poll-disable branch.
|
||||
* drm_kms_helper_poll_disable() only cancels the poll work; it leaves the
|
||||
* poll_enabled flag set (cleared later by drm_kms_helper_poll_fini()).
|
||||
*/
|
||||
static void dm_test_irq_suspend_disables_polling(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_device *adev;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0);
|
||||
|
||||
/* Enable KMS polling so suspend takes the poll-disable branch. */
|
||||
drm_kms_helper_poll_init(&adev->ddev);
|
||||
KUNIT_ASSERT_TRUE(test, adev->ddev.mode_config.poll_enabled);
|
||||
|
||||
amdgpu_dm_irq_suspend(adev);
|
||||
|
||||
KUNIT_EXPECT_TRUE(test, adev->ddev.mode_config.poll_enabled);
|
||||
|
||||
drm_kms_helper_poll_fini(&adev->ddev);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_irq_resume_early_registered - Test early resume reaches dc_interrupt_set
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* Registers a low-context HPD RX handler so early resume calls
|
||||
* dc_interrupt_set() for the short-pulse interrupt source.
|
||||
*/
|
||||
static void dm_test_irq_resume_early_registered(struct kunit *test)
|
||||
{
|
||||
struct dc_interrupt_params int_params = { 0 };
|
||||
struct amdgpu_device *adev;
|
||||
void *handler;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0);
|
||||
|
||||
int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT;
|
||||
int_params.irq_source = DC_IRQ_SOURCE_HPD1RX;
|
||||
handler = amdgpu_dm_irq_register_interrupt(adev, &int_params,
|
||||
dm_test_irq_handler, adev);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, handler);
|
||||
|
||||
amdgpu_dm_irq_resume_early(adev);
|
||||
|
||||
amdgpu_dm_irq_unregister_interrupt(adev, DC_IRQ_SOURCE_HPD1RX,
|
||||
dm_test_irq_handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_irq_resume_late_registered - Test late resume reaches dc_interrupt_set
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* Registers a low-context HPD handler so late resume calls dc_interrupt_set()
|
||||
* for the HPD interrupt source.
|
||||
*/
|
||||
static void dm_test_irq_resume_late_registered(struct kunit *test)
|
||||
{
|
||||
struct dc_interrupt_params int_params = { 0 };
|
||||
struct amdgpu_device *adev;
|
||||
void *handler;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0);
|
||||
|
||||
int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT;
|
||||
int_params.irq_source = DC_IRQ_SOURCE_HPD1;
|
||||
handler = amdgpu_dm_irq_register_interrupt(adev, &int_params,
|
||||
dm_test_irq_handler, adev);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, handler);
|
||||
|
||||
amdgpu_dm_irq_resume_late(adev);
|
||||
|
||||
amdgpu_dm_irq_unregister_interrupt(adev, DC_IRQ_SOURCE_HPD1,
|
||||
dm_test_irq_handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_irq_resume_late_enables_polling - Test late resume re-enables polling
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* With KMS polling active, late resume must take the poll-enable branch and
|
||||
* leave polling enabled.
|
||||
*/
|
||||
static void dm_test_irq_resume_late_enables_polling(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_device *adev;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0);
|
||||
|
||||
/* Enable KMS polling so resume_late takes the poll-enable branch. */
|
||||
drm_kms_helper_poll_init(&adev->ddev);
|
||||
KUNIT_ASSERT_TRUE(test, adev->ddev.mode_config.poll_enabled);
|
||||
|
||||
amdgpu_dm_irq_resume_late(adev);
|
||||
|
||||
KUNIT_EXPECT_TRUE(test, adev->ddev.mode_config.poll_enabled);
|
||||
|
||||
drm_kms_helper_poll_fini(&adev->ddev);
|
||||
}
|
||||
|
||||
/* Tests for amdgpu_dm_hpd_rx_irq_create_workqueue() */
|
||||
|
||||
/**
|
||||
* dm_test_hpd_rx_irq_create_workqueue - Test workqueue array creation
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_hpd_rx_irq_create_workqueue(struct kunit *test)
|
||||
{
|
||||
struct dm_test_hpd_rx_wq_ctx *ctx;
|
||||
struct amdgpu_device *adev;
|
||||
struct dc *dc;
|
||||
int i;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
|
||||
dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc);
|
||||
dc->caps.max_links = 4;
|
||||
adev->dm.dc = dc;
|
||||
|
||||
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
|
||||
ctx->wq = amdgpu_dm_hpd_rx_irq_create_workqueue(adev);
|
||||
KUNIT_ASSERT_NOT_NULL(test, ctx->wq);
|
||||
ctx->count = dc->caps.max_links;
|
||||
KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, dm_test_destroy_hpd_rx_wq, ctx), 0);
|
||||
|
||||
for (i = 0; i < dc->caps.max_links; i++)
|
||||
KUNIT_EXPECT_TRUE(test, ctx->wq[i].wq != NULL);
|
||||
}
|
||||
|
||||
/* Tests for amdgpu_dm_hpd_rx_irq_work_suspend() */
|
||||
|
||||
/**
|
||||
* dm_test_hpd_rx_irq_work_suspend_null - Test suspend with no work queue
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_hpd_rx_irq_work_suspend_null(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_display_manager *dm;
|
||||
|
||||
dm = kunit_kzalloc(test, sizeof(*dm), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dm);
|
||||
|
||||
/* A NULL hpd_rx_offload_wq must be a safe no-op (DC untouched). */
|
||||
amdgpu_dm_hpd_rx_irq_work_suspend(dm);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_hpd_rx_irq_work_suspend_flushes - Test suspend flushes queues
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_hpd_rx_irq_work_suspend_flushes(struct kunit *test)
|
||||
{
|
||||
struct dm_test_hpd_rx_wq_ctx *ctx;
|
||||
struct amdgpu_device *adev;
|
||||
struct dc *dc;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
|
||||
dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc);
|
||||
dc->caps.max_links = 2;
|
||||
adev->dm.dc = dc;
|
||||
|
||||
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
|
||||
ctx->wq = amdgpu_dm_hpd_rx_irq_create_workqueue(adev);
|
||||
KUNIT_ASSERT_NOT_NULL(test, ctx->wq);
|
||||
ctx->count = dc->caps.max_links;
|
||||
KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, dm_test_destroy_hpd_rx_wq, ctx), 0);
|
||||
|
||||
adev->dm.hpd_rx_offload_wq = ctx->wq;
|
||||
|
||||
amdgpu_dm_hpd_rx_irq_work_suspend(&adev->dm);
|
||||
}
|
||||
|
||||
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),
|
||||
@@ -894,6 +1231,22 @@ static struct kunit_case amdgpu_dm_irq_tests[] = {
|
||||
KUNIT_CASE(dm_test_get_crtc_by_otg_inst_returns_match),
|
||||
KUNIT_CASE(dm_test_get_crtc_by_otg_inst_returns_null),
|
||||
KUNIT_CASE(dm_test_get_crtc_by_otg_inst_empty_list),
|
||||
/* amdgpu_dm_set_irq_funcs */
|
||||
KUNIT_CASE(dm_test_set_irq_funcs),
|
||||
/* amdgpu_dm_irq_suspend/resume_early/resume_late */
|
||||
KUNIT_CASE(dm_test_irq_suspend_empty),
|
||||
KUNIT_CASE(dm_test_irq_resume_early_empty),
|
||||
KUNIT_CASE(dm_test_irq_resume_late_empty),
|
||||
KUNIT_CASE(dm_test_irq_suspend_registered),
|
||||
KUNIT_CASE(dm_test_irq_suspend_disables_polling),
|
||||
KUNIT_CASE(dm_test_irq_resume_early_registered),
|
||||
KUNIT_CASE(dm_test_irq_resume_late_registered),
|
||||
KUNIT_CASE(dm_test_irq_resume_late_enables_polling),
|
||||
/* amdgpu_dm_hpd_rx_irq_create_workqueue */
|
||||
KUNIT_CASE(dm_test_hpd_rx_irq_create_workqueue),
|
||||
/* amdgpu_dm_hpd_rx_irq_work_suspend */
|
||||
KUNIT_CASE(dm_test_hpd_rx_irq_work_suspend_null),
|
||||
KUNIT_CASE(dm_test_hpd_rx_irq_work_suspend_flushes),
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user