2010-09-01 15:24:28 +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_ramht.h"
2010-09-01 15:24:36 +10:00
static u32
nouveau_ramht_hash_handle ( struct nouveau_channel * chan , u32 handle )
2010-09-01 15:24:28 +10:00
{
2010-09-01 15:24:35 +10:00
struct drm_device * dev = chan - > dev ;
2010-09-01 15:24:28 +10:00
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
2010-09-01 15:24:35 +10:00
struct nouveau_ramht * ramht = chan - > ramht ;
2010-09-01 15:24:36 +10:00
u32 hash = 0 ;
2010-09-01 15:24:28 +10:00
int i ;
2010-09-01 15:24:35 +10:00
NV_DEBUG ( dev , " ch%d handle=0x%08x \n " , chan - > id , handle ) ;
2010-09-01 15:24:28 +10:00
2010-09-01 15:24:35 +10:00
for ( i = 32 ; i > 0 ; i - = ramht - > bits ) {
hash ^ = ( handle & ( ( 1 < < ramht - > bits ) - 1 ) ) ;
handle > > = ramht - > bits ;
2010-09-01 15:24:28 +10:00
}
if ( dev_priv - > card_type < NV_50 )
2010-09-01 15:24:35 +10:00
hash ^ = chan - > id < < ( ramht - > bits - 4 ) ;
2010-09-01 15:24:28 +10:00
hash < < = 3 ;
NV_DEBUG ( dev , " hash=0x%08x \n " , hash ) ;
return hash ;
}
static int
nouveau_ramht_entry_valid ( struct drm_device * dev , struct nouveau_gpuobj * ramht ,
2010-09-01 15:24:36 +10:00
u32 offset )
2010-09-01 15:24:28 +10:00
{
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
2010-09-01 15:24:36 +10:00
u32 ctx = nv_ro32 ( ramht , offset + 4 ) ;
2010-09-01 15:24:28 +10:00
if ( dev_priv - > card_type < NV_40 )
return ( ( ctx & NV_RAMHT_CONTEXT_VALID ) ! = 0 ) ;
return ( ctx ! = 0 ) ;
}
2010-09-05 06:03:07 +02:00
static int
nouveau_ramht_entry_same_channel ( struct nouveau_channel * chan ,
struct nouveau_gpuobj * ramht , u32 offset )
{
struct drm_nouveau_private * dev_priv = chan - > dev - > dev_private ;
u32 ctx = nv_ro32 ( ramht , offset + 4 ) ;
if ( dev_priv - > card_type > = NV_50 )
return true ;
else if ( dev_priv - > card_type > = NV_40 )
return chan - > id = =
( ( ctx > > NV40_RAMHT_CONTEXT_CHANNEL_SHIFT ) & 0x1f ) ;
else
return chan - > id = =
( ( ctx > > NV_RAMHT_CONTEXT_CHANNEL_SHIFT ) & 0x1f ) ;
}
2010-09-01 15:24:28 +10:00
int
2010-09-01 15:24:31 +10:00
nouveau_ramht_insert ( struct nouveau_channel * chan , u32 handle ,
struct nouveau_gpuobj * gpuobj )
2010-09-01 15:24:28 +10:00
{
2010-09-01 15:24:31 +10:00
struct drm_device * dev = chan - > dev ;
2010-09-01 15:24:28 +10:00
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
struct nouveau_instmem_engine * instmem = & dev_priv - > engine . instmem ;
2010-09-01 15:24:31 +10:00
struct nouveau_ramht_entry * entry ;
struct nouveau_gpuobj * ramht = chan - > ramht - > gpuobj ;
2010-09-01 15:24:36 +10:00
unsigned long flags ;
u32 ctx , co , ho ;
2010-09-01 15:24:28 +10:00
2010-09-01 15:24:31 +10:00
if ( nouveau_ramht_find ( chan , handle ) )
return - EEXIST ;
entry = kmalloc ( sizeof ( * entry ) , GFP_KERNEL ) ;
if ( ! entry )
return - ENOMEM ;
entry - > channel = chan ;
entry - > gpuobj = NULL ;
entry - > handle = handle ;
nouveau_gpuobj_ref ( gpuobj , & entry - > gpuobj ) ;
2010-09-01 15:24:28 +10:00
if ( dev_priv - > card_type < NV_40 ) {
2010-11-20 14:42:57 +01:00
ctx = NV_RAMHT_CONTEXT_VALID | ( gpuobj - > pinst > > 4 ) |
2010-09-01 15:24:28 +10:00
( chan - > id < < NV_RAMHT_CONTEXT_CHANNEL_SHIFT ) |
2010-09-01 15:24:31 +10:00
( gpuobj - > engine < < NV_RAMHT_CONTEXT_ENGINE_SHIFT ) ;
2010-09-01 15:24:28 +10:00
} else
if ( dev_priv - > card_type < NV_50 ) {
2010-11-20 14:42:57 +01:00
ctx = ( gpuobj - > pinst > > 4 ) |
2010-09-01 15:24:28 +10:00
( chan - > id < < NV40_RAMHT_CONTEXT_CHANNEL_SHIFT ) |
2010-09-01 15:24:31 +10:00
( gpuobj - > engine < < NV40_RAMHT_CONTEXT_ENGINE_SHIFT ) ;
2010-09-01 15:24:28 +10:00
} else {
2010-09-01 15:24:31 +10:00
if ( gpuobj - > engine = = NVOBJ_ENGINE_DISPLAY ) {
2011-02-01 10:41:01 +10:00
ctx = ( gpuobj - > cinst < < 10 ) |
( chan - > id < < 28 ) |
chan - > id ; /* HASH_TAG */
2010-09-01 15:24:28 +10:00
} else {
2010-09-01 15:24:31 +10:00
ctx = ( gpuobj - > cinst > > 4 ) |
( ( gpuobj - > engine < <
2010-09-01 15:24:28 +10:00
NV40_RAMHT_CONTEXT_ENGINE_SHIFT ) ) ;
}
}
2010-09-01 15:24:36 +10:00
spin_lock_irqsave ( & chan - > ramht - > lock , flags ) ;
list_add ( & entry - > head , & chan - > ramht - > entries ) ;
2010-09-01 15:24:35 +10:00
co = ho = nouveau_ramht_hash_handle ( chan , handle ) ;
2010-09-01 15:24:28 +10:00
do {
if ( ! nouveau_ramht_entry_valid ( dev , ramht , co ) ) {
NV_DEBUG ( dev ,
" insert ch%d 0x%08x: h=0x%08x, c=0x%08x \n " ,
2010-09-01 15:24:31 +10:00
chan - > id , co , handle , ctx ) ;
nv_wo32 ( ramht , co + 0 , handle ) ;
2010-09-01 15:24:29 +10:00
nv_wo32 ( ramht , co + 4 , ctx ) ;
2010-09-01 15:24:28 +10:00
2010-09-01 15:24:36 +10:00
spin_unlock_irqrestore ( & chan - > ramht - > lock , flags ) ;
2010-09-01 15:24:28 +10:00
instmem - > flush ( dev ) ;
return 0 ;
}
NV_DEBUG ( dev , " collision ch%d 0x%08x: h=0x%08x \n " ,
2010-09-01 15:24:29 +10:00
chan - > id , co , nv_ro32 ( ramht , co ) ) ;
2010-09-01 15:24:28 +10:00
co + = 8 ;
2010-09-01 15:24:35 +10:00
if ( co > = ramht - > size )
2010-09-01 15:24:28 +10:00
co = 0 ;
} while ( co ! = ho ) ;
NV_ERROR ( dev , " RAMHT space exhausted. ch=%d \n " , chan - > id ) ;
2010-09-01 15:24:31 +10:00
list_del ( & entry - > head ) ;
2010-09-01 15:24:36 +10:00
spin_unlock_irqrestore ( & chan - > ramht - > lock , flags ) ;
2010-09-01 15:24:31 +10:00
kfree ( entry ) ;
2010-09-01 15:24:28 +10:00
return - ENOMEM ;
}
2010-10-11 03:37:32 +02:00
static struct nouveau_ramht_entry *
nouveau_ramht_remove_entry ( struct nouveau_channel * chan , u32 handle )
{
struct nouveau_ramht * ramht = chan ? chan - > ramht : NULL ;
struct nouveau_ramht_entry * entry ;
unsigned long flags ;
if ( ! ramht )
return NULL ;
spin_lock_irqsave ( & ramht - > lock , flags ) ;
list_for_each_entry ( entry , & ramht - > entries , head ) {
if ( entry - > channel = = chan & &
( ! handle | | entry - > handle = = handle ) ) {
list_del ( & entry - > head ) ;
spin_unlock_irqrestore ( & ramht - > lock , flags ) ;
return entry ;
}
}
spin_unlock_irqrestore ( & ramht - > lock , flags ) ;
return NULL ;
}
2010-09-01 15:24:36 +10:00
static void
2010-10-11 03:37:32 +02:00
nouveau_ramht_remove_hash ( struct nouveau_channel * chan , u32 handle )
2010-09-01 15:24:28 +10:00
{
2010-09-01 15:24:31 +10:00
struct drm_device * dev = chan - > dev ;
2010-09-01 15:24:28 +10:00
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
struct nouveau_instmem_engine * instmem = & dev_priv - > engine . instmem ;
2010-09-01 15:24:31 +10:00
struct nouveau_gpuobj * ramht = chan - > ramht - > gpuobj ;
2010-10-11 03:37:32 +02:00
unsigned long flags ;
2010-09-01 15:24:31 +10:00
u32 co , ho ;
2010-10-11 03:37:32 +02:00
spin_lock_irqsave ( & chan - > ramht - > lock , flags ) ;
2010-09-01 15:24:35 +10:00
co = ho = nouveau_ramht_hash_handle ( chan , handle ) ;
2010-09-01 15:24:28 +10:00
do {
if ( nouveau_ramht_entry_valid ( dev , ramht , co ) & &
2010-09-05 06:03:07 +02:00
nouveau_ramht_entry_same_channel ( chan , ramht , co ) & &
2010-09-01 15:24:31 +10:00
( handle = = nv_ro32 ( ramht , co ) ) ) {
2010-09-01 15:24:28 +10:00
NV_DEBUG ( dev ,
" remove ch%d 0x%08x: h=0x%08x, c=0x%08x \n " ,
2010-09-01 15:24:31 +10:00
chan - > id , co , handle , nv_ro32 ( ramht , co + 4 ) ) ;
2010-09-01 15:24:29 +10:00
nv_wo32 ( ramht , co + 0 , 0x00000000 ) ;
nv_wo32 ( ramht , co + 4 , 0x00000000 ) ;
2010-09-01 15:24:28 +10:00
instmem - > flush ( dev ) ;
2010-10-11 03:37:32 +02:00
goto out ;
2010-09-01 15:24:28 +10:00
}
co + = 8 ;
2010-09-01 15:24:35 +10:00
if ( co > = ramht - > size )
2010-09-01 15:24:28 +10:00
co = 0 ;
} while ( co ! = ho ) ;
NV_ERROR ( dev , " RAMHT entry not found. ch=%d, handle=0x%08x \n " ,
2010-09-01 15:24:31 +10:00
chan - > id , handle ) ;
2010-10-11 03:37:32 +02:00
out :
spin_unlock_irqrestore ( & chan - > ramht - > lock , flags ) ;
2010-09-01 15:24:31 +10:00
}
2010-10-12 10:11:00 +10:00
int
2010-09-01 15:24:36 +10:00
nouveau_ramht_remove ( struct nouveau_channel * chan , u32 handle )
{
2010-10-11 03:37:32 +02:00
struct nouveau_ramht_entry * entry ;
2010-09-01 15:24:36 +10:00
2010-10-11 03:37:32 +02:00
entry = nouveau_ramht_remove_entry ( chan , handle ) ;
if ( ! entry )
2010-10-12 10:11:00 +10:00
return - ENOENT ;
2010-10-11 03:37:32 +02:00
nouveau_ramht_remove_hash ( chan , entry - > handle ) ;
nouveau_gpuobj_ref ( NULL , & entry - > gpuobj ) ;
kfree ( entry ) ;
2010-10-12 10:11:00 +10:00
return 0 ;
2010-09-01 15:24:36 +10:00
}
2010-09-01 15:24:31 +10:00
struct nouveau_gpuobj *
nouveau_ramht_find ( struct nouveau_channel * chan , u32 handle )
{
2010-09-01 15:24:36 +10:00
struct nouveau_ramht * ramht = chan - > ramht ;
2010-09-01 15:24:31 +10:00
struct nouveau_ramht_entry * entry ;
2010-09-01 15:24:36 +10:00
struct nouveau_gpuobj * gpuobj = NULL ;
unsigned long flags ;
2010-09-01 15:24:31 +10:00
2010-09-03 10:25:02 +10:00
if ( unlikely ( ! chan - > ramht ) )
return NULL ;
2010-09-01 15:24:36 +10:00
spin_lock_irqsave ( & ramht - > lock , flags ) ;
2010-09-01 15:24:31 +10:00
list_for_each_entry ( entry , & chan - > ramht - > entries , head ) {
2010-09-01 15:24:36 +10:00
if ( entry - > channel = = chan & & entry - > handle = = handle ) {
gpuobj = entry - > gpuobj ;
break ;
}
2010-09-01 15:24:31 +10:00
}
2010-09-01 15:24:36 +10:00
spin_unlock_irqrestore ( & ramht - > lock , flags ) ;
2010-09-01 15:24:31 +10:00
2010-09-01 15:24:36 +10:00
return gpuobj ;
2010-09-01 15:24:31 +10:00
}
int
nouveau_ramht_new ( struct drm_device * dev , struct nouveau_gpuobj * gpuobj ,
struct nouveau_ramht * * pramht )
{
struct nouveau_ramht * ramht ;
ramht = kzalloc ( sizeof ( * ramht ) , GFP_KERNEL ) ;
if ( ! ramht )
return - ENOMEM ;
ramht - > dev = dev ;
2010-09-01 15:24:36 +10:00
kref_init ( & ramht - > refcount ) ;
2010-09-01 15:24:35 +10:00
ramht - > bits = drm_order ( gpuobj - > size / 8 ) ;
2010-09-01 15:24:31 +10:00
INIT_LIST_HEAD ( & ramht - > entries ) ;
2010-09-01 15:24:36 +10:00
spin_lock_init ( & ramht - > lock ) ;
2010-09-01 15:24:31 +10:00
nouveau_gpuobj_ref ( gpuobj , & ramht - > gpuobj ) ;
* pramht = ramht ;
return 0 ;
}
2010-09-01 15:24:36 +10:00
static void
nouveau_ramht_del ( struct kref * ref )
{
struct nouveau_ramht * ramht =
container_of ( ref , struct nouveau_ramht , refcount ) ;
nouveau_gpuobj_ref ( NULL , & ramht - > gpuobj ) ;
kfree ( ramht ) ;
}
2010-09-01 15:24:31 +10:00
void
nouveau_ramht_ref ( struct nouveau_ramht * ref , struct nouveau_ramht * * ptr ,
struct nouveau_channel * chan )
{
2010-10-11 03:37:32 +02:00
struct nouveau_ramht_entry * entry ;
2010-09-01 15:24:31 +10:00
struct nouveau_ramht * ramht ;
if ( ref )
2010-09-01 15:24:36 +10:00
kref_get ( & ref - > refcount ) ;
2010-09-01 15:24:31 +10:00
ramht = * ptr ;
if ( ramht ) {
2010-10-11 03:37:32 +02:00
while ( ( entry = nouveau_ramht_remove_entry ( chan , 0 ) ) ) {
nouveau_ramht_remove_hash ( chan , entry - > handle ) ;
nouveau_gpuobj_ref ( NULL , & entry - > gpuobj ) ;
kfree ( entry ) ;
2010-09-01 15:24:31 +10:00
}
2010-09-01 15:24:36 +10:00
kref_put ( & ramht - > refcount , nouveau_ramht_del ) ;
2010-09-01 15:24:31 +10:00
}
* ptr = ref ;
2010-09-01 15:24:28 +10:00
}