[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 <[email protected]>
Signed-off-by: Alex Hung <[email protected]>
---
 .../amd/display/amdgpu_dm/amdgpu_dm_crtc.c    |  40 ++++---
 .../amd/display/amdgpu_dm/amdgpu_dm_crtc.h    |   1 +
 .../amdgpu_dm/tests/amdgpu_dm_crtc_test.c     | 101 ++++++++++++++++++
 3 files changed, 129 insertions(+), 13 deletions(-)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
index 2de758aceda5..4b8530d734e5 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
@@ -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))
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.h 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.h
index 7de79796a56d..91fdd262c2d2 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.h
@@ -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
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 a88175d99092..544445fbc770 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
@@ -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 */
-- 
2.43.0

Reply via email to