806353ec9e
Tilting allows using left/right rotated or invetrted display orientation. This can be changed at runtime such as: echo tilt right > /dev/vgactl This removes the old panning and vga overlays as they are only implemented with some ancient vga controllers.
866 lines
20 KiB
C
866 lines
20 KiB
C
#include "u.h"
|
|
#include "../port/lib.h"
|
|
#include "mem.h"
|
|
#include "dat.h"
|
|
#include "fns.h"
|
|
#include "io.h"
|
|
#include "../port/pci.h"
|
|
#include "../port/error.h"
|
|
|
|
#define Image IMAGE
|
|
#include <draw.h>
|
|
#include <memdraw.h>
|
|
#include <cursor.h>
|
|
#include "screen.h"
|
|
|
|
char Eunsupportedformat[] = "unsupported video format";
|
|
char Enotconfigured[] = "device not configured";
|
|
|
|
#define SCALE_ZERO_EXTEND 0x0
|
|
#define SCALE_DYNAMIC 0x1
|
|
#define SCALE_RED_TEMP_6500K 0x0
|
|
#define SCALE_RED_TEMP_9800K 0x2
|
|
#define SCALE_HORZ_BLEND 0x0
|
|
#define SCALE_HORZ_REP 0x4
|
|
#define SCALE_VERT_BLEND 0x0
|
|
#define SCALE_VERT_REP 0x8
|
|
#define SCALE_BANDWIDTH_NORMAL 0x0
|
|
#define SCALE_BANDWIDTH_EXCEEDED 0x4000000
|
|
#define SCALE_BANDWIDTH_RESET 0x4000000
|
|
#define SCALE_CLK_ACTIVITY 0x0
|
|
#define SCALE_CLK_CONTINUOUS 0x20000000
|
|
#define OVERLAY_DISABLE 0x0
|
|
#define OVERLAY_ENABLE 0x40000000
|
|
#define SCALE_DISABLE 0x0
|
|
#define SCALE_ENABLE 0x80000000
|
|
|
|
#define SCALER_FRAME_READ_MODE_FULL 0x0
|
|
#define SCALER_BUF_MODE_SINGLE 0x0
|
|
#define SCALER_BUF_MODE_DOUBLE 0x40000
|
|
#define SCALER_BUF_NEXT_0 0x0
|
|
#define SCALER_BUF_NEXT_1 0x80000
|
|
#define SCALER_BUF_STATUS_0 0x0
|
|
#define SCALER_BUF_STATUS_1 0x100000
|
|
|
|
#define OVERLAY_MIX_G_CMP 0x0
|
|
#define OVERLAY_MIX_ALWAYS_G 0x100
|
|
#define OVERLAY_MIX_ALWAYS_V 0x200
|
|
#define OVERLAY_MIX_NOT_G 0x300
|
|
#define OVERLAY_MIX_NOT_V 0x400
|
|
#define OVERLAY_MIX_G_XOR_V 0x500
|
|
#define OVERLAY_MIX_NOT_G_XOR_V 0x600
|
|
#define OVERLAY_MIX_V_CMP 0x700
|
|
#define OVERLAY_MIX_NOT_G_OR_NOT_V 0x800
|
|
#define OVERLAY_MIX_G_OR_NOT_V 0x900
|
|
#define OVERLAY_MIX_NOT_G_OR_V 0xA00
|
|
#define OVERLAY_MIX_G_OR_V 0xB00
|
|
#define OVERLAY_MIX_G_AND_V 0xC00
|
|
#define OVERLAY_MIX_NOT_G_AND_V 0xD00
|
|
#define OVERLAY_MIX_G_AND_NOT_V 0xE00
|
|
#define OVERLAY_MIX_NOT_G_AND_NOT_V 0xF00
|
|
#define OVERLAY_EXCLUSIVE_NORMAL 0x0
|
|
#define OVERLAY_EXCLUSIVE_V_ONLY 0x80000000
|
|
|
|
#define VIDEO_IN_8BPP 0x2
|
|
#define VIDEO_IN_16BPP 0x4
|
|
#define VIDEO_IN_32BPP 0x6
|
|
#define VIDEO_IN_VYUY422 0xB /*16 bpp */
|
|
#define VIDEO_IN_YVYU422 0xC /* 16 bpp */
|
|
#define SCALE_IN_15BPP 0x30000 /* aRGB 1555 */
|
|
#define SCALE_IN_16BPP 0x40000 /* RGB 565 */
|
|
#define SCALE_IN_32BPP 0x60000 /* aRGB 8888 */
|
|
#define SCALE_IN_YUV9 0x90000 /* planar */
|
|
#define SCALE_IN_YUV12 0xA0000 /* planar */
|
|
#define SCALE_IN_VYUY422 0xB0000 /* 16 bpp */
|
|
#define SCALE_IN_YVYU422 0xC0000 /* 16 bpp */
|
|
#define HOST_YUV_APERTURE_UPPER 0x0
|
|
#define HOST_YUV_APERTURE_LOWER 0x20000000
|
|
#define HOST_MEM_MODE_Y 0x40000000
|
|
#define HOST_MEM_MODE_U 0x80000000
|
|
#define HOST_MEM_MODE_V 0xC0000000
|
|
#define HOST_MEM_MODE_NORMAL HOST_YUV_APERTURE_UPPER
|
|
|
|
enum {
|
|
VTGTB1S1 = 0x01, /* Asic description for VTB1S1 and GTB1S1. */
|
|
VT4GTIIC = 0x3A, /* asic descr for VT4 and RAGE IIC */
|
|
GTB1U1 = 0x19, /* Asic description for GTB1U1. */
|
|
GTB1S2 = 0x41, /* Asic description for GTB1S2. */
|
|
GTB2U1 = 0x1A,
|
|
GTB2U2 = 0x5A,
|
|
GTB2U3 = 0x9A,
|
|
GTIIIC1U1 = 0x1B, /* 3D RAGE PRO asic descrp. */
|
|
GTIIIC1U2 = 0x5B, /* 3D RAGE PRO asic descrp. */
|
|
GTIIIC2U1 = 0x1C, /* 3D RAGE PRO asic descrp. */
|
|
GTIIIC2U2 = 0x5C, /* 3D RAGE PRO asic descrp. */
|
|
GTIIIC2U3 = 0x7C, /* 3D RAGE PRO asic descrp. */
|
|
GTBC = 0x3A, /* 3D RAGE IIC asic descrp. */
|
|
LTPRO = 0x9C, /* 3D RAGE LT PRO */
|
|
};
|
|
|
|
/*
|
|
* ATI Mach64(CT|ET|G*|V*|L*).
|
|
*/
|
|
typedef struct Mach64types Mach64types;
|
|
struct Mach64types {
|
|
ushort m64_id; /* Chip ID */
|
|
int m64_vtgt; /* Is this a VT or GT chipset? */
|
|
ulong m64_ovlclock; /* Max. overlay clock frequency */
|
|
int m64_pro; /* Is this a PRO? */
|
|
};
|
|
|
|
static ulong mach64refclock;
|
|
static Mach64types *mach64type;
|
|
static int mach64revb; /* Revision B or greater? */
|
|
|
|
static Mach64types mach64s[] = {
|
|
('C'<<8)|'T', 0, 1350000, /*?*/ 0, /* 4354: CT */
|
|
('E'<<8)|'T', 0, 1350000, /*?*/ 0, /* 4554: ET */
|
|
('G'<<8)|'B', 1, 1250000, 1, /* 4742: 264GT PRO */
|
|
('G'<<8)|'D', 1, 1250000, 1, /* 4744: 264GT PRO */
|
|
('G'<<8)|'I', 1, 1250000, 1, /* 4749: 264GT PRO */
|
|
('G'<<8)|'M', 0, 1350000, 0, /* 474D: Rage XL */
|
|
('G'<<8)|'P', 1, 1250000, 1, /* 4750: 264GT PRO */
|
|
('G'<<8)|'Q', 1, 1250000, 1, /* 4751: 264GT PRO */
|
|
('G'<<8)|'R', 1, 1250000, 1, /* 4752: */
|
|
('G'<<8)|'T', 1, 800000, 0, /* 4754: 264GT[B] */
|
|
('G'<<8)|'U', 1, 1000000, 0, /* 4755: 264GT DVD */
|
|
('G'<<8)|'V', 1, 1000000, 0, /* 4756: Rage2C */
|
|
('G'<<8)|'Z', 1, 1000000, 0, /* 475A: Rage2C */
|
|
('V'<<8)|'T', 1, 800000, 0, /* 5654: 264VT/GT/VTB */
|
|
('V'<<8)|'U', 1, 800000, 0, /* 5655: 264VT3 */
|
|
('V'<<8)|'V', 1, 1000000, 0, /* 5656: 264VT4 */
|
|
('L'<<8)|'B', 0, 1350000, 1, /* 4C42: Rage LTPro AGP */
|
|
('L'<<8)|'I', 0, 1350000, 0, /* 4C49: Rage LTPro AGP */
|
|
('L'<<8)|'M', 0, 1350000, 0, /* 4C4D: Rage Mobility */
|
|
('L'<<8)|'P', 0, 1350000, 1, /* 4C50: 264LT PRO */
|
|
};
|
|
|
|
|
|
static int hwfill(VGAscr*, Rectangle, ulong);
|
|
static int hwscroll(VGAscr*, Rectangle, Rectangle);
|
|
static void initengine(VGAscr*);
|
|
|
|
static void
|
|
mach64xxenable(VGAscr* scr)
|
|
{
|
|
Pcidev *p;
|
|
int i;
|
|
|
|
if(scr->io)
|
|
return;
|
|
p = scr->pci;
|
|
if(p == nil || p->vid != 0x1002)
|
|
return;
|
|
|
|
mach64type = nil;
|
|
for (i = 0; i != nelem(mach64s); i++)
|
|
if (mach64s[i].m64_id == p->did) {
|
|
scr->id = p->did;
|
|
mach64type = &mach64s[i];
|
|
break;
|
|
}
|
|
|
|
if(mach64type != nil){
|
|
/*
|
|
* The CT doesn't always have the I/O base address
|
|
* in the PCI base registers. There is a way to find
|
|
* it via the vendor-specific PCI config space but
|
|
* this will do for now.
|
|
*/
|
|
scr->io = p->mem[1].bar & ~0x03;
|
|
if(scr->io == 0)
|
|
scr->io = 0x2EC;
|
|
}
|
|
}
|
|
|
|
static void
|
|
mach64xxlinear(VGAscr* scr, int size, int)
|
|
{
|
|
vgalinearpci(scr);
|
|
if(scr->paddr == 0)
|
|
return;
|
|
scr->mmio = (ulong*)((uchar*)scr->vaddr+size-1024);
|
|
addvgaseg("mach64mmio", scr->paddr+size-BY2PG, BY2PG);
|
|
addvgaseg("mach64screen", scr->paddr, scr->apsize);
|
|
}
|
|
|
|
enum {
|
|
CrtcOffPitch = 0x05,
|
|
CrtcGenCtl = 0x07,
|
|
CurClr0 = 0x0B, /* I/O Select */
|
|
CurClr1 = 0x0C,
|
|
CurOffset = 0x0D,
|
|
CurHVposn = 0x0E,
|
|
CurHVoff = 0x0F,
|
|
BusCntl = 0x13,
|
|
GenTestCntl = 0x19,
|
|
|
|
CrtcHsyncDis = 0x04,
|
|
CrtcVsyncDis = 0x08,
|
|
|
|
ContextMask = 0x100, /* not accessible via I/O */
|
|
FifoStat,
|
|
GuiStat,
|
|
DpFrgdClr,
|
|
DpBkgdClr,
|
|
DpWriteMask,
|
|
DpMix,
|
|
DpPixWidth,
|
|
DpSrc,
|
|
ClrCmpCntl,
|
|
GuiTrajCntl,
|
|
ScLeftRight,
|
|
ScTopBottom,
|
|
DstOffPitch,
|
|
DstYX,
|
|
DstHeightWidth,
|
|
DstCntl,
|
|
DstHeight,
|
|
DstBresErr,
|
|
DstBresInc,
|
|
DstBresDec,
|
|
SrcCntl,
|
|
SrcHeight1Width1,
|
|
SrcHeight2Width2,
|
|
SrcYX,
|
|
SrcWidth1,
|
|
SrcYXstart,
|
|
HostCntl,
|
|
PatReg0,
|
|
PatReg1,
|
|
PatCntl,
|
|
ScBottom,
|
|
ScLeft,
|
|
ScRight,
|
|
ScTop,
|
|
ClrCmpClr,
|
|
ClrCmpMask,
|
|
DpChainMask,
|
|
SrcOffPitch,
|
|
LcdIndex,
|
|
LcdData,
|
|
ClockCntl,
|
|
OverlayScaleCntl,
|
|
ConfigChipId,
|
|
Buf0Pitch,
|
|
ScalerBuf0Pitch,
|
|
CaptureConfig,
|
|
OverlayKeyCntl,
|
|
ScalerColourCntl,
|
|
ScalerHCoef0,
|
|
ScalerHCoef1,
|
|
ScalerHCoef2,
|
|
ScalerHCoef3,
|
|
ScalerHCoef4,
|
|
VideoFormat,
|
|
Buf0Offset,
|
|
ScalerBuf0Offset,
|
|
CrtcGenCntl,
|
|
OverlayScaleInc,
|
|
OverlayYX,
|
|
OverlayYXEnd,
|
|
ScalerHeightWidth,
|
|
HTotalDisp,
|
|
VTotalDisp,
|
|
};
|
|
|
|
enum {
|
|
LCD_ConfigPanel = 0,
|
|
LCD_GenCtrl,
|
|
LCD_DstnCntl,
|
|
LCD_HfbPitchAddr,
|
|
LCD_HorzStretch,
|
|
LCD_VertStretch,
|
|
LCD_ExtVertStretch,
|
|
LCD_LtGio,
|
|
LCD_PowerMngmnt,
|
|
LCD_ZvgPio,
|
|
Nlcd,
|
|
};
|
|
|
|
#define Bank1 (-0x100) /* 1KB */
|
|
|
|
static int mmoffset[] = {
|
|
[HTotalDisp] 0x00,
|
|
[VTotalDisp] 0x02,
|
|
[CrtcOffPitch] 0x05,
|
|
[CrtcGenCntl] 0x07,
|
|
[CurClr0] 0x18,
|
|
[CurClr1] 0x19,
|
|
[CurOffset] 0x1A,
|
|
[CurHVposn] 0x1B,
|
|
[CurHVoff] 0x1C,
|
|
[ClockCntl] 0x24,
|
|
[BusCntl] 0x28,
|
|
[LcdIndex] 0x29,
|
|
[LcdData] 0x2A,
|
|
[GenTestCntl] 0x34,
|
|
[ConfigChipId] 0x38,
|
|
[DstOffPitch] 0x40,
|
|
[DstYX] 0x43,
|
|
[DstHeight] 0x45,
|
|
[DstHeightWidth] 0x46,
|
|
[DstBresErr] 0x49,
|
|
[DstBresInc] 0x4A,
|
|
[DstBresDec] 0x4B,
|
|
[DstCntl] 0x4C,
|
|
[SrcOffPitch] 0x60,
|
|
[SrcYX] 0x63,
|
|
[SrcWidth1] 0x64,
|
|
[SrcYXstart] 0x69,
|
|
[SrcHeight1Width1] 0x66,
|
|
[SrcHeight2Width2] 0x6C,
|
|
[SrcCntl] 0x6D,
|
|
[HostCntl] 0x90,
|
|
[PatReg0] 0xA0,
|
|
[PatReg1] 0xA1,
|
|
[PatCntl] 0xA2,
|
|
[ScLeft] 0xA8,
|
|
[ScRight] 0xA9,
|
|
[ScLeftRight] 0xAA,
|
|
[ScTop] 0xAB,
|
|
[ScBottom] 0xAC,
|
|
[ScTopBottom] 0xAD,
|
|
[DpBkgdClr] 0xB0,
|
|
[DpFrgdClr] 0xB1,
|
|
[DpWriteMask] 0xB2,
|
|
[DpChainMask] 0xB3,
|
|
[DpPixWidth] 0xB4,
|
|
[DpMix] 0xB5,
|
|
[DpSrc] 0xB6,
|
|
[ClrCmpClr] 0xC0,
|
|
[ClrCmpMask] 0xC1,
|
|
[ClrCmpCntl] 0xC2,
|
|
[FifoStat] 0xC4,
|
|
[ContextMask] 0xC8,
|
|
[GuiTrajCntl] 0xCC,
|
|
[GuiStat] 0xCE,
|
|
|
|
/* Bank1 */
|
|
[OverlayYX] Bank1 + 0x00,
|
|
[OverlayYXEnd] Bank1 + 0x01,
|
|
[OverlayKeyCntl] Bank1 + 0x06,
|
|
[OverlayScaleInc] Bank1 + 0x08,
|
|
[OverlayScaleCntl] Bank1 + 0x09,
|
|
[ScalerHeightWidth] Bank1 + 0x0A,
|
|
[ScalerBuf0Offset] Bank1 + 0x0D,
|
|
[ScalerBuf0Pitch] Bank1 + 0x0F,
|
|
[VideoFormat] Bank1 + 0x12,
|
|
[CaptureConfig] Bank1 + 0x14,
|
|
[Buf0Offset] Bank1 + 0x20,
|
|
[Buf0Pitch] Bank1 + 0x23,
|
|
[ScalerColourCntl] Bank1 + 0x54,
|
|
[ScalerHCoef0] Bank1 + 0x55,
|
|
[ScalerHCoef1] Bank1 + 0x56,
|
|
[ScalerHCoef2] Bank1 + 0x57,
|
|
[ScalerHCoef3] Bank1 + 0x58,
|
|
[ScalerHCoef4] Bank1 + 0x59,
|
|
};
|
|
|
|
static ulong
|
|
ior32(VGAscr* scr, int r)
|
|
{
|
|
if(scr->io == 0x2EC || scr->io == 0x1C8)
|
|
return inl((r<<10)+scr->io);
|
|
if(r >= 0x100 && scr->mmio != nil)
|
|
return scr->mmio[mmoffset[r]];
|
|
return inl((mmoffset[r]<<2)+scr->io);
|
|
}
|
|
|
|
static void
|
|
iow32(VGAscr* scr, int r, ulong l)
|
|
{
|
|
if(scr->io == 0x2EC || scr->io == 0x1C8)
|
|
outl(((r)<<10)+scr->io, l);
|
|
else if(r >= 0x100 && scr->mmio != nil)
|
|
scr->mmio[mmoffset[r]] = l;
|
|
else
|
|
outl((mmoffset[r]<<2)+scr->io, l);
|
|
}
|
|
|
|
static ulong
|
|
lcdr32(VGAscr *scr, ulong r)
|
|
{
|
|
ulong or;
|
|
|
|
or = ior32(scr, LcdIndex);
|
|
iow32(scr, LcdIndex, (or&~0x0F) | (r&0x0F));
|
|
return ior32(scr, LcdData);
|
|
}
|
|
|
|
static void
|
|
lcdw32(VGAscr *scr, ulong r, ulong v)
|
|
{
|
|
ulong or;
|
|
|
|
or = ior32(scr, LcdIndex);
|
|
iow32(scr, LcdIndex, (or&~0x0F) | (r&0x0F));
|
|
iow32(scr, LcdData, v);
|
|
}
|
|
|
|
static void
|
|
mach64xxcurdisable(VGAscr* scr)
|
|
{
|
|
ulong r;
|
|
|
|
r = ior32(scr, GenTestCntl);
|
|
iow32(scr, GenTestCntl, r & ~0x80);
|
|
}
|
|
|
|
static void
|
|
mach64xxcurload(VGAscr* scr, Cursor* curs)
|
|
{
|
|
uchar *p;
|
|
int i, y;
|
|
ulong c, s, m, r;
|
|
|
|
/*
|
|
* Disable the cursor.
|
|
*/
|
|
r = ior32(scr, GenTestCntl);
|
|
iow32(scr, GenTestCntl, r & ~0x80);
|
|
|
|
p = scr->vaddr;
|
|
p += scr->storage;
|
|
|
|
/*
|
|
* Initialise the 64x64 cursor RAM array.
|
|
* The cursor mode gives the following truth table:
|
|
* p1 p0 colour
|
|
* 0 0 Cursor Colour 0
|
|
* 0 1 Cursor Colour 1
|
|
* 1 0 Transparent
|
|
* 1 1 Complement
|
|
* Put the cursor into the top-right of the 64x64 array.
|
|
*/
|
|
for(y = 0; y < 16; y++){
|
|
for(i = 0; i < (64-16)/8; i++){
|
|
*p++ = 0xAA;
|
|
*p++ = 0xAA;
|
|
}
|
|
|
|
c = (curs->clr[2*y]<<8)|curs->clr[y*2 + 1];
|
|
s = (curs->set[2*y]<<8)|curs->set[y*2 + 1];
|
|
|
|
m = 0x00000000;
|
|
for(i = 0; i < 16; i++){
|
|
if(s & (1<<(15-i)))
|
|
m |= 0x01<<(2*i);
|
|
else if(c & (1<<(15-i))){
|
|
/* nothing to do */
|
|
}
|
|
else
|
|
m |= 0x02<<(2*i);
|
|
}
|
|
*p++ = m;
|
|
*p++ = m>>8;
|
|
*p++ = m>>16;
|
|
*p++ = m>>24;
|
|
}
|
|
memset(p, 0xAA, (64-16)*16);
|
|
|
|
/*
|
|
* Set the cursor hotpoint and enable the cursor.
|
|
*/
|
|
scr->offset = curs->offset;
|
|
iow32(scr, GenTestCntl, 0x80|r);
|
|
}
|
|
|
|
static int
|
|
ptalmostinrect(Point p, Rectangle r)
|
|
{
|
|
return p.x>=r.min.x && p.x<=r.max.x &&
|
|
p.y>=r.min.y && p.y<=r.max.y;
|
|
}
|
|
|
|
static int
|
|
mach64xxcurmove(VGAscr* scr, Point p)
|
|
{
|
|
int x, xo, y, yo;
|
|
|
|
/*
|
|
* Mustn't position the cursor offscreen even partially,
|
|
* or it disappears. Therefore, if x or y is -ve, adjust the
|
|
* cursor presets instead. If y is negative also have to
|
|
* adjust the starting offset.
|
|
*/
|
|
if((x = p.x+scr->offset.x) < 0){
|
|
xo = x;
|
|
x = 0;
|
|
}
|
|
else
|
|
xo = 0;
|
|
if((y = p.y+scr->offset.y) < 0){
|
|
yo = y;
|
|
y = 0;
|
|
}
|
|
else
|
|
yo = 0;
|
|
|
|
iow32(scr, CurHVoff, ((64-16-yo)<<16)|(64-16-xo));
|
|
iow32(scr, CurOffset, scr->storage/8 + (-yo*2));
|
|
iow32(scr, CurHVposn, (y<<16)|x);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
mach64xxcurenable(VGAscr* scr)
|
|
{
|
|
ulong r, storage;
|
|
|
|
mach64xxenable(scr);
|
|
if(scr->io == 0)
|
|
return;
|
|
|
|
r = ior32(scr, GenTestCntl);
|
|
iow32(scr, GenTestCntl, r & ~0x80);
|
|
|
|
iow32(scr, CurClr0, (Pwhite<<24)|(Pwhite<<16)|(Pwhite<<8)|Pwhite);
|
|
iow32(scr, CurClr1, (Pblack<<24)|(Pblack<<16)|(Pblack<<8)|Pblack);
|
|
|
|
/*
|
|
* Find a place for the cursor data in display memory.
|
|
* Must be 64-bit aligned.
|
|
*/
|
|
storage = (scr->pitch*scr->height+7)/8;
|
|
iow32(scr, CurOffset, storage);
|
|
scr->storage = storage*8;
|
|
|
|
/*
|
|
* Cursor goes in the top right corner of the 64x64 array
|
|
* so the horizontal and vertical presets are 64-16.
|
|
*/
|
|
iow32(scr, CurHVposn, (0<<16)|0);
|
|
iow32(scr, CurHVoff, ((64-16)<<16)|(64-16));
|
|
|
|
/*
|
|
* Load, locate and enable the 64x64 cursor.
|
|
*/
|
|
mach64xxcurload(scr, &cursor);
|
|
mach64xxcurmove(scr, ZP);
|
|
iow32(scr, GenTestCntl, 0x80|r);
|
|
}
|
|
|
|
static void
|
|
waitforfifo(VGAscr *scr, int entries)
|
|
{
|
|
int x;
|
|
|
|
x = 0;
|
|
while((ior32(scr, FifoStat)&0xFF) > (0x8000>>entries) && x++ < 1000000)
|
|
;
|
|
if(x >= 1000000)
|
|
iprint("fifo %d stat %#.8lux %#.8lux scrio %#.8lux mmio %#p scr %#p pc %#p\n", entries, ior32(scr, FifoStat), scr->mmio[mmoffset[FifoStat]], scr->io, scr->mmio, scr, getcallerpc(&scr));
|
|
}
|
|
|
|
static void
|
|
waitforidle(VGAscr *scr)
|
|
{
|
|
int x;
|
|
|
|
waitforfifo(scr, 16);
|
|
x = 0;
|
|
while((ior32(scr, GuiStat)&1) && x++ < 1000000)
|
|
;
|
|
if(x >= 1000000)
|
|
iprint("idle stat %#.8lux %#.8lux scrio %#.8lux mmio %#p scr %#p pc %#p\n", ior32(scr, GuiStat), scr->mmio[mmoffset[GuiStat]], scr->io, scr->mmio, scr, getcallerpc(&scr));
|
|
}
|
|
|
|
static void
|
|
resetengine(VGAscr *scr)
|
|
{
|
|
ulong x;
|
|
x = ior32(scr, GenTestCntl);
|
|
iow32(scr, GenTestCntl, x&~0x100);
|
|
iow32(scr, GenTestCntl, x|0x100);
|
|
iow32(scr, BusCntl, ior32(scr, BusCntl)|0x00A00000);
|
|
}
|
|
|
|
static void
|
|
initengine(VGAscr *scr)
|
|
{
|
|
uchar *bios;
|
|
ushort table;
|
|
|
|
resetengine(scr);
|
|
waitforfifo(scr, 14);
|
|
iow32(scr, ContextMask, ~0);
|
|
iow32(scr, DstOffPitch, scr->pitch<<22);
|
|
iow32(scr, DstYX, 0);
|
|
iow32(scr, DstHeight, 0);
|
|
iow32(scr, DstBresErr, 0);
|
|
iow32(scr, DstBresInc, 0);
|
|
iow32(scr, DstBresDec, 0);
|
|
iow32(scr, DstCntl, 0x23);
|
|
iow32(scr, SrcOffPitch, scr->pitch<<22);
|
|
iow32(scr, SrcYX, 0);
|
|
iow32(scr, SrcHeight1Width1, 1);
|
|
iow32(scr, SrcYXstart, 0);
|
|
iow32(scr, SrcHeight2Width2, 1);
|
|
iow32(scr, SrcCntl, 0x01);
|
|
|
|
waitforfifo(scr, 13);
|
|
iow32(scr, HostCntl, 0);
|
|
iow32(scr, PatReg0, 0);
|
|
iow32(scr, PatReg1, 0);
|
|
iow32(scr, PatCntl, 0);
|
|
iow32(scr, ScLeft, 0);
|
|
iow32(scr, ScTop, 0);
|
|
iow32(scr, ScBottom, 0xFFFF);
|
|
iow32(scr, ScRight, 0xFFFF);
|
|
iow32(scr, DpBkgdClr, 0);
|
|
iow32(scr, DpFrgdClr, ~0);
|
|
iow32(scr, DpWriteMask, ~0);
|
|
iow32(scr, DpMix, 0x70003);
|
|
iow32(scr, DpSrc, 0x00010100);
|
|
|
|
waitforfifo(scr, 3);
|
|
iow32(scr, ClrCmpClr, 0);
|
|
iow32(scr, ClrCmpMask, ~0);
|
|
iow32(scr, ClrCmpCntl, 0);
|
|
|
|
waitforfifo(scr, 2);
|
|
switch(scr->gscreen->depth){
|
|
case 8:
|
|
case 24: /* [sic] */
|
|
iow32(scr, DpPixWidth, 0x00020202);
|
|
iow32(scr, DpChainMask, 0x8080);
|
|
break;
|
|
case 16:
|
|
iow32(scr, DpPixWidth, 0x00040404);
|
|
iow32(scr, DpChainMask, 0x8410);
|
|
break;
|
|
case 32:
|
|
iow32(scr, DpPixWidth, 0x00060606);
|
|
iow32(scr, DpChainMask, 0x8080);
|
|
break;
|
|
}
|
|
|
|
/* Get the base freq from the BIOS */
|
|
bios = kaddr(0xC000);
|
|
table = *(ushort *)(bios + 0x48);
|
|
table = *(ushort *)(bios + table + 0x10);
|
|
switch (*(ushort *)(bios + table + 0x08)) {
|
|
case 2700:
|
|
mach64refclock = 270000;
|
|
break;
|
|
case 2863:
|
|
case 2864:
|
|
mach64refclock = 286363;
|
|
break;
|
|
case 2950:
|
|
mach64refclock = 294989;
|
|
break;
|
|
case 1432:
|
|
default:
|
|
mach64refclock = 143181;
|
|
break ;
|
|
}
|
|
|
|
/* Figure out which revision this chip is */
|
|
switch ((scr->mmio[mmoffset[ConfigChipId]] >> 24) & 0xFF) {
|
|
case VTGTB1S1:
|
|
case GTB1U1:
|
|
case GTB1S2:
|
|
case GTB2U1:
|
|
case GTB2U2:
|
|
case GTB2U3:
|
|
case GTBC:
|
|
case GTIIIC1U1:
|
|
case GTIIIC1U2:
|
|
case GTIIIC2U1:
|
|
case GTIIIC2U2:
|
|
case GTIIIC2U3:
|
|
case LTPRO:
|
|
mach64revb = 1;
|
|
break;
|
|
default:
|
|
mach64revb = 0;
|
|
break;
|
|
}
|
|
|
|
waitforidle(scr);
|
|
}
|
|
|
|
static int
|
|
mach64hwfill(VGAscr *scr, Rectangle r, ulong sval)
|
|
{
|
|
ulong ctl;
|
|
|
|
/* shouldn't happen */
|
|
if(scr->io == 0x2EC || scr->io == 0x1C8 || scr->io == 0)
|
|
return 0;
|
|
|
|
ctl = 1|2; /* left-to-right, top-to-bottom */
|
|
if(scr->gscreen->depth == 24){
|
|
r.min.x *= 3;
|
|
r.max.x *= 3;
|
|
ctl |= (1<<7)|(((r.min.x/4)%6)<<8);
|
|
}
|
|
|
|
waitforfifo(scr, 11);
|
|
iow32(scr, DpFrgdClr, sval);
|
|
iow32(scr, DpWriteMask, 0xFFFFFFFF);
|
|
iow32(scr, DpMix, 0x00070003);
|
|
iow32(scr, DpSrc, 0x00000111);
|
|
iow32(scr, ClrCmpCntl, 0x00000000);
|
|
iow32(scr, ScLeftRight, 0x1FFF0000);
|
|
iow32(scr, ScTopBottom, 0x1FFF0000);
|
|
iow32(scr, DstOffPitch, scr->pitch<<22);
|
|
iow32(scr, DstCntl, ctl);
|
|
iow32(scr, DstYX, (r.min.x<<16)|r.min.y);
|
|
iow32(scr, DstHeightWidth, (Dx(r)<<16)|Dy(r));
|
|
|
|
waitforidle(scr);
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
mach64hwscroll(VGAscr *scr, Rectangle r, Rectangle sr)
|
|
{
|
|
Point dp, sp;
|
|
ulong ctl;
|
|
int dx, dy;
|
|
|
|
dx = Dx(r);
|
|
dy = Dy(r);
|
|
if(scr->gscreen->depth == 24){
|
|
dx *= 3;
|
|
r.min.x *= 3;
|
|
sr.min.x *= 3;
|
|
}
|
|
|
|
ctl = 0;
|
|
if(r.min.x <= sr.min.x){
|
|
ctl |= 1;
|
|
dp.x = r.min.x;
|
|
sp.x = sr.min.x;
|
|
}else{
|
|
dp.x = r.min.x+dx-1;
|
|
sp.x = sr.min.x+dx-1;
|
|
}
|
|
|
|
if(r.min.y <= sr.min.y){
|
|
ctl |= 2;
|
|
dp.y = r.min.y;
|
|
sp.y = sr.min.y;
|
|
}else{
|
|
dp.y = r.min.y+dy-1;
|
|
sp.y = sr.min.y+dy-1;
|
|
}
|
|
|
|
if(scr->gscreen->depth == 24)
|
|
ctl |= (1<<7)|(((dp.x/4)%6)<<8);
|
|
|
|
waitforfifo(scr, 6);
|
|
iow32(scr, ScLeftRight, 0x1FFF0000);
|
|
iow32(scr, ScTopBottom, 0x1FFF0000);
|
|
iow32(scr, DpWriteMask, 0xFFFFFFFF);
|
|
iow32(scr, DpMix, 0x00070003);
|
|
iow32(scr, DpSrc, 0x00000300);
|
|
iow32(scr, ClrCmpCntl, 0x00000000);
|
|
|
|
waitforfifo(scr, 8);
|
|
iow32(scr, SrcOffPitch, scr->pitch<<22);
|
|
iow32(scr, SrcCntl, 0x00000000);
|
|
iow32(scr, SrcYX, (sp.x<<16)|sp.y);
|
|
iow32(scr, SrcWidth1, dx);
|
|
iow32(scr, DstOffPitch, scr->pitch<<22);
|
|
iow32(scr, DstCntl, ctl);
|
|
|
|
iow32(scr, DstYX, (dp.x<<16)|dp.y);
|
|
iow32(scr, DstHeightWidth, (dx<<16)|dy);
|
|
|
|
waitforidle(scr);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* This should work, but doesn't.
|
|
* It messes up the screen timings for some reason.
|
|
*/
|
|
static void
|
|
mach64blank(VGAscr *scr, int blank)
|
|
{
|
|
ulong ctl;
|
|
|
|
ctl = ior32(scr, CrtcGenCtl) & ~(CrtcHsyncDis|CrtcVsyncDis);
|
|
if(blank)
|
|
ctl |= CrtcHsyncDis|CrtcVsyncDis;
|
|
iow32(scr, CrtcGenCtl, ctl);
|
|
}
|
|
|
|
/*
|
|
* We squirrel away whether the LCD and/or CRT were
|
|
* on when we were called to blank the screen, and
|
|
* restore the old state. If we are called to blank the
|
|
* screen when it is already blank, we don't update the state.
|
|
* Such a call sequence should not happen, though.
|
|
*
|
|
* We could try forcing the chip into power management
|
|
* mode instead, but I'm not sure how that would interact
|
|
* with screen updates going on while the screen is blanked.
|
|
*/
|
|
static void
|
|
mach64lcdblank(VGAscr *scr, int blank)
|
|
{
|
|
static int crtlcd;
|
|
ulong x;
|
|
|
|
if(blank) {
|
|
x = lcdr32(scr, LCD_GenCtrl);
|
|
if(x & 3) {
|
|
crtlcd = x & 3;
|
|
lcdw32(scr, LCD_GenCtrl, x&~3);
|
|
}
|
|
} else {
|
|
if(crtlcd == 0)
|
|
crtlcd = 2; /* lcd only */
|
|
x = lcdr32(scr, LCD_GenCtrl);
|
|
lcdw32(scr, LCD_GenCtrl, x | crtlcd);
|
|
}
|
|
}
|
|
|
|
static void
|
|
mach64xxdrawinit(VGAscr *scr)
|
|
{
|
|
if(scr->io > 0x2FF){
|
|
initengine(scr);
|
|
scr->fill = mach64hwfill;
|
|
scr->scroll = mach64hwscroll;
|
|
}
|
|
/* scr->blank = mach64blank; */
|
|
switch(scr->id){
|
|
default:
|
|
break;
|
|
case ('L'<<8)|'B': /* 4C42: Rage 3D LTPro */
|
|
case ('L'<<8)|'I': /* 4C49: Rage 3D LTPro */
|
|
case ('L'<<8)|'M': /* 4C4D: Rage Mobility */
|
|
case ('L'<<8)|'P': /* 4C50: Rage 3D LTPro */
|
|
scr->blank = mach64lcdblank;
|
|
break;
|
|
}
|
|
}
|
|
|
|
VGAdev vgamach64xxdev = {
|
|
"mach64xx",
|
|
|
|
mach64xxenable, /* enable */
|
|
0, /* disable */
|
|
0, /* page */
|
|
mach64xxlinear, /* linear */
|
|
mach64xxdrawinit, /* drawinit */
|
|
};
|
|
|
|
VGAcur vgamach64xxcur = {
|
|
"mach64xxhwgc",
|
|
|
|
mach64xxcurenable, /* enable */
|
|
mach64xxcurdisable, /* disable */
|
|
mach64xxcurload, /* load */
|
|
mach64xxcurmove, /* move */
|
|
};
|
|
|