2015-04-20 16:55:21 -04:00
/*
* Copyright 2008 Advanced Micro Devices , Inc .
* Copyright 2008 Red Hat Inc .
* Copyright 2009 Jerome Glisse .
*
* 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 , sublicense ,
* 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 above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software .
*
* 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 NONINFRINGEMENT . IN NO EVENT SHALL
* THE COPYRIGHT HOLDER ( S ) OR AUTHOR ( S ) 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 .
*
* Authors : Dave Airlie
* Alex Deucher
* Jerome Glisse
*/
# include <linux/console.h>
# include <linux/slab.h>
# include <linux/debugfs.h>
# include <drm/drmP.h>
# include <drm/drm_crtc_helper.h>
# include <drm/amdgpu_drm.h>
# include <linux/vgaarb.h>
# include <linux/vga_switcheroo.h>
# include <linux/efi.h>
# include "amdgpu.h"
# include "amdgpu_i2c.h"
# include "atom.h"
# include "amdgpu_atombios.h"
2015-04-20 17:09:27 -04:00
# ifdef CONFIG_DRM_AMDGPU_CIK
# include "cik.h"
# endif
2015-04-20 17:31:14 -04:00
# include "vi.h"
2015-04-20 16:55:21 -04:00
# include "bif/bif_4_1_d.h"
static int amdgpu_debugfs_regs_init ( struct amdgpu_device * adev ) ;
static void amdgpu_debugfs_regs_cleanup ( struct amdgpu_device * adev ) ;
static const char * amdgpu_asic_name [ ] = {
" BONAIRE " ,
" KAVERI " ,
" KABINI " ,
" HAWAII " ,
" MULLINS " ,
" TOPAZ " ,
" TONGA " ,
" CARRIZO " ,
" LAST " ,
} ;
bool amdgpu_device_is_px ( struct drm_device * dev )
{
struct amdgpu_device * adev = dev - > dev_private ;
if ( adev - > flags & AMDGPU_IS_PX )
return true ;
return false ;
}
/*
* MMIO register access helper functions .
*/
uint32_t amdgpu_mm_rreg ( struct amdgpu_device * adev , uint32_t reg ,
bool always_indirect )
{
if ( ( reg * 4 ) < adev - > rmmio_size & & ! always_indirect )
return readl ( ( ( void __iomem * ) adev - > rmmio ) + ( reg * 4 ) ) ;
else {
unsigned long flags ;
uint32_t ret ;
spin_lock_irqsave ( & adev - > mmio_idx_lock , flags ) ;
writel ( ( reg * 4 ) , ( ( void __iomem * ) adev - > rmmio ) + ( mmMM_INDEX * 4 ) ) ;
ret = readl ( ( ( void __iomem * ) adev - > rmmio ) + ( mmMM_DATA * 4 ) ) ;
spin_unlock_irqrestore ( & adev - > mmio_idx_lock , flags ) ;
return ret ;
}
}
void amdgpu_mm_wreg ( struct amdgpu_device * adev , uint32_t reg , uint32_t v ,
bool always_indirect )
{
if ( ( reg * 4 ) < adev - > rmmio_size & & ! always_indirect )
writel ( v , ( ( void __iomem * ) adev - > rmmio ) + ( reg * 4 ) ) ;
else {
unsigned long flags ;
spin_lock_irqsave ( & adev - > mmio_idx_lock , flags ) ;
writel ( ( reg * 4 ) , ( ( void __iomem * ) adev - > rmmio ) + ( mmMM_INDEX * 4 ) ) ;
writel ( v , ( ( void __iomem * ) adev - > rmmio ) + ( mmMM_DATA * 4 ) ) ;
spin_unlock_irqrestore ( & adev - > mmio_idx_lock , flags ) ;
}
}
u32 amdgpu_io_rreg ( struct amdgpu_device * adev , u32 reg )
{
if ( ( reg * 4 ) < adev - > rio_mem_size )
return ioread32 ( adev - > rio_mem + ( reg * 4 ) ) ;
else {
iowrite32 ( ( reg * 4 ) , adev - > rio_mem + ( mmMM_INDEX * 4 ) ) ;
return ioread32 ( adev - > rio_mem + ( mmMM_DATA * 4 ) ) ;
}
}
void amdgpu_io_wreg ( struct amdgpu_device * adev , u32 reg , u32 v )
{
if ( ( reg * 4 ) < adev - > rio_mem_size )
iowrite32 ( v , adev - > rio_mem + ( reg * 4 ) ) ;
else {
iowrite32 ( ( reg * 4 ) , adev - > rio_mem + ( mmMM_INDEX * 4 ) ) ;
iowrite32 ( v , adev - > rio_mem + ( mmMM_DATA * 4 ) ) ;
}
}
/**
* amdgpu_mm_rdoorbell - read a doorbell dword
*
* @ adev : amdgpu_device pointer
* @ index : doorbell index
*
* Returns the value in the doorbell aperture at the
* requested doorbell index ( CIK ) .
*/
u32 amdgpu_mm_rdoorbell ( struct amdgpu_device * adev , u32 index )
{
if ( index < adev - > doorbell . num_doorbells ) {
return readl ( adev - > doorbell . ptr + index ) ;
} else {
DRM_ERROR ( " reading beyond doorbell aperture: 0x%08x! \n " , index ) ;
return 0 ;
}
}
/**
* amdgpu_mm_wdoorbell - write a doorbell dword
*
* @ adev : amdgpu_device pointer
* @ index : doorbell index
* @ v : value to write
*
* Writes @ v to the doorbell aperture at the
* requested doorbell index ( CIK ) .
*/
void amdgpu_mm_wdoorbell ( struct amdgpu_device * adev , u32 index , u32 v )
{
if ( index < adev - > doorbell . num_doorbells ) {
writel ( v , adev - > doorbell . ptr + index ) ;
} else {
DRM_ERROR ( " writing beyond doorbell aperture: 0x%08x! \n " , index ) ;
}
}
/**
* amdgpu_invalid_rreg - dummy reg read function
*
* @ adev : amdgpu device pointer
* @ reg : offset of register
*
* Dummy register read function . Used for register blocks
* that certain asics don ' t have ( all asics ) .
* Returns the value in the register .
*/
static uint32_t amdgpu_invalid_rreg ( struct amdgpu_device * adev , uint32_t reg )
{
DRM_ERROR ( " Invalid callback to read register 0x%04X \n " , reg ) ;
BUG ( ) ;
return 0 ;
}
/**
* amdgpu_invalid_wreg - dummy reg write function
*
* @ adev : amdgpu device pointer
* @ reg : offset of register
* @ v : value to write to the register
*
* Dummy register read function . Used for register blocks
* that certain asics don ' t have ( all asics ) .
*/
static void amdgpu_invalid_wreg ( struct amdgpu_device * adev , uint32_t reg , uint32_t v )
{
DRM_ERROR ( " Invalid callback to write register 0x%04X with 0x%08X \n " ,
reg , v ) ;
BUG ( ) ;
}
/**
* amdgpu_block_invalid_rreg - dummy reg read function
*
* @ adev : amdgpu device pointer
* @ block : offset of instance
* @ reg : offset of register
*
* Dummy register read function . Used for register blocks
* that certain asics don ' t have ( all asics ) .
* Returns the value in the register .
*/
static uint32_t amdgpu_block_invalid_rreg ( struct amdgpu_device * adev ,
uint32_t block , uint32_t reg )
{
DRM_ERROR ( " Invalid callback to read register 0x%04X in block 0x%04X \n " ,
reg , block ) ;
BUG ( ) ;
return 0 ;
}
/**
* amdgpu_block_invalid_wreg - dummy reg write function
*
* @ adev : amdgpu device pointer
* @ block : offset of instance
* @ reg : offset of register
* @ v : value to write to the register
*
* Dummy register read function . Used for register blocks
* that certain asics don ' t have ( all asics ) .
*/
static void amdgpu_block_invalid_wreg ( struct amdgpu_device * adev ,
uint32_t block ,
uint32_t reg , uint32_t v )
{
DRM_ERROR ( " Invalid block callback to write register 0x%04X in block 0x%04X with 0x%08X \n " ,
reg , block , v ) ;
BUG ( ) ;
}
static int amdgpu_vram_scratch_init ( struct amdgpu_device * adev )
{
int r ;
if ( adev - > vram_scratch . robj = = NULL ) {
r = amdgpu_bo_create ( adev , AMDGPU_GPU_PAGE_SIZE ,
PAGE_SIZE , true , AMDGPU_GEM_DOMAIN_VRAM , 0 ,
NULL , & adev - > vram_scratch . robj ) ;
if ( r ) {
return r ;
}
}
r = amdgpu_bo_reserve ( adev - > vram_scratch . robj , false ) ;
if ( unlikely ( r ! = 0 ) )
return r ;
r = amdgpu_bo_pin ( adev - > vram_scratch . robj ,
AMDGPU_GEM_DOMAIN_VRAM , & adev - > vram_scratch . gpu_addr ) ;
if ( r ) {
amdgpu_bo_unreserve ( adev - > vram_scratch . robj ) ;
return r ;
}
r = amdgpu_bo_kmap ( adev - > vram_scratch . robj ,
( void * * ) & adev - > vram_scratch . ptr ) ;
if ( r )
amdgpu_bo_unpin ( adev - > vram_scratch . robj ) ;
amdgpu_bo_unreserve ( adev - > vram_scratch . robj ) ;
return r ;
}
static void amdgpu_vram_scratch_fini ( struct amdgpu_device * adev )
{
int r ;
if ( adev - > vram_scratch . robj = = NULL ) {
return ;
}
r = amdgpu_bo_reserve ( adev - > vram_scratch . robj , false ) ;
if ( likely ( r = = 0 ) ) {
amdgpu_bo_kunmap ( adev - > vram_scratch . robj ) ;
amdgpu_bo_unpin ( adev - > vram_scratch . robj ) ;
amdgpu_bo_unreserve ( adev - > vram_scratch . robj ) ;
}
amdgpu_bo_unref ( & adev - > vram_scratch . robj ) ;
}
/**
* amdgpu_program_register_sequence - program an array of registers .
*
* @ adev : amdgpu_device pointer
* @ registers : pointer to the register array
* @ array_size : size of the register array
*
* Programs an array or registers with and and or masks .
* This is a helper for setting golden registers .
*/
void amdgpu_program_register_sequence ( struct amdgpu_device * adev ,
const u32 * registers ,
const u32 array_size )
{
u32 tmp , reg , and_mask , or_mask ;
int i ;
if ( array_size % 3 )
return ;
for ( i = 0 ; i < array_size ; i + = 3 ) {
reg = registers [ i + 0 ] ;
and_mask = registers [ i + 1 ] ;
or_mask = registers [ i + 2 ] ;
if ( and_mask = = 0xffffffff ) {
tmp = or_mask ;
} else {
tmp = RREG32 ( reg ) ;
tmp & = ~ and_mask ;
tmp | = or_mask ;
}
WREG32 ( reg , tmp ) ;
}
}
void amdgpu_pci_config_reset ( struct amdgpu_device * adev )
{
pci_write_config_dword ( adev - > pdev , 0x7c , AMDGPU_ASIC_RESET_DATA ) ;
}
/*
* GPU doorbell aperture helpers function .
*/
/**
* amdgpu_doorbell_init - Init doorbell driver information .
*
* @ adev : amdgpu_device pointer
*
* Init doorbell driver information ( CIK )
* Returns 0 on success , error on failure .
*/
static int amdgpu_doorbell_init ( struct amdgpu_device * adev )
{
/* doorbell bar mapping */
adev - > doorbell . base = pci_resource_start ( adev - > pdev , 2 ) ;
adev - > doorbell . size = pci_resource_len ( adev - > pdev , 2 ) ;
adev - > doorbell . num_doorbells = min_t ( u32 , adev - > doorbell . size / sizeof ( u32 ) ,
AMDGPU_DOORBELL_MAX_ASSIGNMENT + 1 ) ;
if ( adev - > doorbell . num_doorbells = = 0 )
return - EINVAL ;
adev - > doorbell . ptr = ioremap ( adev - > doorbell . base , adev - > doorbell . num_doorbells * sizeof ( u32 ) ) ;
if ( adev - > doorbell . ptr = = NULL ) {
return - ENOMEM ;
}
DRM_INFO ( " doorbell mmio base: 0x%08X \n " , ( uint32_t ) adev - > doorbell . base ) ;
DRM_INFO ( " doorbell mmio size: %u \n " , ( unsigned ) adev - > doorbell . size ) ;
return 0 ;
}
/**
* amdgpu_doorbell_fini - Tear down doorbell driver information .
*
* @ adev : amdgpu_device pointer
*
* Tear down doorbell driver information ( CIK )
*/
static void amdgpu_doorbell_fini ( struct amdgpu_device * adev )
{
iounmap ( adev - > doorbell . ptr ) ;
adev - > doorbell . ptr = NULL ;
}
/**
* amdgpu_doorbell_get_kfd_info - Report doorbell configuration required to
* setup amdkfd
*
* @ adev : amdgpu_device pointer
* @ aperture_base : output returning doorbell aperture base physical address
* @ aperture_size : output returning doorbell aperture size in bytes
* @ start_offset : output returning # of doorbell bytes reserved for amdgpu .
*
* amdgpu and amdkfd share the doorbell aperture . amdgpu sets it up ,
* takes doorbells required for its own rings and reports the setup to amdkfd .
* amdgpu reserved doorbells are at the start of the doorbell aperture .
*/
void amdgpu_doorbell_get_kfd_info ( struct amdgpu_device * adev ,
phys_addr_t * aperture_base ,
size_t * aperture_size ,
size_t * start_offset )
{
/*
* The first num_doorbells are used by amdgpu .
* amdkfd takes whatever ' s left in the aperture .
*/
if ( adev - > doorbell . size > adev - > doorbell . num_doorbells * sizeof ( u32 ) ) {
* aperture_base = adev - > doorbell . base ;
* aperture_size = adev - > doorbell . size ;
* start_offset = adev - > doorbell . num_doorbells * sizeof ( u32 ) ;
} else {
* aperture_base = 0 ;
* aperture_size = 0 ;
* start_offset = 0 ;
}
}
/*
* amdgpu_wb_ * ( )
* Writeback is the the method by which the the GPU updates special pages
* in memory with the status of certain GPU events ( fences , ring pointers ,
* etc . ) .
*/
/**
* amdgpu_wb_fini - Disable Writeback and free memory
*
* @ adev : amdgpu_device pointer
*
* Disables Writeback and frees the Writeback memory ( all asics ) .
* Used at driver shutdown .
*/
static void amdgpu_wb_fini ( struct amdgpu_device * adev )
{
if ( adev - > wb . wb_obj ) {
if ( ! amdgpu_bo_reserve ( adev - > wb . wb_obj , false ) ) {
amdgpu_bo_kunmap ( adev - > wb . wb_obj ) ;
amdgpu_bo_unpin ( adev - > wb . wb_obj ) ;
amdgpu_bo_unreserve ( adev - > wb . wb_obj ) ;
}
amdgpu_bo_unref ( & adev - > wb . wb_obj ) ;
adev - > wb . wb = NULL ;
adev - > wb . wb_obj = NULL ;
}
}
/**
* amdgpu_wb_init - Init Writeback driver info and allocate memory
*
* @ adev : amdgpu_device pointer
*
* Disables Writeback and frees the Writeback memory ( all asics ) .
* Used at driver startup .
* Returns 0 on success or an - error on failure .
*/
static int amdgpu_wb_init ( struct amdgpu_device * adev )
{
int r ;
if ( adev - > wb . wb_obj = = NULL ) {
r = amdgpu_bo_create ( adev , AMDGPU_MAX_WB * 4 , PAGE_SIZE , true ,
AMDGPU_GEM_DOMAIN_GTT , 0 , NULL , & adev - > wb . wb_obj ) ;
if ( r ) {
dev_warn ( adev - > dev , " (%d) create WB bo failed \n " , r ) ;
return r ;
}
r = amdgpu_bo_reserve ( adev - > wb . wb_obj , false ) ;
if ( unlikely ( r ! = 0 ) ) {
amdgpu_wb_fini ( adev ) ;
return r ;
}
r = amdgpu_bo_pin ( adev - > wb . wb_obj , AMDGPU_GEM_DOMAIN_GTT ,
& adev - > wb . gpu_addr ) ;
if ( r ) {
amdgpu_bo_unreserve ( adev - > wb . wb_obj ) ;
dev_warn ( adev - > dev , " (%d) pin WB bo failed \n " , r ) ;
amdgpu_wb_fini ( adev ) ;
return r ;
}
r = amdgpu_bo_kmap ( adev - > wb . wb_obj , ( void * * ) & adev - > wb . wb ) ;
amdgpu_bo_unreserve ( adev - > wb . wb_obj ) ;
if ( r ) {
dev_warn ( adev - > dev , " (%d) map WB bo failed \n " , r ) ;
amdgpu_wb_fini ( adev ) ;
return r ;
}
adev - > wb . num_wb = AMDGPU_MAX_WB ;
memset ( & adev - > wb . used , 0 , sizeof ( adev - > wb . used ) ) ;
/* clear wb memory */
memset ( ( char * ) adev - > wb . wb , 0 , AMDGPU_GPU_PAGE_SIZE ) ;
}
return 0 ;
}
/**
* amdgpu_wb_get - Allocate a wb entry
*
* @ adev : amdgpu_device pointer
* @ wb : wb index
*
* Allocate a wb slot for use by the driver ( all asics ) .
* Returns 0 on success or - EINVAL on failure .
*/
int amdgpu_wb_get ( struct amdgpu_device * adev , u32 * wb )
{
unsigned long offset = find_first_zero_bit ( adev - > wb . used , adev - > wb . num_wb ) ;
if ( offset < adev - > wb . num_wb ) {
__set_bit ( offset , adev - > wb . used ) ;
* wb = offset ;
return 0 ;
} else {
return - EINVAL ;
}
}
/**
* amdgpu_wb_free - Free a wb entry
*
* @ adev : amdgpu_device pointer
* @ wb : wb index
*
* Free a wb slot allocated for use by the driver ( all asics )
*/
void amdgpu_wb_free ( struct amdgpu_device * adev , u32 wb )
{
if ( wb < adev - > wb . num_wb )
__clear_bit ( wb , adev - > wb . used ) ;
}
/**
* amdgpu_vram_location - try to find VRAM location
* @ adev : amdgpu device structure holding all necessary informations
* @ mc : memory controller structure holding memory informations
* @ base : base address at which to put VRAM
*
* Function will place try to place VRAM at base address provided
* as parameter ( which is so far either PCI aperture address or
* for IGP TOM base address ) .
*
* If there is not enough space to fit the unvisible VRAM in the 32 bits
* address space then we limit the VRAM size to the aperture .
*
* Note : We don ' t explicitly enforce VRAM start to be aligned on VRAM size ,
* this shouldn ' t be a problem as we are using the PCI aperture as a reference .
* Otherwise this would be needed for rv280 , all r3xx , and all r4xx , but
* not IGP .
*
* Note : we use mc_vram_size as on some board we need to program the mc to
* cover the whole aperture even if VRAM size is inferior to aperture size
* Novell bug 204882 + along with lots of ubuntu ones
*
* Note : when limiting vram it ' s safe to overwritte real_vram_size because
* we are not in case where real_vram_size is inferior to mc_vram_size ( ie
* note afected by bogus hw of Novell bug 204882 + along with lots of ubuntu
* ones )
*
* Note : IGP TOM addr should be the same as the aperture addr , we don ' t
* explicitly check for that thought .
*
* FIXME : when reducing VRAM size align new size on power of 2.
*/
void amdgpu_vram_location ( struct amdgpu_device * adev , struct amdgpu_mc * mc , u64 base )
{
uint64_t limit = ( uint64_t ) amdgpu_vram_limit < < 20 ;
mc - > vram_start = base ;
if ( mc - > mc_vram_size > ( adev - > mc . mc_mask - base + 1 ) ) {
dev_warn ( adev - > dev , " limiting VRAM to PCI aperture size \n " ) ;
mc - > real_vram_size = mc - > aper_size ;
mc - > mc_vram_size = mc - > aper_size ;
}
mc - > vram_end = mc - > vram_start + mc - > mc_vram_size - 1 ;
if ( limit & & limit < mc - > real_vram_size )
mc - > real_vram_size = limit ;
dev_info ( adev - > dev , " VRAM: %lluM 0x%016llX - 0x%016llX (%lluM used) \n " ,
mc - > mc_vram_size > > 20 , mc - > vram_start ,
mc - > vram_end , mc - > real_vram_size > > 20 ) ;
}
/**
* amdgpu_gtt_location - try to find GTT location
* @ adev : amdgpu device structure holding all necessary informations
* @ mc : memory controller structure holding memory informations
*
* Function will place try to place GTT before or after VRAM .
*
* If GTT size is bigger than space left then we ajust GTT size .
* Thus function will never fails .
*
* FIXME : when reducing GTT size align new size on power of 2.
*/
void amdgpu_gtt_location ( struct amdgpu_device * adev , struct amdgpu_mc * mc )
{
u64 size_af , size_bf ;
size_af = ( ( adev - > mc . mc_mask - mc - > vram_end ) + mc - > gtt_base_align ) & ~ mc - > gtt_base_align ;
size_bf = mc - > vram_start & ~ mc - > gtt_base_align ;
if ( size_bf > size_af ) {
if ( mc - > gtt_size > size_bf ) {
dev_warn ( adev - > dev , " limiting GTT \n " ) ;
mc - > gtt_size = size_bf ;
}
mc - > gtt_start = ( mc - > vram_start & ~ mc - > gtt_base_align ) - mc - > gtt_size ;
} else {
if ( mc - > gtt_size > size_af ) {
dev_warn ( adev - > dev , " limiting GTT \n " ) ;
mc - > gtt_size = size_af ;
}
mc - > gtt_start = ( mc - > vram_end + 1 + mc - > gtt_base_align ) & ~ mc - > gtt_base_align ;
}
mc - > gtt_end = mc - > gtt_start + mc - > gtt_size - 1 ;
dev_info ( adev - > dev , " GTT: %lluM 0x%016llX - 0x%016llX \n " ,
mc - > gtt_size > > 20 , mc - > gtt_start , mc - > gtt_end ) ;
}
/*
* GPU helpers function .
*/
/**
* amdgpu_card_posted - check if the hw has already been initialized
*
* @ adev : amdgpu_device pointer
*
* Check if the asic has been initialized ( all asics ) .
* Used at driver startup .
* Returns true if initialized or false if not .
*/
bool amdgpu_card_posted ( struct amdgpu_device * adev )
{
uint32_t reg ;
/* then check MEM_SIZE, in case the crtcs are off */
reg = RREG32 ( mmCONFIG_MEMSIZE ) ;
if ( reg )
return true ;
return false ;
}
/**
* amdgpu_boot_test_post_card - check and possibly initialize the hw
*
* @ adev : amdgpu_device pointer
*
* Check if the asic is initialized and if not , attempt to initialize
* it ( all asics ) .
* Returns true if initialized or false if not .
*/
bool amdgpu_boot_test_post_card ( struct amdgpu_device * adev )
{
if ( amdgpu_card_posted ( adev ) )
return true ;
if ( adev - > bios ) {
DRM_INFO ( " GPU not posted. posting now... \n " ) ;
if ( adev - > is_atom_bios )
amdgpu_atom_asic_init ( adev - > mode_info . atom_context ) ;
return true ;
} else {
dev_err ( adev - > dev , " Card not posted and no BIOS - ignoring \n " ) ;
return false ;
}
}
/**
* amdgpu_dummy_page_init - init dummy page used by the driver
*
* @ adev : amdgpu_device pointer
*
* Allocate the dummy page used by the driver ( all asics ) .
* This dummy page is used by the driver as a filler for gart entries
* when pages are taken out of the GART
* Returns 0 on sucess , - ENOMEM on failure .
*/
int amdgpu_dummy_page_init ( struct amdgpu_device * adev )
{
if ( adev - > dummy_page . page )
return 0 ;
adev - > dummy_page . page = alloc_page ( GFP_DMA32 | GFP_KERNEL | __GFP_ZERO ) ;
if ( adev - > dummy_page . page = = NULL )
return - ENOMEM ;
adev - > dummy_page . addr = pci_map_page ( adev - > pdev , adev - > dummy_page . page ,
0 , PAGE_SIZE , PCI_DMA_BIDIRECTIONAL ) ;
if ( pci_dma_mapping_error ( adev - > pdev , adev - > dummy_page . addr ) ) {
dev_err ( & adev - > pdev - > dev , " Failed to DMA MAP the dummy page \n " ) ;
__free_page ( adev - > dummy_page . page ) ;
adev - > dummy_page . page = NULL ;
return - ENOMEM ;
}
return 0 ;
}
/**
* amdgpu_dummy_page_fini - free dummy page used by the driver
*
* @ adev : amdgpu_device pointer
*
* Frees the dummy page used by the driver ( all asics ) .
*/
void amdgpu_dummy_page_fini ( struct amdgpu_device * adev )
{
if ( adev - > dummy_page . page = = NULL )
return ;
pci_unmap_page ( adev - > pdev , adev - > dummy_page . addr ,
PAGE_SIZE , PCI_DMA_BIDIRECTIONAL ) ;
__free_page ( adev - > dummy_page . page ) ;
adev - > dummy_page . page = NULL ;
}
/* ATOM accessor methods */
/*
* ATOM is an interpreted byte code stored in tables in the vbios . The
* driver registers callbacks to access registers and the interpreter
* in the driver parses the tables and executes then to program specific
* actions ( set display modes , asic init , etc . ) . See amdgpu_atombios . c ,
* atombios . h , and atom . c
*/
/**
* cail_pll_read - read PLL register
*
* @ info : atom card_info pointer
* @ reg : PLL register offset
*
* Provides a PLL register accessor for the atom interpreter ( r4xx + ) .
* Returns the value of the PLL register .
*/
static uint32_t cail_pll_read ( struct card_info * info , uint32_t reg )
{
return 0 ;
}
/**
* cail_pll_write - write PLL register
*
* @ info : atom card_info pointer
* @ reg : PLL register offset
* @ val : value to write to the pll register
*
* Provides a PLL register accessor for the atom interpreter ( r4xx + ) .
*/
static void cail_pll_write ( struct card_info * info , uint32_t reg , uint32_t val )
{
}
/**
* cail_mc_read - read MC ( Memory Controller ) register
*
* @ info : atom card_info pointer
* @ reg : MC register offset
*
* Provides an MC register accessor for the atom interpreter ( r4xx + ) .
* Returns the value of the MC register .
*/
static uint32_t cail_mc_read ( struct card_info * info , uint32_t reg )
{
return 0 ;
}
/**
* cail_mc_write - write MC ( Memory Controller ) register
*
* @ info : atom card_info pointer
* @ reg : MC register offset
* @ val : value to write to the pll register
*
* Provides a MC register accessor for the atom interpreter ( r4xx + ) .
*/
static void cail_mc_write ( struct card_info * info , uint32_t reg , uint32_t val )
{
}
/**
* cail_reg_write - write MMIO register
*
* @ info : atom card_info pointer
* @ reg : MMIO register offset
* @ val : value to write to the pll register
*
* Provides a MMIO register accessor for the atom interpreter ( r4xx + ) .
*/
static void cail_reg_write ( struct card_info * info , uint32_t reg , uint32_t val )
{
struct amdgpu_device * adev = info - > dev - > dev_private ;
WREG32 ( reg , val ) ;
}
/**
* cail_reg_read - read MMIO register
*
* @ info : atom card_info pointer
* @ reg : MMIO register offset
*
* Provides an MMIO register accessor for the atom interpreter ( r4xx + ) .
* Returns the value of the MMIO register .
*/
static uint32_t cail_reg_read ( struct card_info * info , uint32_t reg )
{
struct amdgpu_device * adev = info - > dev - > dev_private ;
uint32_t r ;
r = RREG32 ( reg ) ;
return r ;
}
/**
* cail_ioreg_write - write IO register
*
* @ info : atom card_info pointer
* @ reg : IO register offset
* @ val : value to write to the pll register
*
* Provides a IO register accessor for the atom interpreter ( r4xx + ) .
*/
static void cail_ioreg_write ( struct card_info * info , uint32_t reg , uint32_t val )
{
struct amdgpu_device * adev = info - > dev - > dev_private ;
WREG32_IO ( reg , val ) ;
}
/**
* cail_ioreg_read - read IO register
*
* @ info : atom card_info pointer
* @ reg : IO register offset
*
* Provides an IO register accessor for the atom interpreter ( r4xx + ) .
* Returns the value of the IO register .
*/
static uint32_t cail_ioreg_read ( struct card_info * info , uint32_t reg )
{
struct amdgpu_device * adev = info - > dev - > dev_private ;
uint32_t r ;
r = RREG32_IO ( reg ) ;
return r ;
}
/**
* amdgpu_atombios_fini - free the driver info and callbacks for atombios
*
* @ adev : amdgpu_device pointer
*
* Frees the driver info and register access callbacks for the ATOM
* interpreter ( r4xx + ) .
* Called at driver shutdown .
*/
static void amdgpu_atombios_fini ( struct amdgpu_device * adev )
{
if ( adev - > mode_info . atom_context )
kfree ( adev - > mode_info . atom_context - > scratch ) ;
kfree ( adev - > mode_info . atom_context ) ;
adev - > mode_info . atom_context = NULL ;
kfree ( adev - > mode_info . atom_card_info ) ;
adev - > mode_info . atom_card_info = NULL ;
}
/**
* amdgpu_atombios_init - init the driver info and callbacks for atombios
*
* @ adev : amdgpu_device pointer
*
* Initializes the driver info and register access callbacks for the
* ATOM interpreter ( r4xx + ) .
* Returns 0 on sucess , - ENOMEM on failure .
* Called at driver startup .
*/
static int amdgpu_atombios_init ( struct amdgpu_device * adev )
{
struct card_info * atom_card_info =
kzalloc ( sizeof ( struct card_info ) , GFP_KERNEL ) ;
if ( ! atom_card_info )
return - ENOMEM ;
adev - > mode_info . atom_card_info = atom_card_info ;
atom_card_info - > dev = adev - > ddev ;
atom_card_info - > reg_read = cail_reg_read ;
atom_card_info - > reg_write = cail_reg_write ;
/* needed for iio ops */
if ( adev - > rio_mem ) {
atom_card_info - > ioreg_read = cail_ioreg_read ;
atom_card_info - > ioreg_write = cail_ioreg_write ;
} else {
DRM_ERROR ( " Unable to find PCI I/O BAR; using MMIO for ATOM IIO \n " ) ;
atom_card_info - > ioreg_read = cail_reg_read ;
atom_card_info - > ioreg_write = cail_reg_write ;
}
atom_card_info - > mc_read = cail_mc_read ;
atom_card_info - > mc_write = cail_mc_write ;
atom_card_info - > pll_read = cail_pll_read ;
atom_card_info - > pll_write = cail_pll_write ;
adev - > mode_info . atom_context = amdgpu_atom_parse ( atom_card_info , adev - > bios ) ;
if ( ! adev - > mode_info . atom_context ) {
amdgpu_atombios_fini ( adev ) ;
return - ENOMEM ;
}
mutex_init ( & adev - > mode_info . atom_context - > mutex ) ;
amdgpu_atombios_scratch_regs_init ( adev ) ;
amdgpu_atom_allocate_fb_scratch ( adev - > mode_info . atom_context ) ;
return 0 ;
}
/* if we get transitioned to only one device, take VGA back */
/**
* amdgpu_vga_set_decode - enable / disable vga decode
*
* @ cookie : amdgpu_device pointer
* @ state : enable / disable vga decode
*
* Enable / disable vga decode ( all asics ) .
* Returns VGA resource flags .
*/
static unsigned int amdgpu_vga_set_decode ( void * cookie , bool state )
{
struct amdgpu_device * adev = cookie ;
amdgpu_asic_set_vga_state ( adev , state ) ;
if ( state )
return VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM |
VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM ;
else
return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM ;
}
/**
* amdgpu_check_pot_argument - check that argument is a power of two
*
* @ arg : value to check
*
* Validates that a certain argument is a power of two ( all asics ) .
* Returns true if argument is valid .
*/
static bool amdgpu_check_pot_argument ( int arg )
{
return ( arg & ( arg - 1 ) ) = = 0 ;
}
/**
* amdgpu_check_arguments - validate module params
*
* @ adev : amdgpu_device pointer
*
* Validates certain module parameters and updates
* the associated values used by the driver ( all asics ) .
*/
static void amdgpu_check_arguments ( struct amdgpu_device * adev )
{
/* vramlimit must be a power of two */
if ( ! amdgpu_check_pot_argument ( amdgpu_vram_limit ) ) {
dev_warn ( adev - > dev , " vram limit (%d) must be a power of 2 \n " ,
amdgpu_vram_limit ) ;
amdgpu_vram_limit = 0 ;
}
if ( amdgpu_gart_size ! = - 1 ) {
/* gtt size must be power of two and greater or equal to 32M */
if ( amdgpu_gart_size < 32 ) {
dev_warn ( adev - > dev , " gart size (%d) too small \n " ,
amdgpu_gart_size ) ;
amdgpu_gart_size = - 1 ;
} else if ( ! amdgpu_check_pot_argument ( amdgpu_gart_size ) ) {
dev_warn ( adev - > dev , " gart size (%d) must be a power of 2 \n " ,
amdgpu_gart_size ) ;
amdgpu_gart_size = - 1 ;
}
}
if ( ! amdgpu_check_pot_argument ( amdgpu_vm_size ) ) {
dev_warn ( adev - > dev , " VM size (%d) must be a power of 2 \n " ,
amdgpu_vm_size ) ;
amdgpu_vm_size = 4 ;
}
if ( amdgpu_vm_size < 1 ) {
dev_warn ( adev - > dev , " VM size (%d) too small, min is 1GB \n " ,
amdgpu_vm_size ) ;
amdgpu_vm_size = 4 ;
}
/*
* Max GPUVM size for Cayman , SI and CI are 40 bits .
*/
if ( amdgpu_vm_size > 1024 ) {
dev_warn ( adev - > dev , " VM size (%d) too large, max is 1TB \n " ,
amdgpu_vm_size ) ;
amdgpu_vm_size = 4 ;
}
/* defines number of bits in page table versus page directory,
* a page is 4 KB so we have 12 bits offset , minimum 9 bits in the
* page table and the remaining bits are in the page directory */
if ( amdgpu_vm_block_size = = - 1 ) {
/* Total bits covered by PD + PTs */
unsigned bits = ilog2 ( amdgpu_vm_size ) + 18 ;
/* Make sure the PD is 4K in size up to 8GB address space.
Above that split equal between PD and PTs */
if ( amdgpu_vm_size < = 8 )
amdgpu_vm_block_size = bits - 9 ;
else
amdgpu_vm_block_size = ( bits + 3 ) / 2 ;
} else if ( amdgpu_vm_block_size < 9 ) {
dev_warn ( adev - > dev , " VM page table size (%d) too small \n " ,
amdgpu_vm_block_size ) ;
amdgpu_vm_block_size = 9 ;
}
if ( amdgpu_vm_block_size > 24 | |
( amdgpu_vm_size * 1024 ) < ( 1ull < < amdgpu_vm_block_size ) ) {
dev_warn ( adev - > dev , " VM page table size (%d) too large \n " ,
amdgpu_vm_block_size ) ;
amdgpu_vm_block_size = 9 ;
}
}
/**
* amdgpu_switcheroo_set_state - set switcheroo state
*
* @ pdev : pci dev pointer
* @ state : vga switcheroo state
*
* Callback for the switcheroo driver . Suspends or resumes the
* the asics before or after it is powered up using ACPI methods .
*/
static void amdgpu_switcheroo_set_state ( struct pci_dev * pdev , enum vga_switcheroo_state state )
{
struct drm_device * dev = pci_get_drvdata ( pdev ) ;
if ( amdgpu_device_is_px ( dev ) & & state = = VGA_SWITCHEROO_OFF )
return ;
if ( state = = VGA_SWITCHEROO_ON ) {
unsigned d3_delay = dev - > pdev - > d3_delay ;
printk ( KERN_INFO " amdgpu: switched on \n " ) ;
/* don't suspend or resume card normally */
dev - > switch_power_state = DRM_SWITCH_POWER_CHANGING ;
amdgpu_resume_kms ( dev , true , true ) ;
dev - > pdev - > d3_delay = d3_delay ;
dev - > switch_power_state = DRM_SWITCH_POWER_ON ;
drm_kms_helper_poll_enable ( dev ) ;
} else {
printk ( KERN_INFO " amdgpu: switched off \n " ) ;
drm_kms_helper_poll_disable ( dev ) ;
dev - > switch_power_state = DRM_SWITCH_POWER_CHANGING ;
amdgpu_suspend_kms ( dev , true , true ) ;
dev - > switch_power_state = DRM_SWITCH_POWER_OFF ;
}
}
/**
* amdgpu_switcheroo_can_switch - see if switcheroo state can change
*
* @ pdev : pci dev pointer
*
* Callback for the switcheroo driver . Check of the switcheroo
* state can be changed .
* Returns true if the state can be changed , false if not .
*/
static bool amdgpu_switcheroo_can_switch ( struct pci_dev * pdev )
{
struct drm_device * dev = pci_get_drvdata ( pdev ) ;
/*
* FIXME : open_count is protected by drm_global_mutex but that would lead to
* locking inversion with the driver load path . And the access here is
* completely racy anyway . So don ' t bother with locking for now .
*/
return dev - > open_count = = 0 ;
}
static const struct vga_switcheroo_client_ops amdgpu_switcheroo_ops = {
. set_gpu_state = amdgpu_switcheroo_set_state ,
. reprobe = NULL ,
. can_switch = amdgpu_switcheroo_can_switch ,
} ;
int amdgpu_set_clockgating_state ( struct amdgpu_device * adev ,
enum amdgpu_ip_block_type block_type ,
enum amdgpu_clockgating_state state )
{
int i , r = 0 ;
for ( i = 0 ; i < adev - > num_ip_blocks ; i + + ) {
if ( adev - > ip_blocks [ i ] . type = = block_type ) {
r = adev - > ip_blocks [ i ] . funcs - > set_clockgating_state ( adev ,
state ) ;
if ( r )
return r ;
}
}
return r ;
}
int amdgpu_set_powergating_state ( struct amdgpu_device * adev ,
enum amdgpu_ip_block_type block_type ,
enum amdgpu_powergating_state state )
{
int i , r = 0 ;
for ( i = 0 ; i < adev - > num_ip_blocks ; i + + ) {
if ( adev - > ip_blocks [ i ] . type = = block_type ) {
r = adev - > ip_blocks [ i ] . funcs - > set_powergating_state ( adev ,
state ) ;
if ( r )
return r ;
}
}
return r ;
}
const struct amdgpu_ip_block_version * amdgpu_get_ip_block (
struct amdgpu_device * adev ,
enum amdgpu_ip_block_type type )
{
int i ;
for ( i = 0 ; i < adev - > num_ip_blocks ; i + + )
if ( adev - > ip_blocks [ i ] . type = = type )
return & adev - > ip_blocks [ i ] ;
return NULL ;
}
/**
* amdgpu_ip_block_version_cmp
*
* @ adev : amdgpu_device pointer
* @ type : enum amdgpu_ip_block_type
* @ major : major version
* @ minor : minor version
*
* return 0 if equal or greater
* return 1 if smaller or the ip_block doesn ' t exist
*/
int amdgpu_ip_block_version_cmp ( struct amdgpu_device * adev ,
enum amdgpu_ip_block_type type ,
u32 major , u32 minor )
{
const struct amdgpu_ip_block_version * ip_block ;
ip_block = amdgpu_get_ip_block ( adev , type ) ;
if ( ip_block & & ( ( ip_block - > major > major ) | |
( ( ip_block - > major = = major ) & &
( ip_block - > minor > = minor ) ) ) )
return 0 ;
return 1 ;
}
static int amdgpu_early_init ( struct amdgpu_device * adev )
{
2015-04-20 17:31:14 -04:00
int i , r ;
2015-04-20 16:55:21 -04:00
switch ( adev - > asic_type ) {
2015-04-20 17:31:14 -04:00
case CHIP_TOPAZ :
case CHIP_TONGA :
case CHIP_CARRIZO :
if ( adev - > asic_type = = CHIP_CARRIZO )
adev - > family = AMDGPU_FAMILY_CZ ;
else
adev - > family = AMDGPU_FAMILY_VI ;
r = vi_set_ip_blocks ( adev ) ;
if ( r )
return r ;
break ;
2015-04-20 17:09:27 -04:00
# ifdef CONFIG_DRM_AMDGPU_CIK
case CHIP_BONAIRE :
case CHIP_HAWAII :
case CHIP_KAVERI :
case CHIP_KABINI :
case CHIP_MULLINS :
if ( ( adev - > asic_type = = CHIP_BONAIRE ) | | ( adev - > asic_type = = CHIP_HAWAII ) )
adev - > family = AMDGPU_FAMILY_CI ;
else
adev - > family = AMDGPU_FAMILY_KV ;
r = cik_set_ip_blocks ( adev ) ;
if ( r )
return r ;
break ;
# endif
2015-04-20 16:55:21 -04:00
default :
/* FIXME: not supported yet */
return - EINVAL ;
}
if ( adev - > ip_blocks = = NULL ) {
DRM_ERROR ( " No IP blocks found! \n " ) ;
return r ;
}
for ( i = 0 ; i < adev - > num_ip_blocks ; i + + ) {
if ( ( amdgpu_ip_block_mask & ( 1 < < i ) ) = = 0 ) {
DRM_ERROR ( " disabled ip block: %d \n " , i ) ;
adev - > ip_block_enabled [ i ] = false ;
} else {
if ( adev - > ip_blocks [ i ] . funcs - > early_init ) {
r = adev - > ip_blocks [ i ] . funcs - > early_init ( adev ) ;
if ( r )
return r ;
}
adev - > ip_block_enabled [ i ] = true ;
}
}
return 0 ;
}
static int amdgpu_init ( struct amdgpu_device * adev )
{
int i , r ;
for ( i = 0 ; i < adev - > num_ip_blocks ; i + + ) {
if ( ! adev - > ip_block_enabled [ i ] )
continue ;
r = adev - > ip_blocks [ i ] . funcs - > sw_init ( adev ) ;
if ( r )
return r ;
/* need to do gmc hw init early so we can allocate gpu mem */
if ( adev - > ip_blocks [ i ] . type = = AMDGPU_IP_BLOCK_TYPE_GMC ) {
r = amdgpu_vram_scratch_init ( adev ) ;
if ( r )
return r ;
r = adev - > ip_blocks [ i ] . funcs - > hw_init ( adev ) ;
if ( r )
return r ;
r = amdgpu_wb_init ( adev ) ;
if ( r )
return r ;
}
}
for ( i = 0 ; i < adev - > num_ip_blocks ; i + + ) {
if ( ! adev - > ip_block_enabled [ i ] )
continue ;
/* gmc hw init is done early */
if ( adev - > ip_blocks [ i ] . type = = AMDGPU_IP_BLOCK_TYPE_GMC )
continue ;
r = adev - > ip_blocks [ i ] . funcs - > hw_init ( adev ) ;
if ( r )
return r ;
}
return 0 ;
}
static int amdgpu_late_init ( struct amdgpu_device * adev )
{
int i = 0 , r ;
for ( i = 0 ; i < adev - > num_ip_blocks ; i + + ) {
if ( ! adev - > ip_block_enabled [ i ] )
continue ;
/* enable clockgating to save power */
r = adev - > ip_blocks [ i ] . funcs - > set_clockgating_state ( adev ,
AMDGPU_CG_STATE_GATE ) ;
if ( r )
return r ;
if ( adev - > ip_blocks [ i ] . funcs - > late_init ) {
r = adev - > ip_blocks [ i ] . funcs - > late_init ( adev ) ;
if ( r )
return r ;
}
}
return 0 ;
}
static int amdgpu_fini ( struct amdgpu_device * adev )
{
int i , r ;
for ( i = adev - > num_ip_blocks - 1 ; i > = 0 ; i - - ) {
if ( ! adev - > ip_block_enabled [ i ] )
continue ;
if ( adev - > ip_blocks [ i ] . type = = AMDGPU_IP_BLOCK_TYPE_GMC ) {
amdgpu_wb_fini ( adev ) ;
amdgpu_vram_scratch_fini ( adev ) ;
}
/* ungate blocks before hw fini so that we can shutdown the blocks safely */
r = adev - > ip_blocks [ i ] . funcs - > set_clockgating_state ( adev ,
AMDGPU_CG_STATE_UNGATE ) ;
if ( r )
return r ;
r = adev - > ip_blocks [ i ] . funcs - > hw_fini ( adev ) ;
/* XXX handle errors */
}
for ( i = adev - > num_ip_blocks - 1 ; i > = 0 ; i - - ) {
if ( ! adev - > ip_block_enabled [ i ] )
continue ;
r = adev - > ip_blocks [ i ] . funcs - > sw_fini ( adev ) ;
/* XXX handle errors */
adev - > ip_block_enabled [ i ] = false ;
}
return 0 ;
}
static int amdgpu_suspend ( struct amdgpu_device * adev )
{
int i , r ;
for ( i = adev - > num_ip_blocks - 1 ; i > = 0 ; i - - ) {
if ( ! adev - > ip_block_enabled [ i ] )
continue ;
/* ungate blocks so that suspend can properly shut them down */
r = adev - > ip_blocks [ i ] . funcs - > set_clockgating_state ( adev ,
AMDGPU_CG_STATE_UNGATE ) ;
/* XXX handle errors */
r = adev - > ip_blocks [ i ] . funcs - > suspend ( adev ) ;
/* XXX handle errors */
}
return 0 ;
}
static int amdgpu_resume ( struct amdgpu_device * adev )
{
int i , r ;
for ( i = 0 ; i < adev - > num_ip_blocks ; i + + ) {
if ( ! adev - > ip_block_enabled [ i ] )
continue ;
r = adev - > ip_blocks [ i ] . funcs - > resume ( adev ) ;
if ( r )
return r ;
}
return 0 ;
}
/**
* amdgpu_device_init - initialize the driver
*
* @ adev : amdgpu_device pointer
* @ pdev : drm dev pointer
* @ pdev : pci dev pointer
* @ flags : driver flags
*
* Initializes the driver info and hw ( all asics ) .
* Returns 0 for success or an error on failure .
* Called at driver startup .
*/
int amdgpu_device_init ( struct amdgpu_device * adev ,
struct drm_device * ddev ,
struct pci_dev * pdev ,
uint32_t flags )
{
int r , i ;
bool runtime = false ;
adev - > shutdown = false ;
adev - > dev = & pdev - > dev ;
adev - > ddev = ddev ;
adev - > pdev = pdev ;
adev - > flags = flags ;
adev - > asic_type = flags & AMDGPU_ASIC_MASK ;
adev - > is_atom_bios = false ;
adev - > usec_timeout = AMDGPU_MAX_USEC_TIMEOUT ;
adev - > mc . gtt_size = 512 * 1024 * 1024 ;
adev - > accel_working = false ;
adev - > num_rings = 0 ;
adev - > mman . buffer_funcs = NULL ;
adev - > mman . buffer_funcs_ring = NULL ;
adev - > vm_manager . vm_pte_funcs = NULL ;
adev - > vm_manager . vm_pte_funcs_ring = NULL ;
adev - > gart . gart_funcs = NULL ;
adev - > fence_context = fence_context_alloc ( AMDGPU_MAX_RINGS ) ;
adev - > smc_rreg = & amdgpu_invalid_rreg ;
adev - > smc_wreg = & amdgpu_invalid_wreg ;
adev - > pcie_rreg = & amdgpu_invalid_rreg ;
adev - > pcie_wreg = & amdgpu_invalid_wreg ;
adev - > uvd_ctx_rreg = & amdgpu_invalid_rreg ;
adev - > uvd_ctx_wreg = & amdgpu_invalid_wreg ;
adev - > didt_rreg = & amdgpu_invalid_rreg ;
adev - > didt_wreg = & amdgpu_invalid_wreg ;
adev - > audio_endpt_rreg = & amdgpu_block_invalid_rreg ;
adev - > audio_endpt_wreg = & amdgpu_block_invalid_wreg ;
DRM_INFO ( " initializing kernel modesetting (%s 0x%04X:0x%04X 0x%04X:0x%04X). \n " ,
amdgpu_asic_name [ adev - > asic_type ] , pdev - > vendor , pdev - > device ,
pdev - > subsystem_vendor , pdev - > subsystem_device ) ;
/* mutex initialization are all done here so we
* can recall function without having locking issues */
mutex_init ( & adev - > ring_lock ) ;
atomic_set ( & adev - > irq . ih . lock , 0 ) ;
mutex_init ( & adev - > gem . mutex ) ;
mutex_init ( & adev - > pm . mutex ) ;
mutex_init ( & adev - > gfx . gpu_clock_mutex ) ;
mutex_init ( & adev - > srbm_mutex ) ;
mutex_init ( & adev - > grbm_idx_mutex ) ;
init_rwsem ( & adev - > pm . mclk_lock ) ;
init_rwsem ( & adev - > exclusive_lock ) ;
mutex_init ( & adev - > mn_lock ) ;
hash_init ( adev - > mn_hash ) ;
amdgpu_check_arguments ( adev ) ;
/* Registers mapping */
/* TODO: block userspace mapping of io register */
spin_lock_init ( & adev - > mmio_idx_lock ) ;
spin_lock_init ( & adev - > smc_idx_lock ) ;
spin_lock_init ( & adev - > pcie_idx_lock ) ;
spin_lock_init ( & adev - > uvd_ctx_idx_lock ) ;
spin_lock_init ( & adev - > didt_idx_lock ) ;
spin_lock_init ( & adev - > audio_endpt_idx_lock ) ;
adev - > rmmio_base = pci_resource_start ( adev - > pdev , 5 ) ;
adev - > rmmio_size = pci_resource_len ( adev - > pdev , 5 ) ;
adev - > rmmio = ioremap ( adev - > rmmio_base , adev - > rmmio_size ) ;
if ( adev - > rmmio = = NULL ) {
return - ENOMEM ;
}
DRM_INFO ( " register mmio base: 0x%08X \n " , ( uint32_t ) adev - > rmmio_base ) ;
DRM_INFO ( " register mmio size: %u \n " , ( unsigned ) adev - > rmmio_size ) ;
/* doorbell bar mapping */
amdgpu_doorbell_init ( adev ) ;
/* io port mapping */
for ( i = 0 ; i < DEVICE_COUNT_RESOURCE ; i + + ) {
if ( pci_resource_flags ( adev - > pdev , i ) & IORESOURCE_IO ) {
adev - > rio_mem_size = pci_resource_len ( adev - > pdev , i ) ;
adev - > rio_mem = pci_iomap ( adev - > pdev , i , adev - > rio_mem_size ) ;
break ;
}
}
if ( adev - > rio_mem = = NULL )
DRM_ERROR ( " Unable to find PCI I/O BAR \n " ) ;
/* early init functions */
r = amdgpu_early_init ( adev ) ;
if ( r )
return r ;
/* if we have > 1 VGA cards, then disable the amdgpu VGA resources */
/* this will fail for cards that aren't VGA class devices, just
* ignore it */
vga_client_register ( adev - > pdev , adev , NULL , amdgpu_vga_set_decode ) ;
if ( amdgpu_runtime_pm = = 1 )
runtime = true ;
if ( amdgpu_device_is_px ( ddev ) )
runtime = true ;
vga_switcheroo_register_client ( adev - > pdev , & amdgpu_switcheroo_ops , runtime ) ;
if ( runtime )
vga_switcheroo_init_domain_pm_ops ( adev - > dev , & adev - > vga_pm_domain ) ;
/* Read BIOS */
if ( ! amdgpu_get_bios ( adev ) )
return - EINVAL ;
/* Must be an ATOMBIOS */
if ( ! adev - > is_atom_bios ) {
dev_err ( adev - > dev , " Expecting atombios for GPU \n " ) ;
return - EINVAL ;
}
r = amdgpu_atombios_init ( adev ) ;
if ( r )
return r ;
/* Post card if necessary */
if ( ! amdgpu_card_posted ( adev ) ) {
if ( ! adev - > bios ) {
dev_err ( adev - > dev , " Card not posted and no BIOS - ignoring \n " ) ;
return - EINVAL ;
}
DRM_INFO ( " GPU not posted. posting now... \n " ) ;
amdgpu_atom_asic_init ( adev - > mode_info . atom_context ) ;
}
/* Initialize clocks */
r = amdgpu_atombios_get_clock_info ( adev ) ;
if ( r )
return r ;
/* init i2c buses */
amdgpu_atombios_i2c_init ( adev ) ;
/* Fence driver */
r = amdgpu_fence_driver_init ( adev ) ;
if ( r )
return r ;
/* init the mode config */
drm_mode_config_init ( adev - > ddev ) ;
r = amdgpu_init ( adev ) ;
if ( r ) {
amdgpu_fini ( adev ) ;
return r ;
}
adev - > accel_working = true ;
amdgpu_fbdev_init ( adev ) ;
r = amdgpu_ib_pool_init ( adev ) ;
if ( r ) {
dev_err ( adev - > dev , " IB initialization failed (%d). \n " , r ) ;
return r ;
}
r = amdgpu_ib_ring_tests ( adev ) ;
if ( r )
DRM_ERROR ( " ib ring test failed (%d). \n " , r ) ;
r = amdgpu_gem_debugfs_init ( adev ) ;
if ( r ) {
DRM_ERROR ( " registering gem debugfs failed (%d). \n " , r ) ;
}
r = amdgpu_debugfs_regs_init ( adev ) ;
if ( r ) {
DRM_ERROR ( " registering register debugfs failed (%d). \n " , r ) ;
}
if ( ( amdgpu_testing & 1 ) ) {
if ( adev - > accel_working )
amdgpu_test_moves ( adev ) ;
else
DRM_INFO ( " amdgpu: acceleration disabled, skipping move tests \n " ) ;
}
if ( ( amdgpu_testing & 2 ) ) {
if ( adev - > accel_working )
amdgpu_test_syncing ( adev ) ;
else
DRM_INFO ( " amdgpu: acceleration disabled, skipping sync tests \n " ) ;
}
if ( amdgpu_benchmarking ) {
if ( adev - > accel_working )
amdgpu_benchmark ( adev , amdgpu_benchmarking ) ;
else
DRM_INFO ( " amdgpu: acceleration disabled, skipping benchmarks \n " ) ;
}
/* enable clockgating, etc. after ib tests, etc. since some blocks require
* explicit gating rather than handling it automatically .
*/
r = amdgpu_late_init ( adev ) ;
if ( r )
return r ;
return 0 ;
}
static void amdgpu_debugfs_remove_files ( struct amdgpu_device * adev ) ;
/**
* amdgpu_device_fini - tear down the driver
*
* @ adev : amdgpu_device pointer
*
* Tear down the driver info ( all asics ) .
* Called at driver shutdown .
*/
void amdgpu_device_fini ( struct amdgpu_device * adev )
{
int r ;
DRM_INFO ( " amdgpu: finishing device. \n " ) ;
adev - > shutdown = true ;
/* evict vram memory */
amdgpu_bo_evict_vram ( adev ) ;
amdgpu_ib_pool_fini ( adev ) ;
amdgpu_fence_driver_fini ( adev ) ;
amdgpu_fbdev_fini ( adev ) ;
r = amdgpu_fini ( adev ) ;
if ( adev - > ip_block_enabled )
kfree ( adev - > ip_block_enabled ) ;
adev - > ip_block_enabled = NULL ;
adev - > accel_working = false ;
/* free i2c buses */
amdgpu_i2c_fini ( adev ) ;
amdgpu_atombios_fini ( adev ) ;
kfree ( adev - > bios ) ;
adev - > bios = NULL ;
vga_switcheroo_unregister_client ( adev - > pdev ) ;
vga_client_register ( adev - > pdev , NULL , NULL , NULL ) ;
if ( adev - > rio_mem )
pci_iounmap ( adev - > pdev , adev - > rio_mem ) ;
adev - > rio_mem = NULL ;
iounmap ( adev - > rmmio ) ;
adev - > rmmio = NULL ;
amdgpu_doorbell_fini ( adev ) ;
amdgpu_debugfs_regs_cleanup ( adev ) ;
amdgpu_debugfs_remove_files ( adev ) ;
}
/*
* Suspend & resume .
*/
/**
* amdgpu_suspend_kms - initiate device suspend
*
* @ pdev : drm dev pointer
* @ state : suspend state
*
* Puts the hw in the suspend state ( all asics ) .
* Returns 0 for success or an error on failure .
* Called at driver suspend .
*/
int amdgpu_suspend_kms ( struct drm_device * dev , bool suspend , bool fbcon )
{
struct amdgpu_device * adev ;
struct drm_crtc * crtc ;
struct drm_connector * connector ;
int i , r ;
bool force_completion = false ;
if ( dev = = NULL | | dev - > dev_private = = NULL ) {
return - ENODEV ;
}
adev = dev - > dev_private ;
if ( dev - > switch_power_state = = DRM_SWITCH_POWER_OFF )
return 0 ;
drm_kms_helper_poll_disable ( dev ) ;
/* turn off display hw */
list_for_each_entry ( connector , & dev - > mode_config . connector_list , head ) {
drm_helper_connector_dpms ( connector , DRM_MODE_DPMS_OFF ) ;
}
/* unpin the front buffers */
list_for_each_entry ( crtc , & dev - > mode_config . crtc_list , head ) {
struct amdgpu_framebuffer * rfb = to_amdgpu_framebuffer ( crtc - > primary - > fb ) ;
struct amdgpu_bo * robj ;
if ( rfb = = NULL | | rfb - > obj = = NULL ) {
continue ;
}
robj = gem_to_amdgpu_bo ( rfb - > obj ) ;
/* don't unpin kernel fb objects */
if ( ! amdgpu_fbdev_robj_is_fb ( adev , robj ) ) {
r = amdgpu_bo_reserve ( robj , false ) ;
if ( r = = 0 ) {
amdgpu_bo_unpin ( robj ) ;
amdgpu_bo_unreserve ( robj ) ;
}
}
}
/* evict vram memory */
amdgpu_bo_evict_vram ( adev ) ;
/* wait for gpu to finish processing current batch */
for ( i = 0 ; i < AMDGPU_MAX_RINGS ; i + + ) {
struct amdgpu_ring * ring = adev - > rings [ i ] ;
if ( ! ring )
continue ;
r = amdgpu_fence_wait_empty ( ring ) ;
if ( r ) {
/* delay GPU reset to resume */
force_completion = true ;
}
}
if ( force_completion ) {
amdgpu_fence_driver_force_completion ( adev ) ;
}
r = amdgpu_suspend ( adev ) ;
/* evict remaining vram memory */
amdgpu_bo_evict_vram ( adev ) ;
pci_save_state ( dev - > pdev ) ;
if ( suspend ) {
/* Shut down the device */
pci_disable_device ( dev - > pdev ) ;
pci_set_power_state ( dev - > pdev , PCI_D3hot ) ;
}
if ( fbcon ) {
console_lock ( ) ;
amdgpu_fbdev_set_suspend ( adev , 1 ) ;
console_unlock ( ) ;
}
return 0 ;
}
/**
* amdgpu_resume_kms - initiate device resume
*
* @ pdev : drm dev pointer
*
* Bring the hw back to operating state ( all asics ) .
* Returns 0 for success or an error on failure .
* Called at driver resume .
*/
int amdgpu_resume_kms ( struct drm_device * dev , bool resume , bool fbcon )
{
struct drm_connector * connector ;
struct amdgpu_device * adev = dev - > dev_private ;
int r ;
if ( dev - > switch_power_state = = DRM_SWITCH_POWER_OFF )
return 0 ;
if ( fbcon ) {
console_lock ( ) ;
}
if ( resume ) {
pci_set_power_state ( dev - > pdev , PCI_D0 ) ;
pci_restore_state ( dev - > pdev ) ;
if ( pci_enable_device ( dev - > pdev ) ) {
if ( fbcon )
console_unlock ( ) ;
return - 1 ;
}
}
/* post card */
amdgpu_atom_asic_init ( adev - > mode_info . atom_context ) ;
r = amdgpu_resume ( adev ) ;
r = amdgpu_ib_ring_tests ( adev ) ;
if ( r )
DRM_ERROR ( " ib ring test failed (%d). \n " , r ) ;
r = amdgpu_late_init ( adev ) ;
if ( r )
return r ;
/* blat the mode back in */
if ( fbcon ) {
drm_helper_resume_force_mode ( dev ) ;
/* turn on display hw */
list_for_each_entry ( connector , & dev - > mode_config . connector_list , head ) {
drm_helper_connector_dpms ( connector , DRM_MODE_DPMS_ON ) ;
}
}
drm_kms_helper_poll_enable ( dev ) ;
if ( fbcon ) {
amdgpu_fbdev_set_suspend ( adev , 0 ) ;
console_unlock ( ) ;
}
return 0 ;
}
/**
* amdgpu_gpu_reset - reset the asic
*
* @ adev : amdgpu device pointer
*
* Attempt the reset the GPU if it has hung ( all asics ) .
* Returns 0 for success or an error on failure .
*/
int amdgpu_gpu_reset ( struct amdgpu_device * adev )
{
unsigned ring_sizes [ AMDGPU_MAX_RINGS ] ;
uint32_t * ring_data [ AMDGPU_MAX_RINGS ] ;
bool saved = false ;
int i , r ;
int resched ;
down_write ( & adev - > exclusive_lock ) ;
if ( ! adev - > needs_reset ) {
up_write ( & adev - > exclusive_lock ) ;
return 0 ;
}
adev - > needs_reset = false ;
/* block TTM */
resched = ttm_bo_lock_delayed_workqueue ( & adev - > mman . bdev ) ;
r = amdgpu_suspend ( adev ) ;
for ( i = 0 ; i < AMDGPU_MAX_RINGS ; + + i ) {
struct amdgpu_ring * ring = adev - > rings [ i ] ;
if ( ! ring )
continue ;
ring_sizes [ i ] = amdgpu_ring_backup ( ring , & ring_data [ i ] ) ;
if ( ring_sizes [ i ] ) {
saved = true ;
dev_info ( adev - > dev , " Saved %d dwords of commands "
" on ring %d. \n " , ring_sizes [ i ] , i ) ;
}
}
retry :
r = amdgpu_asic_reset ( adev ) ;
if ( ! r ) {
dev_info ( adev - > dev , " GPU reset succeeded, trying to resume \n " ) ;
r = amdgpu_resume ( adev ) ;
}
if ( ! r ) {
for ( i = 0 ; i < AMDGPU_MAX_RINGS ; + + i ) {
struct amdgpu_ring * ring = adev - > rings [ i ] ;
if ( ! ring )
continue ;
amdgpu_ring_restore ( ring , ring_sizes [ i ] , ring_data [ i ] ) ;
ring_sizes [ i ] = 0 ;
ring_data [ i ] = NULL ;
}
r = amdgpu_ib_ring_tests ( adev ) ;
if ( r ) {
dev_err ( adev - > dev , " ib ring test failed (%d). \n " , r ) ;
if ( saved ) {
saved = false ;
r = amdgpu_suspend ( adev ) ;
goto retry ;
}
}
} else {
amdgpu_fence_driver_force_completion ( adev ) ;
for ( i = 0 ; i < AMDGPU_MAX_RINGS ; + + i ) {
if ( adev - > rings [ i ] )
kfree ( ring_data [ i ] ) ;
}
}
drm_helper_resume_force_mode ( adev - > ddev ) ;
ttm_bo_unlock_delayed_workqueue ( & adev - > mman . bdev , resched ) ;
if ( r ) {
/* bad news, how to tell it to userspace ? */
dev_info ( adev - > dev , " GPU reset failed \n " ) ;
}
up_write ( & adev - > exclusive_lock ) ;
return r ;
}
/*
* Debugfs
*/
int amdgpu_debugfs_add_files ( struct amdgpu_device * adev ,
struct drm_info_list * files ,
unsigned nfiles )
{
unsigned i ;
for ( i = 0 ; i < adev - > debugfs_count ; i + + ) {
if ( adev - > debugfs [ i ] . files = = files ) {
/* Already registered */
return 0 ;
}
}
i = adev - > debugfs_count + 1 ;
if ( i > AMDGPU_DEBUGFS_MAX_COMPONENTS ) {
DRM_ERROR ( " Reached maximum number of debugfs components. \n " ) ;
DRM_ERROR ( " Report so we increase "
" AMDGPU_DEBUGFS_MAX_COMPONENTS. \n " ) ;
return - EINVAL ;
}
adev - > debugfs [ adev - > debugfs_count ] . files = files ;
adev - > debugfs [ adev - > debugfs_count ] . num_files = nfiles ;
adev - > debugfs_count = i ;
# if defined(CONFIG_DEBUG_FS)
drm_debugfs_create_files ( files , nfiles ,
adev - > ddev - > control - > debugfs_root ,
adev - > ddev - > control ) ;
drm_debugfs_create_files ( files , nfiles ,
adev - > ddev - > primary - > debugfs_root ,
adev - > ddev - > primary ) ;
# endif
return 0 ;
}
static void amdgpu_debugfs_remove_files ( struct amdgpu_device * adev )
{
# if defined(CONFIG_DEBUG_FS)
unsigned i ;
for ( i = 0 ; i < adev - > debugfs_count ; i + + ) {
drm_debugfs_remove_files ( adev - > debugfs [ i ] . files ,
adev - > debugfs [ i ] . num_files ,
adev - > ddev - > control ) ;
drm_debugfs_remove_files ( adev - > debugfs [ i ] . files ,
adev - > debugfs [ i ] . num_files ,
adev - > ddev - > primary ) ;
}
# endif
}
# if defined(CONFIG_DEBUG_FS)
static ssize_t amdgpu_debugfs_regs_read ( struct file * f , char __user * buf ,
size_t size , loff_t * pos )
{
struct amdgpu_device * adev = f - > f_inode - > i_private ;
ssize_t result = 0 ;
int r ;
if ( size & 0x3 | | * pos & 0x3 )
return - EINVAL ;
while ( size ) {
uint32_t value ;
if ( * pos > adev - > rmmio_size )
return result ;
value = RREG32 ( * pos > > 2 ) ;
r = put_user ( value , ( uint32_t * ) buf ) ;
if ( r )
return r ;
result + = 4 ;
buf + = 4 ;
* pos + = 4 ;
size - = 4 ;
}
return result ;
}
static ssize_t amdgpu_debugfs_regs_write ( struct file * f , const char __user * buf ,
size_t size , loff_t * pos )
{
struct amdgpu_device * adev = f - > f_inode - > i_private ;
ssize_t result = 0 ;
int r ;
if ( size & 0x3 | | * pos & 0x3 )
return - EINVAL ;
while ( size ) {
uint32_t value ;
if ( * pos > adev - > rmmio_size )
return result ;
r = get_user ( value , ( uint32_t * ) buf ) ;
if ( r )
return r ;
WREG32 ( * pos > > 2 , value ) ;
result + = 4 ;
buf + = 4 ;
* pos + = 4 ;
size - = 4 ;
}
return result ;
}
static const struct file_operations amdgpu_debugfs_regs_fops = {
. owner = THIS_MODULE ,
. read = amdgpu_debugfs_regs_read ,
. write = amdgpu_debugfs_regs_write ,
. llseek = default_llseek
} ;
static int amdgpu_debugfs_regs_init ( struct amdgpu_device * adev )
{
struct drm_minor * minor = adev - > ddev - > primary ;
struct dentry * ent , * root = minor - > debugfs_root ;
ent = debugfs_create_file ( " amdgpu_regs " , S_IFREG | S_IRUGO , root ,
adev , & amdgpu_debugfs_regs_fops ) ;
if ( IS_ERR ( ent ) )
return PTR_ERR ( ent ) ;
i_size_write ( ent - > d_inode , adev - > rmmio_size ) ;
adev - > debugfs_regs = ent ;
return 0 ;
}
static void amdgpu_debugfs_regs_cleanup ( struct amdgpu_device * adev )
{
debugfs_remove ( adev - > debugfs_regs ) ;
adev - > debugfs_regs = NULL ;
}
int amdgpu_debugfs_init ( struct drm_minor * minor )
{
return 0 ;
}
void amdgpu_debugfs_cleanup ( struct drm_minor * minor )
{
}
# endif