2012-07-20 02:17:34 +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 .
*
* Authors : Ben Skeggs
*/
# include <core/object.h>
# include <core/client.h>
# include <core/device.h>
# include <core/class.h>
# include "nouveau_drm.h"
# include "nouveau_dma.h"
# include "nouveau_bo.h"
# include "nouveau_chan.h"
# include "nouveau_fence.h"
# include "nouveau_abi16.h"
MODULE_PARM_DESC ( vram_pushbuf , " Create DMA push buffers in VRAM " ) ;
static int nouveau_vram_pushbuf ;
module_param_named ( vram_pushbuf , nouveau_vram_pushbuf , int , 0400 ) ;
int
nouveau_channel_idle ( struct nouveau_channel * chan )
{
2014-08-09 22:10:22 +04:00
struct nouveau_cli * cli = ( void * ) nvif_client ( chan - > object ) ;
2012-07-20 02:17:34 +04:00
struct nouveau_fence * fence = NULL ;
int ret ;
2013-02-14 07:43:21 +04:00
ret = nouveau_fence_new ( chan , false , & fence ) ;
2012-07-20 02:17:34 +04:00
if ( ! ret ) {
ret = nouveau_fence_wait ( fence , false , false ) ;
nouveau_fence_unref ( & fence ) ;
}
if ( ret )
2014-08-09 22:10:22 +04:00
NV_PRINTK ( error , cli , " failed to idle channel 0x%08x [%s] \n " ,
2014-08-09 22:10:22 +04:00
chan - > object - > handle , nvkm_client ( & cli - > base ) - > name ) ;
2012-07-20 02:17:34 +04:00
return ret ;
}
void
nouveau_channel_del ( struct nouveau_channel * * pchan )
{
struct nouveau_channel * chan = * pchan ;
if ( chan ) {
if ( chan - > fence ) {
nouveau_channel_idle ( chan ) ;
nouveau_fence ( chan - > drm ) - > context_del ( chan ) ;
}
2014-08-09 22:10:22 +04:00
nvif_object_fini ( & chan - > nvsw ) ;
nvif_object_fini ( & chan - > gart ) ;
nvif_object_fini ( & chan - > vram ) ;
nvif_object_ref ( NULL , & chan - > object ) ;
nvif_object_fini ( & chan - > push . ctxdma ) ;
2012-07-20 02:17:34 +04:00
nouveau_bo_vma_del ( chan - > push . buffer , & chan - > push . vma ) ;
nouveau_bo_unmap ( chan - > push . buffer ) ;
2012-11-26 02:02:28 +04:00
if ( chan - > push . buffer & & chan - > push . buffer - > pin_refcnt )
nouveau_bo_unpin ( chan - > push . buffer ) ;
2012-07-20 02:17:34 +04:00
nouveau_bo_ref ( NULL , & chan - > push . buffer ) ;
2014-08-09 22:10:22 +04:00
nvif_device_ref ( NULL , & chan - > device ) ;
2012-07-20 02:17:34 +04:00
kfree ( chan ) ;
}
* pchan = NULL ;
}
static int
2014-08-09 22:10:22 +04:00
nouveau_channel_prep ( struct nouveau_drm * drm , struct nvif_device * device ,
u32 handle , u32 size , struct nouveau_channel * * pchan )
2012-07-20 02:17:34 +04:00
{
2014-08-09 22:10:22 +04:00
struct nouveau_cli * cli = ( void * ) nvif_client ( & device - > base ) ;
2014-08-09 22:10:22 +04:00
struct nouveau_instmem * imem = nvkm_instmem ( device ) ;
struct nouveau_vmmgr * vmm = nvkm_vmmgr ( device ) ;
struct nouveau_fb * pfb = nvkm_fb ( device ) ;
2012-07-20 02:17:34 +04:00
struct nv_dma_class args = { } ;
struct nouveau_channel * chan ;
u32 target ;
int ret ;
chan = * pchan = kzalloc ( sizeof ( * chan ) , GFP_KERNEL ) ;
if ( ! chan )
return - ENOMEM ;
2014-08-09 22:10:22 +04:00
nvif_device_ref ( device , & chan - > device ) ;
2012-07-20 02:17:34 +04:00
chan - > drm = drm ;
/* allocate memory for dma push buffer */
target = TTM_PL_FLAG_TT ;
if ( nouveau_vram_pushbuf )
target = TTM_PL_FLAG_VRAM ;
ret = nouveau_bo_new ( drm - > dev , size , 0 , target , 0 , 0 , NULL ,
& chan - > push . buffer ) ;
if ( ret = = 0 ) {
ret = nouveau_bo_pin ( chan - > push . buffer , target ) ;
if ( ret = = 0 )
ret = nouveau_bo_map ( chan - > push . buffer ) ;
}
if ( ret ) {
nouveau_channel_del ( pchan ) ;
return ret ;
}
/* create dma object covering the *entire* memory space that the
* pushbuf lives in , this is because the GEM code requires that
* we be able to call out to other ( indirect ) push buffers
*/
chan - > push . vma . offset = chan - > push . buffer - > bo . offset ;
2014-08-09 22:10:22 +04:00
if ( device - > info . family > = NV_DEVICE_INFO_V0_TESLA ) {
2014-08-09 22:10:22 +04:00
ret = nouveau_bo_vma_add ( chan - > push . buffer , cli - > vm ,
2012-07-20 02:17:34 +04:00
& chan - > push . vma ) ;
if ( ret ) {
nouveau_channel_del ( pchan ) ;
return ret ;
}
args . flags = NV_DMA_TARGET_VM | NV_DMA_ACCESS_VM ;
args . start = 0 ;
2014-08-09 22:10:22 +04:00
args . limit = cli - > vm - > vmm - > limit - 1 ;
2012-07-20 02:17:34 +04:00
} else
if ( chan - > push . buffer - > bo . mem . mem_type = = TTM_PL_VRAM ) {
2013-03-04 07:01:21 +04:00
u64 limit = pfb - > ram - > size - imem - > reserved - 1 ;
2014-08-09 22:10:22 +04:00
if ( device - > info . family = = NV_DEVICE_INFO_V0_TNT ) {
2012-07-20 02:17:34 +04:00
/* nv04 vram pushbuf hack, retarget to its location in
* the framebuffer bar rather than direct vram access . .
* nfi why this exists , it came from the - nv ddx .
*/
args . flags = NV_DMA_TARGET_PCI | NV_DMA_ACCESS_RDWR ;
2014-08-09 22:10:22 +04:00
args . start = nv_device_resource_start ( nvkm_device ( device ) , 1 ) ;
2012-07-20 02:17:34 +04:00
args . limit = args . start + limit ;
} else {
args . flags = NV_DMA_TARGET_VRAM | NV_DMA_ACCESS_RDWR ;
args . start = 0 ;
args . limit = limit ;
}
} else {
if ( chan - > drm - > agp . stat = = ENABLED ) {
args . flags = NV_DMA_TARGET_AGP | NV_DMA_ACCESS_RDWR ;
args . start = chan - > drm - > agp . base ;
args . limit = chan - > drm - > agp . base +
chan - > drm - > agp . size - 1 ;
} else {
args . flags = NV_DMA_TARGET_VM | NV_DMA_ACCESS_RDWR ;
args . start = 0 ;
args . limit = vmm - > limit - 1 ;
}
}
2014-08-09 22:10:22 +04:00
ret = nvif_object_init ( nvif_object ( device ) , NULL , NVDRM_PUSH |
( handle & 0xffff ) , NV_DMA_FROM_MEMORY_CLASS ,
& args , sizeof ( args ) , & chan - > push . ctxdma ) ;
2012-07-20 02:17:34 +04:00
if ( ret ) {
nouveau_channel_del ( pchan ) ;
return ret ;
}
return 0 ;
}
2012-08-20 01:00:00 +04:00
static int
2014-08-09 22:10:22 +04:00
nouveau_channel_ind ( struct nouveau_drm * drm , struct nvif_device * device ,
u32 handle , u32 engine , struct nouveau_channel * * pchan )
2012-07-20 02:17:34 +04:00
{
2012-08-19 10:03:00 +04:00
static const u16 oclasses [ ] = { NVE0_CHANNEL_IND_CLASS ,
NVC0_CHANNEL_IND_CLASS ,
NV84_CHANNEL_IND_CLASS ,
NV50_CHANNEL_IND_CLASS ,
0 } ;
2012-07-20 02:17:34 +04:00
const u16 * oclass = oclasses ;
2012-08-06 12:16:37 +04:00
struct nve0_channel_ind_class args ;
2012-07-20 02:17:34 +04:00
struct nouveau_channel * chan ;
int ret ;
/* allocate dma push buffer */
2014-08-09 22:10:22 +04:00
ret = nouveau_channel_prep ( drm , device , handle , 0x12000 , & chan ) ;
2012-07-20 02:17:34 +04:00
* pchan = chan ;
if ( ret )
return ret ;
/* create channel object */
2014-08-09 22:10:22 +04:00
args . pushbuf = chan - > push . ctxdma . handle ;
2012-07-20 02:17:34 +04:00
args . ioffset = 0x10000 + chan - > push . vma . offset ;
args . ilength = 0x02000 ;
2012-08-06 13:38:25 +04:00
args . engine = engine ;
2012-07-20 02:17:34 +04:00
do {
2014-08-09 22:10:22 +04:00
ret = nvif_object_new ( nvif_object ( device ) , handle , * oclass + + ,
& args , sizeof ( args ) , & chan - > object ) ;
2012-07-20 02:17:34 +04:00
if ( ret = = 0 )
return ret ;
} while ( * oclass ) ;
nouveau_channel_del ( pchan ) ;
return ret ;
}
static int
2014-08-09 22:10:22 +04:00
nouveau_channel_dma ( struct nouveau_drm * drm , struct nvif_device * device ,
u32 handle , struct nouveau_channel * * pchan )
2012-07-20 02:17:34 +04:00
{
2012-08-19 10:03:00 +04:00
static const u16 oclasses [ ] = { NV40_CHANNEL_DMA_CLASS ,
NV17_CHANNEL_DMA_CLASS ,
NV10_CHANNEL_DMA_CLASS ,
NV03_CHANNEL_DMA_CLASS ,
0 } ;
2012-07-20 02:17:34 +04:00
const u16 * oclass = oclasses ;
2012-08-14 09:02:29 +04:00
struct nv03_channel_dma_class args ;
2012-07-20 02:17:34 +04:00
struct nouveau_channel * chan ;
int ret ;
/* allocate dma push buffer */
2014-08-09 22:10:22 +04:00
ret = nouveau_channel_prep ( drm , device , handle , 0x10000 , & chan ) ;
2012-07-20 02:17:34 +04:00
* pchan = chan ;
if ( ret )
return ret ;
/* create channel object */
2014-08-09 22:10:22 +04:00
args . pushbuf = chan - > push . ctxdma . handle ;
2012-07-20 02:17:34 +04:00
args . offset = chan - > push . vma . offset ;
do {
2014-08-09 22:10:22 +04:00
ret = nvif_object_new ( nvif_object ( device ) , handle , * oclass + + ,
& args , sizeof ( args ) , & chan - > object ) ;
2012-07-20 02:17:34 +04:00
if ( ret = = 0 )
return ret ;
} while ( ret & & * oclass ) ;
nouveau_channel_del ( pchan ) ;
return ret ;
}
static int
nouveau_channel_init ( struct nouveau_channel * chan , u32 vram , u32 gart )
{
2014-08-09 22:10:22 +04:00
struct nvif_device * device = chan - > device ;
struct nouveau_cli * cli = ( void * ) nvif_client ( & device - > base ) ;
2014-08-09 22:10:22 +04:00
struct nouveau_instmem * imem = nvkm_instmem ( device ) ;
struct nouveau_vmmgr * vmm = nvkm_vmmgr ( device ) ;
struct nouveau_fb * pfb = nvkm_fb ( device ) ;
2012-07-20 02:17:34 +04:00
struct nouveau_software_chan * swch ;
2012-10-08 08:29:16 +04:00
struct nv_dma_class args = { } ;
2012-07-20 02:17:34 +04:00
int ret , i ;
/* allocate dma objects to cover all allowed vram, and gart */
2014-08-09 22:10:22 +04:00
if ( device - > info . family < NV_DEVICE_INFO_V0_FERMI ) {
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 = 0 ;
2014-08-09 22:10:22 +04:00
args . limit = cli - > vm - > vmm - > limit - 1 ;
2012-07-20 02:17:34 +04:00
} else {
args . flags = NV_DMA_TARGET_VRAM | NV_DMA_ACCESS_RDWR ;
args . start = 0 ;
2013-03-04 07:01:21 +04:00
args . limit = pfb - > ram - > size - imem - > reserved - 1 ;
2012-07-20 02:17:34 +04:00
}
2014-08-09 22:10:22 +04:00
ret = nvif_object_init ( chan - > object , NULL , vram ,
NV_DMA_IN_MEMORY_CLASS , & args ,
sizeof ( args ) , & chan - > vram ) ;
2012-07-20 02:17:34 +04:00
if ( ret )
return ret ;
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 = 0 ;
2014-08-09 22:10:22 +04:00
args . limit = cli - > vm - > vmm - > limit - 1 ;
2012-07-20 02:17:34 +04:00
} else
if ( chan - > drm - > agp . stat = = ENABLED ) {
args . flags = NV_DMA_TARGET_AGP | NV_DMA_ACCESS_RDWR ;
args . start = chan - > drm - > agp . base ;
args . limit = chan - > drm - > agp . base +
chan - > drm - > agp . size - 1 ;
} else {
args . flags = NV_DMA_TARGET_VM | NV_DMA_ACCESS_RDWR ;
args . start = 0 ;
args . limit = vmm - > limit - 1 ;
}
2014-08-09 22:10:22 +04:00
ret = nvif_object_init ( chan - > object , NULL , gart ,
NV_DMA_IN_MEMORY_CLASS , & args ,
sizeof ( args ) , & chan - > gart ) ;
2012-07-20 02:17:34 +04:00
if ( ret )
return ret ;
}
/* initialise dma tracking parameters */
2014-08-09 22:10:22 +04:00
switch ( chan - > object - > oclass & 0x00ff ) {
2012-08-14 08:53:51 +04:00
case 0x006b :
2012-07-20 02:17:34 +04:00
case 0x006e :
chan - > user_put = 0x40 ;
chan - > user_get = 0x44 ;
chan - > dma . max = ( 0x10000 / 4 ) - 2 ;
break ;
default :
chan - > user_put = 0x40 ;
chan - > user_get = 0x44 ;
chan - > user_get_hi = 0x60 ;
chan - > dma . ib_base = 0x10000 / 4 ;
chan - > dma . ib_max = ( 0x02000 / 8 ) - 1 ;
chan - > dma . ib_put = 0 ;
chan - > dma . ib_free = chan - > dma . ib_max - chan - > dma . ib_put ;
chan - > dma . max = chan - > dma . ib_base ;
break ;
}
chan - > dma . put = 0 ;
chan - > dma . cur = chan - > dma . put ;
chan - > dma . free = chan - > dma . max - chan - > dma . cur ;
ret = RING_SPACE ( chan , NOUVEAU_DMA_SKIPS ) ;
if ( ret )
return ret ;
for ( i = 0 ; i < NOUVEAU_DMA_SKIPS ; i + + )
OUT_RING ( chan , 0x00000000 ) ;
2013-11-13 04:58:51 +04:00
/* allocate software object class (used for fences on <= nv05) */
2014-08-09 22:10:22 +04:00
if ( device - > info . family < NV_DEVICE_INFO_V0_CELSIUS ) {
2014-08-09 22:10:23 +04:00
ret = nvif_object_init ( chan - > object , NULL , 0x006e , 0x006e ,
2014-08-09 22:10:22 +04:00
NULL , 0 , & chan - > nvsw ) ;
2012-08-06 13:38:25 +04:00
if ( ret )
return ret ;
2012-07-20 02:17:34 +04:00
2014-08-09 22:10:22 +04:00
swch = ( void * ) nvkm_object ( & chan - > nvsw ) - > parent ;
2012-08-06 13:38:25 +04:00
swch - > flip = nouveau_flip_complete ;
swch - > flip_data = chan ;
2012-07-20 02:17:34 +04:00
ret = RING_SPACE ( chan , 2 ) ;
if ( ret )
return ret ;
BEGIN_NV04 ( chan , NvSubSw , 0x0000 , 1 ) ;
2014-08-09 22:10:23 +04:00
OUT_RING ( chan , chan - > nvsw . handle ) ;
2012-07-20 02:17:34 +04:00
FIRE_RING ( chan ) ;
}
/* initialise synchronisation */
return nouveau_fence ( chan - > drm ) - > context_new ( chan ) ;
}
int
2014-08-09 22:10:22 +04:00
nouveau_channel_new ( struct nouveau_drm * drm , struct nvif_device * device ,
u32 handle , u32 arg0 , u32 arg1 ,
2012-07-20 02:17:34 +04:00
struct nouveau_channel * * pchan )
{
2014-08-09 22:10:22 +04:00
struct nouveau_cli * cli = ( void * ) nvif_client ( & device - > base ) ;
2012-07-20 02:17:34 +04:00
int ret ;
2014-08-09 22:10:22 +04:00
ret = nouveau_channel_ind ( drm , device , handle , arg0 , pchan ) ;
2012-07-20 02:17:34 +04:00
if ( ret ) {
2014-08-09 22:10:22 +04:00
NV_PRINTK ( debug , cli , " ib channel create, %d \n " , ret ) ;
2014-08-09 22:10:22 +04:00
ret = nouveau_channel_dma ( drm , device , handle , pchan ) ;
2012-07-20 02:17:34 +04:00
if ( ret ) {
2014-08-09 22:10:22 +04:00
NV_PRINTK ( debug , cli , " dma channel create, %d \n " , ret ) ;
2012-07-20 02:17:34 +04:00
return ret ;
}
}
2012-08-06 13:38:25 +04:00
ret = nouveau_channel_init ( * pchan , arg0 , arg1 ) ;
2012-07-20 02:17:34 +04:00
if ( ret ) {
2014-08-09 22:10:22 +04:00
NV_PRINTK ( error , cli , " channel failed to initialise, %d \n " , ret ) ;
2012-07-20 02:17:34 +04:00
nouveau_channel_del ( pchan ) ;
return ret ;
}
return 0 ;
}