995 lines · 23.8 KB · C
RawDownload
/* * Copyright 2011 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. * *//* * Authors: *    Christian König <deathsimple@vodafone.de> */ #include <linux/firmware.h>#include <linux/module.h>#include <drm/drmP.h>#include <drm/drm.h> #include "amdgpu.h"#include "amdgpu_pm.h"#include "amdgpu_uvd.h"#include "cikd.h"#include "uvd/uvd_4_2_d.h" /* 1 second timeout */#define UVD_IDLE_TIMEOUT_MS	1000 /* Firmware Names */#ifdef CONFIG_DRM_AMDGPU_CIK#define FIRMWARE_BONAIRE	"radeon/bonaire_uvd.bin"#define FIRMWARE_KABINI 	"radeon/kabini_uvd.bin"#define FIRMWARE_KAVERI 	"radeon/kaveri_uvd.bin"#define FIRMWARE_HAWAII 	"radeon/hawaii_uvd.bin"#define FIRMWARE_MULLINS	"radeon/mullins_uvd.bin"#endif#define FIRMWARE_TONGA		"amdgpu/tonga_uvd.bin"#define FIRMWARE_CARRIZO	"amdgpu/carrizo_uvd.bin" /** * amdgpu_uvd_cs_ctx - Command submission parser context * * Used for emulating virtual memory support on UVD 4.2. */struct amdgpu_uvd_cs_ctx {	struct amdgpu_cs_parser *parser;	unsigned reg, count;	unsigned data0, data1;	unsigned idx;	unsigned ib_idx; 	/* does the IB has a msg command */	bool has_msg_cmd; 	/* minimum buffer sizes */	unsigned *buf_sizes;}; #ifdef CONFIG_DRM_AMDGPU_CIKMODULE_FIRMWARE(FIRMWARE_BONAIRE);MODULE_FIRMWARE(FIRMWARE_KABINI);MODULE_FIRMWARE(FIRMWARE_KAVERI);MODULE_FIRMWARE(FIRMWARE_HAWAII);MODULE_FIRMWARE(FIRMWARE_MULLINS);#endifMODULE_FIRMWARE(FIRMWARE_TONGA);MODULE_FIRMWARE(FIRMWARE_CARRIZO); static void amdgpu_uvd_note_usage(struct amdgpu_device *adev);static void amdgpu_uvd_idle_work_handler(struct work_struct *work); int amdgpu_uvd_sw_init(struct amdgpu_device *adev){	unsigned long bo_size;	const char *fw_name;	const struct common_firmware_header *hdr;	unsigned version_major, version_minor, family_id;	int i, r; 	INIT_DELAYED_WORK(&adev->uvd.idle_work, amdgpu_uvd_idle_work_handler); 	switch (adev->asic_type) {#ifdef CONFIG_DRM_AMDGPU_CIK	case CHIP_BONAIRE:		fw_name = FIRMWARE_BONAIRE;		break;	case CHIP_KABINI:		fw_name = FIRMWARE_KABINI;		break;	case CHIP_KAVERI:		fw_name = FIRMWARE_KAVERI;		break;	case CHIP_HAWAII:		fw_name = FIRMWARE_HAWAII;		break;	case CHIP_MULLINS:		fw_name = FIRMWARE_MULLINS;		break;#endif	case CHIP_TONGA:		fw_name = FIRMWARE_TONGA;		break;	case CHIP_CARRIZO:		fw_name = FIRMWARE_CARRIZO;		break;	default:		return -EINVAL;	} 	r = request_firmware(&adev->uvd.fw, fw_name, adev->dev);	if (r) {		dev_err(adev->dev, "amdgpu_uvd: Can't load firmware \"%s\"\n",			fw_name);		return r;	} 	r = amdgpu_ucode_validate(adev->uvd.fw);	if (r) {		dev_err(adev->dev, "amdgpu_uvd: Can't validate firmware \"%s\"\n",			fw_name);		release_firmware(adev->uvd.fw);		adev->uvd.fw = NULL;		return r;	} 	hdr = (const struct common_firmware_header *)adev->uvd.fw->data;	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;	DRM_INFO("Found UVD firmware Version: %hu.%hu Family ID: %hu\n",		version_major, version_minor, family_id); 	bo_size = AMDGPU_GPU_PAGE_ALIGN(le32_to_cpu(hdr->ucode_size_bytes) + 8)		 +  AMDGPU_UVD_STACK_SIZE + AMDGPU_UVD_HEAP_SIZE;	r = amdgpu_bo_create(adev, bo_size, PAGE_SIZE, true,			     AMDGPU_GEM_DOMAIN_VRAM, 0, NULL, &adev->uvd.vcpu_bo);	if (r) {		dev_err(adev->dev, "(%d) failed to allocate UVD bo\n", r);		return r;	} 	r = amdgpu_bo_reserve(adev->uvd.vcpu_bo, false);	if (r) {		amdgpu_bo_unref(&adev->uvd.vcpu_bo);		dev_err(adev->dev, "(%d) failed to reserve UVD bo\n", r);		return r;	} 	r = amdgpu_bo_pin(adev->uvd.vcpu_bo, AMDGPU_GEM_DOMAIN_VRAM,			  &adev->uvd.gpu_addr);	if (r) {		amdgpu_bo_unreserve(adev->uvd.vcpu_bo);		amdgpu_bo_unref(&adev->uvd.vcpu_bo);		dev_err(adev->dev, "(%d) UVD bo pin failed\n", r);		return r;	} 	r = amdgpu_bo_kmap(adev->uvd.vcpu_bo, &adev->uvd.cpu_addr);	if (r) {		dev_err(adev->dev, "(%d) UVD map failed\n", r);		return r;	} 	amdgpu_bo_unreserve(adev->uvd.vcpu_bo); 	for (i = 0; i < AMDGPU_MAX_UVD_HANDLES; ++i) {		atomic_set(&adev->uvd.handles[i], 0);		adev->uvd.filp[i] = NULL;	} 	/* from uvd v5.0 HW addressing capacity increased to 64 bits */	if (!amdgpu_ip_block_version_cmp(adev, AMD_IP_BLOCK_TYPE_UVD, 5, 0))		adev->uvd.address_64_bit = true; 	return 0;} int amdgpu_uvd_sw_fini(struct amdgpu_device *adev){	int r; 	if (adev->uvd.vcpu_bo == NULL)		return 0; 	r = amdgpu_bo_reserve(adev->uvd.vcpu_bo, false);	if (!r) {		amdgpu_bo_kunmap(adev->uvd.vcpu_bo);		amdgpu_bo_unpin(adev->uvd.vcpu_bo);		amdgpu_bo_unreserve(adev->uvd.vcpu_bo);	} 	amdgpu_bo_unref(&adev->uvd.vcpu_bo); 	amdgpu_ring_fini(&adev->uvd.ring); 	release_firmware(adev->uvd.fw); 	return 0;} int amdgpu_uvd_suspend(struct amdgpu_device *adev){	unsigned size;	void *ptr;	const struct common_firmware_header *hdr;	int i; 	if (adev->uvd.vcpu_bo == NULL)		return 0; 	for (i = 0; i < AMDGPU_MAX_UVD_HANDLES; ++i)		if (atomic_read(&adev->uvd.handles[i]))			break; 	if (i == AMDGPU_MAX_UVD_HANDLES)		return 0; 	hdr = (const struct common_firmware_header *)adev->uvd.fw->data; 	size = amdgpu_bo_size(adev->uvd.vcpu_bo);	size -= le32_to_cpu(hdr->ucode_size_bytes); 	ptr = adev->uvd.cpu_addr;	ptr += le32_to_cpu(hdr->ucode_size_bytes); 	adev->uvd.saved_bo = kmalloc(size, GFP_KERNEL);	memcpy(adev->uvd.saved_bo, ptr, size); 	return 0;} int amdgpu_uvd_resume(struct amdgpu_device *adev){	unsigned size;	void *ptr;	const struct common_firmware_header *hdr;	unsigned offset; 	if (adev->uvd.vcpu_bo == NULL)		return -EINVAL; 	hdr = (const struct common_firmware_header *)adev->uvd.fw->data;	offset = le32_to_cpu(hdr->ucode_array_offset_bytes);	memcpy(adev->uvd.cpu_addr, (adev->uvd.fw->data) + offset,		(adev->uvd.fw->size) - offset); 	size = amdgpu_bo_size(adev->uvd.vcpu_bo);	size -= le32_to_cpu(hdr->ucode_size_bytes);	ptr = adev->uvd.cpu_addr;	ptr += le32_to_cpu(hdr->ucode_size_bytes); 	if (adev->uvd.saved_bo != NULL) {		memcpy(ptr, adev->uvd.saved_bo, size);		kfree(adev->uvd.saved_bo);		adev->uvd.saved_bo = NULL;	} else		memset(ptr, 0, size); 	return 0;} void amdgpu_uvd_free_handles(struct amdgpu_device *adev, struct drm_file *filp){	struct amdgpu_ring *ring = &adev->uvd.ring;	int i, r; 	for (i = 0; i < AMDGPU_MAX_UVD_HANDLES; ++i) {		uint32_t handle = atomic_read(&adev->uvd.handles[i]);		if (handle != 0 && adev->uvd.filp[i] == filp) {			struct amdgpu_fence *fence; 			amdgpu_uvd_note_usage(adev); 			r = amdgpu_uvd_get_destroy_msg(ring, handle, &fence);			if (r) {				DRM_ERROR("Error destroying UVD (%d)!\n", r);				continue;			} 			amdgpu_fence_wait(fence, false);			amdgpu_fence_unref(&fence); 			adev->uvd.filp[i] = NULL;			atomic_set(&adev->uvd.handles[i], 0);		}	}} static void amdgpu_uvd_force_into_uvd_segment(struct amdgpu_bo *rbo){	int i;	for (i = 0; i < rbo->placement.num_placement; ++i) {		rbo->placements[i].fpfn = 0 >> PAGE_SHIFT;		rbo->placements[i].lpfn = (256 * 1024 * 1024) >> PAGE_SHIFT;	}} /** * amdgpu_uvd_cs_pass1 - first parsing round * * @ctx: UVD parser context * * Make sure UVD message and feedback buffers are in VRAM and * nobody is violating an 256MB boundary. */static int amdgpu_uvd_cs_pass1(struct amdgpu_uvd_cs_ctx *ctx){	struct amdgpu_bo_va_mapping *mapping;	struct amdgpu_bo *bo;	uint32_t cmd, lo, hi;	uint64_t addr;	int r = 0; 	lo = amdgpu_get_ib_value(ctx->parser, ctx->ib_idx, ctx->data0);	hi = amdgpu_get_ib_value(ctx->parser, ctx->ib_idx, ctx->data1);	addr = ((uint64_t)lo) | (((uint64_t)hi) << 32); 	mapping = amdgpu_cs_find_mapping(ctx->parser, addr, &bo);	if (mapping == NULL) {		DRM_ERROR("Can't find BO for addr 0x%08Lx\n", addr);		return -EINVAL;	} 	if (!ctx->parser->adev->uvd.address_64_bit) {		/* check if it's a message or feedback command */		cmd = amdgpu_get_ib_value(ctx->parser, ctx->ib_idx, ctx->idx) >> 1;		if (cmd == 0x0 || cmd == 0x3) {			/* yes, force it into VRAM */			uint32_t domain = AMDGPU_GEM_DOMAIN_VRAM;			amdgpu_ttm_placement_from_domain(bo, domain);		}		amdgpu_uvd_force_into_uvd_segment(bo); 		r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false);	} 	return r;} /** * amdgpu_uvd_cs_msg_decode - handle UVD decode message * * @msg: pointer to message structure * @buf_sizes: returned buffer sizes * * Peek into the decode message and calculate the necessary buffer sizes. */static int amdgpu_uvd_cs_msg_decode(uint32_t *msg, unsigned buf_sizes[]){	unsigned stream_type = msg[4];	unsigned width = msg[6];	unsigned height = msg[7];	unsigned dpb_size = msg[9];	unsigned pitch = msg[28];	unsigned level = msg[57]; 	unsigned width_in_mb = width / 16;	unsigned height_in_mb = ALIGN(height / 16, 2);	unsigned fs_in_mb = width_in_mb * height_in_mb; 	unsigned image_size, tmp, min_dpb_size, num_dpb_buffer, min_ctx_size; 	image_size = width * height;	image_size += image_size / 2;	image_size = ALIGN(image_size, 1024); 	switch (stream_type) {	case 0: /* H264 */	case 7: /* H264 Perf */		switch(level) {		case 30:			num_dpb_buffer = 8100 / fs_in_mb;			break;		case 31:			num_dpb_buffer = 18000 / fs_in_mb;			break;		case 32:			num_dpb_buffer = 20480 / fs_in_mb;			break;		case 41:			num_dpb_buffer = 32768 / fs_in_mb;			break;		case 42:			num_dpb_buffer = 34816 / fs_in_mb;			break;		case 50:			num_dpb_buffer = 110400 / fs_in_mb;			break;		case 51:			num_dpb_buffer = 184320 / fs_in_mb;			break;		default:			num_dpb_buffer = 184320 / fs_in_mb;			break;		}		num_dpb_buffer++;		if (num_dpb_buffer > 17)			num_dpb_buffer = 17; 		/* reference picture buffer */		min_dpb_size = image_size * num_dpb_buffer; 		/* macroblock context buffer */		min_dpb_size += width_in_mb * height_in_mb * num_dpb_buffer * 192; 		/* IT surface buffer */		min_dpb_size += width_in_mb * height_in_mb * 32;		break; 	case 1: /* VC1 */ 		/* reference picture buffer */		min_dpb_size = image_size * 3; 		/* CONTEXT_BUFFER */		min_dpb_size += width_in_mb * height_in_mb * 128; 		/* IT surface buffer */		min_dpb_size += width_in_mb * 64; 		/* DB surface buffer */		min_dpb_size += width_in_mb * 128; 		/* BP */		tmp = max(width_in_mb, height_in_mb);		min_dpb_size += ALIGN(tmp * 7 * 16, 64);		break; 	case 3: /* MPEG2 */ 		/* reference picture buffer */		min_dpb_size = image_size * 3;		break; 	case 4: /* MPEG4 */ 		/* reference picture buffer */		min_dpb_size = image_size * 3; 		/* CM */		min_dpb_size += width_in_mb * height_in_mb * 64; 		/* IT surface buffer */		min_dpb_size += ALIGN(width_in_mb * height_in_mb * 32, 64);		break; 	case 16: /* H265 */		image_size = (ALIGN(width, 16) * ALIGN(height, 16) * 3) / 2;		image_size = ALIGN(image_size, 256); 		num_dpb_buffer = (le32_to_cpu(msg[59]) & 0xff) + 2;		min_dpb_size = image_size * num_dpb_buffer;		min_ctx_size = ((width + 255) / 16) * ((height + 255) / 16)					   * 16 * num_dpb_buffer + 52 * 1024;		break; 	default:		DRM_ERROR("UVD codec not handled %d!\n", stream_type);		return -EINVAL;	} 	if (width > pitch) {		DRM_ERROR("Invalid UVD decoding target pitch!\n");		return -EINVAL;	} 	if (dpb_size < min_dpb_size) {		DRM_ERROR("Invalid dpb_size in UVD message (%d / %d)!\n",			  dpb_size, min_dpb_size);		return -EINVAL;	} 	buf_sizes[0x1] = dpb_size;	buf_sizes[0x2] = image_size;	buf_sizes[0x4] = min_ctx_size;	return 0;} /** * amdgpu_uvd_cs_msg - handle UVD message * * @ctx: UVD parser context * @bo: buffer object containing the message * @offset: offset into the buffer object

Lines 1–500 of 995.