diff options
Diffstat (limited to 'os/pc/vgamach64xx.c')
| -rw-r--r-- | os/pc/vgamach64xx.c | 1250 |
1 files changed, 1250 insertions, 0 deletions
diff --git a/os/pc/vgamach64xx.c b/os/pc/vgamach64xx.c new file mode 100644 index 00000000..322c449c --- /dev/null +++ b/os/pc/vgamach64xx.c @@ -0,0 +1,1250 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.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 + +static Chan *ovl_chan; /* Channel of controlling process */ +static int ovl_width; /* Width of input overlay buffer */ +static int ovl_height; /* Height of input overlay buffer */ +static int ovl_format; /* Overlay format */ +static ulong ovl_fib; /* Frame in bytes */ + +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 ulong mach64overlay; /* Overlay buffer */ + +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 Pcidev* +mach64xxpci(void) +{ + Pcidev *p; + int i; + + if((p = pcimatch(nil, 0x1002, 0)) == nil) + return nil; + + for (i = 0; i != nelem(mach64s); i++) + if (mach64s[i].m64_id == p->did) { + mach64type = &mach64s[i]; + return p; + } + return nil; +} + +static void +mach64xxenable(VGAscr* scr) +{ + Pcidev *p; + + /* + * Only once, can't be disabled for now. + */ + if(scr->io) + return; + if(p = mach64xxpci()){ + scr->id = p->did; + + /* + * 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 ulong +mach64xxlinear(VGAscr* scr, int* size, int* align) +{ + ulong aperture, osize, oaperture; + int i, oapsize, wasupamem; + Pcidev *p; + + osize = *size; + oaperture = scr->aperture; + oapsize = scr->apsize; + wasupamem = scr->isupamem; + + if(p = mach64xxpci()){ + for(i=0; i<nelem(p->mem); i++){ + if(p->mem[i].size >= *size + && ((p->mem[i].bar & ~0x0F) & (*align-1)) == 0) + break; + } + if(i >= nelem(p->mem)){ + print("vgamach64xx: aperture not found\n"); + return 0; + } + aperture = p->mem[i].bar & ~0x0F; + *size = p->mem[i].size; + } + else + aperture = 0; + + if(wasupamem) + upafree(oaperture, oapsize); + scr->isupamem = 0; + + aperture = upamalloc(aperture, *size, *align); + if(aperture == 0){ + if(wasupamem && upamalloc(oaperture, oapsize, 0)) + scr->isupamem = 1; + } + else + scr->isupamem = 1; + + scr->mmio = KADDR(aperture+osize-0x400); + if(oaperture && oaperture != aperture) + print("warning (BUG): redefinition of aperture does not change mach64mmio segment\n"); + addvgaseg("mach64mmio", aperture+osize-BY2PG, BY2PG); + addvgaseg("mach64screen", aperture, osize); + + return aperture; +} + +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 = KADDR(scr->aperture); + 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; +} + +/* + * If necessary, translate the rectangle physr + * some multiple of [dx dy] so that it includes p. + * Return 1 if the rectangle changed. + */ +static int +screenpan(Point p, Rectangle *physr, int dx, int dy) +{ + int d; + + if(ptalmostinrect(p, *physr)) + return 0; + + if(p.y < physr->min.y){ + d = physr->min.y - (p.y&~(dy-1)); + physr->min.y -= d; + physr->max.y -= d; + } + if(p.y > physr->max.y){ + d = ((p.y+dy-1)&~(dy-1)) - physr->max.y; + physr->min.y += d; + physr->max.y += d; + } + + if(p.x < physr->min.x){ + d = physr->min.x - (p.x&~(dx-1)); + physr->min.x -= d; + physr->max.x -= d; + } + if(p.x > physr->max.x){ + d = ((p.x+dx-1)&~(dx-1)) - physr->max.x; + physr->min.x += d; + physr->max.x += d; + } + return 1; +} + +static int +mach64xxcurmove(VGAscr* scr, Point p) +{ + int x, xo, y, yo; + int dx; + ulong off, pitch; + + /* + * If the point we want to display is outside the current + * screen rectangle, pan the screen to display it. + * + * We have to move in 64-bit chunks. + */ + if(scr->gscreen->depth == 24) + dx = (64*3)/24; + else + dx = 64 / scr->gscreen->depth; + + if(panning && screenpan(p, &physgscreenr, dx, 1)){ + off = (physgscreenr.min.y*Dx(scr->gscreen->r)+physgscreenr.min.x)/dx; + pitch = Dx(scr->gscreen->r)/8; + iow32(scr, CrtcOffPitch, (pitch<<22)|off); + } + + p.x -= physgscreenr.min.x; + p.y -= physgscreenr.min.y; + + /* + * 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->gscreen->width*BY2WD*scr->gscreen->r.max.y+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, &arrow); + 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 %luX\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 %luX\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 +init_overlayclock(VGAscr *scr) +{ + uchar *cc, save, pll_ref_div, pll_vclk_cntl, vclk_post_div, + vclk_fb_div, ecp_div; + int i; + ulong dotclock; + + /* Taken from GLX */ + /* Get monitor dotclock, check for Overlay Scaler clock limit */ + cc = (uchar *)&scr->mmio[mmoffset[ClockCntl]]; + save = cc[1]; i = cc[0] & 3; + cc[1] = 2<<2; pll_ref_div = cc[2]; + cc[1] = 5<<2; pll_vclk_cntl = cc[2]; + cc[1] = 6<<2; vclk_post_div = (cc[2]>>(i+i)) & 3; + cc[1] = (7+i)<<2; vclk_fb_div = cc[2]; + + dotclock = 2 * mach64refclock * vclk_fb_div / + (pll_ref_div * (1 << vclk_post_div)); + /* ecp_div: 0=dotclock, 1=dotclock/2, 2=dotclock/4 */ + ecp_div = dotclock / mach64type->m64_ovlclock; + if (ecp_div>2) ecp_div = 2; + + /* Force a scaler clock factor of 1 if refclock * + * is unknown (VCLK_SRC not PLLVCLK) */ + if ((pll_vclk_cntl & 0x03) != 0x03) + ecp_div = 0; + if ((pll_vclk_cntl & 0x30) != ecp_div<<4) { + cc[1] = (5<<2)|2; + cc[2] = (pll_vclk_cntl&0xCF) | (ecp_div<<4); + } + + /* Restore PLL Register Index */ + cc[1] = save; +} + +static void +initengine(VGAscr *scr) +{ + ulong pitch; + uchar *bios; + ushort table; + + pitch = Dx(scr->gscreen->r)/8; + if(scr->gscreen->depth == 24) + pitch *= 3; + + resetengine(scr); + waitforfifo(scr, 14); + iow32(scr, ContextMask, ~0); + iow32(scr, DstOffPitch, 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, 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 pitch; + ulong ctl; + +if(drawdebug) + iprint("hwfill %R val %lux...\n", r, sval); + + /* shouldn't happen */ + if(scr->io == 0x2EC || scr->io == 0x1C8 || scr->io == 0) + return 0; + + pitch = Dx(scr->gscreen->r)/8; + ctl = 1|2; /* left-to-right, top-to-bottom */ + if(scr->gscreen->depth == 24){ + r.min.x *= 3; + r.max.x *= 3; + pitch *= 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, 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) +{ + ulong pitch; + Point dp, sp; + ulong ctl; + int dx, dy; + + dx = Dx(r); + dy = Dy(r); + pitch = Dx(scr->gscreen->r)/8; + if(scr->gscreen->depth == 24){ + dx *= 3; + pitch *= 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, pitch<<22); + iow32(scr, SrcCntl, 0x00000000); + iow32(scr, SrcYX, (sp.x<<16)|sp.y); + iow32(scr, SrcWidth1, dx); + iow32(scr, DstOffPitch, 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; + hwblank = 1; + break; + } +} + +static void +ovl_configure(VGAscr *scr, Chan *c, char **field) +{ + int w, h; + char *format; + + w = (int)strtol(field[1], nil, 0); + h = (int)strtol(field[2], nil, 0); + format = field[3]; + + if (c != ovl_chan) + error(Einuse); + if (strcmp(format, "YUYV")) + error(Eunsupportedformat); + + ovl_width = w; + ovl_height = h; + ovl_fib = w * h * sizeof(ushort); + + waitforidle(scr); + scr->mmio[mmoffset[BusCntl]] |= 0x08000000; /* Enable regblock 1 */ + scr->mmio[mmoffset[OverlayScaleCntl]] = + SCALE_ZERO_EXTEND|SCALE_RED_TEMP_6500K| + SCALE_HORZ_BLEND|SCALE_VERT_BLEND; + scr->mmio[mmoffset[!mach64revb? Buf0Pitch: ScalerBuf0Pitch]] = w; + scr->mmio[mmoffset[CaptureConfig]] = + SCALER_FRAME_READ_MODE_FULL| + SCALER_BUF_MODE_SINGLE| + SCALER_BUF_NEXT_0; + scr->mmio[mmoffset[OverlayKeyCntl]] = !mach64revb? + OVERLAY_MIX_ALWAYS_V|(OVERLAY_EXCLUSIVE_NORMAL << 28): + 0x011; + + if (mach64type->m64_pro) { + waitforfifo(scr, 6); + + /* set the scaler co-efficient registers */ + scr->mmio[mmoffset[ScalerColourCntl]] = + (0x00) | (0x10 << 8) | (0x10 << 16); + scr->mmio[mmoffset[ScalerHCoef0]] = + (0x00) | (0x20 << 8); + scr->mmio[mmoffset[ScalerHCoef1]] = + (0x0D) | (0x20 << 8) | (0x06 << 16) | (0x0D << 24); + scr->mmio[mmoffset[ScalerHCoef2]] = + (0x0D) | (0x1C << 8) | (0x0A << 16) | (0x0D << 24); + scr->mmio[mmoffset[ScalerHCoef3]] = + (0x0C) | (0x1A << 8) | (0x0E << 16) | (0x0C << 24); + scr->mmio[mmoffset[ScalerHCoef4]] = + (0x0C) | (0x14 << 8) | (0x14 << 16) | (0x0C << 24); + } + + waitforfifo(scr, 3); + scr->mmio[mmoffset[VideoFormat]] = SCALE_IN_YVYU422 | + (!mach64revb? 0xC: 0); + + if (mach64overlay == 0) + mach64overlay = scr->storage + 64 * 64 * sizeof(uchar); + scr->mmio[mmoffset[!mach64revb? Buf0Offset: ScalerBuf0Offset]] = + mach64overlay; +} + +static void +ovl_enable(VGAscr *scr, Chan *c, char **field) +{ + int x, y, w, h; + long h_inc, v_inc; + + x = (int)strtol(field[1], nil, 0); + y = (int)strtol(field[2], nil, 0); + w = (int)strtol(field[3], nil, 0); + h = (int)strtol(field[4], nil, 0); + + if (x < 0 || x + w > physgscreenr.max.x || + y < 0 || y + h > physgscreenr.max.y) + error(Ebadarg); + + if (c != ovl_chan) + error(Einuse); + if (scr->mmio[mmoffset[CrtcGenCntl]] & 1) { /* double scan enable */ + y *= 2; + h *= 2; + } + + waitforfifo(scr, 2); + scr->mmio[mmoffset[OverlayYX]] = + ((x & 0xFFFF) << 16) | (y & 0xFFFF); + scr->mmio[mmoffset[OverlayYXEnd]] = + (((x + w) & 0xFFFF) << 16) | ((y + h) & 0xFFFF); + + h_inc = (ovl_width << 12) / (w >> 1); /* ??? */ + v_inc = (ovl_height << 12) / h; + waitforfifo(scr, 2); + scr->mmio[mmoffset[OverlayScaleInc]] = + ((h_inc & 0xFFFF) << 16) | (v_inc & 0xFFFF); + scr->mmio[mmoffset[ScalerHeightWidth]] = + ((ovl_width & 0xFFFF) << 16) | (ovl_height & 0xFFFF); + waitforidle(scr); + scr->mmio[mmoffset[OverlayScaleCntl]] |= + (SCALE_ENABLE|OVERLAY_ENABLE); +} + +static void +ovl_status(VGAscr *scr, Chan *, char **field) +{ + pprint("%s: %s %.4uX, VT/GT %s, PRO %s, ovlclock %d, rev B %s, refclock %ld\n", + scr->dev->name, field[0], mach64type->m64_id, + mach64type->m64_vtgt? "yes": "no", + mach64type->m64_pro? "yes": "no", + mach64type->m64_ovlclock, + mach64revb? "yes": "no", + mach64refclock); + pprint("%s: storage @%.8luX, aperture @%8.ulX, ovl buf @%.8ulX\n", + scr->dev->name, scr->storage, scr->aperture, + mach64overlay); +} + +static void +ovl_openctl(VGAscr *, Chan *c, char **) +{ + if (ovl_chan) + error(Einuse); + ovl_chan = c; +} + +static void +ovl_closectl(VGAscr *scr, Chan *c, char **) +{ + if (c != ovl_chan) return; + + waitforidle(scr); + scr->mmio[mmoffset[OverlayScaleCntl]] &= + ~(SCALE_ENABLE|OVERLAY_ENABLE); + ovl_chan = nil; + ovl_width = ovl_height = ovl_fib = 0; +} + +enum +{ + CMclosectl, + CMconfigure, + CMenable, + CMopenctl, + CMstatus, +}; + +static void (*ovl_cmds[])(VGAscr *, Chan *, char **) = +{ + [CMclosectl] ovl_closectl, + [CMconfigure] ovl_configure, + [CMenable] ovl_enable, + [CMopenctl] ovl_openctl, + [CMstatus] ovl_status, +}; + +static Cmdtab mach64xxcmd[] = +{ + CMclosectl, "closectl", 1, + CMconfigure, "configure", 4, + CMenable, "enable", 5, + CMopenctl, "openctl", 1, + CMstatus, "status", 1, +}; + +static void +mach64xxovlctl(VGAscr *scr, Chan *c, void *a, int n) +{ + Cmdbuf *cb; + Cmdtab *ct; + + if(!mach64type->m64_vtgt) + error(Enodev); + + if(!scr->overlayinit){ + scr->overlayinit = 1; + init_overlayclock(scr); + } + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + + ct = lookupcmd(cb, mach64xxcmd, nelem(mach64xxcmd)); + + ovl_cmds[ct->index](scr, c, cb->f); + + poperror(); + free(cb); +} + +static int +mach64xxovlwrite(VGAscr *scr, void *a, int len, vlong offs) +{ + uchar *src; + int _len; + + if (ovl_chan == nil) return len; /* Acts as a /dev/null */ + + /* Calculate the destination address */ + _len = len; + src = (uchar *)a; + while (len > 0) { + ulong _offs; + int nb; + + _offs = (ulong)(offs % ovl_fib); + nb = (_offs + len > ovl_fib)? ovl_fib - _offs: len; + memmove((uchar *)KADDR(scr->aperture + mach64overlay + _offs), + src, nb); + offs += nb; + src += nb; + len -= nb; + } + return _len; +} + +VGAdev vgamach64xxdev = { + "mach64xx", + + mach64xxenable, /* enable */ + 0, /* disable */ + 0, /* page */ + mach64xxlinear, /* linear */ + mach64xxdrawinit, /* drawinit */ + 0, + mach64xxovlctl, /* overlay control */ + mach64xxovlwrite, /* write the overlay */ +}; + +VGAcur vgamach64xxcur = { + "mach64xxhwgc", + + mach64xxcurenable, /* enable */ + mach64xxcurdisable, /* disable */ + mach64xxcurload, /* load */ + mach64xxcurmove, /* move */ + + 1 /* doespanning */ +}; + |
