[PATCH 75/82] drm/amd/display: Cover hdmi_frl_status_polling_work

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

Add KUnit tests for hdmi_frl_status_polling_work() covering the
no-links, no-local-sink, non-HDMI and zero-FRL-rate skip cases, plus
the poll paths that leave the link unchanged or trigger a retrain.

Assisted-by: Copilot:Claude-Opus-4.8
Reviewed-by: Alex Hung <[email protected]>
Signed-off-by: Bhawanpreet Lakha <[email protected]>
Signed-off-by: Ivan Lipski <[email protected]>
---
 .../display/amdgpu_dm/amdgpu_dm_connector.c   |   1 +
 .../tests/amdgpu_dm_connector_test.c          | 207 ++++++++++++++++++
 2 files changed, 208 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 13f9ed0220c37..837ab9cfd35f6 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
@@ -3121,6 +3121,7 @@ void hdmi_frl_status_polling_work(struct work_struct *work)
 			   &dm->hdmi_frl_status_polling_work,
 			   msecs_to_jiffies(dm->hdmi_frl_status_polling_delay_ms));
 }
+EXPORT_IF_KUNIT(hdmi_frl_status_polling_work);
 
 void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm,
 				     struct amdgpu_dm_connector *aconnector,
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 5e5c372e9e01e..24f55432e3262 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
@@ -7988,6 +7988,206 @@ static void dm_test_update_scaling_underscan(struct kunit *test)
 	KUNIT_EXPECT_EQ(test, stream->dst.height, 1048);
 }
 
+/* Tests for hdmi_frl_status_polling_work() */
+
+static int dm_test_frl_poll_calls;
+static int dm_test_frl_detect_calls;
+
+static bool dm_test_frl_poll_true(struct dc_link *link)
+{
+	dm_test_frl_poll_calls++;
+	return true;
+}
+
+static bool dm_test_frl_poll_false(struct dc_link *link)
+{
+	dm_test_frl_poll_calls++;
+	return false;
+}
+
+static bool dm_test_frl_detect(struct dc_link *link, enum dc_detect_reason reason)
+{
+	dm_test_frl_detect_calls++;
+	return true;
+}
+
+struct dm_test_frl_ctx {
+	struct amdgpu_display_manager *dm;
+	struct dc *dc;
+	struct link_service *link_srv;
+};
+
+/*
+ * Build a display manager whose polling work re-arms on a real workqueue. The
+ * dc starts with an empty link array and link_srv hooks that count poll and
+ * detect calls, so a test can assert exactly how far a link progresses. A large
+ * re-arm delay keeps the requeued work dormant until the test cancels it.
+ */
+static struct dm_test_frl_ctx *dm_test_frl_ctx_alloc(struct kunit *test)
+{
+	struct dm_test_frl_ctx *ctx;
+
+	ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+	ctx->dm = kunit_kzalloc(test, sizeof(*ctx->dm), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, ctx->dm);
+	ctx->dc = kunit_kzalloc(test, sizeof(*ctx->dc), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, ctx->dc);
+	ctx->link_srv = kunit_kzalloc(test, sizeof(*ctx->link_srv), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, ctx->link_srv);
+
+	ctx->link_srv->hdmi_frl_poll_status_flag = dm_test_frl_poll_true;
+	ctx->link_srv->detect_link = dm_test_frl_detect;
+	ctx->dc->link_srv = ctx->link_srv;
+	ctx->dm->dc = ctx->dc;
+	mutex_init(&ctx->dm->dc_lock);
+	INIT_DELAYED_WORK(&ctx->dm->hdmi_frl_status_polling_work, hdmi_frl_status_polling_work);
+	ctx->dm->hdmi_frl_status_polling_delay_ms = 100000;
+	ctx->dm->hdmi_frl_status_polling_wq = system_wq;
+
+	dm_test_frl_poll_calls = 0;
+	dm_test_frl_detect_calls = 0;
+
+	return ctx;
+}
+
+/* Allocate a dc_link, wire its dc back-pointer and register it at index 0. */
+static struct dc_link *dm_test_frl_add_link(struct kunit *test, struct dm_test_frl_ctx *ctx)
+{
+	struct dc_link *link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+
+	KUNIT_ASSERT_NOT_NULL(test, link);
+	link->dc = ctx->dc;
+	ctx->dc->links[0] = link;
+
+	return link;
+}
+
+/* Register a link that satisfies every guard up to the poll status check. */
+static struct dc_link *dm_test_frl_add_hdmi_link(struct kunit *test, struct dm_test_frl_ctx *ctx)
+{
+	struct dc_link *link = dm_test_frl_add_link(test, ctx);
+
+	link->local_sink = kunit_kzalloc(test, sizeof(*link->local_sink), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, link->local_sink);
+	link->connector_signal = SIGNAL_TYPE_HDMI_TYPE_A;
+	link->frl_link_settings.frl_link_rate = HDMI_FRL_LINK_RATE_3GBPS;
+
+	return link;
+}
+
+/* Run the work once directly, then cancel and tear down the re-armed work. */
+static void dm_test_frl_run(struct dm_test_frl_ctx *ctx)
+{
+	hdmi_frl_status_polling_work(&ctx->dm->hdmi_frl_status_polling_work.work);
+	cancel_delayed_work_sync(&ctx->dm->hdmi_frl_status_polling_work);
+}
+
+/**
+ * dm_test_frl_no_links - Test an empty link array is a no-op
+ * @test: The KUnit test context
+ *
+ * With no links registered the loop body never runs, so neither the poll nor
+ * the detect hook is invoked.
+ */
+static void dm_test_frl_no_links(struct kunit *test)
+{
+	struct dm_test_frl_ctx *ctx = dm_test_frl_ctx_alloc(test);
+
+	dm_test_frl_run(ctx);
+
+	KUNIT_EXPECT_EQ(test, dm_test_frl_poll_calls, 0);
+	KUNIT_EXPECT_EQ(test, dm_test_frl_detect_calls, 0);
+}
+
+/**
+ * dm_test_frl_skips_no_local_sink - Test a link without a local sink is skipped
+ * @test: The KUnit test context
+ */
+static void dm_test_frl_skips_no_local_sink(struct kunit *test)
+{
+	struct dm_test_frl_ctx *ctx = dm_test_frl_ctx_alloc(test);
+
+	dm_test_frl_add_link(test, ctx);
+
+	dm_test_frl_run(ctx);
+
+	KUNIT_EXPECT_EQ(test, dm_test_frl_poll_calls, 0);
+}
+
+/**
+ * dm_test_frl_skips_non_hdmi - Test a non-HDMI link is skipped
+ * @test: The KUnit test context
+ */
+static void dm_test_frl_skips_non_hdmi(struct kunit *test)
+{
+	struct dm_test_frl_ctx *ctx = dm_test_frl_ctx_alloc(test);
+	struct dc_link *link = dm_test_frl_add_link(test, ctx);
+
+	link->local_sink = kunit_kzalloc(test, sizeof(*link->local_sink), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, link->local_sink);
+	link->connector_signal = SIGNAL_TYPE_DISPLAY_PORT;
+
+	dm_test_frl_run(ctx);
+
+	KUNIT_EXPECT_EQ(test, dm_test_frl_poll_calls, 0);
+}
+
+/**
+ * dm_test_frl_skips_zero_rate - Test a link with no FRL rate is skipped
+ * @test: The KUnit test context
+ */
+static void dm_test_frl_skips_zero_rate(struct kunit *test)
+{
+	struct dm_test_frl_ctx *ctx = dm_test_frl_ctx_alloc(test);
+	struct dc_link *link = dm_test_frl_add_hdmi_link(test, ctx);
+
+	link->frl_link_settings.frl_link_rate = 0;
+
+	dm_test_frl_run(ctx);
+
+	KUNIT_EXPECT_EQ(test, dm_test_frl_poll_calls, 0);
+}
+
+/**
+ * dm_test_frl_poll_no_update - Test a clear poll status skips retraining
+ * @test: The KUnit test context
+ *
+ * The poll hook is reached but reports no change, so dc_link_detect() is not
+ * called.
+ */
+static void dm_test_frl_poll_no_update(struct kunit *test)
+{
+	struct dm_test_frl_ctx *ctx = dm_test_frl_ctx_alloc(test);
+
+	ctx->link_srv->hdmi_frl_poll_status_flag = dm_test_frl_poll_false;
+	dm_test_frl_add_hdmi_link(test, ctx);
+
+	dm_test_frl_run(ctx);
+
+	KUNIT_EXPECT_EQ(test, dm_test_frl_poll_calls, 1);
+	KUNIT_EXPECT_EQ(test, dm_test_frl_detect_calls, 0);
+}
+
+/**
+ * dm_test_frl_poll_retrains - Test a set poll status triggers a retrain
+ * @test: The KUnit test context
+ *
+ * A poll status change drives dc_link_detect() with DETECT_REASON_RETRAIN.
+ */
+static void dm_test_frl_poll_retrains(struct kunit *test)
+{
+	struct dm_test_frl_ctx *ctx = dm_test_frl_ctx_alloc(test);
+
+	dm_test_frl_add_hdmi_link(test, ctx);
+
+	dm_test_frl_run(ctx);
+
+	KUNIT_EXPECT_EQ(test, dm_test_frl_poll_calls, 1);
+	KUNIT_EXPECT_EQ(test, dm_test_frl_detect_calls, 1);
+}
+
 static struct kunit_case amdgpu_dm_connector_tests[] = {
 	/* get_subconnector_type */
 	KUNIT_CASE(dm_test_subconnector_type_none),
@@ -8348,6 +8548,13 @@ static struct kunit_case amdgpu_dm_connector_tests[] = {
 	KUNIT_CASE(dm_test_update_scaling_rmx_aspect_letterbox),
 	KUNIT_CASE(dm_test_update_scaling_rmx_center),
 	KUNIT_CASE(dm_test_update_scaling_underscan),
+	/* hdmi_frl_status_polling_work */
+	KUNIT_CASE(dm_test_frl_no_links),
+	KUNIT_CASE(dm_test_frl_skips_no_local_sink),
+	KUNIT_CASE(dm_test_frl_skips_non_hdmi),
+	KUNIT_CASE(dm_test_frl_skips_zero_rate),
+	KUNIT_CASE(dm_test_frl_poll_no_update),
+	KUNIT_CASE(dm_test_frl_poll_retrains),
 	{}
 };
 
-- 
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.