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