blob: 2f6e21f7c5e0a9f035bcfe9f153930ed9d057ebd [file] [log] [blame]
// 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