// cirrusGd54.c -- Cirrus Logic GD5426/28/34/36/46/80 accelerated video driver // // Supports the Cirrus Logic GD54xx family of VGA controllers. These // chips were extremely common in the early-to-mid 1990s, found in // everything from budget desktops to laptops. // // The GD54xx BitBLT engine is accessed entirely through extended // Graphics Controller (GR) registers at I/O ports 0x3CE/0x3CF. // There is no MMIO option on the GD54xx series (unlike the later // Laguna chips). The engine supports: // - Screen-to-screen BitBLT // - Solid rectangle fill // - Color expansion (monochrome-to-color, for text) // - 8x8 pattern fill // - Transparent blit (color key) // - Hardware cursor (32x32 on GD5426/28, 64x64 on GD5434+) // // Register unlock: // Write 0x12 to SR6 (sequencer register 6) to unlock the Cirrus // extended registers. Write 0x00 to re-lock. // // BLT engine registers (GR extended, indices 0x20-0x3F): // All BLT parameters are set through the graphics controller // index/data ports (0x3CE/0x3CF). Addresses are linear byte // offsets into VRAM. #include "accelVid.h" #include "vgaCommon.h" #include "pci.h" #include #include #include #include #include // ============================================================ // Cirrus vendor/device IDs // ============================================================ #define CL_VENDOR_ID 0x1013 #define CL_GD5426 0x0000 // ISA/VLB only, no PCI ID -- detected via probe #define CL_GD5428 0x0000 // ISA/VLB only #define CL_GD5429 0x00A0 // shared with 5434 on some boards #define CL_GD5434 0x00A0 #define CL_GD5434_ALT 0x00A8 #define CL_GD5436 0x00AC #define CL_GD5446 0x00B8 #define CL_GD5480 0x00BC static const uint16_t sCirrusDeviceIds[] = { CL_VENDOR_ID, CL_GD5434, CL_VENDOR_ID, CL_GD5434_ALT, CL_VENDOR_ID, CL_GD5436, CL_VENDOR_ID, CL_GD5446, CL_VENDOR_ID, CL_GD5480, 0, 0 }; // ============================================================ // Cirrus extended GR register indices for BLT engine // ============================================================ #define CL_GR20_BLT_WIDTH_LO 0x20 #define CL_GR21_BLT_WIDTH_HI 0x21 #define CL_GR22_BLT_HEIGHT_LO 0x22 #define CL_GR23_BLT_HEIGHT_HI 0x23 #define CL_GR24_BLT_DST_PITCH_LO 0x24 #define CL_GR25_BLT_DST_PITCH_HI 0x25 #define CL_GR26_BLT_SRC_PITCH_LO 0x26 #define CL_GR27_BLT_SRC_PITCH_HI 0x27 #define CL_GR28_BLT_DST_ADDR_LO 0x28 #define CL_GR29_BLT_DST_ADDR_MID 0x29 #define CL_GR2A_BLT_DST_ADDR_HI 0x2A #define CL_GR2C_BLT_SRC_ADDR_LO 0x2C #define CL_GR2D_BLT_SRC_ADDR_MID 0x2D #define CL_GR2E_BLT_SRC_ADDR_HI 0x2E #define CL_GR30_BLT_MODE 0x30 #define CL_GR31_BLT_STATUS 0x31 #define CL_GR32_BLT_ROP 0x32 #define CL_GR33_BLT_MODE_EXT 0x33 #define CL_GR34_BLT_FGCOLOR_LO 0x34 #define CL_GR35_BLT_FGCOLOR_HI 0x35 #define CL_GR38_BLT_TRANS_COLOR_LO 0x38 #define CL_GR39_BLT_TRANS_COLOR_HI 0x39 #define CL_GR3A_BLT_TRANS_MASK_LO 0x3A #define CL_GR3B_BLT_TRANS_MASK_HI 0x3B // ============================================================ // Cirrus BLT mode bits (GR30) // ============================================================ #define CL_BLT_DIR_BACKWARD 0x01 // blit direction backward #define CL_BLT_SRC_SYSTEM 0x02 // source is system memory (CPU) #define CL_BLT_SRC_PATTERN 0x04 // source is 8x8 pattern #define CL_BLT_TRANSPARENT 0x08 // transparent background #define CL_BLT_DST_SYSTEM 0x10 // destination is system memory #define CL_BLT_COLOR_EXPAND 0x80 // monochrome color expansion // ============================================================ // Cirrus BLT status bits (GR31) // ============================================================ #define CL_BLT_START 0x02 // start BLT operation #define CL_BLT_RESET 0x04 // reset BLT engine #define CL_BLT_BUSY 0x01 // BLT engine busy (read) // ============================================================ // Cirrus BLT ROP values (GR32) // ============================================================ // // The Cirrus ROP encoding is different from the S3/Windows ROP // codes. These are the Cirrus-specific values. #define CL_ROP_COPY 0x0D // dest = source #define CL_ROP_PAT_COPY 0x0D // dest = pattern (same as copy in fill mode) #define CL_ROP_XOR 0x59 // dest = src XOR dest #define CL_ROP_AND 0x05 // dest = src AND dest #define CL_ROP_OR 0x6D // dest = src OR dest #define CL_ROP_ZERO 0x00 // dest = 0 #define CL_ROP_ONE 0x0B // dest = 1 // Cirrus sequencer unlock key #define CL_SR6_UNLOCK 0x12 #define CL_SR6_LOCK 0x00 // Hardware cursor constants #define CL_HW_CURSOR_SIZE 64 // 64x64 on GD5434+ #define CL_HW_CURSOR_BYTES 1024 // 64*64*2bpp / 8 = 1024 // Maximum wait iterations #define CL_MAX_IDLE_WAIT 1000000 // ============================================================ // Private driver state // ============================================================ typedef struct { uint32_t lfbPhysAddr; uint32_t vramSize; uint32_t cursorOffset; int32_t bytesPerPixel; int32_t screenPitch; DpmiMappingT lfbMapping; bool is5434Plus; // true for GD5434 and later (64x64 cursor) } CirrusPrivateT; // ============================================================ // Prototypes // ============================================================ static void clBitBlt(AccelDriverT *drv, int32_t srcX, int32_t srcY, int32_t dstX, int32_t dstY, int32_t w, int32_t h); static void clColorExpand(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 clDetect(AccelDriverT *drv); static void clHostBlit(AccelDriverT *drv, const uint8_t *srcBuf, int32_t srcPitch, int32_t dstX, int32_t dstY, int32_t w, int32_t h); static bool clInit(AccelDriverT *drv, const AccelModeRequestT *req); static void clMoveCursor(AccelDriverT *drv, int32_t x, int32_t y); static void clRectFill(AccelDriverT *drv, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color); static void clSetClip(AccelDriverT *drv, int32_t x, int32_t y, int32_t w, int32_t h); static void clSetCursor(AccelDriverT *drv, const HwCursorImageT *image); static void clShowCursor(AccelDriverT *drv, bool visible); static void clShutdown(AccelDriverT *drv); static void clUnlockRegs(void); static void clWaitIdle(AccelDriverT *drv); // ============================================================ // Driver instance // ============================================================ static CirrusPrivateT sCirrusPrivate; static AccelDriverT sCirrusDriver = { .name = "Cirrus Logic GD5434", .chipFamily = "cirrus", .caps = 0, .privData = &sCirrusPrivate, .detect = clDetect, .init = clInit, .shutdown = clShutdown, .waitIdle = clWaitIdle, .setClip = clSetClip, .rectFill = clRectFill, .rectFillPat = NULL, .bitBlt = clBitBlt, .hostBlit = clHostBlit, .colorExpand = clColorExpand, .lineDraw = NULL, // GD54xx has no hardware line draw .setCursor = clSetCursor, .moveCursor = clMoveCursor, .showCursor = clShowCursor, }; // ============================================================ // clRegisterDriver // ============================================================ void clRegisterDriver(void) { accelRegisterDriver(&sCirrusDriver); } // ============================================================ // clBitBlt // ============================================================ // // Screen-to-screen BitBLT. The Cirrus engine uses linear VRAM // addresses for source and destination. Direction is controlled // by the backward bit in GR30 -- for overlapping regions where // dst > src, we must blit backward. static void clBitBlt(AccelDriverT *drv, int32_t srcX, int32_t srcY, int32_t dstX, int32_t dstY, int32_t w, int32_t h) { CirrusPrivateT *priv = (CirrusPrivateT *)drv->privData; if (w <= 0 || h <= 0) { return; } int32_t bpp = priv->bytesPerPixel; int32_t pitch = priv->screenPitch; // Calculate linear addresses uint32_t srcAddr = srcY * pitch + srcX * bpp; uint32_t dstAddr = dstY * pitch + dstX * bpp; // Determine direction for overlapping blits uint8_t mode = 0; if (dstAddr > srcAddr) { mode |= CL_BLT_DIR_BACKWARD; // Adjust addresses to end of blit region srcAddr += (h - 1) * pitch + (w - 1) * bpp; dstAddr += (h - 1) * pitch + (w - 1) * bpp; } // Width in bytes minus 1 int32_t widthBytes = w * bpp - 1; clWaitIdle(drv); // Set up BLT parameters vgaGfxWrite(CL_GR20_BLT_WIDTH_LO, widthBytes & 0xFF); vgaGfxWrite(CL_GR21_BLT_WIDTH_HI, (widthBytes >> 8) & 0x1F); vgaGfxWrite(CL_GR22_BLT_HEIGHT_LO, (h - 1) & 0xFF); vgaGfxWrite(CL_GR23_BLT_HEIGHT_HI, ((h - 1) >> 8) & 0x07); vgaGfxWrite(CL_GR24_BLT_DST_PITCH_LO, pitch & 0xFF); vgaGfxWrite(CL_GR25_BLT_DST_PITCH_HI, (pitch >> 8) & 0x1F); vgaGfxWrite(CL_GR26_BLT_SRC_PITCH_LO, pitch & 0xFF); vgaGfxWrite(CL_GR27_BLT_SRC_PITCH_HI, (pitch >> 8) & 0x1F); vgaGfxWrite(CL_GR28_BLT_DST_ADDR_LO, dstAddr & 0xFF); vgaGfxWrite(CL_GR29_BLT_DST_ADDR_MID, (dstAddr >> 8) & 0xFF); vgaGfxWrite(CL_GR2A_BLT_DST_ADDR_HI, (dstAddr >> 16) & 0x3F); vgaGfxWrite(CL_GR2C_BLT_SRC_ADDR_LO, srcAddr & 0xFF); vgaGfxWrite(CL_GR2D_BLT_SRC_ADDR_MID, (srcAddr >> 8) & 0xFF); vgaGfxWrite(CL_GR2E_BLT_SRC_ADDR_HI, (srcAddr >> 16) & 0x3F); vgaGfxWrite(CL_GR32_BLT_ROP, CL_ROP_COPY); vgaGfxWrite(CL_GR30_BLT_MODE, mode); // Start BLT vgaGfxWrite(CL_GR31_BLT_STATUS, CL_BLT_START); } // ============================================================ // clColorExpand // ============================================================ // // Monochrome-to-color expansion. The source data is 1bpp bitmap // in system memory, which gets transferred through the BLT engine // with color expansion enabled. Each 1-bit becomes the foreground // color, each 0-bit becomes the background color. // // The Cirrus color expand uses GR34/GR35 for the foreground color // and the background is set by first doing a fill, or by using // transparent mode with a pre-filled background. static void clColorExpand(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) { CirrusPrivateT *priv = (CirrusPrivateT *)drv->privData; if (w <= 0 || h <= 0) { return; } int32_t bpp = priv->bytesPerPixel; int32_t pitch = priv->screenPitch; // First fill the destination with background color clRectFill(drv, dstX, dstY, w, h, bg); clWaitIdle(drv); // Now do a transparent color expand for the foreground uint32_t dstAddr = dstY * pitch + dstX * bpp; int32_t widthBytes = w * bpp - 1; // Set foreground color vgaGfxWrite(CL_GR34_BLT_FGCOLOR_LO, fg & 0xFF); vgaGfxWrite(CL_GR35_BLT_FGCOLOR_HI, (fg >> 8) & 0xFF); // Set up BLT parameters vgaGfxWrite(CL_GR20_BLT_WIDTH_LO, widthBytes & 0xFF); vgaGfxWrite(CL_GR21_BLT_WIDTH_HI, (widthBytes >> 8) & 0x1F); vgaGfxWrite(CL_GR22_BLT_HEIGHT_LO, (h - 1) & 0xFF); vgaGfxWrite(CL_GR23_BLT_HEIGHT_HI, ((h - 1) >> 8) & 0x07); vgaGfxWrite(CL_GR24_BLT_DST_PITCH_LO, pitch & 0xFF); vgaGfxWrite(CL_GR25_BLT_DST_PITCH_HI, (pitch >> 8) & 0x1F); // Source pitch for monochrome data vgaGfxWrite(CL_GR26_BLT_SRC_PITCH_LO, srcPitch & 0xFF); vgaGfxWrite(CL_GR27_BLT_SRC_PITCH_HI, (srcPitch >> 8) & 0x1F); vgaGfxWrite(CL_GR28_BLT_DST_ADDR_LO, dstAddr & 0xFF); vgaGfxWrite(CL_GR29_BLT_DST_ADDR_MID, (dstAddr >> 8) & 0xFF); vgaGfxWrite(CL_GR2A_BLT_DST_ADDR_HI, (dstAddr >> 16) & 0x3F); vgaGfxWrite(CL_GR32_BLT_ROP, CL_ROP_COPY); vgaGfxWrite(CL_GR30_BLT_MODE, CL_BLT_COLOR_EXPAND | CL_BLT_SRC_SYSTEM | CL_BLT_TRANSPARENT); // Start BLT vgaGfxWrite(CL_GR31_BLT_STATUS, CL_BLT_START); // Feed monochrome data through PIX_TRANS equivalent // On Cirrus, system-memory source data is written to the // BLT engine via the VGA aperture at 0xA0000 (mapped via DPMI). // Each row of monochrome data is padded to a dword boundary. int32_t srcBytesPerRow = (w + 7) / 8; int32_t padBytesPerRow = (srcBytesPerRow + 3) & ~3; for (int32_t row = 0; row < h; row++) { const uint8_t *rowData = srcBuf + row * srcPitch; for (int32_t i = 0; i < padBytesPerRow; i++) { uint8_t byte = (i < srcBytesPerRow) ? rowData[i] : 0; outportb(0x3CF, byte); // data through GR register space } } } // ============================================================ // clDetect // ============================================================ static bool clDetect(AccelDriverT *drv) { int32_t matchIdx; if (!pciFindDeviceList(sCirrusDeviceIds, &drv->pciDev, &matchIdx)) { return false; } switch (drv->pciDev.deviceId) { case CL_GD5434: case CL_GD5434_ALT: drv->name = "Cirrus Logic GD5434"; break; case CL_GD5436: drv->name = "Cirrus Logic GD5436"; break; case CL_GD5446: drv->name = "Cirrus Logic GD5446"; break; case CL_GD5480: drv->name = "Cirrus Logic GD5480"; break; default: drv->name = "Cirrus Logic GD54xx"; break; } return true; } // ============================================================ // clHostBlit // ============================================================ // // CPU-to-screen blit. Transfers pixel data from system memory to // the framebuffer via the BLT engine with CL_BLT_SRC_SYSTEM mode. // Source data is fed byte-by-byte through the GR data port (0x3CF), // with each row padded to a dword (4-byte) boundary. static void clHostBlit(AccelDriverT *drv, const uint8_t *srcBuf, int32_t srcPitch, int32_t dstX, int32_t dstY, int32_t w, int32_t h) { CirrusPrivateT *priv = (CirrusPrivateT *)drv->privData; if (w <= 0 || h <= 0) { return; } int32_t bpp = priv->bytesPerPixel; int32_t pitch = priv->screenPitch; uint32_t dstAddr = dstY * pitch + dstX * bpp; int32_t widthBytes = w * bpp - 1; int32_t rowBytes = w * bpp; int32_t padBytesPerRow = (rowBytes + 3) & ~3; clWaitIdle(drv); // Set up BLT parameters vgaGfxWrite(CL_GR20_BLT_WIDTH_LO, widthBytes & 0xFF); vgaGfxWrite(CL_GR21_BLT_WIDTH_HI, (widthBytes >> 8) & 0x1F); vgaGfxWrite(CL_GR22_BLT_HEIGHT_LO, (h - 1) & 0xFF); vgaGfxWrite(CL_GR23_BLT_HEIGHT_HI, ((h - 1) >> 8) & 0x07); vgaGfxWrite(CL_GR24_BLT_DST_PITCH_LO, pitch & 0xFF); vgaGfxWrite(CL_GR25_BLT_DST_PITCH_HI, (pitch >> 8) & 0x1F); vgaGfxWrite(CL_GR28_BLT_DST_ADDR_LO, dstAddr & 0xFF); vgaGfxWrite(CL_GR29_BLT_DST_ADDR_MID, (dstAddr >> 8) & 0xFF); vgaGfxWrite(CL_GR2A_BLT_DST_ADDR_HI, (dstAddr >> 16) & 0x3F); // BLT mode: source from CPU vgaGfxWrite(CL_GR30_BLT_MODE, CL_BLT_SRC_SYSTEM); vgaGfxWrite(CL_GR32_BLT_ROP, CL_ROP_COPY); // Start BLT vgaGfxWrite(CL_GR31_BLT_STATUS, CL_BLT_START); // Feed pixel data row by row, padded to dword boundary for (int32_t row = 0; row < h; row++) { const uint8_t *rowData = srcBuf + row * srcPitch; for (int32_t i = 0; i < padBytesPerRow; i++) { uint8_t byte = (i < rowBytes) ? rowData[i] : 0; outportb(0x3CF, byte); } } } // ============================================================ // clInit // ============================================================ static bool clInit(AccelDriverT *drv, const AccelModeRequestT *req) { CirrusPrivateT *priv = (CirrusPrivateT *)drv->privData; memset(priv, 0, sizeof(*priv)); priv->is5434Plus = (drv->pciDev.deviceId != CL_GD5429); // Get VRAM size and LFB address from PCI BAR0 uint32_t bar0 = pciRead32(drv->pciDev.bus, drv->pciDev.dev, drv->pciDev.func, PCI_BAR0); priv->lfbPhysAddr = bar0 & 0xFFFFFFF0; priv->vramSize = pciSizeBar(drv->pciDev.bus, drv->pciDev.dev, drv->pciDev.func, PCI_BAR0); // Unlock Cirrus extended registers clUnlockRegs(); // Detect VRAM size from SR0F if BAR sizing was unreasonable uint8_t sr0f = vgaSeqRead(0x0F); uint32_t ramFromSr = 0; switch ((sr0f >> 3) & 0x03) { case 0: ramFromSr = 256 * 1024; break; case 1: ramFromSr = 512 * 1024; break; case 2: ramFromSr = 1024 * 1024; break; case 3: ramFromSr = 2048 * 1024; break; } // GD5434+ can have 4MB if (priv->is5434Plus && (sr0f & 0x80)) { ramFromSr = 4096 * 1024; } if (priv->vramSize < 256 * 1024 || priv->vramSize > 64 * 1024 * 1024) { priv->vramSize = ramFromSr; } // Find and set VESA mode VesaModeResultT vesa; if (!vesaFindAndSetMode(req->width, req->height, req->bpp, &vesa)) { return false; } // Map LFB via DPMI if (!dpmiMapFramebuffer(priv->lfbPhysAddr, priv->vramSize, &priv->lfbMapping)) { 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; // Re-unlock after mode set clUnlockRegs(); // Reset BLT engine vgaGfxWrite(CL_GR31_BLT_STATUS, CL_BLT_RESET); vgaGfxWrite(CL_GR31_BLT_STATUS, 0x00); // Set up cursor at end of VRAM priv->cursorOffset = priv->vramSize - CL_HW_CURSOR_BYTES; priv->cursorOffset &= ~(CL_HW_CURSOR_BYTES - 1); drv->caps = ACAP_RECT_FILL | ACAP_BITBLT | ACAP_HOST_BLIT | ACAP_COLOR_EXPAND | ACAP_HW_CURSOR; return true; } // ============================================================ // clMoveCursor // ============================================================ // // Moves the hardware cursor. On Cirrus GD5434+, cursor position // is set through sequencer extended registers SR10-SR13. static void clMoveCursor(AccelDriverT *drv, int32_t x, int32_t y) { (void)drv; if (x < 0) { x = 0; } if (y < 0) { y = 0; } vgaSeqWrite(0x10, x & 0xFF); vgaSeqWrite(0x11, (x >> 8) & 0x07); vgaSeqWrite(0x12, y & 0xFF); vgaSeqWrite(0x13, (y >> 8) & 0x07); } // ============================================================ // clRectFill // ============================================================ // // Solid rectangle fill using the BLT engine. The Cirrus engine // doesn't have a dedicated "fill" command -- instead, we set up // a 1-pixel source and use pattern-fill mode, or we set the // source to a single-color region. The simplest approach is to // use the color expansion with all-ones data, but for solid fills // the most efficient method is to use the ROP with the foreground // color register. static void clRectFill(AccelDriverT *drv, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) { CirrusPrivateT *priv = (CirrusPrivateT *)drv->privData; if (w <= 0 || h <= 0) { return; } int32_t bpp = priv->bytesPerPixel; int32_t pitch = priv->screenPitch; uint32_t dstAddr = y * pitch + x * bpp; int32_t widthBytes = w * bpp - 1; clWaitIdle(drv); // Set foreground color for fill vgaGfxWrite(CL_GR34_BLT_FGCOLOR_LO, color & 0xFF); vgaGfxWrite(CL_GR35_BLT_FGCOLOR_HI, (color >> 8) & 0xFF); vgaGfxWrite(CL_GR20_BLT_WIDTH_LO, widthBytes & 0xFF); vgaGfxWrite(CL_GR21_BLT_WIDTH_HI, (widthBytes >> 8) & 0x1F); vgaGfxWrite(CL_GR22_BLT_HEIGHT_LO, (h - 1) & 0xFF); vgaGfxWrite(CL_GR23_BLT_HEIGHT_HI, ((h - 1) >> 8) & 0x07); vgaGfxWrite(CL_GR24_BLT_DST_PITCH_LO, pitch & 0xFF); vgaGfxWrite(CL_GR25_BLT_DST_PITCH_HI, (pitch >> 8) & 0x1F); vgaGfxWrite(CL_GR28_BLT_DST_ADDR_LO, dstAddr & 0xFF); vgaGfxWrite(CL_GR29_BLT_DST_ADDR_MID, (dstAddr >> 8) & 0xFF); vgaGfxWrite(CL_GR2A_BLT_DST_ADDR_HI, (dstAddr >> 16) & 0x3F); // Source = foreground color, color expand with all 1s vgaGfxWrite(CL_GR32_BLT_ROP, CL_ROP_COPY); vgaGfxWrite(CL_GR30_BLT_MODE, CL_BLT_COLOR_EXPAND | CL_BLT_SRC_SYSTEM); // Source pitch for monochrome data (1 byte per row of fill) vgaGfxWrite(CL_GR26_BLT_SRC_PITCH_LO, 0); vgaGfxWrite(CL_GR27_BLT_SRC_PITCH_HI, 0); // Start BLT vgaGfxWrite(CL_GR31_BLT_STATUS, CL_BLT_START); // Feed all-ones data (every pixel is foreground color) int32_t srcBytesPerRow = (w + 7) / 8; int32_t padBytesPerRow = (srcBytesPerRow + 3) & ~3; for (int32_t row = 0; row < h; row++) { for (int32_t i = 0; i < padBytesPerRow; i++) { outportb(0x3CF, 0xFF); } } } // ============================================================ // clSetClip // ============================================================ // // The GD54xx BLT engine doesn't have hardware scissor registers. // Clipping must be done in software by adjusting coordinates // before issuing BLT commands. This is a no-op placeholder. static void clSetClip(AccelDriverT *drv, int32_t x, int32_t y, int32_t w, int32_t h) { (void)drv; (void)x; (void)y; (void)w; (void)h; } // ============================================================ // clSetCursor // ============================================================ // // Uploads cursor image to VRAM. Cirrus GD5434+ uses 64x64 // 2bpp cursor stored at a 1KB-aligned VRAM address. The address // is set via SR2D (high) and SR2C (low) in units of 256 bytes. // Format: interleaved AND/XOR planes, 16 bytes per row // (8 bytes AND, 8 bytes XOR). static void clSetCursor(AccelDriverT *drv, const HwCursorImageT *image) { CirrusPrivateT *priv = (CirrusPrivateT *)drv->privData; if (!image) { clShowCursor(drv, false); return; } clWaitIdle(drv); uint8_t *cursorMem = drv->mode.framebuffer + priv->cursorOffset; for (int32_t row = 0; row < CL_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 address (in units of 256 bytes) uint16_t addrUnits = priv->cursorOffset / 256; vgaSeqWrite(0x2C, addrUnits & 0xFF); vgaSeqWrite(0x2D, (addrUnits >> 8) & 0x3F); } // ============================================================ // clShowCursor // ============================================================ // // Enable/disable the hardware cursor via SR12 bit 0 on Cirrus. static void clShowCursor(AccelDriverT *drv, bool visible) { (void)drv; uint8_t sr12 = vgaSeqRead(0x12); if (visible) { sr12 |= 0x01; } else { sr12 &= ~0x01; } vgaSeqWrite(0x12, sr12); } // ============================================================ // clShutdown // ============================================================ static void clShutdown(AccelDriverT *drv) { CirrusPrivateT *priv = (CirrusPrivateT *)drv->privData; clShowCursor(drv, false); dpmiUnmapFramebuffer(&priv->lfbMapping); vgaRestoreTextMode(); } // ============================================================ // clUnlockRegs // ============================================================ // // Unlock Cirrus extended registers by writing 0x12 to SR6. static void clUnlockRegs(void) { vgaSeqWrite(0x06, CL_SR6_UNLOCK); } // ============================================================ // clWaitIdle // ============================================================ // // Wait for the BLT engine to finish. Poll GR31 bit 0. static void clWaitIdle(AccelDriverT *drv) { (void)drv; for (int32_t i = 0; i < CL_MAX_IDLE_WAIT; i++) { if (!(vgaGfxRead(CL_GR31_BLT_STATUS) & CL_BLT_BUSY)) { return; } } }