2005-04-17 02:20:36 +04:00
/*
*
* tdfxfb . c
*
* Author : Hannu Mallat < hmallat @ cc . hut . fi >
*
2007-10-16 12:29:28 +04:00
* Copyright © 1999 Hannu Mallat
2005-04-17 02:20:36 +04:00
* All rights reserved
*
* Created : Thu Sep 23 18 : 17 : 43 1999 , hmallat
* Last modified : Tue Nov 2 21 : 19 : 47 1999 , hmallat
*
2009-04-07 06:01:03 +04:00
* I2C part copied from the i2c - voodoo3 . c driver by :
* Frodo Looijaard < frodol @ dds . nl > ,
* Philip Edelbrock < phil @ netroedge . com > ,
* Ralph Metzler < rjkm @ thp . uni - koeln . de > , and
* Mark D . Studebaker < mdsxyz123 @ yahoo . com >
*
2007-10-16 12:28:43 +04:00
* Lots of the information here comes from the Daryll Strauss ' Banshee
2005-04-17 02:20:36 +04:00
* patches to the XF86 server , and the rest comes from the 3 dfx
* Banshee specification . I ' m very much indebted to Daryll for his
* work on the X server .
*
* Voodoo3 support was contributed Harold Oga . Lots of additions
* ( proper acceleration , 24 bpp , hardware cursor ) and bug fixes by Attila
* Kesmarki . Thanks guys !
*
* Voodoo1 and Voodoo2 support aren ' t relevant to this driver as they
* behave very differently from the Voodoo3 / 4 / 5. For anyone wanting to
* use frame buffer on the Voodoo1 / 2 , see the sstfb driver ( which is
* located at http : //www.sourceforge.net/projects/sstfb).
2007-10-16 12:28:43 +04:00
*
2005-04-17 02:20:36 +04:00
* While I _am_ grateful to 3 Dfx for releasing the specs for Banshee ,
* I do wish the next version is a bit more complete . Without the XF86
* patches I couldn ' t have gotten even this far . . . for instance , the
* extensions to the VGA register set go completely unmentioned in the
* spec ! Also , lots of references are made to the ' SST core ' , but no
* spec is publicly available , AFAIK .
*
* The structure of this driver comes pretty much from the Permedia
* driver by Ilario Nardinocchi , which in turn is based on skeletonfb .
2007-10-16 12:28:43 +04:00
*
2005-04-17 02:20:36 +04:00
* TODO :
* - multihead support ( basically need to support an array of fb_infos )
* - support other architectures ( PPC , Alpha ) ; does the fact that the VGA
* core can be accessed only thru I / O ( not memory mapped ) complicate
* things ?
*
* Version history :
*
2007-10-16 12:29:28 +04:00
* 0.1 .4 ( released 2002 - 05 - 28 ) ported over to new fbdev api by James Simmons
2005-04-17 02:20:36 +04:00
*
2007-10-16 12:29:28 +04:00
* 0.1 .3 ( released 1999 - 11 - 02 ) added Attila ' s panning support , code
* reorg , hwcursor address page size alignment
tree-wide: fix assorted typos all over the place
That is "success", "unknown", "through", "performance", "[re|un]mapping"
, "access", "default", "reasonable", "[con]currently", "temperature"
, "channel", "[un]used", "application", "example","hierarchy", "therefore"
, "[over|under]flow", "contiguous", "threshold", "enough" and others.
Signed-off-by: André Goddard Rosa <andre.goddard@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2009-11-14 18:09:05 +03:00
* ( for mmapping both frame buffer and regs ) ,
2007-10-16 12:29:28 +04:00
* and my changes to get rid of hardcoded
* VGA i / o register locations ( uses PCI
* configuration info now )
* 0.1 .2 ( released 1999 - 10 - 19 ) added Attila Kesmarki ' s bug fixes and
* improvements
* 0.1 .1 ( released 1999 - 10 - 07 ) added Voodoo3 support by Harold Oga .
* 0.1 .0 ( released 1999 - 10 - 06 ) initial version
2005-04-17 02:20:36 +04:00
*
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/string.h>
# include <linux/mm.h>
# include <linux/slab.h>
# include <linux/fb.h>
# include <linux/init.h>
# include <linux/pci.h>
# include <asm/io.h>
# include <video/tdfx.h>
2008-04-28 13:15:47 +04:00
# define DPRINTK(a, b...) pr_debug("fb: %s: " a, __func__ , ## b)
2005-04-17 02:20:36 +04:00
2007-10-16 12:28:49 +04:00
# ifdef CONFIG_MTRR
# include <asm/mtrr.h>
# else
/* duplicate asm/mtrr.h defines to work on archs without mtrr */
# define MTRR_TYPE_WRCOMB 1
static inline int mtrr_add ( unsigned long base , unsigned long size ,
unsigned int type , char increment )
{
return - ENODEV ;
}
static inline int mtrr_del ( int reg , unsigned long base ,
unsigned long size )
{
return - ENODEV ;
}
# endif
2005-04-17 02:20:36 +04:00
# define BANSHEE_MAX_PIXCLOCK 270000
# define VOODOO3_MAX_PIXCLOCK 300000
# define VOODOO5_MAX_PIXCLOCK 350000
static struct fb_fix_screeninfo tdfx_fix __devinitdata = {
. type = FB_TYPE_PACKED_PIXELS ,
2007-10-16 12:28:43 +04:00
. visual = FB_VISUAL_PSEUDOCOLOR ,
2005-04-17 02:20:36 +04:00
. ypanstep = 1 ,
2007-10-16 12:28:43 +04:00
. ywrapstep = 1 ,
2005-04-17 02:20:36 +04:00
. accel = FB_ACCEL_3DFX_BANSHEE
} ;
static struct fb_var_screeninfo tdfx_var __devinitdata = {
/* "640x480, 8 bpp @ 60 Hz */
. xres = 640 ,
. yres = 480 ,
. xres_virtual = 640 ,
. yres_virtual = 1024 ,
2007-10-16 12:28:43 +04:00
. bits_per_pixel = 8 ,
2005-04-17 02:20:36 +04:00
. red = { 0 , 8 , 0 } ,
. blue = { 0 , 8 , 0 } ,
. green = { 0 , 8 , 0 } ,
. activate = FB_ACTIVATE_NOW ,
. height = - 1 ,
. width = - 1 ,
. accel_flags = FB_ACCELF_TEXT ,
. pixclock = 39722 ,
. left_margin = 40 ,
. right_margin = 24 ,
. upper_margin = 32 ,
. lower_margin = 11 ,
. hsync_len = 96 ,
. vsync_len = 2 ,
. vmode = FB_VMODE_NONINTERLACED
} ;
/*
* PCI driver prototypes
*/
static int __devinit tdfxfb_probe ( struct pci_dev * pdev ,
const struct pci_device_id * id ) ;
static void __devexit tdfxfb_remove ( struct pci_dev * pdev ) ;
static struct pci_device_id tdfxfb_id_table [ ] = {
{ PCI_VENDOR_ID_3DFX , PCI_DEVICE_ID_3DFX_BANSHEE ,
PCI_ANY_ID , PCI_ANY_ID , PCI_BASE_CLASS_DISPLAY < < 16 ,
0xff0000 , 0 } ,
{ PCI_VENDOR_ID_3DFX , PCI_DEVICE_ID_3DFX_VOODOO3 ,
PCI_ANY_ID , PCI_ANY_ID , PCI_BASE_CLASS_DISPLAY < < 16 ,
0xff0000 , 0 } ,
{ PCI_VENDOR_ID_3DFX , PCI_DEVICE_ID_3DFX_VOODOO5 ,
PCI_ANY_ID , PCI_ANY_ID , PCI_BASE_CLASS_DISPLAY < < 16 ,
0xff0000 , 0 } ,
{ 0 , }
} ;
static struct pci_driver tdfxfb_driver = {
. name = " tdfxfb " ,
2007-10-16 12:28:43 +04:00
. id_table = tdfxfb_id_table ,
. probe = tdfxfb_probe ,
. remove = __devexit_p ( tdfxfb_remove ) ,
2005-04-17 02:20:36 +04:00
} ;
MODULE_DEVICE_TABLE ( pci , tdfxfb_id_table ) ;
/*
2007-10-16 12:28:43 +04:00
* Driver data
2005-04-17 02:20:36 +04:00
*/
2007-10-16 12:28:48 +04:00
static int nopan ;
static int nowrap = 1 ; /* not implemented (yet) */
static int hwcursor = 1 ;
2007-10-16 12:28:49 +04:00
static char * mode_option __devinitdata ;
/* mtrr option */
2012-01-13 03:02:20 +04:00
static bool nomtrr __devinitdata ;
2005-04-17 02:20:36 +04:00
2007-10-16 12:28:43 +04:00
/* -------------------------------------------------------------------------
2007-10-16 12:29:28 +04:00
* Hardware - specific funcions
2005-04-17 02:20:36 +04:00
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2007-10-16 12:28:43 +04:00
static inline u8 vga_inb ( struct tdfx_par * par , u32 reg )
{
return inb ( par - > iobase + reg - 0x300 ) ;
2005-04-17 02:20:36 +04:00
}
2007-10-16 12:28:48 +04:00
2007-10-16 12:28:43 +04:00
static inline void vga_outb ( struct tdfx_par * par , u32 reg , u8 val )
{
outb ( val , par - > iobase + reg - 0x300 ) ;
2005-04-17 02:20:36 +04:00
}
2007-10-16 12:28:43 +04:00
static inline void gra_outb ( struct tdfx_par * par , u32 idx , u8 val )
{
vga_outb ( par , GRA_I , idx ) ;
2007-10-16 12:28:46 +04:00
wmb ( ) ;
2007-10-16 12:28:43 +04:00
vga_outb ( par , GRA_D , val ) ;
2007-10-16 12:28:46 +04:00
wmb ( ) ;
2005-04-17 02:20:36 +04:00
}
2007-10-16 12:28:43 +04:00
static inline void seq_outb ( struct tdfx_par * par , u32 idx , u8 val )
{
vga_outb ( par , SEQ_I , idx ) ;
2007-10-16 12:28:46 +04:00
wmb ( ) ;
2007-10-16 12:28:43 +04:00
vga_outb ( par , SEQ_D , val ) ;
2007-10-16 12:28:46 +04:00
wmb ( ) ;
2005-04-17 02:20:36 +04:00
}
2007-10-16 12:28:43 +04:00
static inline u8 seq_inb ( struct tdfx_par * par , u32 idx )
{
vga_outb ( par , SEQ_I , idx ) ;
2007-10-16 12:28:46 +04:00
mb ( ) ;
2007-10-16 12:28:43 +04:00
return vga_inb ( par , SEQ_D ) ;
2005-04-17 02:20:36 +04:00
}
2007-10-16 12:28:43 +04:00
static inline void crt_outb ( struct tdfx_par * par , u32 idx , u8 val )
{
vga_outb ( par , CRT_I , idx ) ;
2007-10-16 12:28:46 +04:00
wmb ( ) ;
2007-10-16 12:28:43 +04:00
vga_outb ( par , CRT_D , val ) ;
2007-10-16 12:28:46 +04:00
wmb ( ) ;
2005-04-17 02:20:36 +04:00
}
2007-10-16 12:28:43 +04:00
static inline u8 crt_inb ( struct tdfx_par * par , u32 idx )
{
vga_outb ( par , CRT_I , idx ) ;
2007-10-16 12:28:46 +04:00
mb ( ) ;
2007-10-16 12:28:43 +04:00
return vga_inb ( par , CRT_D ) ;
2005-04-17 02:20:36 +04:00
}
2007-10-16 12:28:43 +04:00
static inline void att_outb ( struct tdfx_par * par , u32 idx , u8 val )
2005-04-17 02:20:36 +04:00
{
unsigned char tmp ;
2007-10-16 12:28:43 +04:00
2005-04-17 02:20:36 +04:00
tmp = vga_inb ( par , IS1_R ) ;
vga_outb ( par , ATT_IW , idx ) ;
vga_outb ( par , ATT_IW , val ) ;
}
static inline void vga_disable_video ( struct tdfx_par * par )
{
unsigned char s ;
s = seq_inb ( par , 0x01 ) | 0x20 ;
seq_outb ( par , 0x00 , 0x01 ) ;
seq_outb ( par , 0x01 , s ) ;
seq_outb ( par , 0x00 , 0x03 ) ;
}
static inline void vga_enable_video ( struct tdfx_par * par )
{
unsigned char s ;
s = seq_inb ( par , 0x01 ) & 0xdf ;
seq_outb ( par , 0x00 , 0x01 ) ;
seq_outb ( par , 0x01 , s ) ;
seq_outb ( par , 0x00 , 0x03 ) ;
}
static inline void vga_enable_palette ( struct tdfx_par * par )
{
vga_inb ( par , IS1_R ) ;
2007-10-16 12:28:46 +04:00
mb ( ) ;
2005-04-17 02:20:36 +04:00
vga_outb ( par , ATT_IW , 0x20 ) ;
}
2007-10-16 12:28:43 +04:00
static inline u32 tdfx_inl ( struct tdfx_par * par , unsigned int reg )
2005-04-17 02:20:36 +04:00
{
return readl ( par - > regbase_virt + reg ) ;
}
static inline void tdfx_outl ( struct tdfx_par * par , unsigned int reg , u32 val )
{
writel ( val , par - > regbase_virt + reg ) ;
}
static inline void banshee_make_room ( struct tdfx_par * par , int size )
{
/* Note: The Voodoo3's onboard FIFO has 32 slots. This loop
* won ' t quit if you ask for more . */
2007-10-16 12:29:26 +04:00
while ( ( tdfx_inl ( par , STATUS ) & 0x1f ) < size - 1 )
cpu_relax ( ) ;
2005-04-17 02:20:36 +04:00
}
2007-10-16 12:29:28 +04:00
2005-04-17 02:20:36 +04:00
static int banshee_wait_idle ( struct fb_info * info )
{
2006-01-10 07:53:11 +03:00
struct tdfx_par * par = info - > par ;
2005-04-17 02:20:36 +04:00
int i = 0 ;
banshee_make_room ( par , 1 ) ;
tdfx_outl ( par , COMMAND_3D , COMMAND_3D_NOP ) ;
2007-10-16 12:28:48 +04:00
do {
2007-10-16 12:29:28 +04:00
if ( ( tdfx_inl ( par , STATUS ) & STATUS_BUSY ) = = 0 )
i + + ;
2007-10-16 12:28:48 +04:00
} while ( i < 3 ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/*
2007-10-16 12:28:43 +04:00
* Set the color of a palette entry in 8 bpp mode
2005-04-17 02:20:36 +04:00
*/
static inline void do_setpalentry ( struct tdfx_par * par , unsigned regno , u32 c )
2007-10-16 12:29:28 +04:00
{
2005-04-17 02:20:36 +04:00
banshee_make_room ( par , 2 ) ;
tdfx_outl ( par , DACADDR , regno ) ;
2007-10-16 12:28:46 +04:00
/* read after write makes it working */
tdfx_inl ( par , DACADDR ) ;
2005-04-17 02:20:36 +04:00
tdfx_outl ( par , DACDATA , c ) ;
}
2007-10-16 12:28:43 +04:00
static u32 do_calc_pll ( int freq , int * freq_out )
2005-04-17 02:20:36 +04:00
{
2005-05-01 19:59:24 +04:00
int m , n , k , best_m , best_n , best_k , best_error ;
2005-04-17 02:20:36 +04:00
int fref = 14318 ;
2007-10-16 12:28:43 +04:00
2005-04-17 02:20:36 +04:00
best_error = freq ;
best_n = best_m = best_k = 0 ;
2005-05-01 19:59:24 +04:00
for ( k = 3 ; k > = 0 ; k - - ) {
for ( m = 63 ; m > = 0 ; m - - ) {
/*
* Estimate value of n that produces target frequency
* with current m and k
*/
2007-10-16 12:28:48 +04:00
int n_estimated = ( ( freq * ( m + 2 ) < < k ) / fref ) - 2 ;
2005-05-01 19:59:24 +04:00
/* Search neighborhood of estimated n */
2007-10-16 12:28:48 +04:00
for ( n = max ( 0 , n_estimated ) ;
n < = min ( 255 , n_estimated + 1 ) ;
n + + ) {
2005-05-01 19:59:24 +04:00
/*
* Calculate PLL freqency with current m , k and
* estimated n
*/
2007-10-16 12:28:48 +04:00
int f = ( fref * ( n + 2 ) / ( m + 2 ) ) > > k ;
2007-10-16 12:28:43 +04:00
int error = abs ( f - freq ) ;
2005-05-01 19:59:24 +04:00
/*
2007-10-16 12:28:43 +04:00
* If this is the closest we ' ve come to the
2005-05-01 19:59:24 +04:00
* target frequency then remember n , m and k
*/
2007-10-16 12:28:43 +04:00
if ( error < best_error ) {
2005-05-01 19:59:24 +04:00
best_error = error ;
2007-10-16 12:28:43 +04:00
best_n = n ;
best_m = m ;
best_k = k ;
2005-04-17 02:20:36 +04:00
}
}
}
}
2005-05-01 19:59:24 +04:00
2005-04-17 02:20:36 +04:00
n = best_n ;
m = best_m ;
k = best_k ;
2007-10-16 12:28:48 +04:00
* freq_out = ( fref * ( n + 2 ) / ( m + 2 ) ) > > k ;
2005-05-01 19:59:24 +04:00
2005-04-17 02:20:36 +04:00
return ( n < < 8 ) | ( m < < 2 ) | k ;
}
2007-10-16 12:28:43 +04:00
static void do_write_regs ( struct fb_info * info , struct banshee_reg * reg )
2005-04-17 02:20:36 +04:00
{
2006-01-10 07:53:11 +03:00
struct tdfx_par * par = info - > par ;
2005-04-17 02:20:36 +04:00
int i ;
banshee_wait_idle ( info ) ;
tdfx_outl ( par , MISCINIT1 , tdfx_inl ( par , MISCINIT1 ) | 0x01 ) ;
crt_outb ( par , 0x11 , crt_inb ( par , 0x11 ) & 0x7f ) ; /* CRT unprotect */
banshee_make_room ( par , 3 ) ;
2007-10-16 12:28:43 +04:00
tdfx_outl ( par , VGAINIT1 , reg - > vgainit1 & 0x001FFFFF ) ;
tdfx_outl ( par , VIDPROCCFG , reg - > vidcfg & ~ 0x00000001 ) ;
2005-04-17 02:20:36 +04:00
#if 0
tdfx_outl ( par , PLLCTRL1 , reg - > mempll ) ;
tdfx_outl ( par , PLLCTRL2 , reg - > gfxpll ) ;
# endif
2007-10-16 12:28:43 +04:00
tdfx_outl ( par , PLLCTRL0 , reg - > vidpll ) ;
2005-04-17 02:20:36 +04:00
vga_outb ( par , MISC_W , reg - > misc [ 0x00 ] | 0x01 ) ;
for ( i = 0 ; i < 5 ; i + + )
seq_outb ( par , i , reg - > seq [ i ] ) ;
for ( i = 0 ; i < 25 ; i + + )
crt_outb ( par , i , reg - > crt [ i ] ) ;
for ( i = 0 ; i < 9 ; i + + )
gra_outb ( par , i , reg - > gra [ i ] ) ;
for ( i = 0 ; i < 21 ; i + + )
att_outb ( par , i , reg - > att [ i ] ) ;
crt_outb ( par , 0x1a , reg - > ext [ 0 ] ) ;
crt_outb ( par , 0x1b , reg - > ext [ 1 ] ) ;
vga_enable_palette ( par ) ;
vga_enable_video ( par ) ;
2007-10-16 12:28:48 +04:00
banshee_make_room ( par , 9 ) ;
2007-10-16 12:28:43 +04:00
tdfx_outl ( par , VGAINIT0 , reg - > vgainit0 ) ;
tdfx_outl ( par , DACMODE , reg - > dacmode ) ;
tdfx_outl ( par , VIDDESKSTRIDE , reg - > stride ) ;
2007-10-16 12:28:48 +04:00
tdfx_outl ( par , HWCURPATADDR , reg - > curspataddr ) ;
2007-10-16 12:28:43 +04:00
tdfx_outl ( par , VIDSCREENSIZE , reg - > screensize ) ;
tdfx_outl ( par , VIDDESKSTART , reg - > startaddr ) ;
tdfx_outl ( par , VIDPROCCFG , reg - > vidcfg ) ;
tdfx_outl ( par , VGAINIT1 , reg - > vgainit1 ) ;
tdfx_outl ( par , MISCINIT0 , reg - > miscinit0 ) ;
banshee_make_room ( par , 8 ) ;
2007-10-16 12:28:48 +04:00
tdfx_outl ( par , SRCBASE , reg - > startaddr ) ;
tdfx_outl ( par , DSTBASE , reg - > startaddr ) ;
2007-10-16 12:28:43 +04:00
tdfx_outl ( par , COMMANDEXTRA_2D , 0 ) ;
tdfx_outl ( par , CLIP0MIN , 0 ) ;
tdfx_outl ( par , CLIP0MAX , 0x0fff0fff ) ;
tdfx_outl ( par , CLIP1MIN , 0 ) ;
tdfx_outl ( par , CLIP1MAX , 0x0fff0fff ) ;
tdfx_outl ( par , SRCXY , 0 ) ;
2005-04-17 02:20:36 +04:00
banshee_wait_idle ( info ) ;
}
2007-10-16 12:28:43 +04:00
static unsigned long do_lfb_size ( struct tdfx_par * par , unsigned short dev_id )
2005-04-17 02:20:36 +04:00
{
2007-10-16 12:28:48 +04:00
u32 draminit0 = tdfx_inl ( par , DRAMINIT0 ) ;
u32 draminit1 = tdfx_inl ( par , DRAMINIT1 ) ;
2005-05-01 19:59:25 +04:00
u32 miscinit1 ;
2007-10-16 12:28:48 +04:00
int num_chips = ( draminit0 & DRAMINIT0_SGRAM_NUM ) ? 8 : 4 ;
2005-05-01 19:59:25 +04:00
int chip_size ; /* in MB */
2007-10-16 12:28:48 +04:00
int has_sgram = draminit1 & DRAMINIT1_MEM_SDRAM ;
2007-10-16 12:28:43 +04:00
2005-05-01 19:59:25 +04:00
if ( dev_id < PCI_DEVICE_ID_3DFX_VOODOO5 ) {
/* Banshee/Voodoo3 */
2007-10-16 12:28:43 +04:00
chip_size = 2 ;
2008-09-03 01:36:03 +04:00
if ( has_sgram & & ! ( draminit0 & DRAMINIT0_SGRAM_TYPE ) )
2007-10-16 12:28:48 +04:00
chip_size = 1 ;
2005-04-17 02:20:36 +04:00
} else {
/* Voodoo4/5 */
2005-05-01 19:59:25 +04:00
has_sgram = 0 ;
2007-10-16 12:28:48 +04:00
chip_size = draminit0 & DRAMINIT0_SGRAM_TYPE_MASK ;
chip_size = 1 < < ( chip_size > > DRAMINIT0_SGRAM_TYPE_SHIFT ) ;
2005-05-01 19:59:25 +04:00
}
/* disable block writes for SDRAM */
2005-04-17 02:20:36 +04:00
miscinit1 = tdfx_inl ( par , MISCINIT1 ) ;
2005-05-01 19:59:25 +04:00
miscinit1 | = has_sgram ? 0 : MISCINIT1_2DBLOCK_DIS ;
2005-04-17 02:20:36 +04:00
miscinit1 | = MISCINIT1_CLUT_INV ;
2007-10-16 12:28:43 +04:00
banshee_make_room ( par , 1 ) ;
2005-04-17 02:20:36 +04:00
tdfx_outl ( par , MISCINIT1 , miscinit1 ) ;
2007-10-16 12:28:48 +04:00
return num_chips * chip_size * 1024l * 1024 ;
2005-04-17 02:20:36 +04:00
}
/* ------------------------------------------------------------------------- */
2007-10-16 12:28:43 +04:00
static int tdfxfb_check_var ( struct fb_var_screeninfo * var , struct fb_info * info )
2005-04-17 02:20:36 +04:00
{
2006-01-10 07:53:11 +03:00
struct tdfx_par * par = info - > par ;
2005-04-17 02:20:36 +04:00
u32 lpitch ;
if ( var - > bits_per_pixel ! = 8 & & var - > bits_per_pixel ! = 16 & &
var - > bits_per_pixel ! = 24 & & var - > bits_per_pixel ! = 32 ) {
DPRINTK ( " depth not supported: %u \n " , var - > bits_per_pixel ) ;
return - EINVAL ;
}
if ( var - > xres ! = var - > xres_virtual )
var - > xres_virtual = var - > xres ;
if ( var - > yres > var - > yres_virtual )
var - > yres_virtual = var - > yres ;
if ( var - > xoffset ) {
DPRINTK ( " xoffset not supported \n " ) ;
return - EINVAL ;
}
2007-10-16 12:28:48 +04:00
var - > yoffset = 0 ;
2005-04-17 02:20:36 +04:00
2007-10-16 12:29:28 +04:00
/*
* Banshee doesn ' t support interlace , but Voodoo4 / 5 and probably
* Voodoo3 do .
* no direct information about device id now ?
* use max_pixclock for this . . .
*/
2005-04-17 02:20:36 +04:00
if ( ( ( var - > vmode & FB_VMODE_MASK ) = = FB_VMODE_INTERLACED ) & &
2007-10-16 12:28:43 +04:00
( par - > max_pixclock < VOODOO3_MAX_PIXCLOCK ) ) {
2005-04-17 02:20:36 +04:00
DPRINTK ( " interlace not supported \n " ) ;
return - EINVAL ;
}
2009-04-07 06:01:04 +04:00
if ( info - > monspecs . hfmax & & info - > monspecs . vfmax & &
info - > monspecs . dclkmax & & fb_validate_mode ( var , info ) < 0 ) {
DPRINTK ( " mode outside monitor's specs \n " ) ;
return - EINVAL ;
}
2005-04-17 02:20:36 +04:00
var - > xres = ( var - > xres + 15 ) & ~ 15 ; /* could sometimes be 8 */
2007-10-16 12:28:43 +04:00
lpitch = var - > xres * ( ( var - > bits_per_pixel + 7 ) > > 3 ) ;
2005-04-17 02:20:36 +04:00
if ( var - > xres < 320 | | var - > xres > 2048 ) {
DPRINTK ( " width not supported: %u \n " , var - > xres ) ;
return - EINVAL ;
}
2007-10-16 12:28:43 +04:00
2005-04-17 02:20:36 +04:00
if ( var - > yres < 200 | | var - > yres > 2048 ) {
DPRINTK ( " height not supported: %u \n " , var - > yres ) ;
return - EINVAL ;
}
2007-10-16 12:28:43 +04:00
2005-04-17 02:20:36 +04:00
if ( lpitch * var - > yres_virtual > info - > fix . smem_len ) {
2007-10-16 12:28:43 +04:00
var - > yres_virtual = info - > fix . smem_len / lpitch ;
2005-04-17 02:20:36 +04:00
if ( var - > yres_virtual < var - > yres ) {
DPRINTK ( " no memory for screen (%ux%ux%u) \n " ,
2007-10-16 12:28:43 +04:00
var - > xres , var - > yres_virtual ,
var - > bits_per_pixel ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
}
2007-10-16 12:28:43 +04:00
2005-04-17 02:20:36 +04:00
if ( PICOS2KHZ ( var - > pixclock ) > par - > max_pixclock ) {
2007-10-16 12:28:43 +04:00
DPRINTK ( " pixclock too high (%ldKHz) \n " ,
PICOS2KHZ ( var - > pixclock ) ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
2007-10-16 12:28:45 +04:00
var - > transp . offset = 0 ;
var - > transp . length = 0 ;
2007-10-16 12:28:43 +04:00
switch ( var - > bits_per_pixel ) {
case 8 :
2007-10-16 12:29:28 +04:00
var - > red . length = 8 ;
var - > red . offset = 0 ;
var - > green = var - > red ;
var - > blue = var - > red ;
2007-10-16 12:28:43 +04:00
break ;
case 16 :
var - > red . offset = 11 ;
var - > red . length = 5 ;
var - > green . offset = 5 ;
var - > green . length = 6 ;
var - > blue . offset = 0 ;
var - > blue . length = 5 ;
break ;
case 32 :
2007-10-16 12:28:45 +04:00
var - > transp . offset = 24 ;
var - > transp . length = 8 ;
2007-10-16 12:28:43 +04:00
case 24 :
2007-10-16 12:28:45 +04:00
var - > red . offset = 16 ;
var - > green . offset = 8 ;
var - > blue . offset = 0 ;
2007-10-16 12:28:43 +04:00
var - > red . length = var - > green . length = var - > blue . length = 8 ;
break ;
2005-04-17 02:20:36 +04:00
}
2007-10-16 12:29:28 +04:00
var - > width = - 1 ;
var - > height = - 1 ;
2007-10-16 12:28:43 +04:00
2005-04-17 02:20:36 +04:00
var - > accel_flags = FB_ACCELF_TEXT ;
2007-10-16 12:28:43 +04:00
DPRINTK ( " Checking graphics mode at %dx%d depth %d \n " ,
var - > xres , var - > yres , var - > bits_per_pixel ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static int tdfxfb_set_par ( struct fb_info * info )
{
2006-01-10 07:53:11 +03:00
struct tdfx_par * par = info - > par ;
2007-10-16 12:28:48 +04:00
u32 hdispend = info - > var . xres ;
u32 hsyncsta = hdispend + info - > var . right_margin ;
u32 hsyncend = hsyncsta + info - > var . hsync_len ;
u32 htotal = hsyncend + info - > var . left_margin ;
2005-04-17 02:20:36 +04:00
u32 hd , hs , he , ht , hbs , hbe ;
u32 vd , vs , ve , vt , vbs , vbe ;
struct banshee_reg reg ;
int fout , freq ;
2007-10-16 12:28:48 +04:00
u32 wd ;
u32 cpp = ( info - > var . bits_per_pixel + 7 ) > > 3 ;
2007-10-16 12:28:43 +04:00
2005-04-17 02:20:36 +04:00
memset ( & reg , 0 , sizeof ( reg ) ) ;
2007-10-16 12:28:43 +04:00
reg . vidcfg = VIDCFG_VIDPROC_ENABLE | VIDCFG_DESK_ENABLE |
VIDCFG_CURS_X11 |
( ( cpp - 1 ) < < VIDCFG_PIXFMT_SHIFT ) |
( cpp ! = 1 ? VIDCFG_CLUT_BYPASS : 0 ) ;
2005-04-17 02:20:36 +04:00
/* PLL settings */
freq = PICOS2KHZ ( info - > var . pixclock ) ;
2007-10-16 12:28:43 +04:00
reg . vidcfg & = ~ VIDCFG_2X ;
2005-04-17 02:20:36 +04:00
2007-10-16 12:28:43 +04:00
if ( freq > par - > max_pixclock / 2 ) {
2005-04-17 02:20:36 +04:00
freq = freq > par - > max_pixclock ? par - > max_pixclock : freq ;
reg . dacmode | = DACMODE_2X ;
reg . vidcfg | = VIDCFG_2X ;
hdispend > > = 1 ;
hsyncsta > > = 1 ;
hsyncend > > = 1 ;
htotal > > = 1 ;
}
2007-10-16 12:28:43 +04:00
2007-10-16 12:29:28 +04:00
wd = ( hdispend > > 3 ) - 1 ;
hd = wd ;
2005-04-17 02:20:36 +04:00
hs = ( hsyncsta > > 3 ) - 1 ;
he = ( hsyncend > > 3 ) - 1 ;
ht = ( htotal > > 3 ) - 1 ;
hbs = hd ;
hbe = ht ;
if ( ( info - > var . vmode & FB_VMODE_MASK ) = = FB_VMODE_DOUBLE ) {
2007-10-16 12:29:28 +04:00
vd = ( info - > var . yres < < 1 ) - 1 ;
2005-04-17 02:20:36 +04:00
vs = vd + ( info - > var . lower_margin < < 1 ) ;
ve = vs + ( info - > var . vsync_len < < 1 ) ;
2007-10-16 12:29:28 +04:00
vt = ve + ( info - > var . upper_margin < < 1 ) - 1 ;
2007-10-16 12:28:48 +04:00
reg . screensize = info - > var . xres | ( info - > var . yres < < 13 ) ;
reg . vidcfg | = VIDCFG_HALF_MODE ;
reg . crt [ 0x09 ] = 0x80 ;
2005-04-17 02:20:36 +04:00
} else {
2007-10-16 12:29:28 +04:00
vd = info - > var . yres - 1 ;
2005-04-17 02:20:36 +04:00
vs = vd + info - > var . lower_margin ;
ve = vs + info - > var . vsync_len ;
2007-10-16 12:29:28 +04:00
vt = ve + info - > var . upper_margin - 1 ;
2007-10-16 12:28:48 +04:00
reg . screensize = info - > var . xres | ( info - > var . yres < < 12 ) ;
reg . vidcfg & = ~ VIDCFG_HALF_MODE ;
2005-04-17 02:20:36 +04:00
}
2007-10-16 12:29:28 +04:00
vbs = vd ;
vbe = vt ;
2007-10-16 12:28:43 +04:00
2005-04-17 02:20:36 +04:00
/* this is all pretty standard VGA register stuffing */
2007-10-16 12:28:43 +04:00
reg . misc [ 0x00 ] = 0x0f |
2005-04-17 02:20:36 +04:00
( info - > var . xres < 400 ? 0xa0 :
info - > var . xres < 480 ? 0x60 :
info - > var . xres < 768 ? 0xe0 : 0x20 ) ;
2007-10-16 12:28:43 +04:00
2005-04-17 02:20:36 +04:00
reg . gra [ 0x05 ] = 0x40 ;
reg . gra [ 0x06 ] = 0x05 ;
reg . gra [ 0x07 ] = 0x0f ;
reg . gra [ 0x08 ] = 0xff ;
reg . att [ 0x00 ] = 0x00 ;
reg . att [ 0x01 ] = 0x01 ;
reg . att [ 0x02 ] = 0x02 ;
reg . att [ 0x03 ] = 0x03 ;
reg . att [ 0x04 ] = 0x04 ;
reg . att [ 0x05 ] = 0x05 ;
reg . att [ 0x06 ] = 0x06 ;
reg . att [ 0x07 ] = 0x07 ;
reg . att [ 0x08 ] = 0x08 ;
reg . att [ 0x09 ] = 0x09 ;
reg . att [ 0x0a ] = 0x0a ;
reg . att [ 0x0b ] = 0x0b ;
reg . att [ 0x0c ] = 0x0c ;
reg . att [ 0x0d ] = 0x0d ;
reg . att [ 0x0e ] = 0x0e ;
reg . att [ 0x0f ] = 0x0f ;
reg . att [ 0x10 ] = 0x41 ;
reg . att [ 0x12 ] = 0x0f ;
reg . seq [ 0x00 ] = 0x03 ;
reg . seq [ 0x01 ] = 0x01 ; /* fixme: clkdiv2? */
reg . seq [ 0x02 ] = 0x0f ;
reg . seq [ 0x03 ] = 0x00 ;
reg . seq [ 0x04 ] = 0x0e ;
reg . crt [ 0x00 ] = ht - 4 ;
reg . crt [ 0x01 ] = hd ;
reg . crt [ 0x02 ] = hbs ;
reg . crt [ 0x03 ] = 0x80 | ( hbe & 0x1f ) ;
reg . crt [ 0x04 ] = hs ;
2007-10-16 12:28:43 +04:00
reg . crt [ 0x05 ] = ( ( hbe & 0x20 ) < < 2 ) | ( he & 0x1f ) ;
2005-04-17 02:20:36 +04:00
reg . crt [ 0x06 ] = vt ;
reg . crt [ 0x07 ] = ( ( vs & 0x200 ) > > 2 ) |
( ( vd & 0x200 ) > > 3 ) |
( ( vt & 0x200 ) > > 4 ) | 0x10 |
( ( vbs & 0x100 ) > > 5 ) |
2007-10-16 12:28:43 +04:00
( ( vs & 0x100 ) > > 6 ) |
( ( vd & 0x100 ) > > 7 ) |
( ( vt & 0x100 ) > > 8 ) ;
2007-10-16 12:28:48 +04:00
reg . crt [ 0x09 ] | = 0x40 | ( ( vbs & 0x200 ) > > 4 ) ;
2005-04-17 02:20:36 +04:00
reg . crt [ 0x10 ] = vs ;
2007-10-16 12:28:43 +04:00
reg . crt [ 0x11 ] = ( ve & 0x0f ) | 0x20 ;
2005-04-17 02:20:36 +04:00
reg . crt [ 0x12 ] = vd ;
reg . crt [ 0x13 ] = wd ;
reg . crt [ 0x15 ] = vbs ;
2007-10-16 12:28:43 +04:00
reg . crt [ 0x16 ] = vbe + 1 ;
2005-04-17 02:20:36 +04:00
reg . crt [ 0x17 ] = 0xc3 ;
reg . crt [ 0x18 ] = 0xff ;
2007-10-16 12:28:43 +04:00
2005-04-17 02:20:36 +04:00
/* Banshee's nonvga stuff */
2007-10-16 12:28:43 +04:00
reg . ext [ 0x00 ] = ( ( ( ht & 0x100 ) > > 8 ) |
( ( hd & 0x100 ) > > 6 ) |
2005-04-17 02:20:36 +04:00
( ( hbs & 0x100 ) > > 4 ) |
2007-10-16 12:28:43 +04:00
( ( hbe & 0x40 ) > > 1 ) |
( ( hs & 0x100 ) > > 2 ) |
( ( he & 0x20 ) < < 2 ) ) ;
reg . ext [ 0x01 ] = ( ( ( vt & 0x400 ) > > 10 ) |
( ( vd & 0x400 ) > > 8 ) |
( ( vbs & 0x400 ) > > 6 ) |
( ( vbe & 0x400 ) > > 4 ) ) ;
reg . vgainit0 = VGAINIT0_8BIT_DAC |
2005-04-17 02:20:36 +04:00
VGAINIT0_EXT_ENABLE |
VGAINIT0_WAKEUP_3C3 |
VGAINIT0_ALT_READBACK |
VGAINIT0_EXTSHIFTOUT ;
reg . vgainit1 = tdfx_inl ( par , VGAINIT1 ) & 0x1fffff ;
2007-10-16 12:28:48 +04:00
if ( hwcursor )
reg . curspataddr = info - > fix . smem_len ;
2005-04-17 02:20:36 +04:00
reg . cursloc = 0 ;
2007-10-16 12:28:43 +04:00
reg . cursc0 = 0 ;
2005-04-17 02:20:36 +04:00
reg . cursc1 = 0xffffff ;
2007-10-16 12:28:43 +04:00
2005-04-17 02:20:36 +04:00
reg . stride = info - > var . xres * cpp ;
2007-10-16 12:28:48 +04:00
reg . startaddr = info - > var . yoffset * reg . stride
+ info - > var . xoffset * cpp ;
2005-04-17 02:20:36 +04:00
reg . vidpll = do_calc_pll ( freq , & fout ) ;
#if 0
reg . mempll = do_calc_pll ( . . . , & fout ) ;
reg . gfxpll = do_calc_pll ( . . . , & fout ) ;
# endif
if ( ( info - > var . vmode & FB_VMODE_MASK ) = = FB_VMODE_INTERLACED )
reg . vidcfg | = VIDCFG_INTERLACE ;
reg . miscinit0 = tdfx_inl ( par , MISCINIT0 ) ;
# if defined(__BIG_ENDIAN)
switch ( info - > var . bits_per_pixel ) {
2007-10-16 12:28:43 +04:00
case 8 :
case 24 :
reg . miscinit0 & = ~ ( 1 < < 30 ) ;
reg . miscinit0 & = ~ ( 1 < < 31 ) ;
break ;
case 16 :
reg . miscinit0 | = ( 1 < < 30 ) ;
reg . miscinit0 | = ( 1 < < 31 ) ;
break ;
case 32 :
reg . miscinit0 | = ( 1 < < 30 ) ;
reg . miscinit0 & = ~ ( 1 < < 31 ) ;
break ;
2005-04-17 02:20:36 +04:00
}
2007-10-16 12:28:43 +04:00
# endif
2005-04-17 02:20:36 +04:00
do_write_regs ( info , & reg ) ;
/* Now change fb_fix_screeninfo according to changes in par */
2007-10-16 12:28:48 +04:00
info - > fix . line_length = reg . stride ;
2007-10-16 12:28:43 +04:00
info - > fix . visual = ( info - > var . bits_per_pixel = = 8 )
2005-04-17 02:20:36 +04:00
? FB_VISUAL_PSEUDOCOLOR
: FB_VISUAL_TRUECOLOR ;
2007-10-16 12:28:43 +04:00
DPRINTK ( " Graphics mode is now set at %dx%d depth %d \n " ,
info - > var . xres , info - > var . yres , info - > var . bits_per_pixel ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
/* A handy macro shamelessly pinched from matroxfb */
2007-10-16 12:29:28 +04:00
# define CNVT_TOHW(val, width) ((((val) << (width)) + 0x7FFF - (val)) >> 16)
2005-04-17 02:20:36 +04:00
2007-10-16 12:28:43 +04:00
static int tdfxfb_setcolreg ( unsigned regno , unsigned red , unsigned green ,
unsigned blue , unsigned transp ,
struct fb_info * info )
2005-04-17 02:20:36 +04:00
{
2006-01-10 07:53:11 +03:00
struct tdfx_par * par = info - > par ;
2005-04-17 02:20:36 +04:00
u32 rgbcol ;
2007-10-16 12:28:43 +04:00
if ( regno > = info - > cmap . len | | regno > 255 )
return 1 ;
2007-10-16 12:28:46 +04:00
/* grayscale works only partially under directcolor */
if ( info - > var . grayscale ) {
/* grayscale = 0.30*R + 0.59*G + 0.11*B */
2007-10-16 12:29:28 +04:00
blue = ( red * 77 + green * 151 + blue * 28 ) > > 8 ;
green = blue ;
red = blue ;
2007-10-16 12:28:46 +04:00
}
2005-04-17 02:20:36 +04:00
switch ( info - > fix . visual ) {
2006-03-11 14:27:26 +03:00
case FB_VISUAL_PSEUDOCOLOR :
2007-10-16 12:29:28 +04:00
rgbcol = ( ( ( u32 ) red & 0xff00 ) < < 8 ) |
( ( ( u32 ) green & 0xff00 ) < < 0 ) |
( ( ( u32 ) blue & 0xff00 ) > > 8 ) ;
2006-03-11 14:27:26 +03:00
do_setpalentry ( par , regno , rgbcol ) ;
break ;
/* Truecolor has no hardware color palettes. */
case FB_VISUAL_TRUECOLOR :
if ( regno < 16 ) {
2007-10-16 12:28:43 +04:00
rgbcol = ( CNVT_TOHW ( red , info - > var . red . length ) < <
2006-01-10 07:53:11 +03:00
info - > var . red . offset ) |
2007-10-16 12:28:43 +04:00
( CNVT_TOHW ( green , info - > var . green . length ) < <
2006-03-11 14:27:26 +03:00
info - > var . green . offset ) |
2007-10-16 12:28:43 +04:00
( CNVT_TOHW ( blue , info - > var . blue . length ) < <
2006-03-11 14:27:26 +03:00
info - > var . blue . offset ) |
2007-10-16 12:28:43 +04:00
( CNVT_TOHW ( transp , info - > var . transp . length ) < <
2006-03-11 14:27:26 +03:00
info - > var . transp . offset ) ;
par - > palette [ regno ] = rgbcol ;
}
break ;
default :
DPRINTK ( " bad depth %u \n " , info - > var . bits_per_pixel ) ;
break ;
2005-04-17 02:20:36 +04:00
}
2006-03-11 14:27:26 +03:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */
static int tdfxfb_blank ( int blank , struct fb_info * info )
2007-10-16 12:28:43 +04:00
{
2006-01-10 07:53:11 +03:00
struct tdfx_par * par = info - > par ;
2007-10-16 12:28:48 +04:00
int vgablank = 1 ;
u32 dacmode = tdfx_inl ( par , DACMODE ) ;
2005-04-17 02:20:36 +04:00
2007-10-16 12:28:48 +04:00
dacmode & = ~ ( BIT ( 1 ) | BIT ( 3 ) ) ;
2005-04-17 02:20:36 +04:00
switch ( blank ) {
2007-10-16 12:28:43 +04:00
case FB_BLANK_UNBLANK : /* Screen: On; HSync: On, VSync: On */
vgablank = 0 ;
break ;
case FB_BLANK_NORMAL : /* Screen: Off; HSync: On, VSync: On */
break ;
case FB_BLANK_VSYNC_SUSPEND : /* Screen: Off; HSync: On, VSync: Off */
2007-10-16 12:28:48 +04:00
dacmode | = BIT ( 3 ) ;
2007-10-16 12:28:43 +04:00
break ;
case FB_BLANK_HSYNC_SUSPEND : /* Screen: Off; HSync: Off, VSync: On */
2007-10-16 12:28:48 +04:00
dacmode | = BIT ( 1 ) ;
2007-10-16 12:28:43 +04:00
break ;
case FB_BLANK_POWERDOWN : /* Screen: Off; HSync: Off, VSync: Off */
2007-10-16 12:28:48 +04:00
dacmode | = BIT ( 1 ) | BIT ( 3 ) ;
2007-10-16 12:28:43 +04:00
break ;
2005-04-17 02:20:36 +04:00
}
2007-10-16 12:28:43 +04:00
banshee_make_room ( par , 1 ) ;
2005-04-17 02:20:36 +04:00
tdfx_outl ( par , DACMODE , dacmode ) ;
2007-10-16 12:28:43 +04:00
if ( vgablank )
2005-04-17 02:20:36 +04:00
vga_disable_video ( par ) ;
else
vga_enable_video ( par ) ;
return 0 ;
}
2007-10-16 12:28:43 +04:00
/*
2005-04-17 02:20:36 +04:00
* Set the starting position of the visible screen to var - > yoffset
2007-10-16 12:28:43 +04:00
*/
2005-04-17 02:20:36 +04:00
static int tdfxfb_pan_display ( struct fb_var_screeninfo * var ,
2007-10-16 12:28:43 +04:00
struct fb_info * info )
2005-04-17 02:20:36 +04:00
{
2006-01-10 07:53:11 +03:00
struct tdfx_par * par = info - > par ;
2007-10-16 12:28:48 +04:00
u32 addr = var - > yoffset * info - > fix . line_length ;
2005-04-17 02:20:36 +04:00
2008-07-24 08:31:24 +04:00
if ( nopan | | var - > xoffset )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
banshee_make_room ( par , 1 ) ;
tdfx_outl ( par , VIDDESKSTART , addr ) ;
2007-10-16 12:28:43 +04:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
# ifdef CONFIG_FB_3DFX_ACCEL
/*
2007-10-16 12:28:43 +04:00
* FillRect 2 D command ( solidfill or invert ( via ROP_XOR ) )
2005-04-17 02:20:36 +04:00
*/
2007-10-16 12:28:43 +04:00
static void tdfxfb_fillrect ( struct fb_info * info ,
const struct fb_fillrect * rect )
2005-04-17 02:20:36 +04:00
{
2006-01-10 07:53:11 +03:00
struct tdfx_par * par = info - > par ;
2005-04-17 02:20:36 +04:00
u32 bpp = info - > var . bits_per_pixel ;
u32 stride = info - > fix . line_length ;
2007-10-16 12:29:28 +04:00
u32 fmt = stride | ( ( bpp + ( ( bpp = = 8 ) ? 0 : 8 ) ) < < 13 ) ;
2005-04-17 02:20:36 +04:00
int tdfx_rop ;
2007-10-16 12:28:45 +04:00
u32 dx = rect - > dx ;
u32 dy = rect - > dy ;
u32 dstbase = 0 ;
2007-10-16 12:28:43 +04:00
if ( rect - > rop = = ROP_COPY )
2005-04-17 02:20:36 +04:00
tdfx_rop = TDFX_ROP_COPY ;
2007-10-16 12:28:43 +04:00
else
2005-04-17 02:20:36 +04:00
tdfx_rop = TDFX_ROP_XOR ;
2011-03-31 05:57:33 +04:00
/* assume always rect->height < 4096 */
2007-10-16 12:28:45 +04:00
if ( dy + rect - > height > 4095 ) {
dstbase = stride * dy ;
dy = 0 ;
}
2011-03-31 05:57:33 +04:00
/* assume always rect->width < 4096 */
2007-10-16 12:28:45 +04:00
if ( dx + rect - > width > 4095 ) {
dstbase + = dx * bpp > > 3 ;
dx = 0 ;
}
banshee_make_room ( par , 6 ) ;
2007-10-16 12:28:43 +04:00
tdfx_outl ( par , DSTFORMAT , fmt ) ;
2005-04-17 02:20:36 +04:00
if ( info - > fix . visual = = FB_VISUAL_PSEUDOCOLOR ) {
2007-10-16 12:28:43 +04:00
tdfx_outl ( par , COLORFORE , rect - > color ) ;
2005-04-17 02:20:36 +04:00
} else { /* FB_VISUAL_TRUECOLOR */
2006-01-10 07:53:11 +03:00
tdfx_outl ( par , COLORFORE , par - > palette [ rect - > color ] ) ;
2005-04-17 02:20:36 +04:00
}
2007-10-16 12:28:43 +04:00
tdfx_outl ( par , COMMAND_2D , COMMAND_2D_FILLRECT | ( tdfx_rop < < 24 ) ) ;
2007-10-16 12:28:45 +04:00
tdfx_outl ( par , DSTBASE , dstbase ) ;
2007-10-16 12:28:43 +04:00
tdfx_outl ( par , DSTSIZE , rect - > width | ( rect - > height < < 16 ) ) ;
2007-10-16 12:28:45 +04:00
tdfx_outl ( par , LAUNCH_2D , dx | ( dy < < 16 ) ) ;
2005-04-17 02:20:36 +04:00
}
/*
2007-10-16 12:28:43 +04:00
* Screen - to - Screen BitBlt 2 D command ( for the bmove fb op . )
2005-04-17 02:20:36 +04:00
*/
2007-10-16 12:28:43 +04:00
static void tdfxfb_copyarea ( struct fb_info * info ,
const struct fb_copyarea * area )
2005-04-17 02:20:36 +04:00
{
2006-01-10 07:53:11 +03:00
struct tdfx_par * par = info - > par ;
2007-10-16 12:28:43 +04:00
u32 sx = area - > sx , sy = area - > sy , dx = area - > dx , dy = area - > dy ;
2005-04-17 02:20:36 +04:00
u32 bpp = info - > var . bits_per_pixel ;
u32 stride = info - > fix . line_length ;
u32 blitcmd = COMMAND_2D_S2S_BITBLT | ( TDFX_ROP_COPY < < 24 ) ;
2007-10-16 12:28:43 +04:00
u32 fmt = stride | ( ( bpp + ( ( bpp = = 8 ) ? 0 : 8 ) ) < < 13 ) ;
2007-10-16 12:28:45 +04:00
u32 dstbase = 0 ;
u32 srcbase = 0 ;
2011-03-31 05:57:33 +04:00
/* assume always area->height < 4096 */
2007-10-16 12:28:45 +04:00
if ( sy + area - > height > 4095 ) {
srcbase = stride * sy ;
sy = 0 ;
}
2011-03-31 05:57:33 +04:00
/* assume always area->width < 4096 */
2007-10-16 12:28:45 +04:00
if ( sx + area - > width > 4095 ) {
srcbase + = sx * bpp > > 3 ;
sx = 0 ;
}
2011-03-31 05:57:33 +04:00
/* assume always area->height < 4096 */
2007-10-16 12:28:45 +04:00
if ( dy + area - > height > 4095 ) {
dstbase = stride * dy ;
dy = 0 ;
}
2011-03-31 05:57:33 +04:00
/* assume always area->width < 4096 */
2007-10-16 12:28:45 +04:00
if ( dx + area - > width > 4095 ) {
dstbase + = dx * bpp > > 3 ;
dx = 0 ;
}
2005-04-17 02:20:36 +04:00
if ( area - > sx < = area - > dx ) {
2007-10-16 12:29:28 +04:00
/* -X */
2005-04-17 02:20:36 +04:00
blitcmd | = BIT ( 14 ) ;
sx + = area - > width - 1 ;
dx + = area - > width - 1 ;
}
if ( area - > sy < = area - > dy ) {
2007-10-16 12:29:28 +04:00
/* -Y */
2005-04-17 02:20:36 +04:00
blitcmd | = BIT ( 15 ) ;
sy + = area - > height - 1 ;
dy + = area - > height - 1 ;
}
2007-10-16 12:28:43 +04:00
2007-10-16 12:28:45 +04:00
banshee_make_room ( par , 8 ) ;
2005-04-17 02:20:36 +04:00
2007-10-16 12:28:43 +04:00
tdfx_outl ( par , SRCFORMAT , fmt ) ;
tdfx_outl ( par , DSTFORMAT , fmt ) ;
tdfx_outl ( par , COMMAND_2D , blitcmd ) ;
tdfx_outl ( par , DSTSIZE , area - > width | ( area - > height < < 16 ) ) ;
tdfx_outl ( par , DSTXY , dx | ( dy < < 16 ) ) ;
2007-10-16 12:28:45 +04:00
tdfx_outl ( par , SRCBASE , srcbase ) ;
tdfx_outl ( par , DSTBASE , dstbase ) ;
2007-10-16 12:28:43 +04:00
tdfx_outl ( par , LAUNCH_2D , sx | ( sy < < 16 ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-10-16 12:28:43 +04:00
static void tdfxfb_imageblit ( struct fb_info * info , const struct fb_image * image )
2005-04-17 02:20:36 +04:00
{
2006-01-10 07:53:11 +03:00
struct tdfx_par * par = info - > par ;
2007-10-16 12:28:43 +04:00
int size = image - > height * ( ( image - > width * image - > depth + 7 ) > > 3 ) ;
2005-04-17 02:20:36 +04:00
int fifo_free ;
int i , stride = info - > fix . line_length ;
u32 bpp = info - > var . bits_per_pixel ;
2007-10-16 12:28:43 +04:00
u32 dstfmt = stride | ( ( bpp + ( ( bpp = = 8 ) ? 0 : 8 ) ) < < 13 ) ;
2005-04-17 02:20:36 +04:00
u8 * chardata = ( u8 * ) image - > data ;
u32 srcfmt ;
2007-10-16 12:28:45 +04:00
u32 dx = image - > dx ;
u32 dy = image - > dy ;
u32 dstbase = 0 ;
2005-04-17 02:20:36 +04:00
if ( image - > depth ! = 1 ) {
2007-10-16 12:29:28 +04:00
# ifdef BROKEN_CODE
banshee_make_room ( par , 6 + ( ( size + 3 ) > > 2 ) ) ;
srcfmt = stride | ( ( bpp + ( ( bpp = = 8 ) ? 0 : 8 ) ) < < 13 ) |
0x400000 ;
# else
2005-04-17 02:20:36 +04:00
cfb_imageblit ( info , image ) ;
2007-10-16 12:29:28 +04:00
# endif
2005-04-17 02:20:36 +04:00
return ;
2007-10-16 12:28:45 +04:00
}
banshee_make_room ( par , 9 ) ;
switch ( info - > fix . visual ) {
case FB_VISUAL_PSEUDOCOLOR :
tdfx_outl ( par , COLORFORE , image - > fg_color ) ;
tdfx_outl ( par , COLORBACK , image - > bg_color ) ;
break ;
case FB_VISUAL_TRUECOLOR :
default :
tdfx_outl ( par , COLORFORE ,
par - > palette [ image - > fg_color ] ) ;
tdfx_outl ( par , COLORBACK ,
par - > palette [ image - > bg_color ] ) ;
}
2005-04-17 02:20:36 +04:00
# ifdef __BIG_ENDIAN
2007-10-16 12:28:45 +04:00
srcfmt = 0x400000 | BIT ( 20 ) ;
2005-04-17 02:20:36 +04:00
# else
2007-10-16 12:28:45 +04:00
srcfmt = 0x400000 ;
2005-04-17 02:20:36 +04:00
# endif
2011-03-31 05:57:33 +04:00
/* assume always image->height < 4096 */
2007-10-16 12:28:45 +04:00
if ( dy + image - > height > 4095 ) {
dstbase = stride * dy ;
dy = 0 ;
}
2011-03-31 05:57:33 +04:00
/* assume always image->width < 4096 */
2007-10-16 12:28:45 +04:00
if ( dx + image - > width > 4095 ) {
dstbase + = dx * bpp > > 3 ;
dx = 0 ;
2007-10-16 12:28:43 +04:00
}
2005-04-17 02:20:36 +04:00
2007-10-16 12:28:45 +04:00
tdfx_outl ( par , DSTBASE , dstbase ) ;
2007-10-16 12:28:43 +04:00
tdfx_outl ( par , SRCXY , 0 ) ;
2007-10-16 12:28:45 +04:00
tdfx_outl ( par , DSTXY , dx | ( dy < < 16 ) ) ;
2007-10-16 12:29:28 +04:00
tdfx_outl ( par , COMMAND_2D ,
COMMAND_2D_H2S_BITBLT | ( TDFX_ROP_COPY < < 24 ) ) ;
2007-10-16 12:28:43 +04:00
tdfx_outl ( par , SRCFORMAT , srcfmt ) ;
tdfx_outl ( par , DSTFORMAT , dstfmt ) ;
tdfx_outl ( par , DSTSIZE , image - > width | ( image - > height < < 16 ) ) ;
2005-04-17 02:20:36 +04:00
/* A count of how many free FIFO entries we've requested.
* When this goes negative , we need to request more . */
fifo_free = 0 ;
2007-10-16 12:28:43 +04:00
/* Send four bytes at a time of data */
for ( i = ( size > > 2 ) ; i > 0 ; i - - ) {
if ( - - fifo_free < 0 ) {
fifo_free = 31 ;
banshee_make_room ( par , fifo_free ) ;
2005-04-17 02:20:36 +04:00
}
2007-10-16 12:29:28 +04:00
tdfx_outl ( par , LAUNCH_2D , * ( u32 * ) chardata ) ;
2007-10-16 12:28:43 +04:00
chardata + = 4 ;
}
2005-04-17 02:20:36 +04:00
2007-10-16 12:28:43 +04:00
/* Send the leftovers now */
banshee_make_room ( par , 3 ) ;
2007-10-16 12:28:48 +04:00
switch ( size % 4 ) {
2007-10-16 12:28:43 +04:00
case 0 :
break ;
case 1 :
tdfx_outl ( par , LAUNCH_2D , * chardata ) ;
break ;
case 2 :
2007-10-16 12:29:28 +04:00
tdfx_outl ( par , LAUNCH_2D , * ( u16 * ) chardata ) ;
2007-10-16 12:28:43 +04:00
break ;
case 3 :
tdfx_outl ( par , LAUNCH_2D ,
2007-10-16 12:29:28 +04:00
* ( u16 * ) chardata | ( chardata [ 3 ] < < 24 ) ) ;
2007-10-16 12:28:43 +04:00
break ;
2005-04-17 02:20:36 +04:00
}
}
# endif /* CONFIG_FB_3DFX_ACCEL */
static int tdfxfb_cursor ( struct fb_info * info , struct fb_cursor * cursor )
{
2006-01-10 07:53:11 +03:00
struct tdfx_par * par = info - > par ;
2007-10-16 12:28:48 +04:00
u32 vidcfg ;
if ( ! hwcursor )
return - EINVAL ; /* just to force soft_cursor() call */
/* Too large of a cursor or wrong bpp :-( */
if ( cursor - > image . width > 64 | |
cursor - > image . height > 64 | |
cursor - > image . depth > 1 )
return - EINVAL ;
vidcfg = tdfx_inl ( par , VIDPROCCFG ) ;
if ( cursor - > enable )
tdfx_outl ( par , VIDPROCCFG , vidcfg | VIDCFG_HWCURSOR_ENABLE ) ;
else
tdfx_outl ( par , VIDPROCCFG , vidcfg & ~ VIDCFG_HWCURSOR_ENABLE ) ;
2005-04-17 02:20:36 +04:00
/*
2007-10-16 12:28:43 +04:00
* If the cursor is not be changed this means either we want the
2005-04-17 02:20:36 +04:00
* current cursor state ( if enable is set ) or we want to query what
2007-10-16 12:28:43 +04:00
* we can do with the cursor ( if enable is not set )
*/
if ( ! cursor - > set )
return 0 ;
2005-04-17 02:20:36 +04:00
/* fix cursor color - XFree86 forgets to restore it properly */
2007-10-16 12:28:48 +04:00
if ( cursor - > set & FB_CUR_SETCMAP ) {
struct fb_cmap cmap = info - > cmap ;
u32 bg_idx = cursor - > image . bg_color ;
u32 fg_idx = cursor - > image . fg_color ;
2005-04-17 02:20:36 +04:00
unsigned long bg_color , fg_color ;
2007-10-16 12:28:48 +04:00
fg_color = ( ( ( u32 ) cmap . red [ fg_idx ] & 0xff00 ) < < 8 ) |
( ( ( u32 ) cmap . green [ fg_idx ] & 0xff00 ) < < 0 ) |
( ( ( u32 ) cmap . blue [ fg_idx ] & 0xff00 ) > > 8 ) ;
bg_color = ( ( ( u32 ) cmap . red [ bg_idx ] & 0xff00 ) < < 8 ) |
( ( ( u32 ) cmap . green [ bg_idx ] & 0xff00 ) < < 0 ) |
( ( ( u32 ) cmap . blue [ bg_idx ] & 0xff00 ) > > 8 ) ;
2005-04-17 02:20:36 +04:00
banshee_make_room ( par , 2 ) ;
tdfx_outl ( par , HWCURC0 , bg_color ) ;
tdfx_outl ( par , HWCURC1 , fg_color ) ;
}
2007-10-16 12:28:48 +04:00
if ( cursor - > set & FB_CUR_SETPOS ) {
int x = cursor - > image . dx ;
int y = cursor - > image . dy - info - > var . yoffset ;
2005-04-17 02:20:36 +04:00
x + = 63 ;
y + = 63 ;
banshee_make_room ( par , 1 ) ;
tdfx_outl ( par , HWCURLOC , ( y < < 16 ) + x ) ;
}
2007-10-16 12:28:48 +04:00
if ( cursor - > set & ( FB_CUR_SETIMAGE | FB_CUR_SETSHAPE ) ) {
2005-04-17 02:20:36 +04:00
/*
2007-10-16 12:28:43 +04:00
* Voodoo 3 and above cards use 2 monochrome cursor patterns .
2005-04-17 02:20:36 +04:00
* The reason is so the card can fetch 8 words at a time
* and are stored on chip for use for the next 8 scanlines .
* This reduces the number of times for access to draw the
* cursor for each screen refresh .
* Each pattern is a bitmap of 64 bit wide and 64 bit high
2007-10-16 12:28:48 +04:00
* ( total of 8192 bits or 1024 bytes ) . The two patterns are
2005-04-17 02:20:36 +04:00
* stored in such a way that pattern 0 always resides in the
* lower half ( least significant 64 bits ) of a 128 bit word
* and pattern 1 the upper half . If you examine the data of
* the cursor image the graphics card uses then from the
2011-03-31 05:57:33 +04:00
* beginning you see line one of pattern 0 , line one of
2005-04-17 02:20:36 +04:00
* pattern 1 , line two of pattern 0 , line two of pattern 1 ,
* etc etc . The linear stride for the cursor is always 16 bytes
* ( 128 bits ) which is the maximum cursor width times two for
* the two monochrome patterns .
*/
2007-10-16 12:28:48 +04:00
u8 __iomem * cursorbase = info - > screen_base + info - > fix . smem_len ;
u8 * bitmap = ( u8 * ) cursor - > image . data ;
u8 * mask = ( u8 * ) cursor - > mask ;
int i ;
fb_memset ( cursorbase , 0 , 1024 ) ;
for ( i = 0 ; i < cursor - > image . height ; i + + ) {
int h = 0 ;
int j = ( cursor - > image . width + 7 ) > > 3 ;
for ( ; j > 0 ; j - - ) {
u8 data = * mask ^ * bitmap ;
if ( cursor - > rop = = ROP_COPY )
data = * mask & * bitmap ;
/* Pattern 0. Copy the cursor mask to it */
fb_writeb ( * mask , cursorbase + h ) ;
mask + + ;
/* Pattern 1. Copy the cursor bitmap to it */
fb_writeb ( data , cursorbase + h + 8 ) ;
bitmap + + ;
h + + ;
2005-04-17 02:20:36 +04:00
}
2007-10-16 12:28:48 +04:00
cursorbase + = 16 ;
2005-04-17 02:20:36 +04:00
}
}
return 0 ;
}
2007-10-16 12:28:43 +04:00
static struct fb_ops tdfxfb_ops = {
. owner = THIS_MODULE ,
. fb_check_var = tdfxfb_check_var ,
. fb_set_par = tdfxfb_set_par ,
. fb_setcolreg = tdfxfb_setcolreg ,
. fb_blank = tdfxfb_blank ,
. fb_pan_display = tdfxfb_pan_display ,
. fb_sync = banshee_wait_idle ,
2007-10-16 12:28:48 +04:00
. fb_cursor = tdfxfb_cursor ,
2007-10-16 12:28:43 +04:00
# ifdef CONFIG_FB_3DFX_ACCEL
. fb_fillrect = tdfxfb_fillrect ,
. fb_copyarea = tdfxfb_copyarea ,
. fb_imageblit = tdfxfb_imageblit ,
# else
. fb_fillrect = cfb_fillrect ,
. fb_copyarea = cfb_copyarea ,
. fb_imageblit = cfb_imageblit ,
# endif
} ;
2009-04-07 06:01:03 +04:00
# ifdef CONFIG_FB_3DFX_I2C
/* The voo GPIO registers don't have individual masks for each bit
so we always have to read before writing . */
static void tdfxfb_i2c_setscl ( void * data , int val )
{
struct tdfxfb_i2c_chan * chan = data ;
struct tdfx_par * par = chan - > par ;
unsigned int r ;
r = tdfx_inl ( par , VIDSERPARPORT ) ;
if ( val )
r | = I2C_SCL_OUT ;
else
r & = ~ I2C_SCL_OUT ;
tdfx_outl ( par , VIDSERPARPORT , r ) ;
tdfx_inl ( par , VIDSERPARPORT ) ; /* flush posted write */
}
static void tdfxfb_i2c_setsda ( void * data , int val )
{
struct tdfxfb_i2c_chan * chan = data ;
struct tdfx_par * par = chan - > par ;
unsigned int r ;
r = tdfx_inl ( par , VIDSERPARPORT ) ;
if ( val )
r | = I2C_SDA_OUT ;
else
r & = ~ I2C_SDA_OUT ;
tdfx_outl ( par , VIDSERPARPORT , r ) ;
tdfx_inl ( par , VIDSERPARPORT ) ; /* flush posted write */
}
/* The GPIO pins are open drain, so the pins always remain outputs.
We rely on the i2c - algo - bit routines to set the pins high before
reading the input from other chips . */
static int tdfxfb_i2c_getscl ( void * data )
{
struct tdfxfb_i2c_chan * chan = data ;
struct tdfx_par * par = chan - > par ;
return ( 0 ! = ( tdfx_inl ( par , VIDSERPARPORT ) & I2C_SCL_IN ) ) ;
}
static int tdfxfb_i2c_getsda ( void * data )
{
struct tdfxfb_i2c_chan * chan = data ;
struct tdfx_par * par = chan - > par ;
return ( 0 ! = ( tdfx_inl ( par , VIDSERPARPORT ) & I2C_SDA_IN ) ) ;
}
static void tdfxfb_ddc_setscl ( void * data , int val )
{
struct tdfxfb_i2c_chan * chan = data ;
struct tdfx_par * par = chan - > par ;
unsigned int r ;
r = tdfx_inl ( par , VIDSERPARPORT ) ;
if ( val )
r | = DDC_SCL_OUT ;
else
r & = ~ DDC_SCL_OUT ;
tdfx_outl ( par , VIDSERPARPORT , r ) ;
tdfx_inl ( par , VIDSERPARPORT ) ; /* flush posted write */
}
static void tdfxfb_ddc_setsda ( void * data , int val )
{
struct tdfxfb_i2c_chan * chan = data ;
struct tdfx_par * par = chan - > par ;
unsigned int r ;
r = tdfx_inl ( par , VIDSERPARPORT ) ;
if ( val )
r | = DDC_SDA_OUT ;
else
r & = ~ DDC_SDA_OUT ;
tdfx_outl ( par , VIDSERPARPORT , r ) ;
tdfx_inl ( par , VIDSERPARPORT ) ; /* flush posted write */
}
static int tdfxfb_ddc_getscl ( void * data )
{
struct tdfxfb_i2c_chan * chan = data ;
struct tdfx_par * par = chan - > par ;
return ( 0 ! = ( tdfx_inl ( par , VIDSERPARPORT ) & DDC_SCL_IN ) ) ;
}
static int tdfxfb_ddc_getsda ( void * data )
{
struct tdfxfb_i2c_chan * chan = data ;
struct tdfx_par * par = chan - > par ;
return ( 0 ! = ( tdfx_inl ( par , VIDSERPARPORT ) & DDC_SDA_IN ) ) ;
}
static int __devinit tdfxfb_setup_ddc_bus ( struct tdfxfb_i2c_chan * chan ,
const char * name , struct device * dev )
{
int rc ;
strlcpy ( chan - > adapter . name , name , sizeof ( chan - > adapter . name ) ) ;
chan - > adapter . owner = THIS_MODULE ;
chan - > adapter . class = I2C_CLASS_DDC ;
chan - > adapter . algo_data = & chan - > algo ;
chan - > adapter . dev . parent = dev ;
chan - > algo . setsda = tdfxfb_ddc_setsda ;
chan - > algo . setscl = tdfxfb_ddc_setscl ;
chan - > algo . getsda = tdfxfb_ddc_getsda ;
chan - > algo . getscl = tdfxfb_ddc_getscl ;
chan - > algo . udelay = 10 ;
chan - > algo . timeout = msecs_to_jiffies ( 500 ) ;
chan - > algo . data = chan ;
i2c_set_adapdata ( & chan - > adapter , chan ) ;
rc = i2c_bit_add_bus ( & chan - > adapter ) ;
if ( rc = = 0 )
DPRINTK ( " I2C bus %s registered. \n " , name ) ;
else
chan - > par = NULL ;
return rc ;
}
static int __devinit tdfxfb_setup_i2c_bus ( struct tdfxfb_i2c_chan * chan ,
const char * name , struct device * dev )
{
int rc ;
strlcpy ( chan - > adapter . name , name , sizeof ( chan - > adapter . name ) ) ;
chan - > adapter . owner = THIS_MODULE ;
chan - > adapter . algo_data = & chan - > algo ;
chan - > adapter . dev . parent = dev ;
chan - > algo . setsda = tdfxfb_i2c_setsda ;
chan - > algo . setscl = tdfxfb_i2c_setscl ;
chan - > algo . getsda = tdfxfb_i2c_getsda ;
chan - > algo . getscl = tdfxfb_i2c_getscl ;
chan - > algo . udelay = 10 ;
chan - > algo . timeout = msecs_to_jiffies ( 500 ) ;
chan - > algo . data = chan ;
i2c_set_adapdata ( & chan - > adapter , chan ) ;
rc = i2c_bit_add_bus ( & chan - > adapter ) ;
if ( rc = = 0 )
DPRINTK ( " I2C bus %s registered. \n " , name ) ;
else
chan - > par = NULL ;
return rc ;
}
static void __devinit tdfxfb_create_i2c_busses ( struct fb_info * info )
{
struct tdfx_par * par = info - > par ;
tdfx_outl ( par , VIDINFORMAT , 0x8160 ) ;
tdfx_outl ( par , VIDSERPARPORT , 0xcffc0020 ) ;
par - > chan [ 0 ] . par = par ;
par - > chan [ 1 ] . par = par ;
tdfxfb_setup_ddc_bus ( & par - > chan [ 0 ] , " Voodoo3-DDC " , info - > dev ) ;
tdfxfb_setup_i2c_bus ( & par - > chan [ 1 ] , " Voodoo3-I2C " , info - > dev ) ;
}
static void tdfxfb_delete_i2c_busses ( struct tdfx_par * par )
{
if ( par - > chan [ 0 ] . par )
i2c_del_adapter ( & par - > chan [ 0 ] . adapter ) ;
par - > chan [ 0 ] . par = NULL ;
if ( par - > chan [ 1 ] . par )
i2c_del_adapter ( & par - > chan [ 1 ] . adapter ) ;
par - > chan [ 1 ] . par = NULL ;
}
2009-04-07 06:01:04 +04:00
static int tdfxfb_probe_i2c_connector ( struct tdfx_par * par ,
struct fb_monspecs * specs )
{
u8 * edid = NULL ;
DPRINTK ( " Probe DDC Bus \n " ) ;
if ( par - > chan [ 0 ] . par )
edid = fb_ddc_read ( & par - > chan [ 0 ] . adapter ) ;
if ( edid ) {
fb_edid_to_monspecs ( edid , specs ) ;
kfree ( edid ) ;
return 0 ;
}
return 1 ;
}
2009-04-07 06:01:03 +04:00
# endif /* CONFIG_FB_3DFX_I2C */
2005-04-17 02:20:36 +04:00
/**
* tdfxfb_probe - Device Initializiation
*
* @ pdev : PCI Device to initialize
* @ id : PCI Device ID
*
* Initializes and allocates resources for PCI device @ pdev .
*
*/
static int __devinit tdfxfb_probe ( struct pci_dev * pdev ,
2007-10-16 12:28:43 +04:00
const struct pci_device_id * id )
2005-04-17 02:20:36 +04:00
{
struct tdfx_par * default_par ;
struct fb_info * info ;
2006-01-10 07:53:11 +03:00
int err , lpitch ;
2009-04-07 06:01:04 +04:00
struct fb_monspecs * specs ;
bool found ;
2005-04-17 02:20:36 +04:00
2007-10-16 12:29:28 +04:00
err = pci_enable_device ( pdev ) ;
if ( err ) {
printk ( KERN_ERR " tdfxfb: Can't enable pdev: %d \n " , err ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
2006-01-10 07:53:11 +03:00
info = framebuffer_alloc ( sizeof ( struct tdfx_par ) , & pdev - > dev ) ;
2005-04-17 02:20:36 +04:00
2006-01-10 07:53:11 +03:00
if ( ! info )
return - ENOMEM ;
2007-10-16 12:28:43 +04:00
2005-04-17 02:20:36 +04:00
default_par = info - > par ;
2008-10-16 09:03:34 +04:00
info - > fix = tdfx_fix ;
2007-10-16 12:28:43 +04:00
2005-04-17 02:20:36 +04:00
/* Configure the default fb_fix_screeninfo first */
switch ( pdev - > device ) {
2007-10-16 12:28:43 +04:00
case PCI_DEVICE_ID_3DFX_BANSHEE :
2008-10-16 09:03:34 +04:00
strcpy ( info - > fix . id , " 3Dfx Banshee " ) ;
2007-10-16 12:28:43 +04:00
default_par - > max_pixclock = BANSHEE_MAX_PIXCLOCK ;
break ;
case PCI_DEVICE_ID_3DFX_VOODOO3 :
2008-10-16 09:03:34 +04:00
strcpy ( info - > fix . id , " 3Dfx Voodoo3 " ) ;
2007-10-16 12:28:43 +04:00
default_par - > max_pixclock = VOODOO3_MAX_PIXCLOCK ;
break ;
case PCI_DEVICE_ID_3DFX_VOODOO5 :
2008-10-16 09:03:34 +04:00
strcpy ( info - > fix . id , " 3Dfx Voodoo5 " ) ;
2007-10-16 12:28:43 +04:00
default_par - > max_pixclock = VOODOO5_MAX_PIXCLOCK ;
break ;
2005-04-17 02:20:36 +04:00
}
2008-10-16 09:03:34 +04:00
info - > fix . mmio_start = pci_resource_start ( pdev , 0 ) ;
info - > fix . mmio_len = pci_resource_len ( pdev , 0 ) ;
if ( ! request_mem_region ( info - > fix . mmio_start , info - > fix . mmio_len ,
2007-10-16 12:28:45 +04:00
" tdfx regbase " ) ) {
2007-10-16 12:29:28 +04:00
printk ( KERN_ERR " tdfxfb: Can't reserve regbase \n " ) ;
2007-10-16 12:28:45 +04:00
goto out_err ;
}
2007-10-16 12:28:43 +04:00
default_par - > regbase_virt =
2008-10-16 09:03:34 +04:00
ioremap_nocache ( info - > fix . mmio_start , info - > fix . mmio_len ) ;
2005-04-17 02:20:36 +04:00
if ( ! default_par - > regbase_virt ) {
2007-10-16 12:29:28 +04:00
printk ( KERN_ERR " fb: Can't remap %s register area. \n " ,
2008-10-16 09:03:34 +04:00
info - > fix . id ) ;
2007-10-16 12:28:45 +04:00
goto out_err_regbase ;
2007-10-16 12:28:43 +04:00
}
2005-04-17 02:20:36 +04:00
2008-10-16 09:03:34 +04:00
info - > fix . smem_start = pci_resource_start ( pdev , 1 ) ;
info - > fix . smem_len = do_lfb_size ( default_par , pdev - > device ) ;
if ( ! info - > fix . smem_len ) {
printk ( KERN_ERR " fb: Can't count %s memory. \n " , info - > fix . id ) ;
2007-10-16 12:28:45 +04:00
goto out_err_regbase ;
2005-04-17 02:20:36 +04:00
}
2008-10-16 09:03:34 +04:00
if ( ! request_mem_region ( info - > fix . smem_start ,
2007-10-16 12:28:43 +04:00
pci_resource_len ( pdev , 1 ) , " tdfx smem " ) ) {
2007-10-16 12:29:28 +04:00
printk ( KERN_ERR " tdfxfb: Can't reserve smem \n " ) ;
2007-10-16 12:28:45 +04:00
goto out_err_regbase ;
2005-04-17 02:20:36 +04:00
}
2008-10-16 09:03:34 +04:00
info - > screen_base = ioremap_nocache ( info - > fix . smem_start ,
info - > fix . smem_len ) ;
2005-04-17 02:20:36 +04:00
if ( ! info - > screen_base ) {
2007-10-16 12:29:28 +04:00
printk ( KERN_ERR " fb: Can't remap %s framebuffer. \n " ,
2008-10-16 09:03:34 +04:00
info - > fix . id ) ;
2007-10-16 12:28:45 +04:00
goto out_err_screenbase ;
2005-04-17 02:20:36 +04:00
}
default_par - > iobase = pci_resource_start ( pdev , 2 ) ;
2007-10-16 12:28:43 +04:00
2005-04-17 02:20:36 +04:00
if ( ! request_region ( pci_resource_start ( pdev , 2 ) ,
2007-10-16 12:28:43 +04:00
pci_resource_len ( pdev , 2 ) , " tdfx iobase " ) ) {
2007-10-16 12:29:28 +04:00
printk ( KERN_ERR " tdfxfb: Can't reserve iobase \n " ) ;
2007-10-16 12:28:45 +04:00
goto out_err_screenbase ;
2005-04-17 02:20:36 +04:00
}
2008-10-16 09:03:34 +04:00
printk ( KERN_INFO " fb: %s memory = %dK \n " , info - > fix . id ,
info - > fix . smem_len > > 10 ) ;
2005-04-17 02:20:36 +04:00
2007-10-16 12:28:49 +04:00
default_par - > mtrr_handle = - 1 ;
if ( ! nomtrr )
default_par - > mtrr_handle =
2008-10-16 09:03:34 +04:00
mtrr_add ( info - > fix . smem_start , info - > fix . smem_len ,
2007-10-16 12:28:49 +04:00
MTRR_TYPE_WRCOMB , 1 ) ;
2008-10-16 09:03:34 +04:00
info - > fix . ypanstep = nopan ? 0 : 1 ;
info - > fix . ywrapstep = nowrap ? 0 : 1 ;
2007-10-16 12:28:43 +04:00
2005-04-17 02:20:36 +04:00
info - > fbops = & tdfxfb_ops ;
2006-01-10 07:53:11 +03:00
info - > pseudo_palette = default_par - > palette ;
2005-04-17 02:20:36 +04:00
info - > flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN ;
# ifdef CONFIG_FB_3DFX_ACCEL
2007-10-16 12:29:28 +04:00
info - > flags | = FBINFO_HWACCEL_FILLRECT |
2007-10-16 12:28:45 +04:00
FBINFO_HWACCEL_COPYAREA |
FBINFO_HWACCEL_IMAGEBLIT |
FBINFO_READS_FAST ;
2005-04-17 02:20:36 +04:00
# endif
2007-10-16 12:28:48 +04:00
/* reserve 8192 bits for cursor */
/* the 2.4 driver says PAGE_MASK boundary is not enough for Voodoo4 */
if ( hwcursor )
info - > fix . smem_len = ( info - > fix . smem_len - 1024 ) &
( PAGE_MASK < < 1 ) ;
2009-04-07 06:01:04 +04:00
specs = & info - > monspecs ;
found = false ;
info - > var . bits_per_pixel = 8 ;
2009-04-07 06:01:03 +04:00
# ifdef CONFIG_FB_3DFX_I2C
tdfxfb_create_i2c_busses ( info ) ;
2009-04-07 06:01:04 +04:00
err = tdfxfb_probe_i2c_connector ( default_par , specs ) ;
if ( ! err ) {
if ( specs - > modedb = = NULL )
DPRINTK ( " Unable to get Mode Database \n " ) ;
else {
const struct fb_videomode * m ;
fb_videomode_to_modelist ( specs - > modedb ,
specs - > modedb_len ,
& info - > modelist ) ;
m = fb_find_best_display ( specs , & info - > modelist ) ;
if ( m ) {
fb_videomode_to_var ( & info - > var , m ) ;
/* fill all other info->var's fields */
if ( tdfxfb_check_var ( & info - > var , info ) < 0 )
info - > var = tdfx_var ;
else
found = true ;
}
}
}
2009-04-07 06:01:03 +04:00
# endif
2009-04-07 06:01:04 +04:00
if ( ! mode_option & & ! found )
2005-04-17 02:20:36 +04:00
mode_option = " 640x480@60 " ;
2007-10-16 12:28:43 +04:00
2009-04-07 06:01:04 +04:00
if ( mode_option ) {
err = fb_find_mode ( & info - > var , info , mode_option ,
specs - > modedb , specs - > modedb_len ,
NULL , info - > var . bits_per_pixel ) ;
if ( ! err | | err = = 4 )
info - > var = tdfx_var ;
}
if ( found ) {
fb_destroy_modedb ( specs - > modedb ) ;
specs - > modedb = NULL ;
}
2005-04-17 02:20:36 +04:00
/* maximize virtual vertical length */
lpitch = info - > var . xres_virtual * ( ( info - > var . bits_per_pixel + 7 ) > > 3 ) ;
2007-10-16 12:28:43 +04:00
info - > var . yres_virtual = info - > fix . smem_len / lpitch ;
2005-04-17 02:20:36 +04:00
if ( info - > var . yres_virtual < info - > var . yres )
2007-10-16 12:28:45 +04:00
goto out_err_iobase ;
2005-04-17 02:20:36 +04:00
if ( fb_alloc_cmap ( & info - > cmap , 256 , 0 ) < 0 ) {
2007-10-16 12:29:28 +04:00
printk ( KERN_ERR " tdfxfb: Can't allocate color map \n " ) ;
2007-10-16 12:28:45 +04:00
goto out_err_iobase ;
2005-04-17 02:20:36 +04:00
}
if ( register_framebuffer ( info ) < 0 ) {
2007-10-16 12:29:28 +04:00
printk ( KERN_ERR " tdfxfb: can't register framebuffer \n " ) ;
2005-04-17 02:20:36 +04:00
fb_dealloc_cmap ( & info - > cmap ) ;
2007-10-16 12:28:45 +04:00
goto out_err_iobase ;
2005-04-17 02:20:36 +04:00
}
/*
* Our driver data
*/
pci_set_drvdata ( pdev , info ) ;
2007-10-16 12:28:43 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
2007-10-16 12:28:45 +04:00
out_err_iobase :
2009-04-07 06:01:03 +04:00
# ifdef CONFIG_FB_3DFX_I2C
tdfxfb_delete_i2c_busses ( default_par ) ;
# endif
2007-10-16 12:28:49 +04:00
if ( default_par - > mtrr_handle > = 0 )
mtrr_del ( default_par - > mtrr_handle , info - > fix . smem_start ,
info - > fix . smem_len ) ;
2009-08-09 13:42:32 +04:00
release_region ( pci_resource_start ( pdev , 2 ) ,
pci_resource_len ( pdev , 2 ) ) ;
2007-10-16 12:28:45 +04:00
out_err_screenbase :
if ( info - > screen_base )
iounmap ( info - > screen_base ) ;
2008-10-16 09:03:34 +04:00
release_mem_region ( info - > fix . smem_start , pci_resource_len ( pdev , 1 ) ) ;
2007-10-16 12:28:45 +04:00
out_err_regbase :
2005-04-17 02:20:36 +04:00
/*
* Cleanup after anything that was remapped / allocated .
*/
if ( default_par - > regbase_virt )
iounmap ( default_par - > regbase_virt ) ;
2008-10-16 09:03:34 +04:00
release_mem_region ( info - > fix . mmio_start , info - > fix . mmio_len ) ;
2007-10-16 12:28:45 +04:00
out_err :
2005-04-17 02:20:36 +04:00
framebuffer_release ( info ) ;
return - ENXIO ;
}
# ifndef MODULE
2008-02-06 12:39:19 +03:00
static void __init tdfxfb_setup ( char * options )
2005-04-17 02:20:36 +04:00
{
2007-10-16 12:28:43 +04:00
char * this_opt ;
2005-04-17 02:20:36 +04:00
if ( ! options | | ! * options )
return ;
while ( ( this_opt = strsep ( & options , " , " ) ) ! = NULL ) {
if ( ! * this_opt )
continue ;
2007-10-16 12:28:43 +04:00
if ( ! strcmp ( this_opt , " nopan " ) ) {
2005-04-17 02:20:36 +04:00
nopan = 1 ;
2007-10-16 12:28:43 +04:00
} else if ( ! strcmp ( this_opt , " nowrap " ) ) {
2005-04-17 02:20:36 +04:00
nowrap = 1 ;
2007-10-16 12:28:49 +04:00
} else if ( ! strncmp ( this_opt , " hwcursor= " , 9 ) ) {
hwcursor = simple_strtoul ( this_opt + 9 , NULL , 0 ) ;
# ifdef CONFIG_MTRR
} else if ( ! strncmp ( this_opt , " nomtrr " , 6 ) ) {
nomtrr = 1 ;
# endif
2005-04-17 02:20:36 +04:00
} else {
mode_option = this_opt ;
}
}
}
# endif
/**
* tdfxfb_remove - Device removal
*
* @ pdev : PCI Device to cleanup
*
* Releases all resources allocated during the course of the driver ' s
* lifetime for the PCI device @ pdev .
*
*/
static void __devexit tdfxfb_remove ( struct pci_dev * pdev )
{
struct fb_info * info = pci_get_drvdata ( pdev ) ;
2006-01-10 07:53:11 +03:00
struct tdfx_par * par = info - > par ;
2005-04-17 02:20:36 +04:00
unregister_framebuffer ( info ) ;
2009-04-07 06:01:03 +04:00
# ifdef CONFIG_FB_3DFX_I2C
tdfxfb_delete_i2c_busses ( par ) ;
# endif
2007-10-16 12:28:49 +04:00
if ( par - > mtrr_handle > = 0 )
mtrr_del ( par - > mtrr_handle , info - > fix . smem_start ,
info - > fix . smem_len ) ;
2005-04-17 02:20:36 +04:00
iounmap ( par - > regbase_virt ) ;
iounmap ( info - > screen_base ) ;
/* Clean up after reserved regions */
release_region ( pci_resource_start ( pdev , 2 ) ,
pci_resource_len ( pdev , 2 ) ) ;
release_mem_region ( pci_resource_start ( pdev , 1 ) ,
pci_resource_len ( pdev , 1 ) ) ;
release_mem_region ( pci_resource_start ( pdev , 0 ) ,
pci_resource_len ( pdev , 0 ) ) ;
pci_set_drvdata ( pdev , NULL ) ;
2009-04-01 02:25:21 +04:00
fb_dealloc_cmap ( & info - > cmap ) ;
2005-04-17 02:20:36 +04:00
framebuffer_release ( info ) ;
}
static int __init tdfxfb_init ( void )
{
# ifndef MODULE
char * option = NULL ;
if ( fb_get_options ( " tdfxfb " , & option ) )
return - ENODEV ;
tdfxfb_setup ( option ) ;
# endif
2007-10-16 12:28:43 +04:00
return pci_register_driver ( & tdfxfb_driver ) ;
2005-04-17 02:20:36 +04:00
}
static void __exit tdfxfb_exit ( void )
{
2007-10-16 12:28:43 +04:00
pci_unregister_driver ( & tdfxfb_driver ) ;
2005-04-17 02:20:36 +04:00
}
MODULE_AUTHOR ( " Hannu Mallat <hmallat@cc.hut.fi> " ) ;
MODULE_DESCRIPTION ( " 3Dfx framebuffer device driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
2007-10-16 12:28:43 +04:00
2007-10-16 12:28:48 +04:00
module_param ( hwcursor , int , 0644 ) ;
MODULE_PARM_DESC ( hwcursor , " Enable hardware cursor "
" (1=enable, 0=disable, default=1) " ) ;
2008-07-24 08:31:22 +04:00
module_param ( mode_option , charp , 0 ) ;
MODULE_PARM_DESC ( mode_option , " Initial video mode e.g. '648x480-8@60' " ) ;
2007-10-16 12:28:49 +04:00
# ifdef CONFIG_MTRR
module_param ( nomtrr , bool , 0 ) ;
MODULE_PARM_DESC ( nomtrr , " Disable MTRR support (default: enabled) " ) ;
# endif
2007-10-16 12:28:48 +04:00
2005-04-17 02:20:36 +04:00
module_init ( tdfxfb_init ) ;
module_exit ( tdfxfb_exit ) ;