xref: /haiku/src/add-ons/accelerants/common/create_display_modes.cpp (revision 220d04022750f40f8bac8f01fa551211e28d04f2)
1 /*
2  * Copyright 2007-2014, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <create_display_modes.h>
8 
9 #include <math.h>
10 #include <stdlib.h>
11 #include <string.h>
12 
13 #include <compute_display_timing.h>
14 #include <video_overlay.h>
15 
16 
17 #define	POSITIVE_SYNC \
18 	(B_POSITIVE_HSYNC | B_POSITIVE_VSYNC)
19 #define MODE_FLAGS \
20 	(B_8_BIT_DAC | B_HARDWARE_CURSOR | B_PARALLEL_ACCESS | B_DPMS \
21 		| B_SUPPORTS_OVERLAYS)
22 
23 // TODO: move this list into the app_server
24 static const display_mode kBaseModeList[] = {
25 	{{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) */
26 
27 	{{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) */
28 
29 	{{25175, 640, 656, 752, 800, 480, 490, 492, 525, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(640X480X8.Z1) */
30 	{{31500, 640, 664, 704, 832, 480, 489, 492, 520, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70-72Hz_(640X480X8.Z1) */
31 	{{31500, 640, 656, 720, 840, 480, 481, 484, 500, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(640X480X8.Z1) */
32 	{{36000, 640, 696, 752, 832, 480, 481, 484, 509, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(640X480X8.Z1) */
33 
34 	{{29580, 800, 816, 896, 992, 480, 481, 484, 497, B_POSITIVE_VSYNC}, B_CMAP8, 800, 480, 0, 0, MODE_FLAGS}, /* 800x480x60Hz */
35 
36 	{{38100, 800, 832, 960, 1088, 600, 602, 606, 620, 0}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* SVGA_800X600X56HzNI */
37 	{{40000, 800, 840, 968, 1056, 600, 601, 605, 628, POSITIVE_SYNC}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(800X600X8.Z1) */
38 	{{49500, 800, 816, 896, 1056, 600, 601, 604, 625, POSITIVE_SYNC}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(800X600X8.Z1) */
39 	{{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) */
40 	{{56250, 800, 832, 896, 1048, 600, 601, 604, 631, POSITIVE_SYNC}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(800X600X8.Z1) */
41 
42 	{{65000, 1024, 1048, 1184, 1344, 768, 771, 777, 806, 0}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1024X768X8.Z1) */
43 	{{75000, 1024, 1048, 1184, 1328, 768, 771, 777, 806, 0}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70-72Hz_(1024X768X8.Z1) */
44 	{{78750, 1024, 1040, 1136, 1312, 768, 769, 772, 800, POSITIVE_SYNC}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1024X768X8.Z1) */
45 	{{94500, 1024, 1072, 1168, 1376, 768, 769, 772, 808, POSITIVE_SYNC}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(1024X768X8.Z1) */
46 
47 	{{81640, 1152, 1216, 1336, 1520, 864, 865, 868, 895, POSITIVE_SYNC}, B_CMAP8, 1152, 864, 0, 0, MODE_FLAGS}, /* 1152x864x60Hz */
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 
52 	{{74520, 1280, 1368, 1424, 1656, 720, 724, 730, 750, POSITIVE_SYNC}, B_CMAP8, 1280, 720, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1280X720) */
53 
54 	{{83460, 1280, 1344, 1480, 1680, 800, 801, 804, 828, B_POSITIVE_VSYNC}, B_CMAP8, 1280, 800, 0, 0, MODE_FLAGS}, /* WXGA (1280x800x60) */
55 
56 	{{108000, 1280, 1328, 1440, 1688, 1024, 1025, 1028, 1066, POSITIVE_SYNC}, B_CMAP8, 1280, 1024, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1280X1024X8.Z1) */
57 	{{135000, 1280, 1296, 1440, 1688, 1024, 1025, 1028, 1066, POSITIVE_SYNC}, B_CMAP8, 1280, 1024, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1280X1024X8.Z1) */
58 	{{157500, 1280, 1344, 1504, 1728, 1024, 1025, 1028, 1072, POSITIVE_SYNC}, B_CMAP8, 1280, 1024, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(1280X1024X8.Z1) */
59 
60 	{{149400, 1366, 1494, 1624, 1798, 768, 770, 776, 795, POSITIVE_SYNC}, B_CMAP8, 1366, 768, 0, 0, MODE_FLAGS}, /* 1366x768 60Hz */
61 
62 	{{122600, 1400, 1488, 1640, 1880, 1050, 1051, 1054, 1087, POSITIVE_SYNC}, B_CMAP8, 1400, 1050, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1400X1050) */
63 	{{155800, 1400, 1464, 1784, 1912, 1050, 1052, 1064, 1090, POSITIVE_SYNC}, B_CMAP8, 1400, 1050, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1400X1050) */
64 
65 	{{106500, 1440, 1520, 1672, 1904, 900, 901, 904, 932, POSITIVE_SYNC}, B_CMAP8, 1440, 900, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1440X900) */
66 
67 	{{120420, 1600, 1632, 2088, 2120, 900, 918, 927, 946, POSITIVE_SYNC}, B_CMAP8, 1600, 900, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1600X900) */
68 
69 	{{162000, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1600X1200X8.Z1) */
70 	{{175500, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@65Hz_(1600X1200X8.Z1) */
71 	{{189000, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70Hz_(1600X1200X8.Z1) */
72 	{{202500, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1600X1200X8.Z1) */
73 	{{216000, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@80Hz_(1600X1200X8.Z1) */
74 	{{229500, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(1600X1200X8.Z1) */
75 
76 	{{147100, 1680, 1784, 1968, 2256, 1050, 1051, 1054, 1087, POSITIVE_SYNC}, B_CMAP8, 1680, 1050, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1680X1050) */
77 
78 	{{172000, 1920, 2040, 2248, 2576, 1080, 1081, 1084, 1118, POSITIVE_SYNC}, B_CMAP8, 1920, 1080, 0, 0, MODE_FLAGS}, /* 1920x1080 60Hz */
79 	//{{160000, 1920, 2010, 2060, 2110, 1200, 1202, 1208, 1235, POSITIVE_SYNC}, B_CMAP8, 1920, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1920X1200) */
80 	{{193160, 1920, 2048, 2256, 2592, 1200, 1201, 1204, 1242, POSITIVE_SYNC}, B_CMAP8, 1920, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1920X1200) */
81 };
82 static const uint32 kNumBaseModes = sizeof(kBaseModeList) / sizeof(display_mode);
83 
84 
85 namespace BPrivate {
86 
87 class ModeList {
88 public:
89 								ModeList();
90 								~ModeList();
91 
92 			bool				AddModes(edid1_info* info);
93 			bool				AddModes(const display_mode* modes,
94 									uint32 count);
95 
96 			bool				CreateColorSpaces(const color_space* spaces,
97 									uint32 count);
98 			void				Filter(check_display_mode_hook hook);
99 			void				Clean();
100 
101 			const display_mode*	Modes() const { return fModes; }
102 			uint32				Count() const { return fCount; }
103 
104 private:
105 			bool				_MakeSpace(uint32 count);
106 			bool				_AddMode(const display_mode& mode);
107 			void				_RemoveModeAt(uint32 index);
108 			void				_AddBaseMode(uint16 width, uint16 height,
109 									uint32 refresh);
110 			display_mode*		_FindMode(uint16 width, uint16 height) const;
111 
112 private:
113 			display_mode*		fModes;
114 			uint32				fCount;
115 			uint32				fCapacity;
116 };
117 
118 }	// namespace BPrivate
119 
120 using namespace BPrivate;
121 
122 
123 static float
124 get_refresh_rate(const display_mode& mode)
125 {
126 	return float(mode.timing.pixel_clock * 1000)
127 		/ float(mode.timing.h_total * mode.timing.v_total);
128 }
129 
130 
131 static int
132 compare_mode(const void* _mode1, const void* _mode2)
133 {
134 	display_mode *mode1 = (display_mode *)_mode1;
135 	display_mode *mode2 = (display_mode *)_mode2;
136 	uint16 width1, width2, height1, height2;
137 
138 	width1 = mode1->virtual_width;
139 	height1 = mode1->virtual_height;
140 	width2 = mode2->virtual_width;
141 	height2 = mode2->virtual_height;
142 
143 	if (width1 != width2)
144 		return width1 - width2;
145 
146 	if (height1 != height2)
147 		return height1 - height2;
148 
149 	if (mode1->space != mode2->space)
150 		return mode1->space - mode2->space;
151 
152 	return (int)(100 * (get_refresh_rate(*mode1) - get_refresh_rate(*mode2)));
153 }
154 
155 
156 //	#pragma mark -
157 
158 
159 ModeList::ModeList()
160 	:
161 	fModes(NULL),
162 	fCount(0),
163 	fCapacity(0)
164 {
165 }
166 
167 
168 ModeList::~ModeList()
169 {
170 	free(fModes);
171 }
172 
173 
174 bool
175 ModeList::AddModes(edid1_info* info)
176 {
177 	if (info->established_timing.res_720x400x70)
178 		_AddBaseMode(720, 400, 70);
179 	if (info->established_timing.res_720x400x88)
180 		_AddBaseMode(720, 400, 88);
181 
182 	if (info->established_timing.res_640x480x60)
183 		_AddBaseMode(640, 480, 60);
184 	if (info->established_timing.res_640x480x67)
185 		_AddBaseMode(640, 480, 67);
186 	if (info->established_timing.res_640x480x72)
187 		_AddBaseMode(640, 480, 72);
188 	if (info->established_timing.res_640x480x75)
189 		_AddBaseMode(640, 480, 75);
190 
191 	if (info->established_timing.res_800x600x56)
192 		_AddBaseMode(800, 600, 56);
193 	if (info->established_timing.res_800x600x60)
194 		_AddBaseMode(800, 600, 60);
195 	if (info->established_timing.res_800x600x72)
196 		_AddBaseMode(800, 600, 72);
197 	if (info->established_timing.res_800x600x75)
198 		_AddBaseMode(800, 600, 75);
199 
200 #if 0
201 	if (info->established_timing.res_832x624x75)
202 		_AddBaseMode(832, 624, 75);
203 
204 	if (info->established_timing.res_1024x768x87i)
205 		_AddBaseMode(1024, 768, 87);
206 #endif
207 	if (info->established_timing.res_1024x768x60)
208 		_AddBaseMode(1024, 768, 60);
209 	if (info->established_timing.res_1024x768x70)
210 		_AddBaseMode(1024, 768, 70);
211 	if (info->established_timing.res_1024x768x75)
212 		_AddBaseMode(1024, 768, 75);
213 
214 	if (info->established_timing.res_1152x870x75)
215 		_AddBaseMode(1152, 870, 75);
216 	if (info->established_timing.res_1280x1024x75)
217 		_AddBaseMode(1280, 1024, 75);
218 
219 	for (uint32 i = 0; i < EDID1_NUM_STD_TIMING; ++i) {
220 		if (info->std_timing[i].h_size <= 256)
221 			continue;
222 
223 		_AddBaseMode(info->std_timing[i].h_size, info->std_timing[i].v_size,
224 			info->std_timing[i].refresh);
225 	}
226 
227 	bool hasRanges = false;
228 	uint32 minHorizontalFrequency = 0;
229 	uint32 maxHorizontalFrequency = 0;
230 	uint32 minVerticalFrequency = 0;
231 	uint32 maxVerticalFrequency = 0;
232 	uint32 maxPixelClock = 0;
233 
234 	for (uint32 i = 0; i < EDID1_NUM_DETAILED_MONITOR_DESC; ++i) {
235 		if (info->detailed_monitor[i].monitor_desc_type
236 				== EDID1_MONITOR_RANGES) {
237 			edid1_monitor_range& range
238 				= info->detailed_monitor[i].data.monitor_range;
239 
240 			hasRanges = true;
241 			minHorizontalFrequency = range.min_h;
242 			maxHorizontalFrequency = range.max_h;
243 			minVerticalFrequency = range.min_v;
244 			maxVerticalFrequency = range.max_v;
245 			maxPixelClock = range.max_clock * 10000;
246 			continue;
247 		} else if (info->detailed_monitor[i].monitor_desc_type
248 				!= EDID1_IS_DETAILED_TIMING)
249 			continue;
250 
251 		// TODO: handle flags correctly!
252 		const edid1_detailed_timing& timing
253 			= info->detailed_monitor[i].data.detailed_timing;
254 		display_mode mode;
255 
256 		if (timing.pixel_clock <= 0/* || timing.sync != 3*/)
257 			continue;
258 
259 		mode.timing.pixel_clock = timing.pixel_clock * 10;
260 		mode.timing.h_display = timing.h_active;
261 		mode.timing.h_sync_start = timing.h_active + timing.h_sync_off;
262 		mode.timing.h_sync_end = mode.timing.h_sync_start + timing.h_sync_width;
263 		mode.timing.h_total = timing.h_active + timing.h_blank;
264 		mode.timing.v_display = timing.v_active;
265 		mode.timing.v_sync_start = timing.v_active + timing.v_sync_off;
266 		mode.timing.v_sync_end = mode.timing.v_sync_start + timing.v_sync_width;
267 		mode.timing.v_total = timing.v_active + timing.v_blank;
268 		mode.timing.flags = 0;
269 		if (timing.sync == 3) {
270 			if (timing.misc & 1)
271 				mode.timing.flags |= B_POSITIVE_HSYNC;
272 			if (timing.misc & 2)
273 				mode.timing.flags |= B_POSITIVE_VSYNC;
274 		}
275 		if (timing.interlaced)
276 			mode.timing.flags |= B_TIMING_INTERLACED;
277 		mode.space = B_RGB32;
278 		mode.virtual_width = timing.h_active;
279 		mode.virtual_height = timing.v_active;
280 		mode.h_display_start = 0;
281 		mode.v_display_start = 0;
282 		mode.flags = MODE_FLAGS;
283 
284 		_AddMode(mode);
285 	}
286 
287 	// Add other modes from the base list that satisfy the display's
288 	// requirements
289 
290 	for (uint32 i = 0; i < kNumBaseModes; i++) {
291 		const display_mode& mode = kBaseModeList[i];
292 
293 		// Check if a mode with this resolution already exists
294 
295 		if (_FindMode(mode.timing.h_display, mode.timing.v_display) != NULL)
296 			continue;
297 
298 		// Check monitor limits
299 
300 		if (hasRanges) {
301 			uint32 verticalFrequency = 1000 * mode.timing.pixel_clock
302 				/ (mode.timing.h_total * mode.timing.v_total);
303 			uint32 horizontalFrequency = mode.timing.h_total * verticalFrequency
304 				/ 1000;
305 
306 			if (minHorizontalFrequency > horizontalFrequency
307 				|| maxHorizontalFrequency < horizontalFrequency
308 				|| minVerticalFrequency > verticalFrequency
309 				|| maxVerticalFrequency < verticalFrequency
310 				|| maxPixelClock < mode.timing.pixel_clock)
311 				continue;
312 		}
313 
314 		_AddMode(mode);
315 	}
316 
317 	return true;
318 }
319 
320 
321 bool
322 ModeList::AddModes(const display_mode* modes, uint32 count)
323 {
324 	if (!_MakeSpace(count))
325 		return false;
326 
327 	for (uint32 i = 0; i < count; i++) {
328 		fModes[fCount++] = modes[i];
329 	}
330 
331 	return true;
332 }
333 
334 
335 bool
336 ModeList::CreateColorSpaces(const color_space* spaces, uint32 count)
337 {
338 	uint32 baseModeCount = fCount;
339 	size_t baseModesSize = baseModeCount * sizeof(display_mode);
340 	display_mode* baseModes = (display_mode*)malloc(baseModesSize);
341 	if (baseModes == NULL)
342 		return false;
343 
344 	memcpy(baseModes, fModes, baseModesSize);
345 
346 	for (uint32 i = 0; i < count; i++) {
347 		if (i > 0 && !AddModes(baseModes, baseModeCount)) {
348 			free(baseModes);
349 			return false;
350 		}
351 
352 		for (uint32 j = 0; j < baseModeCount; j++) {
353 			fModes[j + fCount - baseModeCount].space = spaces[i];
354 		}
355 	}
356 
357 	free(baseModes);
358 	return true;
359 }
360 
361 
362 void
363 ModeList::Filter(check_display_mode_hook hook)
364 {
365 	if (hook == NULL)
366 		return;
367 
368 	for (uint32 i = fCount; i-- > 0;) {
369 		if (!hook(&fModes[i]))
370 			_RemoveModeAt(i);
371 	}
372 }
373 
374 
375 void
376 ModeList::Clean()
377 {
378 	// sort mode list
379 	qsort(fModes, fCount, sizeof(display_mode), compare_mode);
380 
381 	// remove duplicates
382 	for (uint32 i = fCount; i-- > 1;) {
383 		if (compare_mode(&fModes[i], &fModes[i - 1]) == 0)
384 			_RemoveModeAt(i);
385 	}
386 }
387 
388 
389 void
390 ModeList::_AddBaseMode(uint16 width, uint16 height, uint32 refresh)
391 {
392 	// Check the manually tweaked list first
393 
394 	for (uint32 i = 0; i < kNumBaseModes; i++) {
395 		const display_mode& mode = kBaseModeList[i];
396 
397 		// Add mode if width and height match, and the computed refresh rate of
398 		// the mode is within 1.2 percent of the refresh rate specified by the
399 		// caller.  Note that refresh rates computed from mode parameters is
400 		// not exact;  thus, the tolerance of 1.2% was obtained by testing the
401 		// various established modes that can be selected by the EDID info.
402 
403 		if (mode.timing.h_display == width && mode.timing.v_display == height
404 			&& fabs(get_refresh_rate(mode) - refresh) < refresh * 0.012) {
405 			_AddMode(mode);
406 			return;
407 		}
408 	}
409 
410 	// If that didn't have any entries, compute the entry
411 	display_mode mode;
412 	if (compute_display_timing(width, height, refresh, false, &mode.timing)
413 			!= B_OK)
414 		return;
415 
416 	fill_display_mode(width, height, &mode);
417 
418 	_AddMode(mode);
419 }
420 
421 
422 display_mode*
423 ModeList::_FindMode(uint16 width, uint16 height) const
424 {
425 	for (uint32 i = 0; i < fCount; i++) {
426 		const display_mode& mode = fModes[i];
427 
428 		if (mode.timing.h_display == width && mode.timing.v_display == height)
429 			return &fModes[i];
430 	}
431 
432 	return NULL;
433 }
434 
435 
436 bool
437 ModeList::_MakeSpace(uint32 count)
438 {
439 	if (fCount + count <= fCapacity)
440 		return true;
441 
442 	uint32 capacity = (fCapacity + count + 0xf) & ~0xf;
443 	display_mode* modes = (display_mode*)realloc(fModes,
444 		capacity * sizeof(display_mode));
445 	if (modes == NULL)
446 		return false;
447 
448 	fModes = modes;
449 	fCapacity = capacity;
450 	return true;
451 }
452 
453 
454 bool
455 ModeList::_AddMode(const display_mode& mode)
456 {
457 	// TODO: filter by monitor timing constraints!
458 	// TODO: remove double entries
459 	if (!_MakeSpace(1))
460 		return false;
461 
462 	fModes[fCount++] = mode;
463 	return true;
464 }
465 
466 
467 void
468 ModeList::_RemoveModeAt(uint32 index)
469 {
470 	if (index < fCount - 1) {
471 		memmove(&fModes[index], &fModes[index + 1],
472 			(fCount - 1 - index) * sizeof(display_mode));
473 	}
474 
475 	fCount--;
476 }
477 
478 
479 //	#pragma mark -
480 
481 
482 extern "C" area_id
483 create_display_modes(const char* name, edid1_info* edid,
484 	const display_mode* initialModes, uint32 initialModeCount,
485 	const color_space *spaces, uint32 spacesCount,
486 	check_display_mode_hook hook, display_mode** _modes, uint32* _count)
487 {
488 	if (_modes == NULL || _count == NULL)
489 		return B_BAD_VALUE;
490 
491 	// compile initial mode list from the different sources
492 
493 	ModeList modes;
494 	if (initialModes != NULL)
495 		modes.AddModes(initialModes, initialModeCount);
496 
497 	if (edid != NULL)
498 		modes.AddModes(edid);
499 	else
500 		modes.AddModes(kBaseModeList, kNumBaseModes);
501 
502 	// filter out modes the caller doesn't like, and multiply modes for
503 	// every color space
504 
505 	if (spaces == NULL) {
506 		const color_space kDefaultSpaces[] = {B_RGB32_LITTLE, B_RGB16_LITTLE,
507 			B_RGB15_LITTLE, B_CMAP8};
508 		modes.CreateColorSpaces(kDefaultSpaces,
509 			sizeof(kDefaultSpaces) / sizeof(kDefaultSpaces[0]));
510 	} else
511 		modes.CreateColorSpaces(spaces, spacesCount);
512 
513 	modes.Filter(hook);
514 	modes.Clean();
515 
516 	// create area for output modes
517 
518 	size_t size = (sizeof(display_mode) * modes.Count() + B_PAGE_SIZE - 1)
519 		& ~(B_PAGE_SIZE - 1);
520 	display_mode *list;
521 	area_id area = create_area(name, (void **)&list, B_ANY_ADDRESS,
522 		size, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
523 	if (area < B_OK)
524 		return area;
525 
526 	memcpy(list, modes.Modes(), sizeof(display_mode) * modes.Count());
527 	*_modes = list;
528 	*_count = modes.Count();
529 
530 	return area;
531 }
532 
533 
534 void
535 fill_display_mode(uint32 width, uint32 height, display_mode* mode)
536 {
537 	mode->space = B_CMAP8;
538 	mode->virtual_width = width;
539 	mode->virtual_height = height;
540 	mode->h_display_start = 0;
541 	mode->v_display_start = 0;
542 	mode->flags = MODE_FLAGS;
543 }
544