xref: /haiku/src/add-ons/accelerants/intel_extreme/FlexibleDisplayInterface.cpp (revision 4bd0c1066b227cec4b79883bdef697c7a27f2e90)
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 	write32(targetRegister, value | FDI_TX_PLL_ENABLED);
107 	read32(targetRegister);
108 	spin(100); // warmup 10us + dmi delay 20us, be generous
109 }
110 
111 
112 void
113 FDITransmitter::DisablePLL()
114 {
115 	CALLED();
116 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_ILK)) {
117 		// on IronLake the FDI PLL is alaways enabled, so no point in trying...
118 		return;
119 	}
120 
121 	uint32 targetRegister = fRegisterBase + PCH_FDI_TX_CONTROL;
122 	write32(targetRegister, read32(targetRegister) & ~FDI_TX_PLL_ENABLED);
123 	read32(targetRegister);
124 	spin(100);
125 }
126 
127 
128 // #pragma mark - FDIReceiver
129 
130 
131 FDIReceiver::FDIReceiver(pipe_index pipeIndex)
132 	:
133 	fRegisterBase(PCH_FDI_RX_BASE_REGISTER)
134 {
135 	if (pipeIndex == INTEL_PIPE_B)
136 		fRegisterBase += PCH_FDI_RX_PIPE_OFFSET * 1;
137 }
138 
139 
140 FDIReceiver::~FDIReceiver()
141 {
142 }
143 
144 
145 void
146 FDIReceiver::Enable()
147 {
148 	CALLED();
149 	uint32 targetRegister = fRegisterBase + PCH_FDI_RX_CONTROL;
150 	uint32 value = read32(targetRegister);
151 
152 	write32(targetRegister, value | FDI_RX_ENABLE);
153 	read32(targetRegister);
154 	spin(150);
155 }
156 
157 
158 void
159 FDIReceiver::Disable()
160 {
161 	CALLED();
162 	uint32 targetRegister = fRegisterBase + PCH_FDI_RX_CONTROL;
163 	uint32 value = read32(targetRegister);
164 
165 	write32(targetRegister, value & ~FDI_RX_ENABLE);
166 	read32(targetRegister);
167 	spin(150);
168 }
169 
170 
171 bool
172 FDIReceiver::IsPLLEnabled()
173 {
174 	CALLED();
175 	return (read32(fRegisterBase + PCH_FDI_RX_CONTROL) & FDI_RX_PLL_ENABLED)
176 		!= 0;
177 }
178 
179 
180 void
181 FDIReceiver::EnablePLL(uint32 lanes)
182 {
183 	CALLED();
184 	uint32 targetRegister = fRegisterBase + PCH_FDI_RX_CONTROL;
185 	uint32 value = read32(targetRegister);
186 	if ((value & FDI_RX_PLL_ENABLED) != 0) {
187 		// already enabled, possibly IronLake where it always is
188 		TRACE("%s: Already enabled.\n", __func__);
189 		return;
190 	}
191 
192 	value &= ~(FDI_DP_PORT_WIDTH_MASK | (0x7 << 16));
193 	value |= FDI_DP_PORT_WIDTH(lanes);
194 	//value |= (read32(PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11;
195 
196 	write32(targetRegister, value | FDI_RX_PLL_ENABLED);
197 	read32(targetRegister);
198 	spin(200); // warmup 10us + dmi delay 20us, be generous
199 }
200 
201 
202 void
203 FDIReceiver::DisablePLL()
204 {
205 	CALLED();
206 	uint32 targetRegister = fRegisterBase + PCH_FDI_RX_CONTROL;
207 	write32(targetRegister, read32(targetRegister) & ~FDI_RX_PLL_ENABLED);
208 	read32(targetRegister);
209 	spin(100);
210 }
211 
212 
213 void
214 FDIReceiver::SwitchClock(bool toPCDClock)
215 {
216 	CALLED();
217 	uint32 targetRegister = fRegisterBase + PCH_FDI_RX_CONTROL;
218 	write32(targetRegister, (read32(targetRegister) & ~FDI_RX_CLOCK_MASK)
219 		| (toPCDClock ? FDI_RX_CLOCK_PCD : FDI_RX_CLOCK_RAW));
220 	read32(targetRegister);
221 	spin(200);
222 }
223 
224 
225 // #pragma mark - FDILink
226 
227 
228 FDILink::FDILink(pipe_index pipeIndex)
229 	:
230 	fTransmitter(pipeIndex),
231 	fReceiver(pipeIndex),
232 	fPipeIndex(pipeIndex)
233 {
234 }
235 
236 
237 status_t
238 FDILink::Train(display_mode* target)
239 {
240 	CALLED();
241 
242 	uint32 bitsPerPixel;
243 	switch (target->space) {
244 		case B_RGB32_LITTLE:
245 			bitsPerPixel = 32;
246 			break;
247 		case B_RGB16_LITTLE:
248 			bitsPerPixel = 16;
249 			break;
250 		case B_RGB15_LITTLE:
251 			bitsPerPixel = 15;
252 			break;
253 		case B_CMAP8:
254 		default:
255 			bitsPerPixel = 8;
256 			break;
257 	}
258 
259 	// Khz / 10. ( each output octet encoded as 10 bits.
260 	uint32 linkBandwidth = gInfo->shared_info->fdi_link_frequency * 1000 / 10;
261 	uint32 bps = target->timing.pixel_clock * bitsPerPixel * 21 / 20;
262 
263 	uint32 lanes = bps / (linkBandwidth * 8);
264 
265 	TRACE("%s: FDI Link Lanes: %" B_PRIu32 "\n", __func__, lanes);
266 
267 	// Enable FDI clocks
268 	Receiver().EnablePLL(lanes);
269 	Receiver().SwitchClock(true);
270 	Transmitter().EnablePLL(lanes);
271 
272 	status_t result = B_ERROR;
273 
274 	// TODO: Only _AutoTrain on IVYB Stepping B or later
275 	// otherwise, _ManualTrain
276 	if (gInfo->shared_info->device_type.Generation() >= 7)
277 		result = _AutoTrain(lanes);
278 	else if (gInfo->shared_info->device_type.Generation() == 6)
279 		result = _SnbTrain(lanes);
280 	else if (gInfo->shared_info->device_type.Generation() == 5)
281 		result = _IlkTrain(lanes);
282 	else
283 		result = _NormalTrain(lanes);
284 
285 	if (result != B_OK) {
286 		ERROR("%s: FDI training fault.\n", __func__);
287 	}
288 
289 	return result;
290 }
291 
292 
293 status_t
294 FDILink::_NormalTrain(uint32 lanes)
295 {
296 	CALLED();
297 	uint32 txControl = Transmitter().Base() + PCH_FDI_TX_CONTROL;
298 	uint32 rxControl = Receiver().Base() + PCH_FDI_RX_CONTROL;
299 
300 	// Enable normal link training
301 	uint32 tmp = read32(txControl);
302 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IVB)) {
303 		tmp &= ~FDI_LINK_TRAIN_NONE_IVB;
304 		tmp |= FDI_LINK_TRAIN_NONE_IVB | FDI_TX_ENHANCE_FRAME_ENABLE;
305 	} else {
306 		tmp &= ~FDI_LINK_TRAIN_NONE;
307 		tmp |= FDI_LINK_TRAIN_NONE | FDI_TX_ENHANCE_FRAME_ENABLE;
308 	}
309 	write32(txControl, tmp);
310 
311 	tmp = read32(rxControl);
312 	if (gInfo->shared_info->pch_info == INTEL_PCH_CPT) {
313 		tmp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
314 		tmp |= FDI_LINK_TRAIN_NORMAL_CPT;
315 	} else {
316 		tmp &= ~FDI_LINK_TRAIN_NONE;
317 		tmp |= FDI_LINK_TRAIN_NONE;
318 	}
319 	write32(rxControl, tmp | FDI_RX_ENHANCE_FRAME_ENABLE);
320 
321 	// Wait 1x idle pattern
322 	read32(rxControl);
323 	spin(1000);
324 
325 	// Enable ecc on IVB
326 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IVB)) {
327 		write32(rxControl, read32(rxControl)
328 			| FDI_FS_ERRC_ENABLE | FDI_FE_ERRC_ENABLE);
329 		read32(rxControl);
330 	}
331 
332 	return B_OK;
333 }
334 
335 
336 status_t
337 FDILink::_IlkTrain(uint32 lanes)
338 {
339 	CALLED();
340 	uint32 txControl = Transmitter().Base() + PCH_FDI_TX_CONTROL;
341 	uint32 rxControl = Receiver().Base() + PCH_FDI_RX_CONTROL;
342 
343 	// Train 1: unmask FDI RX Interrupt symbol_lock and bit_lock
344 	uint32 tmp = read32(Receiver().Base() + PCH_FDI_RX_IMR);
345 	tmp &= ~FDI_RX_SYMBOL_LOCK;
346 	tmp &= ~FDI_RX_BIT_LOCK;
347 	write32(Receiver().Base() + PCH_FDI_RX_IMR, tmp);
348 	spin(150);
349 
350 	// Enable CPU FDI TX and RX
351 	tmp = read32(txControl);
352 	tmp &= ~FDI_DP_PORT_WIDTH_MASK;
353 	tmp |= FDI_DP_PORT_WIDTH(lanes);
354 	tmp &= ~FDI_LINK_TRAIN_NONE;
355 	tmp |= FDI_LINK_TRAIN_PATTERN_1;
356 	write32(txControl, tmp);
357 	Transmitter().Enable();
358 
359 	tmp = read32(rxControl);
360 	tmp &= ~FDI_LINK_TRAIN_NONE;
361 	tmp |= FDI_LINK_TRAIN_PATTERN_1;
362 	write32(rxControl, tmp);
363 	Receiver().Enable();
364 
365 	// ILK Workaround, enable clk after FDI enable
366 	if (fPipeIndex == INTEL_PIPE_B) {
367 		write32(PCH_FDI_RXB_CHICKEN, FDI_RX_PHASE_SYNC_POINTER_OVR);
368 		write32(PCH_FDI_RXB_CHICKEN, FDI_RX_PHASE_SYNC_POINTER_OVR
369 			| FDI_RX_PHASE_SYNC_POINTER_EN);
370 	} else {
371 		write32(PCH_FDI_RXA_CHICKEN, FDI_RX_PHASE_SYNC_POINTER_OVR);
372 		write32(PCH_FDI_RXA_CHICKEN, FDI_RX_PHASE_SYNC_POINTER_OVR
373 			| FDI_RX_PHASE_SYNC_POINTER_EN);
374 	}
375 
376 	uint32 iirControl = Receiver().Base() + PCH_FDI_RX_IIR;
377 	TRACE("%s: FDI RX IIR Control @ 0x%" B_PRIx32 "\n", __func__, iirControl);
378 
379 	int tries = 0;
380 	for (tries = 0; tries < 5; tries++) {
381 		tmp = read32(iirControl);
382 		TRACE("%s: FDI RX IIR 0x%" B_PRIx32 "\n", __func__, tmp);
383 
384 		if ((tmp & FDI_RX_BIT_LOCK)) {
385 			TRACE("%s: FDI train 1 done\n", __func__);
386 			write32(iirControl, tmp | FDI_RX_BIT_LOCK);
387 			break;
388 		}
389 	}
390 
391 	if (tries == 5) {
392 		ERROR("%s: FDI train 1 failure!\n", __func__);
393 		return B_ERROR;
394 	}
395 
396 	// Train 2
397 	tmp = read32(txControl);
398 	tmp &= ~FDI_LINK_TRAIN_NONE;
399 	tmp |= FDI_LINK_TRAIN_PATTERN_2;
400 	write32(txControl, tmp);
401 
402 	tmp = read32(rxControl);
403 	tmp &= ~FDI_LINK_TRAIN_NONE;
404 	tmp |= FDI_LINK_TRAIN_PATTERN_2;
405 	write32(rxControl, tmp);
406 
407 	read32(rxControl);
408 	spin(150);
409 
410 	for (tries = 0; tries < 5; tries++) {
411 		tmp = read32(iirControl);
412 		TRACE("%s: FDI RX IIR 0x%" B_PRIx32 "\n", __func__, tmp);
413 
414 		if (tmp & FDI_RX_SYMBOL_LOCK) {
415 			TRACE("%s: FDI train 2 done\n", __func__);
416 			write32(iirControl, tmp | FDI_RX_SYMBOL_LOCK);
417 			break;
418 		}
419 	}
420 
421 	if (tries == 5) {
422 		ERROR("%s: FDI train 2 failure!\n", __func__);
423 		return B_ERROR;
424 	}
425 
426 	return B_OK;
427 }
428 
429 
430 status_t
431 FDILink::_SnbTrain(uint32 lanes)
432 {
433 	CALLED();
434 	uint32 txControl = Transmitter().Base() + PCH_FDI_TX_CONTROL;
435 	uint32 rxControl = Receiver().Base() + PCH_FDI_RX_CONTROL;
436 
437 	// Train 1
438 	uint32 imrControl = Receiver().Base() + PCH_FDI_RX_IMR;
439 	uint32 tmp = read32(imrControl);
440 	tmp &= ~FDI_RX_SYMBOL_LOCK;
441 	tmp &= ~FDI_RX_BIT_LOCK;
442 	write32(imrControl, tmp);
443 	read32(imrControl);
444 	spin(150);
445 
446 	tmp = read32(txControl);
447 	tmp &= ~FDI_DP_PORT_WIDTH_MASK;
448 	tmp |= FDI_DP_PORT_WIDTH(lanes);
449 	tmp &= ~FDI_LINK_TRAIN_NONE;
450 	tmp |= FDI_LINK_TRAIN_PATTERN_1;
451 	tmp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
452 
453 	tmp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B;
454 	write32(txControl, tmp);
455 
456 	write32(Receiver().Base() + PCH_FDI_RX_MISC,
457 		FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90);
458 
459 	tmp = read32(rxControl);
460 	if (gInfo->shared_info->pch_info == INTEL_PCH_CPT) {
461 		tmp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
462 		tmp |= FDI_LINK_TRAIN_PATTERN_1_CPT;
463 	} else {
464 		tmp &= ~FDI_LINK_TRAIN_NONE;
465 		tmp |= FDI_LINK_TRAIN_PATTERN_1;
466 	}
467 	write32(rxControl, rxControl);
468 	Receiver().Enable();
469 
470 	uint32 iirControl = Receiver().Base() + PCH_FDI_RX_IIR;
471 	TRACE("%s: FDI RX IIR Control @ 0x%" B_PRIx32 "\n", __func__, iirControl);
472 
473 	int i = 0;
474 	for (i = 0; i < 4; i++) {
475 		tmp = read32(txControl);
476 		tmp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
477 		tmp |= gSnbBFDITrainParam[i];
478 		write32(txControl, tmp);
479 
480 		read32(txControl);
481 		spin(500);
482 
483 		int retry = 0;
484 		for (retry = 0; retry < 5; retry++) {
485 			tmp = read32(iirControl);
486 			TRACE("%s: FDI RX IIR 0x%" B_PRIx32 "\n", __func__, tmp);
487 			if (tmp & FDI_RX_BIT_LOCK) {
488 				TRACE("%s: FDI train 1 done\n", __func__);
489 				write32(iirControl, tmp | FDI_RX_BIT_LOCK);
490 				break;
491 			}
492 			spin(50);
493 		}
494 		if (retry < 5)
495 			break;
496 	}
497 
498 	if (i == 4) {
499 		ERROR("%s: FDI train 1 failure!\n", __func__);
500 		return B_ERROR;
501 	}
502 
503 	// Train 2
504 	tmp = read32(txControl);
505 	tmp &= ~FDI_LINK_TRAIN_NONE;
506 	tmp |= FDI_LINK_TRAIN_PATTERN_2;
507 
508 	// if gen6? It's always gen6
509 	tmp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
510 	tmp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B;
511 	write32(txControl, tmp);
512 
513 	tmp = read32(rxControl);
514 	if (gInfo->shared_info->pch_info == INTEL_PCH_CPT) {
515 		tmp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
516 		tmp |= FDI_LINK_TRAIN_PATTERN_2_CPT;
517 	} else {
518 		tmp &= ~FDI_LINK_TRAIN_NONE;
519 		tmp |= FDI_LINK_TRAIN_PATTERN_2;
520 	}
521 	write32(rxControl, tmp);
522 
523 	read32(rxControl);
524 	spin(150);
525 
526 	for (i = 0; i < 4; i++) {
527 		tmp = read32(txControl);
528 		tmp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
529 		tmp |= gSnbBFDITrainParam[i];
530 		write32(txControl, tmp);
531 
532 		read32(txControl);
533 		spin(500);
534 
535 		int retry = 0;
536 		for (retry = 0; retry < 5; retry++) {
537 			tmp = read32(iirControl);
538 			TRACE("%s: FDI RX IIR 0x%" B_PRIx32 "\n", __func__, tmp);
539 
540 			if (tmp & FDI_RX_SYMBOL_LOCK) {
541 				TRACE("%s: FDI train 2 done\n", __func__);
542 				write32(iirControl, tmp | FDI_RX_SYMBOL_LOCK);
543 				break;
544 			}
545 			spin(50);
546 		}
547 		if (retry < 5)
548 			break;
549 	}
550 
551 	if (i == 4) {
552 		ERROR("%s: FDI train 1 failure!\n", __func__);
553 		return B_ERROR;
554 	}
555 
556 	return B_OK;
557 }
558 
559 
560 status_t
561 FDILink::_ManualTrain(uint32 lanes)
562 {
563 	CALLED();
564 	//uint32 txControl = Transmitter().Base() + PCH_FDI_TX_CONTROL;
565 	//uint32 rxControl = Receiver().Base() + PCH_FDI_RX_CONTROL;
566 
567 	ERROR("%s: TODO\n", __func__);
568 
569 	return B_ERROR;
570 }
571 
572 
573 status_t
574 FDILink::_AutoTrain(uint32 lanes)
575 {
576 	CALLED();
577 	uint32 txControl = Transmitter().Base() + PCH_FDI_TX_CONTROL;
578 	uint32 rxControl = Receiver().Base() + PCH_FDI_RX_CONTROL;
579 
580 	uint32 buffer = read32(txControl);
581 
582 	// Clear port width selection and set number of lanes
583 	buffer &= ~(7 << 19);
584 	buffer |= (lanes - 1) << 19;
585 
586 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IVB))
587 		buffer &= ~FDI_LINK_TRAIN_NONE_IVB;
588 	else
589 		buffer &= ~FDI_LINK_TRAIN_NONE;
590 	write32(txControl, buffer);
591 
592 	bool trained = false;
593 
594 	for (uint32 i = 0; i < (sizeof(gSnbBFDITrainParam)
595 		/ sizeof(gSnbBFDITrainParam[0])); i++) {
596 		for (int j = 0; j < 2; j++) {
597 			buffer = read32(txControl);
598 			buffer |= FDI_AUTO_TRAINING;
599 			buffer &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
600 			buffer |= gSnbBFDITrainParam[i];
601 			write32(txControl, buffer | FDI_TX_ENABLE);
602 
603 			write32(rxControl, read32(rxControl) | FDI_RX_ENABLE);
604 
605 			spin(5);
606 
607 			buffer = read32(txControl);
608 			if ((buffer & FDI_AUTO_TRAIN_DONE) != 0) {
609 				TRACE("%s: FDI auto train complete!\n", __func__);
610 				trained = true;
611 				break;
612 			}
613 
614 			write32(txControl, read32(txControl) & ~FDI_TX_ENABLE);
615 			write32(rxControl, read32(rxControl) & ~FDI_RX_ENABLE);
616 			read32(rxControl);
617 
618 			spin(31);
619 		}
620 
621 		// If Trained, we fall out of autotraining
622 		if (trained)
623 			break;
624 	}
625 
626 	if (!trained) {
627 		ERROR("%s: FDI auto train failed!\n", __func__);
628 		return B_ERROR;
629 	}
630 
631 	// Enable ecc on IVB
632 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IVB)) {
633 		write32(rxControl, read32(rxControl)
634 			| FDI_FS_ERRC_ENABLE | FDI_FE_ERRC_ENABLE);
635 		read32(rxControl);
636 	}
637 
638 	return B_OK;
639 }
640 
641 
642 FDILink::~FDILink()
643 {
644 }
645