xref: /haiku/src/add-ons/accelerants/intel_extreme/mode.cpp (revision 1deede7388b04dbeec5af85cae7164735ea9e70d)
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 <math.h>
14 #include <stdio.h>
15 #include <string.h>
16 
17 #include <Debug.h>
18 
19 #include <create_display_modes.h>
20 #include <ddc.h>
21 #include <edid.h>
22 #include <validate_display_mode.h>
23 
24 #include "accelerant_protos.h"
25 #include "accelerant.h"
26 #include "pll.h"
27 #include "Ports.h"
28 #include "utility.h"
29 
30 
31 #undef TRACE
32 #define TRACE_MODE
33 #ifdef TRACE_MODE
34 #	define TRACE(x...) _sPrintf("intel_extreme: " x)
35 #else
36 #	define TRACE(x...)
37 #endif
38 
39 #define ERROR(x...) _sPrintf("intel_extreme: " x)
40 #define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
41 
42 
43 static void
44 get_color_space_format(const display_mode &mode, uint32 &colorMode,
45 	uint32 &bytesPerRow, uint32 &bitsPerPixel)
46 {
47 	uint32 bytesPerPixel;
48 
49 	switch (mode.space) {
50 		case B_RGB32_LITTLE:
51 			colorMode = DISPLAY_CONTROL_RGB32;
52 			bytesPerPixel = 4;
53 			bitsPerPixel = 32;
54 			break;
55 		case B_RGB16_LITTLE:
56 			colorMode = DISPLAY_CONTROL_RGB16;
57 			bytesPerPixel = 2;
58 			bitsPerPixel = 16;
59 			break;
60 		case B_RGB15_LITTLE:
61 			colorMode = DISPLAY_CONTROL_RGB15;
62 			bytesPerPixel = 2;
63 			bitsPerPixel = 15;
64 			break;
65 		case B_CMAP8:
66 		default:
67 			colorMode = DISPLAY_CONTROL_CMAP8;
68 			bytesPerPixel = 1;
69 			bitsPerPixel = 8;
70 			break;
71 	}
72 
73 	bytesPerRow = mode.virtual_width * bytesPerPixel;
74 
75 	// Make sure bytesPerRow is a multiple of 64
76 	if ((bytesPerRow & 63) != 0)
77 		bytesPerRow = (bytesPerRow + 63) & ~63;
78 }
79 
80 
81 static bool
82 sanitize_display_mode(display_mode& mode)
83 {
84 	uint16 pixelCount = 1;
85 	// Older cards require pixel count to be even
86 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_Gxx)
87 			|| gInfo->shared_info->device_type.InGroup(INTEL_GROUP_96x)
88 			|| gInfo->shared_info->device_type.InGroup(INTEL_GROUP_94x)
89 			|| gInfo->shared_info->device_type.InGroup(INTEL_GROUP_91x)
90 			|| gInfo->shared_info->device_type.InFamily(INTEL_FAMILY_8xx)) {
91 		pixelCount = 2;
92 	}
93 
94 	display_constraints constraints = {
95 		// resolution
96 		320, 4096, 200, 4096,
97 		// pixel clock
98 		gInfo->shared_info->pll_info.min_frequency,
99 		gInfo->shared_info->pll_info.max_frequency,
100 		// horizontal
101 		{pixelCount, 0, 8160, 32, 8192, 0, 8192},
102 		{1, 1, 8190, 2, 8192, 1, 8192}
103 	};
104 
105 	return sanitize_display_mode(mode, constraints,
106 		gInfo->has_edid ? &gInfo->edid_info : NULL);
107 }
108 
109 
110 // #pragma mark -
111 
112 
113 static void
114 set_frame_buffer_registers(uint32 baseRegister, uint32 surfaceRegister)
115 {
116 	intel_shared_info &sharedInfo = *gInfo->shared_info;
117 	display_mode &mode = gInfo->current_mode;
118 
119 	if (sharedInfo.device_type.InGroup(INTEL_GROUP_96x)
120 		|| sharedInfo.device_type.InGroup(INTEL_GROUP_G4x)
121 		|| sharedInfo.device_type.InGroup(INTEL_GROUP_ILK)
122 		|| sharedInfo.device_type.InFamily(INTEL_FAMILY_SER5)
123 		|| sharedInfo.device_type.InFamily(INTEL_FAMILY_SOC0)) {
124 		write32(baseRegister, mode.v_display_start * sharedInfo.bytes_per_row
125 			+ mode.h_display_start * (sharedInfo.bits_per_pixel + 7) / 8);
126 		read32(baseRegister);
127 		write32(surfaceRegister, sharedInfo.frame_buffer_offset);
128 		read32(surfaceRegister);
129 	} else {
130 		write32(baseRegister, sharedInfo.frame_buffer_offset
131 			+ mode.v_display_start * sharedInfo.bytes_per_row
132 			+ mode.h_display_start * (sharedInfo.bits_per_pixel + 7) / 8);
133 		read32(baseRegister);
134 	}
135 }
136 
137 
138 void
139 set_frame_buffer_base()
140 {
141 	// TODO we always set both displays to the same address. When we support
142 	// multiple framebuffers, they should get different addresses here.
143 	set_frame_buffer_registers(INTEL_DISPLAY_A_BASE, INTEL_DISPLAY_A_SURFACE);
144 	set_frame_buffer_registers(INTEL_DISPLAY_B_BASE, INTEL_DISPLAY_B_SURFACE);
145 }
146 
147 
148 static bool
149 limit_modes_for_gen3_lvds(display_mode* mode)
150 {
151 	// Filter out modes with resolution higher than the internal LCD can
152 	// display.
153 	// FIXME do this only for that display. The whole display mode logic
154 	// needs to be adjusted to know which display we're talking about.
155 	if (gInfo->shared_info->panel_mode.virtual_width < mode->virtual_width)
156 		return false;
157 	if (gInfo->shared_info->panel_mode.virtual_height < mode->virtual_height)
158 		return false;
159 
160 	return true;
161 }
162 
163 /*!	Creates the initial mode list of the primary accelerant.
164 	It's called from intel_init_accelerant().
165 */
166 status_t
167 create_mode_list(void)
168 {
169 	CALLED();
170 
171 	for (uint32 i = 0; i < gInfo->port_count; i++) {
172 		if (gInfo->ports[i] == NULL)
173 			continue;
174 
175 		status_t status = gInfo->ports[i]->GetEDID(&gInfo->edid_info);
176 		if (status == B_OK)
177 			gInfo->has_edid = true;
178 	}
179 
180 	display_mode* list;
181 	uint32 count = 0;
182 
183 	const color_space kSupportedSpaces[] = {B_RGB32_LITTLE, B_RGB16_LITTLE,
184 		B_CMAP8};
185 	const color_space* supportedSpaces;
186 	int colorSpaceCount;
187 
188 	if (gInfo->shared_info->device_type.Generation() >= 4) {
189 		// No B_RGB15, use our custom colorspace list
190 		supportedSpaces = kSupportedSpaces;
191 		colorSpaceCount = B_COUNT_OF(kSupportedSpaces);
192 	} else {
193 		supportedSpaces = NULL;
194 		colorSpaceCount = 0;
195 	}
196 
197 	// If no EDID, but have vbt from driver, use that mode
198 	if (!gInfo->has_edid && gInfo->shared_info->got_vbt) {
199 		// We could not read any EDID info. Fallback to creating a list with
200 		// only the mode set up by the BIOS.
201 
202 		check_display_mode_hook limitModes = NULL;
203 		if (gInfo->shared_info->device_type.Generation() < 4)
204 			limitModes = limit_modes_for_gen3_lvds;
205 
206 		// TODO: support lower modes via scaling and windowing
207 		gInfo->mode_list_area = create_display_modes("intel extreme modes",
208 			NULL, &gInfo->shared_info->panel_mode, 1,
209 			supportedSpaces, colorSpaceCount, limitModes, &list, &count);
210 	} else {
211 		// Otherwise return the 'real' list of modes
212 		gInfo->mode_list_area = create_display_modes("intel extreme modes",
213 			gInfo->has_edid ? &gInfo->edid_info : NULL, NULL, 0,
214 			supportedSpaces, colorSpaceCount, NULL, &list, &count);
215 	}
216 
217 	if (gInfo->mode_list_area < B_OK)
218 		return gInfo->mode_list_area;
219 
220 	gInfo->mode_list = list;
221 	gInfo->shared_info->mode_list_area = gInfo->mode_list_area;
222 	gInfo->shared_info->mode_count = count;
223 
224 	return B_OK;
225 }
226 
227 
228 void
229 wait_for_vblank(void)
230 {
231 	acquire_sem_etc(gInfo->shared_info->vblank_sem, 1, B_RELATIVE_TIMEOUT,
232 		21000);
233 		// With the output turned off via DPMS, we might not get any interrupts
234 		// anymore that's why we don't wait forever for it. At 50Hz, we're sure
235 		// to get a vblank in at most 20ms, so there is no need to wait longer
236 		// than that.
237 }
238 
239 
240 //	#pragma mark -
241 
242 
243 uint32
244 intel_accelerant_mode_count(void)
245 {
246 	CALLED();
247 	return gInfo->shared_info->mode_count;
248 }
249 
250 
251 status_t
252 intel_get_mode_list(display_mode* modeList)
253 {
254 	CALLED();
255 	memcpy(modeList, gInfo->mode_list,
256 		gInfo->shared_info->mode_count * sizeof(display_mode));
257 	return B_OK;
258 }
259 
260 
261 status_t
262 intel_propose_display_mode(display_mode* target, const display_mode* low,
263 	const display_mode* high)
264 {
265 	CALLED();
266 
267 	// first search for the specified mode in the list, if no mode is found
268 	// try to fix the target mode in sanitize_display_mode
269 	// TODO: Only sanitize_display_mode should be used. However, at the moment
270 	// the mode constraints are not optimal and do not work for all
271 	// configurations.
272 	for (uint32 i = 0; i < gInfo->shared_info->mode_count; i++) {
273 		display_mode *mode = &gInfo->mode_list[i];
274 
275 		// TODO: improve this, ie. adapt pixel clock to allowed values!!!
276 
277 		if (target->virtual_width != mode->virtual_width
278 			|| target->virtual_height != mode->virtual_height
279 			|| target->space != mode->space) {
280 			continue;
281 		}
282 
283 		*target = *mode;
284 		return B_OK;
285 	}
286 
287 	sanitize_display_mode(*target);
288 
289 	return is_display_mode_within_bounds(*target, *low, *high)
290 		? B_OK : B_BAD_VALUE;
291 }
292 
293 
294 status_t
295 intel_set_display_mode(display_mode* mode)
296 {
297 	if (mode == NULL)
298 		return B_BAD_VALUE;
299 
300 	TRACE("%s(%" B_PRIu16 "x%" B_PRIu16 ")\n", __func__,
301 		mode->virtual_width, mode->virtual_height);
302 
303 	display_mode target = *mode;
304 
305 	if (sanitize_display_mode(target)) {
306 		TRACE("Video mode was adjusted by sanitize_display_mode\n");
307 		TRACE("Initial mode: Hd %d Hs %d He %d Ht %d Vd %d Vs %d Ve %d Vt %d\n",
308 			mode->timing.h_display, mode->timing.h_sync_start,
309 			mode->timing.h_sync_end, mode->timing.h_total,
310 			mode->timing.v_display, mode->timing.v_sync_start,
311 			mode->timing.v_sync_end, mode->timing.v_total);
312 		TRACE("Sanitized: Hd %d Hs %d He %d Ht %d Vd %d Vs %d Ve %d Vt %d\n",
313 			target.timing.h_display, target.timing.h_sync_start,
314 			target.timing.h_sync_end, target.timing.h_total,
315 			target.timing.v_display, target.timing.v_sync_start,
316 			target.timing.v_sync_end, target.timing.v_total);
317 	}
318 
319 	uint32 colorMode, bytesPerRow, bitsPerPixel;
320 	get_color_space_format(target, colorMode, bytesPerRow, bitsPerPixel);
321 
322 	// TODO: do not go further if the mode is identical to the current one.
323 	// This would avoid the screen being off when switching workspaces when they
324 	// have the same resolution.
325 
326 	intel_shared_info &sharedInfo = *gInfo->shared_info;
327 	Autolock locker(sharedInfo.accelerant_lock);
328 
329 	// First register dump
330 	//dump_registers();
331 
332 	// TODO: This may not be neccesary
333 	set_display_power_mode(B_DPMS_OFF);
334 
335 	// free old and allocate new frame buffer in graphics memory
336 
337 	intel_free_memory(sharedInfo.frame_buffer);
338 
339 	addr_t base;
340 	if (intel_allocate_memory(bytesPerRow * target.virtual_height, 0,
341 			base) < B_OK) {
342 		// oh, how did that happen? Unfortunately, there is no really good way
343 		// back. Try to restore a framebuffer for the previous mode, at least.
344 		if (intel_allocate_memory(gInfo->current_mode.virtual_height
345 				* sharedInfo.bytes_per_row, 0, base) == B_OK) {
346 			sharedInfo.frame_buffer = base;
347 			sharedInfo.frame_buffer_offset = base
348 				- (addr_t)sharedInfo.graphics_memory;
349 			set_frame_buffer_base();
350 		}
351 
352 		ERROR("%s: Failed to allocate framebuffer !\n", __func__);
353 		return B_NO_MEMORY;
354 	}
355 
356 	// clear frame buffer before using it
357 	memset((uint8*)base, 0, bytesPerRow * target.virtual_height);
358 	sharedInfo.frame_buffer = base;
359 	sharedInfo.frame_buffer_offset = base - (addr_t)sharedInfo.graphics_memory;
360 
361 #if 0
362 	if ((gInfo->head_mode & HEAD_MODE_TESTING) != 0) {
363 		// 1. Enable panel power as needed to retrieve panel configuration
364 		// (use AUX VDD enable bit)
365 			// skip, did detection already, might need that before that though
366 
367 		// 2. Enable PCH clock reference source and PCH SSC modulator,
368 		// wait for warmup (Can be done anytime before enabling port)
369 			// skip, most certainly already set up by bios to use other ports,
370 			// will need for coldstart though
371 
372 		// 3. If enabling CPU embedded DisplayPort A: (Can be done anytime
373 		// before enabling CPU pipe or port)
374 		//	a.	Enable PCH 120MHz clock source output to CPU, wait for DMI
375 		//		latency
376 		//	b.	Configure and enable CPU DisplayPort PLL in the DisplayPort A
377 		//		register, wait for warmup
378 			// skip, not doing eDP right now, should go into
379 			// EmbeddedDisplayPort class though
380 
381 		// 4. If enabling port on PCH: (Must be done before enabling CPU pipe
382 		// or FDI)
383 		//	a.	Enable PCH FDI Receiver PLL, wait for warmup plus DMI latency
384 		//	b.	Switch from Rawclk to PCDclk in FDI Receiver (FDI A OR FDI B)
385 		//	c.	[DevSNB] Enable CPU FDI Transmitter PLL, wait for warmup
386 		//	d.	[DevILK] CPU FDI PLL is always on and does not need to be
387 		//		enabled
388 		FDILink* link = pipe->FDILink();
389 		if (link != NULL) {
390 			link->Receiver().EnablePLL();
391 			link->Receiver().SwitchClock(true);
392 			link->Transmitter().EnablePLL();
393 		}
394 
395 		// 5. Enable CPU panel fitter if needed for hires, required for VGA
396 		// (Can be done anytime before enabling CPU pipe)
397 		PanelFitter* fitter = pipe->PanelFitter();
398 		if (fitter != NULL)
399 			fitter->Enable(mode);
400 
401 		// 6. Configure CPU pipe timings, M/N/TU, and other pipe settings
402 		// (Can be done anytime before enabling CPU pipe)
403 		pll_divisors divisors;
404 		compute_pll_divisors(target, divisors, false);
405 		pipe->ConfigureTimings(divisors);
406 
407 		// 7. Enable CPU pipe
408 		pipe->Enable();
409 
410 8. Configure and enable CPU planes (VGA or hires)
411 9. If enabling port on PCH:
412 		//	a.   Program PCH FDI Receiver TU size same as Transmitter TU size for TU error checking
413 		//	b.   Train FDI
414 		//		i. Set pre-emphasis and voltage (iterate if training steps fail)
415                     ii. Enable CPU FDI Transmitter and PCH FDI Receiver with Training Pattern 1 enabled.
416                    iii. Wait for FDI training pattern 1 time
417                    iv. Read PCH FDI Receiver ISR ([DevIBX-B+] IIR) for bit lock in bit 8 (retry at least once if no lock)
418                     v. Enable training pattern 2 on CPU FDI Transmitter and PCH FDI Receiver
419                    vi.  Wait for FDI training pattern 2 time
420                   vii. Read PCH FDI Receiver ISR ([DevIBX-B+] IIR) for symbol lock in bit 9 (retry at least once if no
421                         lock)
422                   viii. Enable normal pixel output on CPU FDI Transmitter and PCH FDI Receiver
423                    ix.  Wait for FDI idle pattern time for link to become active
424          c.   Configure and enable PCH DPLL, wait for PCH DPLL warmup (Can be done anytime before enabling
425               PCH transcoder)
426          d.   [DevCPT] Configure DPLL SEL to set the DPLL to transcoder mapping and enable DPLL to the
427               transcoder.
428          e.   [DevCPT] Configure DPLL_CTL DPLL_HDMI_multipler.
429          f.   Configure PCH transcoder timings, M/N/TU, and other transcoder settings (should match CPU settings).
430          g.   [DevCPT] Configure and enable Transcoder DisplayPort Control if DisplayPort will be used
431          h.   Enable PCH transcoder
432 10. Enable ports (DisplayPort must enable in training pattern 1)
433 11. Enable panel power through panel power sequencing
434 12. Wait for panel power sequencing to reach enabled steady state
435 13. Disable panel power override
436 14. If DisplayPort, complete link training
437 15. Enable panel backlight
438 	}
439 #endif
440 
441 	// make sure VGA display is disabled
442 	write32(INTEL_VGA_DISPLAY_CONTROL, VGA_DISPLAY_DISABLED);
443 	read32(INTEL_VGA_DISPLAY_CONTROL);
444 
445 	// Go over each port and set the display mode
446 	for (uint32 i = 0; i < gInfo->port_count; i++) {
447 		if (gInfo->ports[i] == NULL)
448 			continue;
449 		if (!gInfo->ports[i]->IsConnected())
450 			continue;
451 
452 		status_t status = gInfo->ports[i]->SetDisplayMode(&target, colorMode);
453 		if (status != B_OK)
454 			ERROR("%s: Unable to set display mode!\n", __func__);
455 	}
456 
457 	TRACE("%s: Port configuration completed successfully!\n", __func__);
458 
459 	// We set the same color mode across all pipes
460 	program_pipe_color_modes(colorMode);
461 
462 	// TODO: This may not be neccesary (see DPMS OFF at top)
463 	set_display_power_mode(sharedInfo.dpms_mode);
464 
465 	// Changing bytes per row seems to be ignored if the plane/pipe is turned
466 	// off
467 
468 	// Always set both pipes, just in case
469 	// TODO rework this when we get multiple head support with different
470 	// resolutions
471 	write32(INTEL_DISPLAY_A_BYTES_PER_ROW, bytesPerRow);
472 	write32(INTEL_DISPLAY_B_BYTES_PER_ROW, bytesPerRow);
473 
474 	// update shared info
475 	gInfo->current_mode = target;
476 
477 	// TODO: move to gInfo
478 	sharedInfo.bytes_per_row = bytesPerRow;
479 	sharedInfo.bits_per_pixel = bitsPerPixel;
480 
481 	set_frame_buffer_base();
482 		// triggers writing back double-buffered registers
483 		// which is INTEL_DISPLAY_X_BYTES_PER_ROW only apparantly
484 
485 	// Second register dump
486 	//dump_registers();
487 
488 	return B_OK;
489 }
490 
491 
492 status_t
493 intel_get_display_mode(display_mode* _currentMode)
494 {
495 	CALLED();
496 
497 	*_currentMode = gInfo->current_mode;
498 
499 	// This seems unreliable. We should always know the current_mode
500 	//retrieve_current_mode(*_currentMode, INTEL_DISPLAY_A_PLL);
501 	return B_OK;
502 }
503 
504 
505 status_t
506 intel_get_edid_info(void* info, size_t size, uint32* _version)
507 {
508 	if (!gInfo->has_edid)
509 		return B_ERROR;
510 	if (size < sizeof(struct edid1_info))
511 		return B_BUFFER_OVERFLOW;
512 
513 	memcpy(info, &gInfo->edid_info, sizeof(struct edid1_info));
514 	*_version = EDID_VERSION_1;
515 	return B_OK;
516 }
517 
518 
519 static int32_t
520 intel_get_backlight_register(bool read)
521 {
522 	if (gInfo->shared_info->pch_info == INTEL_PCH_NONE)
523 		return MCH_BLC_PWM_CTL;
524 
525 	if (read)
526 		return PCH_SBLC_PWM_CTL2;
527 	else
528 		return PCH_BLC_PWM_CTL;
529 }
530 
531 
532 status_t
533 intel_set_brightness(float brightness)
534 {
535 	CALLED();
536 
537 	if (brightness < 0 || brightness > 1)
538 		return B_BAD_VALUE;
539 
540 	uint32_t period = read32(intel_get_backlight_register(true)) >> 16;
541 
542 	// The "duty cycle" is a proportion of the period (0 = backlight off,
543 	// period = maximum brightness). The low bit must be masked out because
544 	// it is apparently used for something else on some Atom machines (no
545 	// reference to that in the documentation that I know of).
546 	// Additionally we don't want it to be completely 0 here, because then
547 	// it becomes hard to turn the display on again (at least until we get
548 	// working ACPI keyboard shortcuts for this). So always keep the backlight
549 	// at least a little bit on for now.
550 	uint32_t duty = (uint32_t)(period * brightness) & 0xfffe;
551 	if (duty == 0 && period != 0)
552 		duty = 2;
553 
554 	write32(intel_get_backlight_register(false), duty | (period << 16));
555 
556 	return B_OK;
557 }
558 
559 
560 status_t
561 intel_get_brightness(float* brightness)
562 {
563 	CALLED();
564 
565 	if (brightness == NULL)
566 		return B_BAD_VALUE;
567 
568 	uint16_t period = read32(intel_get_backlight_register(true)) >> 16;
569 	uint16_t   duty = read32(intel_get_backlight_register(false)) & 0xffff;
570 	*brightness = (float)duty / period;
571 
572 	return B_OK;
573 }
574 
575 
576 status_t
577 intel_get_frame_buffer_config(frame_buffer_config* config)
578 {
579 	CALLED();
580 
581 	uint32 offset = gInfo->shared_info->frame_buffer_offset;
582 
583 	config->frame_buffer = gInfo->shared_info->graphics_memory + offset;
584 	config->frame_buffer_dma
585 		= (uint8*)gInfo->shared_info->physical_graphics_memory + offset;
586 	config->bytes_per_row = gInfo->shared_info->bytes_per_row;
587 
588 	return B_OK;
589 }
590 
591 
592 status_t
593 intel_get_pixel_clock_limits(display_mode* mode, uint32* _low, uint32* _high)
594 {
595 	CALLED();
596 
597 	if (_low != NULL) {
598 		// lower limit of about 48Hz vertical refresh
599 		uint32 totalClocks = (uint32)mode->timing.h_total
600 			* (uint32)mode->timing.v_total;
601 		uint32 low = (totalClocks * 48L) / 1000L;
602 		if (low < gInfo->shared_info->pll_info.min_frequency)
603 			low = gInfo->shared_info->pll_info.min_frequency;
604 		else if (low > gInfo->shared_info->pll_info.max_frequency)
605 			return B_ERROR;
606 
607 		*_low = low;
608 	}
609 
610 	if (_high != NULL)
611 		*_high = gInfo->shared_info->pll_info.max_frequency;
612 
613 	return B_OK;
614 }
615 
616 
617 status_t
618 intel_move_display(uint16 horizontalStart, uint16 verticalStart)
619 {
620 	CALLED();
621 
622 	intel_shared_info &sharedInfo = *gInfo->shared_info;
623 	Autolock locker(sharedInfo.accelerant_lock);
624 
625 	display_mode &mode = gInfo->current_mode;
626 
627 	if (horizontalStart + mode.timing.h_display > mode.virtual_width
628 		|| verticalStart + mode.timing.v_display > mode.virtual_height)
629 		return B_BAD_VALUE;
630 
631 	mode.h_display_start = horizontalStart;
632 	mode.v_display_start = verticalStart;
633 
634 	set_frame_buffer_base();
635 
636 	return B_OK;
637 }
638 
639 
640 status_t
641 intel_get_timing_constraints(display_timing_constraints* constraints)
642 {
643 	CALLED();
644 	return B_ERROR;
645 }
646 
647 
648 void
649 intel_set_indexed_colors(uint count, uint8 first, uint8* colors, uint32 flags)
650 {
651 	TRACE("%s(colors = %p, first = %u)\n", __func__, colors, first);
652 
653 	if (colors == NULL)
654 		return;
655 
656 	Autolock locker(gInfo->shared_info->accelerant_lock);
657 
658 	for (; count-- > 0; first++) {
659 		uint32 color = colors[0] << 16 | colors[1] << 8 | colors[2];
660 		colors += 3;
661 
662 		write32(INTEL_DISPLAY_A_PALETTE + first * sizeof(uint32), color);
663 		write32(INTEL_DISPLAY_B_PALETTE + first * sizeof(uint32), color);
664 	}
665 }
666