xref: /haiku/src/add-ons/accelerants/intel_extreme/mode.cpp (revision f648efd3d411e5d5590b586091e5cb75da896cfd)
1 /*
2  * Copyright 2006-2014, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Support for i915 chipset and up based on the X driver,
6  * Copyright 2006-2007 Intel Corporation.
7  *
8  * Authors:
9  *		Axel Dörfler, axeld@pinc-software.de
10  */
11 
12 
13 #include "accelerant_protos.h"
14 #include "accelerant.h"
15 #include "utility.h"
16 
17 #include <Debug.h>
18 #include <stdio.h>
19 #include <string.h>
20 #include <math.h>
21 
22 #include <create_display_modes.h>
23 #include <ddc.h>
24 #include <edid.h>
25 #include <validate_display_mode.h>
26 
27 
28 #undef TRACE
29 #define TRACE_MODE
30 #ifdef TRACE_MODE
31 #	define TRACE(x...) _sPrintf("intel_extreme accelerant:" x)
32 #else
33 #	define TRACE(x...)
34 #endif
35 
36 #define ERROR(x...) _sPrintf("intel_extreme accelerant: " x)
37 #define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
38 
39 
40 struct display_registers {
41 	uint32	pll;
42 	uint32	divisors;
43 	uint32	control;
44 	uint32	pipe_config;
45 	uint32	horiz_total;
46 	uint32	horiz_blank;
47 	uint32	horiz_sync;
48 	uint32	vert_total;
49 	uint32	vert_blank;
50 	uint32	vert_sync;
51 	uint32	size;
52 	uint32	stride;
53 	uint32	position;
54 	uint32	pipe_source;
55 };
56 
57 struct pll_divisors {
58 	uint32	post;
59 	uint32	post1;
60 	uint32	post2;
61 	bool	post2_high;
62 	uint32	n;
63 	uint32	m;
64 	uint32	m1;
65 	uint32	m2;
66 };
67 
68 struct pll_limits {
69 	pll_divisors	min;
70 	pll_divisors	max;
71 	uint32			min_post2_frequency;
72 	uint32			min_vco;
73 	uint32			max_vco;
74 };
75 
76 
77 static void mode_fill_missing_bits(display_mode *, uint32);
78 
79 
80 static status_t
81 get_i2c_signals(void* cookie, int* _clock, int* _data)
82 {
83 	uint32 ioRegister = (uint32)(addr_t)cookie;
84 	uint32 value = read32(ioRegister);
85 
86 	*_clock = (value & I2C_CLOCK_VALUE_IN) != 0;
87 	*_data = (value & I2C_DATA_VALUE_IN) != 0;
88 
89 	return B_OK;
90 }
91 
92 
93 static status_t
94 set_i2c_signals(void* cookie, int clock, int data)
95 {
96 	uint32 ioRegister = (uint32)(addr_t)cookie;
97 	uint32 value;
98 
99 	if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_83x)) {
100 		// on these chips, the reserved values are fixed
101 		value = 0;
102 	} else {
103 		// on all others, we have to preserve them manually
104 		value = read32(ioRegister) & I2C_RESERVED;
105 	}
106 
107 	if (data != 0)
108 		value |= I2C_DATA_DIRECTION_MASK;
109 	else {
110 		value |= I2C_DATA_DIRECTION_MASK | I2C_DATA_DIRECTION_OUT
111 			| I2C_DATA_VALUE_MASK;
112 	}
113 
114 	if (clock != 0)
115 		value |= I2C_CLOCK_DIRECTION_MASK;
116 	else {
117 		value |= I2C_CLOCK_DIRECTION_MASK | I2C_CLOCK_DIRECTION_OUT
118 			| I2C_CLOCK_VALUE_MASK;
119 	}
120 
121 	write32(ioRegister, value);
122 	read32(ioRegister);
123 		// make sure the PCI bus has flushed the write
124 
125 	return B_OK;
126 }
127 
128 
129 static void
130 get_pll_limits(pll_limits &limits)
131 {
132 	// Note, the limits are taken from the X driver; they have not yet been
133 	// tested
134 
135 	if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_ILK)
136 		|| gInfo->shared_info->device_type.InGroup(INTEL_TYPE_SNB)
137 		|| gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IVB)) {
138 		// TODO: support LVDS output limits as well
139 		static const pll_limits kLimits = {
140 			// p, p1, p2, high,   n,   m, m1, m2
141 			{  5,  1, 10, false,  1,  79, 12,  5},	// min
142 			{ 80,  8,  5, true,   5, 127, 22,  9},	// max
143 			225000, 1760000, 3510000
144 		};
145 		limits = kLimits;
146 	} else if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_G4x)) {
147 		// TODO: support LVDS output limits as well
148 		static const pll_limits kLimits = {
149 			// p, p1, p2, high,   n,   m, m1, m2
150 			{ 10,  1, 10, false,  1, 104, 17,  5},	// min
151 			{ 30,  3, 10, true,   4, 138, 23, 11},	// max
152 			270000, 1750000, 3500000
153 		};
154 		limits = kLimits;
155 	} else if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) {
156 		// TODO: support LVDS output limits as well
157 		// m1 is reserved and must be 0
158 		static const pll_limits kLimits = {
159 			// p, p1, p2, high,   n,   m, m1,  m2
160 			{  5,  1, 10, false,  3,   2,  0,   2},	// min
161 			{ 80,  8,  5, true,   6, 256,  0, 256},	// max
162 			200000, 1700000, 3500000
163 		};
164 		limits = kLimits;
165 	} else if (gInfo->shared_info->device_type.InFamily(INTEL_TYPE_9xx)) {
166 		// TODO: support LVDS output limits as well
167 		// (Update: Output limits are adjusted in the computation (post2=7/14))
168 		// Should move them here!
169 		static const pll_limits kLimits = {
170 			// p, p1, p2, high,   n,   m, m1, m2
171 			{  5,  1, 10, false,  5,  70, 12,  7},	// min
172 			{ 80,  8,  5, true,  10, 120, 22, 11},	// max
173 			200000, 1400000, 2800000
174 		};
175 		limits = kLimits;
176 	} else {
177 		// TODO: support LVDS output limits as well
178 		static const pll_limits kLimits = {
179 			// p, p1, p2, high,   n,   m, m1, m2
180 			{  4,  2,  4, false,  5,  96, 20,  8},
181 			{128, 33,  2, true,  18, 140, 28, 18},
182 			165000, 930000, 1400000
183 		};
184 		limits = kLimits;
185 	}
186 
187 	TRACE("PLL limits, min: p %lu (p1 %lu, p2 %lu), n %lu, m %lu "
188 		"(m1 %lu, m2 %lu)\n", limits.min.post, limits.min.post1,
189 		limits.min.post2, limits.min.n, limits.min.m, limits.min.m1,
190 		limits.min.m2);
191 	TRACE("PLL limits, max: p %lu (p1 %lu, p2 %lu), n %lu, m %lu "
192 		"(m1 %lu, m2 %lu)\n", limits.max.post, limits.max.post1,
193 		limits.max.post2, limits.max.n, limits.max.m, limits.max.m1,
194 		limits.max.m2);
195 }
196 
197 
198 static bool
199 valid_pll_divisors(const pll_divisors& divisors, const pll_limits& limits)
200 {
201 	pll_info &info = gInfo->shared_info->pll_info;
202 	uint32 vco = info.reference_frequency * divisors.m / divisors.n;
203 	uint32 frequency = vco / divisors.post;
204 
205 	if (divisors.post < limits.min.post || divisors.post > limits.max.post
206 		|| divisors.m < limits.min.m || divisors.m > limits.max.m
207 		|| vco < limits.min_vco || vco > limits.max_vco
208 		|| frequency < info.min_frequency || frequency > info.max_frequency)
209 		return false;
210 
211 	return true;
212 }
213 
214 
215 static void
216 compute_pll_divisors(const display_mode &current, pll_divisors& divisors,
217 	bool isLVDS)
218 {
219 	float requestedPixelClock = current.timing.pixel_clock / 1000.0f;
220 	float referenceClock
221 		= gInfo->shared_info->pll_info.reference_frequency / 1000.0f;
222 	pll_limits limits;
223 	get_pll_limits(limits);
224 
225 	TRACE("%s: required MHz: %g\n", __func__, requestedPixelClock);
226 
227 	if (isLVDS) {
228 		if ((read32(INTEL_DISPLAY_LVDS_PORT) & LVDS_CLKB_POWER_MASK)
229 				== LVDS_CLKB_POWER_UP)
230 			divisors.post2 = LVDS_POST2_RATE_FAST;
231 		else
232 			divisors.post2 = LVDS_POST2_RATE_SLOW;
233 	} else {
234 		if (current.timing.pixel_clock < limits.min_post2_frequency) {
235 			// slow DAC timing
236 			divisors.post2 = limits.min.post2;
237 			divisors.post2_high = limits.min.post2_high;
238 		} else {
239 			// fast DAC timing
240 			divisors.post2 = limits.max.post2;
241 			divisors.post2_high = limits.max.post2_high;
242 		}
243 	}
244 
245 	float best = requestedPixelClock;
246 	pll_divisors bestDivisors;
247 
248 	bool is_igd = gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD);
249 	for (divisors.m1 = limits.min.m1; divisors.m1 <= limits.max.m1;
250 			divisors.m1++) {
251 		for (divisors.m2 = limits.min.m2; divisors.m2 <= limits.max.m2
252 				&& ((divisors.m2 < divisors.m1) || is_igd); divisors.m2++) {
253 			for (divisors.n = limits.min.n; divisors.n <= limits.max.n;
254 					divisors.n++) {
255 				for (divisors.post1 = limits.min.post1;
256 						divisors.post1 <= limits.max.post1; divisors.post1++) {
257 					divisors.m = 5 * divisors.m1 + divisors.m2;
258 					divisors.post = divisors.post1 * divisors.post2;
259 
260 					if (!valid_pll_divisors(divisors, limits))
261 						continue;
262 
263 					float error = fabs(requestedPixelClock
264 						- ((referenceClock * divisors.m) / divisors.n)
265 						/ divisors.post);
266 					if (error < best) {
267 						best = error;
268 						bestDivisors = divisors;
269 
270 						if (error == 0)
271 							break;
272 					}
273 				}
274 			}
275 		}
276 	}
277 
278 	divisors = bestDivisors;
279 
280 	TRACE("%s: found: %g MHz, p = %lu (p1 = %lu, p2 = %lu), n = %lu, m = %lu "
281 		"(m1 = %lu, m2 = %lu)\n", __func__,
282 		((referenceClock * divisors.m) / divisors.n) / divisors.post,
283 		divisors.post, divisors.post1, divisors.post2, divisors.n,
284 		divisors.m, divisors.m1, divisors.m2);
285 }
286 
287 
288 static void
289 mode_fill_missing_bits(display_mode *mode, uint32 cntrl)
290 {
291 	uint32 value = read32(cntrl);
292 
293 	switch (value & DISPLAY_CONTROL_COLOR_MASK) {
294 		case DISPLAY_CONTROL_RGB32:
295 		default:
296 			mode->space = B_RGB32;
297 			break;
298 		case DISPLAY_CONTROL_RGB16:
299 			mode->space = B_RGB16;
300 			break;
301 		case DISPLAY_CONTROL_RGB15:
302 			mode->space = B_RGB15;
303 			break;
304 		case DISPLAY_CONTROL_CMAP8:
305 			mode->space = B_CMAP8;
306 			break;
307 	}
308 
309 	mode->flags = B_8_BIT_DAC | B_HARDWARE_CURSOR | B_PARALLEL_ACCESS | B_DPMS;
310 }
311 
312 
313 static void
314 retrieve_current_mode(display_mode& mode, uint32 pllRegister)
315 {
316 	uint32 pll = read32(pllRegister);
317 	uint32 pllDivisor;
318 	uint32 hTotalRegister;
319 	uint32 vTotalRegister;
320 	uint32 hSyncRegister;
321 	uint32 vSyncRegister;
322 	uint32 imageSizeRegister;
323 	uint32 controlRegister;
324 
325 	if (pllRegister == INTEL_DISPLAY_A_PLL) {
326 		pllDivisor = read32((pll & DISPLAY_PLL_DIVISOR_1) != 0
327 			? INTEL_DISPLAY_A_PLL_DIVISOR_1 : INTEL_DISPLAY_A_PLL_DIVISOR_0);
328 
329 		hTotalRegister = INTEL_DISPLAY_A_HTOTAL;
330 		vTotalRegister = INTEL_DISPLAY_A_VTOTAL;
331 		hSyncRegister = INTEL_DISPLAY_A_HSYNC;
332 		vSyncRegister = INTEL_DISPLAY_A_VSYNC;
333 		imageSizeRegister = INTEL_DISPLAY_A_IMAGE_SIZE;
334 		controlRegister = INTEL_DISPLAY_A_CONTROL;
335 	} else if (pllRegister == INTEL_DISPLAY_B_PLL) {
336 		pllDivisor = read32((pll & DISPLAY_PLL_DIVISOR_1) != 0
337 			? INTEL_DISPLAY_B_PLL_DIVISOR_1 : INTEL_DISPLAY_B_PLL_DIVISOR_0);
338 
339 		hTotalRegister = INTEL_DISPLAY_B_HTOTAL;
340 		vTotalRegister = INTEL_DISPLAY_B_VTOTAL;
341 		hSyncRegister = INTEL_DISPLAY_B_HSYNC;
342 		vSyncRegister = INTEL_DISPLAY_B_VSYNC;
343 		imageSizeRegister = INTEL_DISPLAY_B_IMAGE_SIZE;
344 		controlRegister = INTEL_DISPLAY_B_CONTROL;
345 	} else {
346 		// TODO: not supported
347 		return;
348 	}
349 
350 	pll_divisors divisors;
351 	if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) {
352 		divisors.m1 = 0;
353 		divisors.m2 = (pllDivisor & DISPLAY_PLL_IGD_M2_DIVISOR_MASK)
354 			>> DISPLAY_PLL_M2_DIVISOR_SHIFT;
355 		divisors.n = ((pllDivisor & DISPLAY_PLL_IGD_N_DIVISOR_MASK)
356 			>> DISPLAY_PLL_N_DIVISOR_SHIFT) - 1;
357 	} else {
358 		divisors.m1 = (pllDivisor & DISPLAY_PLL_M1_DIVISOR_MASK)
359 			>> DISPLAY_PLL_M1_DIVISOR_SHIFT;
360 		divisors.m2 = (pllDivisor & DISPLAY_PLL_M2_DIVISOR_MASK)
361 			>> DISPLAY_PLL_M2_DIVISOR_SHIFT;
362 		divisors.n = (pllDivisor & DISPLAY_PLL_N_DIVISOR_MASK)
363 			>> DISPLAY_PLL_N_DIVISOR_SHIFT;
364 	}
365 
366 	pll_limits limits;
367 	get_pll_limits(limits);
368 
369 	if (gInfo->shared_info->device_type.InFamily(INTEL_TYPE_9xx)) {
370 		if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) {
371 			divisors.post1 = (pll & DISPLAY_PLL_IGD_POST1_DIVISOR_MASK)
372 				>> DISPLAY_PLL_IGD_POST1_DIVISOR_SHIFT;
373 		} else {
374 			divisors.post1 = (pll & DISPLAY_PLL_9xx_POST1_DIVISOR_MASK)
375 				>> DISPLAY_PLL_POST1_DIVISOR_SHIFT;
376 		}
377 
378 		if (pllRegister == INTEL_DISPLAY_B_PLL
379 			&& !gInfo->shared_info->device_type.InGroup(INTEL_TYPE_96x)) {
380 			// TODO: Fix this? Need to support dual channel LVDS.
381 			divisors.post2 = LVDS_POST2_RATE_SLOW;
382 		} else {
383 			if ((pll & DISPLAY_PLL_DIVIDE_HIGH) != 0)
384 				divisors.post2 = limits.max.post2;
385 			else
386 				divisors.post2 = limits.min.post2;
387 		}
388 	} else {
389 		// 8xx
390 		divisors.post1 = (pll & DISPLAY_PLL_POST1_DIVISOR_MASK)
391 			>> DISPLAY_PLL_POST1_DIVISOR_SHIFT;
392 
393 		if ((pll & DISPLAY_PLL_DIVIDE_4X) != 0)
394 			divisors.post2 = limits.max.post2;
395 		else
396 			divisors.post2 = limits.min.post2;
397 	}
398 
399 	divisors.m = 5 * divisors.m1 + divisors.m2;
400 	divisors.post = divisors.post1 * divisors.post2;
401 
402 	float referenceClock
403 		= gInfo->shared_info->pll_info.reference_frequency / 1000.0f;
404 	float pixelClock
405 		= ((referenceClock * divisors.m) / divisors.n) / divisors.post;
406 
407 	// timing
408 
409 	mode.timing.pixel_clock = uint32(pixelClock * 1000);
410 	mode.timing.flags = 0;
411 
412 	uint32 value = read32(hTotalRegister);
413 	mode.timing.h_total = (value >> 16) + 1;
414 	mode.timing.h_display = (value & 0xffff) + 1;
415 
416 	value = read32(hSyncRegister);
417 	mode.timing.h_sync_end = (value >> 16) + 1;
418 	mode.timing.h_sync_start = (value & 0xffff) + 1;
419 
420 	value = read32(vTotalRegister);
421 	mode.timing.v_total = (value >> 16) + 1;
422 	mode.timing.v_display = (value & 0xffff) + 1;
423 
424 	value = read32(vSyncRegister);
425 	mode.timing.v_sync_end = (value >> 16) + 1;
426 	mode.timing.v_sync_start = (value & 0xffff) + 1;
427 
428 	// image size and color space
429 
430 	value = read32(imageSizeRegister);
431 	mode.virtual_width = (value >> 16) + 1;
432 	mode.virtual_height = (value & 0xffff) + 1;
433 
434 	// using virtual size based on image size is the 'proper' way to do it,
435 	// however the bios appears to be suggesting scaling or somesuch, so ignore
436 	// the proper virtual dimension for now if they'd suggest a smaller size.
437 	if (mode.virtual_width < mode.timing.h_display)
438 		mode.virtual_width = mode.timing.h_display;
439 	if (mode.virtual_height < mode.timing.v_display)
440 		mode.virtual_height = mode.timing.v_display;
441 
442 	mode_fill_missing_bits(&mode, controlRegister);
443 
444 	mode.h_display_start = 0;
445 	mode.v_display_start = 0;
446 	if (gInfo->overlay_registers != NULL) {
447 		mode.flags |= B_SUPPORTS_OVERLAYS;
448 	}
449 }
450 
451 
452 static void
453 get_color_space_format(const display_mode &mode, uint32 &colorMode,
454 	uint32 &bytesPerRow, uint32 &bitsPerPixel)
455 {
456 	uint32 bytesPerPixel;
457 
458 	switch (mode.space) {
459 		case B_RGB32_LITTLE:
460 			colorMode = DISPLAY_CONTROL_RGB32;
461 			bytesPerPixel = 4;
462 			bitsPerPixel = 32;
463 			break;
464 		case B_RGB16_LITTLE:
465 			colorMode = DISPLAY_CONTROL_RGB16;
466 			bytesPerPixel = 2;
467 			bitsPerPixel = 16;
468 			break;
469 		case B_RGB15_LITTLE:
470 			colorMode = DISPLAY_CONTROL_RGB15;
471 			bytesPerPixel = 2;
472 			bitsPerPixel = 15;
473 			break;
474 		case B_CMAP8:
475 		default:
476 			colorMode = DISPLAY_CONTROL_CMAP8;
477 			bytesPerPixel = 1;
478 			bitsPerPixel = 8;
479 			break;
480 	}
481 
482 	bytesPerRow = mode.virtual_width * bytesPerPixel;
483 
484 	// Make sure bytesPerRow is a multiple of 64
485 	// TODO: check if the older chips have the same restriction!
486 	if ((bytesPerRow & 63) != 0)
487 		bytesPerRow = (bytesPerRow + 63) & ~63;
488 }
489 
490 
491 static bool
492 sanitize_display_mode(display_mode& mode)
493 {
494 	// Some cards only support even pixel counts, while others require an odd
495 	// one.
496 	bool olderCard = gInfo->shared_info->device_type.InGroup(INTEL_TYPE_Gxx);
497 	olderCard |= gInfo->shared_info->device_type.InGroup(INTEL_TYPE_96x);
498 	olderCard |= gInfo->shared_info->device_type.InGroup(INTEL_TYPE_94x);
499 	olderCard |= gInfo->shared_info->device_type.InGroup(INTEL_TYPE_91x);
500 	olderCard |= gInfo->shared_info->device_type.InFamily(INTEL_TYPE_8xx);
501 	olderCard |= gInfo->shared_info->device_type.InFamily(INTEL_TYPE_7xx);
502 
503 	// TODO: verify constraints - these are more or less taken from the
504 	// radeon driver!
505 	display_constraints constraints = {
506 		// resolution
507 		320, 8192, 200, 4096,
508 		// pixel clock
509 		gInfo->shared_info->pll_info.min_frequency,
510 		gInfo->shared_info->pll_info.max_frequency,
511 		// horizontal
512 		{1, 0, 8160, 32, 8192, 0, 8192},
513 		{1, 1, 4092, 2, 63, 1, 4096}
514 	};
515 
516 	if (olderCard)
517 		constraints.horizontal_timing.resolution = 2;
518 
519 	return sanitize_display_mode(mode, constraints,
520 		gInfo->has_edid ? &gInfo->edid_info : NULL);
521 }
522 
523 
524 static bool
525 check_and_sanitize_display_mode(display_mode* mode)
526 {
527 	uint16 width = mode->timing.h_display;
528 	uint16 height = mode->timing.v_display;
529 
530 	// Only accept the mode if it is within the supported resolution
531 	// TODO: sanitize_display_mode() should report resolution changes
532 	// differently!
533 	return !sanitize_display_mode(*mode) || (width == mode->timing.h_display
534 			&& height == mode->timing.v_display);
535 }
536 
537 
538 // #pragma mark -
539 
540 
541 void
542 set_frame_buffer_base()
543 {
544 	intel_shared_info &sharedInfo = *gInfo->shared_info;
545 	display_mode &mode = sharedInfo.current_mode;
546 	uint32 baseRegister;
547 	uint32 surfaceRegister;
548 
549 	if (gInfo->head_mode & HEAD_MODE_B_DIGITAL) {
550 		baseRegister = INTEL_DISPLAY_B_BASE;
551 		surfaceRegister = INTEL_DISPLAY_B_SURFACE;
552 	} else {
553 		baseRegister = INTEL_DISPLAY_A_BASE;
554 		surfaceRegister = INTEL_DISPLAY_A_SURFACE;
555 	}
556 
557 	if (sharedInfo.device_type.InGroup(INTEL_TYPE_96x)
558 		|| sharedInfo.device_type.InGroup(INTEL_TYPE_G4x)
559 		|| sharedInfo.device_type.InGroup(INTEL_TYPE_ILK)
560 		|| sharedInfo.device_type.InGroup(INTEL_TYPE_SNB)
561 		|| sharedInfo.device_type.InGroup(INTEL_TYPE_IVB)) {
562 		write32(baseRegister, mode.v_display_start * sharedInfo.bytes_per_row
563 			+ mode.h_display_start * (sharedInfo.bits_per_pixel + 7) / 8);
564 		read32(baseRegister);
565 		write32(surfaceRegister, sharedInfo.frame_buffer_offset);
566 		read32(surfaceRegister);
567 	} else {
568 		write32(baseRegister, sharedInfo.frame_buffer_offset
569 			+ mode.v_display_start * sharedInfo.bytes_per_row
570 			+ mode.h_display_start * (sharedInfo.bits_per_pixel + 7) / 8);
571 		read32(baseRegister);
572 	}
573 }
574 
575 
576 /*!	Creates the initial mode list of the primary accelerant.
577 	It's called from intel_init_accelerant().
578 */
579 status_t
580 create_mode_list(void)
581 {
582 	i2c_bus bus;
583 	bus.cookie = (void*)(addr_t)INTEL_I2C_IO_A;
584 	bus.set_signals = &set_i2c_signals;
585 	bus.get_signals = &get_i2c_signals;
586 	ddc2_init_timing(&bus);
587 
588 	status_t error = ddc2_read_edid1(&bus, &gInfo->edid_info, NULL, NULL);
589 	if (error == B_OK) {
590 		edid_dump(&gInfo->edid_info);
591 		gInfo->has_edid = true;
592 		if (gInfo->shared_info->single_head_locked)
593 			gInfo->head_mode = HEAD_MODE_A_ANALOG;
594 	} else {
595 		TRACE("getting EDID on port A (analog) failed: %s. "
596 			"Trying on port C (lvds)\n", strerror(error));
597 		bus.cookie = (void*)INTEL_I2C_IO_C;
598 		error = ddc2_read_edid1(&bus, &gInfo->edid_info, NULL, NULL);
599 		if (error == B_OK) {
600 			edid_dump(&gInfo->edid_info);
601 			gInfo->has_edid = true;
602 		} else if (gInfo->shared_info->has_vesa_edid_info) {
603 			TRACE("getting EDID on port C failed: %s. Use VESA EDID info\n",
604 				strerror(error));
605 			memcpy(&gInfo->edid_info, &gInfo->shared_info->vesa_edid_info,
606 				sizeof(edid1_info));
607 			gInfo->has_edid = true;
608 		} else {
609 			TRACE("getting EDID on port C failed: %s\n",
610 				strerror(error));
611 
612 			// We could not read any EDID info. Fallback to creating a list with
613 			// only the mode set up by the BIOS.
614 			// TODO: support lower modes via scaling and windowing
615 			if (((gInfo->head_mode & HEAD_MODE_LVDS_PANEL) != 0
616 					&& (gInfo->head_mode & HEAD_MODE_A_ANALOG) == 0)
617 					|| ((gInfo->head_mode & HEAD_MODE_LVDS_PANEL) != 0
618 					&& gInfo->shared_info->got_vbt)) {
619 				size_t size = (sizeof(display_mode) + B_PAGE_SIZE - 1)
620 					& ~(B_PAGE_SIZE - 1);
621 
622 				display_mode* list;
623 				area_id area = create_area("intel extreme modes",
624 					(void**)&list, B_ANY_ADDRESS, size, B_NO_LOCK,
625 					B_READ_AREA | B_WRITE_AREA);
626 				if (area < B_OK)
627 					return area;
628 
629 				// Prefer information dumped directly from VBT, as the BIOS
630 				// one may have display scaling, but only do this if the VBT
631 				// resolution is higher than the BIOS one.
632 				if (gInfo->shared_info->got_vbt
633 					&& gInfo->shared_info->current_mode.virtual_width
634 						>= gInfo->lvds_panel_mode.virtual_width
635 					&& gInfo->shared_info->current_mode.virtual_height
636 						>= gInfo->lvds_panel_mode.virtual_height) {
637 					memcpy(list, &gInfo->shared_info->current_mode,
638 						sizeof(display_mode));
639 					mode_fill_missing_bits(list, INTEL_DISPLAY_B_CONTROL);
640 				} else {
641 					memcpy(list, &gInfo->lvds_panel_mode,
642 						sizeof(display_mode));
643 
644 					if (gInfo->shared_info->got_vbt)
645 						TRACE("intel_extreme: ignoring VBT mode.");
646 				}
647 
648 				gInfo->mode_list_area = area;
649 				gInfo->mode_list = list;
650 				gInfo->shared_info->mode_list_area = gInfo->mode_list_area;
651 				gInfo->shared_info->mode_count = 1;
652 				return B_OK;
653 			}
654 		}
655 	}
656 
657 	// Otherwise return the 'real' list of modes
658 	display_mode* list;
659 	uint32 count = 0;
660 	gInfo->mode_list_area = create_display_modes("intel extreme modes",
661 		gInfo->has_edid ? &gInfo->edid_info : NULL, NULL, 0, NULL, 0,
662 		&check_and_sanitize_display_mode, &list, &count);
663 	if (gInfo->mode_list_area < B_OK)
664 		return gInfo->mode_list_area;
665 
666 	gInfo->mode_list = list;
667 	gInfo->shared_info->mode_list_area = gInfo->mode_list_area;
668 	gInfo->shared_info->mode_count = count;
669 
670 	return B_OK;
671 }
672 
673 
674 void
675 wait_for_vblank(void)
676 {
677 	acquire_sem_etc(gInfo->shared_info->vblank_sem, 1, B_RELATIVE_TIMEOUT,
678 		25000);
679 		// With the output turned off via DPMS, we might not get any interrupts
680 		// anymore that's why we don't wait forever for it.
681 }
682 
683 
684 /*! Store away panel information if identified on startup
685 	(used for pipe B->lvds).
686 */
687 void
688 save_lvds_mode(void)
689 {
690 	// dump currently programmed mode.
691 	display_mode biosMode;
692 	retrieve_current_mode(biosMode, INTEL_DISPLAY_B_PLL);
693 
694 	sanitize_display_mode(biosMode);
695 		// The BIOS mode may not be a valid mode, as LVDS output does not
696 		// really care about the sync values
697 
698 	gInfo->lvds_panel_mode = biosMode;
699 }
700 
701 
702 //	#pragma mark -
703 
704 
705 uint32
706 intel_accelerant_mode_count(void)
707 {
708 	CALLED();
709 	return gInfo->shared_info->mode_count;
710 }
711 
712 
713 status_t
714 intel_get_mode_list(display_mode* modeList)
715 {
716 	CALLED();
717 	memcpy(modeList, gInfo->mode_list,
718 		gInfo->shared_info->mode_count * sizeof(display_mode));
719 	return B_OK;
720 }
721 
722 
723 status_t
724 intel_propose_display_mode(display_mode* target, const display_mode* low,
725 	const display_mode* high)
726 {
727 	CALLED();
728 
729 	// first search for the specified mode in the list, if no mode is found
730 	// try to fix the target mode in sanitize_display_mode
731 	// TODO: Only sanitize_display_mode should be used. However, at the moment
732 	// the mode constraints are not optimal and do not work for all
733 	// configurations.
734 	for (uint32 i = 0; i < gInfo->shared_info->mode_count; i++) {
735 		display_mode *mode = &gInfo->mode_list[i];
736 
737 		// TODO: improve this, ie. adapt pixel clock to allowed values!!!
738 
739 		if (target->virtual_width != mode->virtual_width
740 		        || target->virtual_height != mode->virtual_height
741 		        || target->space != mode->space)
742 		        continue;
743 
744 		*target = *mode;
745 		return B_OK;
746 	}
747 
748 	sanitize_display_mode(*target);
749 
750 	return is_display_mode_within_bounds(*target, *low, *high)
751 		? B_OK : B_BAD_VALUE;
752 }
753 
754 
755 status_t
756 intel_set_display_mode(display_mode* mode)
757 {
758 	if (mode == NULL)
759 		return B_BAD_VALUE;
760 
761 	TRACE("%s(%" B_PRIu16 "x%" B_PRIu16 ")\n", __func__,
762 		mode->virtual_width, mode->virtual_height);
763 
764 	display_mode target = *mode;
765 
766 	// TODO: it may be acceptable to continue when using panel fitting or
767 	// centering, since the data from propose_display_mode will not actually be
768 	// used as is in this case.
769 	if (sanitize_display_mode(target)) {
770 		TRACE("%s: invalid mode set!\n", __func__);
771 		return B_BAD_VALUE;
772 	}
773 
774 	uint32 colorMode, bytesPerRow, bitsPerPixel;
775 	get_color_space_format(target, colorMode, bytesPerRow, bitsPerPixel);
776 
777 	// TODO stop here, when the requested mode is the same as the current one.
778 	// This would avoid screen flickering when setting a mode that's already in
779 	// place.
780 
781 #if 0
782 static bool first = true;
783 if (first) {
784 	int fd = open("/boot/home/ie_.regs", O_CREAT | O_WRONLY, 0644);
785 	if (fd >= 0) {
786 		for (int32 i = 0; i < 0x80000; i += 16) {
787 			char line[512];
788 			int length = sprintf(line, "%05lx: %08lx %08lx %08lx %08lx\n",
789 				i, read32(i), read32(i + 4), read32(i + 8), read32(i + 12));
790 			write(fd, line, length);
791 		}
792 		close(fd);
793 		sync();
794 	}
795 	first = false;
796 }
797 #endif
798 
799 	intel_shared_info &sharedInfo = *gInfo->shared_info;
800 	Autolock locker(sharedInfo.accelerant_lock);
801 
802 	// TODO: This may not be neccesary
803 	set_display_power_mode(B_DPMS_OFF);
804 
805 	// free old and allocate new frame buffer in graphics memory
806 
807 	intel_free_memory(sharedInfo.frame_buffer);
808 
809 	addr_t base;
810 	if (intel_allocate_memory(bytesPerRow * target.virtual_height, 0,
811 			base) < B_OK) {
812 		// oh, how did that happen? Unfortunately, there is no really good way
813 		// back
814 		if (intel_allocate_memory(sharedInfo.current_mode.virtual_height
815 				* sharedInfo.bytes_per_row, 0, base) == B_OK) {
816 			sharedInfo.frame_buffer = base;
817 			sharedInfo.frame_buffer_offset = base
818 				- (addr_t)sharedInfo.graphics_memory;
819 			set_frame_buffer_base();
820 		}
821 
822 		TRACE("%s: Failed to allocate framebuffer !\n", __func__);
823 		return B_NO_MEMORY;
824 	}
825 
826 	// clear frame buffer before using it
827 	memset((uint8*)base, 0, bytesPerRow * target.virtual_height);
828 	sharedInfo.frame_buffer = base;
829 	sharedInfo.frame_buffer_offset = base - (addr_t)sharedInfo.graphics_memory;
830 
831 	// make sure VGA display is disabled
832 	write32(INTEL_VGA_DISPLAY_CONTROL, VGA_DISPLAY_DISABLED);
833 	read32(INTEL_VGA_DISPLAY_CONTROL);
834 
835 	if ((gInfo->head_mode & HEAD_MODE_B_DIGITAL) != 0) {
836 		// For LVDS panels, we actually always set the native mode in hardware
837 		// Then we use the panel fitter to scale the picture to that.
838 		display_mode hardwareTarget;
839 		bool needsScaling = false;
840 
841 		// Try to get the panel preferred screen mode from EDID info
842 		if (gInfo->has_edid) {
843 			hardwareTarget.space = target.space;
844 			hardwareTarget.virtual_width
845 				= gInfo->edid_info.std_timing[0].h_size;
846 			hardwareTarget.virtual_height
847 				= gInfo->edid_info.std_timing[0].v_size;
848 			for (int i = 0; i < EDID1_NUM_DETAILED_MONITOR_DESC; i++) {
849 				if (gInfo->edid_info.detailed_monitor[i].monitor_desc_type
850 						== EDID1_IS_DETAILED_TIMING) {
851 					hardwareTarget.virtual_width = gInfo->edid_info
852 						.detailed_monitor[i].data.detailed_timing.h_active;
853 					hardwareTarget.virtual_height = gInfo->edid_info
854 						.detailed_monitor[i].data.detailed_timing.v_active;
855 					break;
856 				}
857 			}
858 			TRACE("%s: hardware mode will actually be %dx%d\n", __func__,
859 				hardwareTarget.virtual_width, hardwareTarget.virtual_height);
860 			if ((hardwareTarget.virtual_width <= target.virtual_width
861 					&& hardwareTarget.virtual_height <= target.virtual_height
862 					&& hardwareTarget.space <= target.space)
863 				|| intel_propose_display_mode(&hardwareTarget, mode, mode)) {
864 				hardwareTarget = target;
865 			} else
866 				needsScaling = true;
867 		} else {
868 			// We don't have EDID data, try to set the requested mode directly
869 			hardwareTarget = target;
870 		}
871 
872 		pll_divisors divisors;
873 		if (needsScaling)
874 			compute_pll_divisors(hardwareTarget, divisors, true);
875 		else
876 			compute_pll_divisors(target, divisors, true);
877 
878 		uint32 dpll = DISPLAY_PLL_NO_VGA_CONTROL | DISPLAY_PLL_ENABLED;
879 		if (gInfo->shared_info->device_type.InFamily(INTEL_TYPE_9xx)) {
880 			dpll |= LVDS_PLL_MODE_LVDS;
881 				// DPLL mode LVDS for i915+
882 		}
883 
884 		// Compute bitmask from p1 value
885 		if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) {
886 			dpll |= (1 << (divisors.post1 - 1))
887 				<< DISPLAY_PLL_IGD_POST1_DIVISOR_SHIFT;
888 		} else {
889 			dpll |= (1 << (divisors.post1 - 1))
890 				<< DISPLAY_PLL_POST1_DIVISOR_SHIFT;
891 		}
892 		switch (divisors.post2) {
893 			case 5:
894 			case 7:
895 				dpll |= DISPLAY_PLL_DIVIDE_HIGH;
896 				break;
897 		}
898 
899 		// Disable panel fitting, but enable 8 to 6-bit dithering
900 		write32(INTEL_PANEL_FIT_CONTROL, 0x4);
901 			// TODO: do not do this if the connected panel is 24-bit
902 			// (I don't know how to detect that)
903 
904 		if ((dpll & DISPLAY_PLL_ENABLED) != 0) {
905 			if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) {
906 				write32(INTEL_DISPLAY_B_PLL_DIVISOR_0,
907 					(((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT)
908 						& DISPLAY_PLL_IGD_N_DIVISOR_MASK)
909 					| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
910 						& DISPLAY_PLL_IGD_M2_DIVISOR_MASK));
911 			} else {
912 				write32(INTEL_DISPLAY_B_PLL_DIVISOR_0,
913 					(((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT)
914 						& DISPLAY_PLL_N_DIVISOR_MASK)
915 					| (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT)
916 						& DISPLAY_PLL_M1_DIVISOR_MASK)
917 					| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
918 						& DISPLAY_PLL_M2_DIVISOR_MASK));
919 			}
920 			write32(INTEL_DISPLAY_B_PLL, dpll & ~DISPLAY_PLL_ENABLED);
921 			read32(INTEL_DISPLAY_B_PLL);
922 			spin(150);
923 		}
924 
925 		uint32 lvds = read32(INTEL_DISPLAY_LVDS_PORT) | LVDS_PORT_EN
926 			| LVDS_A0A2_CLKA_POWER_UP | LVDS_PIPEB_SELECT;
927 
928 		lvds |= LVDS_18BIT_DITHER;
929 			// TODO: do not do this if the connected panel is 24-bit
930 			// (I don't know how to detect that)
931 
932 		float referenceClock = gInfo->shared_info->pll_info.reference_frequency
933 			/ 1000.0f;
934 
935 		// Set the B0-B3 data pairs corresponding to whether we're going to
936 		// set the DPLLs for dual-channel mode or not.
937 		if (divisors.post2 == LVDS_POST2_RATE_FAST)
938 			lvds |= LVDS_B0B3PAIRS_POWER_UP | LVDS_CLKB_POWER_UP;
939 		else
940 			lvds &= ~(LVDS_B0B3PAIRS_POWER_UP | LVDS_CLKB_POWER_UP);
941 
942 		write32(INTEL_DISPLAY_LVDS_PORT, lvds);
943 		read32(INTEL_DISPLAY_LVDS_PORT);
944 
945 		if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) {
946 			write32(INTEL_DISPLAY_B_PLL_DIVISOR_0,
947 				(((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT)
948 					& DISPLAY_PLL_IGD_N_DIVISOR_MASK)
949 				| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
950 					& DISPLAY_PLL_IGD_M2_DIVISOR_MASK));
951 		} else {
952 			write32(INTEL_DISPLAY_B_PLL_DIVISOR_0,
953 				(((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT)
954 					& DISPLAY_PLL_N_DIVISOR_MASK)
955 				| (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT)
956 					& DISPLAY_PLL_M1_DIVISOR_MASK)
957 				| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
958 					& DISPLAY_PLL_M2_DIVISOR_MASK));
959 		}
960 
961 		write32(INTEL_DISPLAY_B_PLL, dpll);
962 		read32(INTEL_DISPLAY_B_PLL);
963 
964 		// Wait for the clocks to stabilize
965 		spin(150);
966 
967 		if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_96x)) {
968 			float adjusted = ((referenceClock * divisors.m) / divisors.n)
969 				/ divisors.post;
970 			uint32 pixelMultiply;
971 			if (needsScaling) {
972 				pixelMultiply = uint32(adjusted
973 					/ (hardwareTarget.timing.pixel_clock / 1000.0f));
974 			} else {
975 				pixelMultiply = uint32(adjusted
976 					/ (target.timing.pixel_clock / 1000.0f));
977 			}
978 
979 			write32(INTEL_DISPLAY_B_PLL_MULTIPLIER_DIVISOR, (0 << 24)
980 				| ((pixelMultiply - 1) << 8));
981 		} else
982 			write32(INTEL_DISPLAY_B_PLL, dpll);
983 
984 		read32(INTEL_DISPLAY_B_PLL);
985 		spin(150);
986 
987 		// update timing parameters
988 		if (needsScaling) {
989 			// TODO: Alternatively, it should be possible to use the panel
990 			// fitter and scale the picture.
991 
992 			// TODO: Perform some sanity check, for example if the target is
993 			// wider than the hardware mode we end up with negative borders and
994 			// broken timings
995 			uint32 borderWidth = hardwareTarget.timing.h_display
996 				- target.timing.h_display;
997 
998 			uint32 syncWidth = hardwareTarget.timing.h_sync_end
999 				- hardwareTarget.timing.h_sync_start;
1000 
1001 			uint32 syncCenter = target.timing.h_display
1002 				+ (hardwareTarget.timing.h_total
1003 				- target.timing.h_display) / 2;
1004 
1005 			write32(INTEL_DISPLAY_B_HTOTAL,
1006 				((uint32)(hardwareTarget.timing.h_total - 1) << 16)
1007 				| ((uint32)target.timing.h_display - 1));
1008 			write32(INTEL_DISPLAY_B_HBLANK,
1009 				((uint32)(hardwareTarget.timing.h_total - borderWidth / 2 - 1)
1010 					<< 16)
1011 				| ((uint32)target.timing.h_display + borderWidth / 2 - 1));
1012 			write32(INTEL_DISPLAY_B_HSYNC,
1013 				((uint32)(syncCenter + syncWidth / 2 - 1) << 16)
1014 				| ((uint32)syncCenter - syncWidth / 2 - 1));
1015 
1016 			uint32 borderHeight = hardwareTarget.timing.v_display
1017 				- target.timing.v_display;
1018 
1019 			uint32 syncHeight = hardwareTarget.timing.v_sync_end
1020 				- hardwareTarget.timing.v_sync_start;
1021 
1022 			syncCenter = target.timing.v_display
1023 				+ (hardwareTarget.timing.v_total
1024 				- target.timing.v_display) / 2;
1025 
1026 			write32(INTEL_DISPLAY_B_VTOTAL,
1027 				((uint32)(hardwareTarget.timing.v_total - 1) << 16)
1028 				| ((uint32)target.timing.v_display - 1));
1029 			write32(INTEL_DISPLAY_B_VBLANK,
1030 				((uint32)(hardwareTarget.timing.v_total - borderHeight / 2 - 1)
1031 					<< 16)
1032 				| ((uint32)target.timing.v_display
1033 					+ borderHeight / 2 - 1));
1034 			write32(INTEL_DISPLAY_B_VSYNC,
1035 				((uint32)(syncCenter + syncHeight / 2 - 1) << 16)
1036 				| ((uint32)syncCenter - syncHeight / 2 - 1));
1037 
1038 			// This is useful for debugging: it sets the border to red, so you
1039 			// can see what is border and what is porch (black area around the
1040 			// sync)
1041 			// write32(0x61020, 0x00FF0000);
1042 		} else {
1043 			write32(INTEL_DISPLAY_B_HTOTAL,
1044 				((uint32)(target.timing.h_total - 1) << 16)
1045 				| ((uint32)target.timing.h_display - 1));
1046 			write32(INTEL_DISPLAY_B_HBLANK,
1047 				((uint32)(target.timing.h_total - 1) << 16)
1048 				| ((uint32)target.timing.h_display - 1));
1049 			write32(INTEL_DISPLAY_B_HSYNC,
1050 				((uint32)(target.timing.h_sync_end - 1) << 16)
1051 				| ((uint32)target.timing.h_sync_start - 1));
1052 
1053 			write32(INTEL_DISPLAY_B_VTOTAL,
1054 				((uint32)(target.timing.v_total - 1) << 16)
1055 				| ((uint32)target.timing.v_display - 1));
1056 			write32(INTEL_DISPLAY_B_VBLANK,
1057 				((uint32)(target.timing.v_total - 1) << 16)
1058 				| ((uint32)target.timing.v_display - 1));
1059 			write32(INTEL_DISPLAY_B_VSYNC, (
1060 				(uint32)(target.timing.v_sync_end - 1) << 16)
1061 				| ((uint32)target.timing.v_sync_start - 1));
1062 		}
1063 
1064 		write32(INTEL_DISPLAY_B_IMAGE_SIZE,
1065 			((uint32)(target.virtual_width - 1) << 16)
1066 			| ((uint32)target.virtual_height - 1));
1067 
1068 		write32(INTEL_DISPLAY_B_POS, 0);
1069 		write32(INTEL_DISPLAY_B_PIPE_SIZE,
1070 			((uint32)(target.timing.v_display - 1) << 16)
1071 			| ((uint32)target.timing.h_display - 1));
1072 
1073 		write32(INTEL_DISPLAY_B_CONTROL, (read32(INTEL_DISPLAY_B_CONTROL)
1074 				& ~(DISPLAY_CONTROL_COLOR_MASK | DISPLAY_CONTROL_GAMMA))
1075 			| colorMode);
1076 
1077 		write32(INTEL_DISPLAY_B_PIPE_CONTROL,
1078 			read32(INTEL_DISPLAY_B_PIPE_CONTROL) | DISPLAY_PIPE_ENABLED);
1079 		read32(INTEL_DISPLAY_B_PIPE_CONTROL);
1080 	}
1081 
1082 	if ((gInfo->head_mode & HEAD_MODE_A_ANALOG) != 0) {
1083 		pll_divisors divisors;
1084 		compute_pll_divisors(target, divisors, false);
1085 
1086 		if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) {
1087 			write32(INTEL_DISPLAY_A_PLL_DIVISOR_0,
1088 				(((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT)
1089 					& DISPLAY_PLL_IGD_N_DIVISOR_MASK)
1090 				| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
1091 					& DISPLAY_PLL_IGD_M2_DIVISOR_MASK));
1092 		} else {
1093 			write32(INTEL_DISPLAY_A_PLL_DIVISOR_0,
1094 				(((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT)
1095 					& DISPLAY_PLL_N_DIVISOR_MASK)
1096 				| (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT)
1097 					& DISPLAY_PLL_M1_DIVISOR_MASK)
1098 				| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
1099 					& DISPLAY_PLL_M2_DIVISOR_MASK));
1100 		}
1101 
1102 		uint32 pll = DISPLAY_PLL_ENABLED | DISPLAY_PLL_NO_VGA_CONTROL;
1103 		if (gInfo->shared_info->device_type.InFamily(INTEL_TYPE_9xx)) {
1104 			if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) {
1105 				pll |= ((1 << (divisors.post1 - 1))
1106 						<< DISPLAY_PLL_IGD_POST1_DIVISOR_SHIFT)
1107 					& DISPLAY_PLL_IGD_POST1_DIVISOR_MASK;
1108 			} else {
1109 				pll |= ((1 << (divisors.post1 - 1))
1110 						<< DISPLAY_PLL_POST1_DIVISOR_SHIFT)
1111 					& DISPLAY_PLL_9xx_POST1_DIVISOR_MASK;
1112 //				pll |= ((divisors.post1 - 1) << DISPLAY_PLL_POST1_DIVISOR_SHIFT)
1113 //					& DISPLAY_PLL_9xx_POST1_DIVISOR_MASK;
1114 			}
1115 			if (divisors.post2_high)
1116 				pll |= DISPLAY_PLL_DIVIDE_HIGH;
1117 
1118 			pll |= DISPLAY_PLL_MODE_ANALOG;
1119 
1120 			if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_96x))
1121 				pll |= 6 << DISPLAY_PLL_PULSE_PHASE_SHIFT;
1122 		} else {
1123 			if (!divisors.post2_high)
1124 				pll |= DISPLAY_PLL_DIVIDE_4X;
1125 
1126 			pll |= DISPLAY_PLL_2X_CLOCK;
1127 
1128 			if (divisors.post1 > 2) {
1129 				pll |= ((divisors.post1 - 2) << DISPLAY_PLL_POST1_DIVISOR_SHIFT)
1130 					& DISPLAY_PLL_POST1_DIVISOR_MASK;
1131 			} else
1132 				pll |= DISPLAY_PLL_POST1_DIVIDE_2;
1133 		}
1134 
1135 		// Programmer's Ref says we must allow the DPLL to "warm up" before starting the plane
1136 		// so mask its bit, wait, enable its bit
1137 		write32(INTEL_DISPLAY_A_PLL, pll & ~DISPLAY_PLL_NO_VGA_CONTROL);
1138 		read32(INTEL_DISPLAY_A_PLL);
1139 		spin(150);
1140 		write32(INTEL_DISPLAY_A_PLL, pll);
1141 		read32(INTEL_DISPLAY_A_PLL);
1142 		spin(150);
1143 
1144 		// update timing parameters
1145 		write32(INTEL_DISPLAY_A_HTOTAL,
1146 			((uint32)(target.timing.h_total - 1) << 16)
1147 			| ((uint32)target.timing.h_display - 1));
1148 		write32(INTEL_DISPLAY_A_HBLANK,
1149 			((uint32)(target.timing.h_total - 1) << 16)
1150 			| ((uint32)target.timing.h_display - 1));
1151 		write32(INTEL_DISPLAY_A_HSYNC,
1152 			((uint32)(target.timing.h_sync_end - 1) << 16)
1153 			| ((uint32)target.timing.h_sync_start - 1));
1154 
1155 		write32(INTEL_DISPLAY_A_VTOTAL,
1156 			((uint32)(target.timing.v_total - 1) << 16)
1157 			| ((uint32)target.timing.v_display - 1));
1158 		write32(INTEL_DISPLAY_A_VBLANK,
1159 			((uint32)(target.timing.v_total - 1) << 16)
1160 			| ((uint32)target.timing.v_display - 1));
1161 		write32(INTEL_DISPLAY_A_VSYNC,
1162 			((uint32)(target.timing.v_sync_end - 1) << 16)
1163 			| ((uint32)target.timing.v_sync_start - 1));
1164 
1165 		write32(INTEL_DISPLAY_A_IMAGE_SIZE,
1166 			((uint32)(target.virtual_width - 1) << 16)
1167 			| ((uint32)target.virtual_height - 1));
1168 
1169 		write32(INTEL_DISPLAY_A_ANALOG_PORT,
1170 			(read32(INTEL_DISPLAY_A_ANALOG_PORT)
1171 				& ~(DISPLAY_MONITOR_POLARITY_MASK
1172 					| DISPLAY_MONITOR_VGA_POLARITY))
1173 			| ((target.timing.flags & B_POSITIVE_HSYNC) != 0
1174 				? DISPLAY_MONITOR_POSITIVE_HSYNC : 0)
1175 			| ((target.timing.flags & B_POSITIVE_VSYNC) != 0
1176 				? DISPLAY_MONITOR_POSITIVE_VSYNC : 0));
1177 
1178 		// TODO: verify the two comments below: the X driver doesn't seem to
1179 		//		care about both of them!
1180 
1181 		// These two have to be set for display B, too - this obviously means
1182 		// that the second head always must adopt the color space of the first
1183 		// head.
1184 		write32(INTEL_DISPLAY_A_CONTROL, (read32(INTEL_DISPLAY_A_CONTROL)
1185 				& ~(DISPLAY_CONTROL_COLOR_MASK | DISPLAY_CONTROL_GAMMA))
1186 			| colorMode);
1187 
1188 		if ((gInfo->head_mode & HEAD_MODE_B_DIGITAL) != 0) {
1189 			write32(INTEL_DISPLAY_B_IMAGE_SIZE,
1190 				((uint32)(target.virtual_width - 1) << 16)
1191 				| ((uint32)target.virtual_height - 1));
1192 
1193 			write32(INTEL_DISPLAY_B_CONTROL, (read32(INTEL_DISPLAY_B_CONTROL)
1194 					& ~(DISPLAY_CONTROL_COLOR_MASK | DISPLAY_CONTROL_GAMMA))
1195 				| colorMode);
1196 		}
1197 	}
1198 
1199 	set_display_power_mode(sharedInfo.dpms_mode);
1200 
1201 	// Changing bytes per row seems to be ignored if the plane/pipe is turned
1202 	// off
1203 
1204 	if (gInfo->head_mode & HEAD_MODE_A_ANALOG)
1205 		write32(INTEL_DISPLAY_A_BYTES_PER_ROW, bytesPerRow);
1206 	if (gInfo->head_mode & HEAD_MODE_B_DIGITAL)
1207 		write32(INTEL_DISPLAY_B_BYTES_PER_ROW, bytesPerRow);
1208 
1209 	set_frame_buffer_base();
1210 		// triggers writing back double-buffered registers
1211 
1212 	// update shared info
1213 	sharedInfo.bytes_per_row = bytesPerRow;
1214 	sharedInfo.current_mode = target;
1215 	sharedInfo.bits_per_pixel = bitsPerPixel;
1216 
1217 	return B_OK;
1218 }
1219 
1220 
1221 status_t
1222 intel_get_display_mode(display_mode* _currentMode)
1223 {
1224 	CALLED();
1225 
1226 	retrieve_current_mode(*_currentMode, INTEL_DISPLAY_A_PLL);
1227 	return B_OK;
1228 }
1229 
1230 
1231 status_t
1232 intel_get_edid_info(void* info, size_t size, uint32* _version)
1233 {
1234 	CALLED();
1235 
1236 	if (!gInfo->has_edid)
1237 		return B_ERROR;
1238 	if (size < sizeof(struct edid1_info))
1239 		return B_BUFFER_OVERFLOW;
1240 
1241 	memcpy(info, &gInfo->edid_info, sizeof(struct edid1_info));
1242 	*_version = EDID_VERSION_1;
1243 	return B_OK;
1244 }
1245 
1246 
1247 status_t
1248 intel_get_frame_buffer_config(frame_buffer_config* config)
1249 {
1250 	CALLED();
1251 
1252 	uint32 offset = gInfo->shared_info->frame_buffer_offset;
1253 
1254 	config->frame_buffer = gInfo->shared_info->graphics_memory + offset;
1255 	config->frame_buffer_dma
1256 		= (uint8*)gInfo->shared_info->physical_graphics_memory + offset;
1257 	config->bytes_per_row = gInfo->shared_info->bytes_per_row;
1258 
1259 	return B_OK;
1260 }
1261 
1262 
1263 status_t
1264 intel_get_pixel_clock_limits(display_mode* mode, uint32* _low, uint32* _high)
1265 {
1266 	CALLED();
1267 
1268 	if (_low != NULL) {
1269 		// lower limit of about 48Hz vertical refresh
1270 		uint32 totalClocks = (uint32)mode->timing.h_total
1271 			* (uint32)mode->timing.v_total;
1272 		uint32 low = (totalClocks * 48L) / 1000L;
1273 		if (low < gInfo->shared_info->pll_info.min_frequency)
1274 			low = gInfo->shared_info->pll_info.min_frequency;
1275 		else if (low > gInfo->shared_info->pll_info.max_frequency)
1276 			return B_ERROR;
1277 
1278 		*_low = low;
1279 	}
1280 
1281 	if (_high != NULL)
1282 		*_high = gInfo->shared_info->pll_info.max_frequency;
1283 
1284 	return B_OK;
1285 }
1286 
1287 
1288 status_t
1289 intel_move_display(uint16 horizontalStart, uint16 verticalStart)
1290 {
1291 	CALLED();
1292 
1293 	intel_shared_info &sharedInfo = *gInfo->shared_info;
1294 	Autolock locker(sharedInfo.accelerant_lock);
1295 
1296 	display_mode &mode = sharedInfo.current_mode;
1297 
1298 	if (horizontalStart + mode.timing.h_display > mode.virtual_width
1299 		|| verticalStart + mode.timing.v_display > mode.virtual_height)
1300 		return B_BAD_VALUE;
1301 
1302 	mode.h_display_start = horizontalStart;
1303 	mode.v_display_start = verticalStart;
1304 
1305 	set_frame_buffer_base();
1306 
1307 	return B_OK;
1308 }
1309 
1310 
1311 status_t
1312 intel_get_timing_constraints(display_timing_constraints* constraints)
1313 {
1314 	CALLED();
1315 	return B_ERROR;
1316 }
1317 
1318 
1319 void
1320 intel_set_indexed_colors(uint count, uint8 first, uint8* colors, uint32 flags)
1321 {
1322 	TRACE("%s(colors = %p, first = %u)\n", __func__, colors, first);
1323 
1324 	if (colors == NULL)
1325 		return;
1326 
1327 	Autolock locker(gInfo->shared_info->accelerant_lock);
1328 
1329 	for (; count-- > 0; first++) {
1330 		uint32 color = colors[0] << 16 | colors[1] << 8 | colors[2];
1331 		colors += 3;
1332 
1333 		write32(INTEL_DISPLAY_A_PALETTE + first * sizeof(uint32), color);
1334 		write32(INTEL_DISPLAY_B_PALETTE + first * sizeof(uint32), color);
1335 	}
1336 }
1337 
1338