// 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 "dm_helpers.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);MODULE_FIRMWARE(FIRMWARE_DCN_60_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);}EXPORT_IF_KUNIT(dm_dmub_aux_setconfig_callback); 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 = ¬ify->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);}EXPORT_IF_KUNIT(dm_dmub_aux_fused_io_callback); /** * 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 || type >= ARRAY_SIZE(adev->dm.dmub_thread_offload)) return false; adev->dm.dmub_callback[type] = callback; adev->dm.dmub_thread_offload[type] = dmub_int_thread_offload; return true;}EXPORT_IF_KUNIT(dm_register_dmub_notify_callback); 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 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; hw_params.fb_info = fb_info; /* 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;}EXPORT_IF_KUNIT(dm_dmub_hw_init); 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); }}EXPORT_IF_KUNIT(dm_dmub_hw_resume); static enum dmub_statusdm_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_IFN_KUNIT 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; case IP_VERSION(6, 0, 0): bb_size = sizeof(struct dmub_soc_bb_params); 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; }EXPORT_IF_KUNIT(dm_dmub_get_vbios_bounding_box); 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; 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;}EXPORT_IF_KUNIT(dm_get_default_ips_mode); 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 */Lines 1–500 of 964. Load more
// 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 "dm_helpers.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); MODULE_FIRMWARE(FIRMWARE_DCN_60_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); } EXPORT_IF_KUNIT(dm_dmub_aux_setconfig_callback); 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 = ¬ify->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); } EXPORT_IF_KUNIT(dm_dmub_aux_fused_io_callback); /** * 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 || type >= ARRAY_SIZE(adev->dm.dmub_thread_offload)) return false; adev->dm.dmub_callback[type] = callback; adev->dm.dmub_thread_offload[type] = dmub_int_thread_offload; return true; } EXPORT_IF_KUNIT(dm_register_dmub_notify_callback); 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 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; hw_params.fb_info = fb_info; /* 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; } EXPORT_IF_KUNIT(dm_dmub_hw_init); 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); } } EXPORT_IF_KUNIT(dm_dmub_hw_resume); 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_IFN_KUNIT 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; case IP_VERSION(6, 0, 0): bb_size = sizeof(struct dmub_soc_bb_params); 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; } EXPORT_IF_KUNIT(dm_dmub_get_vbios_bounding_box); 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; 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; } EXPORT_IF_KUNIT(dm_get_default_ips_mode); 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 */