xref: /haiku/src/add-ons/accelerants/intel_extreme/Pipes.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 #include "Pipes.h"
10 
11 #include "accelerant.h"
12 #include "accelerant_protos.h"
13 #include "intel_extreme.h"
14 
15 #include <stdlib.h>
16 #include <string.h>
17 
18 #include <new>
19 
20 
21 #undef TRACE
22 #define TRACE_PIPE
23 #ifdef TRACE_PIPE
24 #	define TRACE(x...) _sPrintf("intel_extreme: " x)
25 #else
26 #	define TRACE(x...) ;
27 #endif
28 
29 #define ERROR(x...) _sPrintf("intel_extreme: " x)
30 #define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
31 
32 
33 // PIPE: 6
34 // PLANE: 7
35 
36 
37 void
38 program_pipe_color_modes(uint32 colorMode)
39 {
40 	// All pipes get the same color mode
41 	if (gInfo->shared_info->device_type.InFamily(INTEL_FAMILY_LAKE)) {
42 		write32(INTEL_DISPLAY_A_CONTROL, (read32(INTEL_DISPLAY_A_CONTROL)
43 			& ~(DISPLAY_CONTROL_COLOR_MASK_SKY | DISPLAY_CONTROL_GAMMA))
44 			| colorMode);
45 		write32(INTEL_DISPLAY_B_CONTROL, (read32(INTEL_DISPLAY_B_CONTROL)
46 			& ~(DISPLAY_CONTROL_COLOR_MASK_SKY | DISPLAY_CONTROL_GAMMA))
47 			| colorMode);
48 	} else {
49 		write32(INTEL_DISPLAY_A_CONTROL, (read32(INTEL_DISPLAY_A_CONTROL)
50 			& ~(DISPLAY_CONTROL_COLOR_MASK | DISPLAY_CONTROL_GAMMA))
51 			| colorMode);
52 		write32(INTEL_DISPLAY_B_CONTROL, (read32(INTEL_DISPLAY_B_CONTROL)
53 			& ~(DISPLAY_CONTROL_COLOR_MASK | DISPLAY_CONTROL_GAMMA))
54 			| colorMode);
55 	}
56 }
57 
58 
59 // #pragma mark - Pipe
60 
61 
62 Pipe::Pipe(pipe_index pipeIndex)
63 	:
64 	fHasTranscoder(false),
65 	fFDILink(NULL),
66 	fPanelFitter(NULL),
67 	fPipeIndex(pipeIndex),
68 	fPipeOffset(0),
69 	fPlaneOffset(0)
70 {
71 	switch (pipeIndex) {
72 		case INTEL_PIPE_B:
73 			TRACE("Pipe B.\n");
74 			fPipeOffset = 0x1000;
75 			fPlaneOffset = INTEL_PLANE_OFFSET;
76 			break;
77 		case INTEL_PIPE_C:
78 			TRACE("Pipe C.\n");
79 			fPipeOffset = 0x2000;
80 			fPlaneOffset = INTEL_PLANE_OFFSET * 2;
81 			break;
82 		case INTEL_PIPE_D:
83 			TRACE("Pipe D.\n");
84 			fPipeOffset = 0xf000;
85 			//no fPlaneOffset..
86 			break;
87 		default:
88 			TRACE("Pipe A.\n");
89 			break;
90 	}
91 
92 	// IvyBridge: Analog + Digital Ports behind FDI (on northbridge)
93 	// Haswell: Only VGA behind FDI (on northbridge)
94 	// SkyLake: FDI gone. No more northbridge video.
95 	if ((gInfo->shared_info->pch_info != INTEL_PCH_NONE) &&
96 		(gInfo->shared_info->device_type.Generation() <= 8)) {
97 		TRACE("%s: Pipe is routed through FDI\n", __func__);
98 
99 		// Program FDILink if PCH
100 		fFDILink = new(std::nothrow) FDILink(pipeIndex);
101 	}
102 	if (gInfo->shared_info->pch_info != INTEL_PCH_NONE) {
103 		// DDI also has transcoders
104 		fHasTranscoder = true;
105 		// Program gen5(+) style panelfitter as well (DDI has this as well..)
106 		fPanelFitter = new(std::nothrow) PanelFitter(pipeIndex);
107 	}
108 
109 	TRACE("Pipe Base: 0x%" B_PRIxADDR " Plane Base: 0x%" B_PRIxADDR "\n",
110 			fPipeOffset, fPlaneOffset);
111 }
112 
113 
114 Pipe::~Pipe()
115 {
116 }
117 
118 
119 bool
120 Pipe::IsEnabled()
121 {
122 	CALLED();
123 
124 	return (read32(INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset)
125 		& INTEL_PIPE_ENABLED) != 0;
126 }
127 
128 
129 void
130 Pipe::Configure(display_mode* mode)
131 {
132 	uint32 pipeControl = read32(INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset);
133 
134 	// TODO: Haswell+ dithering changes.
135 	//if (gInfo->shared_info->device_type.Generation() >= 4) {
136 	//	pipeControl |= (INTEL_PIPE_DITHER_EN | INTEL_PIPE_DITHER_TYPE_SP);
137 
138 	//Link bit depth: this should be globally known per FDI link (i.e. laptop panel 3x6, rest 3x8)
139 	//currently using BIOS preconfigured setup
140 	//pipeControl = (pipeControl & ~INTEL_PIPE_BPC_MASK) | INTEL_PIPE_BPC(INTEL_PIPE_8BPC);
141 
142 	// TODO: CxSR downclocking?
143 
144 	// TODO: Interlaced modes
145 	pipeControl = (pipeControl & ~(0x7 << 21)) | INTEL_PIPE_PROGRESSIVE;
146 
147 	write32(INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset, pipeControl);
148 	read32(INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset);
149 
150 	if (gInfo->shared_info->device_type.Generation() >= 6) {
151 		// According to SandyBridge modesetting sequence, pipe must be enabled
152 		// before PLL are configured.
153 		addr_t pipeReg = INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset;
154 		write32(pipeReg, read32(pipeReg) | INTEL_PIPE_ENABLED);
155 	}
156 }
157 
158 
159 void
160 Pipe::_ConfigureTranscoder(display_mode* target)
161 {
162 	CALLED();
163 
164 	TRACE("%s: fPipeOffset: 0x%" B_PRIxADDR"\n", __func__, fPipeOffset);
165 
166 	if (gInfo->shared_info->device_type.Generation() < 9) {
167 		// update timing (fPipeOffset bumps the DISPLAY_A to B when needed)
168 		write32(INTEL_TRANSCODER_A_HTOTAL + fPipeOffset,
169 			((uint32)(target->timing.h_total - 1) << 16)
170 			| ((uint32)target->timing.h_display - 1));
171 		write32(INTEL_TRANSCODER_A_HBLANK + fPipeOffset,
172 			((uint32)(target->timing.h_total - 1) << 16)
173 			| ((uint32)target->timing.h_display - 1));
174 		write32(INTEL_TRANSCODER_A_HSYNC + fPipeOffset,
175 			((uint32)(target->timing.h_sync_end - 1) << 16)
176 			| ((uint32)target->timing.h_sync_start - 1));
177 
178 		write32(INTEL_TRANSCODER_A_VTOTAL + fPipeOffset,
179 			((uint32)(target->timing.v_total - 1) << 16)
180 			| ((uint32)target->timing.v_display - 1));
181 		write32(INTEL_TRANSCODER_A_VBLANK + fPipeOffset,
182 			((uint32)(target->timing.v_total - 1) << 16)
183 			| ((uint32)target->timing.v_display - 1));
184 		write32(INTEL_TRANSCODER_A_VSYNC + fPipeOffset,
185 			((uint32)(target->timing.v_sync_end - 1) << 16)
186 			| ((uint32)target->timing.v_sync_start - 1));
187 
188 		#if 0
189 		// XXX: Is it ok to do these on non-digital?
190 		write32(INTEL_TRANSCODER_A_POS + fPipeOffset, 0);
191 		write32(INTEL_TRANSCODER_A_IMAGE_SIZE + fPipeOffset,
192 			((uint32)(target->timing.h_display - 1) << 16)
193 				| ((uint32)target->timing.v_display - 1));
194 		#endif
195 	} else {
196 		//on Skylake timing is already done in ConfigureTimings()
197 
198 		TRACE("%s: trans conf reg: 0x%" B_PRIx32"\n", __func__,
199 			read32(DDI_SKL_TRANS_CONF_A + fPipeOffset));
200 		TRACE("%s: trans DDI func ctl reg: 0x%" B_PRIx32"\n", __func__,
201 			read32(PIPE_DDI_FUNC_CTL_A + fPipeOffset));
202 		switch ((read32(PIPE_DDI_FUNC_CTL_A + fPipeOffset) & PIPE_DDI_MODESEL_MASK)
203 				>> PIPE_DDI_MODESEL_SHIFT) {
204 			case PIPE_DDI_MODE_DVI:
205 				TRACE("%s: Transcoder uses DVI mode\n", __func__);
206 				break;
207 			case PIPE_DDI_MODE_DP_SST:
208 				TRACE("%s: Transcoder uses DP SST mode\n", __func__);
209 				break;
210 			case PIPE_DDI_MODE_DP_MST:
211 				TRACE("%s: Transcoder uses DP MST mode\n", __func__);
212 				break;
213 			default:
214 				TRACE("%s: Transcoder uses HDMI mode\n", __func__);
215 				break;
216 		}
217 	}
218 }
219 
220 
221 status_t
222 Pipe::SetFDILink(const display_timing& timing, uint32 linkBandwidth, uint32 lanes, uint32 bitsPerPixel)
223 {
224 	TRACE("%s: fPipeOffset: 0x%" B_PRIxADDR "\n", __func__, fPipeOffset);
225 	TRACE("%s: FDI/PIPE link reference clock is %gMhz\n", __func__, linkBandwidth / 1000.0f);
226 	TRACE("%s: FDI/PIPE M1 data before: 0x%" B_PRIx32 "\n", __func__, read32(PCH_FDI_PIPE_A_DATA_M1 + fPipeOffset));
227 	TRACE("%s: FDI/PIPE N1 data before: 0x%" B_PRIx32 "\n", __func__, read32(PCH_FDI_PIPE_A_DATA_N1 + fPipeOffset));
228 	TRACE("%s: FDI/PIPE M1 link before: 0x%" B_PRIx32 "\n", __func__, read32(PCH_FDI_PIPE_A_LINK_M1 + fPipeOffset));
229 	TRACE("%s: FDI/PIPE N1 link before: 0x%" B_PRIx32 "\n", __func__, read32(PCH_FDI_PIPE_A_LINK_N1 + fPipeOffset));
230 
231 	if ((bitsPerPixel < 18) || (bitsPerPixel > 36)) {
232 		ERROR("%s: FDI/PIPE illegal colordepth set.\n", __func__);
233 		return B_ERROR;
234 	}
235 	TRACE("%s: FDI/PIPE link colordepth: %" B_PRIu32 "\n", __func__, bitsPerPixel);
236 
237 	if (lanes > 4) {
238 		ERROR("%s: FDI/PIPE illegal number of lanes set.\n", __func__);
239 		return B_ERROR;
240 	}
241 	TRACE("%s: FDI/PIPE link with %" B_PRIx32 " lane(s) in use\n", __func__, lanes);
242 
243 	//Setup Data M/N
244 	uint64 linkspeed = lanes * linkBandwidth * 8;
245 	uint64 ret_n = 1;
246 	while(ret_n < linkspeed) {
247 		ret_n *= 2;
248 	}
249 	if (ret_n > 0x800000) {
250 		ret_n = 0x800000;
251 	}
252 	uint64 ret_m = timing.pixel_clock * ret_n * bitsPerPixel / linkspeed;
253 	while ((ret_n > 0xffffff) || (ret_m > 0xffffff)) {
254 		ret_m >>= 1;
255 		ret_n >>= 1;
256 	}
257 	//Set TU size bits (to default, max) before link training so that error detection works
258 	write32(PCH_FDI_PIPE_A_DATA_M1 + fPipeOffset, ret_m | FDI_PIPE_MN_TU_SIZE_MASK);
259 	write32(PCH_FDI_PIPE_A_DATA_N1 + fPipeOffset, ret_n);
260 
261 	//Setup Link M/N
262 	linkspeed = linkBandwidth;
263 	ret_n = 1;
264 	while(ret_n < linkspeed) {
265 		ret_n *= 2;
266 	}
267 	if (ret_n > 0x800000) {
268 		ret_n = 0x800000;
269 	}
270 	ret_m = timing.pixel_clock * ret_n / linkspeed;
271 	while ((ret_n > 0xffffff) || (ret_m > 0xffffff)) {
272 		ret_m >>= 1;
273 		ret_n >>= 1;
274 	}
275 	write32(PCH_FDI_PIPE_A_LINK_M1 + fPipeOffset, ret_m);
276 	//Writing Link N triggers all four registers to be activated also (on next VBlank)
277 	write32(PCH_FDI_PIPE_A_LINK_N1 + fPipeOffset, ret_n);
278 
279 	TRACE("%s: FDI/PIPE M1 data after: 0x%" B_PRIx32 "\n", __func__, read32(PCH_FDI_PIPE_A_DATA_M1 + fPipeOffset));
280 	TRACE("%s: FDI/PIPE N1 data after: 0x%" B_PRIx32 "\n", __func__, read32(PCH_FDI_PIPE_A_DATA_N1 + fPipeOffset));
281 	TRACE("%s: FDI/PIPE M1 link after: 0x%" B_PRIx32 "\n", __func__, read32(PCH_FDI_PIPE_A_LINK_M1 + fPipeOffset));
282 	TRACE("%s: FDI/PIPE N1 link after: 0x%" B_PRIx32 "\n", __func__, read32(PCH_FDI_PIPE_A_LINK_N1 + fPipeOffset));
283 
284 	return B_OK;
285 }
286 
287 
288 void
289 Pipe::ConfigureScalePos(display_mode* target)
290 {
291 	CALLED();
292 
293 	TRACE("%s: fPipeOffset: 0x%" B_PRIxADDR "\n", __func__, fPipeOffset);
294 
295 	if (target == NULL) {
296 		ERROR("%s: Invalid display mode!\n", __func__);
297 		return;
298 	}
299 
300 	if (gInfo->shared_info->device_type.Generation() < 6) {
301 		// FIXME check on which generations this register exists
302 		// (it appears it would be available only for cursor planes, not
303 		// display planes)
304 		// Since we set the plane to be the same size as the display, we can
305 		// just show it starting at top-left.
306 		write32(INTEL_DISPLAY_A_POS + fPipeOffset, 0);
307 	}
308 
309 	// The only thing that really matters: set the image size and let the
310 	// panel fitter or the transcoder worry about the rest
311 	write32(INTEL_DISPLAY_A_PIPE_SIZE + fPipeOffset,
312 		((uint32)(target->timing.h_display - 1) << 16)
313 			| ((uint32)target->timing.v_display - 1));
314 
315 	// Set the plane size as well while we're at it (this is independant, we
316 	// could have a larger plane and scroll through it).
317 	if ((gInfo->shared_info->device_type.Generation() <= 4)
318 		|| gInfo->shared_info->device_type.HasDDI()) {
319 		// This is "reserved" on G35 and GMA965, but needed on 945 (for which
320 		// there is no public documentation), and I assume earlier devices as
321 		// well.
322 		//
323 		// IMPORTANT WARNING: height and width are swapped when compared to the other registers!
324 		// Be careful when editing this code and don't accidentally swap them!
325 		write32(INTEL_DISPLAY_A_IMAGE_SIZE + fPipeOffset,
326 			((uint32)(target->timing.v_display - 1) << 16)
327 			| ((uint32)target->timing.h_display - 1));
328 	}
329 }
330 
331 
332 void
333 Pipe::ConfigureTimings(display_mode* target, bool hardware, port_index portIndex)
334 {
335 	CALLED();
336 
337 	TRACE("%s(%d): fPipeOffset: 0x%" B_PRIxADDR"\n", __func__, hardware,
338 		fPipeOffset);
339 
340 	if (target == NULL) {
341 		ERROR("%s: Invalid display mode!\n", __func__);
342 		return;
343 	}
344 
345 	/* If using the transcoder, leave the display at its native resolution,
346 	 * and configure only the transcoder (panel fitting will match them
347 	 * together). */
348 	if (!fHasTranscoder || hardware)
349 	{
350 		// update timing (fPipeOffset bumps the DISPLAY_A to B when needed)
351 		// Note: on Skylake below registers are part of the transcoder
352 		write32(INTEL_DISPLAY_A_HTOTAL + fPipeOffset,
353 			((uint32)(target->timing.h_total - 1) << 16)
354 			| ((uint32)target->timing.h_display - 1));
355 		write32(INTEL_DISPLAY_A_HBLANK + fPipeOffset,
356 			((uint32)(target->timing.h_total - 1) << 16)
357 			| ((uint32)target->timing.h_display - 1));
358 		write32(INTEL_DISPLAY_A_HSYNC + fPipeOffset,
359 			((uint32)(target->timing.h_sync_end - 1) << 16)
360 			| ((uint32)target->timing.h_sync_start - 1));
361 
362 		write32(INTEL_DISPLAY_A_VTOTAL + fPipeOffset,
363 			((uint32)(target->timing.v_total - 1) << 16)
364 			| ((uint32)target->timing.v_display - 1));
365 		write32(INTEL_DISPLAY_A_VBLANK + fPipeOffset,
366 			((uint32)(target->timing.v_total - 1) << 16)
367 			| ((uint32)target->timing.v_display - 1));
368 		write32(INTEL_DISPLAY_A_VSYNC + fPipeOffset,
369 			((uint32)(target->timing.v_sync_end - 1) << 16)
370 			| ((uint32)target->timing.v_sync_start - 1));
371 	}
372 
373 	ConfigureScalePos(target);
374 
375 	// transcoder is not applicable if eDP is targeted on Sandy- and IvyBridge
376 	if ((gInfo->shared_info->device_type.InGroup(INTEL_GROUP_SNB) ||
377 		 gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IVB)) &&
378 		(portIndex == INTEL_PORT_A)) {
379 		return;
380 	}
381 
382 	if (fHasTranscoder && hardware) {
383 		_ConfigureTranscoder(target);
384 	}
385 }
386 
387 
388 void
389 Pipe::ConfigureClocks(const pll_divisors& divisors, uint32 pixelClock,
390 	uint32 extraFlags)
391 {
392 	CALLED();
393 
394 	addr_t pllDivisorA = INTEL_DISPLAY_A_PLL_DIVISOR_0;
395 	addr_t pllDivisorB = INTEL_DISPLAY_A_PLL_DIVISOR_1;
396 	addr_t pllControl = INTEL_DISPLAY_A_PLL;
397 	addr_t pllMD = INTEL_DISPLAY_A_PLL_MD;
398 
399 	if (fPipeIndex == INTEL_PIPE_B) {
400 		pllDivisorA = INTEL_DISPLAY_B_PLL_DIVISOR_0;
401 		pllDivisorB = INTEL_DISPLAY_B_PLL_DIVISOR_1;
402 		pllControl = INTEL_DISPLAY_B_PLL;
403 		pllMD = INTEL_DISPLAY_B_PLL_MD;
404 	}
405 
406 	// Disable DPLL first
407 	write32(pllControl, read32(pllControl) & ~DISPLAY_PLL_ENABLED);
408 	spin(150);
409 
410 	float refFreq = gInfo->shared_info->pll_info.reference_frequency / 1000.0f;
411 
412 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_96x)) {
413 		float adjusted = ((refFreq * divisors.m) / divisors.n) / divisors.p;
414 		uint32 pixelMultiply = uint32(adjusted / (pixelClock / 1000.0f));
415 		write32(pllMD, (0 << 24) | ((pixelMultiply - 1) << 8));
416 	}
417 
418 	// XXX: For now we assume no LVDS downclocking and program the same divisor
419 	// value to both divisor 0 (standard) and 1 (reduced divisor)
420 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN)) {
421 		write32(pllDivisorA, (((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT)
422 				& DISPLAY_PLL_IGD_N_DIVISOR_MASK)
423 			| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
424 				& DISPLAY_PLL_IGD_M2_DIVISOR_MASK));
425 		write32(pllDivisorB, (((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT)
426 				& DISPLAY_PLL_IGD_N_DIVISOR_MASK)
427 			| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
428 				& DISPLAY_PLL_IGD_M2_DIVISOR_MASK));
429 	} else {
430 		write32(pllDivisorA, (((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT)
431 				& DISPLAY_PLL_N_DIVISOR_MASK)
432 			| (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT)
433 				& DISPLAY_PLL_M1_DIVISOR_MASK)
434 			| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
435 				& DISPLAY_PLL_M2_DIVISOR_MASK));
436 		write32(pllDivisorB, (((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT)
437 				& DISPLAY_PLL_N_DIVISOR_MASK)
438 			| (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT)
439 				& DISPLAY_PLL_M1_DIVISOR_MASK)
440 			| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
441 				& DISPLAY_PLL_M2_DIVISOR_MASK));
442 	}
443 
444 	//note: bit DISPLAY_PLL_NO_VGA_CONTROL does not exist on IvyBridge and should be left
445 	//      zero there. It does not influence it though.
446 	uint32 pll = DISPLAY_PLL_ENABLED | DISPLAY_PLL_NO_VGA_CONTROL | extraFlags;
447 
448 	if (gInfo->shared_info->device_type.Generation() >= 3) {
449 		// p1 divisor << 1 , 1-8
450 		if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN)) {
451 			pll |= ((1 << (divisors.p1 - 1))
452 					<< DISPLAY_PLL_IGD_POST1_DIVISOR_SHIFT)
453 				& DISPLAY_PLL_IGD_POST1_DIVISOR_MASK;
454 		} else {
455 			pll |= ((1 << (divisors.p1 - 1))
456 					<< DISPLAY_PLL_POST1_DIVISOR_SHIFT)
457 				& DISPLAY_PLL_9xx_POST1_DIVISOR_MASK;
458 		//	pll |= ((divisors.p1 - 1) << DISPLAY_PLL_POST1_DIVISOR_SHIFT)
459 		//		& DISPLAY_PLL_9xx_POST1_DIVISOR_MASK;
460 		}
461 
462 		// Also configure the FP0 divisor on SandyBridge
463 		if (gInfo->shared_info->device_type.Generation() == 6) {
464 			pll |= ((1 << (divisors.p1 - 1))
465 					<< DISPLAY_PLL_SNB_FP0_POST1_DIVISOR_SHIFT)
466 				& DISPLAY_PLL_SNB_FP0_POST1_DIVISOR_MASK;
467 		}
468 
469 		if (divisors.p2 == 5 || divisors.p2 == 7)
470 			pll |= DISPLAY_PLL_DIVIDE_HIGH;
471 
472 		if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_96x))
473 			pll |= 6 << DISPLAY_PLL_PULSE_PHASE_SHIFT;
474 	} else {
475 		if (divisors.p2 != 5 && divisors.p2 != 7)
476 			pll |= DISPLAY_PLL_DIVIDE_4X;
477 
478 		pll |= DISPLAY_PLL_2X_CLOCK;
479 
480 		// TODO: Is this supposed to be DISPLAY_PLL_IGD_POST1_DIVISOR_MASK??
481 		if (divisors.p1 > 2) {
482 			pll |= ((divisors.p1 - 2) << DISPLAY_PLL_POST1_DIVISOR_SHIFT)
483 				& DISPLAY_PLL_POST1_DIVISOR_MASK;
484 		} else
485 			pll |= DISPLAY_PLL_POST1_DIVIDE_2;
486 	}
487 
488 	// Configure PLL while -keeping- it disabled
489 	//note: on older chipsets DISPLAY_PLL_NO_VGA_CONTROL probably enables the PLL and locks regs;
490 	//      on newer chipsets DISPLAY_PLL_ENABLED does this.
491 	write32(pllControl, pll & ~DISPLAY_PLL_ENABLED & ~DISPLAY_PLL_NO_VGA_CONTROL);
492 	read32(pllControl);
493 	spin(150);
494 
495 	// enable pre-configured PLL (locks PLL settings directly blocking changes in this write even)
496 	write32(pllControl, pll);
497 	read32(pllControl);
498 
499 	// Allow the PLL to warm up.
500 	spin(150);
501 
502 	if (gInfo->shared_info->device_type.Generation() >= 6) {
503 		// SandyBridge has 3 transcoders, but only 2 PLLs. So there is a new
504 		// register which routes the PLL output to the transcoder that we need
505 		// to configure
506 		uint32 pllSel = read32(SNB_DPLL_SEL);
507 		TRACE("Old PLL selection: 0x%" B_PRIx32 "\n", pllSel);
508 		uint32 shift = 0;
509 		uint32 pllIndex = 0;
510 
511 		// FIXME we assume that pipe A is used with transcoder A, and pipe B
512 		// with transcoder B, that may not always be the case
513 		if (fPipeIndex == INTEL_PIPE_A) {
514 			shift = 0;
515 			pllIndex = 0;
516 			TRACE("Route PLL A to transcoder A\n");
517 		} else if (fPipeIndex == INTEL_PIPE_B) {
518 			shift = 4;
519 			pllIndex = 1;
520 			TRACE("Route PLL B to transcoder B\n");
521 		} else {
522 			ERROR("Attempting to configure PLL for unhandled pipe");
523 			return;
524 		}
525 
526 		// Mask out the previous PLL configuration for this transcoder
527 		pllSel &= ~(0xF << shift);
528 
529 		// Set up the new configuration for this transcoder and enable it
530 		pllSel |= (8 | pllIndex) << shift;
531 
532 		TRACE("New PLL selection: 0x%" B_PRIx32 "\n", pllSel);
533 		write32(SNB_DPLL_SEL, pllSel);
534 	}
535 }
536 
537 void
538 Pipe::ConfigureClocksSKL(const skl_wrpll_params& wrpll_params, uint32 pixelClock,
539 	port_index pllForPort, uint32* pllSel)
540 {
541 	CALLED();
542 
543 	//find our PLL as set by the BIOS
544 	uint32 portSel = read32(SKL_DPLL_CTRL2);
545 	*pllSel = 0xff;
546 	switch (pllForPort) {
547 	case INTEL_PORT_A:
548 		*pllSel = (portSel & 0x0006) >> 1;
549 		break;
550 	case INTEL_PORT_B:
551 		*pllSel = (portSel & 0x0030) >> 4;
552 		break;
553 	case INTEL_PORT_C:
554 		*pllSel = (portSel & 0x0180) >> 7;
555 		break;
556 	case INTEL_PORT_D:
557 		*pllSel = (portSel & 0x0c00) >> 10;
558 		break;
559 	case INTEL_PORT_E:
560 		*pllSel = (portSel & 0x6000) >> 13;
561 		break;
562 	default:
563 		TRACE("No port selected!\n");
564 		return;
565 	}
566 	TRACE("PLL selected is %" B_PRIx32 "\n", *pllSel);
567 
568 	TRACE("Skylake DPLL_CFGCR1 0x%" B_PRIx32 "\n",
569 		read32(SKL_DPLL1_CFGCR1 + (*pllSel - 1) * 8));
570 	TRACE("Skylake DPLL_CFGCR2 0x%" B_PRIx32 "\n",
571 		read32(SKL_DPLL1_CFGCR2 + (*pllSel - 1) * 8));
572 
573 	// only program PLL's that are in non-DP mode (otherwise the linkspeed sets refresh)
574 	portSel = read32(SKL_DPLL_CTRL1);
575 	if ((portSel & (1 << (*pllSel * 6 + 5))) && *pllSel) { // DPLL0 might only know DP mode
576 		// enable pgm on our PLL in case that's currently disabled
577 		write32(SKL_DPLL_CTRL1, portSel | (1 << (*pllSel * 6)));
578 
579 		write32(SKL_DPLL1_CFGCR1 + (*pllSel - 1) * 8,
580 			1 << 31 |
581 			wrpll_params.dco_fraction << 9 |
582 			wrpll_params.dco_integer);
583 		write32(SKL_DPLL1_CFGCR2 + (*pllSel - 1) * 8,
584 			 wrpll_params.qdiv_ratio << 8 |
585 			 wrpll_params.qdiv_mode << 7 |
586 			 wrpll_params.kdiv << 5 |
587 			 wrpll_params.pdiv << 2 |
588 			 wrpll_params.central_freq);
589 		read32(SKL_DPLL1_CFGCR1 + (*pllSel - 1) * 8);
590 		read32(SKL_DPLL1_CFGCR2 + (*pllSel - 1) * 8);
591 
592 		//assuming DPLL0 and 1 are already enabled by the BIOS if in use (LCPLL1,2 regs)
593 
594 		spin(5);
595 		if (read32(SKL_DPLL_STATUS) & (1 << (*pllSel * 8))) {
596 			TRACE("Programmed PLL; PLL is locked\n");
597 		} else {
598 			TRACE("Programmed PLL; PLL did not lock\n");
599 		}
600 		TRACE("Skylake DPLL_CFGCR1 now: 0x%" B_PRIx32 "\n",
601 			read32(SKL_DPLL1_CFGCR1 + (*pllSel - 1) * 8));
602 		TRACE("Skylake DPLL_CFGCR2 now: 0x%" B_PRIx32 "\n",
603 			read32(SKL_DPLL1_CFGCR2 + (*pllSel - 1) * 8));
604 	} else {
605 		TRACE("PLL programming not needed, skipping.\n");
606 	}
607 
608 	TRACE("Skylake DPLL_CTRL1: 0x%" B_PRIx32 "\n", read32(SKL_DPLL_CTRL1));
609 	TRACE("Skylake DPLL_CTRL2: 0x%" B_PRIx32 "\n", read32(SKL_DPLL_CTRL2));
610 	TRACE("Skylake DPLL_STATUS: 0x%" B_PRIx32 "\n", read32(SKL_DPLL_STATUS));
611 }
612 
613 void
614 Pipe::Enable(bool enable)
615 {
616 	CALLED();
617 
618 	addr_t pipeReg = INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset;
619 	addr_t planeReg = INTEL_DISPLAY_A_CONTROL + fPlaneOffset;
620 
621 	// Planes always have to operate on an enabled pipe
622 
623 	if (enable) {
624 		write32(pipeReg, read32(pipeReg) | INTEL_PIPE_ENABLED);
625 		wait_for_vblank();
626 		write32(planeReg, read32(planeReg) | DISPLAY_CONTROL_ENABLED);
627 
628 		//Enable default display main watermarks
629 		if (gInfo->shared_info->pch_info == INTEL_PCH_CPT) {
630 			if (fPipeOffset == 0)
631 				write32(INTEL_DISPLAY_A_PIPE_WATERMARK, 0x0783818);
632 			else
633 				write32(INTEL_DISPLAY_B_PIPE_WATERMARK, 0x0783818);
634 		}
635 	} else {
636 		write32(planeReg, read32(planeReg) & ~DISPLAY_CONTROL_ENABLED);
637 		wait_for_vblank();
638 		//Sandy+: when link training is to be done re-enable this line but otherwise don't touch!
639 		//GMA(Q45): must disable PIPE or DPLL programming fails.
640 		if (gInfo->shared_info->device_type.Generation() <= 5) {
641 			write32(pipeReg, read32(pipeReg) & ~INTEL_PIPE_ENABLED);
642 		}
643 	}
644 
645 	// flush the eventually cached PCI bus writes
646 	read32(INTEL_DISPLAY_A_BASE);
647 }
648