xref: /haiku/src/add-ons/accelerants/intel_extreme/Pipes.cpp (revision 385ee03ba83b7a40d315e17b03031b3ca37820c0)
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 	uint32 pipeControl = read32(INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset);
105 
106 	// TODO: Haswell+ dithering changes.
107 	if (gInfo->shared_info->device_type.Generation() >= 4) {
108 		pipeControl |= (INTEL_PIPE_DITHER_EN | INTEL_PIPE_DITHER_TYPE_SP);
109 		switch (mode->space) {
110 			case B_CMAP8:
111 			case B_RGB15_LITTLE:
112 			case B_RGB16_LITTLE:
113 				pipeControl |= INTEL_PIPE_6BPC;
114 				break;
115 			case B_RGB24_LITTLE:
116 				pipeControl |= INTEL_PIPE_8BPC;
117 				break;
118 			case B_RGB32_LITTLE:
119 			default:
120 				pipeControl |= INTEL_PIPE_10BPC;
121 				break;
122 		}
123 	}
124 
125 	// TODO: CxSR downclocking?
126 
127 	// TODO: Interlaced modes
128 	pipeControl |= INTEL_PIPE_PROGRESSIVE;
129 
130 	write32(INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset, pipeControl);
131 	read32(INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset);
132 }
133 
134 
135 void
136 Pipe::_ConfigureTranscoder(display_mode* target)
137 {
138 	CALLED();
139 
140 	TRACE("%s: fPipeOffset: 0x%" B_PRIx32"\n", __func__, fPipeOffset);
141 
142 	// update timing (fPipeOffset bumps the DISPLAY_A to B when needed)
143 	write32(INTEL_TRANSCODER_A_HTOTAL + fPipeOffset,
144 		((uint32)(target->timing.h_total - 1) << 16)
145 		| ((uint32)target->timing.h_display - 1));
146 	write32(INTEL_TRANSCODER_A_HBLANK + fPipeOffset,
147 		((uint32)(target->timing.h_total - 1) << 16)
148 		| ((uint32)target->timing.h_display - 1));
149 	write32(INTEL_TRANSCODER_A_HSYNC + fPipeOffset,
150 		((uint32)(target->timing.h_sync_end - 1) << 16)
151 		| ((uint32)target->timing.h_sync_start - 1));
152 
153 	write32(INTEL_TRANSCODER_A_VTOTAL + fPipeOffset,
154 		((uint32)(target->timing.v_total - 1) << 16)
155 		| ((uint32)target->timing.v_display - 1));
156 	write32(INTEL_TRANSCODER_A_VBLANK + fPipeOffset,
157 		((uint32)(target->timing.v_total - 1) << 16)
158 		| ((uint32)target->timing.v_display - 1));
159 	write32(INTEL_TRANSCODER_A_VSYNC + fPipeOffset,
160 		((uint32)(target->timing.v_sync_end - 1) << 16)
161 		| ((uint32)target->timing.v_sync_start - 1));
162 
163 	#if 0
164 	// XXX: Is it ok to do these on non-digital?
165 	write32(INTEL_TRANSCODER_A_POS + fPipeOffset, 0);
166 	write32(INTEL_TRANSCODER_A_IMAGE_SIZE + fPipeOffset,
167 		((uint32)(target->virtual_width - 1) << 16)
168 			| ((uint32)target->virtual_height - 1));
169 	#endif
170 }
171 
172 
173 void
174 Pipe::ConfigureTimings(display_mode* target)
175 {
176 	CALLED();
177 
178 	TRACE("%s: fPipeOffset: 0x%" B_PRIx32"\n", __func__, fPipeOffset);
179 
180 	if (target == NULL) {
181 		ERROR("%s: Invalid display mode!\n", __func__);
182 		return;
183 	}
184 
185 	// update timing (fPipeOffset bumps the DISPLAY_A to B when needed)
186 	write32(INTEL_DISPLAY_A_HTOTAL + fPipeOffset,
187 		((uint32)(target->timing.h_total - 1) << 16)
188 		| ((uint32)target->timing.h_display - 1));
189 	write32(INTEL_DISPLAY_A_HBLANK + fPipeOffset,
190 		((uint32)(target->timing.h_total - 1) << 16)
191 		| ((uint32)target->timing.h_display - 1));
192 	write32(INTEL_DISPLAY_A_HSYNC + fPipeOffset,
193 		((uint32)(target->timing.h_sync_end - 1) << 16)
194 		| ((uint32)target->timing.h_sync_start - 1));
195 
196 	write32(INTEL_DISPLAY_A_VTOTAL + fPipeOffset,
197 		((uint32)(target->timing.v_total - 1) << 16)
198 		| ((uint32)target->timing.v_display - 1));
199 	write32(INTEL_DISPLAY_A_VBLANK + fPipeOffset,
200 		((uint32)(target->timing.v_total - 1) << 16)
201 		| ((uint32)target->timing.v_display - 1));
202 	write32(INTEL_DISPLAY_A_VSYNC + fPipeOffset,
203 		((uint32)(target->timing.v_sync_end - 1) << 16)
204 		| ((uint32)target->timing.v_sync_start - 1));
205 
206 	// XXX: Is it ok to do these on non-digital?
207 
208 	write32(INTEL_DISPLAY_A_POS + fPipeOffset, 0);
209 	write32(INTEL_DISPLAY_A_IMAGE_SIZE + fPipeOffset,
210 		((uint32)(target->virtual_width - 1) << 16)
211 			| ((uint32)target->virtual_height - 1));
212 
213 	write32(INTEL_DISPLAY_A_PIPE_SIZE + fPipeOffset,
214 		((uint32)(target->timing.v_display - 1) << 16)
215 			| ((uint32)target->timing.h_display - 1));
216 
217 	// This is useful for debugging: it sets the border to red, so you
218 	// can see what is border and what is porch (black area around the
219 	// sync)
220 	//write32(INTEL_DISPLAY_A_RED + fPipeOffset, 0x00FF0000);
221 
222 	if (fHasTranscoder)
223 		_ConfigureTranscoder(target);
224 }
225 
226 
227 void
228 Pipe::ConfigureClocks(const pll_divisors& divisors, uint32 pixelClock,
229 	uint32 extraFlags)
230 {
231 	CALLED();
232 
233 	addr_t pllDivisorA = INTEL_DISPLAY_A_PLL_DIVISOR_0;
234 	addr_t pllDivisorB = INTEL_DISPLAY_A_PLL_DIVISOR_1;
235 	addr_t pllControl = INTEL_DISPLAY_A_PLL;
236 	addr_t pllMD = INTEL_DISPLAY_A_PLL_MD;
237 
238 	if (fPipeIndex == INTEL_PIPE_B) {
239 		pllDivisorA = INTEL_DISPLAY_B_PLL_DIVISOR_0;
240 		pllDivisorB = INTEL_DISPLAY_B_PLL_DIVISOR_1;
241 		pllControl = INTEL_DISPLAY_B_PLL;
242 		pllMD = INTEL_DISPLAY_B_PLL_MD;
243 	}
244 
245 	float refFreq = gInfo->shared_info->pll_info.reference_frequency / 1000.0f;
246 
247 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_96x)) {
248 		float adjusted = ((refFreq * divisors.m) / divisors.n) 	/ divisors.p;
249 		uint32 pixelMultiply = uint32(adjusted 	/ (pixelClock / 1000.0f));
250 		write32(pllMD, (0 << 24) | ((pixelMultiply - 1) << 8));
251 	}
252 
253 	// XXX: For now we assume no LVDS downclocking and program the same divisor
254 	// value to both divisor 0 (standard) and 1 (reduced divisor)
255 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN)) {
256 		write32(pllDivisorA, (((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT)
257 				& DISPLAY_PLL_IGD_N_DIVISOR_MASK)
258 			| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
259 				& DISPLAY_PLL_IGD_M2_DIVISOR_MASK));
260 		write32(pllDivisorB, (((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT)
261 				& DISPLAY_PLL_IGD_N_DIVISOR_MASK)
262 			| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
263 				& DISPLAY_PLL_IGD_M2_DIVISOR_MASK));
264 	} else {
265 		write32(pllDivisorA, (((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT)
266 				& DISPLAY_PLL_N_DIVISOR_MASK)
267 			| (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT)
268 				& DISPLAY_PLL_M1_DIVISOR_MASK)
269 			| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
270 				& DISPLAY_PLL_M2_DIVISOR_MASK));
271 		write32(pllDivisorB, (((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT)
272 				& DISPLAY_PLL_N_DIVISOR_MASK)
273 			| (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT)
274 				& DISPLAY_PLL_M1_DIVISOR_MASK)
275 			| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
276 				& DISPLAY_PLL_M2_DIVISOR_MASK));
277 	}
278 
279 	uint32 pll = DISPLAY_PLL_ENABLED | DISPLAY_PLL_NO_VGA_CONTROL | extraFlags;
280 
281 	if (gInfo->shared_info->device_type.Generation() >= 3) {
282 		// p1 divisor << 1 , 1-8
283 		if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN)) {
284 			pll |= ((1 << (divisors.p1 - 1))
285 					<< DISPLAY_PLL_IGD_POST1_DIVISOR_SHIFT)
286 				& DISPLAY_PLL_IGD_POST1_DIVISOR_MASK;
287 		} else {
288 			pll |= ((1 << (divisors.p1 - 1))
289 					<< DISPLAY_PLL_POST1_DIVISOR_SHIFT)
290 				& DISPLAY_PLL_9xx_POST1_DIVISOR_MASK;
291 		//	pll |= ((divisors.p1 - 1) << DISPLAY_PLL_POST1_DIVISOR_SHIFT)
292 		//		& DISPLAY_PLL_9xx_POST1_DIVISOR_MASK;
293 		}
294 
295 		if (divisors.p2 == 5 || divisors.p2 == 7)
296 			pll |= DISPLAY_PLL_DIVIDE_HIGH;
297 
298 		if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_96x))
299 			pll |= 6 << DISPLAY_PLL_PULSE_PHASE_SHIFT;
300 	} else {
301 		if (divisors.p2 != 5 && divisors.p2 != 7)
302 			pll |= DISPLAY_PLL_DIVIDE_4X;
303 
304 		pll |= DISPLAY_PLL_2X_CLOCK;
305 
306 		// TODO: Is this supposed to be DISPLAY_PLL_IGD_POST1_DIVISOR_MASK??
307 		if (divisors.p1 > 2) {
308 			pll |= ((divisors.p1 - 2) << DISPLAY_PLL_POST1_DIVISOR_SHIFT)
309 				& DISPLAY_PLL_POST1_DIVISOR_MASK;
310 		} else
311 			pll |= DISPLAY_PLL_POST1_DIVIDE_2;
312 	}
313 
314 	// Allow the PLL to warm up by masking its bit.
315 	write32(pllControl, pll & ~DISPLAY_PLL_NO_VGA_CONTROL);
316 	read32(pllControl);
317 	spin(150);
318 	write32(pllControl, pll);
319 	read32(pllControl);
320 	spin(150);
321 }
322 
323 
324 void
325 Pipe::Enable(bool enable)
326 {
327 	CALLED();
328 
329 	addr_t pipeReg = INTEL_DISPLAY_A_PIPE_CONTROL + fPipeOffset;
330 	addr_t planeReg = INTEL_DISPLAY_A_CONTROL + fPlaneOffset;
331 
332 	// Planes always have to operate on an enabled pipe
333 
334 	if (enable) {
335 		write32(pipeReg, read32(pipeReg) | INTEL_PIPE_ENABLED);
336 		wait_for_vblank();
337 		write32(planeReg, read32(planeReg) | DISPLAY_CONTROL_ENABLED);
338 	} else {
339 		write32(planeReg, read32(planeReg) & ~DISPLAY_CONTROL_ENABLED);
340 		wait_for_vblank();
341 		write32(pipeReg, read32(pipeReg) & ~INTEL_PIPE_ENABLED);
342 	}
343 
344 	read32(INTEL_DISPLAY_A_BASE);
345 		// flush the eventually cached PCI bus writes
346 }
347