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