xref: /haiku/src/add-ons/accelerants/intel_extreme/mode.cpp (revision 55b40aa53a835472ec7952b138ae4256203d02e4)
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 	{{110808, 1440, 1488, 1504, 1944, 900, 901, 904, 950, POSITIVE_SYNC}, B_CMAP8, 1440, 900, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1280X1024X8.Z1) */
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 static const uint32 kNumBaseModes = sizeof(kBaseModeList) / sizeof(display_mode);
67 static const uint32 kMaxNumModes = kNumBaseModes * 4;
68 
69 
70 /**	Creates the initial mode list of the primary accelerant.
71  *	It's called from intel_init_accelerant().
72  */
73 
74 status_t
75 create_mode_list(void)
76 {
77 	color_space spaces[4] = {B_RGB32_LITTLE, B_RGB16_LITTLE, B_RGB15_LITTLE, B_CMAP8};
78 	size_t size = ROUND_TO_PAGE_SIZE(kMaxNumModes * sizeof(display_mode));
79 	display_mode *list;
80 
81 	gInfo->mode_list_area = create_area("intel extreme modes", (void **)&list, B_ANY_ADDRESS,
82 		size, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
83 	if (gInfo->mode_list_area < B_OK)
84 		return gInfo->mode_list_area;
85 
86 	// for now, just copy all of the modes with different color spaces
87 
88 	const display_mode *source = kBaseModeList;
89 	uint32 count = 0;
90 
91 	for (uint32 i = 0; i < kNumBaseModes; i++) {
92 		for (uint32 j = 0; j < (sizeof(spaces) / sizeof(color_space)); j++) {
93 			list[count] = *source;
94 			list[count].space = spaces[j];
95 
96 			count++;
97 		}
98 
99 		source++;
100 	}
101 
102 	gInfo->mode_list = list;
103 	gInfo->shared_info->mode_list_area = gInfo->mode_list_area;
104 	gInfo->shared_info->mode_count = count;
105 
106 	return B_OK;
107 }
108 
109 
110 void
111 wait_for_vblank(void)
112 {
113 	acquire_sem_etc(gInfo->shared_info->vblank_sem, 1, B_RELATIVE_TIMEOUT, 25000);
114 		// With the output turned off via DPMS, we might not get any interrupts anymore
115 		// that's why we don't wait forever for it.
116 }
117 
118 
119 static void
120 compute_pll_divisors(const display_mode &current, uint32 &postDivisor,
121 	uint32 &nDivisor, uint32 &m1Divisor, uint32 &m2Divisor)
122 {
123 	float requestedPixelClock = current.timing.pixel_clock / 1000.0f;
124 	float referenceClock = gInfo->shared_info->pll_info.reference_frequency / 1000.0f;
125 
126 	TRACE(("required MHz: %g\n", requestedPixelClock));
127 
128 	float best = requestedPixelClock;
129 	uint32 bestP = 0, bestN = 0, bestM = 0;
130 
131 	// Note, the limits are taken from the X driver; they have not yet been tested
132 
133 	for (uint32 p = 3; p < 31; p++) {
134 		for (uint32 n = 3; n < 16; n++) {
135 			for (uint32 m1 = 6; m1 < 26; m1++) {
136 				for (uint32 m2 = 6; m2 < 16; m2++) {
137 					uint32 m = m1 * 5 + m2;
138 					float error = fabs(requestedPixelClock - ((referenceClock * m) / n) / (p*4));
139 					if (error < best) {
140 						best = error;
141 						bestP = p;
142 						bestN = n;
143 						bestM = m;
144 						if (error == 0)
145 							break;
146 					}
147 				}
148 			}
149 		}
150 	}
151 
152 	postDivisor = bestP;
153 	nDivisor = bestN;
154 
155 	TRACE(("found: %g MHz (p = %lu, n = %lu, m = %lu (m1 = %lu, m2 = %lu)\n",
156 		((referenceClock * bestM) / bestN) / (bestP*4), bestP, bestN, bestM,
157 		m1Divisor, m2Divisor));
158 
159 	m1Divisor = bestM / 5;
160 	m2Divisor = bestM % 5;
161 	while (m2Divisor < 6) {
162 		m1Divisor--;
163 		m2Divisor += 5;
164 	}
165 }
166 
167 
168 static void
169 get_color_space_format(const display_mode &mode, uint32 &colorMode,
170 	uint32 &bytesPerRow, uint32 &bitsPerPixel)
171 {
172 	uint32 bytesPerPixel;
173 
174 	switch (mode.space) {
175 		case B_RGB32_LITTLE:
176 			colorMode = DISPLAY_CONTROL_RGB32;
177 			bytesPerPixel = 4;
178 			bitsPerPixel = 32;
179 			break;
180 		case B_RGB16_LITTLE:
181 			colorMode = DISPLAY_CONTROL_RGB16;
182 			bytesPerPixel = 2;
183 			bitsPerPixel = 16;
184 			break;
185 		case B_RGB15_LITTLE:
186 			colorMode = DISPLAY_CONTROL_RGB15;
187 			bytesPerPixel = 2;
188 			bitsPerPixel = 15;
189 			break;
190 		case B_CMAP8:
191 		default:
192 			colorMode = DISPLAY_CONTROL_CMAP8;
193 			bytesPerPixel = 1;
194 			bitsPerPixel = 8;
195 			break;
196 	}
197 
198 	bytesPerRow = mode.virtual_width * bytesPerPixel;
199 }
200 
201 
202 //	#pragma mark -
203 
204 
205 uint32
206 intel_accelerant_mode_count(void)
207 {
208 	TRACE(("intel_accelerant_mode_count()\n"));
209 	return gInfo->shared_info->mode_count;
210 }
211 
212 
213 status_t
214 intel_get_mode_list(display_mode *modeList)
215 {
216 	TRACE(("intel_get_mode_info()\n"));
217 	memcpy(modeList, gInfo->mode_list, gInfo->shared_info->mode_count * sizeof(display_mode));
218 	return B_OK;
219 }
220 
221 
222 status_t
223 intel_propose_display_mode(display_mode *target, const display_mode *low,
224 	const display_mode *high)
225 {
226 	TRACE(("intel_propose_display_mode()\n"));
227 
228 	// just search for the specified mode in the list
229 
230 	for (uint32 i = 0; i < gInfo->shared_info->mode_count; i++) {
231 		display_mode *mode = &gInfo->mode_list[i];
232 
233 		// TODO: improve this, ie. adapt pixel clock to allowed values!!!
234 
235 		if (target->virtual_width != mode->virtual_width
236 			|| target->virtual_height != mode->virtual_height
237 			|| target->space != mode->space)
238 			continue;
239 
240 		*target = *mode;
241 		return B_OK;
242 	}
243 	return B_BAD_VALUE;
244 }
245 
246 
247 status_t
248 intel_set_display_mode(display_mode *mode)
249 {
250 	TRACE(("intel_set_display_mode()\n"));
251 
252 	display_mode target = *mode;
253 
254 	if (mode == NULL || intel_propose_display_mode(&target, mode, mode))
255 		return B_BAD_VALUE;
256 
257 	intel_shared_info &sharedInfo = *gInfo->shared_info;
258 	Autolock locker(sharedInfo.accelerant_lock);
259 
260 	set_display_power_mode(B_DPMS_OFF);
261 
262 	uint32 colorMode, bytesPerRow, bitsPerPixel;
263 	get_color_space_format(target, colorMode, bytesPerRow, bitsPerPixel);
264 
265 	// free old and allocate new frame buffer in graphics memory
266 
267 	intel_free_memory(gInfo->frame_buffer_handle);
268 
269 	uint32 offset;
270 	if (intel_allocate_memory(bytesPerRow * target.virtual_height,
271 			gInfo->frame_buffer_handle, offset) < B_OK) {
272 		// oh, how did that happen? Unfortunately, there is no really good way back
273 		if (intel_allocate_memory(sharedInfo.current_mode.virtual_height
274 				* sharedInfo.bytes_per_row, gInfo->frame_buffer_handle,
275 				offset) == B_OK) {
276 			sharedInfo.frame_buffer_offset = offset;
277 			write32(INTEL_DISPLAY_A_BASE, offset);
278 		}
279 
280 		return B_NO_MEMORY;
281 	}
282 
283 	sharedInfo.frame_buffer_offset = offset;
284 
285 	// make sure VGA display is disabled
286 	write32(INTEL_VGA_DISPLAY_CONTROL, read32(INTEL_VGA_DISPLAY_CONTROL)
287 		| VGA_DISPLAY_DISABLED);
288 
289 	if (gInfo->head_mode & HEAD_MODE_A_ANALOG) {
290 		// update timing parameters
291 		write32(INTEL_DISPLAY_A_HTOTAL, ((uint32)(target.timing.h_total - 1) << 16)
292 			| ((uint32)target.timing.h_display - 1));
293 		write32(INTEL_DISPLAY_A_HBLANK, ((uint32)(target.timing.h_total - 1) << 16)
294 			| ((uint32)target.timing.h_display - 1));
295 		write32(INTEL_DISPLAY_A_HSYNC, ((uint32)(target.timing.h_sync_end - 1) << 16)
296 			| ((uint32)target.timing.h_sync_start - 1));
297 
298 		write32(INTEL_DISPLAY_A_VTOTAL, ((uint32)(target.timing.v_total - 1) << 16)
299 			| ((uint32)target.timing.v_display - 1));
300 		write32(INTEL_DISPLAY_A_VBLANK, ((uint32)(target.timing.v_total - 1) << 16)
301 			| ((uint32)target.timing.v_display - 1));
302 		write32(INTEL_DISPLAY_A_VSYNC, ((uint32)(target.timing.v_sync_end - 1) << 16)
303 			| ((uint32)target.timing.v_sync_start - 1));
304 
305 		write32(INTEL_DISPLAY_A_IMAGE_SIZE, ((uint32)(target.timing.h_display - 1) << 16)
306 			| ((uint32)target.timing.v_display - 1));
307 
308 		write32(INTEL_DISPLAY_A_ANALOG_PORT, (read32(INTEL_DISPLAY_A_ANALOG_PORT)
309 			& ~(DISPLAY_MONITOR_POLARITY_MASK | DISPLAY_MONITOR_VGA_POLARITY))
310 			| ((target.timing.flags & B_POSITIVE_HSYNC) != 0 ? DISPLAY_MONITOR_POSITIVE_HSYNC : 0)
311 			| ((target.timing.flags & B_POSITIVE_VSYNC) != 0 ? DISPLAY_MONITOR_POSITIVE_VSYNC : 0));
312 
313 		uint32 postDivisor, nDivisor, m1Divisor, m2Divisor;
314 		compute_pll_divisors(target, postDivisor, nDivisor, m1Divisor, m2Divisor);
315 
316 		// switch divisor register with every mode change (not required)
317 		uint32 divisorRegister;
318 		if (gInfo->shared_info->pll_info.divisor_register == INTEL_DISPLAY_A_PLL_DIVISOR_0)
319 			divisorRegister = INTEL_DISPLAY_A_PLL_DIVISOR_1;
320 		else
321 			divisorRegister = INTEL_DISPLAY_A_PLL_DIVISOR_0;
322 
323 		write32(divisorRegister,
324 			(((nDivisor - 2) << DISPLAY_PLL_N_DIVISOR_SHIFT) & DISPLAY_PLL_N_DIVISOR_MASK)
325 			| (((m1Divisor - 2) << DISPLAY_PLL_M1_DIVISOR_SHIFT) & DISPLAY_PLL_M1_DIVISOR_MASK)
326 			| (((m2Divisor - 2) << DISPLAY_PLL_M2_DIVISOR_SHIFT) & DISPLAY_PLL_M2_DIVISOR_MASK));
327 		write32(INTEL_DISPLAY_A_PLL, DISPLAY_PLL_ENABLED | DISPLAY_PLL_2X_CLOCK
328 			| DISPLAY_PLL_NO_VGA_CONTROL | DISPLAY_PLL_DIVIDE_4X
329 			| (((postDivisor - 2) << DISPLAY_PLL_POST_DIVISOR_SHIFT) & DISPLAY_PLL_POST_DIVISOR_MASK)
330 			| (divisorRegister == INTEL_DISPLAY_A_PLL_DIVISOR_1 ? DISPLAY_PLL_DIVISOR_1 : 0));
331 	}
332 
333 	// These two have to be set for display B, too - this obviously means
334 	// that the second head always must adopt the color space of the first
335 	// head.
336 	write32(INTEL_DISPLAY_A_CONTROL, (read32(INTEL_DISPLAY_A_CONTROL)
337 		& ~(DISPLAY_CONTROL_COLOR_MASK | DISPLAY_CONTROL_GAMMA)) | colorMode);
338 
339 	if (gInfo->head_mode & HEAD_MODE_B_DIGITAL) {
340 		write32(INTEL_DISPLAY_B_IMAGE_SIZE, ((uint32)(target.timing.h_display - 1) << 16)
341 			| ((uint32)target.timing.v_display - 1));
342 
343 		write32(INTEL_DISPLAY_B_CONTROL, (read32(INTEL_DISPLAY_B_CONTROL)
344 			& ~(DISPLAY_CONTROL_COLOR_MASK | DISPLAY_CONTROL_GAMMA)) | colorMode);
345 	}
346 
347 	set_display_power_mode(sharedInfo.dpms_mode);
348 
349 	// changing bytes per row seems to be ignored if the plane/pipe is turned off
350 
351 	if (gInfo->head_mode & HEAD_MODE_A_ANALOG) {
352 		write32(INTEL_DISPLAY_A_BYTES_PER_ROW, bytesPerRow);
353 		write32(INTEL_DISPLAY_A_BASE, sharedInfo.frame_buffer_offset);
354 			// triggers writing back double-buffered registers
355 	}
356 	if (gInfo->head_mode & HEAD_MODE_B_DIGITAL) {
357 		write32(INTEL_DISPLAY_B_BYTES_PER_ROW, bytesPerRow);
358 		write32(INTEL_DISPLAY_B_BASE, sharedInfo.frame_buffer_offset);
359 			// triggers writing back double-buffered registers
360 	}
361 
362 	// update shared info
363 	sharedInfo.bytes_per_row = bytesPerRow;
364 	sharedInfo.current_mode = target;
365 	sharedInfo.bits_per_pixel = bitsPerPixel;
366 
367 #if 0
368 int fd = open("/boot/home/ie.regs", O_CREAT | O_WRONLY, 0644);
369 if (fd >= 0) {
370 	for (int32 i = 0; i < 0x80000; i += 16) {
371 		char line[512];
372 		int length = sprintf(line, "%05lx: %08lx %08lx %08lx %08lx\n",
373 			i, read32(i), read32(i + 4), read32(i + 8), read32(i + 12));
374 		write(fd, line, length);
375 	}
376 	close(fd);
377 	sync();
378 }
379 #endif
380 
381 	return B_OK;
382 }
383 
384 
385 status_t
386 intel_get_display_mode(display_mode *_currentMode)
387 {
388 	TRACE(("intel_get_display_mode()\n"));
389 	*_currentMode = gInfo->shared_info->current_mode;
390 	return B_OK;
391 }
392 
393 
394 status_t
395 intel_get_frame_buffer_config(frame_buffer_config *config)
396 {
397 	TRACE(("intel_get_frame_buffer_config()\n"));
398 
399 	uint32 offset = gInfo->shared_info->frame_buffer_offset;
400 
401 	config->frame_buffer = gInfo->shared_info->graphics_memory + offset;
402 	config->frame_buffer_dma = gInfo->shared_info->physical_graphics_memory + offset;
403 	config->bytes_per_row = gInfo->shared_info->bytes_per_row;
404 
405 	return B_OK;
406 }
407 
408 
409 status_t
410 intel_get_pixel_clock_limits(display_mode *mode, uint32 *_low, uint32 *_high)
411 {
412 	TRACE(("intel_get_pixel_clock_limits()\n"));
413 
414 	if (_low != NULL) {
415 		// lower limit of about 48Hz vertical refresh
416 		uint32 totalClocks = (uint32)mode->timing.h_total * (uint32)mode->timing.v_total;
417 		uint32 low = (totalClocks * 48L) / 1000L;
418 		if (low < gInfo->shared_info->pll_info.min_frequency)
419 			low = gInfo->shared_info->pll_info.min_frequency;
420 		else if (low > gInfo->shared_info->pll_info.max_frequency)
421 			return B_ERROR;
422 
423 		*_low = low;
424 	}
425 
426 	if (_high != NULL)
427 		*_high = gInfo->shared_info->pll_info.max_frequency;
428 
429 	return B_OK;
430 }
431 
432 
433 status_t
434 intel_move_display(uint16 horizontalStart, uint16 verticalStart)
435 {
436 	TRACE(("intel_move_display()\n"));
437 
438 	intel_shared_info &sharedInfo = *gInfo->shared_info;
439 	Autolock locker(sharedInfo.accelerant_lock);
440 
441 	display_mode &mode = sharedInfo.current_mode;
442 
443 	if (horizontalStart + mode.timing.h_display > mode.virtual_width
444 		|| verticalStart + mode.timing.v_display > mode.virtual_height)
445 		return B_BAD_VALUE;
446 
447 	mode.h_display_start = horizontalStart;
448 	mode.v_display_start = verticalStart;
449 
450 	if (gInfo->head_mode & HEAD_MODE_A_ANALOG) {
451 		write32(INTEL_DISPLAY_A_BASE, sharedInfo.frame_buffer_offset
452 			+ verticalStart * sharedInfo.bytes_per_row
453 			+ horizontalStart * (sharedInfo.bits_per_pixel + 7) / 8);
454 	}
455 	if (gInfo->head_mode & HEAD_MODE_B_DIGITAL) {
456 		write32(INTEL_DISPLAY_B_BASE, sharedInfo.frame_buffer_offset
457 			+ verticalStart * sharedInfo.bytes_per_row
458 			+ horizontalStart * (sharedInfo.bits_per_pixel + 7) / 8);
459 	}
460 
461 	return B_OK;
462 }
463 
464 
465 status_t
466 intel_get_timing_constraints(display_timing_constraints *constraints)
467 {
468 	TRACE(("intel_get_timing_contraints()\n"));
469 	return B_ERROR;
470 }
471 
472 
473 void
474 intel_set_indexed_colors(uint count, uint8 first, uint8 *colors, uint32 flags)
475 {
476 	TRACE(("intel_set_indexed_colors(colors = %p, first = %u)\n", colors, first));
477 
478 	if (colors == NULL)
479 		return;
480 
481 	Autolock locker(gInfo->shared_info->accelerant_lock);
482 
483 	for (; count-- > 0; first++) {
484 		uint32 color = colors[0] << 16 | colors[1] << 8 | colors[2];
485 		colors += 3;
486 
487 		if (gInfo->head_mode & HEAD_MODE_A_ANALOG)
488 			write32(INTEL_DISPLAY_A_PALETTE + first * sizeof(uint32), color);
489 		if (gInfo->head_mode & HEAD_MODE_B_DIGITAL)
490 			write32(INTEL_DISPLAY_B_PALETTE + first * sizeof(uint32), color);
491 	}
492 }
493 
494