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