| /* |
| * 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 <device_config_shared.h> |
| |
| #include "Common.h" |
| #include "HostUtils.h" |
| |
| namespace aidl::android::hardware::graphics::composer3::impl { |
| namespace { |
| |
| constexpr int32_t HertzToPeriodNanos(uint32_t hertz) { |
| return 1000 * 1000 * 1000 / hertz; |
| } |
| |
| HWC3::Error findCuttlefishDisplays(std::vector<DisplayMultiConfigs>* outDisplays) { |
| DEBUG_LOG("%s", __FUNCTION__); |
| |
| // TODO: replace with initializing directly from DRM info. |
| const auto deviceConfig = cuttlefish::GetDeviceConfig(); |
| |
| int64_t displayId = 0; |
| for (const auto& deviceDisplayConfig : deviceConfig.display_config()) { |
| const auto vsyncPeriodNanos = |
| HertzToPeriodNanos(deviceDisplayConfig.refresh_rate_hz()); |
| |
| DisplayMultiConfigs display = { |
| .displayId = displayId, |
| .activeConfigId = 0, |
| .configs = |
| { |
| DisplayConfig(0, // |
| deviceDisplayConfig.width(), // |
| deviceDisplayConfig.height(), // |
| deviceDisplayConfig.dpi(), // |
| deviceDisplayConfig.dpi(), // |
| vsyncPeriodNanos), |
| }, |
| }; |
| outDisplays->push_back(display); |
| ++displayId; |
| } |
| |
| return HWC3::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<int>(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; |
| } |
| |
| HWC3::Error findGoldfishSecondaryDisplays( |
| std::vector<DisplayMultiConfigs>* outDisplays) { |
| 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 HWC3::Error::None; |
| } |
| |
| const std::vector<std::string> 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 HWC3::Error::BadParameter; |
| } |
| |
| 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__, |
| kExternalDisplayProp, propString.c_str()); |
| return HWC3::Error::BadParameter; |
| } |
| propIntParts.push_back(propIntPart); |
| } |
| |
| 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), |
| drmDisplayConfig.width, |
| drmDisplayConfig.height, |
| drmDisplayConfig.dpiX, |
| 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 (IsInNoOpCompositionMode()) { |
| 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 if (IsCuttlefish()) { |
| error = findCuttlefishDisplays(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 |