mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-06-19 21:24:27 +00:00
[idd] driver: cleanup magic numbers in CEdid
This commit is contained in:
@@ -15,34 +15,322 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
static const UINT EDID_BLOCK_SIZE = 128;
|
static const UINT EDID_BLOCK_SIZE = 128;
|
||||||
static const UINT EDID_DTD_SIZE = 18;
|
static const UINT EDID_DTD_SIZE = 18;
|
||||||
|
|
||||||
void CEdid::SetChecksum(BYTE* block)
|
static const UINT EDID_STANDARD_TIMING_COUNT = 8;
|
||||||
|
static const UINT EDID_BASE_DESCRIPTOR_COUNT = 4;
|
||||||
|
static const UINT EDID_BASE_DETAILED_TIMING_COUNT = 3;
|
||||||
|
static const UINT EDID_BASE_MONITOR_NAME_DESCRIPTOR_INDEX = 3;
|
||||||
|
|
||||||
|
static const UINT CTA_HEADER_SIZE = 4;
|
||||||
|
static const UINT CTA_DATA_BLOCK_MAX_PAYLOAD_SIZE = 31;
|
||||||
|
|
||||||
|
static const BYTE EDID_HEADER[8] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 };
|
||||||
|
|
||||||
|
static const WORD EDID_MANUFACTURER_ID_LGD = 0xe430;
|
||||||
|
static const WORD EDID_PRODUCT_CODE = 0x1ddd;
|
||||||
|
static const BYTE EDID_SERIAL_NUMBER[4] = { 0x01, 0x00, 0x00, 0x00 };
|
||||||
|
|
||||||
|
static const BYTE EDID_MANUFACTURE_WEEK = 1;
|
||||||
|
static const BYTE EDID_MANUFACTURE_YEAR_2026 = 36; // 1990 + 36 = 2026
|
||||||
|
static const BYTE EDID_VERSION = 1;
|
||||||
|
static const BYTE EDID_REVISION = 4;
|
||||||
|
|
||||||
|
static const BYTE EDID_VIDEO_INPUT_DIGITAL_10BPC_HDMI_A = 0xb2;
|
||||||
|
static const BYTE EDID_HORIZONTAL_SIZE_CM = 52;
|
||||||
|
static const BYTE EDID_VERTICAL_SIZE_CM = 29;
|
||||||
|
static const BYTE EDID_DISPLAY_GAMMA_2_2 = 0x78;
|
||||||
|
static const BYTE EDID_FEATURES_PREFERRED_TIMING_RGB = 0x0a;
|
||||||
|
|
||||||
|
static const BYTE EDID_STANDARD_TIMING_UNUSED_X = 0x01;
|
||||||
|
static const BYTE EDID_STANDARD_TIMING_UNUSED_AR_REFRESH = 0x01;
|
||||||
|
|
||||||
|
static const DWORD EDID_IMAGE_WIDTH_MM = 520;
|
||||||
|
static const DWORD EDID_IMAGE_HEIGHT_MM = 290;
|
||||||
|
|
||||||
|
static const BYTE EDID_DESCRIPTOR_MONITOR_NAME = 0xfc;
|
||||||
|
static const BYTE EDID_DTD_FLAGS_DIGITAL_SEPARATE_SYNC_POSITIVE = 0x1e;
|
||||||
|
|
||||||
|
static const BYTE CTA_EXTENSION_TAG = 0x02;
|
||||||
|
static const BYTE CTA_REVISION = 0x03;
|
||||||
|
|
||||||
|
static const BYTE CTA_DATA_BLOCK_TAG_VENDOR_SPECIFIC = 0x03;
|
||||||
|
static const BYTE CTA_DATA_BLOCK_TAG_EXTENDED = 0x07;
|
||||||
|
static const BYTE CTA_DATA_BLOCK_LENGTH_MASK = 0x1f;
|
||||||
|
|
||||||
|
static const BYTE CTA_EXTENDED_TAG_COLORIMETRY = 0x05;
|
||||||
|
static const BYTE CTA_EXTENDED_TAG_HDR_STATIC_METADATA = 0x06;
|
||||||
|
|
||||||
|
static const BYTE CTA_HDR_EOTF_TRADITIONAL_SDR = (BYTE)(1 << 0);
|
||||||
|
static const BYTE CTA_HDR_EOTF_SMPTE_ST_2084 = (BYTE)(1 << 2);
|
||||||
|
static const BYTE CTA_HDR_EOTF_HLG = (BYTE)(1 << 3);
|
||||||
|
static const BYTE CTA_HDR_STATIC_METADATA_TYPE_1 = (BYTE)(1 << 0);
|
||||||
|
static const BYTE CTA_HDR_DESIRED_MAX_LUMINANCE = 138;
|
||||||
|
static const BYTE CTA_HDR_DESIRED_MAX_FRAME_AVG_LUMINANCE = 115;
|
||||||
|
static const BYTE CTA_HDR_DESIRED_MIN_LUMINANCE = 14;
|
||||||
|
|
||||||
|
static const BYTE CTA_COLORIMETRY_OPYCC_601 = (BYTE)(1 << 3);
|
||||||
|
static const BYTE CTA_COLORIMETRY_OPRGB = (BYTE)(1 << 4);
|
||||||
|
static const BYTE CTA_COLORIMETRY_BT2020_YCC = (BYTE)(1 << 6);
|
||||||
|
static const BYTE CTA_COLORIMETRY_BT2020_RGB = (BYTE)(1 << 7);
|
||||||
|
static const BYTE CTA_COLORIMETRY_DCI_P3 = (BYTE)(1 << 7);
|
||||||
|
|
||||||
|
static const BYTE CTA_HDMI_FORUM_OUI [3] = { 0xd8, 0x5d, 0xc4 };
|
||||||
|
static const BYTE CTA_HDMI_LICENSING_OUI[3] = { 0x03, 0x0c, 0x00 };
|
||||||
|
|
||||||
|
static const BYTE CTA_HDMI_FORUM_VERSION_1 = 0x01;
|
||||||
|
static const BYTE CTA_HDMI_FORUM_MAX_TMDS_CHARACTER_RATE = 0x6e;
|
||||||
|
static const BYTE CTA_HDMI_FORUM_SCDC_PRESENT = 0x80;
|
||||||
|
|
||||||
|
static const BYTE CTA_HDMI_PHYSICAL_ADDRESS_A_B = 0x00;
|
||||||
|
static const BYTE CTA_HDMI_PHYSICAL_ADDRESS_C_D = 0x00;
|
||||||
|
static const BYTE CTA_HDMI_DEEP_COLOR_30_36 = 0x30;
|
||||||
|
static const BYTE CTA_HDMI_MAX_TMDS_CLOCK_UNSPECIFIED = 0x00;
|
||||||
|
static const BYTE CTA_HDMI_LATENCY_FIELDS_NONE = 0x0b;
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
struct EdidLe16
|
||||||
{
|
{
|
||||||
BYTE sum = 0;
|
BYTE lo;
|
||||||
for (UINT i = 0; i < EDID_BLOCK_SIZE - 1; ++i)
|
BYTE hi;
|
||||||
sum = (BYTE)(sum + block[i]);
|
};
|
||||||
block[EDID_BLOCK_SIZE - 1] = (BYTE)(0 - sum);
|
|
||||||
|
struct EdidStandardTiming
|
||||||
|
{
|
||||||
|
BYTE horizontalActivePixels;
|
||||||
|
BYTE aspectRatioAndRefreshRate;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EdidDetailedTimingDescriptor
|
||||||
|
{
|
||||||
|
EdidLe16 pixelClock10KHz;
|
||||||
|
|
||||||
|
BYTE hActiveLo;
|
||||||
|
BYTE hBlankLo;
|
||||||
|
BYTE hActiveBlankHi;
|
||||||
|
|
||||||
|
BYTE vActiveLo;
|
||||||
|
BYTE vBlankLo;
|
||||||
|
BYTE vActiveBlankHi;
|
||||||
|
|
||||||
|
BYTE hFrontPorchLo;
|
||||||
|
BYTE hSyncPulseWidthLo;
|
||||||
|
BYTE vFrontPorchSyncPulseWidthLo;
|
||||||
|
BYTE syncPorchPulseWidthHi;
|
||||||
|
|
||||||
|
BYTE imageWidthMmLo;
|
||||||
|
BYTE imageHeightMmLo;
|
||||||
|
BYTE imageSizeMmHi;
|
||||||
|
|
||||||
|
BYTE hBorder;
|
||||||
|
BYTE vBorder;
|
||||||
|
BYTE flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EdidMonitorNameDescriptor
|
||||||
|
{
|
||||||
|
EdidLe16 pixelClock;
|
||||||
|
BYTE reserved0;
|
||||||
|
BYTE descriptorTag;
|
||||||
|
BYTE reserved1;
|
||||||
|
char name[13];
|
||||||
|
};
|
||||||
|
|
||||||
|
union EdidDescriptor
|
||||||
|
{
|
||||||
|
EdidDetailedTimingDescriptor detailedTiming;
|
||||||
|
EdidMonitorNameDescriptor monitorName;
|
||||||
|
BYTE raw[EDID_DTD_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EdidBaseBlock
|
||||||
|
{
|
||||||
|
BYTE header[8];
|
||||||
|
|
||||||
|
EdidLe16 manufacturerId;
|
||||||
|
EdidLe16 productCode;
|
||||||
|
BYTE serialNumber[4];
|
||||||
|
|
||||||
|
BYTE manufactureWeek;
|
||||||
|
BYTE manufactureYear;
|
||||||
|
BYTE version;
|
||||||
|
BYTE revision;
|
||||||
|
|
||||||
|
BYTE videoInputDefinition;
|
||||||
|
BYTE horizontalSizeCm;
|
||||||
|
BYTE verticalSizeCm;
|
||||||
|
BYTE displayGamma;
|
||||||
|
BYTE supportedFeatures;
|
||||||
|
|
||||||
|
BYTE chromaticityCoordinates[10];
|
||||||
|
BYTE establishedTimings[3];
|
||||||
|
EdidStandardTiming standardTimings[EDID_STANDARD_TIMING_COUNT];
|
||||||
|
|
||||||
|
EdidDescriptor descriptors[EDID_BASE_DESCRIPTOR_COUNT];
|
||||||
|
|
||||||
|
BYTE extensionBlockCount;
|
||||||
|
BYTE checksum;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CtaDataBlockHeader
|
||||||
|
{
|
||||||
|
BYTE value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CtaExtensionBlock
|
||||||
|
{
|
||||||
|
BYTE tag;
|
||||||
|
BYTE revision;
|
||||||
|
BYTE dtdOffset;
|
||||||
|
BYTE flags;
|
||||||
|
BYTE payload[EDID_BLOCK_SIZE - CTA_HEADER_SIZE - 1];
|
||||||
|
BYTE checksum;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CtaHdrStaticMetadataDataBlock
|
||||||
|
{
|
||||||
|
CtaDataBlockHeader header;
|
||||||
|
BYTE extendedTag;
|
||||||
|
BYTE eotf;
|
||||||
|
BYTE staticMetadataDescriptor;
|
||||||
|
BYTE desiredContentMaxLuminance;
|
||||||
|
BYTE desiredContentMaxFrameAverageLuminance;
|
||||||
|
BYTE desiredContentMinLuminance;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CtaColorimetryDataBlock
|
||||||
|
{
|
||||||
|
CtaDataBlockHeader header;
|
||||||
|
BYTE extendedTag;
|
||||||
|
BYTE colorimetry;
|
||||||
|
BYTE metadataAndAdditionalColorimetry;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CtaHdmiForumVendorSpecificDataBlock
|
||||||
|
{
|
||||||
|
CtaDataBlockHeader header;
|
||||||
|
BYTE ieeeOui[3];
|
||||||
|
BYTE version;
|
||||||
|
BYTE maxTmdsCharacterRate;
|
||||||
|
BYTE flags;
|
||||||
|
BYTE reserved;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CtaHdmiVendorSpecificDataBlock
|
||||||
|
{
|
||||||
|
CtaDataBlockHeader header;
|
||||||
|
BYTE ieeeOui[3];
|
||||||
|
BYTE physicalAddressAB;
|
||||||
|
BYTE physicalAddressCD;
|
||||||
|
BYTE flags;
|
||||||
|
BYTE maxTmdsClock;
|
||||||
|
BYTE latencyFields;
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
static_assert(sizeof(EdidLe16) == 2, "Unexpected EDID little-endian word size");
|
||||||
|
static_assert(sizeof(EdidStandardTiming) == 2, "Unexpected EDID standard timing size");
|
||||||
|
static_assert(sizeof(EdidDetailedTimingDescriptor) == EDID_DTD_SIZE,
|
||||||
|
"Unexpected EDID detailed timing descriptor size");
|
||||||
|
static_assert(sizeof(EdidMonitorNameDescriptor) == EDID_DTD_SIZE,
|
||||||
|
"Unexpected EDID monitor name descriptor size");
|
||||||
|
static_assert(sizeof(EdidDescriptor) == EDID_DTD_SIZE,
|
||||||
|
"Unexpected EDID descriptor size");
|
||||||
|
static_assert(sizeof(EdidBaseBlock) == EDID_BLOCK_SIZE,
|
||||||
|
"Unexpected EDID base block size");
|
||||||
|
|
||||||
|
static_assert(sizeof(CtaExtensionBlock) == EDID_BLOCK_SIZE,
|
||||||
|
"Unexpected CTA extension block size");
|
||||||
|
static_assert(sizeof(CtaHdrStaticMetadataDataBlock) == 7,
|
||||||
|
"Unexpected HDR static metadata data block size");
|
||||||
|
static_assert(sizeof(CtaColorimetryDataBlock) == 4,
|
||||||
|
"Unexpected colorimetry data block size");
|
||||||
|
static_assert(sizeof(CtaHdmiForumVendorSpecificDataBlock) == 8,
|
||||||
|
"Unexpected HDMI Forum VSDB size");
|
||||||
|
static_assert(sizeof(CtaHdmiVendorSpecificDataBlock) == 9,
|
||||||
|
"Unexpected HDMI VSDB size");
|
||||||
|
static_assert(CTA_HEADER_SIZE +
|
||||||
|
sizeof(CtaHdrStaticMetadataDataBlock) +
|
||||||
|
sizeof(CtaColorimetryDataBlock) +
|
||||||
|
sizeof(CtaHdmiForumVendorSpecificDataBlock) +
|
||||||
|
sizeof(CtaHdmiVendorSpecificDataBlock) <= EDID_BLOCK_SIZE - 1,
|
||||||
|
"CTA data blocks exceed extension block space");
|
||||||
|
|
||||||
|
static void SetLe16(EdidLe16& dst, DWORD value)
|
||||||
|
{
|
||||||
|
dst.lo = (BYTE)(value & 0xff);
|
||||||
|
dst.hi = (BYTE)((value >> 8) & 0xff);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CEdid::WriteMonitorName(BYTE* desc, const char* name)
|
static BYTE Lo8(DWORD value)
|
||||||
{
|
{
|
||||||
memset(desc, 0, EDID_DTD_SIZE);
|
return (BYTE)(value & 0xff);
|
||||||
desc[3] = 0xfc;
|
|
||||||
desc[4] = 0x00;
|
|
||||||
|
|
||||||
UINT i = 0;
|
|
||||||
for (; i < 13 && name[i]; ++i)
|
|
||||||
desc[5 + i] = (BYTE)name[i];
|
|
||||||
if (i < 13)
|
|
||||||
desc[5 + i++] = 0x0a;
|
|
||||||
for (; i < 13; ++i)
|
|
||||||
desc[5 + i] = 0x20;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CEdid::WriteDetailedTiming(BYTE* dtd, const CSettings::DisplayMode& mode)
|
static BYTE PackMsbNibbles(DWORD upperValue, DWORD lowerValue)
|
||||||
{
|
{
|
||||||
memset(dtd, 0, EDID_DTD_SIZE);
|
return (BYTE)((((upperValue >> 8) & 0x0f) << 4) |
|
||||||
|
((lowerValue >> 8) & 0x0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
static BYTE PackLowNibbles(DWORD upperValue, DWORD lowerValue)
|
||||||
|
{
|
||||||
|
return (BYTE)(((upperValue & 0x0f) << 4) |
|
||||||
|
(lowerValue & 0x0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
static BYTE PackSyncPorchPulseWidthHi(
|
||||||
|
DWORD hFrontPorch,
|
||||||
|
DWORD hSyncPulseWidth,
|
||||||
|
DWORD vFrontPorch,
|
||||||
|
DWORD vSyncPulseWidth)
|
||||||
|
{
|
||||||
|
return (BYTE)(
|
||||||
|
(((hFrontPorch >> 8) & 0x03) << 6) |
|
||||||
|
(((hSyncPulseWidth >> 8) & 0x03) << 4) |
|
||||||
|
(((vFrontPorch >> 4) & 0x03) << 2) |
|
||||||
|
((vSyncPulseWidth >> 4) & 0x03));
|
||||||
|
}
|
||||||
|
|
||||||
|
static EdidStandardTiming MakeUnusedStandardTiming()
|
||||||
|
{
|
||||||
|
EdidStandardTiming timing = {};
|
||||||
|
timing.horizontalActivePixels = EDID_STANDARD_TIMING_UNUSED_X;
|
||||||
|
timing.aspectRatioAndRefreshRate = EDID_STANDARD_TIMING_UNUSED_AR_REFRESH;
|
||||||
|
return timing;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void InitEdidBaseBlock(EdidBaseBlock& base)
|
||||||
|
{
|
||||||
|
memcpy(base.header, EDID_HEADER, sizeof(base.header));
|
||||||
|
|
||||||
|
// Manufacturer ID: LGD, product/serial values are arbitrary.
|
||||||
|
SetLe16(base.manufacturerId, EDID_MANUFACTURER_ID_LGD);
|
||||||
|
SetLe16(base.productCode, EDID_PRODUCT_CODE);
|
||||||
|
memcpy(base.serialNumber, EDID_SERIAL_NUMBER, sizeof(base.serialNumber));
|
||||||
|
|
||||||
|
base.manufactureWeek = EDID_MANUFACTURE_WEEK;
|
||||||
|
base.manufactureYear = EDID_MANUFACTURE_YEAR_2026;
|
||||||
|
base.version = EDID_VERSION;
|
||||||
|
base.revision = EDID_REVISION;
|
||||||
|
|
||||||
|
base.videoInputDefinition = EDID_VIDEO_INPUT_DIGITAL_10BPC_HDMI_A;
|
||||||
|
base.horizontalSizeCm = EDID_HORIZONTAL_SIZE_CM;
|
||||||
|
base.verticalSizeCm = EDID_VERTICAL_SIZE_CM;
|
||||||
|
base.displayGamma = EDID_DISPLAY_GAMMA_2_2;
|
||||||
|
base.supportedFeatures = EDID_FEATURES_PREFERRED_TIMING_RGB;
|
||||||
|
|
||||||
|
for (UINT i = 0; i < EDID_STANDARD_TIMING_COUNT; ++i)
|
||||||
|
base.standardTimings[i] = MakeUnusedStandardTiming();
|
||||||
|
|
||||||
|
base.extensionBlockCount = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool MakeDetailedTiming(
|
||||||
|
EdidDetailedTimingDescriptor& timing,
|
||||||
|
const CSettings::DisplayMode& mode)
|
||||||
|
{
|
||||||
|
memset(&timing, 0, sizeof(timing));
|
||||||
|
|
||||||
const DWORD hActive = mode.width;
|
const DWORD hActive = mode.width;
|
||||||
const DWORD vActive = mode.height;
|
const DWORD vActive = mode.height;
|
||||||
@@ -59,8 +347,10 @@ bool CEdid::WriteDetailedTiming(BYTE* dtd, const CSettings::DisplayMode& mode)
|
|||||||
|
|
||||||
DWORD hSync = std::max<DWORD>(32, hActive / 100);
|
DWORD hSync = std::max<DWORD>(32, hActive / 100);
|
||||||
hSync = (hSync + 7) & ~7UL;
|
hSync = (hSync + 7) & ~7UL;
|
||||||
|
|
||||||
DWORD hFront = std::max<DWORD>(48, hBlank / 3);
|
DWORD hFront = std::max<DWORD>(48, hBlank / 3);
|
||||||
hFront = (hFront + 7) & ~7UL;
|
hFront = (hFront + 7) & ~7UL;
|
||||||
|
|
||||||
if (hFront + hSync >= hBlank)
|
if (hFront + hSync >= hBlank)
|
||||||
{
|
{
|
||||||
hFront = 48;
|
hFront = 48;
|
||||||
@@ -78,65 +368,175 @@ bool CEdid::WriteDetailedTiming(BYTE* dtd, const CSettings::DisplayMode& mode)
|
|||||||
if (pixelClock10KHz == 0 || pixelClock10KHz > 0xffff)
|
if (pixelClock10KHz == 0 || pixelClock10KHz > 0xffff)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
dtd[0] = (BYTE)(pixelClock10KHz & 0xff);
|
SetLe16(timing.pixelClock10KHz, (DWORD)pixelClock10KHz);
|
||||||
dtd[1] = (BYTE)((pixelClock10KHz >> 8) & 0xff);
|
|
||||||
dtd[2] = (BYTE)(hActive & 0xff);
|
|
||||||
dtd[3] = (BYTE)(hBlank & 0xff);
|
|
||||||
dtd[4] = (BYTE)(((hActive >> 8) & 0x0f) << 4 | ((hBlank >> 8) & 0x0f));
|
|
||||||
dtd[5] = (BYTE)(vActive & 0xff);
|
|
||||||
dtd[6] = (BYTE)(vBlank & 0xff);
|
|
||||||
dtd[7] = (BYTE)(((vActive >> 8) & 0x0f) << 4 | ((vBlank >> 8) & 0x0f));
|
|
||||||
dtd[8] = (BYTE)(hFront & 0xff);
|
|
||||||
dtd[9] = (BYTE)(hSync & 0xff);
|
|
||||||
dtd[10] = (BYTE)((vFront & 0x0f) << 4 | (vSync & 0x0f));
|
|
||||||
dtd[11] = (BYTE)(((hFront >> 8) & 0x03) << 6 |
|
|
||||||
(((hSync >> 8) & 0x03) << 4) |
|
|
||||||
(((vFront >> 4) & 0x03) << 2) |
|
|
||||||
((vSync >> 4) & 0x03));
|
|
||||||
|
|
||||||
// 52cm x 29cm is a conventional 16:9 desktop monitor size.
|
timing.hActiveLo = Lo8(hActive);
|
||||||
const DWORD imageWidth = 520;
|
timing.hBlankLo = Lo8(hBlank);
|
||||||
const DWORD imageHeight = 290;
|
timing.hActiveBlankHi = PackMsbNibbles(hActive, hBlank);
|
||||||
dtd[12] = (BYTE)(imageWidth & 0xff);
|
|
||||||
dtd[13] = (BYTE)(imageHeight & 0xff);
|
timing.vActiveLo = Lo8(vActive);
|
||||||
dtd[14] = (BYTE)(((imageWidth >> 8) & 0x0f) << 4 | ((imageHeight >> 8) & 0x0f));
|
timing.vBlankLo = Lo8(vBlank);
|
||||||
dtd[17] = 0x1e; // digital separate sync, positive hsync/vsync
|
timing.vActiveBlankHi = PackMsbNibbles(vActive, vBlank);
|
||||||
|
|
||||||
|
timing.hFrontPorchLo = Lo8(hFront);
|
||||||
|
timing.hSyncPulseWidthLo = Lo8(hSync);
|
||||||
|
timing.vFrontPorchSyncPulseWidthLo = PackLowNibbles(vFront, vSync);
|
||||||
|
timing.syncPorchPulseWidthHi = PackSyncPorchPulseWidthHi(hFront, hSync, vFront, vSync);
|
||||||
|
|
||||||
|
timing.imageWidthMmLo = Lo8(EDID_IMAGE_WIDTH_MM);
|
||||||
|
timing.imageHeightMmLo = Lo8(EDID_IMAGE_HEIGHT_MM);
|
||||||
|
timing.imageSizeMmHi = PackMsbNibbles(EDID_IMAGE_WIDTH_MM, EDID_IMAGE_HEIGHT_MM);
|
||||||
|
|
||||||
|
timing.flags = EDID_DTD_FLAGS_DIGITAL_SEPARATE_SYNC_POSITIVE;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void MakeMonitorName(
|
||||||
|
EdidMonitorNameDescriptor& monitorName,
|
||||||
|
const char* name)
|
||||||
|
{
|
||||||
|
memset(&monitorName, 0, sizeof(monitorName));
|
||||||
|
|
||||||
|
monitorName.descriptorTag = EDID_DESCRIPTOR_MONITOR_NAME;
|
||||||
|
|
||||||
|
UINT len = 0;
|
||||||
|
for (; len < sizeof(monitorName.name) && name[len]; ++len)
|
||||||
|
monitorName.name[len] = name[len];
|
||||||
|
|
||||||
|
if (len < sizeof(monitorName.name))
|
||||||
|
monitorName.name[len++] = '\n';
|
||||||
|
|
||||||
|
for (; len < sizeof(monitorName.name); ++len)
|
||||||
|
monitorName.name[len] = ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
static CtaDataBlockHeader MakeCtaDataBlockHeader(BYTE tag, UINT payloadSize)
|
||||||
|
{
|
||||||
|
CtaDataBlockHeader header = {};
|
||||||
|
|
||||||
|
if (payloadSize > CTA_DATA_BLOCK_MAX_PAYLOAD_SIZE)
|
||||||
|
payloadSize = CTA_DATA_BLOCK_MAX_PAYLOAD_SIZE;
|
||||||
|
|
||||||
|
header.value = (BYTE)((tag << 5) |
|
||||||
|
(payloadSize & CTA_DATA_BLOCK_LENGTH_MASK));
|
||||||
|
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static void AppendCtaDataBlock(BYTE* cta, UINT& offset, const T& block)
|
||||||
|
{
|
||||||
|
static_assert(sizeof(T) > sizeof(CtaDataBlockHeader),
|
||||||
|
"CTA data block has no payload");
|
||||||
|
static_assert(sizeof(T) - sizeof(CtaDataBlockHeader) <=
|
||||||
|
CTA_DATA_BLOCK_MAX_PAYLOAD_SIZE,
|
||||||
|
"CTA data block payload exceeds header length field");
|
||||||
|
|
||||||
|
memcpy(cta + offset, &block, sizeof(block));
|
||||||
|
offset += (UINT)sizeof(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
static CtaHdrStaticMetadataDataBlock MakeCtaHdrStaticMetadataDataBlock()
|
||||||
|
{
|
||||||
|
CtaHdrStaticMetadataDataBlock block = {};
|
||||||
|
|
||||||
|
block.header = MakeCtaDataBlockHeader(CTA_DATA_BLOCK_TAG_EXTENDED,
|
||||||
|
(UINT)(sizeof(block) - sizeof(block.header)));
|
||||||
|
|
||||||
|
block.extendedTag = CTA_EXTENDED_TAG_HDR_STATIC_METADATA;
|
||||||
|
block.eotf = (BYTE)(
|
||||||
|
CTA_HDR_EOTF_TRADITIONAL_SDR |
|
||||||
|
CTA_HDR_EOTF_SMPTE_ST_2084 |
|
||||||
|
CTA_HDR_EOTF_HLG);
|
||||||
|
|
||||||
|
block.staticMetadataDescriptor = CTA_HDR_STATIC_METADATA_TYPE_1;
|
||||||
|
block.desiredContentMaxLuminance = CTA_HDR_DESIRED_MAX_LUMINANCE;
|
||||||
|
block.desiredContentMaxFrameAverageLuminance = CTA_HDR_DESIRED_MAX_FRAME_AVG_LUMINANCE;
|
||||||
|
block.desiredContentMinLuminance = CTA_HDR_DESIRED_MIN_LUMINANCE;
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
static CtaColorimetryDataBlock MakeCtaColorimetryDataBlock()
|
||||||
|
{
|
||||||
|
CtaColorimetryDataBlock block = {};
|
||||||
|
|
||||||
|
block.header = MakeCtaDataBlockHeader(CTA_DATA_BLOCK_TAG_EXTENDED,
|
||||||
|
(UINT)(sizeof(block) - sizeof(block.header)));
|
||||||
|
block.extendedTag = CTA_EXTENDED_TAG_COLORIMETRY;
|
||||||
|
block.colorimetry = (BYTE)(CTA_COLORIMETRY_OPYCC_601 |
|
||||||
|
CTA_COLORIMETRY_OPRGB |
|
||||||
|
CTA_COLORIMETRY_BT2020_YCC |
|
||||||
|
CTA_COLORIMETRY_BT2020_RGB);
|
||||||
|
block.metadataAndAdditionalColorimetry = CTA_COLORIMETRY_DCI_P3;
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
static CtaHdmiForumVendorSpecificDataBlock MakeCtaHdmiForumVendorSpecificDataBlock()
|
||||||
|
{
|
||||||
|
CtaHdmiForumVendorSpecificDataBlock block = {};
|
||||||
|
|
||||||
|
block.header = MakeCtaDataBlockHeader(CTA_DATA_BLOCK_TAG_VENDOR_SPECIFIC,
|
||||||
|
(UINT)(sizeof(block) - sizeof(block.header)));
|
||||||
|
memcpy(block.ieeeOui, CTA_HDMI_FORUM_OUI, sizeof(block.ieeeOui));
|
||||||
|
block.version = CTA_HDMI_FORUM_VERSION_1;
|
||||||
|
block.maxTmdsCharacterRate = CTA_HDMI_FORUM_MAX_TMDS_CHARACTER_RATE;
|
||||||
|
block.flags = CTA_HDMI_FORUM_SCDC_PRESENT;
|
||||||
|
block.reserved = 0x00;
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
static CtaHdmiVendorSpecificDataBlock MakeCtaHdmiVendorSpecificDataBlock()
|
||||||
|
{
|
||||||
|
CtaHdmiVendorSpecificDataBlock block = {};
|
||||||
|
|
||||||
|
block.header = MakeCtaDataBlockHeader(CTA_DATA_BLOCK_TAG_VENDOR_SPECIFIC,
|
||||||
|
(UINT)(sizeof(block) - sizeof(block.header)));
|
||||||
|
memcpy(block.ieeeOui, CTA_HDMI_LICENSING_OUI, sizeof(block.ieeeOui));
|
||||||
|
block.physicalAddressAB = CTA_HDMI_PHYSICAL_ADDRESS_A_B;
|
||||||
|
block.physicalAddressCD = CTA_HDMI_PHYSICAL_ADDRESS_C_D;
|
||||||
|
block.flags = CTA_HDMI_DEEP_COLOR_30_36;
|
||||||
|
block.maxTmdsClock = CTA_HDMI_MAX_TMDS_CLOCK_UNSPECIFIED;
|
||||||
|
block.latencyFields = CTA_HDMI_LATENCY_FIELDS_NONE;
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEdid::SetChecksum(BYTE* block)
|
||||||
|
{
|
||||||
|
BYTE sum = 0;
|
||||||
|
for (UINT i = 0; i < EDID_BLOCK_SIZE - 1; ++i)
|
||||||
|
sum = (BYTE)(sum + block[i]);
|
||||||
|
|
||||||
|
block[EDID_BLOCK_SIZE - 1] = (BYTE)(0 - sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEdid::WriteMonitorName(BYTE* desc, const char* name)
|
||||||
|
{
|
||||||
|
EdidMonitorNameDescriptor monitorName = {};
|
||||||
|
MakeMonitorName(monitorName, name);
|
||||||
|
memcpy(desc, &monitorName, sizeof(monitorName));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CEdid::WriteDetailedTiming(BYTE* dtd, const CSettings::DisplayMode& mode)
|
||||||
|
{
|
||||||
|
EdidDetailedTimingDescriptor timing = {};
|
||||||
|
|
||||||
|
if (!MakeDetailedTiming(timing, mode))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
memcpy(dtd, &timing, sizeof(timing));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CEdid::Build(const CSettings::DisplayModes& modes)
|
void CEdid::Build(const CSettings::DisplayModes& modes)
|
||||||
{
|
{
|
||||||
m_data.assign(EDID_BLOCK_SIZE * 2, 0);
|
m_data.assign(EDID_BLOCK_SIZE * 2, 0);
|
||||||
BYTE* base = m_data.data();
|
|
||||||
BYTE* cta = base + EDID_BLOCK_SIZE;
|
|
||||||
|
|
||||||
static const BYTE header[8] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 };
|
EdidBaseBlock baseBlock = {};
|
||||||
memcpy(base, header, sizeof(header));
|
InitEdidBaseBlock(baseBlock);
|
||||||
|
|
||||||
// Manufacturer ID: LGD (5-bit EISA ID), product/serial values are arbitrary.
|
|
||||||
base[8] = 0x30;
|
|
||||||
base[9] = 0xe4;
|
|
||||||
base[10] = 0xdd;
|
|
||||||
base[11] = 0x1d;
|
|
||||||
base[12] = 0x01;
|
|
||||||
base[16] = 1;
|
|
||||||
base[17] = 36; // 1990 + 36 = 2026
|
|
||||||
base[18] = 1;
|
|
||||||
base[19] = 4;
|
|
||||||
base[20] = 0xb2; // digital input, 10-bit HDMI-A
|
|
||||||
base[21] = 52;
|
|
||||||
base[22] = 29;
|
|
||||||
base[23] = 0x78; // gamma 2.2
|
|
||||||
base[24] = 0x0a; // preferred timing, RGB color
|
|
||||||
|
|
||||||
for (UINT i = 38; i < 54; i += 2)
|
|
||||||
{
|
|
||||||
base[i] = 0x01;
|
|
||||||
base[i + 1] = 0x01;
|
|
||||||
}
|
|
||||||
|
|
||||||
CSettings::DisplayModes sorted = modes;
|
CSettings::DisplayModes sorted = modes;
|
||||||
std::stable_sort(sorted.begin(), sorted.end(), [](const CSettings::DisplayMode& a, const CSettings::DisplayMode& b)
|
std::stable_sort(sorted.begin(), sorted.end(),
|
||||||
|
[](const CSettings::DisplayMode& a, const CSettings::DisplayMode& b)
|
||||||
{
|
{
|
||||||
if (a.preferred != b.preferred)
|
if (a.preferred != b.preferred)
|
||||||
return a.preferred && !b.preferred;
|
return a.preferred && !b.preferred;
|
||||||
@@ -147,70 +547,52 @@ void CEdid::Build(const CSettings::DisplayModes& modes)
|
|||||||
return a.refresh > b.refresh;
|
return a.refresh > b.refresh;
|
||||||
});
|
});
|
||||||
|
|
||||||
UINT modeIndex = 0;
|
UINT modeIndex = 0;
|
||||||
UINT dtdOffset = 54;
|
UINT baseDtdIndex = 0;
|
||||||
for (; modeIndex < sorted.size() && dtdOffset < 54 + EDID_DTD_SIZE * 3; ++modeIndex)
|
|
||||||
|
for (; modeIndex < sorted.size() &&
|
||||||
|
baseDtdIndex < EDID_BASE_DETAILED_TIMING_COUNT;
|
||||||
|
++modeIndex)
|
||||||
{
|
{
|
||||||
if (WriteDetailedTiming(base + dtdOffset, sorted[modeIndex]))
|
if (MakeDetailedTiming(
|
||||||
dtdOffset += EDID_DTD_SIZE;
|
baseBlock.descriptors[baseDtdIndex].detailedTiming,
|
||||||
|
sorted[modeIndex]))
|
||||||
|
{
|
||||||
|
++baseDtdIndex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
WriteMonitorName(base + 54 + EDID_DTD_SIZE * 3, "Looking Glass");
|
|
||||||
|
|
||||||
base[126] = 1;
|
MakeMonitorName(
|
||||||
SetChecksum(base);
|
baseBlock.descriptors[EDID_BASE_MONITOR_NAME_DESCRIPTOR_INDEX].monitorName,
|
||||||
|
"Looking Glass");
|
||||||
|
|
||||||
cta[0] = 0x02; // CTA-861 extension
|
SetChecksum(reinterpret_cast<BYTE*>(&baseBlock));
|
||||||
cta[1] = 0x03;
|
memcpy(m_data.data(), &baseBlock, sizeof(baseBlock));
|
||||||
|
|
||||||
UINT dataOffset = 4;
|
CtaExtensionBlock ctaBlock = {};
|
||||||
// CTA HDR Static Metadata data block: HDR, PQ and HLG with type 1 metadata and luminance data.
|
BYTE* cta = reinterpret_cast<BYTE*>(&ctaBlock);
|
||||||
cta[dataOffset++] = (7 << 5) | 6;
|
|
||||||
cta[dataOffset++] = 0x06;
|
|
||||||
cta[dataOffset++] = 0x0d;
|
|
||||||
cta[dataOffset++] = 0x01;
|
|
||||||
cta[dataOffset++] = 0xa2;
|
|
||||||
cta[dataOffset++] = 0xa2;
|
|
||||||
cta[dataOffset++] = 0x10;
|
|
||||||
|
|
||||||
// CTA extended colorimetry data block: advertise BT.2020 and DCI-P3 colorimetry.
|
ctaBlock.tag = CTA_EXTENSION_TAG;
|
||||||
cta[dataOffset++] = (7 << 5) | 3;
|
ctaBlock.revision = CTA_REVISION;
|
||||||
cta[dataOffset++] = 0x05;
|
|
||||||
cta[dataOffset++] = 0xd8;
|
|
||||||
cta[dataOffset++] = 0x00;
|
|
||||||
|
|
||||||
// HDMI Forum vendor-specific data block.
|
UINT dataOffset = CTA_HEADER_SIZE;
|
||||||
cta[dataOffset++] = (3 << 5) | 7;
|
AppendCtaDataBlock(cta, dataOffset, MakeCtaHdrStaticMetadataDataBlock ());
|
||||||
cta[dataOffset++] = 0xd8;
|
AppendCtaDataBlock(cta, dataOffset, MakeCtaColorimetryDataBlock ());
|
||||||
cta[dataOffset++] = 0x5d;
|
AppendCtaDataBlock(cta, dataOffset, MakeCtaHdmiForumVendorSpecificDataBlock());
|
||||||
cta[dataOffset++] = 0xc4;
|
AppendCtaDataBlock(cta, dataOffset, MakeCtaHdmiVendorSpecificDataBlock ());
|
||||||
cta[dataOffset++] = 0x01;
|
|
||||||
cta[dataOffset++] = 0x6e;
|
|
||||||
cta[dataOffset++] = 0x80;
|
|
||||||
cta[dataOffset++] = 0x00;
|
|
||||||
|
|
||||||
// HDMI vendor-specific data block.
|
ctaBlock.dtdOffset = (BYTE)dataOffset;
|
||||||
cta[dataOffset++] = (3 << 5) | 8;
|
ctaBlock.flags = 0x00;
|
||||||
cta[dataOffset++] = 0x03;
|
|
||||||
cta[dataOffset++] = 0x0c;
|
|
||||||
cta[dataOffset++] = 0x00;
|
|
||||||
cta[dataOffset++] = 0x00;
|
|
||||||
cta[dataOffset++] = 0x00;
|
|
||||||
cta[dataOffset++] = 0x30;
|
|
||||||
cta[dataOffset++] = 0x00;
|
|
||||||
cta[dataOffset++] = 0x0b;
|
|
||||||
|
|
||||||
UINT ctaDtdOffset = dataOffset;
|
UINT ctaDtdWrite = dataOffset;
|
||||||
if (ctaDtdOffset < 4)
|
for (; modeIndex < sorted.size() &&
|
||||||
ctaDtdOffset = 4;
|
ctaDtdWrite + EDID_DTD_SIZE <= EDID_BLOCK_SIZE - 1;
|
||||||
|
++modeIndex)
|
||||||
UINT ctaDtdWrite = ctaDtdOffset;
|
|
||||||
for (; modeIndex < sorted.size() && ctaDtdWrite + EDID_DTD_SIZE <= EDID_BLOCK_SIZE - 1; ++modeIndex)
|
|
||||||
{
|
{
|
||||||
if (WriteDetailedTiming(cta + ctaDtdWrite, sorted[modeIndex]))
|
if (WriteDetailedTiming(cta + ctaDtdWrite, sorted[modeIndex]))
|
||||||
ctaDtdWrite += EDID_DTD_SIZE;
|
ctaDtdWrite += EDID_DTD_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
cta[2] = (BYTE)ctaDtdOffset;
|
|
||||||
cta[3] = 0x00;
|
|
||||||
SetChecksum(cta);
|
SetChecksum(cta);
|
||||||
|
memcpy(m_data.data() + sizeof(baseBlock), &ctaBlock, sizeof(ctaBlock));
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user