xref: /haiku/src/add-ons/accelerants/intel_extreme/Pipes.cpp (revision 21258e2674226d6aa732321b6f8494841895af5f)
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 	}
79 
80 	TRACE("Pipe %s. Pipe Base: 0x%" B_PRIxADDR
81 		" Plane Base: 0x% " B_PRIxADDR "\n", (pipeIndex == INTEL_PIPE_A)
82 			? "A" : "B", fPipeOffset, fPlaneOffset);
83 }
84 
85 
86 Pipe::~Pipe()
87 {
88 }
89 
90 
91 bool
92 Pipe::IsEnabled()
93 {
94 	CALLED();
95 
96 	return (read32(INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset)
97 		& INTEL_PIPE_ENABLED) != 0;
98 }
99 
100 
101 void
102 Pipe::Configure(display_mode* mode)
103 {
104 #if 0
105 	// FIXME the previous values are never masked out from the
106 	// register, so we just OR things together and hope to fall on a working
107 	// mode. Better do nothing at all for now.
108 	uint32 pipeControl = read32(INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset);
109 
110 	// TODO: Haswell+ dithering changes.
111 	if (gInfo->shared_info->device_type.Generation() >= 4) {
112 		pipeControl |= (INTEL_PIPE_DITHER_EN | INTEL_PIPE_DITHER_TYPE_SP);
113 		// FIXME this makes no sense, if only because B_CMAP8, B_RGB24 and
114 		// B_RGB32 have the same color precision (8bit per component).
115 		// Also because the color mode is a property of the hardware
116 		// (depends on which LVDS panel is used, typically), not the video mode.
117 		switch (mode->space) {
118 			case B_CMAP8:
119 			case B_RGB15_LITTLE:
120 			case B_RGB16_LITTLE:
121 				pipeControl |= INTEL_PIPE_6BPC;
122 				break;
123 			case B_RGB24_LITTLE:
124 				pipeControl |= INTEL_PIPE_8BPC;
125 				break;
126 			case B_RGB32_LITTLE:
127 			default:
128 				pipeControl |= INTEL_PIPE_10BPC;
129 				break;
130 		}
131 	}
132 
133 	// TODO: CxSR downclocking?
134 
135 	// TODO: Interlaced modes
136 	pipeControl |= INTEL_PIPE_PROGRESSIVE;
137 
138 	write32(INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset, pipeControl);
139 	read32(INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset);
140 #endif
141 
142 	if (gInfo->shared_info->device_type.Generation() >= 6) {
143 		// According to SandyBridge modesetting sequence, pipe must be enabled
144 		// before PLL are configured.
145 		addr_t pipeReg = INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset;
146 		write32(pipeReg, read32(pipeReg) | INTEL_PIPE_ENABLED);
147 	}
148 }
149 
150 
151 void
152 Pipe::_ConfigureTranscoder(display_mode* target)
153 {
154 	CALLED();
155 
156 	TRACE("%s: fPipeOffset: 0x%" B_PRIx32"\n", __func__, fPipeOffset);
157 
158 	// update timing (fPipeOffset bumps the DISPLAY_A to B when needed)
159 	write32(INTEL_TRANSCODER_A_HTOTAL + fPipeOffset,
160 		((uint32)(target->timing.h_total - 1) << 16)
161 		| ((uint32)target->timing.h_display - 1));
162 	write32(INTEL_TRANSCODER_A_HBLANK + fPipeOffset,
163 		((uint32)(target->timing.h_total - 1) << 16)
164 		| ((uint32)target->timing.h_display - 1));
165 	write32(INTEL_TRANSCODER_A_HSYNC + fPipeOffset,
166 		((uint32)(target->timing.h_sync_end - 1) << 16)
167 		| ((uint32)target->timing.h_sync_start - 1));
168 
169 	write32(INTEL_TRANSCODER_A_VTOTAL + fPipeOffset,
170 		((uint32)(target->timing.v_total - 1) << 16)
171 		| ((uint32)target->timing.v_display - 1));
172 	write32(INTEL_TRANSCODER_A_VBLANK + fPipeOffset,
173 		((uint32)(target->timing.v_total - 1) << 16)
174 		| ((uint32)target->timing.v_display - 1));
175 	write32(INTEL_TRANSCODER_A_VSYNC + fPipeOffset,
176 		((uint32)(target->timing.v_sync_end - 1) << 16)
177 		| ((uint32)target->timing.v_sync_start - 1));
178 
179 	#if 0
180 	// XXX: Is it ok to do these on non-digital?
181 	write32(INTEL_TRANSCODER_A_POS + fPipeOffset, 0);
182 	write32(INTEL_TRANSCODER_A_IMAGE_SIZE + fPipeOffset,
183 		((uint32)(target->virtual_width - 1) << 16)
184 			| ((uint32)target->virtual_height - 1));
185 	#endif
186 }
187 
188 
189 void
190 Pipe::ConfigureTimings(display_mode* target, bool hardware)
191 {
192 	CALLED();
193 
194 	TRACE("%s(%d): fPipeOffset: 0x%" B_PRIx32"\n", __func__, hardware,
195 		fPipeOffset);
196 
197 	if (target == NULL) {
198 		ERROR("%s: Invalid display mode!\n", __func__);
199 		return;
200 	}
201 
202 	/* If using the transcoder, leave the display at its native resolution,
203 	 * and configure only the transcoder (panel fitting will match them
204 	 * together). */
205 	if (!fHasTranscoder || hardware)
206 	{
207 		// update timing (fPipeOffset bumps the DISPLAY_A to B when needed)
208 		write32(INTEL_DISPLAY_A_HTOTAL + fPipeOffset,
209 			((uint32)(target->timing.h_total - 1) << 16)
210 			| ((uint32)target->timing.h_display - 1));
211 		write32(INTEL_DISPLAY_A_HBLANK + fPipeOffset,
212 			((uint32)(target->timing.h_total - 1) << 16)
213 			| ((uint32)target->timing.h_display - 1));
214 		write32(INTEL_DISPLAY_A_HSYNC + fPipeOffset,
215 			((uint32)(target->timing.h_sync_end - 1) << 16)
216 			| ((uint32)target->timing.h_sync_start - 1));
217 
218 		write32(INTEL_DISPLAY_A_VTOTAL + fPipeOffset,
219 			((uint32)(target->timing.v_total - 1) << 16)
220 			| ((uint32)target->timing.v_display - 1));
221 		write32(INTEL_DISPLAY_A_VBLANK + fPipeOffset,
222 			((uint32)(target->timing.v_total - 1) << 16)
223 			| ((uint32)target->timing.v_display - 1));
224 		write32(INTEL_DISPLAY_A_VSYNC + fPipeOffset,
225 			((uint32)(target->timing.v_sync_end - 1) << 16)
226 			| ((uint32)target->timing.v_sync_start - 1));
227 	}
228 
229 	if (gInfo->shared_info->device_type.Generation() < 6) {
230 		// FIXME check on which generations this register exists
231 		// (it appears it would be available only for cursor planes, not
232 		// display planes)
233 		// Since we set the plane to be the same size as the display, we can
234 		// just show it starting at top-left.
235 		write32(INTEL_DISPLAY_A_POS + fPipeOffset, 0);
236 	}
237 
238 	// The only thing that really matters: set the image size and let the
239 	// panel fitter or the transcoder worry about the rest
240 	write32(INTEL_DISPLAY_A_PIPE_SIZE + fPipeOffset,
241 		((uint32)(target->virtual_width - 1) << 16)
242 			| ((uint32)target->virtual_height - 1));
243 
244 	// Set the plane size as well while we're at it (this is independant, we
245 	// could have a larger plane and scroll through it).
246 	if (gInfo->shared_info->device_type.Generation() <= 4) {
247 		// This is "reserved" on G35 and GMA965, but needed on 945 (for which
248 		// there is no public documentation), and I assume earlier devices as
249 		// well. Note that the height and width are swapped when compared to
250 		// the other registers.
251 		write32(INTEL_DISPLAY_A_IMAGE_SIZE + fPipeOffset,
252 			((uint32)(target->virtual_height - 1) << 16)
253 			| ((uint32)target->virtual_width - 1));
254 	}
255 
256 	if (fHasTranscoder && hardware) {
257 		_ConfigureTranscoder(target);
258 	}
259 }
260 
261 
262 void
263 Pipe::ConfigureClocks(const pll_divisors& divisors, uint32 pixelClock,
264 	uint32 extraFlags)
265 {
266 	CALLED();
267 
268 	addr_t pllDivisorA = INTEL_DISPLAY_A_PLL_DIVISOR_0;
269 	addr_t pllDivisorB = INTEL_DISPLAY_A_PLL_DIVISOR_1;
270 	addr_t pllControl = INTEL_DISPLAY_A_PLL;
271 	addr_t pllMD = INTEL_DISPLAY_A_PLL_MD;
272 
273 	if (fPipeIndex == INTEL_PIPE_B) {
274 		pllDivisorA = INTEL_DISPLAY_B_PLL_DIVISOR_0;
275 		pllDivisorB = INTEL_DISPLAY_B_PLL_DIVISOR_1;
276 		pllControl = INTEL_DISPLAY_B_PLL;
277 		pllMD = INTEL_DISPLAY_B_PLL_MD;
278 	}
279 
280 	float refFreq = gInfo->shared_info->pll_info.reference_frequency / 1000.0f;
281 
282 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_96x)) {
283 		float adjusted = ((refFreq * divisors.m) / divisors.n) / divisors.p;
284 		uint32 pixelMultiply = uint32(adjusted / (pixelClock / 1000.0f));
285 		write32(pllMD, (0 << 24) | ((pixelMultiply - 1) << 8));
286 	}
287 
288 	// XXX: For now we assume no LVDS downclocking and program the same divisor
289 	// value to both divisor 0 (standard) and 1 (reduced divisor)
290 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN)) {
291 		write32(pllDivisorA, (((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT)
292 				& DISPLAY_PLL_IGD_N_DIVISOR_MASK)
293 			| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
294 				& DISPLAY_PLL_IGD_M2_DIVISOR_MASK));
295 		write32(pllDivisorB, (((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT)
296 				& DISPLAY_PLL_IGD_N_DIVISOR_MASK)
297 			| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
298 				& DISPLAY_PLL_IGD_M2_DIVISOR_MASK));
299 	} else {
300 		write32(pllDivisorA, (((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT)
301 				& DISPLAY_PLL_N_DIVISOR_MASK)
302 			| (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT)
303 				& DISPLAY_PLL_M1_DIVISOR_MASK)
304 			| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
305 				& DISPLAY_PLL_M2_DIVISOR_MASK));
306 		write32(pllDivisorB, (((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT)
307 				& DISPLAY_PLL_N_DIVISOR_MASK)
308 			| (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT)
309 				& DISPLAY_PLL_M1_DIVISOR_MASK)
310 			| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
311 				& DISPLAY_PLL_M2_DIVISOR_MASK));
312 	}
313 
314 	uint32 pll = DISPLAY_PLL_ENABLED | DISPLAY_PLL_NO_VGA_CONTROL | extraFlags;
315 
316 	if (gInfo->shared_info->device_type.Generation() >= 3) {
317 		// p1 divisor << 1 , 1-8
318 		if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN)) {
319 			pll |= ((1 << (divisors.p1 - 1))
320 					<< DISPLAY_PLL_IGD_POST1_DIVISOR_SHIFT)
321 				& DISPLAY_PLL_IGD_POST1_DIVISOR_MASK;
322 		} else {
323 			pll |= ((1 << (divisors.p1 - 1))
324 					<< DISPLAY_PLL_POST1_DIVISOR_SHIFT)
325 				& DISPLAY_PLL_9xx_POST1_DIVISOR_MASK;
326 		//	pll |= ((divisors.p1 - 1) << DISPLAY_PLL_POST1_DIVISOR_SHIFT)
327 		//		& DISPLAY_PLL_9xx_POST1_DIVISOR_MASK;
328 		}
329 
330 		// Also configure the FP0 divisor on SandyBridge
331 		if (gInfo->shared_info->device_type.Generation() == 6) {
332 			pll |= ((1 << (divisors.p1 - 1))
333 					<< DISPLAY_PLL_SNB_FP0_POST1_DIVISOR_SHIFT)
334 				& DISPLAY_PLL_SNB_FP0_POST1_DIVISOR_MASK;
335 		}
336 
337 		if (divisors.p2 == 5 || divisors.p2 == 7)
338 			pll |= DISPLAY_PLL_DIVIDE_HIGH;
339 
340 		if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_96x))
341 			pll |= 6 << DISPLAY_PLL_PULSE_PHASE_SHIFT;
342 	} else {
343 		if (divisors.p2 != 5 && divisors.p2 != 7)
344 			pll |= DISPLAY_PLL_DIVIDE_4X;
345 
346 		pll |= DISPLAY_PLL_2X_CLOCK;
347 
348 		// TODO: Is this supposed to be DISPLAY_PLL_IGD_POST1_DIVISOR_MASK??
349 		if (divisors.p1 > 2) {
350 			pll |= ((divisors.p1 - 2) << DISPLAY_PLL_POST1_DIVISOR_SHIFT)
351 				& DISPLAY_PLL_POST1_DIVISOR_MASK;
352 		} else
353 			pll |= DISPLAY_PLL_POST1_DIVIDE_2;
354 	}
355 
356 	write32(pllControl, pll & ~DISPLAY_PLL_NO_VGA_CONTROL);
357 		// FIXME what is this doing? Why put the PLL back under VGA_CONTROL
358 		// here?
359 	read32(pllControl);
360 	spin(150);
361 
362 	// Configure and enable the PLL
363 	write32(pllControl, pll);
364 	read32(pllControl);
365 
366 	// Allow the PLL to warm up.
367 	spin(150);
368 
369 	if (gInfo->shared_info->device_type.Generation() >= 6) {
370 		// SandyBridge has 3 transcoders, but only 2 PLLs. So there is a new
371 		// register which routes the PLL output to the transcoder that we need
372 		// to configure
373 		uint32 pllSel = read32(SNB_DPLL_SEL);
374 		TRACE("Old PLL selection: %x\n", pllSel);
375 		uint32 shift = 0;
376 		uint32 pllIndex = 0;
377 
378 		// FIXME we assume that pipe A is used with transcoder A, and pipe B
379 		// with transcoder B, that may not always be the case
380 		if (fPipeIndex == INTEL_PIPE_A) {
381 			shift = 0;
382 			pllIndex = 0;
383 			TRACE("Route PLL A to transcoder A\n");
384 		} else if (fPipeIndex == INTEL_PIPE_B) {
385 			shift = 4;
386 			pllIndex = 1;
387 			TRACE("Route PLL B to transcoder B\n");
388 		} else {
389 			ERROR("Attempting to configure PLL for unhandled pipe");
390 			return;
391 		}
392 
393 		// Mask out the previous PLL configuration for this transcoder
394 		pllSel &= ~(0xF << shift);
395 
396 		// Set up the new configuration for this transcoder and enable it
397 		pllSel |= (8 | pllIndex) << shift;
398 
399 		TRACE("New PLL selection: %x\n", pllSel);
400 		write32(SNB_DPLL_SEL, pllSel);
401 	}
402 }
403 
404 
405 void
406 Pipe::Enable(bool enable)
407 {
408 	CALLED();
409 
410 	addr_t pipeReg = INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset;
411 	addr_t planeReg = INTEL_DISPLAY_A_CONTROL + fPlaneOffset;
412 
413 	// Planes always have to operate on an enabled pipe
414 
415 	if (enable) {
416 		write32(pipeReg, read32(pipeReg) | INTEL_PIPE_ENABLED);
417 		wait_for_vblank();
418 		write32(planeReg, read32(planeReg) | DISPLAY_CONTROL_ENABLED);
419 	} else {
420 		write32(planeReg, read32(planeReg) & ~DISPLAY_CONTROL_ENABLED);
421 		wait_for_vblank();
422 		write32(pipeReg, read32(pipeReg) & ~INTEL_PIPE_ENABLED);
423 	}
424 
425 	read32(INTEL_DISPLAY_A_BASE);
426 		// flush the eventually cached PCI bus writes
427 }
428