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