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