[PATCH 40/82] drm/amd/display: Test dm_dp_mst_get_modes with a remote EDID

<[email protected]>
Newsgroups org.freedesktop.lists.amd-gfx
Message-ID <[email protected]>
From: Alex Hung <[email protected]>

[WHAT]
Add KUnit tests for dm_dp_mst_get_modes() covering the paths taken once
an EDID is available: a placeholder virtual sink is released and replaced
by a real remote sink, DC refusing that sink returns zero modes, the
content protection state is restored from the HDCP workqueue, and an EDID
read from a port still in the topology is cached with MST_REMOTE_EDID
flagged.

[HOW]
The tests reuse the MST downstream connector fixture and feed it a
minimal EDID base block. For the remote read the fixture topology gains a
branch device holding the port so drm_dp_mst_edid_read() can validate it
and return the cached EDID.

Assisted-by: Copilot:Claude-Opus-5
Reviewed-by: Bhawanpreet Lakha <[email protected]>
Signed-off-by: Alex Hung <[email protected]>
Signed-off-by: Ivan Lipski <[email protected]>
---
 .../tests/amdgpu_dm_mst_types_test.c          | 160 ++++++++++++++++++
 1 file changed, 160 insertions(+)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c
index ee4e1f3963736..b74f0611dfe06 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c
@@ -25,6 +25,7 @@
 #include "amdgpu.h"
 #include "amdgpu_mode.h"
 #include "amdgpu_dm.h"
+#include "amdgpu_dm_hdcp.h"
 #include "amdgpu_dm_mst_types.h"
 #include "amdgpu_dm_kunit_test_helpers.h"
 #include "inc/link_service.h"
@@ -1555,6 +1556,161 @@ static void dm_mst_test_get_modes_no_edid_keeps_existing_sink(struct kunit *test
 	dm_mst_test_fini_child(&child);
 }
 
+/* Minimal EDID base block: valid header, no extensions, correct checksum. */
+static const u8 dm_mst_test_edid[EDID_LENGTH] = {
+	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+	[EDID_LENGTH - 1] = 0x06,
+};
+
+/**
+ * dm_mst_test_get_modes_cached_edid_replaces_virtual_sink - Test EDID sink path
+ * @test: KUnit test context
+ *
+ * With a cached EDID and a placeholder virtual sink, dm_dp_mst_get_modes() must
+ * release the virtual sink, add a real remote sink built from the EDID, and
+ * update the connector's mode list.
+ */
+static void dm_mst_test_get_modes_cached_edid_replaces_virtual_sink(struct kunit *test)
+{
+	struct dm_mst_test_child child;
+	struct dc_sink *virtual_sink;
+	struct dc_sink *sink;
+
+	dm_mst_test_init_child(test, &child);
+
+	child.aconnector->drm_edid = drm_edid_alloc(dm_mst_test_edid, sizeof(dm_mst_test_edid));
+	KUNIT_ASSERT_NOT_NULL(test, child.aconnector->drm_edid);
+
+	virtual_sink = dm_mst_test_alloc_sink(test);
+	virtual_sink->sink_signal = SIGNAL_TYPE_VIRTUAL;
+	child.aconnector->dc_sink = virtual_sink;
+
+	sink = dm_mst_test_alloc_sink(test);
+	dm_mst_test_next_remote_sink = sink;
+
+	KUNIT_EXPECT_EQ(test, dm_dp_mst_get_modes(&child.aconnector->base), 0);
+
+	KUNIT_EXPECT_EQ(test, dm_mst_test_add_remote_sink_calls, 1U);
+	KUNIT_EXPECT_PTR_EQ(test, child.aconnector->dc_sink, sink);
+	KUNIT_EXPECT_EQ(test, sink->sink_signal, SIGNAL_TYPE_DISPLAY_PORT_MST);
+	KUNIT_EXPECT_PTR_EQ(test, sink->priv, (void *)child.aconnector);
+
+	dc_sink_release(sink);
+	dm_mst_test_fini_child(&child);
+}
+
+/**
+ * dm_mst_test_get_modes_cached_edid_sink_alloc_fails - Test EDID sink failure
+ * @test: KUnit test context
+ *
+ * If DC cannot add the remote sink built from the cached EDID,
+ * dm_dp_mst_get_modes() must bail out with zero modes.
+ */
+static void dm_mst_test_get_modes_cached_edid_sink_alloc_fails(struct kunit *test)
+{
+	struct dm_mst_test_child child;
+
+	dm_mst_test_init_child(test, &child);
+
+	child.aconnector->drm_edid = drm_edid_alloc(dm_mst_test_edid, sizeof(dm_mst_test_edid));
+	KUNIT_ASSERT_NOT_NULL(test, child.aconnector->drm_edid);
+
+	KUNIT_EXPECT_EQ(test, dm_dp_mst_get_modes(&child.aconnector->base), 0);
+
+	KUNIT_EXPECT_EQ(test, dm_mst_test_add_remote_sink_calls, 1U);
+	KUNIT_EXPECT_NULL(test, child.aconnector->dc_sink);
+
+	dm_mst_test_fini_child(&child);
+}
+
+/**
+ * dm_mst_test_get_modes_restores_hdcp_properties - Test HDCP property restore
+ * @test: KUnit test context
+ *
+ * A connector re-plugged at the same display index must have its content
+ * protection state restored from the HDCP workqueue when the remote sink is
+ * re-created.
+ */
+static void dm_mst_test_get_modes_restores_hdcp_properties(struct kunit *test)
+{
+	struct dm_connector_state *conn_state;
+	struct dm_mst_test_child child;
+	struct hdcp_workqueue *hdcp;
+	struct dc_sink *sink;
+	unsigned int index;
+
+	dm_mst_test_init_child(test, &child);
+
+	child.aconnector->drm_edid = drm_edid_alloc(dm_mst_test_edid, sizeof(dm_mst_test_edid));
+	KUNIT_ASSERT_NOT_NULL(test, child.aconnector->drm_edid);
+
+	/* drm_connector_cleanup() hands the state back to the DRM helper's kfree(). */
+	conn_state = kzalloc_obj(*conn_state);
+	hdcp = kunit_kzalloc(test, sizeof(*hdcp), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, conn_state);
+	KUNIT_ASSERT_NOT_NULL(test, hdcp);
+
+	index = child.aconnector->base.index;
+	hdcp->hdcp_content_type[index] = DRM_MODE_HDCP_CONTENT_TYPE1;
+	hdcp->content_protection[index] = DRM_MODE_CONTENT_PROTECTION_ENABLED;
+	child.adev->dm.hdcp_workqueue = hdcp;
+	child.aconnector->base.state = &conn_state->base;
+
+	sink = dm_mst_test_alloc_sink(test);
+	dm_mst_test_next_remote_sink = sink;
+
+	KUNIT_EXPECT_EQ(test, dm_dp_mst_get_modes(&child.aconnector->base), 0);
+
+	KUNIT_EXPECT_PTR_EQ(test, child.aconnector->dc_sink, sink);
+	KUNIT_EXPECT_EQ(test, child.aconnector->base.state->hdcp_content_type,
+			(unsigned int)DRM_MODE_HDCP_CONTENT_TYPE1);
+	KUNIT_EXPECT_EQ(test, child.aconnector->base.state->content_protection,
+			(unsigned int)DRM_MODE_CONTENT_PROTECTION_ENABLED);
+
+	dc_sink_release(sink);
+	dm_mst_test_fini_child(&child);
+}
+
+/**
+ * dm_mst_test_get_modes_reads_remote_edid - Test remote EDID caching
+ * @test: KUnit test context
+ *
+ * When the MST port is still in the topology, dm_dp_mst_get_modes() must read
+ * its EDID, cache it on the connector and flag MST_REMOTE_EDID.
+ */
+static void dm_mst_test_get_modes_reads_remote_edid(struct kunit *test)
+{
+	struct drm_dp_mst_branch *mstb;
+	struct dm_mst_test_child child;
+	struct dc_sink *sink;
+
+	dm_mst_test_init_child(test, &child);
+
+	/* Minimal topology so drm_dp_mst_edid_read() can validate the port. */
+	mstb = kunit_kzalloc(test, sizeof(*mstb), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, mstb);
+	mstb->mgr = &child.root->mst_mgr;
+	INIT_LIST_HEAD(&mstb->ports);
+	list_add(&child.port->next, &mstb->ports);
+	kref_init(&child.port->topology_kref);
+	child.root->mst_mgr.mst_primary = mstb;
+
+	child.port->cached_edid = drm_edid_alloc(dm_mst_test_edid, sizeof(dm_mst_test_edid));
+	KUNIT_ASSERT_NOT_NULL(test, child.port->cached_edid);
+
+	sink = dm_mst_test_alloc_sink(test);
+	dm_mst_test_next_remote_sink = sink;
+
+	KUNIT_EXPECT_EQ(test, dm_dp_mst_get_modes(&child.aconnector->base), 0);
+
+	KUNIT_EXPECT_NOT_NULL(test, child.aconnector->drm_edid);
+	KUNIT_EXPECT_EQ(test, child.aconnector->mst_status & MST_REMOTE_EDID, (int)MST_REMOTE_EDID);
+	KUNIT_EXPECT_PTR_EQ(test, child.aconnector->dc_sink, sink);
+
+	dc_sink_release(sink);
+	dm_mst_test_fini_child(&child);
+}
+
 /*
  * Sideband connector with a live topology manager and the DOWN_REP ready bit
  * armed, so dm_handle_mst_sideband_msg_ready_event() reaches its ack path.
@@ -1707,6 +1863,10 @@ static struct kunit_case dm_mst_types_test_cases[] = {
 	KUNIT_CASE(dm_mst_test_get_modes_no_edid_adds_default_sink),
 	KUNIT_CASE(dm_mst_test_get_modes_no_edid_sink_alloc_fails),
 	KUNIT_CASE(dm_mst_test_get_modes_no_edid_keeps_existing_sink),
+	KUNIT_CASE(dm_mst_test_get_modes_cached_edid_replaces_virtual_sink),
+	KUNIT_CASE(dm_mst_test_get_modes_cached_edid_sink_alloc_fails),
+	KUNIT_CASE(dm_mst_test_get_modes_restores_hdcp_properties),
+	KUNIT_CASE(dm_mst_test_get_modes_reads_remote_edid),
 	/* CONFIG_DRM_AMD_DC_FP disabled public paths */
 #if !defined(CONFIG_DRM_AMD_DC_FP)
 	KUNIT_CASE(dm_mst_test_fp_guarded_public_stubs),
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.