2005-04-17 02:20:36 +04:00
/* $Id: ffb_drv.c,v 1.16 2001/10/18 16:00:24 davem Exp $
* ffb_drv . c : Creator / Creator3D direct rendering driver .
*
* Copyright ( C ) 2000 David S . Miller ( davem @ redhat . com )
*/
# include <linux/config.h>
# include "ffb.h"
# include "drmP.h"
# include "ffb_drv.h"
# include <linux/sched.h>
# include <linux/smp_lock.h>
# include <asm/shmparam.h>
# include <asm/oplib.h>
# include <asm/upa.h>
# define DRIVER_AUTHOR "David S. Miller"
# define DRIVER_NAME "ffb"
# define DRIVER_DESC "Creator / Creator3D"
# define DRIVER_DATE "20000517"
# define DRIVER_MAJOR 0
# define DRIVER_MINOR 0
# define DRIVER_PATCHLEVEL 1
typedef struct _ffb_position_t {
int node ;
int root ;
} ffb_position_t ;
static ffb_position_t * ffb_position ;
static void get_ffb_type ( ffb_dev_priv_t * ffb_priv , int instance )
{
volatile unsigned char * strap_bits ;
unsigned char val ;
strap_bits = ( volatile unsigned char * )
( ffb_priv - > card_phys_base + 0x00200000UL ) ;
/* Don't ask, you have to read the value twice for whatever
* reason to get correct contents .
*/
val = upa_readb ( strap_bits ) ;
val = upa_readb ( strap_bits ) ;
switch ( val & 0x78 ) {
case ( 0x0 < < 5 ) | ( 0x0 < < 3 ) :
ffb_priv - > ffb_type = ffb1_prototype ;
printk ( " ffb%d: Detected FFB1 pre-FCS prototype \n " , instance ) ;
break ;
case ( 0x0 < < 5 ) | ( 0x1 < < 3 ) :
ffb_priv - > ffb_type = ffb1_standard ;
printk ( " ffb%d: Detected FFB1 \n " , instance ) ;
break ;
case ( 0x0 < < 5 ) | ( 0x3 < < 3 ) :
ffb_priv - > ffb_type = ffb1_speedsort ;
printk ( " ffb%d: Detected FFB1-SpeedSort \n " , instance ) ;
break ;
case ( 0x1 < < 5 ) | ( 0x0 < < 3 ) :
ffb_priv - > ffb_type = ffb2_prototype ;
printk ( " ffb%d: Detected FFB2/vertical pre-FCS prototype \n " , instance ) ;
break ;
case ( 0x1 < < 5 ) | ( 0x1 < < 3 ) :
ffb_priv - > ffb_type = ffb2_vertical ;
printk ( " ffb%d: Detected FFB2/vertical \n " , instance ) ;
break ;
case ( 0x1 < < 5 ) | ( 0x2 < < 3 ) :
ffb_priv - > ffb_type = ffb2_vertical_plus ;
printk ( " ffb%d: Detected FFB2+/vertical \n " , instance ) ;
break ;
case ( 0x2 < < 5 ) | ( 0x0 < < 3 ) :
ffb_priv - > ffb_type = ffb2_horizontal ;
printk ( " ffb%d: Detected FFB2/horizontal \n " , instance ) ;
break ;
case ( 0x2 < < 5 ) | ( 0x2 < < 3 ) :
ffb_priv - > ffb_type = ffb2_horizontal ;
printk ( " ffb%d: Detected FFB2+/horizontal \n " , instance ) ;
break ;
default :
ffb_priv - > ffb_type = ffb2_vertical ;
printk ( " ffb%d: Unknown boardID[%08x], assuming FFB2 \n " , instance , val ) ;
break ;
} ;
}
static void ffb_apply_upa_parent_ranges ( int parent ,
struct linux_prom64_registers * regs )
{
struct linux_prom64_ranges ranges [ PROMREG_MAX ] ;
char name [ 128 ] ;
int len , i ;
prom_getproperty ( parent , " name " , name , sizeof ( name ) ) ;
if ( strcmp ( name , " upa " ) ! = 0 )
return ;
len = prom_getproperty ( parent , " ranges " , ( void * ) ranges , sizeof ( ranges ) ) ;
if ( len < = 0 )
return ;
len / = sizeof ( struct linux_prom64_ranges ) ;
for ( i = 0 ; i < len ; i + + ) {
struct linux_prom64_ranges * rng = & ranges [ i ] ;
u64 phys_addr = regs - > phys_addr ;
if ( phys_addr > = rng - > ot_child_base & &
phys_addr < ( rng - > ot_child_base + rng - > or_size ) ) {
regs - > phys_addr - = rng - > ot_child_base ;
regs - > phys_addr + = rng - > ot_parent_base ;
return ;
}
}
return ;
}
static int ffb_init_one ( drm_device_t * dev , int prom_node , int parent_node ,
int instance )
{
struct linux_prom64_registers regs [ 2 * PROMREG_MAX ] ;
ffb_dev_priv_t * ffb_priv = ( ffb_dev_priv_t * ) dev - > dev_private ;
int i ;
ffb_priv - > prom_node = prom_node ;
if ( prom_getproperty ( ffb_priv - > prom_node , " reg " ,
( void * ) regs , sizeof ( regs ) ) < = 0 ) {
return - EINVAL ;
}
ffb_apply_upa_parent_ranges ( parent_node , & regs [ 0 ] ) ;
ffb_priv - > card_phys_base = regs [ 0 ] . phys_addr ;
ffb_priv - > regs = ( ffb_fbcPtr )
( regs [ 0 ] . phys_addr + 0x00600000UL ) ;
get_ffb_type ( ffb_priv , instance ) ;
for ( i = 0 ; i < FFB_MAX_CTXS ; i + + )
ffb_priv - > hw_state [ i ] = NULL ;
return 0 ;
}
static drm_map_t * ffb_find_map ( struct file * filp , unsigned long off )
{
drm_file_t * priv = filp - > private_data ;
drm_device_t * dev ;
drm_map_list_t * r_list ;
struct list_head * list ;
drm_map_t * map ;
if ( ! priv | | ( dev = priv - > dev ) = = NULL )
return NULL ;
list_for_each ( list , & dev - > maplist - > head ) {
r_list = ( drm_map_list_t * ) list ;
map = r_list - > map ;
if ( ! map )
continue ;
2005-08-05 16:11:22 +04:00
if ( r_list - > user_token = = off )
2005-04-17 02:20:36 +04:00
return map ;
}
return NULL ;
}
unsigned long ffb_get_unmapped_area ( struct file * filp ,
unsigned long hint ,
unsigned long len ,
unsigned long pgoff ,
unsigned long flags )
{
drm_map_t * map = ffb_find_map ( filp , pgoff < < PAGE_SHIFT ) ;
unsigned long addr = - ENOMEM ;
if ( ! map )
return get_unmapped_area ( NULL , hint , len , pgoff , flags ) ;
if ( map - > type = = _DRM_FRAME_BUFFER | |
map - > type = = _DRM_REGISTERS ) {
# ifdef HAVE_ARCH_FB_UNMAPPED_AREA
addr = get_fb_unmapped_area ( filp , hint , len , pgoff , flags ) ;
# else
addr = get_unmapped_area ( NULL , hint , len , pgoff , flags ) ;
# endif
} else if ( map - > type = = _DRM_SHM & & SHMLBA > PAGE_SIZE ) {
unsigned long slack = SHMLBA - PAGE_SIZE ;
addr = get_unmapped_area ( NULL , hint , len + slack , pgoff , flags ) ;
if ( ! ( addr & ~ PAGE_MASK ) ) {
unsigned long kvirt = ( unsigned long ) map - > handle ;
if ( ( kvirt & ( SHMLBA - 1 ) ) ! = ( addr & ( SHMLBA - 1 ) ) ) {
unsigned long koff , aoff ;
koff = kvirt & ( SHMLBA - 1 ) ;
aoff = addr & ( SHMLBA - 1 ) ;
if ( koff < aoff )
koff + = SHMLBA ;
addr + = ( koff - aoff ) ;
}
}
} else {
addr = get_unmapped_area ( NULL , hint , len , pgoff , flags ) ;
}
return addr ;
}
static int ffb_presetup ( drm_device_t * dev )
{
ffb_dev_priv_t * ffb_priv ;
int ret = 0 ;
int i = 0 ;
/* Check for the case where no device was found. */
if ( ffb_position = = NULL )
return - ENODEV ;
/* code used to use numdevs no numdevs anymore */
ffb_priv = kmalloc ( sizeof ( ffb_dev_priv_t ) , GFP_KERNEL ) ;
if ( ! ffb_priv )
return - ENOMEM ;
memset ( ffb_priv , 0 , sizeof ( * ffb_priv ) ) ;
dev - > dev_private = ffb_priv ;
ret = ffb_init_one ( dev ,
ffb_position [ i ] . node ,
ffb_position [ i ] . root ,
i ) ;
return ret ;
}
static void ffb_driver_release ( drm_device_t * dev , struct file * filp )
{
ffb_dev_priv_t * fpriv = ( ffb_dev_priv_t * ) dev - > dev_private ;
int context = _DRM_LOCKING_CONTEXT ( dev - > lock . hw_lock - > lock ) ;
int idx ;
idx = context - 1 ;
if ( fpriv & &
context ! = DRM_KERNEL_CONTEXT & &
fpriv - > hw_state [ idx ] ! = NULL ) {
kfree ( fpriv - > hw_state [ idx ] ) ;
fpriv - > hw_state [ idx ] = NULL ;
}
}
static void ffb_driver_pretakedown ( drm_device_t * dev )
{
if ( dev - > dev_private ) kfree ( dev - > dev_private ) ;
}
static int ffb_driver_postcleanup ( drm_device_t * dev )
{
if ( ffb_position ! = NULL ) kfree ( ffb_position ) ;
return 0 ;
}
static void ffb_driver_kernel_context_switch_unlock ( struct drm_device * dev , drm_lock_t * lock )
{
dev - > lock . filp = 0 ;
{
__volatile__ unsigned int * plock = & dev - > lock . hw_lock - > lock ;
unsigned int old , new , prev , ctx ;
ctx = lock - > context ;
do {
old = * plock ;
new = ctx ;
prev = cmpxchg ( plock , old , new ) ;
} while ( prev ! = old ) ;
}
wake_up_interruptible ( & dev - > lock . lock_queue ) ;
}
static unsigned long ffb_driver_get_map_ofs ( drm_map_t * map )
{
return ( map - > offset & 0xffffffff ) ;
}
static unsigned long ffb_driver_get_reg_ofs ( drm_device_t * dev )
{
ffb_dev_priv_t * ffb_priv = ( ffb_dev_priv_t * ) dev - > dev_private ;
if ( ffb_priv )
return ffb_priv - > card_phys_base ;
return 0 ;
}
static int postinit ( struct drm_device * dev , unsigned long flags )
{
DRM_INFO ( " Initialized %s %d.%d.%d %s on minor %d \n " ,
DRIVER_NAME ,
DRIVER_MAJOR ,
DRIVER_MINOR ,
DRIVER_PATCHLEVEL ,
DRIVER_DATE ,
dev - > minor
) ;
return 0 ;
}
static int version ( drm_version_t * version )
{
int len ;
version - > version_major = DRIVER_MAJOR ;
version - > version_minor = DRIVER_MINOR ;
version - > version_patchlevel = DRIVER_PATCHLEVEL ;
DRM_COPY ( version - > name , DRIVER_NAME ) ;
DRM_COPY ( version - > date , DRIVER_DATE ) ;
DRM_COPY ( version - > desc , DRIVER_DESC ) ;
return 0 ;
}
static drm_ioctl_desc_t ioctls [ ] = {
} ;
static struct drm_driver driver = {
. driver_features = 0 ,
. dev_priv_size = sizeof ( u32 ) ,
. release = ffb_driver_release ,
. presetup = ffb_presetup ,
. pretakedown = ffb_driver_pretakedown ,
. postcleanup = ffb_driver_postcleanup ,
. kernel_context_switch = ffb_driver_context_switch ,
. kernel_context_switch_unlock = ffb_driver_kernel_context_switch_unlock ,
. get_map_ofs = ffb_driver_get_map_ofs ,
. get_reg_ofs = ffb_driver_get_reg_ofs ,
. postinit = postinit ,
. version = version ,
. ioctls = ioctls ,
. num_ioctls = DRM_ARRAY_SIZE ( ioctls ) ,
. fops = {
. owner = THIS_MODULE ,
. open = drm_open ,
. release = drm_release ,
. ioctl = drm_ioctl ,
. mmap = drm_mmap ,
. poll = drm_poll ,
. fasync = drm_fasync ,
} ,
} ;
static int __init ffb_init ( void )
{
return - ENODEV ;
}
static void __exit ffb_exit ( void )
{
}
module_init ( ffb_init ) ;
module_exit ( ffb_exit ) ;
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( " GPL and additional rights " ) ;