Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"

#define Image   IMAGE
#include <draw.h>
#include <memdraw.h>
#include <cursor.h>
#include "screen.h"

enum {
        PCIS3           = 0x5333,               /* PCI VID */

        SAVAGE3D        = 0x8A20,               /* PCI DID */
        SAVAGE3DMV      = 0x8A21,
        SAVAGE4         = 0x8A22,
        PROSAVAGEP      = 0x8A25,
        PROSAVAGEK      = 0x8A26,
        PROSAVAGE8      = 0x8D04,
        SAVAGEMXMV      = 0x8C10,
        SAVAGEMX        = 0x8C11,
        SAVAGEIXMV      = 0x8C12,
        SAVAGEIX        = 0x8C13,
        SUPERSAVAGEIXC16 = 0x8C2E,
        SAVAGE2000      = 0x9102,

        VIRGE           = 0x5631,
        VIRGEGX2        = 0x8A10,
        VIRGEDXGX       = 0x8A01,
        VIRGEVX         = 0x883D,
        VIRGEMX         = 0x8C01,
        VIRGEMXP        = 0x8C03,

        VIRTUALPC2004   = 0x8810,
        AURORA64VPLUS   = 0x8812,
};

static int
s3pageset(VGAscr* scr, int page)
{
        uchar crt35, crt51;
        int opage;

        crt35 = vgaxi(Crtx, 0x35);
        if(scr->gscreen->depth >= 8){
                /*
                 * The S3 registers need to be unlocked for this.
                 * Let's hope they are already:
                 *      vgaxo(Crtx, 0x38, 0x48);
                 *      vgaxo(Crtx, 0x39, 0xA0);
                 *
                 * The page is 6 bits, the lower 4 bits in Crt35<3:0>,
                 * the upper 2 in Crt51<3:2>.
                 */
                vgaxo(Crtx, 0x35, page & 0x0F);
                crt51 = vgaxi(Crtx, 0x51);
                vgaxo(Crtx, 0x51, (crt51 & ~0x0C)|((page & 0x30)>>2));
                opage = ((crt51 & 0x0C)<<2)|(crt35 & 0x0F);
        }
        else{
                vgaxo(Crtx, 0x35, (page<<2) & 0x0C);
                opage = (crt35>>2) & 0x03;
        }

        return opage;
}

static void
s3page(VGAscr* scr, int page)
{
        int id;

        id = (vgaxi(Crtx, 0x2D)<<8)|vgaxi(Crtx, 0x2E);
        switch(id){

        case VIRGEGX2:
                break;

        default:
                lock(&scr->devlock);
                s3pageset(scr, page);
                unlock(&scr->devlock);
                break;
        }
}

static void
s3linear(VGAscr* scr, int, int)
{
        int id, j;
        ulong mmiobase, mmiosize;
        Pcidev *p;
        
        vgalinearpciid(scr, PCIS3, 0);
        p = scr->pci;
        if(scr->paddr == 0 || p == nil)
                return;
                
        addvgaseg("s3screen", scr->paddr, scr->apsize);
        
        id = (vgaxi(Crtx, 0x2D)<<8)|vgaxi(Crtx, 0x2E);
        switch(id){                     /* find mmio */
        case SAVAGE4:
        case PROSAVAGEP:
        case PROSAVAGEK:
        case PROSAVAGE8:
        case SUPERSAVAGEIXC16:
                /*
                 * We could assume that the MMIO registers
                 * will be in the screen segment and just use
                 * that, but PCI software is allowed to move them
                 * if it feels like it, so we look for an aperture of
                 * the right size; only the first 512k actually means
                 * anything.  The S3 engineers overestimated how
                 * much space they would need in the first design.
                 */
                for(j=0; j<nelem(p->mem); j++){
                        if((p->mem[j].bar&~0x0F) != scr->paddr)
                        if(p->mem[j].size==512*1024 || p->mem[j].size==16*1024*1024){
                                mmiobase = p->mem[j].bar & ~0x0F;
                                mmiosize = 512*1024;
                                scr->mmio = vmap(mmiobase, mmiosize);
                                if(scr->mmio == nil)
                                        return;
                                addvgaseg("savagemmio", mmiobase, mmiosize);
                                break;
                        }
                }
        }
}

static void
s3vsyncactive(void)
{
        /*
         * Hardware cursor information is fetched from display memory
         * during the horizontal blank active time. The 80x chips may hang
         * if the cursor is turned on or off during this period.
         */
        while((vgai(Status1) & 0x08) == 0)
                ;
}

static void
s3disable(VGAscr*)
{
        uchar crt45;

        /*
         * Turn cursor off.
         */
        crt45 = vgaxi(Crtx, 0x45) & 0xFE;
        s3vsyncactive();
        vgaxo(Crtx, 0x45, crt45);
}

static void
s3load(VGAscr* scr, Cursor* curs)
{
        uchar *p;
        int id, dolock, opage, x, y;

        /*
         * Disable the cursor and
         * set the pointer to the two planes.
         */
        s3disable(scr);

        opage = 0;
        p = scr->vaddr;
        id = (vgaxi(Crtx, 0x2D)<<8)|vgaxi(Crtx, 0x2E);
        switch(id){

        case VIRTUALPC2004:
        case VIRGE:
        case VIRGEDXGX:
        case VIRGEGX2:
        case VIRGEVX:   
        case SAVAGEMXMV:
        case SAVAGEIXMV:
        case SAVAGE4:
        case PROSAVAGEP:
        case PROSAVAGEK:
        case PROSAVAGE8:
        case SUPERSAVAGEIXC16:
                dolock = 0;
                p += scr->storage;
                break;

        default:
                dolock = 1;
                lock(&scr->devlock);
                opage = s3pageset(scr, scr->storage>>16);
                p += (scr->storage & 0xFFFF);
                break;
        }

        /*
         * The cursor is set in Microsoft Windows format (the ViRGE/GX2 doesn't
         * support the X11 format) which gives the following truth table:
         *      and xor colour
         *       0   0  background colour
         *       0   1  foreground colour
         *       1   0  current screen pixel
         *       1   1  NOT current screen pixel
         * Put the cursor into the top-left of the 64x64 array.
         *
         * The cursor pattern in memory is interleaved words of
         * AND and XOR patterns.
         */
        for(y = 0; y < 64; y++){
                for(x = 0; x < 64/8; x += 2){
                        if(x < 16/8 && y < 16){
                                *p++ = ~(curs->clr[2*y + x]|curs->set[2*y + x]);
                                *p++ = ~(curs->clr[2*y + x+1]|curs->set[2*y + x+1]);
                                *p++ = curs->set[2*y + x];
                                *p++ = curs->set[2*y + x+1];
                        }
                        else {
                                *p++ = 0xFF;
                                *p++ = 0xFF;
                                *p++ = 0x00;
                                *p++ = 0x00;
                        }
                }
        }

        if(dolock){
                s3pageset(scr, opage);
                unlock(&scr->devlock);
        }

        /*
         * Save the cursor hotpoint and enable the cursor.
         */
        scr->offset = curs->offset;
        s3vsyncactive();
        vgaxo(Crtx, 0x45, 0x01);
}

static int
s3move(VGAscr* scr, Point p)
{
        int x, xo, y, yo;

        /*
         * Mustn't position the cursor offscreen even partially,
         * or it disappears. Therefore, if x or y is -ve, adjust the
         * cursor offset instead.
         * There seems to be a bug in that if the offset is 1, the
         * cursor doesn't disappear off the left edge properly, so
         * round it up to be even.
         */
        if((x = p.x+scr->offset.x) < 0){
                xo = -x;
                xo = ((xo+1)/2)*2;
                x = 0;
        }
        else
                xo = 0;
        if((y = p.y+scr->offset.y) < 0){
                yo = -y;
                y = 0;
        }
        else
                yo = 0;

        vgaxo(Crtx, 0x46, (x>>8) & 0x07);
        vgaxo(Crtx, 0x47, x & 0xFF);
        vgaxo(Crtx, 0x49, y & 0xFF);
        vgaxo(Crtx, 0x4E, xo);
        vgaxo(Crtx, 0x4F, yo);
        vgaxo(Crtx, 0x48, (y>>8) & 0x07);

        return 0;
}

static void
s3enable(VGAscr* scr)
{
        int i;
        ulong storage;

        s3disable(scr);

        /*
         * Cursor colours. Set both the CR0[EF] and the colour
         * stack in case we are using a 16-bit RAMDAC.
         */
        vgaxo(Crtx, 0x0E, Pwhite);
        vgaxo(Crtx, 0x0F, Pblack);
        vgaxi(Crtx, 0x45);

        for(i = 0; i < 3; i++)
                vgaxo(Crtx, 0x4A, Pblack);
        vgaxi(Crtx, 0x45);
        for(i = 0; i < 3; i++)
                vgaxo(Crtx, 0x4B, Pwhite);

        /*
         * Find a place for the cursor data in display memory.
         * Must be on a 1024-byte boundary.
         */
        storage = (scr->gscreen->width*BY2WD*scr->gscreen->r.max.y+1023)/1024;
        vgaxo(Crtx, 0x4C, storage>>8);
        vgaxo(Crtx, 0x4D, storage & 0xFF);
        storage *= 1024;
        scr->storage = storage;

        /*
         * Load, locate and enable the cursor
         * in Microsoft Windows format.
         */
        s3load(scr, &arrow);
        s3move(scr, ZP);
        vgaxo(Crtx, 0x55, vgaxi(Crtx, 0x55) & ~0x10);
        s3vsyncactive();
        vgaxo(Crtx, 0x45, 0x01);
}

/*
 * The manual gives byte offsets, but we want ulong offsets, hence /4.
 */
enum {
        SrcBase = 0xA4D4/4,
        DstBase = 0xA4D8/4,
        Stride = 0xA4E4/4,
        FgrdData = 0xA4F4/4,
        WidthHeight = 0xA504/4,
        SrcXY = 0xA508/4,
        DestXY = 0xA50C/4,
        Command = 0xA500/4,
        SubStat = 0x8504/4,
        FifoStat = 0x850C/4,
};

/*
 * Wait for writes to VGA memory via linear aperture to flush.
 */
enum {Maxloop = 1<<24};
struct {
        ulong linear;
        ulong fifo;
        ulong idle;
        ulong lineartimeout;
        ulong fifotimeout;
        ulong idletimeout;
} waitcount;

static void
waitforlinearfifo(VGAscr *scr)
{
        ulong *mmio;
        long x;
        static ulong nwaitforlinearfifo;
        ulong mask, val;

        switch(scr->id){
        default:
                panic("unknown scr->id in s3 waitforlinearfifo");
        case 0x8A01:    /* ViRGE/[DG]X.  XFree86 says no waiting necessary */
                return;
        case 0x5631:    /* ViRGE */
        case 0x883D:    /* ViRGE/VX */
                mask = 0x0F<<6;
                val = 0x08<<6;
                break;
        case 0x8A10:    /* ViRGE/GX2 */
                mask = 0x1F<<6;
                val = 0x10<<6;
                break;
        }
        mmio = scr->mmio;
        x = 0;
        while((mmio[FifoStat]&mask) != val && x++ < Maxloop)
                waitcount.linear++;
        if(x >= Maxloop)
                waitcount.lineartimeout++;
}

static void
waitforfifo(VGAscr *scr, int entries)
{
        ulong *mmio;
        long x;
        static ulong nwaitforfifo;

        mmio = scr->mmio;
        x = 0;
        while((mmio[SubStat]&0x1F00) < ((entries+2)<<8) && x++ < Maxloop)
                waitcount.fifo++;
        if(x >= Maxloop)
                waitcount.fifotimeout++;
}

static void
waitforidle(VGAscr *scr)
{
        ulong *mmio;
        long x;

        mmio = scr->mmio;
        x = 0;
        while((mmio[SubStat]&0x3F00) != 0x3000 && x++ < Maxloop)
                waitcount.idle++;
        if(x >= Maxloop)
                waitcount.idletimeout++;
}

static int
hwscroll(VGAscr *scr, Rectangle r, Rectangle sr)
{
        enum { Bitbltop = 0xCC };       /* copy source */
        ulong *mmio;
        ulong cmd, stride;
        Point dp, sp;
        int did, d;

        d = scr->gscreen->depth;
        did = (d-8)/8;
        cmd = 0x00000020|(Bitbltop<<17)|(did<<2);
        stride = Dx(scr->gscreen->r)*d/8;

        if(r.min.x <= sr.min.x){
                cmd |= 1<<25;
                dp.x = r.min.x;
                sp.x = sr.min.x;
        }else{
                dp.x = r.max.x-1;
                sp.x = sr.max.x-1;
        }

        if(r.min.y <= sr.min.y){
                cmd |= 1<<26;
                dp.y = r.min.y;
                sp.y = sr.min.y;
        }else{
                dp.y = r.max.y-1;
                sp.y = sr.max.y-1;
        }

        mmio = scr->mmio;
        waitforlinearfifo(scr);
        waitforfifo(scr, 7);
        mmio[SrcBase] = scr->paddr;
        mmio[DstBase] = scr->paddr;
        mmio[Stride] = (stride<<16)|stride;
        mmio[WidthHeight] = ((Dx(r)-1)<<16)|Dy(r);
        mmio[SrcXY] = (sp.x<<16)|sp.y;
        mmio[DestXY] = (dp.x<<16)|dp.y;
        mmio[Command] = cmd;
        waitforidle(scr);
        return 1;
}

static int
hwfill(VGAscr *scr, Rectangle r, ulong sval)
{
        enum { Bitbltop = 0xCC };       /* copy source */
        ulong *mmio;
        ulong cmd, stride;
        int did, d;

        d = scr->gscreen->depth;
        did = (d-8)/8;
        cmd = 0x16000120|(Bitbltop<<17)|(did<<2);
        stride = Dx(scr->gscreen->r)*d/8;
        mmio = scr->mmio;
        waitforlinearfifo(scr);
        waitforfifo(scr, 8);
        mmio[SrcBase] = scr->paddr;
        mmio[DstBase] = scr->paddr;
        mmio[DstBase] = scr->paddr;
        mmio[Stride] = (stride<<16)|stride;
        mmio[FgrdData] = sval;
        mmio[WidthHeight] = ((Dx(r)-1)<<16)|Dy(r);
        mmio[DestXY] = (r.min.x<<16)|r.min.y;
        mmio[Command] = cmd;
        waitforidle(scr);
        return 1;
}

enum {
        CursorSyncCtl = 0x0D,   /* in Seqx */
        VsyncHi = 0x80,
        VsyncLo = 0x40,
        HsyncHi = 0x20,
        HsyncLo = 0x10,
};

static void
s3blank(VGAscr*, int blank)
{
        uchar x;

        x = vgaxi(Seqx, CursorSyncCtl);
        x &= ~0xF0;
        if(blank)
                x |= VsyncLo | HsyncLo;
        vgaxo(Seqx, CursorSyncCtl, x);
}

static void
s3drawinit(VGAscr *scr)
{
        extern void savageinit(VGAscr*);        /* vgasavage.c */
        ulong id;

        id = (vgaxi(Crtx, 0x2D)<<8)|vgaxi(Crtx, 0x2E);
        scr->id = id;

        /*
         * It's highly likely that other ViRGEs will work without
         * change to the driver, with the exception of the size of
         * the linear aperture memory write FIFO.  Since we don't
         * know that size, I'm not turning them on.  See waitforlinearfifo
         * above.
         */
        scr->blank = s3blank;
        /* hwblank = 1;         not known to work well */

        switch(id){
        case VIRGE:
        case VIRGEVX:
        case VIRGEGX2:
                scr->mmio = (ulong*)((char*)scr->vaddr+0x1000000);
                scr->fill = hwfill;
                scr->scroll = hwscroll;
                break;
        case SAVAGEMXMV:
        case SAVAGEIXMV:
                scr->mmio = (ulong*)((char*)scr->vaddr+0x1000000);
                savageinit(scr);        
                break;
        case SUPERSAVAGEIXC16:
        case SAVAGE4:
        case PROSAVAGEP:
        case PROSAVAGE8:
        case PROSAVAGEK:
                /* scr->mmio is set by s3linear */
                savageinit(scr);
                break;
        }
}

VGAdev vgas3dev = {
        "s3",

        0,
        0,
        s3page,
        s3linear,
        s3drawinit,
};

VGAcur vgas3cur = {
        "s3hwgc",

        s3enable,
        s3disable,
        s3load,
        s3move,
};