[PATCH 28/82] drm/amd/display: Test dm_init_microcode

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

[WHAT]
Add KUnit tests for dm_init_microcode(), covering the ASIC to firmware
name mapping for all 21 supported IP versions and the firmware request
failure path.

[HOW]
The real amdgpu_ucode_request() reaches the firmware loader, so add a
KUnit-only indirection table for it. The default table entry points at
the existing function, so no test-only wrapper is introduced, and the
prototype is mirrored verbatim so an upstream change fails the build at
the default initializer rather than diverging silently.

Tests install a fake that records the formatted firmware name, letting a
single table-driven case assert the name chosen for every ASIC,
including the Green Sardine revision check and the Sienna Cichlid versus
Navy Flounder split on the graphics IP version.

Assisted-by: Copilot:Claude-Opus-5
Reviewed-by: Bhawanpreet Lakha <[email protected]>
Signed-off-by: Alex Hung <[email protected]>
Signed-off-by: Ivan Lipski <[email protected]>
---
 .../amd/display/amdgpu_dm/amdgpu_dm_dmub.c    |  26 +++-
 .../amd/display/amdgpu_dm/amdgpu_dm_dmub.h    |   9 ++
 .../amdgpu_dm/tests/amdgpu_dm_dmub_test.c     | 115 ++++++++++++++++++
 3 files changed, 148 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c
index 29990cfe4265e..2b34d5c36b48d 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c
@@ -65,6 +65,28 @@ MODULE_FIRMWARE(FIRMWARE_DCN_42_DMUB);
 MODULE_FIRMWARE(FIRMWARE_DCN_42B_DMUB);
 MODULE_FIRMWARE(FIRMWARE_DCN_60_DMUB);
 
+#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
+static const struct amdgpu_dm_dmub_kunit_ops amdgpu_dm_dmub_default_ops = {
+	.ucode_request = amdgpu_ucode_request,
+};
+
+static const struct amdgpu_dm_dmub_kunit_ops *amdgpu_dm_dmub_ops =
+	&amdgpu_dm_dmub_default_ops;
+
+void amdgpu_dm_dmub_kunit_set_ops(const struct amdgpu_dm_dmub_kunit_ops *ops)
+{
+	amdgpu_dm_dmub_ops = ops ? ops : &amdgpu_dm_dmub_default_ops;
+}
+EXPORT_IF_KUNIT(amdgpu_dm_dmub_kunit_set_ops);
+
+#define dmub_ucode_request	amdgpu_dm_dmub_ops->ucode_request
+
+#else
+
+#define dmub_ucode_request	amdgpu_ucode_request
+
+#endif
+
 /**
  * dm_dmub_aux_setconfig_callback - Callback for AUX or SET_CONFIG command.
  * @adev: amdgpu_device pointer
@@ -768,8 +790,8 @@ int dm_init_microcode(struct amdgpu_device *adev)
 		/* ASIC doesn't support DMUB. */
 		return 0;
 	}
-	r = amdgpu_ucode_request(adev, &adev->dm.dmub_fw, AMDGPU_UCODE_REQUIRED,
-				 "%s", fw_name_dmub);
+	r = dmub_ucode_request(adev, &adev->dm.dmub_fw, AMDGPU_UCODE_REQUIRED,
+			       "%s", fw_name_dmub);
 	return r;
 }
 EXPORT_IF_KUNIT(dm_init_microcode);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.h
index 735e2b4299521..d178e1bf4dd6b 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.h
@@ -75,6 +75,15 @@ void abort_fused_io(struct dc_context *ctx,
 		    const struct dmub_cmd_fused_request *request);
 uint32_t amdgpu_dm_dmub_reg_read(void *ctx, uint32_t address);
 void amdgpu_dm_dmub_reg_write(void *ctx, uint32_t address, uint32_t value);
+
+/* Signatures are verbatim copies so an upstream change breaks the default ops. */
+struct amdgpu_dm_dmub_kunit_ops {
+	__printf(4, 5)
+	int (*ucode_request)(struct amdgpu_device *adev, const struct firmware **fw,
+			     enum amdgpu_ucode_required required, const char *fmt, ...);
+};
+
+void amdgpu_dm_dmub_kunit_set_ops(const struct amdgpu_dm_dmub_kunit_ops *ops);
 #endif
 
 #endif /* AMDGPU_DM_AMDGPU_DM_DMUB_H_ */
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c
index cda048e8297a0..fa81413be05f5 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c
@@ -14,6 +14,7 @@
 #include "dc/inc/hw/abm.h"
 #include "amdgpu_mode.h"
 #include "amdgpu_dm.h"
+#include "dal_asic_id.h"
 #include "dm_services.h"
 #include "dmub/dmub_srv.h"
 #include "amdgpu_dm_dmub.h"
@@ -1445,6 +1446,32 @@ static const struct amdgpu_dm_services_kunit_ops dm_test_services_ops = {
 	.bo_free_kernel = dm_test_bo_free_kernel,
 };
 
+/* Fake firmware loader, records the firmware name the ASIC switch selected. */
+
+static char dm_test_ucode_name[64];
+static unsigned int dm_test_ucode_calls;
+static int dm_test_ucode_ret;
+
+static __printf(4, 5) int dm_test_ucode_request(struct amdgpu_device *adev,
+						const struct firmware **fw,
+						enum amdgpu_ucode_required required,
+						const char *fmt, ...)
+{
+	va_list args;
+
+	dm_test_ucode_calls++;
+
+	va_start(args, fmt);
+	vsnprintf(dm_test_ucode_name, sizeof(dm_test_ucode_name), fmt, args);
+	va_end(args);
+
+	return dm_test_ucode_ret;
+}
+
+static const struct amdgpu_dm_dmub_kunit_ops dm_test_dmub_ops = {
+	.ucode_request = dm_test_ucode_request,
+};
+
 static int dm_test_dmub_hw_access_init(struct kunit *test)
 {
 	void *cpu_ptr;
@@ -1454,8 +1481,12 @@ static int dm_test_dmub_hw_access_init(struct kunit *test)
 
 	dm_test_bo = (struct dm_test_bo_ctx) { .cpu_ptr = cpu_ptr };
 	dm_test_cgs = (struct dm_test_cgs_ctx) { .dev.ops = &dm_test_cgs_ops };
+	dm_test_ucode_name[0] = '\0';
+	dm_test_ucode_calls = 0;
+	dm_test_ucode_ret = 0;
 
 	amdgpu_dm_services_kunit_set_ops(&dm_test_services_ops);
+	amdgpu_dm_dmub_kunit_set_ops(&dm_test_dmub_ops);
 
 	return 0;
 }
@@ -1463,6 +1494,7 @@ static int dm_test_dmub_hw_access_init(struct kunit *test)
 static void dm_test_dmub_hw_access_exit(struct kunit *test)
 {
 	amdgpu_dm_services_kunit_set_ops(NULL);
+	amdgpu_dm_dmub_kunit_set_ops(NULL);
 }
 
 static struct amdgpu_device *dm_test_alloc_adev_with_cgs(struct kunit *test)
@@ -1608,6 +1640,86 @@ static void dm_test_dmub_get_vbios_bounding_box_copy_timeout(struct kunit *test)
 	KUNIT_EXPECT_TRUE(test, list_empty(&adev->dm.da_list));
 }
 
+/* Tests for dm_init_microcode() */
+
+struct dm_test_ucode_case {
+	u32 dce_version;
+	u32 gc_version;
+	u32 external_rev_id;
+	const char *fw_name;
+};
+
+static const struct dm_test_ucode_case dm_test_ucode_cases[] = {
+	{ IP_VERSION(2, 1, 0), 0, 0, FIRMWARE_RENOIR_DMUB },
+	{ IP_VERSION(2, 1, 0), 0, GREEN_SARDINE_A0, FIRMWARE_GREEN_SARDINE_DMUB },
+	{ IP_VERSION(3, 0, 0), IP_VERSION(10, 3, 0), 0, FIRMWARE_SIENNA_CICHLID_DMUB },
+	{ IP_VERSION(3, 0, 0), IP_VERSION(10, 3, 2), 0, FIRMWARE_NAVY_FLOUNDER_DMUB },
+	{ IP_VERSION(3, 0, 1), 0, 0, FIRMWARE_VANGOGH_DMUB },
+	{ IP_VERSION(3, 0, 2), 0, 0, FIRMWARE_DIMGREY_CAVEFISH_DMUB },
+	{ IP_VERSION(3, 0, 3), 0, 0, FIRMWARE_BEIGE_GOBY_DMUB },
+	{ IP_VERSION(3, 1, 2), 0, 0, FIRMWARE_YELLOW_CARP_DMUB },
+	{ IP_VERSION(3, 1, 3), 0, 0, FIRMWARE_YELLOW_CARP_DMUB },
+	{ IP_VERSION(3, 1, 4), 0, 0, FIRMWARE_DCN_314_DMUB },
+	{ IP_VERSION(3, 1, 5), 0, 0, FIRMWARE_DCN_315_DMUB },
+	{ IP_VERSION(3, 1, 6), 0, 0, FIRMWARE_DCN316_DMUB },
+	{ IP_VERSION(3, 2, 0), 0, 0, FIRMWARE_DCN_V3_2_0_DMCUB },
+	{ IP_VERSION(3, 2, 1), 0, 0, FIRMWARE_DCN_V3_2_1_DMCUB },
+	{ IP_VERSION(3, 5, 0), 0, 0, FIRMWARE_DCN_35_DMUB },
+	{ IP_VERSION(3, 5, 1), 0, 0, FIRMWARE_DCN_351_DMUB },
+	{ IP_VERSION(3, 6, 0), 0, 0, FIRMWARE_DCN_36_DMUB },
+	{ IP_VERSION(4, 0, 1), 0, 0, FIRMWARE_DCN_401_DMUB },
+	{ IP_VERSION(4, 2, 0), 0, 0, FIRMWARE_DCN_42_DMUB },
+	{ IP_VERSION(4, 2, 1), 0, 0, FIRMWARE_DCN_42B_DMUB },
+	{ IP_VERSION(6, 0, 0), 0, 0, FIRMWARE_DCN_60_DMUB },
+};
+
+/**
+ * dm_test_init_microcode_fw_names - Test the ASIC to firmware name mapping
+ * @test: The KUnit test context
+ */
+static void dm_test_init_microcode_fw_names(struct kunit *test)
+{
+	struct amdgpu_device *adev;
+	unsigned int i;
+
+	adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+	for (i = 0; i < ARRAY_SIZE(dm_test_ucode_cases); i++) {
+		const struct dm_test_ucode_case *c = &dm_test_ucode_cases[i];
+
+		adev->ip_versions[DCE_HWIP][0] = c->dce_version;
+		adev->ip_versions[GC_HWIP][0] = c->gc_version;
+		adev->external_rev_id = c->external_rev_id;
+		dm_test_ucode_name[0] = '\0';
+
+		KUNIT_EXPECT_EQ_MSG(test, dm_init_microcode(adev), 0,
+				    "IP version 0x%08x", c->dce_version);
+		KUNIT_EXPECT_STREQ_MSG(test, dm_test_ucode_name, c->fw_name,
+				       "IP version 0x%08x", c->dce_version);
+	}
+
+	KUNIT_EXPECT_EQ(test, dm_test_ucode_calls,
+			(unsigned int)ARRAY_SIZE(dm_test_ucode_cases));
+}
+
+/**
+ * dm_test_init_microcode_request_fails - Test a firmware request failure
+ * @test: The KUnit test context
+ */
+static void dm_test_init_microcode_request_fails(struct kunit *test)
+{
+	struct amdgpu_device *adev;
+
+	adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+	adev->ip_versions[DCE_HWIP][0] = IP_VERSION(3, 5, 0);
+	dm_test_ucode_ret = -ENOENT;
+
+	KUNIT_EXPECT_EQ(test, dm_init_microcode(adev), -ENOENT);
+}
+
 static struct kunit_case amdgpu_dm_dmub_tests[] = {
 	/* dm_register_dmub_notify_callback() */
 	KUNIT_CASE(dm_test_register_dmub_notify_callback_null_callback),
@@ -1681,6 +1793,9 @@ static struct kunit_case amdgpu_dm_dmub_hw_access_tests[] = {
 	KUNIT_CASE(dm_test_dmub_get_vbios_bounding_box_alloc_fails),
 	KUNIT_CASE(dm_test_dmub_get_vbios_bounding_box_addr_timeout),
 	KUNIT_CASE(dm_test_dmub_get_vbios_bounding_box_copy_timeout),
+	/* dm_init_microcode() */
+	KUNIT_CASE(dm_test_init_microcode_fw_names),
+	KUNIT_CASE(dm_test_init_microcode_request_fails),
 	{}
 };
 
-- 
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.