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 break; 207 } 208 } 209 // use EDID found at boot time if there since we don't have any ourselves 210 if (!gInfo->has_edid && gInfo->shared_info->has_vesa_edid_info) { 211 TRACE("%s: Using VESA edid info\n", __func__); 212 memcpy(&gInfo->edid_info, &gInfo->shared_info->vesa_edid_info, 213 sizeof(edid1_info)); 214 // show in log what we got 215 edid_dump(&gInfo->edid_info); 216 gInfo->has_edid = true; 217 } 218 219 display_mode* list; 220 uint32 count = 0; 221 222 const color_space kSupportedSpaces[] = {B_RGB32_LITTLE, B_RGB16_LITTLE, 223 B_CMAP8}; 224 const color_space* supportedSpaces; 225 int colorSpaceCount; 226 227 if (gInfo->shared_info->device_type.Generation() >= 4) { 228 // No B_RGB15, use our custom colorspace list 229 supportedSpaces = kSupportedSpaces; 230 colorSpaceCount = B_COUNT_OF(kSupportedSpaces); 231 } else { 232 supportedSpaces = NULL; 233 colorSpaceCount = 0; 234 } 235 236 // If no EDID, but have vbt from driver, use that mode 237 if (!gInfo->has_edid && gInfo->shared_info->got_vbt) { 238 // We could not read any EDID info. Fallback to creating a list with 239 // only the mode set up by the BIOS. 240 241 check_display_mode_hook limitModes = NULL; 242 if (gInfo->shared_info->device_type.Generation() < 4) 243 limitModes = limit_modes_for_gen3_lvds; 244 245 display_mode mode; 246 mode.timing = gInfo->shared_info->panel_timing; 247 mode.space = B_RGB32; 248 mode.virtual_width = mode.timing.h_display; 249 mode.virtual_height = mode.timing.v_display; 250 mode.h_display_start = 0; 251 mode.v_display_start = 0; 252 mode.flags = 0; 253 254 // TODO: support lower modes via scaling and windowing 255 gInfo->mode_list_area = create_display_modes("intel extreme modes", NULL, &mode, 1, 256 supportedSpaces, colorSpaceCount, limitModes, &list, &count); 257 } else { 258 // Otherwise return the 'real' list of modes 259 gInfo->mode_list_area = create_display_modes("intel extreme modes", 260 gInfo->has_edid ? &gInfo->edid_info : NULL, NULL, 0, 261 supportedSpaces, colorSpaceCount, NULL, &list, &count); 262 } 263 264 if (gInfo->mode_list_area < B_OK) 265 return gInfo->mode_list_area; 266 267 gInfo->mode_list = list; 268 gInfo->shared_info->mode_list_area = gInfo->mode_list_area; 269 gInfo->shared_info->mode_count = count; 270 271 return B_OK; 272 } 273 274 275 void 276 wait_for_vblank(void) 277 { 278 acquire_sem_etc(gInfo->shared_info->vblank_sem, 1, B_RELATIVE_TIMEOUT, 279 21000); 280 // With the output turned off via DPMS, we might not get any interrupts 281 // anymore that's why we don't wait forever for it. At 50Hz, we're sure 282 // to get a vblank in at most 20ms, so there is no need to wait longer 283 // than that. 284 } 285 286 287 // #pragma mark - 288 289 290 uint32 291 intel_accelerant_mode_count(void) 292 { 293 CALLED(); 294 return gInfo->shared_info->mode_count; 295 } 296 297 298 status_t 299 intel_get_mode_list(display_mode* modeList) 300 { 301 CALLED(); 302 memcpy(modeList, gInfo->mode_list, 303 gInfo->shared_info->mode_count * sizeof(display_mode)); 304 return B_OK; 305 } 306 307 308 status_t 309 intel_propose_display_mode(display_mode* target, const display_mode* low, 310 const display_mode* high) 311 { 312 CALLED(); 313 314 display_mode mode = *target; 315 316 if (sanitize_display_mode(*target)) { 317 TRACE("Video mode was adjusted by sanitize_display_mode\n"); 318 TRACE("Initial mode: Hd %d Hs %d He %d Ht %d Vd %d Vs %d Ve %d Vt %d\n", 319 mode.timing.h_display, mode.timing.h_sync_start, 320 mode.timing.h_sync_end, mode.timing.h_total, 321 mode.timing.v_display, mode.timing.v_sync_start, 322 mode.timing.v_sync_end, mode.timing.v_total); 323 TRACE("Sanitized: Hd %d Hs %d He %d Ht %d Vd %d Vs %d Ve %d Vt %d\n", 324 target->timing.h_display, target->timing.h_sync_start, 325 target->timing.h_sync_end, target->timing.h_total, 326 target->timing.v_display, target->timing.v_sync_start, 327 target->timing.v_sync_end, target->timing.v_total); 328 } 329 // (most) modeflags are outputs from us (the driver). So we should 330 // set them depending on the mode and the current hardware config 331 target->flags |= B_SCROLL; 332 333 return is_display_mode_within_bounds(*target, *low, *high) 334 ? B_OK : B_BAD_VALUE; 335 } 336 337 338 status_t 339 intel_set_display_mode(display_mode* mode) 340 { 341 if (mode == NULL) 342 return B_BAD_VALUE; 343 344 TRACE("%s(%" B_PRIu16 "x%" B_PRIu16 ", virtual: %" B_PRIu16 "x%" B_PRIu16 ")\n", __func__, 345 mode->timing.h_display, mode->timing.v_display, mode->virtual_width, mode->virtual_height); 346 347 display_mode target = *mode; 348 349 if (intel_propose_display_mode(&target, &target, &target) != B_OK) 350 return B_BAD_VALUE; 351 352 uint32 colorMode, bytesPerRow, bitsPerPixel; 353 get_color_space_format(target, colorMode, bytesPerRow, bitsPerPixel); 354 355 // TODO: do not go further if the mode is identical to the current one. 356 // This would avoid the screen being off when switching workspaces when they 357 // have the same resolution. 358 359 intel_shared_info &sharedInfo = *gInfo->shared_info; 360 Autolock locker(sharedInfo.accelerant_lock); 361 362 // First register dump 363 //dump_registers(); 364 365 // TODO: This may not be neccesary 366 set_display_power_mode(B_DPMS_OFF); 367 368 // free old and allocate new frame buffer in graphics memory 369 370 intel_free_memory(sharedInfo.frame_buffer); 371 372 addr_t base; 373 if (intel_allocate_memory(bytesPerRow * target.virtual_height, 0, 374 base) < B_OK) { 375 // oh, how did that happen? Unfortunately, there is no really good way 376 // back. Try to restore a framebuffer for the previous mode, at least. 377 if (intel_allocate_memory(sharedInfo.current_mode.virtual_height 378 * sharedInfo.bytes_per_row, 0, base) == B_OK) { 379 sharedInfo.frame_buffer = base; 380 sharedInfo.frame_buffer_offset = base 381 - (addr_t)sharedInfo.graphics_memory; 382 set_frame_buffer_base(); 383 } 384 385 ERROR("%s: Failed to allocate framebuffer !\n", __func__); 386 return B_NO_MEMORY; 387 } 388 389 // clear frame buffer before using it 390 memset((uint8*)base, 0, bytesPerRow * target.virtual_height); 391 sharedInfo.frame_buffer = base; 392 sharedInfo.frame_buffer_offset = base - (addr_t)sharedInfo.graphics_memory; 393 394 #if 0 395 if ((gInfo->head_mode & HEAD_MODE_TESTING) != 0) { 396 // 1. Enable panel power as needed to retrieve panel configuration 397 // (use AUX VDD enable bit) 398 // skip, did detection already, might need that before that though 399 400 // 2. Enable PCH clock reference source and PCH SSC modulator, 401 // wait for warmup (Can be done anytime before enabling port) 402 // skip, most certainly already set up by bios to use other ports, 403 // will need for coldstart though 404 405 // 3. If enabling CPU embedded DisplayPort A: (Can be done anytime 406 // before enabling CPU pipe or port) 407 // a. Enable PCH 120MHz clock source output to CPU, wait for DMI 408 // latency 409 // b. Configure and enable CPU DisplayPort PLL in the DisplayPort A 410 // register, wait for warmup 411 // skip, not doing eDP right now, should go into 412 // EmbeddedDisplayPort class though 413 414 // 4. If enabling port on PCH: (Must be done before enabling CPU pipe 415 // or FDI) 416 // a. Enable PCH FDI Receiver PLL, wait for warmup plus DMI latency 417 // b. Switch from Rawclk to PCDclk in FDI Receiver (FDI A OR FDI B) 418 // c. [DevSNB] Enable CPU FDI Transmitter PLL, wait for warmup 419 // d. [DevILK] CPU FDI PLL is always on and does not need to be 420 // enabled 421 FDILink* link = pipe->FDILink(); 422 if (link != NULL) { 423 link->Receiver().EnablePLL(); 424 link->Receiver().SwitchClock(true); 425 link->Transmitter().EnablePLL(); 426 } 427 428 // 5. Enable CPU panel fitter if needed for hires, required for VGA 429 // (Can be done anytime before enabling CPU pipe) 430 PanelFitter* fitter = pipe->PanelFitter(); 431 if (fitter != NULL) 432 fitter->Enable(mode); 433 434 // 6. Configure CPU pipe timings, M/N/TU, and other pipe settings 435 // (Can be done anytime before enabling CPU pipe) 436 pll_divisors divisors; 437 compute_pll_divisors(target, divisors, false); 438 pipe->ConfigureTimings(divisors); 439 440 // 7. Enable CPU pipe 441 pipe->Enable(); 442 443 8. Configure and enable CPU planes (VGA or hires) 444 9. If enabling port on PCH: 445 // a. Program PCH FDI Receiver TU size same as Transmitter TU size for TU error checking 446 // b. Train FDI 447 // i. Set pre-emphasis and voltage (iterate if training steps fail) 448 ii. Enable CPU FDI Transmitter and PCH FDI Receiver with Training Pattern 1 enabled. 449 iii. Wait for FDI training pattern 1 time 450 iv. Read PCH FDI Receiver ISR ([DevIBX-B+] IIR) for bit lock in bit 8 (retry at least once if no lock) 451 v. Enable training pattern 2 on CPU FDI Transmitter and PCH FDI Receiver 452 vi. Wait for FDI training pattern 2 time 453 vii. Read PCH FDI Receiver ISR ([DevIBX-B+] IIR) for symbol lock in bit 9 (retry at least once if no 454 lock) 455 viii. Enable normal pixel output on CPU FDI Transmitter and PCH FDI Receiver 456 ix. Wait for FDI idle pattern time for link to become active 457 c. Configure and enable PCH DPLL, wait for PCH DPLL warmup (Can be done anytime before enabling 458 PCH transcoder) 459 d. [DevCPT] Configure DPLL SEL to set the DPLL to transcoder mapping and enable DPLL to the 460 transcoder. 461 e. [DevCPT] Configure DPLL_CTL DPLL_HDMI_multipler. 462 f. Configure PCH transcoder timings, M/N/TU, and other transcoder settings (should match CPU settings). 463 g. [DevCPT] Configure and enable Transcoder DisplayPort Control if DisplayPort will be used 464 h. Enable PCH transcoder 465 10. Enable ports (DisplayPort must enable in training pattern 1) 466 11. Enable panel power through panel power sequencing 467 12. Wait for panel power sequencing to reach enabled steady state 468 13. Disable panel power override 469 14. If DisplayPort, complete link training 470 15. Enable panel backlight 471 } 472 #endif 473 474 // make sure VGA display is disabled 475 write32(INTEL_VGA_DISPLAY_CONTROL, VGA_DISPLAY_DISABLED); 476 read32(INTEL_VGA_DISPLAY_CONTROL); 477 478 // Go over each port and set the display mode 479 for (uint32 i = 0; i < gInfo->port_count; i++) { 480 if (gInfo->ports[i] == NULL) 481 continue; 482 if (!gInfo->ports[i]->IsConnected()) 483 continue; 484 485 status_t status = gInfo->ports[i]->SetDisplayMode(&target, colorMode); 486 if (status != B_OK) 487 ERROR("%s: Unable to set display mode!\n", __func__); 488 } 489 490 TRACE("%s: Port configuration completed successfully!\n", __func__); 491 492 // We set the same color mode across all pipes 493 program_pipe_color_modes(colorMode); 494 495 // TODO: This may not be neccesary (see DPMS OFF at top) 496 set_display_power_mode(sharedInfo.dpms_mode); 497 498 // Changing bytes per row seems to be ignored if the plane/pipe is turned 499 // off 500 501 // Always set both pipes, just in case 502 // TODO rework this when we get multiple head support with different 503 // resolutions 504 if (sharedInfo.device_type.InFamily(INTEL_FAMILY_LAKE)) { 505 write32(INTEL_DISPLAY_A_BYTES_PER_ROW, bytesPerRow >> 6); 506 write32(INTEL_DISPLAY_B_BYTES_PER_ROW, bytesPerRow >> 6); 507 } else { 508 write32(INTEL_DISPLAY_A_BYTES_PER_ROW, bytesPerRow); 509 write32(INTEL_DISPLAY_B_BYTES_PER_ROW, bytesPerRow); 510 } 511 512 // update shared info 513 sharedInfo.current_mode = target; 514 sharedInfo.bytes_per_row = bytesPerRow; 515 sharedInfo.bits_per_pixel = bitsPerPixel; 516 517 set_frame_buffer_base(); 518 // triggers writing back double-buffered registers 519 // which is INTEL_DISPLAY_X_BYTES_PER_ROW only apparantly 520 521 // Second register dump 522 //dump_registers(); 523 524 return B_OK; 525 } 526 527 528 status_t 529 intel_get_display_mode(display_mode* _currentMode) 530 { 531 CALLED(); 532 533 *_currentMode = gInfo->shared_info->current_mode; 534 535 // This seems unreliable. We should always know the current_mode 536 //retrieve_current_mode(*_currentMode, INTEL_DISPLAY_A_PLL); 537 return B_OK; 538 } 539 540 541 status_t 542 intel_get_preferred_mode(display_mode* preferredMode) 543 { 544 TRACE("%s\n", __func__); 545 display_mode mode; 546 547 if (gInfo->has_edid || !gInfo->shared_info->got_vbt 548 || !gInfo->shared_info->device_type.IsMobile()) { 549 return B_ERROR; 550 } 551 552 mode.timing = gInfo->shared_info->panel_timing; 553 mode.space = B_RGB32; 554 mode.virtual_width = mode.timing.h_display; 555 mode.virtual_height = mode.timing.v_display; 556 mode.h_display_start = 0; 557 mode.v_display_start = 0; 558 mode.flags = 0; 559 memcpy(preferredMode, &mode, sizeof(mode)); 560 return B_OK; 561 } 562 563 564 status_t 565 intel_get_edid_info(void* info, size_t size, uint32* _version) 566 { 567 if (!gInfo->has_edid) 568 return B_ERROR; 569 if (size < sizeof(struct edid1_info)) 570 return B_BUFFER_OVERFLOW; 571 572 memcpy(info, &gInfo->edid_info, sizeof(struct edid1_info)); 573 *_version = EDID_VERSION_1; 574 return B_OK; 575 } 576 577 578 // Get the backlight registers. We need the backlight frequency (we never write it, but we ned to 579 // know it's value as the duty cycle/brihtness level is proportional to it), and the duty cycle 580 // register (read to get the current backlight value, written to set it). On older generations, 581 // the two values are in the same register (16 bits each), on newer ones there are two separate 582 // registers. 583 static int32_t 584 intel_get_backlight_register(bool period) 585 { 586 if (gInfo->shared_info->pch_info >= INTEL_PCH_CNP) { 587 if (period) 588 return PCH_SOUTH_BLC_PWM_PERIOD; 589 else 590 return PCH_SOUTH_BLC_PWM_DUTY_CYCLE; 591 } else if (gInfo->shared_info->pch_info >= INTEL_PCH_SPT) 592 return BLC_PWM_PCH_CTL2; 593 594 if (gInfo->shared_info->pch_info == INTEL_PCH_NONE) 595 return MCH_BLC_PWM_CTL; 596 597 // FIXME this mixup of south and north registers seems very strange; it should either be 598 // a single register with both period and duty in it, or two separate registers. 599 if (period) 600 return PCH_SOUTH_BLC_PWM_PERIOD; 601 else 602 return PCH_BLC_PWM_CTL; 603 } 604 605 606 status_t 607 intel_set_brightness(float brightness) 608 { 609 CALLED(); 610 611 if (brightness < 0 || brightness > 1) 612 return B_BAD_VALUE; 613 614 // The "duty cycle" is a proportion of the period (0 = backlight off, 615 // period = maximum brightness). 616 // Additionally we don't want it to be completely 0 here, because then 617 // it becomes hard to turn the display on again (at least until we get 618 // working ACPI keyboard shortcuts for this). So always keep the backlight 619 // at least a little bit on for now. 620 621 if (gInfo->shared_info->pch_info >= INTEL_PCH_CNP) { 622 uint32_t period = read32(intel_get_backlight_register(true)); 623 624 uint32_t duty = (uint32_t)(period * brightness); 625 duty = std::max(duty, (uint32_t)gInfo->shared_info->min_brightness); 626 627 write32(intel_get_backlight_register(false), duty); 628 } else if (gInfo->shared_info->pch_info >= INTEL_PCH_SPT) { 629 uint32_t period = read32(intel_get_backlight_register(true)) >> 16; 630 631 uint32_t duty = (uint32_t)(period * brightness) & 0xffff; 632 duty = std::max(duty, (uint32_t)gInfo->shared_info->min_brightness); 633 634 write32(intel_get_backlight_register(false), duty | (period << 16)); 635 } else { 636 // On older devices there is a single register with both period and duty cycle 637 uint32 tmp = read32(intel_get_backlight_register(true)); 638 bool legacyMode = false; 639 if (gInfo->shared_info->device_type.Generation() == 2 640 || gInfo->shared_info->device_type.IsModel(INTEL_MODEL_915M) 641 || gInfo->shared_info->device_type.IsModel(INTEL_MODEL_945M)) { 642 legacyMode = (tmp & BLM_LEGACY_MODE) != 0; 643 } 644 645 uint32_t period = tmp >> 16; 646 647 uint32_t mask = 0xffff; 648 uint32_t shift = 0; 649 if (gInfo->shared_info->device_type.Generation() < 4) { 650 // The low bit must be masked out because 651 // it is apparently used for something else on some Atom machines (no 652 // reference to that in the documentation that I know of). 653 mask = 0xfffe; 654 shift = 1; 655 period = tmp >> 17; 656 } 657 if (legacyMode) 658 period *= 0xfe; 659 uint32_t duty = (uint32_t)(period * brightness); 660 if (legacyMode) { 661 uint8 lpc = duty / 0xff + 1; 662 duty /= lpc; 663 664 // set pci config reg with lpc 665 intel_brightness_legacy brightnessLegacy; 666 brightnessLegacy.magic = INTEL_PRIVATE_DATA_MAGIC; 667 brightnessLegacy.lpc = lpc; 668 ioctl(gInfo->device, INTEL_SET_BRIGHTNESS_LEGACY, &brightnessLegacy, 669 sizeof(brightnessLegacy)); 670 } 671 672 duty = std::max(duty, (uint32_t)gInfo->shared_info->min_brightness); 673 duty <<= shift; 674 675 write32(intel_get_backlight_register(false), (duty & mask) | (tmp & ~mask)); 676 } 677 678 return B_OK; 679 } 680 681 682 status_t 683 intel_get_brightness(float* brightness) 684 { 685 CALLED(); 686 687 if (brightness == NULL) 688 return B_BAD_VALUE; 689 690 uint32_t duty; 691 uint32_t period; 692 693 if (gInfo->shared_info->pch_info >= INTEL_PCH_CNP) { 694 period = read32(intel_get_backlight_register(true)); 695 duty = read32(intel_get_backlight_register(false)); 696 } else { 697 uint32 tmp = read32(intel_get_backlight_register(true)); 698 bool legacyMode = false; 699 if (gInfo->shared_info->device_type.Generation() == 2 700 || gInfo->shared_info->device_type.IsModel(INTEL_MODEL_915M) 701 || gInfo->shared_info->device_type.IsModel(INTEL_MODEL_945M)) { 702 legacyMode = (tmp & BLM_LEGACY_MODE) != 0; 703 } 704 period = tmp >> 16; 705 duty = read32(intel_get_backlight_register(false)) & 0xffff; 706 if (legacyMode) { 707 period *= 0xff; 708 709 // get lpc from pci config reg 710 intel_brightness_legacy brightnessLegacy; 711 brightnessLegacy.magic = INTEL_PRIVATE_DATA_MAGIC; 712 ioctl(gInfo->device, INTEL_GET_BRIGHTNESS_LEGACY, &brightnessLegacy, 713 sizeof(brightnessLegacy)); 714 duty *= brightnessLegacy.lpc; 715 } 716 if (gInfo->shared_info->device_type.Generation() < 4) { 717 period >>= 1; 718 duty >>= 1; 719 } 720 } 721 *brightness = (float)duty / period; 722 723 return B_OK; 724 } 725 726 727 status_t 728 intel_get_frame_buffer_config(frame_buffer_config* config) 729 { 730 CALLED(); 731 732 uint32 offset = gInfo->shared_info->frame_buffer_offset; 733 734 config->frame_buffer = gInfo->shared_info->graphics_memory + offset; 735 config->frame_buffer_dma 736 = (uint8*)gInfo->shared_info->physical_graphics_memory + offset; 737 config->bytes_per_row = gInfo->shared_info->bytes_per_row; 738 739 return B_OK; 740 } 741 742 743 status_t 744 intel_get_pixel_clock_limits(display_mode* mode, uint32* _low, uint32* _high) 745 { 746 CALLED(); 747 748 if (_low != NULL) { 749 // lower limit of about 48Hz vertical refresh 750 uint32 totalClocks = (uint32)mode->timing.h_total 751 * (uint32)mode->timing.v_total; 752 uint32 low = (totalClocks * 48L) / 1000L; 753 if (low < gInfo->shared_info->pll_info.min_frequency) 754 low = gInfo->shared_info->pll_info.min_frequency; 755 else if (low > gInfo->shared_info->pll_info.max_frequency) 756 return B_ERROR; 757 758 *_low = low; 759 } 760 761 if (_high != NULL) 762 *_high = gInfo->shared_info->pll_info.max_frequency; 763 764 return B_OK; 765 } 766 767 768 status_t 769 intel_move_display(uint16 horizontalStart, uint16 verticalStart) 770 { 771 intel_shared_info &sharedInfo = *gInfo->shared_info; 772 Autolock locker(sharedInfo.accelerant_lock); 773 774 display_mode &mode = sharedInfo.current_mode; 775 776 if (horizontalStart + mode.timing.h_display > mode.virtual_width 777 || verticalStart + mode.timing.v_display > mode.virtual_height) 778 return B_BAD_VALUE; 779 780 mode.h_display_start = horizontalStart; 781 mode.v_display_start = verticalStart; 782 783 set_frame_buffer_base(); 784 785 return B_OK; 786 } 787 788 789 status_t 790 intel_get_timing_constraints(display_timing_constraints* constraints) 791 { 792 CALLED(); 793 return B_ERROR; 794 } 795 796 797 void 798 intel_set_indexed_colors(uint count, uint8 first, uint8* colors, uint32 flags) 799 { 800 TRACE("%s(colors = %p, first = %u)\n", __func__, colors, first); 801 802 if (colors == NULL) 803 return; 804 805 Autolock locker(gInfo->shared_info->accelerant_lock); 806 807 for (; count-- > 0; first++) { 808 uint32 color = colors[0] << 16 | colors[1] << 8 | colors[2]; 809 colors += 3; 810 811 write32(INTEL_DISPLAY_A_PALETTE + first * sizeof(uint32), color); 812 write32(INTEL_DISPLAY_B_PALETTE + first * sizeof(uint32), color); 813 } 814 } 815