1 /* 2 * Copyright 2007-2010 Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT license. 4 5 * Authors: 6 * Gerald Zajac 7 */ 8 9 #include "accelerant.h" 10 11 #include <create_display_modes.h> // common accelerant header file 12 13 #include <string.h> 14 #include <unistd.h> 15 16 17 static bool 18 IsThereEnoughFBMemory(const display_mode* mode, uint32 bitsPerPixel) 19 { 20 // Test if there is enough Frame Buffer memory for the mode and color depth 21 // specified by the caller, and return true if there is sufficient memory. 22 23 uint32 maxWidth = mode->virtual_width; 24 if (mode->timing.h_display > maxWidth) 25 maxWidth = mode->timing.h_display; 26 27 uint32 maxHeight = mode->virtual_height; 28 if (mode->timing.v_display > maxHeight) 29 maxHeight = mode->timing.v_display; 30 31 uint32 bytesPerPixel = (bitsPerPixel + 7) / 8; 32 33 return (maxWidth * maxHeight * bytesPerPixel 34 < gInfo.sharedInfo->maxFrameBufferSize); 35 } 36 37 38 bool 39 IsModeUsable(const display_mode* mode) 40 { 41 // Test if the display mode is usable by the current video chip. That is, 42 // does the chip have enough memory for the mode and is the pixel clock 43 // within the chips allowable range, etc. 44 // 45 // Return true if the mode is usable. 46 47 SharedInfo& si = *gInfo.sharedInfo; 48 uint8 bitsPerPixel; 49 50 if (!TDFX_GetColorSpaceParams(mode->space, bitsPerPixel)) 51 return false; 52 53 // Is there enough frame buffer memory to handle the mode? 54 55 if (!IsThereEnoughFBMemory(mode, bitsPerPixel)) 56 return false; 57 58 if (mode->timing.pixel_clock > si.maxPixelClock) 59 return false; 60 61 // Is the color space supported? 62 63 bool colorSpaceSupported = false; 64 for (uint32 j = 0; j < si.colorSpaceCount; j++) { 65 if (mode->space == uint32(si.colorSpaces[j])) { 66 colorSpaceSupported = true; 67 break; 68 } 69 } 70 71 if (!colorSpaceSupported) 72 return false; 73 74 // In clock doubled mode for 3DFX chips, widths must be divisible by 16 75 // instead of 8. 76 77 if ((mode->timing.pixel_clock > si.maxPixelClock / 2) 78 && (mode->timing.h_display % 16) != 0) 79 return false; 80 81 // Reject modes with a width of 640 and a height < 480 since they do not 82 // work properly with the 3DFX chipsets. 83 84 if (mode->timing.h_display == 640 && mode->timing.v_display < 480) 85 return false; 86 87 return true; 88 } 89 90 91 status_t 92 CreateModeList(bool (*checkMode)(const display_mode* mode)) 93 { 94 SharedInfo& si = *gInfo.sharedInfo; 95 96 // Obtain EDID info which is needed for for building the mode list. If 97 // function TDFX_GetEdidInfo() fails, no attempt is made to read the EDID 98 // info from the video BIOS since that always failed when attempted during 99 // the development of this driver. 100 101 si.bHaveEDID = TDFX_GetEdidInfo(si.edidInfo); 102 103 if (si.bHaveEDID) { 104 #ifdef ENABLE_DEBUG_TRACE 105 edid_dump(&(si.edidInfo)); 106 #endif 107 } else { 108 TRACE("CreateModeList(); Unable to get EDID info\n"); 109 } 110 111 display_mode* list; 112 uint32 count = 0; 113 area_id listArea; 114 115 listArea = create_display_modes("3DFX modes", 116 si.bHaveEDID ? &si.edidInfo : NULL, 117 NULL, 0, si.colorSpaces, si.colorSpaceCount, 118 (check_display_mode_hook)checkMode, &list, &count); 119 120 if (listArea < 0) 121 return listArea; // listArea has error code 122 123 si.modeArea = gInfo.modeListArea = listArea; 124 si.modeCount = count; 125 gInfo.modeList = list; 126 127 return B_OK; 128 } 129 130 131 status_t 132 ProposeDisplayMode(display_mode* target, const display_mode* low, 133 const display_mode* high) 134 { 135 (void)low; // avoid compiler warning for unused arg 136 (void)high; // avoid compiler warning for unused arg 137 138 SharedInfo& si = *gInfo.sharedInfo; 139 140 TRACE("ProposeDisplayMode() %dx%d, pixel clock: %d kHz, space: 0x%X\n", 141 target->timing.h_display, target->timing.v_display, 142 target->timing.pixel_clock, target->space); 143 144 // In clock doubled mode for 3DFX chips, widths must be divisible by 16 145 // instead of 8. 146 147 if ((target->timing.pixel_clock > si.maxPixelClock / 2) 148 && (target->timing.h_display % 16) != 0) 149 return B_BAD_VALUE; 150 151 // Search the mode list for the specified mode. 152 153 uint32 modeCount = si.modeCount; 154 155 for (uint32 j = 0; j < modeCount; j++) { 156 display_mode& mode = gInfo.modeList[j]; 157 158 if (target->timing.h_display == mode.timing.h_display 159 && target->timing.v_display == mode.timing.v_display 160 && target->space == mode.space) 161 return B_OK; // mode found in list 162 } 163 164 return B_BAD_VALUE; // mode not found in list 165 } 166 167 168 status_t 169 SetDisplayMode(display_mode* pMode) 170 { 171 // First validate the mode, then call a function to set the registers. 172 173 TRACE("SetDisplayMode() begin\n"); 174 175 SharedInfo& si = *gInfo.sharedInfo; 176 DisplayModeEx mode; 177 (display_mode&)mode = *pMode; 178 179 if (!TDFX_GetColorSpaceParams(mode.space, mode.bitsPerPixel)) 180 return B_BAD_VALUE; 181 182 if (ProposeDisplayMode(&mode, pMode, pMode) != B_OK) 183 return B_BAD_VALUE; 184 185 mode.bytesPerPixel = (mode.bitsPerPixel + 7) / 8; 186 mode.bytesPerRow = mode.timing.h_display * mode.bytesPerPixel; 187 188 // Is there enough frame buffer memory for this mode? 189 190 if ( ! IsThereEnoughFBMemory(&mode, mode.bitsPerPixel)) 191 return B_NO_MEMORY; 192 193 TRACE("Set display mode: %dx%d virtual size: %dx%d " 194 "color depth: %d bits/pixel\n", 195 mode.timing.h_display, mode.timing.v_display, 196 mode.virtual_width, mode.virtual_height, mode.bitsPerPixel); 197 198 TRACE(" mode timing: %d %d %d %d %d %d %d %d %d\n", 199 mode.timing.pixel_clock, 200 mode.timing.h_display, 201 mode.timing.h_sync_start, mode.timing.h_sync_end, 202 mode.timing.h_total, 203 mode.timing.v_display, 204 mode.timing.v_sync_start, mode.timing.v_sync_end, 205 mode.timing.v_total); 206 207 TRACE(" mode hFreq: %.1f kHz vFreq: %.1f Hz %chSync %cvSync\n", 208 double(mode.timing.pixel_clock) / mode.timing.h_total, 209 ((double(mode.timing.pixel_clock) / mode.timing.h_total) * 1000.0) 210 / mode.timing.v_total, 211 (mode.timing.flags & B_POSITIVE_HSYNC) ? '+' : '-', 212 (mode.timing.flags & B_POSITIVE_VSYNC) ? '+' : '-'); 213 214 status_t status = TDFX_SetDisplayMode(mode); 215 if (status != B_OK) { 216 TRACE("SetDisplayMode() failed; status 0x%x\n", status); 217 return status; 218 } 219 220 si.displayMode = mode; 221 222 TRACE("SetDisplayMode() done\n"); 223 return B_OK; 224 } 225 226 227 status_t 228 MoveDisplay(uint16 horizontalStart, uint16 verticalStart) 229 { 230 // Set which pixel of the virtual frame buffer will show up in the 231 // top left corner of the display device. Used for page-flipping 232 // games and virtual desktops. 233 234 DisplayModeEx& mode = gInfo.sharedInfo->displayMode; 235 236 if (mode.timing.h_display + horizontalStart > mode.virtual_width 237 || mode.timing.v_display + verticalStart > mode.virtual_height) 238 return B_ERROR; 239 240 mode.h_display_start = horizontalStart; 241 mode.v_display_start = verticalStart; 242 243 TDFX_AdjustFrame(mode); 244 return B_OK; 245 } 246 247 248 uint32 249 AccelerantModeCount(void) 250 { 251 // Return the number of display modes in the mode list. 252 253 return gInfo.sharedInfo->modeCount; 254 } 255 256 257 status_t 258 GetModeList(display_mode* dmList) 259 { 260 // Copy the list of supported video modes to the location pointed at 261 // by dmList. 262 263 memcpy(dmList, gInfo.modeList, 264 gInfo.sharedInfo->modeCount * sizeof(display_mode)); 265 return B_OK; 266 } 267 268 269 status_t 270 GetDisplayMode(display_mode* current_mode) 271 { 272 *current_mode = gInfo.sharedInfo->displayMode; // return current display mode 273 return B_OK; 274 } 275 276 277 status_t 278 GetFrameBufferConfig(frame_buffer_config* pFBC) 279 { 280 SharedInfo& si = *gInfo.sharedInfo; 281 282 pFBC->frame_buffer = (void*)((addr_t)si.videoMemAddr 283 + si.frameBufferOffset); 284 pFBC->frame_buffer_dma = (void*)((addr_t)si.videoMemPCI 285 + si.frameBufferOffset); 286 pFBC->bytes_per_row = si.displayMode.virtual_width 287 * si.displayMode.bytesPerPixel; 288 289 return B_OK; 290 } 291 292 293 status_t 294 GetPixelClockLimits(display_mode* mode, uint32* low, uint32* high) 295 { 296 // Return the maximum and minium pixel clock limits for the specified mode. 297 298 SharedInfo& si = *gInfo.sharedInfo; 299 uint8 bitsPerPixel; 300 301 if (!TDFX_GetColorSpaceParams(mode->space, bitsPerPixel)) 302 return B_ERROR; 303 304 if (low != NULL) { 305 // lower limit of about 48Hz vertical refresh 306 uint32 totalClocks = (uint32)mode->timing.h_total 307 * (uint32)mode->timing.v_total; 308 uint32 lowClock = (totalClocks * 48L) / 1000L; 309 if (lowClock > si.maxPixelClock) 310 return B_ERROR; 311 312 *low = lowClock; 313 } 314 315 if (high != NULL) { 316 // In clock doubled mode for 3DFX chips, widths must be divisible by 16 317 // instead of 8. If width is not divisible by 16, limit pixel clock to 318 // half of max pixel clock since divisor is 8 if pixel clock is < half 319 // max pixel clock. 320 321 if ((mode->timing.h_display % 16) != 0) 322 *high = si.maxPixelClock / 2; 323 else 324 *high = si.maxPixelClock; 325 } 326 327 return B_OK; 328 } 329 330 331 #ifdef __HAIKU__ 332 333 status_t 334 GetEdidInfo(void* info, size_t size, uint32* _version) 335 { 336 SharedInfo& si = *gInfo.sharedInfo; 337 338 if ( ! si.bHaveEDID) 339 return B_ERROR; 340 341 if (size < sizeof(struct edid1_info)) 342 return B_BUFFER_OVERFLOW; 343 344 memcpy(info, &si.edidInfo, sizeof(struct edid1_info)); 345 *_version = EDID_VERSION_1; 346 return B_OK; 347 } 348 349 #endif // __HAIKU__ 350