Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

/* Philippe Anel <philippe.anel@noos.fr> 
        - 2001-08-12 : First release.
        - 2001-08-15 : Added G450, with source code "adapted from" from Xfree86 4.1.0
        - 2001-08-23 : Added 'palettedepth 8' and a few 'ultradebug' ...
        - 2001-08-24 : Removed a possible lock in initialization.
        - 2001-08-30 : Hey ! The 32 bits mode is PALETIZED (Gamma Control I presume) !
                                  And it seems plan9 assume the frame buffer is organized in
                                  Big Endian format ! (+ Fix for the palette init. )
        - 2001-09-06 : Added Full 2D Accel ! (see drivers in /sys/src/9/pc)
        - 2001-10-01 : Rid Fix.
        - 2006-04-01 : Add MGA550 support.
        - 2006-08-07 : Add support for 16 and 24bits modes.
                                HW accel now works for the G200 cards too (see kernel).
                                by Leonardo Valencia <leoval@anixcorp.com>

     Greets and Acknowledgements go to :
        - Sylvain Chipaux <a.k.a. asle>.
        - Nigel Roles.
        - Jean Mehat (the man who introduced me into the world of plan9).
        - Nicolas Stojanovic.
                ... and for those who wrote plan9 of course ... :)
*/      

#include <u.h>
#include <libc.h>
#include <bio.h>

#include "pci.h"
#include "vga.h"

static int       ultradebug = 0;

/*
 * Matrox G4xx 3D graphics accelerators
 */
enum {
        Kilo                    = 1024,
        Meg                     = 1024*1024,
        
        MATROX                  = 0x102B,       /* pci chip manufacturer */
        MGA550                  = 0x2527, /* pci chip device ids */
        MGA4XX                  = 0x0525,
        MGA200                  = 0x0521,

        /* Pci configuration space mapping */
        PCfgMgaFBAA             = 0x10,         /* Frame buffer Aperture Address */
        PCfgMgaCAA              = 0x14,         /* Control Aperture Address base */
        PCfgMgaIAA              = 0x18,         /* ILOAD Aperture base Address */
        PCfgMgaOption1          = 0x40,         /* Option Register 1 */
        PCfgMgaOption2          = 0x50,         /* Option Register 2 */
        PCfgMgaOption3          = 0x54,         /* Option Register 3 */
        PCfgMgaDevCtrl          = 0x04,         /* Device Control */

        /* control aperture offsets */
        DMAWIN                  = 0x0000,       /* 7KByte Pseudo-DMA Window */

        STATUS0                 = 0x1FC2,       /* Input Status 0 */
        STATUS1                 = 0x1FDA,       /* Input Status 1 */
        
        SEQIDX                  = 0x1FC4,       /* Sequencer Index */
        SEQDATA                 = 0x1FC5,       /* Sequencer Data */

        MISC_W                  = 0x1FC2,       /* Misc. WO */
        MISC_R                  = 0x1FCC,       /* Misc. RO */

        GCTLIDX                 = 0x1FCE,       /* Graphic Controler Index */
        GCTLDATA                = 0x1FCF,       /* Graphic Controler Data */

        CRTCIDX                 = 0x1FD4,       /* CRTC Index */
        CRTCDATA                = 0x1FD5,       /* CRTC Data */

        CRTCEXTIDX              = 0x1FDE,       /* CRTC Extension Index */
        CRTCEXTDATA             = 0x1FDF,       /* CRTC Extension Data */
        
        RAMDACIDX               = 0x3C00,       /* RAMDAC registers Index */
        RAMDACDATA              = 0x3C0A,       /* RAMDAC Indexed Data */
        RAMDACPALDATA           = 0x3C01,

        ATTRIDX                 = 0x1FC0,       /* Attribute Index */
        ATTRDATA                = 0x1FC1,       /* Attribute Data */

        CACHEFLUSH              = 0x1FFF,

        C2_CTL                  = 0X3C10,
        MGA_STATUS              = 0X1E14,
        Z_DEPTH_ORG             = 0X1C0C,
        
        /* ... */
        Seq_ClockingMode =      0x01,
                Dotmode =               (1<<0),
                Shftldrt =              (1<<2),
                Dotclkrt =              (1<<3),
                Shiftfour =             (1<<4),
                Scroff =                (1<<5),

        CrtcExt_Horizontcount = 0x01,
                Htotal =                (1<<0),
                Hblkstr =               (1<<1),
                Hsyncstr =              (1<<2),
                Hrsten =                (1<<3),
                Hsyncoff =              (1<<4),
                Vsyncoff =              (1<<5),
                Hblkend =               (1<<6),
                Vrsten =                (1<<7),

        CrtcExt_Miscellaneous = 0x03,
                Mgamode =               (1<<7),

        Dac_Xpixclkctrl =       0x1a,
                Pixclksl =              (3<<0),
                Pixclkdis =             (1<<2),
                Pixpllpdn =             (1<<3),

        Dac_Xpixpllstat =       0x4f,
                Pixlock =               (1<<6),
        
        Dac_Xpixpllan =         0x45,
        Dac_Xpixpllbn =         0x49,
        Dac_Xpixpllcn  =        0x4d,

        Dac_Xpixpllam =         0x44, 
        Dac_Xpixpllbm =         0x48,
        Dac_Xpixpllcm =         0x4c,

        Dac_Xpixpllap =         0x46,
        Dac_Xpixpllbp =         0x4a,
        Dac_Xpixpllcp =         0x4e,

        Dac_Xmulctrl =          0x19,
                ColorDepth =            (7<<0),
                        _8bitsPerPixel =                0,
                        _15bitsPerPixel =               1,
                        _16bitsPerPixel =               2,
                        _24bitsPerPixel =               3,
                        _32bitsPerPixelWithOv =         4,
                        _32bitsPerPixel =               7,

        Dac_Xpanelmode =        0x1f,

        Dac_Xmiscctrl =         0x1e,
                Dacpdn =                (1<<0),
                Mfcsel =                (3<<1),
                Vga8dac =               (1<<3),
                Ramcs =                 (1<<4),
                Vdoutsel =              (7<<5),

        Dac_Xcurctrl =          0x06,
                CursorDis =             0,
                Cursor3Color =          1,
                CursorXGA =             2,
                CursorX11 =             3,
                Cursor16Color =         4,

        Dac_Xzoomctrl =         0x38,

        Misc_loaddsel =                 (1<<0),
        Misc_rammapen =                 (1<<1),
        Misc_clksel =                   (3<<2),
        Misc_videodis =                 (1<<4),
        Misc_hpgoddev =                 (1<<5),
        Misc_hsyncpol =                 (1<<6),
        Misc_vsyncpol =                 (1<<7),

        MNP_TABLE_SIZE =        64,

        TRUE =  (1 == 1),
        FALSE = (1 == 0),
};

typedef struct {
        Pcidev* pci;
        int     devid;
        int     revid;
        
        uchar*  mmio;
        uchar*  mmfb;
        int     fbsize;
        ulong   iload;

        uchar   syspll_m;
        uchar   syspll_n;
        uchar   syspll_p;
        uchar   syspll_s;

        uchar   pixpll_m;
        uchar   pixpll_n;
        uchar   pixpll_p;
        uchar   pixpll_s;

        ulong   option1;
        ulong   option2;
        ulong   option3;

        ulong   Fneeded;

        /* From plan9.ini ... later */
        uchar   sdram;
        uchar   colorkey;
        uchar   maskkey;
        ulong   maxpclk;

        uchar   graphics[9];    
        uchar   attribute[0x14];        
        uchar   sequencer[5];
        uchar   crtc[0x19];
        uchar   crtcext[9];

        ulong   htotal;
        ulong   hdispend;
        ulong   hblkstr;
        ulong   hblkend;
        ulong   hsyncstr;
        ulong   hsyncend;
        ulong   vtotal;
        ulong   vdispend;
        ulong   vblkstr;
        ulong   vblkend;
        ulong   vsyncstr;
        ulong   vsyncend;
        ulong   linecomp;
        ulong   hsyncsel;
        ulong   startadd;
        ulong   offset;
        ulong   maxscan;
        ulong   curloc;
        ulong   prowscan;
        ulong   currowstr;
        ulong   currowend;
        ulong   curoff;
        ulong   undrow;
        ulong   curskew;
        ulong   conv2t4;
        ulong   interlace;
        ulong   hsyncdel;
        ulong   hdispskew;
        ulong   bytepan;
        ulong   dotclkrt;
        ulong   dword;
        ulong   wbmode;
        ulong   addwrap;
        ulong   selrowscan;
        ulong   cms;
        ulong   csynccen;
        ulong   hrsten;
        ulong   vrsten;
        ulong   vinten;
        ulong   vintclr;
        ulong   hsyncoff;
        ulong   vsyncoff;
        ulong   crtcrstN;
        ulong   mgamode;
        ulong   scale;
        ulong   hiprilvl;
        ulong   maxhipri;
        ulong   c2hiprilvl;
        ulong   c2maxhipri;
        ulong   misc;
        ulong   crtcprotect;
        ulong   winsize;
        ulong   winfreq;
        
        ulong   mgaapsize;
} Mga;

static void
mgawrite32(Mga* mga, int index, ulong val)
{
        ((ulong*)mga->mmio)[index] = val;
}

static ulong
mgaread32(Mga* mga, int index)
{
        return ((ulong*)mga->mmio)[index];
}

static void
mgawrite8(Mga* mga, int index, uchar val)
{
        mga->mmio[index] = val;
}

static uchar
mgaread8(Mga* mga, int index)
{
        return mga->mmio[index];
}

static uchar
seqget(Mga* mga, int index)
{
        mgawrite8(mga, SEQIDX, index);
        return mgaread8(mga, SEQDATA);
}

static uchar
seqset(Mga* mga, int index, uchar set, uchar clr)
{
        uchar   tmp;

        mgawrite8(mga, SEQIDX, index);
        tmp = mgaread8(mga, SEQDATA);
        mgawrite8(mga, SEQIDX, index);
        mgawrite8(mga, SEQDATA, (tmp & ~clr) | set);
        return tmp;
}

static uchar
crtcget(Mga* mga, int index)
{
        mgawrite8(mga, CRTCIDX, index);
        return mgaread8(mga, CRTCDATA);
}

static uchar
crtcset(Mga* mga, int index, uchar set, uchar clr)
{
        uchar   tmp;

        mgawrite8(mga, CRTCIDX, index);
        tmp = mgaread8(mga, CRTCDATA);
        mgawrite8(mga, CRTCIDX, index);
        mgawrite8(mga, CRTCDATA, (tmp & ~clr) | set);
        return tmp;
}

static uchar
crtcextget(Mga* mga, int index)
{
        mgawrite8(mga, CRTCEXTIDX, index);
        return mgaread8(mga, CRTCEXTDATA);
}

static uchar
crtcextset(Mga* mga, int index, uchar set, uchar clr)
{
        uchar   tmp;

        mgawrite8(mga, CRTCEXTIDX, index);
        tmp = mgaread8(mga, CRTCEXTDATA);
        mgawrite8(mga, CRTCEXTIDX, index);
        mgawrite8(mga, CRTCEXTDATA, (tmp & ~clr) | set);
        return tmp;
}

static uchar
dacget(Mga* mga, int index)
{
        mgawrite8(mga, RAMDACIDX, index);
        return mgaread8(mga, RAMDACDATA);
}

static uchar
dacset(Mga* mga, int index, uchar set, uchar clr)
{
        uchar   tmp;

        mgawrite8(mga, RAMDACIDX, index);
        tmp = mgaread8(mga, RAMDACDATA);
        mgawrite8(mga, RAMDACIDX, index);
        mgawrite8(mga, RAMDACDATA, (tmp & ~clr) | set);
        return  tmp;
}

static uchar
gctlget(Mga* mga, int index)
{
        mgawrite8(mga, GCTLIDX, index);
        return mgaread8(mga, GCTLDATA);
}

static uchar
gctlset(Mga* mga, int index, uchar set, uchar clr)
{
        uchar   tmp;

        mgawrite8(mga, GCTLIDX, index);
        tmp = mgaread8(mga, GCTLDATA);
        mgawrite8(mga, GCTLIDX, index);
        mgawrite8(mga, GCTLDATA, (tmp & ~clr) | set);
        return  tmp;
}

static uchar
attrget(Mga* mga, int index)
{
        mgawrite8(mga, ATTRIDX, index);
        return mgaread8(mga, ATTRDATA);
}

static uchar
attrset(Mga* mga, int index, uchar set, uchar clr)
{
        uchar   tmp;

        mgawrite8(mga, ATTRIDX, index);
        tmp = mgaread8(mga, ATTRDATA);
        mgawrite8(mga, ATTRIDX, index);
        mgawrite8(mga, ATTRDATA, (tmp & ~clr) | set);
        return  tmp;
}

static uchar
miscget(Mga* mga)
{
        return mgaread8(mga, MISC_R);
}

static uchar
miscset(Mga* mga, uchar set, uchar clr)
{
        uchar   tmp;

        tmp = mgaread8(mga, MISC_R);
        mgawrite8(mga, MISC_W, (tmp & ~clr) | set);
        return  tmp;
}

/* ************************************************************ */

static void
dump_all_regs(Mga* mga)
{
        int     i;

        for (i = 0; i < 25; i++)
                trace("crtc[%d] = 0x%x\n", i, crtcget(mga, i));
        for (i = 0; i < 9; i++)
                trace("crtcext[%d] = 0x%x\n", i, crtcextget(mga, i));
        for (i = 0; i < 5; i++)
                trace("seq[%d] = 0x%x\n", i, seqget(mga, i));
        for (i = 0; i < 9; i++)
                trace("gctl[%d] = 0x%x\n", i, gctlget(mga, i));
        trace("misc = 0x%x\n", mgaread8(mga, MISC_R));
        for (i = 0; i < 0x87; i++)
                trace("dac[%d] = 0x%x\n", i, dacget(mga, i));
}

/* ************************************************************ */

static void
dump(Vga* vga, Ctlr* ctlr)
{
        dump_all_regs(vga->private);
        ctlr->flag |= Fdump;
}

static void
setpalettedepth(int depth)
{
        int     fd;
        char *cmd = strdup("palettedepth X");

        if ((depth != 8) && (depth != 6) && (depth != 16))
                error("mga: invalid palette depth %d\n", depth);

        fd = open("#v/vgactl", OWRITE);
        if(fd < 0)
                error("mga: can't open vgactl\n");

        cmd[13] = '0' + depth;
        if(write(fd, cmd, 14) != 14)
                error("mga: can't set palette depth to %d\n", depth);

        close(fd);
}

static void
mapmga4xx(Vga* vga, Ctlr* ctlr)
{
        int     f;
        uchar*  m;
        Mga *   mga;

        if(vga->private == nil)
                error("%s: g4xxio: no *mga4xx\n", ctlr->name);
        mga = vga->private;

        f = open("#v/vgactl", OWRITE);
        if(f < 0)
                error("%s: can't open vgactl\n", ctlr->name);

        if(write(f, "type mga4xx", 11) != 11)
                error("%s: can't set mga type\n", ctlr->name);
        
        m = segattach(0, "mga4xxmmio", 0, 16*Kilo);
        if(m == (void*)-1)
                error("%s: can't attach mga4xxmmio segment\n", ctlr->name);
        mga->mmio = m;
        trace("%s: mmio at %#p\n", ctlr->name, mga->mmio);

        m = segattach(0, "mga4xxscreen", 0, 32*Meg);
        if(m == (void*)-1) {
                mga->mgaapsize = 8*Meg;
                m = segattach(0, "mga4xxscreen", 0, 8*Meg);
                if(m == (void*)-1)
                        error("%s: can't attach mga4xxscreen segment\n", ctlr->name);
        } else {
                mga->mgaapsize = 32*Meg;
        }
        mga->mmfb = m;
        trace("%s: frame buffer at %#p\n", ctlr->name, mga->mmfb);

        close(f);
}

static void
snarf(Vga* vga, Ctlr* ctlr)
{
        int     i, k, n;
        uchar * p;
        uchar   x[16];
        Pcidev *        pci;
        Mga *   mga;
        uchar   crtcext3;
        uchar   rid;

        trace("%s->snarf\n", ctlr->name);
        if(vga->private == nil) {
                pci = pcimatch(nil, MATROX, MGA4XX);
                if(pci == nil)
                        pci = pcimatch(nil, MATROX, MGA550);
                if(pci == nil)
                        pci = pcimatch(nil, MATROX, MGA200);
                if(pci == nil)
                        error("%s: cannot find matrox adapter\n", ctlr->name);

                rid = pcicfgr8(pci, PciRID); // PciRID = 0x08

                trace("%s: G%d%d0 rev %d\n", ctlr->name, 
                        2*(pci->did==MGA200)
                        +4*(pci->did==MGA4XX)
                        +5*(pci->did==MGA550),
                        rid&0x80 ? 5 : 0,
                        rid&~0x80);
                i = pcicfgr32(pci, PCfgMgaDevCtrl);
                if ((i & 2) != 2)
                        error("%s: Memory Space not enabled ... Aborting ...\n", ctlr->name);   

                vga->private = alloc(sizeof(Mga));
                mga = (Mga*)vga->private;
                mga->devid =    pci->did;
                mga->revid =    rid;    
                mga->pci =      pci;

                mapmga4xx(vga, ctlr);
        }
        else {
                mga = (Mga*)vga->private;
        }

        /* Find out how much memory is here, some multiple of 2Meg */

        /* First Set MGA Mode ... */
        crtcext3 = crtcextset(mga, 3, 0x80, 0x00);

        p = mga->mmfb;
        n = (mga->mgaapsize / Meg) / 2;
        for (i = 0; i < n; i++) {
                k = (2*i+1)*Meg;
                p[k] = 0;
                p[k] = i+1;
                *(mga->mmio + CACHEFLUSH) = 0;
                x[i] = p[k];
                trace("x[%d]=%d\n", i, x[i]);
        }
        for(i = 1; i < n; i++)
                if(x[i] != i+1)
                        break;
        vga->vmz = mga->fbsize = 2*i*Meg;
        trace("probe found %d megabytes\n", 2*i);

        crtcextset(mga, 3, crtcext3, 0xff);

        ctlr->flag |= Fsnarf;
}

static void
options(Vga* vga, Ctlr* ctlr)
{
        if(vga->virtx & 127)
                vga->virtx = (vga->virtx+127)&~127;
        ctlr->flag |= Foptions;
}

/* ************************************************************ */

static void 
G450ApplyPFactor(Mga*, uchar ucP, ulong *pulFIn)
{
        if(!(ucP & 0x40))
        {
                *pulFIn = *pulFIn / (2L << (ucP & 3));
        }
}


static void 
G450RemovePFactor(Mga*, uchar ucP, ulong *pulFIn)
{
        if(!(ucP & 0x40))
        {
                *pulFIn = *pulFIn * (2L << (ucP & 3));
        }
}

static void 
G450CalculVCO(Mga*, ulong ulMNP, ulong *pulF)
{
        uchar ucM, ucN;

        ucM = (uchar)((ulMNP >> 16) & 0xff);
        ucN = (uchar)((ulMNP >>  8) & 0xff);

        *pulF = (27000 * (2 * (ucN + 2)) + ((ucM + 1) >> 1)) / (ucM + 1);
        trace("G450CalculVCO: ulMNP %lx, pulF %ld\n", ulMNP, *pulF);
}


static void 
G450CalculDeltaFreq(Mga*, ulong ulF1, ulong ulF2, ulong *pulDelta)
{
        if(ulF2 < ulF1)
        {
                *pulDelta = ((ulF1 - ulF2) * 1000) / ulF1;
        }
        else
        {
                *pulDelta = ((ulF2 - ulF1) * 1000) / ulF1;
        }
        trace("G450CalculDeltaFreq: ulF1 %ld, ulF2 %ld, pulDelta %ld\n", ulF1, ulF2, *pulDelta);
}

static void 
G450FindNextPLLParam(Mga* mga, ulong ulFout, ulong *pulPLLMNP)
{
        uchar ucM, ucN, ucP, ucS;
        ulong ulVCO, ulVCOMin;

        ucM = (uchar)((*pulPLLMNP >> 16) & 0xff);
        /* ucN = (uchar)((*pulPLLMNP >>  8) & 0xff); */
        ucP = (uchar)(*pulPLLMNP &  0x43);

        ulVCOMin = 256000;

        if(ulVCOMin >= (255L * 8000))
        {
                ulVCOMin = 230000;
        }

        if((ucM == 9) && (ucP & 0x40))
        {
                *pulPLLMNP = 0xffffffff;
        } else if (ucM == 9)
        {
                if(ucP)
                {
                        ucP--;
                }
                else
                {
                        ucP = 0x40;
                }
                ucM = 0;
        }
        else
        {
                ucM++;
        }

        ulVCO = ulFout;

        G450RemovePFactor(mga, ucP, &ulVCO);

        if(ulVCO < ulVCOMin)
        {
                *pulPLLMNP = 0xffffffff;
        }

        if(*pulPLLMNP != 0xffffffff)
        {
                ucN = (uchar)(((ulVCO * (ucM+1) + 27000)/(27000 * 2)) - 2);

                ucS = 5;
                if(ulVCO < 1300000) ucS = 4;
                if(ulVCO < 1100000) ucS = 3;
                if(ulVCO <  900000) ucS = 2;
                if(ulVCO <  700000) ucS = 1;
                if(ulVCO <  550000) ucS = 0;

                ucP |= (uchar)(ucS << 3);

                *pulPLLMNP &= 0xff000000;
                *pulPLLMNP |= (ulong)ucM << 16;
                *pulPLLMNP |= (ulong)ucN << 8;
                *pulPLLMNP |= (ulong)ucP;
        }
}

static void 
G450FindFirstPLLParam(Mga* mga, ulong ulFout, ulong *pulPLLMNP)
{
        uchar ucP;
        ulong ulVCO;
        ulong ulVCOMax;

        /* Default value */
        ulVCOMax = 1300000;

        if(ulFout > (ulVCOMax/2))
        {
                ucP = 0x40;
                ulVCO = ulFout;
        }
        else
        {
                ucP = 3;
                ulVCO = ulFout;
                G450RemovePFactor(mga, ucP, &ulVCO);
                while(ucP && (ulVCO > ulVCOMax))
                {
                        ucP--;
                        ulVCO = ulFout;
                        G450RemovePFactor(mga, ucP, &ulVCO);
                }
        }

        if(ulVCO > ulVCOMax)
        {
                *pulPLLMNP = 0xffffffff;
        }
        else
        {
                /* Pixel clock: 1 */
                *pulPLLMNP = (1 << 24) + 0xff0000 + ucP;
                G450FindNextPLLParam(mga, ulFout, pulPLLMNP);
        }
}


static void 
G450WriteMNP(Mga* mga, ulong ulMNP)
{
        if (0) trace("G450WriteMNP : 0x%lx\n", ulMNP);
        dacset(mga, Dac_Xpixpllcm, (uchar)(ulMNP >> 16), 0xff);
        dacset(mga, Dac_Xpixpllcn, (uchar)(ulMNP >>  8), 0xff);   
        dacset(mga, Dac_Xpixpllcp, (uchar)ulMNP, 0xff);   
}


static void 
G450CompareMNP(Mga* mga, ulong ulFout, ulong ulMNP1,
                            ulong ulMNP2, long *pulResult)
{
        ulong ulFreq, ulDelta1, ulDelta2;

        G450CalculVCO(mga, ulMNP1, &ulFreq);
        G450ApplyPFactor(mga, (uchar) ulMNP1, &ulFreq);
        G450CalculDeltaFreq(mga, ulFout, ulFreq, &ulDelta1);

        G450CalculVCO(mga, ulMNP2, &ulFreq);
        G450ApplyPFactor(mga, (uchar) ulMNP2, &ulFreq);
        G450CalculDeltaFreq(mga, ulFout, ulFreq, &ulDelta2);

        if(ulDelta1 < ulDelta2)
        {
                *pulResult = -1;
        }
        else if(ulDelta1 > ulDelta2)
        {
                *pulResult = 1;
        }
        else
        {
                *pulResult = 0;
        }

        if((ulDelta1 <= 5) && (ulDelta2 <= 5))
        {
                if((ulMNP1 & 0xff0000) < (ulMNP2 & 0xff0000))
                {
                        *pulResult = -1;
                }
                else if((ulMNP1 & 0xff0000) > (ulMNP2 & 0xff0000))
                {
                        *pulResult = 1;
                }
        }
}


static void 
G450IsPllLocked(Mga* mga, int *lpbLocked)
{
        ulong ulFallBackCounter, ulLockCount, ulCount;
        uchar  ucPLLStatus;

        /* Pixel PLL */
        mgawrite8(mga, 0x3c00, 0x4f);    
        ulFallBackCounter = 0;

        do 
        {
                ucPLLStatus = mgaread8(mga, 0x3c0a);
                if (0) trace("ucPLLStatus[1] : 0x%x\n", ucPLLStatus);
                ulFallBackCounter++;
        } while(!(ucPLLStatus & 0x40) && (ulFallBackCounter < 1000));

        ulLockCount = 0;
        if(ulFallBackCounter < 1000)
        {
                for(ulCount = 0; ulCount < 100; ulCount++)
                {
                        ucPLLStatus = mgaread8(mga, 0x3c0a);
                        if (0) trace("ucPLLStatus[2] : 0x%x\n", ucPLLStatus);
                        if(ucPLLStatus & 0x40)
                        {
                                ulLockCount++;
                        }
                }
        }

        *lpbLocked = ulLockCount >= 90;
}

static void 
G450SetPLLFreq(Mga* mga, long f_out) 
{
        int bFoundValidPLL;
        int bLocked;
        ulong ulMaxIndex;
        ulong ulMNP;
        ulong ulMNPTable[MNP_TABLE_SIZE];
        ulong ulIndex;
        ulong ulTryMNP;
        long lCompareResult;

        trace("f_out : %ld\n", f_out);

        G450FindFirstPLLParam(mga, f_out, &ulMNP);
        ulMNPTable[0] = ulMNP;
        G450FindNextPLLParam(mga, f_out, &ulMNP);
        ulMaxIndex = 1;
        while(ulMNP != 0xffffffff)
        {
                int ulIndex;
                int bSkipValue;

                bSkipValue = FALSE;
                if(ulMaxIndex == MNP_TABLE_SIZE)
                {
                        G450CompareMNP(mga, f_out, ulMNP, ulMNPTable[MNP_TABLE_SIZE - 1],
                                       &lCompareResult);

                        if(lCompareResult > 0)
                        {
                                bSkipValue = TRUE;
                        }
                        else
                        {
                                ulMaxIndex--;
                        }
                }

                if(!bSkipValue)
                {
                        for(ulIndex = ulMaxIndex; !bSkipValue && (ulIndex > 0); ulIndex--)
                        {
                                G450CompareMNP(mga, f_out, ulMNP, ulMNPTable[ulIndex - 1],
                                               &lCompareResult);

                                if(lCompareResult < 0)
                                {
                                        ulMNPTable[ulIndex] = ulMNPTable[ulIndex - 1];
                                }
                                else
                                {
                                        break;
                                }
                        }
                        ulMNPTable[ulIndex] = ulMNP;
                        ulMaxIndex++;
                }

                G450FindNextPLLParam(mga, f_out, &ulMNP);
        }

        bFoundValidPLL = FALSE;
        ulMNP = 0;

        for(ulIndex = 0; !bFoundValidPLL && (ulIndex < ulMaxIndex); ulIndex++)
        {
                ulTryMNP = ulMNPTable[ulIndex];

                {
                        bLocked = TRUE;
                        if((ulMNPTable[ulIndex] & 0xff00) < 0x300 ||
                           (ulMNPTable[ulIndex] & 0xff00) > 0x7a00)
                        {
                                bLocked = FALSE;
                        }

                        if(bLocked)
                        {
                                G450WriteMNP(mga, ulTryMNP - 0x300);
                                G450IsPllLocked(mga, &bLocked);
                        }     

                        if(bLocked)
                        {
                                G450WriteMNP(mga, ulTryMNP + 0x300);
                                G450IsPllLocked(mga, &bLocked);
                        }     

                        if(bLocked)
                        {
                                G450WriteMNP(mga, ulTryMNP - 0x200);
                                G450IsPllLocked(mga, &bLocked);
                        }     

                        if(bLocked)
                        {
                                G450WriteMNP(mga, ulTryMNP + 0x200);
                                G450IsPllLocked(mga, &bLocked);
                        }     

                        if(bLocked)
                        {
                                G450WriteMNP(mga, ulTryMNP - 0x100);
                                G450IsPllLocked(mga, &bLocked);
                        }     

                        if(bLocked)
                        {
                                G450WriteMNP(mga, ulTryMNP + 0x100);
                                G450IsPllLocked(mga, &bLocked);
                        }     

                        if(bLocked)
                        {
                                G450WriteMNP(mga, ulTryMNP);
                                G450IsPllLocked(mga, &bLocked);
                        }     
                        else if(!ulMNP)
                        {
                                G450WriteMNP(mga, ulTryMNP);
                                G450IsPllLocked(mga, &bLocked);
                                if(bLocked)
                                {
                                        ulMNP = ulMNPTable[ulIndex]; 
                                }
                                bLocked = FALSE;
                        }

                        if(bLocked)
                        {
                                bFoundValidPLL = TRUE;
                        }
                }
        }

        if(!bFoundValidPLL)
        {
                if(ulMNP)
                {
                        G450WriteMNP(mga, ulMNP);
                }
                else
                {
                        G450WriteMNP(mga, ulMNPTable[0]);
                }
        }
}


/* ************************************************************ */

/*
        calcclock - Calculate the PLL settings (m, n, p, s).
*/
static double
g400_calcclock(Mga* mga, long Fneeded)
{
        double  Fpll;
        double  Fvco;
        double  Fref;
        int             pixpll_m_min;
        int             pixpll_m_max;
        int             pixpll_n_min;
        int             pixpll_n_max;
        int             pixpll_p_max;
        double  Ferr, Fcalc;
        int             m, n, p;
        
        if (mga->devid == MGA4XX || mga->devid == MGA550) {
                /* These values are taken from Matrox G400 Specification - p 4-91 */
                Fref                    = 27000000.0;
                pixpll_n_min    = 7;
                pixpll_n_max    = 127;
                pixpll_m_min    = 1;
                pixpll_m_max    = 31;
                pixpll_p_max    = 7;
        } else {                                /* MGA200 */
                /* These values are taken from Matrox G200 Specification - p 4-77 */
                //Fref                  = 14318180.0;
                Fref                    = 27050500.0;
                pixpll_n_min    = 7;
                pixpll_n_max    = 127;
                pixpll_m_min    = 1;
                pixpll_m_max    = 6;
                pixpll_p_max    = 7;
        }

        Fvco = ( double ) Fneeded;
        for (p = 0;  p <= pixpll_p_max && Fvco < mga->maxpclk; p = p * 2 + 1, Fvco *= 2.0)
                ;
        mga->pixpll_p = p;

        Ferr = Fneeded;
        for ( m = pixpll_m_min ; m <= pixpll_m_max ; m++ )
                for ( n = pixpll_n_min; n <= pixpll_n_max; n++ )
                { 
                        Fcalc = Fref * (n + 1) / (m + 1) ;

                        /*
                         * Pick the closest frequency.
                         */
                        if ( labs(Fcalc - Fvco) < Ferr ) {
                                Ferr = abs(Fcalc - Fvco);
                                mga->pixpll_m = m;
                                mga->pixpll_n = n;
                        }
                }
        
        Fvco = Fref * (mga->pixpll_n + 1) / (mga->pixpll_m + 1);

        if (mga->devid == MGA4XX || mga->devid == MGA550) {
                if ( (50000000.0 <= Fvco) && (Fvco < 110000000.0) )
                        mga->pixpll_p |= 0;     
                if ( (110000000.0 <= Fvco) && (Fvco < 170000000.0) )
                        mga->pixpll_p |= (1<<3);        
                if ( (170000000.0 <= Fvco) && (Fvco < 240000000.0) )
                        mga->pixpll_p |= (2<<3);        
                if ( (300000000.0 <= Fvco) )
                        mga->pixpll_p |= (3<<3);        
        } else {
                if ( (50000000.0 <= Fvco) && (Fvco < 100000000.0) )
                        mga->pixpll_p |= 0;     
                if ( (100000000.0 <= Fvco) && (Fvco < 140000000.0) )
                        mga->pixpll_p |= (1<<3);        
                if ( (140000000.0 <= Fvco) && (Fvco < 180000000.0) )
                        mga->pixpll_p |= (2<<3);        
                if ( (250000000.0 <= Fvco) )
                        mga->pixpll_p |= (3<<3);        
        }
        
        Fpll = Fvco / (p + 1);

        return Fpll;
}

/* ************************************************************ */

static void
init(Vga* vga, Ctlr* ctlr)
{
        Mode*   mode;
        Mga*    mga;
        double  Fpll;
        Ctlr*   c;
        int     i;
        ulong   t;
        int     bppShift;

        mga = vga->private;
        mode = vga->mode;

        trace("mga mmio at %#p\n", mga->mmio);

        ctlr->flag |= Ulinear;

        /*
         * Set the right bppShift based on depth
         */

        switch(mode->z) {
        case 8: 
                bppShift = 0;
                break;
        case 16:
                bppShift = 1;
                break;
        case 24:
                bppShift = 0;
                break;
        case 32:
                bppShift = 2;
                break;
        default:
                bppShift = 0;
                error("depth %d not supported !\n", mode->z);
        }

        if (mode->interlace)
                error("interlaced mode not supported !\n");

        trace("%s: Initializing mode %dx%dx%d on %s\n", ctlr->name, mode->x, mode->y, mode->z, mode->type);
        trace("%s: Suggested Dot Clock : %d\n",         ctlr->name, mode->frequency);
        trace("%s: Horizontal Total = %d\n",            ctlr->name, mode->ht);
        trace("%s: Start Horizontal Blank = %d\n", ctlr->name, mode->shb);
        trace("%s: End Horizontal Blank = %d\n",        ctlr->name, mode->ehb);
        trace("%s: Vertical Total = %d\n",              ctlr->name, mode->vt);
        trace("%s: Vertical Retrace Start = %d\n",      ctlr->name, mode->vrs);
        trace("%s: Vertical Retrace End = %d\n",        ctlr->name, mode->vre);
        trace("%s: Start Horizontal Sync = %d\n",       ctlr->name, mode->shs);
        trace("%s: End Horizontal Sync = %d\n",         ctlr->name, mode->ehs);
        trace("%s: HSync = %c\n",                       ctlr->name, mode->hsync);
        trace("%s: VSync = %c\n",                               ctlr->name, mode->vsync);
        trace("%s: Interlace = %d\n",                   ctlr->name, mode->interlace);

        /* TODO : G400 Max : 360000000 */
        if (mga->devid == MGA4XX || mga->devid == MGA550)
                mga->maxpclk    = 300000000;
        else
                mga->maxpclk    = 250000000;

        if (mode->frequency < 50000)
                error("mga: Too little Frequency %d : Minimum supported by PLL is %d\n", 
                        mode->frequency, 50000);

        if (mode->frequency > mga->maxpclk)
                error("mga: Too big Frequency %d : Maximum supported by PLL is %ld\n",
                        mode->frequency, mga->maxpclk);
        
        trace("mga: revision ID is %x\n", mga->revid);
        if ((mga->devid == MGA200) || ((mga->devid == MGA4XX) && (mga->revid & 0x80) == 0x00)) {
                /* Is it G200/G400 or G450 ? */
                Fpll = g400_calcclock(mga, mode->frequency);
                trace("Fpll set to %f\n", Fpll);
                trace("pixclks : n = %d m = %d p = %d\n", mga->pixpll_n, mga->pixpll_m, mga->pixpll_p & 0x7);
        } else
                mga->Fneeded = mode->frequency;

        trace("PCI Option1 = 0x%x\n", pcicfgr32(mga->pci, PCfgMgaOption1));
        trace("PCI Option2 = 0x%x\n", pcicfgr32(mga->pci, PCfgMgaOption2));
        trace("PCI Option3 = 0x%x\n", pcicfgr32(mga->pci, PCfgMgaOption3));

        mga->htotal =                   (mode->ht >> 3) - 5;
        mga->hdispend =         (mode->x >> 3) - 1;
        mga->hblkstr =          mga->hdispend;          /* (mode->shb >> 3); */
        mga->hblkend =          mga->htotal + 4;                /* (mode->ehb >> 3); */
        mga->hsyncstr =         (mode->shs >> 3) - 1;   // Was (mode->shs >> 3);
        mga->hsyncend =         (mode->ehs >> 3) - 1;   // Was (mode->ehs >> 3);
        mga->hsyncdel =                 0;
        mga->vtotal =                   mode->vt - 2;
        mga->vdispend =                 mode->y - 1;
        mga->vblkstr =          mode->y - 1;
        mga->vblkend =          mode->vt - 1;
        mga->vsyncstr =                 mode->vrs;
        mga->vsyncend =                 mode->vre;
        mga->linecomp =         mode->y;
        mga->hsyncsel =                 0;                                      /* Do not double lines ... */
        mga->startadd =         0;
        mga->offset =           (mode->z==24) ? (vga->virtx * 3) >> (4 - bppShift) : vga->virtx >> (4-bppShift);
        /* No Zoom */
        mga->maxscan =          0;
        /* Not used in Power Graphic mode */
        mga->curloc =                   0;
        mga->prowscan =                 0;
        mga->currowstr =                0;
        mga->currowend =                0;
        mga->curoff =           1;
        mga->undrow =           0;
        mga->curskew =          0;
        mga->conv2t4 =          0;
        mga->interlace =                0;
        mga->hdispskew =                0;
        mga->bytepan =          0;
        mga->dotclkrt =                 0;
        mga->dword =                    0;
        mga->wbmode =           1;
        mga->addwrap =          0;      /* Not Used ! */
        mga->selrowscan =               1;
        mga->cms =                      1;
        mga->csynccen =         0;      /* Disable composite sync */

        /* VIDRST Pin */
        mga->hrsten =                   0;      // Was 1;
        mga->vrsten =                   0;      // Was 1;

        /* vertical interrupt control ... disabled */
        mga->vinten =           1;
        mga->vintclr =          0;

        /* Let [hv]sync run freely */
        mga->hsyncoff =         0;
        mga->vsyncoff =         0;

        mga->crtcrstN =         1;

        mga->mgamode =          1;
        mga->scale   =          (mode->z == 24) ? ((1 << bppShift)*3)-1 : (1 << bppShift)-1;
        
        mga->crtcprotect =      1;
        mga->winsize =          0;
        mga->winfreq =          0;

        if ((mga->htotal == 0)
            || (mga->hblkend <= (mga->hblkstr + 1))
            || ((mga->htotal - mga->hdispend) == 0)
            || ((mga->htotal - mga->bytepan + 2) <= mga->hdispend)
            || (mga->hsyncstr <= (mga->hdispend + 2))
            || (mga->vtotal == 0))
        {
                error("Invalid Power Graphic Mode :\n"
                      "mga->htotal = %ld\n"
                      "mga->hdispend = %ld\n"
                      "mga->hblkstr = %ld\n"
                      "mga->hblkend = %ld\n"
                      "mga->hsyncstr = %ld\n"
                      "mga->hsyncend = %ld\n"
                      "mga->hsyncdel = %ld\n"
                      "mga->vtotal = %ld\n"
                      "mga->vdispend = %ld\n"
                      "mga->vblkstr = %ld\n"
                      "mga->vblkend = %ld\n"
                      "mga->vsyncstr = %ld\n"
                      "mga->vsyncend = %ld\n"
                      "mga->linecomp = %ld\n",
                      mga->htotal,
                      mga->hdispend,
                      mga->hblkstr,
                      mga->hblkend,
                      mga->hsyncstr,
                      mga->hsyncend,
                      mga->hsyncdel,
                      mga->vtotal,
                      mga->vdispend,
                      mga->vblkstr,
                      mga->vblkend,
                      mga->vsyncstr,
                      mga->vsyncend,
                      mga->linecomp
                      );
        }

        mga->hiprilvl = 0;
        mga->maxhipri = 0;
        mga->c2hiprilvl = 0;
        mga->c2maxhipri = 0;

        mga->misc = ((mode->hsync != '-')?0:(1<<6)) | ((mode->vsync != '-')?0:(1<<7));

        trace("mga->htotal = %ld\n"
                      "mga->hdispend = %ld\n"
                      "mga->hblkstr = %ld\n"
                      "mga->hblkend = %ld\n"
                      "mga->hsyncstr = %ld\n"
                      "mga->hsyncend = %ld\n"
                      "mga->hsyncdel = %ld\n"
                      "mga->vtotal = %ld\n"
                      "mga->vdispend = %ld\n"
                      "mga->vblkstr = %ld\n"
                      "mga->vblkend = %ld\n"
                      "mga->vsyncstr = %ld\n"
                      "mga->vsyncend = %ld\n"
                      "mga->linecomp = %ld\n",
                      mga->htotal,
                      mga->hdispend,
                      mga->hblkstr,
                      mga->hblkend,
                      mga->hsyncstr,
                      mga->hsyncend,
                      mga->hsyncdel,
                      mga->vtotal,
                      mga->vdispend,
                      mga->vblkstr,
                      mga->vblkend,
                      mga->vsyncstr,
                      mga->vsyncend,
                      mga->linecomp
                      );        

        mga->crtc[0x00] = 0xff & mga->htotal;

        mga->crtc[0x01] = 0xff & mga->hdispend;

        mga->crtc[0x02] = 0xff & mga->hblkstr;

        mga->crtc[0x03] = (0x1f & mga->hblkend) 
                | ((0x03 & mga->hdispskew) << 5)
                | 0x80  /* cf 3-304 */
                ;

        mga->crtc[0x04] = 0xff & mga->hsyncstr;

        mga->crtc[0x05] = (0x1f & mga->hsyncend) 
                | ((0x03 & mga->hsyncdel) << 5) 
                | ((0x01 & (mga->hblkend >> 5)) << 7)
                ;

        mga->crtc[0x06] = 0xff & mga->vtotal;

        t = ((0x01 & (mga->vtotal >> 8)) << 0)
          | ((0x01 & (mga->vdispend >> 8)) << 1)
          | ((0x01 & (mga->vsyncstr >> 8)) << 2)
          | ((0x01 & (mga->vblkstr >> 8)) << 3)
          | ((0x01 & (mga->linecomp >> 8)) << 4)
          | ((0x01 & (mga->vtotal >> 9)) << 5)
          | ((0x01 & (mga->vdispend >> 9)) << 6)
          | ((0x01 & (mga->vsyncstr >> 9)) << 7)
          ;
        mga->crtc[0x07] = 0xff & t;

        mga->crtc[0x08] = (0x1f & mga->prowscan) 
                | ((0x03 & mga->bytepan) << 5)
                ;

        mga->crtc[0x09] = (0x1f & mga->maxscan) 
                | ((0x01 & (mga->vblkstr >> 9)) << 5)
                | ((0x01 & (mga->linecomp >> 9)) << 6)
                | ((0x01 & mga->conv2t4) << 7)
                ;

        mga->crtc[0x0a] = (0x1f & mga->currowstr)
                | ((0x01 & mga->curoff) << 5)
                ;

        mga->crtc[0x0b] = (0x1f & mga->currowend)
                | ((0x03 & mga->curskew) << 5)
                ;

        mga->crtc[0x0c] = 0xff & (mga->startadd >> 8);

        mga->crtc[0x0d] = 0xff & mga->startadd;

        mga->crtc[0x0e] = 0xff & (mga->curloc >> 8);

        mga->crtc[0x0f] = 0xff & mga->curloc;

        mga->crtc[0x10] = 0xff & mga->vsyncstr;

        mga->crtc[0x11] = (0x0f & mga->vsyncend)
                | ((0x01 & mga->vintclr) << 4)
                | ((0x01 & mga->vinten) << 5)
                | ((0x01 & mga->crtcprotect) << 7)
                ;

        mga->crtc[0x12] = 0xff & mga->vdispend;

        mga->crtc[0x13] = 0xff & mga->offset;

        mga->crtc[0x14] = 0x1f & mga->undrow;   /* vga only */

        mga->crtc[0x15] = 0xff & mga->vblkstr;

        mga->crtc[0x16] = 0xff & mga->vblkend;

        mga->crtc[0x17] = ((0x01 & mga->cms) << 0)
                | ((0x01 & mga->selrowscan) << 1)
                | ((0x01 & mga->hsyncsel) << 2)
                | ((0x01 & mga->addwrap) << 5)
                | ((0x01 & mga->wbmode) << 6)
                | ((0x01 & mga->crtcrstN) << 7)
                ;

        mga->crtc[0x18] = 0xff & mga->linecomp;
        
        mga->crtcext[0] = (0x0f & (mga->startadd >> 16))
                | ((0x03 & (mga->offset >> 8)) << 4)
                | ((0x01 & (mga->startadd >> 20)) << 6)
                | ((0x01 & mga->interlace) << 7)
                ;

        mga->crtcext[1] = ((0x01 & (mga->htotal >> 8)) << 0)
                | ((0x01 & (mga->hblkstr >> 8)) << 1)
                | ((0x01 & (mga->hsyncstr >> 8)) << 2)
                | ((0x01 & mga->hrsten) << 3)
                | ((0x01 & mga->hsyncoff) << 4)
                | ((0x01 & mga->vsyncoff) << 5)
                | ((0x01 & (mga->hblkend >> 6)) << 6)
                | ((0x01 & mga->vrsten) << 7)
                ;

        mga->crtcext[2] = ((0x03 & (mga->vtotal >> 10)) << 0)
                | ((0x01 & (mga->vdispend >> 10)) << 2)
                | ((0x03 & (mga->vblkstr >> 10)) << 3)
                | ((0x03 & (mga->vsyncstr >> 10)) << 5)
                | ((0x01 & (mga->linecomp >> 10)) << 7)
                ;

        mga->crtcext[3] = ((0x07 & mga->scale) << 0)
                | ((0x01 & mga->csynccen) << 6)
                | ((0x01 & mga->mgamode) << 7)
                ;

        mga->crtcext[4] = 0;    /* memory page ... not used in Power Graphic Mode */

        mga->crtcext[5] = 0;    /* Not used in non-interlaced mode */

        mga->crtcext[6] = ((0x07 & mga->hiprilvl) << 0)
                | ((0x07 & mga->maxhipri) << 4)
                ;

        mga->crtcext[7] = ((0x07 & mga->winsize) << 1)
                | ((0x07 & mga->winfreq) << 5)
                ;

        mga->crtcext[8] = (0x01 & (mga->startadd >> 21)) << 0;

        /* Initialize Sequencer */
        mga->sequencer[0] = 0;
        mga->sequencer[1] = 0;
        mga->sequencer[2] = 0x03;
        mga->sequencer[3] = 0;
        mga->sequencer[4] = 0x02;

        /* Graphic Control registers are ignored when not using 0xA0000 aperture */     
        for (i = 0; i < 9; i++)
                mga->graphics[i] = 0;   

        /* The Attribute Controler is not available in Power Graphics mode */
        for (i = 0; i < 0x15; i++)
                mga->attribute[i] = i;  

        /* disable vga load (want to do fields in different order) */
        for(c = vga->link; c; c = c->link)
                if (strncmp(c->name, "vga", 3) == 0)
                        c->load = nil;
}

static void
load(Vga* vga, Ctlr* ctlr)
{
        Mga*    mga;
        int     i;
        uchar*  p;
        Mode*   mode;
        uchar   cursor;

        mga = vga->private;
        mode = vga->mode;

        trace("mga: Loading ...\n");
        dump_all_regs(mga);

        if (mode->z == 8)
                setpalettedepth(mode->z);

        trace("mga mmio at %#p\n", mga->mmio);
        trace("mga: loading vga registers ...\n" );
        if (ultradebug) Bflush(&stdout);

        /* Initialize Sequencer registers */
        for(i = 0; i < 5; i++)
                seqset(mga, i, mga->sequencer[i], 0xff);

        /* Initialize Attribute register */
        for(i = 0; i < 0x15; i++)
                attrset(mga, i, mga->attribute[i], 0xff);

        /* Initialize Graphic Control registers */
        for(i = 0; i < 9; i++)
                gctlset(mga, i, mga->graphics[i], 0xff);

        /* Wait VSYNC */
        while (mgaread8(mga, STATUS1) & 0x08);
        while (! (mgaread8(mga, STATUS1) & ~0x08));

        /* Turn off the video. */
        seqset(mga, Seq_ClockingMode, Scroff, 0);

        /* Crtc2 Off */
        mgawrite32(mga, C2_CTL, 0);

        /* Disable Cursor */
        cursor = dacset(mga, Dac_Xcurctrl, CursorDis, 0xff);

        /* Pixel Pll UP and set Pixel clock source to Pixel Clock PLL */
        dacset(mga, Dac_Xpixclkctrl, 0x01 | 0x08, 0x0f);

        trace("mga: waiting for the clock source becomes stable ...\n");
        while ((dacget(mga, Dac_Xpixpllstat) & Pixlock) != Pixlock)
                ;
        trace("mga: pixpll locked !\n");
        if (ultradebug) Bflush(&stdout);

        /* Enable LUT, Disable MAFC */
        dacset(mga, Dac_Xmiscctrl, Ramcs | Mfcsel, Vdoutsel);

        /* Disable Dac */
        dacset(mga, Dac_Xmiscctrl, 0, Dacpdn);

        /* Initialize Panel Mode */
        dacset(mga, Dac_Xpanelmode, 0, 0xff);

        /* Disable the PIXCLK and set Pixel clock source to Pixel Clock PLL */
        dacset(mga, Dac_Xpixclkctrl, Pixclkdis | 0x01, 0x3);

        /* Disable mapping of the memory */ 
        miscset(mga, 0, Misc_rammapen);

        /* Enable 8 bit palette */
        dacset(mga, Dac_Xmiscctrl, Vga8dac, 0);

        /* Select MGA Pixel Clock */
        miscset(mga, Misc_clksel, 0);

        /* Initialize Z Buffer ... (useful?) */
        mgawrite32(mga, Z_DEPTH_ORG, 0);

        /* Wait */
        for (i = 0; i < 50; i++)
                mgaread32(mga, MGA_STATUS);

        if ((mga->devid == MGA200) || ((mga->devid == MGA4XX) && (mga->revid & 0x80) == 0x00)) { 
                dacset(mga, Dac_Xpixpllcm, mga->pixpll_m, 0xff);
                dacset(mga, Dac_Xpixpllcn, mga->pixpll_n, 0xff);
                dacset(mga, Dac_Xpixpllcp, mga->pixpll_p, 0xff);

                /* Wait until new clock becomes stable */
                trace("mga: waiting for the clock source becomes stable ...\n");
                while ((dacget(mga, Dac_Xpixpllstat) & Pixlock) != Pixlock)
                        ;
                trace("mga: pixpll locked !\n");
        } else {
        /* MGA450 and MGA550 */
                /* Wait until new clock becomes stable */
                trace("mga450: waiting for the clock source becomes stable ...\n");
                while ((dacget(mga, Dac_Xpixpllstat) & Pixlock) != Pixlock)
                        ;
                trace("mga: pixpll locked !\n");

                G450SetPLLFreq(mga, (long) mga->Fneeded / 1000);
        }

        /* Enable Pixel Clock Oscillation */
        dacset(mga, Dac_Xpixclkctrl, 0, Pixclkdis);
        if (ultradebug) Bflush(&stdout);

        /* Enable Dac */
        dacset(mga, Dac_Xmiscctrl, Dacpdn, 0);

        /* Set Video Mode */
        switch (mode->z) {
        case 8:
                dacset(mga, Dac_Xmulctrl, _8bitsPerPixel, ColorDepth);  
                break;
        case 16:
                dacset(mga, Dac_Xmulctrl, _16bitsPerPixel, ColorDepth); 
                break;
        case 24:
                dacset(mga, Dac_Xmulctrl, _24bitsPerPixel, ColorDepth); 
                break;
        case 32:
                dacset(mga, Dac_Xmulctrl, _32bitsPerPixel, ColorDepth);
                break;
        default: 
                error("Unsupported depth %d\n", mode->z);
        }

        /* Wait */
        for (i = 0; i < 50; i++)
                mgaread32(mga, MGA_STATUS);

        /* Wait until new clock becomes stable */
        trace("mga: waiting for the clock source becomes stable ...\n");
        if (ultradebug) Bflush(&stdout);
        while ((dacget(mga, Dac_Xpixpllstat) & Pixlock) != Pixlock)
                ;
        trace("mga: pixpll locked !\n");
        if (ultradebug) Bflush(&stdout);

        /* Initialize CRTC registers and remove irq */
        crtcset(mga, 0x11, (1<<4), (1<<5)|0x80);
        for (i = 0; i < 25; i++)
                crtcset(mga, i, mga->crtc[i], 0xff);

        trace("mga: crtc loaded !\n");
        if (ultradebug) Bflush(&stdout);

        /* Initialize CRTC Extension registers */
        for (i = 0; i < 9; i++)
                crtcextset(mga, i, mga->crtcext[i], 0xff);

        trace("mga: ext loaded !\n");
        if (ultradebug) Bflush(&stdout);

        /* Disable Zoom */
        dacset(mga, Dac_Xzoomctrl, 0, 0xff);

        trace("mga: XzoomCtrl Loaded !\n");
        if (ultradebug) Bflush(&stdout);

        /* Enable mga mode again ... Just in case :) */
        crtcextset(mga, CrtcExt_Miscellaneous, Mgamode, 0);

        trace("mga: crtcext MgaMode loaded !\n");
        if (ultradebug) Bflush(&stdout);

        if (mode->z == 32 || mode->z == 24 ) {
                /* Initialize Big Endian Mode ! */
                mgawrite32(mga, 0x1e54, 0x02 << 16);
        }


        /* Set final misc ... enable mapping ... */
        miscset(mga, mga->misc | Misc_rammapen, 0);

        trace("mga: mapping enabled !\n");
        if (ultradebug) Bflush(&stdout);

        /* Enable Screen */
        seqset(mga, 1, 0, 0xff);

        trace("screen enabled ...\n");

        if (0) {
                p = mga->mmfb;
                for (i = 0; i < mga->fbsize; i++)
                        *p++ = (0xff & i);
        }

        trace("mga: Loaded !\n" );
        dump_all_regs(mga);
        if (ultradebug) Bflush(&stdout);

        trace("mga: Loaded [bis]!\n" );

        /*
         * TODO: In 16bpp mode, what is the correct palette ?
         *       in the meantime lets use the default one,
         *       which has a weird color combination.
         */

        if (mode->z != 8 && mode ->z != 16) {
                /* Initialize Palette */
                mgawrite8(mga, RAMDACIDX, 0);
                for (i = 0; i < 0x100; i++) {
                        mgawrite8(mga, RAMDACPALDATA, i);
                        mgawrite8(mga, RAMDACPALDATA, i);
                        mgawrite8(mga, RAMDACPALDATA, i);
                }
        }

        trace("mga: Palette initialised !\n");

        /* Enable Cursor */
        dacset(mga, Dac_Xcurctrl, cursor, 0xff);

        ctlr->flag |= Fload;
        if (ultradebug) Bflush(&stdout);
}

Ctlr mga4xx = {
        "mga4xx",                       /* name */
        snarf,                          /* snarf */
        options,                                /* options */
        init,                                   /* init */
        load,                                   /* load */
        dump,                           /* dump */
};

Ctlr mga4xxhwgc = {
        "mga4xxhwgc",           /* name */
        0,                                      /* snarf */
        0,                                      /* options */
        0,                                      /* init */
        0,                                      /* load */
        dump,                           /* dump */
};