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
