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