2013-07-19 12:59:32 -04:00
/*
* Copyright ( C ) 2013 Red Hat
* Author : Rob Clark < robdclark @ gmail . com >
*
2014-09-08 10:57:28 -06:00
* Copyright ( c ) 2014 The Linux Foundation . All rights reserved .
*
2013-07-19 12:59:32 -04:00
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*
* You should have received a copy of the GNU General Public License along with
* this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "adreno_gpu.h"
# include "msm_gem.h"
2013-11-16 12:56:06 -05:00
# include "msm_mmu.h"
2013-07-19 12:59:32 -04:00
# define RB_SIZE SZ_32K
# define RB_BLKSIZE 16
int adreno_get_param ( struct msm_gpu * gpu , uint32_t param , uint64_t * value )
{
struct adreno_gpu * adreno_gpu = to_adreno_gpu ( gpu ) ;
switch ( param ) {
case MSM_PARAM_GPU_ID :
* value = adreno_gpu - > info - > revn ;
return 0 ;
case MSM_PARAM_GMEM_SIZE :
2013-12-05 17:39:53 -05:00
* value = adreno_gpu - > gmem ;
2013-07-19 12:59:32 -04:00
return 0 ;
2014-02-04 14:16:04 -05:00
case MSM_PARAM_CHIP_ID :
* value = adreno_gpu - > rev . patchid |
( adreno_gpu - > rev . minor < < 8 ) |
( adreno_gpu - > rev . major < < 16 ) |
( adreno_gpu - > rev . core < < 24 ) ;
return 0 ;
2016-02-09 12:05:30 -05:00
case MSM_PARAM_MAX_FREQ :
* value = adreno_gpu - > base . fast_rate ;
return 0 ;
2016-02-22 06:26:21 -05:00
case MSM_PARAM_TIMESTAMP :
if ( adreno_gpu - > funcs - > get_timestamp )
return adreno_gpu - > funcs - > get_timestamp ( gpu , value ) ;
return - EINVAL ;
2013-07-19 12:59:32 -04:00
default :
DBG ( " %s: invalid param: %u " , gpu - > name , param ) ;
return - EINVAL ;
}
}
# define rbmemptr(adreno_gpu, member) \
( ( adreno_gpu ) - > memptrs_iova + offsetof ( struct adreno_rbmemptrs , member ) )
int adreno_hw_init ( struct msm_gpu * gpu )
{
struct adreno_gpu * adreno_gpu = to_adreno_gpu ( gpu ) ;
2014-07-09 22:08:15 -04:00
int ret ;
2013-07-19 12:59:32 -04:00
DBG ( " %s " , gpu - > name ) ;
2014-07-11 11:59:22 -04:00
ret = msm_gem_get_iova ( gpu - > rb - > bo , gpu - > id , & gpu - > rb_iova ) ;
2014-07-09 22:08:15 -04:00
if ( ret ) {
gpu - > rb_iova = 0 ;
dev_err ( gpu - > dev - > dev , " could not map ringbuffer: %d \n " , ret ) ;
return ret ;
}
2013-07-19 12:59:32 -04:00
/* Setup REG_CP_RB_CNTL: */
2014-09-08 10:57:28 -06:00
adreno_gpu_write ( adreno_gpu , REG_ADRENO_CP_RB_CNTL ,
2013-07-19 12:59:32 -04:00
/* size is log2(quad-words): */
AXXX_CP_RB_CNTL_BUFSZ ( ilog2 ( gpu - > rb - > size / 8 ) ) |
2016-02-18 16:50:02 -08:00
AXXX_CP_RB_CNTL_BLKSZ ( ilog2 ( RB_BLKSIZE / 8 ) ) |
( adreno_is_a430 ( adreno_gpu ) ? AXXX_CP_RB_CNTL_NO_UPDATE : 0 ) ) ;
2013-07-19 12:59:32 -04:00
/* Setup ringbuffer address: */
2014-09-08 10:57:28 -06:00
adreno_gpu_write ( adreno_gpu , REG_ADRENO_CP_RB_BASE , gpu - > rb_iova ) ;
2013-07-19 12:59:32 -04:00
2016-02-18 16:50:02 -08:00
if ( ! adreno_is_a430 ( adreno_gpu ) )
adreno_gpu_write ( adreno_gpu , REG_ADRENO_CP_RB_RPTR_ADDR ,
rbmemptr ( adreno_gpu , rptr ) ) ;
2013-07-19 12:59:32 -04:00
return 0 ;
}
static uint32_t get_wptr ( struct msm_ringbuffer * ring )
{
return ring - > cur - ring - > start ;
}
2016-02-18 16:50:02 -08:00
/* Use this helper to read rptr, since a430 doesn't update rptr in memory */
static uint32_t get_rptr ( struct adreno_gpu * adreno_gpu )
{
if ( adreno_is_a430 ( adreno_gpu ) )
return adreno_gpu - > memptrs - > rptr = adreno_gpu_read (
adreno_gpu , REG_ADRENO_CP_RB_RPTR ) ;
else
return adreno_gpu - > memptrs - > rptr ;
}
2013-07-19 12:59:32 -04:00
uint32_t adreno_last_fence ( struct msm_gpu * gpu )
{
struct adreno_gpu * adreno_gpu = to_adreno_gpu ( gpu ) ;
return adreno_gpu - > memptrs - > fence ;
}
2013-08-24 14:20:38 -04:00
void adreno_recover ( struct msm_gpu * gpu )
{
struct adreno_gpu * adreno_gpu = to_adreno_gpu ( gpu ) ;
struct drm_device * dev = gpu - > dev ;
int ret ;
gpu - > funcs - > pm_suspend ( gpu ) ;
/* reset ringbuffer: */
gpu - > rb - > cur = gpu - > rb - > start ;
2016-03-17 10:18:38 -04:00
/* reset completed fence seqno: */
adreno_gpu - > memptrs - > fence = gpu - > fctx - > completed_fence ;
2013-09-03 07:12:03 -04:00
adreno_gpu - > memptrs - > rptr = 0 ;
adreno_gpu - > memptrs - > wptr = 0 ;
2013-08-24 14:20:38 -04:00
gpu - > funcs - > pm_resume ( gpu ) ;
ret = gpu - > funcs - > hw_init ( gpu ) ;
if ( ret ) {
dev_err ( dev - > dev , " gpu hw init failed: %d \n " , ret ) ;
/* hmm, oh well? */
}
}
2016-05-03 09:46:49 -04:00
void adreno_submit ( struct msm_gpu * gpu , struct msm_gem_submit * submit ,
2013-07-19 12:59:32 -04:00
struct msm_file_private * ctx )
{
struct adreno_gpu * adreno_gpu = to_adreno_gpu ( gpu ) ;
struct msm_drm_private * priv = gpu - > dev - > dev_private ;
struct msm_ringbuffer * ring = gpu - > rb ;
2016-06-01 14:17:40 -04:00
unsigned i ;
2013-07-19 12:59:32 -04:00
for ( i = 0 ; i < submit - > nr_cmds ; i + + ) {
switch ( submit - > cmd [ i ] . type ) {
case MSM_SUBMIT_CMD_IB_TARGET_BUF :
/* ignore IB-targets */
break ;
case MSM_SUBMIT_CMD_CTX_RESTORE_BUF :
/* ignore if there has not been a ctx switch: */
if ( priv - > lastctx = = ctx )
break ;
case MSM_SUBMIT_CMD_BUF :
2016-02-18 16:50:00 -08:00
OUT_PKT3 ( ring , adreno_is_a430 ( adreno_gpu ) ?
CP_INDIRECT_BUFFER_PFE : CP_INDIRECT_BUFFER_PFD , 2 ) ;
2013-07-19 12:59:32 -04:00
OUT_RING ( ring , submit - > cmd [ i ] . iova ) ;
OUT_RING ( ring , submit - > cmd [ i ] . size ) ;
2016-06-01 14:17:40 -04:00
OUT_PKT2 ( ring ) ;
2013-07-19 12:59:32 -04:00
break ;
}
}
OUT_PKT0 ( ring , REG_AXXX_CP_SCRATCH_REG2 , 1 ) ;
2016-03-15 18:26:28 -04:00
OUT_RING ( ring , submit - > fence - > seqno ) ;
2013-07-19 12:59:32 -04:00
2014-09-08 13:40:16 -06:00
if ( adreno_is_a3xx ( adreno_gpu ) | | adreno_is_a4xx ( adreno_gpu ) ) {
2013-07-19 12:59:32 -04:00
/* Flush HLSQ lazy updates to make sure there is nothing
* pending for indirect loads after the timestamp has
* passed :
*/
OUT_PKT3 ( ring , CP_EVENT_WRITE , 1 ) ;
OUT_RING ( ring , HLSQ_FLUSH ) ;
OUT_PKT3 ( ring , CP_WAIT_FOR_IDLE , 1 ) ;
OUT_RING ( ring , 0x00000000 ) ;
}
OUT_PKT3 ( ring , CP_EVENT_WRITE , 3 ) ;
OUT_RING ( ring , CACHE_FLUSH_TS ) ;
OUT_RING ( ring , rbmemptr ( adreno_gpu , fence ) ) ;
2016-03-15 18:26:28 -04:00
OUT_RING ( ring , submit - > fence - > seqno ) ;
2013-07-19 12:59:32 -04:00
/* we could maybe be clever and only CP_COND_EXEC the interrupt: */
OUT_PKT3 ( ring , CP_INTERRUPT , 1 ) ;
OUT_RING ( ring , 0x80000000 ) ;
2015-05-12 11:29:40 -04:00
/* Workaround for missing irq issue on 8x16/a306. Unsure if the
* root cause is a platform issue or some a306 quirk , but this
* keeps things humming along :
*/
if ( adreno_is_a306 ( adreno_gpu ) ) {
OUT_PKT3 ( ring , CP_WAIT_FOR_IDLE , 1 ) ;
OUT_RING ( ring , 0x00000000 ) ;
OUT_PKT3 ( ring , CP_INTERRUPT , 1 ) ;
OUT_RING ( ring , 0x80000000 ) ;
}
2013-07-19 12:59:32 -04:00
#if 0
if ( adreno_is_a3xx ( adreno_gpu ) ) {
/* Dummy set-constant to trigger context rollover */
OUT_PKT3 ( ring , CP_SET_CONSTANT , 2 ) ;
OUT_RING ( ring , CP_REG ( REG_A3XX_HLSQ_CL_KERNEL_GROUP_X_REG ) ) ;
OUT_RING ( ring , 0x00000000 ) ;
}
# endif
gpu - > funcs - > flush ( gpu ) ;
}
void adreno_flush ( struct msm_gpu * gpu )
{
2014-09-08 10:57:28 -06:00
struct adreno_gpu * adreno_gpu = to_adreno_gpu ( gpu ) ;
2013-07-19 12:59:32 -04:00
uint32_t wptr = get_wptr ( gpu - > rb ) ;
/* ensure writes to ringbuffer have hit system memory: */
mb ( ) ;
2014-09-08 10:57:28 -06:00
adreno_gpu_write ( adreno_gpu , REG_ADRENO_CP_RB_WPTR , wptr ) ;
2013-07-19 12:59:32 -04:00
}
void adreno_idle ( struct msm_gpu * gpu )
{
struct adreno_gpu * adreno_gpu = to_adreno_gpu ( gpu ) ;
2014-01-11 16:11:59 -05:00
uint32_t wptr = get_wptr ( gpu - > rb ) ;
2016-02-18 16:50:02 -08:00
int ret ;
2013-07-19 12:59:32 -04:00
2014-01-11 16:11:59 -05:00
/* wait for CP to drain ringbuffer: */
2016-02-18 16:50:02 -08:00
ret = spin_until ( get_rptr ( adreno_gpu ) = = wptr ) ;
if ( ret )
2014-01-11 16:11:59 -05:00
DRM_ERROR ( " %s: timeout waiting to drain ringbuffer! \n " , gpu - > name ) ;
2013-07-19 12:59:32 -04:00
/* TODO maybe we need to reset GPU here to recover from hang? */
}
# ifdef CONFIG_DEBUG_FS
void adreno_show ( struct msm_gpu * gpu , struct seq_file * m )
{
struct adreno_gpu * adreno_gpu = to_adreno_gpu ( gpu ) ;
2014-09-05 15:05:38 -04:00
int i ;
2013-07-19 12:59:32 -04:00
seq_printf ( m , " revision: %d (%d.%d.%d.%d) \n " ,
adreno_gpu - > info - > revn , adreno_gpu - > rev . core ,
adreno_gpu - > rev . major , adreno_gpu - > rev . minor ,
adreno_gpu - > rev . patchid ) ;
seq_printf ( m , " fence: %d/%d \n " , adreno_gpu - > memptrs - > fence ,
2016-03-15 17:22:13 -04:00
gpu - > fctx - > last_fence ) ;
2016-02-18 16:50:02 -08:00
seq_printf ( m , " rptr: %d \n " , get_rptr ( adreno_gpu ) ) ;
2013-07-19 12:59:32 -04:00
seq_printf ( m , " wptr: %d \n " , adreno_gpu - > memptrs - > wptr ) ;
seq_printf ( m , " rb wptr: %d \n " , get_wptr ( gpu - > rb ) ) ;
2014-09-05 15:05:38 -04:00
gpu - > funcs - > pm_resume ( gpu ) ;
/* dump these out in a form that can be parsed by demsm: */
seq_printf ( m , " IO:region %s 00000000 00020000 \n " , gpu - > name ) ;
for ( i = 0 ; adreno_gpu - > registers [ i ] ! = ~ 0 ; i + = 2 ) {
uint32_t start = adreno_gpu - > registers [ i ] ;
uint32_t end = adreno_gpu - > registers [ i + 1 ] ;
uint32_t addr ;
for ( addr = start ; addr < = end ; addr + + ) {
uint32_t val = gpu_read ( gpu , addr ) ;
seq_printf ( m , " IO:R %08x %08x \n " , addr < < 2 , val ) ;
}
}
gpu - > funcs - > pm_suspend ( gpu ) ;
2013-07-19 12:59:32 -04:00
}
# endif
2015-04-19 10:14:09 -04:00
/* Dump common gpu status and scratch registers on any hang, to make
* the hangcheck logs more useful . The scratch registers seem always
* safe to read when GPU has hung ( unlike some other regs , depending
* on how the GPU hung ) , and they are useful to match up to cmdstream
* dumps when debugging hangs :
*/
void adreno_dump_info ( struct msm_gpu * gpu )
2013-12-22 10:29:43 -05:00
{
struct adreno_gpu * adreno_gpu = to_adreno_gpu ( gpu ) ;
printk ( " revision: %d (%d.%d.%d.%d) \n " ,
adreno_gpu - > info - > revn , adreno_gpu - > rev . core ,
adreno_gpu - > rev . major , adreno_gpu - > rev . minor ,
adreno_gpu - > rev . patchid ) ;
printk ( " fence: %d/%d \n " , adreno_gpu - > memptrs - > fence ,
2016-03-15 17:22:13 -04:00
gpu - > fctx - > last_fence ) ;
2016-02-18 16:50:02 -08:00
printk ( " rptr: %d \n " , get_rptr ( adreno_gpu ) ) ;
2013-12-22 10:29:43 -05:00
printk ( " wptr: %d \n " , adreno_gpu - > memptrs - > wptr ) ;
printk ( " rb wptr: %d \n " , get_wptr ( gpu - > rb ) ) ;
2015-04-19 10:14:09 -04:00
}
/* would be nice to not have to duplicate the _show() stuff with printk(): */
void adreno_dump ( struct msm_gpu * gpu )
{
struct adreno_gpu * adreno_gpu = to_adreno_gpu ( gpu ) ;
int i ;
2014-09-05 15:05:38 -04:00
/* dump these out in a form that can be parsed by demsm: */
printk ( " IO:region %s 00000000 00020000 \n " , gpu - > name ) ;
for ( i = 0 ; adreno_gpu - > registers [ i ] ! = ~ 0 ; i + = 2 ) {
uint32_t start = adreno_gpu - > registers [ i ] ;
uint32_t end = adreno_gpu - > registers [ i + 1 ] ;
uint32_t addr ;
for ( addr = start ; addr < = end ; addr + + ) {
uint32_t val = gpu_read ( gpu , addr ) ;
printk ( " IO:R %08x %08x \n " , addr < < 2 , val ) ;
}
}
2013-12-22 10:29:43 -05:00
}
2014-01-11 16:11:59 -05:00
static uint32_t ring_freewords ( struct msm_gpu * gpu )
2013-07-19 12:59:32 -04:00
{
struct adreno_gpu * adreno_gpu = to_adreno_gpu ( gpu ) ;
2014-01-11 16:11:59 -05:00
uint32_t size = gpu - > rb - > size / 4 ;
uint32_t wptr = get_wptr ( gpu - > rb ) ;
2016-02-18 16:50:02 -08:00
uint32_t rptr = get_rptr ( adreno_gpu ) ;
2014-01-11 16:11:59 -05:00
return ( rptr + ( size - 1 ) - wptr ) % size ;
}
void adreno_wait_ring ( struct msm_gpu * gpu , uint32_t ndwords )
{
if ( spin_until ( ring_freewords ( gpu ) > = ndwords ) )
DRM_ERROR ( " %s: timeout waiting for ringbuffer space \n " , gpu - > name ) ;
2013-07-19 12:59:32 -04:00
}
static const char * iommu_ports [ ] = {
" gfx3d_user " , " gfx3d_priv " ,
" gfx3d1_user " , " gfx3d1_priv " ,
} ;
int adreno_gpu_init ( struct drm_device * drm , struct platform_device * pdev ,
2014-09-05 15:03:40 -04:00
struct adreno_gpu * adreno_gpu , const struct adreno_gpu_funcs * funcs )
2013-07-19 12:59:32 -04:00
{
2014-09-05 15:03:40 -04:00
struct adreno_platform_config * config = pdev - > dev . platform_data ;
struct msm_gpu * gpu = & adreno_gpu - > base ;
2013-11-16 12:56:06 -05:00
struct msm_mmu * mmu ;
2014-09-05 13:30:27 -04:00
int ret ;
2013-07-19 12:59:32 -04:00
2014-09-05 15:03:40 -04:00
adreno_gpu - > funcs = funcs ;
adreno_gpu - > info = adreno_info ( config - > rev ) ;
adreno_gpu - > gmem = adreno_gpu - > info - > gmem ;
adreno_gpu - > revn = adreno_gpu - > info - > revn ;
adreno_gpu - > rev = config - > rev ;
gpu - > fast_rate = config - > fast_rate ;
gpu - > slow_rate = config - > slow_rate ;
gpu - > bus_freq = config - > bus_freq ;
2015-06-04 10:26:37 -04:00
# ifdef DOWNSTREAM_CONFIG_MSM_BUS_SCALING
2014-09-05 15:03:40 -04:00
gpu - > bus_scale_table = config - > bus_scale_table ;
# endif
DBG ( " fast_rate=%u, slow_rate=%u, bus_freq=%u " ,
gpu - > fast_rate , gpu - > slow_rate , gpu - > bus_freq ) ;
2013-07-19 12:59:32 -04:00
2014-10-31 11:50:55 -04:00
ret = msm_gpu_init ( drm , pdev , & adreno_gpu - > base , & funcs - > base ,
adreno_gpu - > info - > name , " kgsl_3d0_reg_memory " , " kgsl_3d0_irq " ,
RB_SIZE ) ;
if ( ret )
return ret ;
2014-09-05 15:03:40 -04:00
ret = request_firmware ( & adreno_gpu - > pm4 , adreno_gpu - > info - > pm4fw , drm - > dev ) ;
2013-07-19 12:59:32 -04:00
if ( ret ) {
dev_err ( drm - > dev , " failed to load %s PM4 firmware: %d \n " ,
2014-09-05 15:03:40 -04:00
adreno_gpu - > info - > pm4fw , ret ) ;
2013-07-19 12:59:32 -04:00
return ret ;
}
2014-09-05 15:03:40 -04:00
ret = request_firmware ( & adreno_gpu - > pfp , adreno_gpu - > info - > pfpfw , drm - > dev ) ;
2013-07-19 12:59:32 -04:00
if ( ret ) {
dev_err ( drm - > dev , " failed to load %s PFP firmware: %d \n " ,
2014-09-05 15:03:40 -04:00
adreno_gpu - > info - > pfpfw , ret ) ;
2013-07-19 12:59:32 -04:00
return ret ;
}
2016-09-28 19:58:32 -04:00
mmu = gpu - > aspace - > mmu ;
2013-11-16 12:56:06 -05:00
if ( mmu ) {
ret = mmu - > funcs - > attach ( mmu , iommu_ports ,
ARRAY_SIZE ( iommu_ports ) ) ;
if ( ret )
return ret ;
}
2013-07-19 12:59:32 -04:00
2014-07-11 11:59:22 -04:00
mutex_lock ( & drm - > struct_mutex ) ;
2014-09-05 15:03:40 -04:00
adreno_gpu - > memptrs_bo = msm_gem_new ( drm , sizeof ( * adreno_gpu - > memptrs ) ,
2013-07-19 12:59:32 -04:00
MSM_BO_UNCACHED ) ;
2014-07-11 11:59:22 -04:00
mutex_unlock ( & drm - > struct_mutex ) ;
2014-09-05 15:03:40 -04:00
if ( IS_ERR ( adreno_gpu - > memptrs_bo ) ) {
ret = PTR_ERR ( adreno_gpu - > memptrs_bo ) ;
adreno_gpu - > memptrs_bo = NULL ;
2013-07-19 12:59:32 -04:00
dev_err ( drm - > dev , " could not allocate memptrs: %d \n " , ret ) ;
return ret ;
}
2016-05-26 16:24:35 -04:00
adreno_gpu - > memptrs = msm_gem_get_vaddr ( adreno_gpu - > memptrs_bo ) ;
2016-05-24 18:29:38 -04:00
if ( IS_ERR ( adreno_gpu - > memptrs ) ) {
2013-07-19 12:59:32 -04:00
dev_err ( drm - > dev , " could not vmap memptrs \n " ) ;
return - ENOMEM ;
}
2014-09-05 15:03:40 -04:00
ret = msm_gem_get_iova ( adreno_gpu - > memptrs_bo , gpu - > id ,
& adreno_gpu - > memptrs_iova ) ;
2013-07-19 12:59:32 -04:00
if ( ret ) {
dev_err ( drm - > dev , " could not map memptrs: %d \n " , ret ) ;
return ret ;
}
return 0 ;
}
void adreno_gpu_cleanup ( struct adreno_gpu * gpu )
{
if ( gpu - > memptrs_bo ) {
2016-05-26 16:24:35 -04:00
if ( gpu - > memptrs )
msm_gem_put_vaddr ( gpu - > memptrs_bo ) ;
2013-07-19 12:59:32 -04:00
if ( gpu - > memptrs_iova )
msm_gem_put_iova ( gpu - > memptrs_bo , gpu - > base . id ) ;
2016-05-26 16:24:35 -04:00
2015-05-15 09:19:36 -04:00
drm_gem_object_unreference_unlocked ( gpu - > memptrs_bo ) ;
2013-07-19 12:59:32 -04:00
}
2014-11-25 13:44:20 +01:00
release_firmware ( gpu - > pm4 ) ;
release_firmware ( gpu - > pfp ) ;
2013-07-19 12:59:32 -04:00
msm_gpu_cleanup ( & gpu - > base ) ;
}