[PATCH 57/82] drm/amd/display: Cover funcs_force valid EDID path

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

Add a KUnit test that drives amdgpu_dm_connector_funcs_force() through
the valid-EDID branch: a fake i2c adapter serves a valid EDID over the
DDC read protocol so the connector caches drm_edid and, with an emulated
sink and dc_link present, copies the raw EDID into the sink and parses
its caps.

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]>
---
 .../tests/amdgpu_dm_connector_test.c          | 95 +++++++++++++++++++
 1 file changed, 95 insertions(+)

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 f7ea6aa801bc8..3a5edf84fbc0a 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
@@ -7077,6 +7077,100 @@ static void dm_test_update_after_detect_force_em_keep(struct kunit *test)
 	KUNIT_EXPECT_PTR_EQ(test, ctx->aconnector->dc_sink, dc_sink);
 }
 
+/* Tests for amdgpu_dm_connector_funcs_force() with a valid EDID */
+
+/*
+ * Fake i2c adapter that serves dm_test_uad_edid over the DDC read protocol
+ * drm_do_probe_ddc_edid() uses: a one-byte write to DDC_ADDR (0x50) sets the
+ * block offset and the following read returns bytes from that offset. This
+ * lets amdgpu_dm_connector_funcs_force() obtain a valid EDID without hardware.
+ */
+static int dm_test_force_edid_i2c_xfer(struct i2c_adapter *adap,
+				       struct i2c_msg *msgs, int num)
+{
+	u8 offset = 0;
+	int i;
+
+	for (i = 0; i < num; i++) {
+		if (msgs[i].flags & I2C_M_RD) {
+			size_t len = msgs[i].len;
+
+			if (offset >= sizeof(dm_test_uad_edid))
+				len = 0;
+			else if (len > sizeof(dm_test_uad_edid) - offset)
+				len = sizeof(dm_test_uad_edid) - offset;
+			memcpy(msgs[i].buf, dm_test_uad_edid + offset, len);
+		} else if (msgs[i].addr == 0x50 && msgs[i].len >= 1) {
+			offset = msgs[i].buf[0];
+		}
+	}
+
+	return num;
+}
+
+static u32 dm_test_force_edid_i2c_func(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C;
+}
+
+static const struct i2c_algorithm dm_test_force_edid_i2c_algo = {
+	.master_xfer = dm_test_force_edid_i2c_xfer,
+	.functionality = dm_test_force_edid_i2c_func,
+};
+
+static void dm_test_force_edid_lock_bus(struct i2c_adapter *adap, unsigned int flags) {}
+static int dm_test_force_edid_trylock_bus(struct i2c_adapter *adap, unsigned int flags)
+{
+	return 1;
+}
+static void dm_test_force_edid_unlock_bus(struct i2c_adapter *adap, unsigned int flags) {}
+
+static const struct i2c_lock_operations dm_test_force_edid_lock_ops = {
+	.lock_bus = dm_test_force_edid_lock_bus,
+	.trylock_bus = dm_test_force_edid_trylock_bus,
+	.unlock_bus = dm_test_force_edid_unlock_bus,
+};
+
+/**
+ * dm_test_funcs_force_reads_edid - Test force() caches EDID and updates em_sink
+ * @test: The KUnit test context
+ *
+ * A non-AUX link selects the connector's i2c adapter, which serves a valid
+ * EDID. force() must cache it in drm_edid and, because an emulated sink and a
+ * dc_link are present, copy the raw EDID into the sink and parse its caps.
+ */
+static void dm_test_funcs_force_reads_edid(struct kunit *test)
+{
+	struct dm_test_edid_ctx *ctx =
+		dm_test_edid_ctx_alloc(test, DRM_MODE_CONNECTOR_DisplayPort);
+	struct amdgpu_i2c_adapter *i2c;
+	struct dc_sink *em_sink;
+
+	i2c = kunit_kzalloc(test, sizeof(*i2c), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, i2c);
+	i2c->base.algo = &dm_test_force_edid_i2c_algo;
+	i2c->base.lock_ops = &dm_test_force_edid_lock_ops;
+	ctx->aconnector->i2c = i2c;
+
+	em_sink = kunit_kzalloc(test, sizeof(*em_sink), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, em_sink);
+	ctx->aconnector->dc_em_sink = em_sink;
+
+	/* Non-AUX link picks the i2c adapter; force-on skips the DDC probe. */
+	ctx->link->aux_mode = false;
+	ctx->link->priv = ctx->aconnector;
+	ctx->aconnector->base.force = DRM_FORCE_ON;
+
+	amdgpu_dm_connector_funcs_force(&ctx->aconnector->base);
+
+	KUNIT_EXPECT_NOT_NULL(test, ctx->aconnector->drm_edid);
+	KUNIT_EXPECT_MEMEQ(test, em_sink->dc_edid.raw_edid,
+			   dm_test_uad_edid, sizeof(dm_test_uad_edid));
+
+	drm_edid_free(ctx->aconnector->drm_edid);
+	ctx->aconnector->drm_edid = NULL;
+}
+
 /* Tests for amdgpu_dm_update_stream_scaling_settings() */
 
 /**
@@ -7539,6 +7633,7 @@ static struct kunit_case amdgpu_dm_connector_tests[] = {
 	KUNIT_CASE(dm_test_handle_edid_mgmt_non_dp_leaves_caps),
 	/* amdgpu_dm_connector_funcs_force */
 	KUNIT_CASE(dm_test_funcs_force_no_edid),
+	KUNIT_CASE(dm_test_funcs_force_reads_edid),
 	/* dm_validate_stream_and_context */
 	KUNIT_CASE(dm_test_validate_stream_null_stream),
 	KUNIT_CASE(dm_test_validate_stream_dc_ok_no_pipe),
-- 
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.