diff --git a/headers/private/graphics/common/create_display_modes.h b/headers/private/graphics/common/create_display_modes.h new file mode 100644 index 0000000000..09b6443099 --- /dev/null +++ b/headers/private/graphics/common/create_display_modes.h @@ -0,0 +1,29 @@ +/* + * Copyright 2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _CREATE_DISPLAY_MODES_H +#define _CREATE_DISPLAY_MODES_H + + +#include + +#include + + +typedef bool (*check_display_mode_hook)(display_mode *mode); + +#ifdef __cplusplus +extern "C" { +#endif + +area_id create_display_modes(const char *name, edid1_info *edid, + const display_mode *initialModes, uint32 initialModeCount, + const color_space *spaces, uint32 spacesCount, + check_display_mode_hook hook, display_mode **_modes, uint32 *_count); + +#ifdef __cplusplus +} +#endif + +#endif /* _CREATE_DISPLAY_MODES_H */ diff --git a/src/add-ons/accelerants/common/Jamfile b/src/add-ons/accelerants/common/Jamfile index facbc5a5db..f7cc9b52e9 100644 --- a/src/add-ons/accelerants/common/Jamfile +++ b/src/add-ons/accelerants/common/Jamfile @@ -7,6 +7,7 @@ UsePrivateHeaders [ FDirName graphics radeon ] ; UsePrivateHeaders [ FDirName graphics common ] ; StaticLibrary libaccelerantscommon.a : + create_display_modes.cpp ddc.c decode_edid.c dump_edid.c diff --git a/src/add-ons/accelerants/common/create_display_modes.cpp b/src/add-ons/accelerants/common/create_display_modes.cpp new file mode 100644 index 0000000000..5d0bdfc1c2 --- /dev/null +++ b/src/add-ons/accelerants/common/create_display_modes.cpp @@ -0,0 +1,401 @@ +/* + * Copyright 2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include + +#include +#include +#include + +#include + + +#define POSITIVE_SYNC \ + (B_POSITIVE_HSYNC | B_POSITIVE_VSYNC) +#define MODE_FLAGS \ + (B_8_BIT_DAC | B_HARDWARE_CURSOR | B_PARALLEL_ACCESS | B_DPMS | B_SUPPORTS_OVERLAYS) + +// TODO: move this list into the app_server +static const display_mode kBaseModeList[] = { + {{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) */ + {{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) */ + {{25175, 640, 656, 752, 800, 480, 490, 492, 525, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(640X480X8.Z1) */ + {{27500, 640, 672, 768, 864, 480, 488, 494, 530, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* 640X480X60Hz */ + {{30500, 640, 672, 768, 864, 480, 517, 523, 588, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* SVGA_640X480X60HzNI */ + {{31500, 640, 664, 704, 832, 480, 489, 492, 520, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70-72Hz_(640X480X8.Z1) */ + {{31500, 640, 656, 720, 840, 480, 481, 484, 500, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(640X480X8.Z1) */ + {{36000, 640, 696, 752, 832, 480, 481, 484, 509, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(640X480X8.Z1) */ + {{38100, 800, 832, 960, 1088, 600, 602, 606, 620, 0}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* SVGA_800X600X56HzNI */ + {{40000, 800, 840, 968, 1056, 600, 601, 605, 628, POSITIVE_SYNC}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(800X600X8.Z1) */ + {{49500, 800, 816, 896, 1056, 600, 601, 604, 625, POSITIVE_SYNC}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(800X600X8.Z1) */ + {{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) */ + {{56250, 800, 832, 896, 1048, 600, 601, 604, 631, POSITIVE_SYNC}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(800X600X8.Z1) */ + {{65000, 1024, 1048, 1184, 1344, 768, 771, 777, 806, 0}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1024X768X8.Z1) */ + {{75000, 1024, 1048, 1184, 1328, 768, 771, 777, 806, 0}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70-72Hz_(1024X768X8.Z1) */ + {{78750, 1024, 1040, 1136, 1312, 768, 769, 772, 800, POSITIVE_SYNC}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1024X768X8.Z1) */ + {{94500, 1024, 1072, 1168, 1376, 768, 769, 772, 808, POSITIVE_SYNC}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(1024X768X8.Z1) */ + {{94200, 1152, 1184, 1280, 1472, 864, 865, 868, 914, POSITIVE_SYNC}, B_CMAP8, 1152, 864, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70Hz_(1152X864X8.Z1) */ + {{108000, 1152, 1216, 1344, 1600, 864, 865, 868, 900, POSITIVE_SYNC}, B_CMAP8, 1152, 864, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1152X864X8.Z1) */ + {{121500, 1152, 1216, 1344, 1568, 864, 865, 868, 911, POSITIVE_SYNC}, B_CMAP8, 1152, 864, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(1152X864X8.Z1) */ + {{108000, 1280, 1328, 1440, 1688, 1024, 1025, 1028, 1066, POSITIVE_SYNC}, B_CMAP8, 1280, 1024, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1280X1024X8.Z1) */ + {{135000, 1280, 1296, 1440, 1688, 1024, 1025, 1028, 1066, POSITIVE_SYNC}, B_CMAP8, 1280, 1024, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1280X1024X8.Z1) */ + {{157500, 1280, 1344, 1504, 1728, 1024, 1025, 1028, 1072, POSITIVE_SYNC}, B_CMAP8, 1280, 1024, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(1280X1024X8.Z1) */ + + {{106500, 1440, 1520, 1672, 1904, 900, 901, 904, 932, POSITIVE_SYNC}, B_CMAP8, 1440, 900, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1440X900) */ + + {{162000, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1600X1200X8.Z1) */ + {{175500, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@65Hz_(1600X1200X8.Z1) */ + {{189000, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70Hz_(1600X1200X8.Z1) */ + {{202500, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1600X1200X8.Z1) */ + {{216000, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@80Hz_(1600X1200X8.Z1) */ + {{229500, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(1600X1200X8.Z1) */ + + {{147100, 1680, 1784, 1968, 2256, 1050, 1051, 1054, 1087, POSITIVE_SYNC}, B_CMAP8, 1680, 1050, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1680X1050) */ + + {{154000, 1920, 1968, 2000, 2080, 1200, 1203, 1209, 1235, POSITIVE_SYNC}, B_CMAP8, 1920, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1920X1200) */ +}; +static const uint32 kNumBaseModes = sizeof(kBaseModeList) / sizeof(display_mode); + + +namespace BPrivate { + +class ModeList { +public: + ModeList(); + ~ModeList(); + + bool AddModes(edid1_info* info); + bool AddModes(const display_mode* modes, uint32 count); + + bool CreateColorSpaces(const color_space* spaces, uint32 count); + void Filter(check_display_mode_hook hook); + void Clean(); + + const display_mode* Modes() const { return fModes; } + uint32 Count() const { return fCount; } + +private: + bool _MakeSpace(uint32 count); + bool _AddMode(const display_mode* mode); + void _RemoveModeAt(uint32 index); + void _AddBaseMode(uint16 width, uint16 height, uint32 refresh); + + display_mode* fModes; + uint32 fCount; + uint32 fCapacity; +}; + +} // namespace BPrivate + +using namespace BPrivate; + + +static float +get_refresh_rate(const display_mode& mode) +{ + // we have to be catious as refresh rate cannot be controlled directly, + // so it suffers under rounding errors and hardware restrictions + return rint(10 * float(mode.timing.pixel_clock * 1000) / + float(mode.timing.h_total * mode.timing.v_total)) / 10.0; +} + + +static int +compare_mode(const void* _mode1, const void* _mode2) +{ + display_mode *mode1 = (display_mode *)_mode1; + display_mode *mode2 = (display_mode *)_mode2; + uint16 width1, width2, height1, height2; + + width1 = mode1->virtual_width; + height1 = mode1->virtual_height; + width2 = mode2->virtual_width; + height2 = mode2->virtual_height; + + if (width1 != width2) + return width1 - width2; + + if (height1 != height2) + return height1 - height2; + + if (mode1->space != mode2->space) + return mode1->space - mode2->space; + + return (int)(10 * get_refresh_rate(*mode1) + - 10 * get_refresh_rate(*mode2)); +} + + +// #pragma mark - + + +ModeList::ModeList() + : + fModes(NULL), + fCount(0), + fCapacity(0) +{ +} + + +ModeList::~ModeList() +{ + free(fModes); +} + + +bool +ModeList::AddModes(edid1_info* info) +{ + if (info->established_timing.res_720x400x70) + _AddBaseMode(720, 400, 70); + if (info->established_timing.res_720x400x88) + _AddBaseMode(720, 400, 88); + + if (info->established_timing.res_640x480x60) + _AddBaseMode(640, 480, 60); + if (info->established_timing.res_640x480x67) + _AddBaseMode(640, 480, 67); + if (info->established_timing.res_640x480x72) + _AddBaseMode(640, 480, 72); + if (info->established_timing.res_640x480x75) + _AddBaseMode(640, 480, 75); + + if (info->established_timing.res_800x600x56) + _AddBaseMode(800, 600, 56); + if (info->established_timing.res_800x600x60) + _AddBaseMode(800, 600, 60); + if (info->established_timing.res_800x600x72) + _AddBaseMode(800, 600, 72); + if (info->established_timing.res_800x600x75) + _AddBaseMode(800, 600, 75); + +#if 0 + if (info->established_timing.res_832x624x75) + _AddBaseMode(832, 624, 75); + + if (info->established_timing.res_1024x768x87i) + _AddBaseMode(1024, 768, 87); +#endif + if (info->established_timing.res_1024x768x60) + _AddBaseMode(1024, 768, 60); + if (info->established_timing.res_1024x768x70) + _AddBaseMode(1024, 768, 70); + if (info->established_timing.res_1024x768x75) + _AddBaseMode(1024, 768, 75); + + if (info->established_timing.res_1152x870x75) + _AddBaseMode(1152, 870, 75); + if (info->established_timing.res_1280x1024x75) + _AddBaseMode(1280, 1024, 75); + + for (uint32 i = 0; i < EDID1_NUM_STD_TIMING; ++i) { + if (info->std_timing[i].h_size <= 256) + continue; + + _AddBaseMode(info->std_timing[i].h_size, info->std_timing[i].v_size, + info->std_timing[i].refresh); + } + + for (uint32 i = 0; i < EDID1_NUM_DETAILED_MONITOR_DESC; ++i) { + if (info->detailed_monitor[i].monitor_desc_type != EDID1_IS_DETAILED_TIMING) + continue; + + const edid1_detailed_timing& timing = info->detailed_monitor[i].data.detailed_timing; + display_mode mode; + mode.timing.pixel_clock = timing.pixel_clock * 10; + mode.timing.h_display = timing.h_active; + mode.timing.h_sync_start = timing.h_blank; + mode.timing.h_sync_end = timing.h_sync_off; + mode.timing.h_total = timing.h_sync_width; + mode.timing.v_display = timing.v_active; + mode.timing.v_sync_start = timing.v_blank; + mode.timing.v_sync_end = timing.v_sync_off; + mode.timing.v_total = timing.v_sync_width; + mode.timing.flags = POSITIVE_SYNC; + mode.space = B_RGB32; + mode.virtual_width = timing.h_active; + mode.virtual_height = timing.v_active; + mode.h_display_start = 0; + mode.v_display_start = 0; + mode.flags = MODE_FLAGS; + } + + // TODO: add other modes from the base list that satisfy the display's + // requirements! + + return true; +} + + +bool +ModeList::AddModes(const display_mode* modes, uint32 count) +{ + if (!_MakeSpace(count)) + return false; + + for (uint32 i = 0; i < count; i++) { + fModes[fCount++] = modes[i]; + } + + return true; +} + + +bool +ModeList::CreateColorSpaces(const color_space* spaces, uint32 count) +{ + uint32 modeCount = fCount; + + for (uint32 i = 0; i < count; i++) { + if (i > 0 && !AddModes(fModes, modeCount)) + return false; + + for (uint32 j = 0; j < modeCount; j++) { + fModes[j + fCount - modeCount].space = spaces[i]; + } + } + + return true; +} + + +void +ModeList::Filter(check_display_mode_hook hook) +{ + if (hook == NULL) + return; + + for (uint32 i = fCount; i-- > 0;) { + if (!hook(&fModes[i])) + _RemoveModeAt(i); + } +} + + +void +ModeList::Clean() +{ + // sort mode list + qsort(fModes, fCount, sizeof(display_mode), compare_mode); + + // remove duplicates + for (uint32 i = fCount; i-- > 1;) { + if (compare_mode(&fModes[i], &fModes[i - 1]) == 0) + _RemoveModeAt(i); + } +} + + +void +ModeList::_AddBaseMode(uint16 width, uint16 height, uint32 refresh) +{ + for (uint32 i = 0; i < kNumBaseModes; i++) { + const display_mode& mode = kBaseModeList[i]; + + if (mode.timing.h_display == width && mode.timing.v_display == height + && get_refresh_rate(mode) == refresh) { + _AddMode(&mode); + return; + } + } +} + + +bool +ModeList::_MakeSpace(uint32 count) +{ + if (fCount + count <= fCapacity) + return true; + + uint32 capacity = (fCapacity + count + 0xf) & ~0xf; + display_mode* modes = (display_mode*)realloc(fModes, + capacity * sizeof(display_mode)); + if (modes == NULL) + return false; + + fModes = modes; + fCapacity = capacity; + return true; +} + + +bool +ModeList::_AddMode(const display_mode* mode) +{ + if (!_MakeSpace(1)) + return false; + + fModes[fCount++] = *mode; + return true; +} + + +void +ModeList::_RemoveModeAt(uint32 index) +{ + if (index < fCount - 1) { + memmove(&fModes[index], &fModes[index + 1], + (fCount - 1 - index) * sizeof(display_mode)); + } + + fCount--; +} + + +// #pragma mark - + + +extern "C" area_id +create_display_modes(const char* name, edid1_info* edid, + const display_mode* initialModes, uint32 initialModeCount, + const color_space *spaces, uint32 spacesCount, + check_display_mode_hook hook, display_mode** _modes, uint32* _count) +{ + if (_modes == NULL || _count == NULL) + return B_BAD_VALUE; + + // compile initial mode list from the different sources + + ModeList modes; + modes.AddModes(initialModes, initialModeCount); + + if (edid != NULL) + modes.AddModes(edid); + else + modes.AddModes(kBaseModeList, kNumBaseModes); + + // filter out modes the caller doesn't like, and multiply modes for + // every color space + + if (spaces == NULL) { + const color_space kDefaultSpaces[] = {B_RGB32_LITTLE, B_RGB16_LITTLE, + B_RGB15_LITTLE, B_CMAP8}; + modes.CreateColorSpaces(kDefaultSpaces, + sizeof(kDefaultSpaces) / sizeof(kDefaultSpaces[0])); + } else + modes.CreateColorSpaces(spaces, spacesCount); + + modes.Filter(hook); + modes.Clean(); + + // create area for output modes + + size_t size = (sizeof(display_mode) * modes.Count() + B_PAGE_SIZE - 1) + & ~(B_PAGE_SIZE - 1); + display_mode *list; + area_id area = create_area(name, (void **)&list, B_ANY_ADDRESS, + size, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); + if (area < B_OK) + return area; + + memcpy(list, modes.Modes(), sizeof(display_mode) * modes.Count()); + *_modes = list; + *_count = modes.Count(); + + return area; +} +