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
