From: Alex Hung <[email protected]>

Add KUnit coverage for amdgpu_dm_hpd_init() and amdgpu_dm_hpd_fini():
empty connector list, the per-connector HW fallback path, the analog
polling enable/disable path, and the base-driver irq-ref path. Export
both functions for KUnit and add shared drm_connector test helpers.

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 |   2 +
 .../amdgpu_dm/tests/amdgpu_dm_irq_test.c      | 187 +++++++++++++++++-
 2 files changed, 183 insertions(+), 6 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 5af7f0bebdb8..8e18ed9f9501 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
@@ -1003,6 +1003,7 @@ void amdgpu_dm_hpd_init(struct amdgpu_device *adev)
        if (use_polling)
                drm_kms_helper_poll_init(dev);
 }
+EXPORT_IF_KUNIT(amdgpu_dm_hpd_init);

 /**
  * amdgpu_dm_hpd_fini - hpd tear down callback.
@@ -1056,6 +1057,7 @@ void amdgpu_dm_hpd_fini(struct amdgpu_device *adev)
        if (dev->mode_config.poll_enabled)
                drm_kms_helper_poll_fini(dev);
 }
+EXPORT_IF_KUNIT(amdgpu_dm_hpd_fini);

 /* ========== HPD handling ========== */
 static void force_connector_state(
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 7537d30cd983..8c58523ef1bb 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
@@ -154,6 +154,19 @@ static void dm_test_destroy_hpd_rx_wq(void *data)
        kfree(ctx->wq);
 }

+static const struct drm_connector_funcs dm_test_connector_funcs = {
+       .reset = drm_atomic_helper_connector_reset,
+       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .destroy = drm_connector_cleanup,
+};
+
+static void dm_test_connector_cleanup(void *data)
+{
+       drm_connector_cleanup(data);
+}
+
 /* Tests for amdgpu_dm_hpd_to_dal_irq_source() */

 /**
@@ -917,11 +930,9 @@ static void 
dm_test_get_crtc_by_otg_inst_returns_match(struct kunit *test)
        acrtc_b->otg_inst = 3;

        list_add_tail(&acrtc_a->base.head, &drm->mode_config.crtc_list);
-       KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, 
dm_test_crtc_list_del,
-                                                       acrtc_a), 0);
+       KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, 
dm_test_crtc_list_del, acrtc_a), 0);
        list_add_tail(&acrtc_b->base.head, &drm->mode_config.crtc_list);
-       KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, 
dm_test_crtc_list_del,
-                                                       acrtc_b), 0);
+       KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, 
dm_test_crtc_list_del, acrtc_b), 0);

        KUNIT_EXPECT_PTR_EQ(test, amdgpu_dm_get_crtc_by_otg_inst(adev, 3), 
acrtc_b);
 }
@@ -946,8 +957,7 @@ static void 
dm_test_get_crtc_by_otg_inst_returns_null(struct kunit *test)
        acrtc->otg_inst = 2;

        list_add_tail(&acrtc->base.head, &drm->mode_config.crtc_list);
-       KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, 
dm_test_crtc_list_del,
-                                                       acrtc), 0);
+       KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, 
dm_test_crtc_list_del, acrtc), 0);

        KUNIT_EXPECT_NULL(test, amdgpu_dm_get_crtc_by_otg_inst(adev, 5));
 }
@@ -1733,6 +1743,165 @@ static void dm_test_outbox_init_null_dc(struct kunit 
*test)
        amdgpu_dm_outbox_init(adev);
 }

+/* Tests for amdgpu_dm_hpd_init()/amdgpu_dm_hpd_fini() */
+
+/**
+ * dm_test_hpd_init_empty_connectors - Test HPD init with no connectors
+ * @test: The KUnit test context
+ */
+static void dm_test_hpd_init_empty_connectors(struct kunit *test)
+{
+       struct amdgpu_device *adev;
+
+       adev = dm_kunit_alloc_adev(test);
+
+       /*
+        * With an empty connector list the per-connector loop is skipped and
+        * the initial clear loop relies on dc_interrupt_set() being a no-op
+        * for a NULL dc, so init must complete without touching the DC.
+        */
+       amdgpu_dm_hpd_init(adev);
+}
+
+/**
+ * dm_test_hpd_fini_empty_connectors - Test HPD fini with no connectors
+ * @test: The KUnit test context
+ */
+static void dm_test_hpd_fini_empty_connectors(struct kunit *test)
+{
+       struct amdgpu_device *adev;
+
+       adev = dm_kunit_alloc_adev(test);
+
+       /* Empty connector list and disabled polling: fini is a safe no-op. */
+       amdgpu_dm_hpd_fini(adev);
+}
+
+/**
+ * dm_test_hpd_init_fini_with_connectors - Test HPD init/fini walk connectors
+ * @test: The KUnit test context
+ */
+static void dm_test_hpd_init_fini_with_connectors(struct kunit *test)
+{
+       struct amdgpu_dm_connector *aconn;
+       struct drm_connector *wbconn;
+       struct amdgpu_device *adev;
+       struct dc_link *link;
+
+       adev = dm_kunit_alloc_adev(test);
+
+       /*
+        * num_hpd = 0 forces irq_type >= num_hpd so the loop takes the HW
+        * fallback (dc_interrupt_set()) instead of amdgpu_irq_get(); with a
+        * NULL dc that fallback is a safe no-op.
+        */
+       adev->mode_info.num_hpd = 0;
+
+       /* A writeback connector must be skipped by the per-connector loop. */
+       wbconn = kunit_kzalloc(test, sizeof(*wbconn), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, wbconn);
+       KUNIT_ASSERT_EQ(test, drm_connector_init(&adev->ddev, wbconn, 
&dm_test_connector_funcs,
+                                                DRM_MODE_CONNECTOR_WRITEBACK), 
0);
+       KUNIT_ASSERT_EQ(test,
+                       kunit_add_action_or_reset(test, 
dm_test_connector_cleanup, wbconn), 0);
+
+       /* A DisplayPort connector with a dc_link exercises the loop body. */
+       aconn = kunit_kzalloc(test, sizeof(*aconn), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, aconn);
+       KUNIT_ASSERT_EQ(test, drm_connector_init(&adev->ddev, &aconn->base,
+                                                &dm_test_connector_funcs,
+                                                
DRM_MODE_CONNECTOR_DisplayPort), 0);
+       KUNIT_ASSERT_EQ(test,
+                       kunit_add_action_or_reset(test, 
dm_test_connector_cleanup, &aconn->base), 0);
+
+       link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link);
+       link->irq_source_hpd = DC_IRQ_SOURCE_HPD1;
+       link->irq_source_hpd_rx = DC_IRQ_SOURCE_HPD1RX;
+       aconn->dc_link = link;
+
+       /* DC is absent (NULL), so the HW writes are NULL-safe no-ops. */
+       amdgpu_dm_hpd_init(adev);
+       amdgpu_dm_hpd_fini(adev);
+}
+
+/**
+ * dm_test_hpd_init_fini_analog_connector - Test HPD init/fini analog polling 
path
+ * @test: The KUnit test context
+ */
+static void dm_test_hpd_init_fini_analog_connector(struct kunit *test)
+{
+       struct amdgpu_dm_connector *aconn;
+       struct amdgpu_device *adev;
+       struct dc_link *link;
+
+       adev = dm_kunit_alloc_adev(test);
+       adev->mode_info.num_hpd = 0;
+
+       aconn = kunit_kzalloc(test, sizeof(*aconn), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, aconn);
+       KUNIT_ASSERT_EQ(test, drm_connector_init(&adev->ddev, &aconn->base,
+                                                &dm_test_connector_funcs,
+                                                DRM_MODE_CONNECTOR_VGA), 0);
+       KUNIT_ASSERT_EQ(test,
+                       kunit_add_action_or_reset(test, 
dm_test_connector_cleanup, &aconn->base), 0);
+
+       link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link);
+       link->irq_source_hpd = DC_IRQ_SOURCE_HPD1;
+       link->irq_source_hpd_rx = DC_IRQ_SOURCE_HPD1RX;
+       /* An analog connector id makes the loop request polling. */
+       link->link_id.id = CONNECTOR_ID_VGA;
+       aconn->dc_link = link;
+
+       /* use_polling becomes true, so init must enable KMS polling. */
+       amdgpu_dm_hpd_init(adev);
+       KUNIT_EXPECT_TRUE(test, adev->ddev.mode_config.poll_enabled);
+
+       /* fini must tear the polling back down. */
+       amdgpu_dm_hpd_fini(adev);
+       KUNIT_EXPECT_FALSE(test, adev->ddev.mode_config.poll_enabled);
+}
+
+/**
+ * dm_test_hpd_init_fini_irq_ref - Test HPD init/fini base-driver irq ref path
+ * @test: The KUnit test context
+ */
+static void dm_test_hpd_init_fini_irq_ref(struct kunit *test)
+{
+       struct amdgpu_dm_connector *aconn;
+       struct amdgpu_device *adev;
+       struct dc_link *link;
+
+       adev = dm_kunit_alloc_adev(test);
+
+       /*
+        * num_hpd >= 1 makes irq_type (0) < num_hpd, so the loop takes the
+        * amdgpu_irq_get()/amdgpu_irq_put() branch instead of the
+        * dc_interrupt_set() fallback. The mock device has irq.installed ==
+        * false, so both calls fail early with -ENOENT (logging an error)
+        * without touching the base-driver irq state.
+        */
+       adev->mode_info.num_hpd = 1;
+
+       aconn = kunit_kzalloc(test, sizeof(*aconn), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, aconn);
+       KUNIT_ASSERT_EQ(test, drm_connector_init(&adev->ddev, &aconn->base,
+                                                &dm_test_connector_funcs,
+                                                
DRM_MODE_CONNECTOR_DisplayPort), 0);
+       KUNIT_ASSERT_EQ(test,
+                       kunit_add_action_or_reset(test, 
dm_test_connector_cleanup, &aconn->base), 0);
+
+       link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link);
+       link->irq_source_hpd = DC_IRQ_SOURCE_HPD1;
+       link->irq_source_hpd_rx = DC_IRQ_SOURCE_INVALID;
+       aconn->dc_link = link;
+
+       amdgpu_dm_hpd_init(adev);
+       amdgpu_dm_hpd_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),
@@ -1829,6 +1998,12 @@ static struct kunit_case amdgpu_dm_irq_tests[] = {
        KUNIT_CASE(dm_test_set_dmub_trace_irq_state_null_dc),
        /* amdgpu_dm_outbox_init */
        KUNIT_CASE(dm_test_outbox_init_null_dc),
+       /* amdgpu_dm_hpd_init/amdgpu_dm_hpd_fini */
+       KUNIT_CASE(dm_test_hpd_init_empty_connectors),
+       KUNIT_CASE(dm_test_hpd_fini_empty_connectors),
+       KUNIT_CASE(dm_test_hpd_init_fini_with_connectors),
+       KUNIT_CASE(dm_test_hpd_init_fini_analog_connector),
+       KUNIT_CASE(dm_test_hpd_init_fini_irq_ref),
        {}
 };

--
2.55.0

Reply via email to