1652 lines · 44.4 KB · C
RawDownload
/* * 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.