2013-05-23 14:10:04 +04:00
/*
* Copyright 2013 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 < christian . koenig @ amd . com >
*/
# include <linux/firmware.h>
# include <drm/drmP.h>
# include "radeon.h"
# include "radeon_asic.h"
# include "sid.h"
2015-05-11 23:01:53 +03:00
# define VCE_V1_0_FW_SIZE (256 * 1024)
# define VCE_V1_0_STACK_SIZE (64 * 1024)
# define VCE_V1_0_DATA_SIZE (7808 * (RADEON_MAX_VCE_HANDLES + 1))
struct vce_v1_0_fw_signature
{
int32_t off ;
uint32_t len ;
int32_t num ;
struct {
uint32_t chip_id ;
uint32_t keyselect ;
uint32_t nonce [ 4 ] ;
uint32_t sigval [ 4 ] ;
} val [ 8 ] ;
} ;
2013-05-23 14:10:04 +04:00
/**
* vce_v1_0_get_rptr - get read pointer
*
* @ rdev : radeon_device pointer
* @ ring : radeon_ring pointer
*
* Returns the current hardware read pointer
*/
uint32_t vce_v1_0_get_rptr ( struct radeon_device * rdev ,
struct radeon_ring * ring )
{
if ( ring - > idx = = TN_RING_TYPE_VCE1_INDEX )
return RREG32 ( VCE_RB_RPTR ) ;
else
return RREG32 ( VCE_RB_RPTR2 ) ;
}
/**
* vce_v1_0_get_wptr - get write pointer
*
* @ rdev : radeon_device pointer
* @ ring : radeon_ring pointer
*
* Returns the current hardware write pointer
*/
uint32_t vce_v1_0_get_wptr ( struct radeon_device * rdev ,
struct radeon_ring * ring )
{
if ( ring - > idx = = TN_RING_TYPE_VCE1_INDEX )
return RREG32 ( VCE_RB_WPTR ) ;
else
return RREG32 ( VCE_RB_WPTR2 ) ;
}
/**
* vce_v1_0_set_wptr - set write pointer
*
* @ rdev : radeon_device pointer
* @ ring : radeon_ring pointer
*
* Commits the write pointer to the hardware
*/
void vce_v1_0_set_wptr ( struct radeon_device * rdev ,
struct radeon_ring * ring )
{
if ( ring - > idx = = TN_RING_TYPE_VCE1_INDEX )
WREG32 ( VCE_RB_WPTR , ring - > wptr ) ;
else
WREG32 ( VCE_RB_WPTR2 , ring - > wptr ) ;
}
2015-05-11 23:01:54 +03:00
void vce_v1_0_enable_mgcg ( struct radeon_device * rdev , bool enable )
{
u32 tmp ;
if ( enable & & ( rdev - > cg_flags & RADEON_CG_SUPPORT_VCE_MGCG ) ) {
tmp = RREG32 ( VCE_CLOCK_GATING_A ) ;
tmp | = CGC_DYN_CLOCK_MODE ;
WREG32 ( VCE_CLOCK_GATING_A , tmp ) ;
tmp = RREG32 ( VCE_UENC_CLOCK_GATING ) ;
tmp & = ~ 0x1ff000 ;
tmp | = 0xff800000 ;
WREG32 ( VCE_UENC_CLOCK_GATING , tmp ) ;
tmp = RREG32 ( VCE_UENC_REG_CLOCK_GATING ) ;
tmp & = ~ 0x3ff ;
WREG32 ( VCE_UENC_REG_CLOCK_GATING , tmp ) ;
} else {
tmp = RREG32 ( VCE_CLOCK_GATING_A ) ;
tmp & = ~ CGC_DYN_CLOCK_MODE ;
WREG32 ( VCE_CLOCK_GATING_A , tmp ) ;
tmp = RREG32 ( VCE_UENC_CLOCK_GATING ) ;
tmp | = 0x1ff000 ;
tmp & = ~ 0xff800000 ;
WREG32 ( VCE_UENC_CLOCK_GATING , tmp ) ;
tmp = RREG32 ( VCE_UENC_REG_CLOCK_GATING ) ;
tmp | = 0x3ff ;
WREG32 ( VCE_UENC_REG_CLOCK_GATING , tmp ) ;
}
}
static void vce_v1_0_init_cg ( struct radeon_device * rdev )
{
u32 tmp ;
tmp = RREG32 ( VCE_CLOCK_GATING_A ) ;
tmp | = CGC_DYN_CLOCK_MODE ;
WREG32 ( VCE_CLOCK_GATING_A , tmp ) ;
tmp = RREG32 ( VCE_CLOCK_GATING_B ) ;
tmp | = 0x1e ;
tmp & = ~ 0xe100e1 ;
WREG32 ( VCE_CLOCK_GATING_B , tmp ) ;
tmp = RREG32 ( VCE_UENC_CLOCK_GATING ) ;
tmp & = ~ 0xff9ff000 ;
WREG32 ( VCE_UENC_CLOCK_GATING , tmp ) ;
tmp = RREG32 ( VCE_UENC_REG_CLOCK_GATING ) ;
tmp & = ~ 0x3ff ;
WREG32 ( VCE_UENC_REG_CLOCK_GATING , tmp ) ;
}
2015-05-11 23:01:53 +03:00
int vce_v1_0_load_fw ( struct radeon_device * rdev , uint32_t * data )
{
struct vce_v1_0_fw_signature * sign = ( void * ) rdev - > vce_fw - > data ;
uint32_t chip_id ;
int i ;
switch ( rdev - > family ) {
case CHIP_TAHITI :
chip_id = 0x01000014 ;
break ;
case CHIP_VERDE :
chip_id = 0x01000015 ;
break ;
case CHIP_PITCAIRN :
case CHIP_OLAND :
chip_id = 0x01000016 ;
break ;
case CHIP_ARUBA :
chip_id = 0x01000017 ;
break ;
default :
return - EINVAL ;
}
2016-01-22 08:13:15 +03:00
for ( i = 0 ; i < le32_to_cpu ( sign - > num ) ; + + i ) {
if ( le32_to_cpu ( sign - > val [ i ] . chip_id ) = = chip_id )
2015-05-11 23:01:53 +03:00
break ;
}
2016-01-22 08:13:15 +03:00
if ( i = = le32_to_cpu ( sign - > num ) )
2015-05-11 23:01:53 +03:00
return - EINVAL ;
data + = ( 256 - 64 ) / 4 ;
data [ 0 ] = sign - > val [ i ] . nonce [ 0 ] ;
data [ 1 ] = sign - > val [ i ] . nonce [ 1 ] ;
data [ 2 ] = sign - > val [ i ] . nonce [ 2 ] ;
data [ 3 ] = sign - > val [ i ] . nonce [ 3 ] ;
2016-01-22 08:13:15 +03:00
data [ 4 ] = cpu_to_le32 ( le32_to_cpu ( sign - > len ) + 64 ) ;
2015-05-11 23:01:53 +03:00
memset ( & data [ 5 ] , 0 , 44 ) ;
memcpy ( & data [ 16 ] , & sign [ 1 ] , rdev - > vce_fw - > size - sizeof ( * sign ) ) ;
2016-01-22 08:13:15 +03:00
data + = le32_to_cpu ( data [ 4 ] ) / 4 ;
2015-05-11 23:01:53 +03:00
data [ 0 ] = sign - > val [ i ] . sigval [ 0 ] ;
data [ 1 ] = sign - > val [ i ] . sigval [ 1 ] ;
data [ 2 ] = sign - > val [ i ] . sigval [ 2 ] ;
data [ 3 ] = sign - > val [ i ] . sigval [ 3 ] ;
2016-01-22 08:13:15 +03:00
rdev - > vce . keyselect = le32_to_cpu ( sign - > val [ i ] . keyselect ) ;
2015-05-11 23:01:53 +03:00
return 0 ;
}
unsigned vce_v1_0_bo_size ( struct radeon_device * rdev )
{
WARN_ON ( VCE_V1_0_FW_SIZE < rdev - > vce_fw - > size ) ;
return VCE_V1_0_FW_SIZE + VCE_V1_0_STACK_SIZE + VCE_V1_0_DATA_SIZE ;
}
int vce_v1_0_resume ( struct radeon_device * rdev )
{
uint64_t addr = rdev - > vce . gpu_addr ;
uint32_t size ;
int i ;
WREG32_P ( VCE_CLOCK_GATING_A , 0 , ~ ( 1 < < 16 ) ) ;
WREG32_P ( VCE_UENC_CLOCK_GATING , 0x1FF000 , ~ 0xFF9FF000 ) ;
WREG32_P ( VCE_UENC_REG_CLOCK_GATING , 0x3F , ~ 0x3F ) ;
WREG32 ( VCE_CLOCK_GATING_B , 0 ) ;
WREG32_P ( VCE_LMI_FW_PERIODIC_CTRL , 0x4 , ~ 0x4 ) ;
WREG32 ( VCE_LMI_CTRL , 0x00398000 ) ;
WREG32_P ( VCE_LMI_CACHE_CTRL , 0x0 , ~ 0x1 ) ;
WREG32 ( VCE_LMI_SWAP_CNTL , 0 ) ;
WREG32 ( VCE_LMI_SWAP_CNTL1 , 0 ) ;
WREG32 ( VCE_LMI_VM_CTRL , 0 ) ;
WREG32 ( VCE_VCPU_SCRATCH7 , RADEON_MAX_VCE_HANDLES ) ;
addr + = 256 ;
size = VCE_V1_0_FW_SIZE ;
WREG32 ( VCE_VCPU_CACHE_OFFSET0 , addr & 0x7fffffff ) ;
WREG32 ( VCE_VCPU_CACHE_SIZE0 , size ) ;
addr + = size ;
size = VCE_V1_0_STACK_SIZE ;
WREG32 ( VCE_VCPU_CACHE_OFFSET1 , addr & 0x7fffffff ) ;
WREG32 ( VCE_VCPU_CACHE_SIZE1 , size ) ;
addr + = size ;
size = VCE_V1_0_DATA_SIZE ;
WREG32 ( VCE_VCPU_CACHE_OFFSET2 , addr & 0x7fffffff ) ;
WREG32 ( VCE_VCPU_CACHE_SIZE2 , size ) ;
WREG32_P ( VCE_LMI_CTRL2 , 0x0 , ~ 0x100 ) ;
WREG32 ( VCE_LMI_FW_START_KEYSEL , rdev - > vce . keyselect ) ;
for ( i = 0 ; i < 10 ; + + i ) {
mdelay ( 10 ) ;
if ( RREG32 ( VCE_FW_REG_STATUS ) & VCE_FW_REG_STATUS_DONE )
break ;
}
if ( i = = 10 )
return - ETIMEDOUT ;
if ( ! ( RREG32 ( VCE_FW_REG_STATUS ) & VCE_FW_REG_STATUS_PASS ) )
return - EINVAL ;
for ( i = 0 ; i < 10 ; + + i ) {
mdelay ( 10 ) ;
if ( ! ( RREG32 ( VCE_FW_REG_STATUS ) & VCE_FW_REG_STATUS_BUSY ) )
break ;
}
if ( i = = 10 )
return - ETIMEDOUT ;
2015-05-11 23:01:54 +03:00
vce_v1_0_init_cg ( rdev ) ;
2015-05-11 23:01:53 +03:00
return 0 ;
}
2013-05-23 14:10:04 +04:00
/**
* vce_v1_0_start - start VCE block
*
* @ rdev : radeon_device pointer
*
* Setup and start the VCE block
*/
int vce_v1_0_start ( struct radeon_device * rdev )
{
struct radeon_ring * ring ;
int i , j , r ;
/* set BUSY flag */
WREG32_P ( VCE_STATUS , 1 , ~ 1 ) ;
ring = & rdev - > ring [ TN_RING_TYPE_VCE1_INDEX ] ;
2014-02-18 17:52:33 +04:00
WREG32 ( VCE_RB_RPTR , ring - > wptr ) ;
2013-05-23 14:10:04 +04:00
WREG32 ( VCE_RB_WPTR , ring - > wptr ) ;
WREG32 ( VCE_RB_BASE_LO , ring - > gpu_addr ) ;
WREG32 ( VCE_RB_BASE_HI , upper_32_bits ( ring - > gpu_addr ) ) ;
WREG32 ( VCE_RB_SIZE , ring - > ring_size / 4 ) ;
ring = & rdev - > ring [ TN_RING_TYPE_VCE2_INDEX ] ;
2014-02-18 17:52:33 +04:00
WREG32 ( VCE_RB_RPTR2 , ring - > wptr ) ;
2013-05-23 14:10:04 +04:00
WREG32 ( VCE_RB_WPTR2 , ring - > wptr ) ;
WREG32 ( VCE_RB_BASE_LO2 , ring - > gpu_addr ) ;
WREG32 ( VCE_RB_BASE_HI2 , upper_32_bits ( ring - > gpu_addr ) ) ;
WREG32 ( VCE_RB_SIZE2 , ring - > ring_size / 4 ) ;
WREG32_P ( VCE_VCPU_CNTL , VCE_CLK_EN , ~ VCE_CLK_EN ) ;
WREG32_P ( VCE_SOFT_RESET ,
VCE_ECPU_SOFT_RESET |
VCE_FME_SOFT_RESET , ~ (
VCE_ECPU_SOFT_RESET |
VCE_FME_SOFT_RESET ) ) ;
mdelay ( 100 ) ;
WREG32_P ( VCE_SOFT_RESET , 0 , ~ (
VCE_ECPU_SOFT_RESET |
VCE_FME_SOFT_RESET ) ) ;
for ( i = 0 ; i < 10 ; + + i ) {
uint32_t status ;
for ( j = 0 ; j < 100 ; + + j ) {
status = RREG32 ( VCE_STATUS ) ;
if ( status & 2 )
break ;
mdelay ( 10 ) ;
}
r = 0 ;
if ( status & 2 )
break ;
DRM_ERROR ( " VCE not responding, trying to reset the ECPU!!! \n " ) ;
WREG32_P ( VCE_SOFT_RESET , VCE_ECPU_SOFT_RESET , ~ VCE_ECPU_SOFT_RESET ) ;
mdelay ( 10 ) ;
WREG32_P ( VCE_SOFT_RESET , 0 , ~ VCE_ECPU_SOFT_RESET ) ;
mdelay ( 10 ) ;
r = - 1 ;
}
/* clear BUSY flag */
WREG32_P ( VCE_STATUS , 0 , ~ 1 ) ;
if ( r ) {
DRM_ERROR ( " VCE not responding, giving up!!! \n " ) ;
return r ;
}
return 0 ;
}
int vce_v1_0_init ( struct radeon_device * rdev )
{
struct radeon_ring * ring ;
int r ;
r = vce_v1_0_start ( rdev ) ;
if ( r )
return r ;
ring = & rdev - > ring [ TN_RING_TYPE_VCE1_INDEX ] ;
ring - > ready = true ;
r = radeon_ring_test ( rdev , TN_RING_TYPE_VCE1_INDEX , ring ) ;
if ( r ) {
ring - > ready = false ;
return r ;
}
ring = & rdev - > ring [ TN_RING_TYPE_VCE2_INDEX ] ;
ring - > ready = true ;
r = radeon_ring_test ( rdev , TN_RING_TYPE_VCE2_INDEX , ring ) ;
if ( r ) {
ring - > ready = false ;
return r ;
}
DRM_INFO ( " VCE initialized successfully. \n " ) ;
return 0 ;
}