1
0
Fork 0
android_external_drm_hwcomp.../hwc/HwcDisplayConfigs.cpp
Andrew Wolfers 9255d0d10b drm_hwcomposer: Migrate to android::drm_hwcomposer namespace
Change-Id: I2859aa8f55532d88231389724956fc77b0625339
2025-09-08 18:53:00 +00:00

180 lines
5.6 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 "compositor/DisplayInfo.h"
#include "drm/DrmConnector.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;
constexpr uint32_t kSyncLen = 10;
constexpr uint32_t kBackPorch = 10;
constexpr uint32_t kFrontPorch = 10;
constexpr uint32_t kHzInKHz = 1000;
namespace android::drm_hwcomposer {
void HwcDisplayConfigs::GenFakeMode(uint16_t width, uint16_t height) {
hwc_configs.clear();
preferred_config_id = active_config_id = next_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),
.output_type = OutputType::kSystem,
};
mm_width = kHeadlessModeDisplayWidthMm;
mm_height = kHeadlessModeDisplayHeightMm;
}
bool HwcDisplayConfigs::Init(DrmConnector &connector) {
// Ensure one config is available for headless mode in case we end up with no
// real modes from the connector.
GenFakeMode(0, 0);
// Probe the connector for modes (IOCTL).
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();
preferred_config_id = 0;
mm_width = connector.GetMmWidth();
mm_height = connector.GetMmHeight();
ConfigId first_config_id = next_config_id;
uint32_t next_group_id = 1;
for (const auto &mode : connector.GetModes()) {
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;
}
const ConfigId new_config_id = next_config_id++;
const uint32_t new_group_id = next_group_id++;
hwc_configs[new_config_id] = {
.id = new_config_id,
.group_id = new_group_id,
.mode = mode,
.disabled = disabled,
.output_type = OutputType::kSystem,
};
if ((mode.GetRawMode().type & DRM_MODE_TYPE_PREFERRED) != 0 &&
preferred_config_id == 0) {
preferred_config_id = new_config_id;
}
}
/* We must have preferred mode. Set first mode as preferred
* in case KMS haven't reported anything. */
if (preferred_config_id == 0 && !hwc_configs.empty()) {
preferred_config_id = first_config_id;
}
return true;
}
bool HwcDisplayConfigs::SanitizeGroups() {
/* A config 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;
for (const auto &[id1, config1] : hwc_configs) {
for (auto &[id2, config2] : hwc_configs) {
if (id1 == id2) {
continue;
}
if (config1.group_id != config2.group_id) {
continue;
}
if (config1.disabled || config2.disabled) {
continue;
}
if (fabsf(config1.mode.GetVRefresh() - config2.mode.GetVRefresh()) >=
kMinFpsDelta) {
continue;
}
ALOGI(
"Group %i: Disabling display mode %s (Refresh rate value is "
"too close to existing mode %s)",
config2.group_id, config2.mode.GetName().c_str(),
config1.mode.GetName().c_str());
config2.disabled = true;
}
}
return true;
}
} // namespace android::drm_hwcomposer