// matroxMga.c -- Matrox Millennium/Mystique/G200/G400 accelerated video driver // // Supports the Matrox MGA family: MGA2064W (Millennium), MGA1064SG // (Mystique), G100, G200, and G400/G450. The Matrox 2D drawing engine // is widely regarded as the best 2D accelerator of the PCI/AGP era, // with features including: // - Solid and pattern rectangle fill // - Screen-to-screen BitBLT (very fast, pipelined) // - CPU-to-screen blit with color expansion (ILOAD) // - Bresenham line draw (antialiased on G200+) // - Trapezoid fill // - Hardware clip rectangle // - 64x64 three-color hardware cursor // // Register access: // The MGA register block is mapped via BAR0 (PCI) or BAR1 // depending on the chip. It's a 16KB MMIO region. The drawing // engine registers start at offset 0x1C00 within this block. // // The drawing engine uses a command-based model: you set up // parameters (colors, coordinates, dimensions) in the setup // registers, then write to DWGCTL to start the operation. // Some operations auto-execute when the last parameter is // written (e.g., LEN triggers a draw). // // FIFO: // The MGA has a deep command FIFO (64 entries on Millennium). // The FIFOSTATUS register indicates how many entries are free. // On G200+, the FIFO is deeper and the STATUS register has // a busy bit that's more reliable. #include "accelVid.h" #include "vgaCommon.h" #include "pci.h" #include #include #include #include #include // ============================================================ // Matrox vendor/device IDs // ============================================================ #define MATROX_VENDOR_ID 0x102B #define MGA_2064W 0x0519 // Millennium #define MGA_1064SG 0x051A // Mystique #define MGA_G100_PCI 0x1000 #define MGA_G100_AGP 0x1001 #define MGA_G200_PCI 0x0521 #define MGA_G200_AGP 0x0520 #define MGA_G400 0x0525 #define MGA_G450 0x2527 static const uint16_t sMatroxDeviceIds[] = { MATROX_VENDOR_ID, MGA_2064W, MATROX_VENDOR_ID, MGA_1064SG, MATROX_VENDOR_ID, MGA_G100_PCI, MATROX_VENDOR_ID, MGA_G100_AGP, MATROX_VENDOR_ID, MGA_G200_PCI, MATROX_VENDOR_ID, MGA_G200_AGP, MATROX_VENDOR_ID, MGA_G400, MATROX_VENDOR_ID, MGA_G450, 0, 0 }; // ============================================================ // MGA drawing engine register offsets (from MMIO base) // ============================================================ // Drawing engine setup registers (0x1C00 - 0x1CFF) #define MGA_DWGCTL 0x1C00 // drawing control #define MGA_MACCESS 0x1C04 // memory access control #define MGA_MCTLWTST 0x1C08 // memory control wait state #define MGA_ZORG 0x1C0C // Z origin #define MGA_PAT0 0x1C10 // pattern register 0 #define MGA_PAT1 0x1C14 // pattern register 1 #define MGA_PLNWT 0x1C1C // plane write mask #define MGA_BCOL 0x1C20 // background color #define MGA_FCOL 0x1C24 // foreground color #define MGA_SRC0 0x1C30 // source data 0 (for color expand) #define MGA_SRC1 0x1C34 #define MGA_SRC2 0x1C38 #define MGA_SRC3 0x1C3C #define MGA_XYSTRT 0x1C40 // XY start (for lines) #define MGA_XYEND 0x1C44 // XY end (triggers line draw) #define MGA_SHIFT 0x1C50 #define MGA_SGN 0x1C58 // sign register #define MGA_LEN 0x1C5C // number of lines (triggers rect ops) #define MGA_AR0 0x1C60 // line draw parameter 0 #define MGA_AR1 0x1C64 #define MGA_AR2 0x1C68 #define MGA_AR3 0x1C6C #define MGA_AR4 0x1C70 #define MGA_AR5 0x1C74 #define MGA_AR6 0x1C78 #define MGA_CXBNDRY 0x1C80 // clip X boundaries (left | right<<16) #define MGA_FXBNDRY 0x1C84 // fill X boundaries (left | right<<16) #define MGA_YDSTLEN 0x1C88 // Y dest and length (triggers fill) #define MGA_PITCH 0x1C8C // destination pitch (in pixels) #define MGA_YDST 0x1C90 // Y destination #define MGA_YDSTORG 0x1C94 // Y destination origin (byte offset) #define MGA_YTOP 0x1C98 // clip Y top #define MGA_YBOT 0x1C9C // clip Y bottom #define MGA_CXLEFT 0x1CA0 // clip X left #define MGA_CXRIGHT 0x1CA4 // clip X right #define MGA_FXLEFT 0x1CA8 // fill X left #define MGA_FXRIGHT 0x1CAC // fill X right #define MGA_XDST 0x1CB0 // X destination // Status registers (0x1E00 - 0x1EFF) #define MGA_FIFOSTATUS 0x1E10 // FIFO status #define MGA_STATUS 0x1E14 // engine status #define MGA_ICLEAR 0x1E18 // interrupt clear #define MGA_IEN 0x1E1C // interrupt enable // Source window (for BitBLT) #define MGA_SRCORG 0x2CB4 // source origin // DWGSYNC for synchronization #define MGA_DWGSYNC 0x2C4C // ============================================================ // MGA DWGCTL command values // ============================================================ // // The DWGCTL register is a 32-bit command word that encodes the // operation type, drawing options, and raster operation. // Operation codes (bits 3:0) #define MGA_OPCOD_LINE_OPEN 0x00 // line (open) #define MGA_OPCOD_AUTOLINE_OPEN 0x01 #define MGA_OPCOD_LINE_CLOSE 0x02 // line (closed) #define MGA_OPCOD_AUTOLINE_CLOSE 0x03 #define MGA_OPCOD_TRAP 0x04 // trapezoid fill #define MGA_OPCOD_TEXTURE 0x05 // texture mapping (G200+) #define MGA_OPCOD_BITBLT 0x08 // screen-to-screen blit #define MGA_OPCOD_ILOAD 0x09 // CPU-to-screen (image load) #define MGA_OPCOD_IDUMP 0x0A // screen-to-CPU // Drawing options (bits 31:4) #define MGA_ATYPE_RPL 0x0000 // replace #define MGA_ATYPE_RSTR 0x0010 // raster #define MGA_ATYPE_ZI 0x0030 // Z interpolate #define MGA_ATYPE_BLK 0x0040 // block transfer #define MGA_ATYPE_I 0x0070 // interpolate #define MGA_ZMODE_NOZCMP 0x0000 // no Z compare #define MGA_ZMODE_ZE 0x0200 // Z equal #define MGA_ZMODE_ZNE 0x0300 // Z not equal #define MGA_SOLID 0x0800 // solid fill (no pattern) #define MGA_ARZERO 0x1000 // AR regs are zero (solid fill optimization) #define MGA_SGNZERO 0x2000 // SGN reg is zero #define MGA_SHFTZERO 0x4000 // SHIFT reg is zero #define MGA_BOP_MASK 0x000F0000 // boolean operation (ROP) mask #define MGA_BOP_SHIFT 16 // Boolean operations (ROP2, bits 19:16) #define MGA_BOP_CLEAR (0x0 << MGA_BOP_SHIFT) #define MGA_BOP_NOR (0x1 << MGA_BOP_SHIFT) #define MGA_BOP_COPYINV (0x3 << MGA_BOP_SHIFT) #define MGA_BOP_AND (0x8 << MGA_BOP_SHIFT) #define MGA_BOP_XOR (0x6 << MGA_BOP_SHIFT) #define MGA_BOP_COPY (0xC << MGA_BOP_SHIFT) #define MGA_BOP_OR (0xE << MGA_BOP_SHIFT) #define MGA_BOP_SET (0xF << MGA_BOP_SHIFT) // Transparency #define MGA_TRANSC 0x00100000 // transparent color compare #define MGA_BLTMOD_BFCOL 0x04000000 // BLT mode: foreground color #define MGA_BLTMOD_BU32RGB 0x0C000000 // BLT mode: 32bpp ILOAD #define MGA_BLTMOD_BMONOWF 0x08000000 // BLT mode: mono word expand MSB first // Pattern #define MGA_PATTERN 0x20000000 // enable pattern // Linear source #define MGA_LINEAR 0x80000000 // linear addressing (not XY) // ============================================================ // MGA MACCESS values // ============================================================ #define MGA_MACCESS_8BPP 0x00 #define MGA_MACCESS_16BPP 0x01 #define MGA_MACCESS_32BPP 0x02 #define MGA_MACCESS_24BPP 0x03 // ============================================================ // MGA SGN register bits // ============================================================ #define MGA_SGN_SCANLEFT 0x01 // scan direction left #define MGA_SGN_SCANRIGHT 0x00 // scan direction right #define MGA_SGN_SDY_NEG 0x02 // negative Y direction #define MGA_SGN_SDX_NEG 0x04 // negative X direction // ============================================================ // MGA STATUS register bits // ============================================================ #define MGA_STATUS_BUSY 0x00010000 // drawing engine busy #define MGA_FIFO_FULL_MASK 0x0000007F // FIFO free count // Maximum wait iterations #define MGA_MAX_IDLE_WAIT 1000000 // Hardware cursor #define MGA_HW_CURSOR_SIZE 64 #define MGA_HW_CURSOR_BYTES 1024 // ============================================================ // Private driver state // ============================================================ typedef struct { uint32_t lfbPhysAddr; uint32_t mmioPhysAddr; uint32_t vramSize; uint32_t cursorOffset; int32_t bytesPerPixel; int32_t screenPitch; volatile uint32_t *mmio; // mapped MMIO base DpmiMappingT lfbMapping; DpmiMappingT mmioMapping; bool isG200Plus; // G200/G400/G450 } MatroxPrivateT; // ============================================================ // Prototypes // ============================================================ static void mgaBitBlt(AccelDriverT *drv, int32_t srcX, int32_t srcY, int32_t dstX, int32_t dstY, int32_t w, int32_t h); static void mgaColorExpand(AccelDriverT *drv, const uint8_t *srcBuf, int32_t srcPitch, int32_t dstX, int32_t dstY, int32_t w, int32_t h, uint32_t fg, uint32_t bg); static bool mgaDetect(AccelDriverT *drv); static void mgaHostBlit(AccelDriverT *drv, const uint8_t *srcBuf, int32_t srcPitch, int32_t dstX, int32_t dstY, int32_t w, int32_t h); static bool mgaInit(AccelDriverT *drv, const AccelModeRequestT *req); static void mgaLineDraw(AccelDriverT *drv, int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint32_t color); static void mgaMoveCursor(AccelDriverT *drv, int32_t x, int32_t y); static void mgaRectFill(AccelDriverT *drv, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color); static void mgaRectFillPat(AccelDriverT *drv, int32_t x, int32_t y, int32_t w, int32_t h, const uint8_t *pattern, uint32_t fg, uint32_t bg); static void mgaSetClip(AccelDriverT *drv, int32_t x, int32_t y, int32_t w, int32_t h); static void mgaSetCursor(AccelDriverT *drv, const HwCursorImageT *image); static void mgaShowCursor(AccelDriverT *drv, bool visible); static void mgaShutdown(AccelDriverT *drv); static void mgaWaitFifo(MatroxPrivateT *priv, int32_t entries); static void mgaWaitIdle(AccelDriverT *drv); static inline void mgaWrite(MatroxPrivateT *priv, uint32_t reg, uint32_t val) { priv->mmio[reg / 4] = val; } static inline uint32_t mgaRead(MatroxPrivateT *priv, uint32_t reg) { return priv->mmio[reg / 4]; } // ============================================================ // Driver instance // ============================================================ static MatroxPrivateT sMatroxPrivate; static AccelDriverT sMatroxDriver = { .name = "Matrox Millennium", .chipFamily = "matrox", .caps = 0, .privData = &sMatroxPrivate, .detect = mgaDetect, .init = mgaInit, .shutdown = mgaShutdown, .waitIdle = mgaWaitIdle, .setClip = mgaSetClip, .rectFill = mgaRectFill, .rectFillPat = mgaRectFillPat, .bitBlt = mgaBitBlt, .hostBlit = mgaHostBlit, .colorExpand = mgaColorExpand, .lineDraw = mgaLineDraw, .setCursor = mgaSetCursor, .moveCursor = mgaMoveCursor, .showCursor = mgaShowCursor, }; // ============================================================ // mgaRegisterDriver // ============================================================ void mgaRegisterDriver(void) { accelRegisterDriver(&sMatroxDriver); } // ============================================================ // mgaBitBlt // ============================================================ // // Screen-to-screen BitBLT using the MGA BITBLT opcode. // The MGA engine uses pixel coordinates and pitch, with the // sign register controlling direction for overlapping blits. static void mgaBitBlt(AccelDriverT *drv, int32_t srcX, int32_t srcY, int32_t dstX, int32_t dstY, int32_t w, int32_t h) { MatroxPrivateT *priv = (MatroxPrivateT *)drv->privData; if (w <= 0 || h <= 0) { return; } // Determine direction uint32_t sgn = 0; int32_t startX; int32_t endX; int32_t startY = dstY; uint32_t srcOrg = srcY * priv->screenPitch + srcX * priv->bytesPerPixel; if (dstX <= srcX) { // Left to right startX = dstX; endX = dstX + w - 1; } else { // Right to left startX = dstX + w - 1; endX = dstX; sgn |= MGA_SGN_SCANLEFT; srcOrg = srcY * priv->screenPitch + (srcX + w - 1) * priv->bytesPerPixel; } if (dstY > srcY) { // Bottom to top sgn |= MGA_SGN_SDY_NEG; startY = dstY + h - 1; srcOrg = (srcY + h - 1) * priv->screenPitch + srcX * priv->bytesPerPixel; if (sgn & MGA_SGN_SCANLEFT) { srcOrg = (srcY + h - 1) * priv->screenPitch + (srcX + w - 1) * priv->bytesPerPixel; } } mgaWaitFifo(priv, 8); mgaWrite(priv, MGA_DWGCTL, MGA_OPCOD_BITBLT | MGA_ATYPE_BLK | MGA_BOP_COPY | MGA_SHFTZERO); mgaWrite(priv, MGA_SGN, sgn); mgaWrite(priv, MGA_PLNWT, 0xFFFFFFFF); mgaWrite(priv, MGA_SRCORG, srcOrg); mgaWrite(priv, MGA_AR5, (sgn & MGA_SGN_SDY_NEG) ? -(priv->screenPitch / priv->bytesPerPixel) : (priv->screenPitch / priv->bytesPerPixel)); // Set boundaries and trigger mgaWrite(priv, MGA_FXBNDRY, ((uint32_t)endX << 16) | (uint32_t)(startX & 0xFFFF)); mgaWrite(priv, MGA_YDSTLEN, ((uint32_t)startY << 16) | (uint32_t)h); } // ============================================================ // mgaColorExpand // ============================================================ // // CPU-to-screen monochrome color expansion using the MGA ILOAD // opcode with BLTMOD_BMONOWF. Monochrome bitmap bits are expanded // to foreground/background colors by the hardware. Data is fed // as dwords through MGA_SRC0. static void mgaColorExpand(AccelDriverT *drv, const uint8_t *srcBuf, int32_t srcPitch, int32_t dstX, int32_t dstY, int32_t w, int32_t h, uint32_t fg, uint32_t bg) { MatroxPrivateT *priv = (MatroxPrivateT *)drv->privData; if (w <= 0 || h <= 0) { return; } int32_t bytesPerRow = (w + 7) / 8; int32_t dwordsPerRow = (bytesPerRow + 3) / 4; mgaWaitFifo(priv, 6); mgaWrite(priv, MGA_DWGCTL, MGA_OPCOD_ILOAD | MGA_ATYPE_RPL | MGA_BOP_COPY | MGA_BLTMOD_BMONOWF | MGA_SHFTZERO | MGA_SGNZERO); mgaWrite(priv, MGA_FCOL, fg); mgaWrite(priv, MGA_BCOL, bg); mgaWrite(priv, MGA_PLNWT, 0xFFFFFFFF); mgaWrite(priv, MGA_FXBNDRY, (uint32_t)dstX | ((uint32_t)(dstX + w) << 16)); mgaWrite(priv, MGA_YDSTLEN, ((uint32_t)dstY << 16) | (uint32_t)h); // Feed monochrome data row by row for (int32_t row = 0; row < h; row++) { const uint8_t *rowPtr = srcBuf + row * srcPitch; for (int32_t dw = 0; dw < dwordsPerRow; dw++) { uint32_t val = 0; int32_t offset = dw * 4; for (int32_t b = 0; b < 4; b++) { if (offset + b < bytesPerRow) { val |= (uint32_t)rowPtr[offset + b] << (b * 8); } } mgaWaitFifo(priv, 1); mgaWrite(priv, MGA_SRC0, val); } } } // ============================================================ // mgaDetect // ============================================================ static bool mgaDetect(AccelDriverT *drv) { int32_t matchIdx; if (!pciFindDeviceList(sMatroxDeviceIds, &drv->pciDev, &matchIdx)) { return false; } MatroxPrivateT *priv = (MatroxPrivateT *)drv->privData; switch (drv->pciDev.deviceId) { case MGA_2064W: drv->name = "Matrox Millennium"; priv->isG200Plus = false; break; case MGA_1064SG: drv->name = "Matrox Mystique"; priv->isG200Plus = false; break; case MGA_G100_PCI: case MGA_G100_AGP: drv->name = "Matrox G100"; priv->isG200Plus = true; break; case MGA_G200_PCI: case MGA_G200_AGP: drv->name = "Matrox G200"; priv->isG200Plus = true; break; case MGA_G400: drv->name = "Matrox G400"; priv->isG200Plus = true; break; case MGA_G450: drv->name = "Matrox G450"; priv->isG200Plus = true; break; default: drv->name = "Matrox MGA"; priv->isG200Plus = false; break; } return true; } // ============================================================ // mgaHostBlit // ============================================================ // // CPU-to-screen blit using the MGA ILOAD opcode. Pixel data is // written from host memory to the framebuffer through the MMIO // window via MGA_SRC0. Each row is padded to a dword boundary. static void mgaHostBlit(AccelDriverT *drv, const uint8_t *srcBuf, int32_t srcPitch, int32_t dstX, int32_t dstY, int32_t w, int32_t h) { MatroxPrivateT *priv = (MatroxPrivateT *)drv->privData; if (w <= 0 || h <= 0) { return; } int32_t bytesPerRow = w * priv->bytesPerPixel; int32_t dwordsPerRow = (bytesPerRow + 3) / 4; mgaWaitFifo(priv, 5); mgaWrite(priv, MGA_DWGCTL, MGA_OPCOD_ILOAD | MGA_ATYPE_RPL | MGA_BOP_COPY | MGA_SHFTZERO | MGA_SGNZERO); mgaWrite(priv, MGA_FCOL, 0xFFFFFFFF); mgaWrite(priv, MGA_PLNWT, 0xFFFFFFFF); mgaWrite(priv, MGA_FXBNDRY, (uint32_t)dstX | ((uint32_t)(dstX + w) << 16)); mgaWrite(priv, MGA_YDSTLEN, ((uint32_t)dstY << 16) | (uint32_t)h); // Feed pixel data row by row for (int32_t row = 0; row < h; row++) { const uint8_t *rowPtr = srcBuf + row * srcPitch; for (int32_t dw = 0; dw < dwordsPerRow; dw++) { uint32_t val = 0; int32_t offset = dw * 4; for (int32_t b = 0; b < 4; b++) { if (offset + b < bytesPerRow) { val |= (uint32_t)rowPtr[offset + b] << (b * 8); } } mgaWaitFifo(priv, 1); mgaWrite(priv, MGA_SRC0, val); } } } // ============================================================ // mgaInit // ============================================================ static bool mgaInit(AccelDriverT *drv, const AccelModeRequestT *req) { MatroxPrivateT *priv = (MatroxPrivateT *)drv->privData; // BAR layout depends on chip: // Millennium (2064W): BAR0 = control regs (16KB), BAR1 = framebuffer // Mystique+: BAR0 = control regs (16KB), BAR1 = framebuffer uint32_t bar0 = pciRead32(drv->pciDev.bus, drv->pciDev.dev, drv->pciDev.func, PCI_BAR0); uint32_t bar1 = pciRead32(drv->pciDev.bus, drv->pciDev.dev, drv->pciDev.func, PCI_BAR1); priv->mmioPhysAddr = bar0 & 0xFFFFFFF0; priv->lfbPhysAddr = bar1 & 0xFFFFFFF0; // Size the framebuffer BAR priv->vramSize = pciSizeBar(drv->pciDev.bus, drv->pciDev.dev, drv->pciDev.func, PCI_BAR1); // Map MMIO control registers (16KB) if (!dpmiMapFramebuffer(priv->mmioPhysAddr, 16384, &priv->mmioMapping)) { return false; } priv->mmio = (volatile uint32_t *)priv->mmioMapping.ptr; // Find and set VESA mode VesaModeResultT vesa; if (!vesaFindAndSetMode(req->width, req->height, req->bpp, &vesa)) { dpmiUnmapFramebuffer(&priv->mmioMapping); return false; } // Map framebuffer if (!dpmiMapFramebuffer(priv->lfbPhysAddr, priv->vramSize, &priv->lfbMapping)) { dpmiUnmapFramebuffer(&priv->mmioMapping); vgaRestoreTextMode(); return false; } priv->bytesPerPixel = (vesa.bpp + 7) / 8; priv->screenPitch = vesa.pitch; drv->mode.width = vesa.width; drv->mode.height = vesa.height; drv->mode.bpp = vesa.bpp; drv->mode.pitch = vesa.pitch; drv->mode.framebuffer = priv->lfbMapping.ptr; drv->mode.vramSize = priv->vramSize; drv->mode.offscreenBase = vesa.pitch * vesa.height; // Configure MACCESS for pixel depth uint32_t maccess; switch (vesa.bpp) { case 8: maccess = MGA_MACCESS_8BPP; break; case 15: case 16: maccess = MGA_MACCESS_16BPP; break; case 32: maccess = MGA_MACCESS_32BPP; break; default: maccess = MGA_MACCESS_16BPP; break; } mgaWaitIdle(drv); mgaWrite(priv, MGA_MACCESS, maccess); // Set pitch (in pixels) mgaWrite(priv, MGA_PITCH, vesa.pitch / priv->bytesPerPixel); // Set YDSTORG to 0 (framebuffer starts at beginning of VRAM) mgaWrite(priv, MGA_YDSTORG, 0); // Plane write mask: all bits mgaWrite(priv, MGA_PLNWT, 0xFFFFFFFF); // Set up cursor at end of VRAM priv->cursorOffset = priv->vramSize - MGA_HW_CURSOR_BYTES; priv->cursorOffset &= ~(MGA_HW_CURSOR_BYTES - 1); drv->caps = ACAP_RECT_FILL | ACAP_RECT_FILL_PAT | ACAP_BITBLT | ACAP_HOST_BLIT | ACAP_COLOR_EXPAND | ACAP_LINE_DRAW | ACAP_HW_CURSOR | ACAP_CLIP; // Full screen clip mgaSetClip(drv, 0, 0, vesa.width, vesa.height); return true; } // ============================================================ // mgaLineDraw // ============================================================ // // Line drawing using the MGA AUTOLINE opcode. The MGA engine // takes start XY and end XY coordinates directly (no Bresenham // parameter computation needed on the CPU side). static void mgaLineDraw(AccelDriverT *drv, int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint32_t color) { MatroxPrivateT *priv = (MatroxPrivateT *)drv->privData; mgaWaitFifo(priv, 5); mgaWrite(priv, MGA_DWGCTL, MGA_OPCOD_AUTOLINE_CLOSE | MGA_ATYPE_RPL | MGA_SOLID | MGA_BOP_COPY | MGA_SHFTZERO | MGA_SGNZERO | MGA_ARZERO); mgaWrite(priv, MGA_FCOL, color); mgaWrite(priv, MGA_PLNWT, 0xFFFFFFFF); // Start coordinate mgaWrite(priv, MGA_XYSTRT, ((uint32_t)(y1 & 0xFFFF) << 16) | (uint32_t)(x1 & 0xFFFF)); // End coordinate (triggers draw) mgaWrite(priv, MGA_XYEND, ((uint32_t)(y2 & 0xFFFF) << 16) | (uint32_t)(x2 & 0xFFFF)); } // ============================================================ // mgaMoveCursor // ============================================================ // // Matrox cursor position is set via RAMDAC registers. // On Millennium: TVP3026 RAMDAC external registers. // On Mystique+: integrated RAMDAC at MMIO offset 0x3C00+. static void mgaMoveCursor(AccelDriverT *drv, int32_t x, int32_t y) { MatroxPrivateT *priv = (MatroxPrivateT *)drv->privData; if (x < 0) { x = 0; } if (y < 0) { y = 0; } // Cursor position via DAC registers (Mystique/G200+ integrated DAC) // CURPOS register at MMIO + 0x3C0C mgaWrite(priv, 0x3C0C, ((uint32_t)(y & 0xFFF) << 16) | (uint32_t)(x & 0xFFF)); } // ============================================================ // mgaRectFill // ============================================================ // // Solid rectangle fill using the MGA TRAP opcode with the SOLID // bit set. This is the fastest path for solid fills -- the // engine fills with the foreground color using the ARZERO and // SGNZERO hints to skip setup of unused registers. static void mgaRectFill(AccelDriverT *drv, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) { MatroxPrivateT *priv = (MatroxPrivateT *)drv->privData; if (w <= 0 || h <= 0) { return; } mgaWaitFifo(priv, 5); mgaWrite(priv, MGA_DWGCTL, MGA_OPCOD_TRAP | MGA_ATYPE_BLK | MGA_SOLID | MGA_BOP_COPY | MGA_ARZERO | MGA_SGNZERO | MGA_SHFTZERO); mgaWrite(priv, MGA_FCOL, color); // Set X boundaries mgaWrite(priv, MGA_FXBNDRY, ((uint32_t)(x + w) << 16) | (uint32_t)(x & 0xFFFF)); // Set Y destination and length (triggers fill) mgaWrite(priv, MGA_YDSTLEN, ((uint32_t)(y & 0xFFFF) << 16) | (uint32_t)(h & 0xFFFF)); } // ============================================================ // mgaRectFillPat // ============================================================ // // 8x8 mono pattern fill using the MGA TRAP opcode with the // MGA_PATTERN bit set. The pattern is 8 bytes (one per row, // MSB-first), loaded into PAT0 (rows 0-3) and PAT1 (rows 4-7). // 1-bits use the foreground color, 0-bits use the background. static void mgaRectFillPat(AccelDriverT *drv, int32_t x, int32_t y, int32_t w, int32_t h, const uint8_t *pattern, uint32_t fg, uint32_t bg) { MatroxPrivateT *priv = (MatroxPrivateT *)drv->privData; if (w <= 0 || h <= 0) { return; } // Pack pattern rows 0-3 into PAT0 and rows 4-7 into PAT1 uint32_t pat0 = (uint32_t)pattern[0] | ((uint32_t)pattern[1] << 8) | ((uint32_t)pattern[2] << 16) | ((uint32_t)pattern[3] << 24); uint32_t pat1 = (uint32_t)pattern[4] | ((uint32_t)pattern[5] << 8) | ((uint32_t)pattern[6] << 16) | ((uint32_t)pattern[7] << 24); mgaWaitFifo(priv, 8); mgaWrite(priv, MGA_DWGCTL, MGA_OPCOD_TRAP | MGA_ATYPE_RPL | MGA_PATTERN | MGA_BOP_COPY | MGA_ARZERO | MGA_SGNZERO | MGA_SHFTZERO); mgaWrite(priv, MGA_FCOL, fg); mgaWrite(priv, MGA_BCOL, bg); mgaWrite(priv, MGA_PAT0, pat0); mgaWrite(priv, MGA_PAT1, pat1); mgaWrite(priv, MGA_PLNWT, 0xFFFFFFFF); // Set X boundaries and trigger fill mgaWrite(priv, MGA_FXBNDRY, ((uint32_t)(x + w) << 16) | (uint32_t)(x & 0xFFFF)); mgaWrite(priv, MGA_YDSTLEN, ((uint32_t)(y & 0xFFFF) << 16) | (uint32_t)(h & 0xFFFF)); } // ============================================================ // mgaSetClip // ============================================================ static void mgaSetClip(AccelDriverT *drv, int32_t x, int32_t y, int32_t w, int32_t h) { MatroxPrivateT *priv = (MatroxPrivateT *)drv->privData; mgaWaitFifo(priv, 3); mgaWrite(priv, MGA_CXBNDRY, ((uint32_t)(x + w - 1) << 16) | (uint32_t)(x & 0xFFFF)); mgaWrite(priv, MGA_YTOP, y * (priv->screenPitch / priv->bytesPerPixel)); mgaWrite(priv, MGA_YBOT, (y + h - 1) * (priv->screenPitch / priv->bytesPerPixel)); } // ============================================================ // mgaSetCursor // ============================================================ static void mgaSetCursor(AccelDriverT *drv, const HwCursorImageT *image) { MatroxPrivateT *priv = (MatroxPrivateT *)drv->privData; if (!image) { mgaShowCursor(drv, false); return; } mgaWaitIdle(drv); uint8_t *cursorMem = drv->mode.framebuffer + priv->cursorOffset; for (int32_t row = 0; row < MGA_HW_CURSOR_SIZE; row++) { for (int32_t byte = 0; byte < 8; byte++) { int32_t srcIdx = row * 8 + byte; uint8_t andByte; uint8_t xorByte; if (row < image->height && byte < (image->width + 7) / 8) { andByte = image->andMask[srcIdx]; xorByte = image->xorMask[srcIdx]; } else { andByte = 0xFF; xorByte = 0x00; } cursorMem[row * 16 + byte] = andByte; cursorMem[row * 16 + byte + 8] = xorByte; } } // Set cursor base address via DAC register // CURBASE at MMIO + 0x3C04 mgaWrite(priv, 0x3C04, priv->cursorOffset); } // ============================================================ // mgaShowCursor // ============================================================ static void mgaShowCursor(AccelDriverT *drv, bool visible) { MatroxPrivateT *priv = (MatroxPrivateT *)drv->privData; // CURCTL at MMIO + 0x3C00 uint32_t curCtl = mgaRead(priv, 0x3C00); if (visible) { curCtl |= 0x01; // enable cursor } else { curCtl &= ~0x01; } mgaWrite(priv, 0x3C00, curCtl); } // ============================================================ // mgaShutdown // ============================================================ static void mgaShutdown(AccelDriverT *drv) { MatroxPrivateT *priv = (MatroxPrivateT *)drv->privData; mgaShowCursor(drv, false); dpmiUnmapFramebuffer(&priv->mmioMapping); dpmiUnmapFramebuffer(&priv->lfbMapping); vgaRestoreTextMode(); } // ============================================================ // mgaWaitFifo // ============================================================ // // Wait until the MGA FIFO has enough free entries. // FIFOSTATUS bits 6:0 indicate the number of free slots. static void mgaWaitFifo(MatroxPrivateT *priv, int32_t entries) { for (int32_t i = 0; i < MGA_MAX_IDLE_WAIT; i++) { uint32_t stat = mgaRead(priv, MGA_FIFOSTATUS); int32_t free = stat & MGA_FIFO_FULL_MASK; if (free >= entries) { return; } } } // ============================================================ // mgaWaitIdle // ============================================================ static void mgaWaitIdle(AccelDriverT *drv) { MatroxPrivateT *priv = (MatroxPrivateT *)drv->privData; for (int32_t i = 0; i < MGA_MAX_IDLE_WAIT; i++) { uint32_t stat = mgaRead(priv, MGA_STATUS); if (!(stat & MGA_STATUS_BUSY)) { return; } } }