1d93f7937SChristian König /* 2d93f7937SChristian König * Copyright 2013 Advanced Micro Devices, Inc. 3d93f7937SChristian König * All Rights Reserved. 4d93f7937SChristian König * 5d93f7937SChristian König * Permission is hereby granted, free of charge, to any person obtaining a 6d93f7937SChristian König * copy of this software and associated documentation files (the 7d93f7937SChristian König * "Software"), to deal in the Software without restriction, including 8d93f7937SChristian König * without limitation the rights to use, copy, modify, merge, publish, 9d93f7937SChristian König * distribute, sub license, and/or sell copies of the Software, and to 10d93f7937SChristian König * permit persons to whom the Software is furnished to do so, subject to 11d93f7937SChristian König * the following conditions: 12d93f7937SChristian König * 13d93f7937SChristian König * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14d93f7937SChristian König * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15d93f7937SChristian König * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 16d93f7937SChristian König * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 17d93f7937SChristian König * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18d93f7937SChristian König * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 19d93f7937SChristian König * USE OR OTHER DEALINGS IN THE SOFTWARE. 20d93f7937SChristian König * 21d93f7937SChristian König * The above copyright notice and this permission notice (including the 22d93f7937SChristian König * next paragraph) shall be included in all copies or substantial portions 23d93f7937SChristian König * of the Software. 24d93f7937SChristian König * 25d93f7937SChristian König * Authors: Christian König <christian.koenig@amd.com> 26d93f7937SChristian König */ 27d93f7937SChristian König 28d93f7937SChristian König #include <linux/firmware.h> 29d93f7937SChristian König #include <linux/module.h> 30d93f7937SChristian König #include <drm/drmP.h> 31d93f7937SChristian König #include <drm/drm.h> 32d93f7937SChristian König 33d93f7937SChristian König #include "radeon.h" 34d93f7937SChristian König #include "radeon_asic.h" 35d93f7937SChristian König #include "sid.h" 36d93f7937SChristian König 3703afe6f6SAlex Deucher /* 1 second timeout */ 3803afe6f6SAlex Deucher #define VCE_IDLE_TIMEOUT_MS 1000 3903afe6f6SAlex Deucher 40d93f7937SChristian König /* Firmware Names */ 41d93f7937SChristian König #define FIRMWARE_BONAIRE "radeon/BONAIRE_vce.bin" 42d93f7937SChristian König 43d93f7937SChristian König MODULE_FIRMWARE(FIRMWARE_BONAIRE); 44d93f7937SChristian König 4503afe6f6SAlex Deucher static void radeon_vce_idle_work_handler(struct work_struct *work); 4603afe6f6SAlex Deucher 47d93f7937SChristian König /** 48d93f7937SChristian König * radeon_vce_init - allocate memory, load vce firmware 49d93f7937SChristian König * 50d93f7937SChristian König * @rdev: radeon_device pointer 51d93f7937SChristian König * 52d93f7937SChristian König * First step to get VCE online, allocate memory and load the firmware 53d93f7937SChristian König */ 54d93f7937SChristian König int radeon_vce_init(struct radeon_device *rdev) 55d93f7937SChristian König { 5698ccc291SChristian König static const char *fw_version = "[ATI LIB=VCEFW,"; 5798ccc291SChristian König static const char *fb_version = "[ATI LIB=VCEFWSTATS,"; 5898ccc291SChristian König unsigned long size; 5998ccc291SChristian König const char *fw_name, *c; 6098ccc291SChristian König uint8_t start, mid, end; 61d93f7937SChristian König int i, r; 62d93f7937SChristian König 6303afe6f6SAlex Deucher INIT_DELAYED_WORK(&rdev->vce.idle_work, radeon_vce_idle_work_handler); 6403afe6f6SAlex Deucher 65d93f7937SChristian König switch (rdev->family) { 66d93f7937SChristian König case CHIP_BONAIRE: 67d93f7937SChristian König case CHIP_KAVERI: 68d93f7937SChristian König case CHIP_KABINI: 69d71c48f6SAlex Deucher case CHIP_HAWAII: 70428bedddSLeo Liu case CHIP_MULLINS: 71d93f7937SChristian König fw_name = FIRMWARE_BONAIRE; 72d93f7937SChristian König break; 73d93f7937SChristian König 74d93f7937SChristian König default: 75d93f7937SChristian König return -EINVAL; 76d93f7937SChristian König } 77d93f7937SChristian König 78d93f7937SChristian König r = request_firmware(&rdev->vce_fw, fw_name, rdev->dev); 79d93f7937SChristian König if (r) { 80d93f7937SChristian König dev_err(rdev->dev, "radeon_vce: Can't load firmware \"%s\"\n", 81d93f7937SChristian König fw_name); 82d93f7937SChristian König return r; 83d93f7937SChristian König } 84d93f7937SChristian König 8598ccc291SChristian König /* search for firmware version */ 8698ccc291SChristian König 8798ccc291SChristian König size = rdev->vce_fw->size - strlen(fw_version) - 9; 8898ccc291SChristian König c = rdev->vce_fw->data; 8998ccc291SChristian König for (;size > 0; --size, ++c) 9098ccc291SChristian König if (strncmp(c, fw_version, strlen(fw_version)) == 0) 9198ccc291SChristian König break; 9298ccc291SChristian König 9398ccc291SChristian König if (size == 0) 9498ccc291SChristian König return -EINVAL; 9598ccc291SChristian König 9698ccc291SChristian König c += strlen(fw_version); 9798ccc291SChristian König if (sscanf(c, "%2hhd.%2hhd.%2hhd]", &start, &mid, &end) != 3) 9898ccc291SChristian König return -EINVAL; 9998ccc291SChristian König 10098ccc291SChristian König /* search for feedback version */ 10198ccc291SChristian König 10298ccc291SChristian König size = rdev->vce_fw->size - strlen(fb_version) - 3; 10398ccc291SChristian König c = rdev->vce_fw->data; 10498ccc291SChristian König for (;size > 0; --size, ++c) 10598ccc291SChristian König if (strncmp(c, fb_version, strlen(fb_version)) == 0) 10698ccc291SChristian König break; 10798ccc291SChristian König 10898ccc291SChristian König if (size == 0) 10998ccc291SChristian König return -EINVAL; 11098ccc291SChristian König 11198ccc291SChristian König c += strlen(fb_version); 11298ccc291SChristian König if (sscanf(c, "%2u]", &rdev->vce.fb_version) != 1) 11398ccc291SChristian König return -EINVAL; 11498ccc291SChristian König 11598ccc291SChristian König DRM_INFO("Found VCE firmware/feedback version %hhd.%hhd.%hhd / %d!\n", 11698ccc291SChristian König start, mid, end, rdev->vce.fb_version); 11798ccc291SChristian König 11898ccc291SChristian König rdev->vce.fw_version = (start << 24) | (mid << 16) | (end << 8); 11998ccc291SChristian König 12098ccc291SChristian König /* we can only work with this fw version for now */ 12198ccc291SChristian König if (rdev->vce.fw_version != ((40 << 24) | (2 << 16) | (2 << 8))) 12298ccc291SChristian König return -EINVAL; 12398ccc291SChristian König 124b03b4e4bSChristian König /* allocate firmware, stack and heap BO */ 12598ccc291SChristian König 12698ccc291SChristian König size = RADEON_GPU_PAGE_ALIGN(rdev->vce_fw->size) + 127d93f7937SChristian König RADEON_VCE_STACK_SIZE + RADEON_VCE_HEAP_SIZE; 12898ccc291SChristian König r = radeon_bo_create(rdev, size, PAGE_SIZE, true, 129831b6966SMaarten Lankhorst RADEON_GEM_DOMAIN_VRAM, 0, NULL, NULL, 130831b6966SMaarten Lankhorst &rdev->vce.vcpu_bo); 131d93f7937SChristian König if (r) { 132d93f7937SChristian König dev_err(rdev->dev, "(%d) failed to allocate VCE bo\n", r); 133d93f7937SChristian König return r; 134d93f7937SChristian König } 135d93f7937SChristian König 136b03b4e4bSChristian König r = radeon_bo_reserve(rdev->vce.vcpu_bo, false); 137b03b4e4bSChristian König if (r) { 138b03b4e4bSChristian König radeon_bo_unref(&rdev->vce.vcpu_bo); 139b03b4e4bSChristian König dev_err(rdev->dev, "(%d) failed to reserve VCE bo\n", r); 140d93f7937SChristian König return r; 141b03b4e4bSChristian König } 142d93f7937SChristian König 143b03b4e4bSChristian König r = radeon_bo_pin(rdev->vce.vcpu_bo, RADEON_GEM_DOMAIN_VRAM, 144b03b4e4bSChristian König &rdev->vce.gpu_addr); 145b03b4e4bSChristian König radeon_bo_unreserve(rdev->vce.vcpu_bo); 146b03b4e4bSChristian König if (r) { 147b03b4e4bSChristian König radeon_bo_unref(&rdev->vce.vcpu_bo); 148b03b4e4bSChristian König dev_err(rdev->dev, "(%d) VCE bo pin failed\n", r); 149d93f7937SChristian König return r; 150b03b4e4bSChristian König } 151d93f7937SChristian König 152d93f7937SChristian König for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) { 153d93f7937SChristian König atomic_set(&rdev->vce.handles[i], 0); 154d93f7937SChristian König rdev->vce.filp[i] = NULL; 155d93f7937SChristian König } 156d93f7937SChristian König 157d93f7937SChristian König return 0; 158d93f7937SChristian König } 159d93f7937SChristian König 160d93f7937SChristian König /** 161d93f7937SChristian König * radeon_vce_fini - free memory 162d93f7937SChristian König * 163d93f7937SChristian König * @rdev: radeon_device pointer 164d93f7937SChristian König * 165d93f7937SChristian König * Last step on VCE teardown, free firmware memory 166d93f7937SChristian König */ 167d93f7937SChristian König void radeon_vce_fini(struct radeon_device *rdev) 168d93f7937SChristian König { 169b03b4e4bSChristian König if (rdev->vce.vcpu_bo == NULL) 170b03b4e4bSChristian König return; 171b03b4e4bSChristian König 172d93f7937SChristian König radeon_bo_unref(&rdev->vce.vcpu_bo); 173b03b4e4bSChristian König 174b03b4e4bSChristian König release_firmware(rdev->vce_fw); 175d93f7937SChristian König } 176d93f7937SChristian König 177d93f7937SChristian König /** 178d93f7937SChristian König * radeon_vce_suspend - unpin VCE fw memory 179d93f7937SChristian König * 180d93f7937SChristian König * @rdev: radeon_device pointer 181d93f7937SChristian König * 182d93f7937SChristian König */ 183d93f7937SChristian König int radeon_vce_suspend(struct radeon_device *rdev) 184d93f7937SChristian König { 185b03b4e4bSChristian König int i; 186d93f7937SChristian König 187d93f7937SChristian König if (rdev->vce.vcpu_bo == NULL) 188d93f7937SChristian König return 0; 189d93f7937SChristian König 190b03b4e4bSChristian König for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) 191b03b4e4bSChristian König if (atomic_read(&rdev->vce.handles[i])) 192b03b4e4bSChristian König break; 193b03b4e4bSChristian König 194b03b4e4bSChristian König if (i == RADEON_MAX_VCE_HANDLES) 195b03b4e4bSChristian König return 0; 196b03b4e4bSChristian König 197b03b4e4bSChristian König /* TODO: suspending running encoding sessions isn't supported */ 198b03b4e4bSChristian König return -EINVAL; 199d93f7937SChristian König } 200d93f7937SChristian König 201d93f7937SChristian König /** 202d93f7937SChristian König * radeon_vce_resume - pin VCE fw memory 203d93f7937SChristian König * 204d93f7937SChristian König * @rdev: radeon_device pointer 205d93f7937SChristian König * 206d93f7937SChristian König */ 207d93f7937SChristian König int radeon_vce_resume(struct radeon_device *rdev) 208d93f7937SChristian König { 209b03b4e4bSChristian König void *cpu_addr; 210d93f7937SChristian König int r; 211d93f7937SChristian König 212d93f7937SChristian König if (rdev->vce.vcpu_bo == NULL) 213d93f7937SChristian König return -EINVAL; 214d93f7937SChristian König 215d93f7937SChristian König r = radeon_bo_reserve(rdev->vce.vcpu_bo, false); 216d93f7937SChristian König if (r) { 217d93f7937SChristian König dev_err(rdev->dev, "(%d) failed to reserve VCE bo\n", r); 218d93f7937SChristian König return r; 219d93f7937SChristian König } 220d93f7937SChristian König 221b03b4e4bSChristian König r = radeon_bo_kmap(rdev->vce.vcpu_bo, &cpu_addr); 222d93f7937SChristian König if (r) { 223d93f7937SChristian König radeon_bo_unreserve(rdev->vce.vcpu_bo); 224d93f7937SChristian König dev_err(rdev->dev, "(%d) VCE map failed\n", r); 225d93f7937SChristian König return r; 226d93f7937SChristian König } 227d93f7937SChristian König 228b03b4e4bSChristian König memcpy(cpu_addr, rdev->vce_fw->data, rdev->vce_fw->size); 229b03b4e4bSChristian König 230b03b4e4bSChristian König radeon_bo_kunmap(rdev->vce.vcpu_bo); 231b03b4e4bSChristian König 232d93f7937SChristian König radeon_bo_unreserve(rdev->vce.vcpu_bo); 233d93f7937SChristian König 234d93f7937SChristian König return 0; 235d93f7937SChristian König } 236d93f7937SChristian König 237d93f7937SChristian König /** 23803afe6f6SAlex Deucher * radeon_vce_idle_work_handler - power off VCE 23903afe6f6SAlex Deucher * 24003afe6f6SAlex Deucher * @work: pointer to work structure 24103afe6f6SAlex Deucher * 24203afe6f6SAlex Deucher * power of VCE when it's not used any more 24303afe6f6SAlex Deucher */ 24403afe6f6SAlex Deucher static void radeon_vce_idle_work_handler(struct work_struct *work) 24503afe6f6SAlex Deucher { 24603afe6f6SAlex Deucher struct radeon_device *rdev = 24703afe6f6SAlex Deucher container_of(work, struct radeon_device, vce.idle_work.work); 24803afe6f6SAlex Deucher 24903afe6f6SAlex Deucher if ((radeon_fence_count_emitted(rdev, TN_RING_TYPE_VCE1_INDEX) == 0) && 25003afe6f6SAlex Deucher (radeon_fence_count_emitted(rdev, TN_RING_TYPE_VCE2_INDEX) == 0)) { 25103afe6f6SAlex Deucher if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) { 25203afe6f6SAlex Deucher radeon_dpm_enable_vce(rdev, false); 25303afe6f6SAlex Deucher } else { 25403afe6f6SAlex Deucher radeon_set_vce_clocks(rdev, 0, 0); 25503afe6f6SAlex Deucher } 25603afe6f6SAlex Deucher } else { 25703afe6f6SAlex Deucher schedule_delayed_work(&rdev->vce.idle_work, 25803afe6f6SAlex Deucher msecs_to_jiffies(VCE_IDLE_TIMEOUT_MS)); 25903afe6f6SAlex Deucher } 26003afe6f6SAlex Deucher } 26103afe6f6SAlex Deucher 26203afe6f6SAlex Deucher /** 26303afe6f6SAlex Deucher * radeon_vce_note_usage - power up VCE 26403afe6f6SAlex Deucher * 26503afe6f6SAlex Deucher * @rdev: radeon_device pointer 26603afe6f6SAlex Deucher * 26703afe6f6SAlex Deucher * Make sure VCE is powerd up when we want to use it 26803afe6f6SAlex Deucher */ 26903afe6f6SAlex Deucher void radeon_vce_note_usage(struct radeon_device *rdev) 27003afe6f6SAlex Deucher { 27103afe6f6SAlex Deucher bool streams_changed = false; 27203afe6f6SAlex Deucher bool set_clocks = !cancel_delayed_work_sync(&rdev->vce.idle_work); 27303afe6f6SAlex Deucher set_clocks &= schedule_delayed_work(&rdev->vce.idle_work, 27403afe6f6SAlex Deucher msecs_to_jiffies(VCE_IDLE_TIMEOUT_MS)); 27503afe6f6SAlex Deucher 27603afe6f6SAlex Deucher if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) { 27703afe6f6SAlex Deucher /* XXX figure out if the streams changed */ 27803afe6f6SAlex Deucher streams_changed = false; 27903afe6f6SAlex Deucher } 28003afe6f6SAlex Deucher 28103afe6f6SAlex Deucher if (set_clocks || streams_changed) { 28203afe6f6SAlex Deucher if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) { 28303afe6f6SAlex Deucher radeon_dpm_enable_vce(rdev, true); 28403afe6f6SAlex Deucher } else { 28503afe6f6SAlex Deucher radeon_set_vce_clocks(rdev, 53300, 40000); 28603afe6f6SAlex Deucher } 28703afe6f6SAlex Deucher } 28803afe6f6SAlex Deucher } 28903afe6f6SAlex Deucher 29003afe6f6SAlex Deucher /** 291d93f7937SChristian König * radeon_vce_free_handles - free still open VCE handles 292d93f7937SChristian König * 293d93f7937SChristian König * @rdev: radeon_device pointer 294d93f7937SChristian König * @filp: drm file pointer 295d93f7937SChristian König * 296d93f7937SChristian König * Close all VCE handles still open by this file pointer 297d93f7937SChristian König */ 298d93f7937SChristian König void radeon_vce_free_handles(struct radeon_device *rdev, struct drm_file *filp) 299d93f7937SChristian König { 300d93f7937SChristian König int i, r; 301d93f7937SChristian König for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) { 302d93f7937SChristian König uint32_t handle = atomic_read(&rdev->vce.handles[i]); 303d93f7937SChristian König if (!handle || rdev->vce.filp[i] != filp) 304d93f7937SChristian König continue; 305d93f7937SChristian König 30603afe6f6SAlex Deucher radeon_vce_note_usage(rdev); 30703afe6f6SAlex Deucher 308d93f7937SChristian König r = radeon_vce_get_destroy_msg(rdev, TN_RING_TYPE_VCE1_INDEX, 309d93f7937SChristian König handle, NULL); 310d93f7937SChristian König if (r) 311d93f7937SChristian König DRM_ERROR("Error destroying VCE handle (%d)!\n", r); 312d93f7937SChristian König 313d93f7937SChristian König rdev->vce.filp[i] = NULL; 314d93f7937SChristian König atomic_set(&rdev->vce.handles[i], 0); 315d93f7937SChristian König } 316d93f7937SChristian König } 317d93f7937SChristian König 318d93f7937SChristian König /** 319d93f7937SChristian König * radeon_vce_get_create_msg - generate a VCE create msg 320d93f7937SChristian König * 321d93f7937SChristian König * @rdev: radeon_device pointer 322d93f7937SChristian König * @ring: ring we should submit the msg to 323d93f7937SChristian König * @handle: VCE session handle to use 324d93f7937SChristian König * @fence: optional fence to return 325d93f7937SChristian König * 326d93f7937SChristian König * Open up a stream for HW test 327d93f7937SChristian König */ 328d93f7937SChristian König int radeon_vce_get_create_msg(struct radeon_device *rdev, int ring, 329d93f7937SChristian König uint32_t handle, struct radeon_fence **fence) 330d93f7937SChristian König { 331d93f7937SChristian König const unsigned ib_size_dw = 1024; 332d93f7937SChristian König struct radeon_ib ib; 333d93f7937SChristian König uint64_t dummy; 334d93f7937SChristian König int i, r; 335d93f7937SChristian König 336d93f7937SChristian König r = radeon_ib_get(rdev, ring, &ib, NULL, ib_size_dw * 4); 337d93f7937SChristian König if (r) { 338d93f7937SChristian König DRM_ERROR("radeon: failed to get ib (%d).\n", r); 339d93f7937SChristian König return r; 340d93f7937SChristian König } 341d93f7937SChristian König 342d93f7937SChristian König dummy = ib.gpu_addr + 1024; 343d93f7937SChristian König 344d93f7937SChristian König /* stitch together an VCE create msg */ 345d93f7937SChristian König ib.length_dw = 0; 346d93f7937SChristian König ib.ptr[ib.length_dw++] = 0x0000000c; /* len */ 347d93f7937SChristian König ib.ptr[ib.length_dw++] = 0x00000001; /* session cmd */ 348d93f7937SChristian König ib.ptr[ib.length_dw++] = handle; 349d93f7937SChristian König 350d93f7937SChristian König ib.ptr[ib.length_dw++] = 0x00000030; /* len */ 351d93f7937SChristian König ib.ptr[ib.length_dw++] = 0x01000001; /* create cmd */ 352d93f7937SChristian König ib.ptr[ib.length_dw++] = 0x00000000; 353d93f7937SChristian König ib.ptr[ib.length_dw++] = 0x00000042; 354d93f7937SChristian König ib.ptr[ib.length_dw++] = 0x0000000a; 355d93f7937SChristian König ib.ptr[ib.length_dw++] = 0x00000001; 356d93f7937SChristian König ib.ptr[ib.length_dw++] = 0x00000080; 357d93f7937SChristian König ib.ptr[ib.length_dw++] = 0x00000060; 358d93f7937SChristian König ib.ptr[ib.length_dw++] = 0x00000100; 359d93f7937SChristian König ib.ptr[ib.length_dw++] = 0x00000100; 360d93f7937SChristian König ib.ptr[ib.length_dw++] = 0x0000000c; 361d93f7937SChristian König ib.ptr[ib.length_dw++] = 0x00000000; 362d93f7937SChristian König 363d93f7937SChristian König ib.ptr[ib.length_dw++] = 0x00000014; /* len */ 364d93f7937SChristian König ib.ptr[ib.length_dw++] = 0x05000005; /* feedback buffer */ 365d93f7937SChristian König ib.ptr[ib.length_dw++] = upper_32_bits(dummy); 366d93f7937SChristian König ib.ptr[ib.length_dw++] = dummy; 367d93f7937SChristian König ib.ptr[ib.length_dw++] = 0x00000001; 368d93f7937SChristian König 369d93f7937SChristian König for (i = ib.length_dw; i < ib_size_dw; ++i) 370d93f7937SChristian König ib.ptr[i] = 0x0; 371d93f7937SChristian König 3721538a9e0SMichel Dänzer r = radeon_ib_schedule(rdev, &ib, NULL, false); 373d93f7937SChristian König if (r) { 374d93f7937SChristian König DRM_ERROR("radeon: failed to schedule ib (%d).\n", r); 375d93f7937SChristian König } 376d93f7937SChristian König 377d93f7937SChristian König if (fence) 378d93f7937SChristian König *fence = radeon_fence_ref(ib.fence); 379d93f7937SChristian König 380d93f7937SChristian König radeon_ib_free(rdev, &ib); 381d93f7937SChristian König 382d93f7937SChristian König return r; 383d93f7937SChristian König } 384d93f7937SChristian König 385d93f7937SChristian König /** 386d93f7937SChristian König * radeon_vce_get_destroy_msg - generate a VCE destroy msg 387d93f7937SChristian König * 388d93f7937SChristian König * @rdev: radeon_device pointer 389d93f7937SChristian König * @ring: ring we should submit the msg to 390d93f7937SChristian König * @handle: VCE session handle to use 391d93f7937SChristian König * @fence: optional fence to return 392d93f7937SChristian König * 393d93f7937SChristian König * Close up a stream for HW test or if userspace failed to do so 394d93f7937SChristian König */ 395d93f7937SChristian König int radeon_vce_get_destroy_msg(struct radeon_device *rdev, int ring, 396d93f7937SChristian König uint32_t handle, struct radeon_fence **fence) 397d93f7937SChristian König { 398d93f7937SChristian König const unsigned ib_size_dw = 1024; 399d93f7937SChristian König struct radeon_ib ib; 400d93f7937SChristian König uint64_t dummy; 401d93f7937SChristian König int i, r; 402d93f7937SChristian König 403d93f7937SChristian König r = radeon_ib_get(rdev, ring, &ib, NULL, ib_size_dw * 4); 404d93f7937SChristian König if (r) { 405d93f7937SChristian König DRM_ERROR("radeon: failed to get ib (%d).\n", r); 406d93f7937SChristian König return r; 407d93f7937SChristian König } 408d93f7937SChristian König 409d93f7937SChristian König dummy = ib.gpu_addr + 1024; 410d93f7937SChristian König 411d93f7937SChristian König /* stitch together an VCE destroy msg */ 412d93f7937SChristian König ib.length_dw = 0; 413d93f7937SChristian König ib.ptr[ib.length_dw++] = 0x0000000c; /* len */ 414d93f7937SChristian König ib.ptr[ib.length_dw++] = 0x00000001; /* session cmd */ 415d93f7937SChristian König ib.ptr[ib.length_dw++] = handle; 416d93f7937SChristian König 417d93f7937SChristian König ib.ptr[ib.length_dw++] = 0x00000014; /* len */ 418d93f7937SChristian König ib.ptr[ib.length_dw++] = 0x05000005; /* feedback buffer */ 419d93f7937SChristian König ib.ptr[ib.length_dw++] = upper_32_bits(dummy); 420d93f7937SChristian König ib.ptr[ib.length_dw++] = dummy; 421d93f7937SChristian König ib.ptr[ib.length_dw++] = 0x00000001; 422d93f7937SChristian König 423d93f7937SChristian König ib.ptr[ib.length_dw++] = 0x00000008; /* len */ 424d93f7937SChristian König ib.ptr[ib.length_dw++] = 0x02000001; /* destroy cmd */ 425d93f7937SChristian König 426d93f7937SChristian König for (i = ib.length_dw; i < ib_size_dw; ++i) 427d93f7937SChristian König ib.ptr[i] = 0x0; 428d93f7937SChristian König 4291538a9e0SMichel Dänzer r = radeon_ib_schedule(rdev, &ib, NULL, false); 430d93f7937SChristian König if (r) { 431d93f7937SChristian König DRM_ERROR("radeon: failed to schedule ib (%d).\n", r); 432d93f7937SChristian König } 433d93f7937SChristian König 434d93f7937SChristian König if (fence) 435d93f7937SChristian König *fence = radeon_fence_ref(ib.fence); 436d93f7937SChristian König 437d93f7937SChristian König radeon_ib_free(rdev, &ib); 438d93f7937SChristian König 439d93f7937SChristian König return r; 440d93f7937SChristian König } 441d93f7937SChristian König 442d93f7937SChristian König /** 443d93f7937SChristian König * radeon_vce_cs_reloc - command submission relocation 444d93f7937SChristian König * 445d93f7937SChristian König * @p: parser context 446d93f7937SChristian König * @lo: address of lower dword 447d93f7937SChristian König * @hi: address of higher dword 4482fc5703aSLeo Liu * @size: size of checker for relocation buffer 449d93f7937SChristian König * 450d93f7937SChristian König * Patch relocation inside command stream with real buffer address 451d93f7937SChristian König */ 4522fc5703aSLeo Liu int radeon_vce_cs_reloc(struct radeon_cs_parser *p, int lo, int hi, 4532fc5703aSLeo Liu unsigned size) 454d93f7937SChristian König { 455d93f7937SChristian König struct radeon_cs_chunk *relocs_chunk; 456*1d0c0942SChristian König struct radeon_bo_list *reloc; 4572fc5703aSLeo Liu uint64_t start, end, offset; 458d93f7937SChristian König unsigned idx; 459d93f7937SChristian König 460d93f7937SChristian König relocs_chunk = &p->chunks[p->chunk_relocs_idx]; 461d93f7937SChristian König offset = radeon_get_ib_value(p, lo); 462d93f7937SChristian König idx = radeon_get_ib_value(p, hi); 463d93f7937SChristian König 464d93f7937SChristian König if (idx >= relocs_chunk->length_dw) { 465d93f7937SChristian König DRM_ERROR("Relocs at %d after relocations chunk end %d !\n", 466d93f7937SChristian König idx, relocs_chunk->length_dw); 467d93f7937SChristian König return -EINVAL; 468d93f7937SChristian König } 469d93f7937SChristian König 4702fc5703aSLeo Liu reloc = p->relocs_ptr[(idx / 4)]; 4712fc5703aSLeo Liu start = reloc->gpu_offset; 4722fc5703aSLeo Liu end = start + radeon_bo_size(reloc->robj); 4732fc5703aSLeo Liu start += offset; 474d93f7937SChristian König 4752fc5703aSLeo Liu p->ib.ptr[lo] = start & 0xFFFFFFFF; 4762fc5703aSLeo Liu p->ib.ptr[hi] = start >> 32; 4772fc5703aSLeo Liu 4782fc5703aSLeo Liu if (end <= start) { 4792fc5703aSLeo Liu DRM_ERROR("invalid reloc offset %llX!\n", offset); 4802fc5703aSLeo Liu return -EINVAL; 4812fc5703aSLeo Liu } 4822fc5703aSLeo Liu if ((end - start) < size) { 4832fc5703aSLeo Liu DRM_ERROR("buffer to small (%d / %d)!\n", 4842fc5703aSLeo Liu (unsigned)(end - start), size); 4852fc5703aSLeo Liu return -EINVAL; 4862fc5703aSLeo Liu } 487d93f7937SChristian König 488d93f7937SChristian König return 0; 489d93f7937SChristian König } 490d93f7937SChristian König 491d93f7937SChristian König /** 4922fc5703aSLeo Liu * radeon_vce_validate_handle - validate stream handle 4932fc5703aSLeo Liu * 4942fc5703aSLeo Liu * @p: parser context 4952fc5703aSLeo Liu * @handle: handle to validate 4962fc5703aSLeo Liu * 4972fc5703aSLeo Liu * Validates the handle and return the found session index or -EINVAL 4982fc5703aSLeo Liu * we we don't have another free session index. 4992fc5703aSLeo Liu */ 5002fc5703aSLeo Liu int radeon_vce_validate_handle(struct radeon_cs_parser *p, uint32_t handle) 5012fc5703aSLeo Liu { 5022fc5703aSLeo Liu unsigned i; 5032fc5703aSLeo Liu 5042fc5703aSLeo Liu /* validate the handle */ 5052fc5703aSLeo Liu for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) { 5062fc5703aSLeo Liu if (atomic_read(&p->rdev->vce.handles[i]) == handle) 5072fc5703aSLeo Liu return i; 5082fc5703aSLeo Liu } 5092fc5703aSLeo Liu 5102fc5703aSLeo Liu /* handle not found try to alloc a new one */ 5112fc5703aSLeo Liu for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) { 5122fc5703aSLeo Liu if (!atomic_cmpxchg(&p->rdev->vce.handles[i], 0, handle)) { 5132fc5703aSLeo Liu p->rdev->vce.filp[i] = p->filp; 5142fc5703aSLeo Liu p->rdev->vce.img_size[i] = 0; 5152fc5703aSLeo Liu return i; 5162fc5703aSLeo Liu } 5172fc5703aSLeo Liu } 5182fc5703aSLeo Liu 5192fc5703aSLeo Liu DRM_ERROR("No more free VCE handles!\n"); 5202fc5703aSLeo Liu return -EINVAL; 5212fc5703aSLeo Liu } 5222fc5703aSLeo Liu 5232fc5703aSLeo Liu /** 524d93f7937SChristian König * radeon_vce_cs_parse - parse and validate the command stream 525d93f7937SChristian König * 526d93f7937SChristian König * @p: parser context 527d93f7937SChristian König * 528d93f7937SChristian König */ 529d93f7937SChristian König int radeon_vce_cs_parse(struct radeon_cs_parser *p) 530d93f7937SChristian König { 5312fc5703aSLeo Liu int session_idx = -1; 5322fc5703aSLeo Liu bool destroyed = false; 5332fc5703aSLeo Liu uint32_t tmp, handle = 0; 5342fc5703aSLeo Liu uint32_t *size = &tmp; 535d93f7937SChristian König int i, r; 536d93f7937SChristian König 537d93f7937SChristian König while (p->idx < p->chunks[p->chunk_ib_idx].length_dw) { 538d93f7937SChristian König uint32_t len = radeon_get_ib_value(p, p->idx); 539d93f7937SChristian König uint32_t cmd = radeon_get_ib_value(p, p->idx + 1); 540d93f7937SChristian König 541d93f7937SChristian König if ((len < 8) || (len & 3)) { 542d93f7937SChristian König DRM_ERROR("invalid VCE command length (%d)!\n", len); 543d93f7937SChristian König return -EINVAL; 544d93f7937SChristian König } 545d93f7937SChristian König 5462fc5703aSLeo Liu if (destroyed) { 5472fc5703aSLeo Liu DRM_ERROR("No other command allowed after destroy!\n"); 5482fc5703aSLeo Liu return -EINVAL; 5492fc5703aSLeo Liu } 5502fc5703aSLeo Liu 551d93f7937SChristian König switch (cmd) { 552d93f7937SChristian König case 0x00000001: // session 553d93f7937SChristian König handle = radeon_get_ib_value(p, p->idx + 2); 5542fc5703aSLeo Liu session_idx = radeon_vce_validate_handle(p, handle); 5552fc5703aSLeo Liu if (session_idx < 0) 5562fc5703aSLeo Liu return session_idx; 5572fc5703aSLeo Liu size = &p->rdev->vce.img_size[session_idx]; 558d93f7937SChristian König break; 559d93f7937SChristian König 560d93f7937SChristian König case 0x00000002: // task info 5612fc5703aSLeo Liu break; 5622fc5703aSLeo Liu 563d93f7937SChristian König case 0x01000001: // create 5642fc5703aSLeo Liu *size = radeon_get_ib_value(p, p->idx + 8) * 5652fc5703aSLeo Liu radeon_get_ib_value(p, p->idx + 10) * 5662fc5703aSLeo Liu 8 * 3 / 2; 5672fc5703aSLeo Liu break; 5682fc5703aSLeo Liu 569d93f7937SChristian König case 0x04000001: // config extension 570d93f7937SChristian König case 0x04000002: // pic control 571d93f7937SChristian König case 0x04000005: // rate control 572d93f7937SChristian König case 0x04000007: // motion estimation 573d93f7937SChristian König case 0x04000008: // rdo 574d93f7937SChristian König break; 575d93f7937SChristian König 576d93f7937SChristian König case 0x03000001: // encode 5772fc5703aSLeo Liu r = radeon_vce_cs_reloc(p, p->idx + 10, p->idx + 9, 5782fc5703aSLeo Liu *size); 579d93f7937SChristian König if (r) 580d93f7937SChristian König return r; 581d93f7937SChristian König 5822fc5703aSLeo Liu r = radeon_vce_cs_reloc(p, p->idx + 12, p->idx + 11, 5832fc5703aSLeo Liu *size / 3); 584d93f7937SChristian König if (r) 585d93f7937SChristian König return r; 586d93f7937SChristian König break; 587d93f7937SChristian König 588d93f7937SChristian König case 0x02000001: // destroy 5892fc5703aSLeo Liu destroyed = true; 590d93f7937SChristian König break; 591d93f7937SChristian König 592d93f7937SChristian König case 0x05000001: // context buffer 5932fc5703aSLeo Liu r = radeon_vce_cs_reloc(p, p->idx + 3, p->idx + 2, 5942fc5703aSLeo Liu *size * 2); 5952fc5703aSLeo Liu if (r) 5962fc5703aSLeo Liu return r; 5972fc5703aSLeo Liu break; 5982fc5703aSLeo Liu 599d93f7937SChristian König case 0x05000004: // video bitstream buffer 6002fc5703aSLeo Liu tmp = radeon_get_ib_value(p, p->idx + 4); 6012fc5703aSLeo Liu r = radeon_vce_cs_reloc(p, p->idx + 3, p->idx + 2, 6022fc5703aSLeo Liu tmp); 6032fc5703aSLeo Liu if (r) 6042fc5703aSLeo Liu return r; 6052fc5703aSLeo Liu break; 6062fc5703aSLeo Liu 607d93f7937SChristian König case 0x05000005: // feedback buffer 6082fc5703aSLeo Liu r = radeon_vce_cs_reloc(p, p->idx + 3, p->idx + 2, 6092fc5703aSLeo Liu 4096); 610d93f7937SChristian König if (r) 611d93f7937SChristian König return r; 612d93f7937SChristian König break; 613d93f7937SChristian König 614d93f7937SChristian König default: 615d93f7937SChristian König DRM_ERROR("invalid VCE command (0x%x)!\n", cmd); 616d93f7937SChristian König return -EINVAL; 617d93f7937SChristian König } 618d93f7937SChristian König 6192fc5703aSLeo Liu if (session_idx == -1) { 6202fc5703aSLeo Liu DRM_ERROR("no session command at start of IB\n"); 6212fc5703aSLeo Liu return -EINVAL; 6222fc5703aSLeo Liu } 6232fc5703aSLeo Liu 624d93f7937SChristian König p->idx += len / 4; 625d93f7937SChristian König } 626d93f7937SChristian König 6272fc5703aSLeo Liu if (destroyed) { 628d93f7937SChristian König /* IB contains a destroy msg, free the handle */ 629d93f7937SChristian König for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) 630d93f7937SChristian König atomic_cmpxchg(&p->rdev->vce.handles[i], handle, 0); 6312fc5703aSLeo Liu } 632d93f7937SChristian König 633d93f7937SChristian König return 0; 634d93f7937SChristian König } 635d93f7937SChristian König 636d93f7937SChristian König /** 637d93f7937SChristian König * radeon_vce_semaphore_emit - emit a semaphore command 638d93f7937SChristian König * 639d93f7937SChristian König * @rdev: radeon_device pointer 640d93f7937SChristian König * @ring: engine to use 641d93f7937SChristian König * @semaphore: address of semaphore 642d93f7937SChristian König * @emit_wait: true=emit wait, false=emit signal 643d93f7937SChristian König * 644d93f7937SChristian König */ 645d93f7937SChristian König bool radeon_vce_semaphore_emit(struct radeon_device *rdev, 646d93f7937SChristian König struct radeon_ring *ring, 647d93f7937SChristian König struct radeon_semaphore *semaphore, 648d93f7937SChristian König bool emit_wait) 649d93f7937SChristian König { 650d93f7937SChristian König uint64_t addr = semaphore->gpu_addr; 651d93f7937SChristian König 652d93f7937SChristian König radeon_ring_write(ring, VCE_CMD_SEMAPHORE); 653d93f7937SChristian König radeon_ring_write(ring, (addr >> 3) & 0x000FFFFF); 654d93f7937SChristian König radeon_ring_write(ring, (addr >> 23) & 0x000FFFFF); 655d93f7937SChristian König radeon_ring_write(ring, 0x01003000 | (emit_wait ? 1 : 0)); 656d93f7937SChristian König if (!emit_wait) 657d93f7937SChristian König radeon_ring_write(ring, VCE_CMD_END); 658d93f7937SChristian König 659d93f7937SChristian König return true; 660d93f7937SChristian König } 661d93f7937SChristian König 662d93f7937SChristian König /** 663d93f7937SChristian König * radeon_vce_ib_execute - execute indirect buffer 664d93f7937SChristian König * 665d93f7937SChristian König * @rdev: radeon_device pointer 666d93f7937SChristian König * @ib: the IB to execute 667d93f7937SChristian König * 668d93f7937SChristian König */ 669d93f7937SChristian König void radeon_vce_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib) 670d93f7937SChristian König { 671d93f7937SChristian König struct radeon_ring *ring = &rdev->ring[ib->ring]; 672d93f7937SChristian König radeon_ring_write(ring, VCE_CMD_IB); 673d93f7937SChristian König radeon_ring_write(ring, ib->gpu_addr); 674d93f7937SChristian König radeon_ring_write(ring, upper_32_bits(ib->gpu_addr)); 675d93f7937SChristian König radeon_ring_write(ring, ib->length_dw); 676d93f7937SChristian König } 677d93f7937SChristian König 678d93f7937SChristian König /** 679d93f7937SChristian König * radeon_vce_fence_emit - add a fence command to the ring 680d93f7937SChristian König * 681d93f7937SChristian König * @rdev: radeon_device pointer 682d93f7937SChristian König * @fence: the fence 683d93f7937SChristian König * 684d93f7937SChristian König */ 685d93f7937SChristian König void radeon_vce_fence_emit(struct radeon_device *rdev, 686d93f7937SChristian König struct radeon_fence *fence) 687d93f7937SChristian König { 688d93f7937SChristian König struct radeon_ring *ring = &rdev->ring[fence->ring]; 689681941c1SChristoph Jaeger uint64_t addr = rdev->fence_drv[fence->ring].gpu_addr; 690d93f7937SChristian König 691d93f7937SChristian König radeon_ring_write(ring, VCE_CMD_FENCE); 692d93f7937SChristian König radeon_ring_write(ring, addr); 693d93f7937SChristian König radeon_ring_write(ring, upper_32_bits(addr)); 694d93f7937SChristian König radeon_ring_write(ring, fence->seq); 695d93f7937SChristian König radeon_ring_write(ring, VCE_CMD_TRAP); 696d93f7937SChristian König radeon_ring_write(ring, VCE_CMD_END); 697d93f7937SChristian König } 698d93f7937SChristian König 699d93f7937SChristian König /** 700d93f7937SChristian König * radeon_vce_ring_test - test if VCE ring is working 701d93f7937SChristian König * 702d93f7937SChristian König * @rdev: radeon_device pointer 703d93f7937SChristian König * @ring: the engine to test on 704d93f7937SChristian König * 705d93f7937SChristian König */ 706d93f7937SChristian König int radeon_vce_ring_test(struct radeon_device *rdev, struct radeon_ring *ring) 707d93f7937SChristian König { 708d93f7937SChristian König uint32_t rptr = vce_v1_0_get_rptr(rdev, ring); 709d93f7937SChristian König unsigned i; 710d93f7937SChristian König int r; 711d93f7937SChristian König 712d93f7937SChristian König r = radeon_ring_lock(rdev, ring, 16); 713d93f7937SChristian König if (r) { 714d93f7937SChristian König DRM_ERROR("radeon: vce failed to lock ring %d (%d).\n", 715d93f7937SChristian König ring->idx, r); 716d93f7937SChristian König return r; 717d93f7937SChristian König } 718d93f7937SChristian König radeon_ring_write(ring, VCE_CMD_END); 7191538a9e0SMichel Dänzer radeon_ring_unlock_commit(rdev, ring, false); 720d93f7937SChristian König 721d93f7937SChristian König for (i = 0; i < rdev->usec_timeout; i++) { 722d93f7937SChristian König if (vce_v1_0_get_rptr(rdev, ring) != rptr) 723d93f7937SChristian König break; 724d93f7937SChristian König DRM_UDELAY(1); 725d93f7937SChristian König } 726d93f7937SChristian König 727d93f7937SChristian König if (i < rdev->usec_timeout) { 728d93f7937SChristian König DRM_INFO("ring test on %d succeeded in %d usecs\n", 729d93f7937SChristian König ring->idx, i); 730d93f7937SChristian König } else { 731d93f7937SChristian König DRM_ERROR("radeon: ring %d test failed\n", 732d93f7937SChristian König ring->idx); 733d93f7937SChristian König r = -ETIMEDOUT; 734d93f7937SChristian König } 735d93f7937SChristian König 736d93f7937SChristian König return r; 737d93f7937SChristian König } 738d93f7937SChristian König 739d93f7937SChristian König /** 740d93f7937SChristian König * radeon_vce_ib_test - test if VCE IBs are working 741d93f7937SChristian König * 742d93f7937SChristian König * @rdev: radeon_device pointer 743d93f7937SChristian König * @ring: the engine to test on 744d93f7937SChristian König * 745d93f7937SChristian König */ 746d93f7937SChristian König int radeon_vce_ib_test(struct radeon_device *rdev, struct radeon_ring *ring) 747d93f7937SChristian König { 748d93f7937SChristian König struct radeon_fence *fence = NULL; 749d93f7937SChristian König int r; 750d93f7937SChristian König 751d93f7937SChristian König r = radeon_vce_get_create_msg(rdev, ring->idx, 1, NULL); 752d93f7937SChristian König if (r) { 753d93f7937SChristian König DRM_ERROR("radeon: failed to get create msg (%d).\n", r); 754d93f7937SChristian König goto error; 755d93f7937SChristian König } 756d93f7937SChristian König 757d93f7937SChristian König r = radeon_vce_get_destroy_msg(rdev, ring->idx, 1, &fence); 758d93f7937SChristian König if (r) { 759d93f7937SChristian König DRM_ERROR("radeon: failed to get destroy ib (%d).\n", r); 760d93f7937SChristian König goto error; 761d93f7937SChristian König } 762d93f7937SChristian König 763d93f7937SChristian König r = radeon_fence_wait(fence, false); 764d93f7937SChristian König if (r) { 765d93f7937SChristian König DRM_ERROR("radeon: fence wait failed (%d).\n", r); 766d93f7937SChristian König } else { 767d93f7937SChristian König DRM_INFO("ib test on ring %d succeeded\n", ring->idx); 768d93f7937SChristian König } 769d93f7937SChristian König error: 770d93f7937SChristian König radeon_fence_unref(&fence); 771d93f7937SChristian König return r; 772d93f7937SChristian König } 773