xref: /haiku/src/add-ons/accelerants/intel_extreme/mode.cpp (revision 2cad94c1c30b6223ad8c08710b26e071d32e9979)
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 #if 0
44 // This hack needs to die. Leaving in for a little while
45 // incase we *really* need it.
46 static void
47 retrieve_current_mode(display_mode& mode, uint32 pllRegister)
48 {
49 	uint32 pll = read32(pllRegister);
50 	uint32 pllDivisor;
51 	uint32 hTotalRegister;
52 	uint32 vTotalRegister;
53 	uint32 hSyncRegister;
54 	uint32 vSyncRegister;
55 	uint32 imageSizeRegister;
56 	uint32 controlRegister;
57 
58 	if (pllRegister == INTEL_DISPLAY_A_PLL) {
59 		pllDivisor = read32((pll & DISPLAY_PLL_DIVISOR_1) != 0
60 			? INTEL_DISPLAY_A_PLL_DIVISOR_1 : INTEL_DISPLAY_A_PLL_DIVISOR_0);
61 
62 		hTotalRegister = INTEL_DISPLAY_A_HTOTAL;
63 		vTotalRegister = INTEL_DISPLAY_A_VTOTAL;
64 		hSyncRegister = INTEL_DISPLAY_A_HSYNC;
65 		vSyncRegister = INTEL_DISPLAY_A_VSYNC;
66 		imageSizeRegister = INTEL_DISPLAY_A_IMAGE_SIZE;
67 		controlRegister = INTEL_DISPLAY_A_CONTROL;
68 	} else if (pllRegister == INTEL_DISPLAY_B_PLL) {
69 		pllDivisor = read32((pll & DISPLAY_PLL_DIVISOR_1) != 0
70 			? INTEL_DISPLAY_B_PLL_DIVISOR_1 : INTEL_DISPLAY_B_PLL_DIVISOR_0);
71 
72 		hTotalRegister = INTEL_DISPLAY_B_HTOTAL;
73 		vTotalRegister = INTEL_DISPLAY_B_VTOTAL;
74 		hSyncRegister = INTEL_DISPLAY_B_HSYNC;
75 		vSyncRegister = INTEL_DISPLAY_B_VSYNC;
76 		imageSizeRegister = INTEL_DISPLAY_B_IMAGE_SIZE;
77 		controlRegister = INTEL_DISPLAY_B_CONTROL;
78 	} else {
79 		ERROR("%s: PLL not supported\n", __func__);
80 		return;
81 	}
82 
83 	pll_divisors divisors;
84 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN)) {
85 		divisors.m1 = 0;
86 		divisors.m2 = (pllDivisor & DISPLAY_PLL_IGD_M2_DIVISOR_MASK)
87 			>> DISPLAY_PLL_M2_DIVISOR_SHIFT;
88 		divisors.n = ((pllDivisor & DISPLAY_PLL_IGD_N_DIVISOR_MASK)
89 			>> DISPLAY_PLL_N_DIVISOR_SHIFT) - 1;
90 	} else {
91 		divisors.m1 = (pllDivisor & DISPLAY_PLL_M1_DIVISOR_MASK)
92 			>> DISPLAY_PLL_M1_DIVISOR_SHIFT;
93 		divisors.m2 = (pllDivisor & DISPLAY_PLL_M2_DIVISOR_MASK)
94 			>> DISPLAY_PLL_M2_DIVISOR_SHIFT;
95 		divisors.n = (pllDivisor & DISPLAY_PLL_N_DIVISOR_MASK)
96 			>> DISPLAY_PLL_N_DIVISOR_SHIFT;
97 	}
98 
99 	pll_limits limits;
100 	get_pll_limits(&limits, false);
101 		// TODO: Detect LVDS connector vs assume no
102 
103 	if (gInfo->shared_info->device_type.Generation() >= 3) {
104 
105 		if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN)) {
106 			divisors.post1 = (pll & DISPLAY_PLL_IGD_POST1_DIVISOR_MASK)
107 				>> DISPLAY_PLL_IGD_POST1_DIVISOR_SHIFT;
108 		} else {
109 			divisors.post1 = (pll & DISPLAY_PLL_9xx_POST1_DIVISOR_MASK)
110 				>> DISPLAY_PLL_POST1_DIVISOR_SHIFT;
111 		}
112 
113 		if (pllRegister == INTEL_DISPLAY_B_PLL
114 			&& !gInfo->shared_info->device_type.InGroup(INTEL_GROUP_96x)) {
115 			// TODO: Fix this? Need to support dual channel LVDS.
116 			divisors.post2 = LVDS_POST2_RATE_SLOW;
117 		} else {
118 			if ((pll & DISPLAY_PLL_DIVIDE_HIGH) != 0)
119 				divisors.post2 = limits.max.post2;
120 			else
121 				divisors.post2 = limits.min.post2;
122 		}
123 	} else {
124 		// 8xx
125 		divisors.post1 = (pll & DISPLAY_PLL_POST1_DIVISOR_MASK)
126 			>> DISPLAY_PLL_POST1_DIVISOR_SHIFT;
127 
128 		if ((pll & DISPLAY_PLL_DIVIDE_4X) != 0)
129 			divisors.post2 = limits.max.post2;
130 		else
131 			divisors.post2 = limits.min.post2;
132 	}
133 
134 	divisors.m = 5 * divisors.m1 + divisors.m2;
135 	divisors.post = divisors.post1 * divisors.post2;
136 
137 	float referenceClock
138 		= gInfo->shared_info->pll_info.reference_frequency / 1000.0f;
139 	float pixelClock
140 		= ((referenceClock * divisors.m) / divisors.n) / divisors.post;
141 
142 	// timing
143 
144 	mode.timing.pixel_clock = uint32(pixelClock * 1000);
145 	mode.timing.flags = 0;
146 
147 	uint32 value = read32(hTotalRegister);
148 	mode.timing.h_total = (value >> 16) + 1;
149 	mode.timing.h_display = (value & 0xffff) + 1;
150 
151 	value = read32(hSyncRegister);
152 	mode.timing.h_sync_end = (value >> 16) + 1;
153 	mode.timing.h_sync_start = (value & 0xffff) + 1;
154 
155 	value = read32(vTotalRegister);
156 	mode.timing.v_total = (value >> 16) + 1;
157 	mode.timing.v_display = (value & 0xffff) + 1;
158 
159 	value = read32(vSyncRegister);
160 	mode.timing.v_sync_end = (value >> 16) + 1;
161 	mode.timing.v_sync_start = (value & 0xffff) + 1;
162 
163 	// image size and color space
164 
165 	value = read32(imageSizeRegister);
166 	mode.virtual_width = (value >> 16) + 1;
167 	mode.virtual_height = (value & 0xffff) + 1;
168 
169 	// using virtual size based on image size is the 'proper' way to do it,
170 	// however the bios appears to be suggesting scaling or somesuch, so ignore
171 	// the proper virtual dimension for now if they'd suggest a smaller size.
172 	if (mode.virtual_width < mode.timing.h_display)
173 		mode.virtual_width = mode.timing.h_display;
174 	if (mode.virtual_height < mode.timing.v_display)
175 		mode.virtual_height = mode.timing.v_display;
176 
177 	value = read32(controlRegister);
178 	switch (value & DISPLAY_CONTROL_COLOR_MASK) {
179 		case DISPLAY_CONTROL_RGB32:
180 		default:
181 			mode.space = B_RGB32;
182 			break;
183 		case DISPLAY_CONTROL_RGB16:
184 			mode.space = B_RGB16;
185 			break;
186 		case DISPLAY_CONTROL_RGB15:
187 			mode.space = B_RGB15;
188 			break;
189 		case DISPLAY_CONTROL_CMAP8:
190 			mode.space = B_CMAP8;
191 			break;
192 	}
193 
194 	mode.h_display_start = 0;
195 	mode.v_display_start = 0;
196 	mode.flags = B_8_BIT_DAC | B_HARDWARE_CURSOR | B_PARALLEL_ACCESS
197 		| B_DPMS | B_SUPPORTS_OVERLAYS;
198 }
199 #endif
200 
201 
202 static void
203 get_color_space_format(const display_mode &mode, uint32 &colorMode,
204 	uint32 &bytesPerRow, uint32 &bitsPerPixel)
205 {
206 	uint32 bytesPerPixel;
207 
208 	switch (mode.space) {
209 		case B_RGB32_LITTLE:
210 			colorMode = DISPLAY_CONTROL_RGB32;
211 			bytesPerPixel = 4;
212 			bitsPerPixel = 32;
213 			break;
214 		case B_RGB16_LITTLE:
215 			colorMode = DISPLAY_CONTROL_RGB16;
216 			bytesPerPixel = 2;
217 			bitsPerPixel = 16;
218 			break;
219 		case B_RGB15_LITTLE:
220 			colorMode = DISPLAY_CONTROL_RGB15;
221 			bytesPerPixel = 2;
222 			bitsPerPixel = 15;
223 			break;
224 		case B_CMAP8:
225 		default:
226 			colorMode = DISPLAY_CONTROL_CMAP8;
227 			bytesPerPixel = 1;
228 			bitsPerPixel = 8;
229 			break;
230 	}
231 
232 	bytesPerRow = mode.virtual_width * bytesPerPixel;
233 
234 	// Make sure bytesPerRow is a multiple of 64
235 	// TODO: check if the older chips have the same restriction!
236 	if ((bytesPerRow & 63) != 0)
237 		bytesPerRow = (bytesPerRow + 63) & ~63;
238 }
239 
240 
241 static bool
242 sanitize_display_mode(display_mode& mode)
243 {
244 	// Some cards only support even pixel counts, while others require an odd
245 	// one.
246 	uint16 pixelCount = 1;
247 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_Gxx)
248 			|| gInfo->shared_info->device_type.InGroup(INTEL_GROUP_96x)
249 			|| gInfo->shared_info->device_type.InGroup(INTEL_GROUP_94x)
250 			|| gInfo->shared_info->device_type.InGroup(INTEL_GROUP_91x)
251 			|| gInfo->shared_info->device_type.InFamily(INTEL_FAMILY_8xx)
252 			|| gInfo->shared_info->device_type.InFamily(INTEL_FAMILY_7xx)) {
253 		pixelCount = 2;
254 	}
255 
256 	// TODO: verify constraints - these are more or less taken from the
257 	// radeon driver!
258 	display_constraints constraints = {
259 		// resolution
260 		320, 8192, 200, 4096,
261 		// pixel clock
262 		gInfo->shared_info->pll_info.min_frequency,
263 		gInfo->shared_info->pll_info.max_frequency,
264 		// horizontal
265 		{pixelCount, 0, 8160, 32, 8192, 0, 8192},
266 		{1, 1, 4092, 2, 63, 1, 4096}
267 	};
268 
269 	return sanitize_display_mode(mode, constraints,
270 		gInfo->has_edid ? &gInfo->edid_info : NULL);
271 }
272 
273 
274 // #pragma mark -
275 
276 
277 void
278 set_frame_buffer_base()
279 {
280 	intel_shared_info &sharedInfo = *gInfo->shared_info;
281 	display_mode &mode = gInfo->current_mode;
282 
283 	uint32 baseRegister;
284 	uint32 surfaceRegister;
285 
286 	if (gInfo->head_mode & HEAD_MODE_A_ANALOG) {
287 		baseRegister = INTEL_DISPLAY_A_BASE;
288 		surfaceRegister = INTEL_DISPLAY_A_SURFACE;
289 	} else {
290 		baseRegister = INTEL_DISPLAY_B_BASE;
291 		surfaceRegister = INTEL_DISPLAY_B_SURFACE;
292 	}
293 
294 	if (sharedInfo.device_type.InGroup(INTEL_GROUP_96x)
295 		|| sharedInfo.device_type.InGroup(INTEL_GROUP_G4x)
296 		|| sharedInfo.device_type.InGroup(INTEL_GROUP_ILK)
297 		|| sharedInfo.device_type.InFamily(INTEL_FAMILY_SER5)
298 		|| sharedInfo.device_type.InFamily(INTEL_FAMILY_SOC0)) {
299 		write32(baseRegister, mode.v_display_start * sharedInfo.bytes_per_row
300 			+ mode.h_display_start * (sharedInfo.bits_per_pixel + 7) / 8);
301 		read32(baseRegister);
302 		write32(surfaceRegister, sharedInfo.frame_buffer_offset);
303 		read32(surfaceRegister);
304 	} else {
305 		write32(baseRegister, sharedInfo.frame_buffer_offset
306 			+ mode.v_display_start * sharedInfo.bytes_per_row
307 			+ mode.h_display_start * (sharedInfo.bits_per_pixel + 7) / 8);
308 		read32(baseRegister);
309 	}
310 }
311 
312 
313 /*!	Creates the initial mode list of the primary accelerant.
314 	It's called from intel_init_accelerant().
315 */
316 status_t
317 create_mode_list(void)
318 {
319 	for (uint32 i = 0; i < gInfo->port_count; i++) {
320 		if (gInfo->ports[i] == NULL)
321 			continue;
322 
323 		status_t status = gInfo->ports[i]->GetEDID(&gInfo->edid_info);
324 		if (status == B_OK)
325 			gInfo->has_edid = true;
326 	}
327 
328 	// If no EDID, but have vbt from driver, use that mode
329 	if (!gInfo->has_edid && gInfo->shared_info->got_vbt) {
330 		// We could not read any EDID info. Fallback to creating a list with
331 		// only the mode set up by the BIOS.
332 
333 		// TODO: support lower modes via scaling and windowing
334 		size_t size = (sizeof(display_mode) + B_PAGE_SIZE - 1)
335 			& ~(B_PAGE_SIZE - 1);
336 
337 		display_mode* list;
338 		area_id area = create_area("intel extreme modes",
339 			(void**)&list, B_ANY_ADDRESS, size, B_NO_LOCK,
340 			B_READ_AREA | B_WRITE_AREA);
341 		if (area < 0)
342 			return area;
343 
344 		memcpy(list, &gInfo->shared_info->panel_mode, sizeof(display_mode));
345 
346 		gInfo->mode_list_area = area;
347 		gInfo->mode_list = list;
348 		gInfo->shared_info->mode_list_area = gInfo->mode_list_area;
349 		gInfo->shared_info->mode_count = 1;
350 		return B_OK;
351 	}
352 
353 	// Otherwise return the 'real' list of modes
354 	display_mode* list;
355 	uint32 count = 0;
356 	gInfo->mode_list_area = create_display_modes("intel extreme modes",
357 		gInfo->has_edid ? &gInfo->edid_info : NULL, NULL, 0, NULL, 0, NULL,
358 		&list, &count);
359 	if (gInfo->mode_list_area < B_OK)
360 		return gInfo->mode_list_area;
361 
362 	gInfo->mode_list = list;
363 	gInfo->shared_info->mode_list_area = gInfo->mode_list_area;
364 	gInfo->shared_info->mode_count = count;
365 
366 	return B_OK;
367 }
368 
369 
370 void
371 wait_for_vblank(void)
372 {
373 	acquire_sem_etc(gInfo->shared_info->vblank_sem, 1, B_RELATIVE_TIMEOUT,
374 		25000);
375 		// With the output turned off via DPMS, we might not get any interrupts
376 		// anymore that's why we don't wait forever for it.
377 }
378 
379 
380 //	#pragma mark -
381 
382 
383 uint32
384 intel_accelerant_mode_count(void)
385 {
386 	CALLED();
387 	return gInfo->shared_info->mode_count;
388 }
389 
390 
391 status_t
392 intel_get_mode_list(display_mode* modeList)
393 {
394 	CALLED();
395 	memcpy(modeList, gInfo->mode_list,
396 		gInfo->shared_info->mode_count * sizeof(display_mode));
397 	return B_OK;
398 }
399 
400 
401 status_t
402 intel_propose_display_mode(display_mode* target, const display_mode* low,
403 	const display_mode* high)
404 {
405 	CALLED();
406 
407 	// first search for the specified mode in the list, if no mode is found
408 	// try to fix the target mode in sanitize_display_mode
409 	// TODO: Only sanitize_display_mode should be used. However, at the moments
410 	// the mode constraints are not optimal and do not work for all
411 	// configurations.
412 	for (uint32 i = 0; i < gInfo->shared_info->mode_count; i++) {
413 		display_mode *mode = &gInfo->mode_list[i];
414 
415 		// TODO: improve this, ie. adapt pixel clock to allowed values!!!
416 
417 		if (target->virtual_width != mode->virtual_width
418 			|| target->virtual_height != mode->virtual_height
419 			|| target->space != mode->space) {
420 			continue;
421 		}
422 
423 		*target = *mode;
424 		return B_OK;
425 	}
426 
427 	sanitize_display_mode(*target);
428 
429 	return is_display_mode_within_bounds(*target, *low, *high)
430 		? B_OK : B_BAD_VALUE;
431 }
432 
433 
434 status_t
435 intel_set_display_mode(display_mode* mode)
436 {
437 	TRACE("%s(%" B_PRIu16 "x%" B_PRIu16 ")\n", __func__,
438 		mode->virtual_width, mode->virtual_height);
439 
440 	if (mode == NULL)
441 		return B_BAD_VALUE;
442 
443 	display_mode target = *mode;
444 
445 	// TODO: it may be acceptable to continue when using panel fitting or
446 	// centering, since the data from propose_display_mode will not actually be
447 	// used as is in this case.
448 	if (sanitize_display_mode(target)) {
449 		TRACE("%s: invalid mode set!\n", __func__);
450 		return B_BAD_VALUE;
451 	}
452 
453 	uint32 colorMode, bytesPerRow, bitsPerPixel;
454 	get_color_space_format(target, colorMode, bytesPerRow, bitsPerPixel);
455 
456 	// TODO: do not go further if the mode is identical to the current one.
457 	// This would avoid the screen being off when switching workspaces when they
458 	// have the same resolution.
459 
460 	intel_shared_info &sharedInfo = *gInfo->shared_info;
461 	Autolock locker(sharedInfo.accelerant_lock);
462 
463 	// First register dump
464 	//dump_registers();
465 
466 	// TODO: This may not be neccesary
467 	set_display_power_mode(B_DPMS_OFF);
468 
469 	// free old and allocate new frame buffer in graphics memory
470 
471 	intel_free_memory(sharedInfo.frame_buffer);
472 
473 	addr_t base;
474 	if (intel_allocate_memory(bytesPerRow * target.virtual_height, 0,
475 			base) < B_OK) {
476 		// oh, how did that happen? Unfortunately, there is no really good way
477 		// back
478 		if (intel_allocate_memory(gInfo->current_mode.virtual_height
479 				* sharedInfo.bytes_per_row, 0, base) == B_OK) {
480 			sharedInfo.frame_buffer = base;
481 			sharedInfo.frame_buffer_offset = base
482 				- (addr_t)sharedInfo.graphics_memory;
483 			set_frame_buffer_base();
484 		}
485 
486 		TRACE("%s: Failed to allocate framebuffer !\n", __func__);
487 		return B_NO_MEMORY;
488 	}
489 
490 	// clear frame buffer before using it
491 	memset((uint8*)base, 0, bytesPerRow * target.virtual_height);
492 	sharedInfo.frame_buffer = base;
493 	sharedInfo.frame_buffer_offset = base - (addr_t)sharedInfo.graphics_memory;
494 
495 #if 0
496 	if ((gInfo->head_mode & HEAD_MODE_TESTING) != 0) {
497 		// 1. Enable panel power as needed to retrieve panel configuration
498 		// (use AUX VDD enable bit)
499 			// skip, did detection already, might need that before that though
500 
501 		// 2. Enable PCH clock reference source and PCH SSC modulator,
502 		// wait for warmup (Can be done anytime before enabling port)
503 			// skip, most certainly already set up by bios to use other ports,
504 			// will need for coldstart though
505 
506 		// 3. If enabling CPU embedded DisplayPort A: (Can be done anytime
507 		// before enabling CPU pipe or port)
508 		//	a.	Enable PCH 120MHz clock source output to CPU, wait for DMI
509 		//		latency
510 		//	b.	Configure and enable CPU DisplayPort PLL in the DisplayPort A
511 		//		register, wait for warmup
512 			// skip, not doing eDP right now, should go into
513 			// EmbeddedDisplayPort class though
514 
515 		// 4. If enabling port on PCH: (Must be done before enabling CPU pipe
516 		// or FDI)
517 		//	a.	Enable PCH FDI Receiver PLL, wait for warmup plus DMI latency
518 		//	b.	Switch from Rawclk to PCDclk in FDI Receiver (FDI A OR FDI B)
519 		//	c.	[DevSNB] Enable CPU FDI Transmitter PLL, wait for warmup
520 		//	d.	[DevILK] CPU FDI PLL is always on and does not need to be
521 		//		enabled
522 		FDILink* link = pipe->FDILink();
523 		if (link != NULL) {
524 			link->Receiver().EnablePLL();
525 			link->Receiver().SwitchClock(true);
526 			link->Transmitter().EnablePLL();
527 		}
528 
529 		// 5. Enable CPU panel fitter if needed for hires, required for VGA
530 		// (Can be done anytime before enabling CPU pipe)
531 		PanelFitter* fitter = pipe->PanelFitter();
532 		if (fitter != NULL)
533 			fitter->Enable(mode);
534 
535 		// 6. Configure CPU pipe timings, M/N/TU, and other pipe settings
536 		// (Can be done anytime before enabling CPU pipe)
537 		pll_divisors divisors;
538 		compute_pll_divisors(target, divisors, false);
539 		pipe->ConfigureTimings(divisors);
540 
541 		// 7. Enable CPU pipe
542 		pipe->Enable();
543 
544 8. Configure and enable CPU planes (VGA or hires)
545 9. If enabling port on PCH:
546 		//	a.   Program PCH FDI Receiver TU size same as Transmitter TU size for TU error checking
547 		//	b.   Train FDI
548 		//		i. Set pre-emphasis and voltage (iterate if training steps fail)
549                     ii. Enable CPU FDI Transmitter and PCH FDI Receiver with Training Pattern 1 enabled.
550                    iii. Wait for FDI training pattern 1 time
551                    iv. Read PCH FDI Receiver ISR ([DevIBX-B+] IIR) for bit lock in bit 8 (retry at least once if no lock)
552                     v. Enable training pattern 2 on CPU FDI Transmitter and PCH FDI Receiver
553                    vi.  Wait for FDI training pattern 2 time
554                   vii. Read PCH FDI Receiver ISR ([DevIBX-B+] IIR) for symbol lock in bit 9 (retry at least once if no
555                         lock)
556                   viii. Enable normal pixel output on CPU FDI Transmitter and PCH FDI Receiver
557                    ix.  Wait for FDI idle pattern time for link to become active
558          c.   Configure and enable PCH DPLL, wait for PCH DPLL warmup (Can be done anytime before enabling
559               PCH transcoder)
560          d.   [DevCPT] Configure DPLL SEL to set the DPLL to transcoder mapping and enable DPLL to the
561               transcoder.
562          e.   [DevCPT] Configure DPLL_CTL DPLL_HDMI_multipler.
563          f.   Configure PCH transcoder timings, M/N/TU, and other transcoder settings (should match CPU settings).
564          g.   [DevCPT] Configure and enable Transcoder DisplayPort Control if DisplayPort will be used
565          h.   Enable PCH transcoder
566 10. Enable ports (DisplayPort must enable in training pattern 1)
567 11. Enable panel power through panel power sequencing
568 12. Wait for panel power sequencing to reach enabled steady state
569 13. Disable panel power override
570 14. If DisplayPort, complete link training
571 15. Enable panel backlight
572 	}
573 #endif
574 
575 	// make sure VGA display is disabled
576 	write32(INTEL_VGA_DISPLAY_CONTROL, VGA_DISPLAY_DISABLED);
577 	read32(INTEL_VGA_DISPLAY_CONTROL);
578 
579 	// Go over each port and set the display mode
580 	for (uint32 i = 0; i < gInfo->port_count; i++) {
581 		if (gInfo->ports[i] == NULL)
582 			continue;
583 		if (!gInfo->ports[i]->IsConnected())
584 			continue;
585 
586 		status_t status = gInfo->ports[i]->SetDisplayMode(&target, colorMode);
587 		if (status != B_OK)
588 			ERROR("%s: Unable to set display mode!\n", __func__);
589 	}
590 
591 	TRACE("%s: Port configuration completed successfully!\n", __func__);
592 
593 	// We set the same color mode across all pipes
594 	program_pipe_color_modes(colorMode);
595 
596 	// TODO: This may not be neccesary (see DPMS OFF at top)
597 	set_display_power_mode(sharedInfo.dpms_mode);
598 
599 	// Changing bytes per row seems to be ignored if the plane/pipe is turned
600 	// off
601 
602 	if (gInfo->head_mode & HEAD_MODE_A_ANALOG)
603 		write32(INTEL_DISPLAY_A_BYTES_PER_ROW, bytesPerRow);
604 	if (gInfo->head_mode & HEAD_MODE_B_DIGITAL)
605 		write32(INTEL_DISPLAY_B_BYTES_PER_ROW, bytesPerRow);
606 
607 	// update shared info
608 	gInfo->current_mode = target;
609 
610 	// TODO: move to gInfo
611 	sharedInfo.bytes_per_row = bytesPerRow;
612 	sharedInfo.bits_per_pixel = bitsPerPixel;
613 
614 	set_frame_buffer_base();
615 		// triggers writing back double-buffered registers
616 
617 	// Second register dump
618 	//dump_registers();
619 
620 	return B_OK;
621 }
622 
623 
624 status_t
625 intel_get_display_mode(display_mode* _currentMode)
626 {
627 	CALLED();
628 
629 	*_currentMode = gInfo->current_mode;
630 
631 	// This seems unreliable. We should always know the current_mode
632 	//retrieve_current_mode(*_currentMode, INTEL_DISPLAY_A_PLL);
633 	return B_OK;
634 }
635 
636 
637 status_t
638 intel_get_edid_info(void* info, size_t size, uint32* _version)
639 {
640 	CALLED();
641 
642 	if (!gInfo->has_edid)
643 		return B_ERROR;
644 	if (size < sizeof(struct edid1_info))
645 		return B_BUFFER_OVERFLOW;
646 
647 	memcpy(info, &gInfo->edid_info, sizeof(struct edid1_info));
648 	*_version = EDID_VERSION_1;
649 	return B_OK;
650 }
651 
652 
653 status_t
654 intel_get_frame_buffer_config(frame_buffer_config* config)
655 {
656 	CALLED();
657 
658 	uint32 offset = gInfo->shared_info->frame_buffer_offset;
659 
660 	config->frame_buffer = gInfo->shared_info->graphics_memory + offset;
661 	config->frame_buffer_dma
662 		= (uint8*)gInfo->shared_info->physical_graphics_memory + offset;
663 	config->bytes_per_row = gInfo->shared_info->bytes_per_row;
664 
665 	return B_OK;
666 }
667 
668 
669 status_t
670 intel_get_pixel_clock_limits(display_mode* mode, uint32* _low, uint32* _high)
671 {
672 	CALLED();
673 
674 	if (_low != NULL) {
675 		// lower limit of about 48Hz vertical refresh
676 		uint32 totalClocks = (uint32)mode->timing.h_total
677 			* (uint32)mode->timing.v_total;
678 		uint32 low = (totalClocks * 48L) / 1000L;
679 		if (low < gInfo->shared_info->pll_info.min_frequency)
680 			low = gInfo->shared_info->pll_info.min_frequency;
681 		else if (low > gInfo->shared_info->pll_info.max_frequency)
682 			return B_ERROR;
683 
684 		*_low = low;
685 	}
686 
687 	if (_high != NULL)
688 		*_high = gInfo->shared_info->pll_info.max_frequency;
689 
690 	return B_OK;
691 }
692 
693 
694 status_t
695 intel_move_display(uint16 horizontalStart, uint16 verticalStart)
696 {
697 	CALLED();
698 
699 	intel_shared_info &sharedInfo = *gInfo->shared_info;
700 	Autolock locker(sharedInfo.accelerant_lock);
701 
702 	display_mode &mode = gInfo->current_mode;
703 
704 	if (horizontalStart + mode.timing.h_display > mode.virtual_width
705 		|| verticalStart + mode.timing.v_display > mode.virtual_height)
706 		return B_BAD_VALUE;
707 
708 	mode.h_display_start = horizontalStart;
709 	mode.v_display_start = verticalStart;
710 
711 	set_frame_buffer_base();
712 
713 	return B_OK;
714 }
715 
716 
717 status_t
718 intel_get_timing_constraints(display_timing_constraints* constraints)
719 {
720 	CALLED();
721 	return B_ERROR;
722 }
723 
724 
725 void
726 intel_set_indexed_colors(uint count, uint8 first, uint8* colors, uint32 flags)
727 {
728 	TRACE("%s(colors = %p, first = %u)\n", __func__, colors, first);
729 
730 	if (colors == NULL)
731 		return;
732 
733 	Autolock locker(gInfo->shared_info->accelerant_lock);
734 
735 	for (; count-- > 0; first++) {
736 		uint32 color = colors[0] << 16 | colors[1] << 8 | colors[2];
737 		colors += 3;
738 
739 		write32(INTEL_DISPLAY_A_PALETTE + first * sizeof(uint32), color);
740 		write32(INTEL_DISPLAY_B_PALETTE + first * sizeof(uint32), color);
741 	}
742 }
743