Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992// 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);
#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)static const struct amdgpu_dm_dmub_kunit_ops amdgpu_dm_dmub_default_ops = { .bo_create_kernel = amdgpu_bo_create_kernel, .ucode_request = amdgpu_ucode_request,};
static const struct amdgpu_dm_dmub_kunit_ops *amdgpu_dm_dmub_ops = &amdgpu_dm_dmub_default_ops;
void amdgpu_dm_dmub_kunit_set_ops(const struct amdgpu_dm_dmub_kunit_ops *ops){ amdgpu_dm_dmub_ops = ops ? ops : &amdgpu_dm_dmub_default_ops;}EXPORT_IF_KUNIT(amdgpu_dm_dmub_kunit_set_ops);
#define dmub_bo_create_kernel amdgpu_dm_dmub_ops->bo_create_kernel#define dmub_ucode_request amdgpu_dm_dmub_ops->ucode_request
#else
#define dmub_bo_create_kernel amdgpu_bo_create_kernel#define dmub_ucode_request amdgpu_ucode_request
#endif
/** * 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_IFN_KUNIT 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);}EXPORT_IF_KUNIT(amdgpu_dm_dmub_reg_read);
STATIC_IFN_KUNIT 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);}EXPORT_IF_KUNIT(amdgpu_dm_dmub_reg_write);
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; int mem_domain = AMDGPU_GEM_DOMAIN_GTT;
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; case IP_VERSION(6, 0, 0): dmub_asic = DMUB_ASIC_DCN60; break; default: /* ASIC doesn't support DMUB. */ return 0; }
hdr = (const struct dmcub_firmware_header_v1_0 *)adev->dm.dmub_fw->data; adev->dm.dmcub_fw_version = le32_to_cpu(hdr->header.ucode_version);
if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) { adev->firmware.ucode[AMDGPU_UCODE_ID_DMCUB].ucode_id = AMDGPU_UCODE_ID_DMCUB; adev->firmware.ucode[AMDGPU_UCODE_ID_DMCUB].fw = adev->dm.dmub_fw; adev->firmware.fw_size += ALIGN(le32_to_cpu(hdr->inst_const_bytes), PAGE_SIZE);
drm_info(adev_to_drm(adev), "Loading DMUB firmware via PSP: version=0x%08X\n", adev->dm.dmcub_fw_version); }
adev->dm.dmub_srv = kzalloc_obj(*adev->dm.dmub_srv); dmub_srv = adev->dm.dmub_srv;
if (!dmub_srv) { drm_err(adev_to_drm(adev), "Failed to allocate DMUB service!\n"); return -ENOMEM; }
memset(&create_params, 0, sizeof(create_params)); create_params.user_ctx = adev; create_params.funcs.reg_read = amdgpu_dm_dmub_reg_read; create_params.funcs.reg_write = amdgpu_dm_dmub_reg_write; create_params.asic = dmub_asic;
/* Create the DMUB service. */ status = dmub_srv_create(dmub_srv, &create_params); if (status != DMUB_STATUS_OK) { drm_err(adev_to_drm(adev), "Error creating DMUB service: %d\n", status); return -EINVAL; }
/* Extract the FW meta info. */ memset(&fw_meta_info_params, 0, sizeof(fw_meta_info_params));
fw_meta_info_params.inst_const_size = le32_to_cpu(hdr->inst_const_bytes) - PSP_HEADER_BYTES_256; fw_meta_info_params.bss_data_size = le32_to_cpu(hdr->bss_data_bytes); fw_meta_info_params.fw_inst_const = adev->dm.dmub_fw->data + le32_to_cpu(hdr->header.ucode_array_offset_bytes) + PSP_HEADER_BYTES_256; fw_meta_info_params.fw_bss_data = fw_meta_info_params.bss_data_size ? adev->dm.dmub_fw->data + le32_to_cpu(hdr->header.ucode_array_offset_bytes) + le32_to_cpu(hdr->inst_const_bytes) : NULL; fw_meta_info_params.custom_psp_footer_size = 0;
status = dmub_srv_get_fw_meta_info_from_raw_fw(&fw_meta_info_params, &fw_info); if (status != DMUB_STATUS_OK) { /* Skip returning early, just log the error. */ drm_err(adev_to_drm(adev), "Error getting DMUB FW meta info: %d\n", status); }
/* Calculate the size of all the regions for the DMUB service. */ memset(®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; }
/* Limit to allocate dmub to GTT on DCN32/1 * TODO: Other asics with GDDR7 may have worse latency */ if (dmub_asic != DMUB_ASIC_DCN32 && dmub_asic != DMUB_ASIC_DCN321) mem_domain |= AMDGPU_GEM_DOMAIN_VRAM;
/* * Allocate a framebuffer based on the total size of all the regions. * TODO: Move this into GART. */ r = dmub_bo_create_kernel(adev, region_info.fb_size, PAGE_SIZE, mem_domain, &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_obj(*adev->dm.dmub_fb_info); fb_info = adev->dm.dmub_fb_info;
if (!fb_info) { drm_err(adev_to_drm(adev), "Failed to allocate framebuffer info for DMUB service!\n"); return -ENOMEM; }
status = dmub_srv_calc_mem_info(dmub_srv, &memory_params, fb_info); if (status != DMUB_STATUS_OK) { drm_err(adev_to_drm(adev), "Error calculating DMUB FB info: %d\n", status); return -EINVAL; }
adev->dm.bb_from_dmub = dm_dmub_get_vbios_bounding_box(adev); adev->dm.fw_inst_size = fw_meta_info_params.inst_const_size;
return 0;}EXPORT_IF_KUNIT(dm_dmub_sw_init);
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; case IP_VERSION(6, 0, 0): fw_name_dmub = FIRMWARE_DCN_60_DMUB; break; default: /* ASIC doesn't support DMUB. */ return 0; } r = dmub_ucode_request(adev, &adev->dm.dmub_fw, AMDGPU_UCODE_REQUIRED, "%s", fw_name_dmub); return r;}EXPORT_IF_KUNIT(dm_init_microcode);
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 && payload->data) { /* Bound the reply to the scratch buffer it was read into. */ ret = min_t(uint32_t, p_notify->aux_reply.length, sizeof(p_notify->aux_reply.data));
/* * During a write-status-update retry the caller zeroes * payload->length while still expecting the partial-write * status byte in payload->data (see dce_aux_transfer_with_retries), * so only clamp to payload->length for regular transfers. */ if (!payload->write_status_update) ret = min_t(int, ret, payload->length);
memcpy(payload->data, p_notify->aux_reply.data, ret); } else { /* 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;}EXPORT_IF_KUNIT(amdgpu_dm_process_dmub_aux_transfer_sync);
STATIC_IFN_KUNIT 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);}EXPORT_IF_KUNIT(abort_fused_io);
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;}EXPORT_IF_KUNIT(amdgpu_dm_process_dmub_set_config_sync);
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);}EXPORT_IF_KUNIT(dm_execute_dmub_cmd);
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);}