Subversion Repositories shark

Rev

Blame | Last modification | View Log | RSS feed

/*
Savage chipset driver

Written by Matan Ziv-Av (matan@svgalib.org)

Based on XFree 3.3.6 driver by S. Marineau and Tim Roberts.
And XFree 4.1.0 driver by Kevin Brosius.

*/


#include <stdlib.h>
//#include <stdio.h>           
#include <string.h>
#include <unistd.h>
//#include <sys/mman.h>
#include "vga.h"
#include "libvga.h"
#include "driver.h"
#include "timing.h"
#include "vgaregs.h"
#include "interface.h"
#include "vgapci.h"
#include "endianess.h"
#include "vgammvgaio.h"

#define SAVAGEREG_SAVE(i) (VGA_TOTAL_REGS+i)
#define TOTAL_REGS (VGA_TOTAL_REGS + 64)

typedef struct {    

   unsigned char SR08, SR0A, SR0E, SR0F, SR10, SR11, SR12, SR13;
   unsigned char SR15, SR18, SR1B, SR29, SR30;
   unsigned char SR54, SR55, SR56, SR57;
   unsigned char Clock;
//   unsigned char s3DacRegs[0x101];
   unsigned char CR31, CR33, CR34, CR36, CR3A, CR3B, CR3C;
   unsigned char CR40, CR41, CR42, CR43, CR45;
   unsigned char CR50, CR51, CR53, CR54, CR55, CR58, CR5B, CR5D, CR5E;
   unsigned char CR63, CR65, CR66, CR67, CR68, CR69, CR6D, CR6F; /* Video attrib. */
   unsigned char CR7B, CR7D;
   unsigned char CR85, CR86, CR87, CR88;
   unsigned char CR90, CR91, CR92, CR93, CRB0;
} vgaS3VRec, *vgaS3VPtr;

static int savage_init(int, int, int);
static void unlock(void);
static void lock(void);
static int savage_linear(int op, int param);

enum { UNKNOWN, TRIO64, TRIO3D, TRIO3D2X,
                VIRGE, VIRGEVX, VIRGEDX, VIRGEGX2, VIRGEMX,
                SAVAGE3D, SAVAGEMX, SAVAGE4, SAVAGEPRO, SAVAGE2000 };

static int memory, chipset;
static int is_linear, linear_base;

static CardSpecs *cardspecs;

static void savage_setpage(int page)
{
    __svgalib_outcrtc(0x6a, page);
}

static int inlinearmode(void)
{
return is_linear;
}

/* Fill in chipset specific mode information */

static void savage_getmodeinfo(int mode, vga_modeinfo *modeinfo)
{

    if(modeinfo->colors==16)return;

    modeinfo->maxpixels = memory*1024/modeinfo->bytesperpixel;
    modeinfo->maxlogicalwidth = 4088;
    modeinfo->startaddressrange = memory * 1024 - 1;
    modeinfo->haveblit = 0;
    modeinfo->flags &= ~HAVE_RWPAGE;

    if (modeinfo->bytesperpixel >= 1) {
        if(linear_base)modeinfo->flags |= CAPABLE_LINEAR;
        if (inlinearmode())
            modeinfo->flags |= IS_LINEAR | LINEAR_MODE;
    }
}

/* Read and save chipset-specific registers */

static int savage_saveregs(unsigned char regs[])
{
    unsigned char cr3a, cr66;
    vgaS3VPtr save = (vgaS3VPtr)(regs+VGA_TOTAL_REGS);

    unlock();          

    cr66 = __svgalib_incrtc(0x66);
    __svgalib_outcrtc(0x66, cr66 | 0x80);
    cr3a = __svgalib_incrtc(0x3a);
    __svgalib_outcrtc(0x3a, cr3a | 0x80);

    cr66 = __svgalib_incrtc(0x66);
    __svgalib_outcrtc(0x66, cr66 | 0x80);
    cr3a = __svgalib_incrtc(0x3a);
    __svgalib_outcrtc(0x3a, cr3a | 0x80);

#if 0
   save = (vgaS3VPtr)vgaHWSave((vgaHWPtr)save, sizeof(vgaS3VRec));
#endif

    __svgalib_outcrtc(0x66, cr66);
    __svgalib_outcrtc(0x3a, cr3a);
    __svgalib_outcrtc(0x66, cr66);
    __svgalib_outcrtc(0x3a, cr3a);

   /* First unlock extended sequencer regs */
    save->SR08 = __svgalib_inseq(0x08);
    __svgalib_outseq(0x08, 0x06);

   /* Now we save all the s3 extended regs we need */
    save->CR31 = __svgalib_incrtc(0x31);
    save->CR34 = __svgalib_incrtc(0x34);
    save->CR36 = __svgalib_incrtc(0x36);
    save->CR3A = __svgalib_incrtc(0x3a);

    if(chipset>TRIO3D)
        save->CR40 = __svgalib_incrtc(0x40);

    if(chipset==VIRGEMX)
        save->CR41 = __svgalib_incrtc(0x41);

    save->CR42 = __svgalib_incrtc(0x42);
    save->CR45 = __svgalib_incrtc(0x45);

    if(chipset>=SAVAGE3D)
        save->CR50 = __svgalib_incrtc(0x50);

    save->CR51 = __svgalib_incrtc(0x51);
    save->CR53 = __svgalib_incrtc(0x53);

    if(chipset<SAVAGE3D){
        save->CR54 = __svgalib_incrtc(0x54);
        save->CR55 = __svgalib_incrtc(0x55);
    }

    save->CR58 = __svgalib_incrtc(0x58);

    if(chipset<SAVAGE3D)
        save->CR63 = __svgalib_incrtc(0x63);

    save->CR66 = __svgalib_incrtc(0x66);
    save->CR67 = __svgalib_incrtc(0x67);
    save->CR68 = __svgalib_incrtc(0x68);
    save->CR69 = __svgalib_incrtc(0x69);

    if(chipset>=SAVAGE3D)
        save->CR6F = __svgalib_incrtc(0x6f);

    save->CR33 = __svgalib_incrtc(0x33);

    if((chipset==TRIO3D2X)||(chipset==VIRGEGX2)||(chipset==VIRGEMX))
        save->CR85 = __svgalib_incrtc(0x85);

    if((chipset==VIRGEDX)||(chipset>=SAVAGE3D))
        save->CR86 = __svgalib_incrtc(0x86);
   
    if((chipset==TRIO3D2X)||(chipset==VIRGEGX2)||(chipset==VIRGEMX)) {
        save->CR7B = __svgalib_incrtc(0x7b);
        save->CR7D = __svgalib_incrtc(0x7d);
        save->CR87 = __svgalib_incrtc(0x87);
        save->CR92 = __svgalib_incrtc(0x92);
        save->CR93 = __svgalib_incrtc(0x93);
    }

    if((chipset==TRIO3D2X)||(chipset==VIRGEDX)||(chipset==VIRGEGX2)||
       (chipset==VIRGEMX)) {
        save->CR90 = __svgalib_incrtc(0x90);
        save->CR91 = __svgalib_incrtc(0x91);
    }
   
    if(chipset>=SAVAGE3D) {
        save->CR88 = __svgalib_incrtc(0x88);
        save->CR90 = __svgalib_incrtc(0x90);
        save->CR91 = __svgalib_incrtc(0x91);
        save->CRB0 = __svgalib_incrtc(0xb0) | 0x80;
    }
   
    save->CR3B = __svgalib_incrtc(0x3b);
    save->CR3C = __svgalib_incrtc(0x3c);
    save->CR43 = __svgalib_incrtc(0x43);
    save->CR5D = __svgalib_incrtc(0x5d);
    save->CR5E = __svgalib_incrtc(0x5e);
    save->CR65 = __svgalib_incrtc(0x65);

    if(chipset<SAVAGE3D)
        save->CR6D = __svgalib_incrtc(0x6d);

   /* Save sequencer extended regs for DCLK PLL programming */
    save->SR0E = __svgalib_inseq(0x0E);
    save->SR10 = __svgalib_inseq(0x10);
    save->SR11 = __svgalib_inseq(0x11);
    save->SR12 = __svgalib_inseq(0x12);
    save->SR13 = __svgalib_inseq(0x13);

    if(chipset>=SAVAGE3D)
        save->SR29 = __svgalib_inseq(0x29);

    if((chipset==TRIO3D2X)||(chipset==VIRGEGX2)||(chipset==VIRGEMX)) {
        save->SR29 = __svgalib_inseq(0x29);
        save->SR54 = __svgalib_inseq(0x54);
        save->SR55 = __svgalib_inseq(0x55);
        save->SR56 = __svgalib_inseq(0x56);
        save->SR57 = __svgalib_inseq(0x57);
    }
       
    save->SR15 = __svgalib_inseq(0x15);

        if(chipset>=SAVAGE3D)
        save->SR30 = __svgalib_inseq(0x30);

        save->SR18 = __svgalib_inseq(0x18);

        if(chipset>=SAVAGE3D)
        save->SR1B = __svgalib_inseq(0x1B);

    if(chipset<=TRIO3D) {
        save->SR0A = __svgalib_inseq(0x0a);
        save->SR0F = __svgalib_inseq(0x0f);
    }

    __svgalib_outcrtc(0x3a, cr3a);
    __svgalib_outcrtc(0x66, cr66);

    return TOTAL_REGS - VGA_TOTAL_REGS;
}

/* Set chipset-specific registers */

static void savage_setregs(const unsigned char regs[], int mode)
{  
    int tmp;
    vgaS3VPtr restore = (vgaS3VPtr)(regs+VGA_TOTAL_REGS);
   
    unlock();

#if 0
   /* Are we going to reenable STREAMS in this new mode? */
   s3vPriv.STREAMSRunning = restore->CR67 & 0x0c;

   /* First reset GE to make sure nothing is going on */
   outb(vgaCRIndex, 0x66);
   if(port_in(vgaCRReg) & 0x01) S3SAVGEReset(0,__LINE__,__FILE__);
#endif
   
   /* As per databook, always disable STREAMS before changing modes */
    __svgalib_outcrtc(0x67, __svgalib_incrtc(0x67)&0xf3);

    if(chipset<SAVAGE3D)
        __svgalib_outcrtc(0x63, restore->CR63);

    __svgalib_outcrtc(0x66, restore->CR66);
    __svgalib_outcrtc(0x3a, restore->CR3A);
    __svgalib_outcrtc(0x31, restore->CR31);
    __svgalib_outcrtc(0x58, restore->CR58);
   
    if(chipset<SAVAGE3D)
        __svgalib_outcrtc(0x55, restore->CR55);
   
#if 0 /* why is it done twice? */
    __svgalib_outseq(0x08, 0x06);
    __svgalib_outseq(0x12, restore->SR12);    
    __svgalib_outseq(0x13, restore->SR13);    
    __svgalib_outseq(0x29, restore->SR29);    
    __svgalib_outseq(0x15, restore->SR15);
#endif

    __svgalib_outcrtc(0x53, restore->CR53);
    __svgalib_outcrtc(0x5d, restore->CR5D);
    __svgalib_outcrtc(0x5e, restore->CR5E);
    __svgalib_outcrtc(0x3b, restore->CR3B);
    __svgalib_outcrtc(0x3c, restore->CR3C);
    __svgalib_outcrtc(0x43, restore->CR43);
    __svgalib_outcrtc(0x65, restore->CR65);

    if(chipset<SAVAGE3D)
        __svgalib_outcrtc(0x6d, restore->CR6D);

   /* Restore the desired video mode with CR67 */

    if(chipset<SAVAGE3D)
        __svgalib_outcrtc(0x67, 0x50 | (__svgalib_incrtc(0x67)&0x0f));
    else
        __svgalib_outcrtc(0x67, 0x50 | (__svgalib_incrtc(0x67)&0xf3));
       
    sleep(1);
    __svgalib_outcrtc(0x67, restore->CR67&0xf3);
       
    __svgalib_outcrtc(0x34, restore->CR34);

    if(chipset>TRIO3D)
        __svgalib_outcrtc(0x40, restore->CR40);

    if(chipset==VIRGEMX)
        __svgalib_outcrtc(0x41, restore->CR41);

    __svgalib_outcrtc(0x42, restore->CR42);
    __svgalib_outcrtc(0x45, restore->CR45);

    if(chipset>=SAVAGE3D)
        __svgalib_outcrtc(0x50, restore->CR50);

    __svgalib_outcrtc(0x51, restore->CR51);

    if(chipset<SAVAGE3D)
        __svgalib_outcrtc(0x54, restore->CR54);

    __svgalib_outcrtc(0x36, restore->CR36);
    __svgalib_outcrtc(0x68, restore->CR68);
    __svgalib_outcrtc(0x69, restore->CR69);

    if(chipset>=SAVAGE3D)
        __svgalib_outcrtc(0x6f, restore->CR6F);

    __svgalib_outcrtc(0x33, restore->CR33);

    if(chipset>=SAVAGE3D) {
        __svgalib_outcrtc(0x86, restore->CR86);
        __svgalib_outcrtc(0x88, restore->CR88);
        __svgalib_outcrtc(0x90, restore->CR90);
        __svgalib_outcrtc(0x91, restore->CR91);
        __svgalib_outcrtc(0xb0, restore->CRB0);
    }

    if((chipset==TRIO3D2X)||(chipset==VIRGEGX2)||(chipset==VIRGEMX))
        __svgalib_outcrtc(0x85, restore->CR85);

    if(chipset==VIRGEDX)
        __svgalib_outcrtc(0x86, restore->CR86);

    if(chipset==VIRGEGX2) {
        __svgalib_outcrtc(0x7b, restore->CR7B);
        __svgalib_outcrtc(0x7d, restore->CR7D);
        __svgalib_outcrtc(0x87, restore->CR87);
        __svgalib_outcrtc(0x92, restore->CR92);
        __svgalib_outcrtc(0x93, restore->CR93);
    }

    if((chipset==TRIO3D2X)||(chipset==VIRGEDX)||(chipset==VIRGEGX2)||(chipset==VIRGEMX)) {
        __svgalib_outcrtc(0x90, restore->CR90);
        __svgalib_outcrtc(0x91, restore->CR91);
    }

    __svgalib_outseq(0x08, 0x06);
   /* Restore extended sequencer regs for MCLK. SR10 == 255 indicates that
    * we should leave the default SR10 and SR11 values there.
    */


    if (restore->SR10 != 255) {  
        __svgalib_outseq(0x10, restore->SR10);    
        __svgalib_outseq(0x11, restore->SR11);    
    }

   /* Restore extended sequencer regs for DCLK */

    __svgalib_outseq(0x12, restore->SR12);    
    __svgalib_outseq(0x13, restore->SR13);    

    if(chipset>=SAVAGE3D)
        __svgalib_outseq(0x29, restore->SR29);    

    if((chipset==TRIO3D2X)||(chipset==VIRGEGX2)||(chipset==VIRGEMX)) {
        __svgalib_outseq(0x29, restore->SR29);    
        __svgalib_outseq(0x54, restore->SR54);    
        __svgalib_outseq(0x55, restore->SR55);    
        __svgalib_outseq(0x56, restore->SR56);    
        __svgalib_outseq(0x57, restore->SR57);    
    }

    __svgalib_outseq(0x18, restore->SR18);

    if(chipset>=SAVAGE3D)
        __svgalib_outseq(0x1B, restore->SR1B);

    tmp = __svgalib_inseq(0x15) & ~0x21;
    __svgalib_outseq(0x15, tmp | 0x03);
    __svgalib_outseq(0x15, tmp | 0x23);
    __svgalib_outseq(0x15, tmp | 0x03);
    __svgalib_outseq(0x15, restore->SR15);
   
    sleep(1);
       
    if(chipset<=TRIO3D) {
        __svgalib_outseq(0x0a, restore->SR0A);
        __svgalib_outseq(0x0f, restore->SR0F);
    } else if (chipset >= SAVAGE3D) {
                __svgalib_outseq(0x30, restore->SR30);
        }

    __svgalib_outseq(0x08, restore->SR08);


   /* Now write out CR67 in full, possibly starting STREAMS */
   
    __svgalib_outcrtc(0x67, 0x50);
    sleep(1);
    __svgalib_outcrtc(0x67, restore->CR67&0xf3);

    __svgalib_outcrtc(0x53, restore->CR53);
    __svgalib_outcrtc(0x66, restore->CR66);
    __svgalib_outcrtc(0x3a, restore->CR3A);
}


/* Return nonzero if mode is available */

static int savage_modeavailable(int mode)
{
    struct info *info;
    ModeTiming *modetiming;
    ModeInfo *modeinfo;

    if (IS_IN_STANDARD_VGA_DRIVER(mode))
        return __svgalib_vga_driverspecs.modeavailable(mode);

    info = &__svgalib_infotable[mode];
   
    if (memory * 1024 < info->ydim * info->xbytes)
        return 0;

    modeinfo = __svgalib_createModeInfoStructureForSvgalibMode(mode);

    if(modeinfo->bytesPerPixel==3) return 0;

    modetiming = malloc(sizeof(ModeTiming));

    if (__svgalib_getmodetiming(modetiming, modeinfo, cardspecs)) {
        free(modetiming);
        free(modeinfo);
        return 0;
    }

    free(modetiming);
    free(modeinfo);
    return SVGADRV;
}

/* Local, called by setmode(). */
#define BASE_FREQ 14.31818

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)
{
   double ffreq, ffreq_min, ffreq_max;
   double div, diff, best_diff;
   unsigned int m;
   unsigned char n1, n2;
   unsigned char best_n1=16+2, best_n2=2, best_m=125+2;

   ffreq     = freq     / 1000.0 / BASE_FREQ;
   ffreq_min = freq_min / 1000.0 / BASE_FREQ;
   ffreq_max = freq_max / 1000.0 / BASE_FREQ;

   if (ffreq < ffreq_min / (1<<max_n2)) {
      ffreq = ffreq_min / (1<<max_n2);
   }
   if (ffreq > ffreq_max / (1<<min_n2)) {
      ffreq = ffreq_max / (1<<min_n2);
   }

   /* work out suitable timings */

   best_diff = ffreq;
   
   for (n2=min_n2; n2<=max_n2; n2++) {
      for (n1 = min_n1+2; n1 <= max_n1+2; n1++) {
         m = (int)(ffreq * n1 * (1<<n2) + 0.5) ;
         if (m < min_m+2 || m > 127+2)
            continue;
         div = (double)(m) / (double)(n1);       
         if ((div >= ffreq_min) &&
             (div <= ffreq_max)) {
            diff = ffreq - div / (1<<n2);
            if (diff < 0.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 void savage_initializemode(unsigned char *moderegs,
                            ModeTiming * modetiming, ModeInfo * modeinfo, int mode)
{
#define NL (1<<30)
    int i, j, dclk, width, tmp;
    int clocklimits[14][2]={
        {},
        {86000,0}, /* Trio 64 - Guess */
        {115000,115000}, /* Trio 3D */
        {NL,0}, /* Trio 3D2X */
        {80000,0}, /* Virge */
        {110000,110000}, /* Virge VX */
        {80000,0}, /* Virge DX */
        {NL,0}, /* Virge GX2 */
        {NL,0}, /* Virge MX */
        {NL,NL}, /* Savage 3D */
        {0,0}, /* Savage MX */
        {NL,NL}, /* Savage 4 */
        {NL,NL}, /* Savage Pro */
        {230000,230000}, /* Savage 2000 */
    };
    int cargs[14][4]= {
        {},
        {31,  0, 3, 86000}, /* Trio 64 - Guess */
        {31,  0, 4, 230000},
        {31,  0, 4, 170000},
        {31,  0, 3, 135000},
        {31,  0, 4, 220000},
        {31,  0, 3, 135000},
        {31,  0, 4, 170000},
        {31,  0, 4, 170000},
        {127, 0, 4, 180000},
        {127, 0, 4, 180000},
        {127, 0, 4, 180000},
        {127, 0, 4, 180000},
        {127, 0, 4, 180000},
    };
#undef NL
   
    vgaS3VPtr new = (vgaS3VPtr)(moderegs+VGA_TOTAL_REGS);
   
    if(modeinfo->bitsPerPixel==16) {
        if((chipset==VIRGE)|| (chipset==TRIO64)) {
            modetiming->HDisplay *=2;
            modetiming->HSyncStart *=2;
            modetiming->HSyncEnd *=2;
            modetiming->HTotal *=2;
            modetiming->CrtcHDisplay =modetiming->HDisplay;
            modetiming->CrtcHSyncStart =modetiming->HSyncStart;
            modetiming->CrtcHSyncEnd =modetiming->HSyncEnd;
            modetiming->CrtcHTotal =modetiming->HTotal;
        }
    }

    __svgalib_setup_VGA_registers(moderegs, modetiming, modeinfo);

    tmp = __svgalib_incrtc(0x3a);
    if((chipset==TRIO3D2X)||(chipset==VIRGEGX2)||(chipset==VIRGEMX)) {
        if(0) new->CR3A = tmp | 0x90;                   /* ENH 256, no PCI burst! */
            else new->CR3A = (tmp & 0x38) | 0x10;       /* ENH 256, PCI burst */
    } else {
        if(0) new->CR3A = tmp | 0x95;                   /* ENH 256, no PCI burst! */
            else new->CR3A = (tmp & 0x7f) | 0x15;       /* ENH 256, PCI burst */
    }

    new->CR53 |= 0x08;          /* Disables MMIO */
    new->CR31 = 0x09;           /* Enable 64k window */

    if(chipset==VIRGEVX) {
        new->CR66 = 0x90;
        new->CR63 = 0x09;
        new->CR58 = 0x40;
    } else {
        if((chipset==TRIO3D2X)||(chipset==VIRGEGX2)||(chipset==VIRGEMX)) {
            new->CR63 = 0x08;
        } else {
            new->CR63 = 0x00;
        }
        new->CR66 = 0x89;
        new->CR58 = 0;
    }
   
/* Now do clock PLL programming. Use the s3gendac function to get m,n */
/* Also determine if we need doubling etc. */

    dclk = modetiming->pixelClock;
    new->CR67 = 0x00;             /* Defaults */
    if(chipset > TRIO3D) {
        new->SR15 = 0x03 | 0x80;
        if(chipset==VIRGE)
           new->SR15 = (__svgalib_inseq(0x15) & 0x80) | 3;
    } else {
        new->SR15 = (__svgalib_inseq(0x15) & 0x80) | 3;
        new->SR0A = __svgalib_inseq(0x0a);
    }
    new->SR18 = 0x00;
    new->CR43 = 0x00;
    new->CR45 = 0x00;
    new->CR65 = 0x00;
    new->CR54 = 0x00;
   
    if(chipset > TRIO3D)
        new->CR40 = __svgalib_incrtc(0x40) & 0xfe ;
   
    new->SR10 = 255; /* This is a reserved value, so we use as flag */
    new->SR11 = 255;

        new->SR1B = 0;
        new->SR30 = __svgalib_inseq(0x30);

    switch( modeinfo->colorBits ) {
        case 8:
            new->CR67 = 0x00;   /* 8bpp, 1 pixel/clock */
            if(dclk >= clocklimits[chipset][0]) new->CR67 |= 0x10;
            break;
        case 15:
            new->CR67 = 0x20;
            if(dclk >= clocklimits[chipset][1]) new->CR67 |= 0x10;
            break;
        case 16:
            new->CR67 = 0x40;
            if(dclk >= clocklimits[chipset][1]) new->CR67 |= 0x10;
            break;
        case 24:
            new->CR67 = 0xd0;
            break;
    }
   
    /* Now the special cases */
    if(chipset==VIRGE) {
        if(new->CR67 == 0x10) {
            new->SR15 |= 0x10;
            new->SR18 =  0x80;
        }
    }
   
    {
        unsigned int m, n, r;

        savageCalcClock(dclk, 1, 1, cargs[chipset][0], cargs[chipset][1],
          cargs[chipset][2], cargs[chipset][3], cargs[chipset][3]*2,
          &m, &n, &r);
        if(chipset < SAVAGE3D)
            new->SR12 = (r << 5) | (n & 0x1F);
            else new->SR12 = (r << 6) | (n & 0x3F);
        new->SR13 = m & 0xFF;
        new->SR29 = (r & 4) | (m & 0x100) >> 5 | (n & 0x40) >> 2;
    }


   /* If we have an interlace mode, set the interlace bit. Note that mode
    * vertical timings are already adjusted by the standard VGA code
    */

    if (modetiming->flags & INTERLACED) {
        new->CR42 = 0x20; /* Set interlace mode */
    } else {
        new->CR42 = 0x00;
    }

    /* Set display fifo */
    if((chipset==TRIO3D2X)||(chipset==VIRGEGX2)||(chipset==VIRGEMX)) {
        new->CR34 = 0;
    } else {
        new->CR34 = 0x10;
    }

    /* Now we adjust registers for extended mode timings */
    /* This is taken without change from the accel/s3_virge code */

    i = ((((modetiming->CrtcHTotal >> 3) - 5) & 0x100) >> 8) |
        ((((modetiming->CrtcHDisplay >> 3) - 1) & 0x100) >> 7) |
        ((((modetiming->CrtcHSyncStart >> 3) - 1) & 0x100) >> 6) |
        ((modetiming->CrtcHSyncStart & 0x800) >> 7);

    if ((modetiming->CrtcHSyncEnd >> 3) - (modetiming->CrtcHSyncStart >> 3) > 64)
        i |= 0x08;   /* add another 64 DCLKs to blank pulse width */

    if ((modetiming->CrtcHSyncEnd >> 3) - (modetiming->CrtcHSyncStart >> 3) > 32)
        i |= 0x20;   /* add another 32 DCLKs to hsync pulse width */

    j = (  moderegs[0] + ((i&0x01)<<8)
         + moderegs[4] + ((i&0x10)<<4) + 1) / 2;

    if (j-(moderegs[4] + ((i&0x10)<<4)) < 4) {
        if (moderegs[4] + ((i&0x10)<<4) + 4 <= moderegs[0]+ ((i&0x01)<<8))
            j = moderegs[4] + ((i&0x10)<<4) + 4;
        else
            j = moderegs[0]+ ((i&0x01)<<8) + 1;
    }
   
    new->CR3B = j & 0xFF;
    i |= (j & 0x100) >> 2;
    new->CR3C = (moderegs[0] + ((i&0x01)<<8))/2;

    new->CR5D = i;

    new->CR5E = (((modetiming->CrtcVTotal - 2) & 0x400) >> 10)  |
                (((modetiming->CrtcVDisplay - 1) & 0x400) >> 9) |
                (((modetiming->CrtcVSyncStart) & 0x400) >> 8)   |
                (((modetiming->CrtcVSyncStart) & 0x400) >> 6)   | 0x40;
   
    width = modeinfo->lineWidth >> 3;
    moderegs[19] = 0xFF & width;
    new->CR51 = (0x300 & width) >> 4; /* Extension bits */
   
    /* And finally, select clock source 2 for programmable PLL */
    moderegs[VGA_MISCOUTPUT] |= 0x0c;      

    if(chipset>=SAVAGE3D) {
        /* Set frame buffer description */
        if (modeinfo->colorBits <= 8) {
            new->CR50 = 0;
        } else {
            if (modeinfo->colorBits <= 16) {
                new->CR50 = 0x10;
            } else {
                new->CR50 = 0x30;
            }
        }
   
        if (modeinfo->width == 640)
            new->CR50 |= 0x40;
        else if (modeinfo->width == 800)
            new->CR50 |= 0x80;
        else if (modeinfo->width == 1024);
        else if (modeinfo->width == 1152)
            new->CR50 |= 0x01;
        else if (modeinfo->width == 1280)
            new->CR50 |= 0x41;
        else if (modeinfo->width == 2048 && new->CR31 & 2);
        else if (modeinfo->width == 1600)
            new->CR50 |= 0x81; /* TODO: need to consider bpp=4 */
        else
            new->CR50 |= 0xC1; /* default to use GlobalBD */

        new->CR33 = 0x08;
        new->CR6F = __svgalib_incrtc(0x6f);
        new->CR86 = __svgalib_incrtc(0x86);
        new->CR88 = __svgalib_incrtc(0x88);
        new->CRB0 = __svgalib_incrtc(0xb0) | 0x80;
    } else /* trio, virge */ {
        new->CR33 = 0x20;
        if((chipset==VIRGEDX)||(chipset<=TRIO3D)) {
            new->CR86 = 0x80;
        }

        if((chipset!=VIRGEVX)&&(chipset!=VIRGE)) {
            new->CR91 = (modeinfo->lineWidth+7) >> 3;
            new->CR90 = 0x80 | ((modeinfo->lineWidth+7) >> 11);
        }    

        if(chipset == VIRGEVX) {
            if(modeinfo->colorBits>16) new->CR6D = 0x51 ; else new->CR6D=0;
        } else {
            new->CR6D = __svgalib_incrtc(0x6d);
            new->CR65 &= ~0x38;
            switch(modeinfo->colorBits) {
                case 8:
                    break;
                case 15:
                case 16:
                    new->CR65 |= 2<<3;
                    break;
                default:
                    new->CR65 |= 4<<3;
                    break;
            }
        }

        if(chipset == VIRGEMX) {
            new->SR54=0x10;
            new->SR55=0x80;
            new->SR56=0x10;
            new->SR57=0x80;
        } else {
            new->SR54=0x1f;
            new->SR55=0x9f;
            new->SR56=0x1f;
            new->SR57=0x9f;
        }
    }

   /* Now we handle various XConfig memory options and others */

    new->CR36 = __svgalib_incrtc(0x36);
   
#if 0
   if (mode->Private) {
      new->CR67 &= ~1;
      if(
        (s3vPriv.chip != S3_SAVAGE2000) &&
        (mode->Private[0] & (1 << S3_INVERT_VCLK)) &&
        (mode->Private[S3_INVERT_VCLK])
      )
         new->CR67 |= 1;

      if (mode->Private[0] & (1 << S3_BLANK_DELAY)) {
         new->CR65 = (new->CR65 & ~0x38)
            | (mode->Private[S3_BLANK_DELAY] & 0x07) << 3;
      }
   }
#endif

    if(__svgalib_emulatepage || is_linear) new->CR58 |= 0x13;
    new->CR68 = __svgalib_incrtc(0x68);
    new->CR69 = 0;

    return ;
}


static int savage_setmode(int mode, int prv_mode)
{
    unsigned char *moderegs;
    ModeTiming *modetiming;
    ModeInfo *modeinfo;

    if (IS_IN_STANDARD_VGA_DRIVER(mode)) {
        __svgalib_outcrtc(0x34, 0);
        return __svgalib_vga_driverspecs.setmode(mode, prv_mode);
    }
    if (!savage_modeavailable(mode))
        return 1;

    modeinfo = __svgalib_createModeInfoStructureForSvgalibMode(mode);

    modetiming = malloc(sizeof(ModeTiming));
    if (__svgalib_getmodetiming(modetiming, modeinfo, cardspecs)) {
        free(modetiming);
        free(modeinfo);
        return 1;
    }

    moderegs = malloc(TOTAL_REGS);
   
    savage_initializemode(moderegs, modetiming, modeinfo, mode);
    free(modetiming);

    __svgalib_setregs(moderegs);        /* Set standard regs. */
    savage_setregs(moderegs, mode);             /* Set extended regs. */
    free(moderegs);

    free(modeinfo);

    return 0;
}


/* Unlock chipset-specific registers */

static void unlock(void)
{
    __svgalib_outcrtc(0x11,__svgalib_incrtc(0x11)&0x7f);
    __svgalib_outcrtc(0x38, 0x48);
    __svgalib_outcrtc(0x39, 0xa5);
    __svgalib_outcrtc(0x40,__svgalib_incrtc(0x40)&0xfe);  
}

static void lock(void)
{
}


#define VENDOR_ID 0x5333

/* Indentify chipset, initialize and return non-zero if detected */

int savage_test(void)
{
    int found;
    int id;
    unsigned long buf[64];
   
    found=__svgalib_pci_find_vendor_vga(VENDOR_ID,buf,0);
    id=(buf[0]>>16)&0xffff;
    if(found)return 0;
    switch(id) {
        case 0x8811:
        case 0x8903:
        case 0x8904:
        case 0x8a13:
        case 0x5631:
        case 0x883d:
        case 0x8a01:
        case 0x8a10:
        case 0x8c00:
        case 0x8c01:
        case 0x8c02:
        case 0x8c03:
        case 0x8a20:
        case 0x8a21:
        case 0x8a22:
        case 0x8a23:
        case 0x8c10:
        case 0x8c12:
        case 0x9102:
                case 0x8d03:
                case 0x8d04:
            savage_init(0,0,0);
            return 1;
            break;
        default:
            return 0;
    }
}


/* Set display start address (not for 16 color modes) */

static void savage_setdisplaystart(int address)
{
  address=address >> 2;
  __svgalib_outcrtc(0x0d,address&0xff);
  __svgalib_outcrtc(0x0c,(address>>8)&0xff);
  __svgalib_outcrtc(0x69,(address>>16)&0xff);
 
}

/* Set logical scanline length (usually multiple of 8) */
/* Cirrus supports multiples of 8, up to 4088 */

static void savage_setlogicalwidth(int width)
{  
    int offset = width >> 3;
 
    __svgalib_outcrtc(0x13,offset&0xff);
}

static int savage_linear(int op, int param)
{
    if (op==LINEAR_ENABLE){
       __svgalib_outcrtc(0x58,__svgalib_incrtc(0x58)|0x13);
       is_linear=1;
       return 0;
    };
    if (op==LINEAR_DISABLE) {
       __svgalib_outcrtc(0x58,__svgalib_incrtc(0x58)&~0x13);
       is_linear=0;
       return 0;
    };
    if (op==LINEAR_QUERY_BASE) return linear_base;
    if (op == LINEAR_QUERY_RANGE || op == LINEAR_QUERY_GRANULARITY) return 0;           /* No granularity or range. */
        else return -1;         /* Unknown function. */
}

static int match_programmable_clock(int clock)
{
return clock ;
}

static int map_clock(int bpp, int clock)
{
return clock ;
}

static int map_horizontal_crtc(int bpp, int pixelclock, int htiming)
{
return htiming;
}

static struct {
    unsigned char c8;
    unsigned short c15;
    unsigned short c16;
    unsigned int c32;
} cursor_colors[16*2];

static int pal=1, palette[768];

static int findcolor(int rgb) {
   int i,j,k,l=0;

   if(pal)vga_getpalvec(0,256,palette);
   pal=0;
   k=0xffffff;
   for(i=0;i<256;i++) {
      j=((rgb&0xff)-(palette[i*3+2]<<2))*((rgb&0xff)-(palette[i*3+2]<<2))+
        (((rgb>>8)&0xff)-(palette[i*3+1]<<2))*(((rgb>>8)&0xff)-(palette[i*3+1]<<2))+
        (((rgb>>16)&0xff)-(palette[i*3]<<2))*(((rgb>>16)&0xff)-(palette[i*3]<<2));
      if(j==0) {
         return i;
      }
      if(j<k) {
         k=j;
         l=i;
      }
   }
   return l;
}

static int savage_cursor( int cmd, int p1, int p2, int p3, int p4, void *p5) {
    unsigned long *b3;
    unsigned char *buf;
    int i, j;
    unsigned int l1, l2;
   
    switch(cmd){
        case CURSOR_INIT:
            return 1;
        case CURSOR_HIDE:
            __svgalib_outcrtc(0x45,__svgalib_incrtc(0x45)&0xfe);
            break;
        case CURSOR_SHOW:
            __svgalib_outcrtc(0x45,__svgalib_incrtc(0x45)|0x01); /* Windows */
            break;
        case CURSOR_POSITION:
            __svgalib_outcrtc(0x46,p1>>8);
            __svgalib_outcrtc(0x47,p1&0xff);
            __svgalib_outcrtc(0x49,p2&0xff);
            __svgalib_outcrtc(0x4e,0);
            __svgalib_outcrtc(0x4f,0);
            __svgalib_outcrtc(0x48,p2>>8);
            break;
        case CURSOR_SELECT:
            i=memory-(16-p1);
            switch(CI.colors) {
                case 256:
                    __svgalib_incrtc(0x45);
                    __svgalib_outcrtc(0x4b,cursor_colors[p1*2].c8);
                    __svgalib_outcrtc(0x4b,cursor_colors[p1*2].c8);
                    __svgalib_incrtc(0x45);
                    __svgalib_outcrtc(0x4a,cursor_colors[p1*2+1].c8);
                    __svgalib_outcrtc(0x4a,cursor_colors[p1*2+1].c8);
                   break;
                case 32768:
                    __svgalib_incrtc(0x45);
                    __svgalib_outcrtc(0x4b,cursor_colors[p1*2].c15&0xff);
                    __svgalib_outcrtc(0x4b,cursor_colors[p1*2].c15>>8);
                    __svgalib_outcrtc(0x4b,cursor_colors[p1*2].c15&0xff);
                    __svgalib_outcrtc(0x4b,cursor_colors[p1*2].c15>>8);
                    __svgalib_incrtc(0x45);
                    __svgalib_outcrtc(0x4a,cursor_colors[p1*2+1].c15&0xff);
                    __svgalib_outcrtc(0x4a,cursor_colors[p1*2+1].c15>>8);
                    __svgalib_outcrtc(0x4a,cursor_colors[p1*2+1].c15&0xff);
                    __svgalib_outcrtc(0x4a,cursor_colors[p1*2+1].c15>>8);
                   break;
                case 65536:
                    __svgalib_incrtc(0x45);
                    __svgalib_outcrtc(0x4b,cursor_colors[p1*2].c16&0xff);
                    __svgalib_outcrtc(0x4b,cursor_colors[p1*2].c16>>8);
                    __svgalib_outcrtc(0x4b,cursor_colors[p1*2].c16&0xff);
                    __svgalib_outcrtc(0x4b,cursor_colors[p1*2].c16>>8);
                    __svgalib_incrtc(0x45);
                    __svgalib_outcrtc(0x4a,cursor_colors[p1*2+1].c16&0xff);
                    __svgalib_outcrtc(0x4a,cursor_colors[p1*2+1].c16>>8);
                    __svgalib_outcrtc(0x4a,cursor_colors[p1*2+1].c16&0xff);
                    __svgalib_outcrtc(0x4a,cursor_colors[p1*2+1].c16>>8);
                   break;
                case (1<<24):
                    __svgalib_incrtc(0x45);
                    __svgalib_outcrtc(0x4b,cursor_colors[p1*2].c32&0xff);
                    __svgalib_outcrtc(0x4b,(cursor_colors[p1*2].c32>>8)&0xff);
                    __svgalib_outcrtc(0x4b,(cursor_colors[p1*2].c32>>16)&0xff);
                    __svgalib_incrtc(0x45);
                    __svgalib_outcrtc(0x4a,cursor_colors[p1*2+1].c32&0xff);
                    __svgalib_outcrtc(0x4a,(cursor_colors[p1*2+1].c32>>8)&0xff);
                    __svgalib_outcrtc(0x4a,(cursor_colors[p1*2+1].c32>>16)&0xff);
                   break;
            }
            __svgalib_outcrtc(0x4d, i&0xff);
            __svgalib_outcrtc(0x4c, i>>8);
            break;
        case CURSOR_IMAGE:
            buf=malloc(1024);
            cursor_colors[p1*2].c8=findcolor(p3);
            cursor_colors[p1*2].c32=p3;
            cursor_colors[p1*2].c16=((p3&0xf80000)>>8)|((p3&0xfc00)>>5)|((p3&0xf8)>>3);
            cursor_colors[p1*2].c15=((p3&0xf80000)>>9)|((p3&0xf800)>>5)|((p3&0xf8)>>3);
            cursor_colors[p1*2+1].c8=findcolor(p4);
            cursor_colors[p1*2+1].c32=p4;
            cursor_colors[p1*2+1].c16=((p4&0xf80000)>>8)|((p4&0xfc00)>>5)|((p4&0xf8)>>3);
            cursor_colors[p1*2+1].c15=((p4&0xf80000)>>9)|((p4&0xf800)>>5)|((p4&0xf8)>>3);
            i=memory*1024-(16-p1)*1024;
            b3=(unsigned long *)p5;
            switch(p2) {
                case 0:
                    for(j=0;j<32;j++) {
                        l2=*(b3+j);
                        l1=*(b3+32+j);
                        l1=BE32(l1);
                        l2=BE32(l2);
                        l2=l2&l1;
                        l1=~l1;
                        *(unsigned short *)(buf+16*j)=l1&0xffff;
                        *(unsigned short *)(buf+16*j+2)=l2&0xffff;
                        *(unsigned short *)(buf+16*j+4)=(l1>>16)&0xffff;
                        *(unsigned short *)(buf+16*j+6)=(l2>>16)&0xffff;
                        *(unsigned short *)(buf+16*j+8)=0xffff;
                        *(unsigned short *)(buf+16*j+10)=0;
                        *(unsigned short *)(buf+16*j+12)=0xffff;
                        *(unsigned short *)(buf+16*j+14)=0;
                    }
                    for(j=32;j<64;j++) {
                        *(unsigned short *)(buf+16*j)=0xffff;
                        *(unsigned short *)(buf+16*j+2)=0;
                        *(unsigned short *)(buf+16*j+4)=0xffff;
                        *(unsigned short *)(buf+16*j+6)=0;
                        *(unsigned short *)(buf+16*j+8)=0xffff;
                        *(unsigned short *)(buf+16*j+10)=0;
                        *(unsigned short *)(buf+16*j+12)=0xffff;
                        *(unsigned short *)(buf+16*j+14)=0;
                    }
                    break;
            }
            vga_drawscansegment(buf, i/CI.bytesperpixel,0,1024);
            break;
    }
    return 0;
}      

/* Function table (exported) */
DriverSpecs __svgalib_savage_driverspecs =
{
    savage_saveregs,
    savage_setregs,
    unlock,
    lock,
    savage_test,
    savage_init,
    savage_setpage,
    NULL,
    NULL,
    savage_setmode,
    savage_modeavailable,
    savage_setdisplaystart,
    savage_setlogicalwidth,
    savage_getmodeinfo,
    0,                          /* old blit funcs */
    0,
    0,
    0,
    0,
    0,                          /* ext_set */
    0,                          /* accel */
    savage_linear,
    0,                          /* accelspecs, filled in during init. */
    NULL,                       /* Emulation */
    savage_cursor,
};

/* Initialize chipset (called after detection) */
static int savage_init(int force, int par1, int par2)
{
    unsigned long buf[64];
    unsigned long mmio_base;
    int found=0, config1;
    int mems[8]={2,4,8,12,16,32,64,2};
    char *chipnames[] = {"Unknown", "Trio64", "Trio 3D", "Trio 3d/2X", "Virge", "Virge VX",
                         "Virge DX", "Virge GX2", "Virge MX",
                         "Savage3D", "SavageMX", "Savage4", "SavagePro", "Savage2000"};
    int vmems[9][8]= { {0},
        {0,0,0,1,0,0,1,0},
        {4,0,4,0,2,0,0,0},
        {4,0,4,0,0,0,2,0},
        {4,0,0,0,2,0,1,0},
        {2,4,6,8,2,4,6,8},
        {4,0,0,0,2,0,1,0},
        {0,0,4,4,0,0,2,2},
        {0,0,4,4,0,0,2,2}
    };
    int id;

//    unlock();
    if (force) {
        memory = par1;
        chipset = par2;
    } else {

    };

    found=__svgalib_pci_find_vendor_vga(VENDOR_ID,buf,0);
    if (found) {
      printk("Savage Card Not Found !\n");
      return 0;
    }
   
    id=(buf[0]>>16)&0xffff;
    switch(id) {
        case 0x8811:
            chipset = TRIO64;
            break;
        case 0x8903:
        case 0x8904:
            chipset = TRIO3D;
            break;
        case 0x8a13:
            chipset = TRIO3D2X;
            break;
        case 0x5631:
            chipset = VIRGE;
            break;
        case 0x883d:
            chipset = VIRGEVX;
            break;
        case 0x8a01:
            chipset = VIRGEDX;
            break;
        case 0x8a10:
            chipset = VIRGEGX2;
            break;
        case 0x8c00:
        case 0x8c01:
        case 0x8c02:
        case 0x8c03:
            chipset = VIRGEMX;
            break;
        case 0x8a20:
        case 0x8a21:
            chipset = SAVAGE3D;
            break;
        case 0x8c10:
        case 0x8c12:
            chipset = SAVAGEMX;
            break;
        case 0x8a22:
        case 0x8a23:
        case 0x8d03:
        case 0x8d04:
            chipset = SAVAGE4;
            break;
        case 0x9102:
            chipset = SAVAGE2000;
            break;
        default:
            chipset = UNKNOWN;
    }

    if(chipset<SAVAGE3D) {
        linear_base=buf[4]&0xffffff00;
        mmio_base = linear_base + 0x1000000;
#if 1 /* You need to write linear address to CR59 5A, and enable MMIO in CR53 -
         But how to do it if it's a secondary card??? */
        if(__svgalib_secondary) {
            __svgalib_mmio_base = mmio_base;
            __svgalib_mmio_size = 0x10000;
            map_mmio();
            __svgalib_vgammbase=0x8000;
            __svgalib_mm_io_mapio();
        }
#endif
        unlock();
        config1=__svgalib_incrtc(0x36);
        memory = 1024 * vmems[chipset][(config1&0xe0)>>5];
    } else {
        linear_base=buf[5]&0xffffff00;
        mmio_base  =buf[4]&0xffffff00;
        __svgalib_mmio_base = mmio_base;
        __svgalib_mmio_size = 0x10000;
        map_mmio();
   
        __svgalib_vgammbase=0x8000;
        __svgalib_mm_io_mapio();
        unlock();
   
        config1=__svgalib_incrtc(0x36);
        if(chipset >= SAVAGE4) {
            memory=mems[config1>>5]*1024;
        } else {
            switch(config1>>6) {
                case 0:
                    memory=8192;
                    break;
                case 0x40:
                case 0x80:
                    memory=4096;
                    break;
                case 0xC0:
                    memory=2048;
                    break;
            }
        }
    }

    if (__svgalib_driver_report) {
        printk(KERN_INFO "Using SAVAGE driver, %iKB. Chipset: %s\n",memory, chipnames[chipset]);
    };
    cardspecs = malloc(sizeof(CardSpecs));
    cardspecs->videoMemory = memory;
    cardspecs->maxPixelClock4bpp = 0;  
    cardspecs->maxPixelClock8bpp = 250000;     
    cardspecs->maxPixelClock16bpp = 250000;    
    cardspecs->maxPixelClock24bpp = 220000;
    cardspecs->maxPixelClock32bpp = 220000;
    cardspecs->flags = INTERLACE_DIVIDE_VERT | CLOCK_PROGRAMMABLE;
    cardspecs->maxHorizontalCrtc = 4088;
    cardspecs->nClocks = 0;
    cardspecs->mapClock = map_clock;
    cardspecs->mapHorizontalCrtc = map_horizontal_crtc;
    cardspecs->matchProgrammableClock=match_programmable_clock;
    __svgalib_driverspecs = &__svgalib_savage_driverspecs;
    __svgalib_banked_mem_base=0xa0000;
    __svgalib_banked_mem_size=0x10000;
    __svgalib_linear_mem_base=linear_base;
    __svgalib_linear_mem_size=memory*0x400;

    sleep(4);
   
    return 0;

}