/* * 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 "Display.h" #include #include #include #include #include #include "Common.h" #include "Device.h" namespace android { namespace { using android::hardware::graphics::common::V1_0::ColorTransform; bool IsValidColorMode(android_color_mode_t mode) { switch (mode) { case HAL_COLOR_MODE_NATIVE: // Fall-through case HAL_COLOR_MODE_STANDARD_BT601_625: // Fall-through case HAL_COLOR_MODE_STANDARD_BT601_625_UNADJUSTED: // Fall-through case HAL_COLOR_MODE_STANDARD_BT601_525: // Fall-through case HAL_COLOR_MODE_STANDARD_BT601_525_UNADJUSTED: // Fall-through case HAL_COLOR_MODE_STANDARD_BT709: // Fall-through case HAL_COLOR_MODE_DCI_P3: // Fall-through case HAL_COLOR_MODE_SRGB: // Fall-through case HAL_COLOR_MODE_ADOBE_RGB: // Fall-through case HAL_COLOR_MODE_DISPLAY_P3: return true; default: return false; } } bool isValidPowerMode(HWC2::PowerMode mode) { switch (mode) { case HWC2::PowerMode::Off: // Fall-through case HWC2::PowerMode::DozeSuspend: // Fall-through case HWC2::PowerMode::Doze: // Fall-through case HWC2::PowerMode::On: return true; default: return false; } } } // namespace Display::Display(Composer* composer, hwc2_display_t id) : mComposer(composer), mId(id), mVsyncThread(new VsyncThread(id)) {} Display::~Display() {} HWC2::Error Display::init(const std::vector& configs, hwc2_config_t activeConfigId, const std::optional>& edid) { std::unique_lock lock(mStateMutex); for (const DisplayConfig& config : configs) { mConfigs.emplace(config.getId(), config); } mActiveConfigId = activeConfigId; mEdid = edid; auto it = mConfigs.find(activeConfigId); if (it == mConfigs.end()) { ALOGE("%s: display:%" PRIu64 "missing config:%" PRIu32, __FUNCTION__, mId, activeConfigId); return HWC2::Error::NoResources; } const auto& activeConfig = it->second; const auto activeConfigString = activeConfig.toString(); ALOGD("%s initializing display:%" PRIu64 " with config:%s", __FUNCTION__, mId, activeConfigString.c_str()); mVsyncThread->start(activeConfig.getVsyncPeriod()); return HWC2::Error::None; } HWC2::Error Display::updateParameters( uint32_t width, uint32_t height, uint32_t dpiX, uint32_t dpiY, uint32_t refreshRateHz, const std::optional>& edid) { DEBUG_LOG("%s updating display:%" PRIu64 " width:%d height:%d dpiX:%d dpiY:%d refreshRateHz:%d", __FUNCTION__, mId, width, height, dpiX, dpiY, refreshRateHz); std::unique_lock lock(mStateMutex); auto it = mConfigs.find(*mActiveConfigId); if (it == mConfigs.end()) { ALOGE("%s: failed to find config %" PRIu32, __func__, *mActiveConfigId); return HWC2::Error::NoResources; } it->second.setAttribute(HWC2::Attribute::VsyncPeriod, 1000 * 1000 * 1000 / refreshRateHz); it->second.setAttribute(HWC2::Attribute::Width, width); it->second.setAttribute(HWC2::Attribute::Height, height); it->second.setAttribute(HWC2::Attribute::DpiX, dpiX); it->second.setAttribute(HWC2::Attribute::DpiY, dpiY); mEdid = edid; return HWC2::Error::None; } Layer* Display::getLayer(hwc2_layer_t layerId) { auto it = mLayers.find(layerId); if (it == mLayers.end()) { ALOGE("%s Unknown layer:%" PRIu64, __FUNCTION__, layerId); return nullptr; } return it->second.get(); } buffer_handle_t Display::waitAndGetClientTargetBuffer() { DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); base::unique_fd fence = mClientTarget.getFence(); if (fence.ok()) { int err = sync_wait(fence.get(), 3000); if (err < 0 && errno == ETIME) { ALOGE("%s waited on fence %" PRId32 " for 3000 ms", __FUNCTION__, fence.get()); } } return mClientTarget.getBuffer(); } HWC2::Error Display::acceptChanges() { DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); std::unique_lock lock(mStateMutex); if (!mChanges) { ALOGE("%s: display %" PRIu64 " failed, not validated", __FUNCTION__, mId); return HWC2::Error::NotValidated; } for (auto& [layerId, layerCompositionType] : mChanges->getTypeChanges()) { auto* layer = getLayer(layerId); if (layer == nullptr) { ALOGE("%s: display:%" PRIu64 " layer:%" PRIu64 " dropped before AcceptChanges?", __FUNCTION__, mId, layerId); continue; } layer->setCompositionTypeEnum(layerCompositionType); } mChanges->clearTypeChanges(); return HWC2::Error::None; } HWC2::Error Display::createLayer(hwc2_layer_t* outLayerId) { DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); std::unique_lock lock(mStateMutex); auto layer = std::make_unique(); auto layerId = layer->getId(); DEBUG_LOG("%s created layer:%" PRIu64, __FUNCTION__, layerId); *outLayerId = layerId; mLayers.emplace(layerId, std::move(layer)); return HWC2::Error::None; } HWC2::Error Display::destroyLayer(hwc2_layer_t layerId) { DEBUG_LOG("%s destroy layer:%" PRIu64, __FUNCTION__, layerId); std::unique_lock lock(mStateMutex); auto it = mLayers.find(layerId); if (it == mLayers.end()) { ALOGE("%s display:%" PRIu64 " has no such layer:%." PRIu64, __FUNCTION__, mId, layerId); return HWC2::Error::BadLayer; } mOrderedLayers.erase(std::remove_if(mOrderedLayers.begin(), // mOrderedLayers.end(), // [layerId](Layer* layer) { return layer->getId() == layerId; }), mOrderedLayers.end()); mLayers.erase(it); DEBUG_LOG("%s destroyed layer:%" PRIu64, __FUNCTION__, layerId); return HWC2::Error::None; } HWC2::Error Display::getActiveConfig(hwc2_config_t* outConfig) { DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); std::unique_lock lock(mStateMutex); if (!mActiveConfigId) { ALOGW("%s: display:%" PRIu64 " has no active config.", __FUNCTION__, mId); return HWC2::Error::BadConfig; } *outConfig = *mActiveConfigId; return HWC2::Error::None; } HWC2::Error Display::getDisplayAttributeEnum(hwc2_config_t configId, HWC2::Attribute attribute, int32_t* outValue) { auto attributeString = to_string(attribute); DEBUG_LOG("%s: display:%" PRIu64 " attribute:%s", __FUNCTION__, mId, attributeString.c_str()); std::unique_lock lock(mStateMutex); auto it = mConfigs.find(configId); if (it == mConfigs.end()) { ALOGW("%s: display:%" PRIu64 "bad config:%" PRIu32, __FUNCTION__, mId, configId); return HWC2::Error::BadConfig; } const DisplayConfig& config = it->second; *outValue = config.getAttribute(attribute); DEBUG_LOG("%s: display:%" PRIu64 " attribute:%s value is %" PRIi32, __FUNCTION__, mId, attributeString.c_str(), *outValue); return HWC2::Error::None; } HWC2::Error Display::getDisplayAttribute(hwc2_config_t configId, int32_t attribute, int32_t* outValue) { return getDisplayAttributeEnum( configId, static_cast(attribute), outValue); } HWC2::Error Display::getChangedCompositionTypes(uint32_t* outNumElements, hwc2_layer_t* outLayers, int32_t* outTypes) { DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); std::unique_lock lock(mStateMutex); if (!mChanges) { ALOGE("%s: for display:%" PRIu64 " failed, display not validated", __FUNCTION__, mId); return HWC2::Error::NotValidated; } if ((outLayers == nullptr) || (outTypes == nullptr)) { *outNumElements = mChanges->getTypeChanges().size(); return HWC2::Error::None; } uint32_t numWritten = 0; for (const auto& element : mChanges->getTypeChanges()) { if (numWritten == *outNumElements) { break; } auto layerId = element.first; const auto layerCompositionType = element.second; const auto layerCompositionTypeString = to_string(layerCompositionType); DEBUG_LOG("%s: display:%" PRIu64 " layer:%" PRIu64 " changed to %s", __FUNCTION__, mId, layerId, layerCompositionTypeString.c_str()); outLayers[numWritten] = layerId; outTypes[numWritten] = static_cast(layerCompositionType); ++numWritten; } *outNumElements = numWritten; return HWC2::Error::None; } HWC2::Error Display::getColorModes(uint32_t* outNumModes, int32_t* outModes) { DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); std::unique_lock lock(mStateMutex); if (!outModes) { *outNumModes = mColorModes.size(); return HWC2::Error::None; } // we only support HAL_COLOR_MODE_NATIVE so far uint32_t numModes = std::min( *outNumModes, static_cast(mColorModes.size())); std::copy_n(mColorModes.cbegin(), numModes, outModes); *outNumModes = numModes; return HWC2::Error::None; } HWC2::Error Display::getConfigs(uint32_t* outNumConfigs, hwc2_config_t* outConfigs) { DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); std::unique_lock lock(mStateMutex); if (!outConfigs) { *outNumConfigs = mConfigs.size(); return HWC2::Error::None; } uint32_t numWritten = 0; for (const auto& [configId, config] : mConfigs) { if (numWritten == *outNumConfigs) { break; } outConfigs[numWritten] = configId; ++numWritten; } *outNumConfigs = numWritten; return HWC2::Error::None; } HWC2::Error Display::getDozeSupport(int32_t* outSupport) { DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); // We don't support so far *outSupport = 0; return HWC2::Error::None; } HWC2::Error Display::getHdrCapabilities(uint32_t* outNumTypes, int32_t* /*outTypes*/, float* /*outMaxLuminance*/, float* /*outMaxAverageLuminance*/, float* /*outMinLuminance*/) { DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); // We don't support so far *outNumTypes = 0; return HWC2::Error::None; } HWC2::Error Display::getName(uint32_t* outSize, char* outName) { DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); std::unique_lock lock(mStateMutex); if (!outName) { *outSize = mName.size(); return HWC2::Error::None; } auto numCopied = mName.copy(outName, *outSize); *outSize = numCopied; return HWC2::Error::None; } HWC2::Error Display::addReleaseFenceLocked(hwc2_layer_t layerId, base::unique_fd fence) { DEBUG_LOG("%s: display:%" PRIu64 " layer: %" PRIu64 ", fence: %d", __FUNCTION__, mId, static_cast(layerId), fence.get()); mReleaseFences[layerId] = std::move(fence); return HWC2::Error::None; } HWC2::Error Display::getReleaseFences(uint32_t* outNumElements, hwc2_layer_t* outLayers, int32_t* outFences) { DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); std::unique_lock lock(mStateMutex); uint32_t outArraySize = *outNumElements; *outNumElements = 0; for (const auto& [_, releaseFence] : mReleaseFences) { if (releaseFence.ok()) { (*outNumElements)++; } } if ((!outLayers && !outFences) || (outArraySize == 0) || (*outNumElements == 0)) { return HWC2::Error::None; } DEBUG_LOG("%s export release fences", __FUNCTION__); uint32_t index = 0; for (const auto& [layer_id, releaseFence] : mReleaseFences) { if (index >= outArraySize) { break; } if (!releaseFence.ok()) { continue; } if (outLayers) { outLayers[index] = layer_id; } if (outFences) { int outFence = dup(releaseFence.get()); if (outFence < 0) { ALOGE("%s: Fail to dup release fence for display id = %" PRIu64 ", layer id = %" PRIu64 ", fence = %d, error(%d): %s", __FUNCTION__, static_cast(mId), static_cast(layer_id), releaseFence.get(), errno, strerror(errno)); } outFences[index] = outFence; } index++; } return HWC2::Error::None; } HWC2::Error Display::clearReleaseFencesAndIdsLocked() { DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); mReleaseFences.clear(); return HWC2::Error::None; } HWC2::Error Display::getRequests(int32_t* outDisplayRequests, uint32_t* outNumElements, hwc2_layer_t* outLayers, int32_t* outLayerRequests) { DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); std::unique_lock lock(mStateMutex); if (!mChanges) { return HWC2::Error::NotValidated; } if (outLayers == nullptr || outLayerRequests == nullptr) { *outNumElements = mChanges->getNumLayerRequests(); return HWC2::Error::None; } // TODO // Display requests (HWC2::DisplayRequest) are not supported so far: *outDisplayRequests = 0; uint32_t numWritten = 0; for (const auto& request : mChanges->getLayerRequests()) { if (numWritten == *outNumElements) { break; } outLayers[numWritten] = request.first; outLayerRequests[numWritten] = static_cast(request.second); ++numWritten; } return HWC2::Error::None; } HWC2::Error Display::getType(int32_t* outType) { DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); std::unique_lock lock(mStateMutex); *outType = (int32_t)mType; return HWC2::Error::None; } HWC2::Error Display::present(int32_t* outRetireFencePtr) { ATRACE_CALL(); DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); *outRetireFencePtr = -1; std::unique_lock lock(mStateMutex); if (!mChanges || (mChanges->getNumTypes() > 0)) { ALOGE("%s: display:%" PRIu64 " failed, not validated", __FUNCTION__, mId); return HWC2::Error::NotValidated; } mChanges.reset(); if (mComposer == nullptr) { ALOGE("%s: display:%" PRIu64 " missing composer", __FUNCTION__, mId); return HWC2::Error::NoResources; } HWC2::Error error; base::unique_fd outRetireFence; std::tie(error, outRetireFence) = mComposer->presentDisplay(this); if (error != HWC2::Error::None) { ALOGE("%s: display:%" PRIu64 " failed to present", __FUNCTION__, mId); return error; } DEBUG_LOG("%s: display:%" PRIu64 " present done!", __FUNCTION__, mId); *outRetireFencePtr = outRetireFence.release(); return HWC2::Error::None; } HWC2::Error Display::setActiveConfig(hwc2_config_t configId) { DEBUG_LOG("%s: display:%" PRIu64 " setting active config to %" PRIu32, __FUNCTION__, mId, configId); hwc_vsync_period_change_constraints_t constraints; constraints.desiredTimeNanos = 0; constraints.seamlessRequired = false; hwc_vsync_period_change_timeline_t timeline; return setActiveConfigWithConstraints(configId, &constraints, &timeline); } HWC2::Error Display::setClientTarget(buffer_handle_t target, int32_t acquireFence, int32_t /*dataspace*/, hwc_region_t /*damage*/) { DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); std::unique_lock lock(mStateMutex); mClientTarget.setBuffer(target); mClientTarget.setFence(base::unique_fd(acquireFence)); mComposer->onDisplayClientTargetSet(this); return HWC2::Error::None; } HWC2::Error Display::setColorMode(int32_t intMode) { DEBUG_LOG("%s: display:%" PRIu64 " setting color mode to %" PRId32, __FUNCTION__, mId, intMode); auto mode = static_cast(intMode); if (!IsValidColorMode(mode)) { ALOGE("%s: display:%" PRIu64 " invalid color mode %" PRId32, __FUNCTION__, mId, intMode); return HWC2::Error::BadParameter; } std::unique_lock lock(mStateMutex); if (mColorModes.count(mode) == 0) { ALOGE("%s: display %" PRIu64 " mode %d not found", __FUNCTION__, mId, intMode); return HWC2::Error::Unsupported; } mActiveColorMode = mode; return HWC2::Error::None; } HWC2::Error Display::setColorTransform(const float* transformMatrix, int transformTypeRaw) { const auto transformType = static_cast(transformTypeRaw); return setColorTransformEnum(transformMatrix, transformType); } HWC2::Error Display::setColorTransformEnum(const float* transformMatrix, ColorTransform transformType) { const auto transformTypeString = toString(transformType); DEBUG_LOG("%s: display:%" PRIu64 " color transform type %s", __FUNCTION__, mId, transformTypeString.c_str()); if (transformType == ColorTransform::ARBITRARY_MATRIX && transformMatrix == nullptr) { return HWC2::Error::BadParameter; } std::unique_lock lock(mStateMutex); if (transformType == ColorTransform::IDENTITY) { mColorTransform.reset(); } else { ColorTransformWithMatrix& colorTransform = mColorTransform.emplace(); colorTransform.transformType = transformType; if (transformType == ColorTransform::ARBITRARY_MATRIX) { auto& colorTransformMatrix = colorTransform.transformMatrixOpt.emplace(); std::copy_n(transformMatrix, colorTransformMatrix.size(), colorTransformMatrix.begin()); } } return HWC2::Error::None; } HWC2::Error Display::setOutputBuffer(buffer_handle_t /*buffer*/, int32_t /*releaseFence*/) { DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); // TODO: for virtual display return HWC2::Error::None; } HWC2::Error Display::setPowerMode(int32_t intMode) { auto mode = static_cast(intMode); auto modeString = to_string(mode); DEBUG_LOG("%s: display:%" PRIu64 " setting power mode to %s", __FUNCTION__, mId, modeString.c_str()); if (!isValidPowerMode(mode)) { return HWC2::Error::BadParameter; } if (mode == HWC2::PowerMode::Doze || mode == HWC2::PowerMode::DozeSuspend) { ALOGE("%s display %" PRIu64 " power mode %s not supported", __FUNCTION__, mId, modeString.c_str()); return HWC2::Error::Unsupported; } std::unique_lock lock(mStateMutex); if (IsCuttlefish()) { if (int fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC); fd != -1) { std::ostringstream stream; stream << "VIRTUAL_DEVICE_DISPLAY_POWER_MODE_CHANGED display=" << mId << " mode=" << modeString; std::string message = stream.str(); write(fd, message.c_str(), message.length()); close(fd); } } mPowerMode = mode; return HWC2::Error::None; } HWC2::Error Display::setVsyncEnabled(int32_t intEnable) { auto enable = static_cast(intEnable); auto enableString = to_string(enable); DEBUG_LOG("%s: display:%" PRIu64 " setting vsync to %s", __FUNCTION__, mId, enableString.c_str()); if (enable == HWC2::Vsync::Invalid) { return HWC2::Error::BadParameter; } std::unique_lock lock(mStateMutex); return mVsyncThread->setVsyncEnabled(enable == HWC2::Vsync::Enable); } HWC2::Error Display::setVsyncCallback(HWC2_PFN_VSYNC callback, hwc2_callback_data_t data) { DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); std::unique_lock lock(mStateMutex); return mVsyncThread->setVsyncCallback(callback, data); } HWC2::Error Display::setVsync24Callback(HWC2_PFN_VSYNC_2_4 callback, hwc2_callback_data_t data) { DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); std::unique_lock lock(mStateMutex); return mVsyncThread->setVsync24Callback(callback, data); } HWC2::Error Display::getDisplayVsyncPeriod( hwc2_vsync_period_t* outVsyncPeriod) { DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); std::unique_lock lock(mStateMutex); if (!mActiveConfigId) { ALOGE("%s : display:%" PRIu64 " no active config", __FUNCTION__, mId); return HWC2::Error::BadConfig; } const auto it = mConfigs.find(*mActiveConfigId); if (it == mConfigs.end()) { ALOGE("%s : display:%" PRIu64 " failed to find active config:%" PRIu32, __FUNCTION__, mId, *mActiveConfigId); return HWC2::Error::BadConfig; } const DisplayConfig& activeConfig = it->second; *outVsyncPeriod = static_cast( activeConfig.getAttribute(HWC2::Attribute::VsyncPeriod)); return HWC2::Error::None; } HWC2::Error Display::validate(uint32_t* outNumTypes, uint32_t* outNumRequests) { ATRACE_CALL(); DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); std::unique_lock lock(mStateMutex); mOrderedLayers.clear(); mOrderedLayers.reserve(mLayers.size()); for (auto& [_, layerPtr] : mLayers) { mOrderedLayers.push_back(layerPtr.get()); } std::sort(mOrderedLayers.begin(), mOrderedLayers.end(), [](const Layer* layerA, const Layer* layerB) { const auto zA = layerA->getZ(); const auto zB = layerB->getZ(); if (zA != zB) { return zA < zB; } return layerA->getId() < layerB->getId(); }); if (!mChanges) { mChanges.reset(new Changes); } else { ALOGE("Validate was called more than once!"); } if (mComposer == nullptr) { ALOGE("%s: display:%" PRIu64 " missing composer", __FUNCTION__, mId); return HWC2::Error::NoResources; } std::unordered_map changes; HWC2::Error error = mComposer->validateDisplay(this, &changes); if (error != HWC2::Error::None) { ALOGE("%s: display:%" PRIu64 " failed to validate", __FUNCTION__, mId); return error; } for (const auto& [layerId, changedCompositionType] : changes) { mChanges->addTypeChange(layerId, changedCompositionType); } *outNumTypes = mChanges->getNumTypes(); *outNumRequests = mChanges->getNumLayerRequests(); return *outNumTypes > 0 ? HWC2::Error::HasChanges : HWC2::Error::None; } HWC2::Error Display::updateLayerZ(hwc2_layer_t layerId, uint32_t z) { DEBUG_LOG("%s: display:%" PRIu64 " update layer:%" PRIu64 " z:%d", __FUNCTION__, mId, layerId, z); std::unique_lock lock(mStateMutex); const auto layerIt = mLayers.find(layerId); if (layerIt == mLayers.end()) { ALOGE("%s failed to find layer %" PRIu64, __FUNCTION__, layerId); return HWC2::Error::BadLayer; } auto& layer = layerIt->second; layer->setZ(z); return HWC2::Error::None; } HWC2::Error Display::getClientTargetSupport(uint32_t width, uint32_t height, int32_t format, int32_t dataspace) { DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); std::unique_lock lock(mStateMutex); if (!mActiveConfigId) { return HWC2::Error::Unsupported; } const auto it = mConfigs.find(*mActiveConfigId); if (it == mConfigs.end()) { ALOGE("%s failed to find active config:%" PRIu32, __FUNCTION__, *mActiveConfigId); return HWC2::Error::Unsupported; } const DisplayConfig& activeConfig = it->second; const uint32_t activeConfigWidth = static_cast(activeConfig.getAttribute(HWC2::Attribute::Width)); const uint32_t activeConfigHeight = static_cast(activeConfig.getAttribute(HWC2::Attribute::Height)); if (width == activeConfigWidth && height == activeConfigHeight && format == HAL_PIXEL_FORMAT_RGBA_8888 && dataspace == HAL_DATASPACE_UNKNOWN) { return HWC2::Error::None; } return HWC2::Error::None; } // thess EDIDs are carefully generated according to the EDID spec version 1.3, // more info can be found from the following file: // frameworks/native/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp // approved pnp ids can be found here: https://uefi.org/pnp_id_list // pnp id: GGL, name: EMU_display_0, last byte is checksum // display id is local:8141603649153536 static const uint8_t sEDID0[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1c, 0xec, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1b, 0x10, 0x01, 0x03, 0x80, 0x50, 0x2d, 0x78, 0x0a, 0x0d, 0xc9, 0xa0, 0x57, 0x47, 0x98, 0x27, 0x12, 0x48, 0x4c, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x45, 0x4d, 0x55, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x30, 0x00, 0x4b}; // pnp id: GGL, name: EMU_display_1 // display id is local:8140900251843329 static const uint8_t sEDID1[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1c, 0xec, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1b, 0x10, 0x01, 0x03, 0x80, 0x50, 0x2d, 0x78, 0x0a, 0x0d, 0xc9, 0xa0, 0x57, 0x47, 0x98, 0x27, 0x12, 0x48, 0x4c, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x45, 0x4d, 0x55, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x31, 0x00, 0x3b}; // pnp id: GGL, name: EMU_display_2 // display id is local:8140940453066754 static const uint8_t sEDID2[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1c, 0xec, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1b, 0x10, 0x01, 0x03, 0x80, 0x50, 0x2d, 0x78, 0x0a, 0x0d, 0xc9, 0xa0, 0x57, 0x47, 0x98, 0x27, 0x12, 0x48, 0x4c, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x45, 0x4d, 0x55, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x32, 0x00, 0x49}; #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) HWC2::Error Display::setEdid(std::vector edid) { DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); mEdid = edid; return HWC2::Error::None; } HWC2::Error Display::getDisplayIdentificationData(uint8_t* outPort, uint32_t* outDataSize, uint8_t* outData) { DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); if (outPort == nullptr || outDataSize == nullptr) { return HWC2::Error::BadParameter; } if (mEdid) { if (outData) { *outDataSize = std::min(*outDataSize, (*mEdid).size()); memcpy(outData, (*mEdid).data(), *outDataSize); } else { *outDataSize = (*mEdid).size(); } *outPort = mId; return HWC2::Error::None; } // fallback to legacy EDID implementation uint32_t len = std::min(*outDataSize, (uint32_t)ARRAY_SIZE(sEDID0)); if (outData != nullptr && len < (uint32_t)ARRAY_SIZE(sEDID0)) { ALOGW("%s: display:%" PRIu64 " small buffer size: %u is specified", __FUNCTION__, mId, len); } *outDataSize = ARRAY_SIZE(sEDID0); switch (mId) { case 0: *outPort = 0; if (outData) memcpy(outData, sEDID0, len); break; case 1: *outPort = 1; if (outData) memcpy(outData, sEDID1, len); break; case 2: *outPort = 2; if (outData) memcpy(outData, sEDID2, len); break; default: *outPort = (uint8_t)mId; if (outData) { memcpy(outData, sEDID2, len); uint32_t size = ARRAY_SIZE(sEDID0); // change the name to EMU_display_ // note the 3rd char from back is the number, _0, _1, _2, etc. if (len >= size - 2) outData[size - 3] = '0' + (uint8_t)mId; if (len >= size) { // update the last byte, which is checksum byte uint8_t checksum = -(uint8_t)std::accumulate( outData, outData + size - 1, static_cast(0)); outData[size - 1] = checksum; } } break; } return HWC2::Error::None; } HWC2::Error Display::getDisplayCapabilities(uint32_t* outNumCapabilities, uint32_t* outCapabilities) { DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); if (outNumCapabilities == nullptr) { return HWC2::Error::None; } bool brightness_support = false; bool doze_support = false; uint32_t count = 1 + (doze_support ? 1 : 0) + (brightness_support ? 1 : 0); int index = 0; if (outCapabilities != nullptr && (*outNumCapabilities >= count)) { outCapabilities[index++] = HWC2_DISPLAY_CAPABILITY_SKIP_CLIENT_COLOR_TRANSFORM; if (doze_support) { outCapabilities[index++] = HWC2_DISPLAY_CAPABILITY_DOZE; } if (brightness_support) { outCapabilities[index++] = HWC2_DISPLAY_CAPABILITY_BRIGHTNESS; } } *outNumCapabilities = count; return HWC2::Error::None; } HWC2::Error Display::getDisplayBrightnessSupport(bool* out_support) { DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); *out_support = false; return HWC2::Error::None; } HWC2::Error Display::setDisplayBrightness(float brightness) { DEBUG_LOG("%s: display:%" PRIu64 " brightness %f", __FUNCTION__, mId, brightness); ALOGW("TODO: setDisplayBrightness() is not implemented yet: brightness=%f", brightness); return HWC2::Error::Unsupported; } HWC2::Error Display::setActiveConfigWithConstraints( hwc2_config_t configId, hwc_vsync_period_change_constraints_t* vsyncPeriodChangeConstraints, hwc_vsync_period_change_timeline_t* outTimeline) { DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); std::unique_lock lock(mStateMutex); if (vsyncPeriodChangeConstraints == nullptr || outTimeline == nullptr) { return HWC2::Error::BadParameter; } if (mConfigs.find(configId) == mConfigs.end()) { ALOGE("%s: display:%" PRIu64 " bad config:%" PRIu32, __FUNCTION__, mId, configId); return HWC2::Error::BadConfig; } if (mActiveConfigId == configId) { return HWC2::Error::None; } mActiveConfigId = configId; if (mComposer == nullptr) { ALOGE("%s: display:%" PRIu64 " missing composer", __FUNCTION__, mId); return HWC2::Error::NoResources; } HWC2::Error error = mComposer->onActiveConfigChange(this); if (error != HWC2::Error::None) { ALOGE("%s: display:%" PRIu64 " composer failed to handle config change", __FUNCTION__, mId); return error; } hwc2_vsync_period_t vsyncPeriod; error = getDisplayVsyncPeriod(&vsyncPeriod); if (error != HWC2::Error::None) { ALOGE("%s: display:%" PRIu64 " composer failed to handle config change", __FUNCTION__, mId); return error; } return mVsyncThread->scheduleVsyncUpdate( vsyncPeriod, vsyncPeriodChangeConstraints, outTimeline); } HWC2::Error Display::getDisplayConnectionType(uint32_t* outType) { if (IsCuttlefishFoldable()) { // Workaround to force all displays to INTERNAL for cf_x86_64_foldable. // TODO(b/193568008): Allow configuring internal/external per display. *outType = HWC2_DISPLAY_CONNECTION_TYPE_INTERNAL; } else { // Other devices default to the first display INTERNAL, others EXTERNAL. *outType = mId == 0 ? HWC2_DISPLAY_CONNECTION_TYPE_INTERNAL : HWC2_DISPLAY_CONNECTION_TYPE_EXTERNAL; } return HWC2::Error::None; } HWC2::Error Display::setAutoLowLatencyMode(bool /*on*/) { DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); return HWC2::Error::Unsupported; } HWC2::Error Display::getSupportedContentTypes( uint32_t* outNumSupportedContentTypes, const uint32_t* /*outSupportedContentTypes*/) { DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId); if (outNumSupportedContentTypes != nullptr) { *outNumSupportedContentTypes = 0; } return HWC2::Error::None; } HWC2::Error Display::setContentType(int32_t contentTypeRaw) { auto contentType = static_cast(contentTypeRaw); auto contentTypeString = to_string(contentType); DEBUG_LOG("%s: display:%" PRIu64 " content type:%s", __FUNCTION__, mId, contentTypeString.c_str()); if (contentType != HWC2::ContentType::None) { return HWC2::Error::Unsupported; } return HWC2::Error::None; } } // namespace android