blob: c05d06cfcb97da0d65db5afd6c28041a893ac58e [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_GRAPHICS_DISPLAY_LIB_EDID_EDID_H_
#define SRC_GRAPHICS_DISPLAY_LIB_EDID_EDID_H_
#include <lib/fit/result.h>
#include <lib/stdcompat/span.h>
#include <lib/zx/result.h>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <string>
#include <fbl/vector.h>
#include <hwreg/bitfields.h>
#include "src/graphics/display/lib/api-types/cpp/display-timing.h"
#include "src/graphics/display/lib/edid/internal/iterators.h"
// References
//
// The code contains references to the following documents.
//
// - VESA Enhanced Extended Display Identification Data (E-EDID) Standard,
// Video Electronics Standards Association (VESA), Release A, Revision 2,
// dated September 25, 2006, revised December 31, 2020.
// Referenced as "E-EDID standard".
// Available at https://vesa.org/vesa-standards/ .
namespace edid {
// The size of an EDID block;
static constexpr uint32_t kBlockSize = 128;
// The maximum number of blocks in an E-EDID.
//
// E-EDID standard, Section 2.2.1 "EDID Extensions: Order of the Blocks",
// page 16.
static constexpr int kMaxEdidBlockCount = 256;
// Definitions for parsing EDID data.
// EDID 18-byte detailed timing descriptor.
//
// Many of the parameters in the timing descriptor are split across
// multiple fields, so we define various accessors for reading them.
//
// See "Table 3.21 - Detailed Timing Definition - Part 1" (in Release
// A, Revision 2 of the EDID spec, 2006).
struct __PACKED DetailedTimingDescriptor {
uint32_t horizontal_addressable() const {
return horizontal_addressable_low | (horizontal_addressable_high() << 8);
}
uint32_t horizontal_blanking() const {
return horizontal_blanking_low | (horizontal_blanking_high() << 8);
}
uint32_t vertical_addressable() const {
return vertical_addressable_low | (vertical_addressable_high() << 8);
}
uint32_t vertical_blanking() const {
return vertical_blanking_low | (vertical_blanking_high() << 8);
}
uint32_t horizontal_front_porch() const {
return horizontal_front_porch_low | (horizontal_front_porch_high() << 8);
}
uint32_t horizontal_sync_pulse_width() const {
return horizontal_sync_pulse_width_low | (horizontal_sync_pulse_width_high() << 8);
}
uint32_t vertical_front_porch() const {
return vertical_front_porch_low() | (vertical_front_porch_high() << 4);
}
uint32_t vertical_sync_pulse_width() const {
return vertical_sync_pulse_width_low() | (vertical_sync_pulse_width_high() << 4);
}
// Offset 0
uint16_t pixel_clock_10khz;
// Offset 2
uint8_t horizontal_addressable_low;
uint8_t horizontal_blanking_low;
uint8_t horizontal_fields1;
DEF_SUBFIELD(horizontal_fields1, 7, 4, horizontal_addressable_high);
DEF_SUBFIELD(horizontal_fields1, 3, 0, horizontal_blanking_high);
// Offset 5
uint8_t vertical_addressable_low;
uint8_t vertical_blanking_low;
uint8_t vertical_fields1;
DEF_SUBFIELD(vertical_fields1, 7, 4, vertical_addressable_high);
DEF_SUBFIELD(vertical_fields1, 3, 0, vertical_blanking_high);
// Offset 8
uint8_t horizontal_front_porch_low;
uint8_t horizontal_sync_pulse_width_low;
// Offset 10
uint8_t vertical_fields2;
DEF_SUBFIELD(vertical_fields2, 7, 4, vertical_front_porch_low);
DEF_SUBFIELD(vertical_fields2, 3, 0, vertical_sync_pulse_width_low);
// Offset 11
uint8_t combined;
DEF_SUBFIELD(combined, 7, 6, horizontal_front_porch_high);
DEF_SUBFIELD(combined, 5, 4, horizontal_sync_pulse_width_high);
DEF_SUBFIELD(combined, 3, 2, vertical_front_porch_high);
DEF_SUBFIELD(combined, 1, 0, vertical_sync_pulse_width_high);
uint8_t rest[5]; // Fields that we don't need to read yet.
uint8_t features;
DEF_SUBBIT(features, 7, interlaced);
DEF_SUBFIELD(features, 4, 3, type);
DEF_SUBBIT(features, 2, vsync_polarity);
DEF_SUBBIT(features, 1, hsync_polarity);
};
#define TYPE_ANALOG 0
#define TYPE_ANALOG_BIPOLAR 1
#define TYPE_DIGITAL_COMPOSITE 2
#define TYPE_DIGITAL_SEPARATE 3
static_assert(sizeof(DetailedTimingDescriptor) == 18, "Size check for EdidTimingDesc");
union __PACKED Descriptor {
DetailedTimingDescriptor timing;
struct Monitor {
uint16_t generic_tag;
uint8_t padding;
uint8_t type;
static constexpr uint8_t kDummyType = 0x10;
static constexpr uint8_t kName = 0xfc;
static constexpr uint8_t kSerial = 0xff;
uint8_t padding2;
uint8_t data[13];
} monitor;
};
static_assert(sizeof(Descriptor) == 18, "bad struct");
struct __PACKED StandardTimingDescriptor {
uint32_t horizontal_resolution() const { return (byte1 + 31) * 8; }
uint32_t vertical_resolution(uint8_t edid_version, uint8_t edid_revision) const {
if (aspect_ratio() == 0) {
if (edid_version < 1 || (edid_version == 1 && edid_revision < 3)) {
return horizontal_resolution();
} else {
return horizontal_resolution() * 10 / 16;
}
} else if (aspect_ratio() == 1) {
return horizontal_resolution() * 3 / 4;
} else if (aspect_ratio() == 2) {
return horizontal_resolution() * 4 / 5;
} else if (aspect_ratio() == 3) {
return horizontal_resolution() * 9 / 16;
} else {
ZX_DEBUG_ASSERT(false);
return 0;
}
}
uint8_t byte1;
uint8_t byte2;
DEF_SUBFIELD(byte2, 7, 6, aspect_ratio);
DEF_SUBFIELD(byte2, 5, 0, vertical_freq);
};
// This covers the "base" EDID data -- the first 128 bytes (block 0). In
// many cases, that is all the display provides, but there may be more data
// in extension blocks.
//
// See "Table 3.1 - EDID Structure Version 1, Revision 4" (in Release
// A, Revision 2 of the EDID spec, 2006).
struct __PACKED BaseEdid {
bool validate() const;
// Not actually a tag, but the first byte will always be this
static constexpr uint8_t kTag = 0x00;
// Offset 0
uint8_t header[8];
uint8_t manufacturer_id1;
uint8_t manufacturer_id2;
uint16_t product_code;
uint32_t serial_number;
uint8_t unused1[2];
uint8_t edid_version;
uint8_t edid_revision;
uint8_t video_input_definition;
DEF_SUBBIT(video_input_definition, 7, digital);
uint8_t horizontal_size_cm;
uint8_t vertical_size_cm;
uint8_t features_bitmap;
DEF_SUBBIT(features_bitmap, 2, standard_srgb);
uint8_t various[14]; // Fields that we don't need to read yet.
StandardTimingDescriptor standard_timings[8];
Descriptor detailed_descriptors[4];
uint8_t num_extensions;
uint8_t checksum_byte;
};
static_assert(offsetof(BaseEdid, edid_version) == 0x12, "Layout check");
static_assert(offsetof(BaseEdid, standard_timings) == 0x26, "Layout check");
static_assert(offsetof(BaseEdid, detailed_descriptors) == 0x36, "Layout check");
static_assert(offsetof(BaseEdid, num_extensions) == 0x7e, "Layout check");
// Version 3 of the CEA EDID Timing Extension
struct __PACKED CeaEdidTimingExtension {
static constexpr uint8_t kTag = 0x02;
bool validate() const;
uint8_t tag;
uint8_t revision_number;
uint8_t dtd_start_idx;
uint8_t combined;
DEF_SUBBIT(combined, 7, underscan);
DEF_SUBBIT(combined, 6, basic_audio);
DEF_SUBBIT(combined, 5, ycbcr_444);
DEF_SUBBIT(combined, 4, ycbcr_422);
DEF_SUBFIELD(combined, 3, 0, native_format_dtds);
uint8_t payload[123];
uint8_t checksum_byte;
};
// Short audio descriptor from CEA EDID timing extension's data block collection.
struct ShortAudioDescriptor {
static constexpr uint8_t kType = 1;
uint8_t format_and_channels;
DEF_SUBFIELD(format_and_channels, 6, 3, format);
static constexpr uint8_t kLPcm = 1;
DEF_SUBFIELD(format_and_channels, 2, 0, num_channels_minus_1);
uint8_t sampling_frequencies;
static constexpr uint8_t kHz192 = (1 << 6);
static constexpr uint8_t kHz176 = (1 << 5);
static constexpr uint8_t kHz96 = (1 << 4);
static constexpr uint8_t kHz88 = (1 << 3);
static constexpr uint8_t kHz48 = (1 << 2);
static constexpr uint8_t kHz44 = (1 << 1);
static constexpr uint8_t kHz32 = (1 << 0);
uint8_t bitrate;
static constexpr uint8_t kLpcm_24 = (1 << 2);
static constexpr uint8_t kLpcm_20 = (1 << 1);
static constexpr uint8_t kLpcm_16 = (1 << 0);
DEF_SUBBIT(bitrate, 2, lpcm_24);
DEF_SUBBIT(bitrate, 1, lpcm_20);
DEF_SUBBIT(bitrate, 0, lpcm_16);
};
static_assert(sizeof(ShortAudioDescriptor) == 3, "Bad size for ShortAudioDescriptor");
// Short video descriptor from CEA EDID timing extension's data block collection.
struct ShortVideoDescriptor {
static constexpr uint8_t kType = 2;
uint8_t data;
DEF_SUBBIT(data, 7, native);
DEF_SUBFIELD(data, 6, 0, standard_mode_idx);
};
static_assert(sizeof(ShortVideoDescriptor) == 1, "Bad size for ShortVideoDescriptor");
// Vendor specific block from CEA EDID timing extension's data block collection.
struct VendorSpecificBlock {
static constexpr uint8_t kType = 3;
uint8_t vendor_number[3];
uint8_t physical_addr_low;
uint8_t physical_addr_high;
// The payload contains vendor defined data. It is only valid up to the
// index specified by the data block's length.
uint8_t payload[26];
};
static_assert(sizeof(VendorSpecificBlock) == 31, "Bad size for VendorSpecificBlock");
// Short speaker descriptor from CEA EDID timing extension's data block collection.
struct ShortSpeakerDescriptor {
static constexpr uint8_t kType = 4;
uint8_t features;
DEF_SUBBIT(features, 6, rear_left_right_center);
DEF_SUBBIT(features, 5, front_left_right_center);
DEF_SUBBIT(features, 4, rear_center);
DEF_SUBBIT(features, 3, rear_left_right);
DEF_SUBBIT(features, 2, front_center);
DEF_SUBBIT(features, 1, lfe);
DEF_SUBBIT(features, 0, front_left_right);
uint8_t reserved;
uint8_t reserved2;
};
static_assert(sizeof(ShortSpeakerDescriptor) == 3, "Bad size for ShortSpeakerDescriptor");
// Data block from CEA EDID timing extension's data block collection. Although this
// struct is 32 bytes long, only the first length+1 bytes are actually valid.
struct DataBlock {
uint8_t header;
DEF_SUBFIELD(header, 7, 5, type);
DEF_SUBFIELD(header, 4, 0, length);
union {
ShortAudioDescriptor audio[10];
// Only valid up to the index specified by length;
ShortVideoDescriptor video[31];
VendorSpecificBlock vendor;
ShortSpeakerDescriptor speaker;
} payload;
};
static_assert(sizeof(DataBlock) == 32, "Bad size for DataBlock");
// Returns an EISA vendor name based on its 2 bytes manufacturer name code.
// Returned string is a statically allocated constant. Returns NULL if no match is found.
const char* GetEisaVendorName(uint16_t manufacturer_name_code);
class timing_iterator;
class Edid {
public:
// Factory function preferred for production use.
//
// Creates and validates an Edid from raw bytes.
//
// On error, returns a `const char*` string of static storage duration
// containing the error message.
static fit::result<const char*, Edid> Create(cpp20::span<const uint8_t> bytes);
// Production code must use the factory method `Create()`.
//
// `bytes` must not be empty.
// The size of `bytes` must be divisible by kBlockSize.
// The size of `bytes` must not exceed kMaxEdidBlockCount * kBlockSize.
explicit Edid(fbl::Vector<uint8_t> bytes);
Edid(const Edid&) = delete;
Edid& operator=(const Edid&) = delete;
Edid(Edid&&) = default;
Edid& operator=(Edid&&) = default;
void Print(void (*print_fn)(const char* str)) const;
const uint8_t* edid_bytes() const { return bytes_.data(); }
size_t edid_length() const { return bytes_.size(); }
uint16_t product_code() const { return base_edid().product_code; }
bool is_standard_rgb() const { return base_edid().standard_srgb(); }
bool supports_basic_audio() const;
// Returns the display manufacturer's ISA / UEFI Plug and Play device
// identifier (PNPID).
std::string GetManufacturerId() const;
// Returns an empty string ("") if the manufacturer is not found.
//
// Otherwise, returns the name of the display product manufacturer as
// registered in the PNP ID Registry from the Unified Extensible Firmware
// Interface (UEFI) Forum (https://uefi.org/PNP_ID_List).
//
// The returned string is guaranteed to have a static storage duration.
const char* GetManufacturerName() const;
// Returns the display product name stored in the Display Product Name String
// Descriptor, defined in the E-EDID standard, Section 3.10.3.4 "Display
// Product Name (ASCII) String Descriptor Definition (tag #FCh)", page 44.
std::string GetDisplayProductName() const;
// Returns the display product serial number stored in the Display Product
// Serial Number Descriptor, defined in the E-EDID standard, Section 3.10.3.2
// "Display Product Serial Number Descriptor Definition (tag #FFh)".
std::string GetDisplayProductSerialNumber() const;
// Returns all the display timing parameters supported by this display device.
//
// The first display timing is guaranteed to be preferred.
zx::result<fbl::Vector<display::DisplayTiming>> GetSupportedDisplayTimings() const;
// Guaranteed to be >= 0 and < 2^16.
int horizontal_size_mm() const;
// Guaranteed to be >= 0 and < 2^16.
int vertical_size_mm() const;
bool is_hdmi() const;
const BaseEdid& base_edid() const { return *reinterpret_cast<const BaseEdid*>(bytes_.data()); }
private:
friend class internal::data_block_iterator;
friend class internal::descriptor_iterator;
friend class timing_iterator;
template <typename T>
const T* GetBlock(uint8_t block_num) const;
fit::result<const char*> Validate();
fbl::Vector<uint8_t> bytes_;
};
template <typename T>
const T* Edid::GetBlock(uint8_t block_num) const {
const uint8_t* bytes = bytes_.data() + block_num * kBlockSize;
return bytes[0] == T::kTag ? reinterpret_cast<const T*>(bytes) : nullptr;
}
// Iterator that returns all of the timing modes of the display. The iterator
// **does not** filter out duplicates.
class timing_iterator {
public:
explicit timing_iterator(const Edid* edid)
: edid_(edid), state_(kDtds), state_index_(0), descriptors_(edid), dbs_(edid) {
++(*this);
}
timing_iterator& operator++();
const display::DisplayTiming& operator*() const { return display_timing_; }
const display::DisplayTiming* operator->() const { return &display_timing_; }
bool is_valid() const { return state_ != kDone; }
private:
// The order in which timings are returned is:
// 1) Detailed timings in order across base EDID and CEA blocks
// 2) Short video descriptors in CEA data blocks
// 3) Standard timings in base edid
// TODO: Standart timings in descriptors
// TODO: GTF/CVT timings in descriptors/monitor range limits
// TODO: Established timings
static constexpr uint8_t kDtds = 0;
static constexpr uint8_t kSvds = 1;
static constexpr uint8_t kStds = 2;
static constexpr uint8_t kDone = 3;
void Advance();
display::DisplayTiming display_timing_;
const Edid* edid_;
uint8_t state_;
uint16_t state_index_;
internal::descriptor_iterator descriptors_;
internal::data_block_iterator dbs_;
};
} // namespace edid
#endif // SRC_GRAPHICS_DISPLAY_LIB_EDID_EDID_H_