ref: 8294be6e7c9032e3c472018b53154d5b4faec00c
dir: /os/pc/vgasavage.c/
#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; }