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