[WHAT]
Add KUnit tests for dm_early_init(), covering the display counts of the
legacy ASICs and the DCN IP versions, a BIOS without an object header,
and an unsupported IP version.

[HOW]
Add a fake ATOM BIOS image whose master data table reports a present
entry, which is all amdgpu_atom_parse_data_header() checks here. Only IP
versions without DMUB firmware are used, so dm_init_microcode() does not
reach a firmware request.

Assisted-by: Copilot:Claude-Opus-5 GPT-5.6-Sol
Reviewed-by: Bhawanpreet Lakha <[email protected]>
Signed-off-by: Alex Hung <[email protected]>
---
 .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c |   5 +-
 .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h |   1 +
 .../display/amdgpu_dm/tests/amdgpu_dm_test.c  | 168 ++++++++++++++++++
 3 files changed, 172 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index cd9f44326ae3..0ac4711b62e7 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -307,7 +307,7 @@ STATIC_IFN_KUNIT int dm_set_powergating_state(struct 
amdgpu_ip_block *ip_block,
 EXPORT_IF_KUNIT(dm_set_powergating_state);
 
 /* Prototypes of private functions */
-static int dm_early_init(struct amdgpu_ip_block *ip_block);
+STATIC_IFN_KUNIT int dm_early_init(struct amdgpu_ip_block *ip_block);
 
 #if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
 static const struct amdgpu_dm_kunit_ops amdgpu_dm_default_ops = {
@@ -2751,7 +2751,7 @@ DEVICE_ATTR_WO(s3_debug);
 
 #endif
 
-static int dm_early_init(struct amdgpu_ip_block *ip_block)
+STATIC_IFN_KUNIT int dm_early_init(struct amdgpu_ip_block *ip_block)
 {
        struct amdgpu_device *adev = ip_block->adev;
        struct amdgpu_mode_info *mode_info = &adev->mode_info;
@@ -2901,6 +2901,7 @@ static int dm_early_init(struct amdgpu_ip_block *ip_block)
 
        return dm_init_microcode(adev);
 }
+EXPORT_IF_KUNIT(dm_early_init);
 
 STATIC_IFN_KUNIT bool modereset_required(struct drm_crtc_state *crtc_state)
 {
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
index 19c6b29947f6..88fa4292c5cf 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
@@ -1254,6 +1254,7 @@ struct dc_plane_cap;
 void mmhub_read_system_context(struct amdgpu_device *adev,
                               struct dc_phy_addr_space_config *pa_config);
 int amdgpu_dm_init_power_module(struct amdgpu_display_manager *dm);
+int dm_early_init(struct amdgpu_ip_block *ip_block);
 int load_dmcu_fw(struct amdgpu_device *adev);
 int dm_sw_init(struct amdgpu_ip_block *ip_block);
 int dm_late_init(struct amdgpu_ip_block *ip_block);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c
index 58472b4d5388..7d0b5d8f06a1 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c
@@ -30,6 +30,7 @@
 #include "amd_shared.h"
 #include "amdgpu.h"
 #include "amdgpu_mode.h"
+#include "atom.h"
 #include "amdgpu_dm.h"
 #include "amdgpu_dm_audio.h"
 #include "amdgpu_dm_hdcp.h"
@@ -3562,6 +3563,168 @@ static void 
dm_test_init_power_module_alloc_failure(struct kunit *test)
        KUNIT_EXPECT_NULL(test, adev->dm.power_module);
 }
 
+/* Tests for dm_early_init() */
+
+#define DM_TEST_ATOM_BIOS_SIZE 512
+
+/*
+ * A fake ATOM BIOS image. Every byte is non-zero, so the master data table
+ * reports a present entry for whichever index the object header lives at,
+ * which is all amdgpu_atom_parse_data_header() checks here.
+ */
+static struct atom_context *dm_test_alloc_atom_context(struct kunit *test,
+                                                      bool object_header)
+{
+       struct atom_context *ctx;
+       void *bios;
+
+       ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, ctx);
+       bios = kunit_kzalloc(test, DM_TEST_ATOM_BIOS_SIZE, GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, bios);
+
+       if (object_header)
+               memset(bios, 0x01, DM_TEST_ATOM_BIOS_SIZE);
+
+       ctx->bios = bios;
+       ctx->bios_size = DM_TEST_ATOM_BIOS_SIZE;
+       ctx->data_table = 0;
+
+       return ctx;
+}
+
+static struct amdgpu_device *dm_test_early_init_adev(struct kunit *test)
+{
+       struct amdgpu_device *adev = dm_kunit_alloc_adev(test);
+
+       adev->mode_info.atom_context = dm_test_alloc_atom_context(test, true);
+
+       return adev;
+}
+
+static int dm_test_run_early_init(struct amdgpu_device *adev)
+{
+       struct amdgpu_ip_block ip_block = { .adev = adev };
+
+       return dm_early_init(&ip_block);
+}
+
+/**
+ * dm_test_early_init_no_object_header - Test a BIOS without an object header
+ * @test: The KUnit test context
+ */
+static void dm_test_early_init_no_object_header(struct kunit *test)
+{
+       struct amdgpu_device *adev = dm_kunit_alloc_adev(test);
+
+       adev->mode_info.atom_context = dm_test_alloc_atom_context(test, false);
+
+       KUNIT_EXPECT_EQ(test, dm_test_run_early_init(adev), -ENOENT);
+       KUNIT_EXPECT_TRUE(test, adev->harvest_ip_mask & 
AMD_HARVEST_IP_DMU_MASK);
+       KUNIT_EXPECT_FALSE(test, adev->dc_enabled);
+}
+
+/**
+ * dm_test_early_init_legacy_asics - Test the display counts of legacy ASICs
+ * @test: The KUnit test context
+ */
+static void dm_test_early_init_legacy_asics(struct kunit *test)
+{
+       static const struct {
+               enum amd_asic_type asic_type;
+               u32 num_crtc;
+               u32 num_hpd;
+               u32 num_dig;
+       } cases[] = {
+               { CHIP_BONAIRE, 6, 6, 6 },
+               { CHIP_HAWAII, 6, 6, 6 },
+               { CHIP_KAVERI, 4, 6, 7 },
+               { CHIP_KABINI, 2, 6, 6 },
+               { CHIP_MULLINS, 2, 6, 6 },
+               { CHIP_FIJI, 6, 6, 7 },
+               { CHIP_TONGA, 6, 6, 7 },
+               { CHIP_CARRIZO, 3, 6, 9 },
+               { CHIP_STONEY, 2, 6, 9 },
+               { CHIP_POLARIS11, 5, 5, 5 },
+               { CHIP_POLARIS12, 5, 5, 5 },
+               { CHIP_POLARIS10, 6, 6, 6 },
+               { CHIP_VEGAM, 6, 6, 6 },
+               { CHIP_VEGA10, 6, 6, 6 },
+               { CHIP_VEGA12, 6, 6, 6 },
+               { CHIP_VEGA20, 6, 6, 6 },
+       };
+       struct amdgpu_device *adev = dm_test_early_init_adev(test);
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(cases); i++) {
+               adev->asic_type = cases[i].asic_type;
+
+               KUNIT_EXPECT_EQ_MSG(test, dm_test_run_early_init(adev), 0,
+                                   "asic_type %d", cases[i].asic_type);
+               KUNIT_EXPECT_EQ_MSG(test, adev->mode_info.num_crtc, 
cases[i].num_crtc,
+                                   "asic_type %d", cases[i].asic_type);
+               KUNIT_EXPECT_EQ_MSG(test, adev->mode_info.num_hpd, 
cases[i].num_hpd,
+                                   "asic_type %d", cases[i].asic_type);
+               KUNIT_EXPECT_EQ_MSG(test, adev->mode_info.num_dig, 
cases[i].num_dig,
+                                   "asic_type %d", cases[i].asic_type);
+       }
+
+       KUNIT_EXPECT_NOT_NULL(test, adev->mode_info.funcs);
+       KUNIT_EXPECT_TRUE(test, adev->dc_enabled);
+}
+
+/**
+ * dm_test_early_init_dcn_versions - Test the display counts of DCN IP versions
+ * @test: The KUnit test context
+ *
+ * Only IP versions without DMUB firmware are used, so dm_init_microcode() does
+ * not reach a firmware request.
+ */
+static void dm_test_early_init_dcn_versions(struct kunit *test)
+{
+       static const struct {
+               u32 ip_version;
+               u32 num_crtc;
+       } cases[] = {
+               { IP_VERSION(2, 0, 2), 6 },
+               { IP_VERSION(2, 0, 0), 5 },
+               { IP_VERSION(2, 0, 3), 2 },
+               { IP_VERSION(1, 0, 0), 4 },
+               { IP_VERSION(1, 0, 1), 4 },
+       };
+       struct amdgpu_device *adev = dm_test_early_init_adev(test);
+       unsigned int i;
+
+       adev->asic_type = CHIP_IP_DISCOVERY;
+
+       for (i = 0; i < ARRAY_SIZE(cases); i++) {
+               adev->ip_versions[DCE_HWIP][0] = cases[i].ip_version;
+
+               KUNIT_EXPECT_EQ_MSG(test, dm_test_run_early_init(adev), 0,
+                                   "ip_version 0x%x", cases[i].ip_version);
+               KUNIT_EXPECT_EQ_MSG(test, adev->mode_info.num_crtc, 
cases[i].num_crtc,
+                                   "ip_version 0x%x", cases[i].ip_version);
+               /* Every DCN entry keeps hpd and dig in step with the CRTC 
count. */
+               KUNIT_EXPECT_EQ(test, adev->mode_info.num_hpd, 
cases[i].num_crtc);
+               KUNIT_EXPECT_EQ(test, adev->mode_info.num_dig, 
cases[i].num_crtc);
+       }
+}
+
+/**
+ * dm_test_early_init_unsupported_version - Test an unknown IP version is 
rejected
+ * @test: The KUnit test context
+ */
+static void dm_test_early_init_unsupported_version(struct kunit *test)
+{
+       struct amdgpu_device *adev = dm_test_early_init_adev(test);
+
+       adev->asic_type = CHIP_IP_DISCOVERY;
+       adev->ip_versions[DCE_HWIP][0] = IP_VERSION(9, 9, 9);
+
+       KUNIT_EXPECT_EQ(test, dm_test_run_early_init(adev), -EINVAL);
+       KUNIT_EXPECT_FALSE(test, adev->dc_enabled);
+}
+
 /* Tests for load_dmcu_fw() */
 
 /**
@@ -4091,6 +4254,11 @@ static struct kunit_case amdgpu_dm_tests[] = {
        /* amdgpu_dm_init_power_module */
        KUNIT_CASE(dm_test_init_power_module_no_edp),
        KUNIT_CASE(dm_test_init_power_module_alloc_failure),
+       /* dm_early_init */
+       KUNIT_CASE(dm_test_early_init_no_object_header),
+       KUNIT_CASE(dm_test_early_init_legacy_asics),
+       KUNIT_CASE(dm_test_early_init_dcn_versions),
+       KUNIT_CASE(dm_test_early_init_unsupported_version),
        /* load_dmcu_fw */
        KUNIT_CASE(dm_test_load_dmcu_fw_no_dmcu),
        KUNIT_CASE(dm_test_load_dmcu_fw_dcn),
-- 
2.43.0

Reply via email to