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