aboutsummaryrefslogtreecommitdiff
path: root/hwc2_device/HwcDisplayConfigs.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'hwc2_device/HwcDisplayConfigs.cpp')
-rw-r--r--hwc2_device/HwcDisplayConfigs.cpp202
1 files changed, 202 insertions, 0 deletions
diff --git a/hwc2_device/HwcDisplayConfigs.cpp b/hwc2_device/HwcDisplayConfigs.cpp
new file mode 100644
index 0000000..6a3ed5a
--- /dev/null
+++ b/hwc2_device/HwcDisplayConfigs.cpp
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "hwc-display-configs"
+
+#include "HwcDisplayConfigs.h"
+
+#include <cmath>
+
+#include "drm/DrmConnector.h"
+#include "utils/log.h"
+
+constexpr uint32_t kHeadlessModeDisplayWidthMm = 163;
+constexpr uint32_t kHeadlessModeDisplayHeightMm = 122;
+constexpr uint32_t kHeadlessModeDisplayWidthPx = 1024;
+constexpr uint32_t kHeadlessModeDisplayHeightPx = 768;
+constexpr uint32_t kHeadlessModeDisplayVRefresh = 60;
+
+namespace android {
+
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+uint32_t HwcDisplayConfigs::last_config_id = 1;
+
+void HwcDisplayConfigs::FillHeadless() {
+ hwc_configs.clear();
+
+ last_config_id++;
+ preferred_config_id = active_config_id = last_config_id;
+ auto headless_drm_mode_info = (drmModeModeInfo){
+ .hdisplay = kHeadlessModeDisplayWidthPx,
+ .vdisplay = kHeadlessModeDisplayHeightPx,
+ .vrefresh = kHeadlessModeDisplayVRefresh,
+ .name = "HEADLESS-MODE",
+ };
+ hwc_configs[active_config_id] = (HwcDisplayConfig){
+ .id = active_config_id,
+ .group_id = 1,
+ .mode = DrmMode(&headless_drm_mode_info),
+ };
+
+ mm_width = kHeadlessModeDisplayWidthMm;
+ mm_height = kHeadlessModeDisplayHeightMm;
+}
+
+// NOLINTNEXTLINE (readability-function-cognitive-complexity): Fixme
+HWC2::Error HwcDisplayConfigs::Update(DrmConnector &connector) {
+ /* In case UpdateModes will fail we will still have one mode for headless
+ * mode*/
+ FillHeadless();
+ /* Read real configs */
+ int ret = connector.UpdateModes();
+ if (ret != 0) {
+ ALOGE("Failed to update display modes %d", ret);
+ return HWC2::Error::BadDisplay;
+ }
+
+ if (connector.GetModes().empty()) {
+ ALOGE("No modes reported by KMS");
+ return HWC2::Error::BadDisplay;
+ }
+
+ hwc_configs.clear();
+ mm_width = connector.GetMmWidth();
+ mm_height = connector.GetMmHeight();
+
+ preferred_config_id = 0;
+ uint32_t preferred_config_group_id = 0;
+
+ uint32_t first_config_id = last_config_id;
+ uint32_t last_group_id = 1;
+
+ /* Group modes */
+ for (const auto &mode : connector.GetModes()) {
+ /* Find group for the new mode or create new group */
+ uint32_t group_found = 0;
+ for (auto &hwc_config : hwc_configs) {
+ if (mode.h_display() == hwc_config.second.mode.h_display() &&
+ mode.v_display() == hwc_config.second.mode.v_display()) {
+ group_found = hwc_config.second.group_id;
+ }
+ }
+ if (group_found == 0) {
+ group_found = last_group_id++;
+ }
+
+ bool disabled = false;
+ if ((mode.flags() & DRM_MODE_FLAG_3D_MASK) != 0) {
+ ALOGI("Disabling display mode %s (Modes with 3D flag aren't supported)",
+ mode.name().c_str());
+ disabled = true;
+ }
+
+ /* Add config */
+ hwc_configs[last_config_id] = {
+ .id = last_config_id,
+ .group_id = group_found,
+ .mode = mode,
+ .disabled = disabled,
+ };
+
+ /* Chwck if the mode is preferred */
+ if ((mode.type() & DRM_MODE_TYPE_PREFERRED) != 0 &&
+ preferred_config_id == 0) {
+ preferred_config_id = last_config_id;
+ preferred_config_group_id = group_found;
+ }
+
+ last_config_id++;
+ }
+
+ /* We must have preferred mode. Set first mode as preferred
+ * in case KMS haven't reported anything. */
+ if (preferred_config_id == 0) {
+ preferred_config_id = first_config_id;
+ preferred_config_group_id = 1;
+ }
+
+ for (uint32_t group = 1; group < last_group_id; group++) {
+ bool has_interlaced = false;
+ bool has_progressive = false;
+ for (auto &hwc_config : hwc_configs) {
+ if (hwc_config.second.group_id != group || hwc_config.second.disabled) {
+ continue;
+ }
+
+ if (hwc_config.second.IsInterlaced()) {
+ has_interlaced = true;
+ } else {
+ has_progressive = true;
+ }
+ }
+
+ bool has_both = has_interlaced && has_progressive;
+ if (!has_both) {
+ continue;
+ }
+
+ bool group_contains_preferred_interlaced = false;
+ if (group == preferred_config_group_id &&
+ hwc_configs[preferred_config_id].IsInterlaced()) {
+ group_contains_preferred_interlaced = true;
+ }
+
+ for (auto &hwc_config : hwc_configs) {
+ if (hwc_config.second.group_id != group || hwc_config.second.disabled) {
+ continue;
+ }
+
+ bool disable = group_contains_preferred_interlaced
+ ? !hwc_config.second.IsInterlaced()
+ : hwc_config.second.IsInterlaced();
+
+ if (disable) {
+ ALOGI(
+ "Group %i: Disabling display mode %s (This group should consist "
+ "of %s modes)",
+ group, hwc_config.second.mode.name().c_str(),
+ group_contains_preferred_interlaced ? "interlaced" : "progressive");
+
+ hwc_config.second.disabled = true;
+ }
+ }
+ }
+
+ /* Group should not contain 2 modes with FPS delta less than ~1HZ
+ * otherwise android.graphics.cts.SetFrameRateTest CTS will fail
+ */
+ constexpr float kMinFpsDelta = 1.0; // FPS
+ for (uint32_t m1 = first_config_id; m1 < last_config_id; m1++) {
+ for (uint32_t m2 = first_config_id; m2 < last_config_id; m2++) {
+ if (m1 != m2 && hwc_configs[m1].group_id == hwc_configs[m2].group_id &&
+ !hwc_configs[m1].disabled && !hwc_configs[m2].disabled &&
+ fabsf(hwc_configs[m1].mode.v_refresh() -
+ hwc_configs[m2].mode.v_refresh()) < kMinFpsDelta) {
+ ALOGI(
+ "Group %i: Disabling display mode %s (Refresh rate value is "
+ "too close to existing mode %s)",
+ hwc_configs[m2].group_id, hwc_configs[m2].mode.name().c_str(),
+ hwc_configs[m1].mode.name().c_str());
+
+ hwc_configs[m2].disabled = true;
+ }
+ }
+ }
+
+ return HWC2::Error::None;
+}
+
+} // namespace android