diff options
Diffstat (limited to 'os/pc/vgasavage.c')
| -rw-r--r-- | os/pc/vgasavage.c | 571 |
1 files changed, 571 insertions, 0 deletions
diff --git a/os/pc/vgasavage.c b/os/pc/vgasavage.c new file mode 100644 index 00000000..263b688b --- /dev/null +++ b/os/pc/vgasavage.c @@ -0,0 +1,571 @@ +#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, + + AURORA64VPLUS = 0x8812, +}; + +/* + * Savage4 et al. acceleration. + * + * This is based only on the Savage4 documentation. + * It is expected to work on other Savage cards as well, + * but has not been tried. + * + * There are five ways to access the 2D graphics engine registers: + * - Old MMIO non-packed format + * - Old MMIO packed format + * - New MMIO non-packed format + * - New MMIO packed format + * - Burst Command Interface (BCI) + * + * Of these, the manual hints that the first three are deprecated, + * and it does not document any of those three well enough to use. + * + * I have tried for many hours with no success to understand the BCI + * interface well enough to use it. It is not well documented, and the + * XFree86 driver seems to completely contradict what little documentation + * there is. + * + * This leaves the packed new MMIO. + * The manual contradicts itself here, claming that the registers + * start at 0x2008100 as well as at 0x0008100 from the base of the + * mmio segment. Since the segment is only 512k, we assume that + * the latter is the correct offset. + * + * According to the manual, only 16-bit reads of the 2D registers + * are supported: 32-bit reads will return garbage in the upper word. + * 32-bit writes must be enabled explicitly. + * + * 32-bit reads of the status registers seem just fine. + */ + +/* 2D graphics engine registers for Savage4; others appear to be mostly the same */ +enum { + SubsystemStatus = 0x8504, /* Subsystem Status: read only */ + /* read only: whether we get interrupts on various events */ + VsyncInt = 1<<0, /* vertical sync */ + GeBusyInt = 1<<1, /* 2D graphics engine busy */ + BfifoFullInt = 1<<2, /* BIU FIFO full */ + BfifoEmptyInt = 1<<3, /* BIU FIFO empty */ + CfifoFullInt = 1<<4, /* command FIFO full */ + CfifoEmptyInt = 1<<5, /* command FIFO empty */ + BciInt = 1<<6, /* BCI */ + LpbInt = 1<<7, /* LPB */ + CbHiInt = 1<<16, /* COB upper threshold */ + CbLoInt = 1<<17, /* COB lower threshold */ + + SubsystemCtl = 0x8504, /* Subsystem Control: write only */ + /* clear interrupts for various events */ + VsyncClr = 1<<0, + GeBusyClr = 1<<1, + BfifoFullClr = 1<<2, + BfifoEmptyClr = 1<<3, + CfifoFullClr = 1<<4, + CfifoEmptyClr = 1<<5, + BciClr = 1<<6, + LpbClr = 1<<7, + CbHiClr = 1<<16, + CbLoClr = 1<<17, + + /* enable interrupts for various events */ + VsyncEna = 1<<8, + Busy2DEna = 1<<9, + BfifoFullEna = 1<<10, + BfifoEmptyEna = 1<<11, + CfifoFullEna = 1<<12, + CfifoEmptyEna = 1<<13, + SubsysBciEna = 1<<14, + CbHiEna = 1<<24, + CbLoEna = 1<<25, + + /* 2D graphics engine software reset */ + GeSoftReset = 1<<15, + + FifoStatus = 0x8508, /* FIFO status: read only */ + CwbEmpty = 1<<0, /* command write buffer empty */ + CrbEmpty = 1<<1, /* command read buffer empty */ + CobEmpty = 1<<2, /* command overflow buffer empty */ + CfifoEmpty = 1<<3, /* command FIFO empty */ + CwbFull = 1<<8, /* command write buffer full */ + CrbFull = 1<<9, /* command read buffer full */ + CobFull = 1<<10, /* command overflow buffer full */ + CfifoFull = 1<<11, /* command FIFO full */ + + AdvFunCtl = 0x850C, /* Advanced Function Control: read/write */ + GeEna = 1<<0, /* enable 2D/3D engine */ + /* + * according to the manual, BigPixel should be + * set when bpp >= 8 (bpp != 4), and then CR50_5-4 are + * used to figure out bpp example. however, it does bad things + * to the screen in 8bpp mode. + */ + BigPixel = 1<<2, /* 8 or more bpp enhanced mode */ + LaEna = 1<<3, /* linear addressing ena: or'ed with CR58_4 */ + Mclk_2 = 0<<8, /* 2D engine clock divide: MCLK/2 */ + Mclk_4 = 1<<8, /* " MCLK/4 */ + Mclk = 2<<8, /* " MCLK */ + /* Mclk = 3<<8, /* " MCLK */ + Ic33mhz = 1<<16, /* Internal clock 33 MHz (instead of 66) */ + + WakeupReg = 0x8510, /* Wakeup: read/write */ + WakeupBit = 1<<0, /* wake up: or'ed with 3C3_0 */ + + SourceY = 0x8100, /* UL corner of bitblt source */ + SourceX = 0x8102, /* " */ + RectY = 0x8100, /* UL corner of rectangle fill */ + RectX = 0x8102, /* " */ + DestY = 0x8108, /* UL corner of bitblt dest */ + DestX = 0x810A, /* " */ + Height = 0x8148, /* bitblt, image xfer rectangle height */ + Width = 0x814A, /* bitblt, image xfer rectangle width */ + + StartY = 0x8100, /* Line draw: first point*/ + StartX = 0x8102, /* " */ + /* + * For line draws, the following must be programmed: + * axial step constant = 2*min(|dx|,|dy|) + * diagonal step constant = 2*[min(|dx|,|dy|) - max(|dx|,|dy|)] + * error term = 2*min(|dx|,|dy|) - max(|dx|,|dy| - 1 + * [sic] when start X < end X + * error term = 2*min(|dx|,|dy|) - max(|dx|,|dy| + * [sic] when start X >= end X + */ + AxialStep = 0x8108, + DiagonalStep = 0x810A, + LineError = 0x8110, + MinorLength = 0x8148, /* pixel count along minor axis */ + MajorLength = 0x814A, /* pixel count along major axis */ + + DrawCmd = 0x8118, /* Drawing Command: write only */ + CmdMagic = 0<<1, + AcrossPlane = 1<<1, /* across the plane mode */ + LastPixelOff = 1<<2, /* last pixel of line or vector draw not drawn */ + Radial = 1<<3, /* enable radial direction (else axial) */ + DoDraw = 1<<4, /* draw pixels (else only move current pos) */ + + DrawRight = 1<<5, /* axial drawing direction: left to right */ + /* DrawLeft = 0<<5, */ + MajorY = 1<<6, + /* MajorX = 0<<6, */ + DrawDown = 1<<7, + /* DrawUp = 0<<7, */ + Degree0 = 0<<5, /* drawing direction when Radial */ + Degree45 = 1<<5, + /* ... */ + Degree315 = 7<<5, + + UseCPUData = 1<<8, + + /* image write bus transfer width */ + Bus8 = 0<<9, + Bus16 = 1<<9, + /* + * in Bus32 mode, doubleword bits beyond the image rect width are + * discarded. each line starts on a new doubleword. + * Bus32AP is intended for across-the-plane mode and + * rounds to byte boundaries instead. + */ + Bus32 = 2<<9, + Bus32AP = 3<<9, + + CmdNop = 0<<13, /* nop */ + CmdLine = 1<<13, /* draw line */ + CmdFill = 2<<13, /* fill rectangle */ + CmdBitblt = 6<<13, /* bitblt */ + CmdPatblt = 7<<13, /* 8x8 pattern blt */ + + SrcGBD = 0<<16, + SrcPBD = 1<<16, + SrcSBD = 2<<16, + + DstGBD = 0<<18, + DstPBD = 1<<18, + DstSBD = 2<<18, + + /* color sources, controls */ + BgColor = 0x8120, /* Background Color: read/write */ + FgColor = 0x8124, /* Foreground Color: read/write */ + BitplaneWmask = 0x8128, /* Bitplane Write Mask: read/write */ + BitplaneRmask = 0x812C, /* Bitplane Read Mask: read/write */ + CmpColor = 0x8130, /* Color Compare: read/write */ + BgMix = 0x8134, + FgMix = 0x8136, + MixNew = 7, + SrcBg = 0<<5, + SrcFg = 1<<5, + SrcCPU = 2<<5, + SrcDisp = 3<<5, + + /* clipping rectangle */ + TopScissors = 0x8138, /* Top Scissors: write only */ + LeftScissors = 0x813A, /* Left Scissors: write only */ + BottomScissors = 0x813C, /* Bottom Scissors: write only */ + RightScissors = 0x813E, /* Right Scissors: write only */ + + /* + * Registers with Magic were indirectly accessed in older modes. + * It is not clear whether the Magic is necessary. + * In the older modes, writes to these registers were pipelined, + * so that you had to issue an engine command and wait for engine + * idle before reading a write back. It is not clear if this is + * still the case either. + */ + PixCtl = 0x8140, /* Pixel Control: write only */ + PixMagic = 0xA<<12, + PixMixFg = 0<<6, /* foreground mix register always */ + PixMixCPU = 2<<6, /* CPU data determines mix register */ + PixMixDisp = 3<<6, /* display data determines mix register */ + + MfMisc2Ctl = 0x8142, /* Multifunction Control Misc. 2: write only */ + MfMisc2Magic = 0xD<<12, + DstShift = 0, /* 3 bits: destination base address in MB */ + SrcShift = 4, /* 3 bits: source base address in MB */ + WaitFifoEmpty = 2<<8, /* wait for write FIFO empty between draws */ + + MfMiscCtl = 0x8144, /* Multifunction Control Misc: write only */ + MfMiscMagic = 0xE<<12, + UseHighBits = 1<<4, /* select upper 16 bits for 32-bit reg access */ + ClipInvert = 1<<5, /* only touch pixels outside clip rectangle */ + SkipSame = 0<<6, /* ignore pixels with color CmpColor */ + SkipDifferent = 1<<7, /* ignore pixels not color CmpColor */ + CmpEna = 1<<8, /* enable color compare */ + W32Ena = 1<<9, /* enable 32-bit register write */ + ClipDis = 1<<11, /* disable clipping */ + + /* + * The bitmap descriptor 1 registers contain the starting + * address of the bitmap (in bytes). + * The bitmap descriptor 2 registesr contain stride (in pixels) + * in the lower 16 bits, depth (in bits) in the next 8 bits, + * and whether block write is disabled. + */ + GBD1 = 0x8168, /* Global Bitmap Descriptor 1: read/write */ + GBD2 = 0x816C, /* Global Bitmap Descriptor 2: read/write */ + /* GBD2-only bits */ + BDS64 = 1<<0, /* bitmap descriptor size 64 bits */ + GBDBciEna = 1<<3, /* BCI enable */ + /* generic BD2 bits */ + BlockWriteDis = 1<<28, + StrideShift = 0, + DepthShift = 16, + + PBD1 = 0x8170, /* Primary Bitmap Descriptor: read/write */ + PBD2 = 0x8174, + SBD1 = 0x8178, /* Secondary Bitmap Descriptor: read/write */ + SBD2 = 0x817C, +}; + +/* mastered data transfer registers */ + +/* configuration/status registers */ +enum { + XStatus0 = 0x48C00, /* Status Word 0: read only */ + /* rev. A silicon differs from rev. B; use AltStatus0 */ + CBEMaskA = 0x1FFFF, /* filled command buffer entries */ + CBEShiftA = 0, + BciIdleA = 1<<17, /* BCI idle */ + Ge3IdleA = 1<<18, /* 3D engine idle */ + Ge2IdleA = 1<<19, /* 2D engine idle */ + McpIdleA = 1<<20, /* motion compensation processor idle */ + MeIdleA = 1<<22, /* master engine idle */ + PfPendA = 1<<23, /* page flip pending */ + + CBEMaskB = 0x1FFFFF, + CBEShiftB = 0, + BciIdleB = 1<<25, + Ge3IdleB = 1<<26, + Ge2IdleB = 1<<27, + McpIdleB = 1<<28, + MeIdleB = 1<<30, + PfPendB = 1<<31, + + AltStatus0 = 0x48C60, /* Alternate Status Word 0: read only */ + CBEMask = 0x1FFFF, + CBEShift = 0, + /* the Savage4 manual says bits 17..23 for these, like Status0 */ + /* empirically, they are bits 21..26 */ + BciIdle = 1<<21, + Ge3Idle = 1<<22, + Ge2Idle = 1<<23, + McpIdle = 1<<24, + MeIdle = 1<<25, + PfPend = 1<<26, + + XStatus1 = 0x48C04, /* Status Word 1: read only */ + /* contains event tag 1, event tag 0, both 16 bits */ + + XStatus2 = 0x48C08, /* Status Word 2: read only */ + ScanMask = 0x3FF, /* current scan line */ + ScanShift = 0, + VRTMask = 0x7F100, /* vert retrace count */ + VRTShift = 11, + + CbThresh = 0x48C10, /* Command Buffer Thresholds: read/write */ + CobOff = 0x48C14, /* Command Overflow Buffer: read/write */ + + CobPtr = 0x48C18, /* Command Overflow Buffer Pointers: read/write */ + CobEna = 1<<2, /* command overflow buffer enable */ + CobBciEna = 1<<3, /* BCI function enable */ + CbeMask = 0xFFFF8000, /* no. of entries in command buffer */ + CbeShift = 15, + + AltStatus1 = 0x48C64, /* Alternate Status Word 1: read onnly */ + /* contains current texture surface tag, vertex buffer tag */ + +}; + +struct { + ulong idletimeout; + ulong tostatw[16]; +} savagestats; + +enum { + Maxloop = 1<<20 +}; + +static void +savagewaitidle(VGAscr *scr) +{ + long x; + ulong *statw, mask, goal; + + switch(scr->id){ + case SAVAGE4: + case PROSAVAGEP: + case PROSAVAGEK: + case PROSAVAGE8: + /* wait for engine idle and FIFO empty */ + statw = (ulong*)((uchar*)scr->mmio+AltStatus0); + mask = CBEMask | Ge2Idle; + goal = Ge2Idle; + break; + /* case SAVAGEMXMV: ? */ + /* case SAVAGEMX: ? */ + /* case SAVAGEIX: ? */ + case SUPERSAVAGEIXC16: + case SAVAGEIXMV: + case SAVAGEMXMV: + /* wait for engine idle and FIFO empty */ + statw = (ulong*)((uchar*)scr->mmio+XStatus0); + mask = CBEMaskA | Ge2IdleA; + goal = Ge2IdleA; + break; + default: + /* + * best we can do: can't print or we'll call ourselves. + * savageinit is supposed to not let this happen. + */ + return; + } + + for(x=0; x<Maxloop; x++) + if((*statw & mask) == goal) + return; + + savagestats.tostatw[savagestats.idletimeout++&15] = *statw; + savagestats.tostatw[savagestats.idletimeout++&15] = (ulong)statw; +} + +static int +savagefill(VGAscr *scr, Rectangle r, ulong sval) +{ + uchar *mmio; + + mmio = (uchar*)scr->mmio; + + *(ulong*)(mmio+FgColor) = sval; + *(ulong*)(mmio+BgColor) = sval; + *(ulong*)(mmio+BgMix) = SrcFg|MixNew; + *(ulong*)(mmio+FgMix) = SrcFg|MixNew; + *(ushort*)(mmio+RectY) = r.min.y; + *(ushort*)(mmio+RectX) = r.min.x; + *(ushort*)(mmio+Width) = Dx(r)-1; + *(ushort*)(mmio+Height) = Dy(r)-1; + *(ulong*)(mmio+DrawCmd) = CmdMagic | DoDraw | CmdFill | DrawRight | DrawDown; + savagewaitidle(scr); + return 1; +} + +static int +savagescroll(VGAscr *scr, Rectangle r, Rectangle sr) +{ + uchar *mmio; + ulong cmd; + Point dp, sp; + + cmd = CmdMagic | DoDraw | CmdBitblt | SrcPBD | DstGBD; + + if(r.min.x <= sr.min.x){ + cmd |= DrawRight; + 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 |= DrawDown; + dp.y = r.min.y; + sp.y = sr.min.y; + }else{ + dp.y = r.max.y-1; + sp.y = sr.max.y-1; + } + + mmio = (uchar*)scr->mmio; + + *(ushort*)(mmio+SourceX) = sp.x; + *(ushort*)(mmio+SourceY) = sp.y; + *(ushort*)(mmio+DestX) = dp.x; + *(ushort*)(mmio+DestY) = dp.y; + *(ushort*)(mmio+Width) = Dx(r)-1; + *(ushort*)(mmio+Height) = Dy(r)-1; + *(ulong*)(mmio+BgMix) = SrcDisp|MixNew; + *(ulong*)(mmio+FgMix) = SrcDisp|MixNew; + *(ulong*)(mmio+DrawCmd) = cmd; + savagewaitidle(scr); + return 1; +} + +static void +savageblank(VGAscr*, int blank) +{ + uchar seqD; + + /* + * Will handle DPMS to monitor + */ + vgaxo(Seqx, 8, vgaxi(Seqx,8)|0x06); + seqD = vgaxi(Seqx, 0xD); + seqD &= 0x03; + if(blank) + seqD |= 0x50; + vgaxo(Seqx, 0xD, seqD); + + /* + * Will handle LCD + */ + if(blank) + vgaxo(Seqx, 0x31, vgaxi(Seqx, 0x31) & ~0x10); + else + vgaxo(Seqx, 0x31, vgaxi(Seqx, 0x31) | 0x10); +} + + +void +savageinit(VGAscr *scr) +{ + uchar *mmio; + ulong bd; + + /* if you add chip IDs here be sure to update savagewaitidle */ + switch(scr->id){ + case SAVAGE4: + case PROSAVAGEP: + case PROSAVAGEK: + case PROSAVAGE8: + case SAVAGEIXMV: + case SUPERSAVAGEIXC16: + case SAVAGEMXMV: + break; + default: + print("unknown savage %.4lux\n", scr->id); + return; + } + + mmio = (uchar*)scr->mmio; + if(mmio == nil) { + print("savageinit: no mmio\n"); + return; + } + + /* 2D graphics engine software reset */ + *(ushort*)(mmio+SubsystemCtl) = GeSoftReset; + delay(2); + *(ushort*)(mmio+SubsystemCtl) = 0; + savagewaitidle(scr); + + /* disable BCI as much as possible */ + *(ushort*)(mmio+CobPtr) &= ~CobBciEna; + *(ushort*)(mmio+GBD2) &= ~GBDBciEna; + savagewaitidle(scr); + + /* enable 32-bit writes, disable clipping */ + *(ushort*)(mmio+MfMiscCtl) = MfMiscMagic|W32Ena|ClipDis; + savagewaitidle(scr); + + /* enable all read, write planes */ + *(ulong*)(mmio+BitplaneRmask) = ~0; + *(ulong*)(mmio+BitplaneWmask) = ~0; + savagewaitidle(scr); + + /* turn on linear access, 2D engine */ + *(ulong*)(mmio+AdvFunCtl) |= GeEna|LaEna; + savagewaitidle(scr); + + /* set bitmap descriptors */ + bd = (scr->gscreen->depth<<DepthShift) | + (Dx(scr->gscreen->r)<<StrideShift) | BlockWriteDis + | BDS64; + + *(ulong*)(mmio+GBD1) = 0; + *(ulong*)(mmio+GBD2) = bd; + + *(ulong*)(mmio+PBD1) = 0; + *(ulong*)(mmio+PBD2) = bd; + + *(ulong*)(mmio+SBD1) = 0; + *(ulong*)(mmio+SBD2) = bd; + + /* + * For some reason, the GBD needs to get programmed twice, + * once before the PBD, SBD, and once after. + * This empirically makes it get set right. + * I would like to better understand the ugliness + * going on here. + */ + *(ulong*)(mmio+GBD1) = 0; + *(ulong*)(mmio+GBD2) = bd; + *(ushort*)(mmio+GBD2+2) = bd>>16; + savagewaitidle(scr); + + scr->fill = savagefill; + scr->scroll = savagescroll; + scr->blank = savageblank; + hwblank = 0; +} |
