2010-10-19 07:05:51 +04: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_dma.h"
# include "nouveau_ramht.h"
2011-02-01 03:07:32 +03:00
# include "nv50_display.h"
2010-10-19 07:05:51 +04:00
static void
2010-10-19 08:18:06 +04:00
nv50_evo_channel_del ( struct nouveau_channel * * pevo )
2010-10-19 07:05:51 +04:00
{
2010-10-19 08:18:06 +04:00
struct nouveau_channel * evo = * pevo ;
2010-10-19 07:05:51 +04:00
2010-10-19 08:18:06 +04:00
if ( ! evo )
2010-10-19 07:05:51 +04:00
return ;
2010-10-19 08:18:06 +04:00
* pevo = NULL ;
2010-10-19 07:05:51 +04:00
2011-06-06 05:34:27 +04:00
nouveau_ramht_ref ( NULL , & evo - > ramht , evo ) ;
2010-10-19 08:18:06 +04:00
nouveau_gpuobj_channel_takedown ( evo ) ;
nouveau_bo_unmap ( evo - > pushbuf_bo ) ;
nouveau_bo_ref ( NULL , & evo - > pushbuf_bo ) ;
2010-10-19 07:05:51 +04:00
2010-10-19 08:18:06 +04:00
if ( evo - > user )
iounmap ( evo - > user ) ;
kfree ( evo ) ;
2010-10-19 07:05:51 +04:00
}
2011-02-07 06:08:16 +03:00
void
nv50_evo_dmaobj_init ( struct nouveau_gpuobj * obj , u32 memtype , u64 base , u64 size )
{
struct drm_nouveau_private * dev_priv = obj - > dev - > dev_private ;
u32 flags5 ;
if ( dev_priv - > chipset < 0xc0 ) {
/* not supported on 0x50, specified in format mthd */
if ( dev_priv - > chipset = = 0x50 )
memtype = 0 ;
flags5 = 0x00010000 ;
} else {
if ( memtype & 0x80000000 )
flags5 = 0x00000000 ; /* large pages */
else
flags5 = 0x00020000 ;
}
nv50_gpuobj_dma_init ( obj , 0 , 0x3d , base , size , NV_MEM_TARGET_VRAM ,
NV_MEM_ACCESS_RW , ( memtype > > 8 ) & 0xff , 0 ) ;
nv_wo32 ( obj , 0x14 , flags5 ) ;
dev_priv - > engine . instmem . flush ( obj - > dev ) ;
}
2010-10-19 07:05:51 +04:00
int
2011-02-07 06:08:16 +03:00
nv50_evo_dmaobj_new ( struct nouveau_channel * evo , u32 handle , u32 memtype ,
u64 base , u64 size , struct nouveau_gpuobj * * pobj )
2010-10-19 07:05:51 +04:00
{
2011-02-01 03:07:32 +03:00
struct nv50_display * disp = nv50_display ( evo - > dev ) ;
2010-10-19 07:05:51 +04:00
struct nouveau_gpuobj * obj = NULL ;
int ret ;
2011-02-01 03:24:41 +03:00
ret = nouveau_gpuobj_new ( evo - > dev , disp - > master , 6 * 4 , 32 , 0 , & obj ) ;
2010-10-19 07:05:51 +04:00
if ( ret )
return ret ;
obj - > engine = NVOBJ_ENGINE_DISPLAY ;
2011-02-07 06:08:16 +03:00
nv50_evo_dmaobj_init ( obj , memtype , base , size ) ;
2010-10-19 07:05:51 +04:00
2011-02-07 06:08:16 +03:00
ret = nouveau_ramht_insert ( evo , handle , obj ) ;
if ( ret )
goto out ;
2010-10-19 07:05:51 +04:00
2011-02-07 06:08:16 +03:00
if ( pobj )
nouveau_gpuobj_ref ( obj , pobj ) ;
out :
nouveau_gpuobj_ref ( NULL , & obj ) ;
return ret ;
2010-10-19 07:05:51 +04:00
}
static int
2011-02-01 03:39:45 +03:00
nv50_evo_channel_new ( struct drm_device * dev , int chid ,
struct nouveau_channel * * pevo )
2010-10-19 07:05:51 +04:00
{
2011-02-01 03:07:32 +03:00
struct nv50_display * disp = nv50_display ( dev ) ;
2010-10-19 08:18:06 +04:00
struct nouveau_channel * evo ;
2010-10-19 07:05:51 +04:00
int ret ;
2010-10-19 08:18:06 +04:00
evo = kzalloc ( sizeof ( struct nouveau_channel ) , GFP_KERNEL ) ;
if ( ! evo )
2010-10-19 07:05:51 +04:00
return - ENOMEM ;
2010-10-19 08:18:06 +04:00
* pevo = evo ;
2010-10-19 07:05:51 +04:00
2011-02-01 03:39:45 +03:00
evo - > id = chid ;
2010-10-19 08:18:06 +04:00
evo - > dev = dev ;
evo - > user_get = 4 ;
evo - > user_put = 0 ;
2010-10-19 07:05:51 +04:00
ret = nouveau_bo_new ( dev , NULL , 4096 , 0 , TTM_PL_FLAG_VRAM , 0 , 0 ,
2011-02-16 01:41:56 +03:00
& evo - > pushbuf_bo ) ;
2010-10-19 07:05:51 +04:00
if ( ret = = 0 )
2010-10-19 08:18:06 +04:00
ret = nouveau_bo_pin ( evo - > pushbuf_bo , TTM_PL_FLAG_VRAM ) ;
2010-10-19 07:05:51 +04:00
if ( ret ) {
NV_ERROR ( dev , " Error creating EVO DMA push buffer: %d \n " , ret ) ;
2010-10-19 08:18:06 +04:00
nv50_evo_channel_del ( pevo ) ;
2010-10-19 07:05:51 +04:00
return ret ;
}
2010-10-19 08:18:06 +04:00
ret = nouveau_bo_map ( evo - > pushbuf_bo ) ;
2010-10-19 07:05:51 +04:00
if ( ret ) {
NV_ERROR ( dev , " Error mapping EVO DMA push buffer: %d \n " , ret ) ;
2010-10-19 08:18:06 +04:00
nv50_evo_channel_del ( pevo ) ;
2010-10-19 07:05:51 +04:00
return ret ;
}
2010-10-19 08:18:06 +04:00
evo - > user = ioremap ( pci_resource_start ( dev - > pdev , 0 ) +
NV50_PDISPLAY_USER ( evo - > id ) , PAGE_SIZE ) ;
if ( ! evo - > user ) {
2010-10-19 07:05:51 +04:00
NV_ERROR ( dev , " Error mapping EVO control regs. \n " ) ;
2010-10-19 08:18:06 +04:00
nv50_evo_channel_del ( pevo ) ;
2010-10-19 07:05:51 +04:00
return - ENOMEM ;
}
2010-10-19 08:18:06 +04:00
/* bind primary evo channel's ramht to the channel */
2011-02-01 03:24:41 +03:00
if ( disp - > master & & evo ! = disp - > master )
nouveau_ramht_ref ( disp - > master - > ramht , & evo - > ramht , NULL ) ;
2010-10-19 08:18:06 +04:00
2010-10-19 07:05:51 +04:00
return 0 ;
}
static int
nv50_evo_channel_init ( struct nouveau_channel * evo )
{
struct drm_device * dev = evo - > dev ;
2010-10-19 08:18:06 +04:00
int id = evo - > id , ret , i ;
2011-06-07 05:24:14 +04:00
u64 pushbuf = evo - > pushbuf_bo - > bo . offset ;
2010-10-19 07:05:51 +04:00
u32 tmp ;
2010-10-19 12:01:41 +04:00
tmp = nv_rd32 ( dev , NV50_PDISPLAY_EVO_CTRL ( id ) ) ;
if ( ( tmp & 0x009f0000 ) = = 0x00020000 )
nv_wr32 ( dev , NV50_PDISPLAY_EVO_CTRL ( id ) , tmp | 0x00800000 ) ;
2010-10-19 07:05:51 +04:00
2010-10-19 12:01:41 +04:00
tmp = nv_rd32 ( dev , NV50_PDISPLAY_EVO_CTRL ( id ) ) ;
if ( ( tmp & 0x003f0000 ) = = 0x00030000 )
nv_wr32 ( dev , NV50_PDISPLAY_EVO_CTRL ( id ) , tmp | 0x00600000 ) ;
2010-10-19 07:05:51 +04:00
/* initialise fifo */
2010-10-19 12:01:41 +04:00
nv_wr32 ( dev , NV50_PDISPLAY_EVO_DMA_CB ( id ) , pushbuf > > 8 |
NV50_PDISPLAY_EVO_DMA_CB_LOCATION_VRAM |
NV50_PDISPLAY_EVO_DMA_CB_VALID ) ;
2010-10-19 08:18:06 +04:00
nv_wr32 ( dev , NV50_PDISPLAY_EVO_UNK2 ( id ) , 0x00010000 ) ;
nv_wr32 ( dev , NV50_PDISPLAY_EVO_HASH_TAG ( id ) , id ) ;
2010-10-19 12:01:41 +04:00
nv_mask ( dev , NV50_PDISPLAY_EVO_CTRL ( id ) , NV50_PDISPLAY_EVO_CTRL_DMA ,
NV50_PDISPLAY_EVO_CTRL_DMA_ENABLED ) ;
nv_wr32 ( dev , NV50_PDISPLAY_USER_PUT ( id ) , 0x00000000 ) ;
nv_wr32 ( dev , NV50_PDISPLAY_EVO_CTRL ( id ) , 0x01000003 |
NV50_PDISPLAY_EVO_CTRL_DMA_ENABLED ) ;
2010-10-19 08:18:06 +04:00
if ( ! nv_wait ( dev , NV50_PDISPLAY_EVO_CTRL ( id ) , 0x80000000 , 0x00000000 ) ) {
2010-10-19 12:01:41 +04:00
NV_ERROR ( dev , " EvoCh %d init timeout: 0x%08x \n " , id ,
2010-10-19 08:18:06 +04:00
nv_rd32 ( dev , NV50_PDISPLAY_EVO_CTRL ( id ) ) ) ;
2010-10-19 07:05:51 +04:00
return - EBUSY ;
}
/* enable error reporting on the channel */
2010-10-19 08:18:06 +04:00
nv_mask ( dev , 0x610028 , 0x00000000 , 0x00010001 < < id ) ;
2010-10-19 07:05:51 +04:00
evo - > dma . max = ( 4096 / 4 ) - 2 ;
2011-03-21 14:41:47 +03:00
evo - > dma . max & = ~ 7 ;
2010-10-19 07:05:51 +04:00
evo - > dma . put = 0 ;
evo - > dma . cur = evo - > dma . put ;
evo - > dma . free = evo - > dma . max - evo - > dma . cur ;
ret = RING_SPACE ( evo , NOUVEAU_DMA_SKIPS ) ;
if ( ret )
return ret ;
for ( i = 0 ; i < NOUVEAU_DMA_SKIPS ; i + + )
OUT_RING ( evo , 0 ) ;
return 0 ;
}
static void
nv50_evo_channel_fini ( struct nouveau_channel * evo )
{
struct drm_device * dev = evo - > dev ;
2010-10-19 12:01:41 +04:00
int id = evo - > id ;
nv_mask ( dev , 0x610028 , 0x00010001 < < id , 0x00000000 ) ;
nv_mask ( dev , NV50_PDISPLAY_EVO_CTRL ( id ) , 0x00001010 , 0x00001000 ) ;
nv_wr32 ( dev , NV50_PDISPLAY_INTR_0 , ( 1 < < id ) ) ;
nv_mask ( dev , NV50_PDISPLAY_EVO_CTRL ( id ) , 0x00000003 , 0x00000000 ) ;
if ( ! nv_wait ( dev , NV50_PDISPLAY_EVO_CTRL ( id ) , 0x001e0000 , 0x00000000 ) ) {
NV_ERROR ( dev , " EvoCh %d takedown timeout: 0x%08x \n " , id ,
nv_rd32 ( dev , NV50_PDISPLAY_EVO_CTRL ( id ) ) ) ;
2010-10-19 08:18:06 +04:00
}
}
2011-02-01 03:59:07 +03:00
static void
nv50_evo_destroy ( struct drm_device * dev )
{
struct nv50_display * disp = nv50_display ( dev ) ;
2011-02-07 06:29:23 +03:00
int i ;
for ( i = 0 ; i < 2 ; i + + ) {
if ( disp - > crtc [ i ] . sem . bo ) {
nouveau_bo_unmap ( disp - > crtc [ i ] . sem . bo ) ;
nouveau_bo_ref ( NULL , & disp - > crtc [ i ] . sem . bo ) ;
}
nv50_evo_channel_del ( & disp - > crtc [ i ] . sync ) ;
}
2011-02-03 08:46:14 +03:00
nouveau_gpuobj_ref ( NULL , & disp - > ntfy ) ;
2011-02-01 03:59:07 +03:00
nv50_evo_channel_del ( & disp - > master ) ;
}
2010-10-19 08:18:06 +04:00
static int
nv50_evo_create ( struct drm_device * dev )
{
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
2011-02-01 03:07:32 +03:00
struct nv50_display * disp = nv50_display ( dev ) ;
2010-10-19 08:18:06 +04:00
struct nouveau_gpuobj * ramht = NULL ;
struct nouveau_channel * evo ;
2011-02-07 06:29:23 +03:00
int ret , i , j ;
2010-10-19 08:18:06 +04:00
/* create primary evo channel, the one we use for modesetting
* purporses
*/
2011-02-01 03:39:45 +03:00
ret = nv50_evo_channel_new ( dev , 0 , & disp - > master ) ;
2010-10-19 08:18:06 +04:00
if ( ret )
return ret ;
2011-02-01 03:24:41 +03:00
evo = disp - > master ;
2010-10-19 08:18:06 +04:00
/* setup object management on it, any other evo channel will
* use this also as there ' s no per - channel support on the
* hardware
*/
2010-10-20 09:35:28 +04:00
ret = nouveau_gpuobj_new ( dev , NULL , 32768 , 65536 ,
2010-10-19 08:18:06 +04:00
NVOBJ_FLAG_ZERO_ALLOC , & evo - > ramin ) ;
if ( ret ) {
NV_ERROR ( dev , " Error allocating EVO channel memory: %d \n " , ret ) ;
2011-02-01 03:59:07 +03:00
goto err ;
2010-10-19 08:18:06 +04:00
}
ret = drm_mm_init ( & evo - > ramin_heap , 0 , 32768 ) ;
if ( ret ) {
NV_ERROR ( dev , " Error initialising EVO PRAMIN heap: %d \n " , ret ) ;
2011-02-01 03:59:07 +03:00
goto err ;
2010-10-19 08:18:06 +04:00
}
ret = nouveau_gpuobj_new ( dev , evo , 4096 , 16 , 0 , & ramht ) ;
if ( ret ) {
NV_ERROR ( dev , " Unable to allocate EVO RAMHT: %d \n " , ret ) ;
2011-02-01 03:59:07 +03:00
goto err ;
2010-10-19 08:18:06 +04:00
}
ret = nouveau_ramht_new ( dev , ramht , & evo - > ramht ) ;
nouveau_gpuobj_ref ( NULL , & ramht ) ;
2011-02-01 03:59:07 +03:00
if ( ret )
goto err ;
2010-10-19 08:18:06 +04:00
2011-02-03 08:46:14 +03:00
/* not sure exactly what this is..
*
* the first dword of the structure is used by nvidia to wait on
* full completion of an EVO " update " command .
*
* method 0x8c on the master evo channel will fill a lot more of
* this structure with some undefined info
*/
ret = nouveau_gpuobj_new ( dev , disp - > master , 0x1000 , 0 ,
NVOBJ_FLAG_ZERO_ALLOC , & disp - > ntfy ) ;
if ( ret )
goto err ;
2011-02-07 06:08:16 +03:00
ret = nv50_evo_dmaobj_new ( disp - > master , NvEvoSync , 0x0000 ,
disp - > ntfy - > vinst , disp - > ntfy - > size , NULL ) ;
2011-02-03 08:46:14 +03:00
if ( ret )
goto err ;
2010-10-19 08:18:06 +04:00
/* create some default objects for the scanout memtypes we support */
2011-02-07 06:08:16 +03:00
ret = nv50_evo_dmaobj_new ( disp - > master , NvEvoVRAM , 0x0000 ,
0 , dev_priv - > vram_size , NULL ) ;
if ( ret )
goto err ;
2010-10-19 08:18:06 +04:00
2011-02-07 06:08:16 +03:00
ret = nv50_evo_dmaobj_new ( disp - > master , NvEvoVRAM_LP , 0x80000000 ,
0 , dev_priv - > vram_size , NULL ) ;
if ( ret )
goto err ;
2010-10-19 08:18:06 +04:00
2011-02-07 06:08:16 +03:00
ret = nv50_evo_dmaobj_new ( disp - > master , NvEvoFB32 , 0x80000000 |
( dev_priv - > chipset < 0xc0 ? 0x7a00 : 0xfe00 ) ,
0 , dev_priv - > vram_size , NULL ) ;
if ( ret )
goto err ;
2010-12-08 04:19:30 +03:00
2011-02-07 06:08:16 +03:00
ret = nv50_evo_dmaobj_new ( disp - > master , NvEvoFB16 , 0x80000000 |
( dev_priv - > chipset < 0xc0 ? 0x7000 : 0xfe00 ) ,
0 , dev_priv - > vram_size , NULL ) ;
if ( ret )
goto err ;
2010-10-19 08:18:06 +04:00
2011-02-07 06:29:23 +03:00
/* create "display sync" channels and other structures we need
* to implement page flipping
*/
for ( i = 0 ; i < 2 ; i + + ) {
struct nv50_display_crtc * dispc = & disp - > crtc [ i ] ;
u64 offset ;
ret = nv50_evo_channel_new ( dev , 1 + i , & dispc - > sync ) ;
if ( ret )
goto err ;
ret = nouveau_bo_new ( dev , NULL , 4096 , 0x1000 , TTM_PL_FLAG_VRAM ,
2011-02-16 01:41:56 +03:00
0 , 0x0000 , & dispc - > sem . bo ) ;
2011-02-07 06:29:23 +03:00
if ( ! ret ) {
ret = nouveau_bo_pin ( dispc - > sem . bo , TTM_PL_FLAG_VRAM ) ;
if ( ! ret )
ret = nouveau_bo_map ( dispc - > sem . bo ) ;
if ( ret )
nouveau_bo_ref ( NULL , & dispc - > sem . bo ) ;
2011-06-07 05:24:14 +04:00
offset = dispc - > sem . bo - > bo . offset ;
2011-02-07 06:29:23 +03:00
}
if ( ret )
goto err ;
ret = nv50_evo_dmaobj_new ( dispc - > sync , NvEvoSync , 0x0000 ,
offset , 4096 , NULL ) ;
if ( ret )
goto err ;
ret = nv50_evo_dmaobj_new ( dispc - > sync , NvEvoVRAM_LP , 0x80000000 ,
0 , dev_priv - > vram_size , NULL ) ;
if ( ret )
goto err ;
ret = nv50_evo_dmaobj_new ( dispc - > sync , NvEvoFB32 , 0x80000000 |
( dev_priv - > chipset < 0xc0 ?
0x7a00 : 0xfe00 ) ,
0 , dev_priv - > vram_size , NULL ) ;
if ( ret )
goto err ;
ret = nv50_evo_dmaobj_new ( dispc - > sync , NvEvoFB16 , 0x80000000 |
( dev_priv - > chipset < 0xc0 ?
0x7000 : 0xfe00 ) ,
0 , dev_priv - > vram_size , NULL ) ;
if ( ret )
goto err ;
for ( j = 0 ; j < 4096 ; j + = 4 )
nouveau_bo_wr32 ( dispc - > sem . bo , j / 4 , 0x74b1e000 ) ;
dispc - > sem . offset = 0 ;
}
2010-10-19 08:18:06 +04:00
return 0 ;
2011-02-01 03:59:07 +03:00
err :
nv50_evo_destroy ( dev ) ;
return ret ;
2010-10-19 07:05:51 +04:00
}
int
nv50_evo_init ( struct drm_device * dev )
{
2011-02-01 03:07:32 +03:00
struct nv50_display * disp = nv50_display ( dev ) ;
2011-02-07 06:29:23 +03:00
int ret , i ;
2010-10-19 07:05:51 +04:00
2011-02-01 03:24:41 +03:00
if ( ! disp - > master ) {
2010-10-19 08:18:06 +04:00
ret = nv50_evo_create ( dev ) ;
2010-10-19 07:05:51 +04:00
if ( ret )
return ret ;
}
2011-02-07 06:29:23 +03:00
ret = nv50_evo_channel_init ( disp - > master ) ;
if ( ret )
return ret ;
for ( i = 0 ; i < 2 ; i + + ) {
ret = nv50_evo_channel_init ( disp - > crtc [ i ] . sync ) ;
if ( ret )
return ret ;
}
return 0 ;
2010-10-19 07:05:51 +04:00
}
void
nv50_evo_fini ( struct drm_device * dev )
{
2011-02-01 03:07:32 +03:00
struct nv50_display * disp = nv50_display ( dev ) ;
2011-02-07 06:29:23 +03:00
int i ;
for ( i = 0 ; i < 2 ; i + + ) {
if ( disp - > crtc [ i ] . sync )
nv50_evo_channel_fini ( disp - > crtc [ i ] . sync ) ;
}
2010-10-19 07:05:51 +04:00
2011-02-01 03:59:07 +03:00
if ( disp - > master )
2011-02-01 03:24:41 +03:00
nv50_evo_channel_fini ( disp - > master ) ;
2011-02-07 06:29:23 +03:00
2011-02-01 03:59:07 +03:00
nv50_evo_destroy ( dev ) ;
2010-10-19 07:05:51 +04:00
}