xref: /haiku/src/add-ons/accelerants/intel_extreme/mode.cpp (revision 1b8f7f13a3dc70e0e903cb94248220b40b732204)
1 /*
2  * Copyright 2006-2007, 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 == (INTEL_TYPE_8xx | 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 == (INTEL_TYPE_9xx | INTEL_TYPE_965)) {
134 		write32(baseRegister, mode.v_display_start * sharedInfo.bytes_per_row
135 			+ mode.h_display_start * (sharedInfo.bits_per_pixel + 7) / 8);
136 		read32(baseRegister);
137 		write32(surfaceRegister, sharedInfo.frame_buffer_offset);
138 		read32(surfaceRegister);
139 	} else {
140 		write32(baseRegister, sharedInfo.frame_buffer_offset
141 			+ mode.v_display_start * sharedInfo.bytes_per_row
142 			+ mode.h_display_start * (sharedInfo.bits_per_pixel + 7) / 8);
143 		read32(baseRegister);
144 	}
145 }
146 
147 
148 /*!	Creates the initial mode list of the primary accelerant.
149 	It's called from intel_init_accelerant().
150 */
151 status_t
152 create_mode_list(void)
153 {
154 	i2c_bus bus;
155 	bus.cookie = (void*)INTEL_I2C_IO_A;
156 	bus.set_signals = &set_i2c_signals;
157 	bus.get_signals = &get_i2c_signals;
158 	ddc2_init_timing(&bus);
159 
160 	if (ddc2_read_edid1(&bus, &gInfo->edid_info, NULL, NULL) == B_OK) {
161 		edid_dump(&gInfo->edid_info);
162 		gInfo->has_edid = true;
163 	} else {
164 		TRACE(("intel_extreme: getting EDID failed!\n"));
165 	}
166 
167 	display_mode *list;
168 	uint32 count = 0;
169 	gInfo->mode_list_area = create_display_modes("intel extreme modes",
170 		gInfo->has_edid ? &gInfo->edid_info : NULL, NULL, 0, NULL, 0, NULL,
171 		&list, &count);
172 	if (gInfo->mode_list_area < B_OK)
173 		return gInfo->mode_list_area;
174 
175 	gInfo->mode_list = list;
176 	gInfo->shared_info->mode_list_area = gInfo->mode_list_area;
177 	gInfo->shared_info->mode_count = count;
178 
179 	return B_OK;
180 }
181 
182 
183 void
184 wait_for_vblank(void)
185 {
186 	acquire_sem_etc(gInfo->shared_info->vblank_sem, 1, B_RELATIVE_TIMEOUT, 25000);
187 		// With the output turned off via DPMS, we might not get any interrupts anymore
188 		// that's why we don't wait forever for it.
189 }
190 
191 
192 static void
193 get_pll_limits(pll_limits &limits)
194 {
195 	// Note, the limits are taken from the X driver; they have not yet been tested
196 
197 	if ((gInfo->shared_info->device_type & INTEL_TYPE_9xx) != 0) {
198 		// TODO: support LVDS output limits as well
199 		static const pll_limits kLimits = {
200 			// p, p1, p2, high,   n,   m, m1, m2
201 			{  5,  1, 10, false,  5,  70, 12,  7},	// min
202 			{ 80,  8,  5, true,  10, 120, 22, 11},	// max
203 			200000, 1400000, 2800000
204 		};
205 		limits = kLimits;
206 	} else {
207 		// TODO: support LVDS output limits as well
208 		static const pll_limits kLimits = {
209 			// p, p1, p2, high,   n,   m, m1, m2
210 			{  4,  2,  4, false,  5,  96, 20,  8},
211 			{128, 33,  2, true,  18, 140, 28, 18},
212 			165000, 930000, 1400000
213 		};
214 		limits = kLimits;
215 	}
216 
217 	TRACE(("PLL limits, min: p %lu (p1 %lu, p2 %lu), n %lu, m %lu (m1 %lu, m2 %lu)\n",
218 		limits.min.post, limits.min.post1, limits.min.post2, limits.min.n,
219 		limits.min.m, limits.min.m1, limits.min.m2));
220 	TRACE(("PLL limits, max: p %lu (p1 %lu, p2 %lu), n %lu, m %lu (m1 %lu, m2 %lu)\n",
221 		limits.max.post, limits.max.post1, limits.max.post2, limits.max.n,
222 		limits.max.m, limits.max.m1, limits.max.m2));
223 }
224 
225 
226 static bool
227 valid_pll_divisors(const pll_divisors& divisors, const pll_limits& limits)
228 {
229 	pll_info &info = gInfo->shared_info->pll_info;
230 	uint32 vco = info.reference_frequency * divisors.m / divisors.n;
231 	uint32 frequency = vco / divisors.post;
232 
233 	if (divisors.post < limits.min.post || divisors.post > limits.max.post
234 		|| divisors.m < limits.min.m || divisors.m > limits.max.m
235 		|| vco < limits.min_vco || vco > limits.max_vco
236 		|| frequency < info.min_frequency || frequency > info.max_frequency)
237 		return false;
238 
239 	return true;
240 }
241 
242 
243 static void
244 compute_pll_divisors(const display_mode &current, pll_divisors& divisors)
245 {
246 	float requestedPixelClock = current.timing.pixel_clock / 1000.0f;
247 	float referenceClock = gInfo->shared_info->pll_info.reference_frequency / 1000.0f;
248 	pll_limits limits;
249 	get_pll_limits(limits);
250 
251 	TRACE(("required MHz: %g\n", requestedPixelClock));
252 
253 	if (current.timing.pixel_clock < limits.min_post2_frequency) {
254 		// slow DAC timing
255 	    divisors.post2 = limits.min.post2;
256 	    divisors.post2_high = limits.min.post2_high;
257 	} else {
258 		// fast DAC timing
259 	    divisors.post2 = limits.max.post2;
260 	    divisors.post2_high = limits.max.post2_high;
261 	}
262 
263 	float best = requestedPixelClock;
264 	pll_divisors bestDivisors;
265 
266 	for (divisors.m1 = limits.min.m1; divisors.m1 <= limits.max.m1; divisors.m1++) {
267 		for (divisors.m2 = limits.min.m2; divisors.m2 < divisors.m1
268 				&& divisors.m2 <= limits.max.m2; divisors.m2++) {
269 			for (divisors.n = limits.min.n; divisors.n <= limits.max.n;
270 					divisors.n++) {
271 				for (divisors.post1 = limits.min.post1;
272 						divisors.post1 <= limits.max.post1; divisors.post1++) {
273 					divisors.m = 5 * divisors.m1 + divisors.m2;
274 					divisors.post = divisors.post1 * divisors.post2;
275 
276 					if (!valid_pll_divisors(divisors, limits))
277 						continue;
278 
279 					float error = fabs(requestedPixelClock
280 						- ((referenceClock * divisors.m) / divisors.n) / divisors.post);
281 					if (error < best) {
282 						best = error;
283 						bestDivisors = divisors;
284 
285 						if (error == 0)
286 							break;
287 					}
288 				}
289 			}
290 		}
291 	}
292 
293 	divisors = bestDivisors;
294 
295 	TRACE(("found: %g MHz, p = %lu (p1 = %lu, p2 = %lu), n = %lu, m = %lu (m1 = %lu, m2 = %lu)\n",
296 		((referenceClock * divisors.m) / divisors.n) / divisors.post,
297 		divisors.post, divisors.post1, divisors.post2, divisors.n,
298 		divisors.m, divisors.m1, divisors.m2));
299 }
300 
301 
302 static void
303 get_color_space_format(const display_mode &mode, uint32 &colorMode,
304 	uint32 &bytesPerRow, uint32 &bitsPerPixel)
305 {
306 	uint32 bytesPerPixel;
307 
308 	switch (mode.space) {
309 		case B_RGB32_LITTLE:
310 			colorMode = DISPLAY_CONTROL_RGB32;
311 			bytesPerPixel = 4;
312 			bitsPerPixel = 32;
313 			break;
314 		case B_RGB16_LITTLE:
315 			colorMode = DISPLAY_CONTROL_RGB16;
316 			bytesPerPixel = 2;
317 			bitsPerPixel = 16;
318 			break;
319 		case B_RGB15_LITTLE:
320 			colorMode = DISPLAY_CONTROL_RGB15;
321 			bytesPerPixel = 2;
322 			bitsPerPixel = 15;
323 			break;
324 		case B_CMAP8:
325 		default:
326 			colorMode = DISPLAY_CONTROL_CMAP8;
327 			bytesPerPixel = 1;
328 			bitsPerPixel = 8;
329 			break;
330 	}
331 
332 	bytesPerRow = mode.virtual_width * bytesPerPixel;
333 }
334 
335 
336 //	#pragma mark -
337 
338 
339 uint32
340 intel_accelerant_mode_count(void)
341 {
342 	TRACE(("intel_accelerant_mode_count()\n"));
343 	return gInfo->shared_info->mode_count;
344 }
345 
346 
347 status_t
348 intel_get_mode_list(display_mode *modeList)
349 {
350 	TRACE(("intel_get_mode_info()\n"));
351 	memcpy(modeList, gInfo->mode_list,
352 		gInfo->shared_info->mode_count * sizeof(display_mode));
353 	return B_OK;
354 }
355 
356 
357 status_t
358 intel_propose_display_mode(display_mode *target, const display_mode *low,
359 	const display_mode *high)
360 {
361 	TRACE(("intel_propose_display_mode()\n"));
362 
363 	// just search for the specified mode in the list
364 
365 	for (uint32 i = 0; i < gInfo->shared_info->mode_count; i++) {
366 		display_mode *mode = &gInfo->mode_list[i];
367 
368 		// TODO: improve this, ie. adapt pixel clock to allowed values!!!
369 
370 		if (target->virtual_width != mode->virtual_width
371 			|| target->virtual_height != mode->virtual_height
372 			|| target->space != mode->space)
373 			continue;
374 
375 		*target = *mode;
376 		return B_OK;
377 	}
378 	return B_BAD_VALUE;
379 }
380 
381 
382 status_t
383 intel_set_display_mode(display_mode *mode)
384 {
385 	TRACE(("intel_set_display_mode()\n"));
386 
387 	display_mode target = *mode;
388 
389 	if (mode == NULL || intel_propose_display_mode(&target, mode, mode))
390 		return B_BAD_VALUE;
391 
392 	uint32 colorMode, bytesPerRow, bitsPerPixel;
393 	get_color_space_format(target, colorMode, bytesPerRow, bitsPerPixel);
394 
395 #if 0
396 static bool first = true;
397 if (first) {
398 	int fd = open("/boot/home/ie_.regs", O_CREAT | O_WRONLY, 0644);
399 	if (fd >= 0) {
400 		for (int32 i = 0; i < 0x80000; i += 16) {
401 			char line[512];
402 			int length = sprintf(line, "%05lx: %08lx %08lx %08lx %08lx\n",
403 				i, read32(i), read32(i + 4), read32(i + 8), read32(i + 12));
404 			write(fd, line, length);
405 		}
406 		close(fd);
407 		sync();
408 	}
409 	first = false;
410 }
411 #endif
412 
413 	intel_shared_info &sharedInfo = *gInfo->shared_info;
414 	Autolock locker(sharedInfo.accelerant_lock);
415 
416 	set_display_power_mode(B_DPMS_OFF);
417 
418 	// free old and allocate new frame buffer in graphics memory
419 
420 	intel_free_memory(gInfo->frame_buffer_handle);
421 
422 	uint32 offset;
423 	if (intel_allocate_memory(bytesPerRow * target.virtual_height,
424 			gInfo->frame_buffer_handle, offset) < B_OK) {
425 		// oh, how did that happen? Unfortunately, there is no really good way back
426 		if (intel_allocate_memory(sharedInfo.current_mode.virtual_height
427 				* sharedInfo.bytes_per_row, gInfo->frame_buffer_handle,
428 				offset) == B_OK) {
429 			sharedInfo.frame_buffer_offset = offset;
430 			set_frame_buffer_base();
431 		}
432 
433 		return B_NO_MEMORY;
434 	}
435 
436 	sharedInfo.frame_buffer_offset = offset;
437 
438 	// make sure VGA display is disabled
439 	write32(INTEL_VGA_DISPLAY_CONTROL, VGA_DISPLAY_DISABLED);
440 	read32(INTEL_VGA_DISPLAY_CONTROL);
441 
442 	if (gInfo->shared_info->device_type != (INTEL_TYPE_8xx | INTEL_TYPE_85x)) {
443 	}
444 
445 	if (gInfo->head_mode & HEAD_MODE_A_ANALOG) {
446 		pll_divisors divisors;
447 		compute_pll_divisors(target, divisors);
448 
449 		write32(INTEL_DISPLAY_A_PLL_DIVISOR_0,
450 			(((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT) & DISPLAY_PLL_N_DIVISOR_MASK)
451 			| (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT) & DISPLAY_PLL_M1_DIVISOR_MASK)
452 			| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT) & DISPLAY_PLL_M2_DIVISOR_MASK));
453 
454 		uint32 pll = DISPLAY_PLL_ENABLED | DISPLAY_PLL_NO_VGA_CONTROL;
455 		if ((gInfo->shared_info->device_type & INTEL_TYPE_9xx) != 0) {
456 			pll |= ((1 << (divisors.post1 - 1)) << DISPLAY_PLL_POST1_DIVISOR_SHIFT)
457 				& DISPLAY_PLL_9xx_POST1_DIVISOR_MASK;
458 //			pll |= ((divisors.post1 - 1) << DISPLAY_PLL_POST1_DIVISOR_SHIFT)
459 //				& DISPLAY_PLL_9xx_POST1_DIVISOR_MASK;
460 			if (divisors.post2_high)
461 				pll |= DISPLAY_PLL_DIVIDE_HIGH;
462 
463 			pll |= DISPLAY_PLL_MODE_ANALOG;
464 
465 			if ((gInfo->shared_info->device_type & INTEL_TYPE_GROUP_MASK) == INTEL_TYPE_965)
466 				pll |= 6 << DISPLAY_PLL_PULSE_PHASE_SHIFT;
467 		} else {
468 			if (!divisors.post2_high)
469 				pll |= DISPLAY_PLL_DIVIDE_4X;
470 
471 			pll |= DISPLAY_PLL_2X_CLOCK;
472 
473 			if (divisors.post1 > 2) {
474 				pll |= (((divisors.post1 - 2) << DISPLAY_PLL_POST1_DIVISOR_SHIFT)
475 					& DISPLAY_PLL_POST1_DIVISOR_MASK);
476 			} else
477 				pll |= DISPLAY_PLL_POST1_DIVIDE_2;
478 		}
479 
480 		write32(INTEL_DISPLAY_A_PLL, pll);
481 		read32(INTEL_DISPLAY_A_PLL);
482 		spin(150);
483 		write32(INTEL_DISPLAY_A_PLL, pll);
484 		read32(INTEL_DISPLAY_A_PLL);
485 		spin(150);
486 #if 0
487 		write32(INTEL_DISPLAY_A_PLL, DISPLAY_PLL_ENABLED | DISPLAY_PLL_2X_CLOCK
488 			| DISPLAY_PLL_NO_VGA_CONTROL | DISPLAY_PLL_DIVIDE_4X
489 			| (((divisors.post1 - 2) << DISPLAY_PLL_POST1_DIVISOR_SHIFT) & DISPLAY_PLL_POST1_DIVISOR_MASK)
490 			| (divisorRegister == INTEL_DISPLAY_A_PLL_DIVISOR_1 ? DISPLAY_PLL_DIVISOR_1 : 0));
491 #endif
492 		// update timing parameters
493 		write32(INTEL_DISPLAY_A_HTOTAL, ((uint32)(target.timing.h_total - 1) << 16)
494 			| ((uint32)target.timing.h_display - 1));
495 		write32(INTEL_DISPLAY_A_HBLANK, ((uint32)(target.timing.h_total - 1) << 16)
496 			| ((uint32)target.timing.h_display - 1));
497 		write32(INTEL_DISPLAY_A_HSYNC, ((uint32)(target.timing.h_sync_end - 1) << 16)
498 			| ((uint32)target.timing.h_sync_start - 1));
499 
500 		write32(INTEL_DISPLAY_A_VTOTAL, ((uint32)(target.timing.v_total - 1) << 16)
501 			| ((uint32)target.timing.v_display - 1));
502 		write32(INTEL_DISPLAY_A_VBLANK, ((uint32)(target.timing.v_total - 1) << 16)
503 			| ((uint32)target.timing.v_display - 1));
504 		write32(INTEL_DISPLAY_A_VSYNC, ((uint32)(target.timing.v_sync_end - 1) << 16)
505 			| ((uint32)target.timing.v_sync_start - 1));
506 
507 		write32(INTEL_DISPLAY_A_IMAGE_SIZE, ((uint32)(target.timing.h_display - 1) << 16)
508 			| ((uint32)target.timing.v_display - 1));
509 
510 		write32(INTEL_DISPLAY_A_ANALOG_PORT, (read32(INTEL_DISPLAY_A_ANALOG_PORT)
511 			& ~(DISPLAY_MONITOR_POLARITY_MASK | DISPLAY_MONITOR_VGA_POLARITY))
512 			| ((target.timing.flags & B_POSITIVE_HSYNC) != 0 ? DISPLAY_MONITOR_POSITIVE_HSYNC : 0)
513 			| ((target.timing.flags & B_POSITIVE_VSYNC) != 0 ? DISPLAY_MONITOR_POSITIVE_VSYNC : 0));
514 	}
515 
516 	// These two have to be set for display B, too - this obviously means
517 	// that the second head always must adopt the color space of the first
518 	// head.
519 	write32(INTEL_DISPLAY_A_CONTROL, (read32(INTEL_DISPLAY_A_CONTROL)
520 		& ~(DISPLAY_CONTROL_COLOR_MASK | DISPLAY_CONTROL_GAMMA)) | colorMode);
521 
522 	if (gInfo->head_mode & HEAD_MODE_B_DIGITAL) {
523 		write32(INTEL_DISPLAY_B_IMAGE_SIZE, ((uint32)(target.timing.h_display - 1) << 16)
524 			| ((uint32)target.timing.v_display - 1));
525 
526 		write32(INTEL_DISPLAY_B_CONTROL, (read32(INTEL_DISPLAY_B_CONTROL)
527 			& ~(DISPLAY_CONTROL_COLOR_MASK | DISPLAY_CONTROL_GAMMA)) | colorMode);
528 	}
529 
530 	set_display_power_mode(sharedInfo.dpms_mode);
531 
532 	// changing bytes per row seems to be ignored if the plane/pipe is turned off
533 
534 	if (gInfo->head_mode & HEAD_MODE_A_ANALOG)
535 		write32(INTEL_DISPLAY_A_BYTES_PER_ROW, bytesPerRow);
536 	if (gInfo->head_mode & HEAD_MODE_B_DIGITAL)
537 		write32(INTEL_DISPLAY_B_BYTES_PER_ROW, bytesPerRow);
538 
539 	set_frame_buffer_base();
540 		// triggers writing back double-buffered registers
541 
542 	// update shared info
543 	sharedInfo.bytes_per_row = bytesPerRow;
544 	sharedInfo.current_mode = target;
545 	sharedInfo.bits_per_pixel = bitsPerPixel;
546 
547 	return B_OK;
548 }
549 
550 
551 status_t
552 intel_get_display_mode(display_mode *_currentMode)
553 {
554 	TRACE(("intel_get_display_mode()\n"));
555 
556 	display_mode &mode = *_currentMode;
557 
558 	uint32 pll = read32(INTEL_DISPLAY_A_PLL);
559 	uint32 pllDivisor = read32((pll & DISPLAY_PLL_DIVISOR_1) != 0
560 		? INTEL_DISPLAY_A_PLL_DIVISOR_1 : INTEL_DISPLAY_A_PLL_DIVISOR_0);
561 
562 	pll_divisors divisors;
563 	divisors.m1 = (pllDivisor & DISPLAY_PLL_M1_DIVISOR_MASK)
564 		>> DISPLAY_PLL_M1_DIVISOR_SHIFT;
565 	divisors.m2 = (pllDivisor & DISPLAY_PLL_M2_DIVISOR_MASK)
566 		>> DISPLAY_PLL_M2_DIVISOR_SHIFT;
567 	divisors.n = (pllDivisor & DISPLAY_PLL_N_DIVISOR_MASK)
568 		>> DISPLAY_PLL_N_DIVISOR_SHIFT;
569 
570 	pll_limits limits;
571 	get_pll_limits(limits);
572 
573 	if ((gInfo->shared_info->device_type & INTEL_TYPE_9xx) != 0) {
574 		divisors.post1 = (pll & DISPLAY_PLL_9xx_POST1_DIVISOR_MASK)
575 			>> DISPLAY_PLL_POST1_DIVISOR_SHIFT;
576 
577 		if ((pll & DISPLAY_PLL_DIVIDE_HIGH) != 0)
578 			divisors.post2 = limits.max.post2;
579 		else
580 			divisors.post2 = limits.min.post2;
581 	} else {
582 		// 8xx
583 		divisors.post1 = (pll & DISPLAY_PLL_POST1_DIVISOR_MASK)
584 			>> DISPLAY_PLL_POST1_DIVISOR_SHIFT;
585 
586 		if ((pll & DISPLAY_PLL_DIVIDE_4X) != 0)
587 			divisors.post2 = limits.max.post2;
588 		else
589 			divisors.post2 = limits.min.post2;
590 	}
591 
592 	divisors.m = 5 * divisors.m1 + divisors.m2;
593 	divisors.post = divisors.post1 * divisors.post2;
594 
595 	float referenceClock = gInfo->shared_info->pll_info.reference_frequency / 1000.0f;
596 	float pixelClock = ((referenceClock * divisors.m) / divisors.n) / divisors.post;
597 
598 	// timing
599 
600 	mode.timing.pixel_clock = uint32(pixelClock * 1000);
601 	mode.timing.flags = 0;
602 
603 	uint32 value = read32(INTEL_DISPLAY_A_HTOTAL);
604 	mode.timing.h_total = (value >> 16) + 1;
605 	mode.timing.h_display = (value & 0xffff) + 1;
606 
607 	value = read32(INTEL_DISPLAY_A_HSYNC);
608 	mode.timing.h_sync_end = (value >> 16) + 1;
609 	mode.timing.h_sync_start = (value & 0xffff) + 1;
610 
611 	value = read32(INTEL_DISPLAY_A_VTOTAL);
612 	mode.timing.v_total = (value >> 16) + 1;
613 	mode.timing.v_display = (value & 0xffff) + 1;
614 
615 	value = read32(INTEL_DISPLAY_A_VSYNC);
616 	mode.timing.v_sync_end = (value >> 16) + 1;
617 	mode.timing.v_sync_start = (value & 0xffff) + 1;
618 
619 	// image size and color space
620 
621 	value = read32(INTEL_DISPLAY_A_IMAGE_SIZE);
622 	mode.virtual_width = (value >> 16) + 1;
623 	mode.virtual_height = (value & 0xffff) + 1;
624 
625 	value = read32(INTEL_DISPLAY_A_CONTROL);
626 	switch (value & DISPLAY_CONTROL_COLOR_MASK) {
627 		case DISPLAY_CONTROL_RGB32:
628 		default:
629 			mode.space = B_RGB32;
630 			break;
631 		case DISPLAY_CONTROL_RGB16:
632 			mode.space = B_RGB16;
633 			break;
634 		case DISPLAY_CONTROL_RGB15:
635 			mode.space = B_RGB15;
636 			break;
637 		case DISPLAY_CONTROL_CMAP8:
638 			mode.space = B_CMAP8;
639 			break;
640 	}
641 
642 	mode.h_display_start = 0;
643 	mode.v_display_start = 0;
644 	mode.flags = 0;
645 	return B_OK;
646 }
647 
648 #ifdef __HAIKU__
649 
650 status_t
651 intel_get_edid_info(void* info, size_t size, uint32* _version)
652 {
653 	if (!gInfo->has_edid)
654 		return B_ERROR;
655 	if (size < sizeof(struct edid1_info))
656 		return B_BUFFER_OVERFLOW;
657 
658 	memcpy(info, &gInfo->edid_info, sizeof(struct edid1_info));
659 	*_version = EDID_VERSION_1;
660 	return B_OK;
661 }
662 
663 #endif	// __HAIKU__
664 
665 status_t
666 intel_get_frame_buffer_config(frame_buffer_config *config)
667 {
668 	TRACE(("intel_get_frame_buffer_config()\n"));
669 
670 	uint32 offset = gInfo->shared_info->frame_buffer_offset;
671 
672 	config->frame_buffer = gInfo->shared_info->graphics_memory + offset;
673 	config->frame_buffer_dma = gInfo->shared_info->physical_graphics_memory + offset;
674 	config->bytes_per_row = gInfo->shared_info->bytes_per_row;
675 
676 	return B_OK;
677 }
678 
679 
680 status_t
681 intel_get_pixel_clock_limits(display_mode *mode, uint32 *_low, uint32 *_high)
682 {
683 	TRACE(("intel_get_pixel_clock_limits()\n"));
684 
685 	if (_low != NULL) {
686 		// lower limit of about 48Hz vertical refresh
687 		uint32 totalClocks = (uint32)mode->timing.h_total * (uint32)mode->timing.v_total;
688 		uint32 low = (totalClocks * 48L) / 1000L;
689 		if (low < gInfo->shared_info->pll_info.min_frequency)
690 			low = gInfo->shared_info->pll_info.min_frequency;
691 		else if (low > gInfo->shared_info->pll_info.max_frequency)
692 			return B_ERROR;
693 
694 		*_low = low;
695 	}
696 
697 	if (_high != NULL)
698 		*_high = gInfo->shared_info->pll_info.max_frequency;
699 
700 	return B_OK;
701 }
702 
703 
704 status_t
705 intel_move_display(uint16 horizontalStart, uint16 verticalStart)
706 {
707 	TRACE(("intel_move_display()\n"));
708 
709 	intel_shared_info &sharedInfo = *gInfo->shared_info;
710 	Autolock locker(sharedInfo.accelerant_lock);
711 
712 	display_mode &mode = sharedInfo.current_mode;
713 
714 	if (horizontalStart + mode.timing.h_display > mode.virtual_width
715 		|| verticalStart + mode.timing.v_display > mode.virtual_height)
716 		return B_BAD_VALUE;
717 
718 	mode.h_display_start = horizontalStart;
719 	mode.v_display_start = verticalStart;
720 
721 	set_frame_buffer_base();
722 
723 	return B_OK;
724 }
725 
726 
727 status_t
728 intel_get_timing_constraints(display_timing_constraints *constraints)
729 {
730 	TRACE(("intel_get_timing_contraints()\n"));
731 	return B_ERROR;
732 }
733 
734 
735 void
736 intel_set_indexed_colors(uint count, uint8 first, uint8 *colors, uint32 flags)
737 {
738 	TRACE(("intel_set_indexed_colors(colors = %p, first = %u)\n", colors, first));
739 
740 	if (colors == NULL)
741 		return;
742 
743 	Autolock locker(gInfo->shared_info->accelerant_lock);
744 
745 	for (; count-- > 0; first++) {
746 		uint32 color = colors[0] << 16 | colors[1] << 8 | colors[2];
747 		colors += 3;
748 
749 		if (gInfo->head_mode & HEAD_MODE_A_ANALOG)
750 			write32(INTEL_DISPLAY_A_PALETTE + first * sizeof(uint32), color);
751 		if (gInfo->head_mode & HEAD_MODE_B_DIGITAL)
752 			write32(INTEL_DISPLAY_B_PALETTE + first * sizeof(uint32), color);
753 	}
754 }
755 
756