1 /* 2 Copyright 2007-2008 Haiku, Inc. All rights reserved. 3 Distributed under the terms of the MIT license. 4 5 Authors: 6 Gerald Zajac 2007-2008 7 */ 8 9 #include "accel.h" 10 11 #include <create_display_modes.h> // common accelerant header file 12 #include <string.h> 13 #include <unistd.h> 14 15 16 void 17 InitCrtcTimingValues(const DisplayModeEx& mode, int horzScaleFactor, uint8 crtc[], 18 uint8& cr3b, uint8& cr3c, uint8& cr5d, uint8& cr5e) 19 { 20 // Initialize the timing values for CRTC registers cr00 to cr18 and cr3a, 21 // cr3b, cr5d, and cr5e. Note that the number following the letters 'cr' 22 // is a hexadecimal number. Argument crtc will contain registers cr00 to 23 // cr18; thus, it must contain at least 25 (0x19) elements. 24 25 // Normally the horizontal timing values are divided by 8; however, some 26 // chips require the horizontal timings to be doubled when the color depth 27 // is 16 bpp. The horizontal scale factor is used for this purpose. 28 29 int hTotal = (mode.timing.h_total * horzScaleFactor) / 8 - 5; 30 int hDisp_e = (mode.timing.h_display * horzScaleFactor) / 8 - 1; 31 int hSync_s = (mode.timing.h_sync_start * horzScaleFactor) / 8; 32 int hSync_e = (mode.timing.h_sync_end * horzScaleFactor) / 8; 33 int hBlank_s = hDisp_e + 1; // start of horizontal blanking 34 int hBlank_e = hTotal; // end of horizontal blanking 35 36 int vTotal = mode.timing.v_total - 2; 37 int vDisp_e = mode.timing.v_display - 1; 38 int vSync_s = mode.timing.v_sync_start; 39 int vSync_e = mode.timing.v_sync_end; 40 int vBlank_s = vDisp_e; // start of vertical blanking 41 int vBlank_e = vTotal; // end of vertical blanking 42 43 // CRTC Controller values 44 45 crtc[0x00] = hTotal; 46 crtc[0x01] = hDisp_e; 47 crtc[0x02] = hBlank_s; 48 crtc[0x03] = (hBlank_e & 0x1f) | 0x80; 49 crtc[0x04] = hSync_s; 50 crtc[0x05] = ((hSync_e & 0x1f) | ((hBlank_e & 0x20) << 2)); 51 crtc[0x06] = vTotal; 52 crtc[0x07] = (((vTotal & 0x100) >> 8) 53 | ((vDisp_e & 0x100) >> 7) 54 | ((vSync_s & 0x100) >> 6) 55 | ((vBlank_s & 0x100) >> 5) 56 | 0x10 57 | ((vTotal & 0x200) >> 4) 58 | ((vDisp_e & 0x200) >> 3) 59 | ((vSync_s & 0x200) >> 2)); 60 61 crtc[0x08] = 0x00; 62 crtc[0x09] = ((vBlank_s & 0x200) >> 4) | 0x40; 63 crtc[0x0a] = 0x00; 64 crtc[0x0b] = 0x00; 65 crtc[0x0c] = 0x00; 66 crtc[0x0d] = 0x00; 67 crtc[0x0e] = 0x00; 68 crtc[0x0f] = 0x00; 69 crtc[0x10] = vSync_s; 70 crtc[0x11] = (vSync_e & 0x0f) | 0x20; 71 crtc[0x12] = vDisp_e; 72 crtc[0x13] = mode.bytesPerRow / 8; 73 crtc[0x14] = 0x00; 74 crtc[0x15] = vBlank_s; 75 crtc[0x16] = vBlank_e; 76 crtc[0x17] = 0xc3; 77 crtc[0x18] = 0xff; 78 79 int i = ((hTotal & 0x100) >> 8) | 80 ((hDisp_e & 0x100) >> 7) | 81 ((hBlank_s & 0x100) >> 6) | 82 ((hSync_s & 0x100) >> 4); 83 84 if (hSync_e - hSync_s > 64) 85 i |= 0x08; // add another 64 DCLKs to blank pulse width 86 87 if (hSync_e - hSync_s > 32) 88 i |= 0x20; // add another 32 DCLKs to hsync pulse width 89 90 int j = (crtc[0] + ((i & 0x01) << 8) + crtc[4] + ((i & 0x10) << 4) + 1) / 2; 91 92 if (j - (crtc[4] + ((i & 0x10) << 4)) < 4) { 93 if (crtc[4] + ((i & 0x10) << 4) + 4 <= crtc[0] + ((i & 0x01) << 8)) 94 j = crtc[4] + ((i & 0x10) << 4) + 4; 95 else 96 j = crtc[0] + ((i & 0x01) << 8) + 1; 97 } 98 99 cr3b = j & 0xff; 100 i |= (j & 0x100) >> 2; 101 cr3c = (crtc[0] + ((i & 0x01) << 8)) / 2; 102 cr5d = i; 103 104 cr5e = ((vTotal & 0x400) >> 10) | 105 ((vDisp_e & 0x400) >> 9) | 106 ((vBlank_s & 0x400) >> 8) | 107 ((vSync_s & 0x400) >> 6) | 0x40; 108 } 109 110 111 static display_mode* 112 FindDisplayMode(int width, int height, int refreshRate, uint32 colorDepth) 113 { 114 // Search the mode list for the mode with specified width, height, 115 // refresh rate, and color depth. If argument colorDepth is zero, this 116 // function will search for a mode satisfying the other 3 arguments, and 117 // if more than one matching mode is found, the one with the greatest color 118 // depth will be selected. 119 // 120 // If successful, return a pointer to the selected display_mode object; 121 // else return NULL. 122 123 display_mode* selectedMode = NULL; 124 uint32 modeCount = gInfo.sharedInfo->modeCount; 125 126 for (uint32 j = 0; j < modeCount; j++) { 127 display_mode& mode = gInfo.modeList[j]; 128 129 if (mode.timing.h_display == width && mode.timing.v_display == height) { 130 int modeRefreshRate = int(((mode.timing.pixel_clock * 1000.0 / 131 mode.timing.h_total) / mode.timing.v_total) + 0.5); 132 if (modeRefreshRate == refreshRate) { 133 if (colorDepth == 0) { 134 if (selectedMode == NULL || mode.space > selectedMode->space) 135 selectedMode = &mode; 136 } else { 137 if (mode.space == colorDepth) 138 return &mode; 139 } 140 } 141 } 142 } 143 144 return selectedMode; 145 } 146 147 148 149 static bool 150 IsThereEnoughFBMemory(const display_mode* mode, uint32 bitsPerPixel) 151 { 152 // Test if there is enough Frame Buffer memory for the mode and color depth 153 // specified by the caller, and return true if there is sufficient memory. 154 155 uint32 maxWidth = mode->virtual_width; 156 if (mode->timing.h_display > maxWidth) 157 maxWidth = mode->timing.h_display; 158 159 uint32 maxHeight = mode->virtual_height; 160 if (mode->timing.v_display > maxHeight) 161 maxHeight = mode->timing.v_display; 162 163 uint32 bytesPerPixel = (bitsPerPixel + 7) / 8; 164 165 return (maxWidth * maxHeight * bytesPerPixel < gInfo.sharedInfo->maxFrameBufferSize); 166 } 167 168 169 170 bool 171 IsModeUsable(const display_mode* mode) 172 { 173 // Test if the display mode is usable by the current video chip. That is, 174 // does the chip have enough memory for the mode and is the pixel clock 175 // within the chips allowable range, etc. 176 // 177 // Return true if the mode is usable. 178 179 SharedInfo& si = *gInfo.sharedInfo; 180 uint32 bitsPerPixel; 181 uint32 maxPixelClock; 182 183 if ( ! gInfo.GetColorSpaceParams(mode->space, bitsPerPixel, maxPixelClock)) 184 return false; 185 186 // Is there enough frame buffer memory to handle the mode? 187 188 if ( ! IsThereEnoughFBMemory(mode, bitsPerPixel)) 189 return false; 190 191 if (mode->timing.pixel_clock > maxPixelClock) 192 return false; 193 194 // Is the color space supported? 195 196 bool bColorSpaceSupported = false; 197 for (uint32 j = 0; j < si.colorSpaceCount; j++) { 198 if (mode->space == uint32(si.colorSpaces[j])) { 199 bColorSpaceSupported = true; 200 break; 201 } 202 } 203 204 if ( ! bColorSpaceSupported) 205 return false; 206 207 // Reject modes with a width of 640 and a height < 480 since they do not 208 // work properly with the S3 chipsets. 209 210 if (mode->timing.h_display == 640 && mode->timing.v_display < 480) 211 return false; 212 213 // If the video chip is connected directly to an LCD display (ie, laptop 214 // computer), restrict the display mode to resolutions where the width and 215 // height of the mode are less than or equal to the width and height of the 216 // LCD display. 217 218 if (si.displayType == MT_LCD && si.panelX > 0 && si.panelY > 0 219 && (mode->timing.h_display > si.panelX 220 || mode->timing.v_display > si.panelY)) { 221 return false; 222 } 223 224 return true; 225 } 226 227 228 status_t 229 CreateModeList( bool (*checkMode)(const display_mode* mode), 230 bool (*getEdid)(edid1_info& edidInfo)) 231 { 232 SharedInfo& si = *gInfo.sharedInfo; 233 234 // Obtain EDID info which is needed for for building the mode list. 235 // However, if a laptop's LCD display is active, bypass getting the EDID 236 // info since it is not needed, and if the external display supports only 237 // resolutions smaller than the size of the laptop LCD display, it would 238 // unnecessarily restrict size of the resolutions that could be set for 239 // laptop LCD display. 240 241 si.bHaveEDID = false; 242 243 if (si.displayType != MT_LCD) { 244 if (getEdid != NULL) 245 si.bHaveEDID = getEdid(si.edidInfo); 246 247 if ( ! si.bHaveEDID) { 248 S3GetEDID ged; 249 ged.magic = S3_PRIVATE_DATA_MAGIC; 250 251 if (ioctl(gInfo.deviceFileDesc, S3_GET_EDID, &ged, sizeof(ged)) == B_OK) { 252 if (ged.rawEdid.version.version != 1 253 || ged.rawEdid.version.revision > 4) { 254 TRACE("CreateModeList(); EDID version %d.%d out of range\n", 255 ged.rawEdid.version.version, ged.rawEdid.version.revision); 256 } else { 257 edid_decode(&si.edidInfo, &ged.rawEdid); // decode & save EDID info 258 si.bHaveEDID = true; 259 } 260 } 261 } 262 263 if (si.bHaveEDID) { 264 #ifdef ENABLE_DEBUG_TRACE 265 edid_dump(&(si.edidInfo)); 266 #endif 267 } else { 268 TRACE("CreateModeList(); Unable to get EDID info\n"); 269 } 270 } 271 272 display_mode* list; 273 uint32 count = 0; 274 area_id listArea; 275 276 listArea = create_display_modes("S3 modes", 277 si.bHaveEDID ? &si.edidInfo : NULL, 278 NULL, 0, si.colorSpaces, si.colorSpaceCount, 279 (check_display_mode_hook)checkMode, &list, &count); 280 281 if (listArea < 0) 282 return listArea; // listArea has error code 283 284 si.modeArea = gInfo.modeListArea = listArea; 285 si.modeCount = count; 286 gInfo.modeList = list; 287 288 return B_OK; 289 } 290 291 292 293 status_t 294 ProposeDisplayMode(display_mode *target, const display_mode *low, 295 const display_mode *high) 296 { 297 (void)low; // avoid compiler warning for unused arg 298 (void)high; // avoid compiler warning for unused arg 299 300 TRACE("ProposeDisplayMode() %dx%d, pixel clock: %d kHz, space: 0x%X\n", 301 target->timing.h_display, target->timing.v_display, 302 target->timing.pixel_clock, target->space); 303 304 // Search the mode list for the specified mode. 305 306 uint32 modeCount = gInfo.sharedInfo->modeCount; 307 308 for (uint32 j = 0; j < modeCount; j++) { 309 display_mode& mode = gInfo.modeList[j]; 310 311 if (target->timing.h_display == mode.timing.h_display 312 && target->timing.v_display == mode.timing.v_display 313 && target->space == mode.space) 314 return B_OK; // mode found in list 315 } 316 317 return B_BAD_VALUE; // mode not found in list 318 } 319 320 321 322 status_t 323 SetDisplayMode(display_mode* pMode) 324 { 325 // First validate the mode, then call a function to set the registers. 326 327 TRACE("SetDisplayMode() begin\n"); 328 329 SharedInfo& si = *gInfo.sharedInfo; 330 DisplayModeEx mode; 331 (display_mode&)mode = *pMode; 332 333 uint32 maxPixelClock; 334 if ( ! gInfo.GetColorSpaceParams(mode.space, mode.bpp, maxPixelClock)) 335 return B_BAD_VALUE; 336 337 if (ProposeDisplayMode(&mode, pMode, pMode) != B_OK) 338 return B_BAD_VALUE; 339 340 // Note that for some Savage chips, accelerated drawing is badly messed up 341 // when the display width is 1400 because 1400 is not evenly divisible by 32. 342 // For those chips, set the width to 1408 so that accelerated drawing will 343 // draw properly. 344 345 if (mode.timing.h_display == 1400 && (si.chipType == S3_PROSAVAGE 346 || si.chipType == S3_PROSAVAGE_DDR 347 || si.chipType == S3_TWISTER 348 || si.chipType == S3_SUPERSAVAGE 349 || si.chipType == S3_SAVAGE2000)) { 350 mode.timing.h_display = 1408; 351 mode.virtual_width = 1408; 352 } 353 354 int bytesPerPixel = (mode.bpp + 7) / 8; 355 mode.bytesPerRow = mode.timing.h_display * bytesPerPixel; 356 357 // Is there enough frame buffer memory for this mode? 358 359 if ( ! IsThereEnoughFBMemory(&mode, mode.bpp)) 360 return B_NO_MEMORY; 361 362 TRACE("Set display mode: %dx%d virtual size: %dx%d color depth: %d bpp\n", 363 mode.timing.h_display, mode.timing.v_display, 364 mode.virtual_width, mode.virtual_height, mode.bpp); 365 366 TRACE(" mode timing: %d %d %d %d %d %d %d %d %d\n", 367 mode.timing.pixel_clock, 368 mode.timing.h_display, 369 mode.timing.h_sync_start, mode.timing.h_sync_end, 370 mode.timing.h_total, 371 mode.timing.v_display, 372 mode.timing.v_sync_start, mode.timing.v_sync_end, 373 mode.timing.v_total); 374 375 TRACE(" mode hFreq: %.1f kHz vFreq: %.1f Hz %chSync %cvSync\n", 376 double(mode.timing.pixel_clock) / mode.timing.h_total, 377 ((double(mode.timing.pixel_clock) / mode.timing.h_total) * 1000.0) 378 / mode.timing.v_total, 379 (mode.timing.flags & B_POSITIVE_HSYNC) ? '+' : '-', 380 (mode.timing.flags & B_POSITIVE_VSYNC) ? '+' : '-'); 381 382 if ( ! gInfo.SetDisplayMode(mode)) 383 return B_ERROR; 384 385 si.displayMode = mode; 386 387 TRACE("SetDisplayMode() done\n"); 388 return B_OK; 389 } 390 391 392 393 status_t 394 MoveDisplay(uint16 horizontalStart, uint16 verticalStart) 395 { 396 // Set which pixel of the virtual frame buffer will show up in the 397 // top left corner of the display device. Used for page-flipping 398 // games and virtual desktops. 399 400 DisplayModeEx& mode = gInfo.sharedInfo->displayMode; 401 402 if (mode.timing.h_display + horizontalStart > mode.virtual_width 403 || mode.timing.v_display + verticalStart > mode.virtual_height) 404 return B_ERROR; 405 406 mode.h_display_start = horizontalStart; 407 mode.v_display_start = verticalStart; 408 409 gInfo.AdjustFrame(mode); 410 return B_OK; 411 } 412 413 414 uint32 415 AccelerantModeCount(void) 416 { 417 // Return the number of display modes in the mode list. 418 419 return gInfo.sharedInfo->modeCount; 420 } 421 422 423 status_t 424 GetModeList(display_mode* dmList) 425 { 426 // Copy the list of supported video modes to the location pointed at 427 // by dmList. 428 429 memcpy(dmList, gInfo.modeList, gInfo.sharedInfo->modeCount * sizeof(display_mode)); 430 return B_OK; 431 } 432 433 434 status_t 435 GetDisplayMode(display_mode* current_mode) 436 { 437 *current_mode = gInfo.sharedInfo->displayMode; // return current display mode 438 return B_OK; 439 } 440 441 442 status_t 443 GetFrameBufferConfig(frame_buffer_config* pFBC) 444 { 445 SharedInfo& si = *gInfo.sharedInfo; 446 447 pFBC->frame_buffer = (void*)((addr_t)si.videoMemAddr + si.frameBufferOffset); 448 pFBC->frame_buffer_dma = (void*)((addr_t)si.videoMemPCI + si.frameBufferOffset); 449 uint32 bytesPerPixel = (si.displayMode.bpp + 7) / 8; 450 pFBC->bytes_per_row = si.displayMode.virtual_width * bytesPerPixel; 451 452 return B_OK; 453 } 454 455 456 status_t 457 GetPixelClockLimits(display_mode* mode, uint32* low, uint32* high) 458 { 459 // Return the maximum and minium pixel clock limits for the specified mode. 460 461 uint32 bitsPerPixel; 462 uint32 maxPixelClock; 463 464 if ( ! gInfo.GetColorSpaceParams(mode->space, bitsPerPixel, maxPixelClock)) 465 return B_ERROR; 466 467 if (low != NULL) { 468 // lower limit of about 48Hz vertical refresh 469 uint32 totalClocks = (uint32)mode->timing.h_total * (uint32)mode->timing.v_total; 470 uint32 lowClock = (totalClocks * 48L) / 1000L; 471 if (lowClock > maxPixelClock) 472 return B_ERROR; 473 474 *low = lowClock; 475 } 476 477 if (high != NULL) 478 *high = maxPixelClock; 479 480 return B_OK; 481 } 482 483 484 status_t 485 GetTimingConstraints(display_timing_constraints *constraints) 486 { 487 (void)constraints; // avoid compiler warning for unused arg 488 489 return B_ERROR; 490 } 491 492 493 status_t 494 GetPreferredDisplayMode(display_mode* preferredMode) 495 { 496 // If the chip is connected to a laptop LCD panel, find the mode with 497 // matching width and height, 60 Hz refresh rate, and greatest color depth. 498 499 SharedInfo& si = *gInfo.sharedInfo; 500 501 if (si.displayType == MT_LCD) { 502 display_mode* mode = FindDisplayMode(si.panelX, si.panelY, 60, 0); 503 504 if (mode != NULL) { 505 *preferredMode = *mode; 506 return B_OK; 507 } 508 } 509 510 return B_ERROR; 511 } 512 513 514 515 #ifdef __HAIKU__ 516 517 status_t 518 GetEdidInfo(void* info, size_t size, uint32* _version) 519 { 520 SharedInfo& si = *gInfo.sharedInfo; 521 522 if ( ! si.bHaveEDID) 523 return B_ERROR; 524 525 if (size < sizeof(struct edid1_info)) 526 return B_BUFFER_OVERFLOW; 527 528 memcpy(info, &si.edidInfo, sizeof(struct edid1_info)); 529 *_version = EDID_VERSION_1; 530 return B_OK; 531 } 532 533 #endif // __HAIKU__ 534