1 /* 2 * Copyright 2007, 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 {{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) */ 25 {{25175, 640, 656, 752, 800, 480, 490, 492, 525, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(640X480X8.Z1) */ 26 {{27500, 640, 672, 768, 864, 480, 488, 494, 530, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* 640X480X60Hz */ 27 {{30500, 640, 672, 768, 864, 480, 517, 523, 588, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* SVGA_640X480X60HzNI */ 28 {{31500, 640, 664, 704, 832, 480, 489, 492, 520, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70-72Hz_(640X480X8.Z1) */ 29 {{31500, 640, 656, 720, 840, 480, 481, 484, 500, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(640X480X8.Z1) */ 30 {{36000, 640, 696, 752, 832, 480, 481, 484, 509, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(640X480X8.Z1) */ 31 {{38100, 800, 832, 960, 1088, 600, 602, 606, 620, 0}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* SVGA_800X600X56HzNI */ 32 {{40000, 800, 840, 968, 1056, 600, 601, 605, 628, POSITIVE_SYNC}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(800X600X8.Z1) */ 33 {{49500, 800, 816, 896, 1056, 600, 601, 604, 625, POSITIVE_SYNC}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(800X600X8.Z1) */ 34 {{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) */ 35 {{56250, 800, 832, 896, 1048, 600, 601, 604, 631, POSITIVE_SYNC}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(800X600X8.Z1) */ 36 {{65000, 1024, 1048, 1184, 1344, 768, 771, 777, 806, 0}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1024X768X8.Z1) */ 37 {{75000, 1024, 1048, 1184, 1328, 768, 771, 777, 806, 0}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70-72Hz_(1024X768X8.Z1) */ 38 {{78750, 1024, 1040, 1136, 1312, 768, 769, 772, 800, POSITIVE_SYNC}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1024X768X8.Z1) */ 39 {{94500, 1024, 1072, 1168, 1376, 768, 769, 772, 808, POSITIVE_SYNC}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(1024X768X8.Z1) */ 40 {{94200, 1152, 1184, 1280, 1472, 864, 865, 868, 914, POSITIVE_SYNC}, B_CMAP8, 1152, 864, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70Hz_(1152X864X8.Z1) */ 41 {{108000, 1152, 1216, 1344, 1600, 864, 865, 868, 900, POSITIVE_SYNC}, B_CMAP8, 1152, 864, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1152X864X8.Z1) */ 42 {{121500, 1152, 1216, 1344, 1568, 864, 865, 868, 911, POSITIVE_SYNC}, B_CMAP8, 1152, 864, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(1152X864X8.Z1) */ 43 {{108000, 1280, 1328, 1440, 1688, 1024, 1025, 1028, 1066, POSITIVE_SYNC}, B_CMAP8, 1280, 1024, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1280X1024X8.Z1) */ 44 {{135000, 1280, 1296, 1440, 1688, 1024, 1025, 1028, 1066, POSITIVE_SYNC}, B_CMAP8, 1280, 1024, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1280X1024X8.Z1) */ 45 {{157500, 1280, 1344, 1504, 1728, 1024, 1025, 1028, 1072, POSITIVE_SYNC}, B_CMAP8, 1280, 1024, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(1280X1024X8.Z1) */ 46 47 {{106500, 1440, 1520, 1672, 1904, 900, 901, 904, 932, POSITIVE_SYNC}, B_CMAP8, 1440, 900, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1440X900) */ 48 49 {{162000, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1600X1200X8.Z1) */ 50 {{175500, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@65Hz_(1600X1200X8.Z1) */ 51 {{189000, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70Hz_(1600X1200X8.Z1) */ 52 {{202500, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1600X1200X8.Z1) */ 53 {{216000, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@80Hz_(1600X1200X8.Z1) */ 54 {{229500, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(1600X1200X8.Z1) */ 55 56 {{147100, 1680, 1784, 1968, 2256, 1050, 1051, 1054, 1087, POSITIVE_SYNC}, B_CMAP8, 1680, 1050, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1680X1050) */ 57 58 {{154000, 1920, 1968, 2000, 2080, 1200, 1203, 1209, 1235, POSITIVE_SYNC}, B_CMAP8, 1920, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1920X1200) */ 59 }; 60 static const uint32 kNumBaseModes = sizeof(kBaseModeList) / sizeof(display_mode); 61 62 63 namespace BPrivate { 64 65 class ModeList { 66 public: 67 ModeList(); 68 ~ModeList(); 69 70 bool AddModes(edid1_info* info); 71 bool AddModes(const display_mode* modes, uint32 count); 72 73 bool CreateColorSpaces(const color_space* spaces, uint32 count); 74 void Filter(check_display_mode_hook hook); 75 void Clean(); 76 77 const display_mode* Modes() const { return fModes; } 78 uint32 Count() const { return fCount; } 79 80 private: 81 bool _MakeSpace(uint32 count); 82 bool _AddMode(const display_mode* mode); 83 void _RemoveModeAt(uint32 index); 84 void _AddBaseMode(uint16 width, uint16 height, uint32 refresh); 85 86 display_mode* fModes; 87 uint32 fCount; 88 uint32 fCapacity; 89 }; 90 91 } // namespace BPrivate 92 93 using namespace BPrivate; 94 95 96 static float 97 get_refresh_rate(const display_mode& mode) 98 { 99 // we have to be catious as refresh rate cannot be controlled directly, 100 // so it suffers under rounding errors and hardware restrictions 101 return rint(10 * float(mode.timing.pixel_clock * 1000) / 102 float(mode.timing.h_total * mode.timing.v_total)) / 10.0; 103 } 104 105 106 static int 107 compare_mode(const void* _mode1, const void* _mode2) 108 { 109 display_mode *mode1 = (display_mode *)_mode1; 110 display_mode *mode2 = (display_mode *)_mode2; 111 uint16 width1, width2, height1, height2; 112 113 width1 = mode1->virtual_width; 114 height1 = mode1->virtual_height; 115 width2 = mode2->virtual_width; 116 height2 = mode2->virtual_height; 117 118 if (width1 != width2) 119 return width1 - width2; 120 121 if (height1 != height2) 122 return height1 - height2; 123 124 if (mode1->space != mode2->space) 125 return mode1->space - mode2->space; 126 127 return (int)(10 * get_refresh_rate(*mode1) 128 - 10 * get_refresh_rate(*mode2)); 129 } 130 131 132 // #pragma mark - 133 134 135 ModeList::ModeList() 136 : 137 fModes(NULL), 138 fCount(0), 139 fCapacity(0) 140 { 141 } 142 143 144 ModeList::~ModeList() 145 { 146 free(fModes); 147 } 148 149 150 bool 151 ModeList::AddModes(edid1_info* info) 152 { 153 if (info->established_timing.res_720x400x70) 154 _AddBaseMode(720, 400, 70); 155 if (info->established_timing.res_720x400x88) 156 _AddBaseMode(720, 400, 88); 157 158 if (info->established_timing.res_640x480x60) 159 _AddBaseMode(640, 480, 60); 160 if (info->established_timing.res_640x480x67) 161 _AddBaseMode(640, 480, 67); 162 if (info->established_timing.res_640x480x72) 163 _AddBaseMode(640, 480, 72); 164 if (info->established_timing.res_640x480x75) 165 _AddBaseMode(640, 480, 75); 166 167 if (info->established_timing.res_800x600x56) 168 _AddBaseMode(800, 600, 56); 169 if (info->established_timing.res_800x600x60) 170 _AddBaseMode(800, 600, 60); 171 if (info->established_timing.res_800x600x72) 172 _AddBaseMode(800, 600, 72); 173 if (info->established_timing.res_800x600x75) 174 _AddBaseMode(800, 600, 75); 175 176 #if 0 177 if (info->established_timing.res_832x624x75) 178 _AddBaseMode(832, 624, 75); 179 180 if (info->established_timing.res_1024x768x87i) 181 _AddBaseMode(1024, 768, 87); 182 #endif 183 if (info->established_timing.res_1024x768x60) 184 _AddBaseMode(1024, 768, 60); 185 if (info->established_timing.res_1024x768x70) 186 _AddBaseMode(1024, 768, 70); 187 if (info->established_timing.res_1024x768x75) 188 _AddBaseMode(1024, 768, 75); 189 190 if (info->established_timing.res_1152x870x75) 191 _AddBaseMode(1152, 870, 75); 192 if (info->established_timing.res_1280x1024x75) 193 _AddBaseMode(1280, 1024, 75); 194 195 for (uint32 i = 0; i < EDID1_NUM_STD_TIMING; ++i) { 196 if (info->std_timing[i].h_size <= 256) 197 continue; 198 199 _AddBaseMode(info->std_timing[i].h_size, info->std_timing[i].v_size, 200 info->std_timing[i].refresh); 201 } 202 203 for (uint32 i = 0; i < EDID1_NUM_DETAILED_MONITOR_DESC; ++i) { 204 if (info->detailed_monitor[i].monitor_desc_type != EDID1_IS_DETAILED_TIMING) 205 continue; 206 207 const edid1_detailed_timing& timing = info->detailed_monitor[i].data.detailed_timing; 208 display_mode mode; 209 mode.timing.pixel_clock = timing.pixel_clock * 10; 210 mode.timing.h_display = timing.h_active; 211 mode.timing.h_sync_start = timing.h_blank; 212 mode.timing.h_sync_end = timing.h_sync_off; 213 mode.timing.h_total = timing.h_sync_width; 214 mode.timing.v_display = timing.v_active; 215 mode.timing.v_sync_start = timing.v_blank; 216 mode.timing.v_sync_end = timing.v_sync_off; 217 mode.timing.v_total = timing.v_sync_width; 218 mode.timing.flags = POSITIVE_SYNC; 219 mode.space = B_RGB32; 220 mode.virtual_width = timing.h_active; 221 mode.virtual_height = timing.v_active; 222 mode.h_display_start = 0; 223 mode.v_display_start = 0; 224 mode.flags = MODE_FLAGS; 225 } 226 227 // TODO: add other modes from the base list that satisfy the display's 228 // requirements! 229 230 return true; 231 } 232 233 234 bool 235 ModeList::AddModes(const display_mode* modes, uint32 count) 236 { 237 if (!_MakeSpace(count)) 238 return false; 239 240 for (uint32 i = 0; i < count; i++) { 241 fModes[fCount++] = modes[i]; 242 } 243 244 return true; 245 } 246 247 248 bool 249 ModeList::CreateColorSpaces(const color_space* spaces, uint32 count) 250 { 251 uint32 modeCount = fCount; 252 253 for (uint32 i = 0; i < count; i++) { 254 if (i > 0 && !AddModes(fModes, modeCount)) 255 return false; 256 257 for (uint32 j = 0; j < modeCount; j++) { 258 fModes[j + fCount - modeCount].space = spaces[i]; 259 } 260 } 261 262 return true; 263 } 264 265 266 void 267 ModeList::Filter(check_display_mode_hook hook) 268 { 269 if (hook == NULL) 270 return; 271 272 for (uint32 i = fCount; i-- > 0;) { 273 if (!hook(&fModes[i])) 274 _RemoveModeAt(i); 275 } 276 } 277 278 279 void 280 ModeList::Clean() 281 { 282 // sort mode list 283 qsort(fModes, fCount, sizeof(display_mode), compare_mode); 284 285 // remove duplicates 286 for (uint32 i = fCount; i-- > 1;) { 287 if (compare_mode(&fModes[i], &fModes[i - 1]) == 0) 288 _RemoveModeAt(i); 289 } 290 } 291 292 293 void 294 ModeList::_AddBaseMode(uint16 width, uint16 height, uint32 refresh) 295 { 296 for (uint32 i = 0; i < kNumBaseModes; i++) { 297 const display_mode& mode = kBaseModeList[i]; 298 299 if (mode.timing.h_display == width && mode.timing.v_display == height 300 && get_refresh_rate(mode) == refresh) { 301 _AddMode(&mode); 302 return; 303 } 304 } 305 } 306 307 308 bool 309 ModeList::_MakeSpace(uint32 count) 310 { 311 if (fCount + count <= fCapacity) 312 return true; 313 314 uint32 capacity = (fCapacity + count + 0xf) & ~0xf; 315 display_mode* modes = (display_mode*)realloc(fModes, 316 capacity * sizeof(display_mode)); 317 if (modes == NULL) 318 return false; 319 320 fModes = modes; 321 fCapacity = capacity; 322 return true; 323 } 324 325 326 bool 327 ModeList::_AddMode(const display_mode* mode) 328 { 329 if (!_MakeSpace(1)) 330 return false; 331 332 fModes[fCount++] = *mode; 333 return true; 334 } 335 336 337 void 338 ModeList::_RemoveModeAt(uint32 index) 339 { 340 if (index < fCount - 1) { 341 memmove(&fModes[index], &fModes[index + 1], 342 (fCount - 1 - index) * sizeof(display_mode)); 343 } 344 345 fCount--; 346 } 347 348 349 // #pragma mark - 350 351 352 extern "C" area_id 353 create_display_modes(const char* name, edid1_info* edid, 354 const display_mode* initialModes, uint32 initialModeCount, 355 const color_space *spaces, uint32 spacesCount, 356 check_display_mode_hook hook, display_mode** _modes, uint32* _count) 357 { 358 if (_modes == NULL || _count == NULL) 359 return B_BAD_VALUE; 360 361 // compile initial mode list from the different sources 362 363 ModeList modes; 364 modes.AddModes(initialModes, initialModeCount); 365 366 if (edid != NULL) 367 modes.AddModes(edid); 368 else 369 modes.AddModes(kBaseModeList, kNumBaseModes); 370 371 // filter out modes the caller doesn't like, and multiply modes for 372 // every color space 373 374 if (spaces == NULL) { 375 const color_space kDefaultSpaces[] = {B_RGB32_LITTLE, B_RGB16_LITTLE, 376 B_RGB15_LITTLE, B_CMAP8}; 377 modes.CreateColorSpaces(kDefaultSpaces, 378 sizeof(kDefaultSpaces) / sizeof(kDefaultSpaces[0])); 379 } else 380 modes.CreateColorSpaces(spaces, spacesCount); 381 382 modes.Filter(hook); 383 modes.Clean(); 384 385 // create area for output modes 386 387 size_t size = (sizeof(display_mode) * modes.Count() + B_PAGE_SIZE - 1) 388 & ~(B_PAGE_SIZE - 1); 389 display_mode *list; 390 area_id area = create_area(name, (void **)&list, B_ANY_ADDRESS, 391 size, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); 392 if (area < B_OK) 393 return area; 394 395 memcpy(list, modes.Modes(), sizeof(display_mode) * modes.Count()); 396 *_modes = list; 397 *_count = modes.Count(); 398 399 return area; 400 } 401 402