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