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: 1023f6f0737SSamuel Li case CHIP_MULLINS: 10387167bb1SChristian König fw_name = FIRMWARE_BONAIRE; 10487167bb1SChristian König break; 10587167bb1SChristian König 106f2ba57b5SChristian König default: 107f2ba57b5SChristian König return -EINVAL; 108f2ba57b5SChristian König } 109f2ba57b5SChristian König 1104ad9c1c7SChristian König r = request_firmware(&rdev->uvd_fw, fw_name, rdev->dev); 111f2ba57b5SChristian König if (r) { 112f2ba57b5SChristian König dev_err(rdev->dev, "radeon_uvd: Can't load firmware \"%s\"\n", 113f2ba57b5SChristian König fw_name); 114f2ba57b5SChristian König return r; 115f2ba57b5SChristian König } 116f2ba57b5SChristian König 1174ad9c1c7SChristian König bo_size = RADEON_GPU_PAGE_ALIGN(rdev->uvd_fw->size + 8) + 118f2ba57b5SChristian König RADEON_UVD_STACK_SIZE + RADEON_UVD_HEAP_SIZE; 119f2ba57b5SChristian König r = radeon_bo_create(rdev, bo_size, PAGE_SIZE, true, 120f2ba57b5SChristian König RADEON_GEM_DOMAIN_VRAM, NULL, &rdev->uvd.vcpu_bo); 121f2ba57b5SChristian König if (r) { 122f2ba57b5SChristian König dev_err(rdev->dev, "(%d) failed to allocate UVD bo\n", r); 123f2ba57b5SChristian König return r; 124f2ba57b5SChristian König } 125f2ba57b5SChristian König 126f2ba57b5SChristian König r = radeon_bo_reserve(rdev->uvd.vcpu_bo, false); 127f2ba57b5SChristian König if (r) { 128f2ba57b5SChristian König radeon_bo_unref(&rdev->uvd.vcpu_bo); 129f2ba57b5SChristian König dev_err(rdev->dev, "(%d) failed to reserve UVD bo\n", r); 130f2ba57b5SChristian König return r; 131f2ba57b5SChristian König } 132f2ba57b5SChristian König 133f2ba57b5SChristian König r = radeon_bo_pin(rdev->uvd.vcpu_bo, RADEON_GEM_DOMAIN_VRAM, 134f2ba57b5SChristian König &rdev->uvd.gpu_addr); 135f2ba57b5SChristian König if (r) { 136f2ba57b5SChristian König radeon_bo_unreserve(rdev->uvd.vcpu_bo); 137f2ba57b5SChristian König radeon_bo_unref(&rdev->uvd.vcpu_bo); 138f2ba57b5SChristian König dev_err(rdev->dev, "(%d) UVD bo pin failed\n", r); 139f2ba57b5SChristian König return r; 140f2ba57b5SChristian König } 141f2ba57b5SChristian König 142f2ba57b5SChristian König r = radeon_bo_kmap(rdev->uvd.vcpu_bo, &rdev->uvd.cpu_addr); 143f2ba57b5SChristian König if (r) { 144f2ba57b5SChristian König dev_err(rdev->dev, "(%d) UVD map failed\n", r); 145f2ba57b5SChristian König return r; 146f2ba57b5SChristian König } 147f2ba57b5SChristian König 148f2ba57b5SChristian König radeon_bo_unreserve(rdev->uvd.vcpu_bo); 149f2ba57b5SChristian König 1509cc2e0e9SChristian König for (i = 0; i < RADEON_MAX_UVD_HANDLES; ++i) { 1519cc2e0e9SChristian König atomic_set(&rdev->uvd.handles[i], 0); 1529cc2e0e9SChristian König rdev->uvd.filp[i] = NULL; 15385a129caSAlex Deucher rdev->uvd.img_size[i] = 0; 1549cc2e0e9SChristian König } 1559cc2e0e9SChristian König 1569cc2e0e9SChristian König return 0; 1579cc2e0e9SChristian König } 1589cc2e0e9SChristian König 1599cc2e0e9SChristian König void radeon_uvd_fini(struct radeon_device *rdev) 1609cc2e0e9SChristian König { 1619cc2e0e9SChristian König int r; 1629cc2e0e9SChristian König 1639cc2e0e9SChristian König if (rdev->uvd.vcpu_bo == NULL) 1649cc2e0e9SChristian König return; 1659cc2e0e9SChristian König 1669cc2e0e9SChristian König r = radeon_bo_reserve(rdev->uvd.vcpu_bo, false); 1679cc2e0e9SChristian König if (!r) { 1689cc2e0e9SChristian König radeon_bo_kunmap(rdev->uvd.vcpu_bo); 1699cc2e0e9SChristian König radeon_bo_unpin(rdev->uvd.vcpu_bo); 1709cc2e0e9SChristian König radeon_bo_unreserve(rdev->uvd.vcpu_bo); 1719cc2e0e9SChristian König } 1729cc2e0e9SChristian König 1739cc2e0e9SChristian König radeon_bo_unref(&rdev->uvd.vcpu_bo); 1744ad9c1c7SChristian König 175d9654413SJerome Glisse radeon_ring_fini(rdev, &rdev->ring[R600_RING_TYPE_UVD_INDEX]); 176d9654413SJerome Glisse 1774ad9c1c7SChristian König release_firmware(rdev->uvd_fw); 1789cc2e0e9SChristian König } 1799cc2e0e9SChristian König 1809cc2e0e9SChristian König int radeon_uvd_suspend(struct radeon_device *rdev) 1819cc2e0e9SChristian König { 1829cc2e0e9SChristian König unsigned size; 1834ad9c1c7SChristian König void *ptr; 1844ad9c1c7SChristian König int i; 1859cc2e0e9SChristian König 1869cc2e0e9SChristian König if (rdev->uvd.vcpu_bo == NULL) 1879cc2e0e9SChristian König return 0; 1889cc2e0e9SChristian König 1894ad9c1c7SChristian König for (i = 0; i < RADEON_MAX_UVD_HANDLES; ++i) 1904ad9c1c7SChristian König if (atomic_read(&rdev->uvd.handles[i])) 1914ad9c1c7SChristian König break; 1924ad9c1c7SChristian König 1934ad9c1c7SChristian König if (i == RADEON_MAX_UVD_HANDLES) 1944ad9c1c7SChristian König return 0; 1954ad9c1c7SChristian König 1969cc2e0e9SChristian König size = radeon_bo_size(rdev->uvd.vcpu_bo); 1974ad9c1c7SChristian König size -= rdev->uvd_fw->size; 1984ad9c1c7SChristian König 1994ad9c1c7SChristian König ptr = rdev->uvd.cpu_addr; 2004ad9c1c7SChristian König ptr += rdev->uvd_fw->size; 2014ad9c1c7SChristian König 2029cc2e0e9SChristian König rdev->uvd.saved_bo = kmalloc(size, GFP_KERNEL); 2034ad9c1c7SChristian König memcpy(rdev->uvd.saved_bo, ptr, size); 2049cc2e0e9SChristian König 2059cc2e0e9SChristian König return 0; 2069cc2e0e9SChristian König } 2079cc2e0e9SChristian König 2089cc2e0e9SChristian König int radeon_uvd_resume(struct radeon_device *rdev) 2099cc2e0e9SChristian König { 2104ad9c1c7SChristian König unsigned size; 2114ad9c1c7SChristian König void *ptr; 2124ad9c1c7SChristian König 2139cc2e0e9SChristian König if (rdev->uvd.vcpu_bo == NULL) 2149cc2e0e9SChristian König return -EINVAL; 2159cc2e0e9SChristian König 2164ad9c1c7SChristian König memcpy(rdev->uvd.cpu_addr, rdev->uvd_fw->data, rdev->uvd_fw->size); 2174ad9c1c7SChristian König 2184ad9c1c7SChristian König size = radeon_bo_size(rdev->uvd.vcpu_bo); 2194ad9c1c7SChristian König size -= rdev->uvd_fw->size; 2204ad9c1c7SChristian König 2214ad9c1c7SChristian König ptr = rdev->uvd.cpu_addr; 2224ad9c1c7SChristian König ptr += rdev->uvd_fw->size; 2234ad9c1c7SChristian König 2249cc2e0e9SChristian König if (rdev->uvd.saved_bo != NULL) { 2254ad9c1c7SChristian König memcpy(ptr, rdev->uvd.saved_bo, size); 2269cc2e0e9SChristian König kfree(rdev->uvd.saved_bo); 2279cc2e0e9SChristian König rdev->uvd.saved_bo = NULL; 2284ad9c1c7SChristian König } else 2294ad9c1c7SChristian König memset(ptr, 0, size); 2309cc2e0e9SChristian König 231f2ba57b5SChristian König return 0; 232f2ba57b5SChristian König } 233f2ba57b5SChristian König 234f2ba57b5SChristian König void radeon_uvd_force_into_uvd_segment(struct radeon_bo *rbo) 235f2ba57b5SChristian König { 236f2ba57b5SChristian König rbo->placement.fpfn = 0 >> PAGE_SHIFT; 237f2ba57b5SChristian König rbo->placement.lpfn = (256 * 1024 * 1024) >> PAGE_SHIFT; 238f2ba57b5SChristian König } 239f2ba57b5SChristian König 240f2ba57b5SChristian König void radeon_uvd_free_handles(struct radeon_device *rdev, struct drm_file *filp) 241f2ba57b5SChristian König { 242f2ba57b5SChristian König int i, r; 243f2ba57b5SChristian König for (i = 0; i < RADEON_MAX_UVD_HANDLES; ++i) { 244f2ba57b5SChristian König uint32_t handle = atomic_read(&rdev->uvd.handles[i]); 245641a0059SChristian König if (handle != 0 && rdev->uvd.filp[i] == filp) { 246f2ba57b5SChristian König struct radeon_fence *fence; 247f2ba57b5SChristian König 248c154a763SChristian König radeon_uvd_note_usage(rdev); 249c154a763SChristian König 250f2ba57b5SChristian König r = radeon_uvd_get_destroy_msg(rdev, 251f2ba57b5SChristian König R600_RING_TYPE_UVD_INDEX, handle, &fence); 252f2ba57b5SChristian König if (r) { 253f2ba57b5SChristian König DRM_ERROR("Error destroying UVD (%d)!\n", r); 254f2ba57b5SChristian König continue; 255f2ba57b5SChristian König } 256f2ba57b5SChristian König 257f2ba57b5SChristian König radeon_fence_wait(fence, false); 258f2ba57b5SChristian König radeon_fence_unref(&fence); 259f2ba57b5SChristian König 260f2ba57b5SChristian König rdev->uvd.filp[i] = NULL; 261f2ba57b5SChristian König atomic_set(&rdev->uvd.handles[i], 0); 262f2ba57b5SChristian König } 263f2ba57b5SChristian König } 264f2ba57b5SChristian König } 265f2ba57b5SChristian König 266f2ba57b5SChristian König static int radeon_uvd_cs_msg_decode(uint32_t *msg, unsigned buf_sizes[]) 267f2ba57b5SChristian König { 268f2ba57b5SChristian König unsigned stream_type = msg[4]; 269f2ba57b5SChristian König unsigned width = msg[6]; 270f2ba57b5SChristian König unsigned height = msg[7]; 271f2ba57b5SChristian König unsigned dpb_size = msg[9]; 272f2ba57b5SChristian König unsigned pitch = msg[28]; 273f2ba57b5SChristian König 274f2ba57b5SChristian König unsigned width_in_mb = width / 16; 275f2ba57b5SChristian König unsigned height_in_mb = ALIGN(height / 16, 2); 276f2ba57b5SChristian König 277f2ba57b5SChristian König unsigned image_size, tmp, min_dpb_size; 278f2ba57b5SChristian König 279f2ba57b5SChristian König image_size = width * height; 280f2ba57b5SChristian König image_size += image_size / 2; 281f2ba57b5SChristian König image_size = ALIGN(image_size, 1024); 282f2ba57b5SChristian König 283f2ba57b5SChristian König switch (stream_type) { 284f2ba57b5SChristian König case 0: /* H264 */ 285f2ba57b5SChristian König 286f2ba57b5SChristian König /* reference picture buffer */ 287f2ba57b5SChristian König min_dpb_size = image_size * 17; 288f2ba57b5SChristian König 289f2ba57b5SChristian König /* macroblock context buffer */ 290f2ba57b5SChristian König min_dpb_size += width_in_mb * height_in_mb * 17 * 192; 291f2ba57b5SChristian König 292f2ba57b5SChristian König /* IT surface buffer */ 293f2ba57b5SChristian König min_dpb_size += width_in_mb * height_in_mb * 32; 294f2ba57b5SChristian König break; 295f2ba57b5SChristian König 296f2ba57b5SChristian König case 1: /* VC1 */ 297f2ba57b5SChristian König 298f2ba57b5SChristian König /* reference picture buffer */ 299f2ba57b5SChristian König min_dpb_size = image_size * 3; 300f2ba57b5SChristian König 301f2ba57b5SChristian König /* CONTEXT_BUFFER */ 302f2ba57b5SChristian König min_dpb_size += width_in_mb * height_in_mb * 128; 303f2ba57b5SChristian König 304f2ba57b5SChristian König /* IT surface buffer */ 305f2ba57b5SChristian König min_dpb_size += width_in_mb * 64; 306f2ba57b5SChristian König 307f2ba57b5SChristian König /* DB surface buffer */ 308f2ba57b5SChristian König min_dpb_size += width_in_mb * 128; 309f2ba57b5SChristian König 310f2ba57b5SChristian König /* BP */ 311f2ba57b5SChristian König tmp = max(width_in_mb, height_in_mb); 312f2ba57b5SChristian König min_dpb_size += ALIGN(tmp * 7 * 16, 64); 313f2ba57b5SChristian König break; 314f2ba57b5SChristian König 315f2ba57b5SChristian König case 3: /* MPEG2 */ 316f2ba57b5SChristian König 317f2ba57b5SChristian König /* reference picture buffer */ 318f2ba57b5SChristian König min_dpb_size = image_size * 3; 319f2ba57b5SChristian König break; 320f2ba57b5SChristian König 321f2ba57b5SChristian König case 4: /* MPEG4 */ 322f2ba57b5SChristian König 323f2ba57b5SChristian König /* reference picture buffer */ 324f2ba57b5SChristian König min_dpb_size = image_size * 3; 325f2ba57b5SChristian König 326f2ba57b5SChristian König /* CM */ 327f2ba57b5SChristian König min_dpb_size += width_in_mb * height_in_mb * 64; 328f2ba57b5SChristian König 329f2ba57b5SChristian König /* IT surface buffer */ 330f2ba57b5SChristian König min_dpb_size += ALIGN(width_in_mb * height_in_mb * 32, 64); 331f2ba57b5SChristian König break; 332f2ba57b5SChristian König 333f2ba57b5SChristian König default: 334f2ba57b5SChristian König DRM_ERROR("UVD codec not handled %d!\n", stream_type); 335f2ba57b5SChristian König return -EINVAL; 336f2ba57b5SChristian König } 337f2ba57b5SChristian König 338f2ba57b5SChristian König if (width > pitch) { 339f2ba57b5SChristian König DRM_ERROR("Invalid UVD decoding target pitch!\n"); 340f2ba57b5SChristian König return -EINVAL; 341f2ba57b5SChristian König } 342f2ba57b5SChristian König 343f2ba57b5SChristian König if (dpb_size < min_dpb_size) { 344f2ba57b5SChristian König DRM_ERROR("Invalid dpb_size in UVD message (%d / %d)!\n", 345f2ba57b5SChristian König dpb_size, min_dpb_size); 346f2ba57b5SChristian König return -EINVAL; 347f2ba57b5SChristian König } 348f2ba57b5SChristian König 349f2ba57b5SChristian König buf_sizes[0x1] = dpb_size; 350f2ba57b5SChristian König buf_sizes[0x2] = image_size; 351f2ba57b5SChristian König return 0; 352f2ba57b5SChristian König } 353f2ba57b5SChristian König 354f2ba57b5SChristian König static int radeon_uvd_cs_msg(struct radeon_cs_parser *p, struct radeon_bo *bo, 355f2ba57b5SChristian König unsigned offset, unsigned buf_sizes[]) 356f2ba57b5SChristian König { 357f2ba57b5SChristian König int32_t *msg, msg_type, handle; 35885a129caSAlex Deucher unsigned img_size = 0; 359f2ba57b5SChristian König void *ptr; 360f2ba57b5SChristian König 361f2ba57b5SChristian König int i, r; 362f2ba57b5SChristian König 363f2ba57b5SChristian König if (offset & 0x3F) { 364f2ba57b5SChristian König DRM_ERROR("UVD messages must be 64 byte aligned!\n"); 365f2ba57b5SChristian König return -EINVAL; 366f2ba57b5SChristian König } 367f2ba57b5SChristian König 368112a6d0cSChristian König if (bo->tbo.sync_obj) { 369112a6d0cSChristian König r = radeon_fence_wait(bo->tbo.sync_obj, false); 370112a6d0cSChristian König if (r) { 371112a6d0cSChristian König DRM_ERROR("Failed waiting for UVD message (%d)!\n", r); 372112a6d0cSChristian König return r; 373112a6d0cSChristian König } 374112a6d0cSChristian König } 375112a6d0cSChristian König 376f2ba57b5SChristian König r = radeon_bo_kmap(bo, &ptr); 37756cc2c15SChristian König if (r) { 37856cc2c15SChristian König DRM_ERROR("Failed mapping the UVD message (%d)!\n", r); 379f2ba57b5SChristian König return r; 38056cc2c15SChristian König } 381f2ba57b5SChristian König 382f2ba57b5SChristian König msg = ptr + offset; 383f2ba57b5SChristian König 384f2ba57b5SChristian König msg_type = msg[1]; 385f2ba57b5SChristian König handle = msg[2]; 386f2ba57b5SChristian König 387f2ba57b5SChristian König if (handle == 0) { 388f2ba57b5SChristian König DRM_ERROR("Invalid UVD handle!\n"); 389f2ba57b5SChristian König return -EINVAL; 390f2ba57b5SChristian König } 391f2ba57b5SChristian König 392f2ba57b5SChristian König if (msg_type == 1) { 393f2ba57b5SChristian König /* it's a decode msg, calc buffer sizes */ 394f2ba57b5SChristian König r = radeon_uvd_cs_msg_decode(msg, buf_sizes); 39585a129caSAlex Deucher /* calc image size (width * height) */ 39685a129caSAlex Deucher img_size = msg[6] * msg[7]; 397f2ba57b5SChristian König radeon_bo_kunmap(bo); 398f2ba57b5SChristian König if (r) 399f2ba57b5SChristian König return r; 400f2ba57b5SChristian König 401f2ba57b5SChristian König } else if (msg_type == 2) { 402f2ba57b5SChristian König /* it's a destroy msg, free the handle */ 403f2ba57b5SChristian König for (i = 0; i < RADEON_MAX_UVD_HANDLES; ++i) 404f2ba57b5SChristian König atomic_cmpxchg(&p->rdev->uvd.handles[i], handle, 0); 405f2ba57b5SChristian König radeon_bo_kunmap(bo); 406f2ba57b5SChristian König return 0; 407f2ba57b5SChristian König } else { 40885a129caSAlex Deucher /* it's a create msg, calc image size (width * height) */ 40985a129caSAlex Deucher img_size = msg[7] * msg[8]; 410f2ba57b5SChristian König radeon_bo_kunmap(bo); 41156cc2c15SChristian König 41256cc2c15SChristian König if (msg_type != 0) { 41356cc2c15SChristian König DRM_ERROR("Illegal UVD message type (%d)!\n", msg_type); 41456cc2c15SChristian König return -EINVAL; 41556cc2c15SChristian König } 41656cc2c15SChristian König 41756cc2c15SChristian König /* it's a create msg, no special handling needed */ 418f2ba57b5SChristian König } 419f2ba57b5SChristian König 420f2ba57b5SChristian König /* create or decode, validate the handle */ 421f2ba57b5SChristian König for (i = 0; i < RADEON_MAX_UVD_HANDLES; ++i) { 422f2ba57b5SChristian König if (atomic_read(&p->rdev->uvd.handles[i]) == handle) 423f2ba57b5SChristian König return 0; 424f2ba57b5SChristian König } 425f2ba57b5SChristian König 426f2ba57b5SChristian König /* handle not found try to alloc a new one */ 427f2ba57b5SChristian König for (i = 0; i < RADEON_MAX_UVD_HANDLES; ++i) { 428f2ba57b5SChristian König if (!atomic_cmpxchg(&p->rdev->uvd.handles[i], 0, handle)) { 429f2ba57b5SChristian König p->rdev->uvd.filp[i] = p->filp; 43085a129caSAlex Deucher p->rdev->uvd.img_size[i] = img_size; 431f2ba57b5SChristian König return 0; 432f2ba57b5SChristian König } 433f2ba57b5SChristian König } 434f2ba57b5SChristian König 435f2ba57b5SChristian König DRM_ERROR("No more free UVD handles!\n"); 436f2ba57b5SChristian König return -EINVAL; 437f2ba57b5SChristian König } 438f2ba57b5SChristian König 439f2ba57b5SChristian König static int radeon_uvd_cs_reloc(struct radeon_cs_parser *p, 440f2ba57b5SChristian König int data0, int data1, 44156cc2c15SChristian König unsigned buf_sizes[], bool *has_msg_cmd) 442f2ba57b5SChristian König { 443f2ba57b5SChristian König struct radeon_cs_chunk *relocs_chunk; 444f2ba57b5SChristian König struct radeon_cs_reloc *reloc; 445f2ba57b5SChristian König unsigned idx, cmd, offset; 446f2ba57b5SChristian König uint64_t start, end; 447f2ba57b5SChristian König int r; 448f2ba57b5SChristian König 449f2ba57b5SChristian König relocs_chunk = &p->chunks[p->chunk_relocs_idx]; 450f2ba57b5SChristian König offset = radeon_get_ib_value(p, data0); 451f2ba57b5SChristian König idx = radeon_get_ib_value(p, data1); 452f2ba57b5SChristian König if (idx >= relocs_chunk->length_dw) { 453f2ba57b5SChristian König DRM_ERROR("Relocs at %d after relocations chunk end %d !\n", 454f2ba57b5SChristian König idx, relocs_chunk->length_dw); 455f2ba57b5SChristian König return -EINVAL; 456f2ba57b5SChristian König } 457f2ba57b5SChristian König 458f2ba57b5SChristian König reloc = p->relocs_ptr[(idx / 4)]; 459df0af440SChristian König start = reloc->gpu_offset; 460f2ba57b5SChristian König end = start + radeon_bo_size(reloc->robj); 461f2ba57b5SChristian König start += offset; 462f2ba57b5SChristian König 463f2ba57b5SChristian König p->ib.ptr[data0] = start & 0xFFFFFFFF; 464f2ba57b5SChristian König p->ib.ptr[data1] = start >> 32; 465f2ba57b5SChristian König 466f2ba57b5SChristian König cmd = radeon_get_ib_value(p, p->idx) >> 1; 467f2ba57b5SChristian König 468f2ba57b5SChristian König if (cmd < 0x4) { 469695daf1aSLeo Liu if (end <= start) { 470695daf1aSLeo Liu DRM_ERROR("invalid reloc offset %X!\n", offset); 471695daf1aSLeo Liu return -EINVAL; 472695daf1aSLeo Liu } 473f2ba57b5SChristian König if ((end - start) < buf_sizes[cmd]) { 47456cc2c15SChristian König DRM_ERROR("buffer (%d) to small (%d / %d)!\n", cmd, 475f2ba57b5SChristian König (unsigned)(end - start), buf_sizes[cmd]); 476f2ba57b5SChristian König return -EINVAL; 477f2ba57b5SChristian König } 478f2ba57b5SChristian König 479f2ba57b5SChristian König } else if (cmd != 0x100) { 480f2ba57b5SChristian König DRM_ERROR("invalid UVD command %X!\n", cmd); 481f2ba57b5SChristian König return -EINVAL; 482f2ba57b5SChristian König } 483f2ba57b5SChristian König 484bae651dbSChristian König if ((start >> 28) != ((end - 1) >> 28)) { 485f2ba57b5SChristian König DRM_ERROR("reloc %LX-%LX crossing 256MB boundary!\n", 486f2ba57b5SChristian König start, end); 487f2ba57b5SChristian König return -EINVAL; 488f2ba57b5SChristian König } 489f2ba57b5SChristian König 490bcf6f1e9SChristian König /* TODO: is this still necessary on NI+ ? */ 491bcf6f1e9SChristian König if ((cmd == 0 || cmd == 0x3) && 492a92c7d55SChristian König (start >> 28) != (p->rdev->uvd.gpu_addr >> 28)) { 493a92c7d55SChristian König DRM_ERROR("msg/fb buffer %LX-%LX out of 256MB segment!\n", 494a92c7d55SChristian König start, end); 495a92c7d55SChristian König return -EINVAL; 496a92c7d55SChristian König } 497a92c7d55SChristian König 498a92c7d55SChristian König if (cmd == 0) { 49956cc2c15SChristian König if (*has_msg_cmd) { 50056cc2c15SChristian König DRM_ERROR("More than one message in a UVD-IB!\n"); 50156cc2c15SChristian König return -EINVAL; 50256cc2c15SChristian König } 50356cc2c15SChristian König *has_msg_cmd = true; 504a92c7d55SChristian König r = radeon_uvd_cs_msg(p, reloc->robj, offset, buf_sizes); 505a92c7d55SChristian König if (r) 506a92c7d55SChristian König return r; 50756cc2c15SChristian König } else if (!*has_msg_cmd) { 50856cc2c15SChristian König DRM_ERROR("Message needed before other commands are send!\n"); 50956cc2c15SChristian König return -EINVAL; 510a92c7d55SChristian König } 511a92c7d55SChristian König 512f2ba57b5SChristian König return 0; 513f2ba57b5SChristian König } 514f2ba57b5SChristian König 515f2ba57b5SChristian König static int radeon_uvd_cs_reg(struct radeon_cs_parser *p, 516f2ba57b5SChristian König struct radeon_cs_packet *pkt, 517f2ba57b5SChristian König int *data0, int *data1, 51856cc2c15SChristian König unsigned buf_sizes[], 51956cc2c15SChristian König bool *has_msg_cmd) 520f2ba57b5SChristian König { 521f2ba57b5SChristian König int i, r; 522f2ba57b5SChristian König 523f2ba57b5SChristian König p->idx++; 524f2ba57b5SChristian König for (i = 0; i <= pkt->count; ++i) { 525f2ba57b5SChristian König switch (pkt->reg + i*4) { 526f2ba57b5SChristian König case UVD_GPCOM_VCPU_DATA0: 527f2ba57b5SChristian König *data0 = p->idx; 528f2ba57b5SChristian König break; 529f2ba57b5SChristian König case UVD_GPCOM_VCPU_DATA1: 530f2ba57b5SChristian König *data1 = p->idx; 531f2ba57b5SChristian König break; 532f2ba57b5SChristian König case UVD_GPCOM_VCPU_CMD: 53356cc2c15SChristian König r = radeon_uvd_cs_reloc(p, *data0, *data1, 53456cc2c15SChristian König buf_sizes, has_msg_cmd); 535f2ba57b5SChristian König if (r) 536f2ba57b5SChristian König return r; 537f2ba57b5SChristian König break; 538f2ba57b5SChristian König case UVD_ENGINE_CNTL: 539f2ba57b5SChristian König break; 540f2ba57b5SChristian König default: 541f2ba57b5SChristian König DRM_ERROR("Invalid reg 0x%X!\n", 542f2ba57b5SChristian König pkt->reg + i*4); 543f2ba57b5SChristian König return -EINVAL; 544f2ba57b5SChristian König } 545f2ba57b5SChristian König p->idx++; 546f2ba57b5SChristian König } 547f2ba57b5SChristian König return 0; 548f2ba57b5SChristian König } 549f2ba57b5SChristian König 550f2ba57b5SChristian König int radeon_uvd_cs_parse(struct radeon_cs_parser *p) 551f2ba57b5SChristian König { 552f2ba57b5SChristian König struct radeon_cs_packet pkt; 553f2ba57b5SChristian König int r, data0 = 0, data1 = 0; 554f2ba57b5SChristian König 55556cc2c15SChristian König /* does the IB has a msg command */ 55656cc2c15SChristian König bool has_msg_cmd = false; 55756cc2c15SChristian König 558f2ba57b5SChristian König /* minimum buffer sizes */ 559f2ba57b5SChristian König unsigned buf_sizes[] = { 560f2ba57b5SChristian König [0x00000000] = 2048, 561f2ba57b5SChristian König [0x00000001] = 32 * 1024 * 1024, 562f2ba57b5SChristian König [0x00000002] = 2048 * 1152 * 3, 563f2ba57b5SChristian König [0x00000003] = 2048, 564f2ba57b5SChristian König }; 565f2ba57b5SChristian König 566f2ba57b5SChristian König if (p->chunks[p->chunk_ib_idx].length_dw % 16) { 567f2ba57b5SChristian König DRM_ERROR("UVD IB length (%d) not 16 dwords aligned!\n", 568f2ba57b5SChristian König p->chunks[p->chunk_ib_idx].length_dw); 569f2ba57b5SChristian König return -EINVAL; 570f2ba57b5SChristian König } 571f2ba57b5SChristian König 572f2ba57b5SChristian König if (p->chunk_relocs_idx == -1) { 573f2ba57b5SChristian König DRM_ERROR("No relocation chunk !\n"); 574f2ba57b5SChristian König return -EINVAL; 575f2ba57b5SChristian König } 576f2ba57b5SChristian König 577f2ba57b5SChristian König 578f2ba57b5SChristian König do { 579f2ba57b5SChristian König r = radeon_cs_packet_parse(p, &pkt, p->idx); 580f2ba57b5SChristian König if (r) 581f2ba57b5SChristian König return r; 582f2ba57b5SChristian König switch (pkt.type) { 583f2ba57b5SChristian König case RADEON_PACKET_TYPE0: 58456cc2c15SChristian König r = radeon_uvd_cs_reg(p, &pkt, &data0, &data1, 58556cc2c15SChristian König buf_sizes, &has_msg_cmd); 586f2ba57b5SChristian König if (r) 587f2ba57b5SChristian König return r; 588f2ba57b5SChristian König break; 589f2ba57b5SChristian König case RADEON_PACKET_TYPE2: 590f2ba57b5SChristian König p->idx += pkt.count + 2; 591f2ba57b5SChristian König break; 592f2ba57b5SChristian König default: 593f2ba57b5SChristian König DRM_ERROR("Unknown packet type %d !\n", pkt.type); 594f2ba57b5SChristian König return -EINVAL; 595f2ba57b5SChristian König } 596f2ba57b5SChristian König } while (p->idx < p->chunks[p->chunk_ib_idx].length_dw); 59756cc2c15SChristian König 59856cc2c15SChristian König if (!has_msg_cmd) { 59956cc2c15SChristian König DRM_ERROR("UVD-IBs need a msg command!\n"); 60056cc2c15SChristian König return -EINVAL; 60156cc2c15SChristian König } 60256cc2c15SChristian König 603f2ba57b5SChristian König return 0; 604f2ba57b5SChristian König } 605f2ba57b5SChristian König 606f2ba57b5SChristian König static int radeon_uvd_send_msg(struct radeon_device *rdev, 607f2ba57b5SChristian König int ring, struct radeon_bo *bo, 608f2ba57b5SChristian König struct radeon_fence **fence) 609f2ba57b5SChristian König { 610f2ba57b5SChristian König struct ttm_validate_buffer tv; 611ecff665fSMaarten Lankhorst struct ww_acquire_ctx ticket; 612f2ba57b5SChristian König struct list_head head; 613f2ba57b5SChristian König struct radeon_ib ib; 614f2ba57b5SChristian König uint64_t addr; 615f2ba57b5SChristian König int i, r; 616f2ba57b5SChristian König 617f2ba57b5SChristian König memset(&tv, 0, sizeof(tv)); 618f2ba57b5SChristian König tv.bo = &bo->tbo; 619f2ba57b5SChristian König 620f2ba57b5SChristian König INIT_LIST_HEAD(&head); 621f2ba57b5SChristian König list_add(&tv.head, &head); 622f2ba57b5SChristian König 623ecff665fSMaarten Lankhorst r = ttm_eu_reserve_buffers(&ticket, &head); 624f2ba57b5SChristian König if (r) 625f2ba57b5SChristian König return r; 626f2ba57b5SChristian König 627f2ba57b5SChristian König radeon_ttm_placement_from_domain(bo, RADEON_GEM_DOMAIN_VRAM); 628f2ba57b5SChristian König radeon_uvd_force_into_uvd_segment(bo); 629f2ba57b5SChristian König 630f2ba57b5SChristian König r = ttm_bo_validate(&bo->tbo, &bo->placement, true, false); 631ecff665fSMaarten Lankhorst if (r) 632ecff665fSMaarten Lankhorst goto err; 633f2ba57b5SChristian König 634727ddc84SChristian König r = radeon_ib_get(rdev, ring, &ib, NULL, 64); 635ecff665fSMaarten Lankhorst if (r) 636ecff665fSMaarten Lankhorst goto err; 637f2ba57b5SChristian König 638f2ba57b5SChristian König addr = radeon_bo_gpu_offset(bo); 639f2ba57b5SChristian König ib.ptr[0] = PACKET0(UVD_GPCOM_VCPU_DATA0, 0); 640f2ba57b5SChristian König ib.ptr[1] = addr; 641f2ba57b5SChristian König ib.ptr[2] = PACKET0(UVD_GPCOM_VCPU_DATA1, 0); 642f2ba57b5SChristian König ib.ptr[3] = addr >> 32; 643f2ba57b5SChristian König ib.ptr[4] = PACKET0(UVD_GPCOM_VCPU_CMD, 0); 644f2ba57b5SChristian König ib.ptr[5] = 0; 645f2ba57b5SChristian König for (i = 6; i < 16; ++i) 646f2ba57b5SChristian König ib.ptr[i] = PACKET2(0); 647f2ba57b5SChristian König ib.length_dw = 16; 648f2ba57b5SChristian König 649f2ba57b5SChristian König r = radeon_ib_schedule(rdev, &ib, NULL); 650ecff665fSMaarten Lankhorst if (r) 651ecff665fSMaarten Lankhorst goto err; 652ecff665fSMaarten Lankhorst ttm_eu_fence_buffer_objects(&ticket, &head, ib.fence); 653f2ba57b5SChristian König 654f2ba57b5SChristian König if (fence) 655f2ba57b5SChristian König *fence = radeon_fence_ref(ib.fence); 656f2ba57b5SChristian König 657f2ba57b5SChristian König radeon_ib_free(rdev, &ib); 658f2ba57b5SChristian König radeon_bo_unref(&bo); 659f2ba57b5SChristian König return 0; 660ecff665fSMaarten Lankhorst 661ecff665fSMaarten Lankhorst err: 662ecff665fSMaarten Lankhorst ttm_eu_backoff_reservation(&ticket, &head); 663ecff665fSMaarten Lankhorst return r; 664f2ba57b5SChristian König } 665f2ba57b5SChristian König 666f2ba57b5SChristian König /* multiple fence commands without any stream commands in between can 667f2ba57b5SChristian König crash the vcpu so just try to emmit a dummy create/destroy msg to 668f2ba57b5SChristian König avoid this */ 669f2ba57b5SChristian König int radeon_uvd_get_create_msg(struct radeon_device *rdev, int ring, 670f2ba57b5SChristian König uint32_t handle, struct radeon_fence **fence) 671f2ba57b5SChristian König { 672f2ba57b5SChristian König struct radeon_bo *bo; 673f2ba57b5SChristian König uint32_t *msg; 674f2ba57b5SChristian König int r, i; 675f2ba57b5SChristian König 676f2ba57b5SChristian König r = radeon_bo_create(rdev, 1024, PAGE_SIZE, true, 677f2ba57b5SChristian König RADEON_GEM_DOMAIN_VRAM, NULL, &bo); 678f2ba57b5SChristian König if (r) 679f2ba57b5SChristian König return r; 680f2ba57b5SChristian König 681f2ba57b5SChristian König r = radeon_bo_reserve(bo, false); 682f2ba57b5SChristian König if (r) { 683f2ba57b5SChristian König radeon_bo_unref(&bo); 684f2ba57b5SChristian König return r; 685f2ba57b5SChristian König } 686f2ba57b5SChristian König 687f2ba57b5SChristian König r = radeon_bo_kmap(bo, (void **)&msg); 688f2ba57b5SChristian König if (r) { 689f2ba57b5SChristian König radeon_bo_unreserve(bo); 690f2ba57b5SChristian König radeon_bo_unref(&bo); 691f2ba57b5SChristian König return r; 692f2ba57b5SChristian König } 693f2ba57b5SChristian König 694f2ba57b5SChristian König /* stitch together an UVD create msg */ 6959b1be4dcSAlex Deucher msg[0] = cpu_to_le32(0x00000de4); 6969b1be4dcSAlex Deucher msg[1] = cpu_to_le32(0x00000000); 6979b1be4dcSAlex Deucher msg[2] = cpu_to_le32(handle); 6989b1be4dcSAlex Deucher msg[3] = cpu_to_le32(0x00000000); 6999b1be4dcSAlex Deucher msg[4] = cpu_to_le32(0x00000000); 7009b1be4dcSAlex Deucher msg[5] = cpu_to_le32(0x00000000); 7019b1be4dcSAlex Deucher msg[6] = cpu_to_le32(0x00000000); 7029b1be4dcSAlex Deucher msg[7] = cpu_to_le32(0x00000780); 7039b1be4dcSAlex Deucher msg[8] = cpu_to_le32(0x00000440); 7049b1be4dcSAlex Deucher msg[9] = cpu_to_le32(0x00000000); 7059b1be4dcSAlex Deucher msg[10] = cpu_to_le32(0x01b37000); 706f2ba57b5SChristian König for (i = 11; i < 1024; ++i) 7079b1be4dcSAlex Deucher msg[i] = cpu_to_le32(0x0); 708f2ba57b5SChristian König 709f2ba57b5SChristian König radeon_bo_kunmap(bo); 710f2ba57b5SChristian König radeon_bo_unreserve(bo); 711f2ba57b5SChristian König 712f2ba57b5SChristian König return radeon_uvd_send_msg(rdev, ring, bo, fence); 713f2ba57b5SChristian König } 714f2ba57b5SChristian König 715f2ba57b5SChristian König int radeon_uvd_get_destroy_msg(struct radeon_device *rdev, int ring, 716f2ba57b5SChristian König uint32_t handle, struct radeon_fence **fence) 717f2ba57b5SChristian König { 718f2ba57b5SChristian König struct radeon_bo *bo; 719f2ba57b5SChristian König uint32_t *msg; 720f2ba57b5SChristian König int r, i; 721f2ba57b5SChristian König 722f2ba57b5SChristian König r = radeon_bo_create(rdev, 1024, PAGE_SIZE, true, 723f2ba57b5SChristian König RADEON_GEM_DOMAIN_VRAM, NULL, &bo); 724f2ba57b5SChristian König if (r) 725f2ba57b5SChristian König return r; 726f2ba57b5SChristian König 727f2ba57b5SChristian König r = radeon_bo_reserve(bo, false); 728f2ba57b5SChristian König if (r) { 729f2ba57b5SChristian König radeon_bo_unref(&bo); 730f2ba57b5SChristian König return r; 731f2ba57b5SChristian König } 732f2ba57b5SChristian König 733f2ba57b5SChristian König r = radeon_bo_kmap(bo, (void **)&msg); 734f2ba57b5SChristian König if (r) { 735f2ba57b5SChristian König radeon_bo_unreserve(bo); 736f2ba57b5SChristian König radeon_bo_unref(&bo); 737f2ba57b5SChristian König return r; 738f2ba57b5SChristian König } 739f2ba57b5SChristian König 740f2ba57b5SChristian König /* stitch together an UVD destroy msg */ 7419b1be4dcSAlex Deucher msg[0] = cpu_to_le32(0x00000de4); 7429b1be4dcSAlex Deucher msg[1] = cpu_to_le32(0x00000002); 7439b1be4dcSAlex Deucher msg[2] = cpu_to_le32(handle); 7449b1be4dcSAlex Deucher msg[3] = cpu_to_le32(0x00000000); 745f2ba57b5SChristian König for (i = 4; i < 1024; ++i) 7469b1be4dcSAlex Deucher msg[i] = cpu_to_le32(0x0); 747f2ba57b5SChristian König 748f2ba57b5SChristian König radeon_bo_kunmap(bo); 749f2ba57b5SChristian König radeon_bo_unreserve(bo); 750f2ba57b5SChristian König 751f2ba57b5SChristian König return radeon_uvd_send_msg(rdev, ring, bo, fence); 752f2ba57b5SChristian König } 75355b51c88SChristian König 75485a129caSAlex Deucher /** 75585a129caSAlex Deucher * radeon_uvd_count_handles - count number of open streams 75685a129caSAlex Deucher * 75785a129caSAlex Deucher * @rdev: radeon_device pointer 75885a129caSAlex Deucher * @sd: number of SD streams 75985a129caSAlex Deucher * @hd: number of HD streams 76085a129caSAlex Deucher * 76185a129caSAlex Deucher * Count the number of open SD/HD streams as a hint for power mangement 76285a129caSAlex Deucher */ 76385a129caSAlex Deucher static void radeon_uvd_count_handles(struct radeon_device *rdev, 76485a129caSAlex Deucher unsigned *sd, unsigned *hd) 76585a129caSAlex Deucher { 76685a129caSAlex Deucher unsigned i; 76785a129caSAlex Deucher 76885a129caSAlex Deucher *sd = 0; 76985a129caSAlex Deucher *hd = 0; 77085a129caSAlex Deucher 77185a129caSAlex Deucher for (i = 0; i < RADEON_MAX_UVD_HANDLES; ++i) { 77285a129caSAlex Deucher if (!atomic_read(&rdev->uvd.handles[i])) 77385a129caSAlex Deucher continue; 77485a129caSAlex Deucher 77585a129caSAlex Deucher if (rdev->uvd.img_size[i] >= 720*576) 77685a129caSAlex Deucher ++(*hd); 77785a129caSAlex Deucher else 77885a129caSAlex Deucher ++(*sd); 77985a129caSAlex Deucher } 78085a129caSAlex Deucher } 78185a129caSAlex Deucher 78255b51c88SChristian König static void radeon_uvd_idle_work_handler(struct work_struct *work) 78355b51c88SChristian König { 78455b51c88SChristian König struct radeon_device *rdev = 78555b51c88SChristian König container_of(work, struct radeon_device, uvd.idle_work.work); 78655b51c88SChristian König 7878a227555SAlex Deucher if (radeon_fence_count_emitted(rdev, R600_RING_TYPE_UVD_INDEX) == 0) { 7888a227555SAlex Deucher if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) { 7898158eb9eSChristian König radeon_uvd_count_handles(rdev, &rdev->pm.dpm.sd, 7908158eb9eSChristian König &rdev->pm.dpm.hd); 791ce3537d5SAlex Deucher radeon_dpm_enable_uvd(rdev, false); 7928a227555SAlex Deucher } else { 79355b51c88SChristian König radeon_set_uvd_clocks(rdev, 0, 0); 7948a227555SAlex Deucher } 7958a227555SAlex Deucher } else { 79655b51c88SChristian König schedule_delayed_work(&rdev->uvd.idle_work, 79755b51c88SChristian König msecs_to_jiffies(UVD_IDLE_TIMEOUT_MS)); 79855b51c88SChristian König } 7998a227555SAlex Deucher } 80055b51c88SChristian König 80155b51c88SChristian König void radeon_uvd_note_usage(struct radeon_device *rdev) 80255b51c88SChristian König { 803ce3537d5SAlex Deucher bool streams_changed = false; 80455b51c88SChristian König bool set_clocks = !cancel_delayed_work_sync(&rdev->uvd.idle_work); 80555b51c88SChristian König set_clocks &= schedule_delayed_work(&rdev->uvd.idle_work, 80655b51c88SChristian König msecs_to_jiffies(UVD_IDLE_TIMEOUT_MS)); 807ce3537d5SAlex Deucher 8088a227555SAlex Deucher if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) { 809ce3537d5SAlex Deucher unsigned hd = 0, sd = 0; 810ce3537d5SAlex Deucher radeon_uvd_count_handles(rdev, &sd, &hd); 811ce3537d5SAlex Deucher if ((rdev->pm.dpm.sd != sd) || 812ce3537d5SAlex Deucher (rdev->pm.dpm.hd != hd)) { 813ce3537d5SAlex Deucher rdev->pm.dpm.sd = sd; 814ce3537d5SAlex Deucher rdev->pm.dpm.hd = hd; 8150690a229SAlex Deucher /* disable this for now */ 8160690a229SAlex Deucher /*streams_changed = true;*/ 817ce3537d5SAlex Deucher } 818ce3537d5SAlex Deucher } 819ce3537d5SAlex Deucher 820ce3537d5SAlex Deucher if (set_clocks || streams_changed) { 821ce3537d5SAlex Deucher if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) { 822ce3537d5SAlex Deucher radeon_dpm_enable_uvd(rdev, true); 8238a227555SAlex Deucher } else { 82455b51c88SChristian König radeon_set_uvd_clocks(rdev, 53300, 40000); 82555b51c88SChristian König } 8268a227555SAlex Deucher } 8278a227555SAlex Deucher } 828facd112dSChristian König 829facd112dSChristian König static unsigned radeon_uvd_calc_upll_post_div(unsigned vco_freq, 830facd112dSChristian König unsigned target_freq, 831facd112dSChristian König unsigned pd_min, 832facd112dSChristian König unsigned pd_even) 833facd112dSChristian König { 834facd112dSChristian König unsigned post_div = vco_freq / target_freq; 835facd112dSChristian König 836facd112dSChristian König /* adjust to post divider minimum value */ 837facd112dSChristian König if (post_div < pd_min) 838facd112dSChristian König post_div = pd_min; 839facd112dSChristian König 840facd112dSChristian König /* we alway need a frequency less than or equal the target */ 841facd112dSChristian König if ((vco_freq / post_div) > target_freq) 842facd112dSChristian König post_div += 1; 843facd112dSChristian König 844facd112dSChristian König /* post dividers above a certain value must be even */ 845facd112dSChristian König if (post_div > pd_even && post_div % 2) 846facd112dSChristian König post_div += 1; 847facd112dSChristian König 848facd112dSChristian König return post_div; 849facd112dSChristian König } 850facd112dSChristian König 851facd112dSChristian König /** 852facd112dSChristian König * radeon_uvd_calc_upll_dividers - calc UPLL clock dividers 853facd112dSChristian König * 854facd112dSChristian König * @rdev: radeon_device pointer 855facd112dSChristian König * @vclk: wanted VCLK 856facd112dSChristian König * @dclk: wanted DCLK 857facd112dSChristian König * @vco_min: minimum VCO frequency 858facd112dSChristian König * @vco_max: maximum VCO frequency 859facd112dSChristian König * @fb_factor: factor to multiply vco freq with 860facd112dSChristian König * @fb_mask: limit and bitmask for feedback divider 861facd112dSChristian König * @pd_min: post divider minimum 862facd112dSChristian König * @pd_max: post divider maximum 863facd112dSChristian König * @pd_even: post divider must be even above this value 864facd112dSChristian König * @optimal_fb_div: resulting feedback divider 865facd112dSChristian König * @optimal_vclk_div: resulting vclk post divider 866facd112dSChristian König * @optimal_dclk_div: resulting dclk post divider 867facd112dSChristian König * 868facd112dSChristian König * Calculate dividers for UVDs UPLL (R6xx-SI, except APUs). 869facd112dSChristian König * Returns zero on success -EINVAL on error. 870facd112dSChristian König */ 871facd112dSChristian König int radeon_uvd_calc_upll_dividers(struct radeon_device *rdev, 872facd112dSChristian König unsigned vclk, unsigned dclk, 873facd112dSChristian König unsigned vco_min, unsigned vco_max, 874facd112dSChristian König unsigned fb_factor, unsigned fb_mask, 875facd112dSChristian König unsigned pd_min, unsigned pd_max, 876facd112dSChristian König unsigned pd_even, 877facd112dSChristian König unsigned *optimal_fb_div, 878facd112dSChristian König unsigned *optimal_vclk_div, 879facd112dSChristian König unsigned *optimal_dclk_div) 880facd112dSChristian König { 881facd112dSChristian König unsigned vco_freq, ref_freq = rdev->clock.spll.reference_freq; 882facd112dSChristian König 883facd112dSChristian König /* start off with something large */ 884facd112dSChristian König unsigned optimal_score = ~0; 885facd112dSChristian König 886facd112dSChristian König /* loop through vco from low to high */ 887facd112dSChristian König vco_min = max(max(vco_min, vclk), dclk); 888facd112dSChristian König for (vco_freq = vco_min; vco_freq <= vco_max; vco_freq += 100) { 889facd112dSChristian König 890facd112dSChristian König uint64_t fb_div = (uint64_t)vco_freq * fb_factor; 891facd112dSChristian König unsigned vclk_div, dclk_div, score; 892facd112dSChristian König 893facd112dSChristian König do_div(fb_div, ref_freq); 894facd112dSChristian König 895facd112dSChristian König /* fb div out of range ? */ 896facd112dSChristian König if (fb_div > fb_mask) 897facd112dSChristian König break; /* it can oly get worse */ 898facd112dSChristian König 899facd112dSChristian König fb_div &= fb_mask; 900facd112dSChristian König 901facd112dSChristian König /* calc vclk divider with current vco freq */ 902facd112dSChristian König vclk_div = radeon_uvd_calc_upll_post_div(vco_freq, vclk, 903facd112dSChristian König pd_min, pd_even); 904facd112dSChristian König if (vclk_div > pd_max) 905facd112dSChristian König break; /* vco is too big, it has to stop */ 906facd112dSChristian König 907facd112dSChristian König /* calc dclk divider with current vco freq */ 908facd112dSChristian König dclk_div = radeon_uvd_calc_upll_post_div(vco_freq, dclk, 909facd112dSChristian König pd_min, pd_even); 910facd112dSChristian König if (vclk_div > pd_max) 911facd112dSChristian König break; /* vco is too big, it has to stop */ 912facd112dSChristian König 913facd112dSChristian König /* calc score with current vco freq */ 914facd112dSChristian König score = vclk - (vco_freq / vclk_div) + dclk - (vco_freq / dclk_div); 915facd112dSChristian König 916facd112dSChristian König /* determine if this vco setting is better than current optimal settings */ 917facd112dSChristian König if (score < optimal_score) { 918facd112dSChristian König *optimal_fb_div = fb_div; 919facd112dSChristian König *optimal_vclk_div = vclk_div; 920facd112dSChristian König *optimal_dclk_div = dclk_div; 921facd112dSChristian König optimal_score = score; 922facd112dSChristian König if (optimal_score == 0) 923facd112dSChristian König break; /* it can't get better than this */ 924facd112dSChristian König } 925facd112dSChristian König } 926facd112dSChristian König 927facd112dSChristian König /* did we found a valid setup ? */ 928facd112dSChristian König if (optimal_score == ~0) 929facd112dSChristian König return -EINVAL; 930facd112dSChristian König 931facd112dSChristian König return 0; 932facd112dSChristian König } 933facd112dSChristian König 934facd112dSChristian König int radeon_uvd_send_upll_ctlreq(struct radeon_device *rdev, 935facd112dSChristian König unsigned cg_upll_func_cntl) 936facd112dSChristian König { 937facd112dSChristian König unsigned i; 938facd112dSChristian König 939facd112dSChristian König /* make sure UPLL_CTLREQ is deasserted */ 940facd112dSChristian König WREG32_P(cg_upll_func_cntl, 0, ~UPLL_CTLREQ_MASK); 941facd112dSChristian König 942facd112dSChristian König mdelay(10); 943facd112dSChristian König 944facd112dSChristian König /* assert UPLL_CTLREQ */ 945facd112dSChristian König WREG32_P(cg_upll_func_cntl, UPLL_CTLREQ_MASK, ~UPLL_CTLREQ_MASK); 946facd112dSChristian König 947facd112dSChristian König /* wait for CTLACK and CTLACK2 to get asserted */ 948facd112dSChristian König for (i = 0; i < 100; ++i) { 949facd112dSChristian König uint32_t mask = UPLL_CTLACK_MASK | UPLL_CTLACK2_MASK; 950facd112dSChristian König if ((RREG32(cg_upll_func_cntl) & mask) == mask) 951facd112dSChristian König break; 952facd112dSChristian König mdelay(10); 953facd112dSChristian König } 954facd112dSChristian König 955facd112dSChristian König /* deassert UPLL_CTLREQ */ 956facd112dSChristian König WREG32_P(cg_upll_func_cntl, 0, ~UPLL_CTLREQ_MASK); 957facd112dSChristian König 958facd112dSChristian König if (i == 100) { 959facd112dSChristian König DRM_ERROR("Timeout setting UVD clocks!\n"); 960facd112dSChristian König return -ETIMEDOUT; 961facd112dSChristian König } 962facd112dSChristian König 963facd112dSChristian König return 0; 964facd112dSChristian König } 965