blob: d5975b61a651134ff33e9ff6f9f877302394cb09 [file] [log] [blame]
/*
* 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 <android-base/unique_fd.h>
#include <sync/sync.h>
#include <atomic>
#include <numeric>
#include <sstream>
#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<DisplayConfig>& configs,
hwc2_config_t activeConfigId,
const std::optional<std::vector<uint8_t>>& edid) {
std::unique_lock<std::recursive_mutex> 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<std::vector<uint8_t>>& 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<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> lock(mStateMutex);
auto layer = std::make_unique<Layer>();
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<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> 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<HWC2::Attribute>(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<std::recursive_mutex> 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<int32_t>(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<std::recursive_mutex> 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<uint32_t>(
*outNumModes, static_cast<uint32_t>(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<std::recursive_mutex> 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<std::recursive_mutex> 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<uint64_t>(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<std::recursive_mutex> 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<uint64_t>(mId),
static_cast<uint64_t>(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<std::recursive_mutex> 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<int32_t>(request.second);
++numWritten;
}
return HWC2::Error::None;
}
HWC2::Error Display::getType(int32_t* outType) {
DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, mId);
std::unique_lock<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> 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<android_color_mode_t>(intMode);
if (!IsValidColorMode(mode)) {
ALOGE("%s: display:%" PRIu64 " invalid color mode %" PRId32, __FUNCTION__,
mId, intMode);
return HWC2::Error::BadParameter;
}
std::unique_lock<std::recursive_mutex> 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<ColorTransform>(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<std::recursive_mutex> 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<HWC2::PowerMode>(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<std::recursive_mutex> 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<HWC2::Vsync>(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<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> 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<hwc2_vsync_period_t>(
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<std::recursive_mutex> 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<hwc2_layer_t, HWC2::Composition> 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<std::recursive_mutex> 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<std::recursive_mutex> 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<uint32_t>(activeConfig.getAttribute(HWC2::Attribute::Width));
const uint32_t activeConfigHeight =
static_cast<uint32_t>(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<uint8_t> 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<uint32_t>(*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_<mID>
// 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<uint8_t>(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<std::recursive_mutex> 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<HWC2::ContentType>(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