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