xref: /haiku/src/add-ons/accelerants/intel_extreme/mode.cpp (revision 4d8811742fa447ec05b4993a16a0931bc29aafab)
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("%s: invalid mode set!\n", __func__);
449 		return B_BAD_VALUE;
450 	}
451 
452 	uint32 colorMode, bytesPerRow, bitsPerPixel;
453 	get_color_space_format(target, colorMode, bytesPerRow, bitsPerPixel);
454 
455 	// TODO: do not go further if the mode is identical to the current one.
456 	// This would avoid the screen being off when switching workspaces when they
457 	// have the same resolution.
458 
459 	intel_shared_info &sharedInfo = *gInfo->shared_info;
460 	Autolock locker(sharedInfo.accelerant_lock);
461 
462 	// First register dump
463 	//dump_registers();
464 
465 	// TODO: This may not be neccesary
466 	set_display_power_mode(B_DPMS_OFF);
467 
468 	// free old and allocate new frame buffer in graphics memory
469 
470 	intel_free_memory(sharedInfo.frame_buffer);
471 
472 	addr_t base;
473 	if (intel_allocate_memory(bytesPerRow * target.virtual_height, 0,
474 			base) < B_OK) {
475 		// oh, how did that happen? Unfortunately, there is no really good way
476 		// back
477 		if (intel_allocate_memory(gInfo->current_mode.virtual_height
478 				* sharedInfo.bytes_per_row, 0, base) == B_OK) {
479 			sharedInfo.frame_buffer = base;
480 			sharedInfo.frame_buffer_offset = base
481 				- (addr_t)sharedInfo.graphics_memory;
482 			set_frame_buffer_base();
483 		}
484 
485 		TRACE("%s: Failed to allocate framebuffer !\n", __func__);
486 		return B_NO_MEMORY;
487 	}
488 
489 	// clear frame buffer before using it
490 	memset((uint8*)base, 0, bytesPerRow * target.virtual_height);
491 	sharedInfo.frame_buffer = base;
492 	sharedInfo.frame_buffer_offset = base - (addr_t)sharedInfo.graphics_memory;
493 
494 #if 0
495 	if ((gInfo->head_mode & HEAD_MODE_TESTING) != 0) {
496 		// 1. Enable panel power as needed to retrieve panel configuration
497 		// (use AUX VDD enable bit)
498 			// skip, did detection already, might need that before that though
499 
500 		// 2. Enable PCH clock reference source and PCH SSC modulator,
501 		// wait for warmup (Can be done anytime before enabling port)
502 			// skip, most certainly already set up by bios to use other ports,
503 			// will need for coldstart though
504 
505 		// 3. If enabling CPU embedded DisplayPort A: (Can be done anytime
506 		// before enabling CPU pipe or port)
507 		//	a.	Enable PCH 120MHz clock source output to CPU, wait for DMI
508 		//		latency
509 		//	b.	Configure and enable CPU DisplayPort PLL in the DisplayPort A
510 		//		register, wait for warmup
511 			// skip, not doing eDP right now, should go into
512 			// EmbeddedDisplayPort class though
513 
514 		// 4. If enabling port on PCH: (Must be done before enabling CPU pipe
515 		// or FDI)
516 		//	a.	Enable PCH FDI Receiver PLL, wait for warmup plus DMI latency
517 		//	b.	Switch from Rawclk to PCDclk in FDI Receiver (FDI A OR FDI B)
518 		//	c.	[DevSNB] Enable CPU FDI Transmitter PLL, wait for warmup
519 		//	d.	[DevILK] CPU FDI PLL is always on and does not need to be
520 		//		enabled
521 		FDILink* link = pipe->FDILink();
522 		if (link != NULL) {
523 			link->Receiver().EnablePLL();
524 			link->Receiver().SwitchClock(true);
525 			link->Transmitter().EnablePLL();
526 		}
527 
528 		// 5. Enable CPU panel fitter if needed for hires, required for VGA
529 		// (Can be done anytime before enabling CPU pipe)
530 		PanelFitter* fitter = pipe->PanelFitter();
531 		if (fitter != NULL)
532 			fitter->Enable(mode);
533 
534 		// 6. Configure CPU pipe timings, M/N/TU, and other pipe settings
535 		// (Can be done anytime before enabling CPU pipe)
536 		pll_divisors divisors;
537 		compute_pll_divisors(target, divisors, false);
538 		pipe->ConfigureTimings(divisors);
539 
540 		// 7. Enable CPU pipe
541 		pipe->Enable();
542 
543 8. Configure and enable CPU planes (VGA or hires)
544 9. If enabling port on PCH:
545 		//	a.   Program PCH FDI Receiver TU size same as Transmitter TU size for TU error checking
546 		//	b.   Train FDI
547 		//		i. Set pre-emphasis and voltage (iterate if training steps fail)
548                     ii. Enable CPU FDI Transmitter and PCH FDI Receiver with Training Pattern 1 enabled.
549                    iii. Wait for FDI training pattern 1 time
550                    iv. Read PCH FDI Receiver ISR ([DevIBX-B+] IIR) for bit lock in bit 8 (retry at least once if no lock)
551                     v. Enable training pattern 2 on CPU FDI Transmitter and PCH FDI Receiver
552                    vi.  Wait for FDI training pattern 2 time
553                   vii. Read PCH FDI Receiver ISR ([DevIBX-B+] IIR) for symbol lock in bit 9 (retry at least once if no
554                         lock)
555                   viii. Enable normal pixel output on CPU FDI Transmitter and PCH FDI Receiver
556                    ix.  Wait for FDI idle pattern time for link to become active
557          c.   Configure and enable PCH DPLL, wait for PCH DPLL warmup (Can be done anytime before enabling
558               PCH transcoder)
559          d.   [DevCPT] Configure DPLL SEL to set the DPLL to transcoder mapping and enable DPLL to the
560               transcoder.
561          e.   [DevCPT] Configure DPLL_CTL DPLL_HDMI_multipler.
562          f.   Configure PCH transcoder timings, M/N/TU, and other transcoder settings (should match CPU settings).
563          g.   [DevCPT] Configure and enable Transcoder DisplayPort Control if DisplayPort will be used
564          h.   Enable PCH transcoder
565 10. Enable ports (DisplayPort must enable in training pattern 1)
566 11. Enable panel power through panel power sequencing
567 12. Wait for panel power sequencing to reach enabled steady state
568 13. Disable panel power override
569 14. If DisplayPort, complete link training
570 15. Enable panel backlight
571 	}
572 #endif
573 
574 	// make sure VGA display is disabled
575 	write32(INTEL_VGA_DISPLAY_CONTROL, VGA_DISPLAY_DISABLED);
576 	read32(INTEL_VGA_DISPLAY_CONTROL);
577 
578 	// Go over each port and set the display mode
579 	for (uint32 i = 0; i < gInfo->port_count; i++) {
580 		if (gInfo->ports[i] == NULL)
581 			continue;
582 		if (!gInfo->ports[i]->IsConnected())
583 			continue;
584 
585 		status_t status = gInfo->ports[i]->SetDisplayMode(&target, colorMode);
586 		if (status != B_OK)
587 			ERROR("%s: Unable to set display mode!\n", __func__);
588 	}
589 
590 	TRACE("%s: Port configuration completed successfully!\n", __func__);
591 
592 	// We set the same color mode across all pipes
593 	program_pipe_color_modes(colorMode);
594 
595 	// TODO: This may not be neccesary (see DPMS OFF at top)
596 	set_display_power_mode(sharedInfo.dpms_mode);
597 
598 	// Changing bytes per row seems to be ignored if the plane/pipe is turned
599 	// off
600 
601 	// Always set both pipes, just in case
602 	// TODO rework this when we get multiple head support with different
603 	// resolutions
604 	write32(INTEL_DISPLAY_A_BYTES_PER_ROW, bytesPerRow);
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