diff --git a/drm/DrmConnector.cpp b/drm/DrmConnector.cpp index 590f76f..3de4f5c 100644 --- a/drm/DrmConnector.cpp +++ b/drm/DrmConnector.cpp @@ -21,9 +21,11 @@ #include #include +#include #include #include #include +#include #include #include @@ -265,6 +267,26 @@ std::string DrmConnector::GetName() const { return "None"; } +namespace { +// Some EDIDs describe the same effective resolution/refresh-rate as two +// distinct raw mode entries -- e.g. a detailed-timing "preferred" mode plus a +// CEA-861 mode for the same nominal timing, differing only in blanking or in +// the DRM_MODE_TYPE_PREFERRED bit. A byte-for-byte comparison (DrmMode:: +// operator==) treats these as separate modes, so both survive into the mode +// list. That leaves which mode is "preferred" ambiguous between the kernel's +// initial modeset and drm_hwcomposer's own selection, which is the suspected +// cause of a mid-boot mode switch (visible as overscan until HDMI is +// replugged) and of framework APIs like Display.getSystemPreferredDisplayMode() +// intermittently returning null (TvSettings NPE on the Resolution screen). +constexpr float kDuplicateModeRateEpsilonHz = 0.05F; + +bool IsSameEffectiveMode(const DrmMode &existing, const DrmMode &candidate) { + return existing.SameSize(candidate) && + std::abs(existing.GetVRefresh() - candidate.GetVRefresh()) < + kDuplicateModeRateEpsilonHz; +} +} // namespace + int DrmConnector::UpdateModes() { std::string force_mode = Properties::GetForceMode(); uint32_t xres = 0, yres = 0, rate = 0; @@ -287,35 +309,43 @@ int DrmConnector::UpdateModes() { modes_.clear(); for (int i = 0; i < connector_->count_modes; ++i) { - bool exists = false; - for (const DrmMode &mode : modes_) { - if (mode == connector_->modes[i]) { - exists = true; - break; + DrmMode m(&connector_->modes[i]); + ALOGV("Supported mode %dx%d@%fHz for display in connector %s", + m.GetRawMode().hdisplay, m.GetRawMode().vdisplay, + m.GetVRefresh(), GetName().c_str()); + + auto existing = std::find_if(modes_.begin(), modes_.end(), + [&m](const DrmMode &other) { + return IsSameEffectiveMode(other, m); + }); + if (existing != modes_.end()) { + // Keep whichever duplicate carries the EDID's PREFERRED flag. + if ((m.GetRawMode().type & DRM_MODE_TYPE_PREFERRED) != 0 && + (existing->GetRawMode().type & DRM_MODE_TYPE_PREFERRED) == 0) { + ALOGD("Mode %dx%d@%fHz for display in connector %s duplicated; " + "keeping the PREFERRED entry", + m.GetRawMode().hdisplay, m.GetRawMode().vdisplay, + m.GetVRefresh(), GetName().c_str()); + *existing = m; } + continue; } - if (!exists) { - DrmMode m(&connector_->modes[i]); - ALOGV("Supported mode %dx%d@%fHz for display in connector %s", - m.GetRawMode().hdisplay, m.GetRawMode().vdisplay, - m.GetVRefresh(), GetName().c_str()); - if (xres && yres) { - if (!rate && m.GetRawMode().hdisplay == xres - && m.GetRawMode().vdisplay == yres) { - rate = m.GetVRefresh(); - } - if (m.GetRawMode().hdisplay != xres - || m.GetRawMode().vdisplay != yres - || m.GetVRefresh() != rate) { - continue; - } + if (xres && yres) { + if (!rate && m.GetRawMode().hdisplay == xres + && m.GetRawMode().vdisplay == yres) { + rate = m.GetVRefresh(); + } + if (m.GetRawMode().hdisplay != xres + || m.GetRawMode().vdisplay != yres + || m.GetVRefresh() != rate) { + continue; } - ALOGD("Add mode %dx%d@%fHz for display in connector %s", - m.GetRawMode().hdisplay, m.GetRawMode().vdisplay, - m.GetVRefresh(), GetName().c_str()); - modes_.emplace_back(m); } + ALOGD("Add mode %dx%d@%fHz for display in connector %s", + m.GetRawMode().hdisplay, m.GetRawMode().vdisplay, + m.GetVRefresh(), GetName().c_str()); + modes_.emplace_back(m); } return 0;