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