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