/* * Copyright 2016-2024 Advanced Micro Devices, Inc. * All Rights Reserved. * * 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, sub license, 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 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 NON-INFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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. * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial portions * of the Software. * */ #include <linux/firmware.h>#include <linux/module.h>#include <linux/dmi.h>#include <linux/pci.h>#include <linux/debugfs.h>#include <drm/drm_drv.h> #include "amdgpu.h"#include "amdgpu_pm.h"#include "amdgpu_vcn.h"#include "amdgpu_reset.h"#include "soc15d.h" /* Firmware Names */#define FIRMWARE_RAVEN "amdgpu/raven_vcn.bin"#define FIRMWARE_PICASSO "amdgpu/picasso_vcn.bin"#define FIRMWARE_RAVEN2 "amdgpu/raven2_vcn.bin"#define FIRMWARE_ARCTURUS "amdgpu/arcturus_vcn.bin"#define FIRMWARE_RENOIR "amdgpu/renoir_vcn.bin"#define FIRMWARE_GREEN_SARDINE "amdgpu/green_sardine_vcn.bin"#define FIRMWARE_NAVI10 "amdgpu/navi10_vcn.bin"#define FIRMWARE_NAVI14 "amdgpu/navi14_vcn.bin"#define FIRMWARE_NAVI12 "amdgpu/navi12_vcn.bin"#define FIRMWARE_SIENNA_CICHLID "amdgpu/sienna_cichlid_vcn.bin"#define FIRMWARE_NAVY_FLOUNDER "amdgpu/navy_flounder_vcn.bin"#define FIRMWARE_VANGOGH "amdgpu/vangogh_vcn.bin"#define FIRMWARE_DIMGREY_CAVEFISH "amdgpu/dimgrey_cavefish_vcn.bin"#define FIRMWARE_ALDEBARAN "amdgpu/aldebaran_vcn.bin"#define FIRMWARE_BEIGE_GOBY "amdgpu/beige_goby_vcn.bin"#define FIRMWARE_YELLOW_CARP "amdgpu/yellow_carp_vcn.bin"#define FIRMWARE_VCN_3_1_2 "amdgpu/vcn_3_1_2.bin"#define FIRMWARE_VCN4_0_0 "amdgpu/vcn_4_0_0.bin"#define FIRMWARE_VCN4_0_2 "amdgpu/vcn_4_0_2.bin"#define FIRMWARE_VCN4_0_3 "amdgpu/vcn_4_0_3.bin"#define FIRMWARE_VCN4_0_4 "amdgpu/vcn_4_0_4.bin"#define FIRMWARE_VCN4_0_5 "amdgpu/vcn_4_0_5.bin"#define FIRMWARE_VCN4_0_6 "amdgpu/vcn_4_0_6.bin"#define FIRMWARE_VCN4_0_6_1 "amdgpu/vcn_4_0_6_1.bin"#define FIRMWARE_VCN5_0_0 "amdgpu/vcn_5_0_0.bin"#define FIRMWARE_VCN5_0_1 "amdgpu/vcn_5_0_1.bin"#define FIRMWARE_VCN5_0_2 "amdgpu/vcn_5_0_2.bin"#define FIRMWARE_VCN5_3_0 "amdgpu/vcn_5_3_0.bin" MODULE_FIRMWARE(FIRMWARE_RAVEN);MODULE_FIRMWARE(FIRMWARE_PICASSO);MODULE_FIRMWARE(FIRMWARE_RAVEN2);MODULE_FIRMWARE(FIRMWARE_ARCTURUS);MODULE_FIRMWARE(FIRMWARE_RENOIR);MODULE_FIRMWARE(FIRMWARE_GREEN_SARDINE);MODULE_FIRMWARE(FIRMWARE_ALDEBARAN);MODULE_FIRMWARE(FIRMWARE_NAVI10);MODULE_FIRMWARE(FIRMWARE_NAVI14);MODULE_FIRMWARE(FIRMWARE_NAVI12);MODULE_FIRMWARE(FIRMWARE_SIENNA_CICHLID);MODULE_FIRMWARE(FIRMWARE_NAVY_FLOUNDER);MODULE_FIRMWARE(FIRMWARE_VANGOGH);MODULE_FIRMWARE(FIRMWARE_DIMGREY_CAVEFISH);MODULE_FIRMWARE(FIRMWARE_BEIGE_GOBY);MODULE_FIRMWARE(FIRMWARE_YELLOW_CARP);MODULE_FIRMWARE(FIRMWARE_VCN_3_1_2);MODULE_FIRMWARE(FIRMWARE_VCN4_0_0);MODULE_FIRMWARE(FIRMWARE_VCN4_0_2);MODULE_FIRMWARE(FIRMWARE_VCN4_0_3);MODULE_FIRMWARE(FIRMWARE_VCN4_0_4);MODULE_FIRMWARE(FIRMWARE_VCN4_0_5);MODULE_FIRMWARE(FIRMWARE_VCN4_0_6);MODULE_FIRMWARE(FIRMWARE_VCN4_0_6_1);MODULE_FIRMWARE(FIRMWARE_VCN5_0_0);MODULE_FIRMWARE(FIRMWARE_VCN5_0_1);MODULE_FIRMWARE(FIRMWARE_VCN5_0_2);MODULE_FIRMWARE(FIRMWARE_VCN5_3_0); static void amdgpu_vcn_idle_work_handler(struct work_struct *work);static void amdgpu_vcn_reg_dump_fini(struct amdgpu_device *adev); int amdgpu_vcn_early_init(struct amdgpu_device *adev, int i){ char ucode_prefix[25]; int r; adev->vcn.inst[i].adev = adev; adev->vcn.inst[i].inst = i; amdgpu_ucode_ip_version_decode(adev, UVD_HWIP, ucode_prefix, sizeof(ucode_prefix)); if (i != 0 && adev->vcn.per_inst_fw) { r = amdgpu_ucode_request(adev, &adev->vcn.inst[i].fw, AMDGPU_UCODE_REQUIRED, "amdgpu/%s_%d.bin", ucode_prefix, i); if (r) amdgpu_ucode_release(&adev->vcn.inst[i].fw); } else { if (!adev->vcn.inst[0].fw) { r = amdgpu_ucode_request(adev, &adev->vcn.inst[0].fw, AMDGPU_UCODE_REQUIRED, "amdgpu/%s.bin", ucode_prefix); if (r) amdgpu_ucode_release(&adev->vcn.inst[0].fw); } else { r = 0; } adev->vcn.inst[i].fw = adev->vcn.inst[0].fw; } return r;} int amdgpu_vcn_sw_init(struct amdgpu_device *adev, int i){ unsigned long bo_size; const struct common_firmware_header *hdr; unsigned char fw_check; unsigned int fw_shared_size, log_offset; int r; mutex_init(&adev->vcn.inst[i].vcn1_jpeg1_workaround); mutex_init(&adev->vcn.inst[i].vcn_pg_lock); mutex_init(&adev->vcn.inst[i].engine_reset_mutex); atomic_set(&adev->vcn.inst[i].total_submission_cnt, 0); INIT_DELAYED_WORK(&adev->vcn.inst[i].idle_work, amdgpu_vcn_idle_work_handler); atomic_set(&adev->vcn.inst[i].dpg_enc_submission_cnt, 0); if ((adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) && (adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG)) adev->vcn.inst[i].indirect_sram = true; /* * Some Steam Deck's BIOS versions are incompatible with the * indirect SRAM mode, leading to amdgpu being unable to get * properly probed (and even potentially crashing the kernel). * Hence, check for these versions here - notice this is * restricted to Vangogh (Deck's APU). */ if (amdgpu_ip_version(adev, UVD_HWIP, 0) == IP_VERSION(3, 0, 2)) { const char *bios_ver = dmi_get_system_info(DMI_BIOS_VERSION); if (bios_ver && (!strncmp("F7A0113", bios_ver, 7) || !strncmp("F7A0114", bios_ver, 7))) { adev->vcn.inst[i].indirect_sram = false; dev_info(adev->dev, "Steam Deck quirk: indirect SRAM disabled on BIOS %s\n", bios_ver); } } /* from vcn4 and above, only unified queue is used */ adev->vcn.inst[i].using_unified_queue = amdgpu_ip_version(adev, UVD_HWIP, 0) >= IP_VERSION(4, 0, 0); hdr = (const struct common_firmware_header *)adev->vcn.inst[i].fw->data; adev->vcn.inst[i].fw_version = le32_to_cpu(hdr->ucode_version); adev->vcn.fw_version = le32_to_cpu(hdr->ucode_version); /* Bit 20-23, it is encode major and non-zero for new naming convention. * This field is part of version minor and DRM_DISABLED_FLAG in old naming * convention. Since the l:wq!atest version minor is 0x5B and DRM_DISABLED_FLAG * is zero in old naming convention, this field is always zero so far. * These four bits are used to tell which naming convention is present. */ fw_check = (le32_to_cpu(hdr->ucode_version) >> 20) & 0xf; if (fw_check) { unsigned int dec_ver, enc_major, enc_minor, vep, fw_rev; fw_rev = le32_to_cpu(hdr->ucode_version) & 0xfff; enc_minor = (le32_to_cpu(hdr->ucode_version) >> 12) & 0xff; enc_major = fw_check; dec_ver = (le32_to_cpu(hdr->ucode_version) >> 24) & 0xf; vep = (le32_to_cpu(hdr->ucode_version) >> 28) & 0xf; dev_info(adev->dev, "[VCN instance %d] Found VCN firmware Version ENC: %u.%u DEC: %u VEP: %u Revision: %u\n", i, enc_major, enc_minor, dec_ver, vep, fw_rev); } else { unsigned int version_major, version_minor, family_id; family_id = le32_to_cpu(hdr->ucode_version) & 0xff; version_major = (le32_to_cpu(hdr->ucode_version) >> 24) & 0xff; version_minor = (le32_to_cpu(hdr->ucode_version) >> 8) & 0xff; dev_info(adev->dev, "[VCN instance %d] Found VCN firmware Version: %u.%u Family ID: %u\n", i, version_major, version_minor, family_id); } bo_size = AMDGPU_VCN_STACK_SIZE + AMDGPU_VCN_CONTEXT_SIZE; if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP) bo_size += AMDGPU_GPU_PAGE_ALIGN(le32_to_cpu(hdr->ucode_size_bytes) + 8); if (amdgpu_ip_version(adev, UVD_HWIP, 0) >= IP_VERSION(5, 0, 0)) { fw_shared_size = AMDGPU_GPU_PAGE_ALIGN(sizeof(struct amdgpu_vcn5_fw_shared)); log_offset = offsetof(struct amdgpu_vcn5_fw_shared, fw_log); } else if (amdgpu_ip_version(adev, UVD_HWIP, 0) >= IP_VERSION(4, 0, 0)) { fw_shared_size = AMDGPU_GPU_PAGE_ALIGN(sizeof(struct amdgpu_vcn4_fw_shared)); log_offset = offsetof(struct amdgpu_vcn4_fw_shared, fw_log); } else { fw_shared_size = AMDGPU_GPU_PAGE_ALIGN(sizeof(struct amdgpu_fw_shared)); log_offset = offsetof(struct amdgpu_fw_shared, fw_log); } bo_size += fw_shared_size; if (amdgpu_vcnfw_log) bo_size += AMDGPU_VCNFW_LOG_SIZE; r = amdgpu_bo_create_kernel(adev, bo_size, PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM | AMDGPU_GEM_DOMAIN_GTT, &adev->vcn.inst[i].vcpu_bo, &adev->vcn.inst[i].gpu_addr, &adev->vcn.inst[i].cpu_addr); if (r) { dev_err(adev->dev, "(%d) failed to allocate vcn bo\n", r); return r; } adev->vcn.inst[i].fw_shared.cpu_addr = adev->vcn.inst[i].cpu_addr + bo_size - fw_shared_size; adev->vcn.inst[i].fw_shared.gpu_addr = adev->vcn.inst[i].gpu_addr + bo_size - fw_shared_size; adev->vcn.inst[i].fw_shared.mem_size = fw_shared_size; if (amdgpu_vcnfw_log) { adev->vcn.inst[i].fw_shared.cpu_addr -= AMDGPU_VCNFW_LOG_SIZE; adev->vcn.inst[i].fw_shared.gpu_addr -= AMDGPU_VCNFW_LOG_SIZE; adev->vcn.inst[i].fw_shared.log_offset = log_offset; } if (adev->vcn.inst[i].indirect_sram) { r = amdgpu_bo_create_kernel(adev, 64 * 2 * 4, PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM | AMDGPU_GEM_DOMAIN_GTT, &adev->vcn.inst[i].dpg_sram_bo, &adev->vcn.inst[i].dpg_sram_gpu_addr, &adev->vcn.inst[i].dpg_sram_cpu_addr); if (r) { dev_err(adev->dev, "VCN %d (%d) failed to allocate DPG bo\n", i, r); return r; } } return 0;} void amdgpu_vcn_sw_fini(struct amdgpu_device *adev, int i){ int j; if (adev->vcn.harvest_config & (1 << i)) return; amdgpu_bo_free_kernel( &adev->vcn.inst[i].dpg_sram_bo, &adev->vcn.inst[i].dpg_sram_gpu_addr, (void **)&adev->vcn.inst[i].dpg_sram_cpu_addr); kvfree(adev->vcn.inst[i].saved_bo); amdgpu_bo_free_kernel(&adev->vcn.inst[i].vcpu_bo, &adev->vcn.inst[i].gpu_addr, (void **)&adev->vcn.inst[i].cpu_addr); amdgpu_ring_fini(&adev->vcn.inst[i].ring_dec); for (j = 0; j < adev->vcn.inst[i].num_enc_rings; ++j) amdgpu_ring_fini(&adev->vcn.inst[i].ring_enc[j]); if (adev->vcn.per_inst_fw) { amdgpu_ucode_release(&adev->vcn.inst[i].fw); } else { amdgpu_ucode_release(&adev->vcn.inst[0].fw); adev->vcn.inst[i].fw = NULL; } if (adev->vcn.reg_list) amdgpu_vcn_reg_dump_fini(adev); mutex_destroy(&adev->vcn.inst[i].vcn_pg_lock); mutex_destroy(&adev->vcn.inst[i].vcn1_jpeg1_workaround);} bool amdgpu_vcn_is_disabled_vcn(struct amdgpu_device *adev, enum vcn_ring_type type, uint32_t vcn_instance){ bool ret = false; int vcn_config = adev->vcn.inst[vcn_instance].vcn_config; if ((type == VCN_ENCODE_RING) && (vcn_config & VCN_BLOCK_ENCODE_DISABLE_MASK)) ret = true; else if ((type == VCN_DECODE_RING) && (vcn_config & VCN_BLOCK_DECODE_DISABLE_MASK)) ret = true; else if ((type == VCN_UNIFIED_RING) && (vcn_config & VCN_BLOCK_QUEUE_DISABLE_MASK)) ret = true; return ret;} static int amdgpu_vcn_save_vcpu_bo_inst(struct amdgpu_device *adev, int i){ unsigned int size; void *ptr; int idx; if (adev->vcn.harvest_config & (1 << i)) return 0; if (adev->vcn.inst[i].vcpu_bo == NULL) return 0; size = amdgpu_bo_size(adev->vcn.inst[i].vcpu_bo); ptr = adev->vcn.inst[i].cpu_addr; adev->vcn.inst[i].saved_bo = kvmalloc(size, GFP_KERNEL); if (!adev->vcn.inst[i].saved_bo) return -ENOMEM; if (drm_dev_enter(adev_to_drm(adev), &idx)) { memcpy_fromio(adev->vcn.inst[i].saved_bo, ptr, size); drm_dev_exit(idx); } return 0;} int amdgpu_vcn_save_vcpu_bo(struct amdgpu_device *adev){ int ret, i; for (i = 0; i < adev->vcn.num_vcn_inst; ++i) { ret = amdgpu_vcn_save_vcpu_bo_inst(adev, i); if (ret) return ret; } return 0;} int amdgpu_vcn_suspend(struct amdgpu_device *adev, int i){ bool in_ras_intr = amdgpu_ras_intr_triggered(); if (adev->vcn.harvest_config & (1 << i)) return 0; /* err_event_athub and dpc recovery will corrupt VCPU buffer, so we need to * restore fw data and clear buffer in amdgpu_vcn_resume() */ if (in_ras_intr || amdgpu_reset_in_dpc(adev)) return 0; return amdgpu_vcn_save_vcpu_bo_inst(adev, i);} int amdgpu_vcn_resume(struct amdgpu_device *adev, int i){ unsigned int size; void *ptr; int idx; if (adev->vcn.harvest_config & (1 << i)) return 0; if (adev->vcn.inst[i].vcpu_bo == NULL) return -EINVAL; size = amdgpu_bo_size(adev->vcn.inst[i].vcpu_bo); ptr = adev->vcn.inst[i].cpu_addr; if (adev->vcn.inst[i].saved_bo != NULL) { if (drm_dev_enter(adev_to_drm(adev), &idx)) { memcpy_toio(ptr, adev->vcn.inst[i].saved_bo, size); drm_dev_exit(idx); } kvfree(adev->vcn.inst[i].saved_bo); adev->vcn.inst[i].saved_bo = NULL; } else { const struct common_firmware_header *hdr; unsigned int offset; hdr = (const struct common_firmware_header *)adev->vcn.inst[i].fw->data; if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP) { offset = le32_to_cpu(hdr->ucode_array_offset_bytes); if (drm_dev_enter(adev_to_drm(adev), &idx)) { memcpy_toio(adev->vcn.inst[i].cpu_addr, adev->vcn.inst[i].fw->data + offset, le32_to_cpu(hdr->ucode_size_bytes)); drm_dev_exit(idx); } size -= le32_to_cpu(hdr->ucode_size_bytes); ptr += le32_to_cpu(hdr->ucode_size_bytes); } memset_io(ptr, 0, size); } return 0;} void amdgpu_vcn_get_profile(struct amdgpu_device *adev){ int r; mutex_lock(&adev->vcn.workload_profile_mutex); if (adev->vcn.workload_profile_active) { mutex_unlock(&adev->vcn.workload_profile_mutex); return; } r = amdgpu_dpm_switch_power_profile(adev, PP_SMC_POWER_PROFILE_VIDEO, true); if (r) dev_warn(adev->dev, "(%d) failed to enable video power profile mode\n", r); else adev->vcn.workload_profile_active = true; mutex_unlock(&adev->vcn.workload_profile_mutex);} void amdgpu_vcn_put_profile(struct amdgpu_device *adev){ bool pg = true; int r, i; mutex_lock(&adev->vcn.workload_profile_mutex); for (i = 0; i < adev->vcn.num_vcn_inst; i++) { if (adev->vcn.inst[i].cur_state != AMD_PG_STATE_GATE) { pg = false; break; } } if (pg) { r = amdgpu_dpm_switch_power_profile( adev, PP_SMC_POWER_PROFILE_VIDEO, false); if (r) dev_warn( adev->dev, "(%d) failed to disable video power profile mode\n", r); else adev->vcn.workload_profile_active = false; } mutex_unlock(&adev->vcn.workload_profile_mutex);} static void amdgpu_vcn_idle_work_handler(struct work_struct *work){ struct amdgpu_vcn_inst *vcn_inst = container_of(work, struct amdgpu_vcn_inst, idle_work.work); struct amdgpu_device *adev = vcn_inst->adev; unsigned int fences = 0, fence[AMDGPU_MAX_VCN_INSTANCES] = {0}; unsigned int i = vcn_inst->inst, j; if (adev->vcn.harvest_config & (1 << i)) return; for (j = 0; j < adev->vcn.inst[i].num_enc_rings; ++j) fence[i] += amdgpu_fence_count_emitted(&vcn_inst->ring_enc[j]); /* Only set DPG pause for VCN3 or below, VCN4 and above will be handled by FW */ if (adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG && !adev->vcn.inst[i].using_unified_queue) { struct dpg_pause_state new_state; if (fence[i] || unlikely(atomic_read(&vcn_inst->dpg_enc_submission_cnt))) new_state.fw_based = VCN_DPG_STATE__PAUSE; else new_state.fw_based = VCN_DPG_STATE__UNPAUSE; adev->vcn.inst[i].pause_dpg_mode(vcn_inst, &new_state); } fence[i] += amdgpu_fence_count_emitted(&vcn_inst->ring_dec); fences += fence[i]; if (!fences && !atomic_read(&vcn_inst->total_submission_cnt)) { mutex_lock(&vcn_inst->vcn_pg_lock); vcn_inst->set_pg_state(vcn_inst, AMD_PG_STATE_GATE); mutex_unlock(&vcn_inst->vcn_pg_lock); amdgpu_vcn_put_profile(adev); } else { schedule_delayed_work(&vcn_inst->idle_work, VCN_IDLE_TIMEOUT);Lines 1–500 of 1652. Load more
/* * Copyright 2016-2024 Advanced Micro Devices, Inc. * All Rights Reserved. * * 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, sub license, 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 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 NON-INFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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. * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial portions * of the Software. * */ #include <linux/firmware.h> #include <linux/module.h> #include <linux/dmi.h> #include <linux/pci.h> #include <linux/debugfs.h> #include <drm/drm_drv.h> #include "amdgpu.h" #include "amdgpu_pm.h" #include "amdgpu_vcn.h" #include "amdgpu_reset.h" #include "soc15d.h" /* Firmware Names */ #define FIRMWARE_RAVEN "amdgpu/raven_vcn.bin" #define FIRMWARE_PICASSO "amdgpu/picasso_vcn.bin" #define FIRMWARE_RAVEN2 "amdgpu/raven2_vcn.bin" #define FIRMWARE_ARCTURUS "amdgpu/arcturus_vcn.bin" #define FIRMWARE_RENOIR "amdgpu/renoir_vcn.bin" #define FIRMWARE_GREEN_SARDINE "amdgpu/green_sardine_vcn.bin" #define FIRMWARE_NAVI10 "amdgpu/navi10_vcn.bin" #define FIRMWARE_NAVI14 "amdgpu/navi14_vcn.bin" #define FIRMWARE_NAVI12 "amdgpu/navi12_vcn.bin" #define FIRMWARE_SIENNA_CICHLID "amdgpu/sienna_cichlid_vcn.bin" #define FIRMWARE_NAVY_FLOUNDER "amdgpu/navy_flounder_vcn.bin" #define FIRMWARE_VANGOGH "amdgpu/vangogh_vcn.bin" #define FIRMWARE_DIMGREY_CAVEFISH "amdgpu/dimgrey_cavefish_vcn.bin" #define FIRMWARE_ALDEBARAN "amdgpu/aldebaran_vcn.bin" #define FIRMWARE_BEIGE_GOBY "amdgpu/beige_goby_vcn.bin" #define FIRMWARE_YELLOW_CARP "amdgpu/yellow_carp_vcn.bin" #define FIRMWARE_VCN_3_1_2 "amdgpu/vcn_3_1_2.bin" #define FIRMWARE_VCN4_0_0 "amdgpu/vcn_4_0_0.bin" #define FIRMWARE_VCN4_0_2 "amdgpu/vcn_4_0_2.bin" #define FIRMWARE_VCN4_0_3 "amdgpu/vcn_4_0_3.bin" #define FIRMWARE_VCN4_0_4 "amdgpu/vcn_4_0_4.bin" #define FIRMWARE_VCN4_0_5 "amdgpu/vcn_4_0_5.bin" #define FIRMWARE_VCN4_0_6 "amdgpu/vcn_4_0_6.bin" #define FIRMWARE_VCN4_0_6_1 "amdgpu/vcn_4_0_6_1.bin" #define FIRMWARE_VCN5_0_0 "amdgpu/vcn_5_0_0.bin" #define FIRMWARE_VCN5_0_1 "amdgpu/vcn_5_0_1.bin" #define FIRMWARE_VCN5_0_2 "amdgpu/vcn_5_0_2.bin" #define FIRMWARE_VCN5_3_0 "amdgpu/vcn_5_3_0.bin" MODULE_FIRMWARE(FIRMWARE_RAVEN); MODULE_FIRMWARE(FIRMWARE_PICASSO); MODULE_FIRMWARE(FIRMWARE_RAVEN2); MODULE_FIRMWARE(FIRMWARE_ARCTURUS); MODULE_FIRMWARE(FIRMWARE_RENOIR); MODULE_FIRMWARE(FIRMWARE_GREEN_SARDINE); MODULE_FIRMWARE(FIRMWARE_ALDEBARAN); MODULE_FIRMWARE(FIRMWARE_NAVI10); MODULE_FIRMWARE(FIRMWARE_NAVI14); MODULE_FIRMWARE(FIRMWARE_NAVI12); MODULE_FIRMWARE(FIRMWARE_SIENNA_CICHLID); MODULE_FIRMWARE(FIRMWARE_NAVY_FLOUNDER); MODULE_FIRMWARE(FIRMWARE_VANGOGH); MODULE_FIRMWARE(FIRMWARE_DIMGREY_CAVEFISH); MODULE_FIRMWARE(FIRMWARE_BEIGE_GOBY); MODULE_FIRMWARE(FIRMWARE_YELLOW_CARP); MODULE_FIRMWARE(FIRMWARE_VCN_3_1_2); MODULE_FIRMWARE(FIRMWARE_VCN4_0_0); MODULE_FIRMWARE(FIRMWARE_VCN4_0_2); MODULE_FIRMWARE(FIRMWARE_VCN4_0_3); MODULE_FIRMWARE(FIRMWARE_VCN4_0_4); MODULE_FIRMWARE(FIRMWARE_VCN4_0_5); MODULE_FIRMWARE(FIRMWARE_VCN4_0_6); MODULE_FIRMWARE(FIRMWARE_VCN4_0_6_1); MODULE_FIRMWARE(FIRMWARE_VCN5_0_0); MODULE_FIRMWARE(FIRMWARE_VCN5_0_1); MODULE_FIRMWARE(FIRMWARE_VCN5_0_2); MODULE_FIRMWARE(FIRMWARE_VCN5_3_0); static void amdgpu_vcn_idle_work_handler(struct work_struct *work); static void amdgpu_vcn_reg_dump_fini(struct amdgpu_device *adev); int amdgpu_vcn_early_init(struct amdgpu_device *adev, int i) { char ucode_prefix[25]; int r; adev->vcn.inst[i].adev = adev; adev->vcn.inst[i].inst = i; amdgpu_ucode_ip_version_decode(adev, UVD_HWIP, ucode_prefix, sizeof(ucode_prefix)); if (i != 0 && adev->vcn.per_inst_fw) { r = amdgpu_ucode_request(adev, &adev->vcn.inst[i].fw, AMDGPU_UCODE_REQUIRED, "amdgpu/%s_%d.bin", ucode_prefix, i); if (r) amdgpu_ucode_release(&adev->vcn.inst[i].fw); } else { if (!adev->vcn.inst[0].fw) { r = amdgpu_ucode_request(adev, &adev->vcn.inst[0].fw, AMDGPU_UCODE_REQUIRED, "amdgpu/%s.bin", ucode_prefix); if (r) amdgpu_ucode_release(&adev->vcn.inst[0].fw); } else { r = 0; } adev->vcn.inst[i].fw = adev->vcn.inst[0].fw; } return r; } int amdgpu_vcn_sw_init(struct amdgpu_device *adev, int i) { unsigned long bo_size; const struct common_firmware_header *hdr; unsigned char fw_check; unsigned int fw_shared_size, log_offset; int r; mutex_init(&adev->vcn.inst[i].vcn1_jpeg1_workaround); mutex_init(&adev->vcn.inst[i].vcn_pg_lock); mutex_init(&adev->vcn.inst[i].engine_reset_mutex); atomic_set(&adev->vcn.inst[i].total_submission_cnt, 0); INIT_DELAYED_WORK(&adev->vcn.inst[i].idle_work, amdgpu_vcn_idle_work_handler); atomic_set(&adev->vcn.inst[i].dpg_enc_submission_cnt, 0); if ((adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) && (adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG)) adev->vcn.inst[i].indirect_sram = true; /* * Some Steam Deck's BIOS versions are incompatible with the * indirect SRAM mode, leading to amdgpu being unable to get * properly probed (and even potentially crashing the kernel). * Hence, check for these versions here - notice this is * restricted to Vangogh (Deck's APU). */ if (amdgpu_ip_version(adev, UVD_HWIP, 0) == IP_VERSION(3, 0, 2)) { const char *bios_ver = dmi_get_system_info(DMI_BIOS_VERSION); if (bios_ver && (!strncmp("F7A0113", bios_ver, 7) || !strncmp("F7A0114", bios_ver, 7))) { adev->vcn.inst[i].indirect_sram = false; dev_info(adev->dev, "Steam Deck quirk: indirect SRAM disabled on BIOS %s\n", bios_ver); } } /* from vcn4 and above, only unified queue is used */ adev->vcn.inst[i].using_unified_queue = amdgpu_ip_version(adev, UVD_HWIP, 0) >= IP_VERSION(4, 0, 0); hdr = (const struct common_firmware_header *)adev->vcn.inst[i].fw->data; adev->vcn.inst[i].fw_version = le32_to_cpu(hdr->ucode_version); adev->vcn.fw_version = le32_to_cpu(hdr->ucode_version); /* Bit 20-23, it is encode major and non-zero for new naming convention. * This field is part of version minor and DRM_DISABLED_FLAG in old naming * convention. Since the l:wq!atest version minor is 0x5B and DRM_DISABLED_FLAG * is zero in old naming convention, this field is always zero so far. * These four bits are used to tell which naming convention is present. */ fw_check = (le32_to_cpu(hdr->ucode_version) >> 20) & 0xf; if (fw_check) { unsigned int dec_ver, enc_major, enc_minor, vep, fw_rev; fw_rev = le32_to_cpu(hdr->ucode_version) & 0xfff; enc_minor = (le32_to_cpu(hdr->ucode_version) >> 12) & 0xff; enc_major = fw_check; dec_ver = (le32_to_cpu(hdr->ucode_version) >> 24) & 0xf; vep = (le32_to_cpu(hdr->ucode_version) >> 28) & 0xf; dev_info(adev->dev, "[VCN instance %d] Found VCN firmware Version ENC: %u.%u DEC: %u VEP: %u Revision: %u\n", i, enc_major, enc_minor, dec_ver, vep, fw_rev); } else { unsigned int version_major, version_minor, family_id; family_id = le32_to_cpu(hdr->ucode_version) & 0xff; version_major = (le32_to_cpu(hdr->ucode_version) >> 24) & 0xff; version_minor = (le32_to_cpu(hdr->ucode_version) >> 8) & 0xff; dev_info(adev->dev, "[VCN instance %d] Found VCN firmware Version: %u.%u Family ID: %u\n", i, version_major, version_minor, family_id); } bo_size = AMDGPU_VCN_STACK_SIZE + AMDGPU_VCN_CONTEXT_SIZE; if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP) bo_size += AMDGPU_GPU_PAGE_ALIGN(le32_to_cpu(hdr->ucode_size_bytes) + 8); if (amdgpu_ip_version(adev, UVD_HWIP, 0) >= IP_VERSION(5, 0, 0)) { fw_shared_size = AMDGPU_GPU_PAGE_ALIGN(sizeof(struct amdgpu_vcn5_fw_shared)); log_offset = offsetof(struct amdgpu_vcn5_fw_shared, fw_log); } else if (amdgpu_ip_version(adev, UVD_HWIP, 0) >= IP_VERSION(4, 0, 0)) { fw_shared_size = AMDGPU_GPU_PAGE_ALIGN(sizeof(struct amdgpu_vcn4_fw_shared)); log_offset = offsetof(struct amdgpu_vcn4_fw_shared, fw_log); } else { fw_shared_size = AMDGPU_GPU_PAGE_ALIGN(sizeof(struct amdgpu_fw_shared)); log_offset = offsetof(struct amdgpu_fw_shared, fw_log); } bo_size += fw_shared_size; if (amdgpu_vcnfw_log) bo_size += AMDGPU_VCNFW_LOG_SIZE; r = amdgpu_bo_create_kernel(adev, bo_size, PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM | AMDGPU_GEM_DOMAIN_GTT, &adev->vcn.inst[i].vcpu_bo, &adev->vcn.inst[i].gpu_addr, &adev->vcn.inst[i].cpu_addr); if (r) { dev_err(adev->dev, "(%d) failed to allocate vcn bo\n", r); return r; } adev->vcn.inst[i].fw_shared.cpu_addr = adev->vcn.inst[i].cpu_addr + bo_size - fw_shared_size; adev->vcn.inst[i].fw_shared.gpu_addr = adev->vcn.inst[i].gpu_addr + bo_size - fw_shared_size; adev->vcn.inst[i].fw_shared.mem_size = fw_shared_size; if (amdgpu_vcnfw_log) { adev->vcn.inst[i].fw_shared.cpu_addr -= AMDGPU_VCNFW_LOG_SIZE; adev->vcn.inst[i].fw_shared.gpu_addr -= AMDGPU_VCNFW_LOG_SIZE; adev->vcn.inst[i].fw_shared.log_offset = log_offset; } if (adev->vcn.inst[i].indirect_sram) { r = amdgpu_bo_create_kernel(adev, 64 * 2 * 4, PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM | AMDGPU_GEM_DOMAIN_GTT, &adev->vcn.inst[i].dpg_sram_bo, &adev->vcn.inst[i].dpg_sram_gpu_addr, &adev->vcn.inst[i].dpg_sram_cpu_addr); if (r) { dev_err(adev->dev, "VCN %d (%d) failed to allocate DPG bo\n", i, r); return r; } } return 0; } void amdgpu_vcn_sw_fini(struct amdgpu_device *adev, int i) { int j; if (adev->vcn.harvest_config & (1 << i)) return; amdgpu_bo_free_kernel( &adev->vcn.inst[i].dpg_sram_bo, &adev->vcn.inst[i].dpg_sram_gpu_addr, (void **)&adev->vcn.inst[i].dpg_sram_cpu_addr); kvfree(adev->vcn.inst[i].saved_bo); amdgpu_bo_free_kernel(&adev->vcn.inst[i].vcpu_bo, &adev->vcn.inst[i].gpu_addr, (void **)&adev->vcn.inst[i].cpu_addr); amdgpu_ring_fini(&adev->vcn.inst[i].ring_dec); for (j = 0; j < adev->vcn.inst[i].num_enc_rings; ++j) amdgpu_ring_fini(&adev->vcn.inst[i].ring_enc[j]); if (adev->vcn.per_inst_fw) { amdgpu_ucode_release(&adev->vcn.inst[i].fw); } else { amdgpu_ucode_release(&adev->vcn.inst[0].fw); adev->vcn.inst[i].fw = NULL; } if (adev->vcn.reg_list) amdgpu_vcn_reg_dump_fini(adev); mutex_destroy(&adev->vcn.inst[i].vcn_pg_lock); mutex_destroy(&adev->vcn.inst[i].vcn1_jpeg1_workaround); } bool amdgpu_vcn_is_disabled_vcn(struct amdgpu_device *adev, enum vcn_ring_type type, uint32_t vcn_instance) { bool ret = false; int vcn_config = adev->vcn.inst[vcn_instance].vcn_config; if ((type == VCN_ENCODE_RING) && (vcn_config & VCN_BLOCK_ENCODE_DISABLE_MASK)) ret = true; else if ((type == VCN_DECODE_RING) && (vcn_config & VCN_BLOCK_DECODE_DISABLE_MASK)) ret = true; else if ((type == VCN_UNIFIED_RING) && (vcn_config & VCN_BLOCK_QUEUE_DISABLE_MASK)) ret = true; return ret; } static int amdgpu_vcn_save_vcpu_bo_inst(struct amdgpu_device *adev, int i) { unsigned int size; void *ptr; int idx; if (adev->vcn.harvest_config & (1 << i)) return 0; if (adev->vcn.inst[i].vcpu_bo == NULL) return 0; size = amdgpu_bo_size(adev->vcn.inst[i].vcpu_bo); ptr = adev->vcn.inst[i].cpu_addr; adev->vcn.inst[i].saved_bo = kvmalloc(size, GFP_KERNEL); if (!adev->vcn.inst[i].saved_bo) return -ENOMEM; if (drm_dev_enter(adev_to_drm(adev), &idx)) { memcpy_fromio(adev->vcn.inst[i].saved_bo, ptr, size); drm_dev_exit(idx); } return 0; } int amdgpu_vcn_save_vcpu_bo(struct amdgpu_device *adev) { int ret, i; for (i = 0; i < adev->vcn.num_vcn_inst; ++i) { ret = amdgpu_vcn_save_vcpu_bo_inst(adev, i); if (ret) return ret; } return 0; } int amdgpu_vcn_suspend(struct amdgpu_device *adev, int i) { bool in_ras_intr = amdgpu_ras_intr_triggered(); if (adev->vcn.harvest_config & (1 << i)) return 0; /* err_event_athub and dpc recovery will corrupt VCPU buffer, so we need to * restore fw data and clear buffer in amdgpu_vcn_resume() */ if (in_ras_intr || amdgpu_reset_in_dpc(adev)) return 0; return amdgpu_vcn_save_vcpu_bo_inst(adev, i); } int amdgpu_vcn_resume(struct amdgpu_device *adev, int i) { unsigned int size; void *ptr; int idx; if (adev->vcn.harvest_config & (1 << i)) return 0; if (adev->vcn.inst[i].vcpu_bo == NULL) return -EINVAL; size = amdgpu_bo_size(adev->vcn.inst[i].vcpu_bo); ptr = adev->vcn.inst[i].cpu_addr; if (adev->vcn.inst[i].saved_bo != NULL) { if (drm_dev_enter(adev_to_drm(adev), &idx)) { memcpy_toio(ptr, adev->vcn.inst[i].saved_bo, size); drm_dev_exit(idx); } kvfree(adev->vcn.inst[i].saved_bo); adev->vcn.inst[i].saved_bo = NULL; } else { const struct common_firmware_header *hdr; unsigned int offset; hdr = (const struct common_firmware_header *)adev->vcn.inst[i].fw->data; if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP) { offset = le32_to_cpu(hdr->ucode_array_offset_bytes); if (drm_dev_enter(adev_to_drm(adev), &idx)) { memcpy_toio(adev->vcn.inst[i].cpu_addr, adev->vcn.inst[i].fw->data + offset, le32_to_cpu(hdr->ucode_size_bytes)); drm_dev_exit(idx); } size -= le32_to_cpu(hdr->ucode_size_bytes); ptr += le32_to_cpu(hdr->ucode_size_bytes); } memset_io(ptr, 0, size); } return 0; } void amdgpu_vcn_get_profile(struct amdgpu_device *adev) { int r; mutex_lock(&adev->vcn.workload_profile_mutex); if (adev->vcn.workload_profile_active) { mutex_unlock(&adev->vcn.workload_profile_mutex); return; } r = amdgpu_dpm_switch_power_profile(adev, PP_SMC_POWER_PROFILE_VIDEO, true); if (r) dev_warn(adev->dev, "(%d) failed to enable video power profile mode\n", r); else adev->vcn.workload_profile_active = true; mutex_unlock(&adev->vcn.workload_profile_mutex); } void amdgpu_vcn_put_profile(struct amdgpu_device *adev) { bool pg = true; int r, i; mutex_lock(&adev->vcn.workload_profile_mutex); for (i = 0; i < adev->vcn.num_vcn_inst; i++) { if (adev->vcn.inst[i].cur_state != AMD_PG_STATE_GATE) { pg = false; break; } } if (pg) { r = amdgpu_dpm_switch_power_profile( adev, PP_SMC_POWER_PROFILE_VIDEO, false); if (r) dev_warn( adev->dev, "(%d) failed to disable video power profile mode\n", r); else adev->vcn.workload_profile_active = false; } mutex_unlock(&adev->vcn.workload_profile_mutex); } static void amdgpu_vcn_idle_work_handler(struct work_struct *work) { struct amdgpu_vcn_inst *vcn_inst = container_of(work, struct amdgpu_vcn_inst, idle_work.work); struct amdgpu_device *adev = vcn_inst->adev; unsigned int fences = 0, fence[AMDGPU_MAX_VCN_INSTANCES] = {0}; unsigned int i = vcn_inst->inst, j; if (adev->vcn.harvest_config & (1 << i)) return; for (j = 0; j < adev->vcn.inst[i].num_enc_rings; ++j) fence[i] += amdgpu_fence_count_emitted(&vcn_inst->ring_enc[j]); /* Only set DPG pause for VCN3 or below, VCN4 and above will be handled by FW */ if (adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG && !adev->vcn.inst[i].using_unified_queue) { struct dpg_pause_state new_state; if (fence[i] || unlikely(atomic_read(&vcn_inst->dpg_enc_submission_cnt))) new_state.fw_based = VCN_DPG_STATE__PAUSE; else new_state.fw_based = VCN_DPG_STATE__UNPAUSE; adev->vcn.inst[i].pause_dpg_mode(vcn_inst, &new_state); } fence[i] += amdgpu_fence_count_emitted(&vcn_inst->ring_dec); fences += fence[i]; if (!fences && !atomic_read(&vcn_inst->total_submission_cnt)) { mutex_lock(&vcn_inst->vcn_pg_lock); vcn_inst->set_pg_state(vcn_inst, AMD_PG_STATE_GATE); mutex_unlock(&vcn_inst->vcn_pg_lock); amdgpu_vcn_put_profile(adev); } else { schedule_delayed_work(&vcn_inst->idle_work, VCN_IDLE_TIMEOUT);