1 /* 2 * Copyright 2006-2013, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Support for i915 chipset and up based on the X driver, 6 * Copyright 2006-2007 Intel Corporation. 7 * 8 * Authors: 9 * Axel Dörfler, axeld@pinc-software.de 10 */ 11 12 13 #include "accelerant_protos.h" 14 #include "accelerant.h" 15 #include "utility.h" 16 17 #include <Debug.h> 18 #include <stdio.h> 19 #include <string.h> 20 #include <math.h> 21 22 #include <create_display_modes.h> 23 #include <ddc.h> 24 #include <edid.h> 25 #include <validate_display_mode.h> 26 27 28 #undef TRACE 29 #define TRACE_MODE 30 #ifdef TRACE_MODE 31 # define TRACE(x...) _sPrintf("intel_extreme accelerant:" x) 32 #else 33 # define TRACE(x...) 34 #endif 35 36 #define ERROR(x...) _sPrintf("intel_extreme accelerant: " x) 37 #define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__) 38 39 40 struct display_registers { 41 uint32 pll; 42 uint32 divisors; 43 uint32 control; 44 uint32 pipe_config; 45 uint32 horiz_total; 46 uint32 horiz_blank; 47 uint32 horiz_sync; 48 uint32 vert_total; 49 uint32 vert_blank; 50 uint32 vert_sync; 51 uint32 size; 52 uint32 stride; 53 uint32 position; 54 uint32 pipe_source; 55 }; 56 57 struct pll_divisors { 58 uint32 post; 59 uint32 post1; 60 uint32 post2; 61 bool post2_high; 62 uint32 n; 63 uint32 m; 64 uint32 m1; 65 uint32 m2; 66 }; 67 68 struct pll_limits { 69 pll_divisors min; 70 pll_divisors max; 71 uint32 min_post2_frequency; 72 uint32 min_vco; 73 uint32 max_vco; 74 }; 75 76 77 static status_t 78 get_i2c_signals(void* cookie, int* _clock, int* _data) 79 { 80 uint32 ioRegister = (uint32)(addr_t)cookie; 81 uint32 value = read32(ioRegister); 82 83 *_clock = (value & I2C_CLOCK_VALUE_IN) != 0; 84 *_data = (value & I2C_DATA_VALUE_IN) != 0; 85 86 return B_OK; 87 } 88 89 90 static status_t 91 set_i2c_signals(void* cookie, int clock, int data) 92 { 93 uint32 ioRegister = (uint32)(addr_t)cookie; 94 uint32 value; 95 96 if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_83x)) { 97 // on these chips, the reserved values are fixed 98 value = 0; 99 } else { 100 // on all others, we have to preserve them manually 101 value = read32(ioRegister) & I2C_RESERVED; 102 } 103 104 if (data != 0) 105 value |= I2C_DATA_DIRECTION_MASK; 106 else { 107 value |= I2C_DATA_DIRECTION_MASK | I2C_DATA_DIRECTION_OUT 108 | I2C_DATA_VALUE_MASK; 109 } 110 111 if (clock != 0) 112 value |= I2C_CLOCK_DIRECTION_MASK; 113 else { 114 value |= I2C_CLOCK_DIRECTION_MASK | I2C_CLOCK_DIRECTION_OUT 115 | I2C_CLOCK_VALUE_MASK; 116 } 117 118 write32(ioRegister, value); 119 read32(ioRegister); 120 // make sure the PCI bus has flushed the write 121 122 return B_OK; 123 } 124 125 126 static void 127 get_pll_limits(pll_limits &limits) 128 { 129 // Note, the limits are taken from the X driver; they have not yet been 130 // tested 131 132 if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_ILK) 133 || gInfo->shared_info->device_type.InGroup(INTEL_TYPE_SNB) 134 || gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IVB)) { 135 // TODO: support LVDS output limits as well 136 static const pll_limits kLimits = { 137 // p, p1, p2, high, n, m, m1, m2 138 { 5, 1, 10, false, 1, 79, 12, 5}, // min 139 { 80, 8, 5, true, 5, 127, 22, 9}, // max 140 225000, 1760000, 3510000 141 }; 142 limits = kLimits; 143 } else if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_G4x)) { 144 // TODO: support LVDS output limits as well 145 static const pll_limits kLimits = { 146 // p, p1, p2, high, n, m, m1, m2 147 { 10, 1, 10, false, 1, 104, 17, 5}, // min 148 { 30, 3, 10, true, 4, 138, 23, 11}, // max 149 270000, 1750000, 3500000 150 }; 151 limits = kLimits; 152 } else if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) { 153 // TODO: support LVDS output limits as well 154 // m1 is reserved and must be 0 155 static const pll_limits kLimits = { 156 // p, p1, p2, high, n, m, m1, m2 157 { 5, 1, 10, false, 3, 2, 0, 2}, // min 158 { 80, 8, 5, true, 6, 256, 0, 256}, // max 159 200000, 1700000, 3500000 160 }; 161 limits = kLimits; 162 } else if (gInfo->shared_info->device_type.InFamily(INTEL_TYPE_9xx)) { 163 // TODO: support LVDS output limits as well 164 // (Update: Output limits are adjusted in the computation (post2=7/14)) 165 // Should move them here! 166 static const pll_limits kLimits = { 167 // p, p1, p2, high, n, m, m1, m2 168 { 5, 1, 10, false, 5, 70, 12, 7}, // min 169 { 80, 8, 5, true, 10, 120, 22, 11}, // max 170 200000, 1400000, 2800000 171 }; 172 limits = kLimits; 173 } else { 174 // TODO: support LVDS output limits as well 175 static const pll_limits kLimits = { 176 // p, p1, p2, high, n, m, m1, m2 177 { 4, 2, 4, false, 5, 96, 20, 8}, 178 {128, 33, 2, true, 18, 140, 28, 18}, 179 165000, 930000, 1400000 180 }; 181 limits = kLimits; 182 } 183 184 TRACE("PLL limits, min: p %lu (p1 %lu, p2 %lu), n %lu, m %lu " 185 "(m1 %lu, m2 %lu)\n", limits.min.post, limits.min.post1, 186 limits.min.post2, limits.min.n, limits.min.m, limits.min.m1, 187 limits.min.m2); 188 TRACE("PLL limits, max: p %lu (p1 %lu, p2 %lu), n %lu, m %lu " 189 "(m1 %lu, m2 %lu)\n", limits.max.post, limits.max.post1, 190 limits.max.post2, limits.max.n, limits.max.m, limits.max.m1, 191 limits.max.m2); 192 } 193 194 195 static bool 196 valid_pll_divisors(const pll_divisors& divisors, const pll_limits& limits) 197 { 198 pll_info &info = gInfo->shared_info->pll_info; 199 uint32 vco = info.reference_frequency * divisors.m / divisors.n; 200 uint32 frequency = vco / divisors.post; 201 202 if (divisors.post < limits.min.post || divisors.post > limits.max.post 203 || divisors.m < limits.min.m || divisors.m > limits.max.m 204 || vco < limits.min_vco || vco > limits.max_vco 205 || frequency < info.min_frequency || frequency > info.max_frequency) 206 return false; 207 208 return true; 209 } 210 211 212 static void 213 compute_pll_divisors(const display_mode ¤t, pll_divisors& divisors, 214 bool isLVDS) 215 { 216 float requestedPixelClock = current.timing.pixel_clock / 1000.0f; 217 float referenceClock 218 = gInfo->shared_info->pll_info.reference_frequency / 1000.0f; 219 pll_limits limits; 220 get_pll_limits(limits); 221 222 TRACE("%s: required MHz: %g\n", __func__, requestedPixelClock); 223 224 if (isLVDS) { 225 if ((read32(INTEL_DISPLAY_LVDS_PORT) & LVDS_CLKB_POWER_MASK) 226 == LVDS_CLKB_POWER_UP) 227 divisors.post2 = LVDS_POST2_RATE_FAST; 228 else 229 divisors.post2 = LVDS_POST2_RATE_SLOW; 230 } else { 231 if (current.timing.pixel_clock < limits.min_post2_frequency) { 232 // slow DAC timing 233 divisors.post2 = limits.min.post2; 234 divisors.post2_high = limits.min.post2_high; 235 } else { 236 // fast DAC timing 237 divisors.post2 = limits.max.post2; 238 divisors.post2_high = limits.max.post2_high; 239 } 240 } 241 242 float best = requestedPixelClock; 243 pll_divisors bestDivisors; 244 245 bool is_igd = gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD); 246 for (divisors.m1 = limits.min.m1; divisors.m1 <= limits.max.m1; 247 divisors.m1++) { 248 for (divisors.m2 = limits.min.m2; divisors.m2 <= limits.max.m2 249 && ((divisors.m2 < divisors.m1) || is_igd); divisors.m2++) { 250 for (divisors.n = limits.min.n; divisors.n <= limits.max.n; 251 divisors.n++) { 252 for (divisors.post1 = limits.min.post1; 253 divisors.post1 <= limits.max.post1; divisors.post1++) { 254 divisors.m = 5 * divisors.m1 + divisors.m2; 255 divisors.post = divisors.post1 * divisors.post2; 256 257 if (!valid_pll_divisors(divisors, limits)) 258 continue; 259 260 float error = fabs(requestedPixelClock 261 - ((referenceClock * divisors.m) / divisors.n) 262 / divisors.post); 263 if (error < best) { 264 best = error; 265 bestDivisors = divisors; 266 267 if (error == 0) 268 break; 269 } 270 } 271 } 272 } 273 } 274 275 divisors = bestDivisors; 276 277 TRACE("%s: found: %g MHz, p = %lu (p1 = %lu, p2 = %lu), n = %lu, m = %lu " 278 "(m1 = %lu, m2 = %lu)\n", __func__, 279 ((referenceClock * divisors.m) / divisors.n) / divisors.post, 280 divisors.post, divisors.post1, divisors.post2, divisors.n, 281 divisors.m, divisors.m1, divisors.m2); 282 } 283 284 285 static void 286 retrieve_current_mode(display_mode& mode, uint32 pllRegister) 287 { 288 uint32 pll = read32(pllRegister); 289 uint32 pllDivisor; 290 uint32 hTotalRegister; 291 uint32 vTotalRegister; 292 uint32 hSyncRegister; 293 uint32 vSyncRegister; 294 uint32 imageSizeRegister; 295 uint32 controlRegister; 296 297 if (pllRegister == INTEL_DISPLAY_A_PLL) { 298 pllDivisor = read32((pll & DISPLAY_PLL_DIVISOR_1) != 0 299 ? INTEL_DISPLAY_A_PLL_DIVISOR_1 : INTEL_DISPLAY_A_PLL_DIVISOR_0); 300 301 hTotalRegister = INTEL_DISPLAY_A_HTOTAL; 302 vTotalRegister = INTEL_DISPLAY_A_VTOTAL; 303 hSyncRegister = INTEL_DISPLAY_A_HSYNC; 304 vSyncRegister = INTEL_DISPLAY_A_VSYNC; 305 imageSizeRegister = INTEL_DISPLAY_A_IMAGE_SIZE; 306 controlRegister = INTEL_DISPLAY_A_CONTROL; 307 } else if (pllRegister == INTEL_DISPLAY_B_PLL) { 308 pllDivisor = read32((pll & DISPLAY_PLL_DIVISOR_1) != 0 309 ? INTEL_DISPLAY_B_PLL_DIVISOR_1 : INTEL_DISPLAY_B_PLL_DIVISOR_0); 310 311 hTotalRegister = INTEL_DISPLAY_B_HTOTAL; 312 vTotalRegister = INTEL_DISPLAY_B_VTOTAL; 313 hSyncRegister = INTEL_DISPLAY_B_HSYNC; 314 vSyncRegister = INTEL_DISPLAY_B_VSYNC; 315 imageSizeRegister = INTEL_DISPLAY_B_IMAGE_SIZE; 316 controlRegister = INTEL_DISPLAY_B_CONTROL; 317 } else { 318 // TODO: not supported 319 return; 320 } 321 322 pll_divisors divisors; 323 if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) { 324 divisors.m1 = 0; 325 divisors.m2 = (pllDivisor & DISPLAY_PLL_IGD_M2_DIVISOR_MASK) 326 >> DISPLAY_PLL_M2_DIVISOR_SHIFT; 327 divisors.n = ((pllDivisor & DISPLAY_PLL_IGD_N_DIVISOR_MASK) 328 >> DISPLAY_PLL_N_DIVISOR_SHIFT) - 1; 329 } else { 330 divisors.m1 = (pllDivisor & DISPLAY_PLL_M1_DIVISOR_MASK) 331 >> DISPLAY_PLL_M1_DIVISOR_SHIFT; 332 divisors.m2 = (pllDivisor & DISPLAY_PLL_M2_DIVISOR_MASK) 333 >> DISPLAY_PLL_M2_DIVISOR_SHIFT; 334 divisors.n = (pllDivisor & DISPLAY_PLL_N_DIVISOR_MASK) 335 >> DISPLAY_PLL_N_DIVISOR_SHIFT; 336 } 337 338 pll_limits limits; 339 get_pll_limits(limits); 340 341 if (gInfo->shared_info->device_type.InFamily(INTEL_TYPE_9xx)) { 342 if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) { 343 divisors.post1 = (pll & DISPLAY_PLL_IGD_POST1_DIVISOR_MASK) 344 >> DISPLAY_PLL_IGD_POST1_DIVISOR_SHIFT; 345 } else { 346 divisors.post1 = (pll & DISPLAY_PLL_9xx_POST1_DIVISOR_MASK) 347 >> DISPLAY_PLL_POST1_DIVISOR_SHIFT; 348 } 349 350 if (pllRegister == INTEL_DISPLAY_B_PLL 351 && !gInfo->shared_info->device_type.InGroup(INTEL_TYPE_96x)) { 352 // TODO: Fix this? Need to support dual channel LVDS. 353 divisors.post2 = LVDS_POST2_RATE_SLOW; 354 } else { 355 if ((pll & DISPLAY_PLL_DIVIDE_HIGH) != 0) 356 divisors.post2 = limits.max.post2; 357 else 358 divisors.post2 = limits.min.post2; 359 } 360 } else { 361 // 8xx 362 divisors.post1 = (pll & DISPLAY_PLL_POST1_DIVISOR_MASK) 363 >> DISPLAY_PLL_POST1_DIVISOR_SHIFT; 364 365 if ((pll & DISPLAY_PLL_DIVIDE_4X) != 0) 366 divisors.post2 = limits.max.post2; 367 else 368 divisors.post2 = limits.min.post2; 369 } 370 371 divisors.m = 5 * divisors.m1 + divisors.m2; 372 divisors.post = divisors.post1 * divisors.post2; 373 374 float referenceClock 375 = gInfo->shared_info->pll_info.reference_frequency / 1000.0f; 376 float pixelClock 377 = ((referenceClock * divisors.m) / divisors.n) / divisors.post; 378 379 // timing 380 381 mode.timing.pixel_clock = uint32(pixelClock * 1000); 382 mode.timing.flags = 0; 383 384 uint32 value = read32(hTotalRegister); 385 mode.timing.h_total = (value >> 16) + 1; 386 mode.timing.h_display = (value & 0xffff) + 1; 387 388 value = read32(hSyncRegister); 389 mode.timing.h_sync_end = (value >> 16) + 1; 390 mode.timing.h_sync_start = (value & 0xffff) + 1; 391 392 value = read32(vTotalRegister); 393 mode.timing.v_total = (value >> 16) + 1; 394 mode.timing.v_display = (value & 0xffff) + 1; 395 396 value = read32(vSyncRegister); 397 mode.timing.v_sync_end = (value >> 16) + 1; 398 mode.timing.v_sync_start = (value & 0xffff) + 1; 399 400 // image size and color space 401 402 value = read32(imageSizeRegister); 403 mode.virtual_width = (value >> 16) + 1; 404 mode.virtual_height = (value & 0xffff) + 1; 405 406 // using virtual size based on image size is the 'proper' way to do it, 407 // however the bios appears to be suggesting scaling or somesuch, so ignore 408 // the proper virtual dimension for now if they'd suggest a smaller size. 409 if (mode.virtual_width < mode.timing.h_display) 410 mode.virtual_width = mode.timing.h_display; 411 if (mode.virtual_height < mode.timing.v_display) 412 mode.virtual_height = mode.timing.v_display; 413 414 value = read32(controlRegister); 415 switch (value & DISPLAY_CONTROL_COLOR_MASK) { 416 case DISPLAY_CONTROL_RGB32: 417 default: 418 mode.space = B_RGB32; 419 break; 420 case DISPLAY_CONTROL_RGB16: 421 mode.space = B_RGB16; 422 break; 423 case DISPLAY_CONTROL_RGB15: 424 mode.space = B_RGB15; 425 break; 426 case DISPLAY_CONTROL_CMAP8: 427 mode.space = B_CMAP8; 428 break; 429 } 430 431 mode.h_display_start = 0; 432 mode.v_display_start = 0; 433 mode.flags = B_8_BIT_DAC | B_HARDWARE_CURSOR | B_PARALLEL_ACCESS 434 | B_DPMS | B_SUPPORTS_OVERLAYS; 435 } 436 437 438 static void 439 get_color_space_format(const display_mode &mode, uint32 &colorMode, 440 uint32 &bytesPerRow, uint32 &bitsPerPixel) 441 { 442 uint32 bytesPerPixel; 443 444 switch (mode.space) { 445 case B_RGB32_LITTLE: 446 colorMode = DISPLAY_CONTROL_RGB32; 447 bytesPerPixel = 4; 448 bitsPerPixel = 32; 449 break; 450 case B_RGB16_LITTLE: 451 colorMode = DISPLAY_CONTROL_RGB16; 452 bytesPerPixel = 2; 453 bitsPerPixel = 16; 454 break; 455 case B_RGB15_LITTLE: 456 colorMode = DISPLAY_CONTROL_RGB15; 457 bytesPerPixel = 2; 458 bitsPerPixel = 15; 459 break; 460 case B_CMAP8: 461 default: 462 colorMode = DISPLAY_CONTROL_CMAP8; 463 bytesPerPixel = 1; 464 bitsPerPixel = 8; 465 break; 466 } 467 468 bytesPerRow = mode.virtual_width * bytesPerPixel; 469 470 // Make sure bytesPerRow is a multiple of 64 471 // TODO: check if the older chips have the same restriction! 472 if ((bytesPerRow & 63) != 0) 473 bytesPerRow = (bytesPerRow + 63) & ~63; 474 } 475 476 477 static bool 478 sanitize_display_mode(display_mode& mode) 479 { 480 // Some cards only support even pixel counts, while others require an odd 481 // one. 482 bool olderCard = gInfo->shared_info->device_type.InGroup(INTEL_TYPE_Gxx); 483 olderCard |= gInfo->shared_info->device_type.InGroup(INTEL_TYPE_96x); 484 olderCard |= gInfo->shared_info->device_type.InGroup(INTEL_TYPE_94x); 485 olderCard |= gInfo->shared_info->device_type.InGroup(INTEL_TYPE_91x); 486 olderCard |= gInfo->shared_info->device_type.InFamily(INTEL_TYPE_8xx); 487 olderCard |= gInfo->shared_info->device_type.InFamily(INTEL_TYPE_7xx); 488 489 // TODO: verify constraints - these are more or less taken from the 490 // radeon driver! 491 display_constraints constraints = { 492 // resolution 493 320, 8192, 200, 4096, 494 // pixel clock 495 gInfo->shared_info->pll_info.min_frequency, 496 gInfo->shared_info->pll_info.max_frequency, 497 // horizontal 498 {1, 0, 8160, 32, 8192, 0, 8192}, 499 {1, 1, 4092, 2, 63, 1, 4096} 500 }; 501 502 if (olderCard) 503 constraints.horizontal_timing.resolution = 2; 504 505 return sanitize_display_mode(mode, constraints, 506 gInfo->has_edid ? &gInfo->edid_info : NULL); 507 } 508 509 510 static bool 511 check_and_sanitize_display_mode(display_mode* mode) 512 { 513 uint16 width = mode->timing.h_display; 514 uint16 height = mode->timing.v_display; 515 516 // Only accept the mode if it is within the supported resolution 517 // TODO: sanitize_display_mode() should report resolution changes 518 // differently! 519 return !sanitize_display_mode(*mode) || (width == mode->timing.h_display 520 && height == mode->timing.v_display); 521 } 522 523 524 // #pragma mark - 525 526 527 void 528 set_frame_buffer_base() 529 { 530 intel_shared_info &sharedInfo = *gInfo->shared_info; 531 display_mode &mode = sharedInfo.current_mode; 532 uint32 baseRegister; 533 uint32 surfaceRegister; 534 535 if (gInfo->head_mode & HEAD_MODE_A_ANALOG) { 536 baseRegister = INTEL_DISPLAY_A_BASE; 537 surfaceRegister = INTEL_DISPLAY_A_SURFACE; 538 } else { 539 baseRegister = INTEL_DISPLAY_B_BASE; 540 surfaceRegister = INTEL_DISPLAY_B_SURFACE; 541 } 542 543 if (sharedInfo.device_type.InGroup(INTEL_TYPE_96x) 544 || sharedInfo.device_type.InGroup(INTEL_TYPE_G4x) 545 || sharedInfo.device_type.InGroup(INTEL_TYPE_ILK) 546 || sharedInfo.device_type.InGroup(INTEL_TYPE_SNB) 547 || sharedInfo.device_type.InGroup(INTEL_TYPE_IVB)) { 548 write32(baseRegister, mode.v_display_start * sharedInfo.bytes_per_row 549 + mode.h_display_start * (sharedInfo.bits_per_pixel + 7) / 8); 550 read32(baseRegister); 551 write32(surfaceRegister, sharedInfo.frame_buffer_offset); 552 read32(surfaceRegister); 553 } else { 554 write32(baseRegister, sharedInfo.frame_buffer_offset 555 + mode.v_display_start * sharedInfo.bytes_per_row 556 + mode.h_display_start * (sharedInfo.bits_per_pixel + 7) / 8); 557 read32(baseRegister); 558 } 559 } 560 561 562 /*! Creates the initial mode list of the primary accelerant. 563 It's called from intel_init_accelerant(). 564 */ 565 status_t 566 create_mode_list(void) 567 { 568 i2c_bus bus; 569 bus.cookie = (void*)(addr_t)INTEL_I2C_IO_A; 570 bus.set_signals = &set_i2c_signals; 571 bus.get_signals = &get_i2c_signals; 572 ddc2_init_timing(&bus); 573 574 status_t error = ddc2_read_edid1(&bus, &gInfo->edid_info, NULL, NULL); 575 if (error == B_OK) { 576 edid_dump(&gInfo->edid_info); 577 gInfo->has_edid = true; 578 } else { 579 TRACE("getting EDID on port A (analog) failed : %s. " 580 "Trying on port C (lvds)\n", strerror(error)); 581 bus.cookie = (void*)INTEL_I2C_IO_C; 582 error = ddc2_read_edid1(&bus, &gInfo->edid_info, NULL, NULL); 583 if (error == B_OK) { 584 edid_dump(&gInfo->edid_info); 585 gInfo->has_edid = true; 586 } else { 587 TRACE("getting EDID on port C failed : %s\n", 588 strerror(error)); 589 590 // We could not read any EDID info. Fallback to creating a list with 591 // only the mode set up by the BIOS. 592 // TODO: support lower modes via scaling and windowing 593 if ((gInfo->head_mode & HEAD_MODE_LVDS_PANEL) != 0 594 && (gInfo->head_mode & HEAD_MODE_A_ANALOG) == 0) { 595 size_t size = (sizeof(display_mode) + B_PAGE_SIZE - 1) 596 & ~(B_PAGE_SIZE - 1); 597 598 display_mode* list; 599 area_id area = create_area("intel extreme modes", 600 (void**)&list, B_ANY_ADDRESS, size, B_NO_LOCK, 601 B_READ_AREA | B_WRITE_AREA); 602 if (area < B_OK) 603 return area; 604 605 memcpy(list, &gInfo->lvds_panel_mode, sizeof(display_mode)); 606 607 gInfo->mode_list_area = area; 608 gInfo->mode_list = list; 609 gInfo->shared_info->mode_list_area = gInfo->mode_list_area; 610 gInfo->shared_info->mode_count = 1; 611 return B_OK; 612 } 613 } 614 } 615 616 // Otherwise return the 'real' list of modes 617 display_mode* list; 618 uint32 count = 0; 619 gInfo->mode_list_area = create_display_modes("intel extreme modes", 620 gInfo->has_edid ? &gInfo->edid_info : NULL, NULL, 0, NULL, 0, 621 &check_and_sanitize_display_mode, &list, &count); 622 if (gInfo->mode_list_area < B_OK) 623 return gInfo->mode_list_area; 624 625 gInfo->mode_list = list; 626 gInfo->shared_info->mode_list_area = gInfo->mode_list_area; 627 gInfo->shared_info->mode_count = count; 628 629 return B_OK; 630 } 631 632 633 void 634 wait_for_vblank(void) 635 { 636 acquire_sem_etc(gInfo->shared_info->vblank_sem, 1, B_RELATIVE_TIMEOUT, 637 25000); 638 // With the output turned off via DPMS, we might not get any interrupts 639 // anymore that's why we don't wait forever for it. 640 } 641 642 643 /*! Store away panel information if identified on startup 644 (used for pipe B->lvds). 645 */ 646 void 647 save_lvds_mode(void) 648 { 649 // dump currently programmed mode. 650 display_mode biosMode; 651 retrieve_current_mode(biosMode, INTEL_DISPLAY_B_PLL); 652 653 sanitize_display_mode(biosMode); 654 // The BIOS mode may not be a valid mode, as LVDS output does not 655 // really care about the sync values 656 657 gInfo->lvds_panel_mode = biosMode; 658 } 659 660 661 // #pragma mark - 662 663 664 uint32 665 intel_accelerant_mode_count(void) 666 { 667 CALLED(); 668 return gInfo->shared_info->mode_count; 669 } 670 671 672 status_t 673 intel_get_mode_list(display_mode* modeList) 674 { 675 CALLED(); 676 memcpy(modeList, gInfo->mode_list, 677 gInfo->shared_info->mode_count * sizeof(display_mode)); 678 return B_OK; 679 } 680 681 682 status_t 683 intel_propose_display_mode(display_mode* target, const display_mode* low, 684 const display_mode* high) 685 { 686 CALLED(); 687 688 // first search for the specified mode in the list, if no mode is found 689 // try to fix the target mode in sanitize_display_mode 690 // TODO: Only sanitize_display_mode should be used. However, at the moment 691 // the mode constraints are not optimal and do not work for all 692 // configurations. 693 for (uint32 i = 0; i < gInfo->shared_info->mode_count; i++) { 694 display_mode *mode = &gInfo->mode_list[i]; 695 696 // TODO: improve this, ie. adapt pixel clock to allowed values!!! 697 698 if (target->virtual_width != mode->virtual_width 699 || target->virtual_height != mode->virtual_height 700 || target->space != mode->space) 701 continue; 702 703 *target = *mode; 704 return B_OK; 705 } 706 707 sanitize_display_mode(*target); 708 709 return is_display_mode_within_bounds(*target, *low, *high) 710 ? B_OK : B_BAD_VALUE; 711 } 712 713 714 status_t 715 intel_set_display_mode(display_mode* mode) 716 { 717 TRACE("%s(%" B_PRIu16 "x%" B_PRIu16 ")\n", __func__, 718 mode->virtual_width, mode->virtual_height); 719 720 if (mode == NULL) 721 return B_BAD_VALUE; 722 723 display_mode target = *mode; 724 725 // TODO: it may be acceptable to continue when using panel fitting or 726 // centering, since the data from propose_display_mode will not actually be 727 // used as is in this case. 728 if (sanitize_display_mode(target)) { 729 TRACE("%s: invalid mode set!\n", __func__); 730 return B_BAD_VALUE; 731 } 732 733 uint32 colorMode, bytesPerRow, bitsPerPixel; 734 get_color_space_format(target, colorMode, bytesPerRow, bitsPerPixel); 735 736 // TODO: do not go further if the mode is identical to the current one. 737 // This would avoid the screen being off when switching workspaces when they 738 // have the same resolution. 739 740 #if 0 741 static bool first = true; 742 if (first) { 743 int fd = open("/boot/home/ie_.regs", O_CREAT | O_WRONLY, 0644); 744 if (fd >= 0) { 745 for (int32 i = 0; i < 0x80000; i += 16) { 746 char line[512]; 747 int length = sprintf(line, "%05lx: %08lx %08lx %08lx %08lx\n", 748 i, read32(i), read32(i + 4), read32(i + 8), read32(i + 12)); 749 write(fd, line, length); 750 } 751 close(fd); 752 sync(); 753 } 754 first = false; 755 } 756 #endif 757 758 intel_shared_info &sharedInfo = *gInfo->shared_info; 759 Autolock locker(sharedInfo.accelerant_lock); 760 761 // TODO: This may not be neccesary 762 set_display_power_mode(B_DPMS_OFF); 763 764 // free old and allocate new frame buffer in graphics memory 765 766 intel_free_memory(sharedInfo.frame_buffer); 767 768 addr_t base; 769 if (intel_allocate_memory(bytesPerRow * target.virtual_height, 0, 770 base) < B_OK) { 771 // oh, how did that happen? Unfortunately, there is no really good way 772 // back 773 if (intel_allocate_memory(sharedInfo.current_mode.virtual_height 774 * sharedInfo.bytes_per_row, 0, base) == B_OK) { 775 sharedInfo.frame_buffer = base; 776 sharedInfo.frame_buffer_offset = base 777 - (addr_t)sharedInfo.graphics_memory; 778 set_frame_buffer_base(); 779 } 780 781 TRACE("%s: Failed to allocate framebuffer !\n", __func__); 782 return B_NO_MEMORY; 783 } 784 785 // clear frame buffer before using it 786 memset((uint8*)base, 0, bytesPerRow * target.virtual_height); 787 sharedInfo.frame_buffer = base; 788 sharedInfo.frame_buffer_offset = base - (addr_t)sharedInfo.graphics_memory; 789 790 // make sure VGA display is disabled 791 write32(INTEL_VGA_DISPLAY_CONTROL, VGA_DISPLAY_DISABLED); 792 read32(INTEL_VGA_DISPLAY_CONTROL); 793 794 if ((gInfo->head_mode & HEAD_MODE_B_DIGITAL) != 0) { 795 // For LVDS panels, we actually always set the native mode in hardware 796 // Then we use the panel fitter to scale the picture to that. 797 display_mode hardwareTarget; 798 bool needsScaling = false; 799 800 // Try to get the panel preferred screen mode from EDID info 801 if (gInfo->has_edid) { 802 hardwareTarget.space = target.space; 803 hardwareTarget.virtual_width 804 = gInfo->edid_info.std_timing[0].h_size; 805 hardwareTarget.virtual_height 806 = gInfo->edid_info.std_timing[0].v_size; 807 for (int i = 0; i < EDID1_NUM_DETAILED_MONITOR_DESC; i++) { 808 if (gInfo->edid_info.detailed_monitor[i].monitor_desc_type 809 == EDID1_IS_DETAILED_TIMING) { 810 hardwareTarget.virtual_width = gInfo->edid_info 811 .detailed_monitor[i].data.detailed_timing.h_active; 812 hardwareTarget.virtual_height = gInfo->edid_info 813 .detailed_monitor[i].data.detailed_timing.v_active; 814 break; 815 } 816 } 817 TRACE("%s: hardware mode will actually be %dx%d\n", __func__, 818 hardwareTarget.virtual_width, hardwareTarget.virtual_height); 819 if ((hardwareTarget.virtual_width <= target.virtual_width 820 && hardwareTarget.virtual_height <= target.virtual_height 821 && hardwareTarget.space <= target.space) 822 || intel_propose_display_mode(&hardwareTarget, mode, mode)) { 823 hardwareTarget = target; 824 } else 825 needsScaling = true; 826 } else { 827 // We don't have EDID data, try to set the requested mode directly 828 hardwareTarget = target; 829 } 830 831 pll_divisors divisors; 832 if (needsScaling) 833 compute_pll_divisors(hardwareTarget, divisors, true); 834 else 835 compute_pll_divisors(target, divisors, true); 836 837 uint32 dpll = DISPLAY_PLL_NO_VGA_CONTROL | DISPLAY_PLL_ENABLED; 838 if (gInfo->shared_info->device_type.InFamily(INTEL_TYPE_9xx)) { 839 dpll |= LVDS_PLL_MODE_LVDS; 840 // DPLL mode LVDS for i915+ 841 } 842 843 // Compute bitmask from p1 value 844 if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) { 845 dpll |= (1 << (divisors.post1 - 1)) 846 << DISPLAY_PLL_IGD_POST1_DIVISOR_SHIFT; 847 } else { 848 dpll |= (1 << (divisors.post1 - 1)) 849 << DISPLAY_PLL_POST1_DIVISOR_SHIFT; 850 } 851 switch (divisors.post2) { 852 case 5: 853 case 7: 854 dpll |= DISPLAY_PLL_DIVIDE_HIGH; 855 break; 856 } 857 858 // Disable panel fitting, but enable 8 to 6-bit dithering 859 write32(INTEL_PANEL_FIT_CONTROL, 0x4); 860 // TODO: do not do this if the connected panel is 24-bit 861 // (I don't know how to detect that) 862 863 if ((dpll & DISPLAY_PLL_ENABLED) != 0) { 864 if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) { 865 write32(INTEL_DISPLAY_B_PLL_DIVISOR_0, 866 (((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT) 867 & DISPLAY_PLL_IGD_N_DIVISOR_MASK) 868 | (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT) 869 & DISPLAY_PLL_IGD_M2_DIVISOR_MASK)); 870 } else { 871 write32(INTEL_DISPLAY_B_PLL_DIVISOR_0, 872 (((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT) 873 & DISPLAY_PLL_N_DIVISOR_MASK) 874 | (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT) 875 & DISPLAY_PLL_M1_DIVISOR_MASK) 876 | (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT) 877 & DISPLAY_PLL_M2_DIVISOR_MASK)); 878 } 879 write32(INTEL_DISPLAY_B_PLL, dpll & ~DISPLAY_PLL_ENABLED); 880 read32(INTEL_DISPLAY_B_PLL); 881 spin(150); 882 } 883 884 uint32 lvds = read32(INTEL_DISPLAY_LVDS_PORT) | LVDS_PORT_EN 885 | LVDS_A0A2_CLKA_POWER_UP | LVDS_PIPEB_SELECT; 886 887 lvds |= LVDS_18BIT_DITHER; 888 // TODO: do not do this if the connected panel is 24-bit 889 // (I don't know how to detect that) 890 891 float referenceClock = gInfo->shared_info->pll_info.reference_frequency 892 / 1000.0f; 893 894 // Set the B0-B3 data pairs corresponding to whether we're going to 895 // set the DPLLs for dual-channel mode or not. 896 if (divisors.post2 == LVDS_POST2_RATE_FAST) 897 lvds |= LVDS_B0B3PAIRS_POWER_UP | LVDS_CLKB_POWER_UP; 898 else 899 lvds &= ~(LVDS_B0B3PAIRS_POWER_UP | LVDS_CLKB_POWER_UP); 900 901 write32(INTEL_DISPLAY_LVDS_PORT, lvds); 902 read32(INTEL_DISPLAY_LVDS_PORT); 903 904 if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) { 905 write32(INTEL_DISPLAY_B_PLL_DIVISOR_0, 906 (((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT) 907 & DISPLAY_PLL_IGD_N_DIVISOR_MASK) 908 | (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT) 909 & DISPLAY_PLL_IGD_M2_DIVISOR_MASK)); 910 } else { 911 write32(INTEL_DISPLAY_B_PLL_DIVISOR_0, 912 (((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT) 913 & DISPLAY_PLL_N_DIVISOR_MASK) 914 | (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT) 915 & DISPLAY_PLL_M1_DIVISOR_MASK) 916 | (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT) 917 & DISPLAY_PLL_M2_DIVISOR_MASK)); 918 } 919 920 write32(INTEL_DISPLAY_B_PLL, dpll); 921 read32(INTEL_DISPLAY_B_PLL); 922 923 // Wait for the clocks to stabilize 924 spin(150); 925 926 if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_96x)) { 927 float adjusted = ((referenceClock * divisors.m) / divisors.n) 928 / divisors.post; 929 uint32 pixelMultiply; 930 if (needsScaling) { 931 pixelMultiply = uint32(adjusted 932 / (hardwareTarget.timing.pixel_clock / 1000.0f)); 933 } else { 934 pixelMultiply = uint32(adjusted 935 / (target.timing.pixel_clock / 1000.0f)); 936 } 937 938 write32(INTEL_DISPLAY_B_PLL_MULTIPLIER_DIVISOR, (0 << 24) 939 | ((pixelMultiply - 1) << 8)); 940 } else 941 write32(INTEL_DISPLAY_B_PLL, dpll); 942 943 read32(INTEL_DISPLAY_B_PLL); 944 spin(150); 945 946 // update timing parameters 947 if (needsScaling) { 948 // TODO: Alternatively, it should be possible to use the panel 949 // fitter and scale the picture. 950 951 // TODO: Perform some sanity check, for example if the target is 952 // wider than the hardware mode we end up with negative borders and 953 // broken timings 954 uint32 borderWidth = hardwareTarget.timing.h_display 955 - target.timing.h_display; 956 957 uint32 syncWidth = hardwareTarget.timing.h_sync_end 958 - hardwareTarget.timing.h_sync_start; 959 960 uint32 syncCenter = target.timing.h_display 961 + (hardwareTarget.timing.h_total 962 - target.timing.h_display) / 2; 963 964 write32(INTEL_DISPLAY_B_HTOTAL, 965 ((uint32)(hardwareTarget.timing.h_total - 1) << 16) 966 | ((uint32)target.timing.h_display - 1)); 967 write32(INTEL_DISPLAY_B_HBLANK, 968 ((uint32)(hardwareTarget.timing.h_total - borderWidth / 2 - 1) 969 << 16) 970 | ((uint32)target.timing.h_display + borderWidth / 2 - 1)); 971 write32(INTEL_DISPLAY_B_HSYNC, 972 ((uint32)(syncCenter + syncWidth / 2 - 1) << 16) 973 | ((uint32)syncCenter - syncWidth / 2 - 1)); 974 975 uint32 borderHeight = hardwareTarget.timing.v_display 976 - target.timing.v_display; 977 978 uint32 syncHeight = hardwareTarget.timing.v_sync_end 979 - hardwareTarget.timing.v_sync_start; 980 981 syncCenter = target.timing.v_display 982 + (hardwareTarget.timing.v_total 983 - target.timing.v_display) / 2; 984 985 write32(INTEL_DISPLAY_B_VTOTAL, 986 ((uint32)(hardwareTarget.timing.v_total - 1) << 16) 987 | ((uint32)target.timing.v_display - 1)); 988 write32(INTEL_DISPLAY_B_VBLANK, 989 ((uint32)(hardwareTarget.timing.v_total - borderHeight / 2 - 1) 990 << 16) 991 | ((uint32)target.timing.v_display 992 + borderHeight / 2 - 1)); 993 write32(INTEL_DISPLAY_B_VSYNC, 994 ((uint32)(syncCenter + syncHeight / 2 - 1) << 16) 995 | ((uint32)syncCenter - syncHeight / 2 - 1)); 996 997 // This is useful for debugging: it sets the border to red, so you 998 // can see what is border and what is porch (black area around the 999 // sync) 1000 // write32(0x61020, 0x00FF0000); 1001 } else { 1002 write32(INTEL_DISPLAY_B_HTOTAL, 1003 ((uint32)(target.timing.h_total - 1) << 16) 1004 | ((uint32)target.timing.h_display - 1)); 1005 write32(INTEL_DISPLAY_B_HBLANK, 1006 ((uint32)(target.timing.h_total - 1) << 16) 1007 | ((uint32)target.timing.h_display - 1)); 1008 write32(INTEL_DISPLAY_B_HSYNC, 1009 ((uint32)(target.timing.h_sync_end - 1) << 16) 1010 | ((uint32)target.timing.h_sync_start - 1)); 1011 1012 write32(INTEL_DISPLAY_B_VTOTAL, 1013 ((uint32)(target.timing.v_total - 1) << 16) 1014 | ((uint32)target.timing.v_display - 1)); 1015 write32(INTEL_DISPLAY_B_VBLANK, 1016 ((uint32)(target.timing.v_total - 1) << 16) 1017 | ((uint32)target.timing.v_display - 1)); 1018 write32(INTEL_DISPLAY_B_VSYNC, ( 1019 (uint32)(target.timing.v_sync_end - 1) << 16) 1020 | ((uint32)target.timing.v_sync_start - 1)); 1021 } 1022 1023 write32(INTEL_DISPLAY_B_IMAGE_SIZE, 1024 ((uint32)(target.virtual_width - 1) << 16) 1025 | ((uint32)target.virtual_height - 1)); 1026 1027 write32(INTEL_DISPLAY_B_POS, 0); 1028 write32(INTEL_DISPLAY_B_PIPE_SIZE, 1029 ((uint32)(target.timing.v_display - 1) << 16) 1030 | ((uint32)target.timing.h_display - 1)); 1031 1032 write32(INTEL_DISPLAY_B_CONTROL, (read32(INTEL_DISPLAY_B_CONTROL) 1033 & ~(DISPLAY_CONTROL_COLOR_MASK | DISPLAY_CONTROL_GAMMA)) 1034 | colorMode); 1035 1036 write32(INTEL_DISPLAY_B_PIPE_CONTROL, 1037 read32(INTEL_DISPLAY_B_PIPE_CONTROL) | DISPLAY_PIPE_ENABLED); 1038 read32(INTEL_DISPLAY_B_PIPE_CONTROL); 1039 } 1040 1041 if ((gInfo->head_mode & HEAD_MODE_A_ANALOG) != 0) { 1042 pll_divisors divisors; 1043 compute_pll_divisors(target, divisors, false); 1044 1045 if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) { 1046 write32(INTEL_DISPLAY_A_PLL_DIVISOR_0, 1047 (((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT) 1048 & DISPLAY_PLL_IGD_N_DIVISOR_MASK) 1049 | (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT) 1050 & DISPLAY_PLL_IGD_M2_DIVISOR_MASK)); 1051 } else { 1052 write32(INTEL_DISPLAY_A_PLL_DIVISOR_0, 1053 (((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT) 1054 & DISPLAY_PLL_N_DIVISOR_MASK) 1055 | (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT) 1056 & DISPLAY_PLL_M1_DIVISOR_MASK) 1057 | (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT) 1058 & DISPLAY_PLL_M2_DIVISOR_MASK)); 1059 } 1060 1061 uint32 pll = DISPLAY_PLL_ENABLED | DISPLAY_PLL_NO_VGA_CONTROL; 1062 if (gInfo->shared_info->device_type.InFamily(INTEL_TYPE_9xx)) { 1063 if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) { 1064 pll |= ((1 << (divisors.post1 - 1)) 1065 << DISPLAY_PLL_IGD_POST1_DIVISOR_SHIFT) 1066 & DISPLAY_PLL_IGD_POST1_DIVISOR_MASK; 1067 } else { 1068 pll |= ((1 << (divisors.post1 - 1)) 1069 << DISPLAY_PLL_POST1_DIVISOR_SHIFT) 1070 & DISPLAY_PLL_9xx_POST1_DIVISOR_MASK; 1071 // pll |= ((divisors.post1 - 1) << DISPLAY_PLL_POST1_DIVISOR_SHIFT) 1072 // & DISPLAY_PLL_9xx_POST1_DIVISOR_MASK; 1073 } 1074 if (divisors.post2_high) 1075 pll |= DISPLAY_PLL_DIVIDE_HIGH; 1076 1077 pll |= DISPLAY_PLL_MODE_ANALOG; 1078 1079 if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_96x)) 1080 pll |= 6 << DISPLAY_PLL_PULSE_PHASE_SHIFT; 1081 } else { 1082 if (!divisors.post2_high) 1083 pll |= DISPLAY_PLL_DIVIDE_4X; 1084 1085 pll |= DISPLAY_PLL_2X_CLOCK; 1086 1087 if (divisors.post1 > 2) { 1088 pll |= ((divisors.post1 - 2) << DISPLAY_PLL_POST1_DIVISOR_SHIFT) 1089 & DISPLAY_PLL_POST1_DIVISOR_MASK; 1090 } else 1091 pll |= DISPLAY_PLL_POST1_DIVIDE_2; 1092 } 1093 1094 write32(INTEL_DISPLAY_A_PLL, pll); 1095 read32(INTEL_DISPLAY_A_PLL); 1096 spin(150); 1097 write32(INTEL_DISPLAY_A_PLL, pll); 1098 read32(INTEL_DISPLAY_A_PLL); 1099 spin(150); 1100 1101 // update timing parameters 1102 write32(INTEL_DISPLAY_A_HTOTAL, 1103 ((uint32)(target.timing.h_total - 1) << 16) 1104 | ((uint32)target.timing.h_display - 1)); 1105 write32(INTEL_DISPLAY_A_HBLANK, 1106 ((uint32)(target.timing.h_total - 1) << 16) 1107 | ((uint32)target.timing.h_display - 1)); 1108 write32(INTEL_DISPLAY_A_HSYNC, 1109 ((uint32)(target.timing.h_sync_end - 1) << 16) 1110 | ((uint32)target.timing.h_sync_start - 1)); 1111 1112 write32(INTEL_DISPLAY_A_VTOTAL, 1113 ((uint32)(target.timing.v_total - 1) << 16) 1114 | ((uint32)target.timing.v_display - 1)); 1115 write32(INTEL_DISPLAY_A_VBLANK, 1116 ((uint32)(target.timing.v_total - 1) << 16) 1117 | ((uint32)target.timing.v_display - 1)); 1118 write32(INTEL_DISPLAY_A_VSYNC, 1119 ((uint32)(target.timing.v_sync_end - 1) << 16) 1120 | ((uint32)target.timing.v_sync_start - 1)); 1121 1122 write32(INTEL_DISPLAY_A_IMAGE_SIZE, 1123 ((uint32)(target.virtual_width - 1) << 16) 1124 | ((uint32)target.virtual_height - 1)); 1125 1126 write32(INTEL_DISPLAY_A_ANALOG_PORT, 1127 (read32(INTEL_DISPLAY_A_ANALOG_PORT) 1128 & ~(DISPLAY_MONITOR_POLARITY_MASK 1129 | DISPLAY_MONITOR_VGA_POLARITY)) 1130 | ((target.timing.flags & B_POSITIVE_HSYNC) != 0 1131 ? DISPLAY_MONITOR_POSITIVE_HSYNC : 0) 1132 | ((target.timing.flags & B_POSITIVE_VSYNC) != 0 1133 ? DISPLAY_MONITOR_POSITIVE_VSYNC : 0)); 1134 1135 // TODO: verify the two comments below: the X driver doesn't seem to 1136 // care about both of them! 1137 1138 // These two have to be set for display B, too - this obviously means 1139 // that the second head always must adopt the color space of the first 1140 // head. 1141 write32(INTEL_DISPLAY_A_CONTROL, (read32(INTEL_DISPLAY_A_CONTROL) 1142 & ~(DISPLAY_CONTROL_COLOR_MASK | DISPLAY_CONTROL_GAMMA)) 1143 | colorMode); 1144 1145 if ((gInfo->head_mode & HEAD_MODE_B_DIGITAL) != 0) { 1146 write32(INTEL_DISPLAY_B_IMAGE_SIZE, 1147 ((uint32)(target.virtual_width - 1) << 16) 1148 | ((uint32)target.virtual_height - 1)); 1149 1150 write32(INTEL_DISPLAY_B_CONTROL, (read32(INTEL_DISPLAY_B_CONTROL) 1151 & ~(DISPLAY_CONTROL_COLOR_MASK | DISPLAY_CONTROL_GAMMA)) 1152 | colorMode); 1153 } 1154 } 1155 1156 set_display_power_mode(sharedInfo.dpms_mode); 1157 1158 // Changing bytes per row seems to be ignored if the plane/pipe is turned 1159 // off 1160 1161 if (gInfo->head_mode & HEAD_MODE_A_ANALOG) 1162 write32(INTEL_DISPLAY_A_BYTES_PER_ROW, bytesPerRow); 1163 if (gInfo->head_mode & HEAD_MODE_B_DIGITAL) 1164 write32(INTEL_DISPLAY_B_BYTES_PER_ROW, bytesPerRow); 1165 1166 set_frame_buffer_base(); 1167 // triggers writing back double-buffered registers 1168 1169 // update shared info 1170 sharedInfo.bytes_per_row = bytesPerRow; 1171 sharedInfo.current_mode = target; 1172 sharedInfo.bits_per_pixel = bitsPerPixel; 1173 1174 return B_OK; 1175 } 1176 1177 1178 status_t 1179 intel_get_display_mode(display_mode* _currentMode) 1180 { 1181 CALLED(); 1182 1183 retrieve_current_mode(*_currentMode, INTEL_DISPLAY_A_PLL); 1184 return B_OK; 1185 } 1186 1187 1188 status_t 1189 intel_get_edid_info(void* info, size_t size, uint32* _version) 1190 { 1191 CALLED(); 1192 1193 if (!gInfo->has_edid) 1194 return B_ERROR; 1195 if (size < sizeof(struct edid1_info)) 1196 return B_BUFFER_OVERFLOW; 1197 1198 memcpy(info, &gInfo->edid_info, sizeof(struct edid1_info)); 1199 *_version = EDID_VERSION_1; 1200 return B_OK; 1201 } 1202 1203 1204 status_t 1205 intel_get_frame_buffer_config(frame_buffer_config* config) 1206 { 1207 CALLED(); 1208 1209 uint32 offset = gInfo->shared_info->frame_buffer_offset; 1210 1211 config->frame_buffer = gInfo->shared_info->graphics_memory + offset; 1212 config->frame_buffer_dma 1213 = (uint8*)gInfo->shared_info->physical_graphics_memory + offset; 1214 config->bytes_per_row = gInfo->shared_info->bytes_per_row; 1215 1216 return B_OK; 1217 } 1218 1219 1220 status_t 1221 intel_get_pixel_clock_limits(display_mode* mode, uint32* _low, uint32* _high) 1222 { 1223 CALLED(); 1224 1225 if (_low != NULL) { 1226 // lower limit of about 48Hz vertical refresh 1227 uint32 totalClocks = (uint32)mode->timing.h_total 1228 * (uint32)mode->timing.v_total; 1229 uint32 low = (totalClocks * 48L) / 1000L; 1230 if (low < gInfo->shared_info->pll_info.min_frequency) 1231 low = gInfo->shared_info->pll_info.min_frequency; 1232 else if (low > gInfo->shared_info->pll_info.max_frequency) 1233 return B_ERROR; 1234 1235 *_low = low; 1236 } 1237 1238 if (_high != NULL) 1239 *_high = gInfo->shared_info->pll_info.max_frequency; 1240 1241 return B_OK; 1242 } 1243 1244 1245 status_t 1246 intel_move_display(uint16 horizontalStart, uint16 verticalStart) 1247 { 1248 CALLED(); 1249 1250 intel_shared_info &sharedInfo = *gInfo->shared_info; 1251 Autolock locker(sharedInfo.accelerant_lock); 1252 1253 display_mode &mode = sharedInfo.current_mode; 1254 1255 if (horizontalStart + mode.timing.h_display > mode.virtual_width 1256 || verticalStart + mode.timing.v_display > mode.virtual_height) 1257 return B_BAD_VALUE; 1258 1259 mode.h_display_start = horizontalStart; 1260 mode.v_display_start = verticalStart; 1261 1262 set_frame_buffer_base(); 1263 1264 return B_OK; 1265 } 1266 1267 1268 status_t 1269 intel_get_timing_constraints(display_timing_constraints* constraints) 1270 { 1271 CALLED(); 1272 return B_ERROR; 1273 } 1274 1275 1276 void 1277 intel_set_indexed_colors(uint count, uint8 first, uint8* colors, uint32 flags) 1278 { 1279 TRACE("%s(colors = %p, first = %u)\n", __func__, colors, first); 1280 1281 if (colors == NULL) 1282 return; 1283 1284 Autolock locker(gInfo->shared_info->accelerant_lock); 1285 1286 for (; count-- > 0; first++) { 1287 uint32 color = colors[0] << 16 | colors[1] << 8 | colors[2]; 1288 colors += 3; 1289 1290 write32(INTEL_DISPLAY_A_PALETTE + first * sizeof(uint32), color); 1291 write32(INTEL_DISPLAY_B_PALETTE + first * sizeof(uint32), color); 1292 } 1293 } 1294 1295