xref: /plan9-contrib/sys/src/9/pc/vgas3.c (revision 4de34a7edde43207e841ec91ecd12e6cf5f5ebe7)
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "io.h"
7 #include "../port/error.h"
8 
9 #define	Image	IMAGE
10 #include <draw.h>
11 #include <memdraw.h>
12 #include <cursor.h>
13 #include "screen.h"
14 
15 enum {
16 	PCIS3		= 0x5333,		/* PCI VID */
17 
18 	SAVAGE3D	= 0x8A20,		/* PCI DID */
19 	SAVAGE3DMV	= 0x8A21,
20 	SAVAGE4		= 0x8A22,
21 	PROSAVAGEP	= 0x8A25,
22 	PROSAVAGEK	= 0x8A26,
23 	PROSAVAGE8	= 0x8D04,
24 	SAVAGEMXMV	= 0x8C10,
25 	SAVAGEMX	= 0x8C11,
26 	SAVAGEIXMV	= 0x8C12,
27 	SAVAGEIX	= 0x8C13,
28 	SUPERSAVAGEIXC16 = 0x8C2E,
29 	SAVAGE2000	= 0x9102,
30 
31 	VIRGE		= 0x5631,
32 	VIRGEGX2	= 0x8A10,
33 	VIRGEDXGX	= 0x8A01,
34 	VIRGEVX		= 0x883D,
35 	VIRGEMX		= 0x8C01,
36 	VIRGEMXP	= 0x8C03,
37 
38 	VIRTUALPC2004	= 0x8810,
39 	AURORA64VPLUS	= 0x8812,
40 };
41 
42 static int
s3pageset(VGAscr * scr,int page)43 s3pageset(VGAscr* scr, int page)
44 {
45 	uchar crt35, crt51;
46 	int opage;
47 
48 	crt35 = vgaxi(Crtx, 0x35);
49 	if(scr->gscreen->depth >= 8){
50 		/*
51 		 * The S3 registers need to be unlocked for this.
52 		 * Let's hope they are already:
53 		 *	vgaxo(Crtx, 0x38, 0x48);
54 		 *	vgaxo(Crtx, 0x39, 0xA0);
55 		 *
56 		 * The page is 6 bits, the lower 4 bits in Crt35<3:0>,
57 		 * the upper 2 in Crt51<3:2>.
58 		 */
59 		vgaxo(Crtx, 0x35, page & 0x0F);
60 		crt51 = vgaxi(Crtx, 0x51);
61 		vgaxo(Crtx, 0x51, (crt51 & ~0x0C)|((page & 0x30)>>2));
62 		opage = ((crt51 & 0x0C)<<2)|(crt35 & 0x0F);
63 	}
64 	else{
65 		vgaxo(Crtx, 0x35, (page<<2) & 0x0C);
66 		opage = (crt35>>2) & 0x03;
67 	}
68 
69 	return opage;
70 }
71 
72 static void
s3page(VGAscr * scr,int page)73 s3page(VGAscr* scr, int page)
74 {
75 	int id;
76 
77 	id = (vgaxi(Crtx, 0x2D)<<8)|vgaxi(Crtx, 0x2E);
78 	switch(id){
79 
80 	case VIRGEGX2:
81 		break;
82 
83 	default:
84 		lock(&scr->devlock);
85 		s3pageset(scr, page);
86 		unlock(&scr->devlock);
87 		break;
88 	}
89 }
90 
91 static void
s3linear(VGAscr * scr,int,int)92 s3linear(VGAscr* scr, int, int)
93 {
94 	int id, j;
95 	ulong mmiobase, mmiosize;
96 	Pcidev *p;
97 
98 	vgalinearpciid(scr, PCIS3, 0);
99 	p = scr->pci;
100 	if(scr->paddr == 0 || p == nil)
101 		return;
102 
103 	addvgaseg("s3screen", scr->paddr, scr->apsize);
104 
105 	id = (vgaxi(Crtx, 0x2D)<<8)|vgaxi(Crtx, 0x2E);
106 	switch(id){			/* find mmio */
107 	case SAVAGE4:
108 	case PROSAVAGEP:
109 	case PROSAVAGEK:
110 	case PROSAVAGE8:
111 	case SUPERSAVAGEIXC16:
112 		/*
113 		 * We could assume that the MMIO registers
114 		 * will be in the screen segment and just use
115 		 * that, but PCI software is allowed to move them
116 		 * if it feels like it, so we look for an aperture of
117 		 * the right size; only the first 512k actually means
118 		 * anything.  The S3 engineers overestimated how
119 		 * much space they would need in the first design.
120 		 */
121 		for(j=0; j<nelem(p->mem); j++){
122 			if((p->mem[j].bar&~0x0F) != scr->paddr)
123 			if(p->mem[j].size==512*1024 || p->mem[j].size==16*1024*1024){
124 				mmiobase = p->mem[j].bar & ~0x0F;
125 				mmiosize = 512*1024;
126 				scr->mmio = vmap(mmiobase, mmiosize);
127 				if(scr->mmio == nil)
128 					return;
129 				addvgaseg("savagemmio", mmiobase, mmiosize);
130 				break;
131 			}
132 		}
133 	}
134 }
135 
136 static void
s3vsyncactive(void)137 s3vsyncactive(void)
138 {
139 	/*
140 	 * Hardware cursor information is fetched from display memory
141 	 * during the horizontal blank active time. The 80x chips may hang
142 	 * if the cursor is turned on or off during this period.
143 	 */
144 	while((vgai(Status1) & 0x08) == 0)
145 		;
146 }
147 
148 static void
s3disable(VGAscr *)149 s3disable(VGAscr*)
150 {
151 	uchar crt45;
152 
153 	/*
154 	 * Turn cursor off.
155 	 */
156 	crt45 = vgaxi(Crtx, 0x45) & 0xFE;
157 	s3vsyncactive();
158 	vgaxo(Crtx, 0x45, crt45);
159 }
160 
161 static void
s3load(VGAscr * scr,Cursor * curs)162 s3load(VGAscr* scr, Cursor* curs)
163 {
164 	uchar *p;
165 	int id, dolock, opage, x, y;
166 
167 	/*
168 	 * Disable the cursor and
169 	 * set the pointer to the two planes.
170 	 */
171 	s3disable(scr);
172 
173 	opage = 0;
174 	p = scr->vaddr;
175 	id = (vgaxi(Crtx, 0x2D)<<8)|vgaxi(Crtx, 0x2E);
176 	switch(id){
177 
178 	case VIRTUALPC2004:
179 	case VIRGE:
180 	case VIRGEDXGX:
181 	case VIRGEGX2:
182 	case VIRGEVX:
183 	case SAVAGEMXMV:
184 	case SAVAGEIXMV:
185 	case SAVAGE4:
186 	case PROSAVAGEP:
187 	case PROSAVAGEK:
188 	case PROSAVAGE8:
189 	case SUPERSAVAGEIXC16:
190 		dolock = 0;
191 		p += scr->storage;
192 		break;
193 
194 	default:
195 		dolock = 1;
196 		lock(&scr->devlock);
197 		opage = s3pageset(scr, scr->storage>>16);
198 		p += (scr->storage & 0xFFFF);
199 		break;
200 	}
201 
202 	/*
203 	 * The cursor is set in Microsoft Windows format (the ViRGE/GX2 doesn't
204 	 * support the X11 format) which gives the following truth table:
205 	 *	and xor	colour
206 	 *	 0   0	background colour
207 	 *	 0   1	foreground colour
208 	 *	 1   0	current screen pixel
209 	 *	 1   1	NOT current screen pixel
210 	 * Put the cursor into the top-left of the 64x64 array.
211 	 *
212 	 * The cursor pattern in memory is interleaved words of
213 	 * AND and XOR patterns.
214 	 */
215 	for(y = 0; y < 64; y++){
216 		for(x = 0; x < 64/8; x += 2){
217 			if(x < 16/8 && y < 16){
218 				*p++ = ~(curs->clr[2*y + x]|curs->set[2*y + x]);
219 				*p++ = ~(curs->clr[2*y + x+1]|curs->set[2*y + x+1]);
220 				*p++ = curs->set[2*y + x];
221 				*p++ = curs->set[2*y + x+1];
222 			}
223 			else {
224 				*p++ = 0xFF;
225 				*p++ = 0xFF;
226 				*p++ = 0x00;
227 				*p++ = 0x00;
228 			}
229 		}
230 	}
231 
232 	if(dolock){
233 		s3pageset(scr, opage);
234 		unlock(&scr->devlock);
235 	}
236 
237 	/*
238 	 * Save the cursor hotpoint and enable the cursor.
239 	 */
240 	scr->offset = curs->offset;
241 	s3vsyncactive();
242 	vgaxo(Crtx, 0x45, 0x01);
243 }
244 
245 static int
s3move(VGAscr * scr,Point p)246 s3move(VGAscr* scr, Point p)
247 {
248 	int x, xo, y, yo;
249 
250 	/*
251 	 * Mustn't position the cursor offscreen even partially,
252 	 * or it disappears. Therefore, if x or y is -ve, adjust the
253 	 * cursor offset instead.
254 	 * There seems to be a bug in that if the offset is 1, the
255 	 * cursor doesn't disappear off the left edge properly, so
256 	 * round it up to be even.
257 	 */
258 	if((x = p.x+scr->offset.x) < 0){
259 		xo = -x;
260 		xo = ((xo+1)/2)*2;
261 		x = 0;
262 	}
263 	else
264 		xo = 0;
265 	if((y = p.y+scr->offset.y) < 0){
266 		yo = -y;
267 		y = 0;
268 	}
269 	else
270 		yo = 0;
271 
272 	vgaxo(Crtx, 0x46, (x>>8) & 0x07);
273 	vgaxo(Crtx, 0x47, x & 0xFF);
274 	vgaxo(Crtx, 0x49, y & 0xFF);
275 	vgaxo(Crtx, 0x4E, xo);
276 	vgaxo(Crtx, 0x4F, yo);
277 	vgaxo(Crtx, 0x48, (y>>8) & 0x07);
278 
279 	return 0;
280 }
281 
282 static void
s3enable(VGAscr * scr)283 s3enable(VGAscr* scr)
284 {
285 	int i;
286 	ulong storage;
287 
288 	s3disable(scr);
289 
290 	/*
291 	 * Cursor colours. Set both the CR0[EF] and the colour
292 	 * stack in case we are using a 16-bit RAMDAC.
293 	 */
294 	vgaxo(Crtx, 0x0E, Pwhite);
295 	vgaxo(Crtx, 0x0F, Pblack);
296 	vgaxi(Crtx, 0x45);
297 
298 	for(i = 0; i < 3; i++)
299 		vgaxo(Crtx, 0x4A, Pblack);
300 	vgaxi(Crtx, 0x45);
301 	for(i = 0; i < 3; i++)
302 		vgaxo(Crtx, 0x4B, Pwhite);
303 
304 	/*
305 	 * Find a place for the cursor data in display memory.
306 	 * Must be on a 1024-byte boundary.
307 	 */
308 	storage = (scr->gscreen->width*BY2WD*scr->gscreen->r.max.y+1023)/1024;
309 	vgaxo(Crtx, 0x4C, storage>>8);
310 	vgaxo(Crtx, 0x4D, storage & 0xFF);
311 	storage *= 1024;
312 	scr->storage = storage;
313 
314 	/*
315 	 * Load, locate and enable the cursor
316 	 * in Microsoft Windows format.
317 	 */
318 	s3load(scr, &arrow);
319 	s3move(scr, ZP);
320 	vgaxo(Crtx, 0x55, vgaxi(Crtx, 0x55) & ~0x10);
321 	s3vsyncactive();
322 	vgaxo(Crtx, 0x45, 0x01);
323 }
324 
325 /*
326  * The manual gives byte offsets, but we want ulong offsets, hence /4.
327  */
328 enum {
329 	SrcBase = 0xA4D4/4,
330 	DstBase = 0xA4D8/4,
331 	Stride = 0xA4E4/4,
332 	FgrdData = 0xA4F4/4,
333 	WidthHeight = 0xA504/4,
334 	SrcXY = 0xA508/4,
335 	DestXY = 0xA50C/4,
336 	Command = 0xA500/4,
337 	SubStat = 0x8504/4,
338 	FifoStat = 0x850C/4,
339 };
340 
341 /*
342  * Wait for writes to VGA memory via linear aperture to flush.
343  */
344 enum {Maxloop = 1<<24};
345 struct {
346 	ulong linear;
347 	ulong fifo;
348 	ulong idle;
349 	ulong lineartimeout;
350 	ulong fifotimeout;
351 	ulong idletimeout;
352 } waitcount;
353 
354 static void
waitforlinearfifo(VGAscr * scr)355 waitforlinearfifo(VGAscr *scr)
356 {
357 	ulong *mmio;
358 	long x;
359 	static ulong nwaitforlinearfifo;
360 	ulong mask, val;
361 
362 	switch(scr->id){
363 	default:
364 		panic("unknown scr->id in s3 waitforlinearfifo");
365 	case 0x8A01:	/* ViRGE/[DG]X.  XFree86 says no waiting necessary */
366 		return;
367 	case 0x5631:	/* ViRGE */
368 	case 0x883D:	/* ViRGE/VX */
369 		mask = 0x0F<<6;
370 		val = 0x08<<6;
371 		break;
372 	case 0x8A10:	/* ViRGE/GX2 */
373 		mask = 0x1F<<6;
374 		val = 0x10<<6;
375 		break;
376 	}
377 	mmio = scr->mmio;
378 	x = 0;
379 	while((mmio[FifoStat]&mask) != val && x++ < Maxloop)
380 		waitcount.linear++;
381 	if(x >= Maxloop)
382 		waitcount.lineartimeout++;
383 }
384 
385 static void
waitforfifo(VGAscr * scr,int entries)386 waitforfifo(VGAscr *scr, int entries)
387 {
388 	ulong *mmio;
389 	long x;
390 	static ulong nwaitforfifo;
391 
392 	mmio = scr->mmio;
393 	x = 0;
394 	while((mmio[SubStat]&0x1F00) < ((entries+2)<<8) && x++ < Maxloop)
395 		waitcount.fifo++;
396 	if(x >= Maxloop)
397 		waitcount.fifotimeout++;
398 }
399 
400 static void
waitforidle(VGAscr * scr)401 waitforidle(VGAscr *scr)
402 {
403 	ulong *mmio;
404 	long x;
405 
406 	mmio = scr->mmio;
407 	x = 0;
408 	while((mmio[SubStat]&0x3F00) != 0x3000 && x++ < Maxloop)
409 		waitcount.idle++;
410 	if(x >= Maxloop)
411 		waitcount.idletimeout++;
412 }
413 
414 static int
hwscroll(VGAscr * scr,Rectangle r,Rectangle sr)415 hwscroll(VGAscr *scr, Rectangle r, Rectangle sr)
416 {
417 	enum { Bitbltop = 0xCC };	/* copy source */
418 	ulong *mmio;
419 	ulong cmd, stride;
420 	Point dp, sp;
421 	int did, d;
422 
423 	d = scr->gscreen->depth;
424 	did = (d-8)/8;
425 	cmd = 0x00000020|(Bitbltop<<17)|(did<<2);
426 	stride = Dx(scr->gscreen->r)*d/8;
427 
428 	if(r.min.x <= sr.min.x){
429 		cmd |= 1<<25;
430 		dp.x = r.min.x;
431 		sp.x = sr.min.x;
432 	}else{
433 		dp.x = r.max.x-1;
434 		sp.x = sr.max.x-1;
435 	}
436 
437 	if(r.min.y <= sr.min.y){
438 		cmd |= 1<<26;
439 		dp.y = r.min.y;
440 		sp.y = sr.min.y;
441 	}else{
442 		dp.y = r.max.y-1;
443 		sp.y = sr.max.y-1;
444 	}
445 
446 	mmio = scr->mmio;
447 	waitforlinearfifo(scr);
448 	waitforfifo(scr, 7);
449 	mmio[SrcBase] = scr->paddr;
450 	mmio[DstBase] = scr->paddr;
451 	mmio[Stride] = (stride<<16)|stride;
452 	mmio[WidthHeight] = ((Dx(r)-1)<<16)|Dy(r);
453 	mmio[SrcXY] = (sp.x<<16)|sp.y;
454 	mmio[DestXY] = (dp.x<<16)|dp.y;
455 	mmio[Command] = cmd;
456 	waitforidle(scr);
457 	return 1;
458 }
459 
460 static int
hwfill(VGAscr * scr,Rectangle r,ulong sval)461 hwfill(VGAscr *scr, Rectangle r, ulong sval)
462 {
463 	enum { Bitbltop = 0xCC };	/* copy source */
464 	ulong *mmio;
465 	ulong cmd, stride;
466 	int did, d;
467 
468 	d = scr->gscreen->depth;
469 	did = (d-8)/8;
470 	cmd = 0x16000120|(Bitbltop<<17)|(did<<2);
471 	stride = Dx(scr->gscreen->r)*d/8;
472 	mmio = scr->mmio;
473 	waitforlinearfifo(scr);
474 	waitforfifo(scr, 8);
475 	mmio[SrcBase] = scr->paddr;
476 	mmio[DstBase] = scr->paddr;
477 	mmio[DstBase] = scr->paddr;
478 	mmio[Stride] = (stride<<16)|stride;
479 	mmio[FgrdData] = sval;
480 	mmio[WidthHeight] = ((Dx(r)-1)<<16)|Dy(r);
481 	mmio[DestXY] = (r.min.x<<16)|r.min.y;
482 	mmio[Command] = cmd;
483 	waitforidle(scr);
484 	return 1;
485 }
486 
487 enum {
488 	CursorSyncCtl = 0x0D,	/* in Seqx */
489 	VsyncHi = 0x80,
490 	VsyncLo = 0x40,
491 	HsyncHi = 0x20,
492 	HsyncLo = 0x10,
493 };
494 
495 static void
s3blank(VGAscr *,int blank)496 s3blank(VGAscr*, int blank)
497 {
498 	uchar x;
499 
500 	x = vgaxi(Seqx, CursorSyncCtl);
501 	x &= ~0xF0;
502 	if(blank)
503 		x |= VsyncLo | HsyncLo;
504 	vgaxo(Seqx, CursorSyncCtl, x);
505 }
506 
507 static void
s3drawinit(VGAscr * scr)508 s3drawinit(VGAscr *scr)
509 {
510 	extern void savageinit(VGAscr*);	/* vgasavage.c */
511 	ulong id;
512 
513 	id = (vgaxi(Crtx, 0x2D)<<8)|vgaxi(Crtx, 0x2E);
514 	scr->id = id;
515 
516 	/*
517 	 * It's highly likely that other ViRGEs will work without
518 	 * change to the driver, with the exception of the size of
519 	 * the linear aperture memory write FIFO.  Since we don't
520 	 * know that size, I'm not turning them on.  See waitforlinearfifo
521 	 * above.
522 	 */
523 	scr->blank = s3blank;
524 	/* hwblank = 1;		not known to work well */
525 
526 	switch(id){
527 	case VIRGE:
528 	case VIRGEVX:
529 	case VIRGEGX2:
530 		scr->mmio = (ulong*)((char*)scr->vaddr+0x1000000);
531 		scr->fill = hwfill;
532 		scr->scroll = hwscroll;
533 		break;
534 	case SAVAGEMXMV:
535 	case SAVAGEIXMV:
536 		scr->mmio = (ulong*)((char*)scr->vaddr+0x1000000);
537 		savageinit(scr);
538 		break;
539 	case SUPERSAVAGEIXC16:
540 	case SAVAGE4:
541 	case PROSAVAGEP:
542 	case PROSAVAGE8:
543 	case PROSAVAGEK:
544 		/* scr->mmio is set by s3linear */
545 		savageinit(scr);
546 		break;
547 	}
548 }
549 
550 VGAdev vgas3dev = {
551 	"s3",
552 
553 	0,
554 	0,
555 	s3page,
556 	s3linear,
557 	s3drawinit,
558 };
559 
560 VGAcur vgas3cur = {
561 	"s3hwgc",
562 
563 	s3enable,
564 	s3disable,
565 	s3load,
566 	s3move,
567 };
568 
569