vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / accelerants / 3dfx / 3dfx_mode.cpp
blob33637b9063cc7f4d121d84723ce127fd07d3e53f
1 /*
2 Copyright 2010 Haiku, Inc. All rights reserved.
3 Distributed under the terms of the MIT license.
5 Authors:
6 Gerald Zajac
7 */
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 --------------------------------------------------------------------------
14 Copyright 1998-1999 Precision Insight, Inc., Cedar Park, Texas.
15 All Rights Reserved.
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:
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.
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.
38 #include "accelerant.h"
39 #include "3dfx.h"
41 #include <stdlib.h>
42 #include <unistd.h>
45 // Functions to read/write PIO registers.
46 //=======================================
48 static void
49 WritePIOReg(uint32 offset, int16 index, uint8 value)
51 PIORegInfo prInfo;
52 prInfo.magic = TDFX_PRIVATE_DATA_MAGIC;
53 prInfo.offset = offset;
54 prInfo.index = index;
55 prInfo.value = value;
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);
64 static uint8
65 ReadPIOReg(uint32 offset, int16 index)
67 PIORegInfo prInfo;
68 prInfo.magic = TDFX_PRIVATE_DATA_MAGIC;
69 prInfo.offset = offset;
70 prInfo.index = index;
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);
77 return prInfo.value;
81 static void
82 WriteMiscOutReg(uint8 value)
84 WritePIOReg(MISC_OUT_W - 0x300, -1, value);
88 static void
89 WriteCrtcReg(uint8 index, uint8 value)
91 WritePIOReg(CRTC_INDEX - 0x300, index, value);
95 uint8
96 ReadMiscOutReg()
98 return ReadPIOReg(MISC_OUT_R - 0x300, -1);
102 uint8
103 ReadCrtcReg(uint8 index)
105 return ReadPIOReg(CRTC_INDEX - 0x300, index);
109 void
110 TDFX_WaitForFifo(uint32 entries)
112 // The FIFO has 32 slots. This routine waits until at least `entries'
113 // of these slots are empty.
115 while ((INREG32(STATUS) & 0x1f) < entries) ;
119 void
120 TDFX_WaitForIdle()
122 // Wait for the graphics engine to be completely idle.
124 TDFX_WaitForFifo(1);
125 OUTREG32(CMD_3D, CMD_3D_NOP);
127 int i = 0;
129 do {
130 i = (INREG32(STATUS) & STATUS_BUSY) ? 0 : i + 1;
131 } while (i < 3);
135 static int
136 TDFX_CalcPLL(int freq)
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;
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;
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;
169 return (best_n << 8) | (best_m << 2) | best_k;
173 bool
174 TDFX_GetColorSpaceParams(int colorSpace, uint8& bitsPerPixel)
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.
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;
197 return true;
201 status_t
202 TDFX_SetDisplayMode(const DisplayModeEx& mode)
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.
208 SharedInfo& si = *gInfo.sharedInfo;
209 bool clock2X = mode.timing.pixel_clock > si.maxPixelClock / 2;
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.
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.
220 int horzDiv = clock2X ? 16 : 8;
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
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
236 // CRTC Controller values
238 uint8 crtc[25];
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;
273 // Set up the extra CR reg's to handle the higher resolution modes.
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;
282 uint8 cr1b = (vTotal & 0x400) >> 10
283 | (vDisp_e & 0x400) >> 8
284 | (vBlank_s & 0x400) >> 6
285 | (vBlank_e & 0x400) >> 4;
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);
291 uint32 vgaInit0 = VGA0_EXTENSIONS
292 | WAKEUP_3C3
293 | ENABLE_ALT_READBACK
294 | CLUT_SELECT_8BIT
295 | EXT_SHIFT_OUT;
297 uint32 videoConfig = VIDEO_PROCESSOR_ENABLE | DESKTOP_ENABLE
298 | (mode.bytesPerPixel - 1) << DESKTOP_PIXEL_FORMAT_SHIFT
299 | (mode.bytesPerPixel > 1 ? DESKTOP_CLUT_BYPASS : 0);
301 uint32 dacMode = INREG32(DAC_MODE) & ~DAC_MODE_2X;
303 if (clock2X) {
304 dacMode |= DAC_MODE_2X;
305 videoConfig |= VIDEO_2X_MODE_ENABLE;
308 uint32 pllFreq = TDFX_CalcPLL(mode.timing.pixel_clock);
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.
314 if (si.chipType == BANSHEE && pllFreq == 45831
315 && mode.timing.h_display == 1280 && mode.timing.v_display == 1024)
316 pllFreq = 45912;
318 uint32 screenSize = mode.timing.h_display | (mode.timing.v_display << 12);
320 // Now that the values for the registers have been computed, write the
321 // registers to set the mode.
322 //=====================================================================
324 TDFX_WaitForFifo(2);
325 OUTREG32(VIDEO_PROC_CONFIG, 0);
326 OUTREG32(PLL_CTRL0, pllFreq);
328 WriteMiscOutReg(miscOutReg);
330 for (uint8 j = 0; j < 25; j++)
331 WriteCrtcReg(j, crtc[j]);
333 WriteCrtcReg(0x1a, cr1a);
334 WriteCrtcReg(0x1b, cr1b);
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);
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);
353 TDFX_WaitForIdle();
355 TDFX_AdjustFrame(mode);
357 return B_OK;
361 void
362 TDFX_AdjustFrame(const DisplayModeEx& mode)
364 // Adjust start address in frame buffer.
366 SharedInfo& si = *gInfo.sharedInfo;
368 int address = (mode.v_display_start * mode.virtual_width
369 + mode.h_display_start) * mode.bytesPerPixel;
371 address &= ~0x07;
372 address += si.frameBufferOffset;
374 TDFX_WaitForFifo(1);
375 OUTREG32(VIDEO_DESKTOP_START_ADDR, address);
376 return;
380 void
381 TDFX_SetIndexedColors(uint count, uint8 first, uint8* colorData, uint32 flags)
383 // Set the indexed color palette for 8-bit color depth mode.
385 (void)flags; // avoid compiler warning for unused arg
387 if (gInfo.sharedInfo->displayMode.space != B_CMAP8)
388 return ;
390 uint32 index = first;
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);
399 colorData += 3;