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