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