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