585 lines
20 KiB
C
585 lines
20 KiB
C
// cirrusLaguna.c -- Cirrus Logic Laguna GD5462/5464/5465 accelerated video driver
|
|
//
|
|
// Supports the Cirrus Logic Laguna family: GD5462, GD5464, and GD5465.
|
|
// These are MMIO-based PCI accelerators completely different from the
|
|
// older GD54xx (Alpine) series -- different register set, different
|
|
// BLT engine, and different programming model.
|
|
//
|
|
// The Laguna 2D engine features:
|
|
// - Solid rectangle fill
|
|
// - Screen-to-screen BitBLT
|
|
// - CPU-to-screen blit (host data window)
|
|
// - Monochrome color expansion (text/glyph rendering)
|
|
// - Hardware clip rectangle
|
|
// - 64x64 hardware cursor
|
|
//
|
|
// BAR layout:
|
|
// BAR0 = MMIO registers (4KB)
|
|
// BAR1 = linear framebuffer
|
|
//
|
|
// The 2D engine is programmed via MMIO registers starting at offset
|
|
// 0x0100. Commands are initiated by writing to the COMMAND register
|
|
// at 0x0118. Host data (for CPU-to-screen and color expand) is fed
|
|
// through a 512-byte window at MMIO + 0x0200.
|
|
|
|
#include "accelVid.h"
|
|
#include "vgaCommon.h"
|
|
#include "pci.h"
|
|
|
|
#include <pc.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/nearptr.h>
|
|
|
|
// ============================================================
|
|
// Cirrus Laguna vendor/device IDs
|
|
// ============================================================
|
|
|
|
#define CIRRUS_VENDOR_ID 0x1013
|
|
|
|
#define LAGUNA_GD5462 0x00D0
|
|
#define LAGUNA_GD5464 0x00D4
|
|
#define LAGUNA_GD5465 0x00D6
|
|
|
|
static const uint16_t sLagunaDeviceIds[] = {
|
|
CIRRUS_VENDOR_ID, LAGUNA_GD5462,
|
|
CIRRUS_VENDOR_ID, LAGUNA_GD5464,
|
|
CIRRUS_VENDOR_ID, LAGUNA_GD5465,
|
|
0, 0
|
|
};
|
|
|
|
// ============================================================
|
|
// MMIO register offsets (from BAR0)
|
|
// ============================================================
|
|
|
|
// 0x0000-0x00FF: VGA compatible registers (mapped)
|
|
|
|
// 2D engine registers
|
|
#define LAG_CONTROL 0x0100 // engine control / status
|
|
#define LAG_FGCOLOR 0x0104 // foreground color
|
|
#define LAG_BGCOLOR 0x0108 // background color
|
|
#define LAG_DSTXY 0x010C // destination XY (X | Y<<16)
|
|
#define LAG_SRCXY 0x0110 // source XY (X | Y<<16)
|
|
#define LAG_DSTSIZE 0x0114 // destination size (W | H<<16)
|
|
#define LAG_COMMAND 0x0118 // command register (triggers operation)
|
|
#define LAG_PITCH 0x011C // pitch (srcPitch<<16 | dstPitch)
|
|
#define LAG_PAT0 0x0120 // 8x8 mono pattern (first 32 bits)
|
|
#define LAG_PAT1 0x0124 // 8x8 mono pattern (second 32 bits)
|
|
#define LAG_CLIPLT 0x0130 // clip left/top (left | top<<16)
|
|
#define LAG_CLIPRB 0x0134 // clip right/bottom (right | bottom<<16)
|
|
#define LAG_HOST_DATA 0x0200 // host data window (512 bytes)
|
|
|
|
// Hardware cursor registers
|
|
#define LAG_CUR_CTRL 0x0300 // cursor control (bit 0 = enable)
|
|
#define LAG_CUR_X 0x0304 // cursor X position
|
|
#define LAG_CUR_Y 0x0308 // cursor Y position
|
|
#define LAG_CUR_ADDR 0x030C // cursor VRAM address
|
|
|
|
// ============================================================
|
|
// Status register bits
|
|
// ============================================================
|
|
|
|
#define LAG_STATUS_BUSY 0x01 // engine busy (bit 0 of CONTROL)
|
|
|
|
// ============================================================
|
|
// Command register encoding
|
|
// ============================================================
|
|
|
|
// Operation codes (bits 3:0)
|
|
#define LAG_CMD_NOP 0x00
|
|
#define LAG_CMD_BITBLT 0x01 // screen-to-screen BitBlt
|
|
#define LAG_CMD_RECTFILL 0x02 // solid rectangle fill
|
|
#define LAG_CMD_HOST_BLIT 0x03 // host-to-screen blit
|
|
#define LAG_CMD_LINE 0x04 // line draw
|
|
#define LAG_CMD_COLOR_EXPAND 0x05 // mono color expansion from host
|
|
|
|
// ROP encoding (bits 7:4)
|
|
#define LAG_CMD_ROP_SHIFT 4
|
|
|
|
// Direction and option bits
|
|
#define LAG_CMD_DIR_REV 0x0100 // bit 8: reverse direction
|
|
#define LAG_CMD_PAT_EN 0x0200 // bit 9: pattern enable
|
|
#define LAG_CMD_TRANS_EN 0x0400 // bit 10: transparency enable
|
|
#define LAG_CMD_COLOREXP 0x0800 // bit 11: color expand (mono source)
|
|
|
|
// Common ROP values (shifted into bits 7:4)
|
|
#define LAG_ROP_COPY (0x0C << LAG_CMD_ROP_SHIFT) // 0xCC = dest = src
|
|
#define LAG_ROP_PAT (0x0F << LAG_CMD_ROP_SHIFT) // 0xF0 = dest = pat
|
|
|
|
// ============================================================
|
|
// Constants
|
|
// ============================================================
|
|
|
|
#define LAG_MMIO_SIZE 4096
|
|
#define LAG_MAX_IDLE_WAIT 1000000
|
|
#define LAG_HW_CURSOR_SIZE 64
|
|
#define LAG_HW_CURSOR_BYTES 1024 // 64x64x2bpp / 8 = 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;
|
|
DpmiMappingT lfbMapping;
|
|
DpmiMappingT mmioMapping;
|
|
} LagunaPrivateT;
|
|
|
|
// ============================================================
|
|
// Prototypes
|
|
// ============================================================
|
|
|
|
static void lagBitBlt(AccelDriverT *drv, int32_t srcX, int32_t srcY, int32_t dstX, int32_t dstY, int32_t w, int32_t h);
|
|
static void lagColorExpand(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 lagDetect(AccelDriverT *drv);
|
|
static void lagHostBlit(AccelDriverT *drv, const uint8_t *srcBuf, int32_t srcPitch, int32_t dstX, int32_t dstY, int32_t w, int32_t h);
|
|
static bool lagInit(AccelDriverT *drv, const AccelModeRequestT *req);
|
|
static void lagMoveCursor(AccelDriverT *drv, int32_t x, int32_t y);
|
|
static void lagRectFill(AccelDriverT *drv, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color);
|
|
static void lagSetClip(AccelDriverT *drv, int32_t x, int32_t y, int32_t w, int32_t h);
|
|
static void lagSetCursor(AccelDriverT *drv, const HwCursorImageT *image);
|
|
static void lagShowCursor(AccelDriverT *drv, bool visible);
|
|
static void lagShutdown(AccelDriverT *drv);
|
|
static void lagWaitIdle(AccelDriverT *drv);
|
|
|
|
static inline void lagWrite(LagunaPrivateT *priv, uint32_t reg, uint32_t val) {
|
|
priv->mmio[reg / 4] = val;
|
|
}
|
|
|
|
static inline uint32_t lagRead(LagunaPrivateT *priv, uint32_t reg) {
|
|
return priv->mmio[reg / 4];
|
|
}
|
|
|
|
// ============================================================
|
|
// Driver instance
|
|
// ============================================================
|
|
|
|
static LagunaPrivateT sLagunaPrivate;
|
|
|
|
static AccelDriverT sLagunaDriver = {
|
|
.name = "Cirrus Logic Laguna",
|
|
.chipFamily = "cirrus-laguna",
|
|
.caps = 0,
|
|
.privData = &sLagunaPrivate,
|
|
.detect = lagDetect,
|
|
.init = lagInit,
|
|
.shutdown = lagShutdown,
|
|
.waitIdle = lagWaitIdle,
|
|
.setClip = lagSetClip,
|
|
.rectFill = lagRectFill,
|
|
.rectFillPat = NULL,
|
|
.bitBlt = lagBitBlt,
|
|
.hostBlit = lagHostBlit,
|
|
.colorExpand = lagColorExpand,
|
|
.lineDraw = NULL,
|
|
.setCursor = lagSetCursor,
|
|
.moveCursor = lagMoveCursor,
|
|
.showCursor = lagShowCursor,
|
|
};
|
|
|
|
// ============================================================
|
|
// lagunaRegisterDriver
|
|
// ============================================================
|
|
|
|
void lagunaRegisterDriver(void) {
|
|
accelRegisterDriver(&sLagunaDriver);
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// lagBitBlt
|
|
// ============================================================
|
|
//
|
|
// Screen-to-screen BitBLT. Handles overlapping regions by
|
|
// selecting forward or reverse direction based on src/dst
|
|
// relationship.
|
|
|
|
static void lagBitBlt(AccelDriverT *drv, int32_t srcX, int32_t srcY, int32_t dstX, int32_t dstY, int32_t w, int32_t h) {
|
|
LagunaPrivateT *priv = (LagunaPrivateT *)drv->privData;
|
|
|
|
if (w <= 0 || h <= 0) {
|
|
return;
|
|
}
|
|
|
|
lagWaitIdle(drv);
|
|
|
|
// Determine direction for overlapping blits
|
|
uint32_t cmd = LAG_CMD_BITBLT | LAG_ROP_COPY;
|
|
|
|
if (dstY > srcY || (dstY == srcY && dstX > srcX)) {
|
|
// Reverse direction: start from bottom-right
|
|
cmd |= LAG_CMD_DIR_REV;
|
|
lagWrite(priv, LAG_SRCXY, (uint32_t)(srcX + w - 1) | ((uint32_t)(srcY + h - 1) << 16));
|
|
lagWrite(priv, LAG_DSTXY, (uint32_t)(dstX + w - 1) | ((uint32_t)(dstY + h - 1) << 16));
|
|
} else {
|
|
// Forward direction: start from top-left
|
|
lagWrite(priv, LAG_SRCXY, (uint32_t)srcX | ((uint32_t)srcY << 16));
|
|
lagWrite(priv, LAG_DSTXY, (uint32_t)dstX | ((uint32_t)dstY << 16));
|
|
}
|
|
|
|
lagWrite(priv, LAG_DSTSIZE, (uint32_t)(w - 1) | ((uint32_t)(h - 1) << 16));
|
|
lagWrite(priv, LAG_PITCH, ((uint32_t)priv->screenPitch << 16) | (uint32_t)priv->screenPitch);
|
|
|
|
// Trigger operation
|
|
lagWrite(priv, LAG_COMMAND, cmd);
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// lagColorExpand
|
|
// ============================================================
|
|
//
|
|
// Monochrome color expansion: convert 1bpp bitmap data to
|
|
// full-color pixels using the hardware color expand engine.
|
|
// Set foreground/background colors, then feed mono data
|
|
// through the host data window at MMIO + 0x0200.
|
|
|
|
static void lagColorExpand(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) {
|
|
LagunaPrivateT *priv = (LagunaPrivateT *)drv->privData;
|
|
|
|
if (w <= 0 || h <= 0) {
|
|
return;
|
|
}
|
|
|
|
int32_t bytesPerRow = (w + 7) / 8;
|
|
int32_t dwordsPerRow = (bytesPerRow + 3) / 4;
|
|
|
|
lagWaitIdle(drv);
|
|
|
|
lagWrite(priv, LAG_FGCOLOR, fg);
|
|
lagWrite(priv, LAG_BGCOLOR, bg);
|
|
lagWrite(priv, LAG_DSTXY, (uint32_t)dstX | ((uint32_t)dstY << 16));
|
|
lagWrite(priv, LAG_DSTSIZE, (uint32_t)(w - 1) | ((uint32_t)(h - 1) << 16));
|
|
lagWrite(priv, LAG_PITCH, ((uint32_t)priv->screenPitch << 16) | (uint32_t)priv->screenPitch);
|
|
|
|
// Start color expand operation
|
|
lagWrite(priv, LAG_COMMAND, LAG_CMD_COLOR_EXPAND | LAG_ROP_COPY | LAG_CMD_COLOREXP);
|
|
|
|
// Feed mono data row by row through host data window
|
|
volatile uint32_t *hostWin = (volatile uint32_t *)((volatile uint8_t *)priv->mmio + LAG_HOST_DATA);
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
lagWaitIdle(drv);
|
|
hostWin[0] = val;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// lagDetect
|
|
// ============================================================
|
|
|
|
static bool lagDetect(AccelDriverT *drv) {
|
|
int32_t matchIdx;
|
|
|
|
if (!pciFindDeviceList(sLagunaDeviceIds, &drv->pciDev, &matchIdx)) {
|
|
return false;
|
|
}
|
|
|
|
switch (drv->pciDev.deviceId) {
|
|
case LAGUNA_GD5462:
|
|
drv->name = "Cirrus Logic Laguna GD5462";
|
|
break;
|
|
case LAGUNA_GD5464:
|
|
drv->name = "Cirrus Logic Laguna GD5464";
|
|
break;
|
|
case LAGUNA_GD5465:
|
|
drv->name = "Cirrus Logic Laguna GD5465";
|
|
break;
|
|
default:
|
|
drv->name = "Cirrus Logic Laguna";
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// lagHostBlit
|
|
// ============================================================
|
|
//
|
|
// CPU-to-screen blit: transfer pixel data from system RAM to
|
|
// VRAM through the host data window at MMIO + 0x0200. Each
|
|
// row is padded to a dword boundary.
|
|
|
|
static void lagHostBlit(AccelDriverT *drv, const uint8_t *srcBuf, int32_t srcPitch, int32_t dstX, int32_t dstY, int32_t w, int32_t h) {
|
|
LagunaPrivateT *priv = (LagunaPrivateT *)drv->privData;
|
|
|
|
if (w <= 0 || h <= 0) {
|
|
return;
|
|
}
|
|
|
|
int32_t bytesPerRow = w * priv->bytesPerPixel;
|
|
int32_t dwordsPerRow = (bytesPerRow + 3) / 4;
|
|
|
|
lagWaitIdle(drv);
|
|
|
|
lagWrite(priv, LAG_DSTXY, (uint32_t)dstX | ((uint32_t)dstY << 16));
|
|
lagWrite(priv, LAG_DSTSIZE, (uint32_t)(w - 1) | ((uint32_t)(h - 1) << 16));
|
|
lagWrite(priv, LAG_PITCH, ((uint32_t)priv->screenPitch << 16) | (uint32_t)priv->screenPitch);
|
|
|
|
// Start host-to-screen blit
|
|
lagWrite(priv, LAG_COMMAND, LAG_CMD_HOST_BLIT | LAG_ROP_COPY);
|
|
|
|
// Feed pixel data row by row through host data window
|
|
volatile uint32_t *hostWin = (volatile uint32_t *)((volatile uint8_t *)priv->mmio + LAG_HOST_DATA);
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
lagWaitIdle(drv);
|
|
hostWin[0] = val;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// lagInit
|
|
// ============================================================
|
|
|
|
static bool lagInit(AccelDriverT *drv, const AccelModeRequestT *req) {
|
|
LagunaPrivateT *priv = (LagunaPrivateT *)drv->privData;
|
|
|
|
// Read BARs from PCI config space
|
|
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 (4KB)
|
|
if (!dpmiMapFramebuffer(priv->mmioPhysAddr, LAG_MMIO_SIZE, &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)) {
|
|
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;
|
|
|
|
// Wait for engine idle before configuring
|
|
lagWaitIdle(drv);
|
|
|
|
// Set up hardware cursor at end of VRAM
|
|
priv->cursorOffset = priv->vramSize - LAG_HW_CURSOR_BYTES;
|
|
priv->cursorOffset &= ~(LAG_HW_CURSOR_BYTES - 1);
|
|
|
|
drv->caps = ACAP_RECT_FILL
|
|
| ACAP_BITBLT
|
|
| ACAP_HOST_BLIT
|
|
| ACAP_COLOR_EXPAND
|
|
| ACAP_HW_CURSOR
|
|
| ACAP_CLIP;
|
|
|
|
// Set full-screen clip rectangle
|
|
lagSetClip(drv, 0, 0, vesa.width, vesa.height);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// lagMoveCursor
|
|
// ============================================================
|
|
|
|
static void lagMoveCursor(AccelDriverT *drv, int32_t x, int32_t y) {
|
|
LagunaPrivateT *priv = (LagunaPrivateT *)drv->privData;
|
|
|
|
if (x < 0) { x = 0; }
|
|
if (y < 0) { y = 0; }
|
|
|
|
lagWrite(priv, LAG_CUR_X, (uint32_t)x);
|
|
lagWrite(priv, LAG_CUR_Y, (uint32_t)y);
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// lagRectFill
|
|
// ============================================================
|
|
//
|
|
// Solid rectangle fill using command 0x02. Sets the foreground
|
|
// color, destination position, and size, then triggers the fill.
|
|
|
|
static void lagRectFill(AccelDriverT *drv, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) {
|
|
LagunaPrivateT *priv = (LagunaPrivateT *)drv->privData;
|
|
|
|
if (w <= 0 || h <= 0) {
|
|
return;
|
|
}
|
|
|
|
lagWaitIdle(drv);
|
|
|
|
lagWrite(priv, LAG_FGCOLOR, color);
|
|
lagWrite(priv, LAG_DSTXY, (uint32_t)x | ((uint32_t)y << 16));
|
|
lagWrite(priv, LAG_DSTSIZE, (uint32_t)(w - 1) | ((uint32_t)(h - 1) << 16));
|
|
lagWrite(priv, LAG_PITCH, ((uint32_t)priv->screenPitch << 16) | (uint32_t)priv->screenPitch);
|
|
|
|
// Trigger solid fill
|
|
lagWrite(priv, LAG_COMMAND, LAG_CMD_RECTFILL | LAG_ROP_COPY);
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// lagSetClip
|
|
// ============================================================
|
|
|
|
static void lagSetClip(AccelDriverT *drv, int32_t x, int32_t y, int32_t w, int32_t h) {
|
|
LagunaPrivateT *priv = (LagunaPrivateT *)drv->privData;
|
|
|
|
lagWrite(priv, LAG_CLIPLT, (uint32_t)x | ((uint32_t)y << 16));
|
|
lagWrite(priv, LAG_CLIPRB, (uint32_t)(x + w - 1) | ((uint32_t)(y + h - 1) << 16));
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// lagSetCursor
|
|
// ============================================================
|
|
//
|
|
// Upload a hardware cursor image to VRAM at the cursor offset.
|
|
// The Laguna uses a 64x64 2bpp AND/XOR format stored in VRAM.
|
|
|
|
static void lagSetCursor(AccelDriverT *drv, const HwCursorImageT *image) {
|
|
LagunaPrivateT *priv = (LagunaPrivateT *)drv->privData;
|
|
|
|
if (!image) {
|
|
lagShowCursor(drv, false);
|
|
return;
|
|
}
|
|
|
|
lagWaitIdle(drv);
|
|
|
|
uint8_t *cursorMem = drv->mode.framebuffer + priv->cursorOffset;
|
|
|
|
for (int32_t row = 0; row < LAG_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; // transparent
|
|
xorByte = 0x00;
|
|
}
|
|
|
|
cursorMem[row * 16 + byte] = andByte;
|
|
cursorMem[row * 16 + byte + 8] = xorByte;
|
|
}
|
|
}
|
|
|
|
// Set cursor VRAM address
|
|
lagWrite(priv, LAG_CUR_ADDR, priv->cursorOffset);
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// lagShowCursor
|
|
// ============================================================
|
|
|
|
static void lagShowCursor(AccelDriverT *drv, bool visible) {
|
|
LagunaPrivateT *priv = (LagunaPrivateT *)drv->privData;
|
|
|
|
uint32_t ctrl = lagRead(priv, LAG_CUR_CTRL);
|
|
|
|
if (visible) {
|
|
ctrl |= 0x01;
|
|
} else {
|
|
ctrl &= ~0x01;
|
|
}
|
|
|
|
lagWrite(priv, LAG_CUR_CTRL, ctrl);
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// lagShutdown
|
|
// ============================================================
|
|
|
|
static void lagShutdown(AccelDriverT *drv) {
|
|
LagunaPrivateT *priv = (LagunaPrivateT *)drv->privData;
|
|
|
|
lagShowCursor(drv, false);
|
|
dpmiUnmapFramebuffer(&priv->mmioMapping);
|
|
dpmiUnmapFramebuffer(&priv->lfbMapping);
|
|
vgaRestoreTextMode();
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// lagWaitIdle
|
|
// ============================================================
|
|
//
|
|
// Poll the CONTROL register until bit 0 (engine busy) clears.
|
|
// Bounded by LAG_MAX_IDLE_WAIT iterations to avoid hangs on
|
|
// hardware failure.
|
|
|
|
static void lagWaitIdle(AccelDriverT *drv) {
|
|
LagunaPrivateT *priv = (LagunaPrivateT *)drv->privData;
|
|
|
|
for (int32_t i = 0; i < LAG_MAX_IDLE_WAIT; i++) {
|
|
uint32_t stat = lagRead(priv, LAG_CONTROL);
|
|
if (!(stat & LAG_STATUS_BUSY)) {
|
|
return;
|
|
}
|
|
}
|
|
}
|