xref: /haiku/src/add-ons/accelerants/s3/savage_mode.cpp (revision 46d4471af7fad4e52cfbd09174598cf5318aceed)
1 /*
2 	Haiku S3 Savage driver adapted from the X.org Savage driver.
3 
4 	Copyright (C) 1994-2000 The XFree86 Project, Inc.	All Rights Reserved.
5 	Copyright (c) 2003-2006, X.Org Foundation
6 
7 	Copyright 2007-2008 Haiku, Inc.  All rights reserved.
8 	Distributed under the terms of the MIT license.
9 
10 	Authors:
11 	Gerald Zajac 2006-2008
12 */
13 
14 
15 #include "accel.h"
16 #include "savage.h"
17 
18 
19 #define BASE_FREQ	14.31818
20 
21 
22 struct SavageRegRec {
23 	uint8  CRTC[25];			// Crtc Controller reg's
24 
25 	uint8  SR12, SR13, SR1B, SR29;
26 	uint8  CR33, CR34, CR3A, CR3B, CR3C;
27 	uint8  CR42, CR43, CR45;
28 	uint8  CR50, CR51, CR53, CR58, CR5D, CR5E;
29 	uint8  CR65, CR66, CR67, CR69;
30 	uint8  CR86, CR88;
31 	uint8  CR90, CR91, CRB0;
32 };
33 
34 
35 
36 static void
37 Savage_SetGBD_Twister(const DisplayModeEx& mode)
38 {
39 	SharedInfo& si = *gInfo.sharedInfo;
40 	int bci_enable;
41 
42 	if (si.chipType == S3_SAVAGE4)
43 		bci_enable = BCI_ENABLE;
44 	else
45 		bci_enable = BCI_ENABLE_TWISTER;
46 
47 	// MM81C0 and 81C4 are used to control primary stream.
48 	WriteReg32(PRI_STREAM_FBUF_ADDR0, 0);
49 	WriteReg32(PRI_STREAM_FBUF_ADDR1, 0);
50 
51 	// Program Primary Stream Stride Register.
52 	//
53 	// Tell engine if tiling on or off, set primary stream stride, and
54 	// if tiling, set tiling bits/pixel and primary stream tile offset.
55 	// Note that tile offset (bits 16 - 29) must be scanline width in
56 	// bytes/128bytespertile * 256 Qwords/tile.  This is equivalent to
57 	// lDelta * 2.  Remember that if tiling, lDelta is screenwidth in
58 	// bytes padded up to an even number of tilewidths.
59 
60 	WriteReg32(PRI_STREAM_STRIDE,
61 		(((mode.bytesPerRow * 2) << 16) & 0x3FFFE000) |
62 		(mode.bytesPerRow & 0x00001fff));
63 
64 	//  CR69, bit 7 = 1
65 	//  to use MM streams processor registers to control primary stream.
66 
67 	WriteCrtcReg(0x69, 0x80, 0x80);
68 
69 	WriteReg32(S3_GLOBAL_GBD_REG, bci_enable | S3_LITTLE_ENDIAN | S3_BD64);
70 
71 	// If MS1NB style linear tiling mode.
72 	// bit MM850C[15] = 0 select NB linear tile mode.
73 	// bit MM850C[15] = 1 select MS-1 128-bit non-linear tile mode.
74 
75 	uint32 ulTmp = ReadReg32(ADVANCED_FUNC_CTRL) | 0x8000;	// use MS-s style tile mode
76 	WriteReg32(ADVANCED_FUNC_CTRL, ulTmp);
77 
78 	// CR88, bit 4 - Block write enabled/disabled.
79 	//
80 	// Note: Block write must be disabled when writing to tiled
81 	//		 memory. Even when writing to non-tiled memory, block
82 	//		 write should only be enabled for certain types of SGRAM.
83 
84 	WriteCrtcReg(0x88, DISABLE_BLOCK_WRITE_2D, DISABLE_BLOCK_WRITE_2D);
85 }
86 
87 
88 static void
89 Savage_SetGBD_3D(const DisplayModeEx& mode)
90 {
91 	int bci_enable = BCI_ENABLE;
92 
93 	// MM81C0 and 81C4 are used to control primary stream.
94 	WriteReg32(PRI_STREAM_FBUF_ADDR0, 0);
95 	WriteReg32(PRI_STREAM_FBUF_ADDR1, 0);
96 
97 	// Program Primary Stream Stride Register.
98 	//
99 	// Tell engine if tiling on or off, set primary stream stride, and
100 	// if tiling, set tiling bits/pixel and primary stream tile offset.
101 	// Note that tile offset (bits 16 - 29) must be scanline width in
102 	// bytes/128bytespertile * 256 Qwords/tile.  This is equivalent to
103 	// lDelta * 2.  Remember that if tiling, lDelta is screenwidth in
104 	// bytes padded up to an even number of tilewidths.
105 
106 	WriteReg32(PRI_STREAM_STRIDE,
107 		(((mode.bytesPerRow * 2) << 16) & 0x3FFFE000) |
108 		(mode.bytesPerRow & 0x00001fff));
109 
110 	// CR69, bit 7 = 1 to use MM streams processor registers to control primary
111 	// stream.
112 
113 	WriteCrtcReg(0x69, 0x80, 0x80);
114 
115 	WriteReg32(S3_GLOBAL_GBD_REG, bci_enable | S3_LITTLE_ENDIAN | S3_BD64);
116 
117 	// If MS1NB style linear tiling mode.
118 	// bit MM850C[15] = 0 select NB linear tile mode.
119 	// bit MM850C[15] = 1 select MS-1 128-bit non-linear tile mode.
120 
121 	uint32 ulTmp = ReadReg32(ADVANCED_FUNC_CTRL) | 0x8000;	// use MS-s style tile mode
122 	WriteReg32(ADVANCED_FUNC_CTRL, ulTmp);
123 
124 	// CR88, bit 4 - Block write enabled/disabled.
125 	//
126 	// Note: Block write must be disabled when writing to tiled
127 	//		 memory.  Even when writing to non-tiled memory, block
128 	//		 write should only be enabled for certain types of SGRAM.
129 
130 	WriteCrtcReg(0x88, DISABLE_BLOCK_WRITE_2D, DISABLE_BLOCK_WRITE_2D);
131 }
132 
133 
134 static void
135 Savage_SetGBD_MX(const DisplayModeEx& mode)
136 {
137 	SharedInfo& si = *gInfo.sharedInfo;
138 	int bci_enable = BCI_ENABLE;
139 
140 	// CR67_3:
141 	//	= 1  stream processor MMIO address and stride register
142 	//		 are used to control the primary stream.
143 	//	= 0  standard VGA address and stride registers
144 	//		 are used to control the primary streams.
145 
146 	WriteCrtcReg(0x67, 0x08, 0x08);
147 	// IGA 2.
148 	WriteSeqReg(0x26, 0x4f);		// select IGA 2 read/writes
149 	WriteCrtcReg(0x67, 0x08, 0x08);
150 	WriteSeqReg(0x26, 0x40);		// select IGA 1
151 
152 	// Set primary stream to bank 0 via reg CRCA.
153 	WriteCrtcReg(MEMORY_CTRL0_REG, 0x00, MEM_PS1 + MEM_PS2);
154 
155 	// MM81C0 and 81C4 are used to control primary stream.
156 	WriteReg32(PRI_STREAM_FBUF_ADDR0,  si.frameBufferOffset & 0x7fffff);
157 	WriteReg32(PRI_STREAM_FBUF_ADDR1,  si.frameBufferOffset & 0x7fffff);
158 	WriteReg32(PRI_STREAM2_FBUF_ADDR0, si.frameBufferOffset & 0x7fffff);
159 	WriteReg32(PRI_STREAM2_FBUF_ADDR1, si.frameBufferOffset & 0x7fffff);
160 
161 	// Program Primary Stream Stride Register.
162 	//
163 	// Tell engine if tiling on or off, set primary stream stride, and
164 	// if tiling, set tiling bits/pixel and primary stream tile offset.
165 	// Note that tile offset (bits 16 - 29) must be scanline width in
166 	// bytes/128bytespertile * 256 Qwords/tile.  This is equivalent to
167 	// lDelta * 2.  Remember that if tiling, lDelta is screenwidth in
168 	// bytes padded up to an even number of tilewidths.
169 
170 	WriteReg32(PRI_STREAM_STRIDE,
171 		(((mode.bytesPerRow * 2) << 16) & 0x3FFF0000) |
172 		(mode.bytesPerRow & 0x00003fff));
173 	WriteReg32(PRI_STREAM2_STRIDE,
174 		(((mode.bytesPerRow * 2) << 16) & 0x3FFF0000) |
175 		(mode.bytesPerRow & 0x00003fff));
176 
177 	WriteReg32(S3_GLOBAL_GBD_REG, bci_enable | S3_LITTLE_ENDIAN | S3_BD64);
178 
179 	// CR78, bit 3  - Block write enabled(1)/disabled(0).
180 	//		 bit 2  - Block write cycle time(0:2 cycles,1: 1 cycle)
181 	//	Note:	Block write must be disabled when writing to tiled
182 	//			memory.	Even when writing to non-tiled memory, block
183 	//			write should only be enabled for certain types of SGRAM.
184 
185 	WriteCrtcReg(0x78, 0xfb, 0xfb);
186 }
187 
188 
189 static void
190 Savage_SetGBD_Super(const DisplayModeEx& mode)
191 {
192 	SharedInfo& si = *gInfo.sharedInfo;
193 	int bci_enable = BCI_ENABLE_TWISTER;
194 
195 	// CR67_3:
196 	//	= 1 stream processor MMIO address and stride register
197 	//		are used to control the primary stream.
198 	//	= 0 standard VGA address and stride registers
199 	//		are used to control the primary streams.
200 
201 	WriteCrtcReg(0x67, 0x08, 0x08);
202 	// IGA 2.
203 	WriteSeqReg(0x26, 0x4f);		// select IGA 2 read/writes
204 	WriteCrtcReg(0x67, 0x08, 0x08);
205 	WriteSeqReg(0x26, 0x40);		// select IGA 1
206 
207 	// Load ps1 active registers as determined by MM81C0/81C4.
208 	// Load ps2 active registers as determined by MM81B0/81B4.
209 
210 	WriteCrtcReg(0x65, 0x03, 0x03);
211 
212 	// Program Primary Stream Stride Register.
213 	//
214 	// Tell engine if tiling on or off, set primary stream stride, and
215 	// if tiling, set tiling bits/pixel and primary stream tile offset.
216 	// Note that tile offset (bits 16 - 29) must be scanline width in
217 	// bytes/128bytespertile * 256 Qwords/tile.  This is equivalent to
218 	// lDelta * 2.  Remember that if tiling, lDelta is screenwidth in
219 	// bytes padded up to an even number of tilewidths.
220 
221 	WriteReg32(PRI_STREAM_STRIDE,
222 		(((mode.bytesPerRow * 2) << 16) & 0x3FFF0000) |
223 		(mode.bytesPerRow & 0x00001fff));
224 	WriteReg32(PRI_STREAM2_STRIDE,
225 		(((mode.bytesPerRow * 2) << 16) & 0x3FFF0000) |
226 		(mode.bytesPerRow & 0x00001fff));
227 
228 	// MM81C0 and 81C4 are used to control primary stream.
229 	WriteReg32(PRI_STREAM_FBUF_ADDR0, si.frameBufferOffset);
230 	WriteReg32(PRI_STREAM_FBUF_ADDR1, 0x80000000);
231 	WriteReg32(PRI_STREAM2_FBUF_ADDR0, (si.frameBufferOffset & 0xfffffffc) | 0x80000000);
232 	WriteReg32(PRI_STREAM2_FBUF_ADDR1, si.frameBufferOffset & 0xfffffffc);
233 
234 	// Bit 28:block write disable.
235 	WriteReg32(S3_GLOBAL_GBD_REG, bci_enable | S3_BD64 | 0x10000000);
236 }
237 
238 
239 static void
240 Savage_SetGBD_2000(const DisplayModeEx& mode)
241 {
242 	SharedInfo& si = *gInfo.sharedInfo;
243 	int bci_enable = BCI_ENABLE_TWISTER;
244 
245 	// MM81C0 and 81B0 are used to control primary stream.
246 	WriteReg32(PRI_STREAM_FBUF_ADDR0, si.frameBufferOffset);
247 	WriteReg32(PRI_STREAM2_FBUF_ADDR0, si.frameBufferOffset);
248 
249 	// Program Primary Stream Stride Register.
250 	//
251 	// Tell engine if tiling on or off, set primary stream stride, and
252 	// if tiling, set tiling bits/pixel and primary stream tile offset.
253 	// Note that tile offset (bits 16 - 29) must be scanline width in
254 	// bytes/128bytespertile * 256 Qwords/tile.  This is equivalent to
255 	// lDelta * 2.  Remember that if tiling, lDelta is screenwidth in
256 	// bytes padded up to an even number of tilewidths.
257 
258 	WriteReg32(PRI_STREAM_STRIDE,  ((mode.bytesPerRow << 4) & 0x7ff0));
259 	WriteReg32(PRI_STREAM2_STRIDE, ((mode.bytesPerRow << 4) & 0x7ff0));
260 
261 	// CR67_3:
262 	//	= 1 stream processor MMIO address and stride register
263 	//		are used to control the primary stream.
264 	//	= 0 standard VGA address and stride registers
265 	//		are used to control the primary streams
266 
267 	WriteCrtcReg(0x67, 0x08, 0x08);
268 
269 	// Bit 28:block write disable.
270 	WriteReg32(S3_GLOBAL_GBD_REG, bci_enable | S3_BD64 | 0x10000000);
271 
272 	WriteCrtcReg(0x73, 0x00, 0x20);		// CR73 bit 5 = 0 block write disable
273 }
274 
275 
276 
277 static void
278 Savage_SetGBD(const DisplayModeEx& mode)
279 {
280 	SharedInfo& si = *gInfo.sharedInfo;
281 
282 	VerticalRetraceWait();
283 
284 	switch (gInfo.sharedInfo->chipType) {
285 		case S3_SAVAGE_3D:
286 			Savage_SetGBD_3D(mode);
287 			break;
288 
289 		case S3_SAVAGE_MX:
290 			Savage_SetGBD_MX(mode);
291 			break;
292 
293 		case S3_SAVAGE4:
294 		case S3_PROSAVAGE:
295 		case S3_TWISTER:
296 		case S3_PROSAVAGE_DDR:
297 			Savage_SetGBD_Twister(mode);
298 			break;
299 
300 		case S3_SUPERSAVAGE:
301 			Savage_SetGBD_Super(mode);
302 			break;
303 
304 		case S3_SAVAGE2000:
305 			Savage_SetGBD_2000(mode);
306 			break;
307 	}
308 
309 	WriteCrtcReg(0x50, 0xc1, 0xc1);		// CR50, bit 7,6,0 = 111, Use GBD
310 
311 	// Set up the Global Bitmap Descriptor used for BCI.
312 	// Do not enable block_write because we can't determine if the memory
313 	// type is a certain type of SGRAM for which block write can be used.
314 	//	bit 24~25: tile format,  00 = linear
315 	//	bit 28: block write disble/enable, 0 = disable, 1 = enable
316 
317 	si.globalBitmapDesc = mode.timing.h_display | (mode.bpp << 16)
318 		| TILE_FORMAT_LINEAR | BCI_BD_BW_DISABLE | S3_BD64;	// disable block write
319 }
320 
321 
322 static void
323 Savage_Initialize2DEngine(const DisplayModeEx& mode)
324 {
325 	SharedInfo& si = *gInfo.sharedInfo;
326 
327 	WriteCrtcReg(0x40, 0x01);	// enable graphics engine
328 	WriteCrtcReg(0x31, 0x0c);	// turn on 16-bit register access
329 
330 	// Setup plane masks.
331 	WriteReg32(0x8128, ~0);		// enable all write planes
332 	WriteReg32(0x812C, ~0);		// enable all read planes
333 	WriteReg16(0x8134, 0x27);
334 	WriteReg16(0x8136, 0x07);
335 
336 	switch (si.chipType) {
337 		case S3_SAVAGE_3D:
338 		case S3_SAVAGE_MX:
339 
340 			WriteReg32(0x48C18, ReadReg32(0x48C18) & 0x3FF0);	// Disable BCI
341 
342 			// Setup BCI command overflow buffer.  0x48c14
343 			// Bits 0-11  = Bits 22-11 of the Command Buffer Offset.
344 			// Bits 12-28 = Total number of entries in the command buffer(Read only).
345 			// Bits 29-31 = COB size index, 111 = 32K entries or 128K bytes
346 			WriteReg32(0x48C14, (si.cobOffset >> 11) | (si.cobSizeIndex << 29));
347 
348 			// Program shadow status update.
349 			WriteReg32(0x48C10, 0x78207220);
350 
351 			WriteReg32(0x48C0C, 0);
352 			// Enable BCI and command overflow buffer.
353 			WriteReg32(0x48C18, ReadReg32(0x48C18) | 0x0C);
354 			break;
355 
356 		case S3_SAVAGE4:
357 		case S3_PROSAVAGE:
358 		case S3_TWISTER:
359 		case S3_PROSAVAGE_DDR:
360 		case S3_SUPERSAVAGE:
361 
362 			// Some Savage4 and ProSavage chips have coherency problems with
363 			// respect to the Command Overflow Buffer (COB); thus, do not
364 			// enable the COB.
365 
366 			WriteReg32(0x48C18, ReadReg32(0x48C18) & 0x3FF0);	// Disable BCI
367 			WriteReg32(0x48C10, 0x00700040);
368 			WriteReg32(0x48C0C, 0);
369 			WriteReg32(0x48C18, ReadReg32(0x48C18) | 0x08);		// enable BCI without COB
370 
371 			break;
372 
373 		case S3_SAVAGE2000:
374 
375 			// Disable BCI.
376 			WriteReg32(0x48C18, 0);
377 			// Setup BCI command overflow buffer.
378 			WriteReg32(0x48C18, (si.cobOffset >> 7) | (si.cobSizeIndex));
379 			// Disable shadow status update.
380 			WriteReg32(0x48A30, 0);
381 			// Enable BCI and command overflow buffer.
382 			WriteReg32(0x48C18, ReadReg32(0x48C18) | 0x00280000);
383 
384 			break;
385 	}
386 
387 	// Use and set global bitmap descriptor.
388 
389 	Savage_SetGBD(mode);
390 }
391 
392 
393 static void
394 Savage_GEReset(const DisplayModeEx& mode)
395 {
396 	gInfo.WaitIdleEmpty();
397 	snooze(10000);
398 
399 	for (int r = 1; r < 10; r++) {
400 		bool bSuccess = false;
401 
402 		WriteCrtcReg(0x66, 0x02, 0x02);
403 		snooze(10000);
404 		WriteCrtcReg(0x66, 0x00, 0x02);
405 		snooze(10000);
406 
407 		gInfo.WaitIdleEmpty();
408 
409 		WriteReg32(DEST_SRC_STR, mode.bytesPerRow << 16 | mode.bytesPerRow);
410 
411 		snooze(10000);
412 		switch (gInfo.sharedInfo->chipType) {
413 			case S3_SAVAGE_3D:
414 			case S3_SAVAGE_MX:
415 				bSuccess = (STATUS_WORD0 & 0x0008ffff) == 0x00080000;
416 				break;
417 
418 			case S3_SAVAGE4:
419 			case S3_PROSAVAGE:
420 			case S3_PROSAVAGE_DDR:
421 			case S3_TWISTER:
422 			case S3_SUPERSAVAGE:
423 				bSuccess = (ALT_STATUS_WORD0 & 0x0081ffff) == 0x00800000;
424 				break;
425 
426 			case S3_SAVAGE2000:
427 				bSuccess = (ALT_STATUS_WORD0 & 0x008fffff) == 0;
428 				break;
429 		}
430 
431 		if (bSuccess)
432 			break;
433 
434 		snooze(10000);
435 		TRACE("Savage_GEReset(), restarting S3 graphics engine reset %2d ...\n", r);
436 	}
437 
438 	// At this point, the FIFO is empty and the engine is idle.
439 
440 	WriteReg32(SRC_BASE, 0);
441 	WriteReg32(DEST_BASE, 0);
442 	WriteReg32(CLIP_L_R, ((0) << 16) | mode.timing.h_display);
443 	WriteReg32(CLIP_T_B, ((0) << 16) | mode.timing.v_display);
444 	WriteReg32(MONO_PAT_0, ~0);
445 	WriteReg32(MONO_PAT_1, ~0);
446 
447 	Savage_SetGBD(mode);
448 }
449 
450 
451 static void
452 Savage_CalcClock(long freq, int min_m,
453 				int min_n1, int max_n1,
454 				int min_n2, int max_n2,
455 				long freq_min, long freq_max,
456 				unsigned int *mdiv, unsigned int *ndiv, unsigned int *r)
457 {
458 	uint8 best_n1 = 16 + 2, best_n2 = 2, best_m = 125 + 2;
459 
460 	double ffreq = freq / 1000.0 / BASE_FREQ;
461 	double ffreq_max = freq_max / 1000.0 / BASE_FREQ;
462 	double ffreq_min = freq_min / 1000.0 / BASE_FREQ;
463 
464 	if (ffreq < ffreq_min / (1 << max_n2)) {
465 		TRACE("Savage_CalcClock() invalid frequency %1.3f Mhz\n", ffreq * BASE_FREQ);
466 		ffreq = ffreq_min / (1 << max_n2);
467 	}
468 	if (ffreq > ffreq_max / (1 << min_n2)) {
469 		TRACE("Savage_CalcClock() invalid frequency %1.3f Mhz\n", ffreq * BASE_FREQ);
470 		ffreq = ffreq_max / (1 << min_n2);
471 	}
472 
473 	// Work out suitable timings.
474 
475 	double best_diff = ffreq;
476 
477 	for (uint8 n2 = min_n2; n2 <= max_n2; n2++) {
478 		for (uint8 n1 = min_n1 + 2; n1 <= max_n1 + 2; n1++) {
479 			int m = (int)(ffreq * n1 * (1 << n2) + 0.5);
480 			if (m < min_m + 2 || m > 127 + 2)
481 				continue;
482 
483 			double div = (double)(m) / (double)(n1);
484 			if ((div >= ffreq_min) && (div <= ffreq_max)) {
485 				double diff = ffreq - div / (1 << n2);
486 				if (diff < 0.0)
487 					diff = -diff;
488 				if (diff < best_diff) {
489 					best_diff = diff;
490 					best_m = m;
491 					best_n1 = n1;
492 					best_n2 = n2;
493 				}
494 			}
495 		}
496 	}
497 
498 	*ndiv = best_n1 - 2;
499 	*r = best_n2;
500 	*mdiv = best_m - 2;
501 }
502 
503 
504 static void
505 Savage_WriteMode(const DisplayModeEx& mode, const SavageRegRec& regRec)
506 {
507 	TRACE("Savage_WriteMode() begin\n");
508 
509 	SharedInfo& si = *gInfo.sharedInfo;
510 
511 	gInfo.WaitIdleEmpty();
512 
513 	WriteMiscOutReg(0x23);
514 	WriteCrtcReg(0x38, 0x48);		// unlock sys regs CR20~CR3F
515 	WriteCrtcReg(0x39, 0xa0);		// unlock sys regs CR40~CRFF
516 	WriteSeqReg(0x08, 0x06);		// unlock sequencer regs SR09~SRFF
517 
518 	if ( ! S3_SAVAGE_MOBILE_SERIES(si.chipType))
519 		Savage_Initialize2DEngine(mode);
520 
521 	if (ReadCrtcReg(0x66) & 0x01) {
522 		Savage_GEReset(mode);	// reset GE to make sure nothing is going on
523 	}
524 
525 	WriteCrtcReg(0x67, regRec.CR67 & ~0x0e); // no STREAMS yet old and new
526 
527 	// Set register SR19 to zero so that the ProSavage chips will start up
528 	// when booting under BeOS using the default boot screen, and set register
529 	// CR5F to zero so that the ProSavage chips will start up when Haiku boot
530 	// screen had a depth of 32 bits/pixel
531 
532 	if (si.chipType == S3_PROSAVAGE || si.chipType == S3_TWISTER) {
533 		WriteSeqReg(0x19, 0);
534 		WriteCrtcReg(0x5f, 0);
535 	}
536 
537 	// Clear bit 3 in SR30 so that Savage MX chip will startup.  If bit 3 is
538 	// not cleared, it will startup only if booting under BeOS using the
539 	// default boot screen or the boot screen resolution matches the resolution
540 	// of the mode currently being set.
541 
542 	if (si.chipType == S3_SAVAGE_MX)
543 		WriteSeqReg(0x30, 0x00, 0x08);
544 
545 	// Set extended regs.
546 
547 	WriteCrtcReg(0x66, regRec.CR66);
548 	WriteCrtcReg(0x3a, regRec.CR3A);
549 	WriteCrtcReg(0x58, regRec.CR58);
550 	WriteCrtcReg(0x53, regRec.CR53 & 0x7f);
551 
552 	// If Savage IX/MX or SuperSavage, set SR54 & SR56 to 0x10 so that when
553 	// resolutions are set where the width and/or height is less than the
554 	// native resolution of the attached LCD display, the chip will not expand
555 	// the display to fill the screen.  That is, if a resolution is set to
556 	// 640x480, it will use only 640x480 pixels for the display.  When the chip
557 	// expands the display, text is much less readable.
558 
559 	if (S3_SAVAGE_MOBILE_SERIES(si.chipType)) {
560 		WriteSeqReg(0x54, 0x10);
561 		WriteSeqReg(0x56, 0x10);
562 	}
563 
564 	// Set the standard CRTC vga regs.
565 
566 	WriteCrtcReg(0x11, 0x00, 0x80);		// unlock CRTC reg's 0-7 by clearing bit 7 of cr11
567 
568 	for (int j = 0; j < (int)B_COUNT_OF(regRec.CRTC); j++)
569 		WriteCrtcReg(j, regRec.CRTC[j]);
570 
571 	// Setup HSYNC & VSYNC polarity.
572 
573 	uint8 temp = ((ReadMiscOutReg() & 0x3f) | 0x0c);
574 
575 	if (!(mode.timing.flags & B_POSITIVE_HSYNC))
576 		temp |= 0x40;
577 	if (!(mode.timing.flags & B_POSITIVE_VSYNC))
578 		temp |= 0x80;
579 
580 	WriteMiscOutReg(temp);
581 
582 	// Extended mode timing registers.
583 	WriteCrtcReg(0x53, regRec.CR53);
584 	WriteCrtcReg(0x5d, regRec.CR5D);
585 	WriteCrtcReg(0x5e, regRec.CR5E);
586 	WriteCrtcReg(0x3b, regRec.CR3B);
587 	WriteCrtcReg(0x3c, regRec.CR3C);
588 	WriteCrtcReg(0x43, regRec.CR43);
589 	WriteCrtcReg(0x65, regRec.CR65);
590 
591 	// Restore the desired video mode with cr67.
592 	WriteCrtcReg(0x67, regRec.CR67 & ~0x0e);	// no streams for new and old streams engines
593 
594 	// Other mode timing and extended regs.
595 	WriteCrtcReg(0x34, regRec.CR34);
596 	WriteCrtcReg(0x42, regRec.CR42);
597 	WriteCrtcReg(0x45, regRec.CR45);
598 	WriteCrtcReg(0x50, regRec.CR50);
599 	WriteCrtcReg(0x51, regRec.CR51);
600 
601 	// Memory timings.
602 	VerticalRetraceWait();
603 	WriteCrtcReg(0x69, regRec.CR69);
604 
605 	WriteCrtcReg(0x33, regRec.CR33);
606 	WriteCrtcReg(0x86, regRec.CR86);
607 	WriteCrtcReg(0x88, regRec.CR88);
608 	WriteCrtcReg(0x90, regRec.CR90);
609 	WriteCrtcReg(0x91, regRec.CR91);
610 
611 	if (si.chipType == S3_SAVAGE4)
612 		WriteCrtcReg(0xb0, regRec.CRB0);
613 
614 	WriteSeqReg(0x1b, regRec.SR1B);
615 
616 	if ( ! (S3_SAVAGE_MOBILE_SERIES(si.chipType) &&  si.displayType == MT_LCD)) {
617 		// Set extended seq regs for dclk.
618 		WriteSeqReg(0x12, regRec.SR12);
619 		WriteSeqReg(0x13, regRec.SR13);
620 		WriteSeqReg(0x29, regRec.SR29);
621 
622 		// Load new m, n pll values for dclk & mclk.
623 		temp = ReadSeqReg(0x15) & ~0x20;
624 		WriteSeqReg(0x15, temp);
625 		WriteSeqReg(0x15, temp | 0x20);
626 		WriteSeqReg(0x15, temp);
627 		snooze(100);
628 	}
629 
630 	// Now write out cr67 in full, possibly starting STREAMS.
631 	VerticalRetraceWait();
632 	WriteCrtcReg(0x67, regRec.CR67);
633 
634 	uint8 cr66 = ReadCrtcReg(0x66);
635 	WriteCrtcReg(0x66, cr66 | 0x80);
636 	uint8 cr3a = ReadCrtcReg(0x3a);
637 	WriteCrtcReg(0x3a, cr3a | 0x80);
638 
639 	Savage_GEReset(mode);
640 
641 	WriteCrtcReg(0x66, cr66);
642 	WriteCrtcReg(0x3a, cr3a);
643 
644 	Savage_Initialize2DEngine(mode);
645 
646 	WriteCrtcReg(0x40, 0x01);		// enable graphics engine
647 
648 	Savage_SetGBD(mode);
649 
650 	TRACE("Savage_WriteMode() done\n");
651 
652 	return;
653 }
654 
655 
656 static bool
657 Savage_ModeInit(const DisplayModeEx& mode)
658 {
659 	TRACE("Savage_ModeInit(%dx%d, %d kHz)\n",
660 		mode.timing.h_display, mode.timing.v_display, mode.timing.pixel_clock);
661 
662 	SharedInfo& si = *gInfo.sharedInfo;
663 	SavageRegRec regRec;
664 
665 	int horizScaleFactor = 1;
666 
667 	if (mode.bpp == 16 && si.chipType == S3_SAVAGE_3D)
668 		horizScaleFactor = 2;
669 
670 	InitCrtcTimingValues(mode, horizScaleFactor, regRec.CRTC,
671 			regRec.CR3B, regRec.CR3C, regRec.CR5D, regRec.CR5E);
672 	regRec.CRTC[0x17] = 0xEB;
673 
674 	int dclk = mode.timing.pixel_clock;
675 	regRec.CR67 = 0x00;
676 
677 	switch (mode.bpp) {
678 		case 8:
679 			if ((si.chipType == S3_SAVAGE2000) && (dclk >= 230000))
680 				regRec.CR67 = 0x10;		// 8bpp, 2 pixels/clock
681 			else
682 				regRec.CR67 = 0x00;		// 8bpp, 1 pixel/clock
683 			break;
684 
685 		case 15:
686 			if (S3_SAVAGE_MOBILE_SERIES(si.chipType)
687 				|| ((si.chipType == S3_SAVAGE2000) && (dclk >= 230000)))
688 				regRec.CR67 = 0x30;		// 15bpp, 2 pixel/clock
689 			else
690 				regRec.CR67 = 0x20;		// 15bpp, 1 pixels/clock
691 			break;
692 
693 		case 16:
694 			if (S3_SAVAGE_MOBILE_SERIES(si.chipType)
695 				|| ((si.chipType == S3_SAVAGE2000) && (dclk >= 230000)))
696 				regRec.CR67 = 0x50;		// 16bpp, 2 pixel/clock
697 			else
698 				regRec.CR67 = 0x40;		// 16bpp, 1 pixels/clock
699 			break;
700 
701 		case 32:
702 			regRec.CR67 = 0xd0;
703 			break;
704 	}
705 
706 	regRec.CR3A = (ReadCrtcReg(0x3a) & 0x7f) | 0x15;
707 	regRec.CR53 = 0x00;
708 	regRec.CR66 = 0x89;
709 	regRec.CR58 = (ReadCrtcReg(0x58) & 0x80) | 0x13;
710 
711 	regRec.SR1B = ReadSeqReg(0x1b) | 0x10;	// enable 8-bit Color Lookup Table
712 
713 	regRec.CR43 = regRec.CR45 = regRec.CR65 = 0x00;
714 
715 	unsigned int m, n, r;
716 	Savage_CalcClock(dclk, 1, 1, 127, 0, 4, 180000, 360000, &m, &n, &r);
717 	regRec.SR12 = (r << 6) | (n & 0x3f);
718 	regRec.SR13 = m & 0xff;
719 	regRec.SR29 = (r & 4) | (m & 0x100) >> 5 | (n & 0x40) >> 2;
720 
721 	TRACE("CalcClock, m: %d  n: %d  r: %d\n", m, n, r);
722 
723 	regRec.CR42 = 0x00;
724 	regRec.CR34 = 0x10;
725 
726 	int width = mode.bytesPerRow / 8;
727 	regRec.CR91 = 0xff & width;
728 	regRec.CR51 = (0x300 & width) >> 4;
729 	regRec.CR90 = 0x80 | (width >> 8);
730 
731 	// Set frame buffer description.
732 
733 	if (mode.bpp <= 8)
734 		regRec.CR50 = 0;
735 	else if (mode.bpp <= 16)
736 		regRec.CR50 = 0x10;
737 	else
738 		regRec.CR50 = 0x30;
739 
740 	width = mode.timing.h_display;		// width of display in pixels
741 
742 	if (width == 640)
743 		regRec.CR50 |= 0x40;
744 	else if (width == 800)
745 		regRec.CR50 |= 0x80;
746 	else if (width == 1024)
747 		regRec.CR50 |= 0x00;
748 	else if (width == 1152)
749 		regRec.CR50 |= 0x01;
750 	else if (width == 1280)
751 		regRec.CR50 |= 0xc0;
752 	else if (width == 1600)
753 		regRec.CR50 |= 0x81;
754 	else
755 		regRec.CR50 |= 0xc1;	// use GBD
756 
757 	if (S3_SAVAGE_MOBILE_SERIES(si.chipType))
758 		regRec.CR33 = 0x00;
759 	else
760 		regRec.CR33 = 0x08;
761 
762 	regRec.CR69 = 0;
763 	regRec.CR86 = ReadCrtcReg(0x86) | 0x08;
764 	regRec.CR88 = ReadCrtcReg(0x88) | DISABLE_BLOCK_WRITE_2D;
765 	regRec.CRB0 = ReadCrtcReg(0xb0) | 0x80;
766 
767 	Savage_WriteMode(mode, regRec);		// write registers to set mode
768 
769 	return true;
770 }
771 
772 
773 
774 bool
775 Savage_SetDisplayMode(const DisplayModeEx& mode)
776 {
777 	// The code to actually configure the display.
778 	// All the error checking must be done in ProposeDisplayMode(),
779 	// and assume that the mode values we get here are acceptable.
780 
781 	WriteSeqReg(0x01, 0x20, 0x20);		// blank the screen
782 
783 	if ( ! Savage_ModeInit(mode)) {
784 		TRACE("Savage_ModeInit() failed\n");
785 		return false;
786 	}
787 
788 	Savage_AdjustFrame(mode);
789 
790 	WriteSeqReg(0x01, 0x00, 0x20);		// unblank the screen
791 
792 	return true;
793 }
794 
795 
796 
797 void
798 Savage_AdjustFrame(const DisplayModeEx& mode)
799 {
800 	// Adjust start address in frame buffer.
801 
802 	SharedInfo& si = *gInfo.sharedInfo;
803 
804 	int address = (mode.v_display_start * mode.virtual_width)
805 			+ ((mode.h_display_start & ~0x3F) * (mode.bpp / 8));
806 	address &= ~0x1F;
807 	address += si.frameBufferOffset;
808 
809 	switch (si.chipType) {
810 		case S3_SAVAGE_MX:
811 			WriteReg32(PRI_STREAM_FBUF_ADDR0, address & 0xFFFFFFFC);
812 			WriteReg32(PRI_STREAM_FBUF_ADDR1, address & 0xFFFFFFFC);
813 			break;
814 		case S3_SUPERSAVAGE:
815 			WriteReg32(PRI_STREAM_FBUF_ADDR0, 0x80000000);
816 			WriteReg32(PRI_STREAM_FBUF_ADDR1, address & 0xFFFFFFF8);
817 			break;
818 		case S3_SAVAGE2000:
819 			// Certain Y values seems to cause havoc, not sure why
820 			WriteReg32(PRI_STREAM_FBUF_ADDR0, (address & 0xFFFFFFF8));
821 			WriteReg32(PRI_STREAM2_FBUF_ADDR0, (address & 0xFFFFFFF8));
822 			break;
823 		default:
824 			WriteReg32(PRI_STREAM_FBUF_ADDR0, address | 0xFFFFFFFC);
825 			WriteReg32(PRI_STREAM_FBUF_ADDR1, address | 0x80000000);
826 			break;
827 	}
828 
829 	return;
830 }
831 
832 
833 void
834 Savage_SetIndexedColors(uint count, uint8 first, uint8* colorData, uint32 flags)
835 {
836 	// Set the indexed color palette for 8-bit color depth mode.
837 
838 	(void)flags;		// avoid compiler warning for unused arg
839 
840 	if (gInfo.sharedInfo->displayMode.space != B_CMAP8)
841 		return ;
842 
843 	while (count--) {
844 		WriteIndexedColor(first++,	// color index
845 			colorData[0],			// red
846 			colorData[1],			// green
847 			colorData[2]);			// blue
848 		colorData += 3;
849 	}
850 }
851