| // Copyright 2021 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. |
| |
| #include "src/graphics/display/drivers/coordinator/eld.h" |
| |
| #include <zircon/assert.h> |
| |
| #include <cstddef> |
| #include <cstdint> |
| #include <cstring> |
| #include <limits> |
| |
| #include <fbl/array.h> |
| |
| #include "src/graphics/display/lib/edid/edid.h" |
| #include "src/lib/eld/eld.h" |
| |
| namespace display { |
| |
| fbl::Array<uint8_t> ComputeEld(const edid::Edid& edid) { |
| // First we calculate the total length so we can allocate. |
| // The total ELD length of the ELD includes the ELD header, the ELD baseline (parts 1, 2 and 3) |
| // and the any vendor specific data (not suported). |
| |
| // We need the baseline part 2 variable length from the monitor name. |
| // We populate up to kEldMonitorNameMaxLength bytes of monitor name. |
| constexpr size_t kMaxMonitorNameStringLength = 16; |
| size_t monitor_name_string_len = strlen(edid.monitor_name()); |
| if (monitor_name_string_len > kMaxMonitorNameStringLength) { |
| monitor_name_string_len = kMaxMonitorNameStringLength; |
| } |
| const size_t eld_baseline_part2_length = monitor_name_string_len; |
| |
| // We need the number of short audio descriptors to calculate the baseline part 3 length. |
| size_t number_of_short_audio_descriptors = 0; |
| for (auto it = edid::audio_data_block_iterator(&edid); it.is_valid(); ++it) { |
| if (it->format() != edid::ShortAudioDescriptor::kLPcm) { |
| // TODO(andresoportus): Add compressed formats. |
| continue; |
| } |
| number_of_short_audio_descriptors++; |
| } |
| const size_t eld_baseline_part3_length = |
| number_of_short_audio_descriptors * sizeof(edid::ShortAudioDescriptor); |
| |
| // Now we can calculate the ELD length. |
| size_t eld_length = sizeof(hda::EldHeader) + sizeof(hda::EldBaselinePart1) + |
| eld_baseline_part2_length + eld_baseline_part3_length; |
| eld_length = (eld_length + 3) & ~3; // Make the ELD length multiple of 4. |
| |
| // With the ELD length we can allocate and then fill in the data. |
| // |
| // The array is default-initialized, so all ELD fields are set to zero. |
| fbl::Array<uint8_t> eld = fbl::MakeArray<uint8_t>(eld_length); |
| if (eld.empty()) { |
| // Array allocation failed. |
| return eld; |
| } |
| |
| // Fill the data, moving pointer p along the way. |
| uint8_t* p = eld.get(); |
| |
| // Populate the ELD header. |
| hda::EldHeader* header = reinterpret_cast<hda::EldHeader*>(p); |
| header->set_eld_ver(2); |
| // These asserts guarantee the cast directly below will not cause UB. |
| ZX_ASSERT_MSG(eld_length > 0, "Overflow while computing ELD length"); |
| ZX_ASSERT(eld_length < std::numeric_limits<uint32_t>::max()); |
| header->set_baseline_eld_len(static_cast<uint32_t>(eld_length)); |
| p += sizeof(hda::EldHeader); |
| |
| // Populate the ELD baseline part 1. |
| hda::EldBaselinePart1* part1 = reinterpret_cast<hda::EldBaselinePart1*>(p); |
| // "with CEA-861-C and continuing through present, incrementing the version number is no longer |
| // required. The revision number shall be set to 0x03" |
| part1->set_cea_edid_ver(3); |
| // The cast does not cause UB because monitor_name_string_len has an upper |
| // bound of kMaxMonitorNameStringLength, which is 16. |
| part1->set_mnl(static_cast<char>(monitor_name_string_len)); |
| part1->set_sad_count(static_cast<uint8_t>(number_of_short_audio_descriptors)); |
| part1->set_conn_type(edid.is_hdmi() ? 0 : 1); |
| part1->set_s_ai(0); // Not supported: ACP, ISRC1, or ISRC2 packets. |
| part1->set_hdcp(0); // Not supported. |
| part1->aud_synch_delay = 0; // Not supported. |
| part1->byte4 = 0; // Not supported: FLR, LFE, FC, RLR, RC, FLRC, RLRC. |
| part1->port_id = 0; // Not supported. |
| part1->manufacturer_name = edid.manufacturer_name_code(); |
| part1->product_code = edid.product_code(); |
| p += sizeof(hda::EldBaselinePart1); |
| |
| // Populate the ELD baseline part 2: monitor_name_string. |
| memcpy(p, edid.monitor_name(), monitor_name_string_len); |
| p += monitor_name_string_len; |
| |
| // Populate the ELD baseline part 3: short audio descriptors. |
| for (auto it = edid::audio_data_block_iterator(&edid); it.is_valid(); ++it) { |
| if (it->format() != edid::ShortAudioDescriptor::kLPcm) { |
| // TODO(andresoportus): Add compressed formats. |
| continue; |
| } |
| edid::ShortAudioDescriptor* sad = reinterpret_cast<edid::ShortAudioDescriptor*>(p); |
| p += sizeof(edid::ShortAudioDescriptor); |
| *sad = *it; |
| } |
| // We don't populate the vendor specific block. |
| |
| return eld; |
| } |
| |
| } // namespace display |