2009-12-11 19:24:15 +10:00
/*
* Copyright ( C ) 2007 Ben Skeggs .
*
* All Rights Reserved .
*
* 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 COPYRIGHT OWNER ( S ) AND / OR ITS 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 .
*
*/
# include "drmP.h"
# include "drm.h"
# include "nouveau_drv.h"
2010-09-01 15:24:31 +10:00
# include "nouveau_ramht.h"
2009-12-11 19:24:15 +10:00
int
nouveau_notifier_init_channel ( struct nouveau_channel * chan )
{
struct drm_device * dev = chan - > dev ;
2011-06-07 13:17:45 +10:00
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
2009-12-11 19:24:15 +10:00
struct nouveau_bo * ntfy = NULL ;
2011-04-18 09:12:25 +10:00
uint32_t flags , ttmpl ;
2009-12-11 19:24:15 +10:00
int ret ;
2011-04-18 09:12:25 +10:00
if ( nouveau_vram_notify ) {
2011-02-10 14:42:08 +10:00
flags = NOUVEAU_GEM_DOMAIN_VRAM ;
2011-04-18 09:12:25 +10:00
ttmpl = TTM_PL_FLAG_VRAM ;
} else {
2011-02-10 14:42:08 +10:00
flags = NOUVEAU_GEM_DOMAIN_GART ;
2011-04-18 09:12:25 +10:00
ttmpl = TTM_PL_FLAG_TT ;
}
2010-01-27 14:29:05 +10:00
2011-06-07 12:25:36 +10:00
ret = nouveau_gem_new ( dev , PAGE_SIZE , 0 , flags , 0 , 0 , & ntfy ) ;
2009-12-11 19:24:15 +10:00
if ( ret )
return ret ;
2011-04-18 09:12:25 +10:00
ret = nouveau_bo_pin ( ntfy , ttmpl ) ;
2009-12-11 19:24:15 +10:00
if ( ret )
goto out_err ;
ret = nouveau_bo_map ( ntfy ) ;
if ( ret )
goto out_err ;
2011-06-07 13:17:45 +10:00
if ( dev_priv - > card_type > = NV_50 ) {
ret = nouveau_bo_vma_add ( ntfy , chan - > vm , & chan - > notifier_vma ) ;
if ( ret )
goto out_err ;
}
2010-06-01 15:32:24 +10:00
ret = drm_mm_init ( & chan - > notifier_heap , 0 , ntfy - > bo . mem . size ) ;
2009-12-11 19:24:15 +10:00
if ( ret )
goto out_err ;
chan - > notifier_bo = ntfy ;
out_err :
2011-06-07 13:17:45 +10:00
if ( ret ) {
nouveau_bo_vma_del ( ntfy , & chan - > notifier_vma ) ;
2010-02-09 05:49:12 +00:00
drm_gem_object_unreference_unlocked ( ntfy - > gem ) ;
2011-06-07 13:17:45 +10:00
}
2009-12-11 19:24:15 +10:00
return ret ;
}
void
nouveau_notifier_takedown_channel ( struct nouveau_channel * chan )
{
struct drm_device * dev = chan - > dev ;
if ( ! chan - > notifier_bo )
return ;
2011-06-07 13:17:45 +10:00
nouveau_bo_vma_del ( chan - > notifier_bo , & chan - > notifier_vma ) ;
2009-12-11 19:24:15 +10:00
nouveau_bo_unmap ( chan - > notifier_bo ) ;
mutex_lock ( & dev - > struct_mutex ) ;
nouveau_bo_unpin ( chan - > notifier_bo ) ;
mutex_unlock ( & dev - > struct_mutex ) ;
2010-02-09 05:49:12 +00:00
drm_gem_object_unreference_unlocked ( chan - > notifier_bo - > gem ) ;
2010-06-01 15:32:24 +10:00
drm_mm_takedown ( & chan - > notifier_heap ) ;
2009-12-11 19:24:15 +10:00
}
static void
nouveau_notifier_gpuobj_dtor ( struct drm_device * dev ,
struct nouveau_gpuobj * gpuobj )
{
NV_DEBUG ( dev , " \n " ) ;
if ( gpuobj - > priv )
2010-06-01 15:32:24 +10:00
drm_mm_put_block ( gpuobj - > priv ) ;
2009-12-11 19:24:15 +10:00
}
int
nouveau_notifier_alloc ( struct nouveau_channel * chan , uint32_t handle ,
2011-03-04 09:58:36 +10:00
int size , uint32_t start , uint32_t end ,
uint32_t * b_offset )
2009-12-11 19:24:15 +10:00
{
struct drm_device * dev = chan - > dev ;
2011-02-10 12:59:51 +10:00
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
2009-12-11 19:24:15 +10:00
struct nouveau_gpuobj * nobj = NULL ;
2010-06-01 15:32:24 +10:00
struct drm_mm_node * mem ;
2011-11-19 17:22:37 +01:00
uint64_t offset ;
2009-12-11 19:24:15 +10:00
int target , ret ;
2011-03-04 09:58:36 +10:00
mem = drm_mm_search_free_in_range ( & chan - > notifier_heap , size , 0 ,
start , end , 0 ) ;
2010-06-01 15:32:24 +10:00
if ( mem )
2011-03-04 09:58:36 +10:00
mem = drm_mm_get_block_range ( mem , size , 0 , start , end ) ;
2009-12-11 19:24:15 +10:00
if ( ! mem ) {
NV_ERROR ( dev , " Channel %d notifier block full \n " , chan - > id ) ;
return - ENOMEM ;
}
2011-02-10 12:59:51 +10:00
if ( dev_priv - > card_type < NV_50 ) {
if ( chan - > notifier_bo - > bo . mem . mem_type = = TTM_PL_VRAM )
target = NV_MEM_TARGET_VRAM ;
else
target = NV_MEM_TARGET_GART ;
2011-06-07 11:24:14 +10:00
offset = chan - > notifier_bo - > bo . offset ;
2011-02-10 12:59:51 +10:00
} else {
target = NV_MEM_TARGET_VM ;
2011-06-07 13:17:45 +10:00
offset = chan - > notifier_vma . offset ;
2011-02-10 12:59:51 +10:00
}
2009-12-11 19:24:15 +10:00
offset + = mem - > start ;
ret = nouveau_gpuobj_dma_new ( chan , NV_CLASS_DMA_IN_MEMORY , offset ,
2010-11-16 11:50:09 +10:00
mem - > size , NV_MEM_ACCESS_RW , target ,
2009-12-11 19:24:15 +10:00
& nobj ) ;
if ( ret ) {
2010-06-01 15:32:24 +10:00
drm_mm_put_block ( mem ) ;
2009-12-11 19:24:15 +10:00
NV_ERROR ( dev , " Error creating notifier ctxdma: %d \n " , ret ) ;
return ret ;
}
2010-06-01 15:32:24 +10:00
nobj - > dtor = nouveau_notifier_gpuobj_dtor ;
nobj - > priv = mem ;
2009-12-11 19:24:15 +10:00
2010-09-01 15:24:31 +10:00
ret = nouveau_ramht_insert ( chan , handle , nobj ) ;
nouveau_gpuobj_ref ( NULL , & nobj ) ;
2009-12-11 19:24:15 +10:00
if ( ret ) {
2010-06-01 15:32:24 +10:00
drm_mm_put_block ( mem ) ;
2010-09-01 15:24:31 +10:00
NV_ERROR ( dev , " Error adding notifier to ramht: %d \n " , ret ) ;
2009-12-11 19:24:15 +10:00
return ret ;
}
* b_offset = mem - > start ;
return 0 ;
}
int
nouveau_notifier_offset ( struct nouveau_gpuobj * nobj , uint32_t * poffset )
{
if ( ! nobj | | nobj - > dtor ! = nouveau_notifier_gpuobj_dtor )
return - EINVAL ;
if ( poffset ) {
2010-06-01 15:32:24 +10:00
struct drm_mm_node * mem = nobj - > priv ;
2009-12-11 19:24:15 +10:00
if ( * poffset > = mem - > size )
return false ;
* poffset + = mem - > start ;
}
return 0 ;
}
int
nouveau_ioctl_notifier_alloc ( struct drm_device * dev , void * data ,
struct drm_file * file_priv )
{
2010-11-24 10:07:21 +10:00
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
2009-12-11 19:24:15 +10:00
struct drm_nouveau_notifierobj_alloc * na = data ;
struct nouveau_channel * chan ;
int ret ;
2010-11-24 10:07:21 +10:00
/* completely unnecessary for these chipsets... */
if ( unlikely ( dev_priv - > card_type > = NV_C0 ) )
return - EINVAL ;
2011-06-01 19:18:48 +10:00
chan = nouveau_channel_get ( file_priv , na - > channel ) ;
2010-10-06 16:16:59 +10:00
if ( IS_ERR ( chan ) )
return PTR_ERR ( chan ) ;
2009-12-11 19:24:15 +10:00
2011-03-04 09:58:36 +10:00
ret = nouveau_notifier_alloc ( chan , na - > handle , na - > size , 0 , 0x1000 ,
& na - > offset ) ;
2010-10-06 16:16:59 +10:00
nouveau_channel_put ( & chan ) ;
return ret ;
2009-12-11 19:24:15 +10:00
}