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