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