BPicture: Fix archive constructor.
[haiku.git] / src / add-ons / accelerants / radeon_hd / displayport.cpp
blobb70f92b6a8769e468bb47ef2bcdc350320e7102a
1 /*
2 * Copyright 2011-2015, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Alexander von Gluck IV, kallisti5@unixzen.com
7 * Bill Randle, billr@neocat.org
8 */
11 #include "displayport.h"
13 #include <Debug.h>
15 #include "accelerant_protos.h"
16 #include "connector.h"
17 #include "mode.h"
18 #include "edid.h"
19 #include "encoder.h"
22 #undef TRACE
24 #define TRACE_DP
25 #ifdef TRACE_DP
26 # define TRACE(x...) _sPrintf("radeon_hd: " x)
27 #else
28 # define TRACE(x...) ;
29 #endif
31 #define ERROR(x...) _sPrintf("radeon_hd: " x)
34 static ssize_t
35 dp_aux_speak(uint32 connectorIndex, uint8* send, int sendBytes,
36 uint8* recv, int recvBytes, uint8 delay, uint8* ack)
38 dp_info* dpInfo = &gConnector[connectorIndex]->dpInfo;
39 if (dpInfo->auxPin == 0) {
40 ERROR("%s: cannot speak on invalid GPIO pin!\n", __func__);
41 return B_IO_ERROR;
44 int index = GetIndexIntoMasterTable(COMMAND, ProcessAuxChannelTransaction);
46 // Build AtomBIOS Transaction
47 union auxChannelTransaction {
48 PROCESS_AUX_CHANNEL_TRANSACTION_PS_ALLOCATION v1;
49 PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS_V2 v2;
51 union auxChannelTransaction args;
52 memset(&args, 0, sizeof(args));
54 args.v2.lpAuxRequest = B_HOST_TO_LENDIAN_INT16(0 + 4);
55 args.v2.lpDataOut = B_HOST_TO_LENDIAN_INT16(16 + 4);
56 args.v2.ucDataOutLen = 0;
57 args.v2.ucChannelID = dpInfo->auxPin;
58 args.v2.ucDelay = delay / 10;
60 // Careful! This value differs in different atombios calls :-|
61 args.v2.ucHPD_ID = connector_pick_atom_hpdid(connectorIndex);
63 unsigned char* base = (unsigned char*)(gAtomContext->scratch + 1);
65 // TODO: This isn't correct for big endian systems!
66 // send needs to be swapped on big endian.
67 memcpy(base, send, sendBytes);
69 atom_execute_table(gAtomContext, index, (uint32*)&args);
71 *ack = args.v2.ucReplyStatus;
73 switch (args.v2.ucReplyStatus) {
74 case 1:
75 ERROR("%s: dp_aux channel timeout!\n", __func__);
76 return B_TIMED_OUT;
77 case 2:
78 ERROR("%s: dp_aux channel flags not zero!\n", __func__);
79 return B_BUSY;
80 case 3:
81 ERROR("%s: dp_aux channel error!\n", __func__);
82 return B_IO_ERROR;
85 int recvLength = args.v1.ucDataOutLen;
86 if (recvLength > recvBytes)
87 recvLength = recvBytes;
89 // TODO: This isn't correct for big endian systems!
90 // recv needs to be swapped on big endian.
91 if (recv && recvBytes)
92 memcpy(recv, base + 16, recvLength);
94 return recvLength;
98 status_t
99 dp_aux_transaction(uint32 connectorIndex, dp_aux_msg* message)
101 uint8 delay = 0;
102 if (message == NULL) {
103 ERROR("%s: DP message is invalid!\n", __func__);
104 return B_ERROR;
107 if (message->size > 16) {
108 ERROR("%s: Too many bytes! (%" B_PRIuSIZE ")\n", __func__,
109 message->size);
110 return B_ERROR;
113 uint8 transactionSize = 4;
115 switch(message->request & ~DP_AUX_I2C_MOT) {
116 case DP_AUX_NATIVE_WRITE:
117 case DP_AUX_I2C_WRITE:
118 transactionSize += message->size;
119 break;
122 // If not bare address, check for buffer
123 if (message->size > 0 && message->buffer == NULL) {
124 ERROR("%s: DP message uninitalized buffer!\n", __func__);
125 return B_ERROR;
128 uint8 auxMessage[20];
129 auxMessage[0] = message->address & 0xff;
130 auxMessage[1] = message->address >> 8;
131 auxMessage[2] = message->request << 4;
132 auxMessage[3] = message->size ? (message->size - 1) : 0;
134 if (message->size == 0)
135 auxMessage[3] |= 3 << 4;
136 else
137 auxMessage[3] |= transactionSize << 4;
139 uint8 retry;
140 for (retry = 0; retry < 7; retry++) {
141 uint8 ack;
142 ssize_t result = B_ERROR;
143 switch(message->request & ~DP_AUX_I2C_MOT) {
144 case DP_AUX_NATIVE_WRITE:
145 case DP_AUX_I2C_WRITE:
146 memcpy(auxMessage + 4, message->buffer, message->size);
147 result = dp_aux_speak(connectorIndex, auxMessage,
148 transactionSize, NULL, 0, delay, &ack);
149 break;
150 case DP_AUX_NATIVE_READ:
151 case DP_AUX_I2C_READ:
152 result = dp_aux_speak(connectorIndex, auxMessage,
153 transactionSize, (uint8*)message->buffer, message->size,
154 delay, &ack);
155 break;
156 default:
157 ERROR("%s: Unknown dp_aux_msg request!\n", __func__);
158 return B_ERROR;
161 if (result == B_BUSY)
162 continue;
163 else if (result < B_OK)
164 return result;
166 ack >>= 4;
167 message->reply = ack;
168 switch(message->reply & DP_AUX_NATIVE_REPLY_MASK) {
169 case DP_AUX_NATIVE_REPLY_ACK:
170 return B_OK;
171 case DP_AUX_NATIVE_REPLY_DEFER:
172 TRACE("%s: aux reply defer received. Snoozing.\n", __func__);
173 snooze(400);
174 break;
175 default:
176 TRACE("%s: aux invalid native reply: 0x%02x\n", __func__,
177 message->reply);
178 return B_IO_ERROR;
182 ERROR("%s: IO Error. %" B_PRIu8 " attempts\n", __func__, retry);
183 return B_IO_ERROR;
187 void
188 dpcd_reg_write(uint32 connectorIndex, uint16 address, uint8 value)
190 //TRACE("%s: connector(%" B_PRId32 "): 0x%" B_PRIx16 " -> 0x%" B_PRIx8 "\n",
191 // __func__, connectorIndex, address, value);
192 dp_aux_msg message;
193 memset(&message, 0, sizeof(message));
195 message.address = address;
196 message.buffer = &value;
197 message.request = DP_AUX_NATIVE_WRITE;
198 message.size = 1;
200 status_t result = dp_aux_transaction(connectorIndex, &message);
201 if (result != B_OK) {
202 ERROR("%s: error on DisplayPort aux write (0x%" B_PRIx32 ")\n",
203 __func__, result);
208 uint8
209 dpcd_reg_read(uint32 connectorIndex, uint16 address)
211 //TRACE("%s: connector(%" B_PRId32 "): read 0x%" B_PRIx16 ".\n",
212 // __func__, connectorIndex, address);
213 uint8 response[3];
215 dp_aux_msg message;
216 memset(&message, 0, sizeof(message));
218 message.address = address;
219 message.buffer = &response;
220 message.request = DP_AUX_NATIVE_READ;
221 message.size = 1;
223 status_t result = dp_aux_transaction(connectorIndex, &message);
224 if (result != B_OK) {
225 ERROR("%s: error on DisplayPort aux read (0x%" B_PRIx32 ")\n",
226 __func__, result);
229 return response[0];
233 status_t
234 dp_aux_get_i2c_byte(uint32 connectorIndex, uint16 address, uint8* data,
235 bool start, bool stop)
237 uint8 reply[3];
238 dp_aux_msg message;
239 memset(&message, 0, sizeof(message));
241 message.address = address;
242 message.buffer = reply;
243 message.request = DP_AUX_I2C_READ;
244 message.size = 1;
245 if (stop == false) {
246 // Remove Middle-Of-Transmission on final transaction
247 message.request |= DP_AUX_I2C_MOT;
249 if (stop || start) {
250 // Bare address packet
251 message.buffer = NULL;
252 message.size = 0;
255 for (int attempt = 0; attempt < 7; attempt++) {
256 status_t result = dp_aux_transaction(connectorIndex, &message);
257 if (result != B_OK) {
258 ERROR("%s: aux_ch transaction failed!\n", __func__);
259 return result;
262 switch (message.reply & DP_AUX_I2C_REPLY_MASK) {
263 case DP_AUX_I2C_REPLY_ACK:
264 *data = reply[0];
265 return B_OK;
266 case DP_AUX_I2C_REPLY_NACK:
267 TRACE("%s: aux i2c nack\n", __func__);
268 return B_IO_ERROR;
269 case DP_AUX_I2C_REPLY_DEFER:
270 TRACE("%s: aux i2c defer\n", __func__);
271 snooze(400);
272 break;
273 default:
274 TRACE("%s: aux invalid I2C reply: 0x%02x\n",
275 __func__, message.reply);
276 return B_ERROR;
279 return B_ERROR;
283 status_t
284 dp_aux_set_i2c_byte(uint32 connectorIndex, uint16 address, uint8* data,
285 bool start, bool stop)
287 dp_aux_msg message;
288 memset(&message, 0, sizeof(message));
290 message.address = address;
291 message.buffer = data;
292 message.request = DP_AUX_I2C_WRITE;
293 message.size = 1;
294 if (stop == false)
295 message.request |= DP_AUX_I2C_MOT;
296 if (stop || start) {
297 message.buffer = NULL;
298 message.size = 0;
301 for (int attempt = 0; attempt < 7; attempt++) {
302 status_t result = dp_aux_transaction(connectorIndex, &message);
303 if (result != B_OK) {
304 ERROR("%s: aux_ch transaction failed!\n", __func__);
305 return result;
307 switch (message.reply & DP_AUX_I2C_REPLY_MASK) {
308 case DP_AUX_I2C_REPLY_ACK:
309 return B_OK;
310 case DP_AUX_I2C_REPLY_NACK:
311 ERROR("%s: aux i2c nack\n", __func__);
312 return B_IO_ERROR;
313 case DP_AUX_I2C_REPLY_DEFER:
314 TRACE("%s: aux i2c defer\n", __func__);
315 snooze(400);
316 break;
317 default:
318 ERROR("%s: aux invalid I2C reply: 0x%02x\n", __func__,
319 message.reply);
320 return B_IO_ERROR;
324 return B_ERROR;
328 uint32
329 dp_get_lane_count(uint32 connectorIndex, display_mode* mode)
331 // Radeon specific
332 dp_info* dpInfo = &gConnector[connectorIndex]->dpInfo;
334 size_t pixelChunk;
335 size_t pixelsPerChunk;
336 status_t result = dp_get_pixel_size_for((color_space)mode->space,
337 &pixelChunk, NULL, &pixelsPerChunk);
339 if (result != B_OK) {
340 TRACE("%s: Invalid color space!\n", __func__);
341 return 0;
344 uint32 bitsPerPixel = (pixelChunk / pixelsPerChunk) * 8;
346 uint32 dpMaxLinkRate = dp_get_link_rate_max(dpInfo);
347 uint32 dpMaxLaneCount = dp_get_lane_count_max(dpInfo);
349 uint32 lane;
350 // don't go below 2 lanes or display is jittery
351 for (lane = 2; lane < dpMaxLaneCount; lane <<= 1) {
352 uint32 maxPixelClock = dp_get_pixel_clock_max(dpMaxLinkRate, lane,
353 bitsPerPixel);
354 if (mode->timing.pixel_clock <= maxPixelClock)
355 break;
358 TRACE("%s: Lanes: %" B_PRIu32 "\n", __func__, lane);
359 return lane;
363 uint32
364 dp_get_link_rate(uint32 connectorIndex, display_mode* mode)
366 uint16 encoderID = gConnector[connectorIndex]->encoderExternal.objectID;
368 if (encoderID == ENCODER_OBJECT_ID_NUTMEG)
369 return 270000;
371 dp_info* dpInfo = &gConnector[connectorIndex]->dpInfo;
372 uint32 laneCount = dp_get_lane_count(connectorIndex, mode);
374 size_t pixelChunk;
375 size_t pixelsPerChunk;
376 status_t result = dp_get_pixel_size_for((color_space)mode->space,
377 &pixelChunk, NULL, &pixelsPerChunk);
379 if (result != B_OK) {
380 TRACE("%s: Invalid color space!\n", __func__);
381 return 0;
384 uint32 bitsPerPixel = (pixelChunk / pixelsPerChunk) * 8;
386 uint32 maxPixelClock
387 = dp_get_pixel_clock_max(162000, laneCount, bitsPerPixel);
388 if (mode->timing.pixel_clock <= maxPixelClock)
389 return 162000;
391 maxPixelClock = dp_get_pixel_clock_max(270000, laneCount, bitsPerPixel);
392 if (mode->timing.pixel_clock <= maxPixelClock)
393 return 270000;
395 // TODO: DisplayPort 1.2
396 #if 0
397 if (dp_is_dp12_capable(connectorIndex)) {
398 maxPixelClock = dp_get_pixel_clock_max(540000, laneCount, bitsPerPixel);
399 if (mode->timing.pixel_clock <= maxPixelClock)
400 return 540000;
402 #endif
404 return dp_get_link_rate_max(dpInfo);
408 void
409 dp_setup_connectors()
411 TRACE("%s\n", __func__);
413 for (uint32 index = 0; index < ATOM_MAX_SUPPORTED_DEVICE; index++) {
414 dp_info* dpInfo = &gConnector[index]->dpInfo;
415 dpInfo->valid = false;
416 if (gConnector[index]->valid == false
417 || connector_is_dp(index) == false) {
418 dpInfo->config[0] = 0;
419 continue;
422 TRACE("%s: found dp connector on index %" B_PRIu32 "\n",
423 __func__, index);
424 uint32 i2cPinIndex = gConnector[index]->i2cPinIndex;
426 uint32 auxPin = gGPIOInfo[i2cPinIndex]->hwPin;
427 dpInfo->auxPin = auxPin;
429 dp_aux_msg message;
430 memset(&message, 0, sizeof(message));
432 message.address = DP_DPCD_REV;
433 message.request = DP_AUX_NATIVE_READ;
434 // TODO: validate
435 message.size = DP_DPCD_SIZE;
436 message.buffer = dpInfo->config;
438 status_t result = dp_aux_transaction(index, &message);
440 if (result == B_OK) {
441 dpInfo->valid = true;
442 TRACE("%s: connector(%" B_PRIu32 "): successful read of DPCD\n",
443 __func__, index);
444 } else {
445 TRACE("%s: connector(%" B_PRIu32 "): failed read of DPCD\n",
446 __func__, index);
449 TRACE("%s: DPCD is ", __func__);
450 uint32 position;
451 for (position = 0; position < message.size; position++)
452 _sPrintf("%02x ", message.buffer + position);
453 _sPrintf("\n");
459 bool
460 dp_get_link_status(uint32 connectorIndex)
462 dp_info* dp = &gConnector[connectorIndex]->dpInfo;
464 dp_aux_msg message;
465 memset(&message, 0, sizeof(message));
467 message.request = DP_AUX_NATIVE_READ;
468 message.address = DP_LANE_STATUS_0_1;
469 message.size = DP_LINK_STATUS_SIZE;
470 message.buffer = dp->linkStatus;
472 // TODO: Delay 100? Newer AMD code doesn't care about link status
473 status_t result = dp_aux_transaction(connectorIndex, &message);
475 if (result != B_OK) {
476 ERROR("%s: DisplayPort link status failed\n", __func__);
477 return false;
480 return true;
484 static uint8
485 dp_get_lane_status(dp_info* dp, int lane)
487 int i = DP_LANE_STATUS_0_1 + (lane >> 1);
488 int s = (lane & 1) * 4;
489 uint8 l = dp->linkStatus[i - DP_LANE_STATUS_0_1];
490 return (l >> s) & 0xf;
494 static bool
495 dp_clock_recovery_ok(dp_info* dp)
497 int lane;
498 uint8 laneStatus;
500 for (lane = 0; lane < dp->laneCount; lane++) {
501 laneStatus = dp_get_lane_status(dp, lane);
502 if ((laneStatus & DP_LANE_STATUS_CR_DONE_A) == 0)
503 return false;
505 return true;
509 static bool
510 dp_clock_equalization_ok(dp_info* dp)
512 uint8 laneAlignment
513 = dp->linkStatus[DP_LANE_ALIGN - DP_LANE_STATUS_0_1];
515 if ((laneAlignment & DP_LANE_ALIGN_DONE) == 0)
516 return false;
518 int lane;
519 for (lane = 0; lane < dp->laneCount; lane++) {
520 uint8 laneStatus = dp_get_lane_status(dp, lane);
521 if ((laneStatus & DP_LANE_STATUS_EQUALIZED_A)
522 != DP_LANE_STATUS_EQUALIZED_A) {
523 return false;
526 return true;
530 static void
531 dp_update_vs_emph(uint32 connectorIndex)
533 dp_info* dp = &gConnector[connectorIndex]->dpInfo;
535 // Set initial vs and emph on source
536 transmitter_dig_setup(connectorIndex, dp->linkRate, 0,
537 dp->trainingSet[0], ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH);
539 dp_aux_msg message;
540 memset(&message, 0, sizeof(message));
542 message.request = DP_AUX_NATIVE_WRITE;
543 message.address = DP_TRAIN_LANE0;
544 message.buffer = dp->trainingSet;
545 message.size = dp->laneCount;
546 // TODO: Review laneCount as it sounds strange.
547 dp_aux_transaction(connectorIndex, &message);
551 static uint8
552 dp_get_adjust_request_voltage(dp_info* dp, int lane)
554 int i = DP_ADJ_REQUEST_0_1 + (lane >> 1);
555 int s = (((lane & 1) != 0) ? DP_ADJ_VCC_SWING_LANEB_SHIFT
556 : DP_ADJ_VCC_SWING_LANEA_SHIFT);
557 uint8 l = dp->linkStatus[i - DP_LANE_STATUS_0_1];
559 return ((l >> s) & 0x3) << DP_TRAIN_VCC_SWING_SHIFT;
563 static uint8
564 dp_get_adjust_request_pre_emphasis(dp_info* dp, int lane)
566 int i = DP_ADJ_REQUEST_0_1 + (lane >> 1);
567 int s = (((lane & 1) != 0) ? DP_ADJ_PRE_EMPHASIS_LANEB_SHIFT
568 : DP_ADJ_PRE_EMPHASIS_LANEA_SHIFT);
569 uint8 l = dp->linkStatus[i - DP_LANE_STATUS_0_1];
571 return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT;
575 static void
576 dp_get_adjust_train(dp_info* dp)
578 TRACE("%s\n", __func__);
580 const char* voltageNames[] = {
581 "0.4V", "0.6V", "0.8V", "1.2V"
583 const char* preEmphasisNames[] = {
584 "0dB", "3.5dB", "6dB", "9.5dB"
587 uint8 voltage = 0;
588 uint8 preEmphasis = 0;
589 int lane;
591 for (lane = 0; lane < dp->laneCount; lane++) {
592 uint8 laneVoltage = dp_get_adjust_request_voltage(dp, lane);
593 uint8 lanePreEmphasis = dp_get_adjust_request_pre_emphasis(dp, lane);
595 TRACE("%s: Requested %s at %s for lane %d\n", __func__,
596 preEmphasisNames[lanePreEmphasis >> DP_TRAIN_PRE_EMPHASIS_SHIFT],
597 voltageNames[laneVoltage >> DP_TRAIN_VCC_SWING_SHIFT],
598 lane);
600 if (laneVoltage > voltage)
601 voltage = laneVoltage;
602 if (lanePreEmphasis > preEmphasis)
603 preEmphasis = lanePreEmphasis;
606 // Check for maximum voltage and toggle max if reached
607 if (voltage >= DP_TRAIN_VCC_SWING_1200)
608 voltage |= DP_TRAIN_MAX_SWING_EN;
610 // Check for maximum pre-emphasis and toggle max if reached
611 if (preEmphasis >= DP_TRAIN_PRE_EMPHASIS_9_5)
612 preEmphasis |= DP_TRAIN_MAX_EMPHASIS_EN;
614 for (lane = 0; lane < 4; lane++)
615 dp->trainingSet[lane] = voltage | preEmphasis;
619 static uint8
620 dp_encoder_service(uint32 connectorIndex, int action, int linkRate,
621 uint8 lane)
623 DP_ENCODER_SERVICE_PARAMETERS args;
624 int index = GetIndexIntoMasterTable(COMMAND, DPEncoderService);
626 memset(&args, 0, sizeof(args));
627 args.ucLinkClock = linkRate;
628 args.ucAction = action;
629 args.ucLaneNum = lane;
630 args.ucConfig = 0;
631 args.ucStatus = 0;
633 // We really can't do ATOM_DP_ACTION_GET_SINK_TYPE with the
634 // way I designed this below. Not used though.
636 // Calculate encoder_id config
637 if (encoder_pick_dig(connectorIndex))
638 args.ucConfig |= ATOM_DP_CONFIG_DIG2_ENCODER;
639 else
640 args.ucConfig |= ATOM_DP_CONFIG_DIG1_ENCODER;
642 if (gConnector[connectorIndex]->encoder.linkEnumeration
643 == GRAPH_OBJECT_ENUM_ID2) {
644 args.ucConfig |= ATOM_DP_CONFIG_LINK_B;
645 } else
646 args.ucConfig |= ATOM_DP_CONFIG_LINK_A;
648 atom_execute_table(gAtomContext, index, (uint32*)&args);
650 return args.ucStatus;
654 static void
655 dp_set_tp(uint32 connectorIndex, int trainingPattern)
657 TRACE("%s\n", __func__);
659 radeon_shared_info &info = *gInfo->shared_info;
660 dp_info* dp = &gConnector[connectorIndex]->dpInfo;
661 pll_info* pll = &gConnector[connectorIndex]->encoder.pll;
663 int rawTrainingPattern = 0;
665 /* set training pattern on the source */
666 if (info.dceMajor >= 4 || !dp->trainingUseEncoder) {
667 TRACE("%s: Training with encoder...\n", __func__);
668 switch (trainingPattern) {
669 case DP_TRAIN_PATTERN_1:
670 rawTrainingPattern = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN1;
671 break;
672 case DP_TRAIN_PATTERN_2:
673 rawTrainingPattern = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN2;
674 break;
675 case DP_TRAIN_PATTERN_3:
676 rawTrainingPattern = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN3;
677 break;
679 encoder_dig_setup(connectorIndex, pll->pixelClock, rawTrainingPattern);
680 } else {
681 TRACE("%s: Training with encoder service...\n", __func__);
682 switch (trainingPattern) {
683 case DP_TRAIN_PATTERN_1:
684 rawTrainingPattern = 0;
685 break;
686 case DP_TRAIN_PATTERN_2:
687 rawTrainingPattern = 1;
688 break;
690 dp_encoder_service(connectorIndex, ATOM_DP_ACTION_TRAINING_PATTERN_SEL,
691 dp->linkRate, rawTrainingPattern);
694 // Enable training pattern on the sink
695 dpcd_reg_write(connectorIndex, DP_TRAIN, trainingPattern);
699 status_t
700 dp_link_train_cr(uint32 connectorIndex)
702 TRACE("%s\n", __func__);
704 dp_info* dp = &gConnector[connectorIndex]->dpInfo;
706 // Display Port Clock Recovery Training
708 bool clockRecovery = false;
709 uint8 voltage = 0xff;
710 int lane;
712 dp_set_tp(connectorIndex, DP_TRAIN_PATTERN_1);
713 memset(dp->trainingSet, 0, 4);
714 dp_update_vs_emph(connectorIndex);
715 snooze(400);
717 while (1) {
718 if (dp->trainingReadInterval == 0)
719 snooze(100);
720 else
721 snooze(1000 * 4 * dp->trainingReadInterval);
723 if (!dp_get_link_status(connectorIndex))
724 break;
726 if (dp_clock_recovery_ok(dp)) {
727 clockRecovery = true;
728 break;
731 for (lane = 0; lane < dp->laneCount; lane++) {
732 if ((dp->trainingSet[lane] & DP_TRAIN_MAX_SWING_EN) == 0)
733 break;
736 if (lane == dp->laneCount) {
737 ERROR("%s: clock recovery reached max voltage\n", __func__);
738 break;
741 if ((dp->trainingSet[0] & DP_TRAIN_VCC_SWING_MASK) == voltage) {
742 dp->trainingAttempts++;
743 if (dp->trainingAttempts >= 5) {
744 ERROR("%s: clock recovery tried 5 times\n", __func__);
745 break;
747 } else
748 dp->trainingAttempts = 0;
750 voltage = dp->trainingSet[0] & DP_TRAIN_VCC_SWING_MASK;
752 // Compute new trainingSet as requested by sink
753 dp_get_adjust_train(dp);
755 dp_update_vs_emph(connectorIndex);
758 if (!clockRecovery) {
759 ERROR("%s: clock recovery failed\n", __func__);
760 return B_ERROR;
763 TRACE("%s: clock recovery at voltage %d pre-emphasis %d\n",
764 __func__, dp->trainingSet[0] & DP_TRAIN_VCC_SWING_MASK,
765 (dp->trainingSet[0] & DP_TRAIN_PRE_EMPHASIS_MASK)
766 >> DP_TRAIN_PRE_EMPHASIS_SHIFT);
767 return B_OK;
771 status_t
772 dp_link_train_ce(uint32 connectorIndex, bool tp3Support)
774 TRACE("%s\n", __func__);
776 dp_info* dp = &gConnector[connectorIndex]->dpInfo;
778 if (tp3Support)
779 dp_set_tp(connectorIndex, DP_TRAIN_PATTERN_3);
780 else
781 dp_set_tp(connectorIndex, DP_TRAIN_PATTERN_2);
783 dp->trainingAttempts = 0;
784 bool channelEqual = false;
786 while (1) {
787 if (dp->trainingReadInterval == 0)
788 snooze(400);
789 else
790 snooze(1000 * 4 * dp->trainingReadInterval);
792 if (!dp_get_link_status(connectorIndex)) {
793 ERROR("%s: ERROR: Unable to get link status!\n", __func__);
794 break;
797 if (dp_clock_equalization_ok(dp)) {
798 channelEqual = true;
799 break;
802 if (dp->trainingAttempts > 5) {
803 ERROR("%s: ERROR: failed > 5 times!\n", __func__);
804 break;
807 dp_get_adjust_train(dp);
809 dp_update_vs_emph(connectorIndex);
810 dp->trainingAttempts++;
813 if (!channelEqual) {
814 ERROR("%s: ERROR: failed\n", __func__);
815 return B_ERROR;
818 TRACE("%s: channels equalized at voltage %d pre-emphasis %d\n",
819 __func__, dp->trainingSet[0] & DP_ADJ_VCC_SWING_LANEA_MASK,
820 (dp->trainingSet[0] & DP_TRAIN_PRE_EMPHASIS_MASK)
821 >> DP_TRAIN_PRE_EMPHASIS_SHIFT);
823 return B_OK;
827 status_t
828 dp_link_train(uint8 crtcID)
830 TRACE("%s\n", __func__);
832 uint32 connectorIndex = gDisplay[crtcID]->connectorIndex;
833 dp_info* dp = &gConnector[connectorIndex]->dpInfo;
834 display_mode* mode = &gDisplay[crtcID]->currentMode;
836 if (dp->valid != true) {
837 ERROR("%s: started on invalid DisplayPort connector #%" B_PRIu32 "\n",
838 __func__, connectorIndex);
839 return B_ERROR;
842 int index = GetIndexIntoMasterTable(COMMAND, DPEncoderService);
843 // Table version
844 uint8 tableMajor;
845 uint8 tableMinor;
847 dp->trainingUseEncoder = true;
848 if (atom_parse_cmd_header(gAtomContext, index, &tableMajor, &tableMinor)
849 == B_OK) {
850 if (tableMinor > 1) {
851 // The AtomBIOS DPEncoderService greater then 1.1 can't program the
852 // training pattern properly.
853 dp->trainingUseEncoder = false;
857 uint32 linkEnumeration
858 = gConnector[connectorIndex]->encoder.linkEnumeration;
860 uint32 dpEncoderID = 0;
861 if (encoder_pick_dig(connectorIndex) > 0)
862 dpEncoderID |= ATOM_DP_CONFIG_DIG2_ENCODER;
863 else
864 dpEncoderID |= ATOM_DP_CONFIG_DIG1_ENCODER;
865 if (linkEnumeration == GRAPH_OBJECT_ENUM_ID2)
866 dpEncoderID |= ATOM_DP_CONFIG_LINK_B;
867 else
868 dpEncoderID |= ATOM_DP_CONFIG_LINK_A;
870 dp->trainingReadInterval
871 = dpcd_reg_read(connectorIndex, DP_TRAINING_AUX_RD_INTERVAL);
873 uint8 sandbox = dpcd_reg_read(connectorIndex, DP_MAX_LANE_COUNT);
875 radeon_shared_info &info = *gInfo->shared_info;
876 bool dpTPS3Supported = false;
877 if (info.dceMajor >= 5 && (sandbox & DP_TPS3_SUPPORTED) != 0)
878 dpTPS3Supported = true;
880 // *** DisplayPort link training initialization
882 // Power up the DP sink
883 if (dp->config[0] >= DP_DPCD_REV_11)
884 dpcd_reg_write(connectorIndex, DP_SET_POWER, DP_SET_POWER_D0);
886 // Possibly enable downspread on the sink
887 if ((dp->config[3] & 0x1) != 0) {
888 dpcd_reg_write(connectorIndex, DP_DOWNSPREAD_CTRL,
889 DP_DOWNSPREAD_CTRL_AMP_EN);
890 } else
891 dpcd_reg_write(connectorIndex, DP_DOWNSPREAD_CTRL, 0);
893 encoder_dig_setup(connectorIndex, mode->timing.pixel_clock,
894 ATOM_ENCODER_CMD_SETUP_PANEL_MODE);
896 // TODO: Doesn't this overwrite important dpcd info?
897 sandbox = dp->laneCount;
898 if ((dp->config[0] >= DP_DPCD_REV_11)
899 && (dp->config[2] & DP_ENHANCED_FRAME_CAP_EN))
900 sandbox |= DP_ENHANCED_FRAME_EN;
901 dpcd_reg_write(connectorIndex, DP_LANE_COUNT, sandbox);
903 // Set the link rate on the DP sink
904 sandbox = dp_encode_link_rate(dp->linkRate);
905 dpcd_reg_write(connectorIndex, DP_LINK_RATE, sandbox);
907 // Start link training on source
908 if (info.dceMajor >= 4 || !dp->trainingUseEncoder) {
909 encoder_dig_setup(connectorIndex, mode->timing.pixel_clock,
910 ATOM_ENCODER_CMD_DP_LINK_TRAINING_START);
911 } else {
912 dp_encoder_service(connectorIndex, ATOM_DP_ACTION_TRAINING_START,
913 dp->linkRate, 0);
916 // Disable the training pattern on the sink
917 dpcd_reg_write(connectorIndex, DP_TRAIN, DP_TRAIN_PATTERN_DISABLED);
919 dp_link_train_cr(connectorIndex);
920 dp_link_train_ce(connectorIndex, dpTPS3Supported);
922 // *** DisplayPort link training finish
923 snooze(400);
925 // Disable the training pattern on the sink
926 dpcd_reg_write(connectorIndex, DP_TRAIN, DP_TRAIN_PATTERN_DISABLED);
928 // Disable the training pattern on the source
929 if (info.dceMajor >= 4 || !dp->trainingUseEncoder) {
930 encoder_dig_setup(connectorIndex, mode->timing.pixel_clock,
931 ATOM_ENCODER_CMD_DP_LINK_TRAINING_COMPLETE);
932 } else {
933 dp_encoder_service(connectorIndex, ATOM_DP_ACTION_TRAINING_COMPLETE,
934 dp->linkRate, 0);
937 return B_OK;
941 bool
942 ddc2_dp_read_edid1(uint32 connectorIndex, edid1_info* edid)
944 TRACE("%s\n", __func__);
946 dp_info* dpInfo = &gConnector[connectorIndex]->dpInfo;
948 if (!dpInfo->valid) {
949 ERROR("%s: connector(%" B_PRIu32 ") missing valid DisplayPort data!\n",
950 __func__, connectorIndex);
951 return false;
954 edid1_raw raw;
955 uint8* rdata = (uint8*)&raw;
956 uint8 sdata = 0;
958 // The following sequence is from a trace of the Linux kernel
959 // radeon code; not sure if the initial writes to address 0 are
960 // requried.
961 // TODO: This surely cane be cleaned up
962 dp_aux_set_i2c_byte(connectorIndex, 0x00, &sdata, true, false);
963 dp_aux_set_i2c_byte(connectorIndex, 0x00, &sdata, false, true);
965 dp_aux_set_i2c_byte(connectorIndex, 0x50, &sdata, true, false);
966 dp_aux_set_i2c_byte(connectorIndex, 0x50, &sdata, false, false);
967 dp_aux_get_i2c_byte(connectorIndex, 0x50, rdata, true, false);
968 dp_aux_get_i2c_byte(connectorIndex, 0x50, rdata, false, false);
969 dp_aux_get_i2c_byte(connectorIndex, 0x50, rdata, false, true);
971 dp_aux_set_i2c_byte(connectorIndex, 0x50, &sdata, true, false);
972 dp_aux_set_i2c_byte(connectorIndex, 0x50, &sdata, false, false);
973 dp_aux_get_i2c_byte(connectorIndex, 0x50, rdata, true, false);
975 for (uint32 i = 0; i < sizeof(raw); i++) {
976 status_t result = dp_aux_get_i2c_byte(connectorIndex, 0x50,
977 rdata++, false, false);
978 if (result != B_OK) {
979 TRACE("%s: error reading EDID data at index %" B_PRIu32 ", "
980 "result = 0x%" B_PRIx32 "\n", __func__, i, result);
981 dp_aux_get_i2c_byte(connectorIndex, 0x50, &sdata, false, true);
982 return false;
985 dp_aux_get_i2c_byte(connectorIndex, 0x50, &sdata, false, true);
987 if (raw.version.version != 1 || raw.version.revision > 4) {
988 ERROR("%s: EDID version or revision out of range\n", __func__);
989 return false;
992 edid_decode(edid, &raw);
994 return true;
998 status_t
999 dp_get_pixel_size_for(color_space space, size_t *pixelChunk,
1000 size_t *rowAlignment, size_t *pixelsPerChunk)
1002 status_t result = get_pixel_size_for(space, pixelChunk, NULL,
1003 pixelsPerChunk);
1005 if ((space == B_RGB32) || (space == B_RGBA32) || (space == B_RGB32_BIG)
1006 || (space == B_RGBA32_BIG)) {
1007 *pixelChunk = 3;
1010 return result;
1014 bool
1015 dp_is_dp12_capable(uint32 connectorIndex)
1017 TRACE("%s\n", __func__);
1018 radeon_shared_info &info = *gInfo->shared_info;
1020 uint32 capabilities = gConnector[connectorIndex]->encoder.capabilities;
1022 if (info.dceMajor >= 5
1023 && gInfo->dpExternalClock >= 539000
1024 && (capabilities & ATOM_ENCODER_CAP_RECORD_HBR2) != 0) {
1025 return true;
1028 return false;
1032 void
1033 debug_dp_info()
1035 ERROR("Current DisplayPort Info =================\n");
1036 for (uint32 id = 0; id < ATOM_MAX_SUPPORTED_DEVICE; id++) {
1037 if (gConnector[id]->valid == true) {
1038 dp_info* dp = &gConnector[id]->dpInfo;
1039 ERROR("Connector #%" B_PRIu32 ") DP: %s\n", id,
1040 dp->valid ? "true" : "false");
1042 if (!dp->valid)
1043 continue;
1044 ERROR(" + DP Config Data\n");
1045 ERROR(" - max lane count: %d\n",
1046 dp->config[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK);
1047 ERROR(" - max link rate: %d\n",
1048 dp->config[DP_MAX_LINK_RATE]);
1049 ERROR(" - receiver port count: %d\n",
1050 dp->config[DP_NORP] & DP_NORP_MASK);
1051 ERROR(" - downstream port present: %s\n",
1052 (dp->config[DP_DOWNSTREAMPORT] & DP_DOWNSTREAMPORT_EN)
1053 ? "yes" : "no");
1054 ERROR(" - downstream port count: %d\n",
1055 dp->config[DP_DOWNSTREAMPORT_COUNT]
1056 & DP_DOWNSTREAMPORT_COUNT_MASK);
1057 ERROR(" + Training\n");
1058 ERROR(" - use encoder: %s\n",
1059 dp->trainingUseEncoder ? "true" : "false");
1060 ERROR(" - attempts: %" B_PRIu8 "\n",
1061 dp->trainingAttempts);
1062 ERROR(" - delay: %d\n",
1063 dp->trainingReadInterval);
1064 ERROR(" + Data\n");
1065 ERROR(" - auxPin: 0x%" B_PRIX32"\n", dp->auxPin);
1066 ERROR(" + Video\n");
1067 ERROR(" - laneCount: %d\n", dp->laneCount);
1068 ERROR(" - linkRate: %" B_PRIu32 "\n",
1069 dp->linkRate);
1072 ERROR("==========================================\n");