drm/amd/display: add dm_dmub_hw_init KUnit coverage

[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 <bhawanpreet.lakha@amd.com>
Signed-off-by: Alex Hung <alex.hung@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
Tested-by: Dan Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
Alex Hung
2026-07-06 09:16:18 -06:00
committed by Alex Deucher
parent 0c0d5174b0
commit f1d518d1b7

View File

@@ -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() */