#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 #include #include #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; jmem); 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, };