From: Bhawanpreet Lakha <[email protected]>

Add KUnit coverage for the i2c and EDID parsing helpers:
amdgpu_dm_i2c_func(), amdgpu_dm_i2c_xfer(), get_amd_vsdb(),
parse_hdmi_amd_vsdb() and parse_edid_displayid_vrr().

Assisted-by: Copilot:Claude-Opus-4.8
Reviewed-by: Alex Hung <[email protected]>
Signed-off-by: Bhawanpreet Lakha <[email protected]>
Signed-off-by: Wayne Lin <[email protected]>
---
 .../display/amdgpu_dm/amdgpu_dm_connector.c   |  15 +-
 .../display/amdgpu_dm/amdgpu_dm_connector.h   |  10 +
 .../tests/amdgpu_dm_connector_test.c          | 249 ++++++++++++++++++
 3 files changed, 269 insertions(+), 5 deletions(-)

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 557cc6492529..72c12484d218 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
@@ -2999,7 +2999,7 @@ void amdgpu_dm_connector_init_helper(struct 
amdgpu_display_manager *dm,
        }
 }
 
-static int amdgpu_dm_i2c_xfer(struct i2c_adapter *i2c_adap,
+STATIC_IFN_KUNIT int amdgpu_dm_i2c_xfer(struct i2c_adapter *i2c_adap,
                              struct i2c_msg *msgs, int num)
 {
        struct amdgpu_i2c_adapter *i2c = i2c_get_adapdata(i2c_adap);
@@ -3043,11 +3043,13 @@ static int amdgpu_dm_i2c_xfer(struct i2c_adapter 
*i2c_adap,
        kfree(cmd.payloads);
        return result;
 }
+EXPORT_IF_KUNIT(amdgpu_dm_i2c_xfer);
 
-static u32 amdgpu_dm_i2c_func(struct i2c_adapter *adap)
+STATIC_IFN_KUNIT u32 amdgpu_dm_i2c_func(struct i2c_adapter *adap)
 {
        return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
 }
+EXPORT_IF_KUNIT(amdgpu_dm_i2c_func);
 
 static const struct i2c_algorithm amdgpu_dm_i2c_algo = {
        .master_xfer = amdgpu_dm_i2c_xfer,
@@ -3409,7 +3411,7 @@ static bool parse_edid_cea(struct amdgpu_dm_connector 
*aconnector,
        return ret;
 }
 
-static void parse_edid_displayid_vrr(struct drm_connector *connector,
+STATIC_IFN_KUNIT void parse_edid_displayid_vrr(struct drm_connector *connector,
                                     const struct edid *edid)
 {
        u8 *edid_ext = NULL;
@@ -3451,8 +3453,9 @@ static void parse_edid_displayid_vrr(struct drm_connector 
*connector,
                j++;
        }
 }
+EXPORT_IF_KUNIT(parse_edid_displayid_vrr);
 
-static int get_amd_vsdb(struct amdgpu_dm_connector *aconnector,
+STATIC_IFN_KUNIT int get_amd_vsdb(struct amdgpu_dm_connector *aconnector,
                        struct amdgpu_hdmi_vsdb_info *vsdb_info)
 {
        struct drm_connector *connector = &aconnector->base;
@@ -3462,8 +3465,9 @@ static int get_amd_vsdb(struct amdgpu_dm_connector 
*aconnector,
 
        return connector->display_info.amd_vsdb.version != 0;
 }
+EXPORT_IF_KUNIT(get_amd_vsdb);
 
-static int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector,
+STATIC_IFN_KUNIT int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector 
*aconnector,
                               const struct edid *edid,
                               struct amdgpu_hdmi_vsdb_info *vsdb_info)
 {
@@ -3494,6 +3498,7 @@ static int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector 
*aconnector,
 
        return valid_vsdb_found ? i : -ENODEV;
 }
+EXPORT_IF_KUNIT(parse_hdmi_amd_vsdb);
 
 /**
  * amdgpu_dm_update_freesync_caps - Update Freesync capabilities
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.h 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.h
index b237e8f864db..b64c81477a60 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.h
@@ -146,6 +146,16 @@ int amdgpu_dm_encoder_init(struct drm_device *dev,
                           uint32_t link_index);
 
 #if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
+int amdgpu_dm_i2c_xfer(struct i2c_adapter *i2c_adap,
+                      struct i2c_msg *msgs, int num);
+u32 amdgpu_dm_i2c_func(struct i2c_adapter *adap);
+void parse_edid_displayid_vrr(struct drm_connector *connector,
+                             const struct edid *edid);
+int get_amd_vsdb(struct amdgpu_dm_connector *aconnector,
+                struct amdgpu_hdmi_vsdb_info *vsdb_info);
+int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector,
+                       const struct edid *edid,
+                       struct amdgpu_hdmi_vsdb_info *vsdb_info);
 void amdgpu_dm_connector_funcs_force(struct drm_connector *connector);
 enum dc_status dm_validate_stream_and_context(struct dc *dc,
                                              struct dc_stream_state *stream);
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 459f0eda9a69..f60ce381683e 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
@@ -17,6 +17,7 @@
 #include <drm/drm_modes.h>
 #include <drm/drm_property.h>
 #include <linux/hdmi.h>
+#include <linux/i2c.h>
 
 #include "dc.h"
 #include "amdgpu.h"
@@ -4666,6 +4667,239 @@ static void 
dm_test_add_freesync_modes_null_edid_noop(struct kunit *test)
        KUNIT_EXPECT_EQ(test, aconnector->num_modes, 7);
 }
 
+/* EDID extension block tag values (avoids pulling in private drm headers). */
+#define DM_TEST_CEA_EXT                0x02
+#define DM_TEST_DISPLAYID_EXT  0x70
+
+/**
+ * dm_test_i2c_func_returns_flags - Test the i2c functionality flags
+ * @test: The KUnit test context
+ *
+ * The algorithm advertises plain I2C plus emulated SMBUS regardless of the
+ * adapter argument, which it never dereferences.
+ */
+static void dm_test_i2c_func_returns_flags(struct kunit *test)
+{
+       KUNIT_EXPECT_EQ(test, amdgpu_dm_i2c_func(NULL),
+                       I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL);
+}
+
+/**
+ * dm_test_i2c_xfer_no_ddc_pin - Test transfers without a DDC pin are rejected
+ * @test: The KUnit test context
+ *
+ * When the backing ddc_service has no ddc_pin the transfer bails out early
+ * with -EIO before touching the message buffers or the dc handle.
+ */
+static void dm_test_i2c_xfer_no_ddc_pin(struct kunit *test)
+{
+       struct amdgpu_i2c_adapter *i2c;
+       struct ddc_service *ddc;
+
+       i2c = kunit_kzalloc(test, sizeof(*i2c), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, i2c);
+       ddc = kunit_kzalloc(test, sizeof(*ddc), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, ddc);
+
+       i2c->ddc_service = ddc;
+       i2c_set_adapdata(&i2c->base, i2c);
+
+       /* ddc->ddc_pin is NULL -> transfer is rejected with -EIO. */
+       KUNIT_EXPECT_EQ(test, amdgpu_dm_i2c_xfer(&i2c->base, NULL, 0), -EIO);
+}
+
+/**
+ * dm_test_get_amd_vsdb_unsupported - Test a zero VSDB version reports no 
support
+ * @test: The KUnit test context
+ */
+static void dm_test_get_amd_vsdb_unsupported(struct kunit *test)
+{
+       struct amdgpu_dm_connector *aconnector;
+       struct amdgpu_hdmi_vsdb_info vsdb_info = {0};
+
+       aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, aconnector);
+
+       aconnector->base.display_info.amd_vsdb.version = 0;
+       aconnector->base.display_info.amd_vsdb.replay_mode = false;
+
+       KUNIT_EXPECT_EQ(test, get_amd_vsdb(aconnector, &vsdb_info), 0);
+       KUNIT_EXPECT_EQ(test, vsdb_info.amd_vsdb_version, 0);
+}
+
+/**
+ * dm_test_get_amd_vsdb_supported - Test a non-zero VSDB version is reported
+ * @test: The KUnit test context
+ *
+ * The display info's VSDB version and replay mode are copied out and a
+ * non-zero version reports support.
+ */
+static void dm_test_get_amd_vsdb_supported(struct kunit *test)
+{
+       struct amdgpu_dm_connector *aconnector;
+       struct amdgpu_hdmi_vsdb_info vsdb_info = {0};
+
+       aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, aconnector);
+
+       aconnector->base.display_info.amd_vsdb.version = 2;
+       aconnector->base.display_info.amd_vsdb.replay_mode = true;
+
+       KUNIT_EXPECT_EQ(test, get_amd_vsdb(aconnector, &vsdb_info), 1);
+       KUNIT_EXPECT_EQ(test, vsdb_info.amd_vsdb_version, 2);
+       KUNIT_EXPECT_TRUE(test, vsdb_info.replay_mode);
+}
+
+/**
+ * dm_test_parse_hdmi_amd_vsdb_null_edid - Test NULL EDID returns -ENODEV
+ * @test: The KUnit test context
+ */
+static void dm_test_parse_hdmi_amd_vsdb_null_edid(struct kunit *test)
+{
+       struct amdgpu_dm_connector *aconnector;
+       struct amdgpu_hdmi_vsdb_info vsdb_info = {0};
+
+       aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, aconnector);
+
+       KUNIT_EXPECT_EQ(test,
+                       parse_hdmi_amd_vsdb(aconnector, NULL, &vsdb_info),
+                       -ENODEV);
+}
+
+/**
+ * dm_test_parse_hdmi_amd_vsdb_no_extensions - Test EDID without extensions
+ * @test: The KUnit test context
+ *
+ * An EDID that declares no extension blocks has no CEA block to parse.
+ */
+static void dm_test_parse_hdmi_amd_vsdb_no_extensions(struct kunit *test)
+{
+       struct amdgpu_dm_connector *aconnector;
+       struct amdgpu_hdmi_vsdb_info vsdb_info = {0};
+       struct edid *edid;
+
+       aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, aconnector);
+       edid = kunit_kzalloc(test, sizeof(*edid), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, edid);
+
+       edid->extensions = 0;
+
+       KUNIT_EXPECT_EQ(test,
+                       parse_hdmi_amd_vsdb(aconnector, edid, &vsdb_info),
+                       -ENODEV);
+}
+
+/**
+ * dm_test_parse_hdmi_amd_vsdb_no_cea_ext - Test EDID with no CEA extension
+ * @test: The KUnit test context
+ *
+ * An extension block that is not a CEA block leaves no VSDB to parse.
+ */
+static void dm_test_parse_hdmi_amd_vsdb_no_cea_ext(struct kunit *test)
+{
+       struct amdgpu_dm_connector *aconnector;
+       struct amdgpu_hdmi_vsdb_info vsdb_info = {0};
+       struct edid *edid;
+       u8 *raw;
+
+       aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, aconnector);
+
+       /* Base block + one extension block that is NOT a CEA extension. */
+       raw = kunit_kzalloc(test, 2 * EDID_LENGTH, GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, raw);
+       edid = (struct edid *)raw;
+       edid->extensions = 1;
+       raw[EDID_LENGTH] = DM_TEST_DISPLAYID_EXT;
+
+       KUNIT_EXPECT_EQ(test,
+                       parse_hdmi_amd_vsdb(aconnector, edid, &vsdb_info),
+                       -ENODEV);
+}
+
+/**
+ * dm_test_parse_displayid_vrr_null_edid - Test NULL EDID leaves range 
untouched
+ * @test: The KUnit test context
+ */
+static void dm_test_parse_displayid_vrr_null_edid(struct kunit *test)
+{
+       struct drm_connector *connector;
+
+       connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, connector);
+
+       parse_edid_displayid_vrr(connector, NULL);
+
+       KUNIT_EXPECT_EQ(test, connector->display_info.monitor_range.max_vfreq, 
0);
+       KUNIT_EXPECT_EQ(test, connector->display_info.monitor_range.min_vfreq, 
0);
+}
+
+/**
+ * dm_test_parse_displayid_vrr_no_displayid - Test EDID without a DisplayID ext
+ * @test: The KUnit test context
+ *
+ * Without a DisplayID extension block there is no dynamic range to extract.
+ */
+static void dm_test_parse_displayid_vrr_no_displayid(struct kunit *test)
+{
+       struct drm_connector *connector;
+       struct edid *edid;
+       u8 *raw;
+
+       connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, connector);
+       raw = kunit_kzalloc(test, 2 * EDID_LENGTH, GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, raw);
+       edid = (struct edid *)raw;
+       edid->extensions = 1;
+       raw[EDID_LENGTH] = DM_TEST_CEA_EXT;
+
+       parse_edid_displayid_vrr(connector, edid);
+
+       KUNIT_EXPECT_EQ(test, connector->display_info.monitor_range.max_vfreq, 
0);
+}
+
+/**
+ * dm_test_parse_displayid_vrr_sets_range - Test a DisplayID VRR block is 
parsed
+ * @test: The KUnit test context
+ *
+ * A DisplayID dynamic video timing range descriptor populates the connector's
+ * monitor refresh range.
+ */
+static void dm_test_parse_displayid_vrr_sets_range(struct kunit *test)
+{
+       struct drm_connector *connector;
+       struct edid *edid;
+       u8 *raw, *ext;
+
+       connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, connector);
+       raw = kunit_kzalloc(test, 2 * EDID_LENGTH, GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, raw);
+       edid = (struct edid *)raw;
+       edid->extensions = 1;
+
+       ext = raw + EDID_LENGTH;
+       ext[0] = DM_TEST_DISPLAYID_EXT;
+       /*
+        * DisplayID dynamic video timing range descriptor, parsed from offset
+        * 1: tag 0x25, flags 0 (single-byte max), payload length 9, then the
+        * min/max vfreq bytes.
+        */
+       ext[1] = 0x25;
+       ext[2] = 0x00;
+       ext[3] = 9;
+       ext[10] = 40;
+       ext[11] = 144;
+
+       parse_edid_displayid_vrr(connector, edid);
+
+       KUNIT_EXPECT_EQ(test, connector->display_info.monitor_range.min_vfreq, 
40);
+       KUNIT_EXPECT_EQ(test, connector->display_info.monitor_range.max_vfreq, 
144);
+}
+
 static struct kunit_case amdgpu_dm_connector_tests[] = {
        /* get_subconnector_type */
        KUNIT_CASE(dm_test_subconnector_type_none),
@@ -4917,6 +5151,21 @@ static struct kunit_case amdgpu_dm_connector_tests[] = {
        KUNIT_CASE(dm_test_add_fs_modes_no_preferred_mode),
        /* amdgpu_dm_connector_add_freesync_modes */
        KUNIT_CASE(dm_test_add_freesync_modes_null_edid_noop),
+       /* amdgpu_dm_i2c_func */
+       KUNIT_CASE(dm_test_i2c_func_returns_flags),
+       /* amdgpu_dm_i2c_xfer */
+       KUNIT_CASE(dm_test_i2c_xfer_no_ddc_pin),
+       /* get_amd_vsdb */
+       KUNIT_CASE(dm_test_get_amd_vsdb_unsupported),
+       KUNIT_CASE(dm_test_get_amd_vsdb_supported),
+       /* parse_hdmi_amd_vsdb */
+       KUNIT_CASE(dm_test_parse_hdmi_amd_vsdb_null_edid),
+       KUNIT_CASE(dm_test_parse_hdmi_amd_vsdb_no_extensions),
+       KUNIT_CASE(dm_test_parse_hdmi_amd_vsdb_no_cea_ext),
+       /* parse_edid_displayid_vrr */
+       KUNIT_CASE(dm_test_parse_displayid_vrr_null_edid),
+       KUNIT_CASE(dm_test_parse_displayid_vrr_no_displayid),
+       KUNIT_CASE(dm_test_parse_displayid_vrr_sets_range),
        {}
 };
 
-- 
2.43.0

Reply via email to