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