From: Alex Hung <[email protected]>

[WHAT]
Add KUnit tests for amdgpu_dm_connector_init() covering a HDMI link
that is fully brought up, a DVI link that maps to a DVI-D connector
without a CEC notifier, and the get_modes() helper hook it installs.

Assisted-by: Copilot:Claude-Opus-5
Reviewed-by: Bhawanpreet Lakha <[email protected]>
Signed-off-by: Alex Hung <[email protected]>
Signed-off-by: Chenyu Chen <[email protected]>
---
 .../display/amdgpu_dm/amdgpu_dm_connector.c   |   1 +
 .../tests/amdgpu_dm_connector_test.c          | 190 ++++++++++++++++++
 2 files changed, 191 insertions(+)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
index fdfb6f72b9fc..7056bb6f7e26 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
@@ -3446,6 +3446,7 @@ int amdgpu_dm_connector_init(struct 
amdgpu_display_manager *dm,
        }
        return res;
 }
+EXPORT_IF_KUNIT(amdgpu_dm_connector_init);
 
 static int dm_force_atomic_commit(struct drm_connector *connector)
 {
diff --git 
a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c
index d8a6d6ae90ec..d63529336d69 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c
@@ -16,6 +16,7 @@
 #include <drm/drm_managed.h>
 #include <drm/drm_mode_object.h>
 #include <drm/drm_modes.h>
+#include <drm/drm_modeset_helper_vtables.h>
 #include <drm/drm_property.h>
 #include <linux/hdmi.h>
 #include <linux/i2c.h>
@@ -9860,6 +9861,191 @@ static void dm_test_fs_caps_disables_replay(struct 
kunit *test)
        KUNIT_EXPECT_FALSE(test, 
ctx->link->replay_settings.replay_feature_enabled);
 }
 
+/* Tests for amdgpu_dm_connector_init() and the get_modes() helper hook */
+
+/*
+ * amdgpu_dm_create_i2c() parents the adapter on adev->pdev->dev, so
+ * devm_i2c_add_adapter() needs a registered device there. Provide one by
+ * registering the device embedded in a KUnit allocated pci_dev.
+ */
+static void dm_test_conn_init_release_dev(struct device *dev)
+{
+       /* Backing storage is KUnit managed, so there is nothing to free. */
+}
+
+static void dm_test_conn_init_unregister_dev(void *data)
+{
+       device_unregister(data);
+}
+
+struct dm_test_conn_init_ctx {
+       struct amdgpu_device *adev;
+       struct drm_device *drm;
+       struct amdgpu_display_manager *dm;
+       struct amdgpu_dm_connector *aconnector;
+       struct amdgpu_encoder *aencoder;
+       struct dc_link *link;
+};
+
+static struct dm_test_conn_init_ctx *
+dm_test_conn_init_ctx_alloc(struct kunit *test, enum signal_type signal)
+{
+       struct dm_test_conn_init_ctx *ctx;
+       struct link_service *link_srv;
+       struct dc_context *dc_ctx;
+       struct ddc_service *ddc;
+       struct pci_dev *pdev;
+       struct device *dev;
+       struct dc *dc;
+
+       ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+       dev = drm_kunit_helper_alloc_device(test);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+       ctx->drm = __drm_kunit_helper_alloc_drm_device(test, dev,
+                                                      sizeof(struct 
amdgpu_device),
+                                                      offsetof(struct 
amdgpu_device, ddev),
+                                                      DRIVER_MODESET);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->drm);
+       ctx->adev = drm_to_adev(ctx->drm);
+       ctx->adev->dev = dev;
+       ctx->adev->mode_info.num_crtc = 1;
+       dm_test_create_mode_props(test, ctx->adev);
+
+       pdev = kunit_kzalloc(test, sizeof(*pdev), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, pdev);
+       device_initialize(&pdev->dev);
+       pdev->dev.parent = dev;
+       pdev->dev.release = dm_test_conn_init_release_dev;
+       KUNIT_ASSERT_EQ(test, dev_set_name(&pdev->dev, "dm-test-i2c-parent"), 
0);
+       KUNIT_ASSERT_EQ(test, device_add(&pdev->dev), 0);
+       KUNIT_ASSERT_EQ(test,
+                       kunit_add_action_or_reset(test, 
dm_test_conn_init_unregister_dev,
+                                                 &pdev->dev), 0);
+       ctx->adev->pdev = pdev;
+
+       ctx->dm = &ctx->adev->dm;
+       ctx->dm->adev = ctx->adev;
+       ctx->dm->ddev = ctx->drm;
+
+       dc_ctx = kunit_kzalloc(test, sizeof(*dc_ctx), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, dc_ctx);
+       dc_ctx->driver_context = ctx->adev;
+
+       link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, link_srv);
+       link_srv->dp_get_encoding_format = dm_test_gm_enc_8b10b;
+
+       dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, dc);
+       dc->ctx = dc_ctx;
+       dc->link_srv = link_srv;
+
+       ctx->link = kunit_kzalloc(test, sizeof(*ctx->link), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, ctx->link);
+       ctx->link->connector_signal = signal;
+       ctx->link->dc = dc;
+       ctx->link->link_enc = kunit_kzalloc(test, sizeof(*ctx->link->link_enc), 
GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, ctx->link->link_enc);
+
+       ddc = kunit_kzalloc(test, sizeof(*ddc), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, ddc);
+       ddc->ctx = dc_ctx;
+       ddc->link = ctx->link;
+       ctx->link->ddc = ddc;
+
+       dc->links[0] = ctx->link;
+       dc->link_count = 1;
+       ctx->dm->dc = dc;
+
+       /* amdgpu_dm_connector_destroy() and amdgpu_dm_encoder_destroy() 
kfree() these. */
+       ctx->aconnector = kzalloc_obj(*ctx->aconnector);
+       KUNIT_ASSERT_NOT_NULL(test, ctx->aconnector);
+       ctx->aencoder = kzalloc_obj(*ctx->aencoder);
+       KUNIT_ASSERT_NOT_NULL(test, ctx->aencoder);
+       KUNIT_ASSERT_EQ(test, amdgpu_dm_encoder_init(ctx->drm, ctx->aencoder, 
0), 0);
+
+       return ctx;
+}
+
+/**
+ * dm_test_conn_init_hdmi - Test a HDMI connector is fully brought up
+ * @test: The KUnit test context
+ *
+ * The DC link is bound to the connector, an i2c adapter is created and
+ * registered as the DDC bus, the DRM connector is initialized with the DM
+ * funcs and helpers, the encoder is attached and the CEC notifier registered.
+ */
+static void dm_test_conn_init_hdmi(struct kunit *test)
+{
+       struct dm_test_conn_init_ctx *ctx =
+               dm_test_conn_init_ctx_alloc(test, SIGNAL_TYPE_HDMI_TYPE_A);
+       struct drm_connector *connector = &ctx->aconnector->base;
+
+       KUNIT_ASSERT_EQ(test,
+                       amdgpu_dm_connector_init(ctx->dm, ctx->aconnector, 0,
+                                                ctx->aencoder), 0);
+
+       KUNIT_EXPECT_PTR_EQ(test, ctx->link->priv, ctx->aconnector);
+       KUNIT_ASSERT_NOT_NULL(test, ctx->aconnector->i2c);
+       KUNIT_EXPECT_PTR_EQ(test, connector->ddc, &ctx->aconnector->i2c->base);
+       KUNIT_EXPECT_EQ(test, connector->connector_type, 
DRM_MODE_CONNECTOR_HDMIA);
+       KUNIT_EXPECT_NOT_NULL(test, connector->helper_private);
+       KUNIT_EXPECT_PTR_EQ(test, ctx->aconnector->dc_link, ctx->link);
+       KUNIT_EXPECT_EQ(test, ctx->aconnector->connector_id, 0);
+       KUNIT_EXPECT_EQ(test, connector->possible_encoders,
+                       drm_encoder_mask(&ctx->aencoder->base));
+       KUNIT_EXPECT_NOT_NULL(test, ctx->aconnector->notifier);
+}
+
+/**
+ * dm_test_conn_init_dvi - Test a DVI link maps to a DVI-D connector
+ * @test: The KUnit test context
+ *
+ * A single link DVI signal is not HDMI, so no CEC notifier is registered.
+ */
+static void dm_test_conn_init_dvi(struct kunit *test)
+{
+       struct dm_test_conn_init_ctx *ctx =
+               dm_test_conn_init_ctx_alloc(test, SIGNAL_TYPE_DVI_SINGLE_LINK);
+
+       KUNIT_ASSERT_EQ(test,
+                       amdgpu_dm_connector_init(ctx->dm, ctx->aconnector, 0,
+                                                ctx->aencoder), 0);
+
+       KUNIT_EXPECT_EQ(test, ctx->aconnector->base.connector_type,
+                       DRM_MODE_CONNECTOR_DVID);
+       KUNIT_EXPECT_NULL(test, ctx->aconnector->notifier);
+}
+
+/**
+ * dm_test_conn_init_get_modes_hook - Test the installed get_modes helper
+ * @test: The KUnit test context
+ *
+ * The connector helper funcs that amdgpu_dm_connector_init() installs forward
+ * mode enumeration to amdgpu_dm_connector_get_modes(), which synthesizes the
+ * no-EDID fallback modes.
+ */
+static void dm_test_conn_init_get_modes_hook(struct kunit *test)
+{
+       struct dm_test_conn_init_ctx *ctx =
+               dm_test_conn_init_ctx_alloc(test, SIGNAL_TYPE_HDMI_TYPE_A);
+       const struct drm_connector_helper_funcs *helper;
+       struct drm_connector *connector = &ctx->aconnector->base;
+
+       KUNIT_ASSERT_EQ(test,
+                       amdgpu_dm_connector_init(ctx->dm, ctx->aconnector, 0,
+                                                ctx->aencoder), 0);
+
+       helper = connector->helper_private;
+       KUNIT_ASSERT_NOT_NULL(test, helper);
+       KUNIT_ASSERT_NOT_NULL(test, helper->get_modes);
+
+       KUNIT_EXPECT_GT(test, helper->get_modes(connector), 0);
+       KUNIT_EXPECT_GT(test, ctx->aconnector->num_modes, 0);
+}
+
 static struct kunit_case amdgpu_dm_connector_tests[] = {
        /* get_subconnector_type */
        KUNIT_CASE(dm_test_subconnector_type_none),
@@ -10281,6 +10467,10 @@ static struct kunit_case amdgpu_dm_connector_tests[] = 
{
        KUNIT_CASE(dm_test_fs_caps_force_min_hz_quirk),
        KUNIT_CASE(dm_test_fs_caps_mccs_clears_capability),
        KUNIT_CASE(dm_test_fs_caps_disables_replay),
+       /* amdgpu_dm_connector_init */
+       KUNIT_CASE(dm_test_conn_init_hdmi),
+       KUNIT_CASE(dm_test_conn_init_dvi),
+       KUNIT_CASE(dm_test_conn_init_get_modes_hook),
        {}
 };
 
-- 
2.43.0

Reply via email to