xref: /haiku/src/add-ons/accelerants/intel_extreme/mode.cpp (revision 02354704729d38c3b078c696adc1bbbd33cbcf72)
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 	// first search for the specified mode in the list, if no mode is found
277 	// try to fix the target mode in sanitize_display_mode
278 	// TODO: Only sanitize_display_mode should be used. However, at the moment
279 	// the mode constraints are not optimal and do not work for all
280 	// configurations.
281 	uint32 VirtualWidth = target->virtual_width;
282 	uint32 VirtualHeight = target->virtual_height;
283 	for (uint32 i = 0; i < gInfo->shared_info->mode_count; i++) {
284 		display_mode *mode = &gInfo->mode_list[i];
285 
286 		// TODO: improve this, ie. adapt pixel clock to allowed values!!!
287 
288 		if (target->timing.h_display != mode->timing.h_display
289 			|| target->timing.v_display != mode->timing.v_display
290 			|| target->space != mode->space) {
291 			continue;
292 		}
293 
294 		*target = *mode;
295 		// retain requested virtual size
296 		target->virtual_width = VirtualWidth;
297 		target->virtual_height = VirtualHeight;
298 		// (most) modeflags are outputs from us (the driver). So we should
299 		// set them depending on the mode and the current hardware config
300 		target->flags |= B_SCROLL;
301 		return B_OK;
302 	}
303 
304 	sanitize_display_mode(*target);
305 	// (most) modeflags are outputs from us (the driver). So we should
306 	// set them depending on the mode and the current hardware config
307 	target->flags |= B_SCROLL;
308 
309 	return is_display_mode_within_bounds(*target, *low, *high)
310 		? B_OK : B_BAD_VALUE;
311 }
312 
313 
314 status_t
315 intel_set_display_mode(display_mode* mode)
316 {
317 	if (mode == NULL)
318 		return B_BAD_VALUE;
319 
320 	TRACE("%s(%" B_PRIu16 "x%" B_PRIu16 ", virtual: %" B_PRIu16 "x%" B_PRIu16 ")\n", __func__,
321 		mode->timing.h_display, mode->timing.v_display, mode->virtual_width, mode->virtual_height);
322 
323 	display_mode target = *mode;
324 
325 	if (sanitize_display_mode(target)) {  //should be in proposemode..
326 		TRACE("Video mode was adjusted by sanitize_display_mode\n");
327 		TRACE("Initial mode: Hd %d Hs %d He %d Ht %d Vd %d Vs %d Ve %d Vt %d\n",
328 			mode->timing.h_display, mode->timing.h_sync_start,
329 			mode->timing.h_sync_end, mode->timing.h_total,
330 			mode->timing.v_display, mode->timing.v_sync_start,
331 			mode->timing.v_sync_end, mode->timing.v_total);
332 		TRACE("Sanitized: Hd %d Hs %d He %d Ht %d Vd %d Vs %d Ve %d Vt %d\n",
333 			target.timing.h_display, target.timing.h_sync_start,
334 			target.timing.h_sync_end, target.timing.h_total,
335 			target.timing.v_display, target.timing.v_sync_start,
336 			target.timing.v_sync_end, target.timing.v_total);
337 	}
338 	if (intel_propose_display_mode(&target, &target, &target) != B_OK)
339 		return B_BAD_VALUE;
340 
341 	uint32 colorMode, bytesPerRow, bitsPerPixel;
342 	get_color_space_format(target, colorMode, bytesPerRow, bitsPerPixel);
343 
344 	// TODO: do not go further if the mode is identical to the current one.
345 	// This would avoid the screen being off when switching workspaces when they
346 	// have the same resolution.
347 
348 	intel_shared_info &sharedInfo = *gInfo->shared_info;
349 	Autolock locker(sharedInfo.accelerant_lock);
350 
351 	// First register dump
352 	//dump_registers();
353 
354 	// TODO: This may not be neccesary
355 	set_display_power_mode(B_DPMS_OFF);
356 
357 	// free old and allocate new frame buffer in graphics memory
358 
359 	intel_free_memory(sharedInfo.frame_buffer);
360 
361 	addr_t base;
362 	if (intel_allocate_memory(bytesPerRow * target.virtual_height, 0,
363 			base) < B_OK) {
364 		// oh, how did that happen? Unfortunately, there is no really good way
365 		// back. Try to restore a framebuffer for the previous mode, at least.
366 		if (intel_allocate_memory(sharedInfo.current_mode.virtual_height
367 				* sharedInfo.bytes_per_row, 0, base) == B_OK) {
368 			sharedInfo.frame_buffer = base;
369 			sharedInfo.frame_buffer_offset = base
370 				- (addr_t)sharedInfo.graphics_memory;
371 			set_frame_buffer_base();
372 		}
373 
374 		ERROR("%s: Failed to allocate framebuffer !\n", __func__);
375 		return B_NO_MEMORY;
376 	}
377 
378 	// clear frame buffer before using it
379 	memset((uint8*)base, 0, bytesPerRow * target.virtual_height);
380 	sharedInfo.frame_buffer = base;
381 	sharedInfo.frame_buffer_offset = base - (addr_t)sharedInfo.graphics_memory;
382 
383 #if 0
384 	if ((gInfo->head_mode & HEAD_MODE_TESTING) != 0) {
385 		// 1. Enable panel power as needed to retrieve panel configuration
386 		// (use AUX VDD enable bit)
387 			// skip, did detection already, might need that before that though
388 
389 		// 2. Enable PCH clock reference source and PCH SSC modulator,
390 		// wait for warmup (Can be done anytime before enabling port)
391 			// skip, most certainly already set up by bios to use other ports,
392 			// will need for coldstart though
393 
394 		// 3. If enabling CPU embedded DisplayPort A: (Can be done anytime
395 		// before enabling CPU pipe or port)
396 		//	a.	Enable PCH 120MHz clock source output to CPU, wait for DMI
397 		//		latency
398 		//	b.	Configure and enable CPU DisplayPort PLL in the DisplayPort A
399 		//		register, wait for warmup
400 			// skip, not doing eDP right now, should go into
401 			// EmbeddedDisplayPort class though
402 
403 		// 4. If enabling port on PCH: (Must be done before enabling CPU pipe
404 		// or FDI)
405 		//	a.	Enable PCH FDI Receiver PLL, wait for warmup plus DMI latency
406 		//	b.	Switch from Rawclk to PCDclk in FDI Receiver (FDI A OR FDI B)
407 		//	c.	[DevSNB] Enable CPU FDI Transmitter PLL, wait for warmup
408 		//	d.	[DevILK] CPU FDI PLL is always on and does not need to be
409 		//		enabled
410 		FDILink* link = pipe->FDILink();
411 		if (link != NULL) {
412 			link->Receiver().EnablePLL();
413 			link->Receiver().SwitchClock(true);
414 			link->Transmitter().EnablePLL();
415 		}
416 
417 		// 5. Enable CPU panel fitter if needed for hires, required for VGA
418 		// (Can be done anytime before enabling CPU pipe)
419 		PanelFitter* fitter = pipe->PanelFitter();
420 		if (fitter != NULL)
421 			fitter->Enable(mode);
422 
423 		// 6. Configure CPU pipe timings, M/N/TU, and other pipe settings
424 		// (Can be done anytime before enabling CPU pipe)
425 		pll_divisors divisors;
426 		compute_pll_divisors(target, divisors, false);
427 		pipe->ConfigureTimings(divisors);
428 
429 		// 7. Enable CPU pipe
430 		pipe->Enable();
431 
432 8. Configure and enable CPU planes (VGA or hires)
433 9. If enabling port on PCH:
434 		//	a.   Program PCH FDI Receiver TU size same as Transmitter TU size for TU error checking
435 		//	b.   Train FDI
436 		//		i. Set pre-emphasis and voltage (iterate if training steps fail)
437                     ii. Enable CPU FDI Transmitter and PCH FDI Receiver with Training Pattern 1 enabled.
438                    iii. Wait for FDI training pattern 1 time
439                    iv. Read PCH FDI Receiver ISR ([DevIBX-B+] IIR) for bit lock in bit 8 (retry at least once if no lock)
440                     v. Enable training pattern 2 on CPU FDI Transmitter and PCH FDI Receiver
441                    vi.  Wait for FDI training pattern 2 time
442                   vii. Read PCH FDI Receiver ISR ([DevIBX-B+] IIR) for symbol lock in bit 9 (retry at least once if no
443                         lock)
444                   viii. Enable normal pixel output on CPU FDI Transmitter and PCH FDI Receiver
445                    ix.  Wait for FDI idle pattern time for link to become active
446          c.   Configure and enable PCH DPLL, wait for PCH DPLL warmup (Can be done anytime before enabling
447               PCH transcoder)
448          d.   [DevCPT] Configure DPLL SEL to set the DPLL to transcoder mapping and enable DPLL to the
449               transcoder.
450          e.   [DevCPT] Configure DPLL_CTL DPLL_HDMI_multipler.
451          f.   Configure PCH transcoder timings, M/N/TU, and other transcoder settings (should match CPU settings).
452          g.   [DevCPT] Configure and enable Transcoder DisplayPort Control if DisplayPort will be used
453          h.   Enable PCH transcoder
454 10. Enable ports (DisplayPort must enable in training pattern 1)
455 11. Enable panel power through panel power sequencing
456 12. Wait for panel power sequencing to reach enabled steady state
457 13. Disable panel power override
458 14. If DisplayPort, complete link training
459 15. Enable panel backlight
460 	}
461 #endif
462 
463 	// make sure VGA display is disabled
464 	write32(INTEL_VGA_DISPLAY_CONTROL, VGA_DISPLAY_DISABLED);
465 	read32(INTEL_VGA_DISPLAY_CONTROL);
466 
467 	// Go over each port and set the display mode
468 	for (uint32 i = 0; i < gInfo->port_count; i++) {
469 		if (gInfo->ports[i] == NULL)
470 			continue;
471 		if (!gInfo->ports[i]->IsConnected())
472 			continue;
473 
474 		status_t status = gInfo->ports[i]->SetDisplayMode(&target, colorMode);
475 		if (status != B_OK)
476 			ERROR("%s: Unable to set display mode!\n", __func__);
477 	}
478 
479 	TRACE("%s: Port configuration completed successfully!\n", __func__);
480 
481 	// We set the same color mode across all pipes
482 	program_pipe_color_modes(colorMode);
483 
484 	// TODO: This may not be neccesary (see DPMS OFF at top)
485 	set_display_power_mode(sharedInfo.dpms_mode);
486 
487 	// Changing bytes per row seems to be ignored if the plane/pipe is turned
488 	// off
489 
490 	// Always set both pipes, just in case
491 	// TODO rework this when we get multiple head support with different
492 	// resolutions
493 	write32(INTEL_DISPLAY_A_BYTES_PER_ROW, bytesPerRow);
494 	write32(INTEL_DISPLAY_B_BYTES_PER_ROW, bytesPerRow);
495 
496 	// update shared info
497 	sharedInfo.current_mode = target;
498 	sharedInfo.bytes_per_row = bytesPerRow;
499 	sharedInfo.bits_per_pixel = bitsPerPixel;
500 
501 	set_frame_buffer_base();
502 		// triggers writing back double-buffered registers
503 		// which is INTEL_DISPLAY_X_BYTES_PER_ROW only apparantly
504 
505 	// Second register dump
506 	//dump_registers();
507 
508 	return B_OK;
509 }
510 
511 
512 status_t
513 intel_get_display_mode(display_mode* _currentMode)
514 {
515 	CALLED();
516 
517 	*_currentMode = gInfo->shared_info->current_mode;
518 
519 	// This seems unreliable. We should always know the current_mode
520 	//retrieve_current_mode(*_currentMode, INTEL_DISPLAY_A_PLL);
521 	return B_OK;
522 }
523 
524 
525 status_t
526 intel_get_edid_info(void* info, size_t size, uint32* _version)
527 {
528 	if (!gInfo->has_edid)
529 		return B_ERROR;
530 	if (size < sizeof(struct edid1_info))
531 		return B_BUFFER_OVERFLOW;
532 
533 	memcpy(info, &gInfo->edid_info, sizeof(struct edid1_info));
534 	*_version = EDID_VERSION_1;
535 	return B_OK;
536 }
537 
538 
539 static int32_t
540 intel_get_backlight_register(bool read)
541 {
542 	if (gInfo->shared_info->pch_info == INTEL_PCH_NONE)
543 		return MCH_BLC_PWM_CTL;
544 
545 	if (read)
546 		return PCH_SBLC_PWM_CTL2;
547 	else
548 		return PCH_BLC_PWM_CTL;
549 }
550 
551 
552 status_t
553 intel_set_brightness(float brightness)
554 {
555 	CALLED();
556 
557 	if (brightness < 0 || brightness > 1)
558 		return B_BAD_VALUE;
559 
560 	uint32_t period = read32(intel_get_backlight_register(true)) >> 16;
561 
562 	// The "duty cycle" is a proportion of the period (0 = backlight off,
563 	// period = maximum brightness). The low bit must be masked out because
564 	// it is apparently used for something else on some Atom machines (no
565 	// reference to that in the documentation that I know of).
566 	// Additionally we don't want it to be completely 0 here, because then
567 	// it becomes hard to turn the display on again (at least until we get
568 	// working ACPI keyboard shortcuts for this). So always keep the backlight
569 	// at least a little bit on for now.
570 	uint32_t duty = (uint32_t)(period * brightness) & 0xfffe;
571 	if (duty == 0 && period != 0)
572 		duty = 2;
573 
574 	write32(intel_get_backlight_register(false), duty | (period << 16));
575 
576 	return B_OK;
577 }
578 
579 
580 status_t
581 intel_get_brightness(float* brightness)
582 {
583 	CALLED();
584 
585 	if (brightness == NULL)
586 		return B_BAD_VALUE;
587 
588 	uint16_t period = read32(intel_get_backlight_register(true)) >> 16;
589 	uint16_t   duty = read32(intel_get_backlight_register(false)) & 0xffff;
590 	*brightness = (float)duty / period;
591 
592 	return B_OK;
593 }
594 
595 
596 status_t
597 intel_get_frame_buffer_config(frame_buffer_config* config)
598 {
599 	CALLED();
600 
601 	uint32 offset = gInfo->shared_info->frame_buffer_offset;
602 
603 	config->frame_buffer = gInfo->shared_info->graphics_memory + offset;
604 	config->frame_buffer_dma
605 		= (uint8*)gInfo->shared_info->physical_graphics_memory + offset;
606 	config->bytes_per_row = gInfo->shared_info->bytes_per_row;
607 
608 	return B_OK;
609 }
610 
611 
612 status_t
613 intel_get_pixel_clock_limits(display_mode* mode, uint32* _low, uint32* _high)
614 {
615 	CALLED();
616 
617 	if (_low != NULL) {
618 		// lower limit of about 48Hz vertical refresh
619 		uint32 totalClocks = (uint32)mode->timing.h_total
620 			* (uint32)mode->timing.v_total;
621 		uint32 low = (totalClocks * 48L) / 1000L;
622 		if (low < gInfo->shared_info->pll_info.min_frequency)
623 			low = gInfo->shared_info->pll_info.min_frequency;
624 		else if (low > gInfo->shared_info->pll_info.max_frequency)
625 			return B_ERROR;
626 
627 		*_low = low;
628 	}
629 
630 	if (_high != NULL)
631 		*_high = gInfo->shared_info->pll_info.max_frequency;
632 
633 	return B_OK;
634 }
635 
636 
637 status_t
638 intel_move_display(uint16 horizontalStart, uint16 verticalStart)
639 {
640 	intel_shared_info &sharedInfo = *gInfo->shared_info;
641 	Autolock locker(sharedInfo.accelerant_lock);
642 
643 	display_mode &mode = sharedInfo.current_mode;
644 
645 	if (horizontalStart + mode.timing.h_display > mode.virtual_width
646 		|| verticalStart + mode.timing.v_display > mode.virtual_height)
647 		return B_BAD_VALUE;
648 
649 	mode.h_display_start = horizontalStart;
650 	mode.v_display_start = verticalStart;
651 
652 	set_frame_buffer_base();
653 
654 	return B_OK;
655 }
656 
657 
658 status_t
659 intel_get_timing_constraints(display_timing_constraints* constraints)
660 {
661 	CALLED();
662 	return B_ERROR;
663 }
664 
665 
666 void
667 intel_set_indexed_colors(uint count, uint8 first, uint8* colors, uint32 flags)
668 {
669 	TRACE("%s(colors = %p, first = %u)\n", __func__, colors, first);
670 
671 	if (colors == NULL)
672 		return;
673 
674 	Autolock locker(gInfo->shared_info->accelerant_lock);
675 
676 	for (; count-- > 0; first++) {
677 		uint32 color = colors[0] << 16 | colors[1] << 8 | colors[2];
678 		colors += 3;
679 
680 		write32(INTEL_DISPLAY_A_PALETTE + first * sizeof(uint32), color);
681 		write32(INTEL_DISPLAY_B_PALETTE + first * sizeof(uint32), color);
682 	}
683 }
684