blob: 0dd5922419ad2156e47950e91ae36f651c4555c8 [file] [log] [blame]
/*
* Copyright 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.
*/
#include "DisplayFinder.h"
#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
#include "Common.h"
#include "HostUtils.h"
#include "Time.h"
namespace aidl::android::hardware::graphics::composer3::impl {
namespace {
static uint32_t getVsyncHzFromProperty() {
static constexpr const auto kVsyncProp = "ro.boot.qemu.vsync";
const auto vsyncProp = ::android::base::GetProperty(kVsyncProp, "");
DEBUG_LOG("%s: prop value is: %s", __FUNCTION__, vsyncProp.c_str());
uint64_t vsyncPeriod;
if (!::android::base::ParseUint(vsyncProp, &vsyncPeriod)) {
ALOGE("%s: failed to parse vsync period '%s', returning default 60", __FUNCTION__,
vsyncProp.c_str());
return 60;
}
return static_cast<uint32_t>(vsyncPeriod);
}
HWC3::Error findGoldfishPrimaryDisplay(std::vector<DisplayMultiConfigs>* outDisplays) {
DEBUG_LOG("%s", __FUNCTION__);
DEFINE_AND_VALIDATE_HOST_CONNECTION
hostCon->lock();
const int32_t vsyncPeriodNanos = HertzToPeriodNanos(getVsyncHzFromProperty());
DisplayMultiConfigs display;
display.displayId = 0;
if (rcEnc->hasHWCMultiConfigs()) {
int count = rcEnc->rcGetFBDisplayConfigsCount(rcEnc);
if (count <= 0) {
ALOGE("%s failed to allocate primary display, config count %d", __func__, count);
return HWC3::Error::NoResources;
}
display.activeConfigId = rcEnc->rcGetFBDisplayActiveConfig(rcEnc);
for (int configId = 0; configId < count; configId++) {
display.configs.push_back(DisplayConfig(
configId, //
rcEnc->rcGetFBDisplayConfigsParam(rcEnc, configId, FB_WIDTH), //
rcEnc->rcGetFBDisplayConfigsParam(rcEnc, configId, FB_HEIGHT), //
rcEnc->rcGetFBDisplayConfigsParam(rcEnc, configId, FB_XDPI), //
rcEnc->rcGetFBDisplayConfigsParam(rcEnc, configId, FB_YDPI), //
vsyncPeriodNanos //
));
}
} else {
display.activeConfigId = 0;
display.configs.push_back(DisplayConfig(0, //
rcEnc->rcGetFBParam(rcEnc, FB_WIDTH), //
rcEnc->rcGetFBParam(rcEnc, FB_HEIGHT), //
rcEnc->rcGetFBParam(rcEnc, FB_XDPI), //
rcEnc->rcGetFBParam(rcEnc, FB_YDPI), //
vsyncPeriodNanos //
));
}
hostCon->unlock();
outDisplays->push_back(display);
return HWC3::Error::None;
}
void parseExternalDisplaysFromProperties(std::vector<int>& outPropIntParts) {
static constexpr const char* kExternalDisplayProp[] = {
"hwservicemanager.external.displays",
"ro.boot.qemu.external.displays",
};
for (auto propName : kExternalDisplayProp) {
const std::string propVal = ::android::base::GetProperty(propName, "");
if (propVal.empty()) {
DEBUG_LOG("%s: prop name is: %s, prop value is: empty", __FUNCTION__, propName);
continue;
}
DEBUG_LOG("%s: prop name is: %s, prop value is: %s", __FUNCTION__, propName,
propVal.c_str());
const std::vector<std::string> propStringParts = ::android::base::Split(propVal, ",");
if (propStringParts.size() % 5 != 0) {
ALOGE("%s: Invalid syntax for system prop %s which is %s", __FUNCTION__, propName,
propVal.c_str());
continue;
}
std::vector<int> propIntParts;
for (const std::string& propStringPart : propStringParts) {
int propIntPart;
if (!::android::base::ParseInt(propStringPart, &propIntPart)) {
ALOGE("%s: Invalid syntax for system prop %s which is %s", __FUNCTION__, propName,
propVal.c_str());
break;
}
propIntParts.push_back(propIntPart);
}
if (propIntParts.empty() || propIntParts.size() % 5 != 0) {
continue;
}
outPropIntParts.insert(outPropIntParts.end(), propIntParts.begin(), propIntParts.end());
}
}
HWC3::Error findGoldfishSecondaryDisplays(std::vector<DisplayMultiConfigs>* outDisplays) {
DEBUG_LOG("%s", __FUNCTION__);
std::vector<int> propIntParts;
parseExternalDisplaysFromProperties(propIntParts);
int64_t secondaryDisplayId = 1;
while (!propIntParts.empty()) {
DisplayMultiConfigs display;
display.displayId = secondaryDisplayId;
display.activeConfigId = 0;
display.configs.push_back(DisplayConfig(0, //
/*width=*/propIntParts[1], //
/*heighth=*/propIntParts[2], //
/*dpiXh=*/propIntParts[3], //
/*dpiYh=*/propIntParts[3], //
/*vsyncPeriod=*/HertzToPeriodNanos(160) //
));
outDisplays->push_back(display);
++secondaryDisplayId;
propIntParts.erase(propIntParts.begin(), propIntParts.begin() + 5);
}
return HWC3::Error::None;
}
HWC3::Error findGoldfishDisplays(std::vector<DisplayMultiConfigs>* outDisplays) {
HWC3::Error error = findGoldfishPrimaryDisplay(outDisplays);
if (error != HWC3::Error::None) {
ALOGE("%s failed to find Goldfish primary display", __FUNCTION__);
return error;
}
error = findGoldfishSecondaryDisplays(outDisplays);
if (error != HWC3::Error::None) {
ALOGE("%s failed to find Goldfish secondary displays", __FUNCTION__);
}
return error;
}
// This is currently only used for Gem5 bring-up where virtio-gpu and drm
// are not currently available. For now, just return a placeholder display.
HWC3::Error findNoOpDisplays(std::vector<DisplayMultiConfigs>* outDisplays) {
outDisplays->push_back(DisplayMultiConfigs{
.displayId = 0,
.activeConfigId = 0,
.configs = {DisplayConfig(0,
/*width=*/720, //
/*heighth=*/1280, //
/*dpiXh=*/320, //
/*dpiYh=*/320, //
/*vsyncPeriod=*/HertzToPeriodNanos(30) //
)},
});
return HWC3::Error::None;
}
HWC3::Error findDrmDisplays(const DrmClient& drm, std::vector<DisplayMultiConfigs>* outDisplays) {
outDisplays->clear();
std::vector<DrmClient::DisplayConfig> drmDisplayConfigs;
HWC3::Error error = drm.getDisplayConfigs(&drmDisplayConfigs);
if (error != HWC3::Error::None) {
ALOGE("%s failed to find displays from DRM.", __FUNCTION__);
return error;
}
for (const DrmClient::DisplayConfig drmDisplayConfig : drmDisplayConfigs) {
outDisplays->push_back(DisplayMultiConfigs{
.displayId = drmDisplayConfig.id,
.activeConfigId = static_cast<int32_t>(drmDisplayConfig.id),
.configs =
{
DisplayConfig(static_cast<int32_t>(drmDisplayConfig.id),
static_cast<int32_t>(drmDisplayConfig.width),
static_cast<int32_t>(drmDisplayConfig.height),
static_cast<int32_t>(drmDisplayConfig.dpiX),
static_cast<int32_t>(drmDisplayConfig.dpiY),
HertzToPeriodNanos(drmDisplayConfig.refreshRateHz)),
},
});
}
return HWC3::Error::None;
}
} // namespace
HWC3::Error findDisplays(const DrmClient* drm, std::vector<DisplayMultiConfigs>* outDisplays) {
HWC3::Error error = HWC3::Error::None;
if (IsInGem5DisplayFinderMode() || IsInNoOpDisplayFinderMode()) {
error = findNoOpDisplays(outDisplays);
} else if (IsInDrmDisplayFinderMode()) {
if (drm == nullptr) {
ALOGE("%s asked to find displays from DRM, but DRM not available.", __FUNCTION__);
return HWC3::Error::NoResources;
}
error = findDrmDisplays(*drm, outDisplays);
} else {
error = findGoldfishDisplays(outDisplays);
}
if (error != HWC3::Error::None) {
ALOGE("%s failed to find displays", __FUNCTION__);
return error;
}
for (auto& display : *outDisplays) {
DisplayConfig::addConfigGroups(&display.configs);
}
return HWC3::Error::None;
}
} // namespace aidl::android::hardware::graphics::composer3::impl