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 static void 44 get_color_space_format(const display_mode &mode, uint32 &colorMode, 45 uint32 &bytesPerRow, uint32 &bitsPerPixel) 46 { 47 uint32 bytesPerPixel; 48 49 switch (mode.space) { 50 case B_RGB32_LITTLE: 51 colorMode = DISPLAY_CONTROL_RGB32; 52 bytesPerPixel = 4; 53 bitsPerPixel = 32; 54 break; 55 case B_RGB16_LITTLE: 56 colorMode = DISPLAY_CONTROL_RGB16; 57 bytesPerPixel = 2; 58 bitsPerPixel = 16; 59 break; 60 case B_RGB15_LITTLE: 61 colorMode = DISPLAY_CONTROL_RGB15; 62 bytesPerPixel = 2; 63 bitsPerPixel = 15; 64 break; 65 case B_CMAP8: 66 default: 67 colorMode = DISPLAY_CONTROL_CMAP8; 68 bytesPerPixel = 1; 69 bitsPerPixel = 8; 70 break; 71 } 72 73 bytesPerRow = mode.virtual_width * bytesPerPixel; 74 75 // Make sure bytesPerRow is a multiple of 64 76 if ((bytesPerRow & 63) != 0) 77 bytesPerRow = (bytesPerRow + 63) & ~63; 78 } 79 80 81 static bool 82 sanitize_display_mode(display_mode& mode) 83 { 84 uint16 pixelCount = 1; 85 // Older cards require pixel count to be even 86 if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_Gxx) 87 || gInfo->shared_info->device_type.InGroup(INTEL_GROUP_96x) 88 || gInfo->shared_info->device_type.InGroup(INTEL_GROUP_94x) 89 || gInfo->shared_info->device_type.InGroup(INTEL_GROUP_91x) 90 || gInfo->shared_info->device_type.InFamily(INTEL_FAMILY_8xx)) { 91 pixelCount = 2; 92 } 93 94 display_constraints constraints = { 95 // resolution 96 320, 4096, 200, 4096, 97 // pixel clock 98 gInfo->shared_info->pll_info.min_frequency, 99 gInfo->shared_info->pll_info.max_frequency, 100 // horizontal 101 {pixelCount, 0, 8160, 32, 8192, 0, 8192}, 102 {1, 1, 8190, 2, 8192, 1, 8192} 103 }; 104 105 return sanitize_display_mode(mode, constraints, 106 gInfo->has_edid ? &gInfo->edid_info : NULL); 107 } 108 109 110 // #pragma mark - 111 112 113 static void 114 set_frame_buffer_registers(uint32 baseRegister, uint32 surfaceRegister) 115 { 116 intel_shared_info &sharedInfo = *gInfo->shared_info; 117 display_mode &mode = gInfo->current_mode; 118 119 if (sharedInfo.device_type.InGroup(INTEL_GROUP_96x) 120 || sharedInfo.device_type.InGroup(INTEL_GROUP_G4x) 121 || sharedInfo.device_type.InGroup(INTEL_GROUP_ILK) 122 || sharedInfo.device_type.InFamily(INTEL_FAMILY_SER5) 123 || sharedInfo.device_type.InFamily(INTEL_FAMILY_SOC0)) { 124 write32(baseRegister, mode.v_display_start * sharedInfo.bytes_per_row 125 + mode.h_display_start * (sharedInfo.bits_per_pixel + 7) / 8); 126 read32(baseRegister); 127 write32(surfaceRegister, sharedInfo.frame_buffer_offset); 128 read32(surfaceRegister); 129 } else { 130 write32(baseRegister, sharedInfo.frame_buffer_offset 131 + mode.v_display_start * sharedInfo.bytes_per_row 132 + mode.h_display_start * (sharedInfo.bits_per_pixel + 7) / 8); 133 read32(baseRegister); 134 } 135 } 136 137 138 void 139 set_frame_buffer_base() 140 { 141 // TODO we always set both displays to the same address. When we support 142 // multiple framebuffers, they should get different addresses here. 143 set_frame_buffer_registers(INTEL_DISPLAY_A_BASE, INTEL_DISPLAY_A_SURFACE); 144 set_frame_buffer_registers(INTEL_DISPLAY_B_BASE, INTEL_DISPLAY_B_SURFACE); 145 } 146 147 148 static bool 149 limit_modes_for_gen3_lvds(display_mode* mode) 150 { 151 // Filter out modes with resolution higher than the internal LCD can 152 // display. 153 // FIXME do this only for that display. The whole display mode logic 154 // needs to be adjusted to know which display we're talking about. 155 if (gInfo->shared_info->panel_mode.virtual_width < mode->virtual_width) 156 return false; 157 if (gInfo->shared_info->panel_mode.virtual_height < mode->virtual_height) 158 return false; 159 160 return true; 161 } 162 163 /*! Creates the initial mode list of the primary accelerant. 164 It's called from intel_init_accelerant(). 165 */ 166 status_t 167 create_mode_list(void) 168 { 169 CALLED(); 170 171 for (uint32 i = 0; i < gInfo->port_count; i++) { 172 if (gInfo->ports[i] == NULL) 173 continue; 174 175 status_t status = gInfo->ports[i]->GetEDID(&gInfo->edid_info); 176 if (status == B_OK) 177 gInfo->has_edid = true; 178 } 179 180 display_mode* list; 181 uint32 count = 0; 182 183 const color_space kSupportedSpaces[] = {B_RGB32_LITTLE, B_RGB16_LITTLE, 184 B_CMAP8}; 185 const color_space* supportedSpaces; 186 int colorSpaceCount; 187 188 if (gInfo->shared_info->device_type.Generation() >= 4) { 189 // No B_RGB15, use our custom colorspace list 190 supportedSpaces = kSupportedSpaces; 191 colorSpaceCount = B_COUNT_OF(kSupportedSpaces); 192 } else { 193 supportedSpaces = NULL; 194 colorSpaceCount = 0; 195 } 196 197 // If no EDID, but have vbt from driver, use that mode 198 if (!gInfo->has_edid && gInfo->shared_info->got_vbt) { 199 // We could not read any EDID info. Fallback to creating a list with 200 // only the mode set up by the BIOS. 201 202 check_display_mode_hook limitModes = NULL; 203 if (gInfo->shared_info->device_type.Generation() < 4) 204 limitModes = limit_modes_for_gen3_lvds; 205 206 // TODO: support lower modes via scaling and windowing 207 gInfo->mode_list_area = create_display_modes("intel extreme modes", 208 NULL, &gInfo->shared_info->panel_mode, 1, 209 supportedSpaces, colorSpaceCount, limitModes, &list, &count); 210 } else { 211 // Otherwise return the 'real' list of modes 212 gInfo->mode_list_area = create_display_modes("intel extreme modes", 213 gInfo->has_edid ? &gInfo->edid_info : NULL, NULL, 0, 214 supportedSpaces, colorSpaceCount, NULL, &list, &count); 215 } 216 217 if (gInfo->mode_list_area < B_OK) 218 return gInfo->mode_list_area; 219 220 gInfo->mode_list = list; 221 gInfo->shared_info->mode_list_area = gInfo->mode_list_area; 222 gInfo->shared_info->mode_count = count; 223 224 return B_OK; 225 } 226 227 228 void 229 wait_for_vblank(void) 230 { 231 acquire_sem_etc(gInfo->shared_info->vblank_sem, 1, B_RELATIVE_TIMEOUT, 232 21000); 233 // With the output turned off via DPMS, we might not get any interrupts 234 // anymore that's why we don't wait forever for it. At 50Hz, we're sure 235 // to get a vblank in at most 20ms, so there is no need to wait longer 236 // than that. 237 } 238 239 240 // #pragma mark - 241 242 243 uint32 244 intel_accelerant_mode_count(void) 245 { 246 CALLED(); 247 return gInfo->shared_info->mode_count; 248 } 249 250 251 status_t 252 intel_get_mode_list(display_mode* modeList) 253 { 254 CALLED(); 255 memcpy(modeList, gInfo->mode_list, 256 gInfo->shared_info->mode_count * sizeof(display_mode)); 257 return B_OK; 258 } 259 260 261 status_t 262 intel_propose_display_mode(display_mode* target, const display_mode* low, 263 const display_mode* high) 264 { 265 CALLED(); 266 267 // first search for the specified mode in the list, if no mode is found 268 // try to fix the target mode in sanitize_display_mode 269 // TODO: Only sanitize_display_mode should be used. However, at the moment 270 // the mode constraints are not optimal and do not work for all 271 // configurations. 272 for (uint32 i = 0; i < gInfo->shared_info->mode_count; i++) { 273 display_mode *mode = &gInfo->mode_list[i]; 274 275 // TODO: improve this, ie. adapt pixel clock to allowed values!!! 276 277 if (target->virtual_width != mode->virtual_width 278 || target->virtual_height != mode->virtual_height 279 || target->space != mode->space) { 280 continue; 281 } 282 283 *target = *mode; 284 return B_OK; 285 } 286 287 sanitize_display_mode(*target); 288 289 return is_display_mode_within_bounds(*target, *low, *high) 290 ? B_OK : B_BAD_VALUE; 291 } 292 293 294 status_t 295 intel_set_display_mode(display_mode* mode) 296 { 297 if (mode == NULL) 298 return B_BAD_VALUE; 299 300 TRACE("%s(%" B_PRIu16 "x%" B_PRIu16 ")\n", __func__, 301 mode->virtual_width, mode->virtual_height); 302 303 display_mode target = *mode; 304 305 if (sanitize_display_mode(target)) { 306 TRACE("Video mode was adjusted by sanitize_display_mode\n"); 307 TRACE("Initial mode: Hd %d Hs %d He %d Ht %d Vd %d Vs %d Ve %d Vt %d\n", 308 mode->timing.h_display, mode->timing.h_sync_start, 309 mode->timing.h_sync_end, mode->timing.h_total, 310 mode->timing.v_display, mode->timing.v_sync_start, 311 mode->timing.v_sync_end, mode->timing.v_total); 312 TRACE("Sanitized: Hd %d Hs %d He %d Ht %d Vd %d Vs %d Ve %d Vt %d\n", 313 target.timing.h_display, target.timing.h_sync_start, 314 target.timing.h_sync_end, target.timing.h_total, 315 target.timing.v_display, target.timing.v_sync_start, 316 target.timing.v_sync_end, target.timing.v_total); 317 } 318 319 uint32 colorMode, bytesPerRow, bitsPerPixel; 320 get_color_space_format(target, colorMode, bytesPerRow, bitsPerPixel); 321 322 // TODO: do not go further if the mode is identical to the current one. 323 // This would avoid the screen being off when switching workspaces when they 324 // have the same resolution. 325 326 intel_shared_info &sharedInfo = *gInfo->shared_info; 327 Autolock locker(sharedInfo.accelerant_lock); 328 329 // First register dump 330 //dump_registers(); 331 332 // TODO: This may not be neccesary 333 set_display_power_mode(B_DPMS_OFF); 334 335 // free old and allocate new frame buffer in graphics memory 336 337 intel_free_memory(sharedInfo.frame_buffer); 338 339 addr_t base; 340 if (intel_allocate_memory(bytesPerRow * target.virtual_height, 0, 341 base) < B_OK) { 342 // oh, how did that happen? Unfortunately, there is no really good way 343 // back. Try to restore a framebuffer for the previous mode, at least. 344 if (intel_allocate_memory(gInfo->current_mode.virtual_height 345 * sharedInfo.bytes_per_row, 0, base) == B_OK) { 346 sharedInfo.frame_buffer = base; 347 sharedInfo.frame_buffer_offset = base 348 - (addr_t)sharedInfo.graphics_memory; 349 set_frame_buffer_base(); 350 } 351 352 ERROR("%s: Failed to allocate framebuffer !\n", __func__); 353 return B_NO_MEMORY; 354 } 355 356 // clear frame buffer before using it 357 memset((uint8*)base, 0, bytesPerRow * target.virtual_height); 358 sharedInfo.frame_buffer = base; 359 sharedInfo.frame_buffer_offset = base - (addr_t)sharedInfo.graphics_memory; 360 361 #if 0 362 if ((gInfo->head_mode & HEAD_MODE_TESTING) != 0) { 363 // 1. Enable panel power as needed to retrieve panel configuration 364 // (use AUX VDD enable bit) 365 // skip, did detection already, might need that before that though 366 367 // 2. Enable PCH clock reference source and PCH SSC modulator, 368 // wait for warmup (Can be done anytime before enabling port) 369 // skip, most certainly already set up by bios to use other ports, 370 // will need for coldstart though 371 372 // 3. If enabling CPU embedded DisplayPort A: (Can be done anytime 373 // before enabling CPU pipe or port) 374 // a. Enable PCH 120MHz clock source output to CPU, wait for DMI 375 // latency 376 // b. Configure and enable CPU DisplayPort PLL in the DisplayPort A 377 // register, wait for warmup 378 // skip, not doing eDP right now, should go into 379 // EmbeddedDisplayPort class though 380 381 // 4. If enabling port on PCH: (Must be done before enabling CPU pipe 382 // or FDI) 383 // a. Enable PCH FDI Receiver PLL, wait for warmup plus DMI latency 384 // b. Switch from Rawclk to PCDclk in FDI Receiver (FDI A OR FDI B) 385 // c. [DevSNB] Enable CPU FDI Transmitter PLL, wait for warmup 386 // d. [DevILK] CPU FDI PLL is always on and does not need to be 387 // enabled 388 FDILink* link = pipe->FDILink(); 389 if (link != NULL) { 390 link->Receiver().EnablePLL(); 391 link->Receiver().SwitchClock(true); 392 link->Transmitter().EnablePLL(); 393 } 394 395 // 5. Enable CPU panel fitter if needed for hires, required for VGA 396 // (Can be done anytime before enabling CPU pipe) 397 PanelFitter* fitter = pipe->PanelFitter(); 398 if (fitter != NULL) 399 fitter->Enable(mode); 400 401 // 6. Configure CPU pipe timings, M/N/TU, and other pipe settings 402 // (Can be done anytime before enabling CPU pipe) 403 pll_divisors divisors; 404 compute_pll_divisors(target, divisors, false); 405 pipe->ConfigureTimings(divisors); 406 407 // 7. Enable CPU pipe 408 pipe->Enable(); 409 410 8. Configure and enable CPU planes (VGA or hires) 411 9. If enabling port on PCH: 412 // a. Program PCH FDI Receiver TU size same as Transmitter TU size for TU error checking 413 // b. Train FDI 414 // i. Set pre-emphasis and voltage (iterate if training steps fail) 415 ii. Enable CPU FDI Transmitter and PCH FDI Receiver with Training Pattern 1 enabled. 416 iii. Wait for FDI training pattern 1 time 417 iv. Read PCH FDI Receiver ISR ([DevIBX-B+] IIR) for bit lock in bit 8 (retry at least once if no lock) 418 v. Enable training pattern 2 on CPU FDI Transmitter and PCH FDI Receiver 419 vi. Wait for FDI training pattern 2 time 420 vii. Read PCH FDI Receiver ISR ([DevIBX-B+] IIR) for symbol lock in bit 9 (retry at least once if no 421 lock) 422 viii. Enable normal pixel output on CPU FDI Transmitter and PCH FDI Receiver 423 ix. Wait for FDI idle pattern time for link to become active 424 c. Configure and enable PCH DPLL, wait for PCH DPLL warmup (Can be done anytime before enabling 425 PCH transcoder) 426 d. [DevCPT] Configure DPLL SEL to set the DPLL to transcoder mapping and enable DPLL to the 427 transcoder. 428 e. [DevCPT] Configure DPLL_CTL DPLL_HDMI_multipler. 429 f. Configure PCH transcoder timings, M/N/TU, and other transcoder settings (should match CPU settings). 430 g. [DevCPT] Configure and enable Transcoder DisplayPort Control if DisplayPort will be used 431 h. Enable PCH transcoder 432 10. Enable ports (DisplayPort must enable in training pattern 1) 433 11. Enable panel power through panel power sequencing 434 12. Wait for panel power sequencing to reach enabled steady state 435 13. Disable panel power override 436 14. If DisplayPort, complete link training 437 15. Enable panel backlight 438 } 439 #endif 440 441 // make sure VGA display is disabled 442 write32(INTEL_VGA_DISPLAY_CONTROL, VGA_DISPLAY_DISABLED); 443 read32(INTEL_VGA_DISPLAY_CONTROL); 444 445 // Go over each port and set the display mode 446 for (uint32 i = 0; i < gInfo->port_count; i++) { 447 if (gInfo->ports[i] == NULL) 448 continue; 449 if (!gInfo->ports[i]->IsConnected()) 450 continue; 451 452 status_t status = gInfo->ports[i]->SetDisplayMode(&target, colorMode); 453 if (status != B_OK) 454 ERROR("%s: Unable to set display mode!\n", __func__); 455 } 456 457 TRACE("%s: Port configuration completed successfully!\n", __func__); 458 459 // We set the same color mode across all pipes 460 program_pipe_color_modes(colorMode); 461 462 // TODO: This may not be neccesary (see DPMS OFF at top) 463 set_display_power_mode(sharedInfo.dpms_mode); 464 465 // Changing bytes per row seems to be ignored if the plane/pipe is turned 466 // off 467 468 // Always set both pipes, just in case 469 // TODO rework this when we get multiple head support with different 470 // resolutions 471 write32(INTEL_DISPLAY_A_BYTES_PER_ROW, bytesPerRow); 472 write32(INTEL_DISPLAY_B_BYTES_PER_ROW, bytesPerRow); 473 474 // update shared info 475 gInfo->current_mode = target; 476 477 // TODO: move to gInfo 478 sharedInfo.bytes_per_row = bytesPerRow; 479 sharedInfo.bits_per_pixel = bitsPerPixel; 480 481 set_frame_buffer_base(); 482 // triggers writing back double-buffered registers 483 // which is INTEL_DISPLAY_X_BYTES_PER_ROW only apparantly 484 485 // Second register dump 486 //dump_registers(); 487 488 return B_OK; 489 } 490 491 492 status_t 493 intel_get_display_mode(display_mode* _currentMode) 494 { 495 CALLED(); 496 497 *_currentMode = gInfo->current_mode; 498 499 // This seems unreliable. We should always know the current_mode 500 //retrieve_current_mode(*_currentMode, INTEL_DISPLAY_A_PLL); 501 return B_OK; 502 } 503 504 505 status_t 506 intel_get_edid_info(void* info, size_t size, uint32* _version) 507 { 508 if (!gInfo->has_edid) 509 return B_ERROR; 510 if (size < sizeof(struct edid1_info)) 511 return B_BUFFER_OVERFLOW; 512 513 memcpy(info, &gInfo->edid_info, sizeof(struct edid1_info)); 514 *_version = EDID_VERSION_1; 515 return B_OK; 516 } 517 518 519 static int32_t 520 intel_get_backlight_register(bool read) 521 { 522 if (gInfo->shared_info->pch_info == INTEL_PCH_NONE) 523 return MCH_BLC_PWM_CTL; 524 525 if (read) 526 return PCH_SBLC_PWM_CTL2; 527 else 528 return PCH_BLC_PWM_CTL; 529 } 530 531 532 status_t 533 intel_set_brightness(float brightness) 534 { 535 CALLED(); 536 537 if (brightness < 0 || brightness > 1) 538 return B_BAD_VALUE; 539 540 uint32_t period = read32(intel_get_backlight_register(true)) >> 16; 541 542 // The "duty cycle" is a proportion of the period (0 = backlight off, 543 // period = maximum brightness). The low bit must be masked out because 544 // it is apparently used for something else on some Atom machines (no 545 // reference to that in the documentation that I know of). 546 // Additionally we don't want it to be completely 0 here, because then 547 // it becomes hard to turn the display on again (at least until we get 548 // working ACPI keyboard shortcuts for this). So always keep the backlight 549 // at least a little bit on for now. 550 uint32_t duty = (uint32_t)(period * brightness) & 0xfffe; 551 if (duty == 0 && period != 0) 552 duty = 2; 553 554 write32(intel_get_backlight_register(false), duty | (period << 16)); 555 556 return B_OK; 557 } 558 559 560 status_t 561 intel_get_brightness(float* brightness) 562 { 563 CALLED(); 564 565 if (brightness == NULL) 566 return B_BAD_VALUE; 567 568 uint16_t period = read32(intel_get_backlight_register(true)) >> 16; 569 uint16_t duty = read32(intel_get_backlight_register(false)) & 0xffff; 570 *brightness = (float)duty / period; 571 572 return B_OK; 573 } 574 575 576 status_t 577 intel_get_frame_buffer_config(frame_buffer_config* config) 578 { 579 CALLED(); 580 581 uint32 offset = gInfo->shared_info->frame_buffer_offset; 582 583 config->frame_buffer = gInfo->shared_info->graphics_memory + offset; 584 config->frame_buffer_dma 585 = (uint8*)gInfo->shared_info->physical_graphics_memory + offset; 586 config->bytes_per_row = gInfo->shared_info->bytes_per_row; 587 588 return B_OK; 589 } 590 591 592 status_t 593 intel_get_pixel_clock_limits(display_mode* mode, uint32* _low, uint32* _high) 594 { 595 CALLED(); 596 597 if (_low != NULL) { 598 // lower limit of about 48Hz vertical refresh 599 uint32 totalClocks = (uint32)mode->timing.h_total 600 * (uint32)mode->timing.v_total; 601 uint32 low = (totalClocks * 48L) / 1000L; 602 if (low < gInfo->shared_info->pll_info.min_frequency) 603 low = gInfo->shared_info->pll_info.min_frequency; 604 else if (low > gInfo->shared_info->pll_info.max_frequency) 605 return B_ERROR; 606 607 *_low = low; 608 } 609 610 if (_high != NULL) 611 *_high = gInfo->shared_info->pll_info.max_frequency; 612 613 return B_OK; 614 } 615 616 617 status_t 618 intel_move_display(uint16 horizontalStart, uint16 verticalStart) 619 { 620 CALLED(); 621 622 intel_shared_info &sharedInfo = *gInfo->shared_info; 623 Autolock locker(sharedInfo.accelerant_lock); 624 625 display_mode &mode = gInfo->current_mode; 626 627 if (horizontalStart + mode.timing.h_display > mode.virtual_width 628 || verticalStart + mode.timing.v_display > mode.virtual_height) 629 return B_BAD_VALUE; 630 631 mode.h_display_start = horizontalStart; 632 mode.v_display_start = verticalStart; 633 634 set_frame_buffer_base(); 635 636 return B_OK; 637 } 638 639 640 status_t 641 intel_get_timing_constraints(display_timing_constraints* constraints) 642 { 643 CALLED(); 644 return B_ERROR; 645 } 646 647 648 void 649 intel_set_indexed_colors(uint count, uint8 first, uint8* colors, uint32 flags) 650 { 651 TRACE("%s(colors = %p, first = %u)\n", __func__, colors, first); 652 653 if (colors == NULL) 654 return; 655 656 Autolock locker(gInfo->shared_info->accelerant_lock); 657 658 for (; count-- > 0; first++) { 659 uint32 color = colors[0] << 16 | colors[1] << 8 | colors[2]; 660 colors += 3; 661 662 write32(INTEL_DISPLAY_A_PALETTE + first * sizeof(uint32), color); 663 write32(INTEL_DISPLAY_B_PALETTE + first * sizeof(uint32), color); 664 } 665 } 666