/* * Copyright 2021 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 #include "Common.h" #include "HostUtils.h" namespace android { namespace { constexpr int32_t HertzToPeriodNanos(uint32_t hertz) { return 1000 * 1000 * 1000 / hertz; } HWC2::Error findCuttlefishDisplays(std::vector& displays) { DEBUG_LOG("%s", __FUNCTION__); // TODO: replace with initializing directly from DRM info. const auto deviceConfig = cuttlefish::GetDeviceConfig(); hwc2_display_t displayId = 0; hwc2_config_t configId = 0; for (const auto& deviceDisplayConfig : deviceConfig.display_config()) { DisplayMultiConfigs display = { .displayId = displayId, .activeConfigId = configId, .configs = { DisplayConfig(configId, // deviceDisplayConfig.width(), // deviceDisplayConfig.height(), // deviceDisplayConfig.dpi(), // deviceDisplayConfig.dpi(), // HertzToPeriodNanos( deviceDisplayConfig.refresh_rate_hz()) // ), // }, }; displays.push_back(display); ++configId; ++displayId; } return HWC2::Error::None; } static int 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); } HWC2::Error findGoldfishPrimaryDisplay( std::vector& displays) { 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 HWC2::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(); displays.push_back(display); return HWC2::Error::None; } HWC2::Error findGoldfishSecondaryDisplays( std::vector& displays) { DEBUG_LOG("%s", __FUNCTION__); static constexpr const char kExternalDisplayProp[] = "hwservicemanager.external.displays"; const auto propString = android::base::GetProperty(kExternalDisplayProp, ""); DEBUG_LOG("%s: prop value is: %s", __FUNCTION__, propString.c_str()); if (propString.empty()) { return HWC2::Error::None; } const std::vector propStringParts = android::base::Split(propString, ","); if (propStringParts.size() % 5 != 0) { ALOGE("%s: Invalid syntax for system prop %s which is %s", __FUNCTION__, kExternalDisplayProp, propString.c_str()); return HWC2::Error::BadParameter; } 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__, kExternalDisplayProp, propString.c_str()); return HWC2::Error::BadParameter; } propIntParts.push_back(propIntPart); } hwc2_display_t secondaryDisplayId = 1; hwc2_config_t secondaryConfigId = 1; while (!propIntParts.empty()) { DisplayMultiConfigs display; display.displayId = secondaryDisplayId; display.activeConfigId = 0; display.configs.push_back(DisplayConfig( secondaryConfigId, // /*width=*/propIntParts[1], // /*heighth=*/propIntParts[2], // /*dpiXh=*/propIntParts[3], // /*dpiYh=*/propIntParts[3], // /*vsyncPeriod=*/HertzToPeriodNanos(160) // )); displays.push_back(display); ++secondaryConfigId; ++secondaryDisplayId; propIntParts.erase(propIntParts.begin(), propIntParts.begin() + 5); } return HWC2::Error::None; } HWC2::Error findGoldfishDisplays(std::vector& displays) { HWC2::Error error = findGoldfishPrimaryDisplay(displays); if (error != HWC2::Error::None) { ALOGE("%s failed to find Goldfish primary display", __FUNCTION__); return error; } error = findGoldfishSecondaryDisplays(displays); if (error != HWC2::Error::None) { ALOGE("%s failed to find Goldfish secondary displays", __FUNCTION__); } return error; } } // namespace HWC2::Error findDisplays(std::vector& displays) { HWC2::Error error = HWC2::Error::None; if (IsCuttlefish()) { error = findCuttlefishDisplays(displays); } else { error = findGoldfishDisplays(displays); } if (error != HWC2::Error::None) { ALOGE("%s failed to find displays", __FUNCTION__); return error; } for (auto& display : displays) { DisplayConfig::addConfigGroups(&display.configs); } return HWC2::Error::None; } } // namespace android