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 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 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 82 WriteMiscOutReg(uint8 value) 83 { 84 WritePIOReg(MISC_OUT_W - 0x300, -1, value); 85 } 86 87 88 static void 89 WriteCrtcReg(uint8 index, uint8 value) 90 { 91 WritePIOReg(CRTC_INDEX - 0x300, index, value); 92 } 93 94 95 uint8 96 ReadMiscOutReg() 97 { 98 return ReadPIOReg(MISC_OUT_R - 0x300, -1); 99 } 100 101 102 uint8 103 ReadCrtcReg(uint8 index) 104 { 105 return ReadPIOReg(CRTC_INDEX - 0x300, index); 106 } 107 108 109 void 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 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 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 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 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 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 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