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 "accelerant_protos.h"
14#include "accelerant.h"
15#include "utility.h"
16
17#include <Debug.h>
18#include <stdio.h>
19#include <string.h>
20#include <math.h>
21
22#include <create_display_modes.h>
23#include <ddc.h>
24#include <edid.h>
25#include <validate_display_mode.h>
26
27
28#undef TRACE
29#define TRACE_MODE
30#ifdef TRACE_MODE
31#	define TRACE(x...) _sPrintf("intel_extreme accelerant:" x)
32#else
33#	define TRACE(x...)
34#endif
35
36#define ERROR(x...) _sPrintf("intel_extreme accelerant: " x)
37#define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
38
39
40struct display_registers {
41	uint32	pll;
42	uint32	divisors;
43	uint32	control;
44	uint32	pipe_config;
45	uint32	horiz_total;
46	uint32	horiz_blank;
47	uint32	horiz_sync;
48	uint32	vert_total;
49	uint32	vert_blank;
50	uint32	vert_sync;
51	uint32	size;
52	uint32	stride;
53	uint32	position;
54	uint32	pipe_source;
55};
56
57struct pll_divisors {
58	uint32	post;
59	uint32	post1;
60	uint32	post2;
61	bool	post2_high;
62	uint32	n;
63	uint32	m;
64	uint32	m1;
65	uint32	m2;
66};
67
68struct pll_limits {
69	pll_divisors	min;
70	pll_divisors	max;
71	uint32			min_post2_frequency;
72	uint32			min_vco;
73	uint32			max_vco;
74};
75
76
77static status_t
78get_i2c_signals(void* cookie, int* _clock, int* _data)
79{
80	uint32 ioRegister = (uint32)cookie;
81	uint32 value = read32(ioRegister);
82
83	*_clock = (value & I2C_CLOCK_VALUE_IN) != 0;
84	*_data = (value & I2C_DATA_VALUE_IN) != 0;
85
86	return B_OK;
87}
88
89
90static status_t
91set_i2c_signals(void* cookie, int clock, int data)
92{
93	uint32 ioRegister = (uint32)cookie;
94	uint32 value;
95
96	if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_83x)) {
97		// on these chips, the reserved values are fixed
98		value = 0;
99	} else {
100		// on all others, we have to preserve them manually
101		value = read32(ioRegister) & I2C_RESERVED;
102	}
103
104	if (data != 0)
105		value |= I2C_DATA_DIRECTION_MASK;
106	else {
107		value |= I2C_DATA_DIRECTION_MASK | I2C_DATA_DIRECTION_OUT
108			| I2C_DATA_VALUE_MASK;
109	}
110
111	if (clock != 0)
112		value |= I2C_CLOCK_DIRECTION_MASK;
113	else {
114		value |= I2C_CLOCK_DIRECTION_MASK | I2C_CLOCK_DIRECTION_OUT
115			| I2C_CLOCK_VALUE_MASK;
116	}
117
118	write32(ioRegister, value);
119	read32(ioRegister);
120		// make sure the PCI bus has flushed the write
121
122	return B_OK;
123}
124
125
126void
127set_frame_buffer_base()
128{
129	intel_shared_info &sharedInfo = *gInfo->shared_info;
130	display_mode &mode = sharedInfo.current_mode;
131	uint32 baseRegister;
132	uint32 surfaceRegister;
133
134	if (gInfo->head_mode & HEAD_MODE_A_ANALOG) {
135		baseRegister = INTEL_DISPLAY_A_BASE;
136		surfaceRegister = INTEL_DISPLAY_A_SURFACE;
137	} else {
138		baseRegister = INTEL_DISPLAY_B_BASE;
139		surfaceRegister = INTEL_DISPLAY_B_SURFACE;
140	}
141
142	if (sharedInfo.device_type.InGroup(INTEL_TYPE_96x)
143		|| sharedInfo.device_type.InGroup(INTEL_TYPE_G4x)
144		|| sharedInfo.device_type.InGroup(INTEL_TYPE_ILK)
145		|| sharedInfo.device_type.InGroup(INTEL_TYPE_SNB)
146		|| sharedInfo.device_type.InGroup(INTEL_TYPE_IVB)) {
147		write32(baseRegister, mode.v_display_start * sharedInfo.bytes_per_row
148			+ mode.h_display_start * (sharedInfo.bits_per_pixel + 7) / 8);
149		read32(baseRegister);
150		write32(surfaceRegister, sharedInfo.frame_buffer_offset);
151		read32(surfaceRegister);
152	} else {
153		write32(baseRegister, sharedInfo.frame_buffer_offset
154			+ mode.v_display_start * sharedInfo.bytes_per_row
155			+ mode.h_display_start * (sharedInfo.bits_per_pixel + 7) / 8);
156		read32(baseRegister);
157	}
158}
159
160
161/*!	Creates the initial mode list of the primary accelerant.
162	It's called from intel_init_accelerant().
163*/
164status_t
165create_mode_list(void)
166{
167	i2c_bus bus;
168	bus.cookie = (void*)INTEL_I2C_IO_A;
169	bus.set_signals = &set_i2c_signals;
170	bus.get_signals = &get_i2c_signals;
171	ddc2_init_timing(&bus);
172
173	status_t error = ddc2_read_edid1(&bus, &gInfo->edid_info, NULL, NULL);
174	if (error == B_OK) {
175		edid_dump(&gInfo->edid_info);
176		gInfo->has_edid = true;
177	} else {
178		TRACE("getting EDID on port A (analog) failed : %s. "
179			"Trying on port C (lvds)\n", strerror(error));
180		bus.cookie = (void*)INTEL_I2C_IO_C;
181		error = ddc2_read_edid1(&bus, &gInfo->edid_info, NULL, NULL);
182		if (error == B_OK) {
183			edid_dump(&gInfo->edid_info);
184			gInfo->has_edid = true;
185		} else {
186			TRACE("getting EDID on port C failed : %s\n",
187				strerror(error));
188
189			// We could not read any EDID info. Fallback to creating a list with
190			// only the mode set up by the BIOS.
191			// TODO: support lower modes via scaling and windowing
192			if ((gInfo->head_mode & HEAD_MODE_LVDS_PANEL) != 0
193					&& (gInfo->head_mode & HEAD_MODE_A_ANALOG) == 0) {
194				size_t size = (sizeof(display_mode) + B_PAGE_SIZE - 1)
195					& ~(B_PAGE_SIZE - 1);
196
197				display_mode* list;
198				area_id area = create_area("intel extreme modes",
199					(void**)&list, B_ANY_ADDRESS, size, B_NO_LOCK,
200					B_READ_AREA | B_WRITE_AREA);
201				if (area < B_OK)
202					return area;
203
204				memcpy(list, &gInfo->lvds_panel_mode, sizeof(display_mode));
205
206				gInfo->mode_list_area = area;
207				gInfo->mode_list = list;
208				gInfo->shared_info->mode_list_area = gInfo->mode_list_area;
209				gInfo->shared_info->mode_count = 1;
210				return B_OK;
211			}
212		}
213	}
214
215	// Otherwise return the 'real' list of modes
216	display_mode* list;
217	uint32 count = 0;
218	gInfo->mode_list_area = create_display_modes("intel extreme modes",
219		gInfo->has_edid ? &gInfo->edid_info : NULL, NULL, 0, NULL, 0, NULL,
220		&list, &count);
221	if (gInfo->mode_list_area < B_OK)
222		return gInfo->mode_list_area;
223
224	gInfo->mode_list = list;
225	gInfo->shared_info->mode_list_area = gInfo->mode_list_area;
226	gInfo->shared_info->mode_count = count;
227
228	return B_OK;
229}
230
231
232void
233wait_for_vblank(void)
234{
235	acquire_sem_etc(gInfo->shared_info->vblank_sem, 1, B_RELATIVE_TIMEOUT,
236		25000);
237		// With the output turned off via DPMS, we might not get any interrupts
238		// anymore that's why we don't wait forever for it.
239}
240
241
242static void
243get_pll_limits(pll_limits &limits)
244{
245	// Note, the limits are taken from the X driver; they have not yet been
246	// tested
247
248	if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_ILK)
249		|| gInfo->shared_info->device_type.InGroup(INTEL_TYPE_SNB)
250		|| gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IVB)) {
251		// TODO: support LVDS output limits as well
252		static const pll_limits kLimits = {
253			// p, p1, p2, high,   n,   m, m1, m2
254			{  5,  1, 10, false,  1,  79, 12,  5},	// min
255			{ 80,  8,  5, true,   5, 127, 22,  9},	// max
256			225000, 1760000, 3510000
257		};
258		limits = kLimits;
259	} else if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_G4x)) {
260		// TODO: support LVDS output limits as well
261		static const pll_limits kLimits = {
262			// p, p1, p2, high,   n,   m, m1, m2
263			{ 10,  1, 10, false,  1, 104, 17,  5},	// min
264			{ 30,  3, 10, true,   4, 138, 23, 11},	// max
265			270000, 1750000, 3500000
266		};
267		limits = kLimits;
268	} else if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) {
269		// TODO: support LVDS output limits as well
270		// m1 is reserved and must be 0
271		static const pll_limits kLimits = {
272			// p, p1, p2, high,   n,   m, m1,  m2
273			{  5,  1, 10, false,  3,   2,  0,   2},	// min
274			{ 80,  8,  5, true,   6, 256,  0, 256},	// max
275			200000, 1700000, 3500000
276		};
277		limits = kLimits;
278	} else if (gInfo->shared_info->device_type.InFamily(INTEL_TYPE_9xx)) {
279		// TODO: support LVDS output limits as well
280		// (Update: Output limits are adjusted in the computation (post2=7/14))
281		// Should move them here!
282		static const pll_limits kLimits = {
283			// p, p1, p2, high,   n,   m, m1, m2
284			{  5,  1, 10, false,  5,  70, 12,  7},	// min
285			{ 80,  8,  5, true,  10, 120, 22, 11},	// max
286			200000, 1400000, 2800000
287		};
288		limits = kLimits;
289	} else {
290		// TODO: support LVDS output limits as well
291		static const pll_limits kLimits = {
292			// p, p1, p2, high,   n,   m, m1, m2
293			{  4,  2,  4, false,  5,  96, 20,  8},
294			{128, 33,  2, true,  18, 140, 28, 18},
295			165000, 930000, 1400000
296		};
297		limits = kLimits;
298	}
299
300	TRACE("PLL limits, min: p %lu (p1 %lu, p2 %lu), n %lu, m %lu "
301		"(m1 %lu, m2 %lu)\n", limits.min.post, limits.min.post1,
302		limits.min.post2, limits.min.n, limits.min.m, limits.min.m1,
303		limits.min.m2);
304	TRACE("PLL limits, max: p %lu (p1 %lu, p2 %lu), n %lu, m %lu "
305		"(m1 %lu, m2 %lu)\n", limits.max.post, limits.max.post1,
306		limits.max.post2, limits.max.n, limits.max.m, limits.max.m1,
307		limits.max.m2);
308}
309
310
311static bool
312valid_pll_divisors(const pll_divisors& divisors, const pll_limits& limits)
313{
314	pll_info &info = gInfo->shared_info->pll_info;
315	uint32 vco = info.reference_frequency * divisors.m / divisors.n;
316	uint32 frequency = vco / divisors.post;
317
318	if (divisors.post < limits.min.post || divisors.post > limits.max.post
319		|| divisors.m < limits.min.m || divisors.m > limits.max.m
320		|| vco < limits.min_vco || vco > limits.max_vco
321		|| frequency < info.min_frequency || frequency > info.max_frequency)
322		return false;
323
324	return true;
325}
326
327
328static void
329compute_pll_divisors(const display_mode &current, pll_divisors& divisors,
330	bool isLVDS)
331{
332	float requestedPixelClock = current.timing.pixel_clock / 1000.0f;
333	float referenceClock
334		= gInfo->shared_info->pll_info.reference_frequency / 1000.0f;
335	pll_limits limits;
336	get_pll_limits(limits);
337
338	TRACE("%s: required MHz: %g\n", __func__, requestedPixelClock);
339
340	if (isLVDS) {
341		if ((read32(INTEL_DISPLAY_LVDS_PORT) & LVDS_CLKB_POWER_MASK)
342				== LVDS_CLKB_POWER_UP)
343			divisors.post2 = LVDS_POST2_RATE_FAST;
344		else
345			divisors.post2 = LVDS_POST2_RATE_SLOW;
346	} else {
347		if (current.timing.pixel_clock < limits.min_post2_frequency) {
348			// slow DAC timing
349			divisors.post2 = limits.min.post2;
350			divisors.post2_high = limits.min.post2_high;
351		} else {
352			// fast DAC timing
353			divisors.post2 = limits.max.post2;
354			divisors.post2_high = limits.max.post2_high;
355		}
356	}
357
358	float best = requestedPixelClock;
359	pll_divisors bestDivisors;
360
361	bool is_igd = gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD);
362	for (divisors.m1 = limits.min.m1; divisors.m1 <= limits.max.m1;
363			divisors.m1++) {
364		for (divisors.m2 = limits.min.m2; divisors.m2 <= limits.max.m2
365				&& ((divisors.m2 < divisors.m1) || is_igd); divisors.m2++) {
366			for (divisors.n = limits.min.n; divisors.n <= limits.max.n;
367					divisors.n++) {
368				for (divisors.post1 = limits.min.post1;
369						divisors.post1 <= limits.max.post1; divisors.post1++) {
370					divisors.m = 5 * divisors.m1 + divisors.m2;
371					divisors.post = divisors.post1 * divisors.post2;
372
373					if (!valid_pll_divisors(divisors, limits))
374						continue;
375
376					float error = fabs(requestedPixelClock
377						- ((referenceClock * divisors.m) / divisors.n)
378						/ divisors.post);
379					if (error < best) {
380						best = error;
381						bestDivisors = divisors;
382
383						if (error == 0)
384							break;
385					}
386				}
387			}
388		}
389	}
390
391	divisors = bestDivisors;
392
393	TRACE("%s: found: %g MHz, p = %lu (p1 = %lu, p2 = %lu), n = %lu, m = %lu "
394		"(m1 = %lu, m2 = %lu)\n", __func__,
395		((referenceClock * divisors.m) / divisors.n) / divisors.post,
396		divisors.post, divisors.post1, divisors.post2, divisors.n,
397		divisors.m, divisors.m1, divisors.m2);
398}
399
400
401void
402retrieve_current_mode(display_mode& mode, uint32 pllRegister)
403{
404	uint32 pll = read32(pllRegister);
405	uint32 pllDivisor;
406	uint32 hTotalRegister;
407	uint32 vTotalRegister;
408	uint32 hSyncRegister;
409	uint32 vSyncRegister;
410	uint32 imageSizeRegister;
411	uint32 controlRegister;
412
413	if (pllRegister == INTEL_DISPLAY_A_PLL) {
414		pllDivisor = read32((pll & DISPLAY_PLL_DIVISOR_1) != 0
415			? INTEL_DISPLAY_A_PLL_DIVISOR_1 : INTEL_DISPLAY_A_PLL_DIVISOR_0);
416
417		hTotalRegister = INTEL_DISPLAY_A_HTOTAL;
418		vTotalRegister = INTEL_DISPLAY_A_VTOTAL;
419		hSyncRegister = INTEL_DISPLAY_A_HSYNC;
420		vSyncRegister = INTEL_DISPLAY_A_VSYNC;
421		imageSizeRegister = INTEL_DISPLAY_A_IMAGE_SIZE;
422		controlRegister = INTEL_DISPLAY_A_CONTROL;
423	} else if (pllRegister == INTEL_DISPLAY_B_PLL) {
424		pllDivisor = read32((pll & DISPLAY_PLL_DIVISOR_1) != 0
425			? INTEL_DISPLAY_B_PLL_DIVISOR_1 : INTEL_DISPLAY_B_PLL_DIVISOR_0);
426
427		hTotalRegister = INTEL_DISPLAY_B_HTOTAL;
428		vTotalRegister = INTEL_DISPLAY_B_VTOTAL;
429		hSyncRegister = INTEL_DISPLAY_B_HSYNC;
430		vSyncRegister = INTEL_DISPLAY_B_VSYNC;
431		imageSizeRegister = INTEL_DISPLAY_B_IMAGE_SIZE;
432		controlRegister = INTEL_DISPLAY_B_CONTROL;
433	} else {
434		// TODO: not supported
435		return;
436	}
437
438	pll_divisors divisors;
439	if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) {
440		divisors.m1 = 0;
441		divisors.m2 = (pllDivisor & DISPLAY_PLL_IGD_M2_DIVISOR_MASK)
442			>> DISPLAY_PLL_M2_DIVISOR_SHIFT;
443		divisors.n = ((pllDivisor & DISPLAY_PLL_IGD_N_DIVISOR_MASK)
444			>> DISPLAY_PLL_N_DIVISOR_SHIFT) - 1;
445	} else {
446		divisors.m1 = (pllDivisor & DISPLAY_PLL_M1_DIVISOR_MASK)
447			>> DISPLAY_PLL_M1_DIVISOR_SHIFT;
448		divisors.m2 = (pllDivisor & DISPLAY_PLL_M2_DIVISOR_MASK)
449			>> DISPLAY_PLL_M2_DIVISOR_SHIFT;
450		divisors.n = (pllDivisor & DISPLAY_PLL_N_DIVISOR_MASK)
451			>> DISPLAY_PLL_N_DIVISOR_SHIFT;
452	}
453
454	pll_limits limits;
455	get_pll_limits(limits);
456
457	if (gInfo->shared_info->device_type.InFamily(INTEL_TYPE_9xx)) {
458		if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) {
459			divisors.post1 = (pll & DISPLAY_PLL_IGD_POST1_DIVISOR_MASK)
460				>> DISPLAY_PLL_IGD_POST1_DIVISOR_SHIFT;
461		} else {
462			divisors.post1 = (pll & DISPLAY_PLL_9xx_POST1_DIVISOR_MASK)
463				>> DISPLAY_PLL_POST1_DIVISOR_SHIFT;
464		}
465
466		if (pllRegister == INTEL_DISPLAY_B_PLL
467			&& !gInfo->shared_info->device_type.InGroup(INTEL_TYPE_96x)) {
468			// TODO: Fix this? Need to support dual channel LVDS.
469			divisors.post2 = LVDS_POST2_RATE_SLOW;
470		} else {
471			if ((pll & DISPLAY_PLL_DIVIDE_HIGH) != 0)
472				divisors.post2 = limits.max.post2;
473			else
474				divisors.post2 = limits.min.post2;
475		}
476	} else {
477		// 8xx
478		divisors.post1 = (pll & DISPLAY_PLL_POST1_DIVISOR_MASK)
479			>> DISPLAY_PLL_POST1_DIVISOR_SHIFT;
480
481		if ((pll & DISPLAY_PLL_DIVIDE_4X) != 0)
482			divisors.post2 = limits.max.post2;
483		else
484			divisors.post2 = limits.min.post2;
485	}
486
487	divisors.m = 5 * divisors.m1 + divisors.m2;
488	divisors.post = divisors.post1 * divisors.post2;
489
490	float referenceClock
491		= gInfo->shared_info->pll_info.reference_frequency / 1000.0f;
492	float pixelClock
493		= ((referenceClock * divisors.m) / divisors.n) / divisors.post;
494
495	// timing
496
497	mode.timing.pixel_clock = uint32(pixelClock * 1000);
498	mode.timing.flags = 0;
499
500	uint32 value = read32(hTotalRegister);
501	mode.timing.h_total = (value >> 16) + 1;
502	mode.timing.h_display = (value & 0xffff) + 1;
503
504	value = read32(hSyncRegister);
505	mode.timing.h_sync_end = (value >> 16) + 1;
506	mode.timing.h_sync_start = (value & 0xffff) + 1;
507
508	value = read32(vTotalRegister);
509	mode.timing.v_total = (value >> 16) + 1;
510	mode.timing.v_display = (value & 0xffff) + 1;
511
512	value = read32(vSyncRegister);
513	mode.timing.v_sync_end = (value >> 16) + 1;
514	mode.timing.v_sync_start = (value & 0xffff) + 1;
515
516	// image size and color space
517
518	value = read32(imageSizeRegister);
519	mode.virtual_width = (value >> 16) + 1;
520	mode.virtual_height = (value & 0xffff) + 1;
521
522	// using virtual size based on image size is the 'proper' way to do it,
523	// however the bios appears to be suggesting scaling or somesuch, so ignore
524	// the proper virtual dimension for now if they'd suggest a smaller size.
525	if (mode.virtual_width < mode.timing.h_display)
526		mode.virtual_width = mode.timing.h_display;
527	if (mode.virtual_height < mode.timing.v_display)
528		mode.virtual_height = mode.timing.v_display;
529
530	value = read32(controlRegister);
531	switch (value & DISPLAY_CONTROL_COLOR_MASK) {
532		case DISPLAY_CONTROL_RGB32:
533		default:
534			mode.space = B_RGB32;
535			break;
536		case DISPLAY_CONTROL_RGB16:
537			mode.space = B_RGB16;
538			break;
539		case DISPLAY_CONTROL_RGB15:
540			mode.space = B_RGB15;
541			break;
542		case DISPLAY_CONTROL_CMAP8:
543			mode.space = B_CMAP8;
544			break;
545	}
546
547	mode.h_display_start = 0;
548	mode.v_display_start = 0;
549	mode.flags = B_8_BIT_DAC | B_HARDWARE_CURSOR | B_PARALLEL_ACCESS
550		| B_DPMS | B_SUPPORTS_OVERLAYS;
551}
552
553
554/*! Store away panel information if identified on startup
555	(used for pipe B->lvds).
556*/
557void
558save_lvds_mode(void)
559{
560	// dump currently programmed mode.
561	display_mode biosMode;
562	retrieve_current_mode(biosMode, INTEL_DISPLAY_B_PLL);
563	gInfo->lvds_panel_mode = biosMode;
564}
565
566
567static void
568get_color_space_format(const display_mode &mode, uint32 &colorMode,
569	uint32 &bytesPerRow, uint32 &bitsPerPixel)
570{
571	uint32 bytesPerPixel;
572
573	switch (mode.space) {
574		case B_RGB32_LITTLE:
575			colorMode = DISPLAY_CONTROL_RGB32;
576			bytesPerPixel = 4;
577			bitsPerPixel = 32;
578			break;
579		case B_RGB16_LITTLE:
580			colorMode = DISPLAY_CONTROL_RGB16;
581			bytesPerPixel = 2;
582			bitsPerPixel = 16;
583			break;
584		case B_RGB15_LITTLE:
585			colorMode = DISPLAY_CONTROL_RGB15;
586			bytesPerPixel = 2;
587			bitsPerPixel = 15;
588			break;
589		case B_CMAP8:
590		default:
591			colorMode = DISPLAY_CONTROL_CMAP8;
592			bytesPerPixel = 1;
593			bitsPerPixel = 8;
594			break;
595	}
596
597	bytesPerRow = mode.virtual_width * bytesPerPixel;
598
599	// Make sure bytesPerRow is a multiple of 64
600	// TODO: check if the older chips have the same restriction!
601	if ((bytesPerRow & 63) != 0)
602		bytesPerRow = (bytesPerRow + 63) & ~63;
603}
604
605
606static bool
607sanitize_display_mode(display_mode& mode)
608{
609	// Some cards only support even pixel counts, while others require an odd
610	// one.
611	bool olderCard = gInfo->shared_info->device_type.InGroup(INTEL_TYPE_Gxx);
612	olderCard |= gInfo->shared_info->device_type.InGroup(INTEL_TYPE_96x);
613	olderCard |= gInfo->shared_info->device_type.InGroup(INTEL_TYPE_94x);
614	olderCard |= gInfo->shared_info->device_type.InGroup(INTEL_TYPE_91x);
615	olderCard |= gInfo->shared_info->device_type.InFamily(INTEL_TYPE_8xx);
616	olderCard |= gInfo->shared_info->device_type.InFamily(INTEL_TYPE_7xx);
617
618	// TODO: verify constraints - these are more or less taken from the
619	// radeon driver!
620	const display_constraints constraints = {
621		// resolution
622		320, 8192, 200, 4096,
623		// pixel clock
624		gInfo->shared_info->pll_info.min_frequency,
625		gInfo->shared_info->pll_info.max_frequency,
626		// horizontal
627		{olderCard ? 2 : 1, 0, 8160, 32, 8192, 0, 8192},
628		{1, 1, 4092, 2, 63, 1, 4096}
629	};
630
631	return sanitize_display_mode(mode, constraints,
632		gInfo->has_edid ? &gInfo->edid_info : NULL);
633}
634
635
636//	#pragma mark -
637
638
639uint32
640intel_accelerant_mode_count(void)
641{
642	CALLED();
643	return gInfo->shared_info->mode_count;
644}
645
646
647status_t
648intel_get_mode_list(display_mode* modeList)
649{
650	CALLED();
651	memcpy(modeList, gInfo->mode_list,
652		gInfo->shared_info->mode_count * sizeof(display_mode));
653	return B_OK;
654}
655
656
657status_t
658intel_propose_display_mode(display_mode* target, const display_mode* low,
659	const display_mode* high)
660{
661	CALLED();
662
663	// first search for the specified mode in the list, if no mode is found
664	// try to fix the target mode in sanitize_display_mode
665	// TODO: Only sanitize_display_mode should be used. However, at the moments
666	// the mode constraints are not optimal and do not work for all
667	// configurations.
668	for (uint32 i = 0; i < gInfo->shared_info->mode_count; i++) {
669		display_mode *mode = &gInfo->mode_list[i];
670
671		// TODO: improve this, ie. adapt pixel clock to allowed values!!!
672
673		if (target->virtual_width != mode->virtual_width
674		        || target->virtual_height != mode->virtual_height
675		        || target->space != mode->space)
676		        continue;
677
678		*target = *mode;
679		return B_OK;
680	}
681
682	sanitize_display_mode(*target);
683
684	return is_display_mode_within_bounds(*target, *low, *high)
685		? B_OK : B_BAD_VALUE;
686}
687
688
689status_t
690intel_set_display_mode(display_mode* mode)
691{
692	TRACE("%s(%" B_PRIu16 "x%" B_PRIu16 ")\n", __func__,
693		mode->virtual_width, mode->virtual_height);
694
695	if (mode == NULL)
696		return B_BAD_VALUE;
697
698	display_mode target = *mode;
699
700	// TODO: it may be acceptable to continue when using panel fitting or
701	// centering, since the data from propose_display_mode will not actually be
702	// used as is in this case.
703	if (sanitize_display_mode(target)) {
704		TRACE("%s: invalid mode set!\n", __func__);
705		return B_BAD_VALUE;
706	}
707
708	uint32 colorMode, bytesPerRow, bitsPerPixel;
709	get_color_space_format(target, colorMode, bytesPerRow, bitsPerPixel);
710
711	// TODO: do not go further if the mode is identical to the current one.
712	// This would avoid the screen being off when switching workspaces when they
713	// have the same resolution.
714
715#if 0
716static bool first = true;
717if (first) {
718	int fd = open("/boot/home/ie_.regs", O_CREAT | O_WRONLY, 0644);
719	if (fd >= 0) {
720		for (int32 i = 0; i < 0x80000; i += 16) {
721			char line[512];
722			int length = sprintf(line, "%05lx: %08lx %08lx %08lx %08lx\n",
723				i, read32(i), read32(i + 4), read32(i + 8), read32(i + 12));
724			write(fd, line, length);
725		}
726		close(fd);
727		sync();
728	}
729	first = false;
730}
731#endif
732
733	intel_shared_info &sharedInfo = *gInfo->shared_info;
734	Autolock locker(sharedInfo.accelerant_lock);
735
736	// TODO: This may not be neccesary
737	set_display_power_mode(B_DPMS_OFF);
738
739	// free old and allocate new frame buffer in graphics memory
740
741	intel_free_memory(sharedInfo.frame_buffer);
742
743	uint32 base;
744	if (intel_allocate_memory(bytesPerRow * target.virtual_height, 0,
745			base) < B_OK) {
746		// oh, how did that happen? Unfortunately, there is no really good way
747		// back
748		if (intel_allocate_memory(sharedInfo.current_mode.virtual_height
749				* sharedInfo.bytes_per_row, 0, base) == B_OK) {
750			sharedInfo.frame_buffer = base;
751			sharedInfo.frame_buffer_offset = base
752				- (addr_t)sharedInfo.graphics_memory;
753			set_frame_buffer_base();
754		}
755
756		TRACE("%s: Failed to allocate framebuffer !\n", __func__);
757		return B_NO_MEMORY;
758	}
759
760	// clear frame buffer before using it
761	memset((uint8*)base, 0, bytesPerRow * target.virtual_height);
762	sharedInfo.frame_buffer = base;
763	sharedInfo.frame_buffer_offset = base - (addr_t)sharedInfo.graphics_memory;
764
765	// make sure VGA display is disabled
766	write32(INTEL_VGA_DISPLAY_CONTROL, VGA_DISPLAY_DISABLED);
767	read32(INTEL_VGA_DISPLAY_CONTROL);
768
769	if ((gInfo->head_mode & HEAD_MODE_B_DIGITAL) != 0) {
770		// For LVDS panels, we actually always set the native mode in hardware
771		// Then we use the panel fitter to scale the picture to that.
772		display_mode hardwareTarget;
773		bool needsScaling = false;
774
775		// Try to get the panel preferred screen mode from EDID info
776		if (gInfo->has_edid) {
777			hardwareTarget.space = target.space;
778			hardwareTarget.virtual_width
779				= gInfo->edid_info.std_timing[0].h_size;
780			hardwareTarget.virtual_height
781				= gInfo->edid_info.std_timing[0].v_size;
782			for (int i = 0; i < EDID1_NUM_DETAILED_MONITOR_DESC; i++) {
783				if (gInfo->edid_info.detailed_monitor[i].monitor_desc_type
784						== EDID1_IS_DETAILED_TIMING) {
785					hardwareTarget.virtual_width = gInfo->edid_info
786						.detailed_monitor[i].data.detailed_timing.h_active;
787					hardwareTarget.virtual_height = gInfo->edid_info
788						.detailed_monitor[i].data.detailed_timing.v_active;
789					break;
790				}
791			}
792			TRACE("%s: hardware mode will actually be %dx%d\n", __func__,
793				hardwareTarget.virtual_width, hardwareTarget.virtual_height);
794			if ((hardwareTarget.virtual_width <= target.virtual_width
795					&& hardwareTarget.virtual_height <= target.virtual_height
796					&& hardwareTarget.space <= target.space)
797				|| intel_propose_display_mode(&hardwareTarget, mode, mode)) {
798				hardwareTarget = target;
799			} else
800				needsScaling = true;
801		} else {
802			// We don't have EDID data, try to set the requested mode directly
803			hardwareTarget = target;
804		}
805
806		pll_divisors divisors;
807		if (needsScaling)
808			compute_pll_divisors(hardwareTarget, divisors, true);
809		else
810			compute_pll_divisors(target, divisors, true);
811
812		uint32 dpll = DISPLAY_PLL_NO_VGA_CONTROL | DISPLAY_PLL_ENABLED;
813		if (gInfo->shared_info->device_type.InFamily(INTEL_TYPE_9xx)) {
814			dpll |= LVDS_PLL_MODE_LVDS;
815				// DPLL mode LVDS for i915+
816		}
817
818		// Compute bitmask from p1 value
819		if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) {
820			dpll |= (1 << (divisors.post1 - 1))
821				<< DISPLAY_PLL_IGD_POST1_DIVISOR_SHIFT;
822		} else {
823			dpll |= (1 << (divisors.post1 - 1))
824				<< DISPLAY_PLL_POST1_DIVISOR_SHIFT;
825		}
826		switch (divisors.post2) {
827			case 5:
828			case 7:
829				dpll |= DISPLAY_PLL_DIVIDE_HIGH;
830				break;
831		}
832
833		// Disable panel fitting, but enable 8 to 6-bit dithering
834		write32(INTEL_PANEL_FIT_CONTROL, 0x4);
835			// TODO: do not do this if the connected panel is 24-bit
836			// (I don't know how to detect that)
837
838		if ((dpll & DISPLAY_PLL_ENABLED) != 0) {
839			if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) {
840				write32(INTEL_DISPLAY_B_PLL_DIVISOR_0,
841					(((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT)
842						& DISPLAY_PLL_IGD_N_DIVISOR_MASK)
843					| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
844						& DISPLAY_PLL_IGD_M2_DIVISOR_MASK));
845			} else {
846				write32(INTEL_DISPLAY_B_PLL_DIVISOR_0,
847					(((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT)
848						& DISPLAY_PLL_N_DIVISOR_MASK)
849					| (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT)
850						& DISPLAY_PLL_M1_DIVISOR_MASK)
851					| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
852						& DISPLAY_PLL_M2_DIVISOR_MASK));
853			}
854			write32(INTEL_DISPLAY_B_PLL, dpll & ~DISPLAY_PLL_ENABLED);
855			read32(INTEL_DISPLAY_B_PLL);
856			spin(150);
857		}
858
859		uint32 lvds = read32(INTEL_DISPLAY_LVDS_PORT) | LVDS_PORT_EN
860			| LVDS_A0A2_CLKA_POWER_UP | LVDS_PIPEB_SELECT;
861
862		lvds |= LVDS_18BIT_DITHER;
863			// TODO: do not do this if the connected panel is 24-bit
864			// (I don't know how to detect that)
865
866		float referenceClock = gInfo->shared_info->pll_info.reference_frequency
867			/ 1000.0f;
868
869		// Set the B0-B3 data pairs corresponding to whether we're going to
870		// set the DPLLs for dual-channel mode or not.
871		if (divisors.post2 == LVDS_POST2_RATE_FAST)
872			lvds |= LVDS_B0B3PAIRS_POWER_UP | LVDS_CLKB_POWER_UP;
873		else
874			lvds &= ~(LVDS_B0B3PAIRS_POWER_UP | LVDS_CLKB_POWER_UP);
875
876		write32(INTEL_DISPLAY_LVDS_PORT, lvds);
877		read32(INTEL_DISPLAY_LVDS_PORT);
878
879		if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) {
880			write32(INTEL_DISPLAY_B_PLL_DIVISOR_0,
881				(((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT)
882					& DISPLAY_PLL_IGD_N_DIVISOR_MASK)
883				| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
884					& DISPLAY_PLL_IGD_M2_DIVISOR_MASK));
885		} else {
886			write32(INTEL_DISPLAY_B_PLL_DIVISOR_0,
887				(((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT)
888					& DISPLAY_PLL_N_DIVISOR_MASK)
889				| (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT)
890					& DISPLAY_PLL_M1_DIVISOR_MASK)
891				| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
892					& DISPLAY_PLL_M2_DIVISOR_MASK));
893		}
894
895		write32(INTEL_DISPLAY_B_PLL, dpll);
896		read32(INTEL_DISPLAY_B_PLL);
897
898		// Wait for the clocks to stabilize
899		spin(150);
900
901		if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_96x)) {
902			float adjusted = ((referenceClock * divisors.m) / divisors.n)
903				/ divisors.post;
904			uint32 pixelMultiply;
905			if (needsScaling) {
906				pixelMultiply = uint32(adjusted
907					/ (hardwareTarget.timing.pixel_clock / 1000.0f));
908			} else {
909				pixelMultiply = uint32(adjusted
910					/ (target.timing.pixel_clock / 1000.0f));
911			}
912
913			write32(INTEL_DISPLAY_B_PLL_MULTIPLIER_DIVISOR, (0 << 24)
914				| ((pixelMultiply - 1) << 8));
915		} else
916			write32(INTEL_DISPLAY_B_PLL, dpll);
917
918		read32(INTEL_DISPLAY_B_PLL);
919		spin(150);
920
921		// update timing parameters
922		if (needsScaling) {
923			// TODO: Alternatively, it should be possible to use the panel
924			// fitter and scale the picture.
925
926			// TODO: Perform some sanity check, for example if the target is
927			// wider than the hardware mode we end up with negative borders and
928			// broken timings
929			uint32 borderWidth = hardwareTarget.timing.h_display
930				- target.timing.h_display;
931
932			uint32 syncWidth = hardwareTarget.timing.h_sync_end
933				- hardwareTarget.timing.h_sync_start;
934
935			uint32 syncCenter = target.timing.h_display
936				+ (hardwareTarget.timing.h_total
937				- target.timing.h_display) / 2;
938
939			write32(INTEL_DISPLAY_B_HTOTAL,
940				((uint32)(hardwareTarget.timing.h_total - 1) << 16)
941				| ((uint32)target.timing.h_display - 1));
942			write32(INTEL_DISPLAY_B_HBLANK,
943				((uint32)(hardwareTarget.timing.h_total - borderWidth / 2 - 1)
944					<< 16)
945				| ((uint32)target.timing.h_display + borderWidth / 2 - 1));
946			write32(INTEL_DISPLAY_B_HSYNC,
947				((uint32)(syncCenter + syncWidth / 2 - 1) << 16)
948				| ((uint32)syncCenter - syncWidth / 2 - 1));
949
950			uint32 borderHeight = hardwareTarget.timing.v_display
951				- target.timing.v_display;
952
953			uint32 syncHeight = hardwareTarget.timing.v_sync_end
954				- hardwareTarget.timing.v_sync_start;
955
956			syncCenter = target.timing.v_display
957				+ (hardwareTarget.timing.v_total
958				- target.timing.v_display) / 2;
959
960			write32(INTEL_DISPLAY_B_VTOTAL,
961				((uint32)(hardwareTarget.timing.v_total - 1) << 16)
962				| ((uint32)target.timing.v_display - 1));
963			write32(INTEL_DISPLAY_B_VBLANK,
964				((uint32)(hardwareTarget.timing.v_total - borderHeight / 2 - 1)
965					<< 16)
966				| ((uint32)target.timing.v_display
967					+ borderHeight / 2 - 1));
968			write32(INTEL_DISPLAY_B_VSYNC,
969				((uint32)(syncCenter + syncHeight / 2 - 1) << 16)
970				| ((uint32)syncCenter - syncHeight / 2 - 1));
971
972			// This is useful for debugging: it sets the border to red, so you
973			// can see what is border and what is porch (black area around the
974			// sync)
975			// write32(0x61020, 0x00FF0000);
976		} else {
977			write32(INTEL_DISPLAY_B_HTOTAL,
978				((uint32)(target.timing.h_total - 1) << 16)
979				| ((uint32)target.timing.h_display - 1));
980			write32(INTEL_DISPLAY_B_HBLANK,
981				((uint32)(target.timing.h_total - 1) << 16)
982				| ((uint32)target.timing.h_display - 1));
983			write32(INTEL_DISPLAY_B_HSYNC,
984				((uint32)(target.timing.h_sync_end - 1) << 16)
985				| ((uint32)target.timing.h_sync_start - 1));
986
987			write32(INTEL_DISPLAY_B_VTOTAL,
988				((uint32)(target.timing.v_total - 1) << 16)
989				| ((uint32)target.timing.v_display - 1));
990			write32(INTEL_DISPLAY_B_VBLANK,
991				((uint32)(target.timing.v_total - 1) << 16)
992				| ((uint32)target.timing.v_display - 1));
993			write32(INTEL_DISPLAY_B_VSYNC, (
994				(uint32)(target.timing.v_sync_end - 1) << 16)
995				| ((uint32)target.timing.v_sync_start - 1));
996		}
997
998		write32(INTEL_DISPLAY_B_IMAGE_SIZE,
999			((uint32)(target.virtual_width - 1) << 16)
1000			| ((uint32)target.virtual_height - 1));
1001
1002		write32(INTEL_DISPLAY_B_POS, 0);
1003		write32(INTEL_DISPLAY_B_PIPE_SIZE,
1004			((uint32)(target.timing.v_display - 1) << 16)
1005			| ((uint32)target.timing.h_display - 1));
1006
1007		write32(INTEL_DISPLAY_B_CONTROL, (read32(INTEL_DISPLAY_B_CONTROL)
1008				& ~(DISPLAY_CONTROL_COLOR_MASK | DISPLAY_CONTROL_GAMMA))
1009			| colorMode);
1010
1011		write32(INTEL_DISPLAY_B_PIPE_CONTROL,
1012			read32(INTEL_DISPLAY_B_PIPE_CONTROL) | DISPLAY_PIPE_ENABLED);
1013		read32(INTEL_DISPLAY_B_PIPE_CONTROL);
1014	}
1015
1016	if ((gInfo->head_mode & HEAD_MODE_A_ANALOG) != 0) {
1017		pll_divisors divisors;
1018		compute_pll_divisors(target, divisors, false);
1019
1020		if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) {
1021			write32(INTEL_DISPLAY_A_PLL_DIVISOR_0,
1022				(((1 << divisors.n) << DISPLAY_PLL_N_DIVISOR_SHIFT)
1023					& DISPLAY_PLL_IGD_N_DIVISOR_MASK)
1024				| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
1025					& DISPLAY_PLL_IGD_M2_DIVISOR_MASK));
1026		} else {
1027			write32(INTEL_DISPLAY_A_PLL_DIVISOR_0,
1028				(((divisors.n - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT)
1029					& DISPLAY_PLL_N_DIVISOR_MASK)
1030				| (((divisors.m1 - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT)
1031					& DISPLAY_PLL_M1_DIVISOR_MASK)
1032				| (((divisors.m2 - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT)
1033					& DISPLAY_PLL_M2_DIVISOR_MASK));
1034		}
1035
1036		uint32 pll = DISPLAY_PLL_ENABLED | DISPLAY_PLL_NO_VGA_CONTROL;
1037		if (gInfo->shared_info->device_type.InFamily(INTEL_TYPE_9xx)) {
1038			if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD)) {
1039				pll |= ((1 << (divisors.post1 - 1))
1040						<< DISPLAY_PLL_IGD_POST1_DIVISOR_SHIFT)
1041					& DISPLAY_PLL_IGD_POST1_DIVISOR_MASK;
1042			} else {
1043				pll |= ((1 << (divisors.post1 - 1))
1044						<< DISPLAY_PLL_POST1_DIVISOR_SHIFT)
1045					& DISPLAY_PLL_9xx_POST1_DIVISOR_MASK;
1046//				pll |= ((divisors.post1 - 1) << DISPLAY_PLL_POST1_DIVISOR_SHIFT)
1047//					& DISPLAY_PLL_9xx_POST1_DIVISOR_MASK;
1048			}
1049			if (divisors.post2_high)
1050				pll |= DISPLAY_PLL_DIVIDE_HIGH;
1051
1052			pll |= DISPLAY_PLL_MODE_ANALOG;
1053
1054			if (gInfo->shared_info->device_type.InGroup(INTEL_TYPE_96x))
1055				pll |= 6 << DISPLAY_PLL_PULSE_PHASE_SHIFT;
1056		} else {
1057			if (!divisors.post2_high)
1058				pll |= DISPLAY_PLL_DIVIDE_4X;
1059
1060			pll |= DISPLAY_PLL_2X_CLOCK;
1061
1062			if (divisors.post1 > 2) {
1063				pll |= ((divisors.post1 - 2) << DISPLAY_PLL_POST1_DIVISOR_SHIFT)
1064					& DISPLAY_PLL_POST1_DIVISOR_MASK;
1065			} else
1066				pll |= DISPLAY_PLL_POST1_DIVIDE_2;
1067		}
1068
1069		write32(INTEL_DISPLAY_A_PLL, pll);
1070		read32(INTEL_DISPLAY_A_PLL);
1071		spin(150);
1072		write32(INTEL_DISPLAY_A_PLL, pll);
1073		read32(INTEL_DISPLAY_A_PLL);
1074		spin(150);
1075
1076		// update timing parameters
1077		write32(INTEL_DISPLAY_A_HTOTAL,
1078			((uint32)(target.timing.h_total - 1) << 16)
1079			| ((uint32)target.timing.h_display - 1));
1080		write32(INTEL_DISPLAY_A_HBLANK,
1081			((uint32)(target.timing.h_total - 1) << 16)
1082			| ((uint32)target.timing.h_display - 1));
1083		write32(INTEL_DISPLAY_A_HSYNC,
1084			((uint32)(target.timing.h_sync_end - 1) << 16)
1085			| ((uint32)target.timing.h_sync_start - 1));
1086
1087		write32(INTEL_DISPLAY_A_VTOTAL,
1088			((uint32)(target.timing.v_total - 1) << 16)
1089			| ((uint32)target.timing.v_display - 1));
1090		write32(INTEL_DISPLAY_A_VBLANK,
1091			((uint32)(target.timing.v_total - 1) << 16)
1092			| ((uint32)target.timing.v_display - 1));
1093		write32(INTEL_DISPLAY_A_VSYNC,
1094			((uint32)(target.timing.v_sync_end - 1) << 16)
1095			| ((uint32)target.timing.v_sync_start - 1));
1096
1097		write32(INTEL_DISPLAY_A_IMAGE_SIZE,
1098			((uint32)(target.virtual_width - 1) << 16)
1099			| ((uint32)target.virtual_height - 1));
1100
1101		write32(INTEL_DISPLAY_A_ANALOG_PORT,
1102			(read32(INTEL_DISPLAY_A_ANALOG_PORT)
1103				& ~(DISPLAY_MONITOR_POLARITY_MASK
1104					| DISPLAY_MONITOR_VGA_POLARITY))
1105			| ((target.timing.flags & B_POSITIVE_HSYNC) != 0
1106				? DISPLAY_MONITOR_POSITIVE_HSYNC : 0)
1107			| ((target.timing.flags & B_POSITIVE_VSYNC) != 0
1108				? DISPLAY_MONITOR_POSITIVE_VSYNC : 0));
1109
1110		// TODO: verify the two comments below: the X driver doesn't seem to
1111		//		care about both of them!
1112
1113		// These two have to be set for display B, too - this obviously means
1114		// that the second head always must adopt the color space of the first
1115		// head.
1116		write32(INTEL_DISPLAY_A_CONTROL, (read32(INTEL_DISPLAY_A_CONTROL)
1117				& ~(DISPLAY_CONTROL_COLOR_MASK | DISPLAY_CONTROL_GAMMA))
1118			| colorMode);
1119
1120		if ((gInfo->head_mode & HEAD_MODE_B_DIGITAL) != 0) {
1121			write32(INTEL_DISPLAY_B_IMAGE_SIZE,
1122				((uint32)(target.virtual_width - 1) << 16)
1123				| ((uint32)target.virtual_height - 1));
1124
1125			write32(INTEL_DISPLAY_B_CONTROL, (read32(INTEL_DISPLAY_B_CONTROL)
1126					& ~(DISPLAY_CONTROL_COLOR_MASK | DISPLAY_CONTROL_GAMMA))
1127				| colorMode);
1128		}
1129	}
1130
1131	set_display_power_mode(sharedInfo.dpms_mode);
1132
1133	// Changing bytes per row seems to be ignored if the plane/pipe is turned
1134	// off
1135
1136	if (gInfo->head_mode & HEAD_MODE_A_ANALOG)
1137		write32(INTEL_DISPLAY_A_BYTES_PER_ROW, bytesPerRow);
1138	if (gInfo->head_mode & HEAD_MODE_B_DIGITAL)
1139		write32(INTEL_DISPLAY_B_BYTES_PER_ROW, bytesPerRow);
1140
1141	set_frame_buffer_base();
1142		// triggers writing back double-buffered registers
1143
1144	// update shared info
1145	sharedInfo.bytes_per_row = bytesPerRow;
1146	sharedInfo.current_mode = target;
1147	sharedInfo.bits_per_pixel = bitsPerPixel;
1148
1149	return B_OK;
1150}
1151
1152
1153status_t
1154intel_get_display_mode(display_mode* _currentMode)
1155{
1156	CALLED();
1157
1158	retrieve_current_mode(*_currentMode, INTEL_DISPLAY_A_PLL);
1159	return B_OK;
1160}
1161
1162
1163status_t
1164intel_get_edid_info(void* info, size_t size, uint32* _version)
1165{
1166	CALLED();
1167
1168	if (!gInfo->has_edid)
1169		return B_ERROR;
1170	if (size < sizeof(struct edid1_info))
1171		return B_BUFFER_OVERFLOW;
1172
1173	memcpy(info, &gInfo->edid_info, sizeof(struct edid1_info));
1174	*_version = EDID_VERSION_1;
1175	return B_OK;
1176}
1177
1178
1179status_t
1180intel_get_frame_buffer_config(frame_buffer_config* config)
1181{
1182	CALLED();
1183
1184	uint32 offset = gInfo->shared_info->frame_buffer_offset;
1185
1186	config->frame_buffer = gInfo->shared_info->graphics_memory + offset;
1187	config->frame_buffer_dma
1188		= (uint8*)gInfo->shared_info->physical_graphics_memory + offset;
1189	config->bytes_per_row = gInfo->shared_info->bytes_per_row;
1190
1191	return B_OK;
1192}
1193
1194
1195status_t
1196intel_get_pixel_clock_limits(display_mode* mode, uint32* _low, uint32* _high)
1197{
1198	CALLED();
1199
1200	if (_low != NULL) {
1201		// lower limit of about 48Hz vertical refresh
1202		uint32 totalClocks = (uint32)mode->timing.h_total
1203			* (uint32)mode->timing.v_total;
1204		uint32 low = (totalClocks * 48L) / 1000L;
1205		if (low < gInfo->shared_info->pll_info.min_frequency)
1206			low = gInfo->shared_info->pll_info.min_frequency;
1207		else if (low > gInfo->shared_info->pll_info.max_frequency)
1208			return B_ERROR;
1209
1210		*_low = low;
1211	}
1212
1213	if (_high != NULL)
1214		*_high = gInfo->shared_info->pll_info.max_frequency;
1215
1216	return B_OK;
1217}
1218
1219
1220status_t
1221intel_move_display(uint16 horizontalStart, uint16 verticalStart)
1222{
1223	CALLED();
1224
1225	intel_shared_info &sharedInfo = *gInfo->shared_info;
1226	Autolock locker(sharedInfo.accelerant_lock);
1227
1228	display_mode &mode = sharedInfo.current_mode;
1229
1230	if (horizontalStart + mode.timing.h_display > mode.virtual_width
1231		|| verticalStart + mode.timing.v_display > mode.virtual_height)
1232		return B_BAD_VALUE;
1233
1234	mode.h_display_start = horizontalStart;
1235	mode.v_display_start = verticalStart;
1236
1237	set_frame_buffer_base();
1238
1239	return B_OK;
1240}
1241
1242
1243status_t
1244intel_get_timing_constraints(display_timing_constraints* constraints)
1245{
1246	CALLED();
1247	return B_ERROR;
1248}
1249
1250
1251void
1252intel_set_indexed_colors(uint count, uint8 first, uint8* colors, uint32 flags)
1253{
1254	TRACE("%s(colors = %p, first = %u)\n", __func__, colors, first);
1255
1256	if (colors == NULL)
1257		return;
1258
1259	Autolock locker(gInfo->shared_info->accelerant_lock);
1260
1261	for (; count-- > 0; first++) {
1262		uint32 color = colors[0] << 16 | colors[1] << 8 | colors[2];
1263		colors += 3;
1264
1265		write32(INTEL_DISPLAY_A_PALETTE + first * sizeof(uint32), color);
1266		write32(INTEL_DISPLAY_B_PALETTE + first * sizeof(uint32), color);
1267	}
1268}
1269
1270