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 } else if (gInfo->shared_info->pch_info >= INTEL_PCH_SPT) 590 return BLC_PWM_PCH_CTL2; 591 592 if (gInfo->shared_info->pch_info == INTEL_PCH_NONE) 593 return MCH_BLC_PWM_CTL; 594 595 // FIXME this mixup of south and north registers seems very strange; it should either be 596 // a single register with both period and duty in it, or two separate registers. 597 if (period) 598 return PCH_SOUTH_BLC_PWM_PERIOD; 599 else 600 return PCH_BLC_PWM_CTL; 601 } 602 603 604 status_t 605 intel_set_brightness(float brightness) 606 { 607 CALLED(); 608 609 if (brightness < 0 || brightness > 1) 610 return B_BAD_VALUE; 611 612 // The "duty cycle" is a proportion of the period (0 = backlight off, 613 // period = maximum brightness). 614 // Additionally we don't want it to be completely 0 here, because then 615 // it becomes hard to turn the display on again (at least until we get 616 // working ACPI keyboard shortcuts for this). So always keep the backlight 617 // at least a little bit on for now. 618 619 if (gInfo->shared_info->pch_info >= INTEL_PCH_CNP) { 620 uint32_t period = read32(intel_get_backlight_register(true)); 621 622 uint32_t duty = (uint32_t)(period * brightness); 623 duty = std::max(duty, (uint32_t)gInfo->shared_info->min_brightness); 624 625 write32(intel_get_backlight_register(false), duty); 626 } else if (gInfo->shared_info->pch_info >= INTEL_PCH_SPT) { 627 uint32_t period = read32(intel_get_backlight_register(true)) >> 16; 628 629 uint32_t duty = (uint32_t)(period * brightness) & 0xffff; 630 duty = std::max(duty, (uint32_t)gInfo->shared_info->min_brightness); 631 632 write32(intel_get_backlight_register(false), duty | (period << 16)); 633 } else { 634 // On older devices there is a single register with both period and duty cycle 635 uint32 tmp = read32(intel_get_backlight_register(true)); 636 bool legacyMode = false; 637 if (gInfo->shared_info->device_type.Generation() == 2 638 || gInfo->shared_info->device_type.IsModel(INTEL_MODEL_915M) 639 || gInfo->shared_info->device_type.IsModel(INTEL_MODEL_945M)) { 640 legacyMode = (tmp & BLM_LEGACY_MODE) != 0; 641 } 642 643 uint32_t period = tmp >> 16; 644 645 uint32_t mask = 0xffff; 646 uint32_t shift = 0; 647 if (gInfo->shared_info->device_type.Generation() < 4) { 648 // The low bit must be masked out because 649 // it is apparently used for something else on some Atom machines (no 650 // reference to that in the documentation that I know of). 651 mask = 0xfffe; 652 shift = 1; 653 period = tmp >> 17; 654 } 655 if (legacyMode) 656 period *= 0xfe; 657 uint32_t duty = (uint32_t)(period * brightness); 658 if (legacyMode) { 659 uint8 lpc = duty / 0xff + 1; 660 duty /= lpc; 661 662 // set pci config reg with lpc 663 intel_brightness_legacy brightnessLegacy; 664 brightnessLegacy.magic = INTEL_PRIVATE_DATA_MAGIC; 665 brightnessLegacy.lpc = lpc; 666 ioctl(gInfo->device, INTEL_SET_BRIGHTNESS_LEGACY, &brightnessLegacy, 667 sizeof(brightnessLegacy)); 668 } 669 670 duty = std::max(duty, (uint32_t)gInfo->shared_info->min_brightness); 671 duty <<= shift; 672 673 write32(intel_get_backlight_register(false), (duty & mask) | (tmp & ~mask)); 674 } 675 676 return B_OK; 677 } 678 679 680 status_t 681 intel_get_brightness(float* brightness) 682 { 683 CALLED(); 684 685 if (brightness == NULL) 686 return B_BAD_VALUE; 687 688 uint32_t duty; 689 uint32_t period; 690 691 if (gInfo->shared_info->pch_info >= INTEL_PCH_CNP) { 692 period = read32(intel_get_backlight_register(true)); 693 duty = read32(intel_get_backlight_register(false)); 694 } else { 695 uint32 tmp = read32(intel_get_backlight_register(true)); 696 bool legacyMode = false; 697 if (gInfo->shared_info->device_type.Generation() == 2 698 || gInfo->shared_info->device_type.IsModel(INTEL_MODEL_915M) 699 || gInfo->shared_info->device_type.IsModel(INTEL_MODEL_945M)) { 700 legacyMode = (tmp & BLM_LEGACY_MODE) != 0; 701 } 702 period = tmp >> 16; 703 duty = read32(intel_get_backlight_register(false)) & 0xffff; 704 if (legacyMode) { 705 period *= 0xff; 706 707 // get lpc from pci config reg 708 intel_brightness_legacy brightnessLegacy; 709 brightnessLegacy.magic = INTEL_PRIVATE_DATA_MAGIC; 710 ioctl(gInfo->device, INTEL_GET_BRIGHTNESS_LEGACY, &brightnessLegacy, 711 sizeof(brightnessLegacy)); 712 duty *= brightnessLegacy.lpc; 713 } 714 if (gInfo->shared_info->device_type.Generation() < 4) { 715 period >>= 1; 716 duty >>= 1; 717 } 718 } 719 *brightness = (float)duty / period; 720 721 return B_OK; 722 } 723 724 725 status_t 726 intel_get_frame_buffer_config(frame_buffer_config* config) 727 { 728 CALLED(); 729 730 uint32 offset = gInfo->shared_info->frame_buffer_offset; 731 732 config->frame_buffer = gInfo->shared_info->graphics_memory + offset; 733 config->frame_buffer_dma 734 = (uint8*)gInfo->shared_info->physical_graphics_memory + offset; 735 config->bytes_per_row = gInfo->shared_info->bytes_per_row; 736 737 return B_OK; 738 } 739 740 741 status_t 742 intel_get_pixel_clock_limits(display_mode* mode, uint32* _low, uint32* _high) 743 { 744 CALLED(); 745 746 if (_low != NULL) { 747 // lower limit of about 48Hz vertical refresh 748 uint32 totalClocks = (uint32)mode->timing.h_total 749 * (uint32)mode->timing.v_total; 750 uint32 low = (totalClocks * 48L) / 1000L; 751 if (low < gInfo->shared_info->pll_info.min_frequency) 752 low = gInfo->shared_info->pll_info.min_frequency; 753 else if (low > gInfo->shared_info->pll_info.max_frequency) 754 return B_ERROR; 755 756 *_low = low; 757 } 758 759 if (_high != NULL) 760 *_high = gInfo->shared_info->pll_info.max_frequency; 761 762 return B_OK; 763 } 764 765 766 status_t 767 intel_move_display(uint16 horizontalStart, uint16 verticalStart) 768 { 769 intel_shared_info &sharedInfo = *gInfo->shared_info; 770 Autolock locker(sharedInfo.accelerant_lock); 771 772 display_mode &mode = sharedInfo.current_mode; 773 774 if (horizontalStart + mode.timing.h_display > mode.virtual_width 775 || verticalStart + mode.timing.v_display > mode.virtual_height) 776 return B_BAD_VALUE; 777 778 mode.h_display_start = horizontalStart; 779 mode.v_display_start = verticalStart; 780 781 set_frame_buffer_base(); 782 783 return B_OK; 784 } 785 786 787 status_t 788 intel_get_timing_constraints(display_timing_constraints* constraints) 789 { 790 CALLED(); 791 return B_ERROR; 792 } 793 794 795 void 796 intel_set_indexed_colors(uint count, uint8 first, uint8* colors, uint32 flags) 797 { 798 TRACE("%s(colors = %p, first = %u)\n", __func__, colors, first); 799 800 if (colors == NULL) 801 return; 802 803 Autolock locker(gInfo->shared_info->accelerant_lock); 804 805 for (; count-- > 0; first++) { 806 uint32 color = colors[0] << 16 | colors[1] << 8 | colors[2]; 807 colors += 3; 808 809 write32(INTEL_DISPLAY_A_PALETTE + first * sizeof(uint32), color); 810 write32(INTEL_DISPLAY_B_PALETTE + first * sizeof(uint32), color); 811 } 812 } 813