/* * 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 #include #include #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(vsyncPeriod); } HWC3::Error findGoldfishPrimaryDisplay(std::vector* 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& 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 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 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* outDisplays) { DEBUG_LOG("%s", __FUNCTION__); std::vector 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* 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* 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* outDisplays) { outDisplays->clear(); std::vector 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(drmDisplayConfig.id), .configs = { DisplayConfig(static_cast(drmDisplayConfig.id), static_cast(drmDisplayConfig.width), static_cast(drmDisplayConfig.height), static_cast(drmDisplayConfig.dpiX), static_cast(drmDisplayConfig.dpiY), HertzToPeriodNanos(drmDisplayConfig.refreshRateHz)), }, }); } return HWC3::Error::None; } } // namespace HWC3::Error findDisplays(const DrmClient* drm, std::vector* 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