2009-12-11 19:24:15 +10:00
/*
* Copyright ( C ) The Weather Channel , Inc . 2002. All Rights Reserved .
* Copyright 2005 Stephane Marchesin
*
* The Weather Channel ( TM ) funded Tungsten Graphics to develop the
* initial release of the Radeon 8500 driver under the XFree86 license .
* This notice must be preserved .
*
* 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 ( including the next
* paragraph ) 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 AUTHORS AND / OR THEIR 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 .
*
* Authors :
2012-02-07 00:29:06 +01:00
* Ben Skeggs < bskeggs @ redhat . com >
* Roy Spliet < r . spliet @ student . tudelft . nl >
2009-12-11 19:24:15 +10:00
*/
# include "drmP.h"
# include "drm.h"
# include "drm_sarea.h"
2010-10-11 03:43:58 +02:00
# include "nouveau_drv.h"
# include "nouveau_pm.h"
2010-08-25 15:26:04 +10:00
# include "nouveau_mm.h"
2010-08-27 10:00:25 +10:00
# include "nouveau_vm.h"
2012-04-30 13:30:00 +10:00
# include "nouveau_fence.h"
2010-10-04 23:01:08 +02:00
2009-12-11 16:51:09 +01:00
/*
* NV10 - NV40 tiling helpers
*/
static void
2010-10-24 16:14:41 +02:00
nv10_mem_update_tile_region ( struct drm_device * dev ,
struct nouveau_tile_reg * tile , uint32_t addr ,
uint32_t size , uint32_t pitch , uint32_t flags )
2009-12-11 16:51:09 +01:00
{
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
struct nouveau_fb_engine * pfb = & dev_priv - > engine . fb ;
2011-04-01 13:10:45 +10:00
int i = tile - dev_priv - > tile . reg , j ;
2010-10-24 16:14:41 +02:00
unsigned long save ;
2009-12-11 16:51:09 +01:00
2010-10-20 21:50:24 +02:00
nouveau_fence_unref ( & tile - > fence ) ;
2009-12-11 16:51:09 +01:00
2010-10-24 16:14:41 +02:00
if ( tile - > pitch )
pfb - > free_tile_region ( dev , i ) ;
if ( pitch )
pfb - > init_tile_region ( dev , i , addr , size , pitch , flags ) ;
spin_lock_irqsave ( & dev_priv - > context_switch_lock , save ) ;
2012-05-01 10:14:07 +10:00
nv_wr32 ( dev , NV03_PFIFO_CACHES , 0 ) ;
nv04_fifo_cache_pull ( dev , false ) ;
2009-12-11 16:51:09 +01:00
nouveau_wait_for_idle ( dev ) ;
2010-10-24 16:14:41 +02:00
pfb - > set_tile_region ( dev , i ) ;
2011-04-01 13:10:45 +10:00
for ( j = 0 ; j < NVOBJ_ENGINE_NR ; j + + ) {
if ( dev_priv - > eng [ j ] & & dev_priv - > eng [ j ] - > set_tile_region )
dev_priv - > eng [ j ] - > set_tile_region ( dev , i ) ;
}
2009-12-11 16:51:09 +01:00
2012-05-01 10:14:07 +10:00
nv04_fifo_cache_pull ( dev , true ) ;
nv_wr32 ( dev , NV03_PFIFO_CACHES , 1 ) ;
2010-10-24 16:14:41 +02:00
spin_unlock_irqrestore ( & dev_priv - > context_switch_lock , save ) ;
2009-12-11 16:51:09 +01:00
}
2010-10-24 16:14:41 +02:00
static struct nouveau_tile_reg *
nv10_mem_get_tile_region ( struct drm_device * dev , int i )
2009-12-11 16:51:09 +01:00
{
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
2010-10-24 16:14:41 +02:00
struct nouveau_tile_reg * tile = & dev_priv - > tile . reg [ i ] ;
2009-12-11 16:51:09 +01:00
2010-10-24 16:14:41 +02:00
spin_lock ( & dev_priv - > tile . lock ) ;
2009-12-11 16:51:09 +01:00
2010-10-24 16:14:41 +02:00
if ( ! tile - > used & &
2012-04-30 13:30:00 +10:00
( ! tile - > fence | | nouveau_fence_done ( tile - > fence ) ) )
2010-10-24 16:14:41 +02:00
tile - > used = true ;
else
tile = NULL ;
2009-12-11 16:51:09 +01:00
2010-10-24 16:14:41 +02:00
spin_unlock ( & dev_priv - > tile . lock ) ;
return tile ;
}
2009-12-11 16:51:09 +01:00
2010-10-24 16:14:41 +02:00
void
nv10_mem_put_tile_region ( struct drm_device * dev , struct nouveau_tile_reg * tile ,
struct nouveau_fence * fence )
{
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
2009-12-11 16:51:09 +01:00
2010-10-24 16:14:41 +02:00
if ( tile ) {
spin_lock ( & dev_priv - > tile . lock ) ;
if ( fence ) {
/* Mark it as pending. */
tile - > fence = fence ;
nouveau_fence_ref ( fence ) ;
2009-12-11 16:51:09 +01:00
}
2010-10-24 16:14:41 +02:00
tile - > used = false ;
spin_unlock ( & dev_priv - > tile . lock ) ;
}
2009-12-11 16:51:09 +01:00
}
2010-10-24 16:14:41 +02:00
struct nouveau_tile_reg *
nv10_mem_set_tiling ( struct drm_device * dev , uint32_t addr , uint32_t size ,
uint32_t pitch , uint32_t flags )
2009-12-11 16:51:09 +01:00
{
2010-10-24 16:14:41 +02:00
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
struct nouveau_fb_engine * pfb = & dev_priv - > engine . fb ;
struct nouveau_tile_reg * tile , * found = NULL ;
int i ;
for ( i = 0 ; i < pfb - > num_tiles ; i + + ) {
tile = nv10_mem_get_tile_region ( dev , i ) ;
if ( pitch & & ! found ) {
found = tile ;
continue ;
} else if ( tile & & tile - > pitch ) {
/* Kill an unused tile region. */
nv10_mem_update_tile_region ( dev , tile , 0 , 0 , 0 , 0 ) ;
}
nv10_mem_put_tile_region ( dev , tile , NULL ) ;
2009-12-11 16:51:09 +01:00
}
2010-10-24 16:14:41 +02:00
if ( found )
nv10_mem_update_tile_region ( dev , found , addr , size ,
pitch , flags ) ;
return found ;
2009-12-11 16:51:09 +01:00
}
2009-12-11 19:24:15 +10:00
/*
* Cleanup everything
*/
2010-06-01 15:32:24 +10:00
void
2010-09-01 15:24:34 +10:00
nouveau_mem_vram_fini ( struct drm_device * dev )
2009-12-11 19:24:15 +10:00
{
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
ttm_bo_device_release ( & dev_priv - > ttm . bdev ) ;
nouveau_ttm_global_release ( dev_priv ) ;
2010-09-01 15:24:34 +10:00
if ( dev_priv - > fb_mtrr > = 0 ) {
drm_mtrr_del ( dev_priv - > fb_mtrr ,
pci_resource_start ( dev - > pdev , 1 ) ,
pci_resource_len ( dev - > pdev , 1 ) , DRM_MTRR_WC ) ;
dev_priv - > fb_mtrr = - 1 ;
}
}
void
nouveau_mem_gart_fini ( struct drm_device * dev )
{
nouveau_sgdma_takedown ( dev ) ;
2010-06-01 15:56:22 +10:00
if ( drm_core_has_AGP ( dev ) & & dev - > agp ) {
2009-12-11 19:24:15 +10:00
struct drm_agp_mem * entry , * tempe ;
/* Remove AGP resources, but leave dev->agp
intact until drv_cleanup is called . */
list_for_each_entry_safe ( entry , tempe , & dev - > agp - > memory , head ) {
if ( entry - > bound )
drm_unbind_agp ( entry - > memory ) ;
drm_free_agp ( entry - > memory , entry - > pages ) ;
kfree ( entry ) ;
}
INIT_LIST_HEAD ( & dev - > agp - > memory ) ;
if ( dev - > agp - > acquired )
drm_agp_release ( dev ) ;
dev - > agp - > acquired = 0 ;
dev - > agp - > enabled = 0 ;
}
}
2010-12-06 15:28:54 +10:00
bool
nouveau_mem_flags_valid ( struct drm_device * dev , u32 tile_flags )
{
if ( ! ( tile_flags & NOUVEAU_GEM_TILE_LAYOUT_MASK ) )
return true ;
return false ;
}
2010-09-08 02:23:20 +02:00
# if __OS_HAS_AGP
static unsigned long
get_agp_mode ( struct drm_device * dev , unsigned long mode )
{
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
/*
* FW seems to be broken on nv18 , it makes the card lock up
* randomly .
*/
if ( dev_priv - > chipset = = 0x18 )
mode & = ~ PCI_AGP_COMMAND_FW ;
2010-09-08 02:28:23 +02:00
/*
* AGP mode set in the command line .
*/
if ( nouveau_agpmode > 0 ) {
bool agpv3 = mode & 0x8 ;
int rate = agpv3 ? nouveau_agpmode / 4 : nouveau_agpmode ;
mode = ( mode & ~ 0x7 ) | ( rate & 0x7 ) ;
}
2010-09-08 02:23:20 +02:00
return mode ;
}
# endif
2010-07-23 20:29:13 +02:00
int
nouveau_mem_reset_agp ( struct drm_device * dev )
2009-12-11 19:24:15 +10:00
{
2010-07-23 20:29:13 +02:00
# if __OS_HAS_AGP
uint32_t saved_pci_nv_1 , pmc_enable ;
int ret ;
/* First of all, disable fast writes, otherwise if it's
* already enabled in the AGP bridge and we disable the card ' s
* AGP controller we might be locking ourselves out of it . */
2010-08-26 16:13:49 +02:00
if ( ( nv_rd32 ( dev , NV04_PBUS_PCI_NV_19 ) |
dev - > agp - > mode ) & PCI_AGP_COMMAND_FW ) {
2010-07-23 20:29:13 +02:00
struct drm_agp_info info ;
struct drm_agp_mode mode ;
ret = drm_agp_info ( dev , & info ) ;
if ( ret )
return ret ;
2010-09-08 02:23:20 +02:00
mode . mode = get_agp_mode ( dev , info . mode ) & ~ PCI_AGP_COMMAND_FW ;
2010-07-23 20:29:13 +02:00
ret = drm_agp_enable ( dev , mode ) ;
if ( ret )
return ret ;
}
2009-12-11 19:24:15 +10:00
saved_pci_nv_1 = nv_rd32 ( dev , NV04_PBUS_PCI_NV_1 ) ;
/* clear busmaster bit */
nv_wr32 ( dev , NV04_PBUS_PCI_NV_1 , saved_pci_nv_1 & ~ 0x4 ) ;
2010-07-23 20:29:13 +02:00
/* disable AGP */
nv_wr32 ( dev , NV04_PBUS_PCI_NV_19 , 0 ) ;
2009-12-11 19:24:15 +10:00
/* power cycle pgraph, if enabled */
pmc_enable = nv_rd32 ( dev , NV03_PMC_ENABLE ) ;
if ( pmc_enable & NV_PMC_ENABLE_PGRAPH ) {
nv_wr32 ( dev , NV03_PMC_ENABLE ,
pmc_enable & ~ NV_PMC_ENABLE_PGRAPH ) ;
nv_wr32 ( dev , NV03_PMC_ENABLE , nv_rd32 ( dev , NV03_PMC_ENABLE ) |
NV_PMC_ENABLE_PGRAPH ) ;
}
/* and restore (gives effect of resetting AGP) */
nv_wr32 ( dev , NV04_PBUS_PCI_NV_1 , saved_pci_nv_1 ) ;
2009-12-15 10:38:32 +10:00
# endif
2009-12-11 19:24:15 +10:00
2010-07-23 20:29:13 +02:00
return 0 ;
}
2009-12-11 19:24:15 +10:00
int
nouveau_mem_init_agp ( struct drm_device * dev )
{
2009-12-15 10:38:32 +10:00
# if __OS_HAS_AGP
2009-12-11 19:24:15 +10:00
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
struct drm_agp_info info ;
struct drm_agp_mode mode ;
int ret ;
if ( ! dev - > agp - > acquired ) {
ret = drm_agp_acquire ( dev ) ;
if ( ret ) {
NV_ERROR ( dev , " Unable to acquire AGP: %d \n " , ret ) ;
return ret ;
}
}
2010-07-30 13:57:54 +02:00
nouveau_mem_reset_agp ( dev ) ;
2009-12-11 19:24:15 +10:00
ret = drm_agp_info ( dev , & info ) ;
if ( ret ) {
NV_ERROR ( dev , " Unable to get AGP info: %d \n " , ret ) ;
return ret ;
}
/* see agp.h for the AGPSTAT_* modes available */
2010-09-08 02:23:20 +02:00
mode . mode = get_agp_mode ( dev , info . mode ) ;
2009-12-11 19:24:15 +10:00
ret = drm_agp_enable ( dev , mode ) ;
if ( ret ) {
NV_ERROR ( dev , " Unable to enable AGP: %d \n " , ret ) ;
return ret ;
}
dev_priv - > gart_info . type = NOUVEAU_GART_AGP ;
dev_priv - > gart_info . aper_base = info . aperture_base ;
dev_priv - > gart_info . aper_size = info . aperture_size ;
2009-12-15 10:38:32 +10:00
# endif
2009-12-11 19:24:15 +10:00
return 0 ;
}
2011-12-11 00:30:05 +10:00
static const struct vram_types {
int value ;
const char * name ;
} vram_type_map [ ] = {
{ NV_MEM_TYPE_STOLEN , " stolen system memory " } ,
{ NV_MEM_TYPE_SGRAM , " SGRAM " } ,
{ NV_MEM_TYPE_SDRAM , " SDRAM " } ,
{ NV_MEM_TYPE_DDR1 , " DDR1 " } ,
{ NV_MEM_TYPE_DDR2 , " DDR2 " } ,
{ NV_MEM_TYPE_DDR3 , " DDR3 " } ,
{ NV_MEM_TYPE_GDDR2 , " GDDR2 " } ,
{ NV_MEM_TYPE_GDDR3 , " GDDR3 " } ,
{ NV_MEM_TYPE_GDDR4 , " GDDR4 " } ,
{ NV_MEM_TYPE_GDDR5 , " GDDR5 " } ,
{ NV_MEM_TYPE_UNKNOWN , " unknown type " }
} ;
2009-12-11 19:24:15 +10:00
int
2010-09-01 15:24:34 +10:00
nouveau_mem_vram_init ( struct drm_device * dev )
2009-12-11 19:24:15 +10:00
{
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
struct ttm_bo_device * bdev = & dev_priv - > ttm . bdev ;
2011-12-11 00:30:05 +10:00
const struct vram_types * vram_type ;
2010-09-01 15:24:34 +10:00
int ret , dma_bits ;
2009-12-11 19:24:15 +10:00
2011-01-11 15:50:26 +10:00
dma_bits = 32 ;
if ( dev_priv - > card_type > = NV_50 ) {
if ( pci_dma_supported ( dev - > pdev , DMA_BIT_MASK ( 40 ) ) )
dma_bits = 40 ;
} else
2011-06-27 16:07:50 +00:00
if ( 0 & & pci_is_pcie ( dev - > pdev ) & &
2011-04-08 10:07:34 +10:00
dev_priv - > chipset > 0x40 & &
2011-01-11 15:50:26 +10:00
dev_priv - > chipset ! = 0x45 ) {
if ( pci_dma_supported ( dev - > pdev , DMA_BIT_MASK ( 39 ) ) )
dma_bits = 39 ;
}
2009-12-11 19:24:15 +10:00
ret = pci_set_dma_mask ( dev - > pdev , DMA_BIT_MASK ( dma_bits ) ) ;
2010-09-01 15:24:34 +10:00
if ( ret )
2009-12-11 19:24:15 +10:00
return ret ;
2011-10-17 17:14:26 -04:00
ret = pci_set_consistent_dma_mask ( dev - > pdev , DMA_BIT_MASK ( dma_bits ) ) ;
if ( ret ) {
/* Reset to default value. */
pci_set_consistent_dma_mask ( dev - > pdev , DMA_BIT_MASK ( 32 ) ) ;
}
2010-09-01 15:24:34 +10:00
2009-12-11 19:24:15 +10:00
ret = nouveau_ttm_global_init ( dev_priv ) ;
if ( ret )
return ret ;
ret = ttm_bo_device_init ( & dev_priv - > ttm . bdev ,
dev_priv - > ttm . bo_global_ref . ref . object ,
& nouveau_bo_driver , DRM_FILE_PAGE_OFFSET ,
dma_bits < = 32 ? true : false ) ;
if ( ret ) {
NV_ERROR ( dev , " Error initialising bo driver: %d \n " , ret ) ;
return ret ;
}
2011-12-11 00:30:05 +10:00
vram_type = vram_type_map ;
while ( vram_type - > value ! = NV_MEM_TYPE_UNKNOWN ) {
if ( nouveau_vram_type ) {
if ( ! strcasecmp ( nouveau_vram_type , vram_type - > name ) )
break ;
dev_priv - > vram_type = vram_type - > value ;
} else {
if ( vram_type - > value = = dev_priv - > vram_type )
break ;
}
vram_type + + ;
}
NV_INFO ( dev , " Detected %dMiB VRAM (%s) \n " ,
( int ) ( dev_priv - > vram_size > > 20 ) , vram_type - > name ) ;
2010-12-06 15:28:54 +10:00
if ( dev_priv - > vram_sys_base ) {
NV_INFO ( dev , " Stolen system memory at: 0x%010llx \n " ,
dev_priv - > vram_sys_base ) ;
}
2010-08-25 15:26:04 +10:00
dev_priv - > fb_available_size = dev_priv - > vram_size ;
dev_priv - > fb_mappable_pages = dev_priv - > fb_available_size ;
if ( dev_priv - > fb_mappable_pages > pci_resource_len ( dev - > pdev , 1 ) )
dev_priv - > fb_mappable_pages = pci_resource_len ( dev - > pdev , 1 ) ;
dev_priv - > fb_mappable_pages > > = PAGE_SHIFT ;
2009-12-11 19:24:15 +10:00
dev_priv - > fb_available_size - = dev_priv - > ramin_rsvd_vram ;
dev_priv - > fb_aper_free = dev_priv - > fb_available_size ;
/* mappable vram */
ret = ttm_bo_init_mm ( bdev , TTM_PL_VRAM ,
dev_priv - > fb_available_size > > PAGE_SHIFT ) ;
if ( ret ) {
NV_ERROR ( dev , " Failed VRAM mm init: %d \n " , ret ) ;
return ret ;
}
2011-02-16 08:41:56 +10:00
if ( dev_priv - > card_type < NV_50 ) {
2011-06-07 14:21:29 +10:00
ret = nouveau_bo_new ( dev , 256 * 1024 , 0 , TTM_PL_FLAG_VRAM ,
2012-04-02 11:53:06 +01:00
0 , 0 , NULL , & dev_priv - > vga_ram ) ;
2011-02-16 08:41:56 +10:00
if ( ret = = 0 )
ret = nouveau_bo_pin ( dev_priv - > vga_ram ,
TTM_PL_FLAG_VRAM ) ;
if ( ret ) {
NV_WARN ( dev , " failed to reserve VGA memory \n " ) ;
nouveau_bo_ref ( NULL , & dev_priv - > vga_ram ) ;
}
2010-01-15 09:24:20 +10:00
}
2010-09-01 15:24:34 +10:00
dev_priv - > fb_mtrr = drm_mtrr_add ( pci_resource_start ( dev - > pdev , 1 ) ,
pci_resource_len ( dev - > pdev , 1 ) ,
DRM_MTRR_WC ) ;
return 0 ;
}
int
nouveau_mem_gart_init ( struct drm_device * dev )
{
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
struct ttm_bo_device * bdev = & dev_priv - > ttm . bdev ;
int ret ;
dev_priv - > gart_info . type = NOUVEAU_GART_NONE ;
2009-12-11 19:24:15 +10:00
# if !defined(__powerpc__) && !defined(__ia64__)
2010-12-15 03:16:38 +10:00
if ( drm_pci_device_is_agp ( dev ) & & dev - > agp & & nouveau_agpmode ) {
2009-12-11 19:24:15 +10:00
ret = nouveau_mem_init_agp ( dev ) ;
if ( ret )
NV_ERROR ( dev , " Error initialising AGP: %d \n " , ret ) ;
}
# endif
if ( dev_priv - > gart_info . type = = NOUVEAU_GART_NONE ) {
ret = nouveau_sgdma_init ( dev ) ;
if ( ret ) {
NV_ERROR ( dev , " Error initialising PCI(E): %d \n " , ret ) ;
return ret ;
}
}
NV_INFO ( dev , " %d MiB GART (aperture) \n " ,
( int ) ( dev_priv - > gart_info . aper_size > > 20 ) ) ;
dev_priv - > gart_info . aper_free = dev_priv - > gart_info . aper_size ;
ret = ttm_bo_init_mm ( bdev , TTM_PL_TT ,
dev_priv - > gart_info . aper_size > > PAGE_SHIFT ) ;
if ( ret ) {
NV_ERROR ( dev , " Failed TT mm init: %d \n " , ret ) ;
return ret ;
}
return 0 ;
}
2012-01-17 21:10:58 +10:00
static int
nv40_mem_timing_calc ( struct drm_device * dev , u32 freq ,
struct nouveau_pm_tbl_entry * e , u8 len ,
struct nouveau_pm_memtiming * boot ,
struct nouveau_pm_memtiming * t )
2011-12-17 12:24:59 +01:00
{
2012-01-09 15:23:07 +10:00
t - > reg [ 0 ] = ( e - > tRP < < 24 | e - > tRAS < < 16 | e - > tRFC < < 8 | e - > tRC ) ;
2011-07-09 21:18:11 +02:00
/* XXX: I don't trust the -1's and +1's... they must come
* from somewhere ! */
2012-01-09 15:23:07 +10:00
t - > reg [ 1 ] = ( e - > tWR + 2 + ( t - > tCWL - 1 ) ) < < 24 |
1 < < 16 |
( e - > tWTR + 2 + ( t - > tCWL - 1 ) ) < < 8 |
( e - > tCL + 2 - ( t - > tCWL - 1 ) ) ;
t - > reg [ 2 ] = 0x20200000 |
( ( t - > tCWL - 1 ) < < 24 |
e - > tRRD < < 16 |
e - > tRCDWR < < 8 |
e - > tRCDRD ) ;
NV_DEBUG ( dev , " Entry %d: 220: %08x %08x %08x \n " , t - > id ,
t - > reg [ 0 ] , t - > reg [ 1 ] , t - > reg [ 2 ] ) ;
2012-01-17 21:10:58 +10:00
return 0 ;
2011-07-09 21:18:11 +02:00
}
2012-01-17 21:10:58 +10:00
static int
nv50_mem_timing_calc ( struct drm_device * dev , u32 freq ,
struct nouveau_pm_tbl_entry * e , u8 len ,
struct nouveau_pm_memtiming * boot ,
struct nouveau_pm_memtiming * t )
2011-12-17 12:24:59 +01:00
{
2011-07-09 21:18:11 +02:00
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
2012-01-17 21:10:58 +10:00
struct bit_entry P ;
2012-01-09 15:23:07 +10:00
uint8_t unk18 = 1 , unk20 = 0 , unk21 = 0 , tmp7_3 ;
2011-07-09 21:18:11 +02:00
2012-01-17 21:10:58 +10:00
if ( bit_table ( dev , ' P ' , & P ) )
return - EINVAL ;
switch ( min ( len , ( u8 ) 22 ) ) {
2011-07-09 21:18:11 +02:00
case 22 :
unk21 = e - > tUNK_21 ;
case 21 :
unk20 = e - > tUNK_20 ;
case 20 :
2011-11-25 15:52:22 +01:00
if ( e - > tCWL > 0 )
2012-01-09 15:23:07 +10:00
t - > tCWL = e - > tCWL ;
2011-07-09 21:18:11 +02:00
case 19 :
unk18 = e - > tUNK_18 ;
break ;
}
2012-01-09 15:23:07 +10:00
t - > reg [ 0 ] = ( e - > tRP < < 24 | e - > tRAS < < 16 | e - > tRFC < < 8 | e - > tRC ) ;
2011-07-09 21:18:11 +02:00
2012-01-09 15:23:07 +10:00
t - > reg [ 1 ] = ( e - > tWR + 2 + ( t - > tCWL - 1 ) ) < < 24 |
2011-11-25 15:52:22 +01:00
max ( unk18 , ( u8 ) 1 ) < < 16 |
2012-01-09 15:23:07 +10:00
( e - > tWTR + 2 + ( t - > tCWL - 1 ) ) < < 8 ;
2011-11-25 15:52:22 +01:00
2012-01-09 15:23:07 +10:00
t - > reg [ 2 ] = ( ( t - > tCWL - 1 ) < < 24 |
e - > tRRD < < 16 |
e - > tRCDWR < < 8 |
e - > tRCDRD ) ;
2011-12-17 12:24:59 +01:00
2012-01-09 15:23:07 +10:00
t - > reg [ 4 ] = e - > tUNK_13 < < 8 | e - > tUNK_13 ;
2011-07-09 21:18:11 +02:00
2012-01-09 15:23:07 +10:00
t - > reg [ 5 ] = ( e - > tRFC < < 24 | max ( e - > tRCDRD , e - > tRCDWR ) < < 16 | e - > tRP ) ;
2011-11-25 15:52:22 +01:00
2012-01-09 15:23:07 +10:00
t - > reg [ 8 ] = boot - > reg [ 8 ] & 0xffffff00 ;
2011-07-09 21:18:11 +02:00
2012-01-17 21:10:58 +10:00
if ( P . version = = 1 ) {
2012-01-09 15:23:07 +10:00
t - > reg [ 1 ] | = ( e - > tCL + 2 - ( t - > tCWL - 1 ) ) ;
2011-12-17 12:24:59 +01:00
2012-01-09 15:23:07 +10:00
t - > reg [ 3 ] = ( 0x14 + e - > tCL ) < < 24 |
0x16 < < 16 |
( e - > tCL - 1 ) < < 8 |
( e - > tCL - 1 ) ;
2011-12-17 12:24:59 +01:00
2012-01-09 15:23:07 +10:00
t - > reg [ 4 ] | = boot - > reg [ 4 ] & 0xffff0000 ;
2011-12-17 12:24:59 +01:00
2012-01-09 15:23:07 +10:00
t - > reg [ 6 ] = ( 0x33 - t - > tCWL ) < < 16 |
t - > tCWL < < 8 |
( 0x2e + e - > tCL - t - > tCWL ) ;
2011-12-17 12:24:59 +01:00
2012-01-09 15:23:07 +10:00
t - > reg [ 7 ] = 0x4000202 | ( e - > tCL - 1 ) < < 16 ;
2011-11-25 15:52:22 +01:00
/* XXX: P.version == 1 only has DDR2 and GDDR3? */
if ( dev_priv - > vram_type = = NV_MEM_TYPE_DDR2 ) {
2012-01-09 15:23:07 +10:00
t - > reg [ 5 ] | = ( e - > tCL + 3 ) < < 8 ;
t - > reg [ 6 ] | = ( t - > tCWL - 2 ) < < 8 ;
t - > reg [ 8 ] | = ( e - > tCL - 4 ) ;
2011-11-25 15:52:22 +01:00
} else {
2012-01-09 15:23:07 +10:00
t - > reg [ 5 ] | = ( e - > tCL + 2 ) < < 8 ;
t - > reg [ 6 ] | = t - > tCWL < < 8 ;
t - > reg [ 8 ] | = ( e - > tCL - 2 ) ;
2011-11-25 15:52:22 +01:00
}
2011-07-09 21:18:11 +02:00
} else {
2012-01-09 15:23:07 +10:00
t - > reg [ 1 ] | = ( 5 + e - > tCL - ( t - > tCWL ) ) ;
2011-11-25 15:52:22 +01:00
/* XXX: 0xb? 0x30? */
2012-01-09 15:23:07 +10:00
t - > reg [ 3 ] = ( 0x30 + e - > tCL ) < < 24 |
( boot - > reg [ 3 ] & 0x00ff0000 ) |
( 0xb + e - > tCL ) < < 8 |
( e - > tCL - 1 ) ;
2011-11-25 15:52:22 +01:00
2012-01-09 15:23:07 +10:00
t - > reg [ 4 ] | = ( unk20 < < 24 | unk21 < < 16 ) ;
2011-11-25 15:52:22 +01:00
2011-07-09 21:18:11 +02:00
/* XXX: +6? */
2012-01-09 15:23:07 +10:00
t - > reg [ 5 ] | = ( t - > tCWL + 6 ) < < 8 ;
2011-07-09 21:18:11 +02:00
2012-01-09 15:23:07 +10:00
t - > reg [ 6 ] = ( 0x5a + e - > tCL ) < < 16 |
( 6 - e - > tCL + t - > tCWL ) < < 8 |
( 0x50 + e - > tCL - t - > tCWL ) ;
2011-11-25 15:52:22 +01:00
2012-01-09 15:23:07 +10:00
tmp7_3 = ( boot - > reg [ 7 ] & 0xff000000 ) > > 24 ;
t - > reg [ 7 ] = ( tmp7_3 < < 24 ) |
( ( tmp7_3 - 6 + e - > tCL ) < < 16 ) |
0x202 ;
2011-07-09 21:18:11 +02:00
}
2012-01-09 15:23:07 +10:00
NV_DEBUG ( dev , " Entry %d: 220: %08x %08x %08x %08x \n " , t - > id ,
t - > reg [ 0 ] , t - > reg [ 1 ] , t - > reg [ 2 ] , t - > reg [ 3 ] ) ;
2011-07-09 21:18:11 +02:00
NV_DEBUG ( dev , " 230: %08x %08x %08x %08x \n " ,
2012-01-09 15:23:07 +10:00
t - > reg [ 4 ] , t - > reg [ 5 ] , t - > reg [ 6 ] , t - > reg [ 7 ] ) ;
NV_DEBUG ( dev , " 240: %08x \n " , t - > reg [ 8 ] ) ;
2012-01-17 21:10:58 +10:00
return 0 ;
2011-07-09 21:18:11 +02:00
}
2012-01-17 21:10:58 +10:00
static int
nvc0_mem_timing_calc ( struct drm_device * dev , u32 freq ,
struct nouveau_pm_tbl_entry * e , u8 len ,
struct nouveau_pm_memtiming * boot ,
struct nouveau_pm_memtiming * t )
2011-12-17 12:24:59 +01:00
{
2012-01-09 15:23:07 +10:00
if ( e - > tCWL > 0 )
t - > tCWL = e - > tCWL ;
2011-11-25 15:52:22 +01:00
2012-01-09 15:23:07 +10:00
t - > reg [ 0 ] = ( e - > tRP < < 24 | ( e - > tRAS & 0x7f ) < < 17 |
e - > tRFC < < 8 | e - > tRC ) ;
2011-12-17 12:24:59 +01:00
2012-01-09 15:23:07 +10:00
t - > reg [ 1 ] = ( boot - > reg [ 1 ] & 0xff000000 ) |
( e - > tRCDWR & 0x0f ) < < 20 |
( e - > tRCDRD & 0x0f ) < < 14 |
2012-02-07 00:29:06 +01:00
( t - > tCWL < < 7 ) |
2012-01-09 15:23:07 +10:00
( e - > tCL & 0x0f ) ;
2011-12-17 12:24:59 +01:00
2012-01-09 15:23:07 +10:00
t - > reg [ 2 ] = ( boot - > reg [ 2 ] & 0xff0000ff ) |
e - > tWR < < 16 | e - > tWTR < < 8 ;
2011-12-17 12:24:59 +01:00
2012-02-07 00:29:06 +01:00
t - > reg [ 3 ] = ( e - > tUNK_20 & 0x1f ) < < 9 |
2012-01-09 15:23:07 +10:00
( e - > tUNK_21 & 0xf ) < < 5 |
( e - > tUNK_13 & 0x1f ) ;
2011-12-17 12:24:59 +01:00
2012-01-09 15:23:07 +10:00
t - > reg [ 4 ] = ( boot - > reg [ 4 ] & 0xfff00fff ) |
( e - > tRRD & 0x1f ) < < 15 ;
2011-12-17 12:24:59 +01:00
2012-01-09 15:23:07 +10:00
NV_DEBUG ( dev , " Entry %d: 290: %08x %08x %08x %08x \n " , t - > id ,
t - > reg [ 0 ] , t - > reg [ 1 ] , t - > reg [ 2 ] , t - > reg [ 3 ] ) ;
NV_DEBUG ( dev , " 2a0: %08x \n " , t - > reg [ 4 ] ) ;
2012-01-17 21:10:58 +10:00
return 0 ;
2011-11-25 15:52:22 +01:00
}
2012-01-09 15:23:07 +10:00
/**
* MR generation methods
*/
2012-01-17 21:10:58 +10:00
static int
nouveau_mem_ddr2_mr ( struct drm_device * dev , u32 freq ,
struct nouveau_pm_tbl_entry * e , u8 len ,
struct nouveau_pm_memtiming * boot ,
struct nouveau_pm_memtiming * t )
2012-01-09 15:23:07 +10:00
{
t - > drive_strength = 0 ;
2012-01-17 21:10:58 +10:00
if ( len < 15 ) {
2012-01-09 15:23:07 +10:00
t - > odt = boot - > odt ;
} else {
t - > odt = e - > RAM_FT1 & 0x07 ;
}
if ( e - > tCL > = NV_MEM_CL_DDR2_MAX ) {
NV_WARN ( dev , " (%u) Invalid tCL: %u " , t - > id , e - > tCL ) ;
2012-01-17 21:10:58 +10:00
return - ERANGE ;
2012-01-09 15:23:07 +10:00
}
if ( e - > tWR > = NV_MEM_WR_DDR2_MAX ) {
NV_WARN ( dev , " (%u) Invalid tWR: %u " , t - > id , e - > tWR ) ;
2012-01-17 21:10:58 +10:00
return - ERANGE ;
2012-01-09 15:23:07 +10:00
}
if ( t - > odt > 3 ) {
NV_WARN ( dev , " (%u) Invalid odt value, assuming disabled: %x " ,
t - > id , t - > odt ) ;
t - > odt = 0 ;
}
t - > mr [ 0 ] = ( boot - > mr [ 0 ] & 0x100f ) |
( e - > tCL ) < < 4 |
( e - > tWR - 1 ) < < 9 ;
t - > mr [ 1 ] = ( boot - > mr [ 1 ] & 0x101fbb ) |
( t - > odt & 0x1 ) < < 2 |
( t - > odt & 0x2 ) < < 5 ;
NV_DEBUG ( dev , " (%u) MR: %08x " , t - > id , t - > mr [ 0 ] ) ;
2012-01-17 21:10:58 +10:00
return 0 ;
2012-01-09 15:23:07 +10:00
}
uint8_t nv_mem_wr_lut_ddr3 [ NV_MEM_WR_DDR3_MAX ] = {
0 , 0 , 0 , 0 , 0 , 1 , 2 , 3 , 4 , 5 , 5 , 6 , 6 , 7 , 7 , 0 , 0 } ;
2012-01-17 21:10:58 +10:00
static int
nouveau_mem_ddr3_mr ( struct drm_device * dev , u32 freq ,
struct nouveau_pm_tbl_entry * e , u8 len ,
struct nouveau_pm_memtiming * boot ,
struct nouveau_pm_memtiming * t )
2012-01-09 15:23:07 +10:00
{
u8 cl = e - > tCL - 4 ;
t - > drive_strength = 0 ;
2012-01-17 21:10:58 +10:00
if ( len < 15 ) {
2012-01-09 15:23:07 +10:00
t - > odt = boot - > odt ;
} else {
t - > odt = e - > RAM_FT1 & 0x07 ;
}
if ( e - > tCL > = NV_MEM_CL_DDR3_MAX | | e - > tCL < 4 ) {
NV_WARN ( dev , " (%u) Invalid tCL: %u " , t - > id , e - > tCL ) ;
2012-01-17 21:10:58 +10:00
return - ERANGE ;
2012-01-09 15:23:07 +10:00
}
if ( e - > tWR > = NV_MEM_WR_DDR3_MAX | | e - > tWR < 4 ) {
NV_WARN ( dev , " (%u) Invalid tWR: %u " , t - > id , e - > tWR ) ;
2012-01-17 21:10:58 +10:00
return - ERANGE ;
2012-01-09 15:23:07 +10:00
}
if ( e - > tCWL < 5 ) {
NV_WARN ( dev , " (%u) Invalid tCWL: %u " , t - > id , e - > tCWL ) ;
2012-01-17 21:10:58 +10:00
return - ERANGE ;
2012-01-09 15:23:07 +10:00
}
t - > mr [ 0 ] = ( boot - > mr [ 0 ] & 0x180b ) |
/* CAS */
( cl & 0x7 ) < < 4 |
( cl & 0x8 ) > > 1 |
( nv_mem_wr_lut_ddr3 [ e - > tWR ] ) < < 9 ;
t - > mr [ 1 ] = ( boot - > mr [ 1 ] & 0x101dbb ) |
( t - > odt & 0x1 ) < < 2 |
( t - > odt & 0x2 ) < < 5 |
( t - > odt & 0x4 ) < < 7 ;
t - > mr [ 2 ] = ( boot - > mr [ 2 ] & 0x20ffb7 ) | ( e - > tCWL - 5 ) < < 3 ;
NV_DEBUG ( dev , " (%u) MR: %08x %08x " , t - > id , t - > mr [ 0 ] , t - > mr [ 2 ] ) ;
2012-01-17 21:10:58 +10:00
return 0 ;
2012-01-09 15:23:07 +10:00
}
uint8_t nv_mem_cl_lut_gddr3 [ NV_MEM_CL_GDDR3_MAX ] = {
0 , 0 , 0 , 0 , 4 , 5 , 6 , 7 , 0 , 1 , 2 , 3 , 8 , 9 , 10 , 11 } ;
uint8_t nv_mem_wr_lut_gddr3 [ NV_MEM_WR_GDDR3_MAX ] = {
0 , 0 , 0 , 0 , 0 , 2 , 3 , 8 , 9 , 10 , 11 , 0 , 0 , 1 , 1 , 0 , 3 } ;
2012-01-17 21:10:58 +10:00
static int
nouveau_mem_gddr3_mr ( struct drm_device * dev , u32 freq ,
struct nouveau_pm_tbl_entry * e , u8 len ,
struct nouveau_pm_memtiming * boot ,
struct nouveau_pm_memtiming * t )
2011-11-25 15:52:22 +01:00
{
2012-01-17 21:10:58 +10:00
if ( len < 15 ) {
2012-01-09 15:23:07 +10:00
t - > drive_strength = boot - > drive_strength ;
t - > odt = boot - > odt ;
} else {
t - > drive_strength = ( e - > RAM_FT1 & 0x30 ) > > 4 ;
t - > odt = e - > RAM_FT1 & 0x07 ;
2011-11-25 15:52:22 +01:00
}
2012-01-09 15:23:07 +10:00
if ( e - > tCL > = NV_MEM_CL_GDDR3_MAX ) {
NV_WARN ( dev , " (%u) Invalid tCL: %u " , t - > id , e - > tCL ) ;
2012-01-17 21:10:58 +10:00
return - ERANGE ;
2012-01-09 15:23:07 +10:00
}
if ( e - > tWR > = NV_MEM_WR_GDDR3_MAX ) {
NV_WARN ( dev , " (%u) Invalid tWR: %u " , t - > id , e - > tWR ) ;
2012-01-17 21:10:58 +10:00
return - ERANGE ;
2012-01-09 15:23:07 +10:00
}
if ( t - > odt > 3 ) {
NV_WARN ( dev , " (%u) Invalid odt value, assuming autocal: %x " ,
t - > id , t - > odt ) ;
t - > odt = 0 ;
}
t - > mr [ 0 ] = ( boot - > mr [ 0 ] & 0xe0b ) |
/* CAS */
( ( nv_mem_cl_lut_gddr3 [ e - > tCL ] & 0x7 ) < < 4 ) |
( ( nv_mem_cl_lut_gddr3 [ e - > tCL ] & 0x8 ) > > 2 ) ;
t - > mr [ 1 ] = ( boot - > mr [ 1 ] & 0x100f40 ) | t - > drive_strength |
( t - > odt < < 2 ) |
( nv_mem_wr_lut_gddr3 [ e - > tWR ] & 0xf ) < < 4 ;
2012-01-24 10:24:05 +10:00
t - > mr [ 2 ] = boot - > mr [ 2 ] ;
2012-01-09 15:23:07 +10:00
2012-01-24 10:24:05 +10:00
NV_DEBUG ( dev , " (%u) MR: %08x %08x %08x " , t - > id ,
t - > mr [ 0 ] , t - > mr [ 1 ] , t - > mr [ 2 ] ) ;
2012-01-17 21:10:58 +10:00
return 0 ;
2012-01-09 15:23:07 +10:00
}
2012-01-17 21:10:58 +10:00
static int
nouveau_mem_gddr5_mr ( struct drm_device * dev , u32 freq ,
struct nouveau_pm_tbl_entry * e , u8 len ,
struct nouveau_pm_memtiming * boot ,
struct nouveau_pm_memtiming * t )
2012-01-09 15:23:07 +10:00
{
2012-01-17 21:10:58 +10:00
if ( len < 15 ) {
2012-01-09 15:23:07 +10:00
t - > drive_strength = boot - > drive_strength ;
t - > odt = boot - > odt ;
} else {
t - > drive_strength = ( e - > RAM_FT1 & 0x30 ) > > 4 ;
t - > odt = e - > RAM_FT1 & 0x03 ;
}
if ( e - > tCL > = NV_MEM_CL_GDDR5_MAX ) {
NV_WARN ( dev , " (%u) Invalid tCL: %u " , t - > id , e - > tCL ) ;
2012-01-17 21:10:58 +10:00
return - ERANGE ;
2012-01-09 15:23:07 +10:00
}
if ( e - > tWR > = NV_MEM_WR_GDDR5_MAX ) {
NV_WARN ( dev , " (%u) Invalid tWR: %u " , t - > id , e - > tWR ) ;
2012-01-17 21:10:58 +10:00
return - ERANGE ;
2012-01-09 15:23:07 +10:00
}
if ( t - > odt > 3 ) {
NV_WARN ( dev , " (%u) Invalid odt value, assuming autocal: %x " ,
t - > id , t - > odt ) ;
t - > odt = 0 ;
}
t - > mr [ 0 ] = ( boot - > mr [ 0 ] & 0x007 ) |
( ( e - > tCL - 5 ) < < 3 ) |
( ( e - > tWR - 4 ) < < 8 ) ;
t - > mr [ 1 ] = ( boot - > mr [ 1 ] & 0x1007f0 ) |
t - > drive_strength |
( t - > odt < < 2 ) ;
NV_DEBUG ( dev , " (%u) MR: %08x %08x " , t - > id , t - > mr [ 0 ] , t - > mr [ 1 ] ) ;
2012-01-17 21:10:58 +10:00
return 0 ;
2012-01-09 15:23:07 +10:00
}
2012-01-18 09:02:28 +10:00
int
nouveau_mem_timing_calc ( struct drm_device * dev , u32 freq ,
struct nouveau_pm_memtiming * t )
2012-01-17 21:10:58 +10:00
{
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
struct nouveau_pm_engine * pm = & dev_priv - > engine . pm ;
2012-01-18 09:02:28 +10:00
struct nouveau_pm_memtiming * boot = & pm - > boot . timing ;
2012-01-17 21:10:58 +10:00
struct nouveau_pm_tbl_entry * e ;
2012-01-24 18:30:10 +10:00
u8 ver , len , * ptr , * ramcfg ;
2012-01-17 21:10:58 +10:00
int ret ;
ptr = nouveau_perf_timing ( dev , freq , & ver , & len ) ;
2012-01-18 09:02:28 +10:00
if ( ! ptr | | ptr [ 0 ] = = 0x00 ) {
* t = * boot ;
return 0 ;
}
2012-01-17 21:10:58 +10:00
e = ( struct nouveau_pm_tbl_entry * ) ptr ;
2012-01-18 09:02:28 +10:00
t - > tCWL = boot - > tCWL ;
2012-01-17 21:10:58 +10:00
2012-01-18 09:02:28 +10:00
switch ( dev_priv - > card_type ) {
case NV_40 :
ret = nv40_mem_timing_calc ( dev , freq , e , len , boot , t ) ;
break ;
case NV_50 :
ret = nv50_mem_timing_calc ( dev , freq , e , len , boot , t ) ;
break ;
case NV_C0 :
2012-02-06 11:42:29 +10:00
case NV_D0 :
2012-01-18 09:02:28 +10:00
ret = nvc0_mem_timing_calc ( dev , freq , e , len , boot , t ) ;
break ;
default :
ret = - ENODEV ;
break ;
}
2012-01-17 21:10:58 +10:00
2012-01-18 09:02:28 +10:00
switch ( dev_priv - > vram_type * ! ret ) {
case NV_MEM_TYPE_GDDR3 :
ret = nouveau_mem_gddr3_mr ( dev , freq , e , len , boot , t ) ;
break ;
case NV_MEM_TYPE_GDDR5 :
ret = nouveau_mem_gddr5_mr ( dev , freq , e , len , boot , t ) ;
break ;
case NV_MEM_TYPE_DDR2 :
ret = nouveau_mem_ddr2_mr ( dev , freq , e , len , boot , t ) ;
break ;
case NV_MEM_TYPE_DDR3 :
ret = nouveau_mem_ddr3_mr ( dev , freq , e , len , boot , t ) ;
break ;
default :
ret = - EINVAL ;
2012-01-24 18:30:10 +10:00
break ;
}
ramcfg = nouveau_perf_ramcfg ( dev , freq , & ver , & len ) ;
if ( ramcfg ) {
int dll_off ;
if ( ver = = 0x00 )
dll_off = ! ! ( ramcfg [ 3 ] & 0x04 ) ;
else
dll_off = ! ! ( ramcfg [ 2 ] & 0x40 ) ;
switch ( dev_priv - > vram_type ) {
case NV_MEM_TYPE_GDDR3 :
t - > mr [ 1 ] & = ~ 0x00000040 ;
t - > mr [ 1 ] | = 0x00000040 * dll_off ;
break ;
default :
t - > mr [ 1 ] & = ~ 0x00000001 ;
t - > mr [ 1 ] | = 0x00000001 * dll_off ;
break ;
}
2012-01-17 21:10:58 +10:00
}
2012-01-18 09:02:28 +10:00
return ret ;
2012-01-17 21:10:58 +10:00
}
void
nouveau_mem_timing_read ( struct drm_device * dev , struct nouveau_pm_memtiming * t )
2012-01-09 15:23:07 +10:00
{
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
u32 timing_base , timing_regs , mr_base ;
int i ;
if ( dev_priv - > card_type > = 0xC0 ) {
timing_base = 0x10f290 ;
mr_base = 0x10f300 ;
} else {
timing_base = 0x100220 ;
mr_base = 0x1002c0 ;
}
t - > id = - 1 ;
switch ( dev_priv - > card_type ) {
case NV_50 :
timing_regs = 9 ;
break ;
case NV_C0 :
case NV_D0 :
timing_regs = 5 ;
break ;
case NV_30 :
case NV_40 :
timing_regs = 3 ;
break ;
default :
timing_regs = 0 ;
return ;
}
for ( i = 0 ; i < timing_regs ; i + + )
t - > reg [ i ] = nv_rd32 ( dev , timing_base + ( 0x04 * i ) ) ;
t - > tCWL = 0 ;
if ( dev_priv - > card_type < NV_C0 ) {
t - > tCWL = ( ( nv_rd32 ( dev , 0x100228 ) & 0x0f000000 ) > > 24 ) + 1 ;
2012-02-07 00:29:06 +01:00
} else if ( dev_priv - > card_type < = NV_D0 ) {
t - > tCWL = ( ( nv_rd32 ( dev , 0x10f294 ) & 0x00000f80 ) > > 7 ) ;
2012-01-09 15:23:07 +10:00
}
t - > mr [ 0 ] = nv_rd32 ( dev , mr_base ) ;
t - > mr [ 1 ] = nv_rd32 ( dev , mr_base + 0x04 ) ;
t - > mr [ 2 ] = nv_rd32 ( dev , mr_base + 0x20 ) ;
t - > mr [ 3 ] = nv_rd32 ( dev , mr_base + 0x24 ) ;
t - > odt = 0 ;
t - > drive_strength = 0 ;
switch ( dev_priv - > vram_type ) {
case NV_MEM_TYPE_DDR3 :
t - > odt | = ( t - > mr [ 1 ] & 0x200 ) > > 7 ;
case NV_MEM_TYPE_DDR2 :
t - > odt | = ( t - > mr [ 1 ] & 0x04 ) > > 2 |
( t - > mr [ 1 ] & 0x40 ) > > 5 ;
break ;
case NV_MEM_TYPE_GDDR3 :
case NV_MEM_TYPE_GDDR5 :
t - > drive_strength = t - > mr [ 1 ] & 0x03 ;
t - > odt = ( t - > mr [ 1 ] & 0x0c ) > > 2 ;
break ;
default :
break ;
}
}
2012-01-23 13:12:09 +10:00
int
nouveau_mem_exec ( struct nouveau_mem_exec_func * exec ,
struct nouveau_pm_level * perflvl )
{
struct drm_nouveau_private * dev_priv = exec - > dev - > dev_private ;
struct nouveau_pm_memtiming * info = & perflvl - > timing ;
u32 tMRD = 1000 , tCKSRE = 0 , tCKSRX = 0 , tXS = 0 , tDLLK = 0 ;
u32 mr [ 3 ] = { info - > mr [ 0 ] , info - > mr [ 1 ] , info - > mr [ 2 ] } ;
u32 mr1_dlloff ;
switch ( dev_priv - > vram_type ) {
case NV_MEM_TYPE_DDR2 :
tDLLK = 2000 ;
mr1_dlloff = 0x00000001 ;
break ;
case NV_MEM_TYPE_DDR3 :
tDLLK = 12000 ;
2012-02-06 16:20:30 +10:00
tCKSRE = 2000 ;
tXS = 1000 ;
2012-01-23 13:12:09 +10:00
mr1_dlloff = 0x00000001 ;
break ;
case NV_MEM_TYPE_GDDR3 :
tDLLK = 40000 ;
mr1_dlloff = 0x00000040 ;
break ;
default :
NV_ERROR ( exec - > dev , " cannot reclock unsupported memtype \n " ) ;
return - ENODEV ;
}
/* fetch current MRs */
switch ( dev_priv - > vram_type ) {
2012-01-24 10:24:05 +10:00
case NV_MEM_TYPE_GDDR3 :
2012-01-23 13:12:09 +10:00
case NV_MEM_TYPE_DDR3 :
mr [ 2 ] = exec - > mrg ( exec , 2 ) ;
default :
mr [ 1 ] = exec - > mrg ( exec , 1 ) ;
mr [ 0 ] = exec - > mrg ( exec , 0 ) ;
break ;
}
/* DLL 'on' -> DLL 'off' mode, disable before entering self-refresh */
if ( ! ( mr [ 1 ] & mr1_dlloff ) & & ( info - > mr [ 1 ] & mr1_dlloff ) ) {
exec - > precharge ( exec ) ;
exec - > mrs ( exec , 1 , mr [ 1 ] | mr1_dlloff ) ;
exec - > wait ( exec , tMRD ) ;
}
/* enter self-refresh mode */
exec - > precharge ( exec ) ;
exec - > refresh ( exec ) ;
exec - > refresh ( exec ) ;
exec - > refresh_auto ( exec , false ) ;
exec - > refresh_self ( exec , true ) ;
exec - > wait ( exec , tCKSRE ) ;
/* modify input clock frequency */
exec - > clock_set ( exec ) ;
/* exit self-refresh mode */
exec - > wait ( exec , tCKSRX ) ;
exec - > precharge ( exec ) ;
exec - > refresh_self ( exec , false ) ;
exec - > refresh_auto ( exec , true ) ;
exec - > wait ( exec , tXS ) ;
2012-02-06 16:20:30 +10:00
exec - > wait ( exec , tXS ) ;
2012-01-23 13:12:09 +10:00
/* update MRs */
if ( mr [ 2 ] ! = info - > mr [ 2 ] ) {
exec - > mrs ( exec , 2 , info - > mr [ 2 ] ) ;
exec - > wait ( exec , tMRD ) ;
}
if ( mr [ 1 ] ! = info - > mr [ 1 ] ) {
2012-01-24 13:39:56 +10:00
/* need to keep DLL off until later, at least on GDDR3 */
exec - > mrs ( exec , 1 , info - > mr [ 1 ] | ( mr [ 1 ] & mr1_dlloff ) ) ;
2012-01-23 13:12:09 +10:00
exec - > wait ( exec , tMRD ) ;
}
if ( mr [ 0 ] ! = info - > mr [ 0 ] ) {
exec - > mrs ( exec , 0 , info - > mr [ 0 ] ) ;
exec - > wait ( exec , tMRD ) ;
}
/* update PFB timing registers */
exec - > timing_set ( exec ) ;
2012-01-24 13:39:56 +10:00
/* DLL (enable + ) reset */
2012-01-23 13:12:09 +10:00
if ( ! ( info - > mr [ 1 ] & mr1_dlloff ) ) {
2012-01-24 13:39:56 +10:00
if ( mr [ 1 ] & mr1_dlloff ) {
exec - > mrs ( exec , 1 , info - > mr [ 1 ] ) ;
exec - > wait ( exec , tMRD ) ;
}
2012-01-23 13:12:09 +10:00
exec - > mrs ( exec , 0 , info - > mr [ 0 ] | 0x00000100 ) ;
exec - > wait ( exec , tMRD ) ;
exec - > mrs ( exec , 0 , info - > mr [ 0 ] | 0x00000000 ) ;
exec - > wait ( exec , tMRD ) ;
exec - > wait ( exec , tDLLK ) ;
if ( dev_priv - > vram_type = = NV_MEM_TYPE_GDDR3 )
exec - > precharge ( exec ) ;
}
return 0 ;
}
2011-12-13 11:57:55 +10:00
int
nouveau_mem_vbios_type ( struct drm_device * dev )
{
struct bit_entry M ;
u8 ramcfg = ( nv_rd32 ( dev , 0x101000 ) & 0x0000003c ) > > 2 ;
if ( ! bit_table ( dev , ' M ' , & M ) | | M . version ! = 2 | | M . length < 5 ) {
u8 * table = ROMPTR ( dev , M . data [ 3 ] ) ;
if ( table & & table [ 0 ] = = 0x10 & & ramcfg < table [ 3 ] ) {
u8 * entry = table + table [ 1 ] + ( ramcfg * table [ 2 ] ) ;
switch ( entry [ 0 ] & 0x0f ) {
case 0 : return NV_MEM_TYPE_DDR2 ;
case 1 : return NV_MEM_TYPE_DDR3 ;
case 2 : return NV_MEM_TYPE_GDDR3 ;
case 3 : return NV_MEM_TYPE_GDDR5 ;
default :
break ;
}
}
}
return NV_MEM_TYPE_UNKNOWN ;
}
2010-08-25 15:26:04 +10:00
static int
2011-06-10 13:36:08 +10:00
nouveau_vram_manager_init ( struct ttm_mem_type_manager * man , unsigned long psize )
2010-08-25 15:26:04 +10:00
{
2011-06-10 13:36:08 +10:00
/* nothing to do */
2010-08-25 15:26:04 +10:00
return 0 ;
}
static int
nouveau_vram_manager_fini ( struct ttm_mem_type_manager * man )
{
2011-06-10 13:36:08 +10:00
/* nothing to do */
2010-08-25 15:26:04 +10:00
return 0 ;
}
2011-06-06 20:54:42 +10:00
static inline void
nouveau_mem_node_cleanup ( struct nouveau_mem * node )
{
if ( node - > vma [ 0 ] . node ) {
nouveau_vm_unmap ( & node - > vma [ 0 ] ) ;
nouveau_vm_put ( & node - > vma [ 0 ] ) ;
}
if ( node - > vma [ 1 ] . node ) {
nouveau_vm_unmap ( & node - > vma [ 1 ] ) ;
nouveau_vm_put ( & node - > vma [ 1 ] ) ;
}
}
2010-08-25 15:26:04 +10:00
static void
nouveau_vram_manager_del ( struct ttm_mem_type_manager * man ,
struct ttm_mem_reg * mem )
{
struct drm_nouveau_private * dev_priv = nouveau_bdev ( man - > bdev ) ;
2010-12-06 15:28:54 +10:00
struct nouveau_vram_engine * vram = & dev_priv - > engine . vram ;
2010-08-25 15:26:04 +10:00
struct drm_device * dev = dev_priv - > dev ;
2011-06-06 20:54:42 +10:00
nouveau_mem_node_cleanup ( mem - > mm_node ) ;
2011-02-10 12:22:52 +10:00
vram - > put ( dev , ( struct nouveau_mem * * ) & mem - > mm_node ) ;
2010-08-25 15:26:04 +10:00
}
static int
nouveau_vram_manager_new ( struct ttm_mem_type_manager * man ,
struct ttm_buffer_object * bo ,
struct ttm_placement * placement ,
struct ttm_mem_reg * mem )
{
struct drm_nouveau_private * dev_priv = nouveau_bdev ( man - > bdev ) ;
2010-12-06 15:28:54 +10:00
struct nouveau_vram_engine * vram = & dev_priv - > engine . vram ;
2010-08-25 15:26:04 +10:00
struct drm_device * dev = dev_priv - > dev ;
struct nouveau_bo * nvbo = nouveau_bo ( bo ) ;
2011-02-10 12:22:52 +10:00
struct nouveau_mem * node ;
2010-11-12 15:13:59 +10:00
u32 size_nc = 0 ;
2010-08-25 15:26:04 +10:00
int ret ;
2010-11-12 15:13:59 +10:00
if ( nvbo - > tile_flags & NOUVEAU_GEM_TILE_NONCONTIG )
2011-06-06 14:15:46 +10:00
size_nc = 1 < < nvbo - > page_shift ;
2010-11-12 15:13:59 +10:00
2010-12-06 15:28:54 +10:00
ret = vram - > get ( dev , mem - > num_pages < < PAGE_SHIFT ,
mem - > page_alignment < < PAGE_SHIFT , size_nc ,
2011-02-14 09:57:35 +10:00
( nvbo - > tile_flags > > 8 ) & 0x3ff , & node ) ;
2011-03-07 17:18:03 +10:00
if ( ret ) {
mem - > mm_node = NULL ;
return ( ret = = - ENOSPC ) ? 0 : ret ;
}
2010-08-25 15:26:04 +10:00
2011-06-06 14:15:46 +10:00
node - > page_shift = nvbo - > page_shift ;
2010-11-10 14:10:04 +10:00
2010-12-06 15:28:54 +10:00
mem - > mm_node = node ;
mem - > start = node - > offset > > PAGE_SHIFT ;
2010-08-25 15:26:04 +10:00
return 0 ;
}
void
nouveau_vram_manager_debug ( struct ttm_mem_type_manager * man , const char * prefix )
{
struct nouveau_mm * mm = man - > priv ;
struct nouveau_mm_node * r ;
2011-01-14 15:46:30 +10:00
u32 total = 0 , free = 0 ;
2010-08-25 15:26:04 +10:00
mutex_lock ( & mm - > mutex ) ;
list_for_each_entry ( r , & mm - > nodes , nl_entry ) {
2011-01-14 15:46:30 +10:00
printk ( KERN_DEBUG " %s %d: 0x%010llx 0x%010llx \n " ,
prefix , r - > type , ( ( u64 ) r - > offset < < 12 ) ,
2010-08-25 15:26:04 +10:00
( ( ( u64 ) r - > offset + r - > length ) < < 12 ) ) ;
2011-01-14 15:46:30 +10:00
2010-08-25 15:26:04 +10:00
total + = r - > length ;
2011-01-14 15:46:30 +10:00
if ( ! r - > type )
free + = r - > length ;
2010-08-25 15:26:04 +10:00
}
mutex_unlock ( & mm - > mutex ) ;
2011-01-14 15:46:30 +10:00
printk ( KERN_DEBUG " %s total: 0x%010llx free: 0x%010llx \n " ,
prefix , ( u64 ) total < < 12 , ( u64 ) free < < 12 ) ;
printk ( KERN_DEBUG " %s block: 0x%08x \n " ,
prefix , mm - > block_size < < 12 ) ;
2010-08-25 15:26:04 +10:00
}
const struct ttm_mem_type_manager_func nouveau_vram_manager = {
nouveau_vram_manager_init ,
nouveau_vram_manager_fini ,
nouveau_vram_manager_new ,
nouveau_vram_manager_del ,
nouveau_vram_manager_debug
} ;
2011-02-10 12:59:51 +10:00
static int
nouveau_gart_manager_init ( struct ttm_mem_type_manager * man , unsigned long psize )
{
return 0 ;
}
static int
nouveau_gart_manager_fini ( struct ttm_mem_type_manager * man )
{
return 0 ;
}
static void
nouveau_gart_manager_del ( struct ttm_mem_type_manager * man ,
struct ttm_mem_reg * mem )
{
2011-06-06 20:54:42 +10:00
nouveau_mem_node_cleanup ( mem - > mm_node ) ;
kfree ( mem - > mm_node ) ;
2011-06-23 16:35:31 +02:00
mem - > mm_node = NULL ;
2011-02-10 12:59:51 +10:00
}
static int
nouveau_gart_manager_new ( struct ttm_mem_type_manager * man ,
struct ttm_buffer_object * bo ,
struct ttm_placement * placement ,
struct ttm_mem_reg * mem )
{
struct drm_nouveau_private * dev_priv = nouveau_bdev ( bo - > bdev ) ;
struct nouveau_mem * node ;
if ( unlikely ( ( mem - > num_pages < < PAGE_SHIFT ) > =
dev_priv - > gart_info . aper_size ) )
return - ENOMEM ;
node = kzalloc ( sizeof ( * node ) , GFP_KERNEL ) ;
if ( ! node )
return - ENOMEM ;
2011-06-06 20:54:42 +10:00
node - > page_shift = 12 ;
2011-02-10 12:59:51 +10:00
mem - > mm_node = node ;
mem - > start = 0 ;
return 0 ;
}
void
nouveau_gart_manager_debug ( struct ttm_mem_type_manager * man , const char * prefix )
{
}
const struct ttm_mem_type_manager_func nouveau_gart_manager = {
nouveau_gart_manager_init ,
nouveau_gart_manager_fini ,
nouveau_gart_manager_new ,
nouveau_gart_manager_del ,
nouveau_gart_manager_debug
} ;