1
0
Fork 0
android_external_drm_hwcomp.../hwc/HwcDisplayConfigs.cpp
2025-06-27 00:08:13 -05:00

255 lines
8.2 KiB
C++

/*
* 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 "drmhwc"
#include "HwcDisplayConfigs.h"
#include <cmath>
#include <cstring>
#include "drm/DrmConnector.h"
#include "utils/log.h"
#include "utils/properties.h"
#include <cutils/properties.h>
constexpr uint32_t kHeadlessModeDisplayVRefresh = 60;
constexpr uint32_t kSyncLen = 10;
constexpr uint32_t kBackPorch = 10;
constexpr uint32_t kFrontPorch = 10;
constexpr uint32_t kHzInKHz = 1000;
namespace android {
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
ConfigId HwcDisplayConfigs::last_config_id = 1;
void HwcDisplayConfigs::GenFakeMode(uint16_t width, uint16_t height) {
char value[PROPERTY_VALUE_MAX];
uint32_t xres = 0, yres = 0;
if (property_get("debug.drm.mode.force", value, NULL)) {
// parse <xres>x<yres>[@<refreshrate>]
if (sscanf(value, "%dx%d", &xres, &yres) != 2) {
xres = yres = 0;
}
}
const uint16_t kHeadlessModeDisplayWidthMm = xres ? xres : 1600;
const uint16_t kHeadlessModeDisplayHeightMm = yres ? yres : 900;
const uint16_t kHeadlessModeDisplayWidthPx = xres ? xres : 1920;
const uint16_t kHeadlessModeDisplayHeightPx = yres ? yres : 1080;
ALOGI("set mode %dx%d@%dHz for HEADLESS-MODE", kHeadlessModeDisplayWidthPx,
kHeadlessModeDisplayHeightPx, kHeadlessModeDisplayVRefresh);
hwc_configs.clear();
last_config_id++;
preferred_config_id = active_config_id = last_config_id;
auto headless_drm_mode_info = (drmModeModeInfo){
.hdisplay = width,
.vdisplay = height,
.vrefresh = kHeadlessModeDisplayVRefresh,
.name = "VIRTUAL-MODE",
};
if (width == 0 || height == 0) {
strcpy(headless_drm_mode_info.name, "HEADLESS-MODE");
headless_drm_mode_info.hdisplay = kHeadlessModeDisplayWidthPx;
headless_drm_mode_info.vdisplay = kHeadlessModeDisplayHeightPx;
}
/* We need a valid mode to pass the kernel validation */
headless_drm_mode_info.hsync_start = headless_drm_mode_info.hdisplay +
kFrontPorch;
headless_drm_mode_info.hsync_end = headless_drm_mode_info.hsync_start +
kSyncLen;
headless_drm_mode_info.htotal = headless_drm_mode_info.hsync_end + kBackPorch;
headless_drm_mode_info.vsync_start = headless_drm_mode_info.vdisplay +
kFrontPorch;
headless_drm_mode_info.vsync_end = headless_drm_mode_info.vsync_start +
kSyncLen;
headless_drm_mode_info.vtotal = headless_drm_mode_info.vsync_end + kBackPorch;
headless_drm_mode_info.clock = (headless_drm_mode_info.htotal *
headless_drm_mode_info.vtotal *
headless_drm_mode_info.vrefresh) /
kHzInKHz;
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
bool HwcDisplayConfigs::Update(DrmConnector &connector) {
/* In case UpdateModes will fail we will still have one mode for headless
* mode
*/
GenFakeMode(0, 0);
/* Read real configs */
auto ret = connector.UpdateModes();
if (ret != 0) {
ALOGE("Failed to update display modes %d", ret);
return false;
}
if (connector.GetModes().empty()) {
ALOGE("No modes reported by KMS");
return false;
}
hwc_configs.clear();
mm_width = connector.GetMmWidth();
mm_height = connector.GetMmHeight();
preferred_config_id = 0;
uint32_t preferred_config_group_id = 0;
auto first_config_id = last_config_id;
uint32_t last_group_id = 1;
const bool use_config_groups = Properties::UseConfigGroups();
/* Group modes */
for (const auto &mode : connector.GetModes()) {
/* Find group for the new mode or create new group */
uint32_t group_found = 0;
if (use_config_groups) {
for (auto &hwc_config : hwc_configs) {
if (mode.GetRawMode().hdisplay ==
hwc_config.second.mode.GetRawMode().hdisplay &&
mode.GetRawMode().vdisplay ==
hwc_config.second.mode.GetRawMode().vdisplay) {
group_found = hwc_config.second.group_id;
}
}
}
if (group_found == 0) {
group_found = last_group_id++;
}
bool disabled = false;
if ((mode.GetRawMode().flags & DRM_MODE_FLAG_3D_MASK) != 0) {
ALOGI("Disabling display mode %s (Modes with 3D flag aren't supported)",
mode.GetName().c_str());
disabled = true;
}
/* Add config */
hwc_configs[last_config_id] = {
.id = last_config_id,
.group_id = group_found,
.mode = mode,
.disabled = disabled,
.output_type = 1, // OutputType::SYSTEM
};
/* Chwck if the mode is preferred */
if ((mode.GetRawMode().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;
}
}
auto 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;
}
auto 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.GetName().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 (ConfigId m1 = first_config_id; m1 < last_config_id; m1++) {
for (ConfigId 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.GetVRefresh() -
hwc_configs[m2].mode.GetVRefresh()) < 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.GetName().c_str(),
hwc_configs[m1].mode.GetName().c_str());
hwc_configs[m2].disabled = true;
}
}
}
return true;
}
} // namespace android