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