summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp91
-rw-r--r--services/surfaceflinger/DisplayHardware/DisplayIdentification.h21
-rw-r--r--services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp30
3 files changed, 136 insertions, 6 deletions
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
index f24f3142f1..b6d904f9f9 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
@@ -35,6 +35,7 @@ namespace {
using byte_view = std::basic_string_view<uint8_t>;
+constexpr size_t kEdidBlockSize = 128;
constexpr size_t kEdidHeaderLength = 5;
constexpr uint16_t kFallbackEdidManufacturerId = 0;
@@ -98,6 +99,57 @@ DeviceProductInfo buildDeviceProductInfo(const Edid& edid) {
return info;
}
+Cea861ExtensionBlock parseCea861Block(const byte_view& block) {
+ Cea861ExtensionBlock cea861Block;
+
+ constexpr size_t kRevisionNumberOffset = 1;
+ cea861Block.revisionNumber = block[kRevisionNumberOffset];
+
+ constexpr size_t kDetailedTimingDescriptorsOffset = 2;
+ const size_t dtdStart =
+ std::min(kEdidBlockSize, static_cast<size_t>(block[kDetailedTimingDescriptorsOffset]));
+
+ // Parse data blocks.
+ for (size_t dataBlockOffset = 4; dataBlockOffset < dtdStart;) {
+ const uint8_t header = block[dataBlockOffset];
+ const uint8_t tag = header >> 5;
+ const size_t bodyLength = header & 0b11111;
+ constexpr size_t kDataBlockHeaderSize = 1;
+ const size_t dataBlockSize = bodyLength + kDataBlockHeaderSize;
+
+ if (block.size() < dataBlockOffset + dataBlockSize) {
+ ALOGW("Invalid EDID: CEA 861 data block is truncated.");
+ break;
+ }
+
+ const byte_view dataBlock(block.data() + dataBlockOffset, dataBlockSize);
+ constexpr uint8_t kVendorSpecificDataBlockTag = 0x3;
+
+ if (tag == kVendorSpecificDataBlockTag) {
+ const uint32_t ieeeRegistrationId =
+ dataBlock[1] | (dataBlock[2] << 8) | (dataBlock[3] << 16);
+ constexpr uint32_t kHdmiIeeeRegistrationId = 0xc03;
+
+ if (ieeeRegistrationId == kHdmiIeeeRegistrationId) {
+ const uint8_t a = dataBlock[4] >> 4;
+ const uint8_t b = dataBlock[4] & 0b1111;
+ const uint8_t c = dataBlock[5] >> 4;
+ const uint8_t d = dataBlock[5] & 0b1111;
+ cea861Block.hdmiVendorDataBlock =
+ HdmiVendorDataBlock{.physicalAddress = HdmiPhysicalAddress{a, b, c, d}};
+ } else {
+ ALOGV("Ignoring vendor specific data block for vendor with IEEE OUI %x",
+ ieeeRegistrationId);
+ }
+ } else {
+ ALOGV("Ignoring CEA-861 data block with tag %x", tag);
+ }
+ dataBlockOffset += bodyLength + kDataBlockHeaderSize;
+ }
+
+ return cea861Block;
+}
+
} // namespace
uint16_t DisplayId::manufacturerId() const {
@@ -115,13 +167,12 @@ bool isEdid(const DisplayIdentificationData& data) {
}
std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) {
- constexpr size_t kMinLength = 128;
- if (edid.size() < kMinLength) {
+ if (edid.size() < kEdidBlockSize) {
ALOGW("Invalid EDID: structure is truncated.");
// Attempt parsing even if EDID is malformed.
} else {
- ALOGW_IF(edid[126] != 0, "EDID extensions are currently unsupported.");
- ALOGW_IF(std::accumulate(edid.begin(), edid.begin() + kMinLength, static_cast<uint8_t>(0)),
+ ALOGW_IF(std::accumulate(edid.begin(), edid.begin() + kEdidBlockSize,
+ static_cast<uint8_t>(0)),
"Invalid EDID: structure does not checksum.");
}
@@ -227,13 +278,43 @@ std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) {
// have been observed to change on some displays with multiple inputs.
const auto modelHash = static_cast<uint32_t>(std::hash<std::string_view>()(modelString));
+ // Parse extension blocks.
+ std::optional<Cea861ExtensionBlock> cea861Block;
+ if (edid.size() < kEdidBlockSize) {
+ ALOGW("Invalid EDID: block 0 is truncated.");
+ } else {
+ constexpr size_t kNumExtensionsOffset = 126;
+ const size_t numExtensions = edid[kNumExtensionsOffset];
+ view = byte_view(edid.data(), edid.size());
+ for (size_t blockNumber = 1; blockNumber <= numExtensions; blockNumber++) {
+ view.remove_prefix(kEdidBlockSize);
+ if (view.size() < kEdidBlockSize) {
+ ALOGW("Invalid EDID: block %zu is truncated.", blockNumber);
+ break;
+ }
+
+ const byte_view block(view.data(), kEdidBlockSize);
+ ALOGW_IF(std::accumulate(block.begin(), block.end(), static_cast<uint8_t>(0)),
+ "Invalid EDID: block %zu does not checksum.", blockNumber);
+ const uint8_t tag = block[0];
+
+ constexpr uint8_t kCea861BlockTag = 0x2;
+ if (tag == kCea861BlockTag) {
+ cea861Block = parseCea861Block(block);
+ } else {
+ ALOGV("Ignoring block number %zu with tag %x.", blockNumber, tag);
+ }
+ }
+ }
+
return Edid{.manufacturerId = manufacturerId,
.productId = productId,
.pnpId = *pnpId,
.modelHash = modelHash,
.displayName = displayName,
+ .manufactureOrModelYear = manufactureOrModelYear,
.manufactureWeek = manufactureWeek,
- .manufactureOrModelYear = manufactureOrModelYear};
+ .cea861Block = cea861Block};
}
std::optional<PnpId> getPnpId(uint16_t manufacturerId) {
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
index d91b95773b..fc2f72e80c 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
@@ -57,6 +57,26 @@ struct DisplayIdentificationInfo {
std::optional<DeviceProductInfo> deviceProductInfo;
};
+struct ExtensionBlock {
+ uint8_t tag;
+ uint8_t revisionNumber;
+};
+
+struct HdmiPhysicalAddress {
+ // The address describes the path from the display sink in the network of connected HDMI
+ // devices. The format of the address is "a.b.c.d". For example, address 2.1.0.0 means we are
+ // connected to port 1 of a device which is connected to port 2 of the sink.
+ uint8_t a, b, c, d;
+};
+
+struct HdmiVendorDataBlock {
+ std::optional<HdmiPhysicalAddress> physicalAddress;
+};
+
+struct Cea861ExtensionBlock : ExtensionBlock {
+ std::optional<HdmiVendorDataBlock> hdmiVendorDataBlock;
+};
+
struct Edid {
uint16_t manufacturerId;
uint16_t productId;
@@ -65,6 +85,7 @@ struct Edid {
std::string_view displayName;
uint8_t manufactureOrModelYear;
uint8_t manufactureWeek;
+ std::optional<Cea861ExtensionBlock> cea861Block;
};
bool isEdid(const DisplayIdentificationData&);
diff --git a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
index c2ddfcee45..cc11aa9594 100644
--- a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
@@ -96,7 +96,7 @@ const unsigned char kHisenseTvEdid[] =
"\x0a\x20\x20\x20\x20\x20\x01\x47\x02\x03\x2d\x71\x50\x90\x05"
"\x04\x03\x07\x02\x06\x01\x1f\x14\x13\x12\x16\x11\x15\x20\x2c"
"\x09\x07\x03\x15\x07\x50\x57\x07\x00\x39\x07\xbb\x66\x03\x0c"
- "\x00\x20\x00\x00\x83\x01\x00\x00\x01\x1d\x00\x72\x51\xd0\x1e"
+ "\x00\x12\x34\x00\x83\x01\x00\x00\x01\x1d\x00\x72\x51\xd0\x1e"
"\x20\x6e\x28\x55\x00\xc4\x8e\x21\x00\x00\x1e\x01\x1d\x80\x18"
"\x71\x1c\x16\x20\x58\x2c\x25\x00\xc4\x8e\x21\x00\x00\x9e\x8c"
"\x0a\xd0\x8a\x20\xe0\x2d\x10\x10\x3e\x96\x00\x13\x8e\x21\x00"
@@ -185,6 +185,7 @@ TEST(DisplayIdentificationTest, parseEdid) {
EXPECT_EQ(12610, edid->productId);
EXPECT_EQ(21, edid->manufactureOrModelYear);
EXPECT_EQ(0, edid->manufactureWeek);
+ EXPECT_FALSE(edid->cea861Block);
edid = parseEdid(getExternalEdid());
ASSERT_TRUE(edid);
@@ -195,6 +196,7 @@ TEST(DisplayIdentificationTest, parseEdid) {
EXPECT_EQ(10348, edid->productId);
EXPECT_EQ(22, edid->manufactureOrModelYear);
EXPECT_EQ(2, edid->manufactureWeek);
+ EXPECT_FALSE(edid->cea861Block);
edid = parseEdid(getExternalEedid());
ASSERT_TRUE(edid);
@@ -205,6 +207,14 @@ TEST(DisplayIdentificationTest, parseEdid) {
EXPECT_EQ(2302, edid->productId);
EXPECT_EQ(21, edid->manufactureOrModelYear);
EXPECT_EQ(41, edid->manufactureWeek);
+ ASSERT_TRUE(edid->cea861Block);
+ ASSERT_TRUE(edid->cea861Block->hdmiVendorDataBlock);
+ ASSERT_TRUE(edid->cea861Block->hdmiVendorDataBlock->physicalAddress);
+ auto physicalAddress = edid->cea861Block->hdmiVendorDataBlock->physicalAddress;
+ EXPECT_EQ(2, physicalAddress->a);
+ EXPECT_EQ(0, physicalAddress->b);
+ EXPECT_EQ(0, physicalAddress->c);
+ EXPECT_EQ(0, physicalAddress->d);
edid = parseEdid(getPanasonicTvEdid());
ASSERT_TRUE(edid);
@@ -215,6 +225,14 @@ TEST(DisplayIdentificationTest, parseEdid) {
EXPECT_EQ(41622, edid->productId);
EXPECT_EQ(29, edid->manufactureOrModelYear);
EXPECT_EQ(0, edid->manufactureWeek);
+ ASSERT_TRUE(edid->cea861Block);
+ ASSERT_TRUE(edid->cea861Block->hdmiVendorDataBlock);
+ ASSERT_TRUE(edid->cea861Block->hdmiVendorDataBlock->physicalAddress);
+ physicalAddress = edid->cea861Block->hdmiVendorDataBlock->physicalAddress;
+ EXPECT_EQ(2, physicalAddress->a);
+ EXPECT_EQ(0, physicalAddress->b);
+ EXPECT_EQ(0, physicalAddress->c);
+ EXPECT_EQ(0, physicalAddress->d);
edid = parseEdid(getHisenseTvEdid());
ASSERT_TRUE(edid);
@@ -225,6 +243,14 @@ TEST(DisplayIdentificationTest, parseEdid) {
EXPECT_EQ(0, edid->productId);
EXPECT_EQ(29, edid->manufactureOrModelYear);
EXPECT_EQ(18, edid->manufactureWeek);
+ ASSERT_TRUE(edid->cea861Block);
+ ASSERT_TRUE(edid->cea861Block->hdmiVendorDataBlock);
+ ASSERT_TRUE(edid->cea861Block->hdmiVendorDataBlock->physicalAddress);
+ physicalAddress = edid->cea861Block->hdmiVendorDataBlock->physicalAddress;
+ EXPECT_EQ(1, physicalAddress->a);
+ EXPECT_EQ(2, physicalAddress->b);
+ EXPECT_EQ(3, physicalAddress->c);
+ EXPECT_EQ(4, physicalAddress->d);
edid = parseEdid(getCtlDisplayEdid());
ASSERT_TRUE(edid);
@@ -235,6 +261,8 @@ TEST(DisplayIdentificationTest, parseEdid) {
EXPECT_EQ(9373, edid->productId);
EXPECT_EQ(23, edid->manufactureOrModelYear);
EXPECT_EQ(0xff, edid->manufactureWeek);
+ ASSERT_TRUE(edid->cea861Block);
+ EXPECT_FALSE(edid->cea861Block->hdmiVendorDataBlock);
}
TEST(DisplayIdentificationTest, parseInvalidEdid) {