summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Hung <alex.hung@amd.com>2026-04-27 20:49:30 -0600
committerAlex Deucher <alexander.deucher@amd.com>2026-06-17 16:15:26 -0400
commit4734e045f49d9bb801b4a3eef30d6ca7fba44280 (patch)
tree628022b6170f802303166bd9c260e4ebdc9d882e
parentee55bf7d6a63f02738d79aa4368c145e97e032e3 (diff)
drm/amd/display: Extract DMUB code to amdgpu_dm_dmub
Move DMUB-related functions and firmware defines from amdgpu_dm.c into new amdgpu_dm_dmub.c and amdgpu_dm_dmub.h files to reduce the size of amdgpu_dm.c and improve code organization. No functional change intended. Assisted-by: Copilot:Claude-Opus-4.6 Reviewed-by: Bhawanpreet Lakha <bhawanpreet.lakha@amd.com> Signed-off-by: Alex Hung <alex.hung@amd.com> Signed-off-by: Chenyu Chen <chen-yu.chen@amd.com> Tested-by: Daniel Wheeler <daniel.wheeler@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/Makefile3
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c927
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c924
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.h68
4 files changed, 1002 insertions, 920 deletions
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile b/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile
index 83a7d03a0348..a6408da05583 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile
@@ -43,7 +43,8 @@ AMDGPUDM = \
amdgpu_dm_colorop.o \
amdgpu_dm_ism.o \
amdgpu_dm_backlight.o \
- amdgpu_dm_audio.o
+ amdgpu_dm_audio.o \
+ amdgpu_dm_dmub.o
ifdef CONFIG_DRM_AMD_DC_FP
AMDGPUDM += dc_fpu.o
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 d72ce66e3fd4..f5766d083213 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -68,6 +68,7 @@
#include "amdgpu_dm_replay.h"
#include "amdgpu_dm_backlight.h"
#include "amdgpu_dm_audio.h"
+#include "amdgpu_dm_dmub.h"
#include "ivsrcid/ivsrcid_vislands30.h"
@@ -108,60 +109,9 @@
#include "modules/inc/mod_power.h"
#include "modules/power/power_helpers.h"
-static_assert(AMDGPU_DMUB_NOTIFICATION_MAX == DMUB_NOTIFICATION_MAX, "AMDGPU_DMUB_NOTIFICATION_MAX mismatch");
-
-#define FIRMWARE_RENOIR_DMUB "amdgpu/renoir_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_RENOIR_DMUB);
-#define FIRMWARE_SIENNA_CICHLID_DMUB "amdgpu/sienna_cichlid_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_SIENNA_CICHLID_DMUB);
-#define FIRMWARE_NAVY_FLOUNDER_DMUB "amdgpu/navy_flounder_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_NAVY_FLOUNDER_DMUB);
-#define FIRMWARE_GREEN_SARDINE_DMUB "amdgpu/green_sardine_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_GREEN_SARDINE_DMUB);
-#define FIRMWARE_VANGOGH_DMUB "amdgpu/vangogh_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_VANGOGH_DMUB);
-#define FIRMWARE_DIMGREY_CAVEFISH_DMUB "amdgpu/dimgrey_cavefish_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_DIMGREY_CAVEFISH_DMUB);
-#define FIRMWARE_BEIGE_GOBY_DMUB "amdgpu/beige_goby_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_BEIGE_GOBY_DMUB);
-#define FIRMWARE_YELLOW_CARP_DMUB "amdgpu/yellow_carp_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_YELLOW_CARP_DMUB);
-#define FIRMWARE_DCN_314_DMUB "amdgpu/dcn_3_1_4_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_DCN_314_DMUB);
-#define FIRMWARE_DCN_315_DMUB "amdgpu/dcn_3_1_5_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_DCN_315_DMUB);
-#define FIRMWARE_DCN316_DMUB "amdgpu/dcn_3_1_6_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_DCN316_DMUB);
-
-#define FIRMWARE_DCN_V3_2_0_DMCUB "amdgpu/dcn_3_2_0_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_DCN_V3_2_0_DMCUB);
-#define FIRMWARE_DCN_V3_2_1_DMCUB "amdgpu/dcn_3_2_1_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_DCN_V3_2_1_DMCUB);
-
-#define FIRMWARE_RAVEN_DMCU "amdgpu/raven_dmcu.bin"
MODULE_FIRMWARE(FIRMWARE_RAVEN_DMCU);
-
-#define FIRMWARE_NAVI12_DMCU "amdgpu/navi12_dmcu.bin"
MODULE_FIRMWARE(FIRMWARE_NAVI12_DMCU);
-#define FIRMWARE_DCN_35_DMUB "amdgpu/dcn_3_5_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_DCN_35_DMUB);
-
-#define FIRMWARE_DCN_351_DMUB "amdgpu/dcn_3_5_1_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_DCN_351_DMUB);
-
-#define FIRMWARE_DCN_36_DMUB "amdgpu/dcn_3_6_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_DCN_36_DMUB);
-
-#define FIRMWARE_DCN_401_DMUB "amdgpu/dcn_4_0_1_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_DCN_401_DMUB);
-
-#define FIRMWARE_DCN_42_DMUB "amdgpu/dcn_4_2_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_DCN_42_DMUB);
-
-#define FIRMWARE_DCN_42B_DMUB "amdgpu/dcn_4_2_1_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_DCN_42B_DMUB);
-
/**
* DOC: overview
*
@@ -782,47 +732,6 @@ static void dm_dcn_vertical_interrupt0_high_irq(void *interrupt_params)
#endif /* CONFIG_DRM_AMD_SECURE_DISPLAY */
/**
- * dmub_aux_setconfig_callback - Callback for AUX or SET_CONFIG command.
- * @adev: amdgpu_device pointer
- * @notify: dmub notification structure
- *
- * Dmub AUX or SET_CONFIG command completion processing callback
- * Copies dmub notification to DM which is to be read by AUX command.
- * issuing thread and also signals the event to wake up the thread.
- */
-static void dmub_aux_setconfig_callback(struct amdgpu_device *adev,
- struct dmub_notification *notify)
-{
- if (adev->dm.dmub_notify)
- memcpy(adev->dm.dmub_notify, notify, sizeof(struct dmub_notification));
- if (notify->type == DMUB_NOTIFICATION_AUX_REPLY)
- complete(&adev->dm.dmub_aux_transfer_done);
-}
-
-static void dmub_aux_fused_io_callback(struct amdgpu_device *adev,
- struct dmub_notification *notify)
-{
- if (!adev || !notify) {
- ASSERT(false);
- return;
- }
-
- const struct dmub_cmd_fused_request *req = &notify->fused_request;
- const uint8_t ddc_line = req->u.aux.ddc_line;
-
- if (ddc_line >= ARRAY_SIZE(adev->dm.fused_io)) {
- ASSERT(false);
- return;
- }
-
- struct fused_io_sync *sync = &adev->dm.fused_io[ddc_line];
-
- static_assert(sizeof(*req) <= sizeof(sync->reply_data), "Size mismatch");
- memcpy(sync->reply_data, req, sizeof(*req));
- complete(&sync->replied);
-}
-
-/**
* dmub_hpd_callback - DMUB HPD interrupt processing callback.
* @adev: amdgpu_device pointer
* @notify: dmub notification structure
@@ -911,32 +820,6 @@ static void dmub_hpd_sense_callback(struct amdgpu_device *adev,
drm_dbg_driver(adev_to_drm(adev), "DMUB HPD SENSE callback.\n");
}
-/**
- * register_dmub_notify_callback - Sets callback for DMUB notify
- * @adev: amdgpu_device pointer
- * @type: Type of dmub notification
- * @callback: Dmub interrupt callback function
- * @dmub_int_thread_offload: offload indicator
- *
- * API to register a dmub callback handler for a dmub notification
- * Also sets indicator whether callback processing to be offloaded.
- * to dmub interrupt handling thread
- * Return: true if successfully registered, false if there is existing registration
- */
-static bool register_dmub_notify_callback(struct amdgpu_device *adev,
- enum dmub_notification_type type,
- dmub_notify_interrupt_callback_t callback,
- bool dmub_int_thread_offload)
-{
- if (callback != NULL && type < ARRAY_SIZE(adev->dm.dmub_thread_offload)) {
- adev->dm.dmub_callback[type] = callback;
- adev->dm.dmub_thread_offload[type] = dmub_int_thread_offload;
- } else
- return false;
-
- return true;
-}
-
static void dm_handle_hpd_work(struct work_struct *work)
{
struct dmub_hpd_work *dmub_hpd_wrk;
@@ -1110,224 +993,6 @@ static void amdgpu_dm_fbc_init(struct drm_connector *connector)
}
-static int dm_dmub_hw_init(struct amdgpu_device *adev)
-{
- const struct dmcub_firmware_header_v1_0 *hdr;
- struct dmub_srv *dmub_srv = adev->dm.dmub_srv;
- struct dmub_srv_fb_info *fb_info = adev->dm.dmub_fb_info;
- const struct firmware *dmub_fw = adev->dm.dmub_fw;
- struct dc *dc = adev->dm.dc;
- struct dmcu *dmcu = adev->dm.dc->res_pool->dmcu;
- struct abm *abm = adev->dm.dc->res_pool->abm;
- struct dc_context *ctx = adev->dm.dc->ctx;
- struct dmub_srv_hw_params hw_params;
- enum dmub_status status;
- const unsigned char *fw_inst_const, *fw_bss_data;
- u32 i, fw_inst_const_size, fw_bss_data_size;
- bool has_hw_support;
-
- if (!dmub_srv)
- /* DMUB isn't supported on the ASIC. */
- return 0;
-
- if (!fb_info) {
- drm_err(adev_to_drm(adev), "No framebuffer info for DMUB service.\n");
- return -EINVAL;
- }
-
- if (!dmub_fw) {
- /* Firmware required for DMUB support. */
- drm_err(adev_to_drm(adev), "No firmware provided for DMUB.\n");
- return -EINVAL;
- }
-
- /* initialize register offsets for ASICs with runtime initialization available */
- if (dmub_srv->hw_funcs.init_reg_offsets)
- dmub_srv->hw_funcs.init_reg_offsets(dmub_srv, ctx);
-
- status = dmub_srv_has_hw_support(dmub_srv, &has_hw_support);
- if (status != DMUB_STATUS_OK) {
- drm_err(adev_to_drm(adev), "Error checking HW support for DMUB: %d\n", status);
- return -EINVAL;
- }
-
- if (!has_hw_support) {
- drm_info(adev_to_drm(adev), "DMUB unsupported on ASIC\n");
- return 0;
- }
-
- /* Reset DMCUB if it was previously running - before we overwrite its memory. */
- status = dmub_srv_hw_reset(dmub_srv);
- if (status != DMUB_STATUS_OK)
- drm_warn(adev_to_drm(adev), "Error resetting DMUB HW: %d\n", status);
-
- hdr = (const struct dmcub_firmware_header_v1_0 *)dmub_fw->data;
-
- fw_inst_const = dmub_fw->data +
- le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
- PSP_HEADER_BYTES_256;
-
- fw_bss_data = dmub_fw->data +
- le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
- le32_to_cpu(hdr->inst_const_bytes);
-
- /* Copy firmware and bios info into FB memory. */
- fw_inst_const_size = adev->dm.fw_inst_size;
-
- fw_bss_data_size = le32_to_cpu(hdr->bss_data_bytes);
-
- /* if adev->firmware.load_type == AMDGPU_FW_LOAD_PSP,
- * amdgpu_ucode_init_single_fw will load dmub firmware
- * fw_inst_const part to cw0; otherwise, the firmware back door load
- * will be done by dm_dmub_hw_init
- */
- if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP) {
- memcpy(fb_info->fb[DMUB_WINDOW_0_INST_CONST].cpu_addr, fw_inst_const,
- fw_inst_const_size);
- }
-
- if (fw_bss_data_size)
- memcpy(fb_info->fb[DMUB_WINDOW_2_BSS_DATA].cpu_addr,
- fw_bss_data, fw_bss_data_size);
-
- /* Copy firmware bios info into FB memory. */
- memcpy(fb_info->fb[DMUB_WINDOW_3_VBIOS].cpu_addr, adev->bios,
- adev->bios_size);
-
- /* Reset regions that need to be reset. */
- memset(fb_info->fb[DMUB_WINDOW_4_MAILBOX].cpu_addr, 0,
- fb_info->fb[DMUB_WINDOW_4_MAILBOX].size);
-
- memset(fb_info->fb[DMUB_WINDOW_5_TRACEBUFF].cpu_addr, 0,
- fb_info->fb[DMUB_WINDOW_5_TRACEBUFF].size);
-
- memset(fb_info->fb[DMUB_WINDOW_6_FW_STATE].cpu_addr, 0,
- fb_info->fb[DMUB_WINDOW_6_FW_STATE].size);
-
- memset(fb_info->fb[DMUB_WINDOW_SHARED_STATE].cpu_addr, 0,
- fb_info->fb[DMUB_WINDOW_SHARED_STATE].size);
-
- /* Initialize hardware. */
- memset(&hw_params, 0, sizeof(hw_params));
- hw_params.soc_fb_info.fb_base = adev->gmc.fb_start;
- hw_params.soc_fb_info.fb_offset = adev->vm_manager.vram_base_offset;
-
- /* backdoor load firmware and trigger dmub running */
- if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP)
- hw_params.load_inst_const = true;
-
- if (dmcu)
- hw_params.psp_version = dmcu->psp_version;
-
- for (i = 0; i < fb_info->num_fb; ++i)
- hw_params.fb[i] = &fb_info->fb[i];
-
- /* Enable usb4 dpia in the FW APU */
- if (dc->caps.is_apu &&
- dc->res_pool->usb4_dpia_count != 0 &&
- !dc->debug.dpia_debug.bits.disable_dpia) {
- hw_params.dpia_supported = true;
- hw_params.disable_dpia = dc->debug.dpia_debug.bits.disable_dpia;
- hw_params.dpia_hpd_int_enable_supported = false;
- hw_params.enable_non_transparent_setconfig = dc->config.consolidated_dpia_dp_lt;
- hw_params.disable_dpia_bw_allocation = !dc->config.usb4_bw_alloc_support;
- }
-
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(3, 5, 0):
- case IP_VERSION(3, 5, 1):
- case IP_VERSION(3, 6, 0):
- case IP_VERSION(4, 2, 0):
- case IP_VERSION(4, 2, 1):
- hw_params.ips_sequential_ono = adev->external_rev_id > 0x10;
- hw_params.lower_hbr3_phy_ssc = true;
- break;
- default:
- break;
- }
-
- status = dmub_srv_hw_init(dmub_srv, &hw_params);
- if (status != DMUB_STATUS_OK) {
- drm_err(adev_to_drm(adev), "Error initializing DMUB HW: %d\n", status);
- return -EINVAL;
- }
-
- /* Wait for firmware load to finish. */
- status = dmub_srv_wait_for_auto_load(dmub_srv, 100000);
- if (status != DMUB_STATUS_OK)
- drm_warn(adev_to_drm(adev), "Wait for DMUB auto-load failed: %d\n", status);
-
- /* Init DMCU and ABM if available. */
- if (dmcu && abm) {
- dmcu->funcs->dmcu_init(dmcu);
- abm->dmcu_is_running = dmcu->funcs->is_dmcu_initialized(dmcu);
- }
-
- if (!adev->dm.dc->ctx->dmub_srv)
- adev->dm.dc->ctx->dmub_srv = dc_dmub_srv_create(adev->dm.dc, dmub_srv);
- if (!adev->dm.dc->ctx->dmub_srv) {
- drm_err(adev_to_drm(adev), "Couldn't allocate DC DMUB server!\n");
- return -ENOMEM;
- }
-
- drm_info(adev_to_drm(adev), "DMUB hardware initialized: version=0x%08X\n",
- adev->dm.dmcub_fw_version);
-
- /* Keeping sanity checks off if
- * DCN31 >= 4.0.59.0
- * DCN314 >= 8.0.16.0
- * Otherwise, turn on sanity checks
- */
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(3, 1, 2):
- case IP_VERSION(3, 1, 3):
- if (adev->dm.dmcub_fw_version &&
- adev->dm.dmcub_fw_version >= DMUB_FW_VERSION(4, 0, 0) &&
- adev->dm.dmcub_fw_version < DMUB_FW_VERSION(4, 0, 59))
- adev->dm.dc->debug.sanity_checks = true;
- break;
- case IP_VERSION(3, 1, 4):
- if (adev->dm.dmcub_fw_version &&
- adev->dm.dmcub_fw_version >= DMUB_FW_VERSION(4, 0, 0) &&
- adev->dm.dmcub_fw_version < DMUB_FW_VERSION(8, 0, 16))
- adev->dm.dc->debug.sanity_checks = true;
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static void dm_dmub_hw_resume(struct amdgpu_device *adev)
-{
- struct dmub_srv *dmub_srv = adev->dm.dmub_srv;
- enum dmub_status status;
- bool init;
- int r;
-
- if (!dmub_srv) {
- /* DMUB isn't supported on the ASIC. */
- return;
- }
-
- status = dmub_srv_is_hw_init(dmub_srv, &init);
- if (status != DMUB_STATUS_OK)
- drm_warn(adev_to_drm(adev), "DMUB hardware init check failed: %d\n", status);
-
- if (status == DMUB_STATUS_OK && init) {
- /* Wait for firmware load to finish. */
- status = dmub_srv_wait_for_auto_load(dmub_srv, 100000);
- if (status != DMUB_STATUS_OK)
- drm_warn(adev_to_drm(adev), "Wait for DMUB auto-load failed: %d\n", status);
- } else {
- /* Perform the full hardware initialization. */
- r = dm_dmub_hw_init(adev);
- if (r)
- drm_err(adev_to_drm(adev), "DMUB interface failed to initialize: status=%d\n", r);
- }
-}
-
static void mmhub_read_system_context(struct amdgpu_device *adev, struct dc_phy_addr_space_config *pa_config)
{
u64 pt_base;
@@ -1635,119 +1300,6 @@ dm_free_gpu_mem(
}
-static enum dmub_status
-dm_dmub_send_vbios_gpint_command(struct amdgpu_device *adev,
- enum dmub_gpint_command command_code,
- uint16_t param,
- uint32_t timeout_us)
-{
- union dmub_gpint_data_register reg, test;
- uint32_t i;
-
- /* Assume that VBIOS DMUB is ready to take commands */
-
- reg.bits.status = 1;
- reg.bits.command_code = command_code;
- reg.bits.param = param;
-
- cgs_write_register(adev->dm.cgs_device, 0x34c0 + 0x01f8, reg.all);
-
- for (i = 0; i < timeout_us; ++i) {
- udelay(1);
-
- /* Check if our GPINT got acked */
- reg.bits.status = 0;
- test = (union dmub_gpint_data_register)
- cgs_read_register(adev->dm.cgs_device, 0x34c0 + 0x01f8);
-
- if (test.all == reg.all)
- return DMUB_STATUS_OK;
- }
-
- return DMUB_STATUS_TIMEOUT;
-}
-
-static void *dm_dmub_get_vbios_bounding_box(struct amdgpu_device *adev)
-{
- void *bb;
- long long addr;
- unsigned int bb_size;
- int i = 0;
- uint16_t chunk;
- enum dmub_gpint_command send_addrs[] = {
- DMUB_GPINT__SET_BB_ADDR_WORD0,
- DMUB_GPINT__SET_BB_ADDR_WORD1,
- DMUB_GPINT__SET_BB_ADDR_WORD2,
- DMUB_GPINT__SET_BB_ADDR_WORD3,
- };
- enum dmub_status ret;
-
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(4, 0, 1):
- bb_size = sizeof(struct dml2_soc_bb);
- break;
- case IP_VERSION(4, 2, 0):
- case IP_VERSION(4, 2, 1):
- bb_size = sizeof(struct dml2_soc_bb);
- break;
- default:
- return NULL;
- }
-
- bb = dm_allocate_gpu_mem(adev,
- DC_MEM_ALLOC_TYPE_GART,
- bb_size,
- &addr);
- if (!bb)
- return NULL;
-
- for (i = 0; i < 4; i++) {
- /* Extract 16-bit chunk */
- chunk = ((uint64_t) addr >> (i * 16)) & 0xFFFF;
- /* Send the chunk */
- ret = dm_dmub_send_vbios_gpint_command(adev, send_addrs[i], chunk, 30000);
- if (ret != DMUB_STATUS_OK)
- goto free_bb;
- }
-
- /* Now ask DMUB to copy the bb */
- ret = dm_dmub_send_vbios_gpint_command(adev, DMUB_GPINT__BB_COPY, 1, 200000);
- if (ret != DMUB_STATUS_OK)
- goto free_bb;
-
- return bb;
-
-free_bb:
- dm_free_gpu_mem(adev, DC_MEM_ALLOC_TYPE_GART, (void *) bb);
- return NULL;
-
-}
-
-static enum dmub_ips_disable_type dm_get_default_ips_mode(
- struct amdgpu_device *adev)
-{
- enum dmub_ips_disable_type ret = DMUB_IPS_ENABLE;
-
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(3, 5, 0):
- case IP_VERSION(3, 6, 0):
- case IP_VERSION(3, 5, 1):
- ret = DMUB_IPS_RCG_IN_ACTIVE_IPS2_IN_OFF;
- break;
- case IP_VERSION(4, 2, 0):
- case IP_VERSION(4, 2, 1):
- ret = DMUB_IPS_ENABLE;
- break;
- default:
- /* ASICs older than DCN35 do not have IPSs */
- if (amdgpu_ip_version(adev, DCE_HWIP, 0) < IP_VERSION(3, 5, 0))
- ret = DMUB_IPS_DISABLE_ALL;
- break;
- }
-
- return ret;
-}
-
static int amdgpu_dm_init_power_module(struct amdgpu_display_manager *dm)
{
struct mod_power_init_params init_data[MAX_NUM_EDP];
@@ -2143,8 +1695,8 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
}
amdgpu_dm_outbox_init(adev);
- if (!register_dmub_notify_callback(adev, DMUB_NOTIFICATION_AUX_REPLY,
- dmub_aux_setconfig_callback, false)) {
+ if (!dm_register_dmub_notify_callback(adev, DMUB_NOTIFICATION_AUX_REPLY,
+ dm_dmub_aux_setconfig_callback, false)) {
drm_err(adev_to_drm(adev), "fail to register dmub aux callback");
goto error;
}
@@ -2152,8 +1704,8 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
for (size_t i = 0; i < ARRAY_SIZE(adev->dm.fused_io); i++)
init_completion(&adev->dm.fused_io[i].replied);
- if (!register_dmub_notify_callback(adev, DMUB_NOTIFICATION_FUSED_IO,
- dmub_aux_fused_io_callback, false)) {
+ if (!dm_register_dmub_notify_callback(adev, DMUB_NOTIFICATION_FUSED_IO,
+ dm_dmub_aux_fused_io_callback, false)) {
drm_err(adev_to_drm(adev), "fail to register dmub fused io callback");
goto error;
}
@@ -2441,224 +1993,6 @@ static int load_dmcu_fw(struct amdgpu_device *adev)
return 0;
}
-static uint32_t amdgpu_dm_dmub_reg_read(void *ctx, uint32_t address)
-{
- struct amdgpu_device *adev = ctx;
-
- return dm_read_reg(adev->dm.dc->ctx, address);
-}
-
-static void amdgpu_dm_dmub_reg_write(void *ctx, uint32_t address,
- uint32_t value)
-{
- struct amdgpu_device *adev = ctx;
-
- return dm_write_reg(adev->dm.dc->ctx, address, value);
-}
-
-static int dm_dmub_sw_init(struct amdgpu_device *adev)
-{
- struct dmub_srv_create_params create_params;
- struct dmub_srv_fw_meta_info_params fw_meta_info_params;
- struct dmub_srv_region_params region_params;
- struct dmub_srv_region_info region_info;
- struct dmub_srv_memory_params memory_params;
- struct dmub_fw_meta_info fw_info;
- struct dmub_srv_fb_info *fb_info;
- struct dmub_srv *dmub_srv;
- const struct dmcub_firmware_header_v1_0 *hdr;
- enum dmub_asic dmub_asic;
- enum dmub_status status;
- static enum dmub_window_memory_type window_memory_type[DMUB_WINDOW_TOTAL] = {
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_0_INST_CONST
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_1_STACK
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_2_BSS_DATA
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_3_VBIOS
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_4_MAILBOX
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_5_TRACEBUFF
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_6_FW_STATE
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_7_SCRATCH_MEM
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_IB_MEM
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_SHARED_STATE
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_LSDMA_BUFFER
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_CURSOR_OFFLOAD
- };
- int r;
-
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(2, 1, 0):
- dmub_asic = DMUB_ASIC_DCN21;
- break;
- case IP_VERSION(3, 0, 0):
- dmub_asic = DMUB_ASIC_DCN30;
- break;
- case IP_VERSION(3, 0, 1):
- dmub_asic = DMUB_ASIC_DCN301;
- break;
- case IP_VERSION(3, 0, 2):
- dmub_asic = DMUB_ASIC_DCN302;
- break;
- case IP_VERSION(3, 0, 3):
- dmub_asic = DMUB_ASIC_DCN303;
- break;
- case IP_VERSION(3, 1, 2):
- case IP_VERSION(3, 1, 3):
- dmub_asic = (adev->external_rev_id == YELLOW_CARP_B0) ? DMUB_ASIC_DCN31B : DMUB_ASIC_DCN31;
- break;
- case IP_VERSION(3, 1, 4):
- dmub_asic = DMUB_ASIC_DCN314;
- break;
- case IP_VERSION(3, 1, 5):
- dmub_asic = DMUB_ASIC_DCN315;
- break;
- case IP_VERSION(3, 1, 6):
- dmub_asic = DMUB_ASIC_DCN316;
- break;
- case IP_VERSION(3, 2, 0):
- dmub_asic = DMUB_ASIC_DCN32;
- break;
- case IP_VERSION(3, 2, 1):
- dmub_asic = DMUB_ASIC_DCN321;
- break;
- case IP_VERSION(3, 5, 0):
- case IP_VERSION(3, 5, 1):
- dmub_asic = DMUB_ASIC_DCN35;
- break;
- case IP_VERSION(3, 6, 0):
- dmub_asic = DMUB_ASIC_DCN36;
- break;
- case IP_VERSION(4, 0, 1):
- dmub_asic = DMUB_ASIC_DCN401;
- break;
- case IP_VERSION(4, 2, 0):
- dmub_asic = DMUB_ASIC_DCN42;
- break;
- case IP_VERSION(4, 2, 1):
- dmub_asic = DMUB_ASIC_DCN42B;
- break;
- default:
- /* ASIC doesn't support DMUB. */
- return 0;
- }
-
- hdr = (const struct dmcub_firmware_header_v1_0 *)adev->dm.dmub_fw->data;
- adev->dm.dmcub_fw_version = le32_to_cpu(hdr->header.ucode_version);
-
- if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) {
- adev->firmware.ucode[AMDGPU_UCODE_ID_DMCUB].ucode_id =
- AMDGPU_UCODE_ID_DMCUB;
- adev->firmware.ucode[AMDGPU_UCODE_ID_DMCUB].fw =
- adev->dm.dmub_fw;
- adev->firmware.fw_size +=
- ALIGN(le32_to_cpu(hdr->inst_const_bytes), PAGE_SIZE);
-
- drm_info(adev_to_drm(adev), "Loading DMUB firmware via PSP: version=0x%08X\n",
- adev->dm.dmcub_fw_version);
- }
-
-
- adev->dm.dmub_srv = kzalloc_obj(*adev->dm.dmub_srv);
- dmub_srv = adev->dm.dmub_srv;
-
- if (!dmub_srv) {
- drm_err(adev_to_drm(adev), "Failed to allocate DMUB service!\n");
- return -ENOMEM;
- }
-
- memset(&create_params, 0, sizeof(create_params));
- create_params.user_ctx = adev;
- create_params.funcs.reg_read = amdgpu_dm_dmub_reg_read;
- create_params.funcs.reg_write = amdgpu_dm_dmub_reg_write;
- create_params.asic = dmub_asic;
-
- /* Create the DMUB service. */
- status = dmub_srv_create(dmub_srv, &create_params);
- if (status != DMUB_STATUS_OK) {
- drm_err(adev_to_drm(adev), "Error creating DMUB service: %d\n", status);
- return -EINVAL;
- }
-
- /* Extract the FW meta info. */
- memset(&fw_meta_info_params, 0, sizeof(fw_meta_info_params));
-
- fw_meta_info_params.inst_const_size = le32_to_cpu(hdr->inst_const_bytes) -
- PSP_HEADER_BYTES_256;
- fw_meta_info_params.bss_data_size = le32_to_cpu(hdr->bss_data_bytes);
- fw_meta_info_params.fw_inst_const = adev->dm.dmub_fw->data +
- le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
- PSP_HEADER_BYTES_256;
- fw_meta_info_params.fw_bss_data = fw_meta_info_params.bss_data_size ? adev->dm.dmub_fw->data +
- le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
- le32_to_cpu(hdr->inst_const_bytes) : NULL;
- fw_meta_info_params.custom_psp_footer_size = 0;
-
- status = dmub_srv_get_fw_meta_info_from_raw_fw(&fw_meta_info_params, &fw_info);
- if (status != DMUB_STATUS_OK) {
- /* Skip returning early, just log the error. */
- drm_err(adev_to_drm(adev), "Error getting DMUB FW meta info: %d\n", status);
- // return -EINVAL;
- }
-
- /* Calculate the size of all the regions for the DMUB service. */
- memset(&region_params, 0, sizeof(region_params));
-
- region_params.inst_const_size = fw_meta_info_params.inst_const_size;
- region_params.bss_data_size = fw_meta_info_params.bss_data_size;
- region_params.vbios_size = adev->bios_size;
- region_params.fw_bss_data = fw_meta_info_params.fw_bss_data;
- region_params.fw_inst_const = fw_meta_info_params.fw_inst_const;
- region_params.window_memory_type = window_memory_type;
- region_params.fw_info = (status == DMUB_STATUS_OK) ? &fw_info : NULL;
-
- status = dmub_srv_calc_region_info(dmub_srv, &region_params,
- &region_info);
-
- if (status != DMUB_STATUS_OK) {
- drm_err(adev_to_drm(adev), "Error calculating DMUB region info: %d\n", status);
- return -EINVAL;
- }
-
- /*
- * Allocate a framebuffer based on the total size of all the regions.
- * TODO: Move this into GART.
- */
- r = amdgpu_bo_create_kernel(adev, region_info.fb_size, PAGE_SIZE,
- AMDGPU_GEM_DOMAIN_VRAM |
- AMDGPU_GEM_DOMAIN_GTT,
- &adev->dm.dmub_bo,
- &adev->dm.dmub_bo_gpu_addr,
- &adev->dm.dmub_bo_cpu_addr);
- if (r)
- return r;
-
- /* Rebase the regions on the framebuffer address. */
- memset(&memory_params, 0, sizeof(memory_params));
- memory_params.cpu_fb_addr = adev->dm.dmub_bo_cpu_addr;
- memory_params.gpu_fb_addr = adev->dm.dmub_bo_gpu_addr;
- memory_params.region_info = &region_info;
- memory_params.window_memory_type = window_memory_type;
-
- adev->dm.dmub_fb_info = kzalloc_obj(*adev->dm.dmub_fb_info);
- fb_info = adev->dm.dmub_fb_info;
-
- if (!fb_info) {
- drm_err(adev_to_drm(adev),
- "Failed to allocate framebuffer info for DMUB service!\n");
- return -ENOMEM;
- }
-
- status = dmub_srv_calc_mem_info(dmub_srv, &memory_params, fb_info);
- if (status != DMUB_STATUS_OK) {
- drm_err(adev_to_drm(adev), "Error calculating DMUB FB info: %d\n", status);
- return -EINVAL;
- }
-
- adev->dm.bb_from_dmub = dm_dmub_get_vbios_bounding_box(adev);
- adev->dm.fw_inst_size = fw_meta_info_params.inst_const_size;
-
- return 0;
-}
-
static int dm_sw_init(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
@@ -4382,19 +3716,19 @@ static int register_hpd_handlers(struct amdgpu_device *adev)
int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT;
if (dc_is_dmub_outbox_supported(adev->dm.dc)) {
- if (!register_dmub_notify_callback(adev, DMUB_NOTIFICATION_HPD,
+ if (!dm_register_dmub_notify_callback(adev, DMUB_NOTIFICATION_HPD,
dmub_hpd_callback, true)) {
drm_err(adev_to_drm(adev), "fail to register dmub hpd callback");
return -EINVAL;
}
- if (!register_dmub_notify_callback(adev, DMUB_NOTIFICATION_HPD_IRQ,
+ if (!dm_register_dmub_notify_callback(adev, DMUB_NOTIFICATION_HPD_IRQ,
dmub_hpd_callback, true)) {
drm_err(adev_to_drm(adev), "fail to register dmub hpd callback");
return -EINVAL;
}
- if (!register_dmub_notify_callback(adev, DMUB_NOTIFICATION_HPD_SENSE_NOTIFY,
+ if (!dm_register_dmub_notify_callback(adev, DMUB_NOTIFICATION_HPD_SENSE_NOTIFY,
dmub_hpd_sense_callback, true)) {
drm_err(adev_to_drm(adev), "fail to register dmub hpd sense callback");
return -EINVAL;
@@ -5405,78 +4739,6 @@ DEVICE_ATTR_WO(s3_debug);
#endif
-static int dm_init_microcode(struct amdgpu_device *adev)
-{
- char *fw_name_dmub;
- int r;
-
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(2, 1, 0):
- fw_name_dmub = FIRMWARE_RENOIR_DMUB;
- if (ASICREV_IS_GREEN_SARDINE(adev->external_rev_id))
- fw_name_dmub = FIRMWARE_GREEN_SARDINE_DMUB;
- break;
- case IP_VERSION(3, 0, 0):
- if (amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(10, 3, 0))
- fw_name_dmub = FIRMWARE_SIENNA_CICHLID_DMUB;
- else
- fw_name_dmub = FIRMWARE_NAVY_FLOUNDER_DMUB;
- break;
- case IP_VERSION(3, 0, 1):
- fw_name_dmub = FIRMWARE_VANGOGH_DMUB;
- break;
- case IP_VERSION(3, 0, 2):
- fw_name_dmub = FIRMWARE_DIMGREY_CAVEFISH_DMUB;
- break;
- case IP_VERSION(3, 0, 3):
- fw_name_dmub = FIRMWARE_BEIGE_GOBY_DMUB;
- break;
- case IP_VERSION(3, 1, 2):
- case IP_VERSION(3, 1, 3):
- fw_name_dmub = FIRMWARE_YELLOW_CARP_DMUB;
- break;
- case IP_VERSION(3, 1, 4):
- fw_name_dmub = FIRMWARE_DCN_314_DMUB;
- break;
- case IP_VERSION(3, 1, 5):
- fw_name_dmub = FIRMWARE_DCN_315_DMUB;
- break;
- case IP_VERSION(3, 1, 6):
- fw_name_dmub = FIRMWARE_DCN316_DMUB;
- break;
- case IP_VERSION(3, 2, 0):
- fw_name_dmub = FIRMWARE_DCN_V3_2_0_DMCUB;
- break;
- case IP_VERSION(3, 2, 1):
- fw_name_dmub = FIRMWARE_DCN_V3_2_1_DMCUB;
- break;
- case IP_VERSION(3, 5, 0):
- fw_name_dmub = FIRMWARE_DCN_35_DMUB;
- break;
- case IP_VERSION(3, 5, 1):
- fw_name_dmub = FIRMWARE_DCN_351_DMUB;
- break;
- case IP_VERSION(3, 6, 0):
- fw_name_dmub = FIRMWARE_DCN_36_DMUB;
- break;
- case IP_VERSION(4, 0, 1):
- fw_name_dmub = FIRMWARE_DCN_401_DMUB;
- break;
- case IP_VERSION(4, 2, 0):
- fw_name_dmub = FIRMWARE_DCN_42_DMUB;
- break;
- case IP_VERSION(4, 2, 1):
- fw_name_dmub = FIRMWARE_DCN_42B_DMUB;
- break;
- default:
- /* ASIC doesn't support DMUB. */
- return 0;
- }
- r = amdgpu_ucode_request(adev, &adev->dm.dmub_fw, AMDGPU_UCODE_REQUIRED,
- "%s", fw_name_dmub);
- return r;
-}
-
static int dm_early_init(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
@@ -13009,179 +12271,6 @@ uint32_t dm_read_reg_func(const struct dc_context *ctx, uint32_t address,
return value;
}
-int amdgpu_dm_process_dmub_aux_transfer_sync(
- struct dc_context *ctx,
- unsigned int link_index,
- struct aux_payload *payload,
- enum aux_return_code_type *operation_result)
-{
- struct amdgpu_device *adev = ctx->driver_context;
- struct dmub_notification *p_notify = adev->dm.dmub_notify;
- int ret = -1;
-
- mutex_lock(&adev->dm.dpia_aux_lock);
- if (!dc_process_dmub_aux_transfer_async(ctx->dc, link_index, payload)) {
- *operation_result = AUX_RET_ERROR_ENGINE_ACQUIRE;
- goto out;
- }
-
- if (!wait_for_completion_timeout(&adev->dm.dmub_aux_transfer_done, 10 * HZ)) {
- drm_err(adev_to_drm(adev), "wait_for_completion_timeout timeout!");
- *operation_result = AUX_RET_ERROR_TIMEOUT;
- goto out;
- }
-
- if (p_notify->result != AUX_RET_SUCCESS) {
- /*
- * Transient states before tunneling is enabled could
- * lead to this error. We can ignore this for now.
- */
- if (p_notify->result == AUX_RET_ERROR_PROTOCOL_ERROR) {
- drm_warn(adev_to_drm(adev), "DPIA AUX failed on 0x%x(%d), error %d\n",
- payload->address, payload->length,
- p_notify->result);
- }
- *operation_result = p_notify->result;
- goto out;
- }
-
- payload->reply[0] = adev->dm.dmub_notify->aux_reply.command & 0xF;
- if (adev->dm.dmub_notify->aux_reply.command & 0xF0)
- /* The reply is stored in the top nibble of the command. */
- payload->reply[0] = (adev->dm.dmub_notify->aux_reply.command >> 4) & 0xF;
-
- /*write req may receive a byte indicating partially written number as well*/
- if (p_notify->aux_reply.length)
- memcpy(payload->data, p_notify->aux_reply.data,
- p_notify->aux_reply.length);
-
- /* success */
- ret = p_notify->aux_reply.length;
- *operation_result = p_notify->result;
-out:
- reinit_completion(&adev->dm.dmub_aux_transfer_done);
- mutex_unlock(&adev->dm.dpia_aux_lock);
- return ret;
-}
-
-static void abort_fused_io(
- struct dc_context *ctx,
- const struct dmub_cmd_fused_request *request
-)
-{
- union dmub_rb_cmd command = { 0 };
- struct dmub_rb_cmd_fused_io *io = &command.fused_io;
-
- io->header.type = DMUB_CMD__FUSED_IO;
- io->header.sub_type = DMUB_CMD__FUSED_IO_ABORT;
- io->header.payload_bytes = sizeof(*io) - sizeof(io->header);
- io->request = *request;
- dm_execute_dmub_cmd(ctx, &command, DM_DMUB_WAIT_TYPE_NO_WAIT);
-}
-
-static bool execute_fused_io(
- struct amdgpu_device *dev,
- struct dc_context *ctx,
- union dmub_rb_cmd *commands,
- uint8_t count,
- uint32_t timeout_us
-)
-{
- const uint8_t ddc_line = commands[0].fused_io.request.u.aux.ddc_line;
-
- if (ddc_line >= ARRAY_SIZE(dev->dm.fused_io))
- return false;
-
- struct fused_io_sync *sync = &dev->dm.fused_io[ddc_line];
- struct dmub_rb_cmd_fused_io *first = &commands[0].fused_io;
- const bool result = dm_execute_dmub_cmd_list(ctx, count, commands, DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY)
- && first->header.ret_status
- && first->request.status == FUSED_REQUEST_STATUS_SUCCESS;
-
- if (!result)
- return false;
-
- while (wait_for_completion_timeout(&sync->replied, usecs_to_jiffies(timeout_us))) {
- reinit_completion(&sync->replied);
-
- struct dmub_cmd_fused_request *reply = (struct dmub_cmd_fused_request *) sync->reply_data;
-
- static_assert(sizeof(*reply) <= sizeof(sync->reply_data), "Size mismatch");
-
- if (reply->identifier == first->request.identifier) {
- first->request = *reply;
- return true;
- }
- }
-
- reinit_completion(&sync->replied);
- first->request.status = FUSED_REQUEST_STATUS_TIMEOUT;
- abort_fused_io(ctx, &first->request);
- return false;
-}
-
-bool amdgpu_dm_execute_fused_io(
- struct amdgpu_device *dev,
- struct dc_link *link,
- union dmub_rb_cmd *commands,
- uint8_t count,
- uint32_t timeout_us)
-{
- struct amdgpu_display_manager *dm = &dev->dm;
-
- mutex_lock(&dm->dpia_aux_lock);
-
- const bool result = execute_fused_io(dev, link->ctx, commands, count, timeout_us);
-
- mutex_unlock(&dm->dpia_aux_lock);
- return result;
-}
-
-int amdgpu_dm_process_dmub_set_config_sync(
- struct dc_context *ctx,
- unsigned int link_index,
- struct set_config_cmd_payload *payload,
- enum set_config_status *operation_result)
-{
- struct amdgpu_device *adev = ctx->driver_context;
- bool is_cmd_complete;
- int ret;
-
- mutex_lock(&adev->dm.dpia_aux_lock);
- is_cmd_complete = dc_process_dmub_set_config_async(ctx->dc,
- link_index, payload, adev->dm.dmub_notify);
-
- if (is_cmd_complete || wait_for_completion_timeout(&adev->dm.dmub_aux_transfer_done, 10 * HZ)) {
- ret = 0;
- *operation_result = adev->dm.dmub_notify->sc_status;
- } else {
- drm_err(adev_to_drm(adev), "wait_for_completion_timeout timeout!");
- ret = -1;
- *operation_result = SET_CONFIG_UNKNOWN_ERROR;
- }
-
- if (!is_cmd_complete)
- reinit_completion(&adev->dm.dmub_aux_transfer_done);
- mutex_unlock(&adev->dm.dpia_aux_lock);
- return ret;
-}
-
-bool dm_execute_dmub_cmd(const struct dc_context *ctx, union dmub_rb_cmd *cmd, enum dm_dmub_wait_type wait_type)
-{
- struct amdgpu_device *adev = ctx->driver_context;
-
- guard(spinlock_irqsave)(&adev->dm.dmub_lock);
- return dc_dmub_srv_cmd_run(ctx->dmub_srv, cmd, wait_type);
-}
-
-bool dm_execute_dmub_cmd_list(const struct dc_context *ctx, unsigned int count, union dmub_rb_cmd *cmd, enum dm_dmub_wait_type wait_type)
-{
- struct amdgpu_device *adev = ctx->driver_context;
-
- guard(spinlock_irqsave)(&adev->dm.dmub_lock);
- return dc_dmub_srv_cmd_run_list(ctx->dmub_srv, count, cmd, wait_type);
-}
-
void dm_acpi_process_phy_transition_interlock(
const struct dc_context *ctx,
struct dm_process_phy_transition_init_params process_phy_transition_init_params)
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
new file mode 100644
index 000000000000..739e685f1c3c
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c
@@ -0,0 +1,924 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: AMD
+ *
+ */
+
+#include "dm_services_types.h"
+#include "dc.h"
+#include "dc/inc/core_types.h"
+#include "dc/dc_dmub_srv.h"
+#include "dmub/dmub_srv.h"
+#include "dc/inc/hw/dmcu.h"
+#include "dc/inc/hw/abm.h"
+#include "dal_asic_id.h"
+
+#include "amdgpu.h"
+#include "amdgpu_display.h"
+#include "amdgpu_ucode.h"
+#include "amdgpu_dm.h"
+#include "amdgpu_dm_dmub.h"
+#include <linux/component.h>
+#include <linux/firmware.h>
+
+static_assert(AMDGPU_DMUB_NOTIFICATION_MAX == DMUB_NOTIFICATION_MAX, "AMDGPU_DMUB_NOTIFICATION_MAX mismatch");
+
+MODULE_FIRMWARE(FIRMWARE_RENOIR_DMUB);
+MODULE_FIRMWARE(FIRMWARE_SIENNA_CICHLID_DMUB);
+MODULE_FIRMWARE(FIRMWARE_NAVY_FLOUNDER_DMUB);
+MODULE_FIRMWARE(FIRMWARE_GREEN_SARDINE_DMUB);
+MODULE_FIRMWARE(FIRMWARE_VANGOGH_DMUB);
+MODULE_FIRMWARE(FIRMWARE_DIMGREY_CAVEFISH_DMUB);
+MODULE_FIRMWARE(FIRMWARE_BEIGE_GOBY_DMUB);
+MODULE_FIRMWARE(FIRMWARE_YELLOW_CARP_DMUB);
+MODULE_FIRMWARE(FIRMWARE_DCN_314_DMUB);
+MODULE_FIRMWARE(FIRMWARE_DCN_315_DMUB);
+MODULE_FIRMWARE(FIRMWARE_DCN316_DMUB);
+MODULE_FIRMWARE(FIRMWARE_DCN_V3_2_0_DMCUB);
+MODULE_FIRMWARE(FIRMWARE_DCN_V3_2_1_DMCUB);
+MODULE_FIRMWARE(FIRMWARE_DCN_35_DMUB);
+MODULE_FIRMWARE(FIRMWARE_DCN_351_DMUB);
+MODULE_FIRMWARE(FIRMWARE_DCN_36_DMUB);
+MODULE_FIRMWARE(FIRMWARE_DCN_401_DMUB);
+MODULE_FIRMWARE(FIRMWARE_DCN_42_DMUB);
+MODULE_FIRMWARE(FIRMWARE_DCN_42B_DMUB);
+
+/**
+ * dm_dmub_aux_setconfig_callback - Callback for AUX or SET_CONFIG command.
+ * @adev: amdgpu_device pointer
+ * @notify: dmub notification structure
+ *
+ * Dmub AUX or SET_CONFIG command completion processing callback
+ * Copies dmub notification to DM which is to be read by AUX command.
+ * issuing thread and also signals the event to wake up the thread.
+ */
+void dm_dmub_aux_setconfig_callback(struct amdgpu_device *adev,
+ struct dmub_notification *notify)
+{
+ if (adev->dm.dmub_notify)
+ memcpy(adev->dm.dmub_notify, notify, sizeof(struct dmub_notification));
+ if (notify->type == DMUB_NOTIFICATION_AUX_REPLY)
+ complete(&adev->dm.dmub_aux_transfer_done);
+}
+
+void dm_dmub_aux_fused_io_callback(struct amdgpu_device *adev,
+ struct dmub_notification *notify)
+{
+ if (!adev || !notify) {
+ ASSERT(false);
+ return;
+ }
+
+ const struct dmub_cmd_fused_request *req = &notify->fused_request;
+ const uint8_t ddc_line = req->u.aux.ddc_line;
+
+ if (ddc_line >= ARRAY_SIZE(adev->dm.fused_io)) {
+ ASSERT(false);
+ return;
+ }
+
+ struct fused_io_sync *sync = &adev->dm.fused_io[ddc_line];
+
+ static_assert(sizeof(*req) <= sizeof(sync->reply_data), "Size mismatch");
+ memcpy(sync->reply_data, req, sizeof(*req));
+ complete(&sync->replied);
+}
+
+/**
+ * dm_register_dmub_notify_callback - Sets callback for DMUB notify
+ * @adev: amdgpu_device pointer
+ * @type: Type of dmub notification
+ * @callback: Dmub interrupt callback function
+ * @dmub_int_thread_offload: offload indicator
+ *
+ * API to register a dmub callback handler for a dmub notification
+ * Also sets indicator whether callback processing to be offloaded.
+ * to dmub interrupt handling thread
+ * Return: true if successfully registered, false if there is existing registration
+ */
+bool dm_register_dmub_notify_callback(struct amdgpu_device *adev,
+ enum dmub_notification_type type,
+ dmub_notify_interrupt_callback_t callback,
+ bool dmub_int_thread_offload)
+{
+ if (callback != NULL && type < ARRAY_SIZE(adev->dm.dmub_thread_offload)) {
+ adev->dm.dmub_callback[type] = callback;
+ adev->dm.dmub_thread_offload[type] = dmub_int_thread_offload;
+ } else
+ return false;
+
+ return true;
+}
+
+int dm_dmub_hw_init(struct amdgpu_device *adev)
+{
+ const struct dmcub_firmware_header_v1_0 *hdr;
+ struct dmub_srv *dmub_srv = adev->dm.dmub_srv;
+ struct dmub_srv_fb_info *fb_info = adev->dm.dmub_fb_info;
+ const struct firmware *dmub_fw = adev->dm.dmub_fw;
+ struct dc *dc = adev->dm.dc;
+ struct dmcu *dmcu = adev->dm.dc->res_pool->dmcu;
+ struct abm *abm = adev->dm.dc->res_pool->abm;
+ struct dc_context *ctx = adev->dm.dc->ctx;
+ struct dmub_srv_hw_params hw_params;
+ enum dmub_status status;
+ const unsigned char *fw_inst_const, *fw_bss_data;
+ u32 i, fw_inst_const_size, fw_bss_data_size;
+ bool has_hw_support;
+
+ if (!dmub_srv)
+ /* DMUB isn't supported on the ASIC. */
+ return 0;
+
+ if (!fb_info) {
+ drm_err(adev_to_drm(adev), "No framebuffer info for DMUB service.\n");
+ return -EINVAL;
+ }
+
+ if (!dmub_fw) {
+ /* Firmware required for DMUB support. */
+ drm_err(adev_to_drm(adev), "No firmware provided for DMUB.\n");
+ return -EINVAL;
+ }
+
+ /* initialize register offsets for ASICs with runtime initialization available */
+ if (dmub_srv->hw_funcs.init_reg_offsets)
+ dmub_srv->hw_funcs.init_reg_offsets(dmub_srv, ctx);
+
+ status = dmub_srv_has_hw_support(dmub_srv, &has_hw_support);
+ if (status != DMUB_STATUS_OK) {
+ drm_err(adev_to_drm(adev), "Error checking HW support for DMUB: %d\n", status);
+ return -EINVAL;
+ }
+
+ if (!has_hw_support) {
+ drm_info(adev_to_drm(adev), "DMUB unsupported on ASIC\n");
+ return 0;
+ }
+
+ /* Reset DMCUB if it was previously running - before we overwrite its memory. */
+ status = dmub_srv_hw_reset(dmub_srv);
+ if (status != DMUB_STATUS_OK)
+ drm_warn(adev_to_drm(adev), "Error resetting DMUB HW: %d\n", status);
+
+ hdr = (const struct dmcub_firmware_header_v1_0 *)dmub_fw->data;
+
+ fw_inst_const = dmub_fw->data +
+ le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
+ PSP_HEADER_BYTES_256;
+
+ fw_bss_data = dmub_fw->data +
+ le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
+ le32_to_cpu(hdr->inst_const_bytes);
+
+ /* Copy firmware and bios info into FB memory. */
+ fw_inst_const_size = adev->dm.fw_inst_size;
+
+ fw_bss_data_size = le32_to_cpu(hdr->bss_data_bytes);
+
+ /* if adev->firmware.load_type == AMDGPU_FW_LOAD_PSP,
+ * amdgpu_ucode_init_single_fw will load dmub firmware
+ * fw_inst_const part to cw0; otherwise, the firmware back door load
+ * will be done by dm_dmub_hw_init
+ */
+ if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP) {
+ memcpy(fb_info->fb[DMUB_WINDOW_0_INST_CONST].cpu_addr, fw_inst_const,
+ fw_inst_const_size);
+ }
+
+ if (fw_bss_data_size)
+ memcpy(fb_info->fb[DMUB_WINDOW_2_BSS_DATA].cpu_addr,
+ fw_bss_data, fw_bss_data_size);
+
+ /* Copy firmware bios info into FB memory. */
+ memcpy(fb_info->fb[DMUB_WINDOW_3_VBIOS].cpu_addr, adev->bios,
+ adev->bios_size);
+
+ /* Reset regions that need to be reset. */
+ memset(fb_info->fb[DMUB_WINDOW_4_MAILBOX].cpu_addr, 0,
+ fb_info->fb[DMUB_WINDOW_4_MAILBOX].size);
+
+ memset(fb_info->fb[DMUB_WINDOW_5_TRACEBUFF].cpu_addr, 0,
+ fb_info->fb[DMUB_WINDOW_5_TRACEBUFF].size);
+
+ memset(fb_info->fb[DMUB_WINDOW_6_FW_STATE].cpu_addr, 0,
+ fb_info->fb[DMUB_WINDOW_6_FW_STATE].size);
+
+ memset(fb_info->fb[DMUB_WINDOW_SHARED_STATE].cpu_addr, 0,
+ fb_info->fb[DMUB_WINDOW_SHARED_STATE].size);
+
+ /* Initialize hardware. */
+ memset(&hw_params, 0, sizeof(hw_params));
+ hw_params.soc_fb_info.fb_base = adev->gmc.fb_start;
+ hw_params.soc_fb_info.fb_offset = adev->vm_manager.vram_base_offset;
+
+ /* backdoor load firmware and trigger dmub running */
+ if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP)
+ hw_params.load_inst_const = true;
+
+ if (dmcu)
+ hw_params.psp_version = dmcu->psp_version;
+
+ for (i = 0; i < fb_info->num_fb; ++i)
+ hw_params.fb[i] = &fb_info->fb[i];
+
+ /* Enable usb4 dpia in the FW APU */
+ if (dc->caps.is_apu &&
+ dc->res_pool->usb4_dpia_count != 0 &&
+ !dc->debug.dpia_debug.bits.disable_dpia) {
+ hw_params.dpia_supported = true;
+ hw_params.disable_dpia = dc->debug.dpia_debug.bits.disable_dpia;
+ hw_params.dpia_hpd_int_enable_supported = false;
+ hw_params.enable_non_transparent_setconfig = dc->config.consolidated_dpia_dp_lt;
+ hw_params.disable_dpia_bw_allocation = !dc->config.usb4_bw_alloc_support;
+ }
+
+ switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
+ case IP_VERSION(3, 5, 0):
+ case IP_VERSION(3, 5, 1):
+ case IP_VERSION(3, 6, 0):
+ case IP_VERSION(4, 2, 0):
+ case IP_VERSION(4, 2, 1):
+ hw_params.ips_sequential_ono = adev->external_rev_id > 0x10;
+ hw_params.lower_hbr3_phy_ssc = true;
+ break;
+ default:
+ break;
+ }
+
+ status = dmub_srv_hw_init(dmub_srv, &hw_params);
+ if (status != DMUB_STATUS_OK) {
+ drm_err(adev_to_drm(adev), "Error initializing DMUB HW: %d\n", status);
+ return -EINVAL;
+ }
+
+ /* Wait for firmware load to finish. */
+ status = dmub_srv_wait_for_auto_load(dmub_srv, 100000);
+ if (status != DMUB_STATUS_OK)
+ drm_warn(adev_to_drm(adev), "Wait for DMUB auto-load failed: %d\n", status);
+
+ /* Init DMCU and ABM if available. */
+ if (dmcu && abm) {
+ dmcu->funcs->dmcu_init(dmcu);
+ abm->dmcu_is_running = dmcu->funcs->is_dmcu_initialized(dmcu);
+ }
+
+ if (!adev->dm.dc->ctx->dmub_srv)
+ adev->dm.dc->ctx->dmub_srv = dc_dmub_srv_create(adev->dm.dc, dmub_srv);
+ if (!adev->dm.dc->ctx->dmub_srv) {
+ drm_err(adev_to_drm(adev), "Couldn't allocate DC DMUB server!\n");
+ return -ENOMEM;
+ }
+
+ drm_info(adev_to_drm(adev), "DMUB hardware initialized: version=0x%08X\n",
+ adev->dm.dmcub_fw_version);
+
+ /* Keeping sanity checks off if
+ * DCN31 >= 4.0.59.0
+ * DCN314 >= 8.0.16.0
+ * Otherwise, turn on sanity checks
+ */
+ switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
+ case IP_VERSION(3, 1, 2):
+ case IP_VERSION(3, 1, 3):
+ if (adev->dm.dmcub_fw_version &&
+ adev->dm.dmcub_fw_version >= DMUB_FW_VERSION(4, 0, 0) &&
+ adev->dm.dmcub_fw_version < DMUB_FW_VERSION(4, 0, 59))
+ adev->dm.dc->debug.sanity_checks = true;
+ break;
+ case IP_VERSION(3, 1, 4):
+ if (adev->dm.dmcub_fw_version &&
+ adev->dm.dmcub_fw_version >= DMUB_FW_VERSION(4, 0, 0) &&
+ adev->dm.dmcub_fw_version < DMUB_FW_VERSION(8, 0, 16))
+ adev->dm.dc->debug.sanity_checks = true;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+void dm_dmub_hw_resume(struct amdgpu_device *adev)
+{
+ struct dmub_srv *dmub_srv = adev->dm.dmub_srv;
+ enum dmub_status status;
+ bool init;
+ int r;
+
+ if (!dmub_srv) {
+ /* DMUB isn't supported on the ASIC. */
+ return;
+ }
+
+ status = dmub_srv_is_hw_init(dmub_srv, &init);
+ if (status != DMUB_STATUS_OK)
+ drm_warn(adev_to_drm(adev), "DMUB hardware init check failed: %d\n", status);
+
+ if (status == DMUB_STATUS_OK && init) {
+ /* Wait for firmware load to finish. */
+ status = dmub_srv_wait_for_auto_load(dmub_srv, 100000);
+ if (status != DMUB_STATUS_OK)
+ drm_warn(adev_to_drm(adev), "Wait for DMUB auto-load failed: %d\n", status);
+ } else {
+ /* Perform the full hardware initialization. */
+ r = dm_dmub_hw_init(adev);
+ if (r)
+ drm_err(adev_to_drm(adev), "DMUB interface failed to initialize: status=%d\n", r);
+ }
+}
+
+static enum dmub_status
+dm_dmub_send_vbios_gpint_command(struct amdgpu_device *adev,
+ enum dmub_gpint_command command_code,
+ uint16_t param,
+ uint32_t timeout_us)
+{
+ union dmub_gpint_data_register reg, test;
+ uint32_t i;
+
+ /* Assume that VBIOS DMUB is ready to take commands */
+
+ reg.bits.status = 1;
+ reg.bits.command_code = command_code;
+ reg.bits.param = param;
+
+ cgs_write_register(adev->dm.cgs_device, 0x34c0 + 0x01f8, reg.all);
+
+ for (i = 0; i < timeout_us; ++i) {
+ udelay(1);
+
+ /* Check if our GPINT got acked */
+ reg.bits.status = 0;
+ test = (union dmub_gpint_data_register)
+ cgs_read_register(adev->dm.cgs_device, 0x34c0 + 0x01f8);
+
+ if (test.all == reg.all)
+ return DMUB_STATUS_OK;
+ }
+
+ return DMUB_STATUS_TIMEOUT;
+}
+
+static void *dm_dmub_get_vbios_bounding_box(struct amdgpu_device *adev)
+{
+ void *bb;
+ long long addr;
+ unsigned int bb_size;
+ int i = 0;
+ uint16_t chunk;
+ enum dmub_gpint_command send_addrs[] = {
+ DMUB_GPINT__SET_BB_ADDR_WORD0,
+ DMUB_GPINT__SET_BB_ADDR_WORD1,
+ DMUB_GPINT__SET_BB_ADDR_WORD2,
+ DMUB_GPINT__SET_BB_ADDR_WORD3,
+ };
+ enum dmub_status ret;
+
+ switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
+ case IP_VERSION(4, 0, 1):
+ bb_size = sizeof(struct dml2_soc_bb);
+ break;
+ case IP_VERSION(4, 2, 0):
+ case IP_VERSION(4, 2, 1):
+ bb_size = sizeof(struct dml2_soc_bb);
+ break;
+ default:
+ return NULL;
+ }
+
+ bb = dm_allocate_gpu_mem(adev,
+ DC_MEM_ALLOC_TYPE_GART,
+ bb_size,
+ &addr);
+ if (!bb)
+ return NULL;
+
+ for (i = 0; i < 4; i++) {
+ /* Extract 16-bit chunk */
+ chunk = ((uint64_t) addr >> (i * 16)) & 0xFFFF;
+ /* Send the chunk */
+ ret = dm_dmub_send_vbios_gpint_command(adev, send_addrs[i], chunk, 30000);
+ if (ret != DMUB_STATUS_OK)
+ goto free_bb;
+ }
+
+ /* Now ask DMUB to copy the bb */
+ ret = dm_dmub_send_vbios_gpint_command(adev, DMUB_GPINT__BB_COPY, 1, 200000);
+ if (ret != DMUB_STATUS_OK)
+ goto free_bb;
+
+ return bb;
+
+free_bb:
+ dm_free_gpu_mem(adev, DC_MEM_ALLOC_TYPE_GART, (void *) bb);
+ return NULL;
+
+}
+
+enum dmub_ips_disable_type dm_get_default_ips_mode(
+ struct amdgpu_device *adev)
+{
+ enum dmub_ips_disable_type ret = DMUB_IPS_ENABLE;
+
+ switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
+ case IP_VERSION(3, 5, 0):
+ case IP_VERSION(3, 6, 0):
+ case IP_VERSION(3, 5, 1):
+ ret = DMUB_IPS_RCG_IN_ACTIVE_IPS2_IN_OFF;
+ break;
+ case IP_VERSION(4, 2, 0):
+ case IP_VERSION(4, 2, 1):
+ ret = DMUB_IPS_ENABLE;
+ break;
+ default:
+ /* ASICs older than DCN35 do not have IPSs */
+ if (amdgpu_ip_version(adev, DCE_HWIP, 0) < IP_VERSION(3, 5, 0))
+ ret = DMUB_IPS_DISABLE_ALL;
+ break;
+ }
+
+ return ret;
+}
+
+static uint32_t amdgpu_dm_dmub_reg_read(void *ctx, uint32_t address)
+{
+ struct amdgpu_device *adev = ctx;
+
+ return dm_read_reg(adev->dm.dc->ctx, address);
+}
+
+static void amdgpu_dm_dmub_reg_write(void *ctx, uint32_t address,
+ uint32_t value)
+{
+ struct amdgpu_device *adev = ctx;
+
+ return dm_write_reg(adev->dm.dc->ctx, address, value);
+}
+
+int dm_dmub_sw_init(struct amdgpu_device *adev)
+{
+ struct dmub_srv_create_params create_params;
+ struct dmub_srv_fw_meta_info_params fw_meta_info_params;
+ struct dmub_srv_region_params region_params;
+ struct dmub_srv_region_info region_info;
+ struct dmub_srv_memory_params memory_params;
+ struct dmub_fw_meta_info fw_info;
+ struct dmub_srv_fb_info *fb_info;
+ struct dmub_srv *dmub_srv;
+ const struct dmcub_firmware_header_v1_0 *hdr;
+ enum dmub_asic dmub_asic;
+ enum dmub_status status;
+ static enum dmub_window_memory_type window_memory_type[DMUB_WINDOW_TOTAL] = {
+ DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_0_INST_CONST */
+ DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_1_STACK */
+ DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_2_BSS_DATA */
+ DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_3_VBIOS */
+ DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_4_MAILBOX */
+ DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_5_TRACEBUFF */
+ DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_6_FW_STATE */
+ DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_7_SCRATCH_MEM */
+ DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_IB_MEM */
+ DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_SHARED_STATE */
+ DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_LSDMA_BUFFER */
+ DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_CURSOR_OFFLOAD */
+ };
+ int r;
+
+ switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
+ case IP_VERSION(2, 1, 0):
+ dmub_asic = DMUB_ASIC_DCN21;
+ break;
+ case IP_VERSION(3, 0, 0):
+ dmub_asic = DMUB_ASIC_DCN30;
+ break;
+ case IP_VERSION(3, 0, 1):
+ dmub_asic = DMUB_ASIC_DCN301;
+ break;
+ case IP_VERSION(3, 0, 2):
+ dmub_asic = DMUB_ASIC_DCN302;
+ break;
+ case IP_VERSION(3, 0, 3):
+ dmub_asic = DMUB_ASIC_DCN303;
+ break;
+ case IP_VERSION(3, 1, 2):
+ case IP_VERSION(3, 1, 3):
+ dmub_asic = (adev->external_rev_id == YELLOW_CARP_B0) ? DMUB_ASIC_DCN31B : DMUB_ASIC_DCN31;
+ break;
+ case IP_VERSION(3, 1, 4):
+ dmub_asic = DMUB_ASIC_DCN314;
+ break;
+ case IP_VERSION(3, 1, 5):
+ dmub_asic = DMUB_ASIC_DCN315;
+ break;
+ case IP_VERSION(3, 1, 6):
+ dmub_asic = DMUB_ASIC_DCN316;
+ break;
+ case IP_VERSION(3, 2, 0):
+ dmub_asic = DMUB_ASIC_DCN32;
+ break;
+ case IP_VERSION(3, 2, 1):
+ dmub_asic = DMUB_ASIC_DCN321;
+ break;
+ case IP_VERSION(3, 5, 0):
+ case IP_VERSION(3, 5, 1):
+ dmub_asic = DMUB_ASIC_DCN35;
+ break;
+ case IP_VERSION(3, 6, 0):
+ dmub_asic = DMUB_ASIC_DCN36;
+ break;
+ case IP_VERSION(4, 0, 1):
+ dmub_asic = DMUB_ASIC_DCN401;
+ break;
+ case IP_VERSION(4, 2, 0):
+ dmub_asic = DMUB_ASIC_DCN42;
+ break;
+ case IP_VERSION(4, 2, 1):
+ dmub_asic = DMUB_ASIC_DCN42B;
+ break;
+ default:
+ /* ASIC doesn't support DMUB. */
+ return 0;
+ }
+
+ hdr = (const struct dmcub_firmware_header_v1_0 *)adev->dm.dmub_fw->data;
+ adev->dm.dmcub_fw_version = le32_to_cpu(hdr->header.ucode_version);
+
+ if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) {
+ adev->firmware.ucode[AMDGPU_UCODE_ID_DMCUB].ucode_id =
+ AMDGPU_UCODE_ID_DMCUB;
+ adev->firmware.ucode[AMDGPU_UCODE_ID_DMCUB].fw =
+ adev->dm.dmub_fw;
+ adev->firmware.fw_size +=
+ ALIGN(le32_to_cpu(hdr->inst_const_bytes), PAGE_SIZE);
+
+ drm_info(adev_to_drm(adev), "Loading DMUB firmware via PSP: version=0x%08X\n",
+ adev->dm.dmcub_fw_version);
+ }
+
+
+ adev->dm.dmub_srv = kzalloc_obj(*adev->dm.dmub_srv);
+ dmub_srv = adev->dm.dmub_srv;
+
+ if (!dmub_srv) {
+ drm_err(adev_to_drm(adev), "Failed to allocate DMUB service!\n");
+ return -ENOMEM;
+ }
+
+ memset(&create_params, 0, sizeof(create_params));
+ create_params.user_ctx = adev;
+ create_params.funcs.reg_read = amdgpu_dm_dmub_reg_read;
+ create_params.funcs.reg_write = amdgpu_dm_dmub_reg_write;
+ create_params.asic = dmub_asic;
+
+ /* Create the DMUB service. */
+ status = dmub_srv_create(dmub_srv, &create_params);
+ if (status != DMUB_STATUS_OK) {
+ drm_err(adev_to_drm(adev), "Error creating DMUB service: %d\n", status);
+ return -EINVAL;
+ }
+
+ /* Extract the FW meta info. */
+ memset(&fw_meta_info_params, 0, sizeof(fw_meta_info_params));
+
+ fw_meta_info_params.inst_const_size = le32_to_cpu(hdr->inst_const_bytes) -
+ PSP_HEADER_BYTES_256;
+ fw_meta_info_params.bss_data_size = le32_to_cpu(hdr->bss_data_bytes);
+ fw_meta_info_params.fw_inst_const = adev->dm.dmub_fw->data +
+ le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
+ PSP_HEADER_BYTES_256;
+ fw_meta_info_params.fw_bss_data = fw_meta_info_params.bss_data_size ? adev->dm.dmub_fw->data +
+ le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
+ le32_to_cpu(hdr->inst_const_bytes) : NULL;
+ fw_meta_info_params.custom_psp_footer_size = 0;
+
+ status = dmub_srv_get_fw_meta_info_from_raw_fw(&fw_meta_info_params, &fw_info);
+ if (status != DMUB_STATUS_OK) {
+ /* Skip returning early, just log the error. */
+ drm_err(adev_to_drm(adev), "Error getting DMUB FW meta info: %d\n", status);
+ }
+
+ /* Calculate the size of all the regions for the DMUB service. */
+ memset(&region_params, 0, sizeof(region_params));
+
+ region_params.inst_const_size = fw_meta_info_params.inst_const_size;
+ region_params.bss_data_size = fw_meta_info_params.bss_data_size;
+ region_params.vbios_size = adev->bios_size;
+ region_params.fw_bss_data = fw_meta_info_params.fw_bss_data;
+ region_params.fw_inst_const = fw_meta_info_params.fw_inst_const;
+ region_params.window_memory_type = window_memory_type;
+ region_params.fw_info = (status == DMUB_STATUS_OK) ? &fw_info : NULL;
+
+ status = dmub_srv_calc_region_info(dmub_srv, &region_params,
+ &region_info);
+
+ if (status != DMUB_STATUS_OK) {
+ drm_err(adev_to_drm(adev), "Error calculating DMUB region info: %d\n", status);
+ return -EINVAL;
+ }
+
+ /*
+ * Allocate a framebuffer based on the total size of all the regions.
+ * TODO: Move this into GART.
+ */
+ r = amdgpu_bo_create_kernel(adev, region_info.fb_size, PAGE_SIZE,
+ AMDGPU_GEM_DOMAIN_VRAM |
+ AMDGPU_GEM_DOMAIN_GTT,
+ &adev->dm.dmub_bo,
+ &adev->dm.dmub_bo_gpu_addr,
+ &adev->dm.dmub_bo_cpu_addr);
+ if (r)
+ return r;
+
+ /* Rebase the regions on the framebuffer address. */
+ memset(&memory_params, 0, sizeof(memory_params));
+ memory_params.cpu_fb_addr = adev->dm.dmub_bo_cpu_addr;
+ memory_params.gpu_fb_addr = adev->dm.dmub_bo_gpu_addr;
+ memory_params.region_info = &region_info;
+ memory_params.window_memory_type = window_memory_type;
+
+ adev->dm.dmub_fb_info = kzalloc_obj(*adev->dm.dmub_fb_info);
+ fb_info = adev->dm.dmub_fb_info;
+
+ if (!fb_info) {
+ drm_err(adev_to_drm(adev),
+ "Failed to allocate framebuffer info for DMUB service!\n");
+ return -ENOMEM;
+ }
+
+ status = dmub_srv_calc_mem_info(dmub_srv, &memory_params, fb_info);
+ if (status != DMUB_STATUS_OK) {
+ drm_err(adev_to_drm(adev), "Error calculating DMUB FB info: %d\n", status);
+ return -EINVAL;
+ }
+
+ adev->dm.bb_from_dmub = dm_dmub_get_vbios_bounding_box(adev);
+ adev->dm.fw_inst_size = fw_meta_info_params.inst_const_size;
+
+ return 0;
+}
+
+int dm_init_microcode(struct amdgpu_device *adev)
+{
+ char *fw_name_dmub;
+ int r;
+
+ switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
+ case IP_VERSION(2, 1, 0):
+ fw_name_dmub = FIRMWARE_RENOIR_DMUB;
+ if (ASICREV_IS_GREEN_SARDINE(adev->external_rev_id))
+ fw_name_dmub = FIRMWARE_GREEN_SARDINE_DMUB;
+ break;
+ case IP_VERSION(3, 0, 0):
+ if (amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(10, 3, 0))
+ fw_name_dmub = FIRMWARE_SIENNA_CICHLID_DMUB;
+ else
+ fw_name_dmub = FIRMWARE_NAVY_FLOUNDER_DMUB;
+ break;
+ case IP_VERSION(3, 0, 1):
+ fw_name_dmub = FIRMWARE_VANGOGH_DMUB;
+ break;
+ case IP_VERSION(3, 0, 2):
+ fw_name_dmub = FIRMWARE_DIMGREY_CAVEFISH_DMUB;
+ break;
+ case IP_VERSION(3, 0, 3):
+ fw_name_dmub = FIRMWARE_BEIGE_GOBY_DMUB;
+ break;
+ case IP_VERSION(3, 1, 2):
+ case IP_VERSION(3, 1, 3):
+ fw_name_dmub = FIRMWARE_YELLOW_CARP_DMUB;
+ break;
+ case IP_VERSION(3, 1, 4):
+ fw_name_dmub = FIRMWARE_DCN_314_DMUB;
+ break;
+ case IP_VERSION(3, 1, 5):
+ fw_name_dmub = FIRMWARE_DCN_315_DMUB;
+ break;
+ case IP_VERSION(3, 1, 6):
+ fw_name_dmub = FIRMWARE_DCN316_DMUB;
+ break;
+ case IP_VERSION(3, 2, 0):
+ fw_name_dmub = FIRMWARE_DCN_V3_2_0_DMCUB;
+ break;
+ case IP_VERSION(3, 2, 1):
+ fw_name_dmub = FIRMWARE_DCN_V3_2_1_DMCUB;
+ break;
+ case IP_VERSION(3, 5, 0):
+ fw_name_dmub = FIRMWARE_DCN_35_DMUB;
+ break;
+ case IP_VERSION(3, 5, 1):
+ fw_name_dmub = FIRMWARE_DCN_351_DMUB;
+ break;
+ case IP_VERSION(3, 6, 0):
+ fw_name_dmub = FIRMWARE_DCN_36_DMUB;
+ break;
+ case IP_VERSION(4, 0, 1):
+ fw_name_dmub = FIRMWARE_DCN_401_DMUB;
+ break;
+ case IP_VERSION(4, 2, 0):
+ fw_name_dmub = FIRMWARE_DCN_42_DMUB;
+ break;
+ case IP_VERSION(4, 2, 1):
+ fw_name_dmub = FIRMWARE_DCN_42B_DMUB;
+ break;
+ default:
+ /* ASIC doesn't support DMUB. */
+ return 0;
+ }
+ r = amdgpu_ucode_request(adev, &adev->dm.dmub_fw, AMDGPU_UCODE_REQUIRED,
+ "%s", fw_name_dmub);
+ return r;
+}
+
+int amdgpu_dm_process_dmub_aux_transfer_sync(
+ struct dc_context *ctx,
+ unsigned int link_index,
+ struct aux_payload *payload,
+ enum aux_return_code_type *operation_result)
+{
+ struct amdgpu_device *adev = ctx->driver_context;
+ struct dmub_notification *p_notify = adev->dm.dmub_notify;
+ int ret = -1;
+
+ mutex_lock(&adev->dm.dpia_aux_lock);
+ if (!dc_process_dmub_aux_transfer_async(ctx->dc, link_index, payload)) {
+ *operation_result = AUX_RET_ERROR_ENGINE_ACQUIRE;
+ goto out;
+ }
+
+ if (!wait_for_completion_timeout(&adev->dm.dmub_aux_transfer_done, 10 * HZ)) {
+ drm_err(adev_to_drm(adev), "wait_for_completion_timeout timeout!");
+ *operation_result = AUX_RET_ERROR_TIMEOUT;
+ goto out;
+ }
+
+ if (p_notify->result != AUX_RET_SUCCESS) {
+ /*
+ * Transient states before tunneling is enabled could
+ * lead to this error. We can ignore this for now.
+ */
+ if (p_notify->result == AUX_RET_ERROR_PROTOCOL_ERROR) {
+ drm_warn(adev_to_drm(adev), "DPIA AUX failed on 0x%x(%d), error %d\n",
+ payload->address, payload->length,
+ p_notify->result);
+ }
+ *operation_result = p_notify->result;
+ goto out;
+ }
+
+ payload->reply[0] = adev->dm.dmub_notify->aux_reply.command & 0xF;
+ if (adev->dm.dmub_notify->aux_reply.command & 0xF0)
+ /* The reply is stored in the top nibble of the command. */
+ payload->reply[0] = (adev->dm.dmub_notify->aux_reply.command >> 4) & 0xF;
+
+ /*write req may receive a byte indicating partially written number as well*/
+ if (p_notify->aux_reply.length)
+ memcpy(payload->data, p_notify->aux_reply.data,
+ p_notify->aux_reply.length);
+
+ /* success */
+ ret = p_notify->aux_reply.length;
+ *operation_result = p_notify->result;
+out:
+ reinit_completion(&adev->dm.dmub_aux_transfer_done);
+ mutex_unlock(&adev->dm.dpia_aux_lock);
+ return ret;
+}
+
+static void abort_fused_io(
+ struct dc_context *ctx,
+ const struct dmub_cmd_fused_request *request
+)
+{
+ union dmub_rb_cmd command = { 0 };
+ struct dmub_rb_cmd_fused_io *io = &command.fused_io;
+
+ io->header.type = DMUB_CMD__FUSED_IO;
+ io->header.sub_type = DMUB_CMD__FUSED_IO_ABORT;
+ io->header.payload_bytes = sizeof(*io) - sizeof(io->header);
+ io->request = *request;
+ dm_execute_dmub_cmd(ctx, &command, DM_DMUB_WAIT_TYPE_NO_WAIT);
+}
+
+static bool execute_fused_io(
+ struct amdgpu_device *dev,
+ struct dc_context *ctx,
+ union dmub_rb_cmd *commands,
+ uint8_t count,
+ uint32_t timeout_us
+)
+{
+ const uint8_t ddc_line = commands[0].fused_io.request.u.aux.ddc_line;
+
+ if (ddc_line >= ARRAY_SIZE(dev->dm.fused_io))
+ return false;
+
+ struct fused_io_sync *sync = &dev->dm.fused_io[ddc_line];
+ struct dmub_rb_cmd_fused_io *first = &commands[0].fused_io;
+ const bool result = dm_execute_dmub_cmd_list(ctx, count, commands, DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY)
+ && first->header.ret_status
+ && first->request.status == FUSED_REQUEST_STATUS_SUCCESS;
+
+ if (!result)
+ return false;
+
+ while (wait_for_completion_timeout(&sync->replied, usecs_to_jiffies(timeout_us))) {
+ reinit_completion(&sync->replied);
+
+ struct dmub_cmd_fused_request *reply = (struct dmub_cmd_fused_request *) sync->reply_data;
+
+ static_assert(sizeof(*reply) <= sizeof(sync->reply_data), "Size mismatch");
+
+ if (reply->identifier == first->request.identifier) {
+ first->request = *reply;
+ return true;
+ }
+ }
+
+ reinit_completion(&sync->replied);
+ first->request.status = FUSED_REQUEST_STATUS_TIMEOUT;
+ abort_fused_io(ctx, &first->request);
+ return false;
+}
+
+bool amdgpu_dm_execute_fused_io(
+ struct amdgpu_device *dev,
+ struct dc_link *link,
+ union dmub_rb_cmd *commands,
+ uint8_t count,
+ uint32_t timeout_us)
+{
+ struct amdgpu_display_manager *dm = &dev->dm;
+
+ mutex_lock(&dm->dpia_aux_lock);
+
+ const bool result = execute_fused_io(dev, link->ctx, commands, count, timeout_us);
+
+ mutex_unlock(&dm->dpia_aux_lock);
+ return result;
+}
+
+int amdgpu_dm_process_dmub_set_config_sync(
+ struct dc_context *ctx,
+ unsigned int link_index,
+ struct set_config_cmd_payload *payload,
+ enum set_config_status *operation_result)
+{
+ struct amdgpu_device *adev = ctx->driver_context;
+ bool is_cmd_complete;
+ int ret;
+
+ mutex_lock(&adev->dm.dpia_aux_lock);
+ is_cmd_complete = dc_process_dmub_set_config_async(ctx->dc,
+ link_index, payload, adev->dm.dmub_notify);
+
+ if (is_cmd_complete || wait_for_completion_timeout(&adev->dm.dmub_aux_transfer_done, 10 * HZ)) {
+ ret = 0;
+ *operation_result = adev->dm.dmub_notify->sc_status;
+ } else {
+ drm_err(adev_to_drm(adev), "wait_for_completion_timeout timeout!");
+ ret = -1;
+ *operation_result = SET_CONFIG_UNKNOWN_ERROR;
+ }
+
+ if (!is_cmd_complete)
+ reinit_completion(&adev->dm.dmub_aux_transfer_done);
+ mutex_unlock(&adev->dm.dpia_aux_lock);
+ return ret;
+}
+
+bool dm_execute_dmub_cmd(const struct dc_context *ctx, union dmub_rb_cmd *cmd, enum dm_dmub_wait_type wait_type)
+{
+ struct amdgpu_device *adev = ctx->driver_context;
+
+ guard(spinlock_irqsave)(&adev->dm.dmub_lock);
+ return dc_dmub_srv_cmd_run(ctx->dmub_srv, cmd, wait_type);
+}
+
+bool dm_execute_dmub_cmd_list(const struct dc_context *ctx, unsigned int count, union dmub_rb_cmd *cmd, enum dm_dmub_wait_type wait_type)
+{
+ struct amdgpu_device *adev = ctx->driver_context;
+
+ guard(spinlock_irqsave)(&adev->dm.dmub_lock);
+ return dc_dmub_srv_cmd_run_list(ctx->dmub_srv, count, cmd, wait_type);
+}
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
new file mode 100644
index 000000000000..a4a03e40ec37
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: AMD
+ *
+ */
+
+#ifndef AMDGPU_DM_AMDGPU_DM_DMUB_H_
+#define AMDGPU_DM_AMDGPU_DM_DMUB_H_
+
+#include "amdgpu.h"
+
+void dm_dmub_aux_setconfig_callback(struct amdgpu_device *adev,
+ struct dmub_notification *notify);
+void dm_dmub_aux_fused_io_callback(struct amdgpu_device *adev,
+ struct dmub_notification *notify);
+bool dm_register_dmub_notify_callback(struct amdgpu_device *adev,
+ enum dmub_notification_type type,
+ dmub_notify_interrupt_callback_t callback,
+ bool dmub_int_thread_offload);
+int dm_dmub_hw_init(struct amdgpu_device *adev);
+void dm_dmub_hw_resume(struct amdgpu_device *adev);
+enum dmub_ips_disable_type dm_get_default_ips_mode(struct amdgpu_device *adev);
+int dm_dmub_sw_init(struct amdgpu_device *adev);
+int dm_init_microcode(struct amdgpu_device *adev);
+
+#define FIRMWARE_RENOIR_DMUB "amdgpu/renoir_dmcub.bin"
+#define FIRMWARE_SIENNA_CICHLID_DMUB "amdgpu/sienna_cichlid_dmcub.bin"
+#define FIRMWARE_NAVY_FLOUNDER_DMUB "amdgpu/navy_flounder_dmcub.bin"
+#define FIRMWARE_GREEN_SARDINE_DMUB "amdgpu/green_sardine_dmcub.bin"
+#define FIRMWARE_VANGOGH_DMUB "amdgpu/vangogh_dmcub.bin"
+#define FIRMWARE_DIMGREY_CAVEFISH_DMUB "amdgpu/dimgrey_cavefish_dmcub.bin"
+#define FIRMWARE_BEIGE_GOBY_DMUB "amdgpu/beige_goby_dmcub.bin"
+#define FIRMWARE_YELLOW_CARP_DMUB "amdgpu/yellow_carp_dmcub.bin"
+#define FIRMWARE_DCN_314_DMUB "amdgpu/dcn_3_1_4_dmcub.bin"
+#define FIRMWARE_DCN_315_DMUB "amdgpu/dcn_3_1_5_dmcub.bin"
+#define FIRMWARE_DCN316_DMUB "amdgpu/dcn_3_1_6_dmcub.bin"
+#define FIRMWARE_DCN_V3_2_0_DMCUB "amdgpu/dcn_3_2_0_dmcub.bin"
+#define FIRMWARE_DCN_V3_2_1_DMCUB "amdgpu/dcn_3_2_1_dmcub.bin"
+#define FIRMWARE_DCN_35_DMUB "amdgpu/dcn_3_5_dmcub.bin"
+#define FIRMWARE_DCN_351_DMUB "amdgpu/dcn_3_5_1_dmcub.bin"
+#define FIRMWARE_DCN_36_DMUB "amdgpu/dcn_3_6_dmcub.bin"
+#define FIRMWARE_DCN_401_DMUB "amdgpu/dcn_4_0_1_dmcub.bin"
+#define FIRMWARE_DCN_42_DMUB "amdgpu/dcn_4_2_dmcub.bin"
+#define FIRMWARE_DCN_42B_DMUB "amdgpu/dcn_4_2_1_dmcub.bin"
+#define FIRMWARE_RAVEN_DMCU "amdgpu/raven_dmcu.bin"
+#define FIRMWARE_NAVI12_DMCU "amdgpu/navi12_dmcu.bin"
+
+#endif /* AMDGPU_DM_AMDGPU_DM_DMUB_H_ */