Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 81835446 authored by Marin Shalamanov's avatar Marin Shalamanov Committed by Automerger Merge Worker
Browse files

SF: Parse HDMI Physical Address from EDID. am: f1274d45 am: cdd4894c

Change-Id: If24ffe3e86e124c3488455155752e5a10249b6ad
parents 1053c110 cdd4894c
Loading
Loading
Loading
Loading
+86 −5
Original line number Diff line number Diff line
@@ -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) {
+21 −0
Original line number Diff line number Diff line
@@ -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&);
+29 −1
Original line number Diff line number Diff line
@@ -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) {