xref: /haiku/src/add-ons/accelerants/intel_extreme/mode.cpp (revision 2f470aec1c92ce6917b8a903e343795dc77af41f)
1 /*
2  * Copyright 2006, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Axel Dörfler, axeld@pinc-software.de
7  */
8 
9 
10 #include "accelerant_protos.h"
11 #include "accelerant.h"
12 #include "utility.h"
13 
14 #include <string.h>
15 #include <math.h>
16 
17 #define TRACE_MODE
18 #ifdef TRACE_MODE
19 extern "C" void _sPrintf(const char *format, ...);
20 #	define TRACE(x) _sPrintf x
21 #else
22 #	define TRACE(x) ;
23 #endif
24 
25 
26 #define	POSITIVE_SYNC \
27 	(B_POSITIVE_HSYNC | B_POSITIVE_VSYNC)
28 #define MODE_FLAGS \
29 	(B_8_BIT_DAC | B_HARDWARE_CURSOR | B_PARALLEL_ACCESS | B_DPMS | B_SUPPORTS_OVERLAYS)
30 
31 
32 static const display_mode kBaseModeList[] = {
33 	{{25175, 640, 656, 752, 800, 350, 387, 389, 449, B_POSITIVE_HSYNC}, B_CMAP8, 640, 350, 0, 0, MODE_FLAGS}, /* 640x350 - www.epanorama.net/documents/pc/vga_timing.html) */
34 	{{25175, 640, 656, 752, 800, 400, 412, 414, 449, B_POSITIVE_VSYNC}, B_CMAP8, 640, 400, 0, 0, MODE_FLAGS}, /* 640x400 - www.epanorama.net/documents/pc/vga_timing.html) */
35 	{{25175, 640, 656, 752, 800, 480, 490, 492, 525, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(640X480X8.Z1) */
36 	{{27500, 640, 672, 768, 864, 480, 488, 494, 530, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* 640X480X60Hz */
37 	{{30500, 640, 672, 768, 864, 480, 517, 523, 588, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* SVGA_640X480X60HzNI */
38 	{{31500, 640, 664, 704, 832, 480, 489, 492, 520, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70-72Hz_(640X480X8.Z1) */
39 	{{31500, 640, 656, 720, 840, 480, 481, 484, 500, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(640X480X8.Z1) */
40 	{{36000, 640, 696, 752, 832, 480, 481, 484, 509, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(640X480X8.Z1) */
41 	{{38100, 800, 832, 960, 1088, 600, 602, 606, 620, 0}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* SVGA_800X600X56HzNI */
42 	{{40000, 800, 840, 968, 1056, 600, 601, 605, 628, POSITIVE_SYNC}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(800X600X8.Z1) */
43 	{{49500, 800, 816, 896, 1056, 600, 601, 604, 625, POSITIVE_SYNC}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(800X600X8.Z1) */
44 	{{50000, 800, 856, 976, 1040, 600, 637, 643, 666, POSITIVE_SYNC}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70-72Hz_(800X600X8.Z1) */
45 	{{56250, 800, 832, 896, 1048, 600, 601, 604, 631, POSITIVE_SYNC}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(800X600X8.Z1) */
46 	{{65000, 1024, 1048, 1184, 1344, 768, 771, 777, 806, 0}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1024X768X8.Z1) */
47 	{{75000, 1024, 1048, 1184, 1328, 768, 771, 777, 806, 0}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70-72Hz_(1024X768X8.Z1) */
48 	{{78750, 1024, 1040, 1136, 1312, 768, 769, 772, 800, POSITIVE_SYNC}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1024X768X8.Z1) */
49 	{{94500, 1024, 1072, 1168, 1376, 768, 769, 772, 808, POSITIVE_SYNC}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(1024X768X8.Z1) */
50 	{{94200, 1152, 1184, 1280, 1472, 864, 865, 868, 914, POSITIVE_SYNC}, B_CMAP8, 1152, 864, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70Hz_(1152X864X8.Z1) */
51 	{{108000, 1152, 1216, 1344, 1600, 864, 865, 868, 900, POSITIVE_SYNC}, B_CMAP8, 1152, 864, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1152X864X8.Z1) */
52 	{{121500, 1152, 1216, 1344, 1568, 864, 865, 868, 911, POSITIVE_SYNC}, B_CMAP8, 1152, 864, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(1152X864X8.Z1) */
53 	{{108000, 1280, 1328, 1440, 1688, 1024, 1025, 1028, 1066, POSITIVE_SYNC}, B_CMAP8, 1280, 1024, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1280X1024X8.Z1) */
54 	{{135000, 1280, 1296, 1440, 1688, 1024, 1025, 1028, 1066, POSITIVE_SYNC}, B_CMAP8, 1280, 1024, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1280X1024X8.Z1) */
55 	{{157500, 1280, 1344, 1504, 1728, 1024, 1025, 1028, 1072, POSITIVE_SYNC}, B_CMAP8, 1280, 1024, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(1280X1024X8.Z1) */
56 
57 	{{106500, 1440, 1520, 1672, 1904, 900, 901, 904, 932, POSITIVE_SYNC}, B_CMAP8, 1440, 900, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1440X900) */
58 
59 	{{162000, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1600X1200X8.Z1) */
60 	{{175500, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@65Hz_(1600X1200X8.Z1) */
61 	{{189000, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70Hz_(1600X1200X8.Z1) */
62 	{{202500, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1600X1200X8.Z1) */
63 	{{216000, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@80Hz_(1600X1200X8.Z1) */
64 	{{229500, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(1600X1200X8.Z1) */
65 
66 	{{147100, 1680, 1784, 1968, 2256, 1050, 1051, 1054, 1087, POSITIVE_SYNC}, B_CMAP8, 1680, 1050, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1680X1050) */
67 };
68 static const uint32 kNumBaseModes = sizeof(kBaseModeList) / sizeof(display_mode);
69 static const uint32 kMaxNumModes = kNumBaseModes * 4;
70 
71 
72 /**	Creates the initial mode list of the primary accelerant.
73  *	It's called from intel_init_accelerant().
74  */
75 
76 status_t
77 create_mode_list(void)
78 {
79 	color_space spaces[4] = {B_RGB32_LITTLE, B_RGB16_LITTLE, B_RGB15_LITTLE, B_CMAP8};
80 	size_t size = ROUND_TO_PAGE_SIZE(kMaxNumModes * sizeof(display_mode));
81 	display_mode *list;
82 
83 	gInfo->mode_list_area = create_area("intel extreme modes", (void **)&list, B_ANY_ADDRESS,
84 		size, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
85 	if (gInfo->mode_list_area < B_OK)
86 		return gInfo->mode_list_area;
87 
88 	// for now, just copy all of the modes with different color spaces
89 
90 	const display_mode *source = kBaseModeList;
91 	uint32 count = 0;
92 
93 	for (uint32 i = 0; i < kNumBaseModes; i++) {
94 		for (uint32 j = 0; j < (sizeof(spaces) / sizeof(color_space)); j++) {
95 			list[count] = *source;
96 			list[count].space = spaces[j];
97 
98 			count++;
99 		}
100 
101 		source++;
102 	}
103 
104 	gInfo->mode_list = list;
105 	gInfo->shared_info->mode_list_area = gInfo->mode_list_area;
106 	gInfo->shared_info->mode_count = count;
107 
108 	return B_OK;
109 }
110 
111 
112 void
113 wait_for_vblank(void)
114 {
115 	acquire_sem_etc(gInfo->shared_info->vblank_sem, 1, B_RELATIVE_TIMEOUT, 25000);
116 		// With the output turned off via DPMS, we might not get any interrupts anymore
117 		// that's why we don't wait forever for it.
118 }
119 
120 
121 static void
122 compute_pll_divisors(const display_mode &current, uint32 &postDivisor,
123 	uint32 &nDivisor, uint32 &m1Divisor, uint32 &m2Divisor)
124 {
125 	float requestedPixelClock = current.timing.pixel_clock / 1000.0f;
126 	float referenceClock = gInfo->shared_info->pll_info.reference_frequency / 1000.0f;
127 
128 	TRACE(("required MHz: %g\n", requestedPixelClock));
129 
130 	float best = requestedPixelClock;
131 	uint32 bestP = 0, bestN = 0, bestM = 0;
132 
133 	// Note, the limits are taken from the X driver; they have not yet been tested
134 
135 	for (uint32 p = 3; p < 31; p++) {
136 		for (uint32 n = 3; n < 16; n++) {
137 			for (uint32 m1 = 6; m1 < 26; m1++) {
138 				for (uint32 m2 = 6; m2 < 16; m2++) {
139 					uint32 m = m1 * 5 + m2;
140 					float error = fabs(requestedPixelClock - ((referenceClock * m) / n) / (p*4));
141 					if (error < best) {
142 						best = error;
143 						bestP = p;
144 						bestN = n;
145 						bestM = m;
146 						if (error == 0)
147 							break;
148 					}
149 				}
150 			}
151 		}
152 	}
153 
154 	postDivisor = bestP;
155 	nDivisor = bestN;
156 
157 	TRACE(("found: %g MHz (p = %lu, n = %lu, m = %lu (m1 = %lu, m2 = %lu)\n",
158 		((referenceClock * bestM) / bestN) / (bestP*4), bestP, bestN, bestM,
159 		m1Divisor, m2Divisor));
160 
161 	m1Divisor = bestM / 5;
162 	m2Divisor = bestM % 5;
163 	while (m2Divisor < 6) {
164 		m1Divisor--;
165 		m2Divisor += 5;
166 	}
167 }
168 
169 
170 static void
171 get_color_space_format(const display_mode &mode, uint32 &colorMode,
172 	uint32 &bytesPerRow, uint32 &bitsPerPixel)
173 {
174 	uint32 bytesPerPixel;
175 
176 	switch (mode.space) {
177 		case B_RGB32_LITTLE:
178 			colorMode = DISPLAY_CONTROL_RGB32;
179 			bytesPerPixel = 4;
180 			bitsPerPixel = 32;
181 			break;
182 		case B_RGB16_LITTLE:
183 			colorMode = DISPLAY_CONTROL_RGB16;
184 			bytesPerPixel = 2;
185 			bitsPerPixel = 16;
186 			break;
187 		case B_RGB15_LITTLE:
188 			colorMode = DISPLAY_CONTROL_RGB15;
189 			bytesPerPixel = 2;
190 			bitsPerPixel = 15;
191 			break;
192 		case B_CMAP8:
193 		default:
194 			colorMode = DISPLAY_CONTROL_CMAP8;
195 			bytesPerPixel = 1;
196 			bitsPerPixel = 8;
197 			break;
198 	}
199 
200 	bytesPerRow = mode.virtual_width * bytesPerPixel;
201 }
202 
203 
204 //	#pragma mark -
205 
206 
207 uint32
208 intel_accelerant_mode_count(void)
209 {
210 	TRACE(("intel_accelerant_mode_count()\n"));
211 	return gInfo->shared_info->mode_count;
212 }
213 
214 
215 status_t
216 intel_get_mode_list(display_mode *modeList)
217 {
218 	TRACE(("intel_get_mode_info()\n"));
219 	memcpy(modeList, gInfo->mode_list, gInfo->shared_info->mode_count * sizeof(display_mode));
220 	return B_OK;
221 }
222 
223 
224 status_t
225 intel_propose_display_mode(display_mode *target, const display_mode *low,
226 	const display_mode *high)
227 {
228 	TRACE(("intel_propose_display_mode()\n"));
229 
230 	// just search for the specified mode in the list
231 
232 	for (uint32 i = 0; i < gInfo->shared_info->mode_count; i++) {
233 		display_mode *mode = &gInfo->mode_list[i];
234 
235 		// TODO: improve this, ie. adapt pixel clock to allowed values!!!
236 
237 		if (target->virtual_width != mode->virtual_width
238 			|| target->virtual_height != mode->virtual_height
239 			|| target->space != mode->space)
240 			continue;
241 
242 		*target = *mode;
243 		return B_OK;
244 	}
245 	return B_BAD_VALUE;
246 }
247 
248 
249 status_t
250 intel_set_display_mode(display_mode *mode)
251 {
252 	TRACE(("intel_set_display_mode()\n"));
253 
254 	display_mode target = *mode;
255 
256 	if (mode == NULL || intel_propose_display_mode(&target, mode, mode))
257 		return B_BAD_VALUE;
258 
259 	intel_shared_info &sharedInfo = *gInfo->shared_info;
260 	Autolock locker(sharedInfo.accelerant_lock);
261 
262 	set_display_power_mode(B_DPMS_OFF);
263 
264 	uint32 colorMode, bytesPerRow, bitsPerPixel;
265 	get_color_space_format(target, colorMode, bytesPerRow, bitsPerPixel);
266 
267 	// free old and allocate new frame buffer in graphics memory
268 
269 	intel_free_memory(gInfo->frame_buffer_handle);
270 
271 	uint32 offset;
272 	if (intel_allocate_memory(bytesPerRow * target.virtual_height,
273 			gInfo->frame_buffer_handle, offset) < B_OK) {
274 		// oh, how did that happen? Unfortunately, there is no really good way back
275 		if (intel_allocate_memory(sharedInfo.current_mode.virtual_height
276 				* sharedInfo.bytes_per_row, gInfo->frame_buffer_handle,
277 				offset) == B_OK) {
278 			sharedInfo.frame_buffer_offset = offset;
279 			write32(INTEL_DISPLAY_A_BASE, offset);
280 		}
281 
282 		return B_NO_MEMORY;
283 	}
284 
285 	sharedInfo.frame_buffer_offset = offset;
286 
287 	// make sure VGA display is disabled
288 	write32(INTEL_VGA_DISPLAY_CONTROL, read32(INTEL_VGA_DISPLAY_CONTROL)
289 		| VGA_DISPLAY_DISABLED);
290 
291 	if (gInfo->head_mode & HEAD_MODE_A_ANALOG) {
292 		// update timing parameters
293 		write32(INTEL_DISPLAY_A_HTOTAL, ((uint32)(target.timing.h_total - 1) << 16)
294 			| ((uint32)target.timing.h_display - 1));
295 		write32(INTEL_DISPLAY_A_HBLANK, ((uint32)(target.timing.h_total - 1) << 16)
296 			| ((uint32)target.timing.h_display - 1));
297 		write32(INTEL_DISPLAY_A_HSYNC, ((uint32)(target.timing.h_sync_end - 1) << 16)
298 			| ((uint32)target.timing.h_sync_start - 1));
299 
300 		write32(INTEL_DISPLAY_A_VTOTAL, ((uint32)(target.timing.v_total - 1) << 16)
301 			| ((uint32)target.timing.v_display - 1));
302 		write32(INTEL_DISPLAY_A_VBLANK, ((uint32)(target.timing.v_total - 1) << 16)
303 			| ((uint32)target.timing.v_display - 1));
304 		write32(INTEL_DISPLAY_A_VSYNC, ((uint32)(target.timing.v_sync_end - 1) << 16)
305 			| ((uint32)target.timing.v_sync_start - 1));
306 
307 		write32(INTEL_DISPLAY_A_IMAGE_SIZE, ((uint32)(target.timing.h_display - 1) << 16)
308 			| ((uint32)target.timing.v_display - 1));
309 
310 		write32(INTEL_DISPLAY_A_ANALOG_PORT, (read32(INTEL_DISPLAY_A_ANALOG_PORT)
311 			& ~(DISPLAY_MONITOR_POLARITY_MASK | DISPLAY_MONITOR_VGA_POLARITY))
312 			| ((target.timing.flags & B_POSITIVE_HSYNC) != 0 ? DISPLAY_MONITOR_POSITIVE_HSYNC : 0)
313 			| ((target.timing.flags & B_POSITIVE_VSYNC) != 0 ? DISPLAY_MONITOR_POSITIVE_VSYNC : 0));
314 
315 		uint32 postDivisor, nDivisor, m1Divisor, m2Divisor;
316 		compute_pll_divisors(target, postDivisor, nDivisor, m1Divisor, m2Divisor);
317 
318 		// switch divisor register with every mode change (not required)
319 		uint32 divisorRegister;
320 		if (gInfo->shared_info->pll_info.divisor_register == INTEL_DISPLAY_A_PLL_DIVISOR_0)
321 			divisorRegister = INTEL_DISPLAY_A_PLL_DIVISOR_1;
322 		else
323 			divisorRegister = INTEL_DISPLAY_A_PLL_DIVISOR_0;
324 
325 		write32(divisorRegister,
326 			(((nDivisor - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT) & DISPLAY_PLL_N_DIVISOR_MASK)
327 			| (((m1Divisor - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT) & DISPLAY_PLL_M1_DIVISOR_MASK)
328 			| (((m2Divisor - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT) & DISPLAY_PLL_M2_DIVISOR_MASK));
329 		write32(INTEL_DISPLAY_A_PLL, DISPLAY_PLL_ENABLED | DISPLAY_PLL_2X_CLOCK
330 			| DISPLAY_PLL_NO_VGA_CONTROL | DISPLAY_PLL_DIVIDE_4X
331 			| (((postDivisor - 2) << DISPLAY_PLL_POST_DIVISOR_SHIFT) & DISPLAY_PLL_POST_DIVISOR_MASK)
332 			| (divisorRegister == INTEL_DISPLAY_A_PLL_DIVISOR_1 ? DISPLAY_PLL_DIVISOR_1 : 0));
333 	}
334 
335 	// These two have to be set for display B, too - this obviously means
336 	// that the second head always must adopt the color space of the first
337 	// head.
338 	write32(INTEL_DISPLAY_A_CONTROL, (read32(INTEL_DISPLAY_A_CONTROL)
339 		& ~(DISPLAY_CONTROL_COLOR_MASK | DISPLAY_CONTROL_GAMMA)) | colorMode);
340 
341 	if (gInfo->head_mode & HEAD_MODE_B_DIGITAL) {
342 		write32(INTEL_DISPLAY_B_IMAGE_SIZE, ((uint32)(target.timing.h_display - 1) << 16)
343 			| ((uint32)target.timing.v_display - 1));
344 
345 		write32(INTEL_DISPLAY_B_CONTROL, (read32(INTEL_DISPLAY_B_CONTROL)
346 			& ~(DISPLAY_CONTROL_COLOR_MASK | DISPLAY_CONTROL_GAMMA)) | colorMode);
347 	}
348 
349 	set_display_power_mode(sharedInfo.dpms_mode);
350 
351 	// changing bytes per row seems to be ignored if the plane/pipe is turned off
352 
353 	if (gInfo->head_mode & HEAD_MODE_A_ANALOG) {
354 		write32(INTEL_DISPLAY_A_BYTES_PER_ROW, bytesPerRow);
355 		write32(INTEL_DISPLAY_A_BASE, sharedInfo.frame_buffer_offset);
356 			// triggers writing back double-buffered registers
357 	}
358 	if (gInfo->head_mode & HEAD_MODE_B_DIGITAL) {
359 		write32(INTEL_DISPLAY_B_BYTES_PER_ROW, bytesPerRow);
360 		write32(INTEL_DISPLAY_B_BASE, sharedInfo.frame_buffer_offset);
361 			// triggers writing back double-buffered registers
362 	}
363 
364 	// update shared info
365 	sharedInfo.bytes_per_row = bytesPerRow;
366 	sharedInfo.current_mode = target;
367 	sharedInfo.bits_per_pixel = bitsPerPixel;
368 
369 #if 0
370 int fd = open("/boot/home/ie.regs", O_CREAT | O_WRONLY, 0644);
371 if (fd >= 0) {
372 	for (int32 i = 0; i < 0x80000; i += 16) {
373 		char line[512];
374 		int length = sprintf(line, "%05lx: %08lx %08lx %08lx %08lx\n",
375 			i, read32(i), read32(i + 4), read32(i + 8), read32(i + 12));
376 		write(fd, line, length);
377 	}
378 	close(fd);
379 	sync();
380 }
381 #endif
382 
383 	return B_OK;
384 }
385 
386 
387 status_t
388 intel_get_display_mode(display_mode *_currentMode)
389 {
390 	TRACE(("intel_get_display_mode()\n"));
391 	*_currentMode = gInfo->shared_info->current_mode;
392 	return B_OK;
393 }
394 
395 
396 status_t
397 intel_get_frame_buffer_config(frame_buffer_config *config)
398 {
399 	TRACE(("intel_get_frame_buffer_config()\n"));
400 
401 	uint32 offset = gInfo->shared_info->frame_buffer_offset;
402 
403 	config->frame_buffer = gInfo->shared_info->graphics_memory + offset;
404 	config->frame_buffer_dma = gInfo->shared_info->physical_graphics_memory + offset;
405 	config->bytes_per_row = gInfo->shared_info->bytes_per_row;
406 
407 	return B_OK;
408 }
409 
410 
411 status_t
412 intel_get_pixel_clock_limits(display_mode *mode, uint32 *_low, uint32 *_high)
413 {
414 	TRACE(("intel_get_pixel_clock_limits()\n"));
415 
416 	if (_low != NULL) {
417 		// lower limit of about 48Hz vertical refresh
418 		uint32 totalClocks = (uint32)mode->timing.h_total * (uint32)mode->timing.v_total;
419 		uint32 low = (totalClocks * 48L) / 1000L;
420 		if (low < gInfo->shared_info->pll_info.min_frequency)
421 			low = gInfo->shared_info->pll_info.min_frequency;
422 		else if (low > gInfo->shared_info->pll_info.max_frequency)
423 			return B_ERROR;
424 
425 		*_low = low;
426 	}
427 
428 	if (_high != NULL)
429 		*_high = gInfo->shared_info->pll_info.max_frequency;
430 
431 	return B_OK;
432 }
433 
434 
435 status_t
436 intel_move_display(uint16 horizontalStart, uint16 verticalStart)
437 {
438 	TRACE(("intel_move_display()\n"));
439 
440 	intel_shared_info &sharedInfo = *gInfo->shared_info;
441 	Autolock locker(sharedInfo.accelerant_lock);
442 
443 	display_mode &mode = sharedInfo.current_mode;
444 
445 	if (horizontalStart + mode.timing.h_display > mode.virtual_width
446 		|| verticalStart + mode.timing.v_display > mode.virtual_height)
447 		return B_BAD_VALUE;
448 
449 	mode.h_display_start = horizontalStart;
450 	mode.v_display_start = verticalStart;
451 
452 	if (gInfo->head_mode & HEAD_MODE_A_ANALOG) {
453 		write32(INTEL_DISPLAY_A_BASE, sharedInfo.frame_buffer_offset
454 			+ verticalStart * sharedInfo.bytes_per_row
455 			+ horizontalStart * (sharedInfo.bits_per_pixel + 7) / 8);
456 	}
457 	if (gInfo->head_mode & HEAD_MODE_B_DIGITAL) {
458 		write32(INTEL_DISPLAY_B_BASE, sharedInfo.frame_buffer_offset
459 			+ verticalStart * sharedInfo.bytes_per_row
460 			+ horizontalStart * (sharedInfo.bits_per_pixel + 7) / 8);
461 	}
462 
463 	return B_OK;
464 }
465 
466 
467 status_t
468 intel_get_timing_constraints(display_timing_constraints *constraints)
469 {
470 	TRACE(("intel_get_timing_contraints()\n"));
471 	return B_ERROR;
472 }
473 
474 
475 void
476 intel_set_indexed_colors(uint count, uint8 first, uint8 *colors, uint32 flags)
477 {
478 	TRACE(("intel_set_indexed_colors(colors = %p, first = %u)\n", colors, first));
479 
480 	if (colors == NULL)
481 		return;
482 
483 	Autolock locker(gInfo->shared_info->accelerant_lock);
484 
485 	for (; count-- > 0; first++) {
486 		uint32 color = colors[0] << 16 | colors[1] << 8 | colors[2];
487 		colors += 3;
488 
489 		if (gInfo->head_mode & HEAD_MODE_A_ANALOG)
490 			write32(INTEL_DISPLAY_A_PALETTE + first * sizeof(uint32), color);
491 		if (gInfo->head_mode & HEAD_MODE_B_DIGITAL)
492 			write32(INTEL_DISPLAY_B_PALETTE + first * sizeof(uint32), color);
493 	}
494 }
495 
496