Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

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

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

/*
 * ATI Mach32. Some hope.
 * No support for accelerator so can only do up to 1024x768.
 *
 * All ATI Extended Registers are addressed using the modified index
 *      index = (0x02<<6)|(index & 0x3F);
 * so registers 0x00->0x3F map to 0x80->0xBF, but we will only ever
 * look at a few in the range 0xA0->0xBF. In this way we can stash
 * them in the vga->crt[] array.
 */
enum {
        Advfunc         = 0x4AE8,       /* Advanced Function Control Register */
        Clocksel        = 0x4AEE,       /* Clock Select Register */
        Misc            = 0x36EE,       /* Miscellaneous Register */
        Membndry        = 0x42EE,       /* Memory Boundary Register */
        Memcfg          = 0x5EEE,       /* Memory Control Register */
};

typedef struct {
        ushort  advfunc;
        ushort  clocksel;
        ushort  misc;
        ushort  membndry;
        ushort  memcfg;
} Mach32;

/*
 * There are a number of possible clock generator chips for these
 * boards, and I don't know how to find out which is installed, other
 * than by looking at the board. So, pick a subset that will work for
 * all.
 */
typedef struct {
        ulong   frequency;
        uchar   b8;                     /* <6> - divide by 2 */
        uchar   b9;                     /* <1> - bit <3> of frequency index */
        uchar   be;                     /* <4> - bit <2> of frequency index */
        uchar   misc;                   /* <3:2> - bits <1:0> of frequency index */
} Clock;

static Clock clocks[] = {
        {  VgaFreq0,    0x40, 0x02, 0x00, 0x00, },
        {  32000000,    0x00, 0x00, 0x10, 0x04, },
        {  40000000,    0x00, 0x02, 0x10, 0x00, },
        {  44900000,    0x00, 0x02, 0x00, 0x0C, },
        {  65000000,    0x00, 0x02, 0x10, 0x0C, },
        {  75000000,    0x00, 0x02, 0x10, 0x08, },
        {         0, },
};
        
static ulong atix;

static uchar
atixi(uchar index)
{
        outportb(atix, index);
        return inportb(atix+1);
}

static void
atixo(uchar index, uchar data)
{
        outportw(atix, (data<<8)|index);
}

static void
atixinit(Vga* vga, Ctlr*)
{
        uchar b;
        Mach32 *mach32;

        /*
         * We could try to read in a part of the BIOS and try to determine
         * the extended register address, but since we can't determine the offset value,
         * we'll just have to assume the defaults all round.
         */
        atix = 0x1CE;

        /*
         * Unlock the ATI Extended Registers.
         * We leave them unlocked from now on.
         * Why does this chip have so many
         * lock bits?
         */
        if((b = atixi(0xB8)) & 0x3F)
                atixo(0xB8, b & 0xC0);
        b = atixi(0xAB);
        atixo(0xAB, b & ~0x18);
        atixo(0xB4, 0x00);
        b = atixi(0xB9);
        atixo(0xB9, b & ~0x80);
        b = atixi(0xBE);
        atixo(0xBE, b|0x09);

        if(vga->private == 0)
                vga->private = alloc(sizeof(mach32));
}

static void
snarf(Vga* vga, Ctlr* ctlr)
{
        int i;
        Mach32 *mach32;

        atixinit(vga, ctlr);
        for(i = 0xA0; i < 0xC0; i++)
                vga->crt[i] = atixi(i);

        mach32 = vga->private;
        mach32->advfunc = inportw(Advfunc);
        mach32->clocksel = inportw(Clocksel);
        mach32->misc = inportw(Misc);
        mach32->membndry = inportw(Membndry);
        mach32->memcfg = inportw(Memcfg);

        /*
         * Memory size.
         */
        switch((mach32->misc>>2) & 0x03){

        case 0:
                vga->vmz = 512*1024;
                break;

        case 1:
                vga->vmz = 1024*1024;
                break;

        case 2:
                vga->vmz = 2*1024*1024;
                break;

        case 3:
                vga->vmz = 4*1024*1024;
                break;
        }

        ctlr->flag |= Fsnarf;
}

static void
options(Vga*, Ctlr* ctlr)
{
        ctlr->flag |= Foptions;
}

static void
init(Vga* vga, Ctlr* ctlr)
{
        Clock *clockp;
        Mode *mode;

        mode = vga->mode;

        if(vga->f[0] == 0)
                vga->f[0] = vga->mode->frequency;
        for(clockp = clocks; clockp->frequency; clockp++){
                if(clockp->frequency > vga->f[0]+100000)
                        continue;
                if(clockp->frequency > vga->f[0]-100000)
                        break;
        }
        if(clockp->frequency == 0)
                error("%s: no suitable clock for %lud\n",
                        ctlr->name, vga->f[0]);

        vga->crt[0xB0] &= 0xDA;
        vga->crt[0xB1] &= 0x87;
        vga->crt[0xB5] &= 0x7E;
        vga->crt[0xB6] &= 0xE2;
        vga->crt[0xB3] &= 0xAF;
        vga->crt[0xA6] &= 0xFE;
        vga->crt[0xA7] &= 0xF4;

        /*
         * 256-colour linear addressing.
         */
        if(mode->z == 8){
                vga->graphics[0x05] = 0x00;
                vga->attribute[0x10] &= ~0x40;
                vga->crt[0x13] = (mode->x/8)/2;
                vga->crt[0x14] = 0x00;
                vga->crt[0x17] = 0xE3;

                vga->crt[0xB0] |= 0x20;
                vga->crt[0xB6] |= 0x04;
        }
        vga->attribute[0x11] = 0x00;
        vga->crt[0xB6] |= 0x01;
        vga->crt[0xBE] &= ~0x04;

        /*
         * Do the clock index bits.
         */
        vga->crt[0xB9] &= 0xFD;
        vga->crt[0xB8] &= 0x3F;
        vga->crt[0xBE] &= 0xE5;

        vga->crt[0xB8] |= clockp->b8;
        vga->crt[0xB9] |= clockp->b9;
        vga->crt[0xBE] |= clockp->be;
        vga->misc |= clockp->misc;

        if(vga->mode->interlace == 'v')
                vga->crt[0xBE] |= 0x02;

        /*
         * Turn off 128Kb CPU address bit so
         * we only have a 64Kb aperture at 0xA0000.
         */
        vga->crt[0xBD] &= ~0x04;

        ctlr->flag |= Finit;
}

static void
load(Vga* vga, Ctlr* ctlr)
{
        ushort x;

        /*
         * Make sure we are in VGA mode,
         * and that we have access to all the video memory through
         * the 64Kb VGA aperture by disabling and linear aperture
         * and memory boundary.
         */
        outportw(Clocksel, 0x0000);
        x = inportw(Memcfg) & ~0x0003;
        outportw(Memcfg, x);
        outportw(Membndry, 0x0000);

        atixo(0xB0, vga->crt[0xB0]);
        atixo(0xB1, vga->crt[0xB1]);
        atixo(0xB5, vga->crt[0xB5]);
        atixo(0xB6, vga->crt[0xB6]);
        atixo(0xB3, vga->crt[0xB3]);
        atixo(0xA6, vga->crt[0xA6]);
        atixo(0xA7, vga->crt[0xA7]);
        atixo(0xB8, vga->crt[0xB8]);
        atixo(0xB9, vga->crt[0xB9]);
        atixo(0xBE, vga->crt[0xBE]);
        vgao(MiscW, vga->misc);

        ctlr->flag |= Fload;
}

static void
dump(Vga* vga, Ctlr* ctlr)
{
        int i;
        Mach32 *mach32;

        printitem(ctlr->name, "ATIX");
        for(i = 0xA0; i < 0xC0; i++)
                printreg(vga->crt[i]);

        if((mach32 = vga->private) == 0)
                return;

        printitem(ctlr->name, "ADVFUNC");
        Bprint(&stdout, "%.4ux\n", mach32->advfunc);
        printitem(ctlr->name, "CLOCKSEL");
        Bprint(&stdout, "%.4ux\n", mach32->clocksel);
        printitem(ctlr->name, "MISC");
        Bprint(&stdout, "%.4ux\n", mach32->misc);
        printitem(ctlr->name, "MEMBNDRY");
        Bprint(&stdout, "%.4ux\n", mach32->membndry);
        printitem(ctlr->name, "MEMCFG");
        Bprint(&stdout, "%.4ux\n", mach32->memcfg);
}

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