From: Alex Hung <[email protected]>

[WHAT]
Add KUnit coverage for dm_dmub_hw_init() beyond the existing
early-return cases. Introduce reusable fake-DMUB fixtures (fake
dmub_srv/firmware, DMCU/ABM stubs, and adev builders) so the init path
runs without real register access, TTM allocation, or firmware loading.

New cases cover the fake-DMUB success path, unsupported hardware, BSS
data copy, hardware-init failure, auto-load timeout, the APU/DPIA DCN3.5
params, the DCN3.1.x sanity-check ranges, and DMCU/ABM initialization.

Assisted-by: Copilot:Claude-Opus-4.8 GPT-5.5
Reviewed-by: Bhawanpreet Lakha <[email protected]>
Signed-off-by: Alex Hung <[email protected]>
Signed-off-by: Wayne Lin <[email protected]>
---
 .../amdgpu_dm/tests/amdgpu_dm_dmub_test.c     | 315 ++++++++++++++++++
 1 file changed, 315 insertions(+)

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 bf90ccfbf431..4c01f7919170 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
@@ -6,14 +6,20 @@
  */
 
 #include <kunit/test.h>
+#include <linux/firmware.h>
 
 #include "dc.h"
 #include "dc/inc/core_types.h"
+#include "dc/inc/hw/dmcu.h"
+#include "dc/inc/hw/abm.h"
 #include "amdgpu_mode.h"
 #include "amdgpu_dm.h"
+#include "dm_services.h"
 #include "dmub/dmub_srv.h"
 #include "amdgpu_dm_dmub.h"
 
+#define DM_TEST_FW_SIZE        512
+
 /* Tests for dm_register_dmub_notify_callback() */
 
 static void dummy_callback(struct amdgpu_device *adev,
@@ -21,6 +27,99 @@ static void dummy_callback(struct amdgpu_device *adev,
 {
 }
 
+static bool dm_test_dmub_supported(struct dmub_srv *dmub)
+{
+       return true;
+}
+
+static bool dm_test_dmub_unsupported(struct dmub_srv *dmub)
+{
+       return false;
+}
+
+static bool dm_test_dmub_hw_initialized(struct dmub_srv *dmub)
+{
+       return true;
+}
+
+static union dmub_fw_boot_status dm_test_dmub_fw_ready(struct dmub_srv *dmub)
+{
+       union dmub_fw_boot_status status = { 0 };
+
+       status.bits.dal_fw = 1;
+       status.bits.mailbox_rdy = 1;
+       return status;
+}
+
+static union dmub_fw_boot_status dm_test_dmub_fw_not_ready(struct dmub_srv 
*dmub)
+{
+       union dmub_fw_boot_status status = { 0 };
+
+       return status;
+}
+
+static void dm_test_dmub_init_reg_offsets(struct dmub_srv *dmub,
+                                         struct dc_context *ctx)
+{
+}
+
+static bool dm_test_dmcu_init(struct dmcu *dmcu)
+{
+       return true;
+}
+
+static bool dm_test_dmcu_is_initialized(struct dmcu *dmcu)
+{
+       return true;
+}
+
+static const struct dmcu_funcs dm_test_dmcu_funcs = {
+       .dmcu_init = dm_test_dmcu_init,
+       .is_dmcu_initialized = dm_test_dmcu_is_initialized,
+};
+
+static struct dmub_srv *dm_test_alloc_dmub_srv(struct kunit *test)
+{
+       struct dmub_srv *dmub_srv;
+
+       dmub_srv = kunit_kzalloc(test, sizeof(*dmub_srv), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dmub_srv);
+
+       dmub_srv->sw_init = true;
+       dmub_srv->hw_init = true;
+       dmub_srv->power_state = DMUB_POWER_STATE_D0;
+       dmub_srv->hw_funcs.is_supported = dm_test_dmub_supported;
+       dmub_srv->hw_funcs.is_hw_init = dm_test_dmub_hw_initialized;
+       dmub_srv->hw_funcs.get_fw_status = dm_test_dmub_fw_ready;
+       dmub_srv->hw_funcs.init_reg_offsets = dm_test_dmub_init_reg_offsets;
+
+       return dmub_srv;
+}
+
+static const struct firmware *dm_test_alloc_dmub_fw(struct kunit *test)
+{
+       struct dmcub_firmware_header_v1_0 *hdr;
+       struct firmware *fw;
+       u8 *data;
+
+       fw = kunit_kzalloc(test, sizeof(*fw), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fw);
+
+       data = kunit_kzalloc(test, DM_TEST_FW_SIZE, GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, data);
+
+       hdr = (struct dmcub_firmware_header_v1_0 *)data;
+       hdr->header.ucode_array_offset_bytes = cpu_to_le32(0);
+       hdr->header.ucode_version = cpu_to_le32(DMUB_FW_VERSION(9, 9, 9));
+       hdr->inst_const_bytes = cpu_to_le32(PSP_HEADER_BYTES_256);
+       hdr->bss_data_bytes = cpu_to_le32(0);
+
+       fw->size = DM_TEST_FW_SIZE;
+       fw->data = data;
+
+       return fw;
+}
+
 /**
  * dm_test_register_dmub_notify_callback_null_callback - Test null callback is 
rejected
  * @test: The KUnit test context
@@ -395,6 +494,7 @@ static void 
dm_test_get_default_ips_mode_newer_default(struct kunit *test)
 static struct amdgpu_device *dm_test_alloc_adev_with_dc(struct kunit *test)
 {
        struct amdgpu_device *adev;
+       struct dc_context *ctx;
        struct dc *dc;
        struct resource_pool *res_pool;
 
@@ -407,12 +507,47 @@ static struct amdgpu_device 
*dm_test_alloc_adev_with_dc(struct kunit *test)
        res_pool = kunit_kzalloc(test, sizeof(*res_pool), GFP_KERNEL);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, res_pool);
 
+       ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
        dc->res_pool = res_pool;
+       dc->ctx = ctx;
+       ctx->dc = dc;
+       ctx->driver_context = adev;
        adev->dm.dc = dc;
 
        return adev;
 }
 
+static struct amdgpu_device *dm_test_alloc_adev_with_dmub(struct kunit *test)
+{
+       struct amdgpu_device *adev;
+       struct dmub_srv_fb_info *fb_info;
+       int i;
+
+       adev = dm_test_alloc_adev_with_dc(test);
+
+       fb_info = kunit_kzalloc(test, sizeof(*fb_info), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fb_info);
+
+       fb_info->num_fb = DMUB_WINDOW_TOTAL;
+       for (i = 0; i < DMUB_WINDOW_TOTAL; i++) {
+               fb_info->fb[i].size = PAGE_SIZE;
+               fb_info->fb[i].cpu_addr = kunit_kzalloc(test, PAGE_SIZE, 
GFP_KERNEL);
+               KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fb_info->fb[i].cpu_addr);
+       }
+
+       adev->dm.dmub_srv = dm_test_alloc_dmub_srv(test);
+       adev->dm.dmub_fb_info = fb_info;
+       adev->dm.dmub_fw = dm_test_alloc_dmub_fw(test);
+       adev->bios = kunit_kzalloc(test, 4, GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev->bios);
+       adev->bios_size = 4;
+       adev->dm.fw_inst_size = 0;
+
+       return adev;
+}
+
 /**
  * dm_test_dmub_hw_init_no_dmub_srv - Test hw init returns 0 when DMUB 
unsupported
  * @test: The KUnit test context
@@ -476,6 +611,177 @@ static void dm_test_dmub_hw_init_no_firmware(struct kunit 
*test)
        KUNIT_EXPECT_EQ(test, dm_dmub_hw_init(adev), -EINVAL);
 }
 
+/**
+ * dm_test_dmub_hw_init_success_fake_dmub - Test hw init with a fake DMUB 
service
+ * @test: The KUnit test context
+ *
+ * With fake DMUB callbacks and preallocated framebuffer windows, the init path
+ * should reach DMUB service initialization without real register access.
+ */
+static void dm_test_dmub_hw_init_success_fake_dmub(struct kunit *test)
+{
+       struct amdgpu_device *adev = dm_test_alloc_adev_with_dmub(test);
+
+       KUNIT_EXPECT_EQ(test, dm_dmub_hw_init(adev), 0);
+       KUNIT_EXPECT_TRUE(test, adev->dm.dmub_srv->hw_init);
+       KUNIT_EXPECT_NOT_NULL(test, adev->dm.dc->ctx->dmub_srv);
+}
+
+/**
+ * dm_test_dmub_hw_init_no_hw_support - Test hw init returns 0 when HW is 
unsupported
+ * @test: The KUnit test context
+ *
+ * When the DMUB service reports no hardware support, dm_dmub_hw_init() should
+ * log and return 0 without initializing the DMUB hardware.
+ */
+static void dm_test_dmub_hw_init_no_hw_support(struct kunit *test)
+{
+       struct amdgpu_device *adev = dm_test_alloc_adev_with_dmub(test);
+
+       adev->dm.dmub_srv->hw_funcs.is_supported = dm_test_dmub_unsupported;
+
+       KUNIT_EXPECT_EQ(test, dm_dmub_hw_init(adev), 0);
+       KUNIT_EXPECT_NULL(test, adev->dm.dc->ctx->dmub_srv);
+}
+
+/**
+ * dm_test_dmub_hw_init_bss_data - Test hw init copies BSS data into FB memory
+ * @test: The KUnit test context
+ *
+ * When the DMUB firmware declares a non-zero BSS data size, dm_dmub_hw_init()
+ * should copy that region into the BSS framebuffer window.
+ */
+static void dm_test_dmub_hw_init_bss_data(struct kunit *test)
+{
+       struct amdgpu_device *adev = dm_test_alloc_adev_with_dmub(test);
+       struct dmcub_firmware_header_v1_0 *hdr;
+
+       hdr = (struct dmcub_firmware_header_v1_0 *)adev->dm.dmub_fw->data;
+       hdr->bss_data_bytes = cpu_to_le32(16);
+
+       KUNIT_EXPECT_EQ(test, dm_dmub_hw_init(adev), 0);
+       KUNIT_EXPECT_TRUE(test, adev->dm.dmub_srv->hw_init);
+}
+
+/**
+ * dm_test_dmub_hw_init_hw_init_fails - Test hw init returns -EINVAL on DMUB 
init failure
+ * @test: The KUnit test context
+ *
+ * A framebuffer-info window count below the required total makes
+ * dmub_srv_hw_init() reject the request, so dm_dmub_hw_init() logs and
+ * returns -EINVAL. (The rejection path emits a one-time WARN via ASSERT.)
+ */
+static void dm_test_dmub_hw_init_hw_init_fails(struct kunit *test)
+{
+       struct amdgpu_device *adev = dm_test_alloc_adev_with_dmub(test);
+
+       adev->dm.dmub_fb_info->num_fb = 0;
+
+       KUNIT_EXPECT_EQ(test, dm_dmub_hw_init(adev), -EINVAL);
+}
+
+/**
+ * dm_test_dmub_hw_init_auto_load_timeout - Test hw init tolerates an 
auto-load timeout
+ * @test: The KUnit test context
+ *
+ * When the DMUB firmware never reports ready, dmub_srv_wait_for_auto_load()
+ * times out; dm_dmub_hw_init() only warns and still completes successfully.
+ */
+static void dm_test_dmub_hw_init_auto_load_timeout(struct kunit *test)
+{
+       struct amdgpu_device *adev = dm_test_alloc_adev_with_dmub(test);
+
+       adev->dm.dmub_srv->hw_funcs.get_fw_status = dm_test_dmub_fw_not_ready;
+
+       KUNIT_EXPECT_EQ(test, dm_dmub_hw_init(adev), 0);
+       KUNIT_EXPECT_NOT_NULL(test, adev->dm.dc->ctx->dmub_srv);
+}
+
+/**
+ * dm_test_dmub_hw_init_apu_dpia_dcn35 - Test hw init APU DPIA and DCN35 hw 
params
+ * @test: The KUnit test context
+ *
+ * On a DCN3.5 APU with a USB4 DPIA link, dm_dmub_hw_init() should populate the
+ * DPIA hw params and the DCN3.5 IPS-sequential hw params before initializing
+ * the fake DMUB service.
+ */
+static void dm_test_dmub_hw_init_apu_dpia_dcn35(struct kunit *test)
+{
+       struct amdgpu_device *adev = dm_test_alloc_adev_with_dmub(test);
+
+       adev->ip_versions[DCE_HWIP][0] = IP_VERSION(3, 5, 0);
+       adev->dm.dc->caps.is_apu = true;
+       adev->dm.dc->res_pool->usb4_dpia_count = 1;
+
+       KUNIT_EXPECT_EQ(test, dm_dmub_hw_init(adev), 0);
+       KUNIT_EXPECT_TRUE(test, adev->dm.dmub_srv->hw_init);
+}
+
+/**
+ * dm_test_dmub_hw_init_sanity_checks_dcn31 - Test hw init enables DCN31 
sanity checks
+ * @test: The KUnit test context
+ *
+ * On DCN3.1.2 with a DMCUB firmware version in the affected range,
+ * dm_dmub_hw_init() should enable the DC sanity-check debug flag.
+ */
+static void dm_test_dmub_hw_init_sanity_checks_dcn31(struct kunit *test)
+{
+       struct amdgpu_device *adev = dm_test_alloc_adev_with_dmub(test);
+
+       adev->ip_versions[DCE_HWIP][0] = IP_VERSION(3, 1, 2);
+       adev->dm.dmcub_fw_version = DMUB_FW_VERSION(4, 0, 10);
+
+       KUNIT_EXPECT_EQ(test, dm_dmub_hw_init(adev), 0);
+       KUNIT_EXPECT_TRUE(test, adev->dm.dc->debug.sanity_checks);
+}
+
+/**
+ * dm_test_dmub_hw_init_sanity_checks_dcn314 - Test hw init enables DCN314 
sanity checks
+ * @test: The KUnit test context
+ *
+ * On DCN3.1.4 with a DMCUB firmware version in the affected range,
+ * dm_dmub_hw_init() should enable the DC sanity-check debug flag.
+ */
+static void dm_test_dmub_hw_init_sanity_checks_dcn314(struct kunit *test)
+{
+       struct amdgpu_device *adev = dm_test_alloc_adev_with_dmub(test);
+
+       adev->ip_versions[DCE_HWIP][0] = IP_VERSION(3, 1, 4);
+       adev->dm.dmcub_fw_version = DMUB_FW_VERSION(4, 0, 10);
+
+       KUNIT_EXPECT_EQ(test, dm_dmub_hw_init(adev), 0);
+       KUNIT_EXPECT_TRUE(test, adev->dm.dc->debug.sanity_checks);
+}
+
+/**
+ * dm_test_dmub_hw_init_dmcu_abm - Test hw init initializes DMCU and ABM when 
present
+ * @test: The KUnit test context
+ *
+ * When the resource pool exposes a DMCU and ABM, dm_dmub_hw_init() should
+ * program the PSP version, invoke the DMCU init callback, and record the
+ * running state reported by the DMCU.
+ */
+static void dm_test_dmub_hw_init_dmcu_abm(struct kunit *test)
+{
+       struct amdgpu_device *adev = dm_test_alloc_adev_with_dmub(test);
+       struct dmcu *dmcu;
+       struct abm *abm;
+
+       dmcu = kunit_kzalloc(test, sizeof(*dmcu), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dmcu);
+
+       abm = kunit_kzalloc(test, sizeof(*abm), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, abm);
+
+       dmcu->funcs = &dm_test_dmcu_funcs;
+       dmcu->psp_version = 0x12345678;
+       adev->dm.dc->res_pool->dmcu = dmcu;
+       adev->dm.dc->res_pool->abm = abm;
+
+       KUNIT_EXPECT_EQ(test, dm_dmub_hw_init(adev), 0);
+       KUNIT_EXPECT_TRUE(test, abm->dmcu_is_running);
+}
+
 /* Tests for dm_dmub_hw_resume() */
 
 /**
@@ -561,6 +867,15 @@ static struct kunit_case amdgpu_dm_dmub_tests[] = {
        KUNIT_CASE(dm_test_dmub_hw_init_no_dmub_srv),
        KUNIT_CASE(dm_test_dmub_hw_init_no_fb_info),
        KUNIT_CASE(dm_test_dmub_hw_init_no_firmware),
+       KUNIT_CASE(dm_test_dmub_hw_init_success_fake_dmub),
+       KUNIT_CASE(dm_test_dmub_hw_init_no_hw_support),
+       KUNIT_CASE(dm_test_dmub_hw_init_bss_data),
+       KUNIT_CASE(dm_test_dmub_hw_init_hw_init_fails),
+       KUNIT_CASE(dm_test_dmub_hw_init_auto_load_timeout),
+       KUNIT_CASE(dm_test_dmub_hw_init_apu_dpia_dcn35),
+       KUNIT_CASE(dm_test_dmub_hw_init_sanity_checks_dcn31),
+       KUNIT_CASE(dm_test_dmub_hw_init_sanity_checks_dcn314),
+       KUNIT_CASE(dm_test_dmub_hw_init_dmcu_abm),
        /* dm_dmub_hw_resume() */
        KUNIT_CASE(dm_test_dmub_hw_resume_no_dmub_srv),
        /* dm_dmub_sw_init() */
-- 
2.43.0

Reply via email to