// banshee.c -- 3dfx Banshee/Voodoo3 accelerated video driver // // Supports the 3dfx Banshee and Voodoo3 2D/3D accelerators. // The Banshee was 3dfx's first 2D/3D combo chip, and the Voodoo3 // improved on it with higher clock speeds. Both share the same // 2D register interface: // - Hardware rectangle fill // - Screen-to-screen BitBLT // - CPU-to-screen blit (host blit via launch area) // - Monochrome color expansion (host blit with mono source) // - Bresenham line draw // - Hardware clip rectangle // - 64x64 hardware cursor // // Register access: // BAR0 maps the 32KB MMIO register block. The 2D engine // registers live at offsets 0x200-0x270 within this block. // The status register at 0x100 provides engine busy state. // // For host-to-screen operations, pixel data is fed through the // "launch area" -- a write-combining window at MMIO physical // address + 0x80000. Data is written as 32-bit dwords. // // BAR1 maps the linear framebuffer. #include "accelVid.h" #include "vgaCommon.h" #include "pci.h" #include #include #include #include #include // ============================================================ // 3dfx vendor/device IDs // ============================================================ #define TDFX_VENDOR_ID 0x121A #define TDFX_BANSHEE 0x0003 #define TDFX_VOODOO3 0x0005 static const uint16_t sBansheeDeviceIds[] = { TDFX_VENDOR_ID, TDFX_BANSHEE, TDFX_VENDOR_ID, TDFX_VOODOO3, 0, 0 }; // ============================================================ // 2D engine register offsets (from MMIO base) // ============================================================ #define BAN_STATUS 0x100 // bits 0-10: busy when any set #define BAN_INTRCTRL 0x108 // interrupt control #define BAN_CLIP0MIN 0x200 // clip rect 0 min (X | Y<<16) #define BAN_CLIP0MAX 0x204 // clip rect 0 max (X | Y<<16) #define BAN_DSTBASEADDR 0x208 // destination base address #define BAN_DSTFORMAT 0x20C // pitch<<16 | bpp encoding #define BAN_SRCCKMIN 0x210 // source color key min #define BAN_SRCCKMAX 0x214 // source color key max #define BAN_DSTCKMIN 0x218 // dest color key min #define BAN_DSTCKMAX 0x21C // dest color key max #define BAN_BRESERROR0 0x220 // Bresenham error 0 #define BAN_BRESERROR1 0x224 // Bresenham error 1 #define BAN_ROP 0x230 // raster operation (bits 7:0) #define BAN_SRCBASEADDR 0x234 // source base address #define BAN_COMMANDEXTRA 0x238 // command extra #define BAN_LINESTIPPLE 0x23C // line stipple #define BAN_LINESTYLE 0x240 // line style #define BAN_PATTERN0 0x244 // pattern alias 0 #define BAN_PATTERN1 0x248 // pattern alias 1 #define BAN_CLIP1MIN 0x24C // clip rect 1 min #define BAN_CLIP1MAX 0x250 // clip rect 1 max #define BAN_SRCFORMAT 0x254 // pitch<<16 | bpp encoding #define BAN_SRCSIZE 0x258 // width | height<<16 #define BAN_SRCXY 0x25C // X | Y<<16 #define BAN_COLORBACK 0x260 // background color #define BAN_COLORFORE 0x264 // foreground color #define BAN_DSTSIZE 0x268 // width | height<<16 #define BAN_DSTXY 0x26C // X | Y<<16 #define BAN_COMMAND 0x270 // command (triggers operation) // ============================================================ // Command register encoding // ============================================================ // Command types (bits 3:0) #define BAN_CMD_NOP 0x00 #define BAN_CMD_S2S_BLIT 0x01 // screen-to-screen blit #define BAN_CMD_S2S_STRETCH 0x02 // screen-to-screen stretch blit #define BAN_CMD_H2S_BLIT 0x03 // host-to-screen blit #define BAN_CMD_RECTFILL 0x05 // rectangle fill #define BAN_CMD_LINEDRAW 0x06 // line draw #define BAN_CMD_POLYLINE 0x07 // polyline // Command flags #define BAN_CMD_INITIATE (1 << 4) // must be set to start operation #define BAN_CMD_STIPPLE (1 << 8) // stipple line #define BAN_CMD_CLIPSEL1 (1 << 9) // use clip1 instead of clip0 #define BAN_CMD_SRCCKENA (1 << 12) // source color key enable #define BAN_CMD_DSTCKENA (1 << 13) // dest color key enable #define BAN_CMD_MONOPAT (1 << 14) // mono pattern #define BAN_CMD_SRCMONO (1 << 15) // source is monochrome // ============================================================ // BPP format encodings (for srcFormat/dstFormat low bits) // ============================================================ #define BAN_FMT_8BPP 1 #define BAN_FMT_16BPP 3 #define BAN_FMT_32BPP 5 // ============================================================ // Status register // ============================================================ #define BAN_STATUS_BUSY_MASK 0x7FF // bits 0-10: engine busy // ============================================================ // Hardware cursor registers // ============================================================ #define BAN_VIDPROCCFG 0x5C // bit 27 = cursor enable #define BAN_CURSORLOC 0x60 // X | Y<<16 #define BAN_CURSOR_ENABLE (1 << 27) // ============================================================ // Launch area // ============================================================ #define BAN_LAUNCH_OFFSET 0x80000 // offset from MMIO phys base #define BAN_LAUNCH_MAP_SIZE 4096 // map 4KB of launch area // ============================================================ // Misc constants // ============================================================ #define BAN_MMIO_SIZE 32768 // BAR0: 32KB MMIO #define BAN_MAX_IDLE_WAIT 1000000 #define BAN_ROP_COPY 0xCC #define BAN_HW_CURSOR_SIZE 64 // ============================================================ // Private driver state // ============================================================ typedef struct { uint32_t lfbPhysAddr; uint32_t mmioPhysAddr; uint32_t vramSize; int32_t bytesPerPixel; int32_t screenPitch; uint32_t bppFormat; volatile uint32_t *mmio; volatile uint32_t *launch; DpmiMappingT mmioMap; DpmiMappingT lfbMap; DpmiMappingT launchMap; } BansheePrivateT; // ============================================================ // Prototypes // ============================================================ static void bansheeBitBlt(AccelDriverT *drv, int32_t srcX, int32_t srcY, int32_t dstX, int32_t dstY, int32_t w, int32_t h); static void bansheeColorExpand(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 bansheeDetect(AccelDriverT *drv); static void bansheeHostBlit(AccelDriverT *drv, const uint8_t *srcBuf, int32_t srcPitch, int32_t dstX, int32_t dstY, int32_t w, int32_t h); static bool bansheeInit(AccelDriverT *drv, const AccelModeRequestT *req); static void bansheeLineDraw(AccelDriverT *drv, int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint32_t color); static void bansheeMoveCursor(AccelDriverT *drv, int32_t x, int32_t y); static void bansheeRectFill(AccelDriverT *drv, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color); static void bansheeRectFillPat(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 bansheeSetClip(AccelDriverT *drv, int32_t x, int32_t y, int32_t w, int32_t h); static void bansheeSetCursor(AccelDriverT *drv, const HwCursorImageT *image); static void bansheeShowCursor(AccelDriverT *drv, bool visible); static void bansheeShutdown(AccelDriverT *drv); static void bansheeWaitIdle(AccelDriverT *drv); static uint32_t bppToFormat(int32_t bpp); static inline void bansheeWrite(BansheePrivateT *priv, uint32_t reg, uint32_t val) { priv->mmio[reg / 4] = val; } static inline uint32_t bansheeRead(BansheePrivateT *priv, uint32_t reg) { return priv->mmio[reg / 4]; } // ============================================================ // Driver instance // ============================================================ static BansheePrivateT sBansheePrivate; static AccelDriverT sBansheeDriver = { .name = "3dfx Banshee", .chipFamily = "3dfx", .caps = 0, .privData = &sBansheePrivate, .detect = bansheeDetect, .init = bansheeInit, .shutdown = bansheeShutdown, .waitIdle = bansheeWaitIdle, .setClip = bansheeSetClip, .rectFill = bansheeRectFill, .rectFillPat = bansheeRectFillPat, .bitBlt = bansheeBitBlt, .hostBlit = bansheeHostBlit, .colorExpand = bansheeColorExpand, .lineDraw = bansheeLineDraw, .setCursor = bansheeSetCursor, .moveCursor = bansheeMoveCursor, .showCursor = bansheeShowCursor, }; // ============================================================ // bansheeRegisterDriver // ============================================================ void bansheeRegisterDriver(void) { accelRegisterDriver(&sBansheeDriver); } // ============================================================ // bansheeBitBlt // ============================================================ // // Screen-to-screen BitBLT. The Banshee engine handles overlapping // regions automatically when srcXY and dstXY are set correctly -- // the hardware determines the blit direction internally. static void bansheeBitBlt(AccelDriverT *drv, int32_t srcX, int32_t srcY, int32_t dstX, int32_t dstY, int32_t w, int32_t h) { BansheePrivateT *priv = (BansheePrivateT *)drv->privData; if (w <= 0 || h <= 0) { return; } bansheeWaitIdle(drv); bansheeWrite(priv, BAN_SRCBASEADDR, 0); bansheeWrite(priv, BAN_DSTBASEADDR, 0); bansheeWrite(priv, BAN_SRCFORMAT, ((uint32_t)priv->screenPitch << 16) | priv->bppFormat); bansheeWrite(priv, BAN_DSTFORMAT, ((uint32_t)priv->screenPitch << 16) | priv->bppFormat); bansheeWrite(priv, BAN_ROP, BAN_ROP_COPY); bansheeWrite(priv, BAN_SRCSIZE, (uint32_t)w | ((uint32_t)h << 16)); bansheeWrite(priv, BAN_SRCXY, (uint32_t)srcX | ((uint32_t)srcY << 16)); bansheeWrite(priv, BAN_DSTSIZE, (uint32_t)w | ((uint32_t)h << 16)); bansheeWrite(priv, BAN_DSTXY, (uint32_t)dstX | ((uint32_t)dstY << 16)); bansheeWrite(priv, BAN_COMMAND, BAN_CMD_S2S_BLIT | BAN_CMD_INITIATE); } // ============================================================ // bansheeColorExpand // ============================================================ // // Monochrome-to-color expansion using host-to-screen blit with // the SRCMONO flag. Mono bitmap bits are expanded to fg/bg colors // by the hardware. Data is fed as dwords through the launch area. static void bansheeColorExpand(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) { BansheePrivateT *priv = (BansheePrivateT *)drv->privData; if (w <= 0 || h <= 0) { return; } int32_t bytesPerRow = (w + 7) / 8; int32_t dwordsPerRow = (bytesPerRow + 3) / 4; bansheeWaitIdle(drv); bansheeWrite(priv, BAN_DSTBASEADDR, 0); bansheeWrite(priv, BAN_DSTFORMAT, ((uint32_t)priv->screenPitch << 16) | priv->bppFormat); bansheeWrite(priv, BAN_SRCFORMAT, ((uint32_t)bytesPerRow << 16) | BAN_FMT_8BPP); bansheeWrite(priv, BAN_ROP, BAN_ROP_COPY); bansheeWrite(priv, BAN_COLORFORE, fg); bansheeWrite(priv, BAN_COLORBACK, bg); bansheeWrite(priv, BAN_SRCSIZE, (uint32_t)w | ((uint32_t)h << 16)); bansheeWrite(priv, BAN_DSTSIZE, (uint32_t)w | ((uint32_t)h << 16)); bansheeWrite(priv, BAN_DSTXY, (uint32_t)dstX | ((uint32_t)dstY << 16)); bansheeWrite(priv, BAN_COMMAND, BAN_CMD_H2S_BLIT | BAN_CMD_INITIATE | BAN_CMD_SRCMONO); // Feed mono data row by row through the launch area 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); } } priv->launch[0] = val; } } } // ============================================================ // bansheeDetect // ============================================================ static bool bansheeDetect(AccelDriverT *drv) { int32_t matchIdx; if (!pciFindDeviceList(sBansheeDeviceIds, &drv->pciDev, &matchIdx)) { return false; } switch (drv->pciDev.deviceId) { case TDFX_BANSHEE: drv->name = "3dfx Banshee"; break; case TDFX_VOODOO3: drv->name = "3dfx Voodoo3"; break; default: drv->name = "3dfx Banshee/Voodoo3"; break; } return true; } // ============================================================ // bansheeHostBlit // ============================================================ // // CPU-to-screen blit using host-to-screen command. Pixel data is // fed as dwords through the launch area write-combining window. static void bansheeHostBlit(AccelDriverT *drv, const uint8_t *srcBuf, int32_t srcPitch, int32_t dstX, int32_t dstY, int32_t w, int32_t h) { BansheePrivateT *priv = (BansheePrivateT *)drv->privData; if (w <= 0 || h <= 0) { return; } int32_t bytesPerRow = w * priv->bytesPerPixel; int32_t dwordsPerRow = (bytesPerRow + 3) / 4; bansheeWaitIdle(drv); bansheeWrite(priv, BAN_DSTBASEADDR, 0); bansheeWrite(priv, BAN_SRCBASEADDR, 0); bansheeWrite(priv, BAN_SRCFORMAT, ((uint32_t)(w * priv->bytesPerPixel) << 16) | priv->bppFormat); bansheeWrite(priv, BAN_DSTFORMAT, ((uint32_t)priv->screenPitch << 16) | priv->bppFormat); bansheeWrite(priv, BAN_ROP, BAN_ROP_COPY); bansheeWrite(priv, BAN_SRCSIZE, (uint32_t)w | ((uint32_t)h << 16)); bansheeWrite(priv, BAN_DSTSIZE, (uint32_t)w | ((uint32_t)h << 16)); bansheeWrite(priv, BAN_DSTXY, (uint32_t)dstX | ((uint32_t)dstY << 16)); bansheeWrite(priv, BAN_COMMAND, BAN_CMD_H2S_BLIT | BAN_CMD_INITIATE); // Feed pixel data row by row through the launch area 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); } } priv->launch[0] = val; } } } // ============================================================ // bansheeInit // ============================================================ static bool bansheeInit(AccelDriverT *drv, const AccelModeRequestT *req) { BansheePrivateT *priv = (BansheePrivateT *)drv->privData; // Read BARs 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 (32KB) if (!dpmiMapFramebuffer(priv->mmioPhysAddr, BAN_MMIO_SIZE, &priv->mmioMap)) { return false; } priv->mmio = (volatile uint32_t *)priv->mmioMap.ptr; // Map launch area (4KB at MMIO phys + 0x80000) if (!dpmiMapFramebuffer(priv->mmioPhysAddr + BAN_LAUNCH_OFFSET, BAN_LAUNCH_MAP_SIZE, &priv->launchMap)) { dpmiUnmapFramebuffer(&priv->mmioMap); return false; } priv->launch = (volatile uint32_t *)priv->launchMap.ptr; // Find and set VESA mode VesaModeResultT vesa; if (!vesaFindAndSetMode(req->width, req->height, req->bpp, &vesa)) { dpmiUnmapFramebuffer(&priv->launchMap); dpmiUnmapFramebuffer(&priv->mmioMap); return false; } // Map framebuffer if (!dpmiMapFramebuffer(priv->lfbPhysAddr, priv->vramSize, &priv->lfbMap)) { vgaRestoreTextMode(); dpmiUnmapFramebuffer(&priv->launchMap); dpmiUnmapFramebuffer(&priv->mmioMap); return false; } priv->bytesPerPixel = (vesa.bpp + 7) / 8; priv->screenPitch = vesa.pitch; priv->bppFormat = bppToFormat(vesa.bpp); drv->mode.width = vesa.width; drv->mode.height = vesa.height; drv->mode.bpp = vesa.bpp; drv->mode.pitch = vesa.pitch; drv->mode.framebuffer = priv->lfbMap.ptr; drv->mode.vramSize = priv->vramSize; drv->mode.offscreenBase = vesa.pitch * vesa.height; // Wait for engine idle before configuring bansheeWaitIdle(drv); // Set default engine state bansheeWrite(priv, BAN_SRCBASEADDR, 0); bansheeWrite(priv, BAN_DSTBASEADDR, 0); bansheeWrite(priv, BAN_DSTFORMAT, ((uint32_t)priv->screenPitch << 16) | priv->bppFormat); bansheeWrite(priv, BAN_SRCFORMAT, ((uint32_t)priv->screenPitch << 16) | priv->bppFormat); bansheeWrite(priv, BAN_ROP, BAN_ROP_COPY); bansheeWrite(priv, BAN_COMMANDEXTRA, 0); 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 bansheeSetClip(drv, 0, 0, vesa.width, vesa.height); return true; } // ============================================================ // bansheeLineDraw // ============================================================ // // Bresenham line draw with inclusive endpoints. The Banshee engine // takes start/end XY coordinates directly via srcXY/dstXY registers. static void bansheeLineDraw(AccelDriverT *drv, int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint32_t color) { BansheePrivateT *priv = (BansheePrivateT *)drv->privData; bansheeWaitIdle(drv); bansheeWrite(priv, BAN_DSTBASEADDR, 0); bansheeWrite(priv, BAN_DSTFORMAT, ((uint32_t)priv->screenPitch << 16) | priv->bppFormat); bansheeWrite(priv, BAN_ROP, BAN_ROP_COPY); bansheeWrite(priv, BAN_COLORFORE, color); bansheeWrite(priv, BAN_SRCXY, (uint32_t)x1 | ((uint32_t)y1 << 16)); bansheeWrite(priv, BAN_DSTXY, (uint32_t)x2 | ((uint32_t)y2 << 16)); bansheeWrite(priv, BAN_COMMAND, BAN_CMD_LINEDRAW | BAN_CMD_INITIATE); } // ============================================================ // bansheeMoveCursor // ============================================================ static void bansheeMoveCursor(AccelDriverT *drv, int32_t x, int32_t y) { BansheePrivateT *priv = (BansheePrivateT *)drv->privData; if (x < 0) { x = 0; } if (y < 0) { y = 0; } bansheeWrite(priv, BAN_CURSORLOC, (uint32_t)x | ((uint32_t)y << 16)); } // ============================================================ // bansheeRectFill // ============================================================ // // Solid rectangle fill using the Banshee RECTFILL command. The // foreground color is set, coordinates and dimensions are loaded, // and the command register triggers the fill. static void bansheeRectFill(AccelDriverT *drv, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) { BansheePrivateT *priv = (BansheePrivateT *)drv->privData; if (w <= 0 || h <= 0) { return; } bansheeWaitIdle(drv); bansheeWrite(priv, BAN_DSTBASEADDR, 0); bansheeWrite(priv, BAN_DSTFORMAT, ((uint32_t)priv->screenPitch << 16) | priv->bppFormat); bansheeWrite(priv, BAN_ROP, BAN_ROP_COPY); bansheeWrite(priv, BAN_COLORFORE, color); bansheeWrite(priv, BAN_DSTSIZE, (uint32_t)w | ((uint32_t)h << 16)); bansheeWrite(priv, BAN_DSTXY, (uint32_t)x | ((uint32_t)y << 16)); bansheeWrite(priv, BAN_COMMAND, BAN_CMD_RECTFILL | BAN_CMD_INITIATE); } // ============================================================ // bansheeRectFillPat // ============================================================ // // 8x8 mono pattern fill using the Banshee RECTFILL command with // BAN_CMD_MONOPAT. The pattern is 8 bytes (one per row, MSB-first), // written to pattern0Alias and pattern1Alias as two 32-bit values. // 1-bits use the foreground color, 0-bits use the background. static void bansheeRectFillPat(AccelDriverT *drv, int32_t x, int32_t y, int32_t w, int32_t h, const uint8_t *pattern, uint32_t fg, uint32_t bg) { BansheePrivateT *priv = (BansheePrivateT *)drv->privData; if (w <= 0 || h <= 0) { return; } // Pack pattern rows 0-3 into PATTERN0 and rows 4-7 into PATTERN1 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); bansheeWaitIdle(drv); bansheeWrite(priv, BAN_DSTBASEADDR, 0); bansheeWrite(priv, BAN_DSTFORMAT, ((uint32_t)priv->screenPitch << 16) | priv->bppFormat); bansheeWrite(priv, BAN_ROP, BAN_ROP_COPY); bansheeWrite(priv, BAN_COLORFORE, fg); bansheeWrite(priv, BAN_COLORBACK, bg); bansheeWrite(priv, BAN_PATTERN0, pat0); bansheeWrite(priv, BAN_PATTERN1, pat1); bansheeWrite(priv, BAN_DSTSIZE, (uint32_t)w | ((uint32_t)h << 16)); bansheeWrite(priv, BAN_DSTXY, (uint32_t)x | ((uint32_t)y << 16)); bansheeWrite(priv, BAN_COMMAND, BAN_CMD_RECTFILL | BAN_CMD_INITIATE | BAN_CMD_MONOPAT); } // ============================================================ // bansheeSetClip // ============================================================ static void bansheeSetClip(AccelDriverT *drv, int32_t x, int32_t y, int32_t w, int32_t h) { BansheePrivateT *priv = (BansheePrivateT *)drv->privData; bansheeWrite(priv, BAN_CLIP0MIN, (uint32_t)x | ((uint32_t)y << 16)); bansheeWrite(priv, BAN_CLIP0MAX, (uint32_t)(x + w) | ((uint32_t)(y + h) << 16)); } // ============================================================ // bansheeSetCursor // ============================================================ // // The Banshee hardware cursor is a 64x64 two-color cursor stored // in VRAM. The format is 2 bits per pixel: AND plane followed by // XOR plane, packed as 64x64 = 1024 bytes per plane. static void bansheeSetCursor(AccelDriverT *drv, const HwCursorImageT *image) { BansheePrivateT *priv = (BansheePrivateT *)drv->privData; if (!image) { bansheeShowCursor(drv, false); return; } bansheeWaitIdle(drv); // Store cursor image at end of VRAM (1KB AND + 1KB XOR = 2KB) uint32_t cursorOffset = priv->vramSize - 2048; cursorOffset &= ~0x7FF; // align to 2KB uint8_t *cursorMem = drv->mode.framebuffer + cursorOffset; // Write AND mask then XOR mask, each 64x64 / 8 = 512 bytes for (int32_t row = 0; row < BAN_HW_CURSOR_SIZE; row++) { for (int32_t byteIdx = 0; byteIdx < 8; byteIdx++) { int32_t srcIdx = row * 8 + byteIdx; uint8_t andByte; uint8_t xorByte; if (row < image->height && byteIdx < (image->width + 7) / 8) { andByte = image->andMask[srcIdx]; xorByte = image->xorMask[srcIdx]; } else { andByte = 0xFF; // transparent xorByte = 0x00; } cursorMem[row * 16 + byteIdx] = andByte; cursorMem[row * 16 + byteIdx + 8] = xorByte; } } } // ============================================================ // bansheeShowCursor // ============================================================ static void bansheeShowCursor(AccelDriverT *drv, bool visible) { BansheePrivateT *priv = (BansheePrivateT *)drv->privData; uint32_t vidProcCfg = bansheeRead(priv, BAN_VIDPROCCFG); if (visible) { vidProcCfg |= BAN_CURSOR_ENABLE; } else { vidProcCfg &= ~BAN_CURSOR_ENABLE; } bansheeWrite(priv, BAN_VIDPROCCFG, vidProcCfg); } // ============================================================ // bansheeShutdown // ============================================================ static void bansheeShutdown(AccelDriverT *drv) { BansheePrivateT *priv = (BansheePrivateT *)drv->privData; bansheeShowCursor(drv, false); vgaRestoreTextMode(); dpmiUnmapFramebuffer(&priv->launchMap); dpmiUnmapFramebuffer(&priv->lfbMap); dpmiUnmapFramebuffer(&priv->mmioMap); priv->mmio = NULL; priv->launch = NULL; } // ============================================================ // bansheeWaitIdle // ============================================================ // // Wait until the 2D engine is completely idle. Bits 0-10 of the // status register must all be zero. static void bansheeWaitIdle(AccelDriverT *drv) { BansheePrivateT *priv = (BansheePrivateT *)drv->privData; for (int32_t i = 0; i < BAN_MAX_IDLE_WAIT; i++) { uint32_t stat = bansheeRead(priv, BAN_STATUS); if (!(stat & BAN_STATUS_BUSY_MASK)) { return; } } } // ============================================================ // bppToFormat // ============================================================ // // Convert bits-per-pixel to the Banshee srcFormat/dstFormat // encoding for the low bits of those registers. static uint32_t bppToFormat(int32_t bpp) { switch (bpp) { case 8: return BAN_FMT_8BPP; case 15: case 16: return BAN_FMT_16BPP; case 32: return BAN_FMT_32BPP; default: return BAN_FMT_16BPP; } }