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