732 lines
24 KiB
C
732 lines
24 KiB
C
// 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 <pc.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/nearptr.h>
|
|
|
|
// ============================================================
|
|
// 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;
|
|
}
|
|
}
|
|
}
|