xref: /haiku/src/add-ons/accelerants/intel_extreme/FlexibleDisplayInterface.cpp (revision fce7f3a748cd57bb62fcdd32c84bf013d88ea351)
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 	// note: if used for eDP (PORT_A) might be we should check reg. DP_CTL (0x64000), bit 16-17 (Ivy).
285 	*linkBandwidth = gInfo->shared_info->fdi_link_frequency * 1000 / 10;
286 	//Reserving 5% bandwidth for possible spread spectrum clock use
287 	uint32 bps = target->pixel_clock * *bitsPerPixel * 21 / 20;
288 
289 	//use DIV_ROUND_UP:
290 	*lanes = (bps + (*linkBandwidth * 8) - 1) / (*linkBandwidth * 8);
291 	//remove below line when link training is to be done
292 	*lanes = ((read32(txControl) & FDI_DP_PORT_WIDTH_MASK) >> FDI_DP_PORT_WIDTH_SHIFT) + 1;
293 
294 	TRACE("%s: FDI Link Lanes: %" B_PRIu32 "\n", __func__, *lanes);
295 	//assuming we'll only use link A and B (not C)
296 	if (*lanes > 4) {
297 		ERROR("%s: FDI not enough lanes in hardware.\n", __func__);
298 		return B_ERROR;
299 	}
300 
301 	TRACE("%s: FDI TX ctrl before: 0x%" B_PRIx32 "\n", __func__, read32(txControl));
302 	TRACE("%s: FDI RX ctrl before: 0x%" B_PRIx32 "\n", __func__, read32(rxControl));
303 
304 #if 0
305 	//when link training is to be done re-enable this code
306 
307 	//The order of handling things is important here..
308 	write32(txControl, read32(txControl) & ~FDI_TX_ENABLE);
309 	read32(txControl);
310 	write32(rxControl, read32(rxControl) & ~FDI_RX_ENABLE);
311 	read32(rxControl);
312 
313 	write32(txControl, (read32(txControl) & ~FDI_LINK_TRAIN_NONE) | FDI_LINK_TRAIN_PATTERN_1);
314 	read32(txControl);
315 	if (gInfo->shared_info->pch_info == INTEL_PCH_CPT) {
316 		write32(rxControl, (read32(rxControl) & ~FDI_LINK_TRAIN_PATTERN_MASK_CPT) | FDI_LINK_TRAIN_PATTERN_1_CPT);
317 	} else {
318 		write32(rxControl, (read32(rxControl) & ~FDI_LINK_TRAIN_NONE) | FDI_LINK_TRAIN_PATTERN_1);
319 	}
320 	read32(rxControl);
321 	spin(100);
322 
323 	// Disable FDI clocks
324 	Receiver().SwitchClock(false);
325 	Transmitter().DisablePLL();
326 	Receiver().DisablePLL();
327 #endif
328 
329 	return B_OK;
330 }
331 
332 
333 status_t
334 FDILink::Train(display_timing* target, uint32 lanes)
335 {
336 	CALLED();
337 
338 	status_t result = B_OK;
339 
340 	uint32 txControl = Transmitter().Base() + PCH_FDI_TX_CONTROL;
341 	uint32 rxControl = Receiver().Base() + PCH_FDI_RX_CONTROL;
342 
343 	//Set receiving end TU size bits to match sending end's setting
344 	write32(Receiver().Base() + PCH_FDI_RX_TRANS_UNIT_SIZE_1, FDI_RX_TRANS_UNIT_MASK);
345 	write32(Receiver().Base() + PCH_FDI_RX_TRANS_UNIT_SIZE_2, FDI_RX_TRANS_UNIT_MASK);
346 
347 #if 0
348 	//when link training is to be done re-enable this code
349 	// Enable FDI clocks
350 	Receiver().EnablePLL(lanes);
351 	Receiver().SwitchClock(true);
352 	Transmitter().EnablePLL(lanes);
353 
354 	// TODO: Only _AutoTrain on IVYB Stepping B or later
355 	// otherwise, _ManualTrain
356 	if (gInfo->shared_info->device_type.Generation() >= 7)
357 		result = _AutoTrain(lanes);
358 	else if (gInfo->shared_info->device_type.Generation() == 6)
359 		result = _SnbTrain(lanes);
360 	else if (gInfo->shared_info->device_type.Generation() == 5)
361 		result = _IlkTrain(lanes);
362 	else
363 		result = _NormalTrain(lanes);
364 #endif
365 
366 	TRACE("%s: FDI TX ctrl after: 0x%" B_PRIx32 "\n", __func__, read32(txControl));
367 	TRACE("%s: FDI RX ctrl after: 0x%" B_PRIx32 "\n", __func__, read32(rxControl));
368 
369 	if (result != B_OK)
370 		ERROR("%s: FDI training fault.\n", __func__);
371 
372 	return result;
373 }
374 
375 
376 status_t
377 FDILink::_NormalTrain(uint32 lanes)
378 {
379 	CALLED();
380 	uint32 txControl = Transmitter().Base() + PCH_FDI_TX_CONTROL;
381 	uint32 rxControl = Receiver().Base() + PCH_FDI_RX_CONTROL;
382 
383 	// Enable normal link training
384 	uint32 tmp = read32(txControl);
385 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IVB)) {
386 		tmp &= ~FDI_LINK_TRAIN_NONE_IVB;
387 		tmp |= FDI_LINK_TRAIN_NONE_IVB | FDI_TX_ENHANCE_FRAME_ENABLE;
388 	} else {
389 		tmp &= ~FDI_LINK_TRAIN_NONE;
390 		tmp |= FDI_LINK_TRAIN_NONE | FDI_TX_ENHANCE_FRAME_ENABLE;
391 	}
392 	write32(txControl, tmp);
393 
394 	tmp = read32(rxControl);
395 	if (gInfo->shared_info->pch_info == INTEL_PCH_CPT) {
396 		tmp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
397 		tmp |= FDI_LINK_TRAIN_NORMAL_CPT;
398 	} else {
399 		tmp &= ~FDI_LINK_TRAIN_NONE;
400 		tmp |= FDI_LINK_TRAIN_NONE;
401 	}
402 	write32(rxControl, tmp | FDI_RX_ENHANCE_FRAME_ENABLE);
403 
404 	// Wait 1x idle pattern
405 	read32(rxControl);
406 	spin(1000);
407 
408 	// Enable ecc on IVB
409 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IVB)) {
410 		write32(rxControl, read32(rxControl)
411 			| FDI_FS_ERRC_ENABLE | FDI_FE_ERRC_ENABLE);
412 		read32(rxControl);
413 	}
414 
415 	return B_OK;
416 }
417 
418 
419 status_t
420 FDILink::_IlkTrain(uint32 lanes)
421 {
422 	CALLED();
423 	uint32 txControl = Transmitter().Base() + PCH_FDI_TX_CONTROL;
424 	uint32 rxControl = Receiver().Base() + PCH_FDI_RX_CONTROL;
425 
426 	// Train 1: unmask FDI RX Interrupt symbol_lock and bit_lock
427 	uint32 tmp = read32(Receiver().Base() + PCH_FDI_RX_IMR);
428 	tmp &= ~FDI_RX_SYMBOL_LOCK;
429 	tmp &= ~FDI_RX_BIT_LOCK;
430 	write32(Receiver().Base() + PCH_FDI_RX_IMR, tmp);
431 	spin(150);
432 
433 	// Enable CPU FDI TX and RX
434 	tmp = read32(txControl);
435 	tmp &= ~FDI_DP_PORT_WIDTH_MASK;
436 	tmp |= FDI_DP_PORT_WIDTH(lanes);
437 	tmp &= ~FDI_LINK_TRAIN_NONE;
438 	tmp |= FDI_LINK_TRAIN_PATTERN_1;
439 	write32(txControl, tmp);
440 	Transmitter().Enable();
441 
442 	tmp = read32(rxControl);
443 	tmp &= ~FDI_LINK_TRAIN_NONE;
444 	tmp |= FDI_LINK_TRAIN_PATTERN_1;
445 	write32(rxControl, tmp);
446 	Receiver().Enable();
447 
448 	// ILK Workaround, enable clk after FDI enable
449 	if (fPipeIndex == INTEL_PIPE_B) {
450 		write32(PCH_FDI_RXB_CHICKEN, FDI_RX_PHASE_SYNC_POINTER_OVR);
451 		write32(PCH_FDI_RXB_CHICKEN, FDI_RX_PHASE_SYNC_POINTER_OVR
452 			| FDI_RX_PHASE_SYNC_POINTER_EN);
453 	} else {
454 		write32(PCH_FDI_RXA_CHICKEN, FDI_RX_PHASE_SYNC_POINTER_OVR);
455 		write32(PCH_FDI_RXA_CHICKEN, FDI_RX_PHASE_SYNC_POINTER_OVR
456 			| FDI_RX_PHASE_SYNC_POINTER_EN);
457 	}
458 
459 	uint32 iirControl = Receiver().Base() + PCH_FDI_RX_IIR;
460 	TRACE("%s: FDI RX IIR Control @ 0x%" B_PRIx32 "\n", __func__, iirControl);
461 
462 	int tries = 0;
463 	for (tries = 0; tries < 5; tries++) {
464 		tmp = read32(iirControl);
465 		TRACE("%s: FDI RX IIR 0x%" B_PRIx32 "\n", __func__, tmp);
466 
467 		if ((tmp & FDI_RX_BIT_LOCK)) {
468 			TRACE("%s: FDI train 1 done\n", __func__);
469 			write32(iirControl, tmp | FDI_RX_BIT_LOCK);
470 			break;
471 		}
472 	}
473 
474 	if (tries == 5) {
475 		ERROR("%s: FDI train 1 failure!\n", __func__);
476 		return B_ERROR;
477 	}
478 
479 	// Train 2
480 	tmp = read32(txControl);
481 	tmp &= ~FDI_LINK_TRAIN_NONE;
482 	tmp |= FDI_LINK_TRAIN_PATTERN_2;
483 	write32(txControl, tmp);
484 
485 	tmp = read32(rxControl);
486 	tmp &= ~FDI_LINK_TRAIN_NONE;
487 	tmp |= FDI_LINK_TRAIN_PATTERN_2;
488 	write32(rxControl, tmp);
489 
490 	read32(rxControl);
491 	spin(150);
492 
493 	for (tries = 0; tries < 5; tries++) {
494 		tmp = read32(iirControl);
495 		TRACE("%s: FDI RX IIR 0x%" B_PRIx32 "\n", __func__, tmp);
496 
497 		if (tmp & FDI_RX_SYMBOL_LOCK) {
498 			TRACE("%s: FDI train 2 done\n", __func__);
499 			write32(iirControl, tmp | FDI_RX_SYMBOL_LOCK);
500 			break;
501 		}
502 	}
503 
504 	if (tries == 5) {
505 		ERROR("%s: FDI train 2 failure!\n", __func__);
506 		return B_ERROR;
507 	}
508 
509 	return B_OK;
510 }
511 
512 
513 status_t
514 FDILink::_SnbTrain(uint32 lanes)
515 {
516 	CALLED();
517 	uint32 txControl = Transmitter().Base() + PCH_FDI_TX_CONTROL;
518 	uint32 rxControl = Receiver().Base() + PCH_FDI_RX_CONTROL;
519 
520 	// Train 1
521 	uint32 imrControl = Receiver().Base() + PCH_FDI_RX_IMR;
522 	uint32 tmp = read32(imrControl);
523 	tmp &= ~FDI_RX_SYMBOL_LOCK;
524 	tmp &= ~FDI_RX_BIT_LOCK;
525 	write32(imrControl, tmp);
526 	read32(imrControl);
527 	spin(150);
528 
529 	tmp = read32(txControl);
530 	tmp &= ~FDI_DP_PORT_WIDTH_MASK;
531 	tmp |= FDI_DP_PORT_WIDTH(lanes);
532 	tmp &= ~FDI_LINK_TRAIN_NONE;
533 	tmp |= FDI_LINK_TRAIN_PATTERN_1;
534 	tmp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
535 
536 	tmp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B;
537 	write32(txControl, tmp);
538 
539 	write32(Receiver().Base() + PCH_FDI_RX_MISC,
540 		FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90);
541 
542 	tmp = read32(rxControl);
543 	if (gInfo->shared_info->pch_info == INTEL_PCH_CPT) {
544 		tmp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
545 		tmp |= FDI_LINK_TRAIN_PATTERN_1_CPT;
546 	} else {
547 		tmp &= ~FDI_LINK_TRAIN_NONE;
548 		tmp |= FDI_LINK_TRAIN_PATTERN_1;
549 	}
550 	write32(rxControl, tmp);
551 	Receiver().Enable();
552 
553 	uint32 iirControl = Receiver().Base() + PCH_FDI_RX_IIR;
554 	TRACE("%s: FDI RX IIR Control @ 0x%" B_PRIx32 "\n", __func__, iirControl);
555 
556 	int i = 0;
557 	for (i = 0; i < 4; i++) {
558 		tmp = read32(txControl);
559 		tmp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
560 		tmp |= gSnbBFDITrainParam[i];
561 		write32(txControl, tmp);
562 
563 		read32(txControl);
564 		spin(500);
565 
566 		int retry = 0;
567 		for (retry = 0; retry < 5; retry++) {
568 			tmp = read32(iirControl);
569 			TRACE("%s: FDI RX IIR 0x%" B_PRIx32 "\n", __func__, tmp);
570 			if (tmp & FDI_RX_BIT_LOCK) {
571 				TRACE("%s: FDI train 1 done\n", __func__);
572 				write32(iirControl, tmp | FDI_RX_BIT_LOCK);
573 				break;
574 			}
575 			spin(50);
576 		}
577 		if (retry < 5)
578 			break;
579 	}
580 
581 	if (i == 4) {
582 		ERROR("%s: FDI train 1 failure!\n", __func__);
583 		return B_ERROR;
584 	}
585 
586 	// Train 2
587 	tmp = read32(txControl);
588 	tmp &= ~FDI_LINK_TRAIN_NONE;
589 	tmp |= FDI_LINK_TRAIN_PATTERN_2;
590 
591 	// if gen6? It's always gen6
592 	tmp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
593 	tmp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B;
594 	write32(txControl, tmp);
595 
596 	tmp = read32(rxControl);
597 	if (gInfo->shared_info->pch_info == INTEL_PCH_CPT) {
598 		tmp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
599 		tmp |= FDI_LINK_TRAIN_PATTERN_2_CPT;
600 	} else {
601 		tmp &= ~FDI_LINK_TRAIN_NONE;
602 		tmp |= FDI_LINK_TRAIN_PATTERN_2;
603 	}
604 	write32(rxControl, tmp);
605 
606 	read32(rxControl);
607 	spin(150);
608 
609 	for (i = 0; i < 4; i++) {
610 		tmp = read32(txControl);
611 		tmp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
612 		tmp |= gSnbBFDITrainParam[i];
613 		write32(txControl, tmp);
614 
615 		read32(txControl);
616 		spin(500);
617 
618 		int retry = 0;
619 		for (retry = 0; retry < 5; retry++) {
620 			tmp = read32(iirControl);
621 			TRACE("%s: FDI RX IIR 0x%" B_PRIx32 "\n", __func__, tmp);
622 
623 			if (tmp & FDI_RX_SYMBOL_LOCK) {
624 				TRACE("%s: FDI train 2 done\n", __func__);
625 				write32(iirControl, tmp | FDI_RX_SYMBOL_LOCK);
626 				break;
627 			}
628 			spin(50);
629 		}
630 		if (retry < 5)
631 			break;
632 	}
633 
634 	if (i == 4) {
635 		ERROR("%s: FDI train 1 failure!\n", __func__);
636 		return B_ERROR;
637 	}
638 
639 	return B_OK;
640 }
641 
642 
643 status_t
644 FDILink::_ManualTrain(uint32 lanes)
645 {
646 	CALLED();
647 	//uint32 txControl = Transmitter().Base() + PCH_FDI_TX_CONTROL;
648 	//uint32 rxControl = Receiver().Base() + PCH_FDI_RX_CONTROL;
649 
650 	ERROR("%s: TODO\n", __func__);
651 
652 	return B_ERROR;
653 }
654 
655 
656 status_t
657 FDILink::_AutoTrain(uint32 lanes)
658 {
659 	CALLED();
660 	uint32 txControl = Transmitter().Base() + PCH_FDI_TX_CONTROL;
661 	uint32 rxControl = Receiver().Base() + PCH_FDI_RX_CONTROL;
662 
663 	uint32 buffer = read32(txControl);
664 
665 	// Clear port width selection and set number of lanes
666 	// fixme: does not belong in the train routines (?), (now) sits in FDI EnablePLL() routines
667 	buffer &= ~(7 << 19);
668 	buffer |= (lanes - 1) << 19;
669 
670 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IVB))
671 		buffer &= ~FDI_LINK_TRAIN_NONE_IVB;
672 	else
673 		buffer &= ~FDI_LINK_TRAIN_NONE;
674 	write32(txControl, buffer);
675 
676 	write32(Receiver().Base() + PCH_FDI_RX_MISC,
677 		FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90);
678 
679 	bool trained = false;
680 
681 	for (uint32 i = 0; i < (sizeof(gSnbBFDITrainParam)
682 		/ sizeof(gSnbBFDITrainParam[0])); i++) {
683 		for (int j = 0; j < 2; j++) {
684 			buffer = read32(txControl);
685 			buffer |= FDI_AUTO_TRAINING;
686 			buffer &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
687 			buffer |= gSnbBFDITrainParam[i];
688 			write32(txControl, buffer | FDI_TX_ENABLE);
689 			read32(txControl);
690 			write32(rxControl, read32(rxControl) | FDI_RX_ENABLE);
691 			read32(rxControl);
692 
693 			spin(50);//looks like datasheet specified 5uS is not enough..?
694 
695 			buffer = read32(txControl);
696 			if ((buffer & FDI_AUTO_TRAIN_DONE) != 0) {
697 				TRACE("%s: FDI auto train complete!\n", __func__);
698 				trained = true;
699 				break;
700 			}
701 
702 			write32(txControl, read32(txControl) & ~FDI_TX_ENABLE);
703 			read32(txControl);
704 			write32(rxControl, read32(rxControl) & ~FDI_RX_ENABLE);
705 			read32(rxControl);
706 
707 			spin(31);
708 		}
709 
710 		// If Trained, we fall out of autotraining
711 		if (trained)
712 			break;
713 	}
714 
715 	if (!trained) {
716 		ERROR("%s: FDI auto train failed!\n", __func__);
717 		return B_ERROR;
718 	}
719 
720 	// Enable ecc on IVB (and disable test pattern at sending and receiving end)
721 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IVB)) {
722 		write32(rxControl, read32(rxControl)
723 			| FDI_FS_ERRC_ENABLE | FDI_FE_ERRC_ENABLE);
724 		read32(rxControl);
725 		//enable normal pixels (kill testpattern)
726 		write32(txControl, read32(txControl) | (0x3 << 8));
727 		read32(txControl);
728 	}
729 
730 	return B_OK;
731 }
732 
733 
734 FDILink::~FDILink()
735 {
736 }
737