2005-04-17 02:20:36 +04:00
/*
* linux / drivers / video / savagefb . c - - S3 Savage Framebuffer Driver
*
* Copyright ( c ) 2001 - 2002 Denis Oliver Kropp < dok @ directfb . org >
* Sven Neumann < neo @ directfb . org >
*
*
* Card specific code is based on XFree86 ' s savage driver .
* Framebuffer framework code is based on code of cyber2000fb and tdfxfb .
*
* This file is subject to the terms and conditions of the GNU General
* Public License . See the file COPYING in the main directory of this
* archive for more details .
*
* 0.4 .0 ( neo )
* - hardware accelerated clear and move
*
* 0.3 .2 ( dok )
* - wait for vertical retrace before writing to cr67
* at the beginning of savagefb_set_par
* - use synchronization registers cr23 and cr26
*
* 0.3 .1 ( dok )
* - reset 3 D engine
* - don ' t return alpha bits for 32 bit format
*
* 0.3 .0 ( dok )
* - added WaitIdle functions for all Savage types
* - do WaitIdle before mode switching
* - code cleanup
*
* 0.2 .0 ( dok )
* - first working version
*
*
* TODO
* - clock validations in decode_var
*
* BUGS
* - white margin on bootup
*
*/
# 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/delay.h>
# include <linux/fb.h>
# include <linux/pci.h>
# include <linux/init.h>
# include <linux/console.h>
# include <asm/io.h>
# include <asm/irq.h>
# include <asm/pgtable.h>
# include <asm/system.h>
# ifdef CONFIG_MTRR
# include <asm/mtrr.h>
# endif
# include "savagefb.h"
# define SAVAGEFB_VERSION "0.4.0_2.6"
/* --------------------------------------------------------------------- */
2006-04-19 09:22:09 +04:00
static char * mode_option __devinitdata = NULL ;
2005-04-17 02:20:36 +04:00
# ifdef MODULE
MODULE_AUTHOR ( " (c) 2001-2002 Denis Oliver Kropp <dok@directfb.org> " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " FBDev driver for S3 Savage PCI/AGP Chips " ) ;
# endif
/* --------------------------------------------------------------------- */
2006-06-26 11:26:36 +04:00
static void vgaHWSeqReset ( struct savagefb_par * par , int start )
2005-04-17 02:20:36 +04:00
{
if ( start )
2006-06-26 11:26:36 +04:00
VGAwSEQ ( 0x00 , 0x01 , par ) ; /* Synchronous Reset */
2005-04-17 02:20:36 +04:00
else
2006-06-26 11:26:36 +04:00
VGAwSEQ ( 0x00 , 0x03 , par ) ; /* End Reset */
2005-04-17 02:20:36 +04:00
}
2006-06-26 11:26:36 +04:00
static void vgaHWProtect ( struct savagefb_par * par , int on )
2005-04-17 02:20:36 +04:00
{
unsigned char tmp ;
if ( on ) {
/*
* Turn off screen and disable sequencer .
*/
2006-06-26 11:26:36 +04:00
tmp = VGArSEQ ( 0x01 , par ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:36 +04:00
vgaHWSeqReset ( par , 1 ) ; /* start synchronous reset */
VGAwSEQ ( 0x01 , tmp | 0x20 , par ) ; /* disable the display */
2005-04-17 02:20:36 +04:00
2005-11-07 12:00:41 +03:00
VGAenablePalette ( par ) ;
2005-04-17 02:20:36 +04:00
} else {
/*
* Reenable sequencer , then turn on screen .
*/
2006-06-26 11:26:36 +04:00
tmp = VGArSEQ ( 0x01 , par ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:36 +04:00
VGAwSEQ ( 0x01 , tmp & ~ 0x20 , par ) ; /* reenable display */
vgaHWSeqReset ( par , 0 ) ; /* clear synchronous reset */
2005-04-17 02:20:36 +04:00
2005-11-07 12:00:41 +03:00
VGAdisablePalette ( par ) ;
2005-04-17 02:20:36 +04:00
}
}
2006-06-26 11:26:36 +04:00
static void vgaHWRestore ( struct savagefb_par * par , struct savage_reg * reg )
2005-04-17 02:20:36 +04:00
{
int i ;
2006-06-26 11:26:36 +04:00
VGAwMISC ( reg - > MiscOutReg , par ) ;
2005-04-17 02:20:36 +04:00
for ( i = 1 ; i < 5 ; i + + )
2006-06-26 11:26:36 +04:00
VGAwSEQ ( i , reg - > Sequencer [ i ] , par ) ;
2005-04-17 02:20:36 +04:00
/* Ensure CRTC registers 0-7 are unlocked by clearing bit 7 or
CRTC [ 17 ] */
2006-06-26 11:26:36 +04:00
VGAwCR ( 17 , reg - > CRTC [ 17 ] & ~ 0x80 , par ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < 25 ; i + + )
2006-06-26 11:26:36 +04:00
VGAwCR ( i , reg - > CRTC [ i ] , par ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < 9 ; i + + )
2006-06-26 11:26:36 +04:00
VGAwGR ( i , reg - > Graphics [ i ] , par ) ;
2005-04-17 02:20:36 +04:00
2005-11-07 12:00:41 +03:00
VGAenablePalette ( par ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < 21 ; i + + )
2006-06-26 11:26:36 +04:00
VGAwATTR ( i , reg - > Attribute [ i ] , par ) ;
2005-04-17 02:20:36 +04:00
2005-11-07 12:00:41 +03:00
VGAdisablePalette ( par ) ;
2005-04-17 02:20:36 +04:00
}
2006-06-26 11:26:36 +04:00
static void vgaHWInit ( struct fb_var_screeninfo * var ,
struct savagefb_par * par ,
struct xtimings * timings ,
struct savage_reg * reg )
2005-04-17 02:20:36 +04:00
{
2006-06-26 11:26:23 +04:00
reg - > MiscOutReg = 0x23 ;
2005-04-17 02:20:36 +04:00
if ( ! ( timings - > sync & FB_SYNC_HOR_HIGH_ACT ) )
2006-06-26 11:26:23 +04:00
reg - > MiscOutReg | = 0x40 ;
2005-04-17 02:20:36 +04:00
if ( ! ( timings - > sync & FB_SYNC_VERT_HIGH_ACT ) )
2006-06-26 11:26:23 +04:00
reg - > MiscOutReg | = 0x80 ;
2005-04-17 02:20:36 +04:00
/*
* Time Sequencer
*/
2006-06-26 11:26:23 +04:00
reg - > Sequencer [ 0x00 ] = 0x00 ;
reg - > Sequencer [ 0x01 ] = 0x01 ;
reg - > Sequencer [ 0x02 ] = 0x0F ;
reg - > Sequencer [ 0x03 ] = 0x00 ; /* Font select */
reg - > Sequencer [ 0x04 ] = 0x0E ; /* Misc */
2005-04-17 02:20:36 +04:00
/*
* CRTC Controller
*/
2006-06-26 11:26:23 +04:00
reg - > CRTC [ 0x00 ] = ( timings - > HTotal > > 3 ) - 5 ;
reg - > CRTC [ 0x01 ] = ( timings - > HDisplay > > 3 ) - 1 ;
reg - > CRTC [ 0x02 ] = ( timings - > HSyncStart > > 3 ) - 1 ;
reg - > CRTC [ 0x03 ] = ( ( ( timings - > HSyncEnd > > 3 ) - 1 ) & 0x1f ) | 0x80 ;
reg - > CRTC [ 0x04 ] = ( timings - > HSyncStart > > 3 ) ;
reg - > CRTC [ 0x05 ] = ( ( ( ( timings - > HSyncEnd > > 3 ) - 1 ) & 0x20 ) < < 2 ) |
2005-04-17 02:20:36 +04:00
( ( ( timings - > HSyncEnd > > 3 ) ) & 0x1f ) ;
2006-06-26 11:26:23 +04:00
reg - > CRTC [ 0x06 ] = ( timings - > VTotal - 2 ) & 0xFF ;
reg - > CRTC [ 0x07 ] = ( ( ( timings - > VTotal - 2 ) & 0x100 ) > > 8 ) |
2005-04-17 02:20:36 +04:00
( ( ( timings - > VDisplay - 1 ) & 0x100 ) > > 7 ) |
( ( timings - > VSyncStart & 0x100 ) > > 6 ) |
( ( ( timings - > VSyncStart - 1 ) & 0x100 ) > > 5 ) |
0x10 |
( ( ( timings - > VTotal - 2 ) & 0x200 ) > > 4 ) |
( ( ( timings - > VDisplay - 1 ) & 0x200 ) > > 3 ) |
( ( timings - > VSyncStart & 0x200 ) > > 2 ) ;
2006-06-26 11:26:23 +04:00
reg - > CRTC [ 0x08 ] = 0x00 ;
reg - > CRTC [ 0x09 ] = ( ( ( timings - > VSyncStart - 1 ) & 0x200 ) > > 4 ) | 0x40 ;
2005-04-17 02:20:36 +04:00
if ( timings - > dblscan )
2006-06-26 11:26:23 +04:00
reg - > CRTC [ 0x09 ] | = 0x80 ;
reg - > CRTC [ 0x0a ] = 0x00 ;
reg - > CRTC [ 0x0b ] = 0x00 ;
reg - > CRTC [ 0x0c ] = 0x00 ;
reg - > CRTC [ 0x0d ] = 0x00 ;
reg - > CRTC [ 0x0e ] = 0x00 ;
reg - > CRTC [ 0x0f ] = 0x00 ;
reg - > CRTC [ 0x10 ] = timings - > VSyncStart & 0xff ;
reg - > CRTC [ 0x11 ] = ( timings - > VSyncEnd & 0x0f ) | 0x20 ;
reg - > CRTC [ 0x12 ] = ( timings - > VDisplay - 1 ) & 0xff ;
reg - > CRTC [ 0x13 ] = var - > xres_virtual > > 4 ;
reg - > CRTC [ 0x14 ] = 0x00 ;
reg - > CRTC [ 0x15 ] = ( timings - > VSyncStart - 1 ) & 0xff ;
reg - > CRTC [ 0x16 ] = ( timings - > VSyncEnd - 1 ) & 0xff ;
reg - > CRTC [ 0x17 ] = 0xc3 ;
reg - > CRTC [ 0x18 ] = 0xff ;
2005-04-17 02:20:36 +04:00
/*
* are these unnecessary ?
* vgaHWHBlankKGA ( mode , regp , 0 , KGA_FIX_OVERSCAN | KGA_ENABLE_ON_ZERO ) ;
* vgaHWVBlankKGA ( mode , regp , 0 , KGA_FIX_OVERSCAN | KGA_ENABLE_ON_ZERO ) ;
*/
/*
* Graphics Display Controller
*/
2006-06-26 11:26:23 +04:00
reg - > Graphics [ 0x00 ] = 0x00 ;
reg - > Graphics [ 0x01 ] = 0x00 ;
reg - > Graphics [ 0x02 ] = 0x00 ;
reg - > Graphics [ 0x03 ] = 0x00 ;
reg - > Graphics [ 0x04 ] = 0x00 ;
reg - > Graphics [ 0x05 ] = 0x40 ;
reg - > Graphics [ 0x06 ] = 0x05 ; /* only map 64k VGA memory !!!! */
reg - > Graphics [ 0x07 ] = 0x0F ;
reg - > Graphics [ 0x08 ] = 0xFF ;
reg - > Attribute [ 0x00 ] = 0x00 ; /* standard colormap translation */
reg - > Attribute [ 0x01 ] = 0x01 ;
reg - > Attribute [ 0x02 ] = 0x02 ;
reg - > Attribute [ 0x03 ] = 0x03 ;
reg - > Attribute [ 0x04 ] = 0x04 ;
reg - > Attribute [ 0x05 ] = 0x05 ;
reg - > Attribute [ 0x06 ] = 0x06 ;
reg - > Attribute [ 0x07 ] = 0x07 ;
reg - > Attribute [ 0x08 ] = 0x08 ;
reg - > Attribute [ 0x09 ] = 0x09 ;
reg - > Attribute [ 0x0a ] = 0x0A ;
reg - > Attribute [ 0x0b ] = 0x0B ;
reg - > Attribute [ 0x0c ] = 0x0C ;
reg - > Attribute [ 0x0d ] = 0x0D ;
reg - > Attribute [ 0x0e ] = 0x0E ;
reg - > Attribute [ 0x0f ] = 0x0F ;
reg - > Attribute [ 0x10 ] = 0x41 ;
reg - > Attribute [ 0x11 ] = 0xFF ;
reg - > Attribute [ 0x12 ] = 0x0F ;
reg - > Attribute [ 0x13 ] = 0x00 ;
reg - > Attribute [ 0x14 ] = 0x00 ;
2005-04-17 02:20:36 +04:00
}
/* -------------------- Hardware specific routines ------------------------- */
/*
* Hardware Acceleration for SavageFB
*/
/* Wait for fifo space */
static void
savage3D_waitfifo ( struct savagefb_par * par , int space )
{
int slots = MAXFIFO - space ;
2005-11-07 12:00:41 +03:00
while ( ( savage_in32 ( 0x48C00 , par ) & 0x0000ffff ) > slots ) ;
2005-04-17 02:20:36 +04:00
}
static void
savage4_waitfifo ( struct savagefb_par * par , int space )
{
int slots = MAXFIFO - space ;
2005-11-07 12:00:41 +03:00
while ( ( savage_in32 ( 0x48C60 , par ) & 0x001fffff ) > slots ) ;
2005-04-17 02:20:36 +04:00
}
static void
savage2000_waitfifo ( struct savagefb_par * par , int space )
{
int slots = MAXFIFO - space ;
2005-11-07 12:00:41 +03:00
while ( ( savage_in32 ( 0x48C60 , par ) & 0x0000ffff ) > slots ) ;
2005-04-17 02:20:36 +04:00
}
/* Wait for idle accelerator */
static void
savage3D_waitidle ( struct savagefb_par * par )
{
2005-11-07 12:00:41 +03:00
while ( ( savage_in32 ( 0x48C00 , par ) & 0x0008ffff ) ! = 0x80000 ) ;
2005-04-17 02:20:36 +04:00
}
static void
savage4_waitidle ( struct savagefb_par * par )
{
2005-11-07 12:00:41 +03:00
while ( ( savage_in32 ( 0x48C60 , par ) & 0x00a00000 ) ! = 0x00a00000 ) ;
2005-04-17 02:20:36 +04:00
}
static void
savage2000_waitidle ( struct savagefb_par * par )
{
2005-11-07 12:00:41 +03:00
while ( ( savage_in32 ( 0x48C60 , par ) & 0x009fffff ) ) ;
2005-04-17 02:20:36 +04:00
}
2006-06-26 11:26:24 +04:00
# ifdef CONFIG_FB_SAVAGE_ACCEL
2005-04-17 02:20:36 +04:00
static void
2006-06-26 11:26:36 +04:00
SavageSetup2DEngine ( struct savagefb_par * par )
2005-04-17 02:20:36 +04:00
{
unsigned long GlobalBitmapDescriptor ;
GlobalBitmapDescriptor = 1 | 8 | BCI_BD_BW_DISABLE ;
2006-06-26 11:26:36 +04:00
BCI_BD_SET_BPP ( GlobalBitmapDescriptor , par - > depth ) ;
BCI_BD_SET_STRIDE ( GlobalBitmapDescriptor , par - > vwidth ) ;
2005-04-17 02:20:36 +04:00
switch ( par - > chip ) {
case S3_SAVAGE3D :
case S3_SAVAGE_MX :
/* Disable BCI */
2005-11-07 12:00:41 +03:00
savage_out32 ( 0x48C18 , savage_in32 ( 0x48C18 , par ) & 0x3FF0 , par ) ;
2005-04-17 02:20:36 +04:00
/* Setup BCI command overflow buffer */
2005-11-07 12:00:41 +03:00
savage_out32 ( 0x48C14 ,
( par - > cob_offset > > 11 ) | ( par - > cob_index < < 29 ) ,
par ) ;
2005-04-17 02:20:36 +04:00
/* Program shadow status update. */
2005-11-07 12:00:41 +03:00
savage_out32 ( 0x48C10 , 0x78207220 , par ) ;
savage_out32 ( 0x48C0C , 0 , par ) ;
2005-04-17 02:20:36 +04:00
/* Enable BCI and command overflow buffer */
2005-11-07 12:00:41 +03:00
savage_out32 ( 0x48C18 , savage_in32 ( 0x48C18 , par ) | 0x0C , par ) ;
2005-04-17 02:20:36 +04:00
break ;
case S3_SAVAGE4 :
case S3_PROSAVAGE :
case S3_SUPERSAVAGE :
/* Disable BCI */
2005-11-07 12:00:41 +03:00
savage_out32 ( 0x48C18 , savage_in32 ( 0x48C18 , par ) & 0x3FF0 , par ) ;
2005-04-17 02:20:36 +04:00
/* Program shadow status update */
2005-11-07 12:00:41 +03:00
savage_out32 ( 0x48C10 , 0x00700040 , par ) ;
savage_out32 ( 0x48C0C , 0 , par ) ;
2005-04-17 02:20:36 +04:00
/* Enable BCI without the COB */
2005-11-07 12:00:41 +03:00
savage_out32 ( 0x48C18 , savage_in32 ( 0x48C18 , par ) | 0x08 , par ) ;
2005-04-17 02:20:36 +04:00
break ;
case S3_SAVAGE2000 :
/* Disable BCI */
2005-11-07 12:00:41 +03:00
savage_out32 ( 0x48C18 , 0 , par ) ;
2005-04-17 02:20:36 +04:00
/* Setup BCI command overflow buffer */
2005-11-07 12:00:41 +03:00
savage_out32 ( 0x48C18 ,
( par - > cob_offset > > 7 ) | ( par - > cob_index ) ,
par ) ;
2005-04-17 02:20:36 +04:00
/* Disable shadow status update */
2005-11-07 12:00:41 +03:00
savage_out32 ( 0x48A30 , 0 , par ) ;
2005-04-17 02:20:36 +04:00
/* Enable BCI and command overflow buffer */
2005-11-07 12:00:41 +03:00
savage_out32 ( 0x48C18 , savage_in32 ( 0x48C18 , par ) | 0x00280000 ,
par ) ;
2005-04-17 02:20:36 +04:00
break ;
default :
break ;
}
/* Turn on 16-bit register access. */
2005-11-07 12:00:41 +03:00
vga_out8 ( 0x3d4 , 0x31 , par ) ;
vga_out8 ( 0x3d5 , 0x0c , par ) ;
2005-04-17 02:20:36 +04:00
/* Set stride to use GBD. */
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3d4 , 0x50 , par ) ;
vga_out8 ( 0x3d5 , vga_in8 ( 0x3d5 , par ) | 0xC1 , par ) ;
2005-04-17 02:20:36 +04:00
/* Enable 2D engine. */
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3d4 , 0x40 , par ) ;
vga_out8 ( 0x3d5 , 0x01 , par ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:36 +04:00
savage_out32 ( MONO_PAT_0 , ~ 0 , par ) ;
savage_out32 ( MONO_PAT_1 , ~ 0 , par ) ;
2005-04-17 02:20:36 +04:00
/* Setup plane masks */
2006-06-26 11:26:36 +04:00
savage_out32 ( 0x8128 , ~ 0 , par ) ; /* enable all write planes */
savage_out32 ( 0x812C , ~ 0 , par ) ; /* enable all read planes */
savage_out16 ( 0x8134 , 0x27 , par ) ;
savage_out16 ( 0x8136 , 0x07 , par ) ;
2005-04-17 02:20:36 +04:00
/* Now set the GBD */
par - > bci_ptr = 0 ;
2006-06-26 11:26:36 +04:00
par - > SavageWaitFifo ( par , 4 ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:36 +04:00
BCI_SEND ( BCI_CMD_SETREG | ( 1 < < 16 ) | BCI_GBD1 ) ;
BCI_SEND ( 0 ) ;
BCI_SEND ( BCI_CMD_SETREG | ( 1 < < 16 ) | BCI_GBD2 ) ;
BCI_SEND ( GlobalBitmapDescriptor ) ;
2007-03-17 00:38:18 +03:00
/*
* I don ' t know why , sending this twice fixes the intial black screen ,
* prevents X from crashing at least in Toshiba laptops with SavageIX .
* - - Tony
*/
par - > bci_ptr = 0 ;
par - > SavageWaitFifo ( par , 4 ) ;
BCI_SEND ( BCI_CMD_SETREG | ( 1 < < 16 ) | BCI_GBD1 ) ;
BCI_SEND ( 0 ) ;
BCI_SEND ( BCI_CMD_SETREG | ( 1 < < 16 ) | BCI_GBD2 ) ;
BCI_SEND ( GlobalBitmapDescriptor ) ;
2005-04-17 02:20:36 +04:00
}
2006-06-26 11:26:24 +04:00
static void savagefb_set_clip ( struct fb_info * info )
{
struct savagefb_par * par = info - > par ;
int cmd ;
cmd = BCI_CMD_NOP | BCI_CMD_CLIP_NEW ;
par - > bci_ptr = 0 ;
par - > SavageWaitFifo ( par , 3 ) ;
BCI_SEND ( cmd ) ;
BCI_SEND ( BCI_CLIP_TL ( 0 , 0 ) ) ;
BCI_SEND ( BCI_CLIP_BR ( 0xfff , 0xfff ) ) ;
}
# else
2006-06-26 11:26:36 +04:00
static void SavageSetup2DEngine ( struct savagefb_par * par ) { }
2006-06-26 11:26:24 +04:00
# endif
2005-04-17 02:20:36 +04:00
static void SavageCalcClock ( long freq , int min_m , int min_n1 , int max_n1 ,
int min_n2 , int max_n2 , long freq_min ,
long freq_max , unsigned int * mdiv ,
unsigned int * ndiv , unsigned int * r )
{
long diff , best_diff ;
unsigned int m ;
unsigned char n1 , n2 , best_n1 = 16 + 2 , best_n2 = 2 , best_m = 125 + 2 ;
if ( freq < freq_min / ( 1 < < max_n2 ) ) {
2006-06-26 11:26:36 +04:00
printk ( KERN_ERR " invalid frequency %ld Khz \n " , freq ) ;
2005-04-17 02:20:36 +04:00
freq = freq_min / ( 1 < < max_n2 ) ;
}
if ( freq > freq_max / ( 1 < < min_n2 ) ) {
2006-06-26 11:26:36 +04:00
printk ( KERN_ERR " invalid frequency %ld Khz \n " , freq ) ;
2005-04-17 02:20:36 +04:00
freq = freq_max / ( 1 < < min_n2 ) ;
}
/* work out suitable timings */
best_diff = freq ;
for ( n2 = min_n2 ; n2 < = max_n2 ; n2 + + ) {
for ( n1 = min_n1 + 2 ; n1 < = max_n1 + 2 ; n1 + + ) {
m = ( freq * n1 * ( 1 < < n2 ) + HALF_BASE_FREQ ) /
BASE_FREQ ;
if ( m < min_m + 2 | | m > 127 + 2 )
continue ;
if ( ( m * BASE_FREQ > = freq_min * n1 ) & &
( m * BASE_FREQ < = freq_max * n1 ) ) {
diff = freq * ( 1 < < n2 ) * n1 - BASE_FREQ * m ;
if ( diff < 0 )
diff = - diff ;
if ( diff < best_diff ) {
best_diff = diff ;
best_m = m ;
best_n1 = n1 ;
best_n2 = n2 ;
}
}
}
}
* ndiv = best_n1 - 2 ;
* r = best_n2 ;
* mdiv = best_m - 2 ;
}
static int common_calc_clock ( long freq , int min_m , int min_n1 , int max_n1 ,
int min_n2 , int max_n2 , long freq_min ,
long freq_max , unsigned char * mdiv ,
unsigned char * ndiv )
{
long diff , best_diff ;
unsigned int m ;
unsigned char n1 , n2 ;
unsigned char best_n1 = 16 + 2 , best_n2 = 2 , best_m = 125 + 2 ;
best_diff = freq ;
for ( n2 = min_n2 ; n2 < = max_n2 ; n2 + + ) {
for ( n1 = min_n1 + 2 ; n1 < = max_n1 + 2 ; n1 + + ) {
m = ( freq * n1 * ( 1 < < n2 ) + HALF_BASE_FREQ ) /
BASE_FREQ ;
if ( m < min_m + 2 | | m > 127 + 2 )
continue ;
2006-06-26 11:26:36 +04:00
if ( ( m * BASE_FREQ > = freq_min * n1 ) & &
( m * BASE_FREQ < = freq_max * n1 ) ) {
2005-04-17 02:20:36 +04:00
diff = freq * ( 1 < < n2 ) * n1 - BASE_FREQ * m ;
2006-06-26 11:26:36 +04:00
if ( diff < 0 )
2005-04-17 02:20:36 +04:00
diff = - diff ;
2006-06-26 11:26:36 +04:00
if ( diff < best_diff ) {
2005-04-17 02:20:36 +04:00
best_diff = diff ;
best_m = m ;
best_n1 = n1 ;
best_n2 = n2 ;
}
}
}
}
2006-06-26 11:26:36 +04:00
if ( max_n1 = = 63 )
2005-04-17 02:20:36 +04:00
* ndiv = ( best_n1 - 2 ) | ( best_n2 < < 6 ) ;
else
* ndiv = ( best_n1 - 2 ) | ( best_n2 < < 5 ) ;
* mdiv = best_m - 2 ;
return 0 ;
}
# ifdef SAVAGEFB_DEBUG
/* This function is used to debug, it prints out the contents of s3 regs */
2007-03-17 00:38:18 +03:00
static void SavagePrintRegs ( struct savagefb_par * par )
2005-04-17 02:20:36 +04:00
{
unsigned char i ;
int vgaCRIndex = 0x3d4 ;
int vgaCRReg = 0x3d5 ;
printk ( KERN_DEBUG " SR x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE "
2006-06-26 11:26:36 +04:00
" xF " ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:36 +04:00
for ( i = 0 ; i < 0x70 ; i + + ) {
if ( ! ( i % 16 ) )
printk ( KERN_DEBUG " \n SR%xx " , i > > 4 ) ;
vga_out8 ( 0x3c4 , i , par ) ;
printk ( KERN_DEBUG " %02x " , vga_in8 ( 0x3c5 , par ) ) ;
2005-04-17 02:20:36 +04:00
}
printk ( KERN_DEBUG " \n \n CR x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC "
2006-06-26 11:26:36 +04:00
" xD xE xF " ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:36 +04:00
for ( i = 0 ; i < 0xB7 ; i + + ) {
if ( ! ( i % 16 ) )
printk ( KERN_DEBUG " \n CR%xx " , i > > 4 ) ;
vga_out8 ( vgaCRIndex , i , par ) ;
printk ( KERN_DEBUG " %02x " , vga_in8 ( vgaCRReg , par ) ) ;
2005-04-17 02:20:36 +04:00
}
printk ( KERN_DEBUG " \n \n " ) ;
}
# endif
/* --------------------------------------------------------------------- */
2006-06-26 11:26:23 +04:00
static void savage_get_default_par ( struct savagefb_par * par , struct savage_reg * reg )
2005-04-17 02:20:36 +04:00
{
unsigned char cr3a , cr53 , cr66 ;
2006-06-26 11:26:36 +04:00
vga_out16 ( 0x3d4 , 0x4838 , par ) ;
vga_out16 ( 0x3d4 , 0xa039 , par ) ;
vga_out16 ( 0x3c4 , 0x0608 , par ) ;
vga_out8 ( 0x3d4 , 0x66 , par ) ;
cr66 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d5 , cr66 | 0x80 , par ) ;
vga_out8 ( 0x3d4 , 0x3a , par ) ;
cr3a = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d5 , cr3a | 0x80 , par ) ;
vga_out8 ( 0x3d4 , 0x53 , par ) ;
cr53 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d5 , cr53 & 0x7f , par ) ;
vga_out8 ( 0x3d4 , 0x66 , par ) ;
vga_out8 ( 0x3d5 , cr66 , par ) ;
vga_out8 ( 0x3d4 , 0x3a , par ) ;
vga_out8 ( 0x3d5 , cr3a , par ) ;
vga_out8 ( 0x3d4 , 0x66 , par ) ;
vga_out8 ( 0x3d5 , cr66 , par ) ;
vga_out8 ( 0x3d4 , 0x3a , par ) ;
vga_out8 ( 0x3d5 , cr3a , par ) ;
2005-04-17 02:20:36 +04:00
/* unlock extended seq regs */
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3c4 , 0x08 , par ) ;
reg - > SR08 = vga_in8 ( 0x3c5 , par ) ;
vga_out8 ( 0x3c5 , 0x06 , par ) ;
2005-04-17 02:20:36 +04:00
/* now save all the extended regs we need */
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3d4 , 0x31 , par ) ;
reg - > CR31 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d4 , 0x32 , par ) ;
reg - > CR32 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d4 , 0x34 , par ) ;
reg - > CR34 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d4 , 0x36 , par ) ;
reg - > CR36 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d4 , 0x3a , par ) ;
reg - > CR3A = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d4 , 0x40 , par ) ;
reg - > CR40 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d4 , 0x42 , par ) ;
reg - > CR42 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d4 , 0x45 , par ) ;
reg - > CR45 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d4 , 0x50 , par ) ;
reg - > CR50 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d4 , 0x51 , par ) ;
reg - > CR51 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d4 , 0x53 , par ) ;
reg - > CR53 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d4 , 0x58 , par ) ;
reg - > CR58 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d4 , 0x60 , par ) ;
reg - > CR60 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d4 , 0x66 , par ) ;
reg - > CR66 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d4 , 0x67 , par ) ;
reg - > CR67 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d4 , 0x68 , par ) ;
reg - > CR68 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d4 , 0x69 , par ) ;
reg - > CR69 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d4 , 0x6f , par ) ;
reg - > CR6F = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d4 , 0x33 , par ) ;
reg - > CR33 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d4 , 0x86 , par ) ;
reg - > CR86 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d4 , 0x88 , par ) ;
reg - > CR88 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d4 , 0x90 , par ) ;
reg - > CR90 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d4 , 0x91 , par ) ;
reg - > CR91 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d4 , 0xb0 , par ) ;
reg - > CRB0 = vga_in8 ( 0x3d5 , par ) | 0x80 ;
2005-04-17 02:20:36 +04:00
/* extended mode timing regs */
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3d4 , 0x3b , par ) ;
reg - > CR3B = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d4 , 0x3c , par ) ;
reg - > CR3C = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d4 , 0x43 , par ) ;
reg - > CR43 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d4 , 0x5d , par ) ;
reg - > CR5D = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d4 , 0x5e , par ) ;
reg - > CR5E = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d4 , 0x65 , par ) ;
reg - > CR65 = vga_in8 ( 0x3d5 , par ) ;
2005-04-17 02:20:36 +04:00
/* save seq extended regs for DCLK PLL programming */
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3c4 , 0x0e , par ) ;
reg - > SR0E = vga_in8 ( 0x3c5 , par ) ;
vga_out8 ( 0x3c4 , 0x0f , par ) ;
reg - > SR0F = vga_in8 ( 0x3c5 , par ) ;
vga_out8 ( 0x3c4 , 0x10 , par ) ;
reg - > SR10 = vga_in8 ( 0x3c5 , par ) ;
vga_out8 ( 0x3c4 , 0x11 , par ) ;
reg - > SR11 = vga_in8 ( 0x3c5 , par ) ;
vga_out8 ( 0x3c4 , 0x12 , par ) ;
reg - > SR12 = vga_in8 ( 0x3c5 , par ) ;
vga_out8 ( 0x3c4 , 0x13 , par ) ;
reg - > SR13 = vga_in8 ( 0x3c5 , par ) ;
vga_out8 ( 0x3c4 , 0x29 , par ) ;
reg - > SR29 = vga_in8 ( 0x3c5 , par ) ;
vga_out8 ( 0x3c4 , 0x15 , par ) ;
reg - > SR15 = vga_in8 ( 0x3c5 , par ) ;
vga_out8 ( 0x3c4 , 0x30 , par ) ;
reg - > SR30 = vga_in8 ( 0x3c5 , par ) ;
vga_out8 ( 0x3c4 , 0x18 , par ) ;
reg - > SR18 = vga_in8 ( 0x3c5 , par ) ;
2005-04-17 02:20:36 +04:00
/* Save flat panel expansion regsters. */
if ( par - > chip = = S3_SAVAGE_MX ) {
int i ;
for ( i = 0 ; i < 8 ; i + + ) {
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3c4 , 0x54 + i , par ) ;
reg - > SR54 [ i ] = vga_in8 ( 0x3c5 , par ) ;
2005-04-17 02:20:36 +04:00
}
}
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3d4 , 0x66 , par ) ;
cr66 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d5 , cr66 | 0x80 , par ) ;
vga_out8 ( 0x3d4 , 0x3a , par ) ;
cr3a = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d5 , cr3a | 0x80 , par ) ;
2005-04-17 02:20:36 +04:00
/* now save MIU regs */
if ( par - > chip ! = S3_SAVAGE_MX ) {
2006-06-26 11:26:23 +04:00
reg - > MMPR0 = savage_in32 ( FIFO_CONTROL_REG , par ) ;
reg - > MMPR1 = savage_in32 ( MIU_CONTROL_REG , par ) ;
reg - > MMPR2 = savage_in32 ( STREAMS_TIMEOUT_REG , par ) ;
reg - > MMPR3 = savage_in32 ( MISC_TIMEOUT_REG , par ) ;
2005-04-17 02:20:36 +04:00
}
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3d4 , 0x3a , par ) ;
vga_out8 ( 0x3d5 , cr3a , par ) ;
vga_out8 ( 0x3d4 , 0x66 , par ) ;
vga_out8 ( 0x3d5 , cr66 , par ) ;
2005-04-17 02:20:36 +04:00
}
2006-06-26 11:26:24 +04:00
static void savage_set_default_par ( struct savagefb_par * par ,
struct savage_reg * reg )
{
unsigned char cr3a , cr53 , cr66 ;
vga_out16 ( 0x3d4 , 0x4838 , par ) ;
vga_out16 ( 0x3d4 , 0xa039 , par ) ;
vga_out16 ( 0x3c4 , 0x0608 , par ) ;
vga_out8 ( 0x3d4 , 0x66 , par ) ;
cr66 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d5 , cr66 | 0x80 , par ) ;
vga_out8 ( 0x3d4 , 0x3a , par ) ;
cr3a = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d5 , cr3a | 0x80 , par ) ;
vga_out8 ( 0x3d4 , 0x53 , par ) ;
cr53 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d5 , cr53 & 0x7f , par ) ;
vga_out8 ( 0x3d4 , 0x66 , par ) ;
vga_out8 ( 0x3d5 , cr66 , par ) ;
vga_out8 ( 0x3d4 , 0x3a , par ) ;
vga_out8 ( 0x3d5 , cr3a , par ) ;
vga_out8 ( 0x3d4 , 0x66 , par ) ;
vga_out8 ( 0x3d5 , cr66 , par ) ;
vga_out8 ( 0x3d4 , 0x3a , par ) ;
vga_out8 ( 0x3d5 , cr3a , par ) ;
/* unlock extended seq regs */
vga_out8 ( 0x3c4 , 0x08 , par ) ;
vga_out8 ( 0x3c5 , reg - > SR08 , par ) ;
vga_out8 ( 0x3c5 , 0x06 , par ) ;
/* now restore all the extended regs we need */
vga_out8 ( 0x3d4 , 0x31 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR31 , par ) ;
vga_out8 ( 0x3d4 , 0x32 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR32 , par ) ;
vga_out8 ( 0x3d4 , 0x34 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR34 , par ) ;
vga_out8 ( 0x3d4 , 0x36 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR36 , par ) ;
vga_out8 ( 0x3d4 , 0x3a , par ) ;
vga_out8 ( 0x3d5 , reg - > CR3A , par ) ;
vga_out8 ( 0x3d4 , 0x40 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR40 , par ) ;
vga_out8 ( 0x3d4 , 0x42 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR42 , par ) ;
vga_out8 ( 0x3d4 , 0x45 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR45 , par ) ;
vga_out8 ( 0x3d4 , 0x50 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR50 , par ) ;
vga_out8 ( 0x3d4 , 0x51 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR51 , par ) ;
vga_out8 ( 0x3d4 , 0x53 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR53 , par ) ;
vga_out8 ( 0x3d4 , 0x58 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR58 , par ) ;
vga_out8 ( 0x3d4 , 0x60 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR60 , par ) ;
vga_out8 ( 0x3d4 , 0x66 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR66 , par ) ;
vga_out8 ( 0x3d4 , 0x67 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR67 , par ) ;
vga_out8 ( 0x3d4 , 0x68 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR68 , par ) ;
vga_out8 ( 0x3d4 , 0x69 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR69 , par ) ;
vga_out8 ( 0x3d4 , 0x6f , par ) ;
vga_out8 ( 0x3d5 , reg - > CR6F , par ) ;
vga_out8 ( 0x3d4 , 0x33 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR33 , par ) ;
vga_out8 ( 0x3d4 , 0x86 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR86 , par ) ;
vga_out8 ( 0x3d4 , 0x88 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR88 , par ) ;
vga_out8 ( 0x3d4 , 0x90 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR90 , par ) ;
vga_out8 ( 0x3d4 , 0x91 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR91 , par ) ;
vga_out8 ( 0x3d4 , 0xb0 , par ) ;
vga_out8 ( 0x3d5 , reg - > CRB0 , par ) ;
/* extended mode timing regs */
vga_out8 ( 0x3d4 , 0x3b , par ) ;
vga_out8 ( 0x3d5 , reg - > CR3B , par ) ;
vga_out8 ( 0x3d4 , 0x3c , par ) ;
vga_out8 ( 0x3d5 , reg - > CR3C , par ) ;
vga_out8 ( 0x3d4 , 0x43 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR43 , par ) ;
vga_out8 ( 0x3d4 , 0x5d , par ) ;
vga_out8 ( 0x3d5 , reg - > CR5D , par ) ;
vga_out8 ( 0x3d4 , 0x5e , par ) ;
vga_out8 ( 0x3d5 , reg - > CR5E , par ) ;
vga_out8 ( 0x3d4 , 0x65 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR65 , par ) ;
/* save seq extended regs for DCLK PLL programming */
vga_out8 ( 0x3c4 , 0x0e , par ) ;
vga_out8 ( 0x3c5 , reg - > SR0E , par ) ;
vga_out8 ( 0x3c4 , 0x0f , par ) ;
vga_out8 ( 0x3c5 , reg - > SR0F , par ) ;
vga_out8 ( 0x3c4 , 0x10 , par ) ;
vga_out8 ( 0x3c5 , reg - > SR10 , par ) ;
vga_out8 ( 0x3c4 , 0x11 , par ) ;
vga_out8 ( 0x3c5 , reg - > SR11 , par ) ;
vga_out8 ( 0x3c4 , 0x12 , par ) ;
vga_out8 ( 0x3c5 , reg - > SR12 , par ) ;
vga_out8 ( 0x3c4 , 0x13 , par ) ;
vga_out8 ( 0x3c5 , reg - > SR13 , par ) ;
vga_out8 ( 0x3c4 , 0x29 , par ) ;
vga_out8 ( 0x3c5 , reg - > SR29 , par ) ;
vga_out8 ( 0x3c4 , 0x15 , par ) ;
vga_out8 ( 0x3c5 , reg - > SR15 , par ) ;
vga_out8 ( 0x3c4 , 0x30 , par ) ;
vga_out8 ( 0x3c5 , reg - > SR30 , par ) ;
vga_out8 ( 0x3c4 , 0x18 , par ) ;
vga_out8 ( 0x3c5 , reg - > SR18 , par ) ;
/* Save flat panel expansion regsters. */
if ( par - > chip = = S3_SAVAGE_MX ) {
int i ;
for ( i = 0 ; i < 8 ; i + + ) {
vga_out8 ( 0x3c4 , 0x54 + i , par ) ;
vga_out8 ( 0x3c5 , reg - > SR54 [ i ] , par ) ;
}
}
vga_out8 ( 0x3d4 , 0x66 , par ) ;
cr66 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d5 , cr66 | 0x80 , par ) ;
vga_out8 ( 0x3d4 , 0x3a , par ) ;
cr3a = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d5 , cr3a | 0x80 , par ) ;
/* now save MIU regs */
if ( par - > chip ! = S3_SAVAGE_MX ) {
savage_out32 ( FIFO_CONTROL_REG , reg - > MMPR0 , par ) ;
savage_out32 ( MIU_CONTROL_REG , reg - > MMPR1 , par ) ;
savage_out32 ( STREAMS_TIMEOUT_REG , reg - > MMPR2 , par ) ;
savage_out32 ( MISC_TIMEOUT_REG , reg - > MMPR3 , par ) ;
}
vga_out8 ( 0x3d4 , 0x3a , par ) ;
vga_out8 ( 0x3d5 , cr3a , par ) ;
vga_out8 ( 0x3d4 , 0x66 , par ) ;
vga_out8 ( 0x3d5 , cr66 , par ) ;
}
2007-02-12 11:55:19 +03:00
static void savage_update_var ( struct fb_var_screeninfo * var ,
const struct fb_videomode * modedb )
2005-04-17 02:20:36 +04:00
{
var - > xres = var - > xres_virtual = modedb - > xres ;
var - > yres = modedb - > yres ;
if ( var - > yres_virtual < var - > yres )
var - > yres_virtual = var - > yres ;
var - > xoffset = var - > yoffset = 0 ;
var - > pixclock = modedb - > pixclock ;
var - > left_margin = modedb - > left_margin ;
var - > right_margin = modedb - > right_margin ;
var - > upper_margin = modedb - > upper_margin ;
var - > lower_margin = modedb - > lower_margin ;
var - > hsync_len = modedb - > hsync_len ;
var - > vsync_len = modedb - > vsync_len ;
var - > sync = modedb - > sync ;
var - > vmode = modedb - > vmode ;
}
2006-06-26 11:26:36 +04:00
static int savagefb_check_var ( struct fb_var_screeninfo * var ,
struct fb_info * info )
2005-04-17 02:20:36 +04:00
{
2006-01-10 07:53:02 +03:00
struct savagefb_par * par = info - > par ;
2005-04-17 02:20:36 +04:00
int memlen , vramlen , mode_valid = 0 ;
DBG ( " savagefb_check_var " ) ;
var - > transp . offset = 0 ;
var - > transp . length = 0 ;
switch ( var - > bits_per_pixel ) {
case 8 :
var - > red . offset = var - > green . offset =
var - > blue . offset = 0 ;
var - > red . length = var - > green . length =
var - > blue . length = var - > bits_per_pixel ;
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 :
var - > transp . offset = 24 ;
var - > transp . length = 8 ;
var - > red . offset = 16 ;
var - > red . length = 8 ;
var - > green . offset = 8 ;
var - > green . length = 8 ;
var - > blue . offset = 0 ;
var - > blue . length = 8 ;
break ;
default :
return - EINVAL ;
}
if ( ! info - > monspecs . hfmax | | ! info - > monspecs . vfmax | |
! info - > monspecs . dclkmax | | ! fb_validate_mode ( var , info ) )
mode_valid = 1 ;
/* calculate modeline if supported by monitor */
if ( ! mode_valid & & info - > monspecs . gtf ) {
if ( ! fb_get_mode ( FB_MAXTIMINGS , 0 , var , info ) )
mode_valid = 1 ;
}
if ( ! mode_valid ) {
2007-02-12 11:55:19 +03:00
const struct fb_videomode * mode ;
2005-04-17 02:20:36 +04:00
mode = fb_find_best_mode ( var , & info - > modelist ) ;
if ( mode ) {
savage_update_var ( var , mode ) ;
mode_valid = 1 ;
}
}
if ( ! mode_valid & & info - > monspecs . modedb_len )
return - EINVAL ;
/* Is the mode larger than the LCD panel? */
if ( par - > SavagePanelWidth & &
( var - > xres > par - > SavagePanelWidth | |
var - > yres > par - > SavagePanelHeight ) ) {
2006-06-26 11:26:36 +04:00
printk ( KERN_INFO " Mode (%dx%d) larger than the LCD panel "
" (%dx%d) \n " , var - > xres , var - > yres ,
par - > SavagePanelWidth ,
par - > SavagePanelHeight ) ;
2005-04-17 02:20:36 +04:00
return - 1 ;
}
if ( var - > yres_virtual < var - > yres )
var - > yres_virtual = var - > yres ;
if ( var - > xres_virtual < var - > xres )
var - > xres_virtual = var - > xres ;
vramlen = info - > fix . smem_len ;
memlen = var - > xres_virtual * var - > bits_per_pixel *
var - > yres_virtual / 8 ;
if ( memlen > vramlen ) {
var - > yres_virtual = vramlen * 8 /
( var - > xres_virtual * var - > bits_per_pixel ) ;
memlen = var - > xres_virtual * var - > bits_per_pixel *
var - > yres_virtual / 8 ;
}
/* we must round yres/xres down, we already rounded y/xres_virtual up
if it was possible . We should return - EINVAL , but I disagree */
if ( var - > yres_virtual < var - > yres )
var - > yres = var - > yres_virtual ;
if ( var - > xres_virtual < var - > xres )
var - > xres = var - > xres_virtual ;
if ( var - > xoffset + var - > xres > var - > xres_virtual )
var - > xoffset = var - > xres_virtual - var - > xres ;
if ( var - > yoffset + var - > yres > var - > yres_virtual )
var - > yoffset = var - > yres_virtual - var - > yres ;
return 0 ;
}
2006-06-26 11:26:36 +04:00
static int savagefb_decode_var ( struct fb_var_screeninfo * var ,
struct savagefb_par * par ,
struct savage_reg * reg )
2005-04-17 02:20:36 +04:00
{
struct xtimings timings ;
int width , dclk , i , j ; /*, refresh; */
unsigned int m , n , r ;
unsigned char tmp = 0 ;
unsigned int pixclock = var - > pixclock ;
DBG ( " savagefb_decode_var " ) ;
2006-06-26 11:26:36 +04:00
memset ( & timings , 0 , sizeof ( timings ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! pixclock ) pixclock = 10000 ; /* 10ns = 100MHz */
timings . Clock = 1000000000 / pixclock ;
if ( timings . Clock < 1 ) timings . Clock = 1 ;
timings . dblscan = var - > vmode & FB_VMODE_DOUBLE ;
timings . interlaced = var - > vmode & FB_VMODE_INTERLACED ;
timings . HDisplay = var - > xres ;
timings . HSyncStart = timings . HDisplay + var - > right_margin ;
timings . HSyncEnd = timings . HSyncStart + var - > hsync_len ;
timings . HTotal = timings . HSyncEnd + var - > left_margin ;
timings . VDisplay = var - > yres ;
timings . VSyncStart = timings . VDisplay + var - > lower_margin ;
timings . VSyncEnd = timings . VSyncStart + var - > vsync_len ;
timings . VTotal = timings . VSyncEnd + var - > upper_margin ;
timings . sync = var - > sync ;
par - > depth = var - > bits_per_pixel ;
par - > vwidth = var - > xres_virtual ;
if ( var - > bits_per_pixel = = 16 & & par - > chip = = S3_SAVAGE3D ) {
timings . HDisplay * = 2 ;
timings . HSyncStart * = 2 ;
timings . HSyncEnd * = 2 ;
timings . HTotal * = 2 ;
}
/*
* This will allocate the datastructure and initialize all of the
* generic VGA registers .
*/
2006-06-26 11:26:36 +04:00
vgaHWInit ( var , par , & timings , reg ) ;
2005-04-17 02:20:36 +04:00
/* We need to set CR67 whether or not we use the BIOS. */
dclk = timings . Clock ;
2006-06-26 11:26:23 +04:00
reg - > CR67 = 0x00 ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:36 +04:00
switch ( var - > bits_per_pixel ) {
2005-04-17 02:20:36 +04:00
case 8 :
2006-06-26 11:26:36 +04:00
if ( ( par - > chip = = S3_SAVAGE2000 ) & & ( dclk > = 230000 ) )
2006-06-26 11:26:23 +04:00
reg - > CR67 = 0x10 ; /* 8bpp, 2 pixels/clock */
2005-04-17 02:20:36 +04:00
else
2006-06-26 11:26:23 +04:00
reg - > CR67 = 0x00 ; /* 8bpp, 1 pixel/clock */
2005-04-17 02:20:36 +04:00
break ;
case 15 :
2006-06-26 11:26:36 +04:00
if ( S3_SAVAGE_MOBILE_SERIES ( par - > chip ) | |
( ( par - > chip = = S3_SAVAGE2000 ) & & ( dclk > = 230000 ) ) )
2006-06-26 11:26:23 +04:00
reg - > CR67 = 0x30 ; /* 15bpp, 2 pixel/clock */
2005-04-17 02:20:36 +04:00
else
2006-06-26 11:26:23 +04:00
reg - > CR67 = 0x20 ; /* 15bpp, 1 pixels/clock */
2005-04-17 02:20:36 +04:00
break ;
case 16 :
2006-06-26 11:26:36 +04:00
if ( S3_SAVAGE_MOBILE_SERIES ( par - > chip ) | |
( ( par - > chip = = S3_SAVAGE2000 ) & & ( dclk > = 230000 ) ) )
2006-06-26 11:26:23 +04:00
reg - > CR67 = 0x50 ; /* 16bpp, 2 pixel/clock */
2005-04-17 02:20:36 +04:00
else
2006-06-26 11:26:23 +04:00
reg - > CR67 = 0x40 ; /* 16bpp, 1 pixels/clock */
2005-04-17 02:20:36 +04:00
break ;
case 24 :
2006-06-26 11:26:23 +04:00
reg - > CR67 = 0x70 ;
2005-04-17 02:20:36 +04:00
break ;
case 32 :
2006-06-26 11:26:23 +04:00
reg - > CR67 = 0xd0 ;
2005-04-17 02:20:36 +04:00
break ;
}
/*
* Either BIOS use is disabled , or we failed to find a suitable
* match . Fall back to traditional register - crunching .
*/
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3d4 , 0x3a , par ) ;
tmp = vga_in8 ( 0x3d5 , par ) ;
2005-04-17 02:20:36 +04:00
if ( 1 /*FIXME:psav->pci_burst*/ )
2006-06-26 11:26:23 +04:00
reg - > CR3A = ( tmp & 0x7f ) | 0x15 ;
2005-04-17 02:20:36 +04:00
else
2006-06-26 11:26:23 +04:00
reg - > CR3A = tmp | 0x95 ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:23 +04:00
reg - > CR53 = 0x00 ;
reg - > CR31 = 0x8c ;
reg - > CR66 = 0x89 ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3d4 , 0x58 , par ) ;
reg - > CR58 = vga_in8 ( 0x3d5 , par ) & 0x80 ;
2006-06-26 11:26:23 +04:00
reg - > CR58 | = 0x13 ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:23 +04:00
reg - > SR15 = 0x03 | 0x80 ;
reg - > SR18 = 0x00 ;
reg - > CR43 = reg - > CR45 = reg - > CR65 = 0x00 ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3d4 , 0x40 , par ) ;
reg - > CR40 = vga_in8 ( 0x3d5 , par ) & ~ 0x01 ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:23 +04:00
reg - > MMPR0 = 0x010400 ;
reg - > MMPR1 = 0x00 ;
reg - > MMPR2 = 0x0808 ;
reg - > MMPR3 = 0x08080810 ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:36 +04:00
SavageCalcClock ( dclk , 1 , 1 , 127 , 0 , 4 , 180000 , 360000 , & m , & n , & r ) ;
2005-04-17 02:20:36 +04:00
/* m = 107; n = 4; r = 2; */
if ( par - > MCLK < = 0 ) {
2006-06-26 11:26:23 +04:00
reg - > SR10 = 255 ;
reg - > SR11 = 255 ;
2005-04-17 02:20:36 +04:00
} else {
2006-06-26 11:26:36 +04:00
common_calc_clock ( par - > MCLK , 1 , 1 , 31 , 0 , 3 , 135000 , 270000 ,
2006-06-26 11:26:23 +04:00
& reg - > SR11 , & reg - > SR10 ) ;
/* reg->SR10 = 80; // MCLK == 286000 */
/* reg->SR11 = 125; */
2005-04-17 02:20:36 +04:00
}
2006-06-26 11:26:23 +04:00
reg - > SR12 = ( r < < 6 ) | ( n & 0x3f ) ;
reg - > SR13 = m & 0xff ;
reg - > SR29 = ( r & 4 ) | ( m & 0x100 ) > > 5 | ( n & 0x40 ) > > 2 ;
2005-04-17 02:20:36 +04:00
if ( var - > bits_per_pixel < 24 )
2006-06-26 11:26:23 +04:00
reg - > MMPR0 - = 0x8000 ;
2005-04-17 02:20:36 +04:00
else
2006-06-26 11:26:23 +04:00
reg - > MMPR0 - = 0x4000 ;
2005-04-17 02:20:36 +04:00
if ( timings . interlaced )
2006-06-26 11:26:23 +04:00
reg - > CR42 = 0x20 ;
2005-04-17 02:20:36 +04:00
else
2006-06-26 11:26:23 +04:00
reg - > CR42 = 0x00 ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:23 +04:00
reg - > CR34 = 0x10 ; /* display fifo */
2005-04-17 02:20:36 +04:00
i = ( ( ( ( timings . HTotal > > 3 ) - 5 ) & 0x100 ) > > 8 ) |
( ( ( ( timings . HDisplay > > 3 ) - 1 ) & 0x100 ) > > 7 ) |
( ( ( ( timings . HSyncStart > > 3 ) - 1 ) & 0x100 ) > > 6 ) |
( ( timings . HSyncStart & 0x800 ) > > 7 ) ;
if ( ( timings . HSyncEnd > > 3 ) - ( timings . HSyncStart > > 3 ) > 64 )
i | = 0x08 ;
if ( ( timings . HSyncEnd > > 3 ) - ( timings . HSyncStart > > 3 ) > 32 )
i | = 0x20 ;
2006-06-26 11:26:23 +04:00
j = ( reg - > CRTC [ 0 ] + ( ( i & 0x01 ) < < 8 ) +
reg - > CRTC [ 4 ] + ( ( i & 0x10 ) < < 4 ) + 1 ) / 2 ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:23 +04:00
if ( j - ( reg - > CRTC [ 4 ] + ( ( i & 0x10 ) < < 4 ) ) < 4 ) {
if ( reg - > CRTC [ 4 ] + ( ( i & 0x10 ) < < 4 ) + 4 < =
reg - > CRTC [ 0 ] + ( ( i & 0x01 ) < < 8 ) )
j = reg - > CRTC [ 4 ] + ( ( i & 0x10 ) < < 4 ) + 4 ;
2005-04-17 02:20:36 +04:00
else
2006-06-26 11:26:23 +04:00
j = reg - > CRTC [ 0 ] + ( ( i & 0x01 ) < < 8 ) + 1 ;
2005-04-17 02:20:36 +04:00
}
2006-06-26 11:26:23 +04:00
reg - > CR3B = j & 0xff ;
2005-04-17 02:20:36 +04:00
i | = ( j & 0x100 ) > > 2 ;
2006-06-26 11:26:23 +04:00
reg - > CR3C = ( reg - > CRTC [ 0 ] + ( ( i & 0x01 ) < < 8 ) ) / 2 ;
reg - > CR5D = i ;
reg - > CR5E = ( ( ( timings . VTotal - 2 ) & 0x400 ) > > 10 ) |
2005-04-17 02:20:36 +04:00
( ( ( timings . VDisplay - 1 ) & 0x400 ) > > 9 ) |
( ( ( timings . VSyncStart ) & 0x400 ) > > 8 ) |
( ( ( timings . VSyncStart ) & 0x400 ) > > 6 ) | 0x40 ;
width = ( var - > xres_virtual * ( ( var - > bits_per_pixel + 7 ) / 8 ) ) > > 3 ;
2006-06-26 11:26:23 +04:00
reg - > CR91 = reg - > CRTC [ 19 ] = 0xff & width ;
reg - > CR51 = ( 0x300 & width ) > > 4 ;
reg - > CR90 = 0x80 | ( width > > 8 ) ;
reg - > MiscOutReg | = 0x0c ;
2005-04-17 02:20:36 +04:00
/* Set frame buffer description. */
if ( var - > bits_per_pixel < = 8 )
2006-06-26 11:26:23 +04:00
reg - > CR50 = 0 ;
2005-04-17 02:20:36 +04:00
else if ( var - > bits_per_pixel < = 16 )
2006-06-26 11:26:23 +04:00
reg - > CR50 = 0x10 ;
2005-04-17 02:20:36 +04:00
else
2006-06-26 11:26:23 +04:00
reg - > CR50 = 0x30 ;
2005-04-17 02:20:36 +04:00
if ( var - > xres_virtual < = 640 )
2006-06-26 11:26:23 +04:00
reg - > CR50 | = 0x40 ;
2005-04-17 02:20:36 +04:00
else if ( var - > xres_virtual = = 800 )
2006-06-26 11:26:23 +04:00
reg - > CR50 | = 0x80 ;
2005-04-17 02:20:36 +04:00
else if ( var - > xres_virtual = = 1024 )
2006-06-26 11:26:23 +04:00
reg - > CR50 | = 0x00 ;
2005-04-17 02:20:36 +04:00
else if ( var - > xres_virtual = = 1152 )
2006-06-26 11:26:23 +04:00
reg - > CR50 | = 0x01 ;
2005-04-17 02:20:36 +04:00
else if ( var - > xres_virtual = = 1280 )
2006-06-26 11:26:23 +04:00
reg - > CR50 | = 0xc0 ;
2005-04-17 02:20:36 +04:00
else if ( var - > xres_virtual = = 1600 )
2006-06-26 11:26:23 +04:00
reg - > CR50 | = 0x81 ;
2005-04-17 02:20:36 +04:00
else
2006-06-26 11:26:23 +04:00
reg - > CR50 | = 0xc1 ; /* Use GBD */
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:36 +04:00
if ( par - > chip = = S3_SAVAGE2000 )
2006-06-26 11:26:23 +04:00
reg - > CR33 = 0x08 ;
2005-04-17 02:20:36 +04:00
else
2006-06-26 11:26:23 +04:00
reg - > CR33 = 0x20 ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:23 +04:00
reg - > CRTC [ 0x17 ] = 0xeb ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:23 +04:00
reg - > CR67 | = 1 ;
2005-04-17 02:20:36 +04:00
2005-11-07 12:00:41 +03:00
vga_out8 ( 0x3d4 , 0x36 , par ) ;
2006-06-26 11:26:36 +04:00
reg - > CR36 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d4 , 0x68 , par ) ;
reg - > CR68 = vga_in8 ( 0x3d5 , par ) ;
2006-06-26 11:26:23 +04:00
reg - > CR69 = 0 ;
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3d4 , 0x6f , par ) ;
reg - > CR6F = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d4 , 0x86 , par ) ;
reg - > CR86 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d4 , 0x88 , par ) ;
reg - > CR88 = vga_in8 ( 0x3d5 , par ) | 0x08 ;
vga_out8 ( 0x3d4 , 0xb0 , par ) ;
reg - > CRB0 = vga_in8 ( 0x3d5 , par ) | 0x80 ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/* --------------------------------------------------------------------- */
/*
* Set a single color register . Return ! = 0 for invalid regno .
*/
static int savagefb_setcolreg ( unsigned regno ,
unsigned red ,
unsigned green ,
unsigned blue ,
unsigned transp ,
struct fb_info * info )
{
2006-01-10 07:53:02 +03:00
struct savagefb_par * par = info - > par ;
2005-04-17 02:20:36 +04:00
if ( regno > = NR_PALETTE )
return - EINVAL ;
par - > palette [ regno ] . red = red ;
par - > palette [ regno ] . green = green ;
par - > palette [ regno ] . blue = blue ;
par - > palette [ regno ] . transp = transp ;
switch ( info - > var . bits_per_pixel ) {
case 8 :
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3c8 , regno , par ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3c9 , red > > 10 , par ) ;
vga_out8 ( 0x3c9 , green > > 10 , par ) ;
vga_out8 ( 0x3c9 , blue > > 10 , par ) ;
2005-04-17 02:20:36 +04:00
break ;
case 16 :
if ( regno < 16 )
( ( u32 * ) info - > pseudo_palette ) [ regno ] =
( ( red & 0xf800 ) ) |
( ( green & 0xfc00 ) > > 5 ) |
( ( blue & 0xf800 ) > > 11 ) ;
break ;
case 24 :
if ( regno < 16 )
( ( u32 * ) info - > pseudo_palette ) [ regno ] =
( ( red & 0xff00 ) < < 8 ) |
( ( green & 0xff00 ) ) |
( ( blue & 0xff00 ) > > 8 ) ;
break ;
case 32 :
if ( regno < 16 )
( ( u32 * ) info - > pseudo_palette ) [ regno ] =
( ( transp & 0xff00 ) < < 16 ) |
( ( red & 0xff00 ) < < 8 ) |
( ( green & 0xff00 ) ) |
( ( blue & 0xff00 ) > > 8 ) ;
break ;
default :
return 1 ;
}
return 0 ;
}
2006-06-26 11:26:36 +04:00
static void savagefb_set_par_int ( struct savagefb_par * par , struct savage_reg * reg )
2005-04-17 02:20:36 +04:00
{
unsigned char tmp , cr3a , cr66 , cr67 ;
2006-06-26 11:26:36 +04:00
DBG ( " savagefb_set_par_int " ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:36 +04:00
par - > SavageWaitIdle ( par ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3c2 , 0x23 , par ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:36 +04:00
vga_out16 ( 0x3d4 , 0x4838 , par ) ;
vga_out16 ( 0x3d4 , 0xa539 , par ) ;
vga_out16 ( 0x3c4 , 0x0608 , par ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:36 +04:00
vgaHWProtect ( par , 1 ) ;
2005-04-17 02:20:36 +04:00
/*
* Some Savage / MX and / IX systems go nuts when trying to exit the
* server after WindowMaker has displayed a gradient background . I
* haven ' t been able to find what causes it , but a non - destructive
* switch to mode 3 here seems to eliminate the issue .
*/
2005-11-07 12:00:41 +03:00
VerticalRetraceWait ( par ) ;
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3d4 , 0x67 , par ) ;
cr67 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d5 , cr67 /*par->CR67*/ & ~ 0x0c , par ) ; /* no STREAMS yet */
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3d4 , 0x23 , par ) ;
vga_out8 ( 0x3d5 , 0x00 , par ) ;
vga_out8 ( 0x3d4 , 0x26 , par ) ;
vga_out8 ( 0x3d5 , 0x00 , par ) ;
2005-04-17 02:20:36 +04:00
/* restore extended regs */
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3d4 , 0x66 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR66 , par ) ;
vga_out8 ( 0x3d4 , 0x3a , par ) ;
vga_out8 ( 0x3d5 , reg - > CR3A , par ) ;
vga_out8 ( 0x3d4 , 0x31 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR31 , par ) ;
vga_out8 ( 0x3d4 , 0x32 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR32 , par ) ;
vga_out8 ( 0x3d4 , 0x58 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR58 , par ) ;
vga_out8 ( 0x3d4 , 0x53 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR53 & 0x7f , par ) ;
vga_out16 ( 0x3c4 , 0x0608 , par ) ;
2005-04-17 02:20:36 +04:00
/* Restore DCLK registers. */
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3c4 , 0x0e , par ) ;
vga_out8 ( 0x3c5 , reg - > SR0E , par ) ;
vga_out8 ( 0x3c4 , 0x0f , par ) ;
vga_out8 ( 0x3c5 , reg - > SR0F , par ) ;
vga_out8 ( 0x3c4 , 0x29 , par ) ;
vga_out8 ( 0x3c5 , reg - > SR29 , par ) ;
vga_out8 ( 0x3c4 , 0x15 , par ) ;
vga_out8 ( 0x3c5 , reg - > SR15 , par ) ;
2005-04-17 02:20:36 +04:00
/* Restore flat panel expansion regsters. */
2006-06-26 11:26:36 +04:00
if ( par - > chip = = S3_SAVAGE_MX ) {
2005-04-17 02:20:36 +04:00
int i ;
2006-06-26 11:26:36 +04:00
for ( i = 0 ; i < 8 ; i + + ) {
vga_out8 ( 0x3c4 , 0x54 + i , par ) ;
vga_out8 ( 0x3c5 , reg - > SR54 [ i ] , par ) ;
2005-04-17 02:20:36 +04:00
}
}
2006-06-26 11:26:23 +04:00
vgaHWRestore ( par , reg ) ;
2005-04-17 02:20:36 +04:00
/* extended mode timing registers */
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3d4 , 0x53 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR53 , par ) ;
vga_out8 ( 0x3d4 , 0x5d , par ) ;
vga_out8 ( 0x3d5 , reg - > CR5D , par ) ;
vga_out8 ( 0x3d4 , 0x5e , par ) ;
vga_out8 ( 0x3d5 , reg - > CR5E , par ) ;
vga_out8 ( 0x3d4 , 0x3b , par ) ;
vga_out8 ( 0x3d5 , reg - > CR3B , par ) ;
vga_out8 ( 0x3d4 , 0x3c , par ) ;
vga_out8 ( 0x3d5 , reg - > CR3C , par ) ;
vga_out8 ( 0x3d4 , 0x43 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR43 , par ) ;
vga_out8 ( 0x3d4 , 0x65 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR65 , par ) ;
2005-04-17 02:20:36 +04:00
/* restore the desired video mode with cr67 */
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3d4 , 0x67 , par ) ;
2005-04-17 02:20:36 +04:00
/* following part not present in X11 driver */
2006-06-26 11:26:36 +04:00
cr67 = vga_in8 ( 0x3d5 , par ) & 0xf ;
vga_out8 ( 0x3d5 , 0x50 | cr67 , par ) ;
udelay ( 10000 ) ;
vga_out8 ( 0x3d4 , 0x67 , par ) ;
2005-04-17 02:20:36 +04:00
/* end of part */
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3d5 , reg - > CR67 & ~ 0x0c , par ) ;
2005-04-17 02:20:36 +04:00
/* other mode timing and extended regs */
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3d4 , 0x34 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR34 , par ) ;
vga_out8 ( 0x3d4 , 0x40 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR40 , par ) ;
vga_out8 ( 0x3d4 , 0x42 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR42 , par ) ;
vga_out8 ( 0x3d4 , 0x45 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR45 , par ) ;
vga_out8 ( 0x3d4 , 0x50 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR50 , par ) ;
vga_out8 ( 0x3d4 , 0x51 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR51 , par ) ;
2005-04-17 02:20:36 +04:00
/* memory timings */
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3d4 , 0x36 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR36 , par ) ;
vga_out8 ( 0x3d4 , 0x60 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR60 , par ) ;
vga_out8 ( 0x3d4 , 0x68 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR68 , par ) ;
vga_out8 ( 0x3d4 , 0x69 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR69 , par ) ;
vga_out8 ( 0x3d4 , 0x6f , par ) ;
vga_out8 ( 0x3d5 , reg - > CR6F , par ) ;
vga_out8 ( 0x3d4 , 0x33 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR33 , par ) ;
vga_out8 ( 0x3d4 , 0x86 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR86 , par ) ;
vga_out8 ( 0x3d4 , 0x88 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR88 , par ) ;
vga_out8 ( 0x3d4 , 0x90 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR90 , par ) ;
vga_out8 ( 0x3d4 , 0x91 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR91 , par ) ;
2005-04-17 02:20:36 +04:00
if ( par - > chip = = S3_SAVAGE4 ) {
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3d4 , 0xb0 , par ) ;
vga_out8 ( 0x3d5 , reg - > CRB0 , par ) ;
2005-04-17 02:20:36 +04:00
}
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3d4 , 0x32 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR32 , par ) ;
2005-04-17 02:20:36 +04:00
/* unlock extended seq regs */
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3c4 , 0x08 , par ) ;
vga_out8 ( 0x3c5 , 0x06 , par ) ;
2005-04-17 02:20:36 +04:00
/* Restore extended sequencer regs for MCLK. SR10 == 255 indicates
* that we should leave the default SR10 and SR11 values there .
*/
2006-06-26 11:26:23 +04:00
if ( reg - > SR10 ! = 255 ) {
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3c4 , 0x10 , par ) ;
vga_out8 ( 0x3c5 , reg - > SR10 , par ) ;
vga_out8 ( 0x3c4 , 0x11 , par ) ;
vga_out8 ( 0x3c5 , reg - > SR11 , par ) ;
2005-04-17 02:20:36 +04:00
}
/* restore extended seq regs for dclk */
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3c4 , 0x0e , par ) ;
vga_out8 ( 0x3c5 , reg - > SR0E , par ) ;
vga_out8 ( 0x3c4 , 0x0f , par ) ;
vga_out8 ( 0x3c5 , reg - > SR0F , par ) ;
vga_out8 ( 0x3c4 , 0x12 , par ) ;
vga_out8 ( 0x3c5 , reg - > SR12 , par ) ;
vga_out8 ( 0x3c4 , 0x13 , par ) ;
vga_out8 ( 0x3c5 , reg - > SR13 , par ) ;
vga_out8 ( 0x3c4 , 0x29 , par ) ;
vga_out8 ( 0x3c5 , reg - > SR29 , par ) ;
vga_out8 ( 0x3c4 , 0x18 , par ) ;
vga_out8 ( 0x3c5 , reg - > SR18 , par ) ;
2005-04-17 02:20:36 +04:00
/* load new m, n pll values for dclk & mclk */
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3c4 , 0x15 , par ) ;
tmp = vga_in8 ( 0x3c5 , par ) & ~ 0x21 ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3c5 , tmp | 0x03 , par ) ;
vga_out8 ( 0x3c5 , tmp | 0x23 , par ) ;
vga_out8 ( 0x3c5 , tmp | 0x03 , par ) ;
vga_out8 ( 0x3c5 , reg - > SR15 , par ) ;
udelay ( 100 ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3c4 , 0x30 , par ) ;
vga_out8 ( 0x3c5 , reg - > SR30 , par ) ;
vga_out8 ( 0x3c4 , 0x08 , par ) ;
vga_out8 ( 0x3c5 , reg - > SR08 , par ) ;
2005-04-17 02:20:36 +04:00
/* now write out cr67 in full, possibly starting STREAMS */
2005-11-07 12:00:41 +03:00
VerticalRetraceWait ( par ) ;
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3d4 , 0x67 , par ) ;
vga_out8 ( 0x3d5 , reg - > CR67 , par ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3d4 , 0x66 , par ) ;
cr66 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d5 , cr66 | 0x80 , par ) ;
vga_out8 ( 0x3d4 , 0x3a , par ) ;
cr3a = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d5 , cr3a | 0x80 , par ) ;
2005-04-17 02:20:36 +04:00
if ( par - > chip ! = S3_SAVAGE_MX ) {
2005-11-07 12:00:41 +03:00
VerticalRetraceWait ( par ) ;
2006-06-26 11:26:36 +04:00
savage_out32 ( FIFO_CONTROL_REG , reg - > MMPR0 , par ) ;
par - > SavageWaitIdle ( par ) ;
savage_out32 ( MIU_CONTROL_REG , reg - > MMPR1 , par ) ;
par - > SavageWaitIdle ( par ) ;
savage_out32 ( STREAMS_TIMEOUT_REG , reg - > MMPR2 , par ) ;
par - > SavageWaitIdle ( par ) ;
savage_out32 ( MISC_TIMEOUT_REG , reg - > MMPR3 , par ) ;
2005-04-17 02:20:36 +04:00
}
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3d4 , 0x66 , par ) ;
vga_out8 ( 0x3d5 , cr66 , par ) ;
vga_out8 ( 0x3d4 , 0x3a , par ) ;
vga_out8 ( 0x3d5 , cr3a , par ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:36 +04:00
SavageSetup2DEngine ( par ) ;
vgaHWProtect ( par , 0 ) ;
2005-04-17 02:20:36 +04:00
}
2006-06-26 11:26:36 +04:00
static void savagefb_update_start ( struct savagefb_par * par ,
struct fb_var_screeninfo * var )
2005-04-17 02:20:36 +04:00
{
int base ;
base = ( ( var - > yoffset * var - > xres_virtual + ( var - > xoffset & ~ 1 ) )
* ( ( var - > bits_per_pixel + 7 ) / 8 ) ) > > 2 ;
/* now program the start address registers */
2005-11-07 12:00:41 +03:00
vga_out16 ( 0x3d4 , ( base & 0x00ff00 ) | 0x0c , par ) ;
vga_out16 ( 0x3d4 , ( ( base & 0x00ff ) < < 8 ) | 0x0d , par ) ;
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3d4 , 0x69 , par ) ;
vga_out8 ( 0x3d5 , ( base & 0x7f0000 ) > > 16 , par ) ;
2005-04-17 02:20:36 +04:00
}
static void savagefb_set_fix ( struct fb_info * info )
{
info - > fix . line_length = info - > var . xres_virtual *
info - > var . bits_per_pixel / 8 ;
2005-11-09 08:39:05 +03:00
if ( info - > var . bits_per_pixel = = 8 ) {
2005-04-17 02:20:36 +04:00
info - > fix . visual = FB_VISUAL_PSEUDOCOLOR ;
2005-11-09 08:39:05 +03:00
info - > fix . xpanstep = 4 ;
} else {
2005-04-17 02:20:36 +04:00
info - > fix . visual = FB_VISUAL_TRUECOLOR ;
2005-11-09 08:39:05 +03:00
info - > fix . xpanstep = 2 ;
}
2005-04-17 02:20:36 +04:00
}
2006-06-26 11:26:36 +04:00
static int savagefb_set_par ( struct fb_info * info )
2005-04-17 02:20:36 +04:00
{
2006-01-10 07:53:02 +03:00
struct savagefb_par * par = info - > par ;
2005-04-17 02:20:36 +04:00
struct fb_var_screeninfo * var = & info - > var ;
int err ;
DBG ( " savagefb_set_par " ) ;
2006-06-26 11:26:36 +04:00
err = savagefb_decode_var ( var , par , & par - > state ) ;
2005-04-17 02:20:36 +04:00
if ( err )
return err ;
if ( par - > dacSpeedBpp < = 0 ) {
if ( var - > bits_per_pixel > 24 )
par - > dacSpeedBpp = par - > clock [ 3 ] ;
else if ( var - > bits_per_pixel > = 24 )
par - > dacSpeedBpp = par - > clock [ 2 ] ;
else if ( ( var - > bits_per_pixel > 8 ) & & ( var - > bits_per_pixel < 24 ) )
par - > dacSpeedBpp = par - > clock [ 1 ] ;
else if ( var - > bits_per_pixel < = 8 )
par - > dacSpeedBpp = par - > clock [ 0 ] ;
}
/* Set ramdac limits */
par - > maxClock = par - > dacSpeedBpp ;
par - > minClock = 10000 ;
2006-06-26 11:26:36 +04:00
savagefb_set_par_int ( par , & par - > state ) ;
fb_set_cmap ( & info - > cmap , info ) ;
2005-04-17 02:20:36 +04:00
savagefb_set_fix ( info ) ;
savagefb_set_clip ( info ) ;
2007-03-17 00:38:18 +03:00
SavagePrintRegs ( par ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/*
* Pan or Wrap the Display
*/
2006-06-26 11:26:36 +04:00
static int savagefb_pan_display ( struct fb_var_screeninfo * var ,
struct fb_info * info )
2005-04-17 02:20:36 +04:00
{
2006-01-10 07:53:02 +03:00
struct savagefb_par * par = info - > par ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:36 +04:00
savagefb_update_start ( par , var ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-09-10 00:04:35 +04:00
static int savagefb_blank ( int blank , struct fb_info * info )
{
struct savagefb_par * par = info - > par ;
u8 sr8 = 0 , srd = 0 ;
if ( par - > display_type = = DISP_CRT ) {
2005-11-07 12:00:41 +03:00
vga_out8 ( 0x3c4 , 0x08 , par ) ;
sr8 = vga_in8 ( 0x3c5 , par ) ;
2005-09-10 00:04:35 +04:00
sr8 | = 0x06 ;
2005-11-07 12:00:41 +03:00
vga_out8 ( 0x3c5 , sr8 , par ) ;
vga_out8 ( 0x3c4 , 0x0d , par ) ;
srd = vga_in8 ( 0x3c5 , par ) ;
2005-09-10 00:04:35 +04:00
srd & = 0x03 ;
switch ( blank ) {
case FB_BLANK_UNBLANK :
case FB_BLANK_NORMAL :
break ;
case FB_BLANK_VSYNC_SUSPEND :
srd | = 0x10 ;
break ;
case FB_BLANK_HSYNC_SUSPEND :
srd | = 0x40 ;
break ;
case FB_BLANK_POWERDOWN :
srd | = 0x50 ;
break ;
}
2005-11-07 12:00:41 +03:00
vga_out8 ( 0x3c4 , 0x0d , par ) ;
vga_out8 ( 0x3c5 , srd , par ) ;
2005-09-10 00:04:35 +04:00
}
if ( par - > display_type = = DISP_LCD | |
par - > display_type = = DISP_DFP ) {
switch ( blank ) {
case FB_BLANK_UNBLANK :
case FB_BLANK_NORMAL :
2005-11-07 12:00:41 +03:00
vga_out8 ( 0x3c4 , 0x31 , par ) ; /* SR31 bit 4 - FP enable */
vga_out8 ( 0x3c5 , vga_in8 ( 0x3c5 , par ) | 0x10 , par ) ;
2005-09-10 00:04:35 +04:00
break ;
case FB_BLANK_VSYNC_SUSPEND :
case FB_BLANK_HSYNC_SUSPEND :
case FB_BLANK_POWERDOWN :
2005-11-07 12:00:41 +03:00
vga_out8 ( 0x3c4 , 0x31 , par ) ; /* SR31 bit 4 - FP enable */
vga_out8 ( 0x3c5 , vga_in8 ( 0x3c5 , par ) & ~ 0x10 , par ) ;
2005-09-10 00:04:35 +04:00
break ;
}
}
return ( blank = = FB_BLANK_NORMAL ) ? 1 : 0 ;
}
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:24 +04:00
static void savagefb_save_state ( struct fb_info * info )
{
struct savagefb_par * par = info - > par ;
savage_get_default_par ( par , & par - > save ) ;
}
static void savagefb_restore_state ( struct fb_info * info )
{
struct savagefb_par * par = info - > par ;
savagefb_blank ( FB_BLANK_POWERDOWN , info ) ;
savage_set_default_par ( par , & par - > save ) ;
savagefb_blank ( FB_BLANK_UNBLANK , info ) ;
}
2007-05-08 11:38:36 +04:00
static int savagefb_open ( struct fb_info * info , int user )
{
struct savagefb_par * par = info - > par ;
mutex_lock ( & par - > open_lock ) ;
if ( ! par - > open_count ) {
memset ( & par - > vgastate , 0 , sizeof ( par - > vgastate ) ) ;
par - > vgastate . flags = VGA_SAVE_CMAP | VGA_SAVE_FONTS |
VGA_SAVE_MODE ;
par - > vgastate . vgabase = par - > mmio . vbase + 0x8000 ;
save_vga ( & par - > vgastate ) ;
savage_get_default_par ( par , & par - > initial ) ;
}
par - > open_count + + ;
mutex_unlock ( & par - > open_lock ) ;
return 0 ;
}
static int savagefb_release ( struct fb_info * info , int user )
{
struct savagefb_par * par = info - > par ;
mutex_lock ( & par - > open_lock ) ;
if ( par - > open_count = = 1 ) {
savage_set_default_par ( par , & par - > initial ) ;
restore_vga ( & par - > vgastate ) ;
}
par - > open_count - - ;
mutex_unlock ( & par - > open_lock ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
static struct fb_ops savagefb_ops = {
. owner = THIS_MODULE ,
2007-05-08 11:38:36 +04:00
. fb_open = savagefb_open ,
. fb_release = savagefb_release ,
2005-04-17 02:20:36 +04:00
. fb_check_var = savagefb_check_var ,
. fb_set_par = savagefb_set_par ,
. fb_setcolreg = savagefb_setcolreg ,
. fb_pan_display = savagefb_pan_display ,
2005-09-10 00:04:35 +04:00
. fb_blank = savagefb_blank ,
2006-06-26 11:26:24 +04:00
. fb_save_state = savagefb_save_state ,
. fb_restore_state = savagefb_restore_state ,
2005-04-17 02:20:36 +04:00
# if defined(CONFIG_FB_SAVAGE_ACCEL)
. fb_fillrect = savagefb_fillrect ,
. fb_copyarea = savagefb_copyarea ,
. fb_imageblit = savagefb_imageblit ,
. fb_sync = savagefb_sync ,
# else
. fb_fillrect = cfb_fillrect ,
. fb_copyarea = cfb_copyarea ,
. fb_imageblit = cfb_imageblit ,
# endif
} ;
/* --------------------------------------------------------------------- */
static struct fb_var_screeninfo __devinitdata savagefb_var800x600x8 = {
. accel_flags = FB_ACCELF_TEXT ,
. xres = 800 ,
. yres = 600 ,
. xres_virtual = 800 ,
. yres_virtual = 600 ,
. bits_per_pixel = 8 ,
. pixclock = 25000 ,
. left_margin = 88 ,
. right_margin = 40 ,
. upper_margin = 23 ,
. lower_margin = 1 ,
. hsync_len = 128 ,
. vsync_len = 4 ,
. sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT ,
. vmode = FB_VMODE_NONINTERLACED
} ;
2006-06-26 11:26:36 +04:00
static void savage_enable_mmio ( struct savagefb_par * par )
2005-04-17 02:20:36 +04:00
{
unsigned char val ;
2006-06-26 11:26:36 +04:00
DBG ( " savage_enable_mmio \n " ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:36 +04:00
val = vga_in8 ( 0x3c3 , par ) ;
vga_out8 ( 0x3c3 , val | 0x01 , par ) ;
val = vga_in8 ( 0x3cc , par ) ;
vga_out8 ( 0x3c2 , val | 0x01 , par ) ;
2005-04-17 02:20:36 +04:00
if ( par - > chip > = S3_SAVAGE4 ) {
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3d4 , 0x40 , par ) ;
val = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d5 , val | 1 , par ) ;
2005-04-17 02:20:36 +04:00
}
}
2006-06-26 11:26:36 +04:00
static void savage_disable_mmio ( struct savagefb_par * par )
2005-04-17 02:20:36 +04:00
{
unsigned char val ;
2006-06-26 11:26:36 +04:00
DBG ( " savage_disable_mmio \n " ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:36 +04:00
if ( par - > chip > = S3_SAVAGE4 ) {
vga_out8 ( 0x3d4 , 0x40 , par ) ;
val = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d5 , val | 1 , par ) ;
2005-04-17 02:20:36 +04:00
}
}
2006-06-26 11:26:36 +04:00
static int __devinit savage_map_mmio ( struct fb_info * info )
2005-04-17 02:20:36 +04:00
{
2006-01-10 07:53:02 +03:00
struct savagefb_par * par = info - > par ;
2006-06-26 11:26:36 +04:00
DBG ( " savage_map_mmio " ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:36 +04:00
if ( S3_SAVAGE3D_SERIES ( par - > chip ) )
par - > mmio . pbase = pci_resource_start ( par - > pcidev , 0 ) +
2005-04-17 02:20:36 +04:00
SAVAGE_NEWMMIO_REGBASE_S3 ;
else
2006-06-26 11:26:36 +04:00
par - > mmio . pbase = pci_resource_start ( par - > pcidev , 0 ) +
2005-04-17 02:20:36 +04:00
SAVAGE_NEWMMIO_REGBASE_S4 ;
par - > mmio . len = SAVAGE_NEWMMIO_REGSIZE ;
2006-06-26 11:26:36 +04:00
par - > mmio . vbase = ioremap ( par - > mmio . pbase , par - > mmio . len ) ;
2005-04-17 02:20:36 +04:00
if ( ! par - > mmio . vbase ) {
2006-06-26 11:26:36 +04:00
printk ( " savagefb: unable to map memory mapped IO \n " ) ;
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
} else
2006-06-26 11:26:36 +04:00
printk ( KERN_INFO " savagefb: mapped io at %p \n " ,
2005-04-17 02:20:36 +04:00
par - > mmio . vbase ) ;
info - > fix . mmio_start = par - > mmio . pbase ;
info - > fix . mmio_len = par - > mmio . len ;
2005-04-26 18:43:41 +04:00
par - > bci_base = ( u32 __iomem * ) ( par - > mmio . vbase + BCI_BUFFER_OFFSET ) ;
2005-04-17 02:20:36 +04:00
par - > bci_ptr = 0 ;
2006-06-26 11:26:36 +04:00
savage_enable_mmio ( par ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2006-06-26 11:26:36 +04:00
static void savage_unmap_mmio ( struct fb_info * info )
2005-04-17 02:20:36 +04:00
{
2006-01-10 07:53:02 +03:00
struct savagefb_par * par = info - > par ;
2006-06-26 11:26:36 +04:00
DBG ( " savage_unmap_mmio " ) ;
2005-04-17 02:20:36 +04:00
savage_disable_mmio ( par ) ;
if ( par - > mmio . vbase ) {
2005-04-26 18:43:41 +04:00
iounmap ( par - > mmio . vbase ) ;
2005-04-17 02:20:36 +04:00
par - > mmio . vbase = NULL ;
}
}
2006-06-26 11:26:36 +04:00
static int __devinit savage_map_video ( struct fb_info * info ,
int video_len )
2005-04-17 02:20:36 +04:00
{
2006-01-10 07:53:02 +03:00
struct savagefb_par * par = info - > par ;
2005-04-17 02:20:36 +04:00
int resource ;
DBG ( " savage_map_video " ) ;
2006-06-26 11:26:36 +04:00
if ( S3_SAVAGE3D_SERIES ( par - > chip ) )
2005-04-17 02:20:36 +04:00
resource = 0 ;
else
resource = 1 ;
2006-06-26 11:26:36 +04:00
par - > video . pbase = pci_resource_start ( par - > pcidev , resource ) ;
2005-04-17 02:20:36 +04:00
par - > video . len = video_len ;
2006-06-26 11:26:36 +04:00
par - > video . vbase = ioremap ( par - > video . pbase , par - > video . len ) ;
2005-04-17 02:20:36 +04:00
if ( ! par - > video . vbase ) {
2006-06-26 11:26:36 +04:00
printk ( " savagefb: unable to map screen memory \n " ) ;
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
} else
2006-06-26 11:26:36 +04:00
printk ( KERN_INFO " savagefb: mapped framebuffer at %p, "
" pbase == %x \n " , par - > video . vbase , par - > video . pbase ) ;
2005-04-17 02:20:36 +04:00
info - > fix . smem_start = par - > video . pbase ;
info - > fix . smem_len = par - > video . len - par - > cob_size ;
info - > screen_base = par - > video . vbase ;
# ifdef CONFIG_MTRR
2006-06-26 11:26:36 +04:00
par - > video . mtrr = mtrr_add ( par - > video . pbase , video_len ,
MTRR_TYPE_WRCOMB , 1 ) ;
2005-04-17 02:20:36 +04:00
# endif
/* Clear framebuffer, it's all white in memory after boot */
2006-06-26 11:26:36 +04:00
memset_io ( par - > video . vbase , 0 , par - > video . len ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2006-06-26 11:26:36 +04:00
static void savage_unmap_video ( struct fb_info * info )
2005-04-17 02:20:36 +04:00
{
2006-01-10 07:53:02 +03:00
struct savagefb_par * par = info - > par ;
2005-04-17 02:20:36 +04:00
DBG ( " savage_unmap_video " ) ;
if ( par - > video . vbase ) {
# ifdef CONFIG_MTRR
2006-06-26 11:26:36 +04:00
mtrr_del ( par - > video . mtrr , par - > video . pbase , par - > video . len ) ;
2005-04-17 02:20:36 +04:00
# endif
2006-06-26 11:26:36 +04:00
iounmap ( par - > video . vbase ) ;
2005-04-17 02:20:36 +04:00
par - > video . vbase = NULL ;
info - > screen_base = NULL ;
}
}
2006-06-26 11:26:36 +04:00
static int savage_init_hw ( struct savagefb_par * par )
2005-04-17 02:20:36 +04:00
{
unsigned char config1 , m , n , n1 , n2 , sr8 , cr3f , cr66 = 0 , tmp ;
static unsigned char RamSavage3D [ ] = { 8 , 4 , 4 , 2 } ;
static unsigned char RamSavage4 [ ] = { 2 , 4 , 8 , 12 , 16 , 32 , 64 , 32 } ;
static unsigned char RamSavageMX [ ] = { 2 , 8 , 4 , 16 , 8 , 16 , 4 , 16 } ;
static unsigned char RamSavageNB [ ] = { 0 , 2 , 4 , 8 , 16 , 32 , 2 , 2 } ;
2005-09-10 00:04:35 +04:00
int videoRam , videoRambytes , dvi ;
2005-04-17 02:20:36 +04:00
DBG ( " savage_init_hw " ) ;
/* unprotect CRTC[0-7] */
2005-11-07 12:00:41 +03:00
vga_out8 ( 0x3d4 , 0x11 , par ) ;
tmp = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d5 , tmp & 0x7f , par ) ;
2005-04-17 02:20:36 +04:00
/* unlock extended regs */
2005-11-07 12:00:41 +03:00
vga_out16 ( 0x3d4 , 0x4838 , par ) ;
vga_out16 ( 0x3d4 , 0xa039 , par ) ;
vga_out16 ( 0x3c4 , 0x0608 , par ) ;
2005-04-17 02:20:36 +04:00
2005-11-07 12:00:41 +03:00
vga_out8 ( 0x3d4 , 0x40 , par ) ;
tmp = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d5 , tmp & ~ 0x01 , par ) ;
2005-04-17 02:20:36 +04:00
/* unlock sys regs */
2005-11-07 12:00:41 +03:00
vga_out8 ( 0x3d4 , 0x38 , par ) ;
vga_out8 ( 0x3d5 , 0x48 , par ) ;
2005-04-17 02:20:36 +04:00
/* Unlock system registers. */
2005-11-07 12:00:41 +03:00
vga_out16 ( 0x3d4 , 0x4838 , par ) ;
2005-04-17 02:20:36 +04:00
/* Next go on to detect amount of installed ram */
2005-11-07 12:00:41 +03:00
vga_out8 ( 0x3d4 , 0x36 , par ) ; /* for register CR36 (CONFG_REG1), */
config1 = vga_in8 ( 0x3d5 , par ) ; /* get amount of vram installed */
2005-04-17 02:20:36 +04:00
/* Compute the amount of video memory and offscreen memory. */
switch ( par - > chip ) {
case S3_SAVAGE3D :
2006-06-26 11:26:36 +04:00
videoRam = RamSavage3D [ ( config1 & 0xC0 ) > > 6 ] * 1024 ;
2005-04-17 02:20:36 +04:00
break ;
case S3_SAVAGE4 :
/*
* The Savage4 has one ugly special case to consider . On
* systems with 4 banks of 2 Mx32 SDRAM , the BIOS says 4 MB
* when it really means 8 MB . Why do it the same when you
* can do it different . . .
*/
2005-11-07 12:00:41 +03:00
vga_out8 ( 0x3d4 , 0x68 , par ) ; /* memory control 1 */
2006-06-26 11:26:36 +04:00
if ( ( vga_in8 ( 0x3d5 , par ) & 0xC0 ) = = ( 0x01 < < 6 ) )
2005-04-17 02:20:36 +04:00
RamSavage4 [ 1 ] = 8 ;
/*FALLTHROUGH*/
case S3_SAVAGE2000 :
2006-06-26 11:26:36 +04:00
videoRam = RamSavage4 [ ( config1 & 0xE0 ) > > 5 ] * 1024 ;
2005-04-17 02:20:36 +04:00
break ;
case S3_SAVAGE_MX :
case S3_SUPERSAVAGE :
2006-06-26 11:26:36 +04:00
videoRam = RamSavageMX [ ( config1 & 0x0E ) > > 1 ] * 1024 ;
2005-04-17 02:20:36 +04:00
break ;
case S3_PROSAVAGE :
2006-06-26 11:26:36 +04:00
videoRam = RamSavageNB [ ( config1 & 0xE0 ) > > 5 ] * 1024 ;
2005-04-17 02:20:36 +04:00
break ;
default :
/* How did we get here? */
videoRam = 0 ;
break ;
}
videoRambytes = videoRam * 1024 ;
2006-06-26 11:26:36 +04:00
printk ( KERN_INFO " savagefb: probed videoram: %dk \n " , videoRam ) ;
2005-04-17 02:20:36 +04:00
/* reset graphics engine to avoid memory corruption */
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3d4 , 0x66 , par ) ;
cr66 = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d5 , cr66 | 0x02 , par ) ;
udelay ( 10000 ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3d4 , 0x66 , par ) ;
vga_out8 ( 0x3d5 , cr66 & ~ 0x02 , par ) ; /* clear reset flag */
udelay ( 10000 ) ;
2005-04-17 02:20:36 +04:00
/*
* reset memory interface , 3 D engine , AGP master , PCI master ,
* master engine unit , motion compensation / LPB
*/
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3d4 , 0x3f , par ) ;
cr3f = vga_in8 ( 0x3d5 , par ) ;
vga_out8 ( 0x3d5 , cr3f | 0x08 , par ) ;
udelay ( 10000 ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:36 +04:00
vga_out8 ( 0x3d4 , 0x3f , par ) ;
vga_out8 ( 0x3d5 , cr3f & ~ 0x08 , par ) ; /* clear reset flags */
udelay ( 10000 ) ;
2005-04-17 02:20:36 +04:00
/* Savage ramdac speeds */
par - > numClocks = 4 ;
par - > clock [ 0 ] = 250000 ;
par - > clock [ 1 ] = 250000 ;
par - > clock [ 2 ] = 220000 ;
par - > clock [ 3 ] = 220000 ;
/* detect current mclk */
2005-11-07 12:00:41 +03:00
vga_out8 ( 0x3c4 , 0x08 , par ) ;
sr8 = vga_in8 ( 0x3c5 , par ) ;
vga_out8 ( 0x3c5 , 0x06 , par ) ;
vga_out8 ( 0x3c4 , 0x10 , par ) ;
n = vga_in8 ( 0x3c5 , par ) ;
vga_out8 ( 0x3c4 , 0x11 , par ) ;
m = vga_in8 ( 0x3c5 , par ) ;
vga_out8 ( 0x3c4 , 0x08 , par ) ;
vga_out8 ( 0x3c5 , sr8 , par ) ;
2005-04-17 02:20:36 +04:00
m & = 0x7f ;
n1 = n & 0x1f ;
n2 = ( n > > 5 ) & 0x03 ;
par - > MCLK = ( ( 1431818 * ( m + 2 ) ) / ( n1 + 2 ) / ( 1 < < n2 ) + 50 ) / 100 ;
2006-06-26 11:26:36 +04:00
printk ( KERN_INFO " savagefb: Detected current MCLK value of %d kHz \n " ,
2005-04-17 02:20:36 +04:00
par - > MCLK ) ;
2005-09-10 00:04:35 +04:00
/* check for DVI/flat panel */
dvi = 0 ;
if ( par - > chip = = S3_SAVAGE4 ) {
unsigned char sr30 = 0x00 ;
2005-11-07 12:00:41 +03:00
vga_out8 ( 0x3c4 , 0x30 , par ) ;
2005-09-10 00:04:35 +04:00
/* clear bit 1 */
2005-11-07 12:00:41 +03:00
vga_out8 ( 0x3c5 , vga_in8 ( 0x3c5 , par ) & ~ 0x02 , par ) ;
sr30 = vga_in8 ( 0x3c5 , par ) ;
2005-09-10 00:04:35 +04:00
if ( sr30 & 0x02 /*0x04 */ ) {
dvi = 1 ;
printk ( " savagefb: Digital Flat Panel Detected \n " ) ;
}
}
2005-09-15 16:58:57 +04:00
if ( S3_SAVAGE_MOBILE_SERIES ( par - > chip ) & & ! par - > crtonly )
2005-09-10 00:04:35 +04:00
par - > display_type = DISP_LCD ;
else if ( dvi | | ( par - > chip = = S3_SAVAGE4 & & par - > dvi ) )
par - > display_type = DISP_DFP ;
else
par - > display_type = DISP_CRT ;
2005-04-17 02:20:36 +04:00
/* Check LCD panel parrmation */
2005-09-15 16:58:57 +04:00
if ( par - > display_type = = DISP_LCD ) {
2006-06-26 11:26:36 +04:00
unsigned char cr6b = VGArCR ( 0x6b , par ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:36 +04:00
int panelX = ( VGArSEQ ( 0x61 , par ) +
( ( VGArSEQ ( 0x66 , par ) & 0x02 ) < < 7 ) + 1 ) * 8 ;
int panelY = ( VGArSEQ ( 0x69 , par ) +
( ( VGArSEQ ( 0x6e , par ) & 0x70 ) < < 4 ) + 1 ) ;
2005-04-17 02:20:36 +04:00
char * sTechnology = " Unknown " ;
/* OK, I admit it. I don't know how to limit the max dot clock
* for LCD panels of various sizes . I thought I copied the
* formula from the BIOS , but many users have parrmed me of
* my folly .
*
* Instead , I ' ll abandon any attempt to automatically limit the
* clock , and add an LCDClock option to XF86Config . Some day ,
* I should come back to this .
*/
enum ACTIVE_DISPLAYS { /* These are the bits in CR6B */
ActiveCRT = 0x01 ,
ActiveLCD = 0x02 ,
ActiveTV = 0x04 ,
ActiveCRT2 = 0x20 ,
ActiveDUO = 0x80
} ;
2006-06-26 11:26:36 +04:00
if ( ( VGArSEQ ( 0x39 , par ) & 0x03 ) = = 0 ) {
2005-04-17 02:20:36 +04:00
sTechnology = " TFT " ;
2006-06-26 11:26:36 +04:00
} else if ( ( VGArSEQ ( 0x30 , par ) & 0x01 ) = = 0 ) {
2005-04-17 02:20:36 +04:00
sTechnology = " DSTN " ;
} else {
sTechnology = " STN " ;
}
2006-06-26 11:26:36 +04:00
printk ( KERN_INFO " savagefb: %dx%d %s LCD panel detected %s \n " ,
panelX , panelY , sTechnology ,
cr6b & ActiveLCD ? " and active " : " but not active " ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:36 +04:00
if ( cr6b & ActiveLCD ) {
2005-04-17 02:20:36 +04:00
/*
* If the LCD is active and panel expansion is enabled ,
* we probably want to kill the HW cursor .
*/
2006-06-26 11:26:36 +04:00
printk ( KERN_INFO " savagefb: Limiting video mode to "
" %dx%d \n " , panelX , panelY ) ;
2005-04-17 02:20:36 +04:00
par - > SavagePanelWidth = panelX ;
par - > SavagePanelHeight = panelY ;
2005-09-10 00:04:35 +04:00
} else
par - > display_type = DISP_CRT ;
2005-04-17 02:20:36 +04:00
}
2006-06-26 11:26:36 +04:00
savage_get_default_par ( par , & par - > state ) ;
2006-06-26 11:26:23 +04:00
par - > save = par - > state ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:36 +04:00
if ( S3_SAVAGE4_SERIES ( par - > chip ) ) {
2005-04-17 02:20:36 +04:00
/*
* The Savage4 and ProSavage have COB coherency bugs which
* render the buffer useless . We disable it .
*/
par - > cob_index = 2 ;
par - > cob_size = 0x8000 < < par - > cob_index ;
par - > cob_offset = videoRambytes ;
} else {
/* We use 128kB for the COB on all chips. */
par - > cob_index = 7 ;
par - > cob_size = 0x400 < < par - > cob_index ;
par - > cob_offset = videoRambytes - par - > cob_size ;
}
return videoRambytes ;
}
2006-06-26 11:26:36 +04:00
static int __devinit savage_init_fb_info ( struct fb_info * info ,
struct pci_dev * dev ,
const struct pci_device_id * id )
2005-04-17 02:20:36 +04:00
{
2006-01-10 07:53:02 +03:00
struct savagefb_par * par = info - > par ;
2005-04-17 02:20:36 +04:00
int err = 0 ;
par - > pcidev = dev ;
info - > fix . type = FB_TYPE_PACKED_PIXELS ;
info - > fix . type_aux = 0 ;
info - > fix . ypanstep = 1 ;
info - > fix . ywrapstep = 0 ;
info - > fix . accel = id - > driver_data ;
switch ( info - > fix . accel ) {
case FB_ACCEL_SUPERSAVAGE :
par - > chip = S3_SUPERSAVAGE ;
2006-06-26 11:26:36 +04:00
snprintf ( info - > fix . id , 16 , " SuperSavage " ) ;
2005-04-17 02:20:36 +04:00
break ;
case FB_ACCEL_SAVAGE4 :
par - > chip = S3_SAVAGE4 ;
2006-06-26 11:26:36 +04:00
snprintf ( info - > fix . id , 16 , " Savage4 " ) ;
2005-04-17 02:20:36 +04:00
break ;
case FB_ACCEL_SAVAGE3D :
par - > chip = S3_SAVAGE3D ;
2006-06-26 11:26:36 +04:00
snprintf ( info - > fix . id , 16 , " Savage3D " ) ;
2005-04-17 02:20:36 +04:00
break ;
case FB_ACCEL_SAVAGE3D_MV :
par - > chip = S3_SAVAGE3D ;
2006-06-26 11:26:36 +04:00
snprintf ( info - > fix . id , 16 , " Savage3D-MV " ) ;
2005-04-17 02:20:36 +04:00
break ;
case FB_ACCEL_SAVAGE2000 :
par - > chip = S3_SAVAGE2000 ;
2006-06-26 11:26:36 +04:00
snprintf ( info - > fix . id , 16 , " Savage2000 " ) ;
2005-04-17 02:20:36 +04:00
break ;
case FB_ACCEL_SAVAGE_MX_MV :
par - > chip = S3_SAVAGE_MX ;
2006-06-26 11:26:36 +04:00
snprintf ( info - > fix . id , 16 , " Savage/MX-MV " ) ;
2005-04-17 02:20:36 +04:00
break ;
case FB_ACCEL_SAVAGE_MX :
par - > chip = S3_SAVAGE_MX ;
2006-06-26 11:26:36 +04:00
snprintf ( info - > fix . id , 16 , " Savage/MX " ) ;
2005-04-17 02:20:36 +04:00
break ;
case FB_ACCEL_SAVAGE_IX_MV :
par - > chip = S3_SAVAGE_MX ;
2006-06-26 11:26:36 +04:00
snprintf ( info - > fix . id , 16 , " Savage/IX-MV " ) ;
2005-04-17 02:20:36 +04:00
break ;
case FB_ACCEL_SAVAGE_IX :
par - > chip = S3_SAVAGE_MX ;
2006-06-26 11:26:36 +04:00
snprintf ( info - > fix . id , 16 , " Savage/IX " ) ;
2005-04-17 02:20:36 +04:00
break ;
case FB_ACCEL_PROSAVAGE_PM :
par - > chip = S3_PROSAVAGE ;
2006-06-26 11:26:36 +04:00
snprintf ( info - > fix . id , 16 , " ProSavagePM " ) ;
2005-04-17 02:20:36 +04:00
break ;
case FB_ACCEL_PROSAVAGE_KM :
par - > chip = S3_PROSAVAGE ;
2006-06-26 11:26:36 +04:00
snprintf ( info - > fix . id , 16 , " ProSavageKM " ) ;
2005-04-17 02:20:36 +04:00
break ;
case FB_ACCEL_S3TWISTER_P :
2005-09-15 16:58:57 +04:00
par - > chip = S3_PROSAVAGE ;
2006-06-26 11:26:36 +04:00
snprintf ( info - > fix . id , 16 , " TwisterP " ) ;
2005-04-17 02:20:36 +04:00
break ;
case FB_ACCEL_S3TWISTER_K :
2005-09-15 16:58:57 +04:00
par - > chip = S3_PROSAVAGE ;
2006-06-26 11:26:36 +04:00
snprintf ( info - > fix . id , 16 , " TwisterK " ) ;
2005-04-17 02:20:36 +04:00
break ;
case FB_ACCEL_PROSAVAGE_DDR :
2005-09-15 16:58:57 +04:00
par - > chip = S3_PROSAVAGE ;
2006-06-26 11:26:36 +04:00
snprintf ( info - > fix . id , 16 , " ProSavageDDR " ) ;
2005-04-17 02:20:36 +04:00
break ;
case FB_ACCEL_PROSAVAGE_DDRK :
par - > chip = S3_PROSAVAGE ;
2006-06-26 11:26:36 +04:00
snprintf ( info - > fix . id , 16 , " ProSavage8 " ) ;
2005-04-17 02:20:36 +04:00
break ;
}
if ( S3_SAVAGE3D_SERIES ( par - > chip ) ) {
par - > SavageWaitIdle = savage3D_waitidle ;
par - > SavageWaitFifo = savage3D_waitfifo ;
} else if ( S3_SAVAGE4_SERIES ( par - > chip ) | |
S3_SUPERSAVAGE = = par - > chip ) {
par - > SavageWaitIdle = savage4_waitidle ;
par - > SavageWaitFifo = savage4_waitfifo ;
} else {
par - > SavageWaitIdle = savage2000_waitidle ;
par - > SavageWaitFifo = savage2000_waitfifo ;
}
info - > var . nonstd = 0 ;
info - > var . activate = FB_ACTIVATE_NOW ;
info - > var . width = - 1 ;
info - > var . height = - 1 ;
info - > var . accel_flags = 0 ;
info - > fbops = & savagefb_ops ;
info - > flags = FBINFO_DEFAULT |
FBINFO_HWACCEL_YPAN |
FBINFO_HWACCEL_XPAN ;
info - > pseudo_palette = par - > pseudo_palette ;
# if defined(CONFIG_FB_SAVAGE_ACCEL)
/* FIFO size + padding for commands */
some kmalloc/memset ->kzalloc (tree wide)
Transform some calls to kmalloc/memset to a single kzalloc (or kcalloc).
Here is a short excerpt of the semantic patch performing
this transformation:
@@
type T2;
expression x;
identifier f,fld;
expression E;
expression E1,E2;
expression e1,e2,e3,y;
statement S;
@@
x =
- kmalloc
+ kzalloc
(E1,E2)
... when != \(x->fld=E;\|y=f(...,x,...);\|f(...,x,...);\|x=E;\|while(...) S\|for(e1;e2;e3) S\)
- memset((T2)x,0,E1);
@@
expression E1,E2,E3;
@@
- kzalloc(E1 * E2,E3)
+ kcalloc(E1,E2,E3)
[akpm@linux-foundation.org: get kcalloc args the right way around]
Signed-off-by: Yoann Padioleau <padator@wanadoo.fr>
Cc: Richard Henderson <rth@twiddle.net>
Cc: Ivan Kokshaysky <ink@jurassic.park.msu.ru>
Acked-by: Russell King <rmk@arm.linux.org.uk>
Cc: Bryan Wu <bryan.wu@analog.com>
Acked-by: Jiri Slaby <jirislaby@gmail.com>
Cc: Dave Airlie <airlied@linux.ie>
Acked-by: Roland Dreier <rolandd@cisco.com>
Cc: Jiri Kosina <jkosina@suse.cz>
Acked-by: Dmitry Torokhov <dtor@mail.ru>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Acked-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Acked-by: Pierre Ossman <drzeus-list@drzeus.cx>
Cc: Jeff Garzik <jeff@garzik.org>
Cc: "David S. Miller" <davem@davemloft.net>
Acked-by: Greg KH <greg@kroah.com>
Cc: James Bottomley <James.Bottomley@steeleye.com>
Cc: "Antonino A. Daplas" <adaplas@pol.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-07-19 12:49:03 +04:00
info - > pixmap . addr = kcalloc ( 8 , 1024 , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
err = - ENOMEM ;
if ( info - > pixmap . addr ) {
info - > pixmap . size = 8 * 1024 ;
info - > pixmap . scan_align = 4 ;
info - > pixmap . buf_align = 4 ;
2005-06-22 04:17:08 +04:00
info - > pixmap . access_align = 32 ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:36 +04:00
err = fb_alloc_cmap ( & info - > cmap , NR_PALETTE , 0 ) ;
2005-09-10 00:04:55 +04:00
if ( ! err )
2005-04-17 02:20:36 +04:00
info - > flags | = FBINFO_HWACCEL_COPYAREA |
FBINFO_HWACCEL_FILLRECT |
FBINFO_HWACCEL_IMAGEBLIT ;
}
# endif
return err ;
}
/* --------------------------------------------------------------------- */
2006-06-26 11:26:36 +04:00
static int __devinit savagefb_probe ( struct pci_dev * dev ,
const struct pci_device_id * id )
2005-04-17 02:20:36 +04:00
{
struct fb_info * info ;
struct savagefb_par * par ;
u_int h_sync , v_sync ;
int err , lpitch ;
int video_len ;
DBG ( " savagefb_probe " ) ;
info = framebuffer_alloc ( sizeof ( struct savagefb_par ) , & dev - > dev ) ;
if ( ! info )
return - ENOMEM ;
par = info - > par ;
2007-05-08 11:38:36 +04:00
mutex_init ( & par - > open_lock ) ;
2005-04-17 02:20:36 +04:00
err = pci_enable_device ( dev ) ;
if ( err )
goto failed_enable ;
2005-09-10 00:04:55 +04:00
if ( ( err = pci_request_regions ( dev , " savagefb " ) ) ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_ERR " cannot request PCI regions \n " ) ;
goto failed_enable ;
}
err = - ENOMEM ;
2005-09-10 00:04:55 +04:00
if ( ( err = savage_init_fb_info ( info , dev , id ) ) )
2005-04-17 02:20:36 +04:00
goto failed_init ;
err = savage_map_mmio ( info ) ;
if ( err )
goto failed_mmio ;
video_len = savage_init_hw ( par ) ;
2005-09-10 00:04:55 +04:00
/* FIXME: cant be negative */
2005-04-17 02:20:36 +04:00
if ( video_len < 0 ) {
err = video_len ;
goto failed_mmio ;
}
err = savage_map_video ( info , video_len ) ;
if ( err )
goto failed_video ;
INIT_LIST_HEAD ( & info - > modelist ) ;
# if defined(CONFIG_FB_SAVAGE_I2C)
savagefb_create_i2c_busses ( info ) ;
2005-09-10 00:04:35 +04:00
savagefb_probe_i2c_connector ( info , & par - > edid ) ;
2005-04-17 02:20:36 +04:00
fb_edid_to_monspecs ( par - > edid , & info - > monspecs ) ;
2006-03-11 14:27:25 +03:00
kfree ( par - > edid ) ;
2005-04-17 02:20:36 +04:00
fb_videomode_to_modelist ( info - > monspecs . modedb ,
info - > monspecs . modedb_len ,
& info - > modelist ) ;
# endif
info - > var = savagefb_var800x600x8 ;
if ( mode_option ) {
fb_find_mode ( & info - > var , info , mode_option ,
info - > monspecs . modedb , info - > monspecs . modedb_len ,
NULL , 8 ) ;
} else if ( info - > monspecs . modedb ! = NULL ) {
2007-02-12 11:55:19 +03:00
const struct fb_videomode * mode ;
2005-04-17 02:20:36 +04:00
2007-02-12 11:55:19 +03:00
mode = fb_find_best_display ( & info - > monspecs , & info - > modelist ) ;
savage_update_var ( & info - > var , mode ) ;
2005-04-17 02:20:36 +04:00
}
/* maximize virtual vertical length */
lpitch = info - > var . xres_virtual * ( ( info - > var . bits_per_pixel + 7 ) > > 3 ) ;
info - > var . yres_virtual = info - > fix . smem_len / lpitch ;
if ( info - > var . yres_virtual < info - > var . yres )
goto failed ;
# if defined(CONFIG_FB_SAVAGE_ACCEL)
/*
* The clipping coordinates are masked with 0xFFF , so limit our
* virtual resolutions to these sizes .
*/
if ( info - > var . yres_virtual > 0x1000 )
info - > var . yres_virtual = 0x1000 ;
if ( info - > var . xres_virtual > 0x1000 )
info - > var . xres_virtual = 0x1000 ;
# endif
savagefb_check_var ( & info - > var , info ) ;
savagefb_set_fix ( info ) ;
/*
* Calculate the hsync and vsync frequencies . Note that
* we split the 1e12 constant up so that we can preserve
* the precision and fit the results into 32 - bit registers .
* ( 1953125000 * 512 = 1e12 )
*/
h_sync = 1953125000 / info - > var . pixclock ;
h_sync = h_sync * 512 / ( info - > var . xres + info - > var . left_margin +
info - > var . right_margin +
info - > var . hsync_len ) ;
v_sync = h_sync / ( info - > var . yres + info - > var . upper_margin +
info - > var . lower_margin + info - > var . vsync_len ) ;
printk ( KERN_INFO " savagefb v " SAVAGEFB_VERSION " : "
" %dkB VRAM, using %dx%d, %d.%03dkHz, %dHz \n " ,
info - > fix . smem_len > > 10 ,
info - > var . xres , info - > var . yres ,
h_sync / 1000 , h_sync % 1000 , v_sync ) ;
fb_destroy_modedb ( info - > monspecs . modedb ) ;
info - > monspecs . modedb = NULL ;
2006-06-26 11:26:36 +04:00
err = register_framebuffer ( info ) ;
2005-04-17 02:20:36 +04:00
if ( err < 0 )
goto failed ;
2006-06-26 11:26:36 +04:00
printk ( KERN_INFO " fb: S3 %s frame buffer device \n " ,
info - > fix . id ) ;
2005-04-17 02:20:36 +04:00
/*
* Our driver data
*/
pci_set_drvdata ( dev , info ) ;
return 0 ;
failed :
# ifdef CONFIG_FB_SAVAGE_I2C
savagefb_delete_i2c_busses ( info ) ;
# endif
2006-06-26 11:26:36 +04:00
fb_alloc_cmap ( & info - > cmap , 0 , 0 ) ;
2005-04-17 02:20:36 +04:00
savage_unmap_video ( info ) ;
failed_video :
2006-06-26 11:26:36 +04:00
savage_unmap_mmio ( info ) ;
2005-04-17 02:20:36 +04:00
failed_mmio :
kfree ( info - > pixmap . addr ) ;
failed_init :
pci_release_regions ( dev ) ;
failed_enable :
framebuffer_release ( info ) ;
return err ;
}
2006-06-26 11:26:36 +04:00
static void __devexit savagefb_remove ( struct pci_dev * dev )
2005-04-17 02:20:36 +04:00
{
2006-01-10 07:53:02 +03:00
struct fb_info * info = pci_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
DBG ( " savagefb_remove " ) ;
if ( info ) {
/*
* If unregister_framebuffer fails , then
* we will be leaving hooks that could cause
* oopsen laying around .
*/
2006-06-26 11:26:36 +04:00
if ( unregister_framebuffer ( info ) )
printk ( KERN_WARNING " savagefb: danger danger! "
" Oopsen imminent! \n " ) ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_FB_SAVAGE_I2C
savagefb_delete_i2c_busses ( info ) ;
# endif
2006-06-26 11:26:36 +04:00
fb_alloc_cmap ( & info - > cmap , 0 , 0 ) ;
savage_unmap_video ( info ) ;
savage_unmap_mmio ( info ) ;
2005-04-17 02:20:36 +04:00
kfree ( info - > pixmap . addr ) ;
pci_release_regions ( dev ) ;
framebuffer_release ( info ) ;
/*
* Ensure that the driver data is no longer
* valid .
*/
pci_set_drvdata ( dev , NULL ) ;
}
}
2006-08-15 10:11:06 +04:00
static int savagefb_suspend ( struct pci_dev * dev , pm_message_t mesg )
2005-04-17 02:20:36 +04:00
{
2006-01-10 07:53:02 +03:00
struct fb_info * info = pci_get_drvdata ( dev ) ;
struct savagefb_par * par = info - > par ;
2005-04-17 02:20:36 +04:00
DBG ( " savagefb_suspend " ) ;
2006-08-15 10:11:06 +04:00
if ( mesg . event = = PM_EVENT_PRETHAW )
mesg . event = PM_EVENT_FREEZE ;
par - > pm_state = mesg . event ;
dev - > dev . power . power_state = mesg ;
2005-09-10 00:04:35 +04:00
/*
* For PM_EVENT_FREEZE , do not power down so the console
* can remain active .
*/
2006-08-15 10:11:06 +04:00
if ( mesg . event = = PM_EVENT_FREEZE )
2005-09-10 00:04:35 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
acquire_console_sem ( ) ;
2005-09-10 00:04:35 +04:00
fb_set_suspend ( info , 1 ) ;
2005-04-17 02:20:36 +04:00
2005-09-10 00:04:35 +04:00
if ( info - > fbops - > fb_sync )
info - > fbops - > fb_sync ( info ) ;
savagefb_blank ( FB_BLANK_POWERDOWN , info ) ;
2006-06-26 11:26:24 +04:00
savage_set_default_par ( par , & par - > save ) ;
2005-09-10 00:04:35 +04:00
savage_disable_mmio ( par ) ;
pci_save_state ( dev ) ;
2005-04-17 02:20:36 +04:00
pci_disable_device ( dev ) ;
2006-08-15 10:11:06 +04:00
pci_set_power_state ( dev , pci_choose_state ( dev , mesg ) ) ;
2005-09-10 00:04:35 +04:00
release_console_sem ( ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2006-06-26 11:26:36 +04:00
static int savagefb_resume ( struct pci_dev * dev )
2005-04-17 02:20:36 +04:00
{
2006-01-10 07:53:02 +03:00
struct fb_info * info = pci_get_drvdata ( dev ) ;
struct savagefb_par * par = info - > par ;
2005-09-10 00:04:35 +04:00
int cur_state = par - > pm_state ;
2005-04-17 02:20:36 +04:00
DBG ( " savage_resume " ) ;
2005-09-10 00:04:35 +04:00
par - > pm_state = PM_EVENT_ON ;
2005-04-17 02:20:36 +04:00
2005-09-10 00:04:35 +04:00
/*
* The adapter was not powered down coming back from a
* PM_EVENT_FREEZE .
*/
if ( cur_state = = PM_EVENT_FREEZE ) {
pci_set_power_state ( dev , PCI_D0 ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
acquire_console_sem ( ) ;
2005-09-10 00:04:35 +04:00
pci_set_power_state ( dev , PCI_D0 ) ;
pci_restore_state ( dev ) ;
2006-06-26 11:26:36 +04:00
if ( pci_enable_device ( dev ) )
2005-09-10 00:04:35 +04:00
DBG ( " err " ) ;
pci_set_master ( dev ) ;
2005-04-17 02:20:36 +04:00
savage_enable_mmio ( par ) ;
savage_init_hw ( par ) ;
2006-06-26 11:26:24 +04:00
savagefb_set_par ( info ) ;
2006-06-26 11:26:36 +04:00
fb_set_suspend ( info , 0 ) ;
2006-06-26 11:26:24 +04:00
savagefb_blank ( FB_BLANK_UNBLANK , info ) ;
2005-04-17 02:20:36 +04:00
release_console_sem ( ) ;
return 0 ;
}
static struct pci_device_id savagefb_devices [ ] __devinitdata = {
{ PCI_VENDOR_ID_S3 , PCI_CHIP_SUPSAV_MX128 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , FB_ACCEL_SUPERSAVAGE } ,
{ PCI_VENDOR_ID_S3 , PCI_CHIP_SUPSAV_MX64 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , FB_ACCEL_SUPERSAVAGE } ,
{ PCI_VENDOR_ID_S3 , PCI_CHIP_SUPSAV_MX64C ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , FB_ACCEL_SUPERSAVAGE } ,
{ PCI_VENDOR_ID_S3 , PCI_CHIP_SUPSAV_IX128SDR ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , FB_ACCEL_SUPERSAVAGE } ,
{ PCI_VENDOR_ID_S3 , PCI_CHIP_SUPSAV_IX128DDR ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , FB_ACCEL_SUPERSAVAGE } ,
{ PCI_VENDOR_ID_S3 , PCI_CHIP_SUPSAV_IX64SDR ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , FB_ACCEL_SUPERSAVAGE } ,
{ PCI_VENDOR_ID_S3 , PCI_CHIP_SUPSAV_IX64DDR ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , FB_ACCEL_SUPERSAVAGE } ,
{ PCI_VENDOR_ID_S3 , PCI_CHIP_SUPSAV_IXCSDR ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , FB_ACCEL_SUPERSAVAGE } ,
{ PCI_VENDOR_ID_S3 , PCI_CHIP_SUPSAV_IXCDDR ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , FB_ACCEL_SUPERSAVAGE } ,
{ PCI_VENDOR_ID_S3 , PCI_CHIP_SAVAGE4 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , FB_ACCEL_SAVAGE4 } ,
{ PCI_VENDOR_ID_S3 , PCI_CHIP_SAVAGE3D ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , FB_ACCEL_SAVAGE3D } ,
{ PCI_VENDOR_ID_S3 , PCI_CHIP_SAVAGE3D_MV ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , FB_ACCEL_SAVAGE3D_MV } ,
{ PCI_VENDOR_ID_S3 , PCI_CHIP_SAVAGE2000 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , FB_ACCEL_SAVAGE2000 } ,
{ PCI_VENDOR_ID_S3 , PCI_CHIP_SAVAGE_MX_MV ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , FB_ACCEL_SAVAGE_MX_MV } ,
{ PCI_VENDOR_ID_S3 , PCI_CHIP_SAVAGE_MX ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , FB_ACCEL_SAVAGE_MX } ,
{ PCI_VENDOR_ID_S3 , PCI_CHIP_SAVAGE_IX_MV ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , FB_ACCEL_SAVAGE_IX_MV } ,
{ PCI_VENDOR_ID_S3 , PCI_CHIP_SAVAGE_IX ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , FB_ACCEL_SAVAGE_IX } ,
{ PCI_VENDOR_ID_S3 , PCI_CHIP_PROSAVAGE_PM ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , FB_ACCEL_PROSAVAGE_PM } ,
{ PCI_VENDOR_ID_S3 , PCI_CHIP_PROSAVAGE_KM ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , FB_ACCEL_PROSAVAGE_KM } ,
{ PCI_VENDOR_ID_S3 , PCI_CHIP_S3TWISTER_P ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , FB_ACCEL_S3TWISTER_P } ,
{ PCI_VENDOR_ID_S3 , PCI_CHIP_S3TWISTER_K ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , FB_ACCEL_S3TWISTER_K } ,
{ PCI_VENDOR_ID_S3 , PCI_CHIP_PROSAVAGE_DDR ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , FB_ACCEL_PROSAVAGE_DDR } ,
{ PCI_VENDOR_ID_S3 , PCI_CHIP_PROSAVAGE_DDRK ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , FB_ACCEL_PROSAVAGE_DDRK } ,
{ 0 , 0 , 0 , 0 , 0 , 0 , 0 }
} ;
MODULE_DEVICE_TABLE ( pci , savagefb_devices ) ;
static struct pci_driver savagefb_driver = {
. name = " savagefb " ,
. id_table = savagefb_devices ,
. probe = savagefb_probe ,
. suspend = savagefb_suspend ,
. resume = savagefb_resume ,
. remove = __devexit_p ( savagefb_remove )
} ;
/* **************************** exit-time only **************************** */
2006-06-26 11:26:36 +04:00
static void __exit savage_done ( void )
2005-04-17 02:20:36 +04:00
{
DBG ( " savage_done " ) ;
2006-06-26 11:26:36 +04:00
pci_unregister_driver ( & savagefb_driver ) ;
2005-04-17 02:20:36 +04:00
}
/* ************************* init in-kernel code ************************** */
static int __init savagefb_setup ( char * options )
{
# ifndef MODULE
char * this_opt ;
if ( ! options | | ! * options )
return 0 ;
while ( ( this_opt = strsep ( & options , " , " ) ) ! = NULL ) {
mode_option = this_opt ;
}
# endif /* !MODULE */
return 0 ;
}
static int __init savagefb_init ( void )
{
char * option ;
DBG ( " savagefb_init " ) ;
if ( fb_get_options ( " savagefb " , & option ) )
return - ENODEV ;
savagefb_setup ( option ) ;
2006-06-26 11:26:36 +04:00
return pci_register_driver ( & savagefb_driver ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( savagefb_init ) ;
module_exit ( savage_done ) ;
2005-09-10 00:09:59 +04:00
module_param ( mode_option , charp , 0 ) ;
MODULE_PARM_DESC ( mode_option , " Specify initial video mode " ) ;