2 * Copyright 2007-2014, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
7 #include <create_display_modes.h>
13 #include <compute_display_timing.h>
14 #include <video_overlay.h>
17 #define POSITIVE_SYNC \
18 (B_POSITIVE_HSYNC | B_POSITIVE_VSYNC)
20 (B_8_BIT_DAC | B_HARDWARE_CURSOR | B_PARALLEL_ACCESS | B_DPMS \
21 | B_SUPPORTS_OVERLAYS)
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) */
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) */
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) */
34 {{29580, 800, 816, 896, 992, 480, 481, 484, 497, B_POSITIVE_VSYNC
}, B_CMAP8
, 800, 480, 0, 0, MODE_FLAGS
}, /* 800x480x60Hz */
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) */
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) */
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) */
52 {{74520, 1280, 1368, 1424, 1656, 720, 724, 730, 750, POSITIVE_SYNC
}, B_CMAP8
, 1280, 720, 0, 0, MODE_FLAGS
}, /* Vesa_Monitor_@60Hz_(1280X720) */
54 {{83460, 1280, 1344, 1480, 1680, 800, 801, 804, 828, B_POSITIVE_VSYNC
}, B_CMAP8
, 1280, 800, 0, 0, MODE_FLAGS
}, /* WXGA (1280x800x60) */
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) */
60 {{85500, 1360, 1424, 1536, 1792, 768, 771, 778, 795, POSITIVE_SYNC
}, B_CMAP8
, 1360, 768, 0, 0, MODE_FLAGS
}, /* 1360x768 60Hz */
62 {{85765, 1366, 1494, 1624, 1798, 768, 770, 776, 795, POSITIVE_SYNC
}, B_CMAP8
, 1366, 768, 0, 0, MODE_FLAGS
}, /* 1366x768 60Hz */
64 {{122600, 1400, 1488, 1640, 1880, 1050, 1051, 1054, 1087, POSITIVE_SYNC
}, B_CMAP8
, 1400, 1050, 0, 0, MODE_FLAGS
}, /* Vesa_Monitor_@60Hz_(1400X1050) */
65 {{155800, 1400, 1464, 1784, 1912, 1050, 1052, 1064, 1090, POSITIVE_SYNC
}, B_CMAP8
, 1400, 1050, 0, 0, MODE_FLAGS
}, /* Vesa_Monitor_@75Hz_(1400X1050) */
67 {{106500, 1440, 1520, 1672, 1904, 900, 901, 904, 932, POSITIVE_SYNC
}, B_CMAP8
, 1440, 900, 0, 0, MODE_FLAGS
}, /* Vesa_Monitor_@60Hz_(1440X900) */
69 {{120420, 1600, 1632, 2088, 2120, 900, 918, 927, 946, POSITIVE_SYNC
}, B_CMAP8
, 1600, 900, 0, 0, MODE_FLAGS
}, /* Vesa_Monitor_@60Hz_(1600X900) */
71 {{162000, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC
}, B_CMAP8
, 1600, 1200, 0, 0, MODE_FLAGS
}, /* Vesa_Monitor_@60Hz_(1600X1200X8.Z1) */
72 {{175500, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC
}, B_CMAP8
, 1600, 1200, 0, 0, MODE_FLAGS
}, /* Vesa_Monitor_@65Hz_(1600X1200X8.Z1) */
73 {{189000, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC
}, B_CMAP8
, 1600, 1200, 0, 0, MODE_FLAGS
}, /* Vesa_Monitor_@70Hz_(1600X1200X8.Z1) */
74 {{202500, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC
}, B_CMAP8
, 1600, 1200, 0, 0, MODE_FLAGS
}, /* Vesa_Monitor_@75Hz_(1600X1200X8.Z1) */
75 {{216000, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC
}, B_CMAP8
, 1600, 1200, 0, 0, MODE_FLAGS
}, /* Vesa_Monitor_@80Hz_(1600X1200X8.Z1) */
76 {{229500, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC
}, B_CMAP8
, 1600, 1200, 0, 0, MODE_FLAGS
}, /* Vesa_Monitor_@85Hz_(1600X1200X8.Z1) */
78 {{147100, 1680, 1784, 1968, 2256, 1050, 1051, 1054, 1087, POSITIVE_SYNC
}, B_CMAP8
, 1680, 1050, 0, 0, MODE_FLAGS
}, /* Vesa_Monitor_@60Hz_(1680X1050) */
80 {{172000, 1920, 2040, 2248, 2576, 1080, 1081, 1084, 1118, POSITIVE_SYNC
}, B_CMAP8
, 1920, 1080, 0, 0, MODE_FLAGS
}, /* 1920x1080 60Hz */
81 //{{160000, 1920, 2010, 2060, 2110, 1200, 1202, 1208, 1235, POSITIVE_SYNC}, B_CMAP8, 1920, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1920X1200) */
82 {{193160, 1920, 2048, 2256, 2592, 1200, 1201, 1204, 1242, POSITIVE_SYNC
}, B_CMAP8
, 1920, 1200, 0, 0, MODE_FLAGS
}, /* Vesa_Monitor_@60Hz_(1920X1200) */
84 static const uint32 kNumBaseModes
= sizeof(kBaseModeList
) / sizeof(display_mode
);
94 bool AddModes(edid1_info
* info
);
95 bool AddModes(const display_mode
* modes
,
98 bool CreateColorSpaces(const color_space
* spaces
,
100 void Filter(check_display_mode_hook hook
);
103 const display_mode
* Modes() const { return fModes
; }
104 uint32
Count() const { return fCount
; }
107 bool _MakeSpace(uint32 count
);
108 bool _AddMode(const display_mode
& mode
);
109 void _RemoveModeAt(uint32 index
);
110 void _AddBaseMode(uint16 width
, uint16 height
,
112 display_mode
* _FindMode(uint16 width
, uint16 height
) const;
115 display_mode
* fModes
;
120 } // namespace BPrivate
122 using namespace BPrivate
;
126 get_refresh_rate(const display_mode
& mode
)
128 return float(mode
.timing
.pixel_clock
* 1000)
129 / float(mode
.timing
.h_total
* mode
.timing
.v_total
);
134 compare_mode(const void* _mode1
, const void* _mode2
)
136 display_mode
*mode1
= (display_mode
*)_mode1
;
137 display_mode
*mode2
= (display_mode
*)_mode2
;
138 uint16 width1
, width2
, height1
, height2
;
140 width1
= mode1
->virtual_width
;
141 height1
= mode1
->virtual_height
;
142 width2
= mode2
->virtual_width
;
143 height2
= mode2
->virtual_height
;
145 if (width1
!= width2
)
146 return width1
- width2
;
148 if (height1
!= height2
)
149 return height1
- height2
;
151 if (mode1
->space
!= mode2
->space
)
152 return mode1
->space
- mode2
->space
;
154 return (int)(100 * (get_refresh_rate(*mode1
) - get_refresh_rate(*mode2
)));
170 ModeList::~ModeList()
177 ModeList::AddModes(edid1_info
* info
)
179 if (info
->established_timing
.res_720x400x70
)
180 _AddBaseMode(720, 400, 70);
181 if (info
->established_timing
.res_720x400x88
)
182 _AddBaseMode(720, 400, 88);
184 if (info
->established_timing
.res_640x480x60
)
185 _AddBaseMode(640, 480, 60);
186 if (info
->established_timing
.res_640x480x67
)
187 _AddBaseMode(640, 480, 67);
188 if (info
->established_timing
.res_640x480x72
)
189 _AddBaseMode(640, 480, 72);
190 if (info
->established_timing
.res_640x480x75
)
191 _AddBaseMode(640, 480, 75);
193 if (info
->established_timing
.res_800x600x56
)
194 _AddBaseMode(800, 600, 56);
195 if (info
->established_timing
.res_800x600x60
)
196 _AddBaseMode(800, 600, 60);
197 if (info
->established_timing
.res_800x600x72
)
198 _AddBaseMode(800, 600, 72);
199 if (info
->established_timing
.res_800x600x75
)
200 _AddBaseMode(800, 600, 75);
203 if (info
->established_timing
.res_832x624x75
)
204 _AddBaseMode(832, 624, 75);
206 if (info
->established_timing
.res_1024x768x87i
)
207 _AddBaseMode(1024, 768, 87);
209 if (info
->established_timing
.res_1024x768x60
)
210 _AddBaseMode(1024, 768, 60);
211 if (info
->established_timing
.res_1024x768x70
)
212 _AddBaseMode(1024, 768, 70);
213 if (info
->established_timing
.res_1024x768x75
)
214 _AddBaseMode(1024, 768, 75);
216 if (info
->established_timing
.res_1152x870x75
)
217 _AddBaseMode(1152, 870, 75);
218 if (info
->established_timing
.res_1280x1024x75
)
219 _AddBaseMode(1280, 1024, 75);
221 for (uint32 i
= 0; i
< EDID1_NUM_STD_TIMING
; ++i
) {
222 if (info
->std_timing
[i
].h_size
<= 256)
225 _AddBaseMode(info
->std_timing
[i
].h_size
, info
->std_timing
[i
].v_size
,
226 info
->std_timing
[i
].refresh
);
229 bool hasRanges
= false;
230 uint32 minHorizontalFrequency
= 0;
231 uint32 maxHorizontalFrequency
= 0;
232 uint32 minVerticalFrequency
= 0;
233 uint32 maxVerticalFrequency
= 0;
234 uint32 maxPixelClock
= 0;
236 for (uint32 i
= 0; i
< EDID1_NUM_DETAILED_MONITOR_DESC
; ++i
) {
237 if (info
->detailed_monitor
[i
].monitor_desc_type
238 == EDID1_MONITOR_RANGES
) {
239 edid1_monitor_range
& range
240 = info
->detailed_monitor
[i
].data
.monitor_range
;
243 minHorizontalFrequency
= range
.min_h
;
244 maxHorizontalFrequency
= range
.max_h
;
245 minVerticalFrequency
= range
.min_v
;
246 maxVerticalFrequency
= range
.max_v
;
247 maxPixelClock
= range
.max_clock
* 10000;
249 } else if (info
->detailed_monitor
[i
].monitor_desc_type
250 != EDID1_IS_DETAILED_TIMING
)
253 // TODO: handle flags correctly!
254 const edid1_detailed_timing
& timing
255 = info
->detailed_monitor
[i
].data
.detailed_timing
;
258 if (timing
.pixel_clock
<= 0/* || timing.sync != 3*/)
261 mode
.timing
.pixel_clock
= timing
.pixel_clock
* 10;
262 mode
.timing
.h_display
= timing
.h_active
;
263 mode
.timing
.h_sync_start
= timing
.h_active
+ timing
.h_sync_off
;
264 mode
.timing
.h_sync_end
= mode
.timing
.h_sync_start
+ timing
.h_sync_width
;
265 mode
.timing
.h_total
= timing
.h_active
+ timing
.h_blank
;
266 mode
.timing
.v_display
= timing
.v_active
;
267 mode
.timing
.v_sync_start
= timing
.v_active
+ timing
.v_sync_off
;
268 mode
.timing
.v_sync_end
= mode
.timing
.v_sync_start
+ timing
.v_sync_width
;
269 mode
.timing
.v_total
= timing
.v_active
+ timing
.v_blank
;
270 mode
.timing
.flags
= 0;
271 if (timing
.sync
== 3) {
273 mode
.timing
.flags
|= B_POSITIVE_HSYNC
;
275 mode
.timing
.flags
|= B_POSITIVE_VSYNC
;
277 if (timing
.interlaced
)
278 mode
.timing
.flags
|= B_TIMING_INTERLACED
;
279 mode
.space
= B_RGB32
;
280 mode
.virtual_width
= timing
.h_active
;
281 mode
.virtual_height
= timing
.v_active
;
282 mode
.h_display_start
= 0;
283 mode
.v_display_start
= 0;
284 mode
.flags
= MODE_FLAGS
;
289 // Add other modes from the base list that satisfy the display's
292 for (uint32 i
= 0; i
< kNumBaseModes
; i
++) {
293 const display_mode
& mode
= kBaseModeList
[i
];
295 // Check if a mode with this resolution already exists
297 if (_FindMode(mode
.timing
.h_display
, mode
.timing
.v_display
) != NULL
)
300 // Check monitor limits
303 uint32 verticalFrequency
= 1000 * mode
.timing
.pixel_clock
304 / (mode
.timing
.h_total
* mode
.timing
.v_total
);
305 uint32 horizontalFrequency
= mode
.timing
.h_total
* verticalFrequency
308 if (minHorizontalFrequency
> horizontalFrequency
309 || maxHorizontalFrequency
< horizontalFrequency
310 || minVerticalFrequency
> verticalFrequency
311 || maxVerticalFrequency
< verticalFrequency
312 || maxPixelClock
< mode
.timing
.pixel_clock
)
324 ModeList::AddModes(const display_mode
* modes
, uint32 count
)
326 if (!_MakeSpace(count
))
329 for (uint32 i
= 0; i
< count
; i
++) {
330 fModes
[fCount
++] = modes
[i
];
338 ModeList::CreateColorSpaces(const color_space
* spaces
, uint32 count
)
340 uint32 baseModeCount
= fCount
;
341 size_t baseModesSize
= baseModeCount
* sizeof(display_mode
);
342 display_mode
* baseModes
= (display_mode
*)malloc(baseModesSize
);
343 if (baseModes
== NULL
)
346 memcpy(baseModes
, fModes
, baseModesSize
);
348 for (uint32 i
= 0; i
< count
; i
++) {
349 if (i
> 0 && !AddModes(baseModes
, baseModeCount
)) {
354 for (uint32 j
= 0; j
< baseModeCount
; j
++) {
355 fModes
[j
+ fCount
- baseModeCount
].space
= spaces
[i
];
365 ModeList::Filter(check_display_mode_hook hook
)
370 for (uint32 i
= fCount
; i
-- > 0;) {
371 if (!hook(&fModes
[i
]))
381 qsort(fModes
, fCount
, sizeof(display_mode
), compare_mode
);
384 for (uint32 i
= fCount
; i
-- > 1;) {
385 if (compare_mode(&fModes
[i
], &fModes
[i
- 1]) == 0)
392 ModeList::_AddBaseMode(uint16 width
, uint16 height
, uint32 refresh
)
394 // Check the manually tweaked list first
396 for (uint32 i
= 0; i
< kNumBaseModes
; i
++) {
397 const display_mode
& mode
= kBaseModeList
[i
];
399 // Add mode if width and height match, and the computed refresh rate of
400 // the mode is within 1.2 percent of the refresh rate specified by the
401 // caller. Note that refresh rates computed from mode parameters is
402 // not exact; thus, the tolerance of 1.2% was obtained by testing the
403 // various established modes that can be selected by the EDID info.
405 if (mode
.timing
.h_display
== width
&& mode
.timing
.v_display
== height
406 && fabs(get_refresh_rate(mode
) - refresh
) < refresh
* 0.012) {
412 // If that didn't have any entries, compute the entry
414 if (compute_display_timing(width
, height
, refresh
, false, &mode
.timing
)
418 fill_display_mode(width
, height
, &mode
);
425 ModeList::_FindMode(uint16 width
, uint16 height
) const
427 for (uint32 i
= 0; i
< fCount
; i
++) {
428 const display_mode
& mode
= fModes
[i
];
430 if (mode
.timing
.h_display
== width
&& mode
.timing
.v_display
== height
)
439 ModeList::_MakeSpace(uint32 count
)
441 if (fCount
+ count
<= fCapacity
)
444 uint32 capacity
= (fCapacity
+ count
+ 0xf) & ~0xf;
445 display_mode
* modes
= (display_mode
*)realloc(fModes
,
446 capacity
* sizeof(display_mode
));
451 fCapacity
= capacity
;
457 ModeList::_AddMode(const display_mode
& mode
)
459 // TODO: filter by monitor timing constraints!
460 // TODO: remove double entries
464 fModes
[fCount
++] = mode
;
470 ModeList::_RemoveModeAt(uint32 index
)
472 if (index
< fCount
- 1) {
473 memmove(&fModes
[index
], &fModes
[index
+ 1],
474 (fCount
- 1 - index
) * sizeof(display_mode
));
485 create_display_modes(const char* name
, edid1_info
* edid
,
486 const display_mode
* initialModes
, uint32 initialModeCount
,
487 const color_space
*spaces
, uint32 spacesCount
,
488 check_display_mode_hook hook
, display_mode
** _modes
, uint32
* _count
)
490 if (_modes
== NULL
|| _count
== NULL
)
493 // compile initial mode list from the different sources
496 if (initialModes
!= NULL
)
497 modes
.AddModes(initialModes
, initialModeCount
);
500 modes
.AddModes(edid
);
502 modes
.AddModes(kBaseModeList
, kNumBaseModes
);
504 // filter out modes the caller doesn't like, and multiply modes for
507 if (spaces
== NULL
) {
508 const color_space kDefaultSpaces
[] = {B_RGB32_LITTLE
, B_RGB16_LITTLE
,
509 B_RGB15_LITTLE
, B_CMAP8
};
510 modes
.CreateColorSpaces(kDefaultSpaces
,
511 sizeof(kDefaultSpaces
) / sizeof(kDefaultSpaces
[0]));
513 modes
.CreateColorSpaces(spaces
, spacesCount
);
518 // create area for output modes
520 size_t size
= (sizeof(display_mode
) * modes
.Count() + B_PAGE_SIZE
- 1)
521 & ~(B_PAGE_SIZE
- 1);
523 area_id area
= create_area(name
, (void **)&list
, B_ANY_ADDRESS
,
524 size
, B_NO_LOCK
, B_READ_AREA
| B_WRITE_AREA
);
528 memcpy(list
, modes
.Modes(), sizeof(display_mode
) * modes
.Count());
530 *_count
= modes
.Count();
537 fill_display_mode(uint32 width
, uint32 height
, display_mode
* mode
)
539 mode
->space
= B_CMAP8
;
540 mode
->virtual_width
= width
;
541 mode
->virtual_height
= height
;
542 mode
->h_display_start
= 0;
543 mode
->v_display_start
= 0;
544 mode
->flags
= MODE_FLAGS
;