1 /* 2 Copyright 2007-2009 Haiku, Inc. All rights reserved. 3 Distributed under the terms of the MIT license. 4 5 Authors: 6 Gerald Zajac 2007-2009 7 */ 8 9 #include "accelerant.h" 10 11 #include <string.h> 12 #include <unistd.h> 13 14 #include <create_display_modes.h> // common accelerant header file 15 16 17 static display_mode* 18 FindDisplayMode(int width, int height, int refreshRate, uint32 colorDepth) 19 { 20 // Search the mode list for the mode with specified width, height, 21 // refresh rate, and color depth. If argument colorDepth is zero, this 22 // function will search for a mode satisfying the other 3 arguments, and 23 // if more than one matching mode is found, the one with the greatest color 24 // depth will be selected. 25 // 26 // If successful, return a pointer to the selected display_mode object; 27 // else return NULL. 28 29 display_mode* selectedMode = NULL; 30 uint32 modeCount = gInfo.sharedInfo->modeCount; 31 32 for (uint32 j = 0; j < modeCount; j++) { 33 display_mode& mode = gInfo.modeList[j]; 34 35 if (mode.timing.h_display == width && mode.timing.v_display == height) { 36 int modeRefreshRate = int(((mode.timing.pixel_clock * 1000.0 / 37 mode.timing.h_total) / mode.timing.v_total) + 0.5); 38 if (modeRefreshRate == refreshRate) { 39 if (colorDepth == 0) { 40 if (selectedMode == NULL || mode.space > selectedMode->space) 41 selectedMode = &mode; 42 } else { 43 if (mode.space == colorDepth) 44 return &mode; 45 } 46 } 47 } 48 } 49 50 return selectedMode; 51 } 52 53 54 55 static bool 56 IsThereEnoughFBMemory(const display_mode* mode, uint32 bitsPerPixel) 57 { 58 // Test if there is enough Frame Buffer memory for the mode and color depth 59 // specified by the caller, and return true if there is sufficient memory. 60 61 uint32 maxWidth = mode->virtual_width; 62 if (mode->timing.h_display > maxWidth) 63 maxWidth = mode->timing.h_display; 64 65 uint32 maxHeight = mode->virtual_height; 66 if (mode->timing.v_display > maxHeight) 67 maxHeight = mode->timing.v_display; 68 69 uint32 bytesPerPixel = (bitsPerPixel + 7) / 8; 70 71 return (maxWidth * maxHeight * bytesPerPixel < gInfo.sharedInfo->maxFrameBufferSize); 72 } 73 74 75 76 uint16 77 GetVesaModeNumber(const display_mode& mode, uint8 bitsPerPixel) 78 { 79 // Search VESA mode table for a matching mode, and return the VESA mode 80 // number if a match is found; else return 0 if no match is found. 81 82 SharedInfo& si = *gInfo.sharedInfo; 83 84 VesaMode* vesaModeTable = (VesaMode*)((uint8*)&si + si.vesaModeTableOffset); 85 86 for (uint32 j = 0; j < si.vesaModeCount; j++) { 87 VesaMode& vesaMode = vesaModeTable[j]; 88 89 if (vesaMode.width == mode.timing.h_display 90 && vesaMode.height == mode.timing.v_display 91 && vesaMode.bitsPerPixel == bitsPerPixel) 92 return vesaMode.mode; 93 } 94 95 return 0; // matching VESA mode not found 96 } 97 98 99 100 bool 101 IsModeUsable(const display_mode* mode) 102 { 103 // Test if the display mode is usable by the current video chip. That is, 104 // does the chip have enough memory for the mode and is the pixel clock 105 // within the chips allowable range, etc. 106 // 107 // Return true if the mode is usable. 108 109 SharedInfo& si = *gInfo.sharedInfo; 110 uint8 bitsPerPixel; 111 uint32 maxPixelClock; 112 113 if (!gInfo.GetColorSpaceParams(mode->space, bitsPerPixel, maxPixelClock)) 114 return false; 115 116 // Is there enough frame buffer memory to handle the mode? 117 118 if (!IsThereEnoughFBMemory(mode, bitsPerPixel)) 119 return false; 120 121 if (si.displayType == MT_VGA) { 122 // Test if mode is usable for a chip that is connected to a monitor 123 // via an analog VGA connector. 124 125 if (mode->timing.pixel_clock > maxPixelClock) 126 return false; 127 128 // Is the color space supported? 129 130 bool colorSpaceSupported = false; 131 for (uint32 j = 0; j < si.colorSpaceCount; j++) { 132 if (mode->space == uint32(si.colorSpaces[j])) { 133 colorSpaceSupported = true; 134 break; 135 } 136 } 137 138 if (!colorSpaceSupported) 139 return false; 140 141 // Reject modes with a width of 640 and a height < 480 since they do not 142 // work properly with the ATI chipsets. 143 144 if (mode->timing.h_display == 640 && mode->timing.v_display < 480) 145 return false; 146 } else { 147 // Test if mode is usable for a chip that is connected to a laptop LCD 148 // display or a monitor via a DVI interface. 149 150 // If chip is a Mach64 Mobility chip exclude 640x350 resolution since 151 // that resolution can not be set without a failure in the VESA set mode 152 // function. 153 154 if (si.chipType == MACH64_MOBILITY && mode->timing.h_display == 640 155 && mode->timing.v_display == 350) 156 return false; 157 158 // Search VESA mode table for matching mode. 159 160 if (GetVesaModeNumber(*mode, bitsPerPixel) == 0) 161 return false; 162 } 163 164 return true; 165 } 166 167 168 status_t 169 CreateModeList(bool (*checkMode)(const display_mode* mode)) 170 { 171 SharedInfo& si = *gInfo.sharedInfo; 172 173 // Obtain EDID info which is needed for for building the mode list. 174 // However, if a laptop's LCD display is active, bypass getting the EDID 175 // info since it is not needed, and if the external display supports only 176 // resolutions smaller than the size of the laptop LCD display, it would 177 // unnecessarily restrict size of the resolutions that could be set for 178 // laptop LCD display. 179 180 si.bHaveEDID = false; 181 182 if (si.displayType == MT_VGA && !si.bHaveEDID) { 183 edid1_raw rawEdid; // raw EDID info to obtain 184 185 if (ioctl(gInfo.deviceFileDesc, ATI_GET_EDID, &rawEdid, 186 sizeof(rawEdid)) == B_OK) { 187 if (rawEdid.version.version != 1 || rawEdid.version.revision > 4) { 188 TRACE("CreateModeList(); EDID version %d.%d out of range\n", 189 rawEdid.version.version, rawEdid.version.revision); 190 } else { 191 edid_decode(&si.edidInfo, &rawEdid); // decode & save EDID info 192 si.bHaveEDID = true; 193 } 194 } 195 196 if (si.bHaveEDID) { 197 #ifdef ENABLE_DEBUG_TRACE 198 edid_dump(&(si.edidInfo)); 199 #endif 200 } else { 201 TRACE("CreateModeList(); Unable to get EDID info\n"); 202 } 203 } 204 205 display_mode* list; 206 uint32 count = 0; 207 area_id listArea; 208 209 listArea = create_display_modes("ATI modes", 210 si.bHaveEDID ? &si.edidInfo : NULL, 211 NULL, 0, si.colorSpaces, si.colorSpaceCount, 212 (check_display_mode_hook)checkMode, &list, &count); 213 214 if (listArea < 0) 215 return listArea; // listArea has error code 216 217 si.modeArea = gInfo.modeListArea = listArea; 218 si.modeCount = count; 219 gInfo.modeList = list; 220 221 return B_OK; 222 } 223 224 225 226 status_t 227 ProposeDisplayMode(display_mode *target, const display_mode *low, 228 const display_mode *high) 229 { 230 (void)low; // avoid compiler warning for unused arg 231 (void)high; // avoid compiler warning for unused arg 232 233 TRACE("ProposeDisplayMode() %dx%d, pixel clock: %d kHz, space: 0x%X\n", 234 target->timing.h_display, target->timing.v_display, 235 target->timing.pixel_clock, target->space); 236 237 // Search the mode list for the specified mode. 238 239 uint32 modeCount = gInfo.sharedInfo->modeCount; 240 241 for (uint32 j = 0; j < modeCount; j++) { 242 display_mode& mode = gInfo.modeList[j]; 243 244 if (target->timing.h_display == mode.timing.h_display 245 && target->timing.v_display == mode.timing.v_display 246 && target->space == mode.space) 247 return B_OK; // mode found in list 248 } 249 250 return B_BAD_VALUE; // mode not found in list 251 } 252 253 254 status_t 255 SetDisplayMode(display_mode* pMode) 256 { 257 // First validate the mode, then call a function to set the registers. 258 259 TRACE("SetDisplayMode() begin\n"); 260 261 SharedInfo& si = *gInfo.sharedInfo; 262 DisplayModeEx mode; 263 (display_mode&)mode = *pMode; 264 265 uint32 maxPixelClock; 266 if ( ! gInfo.GetColorSpaceParams(mode.space, mode.bitsPerPixel, maxPixelClock)) 267 return B_BAD_VALUE; 268 269 if (ProposeDisplayMode(&mode, pMode, pMode) != B_OK) 270 return B_BAD_VALUE; 271 272 int bytesPerPixel = (mode.bitsPerPixel + 7) / 8; 273 mode.bytesPerRow = mode.timing.h_display * bytesPerPixel; 274 275 // Is there enough frame buffer memory for this mode? 276 277 if ( ! IsThereEnoughFBMemory(&mode, mode.bitsPerPixel)) 278 return B_NO_MEMORY; 279 280 TRACE("Set display mode: %dx%d virtual size: %dx%d color depth: %d bits/pixel\n", 281 mode.timing.h_display, mode.timing.v_display, 282 mode.virtual_width, mode.virtual_height, mode.bitsPerPixel); 283 284 if (si.displayType == MT_VGA) { 285 TRACE(" mode timing: %d %d %d %d %d %d %d %d %d\n", 286 mode.timing.pixel_clock, 287 mode.timing.h_display, 288 mode.timing.h_sync_start, mode.timing.h_sync_end, 289 mode.timing.h_total, 290 mode.timing.v_display, 291 mode.timing.v_sync_start, mode.timing.v_sync_end, 292 mode.timing.v_total); 293 294 TRACE(" mode hFreq: %.1f kHz vFreq: %.1f Hz %chSync %cvSync\n", 295 double(mode.timing.pixel_clock) / mode.timing.h_total, 296 ((double(mode.timing.pixel_clock) / mode.timing.h_total) * 1000.0) 297 / mode.timing.v_total, 298 (mode.timing.flags & B_POSITIVE_HSYNC) ? '+' : '-', 299 (mode.timing.flags & B_POSITIVE_VSYNC) ? '+' : '-'); 300 } 301 302 status_t status = gInfo.SetDisplayMode(mode); 303 if (status != B_OK) { 304 TRACE("SetDisplayMode() failed; status 0x%x\n", status); 305 return status; 306 } 307 308 si.displayMode = mode; 309 310 TRACE("SetDisplayMode() done\n"); 311 return B_OK; 312 } 313 314 315 316 status_t 317 MoveDisplay(uint16 horizontalStart, uint16 verticalStart) 318 { 319 // Set which pixel of the virtual frame buffer will show up in the 320 // top left corner of the display device. Used for page-flipping 321 // games and virtual desktops. 322 323 DisplayModeEx& mode = gInfo.sharedInfo->displayMode; 324 325 if (mode.timing.h_display + horizontalStart > mode.virtual_width 326 || mode.timing.v_display + verticalStart > mode.virtual_height) 327 return B_ERROR; 328 329 mode.h_display_start = horizontalStart; 330 mode.v_display_start = verticalStart; 331 332 gInfo.AdjustFrame(mode); 333 return B_OK; 334 } 335 336 337 uint32 338 AccelerantModeCount(void) 339 { 340 // Return the number of display modes in the mode list. 341 342 return gInfo.sharedInfo->modeCount; 343 } 344 345 346 status_t 347 GetModeList(display_mode* dmList) 348 { 349 // Copy the list of supported video modes to the location pointed at 350 // by dmList. 351 352 memcpy(dmList, gInfo.modeList, gInfo.sharedInfo->modeCount * sizeof(display_mode)); 353 return B_OK; 354 } 355 356 357 status_t 358 GetDisplayMode(display_mode* current_mode) 359 { 360 *current_mode = gInfo.sharedInfo->displayMode; // return current display mode 361 return B_OK; 362 } 363 364 365 status_t 366 GetFrameBufferConfig(frame_buffer_config* pFBC) 367 { 368 SharedInfo& si = *gInfo.sharedInfo; 369 370 pFBC->frame_buffer = (void*)((addr_t)si.videoMemAddr + si.frameBufferOffset); 371 pFBC->frame_buffer_dma = (void*)((addr_t)si.videoMemPCI + si.frameBufferOffset); 372 uint32 bytesPerPixel = (si.displayMode.bitsPerPixel + 7) / 8; 373 pFBC->bytes_per_row = si.displayMode.virtual_width * bytesPerPixel; 374 375 return B_OK; 376 } 377 378 379 status_t 380 GetPixelClockLimits(display_mode* mode, uint32* low, uint32* high) 381 { 382 // Return the maximum and minium pixel clock limits for the specified mode. 383 384 uint8 bitsPerPixel; 385 uint32 maxPixelClock; 386 387 if ( ! gInfo.GetColorSpaceParams(mode->space, bitsPerPixel, maxPixelClock)) 388 return B_ERROR; 389 390 if (low != NULL) { 391 // lower limit of about 48Hz vertical refresh 392 uint32 totalClocks = (uint32)mode->timing.h_total * (uint32)mode->timing.v_total; 393 uint32 lowClock = (totalClocks * 48L) / 1000L; 394 if (lowClock > maxPixelClock) 395 return B_ERROR; 396 397 *low = lowClock; 398 } 399 400 if (high != NULL) 401 *high = maxPixelClock; 402 403 return B_OK; 404 } 405 406 407 status_t 408 GetTimingConstraints(display_timing_constraints *constraints) 409 { 410 (void)constraints; // avoid compiler warning for unused arg 411 412 return B_ERROR; 413 } 414 415 416 status_t 417 GetPreferredDisplayMode(display_mode* preferredMode) 418 { 419 // If the chip is connected to a laptop LCD panel, find the mode with 420 // matching width and height, 60 Hz refresh rate, and greatest color depth. 421 422 SharedInfo& si = *gInfo.sharedInfo; 423 424 if (si.displayType == MT_LAPTOP) { 425 display_mode* mode = FindDisplayMode(si.panelX, si.panelY, 60, 0); 426 427 if (mode != NULL) { 428 *preferredMode = *mode; 429 return B_OK; 430 } 431 } 432 433 return B_ERROR; 434 } 435 436 437 status_t 438 GetEdidInfo(void* info, size_t size, uint32* _version) 439 { 440 SharedInfo& si = *gInfo.sharedInfo; 441 442 if ( ! si.bHaveEDID) 443 return B_ERROR; 444 445 if (size < sizeof(struct edid1_info)) 446 return B_BUFFER_OVERFLOW; 447 448 memcpy(info, &si.edidInfo, sizeof(struct edid1_info)); 449 *_version = EDID_VERSION_1; 450 return B_OK; 451 } 452