1f2ba57b5SChristian König /* 2f2ba57b5SChristian König * Copyright 2011 Advanced Micro Devices, Inc. 3f2ba57b5SChristian König * All Rights Reserved. 4f2ba57b5SChristian König * 5f2ba57b5SChristian König * Permission is hereby granted, free of charge, to any person obtaining a 6f2ba57b5SChristian König * copy of this software and associated documentation files (the 7f2ba57b5SChristian König * "Software"), to deal in the Software without restriction, including 8f2ba57b5SChristian König * without limitation the rights to use, copy, modify, merge, publish, 9f2ba57b5SChristian König * distribute, sub license, and/or sell copies of the Software, and to 10f2ba57b5SChristian König * permit persons to whom the Software is furnished to do so, subject to 11f2ba57b5SChristian König * the following conditions: 12f2ba57b5SChristian König * 13f2ba57b5SChristian König * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14f2ba57b5SChristian König * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15f2ba57b5SChristian König * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 16f2ba57b5SChristian König * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 17f2ba57b5SChristian König * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18f2ba57b5SChristian König * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 19f2ba57b5SChristian König * USE OR OTHER DEALINGS IN THE SOFTWARE. 20f2ba57b5SChristian König * 21f2ba57b5SChristian König * The above copyright notice and this permission notice (including the 22f2ba57b5SChristian König * next paragraph) shall be included in all copies or substantial portions 23f2ba57b5SChristian König * of the Software. 24f2ba57b5SChristian König * 25f2ba57b5SChristian König */ 26f2ba57b5SChristian König /* 27f2ba57b5SChristian König * Authors: 28f2ba57b5SChristian König * Christian König <deathsimple@vodafone.de> 29f2ba57b5SChristian König */ 30f2ba57b5SChristian König 31f2ba57b5SChristian König #include <linux/firmware.h> 32f2ba57b5SChristian König #include <linux/module.h> 33f2ba57b5SChristian König #include <drm/drmP.h> 34f2ba57b5SChristian König #include <drm/drm.h> 35f2ba57b5SChristian König 36f2ba57b5SChristian König #include "radeon.h" 37f2ba57b5SChristian König #include "r600d.h" 38f2ba57b5SChristian König 3955b51c88SChristian König /* 1 second timeout */ 4055b51c88SChristian König #define UVD_IDLE_TIMEOUT_MS 1000 4155b51c88SChristian König 42f2ba57b5SChristian König /* Firmware Names */ 43f2ba57b5SChristian König #define FIRMWARE_RV710 "radeon/RV710_uvd.bin" 44f2ba57b5SChristian König #define FIRMWARE_CYPRESS "radeon/CYPRESS_uvd.bin" 45f2ba57b5SChristian König #define FIRMWARE_SUMO "radeon/SUMO_uvd.bin" 46f2ba57b5SChristian König #define FIRMWARE_TAHITI "radeon/TAHITI_uvd.bin" 4787167bb1SChristian König #define FIRMWARE_BONAIRE "radeon/BONAIRE_uvd.bin" 48f2ba57b5SChristian König 49f2ba57b5SChristian König MODULE_FIRMWARE(FIRMWARE_RV710); 50f2ba57b5SChristian König MODULE_FIRMWARE(FIRMWARE_CYPRESS); 51f2ba57b5SChristian König MODULE_FIRMWARE(FIRMWARE_SUMO); 52f2ba57b5SChristian König MODULE_FIRMWARE(FIRMWARE_TAHITI); 5387167bb1SChristian König MODULE_FIRMWARE(FIRMWARE_BONAIRE); 54f2ba57b5SChristian König 5555b51c88SChristian König static void radeon_uvd_idle_work_handler(struct work_struct *work); 5655b51c88SChristian König 57f2ba57b5SChristian König int radeon_uvd_init(struct radeon_device *rdev) 58f2ba57b5SChristian König { 59f2ba57b5SChristian König unsigned long bo_size; 60f2ba57b5SChristian König const char *fw_name; 61f2ba57b5SChristian König int i, r; 62f2ba57b5SChristian König 6355b51c88SChristian König INIT_DELAYED_WORK(&rdev->uvd.idle_work, radeon_uvd_idle_work_handler); 6455b51c88SChristian König 65f2ba57b5SChristian König switch (rdev->family) { 66f2ba57b5SChristian König case CHIP_RV710: 67f2ba57b5SChristian König case CHIP_RV730: 68f2ba57b5SChristian König case CHIP_RV740: 69f2ba57b5SChristian König fw_name = FIRMWARE_RV710; 70f2ba57b5SChristian König break; 71f2ba57b5SChristian König 72f2ba57b5SChristian König case CHIP_CYPRESS: 73f2ba57b5SChristian König case CHIP_HEMLOCK: 74f2ba57b5SChristian König case CHIP_JUNIPER: 75f2ba57b5SChristian König case CHIP_REDWOOD: 76f2ba57b5SChristian König case CHIP_CEDAR: 77f2ba57b5SChristian König fw_name = FIRMWARE_CYPRESS; 78f2ba57b5SChristian König break; 79f2ba57b5SChristian König 80f2ba57b5SChristian König case CHIP_SUMO: 81f2ba57b5SChristian König case CHIP_SUMO2: 82f2ba57b5SChristian König case CHIP_PALM: 83f2ba57b5SChristian König case CHIP_CAYMAN: 84f2ba57b5SChristian König case CHIP_BARTS: 85f2ba57b5SChristian König case CHIP_TURKS: 86f2ba57b5SChristian König case CHIP_CAICOS: 87f2ba57b5SChristian König fw_name = FIRMWARE_SUMO; 88f2ba57b5SChristian König break; 89f2ba57b5SChristian König 90f2ba57b5SChristian König case CHIP_TAHITI: 91f2ba57b5SChristian König case CHIP_VERDE: 92f2ba57b5SChristian König case CHIP_PITCAIRN: 93f2ba57b5SChristian König case CHIP_ARUBA: 945d029339SAlex Deucher case CHIP_OLAND: 95f2ba57b5SChristian König fw_name = FIRMWARE_TAHITI; 96f2ba57b5SChristian König break; 97f2ba57b5SChristian König 9887167bb1SChristian König case CHIP_BONAIRE: 9987167bb1SChristian König case CHIP_KABINI: 10087167bb1SChristian König case CHIP_KAVERI: 1014256331aSAlex Deucher case CHIP_HAWAII: 10287167bb1SChristian König fw_name = FIRMWARE_BONAIRE; 10387167bb1SChristian König break; 10487167bb1SChristian König 105f2ba57b5SChristian König default: 106f2ba57b5SChristian König return -EINVAL; 107f2ba57b5SChristian König } 108f2ba57b5SChristian König 1094ad9c1c7SChristian König r = request_firmware(&rdev->uvd_fw, fw_name, rdev->dev); 110f2ba57b5SChristian König if (r) { 111f2ba57b5SChristian König dev_err(rdev->dev, "radeon_uvd: Can't load firmware \"%s\"\n", 112f2ba57b5SChristian König fw_name); 113f2ba57b5SChristian König return r; 114f2ba57b5SChristian König } 115f2ba57b5SChristian König 1164ad9c1c7SChristian König bo_size = RADEON_GPU_PAGE_ALIGN(rdev->uvd_fw->size + 8) + 117f2ba57b5SChristian König RADEON_UVD_STACK_SIZE + RADEON_UVD_HEAP_SIZE; 118f2ba57b5SChristian König r = radeon_bo_create(rdev, bo_size, PAGE_SIZE, true, 119f2ba57b5SChristian König RADEON_GEM_DOMAIN_VRAM, NULL, &rdev->uvd.vcpu_bo); 120f2ba57b5SChristian König if (r) { 121f2ba57b5SChristian König dev_err(rdev->dev, "(%d) failed to allocate UVD bo\n", r); 122f2ba57b5SChristian König return r; 123f2ba57b5SChristian König } 124f2ba57b5SChristian König 125f2ba57b5SChristian König r = radeon_bo_reserve(rdev->uvd.vcpu_bo, false); 126f2ba57b5SChristian König if (r) { 127f2ba57b5SChristian König radeon_bo_unref(&rdev->uvd.vcpu_bo); 128f2ba57b5SChristian König dev_err(rdev->dev, "(%d) failed to reserve UVD bo\n", r); 129f2ba57b5SChristian König return r; 130f2ba57b5SChristian König } 131f2ba57b5SChristian König 132f2ba57b5SChristian König r = radeon_bo_pin(rdev->uvd.vcpu_bo, RADEON_GEM_DOMAIN_VRAM, 133f2ba57b5SChristian König &rdev->uvd.gpu_addr); 134f2ba57b5SChristian König if (r) { 135f2ba57b5SChristian König radeon_bo_unreserve(rdev->uvd.vcpu_bo); 136f2ba57b5SChristian König radeon_bo_unref(&rdev->uvd.vcpu_bo); 137f2ba57b5SChristian König dev_err(rdev->dev, "(%d) UVD bo pin failed\n", r); 138f2ba57b5SChristian König return r; 139f2ba57b5SChristian König } 140f2ba57b5SChristian König 141f2ba57b5SChristian König r = radeon_bo_kmap(rdev->uvd.vcpu_bo, &rdev->uvd.cpu_addr); 142f2ba57b5SChristian König if (r) { 143f2ba57b5SChristian König dev_err(rdev->dev, "(%d) UVD map failed\n", r); 144f2ba57b5SChristian König return r; 145f2ba57b5SChristian König } 146f2ba57b5SChristian König 147f2ba57b5SChristian König radeon_bo_unreserve(rdev->uvd.vcpu_bo); 148f2ba57b5SChristian König 1499cc2e0e9SChristian König for (i = 0; i < RADEON_MAX_UVD_HANDLES; ++i) { 1509cc2e0e9SChristian König atomic_set(&rdev->uvd.handles[i], 0); 1519cc2e0e9SChristian König rdev->uvd.filp[i] = NULL; 15285a129caSAlex Deucher rdev->uvd.img_size[i] = 0; 1539cc2e0e9SChristian König } 1549cc2e0e9SChristian König 1559cc2e0e9SChristian König return 0; 1569cc2e0e9SChristian König } 1579cc2e0e9SChristian König 1589cc2e0e9SChristian König void radeon_uvd_fini(struct radeon_device *rdev) 1599cc2e0e9SChristian König { 1609cc2e0e9SChristian König int r; 1619cc2e0e9SChristian König 1629cc2e0e9SChristian König if (rdev->uvd.vcpu_bo == NULL) 1639cc2e0e9SChristian König return; 1649cc2e0e9SChristian König 1659cc2e0e9SChristian König r = radeon_bo_reserve(rdev->uvd.vcpu_bo, false); 1669cc2e0e9SChristian König if (!r) { 1679cc2e0e9SChristian König radeon_bo_kunmap(rdev->uvd.vcpu_bo); 1689cc2e0e9SChristian König radeon_bo_unpin(rdev->uvd.vcpu_bo); 1699cc2e0e9SChristian König radeon_bo_unreserve(rdev->uvd.vcpu_bo); 1709cc2e0e9SChristian König } 1719cc2e0e9SChristian König 1729cc2e0e9SChristian König radeon_bo_unref(&rdev->uvd.vcpu_bo); 1734ad9c1c7SChristian König 174d9654413SJerome Glisse radeon_ring_fini(rdev, &rdev->ring[R600_RING_TYPE_UVD_INDEX]); 175d9654413SJerome Glisse 1764ad9c1c7SChristian König release_firmware(rdev->uvd_fw); 1779cc2e0e9SChristian König } 1789cc2e0e9SChristian König 1799cc2e0e9SChristian König int radeon_uvd_suspend(struct radeon_device *rdev) 1809cc2e0e9SChristian König { 1819cc2e0e9SChristian König unsigned size; 1824ad9c1c7SChristian König void *ptr; 1834ad9c1c7SChristian König int i; 1849cc2e0e9SChristian König 1859cc2e0e9SChristian König if (rdev->uvd.vcpu_bo == NULL) 1869cc2e0e9SChristian König return 0; 1879cc2e0e9SChristian König 1884ad9c1c7SChristian König for (i = 0; i < RADEON_MAX_UVD_HANDLES; ++i) 1894ad9c1c7SChristian König if (atomic_read(&rdev->uvd.handles[i])) 1904ad9c1c7SChristian König break; 1914ad9c1c7SChristian König 1924ad9c1c7SChristian König if (i == RADEON_MAX_UVD_HANDLES) 1934ad9c1c7SChristian König return 0; 1944ad9c1c7SChristian König 1959cc2e0e9SChristian König size = radeon_bo_size(rdev->uvd.vcpu_bo); 1964ad9c1c7SChristian König size -= rdev->uvd_fw->size; 1974ad9c1c7SChristian König 1984ad9c1c7SChristian König ptr = rdev->uvd.cpu_addr; 1994ad9c1c7SChristian König ptr += rdev->uvd_fw->size; 2004ad9c1c7SChristian König 2019cc2e0e9SChristian König rdev->uvd.saved_bo = kmalloc(size, GFP_KERNEL); 2024ad9c1c7SChristian König memcpy(rdev->uvd.saved_bo, ptr, size); 2039cc2e0e9SChristian König 2049cc2e0e9SChristian König return 0; 2059cc2e0e9SChristian König } 2069cc2e0e9SChristian König 2079cc2e0e9SChristian König int radeon_uvd_resume(struct radeon_device *rdev) 2089cc2e0e9SChristian König { 2094ad9c1c7SChristian König unsigned size; 2104ad9c1c7SChristian König void *ptr; 2114ad9c1c7SChristian König 2129cc2e0e9SChristian König if (rdev->uvd.vcpu_bo == NULL) 2139cc2e0e9SChristian König return -EINVAL; 2149cc2e0e9SChristian König 2154ad9c1c7SChristian König memcpy(rdev->uvd.cpu_addr, rdev->uvd_fw->data, rdev->uvd_fw->size); 2164ad9c1c7SChristian König 2174ad9c1c7SChristian König size = radeon_bo_size(rdev->uvd.vcpu_bo); 2184ad9c1c7SChristian König size -= rdev->uvd_fw->size; 2194ad9c1c7SChristian König 2204ad9c1c7SChristian König ptr = rdev->uvd.cpu_addr; 2214ad9c1c7SChristian König ptr += rdev->uvd_fw->size; 2224ad9c1c7SChristian König 2239cc2e0e9SChristian König if (rdev->uvd.saved_bo != NULL) { 2244ad9c1c7SChristian König memcpy(ptr, rdev->uvd.saved_bo, size); 2259cc2e0e9SChristian König kfree(rdev->uvd.saved_bo); 2269cc2e0e9SChristian König rdev->uvd.saved_bo = NULL; 2274ad9c1c7SChristian König } else 2284ad9c1c7SChristian König memset(ptr, 0, size); 2299cc2e0e9SChristian König 230f2ba57b5SChristian König return 0; 231f2ba57b5SChristian König } 232f2ba57b5SChristian König 233f2ba57b5SChristian König void radeon_uvd_force_into_uvd_segment(struct radeon_bo *rbo) 234f2ba57b5SChristian König { 235f2ba57b5SChristian König rbo->placement.fpfn = 0 >> PAGE_SHIFT; 236f2ba57b5SChristian König rbo->placement.lpfn = (256 * 1024 * 1024) >> PAGE_SHIFT; 237f2ba57b5SChristian König } 238f2ba57b5SChristian König 239f2ba57b5SChristian König void radeon_uvd_free_handles(struct radeon_device *rdev, struct drm_file *filp) 240f2ba57b5SChristian König { 241f2ba57b5SChristian König int i, r; 242f2ba57b5SChristian König for (i = 0; i < RADEON_MAX_UVD_HANDLES; ++i) { 243f2ba57b5SChristian König uint32_t handle = atomic_read(&rdev->uvd.handles[i]); 244641a0059SChristian König if (handle != 0 && rdev->uvd.filp[i] == filp) { 245f2ba57b5SChristian König struct radeon_fence *fence; 246f2ba57b5SChristian König 247c154a763SChristian König radeon_uvd_note_usage(rdev); 248c154a763SChristian König 249f2ba57b5SChristian König r = radeon_uvd_get_destroy_msg(rdev, 250f2ba57b5SChristian König R600_RING_TYPE_UVD_INDEX, handle, &fence); 251f2ba57b5SChristian König if (r) { 252f2ba57b5SChristian König DRM_ERROR("Error destroying UVD (%d)!\n", r); 253f2ba57b5SChristian König continue; 254f2ba57b5SChristian König } 255f2ba57b5SChristian König 256f2ba57b5SChristian König radeon_fence_wait(fence, false); 257f2ba57b5SChristian König radeon_fence_unref(&fence); 258f2ba57b5SChristian König 259f2ba57b5SChristian König rdev->uvd.filp[i] = NULL; 260f2ba57b5SChristian König atomic_set(&rdev->uvd.handles[i], 0); 261f2ba57b5SChristian König } 262f2ba57b5SChristian König } 263f2ba57b5SChristian König } 264f2ba57b5SChristian König 265f2ba57b5SChristian König static int radeon_uvd_cs_msg_decode(uint32_t *msg, unsigned buf_sizes[]) 266f2ba57b5SChristian König { 267f2ba57b5SChristian König unsigned stream_type = msg[4]; 268f2ba57b5SChristian König unsigned width = msg[6]; 269f2ba57b5SChristian König unsigned height = msg[7]; 270f2ba57b5SChristian König unsigned dpb_size = msg[9]; 271f2ba57b5SChristian König unsigned pitch = msg[28]; 272f2ba57b5SChristian König 273f2ba57b5SChristian König unsigned width_in_mb = width / 16; 274f2ba57b5SChristian König unsigned height_in_mb = ALIGN(height / 16, 2); 275f2ba57b5SChristian König 276f2ba57b5SChristian König unsigned image_size, tmp, min_dpb_size; 277f2ba57b5SChristian König 278f2ba57b5SChristian König image_size = width * height; 279f2ba57b5SChristian König image_size += image_size / 2; 280f2ba57b5SChristian König image_size = ALIGN(image_size, 1024); 281f2ba57b5SChristian König 282f2ba57b5SChristian König switch (stream_type) { 283f2ba57b5SChristian König case 0: /* H264 */ 284f2ba57b5SChristian König 285f2ba57b5SChristian König /* reference picture buffer */ 286f2ba57b5SChristian König min_dpb_size = image_size * 17; 287f2ba57b5SChristian König 288f2ba57b5SChristian König /* macroblock context buffer */ 289f2ba57b5SChristian König min_dpb_size += width_in_mb * height_in_mb * 17 * 192; 290f2ba57b5SChristian König 291f2ba57b5SChristian König /* IT surface buffer */ 292f2ba57b5SChristian König min_dpb_size += width_in_mb * height_in_mb * 32; 293f2ba57b5SChristian König break; 294f2ba57b5SChristian König 295f2ba57b5SChristian König case 1: /* VC1 */ 296f2ba57b5SChristian König 297f2ba57b5SChristian König /* reference picture buffer */ 298f2ba57b5SChristian König min_dpb_size = image_size * 3; 299f2ba57b5SChristian König 300f2ba57b5SChristian König /* CONTEXT_BUFFER */ 301f2ba57b5SChristian König min_dpb_size += width_in_mb * height_in_mb * 128; 302f2ba57b5SChristian König 303f2ba57b5SChristian König /* IT surface buffer */ 304f2ba57b5SChristian König min_dpb_size += width_in_mb * 64; 305f2ba57b5SChristian König 306f2ba57b5SChristian König /* DB surface buffer */ 307f2ba57b5SChristian König min_dpb_size += width_in_mb * 128; 308f2ba57b5SChristian König 309f2ba57b5SChristian König /* BP */ 310f2ba57b5SChristian König tmp = max(width_in_mb, height_in_mb); 311f2ba57b5SChristian König min_dpb_size += ALIGN(tmp * 7 * 16, 64); 312f2ba57b5SChristian König break; 313f2ba57b5SChristian König 314f2ba57b5SChristian König case 3: /* MPEG2 */ 315f2ba57b5SChristian König 316f2ba57b5SChristian König /* reference picture buffer */ 317f2ba57b5SChristian König min_dpb_size = image_size * 3; 318f2ba57b5SChristian König break; 319f2ba57b5SChristian König 320f2ba57b5SChristian König case 4: /* MPEG4 */ 321f2ba57b5SChristian König 322f2ba57b5SChristian König /* reference picture buffer */ 323f2ba57b5SChristian König min_dpb_size = image_size * 3; 324f2ba57b5SChristian König 325f2ba57b5SChristian König /* CM */ 326f2ba57b5SChristian König min_dpb_size += width_in_mb * height_in_mb * 64; 327f2ba57b5SChristian König 328f2ba57b5SChristian König /* IT surface buffer */ 329f2ba57b5SChristian König min_dpb_size += ALIGN(width_in_mb * height_in_mb * 32, 64); 330f2ba57b5SChristian König break; 331f2ba57b5SChristian König 332f2ba57b5SChristian König default: 333f2ba57b5SChristian König DRM_ERROR("UVD codec not handled %d!\n", stream_type); 334f2ba57b5SChristian König return -EINVAL; 335f2ba57b5SChristian König } 336f2ba57b5SChristian König 337f2ba57b5SChristian König if (width > pitch) { 338f2ba57b5SChristian König DRM_ERROR("Invalid UVD decoding target pitch!\n"); 339f2ba57b5SChristian König return -EINVAL; 340f2ba57b5SChristian König } 341f2ba57b5SChristian König 342f2ba57b5SChristian König if (dpb_size < min_dpb_size) { 343f2ba57b5SChristian König DRM_ERROR("Invalid dpb_size in UVD message (%d / %d)!\n", 344f2ba57b5SChristian König dpb_size, min_dpb_size); 345f2ba57b5SChristian König return -EINVAL; 346f2ba57b5SChristian König } 347f2ba57b5SChristian König 348f2ba57b5SChristian König buf_sizes[0x1] = dpb_size; 349f2ba57b5SChristian König buf_sizes[0x2] = image_size; 350f2ba57b5SChristian König return 0; 351f2ba57b5SChristian König } 352f2ba57b5SChristian König 353f2ba57b5SChristian König static int radeon_uvd_cs_msg(struct radeon_cs_parser *p, struct radeon_bo *bo, 354f2ba57b5SChristian König unsigned offset, unsigned buf_sizes[]) 355f2ba57b5SChristian König { 356f2ba57b5SChristian König int32_t *msg, msg_type, handle; 35785a129caSAlex Deucher unsigned img_size = 0; 358f2ba57b5SChristian König void *ptr; 359f2ba57b5SChristian König 360f2ba57b5SChristian König int i, r; 361f2ba57b5SChristian König 362f2ba57b5SChristian König if (offset & 0x3F) { 363f2ba57b5SChristian König DRM_ERROR("UVD messages must be 64 byte aligned!\n"); 364f2ba57b5SChristian König return -EINVAL; 365f2ba57b5SChristian König } 366f2ba57b5SChristian König 367112a6d0cSChristian König if (bo->tbo.sync_obj) { 368112a6d0cSChristian König r = radeon_fence_wait(bo->tbo.sync_obj, false); 369112a6d0cSChristian König if (r) { 370112a6d0cSChristian König DRM_ERROR("Failed waiting for UVD message (%d)!\n", r); 371112a6d0cSChristian König return r; 372112a6d0cSChristian König } 373112a6d0cSChristian König } 374112a6d0cSChristian König 375f2ba57b5SChristian König r = radeon_bo_kmap(bo, &ptr); 37656cc2c15SChristian König if (r) { 37756cc2c15SChristian König DRM_ERROR("Failed mapping the UVD message (%d)!\n", r); 378f2ba57b5SChristian König return r; 37956cc2c15SChristian König } 380f2ba57b5SChristian König 381f2ba57b5SChristian König msg = ptr + offset; 382f2ba57b5SChristian König 383f2ba57b5SChristian König msg_type = msg[1]; 384f2ba57b5SChristian König handle = msg[2]; 385f2ba57b5SChristian König 386f2ba57b5SChristian König if (handle == 0) { 387f2ba57b5SChristian König DRM_ERROR("Invalid UVD handle!\n"); 388f2ba57b5SChristian König return -EINVAL; 389f2ba57b5SChristian König } 390f2ba57b5SChristian König 391f2ba57b5SChristian König if (msg_type == 1) { 392f2ba57b5SChristian König /* it's a decode msg, calc buffer sizes */ 393f2ba57b5SChristian König r = radeon_uvd_cs_msg_decode(msg, buf_sizes); 39485a129caSAlex Deucher /* calc image size (width * height) */ 39585a129caSAlex Deucher img_size = msg[6] * msg[7]; 396f2ba57b5SChristian König radeon_bo_kunmap(bo); 397f2ba57b5SChristian König if (r) 398f2ba57b5SChristian König return r; 399f2ba57b5SChristian König 400f2ba57b5SChristian König } else if (msg_type == 2) { 401f2ba57b5SChristian König /* it's a destroy msg, free the handle */ 402f2ba57b5SChristian König for (i = 0; i < RADEON_MAX_UVD_HANDLES; ++i) 403f2ba57b5SChristian König atomic_cmpxchg(&p->rdev->uvd.handles[i], handle, 0); 404f2ba57b5SChristian König radeon_bo_kunmap(bo); 405f2ba57b5SChristian König return 0; 406f2ba57b5SChristian König } else { 40785a129caSAlex Deucher /* it's a create msg, calc image size (width * height) */ 40885a129caSAlex Deucher img_size = msg[7] * msg[8]; 409f2ba57b5SChristian König radeon_bo_kunmap(bo); 41056cc2c15SChristian König 41156cc2c15SChristian König if (msg_type != 0) { 41256cc2c15SChristian König DRM_ERROR("Illegal UVD message type (%d)!\n", msg_type); 41356cc2c15SChristian König return -EINVAL; 41456cc2c15SChristian König } 41556cc2c15SChristian König 41656cc2c15SChristian König /* it's a create msg, no special handling needed */ 417f2ba57b5SChristian König } 418f2ba57b5SChristian König 419f2ba57b5SChristian König /* create or decode, validate the handle */ 420f2ba57b5SChristian König for (i = 0; i < RADEON_MAX_UVD_HANDLES; ++i) { 421f2ba57b5SChristian König if (atomic_read(&p->rdev->uvd.handles[i]) == handle) 422f2ba57b5SChristian König return 0; 423f2ba57b5SChristian König } 424f2ba57b5SChristian König 425f2ba57b5SChristian König /* handle not found try to alloc a new one */ 426f2ba57b5SChristian König for (i = 0; i < RADEON_MAX_UVD_HANDLES; ++i) { 427f2ba57b5SChristian König if (!atomic_cmpxchg(&p->rdev->uvd.handles[i], 0, handle)) { 428f2ba57b5SChristian König p->rdev->uvd.filp[i] = p->filp; 42985a129caSAlex Deucher p->rdev->uvd.img_size[i] = img_size; 430f2ba57b5SChristian König return 0; 431f2ba57b5SChristian König } 432f2ba57b5SChristian König } 433f2ba57b5SChristian König 434f2ba57b5SChristian König DRM_ERROR("No more free UVD handles!\n"); 435f2ba57b5SChristian König return -EINVAL; 436f2ba57b5SChristian König } 437f2ba57b5SChristian König 438f2ba57b5SChristian König static int radeon_uvd_cs_reloc(struct radeon_cs_parser *p, 439f2ba57b5SChristian König int data0, int data1, 44056cc2c15SChristian König unsigned buf_sizes[], bool *has_msg_cmd) 441f2ba57b5SChristian König { 442f2ba57b5SChristian König struct radeon_cs_chunk *relocs_chunk; 443f2ba57b5SChristian König struct radeon_cs_reloc *reloc; 444f2ba57b5SChristian König unsigned idx, cmd, offset; 445f2ba57b5SChristian König uint64_t start, end; 446f2ba57b5SChristian König int r; 447f2ba57b5SChristian König 448f2ba57b5SChristian König relocs_chunk = &p->chunks[p->chunk_relocs_idx]; 449f2ba57b5SChristian König offset = radeon_get_ib_value(p, data0); 450f2ba57b5SChristian König idx = radeon_get_ib_value(p, data1); 451f2ba57b5SChristian König if (idx >= relocs_chunk->length_dw) { 452f2ba57b5SChristian König DRM_ERROR("Relocs at %d after relocations chunk end %d !\n", 453f2ba57b5SChristian König idx, relocs_chunk->length_dw); 454f2ba57b5SChristian König return -EINVAL; 455f2ba57b5SChristian König } 456f2ba57b5SChristian König 457f2ba57b5SChristian König reloc = p->relocs_ptr[(idx / 4)]; 458df0af440SChristian König start = reloc->gpu_offset; 459f2ba57b5SChristian König end = start + radeon_bo_size(reloc->robj); 460f2ba57b5SChristian König start += offset; 461f2ba57b5SChristian König 462f2ba57b5SChristian König p->ib.ptr[data0] = start & 0xFFFFFFFF; 463f2ba57b5SChristian König p->ib.ptr[data1] = start >> 32; 464f2ba57b5SChristian König 465f2ba57b5SChristian König cmd = radeon_get_ib_value(p, p->idx) >> 1; 466f2ba57b5SChristian König 467f2ba57b5SChristian König if (cmd < 0x4) { 468695daf1aSLeo Liu if (end <= start) { 469695daf1aSLeo Liu DRM_ERROR("invalid reloc offset %X!\n", offset); 470695daf1aSLeo Liu return -EINVAL; 471695daf1aSLeo Liu } 472f2ba57b5SChristian König if ((end - start) < buf_sizes[cmd]) { 47356cc2c15SChristian König DRM_ERROR("buffer (%d) to small (%d / %d)!\n", cmd, 474f2ba57b5SChristian König (unsigned)(end - start), buf_sizes[cmd]); 475f2ba57b5SChristian König return -EINVAL; 476f2ba57b5SChristian König } 477f2ba57b5SChristian König 478f2ba57b5SChristian König } else if (cmd != 0x100) { 479f2ba57b5SChristian König DRM_ERROR("invalid UVD command %X!\n", cmd); 480f2ba57b5SChristian König return -EINVAL; 481f2ba57b5SChristian König } 482f2ba57b5SChristian König 483bae651dbSChristian König if ((start >> 28) != ((end - 1) >> 28)) { 484f2ba57b5SChristian König DRM_ERROR("reloc %LX-%LX crossing 256MB boundary!\n", 485f2ba57b5SChristian König start, end); 486f2ba57b5SChristian König return -EINVAL; 487f2ba57b5SChristian König } 488f2ba57b5SChristian König 489bcf6f1e9SChristian König /* TODO: is this still necessary on NI+ ? */ 490bcf6f1e9SChristian König if ((cmd == 0 || cmd == 0x3) && 491a92c7d55SChristian König (start >> 28) != (p->rdev->uvd.gpu_addr >> 28)) { 492a92c7d55SChristian König DRM_ERROR("msg/fb buffer %LX-%LX out of 256MB segment!\n", 493a92c7d55SChristian König start, end); 494a92c7d55SChristian König return -EINVAL; 495a92c7d55SChristian König } 496a92c7d55SChristian König 497a92c7d55SChristian König if (cmd == 0) { 49856cc2c15SChristian König if (*has_msg_cmd) { 49956cc2c15SChristian König DRM_ERROR("More than one message in a UVD-IB!\n"); 50056cc2c15SChristian König return -EINVAL; 50156cc2c15SChristian König } 50256cc2c15SChristian König *has_msg_cmd = true; 503a92c7d55SChristian König r = radeon_uvd_cs_msg(p, reloc->robj, offset, buf_sizes); 504a92c7d55SChristian König if (r) 505a92c7d55SChristian König return r; 50656cc2c15SChristian König } else if (!*has_msg_cmd) { 50756cc2c15SChristian König DRM_ERROR("Message needed before other commands are send!\n"); 50856cc2c15SChristian König return -EINVAL; 509a92c7d55SChristian König } 510a92c7d55SChristian König 511f2ba57b5SChristian König return 0; 512f2ba57b5SChristian König } 513f2ba57b5SChristian König 514f2ba57b5SChristian König static int radeon_uvd_cs_reg(struct radeon_cs_parser *p, 515f2ba57b5SChristian König struct radeon_cs_packet *pkt, 516f2ba57b5SChristian König int *data0, int *data1, 51756cc2c15SChristian König unsigned buf_sizes[], 51856cc2c15SChristian König bool *has_msg_cmd) 519f2ba57b5SChristian König { 520f2ba57b5SChristian König int i, r; 521f2ba57b5SChristian König 522f2ba57b5SChristian König p->idx++; 523f2ba57b5SChristian König for (i = 0; i <= pkt->count; ++i) { 524f2ba57b5SChristian König switch (pkt->reg + i*4) { 525f2ba57b5SChristian König case UVD_GPCOM_VCPU_DATA0: 526f2ba57b5SChristian König *data0 = p->idx; 527f2ba57b5SChristian König break; 528f2ba57b5SChristian König case UVD_GPCOM_VCPU_DATA1: 529f2ba57b5SChristian König *data1 = p->idx; 530f2ba57b5SChristian König break; 531f2ba57b5SChristian König case UVD_GPCOM_VCPU_CMD: 53256cc2c15SChristian König r = radeon_uvd_cs_reloc(p, *data0, *data1, 53356cc2c15SChristian König buf_sizes, has_msg_cmd); 534f2ba57b5SChristian König if (r) 535f2ba57b5SChristian König return r; 536f2ba57b5SChristian König break; 537f2ba57b5SChristian König case UVD_ENGINE_CNTL: 538f2ba57b5SChristian König break; 539f2ba57b5SChristian König default: 540f2ba57b5SChristian König DRM_ERROR("Invalid reg 0x%X!\n", 541f2ba57b5SChristian König pkt->reg + i*4); 542f2ba57b5SChristian König return -EINVAL; 543f2ba57b5SChristian König } 544f2ba57b5SChristian König p->idx++; 545f2ba57b5SChristian König } 546f2ba57b5SChristian König return 0; 547f2ba57b5SChristian König } 548f2ba57b5SChristian König 549f2ba57b5SChristian König int radeon_uvd_cs_parse(struct radeon_cs_parser *p) 550f2ba57b5SChristian König { 551f2ba57b5SChristian König struct radeon_cs_packet pkt; 552f2ba57b5SChristian König int r, data0 = 0, data1 = 0; 553f2ba57b5SChristian König 55456cc2c15SChristian König /* does the IB has a msg command */ 55556cc2c15SChristian König bool has_msg_cmd = false; 55656cc2c15SChristian König 557f2ba57b5SChristian König /* minimum buffer sizes */ 558f2ba57b5SChristian König unsigned buf_sizes[] = { 559f2ba57b5SChristian König [0x00000000] = 2048, 560f2ba57b5SChristian König [0x00000001] = 32 * 1024 * 1024, 561f2ba57b5SChristian König [0x00000002] = 2048 * 1152 * 3, 562f2ba57b5SChristian König [0x00000003] = 2048, 563f2ba57b5SChristian König }; 564f2ba57b5SChristian König 565f2ba57b5SChristian König if (p->chunks[p->chunk_ib_idx].length_dw % 16) { 566f2ba57b5SChristian König DRM_ERROR("UVD IB length (%d) not 16 dwords aligned!\n", 567f2ba57b5SChristian König p->chunks[p->chunk_ib_idx].length_dw); 568f2ba57b5SChristian König return -EINVAL; 569f2ba57b5SChristian König } 570f2ba57b5SChristian König 571f2ba57b5SChristian König if (p->chunk_relocs_idx == -1) { 572f2ba57b5SChristian König DRM_ERROR("No relocation chunk !\n"); 573f2ba57b5SChristian König return -EINVAL; 574f2ba57b5SChristian König } 575f2ba57b5SChristian König 576f2ba57b5SChristian König 577f2ba57b5SChristian König do { 578f2ba57b5SChristian König r = radeon_cs_packet_parse(p, &pkt, p->idx); 579f2ba57b5SChristian König if (r) 580f2ba57b5SChristian König return r; 581f2ba57b5SChristian König switch (pkt.type) { 582f2ba57b5SChristian König case RADEON_PACKET_TYPE0: 58356cc2c15SChristian König r = radeon_uvd_cs_reg(p, &pkt, &data0, &data1, 58456cc2c15SChristian König buf_sizes, &has_msg_cmd); 585f2ba57b5SChristian König if (r) 586f2ba57b5SChristian König return r; 587f2ba57b5SChristian König break; 588f2ba57b5SChristian König case RADEON_PACKET_TYPE2: 589f2ba57b5SChristian König p->idx += pkt.count + 2; 590f2ba57b5SChristian König break; 591f2ba57b5SChristian König default: 592f2ba57b5SChristian König DRM_ERROR("Unknown packet type %d !\n", pkt.type); 593f2ba57b5SChristian König return -EINVAL; 594f2ba57b5SChristian König } 595f2ba57b5SChristian König } while (p->idx < p->chunks[p->chunk_ib_idx].length_dw); 59656cc2c15SChristian König 59756cc2c15SChristian König if (!has_msg_cmd) { 59856cc2c15SChristian König DRM_ERROR("UVD-IBs need a msg command!\n"); 59956cc2c15SChristian König return -EINVAL; 60056cc2c15SChristian König } 60156cc2c15SChristian König 602f2ba57b5SChristian König return 0; 603f2ba57b5SChristian König } 604f2ba57b5SChristian König 605f2ba57b5SChristian König static int radeon_uvd_send_msg(struct radeon_device *rdev, 606f2ba57b5SChristian König int ring, struct radeon_bo *bo, 607f2ba57b5SChristian König struct radeon_fence **fence) 608f2ba57b5SChristian König { 609f2ba57b5SChristian König struct ttm_validate_buffer tv; 610ecff665fSMaarten Lankhorst struct ww_acquire_ctx ticket; 611f2ba57b5SChristian König struct list_head head; 612f2ba57b5SChristian König struct radeon_ib ib; 613f2ba57b5SChristian König uint64_t addr; 614f2ba57b5SChristian König int i, r; 615f2ba57b5SChristian König 616f2ba57b5SChristian König memset(&tv, 0, sizeof(tv)); 617f2ba57b5SChristian König tv.bo = &bo->tbo; 618f2ba57b5SChristian König 619f2ba57b5SChristian König INIT_LIST_HEAD(&head); 620f2ba57b5SChristian König list_add(&tv.head, &head); 621f2ba57b5SChristian König 622ecff665fSMaarten Lankhorst r = ttm_eu_reserve_buffers(&ticket, &head); 623f2ba57b5SChristian König if (r) 624f2ba57b5SChristian König return r; 625f2ba57b5SChristian König 626f2ba57b5SChristian König radeon_ttm_placement_from_domain(bo, RADEON_GEM_DOMAIN_VRAM); 627f2ba57b5SChristian König radeon_uvd_force_into_uvd_segment(bo); 628f2ba57b5SChristian König 629f2ba57b5SChristian König r = ttm_bo_validate(&bo->tbo, &bo->placement, true, false); 630ecff665fSMaarten Lankhorst if (r) 631ecff665fSMaarten Lankhorst goto err; 632f2ba57b5SChristian König 633727ddc84SChristian König r = radeon_ib_get(rdev, ring, &ib, NULL, 64); 634ecff665fSMaarten Lankhorst if (r) 635ecff665fSMaarten Lankhorst goto err; 636f2ba57b5SChristian König 637f2ba57b5SChristian König addr = radeon_bo_gpu_offset(bo); 638f2ba57b5SChristian König ib.ptr[0] = PACKET0(UVD_GPCOM_VCPU_DATA0, 0); 639f2ba57b5SChristian König ib.ptr[1] = addr; 640f2ba57b5SChristian König ib.ptr[2] = PACKET0(UVD_GPCOM_VCPU_DATA1, 0); 641f2ba57b5SChristian König ib.ptr[3] = addr >> 32; 642f2ba57b5SChristian König ib.ptr[4] = PACKET0(UVD_GPCOM_VCPU_CMD, 0); 643f2ba57b5SChristian König ib.ptr[5] = 0; 644f2ba57b5SChristian König for (i = 6; i < 16; ++i) 645f2ba57b5SChristian König ib.ptr[i] = PACKET2(0); 646f2ba57b5SChristian König ib.length_dw = 16; 647f2ba57b5SChristian König 648f2ba57b5SChristian König r = radeon_ib_schedule(rdev, &ib, NULL); 649ecff665fSMaarten Lankhorst if (r) 650ecff665fSMaarten Lankhorst goto err; 651ecff665fSMaarten Lankhorst ttm_eu_fence_buffer_objects(&ticket, &head, ib.fence); 652f2ba57b5SChristian König 653f2ba57b5SChristian König if (fence) 654f2ba57b5SChristian König *fence = radeon_fence_ref(ib.fence); 655f2ba57b5SChristian König 656f2ba57b5SChristian König radeon_ib_free(rdev, &ib); 657f2ba57b5SChristian König radeon_bo_unref(&bo); 658f2ba57b5SChristian König return 0; 659ecff665fSMaarten Lankhorst 660ecff665fSMaarten Lankhorst err: 661ecff665fSMaarten Lankhorst ttm_eu_backoff_reservation(&ticket, &head); 662ecff665fSMaarten Lankhorst return r; 663f2ba57b5SChristian König } 664f2ba57b5SChristian König 665f2ba57b5SChristian König /* multiple fence commands without any stream commands in between can 666f2ba57b5SChristian König crash the vcpu so just try to emmit a dummy create/destroy msg to 667f2ba57b5SChristian König avoid this */ 668f2ba57b5SChristian König int radeon_uvd_get_create_msg(struct radeon_device *rdev, int ring, 669f2ba57b5SChristian König uint32_t handle, struct radeon_fence **fence) 670f2ba57b5SChristian König { 671f2ba57b5SChristian König struct radeon_bo *bo; 672f2ba57b5SChristian König uint32_t *msg; 673f2ba57b5SChristian König int r, i; 674f2ba57b5SChristian König 675f2ba57b5SChristian König r = radeon_bo_create(rdev, 1024, PAGE_SIZE, true, 676f2ba57b5SChristian König RADEON_GEM_DOMAIN_VRAM, NULL, &bo); 677f2ba57b5SChristian König if (r) 678f2ba57b5SChristian König return r; 679f2ba57b5SChristian König 680f2ba57b5SChristian König r = radeon_bo_reserve(bo, false); 681f2ba57b5SChristian König if (r) { 682f2ba57b5SChristian König radeon_bo_unref(&bo); 683f2ba57b5SChristian König return r; 684f2ba57b5SChristian König } 685f2ba57b5SChristian König 686f2ba57b5SChristian König r = radeon_bo_kmap(bo, (void **)&msg); 687f2ba57b5SChristian König if (r) { 688f2ba57b5SChristian König radeon_bo_unreserve(bo); 689f2ba57b5SChristian König radeon_bo_unref(&bo); 690f2ba57b5SChristian König return r; 691f2ba57b5SChristian König } 692f2ba57b5SChristian König 693f2ba57b5SChristian König /* stitch together an UVD create msg */ 6949b1be4dcSAlex Deucher msg[0] = cpu_to_le32(0x00000de4); 6959b1be4dcSAlex Deucher msg[1] = cpu_to_le32(0x00000000); 6969b1be4dcSAlex Deucher msg[2] = cpu_to_le32(handle); 6979b1be4dcSAlex Deucher msg[3] = cpu_to_le32(0x00000000); 6989b1be4dcSAlex Deucher msg[4] = cpu_to_le32(0x00000000); 6999b1be4dcSAlex Deucher msg[5] = cpu_to_le32(0x00000000); 7009b1be4dcSAlex Deucher msg[6] = cpu_to_le32(0x00000000); 7019b1be4dcSAlex Deucher msg[7] = cpu_to_le32(0x00000780); 7029b1be4dcSAlex Deucher msg[8] = cpu_to_le32(0x00000440); 7039b1be4dcSAlex Deucher msg[9] = cpu_to_le32(0x00000000); 7049b1be4dcSAlex Deucher msg[10] = cpu_to_le32(0x01b37000); 705f2ba57b5SChristian König for (i = 11; i < 1024; ++i) 7069b1be4dcSAlex Deucher msg[i] = cpu_to_le32(0x0); 707f2ba57b5SChristian König 708f2ba57b5SChristian König radeon_bo_kunmap(bo); 709f2ba57b5SChristian König radeon_bo_unreserve(bo); 710f2ba57b5SChristian König 711f2ba57b5SChristian König return radeon_uvd_send_msg(rdev, ring, bo, fence); 712f2ba57b5SChristian König } 713f2ba57b5SChristian König 714f2ba57b5SChristian König int radeon_uvd_get_destroy_msg(struct radeon_device *rdev, int ring, 715f2ba57b5SChristian König uint32_t handle, struct radeon_fence **fence) 716f2ba57b5SChristian König { 717f2ba57b5SChristian König struct radeon_bo *bo; 718f2ba57b5SChristian König uint32_t *msg; 719f2ba57b5SChristian König int r, i; 720f2ba57b5SChristian König 721f2ba57b5SChristian König r = radeon_bo_create(rdev, 1024, PAGE_SIZE, true, 722f2ba57b5SChristian König RADEON_GEM_DOMAIN_VRAM, NULL, &bo); 723f2ba57b5SChristian König if (r) 724f2ba57b5SChristian König return r; 725f2ba57b5SChristian König 726f2ba57b5SChristian König r = radeon_bo_reserve(bo, false); 727f2ba57b5SChristian König if (r) { 728f2ba57b5SChristian König radeon_bo_unref(&bo); 729f2ba57b5SChristian König return r; 730f2ba57b5SChristian König } 731f2ba57b5SChristian König 732f2ba57b5SChristian König r = radeon_bo_kmap(bo, (void **)&msg); 733f2ba57b5SChristian König if (r) { 734f2ba57b5SChristian König radeon_bo_unreserve(bo); 735f2ba57b5SChristian König radeon_bo_unref(&bo); 736f2ba57b5SChristian König return r; 737f2ba57b5SChristian König } 738f2ba57b5SChristian König 739f2ba57b5SChristian König /* stitch together an UVD destroy msg */ 7409b1be4dcSAlex Deucher msg[0] = cpu_to_le32(0x00000de4); 7419b1be4dcSAlex Deucher msg[1] = cpu_to_le32(0x00000002); 7429b1be4dcSAlex Deucher msg[2] = cpu_to_le32(handle); 7439b1be4dcSAlex Deucher msg[3] = cpu_to_le32(0x00000000); 744f2ba57b5SChristian König for (i = 4; i < 1024; ++i) 7459b1be4dcSAlex Deucher msg[i] = cpu_to_le32(0x0); 746f2ba57b5SChristian König 747f2ba57b5SChristian König radeon_bo_kunmap(bo); 748f2ba57b5SChristian König radeon_bo_unreserve(bo); 749f2ba57b5SChristian König 750f2ba57b5SChristian König return radeon_uvd_send_msg(rdev, ring, bo, fence); 751f2ba57b5SChristian König } 75255b51c88SChristian König 75385a129caSAlex Deucher /** 75485a129caSAlex Deucher * radeon_uvd_count_handles - count number of open streams 75585a129caSAlex Deucher * 75685a129caSAlex Deucher * @rdev: radeon_device pointer 75785a129caSAlex Deucher * @sd: number of SD streams 75885a129caSAlex Deucher * @hd: number of HD streams 75985a129caSAlex Deucher * 76085a129caSAlex Deucher * Count the number of open SD/HD streams as a hint for power mangement 76185a129caSAlex Deucher */ 76285a129caSAlex Deucher static void radeon_uvd_count_handles(struct radeon_device *rdev, 76385a129caSAlex Deucher unsigned *sd, unsigned *hd) 76485a129caSAlex Deucher { 76585a129caSAlex Deucher unsigned i; 76685a129caSAlex Deucher 76785a129caSAlex Deucher *sd = 0; 76885a129caSAlex Deucher *hd = 0; 76985a129caSAlex Deucher 77085a129caSAlex Deucher for (i = 0; i < RADEON_MAX_UVD_HANDLES; ++i) { 77185a129caSAlex Deucher if (!atomic_read(&rdev->uvd.handles[i])) 77285a129caSAlex Deucher continue; 77385a129caSAlex Deucher 77485a129caSAlex Deucher if (rdev->uvd.img_size[i] >= 720*576) 77585a129caSAlex Deucher ++(*hd); 77685a129caSAlex Deucher else 77785a129caSAlex Deucher ++(*sd); 77885a129caSAlex Deucher } 77985a129caSAlex Deucher } 78085a129caSAlex Deucher 78155b51c88SChristian König static void radeon_uvd_idle_work_handler(struct work_struct *work) 78255b51c88SChristian König { 78355b51c88SChristian König struct radeon_device *rdev = 78455b51c88SChristian König container_of(work, struct radeon_device, uvd.idle_work.work); 78555b51c88SChristian König 7868a227555SAlex Deucher if (radeon_fence_count_emitted(rdev, R600_RING_TYPE_UVD_INDEX) == 0) { 7878a227555SAlex Deucher if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) { 7888158eb9eSChristian König radeon_uvd_count_handles(rdev, &rdev->pm.dpm.sd, 7898158eb9eSChristian König &rdev->pm.dpm.hd); 790ce3537d5SAlex Deucher radeon_dpm_enable_uvd(rdev, false); 7918a227555SAlex Deucher } else { 79255b51c88SChristian König radeon_set_uvd_clocks(rdev, 0, 0); 7938a227555SAlex Deucher } 7948a227555SAlex Deucher } else { 79555b51c88SChristian König schedule_delayed_work(&rdev->uvd.idle_work, 79655b51c88SChristian König msecs_to_jiffies(UVD_IDLE_TIMEOUT_MS)); 79755b51c88SChristian König } 7988a227555SAlex Deucher } 79955b51c88SChristian König 80055b51c88SChristian König void radeon_uvd_note_usage(struct radeon_device *rdev) 80155b51c88SChristian König { 802ce3537d5SAlex Deucher bool streams_changed = false; 80355b51c88SChristian König bool set_clocks = !cancel_delayed_work_sync(&rdev->uvd.idle_work); 80455b51c88SChristian König set_clocks &= schedule_delayed_work(&rdev->uvd.idle_work, 80555b51c88SChristian König msecs_to_jiffies(UVD_IDLE_TIMEOUT_MS)); 806ce3537d5SAlex Deucher 8078a227555SAlex Deucher if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) { 808ce3537d5SAlex Deucher unsigned hd = 0, sd = 0; 809ce3537d5SAlex Deucher radeon_uvd_count_handles(rdev, &sd, &hd); 810ce3537d5SAlex Deucher if ((rdev->pm.dpm.sd != sd) || 811ce3537d5SAlex Deucher (rdev->pm.dpm.hd != hd)) { 812ce3537d5SAlex Deucher rdev->pm.dpm.sd = sd; 813ce3537d5SAlex Deucher rdev->pm.dpm.hd = hd; 81414a9579dSAlex Deucher streams_changed = true; 815ce3537d5SAlex Deucher } 816ce3537d5SAlex Deucher } 817ce3537d5SAlex Deucher 818ce3537d5SAlex Deucher if (set_clocks || streams_changed) { 819ce3537d5SAlex Deucher if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) { 820ce3537d5SAlex Deucher radeon_dpm_enable_uvd(rdev, true); 8218a227555SAlex Deucher } else { 82255b51c88SChristian König radeon_set_uvd_clocks(rdev, 53300, 40000); 82355b51c88SChristian König } 8248a227555SAlex Deucher } 8258a227555SAlex Deucher } 826facd112dSChristian König 827facd112dSChristian König static unsigned radeon_uvd_calc_upll_post_div(unsigned vco_freq, 828facd112dSChristian König unsigned target_freq, 829facd112dSChristian König unsigned pd_min, 830facd112dSChristian König unsigned pd_even) 831facd112dSChristian König { 832facd112dSChristian König unsigned post_div = vco_freq / target_freq; 833facd112dSChristian König 834facd112dSChristian König /* adjust to post divider minimum value */ 835facd112dSChristian König if (post_div < pd_min) 836facd112dSChristian König post_div = pd_min; 837facd112dSChristian König 838facd112dSChristian König /* we alway need a frequency less than or equal the target */ 839facd112dSChristian König if ((vco_freq / post_div) > target_freq) 840facd112dSChristian König post_div += 1; 841facd112dSChristian König 842facd112dSChristian König /* post dividers above a certain value must be even */ 843facd112dSChristian König if (post_div > pd_even && post_div % 2) 844facd112dSChristian König post_div += 1; 845facd112dSChristian König 846facd112dSChristian König return post_div; 847facd112dSChristian König } 848facd112dSChristian König 849facd112dSChristian König /** 850facd112dSChristian König * radeon_uvd_calc_upll_dividers - calc UPLL clock dividers 851facd112dSChristian König * 852facd112dSChristian König * @rdev: radeon_device pointer 853facd112dSChristian König * @vclk: wanted VCLK 854facd112dSChristian König * @dclk: wanted DCLK 855facd112dSChristian König * @vco_min: minimum VCO frequency 856facd112dSChristian König * @vco_max: maximum VCO frequency 857facd112dSChristian König * @fb_factor: factor to multiply vco freq with 858facd112dSChristian König * @fb_mask: limit and bitmask for feedback divider 859facd112dSChristian König * @pd_min: post divider minimum 860facd112dSChristian König * @pd_max: post divider maximum 861facd112dSChristian König * @pd_even: post divider must be even above this value 862facd112dSChristian König * @optimal_fb_div: resulting feedback divider 863facd112dSChristian König * @optimal_vclk_div: resulting vclk post divider 864facd112dSChristian König * @optimal_dclk_div: resulting dclk post divider 865facd112dSChristian König * 866facd112dSChristian König * Calculate dividers for UVDs UPLL (R6xx-SI, except APUs). 867facd112dSChristian König * Returns zero on success -EINVAL on error. 868facd112dSChristian König */ 869facd112dSChristian König int radeon_uvd_calc_upll_dividers(struct radeon_device *rdev, 870facd112dSChristian König unsigned vclk, unsigned dclk, 871facd112dSChristian König unsigned vco_min, unsigned vco_max, 872facd112dSChristian König unsigned fb_factor, unsigned fb_mask, 873facd112dSChristian König unsigned pd_min, unsigned pd_max, 874facd112dSChristian König unsigned pd_even, 875facd112dSChristian König unsigned *optimal_fb_div, 876facd112dSChristian König unsigned *optimal_vclk_div, 877facd112dSChristian König unsigned *optimal_dclk_div) 878facd112dSChristian König { 879facd112dSChristian König unsigned vco_freq, ref_freq = rdev->clock.spll.reference_freq; 880facd112dSChristian König 881facd112dSChristian König /* start off with something large */ 882facd112dSChristian König unsigned optimal_score = ~0; 883facd112dSChristian König 884facd112dSChristian König /* loop through vco from low to high */ 885facd112dSChristian König vco_min = max(max(vco_min, vclk), dclk); 886facd112dSChristian König for (vco_freq = vco_min; vco_freq <= vco_max; vco_freq += 100) { 887facd112dSChristian König 888facd112dSChristian König uint64_t fb_div = (uint64_t)vco_freq * fb_factor; 889facd112dSChristian König unsigned vclk_div, dclk_div, score; 890facd112dSChristian König 891facd112dSChristian König do_div(fb_div, ref_freq); 892facd112dSChristian König 893facd112dSChristian König /* fb div out of range ? */ 894facd112dSChristian König if (fb_div > fb_mask) 895facd112dSChristian König break; /* it can oly get worse */ 896facd112dSChristian König 897facd112dSChristian König fb_div &= fb_mask; 898facd112dSChristian König 899facd112dSChristian König /* calc vclk divider with current vco freq */ 900facd112dSChristian König vclk_div = radeon_uvd_calc_upll_post_div(vco_freq, vclk, 901facd112dSChristian König pd_min, pd_even); 902facd112dSChristian König if (vclk_div > pd_max) 903facd112dSChristian König break; /* vco is too big, it has to stop */ 904facd112dSChristian König 905facd112dSChristian König /* calc dclk divider with current vco freq */ 906facd112dSChristian König dclk_div = radeon_uvd_calc_upll_post_div(vco_freq, dclk, 907facd112dSChristian König pd_min, pd_even); 908facd112dSChristian König if (vclk_div > pd_max) 909facd112dSChristian König break; /* vco is too big, it has to stop */ 910facd112dSChristian König 911facd112dSChristian König /* calc score with current vco freq */ 912facd112dSChristian König score = vclk - (vco_freq / vclk_div) + dclk - (vco_freq / dclk_div); 913facd112dSChristian König 914facd112dSChristian König /* determine if this vco setting is better than current optimal settings */ 915facd112dSChristian König if (score < optimal_score) { 916facd112dSChristian König *optimal_fb_div = fb_div; 917facd112dSChristian König *optimal_vclk_div = vclk_div; 918facd112dSChristian König *optimal_dclk_div = dclk_div; 919facd112dSChristian König optimal_score = score; 920facd112dSChristian König if (optimal_score == 0) 921facd112dSChristian König break; /* it can't get better than this */ 922facd112dSChristian König } 923facd112dSChristian König } 924facd112dSChristian König 925facd112dSChristian König /* did we found a valid setup ? */ 926facd112dSChristian König if (optimal_score == ~0) 927facd112dSChristian König return -EINVAL; 928facd112dSChristian König 929facd112dSChristian König return 0; 930facd112dSChristian König } 931facd112dSChristian König 932facd112dSChristian König int radeon_uvd_send_upll_ctlreq(struct radeon_device *rdev, 933facd112dSChristian König unsigned cg_upll_func_cntl) 934facd112dSChristian König { 935facd112dSChristian König unsigned i; 936facd112dSChristian König 937facd112dSChristian König /* make sure UPLL_CTLREQ is deasserted */ 938facd112dSChristian König WREG32_P(cg_upll_func_cntl, 0, ~UPLL_CTLREQ_MASK); 939facd112dSChristian König 940facd112dSChristian König mdelay(10); 941facd112dSChristian König 942facd112dSChristian König /* assert UPLL_CTLREQ */ 943facd112dSChristian König WREG32_P(cg_upll_func_cntl, UPLL_CTLREQ_MASK, ~UPLL_CTLREQ_MASK); 944facd112dSChristian König 945facd112dSChristian König /* wait for CTLACK and CTLACK2 to get asserted */ 946facd112dSChristian König for (i = 0; i < 100; ++i) { 947facd112dSChristian König uint32_t mask = UPLL_CTLACK_MASK | UPLL_CTLACK2_MASK; 948facd112dSChristian König if ((RREG32(cg_upll_func_cntl) & mask) == mask) 949facd112dSChristian König break; 950facd112dSChristian König mdelay(10); 951facd112dSChristian König } 952facd112dSChristian König 953facd112dSChristian König /* deassert UPLL_CTLREQ */ 954facd112dSChristian König WREG32_P(cg_upll_func_cntl, 0, ~UPLL_CTLREQ_MASK); 955facd112dSChristian König 956facd112dSChristian König if (i == 100) { 957facd112dSChristian König DRM_ERROR("Timeout setting UVD clocks!\n"); 958facd112dSChristian König return -ETIMEDOUT; 959facd112dSChristian König } 960facd112dSChristian König 961facd112dSChristian König return 0; 962facd112dSChristian König } 963