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