drm/amd/display: Test vline0 irq control

[WHAT]
The vline0 interrupt handling in amdgpu_dm_crtc_set_vblank() sits
inside a CONFIG_DRM_AMD_SECURE_DISPLAY block. That symbol depends on
CONFIG_DRM_AMD_DC_FP, which cannot be enabled on UML because UML does
not select ARCH_HAS_KERNEL_FPU_SUPPORT, so the logic is never built by
the KUnit tests and stays uncovered.

[HOW]
Move the block into amdgpu_dm_crtc_set_vline0_irq() and build it
whenever CONFIG_DRM_AMD_SECURE_DISPLAY or the KUnit tests are enabled.
The DCN-only check becomes an early return. Behaviour of
amdgpu_dm_crtc_set_vblank() is unchanged.

Add tests for amdgpu_dm_crtc_set_vline0_irq() covering the DCE no-op
path, error propagation from amdgpu_irq_get() and the enable/disable
refcount round-trip.

Assisted-by: Copilot:Claude-Opus-5
Reviewed-by: Bhawanpreet Lakha <bhawanpreet.lakha@amd.com>
Signed-off-by: Alex Hung <alex.hung@amd.com>
Tested-by: Dan Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
Alex Hung 2026-08-21 12:24:16 -06:00 committed by Alex Deucher
parent 285f6b4557
commit 22dbf5c4b2
3 changed files with 129 additions and 13 deletions

View File

@ -249,6 +249,30 @@ STATIC_IFN_KUNIT void amdgpu_dm_crtc_vblank_control_worker(struct work_struct *w
}
EXPORT_IF_KUNIT(amdgpu_dm_crtc_vblank_control_worker);
#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) || IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
STATIC_IFN_KUNIT int amdgpu_dm_crtc_set_vline0_irq(struct drm_crtc *crtc, int irq_type,
bool enable)
{
struct amdgpu_device *adev = drm_to_adev(crtc->dev);
int rc;
/* crtc vline0 interrupt, only available on DCN+ */
if (amdgpu_ip_version(adev, DCE_HWIP, 0) == 0)
return 0;
if (enable) {
rc = amdgpu_irq_get(adev, &adev->vline0_irq, irq_type);
drm_dbg_vbl(crtc->dev, "Get vline0_irq ret=%d\n", rc);
} else {
rc = amdgpu_irq_put(adev, &adev->vline0_irq, irq_type);
drm_dbg_vbl(crtc->dev, "Put vline0_irq ret=%d\n", rc);
}
return rc;
}
EXPORT_IF_KUNIT(amdgpu_dm_crtc_set_vline0_irq);
#endif
static inline int amdgpu_dm_crtc_set_vblank(struct drm_crtc *crtc, bool enable)
{
struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
@ -358,19 +382,9 @@ static inline int amdgpu_dm_crtc_set_vblank(struct drm_crtc *crtc, bool enable)
}
#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
/* crtc vline0 interrupt, only available on DCN+ */
if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0) {
if (enable) {
rc = amdgpu_irq_get(adev, &adev->vline0_irq, irq_type);
drm_dbg_vbl(crtc->dev, "Get vline0_irq ret=%d\n", rc);
} else {
rc = amdgpu_irq_put(adev, &adev->vline0_irq, irq_type);
drm_dbg_vbl(crtc->dev, "Put vline0_irq ret=%d\n", rc);
}
if (rc)
return rc;
}
rc = amdgpu_dm_crtc_set_vline0_irq(crtc, irq_type, enable);
if (rc)
return rc;
#endif
if (amdgpu_in_reset(adev))

View File

@ -56,6 +56,7 @@ void amdgpu_dm_crtc_update_crtc_active_planes(struct drm_crtc *crtc,
struct drm_crtc_state *new_crtc_state);
void amdgpu_dm_crtc_vblank_control_worker(struct work_struct *work);
void amdgpu_dm_idle_worker(struct work_struct *work);
int amdgpu_dm_crtc_set_vline0_irq(struct drm_crtc *crtc, int irq_type, bool enable);
int amdgpu_dm_crtc_helper_atomic_check(struct drm_crtc *crtc,
struct drm_atomic_commit *state);
#ifdef CONFIG_DEBUG_FS

View File

@ -1226,6 +1226,103 @@ static void dm_test_crtc_enable_vblank_ips_restore_replay(struct kunit *test)
KUNIT_EXPECT_EQ(test, amdgpu_dm_crtc_enable_vblank(&acrtc->base), 0);
}
/* Tests for amdgpu_dm_crtc_set_vline0_irq() */
/*
* dm_test_crtc_setup_vline0 - Build an adev/CRTC for the vline0 IRQ helper.
* @test: The KUnit test context
* @adev_out: Receives the allocated device
* @ip_version: DCE IP version stamped on the device (0 selects the DCE path)
*
* Returns a bare CRTC attached to the device. The IRQ subsystem is left
* uninstalled for callers to arm.
*/
static struct drm_crtc *dm_test_crtc_setup_vline0(struct kunit *test,
struct amdgpu_device **adev_out,
uint32_t ip_version)
{
struct amdgpu_device *adev;
struct drm_crtc *crtc;
adev = dm_kunit_alloc_adev(test);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc);
crtc->dev = &adev->ddev;
adev->ip_versions[DCE_HWIP][0] = ip_version;
*adev_out = adev;
return crtc;
}
/**
* dm_test_crtc_set_vline0_irq_dce_noop - Test vline0 irq is skipped on DCE
* @test: The KUnit test context
*
* VLINE0 only exists on DCN+. With no DCE IP version stamped the helper must
* return 0 without touching the IRQ source, even though the IRQ subsystem is
* uninstalled (which would otherwise make amdgpu_irq_get() fail).
*/
static void dm_test_crtc_set_vline0_irq_dce_noop(struct kunit *test)
{
struct amdgpu_device *adev;
struct drm_crtc *crtc;
crtc = dm_test_crtc_setup_vline0(test, &adev, 0);
KUNIT_EXPECT_EQ(test,
amdgpu_dm_crtc_set_vline0_irq(crtc, AMDGPU_CRTC_IRQ_VBLANK1, true), 0);
}
/**
* dm_test_crtc_set_vline0_irq_error - Test vline0 irq failure is propagated
* @test: The KUnit test context
*
* On DCN with the IRQ subsystem uninstalled, amdgpu_irq_get() returns -ENOENT
* and the helper must propagate it.
*/
static void dm_test_crtc_set_vline0_irq_error(struct kunit *test)
{
struct amdgpu_device *adev;
struct drm_crtc *crtc;
crtc = dm_test_crtc_setup_vline0(test, &adev, IP_VERSION(3, 5, 0));
KUNIT_EXPECT_EQ(test,
amdgpu_dm_crtc_set_vline0_irq(crtc, AMDGPU_CRTC_IRQ_VBLANK1, true),
-ENOENT);
}
/**
* dm_test_crtc_set_vline0_irq_enable_disable - Test vline0 irq refcounting
* @test: The KUnit test context
*
* On DCN with an armed IRQ source, enabling takes a vline0 reference and
* disabling drops it again.
*/
static void dm_test_crtc_set_vline0_irq_enable_disable(struct kunit *test)
{
struct amdgpu_device *adev;
struct drm_crtc *crtc;
atomic_t *refcount;
crtc = dm_test_crtc_setup_vline0(test, &adev, IP_VERSION(3, 5, 0));
adev->irq.installed = true;
dm_test_crtc_arm_irq_src(test, &adev->vline0_irq, 1);
refcount = &adev->vline0_irq.enabled_types[AMDGPU_CRTC_IRQ_VBLANK1];
KUNIT_EXPECT_EQ(test,
amdgpu_dm_crtc_set_vline0_irq(crtc, AMDGPU_CRTC_IRQ_VBLANK1, true), 0);
KUNIT_EXPECT_EQ(test, atomic_read(refcount), 2);
KUNIT_EXPECT_EQ(test,
amdgpu_dm_crtc_set_vline0_irq(crtc, AMDGPU_CRTC_IRQ_VBLANK1, false), 0);
KUNIT_EXPECT_EQ(test, atomic_read(refcount), 1);
}
/* Tests for amdgpu_dm_crtc_update_crtc_active_planes() */
/**
@ -2526,6 +2623,10 @@ static struct kunit_case amdgpu_dm_crtc_tests[] = {
KUNIT_CASE(dm_test_crtc_enable_vblank_queues_work),
KUNIT_CASE(dm_test_crtc_enable_vblank_ips_restore),
KUNIT_CASE(dm_test_crtc_enable_vblank_ips_restore_replay),
/* amdgpu_dm_crtc_set_vline0_irq */
KUNIT_CASE(dm_test_crtc_set_vline0_irq_dce_noop),
KUNIT_CASE(dm_test_crtc_set_vline0_irq_error),
KUNIT_CASE(dm_test_crtc_set_vline0_irq_enable_disable),
/* amdgpu_dm_crtc_update_crtc_active_planes */
KUNIT_CASE(dm_test_crtc_update_active_planes_no_stream),
/* amdgpu_dm_crtc_count_crtc_active_planes */