From: Alex Hung <[email protected]>

[WHAT]
Add KUnit tests for amdgpu_dm_connector_init_helper() covering the
common connector defaults and attached properties, the HDMI,
DisplayPort, DVI-D, VGA, eDP and unhandled connector types, an MST
branch connector that skips the root-only properties, the content
protection property and the default HPD debounce delay.

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          | 355 ++++++++++++++++++
 2 files changed, 356 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 fa462ee0b053..a80d5426bd9f 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
@@ -3263,6 +3263,7 @@ void amdgpu_dm_connector_init_helper(struct 
amdgpu_display_manager *dm,
                }
        }
 }
+EXPORT_IF_KUNIT(amdgpu_dm_connector_init_helper);
 
 STATIC_IFN_KUNIT int amdgpu_dm_i2c_xfer(struct i2c_adapter *i2c_adap,
                              struct i2c_msg *msgs, int num)
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 4c965a6496f8..2fe55f07e464 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
@@ -27,6 +27,7 @@
 #include "amdgpu_dm.h"
 #include "amdgpu_dm_connector.h"
 #include "amdgpu_dm_backlight.h"
+#include "amdgpu_dm_hdcp.h"
 #include "amdgpu_dm_psr.h"
 #include "include/grph_object_id.h"
 #include "amdgpu_dm_kunit_test_helpers.h"
@@ -9078,6 +9079,349 @@ static void dm_test_frl_poll_retrains(struct kunit 
*test)
        KUNIT_EXPECT_EQ(test, dm_test_frl_detect_calls, 1);
 }
 
+/* Tests for amdgpu_dm_connector_init_helper() */
+
+/*
+ * Build an amdgpu_dm_connector on a kunit drm_device embedded in an
+ * amdgpu_device, with the amdgpu mode properties created exactly as
+ * amdgpu_dm_mode_config_init() does so the helper has real properties to
+ * attach. The dc_link carries a link encoder and leaves DIG mapping
+ * inflexible, so link_enc_cfg_get_link_enc() resolves without a dc instance.
+ */
+struct dm_test_init_helper_ctx {
+       struct amdgpu_device *adev;
+       struct drm_device *drm;
+       struct amdgpu_display_manager *dm;
+       struct amdgpu_dm_connector *aconnector;
+       struct dc_link *link;
+};
+
+/*
+ * Stand in for amdgpu_display_modeset_create_props(), which the test module
+ * cannot link against. Only the properties the init helper attaches are
+ * created; underscan is a plain range because only attachment is under test.
+ */
+static void dm_test_create_mode_props(struct kunit *test, struct amdgpu_device 
*adev)
+{
+       struct amdgpu_mode_info *mode_info = &adev->mode_info;
+       struct drm_device *drm = adev_to_drm(adev);
+
+       KUNIT_ASSERT_EQ(test, drm_mode_create_scaling_mode_property(drm), 0);
+
+       mode_info->underscan_property =
+               drm_property_create_range(drm, 0, "underscan", 0, 1);
+       KUNIT_ASSERT_NOT_NULL(test, mode_info->underscan_property);
+       mode_info->underscan_hborder_property =
+               drm_property_create_range(drm, 0, "underscan hborder", 0, 128);
+       KUNIT_ASSERT_NOT_NULL(test, mode_info->underscan_hborder_property);
+       mode_info->underscan_vborder_property =
+               drm_property_create_range(drm, 0, "underscan vborder", 0, 128);
+       KUNIT_ASSERT_NOT_NULL(test, mode_info->underscan_vborder_property);
+}
+
+static struct dm_test_init_helper_ctx *
+dm_test_init_helper_ctx_alloc(struct kunit *test, int connector_type)
+{
+       struct dm_test_init_helper_ctx *ctx;
+       struct device *dev;
+
+       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 | 
DRIVER_ATOMIC);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->drm);
+
+       ctx->adev = drm_to_adev(ctx->drm);
+       ctx->adev->dev = dev;
+       dm_test_create_mode_props(test, ctx->adev);
+
+       ctx->dm = &ctx->adev->dm;
+       ctx->dm->adev = ctx->adev;
+       ctx->dm->ddev = ctx->drm;
+
+       ctx->link = kunit_kzalloc(test, sizeof(*ctx->link), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, ctx->link);
+       ctx->link->link_enc = kunit_kzalloc(test, sizeof(*ctx->link->link_enc), 
GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, ctx->link->link_enc);
+       ctx->aconnector = drmm_kzalloc(ctx->drm, sizeof(*ctx->aconnector), 
GFP_KERNEL);
+
+       KUNIT_ASSERT_NOT_NULL(test, ctx->aconnector);
+       KUNIT_ASSERT_EQ(test,
+                       drmm_connector_init(ctx->drm, &ctx->aconnector->base,
+                                           &dm_test_connector_funcs, 
connector_type,
+                                           NULL), 0);
+
+       return ctx;
+}
+
+/* True when @prop is attached to the connector's mode object. */
+static bool dm_test_has_prop(struct drm_connector *connector, struct 
drm_property *prop)
+{
+       struct drm_object_properties *props = connector->base.properties;
+       int i;
+
+       if (!prop)
+               return false;
+
+       for (i = 0; i < props->count; i++)
+               if (props->properties[i] == prop)
+                       return true;
+
+       return false;
+}
+
+/**
+ * dm_test_init_helper_common_defaults - Test the connector defaults and 
properties
+ * @test: The KUnit test context
+ *
+ * The helper resets the connector state, records the link and its index, and
+ * attaches the scaling mode and the three underscan properties that every
+ * connector type receives.
+ */
+static void dm_test_init_helper_common_defaults(struct kunit *test)
+{
+       struct dm_test_init_helper_ctx *ctx =
+               dm_test_init_helper_ctx_alloc(test, DRM_MODE_CONNECTOR_HDMIA);
+       struct drm_connector *connector = &ctx->aconnector->base;
+
+       amdgpu_dm_connector_init_helper(ctx->dm, ctx->aconnector,
+                                       DRM_MODE_CONNECTOR_HDMIA, ctx->link, 3);
+
+       KUNIT_ASSERT_NOT_NULL(test, connector->state);
+       KUNIT_EXPECT_EQ(test, ctx->aconnector->connector_id, 3);
+       KUNIT_EXPECT_EQ(test, ctx->aconnector->bl_idx, -1);
+       KUNIT_EXPECT_PTR_EQ(test, ctx->aconnector->dc_link, ctx->link);
+       KUNIT_EXPECT_EQ(test, ctx->aconnector->audio_inst, -1);
+       KUNIT_EXPECT_FALSE(test, ctx->aconnector->pack_sdp_v1_3);
+       KUNIT_EXPECT_EQ(test, (int)ctx->aconnector->as_type, 
(int)ADAPTIVE_SYNC_TYPE_NONE);
+       KUNIT_EXPECT_FALSE(test, connector->interlace_allowed);
+       KUNIT_EXPECT_FALSE(test, connector->doublescan_allowed);
+       KUNIT_EXPECT_FALSE(test, connector->stereo_allowed);
+       KUNIT_EXPECT_EQ(test, connector->dpms, DRM_MODE_DPMS_OFF);
+       KUNIT_EXPECT_EQ(test, (int)ctx->aconnector->hpd.hpd, 
(int)AMDGPU_HPD_NONE);
+       KUNIT_EXPECT_EQ(test, ctx->aconnector->hdmi_hpd_debounce_delay_ms, 0);
+
+       /* The link encoder advertises no YCbCr 4:2:0 support. */
+       KUNIT_EXPECT_FALSE(test, connector->ycbcr_420_allowed);
+
+       KUNIT_EXPECT_TRUE(test,
+                         dm_test_has_prop(connector, 
ctx->drm->mode_config.scaling_mode_property));
+       KUNIT_EXPECT_TRUE(test,
+                         dm_test_has_prop(connector, 
ctx->adev->mode_info.underscan_property));
+       KUNIT_EXPECT_TRUE(test,
+                         dm_test_has_prop(connector,
+                                          
ctx->adev->mode_info.underscan_hborder_property));
+       KUNIT_EXPECT_TRUE(test,
+                         dm_test_has_prop(connector,
+                                          
ctx->adev->mode_info.underscan_vborder_property));
+}
+
+/**
+ * dm_test_init_helper_hdmi - Test the HDMI connector wiring
+ * @test: The KUnit test context
+ *
+ * HDMI polls on hotplug, takes YCbCr 4:2:0 support straight from the link
+ * encoder features and gets the max bpc, content type, colorspace, HDR
+ * metadata and VRR capable properties.
+ */
+static void dm_test_init_helper_hdmi(struct kunit *test)
+{
+       struct dm_test_init_helper_ctx *ctx =
+               dm_test_init_helper_ctx_alloc(test, DRM_MODE_CONNECTOR_HDMIA);
+       struct drm_connector *connector = &ctx->aconnector->base;
+
+       ctx->link->link_enc->features.hdmi_ycbcr420_supported = true;
+
+       amdgpu_dm_connector_init_helper(ctx->dm, ctx->aconnector,
+                                       DRM_MODE_CONNECTOR_HDMIA, ctx->link, 0);
+
+       KUNIT_EXPECT_EQ(test, (int)connector->polled, 
(int)DRM_CONNECTOR_POLL_HPD);
+       KUNIT_EXPECT_TRUE(test, connector->ycbcr_420_allowed);
+       KUNIT_ASSERT_NOT_NULL(test, connector->state);
+       KUNIT_EXPECT_EQ(test, connector->state->max_bpc, 16);
+       KUNIT_EXPECT_EQ(test, connector->state->max_requested_bpc, 16);
+       KUNIT_EXPECT_NOT_NULL(test, connector->max_bpc_property);
+       KUNIT_EXPECT_NOT_NULL(test, connector->broadcast_rgb_property);
+       KUNIT_EXPECT_NOT_NULL(test, connector->colorspace_property);
+       KUNIT_EXPECT_NOT_NULL(test, connector->vrr_capable_property);
+       KUNIT_EXPECT_TRUE(test,
+                         dm_test_has_prop(connector, 
ctx->drm->mode_config.content_type_property));
+       KUNIT_EXPECT_TRUE(test,
+                         dm_test_has_prop(connector,
+                                          
ctx->drm->mode_config.hdr_output_metadata_property));
+}
+
+/**
+ * dm_test_init_helper_dp - Test the DisplayPort connector wiring
+ * @test: The KUnit test context
+ *
+ * DisplayPort re-resolves the link encoder through the encoder configuration
+ * and takes YCbCr 4:2:0 support from its DP feature bit.
+ */
+static void dm_test_init_helper_dp(struct kunit *test)
+{
+       struct dm_test_init_helper_ctx *ctx =
+               dm_test_init_helper_ctx_alloc(test, 
DRM_MODE_CONNECTOR_DisplayPort);
+       struct drm_connector *connector = &ctx->aconnector->base;
+
+       ctx->link->link_enc->features.dp_ycbcr420_supported = true;
+
+       amdgpu_dm_connector_init_helper(ctx->dm, ctx->aconnector,
+                                       DRM_MODE_CONNECTOR_DisplayPort, 
ctx->link, 1);
+
+       KUNIT_EXPECT_EQ(test, (int)connector->polled, 
(int)DRM_CONNECTOR_POLL_HPD);
+       KUNIT_EXPECT_TRUE(test, connector->ycbcr_420_allowed);
+       KUNIT_EXPECT_NOT_NULL(test, connector->broadcast_rgb_property);
+       KUNIT_EXPECT_NOT_NULL(test, connector->colorspace_property);
+       KUNIT_EXPECT_NOT_NULL(test, connector->vrr_capable_property);
+}
+
+/**
+ * dm_test_init_helper_dp_mst_root - Test an MST branch connector skips 
properties
+ * @test: The KUnit test context
+ *
+ * A connector below an MST root gets neither the broadcast RGB, max bpc,
+ * colorspace nor VRR capable property, because those live on the root.
+ */
+static void dm_test_init_helper_dp_mst_root(struct kunit *test)
+{
+       struct dm_test_init_helper_ctx *ctx =
+               dm_test_init_helper_ctx_alloc(test, 
DRM_MODE_CONNECTOR_DisplayPort);
+       struct drm_connector *connector = &ctx->aconnector->base;
+
+       ctx->aconnector->mst_root = ctx->aconnector;
+
+       amdgpu_dm_connector_init_helper(ctx->dm, ctx->aconnector,
+                                       DRM_MODE_CONNECTOR_DisplayPort, 
ctx->link, 0);
+
+       KUNIT_EXPECT_NULL(test, connector->broadcast_rgb_property);
+       KUNIT_EXPECT_NULL(test, connector->max_bpc_property);
+       KUNIT_EXPECT_NULL(test, connector->colorspace_property);
+       KUNIT_EXPECT_NULL(test, connector->vrr_capable_property);
+}
+
+/**
+ * dm_test_init_helper_dvid - Test DVI-D polls on hotplug
+ * @test: The KUnit test context
+ */
+static void dm_test_init_helper_dvid(struct kunit *test)
+{
+       struct dm_test_init_helper_ctx *ctx =
+               dm_test_init_helper_ctx_alloc(test, DRM_MODE_CONNECTOR_DVID);
+
+       amdgpu_dm_connector_init_helper(ctx->dm, ctx->aconnector,
+                                       DRM_MODE_CONNECTOR_DVID, ctx->link, 0);
+
+       KUNIT_EXPECT_EQ(test, (int)ctx->aconnector->base.polled, 
(int)DRM_CONNECTOR_POLL_HPD);
+       KUNIT_EXPECT_FALSE(test, ctx->aconnector->base.ycbcr_420_allowed);
+}
+
+/**
+ * dm_test_init_helper_vga - Test VGA polls on connect and disconnect
+ * @test: The KUnit test context
+ */
+static void dm_test_init_helper_vga(struct kunit *test)
+{
+       struct dm_test_init_helper_ctx *ctx =
+               dm_test_init_helper_ctx_alloc(test, DRM_MODE_CONNECTOR_VGA);
+
+       amdgpu_dm_connector_init_helper(ctx->dm, ctx->aconnector,
+                                       DRM_MODE_CONNECTOR_VGA, ctx->link, 0);
+
+       KUNIT_EXPECT_EQ(test, (int)ctx->aconnector->base.polled,
+                       (int)(DRM_CONNECTOR_POLL_CONNECT | 
DRM_CONNECTOR_POLL_DISCONNECT));
+}
+
+/**
+ * dm_test_init_helper_unpolled_default - Test an unhandled type is left 
unpolled
+ * @test: The KUnit test context
+ */
+static void dm_test_init_helper_unpolled_default(struct kunit *test)
+{
+       struct dm_test_init_helper_ctx *ctx =
+               dm_test_init_helper_ctx_alloc(test, 
DRM_MODE_CONNECTOR_Composite);
+
+       amdgpu_dm_connector_init_helper(ctx->dm, ctx->aconnector,
+                                       DRM_MODE_CONNECTOR_Composite, 
ctx->link, 0);
+
+       KUNIT_EXPECT_EQ(test, (int)ctx->aconnector->base.polled, 0);
+       KUNIT_EXPECT_NULL(test, ctx->aconnector->base.colorspace_property);
+       KUNIT_EXPECT_NULL(test, ctx->aconnector->base.vrr_capable_property);
+}
+
+/**
+ * dm_test_init_helper_edp - Test eDP gets the panel type property
+ * @test: The KUnit test context
+ *
+ * eDP shares the DisplayPort colorspace and HDR property set and additionally
+ * receives the panel type property. No privacy screen provider is registered
+ * in the test environment, so the lookup fails with -ENODEV and is ignored.
+ */
+static void dm_test_init_helper_edp(struct kunit *test)
+{
+       struct dm_test_init_helper_ctx *ctx =
+               dm_test_init_helper_ctx_alloc(test, DRM_MODE_CONNECTOR_eDP);
+       struct drm_connector *connector = &ctx->aconnector->base;
+
+       amdgpu_dm_connector_init_helper(ctx->dm, ctx->aconnector,
+                                       DRM_MODE_CONNECTOR_eDP, ctx->link, 0);
+
+       KUNIT_EXPECT_EQ(test, (int)connector->polled, 0);
+       KUNIT_EXPECT_NOT_NULL(test, connector->colorspace_property);
+       KUNIT_EXPECT_NOT_NULL(test, connector->vrr_capable_property);
+       KUNIT_EXPECT_TRUE(test,
+                         dm_test_has_prop(connector, 
ctx->drm->mode_config.panel_type_property));
+       KUNIT_EXPECT_NULL(test, connector->privacy_screen);
+}
+
+/**
+ * dm_test_init_helper_hdcp_property - Test the content protection property
+ * @test: The KUnit test context
+ *
+ * The content protection property is only attached when a HDCP workqueue was
+ * created for the device.
+ */
+static void dm_test_init_helper_hdcp_property(struct kunit *test)
+{
+       struct dm_test_init_helper_ctx *ctx =
+               dm_test_init_helper_ctx_alloc(test, DRM_MODE_CONNECTOR_HDMIA);
+       struct drm_connector *connector = &ctx->aconnector->base;
+
+       ctx->adev->dm.hdcp_workqueue = kunit_kzalloc(test, sizeof(struct 
hdcp_workqueue),
+                                                    GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, ctx->adev->dm.hdcp_workqueue);
+
+       amdgpu_dm_connector_init_helper(ctx->dm, ctx->aconnector,
+                                       DRM_MODE_CONNECTOR_HDMIA, ctx->link, 0);
+
+       KUNIT_EXPECT_TRUE(test,
+                         dm_test_has_prop(connector,
+                                          
ctx->drm->mode_config.content_protection_property));
+}
+
+/**
+ * dm_test_init_helper_hpd_debounce_disabled - Test the default HPD debounce 
delay
+ * @test: The KUnit test context
+ *
+ * With the hdmi_hpd_debounce_delay_ms module parameter left at its default the
+ * debounce work is not armed and the delay stays zero.
+ */
+static void dm_test_init_helper_hpd_debounce_disabled(struct kunit *test)
+{
+       struct dm_test_init_helper_ctx *ctx =
+               dm_test_init_helper_ctx_alloc(test, DRM_MODE_CONNECTOR_HDMIA);
+
+       amdgpu_dm_connector_init_helper(ctx->dm, ctx->aconnector,
+                                       DRM_MODE_CONNECTOR_HDMIA, ctx->link, 0);
+
+       KUNIT_EXPECT_EQ(test, ctx->aconnector->hdmi_hpd_debounce_delay_ms, 0);
+}
+
 static struct kunit_case amdgpu_dm_connector_tests[] = {
        /* get_subconnector_type */
        KUNIT_CASE(dm_test_subconnector_type_none),
@@ -9475,6 +9819,17 @@ static struct kunit_case amdgpu_dm_connector_tests[] = {
        KUNIT_CASE(dm_test_frl_skips_zero_rate),
        KUNIT_CASE(dm_test_frl_poll_no_update),
        KUNIT_CASE(dm_test_frl_poll_retrains),
+       /* amdgpu_dm_connector_init_helper */
+       KUNIT_CASE(dm_test_init_helper_common_defaults),
+       KUNIT_CASE(dm_test_init_helper_hdmi),
+       KUNIT_CASE(dm_test_init_helper_dp),
+       KUNIT_CASE(dm_test_init_helper_dp_mst_root),
+       KUNIT_CASE(dm_test_init_helper_dvid),
+       KUNIT_CASE(dm_test_init_helper_vga),
+       KUNIT_CASE(dm_test_init_helper_unpolled_default),
+       KUNIT_CASE(dm_test_init_helper_edp),
+       KUNIT_CASE(dm_test_init_helper_hdcp_property),
+       KUNIT_CASE(dm_test_init_helper_hpd_debounce_disabled),
        {}
 };
 
-- 
2.43.0

Reply via email to