xref: /haiku/src/add-ons/accelerants/common/create_display_modes.cpp (revision cfc3fa87da824bdf593eb8b817a83b6376e77935)
1 /*
2  * Copyright 2007-2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
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 <video_overlay.h>
14 
15 
16 #define	POSITIVE_SYNC \
17 	(B_POSITIVE_HSYNC | B_POSITIVE_VSYNC)
18 #define MODE_FLAGS \
19 	(B_8_BIT_DAC | B_HARDWARE_CURSOR | B_PARALLEL_ACCESS | B_DPMS | B_SUPPORTS_OVERLAYS)
20 
21 // TODO: move this list into the app_server
22 static const display_mode kBaseModeList[] = {
23 	{{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) */
24 
25 	{{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) */
26 
27 	{{25175, 640, 656, 752, 800, 480, 490, 492, 525, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(640X480X8.Z1) */
28 	{{27500, 640, 672, 768, 864, 480, 488, 494, 530, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* 640X480X60Hz */
29 	{{30500, 640, 672, 768, 864, 480, 517, 523, 588, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* SVGA_640X480X60HzNI */
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 	{{122600, 1400, 1488, 1640, 1880, 1050, 1051, 1054, 1087, POSITIVE_SYNC}, B_CMAP8, 1400, 1050, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1400X1050) */
61 	{{155800, 1400, 1464, 1784, 1912, 1050, 1052, 1064, 1090, POSITIVE_SYNC}, B_CMAP8, 1400, 1050, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1400X1050) */
62 
63 	{{106500, 1440, 1520, 1672, 1904, 900, 901, 904, 932, POSITIVE_SYNC}, B_CMAP8, 1440, 900, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1440X900) */
64 
65 	{{162000, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1600X1200X8.Z1) */
66 	{{175500, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@65Hz_(1600X1200X8.Z1) */
67 	{{189000, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70Hz_(1600X1200X8.Z1) */
68 	{{202500, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1600X1200X8.Z1) */
69 	{{216000, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@80Hz_(1600X1200X8.Z1) */
70 	{{229500, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(1600X1200X8.Z1) */
71 
72 	{{147100, 1680, 1784, 1968, 2256, 1050, 1051, 1054, 1087, POSITIVE_SYNC}, B_CMAP8, 1680, 1050, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1680X1050) */
73 
74 	{{160000, 1920, 2010, 2060, 2110, 1200, 1202, 1208, 1235, POSITIVE_SYNC}, B_CMAP8, 1920, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1920X1200) */
75 };
76 static const uint32 kNumBaseModes = sizeof(kBaseModeList) / sizeof(display_mode);
77 
78 
79 namespace BPrivate {
80 
81 class ModeList {
82 public:
83 	ModeList();
84 	~ModeList();
85 
86 	bool AddModes(edid1_info* info);
87 	bool AddModes(const display_mode* modes, uint32 count);
88 
89 	bool CreateColorSpaces(const color_space* spaces, uint32 count);
90 	void Filter(check_display_mode_hook hook);
91 	void Clean();
92 
93 	const display_mode* Modes() const { return fModes; }
94 	uint32 Count() const { return fCount; }
95 
96 private:
97 	bool _MakeSpace(uint32 count);
98 	bool _AddMode(const display_mode* mode);
99 	void _RemoveModeAt(uint32 index);
100 	void _AddBaseMode(uint16 width, uint16 height, uint32 refresh);
101 
102 	display_mode*	fModes;
103 	uint32			fCount;
104 	uint32			fCapacity;
105 };
106 
107 }	// namespace BPrivate
108 
109 using namespace BPrivate;
110 
111 
112 static float
113 get_refresh_rate(const display_mode& mode)
114 {
115 	// we have to be catious as refresh rate cannot be controlled directly,
116 	// so it suffers under rounding errors and hardware restrictions
117 	return rint(10 * float(mode.timing.pixel_clock * 1000) /
118 		float(mode.timing.h_total * mode.timing.v_total)) / 10.0;
119 }
120 
121 
122 static int
123 compare_mode(const void* _mode1, const void* _mode2)
124 {
125 	display_mode *mode1 = (display_mode *)_mode1;
126 	display_mode *mode2 = (display_mode *)_mode2;
127 	uint16 width1, width2, height1, height2;
128 
129 	width1 = mode1->virtual_width;
130 	height1 = mode1->virtual_height;
131 	width2 = mode2->virtual_width;
132 	height2 = mode2->virtual_height;
133 
134 	if (width1 != width2)
135 		return width1 - width2;
136 
137 	if (height1 != height2)
138 		return height1 - height2;
139 
140 	if (mode1->space != mode2->space)
141 		return mode1->space - mode2->space;
142 
143 	return (int)(10 * get_refresh_rate(*mode1)
144 		-  10 * get_refresh_rate(*mode2));
145 }
146 
147 
148 //	#pragma mark -
149 
150 
151 ModeList::ModeList()
152 	:
153 	fModes(NULL),
154 	fCount(0),
155 	fCapacity(0)
156 {
157 }
158 
159 
160 ModeList::~ModeList()
161 {
162 	free(fModes);
163 }
164 
165 
166 bool
167 ModeList::AddModes(edid1_info* info)
168 {
169 	if (info->established_timing.res_720x400x70)
170 		_AddBaseMode(720, 400, 70);
171 	if (info->established_timing.res_720x400x88)
172 		_AddBaseMode(720, 400, 88);
173 
174 	if (info->established_timing.res_640x480x60)
175 		_AddBaseMode(640, 480, 60);
176 	if (info->established_timing.res_640x480x67)
177 		_AddBaseMode(640, 480, 67);
178 	if (info->established_timing.res_640x480x72)
179 		_AddBaseMode(640, 480, 72);
180 	if (info->established_timing.res_640x480x75)
181 		_AddBaseMode(640, 480, 75);
182 
183 	if (info->established_timing.res_800x600x56)
184 		_AddBaseMode(800, 600, 56);
185 	if (info->established_timing.res_800x600x60)
186 		_AddBaseMode(800, 600, 60);
187 	if (info->established_timing.res_800x600x72)
188 		_AddBaseMode(800, 600, 72);
189 	if (info->established_timing.res_800x600x75)
190 		_AddBaseMode(800, 600, 75);
191 
192 #if 0
193 	if (info->established_timing.res_832x624x75)
194 		_AddBaseMode(832, 624, 75);
195 
196 	if (info->established_timing.res_1024x768x87i)
197 		_AddBaseMode(1024, 768, 87);
198 #endif
199 	if (info->established_timing.res_1024x768x60)
200 		_AddBaseMode(1024, 768, 60);
201 	if (info->established_timing.res_1024x768x70)
202 		_AddBaseMode(1024, 768, 70);
203 	if (info->established_timing.res_1024x768x75)
204 		_AddBaseMode(1024, 768, 75);
205 
206 	if (info->established_timing.res_1152x870x75)
207 		_AddBaseMode(1152, 870, 75);
208 	if (info->established_timing.res_1280x1024x75)
209 		_AddBaseMode(1280, 1024, 75);
210 
211 	for (uint32 i = 0; i < EDID1_NUM_STD_TIMING; ++i) {
212 		if (info->std_timing[i].h_size <= 256)
213 			continue;
214 
215 		_AddBaseMode(info->std_timing[i].h_size, info->std_timing[i].v_size,
216 			info->std_timing[i].refresh);
217 	}
218 
219 	for (uint32 i = 0; i < EDID1_NUM_DETAILED_MONITOR_DESC; ++i) {
220 		if (info->detailed_monitor[i].monitor_desc_type != EDID1_IS_DETAILED_TIMING)
221 			continue;
222 
223 		// TODO: handle sync and flags correctly!
224 		const edid1_detailed_timing& timing = info->detailed_monitor[i].data.detailed_timing;
225 		display_mode mode;
226 		mode.timing.pixel_clock = timing.pixel_clock * 10;
227 		mode.timing.h_display = timing.h_active;
228 		mode.timing.h_sync_start = timing.h_active + timing.h_sync_off;
229 		mode.timing.h_sync_end = mode.timing.h_sync_start + timing.h_sync_width;
230 		mode.timing.h_total = timing.h_active + timing.h_blank;
231 		mode.timing.v_display = timing.v_active;
232 		mode.timing.v_sync_start = timing.v_active + timing.v_sync_off;
233 		mode.timing.v_sync_end = mode.timing.v_sync_start + timing.v_sync_width;
234 		mode.timing.v_total = timing.v_active + timing.v_blank;
235 		mode.timing.flags = POSITIVE_SYNC;
236 		if (timing.interlaced)
237 			mode.timing.flags |= B_TIMING_INTERLACED;
238 		mode.space = B_RGB32;
239 		mode.virtual_width = timing.h_active;
240 		mode.virtual_height = timing.v_active;
241 		mode.h_display_start = 0;
242 		mode.v_display_start = 0;
243 		mode.flags = MODE_FLAGS;
244 	}
245 
246 	// TODO: add other modes from the base list that satisfy the display's
247 	//	requirements!
248 
249 	return true;
250 }
251 
252 
253 bool
254 ModeList::AddModes(const display_mode* modes, uint32 count)
255 {
256 	if (!_MakeSpace(count))
257 		return false;
258 
259 	for (uint32 i = 0; i < count; i++) {
260 		fModes[fCount++] = modes[i];
261 	}
262 
263 	return true;
264 }
265 
266 
267 bool
268 ModeList::CreateColorSpaces(const color_space* spaces, uint32 count)
269 {
270 	uint32 modeCount = fCount;
271 
272 	for (uint32 i = 0; i < count; i++) {
273 		if (i > 0 && !AddModes(fModes, modeCount))
274 			return false;
275 
276 		for (uint32 j = 0; j < modeCount; j++) {
277 			fModes[j + fCount - modeCount].space = spaces[i];
278 		}
279 	}
280 
281 	return true;
282 }
283 
284 
285 void
286 ModeList::Filter(check_display_mode_hook hook)
287 {
288 	if (hook == NULL)
289 		return;
290 
291 	for (uint32 i = fCount; i-- > 0;) {
292 		if (!hook(&fModes[i]))
293 			_RemoveModeAt(i);
294 	}
295 }
296 
297 
298 void
299 ModeList::Clean()
300 {
301 	// sort mode list
302 	qsort(fModes, fCount, sizeof(display_mode), compare_mode);
303 
304 	// remove duplicates
305 	for (uint32 i = fCount; i-- > 1;) {
306 		if (compare_mode(&fModes[i], &fModes[i - 1]) == 0)
307 			_RemoveModeAt(i);
308 	}
309 }
310 
311 
312 void
313 ModeList::_AddBaseMode(uint16 width, uint16 height, uint32 refresh)
314 {
315 	for (uint32 i = 0; i < kNumBaseModes; i++) {
316 		const display_mode& mode = kBaseModeList[i];
317 
318 		if (mode.timing.h_display == width && mode.timing.v_display == height
319 			&& get_refresh_rate(mode) == refresh) {
320 			_AddMode(&mode);
321 			return;
322 		}
323 	}
324 }
325 
326 
327 bool
328 ModeList::_MakeSpace(uint32 count)
329 {
330 	if (fCount + count <= fCapacity)
331 		return true;
332 
333 	uint32 capacity = (fCapacity + count + 0xf) & ~0xf;
334 	display_mode* modes = (display_mode*)realloc(fModes,
335 		capacity * sizeof(display_mode));
336 	if (modes == NULL)
337 		return false;
338 
339 	fModes = modes;
340 	fCapacity = capacity;
341 	return true;
342 }
343 
344 
345 bool
346 ModeList::_AddMode(const display_mode* mode)
347 {
348 	if (!_MakeSpace(1))
349 		return false;
350 
351 	fModes[fCount++] = *mode;
352 	return true;
353 }
354 
355 
356 void
357 ModeList::_RemoveModeAt(uint32 index)
358 {
359 	if (index < fCount - 1) {
360 		memmove(&fModes[index], &fModes[index + 1],
361 			(fCount - 1 - index) * sizeof(display_mode));
362 	}
363 
364 	fCount--;
365 }
366 
367 
368 //	#pragma mark -
369 
370 
371 extern "C" area_id
372 create_display_modes(const char* name, edid1_info* edid,
373 	const display_mode* initialModes, uint32 initialModeCount,
374 	const color_space *spaces, uint32 spacesCount,
375 	check_display_mode_hook hook, display_mode** _modes, uint32* _count)
376 {
377 	if (_modes == NULL || _count == NULL)
378 		return B_BAD_VALUE;
379 
380 	// compile initial mode list from the different sources
381 
382 	ModeList modes;
383 	modes.AddModes(initialModes, initialModeCount);
384 
385 	if (edid != NULL)
386 		modes.AddModes(edid);
387 	else
388 		modes.AddModes(kBaseModeList, kNumBaseModes);
389 
390 	// filter out modes the caller doesn't like, and multiply modes for
391 	// every color space
392 
393 	if (spaces == NULL) {
394 		const color_space kDefaultSpaces[] = {B_RGB32_LITTLE, B_RGB16_LITTLE,
395 			B_RGB15_LITTLE, B_CMAP8};
396 		modes.CreateColorSpaces(kDefaultSpaces,
397 			sizeof(kDefaultSpaces) / sizeof(kDefaultSpaces[0]));
398 	} else
399 		modes.CreateColorSpaces(spaces, spacesCount);
400 
401 	modes.Filter(hook);
402 	modes.Clean();
403 
404 	// create area for output modes
405 
406 	size_t size = (sizeof(display_mode) * modes.Count() + B_PAGE_SIZE - 1)
407 		& ~(B_PAGE_SIZE - 1);
408 	display_mode *list;
409 	area_id area = create_area(name, (void **)&list, B_ANY_ADDRESS,
410 		size, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
411 	if (area < B_OK)
412 		return area;
413 
414 	memcpy(list, modes.Modes(), sizeof(display_mode) * modes.Count());
415 	*_modes = list;
416 	*_count = modes.Count();
417 
418 	return area;
419 }
420 
421