1 /* 2 * Copyright 2006-2010, 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 <math.h> 14 #include <stdio.h> 15 #include <string.h> 16 17 #include <Debug.h> 18 19 #include <create_display_modes.h> 20 #include <ddc.h> 21 #include <edid.h> 22 #include <validate_display_mode.h> 23 24 #include "accelerant_protos.h" 25 #include "accelerant.h" 26 #include "pll.h" 27 #include "Ports.h" 28 #include "utility.h" 29 30 31 #undef TRACE 32 #define TRACE_MODE 33 #ifdef TRACE_MODE 34 # define TRACE(x...) _sPrintf("intel_extreme: " x) 35 #else 36 # define TRACE(x...) 37 #endif 38 39 #define ERROR(x...) _sPrintf("intel_extreme: " x) 40 #define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__) 41 42 43 #if 0 44 // This hack needs to die. Leaving in for a little while 45 // incase we *really* need it. 46 static void 47 retrieve_current_mode(display_mode& mode, uint32 pllRegister) 48 { 49 uint32 pll = read32(pllRegister); 50 uint32 pllDivisor; 51 uint32 hTotalRegister; 52 uint32 vTotalRegister; 53 uint32 hSyncRegister; 54 uint32 vSyncRegister; 55 uint32 imageSizeRegister; 56 uint32 controlRegister; 57 58 if (pllRegister == INTEL_DISPLAY_A_PLL) { 59 pllDivisor = read32((pll & DISPLAY_PLL_DIVISOR_1) != 0 60 ? INTEL_DISPLAY_A_PLL_DIVISOR_1 : INTEL_DISPLAY_A_PLL_DIVISOR_0); 61 62 hTotalRegister = INTEL_DISPLAY_A_HTOTAL; 63 vTotalRegister = INTEL_DISPLAY_A_VTOTAL; 64 hSyncRegister = INTEL_DISPLAY_A_HSYNC; 65 vSyncRegister = INTEL_DISPLAY_A_VSYNC; 66 imageSizeRegister = INTEL_DISPLAY_A_IMAGE_SIZE; 67 controlRegister = INTEL_DISPLAY_A_CONTROL; 68 } else if (pllRegister == INTEL_DISPLAY_B_PLL) { 69 pllDivisor = read32((pll & DISPLAY_PLL_DIVISOR_1) != 0 70 ? INTEL_DISPLAY_B_PLL_DIVISOR_1 : INTEL_DISPLAY_B_PLL_DIVISOR_0); 71 72 hTotalRegister = INTEL_DISPLAY_B_HTOTAL; 73 vTotalRegister = INTEL_DISPLAY_B_VTOTAL; 74 hSyncRegister = INTEL_DISPLAY_B_HSYNC; 75 vSyncRegister = INTEL_DISPLAY_B_VSYNC; 76 imageSizeRegister = INTEL_DISPLAY_B_IMAGE_SIZE; 77 controlRegister = INTEL_DISPLAY_B_CONTROL; 78 } else { 79 ERROR("%s: PLL not supported\n", __func__); 80 return; 81 } 82 83 pll_divisors divisors; 84 if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN)) { 85 divisors.m1 = 0; 86 divisors.m2 = (pllDivisor & DISPLAY_PLL_IGD_M2_DIVISOR_MASK) 87 >> DISPLAY_PLL_M2_DIVISOR_SHIFT; 88 divisors.n = ((pllDivisor & DISPLAY_PLL_IGD_N_DIVISOR_MASK) 89 >> DISPLAY_PLL_N_DIVISOR_SHIFT) - 1; 90 } else { 91 divisors.m1 = (pllDivisor & DISPLAY_PLL_M1_DIVISOR_MASK) 92 >> DISPLAY_PLL_M1_DIVISOR_SHIFT; 93 divisors.m2 = (pllDivisor & DISPLAY_PLL_M2_DIVISOR_MASK) 94 >> DISPLAY_PLL_M2_DIVISOR_SHIFT; 95 divisors.n = (pllDivisor & DISPLAY_PLL_N_DIVISOR_MASK) 96 >> DISPLAY_PLL_N_DIVISOR_SHIFT; 97 } 98 99 pll_limits limits; 100 get_pll_limits(&limits, false); 101 // TODO: Detect LVDS connector vs assume no 102 103 if (gInfo->shared_info->device_type.Generation() >= 3) { 104 105 if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN)) { 106 divisors.post1 = (pll & DISPLAY_PLL_IGD_POST1_DIVISOR_MASK) 107 >> DISPLAY_PLL_IGD_POST1_DIVISOR_SHIFT; 108 } else { 109 divisors.post1 = (pll & DISPLAY_PLL_9xx_POST1_DIVISOR_MASK) 110 >> DISPLAY_PLL_POST1_DIVISOR_SHIFT; 111 } 112 113 if (pllRegister == INTEL_DISPLAY_B_PLL 114 && !gInfo->shared_info->device_type.InGroup(INTEL_GROUP_96x)) { 115 // TODO: Fix this? Need to support dual channel LVDS. 116 divisors.post2 = LVDS_POST2_RATE_SLOW; 117 } else { 118 if ((pll & DISPLAY_PLL_DIVIDE_HIGH) != 0) 119 divisors.post2 = limits.max.post2; 120 else 121 divisors.post2 = limits.min.post2; 122 } 123 } else { 124 // 8xx 125 divisors.post1 = (pll & DISPLAY_PLL_POST1_DIVISOR_MASK) 126 >> DISPLAY_PLL_POST1_DIVISOR_SHIFT; 127 128 if ((pll & DISPLAY_PLL_DIVIDE_4X) != 0) 129 divisors.post2 = limits.max.post2; 130 else 131 divisors.post2 = limits.min.post2; 132 } 133 134 divisors.m = 5 * divisors.m1 + divisors.m2; 135 divisors.post = divisors.post1 * divisors.post2; 136 137 float referenceClock 138 = gInfo->shared_info->pll_info.reference_frequency / 1000.0f; 139 float pixelClock 140 = ((referenceClock * divisors.m) / divisors.n) / divisors.post; 141 142 // timing 143 144 mode.timing.pixel_clock = uint32(pixelClock * 1000); 145 mode.timing.flags = 0; 146 147 uint32 value = read32(hTotalRegister); 148 mode.timing.h_total = (value >> 16) + 1; 149 mode.timing.h_display = (value & 0xffff) + 1; 150 151 value = read32(hSyncRegister); 152 mode.timing.h_sync_end = (value >> 16) + 1; 153 mode.timing.h_sync_start = (value & 0xffff) + 1; 154 155 value = read32(vTotalRegister); 156 mode.timing.v_total = (value >> 16) + 1; 157 mode.timing.v_display = (value & 0xffff) + 1; 158 159 value = read32(vSyncRegister); 160 mode.timing.v_sync_end = (value >> 16) + 1; 161 mode.timing.v_sync_start = (value & 0xffff) + 1; 162 163 // image size and color space 164 165 value = read32(imageSizeRegister); 166 mode.virtual_width = (value >> 16) + 1; 167 mode.virtual_height = (value & 0xffff) + 1; 168 169 // using virtual size based on image size is the 'proper' way to do it, 170 // however the bios appears to be suggesting scaling or somesuch, so ignore 171 // the proper virtual dimension for now if they'd suggest a smaller size. 172 if (mode.virtual_width < mode.timing.h_display) 173 mode.virtual_width = mode.timing.h_display; 174 if (mode.virtual_height < mode.timing.v_display) 175 mode.virtual_height = mode.timing.v_display; 176 177 value = read32(controlRegister); 178 switch (value & DISPLAY_CONTROL_COLOR_MASK) { 179 case DISPLAY_CONTROL_RGB32: 180 default: 181 mode.space = B_RGB32; 182 break; 183 case DISPLAY_CONTROL_RGB16: 184 mode.space = B_RGB16; 185 break; 186 case DISPLAY_CONTROL_RGB15: 187 mode.space = B_RGB15; 188 break; 189 case DISPLAY_CONTROL_CMAP8: 190 mode.space = B_CMAP8; 191 break; 192 } 193 194 mode.h_display_start = 0; 195 mode.v_display_start = 0; 196 mode.flags = B_8_BIT_DAC | B_HARDWARE_CURSOR | B_PARALLEL_ACCESS 197 | B_DPMS | B_SUPPORTS_OVERLAYS; 198 } 199 #endif 200 201 202 static void 203 get_color_space_format(const display_mode &mode, uint32 &colorMode, 204 uint32 &bytesPerRow, uint32 &bitsPerPixel) 205 { 206 uint32 bytesPerPixel; 207 208 switch (mode.space) { 209 case B_RGB32_LITTLE: 210 colorMode = DISPLAY_CONTROL_RGB32; 211 bytesPerPixel = 4; 212 bitsPerPixel = 32; 213 break; 214 case B_RGB16_LITTLE: 215 colorMode = DISPLAY_CONTROL_RGB16; 216 bytesPerPixel = 2; 217 bitsPerPixel = 16; 218 break; 219 case B_RGB15_LITTLE: 220 colorMode = DISPLAY_CONTROL_RGB15; 221 bytesPerPixel = 2; 222 bitsPerPixel = 15; 223 break; 224 case B_CMAP8: 225 default: 226 colorMode = DISPLAY_CONTROL_CMAP8; 227 bytesPerPixel = 1; 228 bitsPerPixel = 8; 229 break; 230 } 231 232 bytesPerRow = mode.virtual_width * bytesPerPixel; 233 234 // Make sure bytesPerRow is a multiple of 64 235 // TODO: check if the older chips have the same restriction! 236 if ((bytesPerRow & 63) != 0) 237 bytesPerRow = (bytesPerRow + 63) & ~63; 238 } 239 240 241 static bool 242 sanitize_display_mode(display_mode& mode) 243 { 244 // Some cards only support even pixel counts, while others require an odd 245 // one. 246 uint16 pixelCount = 1; 247 if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_Gxx) 248 || gInfo->shared_info->device_type.InGroup(INTEL_GROUP_96x) 249 || gInfo->shared_info->device_type.InGroup(INTEL_GROUP_94x) 250 || gInfo->shared_info->device_type.InGroup(INTEL_GROUP_91x) 251 || gInfo->shared_info->device_type.InFamily(INTEL_FAMILY_8xx) 252 || gInfo->shared_info->device_type.InFamily(INTEL_FAMILY_7xx)) { 253 pixelCount = 2; 254 } 255 256 // TODO: verify constraints - these are more or less taken from the 257 // radeon driver! 258 display_constraints constraints = { 259 // resolution 260 320, 8192, 200, 4096, 261 // pixel clock 262 gInfo->shared_info->pll_info.min_frequency, 263 gInfo->shared_info->pll_info.max_frequency, 264 // horizontal 265 {pixelCount, 0, 8160, 32, 8192, 0, 8192}, 266 {1, 1, 4092, 2, 63, 1, 4096} 267 }; 268 269 return sanitize_display_mode(mode, constraints, 270 gInfo->has_edid ? &gInfo->edid_info : NULL); 271 } 272 273 274 // #pragma mark - 275 276 277 static void 278 set_frame_buffer_registers(uint32 baseRegister, uint32 surfaceRegister) 279 { 280 intel_shared_info &sharedInfo = *gInfo->shared_info; 281 display_mode &mode = gInfo->current_mode; 282 283 if (sharedInfo.device_type.InGroup(INTEL_GROUP_96x) 284 || sharedInfo.device_type.InGroup(INTEL_GROUP_G4x) 285 || sharedInfo.device_type.InGroup(INTEL_GROUP_ILK) 286 || sharedInfo.device_type.InFamily(INTEL_FAMILY_SER5) 287 || sharedInfo.device_type.InFamily(INTEL_FAMILY_SOC0)) { 288 write32(baseRegister, mode.v_display_start * sharedInfo.bytes_per_row 289 + mode.h_display_start * (sharedInfo.bits_per_pixel + 7) / 8); 290 read32(baseRegister); 291 write32(surfaceRegister, sharedInfo.frame_buffer_offset); 292 read32(surfaceRegister); 293 } else { 294 write32(baseRegister, sharedInfo.frame_buffer_offset 295 + mode.v_display_start * sharedInfo.bytes_per_row 296 + mode.h_display_start * (sharedInfo.bits_per_pixel + 7) / 8); 297 read32(baseRegister); 298 } 299 } 300 301 302 void 303 set_frame_buffer_base() 304 { 305 // TODO we always set both displays to the same address. When we support 306 // multiple framebuffers, they should get different addresses here. 307 set_frame_buffer_registers(INTEL_DISPLAY_A_BASE, INTEL_DISPLAY_A_SURFACE); 308 set_frame_buffer_registers(INTEL_DISPLAY_B_BASE, INTEL_DISPLAY_B_SURFACE); 309 } 310 311 312 /*! Creates the initial mode list of the primary accelerant. 313 It's called from intel_init_accelerant(). 314 */ 315 status_t 316 create_mode_list(void) 317 { 318 CALLED(); 319 320 for (uint32 i = 0; i < gInfo->port_count; i++) { 321 if (gInfo->ports[i] == NULL) 322 continue; 323 324 status_t status = gInfo->ports[i]->GetEDID(&gInfo->edid_info); 325 if (status == B_OK) 326 gInfo->has_edid = true; 327 } 328 329 display_mode* list; 330 uint32 count = 0; 331 332 // If no EDID, but have vbt from driver, use that mode 333 if (!gInfo->has_edid && gInfo->shared_info->got_vbt) { 334 // We could not read any EDID info. Fallback to creating a list with 335 // only the mode set up by the BIOS. 336 337 // TODO: support lower modes via scaling and windowing 338 gInfo->mode_list_area = create_display_modes("intel extreme modes", 339 NULL, &gInfo->shared_info->panel_mode, 1, NULL, 0, NULL, 340 &list, &count); 341 } else { 342 // Otherwise return the 'real' list of modes 343 gInfo->mode_list_area = create_display_modes("intel extreme modes", 344 gInfo->has_edid ? &gInfo->edid_info : NULL, NULL, 0, NULL, 0, NULL, 345 &list, &count); 346 } 347 348 if (gInfo->mode_list_area < B_OK) 349 return gInfo->mode_list_area; 350 351 gInfo->mode_list = list; 352 gInfo->shared_info->mode_list_area = gInfo->mode_list_area; 353 gInfo->shared_info->mode_count = count; 354 355 return B_OK; 356 } 357 358 359 void 360 wait_for_vblank(void) 361 { 362 acquire_sem_etc(gInfo->shared_info->vblank_sem, 1, B_RELATIVE_TIMEOUT, 363 25000); 364 // With the output turned off via DPMS, we might not get any interrupts 365 // anymore that's why we don't wait forever for it. 366 } 367 368 369 // #pragma mark - 370 371 372 uint32 373 intel_accelerant_mode_count(void) 374 { 375 CALLED(); 376 return gInfo->shared_info->mode_count; 377 } 378 379 380 status_t 381 intel_get_mode_list(display_mode* modeList) 382 { 383 CALLED(); 384 memcpy(modeList, gInfo->mode_list, 385 gInfo->shared_info->mode_count * sizeof(display_mode)); 386 return B_OK; 387 } 388 389 390 status_t 391 intel_propose_display_mode(display_mode* target, const display_mode* low, 392 const display_mode* high) 393 { 394 CALLED(); 395 396 // first search for the specified mode in the list, if no mode is found 397 // try to fix the target mode in sanitize_display_mode 398 // TODO: Only sanitize_display_mode should be used. However, at the moments 399 // the mode constraints are not optimal and do not work for all 400 // configurations. 401 for (uint32 i = 0; i < gInfo->shared_info->mode_count; i++) { 402 display_mode *mode = &gInfo->mode_list[i]; 403 404 // TODO: improve this, ie. adapt pixel clock to allowed values!!! 405 406 if (target->virtual_width != mode->virtual_width 407 || target->virtual_height != mode->virtual_height 408 || target->space != mode->space) { 409 continue; 410 } 411 412 *target = *mode; 413 return B_OK; 414 } 415 416 sanitize_display_mode(*target); 417 418 return is_display_mode_within_bounds(*target, *low, *high) 419 ? B_OK : B_BAD_VALUE; 420 } 421 422 423 status_t 424 intel_set_display_mode(display_mode* mode) 425 { 426 TRACE("%s(%" B_PRIu16 "x%" B_PRIu16 ")\n", __func__, 427 mode->virtual_width, mode->virtual_height); 428 429 if (mode == NULL) 430 return B_BAD_VALUE; 431 432 display_mode target = *mode; 433 434 // TODO: it may be acceptable to continue when using panel fitting or 435 // centering, since the data from propose_display_mode will not actually be 436 // used as is in this case. 437 if (sanitize_display_mode(target)) { 438 TRACE("Video mode was adjusted by sanitize_display_mode\n"); 439 TRACE("Initial mode: Hd %d Hs %d He %d Ht %d Vd %d Vs %d Ve %d Vt %d\n", 440 mode->timing.h_display, mode->timing.h_sync_start, 441 mode->timing.h_sync_end, mode->timing.h_total, 442 mode->timing.v_display, mode->timing.v_sync_start, 443 mode->timing.v_sync_end, mode->timing.v_total); 444 TRACE("Sanitized: Hd %d Hs %d He %d Ht %d Vd %d Vs %d Ve %d Vt %d\n", 445 target.timing.h_display, target.timing.h_sync_start, 446 target.timing.h_sync_end, target.timing.h_total, 447 target.timing.v_display, target.timing.v_sync_start, 448 target.timing.v_sync_end, target.timing.v_total); 449 } 450 451 uint32 colorMode, bytesPerRow, bitsPerPixel; 452 get_color_space_format(target, colorMode, bytesPerRow, bitsPerPixel); 453 454 // TODO: do not go further if the mode is identical to the current one. 455 // This would avoid the screen being off when switching workspaces when they 456 // have the same resolution. 457 458 intel_shared_info &sharedInfo = *gInfo->shared_info; 459 Autolock locker(sharedInfo.accelerant_lock); 460 461 // First register dump 462 //dump_registers(); 463 464 // TODO: This may not be neccesary 465 set_display_power_mode(B_DPMS_OFF); 466 467 // free old and allocate new frame buffer in graphics memory 468 469 intel_free_memory(sharedInfo.frame_buffer); 470 471 addr_t base; 472 if (intel_allocate_memory(bytesPerRow * target.virtual_height, 0, 473 base) < B_OK) { 474 // oh, how did that happen? Unfortunately, there is no really good way 475 // back 476 if (intel_allocate_memory(gInfo->current_mode.virtual_height 477 * sharedInfo.bytes_per_row, 0, base) == B_OK) { 478 sharedInfo.frame_buffer = base; 479 sharedInfo.frame_buffer_offset = base 480 - (addr_t)sharedInfo.graphics_memory; 481 set_frame_buffer_base(); 482 } 483 484 TRACE("%s: Failed to allocate framebuffer !\n", __func__); 485 return B_NO_MEMORY; 486 } 487 488 // clear frame buffer before using it 489 memset((uint8*)base, 0, bytesPerRow * target.virtual_height); 490 sharedInfo.frame_buffer = base; 491 sharedInfo.frame_buffer_offset = base - (addr_t)sharedInfo.graphics_memory; 492 493 #if 0 494 if ((gInfo->head_mode & HEAD_MODE_TESTING) != 0) { 495 // 1. Enable panel power as needed to retrieve panel configuration 496 // (use AUX VDD enable bit) 497 // skip, did detection already, might need that before that though 498 499 // 2. Enable PCH clock reference source and PCH SSC modulator, 500 // wait for warmup (Can be done anytime before enabling port) 501 // skip, most certainly already set up by bios to use other ports, 502 // will need for coldstart though 503 504 // 3. If enabling CPU embedded DisplayPort A: (Can be done anytime 505 // before enabling CPU pipe or port) 506 // a. Enable PCH 120MHz clock source output to CPU, wait for DMI 507 // latency 508 // b. Configure and enable CPU DisplayPort PLL in the DisplayPort A 509 // register, wait for warmup 510 // skip, not doing eDP right now, should go into 511 // EmbeddedDisplayPort class though 512 513 // 4. If enabling port on PCH: (Must be done before enabling CPU pipe 514 // or FDI) 515 // a. Enable PCH FDI Receiver PLL, wait for warmup plus DMI latency 516 // b. Switch from Rawclk to PCDclk in FDI Receiver (FDI A OR FDI B) 517 // c. [DevSNB] Enable CPU FDI Transmitter PLL, wait for warmup 518 // d. [DevILK] CPU FDI PLL is always on and does not need to be 519 // enabled 520 FDILink* link = pipe->FDILink(); 521 if (link != NULL) { 522 link->Receiver().EnablePLL(); 523 link->Receiver().SwitchClock(true); 524 link->Transmitter().EnablePLL(); 525 } 526 527 // 5. Enable CPU panel fitter if needed for hires, required for VGA 528 // (Can be done anytime before enabling CPU pipe) 529 PanelFitter* fitter = pipe->PanelFitter(); 530 if (fitter != NULL) 531 fitter->Enable(mode); 532 533 // 6. Configure CPU pipe timings, M/N/TU, and other pipe settings 534 // (Can be done anytime before enabling CPU pipe) 535 pll_divisors divisors; 536 compute_pll_divisors(target, divisors, false); 537 pipe->ConfigureTimings(divisors); 538 539 // 7. Enable CPU pipe 540 pipe->Enable(); 541 542 8. Configure and enable CPU planes (VGA or hires) 543 9. If enabling port on PCH: 544 // a. Program PCH FDI Receiver TU size same as Transmitter TU size for TU error checking 545 // b. Train FDI 546 // i. Set pre-emphasis and voltage (iterate if training steps fail) 547 ii. Enable CPU FDI Transmitter and PCH FDI Receiver with Training Pattern 1 enabled. 548 iii. Wait for FDI training pattern 1 time 549 iv. Read PCH FDI Receiver ISR ([DevIBX-B+] IIR) for bit lock in bit 8 (retry at least once if no lock) 550 v. Enable training pattern 2 on CPU FDI Transmitter and PCH FDI Receiver 551 vi. Wait for FDI training pattern 2 time 552 vii. Read PCH FDI Receiver ISR ([DevIBX-B+] IIR) for symbol lock in bit 9 (retry at least once if no 553 lock) 554 viii. Enable normal pixel output on CPU FDI Transmitter and PCH FDI Receiver 555 ix. Wait for FDI idle pattern time for link to become active 556 c. Configure and enable PCH DPLL, wait for PCH DPLL warmup (Can be done anytime before enabling 557 PCH transcoder) 558 d. [DevCPT] Configure DPLL SEL to set the DPLL to transcoder mapping and enable DPLL to the 559 transcoder. 560 e. [DevCPT] Configure DPLL_CTL DPLL_HDMI_multipler. 561 f. Configure PCH transcoder timings, M/N/TU, and other transcoder settings (should match CPU settings). 562 g. [DevCPT] Configure and enable Transcoder DisplayPort Control if DisplayPort will be used 563 h. Enable PCH transcoder 564 10. Enable ports (DisplayPort must enable in training pattern 1) 565 11. Enable panel power through panel power sequencing 566 12. Wait for panel power sequencing to reach enabled steady state 567 13. Disable panel power override 568 14. If DisplayPort, complete link training 569 15. Enable panel backlight 570 } 571 #endif 572 573 // make sure VGA display is disabled 574 write32(INTEL_VGA_DISPLAY_CONTROL, VGA_DISPLAY_DISABLED); 575 read32(INTEL_VGA_DISPLAY_CONTROL); 576 577 // Go over each port and set the display mode 578 for (uint32 i = 0; i < gInfo->port_count; i++) { 579 if (gInfo->ports[i] == NULL) 580 continue; 581 if (!gInfo->ports[i]->IsConnected()) 582 continue; 583 584 status_t status = gInfo->ports[i]->SetDisplayMode(&target, colorMode); 585 if (status != B_OK) 586 ERROR("%s: Unable to set display mode!\n", __func__); 587 } 588 589 TRACE("%s: Port configuration completed successfully!\n", __func__); 590 591 // We set the same color mode across all pipes 592 program_pipe_color_modes(colorMode); 593 594 // TODO: This may not be neccesary (see DPMS OFF at top) 595 set_display_power_mode(sharedInfo.dpms_mode); 596 597 // Changing bytes per row seems to be ignored if the plane/pipe is turned 598 // off 599 600 // Always set both pipes, just in case 601 // TODO rework this when we get multiple head support with different 602 // resolutions 603 write32(INTEL_DISPLAY_A_BYTES_PER_ROW, bytesPerRow); 604 write32(INTEL_DISPLAY_B_BYTES_PER_ROW, bytesPerRow); 605 606 // update shared info 607 gInfo->current_mode = target; 608 609 // TODO: move to gInfo 610 sharedInfo.bytes_per_row = bytesPerRow; 611 sharedInfo.bits_per_pixel = bitsPerPixel; 612 613 set_frame_buffer_base(); 614 // triggers writing back double-buffered registers 615 616 // Second register dump 617 //dump_registers(); 618 619 return B_OK; 620 } 621 622 623 status_t 624 intel_get_display_mode(display_mode* _currentMode) 625 { 626 CALLED(); 627 628 *_currentMode = gInfo->current_mode; 629 630 // This seems unreliable. We should always know the current_mode 631 //retrieve_current_mode(*_currentMode, INTEL_DISPLAY_A_PLL); 632 return B_OK; 633 } 634 635 636 status_t 637 intel_get_edid_info(void* info, size_t size, uint32* _version) 638 { 639 CALLED(); 640 641 if (!gInfo->has_edid) 642 return B_ERROR; 643 if (size < sizeof(struct edid1_info)) 644 return B_BUFFER_OVERFLOW; 645 646 memcpy(info, &gInfo->edid_info, sizeof(struct edid1_info)); 647 *_version = EDID_VERSION_1; 648 return B_OK; 649 } 650 651 652 static int32_t 653 intel_get_backlight_register(bool read) 654 { 655 if (gInfo->shared_info->pch_info == INTEL_PCH_NONE) 656 return MCH_BLC_PWM_CTL; 657 658 if (read) 659 return PCH_SBLC_PWM_CTL2; 660 else 661 return PCH_BLC_PWM_CTL; 662 } 663 664 665 status_t 666 intel_set_brightness(float brightness) 667 { 668 CALLED(); 669 670 if (brightness < 0 || brightness > 1) 671 return B_BAD_VALUE; 672 673 uint32_t period = read32(intel_get_backlight_register(true)) >> 16; 674 uint32_t duty = (uint32_t)(period * brightness) & 0xfffe; 675 /* Setting the low bit seems to give strange results on some Atom machines */ 676 write32(intel_get_backlight_register(false), duty | (period << 16)); 677 678 return B_OK; 679 } 680 681 682 status_t 683 intel_get_brightness(float* brightness) 684 { 685 CALLED(); 686 687 if (brightness == NULL) 688 return B_BAD_VALUE; 689 690 uint16_t period = read32(intel_get_backlight_register(true)) >> 16; 691 uint16_t duty = read32(intel_get_backlight_register(false)) & 0xffff; 692 *brightness = (float)duty / period; 693 694 return B_OK; 695 } 696 697 698 status_t 699 intel_get_frame_buffer_config(frame_buffer_config* config) 700 { 701 CALLED(); 702 703 uint32 offset = gInfo->shared_info->frame_buffer_offset; 704 705 config->frame_buffer = gInfo->shared_info->graphics_memory + offset; 706 config->frame_buffer_dma 707 = (uint8*)gInfo->shared_info->physical_graphics_memory + offset; 708 config->bytes_per_row = gInfo->shared_info->bytes_per_row; 709 710 return B_OK; 711 } 712 713 714 status_t 715 intel_get_pixel_clock_limits(display_mode* mode, uint32* _low, uint32* _high) 716 { 717 CALLED(); 718 719 if (_low != NULL) { 720 // lower limit of about 48Hz vertical refresh 721 uint32 totalClocks = (uint32)mode->timing.h_total 722 * (uint32)mode->timing.v_total; 723 uint32 low = (totalClocks * 48L) / 1000L; 724 if (low < gInfo->shared_info->pll_info.min_frequency) 725 low = gInfo->shared_info->pll_info.min_frequency; 726 else if (low > gInfo->shared_info->pll_info.max_frequency) 727 return B_ERROR; 728 729 *_low = low; 730 } 731 732 if (_high != NULL) 733 *_high = gInfo->shared_info->pll_info.max_frequency; 734 735 return B_OK; 736 } 737 738 739 status_t 740 intel_move_display(uint16 horizontalStart, uint16 verticalStart) 741 { 742 CALLED(); 743 744 intel_shared_info &sharedInfo = *gInfo->shared_info; 745 Autolock locker(sharedInfo.accelerant_lock); 746 747 display_mode &mode = gInfo->current_mode; 748 749 if (horizontalStart + mode.timing.h_display > mode.virtual_width 750 || verticalStart + mode.timing.v_display > mode.virtual_height) 751 return B_BAD_VALUE; 752 753 mode.h_display_start = horizontalStart; 754 mode.v_display_start = verticalStart; 755 756 set_frame_buffer_base(); 757 758 return B_OK; 759 } 760 761 762 status_t 763 intel_get_timing_constraints(display_timing_constraints* constraints) 764 { 765 CALLED(); 766 return B_ERROR; 767 } 768 769 770 void 771 intel_set_indexed_colors(uint count, uint8 first, uint8* colors, uint32 flags) 772 { 773 TRACE("%s(colors = %p, first = %u)\n", __func__, colors, first); 774 775 if (colors == NULL) 776 return; 777 778 Autolock locker(gInfo->shared_info->accelerant_lock); 779 780 for (; count-- > 0; first++) { 781 uint32 color = colors[0] << 16 | colors[1] << 8 | colors[2]; 782 colors += 3; 783 784 write32(INTEL_DISPLAY_A_PALETTE + first * sizeof(uint32), color); 785 write32(INTEL_DISPLAY_B_PALETTE + first * sizeof(uint32), color); 786 } 787 } 788