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