From: Alex Hung <[email protected]> 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 <[email protected]> Signed-off-by: Alex Hung <[email protected]> Signed-off-by: Chenyu Chen <[email protected]> --- .../gpu/drm/amd/display/amdgpu_dm/Makefile | 3 +- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 928 +----------------- .../amd/display/amdgpu_dm/amdgpu_dm_dmub.c | 925 +++++++++++++++++ .../amd/display/amdgpu_dm/amdgpu_dm_dmub.h | 68 ++ 4 files changed, 1003 insertions(+), 921 deletions(-) create mode 100644 drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c create mode 100644 drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.h 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 a07820ba5759..7090e366f85f 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 * @@ -779,47 +729,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 = ¬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); -} - /** * dmub_hpd_callback - DMUB HPD interrupt processing callback. * @adev: amdgpu_device pointer @@ -909,32 +818,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; @@ -1107,224 +990,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; @@ -1632,119 +1297,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]; @@ -2140,8 +1692,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; } @@ -2149,8 +1701,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; } @@ -2438,225 +1990,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(sizeof(*adev->dm.dmub_srv), GFP_KERNEL); - 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(®ion_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, ®ion_params, - ®ion_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 = ®ion_info; - memory_params.window_memory_type = window_memory_type; - - adev->dm.dmub_fb_info = - kzalloc(sizeof(*adev->dm.dmub_fb_info), GFP_KERNEL); - 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; @@ -4381,19 +3714,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; @@ -5401,78 +4734,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; @@ -13005,179 +12266,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..54b3b884968b --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c @@ -0,0 +1,925 @@ +// 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 = ¬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); +} + +/** + * 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(sizeof(*adev->dm.dmub_srv), GFP_KERNEL); + 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(®ion_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, ®ion_params, + ®ion_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 = ®ion_info; + memory_params.window_memory_type = window_memory_type; + + adev->dm.dmub_fb_info = + kzalloc(sizeof(*adev->dm.dmub_fb_info), GFP_KERNEL); + 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_ */ -- 2.43.0
