162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright 2013 Advanced Micro Devices, Inc. 362306a36Sopenharmony_ci * All Rights Reserved. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 662306a36Sopenharmony_ci * copy of this software and associated documentation files (the 762306a36Sopenharmony_ci * "Software"), to deal in the Software without restriction, including 862306a36Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish, 962306a36Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to 1062306a36Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to 1162306a36Sopenharmony_ci * the following conditions: 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1462306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1562306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 1662306a36Sopenharmony_ci * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 1762306a36Sopenharmony_ci * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 1862306a36Sopenharmony_ci * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 1962306a36Sopenharmony_ci * USE OR OTHER DEALINGS IN THE SOFTWARE. 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the 2262306a36Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions 2362306a36Sopenharmony_ci * of the Software. 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci * Authors: Christian König <christian.koenig@amd.com> 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include <linux/firmware.h> 2962306a36Sopenharmony_ci#include <linux/module.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include <drm/drm.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include "radeon.h" 3462306a36Sopenharmony_ci#include "radeon_asic.h" 3562306a36Sopenharmony_ci#include "sid.h" 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* 1 second timeout */ 3862306a36Sopenharmony_ci#define VCE_IDLE_TIMEOUT_MS 1000 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* Firmware Names */ 4162306a36Sopenharmony_ci#define FIRMWARE_TAHITI "radeon/TAHITI_vce.bin" 4262306a36Sopenharmony_ci#define FIRMWARE_BONAIRE "radeon/BONAIRE_vce.bin" 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ciMODULE_FIRMWARE(FIRMWARE_TAHITI); 4562306a36Sopenharmony_ciMODULE_FIRMWARE(FIRMWARE_BONAIRE); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic void radeon_vce_idle_work_handler(struct work_struct *work); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/** 5062306a36Sopenharmony_ci * radeon_vce_init - allocate memory, load vce firmware 5162306a36Sopenharmony_ci * 5262306a36Sopenharmony_ci * @rdev: radeon_device pointer 5362306a36Sopenharmony_ci * 5462306a36Sopenharmony_ci * First step to get VCE online, allocate memory and load the firmware 5562306a36Sopenharmony_ci */ 5662306a36Sopenharmony_ciint radeon_vce_init(struct radeon_device *rdev) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci static const char *fw_version = "[ATI LIB=VCEFW,"; 5962306a36Sopenharmony_ci static const char *fb_version = "[ATI LIB=VCEFWSTATS,"; 6062306a36Sopenharmony_ci unsigned long size; 6162306a36Sopenharmony_ci const char *fw_name, *c; 6262306a36Sopenharmony_ci uint8_t start, mid, end; 6362306a36Sopenharmony_ci int i, r; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci INIT_DELAYED_WORK(&rdev->vce.idle_work, radeon_vce_idle_work_handler); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci switch (rdev->family) { 6862306a36Sopenharmony_ci case CHIP_TAHITI: 6962306a36Sopenharmony_ci case CHIP_PITCAIRN: 7062306a36Sopenharmony_ci case CHIP_VERDE: 7162306a36Sopenharmony_ci case CHIP_ARUBA: 7262306a36Sopenharmony_ci fw_name = FIRMWARE_TAHITI; 7362306a36Sopenharmony_ci break; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci case CHIP_BONAIRE: 7662306a36Sopenharmony_ci case CHIP_KAVERI: 7762306a36Sopenharmony_ci case CHIP_KABINI: 7862306a36Sopenharmony_ci case CHIP_HAWAII: 7962306a36Sopenharmony_ci case CHIP_MULLINS: 8062306a36Sopenharmony_ci fw_name = FIRMWARE_BONAIRE; 8162306a36Sopenharmony_ci break; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci default: 8462306a36Sopenharmony_ci return -EINVAL; 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci r = request_firmware(&rdev->vce_fw, fw_name, rdev->dev); 8862306a36Sopenharmony_ci if (r) { 8962306a36Sopenharmony_ci dev_err(rdev->dev, "radeon_vce: Can't load firmware \"%s\"\n", 9062306a36Sopenharmony_ci fw_name); 9162306a36Sopenharmony_ci return r; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci /* search for firmware version */ 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci size = rdev->vce_fw->size - strlen(fw_version) - 9; 9762306a36Sopenharmony_ci c = rdev->vce_fw->data; 9862306a36Sopenharmony_ci for (; size > 0; --size, ++c) 9962306a36Sopenharmony_ci if (strncmp(c, fw_version, strlen(fw_version)) == 0) 10062306a36Sopenharmony_ci break; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (size == 0) 10362306a36Sopenharmony_ci return -EINVAL; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci c += strlen(fw_version); 10662306a36Sopenharmony_ci if (sscanf(c, "%2hhd.%2hhd.%2hhd]", &start, &mid, &end) != 3) 10762306a36Sopenharmony_ci return -EINVAL; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* search for feedback version */ 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci size = rdev->vce_fw->size - strlen(fb_version) - 3; 11262306a36Sopenharmony_ci c = rdev->vce_fw->data; 11362306a36Sopenharmony_ci for (; size > 0; --size, ++c) 11462306a36Sopenharmony_ci if (strncmp(c, fb_version, strlen(fb_version)) == 0) 11562306a36Sopenharmony_ci break; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (size == 0) 11862306a36Sopenharmony_ci return -EINVAL; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci c += strlen(fb_version); 12162306a36Sopenharmony_ci if (sscanf(c, "%2u]", &rdev->vce.fb_version) != 1) 12262306a36Sopenharmony_ci return -EINVAL; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci DRM_INFO("Found VCE firmware/feedback version %d.%d.%d / %d!\n", 12562306a36Sopenharmony_ci start, mid, end, rdev->vce.fb_version); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci rdev->vce.fw_version = (start << 24) | (mid << 16) | (end << 8); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* we can only work with this fw version for now */ 13062306a36Sopenharmony_ci if ((rdev->vce.fw_version != ((40 << 24) | (2 << 16) | (2 << 8))) && 13162306a36Sopenharmony_ci (rdev->vce.fw_version != ((50 << 24) | (0 << 16) | (1 << 8))) && 13262306a36Sopenharmony_ci (rdev->vce.fw_version != ((50 << 24) | (1 << 16) | (2 << 8)))) 13362306a36Sopenharmony_ci return -EINVAL; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* allocate firmware, stack and heap BO */ 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (rdev->family < CHIP_BONAIRE) 13862306a36Sopenharmony_ci size = vce_v1_0_bo_size(rdev); 13962306a36Sopenharmony_ci else 14062306a36Sopenharmony_ci size = vce_v2_0_bo_size(rdev); 14162306a36Sopenharmony_ci r = radeon_bo_create(rdev, size, PAGE_SIZE, true, 14262306a36Sopenharmony_ci RADEON_GEM_DOMAIN_VRAM, 0, NULL, NULL, 14362306a36Sopenharmony_ci &rdev->vce.vcpu_bo); 14462306a36Sopenharmony_ci if (r) { 14562306a36Sopenharmony_ci dev_err(rdev->dev, "(%d) failed to allocate VCE bo\n", r); 14662306a36Sopenharmony_ci return r; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci r = radeon_bo_reserve(rdev->vce.vcpu_bo, false); 15062306a36Sopenharmony_ci if (r) { 15162306a36Sopenharmony_ci radeon_bo_unref(&rdev->vce.vcpu_bo); 15262306a36Sopenharmony_ci dev_err(rdev->dev, "(%d) failed to reserve VCE bo\n", r); 15362306a36Sopenharmony_ci return r; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci r = radeon_bo_pin(rdev->vce.vcpu_bo, RADEON_GEM_DOMAIN_VRAM, 15762306a36Sopenharmony_ci &rdev->vce.gpu_addr); 15862306a36Sopenharmony_ci radeon_bo_unreserve(rdev->vce.vcpu_bo); 15962306a36Sopenharmony_ci if (r) { 16062306a36Sopenharmony_ci radeon_bo_unref(&rdev->vce.vcpu_bo); 16162306a36Sopenharmony_ci dev_err(rdev->dev, "(%d) VCE bo pin failed\n", r); 16262306a36Sopenharmony_ci return r; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) { 16662306a36Sopenharmony_ci atomic_set(&rdev->vce.handles[i], 0); 16762306a36Sopenharmony_ci rdev->vce.filp[i] = NULL; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return 0; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci/** 17462306a36Sopenharmony_ci * radeon_vce_fini - free memory 17562306a36Sopenharmony_ci * 17662306a36Sopenharmony_ci * @rdev: radeon_device pointer 17762306a36Sopenharmony_ci * 17862306a36Sopenharmony_ci * Last step on VCE teardown, free firmware memory 17962306a36Sopenharmony_ci */ 18062306a36Sopenharmony_civoid radeon_vce_fini(struct radeon_device *rdev) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci if (rdev->vce.vcpu_bo == NULL) 18362306a36Sopenharmony_ci return; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci radeon_bo_unref(&rdev->vce.vcpu_bo); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci release_firmware(rdev->vce_fw); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci/** 19162306a36Sopenharmony_ci * radeon_vce_suspend - unpin VCE fw memory 19262306a36Sopenharmony_ci * 19362306a36Sopenharmony_ci * @rdev: radeon_device pointer 19462306a36Sopenharmony_ci * 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_ciint radeon_vce_suspend(struct radeon_device *rdev) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci int i; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (rdev->vce.vcpu_bo == NULL) 20162306a36Sopenharmony_ci return 0; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) 20462306a36Sopenharmony_ci if (atomic_read(&rdev->vce.handles[i])) 20562306a36Sopenharmony_ci break; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (i == RADEON_MAX_VCE_HANDLES) 20862306a36Sopenharmony_ci return 0; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* TODO: suspending running encoding sessions isn't supported */ 21162306a36Sopenharmony_ci return -EINVAL; 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci/** 21562306a36Sopenharmony_ci * radeon_vce_resume - pin VCE fw memory 21662306a36Sopenharmony_ci * 21762306a36Sopenharmony_ci * @rdev: radeon_device pointer 21862306a36Sopenharmony_ci * 21962306a36Sopenharmony_ci */ 22062306a36Sopenharmony_ciint radeon_vce_resume(struct radeon_device *rdev) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci void *cpu_addr; 22362306a36Sopenharmony_ci int r; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (rdev->vce.vcpu_bo == NULL) 22662306a36Sopenharmony_ci return -EINVAL; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci r = radeon_bo_reserve(rdev->vce.vcpu_bo, false); 22962306a36Sopenharmony_ci if (r) { 23062306a36Sopenharmony_ci dev_err(rdev->dev, "(%d) failed to reserve VCE bo\n", r); 23162306a36Sopenharmony_ci return r; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci r = radeon_bo_kmap(rdev->vce.vcpu_bo, &cpu_addr); 23562306a36Sopenharmony_ci if (r) { 23662306a36Sopenharmony_ci radeon_bo_unreserve(rdev->vce.vcpu_bo); 23762306a36Sopenharmony_ci dev_err(rdev->dev, "(%d) VCE map failed\n", r); 23862306a36Sopenharmony_ci return r; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci memset(cpu_addr, 0, radeon_bo_size(rdev->vce.vcpu_bo)); 24262306a36Sopenharmony_ci if (rdev->family < CHIP_BONAIRE) 24362306a36Sopenharmony_ci r = vce_v1_0_load_fw(rdev, cpu_addr); 24462306a36Sopenharmony_ci else 24562306a36Sopenharmony_ci memcpy(cpu_addr, rdev->vce_fw->data, rdev->vce_fw->size); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci radeon_bo_kunmap(rdev->vce.vcpu_bo); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci radeon_bo_unreserve(rdev->vce.vcpu_bo); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci return r; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci/** 25562306a36Sopenharmony_ci * radeon_vce_idle_work_handler - power off VCE 25662306a36Sopenharmony_ci * 25762306a36Sopenharmony_ci * @work: pointer to work structure 25862306a36Sopenharmony_ci * 25962306a36Sopenharmony_ci * power of VCE when it's not used any more 26062306a36Sopenharmony_ci */ 26162306a36Sopenharmony_cistatic void radeon_vce_idle_work_handler(struct work_struct *work) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci struct radeon_device *rdev = 26462306a36Sopenharmony_ci container_of(work, struct radeon_device, vce.idle_work.work); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if ((radeon_fence_count_emitted(rdev, TN_RING_TYPE_VCE1_INDEX) == 0) && 26762306a36Sopenharmony_ci (radeon_fence_count_emitted(rdev, TN_RING_TYPE_VCE2_INDEX) == 0)) { 26862306a36Sopenharmony_ci if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) { 26962306a36Sopenharmony_ci radeon_dpm_enable_vce(rdev, false); 27062306a36Sopenharmony_ci } else { 27162306a36Sopenharmony_ci radeon_set_vce_clocks(rdev, 0, 0); 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci } else { 27462306a36Sopenharmony_ci schedule_delayed_work(&rdev->vce.idle_work, 27562306a36Sopenharmony_ci msecs_to_jiffies(VCE_IDLE_TIMEOUT_MS)); 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci/** 28062306a36Sopenharmony_ci * radeon_vce_note_usage - power up VCE 28162306a36Sopenharmony_ci * 28262306a36Sopenharmony_ci * @rdev: radeon_device pointer 28362306a36Sopenharmony_ci * 28462306a36Sopenharmony_ci * Make sure VCE is powerd up when we want to use it 28562306a36Sopenharmony_ci */ 28662306a36Sopenharmony_civoid radeon_vce_note_usage(struct radeon_device *rdev) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci bool streams_changed = false; 28962306a36Sopenharmony_ci bool set_clocks = !cancel_delayed_work_sync(&rdev->vce.idle_work); 29062306a36Sopenharmony_ci set_clocks &= schedule_delayed_work(&rdev->vce.idle_work, 29162306a36Sopenharmony_ci msecs_to_jiffies(VCE_IDLE_TIMEOUT_MS)); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) { 29462306a36Sopenharmony_ci /* XXX figure out if the streams changed */ 29562306a36Sopenharmony_ci streams_changed = false; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (set_clocks || streams_changed) { 29962306a36Sopenharmony_ci if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) { 30062306a36Sopenharmony_ci radeon_dpm_enable_vce(rdev, true); 30162306a36Sopenharmony_ci } else { 30262306a36Sopenharmony_ci radeon_set_vce_clocks(rdev, 53300, 40000); 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci/** 30862306a36Sopenharmony_ci * radeon_vce_free_handles - free still open VCE handles 30962306a36Sopenharmony_ci * 31062306a36Sopenharmony_ci * @rdev: radeon_device pointer 31162306a36Sopenharmony_ci * @filp: drm file pointer 31262306a36Sopenharmony_ci * 31362306a36Sopenharmony_ci * Close all VCE handles still open by this file pointer 31462306a36Sopenharmony_ci */ 31562306a36Sopenharmony_civoid radeon_vce_free_handles(struct radeon_device *rdev, struct drm_file *filp) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci int i, r; 31862306a36Sopenharmony_ci for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) { 31962306a36Sopenharmony_ci uint32_t handle = atomic_read(&rdev->vce.handles[i]); 32062306a36Sopenharmony_ci if (!handle || rdev->vce.filp[i] != filp) 32162306a36Sopenharmony_ci continue; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci radeon_vce_note_usage(rdev); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci r = radeon_vce_get_destroy_msg(rdev, TN_RING_TYPE_VCE1_INDEX, 32662306a36Sopenharmony_ci handle, NULL); 32762306a36Sopenharmony_ci if (r) 32862306a36Sopenharmony_ci DRM_ERROR("Error destroying VCE handle (%d)!\n", r); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci rdev->vce.filp[i] = NULL; 33162306a36Sopenharmony_ci atomic_set(&rdev->vce.handles[i], 0); 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci/** 33662306a36Sopenharmony_ci * radeon_vce_get_create_msg - generate a VCE create msg 33762306a36Sopenharmony_ci * 33862306a36Sopenharmony_ci * @rdev: radeon_device pointer 33962306a36Sopenharmony_ci * @ring: ring we should submit the msg to 34062306a36Sopenharmony_ci * @handle: VCE session handle to use 34162306a36Sopenharmony_ci * @fence: optional fence to return 34262306a36Sopenharmony_ci * 34362306a36Sopenharmony_ci * Open up a stream for HW test 34462306a36Sopenharmony_ci */ 34562306a36Sopenharmony_ciint radeon_vce_get_create_msg(struct radeon_device *rdev, int ring, 34662306a36Sopenharmony_ci uint32_t handle, struct radeon_fence **fence) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci const unsigned ib_size_dw = 1024; 34962306a36Sopenharmony_ci struct radeon_ib ib; 35062306a36Sopenharmony_ci uint64_t dummy; 35162306a36Sopenharmony_ci int i, r; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci r = radeon_ib_get(rdev, ring, &ib, NULL, ib_size_dw * 4); 35462306a36Sopenharmony_ci if (r) { 35562306a36Sopenharmony_ci DRM_ERROR("radeon: failed to get ib (%d).\n", r); 35662306a36Sopenharmony_ci return r; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci dummy = ib.gpu_addr + 1024; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* stitch together an VCE create msg */ 36262306a36Sopenharmony_ci ib.length_dw = 0; 36362306a36Sopenharmony_ci ib.ptr[ib.length_dw++] = cpu_to_le32(0x0000000c); /* len */ 36462306a36Sopenharmony_ci ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000001); /* session cmd */ 36562306a36Sopenharmony_ci ib.ptr[ib.length_dw++] = cpu_to_le32(handle); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000030); /* len */ 36862306a36Sopenharmony_ci ib.ptr[ib.length_dw++] = cpu_to_le32(0x01000001); /* create cmd */ 36962306a36Sopenharmony_ci ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000000); 37062306a36Sopenharmony_ci ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000042); 37162306a36Sopenharmony_ci ib.ptr[ib.length_dw++] = cpu_to_le32(0x0000000a); 37262306a36Sopenharmony_ci ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000001); 37362306a36Sopenharmony_ci ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000080); 37462306a36Sopenharmony_ci ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000060); 37562306a36Sopenharmony_ci ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000100); 37662306a36Sopenharmony_ci ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000100); 37762306a36Sopenharmony_ci ib.ptr[ib.length_dw++] = cpu_to_le32(0x0000000c); 37862306a36Sopenharmony_ci ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000000); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000014); /* len */ 38162306a36Sopenharmony_ci ib.ptr[ib.length_dw++] = cpu_to_le32(0x05000005); /* feedback buffer */ 38262306a36Sopenharmony_ci ib.ptr[ib.length_dw++] = cpu_to_le32(upper_32_bits(dummy)); 38362306a36Sopenharmony_ci ib.ptr[ib.length_dw++] = cpu_to_le32(dummy); 38462306a36Sopenharmony_ci ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000001); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci for (i = ib.length_dw; i < ib_size_dw; ++i) 38762306a36Sopenharmony_ci ib.ptr[i] = cpu_to_le32(0x0); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci r = radeon_ib_schedule(rdev, &ib, NULL, false); 39062306a36Sopenharmony_ci if (r) 39162306a36Sopenharmony_ci DRM_ERROR("radeon: failed to schedule ib (%d).\n", r); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (fence) 39562306a36Sopenharmony_ci *fence = radeon_fence_ref(ib.fence); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci radeon_ib_free(rdev, &ib); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci return r; 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci/** 40362306a36Sopenharmony_ci * radeon_vce_get_destroy_msg - generate a VCE destroy msg 40462306a36Sopenharmony_ci * 40562306a36Sopenharmony_ci * @rdev: radeon_device pointer 40662306a36Sopenharmony_ci * @ring: ring we should submit the msg to 40762306a36Sopenharmony_ci * @handle: VCE session handle to use 40862306a36Sopenharmony_ci * @fence: optional fence to return 40962306a36Sopenharmony_ci * 41062306a36Sopenharmony_ci * Close up a stream for HW test or if userspace failed to do so 41162306a36Sopenharmony_ci */ 41262306a36Sopenharmony_ciint radeon_vce_get_destroy_msg(struct radeon_device *rdev, int ring, 41362306a36Sopenharmony_ci uint32_t handle, struct radeon_fence **fence) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci const unsigned ib_size_dw = 1024; 41662306a36Sopenharmony_ci struct radeon_ib ib; 41762306a36Sopenharmony_ci uint64_t dummy; 41862306a36Sopenharmony_ci int i, r; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci r = radeon_ib_get(rdev, ring, &ib, NULL, ib_size_dw * 4); 42162306a36Sopenharmony_ci if (r) { 42262306a36Sopenharmony_ci DRM_ERROR("radeon: failed to get ib (%d).\n", r); 42362306a36Sopenharmony_ci return r; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci dummy = ib.gpu_addr + 1024; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci /* stitch together an VCE destroy msg */ 42962306a36Sopenharmony_ci ib.length_dw = 0; 43062306a36Sopenharmony_ci ib.ptr[ib.length_dw++] = cpu_to_le32(0x0000000c); /* len */ 43162306a36Sopenharmony_ci ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000001); /* session cmd */ 43262306a36Sopenharmony_ci ib.ptr[ib.length_dw++] = cpu_to_le32(handle); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000014); /* len */ 43562306a36Sopenharmony_ci ib.ptr[ib.length_dw++] = cpu_to_le32(0x05000005); /* feedback buffer */ 43662306a36Sopenharmony_ci ib.ptr[ib.length_dw++] = cpu_to_le32(upper_32_bits(dummy)); 43762306a36Sopenharmony_ci ib.ptr[ib.length_dw++] = cpu_to_le32(dummy); 43862306a36Sopenharmony_ci ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000001); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000008); /* len */ 44162306a36Sopenharmony_ci ib.ptr[ib.length_dw++] = cpu_to_le32(0x02000001); /* destroy cmd */ 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci for (i = ib.length_dw; i < ib_size_dw; ++i) 44462306a36Sopenharmony_ci ib.ptr[i] = cpu_to_le32(0x0); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci r = radeon_ib_schedule(rdev, &ib, NULL, false); 44762306a36Sopenharmony_ci if (r) { 44862306a36Sopenharmony_ci DRM_ERROR("radeon: failed to schedule ib (%d).\n", r); 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci if (fence) 45262306a36Sopenharmony_ci *fence = radeon_fence_ref(ib.fence); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci radeon_ib_free(rdev, &ib); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci return r; 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci/** 46062306a36Sopenharmony_ci * radeon_vce_cs_reloc - command submission relocation 46162306a36Sopenharmony_ci * 46262306a36Sopenharmony_ci * @p: parser context 46362306a36Sopenharmony_ci * @lo: address of lower dword 46462306a36Sopenharmony_ci * @hi: address of higher dword 46562306a36Sopenharmony_ci * @size: size of checker for relocation buffer 46662306a36Sopenharmony_ci * 46762306a36Sopenharmony_ci * Patch relocation inside command stream with real buffer address 46862306a36Sopenharmony_ci */ 46962306a36Sopenharmony_ciint radeon_vce_cs_reloc(struct radeon_cs_parser *p, int lo, int hi, 47062306a36Sopenharmony_ci unsigned size) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci struct radeon_cs_chunk *relocs_chunk; 47362306a36Sopenharmony_ci struct radeon_bo_list *reloc; 47462306a36Sopenharmony_ci uint64_t start, end, offset; 47562306a36Sopenharmony_ci unsigned idx; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci relocs_chunk = p->chunk_relocs; 47862306a36Sopenharmony_ci offset = radeon_get_ib_value(p, lo); 47962306a36Sopenharmony_ci idx = radeon_get_ib_value(p, hi); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci if (idx >= relocs_chunk->length_dw) { 48262306a36Sopenharmony_ci DRM_ERROR("Relocs at %d after relocations chunk end %d !\n", 48362306a36Sopenharmony_ci idx, relocs_chunk->length_dw); 48462306a36Sopenharmony_ci return -EINVAL; 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci reloc = &p->relocs[(idx / 4)]; 48862306a36Sopenharmony_ci start = reloc->gpu_offset; 48962306a36Sopenharmony_ci end = start + radeon_bo_size(reloc->robj); 49062306a36Sopenharmony_ci start += offset; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci p->ib.ptr[lo] = start & 0xFFFFFFFF; 49362306a36Sopenharmony_ci p->ib.ptr[hi] = start >> 32; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci if (end <= start) { 49662306a36Sopenharmony_ci DRM_ERROR("invalid reloc offset %llX!\n", offset); 49762306a36Sopenharmony_ci return -EINVAL; 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci if ((end - start) < size) { 50062306a36Sopenharmony_ci DRM_ERROR("buffer to small (%d / %d)!\n", 50162306a36Sopenharmony_ci (unsigned)(end - start), size); 50262306a36Sopenharmony_ci return -EINVAL; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci return 0; 50662306a36Sopenharmony_ci} 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci/** 50962306a36Sopenharmony_ci * radeon_vce_validate_handle - validate stream handle 51062306a36Sopenharmony_ci * 51162306a36Sopenharmony_ci * @p: parser context 51262306a36Sopenharmony_ci * @handle: handle to validate 51362306a36Sopenharmony_ci * @allocated: allocated a new handle? 51462306a36Sopenharmony_ci * 51562306a36Sopenharmony_ci * Validates the handle and return the found session index or -EINVAL 51662306a36Sopenharmony_ci * we don't have another free session index. 51762306a36Sopenharmony_ci */ 51862306a36Sopenharmony_cistatic int radeon_vce_validate_handle(struct radeon_cs_parser *p, 51962306a36Sopenharmony_ci uint32_t handle, bool *allocated) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci unsigned i; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci *allocated = false; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci /* validate the handle */ 52662306a36Sopenharmony_ci for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) { 52762306a36Sopenharmony_ci if (atomic_read(&p->rdev->vce.handles[i]) == handle) { 52862306a36Sopenharmony_ci if (p->rdev->vce.filp[i] != p->filp) { 52962306a36Sopenharmony_ci DRM_ERROR("VCE handle collision detected!\n"); 53062306a36Sopenharmony_ci return -EINVAL; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci return i; 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci /* handle not found try to alloc a new one */ 53762306a36Sopenharmony_ci for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) { 53862306a36Sopenharmony_ci if (!atomic_cmpxchg(&p->rdev->vce.handles[i], 0, handle)) { 53962306a36Sopenharmony_ci p->rdev->vce.filp[i] = p->filp; 54062306a36Sopenharmony_ci p->rdev->vce.img_size[i] = 0; 54162306a36Sopenharmony_ci *allocated = true; 54262306a36Sopenharmony_ci return i; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci DRM_ERROR("No more free VCE handles!\n"); 54762306a36Sopenharmony_ci return -EINVAL; 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci/** 55162306a36Sopenharmony_ci * radeon_vce_cs_parse - parse and validate the command stream 55262306a36Sopenharmony_ci * 55362306a36Sopenharmony_ci * @p: parser context 55462306a36Sopenharmony_ci * 55562306a36Sopenharmony_ci */ 55662306a36Sopenharmony_ciint radeon_vce_cs_parse(struct radeon_cs_parser *p) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci int session_idx = -1; 55962306a36Sopenharmony_ci bool destroyed = false, created = false, allocated = false; 56062306a36Sopenharmony_ci uint32_t tmp, handle = 0; 56162306a36Sopenharmony_ci uint32_t *size = &tmp; 56262306a36Sopenharmony_ci int i, r = 0; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci while (p->idx < p->chunk_ib->length_dw) { 56562306a36Sopenharmony_ci uint32_t len = radeon_get_ib_value(p, p->idx); 56662306a36Sopenharmony_ci uint32_t cmd = radeon_get_ib_value(p, p->idx + 1); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci if ((len < 8) || (len & 3)) { 56962306a36Sopenharmony_ci DRM_ERROR("invalid VCE command length (%d)!\n", len); 57062306a36Sopenharmony_ci r = -EINVAL; 57162306a36Sopenharmony_ci goto out; 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci if (destroyed) { 57562306a36Sopenharmony_ci DRM_ERROR("No other command allowed after destroy!\n"); 57662306a36Sopenharmony_ci r = -EINVAL; 57762306a36Sopenharmony_ci goto out; 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci switch (cmd) { 58162306a36Sopenharmony_ci case 0x00000001: // session 58262306a36Sopenharmony_ci handle = radeon_get_ib_value(p, p->idx + 2); 58362306a36Sopenharmony_ci session_idx = radeon_vce_validate_handle(p, handle, 58462306a36Sopenharmony_ci &allocated); 58562306a36Sopenharmony_ci if (session_idx < 0) 58662306a36Sopenharmony_ci return session_idx; 58762306a36Sopenharmony_ci size = &p->rdev->vce.img_size[session_idx]; 58862306a36Sopenharmony_ci break; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci case 0x00000002: // task info 59162306a36Sopenharmony_ci break; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci case 0x01000001: // create 59462306a36Sopenharmony_ci created = true; 59562306a36Sopenharmony_ci if (!allocated) { 59662306a36Sopenharmony_ci DRM_ERROR("Handle already in use!\n"); 59762306a36Sopenharmony_ci r = -EINVAL; 59862306a36Sopenharmony_ci goto out; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci *size = radeon_get_ib_value(p, p->idx + 8) * 60262306a36Sopenharmony_ci radeon_get_ib_value(p, p->idx + 10) * 60362306a36Sopenharmony_ci 8 * 3 / 2; 60462306a36Sopenharmony_ci break; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci case 0x04000001: // config extension 60762306a36Sopenharmony_ci case 0x04000002: // pic control 60862306a36Sopenharmony_ci case 0x04000005: // rate control 60962306a36Sopenharmony_ci case 0x04000007: // motion estimation 61062306a36Sopenharmony_ci case 0x04000008: // rdo 61162306a36Sopenharmony_ci case 0x04000009: // vui 61262306a36Sopenharmony_ci break; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci case 0x03000001: // encode 61562306a36Sopenharmony_ci r = radeon_vce_cs_reloc(p, p->idx + 10, p->idx + 9, 61662306a36Sopenharmony_ci *size); 61762306a36Sopenharmony_ci if (r) 61862306a36Sopenharmony_ci goto out; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci r = radeon_vce_cs_reloc(p, p->idx + 12, p->idx + 11, 62162306a36Sopenharmony_ci *size / 3); 62262306a36Sopenharmony_ci if (r) 62362306a36Sopenharmony_ci goto out; 62462306a36Sopenharmony_ci break; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci case 0x02000001: // destroy 62762306a36Sopenharmony_ci destroyed = true; 62862306a36Sopenharmony_ci break; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci case 0x05000001: // context buffer 63162306a36Sopenharmony_ci r = radeon_vce_cs_reloc(p, p->idx + 3, p->idx + 2, 63262306a36Sopenharmony_ci *size * 2); 63362306a36Sopenharmony_ci if (r) 63462306a36Sopenharmony_ci goto out; 63562306a36Sopenharmony_ci break; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci case 0x05000004: // video bitstream buffer 63862306a36Sopenharmony_ci tmp = radeon_get_ib_value(p, p->idx + 4); 63962306a36Sopenharmony_ci r = radeon_vce_cs_reloc(p, p->idx + 3, p->idx + 2, 64062306a36Sopenharmony_ci tmp); 64162306a36Sopenharmony_ci if (r) 64262306a36Sopenharmony_ci goto out; 64362306a36Sopenharmony_ci break; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci case 0x05000005: // feedback buffer 64662306a36Sopenharmony_ci r = radeon_vce_cs_reloc(p, p->idx + 3, p->idx + 2, 64762306a36Sopenharmony_ci 4096); 64862306a36Sopenharmony_ci if (r) 64962306a36Sopenharmony_ci goto out; 65062306a36Sopenharmony_ci break; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci default: 65362306a36Sopenharmony_ci DRM_ERROR("invalid VCE command (0x%x)!\n", cmd); 65462306a36Sopenharmony_ci r = -EINVAL; 65562306a36Sopenharmony_ci goto out; 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci if (session_idx == -1) { 65962306a36Sopenharmony_ci DRM_ERROR("no session command at start of IB\n"); 66062306a36Sopenharmony_ci r = -EINVAL; 66162306a36Sopenharmony_ci goto out; 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci p->idx += len / 4; 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci if (allocated && !created) { 66862306a36Sopenharmony_ci DRM_ERROR("New session without create command!\n"); 66962306a36Sopenharmony_ci r = -ENOENT; 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ciout: 67362306a36Sopenharmony_ci if ((!r && destroyed) || (r && allocated)) { 67462306a36Sopenharmony_ci /* 67562306a36Sopenharmony_ci * IB contains a destroy msg or we have allocated an 67662306a36Sopenharmony_ci * handle and got an error, anyway free the handle 67762306a36Sopenharmony_ci */ 67862306a36Sopenharmony_ci for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) 67962306a36Sopenharmony_ci atomic_cmpxchg(&p->rdev->vce.handles[i], handle, 0); 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci return r; 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci/** 68662306a36Sopenharmony_ci * radeon_vce_semaphore_emit - emit a semaphore command 68762306a36Sopenharmony_ci * 68862306a36Sopenharmony_ci * @rdev: radeon_device pointer 68962306a36Sopenharmony_ci * @ring: engine to use 69062306a36Sopenharmony_ci * @semaphore: address of semaphore 69162306a36Sopenharmony_ci * @emit_wait: true=emit wait, false=emit signal 69262306a36Sopenharmony_ci * 69362306a36Sopenharmony_ci */ 69462306a36Sopenharmony_cibool radeon_vce_semaphore_emit(struct radeon_device *rdev, 69562306a36Sopenharmony_ci struct radeon_ring *ring, 69662306a36Sopenharmony_ci struct radeon_semaphore *semaphore, 69762306a36Sopenharmony_ci bool emit_wait) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci uint64_t addr = semaphore->gpu_addr; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci radeon_ring_write(ring, cpu_to_le32(VCE_CMD_SEMAPHORE)); 70262306a36Sopenharmony_ci radeon_ring_write(ring, cpu_to_le32((addr >> 3) & 0x000FFFFF)); 70362306a36Sopenharmony_ci radeon_ring_write(ring, cpu_to_le32((addr >> 23) & 0x000FFFFF)); 70462306a36Sopenharmony_ci radeon_ring_write(ring, cpu_to_le32(0x01003000 | (emit_wait ? 1 : 0))); 70562306a36Sopenharmony_ci if (!emit_wait) 70662306a36Sopenharmony_ci radeon_ring_write(ring, cpu_to_le32(VCE_CMD_END)); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci return true; 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci/** 71262306a36Sopenharmony_ci * radeon_vce_ib_execute - execute indirect buffer 71362306a36Sopenharmony_ci * 71462306a36Sopenharmony_ci * @rdev: radeon_device pointer 71562306a36Sopenharmony_ci * @ib: the IB to execute 71662306a36Sopenharmony_ci * 71762306a36Sopenharmony_ci */ 71862306a36Sopenharmony_civoid radeon_vce_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib) 71962306a36Sopenharmony_ci{ 72062306a36Sopenharmony_ci struct radeon_ring *ring = &rdev->ring[ib->ring]; 72162306a36Sopenharmony_ci radeon_ring_write(ring, cpu_to_le32(VCE_CMD_IB)); 72262306a36Sopenharmony_ci radeon_ring_write(ring, cpu_to_le32(ib->gpu_addr)); 72362306a36Sopenharmony_ci radeon_ring_write(ring, cpu_to_le32(upper_32_bits(ib->gpu_addr))); 72462306a36Sopenharmony_ci radeon_ring_write(ring, cpu_to_le32(ib->length_dw)); 72562306a36Sopenharmony_ci} 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci/** 72862306a36Sopenharmony_ci * radeon_vce_fence_emit - add a fence command to the ring 72962306a36Sopenharmony_ci * 73062306a36Sopenharmony_ci * @rdev: radeon_device pointer 73162306a36Sopenharmony_ci * @fence: the fence 73262306a36Sopenharmony_ci * 73362306a36Sopenharmony_ci */ 73462306a36Sopenharmony_civoid radeon_vce_fence_emit(struct radeon_device *rdev, 73562306a36Sopenharmony_ci struct radeon_fence *fence) 73662306a36Sopenharmony_ci{ 73762306a36Sopenharmony_ci struct radeon_ring *ring = &rdev->ring[fence->ring]; 73862306a36Sopenharmony_ci uint64_t addr = rdev->fence_drv[fence->ring].gpu_addr; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci radeon_ring_write(ring, cpu_to_le32(VCE_CMD_FENCE)); 74162306a36Sopenharmony_ci radeon_ring_write(ring, cpu_to_le32(addr)); 74262306a36Sopenharmony_ci radeon_ring_write(ring, cpu_to_le32(upper_32_bits(addr))); 74362306a36Sopenharmony_ci radeon_ring_write(ring, cpu_to_le32(fence->seq)); 74462306a36Sopenharmony_ci radeon_ring_write(ring, cpu_to_le32(VCE_CMD_TRAP)); 74562306a36Sopenharmony_ci radeon_ring_write(ring, cpu_to_le32(VCE_CMD_END)); 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci/** 74962306a36Sopenharmony_ci * radeon_vce_ring_test - test if VCE ring is working 75062306a36Sopenharmony_ci * 75162306a36Sopenharmony_ci * @rdev: radeon_device pointer 75262306a36Sopenharmony_ci * @ring: the engine to test on 75362306a36Sopenharmony_ci * 75462306a36Sopenharmony_ci */ 75562306a36Sopenharmony_ciint radeon_vce_ring_test(struct radeon_device *rdev, struct radeon_ring *ring) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci uint32_t rptr = vce_v1_0_get_rptr(rdev, ring); 75862306a36Sopenharmony_ci unsigned i; 75962306a36Sopenharmony_ci int r; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci r = radeon_ring_lock(rdev, ring, 16); 76262306a36Sopenharmony_ci if (r) { 76362306a36Sopenharmony_ci DRM_ERROR("radeon: vce failed to lock ring %d (%d).\n", 76462306a36Sopenharmony_ci ring->idx, r); 76562306a36Sopenharmony_ci return r; 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci radeon_ring_write(ring, cpu_to_le32(VCE_CMD_END)); 76862306a36Sopenharmony_ci radeon_ring_unlock_commit(rdev, ring, false); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci for (i = 0; i < rdev->usec_timeout; i++) { 77162306a36Sopenharmony_ci if (vce_v1_0_get_rptr(rdev, ring) != rptr) 77262306a36Sopenharmony_ci break; 77362306a36Sopenharmony_ci udelay(1); 77462306a36Sopenharmony_ci } 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci if (i < rdev->usec_timeout) { 77762306a36Sopenharmony_ci DRM_INFO("ring test on %d succeeded in %d usecs\n", 77862306a36Sopenharmony_ci ring->idx, i); 77962306a36Sopenharmony_ci } else { 78062306a36Sopenharmony_ci DRM_ERROR("radeon: ring %d test failed\n", 78162306a36Sopenharmony_ci ring->idx); 78262306a36Sopenharmony_ci r = -ETIMEDOUT; 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci return r; 78662306a36Sopenharmony_ci} 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci/** 78962306a36Sopenharmony_ci * radeon_vce_ib_test - test if VCE IBs are working 79062306a36Sopenharmony_ci * 79162306a36Sopenharmony_ci * @rdev: radeon_device pointer 79262306a36Sopenharmony_ci * @ring: the engine to test on 79362306a36Sopenharmony_ci * 79462306a36Sopenharmony_ci */ 79562306a36Sopenharmony_ciint radeon_vce_ib_test(struct radeon_device *rdev, struct radeon_ring *ring) 79662306a36Sopenharmony_ci{ 79762306a36Sopenharmony_ci struct radeon_fence *fence = NULL; 79862306a36Sopenharmony_ci int r; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci r = radeon_vce_get_create_msg(rdev, ring->idx, 1, NULL); 80162306a36Sopenharmony_ci if (r) { 80262306a36Sopenharmony_ci DRM_ERROR("radeon: failed to get create msg (%d).\n", r); 80362306a36Sopenharmony_ci goto error; 80462306a36Sopenharmony_ci } 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci r = radeon_vce_get_destroy_msg(rdev, ring->idx, 1, &fence); 80762306a36Sopenharmony_ci if (r) { 80862306a36Sopenharmony_ci DRM_ERROR("radeon: failed to get destroy ib (%d).\n", r); 80962306a36Sopenharmony_ci goto error; 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci r = radeon_fence_wait_timeout(fence, false, usecs_to_jiffies( 81362306a36Sopenharmony_ci RADEON_USEC_IB_TEST_TIMEOUT)); 81462306a36Sopenharmony_ci if (r < 0) { 81562306a36Sopenharmony_ci DRM_ERROR("radeon: fence wait failed (%d).\n", r); 81662306a36Sopenharmony_ci } else if (r == 0) { 81762306a36Sopenharmony_ci DRM_ERROR("radeon: fence wait timed out.\n"); 81862306a36Sopenharmony_ci r = -ETIMEDOUT; 81962306a36Sopenharmony_ci } else { 82062306a36Sopenharmony_ci DRM_INFO("ib test on ring %d succeeded\n", ring->idx); 82162306a36Sopenharmony_ci r = 0; 82262306a36Sopenharmony_ci } 82362306a36Sopenharmony_cierror: 82462306a36Sopenharmony_ci radeon_fence_unref(&fence); 82562306a36Sopenharmony_ci return r; 82662306a36Sopenharmony_ci} 827