2010-08-25 15:26:04 +10:00
/*
* Copyright 2010 Red Hat Inc .
*
* 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 : Ben Skeggs
*/
# include "drmP.h"
# include "nouveau_drv.h"
# include "nouveau_mm.h"
static int types [ 0x80 ] = {
1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 2 , 2 , 2 , 2 , 0 , 0 , 0 , 0 ,
1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
1 , 1 , 1 , 1 , 1 , 1 , 1 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 0 , 0 ,
0 , 0 , 0 , 0 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 1 , 2 , 2 , 2 , 2 ,
1 , 0 , 2 , 0 , 1 , 0 , 2 , 0 , 1 , 1 , 2 , 2 , 1 , 1 , 0 , 0
} ;
2010-12-06 15:28:54 +10:00
bool
nv50_vram_flags_valid ( struct drm_device * dev , u32 tile_flags )
{
int type = ( tile_flags & NOUVEAU_GEM_TILE_LAYOUT_MASK ) > > 8 ;
2010-12-20 12:26:26 +03:00
if ( likely ( type < ARRAY_SIZE ( types ) & & types [ type ] ) )
2010-12-06 15:28:54 +10:00
return true ;
return false ;
}
2010-08-25 15:26:04 +10:00
void
2011-02-10 12:22:52 +10:00
nv50_vram_del ( struct drm_device * dev , struct nouveau_mem * * pmem )
2010-08-25 15:26:04 +10:00
{
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
2011-06-24 10:14:07 +10:00
struct nouveau_mm * mm = & dev_priv - > engine . vram . mm ;
2010-08-25 15:26:04 +10:00
struct nouveau_mm_node * this ;
2011-02-10 12:22:52 +10:00
struct nouveau_mem * mem ;
2010-08-25 15:26:04 +10:00
2011-02-10 12:22:52 +10:00
mem = * pmem ;
* pmem = NULL ;
if ( unlikely ( mem = = NULL ) )
2010-08-25 15:26:04 +10:00
return ;
mutex_lock ( & mm - > mutex ) ;
2011-02-10 12:22:52 +10:00
while ( ! list_empty ( & mem - > regions ) ) {
this = list_first_entry ( & mem - > regions , struct nouveau_mm_node , rl_entry ) ;
2010-08-25 15:26:04 +10:00
list_del ( & this - > rl_entry ) ;
nouveau_mm_put ( mm , this ) ;
}
2011-02-14 09:57:35 +10:00
if ( mem - > tag ) {
drm_mm_put_block ( mem - > tag ) ;
mem - > tag = NULL ;
}
2010-08-25 15:26:04 +10:00
mutex_unlock ( & mm - > mutex ) ;
2011-02-10 12:22:52 +10:00
kfree ( mem ) ;
2010-08-25 15:26:04 +10:00
}
int
nv50_vram_new ( struct drm_device * dev , u64 size , u32 align , u32 size_nc ,
2011-02-14 09:57:35 +10:00
u32 memtype , struct nouveau_mem * * pmem )
2010-08-25 15:26:04 +10:00
{
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
2011-06-24 10:14:07 +10:00
struct nouveau_mm * mm = & dev_priv - > engine . vram . mm ;
2010-08-25 15:26:04 +10:00
struct nouveau_mm_node * r ;
2011-02-10 12:22:52 +10:00
struct nouveau_mem * mem ;
2011-02-14 09:57:35 +10:00
int comp = ( memtype & 0x300 ) > > 8 ;
int type = ( memtype & 0x07f ) ;
2010-08-25 15:26:04 +10:00
int ret ;
if ( ! types [ type ] )
return - EINVAL ;
size > > = 12 ;
align > > = 12 ;
size_nc > > = 12 ;
2011-02-10 12:22:52 +10:00
mem = kzalloc ( sizeof ( * mem ) , GFP_KERNEL ) ;
if ( ! mem )
2010-08-25 15:26:04 +10:00
return - ENOMEM ;
2011-02-14 09:57:35 +10:00
mutex_lock ( & mm - > mutex ) ;
if ( comp ) {
if ( align = = 16 ) {
struct nouveau_fb_engine * pfb = & dev_priv - > engine . fb ;
int n = ( size > > 4 ) * comp ;
mem - > tag = drm_mm_search_free ( & pfb - > tag_heap , n , 0 , 0 ) ;
if ( mem - > tag )
mem - > tag = drm_mm_get_block ( mem - > tag , n , 0 ) ;
}
if ( unlikely ( ! mem - > tag ) )
comp = 0 ;
}
2011-02-10 12:22:52 +10:00
INIT_LIST_HEAD ( & mem - > regions ) ;
mem - > dev = dev_priv - > dev ;
2011-02-14 09:57:35 +10:00
mem - > memtype = ( comp < < 7 ) | type ;
2011-02-10 12:22:52 +10:00
mem - > size = size ;
2010-08-25 15:26:04 +10:00
do {
ret = nouveau_mm_get ( mm , types [ type ] , size , size_nc , align , & r ) ;
if ( ret ) {
mutex_unlock ( & mm - > mutex ) ;
2011-02-10 12:22:52 +10:00
nv50_vram_del ( dev , & mem ) ;
2010-08-25 15:26:04 +10:00
return ret ;
}
2011-02-10 12:22:52 +10:00
list_add_tail ( & r - > rl_entry , & mem - > regions ) ;
2010-08-25 15:26:04 +10:00
size - = r - > length ;
} while ( size ) ;
mutex_unlock ( & mm - > mutex ) ;
2011-02-10 12:22:52 +10:00
r = list_first_entry ( & mem - > regions , struct nouveau_mm_node , rl_entry ) ;
mem - > offset = ( u64 ) r - > offset < < 12 ;
* pmem = mem ;
2010-08-25 15:26:04 +10:00
return 0 ;
}
static u32
nv50_vram_rblock ( struct drm_device * dev )
{
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
int i , parts , colbits , rowbitsa , rowbitsb , banks ;
u64 rowsize , predicted ;
u32 r0 , r4 , rt , ru , rblock_size ;
r0 = nv_rd32 ( dev , 0x100200 ) ;
r4 = nv_rd32 ( dev , 0x100204 ) ;
rt = nv_rd32 ( dev , 0x100250 ) ;
ru = nv_rd32 ( dev , 0x001540 ) ;
NV_DEBUG ( dev , " memcfg 0x%08x 0x%08x 0x%08x 0x%08x \n " , r0 , r4 , rt , ru ) ;
for ( i = 0 , parts = 0 ; i < 8 ; i + + ) {
if ( ru & ( 0x00010000 < < i ) )
parts + + ;
}
colbits = ( r4 & 0x0000f000 ) > > 12 ;
rowbitsa = ( ( r4 & 0x000f0000 ) > > 16 ) + 8 ;
rowbitsb = ( ( r4 & 0x00f00000 ) > > 20 ) + 8 ;
2011-10-07 16:00:31 +10:00
banks = 1 < < ( ( ( r4 & 0x03000000 ) > > 24 ) + 2 ) ;
2010-08-25 15:26:04 +10:00
rowsize = parts * banks * ( 1 < < colbits ) * 8 ;
predicted = rowsize < < rowbitsa ;
if ( r0 & 0x00000004 )
predicted + = rowsize < < rowbitsb ;
if ( predicted ! = dev_priv - > vram_size ) {
NV_WARN ( dev , " memory controller reports %dMiB VRAM \n " ,
( u32 ) ( dev_priv - > vram_size > > 20 ) ) ;
NV_WARN ( dev , " we calculated %dMiB VRAM \n " ,
( u32 ) ( predicted > > 20 ) ) ;
}
rblock_size = rowsize ;
if ( rt & 1 )
rblock_size * = 3 ;
NV_DEBUG ( dev , " rblock %d bytes \n " , rblock_size ) ;
return rblock_size ;
}
int
nv50_vram_init ( struct drm_device * dev )
{
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
2011-06-10 13:36:08 +10:00
struct nouveau_vram_engine * vram = & dev_priv - > engine . vram ;
const u32 rsvd_head = ( 256 * 1024 ) > > 12 ; /* vga memory */
const u32 rsvd_tail = ( 1024 * 1024 ) > > 12 ; /* vbios etc */
u32 rblock , length ;
2010-08-25 15:26:04 +10:00
dev_priv - > vram_size = nv_rd32 ( dev , 0x10020c ) ;
dev_priv - > vram_size | = ( dev_priv - > vram_size & 0xff ) < < 32 ;
dev_priv - > vram_size & = 0xffffffff00ULL ;
2011-06-10 13:36:08 +10:00
/* IGPs, no funky reordering happens here, they don't have VRAM */
if ( dev_priv - > chipset = = 0xaa | |
dev_priv - > chipset = = 0xac | |
dev_priv - > chipset = = 0xaf ) {
2010-08-25 15:26:04 +10:00
dev_priv - > vram_sys_base = ( u64 ) nv_rd32 ( dev , 0x100e10 ) < < 12 ;
2011-06-10 13:36:08 +10:00
rblock = 4096 > > 12 ;
} else {
rblock = nv50_vram_rblock ( dev ) > > 12 ;
2010-08-25 15:26:04 +10:00
}
2011-06-10 13:36:08 +10:00
length = ( dev_priv - > vram_size > > 12 ) - rsvd_head - rsvd_tail ;
return nouveau_mm_init ( & vram - > mm , rsvd_head , length , rblock ) ;
}
void
nv50_vram_fini ( struct drm_device * dev )
{
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
struct nouveau_vram_engine * vram = & dev_priv - > engine . vram ;
nouveau_mm_fini ( & vram - > mm ) ;
2010-08-25 15:26:04 +10:00
}