2005-04-17 02:20:36 +04:00
/*
* Frame buffer driver for Trident Blade and Image series
*
2007-10-16 12:28:42 +04:00
* Copyright 2001 , 2002 - Jani Monoses < jani @ iv . ro >
2005-04-17 02:20:36 +04:00
*
*
* CREDITS : ( in order of appearance )
2007-10-16 12:28:42 +04:00
* skeletonfb . c by Geert Uytterhoeven and other fb code in drivers / video
* Special thanks ; ) to Mattia Crivellini < tia @ mclink . it >
* much inspired by the XFree86 4. x Trident driver sources
* by Alan Hourihane the FreeVGA project
* Francesco Salvestrini < salvestrini @ users . sf . net > XP support ,
* code , suggestions
2005-04-17 02:20:36 +04:00
* TODO :
2007-10-16 12:28:42 +04:00
* timing value tweaking so it looks good on every monitor in every mode
* TGUI acceleration
2005-04-17 02:20:36 +04:00
*/
# include <linux/module.h>
# include <linux/fb.h>
# include <linux/init.h>
# include <linux/pci.h>
# include <linux/delay.h>
# include <video/trident.h>
# define VERSION "0.7.8-NEWAPI"
struct tridentfb_par {
2007-10-16 12:28:42 +04:00
void __iomem * io_virt ; /* iospace virtual memory address */
2005-04-17 02:20:36 +04:00
} ;
2007-10-16 12:28:42 +04:00
static unsigned char eng_oper ; /* engine operation... */
2005-04-17 02:20:36 +04:00
static struct fb_ops tridentfb_ops ;
static struct tridentfb_par default_par ;
/* FIXME:kmalloc these 3 instead */
static struct fb_info fb_info ;
static u32 pseudo_pal [ 16 ] ;
static struct fb_var_screeninfo default_var ;
static struct fb_fix_screeninfo tridentfb_fix = {
2007-10-16 12:28:42 +04:00
. id = " Trident " ,
2005-04-17 02:20:36 +04:00
. type = FB_TYPE_PACKED_PIXELS ,
. ypanstep = 1 ,
. visual = FB_VISUAL_PSEUDOCOLOR ,
. accel = FB_ACCEL_NONE ,
} ;
static int chip_id ;
static int defaultaccel ;
static int displaytype ;
/* defaults which are normally overriden by user values */
/* video mode */
2008-04-28 13:15:06 +04:00
static char * mode_option __devinitdata = " 640x480 " ;
2005-04-17 02:20:36 +04:00
static int bpp = 8 ;
static int noaccel ;
static int center ;
static int stretch ;
static int fp ;
static int crt ;
static int memsize ;
static int memdiff ;
static int nativex ;
2008-04-28 13:15:06 +04:00
module_param ( mode_option , charp , 0 ) ;
MODULE_PARM_DESC ( mode_option , " Initial video mode e.g. '648x480-8@60' " ) ;
2008-04-28 13:15:10 +04:00
module_param_named ( mode , mode_option , charp , 0 ) ;
MODULE_PARM_DESC ( mode , " Initial video mode e.g. '648x480-8@60' (deprecated) " ) ;
2005-04-17 02:20:36 +04:00
module_param ( bpp , int , 0 ) ;
module_param ( center , int , 0 ) ;
module_param ( stretch , int , 0 ) ;
module_param ( noaccel , int , 0 ) ;
module_param ( memsize , int , 0 ) ;
module_param ( memdiff , int , 0 ) ;
module_param ( nativex , int , 0 ) ;
module_param ( fp , int , 0 ) ;
module_param ( crt , int , 0 ) ;
static int chip3D ;
static int chipcyber ;
static int is3Dchip ( int id )
{
2007-10-16 12:28:42 +04:00
return ( ( id = = BLADE3D ) | | ( id = = CYBERBLADEE4 ) | |
( id = = CYBERBLADEi7 ) | | ( id = = CYBERBLADEi7D ) | |
( id = = CYBER9397 ) | | ( id = = CYBER9397DVD ) | |
( id = = CYBER9520 ) | | ( id = = CYBER9525DVD ) | |
( id = = IMAGE975 ) | | ( id = = IMAGE985 ) | |
( id = = CYBERBLADEi1 ) | | ( id = = CYBERBLADEi1D ) | |
( id = = CYBERBLADEAi1 ) | | ( id = = CYBERBLADEAi1D ) | |
( id = = CYBERBLADEXPm8 ) | | ( id = = CYBERBLADEXPm16 ) | |
( id = = CYBERBLADEXPAi1 ) ) ;
2005-04-17 02:20:36 +04:00
}
static int iscyber ( int id )
{
switch ( id ) {
2007-10-16 12:28:42 +04:00
case CYBER9388 :
case CYBER9382 :
case CYBER9385 :
case CYBER9397 :
case CYBER9397DVD :
case CYBER9520 :
case CYBER9525DVD :
case CYBERBLADEE4 :
case CYBERBLADEi7D :
case CYBERBLADEi1 :
case CYBERBLADEi1D :
case CYBERBLADEAi1 :
case CYBERBLADEAi1D :
case CYBERBLADEXPAi1 :
return 1 ;
2005-04-17 02:20:36 +04:00
2007-10-16 12:28:42 +04:00
case CYBER9320 :
case TGUI9660 :
case IMAGE975 :
case IMAGE985 :
case BLADE3D :
case CYBERBLADEi7 : /* VIA MPV4 integrated version */
default :
/* case CYBERBLDAEXPm8: Strange */
/* case CYBERBLDAEXPm16: Strange */
return 0 ;
2005-04-17 02:20:36 +04:00
}
}
2007-10-16 12:28:42 +04:00
# define CRT 0x3D0 /* CRTC registers offset for color display */
2005-04-17 02:20:36 +04:00
2008-07-24 08:30:50 +04:00
static inline void t_outb ( struct tridentfb_par * p , u8 val , u16 reg )
{
fb_writeb ( val , p - > io_virt + reg ) ;
}
2005-04-17 02:20:36 +04:00
2008-07-24 08:30:50 +04:00
static inline u8 t_inb ( struct tridentfb_par * p , u16 reg )
{
return fb_readb ( p - > io_virt + reg ) ;
}
2005-04-17 02:20:36 +04:00
static struct accel_switch {
2008-07-24 08:30:50 +04:00
void ( * init_accel ) ( struct tridentfb_par * , int , int ) ;
void ( * wait_engine ) ( struct tridentfb_par * ) ;
void ( * fill_rect )
( struct tridentfb_par * par , u32 , u32 , u32 , u32 , u32 , u32 ) ;
void ( * copy_rect )
( struct tridentfb_par * par , u32 , u32 , u32 , u32 , u32 , u32 ) ;
2005-04-17 02:20:36 +04:00
} * acc ;
2008-07-24 08:30:50 +04:00
static inline void writemmr ( struct tridentfb_par * par , u16 r , u32 v )
{
fb_writel ( v , par - > io_virt + r ) ;
}
static inline u32 readmmr ( struct tridentfb_par * par , u16 r )
{
return fb_readl ( par - > io_virt + r ) ;
}
2005-04-17 02:20:36 +04:00
/*
* Blade specific acceleration .
*/
2007-10-16 12:28:42 +04:00
# define point(x, y) ((y) << 16 | (x))
2005-04-17 02:20:36 +04:00
# define STA 0x2120
# define CMD 0x2144
# define ROP 0x2148
# define CLR 0x2160
# define SR1 0x2100
# define SR2 0x2104
# define DR1 0x2108
# define DR2 0x210C
# define ROP_S 0xCC
2008-07-24 08:30:50 +04:00
static void blade_init_accel ( struct tridentfb_par * par , int pitch , int bpp )
2005-04-17 02:20:36 +04:00
{
2007-10-16 12:28:42 +04:00
int v1 = ( pitch > > 3 ) < < 20 ;
int tmp = 0 , v2 ;
2005-04-17 02:20:36 +04:00
switch ( bpp ) {
2007-10-16 12:28:42 +04:00
case 8 :
tmp = 0 ;
break ;
case 15 :
tmp = 5 ;
break ;
case 16 :
tmp = 1 ;
break ;
case 24 :
case 32 :
tmp = 2 ;
break ;
2005-04-17 02:20:36 +04:00
}
2007-10-16 12:28:42 +04:00
v2 = v1 | ( tmp < < 29 ) ;
2008-07-24 08:30:50 +04:00
writemmr ( par , 0x21C0 , v2 ) ;
writemmr ( par , 0x21C4 , v2 ) ;
writemmr ( par , 0x21B8 , v2 ) ;
writemmr ( par , 0x21BC , v2 ) ;
writemmr ( par , 0x21D0 , v1 ) ;
writemmr ( par , 0x21D4 , v1 ) ;
writemmr ( par , 0x21C8 , v1 ) ;
writemmr ( par , 0x21CC , v1 ) ;
writemmr ( par , 0x216C , 0 ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-24 08:30:50 +04:00
static void blade_wait_engine ( struct tridentfb_par * par )
2005-04-17 02:20:36 +04:00
{
2008-07-24 08:30:50 +04:00
while ( readmmr ( par , STA ) & 0xFA800000 ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-24 08:30:50 +04:00
static void blade_fill_rect ( struct tridentfb_par * par ,
u32 x , u32 y , u32 w , u32 h , u32 c , u32 rop )
2005-04-17 02:20:36 +04:00
{
2008-07-24 08:30:50 +04:00
writemmr ( par , CLR , c ) ;
writemmr ( par , ROP , rop ? 0x66 : ROP_S ) ;
writemmr ( par , CMD , 0x20000000 | 1 < < 19 | 1 < < 4 | 2 < < 2 ) ;
2005-04-17 02:20:36 +04:00
2008-07-24 08:30:50 +04:00
writemmr ( par , DR1 , point ( x , y ) ) ;
writemmr ( par , DR2 , point ( x + w - 1 , y + h - 1 ) ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-24 08:30:50 +04:00
static void blade_copy_rect ( struct tridentfb_par * par ,
u32 x1 , u32 y1 , u32 x2 , u32 y2 , u32 w , u32 h )
2005-04-17 02:20:36 +04:00
{
2007-10-16 12:28:42 +04:00
u32 s1 , s2 , d1 , d2 ;
2005-04-17 02:20:36 +04:00
int direction = 2 ;
2007-10-16 12:28:42 +04:00
s1 = point ( x1 , y1 ) ;
s2 = point ( x1 + w - 1 , y1 + h - 1 ) ;
d1 = point ( x2 , y2 ) ;
d2 = point ( x2 + w - 1 , y2 + h - 1 ) ;
2005-04-17 02:20:36 +04:00
if ( ( y1 > y2 ) | | ( ( y1 = = y2 ) & & ( x1 > x2 ) ) )
2007-10-16 12:28:42 +04:00
direction = 0 ;
2005-04-17 02:20:36 +04:00
2008-07-24 08:30:50 +04:00
writemmr ( par , ROP , ROP_S ) ;
writemmr ( par , CMD , 0xE0000000 | 1 < < 19 | 1 < < 4 | 1 < < 2 | direction ) ;
2005-04-17 02:20:36 +04:00
2008-07-24 08:30:50 +04:00
writemmr ( par , SR1 , direction ? s2 : s1 ) ;
writemmr ( par , SR2 , direction ? s1 : s2 ) ;
writemmr ( par , DR1 , direction ? d2 : d1 ) ;
writemmr ( par , DR2 , direction ? d1 : d2 ) ;
2005-04-17 02:20:36 +04:00
}
static struct accel_switch accel_blade = {
blade_init_accel ,
blade_wait_engine ,
blade_fill_rect ,
blade_copy_rect ,
} ;
/*
* BladeXP specific acceleration functions
*/
# define ROP_P 0xF0
2007-10-16 12:28:42 +04:00
# define masked_point(x, y) ((y & 0xffff)<<16|(x & 0xffff))
2005-04-17 02:20:36 +04:00
2008-07-24 08:30:50 +04:00
static void xp_init_accel ( struct tridentfb_par * par , int pitch , int bpp )
2005-04-17 02:20:36 +04:00
{
2007-10-16 12:28:42 +04:00
int tmp = 0 , v1 ;
2005-04-17 02:20:36 +04:00
unsigned char x = 0 ;
switch ( bpp ) {
2007-10-16 12:28:42 +04:00
case 8 :
x = 0 ;
break ;
case 16 :
x = 1 ;
break ;
case 24 :
x = 3 ;
break ;
case 32 :
x = 2 ;
break ;
2005-04-17 02:20:36 +04:00
}
switch ( pitch < < ( bpp > > 3 ) ) {
2007-10-16 12:28:42 +04:00
case 8192 :
case 512 :
x | = 0x00 ;
break ;
case 1024 :
x | = 0x04 ;
break ;
case 2048 :
x | = 0x08 ;
break ;
case 4096 :
x | = 0x0C ;
break ;
2005-04-17 02:20:36 +04:00
}
2008-07-24 08:30:50 +04:00
t_outb ( par , x , 0x2125 ) ;
2005-04-17 02:20:36 +04:00
eng_oper = x | 0x40 ;
switch ( bpp ) {
2007-10-16 12:28:42 +04:00
case 8 :
tmp = 18 ;
break ;
case 15 :
case 16 :
tmp = 19 ;
break ;
case 24 :
case 32 :
tmp = 20 ;
break ;
2005-04-17 02:20:36 +04:00
}
v1 = pitch < < tmp ;
2008-07-24 08:30:50 +04:00
writemmr ( par , 0x2154 , v1 ) ;
writemmr ( par , 0x2150 , v1 ) ;
t_outb ( par , 3 , 0x2126 ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-24 08:30:50 +04:00
static void xp_wait_engine ( struct tridentfb_par * par )
2005-04-17 02:20:36 +04:00
{
int busy ;
int count , timeout ;
count = 0 ;
timeout = 0 ;
for ( ; ; ) {
2008-07-24 08:30:50 +04:00
busy = t_inb ( par , STA ) & 0x80 ;
2005-04-17 02:20:36 +04:00
if ( busy ! = 0x80 )
return ;
count + + ;
if ( count = = 10000000 ) {
/* Timeout */
count = 9990000 ;
timeout + + ;
if ( timeout = = 8 ) {
/* Reset engine */
2008-07-24 08:30:50 +04:00
t_outb ( par , 0x00 , 0x2120 ) ;
2005-04-17 02:20:36 +04:00
return ;
}
}
}
}
2008-07-24 08:30:50 +04:00
static void xp_fill_rect ( struct tridentfb_par * par ,
u32 x , u32 y , u32 w , u32 h , u32 c , u32 rop )
2005-04-17 02:20:36 +04:00
{
2008-07-24 08:30:50 +04:00
writemmr ( par , 0x2127 , ROP_P ) ;
writemmr ( par , 0x2158 , c ) ;
writemmr ( par , 0x2128 , 0x4000 ) ;
writemmr ( par , 0x2140 , masked_point ( h , w ) ) ;
writemmr ( par , 0x2138 , masked_point ( y , x ) ) ;
t_outb ( par , 0x01 , 0x2124 ) ;
t_outb ( par , eng_oper , 0x2125 ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-24 08:30:50 +04:00
static void xp_copy_rect ( struct tridentfb_par * par ,
u32 x1 , u32 y1 , u32 x2 , u32 y2 , u32 w , u32 h )
2005-04-17 02:20:36 +04:00
{
int direction ;
2007-10-16 12:28:42 +04:00
u32 x1_tmp , x2_tmp , y1_tmp , y2_tmp ;
2005-04-17 02:20:36 +04:00
direction = 0x0004 ;
2007-10-16 12:28:42 +04:00
2005-04-17 02:20:36 +04:00
if ( ( x1 < x2 ) & & ( y1 = = y2 ) ) {
direction | = 0x0200 ;
x1_tmp = x1 + w - 1 ;
x2_tmp = x2 + w - 1 ;
} else {
x1_tmp = x1 ;
x2_tmp = x2 ;
}
2007-10-16 12:28:42 +04:00
2005-04-17 02:20:36 +04:00
if ( y1 < y2 ) {
direction | = 0x0100 ;
y1_tmp = y1 + h - 1 ;
y2_tmp = y2 + h - 1 ;
2007-10-16 12:28:42 +04:00
} else {
2005-04-17 02:20:36 +04:00
y1_tmp = y1 ;
y2_tmp = y2 ;
}
2008-07-24 08:30:50 +04:00
writemmr ( par , 0x2128 , direction ) ;
t_outb ( par , ROP_S , 0x2127 ) ;
writemmr ( par , 0x213C , masked_point ( y1_tmp , x1_tmp ) ) ;
writemmr ( par , 0x2138 , masked_point ( y2_tmp , x2_tmp ) ) ;
writemmr ( par , 0x2140 , masked_point ( h , w ) ) ;
t_outb ( par , 0x01 , 0x2124 ) ;
2005-04-17 02:20:36 +04:00
}
static struct accel_switch accel_xp = {
2007-10-16 12:28:42 +04:00
xp_init_accel ,
2005-04-17 02:20:36 +04:00
xp_wait_engine ,
xp_fill_rect ,
xp_copy_rect ,
} ;
/*
* Image specific acceleration functions
*/
2008-07-24 08:30:50 +04:00
static void image_init_accel ( struct tridentfb_par * par , int pitch , int bpp )
2005-04-17 02:20:36 +04:00
{
int tmp = 0 ;
2007-10-16 12:28:42 +04:00
switch ( bpp ) {
case 8 :
tmp = 0 ;
break ;
case 15 :
tmp = 5 ;
break ;
case 16 :
tmp = 1 ;
break ;
case 24 :
case 32 :
tmp = 2 ;
break ;
2005-04-17 02:20:36 +04:00
}
2008-07-24 08:30:50 +04:00
writemmr ( par , 0x2120 , 0xF0000000 ) ;
writemmr ( par , 0x2120 , 0x40000000 | tmp ) ;
writemmr ( par , 0x2120 , 0x80000000 ) ;
writemmr ( par , 0x2144 , 0x00000000 ) ;
writemmr ( par , 0x2148 , 0x00000000 ) ;
writemmr ( par , 0x2150 , 0x00000000 ) ;
writemmr ( par , 0x2154 , 0x00000000 ) ;
writemmr ( par , 0x2120 , 0x60000000 | ( pitch < < 16 ) | pitch ) ;
writemmr ( par , 0x216C , 0x00000000 ) ;
writemmr ( par , 0x2170 , 0x00000000 ) ;
writemmr ( par , 0x217C , 0x00000000 ) ;
writemmr ( par , 0x2120 , 0x10000000 ) ;
writemmr ( par , 0x2130 , ( 2047 < < 16 ) | 2047 ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-24 08:30:50 +04:00
static void image_wait_engine ( struct tridentfb_par * par )
2005-04-17 02:20:36 +04:00
{
2008-07-24 08:30:50 +04:00
while ( readmmr ( par , 0x2164 ) & 0xF0000000 ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-24 08:30:50 +04:00
static void image_fill_rect ( struct tridentfb_par * par ,
u32 x , u32 y , u32 w , u32 h , u32 c , u32 rop )
2005-04-17 02:20:36 +04:00
{
2008-07-24 08:30:50 +04:00
writemmr ( par , 0x2120 , 0x80000000 ) ;
writemmr ( par , 0x2120 , 0x90000000 | ROP_S ) ;
2005-04-17 02:20:36 +04:00
2008-07-24 08:30:50 +04:00
writemmr ( par , 0x2144 , c ) ;
2005-04-17 02:20:36 +04:00
2008-07-24 08:30:50 +04:00
writemmr ( par , DR1 , point ( x , y ) ) ;
writemmr ( par , DR2 , point ( x + w - 1 , y + h - 1 ) ) ;
2005-04-17 02:20:36 +04:00
2008-07-24 08:30:50 +04:00
writemmr ( par , 0x2124 , 0x80000000 | 3 < < 22 | 1 < < 10 | 1 < < 9 ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-24 08:30:50 +04:00
static void image_copy_rect ( struct tridentfb_par * par ,
u32 x1 , u32 y1 , u32 x2 , u32 y2 , u32 w , u32 h )
2005-04-17 02:20:36 +04:00
{
2007-10-16 12:28:42 +04:00
u32 s1 , s2 , d1 , d2 ;
2005-04-17 02:20:36 +04:00
int direction = 2 ;
2007-10-16 12:28:42 +04:00
s1 = point ( x1 , y1 ) ;
s2 = point ( x1 + w - 1 , y1 + h - 1 ) ;
d1 = point ( x2 , y2 ) ;
d2 = point ( x2 + w - 1 , y2 + h - 1 ) ;
2005-04-17 02:20:36 +04:00
2007-10-16 12:28:42 +04:00
if ( ( y1 > y2 ) | | ( ( y1 = = y2 ) & & ( x1 > x2 ) ) )
direction = 0 ;
2008-07-24 08:30:50 +04:00
writemmr ( par , 0x2120 , 0x80000000 ) ;
writemmr ( par , 0x2120 , 0x90000000 | ROP_S ) ;
2007-10-16 12:28:42 +04:00
2008-07-24 08:30:50 +04:00
writemmr ( par , SR1 , direction ? s2 : s1 ) ;
writemmr ( par , SR2 , direction ? s1 : s2 ) ;
writemmr ( par , DR1 , direction ? d2 : d1 ) ;
writemmr ( par , DR2 , direction ? d1 : d2 ) ;
writemmr ( par , 0x2124 ,
0x80000000 | 1 < < 22 | 1 < < 10 | 1 < < 7 | direction ) ;
2007-10-16 12:28:42 +04:00
}
2005-04-17 02:20:36 +04:00
static struct accel_switch accel_image = {
image_init_accel ,
image_wait_engine ,
image_fill_rect ,
image_copy_rect ,
} ;
/*
* Accel functions called by the upper layers
*/
# ifdef CONFIG_FB_TRIDENT_ACCEL
2007-10-16 12:28:42 +04:00
static void tridentfb_fillrect ( struct fb_info * info ,
const struct fb_fillrect * fr )
2005-04-17 02:20:36 +04:00
{
2008-07-24 08:30:50 +04:00
struct tridentfb_par * par = info - > par ;
2005-04-17 02:20:36 +04:00
int bpp = info - > var . bits_per_pixel ;
2005-08-01 19:46:44 +04:00
int col = 0 ;
2007-10-16 12:28:42 +04:00
2005-04-17 02:20:36 +04:00
switch ( bpp ) {
2007-10-16 12:28:42 +04:00
default :
case 8 :
col | = fr - > color ;
col | = col < < 8 ;
col | = col < < 16 ;
break ;
case 16 :
col = ( ( u32 * ) ( info - > pseudo_palette ) ) [ fr - > color ] ;
break ;
case 32 :
col = ( ( u32 * ) ( info - > pseudo_palette ) ) [ fr - > color ] ;
break ;
}
2008-07-24 08:30:50 +04:00
acc - > fill_rect ( par , fr - > dx , fr - > dy , fr - > width ,
fr - > height , col , fr - > rop ) ;
acc - > wait_engine ( par ) ;
2005-04-17 02:20:36 +04:00
}
2007-10-16 12:28:42 +04:00
static void tridentfb_copyarea ( struct fb_info * info ,
const struct fb_copyarea * ca )
2005-04-17 02:20:36 +04:00
{
2008-07-24 08:30:50 +04:00
struct tridentfb_par * par = info - > par ;
acc - > copy_rect ( par , ca - > sx , ca - > sy , ca - > dx , ca - > dy ,
ca - > width , ca - > height ) ;
acc - > wait_engine ( par ) ;
2005-04-17 02:20:36 +04:00
}
# else /* !CONFIG_FB_TRIDENT_ACCEL */
# define tridentfb_fillrect cfb_fillrect
# define tridentfb_copyarea cfb_copyarea
# endif /* CONFIG_FB_TRIDENT_ACCEL */
/*
* Hardware access functions
*/
2008-07-24 08:30:50 +04:00
static inline unsigned char read3X4 ( struct tridentfb_par * par , int reg )
2005-04-17 02:20:36 +04:00
{
writeb ( reg , par - > io_virt + CRT + 4 ) ;
2007-10-16 12:28:42 +04:00
return readb ( par - > io_virt + CRT + 5 ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-24 08:30:50 +04:00
static inline void write3X4 ( struct tridentfb_par * par , int reg ,
unsigned char val )
2005-04-17 02:20:36 +04:00
{
writeb ( reg , par - > io_virt + CRT + 4 ) ;
writeb ( val , par - > io_virt + CRT + 5 ) ;
}
2008-07-24 08:30:50 +04:00
static inline unsigned char read3C4 ( struct tridentfb_par * par , int reg )
2005-04-17 02:20:36 +04:00
{
2008-07-24 08:30:50 +04:00
t_outb ( par , reg , 0x3C4 ) ;
return t_inb ( par , 0x3C5 ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-24 08:30:50 +04:00
static inline void write3C4 ( struct tridentfb_par * par , int reg ,
unsigned char val )
2005-04-17 02:20:36 +04:00
{
2008-07-24 08:30:50 +04:00
t_outb ( par , reg , 0x3C4 ) ;
t_outb ( par , val , 0x3C5 ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-24 08:30:50 +04:00
static inline unsigned char read3CE ( struct tridentfb_par * par , int reg )
2005-04-17 02:20:36 +04:00
{
2008-07-24 08:30:50 +04:00
t_outb ( par , reg , 0x3CE ) ;
return t_inb ( par , 0x3CF ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-24 08:30:50 +04:00
static inline void writeAttr ( struct tridentfb_par * par , int reg ,
unsigned char val )
2005-04-17 02:20:36 +04:00
{
2008-07-24 08:30:50 +04:00
fb_readb ( par - > io_virt + CRT + 0x0A ) ; /* flip-flop to index */
t_outb ( par , reg , 0x3C0 ) ;
t_outb ( par , val , 0x3C0 ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-24 08:30:50 +04:00
static inline void write3CE ( struct tridentfb_par * par , int reg ,
unsigned char val )
2005-04-17 02:20:36 +04:00
{
2008-07-24 08:30:50 +04:00
t_outb ( par , reg , 0x3CE ) ;
t_outb ( par , val , 0x3CF ) ;
2005-04-17 02:20:36 +04:00
}
2008-03-05 01:28:39 +03:00
static void enable_mmio ( void )
2005-04-17 02:20:36 +04:00
{
/* Goto New Mode */
outb ( 0x0B , 0x3C4 ) ;
inb ( 0x3C5 ) ;
/* Unprotect registers */
outb ( NewMode1 , 0x3C4 ) ;
outb ( 0x80 , 0x3C5 ) ;
2007-10-16 12:28:42 +04:00
2005-04-17 02:20:36 +04:00
/* Enable MMIO */
2007-10-16 12:28:42 +04:00
outb ( PCIReg , 0x3D4 ) ;
2005-04-17 02:20:36 +04:00
outb ( inb ( 0x3D5 ) | 0x01 , 0x3D5 ) ;
2008-03-05 01:28:39 +03:00
}
2008-07-24 08:30:50 +04:00
static void disable_mmio ( struct tridentfb_par * par )
2008-03-05 01:28:39 +03:00
{
/* Goto New Mode */
2008-07-24 08:30:50 +04:00
t_outb ( par , 0x0B , 0x3C4 ) ;
t_inb ( par , 0x3C5 ) ;
2008-03-05 01:28:39 +03:00
/* Unprotect registers */
2008-07-24 08:30:50 +04:00
t_outb ( par , NewMode1 , 0x3C4 ) ;
t_outb ( par , 0x80 , 0x3C5 ) ;
2008-03-05 01:28:39 +03:00
/* Disable MMIO */
2008-07-24 08:30:50 +04:00
t_outb ( par , PCIReg , 0x3D4 ) ;
t_outb ( par , t_inb ( par , 0x3D5 ) & ~ 0x01 , 0x3D5 ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-24 08:30:50 +04:00
static void crtc_unlock ( struct tridentfb_par * par )
{
write3X4 ( par , CRTVSyncEnd , read3X4 ( par , CRTVSyncEnd ) & 0x7F ) ;
}
2005-04-17 02:20:36 +04:00
/* Return flat panel's maximum x resolution */
2008-07-24 08:30:50 +04:00
static int __devinit get_nativex ( struct tridentfb_par * par )
2005-04-17 02:20:36 +04:00
{
2007-10-16 12:28:42 +04:00
int x , y , tmp ;
2005-04-17 02:20:36 +04:00
if ( nativex )
return nativex ;
2008-07-24 08:30:50 +04:00
tmp = ( read3CE ( par , VertStretch ) > > 4 ) & 3 ;
2005-04-17 02:20:36 +04:00
switch ( tmp ) {
2007-10-16 12:28:42 +04:00
case 0 :
x = 1280 ; y = 1024 ;
break ;
case 2 :
x = 1024 ; y = 768 ;
break ;
case 3 :
x = 800 ; y = 600 ;
break ;
case 4 :
x = 1400 ; y = 1050 ;
break ;
case 1 :
default :
x = 640 ; y = 480 ;
break ;
2005-04-17 02:20:36 +04:00
}
output ( " %dx%d flat panel found \n " , x , y ) ;
return x ;
}
/* Set pitch */
2008-07-24 08:30:50 +04:00
static void set_lwidth ( struct tridentfb_par * par , int width )
2005-04-17 02:20:36 +04:00
{
2008-07-24 08:30:50 +04:00
write3X4 ( par , Offset , width & 0xFF ) ;
write3X4 ( par , AddColReg ,
( read3X4 ( par , AddColReg ) & 0xCF ) | ( ( width & 0x300 ) > > 4 ) ) ;
2005-04-17 02:20:36 +04:00
}
/* For resolutions smaller than FP resolution stretch */
2008-07-24 08:30:50 +04:00
static void screen_stretch ( struct tridentfb_par * par )
2005-04-17 02:20:36 +04:00
{
2007-10-16 12:28:42 +04:00
if ( chip_id ! = CYBERBLADEXPAi1 )
2008-07-24 08:30:50 +04:00
write3CE ( par , BiosReg , 0 ) ;
2007-10-16 12:28:42 +04:00
else
2008-07-24 08:30:50 +04:00
write3CE ( par , BiosReg , 8 ) ;
write3CE ( par , VertStretch , ( read3CE ( par , VertStretch ) & 0x7C ) | 1 ) ;
write3CE ( par , HorStretch , ( read3CE ( par , HorStretch ) & 0x7C ) | 1 ) ;
2005-04-17 02:20:36 +04:00
}
/* For resolutions smaller than FP resolution center */
2008-07-24 08:30:50 +04:00
static void screen_center ( struct tridentfb_par * par )
2005-04-17 02:20:36 +04:00
{
2008-07-24 08:30:50 +04:00
write3CE ( par , VertStretch , ( read3CE ( par , VertStretch ) & 0x7C ) | 0x80 ) ;
write3CE ( par , HorStretch , ( read3CE ( par , HorStretch ) & 0x7C ) | 0x80 ) ;
2005-04-17 02:20:36 +04:00
}
/* Address of first shown pixel in display memory */
2008-07-24 08:30:50 +04:00
static void set_screen_start ( struct tridentfb_par * par , int base )
2005-04-17 02:20:36 +04:00
{
2008-07-24 08:30:50 +04:00
u8 tmp ;
write3X4 ( par , StartAddrLow , base & 0xFF ) ;
write3X4 ( par , StartAddrHigh , ( base & 0xFF00 ) > > 8 ) ;
tmp = read3X4 ( par , CRTCModuleTest ) & 0xDF ;
write3X4 ( par , CRTCModuleTest , tmp | ( ( base & 0x10000 ) > > 11 ) ) ;
tmp = read3X4 ( par , CRTHiOrd ) & 0xF8 ;
write3X4 ( par , CRTHiOrd , tmp | ( ( base & 0xE0000 ) > > 17 ) ) ;
2005-04-17 02:20:36 +04:00
}
/* Set dotclock frequency */
2008-07-24 08:30:50 +04:00
static void set_vclk ( struct tridentfb_par * par , unsigned long freq )
2005-04-17 02:20:36 +04:00
{
2007-10-16 12:28:42 +04:00
int m , n , k ;
2008-05-13 01:02:11 +04:00
unsigned long f , fi , d , di ;
2007-10-16 12:28:42 +04:00
unsigned char lo = 0 , hi = 0 ;
2005-04-17 02:20:36 +04:00
2008-05-13 01:02:11 +04:00
d = 20000 ;
2007-10-16 12:28:42 +04:00
for ( k = 2 ; k > = 0 ; k - - )
for ( m = 0 ; m < 63 ; m + + )
for ( n = 0 ; n < 128 ; n + + ) {
2008-05-13 01:02:11 +04:00
fi = ( ( 14318l * ( n + 8 ) ) / ( m + 2 ) ) > > k ;
2007-10-16 12:28:42 +04:00
if ( ( di = abs ( fi - freq ) ) < d ) {
d = di ;
f = fi ;
lo = n ;
hi = ( k < < 6 ) | m ;
}
2008-05-13 01:02:11 +04:00
if ( fi > freq )
break ;
2007-10-16 12:28:42 +04:00
}
2005-04-17 02:20:36 +04:00
if ( chip3D ) {
2008-07-24 08:30:50 +04:00
write3C4 ( par , ClockHigh , hi ) ;
write3C4 ( par , ClockLow , lo ) ;
2005-04-17 02:20:36 +04:00
} else {
2007-10-16 12:28:42 +04:00
outb ( lo , 0x43C8 ) ;
outb ( hi , 0x43C9 ) ;
2005-04-17 02:20:36 +04:00
}
2007-10-16 12:28:42 +04:00
debug ( " VCLK = %X %X \n " , hi , lo ) ;
2005-04-17 02:20:36 +04:00
}
/* Set number of lines for flat panels*/
2008-07-24 08:30:50 +04:00
static void set_number_of_lines ( struct tridentfb_par * par , int lines )
2005-04-17 02:20:36 +04:00
{
2008-07-24 08:30:50 +04:00
int tmp = read3CE ( par , CyberEnhance ) & 0x8F ;
2005-04-17 02:20:36 +04:00
if ( lines > 1024 )
tmp | = 0x50 ;
else if ( lines > 768 )
tmp | = 0x30 ;
else if ( lines > 600 )
tmp | = 0x20 ;
else if ( lines > 480 )
tmp | = 0x10 ;
2008-07-24 08:30:50 +04:00
write3CE ( par , CyberEnhance , tmp ) ;
2005-04-17 02:20:36 +04:00
}
/*
* If we see that FP is active we assume we have one .
* Otherwise we have a CRT display . User can override .
*/
2008-07-24 08:30:50 +04:00
static unsigned int __devinit get_displaytype ( struct tridentfb_par * par )
2005-04-17 02:20:36 +04:00
{
if ( fp )
return DISPLAY_FP ;
if ( crt | | ! chipcyber )
return DISPLAY_CRT ;
2008-07-24 08:30:50 +04:00
return ( read3CE ( par , FPConfig ) & 0x10 ) ? DISPLAY_FP : DISPLAY_CRT ;
2005-04-17 02:20:36 +04:00
}
/* Try detecting the video memory size */
2008-07-24 08:30:50 +04:00
static unsigned int __devinit get_memsize ( struct tridentfb_par * par )
2005-04-17 02:20:36 +04:00
{
unsigned char tmp , tmp2 ;
unsigned int k ;
/* If memory size provided by user */
if ( memsize )
k = memsize * Kb ;
else
2007-10-16 12:28:42 +04:00
switch ( chip_id ) {
case CYBER9525DVD :
k = 2560 * Kb ;
break ;
2005-04-17 02:20:36 +04:00
default :
2008-07-24 08:30:50 +04:00
tmp = read3X4 ( par , SPR ) & 0x0F ;
2005-04-17 02:20:36 +04:00
switch ( tmp ) {
2007-10-16 12:28:42 +04:00
case 0x01 :
2008-03-10 21:43:37 +03:00
k = 512 * Kb ;
2007-10-16 12:28:42 +04:00
break ;
case 0x02 :
k = 6 * Mb ; /* XP */
break ;
case 0x03 :
k = 1 * Mb ;
break ;
case 0x04 :
k = 8 * Mb ;
break ;
case 0x06 :
k = 10 * Mb ; /* XP */
break ;
case 0x07 :
k = 2 * Mb ;
break ;
case 0x08 :
k = 12 * Mb ; /* XP */
break ;
case 0x0A :
k = 14 * Mb ; /* XP */
break ;
case 0x0C :
k = 16 * Mb ; /* XP */
break ;
case 0x0E : /* XP */
2008-07-24 08:30:50 +04:00
tmp2 = read3C4 ( par , 0xC1 ) ;
2007-10-16 12:28:42 +04:00
switch ( tmp2 ) {
case 0x00 :
k = 20 * Mb ;
break ;
case 0x01 :
k = 24 * Mb ;
break ;
case 0x10 :
k = 28 * Mb ;
break ;
case 0x11 :
k = 32 * Mb ;
break ;
default :
k = 1 * Mb ;
break ;
}
break ;
case 0x0F :
k = 4 * Mb ;
break ;
default :
k = 1 * Mb ;
2005-04-17 02:20:36 +04:00
break ;
}
2007-10-16 12:28:42 +04:00
}
2005-04-17 02:20:36 +04:00
k - = memdiff * Kb ;
2007-10-16 12:28:42 +04:00
output ( " framebuffer size = %d Kb \n " , k / Kb ) ;
2005-04-17 02:20:36 +04:00
return k ;
}
/* See if we can handle the video mode described in var */
2007-10-16 12:28:42 +04:00
static int tridentfb_check_var ( struct fb_var_screeninfo * var ,
struct fb_info * info )
2005-04-17 02:20:36 +04:00
{
int bpp = var - > bits_per_pixel ;
debug ( " enter \n " ) ;
/* check color depth */
2007-10-16 12:28:42 +04:00
if ( bpp = = 24 )
2005-04-17 02:20:36 +04:00
bpp = var - > bits_per_pixel = 32 ;
2007-10-16 12:28:42 +04:00
/* check whether resolution fits on panel and in memory */
2005-04-17 02:20:36 +04:00
if ( flatpanel & & nativex & & var - > xres > nativex )
return - EINVAL ;
2007-10-16 12:28:42 +04:00
if ( var - > xres * var - > yres_virtual * bpp / 8 > info - > fix . smem_len )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
switch ( bpp ) {
2007-10-16 12:28:42 +04:00
case 8 :
var - > red . offset = 0 ;
var - > green . offset = 0 ;
var - > blue . offset = 0 ;
var - > red . length = 6 ;
var - > green . length = 6 ;
var - > blue . length = 6 ;
break ;
case 16 :
var - > red . offset = 11 ;
var - > green . offset = 5 ;
var - > blue . offset = 0 ;
var - > red . length = 5 ;
var - > green . length = 6 ;
var - > blue . length = 5 ;
break ;
case 32 :
var - > red . offset = 16 ;
var - > green . offset = 8 ;
var - > blue . offset = 0 ;
var - > red . length = 8 ;
var - > green . length = 8 ;
var - > blue . length = 8 ;
break ;
default :
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
debug ( " exit \n " ) ;
return 0 ;
}
2007-10-16 12:28:42 +04:00
2005-04-17 02:20:36 +04:00
/* Pan the display */
static int tridentfb_pan_display ( struct fb_var_screeninfo * var ,
2007-10-16 12:28:42 +04:00
struct fb_info * info )
2005-04-17 02:20:36 +04:00
{
2008-07-24 08:30:50 +04:00
struct tridentfb_par * par = info - > par ;
2005-04-17 02:20:36 +04:00
unsigned int offset ;
debug ( " enter \n " ) ;
offset = ( var - > xoffset + ( var - > yoffset * var - > xres ) )
2007-10-16 12:28:42 +04:00
* var - > bits_per_pixel / 32 ;
2005-04-17 02:20:36 +04:00
info - > var . xoffset = var - > xoffset ;
info - > var . yoffset = var - > yoffset ;
2008-07-24 08:30:50 +04:00
set_screen_start ( par , offset ) ;
2005-04-17 02:20:36 +04:00
debug ( " exit \n " ) ;
return 0 ;
}
2008-07-24 08:30:50 +04:00
static void shadowmode_on ( struct tridentfb_par * par )
{
write3CE ( par , CyberControl , read3CE ( par , CyberControl ) | 0x81 ) ;
}
static void shadowmode_off ( struct tridentfb_par * par )
{
write3CE ( par , CyberControl , read3CE ( par , CyberControl ) & 0x7E ) ;
}
2005-04-17 02:20:36 +04:00
/* Set the hardware to the requested video mode */
static int tridentfb_set_par ( struct fb_info * info )
{
2007-10-16 12:28:42 +04:00
struct tridentfb_par * par = ( struct tridentfb_par * ) ( info - > par ) ;
u32 htotal , hdispend , hsyncstart , hsyncend , hblankstart , hblankend ;
u32 vtotal , vdispend , vsyncstart , vsyncend , vblankstart , vblankend ;
struct fb_var_screeninfo * var = & info - > var ;
2005-04-17 02:20:36 +04:00
int bpp = var - > bits_per_pixel ;
unsigned char tmp ;
2008-05-13 01:02:11 +04:00
unsigned long vclk ;
2005-04-17 02:20:36 +04:00
debug ( " enter \n " ) ;
2007-10-16 12:28:42 +04:00
hdispend = var - > xres / 8 - 1 ;
hsyncstart = ( var - > xres + var - > right_margin ) / 8 ;
hsyncend = var - > hsync_len / 8 ;
htotal =
( var - > xres + var - > left_margin + var - > right_margin +
var - > hsync_len ) / 8 - 10 ;
2005-04-17 02:20:36 +04:00
hblankstart = hdispend + 1 ;
hblankend = htotal + 5 ;
vdispend = var - > yres - 1 ;
vsyncstart = var - > yres + var - > lower_margin ;
vsyncend = var - > vsync_len ;
2007-10-16 12:28:42 +04:00
vtotal = var - > upper_margin + vsyncstart + vsyncend - 2 ;
2005-04-17 02:20:36 +04:00
vblankstart = var - > yres ;
vblankend = vtotal + 2 ;
2008-07-24 08:30:50 +04:00
crtc_unlock ( par ) ;
write3CE ( par , CyberControl , 8 ) ;
2005-04-17 02:20:36 +04:00
if ( flatpanel & & var - > xres < nativex ) {
/*
* on flat panels with native size larger
* than requested resolution decide whether
* we stretch or center
*/
2008-07-24 08:30:50 +04:00
t_outb ( par , 0xEB , 0x3C2 ) ;
2005-04-17 02:20:36 +04:00
2008-07-24 08:30:50 +04:00
shadowmode_on ( par ) ;
2005-04-17 02:20:36 +04:00
2007-10-16 12:28:42 +04:00
if ( center )
2008-07-24 08:30:50 +04:00
screen_center ( par ) ;
2005-04-17 02:20:36 +04:00
else if ( stretch )
2008-07-24 08:30:50 +04:00
screen_stretch ( par ) ;
2005-04-17 02:20:36 +04:00
} else {
2008-07-24 08:30:50 +04:00
t_outb ( par , 0x2B , 0x3C2 ) ;
write3CE ( par , CyberControl , 8 ) ;
2005-04-17 02:20:36 +04:00
}
/* vertical timing values */
2008-07-24 08:30:50 +04:00
write3X4 ( par , CRTVTotal , vtotal & 0xFF ) ;
write3X4 ( par , CRTVDispEnd , vdispend & 0xFF ) ;
write3X4 ( par , CRTVSyncStart , vsyncstart & 0xFF ) ;
write3X4 ( par , CRTVSyncEnd , ( vsyncend & 0x0F ) ) ;
write3X4 ( par , CRTVBlankStart , vblankstart & 0xFF ) ;
write3X4 ( par , CRTVBlankEnd , 0 /* p->vblankend & 0xFF */ ) ;
2005-04-17 02:20:36 +04:00
/* horizontal timing values */
2008-07-24 08:30:50 +04:00
write3X4 ( par , CRTHTotal , htotal & 0xFF ) ;
write3X4 ( par , CRTHDispEnd , hdispend & 0xFF ) ;
write3X4 ( par , CRTHSyncStart , hsyncstart & 0xFF ) ;
write3X4 ( par , CRTHSyncEnd ,
( hsyncend & 0x1F ) | ( ( hblankend & 0x20 ) < < 2 ) ) ;
write3X4 ( par , CRTHBlankStart , hblankstart & 0xFF ) ;
write3X4 ( par , CRTHBlankEnd , 0 /* (p->hblankend & 0x1F) */ ) ;
2005-04-17 02:20:36 +04:00
/* higher bits of vertical timing values */
tmp = 0x10 ;
if ( vtotal & 0x100 ) tmp | = 0x01 ;
if ( vdispend & 0x100 ) tmp | = 0x02 ;
if ( vsyncstart & 0x100 ) tmp | = 0x04 ;
if ( vblankstart & 0x100 ) tmp | = 0x08 ;
if ( vtotal & 0x200 ) tmp | = 0x20 ;
if ( vdispend & 0x200 ) tmp | = 0x40 ;
if ( vsyncstart & 0x200 ) tmp | = 0x80 ;
2008-07-24 08:30:50 +04:00
write3X4 ( par , CRTOverflow , tmp ) ;
2005-04-17 02:20:36 +04:00
2008-07-24 08:30:50 +04:00
tmp = read3X4 ( par , CRTHiOrd ) | 0x08 ; /* line compare bit 10 */
2005-04-17 02:20:36 +04:00
if ( vtotal & 0x400 ) tmp | = 0x80 ;
if ( vblankstart & 0x400 ) tmp | = 0x40 ;
if ( vsyncstart & 0x400 ) tmp | = 0x20 ;
if ( vdispend & 0x400 ) tmp | = 0x10 ;
2008-07-24 08:30:50 +04:00
write3X4 ( par , CRTHiOrd , tmp ) ;
2005-04-17 02:20:36 +04:00
tmp = 0 ;
if ( htotal & 0x800 ) tmp | = 0x800 > > 11 ;
if ( hblankstart & 0x800 ) tmp | = 0x800 > > 7 ;
2008-07-24 08:30:50 +04:00
write3X4 ( par , HorizOverflow , tmp ) ;
2007-10-16 12:28:42 +04:00
2005-04-17 02:20:36 +04:00
tmp = 0x40 ;
if ( vblankstart & 0x200 ) tmp | = 0x20 ;
2007-10-16 12:28:42 +04:00
//FIXME if (info->var.vmode & FB_VMODE_DOUBLE) tmp |= 0x80; /* double scan for 200 line modes */
2008-07-24 08:30:50 +04:00
write3X4 ( par , CRTMaxScanLine , tmp ) ;
2005-04-17 02:20:36 +04:00
2008-07-24 08:30:50 +04:00
write3X4 ( par , CRTLineCompare , 0xFF ) ;
write3X4 ( par , CRTPRowScan , 0 ) ;
write3X4 ( par , CRTModeControl , 0xC3 ) ;
2005-04-17 02:20:36 +04:00
2008-07-24 08:30:50 +04:00
write3X4 ( par , LinearAddReg , 0x20 ) ; /* enable linear addressing */
2005-04-17 02:20:36 +04:00
2007-10-16 12:28:42 +04:00
tmp = ( info - > var . vmode & FB_VMODE_INTERLACED ) ? 0x84 : 0x80 ;
2008-07-24 08:30:50 +04:00
/* enable access extended memory */
write3X4 ( par , CRTCModuleTest , tmp ) ;
2005-04-17 02:20:36 +04:00
2008-07-24 08:30:50 +04:00
/* enable GE for text acceleration */
write3X4 ( par , GraphEngReg , 0x80 ) ;
2005-04-17 02:20:36 +04:00
2007-10-16 12:28:42 +04:00
# ifdef CONFIG_FB_TRIDENT_ACCEL
2008-07-24 08:30:50 +04:00
acc - > init_accel ( par , info - > var . xres , bpp ) ;
2005-08-01 19:46:44 +04:00
# endif
2007-10-16 12:28:42 +04:00
2005-04-17 02:20:36 +04:00
switch ( bpp ) {
2007-10-16 12:28:42 +04:00
case 8 :
tmp = 0x00 ;
break ;
case 16 :
tmp = 0x05 ;
break ;
case 24 :
tmp = 0x29 ;
break ;
case 32 :
tmp = 0x09 ;
break ;
2005-04-17 02:20:36 +04:00
}
2008-07-24 08:30:50 +04:00
write3X4 ( par , PixelBusReg , tmp ) ;
2005-04-17 02:20:36 +04:00
tmp = 0x10 ;
if ( chipcyber )
2007-10-16 12:28:42 +04:00
tmp | = 0x20 ;
2008-07-24 08:30:50 +04:00
write3X4 ( par , DRAMControl , tmp ) ; /* both IO, linear enable */
2005-04-17 02:20:36 +04:00
2008-07-24 08:30:50 +04:00
write3X4 ( par , InterfaceSel , read3X4 ( par , InterfaceSel ) | 0x40 ) ;
write3X4 ( par , Performance , 0x92 ) ;
/* MMIO & PCI read and write burst enable */
write3X4 ( par , PCIReg , 0x07 ) ;
2005-04-17 02:20:36 +04:00
2008-05-13 01:02:11 +04:00
/* convert from picoseconds to kHz */
vclk = PICOS2KHZ ( info - > var . pixclock ) ;
2005-04-17 02:20:36 +04:00
if ( bpp = = 32 )
2008-05-13 01:02:11 +04:00
vclk * = 2 ;
2008-07-24 08:30:50 +04:00
set_vclk ( par , vclk ) ;
2005-04-17 02:20:36 +04:00
2008-07-24 08:30:50 +04:00
write3C4 ( par , 0 , 3 ) ;
write3C4 ( par , 1 , 1 ) ; /* set char clock 8 dots wide */
/* enable 4 maps because needed in chain4 mode */
write3C4 ( par , 2 , 0x0F ) ;
write3C4 ( par , 3 , 0 ) ;
write3C4 ( par , 4 , 0x0E ) ; /* memory mode enable bitmaps ?? */
2005-04-17 02:20:36 +04:00
2008-07-24 08:30:50 +04:00
/* divide clock by 2 if 32bpp chain4 mode display and CPU path */
write3CE ( par , MiscExtFunc , ( bpp = = 32 ) ? 0x1A : 0x12 ) ;
write3CE ( par , 0x5 , 0x40 ) ; /* no CGA compat, allow 256 col */
write3CE ( par , 0x6 , 0x05 ) ; /* graphics mode */
write3CE ( par , 0x7 , 0x0F ) ; /* planes? */
2005-04-17 02:20:36 +04:00
if ( chip_id = = CYBERBLADEXPAi1 ) {
/* This fixes snow-effect in 32 bpp */
2008-07-24 08:30:50 +04:00
write3X4 ( par , CRTHSyncStart , 0x84 ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-24 08:30:50 +04:00
/* graphics mode and support 256 color modes */
writeAttr ( par , 0x10 , 0x41 ) ;
writeAttr ( par , 0x12 , 0x0F ) ; /* planes */
writeAttr ( par , 0x13 , 0 ) ; /* horizontal pel panning */
2005-04-17 02:20:36 +04:00
2007-10-16 12:28:42 +04:00
/* colors */
for ( tmp = 0 ; tmp < 0x10 ; tmp + + )
2008-07-24 08:30:50 +04:00
writeAttr ( par , tmp , tmp ) ;
fb_readb ( par - > io_virt + CRT + 0x0A ) ; /* flip-flop to index */
t_outb ( par , 0x20 , 0x3C0 ) ; /* enable attr */
2005-04-17 02:20:36 +04:00
switch ( bpp ) {
2007-10-16 12:28:42 +04:00
case 8 :
tmp = 0 ;
break ;
case 15 :
tmp = 0x10 ;
break ;
case 16 :
tmp = 0x30 ;
break ;
case 24 :
case 32 :
tmp = 0xD0 ;
break ;
2005-04-17 02:20:36 +04:00
}
2008-07-24 08:30:50 +04:00
t_inb ( par , 0x3C8 ) ;
t_inb ( par , 0x3C6 ) ;
t_inb ( par , 0x3C6 ) ;
t_inb ( par , 0x3C6 ) ;
t_inb ( par , 0x3C6 ) ;
t_outb ( par , tmp , 0x3C6 ) ;
t_inb ( par , 0x3C8 ) ;
2005-04-17 02:20:36 +04:00
if ( flatpanel )
2008-07-24 08:30:50 +04:00
set_number_of_lines ( par , info - > var . yres ) ;
set_lwidth ( par , info - > var . xres * bpp / ( 4 * 16 ) ) ;
2005-04-17 02:20:36 +04:00
info - > fix . visual = ( bpp = = 8 ) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR ;
2007-10-16 12:28:42 +04:00
info - > fix . line_length = info - > var . xres * ( bpp > > 3 ) ;
info - > cmap . len = ( bpp = = 8 ) ? 256 : 16 ;
2005-04-17 02:20:36 +04:00
debug ( " exit \n " ) ;
return 0 ;
}
/* Set one color register */
static int tridentfb_setcolreg ( unsigned regno , unsigned red , unsigned green ,
2007-10-16 12:28:42 +04:00
unsigned blue , unsigned transp ,
struct fb_info * info )
2005-04-17 02:20:36 +04:00
{
int bpp = info - > var . bits_per_pixel ;
2008-07-24 08:30:50 +04:00
struct tridentfb_par * par = info - > par ;
2005-04-17 02:20:36 +04:00
if ( regno > = info - > cmap . len )
return 1 ;
2007-07-17 15:05:41 +04:00
if ( bpp = = 8 ) {
2008-07-24 08:30:50 +04:00
t_outb ( par , 0xFF , 0x3C6 ) ;
t_outb ( par , regno , 0x3C8 ) ;
2005-04-17 02:20:36 +04:00
2008-07-24 08:30:50 +04:00
t_outb ( par , red > > 10 , 0x3C9 ) ;
t_outb ( par , green > > 10 , 0x3C9 ) ;
t_outb ( par , blue > > 10 , 0x3C9 ) ;
2005-04-17 02:20:36 +04:00
2007-07-17 15:05:41 +04:00
} else if ( regno < 16 ) {
if ( bpp = = 16 ) { /* RGB 565 */
u32 col ;
col = ( red & 0xF800 ) | ( ( green & 0xFC00 ) > > 5 ) |
( ( blue & 0xF800 ) > > 11 ) ;
col | = col < < 16 ;
( ( u32 * ) ( info - > pseudo_palette ) ) [ regno ] = col ;
} else if ( bpp = = 32 ) /* ARGB 8888 */
( ( u32 * ) info - > pseudo_palette ) [ regno ] =
2007-10-16 12:28:42 +04:00
( ( transp & 0xFF00 ) < < 16 ) |
( ( red & 0xFF00 ) < < 8 ) |
2007-07-17 15:05:41 +04:00
( ( green & 0xFF00 ) ) |
2007-10-16 12:28:42 +04:00
( ( blue & 0xFF00 ) > > 8 ) ;
2007-07-17 15:05:41 +04:00
}
2005-04-17 02:20:36 +04:00
2007-10-16 12:28:42 +04:00
/* debug("exit\n"); */
2005-04-17 02:20:36 +04:00
return 0 ;
}
/* Try blanking the screen.For flat panels it does nothing */
static int tridentfb_blank ( int blank_mode , struct fb_info * info )
{
2007-10-16 12:28:42 +04:00
unsigned char PMCont , DPMSCont ;
2008-07-24 08:30:50 +04:00
struct tridentfb_par * par = info - > par ;
2005-04-17 02:20:36 +04:00
debug ( " enter \n " ) ;
if ( flatpanel )
return 0 ;
2008-07-24 08:30:50 +04:00
t_outb ( par , 0x04 , 0x83C8 ) ; /* Read DPMS Control */
PMCont = t_inb ( par , 0x83C6 ) & 0xFC ;
DPMSCont = read3CE ( par , PowerStatus ) & 0xFC ;
2007-10-16 12:28:42 +04:00
switch ( blank_mode ) {
2005-04-17 02:20:36 +04:00
case FB_BLANK_UNBLANK :
/* Screen: On, HSync: On, VSync: On */
case FB_BLANK_NORMAL :
/* Screen: Off, HSync: On, VSync: On */
PMCont | = 0x03 ;
DPMSCont | = 0x00 ;
break ;
case FB_BLANK_HSYNC_SUSPEND :
/* Screen: Off, HSync: Off, VSync: On */
PMCont | = 0x02 ;
DPMSCont | = 0x01 ;
break ;
case FB_BLANK_VSYNC_SUSPEND :
/* Screen: Off, HSync: On, VSync: Off */
PMCont | = 0x02 ;
DPMSCont | = 0x02 ;
break ;
case FB_BLANK_POWERDOWN :
/* Screen: Off, HSync: Off, VSync: Off */
PMCont | = 0x00 ;
DPMSCont | = 0x03 ;
break ;
2007-10-16 12:28:42 +04:00
}
2005-04-17 02:20:36 +04:00
2008-07-24 08:30:50 +04:00
write3CE ( par , PowerStatus , DPMSCont ) ;
t_outb ( par , 4 , 0x83C8 ) ;
t_outb ( par , PMCont , 0x83C6 ) ;
2005-04-17 02:20:36 +04:00
debug ( " exit \n " ) ;
/* let fbcon do a softblank for us */
return ( blank_mode = = FB_BLANK_NORMAL ) ? 1 : 0 ;
}
2007-10-16 12:28:42 +04:00
static struct fb_ops tridentfb_ops = {
. owner = THIS_MODULE ,
. fb_setcolreg = tridentfb_setcolreg ,
. fb_pan_display = tridentfb_pan_display ,
. fb_blank = tridentfb_blank ,
. fb_check_var = tridentfb_check_var ,
. fb_set_par = tridentfb_set_par ,
. fb_fillrect = tridentfb_fillrect ,
. fb_copyarea = tridentfb_copyarea ,
. fb_imageblit = cfb_imageblit ,
} ;
static int __devinit trident_pci_probe ( struct pci_dev * dev ,
const struct pci_device_id * id )
2005-04-17 02:20:36 +04:00
{
int err ;
unsigned char revision ;
err = pci_enable_device ( dev ) ;
if ( err )
return err ;
chip_id = id - > device ;
2007-10-16 12:28:42 +04:00
if ( chip_id = = CYBERBLADEi1 )
2005-09-10 00:04:56 +04:00
output ( " *** Please do use cyblafb, Cyberblade/i1 support "
" will soon be removed from tridentfb! \n " ) ;
2005-04-17 02:20:36 +04:00
/* If PCI id is 0x9660 then further detect chip type */
2007-10-16 12:28:42 +04:00
2005-04-17 02:20:36 +04:00
if ( chip_id = = TGUI9660 ) {
2007-10-16 12:28:42 +04:00
outb ( RevisionID , 0x3C4 ) ;
revision = inb ( 0x3C5 ) ;
2005-04-17 02:20:36 +04:00
switch ( revision ) {
2007-10-16 12:28:42 +04:00
case 0x22 :
case 0x23 :
chip_id = CYBER9397 ;
break ;
case 0x2A :
chip_id = CYBER9397DVD ;
break ;
case 0x30 :
case 0x33 :
case 0x34 :
case 0x35 :
case 0x38 :
case 0x3A :
case 0xB3 :
chip_id = CYBER9385 ;
break ;
case 0x40 . . . 0x43 :
chip_id = CYBER9382 ;
break ;
case 0x4A :
chip_id = CYBER9388 ;
break ;
default :
break ;
2005-04-17 02:20:36 +04:00
}
}
chip3D = is3Dchip ( chip_id ) ;
chipcyber = iscyber ( chip_id ) ;
if ( is_xp ( chip_id ) ) {
acc = & accel_xp ;
2007-10-16 12:28:42 +04:00
} else if ( is_blade ( chip_id ) ) {
2005-04-17 02:20:36 +04:00
acc = & accel_blade ;
} else {
acc = & accel_image ;
}
/* acceleration is on by default for 3D chips */
defaultaccel = chip3D & & ! noaccel ;
fb_info . par = & default_par ;
/* setup MMIO region */
2007-10-16 12:28:42 +04:00
tridentfb_fix . mmio_start = pci_resource_start ( dev , 1 ) ;
tridentfb_fix . mmio_len = chip3D ? 0x20000 : 0x10000 ;
2005-04-17 02:20:36 +04:00
if ( ! request_mem_region ( tridentfb_fix . mmio_start , tridentfb_fix . mmio_len , " tridentfb " ) ) {
debug ( " request_region failed! \n " ) ;
return - 1 ;
}
default_par . io_virt = ioremap_nocache ( tridentfb_fix . mmio_start , tridentfb_fix . mmio_len ) ;
if ( ! default_par . io_virt ) {
debug ( " ioremap failed \n " ) ;
2008-03-05 01:28:39 +03:00
err = - 1 ;
goto out_unmap1 ;
2005-04-17 02:20:36 +04:00
}
enable_mmio ( ) ;
2007-10-16 12:28:42 +04:00
2005-04-17 02:20:36 +04:00
/* setup framebuffer memory */
2007-10-16 12:28:42 +04:00
tridentfb_fix . smem_start = pci_resource_start ( dev , 0 ) ;
2008-07-24 08:30:50 +04:00
tridentfb_fix . smem_len = get_memsize ( & default_par ) ;
2007-10-16 12:28:42 +04:00
2005-04-17 02:20:36 +04:00
if ( ! request_mem_region ( tridentfb_fix . smem_start , tridentfb_fix . smem_len , " tridentfb " ) ) {
debug ( " request_mem_region failed! \n " ) ;
2008-07-24 08:30:50 +04:00
disable_mmio ( fb_info . par ) ;
2006-12-08 13:40:03 +03:00
err = - 1 ;
2008-03-05 01:28:39 +03:00
goto out_unmap1 ;
2005-04-17 02:20:36 +04:00
}
fb_info . screen_base = ioremap_nocache ( tridentfb_fix . smem_start ,
2007-10-16 12:28:42 +04:00
tridentfb_fix . smem_len ) ;
2005-04-17 02:20:36 +04:00
if ( ! fb_info . screen_base ) {
debug ( " ioremap failed \n " ) ;
2006-12-08 13:40:03 +03:00
err = - 1 ;
2008-03-05 01:28:39 +03:00
goto out_unmap2 ;
2005-04-17 02:20:36 +04:00
}
output ( " %s board found \n " , pci_name ( dev ) ) ;
2008-07-24 08:30:50 +04:00
displaytype = get_displaytype ( & default_par ) ;
2005-04-17 02:20:36 +04:00
2007-10-16 12:28:42 +04:00
if ( flatpanel )
2008-07-24 08:30:50 +04:00
nativex = get_nativex ( & default_par ) ;
2005-04-17 02:20:36 +04:00
fb_info . fix = tridentfb_fix ;
fb_info . fbops = & tridentfb_ops ;
fb_info . flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN ;
# ifdef CONFIG_FB_TRIDENT_ACCEL
fb_info . flags | = FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT ;
# endif
fb_info . pseudo_palette = pseudo_pal ;
2008-04-28 13:15:06 +04:00
if ( ! fb_find_mode ( & default_var , & fb_info ,
mode_option , NULL , 0 , NULL , bpp ) ) {
2006-12-08 13:40:03 +03:00
err = - EINVAL ;
2008-03-05 01:28:39 +03:00
goto out_unmap2 ;
2006-12-08 13:40:03 +03:00
}
2008-03-05 01:28:39 +03:00
err = fb_alloc_cmap ( & fb_info . cmap , 256 , 0 ) ;
if ( err < 0 )
goto out_unmap2 ;
2005-04-17 02:20:36 +04:00
if ( defaultaccel & & acc )
default_var . accel_flags | = FB_ACCELF_TEXT ;
else
default_var . accel_flags & = ~ FB_ACCELF_TEXT ;
default_var . activate | = FB_ACTIVATE_NOW ;
fb_info . var = default_var ;
fb_info . device = & dev - > dev ;
if ( register_framebuffer ( & fb_info ) < 0 ) {
printk ( KERN_ERR " tridentfb: could not register Trident framebuffer \n " ) ;
2008-03-05 01:28:39 +03:00
fb_dealloc_cmap ( & fb_info . cmap ) ;
2006-12-08 13:40:03 +03:00
err = - EINVAL ;
2008-03-05 01:28:39 +03:00
goto out_unmap2 ;
2005-04-17 02:20:36 +04:00
}
output ( " fb%d: %s frame buffer device %dx%d-%dbpp \n " ,
2007-10-16 12:28:42 +04:00
fb_info . node , fb_info . fix . id , default_var . xres ,
default_var . yres , default_var . bits_per_pixel ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
2006-12-08 13:40:03 +03:00
2008-03-05 01:28:39 +03:00
out_unmap2 :
2006-12-08 13:40:03 +03:00
if ( fb_info . screen_base )
iounmap ( fb_info . screen_base ) ;
2008-03-05 01:28:39 +03:00
release_mem_region ( tridentfb_fix . smem_start , tridentfb_fix . smem_len ) ;
2008-07-24 08:30:50 +04:00
disable_mmio ( fb_info . par ) ;
2008-03-05 01:28:39 +03:00
out_unmap1 :
if ( default_par . io_virt )
iounmap ( default_par . io_virt ) ;
release_mem_region ( tridentfb_fix . mmio_start , tridentfb_fix . mmio_len ) ;
2006-12-08 13:40:03 +03:00
return err ;
2005-04-17 02:20:36 +04:00
}
2007-10-16 12:28:42 +04:00
static void __devexit trident_pci_remove ( struct pci_dev * dev )
2005-04-17 02:20:36 +04:00
{
struct tridentfb_par * par = ( struct tridentfb_par * ) fb_info . par ;
unregister_framebuffer ( & fb_info ) ;
iounmap ( par - > io_virt ) ;
iounmap ( fb_info . screen_base ) ;
release_mem_region ( tridentfb_fix . smem_start , tridentfb_fix . smem_len ) ;
2008-03-05 01:28:39 +03:00
release_mem_region ( tridentfb_fix . mmio_start , tridentfb_fix . mmio_len ) ;
2005-04-17 02:20:36 +04:00
}
/* List of boards that we are trying to support */
static struct pci_device_id trident_devices [ ] = {
2007-10-16 12:28:42 +04:00
{ PCI_VENDOR_ID_TRIDENT , BLADE3D , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 0 } ,
{ PCI_VENDOR_ID_TRIDENT , CYBERBLADEi7 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 0 } ,
{ PCI_VENDOR_ID_TRIDENT , CYBERBLADEi7D , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 0 } ,
{ PCI_VENDOR_ID_TRIDENT , CYBERBLADEi1 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 0 } ,
{ PCI_VENDOR_ID_TRIDENT , CYBERBLADEi1D , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 0 } ,
{ PCI_VENDOR_ID_TRIDENT , CYBERBLADEAi1 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 0 } ,
{ PCI_VENDOR_ID_TRIDENT , CYBERBLADEAi1D , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 0 } ,
{ PCI_VENDOR_ID_TRIDENT , CYBERBLADEE4 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 0 } ,
{ PCI_VENDOR_ID_TRIDENT , TGUI9660 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 0 } ,
{ PCI_VENDOR_ID_TRIDENT , IMAGE975 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 0 } ,
{ PCI_VENDOR_ID_TRIDENT , IMAGE985 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 0 } ,
{ PCI_VENDOR_ID_TRIDENT , CYBER9320 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 0 } ,
{ PCI_VENDOR_ID_TRIDENT , CYBER9388 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 0 } ,
{ PCI_VENDOR_ID_TRIDENT , CYBER9520 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 0 } ,
{ PCI_VENDOR_ID_TRIDENT , CYBER9525DVD , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 0 } ,
{ PCI_VENDOR_ID_TRIDENT , CYBER9397 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 0 } ,
{ PCI_VENDOR_ID_TRIDENT , CYBER9397DVD , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 0 } ,
{ PCI_VENDOR_ID_TRIDENT , CYBERBLADEXPAi1 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 0 } ,
{ PCI_VENDOR_ID_TRIDENT , CYBERBLADEXPm8 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 0 } ,
{ PCI_VENDOR_ID_TRIDENT , CYBERBLADEXPm16 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 0 } ,
2005-04-17 02:20:36 +04:00
{ 0 , }
2007-10-16 12:28:42 +04:00
} ;
MODULE_DEVICE_TABLE ( pci , trident_devices ) ;
2005-04-17 02:20:36 +04:00
static struct pci_driver tridentfb_pci_driver = {
2007-10-16 12:28:42 +04:00
. name = " tridentfb " ,
. id_table = trident_devices ,
. probe = trident_pci_probe ,
. remove = __devexit_p ( trident_pci_remove )
2005-04-17 02:20:36 +04:00
} ;
/*
* Parse user specified options ( ` video = trident : ' )
* example :
2007-10-16 12:28:42 +04:00
* video = trident : 800 x600 , bpp = 16 , noaccel
2005-04-17 02:20:36 +04:00
*/
# ifndef MODULE
2008-04-28 13:15:06 +04:00
static int __init tridentfb_setup ( char * options )
2005-04-17 02:20:36 +04:00
{
2007-10-16 12:28:42 +04:00
char * opt ;
2005-04-17 02:20:36 +04:00
if ( ! options | | ! * options )
return 0 ;
2007-10-16 12:28:42 +04:00
while ( ( opt = strsep ( & options , " , " ) ) ! = NULL ) {
if ( ! * opt )
continue ;
if ( ! strncmp ( opt , " noaccel " , 7 ) )
2005-04-17 02:20:36 +04:00
noaccel = 1 ;
2007-10-16 12:28:42 +04:00
else if ( ! strncmp ( opt , " fp " , 2 ) )
2005-04-17 02:20:36 +04:00
displaytype = DISPLAY_FP ;
2007-10-16 12:28:42 +04:00
else if ( ! strncmp ( opt , " crt " , 3 ) )
2005-04-17 02:20:36 +04:00
displaytype = DISPLAY_CRT ;
2007-10-16 12:28:42 +04:00
else if ( ! strncmp ( opt , " bpp= " , 4 ) )
bpp = simple_strtoul ( opt + 4 , NULL , 0 ) ;
else if ( ! strncmp ( opt , " center " , 6 ) )
2005-04-17 02:20:36 +04:00
center = 1 ;
2007-10-16 12:28:42 +04:00
else if ( ! strncmp ( opt , " stretch " , 7 ) )
2005-04-17 02:20:36 +04:00
stretch = 1 ;
2007-10-16 12:28:42 +04:00
else if ( ! strncmp ( opt , " memsize= " , 8 ) )
memsize = simple_strtoul ( opt + 8 , NULL , 0 ) ;
else if ( ! strncmp ( opt , " memdiff= " , 8 ) )
memdiff = simple_strtoul ( opt + 8 , NULL , 0 ) ;
else if ( ! strncmp ( opt , " nativex= " , 8 ) )
nativex = simple_strtoul ( opt + 8 , NULL , 0 ) ;
2005-04-17 02:20:36 +04:00
else
2008-04-28 13:15:06 +04:00
mode_option = opt ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
# endif
static int __init tridentfb_init ( void )
{
# ifndef MODULE
char * option = NULL ;
if ( fb_get_options ( " tridentfb " , & option ) )
return - ENODEV ;
tridentfb_setup ( option ) ;
# endif
output ( " Trident framebuffer %s initializing \n " , VERSION ) ;
return pci_register_driver ( & tridentfb_pci_driver ) ;
}
static void __exit tridentfb_exit ( void )
{
pci_unregister_driver ( & tridentfb_pci_driver ) ;
}
module_init ( tridentfb_init ) ;
module_exit ( tridentfb_exit ) ;
MODULE_AUTHOR ( " Jani Monoses <jani@iv.ro> " ) ;
MODULE_DESCRIPTION ( " Framebuffer driver for Trident cards " ) ;
MODULE_LICENSE ( " GPL " ) ;