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