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