1 /* 2 * Copyright 2011, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Alexander von Gluck IV, kallisti5@unixzen.com 7 */ 8 9 10 #include "displayport.h" 11 12 #include <Debug.h> 13 14 #include "accelerant_protos.h" 15 #include "connector.h" 16 #include "mode.h" 17 18 19 #undef TRACE 20 21 #define TRACE_DP 22 #ifdef TRACE_DP 23 # define TRACE(x...) _sPrintf("radeon_hd: " x) 24 #else 25 # define TRACE(x...) ; 26 #endif 27 28 #define ERROR(x...) _sPrintf("radeon_hd: " x) 29 30 31 static int 32 dp_aux_speak(uint32 hwPin, uint8* send, int sendBytes, 33 uint8* recv, int recvBytes, uint8 delay, uint8* ack) 34 { 35 if (hwPin == 0) { 36 ERROR("%s: cannot speak on invalid GPIO pin!\n", __func__); 37 return B_IO_ERROR; 38 } 39 40 int index = GetIndexIntoMasterTable(COMMAND, ProcessAuxChannelTransaction); 41 42 // Build AtomBIOS Transaction 43 union auxChannelTransaction { 44 PROCESS_AUX_CHANNEL_TRANSACTION_PS_ALLOCATION v1; 45 PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS_V2 v2; 46 }; 47 union auxChannelTransaction args; 48 memset(&args, 0, sizeof(args)); 49 50 args.v1.lpAuxRequest = 0; 51 args.v1.lpDataOut = 16; 52 args.v1.ucDataOutLen = 0; 53 args.v1.ucChannelID = hwPin; 54 args.v1.ucDelay = delay / 10; 55 56 //if (ASIC_IS_DCE4(rdev)) 57 // args.v2.ucHPD_ID = chan->rec.hpd; 58 59 unsigned char* base = (unsigned char*)gAtomContext->scratch; 60 memcpy(base, send, sendBytes); 61 62 atom_execute_table(gAtomContext, index, (uint32*)&args); 63 64 *ack = args.v1.ucReplyStatus; 65 66 switch (args.v1.ucReplyStatus) { 67 case 1: 68 ERROR("%s: dp_aux_ch timeout!\n", __func__); 69 return B_TIMED_OUT; 70 case 2: 71 ERROR("%s: dp_aux_ch flags not zero!\n", __func__); 72 return B_BUSY; 73 case 3: 74 ERROR("%s: dp_aux_ch error!\n", __func__); 75 return B_IO_ERROR; 76 } 77 78 int recvLength = args.v1.ucDataOutLen; 79 if (recvLength > recvBytes) 80 recvLength = recvBytes; 81 82 if (recv && recvBytes) 83 memcpy(recv, base + 16, recvLength); 84 85 return recvLength; 86 } 87 88 89 int 90 dp_aux_write(uint32 hwPin, uint16 address, 91 uint8* send, uint8 sendBytes, uint8 delay) 92 { 93 uint8 auxMessage[20]; 94 int auxMessageBytes = sendBytes + 4; 95 96 if (sendBytes > 16) 97 return -1; 98 99 auxMessage[0] = address; 100 auxMessage[1] = address >> 8; 101 auxMessage[2] = AUX_NATIVE_WRITE << 4; 102 auxMessage[3] = (auxMessageBytes << 4) | (sendBytes - 1); 103 memcpy(&auxMessage[4], send, sendBytes); 104 105 uint8 retry; 106 for (retry = 0; retry < 4; retry++) { 107 uint8 ack; 108 int result = dp_aux_speak(hwPin, auxMessage, auxMessageBytes, 109 NULL, 0, delay, &ack); 110 111 if (result == B_BUSY) 112 continue; 113 else if (result < B_OK) 114 return result; 115 116 if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK) 117 return sendBytes; 118 else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER) 119 snooze(400); 120 else 121 return B_IO_ERROR; 122 } 123 124 return B_IO_ERROR; 125 } 126 127 128 int 129 dp_aux_read(uint32 hwPin, uint16 address, 130 uint8* recv, int recvBytes, uint8 delay) 131 { 132 uint8 auxMessage[4]; 133 int auxMessageBytes = 4; 134 135 auxMessage[0] = address; 136 auxMessage[1] = address >> 8; 137 auxMessage[2] = AUX_NATIVE_READ << 4; 138 auxMessage[3] = (auxMessageBytes << 4) | (recvBytes - 1); 139 140 uint8 retry; 141 for (retry = 0; retry < 4; retry++) { 142 uint8 ack; 143 int result = dp_aux_speak(hwPin, auxMessage, auxMessageBytes, 144 recv, recvBytes, delay, &ack); 145 146 if (result == B_BUSY) 147 continue; 148 else if (result < B_OK) 149 return result; 150 151 if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK) 152 return result; 153 else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER) 154 snooze(400); 155 else 156 return B_IO_ERROR; 157 } 158 159 return B_IO_ERROR; 160 } 161 162 163 static void 164 dpcd_reg_write(uint32 hwPin, uint16 address, uint8 value) 165 { 166 dp_aux_write(hwPin, address, &value, 1, 0); 167 } 168 169 170 static uint8 171 dpcd_reg_read(uint32 hwPin, uint16 address) 172 { 173 uint8 value = 0; 174 dp_aux_read(hwPin, address, &value, 1, 0); 175 176 return value; 177 } 178 179 180 status_t 181 dp_aux_get_i2c_byte(uint32 hwPin, uint16 address, uint8* data, bool end) 182 { 183 uint8 auxMessage[5]; 184 int auxMessageBytes = 4; // 4 for read 185 186 /* Set up the command byte */ 187 auxMessage[2] = AUX_I2C_READ << 4; 188 if (end == false) 189 auxMessage[2] |= AUX_I2C_MOT << 4; 190 191 auxMessage[0] = address; 192 auxMessage[1] = address >> 8; 193 194 auxMessage[3] = auxMessageBytes << 4; 195 196 int retry; 197 for (retry = 0; retry < 4; retry++) { 198 uint8 ack; 199 uint8 reply[2]; 200 int replyBytes = 1; 201 202 int result = dp_aux_speak(hwPin, auxMessage, auxMessageBytes, 203 reply, replyBytes, 0, &ack); 204 if (result == B_BUSY) 205 continue; 206 else if (result < 0) { 207 ERROR("%s: aux_ch failed: %d\n", __func__, result); 208 return B_ERROR; 209 } 210 211 switch (ack & AUX_NATIVE_REPLY_MASK) { 212 case AUX_NATIVE_REPLY_ACK: 213 // I2C-over-AUX Reply field is only valid for AUX_ACK 214 break; 215 case AUX_NATIVE_REPLY_NACK: 216 TRACE("%s: aux_ch native nack\n", __func__); 217 return B_IO_ERROR; 218 case AUX_NATIVE_REPLY_DEFER: 219 TRACE("%s: aux_ch native defer\n", __func__); 220 snooze(400); 221 continue; 222 default: 223 TRACE("%s: aux_ch invalid native reply: 0x%02x\n", 224 __func__, ack); 225 return B_ERROR; 226 } 227 228 switch (ack & AUX_I2C_REPLY_MASK) { 229 case AUX_I2C_REPLY_ACK: 230 *data = reply[0]; 231 return B_OK; 232 case AUX_I2C_REPLY_NACK: 233 TRACE("%s: aux_i2c nack\n", __func__); 234 return B_IO_ERROR; 235 case AUX_I2C_REPLY_DEFER: 236 TRACE("%s: aux_i2c defer\n", __func__); 237 snooze(400); 238 break; 239 default: 240 TRACE("%s: aux_i2c invalid native reply: 0x%02x\n", 241 __func__, ack); 242 return B_ERROR; 243 } 244 } 245 246 TRACE("%s: aux i2c too many retries, giving up.\n", __func__); 247 return B_ERROR; 248 } 249 250 251 status_t 252 dp_aux_set_i2c_byte(uint32 hwPin, uint16 address, uint8* data, bool end) 253 { 254 uint8 auxMessage[5]; 255 int auxMessageBytes = 5; // 5 for write 256 257 /* Set up the command byte */ 258 auxMessage[2] = AUX_I2C_WRITE << 4; 259 if (end == false) 260 auxMessage[2] |= AUX_I2C_MOT << 4; 261 262 auxMessage[0] = address; 263 auxMessage[1] = address >> 8; 264 265 auxMessage[3] = auxMessageBytes << 4; 266 auxMessage[4] = *data; 267 268 int retry; 269 for (retry = 0; retry < 4; retry++) { 270 uint8 ack; 271 uint8 reply[2]; 272 int replyBytes = 1; 273 274 int result = dp_aux_speak(hwPin, auxMessage, auxMessageBytes, 275 reply, replyBytes, 0, &ack); 276 if (result == B_BUSY) 277 continue; 278 else if (result < 0) { 279 ERROR("%s: aux_ch failed: %d\n", __func__, result); 280 return B_ERROR; 281 } 282 283 switch (ack & AUX_NATIVE_REPLY_MASK) { 284 case AUX_NATIVE_REPLY_ACK: 285 // I2C-over-AUX Reply field is only valid for AUX_ACK 286 break; 287 case AUX_NATIVE_REPLY_NACK: 288 TRACE("%s: aux_ch native nack\n", __func__); 289 return B_IO_ERROR; 290 case AUX_NATIVE_REPLY_DEFER: 291 TRACE("%s: aux_ch native defer\n", __func__); 292 snooze(400); 293 continue; 294 default: 295 TRACE("%s: aux_ch invalid native reply: 0x%02x\n", 296 __func__, ack); 297 return B_ERROR; 298 } 299 300 switch (ack & AUX_I2C_REPLY_MASK) { 301 case AUX_I2C_REPLY_ACK: 302 // Success! 303 return B_OK; 304 case AUX_I2C_REPLY_NACK: 305 TRACE("%s: aux_i2c nack\n", __func__); 306 return B_IO_ERROR; 307 case AUX_I2C_REPLY_DEFER: 308 TRACE("%s: aux_i2c defer\n", __func__); 309 snooze(400); 310 break; 311 default: 312 TRACE("%s: aux_i2c invalid native reply: 0x%02x\n", 313 __func__, ack); 314 return B_ERROR; 315 } 316 } 317 318 TRACE("%s: aux i2c too many retries, giving up.\n", __func__); 319 return B_OK; 320 } 321 322 323 uint32 324 dp_get_link_clock(uint32 connectorIndex) 325 { 326 uint16 encoderID = gConnector[connectorIndex]->encoderExternal.objectID; 327 328 if (encoderID == ENCODER_OBJECT_ID_NUTMEG) 329 return 270000; 330 331 // TODO: calculate DisplayPort max pixel clock based on bpp and DP channels 332 return 162000; 333 } 334 335 336 void 337 dp_setup_connectors() 338 { 339 TRACE("%s\n", __func__); 340 341 for (uint32 index = 0; index < ATOM_MAX_SUPPORTED_DEVICE; index++) { 342 dp_info* dpInfo = &gConnector[index]->dpInfo; 343 dpInfo->valid = false; 344 if (gConnector[index]->valid == false) { 345 dpInfo->config[0] = 0; 346 continue; 347 } 348 349 if (connector_is_dp(index) == false) { 350 dpInfo->config[0] = 0; 351 continue; 352 } 353 354 uint32 gpioID = gConnector[index]->gpioID; 355 356 uint32 auxPin = gGPIOInfo[gpioID]->hwPin; 357 dpInfo->auxPin = auxPin; 358 359 uint8 auxMessage[25]; 360 int result; 361 362 result = dp_aux_read(auxPin, DP_DPCD_REV, auxMessage, 8, 0); 363 if (result > 0) { 364 dpInfo->valid = true; 365 memcpy(dpInfo->config, auxMessage, 8); 366 } 367 368 dpInfo->linkRate = dp_get_link_clock(index); 369 } 370 } 371 372 373 static bool 374 dp_get_link_status(dp_info* dp) 375 { 376 int result = dp_aux_read(dp->auxPin, DP_LANE_STATUS_0_1, 377 dp->linkStatus, DP_LINK_STATUS_SIZE, 100); 378 379 if (result <= 0) { 380 ERROR("%s: DisplayPort link status failed\n", __func__); 381 return false; 382 } 383 384 return true; 385 } 386 387 388 static uint8 389 dp_get_lane_status(dp_info* dp, int lane) 390 { 391 int i = DP_LANE_STATUS_0_1 + (lane >> 1); 392 int s = (lane & 1) * 4; 393 uint8 l = dp->linkStatus[i - DP_LANE_STATUS_0_1]; 394 return (l >> s) & 0xf; 395 } 396 397 398 static bool 399 dp_clock_recovery_ok(dp_info* dp) 400 { 401 int lane; 402 uint8 laneStatus; 403 404 for (lane = 0; lane < dp->laneCount; lane++) { 405 laneStatus = dp_get_lane_status(dp, lane); 406 if ((laneStatus & DP_LANE_STATUS_CR_DONE_A) == 0) 407 return false; 408 } 409 return true; 410 } 411 412 413 static void 414 dp_update_vs_emph(uint32 connectorIndex) 415 { 416 dp_info* dp = &gConnector[connectorIndex]->dpInfo; 417 418 // Set initial vs and emph on source 419 transmitter_dig_setup(connectorIndex, dp->linkRate, 0, 420 dp->trainingSet[0], ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH); 421 422 // Set vs and emph on the sink 423 dp_aux_write(dp->auxPin, DP_TRAIN_LANE0, 424 dp->trainingSet, dp->laneCount, 0); 425 } 426 427 428 static uint8 429 dp_get_adjust_request_voltage(dp_info* dp, int lane) 430 { 431 int i = DP_ADJ_REQUEST_0_1 + (lane >> 1); 432 int s = (((lane & 1) != 0) ? DP_ADJ_VCC_SWING_LANEB_SHIFT 433 : DP_ADJ_VCC_SWING_LANEA_SHIFT); 434 uint8 l = dp->linkStatus[i - DP_LANE_STATUS_0_1]; 435 436 return ((l >> s) & 0x3) << DP_TRAIN_VCC_SWING_SHIFT; 437 } 438 439 440 static uint8 441 dp_get_adjust_request_pre_emphasis(dp_info* dp, int lane) 442 { 443 int i = DP_ADJ_REQUEST_0_1 + (lane >> 1); 444 int s = (((lane & 1) != 0) ? DP_ADJ_PRE_EMPHASIS_LANEB_SHIFT 445 : DP_ADJ_PRE_EMPHASIS_LANEB_SHIFT); 446 uint8 l = dp->linkStatus[i - DP_LANE_STATUS_0_1]; 447 448 return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT; 449 } 450 451 452 static void 453 dp_get_adjust_train(dp_info* dp) 454 { 455 TRACE("%s\n", __func__); 456 457 const char* voltageNames[] = { 458 "0.4V", "0.6V", "0.8V", "1.2V" 459 }; 460 const char* preEmphasisNames[] = { 461 "0dB", "3.5dB", "6dB", "9.5dB" 462 }; 463 464 uint8 voltage = 0; 465 uint8 preEmphasis = 0; 466 int lane; 467 468 for (lane = 0; lane < dp->laneCount; lane++) { 469 uint8 laneVoltage = dp_get_adjust_request_voltage(dp, lane); 470 uint8 lanePreEmphasis = dp_get_adjust_request_pre_emphasis(dp, lane); 471 472 TRACE("%s: Requested %s at %s for lane %d\n", __func__, 473 preEmphasisNames[lanePreEmphasis >> DP_TRAIN_PRE_EMPHASIS_SHIFT], 474 voltageNames[laneVoltage >> DP_TRAIN_VCC_SWING_SHIFT], 475 lane); 476 477 if (laneVoltage > voltage) 478 voltage = laneVoltage; 479 if (lanePreEmphasis > preEmphasis) 480 preEmphasis = lanePreEmphasis; 481 } 482 483 // Check for maximum voltage and toggle max if reached 484 if (voltage >= DP_TRAIN_VCC_SWING_1200) 485 voltage |= DP_TRAIN_MAX_SWING_EN; 486 487 // Check for maximum pre-emphasis and toggle max if reached 488 if (preEmphasis >= DP_TRAIN_PRE_EMPHASIS_9_5) 489 preEmphasis |= DP_TRAIN_MAX_EMPHASIS_EN; 490 491 for (lane = 0; lane < 4; lane++) 492 dp->trainingSet[lane] = voltage | preEmphasis; 493 } 494 495 496 static void 497 dp_set_tp(uint32 connectorIndex, int trainingPattern) 498 { 499 TRACE("%s\n", __func__); 500 501 radeon_shared_info &info = *gInfo->shared_info; 502 dp_info* dp = &gConnector[connectorIndex]->dpInfo; 503 504 int rawTrainingPattern = 0; 505 506 /* set training pattern on the source */ 507 if (info.dceMajor >= 4 || !dp->trainingUseEncoder) { 508 switch (trainingPattern) { 509 case DP_TRAIN_PATTERN_1: 510 rawTrainingPattern = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN1; 511 break; 512 case DP_TRAIN_PATTERN_2: 513 rawTrainingPattern = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN2; 514 break; 515 case DP_TRAIN_PATTERN_3: 516 rawTrainingPattern = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN3; 517 break; 518 } 519 // TODO: PixelClock 0 ok? 520 encoder_dig_setup(connectorIndex, 0, rawTrainingPattern); 521 } else { 522 ERROR("%s: TODO: dp_encoder_service\n", __func__); 523 return; 524 #if 0 525 switch (trainingPattern) { 526 case DP_TRAINING_PATTERN_1: 527 rawTrainingPattern = 0; 528 break; 529 case DP_TRAINING_PATTERN_2: 530 rawTrainingPattern = 1; 531 break; 532 } 533 radeon_dp_encoder_service(dp_info->rdev, 534 ATOM_DP_ACTION_TRAINING_PATTERN_SEL, dp_info->dp_clock, 535 dp_info->enc_id, rawTrainingPattern); 536 #endif 537 } 538 539 // Enable training pattern on the sink 540 dpcd_reg_write(dp->auxPin, DP_TRAIN, trainingPattern); 541 } 542 543 544 status_t 545 dp_link_train_cr(uint32 connectorIndex) 546 { 547 TRACE("%s\n", __func__); 548 549 dp_info* dp = &gConnector[connectorIndex]->dpInfo; 550 551 // Display Port Clock Recovery Training 552 553 bool clockRecovery = false; 554 uint8 voltage = 0xff; 555 int lane; 556 557 dp_set_tp(connectorIndex, DP_TRAIN_PATTERN_1); 558 memset(dp->trainingSet, 0, 4); 559 dp_update_vs_emph(connectorIndex); 560 561 while (1) { 562 if (dp->trainingReadInterval == 0) 563 snooze(100); 564 else 565 snooze(1000 * 4 * dp->trainingReadInterval); 566 567 if (!dp_get_link_status(dp)) 568 break; 569 570 if (dp_clock_recovery_ok(dp)) { 571 clockRecovery = true; 572 break; 573 } 574 575 for (lane = 0; lane < dp->laneCount; lane++) { 576 if ((dp->trainingSet[lane] & DP_TRAIN_MAX_SWING_EN) == 0) 577 break; 578 } 579 580 if (lane == dp->laneCount) { 581 ERROR("%s: clock recovery reached max voltage\n", __func__); 582 break; 583 } 584 585 if ((dp->trainingSet[0] & DP_TRAIN_VCC_SWING_MASK) == voltage) { 586 dp->trainingAttempts++; 587 if (dp->trainingAttempts >= 5) { 588 ERROR("%s: clock recovery tried 5 times\n", __func__); 589 break; 590 } 591 } else 592 dp->trainingAttempts = 0; 593 594 voltage = dp->trainingSet[0] & DP_TRAIN_VCC_SWING_MASK; 595 596 // Compute new trainingSet as requested by sink 597 dp_get_adjust_train(dp); 598 599 dp_update_vs_emph(connectorIndex); 600 } 601 602 if (!clockRecovery) { 603 ERROR("%s: clock recovery failed\n", __func__); 604 return B_ERROR; 605 } 606 607 TRACE("%s: clock recovery at voltage %d pre-emphasis %d\n", 608 __func__, dp->trainingSet[0] & DP_TRAIN_VCC_SWING_MASK, 609 (dp->trainingSet[0] & DP_TRAIN_PRE_EMPHASIS_MASK) 610 >> DP_TRAIN_PRE_EMPHASIS_SHIFT); 611 return B_OK; 612 } 613 614 615 status_t 616 dp_link_train(uint8 crtcID, display_mode* mode) 617 { 618 TRACE("%s\n", __func__); 619 620 uint32 connectorIndex = gDisplay[crtcID]->connectorIndex; 621 dp_info* dp = &gConnector[connectorIndex]->dpInfo; 622 623 if (dp->valid != true) { 624 ERROR("%s: started on invalid DisplayPort connector #%" B_PRIu32 "\n", 625 __func__, connectorIndex); 626 return B_ERROR; 627 } 628 629 int index = GetIndexIntoMasterTable(COMMAND, DPEncoderService); 630 // Table version 631 uint8 tableMajor; 632 uint8 tableMinor; 633 634 dp->trainingUseEncoder = true; 635 if (atom_parse_cmd_header(gAtomContext, index, &tableMajor, &tableMinor) 636 == B_OK) { 637 if (tableMinor > 1) { 638 // The AtomBIOS DPEncoderService greater then 1.1 can't program the 639 // training pattern properly. 640 dp->trainingUseEncoder = false; 641 } 642 } 643 644 uint32 linkEnumeration 645 = gConnector[connectorIndex]->encoder.linkEnumeration; 646 uint32 gpioID = gConnector[connectorIndex]->gpioID; 647 uint32 hwPin = gGPIOInfo[gpioID]->hwPin; 648 649 uint32 dpEncoderID = 0; 650 if (encoder_pick_dig(connectorIndex) > 0) 651 dpEncoderID |= ATOM_DP_CONFIG_DIG2_ENCODER; 652 else 653 dpEncoderID |= ATOM_DP_CONFIG_DIG1_ENCODER; 654 if (linkEnumeration == GRAPH_OBJECT_ENUM_ID2) 655 dpEncoderID |= ATOM_DP_CONFIG_LINK_B; 656 else 657 dpEncoderID |= ATOM_DP_CONFIG_LINK_A; 658 659 dp->trainingReadInterval 660 = dpcd_reg_read(hwPin, DP_TRAINING_AUX_RD_INTERVAL); 661 662 uint8 sandbox = dpcd_reg_read(hwPin, DP_MAX_LANE_COUNT); 663 664 radeon_shared_info &info = *gInfo->shared_info; 665 //bool dpTPS3Supported = false; 666 //if (info.dceMajor >= 5 && (sandbox & DP_TPS3_SUPPORTED) != 0) 667 // dpTPS3Supported = true; 668 669 // *** DisplayPort link training initialization 670 671 // Power up the DP sink 672 if (dp->config[0] >= DP_DPCD_REV_11) 673 dpcd_reg_write(hwPin, DP_SET_POWER, DP_SET_POWER_D0); 674 675 // Possibly enable downspread on the sink 676 if ((dp->config[3] & 0x1) != 0) 677 dpcd_reg_write(hwPin, DP_DOWNSPREAD_CTRL, DP_DOWNSPREAD_CTRL_AMP_EN); 678 else 679 dpcd_reg_write(hwPin, DP_DOWNSPREAD_CTRL, 0); 680 681 encoder_dig_setup(connectorIndex, mode->timing.pixel_clock, 682 ATOM_ENCODER_CMD_SETUP_PANEL_MODE); 683 684 if (dp->config[0] >= DP_DPCD_REV_11) 685 sandbox |= DP_ENHANCED_FRAME_EN; 686 dpcd_reg_write(hwPin, DP_LANE_COUNT, sandbox); 687 688 // Set the link rate on the DP sink 689 sandbox = dp_encode_link_rate(dp->linkRate); 690 dpcd_reg_write(hwPin, DP_LINK_RATE, sandbox); 691 692 // Start link training on source 693 if (info.dceMajor >= 4 || !dp->trainingUseEncoder) { 694 encoder_dig_setup(connectorIndex, mode->timing.pixel_clock, 695 ATOM_ENCODER_CMD_DP_LINK_TRAINING_START); 696 } else { 697 ERROR("%s: TODO: cannot use AtomBIOS DPEncoderService on card!\n", 698 __func__); 699 } 700 701 // Disable the training pattern on the sink 702 dpcd_reg_write(hwPin, DP_TRAIN, DP_TRAIN_PATTERN_DISABLED); 703 704 dp_link_train_cr(connectorIndex); 705 // TODO: dp_link_train_ce 706 707 708 // *** DisplayPort link training finish 709 snooze(400); 710 711 // Disable the training pattern on the sink 712 dpcd_reg_write(hwPin, DP_TRAIN, DP_TRAIN_PATTERN_DISABLED); 713 714 // Disable the training pattern on the source 715 if (info.dceMajor >= 4 || !dp->trainingUseEncoder) { 716 encoder_dig_setup(connectorIndex, mode->timing.pixel_clock, 717 ATOM_ENCODER_CMD_DP_LINK_TRAINING_COMPLETE); 718 } else { 719 ERROR("%s: TODO: cannot use AtomBIOS DPEncoderService on card!\n", 720 __func__); 721 } 722 723 return B_OK; 724 } 725