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("%s: invalid mode set!\n", __func__); 449 return B_BAD_VALUE; 450 } 451 452 uint32 colorMode, bytesPerRow, bitsPerPixel; 453 get_color_space_format(target, colorMode, bytesPerRow, bitsPerPixel); 454 455 // TODO: do not go further if the mode is identical to the current one. 456 // This would avoid the screen being off when switching workspaces when they 457 // have the same resolution. 458 459 intel_shared_info &sharedInfo = *gInfo->shared_info; 460 Autolock locker(sharedInfo.accelerant_lock); 461 462 // First register dump 463 //dump_registers(); 464 465 // TODO: This may not be neccesary 466 set_display_power_mode(B_DPMS_OFF); 467 468 // free old and allocate new frame buffer in graphics memory 469 470 intel_free_memory(sharedInfo.frame_buffer); 471 472 addr_t base; 473 if (intel_allocate_memory(bytesPerRow * target.virtual_height, 0, 474 base) < B_OK) { 475 // oh, how did that happen? Unfortunately, there is no really good way 476 // back 477 if (intel_allocate_memory(gInfo->current_mode.virtual_height 478 * sharedInfo.bytes_per_row, 0, base) == B_OK) { 479 sharedInfo.frame_buffer = base; 480 sharedInfo.frame_buffer_offset = base 481 - (addr_t)sharedInfo.graphics_memory; 482 set_frame_buffer_base(); 483 } 484 485 TRACE("%s: Failed to allocate framebuffer !\n", __func__); 486 return B_NO_MEMORY; 487 } 488 489 // clear frame buffer before using it 490 memset((uint8*)base, 0, bytesPerRow * target.virtual_height); 491 sharedInfo.frame_buffer = base; 492 sharedInfo.frame_buffer_offset = base - (addr_t)sharedInfo.graphics_memory; 493 494 #if 0 495 if ((gInfo->head_mode & HEAD_MODE_TESTING) != 0) { 496 // 1. Enable panel power as needed to retrieve panel configuration 497 // (use AUX VDD enable bit) 498 // skip, did detection already, might need that before that though 499 500 // 2. Enable PCH clock reference source and PCH SSC modulator, 501 // wait for warmup (Can be done anytime before enabling port) 502 // skip, most certainly already set up by bios to use other ports, 503 // will need for coldstart though 504 505 // 3. If enabling CPU embedded DisplayPort A: (Can be done anytime 506 // before enabling CPU pipe or port) 507 // a. Enable PCH 120MHz clock source output to CPU, wait for DMI 508 // latency 509 // b. Configure and enable CPU DisplayPort PLL in the DisplayPort A 510 // register, wait for warmup 511 // skip, not doing eDP right now, should go into 512 // EmbeddedDisplayPort class though 513 514 // 4. If enabling port on PCH: (Must be done before enabling CPU pipe 515 // or FDI) 516 // a. Enable PCH FDI Receiver PLL, wait for warmup plus DMI latency 517 // b. Switch from Rawclk to PCDclk in FDI Receiver (FDI A OR FDI B) 518 // c. [DevSNB] Enable CPU FDI Transmitter PLL, wait for warmup 519 // d. [DevILK] CPU FDI PLL is always on and does not need to be 520 // enabled 521 FDILink* link = pipe->FDILink(); 522 if (link != NULL) { 523 link->Receiver().EnablePLL(); 524 link->Receiver().SwitchClock(true); 525 link->Transmitter().EnablePLL(); 526 } 527 528 // 5. Enable CPU panel fitter if needed for hires, required for VGA 529 // (Can be done anytime before enabling CPU pipe) 530 PanelFitter* fitter = pipe->PanelFitter(); 531 if (fitter != NULL) 532 fitter->Enable(mode); 533 534 // 6. Configure CPU pipe timings, M/N/TU, and other pipe settings 535 // (Can be done anytime before enabling CPU pipe) 536 pll_divisors divisors; 537 compute_pll_divisors(target, divisors, false); 538 pipe->ConfigureTimings(divisors); 539 540 // 7. Enable CPU pipe 541 pipe->Enable(); 542 543 8. Configure and enable CPU planes (VGA or hires) 544 9. If enabling port on PCH: 545 // a. Program PCH FDI Receiver TU size same as Transmitter TU size for TU error checking 546 // b. Train FDI 547 // i. Set pre-emphasis and voltage (iterate if training steps fail) 548 ii. Enable CPU FDI Transmitter and PCH FDI Receiver with Training Pattern 1 enabled. 549 iii. Wait for FDI training pattern 1 time 550 iv. Read PCH FDI Receiver ISR ([DevIBX-B+] IIR) for bit lock in bit 8 (retry at least once if no lock) 551 v. Enable training pattern 2 on CPU FDI Transmitter and PCH FDI Receiver 552 vi. Wait for FDI training pattern 2 time 553 vii. Read PCH FDI Receiver ISR ([DevIBX-B+] IIR) for symbol lock in bit 9 (retry at least once if no 554 lock) 555 viii. Enable normal pixel output on CPU FDI Transmitter and PCH FDI Receiver 556 ix. Wait for FDI idle pattern time for link to become active 557 c. Configure and enable PCH DPLL, wait for PCH DPLL warmup (Can be done anytime before enabling 558 PCH transcoder) 559 d. [DevCPT] Configure DPLL SEL to set the DPLL to transcoder mapping and enable DPLL to the 560 transcoder. 561 e. [DevCPT] Configure DPLL_CTL DPLL_HDMI_multipler. 562 f. Configure PCH transcoder timings, M/N/TU, and other transcoder settings (should match CPU settings). 563 g. [DevCPT] Configure and enable Transcoder DisplayPort Control if DisplayPort will be used 564 h. Enable PCH transcoder 565 10. Enable ports (DisplayPort must enable in training pattern 1) 566 11. Enable panel power through panel power sequencing 567 12. Wait for panel power sequencing to reach enabled steady state 568 13. Disable panel power override 569 14. If DisplayPort, complete link training 570 15. Enable panel backlight 571 } 572 #endif 573 574 // make sure VGA display is disabled 575 write32(INTEL_VGA_DISPLAY_CONTROL, VGA_DISPLAY_DISABLED); 576 read32(INTEL_VGA_DISPLAY_CONTROL); 577 578 // Go over each port and set the display mode 579 for (uint32 i = 0; i < gInfo->port_count; i++) { 580 if (gInfo->ports[i] == NULL) 581 continue; 582 if (!gInfo->ports[i]->IsConnected()) 583 continue; 584 585 status_t status = gInfo->ports[i]->SetDisplayMode(&target, colorMode); 586 if (status != B_OK) 587 ERROR("%s: Unable to set display mode!\n", __func__); 588 } 589 590 TRACE("%s: Port configuration completed successfully!\n", __func__); 591 592 // We set the same color mode across all pipes 593 program_pipe_color_modes(colorMode); 594 595 // TODO: This may not be neccesary (see DPMS OFF at top) 596 set_display_power_mode(sharedInfo.dpms_mode); 597 598 // Changing bytes per row seems to be ignored if the plane/pipe is turned 599 // off 600 601 // Always set both pipes, just in case 602 // TODO rework this when we get multiple head support with different 603 // resolutions 604 write32(INTEL_DISPLAY_A_BYTES_PER_ROW, bytesPerRow); 605 write32(INTEL_DISPLAY_B_BYTES_PER_ROW, bytesPerRow); 606 607 // update shared info 608 gInfo->current_mode = target; 609 610 // TODO: move to gInfo 611 sharedInfo.bytes_per_row = bytesPerRow; 612 sharedInfo.bits_per_pixel = bitsPerPixel; 613 614 set_frame_buffer_base(); 615 // triggers writing back double-buffered registers 616 617 // Second register dump 618 //dump_registers(); 619 620 return B_OK; 621 } 622 623 624 status_t 625 intel_get_display_mode(display_mode* _currentMode) 626 { 627 CALLED(); 628 629 *_currentMode = gInfo->current_mode; 630 631 // This seems unreliable. We should always know the current_mode 632 //retrieve_current_mode(*_currentMode, INTEL_DISPLAY_A_PLL); 633 return B_OK; 634 } 635 636 637 status_t 638 intel_get_edid_info(void* info, size_t size, uint32* _version) 639 { 640 CALLED(); 641 642 if (!gInfo->has_edid) 643 return B_ERROR; 644 if (size < sizeof(struct edid1_info)) 645 return B_BUFFER_OVERFLOW; 646 647 memcpy(info, &gInfo->edid_info, sizeof(struct edid1_info)); 648 *_version = EDID_VERSION_1; 649 return B_OK; 650 } 651 652 653 status_t 654 intel_get_frame_buffer_config(frame_buffer_config* config) 655 { 656 CALLED(); 657 658 uint32 offset = gInfo->shared_info->frame_buffer_offset; 659 660 config->frame_buffer = gInfo->shared_info->graphics_memory + offset; 661 config->frame_buffer_dma 662 = (uint8*)gInfo->shared_info->physical_graphics_memory + offset; 663 config->bytes_per_row = gInfo->shared_info->bytes_per_row; 664 665 return B_OK; 666 } 667 668 669 status_t 670 intel_get_pixel_clock_limits(display_mode* mode, uint32* _low, uint32* _high) 671 { 672 CALLED(); 673 674 if (_low != NULL) { 675 // lower limit of about 48Hz vertical refresh 676 uint32 totalClocks = (uint32)mode->timing.h_total 677 * (uint32)mode->timing.v_total; 678 uint32 low = (totalClocks * 48L) / 1000L; 679 if (low < gInfo->shared_info->pll_info.min_frequency) 680 low = gInfo->shared_info->pll_info.min_frequency; 681 else if (low > gInfo->shared_info->pll_info.max_frequency) 682 return B_ERROR; 683 684 *_low = low; 685 } 686 687 if (_high != NULL) 688 *_high = gInfo->shared_info->pll_info.max_frequency; 689 690 return B_OK; 691 } 692 693 694 status_t 695 intel_move_display(uint16 horizontalStart, uint16 verticalStart) 696 { 697 CALLED(); 698 699 intel_shared_info &sharedInfo = *gInfo->shared_info; 700 Autolock locker(sharedInfo.accelerant_lock); 701 702 display_mode &mode = gInfo->current_mode; 703 704 if (horizontalStart + mode.timing.h_display > mode.virtual_width 705 || verticalStart + mode.timing.v_display > mode.virtual_height) 706 return B_BAD_VALUE; 707 708 mode.h_display_start = horizontalStart; 709 mode.v_display_start = verticalStart; 710 711 set_frame_buffer_base(); 712 713 return B_OK; 714 } 715 716 717 status_t 718 intel_get_timing_constraints(display_timing_constraints* constraints) 719 { 720 CALLED(); 721 return B_ERROR; 722 } 723 724 725 void 726 intel_set_indexed_colors(uint count, uint8 first, uint8* colors, uint32 flags) 727 { 728 TRACE("%s(colors = %p, first = %u)\n", __func__, colors, first); 729 730 if (colors == NULL) 731 return; 732 733 Autolock locker(gInfo->shared_info->accelerant_lock); 734 735 for (; count-- > 0; first++) { 736 uint32 color = colors[0] << 16 | colors[1] << 8 | colors[2]; 737 colors += 3; 738 739 write32(INTEL_DISPLAY_A_PALETTE + first * sizeof(uint32), color); 740 write32(INTEL_DISPLAY_B_PALETTE + first * sizeof(uint32), color); 741 } 742 } 743