plan 9 kernel history: overview | file list | diff list

2000/0612/pc/vgas3.c (diff list | history)

2000/0612/sys/src/9/pc/vgas3.c:1,5672000/0701/sys/src/9/pc/vgas3.c:1,569 (short | long | prev | next)
1994/0803    
#include "u.h" 
#include "../port/lib.h" 
#include "mem.h" 
#include "dat.h" 
#include "fns.h" 
1997/1101    
#include "io.h" 
1994/0803    
#include "../port/error.h" 
 
1997/1101    
#define	Image	IMAGE 
#include <draw.h> 
#include <memdraw.h> 
1999/0119    
#include <cursor.h> 
1994/0803    
#include "screen.h" 
 
1997/1101    
static int 
s3pageset(VGAscr* scr, int page) 
1994/0803    
{ 
1997/1101    
	uchar crt35, crt51; 
	int opage; 
1994/0803    
 
1997/1101    
	crt35 = vgaxi(Crtx, 0x35); 
1999/1005    
	if(scr->gscreen->depth >= 8){ 
1994/0803    
		/* 
		 * The S3 registers need to be unlocked for this. 
		 * Let's hope they are already: 
		 *	vgaxo(Crtx, 0x38, 0x48); 
		 *	vgaxo(Crtx, 0x39, 0xA0); 
		 * 
		 * The page is 6 bits, the lower 4 bits in Crt35<3:0>, 
		 * the upper 2 in Crt51<3:2>. 
		 */ 
		vgaxo(Crtx, 0x35, page & 0x0F); 
1997/1101    
		crt51 = vgaxi(Crtx, 0x51); 
		vgaxo(Crtx, 0x51, (crt51 & ~0x0C)|((page & 0x30)>>2)); 
		opage = ((crt51 & 0x0C)<<2)|(crt35 & 0x0F); 
1994/0803    
	} 
1997/1101    
	else{ 
1994/0803    
		vgaxo(Crtx, 0x35, (page<<2) & 0x0C); 
1997/1101    
		opage = (crt35>>2) & 0x03; 
	} 
 
	return opage; 
1994/0803    
} 
 
static void 
1997/1101    
s3page(VGAscr* scr, int page) 
1994/0803    
{ 
1998/0116    
	int id; 
 
	id = (vgaxi(Crtx, 0x30)<<8)|vgaxi(Crtx, 0x2E); 
	switch(id){ 
 
	case 0xE110:				/* ViRGE/GX2 */ 
		break; 
 
	default: 
		lock(&scr->devlock); 
		s3pageset(scr, page); 
		unlock(&scr->devlock); 
		break; 
	} 
1997/1101    
} 
 
static ulong 
s3linear(VGAscr* scr, int* size, int* align) 
{ 
2000/0612    
	char *mmioname; 
	ulong aperture, oaperture, mmiobase, mmiosize; 
	int i, id, j, osize, oapsize, wasupamem; 
1997/1101    
	Pcidev *p; 
1999/0716    
	Physseg seg; 
1997/1101    
 
1999/1005    
	osize = *size; 
1997/1101    
	oaperture = scr->aperture; 
	oapsize = scr->apsize; 
	wasupamem = scr->isupamem; 
 
2000/0612    
	mmiosize = 0; 
	mmiobase = 0; 
	mmioname = nil; 
1997/1101    
	if(p = pcimatch(nil, 0x5333, 0)){ 
2000/0611    
		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("vgas3: aperture not found\n"); 
			return 0; 
		} 
		aperture = p->mem[i].bar & ~0x0F; 
		*size = p->mem[i].size; 
2000/0612    
 
		id = (vgaxi(Crtx, 0x30)<<8)|vgaxi(Crtx, 0x2E); 
		if(id == 0xE122){		/* find Savage4 mmio */ 
			/* 
			 * We could assume that the MMIO registers 
			 * will be in the screen segment and just use 
			 * that, but PCI software is allowed to move them 
			 * if it feels like it, so we look for an aperture of 
			 * the right size; only the first 512k actually means 
			 * anything.  The S3 engineers overestimated how 
			 * much space they would need in the first design. 
			 */ 
			for(j=0; j<nelem(p->mem); j++){ 
				if(i == j) 
					continue; 
				if(p->mem[j].size==512*1024 || p->mem[j].size==16*1024*1024){ 
					mmiobase = p->mem[j].bar & ~0x0F; 
					mmiosize = 512*1024; 
					mmioname = "savage4mmio"; 
					break; 
				} 
			} 
			if(mmiosize == 0){ 
				print("savage4: mmio not found\n"); 
				return 0; 
			} 
		} 
2000/0611    
	}else 
1997/1101    
		aperture = 0; 
 
2000/0611    
	if(wasupamem) 
		upafree(oaperture, oapsize); 
	scr->isupamem = 0; 
 
1997/1101    
	aperture = upamalloc(aperture, *size, *align); 
	if(aperture == 0){ 
		if(wasupamem && upamalloc(oaperture, oapsize, 0)) 
			scr->isupamem = 1; 
	} 
	else 
		scr->isupamem = 1; 
 
1999/1005    
	if(oaperture) 
		print("warning (BUG): redefinition of aperture does not change s3screen segment\n"); 
1999/0716    
	memset(&seg, 0, sizeof(seg)); 
	seg.attr = SG_PHYSICAL; 
	seg.name = smalloc(NAMELEN); 
	snprint(seg.name, NAMELEN, "s3screen"); 
	seg.pa = aperture; 
	seg.size = osize; 
	addphysseg(&seg); 
1999/1005    
 
2000/0612    
	if(mmiosize){ 
		memset(&seg, 0, sizeof(seg)); 
		seg.attr = SG_PHYSICAL; 
		seg.name = smalloc(NAMELEN); 
		snprint(seg.name, NAMELEN, mmioname); 
		seg.pa = mmiobase; 
		seg.size = mmiosize; 
		addphysseg(&seg); 
	} 
1997/1101    
	return aperture; 
} 
 
static void 
s3vsyncactive(void) 
{ 
1994/0803    
	/* 
	 * Hardware cursor information is fetched from display memory 
	 * during the horizontal blank active time. The 80x chips may hang 
	 * if the cursor is turned on or off during this period. 
	 */ 
	while((vgai(Status1) & 0x08) == 0) 
		; 
} 
 
static void 
1997/1101    
s3disable(VGAscr*) 
1994/0803    
{ 
	uchar crt45; 
 
	/* 
	 * Turn cursor off. 
	 */ 
	crt45 = vgaxi(Crtx, 0x45) & 0xFE; 
1997/1101    
	s3vsyncactive(); 
1994/0803    
	vgaxo(Crtx, 0x45, crt45); 
} 
 
static void 
1997/1101    
s3enable(VGAscr* scr) 
1997/0327    
{ 
1999/1005    
	int i; 
1997/1101    
	ulong storage; 
1994/0803    
 
1997/1101    
	s3disable(scr); 
1994/0803    
 
	/* 
	 * Cursor colours. Set both the CR0[EF] and the colour 
	 * stack in case we are using a 16-bit RAMDAC. 
	 */ 
1995/0126    
	vgaxo(Crtx, 0x0E, Pwhite); 
	vgaxo(Crtx, 0x0F, Pblack); 
1994/0803    
	vgaxi(Crtx, 0x45); 
 
1999/1005    
	for(i = 0; i < 3; i++) 
		vgaxo(Crtx, 0x4A, Pblack); 
	vgaxi(Crtx, 0x45); 
	for(i = 0; i < 3; i++) 
		vgaxo(Crtx, 0x4B, Pwhite); 
1998/0116    
 
1994/0803    
	/* 
	 * Find a place for the cursor data in display memory. 
	 * Must be on a 1024-byte boundary. 
	 */ 
1997/1101    
	storage = (scr->gscreen->width*BY2WD*scr->gscreen->r.max.y+1023)/1024; 
2000/0612    
	vgaxo(Crtx, 0x4C, storage>>8); 
1994/0803    
	vgaxo(Crtx, 0x4D, storage & 0xFF); 
	storage *= 1024; 
1997/1101    
	scr->storage = storage; 
1994/0803    
 
	/* 
1998/0116    
	 * Enable the cursor in Microsoft Windows format. 
1994/0803    
	 */ 
1998/0116    
	vgaxo(Crtx, 0x55, vgaxi(Crtx, 0x55) & ~0x10); 
1997/1101    
	s3vsyncactive(); 
1994/0803    
	vgaxo(Crtx, 0x45, 0x01); 
} 
 
static void 
1997/1101    
s3load(VGAscr* scr, Cursor* curs) 
1994/0803    
{ 
1994/0804    
	uchar *p; 
1998/0116    
	int id, opage, x, y; 
1994/0803    
 
	/* 
1997/1101    
	 * Disable the cursor and 
	 * set the pointer to the two planes. 
1994/0809    
	 */ 
1997/1101    
	s3disable(scr); 
1994/0809    
 
1998/0116    
	opage = 0; 
1997/1101    
	p = KADDR(scr->aperture); 
1998/0116    
	id = (vgaxi(Crtx, 0x30)<<8)|vgaxi(Crtx, 0x2E); 
	switch(id){ 
1994/0803    
 
1999/0821    
	case 0xE131:				/* ViRGE */ 
1999/1005    
	case 0xE18A:				/* ViRGE/[DG]X */ 
	case 0xE110:				/* ViRGE/GX2 */ 
	case 0xE13D:				/* ViRGE/VX */ 
2000/0701    
	case 0xE112:				/* Savage4/MX */ 
2000/0611    
	case 0xE122:				/* Savage4 */ 
1998/0116    
		p += scr->storage; 
		break; 
 
	default: 
		lock(&scr->devlock); 
		opage = s3pageset(scr, scr->storage>>16); 
		p += (scr->storage & 0xFFFF); 
		break; 
	} 
 
1994/0803    
	/* 
1998/0116    
	 * The cursor is set in Microsoft Windows format (the ViRGE/GX2 no 
	 * longer supports the X11 format) which gives the following truth table: 
1994/0803    
	 *	and xor	colour 
1998/0116    
	 *	 0   0	background colour 
	 *	 0   1	foreground colour 
	 *	 1   0	current screen pixel 
	 *	 1   1	NOT current screen pixel 
1994/0803    
	 * Put the cursor into the top-left of the 64x64 array. 
1994/0809    
	 * 
	 * The cursor pattern in memory is interleaved words of 
	 * AND and XOR patterns. 
1994/0803    
	 */ 
	for(y = 0; y < 64; y++){ 
1994/0804    
		for(x = 0; x < 64/8; x += 2){ 
			if(x < 16/8 && y < 16){ 
1998/0116    
				*p++ = ~(curs->clr[2*y + x]|curs->set[2*y + x]); 
				*p++ = ~(curs->clr[2*y + x+1]|curs->set[2*y + x+1]); 
1997/1101    
				*p++ = curs->set[2*y + x]; 
				*p++ = curs->set[2*y + x+1]; 
1994/0803    
			} 
			else { 
1998/0116    
				*p++ = 0xFF; 
				*p++ = 0xFF; 
1994/0804    
				*p++ = 0x00; 
				*p++ = 0x00; 
1994/0803    
			} 
		} 
	} 
 
1998/0116    
	switch(id){ 
 
1999/0821    
	case 0xE131:				/* ViRGE */ 
1999/1005    
	case 0xE18A:				/* ViRGE/[DG]X */ 
	case 0xE110:				/* ViRGE/GX2 */ 
	case 0xE13D:				/* ViRGE/VX */ 
2000/0701    
	case 0xE112:				/* Savage4/MX */ 
2000/0611    
	case 0xE122:				/* Savage4 */ 
1998/0116    
		break; 
 
	default: 
		s3pageset(scr, opage); 
		unlock(&scr->devlock); 
		break; 
	} 
1997/1101    
 
1994/0803    
	/* 
1997/1101    
	 * Save the cursor hotpoint and enable the cursor. 
1994/0803    
	 */ 
1997/1101    
	scr->offset = curs->offset; 
	s3vsyncactive(); 
1994/0803    
	vgaxo(Crtx, 0x45, 0x01); 
} 
 
static int 
1997/1101    
s3move(VGAscr* scr, Point p) 
1994/0803    
{ 
1994/0804    
	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 offset instead. 
1994/0809    
	 * There seems to be a bug in that if the offset is 1, the 
	 * cursor doesn't disappear off the left edge properly, so 
	 * round it up to be even. 
1994/0804    
	 */ 
1997/1101    
	if((x = p.x+scr->offset.x) < 0){ 
1994/0804    
		xo = -x; 
		xo = ((xo+1)/2)*2; 
		x = 0; 
	} 
	else 
		xo = 0; 
1997/1101    
	if((y = p.y+scr->offset.y) < 0){ 
1994/0804    
		yo = -y; 
		y = 0; 
	} 
	else 
		yo = 0; 
 
	vgaxo(Crtx, 0x46, (x>>8) & 0x07); 
	vgaxo(Crtx, 0x47, x & 0xFF); 
	vgaxo(Crtx, 0x49, y & 0xFF); 
	vgaxo(Crtx, 0x4E, xo); 
	vgaxo(Crtx, 0x4F, yo); 
	vgaxo(Crtx, 0x48, (y>>8) & 0x07); 
1994/0803    
 
	return 0; 
} 
 
1999/1005    
/* 
 * The manual gives byte offsets, but we want ulong offsets, hence /4. 
 */ 
enum { 
	SrcBase = 0xA4D4/4, 
	DstBase = 0xA4D8/4, 
	Stride = 0xA4E4/4, 
	FgrdData = 0xA4F4/4, 
	WidthHeight = 0xA504/4, 
	SrcXY = 0xA508/4, 
	DestXY = 0xA50C/4, 
	Command = 0xA500/4, 
	SubStat = 0x8504/4, 
	FifoStat = 0x850C/4, 
}; 
 
/* 
 * Wait for writes to VGA memory via linear aperture to flush. 
 */ 
enum {Maxloop = 1<<24}; 
struct { 
	ulong linear; 
	ulong fifo; 
	ulong idle; 
2000/0612    
	ulong lineartimeout; 
	ulong fifotimeout; 
	ulong idletimeout; 
1999/1005    
} waitcount; 
 
static void 
waitforlinearfifo(VGAscr *scr) 
{ 
	ulong *mmio; 
	long x; 
	static ulong nwaitforlinearfifo; 
	ulong mask, val; 
 
	switch(scr->id){ 
	default: 
		panic("unknown scr->id in s3 waitforlinearfifo"); 
	case 0xE131:	/* ViRGE */ 
	case 0xE13D:	/* ViRGE/VX */ 
		mask = 0x0F<<6; 
		val = 0x08<<6; 
		break; 
	case 0xE110:	/* ViRGE/GX2 */ 
		mask = 0x1F<<6; 
		val = 0x10<<6; 
		break; 
	} 
	mmio = scr->mmio; 
	x = 0; 
	while((mmio[FifoStat]&mask) != val && x++ < Maxloop) 
		waitcount.linear++; 
2000/0612    
	if(x >= Maxloop) 
		waitcount.lineartimeout++; 
1999/1005    
} 
 
static void 
waitforfifo(VGAscr *scr, int entries) 
{ 
	ulong *mmio; 
	long x; 
	static ulong nwaitforfifo; 
 
	mmio = scr->mmio; 
	x = 0; 
	while((mmio[SubStat]&0x1F00) < ((entries+2)<<8) && x++ < Maxloop) 
		waitcount.fifo++; 
2000/0612    
	if(x >= Maxloop) 
		waitcount.fifotimeout++; 
1999/1005    
} 
 
static void 
waitforidle(VGAscr *scr) 
{ 
	ulong *mmio; 
	long x; 
 
	mmio = scr->mmio; 
	x = 0; 
	while((mmio[SubStat]&0x3F00) != 0x3000 && x++ < Maxloop) 
		waitcount.idle++; 
2000/0612    
	if(x >= Maxloop) 
		waitcount.idletimeout++; 
1999/1005    
} 
 
static int 
hwscroll(VGAscr *scr, Rectangle r, Rectangle sr) 
{ 
	enum { Bitbltop = 0xCC };	/* copy source */ 
	ulong *mmio; 
	ulong cmd, stride; 
	Point dp, sp; 
	int did, d; 
 
	d = scr->gscreen->depth; 
	did = (d-8)/8; 
	cmd = 0x00000020|(Bitbltop<<17)|(did<<2); 
	stride = Dx(scr->gscreen->r)*d/8; 
 
	if(r.min.x <= sr.min.x){ 
		cmd |= 1<<25; 
		dp.x = r.min.x; 
		sp.x = sr.min.x; 
	}else{ 
		dp.x = r.max.x-1; 
		sp.x = sr.max.x-1; 
	} 
 
	if(r.min.y <= sr.min.y){ 
		cmd |= 1<<26; 
		dp.y = r.min.y; 
		sp.y = sr.min.y; 
	}else{ 
		dp.y = r.max.y-1; 
		sp.y = sr.max.y-1; 
	} 
 
	mmio = scr->mmio; 
	waitforlinearfifo(scr); 
	waitforfifo(scr, 7); 
	mmio[SrcBase] = scr->aperture; 
	mmio[DstBase] = scr->aperture; 
	mmio[Stride] = (stride<<16)|stride; 
	mmio[WidthHeight] = ((Dx(r)-1)<<16)|Dy(r); 
	mmio[SrcXY] = (sp.x<<16)|sp.y; 
	mmio[DestXY] = (dp.x<<16)|dp.y; 
	mmio[Command] = cmd; 
	waitforidle(scr); 
	return 1; 
} 
 
static int 
hwfill(VGAscr *scr, Rectangle r, ulong sval) 
{ 
	enum { Bitbltop = 0xCC };	/* copy source */ 
	ulong *mmio; 
	ulong cmd, stride; 
	int did, d; 
 
	d = scr->gscreen->depth; 
	did = (d-8)/8; 
	cmd = 0x16000120|(Bitbltop<<17)|(did<<2); 
	stride = Dx(scr->gscreen->r)*d/8; 
	mmio = scr->mmio; 
	waitforlinearfifo(scr); 
	waitforfifo(scr, 8); 
	mmio[SrcBase] = scr->aperture; 
	mmio[DstBase] = scr->aperture; 
	mmio[DstBase] = scr->aperture; 
	mmio[Stride] = (stride<<16)|stride; 
	mmio[FgrdData] = sval; 
	mmio[WidthHeight] = ((Dx(r)-1)<<16)|Dy(r); 
	mmio[DestXY] = (r.min.x<<16)|r.min.y; 
	mmio[Command] = cmd; 
	waitforidle(scr); 
	return 1; 
} 
 
2000/0326    
enum { 
	CursorSyncCtl = 0x0D,	/* in Seqx */ 
	VsyncHi = 0x80, 
	VsyncLo = 0x40, 
	HsyncHi = 0x20, 
	HsyncLo = 0x10, 
}; 
 
1999/1005    
static void 
2000/0326    
s3blank(int blank) 
{ 
	uchar x; 
 
	x = vgaxi(Seqx, CursorSyncCtl); 
	x &= ~0xF0; 
	if(blank) 
		x |= VsyncLo | HsyncLo; 
	vgaxo(Seqx, CursorSyncCtl, x); 
} 
 
static void 
1999/1005    
s3drawinit(VGAscr *scr) 
{ 
	ulong id; 
 
	id = (vgaxi(Crtx, 0x30)<<8)|vgaxi(Crtx, 0x2E); 
	scr->id = id; 
 
	/* 
	 * It's highly likely that other ViRGEs will work without 
	 * change to the driver, with the exception of the size of 
	 * the linear aperture memory write FIFO.  Since we don't 
	 * know that size, I'm not turning them on.  See waitforlinearfifo 
	 * above. 
	 */ 
	switch(id){ 
	case 0xE131:				/* ViRGE */ 
	case 0xE13D:				/* ViRGE/VX */ 
	case 0xE110:				/* ViRGE/GX2 */ 
		scr->mmio = (ulong*)(scr->aperture+0x1000000); 
		scr->fill = hwfill; 
		scr->scroll = hwscroll; 
2000/0326    
		/* scr->blank = hwblank; */ 
1999/1005    
	} 
} 
 
1997/1101    
VGAdev vgas3dev = { 
	"s3", 
1995/0126    
 
	0, 
1997/1101    
	0, 
	s3page, 
	s3linear, 
1999/1005    
	s3drawinit, 
1994/0803    
}; 
 
1997/1101    
VGAcur vgas3cur = { 
	"s3hwgc", 
1995/0126    
 
1997/1101    
	s3enable, 
	s3disable, 
	s3load, 
	s3move, 
1995/0126    
}; 


source code copyright © 1990-2005 Lucent Technologies; see license
Plan 9 distribution
comments to russ cox (rsc@swtch.com)