xref: /haiku/src/add-ons/accelerants/intel_extreme/mode.cpp (revision 481f986b59e7782458dcc5fe98ad59a57480e5db)
1 /*
2  * Copyright 2006-2010, 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 <stdio.h>
18 #include <string.h>
19 #include <math.h>
20 
21 #include <create_display_modes.h>
22 #include <ddc.h>
23 #include <edid.h>
24 
25 
26 #define TRACE_MODE
27 #ifdef TRACE_MODE
28 extern "C" void _sPrintf(const char *format, ...);
29 #	define TRACE(x) _sPrintf x
30 #else
31 #	define TRACE(x) ;
32 #endif
33 
34 
35 struct display_registers {
36 	uint32	pll;
37 	uint32	divisors;
38 	uint32	control;
39 	uint32	pipe_config;
40 	uint32	horiz_total;
41 	uint32	horiz_blank;
42 	uint32	horiz_sync;
43 	uint32	vert_total;
44 	uint32	vert_blank;
45 	uint32	vert_sync;
46 	uint32	size;
47 	uint32	stride;
48 	uint32	position;
49 	uint32	pipe_source;
50 };
51 
52 struct pll_divisors {
53 	uint32	post;
54 	uint32	post1;
55 	uint32	post2;
56 	bool	post2_high;
57 	uint32	n;
58 	uint32	m;
59 	uint32	m1;
60 	uint32	m2;
61 };
62 
63 struct pll_limits {
64 	pll_divisors	min;
65 	pll_divisors	max;
66 	uint32			min_post2_frequency;
67 	uint32			min_vco;
68 	uint32			max_vco;
69 };
70 
71 
72 static status_t
73 get_i2c_signals(void* cookie, int* _clock, int* _data)
74 {
75 	uint32 ioRegister = (uint32)cookie;
76 	uint32 value = read32(ioRegister);
77 
78 	*_clock = (value & I2C_CLOCK_VALUE_IN) != 0;
79 	*_data = (value & I2C_DATA_VALUE_IN) != 0;
80 
81 	return B_OK;
82 }
83 
84 
85 static status_t
86 set_i2c_signals(void* cookie, int clock, int data)
87 {
88 	uint32 ioRegister = (uint32)cookie;
89 	uint32 value;
90 
91 	if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_83x)) {
92 		// on these chips, the reserved values are fixed
93 		value = 0;
94 	} else {
95 		// on all others, we have to preserve them manually
96 		value = read32(ioRegister) & I2C_RESERVED;
97 	}
98 
99 	if (data != 0)
100 		value |= I2C_DATA_DIRECTION_MASK;
101 	else
102 		value |= I2C_DATA_DIRECTION_MASK | I2C_DATA_DIRECTION_OUT | I2C_DATA_VALUE_MASK;
103 
104 	if (clock != 0)
105 		value |= I2C_CLOCK_DIRECTION_MASK;
106 	else
107 		value |= I2C_CLOCK_DIRECTION_MASK | I2C_CLOCK_DIRECTION_OUT | I2C_CLOCK_VALUE_MASK;
108 
109 	write32(ioRegister, value);
110 	read32(ioRegister);
111 		// make sure the PCI bus has flushed the write
112 
113 	return B_OK;
114 }
115 
116 
117 void
118 set_frame_buffer_base()
119 {
120 	intel_shared_info &sharedInfo = *gInfo->shared_info;
121 	display_mode &mode = sharedInfo.current_mode;
122 	uint32 baseRegister;
123 	uint32 surfaceRegister;
124 
125 	if (gInfo->head_mode & HEAD_MODE_A_ANALOG) {
126 		baseRegister = INTEL_DISPLAY_A_BASE;
127 		surfaceRegister = INTEL_DISPLAY_A_SURFACE;
128 	} else {
129 		baseRegister = INTEL_DISPLAY_B_BASE;
130 		surfaceRegister = INTEL_DISPLAY_B_SURFACE;
131 	}
132 
133 	if (sharedInfo.device_type.InGroup(INTEL_TYPE_96x)
134 		|| sharedInfo.device_type.InGroup(INTEL_TYPE_G4x)) {
135 		write32(baseRegister, mode.v_display_start * sharedInfo.bytes_per_row
136 			+ mode.h_display_start * (sharedInfo.bits_per_pixel + 7) / 8);
137 		read32(baseRegister);
138 		write32(surfaceRegister, sharedInfo.frame_buffer_offset);
139 		read32(surfaceRegister);
140 	} else {
141 		write32(baseRegister, sharedInfo.frame_buffer_offset
142 			+ mode.v_display_start * sharedInfo.bytes_per_row
143 			+ mode.h_display_start * (sharedInfo.bits_per_pixel + 7) / 8);
144 		read32(baseRegister);
145 	}
146 }
147 
148 
149 /*!	Creates the initial mode list of the primary accelerant.
150 	It's called from intel_init_accelerant().
151 */
152 status_t
153 create_mode_list(void)
154 {
155 	i2c_bus bus;
156 	bus.cookie = (void*)INTEL_I2C_IO_A;
157 	bus.set_signals = &set_i2c_signals;
158 	bus.get_signals = &get_i2c_signals;
159 	ddc2_init_timing(&bus);
160 
161 	if (ddc2_read_edid1(&bus, &gInfo->edid_info, NULL, NULL) == B_OK) {
162 		edid_dump(&gInfo->edid_info);
163 		gInfo->has_edid = true;
164 	} else {
165 		TRACE(("intel_extreme: getting EDID failed!\n"));
166 	}
167 
168 	// TODO: support lower modes via scaling and windowing
169 	if (gInfo->head_mode & HEAD_MODE_LVDS_PANEL
170 		&& ((gInfo->head_mode & HEAD_MODE_A_ANALOG) == 0)) {
171 		size_t size = (sizeof(display_mode) + B_PAGE_SIZE - 1)
172 			& ~(B_PAGE_SIZE - 1);
173 
174 		display_mode *list;
175 		area_id area = create_area("intel extreme modes", (void **)&list,
176 			B_ANY_ADDRESS, size, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
177 		if (area < B_OK)
178 			return area;
179 
180 		memcpy(list, &gInfo->lvds_panel_mode, sizeof(display_mode));
181 
182 		gInfo->mode_list_area = area;
183 		gInfo->mode_list = list;
184 		gInfo->shared_info->mode_list_area = gInfo->mode_list_area;
185 		gInfo->shared_info->mode_count = 1;
186 		return B_OK;
187 	}
188 
189 	// Otherwise return the 'real' list of modes
190 	display_mode *list;
191 	uint32 count = 0;
192 	gInfo->mode_list_area = create_display_modes("intel extreme modes",
193 		gInfo->has_edid ? &gInfo->edid_info : NULL, NULL, 0, NULL, 0, NULL,
194 		&list, &count);
195 	if (gInfo->mode_list_area < B_OK)
196 		return gInfo->mode_list_area;
197 
198 	gInfo->mode_list = list;
199 	gInfo->shared_info->mode_list_area = gInfo->mode_list_area;
200 	gInfo->shared_info->mode_count = count;
201 
202 	return B_OK;
203 }
204 
205 
206 void
207 wait_for_vblank(void)
208 {
209 	acquire_sem_etc(gInfo->shared_info->vblank_sem, 1, B_RELATIVE_TIMEOUT, 25000);
210 		// With the output turned off via DPMS, we might not get any interrupts anymore
211 		// that's why we don't wait forever for it.
212 }
213 
214 
215 static void
216 get_pll_limits(pll_limits &limits)
217 {
218 	// Note, the limits are taken from the X driver; they have not yet been
219 	// tested
220 
221 	if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_G4x)) {
222 		// single LVDS output
223 		// NOTE: not sure these will work with CRT displays
224 		static const pll_limits kLimits = {
225 			// p, p1, p2, high,   n,   m, m1, m2
226 			{ 28,  2, 14, false,  1, 104, 17,  5},	// min
227 			{112,  8,  0, true,   3, 138, 23, 11},	// max
228 			200000, 1750000, 3500000
229 		};
230 		limits = kLimits;
231 	} else if (gInfo->shared_info->device_type.InFamily(INTEL_TYPE_9xx)) {
232 		// TODO: support LVDS output limits as well
233 		// (Update: Output limits are adjusted in the computation (post2=7/14))
234 		// Should move them here!
235 		static const pll_limits kLimits = {
236 			// p, p1, p2, high,   n,   m, m1, m2
237 			{  5,  1, 10, false,  5,  70, 12,  7},	// min
238 			{ 80,  8,  5, true,  10, 120, 22, 11},	// max
239 			200000, 1400000, 2800000
240 		};
241 		limits = kLimits;
242 	} else {
243 		// TODO: support LVDS output limits as well
244 		static const pll_limits kLimits = {
245 			// p, p1, p2, high,   n,   m, m1, m2
246 			{  4,  2,  4, false,  5,  96, 20,  8},
247 			{128, 33,  2, true,  18, 140, 28, 18},
248 			165000, 930000, 1400000
249 		};
250 		limits = kLimits;
251 	}
252 
253 	TRACE(("PLL limits, min: p %lu (p1 %lu, p2 %lu), n %lu, m %lu (m1 %lu, m2 %lu)\n",
254 		limits.min.post, limits.min.post1, limits.min.post2, limits.min.n,
255 		limits.min.m, limits.min.m1, limits.min.m2));
256 	TRACE(("PLL limits, max: p %lu (p1 %lu, p2 %lu), n %lu, m %lu (m1 %lu, m2 %lu)\n",
257 		limits.max.post, limits.max.post1, limits.max.post2, limits.max.n,
258 		limits.max.m, limits.max.m1, limits.max.m2));
259 }
260 
261 
262 static bool
263 valid_pll_divisors(const pll_divisors& divisors, const pll_limits& limits)
264 {
265 	pll_info &info = gInfo->shared_info->pll_info;
266 	uint32 vco = info.reference_frequency * divisors.m / divisors.n;
267 	uint32 frequency = vco / divisors.post;
268 
269 	if (divisors.post < limits.min.post || divisors.post > limits.max.post
270 		|| divisors.m < limits.min.m || divisors.m > limits.max.m
271 		|| vco < limits.min_vco || vco > limits.max_vco
272 		|| frequency < info.min_frequency || frequency > info.max_frequency)
273 		return false;
274 
275 	return true;
276 }
277 
278 
279 static void
280 compute_pll_divisors(const display_mode &current, pll_divisors& divisors,
281 	bool isLVDS)
282 {
283 	float requestedPixelClock = current.timing.pixel_clock / 1000.0f;
284 	float referenceClock = gInfo->shared_info->pll_info.reference_frequency / 1000.0f;
285 	pll_limits limits;
286 	get_pll_limits(limits);
287 
288 	TRACE(("required MHz: %g\n", requestedPixelClock));
289 
290 	if (isLVDS) {
291 		if ((read32(INTEL_DISPLAY_LVDS_PORT) & LVDS_CLKB_POWER_MASK)
292 				== LVDS_CLKB_POWER_UP)
293 			divisors.post2 = LVDS_POST2_RATE_FAST;
294 		else
295 			divisors.post2 = LVDS_POST2_RATE_SLOW;
296 	} else {
297 		if (current.timing.pixel_clock < limits.min_post2_frequency) {
298 			// slow DAC timing
299 			divisors.post2 = limits.min.post2;
300 			divisors.post2_high = limits.min.post2_high;
301 		} else {
302 			// fast DAC timing
303 			divisors.post2 = limits.max.post2;
304 			divisors.post2_high = limits.max.post2_high;
305 		}
306 	}
307 
308 	float best = requestedPixelClock;
309 	pll_divisors bestDivisors;
310 
311 	for (divisors.m1 = limits.min.m1; divisors.m1 <= limits.max.m1; divisors.m1++) {
312 		for (divisors.m2 = limits.min.m2; divisors.m2 < divisors.m1
313 				&& divisors.m2 <= limits.max.m2; divisors.m2++) {
314 			for (divisors.n = limits.min.n; divisors.n <= limits.max.n;
315 					divisors.n++) {
316 				for (divisors.post1 = limits.min.post1;
317 						divisors.post1 <= limits.max.post1; divisors.post1++) {
318 					divisors.m = 5 * divisors.m1 + divisors.m2;
319 					divisors.post = divisors.post1 * divisors.post2;
320 
321 					if (!valid_pll_divisors(divisors, limits))
322 						continue;
323 
324 					float error = fabs(requestedPixelClock
325 						- ((referenceClock * divisors.m) / divisors.n) / divisors.post);
326 					if (error < best) {
327 						best = error;
328 						bestDivisors = divisors;
329 
330 						if (error == 0)
331 							break;
332 					}
333 				}
334 			}
335 		}
336 	}
337 
338 	divisors = bestDivisors;
339 
340 	TRACE(("found: %g MHz, p = %lu (p1 = %lu, p2 = %lu), n = %lu, m = %lu (m1 = %lu, m2 = %lu)\n",
341 		((referenceClock * divisors.m) / divisors.n) / divisors.post,
342 		divisors.post, divisors.post1, divisors.post2, divisors.n,
343 		divisors.m, divisors.m1, divisors.m2));
344 }
345 
346 
347 void
348 retrieve_current_mode(display_mode& mode, uint32 pllRegister)
349 {
350 	uint32 pll = read32(pllRegister);
351 	uint32 pllDivisor;
352 	uint32 hTotalRegister;
353 	uint32 vTotalRegister;
354 	uint32 hSyncRegister;
355 	uint32 vSyncRegister;
356 	uint32 imageSizeRegister;
357 	uint32 controlRegister;
358 
359 	if (pllRegister == INTEL_DISPLAY_A_PLL) {
360 		pllDivisor = read32((pll & DISPLAY_PLL_DIVISOR_1) != 0
361 			? INTEL_DISPLAY_A_PLL_DIVISOR_1 : INTEL_DISPLAY_A_PLL_DIVISOR_0);
362 
363 		hTotalRegister = INTEL_DISPLAY_A_HTOTAL;
364 		vTotalRegister = INTEL_DISPLAY_A_VTOTAL;
365 		hSyncRegister = INTEL_DISPLAY_A_HSYNC;
366 		vSyncRegister = INTEL_DISPLAY_A_VSYNC;
367 		imageSizeRegister = INTEL_DISPLAY_A_IMAGE_SIZE;
368 		controlRegister = INTEL_DISPLAY_A_CONTROL;
369 	} else if (pllRegister == INTEL_DISPLAY_B_PLL) {
370 		pllDivisor = read32((pll & DISPLAY_PLL_DIVISOR_1) != 0
371 			? INTEL_DISPLAY_B_PLL_DIVISOR_1 : INTEL_DISPLAY_B_PLL_DIVISOR_0);
372 
373 		hTotalRegister = INTEL_DISPLAY_B_HTOTAL;
374 		vTotalRegister = INTEL_DISPLAY_B_VTOTAL;
375 		hSyncRegister = INTEL_DISPLAY_B_HSYNC;
376 		vSyncRegister = INTEL_DISPLAY_B_VSYNC;
377 		imageSizeRegister = INTEL_DISPLAY_B_IMAGE_SIZE;
378 		controlRegister = INTEL_DISPLAY_B_CONTROL;
379 	} else {
380 		// TODO: not supported
381 		return;
382 	}
383 
384 	pll_divisors divisors;
385 	divisors.m1 = (pllDivisor & DISPLAY_PLL_M1_DIVISOR_MASK)
386 		>> DISPLAY_PLL_M1_DIVISOR_SHIFT;
387 	divisors.m2 = (pllDivisor & DISPLAY_PLL_M2_DIVISOR_MASK)
388 		>> DISPLAY_PLL_M2_DIVISOR_SHIFT;
389 	divisors.n = (pllDivisor & DISPLAY_PLL_N_DIVISOR_MASK)
390 		>> DISPLAY_PLL_N_DIVISOR_SHIFT;
391 
392 	pll_limits limits;
393 	get_pll_limits(limits);
394 
395 	if (gInfo->shared_info->device_type.InFamily(INTEL_TYPE_9xx)) {
396 		divisors.post1 = (pll & DISPLAY_PLL_9xx_POST1_DIVISOR_MASK)
397 			>> DISPLAY_PLL_POST1_DIVISOR_SHIFT;
398 
399 		if (pllRegister == INTEL_DISPLAY_B_PLL
400 			&& !gInfo->shared_info->device_type.InGroup(INTEL_TYPE_96x)) {
401 			// TODO: Fix this? Need to support dual channel LVDS.
402 			divisors.post2 = LVDS_POST2_RATE_SLOW;
403 		} else {
404 			if ((pll & DISPLAY_PLL_DIVIDE_HIGH) != 0)
405 				divisors.post2 = limits.max.post2;
406 			else
407 				divisors.post2 = limits.min.post2;
408 		}
409 	} else {
410 		// 8xx
411 		divisors.post1 = (pll & DISPLAY_PLL_POST1_DIVISOR_MASK)
412 			>> DISPLAY_PLL_POST1_DIVISOR_SHIFT;
413 
414 		if ((pll & DISPLAY_PLL_DIVIDE_4X) != 0)
415 			divisors.post2 = limits.max.post2;
416 		else
417 			divisors.post2 = limits.min.post2;
418 	}
419 
420 	divisors.m = 5 * divisors.m1 + divisors.m2;
421 	divisors.post = divisors.post1 * divisors.post2;
422 
423 	float referenceClock
424 		= gInfo->shared_info->pll_info.reference_frequency / 1000.0f;
425 	float pixelClock
426 		= ((referenceClock * divisors.m) / divisors.n) / divisors.post;
427 
428 	// timing
429 
430 	mode.timing.pixel_clock = uint32(pixelClock * 1000);
431 	mode.timing.flags = 0;
432 
433 	uint32 value = read32(hTotalRegister);
434 	mode.timing.h_total = (value >> 16) + 1;
435 	mode.timing.h_display = (value & 0xffff) + 1;
436 
437 	value = read32(hSyncRegister);
438 	mode.timing.h_sync_end = (value >> 16) + 1;
439 	mode.timing.h_sync_start = (value & 0xffff) + 1;
440 
441 	value = read32(vTotalRegister);
442 	mode.timing.v_total = (value >> 16) + 1;
443 	mode.timing.v_display = (value & 0xffff) + 1;
444 
445 	value = read32(vSyncRegister);
446 	mode.timing.v_sync_end = (value >> 16) + 1;
447 	mode.timing.v_sync_start = (value & 0xffff) + 1;
448 
449 	// image size and color space
450 
451 	value = read32(imageSizeRegister);
452 	mode.virtual_width = (value >> 16) + 1;
453 	mode.virtual_height = (value & 0xffff) + 1;
454 
455 	// using virtual size based on image size is the 'proper' way to do it,
456 	// however the bios appears to be suggesting scaling or somesuch, so ignore
457 	// the proper virtual dimension for now if they'd suggest a smaller size.
458 	if (mode.virtual_width < mode.timing.h_display)
459 		mode.virtual_width = mode.timing.h_display;
460 	if (mode.virtual_height < mode.timing.v_display)
461 		mode.virtual_height = mode.timing.v_display;
462 
463 	value = read32(controlRegister);
464 	switch (value & DISPLAY_CONTROL_COLOR_MASK) {
465 		case DISPLAY_CONTROL_RGB32:
466 		default:
467 			mode.space = B_RGB32;
468 			break;
469 		case DISPLAY_CONTROL_RGB16:
470 			mode.space = B_RGB16;
471 			break;
472 		case DISPLAY_CONTROL_RGB15:
473 			mode.space = B_RGB15;
474 			break;
475 		case DISPLAY_CONTROL_CMAP8:
476 			mode.space = B_CMAP8;
477 			break;
478 	}
479 
480 	mode.h_display_start = 0;
481 	mode.v_display_start = 0;
482 	mode.flags = B_8_BIT_DAC | B_HARDWARE_CURSOR | B_PARALLEL_ACCESS
483 		| B_DPMS | B_SUPPORTS_OVERLAYS;
484 }
485 
486 
487 /*! Store away panel information if identified on startup
488 	(used for pipe B->lvds).
489 */
490 void
491 save_lvds_mode(void)
492 {
493 	// dump currently programmed mode.
494 	display_mode biosMode;
495 	retrieve_current_mode(biosMode, INTEL_DISPLAY_B_PLL);
496 	gInfo->lvds_panel_mode = biosMode;
497 }
498 
499 
500 static void
501 get_color_space_format(const display_mode &mode, uint32 &colorMode,
502 	uint32 &bytesPerRow, uint32 &bitsPerPixel)
503 {
504 	uint32 bytesPerPixel;
505 
506 	switch (mode.space) {
507 		case B_RGB32_LITTLE:
508 			colorMode = DISPLAY_CONTROL_RGB32;
509 			bytesPerPixel = 4;
510 			bitsPerPixel = 32;
511 			break;
512 		case B_RGB16_LITTLE:
513 			colorMode = DISPLAY_CONTROL_RGB16;
514 			bytesPerPixel = 2;
515 			bitsPerPixel = 16;
516 			break;
517 		case B_RGB15_LITTLE:
518 			colorMode = DISPLAY_CONTROL_RGB15;
519 			bytesPerPixel = 2;
520 			bitsPerPixel = 15;
521 			break;
522 		case B_CMAP8:
523 		default:
524 			colorMode = DISPLAY_CONTROL_CMAP8;
525 			bytesPerPixel = 1;
526 			bitsPerPixel = 8;
527 			break;
528 	}
529 
530 	bytesPerRow = mode.virtual_width * bytesPerPixel;
531 
532 	// Make sure bytesPerRow is a multiple of 64
533 	// TODO: check if the older chips have the same restriction!
534 	if ((bytesPerRow & 63) != 0)
535 		bytesPerRow = (bytesPerRow + 63) & ~63;
536 }
537 
538 
539 //	#pragma mark -
540 
541 
542 uint32
543 intel_accelerant_mode_count(void)
544 {
545 	TRACE(("intel_accelerant_mode_count()\n"));
546 	return gInfo->shared_info->mode_count;
547 }
548 
549 
550 status_t
551 intel_get_mode_list(display_mode *modeList)
552 {
553 	TRACE(("intel_get_mode_info()\n"));
554 	memcpy(modeList, gInfo->mode_list,
555 		gInfo->shared_info->mode_count * sizeof(display_mode));
556 	return B_OK;
557 }
558 
559 
560 status_t
561 intel_propose_display_mode(display_mode *target, const display_mode *low,
562 	const display_mode *high)
563 {
564 	TRACE(("intel_propose_display_mode()\n"));
565 
566 	// just search for the specified mode in the list
567 
568 	for (uint32 i = 0; i < gInfo->shared_info->mode_count; i++) {
569 		display_mode *mode = &gInfo->mode_list[i];
570 
571 		// TODO: improve this, ie. adapt pixel clock to allowed values!!!
572 
573 		if (target->virtual_width != mode->virtual_width
574 			|| target->virtual_height != mode->virtual_height
575 			|| target->space != mode->space)
576 			continue;
577 
578 		*target = *mode;
579 		return B_OK;
580 	}
581 	return B_BAD_VALUE;
582 }
583 
584 
585 status_t
586 intel_set_display_mode(display_mode *mode)
587 {
588 	TRACE(("intel_set_display_mode(%ldx%ld)\n", mode->virtual_width,
589 		mode->virtual_height));
590 
591 	if (mode == NULL)
592 		return B_BAD_VALUE;
593 
594 	display_mode target = *mode;
595 	if (intel_propose_display_mode(&target, mode, mode))
596 		return B_BAD_VALUE;
597 
598 	uint32 colorMode, bytesPerRow, bitsPerPixel;
599 	get_color_space_format(target, colorMode, bytesPerRow, bitsPerPixel);
600 
601 #if 0
602 static bool first = true;
603 if (first) {
604 	int fd = open("/boot/home/ie_.regs", O_CREAT | O_WRONLY, 0644);
605 	if (fd >= 0) {
606 		for (int32 i = 0; i < 0x80000; i += 16) {
607 			char line[512];
608 			int length = sprintf(line, "%05lx: %08lx %08lx %08lx %08lx\n",
609 				i, read32(i), read32(i + 4), read32(i + 8), read32(i + 12));
610 			write(fd, line, length);
611 		}
612 		close(fd);
613 		sync();
614 	}
615 	first = false;
616 }
617 #endif
618 
619 	intel_shared_info &sharedInfo = *gInfo->shared_info;
620 	Autolock locker(sharedInfo.accelerant_lock);
621 
622 	// TODO: This may not be neccesary
623 	set_display_power_mode(B_DPMS_OFF);
624 
625 	// free old and allocate new frame buffer in graphics memory
626 
627 	intel_free_memory(sharedInfo.frame_buffer);
628 
629 	uint32 base;
630 	if (intel_allocate_memory(bytesPerRow * target.virtual_height, 0,
631 			base) < B_OK) {
632 		// oh, how did that happen? Unfortunately, there is no really good way back
633 		if (intel_allocate_memory(sharedInfo.current_mode.virtual_height
634 				* sharedInfo.bytes_per_row, 0, base) == B_OK) {
635 			sharedInfo.frame_buffer = base;
636 			sharedInfo.frame_buffer_offset = base
637 				- (addr_t)sharedInfo.graphics_memory;
638 			set_frame_buffer_base();
639 		}
640 
641 		return B_NO_MEMORY;
642 	}
643 
644 	// clear frame buffer before using it
645 	memset((uint8 *)base, 0, bytesPerRow * target.virtual_height);
646 	sharedInfo.frame_buffer = base;
647 	sharedInfo.frame_buffer_offset = base - (addr_t)sharedInfo.graphics_memory;
648 
649 	// make sure VGA display is disabled
650 	write32(INTEL_VGA_DISPLAY_CONTROL, VGA_DISPLAY_DISABLED);
651 	read32(INTEL_VGA_DISPLAY_CONTROL);
652 
653 	if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_85x)) {
654 	}
655 
656 	if ((gInfo->head_mode & HEAD_MODE_B_DIGITAL) != 0) {
657 		pll_divisors divisors;
658 		compute_pll_divisors(target, divisors, true);
659 
660 		uint32 dpll = DISPLAY_PLL_NO_VGA_CONTROL;
661 		if (gInfo->shared_info->device_type.InFamily(INTEL_TYPE_9xx)) {
662 			 dpll |= LVDS_PLL_MODE_LVDS;
663 			 	// DPLL mode LVDS for i915+
664 		}
665 
666 		// compute bitmask from p1 value
667 		dpll |= (1 << (divisors.post1 - 1)) << 16;
668 		switch (divisors.post2) {
669 			case 5:
670 			case 7:
671 				dpll |= DISPLAY_PLL_DIVIDE_HIGH;
672 				break;
673 		}
674 
675 		dpll |= (1 << (divisors.post1 - 1)) << DISPLAY_PLL_POST1_DIVISOR_SHIFT;
676 
677 		uint32 displayControl = ~(DISPLAY_CONTROL_COLOR_MASK
678 			| DISPLAY_CONTROL_GAMMA) | colorMode;
679 		displayControl |= 1 << 24; // select pipe B
680 
681 		// runs in dpms also?
682 		displayControl |= DISPLAY_PIPE_ENABLED;
683 		dpll |= DISPLAY_PLL_ENABLED;
684 
685 		write32(INTEL_PANEL_FIT_CONTROL, 0);
686 
687 		if ((dpll & DISPLAY_PLL_ENABLED) != 0) {
688 			write32(INTEL_DISPLAY_B_PLL_DIVISOR_0,
689 				(((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT)
690 					& DISPLAY_PLL_N_DIVISOR_MASK)
691 				| (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT)
692 					& DISPLAY_PLL_M1_DIVISOR_MASK)
693 				| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
694 					& DISPLAY_PLL_M2_DIVISOR_MASK));
695 			write32(INTEL_DISPLAY_B_PLL, dpll & ~DISPLAY_PLL_ENABLED);
696 			read32(INTEL_DISPLAY_B_PLL);
697 			spin(150);
698 		}
699 
700 		uint32 lvds = read32(INTEL_DISPLAY_LVDS_PORT)
701 			| LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP | LVDS_PIPEB_SELECT;
702 
703 		float referenceClock = gInfo->shared_info->pll_info.reference_frequency
704 			/ 1000.0f;
705 
706 		// Set the B0-B3 data pairs corresponding to whether we're going to
707 		// set the DPLLs for dual-channel mode or not.
708 		if (divisors.post2 == LVDS_POST2_RATE_FAST)
709 			lvds |= LVDS_B0B3PAIRS_POWER_UP | LVDS_CLKB_POWER_UP;
710 		else
711 			lvds &= ~( LVDS_B0B3PAIRS_POWER_UP | LVDS_CLKB_POWER_UP);
712 
713 		write32(INTEL_DISPLAY_LVDS_PORT, lvds);
714 		read32(INTEL_DISPLAY_LVDS_PORT);
715 
716 		write32(INTEL_DISPLAY_B_PLL_DIVISOR_0,
717 			(((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT)
718 				& DISPLAY_PLL_N_DIVISOR_MASK)
719 			| (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT)
720 				& DISPLAY_PLL_M1_DIVISOR_MASK)
721 			| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
722 				& DISPLAY_PLL_M2_DIVISOR_MASK));
723 
724 		write32(INTEL_DISPLAY_B_PLL, dpll);
725 		read32(INTEL_DISPLAY_B_PLL);
726 
727 		// Wait for the clocks to stabilize
728 		spin(150);
729 
730 		if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_96x)) {
731 			float adjusted = ((referenceClock * divisors.m) / divisors.n)
732 				/ divisors.post;
733 			uint32 pixelMultiply = uint32(adjusted
734 				/ (target.timing.pixel_clock / 1000.0f));
735 
736 			write32(INTEL_DISPLAY_B_PLL_MULTIPLIER_DIVISOR, (0 << 24)
737 				| ((pixelMultiply - 1) << 8));
738 		} else
739 			write32(INTEL_DISPLAY_B_PLL, dpll);
740 
741 		read32(INTEL_DISPLAY_B_PLL);
742 		spin(150);
743 
744 		// update timing parameters
745 		write32(INTEL_DISPLAY_B_HTOTAL, ((uint32)(target.timing.h_total - 1) << 16)
746 			| ((uint32)target.timing.h_display - 1));
747 		write32(INTEL_DISPLAY_B_HBLANK, ((uint32)(target.timing.h_total - 1) << 16)
748 			| ((uint32)target.timing.h_display - 1));
749 		write32(INTEL_DISPLAY_B_HSYNC, ((uint32)(target.timing.h_sync_end - 1) << 16)
750 			| ((uint32)target.timing.h_sync_start - 1));
751 
752 		write32(INTEL_DISPLAY_B_VTOTAL, ((uint32)(target.timing.v_total - 1) << 16)
753 			| ((uint32)target.timing.v_display - 1));
754 		write32(INTEL_DISPLAY_B_VBLANK, ((uint32)(target.timing.v_total - 1) << 16)
755 			| ((uint32)target.timing.v_display - 1));
756 		write32(INTEL_DISPLAY_B_VSYNC, ((uint32)(target.timing.v_sync_end - 1) << 16)
757 			| ((uint32)target.timing.v_sync_start - 1));
758 
759 		write32(INTEL_DISPLAY_B_IMAGE_SIZE, ((uint32)(target.timing.h_display - 1) << 16)
760 			| ((uint32)target.timing.v_display - 1));
761 
762 		write32(INTEL_DISPLAY_B_POS, 0);
763 		write32(INTEL_DISPLAY_B_PIPE_SIZE, ((uint32)(target.timing.v_display - 1) << 16)
764 			| ((uint32)target.timing.h_display - 1));
765 
766 		write32(INTEL_DISPLAY_B_PIPE_CONTROL,
767 			read32(INTEL_DISPLAY_B_PIPE_CONTROL) | DISPLAY_PIPE_ENABLED);
768 		read32(INTEL_DISPLAY_B_PIPE_CONTROL);
769 	}
770 
771 	if (gInfo->head_mode & HEAD_MODE_A_ANALOG) {
772 		pll_divisors divisors;
773 		compute_pll_divisors(target, divisors,false);
774 
775 		write32(INTEL_DISPLAY_A_PLL_DIVISOR_0,
776 			(((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT) & DISPLAY_PLL_N_DIVISOR_MASK)
777 			| (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT) & DISPLAY_PLL_M1_DIVISOR_MASK)
778 			| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT) & DISPLAY_PLL_M2_DIVISOR_MASK));
779 
780 		uint32 pll = DISPLAY_PLL_ENABLED | DISPLAY_PLL_NO_VGA_CONTROL;
781 		if (gInfo->shared_info->device_type.InFamily(INTEL_TYPE_9xx)) {
782 			pll |= ((1 << (divisors.post1 - 1))
783 					<< DISPLAY_PLL_POST1_DIVISOR_SHIFT)
784 				& DISPLAY_PLL_9xx_POST1_DIVISOR_MASK;
785 //			pll |= ((divisors.post1 - 1) << DISPLAY_PLL_POST1_DIVISOR_SHIFT)
786 //				& DISPLAY_PLL_9xx_POST1_DIVISOR_MASK;
787 			if (divisors.post2_high)
788 				pll |= DISPLAY_PLL_DIVIDE_HIGH;
789 
790 			pll |= DISPLAY_PLL_MODE_ANALOG;
791 
792 			if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_96x))
793 				pll |= 6 << DISPLAY_PLL_PULSE_PHASE_SHIFT;
794 		} else {
795 			if (!divisors.post2_high)
796 				pll |= DISPLAY_PLL_DIVIDE_4X;
797 
798 			pll |= DISPLAY_PLL_2X_CLOCK;
799 
800 			if (divisors.post1 > 2) {
801 				pll |= (((divisors.post1 - 2) << DISPLAY_PLL_POST1_DIVISOR_SHIFT)
802 					& DISPLAY_PLL_POST1_DIVISOR_MASK);
803 			} else
804 				pll |= DISPLAY_PLL_POST1_DIVIDE_2;
805 		}
806 
807 		write32(INTEL_DISPLAY_A_PLL, pll);
808 		read32(INTEL_DISPLAY_A_PLL);
809 		spin(150);
810 		write32(INTEL_DISPLAY_A_PLL, pll);
811 		read32(INTEL_DISPLAY_A_PLL);
812 		spin(150);
813 
814 		// update timing parameters
815 		write32(INTEL_DISPLAY_A_HTOTAL, ((uint32)(target.timing.h_total - 1) << 16)
816 			| ((uint32)target.timing.h_display - 1));
817 		write32(INTEL_DISPLAY_A_HBLANK, ((uint32)(target.timing.h_total - 1) << 16)
818 			| ((uint32)target.timing.h_display - 1));
819 		write32(INTEL_DISPLAY_A_HSYNC, ((uint32)(target.timing.h_sync_end - 1) << 16)
820 			| ((uint32)target.timing.h_sync_start - 1));
821 
822 		write32(INTEL_DISPLAY_A_VTOTAL, ((uint32)(target.timing.v_total - 1) << 16)
823 			| ((uint32)target.timing.v_display - 1));
824 		write32(INTEL_DISPLAY_A_VBLANK, ((uint32)(target.timing.v_total - 1) << 16)
825 			| ((uint32)target.timing.v_display - 1));
826 		write32(INTEL_DISPLAY_A_VSYNC, ((uint32)(target.timing.v_sync_end - 1) << 16)
827 			| ((uint32)target.timing.v_sync_start - 1));
828 
829 		write32(INTEL_DISPLAY_A_IMAGE_SIZE, ((uint32)(target.timing.h_display - 1) << 16)
830 			| ((uint32)target.timing.v_display - 1));
831 
832 		write32(INTEL_DISPLAY_A_ANALOG_PORT, (read32(INTEL_DISPLAY_A_ANALOG_PORT)
833 			& ~(DISPLAY_MONITOR_POLARITY_MASK | DISPLAY_MONITOR_VGA_POLARITY))
834 			| ((target.timing.flags & B_POSITIVE_HSYNC) != 0 ? DISPLAY_MONITOR_POSITIVE_HSYNC : 0)
835 			| ((target.timing.flags & B_POSITIVE_VSYNC) != 0 ? DISPLAY_MONITOR_POSITIVE_VSYNC : 0));
836 
837 		// TODO: verify the two comments below: the X driver doesn't seem to
838 		//		care about both of them!
839 
840 		// These two have to be set for display B, too - this obviously means
841 		// that the second head always must adopt the color space of the first
842 		// head.
843 		write32(INTEL_DISPLAY_A_CONTROL, (read32(INTEL_DISPLAY_A_CONTROL)
844 			& ~(DISPLAY_CONTROL_COLOR_MASK | DISPLAY_CONTROL_GAMMA)) | colorMode);
845 
846 		if (gInfo->head_mode & HEAD_MODE_B_DIGITAL) {
847 			write32(INTEL_DISPLAY_B_IMAGE_SIZE, ((uint32)(target.timing.h_display - 1) << 16)
848 				| ((uint32)target.timing.v_display - 1));
849 
850 			write32(INTEL_DISPLAY_B_CONTROL, (read32(INTEL_DISPLAY_B_CONTROL)
851 				& ~(DISPLAY_CONTROL_COLOR_MASK | DISPLAY_CONTROL_GAMMA)) | colorMode);
852 		}
853 	}
854 
855 	set_display_power_mode(sharedInfo.dpms_mode);
856 
857 	// changing bytes per row seems to be ignored if the plane/pipe is turned off
858 
859 	if (gInfo->head_mode & HEAD_MODE_A_ANALOG)
860 		write32(INTEL_DISPLAY_A_BYTES_PER_ROW, bytesPerRow);
861 	if (gInfo->head_mode & HEAD_MODE_B_DIGITAL)
862 		write32(INTEL_DISPLAY_B_BYTES_PER_ROW, bytesPerRow);
863 
864 	set_frame_buffer_base();
865 		// triggers writing back double-buffered registers
866 
867 	// update shared info
868 	sharedInfo.bytes_per_row = bytesPerRow;
869 	sharedInfo.current_mode = target;
870 	sharedInfo.bits_per_pixel = bitsPerPixel;
871 
872 	return B_OK;
873 }
874 
875 
876 status_t
877 intel_get_display_mode(display_mode *_currentMode)
878 {
879 	TRACE(("intel_get_display_mode()\n"));
880 
881 	retrieve_current_mode(*_currentMode, INTEL_DISPLAY_A_PLL);
882 	return B_OK;
883 }
884 
885 
886 status_t
887 intel_get_edid_info(void* info, size_t size, uint32* _version)
888 {
889 	TRACE(("intel_get_edid_info()\n"));
890 
891 	if (!gInfo->has_edid)
892 		return B_ERROR;
893 	if (size < sizeof(struct edid1_info))
894 		return B_BUFFER_OVERFLOW;
895 
896 	memcpy(info, &gInfo->edid_info, sizeof(struct edid1_info));
897 	*_version = EDID_VERSION_1;
898 	return B_OK;
899 }
900 
901 
902 status_t
903 intel_get_frame_buffer_config(frame_buffer_config *config)
904 {
905 	TRACE(("intel_get_frame_buffer_config()\n"));
906 
907 	uint32 offset = gInfo->shared_info->frame_buffer_offset;
908 
909 	config->frame_buffer = gInfo->shared_info->graphics_memory + offset;
910 	config->frame_buffer_dma
911 		= (uint8 *)gInfo->shared_info->physical_graphics_memory + offset;
912 	config->bytes_per_row = gInfo->shared_info->bytes_per_row;
913 
914 	return B_OK;
915 }
916 
917 
918 status_t
919 intel_get_pixel_clock_limits(display_mode *mode, uint32 *_low, uint32 *_high)
920 {
921 	TRACE(("intel_get_pixel_clock_limits()\n"));
922 
923 	if (_low != NULL) {
924 		// lower limit of about 48Hz vertical refresh
925 		uint32 totalClocks = (uint32)mode->timing.h_total * (uint32)mode->timing.v_total;
926 		uint32 low = (totalClocks * 48L) / 1000L;
927 		if (low < gInfo->shared_info->pll_info.min_frequency)
928 			low = gInfo->shared_info->pll_info.min_frequency;
929 		else if (low > gInfo->shared_info->pll_info.max_frequency)
930 			return B_ERROR;
931 
932 		*_low = low;
933 	}
934 
935 	if (_high != NULL)
936 		*_high = gInfo->shared_info->pll_info.max_frequency;
937 
938 	return B_OK;
939 }
940 
941 
942 status_t
943 intel_move_display(uint16 horizontalStart, uint16 verticalStart)
944 {
945 	TRACE(("intel_move_display()\n"));
946 
947 	intel_shared_info &sharedInfo = *gInfo->shared_info;
948 	Autolock locker(sharedInfo.accelerant_lock);
949 
950 	display_mode &mode = sharedInfo.current_mode;
951 
952 	if (horizontalStart + mode.timing.h_display > mode.virtual_width
953 		|| verticalStart + mode.timing.v_display > mode.virtual_height)
954 		return B_BAD_VALUE;
955 
956 	mode.h_display_start = horizontalStart;
957 	mode.v_display_start = verticalStart;
958 
959 	set_frame_buffer_base();
960 
961 	return B_OK;
962 }
963 
964 
965 status_t
966 intel_get_timing_constraints(display_timing_constraints *constraints)
967 {
968 	TRACE(("intel_get_timing_contraints()\n"));
969 	return B_ERROR;
970 }
971 
972 
973 void
974 intel_set_indexed_colors(uint count, uint8 first, uint8 *colors, uint32 flags)
975 {
976 	TRACE(("intel_set_indexed_colors(colors = %p, first = %u)\n", colors, first));
977 
978 	if (colors == NULL)
979 		return;
980 
981 	Autolock locker(gInfo->shared_info->accelerant_lock);
982 
983 	for (; count-- > 0; first++) {
984 		uint32 color = colors[0] << 16 | colors[1] << 8 | colors[2];
985 		colors += 3;
986 
987 		if (gInfo->head_mode & HEAD_MODE_A_ANALOG)
988 			write32(INTEL_DISPLAY_A_PALETTE + first * sizeof(uint32), color);
989 		if (gInfo->head_mode & HEAD_MODE_B_DIGITAL)
990 			write32(INTEL_DISPLAY_B_PALETTE + first * sizeof(uint32), color);
991 	}
992 }
993 
994