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 for (uint32 i = 0; i < gInfo->port_count; i++) { 319 if (gInfo->ports[i] == NULL) 320 continue; 321 322 status_t status = gInfo->ports[i]->GetEDID(&gInfo->edid_info); 323 if (status == B_OK) 324 gInfo->has_edid = true; 325 } 326 327 // If no EDID, but have vbt from driver, use that mode 328 if (!gInfo->has_edid && gInfo->shared_info->got_vbt) { 329 // We could not read any EDID info. Fallback to creating a list with 330 // only the mode set up by the BIOS. 331 332 // TODO: support lower modes via scaling and windowing 333 size_t size = (sizeof(display_mode) + B_PAGE_SIZE - 1) 334 & ~(B_PAGE_SIZE - 1); 335 336 display_mode* list; 337 area_id area = create_area("intel extreme modes", 338 (void**)&list, B_ANY_ADDRESS, size, B_NO_LOCK, 339 B_READ_AREA | B_WRITE_AREA); 340 if (area < 0) 341 return area; 342 343 memcpy(list, &gInfo->shared_info->panel_mode, sizeof(display_mode)); 344 345 gInfo->mode_list_area = area; 346 gInfo->mode_list = list; 347 gInfo->shared_info->mode_list_area = gInfo->mode_list_area; 348 gInfo->shared_info->mode_count = 1; 349 return B_OK; 350 } 351 352 // Otherwise return the 'real' list of modes 353 display_mode* list; 354 uint32 count = 0; 355 gInfo->mode_list_area = create_display_modes("intel extreme modes", 356 gInfo->has_edid ? &gInfo->edid_info : NULL, NULL, 0, NULL, 0, NULL, 357 &list, &count); 358 if (gInfo->mode_list_area < B_OK) 359 return gInfo->mode_list_area; 360 361 gInfo->mode_list = list; 362 gInfo->shared_info->mode_list_area = gInfo->mode_list_area; 363 gInfo->shared_info->mode_count = count; 364 365 return B_OK; 366 } 367 368 369 void 370 wait_for_vblank(void) 371 { 372 acquire_sem_etc(gInfo->shared_info->vblank_sem, 1, B_RELATIVE_TIMEOUT, 373 25000); 374 // With the output turned off via DPMS, we might not get any interrupts 375 // anymore that's why we don't wait forever for it. 376 } 377 378 379 // #pragma mark - 380 381 382 uint32 383 intel_accelerant_mode_count(void) 384 { 385 CALLED(); 386 return gInfo->shared_info->mode_count; 387 } 388 389 390 status_t 391 intel_get_mode_list(display_mode* modeList) 392 { 393 CALLED(); 394 memcpy(modeList, gInfo->mode_list, 395 gInfo->shared_info->mode_count * sizeof(display_mode)); 396 return B_OK; 397 } 398 399 400 status_t 401 intel_propose_display_mode(display_mode* target, const display_mode* low, 402 const display_mode* high) 403 { 404 CALLED(); 405 406 // first search for the specified mode in the list, if no mode is found 407 // try to fix the target mode in sanitize_display_mode 408 // TODO: Only sanitize_display_mode should be used. However, at the moments 409 // the mode constraints are not optimal and do not work for all 410 // configurations. 411 for (uint32 i = 0; i < gInfo->shared_info->mode_count; i++) { 412 display_mode *mode = &gInfo->mode_list[i]; 413 414 // TODO: improve this, ie. adapt pixel clock to allowed values!!! 415 416 if (target->virtual_width != mode->virtual_width 417 || target->virtual_height != mode->virtual_height 418 || target->space != mode->space) { 419 continue; 420 } 421 422 *target = *mode; 423 return B_OK; 424 } 425 426 sanitize_display_mode(*target); 427 428 return is_display_mode_within_bounds(*target, *low, *high) 429 ? B_OK : B_BAD_VALUE; 430 } 431 432 433 status_t 434 intel_set_display_mode(display_mode* mode) 435 { 436 TRACE("%s(%" B_PRIu16 "x%" B_PRIu16 ")\n", __func__, 437 mode->virtual_width, mode->virtual_height); 438 439 if (mode == NULL) 440 return B_BAD_VALUE; 441 442 display_mode target = *mode; 443 444 // TODO: it may be acceptable to continue when using panel fitting or 445 // centering, since the data from propose_display_mode will not actually be 446 // used as is in this case. 447 if (sanitize_display_mode(target)) { 448 TRACE("Video mode was adjusted by sanitize_display_mode\n"); 449 TRACE("Initial mode: Hd %d Hs %d He %d Ht %d Vd %d Vs %d Ve %d Vt %d\n", 450 mode->timing.h_display, mode->timing.h_sync_start, 451 mode->timing.h_sync_end, mode->timing.h_total, 452 mode->timing.v_display, mode->timing.v_sync_start, 453 mode->timing.v_sync_end, mode->timing.v_total); 454 TRACE("Sanitized: Hd %d Hs %d He %d Ht %d Vd %d Vs %d Ve %d Vt %d\n", 455 target.timing.h_display, target.timing.h_sync_start, 456 target.timing.h_sync_end, target.timing.h_total, 457 target.timing.v_display, target.timing.v_sync_start, 458 target.timing.v_sync_end, target.timing.v_total); 459 } 460 461 uint32 colorMode, bytesPerRow, bitsPerPixel; 462 get_color_space_format(target, colorMode, bytesPerRow, bitsPerPixel); 463 464 // TODO: do not go further if the mode is identical to the current one. 465 // This would avoid the screen being off when switching workspaces when they 466 // have the same resolution. 467 468 intel_shared_info &sharedInfo = *gInfo->shared_info; 469 Autolock locker(sharedInfo.accelerant_lock); 470 471 // First register dump 472 //dump_registers(); 473 474 // TODO: This may not be neccesary 475 set_display_power_mode(B_DPMS_OFF); 476 477 // free old and allocate new frame buffer in graphics memory 478 479 intel_free_memory(sharedInfo.frame_buffer); 480 481 addr_t base; 482 if (intel_allocate_memory(bytesPerRow * target.virtual_height, 0, 483 base) < B_OK) { 484 // oh, how did that happen? Unfortunately, there is no really good way 485 // back 486 if (intel_allocate_memory(gInfo->current_mode.virtual_height 487 * sharedInfo.bytes_per_row, 0, base) == B_OK) { 488 sharedInfo.frame_buffer = base; 489 sharedInfo.frame_buffer_offset = base 490 - (addr_t)sharedInfo.graphics_memory; 491 set_frame_buffer_base(); 492 } 493 494 TRACE("%s: Failed to allocate framebuffer !\n", __func__); 495 return B_NO_MEMORY; 496 } 497 498 // clear frame buffer before using it 499 memset((uint8*)base, 0, bytesPerRow * target.virtual_height); 500 sharedInfo.frame_buffer = base; 501 sharedInfo.frame_buffer_offset = base - (addr_t)sharedInfo.graphics_memory; 502 503 #if 0 504 if ((gInfo->head_mode & HEAD_MODE_TESTING) != 0) { 505 // 1. Enable panel power as needed to retrieve panel configuration 506 // (use AUX VDD enable bit) 507 // skip, did detection already, might need that before that though 508 509 // 2. Enable PCH clock reference source and PCH SSC modulator, 510 // wait for warmup (Can be done anytime before enabling port) 511 // skip, most certainly already set up by bios to use other ports, 512 // will need for coldstart though 513 514 // 3. If enabling CPU embedded DisplayPort A: (Can be done anytime 515 // before enabling CPU pipe or port) 516 // a. Enable PCH 120MHz clock source output to CPU, wait for DMI 517 // latency 518 // b. Configure and enable CPU DisplayPort PLL in the DisplayPort A 519 // register, wait for warmup 520 // skip, not doing eDP right now, should go into 521 // EmbeddedDisplayPort class though 522 523 // 4. If enabling port on PCH: (Must be done before enabling CPU pipe 524 // or FDI) 525 // a. Enable PCH FDI Receiver PLL, wait for warmup plus DMI latency 526 // b. Switch from Rawclk to PCDclk in FDI Receiver (FDI A OR FDI B) 527 // c. [DevSNB] Enable CPU FDI Transmitter PLL, wait for warmup 528 // d. [DevILK] CPU FDI PLL is always on and does not need to be 529 // enabled 530 FDILink* link = pipe->FDILink(); 531 if (link != NULL) { 532 link->Receiver().EnablePLL(); 533 link->Receiver().SwitchClock(true); 534 link->Transmitter().EnablePLL(); 535 } 536 537 // 5. Enable CPU panel fitter if needed for hires, required for VGA 538 // (Can be done anytime before enabling CPU pipe) 539 PanelFitter* fitter = pipe->PanelFitter(); 540 if (fitter != NULL) 541 fitter->Enable(mode); 542 543 // 6. Configure CPU pipe timings, M/N/TU, and other pipe settings 544 // (Can be done anytime before enabling CPU pipe) 545 pll_divisors divisors; 546 compute_pll_divisors(target, divisors, false); 547 pipe->ConfigureTimings(divisors); 548 549 // 7. Enable CPU pipe 550 pipe->Enable(); 551 552 8. Configure and enable CPU planes (VGA or hires) 553 9. If enabling port on PCH: 554 // a. Program PCH FDI Receiver TU size same as Transmitter TU size for TU error checking 555 // b. Train FDI 556 // i. Set pre-emphasis and voltage (iterate if training steps fail) 557 ii. Enable CPU FDI Transmitter and PCH FDI Receiver with Training Pattern 1 enabled. 558 iii. Wait for FDI training pattern 1 time 559 iv. Read PCH FDI Receiver ISR ([DevIBX-B+] IIR) for bit lock in bit 8 (retry at least once if no lock) 560 v. Enable training pattern 2 on CPU FDI Transmitter and PCH FDI Receiver 561 vi. Wait for FDI training pattern 2 time 562 vii. Read PCH FDI Receiver ISR ([DevIBX-B+] IIR) for symbol lock in bit 9 (retry at least once if no 563 lock) 564 viii. Enable normal pixel output on CPU FDI Transmitter and PCH FDI Receiver 565 ix. Wait for FDI idle pattern time for link to become active 566 c. Configure and enable PCH DPLL, wait for PCH DPLL warmup (Can be done anytime before enabling 567 PCH transcoder) 568 d. [DevCPT] Configure DPLL SEL to set the DPLL to transcoder mapping and enable DPLL to the 569 transcoder. 570 e. [DevCPT] Configure DPLL_CTL DPLL_HDMI_multipler. 571 f. Configure PCH transcoder timings, M/N/TU, and other transcoder settings (should match CPU settings). 572 g. [DevCPT] Configure and enable Transcoder DisplayPort Control if DisplayPort will be used 573 h. Enable PCH transcoder 574 10. Enable ports (DisplayPort must enable in training pattern 1) 575 11. Enable panel power through panel power sequencing 576 12. Wait for panel power sequencing to reach enabled steady state 577 13. Disable panel power override 578 14. If DisplayPort, complete link training 579 15. Enable panel backlight 580 } 581 #endif 582 583 // make sure VGA display is disabled 584 write32(INTEL_VGA_DISPLAY_CONTROL, VGA_DISPLAY_DISABLED); 585 read32(INTEL_VGA_DISPLAY_CONTROL); 586 587 // Go over each port and set the display mode 588 for (uint32 i = 0; i < gInfo->port_count; i++) { 589 if (gInfo->ports[i] == NULL) 590 continue; 591 if (!gInfo->ports[i]->IsConnected()) 592 continue; 593 594 status_t status = gInfo->ports[i]->SetDisplayMode(&target, colorMode); 595 if (status != B_OK) 596 ERROR("%s: Unable to set display mode!\n", __func__); 597 } 598 599 TRACE("%s: Port configuration completed successfully!\n", __func__); 600 601 // We set the same color mode across all pipes 602 program_pipe_color_modes(colorMode); 603 604 // TODO: This may not be neccesary (see DPMS OFF at top) 605 set_display_power_mode(sharedInfo.dpms_mode); 606 607 // Changing bytes per row seems to be ignored if the plane/pipe is turned 608 // off 609 610 // Always set both pipes, just in case 611 // TODO rework this when we get multiple head support with different 612 // resolutions 613 write32(INTEL_DISPLAY_A_BYTES_PER_ROW, bytesPerRow); 614 write32(INTEL_DISPLAY_B_BYTES_PER_ROW, bytesPerRow); 615 616 // update shared info 617 gInfo->current_mode = target; 618 619 // TODO: move to gInfo 620 sharedInfo.bytes_per_row = bytesPerRow; 621 sharedInfo.bits_per_pixel = bitsPerPixel; 622 623 set_frame_buffer_base(); 624 // triggers writing back double-buffered registers 625 626 // Second register dump 627 //dump_registers(); 628 629 return B_OK; 630 } 631 632 633 status_t 634 intel_get_display_mode(display_mode* _currentMode) 635 { 636 CALLED(); 637 638 *_currentMode = gInfo->current_mode; 639 640 // This seems unreliable. We should always know the current_mode 641 //retrieve_current_mode(*_currentMode, INTEL_DISPLAY_A_PLL); 642 return B_OK; 643 } 644 645 646 status_t 647 intel_get_edid_info(void* info, size_t size, uint32* _version) 648 { 649 CALLED(); 650 651 if (!gInfo->has_edid) 652 return B_ERROR; 653 if (size < sizeof(struct edid1_info)) 654 return B_BUFFER_OVERFLOW; 655 656 memcpy(info, &gInfo->edid_info, sizeof(struct edid1_info)); 657 *_version = EDID_VERSION_1; 658 return B_OK; 659 } 660 661 662 static int32_t 663 intel_get_backlight_register(bool read) 664 { 665 if (gInfo->shared_info->pch_info == INTEL_PCH_NONE) 666 return MCH_BLC_PWM_CTL; 667 668 if (read) 669 return PCH_SBLC_PWM_CTL2; 670 else 671 return PCH_BLC_PWM_CTL; 672 } 673 674 675 status_t 676 intel_set_brightness(float brightness) 677 { 678 CALLED(); 679 680 if (brightness < 0 || brightness > 1) 681 return B_BAD_VALUE; 682 683 uint32_t period = read32(intel_get_backlight_register(true)) >> 16; 684 uint32_t duty = (uint32_t)(period * brightness) & 0xfffe; 685 /* Setting the low bit seems to give strange results on some Atom machines */ 686 write32(intel_get_backlight_register(false), duty | (period << 16)); 687 688 return B_OK; 689 } 690 691 692 status_t 693 intel_get_brightness(float* brightness) 694 { 695 CALLED(); 696 697 if (brightness == NULL) 698 return B_BAD_VALUE; 699 700 uint16_t period = read32(intel_get_backlight_register(true)) >> 16; 701 uint16_t duty = read32(intel_get_backlight_register(false)) & 0xffff; 702 *brightness = (float)duty / period; 703 704 return B_OK; 705 } 706 707 708 status_t 709 intel_get_frame_buffer_config(frame_buffer_config* config) 710 { 711 CALLED(); 712 713 uint32 offset = gInfo->shared_info->frame_buffer_offset; 714 715 config->frame_buffer = gInfo->shared_info->graphics_memory + offset; 716 config->frame_buffer_dma 717 = (uint8*)gInfo->shared_info->physical_graphics_memory + offset; 718 config->bytes_per_row = gInfo->shared_info->bytes_per_row; 719 720 return B_OK; 721 } 722 723 724 status_t 725 intel_get_pixel_clock_limits(display_mode* mode, uint32* _low, uint32* _high) 726 { 727 CALLED(); 728 729 if (_low != NULL) { 730 // lower limit of about 48Hz vertical refresh 731 uint32 totalClocks = (uint32)mode->timing.h_total 732 * (uint32)mode->timing.v_total; 733 uint32 low = (totalClocks * 48L) / 1000L; 734 if (low < gInfo->shared_info->pll_info.min_frequency) 735 low = gInfo->shared_info->pll_info.min_frequency; 736 else if (low > gInfo->shared_info->pll_info.max_frequency) 737 return B_ERROR; 738 739 *_low = low; 740 } 741 742 if (_high != NULL) 743 *_high = gInfo->shared_info->pll_info.max_frequency; 744 745 return B_OK; 746 } 747 748 749 status_t 750 intel_move_display(uint16 horizontalStart, uint16 verticalStart) 751 { 752 CALLED(); 753 754 intel_shared_info &sharedInfo = *gInfo->shared_info; 755 Autolock locker(sharedInfo.accelerant_lock); 756 757 display_mode &mode = gInfo->current_mode; 758 759 if (horizontalStart + mode.timing.h_display > mode.virtual_width 760 || verticalStart + mode.timing.v_display > mode.virtual_height) 761 return B_BAD_VALUE; 762 763 mode.h_display_start = horizontalStart; 764 mode.v_display_start = verticalStart; 765 766 set_frame_buffer_base(); 767 768 return B_OK; 769 } 770 771 772 status_t 773 intel_get_timing_constraints(display_timing_constraints* constraints) 774 { 775 CALLED(); 776 return B_ERROR; 777 } 778 779 780 void 781 intel_set_indexed_colors(uint count, uint8 first, uint8* colors, uint32 flags) 782 { 783 TRACE("%s(colors = %p, first = %u)\n", __func__, colors, first); 784 785 if (colors == NULL) 786 return; 787 788 Autolock locker(gInfo->shared_info->accelerant_lock); 789 790 for (; count-- > 0; first++) { 791 uint32 color = colors[0] << 16 | colors[1] << 8 | colors[2]; 792 colors += 3; 793 794 write32(INTEL_DISPLAY_A_PALETTE + first * sizeof(uint32), color); 795 write32(INTEL_DISPLAY_B_PALETTE + first * sizeof(uint32), color); 796 } 797 } 798