2005-06-28 22:48:56 +10:00
/*
* Copyright 1998 - 2003 VIA Technologies , Inc . All Rights Reserved .
* Copyright 2001 - 2003 S3 Graphics , Inc . 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 , sub license ,
* 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 NON - INFRINGEMENT . IN NO EVENT SHALL
* VIA , S3 GRAPHICS , 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 "via_drm.h"
# include "via_drv.h"
# include "via_ds.h"
# include "via_mm.h"
# define MAX_CONTEXT 100
typedef struct {
int used ;
int context ;
set_t * sets [ 2 ] ; /* 0 for frame buffer, 1 for AGP , 2 for System */
} via_context_t ;
static via_context_t global_ppriv [ MAX_CONTEXT ] ;
static int via_agp_alloc ( drm_via_mem_t * mem ) ;
static int via_agp_free ( drm_via_mem_t * mem ) ;
static int via_fb_alloc ( drm_via_mem_t * mem ) ;
static int via_fb_free ( drm_via_mem_t * mem ) ;
2006-01-02 14:26:20 +11:00
static int add_alloc_set ( int context , int type , unsigned long val )
2005-06-28 22:48:56 +10:00
{
int i , retval = 0 ;
for ( i = 0 ; i < MAX_CONTEXT ; i + + ) {
if ( global_ppriv [ i ] . used & & global_ppriv [ i ] . context = = context ) {
retval = via_setAdd ( global_ppriv [ i ] . sets [ type ] , val ) ;
break ;
}
}
return retval ;
}
2006-01-02 14:26:20 +11:00
static int del_alloc_set ( int context , int type , unsigned long val )
2005-06-28 22:48:56 +10:00
{
int i , retval = 0 ;
for ( i = 0 ; i < MAX_CONTEXT ; i + + )
if ( global_ppriv [ i ] . used & & global_ppriv [ i ] . context = = context ) {
retval = via_setDel ( global_ppriv [ i ] . sets [ type ] , val ) ;
break ;
}
return retval ;
}
/* agp memory management */
static memHeap_t * AgpHeap = NULL ;
int via_agp_init ( DRM_IOCTL_ARGS )
{
drm_via_agp_t agp ;
2005-07-27 11:43:56 -07:00
DRM_COPY_FROM_USER_IOCTL ( agp , ( drm_via_agp_t __user * ) data ,
sizeof ( agp ) ) ;
2005-06-28 22:48:56 +10:00
AgpHeap = via_mmInit ( agp . offset , agp . size ) ;
2005-09-25 14:28:13 +10:00
DRM_DEBUG ( " offset = %lu, size = %lu " , ( unsigned long ) agp . offset ,
( unsigned long ) agp . size ) ;
2005-06-28 22:48:56 +10:00
return 0 ;
}
/* fb memory management */
static memHeap_t * FBHeap = NULL ;
int via_fb_init ( DRM_IOCTL_ARGS )
{
drm_via_fb_t fb ;
2005-07-27 11:43:56 -07:00
DRM_COPY_FROM_USER_IOCTL ( fb , ( drm_via_fb_t __user * ) data , sizeof ( fb ) ) ;
2005-06-28 22:48:56 +10:00
FBHeap = via_mmInit ( fb . offset , fb . size ) ;
2005-09-25 14:28:13 +10:00
DRM_DEBUG ( " offset = %lu, size = %lu " , ( unsigned long ) fb . offset ,
( unsigned long ) fb . size ) ;
2005-06-28 22:48:56 +10:00
return 0 ;
}
int via_init_context ( struct drm_device * dev , int context )
{
int i ;
for ( i = 0 ; i < MAX_CONTEXT ; i + + )
if ( global_ppriv [ i ] . used & &
( global_ppriv [ i ] . context = = context ) )
break ;
if ( i > = MAX_CONTEXT ) {
for ( i = 0 ; i < MAX_CONTEXT ; i + + ) {
if ( ! global_ppriv [ i ] . used ) {
global_ppriv [ i ] . context = context ;
global_ppriv [ i ] . used = 1 ;
global_ppriv [ i ] . sets [ 0 ] = via_setInit ( ) ;
global_ppriv [ i ] . sets [ 1 ] = via_setInit ( ) ;
DRM_DEBUG ( " init allocation set, socket=%d, "
" context = %d \n " , i , context ) ;
break ;
}
}
if ( ( i > = MAX_CONTEXT ) | | ( global_ppriv [ i ] . sets [ 0 ] = = NULL ) | |
( global_ppriv [ i ] . sets [ 1 ] = = NULL ) ) {
return 0 ;
}
}
return 1 ;
}
int via_final_context ( struct drm_device * dev , int context )
2005-09-25 14:28:13 +10:00
{
int i ;
2005-06-28 22:48:56 +10:00
drm_via_private_t * dev_priv = ( drm_via_private_t * ) dev - > dev_private ;
for ( i = 0 ; i < MAX_CONTEXT ; i + + )
if ( global_ppriv [ i ] . used & &
( global_ppriv [ i ] . context = = context ) )
break ;
if ( i < MAX_CONTEXT ) {
set_t * set ;
ITEM_TYPE item ;
int retval ;
DRM_DEBUG ( " find socket %d, context = %d \n " , i , context ) ;
/* Video Memory */
set = global_ppriv [ i ] . sets [ 0 ] ;
retval = via_setFirst ( set , & item ) ;
while ( retval ) {
DRM_DEBUG ( " free video memory 0x%lx \n " , item ) ;
via_mmFreeMem ( ( PMemBlock ) item ) ;
retval = via_setNext ( set , & item ) ;
}
via_setDestroy ( set ) ;
/* AGP Memory */
set = global_ppriv [ i ] . sets [ 1 ] ;
retval = via_setFirst ( set , & item ) ;
while ( retval ) {
DRM_DEBUG ( " free agp memory 0x%lx \n " , item ) ;
via_mmFreeMem ( ( PMemBlock ) item ) ;
retval = via_setNext ( set , & item ) ;
}
via_setDestroy ( set ) ;
global_ppriv [ i ] . used = 0 ;
}
2005-09-25 14:28:13 +10:00
via_release_futex ( dev_priv , context ) ;
2005-06-28 22:48:56 +10:00
# if defined(__linux__)
/* Linux specific until context tracking code gets ported to BSD */
/* Last context, perform cleanup */
if ( dev - > ctx_count = = 1 & & dev - > dev_private ) {
2005-09-25 14:28:13 +10:00
DRM_DEBUG ( " Last Context \n " ) ;
2005-06-28 22:48:56 +10:00
if ( dev - > irq )
drm_irq_uninstall ( dev ) ;
via_cleanup_futex ( dev_priv ) ;
via_do_cleanup_map ( dev ) ;
}
# endif
return 1 ;
}
int via_mem_alloc ( DRM_IOCTL_ARGS )
{
drm_via_mem_t mem ;
2005-07-27 11:43:56 -07:00
DRM_COPY_FROM_USER_IOCTL ( mem , ( drm_via_mem_t __user * ) data ,
sizeof ( mem ) ) ;
2005-06-28 22:48:56 +10:00
switch ( mem . type ) {
2005-11-12 21:52:46 +11:00
case VIA_MEM_VIDEO :
2005-06-28 22:48:56 +10:00
if ( via_fb_alloc ( & mem ) < 0 )
return - EFAULT ;
2005-07-27 11:43:56 -07:00
DRM_COPY_TO_USER_IOCTL ( ( drm_via_mem_t __user * ) data , mem ,
2005-06-28 22:48:56 +10:00
sizeof ( mem ) ) ;
return 0 ;
2005-11-12 21:52:46 +11:00
case VIA_MEM_AGP :
2005-06-28 22:48:56 +10:00
if ( via_agp_alloc ( & mem ) < 0 )
return - EFAULT ;
2005-07-27 11:43:56 -07:00
DRM_COPY_TO_USER_IOCTL ( ( drm_via_mem_t __user * ) data , mem ,
2005-06-28 22:48:56 +10:00
sizeof ( mem ) ) ;
return 0 ;
}
return - EFAULT ;
}
static int via_fb_alloc ( drm_via_mem_t * mem )
{
drm_via_mm_t fb ;
PMemBlock block ;
int retval = 0 ;
if ( ! FBHeap )
return - 1 ;
fb . size = mem - > size ;
fb . context = mem - > context ;
block = via_mmAllocMem ( FBHeap , fb . size , 5 , 0 ) ;
if ( block ) {
fb . offset = block - > ofs ;
fb . free = ( unsigned long ) block ;
2005-11-12 21:52:46 +11:00
if ( ! add_alloc_set ( fb . context , VIA_MEM_VIDEO , fb . free ) ) {
2005-06-28 22:48:56 +10:00
DRM_DEBUG ( " adding to allocation set fails \n " ) ;
via_mmFreeMem ( ( PMemBlock ) fb . free ) ;
retval = - 1 ;
}
} else {
fb . offset = 0 ;
fb . size = 0 ;
fb . free = 0 ;
retval = - 1 ;
}
mem - > offset = fb . offset ;
mem - > index = fb . free ;
DRM_DEBUG ( " alloc fb, size = %d, offset = %d \n " , fb . size ,
( int ) fb . offset ) ;
return retval ;
}
static int via_agp_alloc ( drm_via_mem_t * mem )
{
drm_via_mm_t agp ;
PMemBlock block ;
int retval = 0 ;
if ( ! AgpHeap )
return - 1 ;
agp . size = mem - > size ;
agp . context = mem - > context ;
block = via_mmAllocMem ( AgpHeap , agp . size , 5 , 0 ) ;
if ( block ) {
agp . offset = block - > ofs ;
agp . free = ( unsigned long ) block ;
2005-11-12 21:52:46 +11:00
if ( ! add_alloc_set ( agp . context , VIA_MEM_AGP , agp . free ) ) {
2005-06-28 22:48:56 +10:00
DRM_DEBUG ( " adding to allocation set fails \n " ) ;
via_mmFreeMem ( ( PMemBlock ) agp . free ) ;
retval = - 1 ;
}
} else {
agp . offset = 0 ;
agp . size = 0 ;
agp . free = 0 ;
}
mem - > offset = agp . offset ;
mem - > index = agp . free ;
DRM_DEBUG ( " alloc agp, size = %d, offset = %d \n " , agp . size ,
( unsigned int ) agp . offset ) ;
return retval ;
}
int via_mem_free ( DRM_IOCTL_ARGS )
{
drm_via_mem_t mem ;
2005-07-27 11:43:56 -07:00
DRM_COPY_FROM_USER_IOCTL ( mem , ( drm_via_mem_t __user * ) data ,
sizeof ( mem ) ) ;
2005-06-28 22:48:56 +10:00
switch ( mem . type ) {
2005-11-12 21:52:46 +11:00
case VIA_MEM_VIDEO :
2005-06-28 22:48:56 +10:00
if ( via_fb_free ( & mem ) = = 0 )
return 0 ;
break ;
2005-11-12 21:52:46 +11:00
case VIA_MEM_AGP :
2005-06-28 22:48:56 +10:00
if ( via_agp_free ( & mem ) = = 0 )
return 0 ;
break ;
}
return - EFAULT ;
}
static int via_fb_free ( drm_via_mem_t * mem )
{
drm_via_mm_t fb ;
int retval = 0 ;
if ( ! FBHeap ) {
return - 1 ;
}
fb . free = mem - > index ;
fb . context = mem - > context ;
if ( ! fb . free ) {
return - 1 ;
}
via_mmFreeMem ( ( PMemBlock ) fb . free ) ;
2005-11-12 21:52:46 +11:00
if ( ! del_alloc_set ( fb . context , VIA_MEM_VIDEO , fb . free ) ) {
2005-06-28 22:48:56 +10:00
retval = - 1 ;
}
DRM_DEBUG ( " free fb, free = %ld \n " , fb . free ) ;
return retval ;
}
static int via_agp_free ( drm_via_mem_t * mem )
{
drm_via_mm_t agp ;
int retval = 0 ;
agp . free = mem - > index ;
agp . context = mem - > context ;
if ( ! agp . free )
return - 1 ;
via_mmFreeMem ( ( PMemBlock ) agp . free ) ;
2005-11-12 21:52:46 +11:00
if ( ! del_alloc_set ( agp . context , VIA_MEM_AGP , agp . free ) ) {
2005-06-28 22:48:56 +10:00
retval = - 1 ;
}
DRM_DEBUG ( " free agp, free = %ld \n " , agp . free ) ;
return retval ;
}