2015-04-20 16:55:21 -04:00
/*
* Copyright 2011 Advanced Micro Devices , Inc .
* All Rights Reserved .
*
* Permission is hereby granted , free of charge , to any person obtaining a
* copy of this software and associated documentation files ( the
* " Software " ) , to deal in the Software without restriction , including
* without limitation the rights to use , copy , modify , merge , publish ,
* distribute , sub license , and / or sell copies of the Software , and to
* permit persons to whom the Software is furnished to do so , subject to
* the following conditions :
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NON - INFRINGEMENT . IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS , AUTHORS AND / OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM ,
* DAMAGES OR OTHER LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR
* OTHERWISE , ARISING FROM , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE .
*
* The above copyright notice and this permission notice ( including the
* next paragraph ) shall be included in all copies or substantial portions
* of the Software .
*
*/
/*
* Authors :
* Christian König < deathsimple @ vodafone . de >
*/
# include <linux/firmware.h>
# include <linux/module.h>
# include <drm/drmP.h>
# include <drm/drm.h>
# include "amdgpu.h"
# include "amdgpu_pm.h"
# include "amdgpu_uvd.h"
# include "cikd.h"
# include "uvd/uvd_4_2_d.h"
/* 1 second timeout */
2016-07-01 17:45:49 +02:00
# define UVD_IDLE_TIMEOUT msecs_to_jiffies(1000)
2016-07-26 12:05:40 +02:00
/* Firmware versions for VI */
# define FW_1_65_10 ((1 << 24) | (65 << 16) | (10 << 8))
# define FW_1_87_11 ((1 << 24) | (87 << 16) | (11 << 8))
# define FW_1_87_12 ((1 << 24) | (87 << 16) | (12 << 8))
# define FW_1_37_15 ((1 << 24) | (37 << 16) | (15 << 8))
2016-05-11 13:29:48 -04:00
/* Polaris10/11 firmware version */
2016-07-26 12:05:40 +02:00
# define FW_1_66_16 ((1 << 24) | (66 << 16) | (16 << 8))
2015-04-20 16:55:21 -04:00
/* Firmware Names */
# ifdef CONFIG_DRM_AMDGPU_CIK
# define FIRMWARE_BONAIRE "radeon / bonaire_uvd.bin"
2016-05-03 15:54:54 +02:00
# define FIRMWARE_KABINI "radeon / kabini_uvd.bin"
# define FIRMWARE_KAVERI "radeon / kaveri_uvd.bin"
# define FIRMWARE_HAWAII "radeon / hawaii_uvd.bin"
2015-04-20 16:55:21 -04:00
# define FIRMWARE_MULLINS "radeon / mullins_uvd.bin"
# endif
2015-05-13 22:49:04 +08:00
# define FIRMWARE_TONGA "amdgpu / tonga_uvd.bin"
# define FIRMWARE_CARRIZO "amdgpu / carrizo_uvd.bin"
2015-07-08 17:32:15 +08:00
# define FIRMWARE_FIJI "amdgpu / fiji_uvd.bin"
2015-10-08 16:27:21 -04:00
# define FIRMWARE_STONEY "amdgpu / stoney_uvd.bin"
2016-03-14 18:33:29 -04:00
# define FIRMWARE_POLARIS10 "amdgpu / polaris10_uvd.bin"
2016-03-23 14:48:03 +08:00
# define FIRMWARE_POLARIS11 "amdgpu / polaris11_uvd.bin"
2016-12-14 15:32:28 -05:00
# define FIRMWARE_POLARIS12 "amdgpu / polaris12_uvd.bin"
2015-04-20 16:55:21 -04:00
2017-03-03 18:13:26 -05:00
# define FIRMWARE_VEGA10 "amdgpu / vega10_uvd.bin"
# define mmUVD_GPCOM_VCPU_DATA0_VEGA10 (0x03c4 + 0x7e00)
# define mmUVD_GPCOM_VCPU_DATA1_VEGA10 (0x03c5 + 0x7e00)
# define mmUVD_GPCOM_VCPU_CMD_VEGA10 (0x03c3 + 0x7e00)
# define mmUVD_NO_OP_VEGA10 (0x03ff + 0x7e00)
# define mmUVD_ENGINE_CNTL_VEGA10 (0x03c6 + 0x7e00)
2015-04-20 16:55:21 -04:00
/**
* amdgpu_uvd_cs_ctx - Command submission parser context
*
* Used for emulating virtual memory support on UVD 4.2 .
*/
struct amdgpu_uvd_cs_ctx {
struct amdgpu_cs_parser * parser ;
unsigned reg , count ;
unsigned data0 , data1 ;
unsigned idx ;
unsigned ib_idx ;
/* does the IB has a msg command */
bool has_msg_cmd ;
/* minimum buffer sizes */
unsigned * buf_sizes ;
} ;
# ifdef CONFIG_DRM_AMDGPU_CIK
MODULE_FIRMWARE ( FIRMWARE_BONAIRE ) ;
MODULE_FIRMWARE ( FIRMWARE_KABINI ) ;
MODULE_FIRMWARE ( FIRMWARE_KAVERI ) ;
MODULE_FIRMWARE ( FIRMWARE_HAWAII ) ;
MODULE_FIRMWARE ( FIRMWARE_MULLINS ) ;
# endif
MODULE_FIRMWARE ( FIRMWARE_TONGA ) ;
MODULE_FIRMWARE ( FIRMWARE_CARRIZO ) ;
2015-07-08 17:32:15 +08:00
MODULE_FIRMWARE ( FIRMWARE_FIJI ) ;
2015-10-08 16:27:21 -04:00
MODULE_FIRMWARE ( FIRMWARE_STONEY ) ;
2016-03-14 18:33:29 -04:00
MODULE_FIRMWARE ( FIRMWARE_POLARIS10 ) ;
MODULE_FIRMWARE ( FIRMWARE_POLARIS11 ) ;
2016-12-14 15:32:28 -05:00
MODULE_FIRMWARE ( FIRMWARE_POLARIS12 ) ;
2015-04-20 16:55:21 -04:00
2017-03-03 18:13:26 -05:00
MODULE_FIRMWARE ( FIRMWARE_VEGA10 ) ;
2015-04-20 16:55:21 -04:00
static void amdgpu_uvd_idle_work_handler ( struct work_struct * work ) ;
int amdgpu_uvd_sw_init ( struct amdgpu_device * adev )
{
2016-02-10 14:35:19 +01:00
struct amdgpu_ring * ring ;
struct amd_sched_rq * rq ;
2015-04-20 16:55:21 -04:00
unsigned long bo_size ;
const char * fw_name ;
const struct common_firmware_header * hdr ;
unsigned version_major , version_minor , family_id ;
int i , r ;
INIT_DELAYED_WORK ( & adev - > uvd . idle_work , amdgpu_uvd_idle_work_handler ) ;
switch ( adev - > asic_type ) {
# ifdef CONFIG_DRM_AMDGPU_CIK
case CHIP_BONAIRE :
fw_name = FIRMWARE_BONAIRE ;
break ;
case CHIP_KABINI :
fw_name = FIRMWARE_KABINI ;
break ;
case CHIP_KAVERI :
fw_name = FIRMWARE_KAVERI ;
break ;
case CHIP_HAWAII :
fw_name = FIRMWARE_HAWAII ;
break ;
case CHIP_MULLINS :
fw_name = FIRMWARE_MULLINS ;
break ;
# endif
case CHIP_TONGA :
fw_name = FIRMWARE_TONGA ;
break ;
2015-07-08 17:32:15 +08:00
case CHIP_FIJI :
fw_name = FIRMWARE_FIJI ;
break ;
2015-04-20 16:55:21 -04:00
case CHIP_CARRIZO :
fw_name = FIRMWARE_CARRIZO ;
break ;
2015-10-08 16:27:21 -04:00
case CHIP_STONEY :
fw_name = FIRMWARE_STONEY ;
break ;
2016-03-14 18:33:29 -04:00
case CHIP_POLARIS10 :
fw_name = FIRMWARE_POLARIS10 ;
2015-11-05 15:17:18 -05:00
break ;
2016-03-14 18:33:29 -04:00
case CHIP_POLARIS11 :
fw_name = FIRMWARE_POLARIS11 ;
2016-12-14 15:32:28 -05:00
break ;
2017-03-03 18:13:26 -05:00
case CHIP_VEGA10 :
fw_name = FIRMWARE_VEGA10 ;
break ;
2016-12-14 15:32:28 -05:00
case CHIP_POLARIS12 :
fw_name = FIRMWARE_POLARIS12 ;
2015-11-05 15:17:18 -05:00
break ;
2015-04-20 16:55:21 -04:00
default :
return - EINVAL ;
}
r = request_firmware ( & adev - > uvd . fw , fw_name , adev - > dev ) ;
if ( r ) {
dev_err ( adev - > dev , " amdgpu_uvd: Can't load firmware \" %s \" \n " ,
fw_name ) ;
return r ;
}
r = amdgpu_ucode_validate ( adev - > uvd . fw ) ;
if ( r ) {
dev_err ( adev - > dev , " amdgpu_uvd: Can't validate firmware \" %s \" \n " ,
fw_name ) ;
release_firmware ( adev - > uvd . fw ) ;
adev - > uvd . fw = NULL ;
return r ;
}
2016-04-12 13:46:15 +02:00
/* Set the default UVD handles that the firmware can handle */
adev - > uvd . max_handles = AMDGPU_DEFAULT_UVD_HANDLES ;
2015-04-20 16:55:21 -04:00
hdr = ( const struct common_firmware_header * ) adev - > uvd . fw - > data ;
family_id = le32_to_cpu ( hdr - > ucode_version ) & 0xff ;
version_major = ( le32_to_cpu ( hdr - > ucode_version ) > > 24 ) & 0xff ;
version_minor = ( le32_to_cpu ( hdr - > ucode_version ) > > 8 ) & 0xff ;
DRM_INFO ( " Found UVD firmware Version: %hu.%hu Family ID: %hu \n " ,
version_major , version_minor , family_id ) ;
2016-04-12 13:46:15 +02:00
/*
* Limit the number of UVD handles depending on microcode major
* and minor versions . The firmware version which has 40 UVD
* instances support is 1.80 . So all subsequent versions should
* also have the same support .
*/
if ( ( version_major > 0x01 ) | |
( ( version_major = = 0x01 ) & & ( version_minor > = 0x50 ) ) )
adev - > uvd . max_handles = AMDGPU_MAX_UVD_HANDLES ;
2016-04-18 16:05:04 -04:00
adev - > uvd . fw_version = ( ( version_major < < 24 ) | ( version_minor < < 16 ) |
( family_id < < 8 ) ) ;
2016-05-11 13:29:48 -04:00
if ( ( adev - > asic_type = = CHIP_POLARIS10 | |
adev - > asic_type = = CHIP_POLARIS11 ) & &
( adev - > uvd . fw_version < FW_1_66_16 ) )
DRM_ERROR ( " POLARIS10/11 UVD firmware version %hu.%hu is too old. \n " ,
version_major , version_minor ) ;
2017-03-03 18:13:26 -05:00
bo_size = AMDGPU_UVD_STACK_SIZE + AMDGPU_UVD_HEAP_SIZE
2016-04-12 13:46:15 +02:00
+ AMDGPU_UVD_SESSION_SIZE * adev - > uvd . max_handles ;
2017-03-03 18:13:26 -05:00
if ( adev - > firmware . load_type ! = AMDGPU_FW_LOAD_PSP )
bo_size + = AMDGPU_GPU_PAGE_ALIGN ( le32_to_cpu ( hdr - > ucode_size_bytes ) + 8 ) ;
2016-07-25 17:37:38 +02:00
r = amdgpu_bo_create_kernel ( adev , bo_size , PAGE_SIZE ,
AMDGPU_GEM_DOMAIN_VRAM , & adev - > uvd . vcpu_bo ,
& adev - > uvd . gpu_addr , & adev - > uvd . cpu_addr ) ;
2015-04-20 16:55:21 -04:00
if ( r ) {
dev_err ( adev - > dev , " (%d) failed to allocate UVD bo \n " , r ) ;
return r ;
}
2016-02-10 14:35:19 +01:00
ring = & adev - > uvd . ring ;
rq = & ring - > sched . sched_rq [ AMD_SCHED_PRIORITY_NORMAL ] ;
r = amd_sched_entity_init ( & ring - > sched , & adev - > uvd . entity ,
rq , amdgpu_sched_jobs ) ;
if ( r ! = 0 ) {
DRM_ERROR ( " Failed setting up UVD run queue. \n " ) ;
return r ;
}
2016-04-12 13:46:15 +02:00
for ( i = 0 ; i < adev - > uvd . max_handles ; + + i ) {
2015-04-20 16:55:21 -04:00
atomic_set ( & adev - > uvd . handles [ i ] , 0 ) ;
adev - > uvd . filp [ i ] = NULL ;
}
/* from uvd v5.0 HW addressing capacity increased to 64 bits */
2015-05-22 14:39:35 -04:00
if ( ! amdgpu_ip_block_version_cmp ( adev , AMD_IP_BLOCK_TYPE_UVD , 5 , 0 ) )
2015-04-20 16:55:21 -04:00
adev - > uvd . address_64_bit = true ;
2016-07-26 12:05:40 +02:00
switch ( adev - > asic_type ) {
case CHIP_TONGA :
adev - > uvd . use_ctx_buf = adev - > uvd . fw_version > = FW_1_65_10 ;
break ;
case CHIP_CARRIZO :
adev - > uvd . use_ctx_buf = adev - > uvd . fw_version > = FW_1_87_11 ;
break ;
case CHIP_FIJI :
adev - > uvd . use_ctx_buf = adev - > uvd . fw_version > = FW_1_87_12 ;
break ;
case CHIP_STONEY :
adev - > uvd . use_ctx_buf = adev - > uvd . fw_version > = FW_1_37_15 ;
break ;
default :
adev - > uvd . use_ctx_buf = adev - > asic_type > = CHIP_POLARIS10 ;
}
2015-04-20 16:55:21 -04:00
return 0 ;
}
int amdgpu_uvd_sw_fini ( struct amdgpu_device * adev )
{
2016-05-30 15:13:59 +08:00
kfree ( adev - > uvd . saved_bo ) ;
2015-04-20 16:55:21 -04:00
2016-02-10 14:35:19 +01:00
amd_sched_entity_fini ( & adev - > uvd . ring . sched , & adev - > uvd . entity ) ;
2016-09-07 17:14:46 +08:00
amdgpu_bo_free_kernel ( & adev - > uvd . vcpu_bo ,
& adev - > uvd . gpu_addr ,
( void * * ) & adev - > uvd . cpu_addr ) ;
2015-04-20 16:55:21 -04:00
amdgpu_ring_fini ( & adev - > uvd . ring ) ;
release_firmware ( adev - > uvd . fw ) ;
return 0 ;
}
int amdgpu_uvd_suspend ( struct amdgpu_device * adev )
{
2016-04-01 10:36:06 -04:00
unsigned size ;
void * ptr ;
int i ;
2015-04-20 16:55:21 -04:00
if ( adev - > uvd . vcpu_bo = = NULL )
return 0 ;
2016-04-12 13:46:15 +02:00
for ( i = 0 ; i < adev - > uvd . max_handles ; + + i )
2016-04-01 10:36:06 -04:00
if ( atomic_read ( & adev - > uvd . handles [ i ] ) )
break ;
if ( i = = AMDGPU_MAX_UVD_HANDLES )
return 0 ;
2016-04-12 19:25:52 +08:00
cancel_delayed_work_sync ( & adev - > uvd . idle_work ) ;
2016-04-01 10:36:06 -04:00
size = amdgpu_bo_size ( adev - > uvd . vcpu_bo ) ;
ptr = adev - > uvd . cpu_addr ;
2015-04-20 16:55:21 -04:00
2016-04-01 10:36:06 -04:00
adev - > uvd . saved_bo = kmalloc ( size , GFP_KERNEL ) ;
if ( ! adev - > uvd . saved_bo )
return - ENOMEM ;
2015-04-20 16:55:21 -04:00
2016-08-23 11:00:17 +02:00
memcpy_fromio ( adev - > uvd . saved_bo , ptr , size ) ;
2015-04-20 16:55:21 -04:00
return 0 ;
}
int amdgpu_uvd_resume ( struct amdgpu_device * adev )
{
unsigned size ;
void * ptr ;
if ( adev - > uvd . vcpu_bo = = NULL )
return - EINVAL ;
size = amdgpu_bo_size ( adev - > uvd . vcpu_bo ) ;
ptr = adev - > uvd . cpu_addr ;
2016-04-01 10:36:06 -04:00
if ( adev - > uvd . saved_bo ! = NULL ) {
2016-08-23 11:00:17 +02:00
memcpy_toio ( ptr , adev - > uvd . saved_bo , size ) ;
2016-04-01 10:36:06 -04:00
kfree ( adev - > uvd . saved_bo ) ;
adev - > uvd . saved_bo = NULL ;
2016-04-04 10:55:43 -04:00
} else {
const struct common_firmware_header * hdr ;
unsigned offset ;
hdr = ( const struct common_firmware_header * ) adev - > uvd . fw - > data ;
2017-03-03 18:13:26 -05:00
if ( adev - > firmware . load_type ! = AMDGPU_FW_LOAD_PSP ) {
offset = le32_to_cpu ( hdr - > ucode_array_offset_bytes ) ;
memcpy_toio ( adev - > uvd . cpu_addr , adev - > uvd . fw - > data + offset ,
le32_to_cpu ( hdr - > ucode_size_bytes ) ) ;
size - = le32_to_cpu ( hdr - > ucode_size_bytes ) ;
ptr + = le32_to_cpu ( hdr - > ucode_size_bytes ) ;
}
2016-08-23 11:00:17 +02:00
memset_io ( ptr , 0 , size ) ;
2016-04-04 10:55:43 -04:00
}
2015-04-20 16:55:21 -04:00
return 0 ;
}
void amdgpu_uvd_free_handles ( struct amdgpu_device * adev , struct drm_file * filp )
{
struct amdgpu_ring * ring = & adev - > uvd . ring ;
int i , r ;
2016-04-12 13:46:15 +02:00
for ( i = 0 ; i < adev - > uvd . max_handles ; + + i ) {
2015-04-20 16:55:21 -04:00
uint32_t handle = atomic_read ( & adev - > uvd . handles [ i ] ) ;
if ( handle ! = 0 & & adev - > uvd . filp [ i ] = = filp ) {
2016-10-25 13:00:45 +01:00
struct dma_fence * fence ;
2015-04-20 16:55:21 -04:00
2016-02-03 16:01:06 +01:00
r = amdgpu_uvd_get_destroy_msg ( ring , handle ,
false , & fence ) ;
2015-04-20 16:55:21 -04:00
if ( r ) {
DRM_ERROR ( " Error destroying UVD (%d)! \n " , r ) ;
continue ;
}
2016-10-25 13:00:45 +01:00
dma_fence_wait ( fence , false ) ;
dma_fence_put ( fence ) ;
2015-04-20 16:55:21 -04:00
adev - > uvd . filp [ i ] = NULL ;
atomic_set ( & adev - > uvd . handles [ i ] , 0 ) ;
}
}
}
2016-09-15 15:06:50 +02:00
static void amdgpu_uvd_force_into_uvd_segment ( struct amdgpu_bo * abo )
2015-04-20 16:55:21 -04:00
{
int i ;
2016-09-15 15:06:50 +02:00
for ( i = 0 ; i < abo - > placement . num_placement ; + + i ) {
abo - > placements [ i ] . fpfn = 0 > > PAGE_SHIFT ;
abo - > placements [ i ] . lpfn = ( 256 * 1024 * 1024 ) > > PAGE_SHIFT ;
2015-04-20 16:55:21 -04:00
}
}
2016-11-21 16:24:37 -05:00
static u64 amdgpu_uvd_get_addr_from_ctx ( struct amdgpu_uvd_cs_ctx * ctx )
{
uint32_t lo , hi ;
uint64_t addr ;
lo = amdgpu_get_ib_value ( ctx - > parser , ctx - > ib_idx , ctx - > data0 ) ;
hi = amdgpu_get_ib_value ( ctx - > parser , ctx - > ib_idx , ctx - > data1 ) ;
addr = ( ( uint64_t ) lo ) | ( ( ( uint64_t ) hi ) < < 32 ) ;
return addr ;
}
2015-04-20 16:55:21 -04:00
/**
* amdgpu_uvd_cs_pass1 - first parsing round
*
* @ ctx : UVD parser context
*
* Make sure UVD message and feedback buffers are in VRAM and
* nobody is violating an 256 MB boundary .
*/
static int amdgpu_uvd_cs_pass1 ( struct amdgpu_uvd_cs_ctx * ctx )
{
struct amdgpu_bo_va_mapping * mapping ;
struct amdgpu_bo * bo ;
2016-11-21 16:24:37 -05:00
uint32_t cmd ;
uint64_t addr = amdgpu_uvd_get_addr_from_ctx ( ctx ) ;
2015-04-20 16:55:21 -04:00
int r = 0 ;
mapping = amdgpu_cs_find_mapping ( ctx - > parser , addr , & bo ) ;
if ( mapping = = NULL ) {
DRM_ERROR ( " Can't find BO for addr 0x%08Lx \n " , addr ) ;
return - EINVAL ;
}
if ( ! ctx - > parser - > adev - > uvd . address_64_bit ) {
/* check if it's a message or feedback command */
cmd = amdgpu_get_ib_value ( ctx - > parser , ctx - > ib_idx , ctx - > idx ) > > 1 ;
if ( cmd = = 0x0 | | cmd = = 0x3 ) {
/* yes, force it into VRAM */
uint32_t domain = AMDGPU_GEM_DOMAIN_VRAM ;
amdgpu_ttm_placement_from_domain ( bo , domain ) ;
}
amdgpu_uvd_force_into_uvd_segment ( bo ) ;
r = ttm_bo_validate ( & bo - > tbo , & bo - > placement , false , false ) ;
}
return r ;
}
/**
* amdgpu_uvd_cs_msg_decode - handle UVD decode message
*
* @ msg : pointer to message structure
* @ buf_sizes : returned buffer sizes
*
* Peek into the decode message and calculate the necessary buffer sizes .
*/
2016-05-11 13:29:48 -04:00
static int amdgpu_uvd_cs_msg_decode ( struct amdgpu_device * adev , uint32_t * msg ,
unsigned buf_sizes [ ] )
2015-04-20 16:55:21 -04:00
{
unsigned stream_type = msg [ 4 ] ;
unsigned width = msg [ 6 ] ;
unsigned height = msg [ 7 ] ;
unsigned dpb_size = msg [ 9 ] ;
unsigned pitch = msg [ 28 ] ;
unsigned level = msg [ 57 ] ;
unsigned width_in_mb = width / 16 ;
unsigned height_in_mb = ALIGN ( height / 16 , 2 ) ;
unsigned fs_in_mb = width_in_mb * height_in_mb ;
2015-08-07 15:30:44 +08:00
unsigned image_size , tmp , min_dpb_size , num_dpb_buffer ;
2016-07-26 10:51:29 +02:00
unsigned min_ctx_size = ~ 0 ;
2015-04-20 16:55:21 -04:00
image_size = width * height ;
image_size + = image_size / 2 ;
image_size = ALIGN ( image_size , 1024 ) ;
switch ( stream_type ) {
case 0 : /* H264 */
switch ( level ) {
case 30 :
num_dpb_buffer = 8100 / fs_in_mb ;
break ;
case 31 :
num_dpb_buffer = 18000 / fs_in_mb ;
break ;
case 32 :
num_dpb_buffer = 20480 / fs_in_mb ;
break ;
case 41 :
num_dpb_buffer = 32768 / fs_in_mb ;
break ;
case 42 :
num_dpb_buffer = 34816 / fs_in_mb ;
break ;
case 50 :
num_dpb_buffer = 110400 / fs_in_mb ;
break ;
case 51 :
num_dpb_buffer = 184320 / fs_in_mb ;
break ;
default :
num_dpb_buffer = 184320 / fs_in_mb ;
break ;
}
num_dpb_buffer + + ;
if ( num_dpb_buffer > 17 )
num_dpb_buffer = 17 ;
/* reference picture buffer */
min_dpb_size = image_size * num_dpb_buffer ;
/* macroblock context buffer */
min_dpb_size + = width_in_mb * height_in_mb * num_dpb_buffer * 192 ;
/* IT surface buffer */
min_dpb_size + = width_in_mb * height_in_mb * 32 ;
break ;
case 1 : /* VC1 */
/* reference picture buffer */
min_dpb_size = image_size * 3 ;
/* CONTEXT_BUFFER */
min_dpb_size + = width_in_mb * height_in_mb * 128 ;
/* IT surface buffer */
min_dpb_size + = width_in_mb * 64 ;
/* DB surface buffer */
min_dpb_size + = width_in_mb * 128 ;
/* BP */
tmp = max ( width_in_mb , height_in_mb ) ;
min_dpb_size + = ALIGN ( tmp * 7 * 16 , 64 ) ;
break ;
case 3 : /* MPEG2 */
/* reference picture buffer */
min_dpb_size = image_size * 3 ;
break ;
case 4 : /* MPEG4 */
/* reference picture buffer */
min_dpb_size = image_size * 3 ;
/* CM */
min_dpb_size + = width_in_mb * height_in_mb * 64 ;
/* IT surface buffer */
min_dpb_size + = ALIGN ( width_in_mb * height_in_mb * 32 , 64 ) ;
break ;
2016-05-11 13:29:48 -04:00
case 7 : /* H264 Perf */
switch ( level ) {
case 30 :
num_dpb_buffer = 8100 / fs_in_mb ;
break ;
case 31 :
num_dpb_buffer = 18000 / fs_in_mb ;
break ;
case 32 :
num_dpb_buffer = 20480 / fs_in_mb ;
break ;
case 41 :
num_dpb_buffer = 32768 / fs_in_mb ;
break ;
case 42 :
num_dpb_buffer = 34816 / fs_in_mb ;
break ;
case 50 :
num_dpb_buffer = 110400 / fs_in_mb ;
break ;
case 51 :
num_dpb_buffer = 184320 / fs_in_mb ;
break ;
default :
num_dpb_buffer = 184320 / fs_in_mb ;
break ;
}
num_dpb_buffer + + ;
if ( num_dpb_buffer > 17 )
num_dpb_buffer = 17 ;
/* reference picture buffer */
min_dpb_size = image_size * num_dpb_buffer ;
2016-07-26 12:05:40 +02:00
if ( ! adev - > uvd . use_ctx_buf ) {
2016-05-11 13:29:48 -04:00
/* macroblock context buffer */
min_dpb_size + =
width_in_mb * height_in_mb * num_dpb_buffer * 192 ;
/* IT surface buffer */
min_dpb_size + = width_in_mb * height_in_mb * 32 ;
} else {
/* macroblock context buffer */
min_ctx_size =
width_in_mb * height_in_mb * num_dpb_buffer * 192 ;
}
break ;
2015-05-05 16:36:01 +02:00
case 16 : /* H265 */
image_size = ( ALIGN ( width , 16 ) * ALIGN ( height , 16 ) * 3 ) / 2 ;
image_size = ALIGN ( image_size , 256 ) ;
num_dpb_buffer = ( le32_to_cpu ( msg [ 59 ] ) & 0xff ) + 2 ;
min_dpb_size = image_size * num_dpb_buffer ;
2015-08-05 14:03:48 -04:00
min_ctx_size = ( ( width + 255 ) / 16 ) * ( ( height + 255 ) / 16 )
* 16 * num_dpb_buffer + 52 * 1024 ;
2015-05-05 16:36:01 +02:00
break ;
2015-04-20 16:55:21 -04:00
default :
DRM_ERROR ( " UVD codec not handled %d! \n " , stream_type ) ;
return - EINVAL ;
}
if ( width > pitch ) {
DRM_ERROR ( " Invalid UVD decoding target pitch! \n " ) ;
return - EINVAL ;
}
if ( dpb_size < min_dpb_size ) {
DRM_ERROR ( " Invalid dpb_size in UVD message (%d / %d)! \n " ,
dpb_size , min_dpb_size ) ;
return - EINVAL ;
}
buf_sizes [ 0x1 ] = dpb_size ;
buf_sizes [ 0x2 ] = image_size ;
2015-08-05 14:03:48 -04:00
buf_sizes [ 0x4 ] = min_ctx_size ;
2015-04-20 16:55:21 -04:00
return 0 ;
}
/**
* amdgpu_uvd_cs_msg - handle UVD message
*
* @ ctx : UVD parser context
* @ bo : buffer object containing the message
* @ offset : offset into the buffer object
*
* Peek into the UVD message and extract the session id .
* Make sure that we don ' t open up to many sessions .
*/
static int amdgpu_uvd_cs_msg ( struct amdgpu_uvd_cs_ctx * ctx ,
struct amdgpu_bo * bo , unsigned offset )
{
struct amdgpu_device * adev = ctx - > parser - > adev ;
int32_t * msg , msg_type , handle ;
void * ptr ;
2015-08-11 16:35:54 +02:00
long r ;
int i ;
2015-04-20 16:55:21 -04:00
if ( offset & 0x3F ) {
DRM_ERROR ( " UVD messages must be 64 byte aligned! \n " ) ;
return - EINVAL ;
}
r = amdgpu_bo_kmap ( bo , & ptr ) ;
if ( r ) {
2015-08-11 16:35:54 +02:00
DRM_ERROR ( " Failed mapping the UVD message (%ld)! \n " , r ) ;
2015-04-20 16:55:21 -04:00
return r ;
}
msg = ptr + offset ;
msg_type = msg [ 1 ] ;
handle = msg [ 2 ] ;
if ( handle = = 0 ) {
DRM_ERROR ( " Invalid UVD handle! \n " ) ;
return - EINVAL ;
}
2015-09-15 10:38:38 -04:00
switch ( msg_type ) {
case 0 :
/* it's a create msg, calc image size (width * height) */
amdgpu_bo_kunmap ( bo ) ;
/* try to alloc a new handle */
2016-04-12 13:46:15 +02:00
for ( i = 0 ; i < adev - > uvd . max_handles ; + + i ) {
2015-09-15 10:38:38 -04:00
if ( atomic_read ( & adev - > uvd . handles [ i ] ) = = handle ) {
DRM_ERROR ( " Handle 0x%x already in use! \n " , handle ) ;
return - EINVAL ;
}
if ( ! atomic_cmpxchg ( & adev - > uvd . handles [ i ] , 0 , handle ) ) {
adev - > uvd . filp [ i ] = ctx - > parser - > filp ;
return 0 ;
}
}
DRM_ERROR ( " No more free UVD handles! \n " ) ;
2016-07-13 21:24:59 +02:00
return - ENOSPC ;
2015-09-15 10:38:38 -04:00
case 1 :
2015-04-20 16:55:21 -04:00
/* it's a decode msg, calc buffer sizes */
2016-05-11 13:29:48 -04:00
r = amdgpu_uvd_cs_msg_decode ( adev , msg , ctx - > buf_sizes ) ;
2015-04-20 16:55:21 -04:00
amdgpu_bo_kunmap ( bo ) ;
if ( r )
return r ;
2015-09-15 10:38:38 -04:00
/* validate the handle */
2016-04-12 13:46:15 +02:00
for ( i = 0 ; i < adev - > uvd . max_handles ; + + i ) {
2015-09-15 10:38:38 -04:00
if ( atomic_read ( & adev - > uvd . handles [ i ] ) = = handle ) {
if ( adev - > uvd . filp [ i ] ! = ctx - > parser - > filp ) {
DRM_ERROR ( " UVD handle collision detected! \n " ) ;
return - EINVAL ;
}
return 0 ;
}
}
DRM_ERROR ( " Invalid UVD handle 0x%x! \n " , handle ) ;
return - ENOENT ;
case 2 :
2015-04-20 16:55:21 -04:00
/* it's a destroy msg, free the handle */
2016-04-12 13:46:15 +02:00
for ( i = 0 ; i < adev - > uvd . max_handles ; + + i )
2015-04-20 16:55:21 -04:00
atomic_cmpxchg ( & adev - > uvd . handles [ i ] , handle , 0 ) ;
amdgpu_bo_kunmap ( bo ) ;
return 0 ;
2015-09-15 10:38:38 -04:00
default :
DRM_ERROR ( " Illegal UVD message type (%d)! \n " , msg_type ) ;
return - EINVAL ;
2015-04-20 16:55:21 -04:00
}
2015-09-15 10:38:38 -04:00
BUG ( ) ;
2015-04-20 16:55:21 -04:00
return - EINVAL ;
}
/**
* amdgpu_uvd_cs_pass2 - second parsing round
*
* @ ctx : UVD parser context
*
* Patch buffer addresses , make sure buffer sizes are correct .
*/
static int amdgpu_uvd_cs_pass2 ( struct amdgpu_uvd_cs_ctx * ctx )
{
struct amdgpu_bo_va_mapping * mapping ;
struct amdgpu_bo * bo ;
2016-11-21 16:24:37 -05:00
uint32_t cmd ;
2015-04-20 16:55:21 -04:00
uint64_t start , end ;
2016-11-21 16:24:37 -05:00
uint64_t addr = amdgpu_uvd_get_addr_from_ctx ( ctx ) ;
2015-04-20 16:55:21 -04:00
int r ;
mapping = amdgpu_cs_find_mapping ( ctx - > parser , addr , & bo ) ;
2016-11-21 16:34:29 -05:00
if ( mapping = = NULL ) {
DRM_ERROR ( " Can't find BO for addr 0x%08Lx \n " , addr ) ;
2015-04-20 16:55:21 -04:00
return - EINVAL ;
2016-11-21 16:34:29 -05:00
}
2015-04-20 16:55:21 -04:00
start = amdgpu_bo_gpu_offset ( bo ) ;
2017-03-30 14:03:59 +02:00
end = ( mapping - > last + 1 - mapping - > start ) ;
2015-04-20 16:55:21 -04:00
end = end * AMDGPU_GPU_PAGE_SIZE + start ;
2017-03-30 14:03:59 +02:00
addr - = mapping - > start * AMDGPU_GPU_PAGE_SIZE ;
2015-04-20 16:55:21 -04:00
start + = addr ;
2016-01-31 11:00:41 +01:00
amdgpu_set_ib_value ( ctx - > parser , ctx - > ib_idx , ctx - > data0 ,
lower_32_bits ( start ) ) ;
amdgpu_set_ib_value ( ctx - > parser , ctx - > ib_idx , ctx - > data1 ,
upper_32_bits ( start ) ) ;
2015-04-20 16:55:21 -04:00
cmd = amdgpu_get_ib_value ( ctx - > parser , ctx - > ib_idx , ctx - > idx ) > > 1 ;
if ( cmd < 0x4 ) {
if ( ( end - start ) < ctx - > buf_sizes [ cmd ] ) {
DRM_ERROR ( " buffer (%d) to small (%d / %d)! \n " , cmd ,
( unsigned ) ( end - start ) ,
ctx - > buf_sizes [ cmd ] ) ;
return - EINVAL ;
}
2015-08-05 14:03:48 -04:00
} else if ( cmd = = 0x206 ) {
if ( ( end - start ) < ctx - > buf_sizes [ 4 ] ) {
DRM_ERROR ( " buffer (%d) to small (%d / %d)! \n " , cmd ,
( unsigned ) ( end - start ) ,
ctx - > buf_sizes [ 4 ] ) ;
return - EINVAL ;
}
2015-04-20 16:55:21 -04:00
} else if ( ( cmd ! = 0x100 ) & & ( cmd ! = 0x204 ) ) {
DRM_ERROR ( " invalid UVD command %X! \n " , cmd ) ;
return - EINVAL ;
}
if ( ! ctx - > parser - > adev - > uvd . address_64_bit ) {
if ( ( start > > 28 ) ! = ( ( end - 1 ) > > 28 ) ) {
DRM_ERROR ( " reloc %LX-%LX crossing 256MB boundary! \n " ,
start , end ) ;
return - EINVAL ;
}
if ( ( cmd = = 0 | | cmd = = 0x3 ) & &
( start > > 28 ) ! = ( ctx - > parser - > adev - > uvd . gpu_addr > > 28 ) ) {
DRM_ERROR ( " msg/fb buffer %LX-%LX out of 256MB segment! \n " ,
start , end ) ;
return - EINVAL ;
}
}
if ( cmd = = 0 ) {
ctx - > has_msg_cmd = true ;
r = amdgpu_uvd_cs_msg ( ctx , bo , addr ) ;
if ( r )
return r ;
} else if ( ! ctx - > has_msg_cmd ) {
DRM_ERROR ( " Message needed before other commands are send! \n " ) ;
return - EINVAL ;
}
return 0 ;
}
/**
* amdgpu_uvd_cs_reg - parse register writes
*
* @ ctx : UVD parser context
* @ cb : callback function
*
* Parse the register writes , call cb on each complete command .
*/
static int amdgpu_uvd_cs_reg ( struct amdgpu_uvd_cs_ctx * ctx ,
int ( * cb ) ( struct amdgpu_uvd_cs_ctx * ctx ) )
{
2016-02-03 13:44:52 +01:00
struct amdgpu_ib * ib = & ctx - > parser - > job - > ibs [ ctx - > ib_idx ] ;
2015-04-20 16:55:21 -04:00
int i , r ;
ctx - > idx + + ;
for ( i = 0 ; i < = ctx - > count ; + + i ) {
unsigned reg = ctx - > reg + i ;
if ( ctx - > idx > = ib - > length_dw ) {
DRM_ERROR ( " Register command after end of CS! \n " ) ;
return - EINVAL ;
}
switch ( reg ) {
case mmUVD_GPCOM_VCPU_DATA0 :
ctx - > data0 = ctx - > idx ;
break ;
case mmUVD_GPCOM_VCPU_DATA1 :
ctx - > data1 = ctx - > idx ;
break ;
case mmUVD_GPCOM_VCPU_CMD :
r = cb ( ctx ) ;
if ( r )
return r ;
break ;
case mmUVD_ENGINE_CNTL :
2016-08-22 17:58:14 -04:00
case mmUVD_NO_OP :
2015-04-20 16:55:21 -04:00
break ;
default :
DRM_ERROR ( " Invalid reg 0x%X! \n " , reg ) ;
return - EINVAL ;
}
ctx - > idx + + ;
}
return 0 ;
}
/**
* amdgpu_uvd_cs_packets - parse UVD packets
*
* @ ctx : UVD parser context
* @ cb : callback function
*
* Parse the command stream packets .
*/
static int amdgpu_uvd_cs_packets ( struct amdgpu_uvd_cs_ctx * ctx ,
int ( * cb ) ( struct amdgpu_uvd_cs_ctx * ctx ) )
{
2016-02-03 13:44:52 +01:00
struct amdgpu_ib * ib = & ctx - > parser - > job - > ibs [ ctx - > ib_idx ] ;
2015-04-20 16:55:21 -04:00
int r ;
for ( ctx - > idx = 0 ; ctx - > idx < ib - > length_dw ; ) {
uint32_t cmd = amdgpu_get_ib_value ( ctx - > parser , ctx - > ib_idx , ctx - > idx ) ;
unsigned type = CP_PACKET_GET_TYPE ( cmd ) ;
switch ( type ) {
case PACKET_TYPE0 :
ctx - > reg = CP_PACKET0_GET_REG ( cmd ) ;
ctx - > count = CP_PACKET_GET_COUNT ( cmd ) ;
r = amdgpu_uvd_cs_reg ( ctx , cb ) ;
if ( r )
return r ;
break ;
case PACKET_TYPE2 :
+ + ctx - > idx ;
break ;
default :
DRM_ERROR ( " Unknown packet type %d ! \n " , type ) ;
return - EINVAL ;
}
}
return 0 ;
}
/**
* amdgpu_uvd_ring_parse_cs - UVD command submission parser
*
* @ parser : Command submission parser context
*
* Parse the command stream , patch in addresses as necessary .
*/
int amdgpu_uvd_ring_parse_cs ( struct amdgpu_cs_parser * parser , uint32_t ib_idx )
{
struct amdgpu_uvd_cs_ctx ctx = { } ;
unsigned buf_sizes [ ] = {
[ 0x00000000 ] = 2048 ,
2015-08-05 14:03:48 -04:00
[ 0x00000001 ] = 0xFFFFFFFF ,
[ 0x00000002 ] = 0xFFFFFFFF ,
2015-04-20 16:55:21 -04:00
[ 0x00000003 ] = 2048 ,
2015-08-05 14:03:48 -04:00
[ 0x00000004 ] = 0xFFFFFFFF ,
2015-04-20 16:55:21 -04:00
} ;
2016-02-03 13:44:52 +01:00
struct amdgpu_ib * ib = & parser - > job - > ibs [ ib_idx ] ;
2015-04-20 16:55:21 -04:00
int r ;
2016-10-05 16:49:19 +02:00
parser - > job - > vm = NULL ;
ib - > gpu_addr = amdgpu_sa_bo_gpu_addr ( ib - > sa_bo ) ;
2015-04-20 16:55:21 -04:00
if ( ib - > length_dw % 16 ) {
DRM_ERROR ( " UVD IB length (%d) not 16 dwords aligned! \n " ,
ib - > length_dw ) ;
return - EINVAL ;
}
2016-09-05 17:00:57 +02:00
r = amdgpu_cs_sysvm_access_required ( parser ) ;
if ( r )
return r ;
2015-04-20 16:55:21 -04:00
ctx . parser = parser ;
ctx . buf_sizes = buf_sizes ;
ctx . ib_idx = ib_idx ;
2016-11-21 16:34:29 -05:00
/* first round only required on chips without UVD 64 bit address support */
if ( ! parser - > adev - > uvd . address_64_bit ) {
/* first round, make sure the buffers are actually in the UVD segment */
r = amdgpu_uvd_cs_packets ( & ctx , amdgpu_uvd_cs_pass1 ) ;
if ( r )
return r ;
}
2015-04-20 16:55:21 -04:00
/* second round, patch buffer addresses into the command stream */
r = amdgpu_uvd_cs_packets ( & ctx , amdgpu_uvd_cs_pass2 ) ;
if ( r )
return r ;
if ( ! ctx . has_msg_cmd ) {
DRM_ERROR ( " UVD-IBs need a msg command! \n " ) ;
return - EINVAL ;
}
return 0 ;
}
2016-02-03 16:01:06 +01:00
static int amdgpu_uvd_send_msg ( struct amdgpu_ring * ring , struct amdgpu_bo * bo ,
2016-10-25 13:00:45 +01:00
bool direct , struct dma_fence * * fence )
2015-04-20 16:55:21 -04:00
{
struct ttm_validate_buffer tv ;
struct ww_acquire_ctx ticket ;
struct list_head head ;
2016-02-01 12:20:25 +01:00
struct amdgpu_job * job ;
struct amdgpu_ib * ib ;
2016-10-25 13:00:45 +01:00
struct dma_fence * f = NULL ;
2015-07-03 14:08:18 +08:00
struct amdgpu_device * adev = ring - > adev ;
2015-04-20 16:55:21 -04:00
uint64_t addr ;
2017-03-03 18:13:26 -05:00
uint32_t data [ 4 ] ;
2015-04-20 16:55:21 -04:00
int i , r ;
memset ( & tv , 0 , sizeof ( tv ) ) ;
tv . bo = & bo - > tbo ;
INIT_LIST_HEAD ( & head ) ;
list_add ( & tv . head , & head ) ;
r = ttm_eu_reserve_buffers ( & ticket , & head , true , NULL ) ;
if ( r )
return r ;
2016-09-15 14:58:48 +02:00
if ( ! ring - > adev - > uvd . address_64_bit ) {
2015-04-20 16:55:21 -04:00
amdgpu_ttm_placement_from_domain ( bo , AMDGPU_GEM_DOMAIN_VRAM ) ;
amdgpu_uvd_force_into_uvd_segment ( bo ) ;
}
r = ttm_bo_validate ( & bo - > tbo , & bo - > placement , true , false ) ;
if ( r )
goto err ;
2016-02-01 12:20:25 +01:00
r = amdgpu_job_alloc_with_ib ( adev , 64 , & job ) ;
2015-07-03 14:08:18 +08:00
if ( r )
2016-02-01 12:20:25 +01:00
goto err ;
2015-04-20 16:55:21 -04:00
2017-03-03 18:13:26 -05:00
if ( adev - > asic_type > = CHIP_VEGA10 ) {
data [ 0 ] = PACKET0 ( mmUVD_GPCOM_VCPU_DATA0_VEGA10 , 0 ) ;
data [ 1 ] = PACKET0 ( mmUVD_GPCOM_VCPU_DATA1_VEGA10 , 0 ) ;
data [ 2 ] = PACKET0 ( mmUVD_GPCOM_VCPU_CMD_VEGA10 , 0 ) ;
data [ 3 ] = PACKET0 ( mmUVD_NO_OP_VEGA10 , 0 ) ;
} else {
data [ 0 ] = PACKET0 ( mmUVD_GPCOM_VCPU_DATA0 , 0 ) ;
data [ 1 ] = PACKET0 ( mmUVD_GPCOM_VCPU_DATA1 , 0 ) ;
data [ 2 ] = PACKET0 ( mmUVD_GPCOM_VCPU_CMD , 0 ) ;
data [ 3 ] = PACKET0 ( mmUVD_NO_OP , 0 ) ;
}
2016-02-01 12:20:25 +01:00
ib = & job - > ibs [ 0 ] ;
2015-04-20 16:55:21 -04:00
addr = amdgpu_bo_gpu_offset ( bo ) ;
2017-03-03 18:13:26 -05:00
ib - > ptr [ 0 ] = data [ 0 ] ;
2015-07-03 14:08:18 +08:00
ib - > ptr [ 1 ] = addr ;
2017-03-03 18:13:26 -05:00
ib - > ptr [ 2 ] = data [ 1 ] ;
2015-07-03 14:08:18 +08:00
ib - > ptr [ 3 ] = addr > > 32 ;
2017-03-03 18:13:26 -05:00
ib - > ptr [ 4 ] = data [ 2 ] ;
2015-07-03 14:08:18 +08:00
ib - > ptr [ 5 ] = 0 ;
2016-08-23 09:12:21 -04:00
for ( i = 6 ; i < 16 ; i + = 2 ) {
2017-03-03 18:13:26 -05:00
ib - > ptr [ i ] = data [ 3 ] ;
2016-08-23 09:12:21 -04:00
ib - > ptr [ i + 1 ] = 0 ;
}
2015-07-03 14:08:18 +08:00
ib - > length_dw = 16 ;
2015-04-20 16:55:21 -04:00
2016-02-03 16:01:06 +01:00
if ( direct ) {
2017-01-23 16:30:38 +08:00
r = amdgpu_ib_schedule ( ring , 1 , ib , NULL , & f ) ;
2016-10-25 13:00:45 +01:00
job - > fence = dma_fence_get ( f ) ;
2016-02-03 16:01:06 +01:00
if ( r )
goto err_free ;
amdgpu_job_free ( job ) ;
} else {
2016-02-10 14:35:19 +01:00
r = amdgpu_job_submit ( job , ring , & adev - > uvd . entity ,
2016-02-03 16:01:06 +01:00
AMDGPU_FENCE_OWNER_UNDEFINED , & f ) ;
if ( r )
goto err_free ;
}
2015-04-20 16:55:21 -04:00
2015-08-03 11:43:19 +08:00
ttm_eu_fence_buffer_objects ( & ticket , & head , f ) ;
2015-04-20 16:55:21 -04:00
2015-07-03 14:08:18 +08:00
if ( fence )
2016-10-25 13:00:45 +01:00
* fence = dma_fence_get ( f ) ;
2015-04-20 16:55:21 -04:00
amdgpu_bo_unref ( & bo ) ;
2016-10-25 13:00:45 +01:00
dma_fence_put ( f ) ;
2015-07-03 14:08:18 +08:00
return 0 ;
2016-02-01 12:20:25 +01:00
err_free :
amdgpu_job_free ( job ) ;
2015-04-20 16:55:21 -04:00
err :
ttm_eu_backoff_reservation ( & ticket , & head ) ;
return r ;
}
/* multiple fence commands without any stream commands in between can
crash the vcpu so just try to emmit a dummy create / destroy msg to
avoid this */
int amdgpu_uvd_get_create_msg ( struct amdgpu_ring * ring , uint32_t handle ,
2016-10-25 13:00:45 +01:00
struct dma_fence * * fence )
2015-04-20 16:55:21 -04:00
{
struct amdgpu_device * adev = ring - > adev ;
struct amdgpu_bo * bo ;
uint32_t * msg ;
int r , i ;
r = amdgpu_bo_create ( adev , 1024 , PAGE_SIZE , true ,
2015-08-27 00:14:16 -04:00
AMDGPU_GEM_DOMAIN_VRAM ,
2016-08-15 17:00:22 +02:00
AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED |
AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS ,
2015-09-03 17:34:59 +02:00
NULL , NULL , & bo ) ;
2015-04-20 16:55:21 -04:00
if ( r )
return r ;
r = amdgpu_bo_reserve ( bo , false ) ;
if ( r ) {
amdgpu_bo_unref ( & bo ) ;
return r ;
}
r = amdgpu_bo_kmap ( bo , ( void * * ) & msg ) ;
if ( r ) {
amdgpu_bo_unreserve ( bo ) ;
amdgpu_bo_unref ( & bo ) ;
return r ;
}
/* stitch together an UVD create msg */
msg [ 0 ] = cpu_to_le32 ( 0x00000de4 ) ;
msg [ 1 ] = cpu_to_le32 ( 0x00000000 ) ;
msg [ 2 ] = cpu_to_le32 ( handle ) ;
msg [ 3 ] = cpu_to_le32 ( 0x00000000 ) ;
msg [ 4 ] = cpu_to_le32 ( 0x00000000 ) ;
msg [ 5 ] = cpu_to_le32 ( 0x00000000 ) ;
msg [ 6 ] = cpu_to_le32 ( 0x00000000 ) ;
msg [ 7 ] = cpu_to_le32 ( 0x00000780 ) ;
msg [ 8 ] = cpu_to_le32 ( 0x00000440 ) ;
msg [ 9 ] = cpu_to_le32 ( 0x00000000 ) ;
msg [ 10 ] = cpu_to_le32 ( 0x01b37000 ) ;
for ( i = 11 ; i < 1024 ; + + i )
msg [ i ] = cpu_to_le32 ( 0x0 ) ;
amdgpu_bo_kunmap ( bo ) ;
amdgpu_bo_unreserve ( bo ) ;
2016-02-03 16:01:06 +01:00
return amdgpu_uvd_send_msg ( ring , bo , true , fence ) ;
2015-04-20 16:55:21 -04:00
}
int amdgpu_uvd_get_destroy_msg ( struct amdgpu_ring * ring , uint32_t handle ,
2016-10-25 13:00:45 +01:00
bool direct , struct dma_fence * * fence )
2015-04-20 16:55:21 -04:00
{
struct amdgpu_device * adev = ring - > adev ;
struct amdgpu_bo * bo ;
uint32_t * msg ;
int r , i ;
r = amdgpu_bo_create ( adev , 1024 , PAGE_SIZE , true ,
2015-08-27 00:14:16 -04:00
AMDGPU_GEM_DOMAIN_VRAM ,
2016-08-15 17:00:22 +02:00
AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED |
AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS ,
2015-09-03 17:34:59 +02:00
NULL , NULL , & bo ) ;
2015-04-20 16:55:21 -04:00
if ( r )
return r ;
r = amdgpu_bo_reserve ( bo , false ) ;
if ( r ) {
amdgpu_bo_unref ( & bo ) ;
return r ;
}
r = amdgpu_bo_kmap ( bo , ( void * * ) & msg ) ;
if ( r ) {
amdgpu_bo_unreserve ( bo ) ;
amdgpu_bo_unref ( & bo ) ;
return r ;
}
/* stitch together an UVD destroy msg */
msg [ 0 ] = cpu_to_le32 ( 0x00000de4 ) ;
msg [ 1 ] = cpu_to_le32 ( 0x00000002 ) ;
msg [ 2 ] = cpu_to_le32 ( handle ) ;
msg [ 3 ] = cpu_to_le32 ( 0x00000000 ) ;
for ( i = 4 ; i < 1024 ; + + i )
msg [ i ] = cpu_to_le32 ( 0x0 ) ;
amdgpu_bo_kunmap ( bo ) ;
amdgpu_bo_unreserve ( bo ) ;
2016-02-03 16:01:06 +01:00
return amdgpu_uvd_send_msg ( ring , bo , direct , fence ) ;
2015-04-20 16:55:21 -04:00
}
static void amdgpu_uvd_idle_work_handler ( struct work_struct * work )
{
struct amdgpu_device * adev =
container_of ( work , struct amdgpu_device , uvd . idle_work . work ) ;
2016-08-03 09:25:59 -04:00
unsigned fences = amdgpu_fence_count_emitted ( & adev - > uvd . ring ) ;
2015-04-20 16:55:21 -04:00
2017-03-07 14:45:25 +08:00
if ( amdgpu_sriov_vf ( adev ) )
return ;
2016-08-03 09:25:59 -04:00
if ( fences = = 0 ) {
2015-04-20 16:55:21 -04:00
if ( adev - > pm . dpm_enabled ) {
amdgpu_dpm_enable_uvd ( adev , false ) ;
} else {
amdgpu_asic_set_uvd_clocks ( adev , 0 , 0 ) ;
2017-01-20 12:06:05 +08:00
/* shutdown the UVD block */
amdgpu_set_powergating_state ( adev , AMD_IP_BLOCK_TYPE_UVD ,
AMD_PG_STATE_GATE ) ;
amdgpu_set_clockgating_state ( adev , AMD_IP_BLOCK_TYPE_UVD ,
AMD_CG_STATE_GATE ) ;
2015-04-20 16:55:21 -04:00
}
} else {
2016-07-01 17:45:49 +02:00
schedule_delayed_work ( & adev - > uvd . idle_work , UVD_IDLE_TIMEOUT ) ;
2015-04-20 16:55:21 -04:00
}
}
2016-07-20 14:11:26 +02:00
void amdgpu_uvd_ring_begin_use ( struct amdgpu_ring * ring )
2015-04-20 16:55:21 -04:00
{
2016-07-20 14:11:26 +02:00
struct amdgpu_device * adev = ring - > adev ;
2015-04-20 16:55:21 -04:00
bool set_clocks = ! cancel_delayed_work_sync ( & adev - > uvd . idle_work ) ;
2017-03-07 14:45:25 +08:00
if ( amdgpu_sriov_vf ( adev ) )
return ;
2015-04-20 16:55:21 -04:00
if ( set_clocks ) {
if ( adev - > pm . dpm_enabled ) {
amdgpu_dpm_enable_uvd ( adev , true ) ;
} else {
amdgpu_asic_set_uvd_clocks ( adev , 53300 , 40000 ) ;
2017-01-20 12:06:05 +08:00
amdgpu_set_clockgating_state ( adev , AMD_IP_BLOCK_TYPE_UVD ,
AMD_CG_STATE_UNGATE ) ;
amdgpu_set_powergating_state ( adev , AMD_IP_BLOCK_TYPE_UVD ,
AMD_PG_STATE_UNGATE ) ;
2015-04-20 16:55:21 -04:00
}
}
}
2016-07-20 14:11:26 +02:00
void amdgpu_uvd_ring_end_use ( struct amdgpu_ring * ring )
{
schedule_delayed_work ( & ring - > adev - > uvd . idle_work , UVD_IDLE_TIMEOUT ) ;
}
2016-07-05 16:47:54 +02:00
/**
* amdgpu_uvd_ring_test_ib - test ib execution
*
* @ ring : amdgpu_ring pointer
*
* Test if we can successfully execute an IB
*/
2016-07-05 21:07:17 +02:00
int amdgpu_uvd_ring_test_ib ( struct amdgpu_ring * ring , long timeout )
2016-07-05 16:47:54 +02:00
{
2016-10-25 13:00:45 +01:00
struct dma_fence * fence ;
2016-07-05 21:07:17 +02:00
long r ;
2016-07-05 16:47:54 +02:00
r = amdgpu_uvd_get_create_msg ( ring , 1 , NULL ) ;
if ( r ) {
2016-07-05 21:07:17 +02:00
DRM_ERROR ( " amdgpu: failed to get create msg (%ld). \n " , r ) ;
2016-07-05 16:47:54 +02:00
goto error ;
}
r = amdgpu_uvd_get_destroy_msg ( ring , 1 , true , & fence ) ;
if ( r ) {
2016-07-05 21:07:17 +02:00
DRM_ERROR ( " amdgpu: failed to get destroy ib (%ld). \n " , r ) ;
2016-07-05 16:47:54 +02:00
goto error ;
}
2016-10-25 13:00:45 +01:00
r = dma_fence_wait_timeout ( fence , false , timeout ) ;
2016-07-05 21:07:17 +02:00
if ( r = = 0 ) {
DRM_ERROR ( " amdgpu: IB test timed out. \n " ) ;
r = - ETIMEDOUT ;
} else if ( r < 0 ) {
DRM_ERROR ( " amdgpu: fence wait failed (%ld). \n " , r ) ;
} else {
DRM_INFO ( " ib test on ring %d succeeded \n " , ring - > idx ) ;
r = 0 ;
2016-07-05 16:47:54 +02:00
}
2016-07-05 21:07:17 +02:00
2016-10-25 13:00:45 +01:00
dma_fence_put ( fence ) ;
2016-08-03 13:39:42 -05:00
error :
2016-07-05 16:47:54 +02:00
return r ;
}
2016-12-12 15:29:33 +05:30
/**
* amdgpu_uvd_used_handles - returns used UVD handles
*
* @ adev : amdgpu_device pointer
*
* Returns the number of UVD handles in use
*/
uint32_t amdgpu_uvd_used_handles ( struct amdgpu_device * adev )
{
unsigned i ;
uint32_t used_handles = 0 ;
for ( i = 0 ; i < adev - > uvd . max_handles ; + + i ) {
/*
* Handles can be freed in any order , and not
* necessarily linear . So we need to count
* all non - zero handles .
*/
if ( atomic_read ( & adev - > uvd . handles [ i ] ) )
used_handles + + ;
}
return used_handles ;
}