1 /* 2 * Copyright 2011-2015, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Michael Lotz, mmlr@mlotz.ch 7 * Alexander von Gluck IV, kallisti5@unixzen.com 8 */ 9 10 11 #include "FlexibleDisplayInterface.h" 12 13 #include <stdlib.h> 14 #include <string.h> 15 #include <Debug.h> 16 #include <KernelExport.h> 17 18 #include "accelerant.h" 19 #include "intel_extreme.h" 20 21 22 #undef TRACE 23 #define TRACE_FDI 24 #ifdef TRACE_FDI 25 # define TRACE(x...) _sPrintf("intel_extreme: " x) 26 #else 27 # define TRACE(x...) 28 #endif 29 30 #define ERROR(x...) _sPrintf("intel_extreme: " x) 31 #define CALLED() TRACE("CALLED %s\n", __PRETTY_FUNCTION__) 32 33 34 static const int gSnbBFDITrainParam[] = { 35 FDI_LINK_TRAIN_400MV_0DB_SNB_B, 36 FDI_LINK_TRAIN_400MV_6DB_SNB_B, 37 FDI_LINK_TRAIN_600MV_3_5DB_SNB_B, 38 FDI_LINK_TRAIN_800MV_0DB_SNB_B, 39 }; 40 41 42 // #pragma mark - FDITransmitter 43 44 45 FDITransmitter::FDITransmitter(pipe_index pipeIndex) 46 : 47 fRegisterBase(PCH_FDI_TX_BASE_REGISTER) 48 { 49 if (pipeIndex == INTEL_PIPE_B) 50 fRegisterBase += PCH_FDI_TX_PIPE_OFFSET * 1; 51 } 52 53 54 FDITransmitter::~FDITransmitter() 55 { 56 } 57 58 59 void 60 FDITransmitter::Enable() 61 { 62 CALLED(); 63 uint32 targetRegister = fRegisterBase + PCH_FDI_TX_CONTROL; 64 uint32 value = read32(targetRegister); 65 66 write32(targetRegister, value | FDI_TX_ENABLE); 67 read32(targetRegister); 68 spin(150); 69 } 70 71 72 void 73 FDITransmitter::Disable() 74 { 75 CALLED(); 76 uint32 targetRegister = fRegisterBase + PCH_FDI_TX_CONTROL; 77 uint32 value = read32(targetRegister); 78 79 write32(targetRegister, value & ~FDI_TX_ENABLE); 80 read32(targetRegister); 81 spin(150); 82 } 83 84 85 bool 86 FDITransmitter::IsPLLEnabled() 87 { 88 CALLED(); 89 return (read32(fRegisterBase + PCH_FDI_TX_CONTROL) & FDI_TX_PLL_ENABLED) 90 != 0; 91 } 92 93 94 void 95 FDITransmitter::EnablePLL(uint32 lanes) 96 { 97 CALLED(); 98 uint32 targetRegister = fRegisterBase + PCH_FDI_TX_CONTROL; 99 uint32 value = read32(targetRegister); 100 if ((value & FDI_TX_PLL_ENABLED) != 0) { 101 // already enabled, possibly IronLake where it always is 102 TRACE("%s: Already enabled.\n", __func__); 103 return; 104 } 105 106 write32(targetRegister, value | FDI_TX_PLL_ENABLED); 107 read32(targetRegister); 108 spin(100); // warmup 10us + dmi delay 20us, be generous 109 } 110 111 112 void 113 FDITransmitter::DisablePLL() 114 { 115 CALLED(); 116 if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_ILK)) { 117 // on IronLake the FDI PLL is alaways enabled, so no point in trying... 118 return; 119 } 120 121 uint32 targetRegister = fRegisterBase + PCH_FDI_TX_CONTROL; 122 write32(targetRegister, read32(targetRegister) & ~FDI_TX_PLL_ENABLED); 123 read32(targetRegister); 124 spin(100); 125 } 126 127 128 // #pragma mark - FDIReceiver 129 130 131 FDIReceiver::FDIReceiver(pipe_index pipeIndex) 132 : 133 fRegisterBase(PCH_FDI_RX_BASE_REGISTER) 134 { 135 if (pipeIndex == INTEL_PIPE_B) 136 fRegisterBase += PCH_FDI_RX_PIPE_OFFSET * 1; 137 } 138 139 140 FDIReceiver::~FDIReceiver() 141 { 142 } 143 144 145 void 146 FDIReceiver::Enable() 147 { 148 CALLED(); 149 uint32 targetRegister = fRegisterBase + PCH_FDI_RX_CONTROL; 150 uint32 value = read32(targetRegister); 151 152 write32(targetRegister, value | FDI_RX_ENABLE); 153 read32(targetRegister); 154 spin(150); 155 } 156 157 158 void 159 FDIReceiver::Disable() 160 { 161 CALLED(); 162 uint32 targetRegister = fRegisterBase + PCH_FDI_RX_CONTROL; 163 uint32 value = read32(targetRegister); 164 165 write32(targetRegister, value & ~FDI_RX_ENABLE); 166 read32(targetRegister); 167 spin(150); 168 } 169 170 171 bool 172 FDIReceiver::IsPLLEnabled() 173 { 174 CALLED(); 175 return (read32(fRegisterBase + PCH_FDI_RX_CONTROL) & FDI_RX_PLL_ENABLED) 176 != 0; 177 } 178 179 180 void 181 FDIReceiver::EnablePLL(uint32 lanes) 182 { 183 CALLED(); 184 uint32 targetRegister = fRegisterBase + PCH_FDI_RX_CONTROL; 185 uint32 value = read32(targetRegister); 186 if ((value & FDI_RX_PLL_ENABLED) != 0) { 187 // already enabled, possibly IronLake where it always is 188 TRACE("%s: Already enabled.\n", __func__); 189 return; 190 } 191 192 value &= ~(FDI_DP_PORT_WIDTH_MASK | (0x7 << 16)); 193 value |= FDI_DP_PORT_WIDTH(lanes); 194 //value |= (read32(PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11; 195 196 write32(targetRegister, value | FDI_RX_PLL_ENABLED); 197 read32(targetRegister); 198 spin(200); // warmup 10us + dmi delay 20us, be generous 199 } 200 201 202 void 203 FDIReceiver::DisablePLL() 204 { 205 CALLED(); 206 uint32 targetRegister = fRegisterBase + PCH_FDI_RX_CONTROL; 207 write32(targetRegister, read32(targetRegister) & ~FDI_RX_PLL_ENABLED); 208 read32(targetRegister); 209 spin(100); 210 } 211 212 213 void 214 FDIReceiver::SwitchClock(bool toPCDClock) 215 { 216 CALLED(); 217 uint32 targetRegister = fRegisterBase + PCH_FDI_RX_CONTROL; 218 write32(targetRegister, (read32(targetRegister) & ~FDI_RX_CLOCK_MASK) 219 | (toPCDClock ? FDI_RX_CLOCK_PCD : FDI_RX_CLOCK_RAW)); 220 read32(targetRegister); 221 spin(200); 222 } 223 224 225 // #pragma mark - FDILink 226 227 228 FDILink::FDILink(pipe_index pipeIndex) 229 : 230 fTransmitter(pipeIndex), 231 fReceiver(pipeIndex), 232 fPipeIndex(pipeIndex) 233 { 234 } 235 236 237 status_t 238 FDILink::Train(display_mode* target) 239 { 240 CALLED(); 241 242 uint32 bitsPerPixel; 243 switch (target->space) { 244 case B_RGB32_LITTLE: 245 bitsPerPixel = 32; 246 break; 247 case B_RGB16_LITTLE: 248 bitsPerPixel = 16; 249 break; 250 case B_RGB15_LITTLE: 251 bitsPerPixel = 15; 252 break; 253 case B_CMAP8: 254 default: 255 bitsPerPixel = 8; 256 break; 257 } 258 259 // Khz / 10. ( each output octet encoded as 10 bits. 260 uint32 linkBandwidth = gInfo->shared_info->fdi_link_frequency * 1000 / 10; 261 uint32 bps = target->timing.pixel_clock * bitsPerPixel * 21 / 20; 262 263 uint32 lanes = bps / (linkBandwidth * 8); 264 265 TRACE("%s: FDI Link Lanes: %" B_PRIu32 "\n", __func__, lanes); 266 267 // Enable FDI clocks 268 Receiver().EnablePLL(lanes); 269 Receiver().SwitchClock(true); 270 Transmitter().EnablePLL(lanes); 271 272 status_t result = B_ERROR; 273 274 // TODO: Only _AutoTrain on IVYB Stepping B or later 275 // otherwise, _ManualTrain 276 if (gInfo->shared_info->device_type.Generation() >= 7) 277 result = _AutoTrain(lanes); 278 else if (gInfo->shared_info->device_type.Generation() == 6) 279 result = _SnbTrain(lanes); 280 else if (gInfo->shared_info->device_type.Generation() == 5) 281 result = _IlkTrain(lanes); 282 else 283 result = _NormalTrain(lanes); 284 285 if (result != B_OK) { 286 ERROR("%s: FDI training fault.\n", __func__); 287 } 288 289 return result; 290 } 291 292 293 status_t 294 FDILink::_NormalTrain(uint32 lanes) 295 { 296 CALLED(); 297 uint32 txControl = Transmitter().Base() + PCH_FDI_TX_CONTROL; 298 uint32 rxControl = Receiver().Base() + PCH_FDI_RX_CONTROL; 299 300 // Enable normal link training 301 uint32 tmp = read32(txControl); 302 if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IVB)) { 303 tmp &= ~FDI_LINK_TRAIN_NONE_IVB; 304 tmp |= FDI_LINK_TRAIN_NONE_IVB | FDI_TX_ENHANCE_FRAME_ENABLE; 305 } else { 306 tmp &= ~FDI_LINK_TRAIN_NONE; 307 tmp |= FDI_LINK_TRAIN_NONE | FDI_TX_ENHANCE_FRAME_ENABLE; 308 } 309 write32(txControl, tmp); 310 311 tmp = read32(rxControl); 312 if (gInfo->shared_info->pch_info == INTEL_PCH_CPT) { 313 tmp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; 314 tmp |= FDI_LINK_TRAIN_NORMAL_CPT; 315 } else { 316 tmp &= ~FDI_LINK_TRAIN_NONE; 317 tmp |= FDI_LINK_TRAIN_NONE; 318 } 319 write32(rxControl, tmp | FDI_RX_ENHANCE_FRAME_ENABLE); 320 321 // Wait 1x idle pattern 322 read32(rxControl); 323 spin(1000); 324 325 // Enable ecc on IVB 326 if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IVB)) { 327 write32(rxControl, read32(rxControl) 328 | FDI_FS_ERRC_ENABLE | FDI_FE_ERRC_ENABLE); 329 read32(rxControl); 330 } 331 332 return B_OK; 333 } 334 335 336 status_t 337 FDILink::_IlkTrain(uint32 lanes) 338 { 339 CALLED(); 340 uint32 txControl = Transmitter().Base() + PCH_FDI_TX_CONTROL; 341 uint32 rxControl = Receiver().Base() + PCH_FDI_RX_CONTROL; 342 343 // Train 1: unmask FDI RX Interrupt symbol_lock and bit_lock 344 uint32 tmp = read32(Receiver().Base() + PCH_FDI_RX_IMR); 345 tmp &= ~FDI_RX_SYMBOL_LOCK; 346 tmp &= ~FDI_RX_BIT_LOCK; 347 write32(Receiver().Base() + PCH_FDI_RX_IMR, tmp); 348 spin(150); 349 350 // Enable CPU FDI TX and RX 351 tmp = read32(txControl); 352 tmp &= ~FDI_DP_PORT_WIDTH_MASK; 353 tmp |= FDI_DP_PORT_WIDTH(lanes); 354 tmp &= ~FDI_LINK_TRAIN_NONE; 355 tmp |= FDI_LINK_TRAIN_PATTERN_1; 356 write32(txControl, tmp); 357 Transmitter().Enable(); 358 359 tmp = read32(rxControl); 360 tmp &= ~FDI_LINK_TRAIN_NONE; 361 tmp |= FDI_LINK_TRAIN_PATTERN_1; 362 write32(rxControl, tmp); 363 Receiver().Enable(); 364 365 // ILK Workaround, enable clk after FDI enable 366 if (fPipeIndex == INTEL_PIPE_B) { 367 write32(PCH_FDI_RXB_CHICKEN, FDI_RX_PHASE_SYNC_POINTER_OVR); 368 write32(PCH_FDI_RXB_CHICKEN, FDI_RX_PHASE_SYNC_POINTER_OVR 369 | FDI_RX_PHASE_SYNC_POINTER_EN); 370 } else { 371 write32(PCH_FDI_RXA_CHICKEN, FDI_RX_PHASE_SYNC_POINTER_OVR); 372 write32(PCH_FDI_RXA_CHICKEN, FDI_RX_PHASE_SYNC_POINTER_OVR 373 | FDI_RX_PHASE_SYNC_POINTER_EN); 374 } 375 376 uint32 iirControl = Receiver().Base() + PCH_FDI_RX_IIR; 377 TRACE("%s: FDI RX IIR Control @ 0x%" B_PRIx32 "\n", __func__, iirControl); 378 379 int tries = 0; 380 for (tries = 0; tries < 5; tries++) { 381 tmp = read32(iirControl); 382 TRACE("%s: FDI RX IIR 0x%" B_PRIx32 "\n", __func__, tmp); 383 384 if ((tmp & FDI_RX_BIT_LOCK)) { 385 TRACE("%s: FDI train 1 done\n", __func__); 386 write32(iirControl, tmp | FDI_RX_BIT_LOCK); 387 break; 388 } 389 } 390 391 if (tries == 5) { 392 ERROR("%s: FDI train 1 failure!\n", __func__); 393 return B_ERROR; 394 } 395 396 // Train 2 397 tmp = read32(txControl); 398 tmp &= ~FDI_LINK_TRAIN_NONE; 399 tmp |= FDI_LINK_TRAIN_PATTERN_2; 400 write32(txControl, tmp); 401 402 tmp = read32(rxControl); 403 tmp &= ~FDI_LINK_TRAIN_NONE; 404 tmp |= FDI_LINK_TRAIN_PATTERN_2; 405 write32(rxControl, tmp); 406 407 read32(rxControl); 408 spin(150); 409 410 for (tries = 0; tries < 5; tries++) { 411 tmp = read32(iirControl); 412 TRACE("%s: FDI RX IIR 0x%" B_PRIx32 "\n", __func__, tmp); 413 414 if (tmp & FDI_RX_SYMBOL_LOCK) { 415 TRACE("%s: FDI train 2 done\n", __func__); 416 write32(iirControl, tmp | FDI_RX_SYMBOL_LOCK); 417 break; 418 } 419 } 420 421 if (tries == 5) { 422 ERROR("%s: FDI train 2 failure!\n", __func__); 423 return B_ERROR; 424 } 425 426 return B_OK; 427 } 428 429 430 status_t 431 FDILink::_SnbTrain(uint32 lanes) 432 { 433 CALLED(); 434 uint32 txControl = Transmitter().Base() + PCH_FDI_TX_CONTROL; 435 uint32 rxControl = Receiver().Base() + PCH_FDI_RX_CONTROL; 436 437 // Train 1 438 uint32 imrControl = Receiver().Base() + PCH_FDI_RX_IMR; 439 uint32 tmp = read32(imrControl); 440 tmp &= ~FDI_RX_SYMBOL_LOCK; 441 tmp &= ~FDI_RX_BIT_LOCK; 442 write32(imrControl, tmp); 443 read32(imrControl); 444 spin(150); 445 446 tmp = read32(txControl); 447 tmp &= ~FDI_DP_PORT_WIDTH_MASK; 448 tmp |= FDI_DP_PORT_WIDTH(lanes); 449 tmp &= ~FDI_LINK_TRAIN_NONE; 450 tmp |= FDI_LINK_TRAIN_PATTERN_1; 451 tmp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; 452 453 tmp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B; 454 write32(txControl, tmp); 455 456 write32(Receiver().Base() + PCH_FDI_RX_MISC, 457 FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90); 458 459 tmp = read32(rxControl); 460 if (gInfo->shared_info->pch_info == INTEL_PCH_CPT) { 461 tmp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; 462 tmp |= FDI_LINK_TRAIN_PATTERN_1_CPT; 463 } else { 464 tmp &= ~FDI_LINK_TRAIN_NONE; 465 tmp |= FDI_LINK_TRAIN_PATTERN_1; 466 } 467 write32(rxControl, rxControl); 468 Receiver().Enable(); 469 470 uint32 iirControl = Receiver().Base() + PCH_FDI_RX_IIR; 471 TRACE("%s: FDI RX IIR Control @ 0x%" B_PRIx32 "\n", __func__, iirControl); 472 473 int i = 0; 474 for (i = 0; i < 4; i++) { 475 tmp = read32(txControl); 476 tmp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; 477 tmp |= gSnbBFDITrainParam[i]; 478 write32(txControl, tmp); 479 480 read32(txControl); 481 spin(500); 482 483 int retry = 0; 484 for (retry = 0; retry < 5; retry++) { 485 tmp = read32(iirControl); 486 TRACE("%s: FDI RX IIR 0x%" B_PRIx32 "\n", __func__, tmp); 487 if (tmp & FDI_RX_BIT_LOCK) { 488 TRACE("%s: FDI train 1 done\n", __func__); 489 write32(iirControl, tmp | FDI_RX_BIT_LOCK); 490 break; 491 } 492 spin(50); 493 } 494 if (retry < 5) 495 break; 496 } 497 498 if (i == 4) { 499 ERROR("%s: FDI train 1 failure!\n", __func__); 500 return B_ERROR; 501 } 502 503 // Train 2 504 tmp = read32(txControl); 505 tmp &= ~FDI_LINK_TRAIN_NONE; 506 tmp |= FDI_LINK_TRAIN_PATTERN_2; 507 508 // if gen6? It's always gen6 509 tmp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; 510 tmp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B; 511 write32(txControl, tmp); 512 513 tmp = read32(rxControl); 514 if (gInfo->shared_info->pch_info == INTEL_PCH_CPT) { 515 tmp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; 516 tmp |= FDI_LINK_TRAIN_PATTERN_2_CPT; 517 } else { 518 tmp &= ~FDI_LINK_TRAIN_NONE; 519 tmp |= FDI_LINK_TRAIN_PATTERN_2; 520 } 521 write32(rxControl, tmp); 522 523 read32(rxControl); 524 spin(150); 525 526 for (i = 0; i < 4; i++) { 527 tmp = read32(txControl); 528 tmp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; 529 tmp |= gSnbBFDITrainParam[i]; 530 write32(txControl, tmp); 531 532 read32(txControl); 533 spin(500); 534 535 int retry = 0; 536 for (retry = 0; retry < 5; retry++) { 537 tmp = read32(iirControl); 538 TRACE("%s: FDI RX IIR 0x%" B_PRIx32 "\n", __func__, tmp); 539 540 if (tmp & FDI_RX_SYMBOL_LOCK) { 541 TRACE("%s: FDI train 2 done\n", __func__); 542 write32(iirControl, tmp | FDI_RX_SYMBOL_LOCK); 543 break; 544 } 545 spin(50); 546 } 547 if (retry < 5) 548 break; 549 } 550 551 if (i == 4) { 552 ERROR("%s: FDI train 1 failure!\n", __func__); 553 return B_ERROR; 554 } 555 556 return B_OK; 557 } 558 559 560 status_t 561 FDILink::_ManualTrain(uint32 lanes) 562 { 563 CALLED(); 564 //uint32 txControl = Transmitter().Base() + PCH_FDI_TX_CONTROL; 565 //uint32 rxControl = Receiver().Base() + PCH_FDI_RX_CONTROL; 566 567 ERROR("%s: TODO\n", __func__); 568 569 return B_ERROR; 570 } 571 572 573 status_t 574 FDILink::_AutoTrain(uint32 lanes) 575 { 576 CALLED(); 577 uint32 txControl = Transmitter().Base() + PCH_FDI_TX_CONTROL; 578 uint32 rxControl = Receiver().Base() + PCH_FDI_RX_CONTROL; 579 580 uint32 buffer = read32(txControl); 581 582 // Clear port width selection and set number of lanes 583 buffer &= ~(7 << 19); 584 buffer |= (lanes - 1) << 19; 585 586 if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IVB)) 587 buffer &= ~FDI_LINK_TRAIN_NONE_IVB; 588 else 589 buffer &= ~FDI_LINK_TRAIN_NONE; 590 write32(txControl, buffer); 591 592 bool trained = false; 593 594 for (uint32 i = 0; i < (sizeof(gSnbBFDITrainParam) 595 / sizeof(gSnbBFDITrainParam[0])); i++) { 596 for (int j = 0; j < 2; j++) { 597 buffer = read32(txControl); 598 buffer |= FDI_AUTO_TRAINING; 599 buffer &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; 600 buffer |= gSnbBFDITrainParam[i]; 601 write32(txControl, buffer | FDI_TX_ENABLE); 602 603 write32(rxControl, read32(rxControl) | FDI_RX_ENABLE); 604 605 spin(5); 606 607 buffer = read32(txControl); 608 if ((buffer & FDI_AUTO_TRAIN_DONE) != 0) { 609 TRACE("%s: FDI auto train complete!\n", __func__); 610 trained = true; 611 break; 612 } 613 614 write32(txControl, read32(txControl) & ~FDI_TX_ENABLE); 615 write32(rxControl, read32(rxControl) & ~FDI_RX_ENABLE); 616 read32(rxControl); 617 618 spin(31); 619 } 620 621 // If Trained, we fall out of autotraining 622 if (trained) 623 break; 624 } 625 626 if (!trained) { 627 ERROR("%s: FDI auto train failed!\n", __func__); 628 return B_ERROR; 629 } 630 631 // Enable ecc on IVB 632 if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IVB)) { 633 write32(rxControl, read32(rxControl) 634 | FDI_FS_ERRC_ENABLE | FDI_FE_ERRC_ENABLE); 635 read32(rxControl); 636 } 637 638 return B_OK; 639 } 640 641 642 FDILink::~FDILink() 643 { 644 } 645