xref: /haiku/src/add-ons/accelerants/3dfx/3dfx_mode.cpp (revision fd9fa13896676181d9ce408f3843e96f26c592c6)
1 /*
2 	Copyright 2010 Haiku, Inc.  All rights reserved.
3 	Distributed under the terms of the MIT license.
4 
5 	Authors:
6 	Gerald Zajac
7 */
8 
9 /*
10 	Some of the code in this source file was adapted from the X.org tdfx
11 	video driver, and was covered by the following copyright and license.
12 	--------------------------------------------------------------------------
13 
14 	Copyright 1998-1999 Precision Insight, Inc., Cedar Park, Texas.
15 	All Rights Reserved.
16 
17 	Permission is hereby granted, free of charge, to any person obtaining a
18 	copy of this software and associated documentation files (the
19 	"Software"), to deal in the Software without restriction, including
20 	without limitation the rights to use, copy, modify, merge, publish,
21 	distribute, sub license, and/or sell copies of the Software, and to
22 	permit persons to whom the Software is furnished to do so, subject to
23 	the following conditions:
24 
25 	The above copyright notice and this permission notice (including the
26 	next paragraph) shall be included in all copies or substantial portions
27 	of the Software.
28 
29 	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
30 	OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31 	MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
32 	IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
33 	ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
34 	TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
35 	SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 */
37 
38 #include "accelerant.h"
39 #include "3dfx.h"
40 
41 #include <stdlib.h>
42 #include <unistd.h>
43 
44 
45 // Functions to read/write PIO registers.
46 //=======================================
47 
48 static void
WritePIOReg(uint32 offset,int16 index,uint8 value)49 WritePIOReg(uint32 offset, int16 index, uint8 value)
50 {
51 	PIORegInfo prInfo;
52 	prInfo.magic = TDFX_PRIVATE_DATA_MAGIC;
53 	prInfo.offset = offset;
54 	prInfo.index = index;
55 	prInfo.value = value;
56 
57 	status_t result = ioctl(gInfo.deviceFileDesc, TDFX_SET_PIO_REG,
58 		&prInfo, sizeof(prInfo));
59 	if (result != B_OK)
60 		TRACE("WritePIOReg() failed, result = 0x%x\n", result);
61 }
62 
63 
64 static uint8
ReadPIOReg(uint32 offset,int16 index)65 ReadPIOReg(uint32 offset, int16 index)
66 {
67 	PIORegInfo prInfo;
68 	prInfo.magic = TDFX_PRIVATE_DATA_MAGIC;
69 	prInfo.offset = offset;
70 	prInfo.index = index;
71 
72 	status_t result = ioctl(gInfo.deviceFileDesc, TDFX_GET_PIO_REG,
73 		&prInfo, sizeof(prInfo));
74 	if (result != B_OK)
75 		TRACE("ReadPIOReg() failed, result = 0x%x\n", result);
76 
77 	return prInfo.value;
78 }
79 
80 
81 static void
WriteMiscOutReg(uint8 value)82 WriteMiscOutReg(uint8 value)
83 {
84 	WritePIOReg(MISC_OUT_W - 0x300, -1, value);
85 }
86 
87 
88 static void
WriteCrtcReg(uint8 index,uint8 value)89 WriteCrtcReg(uint8 index, uint8 value)
90 {
91 	WritePIOReg(CRTC_INDEX - 0x300, index, value);
92 }
93 
94 
95 uint8
ReadMiscOutReg()96 ReadMiscOutReg()
97 {
98 	return ReadPIOReg(MISC_OUT_R - 0x300, -1);
99 }
100 
101 
102 uint8
ReadCrtcReg(uint8 index)103 ReadCrtcReg(uint8 index)
104 {
105 	return ReadPIOReg(CRTC_INDEX - 0x300, index);
106 }
107 
108 
109 void
TDFX_WaitForFifo(uint32 entries)110 TDFX_WaitForFifo(uint32 entries)
111 {
112 	// The FIFO has 32 slots.  This routine waits until at least `entries'
113 	// of these slots are empty.
114 
115 	while ((INREG32(STATUS) & 0x1f) < entries) ;
116 }
117 
118 
119 void
TDFX_WaitForIdle()120 TDFX_WaitForIdle()
121 {
122 	// Wait for the graphics engine to be completely idle.
123 
124 	TDFX_WaitForFifo(1);
125 	OUTREG32(CMD_3D, CMD_3D_NOP);
126 
127 	int i = 0;
128 
129 	do {
130 		i = (INREG32(STATUS) & STATUS_BUSY) ? 0 : i + 1;
131 	} while (i < 3);
132 }
133 
134 
135 static int
TDFX_CalcPLL(int freq)136 TDFX_CalcPLL(int freq)
137 {
138 	const int refFreq = 14318;
139 	int best_error = freq;
140 	int best_k = 0;
141 	int best_m = 0;
142 	int best_n = 0;
143 
144 	for (int n = 1; n < 256; n++) {
145 		int freqCur = refFreq * (n + 2);
146 		if (freqCur < freq) {
147 			freqCur = freqCur / 3;
148 			if (freq - freqCur < best_error) {
149 				best_error = freq - freqCur;
150 				best_n = n;
151 				best_m = 1;
152 				best_k = 0;
153 				continue;
154 			}
155 		}
156 		for (int m = 1; m < 64; m++) {
157 			for (int k = 0; k < 4; k++) {
158 				freqCur = refFreq * (n + 2) / (m + 2) / (1 << k);
159 				if (abs(freqCur - freq) < best_error) {
160 					best_error = abs(freqCur - freq);
161 					best_n = n;
162 					best_m = m;
163 					best_k = k;
164 				}
165 			}
166 		}
167 	}
168 
169 	return (best_n << 8) | (best_m << 2) | best_k;
170 }
171 
172 
173 bool
TDFX_GetColorSpaceParams(int colorSpace,uint8 & bitsPerPixel)174 TDFX_GetColorSpaceParams(int colorSpace, uint8& bitsPerPixel)
175 {
176 	// Get parameters for a color space which is supported by the 3dfx chips.
177 	// Return true if the color space is supported;  else return false.
178 
179 	switch (colorSpace) {
180 		case B_RGB32:
181 			bitsPerPixel = 32;
182 			break;
183 		case B_RGB16:
184 			bitsPerPixel = 16;
185 			break;
186 		case B_RGB15:
187 			bitsPerPixel = 15;
188 			break;
189 		case B_CMAP8:
190 			bitsPerPixel = 8;
191 			break;
192 		default:
193 			TRACE("Unsupported color space: 0x%X\n", colorSpace);
194 			return false;
195 	}
196 
197 	return true;
198 }
199 
200 
201 status_t
TDFX_SetDisplayMode(const DisplayModeEx & mode)202 TDFX_SetDisplayMode(const DisplayModeEx& mode)
203 {
204 	// The code to actually configure the display.
205 	// All the error checking must be done in ProposeDisplayMode(),
206 	// and assume that the mode values we get here are acceptable.
207 
208 	SharedInfo& si = *gInfo.sharedInfo;
209 	bool clock2X = mode.timing.pixel_clock > si.maxPixelClock / 2;
210 
211 	// Initialize the timing values for CRTC registers cr00 to cr18.  Note
212 	// that the number following the letters 'cr' is a hexadecimal number.
213 	// Argument crtc will contain registers cr00 to cr18;  thus, it must
214 	// contain at least 25 (0x19) elements.
215 
216 	// Normally the horizontal timing values are divided by 8;  however,
217 	// if the clock is above the 2X value, divide by 16 such that the values
218 	// are halved.
219 
220 	int horzDiv = clock2X ? 16 : 8;
221 
222 	int hTotal = mode.timing.h_total / horzDiv - 5;
223 	int hDisp_e = mode.timing.h_display / horzDiv - 1;
224 	int hSync_s = mode.timing.h_sync_start / horzDiv;
225 	int hSync_e = mode.timing.h_sync_end / horzDiv;
226 	int hBlank_s = hDisp_e + 1;		// start of horizontal blanking
227 	int hBlank_e = hTotal + 3;		// end of horizontal blanking
228 
229 	int vTotal = mode.timing.v_total - 2;
230 	int vDisp_e = mode.timing.v_display - 1;
231 	int vSync_s = mode.timing.v_sync_start;
232 	int vSync_e = mode.timing.v_sync_end;
233 	int vBlank_s = vDisp_e + 1;		// start of vertical blanking
234 	int vBlank_e = vTotal;			// end of vertical blanking
235 
236 	// CRTC Controller values
237 
238 	uint8 crtc[25];
239 
240 	crtc[0x00] = hTotal;
241 	crtc[0x01] = hDisp_e;
242 	crtc[0x02] = hBlank_s;
243 	crtc[0x03] = (hBlank_e & 0x1f) | 0x80;
244 	crtc[0x04] = hSync_s;
245 	crtc[0x05] = ((hSync_e & 0x1f) | ((hBlank_e & 0x20) << 2));
246 	crtc[0x06] = vTotal;
247 	crtc[0x07] = (((vTotal & 0x100) >> 8)
248 		| ((vDisp_e & 0x100) >> 7)
249 		| ((vSync_s & 0x100) >> 6)
250 		| ((vBlank_s & 0x100) >> 5)
251 		| 0x10
252 		| ((vTotal & 0x200) >> 4)
253 		| ((vDisp_e & 0x200) >> 3)
254 		| ((vSync_s & 0x200) >> 2));
255 	crtc[0x08] = 0x00;
256 	crtc[0x09] = ((vBlank_s & 0x200) >> 4) | 0x40;
257 	crtc[0x0a] = 0x00;
258 	crtc[0x0b] = 0x00;
259 	crtc[0x0c] = 0x00;
260 	crtc[0x0d] = 0x00;
261 	crtc[0x0e] = 0x00;
262 	crtc[0x0f] = 0x00;
263 	crtc[0x10] = vSync_s;
264 	crtc[0x11] = (vSync_e & 0x0f) | 0x20;
265 	crtc[0x12] = vDisp_e;
266 	crtc[0x13] = hDisp_e + 1;
267 	crtc[0x14] = 0x00;
268 	crtc[0x15] = vBlank_s;
269 	crtc[0x16] = vBlank_e;
270 	crtc[0x17] = 0xc3;
271 	crtc[0x18] = 0xff;
272 
273 	// Set up the extra CR reg's to handle the higher resolution modes.
274 
275 	uint8 cr1a = (hTotal & 0x100) >> 8
276 				| (hDisp_e & 0x100) >> 6
277 				| (hBlank_s & 0x100) >> 4
278 				| (hBlank_e & 0x40) >> 1
279 				| (hSync_s & 0x100) >> 2
280 				| (hSync_e & 0x20) << 2;
281 
282 	uint8 cr1b = (vTotal & 0x400) >> 10
283 				| (vDisp_e & 0x400) >> 8
284 				| (vBlank_s & 0x400) >> 6
285 				| (vBlank_e & 0x400) >> 4;
286 
287 	uint8 miscOutReg = 0x0f | (mode.timing.v_display < 400 ? 0xa0
288 							: mode.timing.v_display < 480 ? 0x60
289 							: mode.timing.v_display < 768 ? 0xe0 : 0x20);
290 
291 	uint32 vgaInit0 = VGA0_EXTENSIONS
292 					| WAKEUP_3C3
293 					| ENABLE_ALT_READBACK
294 					| CLUT_SELECT_8BIT
295 					| EXT_SHIFT_OUT;
296 
297 	uint32 videoConfig = VIDEO_PROCESSOR_ENABLE | DESKTOP_ENABLE
298 					| (mode.bytesPerPixel - 1) << DESKTOP_PIXEL_FORMAT_SHIFT
299 					| (mode.bytesPerPixel > 1 ? DESKTOP_CLUT_BYPASS : 0);
300 
301 	uint32 dacMode = INREG32(DAC_MODE) & ~DAC_MODE_2X;
302 
303 	if (clock2X) {
304 		dacMode |= DAC_MODE_2X;
305 		videoConfig |= VIDEO_2X_MODE_ENABLE;
306 	}
307 
308 	uint32 pllFreq = TDFX_CalcPLL(mode.timing.pixel_clock);
309 
310 	// Note that for the Banshee chip, the mode 1280x1024 at 60Hz refresh does
311 	// not display properly using the computed PLL frequency;  thus, set it to
312 	// the value that is computed when set by VESA.
313 
314 	if (si.chipType == BANSHEE && pllFreq == 45831
315 			&& mode.timing.h_display == 1280 && mode.timing.v_display == 1024)
316 		pllFreq = 45912;
317 
318 	uint32 screenSize = mode.timing.h_display | (mode.timing.v_display << 12);
319 
320 	// Now that the values for the registers have been computed, write the
321 	// registers to set the mode.
322 	//=====================================================================
323 
324 	TDFX_WaitForFifo(2);
325 	OUTREG32(VIDEO_PROC_CONFIG, 0);
326 	OUTREG32(PLL_CTRL0, pllFreq);
327 
328 	WriteMiscOutReg(miscOutReg);
329 
330 	for (uint8 j = 0; j < 25; j++)
331 		WriteCrtcReg(j, crtc[j]);
332 
333 	WriteCrtcReg(0x1a, cr1a);
334 	WriteCrtcReg(0x1b, cr1b);
335 
336 	TDFX_WaitForFifo(6);
337 	OUTREG32(VGA_INIT0, vgaInit0);
338 	OUTREG32(DAC_MODE, dacMode);
339 	OUTREG32(VIDEO_DESKTOP_OVERLAY_STRIDE, mode.bytesPerRow);
340 	OUTREG32(HW_CURSOR_PAT_ADDR, si.cursorOffset);
341 	OUTREG32(VIDEO_SCREEN_SIZE, screenSize);
342 	OUTREG32(VIDEO_DESKTOP_START_ADDR, si.frameBufferOffset);
343 
344 	TDFX_WaitForFifo(7);
345 	OUTREG32(CLIP0_MIN, 0);
346 	OUTREG32(CLIP0_MAX, 0x0fff0fff);
347 	OUTREG32(CLIP1_MIN, 0);
348 	OUTREG32(CLIP1_MAX, 0x0fff0fff);
349 	OUTREG32(VIDEO_PROC_CONFIG, videoConfig);
350 	OUTREG32(SRC_BASE_ADDR, si.frameBufferOffset);
351 	OUTREG32(DST_BASE_ADDR, si.frameBufferOffset);
352 
353 	TDFX_WaitForIdle();
354 
355 	TDFX_AdjustFrame(mode);
356 
357 	return B_OK;
358 }
359 
360 
361 void
TDFX_AdjustFrame(const DisplayModeEx & mode)362 TDFX_AdjustFrame(const DisplayModeEx& mode)
363 {
364 	// Adjust start address in frame buffer.
365 
366 	SharedInfo& si = *gInfo.sharedInfo;
367 
368 	int address = (mode.v_display_start * mode.virtual_width
369 			+ mode.h_display_start) * mode.bytesPerPixel;
370 
371 	address &= ~0x07;
372 	address += si.frameBufferOffset;
373 
374 	TDFX_WaitForFifo(1);
375 	OUTREG32(VIDEO_DESKTOP_START_ADDR, address);
376 	return;
377 }
378 
379 
380 void
TDFX_SetIndexedColors(uint count,uint8 first,uint8 * colorData,uint32 flags)381 TDFX_SetIndexedColors(uint count, uint8 first, uint8* colorData, uint32 flags)
382 {
383 	// Set the indexed color palette for 8-bit color depth mode.
384 
385 	(void)flags;		// avoid compiler warning for unused arg
386 
387 	if (gInfo.sharedInfo->displayMode.space != B_CMAP8)
388 		return ;
389 
390 	uint32 index = first;
391 
392 	while (count--) {
393 		uint32 color = ((colorData[0] << 16) | (colorData[1] << 8) | colorData[2]);
394 		TDFX_WaitForFifo(2);
395 		OUTREG32(DAC_ADDR, index++);
396 		INREG32(DAC_ADDR);		// color not always set unless we read after write
397 		OUTREG32(DAC_DATA, color);
398 
399 		colorData += 3;
400 	}
401 }
402