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

Reply via email to