xref: /haiku/src/add-ons/accelerants/intel_extreme/FlexibleDisplayInterface.cpp (revision 6f80a9801fedbe7355c4360bd204ba746ec3ec2d)
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 	fPipeIndex(pipeIndex)
48 {
49 }
50 
51 
52 FDITransmitter::~FDITransmitter()
53 {
54 }
55 
56 
57 void
58 FDITransmitter::Enable()
59 {
60 	CALLED();
61 	uint32 targetRegister = FDI_TX_CTL(fPipeIndex);
62 	uint32 value = read32(targetRegister);
63 
64 	write32(targetRegister, value | FDI_TX_ENABLE);
65 	read32(targetRegister);
66 	spin(150);
67 }
68 
69 
70 void
71 FDITransmitter::Disable()
72 {
73 	CALLED();
74 	uint32 targetRegister = FDI_TX_CTL(fPipeIndex);
75 	uint32 value = read32(targetRegister);
76 
77 	write32(targetRegister, value & ~FDI_TX_ENABLE);
78 	read32(targetRegister);
79 	spin(150);
80 }
81 
82 
83 bool
84 FDITransmitter::IsPLLEnabled()
85 {
86 	CALLED();
87 	return (read32(FDI_TX_CTL(fPipeIndex)) & FDI_TX_PLL_ENABLED) != 0;
88 }
89 
90 
91 void
92 FDITransmitter::EnablePLL(uint32 lanes)
93 {
94 	CALLED();
95 	uint32 targetRegister = FDI_TX_CTL(fPipeIndex);
96 	uint32 value = read32(targetRegister);
97 	if ((value & FDI_TX_PLL_ENABLED) != 0) {
98 		// already enabled, possibly IronLake where it always is
99 		TRACE("%s: Already enabled.\n", __func__);
100 		return;
101 	}
102 
103 	value &= ~FDI_DP_PORT_WIDTH_MASK;
104 	value |= FDI_DP_PORT_WIDTH(lanes);
105 
106 	//first update config, -then- enable PLL to be sure config is indeed updated
107 	write32(targetRegister, value);
108 	read32(targetRegister);
109 
110 	write32(targetRegister, value | FDI_TX_PLL_ENABLED);
111 	read32(targetRegister);
112 	spin(100); // warmup 10us + dmi delay 20us, be generous
113 }
114 
115 
116 void
117 FDITransmitter::DisablePLL()
118 {
119 	CALLED();
120 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_ILK)) {
121 		// on IronLake the FDI PLL is always enabled, so no point in trying...
122 		return;
123 	}
124 
125 	uint32 targetRegister = FDI_TX_CTL(fPipeIndex);
126 	write32(targetRegister, read32(targetRegister) & ~FDI_TX_PLL_ENABLED);
127 	read32(targetRegister);
128 	spin(100);
129 }
130 
131 
132 // #pragma mark - FDIReceiver
133 
134 
135 FDIReceiver::FDIReceiver(pipe_index pipeIndex)
136 	:
137 	fPipeIndex(pipeIndex)
138 {
139 }
140 
141 
142 FDIReceiver::~FDIReceiver()
143 {
144 }
145 
146 
147 void
148 FDIReceiver::Enable()
149 {
150 	CALLED();
151 	uint32 targetRegister = FDI_RX_CTL(fPipeIndex);
152 	uint32 value = read32(targetRegister);
153 
154 	write32(targetRegister, value | FDI_RX_ENABLE);
155 	read32(targetRegister);
156 	spin(150);
157 }
158 
159 
160 void
161 FDIReceiver::Disable()
162 {
163 	CALLED();
164 	uint32 targetRegister = FDI_RX_CTL(fPipeIndex);
165 	uint32 value = read32(targetRegister);
166 
167 	write32(targetRegister, value & ~FDI_RX_ENABLE);
168 	read32(targetRegister);
169 	spin(150);
170 }
171 
172 
173 bool
174 FDIReceiver::IsPLLEnabled()
175 {
176 	CALLED();
177 	return (read32(FDI_RX_CTL(fPipeIndex)) & FDI_RX_PLL_ENABLED) != 0;
178 }
179 
180 
181 void
182 FDIReceiver::EnablePLL(uint32 lanes)
183 {
184 	CALLED();
185 	uint32 targetRegister = FDI_RX_CTL(fPipeIndex);
186 	uint32 value = read32(targetRegister);
187 	if ((value & FDI_RX_PLL_ENABLED) != 0) {
188 		// already enabled, possibly IronLake where it always is
189 		TRACE("%s: Already enabled.\n", __func__);
190 		return;
191 	}
192 
193 	//Link bit depth: this should be globally known per FDI link (i.e. laptop panel 3x6, rest 3x8)
194 	//currently using BIOS preconfigured setup
195 	//value &= ~FDI_DP_PORT_WIDTH_MASK;
196 	//value |= FDI_RX_LINK_BPC(INTEL_PIPE_8BPC);
197 
198 	value &= ~FDI_DP_PORT_WIDTH_MASK;
199 	value |= FDI_DP_PORT_WIDTH(lanes);
200 
201 	//first update config, -then- enable PLL to be sure config is indeed updated
202 	write32(targetRegister, value);
203 	read32(targetRegister);
204 
205 	write32(targetRegister, value | FDI_RX_PLL_ENABLED);
206 	read32(targetRegister);
207 	spin(200); // warmup 10us + dmi delay 20us, be generous
208 }
209 
210 
211 void
212 FDIReceiver::DisablePLL()
213 {
214 	CALLED();
215 	uint32 targetRegister = FDI_RX_CTL(fPipeIndex);
216 	write32(targetRegister, read32(targetRegister) & ~FDI_RX_PLL_ENABLED);
217 	read32(targetRegister);
218 	spin(100);
219 }
220 
221 
222 void
223 FDIReceiver::SwitchClock(bool toPCDClock)
224 {
225 	CALLED();
226 	uint32 targetRegister = FDI_RX_CTL(fPipeIndex);
227 	write32(targetRegister, (read32(targetRegister) & ~FDI_RX_CLOCK_MASK)
228 		| (toPCDClock ? FDI_RX_CLOCK_PCD : FDI_RX_CLOCK_RAW));
229 	read32(targetRegister);
230 	spin(200);
231 }
232 
233 
234 // #pragma mark - FDILink
235 
236 
237 FDILink::FDILink(pipe_index pipeIndex)
238 	:
239 	fTransmitter(pipeIndex),
240 	fReceiver(pipeIndex),
241 	fPipeIndex(pipeIndex)
242 {
243 }
244 
245 
246 status_t
247 FDILink::PreTrain(display_timing* target, uint32* linkBandwidth, uint32* lanes, uint32* bitsPerPixel)
248 {
249 	CALLED();
250 
251 	uint32 txControl = FDI_TX_CTL(fPipeIndex);
252 	uint32 rxControl = FDI_RX_CTL(fPipeIndex);
253 
254 	//Link bit depth: this should be globally known per FDI link (i.e. laptop panel 3x6, rest 3x8)
255 	*bitsPerPixel = ((read32(rxControl) & FDI_RX_LINK_BPC_MASK) >> FDI_RX_LINK_COLOR_SHIFT);
256 	switch (*bitsPerPixel) {
257 		case INTEL_PIPE_8BPC:
258 			*bitsPerPixel = 24;
259 			break;
260 		case INTEL_PIPE_10BPC:
261 			*bitsPerPixel = 30;
262 			break;
263 		case INTEL_PIPE_6BPC:
264 			*bitsPerPixel = 18;
265 			break;
266 		case INTEL_PIPE_12BPC:
267 			*bitsPerPixel = 36;
268 			break;
269 		default:
270 			*bitsPerPixel = 0;
271 			ERROR("%s: FDI illegal link colordepth set.\n", __func__);
272 			return B_ERROR;
273 	}
274 	TRACE("%s: FDI Link %s:\n", __func__, (fPipeIndex == INTEL_PIPE_A) ? "A" : "B");
275 	TRACE("%s: FDI Link Colordepth: %" B_PRIu32 "\n", __func__, *bitsPerPixel);
276 
277 	// Khz / 10. ( each output octet encoded as 10 bits.
278 	*linkBandwidth = gInfo->shared_info->fdi_link_frequency * 1000 / 10;
279 	//Reserving 5% bandwidth for possible spread spectrum clock use
280 	uint32 bps = target->pixel_clock * *bitsPerPixel * 21 / 20;
281 
282 	//use DIV_ROUND_UP:
283 	*lanes = (bps + (*linkBandwidth * 8) - 1) / (*linkBandwidth * 8);
284 	//remove below line when link training is to be done
285 	*lanes = ((read32(txControl) & FDI_DP_PORT_WIDTH_MASK) >> FDI_DP_PORT_WIDTH_SHIFT) + 1;
286 
287 	TRACE("%s: FDI Link Lanes: %" B_PRIu32 "\n", __func__, *lanes);
288 	//assuming we'll only use link A and B (not C)
289 	if (*lanes > 4) {
290 		ERROR("%s: FDI not enough lanes in hardware.\n", __func__);
291 		return B_ERROR;
292 	}
293 
294 	TRACE("%s: FDI TX ctrl before: 0x%" B_PRIx32 "\n", __func__, read32(txControl));
295 	TRACE("%s: FDI RX ctrl before: 0x%" B_PRIx32 "\n", __func__, read32(rxControl));
296 
297 #if 0
298 	//when link training is to be done re-enable this code
299 
300 	//The order of handling things is important here..
301 	write32(txControl, read32(txControl) & ~FDI_TX_ENABLE);
302 	read32(txControl);
303 	write32(rxControl, read32(rxControl) & ~FDI_RX_ENABLE);
304 	read32(rxControl);
305 
306 	write32(txControl, (read32(txControl) & ~FDI_LINK_TRAIN_NONE) | FDI_LINK_TRAIN_PATTERN_1);
307 	read32(txControl);
308 	if (gInfo->shared_info->pch_info == INTEL_PCH_CPT) {
309 		write32(rxControl, (read32(rxControl) & ~FDI_LINK_TRAIN_PATTERN_MASK_CPT) | FDI_LINK_TRAIN_PATTERN_1_CPT);
310 	} else {
311 		write32(rxControl, (read32(rxControl) & ~FDI_LINK_TRAIN_NONE) | FDI_LINK_TRAIN_PATTERN_1);
312 	}
313 	read32(rxControl);
314 	spin(100);
315 
316 	// Disable FDI clocks
317 	Receiver().SwitchClock(false);
318 	Transmitter().DisablePLL();
319 	Receiver().DisablePLL();
320 #endif
321 
322 	return B_OK;
323 }
324 
325 
326 status_t
327 FDILink::Train(display_timing* target, uint32 lanes)
328 {
329 	CALLED();
330 
331 	status_t result = B_OK;
332 
333 	uint32 txControl = FDI_TX_CTL(fPipeIndex);
334 	uint32 rxControl = FDI_RX_CTL(fPipeIndex);
335 
336 	//Set receiving end TU size bits to match sending end's setting
337 	write32(FDI_RX_TUSIZE1(fPipeIndex), FDI_RX_TRANS_UNIT_MASK);
338 	write32(FDI_RX_TUSIZE2(fPipeIndex), FDI_RX_TRANS_UNIT_MASK);
339 
340 #if 0
341 	//when link training is to be done re-enable this code
342 	// Enable FDI clocks
343 	Receiver().EnablePLL(lanes);
344 	Receiver().SwitchClock(true);
345 	Transmitter().EnablePLL(lanes);
346 
347 	// TODO: Only _AutoTrain on IVYB Stepping B or later
348 	// otherwise, _ManualTrain
349 	if (gInfo->shared_info->device_type.Generation() >= 7)
350 		result = _AutoTrain(lanes);
351 	else if (gInfo->shared_info->device_type.Generation() == 6)
352 		result = _SnbTrain(lanes);
353 	else if (gInfo->shared_info->device_type.Generation() == 5)
354 		result = _IlkTrain(lanes);
355 	else
356 		result = _NormalTrain(lanes);
357 #endif
358 
359 	TRACE("%s: FDI TX ctrl after: 0x%" B_PRIx32 "\n", __func__, read32(txControl));
360 	TRACE("%s: FDI RX ctrl after: 0x%" B_PRIx32 "\n", __func__, read32(rxControl));
361 
362 	if (result != B_OK)
363 		ERROR("%s: FDI training fault.\n", __func__);
364 
365 	return result;
366 }
367 
368 
369 status_t
370 FDILink::_NormalTrain(uint32 lanes)
371 {
372 	CALLED();
373 	uint32 txControl = FDI_TX_CTL(fPipeIndex);
374 	uint32 rxControl = FDI_RX_CTL(fPipeIndex);
375 
376 	// Enable normal link training
377 	uint32 tmp = read32(txControl);
378 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IVB)) {
379 		tmp &= ~FDI_LINK_TRAIN_NONE_IVB;
380 		tmp |= FDI_LINK_TRAIN_NONE_IVB | FDI_TX_ENHANCE_FRAME_ENABLE;
381 	} else {
382 		tmp &= ~FDI_LINK_TRAIN_NONE;
383 		tmp |= FDI_LINK_TRAIN_NONE | FDI_TX_ENHANCE_FRAME_ENABLE;
384 	}
385 	write32(txControl, tmp);
386 
387 	tmp = read32(rxControl);
388 	if (gInfo->shared_info->pch_info == INTEL_PCH_CPT) {
389 		tmp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
390 		tmp |= FDI_LINK_TRAIN_NORMAL_CPT;
391 	} else {
392 		tmp &= ~FDI_LINK_TRAIN_NONE;
393 		tmp |= FDI_LINK_TRAIN_NONE;
394 	}
395 	write32(rxControl, tmp | FDI_RX_ENHANCE_FRAME_ENABLE);
396 
397 	// Wait 1x idle pattern
398 	read32(rxControl);
399 	spin(1000);
400 
401 	// Enable ecc on IVB
402 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IVB)) {
403 		write32(rxControl, read32(rxControl)
404 			| FDI_FS_ERRC_ENABLE | FDI_FE_ERRC_ENABLE);
405 		read32(rxControl);
406 	}
407 
408 	return B_OK;
409 }
410 
411 
412 status_t
413 FDILink::_IlkTrain(uint32 lanes)
414 {
415 	CALLED();
416 	uint32 txControl = FDI_TX_CTL(fPipeIndex);
417 	uint32 rxControl = FDI_RX_CTL(fPipeIndex);
418 
419 	// Train 1: unmask FDI RX Interrupt symbol_lock and bit_lock
420 	uint32 tmp = read32(FDI_RX_IMR(fPipeIndex));
421 	tmp &= ~FDI_RX_SYMBOL_LOCK;
422 	tmp &= ~FDI_RX_BIT_LOCK;
423 	write32(FDI_RX_IMR(fPipeIndex), tmp);
424 	spin(150);
425 
426 	// Enable CPU FDI TX and RX
427 	tmp = read32(txControl);
428 	tmp &= ~FDI_DP_PORT_WIDTH_MASK;
429 	tmp |= FDI_DP_PORT_WIDTH(lanes);
430 	tmp &= ~FDI_LINK_TRAIN_NONE;
431 	tmp |= FDI_LINK_TRAIN_PATTERN_1;
432 	write32(txControl, tmp);
433 	Transmitter().Enable();
434 
435 	tmp = read32(rxControl);
436 	tmp &= ~FDI_LINK_TRAIN_NONE;
437 	tmp |= FDI_LINK_TRAIN_PATTERN_1;
438 	write32(rxControl, tmp);
439 	Receiver().Enable();
440 
441 	// ILK Workaround, enable clk after FDI enable
442 	if (fPipeIndex == INTEL_PIPE_B) {
443 		write32(PCH_FDI_RXB_CHICKEN, FDI_RX_PHASE_SYNC_POINTER_OVR);
444 		write32(PCH_FDI_RXB_CHICKEN, FDI_RX_PHASE_SYNC_POINTER_OVR
445 			| FDI_RX_PHASE_SYNC_POINTER_EN);
446 	} else {
447 		write32(PCH_FDI_RXA_CHICKEN, FDI_RX_PHASE_SYNC_POINTER_OVR);
448 		write32(PCH_FDI_RXA_CHICKEN, FDI_RX_PHASE_SYNC_POINTER_OVR
449 			| FDI_RX_PHASE_SYNC_POINTER_EN);
450 	}
451 
452 	uint32 iirControl = FDI_RX_IIR(fPipeIndex);
453 	TRACE("%s: FDI RX IIR Control @ 0x%" B_PRIx32 "\n", __func__, iirControl);
454 
455 	int tries = 0;
456 	for (tries = 0; tries < 5; tries++) {
457 		tmp = read32(iirControl);
458 		TRACE("%s: FDI RX IIR 0x%" B_PRIx32 "\n", __func__, tmp);
459 
460 		if ((tmp & FDI_RX_BIT_LOCK)) {
461 			TRACE("%s: FDI train 1 done\n", __func__);
462 			write32(iirControl, tmp | FDI_RX_BIT_LOCK);
463 			break;
464 		}
465 	}
466 
467 	if (tries == 5) {
468 		ERROR("%s: FDI train 1 failure!\n", __func__);
469 		return B_ERROR;
470 	}
471 
472 	// Train 2
473 	tmp = read32(txControl);
474 	tmp &= ~FDI_LINK_TRAIN_NONE;
475 	tmp |= FDI_LINK_TRAIN_PATTERN_2;
476 	write32(txControl, tmp);
477 
478 	tmp = read32(rxControl);
479 	tmp &= ~FDI_LINK_TRAIN_NONE;
480 	tmp |= FDI_LINK_TRAIN_PATTERN_2;
481 	write32(rxControl, tmp);
482 
483 	read32(rxControl);
484 	spin(150);
485 
486 	for (tries = 0; tries < 5; tries++) {
487 		tmp = read32(iirControl);
488 		TRACE("%s: FDI RX IIR 0x%" B_PRIx32 "\n", __func__, tmp);
489 
490 		if (tmp & FDI_RX_SYMBOL_LOCK) {
491 			TRACE("%s: FDI train 2 done\n", __func__);
492 			write32(iirControl, tmp | FDI_RX_SYMBOL_LOCK);
493 			break;
494 		}
495 	}
496 
497 	if (tries == 5) {
498 		ERROR("%s: FDI train 2 failure!\n", __func__);
499 		return B_ERROR;
500 	}
501 
502 	return B_OK;
503 }
504 
505 
506 status_t
507 FDILink::_SnbTrain(uint32 lanes)
508 {
509 	CALLED();
510 	uint32 txControl = FDI_TX_CTL(fPipeIndex);
511 	uint32 rxControl = FDI_RX_CTL(fPipeIndex);
512 
513 	// Train 1
514 	uint32 imrControl = FDI_RX_IMR(fPipeIndex);
515 	uint32 tmp = read32(imrControl);
516 	tmp &= ~FDI_RX_SYMBOL_LOCK;
517 	tmp &= ~FDI_RX_BIT_LOCK;
518 	write32(imrControl, tmp);
519 	read32(imrControl);
520 	spin(150);
521 
522 	tmp = read32(txControl);
523 	tmp &= ~FDI_DP_PORT_WIDTH_MASK;
524 	tmp |= FDI_DP_PORT_WIDTH(lanes);
525 	tmp &= ~FDI_LINK_TRAIN_NONE;
526 	tmp |= FDI_LINK_TRAIN_PATTERN_1;
527 	tmp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
528 
529 	tmp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B;
530 	write32(txControl, tmp);
531 
532 	write32(FDI_RX_MISC(fPipeIndex),
533 		FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90);
534 
535 	tmp = read32(rxControl);
536 	if (gInfo->shared_info->pch_info == INTEL_PCH_CPT) {
537 		tmp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
538 		tmp |= FDI_LINK_TRAIN_PATTERN_1_CPT;
539 	} else {
540 		tmp &= ~FDI_LINK_TRAIN_NONE;
541 		tmp |= FDI_LINK_TRAIN_PATTERN_1;
542 	}
543 	write32(rxControl, tmp);
544 	Receiver().Enable();
545 
546 	uint32 iirControl = FDI_RX_IIR(fPipeIndex);
547 	TRACE("%s: FDI RX IIR Control @ 0x%" B_PRIx32 "\n", __func__, iirControl);
548 
549 	int i = 0;
550 	for (i = 0; i < 4; i++) {
551 		tmp = read32(txControl);
552 		tmp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
553 		tmp |= gSnbBFDITrainParam[i];
554 		write32(txControl, tmp);
555 
556 		read32(txControl);
557 		spin(500);
558 
559 		int retry = 0;
560 		for (retry = 0; retry < 5; retry++) {
561 			tmp = read32(iirControl);
562 			TRACE("%s: FDI RX IIR 0x%" B_PRIx32 "\n", __func__, tmp);
563 			if (tmp & FDI_RX_BIT_LOCK) {
564 				TRACE("%s: FDI train 1 done\n", __func__);
565 				write32(iirControl, tmp | FDI_RX_BIT_LOCK);
566 				break;
567 			}
568 			spin(50);
569 		}
570 		if (retry < 5)
571 			break;
572 	}
573 
574 	if (i == 4) {
575 		ERROR("%s: FDI train 1 failure!\n", __func__);
576 		return B_ERROR;
577 	}
578 
579 	// Train 2
580 	tmp = read32(txControl);
581 	tmp &= ~FDI_LINK_TRAIN_NONE;
582 	tmp |= FDI_LINK_TRAIN_PATTERN_2;
583 
584 	// if gen6? It's always gen6
585 	tmp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
586 	tmp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B;
587 	write32(txControl, tmp);
588 
589 	tmp = read32(rxControl);
590 	if (gInfo->shared_info->pch_info == INTEL_PCH_CPT) {
591 		tmp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
592 		tmp |= FDI_LINK_TRAIN_PATTERN_2_CPT;
593 	} else {
594 		tmp &= ~FDI_LINK_TRAIN_NONE;
595 		tmp |= FDI_LINK_TRAIN_PATTERN_2;
596 	}
597 	write32(rxControl, tmp);
598 
599 	read32(rxControl);
600 	spin(150);
601 
602 	for (i = 0; i < 4; i++) {
603 		tmp = read32(txControl);
604 		tmp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
605 		tmp |= gSnbBFDITrainParam[i];
606 		write32(txControl, tmp);
607 
608 		read32(txControl);
609 		spin(500);
610 
611 		int retry = 0;
612 		for (retry = 0; retry < 5; retry++) {
613 			tmp = read32(iirControl);
614 			TRACE("%s: FDI RX IIR 0x%" B_PRIx32 "\n", __func__, tmp);
615 
616 			if (tmp & FDI_RX_SYMBOL_LOCK) {
617 				TRACE("%s: FDI train 2 done\n", __func__);
618 				write32(iirControl, tmp | FDI_RX_SYMBOL_LOCK);
619 				break;
620 			}
621 			spin(50);
622 		}
623 		if (retry < 5)
624 			break;
625 	}
626 
627 	if (i == 4) {
628 		ERROR("%s: FDI train 1 failure!\n", __func__);
629 		return B_ERROR;
630 	}
631 
632 	return B_OK;
633 }
634 
635 
636 status_t
637 FDILink::_ManualTrain(uint32 lanes)
638 {
639 	CALLED();
640 	//uint32 txControl = Transmitter().Base() + PCH_FDI_TX_CONTROL;
641 	//uint32 rxControl = Receiver().Base() + PCH_FDI_RX_CONTROL;
642 
643 	ERROR("%s: TODO\n", __func__);
644 
645 	return B_ERROR;
646 }
647 
648 
649 status_t
650 FDILink::_AutoTrain(uint32 lanes)
651 {
652 	CALLED();
653 	uint32 txControl = FDI_TX_CTL(fPipeIndex);
654 	uint32 rxControl = FDI_RX_CTL(fPipeIndex);
655 
656 	uint32 buffer = read32(txControl);
657 
658 	// Clear port width selection and set number of lanes
659 	// fixme: does not belong in the train routines (?), (now) sits in FDI EnablePLL() routines
660 	buffer &= ~(7 << 19);
661 	buffer |= (lanes - 1) << 19;
662 
663 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IVB))
664 		buffer &= ~FDI_LINK_TRAIN_NONE_IVB;
665 	else
666 		buffer &= ~FDI_LINK_TRAIN_NONE;
667 	write32(txControl, buffer);
668 
669 	write32(FDI_RX_MISC(fPipeIndex), FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90);
670 
671 	bool trained = false;
672 
673 	for (uint32 i = 0; i < (sizeof(gSnbBFDITrainParam)
674 		/ sizeof(gSnbBFDITrainParam[0])); i++) {
675 		for (int j = 0; j < 2; j++) {
676 			buffer = read32(txControl);
677 			buffer |= FDI_AUTO_TRAINING;
678 			buffer &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
679 			buffer |= gSnbBFDITrainParam[i];
680 			write32(txControl, buffer | FDI_TX_ENABLE);
681 			read32(txControl);
682 			write32(rxControl, read32(rxControl) | FDI_RX_ENABLE);
683 			read32(rxControl);
684 
685 			spin(50);//looks like datasheet specified 5uS is not enough..?
686 
687 			buffer = read32(txControl);
688 			if ((buffer & FDI_AUTO_TRAIN_DONE) != 0) {
689 				TRACE("%s: FDI auto train complete!\n", __func__);
690 				trained = true;
691 				break;
692 			}
693 
694 			write32(txControl, read32(txControl) & ~FDI_TX_ENABLE);
695 			read32(txControl);
696 			write32(rxControl, read32(rxControl) & ~FDI_RX_ENABLE);
697 			read32(rxControl);
698 
699 			spin(31);
700 		}
701 
702 		// If Trained, we fall out of autotraining
703 		if (trained)
704 			break;
705 	}
706 
707 	if (!trained) {
708 		ERROR("%s: FDI auto train failed!\n", __func__);
709 		return B_ERROR;
710 	}
711 
712 	// Enable ecc on IVB (and disable test pattern at sending and receiving end)
713 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IVB)) {
714 		write32(rxControl, read32(rxControl)
715 			| FDI_FS_ERRC_ENABLE | FDI_FE_ERRC_ENABLE);
716 		read32(rxControl);
717 		//enable normal pixels (kill testpattern)
718 		write32(txControl, read32(txControl) | (0x3 << 8));
719 		read32(txControl);
720 	}
721 
722 	return B_OK;
723 }
724 
725 
726 FDILink::~FDILink()
727 {
728 }
729