summaryrefslogtreecommitdiff
path: root/os/pc/vgas3.c
diff options
context:
space:
mode:
Diffstat (limited to 'os/pc/vgas3.c')
-rw-r--r--os/pc/vgas3.c620
1 files changed, 620 insertions, 0 deletions
diff --git a/os/pc/vgas3.c b/os/pc/vgas3.c
new file mode 100644
index 00000000..b23657e6
--- /dev/null
+++ b/os/pc/vgas3.c
@@ -0,0 +1,620 @@
+#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 ulong
+s3linear(VGAscr* scr, int* size, int* align)
+{
+ char *mmioname;
+ ulong aperture, oaperture, mmiobase, mmiosize;
+ int i, id, j, osize, oapsize, wasupamem;
+ Pcidev *p;
+
+ osize = *size;
+ oaperture = scr->aperture;
+ oapsize = scr->apsize;
+ wasupamem = scr->isupamem;
+
+ mmiosize = 0;
+ mmiobase = 0;
+ mmioname = nil;
+
+ /*
+ * S3 makes cards other than display controllers, so
+ * look for the first S3 display controller (device class 3)
+ * and not one of their sound cards.
+ */
+ p = nil;
+ while(p = pcimatch(p, PCIS3, 0)){
+ if(p->ccrb == 0x03)
+ break;
+ }
+ if(p != nil){
+ for(i=0; i<nelem(p->mem); i++){
+ if(p->mem[i].size >= *size
+ && ((p->mem[i].bar & ~0x0F) & (*align-1)) == 0)
+ break;
+ }
+ if(i >= nelem(p->mem)){
+ print("vgas3: aperture not found\n");
+ return 0;
+ }
+ aperture = p->mem[i].bar & ~0x0F;
+ *size = p->mem[i].size;
+
+ 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(i == j)
+ continue;
+ 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 = (ulong*)upamalloc(mmiobase, mmiosize, 0);
+ mmioname = "savagemmio";
+ break;
+ }
+ }
+ if(mmiosize == 0){
+ print("savage4: mmio not found\n");
+ return 0;
+ }
+ }
+ }else
+ aperture = 0;
+
+ if(wasupamem)
+ upafree(oaperture, oapsize);
+ scr->isupamem = 0;
+
+ aperture = upamalloc(aperture, *size, *align);
+ if(aperture == 0){
+ if(wasupamem && upamalloc(oaperture, oapsize, 0))
+ scr->isupamem = 1;
+ }
+ else
+ scr->isupamem = 1;
+
+ if(oaperture && oaperture != aperture)
+ print("warning (BUG): redefinition of aperture does not change s3screen segment\n");
+ addvgaseg("s3screen", aperture, osize);
+
+ if(mmiosize)
+ addvgaseg(mmioname, mmiobase, mmiosize);
+
+ return aperture;
+}
+
+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 = KADDR(scr->aperture);
+ 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->aperture;
+ mmio[DstBase] = scr->aperture;
+ 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->aperture;
+ mmio[DstBase] = scr->aperture;
+ mmio[DstBase] = scr->aperture;
+ 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*)(scr->aperture+0x1000000);
+ scr->fill = hwfill;
+ scr->scroll = hwscroll;
+ break;
+ case SAVAGEMXMV:
+ case SAVAGEIXMV:
+ scr->mmio = (ulong*)(scr->aperture+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,
+};
+