xref: /haiku/src/add-ons/accelerants/intel_extreme/Pipes.cpp (revision 38eb9fb0eb050aefd2ca57bd87210a1cce158aa4)
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 "intel_extreme.h"
13 #include <KernelExport.h>
14 
15 #include <stdlib.h>
16 #include <string.h>
17 
18 #include <new>
19 
20 
21 #define TRACE_PIPE
22 #ifdef TRACE_PIPE
23 extern "C" void _sPrintf(const char* format, ...);
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 	if (pipeIndex == INTEL_PIPE_B) {
72 		fPipeOffset = INTEL_DISPLAY_OFFSET;
73 		fPlaneOffset = INTEL_PLANE_OFFSET;
74 	}
75 
76 	// IvyBridge: Analog + Digital Ports behind FDI (on northbridge)
77 	// Haswell: Only VGA behind FDI (on northbridge)
78 	// SkyLake: FDI gone. No more northbridge video.
79 	if ((gInfo->shared_info->pch_info != INTEL_PCH_NONE) &&
80 		(gInfo->shared_info->device_type.Generation() <= 8)) {
81 		TRACE("%s: Pipe %s routed through FDI\n", __func__,
82 			(pipeIndex == INTEL_PIPE_A) ? "A" : "B");
83 
84 		fHasTranscoder = true;
85 
86 		// Program FDILink if PCH
87 		fFDILink = new(std::nothrow) FDILink(pipeIndex);
88 		// Program gen5(+) style panelfitter as well
89 		fPanelFitter = new(std::nothrow) PanelFitter(pipeIndex);
90 	}
91 
92 	TRACE("Pipe %s. Pipe Base: 0x%" B_PRIxADDR
93 		" Plane Base: 0x% " B_PRIxADDR "\n", (pipeIndex == INTEL_PIPE_A)
94 			? "A" : "B", fPipeOffset, fPlaneOffset);
95 }
96 
97 
98 Pipe::~Pipe()
99 {
100 }
101 
102 
103 bool
104 Pipe::IsEnabled()
105 {
106 	CALLED();
107 
108 	return (read32(INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset)
109 		& INTEL_PIPE_ENABLED) != 0;
110 }
111 
112 
113 void
114 Pipe::Configure(display_mode* mode)
115 {
116 	uint32 pipeControl = read32(INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset);
117 
118 	// TODO: Haswell+ dithering changes.
119 	//if (gInfo->shared_info->device_type.Generation() >= 4) {
120 	//	pipeControl |= (INTEL_PIPE_DITHER_EN | INTEL_PIPE_DITHER_TYPE_SP);
121 
122 	//Link bit depth: this should be globally known per FDI link (i.e. laptop panel 3x6, rest 3x8)
123 	//currently using BIOS preconfigured setup
124 	//pipeControl = (pipeControl & ~INTEL_PIPE_BPC_MASK) | INTEL_PIPE_BPC(INTEL_PIPE_8BPC);
125 
126 	// TODO: CxSR downclocking?
127 
128 	// TODO: Interlaced modes
129 	pipeControl = (pipeControl & ~(0x7 << 21)) | INTEL_PIPE_PROGRESSIVE;
130 
131 	write32(INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset, pipeControl);
132 	read32(INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset);
133 
134 	if (gInfo->shared_info->device_type.Generation() >= 6) {
135 		// According to SandyBridge modesetting sequence, pipe must be enabled
136 		// before PLL are configured.
137 		addr_t pipeReg = INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset;
138 		write32(pipeReg, read32(pipeReg) | INTEL_PIPE_ENABLED);
139 	}
140 }
141 
142 
143 void
144 Pipe::_ConfigureTranscoder(display_mode* target)
145 {
146 	CALLED();
147 
148 	TRACE("%s: fPipeOffset: 0x%" B_PRIx32"\n", __func__, fPipeOffset);
149 
150 	// update timing (fPipeOffset bumps the DISPLAY_A to B when needed)
151 	write32(INTEL_TRANSCODER_A_HTOTAL + fPipeOffset,
152 		((uint32)(target->timing.h_total - 1) << 16)
153 		| ((uint32)target->timing.h_display - 1));
154 	write32(INTEL_TRANSCODER_A_HBLANK + fPipeOffset,
155 		((uint32)(target->timing.h_total - 1) << 16)
156 		| ((uint32)target->timing.h_display - 1));
157 	write32(INTEL_TRANSCODER_A_HSYNC + fPipeOffset,
158 		((uint32)(target->timing.h_sync_end - 1) << 16)
159 		| ((uint32)target->timing.h_sync_start - 1));
160 
161 	write32(INTEL_TRANSCODER_A_VTOTAL + fPipeOffset,
162 		((uint32)(target->timing.v_total - 1) << 16)
163 		| ((uint32)target->timing.v_display - 1));
164 	write32(INTEL_TRANSCODER_A_VBLANK + fPipeOffset,
165 		((uint32)(target->timing.v_total - 1) << 16)
166 		| ((uint32)target->timing.v_display - 1));
167 	write32(INTEL_TRANSCODER_A_VSYNC + fPipeOffset,
168 		((uint32)(target->timing.v_sync_end - 1) << 16)
169 		| ((uint32)target->timing.v_sync_start - 1));
170 
171 	#if 0
172 	// XXX: Is it ok to do these on non-digital?
173 	write32(INTEL_TRANSCODER_A_POS + fPipeOffset, 0);
174 	write32(INTEL_TRANSCODER_A_IMAGE_SIZE + fPipeOffset,
175 		((uint32)(target->timing.h_display - 1) << 16)
176 			| ((uint32)target->timing.v_display - 1));
177 	#endif
178 }
179 
180 
181 void
182 Pipe::ConfigureScalePos(display_mode* target)
183 {
184 	CALLED();
185 
186 	TRACE("%s: fPipeOffset: 0x%" B_PRIx32"\n", __func__, fPipeOffset);
187 
188 	if (target == NULL) {
189 		ERROR("%s: Invalid display mode!\n", __func__);
190 		return;
191 	}
192 
193 	if (gInfo->shared_info->device_type.Generation() < 6) {
194 		// FIXME check on which generations this register exists
195 		// (it appears it would be available only for cursor planes, not
196 		// display planes)
197 		// Since we set the plane to be the same size as the display, we can
198 		// just show it starting at top-left.
199 		write32(INTEL_DISPLAY_A_POS + fPipeOffset, 0);
200 	}
201 
202 	// The only thing that really matters: set the image size and let the
203 	// panel fitter or the transcoder worry about the rest
204 	write32(INTEL_DISPLAY_A_PIPE_SIZE + fPipeOffset,
205 		((uint32)(target->timing.h_display - 1) << 16)
206 			| ((uint32)target->timing.v_display - 1));
207 
208 	// Set the plane size as well while we're at it (this is independant, we
209 	// could have a larger plane and scroll through it).
210 	if (gInfo->shared_info->device_type.Generation() <= 4) {
211 		// This is "reserved" on G35 and GMA965, but needed on 945 (for which
212 		// there is no public documentation), and I assume earlier devices as
213 		// well.
214 		//
215 		// IMPORTANT WARNING: height and width are swapped when compared to the other registers!
216 		// Be careful when editing this code and don't accidentally swap them!
217 		write32(INTEL_DISPLAY_A_IMAGE_SIZE + fPipeOffset,
218 			((uint32)(target->timing.v_display - 1) << 16)
219 			| ((uint32)target->timing.h_display - 1));
220 	}
221 }
222 
223 
224 void
225 Pipe::ConfigureTimings(display_mode* target, bool hardware)
226 {
227 	CALLED();
228 
229 	TRACE("%s(%d): fPipeOffset: 0x%" B_PRIx32"\n", __func__, hardware,
230 		fPipeOffset);
231 
232 	if (target == NULL) {
233 		ERROR("%s: Invalid display mode!\n", __func__);
234 		return;
235 	}
236 
237 	/* If using the transcoder, leave the display at its native resolution,
238 	 * and configure only the transcoder (panel fitting will match them
239 	 * together). */
240 	if (!fHasTranscoder || hardware)
241 	{
242 		// update timing (fPipeOffset bumps the DISPLAY_A to B when needed)
243 		write32(INTEL_DISPLAY_A_HTOTAL + fPipeOffset,
244 			((uint32)(target->timing.h_total - 1) << 16)
245 			| ((uint32)target->timing.h_display - 1));
246 		write32(INTEL_DISPLAY_A_HBLANK + fPipeOffset,
247 			((uint32)(target->timing.h_total - 1) << 16)
248 			| ((uint32)target->timing.h_display - 1));
249 		write32(INTEL_DISPLAY_A_HSYNC + fPipeOffset,
250 			((uint32)(target->timing.h_sync_end - 1) << 16)
251 			| ((uint32)target->timing.h_sync_start - 1));
252 
253 		write32(INTEL_DISPLAY_A_VTOTAL + fPipeOffset,
254 			((uint32)(target->timing.v_total - 1) << 16)
255 			| ((uint32)target->timing.v_display - 1));
256 		write32(INTEL_DISPLAY_A_VBLANK + fPipeOffset,
257 			((uint32)(target->timing.v_total - 1) << 16)
258 			| ((uint32)target->timing.v_display - 1));
259 		write32(INTEL_DISPLAY_A_VSYNC + fPipeOffset,
260 			((uint32)(target->timing.v_sync_end - 1) << 16)
261 			| ((uint32)target->timing.v_sync_start - 1));
262 	}
263 
264 	ConfigureScalePos(target);
265 
266 	if (fHasTranscoder && hardware) {
267 		_ConfigureTranscoder(target);
268 	}
269 }
270 
271 
272 void
273 Pipe::ConfigureClocks(const pll_divisors& divisors, uint32 pixelClock,
274 	uint32 extraFlags)
275 {
276 	CALLED();
277 
278 	addr_t pllDivisorA = INTEL_DISPLAY_A_PLL_DIVISOR_0;
279 	addr_t pllDivisorB = INTEL_DISPLAY_A_PLL_DIVISOR_1;
280 	addr_t pllControl = INTEL_DISPLAY_A_PLL;
281 	addr_t pllMD = INTEL_DISPLAY_A_PLL_MD;
282 
283 	if (fPipeIndex == INTEL_PIPE_B) {
284 		pllDivisorA = INTEL_DISPLAY_B_PLL_DIVISOR_0;
285 		pllDivisorB = INTEL_DISPLAY_B_PLL_DIVISOR_1;
286 		pllControl = INTEL_DISPLAY_B_PLL;
287 		pllMD = INTEL_DISPLAY_B_PLL_MD;
288 	}
289 
290 	// Disable DPLL first
291 	write32(pllControl, read32(pllControl) & ~DISPLAY_PLL_ENABLED);
292 	spin(150);
293 
294 	float refFreq = gInfo->shared_info->pll_info.reference_frequency / 1000.0f;
295 
296 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_96x)) {
297 		float adjusted = ((refFreq * divisors.m) / divisors.n) / divisors.p;
298 		uint32 pixelMultiply = uint32(adjusted / (pixelClock / 1000.0f));
299 		write32(pllMD, (0 << 24) | ((pixelMultiply - 1) << 8));
300 	}
301 
302 	// XXX: For now we assume no LVDS downclocking and program the same divisor
303 	// value to both divisor 0 (standard) and 1 (reduced divisor)
304 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN)) {
305 		write32(pllDivisorA, (((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT)
306 				& DISPLAY_PLL_IGD_N_DIVISOR_MASK)
307 			| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
308 				& DISPLAY_PLL_IGD_M2_DIVISOR_MASK));
309 		write32(pllDivisorB, (((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT)
310 				& DISPLAY_PLL_IGD_N_DIVISOR_MASK)
311 			| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
312 				& DISPLAY_PLL_IGD_M2_DIVISOR_MASK));
313 	} else {
314 		write32(pllDivisorA, (((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT)
315 				& DISPLAY_PLL_N_DIVISOR_MASK)
316 			| (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT)
317 				& DISPLAY_PLL_M1_DIVISOR_MASK)
318 			| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
319 				& DISPLAY_PLL_M2_DIVISOR_MASK));
320 		write32(pllDivisorB, (((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT)
321 				& DISPLAY_PLL_N_DIVISOR_MASK)
322 			| (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT)
323 				& DISPLAY_PLL_M1_DIVISOR_MASK)
324 			| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
325 				& DISPLAY_PLL_M2_DIVISOR_MASK));
326 	}
327 
328 	//note: bit DISPLAY_PLL_NO_VGA_CONTROL does not exist on IvyBridge and should be left
329 	//      zero there. It does not influence it though.
330 	uint32 pll = DISPLAY_PLL_ENABLED | DISPLAY_PLL_NO_VGA_CONTROL | extraFlags;
331 
332 	if (gInfo->shared_info->device_type.Generation() >= 3) {
333 		// p1 divisor << 1 , 1-8
334 		if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN)) {
335 			pll |= ((1 << (divisors.p1 - 1))
336 					<< DISPLAY_PLL_IGD_POST1_DIVISOR_SHIFT)
337 				& DISPLAY_PLL_IGD_POST1_DIVISOR_MASK;
338 		} else {
339 			pll |= ((1 << (divisors.p1 - 1))
340 					<< DISPLAY_PLL_POST1_DIVISOR_SHIFT)
341 				& DISPLAY_PLL_9xx_POST1_DIVISOR_MASK;
342 		//	pll |= ((divisors.p1 - 1) << DISPLAY_PLL_POST1_DIVISOR_SHIFT)
343 		//		& DISPLAY_PLL_9xx_POST1_DIVISOR_MASK;
344 		}
345 
346 		// Also configure the FP0 divisor on SandyBridge
347 		if (gInfo->shared_info->device_type.Generation() == 6) {
348 			pll |= ((1 << (divisors.p1 - 1))
349 					<< DISPLAY_PLL_SNB_FP0_POST1_DIVISOR_SHIFT)
350 				& DISPLAY_PLL_SNB_FP0_POST1_DIVISOR_MASK;
351 		}
352 
353 		if (divisors.p2 == 5 || divisors.p2 == 7)
354 			pll |= DISPLAY_PLL_DIVIDE_HIGH;
355 
356 		if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_96x))
357 			pll |= 6 << DISPLAY_PLL_PULSE_PHASE_SHIFT;
358 	} else {
359 		if (divisors.p2 != 5 && divisors.p2 != 7)
360 			pll |= DISPLAY_PLL_DIVIDE_4X;
361 
362 		pll |= DISPLAY_PLL_2X_CLOCK;
363 
364 		// TODO: Is this supposed to be DISPLAY_PLL_IGD_POST1_DIVISOR_MASK??
365 		if (divisors.p1 > 2) {
366 			pll |= ((divisors.p1 - 2) << DISPLAY_PLL_POST1_DIVISOR_SHIFT)
367 				& DISPLAY_PLL_POST1_DIVISOR_MASK;
368 		} else
369 			pll |= DISPLAY_PLL_POST1_DIVIDE_2;
370 	}
371 
372 	// Configure PLL while -keeping- it disabled
373 	//note: on older chipsets DISPLAY_PLL_NO_VGA_CONTROL probably enables the PLL and locks regs;
374 	//      on newer chipsets DISPLAY_PLL_ENABLED does this.
375 	write32(pllControl, pll & ~DISPLAY_PLL_ENABLED & ~DISPLAY_PLL_NO_VGA_CONTROL);
376 	read32(pllControl);
377 	spin(150);
378 
379 	// enable pre-configured PLL (locks PLL settings directly blocking changes in this write even)
380 	write32(pllControl, pll);
381 	read32(pllControl);
382 
383 	// Allow the PLL to warm up.
384 	spin(150);
385 
386 	if (gInfo->shared_info->device_type.Generation() >= 6) {
387 		// SandyBridge has 3 transcoders, but only 2 PLLs. So there is a new
388 		// register which routes the PLL output to the transcoder that we need
389 		// to configure
390 		uint32 pllSel = read32(SNB_DPLL_SEL);
391 		TRACE("Old PLL selection: 0x%" B_PRIx32 "\n", pllSel);
392 		uint32 shift = 0;
393 		uint32 pllIndex = 0;
394 
395 		// FIXME we assume that pipe A is used with transcoder A, and pipe B
396 		// with transcoder B, that may not always be the case
397 		if (fPipeIndex == INTEL_PIPE_A) {
398 			shift = 0;
399 			pllIndex = 0;
400 			TRACE("Route PLL A to transcoder A\n");
401 		} else if (fPipeIndex == INTEL_PIPE_B) {
402 			shift = 4;
403 			pllIndex = 1;
404 			TRACE("Route PLL B to transcoder B\n");
405 		} else {
406 			ERROR("Attempting to configure PLL for unhandled pipe");
407 			return;
408 		}
409 
410 		// Mask out the previous PLL configuration for this transcoder
411 		pllSel &= ~(0xF << shift);
412 
413 		// Set up the new configuration for this transcoder and enable it
414 		pllSel |= (8 | pllIndex) << shift;
415 
416 		TRACE("New PLL selection: 0x%" B_PRIx32 "\n", pllSel);
417 		write32(SNB_DPLL_SEL, pllSel);
418 	}
419 }
420 
421 void
422 Pipe::Enable(bool enable)
423 {
424 	CALLED();
425 
426 	addr_t pipeReg = INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset;
427 	addr_t planeReg = INTEL_DISPLAY_A_CONTROL + fPlaneOffset;
428 
429 	// Planes always have to operate on an enabled pipe
430 
431 	if (enable) {
432 		write32(pipeReg, read32(pipeReg) | INTEL_PIPE_ENABLED);
433 		wait_for_vblank();
434 		write32(planeReg, read32(planeReg) | DISPLAY_CONTROL_ENABLED);
435 
436 		//Enable default display main watermarks
437 		if (gInfo->shared_info->pch_info == INTEL_PCH_CPT) {
438 			if (fPipeOffset == 0)
439 				write32(INTEL_DISPLAY_A_PIPE_WATERMARK, 0x0783818);
440 			else
441 				write32(INTEL_DISPLAY_B_PIPE_WATERMARK, 0x0783818);
442 		}
443 	} else {
444 		write32(planeReg, read32(planeReg) & ~DISPLAY_CONTROL_ENABLED);
445 		wait_for_vblank();
446 		//Sandy+: when link training is to be done re-enable this line but otherwise don't touch!
447 		//GMA(Q45): must disable PIPE or DPLL programming fails.
448 		if (gInfo->shared_info->device_type.Generation() <= 5) {
449 			write32(pipeReg, read32(pipeReg) & ~INTEL_PIPE_ENABLED);
450 		}
451 	}
452 
453 	// flush the eventually cached PCI bus writes
454 	read32(INTEL_DISPLAY_A_BASE);
455 }
456