2012-05-08 04:24:27 +04:00
/*
* Copyright 2012 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 .
*
*/
2012-07-20 02:17:34 +04:00
# include <core/object.h>
# include <core/client.h>
# include <core/device.h>
# include <core/class.h>
# include <core/mm.h>
2012-05-08 04:24:27 +04:00
2012-07-20 02:17:34 +04:00
# include <subdev/fb.h>
# include <subdev/timer.h>
# include <subdev/instmem.h>
2013-03-28 01:16:54 +04:00
# include <engine/graph.h>
2012-07-20 02:17:34 +04:00
# include "nouveau_drm.h"
2012-05-08 04:24:27 +04:00
# include "nouveau_dma.h"
2012-07-20 02:17:34 +04:00
# include "nouveau_gem.h"
# include "nouveau_chan.h"
2012-05-08 04:24:27 +04:00
# include "nouveau_abi16.h"
2012-07-20 02:17:34 +04:00
2014-08-09 22:10:22 +04:00
void nouveau_drm_hack_device ( struct nouveau_drm * , struct nvif_device * ) ;
2012-07-20 02:17:34 +04:00
struct nouveau_abi16 *
nouveau_abi16_get ( struct drm_file * file_priv , struct drm_device * dev )
{
struct nouveau_cli * cli = nouveau_cli ( file_priv ) ;
2014-08-09 22:10:22 +04:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2012-07-20 02:17:34 +04:00
mutex_lock ( & cli - > mutex ) ;
if ( ! cli - > abi16 ) {
struct nouveau_abi16 * abi16 ;
cli - > abi16 = abi16 = kzalloc ( sizeof ( * abi16 ) , GFP_KERNEL ) ;
if ( cli - > abi16 ) {
INIT_LIST_HEAD ( & abi16 - > channels ) ;
abi16 - > client = nv_object ( cli ) ;
/* allocate device object targeting client's default
* device ( ie . the one that belongs to the fd it
* opened )
*/
if ( nouveau_object_new ( abi16 - > client , NVDRM_CLIENT ,
NVDRM_DEVICE , 0x0080 ,
& ( struct nv_device_class ) {
. device = ~ 0ULL ,
} ,
sizeof ( struct nv_device_class ) ,
2014-08-09 22:10:22 +04:00
( struct nouveau_object * * )
& abi16 - > device . object ) = = 0 ) {
nouveau_drm_hack_device ( drm , & abi16 - > device ) ;
2012-07-20 02:17:34 +04:00
return cli - > abi16 ;
2014-08-09 22:10:22 +04:00
}
2012-07-20 02:17:34 +04:00
kfree ( cli - > abi16 ) ;
cli - > abi16 = NULL ;
}
mutex_unlock ( & cli - > mutex ) ;
}
return cli - > abi16 ;
}
int
nouveau_abi16_put ( struct nouveau_abi16 * abi16 , int ret )
{
struct nouveau_cli * cli = ( void * ) abi16 - > client ;
mutex_unlock ( & cli - > mutex ) ;
return ret ;
}
u16
nouveau_abi16_swclass ( struct nouveau_drm * drm )
{
2014-08-09 22:10:22 +04:00
switch ( drm - > device . info . family ) {
case NV_DEVICE_INFO_V0_TNT :
2012-07-20 02:17:34 +04:00
return 0x006e ;
2014-08-09 22:10:22 +04:00
case NV_DEVICE_INFO_V0_CELSIUS :
case NV_DEVICE_INFO_V0_KELVIN :
case NV_DEVICE_INFO_V0_RANKINE :
case NV_DEVICE_INFO_V0_CURIE :
2012-07-20 02:17:34 +04:00
return 0x016e ;
2014-08-09 22:10:22 +04:00
case NV_DEVICE_INFO_V0_TESLA :
2012-07-20 02:17:34 +04:00
return 0x506e ;
2014-08-09 22:10:22 +04:00
case NV_DEVICE_INFO_V0_FERMI :
case NV_DEVICE_INFO_V0_KEPLER :
case NV_DEVICE_INFO_V0_MAXWELL :
2012-07-20 02:17:34 +04:00
return 0x906e ;
}
return 0x0000 ;
}
static void
nouveau_abi16_ntfy_fini ( struct nouveau_abi16_chan * chan ,
struct nouveau_abi16_ntfy * ntfy )
{
nouveau_mm_free ( & chan - > heap , & ntfy - > node ) ;
list_del ( & ntfy - > head ) ;
kfree ( ntfy ) ;
}
static void
nouveau_abi16_chan_fini ( struct nouveau_abi16 * abi16 ,
struct nouveau_abi16_chan * chan )
{
struct nouveau_abi16_ntfy * ntfy , * temp ;
2013-03-03 21:58:45 +04:00
/* wait for all activity to stop before releasing notify object, which
* may be still in use */
if ( chan - > chan & & chan - > ntfy )
nouveau_channel_idle ( chan - > chan ) ;
2012-07-20 02:17:34 +04:00
/* cleanup notifier state */
list_for_each_entry_safe ( ntfy , temp , & chan - > notifiers , head ) {
nouveau_abi16_ntfy_fini ( chan , ntfy ) ;
}
if ( chan - > ntfy ) {
nouveau_bo_vma_del ( chan - > ntfy , & chan - > ntfy_vma ) ;
2013-06-27 15:38:20 +04:00
nouveau_bo_unpin ( chan - > ntfy ) ;
2013-10-02 12:15:17 +04:00
drm_gem_object_unreference_unlocked ( & chan - > ntfy - > gem ) ;
2012-07-20 02:17:34 +04:00
}
if ( chan - > heap . block_size )
nouveau_mm_fini ( & chan - > heap ) ;
/* destroy channel object, all children will be killed too */
if ( chan - > chan ) {
2014-02-10 00:51:25 +04:00
abi16 - > handles & = ~ ( 1ULL < < ( chan - > chan - > handle & 0xffff ) ) ;
2012-07-20 02:17:34 +04:00
nouveau_channel_del ( & chan - > chan ) ;
}
list_del ( & chan - > head ) ;
kfree ( chan ) ;
}
void
nouveau_abi16_fini ( struct nouveau_abi16 * abi16 )
{
struct nouveau_cli * cli = ( void * ) abi16 - > client ;
struct nouveau_abi16_chan * chan , * temp ;
/* cleanup channels */
list_for_each_entry_safe ( chan , temp , & abi16 - > channels , head ) {
nouveau_abi16_chan_fini ( abi16 , chan ) ;
}
/* destroy the device object */
nouveau_object_del ( abi16 - > client , NVDRM_CLIENT , NVDRM_DEVICE ) ;
kfree ( cli - > abi16 ) ;
cli - > abi16 = NULL ;
}
2012-05-08 04:24:27 +04:00
int
nouveau_abi16_ioctl_getparam ( ABI16_IOCTL_ARGS )
{
2014-08-09 22:10:22 +04:00
struct nouveau_cli * cli = nouveau_cli ( file_priv ) ;
2012-07-20 02:17:34 +04:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2014-08-09 22:10:22 +04:00
struct nvif_device * device = & drm - > device ;
struct nouveau_timer * ptimer = nvkm_timer ( device ) ;
struct nouveau_graph * graph = nvkm_gr ( device ) ;
2012-05-08 04:24:27 +04:00
struct drm_nouveau_getparam * getparam = data ;
switch ( getparam - > param ) {
case NOUVEAU_GETPARAM_CHIPSET_ID :
2014-08-09 22:10:22 +04:00
getparam - > value = device - > info . chipset ;
2012-05-08 04:24:27 +04:00
break ;
case NOUVEAU_GETPARAM_PCI_VENDOR :
2014-08-09 22:10:22 +04:00
if ( nv_device_is_pci ( nvkm_device ( device ) ) )
2014-02-17 10:17:26 +04:00
getparam - > value = dev - > pdev - > vendor ;
else
getparam - > value = 0 ;
2012-05-08 04:24:27 +04:00
break ;
case NOUVEAU_GETPARAM_PCI_DEVICE :
2014-08-09 22:10:22 +04:00
if ( nv_device_is_pci ( nvkm_device ( device ) ) )
2014-02-17 10:17:26 +04:00
getparam - > value = dev - > pdev - > device ;
else
getparam - > value = 0 ;
2012-05-08 04:24:27 +04:00
break ;
case NOUVEAU_GETPARAM_BUS_TYPE :
2014-08-09 22:10:22 +04:00
if ( ! nv_device_is_pci ( nvkm_device ( device ) ) )
2014-02-17 10:17:26 +04:00
getparam - > value = 3 ;
else
2012-05-08 04:24:27 +04:00
if ( drm_pci_device_is_agp ( dev ) )
getparam - > value = 0 ;
else
if ( ! pci_is_pcie ( dev - > pdev ) )
getparam - > value = 1 ;
else
getparam - > value = 2 ;
break ;
case NOUVEAU_GETPARAM_FB_SIZE :
2012-07-20 02:17:34 +04:00
getparam - > value = drm - > gem . vram_available ;
2012-05-08 04:24:27 +04:00
break ;
case NOUVEAU_GETPARAM_AGP_SIZE :
2012-07-20 02:17:34 +04:00
getparam - > value = drm - > gem . gart_available ;
2012-05-08 04:24:27 +04:00
break ;
case NOUVEAU_GETPARAM_VM_VRAM_BASE :
getparam - > value = 0 ; /* deprecated */
break ;
case NOUVEAU_GETPARAM_PTIMER_TIME :
2012-07-20 02:17:34 +04:00
getparam - > value = ptimer - > read ( ptimer ) ;
2012-05-08 04:24:27 +04:00
break ;
case NOUVEAU_GETPARAM_HAS_BO_USAGE :
getparam - > value = 1 ;
break ;
case NOUVEAU_GETPARAM_HAS_PAGEFLIP :
getparam - > value = 1 ;
break ;
case NOUVEAU_GETPARAM_GRAPH_UNITS :
2013-03-28 01:16:54 +04:00
getparam - > value = graph - > units ? graph - > units ( graph ) : 0 ;
break ;
2012-05-08 04:24:27 +04:00
default :
2014-08-09 22:10:22 +04:00
NV_PRINTK ( debug , cli , " unknown parameter %lld \n " , getparam - > param ) ;
2012-05-08 04:24:27 +04:00
return - EINVAL ;
}
return 0 ;
}
int
nouveau_abi16_ioctl_setparam ( ABI16_IOCTL_ARGS )
{
return - EINVAL ;
}
int
nouveau_abi16_ioctl_channel_alloc ( ABI16_IOCTL_ARGS )
{
struct drm_nouveau_channel_alloc * init = data ;
2012-07-20 02:17:34 +04:00
struct nouveau_cli * cli = nouveau_cli ( file_priv ) ;
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
struct nouveau_abi16 * abi16 = nouveau_abi16_get ( file_priv , dev ) ;
struct nouveau_abi16_chan * chan ;
struct nouveau_client * client ;
2014-08-09 22:10:22 +04:00
struct nvif_device * device ;
2012-07-20 02:17:34 +04:00
struct nouveau_instmem * imem ;
struct nouveau_fb * pfb ;
2012-05-08 04:24:27 +04:00
int ret ;
2012-07-20 02:17:34 +04:00
if ( unlikely ( ! abi16 ) )
return - ENOMEM ;
2012-11-11 23:00:09 +04:00
if ( ! drm - > channel )
return nouveau_abi16_put ( abi16 , - ENODEV ) ;
2012-07-20 02:17:34 +04:00
client = nv_client ( abi16 - > client ) ;
2014-08-09 22:10:22 +04:00
device = & abi16 - > device ;
imem = nvkm_instmem ( device ) ;
pfb = nvkm_fb ( device ) ;
2012-07-20 02:17:34 +04:00
2012-11-22 07:43:55 +04:00
/* hack to allow channel engine type specification on kepler */
2014-08-09 22:10:22 +04:00
if ( device - > info . family > = NV_DEVICE_INFO_V0_KEPLER ) {
2012-11-22 07:43:55 +04:00
if ( init - > fb_ctxdma_handle ! = ~ 0 )
init - > fb_ctxdma_handle = NVE0_CHANNEL_IND_ENGINE_GR ;
else
init - > fb_ctxdma_handle = init - > tt_ctxdma_handle ;
/* allow flips to be executed if this is a graphics channel */
init - > tt_ctxdma_handle = 0 ;
if ( init - > fb_ctxdma_handle = = NVE0_CHANNEL_IND_ENGINE_GR )
init - > tt_ctxdma_handle = 1 ;
}
if ( init - > fb_ctxdma_handle = = ~ 0 | | init - > tt_ctxdma_handle = = ~ 0 )
return nouveau_abi16_put ( abi16 , - EINVAL ) ;
2012-07-20 02:17:34 +04:00
/* allocate "abi16 channel" data and make up a handle for it */
2014-02-10 00:51:24 +04:00
init - > channel = __ffs64 ( ~ abi16 - > handles ) ;
if ( ~ abi16 - > handles = = 0 )
2012-07-20 02:17:34 +04:00
return nouveau_abi16_put ( abi16 , - ENOSPC ) ;
chan = kzalloc ( sizeof ( * chan ) , GFP_KERNEL ) ;
if ( ! chan )
return nouveau_abi16_put ( abi16 , - ENOMEM ) ;
INIT_LIST_HEAD ( & chan - > notifiers ) ;
list_add ( & chan - > head , & abi16 - > channels ) ;
2014-02-10 00:51:25 +04:00
abi16 - > handles | = ( 1ULL < < init - > channel ) ;
2012-05-08 04:24:27 +04:00
2012-07-20 02:17:34 +04:00
/* create channel object and initialise dma and fence management */
ret = nouveau_channel_new ( drm , cli , NVDRM_DEVICE , NVDRM_CHAN |
init - > channel , init - > fb_ctxdma_handle ,
init - > tt_ctxdma_handle , & chan - > chan ) ;
2012-05-08 04:24:27 +04:00
if ( ret )
2012-07-20 02:17:34 +04:00
goto done ;
2014-08-09 22:10:22 +04:00
if ( device - > info . family > = NV_DEVICE_INFO_V0_TESLA )
2012-07-20 02:17:34 +04:00
init - > pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM |
NOUVEAU_GEM_DOMAIN_GART ;
else
if ( chan - > chan - > push . buffer - > bo . mem . mem_type = = TTM_PL_VRAM )
2012-05-08 04:24:27 +04:00
init - > pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM ;
2012-07-20 02:17:34 +04:00
else
init - > pushbuf_domains = NOUVEAU_GEM_DOMAIN_GART ;
2012-05-08 04:24:27 +04:00
2014-08-09 22:10:22 +04:00
if ( device - > info . family < NV_DEVICE_INFO_V0_CELSIUS ) {
2012-05-08 04:24:27 +04:00
init - > subchan [ 0 ] . handle = 0x00000000 ;
init - > subchan [ 0 ] . grclass = 0x0000 ;
init - > subchan [ 1 ] . handle = NvSw ;
2012-07-20 02:17:34 +04:00
init - > subchan [ 1 ] . grclass = 0x506e ;
2012-05-08 04:24:27 +04:00
init - > nr_subchan = 2 ;
}
/* Named memory object area */
2012-07-20 02:17:34 +04:00
ret = nouveau_gem_new ( dev , PAGE_SIZE , 0 , NOUVEAU_GEM_DOMAIN_GART ,
0 , 0 , & chan - > ntfy ) ;
if ( ret = = 0 )
ret = nouveau_bo_pin ( chan - > ntfy , TTM_PL_FLAG_TT ) ;
if ( ret )
goto done ;
2014-08-09 22:10:22 +04:00
if ( device - > info . family > = NV_DEVICE_INFO_V0_TESLA ) {
2012-07-20 02:17:34 +04:00
ret = nouveau_bo_vma_add ( chan - > ntfy , client - > vm ,
& chan - > ntfy_vma ) ;
if ( ret )
goto done ;
}
2013-10-02 12:15:17 +04:00
ret = drm_gem_handle_create ( file_priv , & chan - > ntfy - > gem ,
2012-05-08 04:24:27 +04:00
& init - > notifier_handle ) ;
2012-07-20 02:17:34 +04:00
if ( ret )
goto done ;
2012-05-08 04:24:27 +04:00
2012-07-20 02:17:34 +04:00
ret = nouveau_mm_init ( & chan - > heap , 0 , PAGE_SIZE , 1 ) ;
done :
if ( ret )
nouveau_abi16_chan_fini ( abi16 , chan ) ;
return nouveau_abi16_put ( abi16 , ret ) ;
2012-05-08 04:24:27 +04:00
}
2012-07-20 02:17:34 +04:00
2012-05-08 04:24:27 +04:00
int
nouveau_abi16_ioctl_channel_free ( ABI16_IOCTL_ARGS )
{
struct drm_nouveau_channel_free * req = data ;
2012-07-20 02:17:34 +04:00
struct nouveau_abi16 * abi16 = nouveau_abi16_get ( file_priv , dev ) ;
struct nouveau_abi16_chan * chan ;
int ret = - ENOENT ;
2012-05-08 04:24:27 +04:00
2012-07-20 02:17:34 +04:00
if ( unlikely ( ! abi16 ) )
return - ENOMEM ;
2012-05-08 04:24:27 +04:00
2012-07-20 02:17:34 +04:00
list_for_each_entry ( chan , & abi16 - > channels , head ) {
if ( chan - > chan - > handle = = ( NVDRM_CHAN | req - > channel ) ) {
nouveau_abi16_chan_fini ( abi16 , chan ) ;
return nouveau_abi16_put ( abi16 , 0 ) ;
}
}
return nouveau_abi16_put ( abi16 , ret ) ;
2012-05-08 04:24:27 +04:00
}
int
nouveau_abi16_ioctl_grobj_alloc ( ABI16_IOCTL_ARGS )
{
struct drm_nouveau_grobj_alloc * init = data ;
2012-07-20 02:17:34 +04:00
struct nouveau_abi16 * abi16 = nouveau_abi16_get ( file_priv , dev ) ;
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
struct nouveau_object * object ;
2012-05-08 04:24:27 +04:00
int ret ;
2012-07-20 02:17:34 +04:00
if ( unlikely ( ! abi16 ) )
return - ENOMEM ;
2012-05-08 04:24:27 +04:00
if ( init - > handle = = ~ 0 )
2012-07-20 02:17:34 +04:00
return nouveau_abi16_put ( abi16 , - EINVAL ) ;
2012-05-08 04:24:27 +04:00
/* compatibility with userspace that assumes 506e for all chipsets */
if ( init - > class = = 0x506e ) {
2012-07-20 02:17:34 +04:00
init - > class = nouveau_abi16_swclass ( drm ) ;
2012-05-08 04:24:27 +04:00
if ( init - > class = = 0x906e )
2012-07-20 02:17:34 +04:00
return nouveau_abi16_put ( abi16 , 0 ) ;
2012-05-08 04:24:27 +04:00
}
2012-07-20 02:17:34 +04:00
ret = nouveau_object_new ( abi16 - > client , NVDRM_CHAN | init - > channel ,
init - > handle , init - > class , NULL , 0 , & object ) ;
return nouveau_abi16_put ( abi16 , ret ) ;
2012-05-08 04:24:27 +04:00
}
int
nouveau_abi16_ioctl_notifierobj_alloc ( ABI16_IOCTL_ARGS )
{
2012-07-20 02:17:34 +04:00
struct drm_nouveau_notifierobj_alloc * info = data ;
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
struct nouveau_abi16 * abi16 = nouveau_abi16_get ( file_priv , dev ) ;
2013-03-24 18:36:38 +04:00
struct nouveau_abi16_chan * chan = NULL , * temp ;
2012-07-20 02:17:34 +04:00
struct nouveau_abi16_ntfy * ntfy ;
2014-08-09 22:10:22 +04:00
struct nvif_device * device = & abi16 - > device ;
2012-07-20 02:17:34 +04:00
struct nouveau_object * object ;
2012-10-08 08:29:16 +04:00
struct nv_dma_class args = { } ;
2012-05-08 04:24:27 +04:00
int ret ;
2012-07-20 02:17:34 +04:00
if ( unlikely ( ! abi16 ) )
return - ENOMEM ;
2012-05-08 04:24:27 +04:00
/* completely unnecessary for these chipsets... */
2014-08-09 22:10:22 +04:00
if ( unlikely ( device - > info . family > = NV_DEVICE_INFO_V0_FERMI ) )
2012-07-20 02:17:34 +04:00
return nouveau_abi16_put ( abi16 , - EINVAL ) ;
2012-05-08 04:24:27 +04:00
2013-03-24 18:36:38 +04:00
list_for_each_entry ( temp , & abi16 - > channels , head ) {
if ( temp - > chan - > handle = = ( NVDRM_CHAN | info - > channel ) ) {
chan = temp ;
2012-07-20 02:17:34 +04:00
break ;
2013-03-24 18:36:38 +04:00
}
2012-07-20 02:17:34 +04:00
}
2012-05-08 04:24:27 +04:00
2012-07-20 02:17:34 +04:00
if ( ! chan )
return nouveau_abi16_put ( abi16 , - ENOENT ) ;
ntfy = kzalloc ( sizeof ( * ntfy ) , GFP_KERNEL ) ;
if ( ! ntfy )
return nouveau_abi16_put ( abi16 , - ENOMEM ) ;
list_add ( & ntfy - > head , & chan - > notifiers ) ;
ntfy - > handle = info - > handle ;
ret = nouveau_mm_head ( & chan - > heap , 1 , info - > size , info - > size , 1 ,
& ntfy - > node ) ;
if ( ret )
goto done ;
args . start = ntfy - > node - > offset ;
args . limit = ntfy - > node - > offset + ntfy - > node - > length - 1 ;
2014-08-09 22:10:22 +04:00
if ( device - > info . family > = NV_DEVICE_INFO_V0_TESLA ) {
2012-07-20 02:17:34 +04:00
args . flags = NV_DMA_TARGET_VM | NV_DMA_ACCESS_VM ;
args . start + = chan - > ntfy_vma . offset ;
args . limit + = chan - > ntfy_vma . offset ;
} else
if ( drm - > agp . stat = = ENABLED ) {
args . flags = NV_DMA_TARGET_AGP | NV_DMA_ACCESS_RDWR ;
args . start + = drm - > agp . base + chan - > ntfy - > bo . offset ;
args . limit + = drm - > agp . base + chan - > ntfy - > bo . offset ;
} else {
args . flags = NV_DMA_TARGET_VM | NV_DMA_ACCESS_RDWR ;
args . start + = chan - > ntfy - > bo . offset ;
args . limit + = chan - > ntfy - > bo . offset ;
}
ret = nouveau_object_new ( abi16 - > client , chan - > chan - > handle ,
ntfy - > handle , 0x003d , & args ,
sizeof ( args ) , & object ) ;
if ( ret )
goto done ;
2014-01-06 02:59:07 +04:00
info - > offset = ntfy - > node - > offset ;
2012-07-20 02:17:34 +04:00
done :
if ( ret )
nouveau_abi16_ntfy_fini ( chan , ntfy ) ;
return nouveau_abi16_put ( abi16 , ret ) ;
2012-05-08 04:24:27 +04:00
}
int
nouveau_abi16_ioctl_gpuobj_free ( ABI16_IOCTL_ARGS )
{
2012-07-20 02:17:34 +04:00
struct drm_nouveau_gpuobj_free * fini = data ;
struct nouveau_abi16 * abi16 = nouveau_abi16_get ( file_priv , dev ) ;
2013-03-24 18:36:38 +04:00
struct nouveau_abi16_chan * chan = NULL , * temp ;
2012-07-20 02:17:34 +04:00
struct nouveau_abi16_ntfy * ntfy ;
2012-05-08 04:24:27 +04:00
int ret ;
2012-07-20 02:17:34 +04:00
if ( unlikely ( ! abi16 ) )
return - ENOMEM ;
2013-03-24 18:36:38 +04:00
list_for_each_entry ( temp , & abi16 - > channels , head ) {
if ( temp - > chan - > handle = = ( NVDRM_CHAN | fini - > channel ) ) {
chan = temp ;
2012-07-20 02:17:34 +04:00
break ;
2013-03-24 18:36:38 +04:00
}
2012-07-20 02:17:34 +04:00
}
if ( ! chan )
return nouveau_abi16_put ( abi16 , - ENOENT ) ;
2012-05-08 04:24:27 +04:00
2012-07-20 02:17:34 +04:00
/* synchronize with the user channel and destroy the gpu object */
nouveau_channel_idle ( chan - > chan ) ;
2012-05-08 04:24:27 +04:00
2012-07-20 02:17:34 +04:00
ret = nouveau_object_del ( abi16 - > client , chan - > chan - > handle , fini - > handle ) ;
if ( ret )
return nouveau_abi16_put ( abi16 , ret ) ;
/* cleanup extra state if this object was a notifier */
list_for_each_entry ( ntfy , & chan - > notifiers , head ) {
if ( ntfy - > handle = = fini - > handle ) {
nouveau_mm_free ( & chan - > heap , & ntfy - > node ) ;
list_del ( & ntfy - > head ) ;
break ;
}
}
return nouveau_abi16_put ( abi16 , 0 ) ;
2012-05-08 04:24:27 +04:00
}