blob: 92b6c0a2dedd5eb40e4f8daeb2e8b84661ed80a9 [file] [log] [blame]
/*
* Copyright (C) 2012 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.
*/
#define ATRACE_TAG (ATRACE_TAG_GRAPHICS | ATRACE_TAG_HAL)
//#include <linux/fb.h>
#include "ExynosDisplay.h"
#include <cutils/properties.h>
#include <hardware/hwcomposer_defs.h>
#include <linux/fb.h>
#include <sync/sync.h>
#include <sys/ioctl.h>
#include <utils/CallStack.h>
#include <map>
#include "ExynosExternalDisplay.h"
#include "ExynosHWCHelper.h"
#include "ExynosLayer.h"
#include "exynos_format.h"
/**
* ExynosDisplay implementation
*/
using namespace android;
extern struct exynos_hwc_control exynosHWCControl;
extern struct update_time_info updateTimeInfo;
int ExynosSortedLayer::compare(ExynosLayer * const *lhs, ExynosLayer *const *rhs)
{
ExynosLayer *left = *((ExynosLayer**)(lhs));
ExynosLayer *right = *((ExynosLayer**)(rhs));
return left->mZOrder > right->mZOrder;
}
ssize_t ExynosSortedLayer::remove(const ExynosLayer *item)
{
for (size_t i = 0; i < size(); i++)
{
if (array()[i] == item)
{
removeAt(i);
return i;
}
}
return -1;
}
status_t ExynosSortedLayer::vector_sort()
{
return sort(compare);
}
ExynosLowFpsLayerInfo::ExynosLowFpsLayerInfo()
: mHasLowFpsLayer(false),
mFirstIndex(-1),
mLastIndex(-1)
{
}
void ExynosLowFpsLayerInfo::initializeInfos()
{
mHasLowFpsLayer = false;
mFirstIndex = -1;
mLastIndex = -1;
}
int32_t ExynosLowFpsLayerInfo::addLowFpsLayer(uint32_t layerIndex)
{
if (mHasLowFpsLayer == false) {
mFirstIndex = layerIndex;
mLastIndex = layerIndex;
mHasLowFpsLayer = true;
} else {
mFirstIndex = min(mFirstIndex, (int32_t)layerIndex);
mLastIndex = max(mLastIndex, (int32_t)layerIndex);
}
return NO_ERROR;
}
ExynosCompositionInfo::ExynosCompositionInfo(uint32_t type)
: ExynosMPPSource(MPP_SOURCE_COMPOSITION_TARGET, this),
mType(type),
mHasCompositionLayer(false),
mFirstIndex(-1),
mLastIndex(-1),
mTargetBuffer(NULL),
mDataSpace(HAL_DATASPACE_UNKNOWN),
mAcquireFence(-1),
mReleaseFence(-1),
mEnableSkipStatic(false),
mSkipStaticInitFlag(false),
mSkipFlag(false),
mWindowIndex(-1)
{
/* If AFBC compression of mTargetBuffer is changed, */
/* mCompressed should be set properly before resource assigning */
char value[256];
int afbc_prop;
property_get("ro.vendor.ddk.set.afbc", value, "0");
afbc_prop = atoi(value);
if (afbc_prop == 0)
mCompressed = false;
else
mCompressed = true;
memset(&mSkipSrcInfo, 0, sizeof(mSkipSrcInfo));
for (int i = 0; i < NUM_SKIP_STATIC_LAYER; i++) {
mSkipSrcInfo.srcInfo[i].acquireFenceFd = -1;
mSkipSrcInfo.srcInfo[i].releaseFenceFd = -1;
mSkipSrcInfo.dstInfo[i].acquireFenceFd = -1;
mSkipSrcInfo.dstInfo[i].releaseFenceFd = -1;
}
if(type == COMPOSITION_CLIENT)
mEnableSkipStatic = true;
memset(&mLastWinConfigData, 0x0, sizeof(mLastWinConfigData));
mLastWinConfigData.acq_fence = -1;
mLastWinConfigData.rel_fence = -1;
}
void ExynosCompositionInfo::initializeInfos(ExynosDisplay *display)
{
mHasCompositionLayer = false;
mFirstIndex = -1;
mLastIndex = -1;
if (mType != COMPOSITION_CLIENT) {
mTargetBuffer = NULL;
mDataSpace = HAL_DATASPACE_UNKNOWN;
if (mAcquireFence >= 0) {
ALOGD("ExynosCompositionInfo(%d):: mAcquire is not initialized(%d)", mType, mAcquireFence);
if (display != NULL)
fence_close(mAcquireFence, display, FENCE_TYPE_UNDEFINED, FENCE_IP_UNDEFINED);
}
mAcquireFence = -1;
}
if (mReleaseFence >= 0) {
ALOGD("ExynosCompositionInfo(%d):: mReleaseFence is not initialized(%d)", mType, mReleaseFence);
if (display!= NULL)
fence_close(mReleaseFence, display, FENCE_TYPE_UNDEFINED, FENCE_IP_UNDEFINED);
}
mReleaseFence = -1;
mWindowIndex = -1;
mOtfMPP = NULL;
mM2mMPP = NULL;
if ((display != NULL) &&
(display->mType == HWC_DISPLAY_VIRTUAL) &&
(mType == COMPOSITION_EXYNOS)) {
mM2mMPP = display->mResourceManager->getExynosMPP(MPP_LOGICAL_G2D_COMBO);
}
}
void ExynosCompositionInfo::setTargetBuffer(ExynosDisplay *display, private_handle_t *handle,
int32_t acquireFence, android_dataspace dataspace)
{
mTargetBuffer = handle;
if (mType == COMPOSITION_CLIENT) {
if (display != NULL)
mAcquireFence = hwcCheckFenceDebug(display, FENCE_TYPE_DST_ACQUIRE, FENCE_IP_FB, acquireFence);
} else {
if (display != NULL)
mAcquireFence = hwcCheckFenceDebug(display, FENCE_TYPE_DST_ACQUIRE, FENCE_IP_G2D, acquireFence);
}
if ((display != NULL) && (mDataSpace != dataspace))
display->setGeometryChanged(GEOMETRY_DISPLAY_DATASPACE_CHANGED);
mDataSpace = dataspace;
}
void ExynosCompositionInfo::setCompressed(bool compressed)
{
mCompressed = compressed;
}
bool ExynosCompositionInfo::getCompressed()
{
return mCompressed;
}
void ExynosCompositionInfo::dump(String8& result)
{
result.appendFormat("CompositionInfo (%d)\n", mType);
result.appendFormat("mHasCompositionLayer(%d)\n", mHasCompositionLayer);
if (mHasCompositionLayer) {
result.appendFormat("\tfirstIndex: %d, lastIndex: %d, dataSpace: 0x%8x, compressed: %d, windowIndex: %d\n",
mFirstIndex, mLastIndex, mDataSpace, mCompressed, mWindowIndex);
result.appendFormat("\thandle: %p, acquireFence: %d, releaseFence: %d, skipFlag: %d",
mTargetBuffer, mAcquireFence, mReleaseFence, mSkipFlag);
if ((mOtfMPP == NULL) && (mM2mMPP == NULL))
result.appendFormat("\tresource is not assigned\n");
if (mOtfMPP != NULL)
result.appendFormat("\tassignedMPP: %s\n", mOtfMPP->mName.string());
if (mM2mMPP != NULL)
result.appendFormat("\t%s\n", mM2mMPP->mName.string());
}
if (mTargetBuffer != NULL) {
uint64_t internal_format = 0;
internal_format = mTargetBuffer->internal_format;
result.appendFormat("\tinternal_format: 0x%" PRIx64 ", afbc: %d\n", internal_format,
isAFBCCompressed(mTargetBuffer));
}
uint32_t assignedSrcNum = 0;
if ((mM2mMPP != NULL) &&
((assignedSrcNum = mM2mMPP->mAssignedSources.size()) > 0)) {
result.appendFormat("\tAssigned source num: %d\n", assignedSrcNum);
result.append("\t");
for (uint32_t i = 0; i < assignedSrcNum; i++) {
if (mM2mMPP->mAssignedSources[i]->mSourceType == MPP_SOURCE_LAYER) {
ExynosLayer* layer = (ExynosLayer*)(mM2mMPP->mAssignedSources[i]);
result.appendFormat("[%d]layer_%p ", i, layer->mLayerBuffer);
} else {
result.appendFormat("[%d]sourceType_%d ", i, mM2mMPP->mAssignedSources[i]->mSourceType);
}
}
result.append("\n");
}
result.append("\n");
}
String8 ExynosCompositionInfo::getTypeStr()
{
switch(mType) {
case COMPOSITION_NONE:
return String8("COMPOSITION_NONE");
case COMPOSITION_CLIENT:
return String8("COMPOSITION_CLIENT");
case COMPOSITION_EXYNOS:
return String8("COMPOSITION_EXYNOS");
default:
return String8("InvalidType");
}
}
ExynosDisplay::ExynosDisplay(uint32_t type, ExynosDevice *device)
: mType(type),
mXres(1440),
mYres(2960),
mXdpi(25400),
mYdpi(25400),
mVsyncPeriod(16666666),
mDevice(device),
mDisplayId(HWC_DISPLAY_PRIMARY),
mDisplayName(android::String8("PrimaryDisplay")),
mPlugState(false),
mHasSingleBuffer(false),
mResourceManager(NULL),
mClientCompositionInfo(COMPOSITION_CLIENT),
mExynosCompositionInfo(COMPOSITION_EXYNOS),
mGeometryChanged(0x0),
mRenderingState(RENDERING_STATE_NONE),
mHWCRenderingState(RENDERING_STATE_NONE),
mDisplayBW(0),
mDynamicReCompMode(NO_MODE_SWITCH),
mDREnable(false),
mDRDefault(false),
mLastFpsTime(0),
mFrameCount(0),
mLastFrameCount(0),
mErrorFrameCount(0),
mUpdateEventCnt(0),
mUpdateCallCnt(0),
mDefaultDMA(MAX_DECON_DMA_TYPE),
mLastRetireFence(-1),
mWindowNumUsed(0),
mBaseWindowIndex(0),
mNumMaxPriorityAllowed(1),
mCursorIndex(-1),
mColorTransformHint(HAL_COLOR_TRANSFORM_IDENTITY),
mMaxLuminance(0),
mMaxAverageLuminance(0),
mMinLuminance(0),
mHWC1LayerList(NULL),
/* Support DDI scalser */
mOldScalerMode(0),
mNewScaledWidth(0),
mNewScaledHeight(0),
mDeviceXres(0),
mDeviceYres(0),
mColorMode(HAL_COLOR_MODE_NATIVE),
mSkipFrame(false),
mBrightnessFd(NULL),
mMaxBrightness(0)
{
mDisplayControl.enableCompositionCrop = true;
mDisplayControl.enableExynosCompositionOptimization = true;
mDisplayControl.enableClientCompositionOptimization = true;
mDisplayControl.useMaxG2DSrc = false;
mDisplayControl.handleLowFpsLayers = false;
if (mType == HWC_DISPLAY_VIRTUAL)
mDisplayControl.earlyStartMPP = false;
else
mDisplayControl.earlyStartMPP = true;
mDisplayControl.adjustDisplayFrame = false;
mDisplayControl.cursorSupport = false;
mDisplayConfigs.clear();
mPowerModeState = HWC2_POWER_MODE_OFF;
mVsyncState = HWC2_VSYNC_DISABLE;
/* TODO : Exception handling here */
if (device == NULL) {
ALOGE("Display creation failed!");
return;
}
mResourceManager = device->mResourceManager;
/* The number of window is same with the number of otfMPP */
mMaxWindowNum = mResourceManager->getOtfMPPs().size();
mDpuData.init(mMaxWindowNum);
mLastDpuData.init(mMaxWindowNum);
ALOGI("window configs size(%zu)", mDpuData.configs.size());
mLowFpsLayerInfo.initializeInfos();
mUseDpu = true;
return;
}
ExynosDisplay::~ExynosDisplay()
{
}
/**
* Member function for Dynamic AFBC Control solution.
*/
bool ExynosDisplay::comparePreferedLayers() {
return false;
}
int ExynosDisplay::getDisplayId() {
return mDisplayId;
}
void ExynosDisplay::initDisplay() {
mClientCompositionInfo.initializeInfos(this);
mClientCompositionInfo.mEnableSkipStatic = true;
mClientCompositionInfo.mSkipStaticInitFlag = false;
mClientCompositionInfo.mSkipFlag = false;
memset(&mClientCompositionInfo.mSkipSrcInfo, 0x0, sizeof(mClientCompositionInfo.mSkipSrcInfo));
for (int i = 0; i < NUM_SKIP_STATIC_LAYER; i++) {
mClientCompositionInfo.mSkipSrcInfo.srcInfo[i].acquireFenceFd = -1;
mClientCompositionInfo.mSkipSrcInfo.srcInfo[i].releaseFenceFd = -1;
mClientCompositionInfo.mSkipSrcInfo.dstInfo[i].acquireFenceFd = -1;
mClientCompositionInfo.mSkipSrcInfo.dstInfo[i].releaseFenceFd = -1;
}
memset(&mClientCompositionInfo.mLastWinConfigData, 0x0, sizeof(mClientCompositionInfo.mLastWinConfigData));
mClientCompositionInfo.mLastWinConfigData.acq_fence = -1;
mClientCompositionInfo.mLastWinConfigData.rel_fence = -1;
mExynosCompositionInfo.initializeInfos(this);
mExynosCompositionInfo.mEnableSkipStatic = false;
mExynosCompositionInfo.mSkipStaticInitFlag = false;
mExynosCompositionInfo.mSkipFlag = false;
memset(&mExynosCompositionInfo.mSkipSrcInfo, 0x0, sizeof(mExynosCompositionInfo.mSkipSrcInfo));
for (int i = 0; i < NUM_SKIP_STATIC_LAYER; i++) {
mExynosCompositionInfo.mSkipSrcInfo.srcInfo[i].acquireFenceFd = -1;
mExynosCompositionInfo.mSkipSrcInfo.srcInfo[i].releaseFenceFd = -1;
mExynosCompositionInfo.mSkipSrcInfo.dstInfo[i].acquireFenceFd = -1;
mExynosCompositionInfo.mSkipSrcInfo.dstInfo[i].releaseFenceFd = -1;
}
memset(&mExynosCompositionInfo.mLastWinConfigData, 0x0, sizeof(mExynosCompositionInfo.mLastWinConfigData));
mExynosCompositionInfo.mLastWinConfigData.acq_fence = -1;
mExynosCompositionInfo.mLastWinConfigData.rel_fence = -1;
mGeometryChanged = 0x0;
mRenderingState = RENDERING_STATE_NONE;
mDisplayBW = 0;
mDynamicReCompMode = NO_MODE_SWITCH;
mCursorIndex = -1;
mDpuData.reset();
mLastDpuData.reset();
if (mDisplayControl.earlyStartMPP == true) {
for (size_t i = 0; i < mLayers.size(); i++) {
exynos_image outImage;
ExynosMPP* m2mMPP = mLayers[i]->mM2mMPP;
/* Close release fence of dst buffer of last frame */
if ((mLayers[i]->mValidateCompositionType == HWC2_COMPOSITION_DEVICE) &&
(m2mMPP != NULL) &&
(m2mMPP->mAssignedDisplay == this) &&
(m2mMPP->getDstImageInfo(&outImage) == NO_ERROR)) {
if (m2mMPP->mPhysicalType == MPP_MSC) {
fence_close(outImage.releaseFenceFd, this, FENCE_TYPE_DST_RELEASE, FENCE_IP_MSC);
} else if (m2mMPP->mPhysicalType == MPP_G2D) {
fence_close(outImage.releaseFenceFd, this, FENCE_TYPE_DST_RELEASE, FENCE_IP_G2D);
} else {
DISPLAY_LOGE("[%zu] layer has invalid mppType(%d)", i, m2mMPP->mPhysicalType);
fence_close(outImage.releaseFenceFd, this, FENCE_TYPE_DST_RELEASE, FENCE_IP_ALL);
}
m2mMPP->resetDstReleaseFence();
}
}
}
}
/**
* @param outLayer
* @return int32_t
*/
int32_t ExynosDisplay::destroyLayer(hwc2_layer_t outLayer) {
Mutex::Autolock lock(mDRMutex);
if ((ExynosLayer*)outLayer == NULL)
return HWC2_ERROR_BAD_LAYER;
mLayers.remove((ExynosLayer*)outLayer);
delete (ExynosLayer*)outLayer;
setGeometryChanged(GEOMETRY_DISPLAY_LAYER_REMOVED);
if (mPlugState == false) {
DISPLAY_LOGI("%s : destroyLayer is done. But display is already disconnected",
__func__);
return HWC2_ERROR_BAD_DISPLAY;
}
return HWC2_ERROR_NONE;
}
/**
* @return void
*/
void ExynosDisplay::destroyLayers() {
while (!mLayers.isEmpty()) {
ExynosLayer *layer = mLayers[0];
if (layer != NULL) {
mLayers.remove(layer);
delete layer;
}
}
}
/**
* @param index
* @return ExynosLayer
*/
ExynosLayer *ExynosDisplay::getLayer(uint32_t index) {
if (mLayers.size() <= index) {
HWC_LOGE(this, "HWC2 : %s : size(%zu), index(%d), wrong layer request!",
__func__, mLayers.size(), index);
return NULL;
}
if(mLayers[index] != NULL) {
return mLayers[index];
}
else {
HWC_LOGE(this, "HWC2 : %s : %d, wrong layer request!", __func__, __LINE__);
return NULL;
}
}
ExynosLayer *ExynosDisplay::checkLayer(hwc2_layer_t addr) {
if (!mLayers.isEmpty()) {
ExynosLayer *temp = (ExynosLayer *)addr;
for (size_t i = 0; i < mLayers.size(); i++) {
if (mLayers[i] == temp)
return mLayers[i];
}
} else {
ALOGE("HWC2: mLayers is empty");
return NULL;
}
ALOGE("HWC2 : %s : %d, wrong layer request!", __func__, __LINE__);
return NULL;
}
/**
* @return void
*/
void ExynosDisplay::doPreProcessing() {
/* Low persistence setting */
int ret = 0;
uint32_t selfRefresh = 0;
unsigned int skipProcessing = 1;
bool hasSingleBuffer = false;
bool skipStaticLayers = true;
for (size_t i=0; i < mLayers.size(); i++) {
private_handle_t *handle = mLayers[i]->mLayerBuffer;
/* TODO: This should be checked **/
if ((handle != NULL) &&
#ifdef GRALLOC_VERSION1
(handle->consumer_usage & GRALLOC1_CONSUMER_USAGE_DAYDREAM_SINGLE_BUFFER_MODE))
#else
(handle->flags & GRALLOC_USAGE_DAYDREAM_SINGLE_BUFFER_MODE))
#endif
{
hasSingleBuffer = true;
}
if (mLayers[i]->mCompositionType == HWC2_COMPOSITION_CLIENT)
skipStaticLayers = false;
exynos_image srcImg;
exynos_image dstImg;
mLayers[i]->setSrcExynosImage(&srcImg);
mLayers[i]->setDstExynosImage(&dstImg);
mLayers[i]->setExynosImage(srcImg, dstImg);
}
/*
* Disable skip static layer feature if there is the layer that's
* mCompositionType is HWC2_COMPOSITION_CLIENT
* HWC should not change compositionType if it is HWC2_COMPOSITION_CLIENT
*/
if (mDisplayId != HWC_DISPLAY_VIRTUAL)
mClientCompositionInfo.mEnableSkipStatic = skipStaticLayers;
if (mHasSingleBuffer != hasSingleBuffer) {
if (hasSingleBuffer) {
selfRefresh = 1;
skipProcessing = 0;
} else {
selfRefresh = 0;
skipProcessing = 1;
}
if ((ret = mDisplayInterface->disableSelfRefresh(selfRefresh)) < 0)
DISPLAY_LOGE("ioctl S3CFB_LOW_PERSISTENCE failed: %s ret(%d)", strerror(errno), ret);
mHasSingleBuffer = hasSingleBuffer;
mDevice->setHWCControl(mDisplayId, HWC_CTL_SKIP_M2M_PROCESSING, skipProcessing);
mDevice->setHWCControl(mDisplayId, HWC_CTL_SKIP_STATIC, skipProcessing);
setGeometryChanged(GEOMETRY_DISPLAY_SINGLEBUF_CHANGED);
}
if ((exynosHWCControl.displayMode < DISPLAY_MODE_NUM) &&
(mDevice->mDisplayMode != exynosHWCControl.displayMode))
setGeometryChanged(GEOMETRY_DEVICE_DISP_MODE_CHAGED);
if ((ret = mResourceManager->checkScenario(this)) != NO_ERROR)
DISPLAY_LOGE("checkScenario error ret(%d)", ret);
if (exynosHWCControl.skipResourceAssign == 0) {
/* Set any flag to mGeometryChanged */
setGeometryChanged(GEOMETRY_DEVICE_SCENARIO_CHANGED);
}
#ifndef HWC_SKIP_VALIDATE
if (mDevice->checkConnection(HWC_DISPLAY_EXTERNAL) ||
mDevice->checkConnection(HWC_DISPLAY_VIRTUAL)) {
/* Set any flag to mGeometryChanged */
mDevice->mGeometryChanged = 0x10;
}
#endif
return;
}
/**
* @return int
*/
int ExynosDisplay::checkLayerFps() {
mLowFpsLayerInfo.initializeInfos();
if (mDisplayControl.handleLowFpsLayers == false)
return NO_ERROR;
for (size_t i=0; i < mLayers.size(); i++) {
if ((mLayers[i]->mOverlayPriority < ePriorityHigh) &&
(mLayers[i]->getFps() < LOW_FPS_THRESHOLD)) {
mLowFpsLayerInfo.addLowFpsLayer(i);
} else {
if (mLowFpsLayerInfo.mHasLowFpsLayer == true)
break;
else
continue;
}
}
/* There is only one low fps layer, Overlay is better in this case */
if ((mLowFpsLayerInfo.mHasLowFpsLayer == true) &&
(mLowFpsLayerInfo.mFirstIndex == mLowFpsLayerInfo.mLastIndex))
mLowFpsLayerInfo.initializeInfos();
return NO_ERROR;
}
/**
* @return int
*/
int ExynosDisplay::checkDynamicReCompMode() {
unsigned int updateFps = 0;
unsigned int lcd_size = mXres * mYres;
uint64_t TimeStampDiff;
uint64_t w = 0, h = 0, incomingPixels = 0;
uint64_t maxFps = 0, layerFps = 0;
Mutex::Autolock lock(mDRMutex);
if (!exynosHWCControl.useDynamicRecomp) {
mLastModeSwitchTimeStamp = 0;
mDynamicReCompMode = NO_MODE_SWITCH;
return 0;
}
/* initialize the Timestamps */
if (!mLastModeSwitchTimeStamp) {
mLastModeSwitchTimeStamp = mLastUpdateTimeStamp;
mDynamicReCompMode = NO_MODE_SWITCH;
return 0;
}
/* If video layer is there, skip the mode switch */
for (size_t i = 0; i < mLayers.size(); i++) {
if ((mLayers[i]->mOverlayPriority >= ePriorityHigh) ||
mLayers[i]->mPreprocessedInfo.preProcessed) {
if (mDynamicReCompMode != DEVICE_2_CLIENT) {
return 0;
} else {
mDynamicReCompMode = CLIENT_2_DEVICE;
mUpdateCallCnt = 0;
mLastModeSwitchTimeStamp = mLastUpdateTimeStamp;
DISPLAY_LOGD(eDebugDynamicRecomp, "[DYNAMIC_RECOMP] GLES_2_HWC by video layer");
setGeometryChanged(GEOMETRY_DISPLAY_DYNAMIC_RECOMPOSITION);
return CLIENT_2_DEVICE;
}
}
}
for (size_t i = 0; i < mLayers.size(); i++) {
w = WIDTH(mLayers[i]->mPreprocessedInfo.displayFrame);
h = HEIGHT(mLayers[i]->mPreprocessedInfo.displayFrame);
incomingPixels += w * h;
}
/* Mode Switch is not required if total pixels are not more than the threshold */
if (incomingPixels <= lcd_size) {
if (mDynamicReCompMode != DEVICE_2_CLIENT) {
return 0;
} else {
mDynamicReCompMode = CLIENT_2_DEVICE;
mUpdateCallCnt = 0;
mLastModeSwitchTimeStamp = mLastUpdateTimeStamp;
DISPLAY_LOGD(eDebugDynamicRecomp, "[DYNAMIC_RECOMP] GLES_2_HWC by BW check");
setGeometryChanged(GEOMETRY_DISPLAY_DYNAMIC_RECOMPOSITION);
return CLIENT_2_DEVICE;
}
}
/*
* There will be at least one composition call per one minute (because of time update)
* To minimize the analysis overhead, just analyze it once in a second
*/
TimeStampDiff = systemTime(SYSTEM_TIME_MONOTONIC) - mLastModeSwitchTimeStamp;
/*
* previous CompModeSwitch was CLIENT_2_DEVICE: check fps every 250ms from mLastModeSwitchTimeStamp
* previous CompModeSwitch was DEVICE_2_CLIENT: check immediately
*/
if ((mDynamicReCompMode != DEVICE_2_CLIENT) && (TimeStampDiff < (VSYNC_INTERVAL * 15)))
return 0;
mLastModeSwitchTimeStamp = mLastUpdateTimeStamp;
if ((mUpdateEventCnt != 1) &&
(mDynamicReCompMode == DEVICE_2_CLIENT) && (mUpdateCallCnt == 1)) {
DISPLAY_LOGD(eDebugDynamicRecomp, "[DYNAMIC_RECOMP] first frame after DEVICE_2_CLIENT");
updateFps = HWC_FPS_TH;
} else {
for (uint32_t i = 0; i < mLayers.size(); i++) {
layerFps = mLayers[i]->getFps();
if (maxFps < layerFps)
maxFps = layerFps;
}
updateFps = maxFps;
}
mUpdateCallCnt = 0;
/*
* FPS estimation.
* If FPS is lower than HWC_FPS_TH, try to switch the mode to GLES
*/
if (updateFps < HWC_FPS_TH) {
if (mDynamicReCompMode != DEVICE_2_CLIENT) {
mDynamicReCompMode = DEVICE_2_CLIENT;
DISPLAY_LOGD(eDebugDynamicRecomp, "[DYNAMIC_RECOMP] DEVICE_2_CLIENT by low FPS(%d)", updateFps);
setGeometryChanged(GEOMETRY_DISPLAY_DYNAMIC_RECOMPOSITION);
return DEVICE_2_CLIENT;
} else {
return 0;
}
} else {
if (mDynamicReCompMode == DEVICE_2_CLIENT) {
mDynamicReCompMode = CLIENT_2_DEVICE;
DISPLAY_LOGD(eDebugDynamicRecomp, "[DYNAMIC_RECOMP] CLIENT_2_HWC by high FPS(%d)", updateFps);
setGeometryChanged(GEOMETRY_DISPLAY_DYNAMIC_RECOMPOSITION);
return CLIENT_2_DEVICE;
} else {
return 0;
}
}
return 0;
}
/**
* @return int
*/
int ExynosDisplay::handleDynamicReCompMode() {
return 0;
}
/**
* @param changedBit
* @return int
*/
void ExynosDisplay::setGeometryChanged(uint64_t changedBit) {
mGeometryChanged |= changedBit;
mDevice->setGeometryChanged(changedBit);
}
void ExynosDisplay::clearGeometryChanged()
{
mGeometryChanged = 0;
for (size_t i=0; i < mLayers.size(); i++) {
mLayers[i]->clearGeometryChanged();
}
}
int ExynosDisplay::handleStaticLayers(ExynosCompositionInfo& compositionInfo)
{
if (compositionInfo.mType != COMPOSITION_CLIENT)
return -EINVAL;
if (mType == HWC_DISPLAY_VIRTUAL)
return NO_ERROR;
if (compositionInfo.mHasCompositionLayer == false) {
DISPLAY_LOGD(eDebugSkipStaicLayer, "there is no client composition");
return NO_ERROR;
}
if ((compositionInfo.mWindowIndex < 0) ||
(compositionInfo.mWindowIndex >= (int32_t)mDpuData.configs.size()))
{
DISPLAY_LOGE("invalid mWindowIndex(%d)", compositionInfo.mWindowIndex);
return -EINVAL;
}
exynos_win_config_data &config = mDpuData.configs[compositionInfo.mWindowIndex];
/* Store configuration of client target configuration */
if (compositionInfo.mSkipFlag == false) {
compositionInfo.mLastWinConfigData = config;
DISPLAY_LOGD(eDebugSkipStaicLayer, "config[%d] is stored",
compositionInfo.mWindowIndex);
} else {
for (size_t i = (size_t)compositionInfo.mFirstIndex; i <= (size_t)compositionInfo.mLastIndex; i++) {
if ((mLayers[i]->mExynosCompositionType == HWC2_COMPOSITION_CLIENT) &&
(mLayers[i]->mAcquireFence >= 0))
fence_close(mLayers[i]->mAcquireFence, this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_ALL);
mLayers[i]->mAcquireFence = -1;
mLayers[i]->mReleaseFence = -1;
}
if (compositionInfo.mTargetBuffer == NULL) {
fence_close(config.acq_fence, this,
FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_ALL);
config = compositionInfo.mLastWinConfigData;
/* Assigned otfMPP for client target can be changed */
config.assignedMPP = compositionInfo.mOtfMPP;
/* acq_fence was closed by DPU driver in the previous frame */
config.acq_fence = -1;
} else {
/* Check target buffer is same with previous frame */
if (!std::equal(config.fd_idma, config.fd_idma+3, compositionInfo.mLastWinConfigData.fd_idma)) {
DISPLAY_LOGE("Current config [%d][%d, %d, %d]",
compositionInfo.mWindowIndex,
config.fd_idma[0], config.fd_idma[1], config.fd_idma[2]);
DISPLAY_LOGE("============================= dump last win configs ===================================");
for (size_t i = 0; i <= mLastDpuData.configs.size(); i++) {
android::String8 result;
result.appendFormat("config[%zu]\n", i);
dumpConfig(result, mLastDpuData.configs[i]);
DISPLAY_LOGE("%s", result.string());
}
DISPLAY_LOGE("compositionInfo.mLastWinConfigData config [%d, %d, %d]",
compositionInfo.mLastWinConfigData.fd_idma[0],
compositionInfo.mLastWinConfigData.fd_idma[1],
compositionInfo.mLastWinConfigData.fd_idma[2]);
return -EINVAL;
}
}
DISPLAY_LOGD(eDebugSkipStaicLayer, "skipStaticLayer config[%d]", compositionInfo.mWindowIndex);
dumpConfig(config);
}
return NO_ERROR;
}
bool ExynosDisplay::skipStaticLayerChanged(ExynosCompositionInfo& compositionInfo)
{
if ((int)compositionInfo.mSkipSrcInfo.srcNum !=
(compositionInfo.mLastIndex - compositionInfo.mFirstIndex + 1)) {
DISPLAY_LOGD(eDebugSkipStaicLayer, "Client composition number is changed (%d -> %d)",
compositionInfo.mSkipSrcInfo.srcNum,
compositionInfo.mLastIndex - compositionInfo.mFirstIndex + 1);
return true;
}
bool isChanged = false;
for (size_t i = (size_t)compositionInfo.mFirstIndex; i <= (size_t)compositionInfo.mLastIndex; i++) {
ExynosLayer *layer = mLayers[i];
size_t index = i - compositionInfo.mFirstIndex;
if ((layer->mLayerBuffer == NULL) ||
(compositionInfo.mSkipSrcInfo.srcInfo[index].bufferHandle != layer->mLayerBuffer))
{
isChanged = true;
DISPLAY_LOGD(eDebugSkipStaicLayer, "layer[%zu] handle is changed"\
" handle(%p -> %p), layerFlag(0x%8x)",
i, compositionInfo.mSkipSrcInfo.srcInfo[index].bufferHandle,
layer->mLayerBuffer, layer->mLayerFlag);
break;
} else if ((compositionInfo.mSkipSrcInfo.srcInfo[index].x != layer->mSrcImg.x) ||
(compositionInfo.mSkipSrcInfo.srcInfo[index].y != layer->mSrcImg.y) ||
(compositionInfo.mSkipSrcInfo.srcInfo[index].w != layer->mSrcImg.w) ||
(compositionInfo.mSkipSrcInfo.srcInfo[index].h != layer->mSrcImg.h) ||
(compositionInfo.mSkipSrcInfo.srcInfo[index].dataSpace != layer->mSrcImg.dataSpace) ||
(compositionInfo.mSkipSrcInfo.srcInfo[index].blending != layer->mSrcImg.blending) ||
(compositionInfo.mSkipSrcInfo.srcInfo[index].transform != layer->mSrcImg.transform) ||
(compositionInfo.mSkipSrcInfo.srcInfo[index].planeAlpha != layer->mSrcImg.planeAlpha))
{
isChanged = true;
DISPLAY_LOGD(eDebugSkipStaicLayer, "layer[%zu] source info is changed, "\
"x(%d->%d), y(%d->%d), w(%d->%d), h(%d->%d), dataSpace(%d->%d), "\
"blending(%d->%d), transform(%d->%d), planeAlpha(%3.1f->%3.1f)", i,
compositionInfo.mSkipSrcInfo.srcInfo[index].x, layer->mSrcImg.x,
compositionInfo.mSkipSrcInfo.srcInfo[index].y, layer->mSrcImg.y,
compositionInfo.mSkipSrcInfo.srcInfo[index].w, layer->mSrcImg.w,
compositionInfo.mSkipSrcInfo.srcInfo[index].h, layer->mSrcImg.h,
compositionInfo.mSkipSrcInfo.srcInfo[index].dataSpace, layer->mSrcImg.dataSpace,
compositionInfo.mSkipSrcInfo.srcInfo[index].blending, layer->mSrcImg.blending,
compositionInfo.mSkipSrcInfo.srcInfo[index].transform, layer->mSrcImg.transform,
compositionInfo.mSkipSrcInfo.srcInfo[index].planeAlpha, layer->mSrcImg.planeAlpha);
break;
} else if ((compositionInfo.mSkipSrcInfo.dstInfo[index].x != layer->mDstImg.x) ||
(compositionInfo.mSkipSrcInfo.dstInfo[index].y != layer->mDstImg.y) ||
(compositionInfo.mSkipSrcInfo.dstInfo[index].w != layer->mDstImg.w) ||
(compositionInfo.mSkipSrcInfo.dstInfo[index].h != layer->mDstImg.h))
{
isChanged = true;
DISPLAY_LOGD(eDebugSkipStaicLayer, "layer[%zu] dst info is changed, "\
"x(%d->%d), y(%d->%d), w(%d->%d), h(%d->%d)", i,
compositionInfo.mSkipSrcInfo.dstInfo[index].x, layer->mDstImg.x,
compositionInfo.mSkipSrcInfo.dstInfo[index].y, layer->mDstImg.y,
compositionInfo.mSkipSrcInfo.dstInfo[index].w, layer->mDstImg.w,
compositionInfo.mSkipSrcInfo.dstInfo[index].h, layer->mDstImg.h);
break;
}
}
return isChanged;
}
/**
* @param compositionType
* @return int
*/
int ExynosDisplay::skipStaticLayers(ExynosCompositionInfo& compositionInfo)
{
compositionInfo.mSkipFlag = false;
if (compositionInfo.mType != COMPOSITION_CLIENT)
return -EINVAL;
if ((exynosHWCControl.skipStaticLayers == 0) ||
(compositionInfo.mEnableSkipStatic == false)) {
DISPLAY_LOGD(eDebugSkipStaicLayer, "skipStaticLayers(%d), mEnableSkipStatic(%d)",
exynosHWCControl.skipStaticLayers, compositionInfo.mEnableSkipStatic);
compositionInfo.mSkipStaticInitFlag = false;
return NO_ERROR;
}
if ((compositionInfo.mHasCompositionLayer == false) ||
(compositionInfo.mFirstIndex < 0) ||
(compositionInfo.mLastIndex < 0) ||
((compositionInfo.mLastIndex - compositionInfo.mFirstIndex + 1) > NUM_SKIP_STATIC_LAYER)) {
DISPLAY_LOGD(eDebugSkipStaicLayer, "mHasCompositionLayer(%d), mFirstIndex(%d), mLastIndex(%d)",
compositionInfo.mHasCompositionLayer,
compositionInfo.mFirstIndex, compositionInfo.mLastIndex);
compositionInfo.mSkipStaticInitFlag = false;
return NO_ERROR;
}
if (compositionInfo.mSkipStaticInitFlag) {
bool isChanged = skipStaticLayerChanged(compositionInfo);
if (isChanged == true) {
compositionInfo.mSkipStaticInitFlag = false;
return NO_ERROR;
}
for (size_t i = (size_t)compositionInfo.mFirstIndex; i <= (size_t)compositionInfo.mLastIndex; i++) {
ExynosLayer *layer = mLayers[i];
if (layer->mValidateCompositionType == COMPOSITION_CLIENT) {
layer->mOverlayInfo |= eSkipStaticLayer;
} else {
compositionInfo.mSkipStaticInitFlag = false;
if (layer->mOverlayPriority < ePriorityHigh) {
DISPLAY_LOGE("[%zu] Invalid layer type: %d",
i, layer->mValidateCompositionType);
return -EINVAL;
} else {
return NO_ERROR;
}
}
}
compositionInfo.mSkipFlag = true;
DISPLAY_LOGD(eDebugSkipStaicLayer, "SkipStaicLayer is enabled");
return NO_ERROR;
}
compositionInfo.mSkipStaticInitFlag = true;
memset(&compositionInfo.mSkipSrcInfo, 0, sizeof(compositionInfo.mSkipSrcInfo));
for (int i = 0; i < NUM_SKIP_STATIC_LAYER; i++) {
compositionInfo.mSkipSrcInfo.srcInfo[i].acquireFenceFd = -1;
compositionInfo.mSkipSrcInfo.srcInfo[i].releaseFenceFd = -1;
compositionInfo.mSkipSrcInfo.dstInfo[i].acquireFenceFd = -1;
compositionInfo.mSkipSrcInfo.dstInfo[i].releaseFenceFd = -1;
}
for (size_t i = (size_t)compositionInfo.mFirstIndex; i <= (size_t)compositionInfo.mLastIndex; i++) {
ExynosLayer *layer = mLayers[i];
size_t index = i - compositionInfo.mFirstIndex;
compositionInfo.mSkipSrcInfo.srcInfo[index] = layer->mSrcImg;
compositionInfo.mSkipSrcInfo.dstInfo[index] = layer->mDstImg;
DISPLAY_LOGD(eDebugSkipStaicLayer, "mSkipSrcInfo.srcInfo[%zu] is initialized, %p",
index, layer->mSrcImg.bufferHandle);
}
compositionInfo.mSkipSrcInfo.srcNum = (compositionInfo.mLastIndex - compositionInfo.mFirstIndex + 1);
return NO_ERROR;
}
/**
* @return int
*/
int ExynosDisplay::doPostProcessing() {
for (size_t i=0; i < mLayers.size(); i++) {
/* Layer handle back-up */
mLayers[i]->mLastLayerBuffer = mLayers[i]->mLayerBuffer;
}
clearGeometryChanged();
return 0;
}
bool ExynosDisplay::validateExynosCompositionLayer()
{
bool isValid = true;
ExynosMPP *m2mMpp = mExynosCompositionInfo.mM2mMPP;
int sourceSize = (int)m2mMpp->mAssignedSources.size();
if ((mExynosCompositionInfo.mFirstIndex >= 0) &&
(mExynosCompositionInfo.mLastIndex >= 0)) {
sourceSize = mExynosCompositionInfo.mLastIndex - mExynosCompositionInfo.mFirstIndex + 1;
if (!mUseDpu && mClientCompositionInfo.mHasCompositionLayer)
sourceSize++;
}
if (m2mMpp->mAssignedSources.size() == 0) {
DISPLAY_LOGE("No source images");
isValid = false;
} else if (mUseDpu && (((mExynosCompositionInfo.mFirstIndex < 0) ||
(mExynosCompositionInfo.mLastIndex < 0)) ||
(sourceSize != (int)m2mMpp->mAssignedSources.size()))) {
DISPLAY_LOGE("Invalid index (%d, %d), size(%zu), sourceSize(%d)",
mExynosCompositionInfo.mFirstIndex,
mExynosCompositionInfo.mLastIndex,
m2mMpp->mAssignedSources.size(),
sourceSize);
isValid = false;
}
if (isValid == false) {
for (int32_t i = mExynosCompositionInfo.mFirstIndex; i <= mExynosCompositionInfo.mLastIndex; i++) {
/* break when only framebuffer target is assigned on ExynosCompositor */
if (i == -1)
break;
if (mLayers[i]->mAcquireFence >= 0)
fence_close(mLayers[i]->mAcquireFence, this,
FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_ALL);
mLayers[i]->mAcquireFence = -1;
}
mExynosCompositionInfo.mM2mMPP->requestHWStateChange(MPP_HW_STATE_IDLE);
}
return isValid;
}
/**
* @return int
*/
int ExynosDisplay::doExynosComposition() {
int ret = NO_ERROR;
exynos_image src_img;
exynos_image dst_img;
if (mExynosCompositionInfo.mHasCompositionLayer) {
if (mExynosCompositionInfo.mM2mMPP == NULL) {
DISPLAY_LOGE("mExynosCompositionInfo.mM2mMPP is NULL");
return -EINVAL;
}
mExynosCompositionInfo.mM2mMPP->requestHWStateChange(MPP_HW_STATE_RUNNING);
/* mAcquireFence is updated, Update image info */
for (int32_t i = mExynosCompositionInfo.mFirstIndex; i <= mExynosCompositionInfo.mLastIndex; i++) {
/* break when only framebuffer target is assigned on ExynosCompositor */
if (i == -1)
break;
struct exynos_image srcImg, dstImg;
mLayers[i]->setSrcExynosImage(&srcImg);
dumpExynosImage(eDebugFence, srcImg);
mLayers[i]->setDstExynosImage(&dstImg);
mLayers[i]->setExynosImage(srcImg, dstImg);
}
/* For debugging */
if (validateExynosCompositionLayer() == false) {
DISPLAY_LOGE("mExynosCompositionInfo is not valid");
return -EINVAL;
}
if ((ret = mExynosCompositionInfo.mM2mMPP->doPostProcessing(mExynosCompositionInfo.mSrcImg,
mExynosCompositionInfo.mDstImg)) != NO_ERROR) {
DISPLAY_LOGE("exynosComposition doPostProcessing fail ret(%d)", ret);
return ret;
}
for (int32_t i = mExynosCompositionInfo.mFirstIndex; i <= mExynosCompositionInfo.mLastIndex; i++) {
/* break when only framebuffer target is assigned on ExynosCompositor */
if (i == -1)
break;
/* This should be closed by resource lib (libmpp or libacryl) */
mLayers[i]->mAcquireFence = -1;
}
exynos_image outImage;
if ((ret = mExynosCompositionInfo.mM2mMPP->getDstImageInfo(&outImage)) != NO_ERROR) {
DISPLAY_LOGE("exynosComposition getDstImageInfo fail ret(%d)", ret);
return ret;
}
android_dataspace dataspace = HAL_DATASPACE_UNKNOWN;
if (mColorMode != HAL_COLOR_MODE_NATIVE)
dataspace = colorModeToDataspace(mColorMode);
mExynosCompositionInfo.setTargetBuffer(this, outImage.bufferHandle,
outImage.releaseFenceFd, dataspace);
/*
* buffer handle, dataspace can be changed by setTargetBuffer()
* ExynosImage should be set again according to changed handle and dataspace
*/
setCompositionTargetExynosImage(COMPOSITION_EXYNOS, &src_img, &dst_img);
mExynosCompositionInfo.setExynosImage(src_img, dst_img);
DISPLAY_LOGD(eDebugFence, "mExynosCompositionInfo acquireFencefd(%d)",
mExynosCompositionInfo.mAcquireFence);
// Test..
// setFenceInfo(mExynosCompositionInfo.mAcquireFence, this, "G2D_DST_ACQ", FENCE_FROM);
if ((ret = mExynosCompositionInfo.mM2mMPP->resetDstReleaseFence()) != NO_ERROR)
{
DISPLAY_LOGE("exynosComposition resetDstReleaseFence fail ret(%d)", ret);
return ret;
}
}
return ret;
}
bool ExynosDisplay::getHDRException(ExynosLayer* __unused layer)
{
return false;
}
int32_t ExynosDisplay::configureHandle(ExynosLayer &layer, int fence_fd, exynos_win_config_data &cfg)
{
/* TODO : this is hardcoded */
int32_t ret = NO_ERROR;
private_handle_t *handle = NULL;
int32_t blending = 0x0100;
uint32_t x = 0, y = 0;
uint32_t w = WIDTH(layer.mPreprocessedInfo.displayFrame);
uint32_t h = HEIGHT(layer.mPreprocessedInfo.displayFrame);
ExynosMPP* otfMPP = NULL;
ExynosMPP* m2mMPP = NULL;
unsigned int luminanceMin = 0;
unsigned int luminanceMax = 0;
blending = layer.mBlending;
otfMPP = layer.mOtfMPP;
m2mMPP = layer.mM2mMPP;
cfg.compression = layer.mCompressed;
if (layer.mCompressed) {
cfg.comp_src = DPP_COMP_SRC_GPU;
}
if (otfMPP == NULL) {
HWC_LOGE(this, "%s:: otfMPP is NULL", __func__);
return -EINVAL;
}
if (m2mMPP != NULL)
handle = m2mMPP->mDstImgs[m2mMPP->mCurrentDstBuf].bufferHandle;
else
handle = layer.mLayerBuffer;
if ((!layer.isDimLayer()) && handle == NULL) {
HWC_LOGE(this, "%s:: invalid handle", __func__);
return -EINVAL;
}
if (layer.mPreprocessedInfo.displayFrame.left < 0) {
unsigned int crop = -layer.mPreprocessedInfo.displayFrame.left;
DISPLAY_LOGD(eDebugWinConfig, "layer off left side of screen; cropping %u pixels from left edge",
crop);
x = 0;
w -= crop;
} else {
x = layer.mPreprocessedInfo.displayFrame.left;
}
if (layer.mPreprocessedInfo.displayFrame.right > (int)mXres) {
unsigned int crop = layer.mPreprocessedInfo.displayFrame.right - mXres;
DISPLAY_LOGD(eDebugWinConfig, "layer off right side of screen; cropping %u pixels from right edge",
crop);
w -= crop;
}
if (layer.mPreprocessedInfo.displayFrame.top < 0) {
unsigned int crop = -layer.mPreprocessedInfo.displayFrame.top;
DISPLAY_LOGD(eDebugWinConfig, "layer off top side of screen; cropping %u pixels from top edge",
crop);
y = 0;
h -= crop;
} else {
y = layer.mPreprocessedInfo.displayFrame.top;
}
if (layer.mPreprocessedInfo.displayFrame.bottom > (int)mYres) {
int crop = layer.mPreprocessedInfo.displayFrame.bottom - mYres;
DISPLAY_LOGD(eDebugWinConfig, "layer off bottom side of screen; cropping %u pixels from bottom edge",
crop);
h -= crop;
}
if ((layer.mExynosCompositionType == HWC2_COMPOSITION_DEVICE) &&
(layer.mCompositionType == HWC2_COMPOSITION_CURSOR))
cfg.state = cfg.WIN_STATE_CURSOR;
else
cfg.state = cfg.WIN_STATE_BUFFER;
cfg.dst.x = x;
cfg.dst.y = y;
cfg.dst.w = w;
cfg.dst.h = h;
cfg.dst.f_w = mXres;
cfg.dst.f_h = mYres;
cfg.plane_alpha = layer.mPlaneAlpha;
cfg.blending = blending;
cfg.assignedMPP = otfMPP;
if (layer.isDimLayer()) {
cfg.state = cfg.WIN_STATE_COLOR;
hwc_color_t color = layer.mColor;
cfg.color = (color.a << 24) | (color.r << 16) | (color.g << 8) | color.b;
DISPLAY_LOGD(eDebugWinConfig, "HWC2: DIM layer is enabled, color: %d, alpha : %f",
cfg.color, cfg.plane_alpha);
return ret;
}
if (!layer.mPreprocessedInfo.mUsePrivateFormat)
cfg.format = handle->format;
else
cfg.format = layer.mPreprocessedInfo.mPrivateFormat;
cfg.fd_idma[0] = handle->fd;
cfg.fd_idma[1] = handle->fd1;
cfg.fd_idma[2] = handle->fd2;
cfg.protection = (getDrmMode(handle) == SECURE_DRM) ? 1 : 0;
exynos_image src_img = layer.mSrcImg;
if (m2mMPP != NULL)
{
DISPLAY_LOGD(eDebugWinConfig, "\tUse m2mMPP, bufIndex: %d", m2mMPP->mCurrentDstBuf);
dumpExynosImage(eDebugWinConfig, m2mMPP->mAssignedSources[0]->mMidImg);
exynos_image mpp_dst_img;
if (m2mMPP->getDstImageInfo(&mpp_dst_img) == NO_ERROR) {
dumpExynosImage(eDebugWinConfig, mpp_dst_img);
cfg.src.f_w = mpp_dst_img.fullWidth;
cfg.src.f_h = mpp_dst_img.fullHeight;
cfg.src.x = mpp_dst_img.x;
cfg.src.y = mpp_dst_img.y;
cfg.src.w = mpp_dst_img.w;
cfg.src.h = mpp_dst_img.h;
cfg.format = mpp_dst_img.format;
cfg.acq_fence =
hwcCheckFenceDebug(this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_DPP, mpp_dst_img.releaseFenceFd);
if (m2mMPP->mPhysicalType == MPP_MSC) {
setFenceName(cfg.acq_fence, FENCE_DPP_SRC_MSC);
} else if (m2mMPP->mPhysicalType == MPP_G2D) {
setFenceName(cfg.acq_fence, FENCE_DPP_SRC_G2D);
} else {
setFenceName(cfg.acq_fence, FENCE_DPP_SRC_MPP);
}
m2mMPP->resetDstReleaseFence();
} else {
HWC_LOGE(this, "%s:: Failed to get dst info of m2mMPP", __func__);
}
cfg.dataspace = mpp_dst_img.dataSpace;
cfg.transform = 0;
if (hasHdrInfo(layer.mMidImg)) {
bool hdr_exception = getHDRException(&layer);
uint32_t parcelFdIndex =
getBufferNumOfFormat(layer.mMidImg.format,
getAFBCCompressionType(layer.mMidImg.bufferHandle));
if (parcelFdIndex == 0) {
DISPLAY_LOGE("%s:: failed to get parcelFdIndex for midImg with format: %d",
__func__, layer.mMidImg.format);
return -EINVAL;
}
if (layer.mBufferHasMetaParcel) {
if (layer.mLayerBuffer->flags & private_handle_t::PRIV_FLAGS_USES_2PRIVATE_DATA)
cfg.fd_idma[parcelFdIndex] = layer.mLayerBuffer->fd1;
else if (layer.mLayerBuffer->flags &
private_handle_t::PRIV_FLAGS_USES_3PRIVATE_DATA)
cfg.fd_idma[parcelFdIndex] = layer.mLayerBuffer->fd2;
} else {
cfg.fd_idma[parcelFdIndex] = layer.mMetaParcelFd;
}
if (!hdr_exception)
cfg.hdr_enable = true;
else
cfg.hdr_enable = false;
/* Min/Max luminance should be set as M2M MPP's HDR operations
* If HDR is not processed by M2M MPP, M2M's dst image should have source's min/max luminance
* */
dstMetaInfo_t metaInfo = m2mMPP->getDstMetaInfo(mpp_dst_img.dataSpace);
luminanceMin = metaInfo.minLuminance;
luminanceMax = metaInfo.maxLuminance;
DISPLAY_LOGD(eDebugMPP, "HWC2: DPP luminance min %d, max %d", luminanceMin, luminanceMax);
} else {
cfg.hdr_enable = true;
}
src_img = layer.mMidImg;
} else {
cfg.src.f_w = src_img.fullWidth;
cfg.src.f_h = src_img.fullHeight;
cfg.src.x = layer.mPreprocessedInfo.sourceCrop.left;
cfg.src.y = layer.mPreprocessedInfo.sourceCrop.top;
cfg.src.w = WIDTH(layer.mPreprocessedInfo.sourceCrop) - (cfg.src.x - (uint32_t)layer.mPreprocessedInfo.sourceCrop.left);
cfg.src.h = HEIGHT(layer.mPreprocessedInfo.sourceCrop) - (cfg.src.y - (uint32_t)layer.mPreprocessedInfo.sourceCrop.top);
cfg.acq_fence = hwcCheckFenceDebug(this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_DPP, fence_fd);
setFenceName(cfg.acq_fence, FENCE_DPP_SRC_LAYER);
cfg.dataspace = src_img.dataSpace;
cfg.transform = src_img.transform;
if (hasHdrInfo(src_img)) {
bool hdr_exception = getHDRException(&layer);
if (!hdr_exception)
cfg.hdr_enable = true;
else
cfg.hdr_enable = false;
if (layer.mBufferHasMetaParcel == false) {
uint32_t parcelFdIndex =
getBufferNumOfFormat(handle->format, getAFBCCompressionType(handle));
if (parcelFdIndex == 0) {
DISPLAY_LOGE("%s:: failed to get parcelFdIndex for srcImg with format: %d",
__func__, handle->format);
return -EINVAL;
}
cfg.fd_idma[parcelFdIndex] = layer.mMetaParcelFd;
}
/*
* Static info uses 0.0001nit unit for luminace
* Display uses 1nit unit for max luminance
* and uses 0.0001nit unit for min luminance
* Conversion is required
*/
luminanceMin = src_img.metaParcel.sHdrStaticInfo.sType1.mMinDisplayLuminance;
luminanceMax = src_img.metaParcel.sHdrStaticInfo.sType1.mMaxDisplayLuminance/10000;
DISPLAY_LOGD(eDebugMPP, "HWC2: DPP luminance min %d, max %d", luminanceMin, luminanceMax);
} else {
cfg.hdr_enable = true;
}
}
cfg.min_luminance = luminanceMin;
cfg.max_luminance = luminanceMax;
cfg.needColorTransform = src_img.needColorTransform;
/* Adjust configuration */
uint32_t srcMaxWidth, srcMaxHeight, srcWidthAlign, srcHeightAlign = 0;
uint32_t srcXAlign, srcYAlign, srcMaxCropWidth, srcMaxCropHeight, srcCropWidthAlign, srcCropHeightAlign = 0;
srcMaxWidth = otfMPP->getSrcMaxWidth(src_img);
srcMaxHeight = otfMPP->getSrcMaxHeight(src_img);
srcWidthAlign = otfMPP->getSrcWidthAlign(src_img);
srcHeightAlign = otfMPP->getSrcHeightAlign(src_img);
srcXAlign = otfMPP->getSrcXOffsetAlign(src_img);
srcYAlign = otfMPP->getSrcYOffsetAlign(src_img);
srcMaxCropWidth = otfMPP->getSrcMaxCropWidth(src_img);
srcMaxCropHeight = otfMPP->getSrcMaxCropHeight(src_img);
srcCropWidthAlign = otfMPP->getSrcCropWidthAlign(src_img);
srcCropHeightAlign = otfMPP->getSrcCropHeightAlign(src_img);
if (cfg.src.x < 0)
cfg.src.x = 0;
if (cfg.src.y < 0)
cfg.src.y = 0;
if (otfMPP != NULL) {
if (cfg.src.f_w > srcMaxWidth)
cfg.src.f_w = srcMaxWidth;
if (cfg.src.f_h > srcMaxHeight)
cfg.src.f_h = srcMaxHeight;
cfg.src.f_w = pixel_align_down((unsigned int)cfg.src.f_w, srcWidthAlign);
cfg.src.f_h = pixel_align_down((unsigned int)cfg.src.f_h, srcHeightAlign);
cfg.src.x = pixel_align(cfg.src.x, srcXAlign);
cfg.src.y = pixel_align(cfg.src.y, srcYAlign);
}
if (cfg.src.x + cfg.src.w > cfg.src.f_w)
cfg.src.w = cfg.src.f_w - cfg.src.x;
if (cfg.src.y + cfg.src.h > cfg.src.f_h)
cfg.src.h = cfg.src.f_h - cfg.src.y;
if (otfMPP != NULL) {
if (cfg.src.w > srcMaxCropWidth)
cfg.src.w = srcMaxCropWidth;
if (cfg.src.h > srcMaxCropHeight)
cfg.src.h = srcMaxCropHeight;
cfg.src.w = pixel_align_down(cfg.src.w, srcCropWidthAlign);
cfg.src.h = pixel_align_down(cfg.src.h, srcCropHeightAlign);
}
uint64_t bufSize = handle->size * formatToBpp(handle->format);
uint64_t srcSize = cfg.src.f_w * cfg.src.f_h * formatToBpp(cfg.format);
if (!isFormatLossy(handle->format) && (bufSize < srcSize)) {
DISPLAY_LOGE("%s:: buffer size is smaller than source size, buf(size: %d, format: %d), src(w: %d, h: %d, format: %d)",
__func__, handle->size, handle->format, cfg.src.f_w, cfg.src.f_h, cfg.format);
return -EINVAL;
}
return ret;
}
int32_t ExynosDisplay::configureOverlay(ExynosLayer *layer, exynos_win_config_data &cfg)
{
int32_t ret = NO_ERROR;
if(layer != NULL) {
if ((ret = configureHandle(*layer, layer->mAcquireFence, cfg)) != NO_ERROR)
return ret;
/* This will be closed by setReleaseFences() using config.acq_fence */
layer->mAcquireFence = -1;
}
return ret;
}
int32_t ExynosDisplay::configureOverlay(ExynosCompositionInfo &compositionInfo)
{
int32_t windowIndex = compositionInfo.mWindowIndex;
private_handle_t *handle = compositionInfo.mTargetBuffer;
if ((windowIndex < 0) || (windowIndex >= (int32_t)mDpuData.configs.size()))
{
HWC_LOGE(this, "%s:: ExynosCompositionInfo(%d) has invalid data, windowIndex(%d)",
__func__, compositionInfo.mType, windowIndex);
return -EINVAL;
}
exynos_win_config_data &config = mDpuData.configs[windowIndex];
if (handle == NULL) {
/* config will be set by handleStaticLayers */
if (compositionInfo.mSkipFlag)
return NO_ERROR;
if (compositionInfo.mType == COMPOSITION_CLIENT) {
ALOGW("%s:: ExynosCompositionInfo(%d) has invalid data, handle(%p)",
__func__, compositionInfo.mType, handle);
if (compositionInfo.mAcquireFence >= 0) {
compositionInfo.mAcquireFence = fence_close(compositionInfo.mAcquireFence, this,
FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_FB);
}
config.state = config.WIN_STATE_DISABLED;
return NO_ERROR;
} else {
HWC_LOGE(this, "%s:: ExynosCompositionInfo(%d) has invalid data, handle(%p)",
__func__, compositionInfo.mType, handle);
return -EINVAL;
}
}
config.fd_idma[0] = handle->fd;
config.fd_idma[1] = handle->fd1;
config.fd_idma[2] = handle->fd2;
config.protection = (getDrmMode(handle) == SECURE_DRM) ? 1 : 0;
config.state = config.WIN_STATE_BUFFER;
config.assignedMPP = compositionInfo.mOtfMPP;
config.dst.f_w = mXres;
config.dst.f_h = mYres;
config.format = handle->format;
if (compositionInfo.mType == COMPOSITION_EXYNOS) {
config.src.f_w = pixel_align(mXres, G2D_JUSTIFIED_DST_ALIGN);
config.src.f_h = pixel_align(mYres, G2D_JUSTIFIED_DST_ALIGN);
} else {
config.src.f_w = handle->stride;
config.src.f_h = handle->vstride;
}
config.compression = compositionInfo.mCompressed;
if (compositionInfo.mCompressed) {
if (compositionInfo.mType == COMPOSITION_EXYNOS)
config.comp_src = DPP_COMP_SRC_G2D;
else if (compositionInfo.mType == COMPOSITION_CLIENT)
config.comp_src = DPP_COMP_SRC_GPU;
else
HWC_LOGE(this, "unknown composition type: %d", compositionInfo.mType);
}
bool useCompositionCrop = true;
if ((mDisplayControl.enableCompositionCrop) &&
(compositionInfo.mHasCompositionLayer) &&
(compositionInfo.mFirstIndex >= 0) &&
(compositionInfo.mLastIndex >= 0)) {
hwc_rect merged_rect, src_rect;
merged_rect.left = mXres;
merged_rect.top = mYres;
merged_rect.right = 0;
merged_rect.bottom = 0;
for (int i = compositionInfo.mFirstIndex; i <= compositionInfo.mLastIndex; i++) {
ExynosLayer *layer = mLayers[i];
src_rect.left = layer->mDisplayFrame.left;
src_rect.top = layer->mDisplayFrame.top;
src_rect.right = layer->mDisplayFrame.right;
src_rect.bottom = layer->mDisplayFrame.bottom;
merged_rect = expand(merged_rect, src_rect);
DISPLAY_LOGD(eDebugWinConfig, "[%d] layer type: [%d, %d] dispFrame [l: %d, t: %d, r: %d, b: %d], mergedRect [l: %d, t: %d, r: %d, b: %d]",
i,
layer->mCompositionType,
layer->mExynosCompositionType,
layer->mDisplayFrame.left,
layer->mDisplayFrame.top,
layer->mDisplayFrame.right,
layer->mDisplayFrame.bottom,
merged_rect.left,
merged_rect.top,
merged_rect.right,
merged_rect.bottom);
}
config.src.x = merged_rect.left;
config.src.y = merged_rect.top;
config.src.w = merged_rect.right - merged_rect.left;
config.src.h = merged_rect.bottom - merged_rect.top;
ExynosMPP* exynosMPP = config.assignedMPP;
if (exynosMPP == NULL) {
DISPLAY_LOGE("%s:: assignedMPP is NULL", __func__);
useCompositionCrop = false;
} else {
/* Check size constraints */
uint32_t restrictionIdx = getRestrictionIndex(config.format);
uint32_t srcXAlign = exynosMPP->getSrcXOffsetAlign(restrictionIdx);
uint32_t srcYAlign = exynosMPP->getSrcYOffsetAlign(restrictionIdx);
uint32_t srcWidthAlign = exynosMPP->getSrcCropWidthAlign(restrictionIdx);
uint32_t srcHeightAlign = exynosMPP->getSrcCropHeightAlign(restrictionIdx);
uint32_t srcMinWidth = exynosMPP->getSrcMinWidth(restrictionIdx);
uint32_t srcMinHeight = exynosMPP->getSrcMinHeight(restrictionIdx);
if (config.src.w < srcMinWidth) {
config.src.x -= (srcMinWidth - config.src.w);
if (config.src.x < 0)
config.src.x = 0;
config.src.w = srcMinWidth;
}
if (config.src.h < srcMinHeight) {
config.src.y -= (srcMinHeight - config.src.h);
if (config.src.y < 0)
config.src.y = 0;
config.src.h = srcMinHeight;
}
int32_t alignedSrcX = pixel_align_down(config.src.x, srcXAlign);
int32_t alignedSrcY = pixel_align_down(config.src.y, srcYAlign);
config.src.w += (config.src.x - alignedSrcX);
config.src.h += (config.src.y - alignedSrcY);
config.src.x = alignedSrcX;
config.src.y = alignedSrcY;
config.src.w = pixel_align(config.src.w, srcWidthAlign);
config.src.h = pixel_align(config.src.h, srcHeightAlign);
}
config.dst.x = config.src.x;
config.dst.y = config.src.y;
config.dst.w = config.src.w;
config.dst.h = config.src.h;
if ((config.src.x < 0) ||
(config.src.y < 0) ||
((config.src.x + config.src.w) > mXres) ||
((config.src.y + config.src.h) > mYres)) {
useCompositionCrop = false;
ALOGW("Invalid composition target crop size: (%d, %d, %d, %d)",
config.src.x, config.src.y,
config.src.w, config.src.h);
}
DISPLAY_LOGD(eDebugWinConfig, "composition(%d) config[%d] x : %d, y : %d, w : %d, h : %d",
compositionInfo.mType, windowIndex,
config.dst.x, config.dst.y,
config.dst.w, config.dst.h);
} else {
useCompositionCrop = false;
}
if (useCompositionCrop == false) {
config.src.x = 0;
config.src.y = 0;
config.src.w = mXres;
config.src.h = mYres;
config.dst.x = 0;
config.dst.y = 0;
config.dst.w = mXres;
config.dst.h = mYres;
}
config.blending = HWC2_BLEND_MODE_PREMULTIPLIED;
config.acq_fence =
hwcCheckFenceDebug(this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_DPP, compositionInfo.mAcquireFence);
config.plane_alpha = 1;
config.dataspace = compositionInfo.mSrcImg.dataSpace;
config.hdr_enable = true;
/* This will be closed by setReleaseFences() using config.acq_fence */
compositionInfo.mAcquireFence = -1;
DISPLAY_LOGD(eDebugSkipStaicLayer, "Configure composition target[%d], config[%d]!!!!",
compositionInfo.mType, windowIndex);
dumpConfig(config);
uint64_t bufSize = handle->size * formatToBpp(handle->format);
uint64_t srcSize = config.src.f_w * config.src.f_h * formatToBpp(config.format);
if (!isFormatLossy(handle->format) && (bufSize < srcSize)) {
DISPLAY_LOGE("%s:: buffer size is smaller than source size, buf(size: %d, format: %d), src(w: %d, h: %d, format: %d)",
__func__, handle->size, handle->format, config.src.f_w, config.src.f_h, config.format);
return -EINVAL;
}
return NO_ERROR;
}
/**
* @return int
*/
int ExynosDisplay::setWinConfigData() {
int ret = NO_ERROR;
mDpuData.reset();
if (mClientCompositionInfo.mHasCompositionLayer) {
if ((ret = configureOverlay(mClientCompositionInfo)) != NO_ERROR)
return ret;
}
if (mExynosCompositionInfo.mHasCompositionLayer) {
if ((ret = configureOverlay(mExynosCompositionInfo)) != NO_ERROR) {
/* TEST */
//return ret;
HWC_LOGE(this, "configureOverlay(ExynosCompositionInfo) is failed");
}
}
/* TODO loop for number of layers */
for (size_t i = 0; i < mLayers.size(); i++) {
if ((mLayers[i]->mExynosCompositionType == HWC2_COMPOSITION_EXYNOS) ||
(mLayers[i]->mExynosCompositionType == HWC2_COMPOSITION_CLIENT))
continue;
int32_t windowIndex = mLayers[i]->mWindowIndex;
if ((windowIndex < 0) || (windowIndex >= (int32_t)mDpuData.configs.size())) {
DISPLAY_LOGE("%s:: %zu layer has invalid windowIndex(%d)",
__func__, i, windowIndex);
return -EINVAL;
}
DISPLAY_LOGD(eDebugWinConfig, "%zu layer, config[%d]", i, windowIndex);
if ((ret = configureOverlay(mLayers[i], mDpuData.configs[windowIndex])) != NO_ERROR)
return ret;
}
return 0;
}
void ExynosDisplay::printDebugInfos(String8 &reason)
{
bool writeFile = true;
FILE *pFile = NULL;
struct timeval tv;
struct tm* localTime;
gettimeofday(&tv, NULL);
localTime = (struct tm*)localtime((time_t*)&tv.tv_sec);
reason.appendFormat("errFrameNumber: %" PRId64 " time:%02d-%02d %02d:%02d:%02d.%03lu(%lu)\n",
mErrorFrameCount,
localTime->tm_mon+1, localTime->tm_mday,
localTime->tm_hour, localTime->tm_min,
localTime->tm_sec, tv.tv_usec/1000,
((tv.tv_sec * 1000) + (tv.tv_usec / 1000)));
ALOGD("%s", reason.string());
if (mErrorFrameCount >= HWC_PRINT_FRAME_NUM)
writeFile = false;
else {
char filePath[128];
sprintf(filePath, "%s/%s_hwc_debug%d.dump", ERROR_LOG_PATH0, mDisplayName.string(), (int)mErrorFrameCount);
pFile = fopen(filePath, "wb");
if (pFile == NULL) {
ALOGE("Fail to open file %s, error: %s", filePath, strerror(errno));
sprintf(filePath, "%s/%s_hwc_debug%d.dump", ERROR_LOG_PATH1, mDisplayName.string(), (int)mErrorFrameCount);
pFile = fopen(filePath, "wb");
}
if (pFile == NULL) {
ALOGE("Fail to open file %s, error: %s", filePath, strerror(errno));
} else {
ALOGI("%s was created", filePath);
fwrite(reason.string(), 1, reason.size(), pFile);
}
}
mErrorFrameCount++;
android::String8 result;
result.appendFormat("Device mGeometryChanged(%" PRIx64 "), mGeometryChanged(%" PRIx64 "), mRenderingState(%d)\n",
mDevice->mGeometryChanged, mGeometryChanged, mRenderingState);
result.appendFormat("======================= dump composition infos ================================\n");
ExynosCompositionInfo clientCompInfo = mClientCompositionInfo;
ExynosCompositionInfo exynosCompInfo = mExynosCompositionInfo;
clientCompInfo.dump(result);
exynosCompInfo.dump(result);
ALOGD("%s", result.string());
if (pFile != NULL) {
fwrite(result.string(), 1, result.size(), pFile);
}
result.clear();
result.appendFormat("======================= dump exynos layers (%zu) ================================\n",
mLayers.size());
ALOGD("%s", result.string());
if (pFile != NULL) {
fwrite(result.string(), 1, result.size(), pFile);
}
result.clear();
for (uint32_t i = 0; i < mLayers.size(); i++) {
ExynosLayer *layer = mLayers[i];
layer->printLayer();
if (pFile != NULL) {
layer->dump(result);
fwrite(result.string(), 1, result.size(), pFile);
result.clear();
}
}
result.appendFormat("============================= dump win configs ===================================\n");
ALOGD("%s", result.string());
if (pFile != NULL) {
fwrite(result.string(), 1, result.size(), pFile);
}
result.clear();
for (size_t i = 0; i <= mDpuData.configs.size(); i++) {
ALOGD("config[%zu]", i);
printConfig(mDpuData.configs[i]);
if (pFile != NULL) {
result.appendFormat("config[%zu]\n", i);
dumpConfig(result, mDpuData.configs[i]);
fwrite(result.string(), 1, result.size(), pFile);
result.clear();
}
}
if (pFile != NULL) {
fclose(pFile);
}
}
int32_t ExynosDisplay::validateWinConfigData()
{
bool flagValidConfig = true;
int bufferStateCnt = 0;
for (size_t i = 0; i < mDpuData.configs.size(); i++) {
exynos_win_config_data &config = mDpuData.configs[i];
if (config.state == config.WIN_STATE_BUFFER) {
bool configInvalid = false;
/* multiple dma mapping */
for (size_t j = (i+1); j < mDpuData.configs.size(); j++) {
exynos_win_config_data &compare_config = mDpuData.configs[j];
if ((config.state == config.WIN_STATE_BUFFER) &&
(compare_config.state == compare_config.WIN_STATE_BUFFER)) {
if ((config.assignedMPP != NULL) &&
(config.assignedMPP == compare_config.assignedMPP)) {
DISPLAY_LOGE("WIN_CONFIG error: duplicated assignedMPP(%s) between win%zu, win%zu",
config.assignedMPP->mName.string(), i, j);
compare_config.state = compare_config.WIN_STATE_DISABLED;
flagValidConfig = false;
continue;
}
}
}
if ((config.src.x < 0) || (config.src.y < 0)||
(config.dst.x < 0) || (config.dst.y < 0)||
(config.src.w <= 0) || (config.src.h <= 0)||
(config.dst.w <= 0) || (config.dst.h <= 0)||
(config.dst.x + config.dst.w > (uint32_t)mXres) ||
(config.dst.y + config.dst.h > (uint32_t)mYres)) {
DISPLAY_LOGE("WIN_CONFIG error: invalid pos or size win%zu", i);
configInvalid = true;
}
if ((config.src.w > config.src.f_w) ||
(config.src.h > config.src.f_h)) {
DISPLAY_LOGE("WIN_CONFIG error: invalid size %zu, %d, %d, %d, %d", i,
config.src.w, config.src.f_w, config.src.h, config.src.f_h);
configInvalid = true;
}
/* Source alignment check */
ExynosMPP* exynosMPP = config.assignedMPP;
if (exynosMPP == NULL) {
DISPLAY_LOGE("WIN_CONFIG error: config %zu assigendMPP is NULL", i);
configInvalid = true;
} else {
uint32_t restrictionIdx = getRestrictionIndex(config.format);
uint32_t srcXAlign = exynosMPP->getSrcXOffsetAlign(restrictionIdx);
uint32_t srcYAlign = exynosMPP->getSrcYOffsetAlign(restrictionIdx);
uint32_t srcWidthAlign = exynosMPP->getSrcCropWidthAlign(restrictionIdx);
uint32_t srcHeightAlign = exynosMPP->getSrcCropHeightAlign(restrictionIdx);
if ((config.src.x % srcXAlign != 0) ||
(config.src.y % srcYAlign != 0) ||
(config.src.w % srcWidthAlign != 0) ||
(config.src.h % srcHeightAlign != 0))
{
DISPLAY_LOGE("WIN_CONFIG error: invalid src alignment : %zu, "\
"assignedMPP: %s, mppType:%d, format(%d), s_x: %d(%d), s_y: %d(%d), s_w : %d(%d), s_h : %d(%d)", i,
config.assignedMPP->mName.string(), exynosMPP->mLogicalType, config.format, config.src.x, srcXAlign,
config.src.y, srcYAlign, config.src.w, srcWidthAlign, config.src.h, srcHeightAlign);
configInvalid = true;
}
}
if (configInvalid) {
config.state = config.WIN_STATE_DISABLED;
flagValidConfig = false;
}
bufferStateCnt++;
}
if ((config.state == config.WIN_STATE_COLOR) ||
(config.state == config.WIN_STATE_CURSOR))
bufferStateCnt++;
}
if (bufferStateCnt == 0) {
DISPLAY_LOGE("WIN_CONFIG error: has no buffer window");
flagValidConfig = false;
}
if (flagValidConfig)
return NO_ERROR;
else
return -EINVAL;
}
/**
* @return int
*/
int ExynosDisplay::setDisplayWinConfigData() {
return 0;
}
bool ExynosDisplay::checkConfigChanged(const exynos_dpu_data &lastConfigsData, const exynos_dpu_data &newConfigsData)
{
if (exynosHWCControl.skipWinConfig == 0)
return true;
/* HWC doesn't skip WIN_CONFIG if other display is connected */
if (((mDevice->checkConnection(HWC_DISPLAY_EXTERNAL) == 1) ||
(mDevice->checkConnection(HWC_DISPLAY_VIRTUAL) == 1)) &&
(mDisplayId == HWC_DISPLAY_PRIMARY))
return true;
for (size_t i = 0; i < lastConfigsData.configs.size(); i++) {
if ((lastConfigsData.configs[i].state != newConfigsData.configs[i].state) ||
(lastConfigsData.configs[i].fd_idma[0] != newConfigsData.configs[i].fd_idma[0]) ||
(lastConfigsData.configs[i].fd_idma[1] != newConfigsData.configs[i].fd_idma[1]) ||
(lastConfigsData.configs[i].fd_idma[2] != newConfigsData.configs[i].fd_idma[2]) ||
(lastConfigsData.configs[i].dst.x != newConfigsData.configs[i].dst.x) ||
(lastConfigsData.configs[i].dst.y != newConfigsData.configs[i].dst.y) ||
(lastConfigsData.configs[i].dst.w != newConfigsData.configs[i].dst.w) ||
(lastConfigsData.configs[i].dst.h != newConfigsData.configs[i].dst.h) ||
(lastConfigsData.configs[i].src.x != newConfigsData.configs[i].src.x) ||
(lastConfigsData.configs[i].src.y != newConfigsData.configs[i].src.y) ||
(lastConfigsData.configs[i].src.w != newConfigsData.configs[i].src.w) ||
(lastConfigsData.configs[i].src.h != newConfigsData.configs[i].src.h) ||
(lastConfigsData.configs[i].format != newConfigsData.configs[i].format) ||
(lastConfigsData.configs[i].blending != newConfigsData.configs[i].blending) ||
(lastConfigsData.configs[i].plane_alpha != newConfigsData.configs[i].plane_alpha))
return true;
}
/* To cover buffer payload changed case */
for (size_t i = 0; i < mLayers.size(); i++) {
if(mLayers[i]->mLastLayerBuffer != mLayers[i]->mLayerBuffer)
return true;
}
return false;
}
int ExynosDisplay::checkConfigDstChanged(const exynos_dpu_data &lastConfigsData, const exynos_dpu_data &newConfigsData, uint32_t index)
{
if ((lastConfigsData.configs[index].state != newConfigsData.configs[index].state) ||
(lastConfigsData.configs[index].fd_idma[0] != newConfigsData.configs[index].fd_idma[0]) ||
(lastConfigsData.configs[index].fd_idma[1] != newConfigsData.configs[index].fd_idma[1]) ||
(lastConfigsData.configs[index].fd_idma[2] != newConfigsData.configs[index].fd_idma[2]) ||
(lastConfigsData.configs[index].format != newConfigsData.configs[index].format) ||
(lastConfigsData.configs[index].blending != newConfigsData.configs[index].blending) ||
(lastConfigsData.configs[index].plane_alpha != newConfigsData.configs[index].plane_alpha)) {
DISPLAY_LOGD(eDebugWindowUpdate, "damage region is skip, but other configuration except dst was changed");
DISPLAY_LOGD(eDebugWindowUpdate, "\tstate[%d, %d], fd[%d, %d], format[0x%8x, 0x%8x], blending[%d, %d], plane_alpha[%f, %f]",
lastConfigsData.configs[index].state, newConfigsData.configs[index].state,
lastConfigsData.configs[index].fd_idma[0], newConfigsData.configs[index].fd_idma[0],
lastConfigsData.configs[index].format, newConfigsData.configs[index].format,
lastConfigsData.configs[index].blending, newConfigsData.configs[index].blending,
lastConfigsData.configs[index].plane_alpha, newConfigsData.configs[index].plane_alpha);
return -1;
}
if ((lastConfigsData.configs[index].dst.x != newConfigsData.configs[index].dst.x) ||
(lastConfigsData.configs[index].dst.y != newConfigsData.configs[index].dst.y) ||
(lastConfigsData.configs[index].dst.w != newConfigsData.configs[index].dst.w) ||
(lastConfigsData.configs[index].dst.h != newConfigsData.configs[index].dst.h) ||
(lastConfigsData.configs[index].src.x != newConfigsData.configs[index].src.x) ||
(lastConfigsData.configs[index].src.y != newConfigsData.configs[index].src.y) ||
(lastConfigsData.configs[index].src.w != newConfigsData.configs[index].src.w) ||
(lastConfigsData.configs[index].src.h != newConfigsData.configs[index].src.h))
return 1;
else
return 0;
}
/**
* @return int
*/
int ExynosDisplay::deliverWinConfigData() {
ATRACE_CALL();
String8 errString;
int ret = NO_ERROR;
struct timeval tv_s, tv_e;
long timediff;
ret = validateWinConfigData();
if (ret != NO_ERROR) {
errString.appendFormat("Invalid WIN_CONFIG\n");
goto err;
}
for (size_t i = 0; i < mDpuData.configs.size(); i++) {
DISPLAY_LOGD(eDebugWinConfig|eDebugSkipStaicLayer, "deliver config[%zu]", i);
dumpConfig(mDpuData.configs[i]);
}
if (checkConfigChanged(mDpuData, mLastDpuData) == false) {
DISPLAY_LOGD(eDebugWinConfig, "Winconfig : same");
#ifndef DISABLE_FENCE
if (mLastRetireFence > 0) {
mDpuData.retire_fence =
hwcCheckFenceDebug(this, FENCE_TYPE_RETIRE, FENCE_IP_DPP,
hwc_dup(mLastRetireFence, this, FENCE_TYPE_RETIRE, FENCE_IP_DPP));
} else
mDpuData.retire_fence = -1;
#endif
ret = 0;
} else {
/* wait for 5 vsync */
int32_t waitTime = mVsyncPeriod / 1000000 * 5;
gettimeofday(&tv_s, NULL);
if (fence_valid(mLastRetireFence)) {
ATRACE_CALL();
if (sync_wait(mLastRetireFence, waitTime) < 0) {
DISPLAY_LOGE("%s:: mLastRetireFence(%d) is not released during (%d ms)",
__func__, mLastRetireFence, waitTime);
if (sync_wait(mLastRetireFence, 1000 - waitTime) < 0) {
DISPLAY_LOGE("%s:: mLastRetireFence sync wait error (%d)", __func__, mLastRetireFence);
}
else {
gettimeofday(&tv_e, NULL);
tv_e.tv_usec += (tv_e.tv_sec - tv_s.tv_sec) * 1000000;
timediff = tv_e.tv_usec - tv_s.tv_usec;
DISPLAY_LOGE("%s:: winconfig is delayed over 5 vysnc (fence:%d)(time:%ld)",
__func__, mLastRetireFence, timediff);
}
}
}
for (size_t i = 0; i < mDpuData.configs.size(); i++) {
setFenceInfo(mDpuData.configs[i].acq_fence, this,
FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_DPP, FENCE_TO);
}
if ((ret = mDisplayInterface->deliverWinConfigData()) < 0) {
errString.appendFormat("interface's deliverWinConfigData() failed: %s ret(%d)\n", strerror(errno), ret);
goto err;
} else {
mLastDpuData = mDpuData;
}
for (size_t i = 0; i < mDpuData.configs.size(); i++) {
setFenceInfo(mDpuData.configs[i].rel_fence, this,
FENCE_TYPE_SRC_RELEASE, FENCE_IP_DPP, FENCE_FROM);
}
setFenceInfo(mDpuData.retire_fence, this,
FENCE_TYPE_RETIRE, FENCE_IP_DPP, FENCE_FROM);
}
return ret;
err:
printDebugInfos(errString);
closeFences();
clearDisplay();
mDisplayInterface->setForcePanic();
return ret;
}
/**
* @return int
*/
int ExynosDisplay::setReleaseFences() {
int release_fd = -1;
String8 errString;
/*
* Close release fence for client target buffer
* SurfaceFlinger doesn't get release fence for client target buffer
*/
if ((mClientCompositionInfo.mHasCompositionLayer) &&
(mClientCompositionInfo.mWindowIndex >= 0) &&
(mClientCompositionInfo.mWindowIndex < (int32_t)mDpuData.configs.size())) {
exynos_win_config_data &config = mDpuData.configs[mClientCompositionInfo.mWindowIndex];
for (int i = mClientCompositionInfo.mFirstIndex; i <= mClientCompositionInfo.mLastIndex; i++) {
if (mLayers[i]->mExynosCompositionType != HWC2_COMPOSITION_CLIENT) {
if(mLayers[i]->mOverlayPriority < ePriorityHigh) {
errString.appendFormat("%d layer compositionType is not client(%d)\n", i, mLayers[i]->mExynosCompositionType);
goto err;
} else {
continue;
}
}
if (mDisplayId == HWC_DISPLAY_VIRTUAL)
mLayers[i]->mReleaseFence = -1;
else
mLayers[i]->mReleaseFence =
hwcCheckFenceDebug(this, FENCE_TYPE_SRC_RELEASE, FENCE_IP_DPP,
hwc_dup(config.rel_fence, this,
FENCE_TYPE_SRC_RELEASE, FENCE_IP_DPP));
}
config.rel_fence = fence_close(config.rel_fence, this,
FENCE_TYPE_SRC_RELEASE, FENCE_IP_FB);
}
// DPU doesn't close acq_fence, HWC should close it.
for (size_t i = 0; i < mDpuData.configs.size(); i++) {
if (mDpuData.configs[i].acq_fence != -1)
fence_close(mDpuData.configs[i].acq_fence, this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_DPP);
mDpuData.configs[i].acq_fence = -1;
}
// DPU doesn't close rel_fence of readback buffer, HWC should close it
if (mDpuData.readback_info.rel_fence >= 0) {
mDpuData.readback_info.rel_fence =
fence_close(mDpuData.readback_info.rel_fence, this,
FENCE_TYPE_READBACK_RELEASE, FENCE_IP_FB);
}
for (size_t i = 0; i < mLayers.size(); i++) {
if ((mLayers[i]->mExynosCompositionType == HWC2_COMPOSITION_CLIENT) ||
(mLayers[i]->mExynosCompositionType == HWC2_COMPOSITION_EXYNOS))
continue;
if ((mLayers[i]->mWindowIndex < 0) ||
(mLayers[i]->mWindowIndex >= mDpuData.configs.size())) {
errString.appendFormat("%s:: layer[%zu] has invalid window index(%d)\n",
__func__, i, mLayers[i]->mWindowIndex);
goto err;
}
exynos_win_config_data &config = mDpuData.configs[mLayers[i]->mWindowIndex];
if (mLayers[i]->mOtfMPP != NULL) {
mLayers[i]->mOtfMPP->setHWStateFence(-1);
}
if (mLayers[i]->mM2mMPP != NULL) {
if (mLayers[i]->mM2mMPP->mUseM2MSrcFence)
mLayers[i]->mReleaseFence = mLayers[i]->mM2mMPP->getSrcReleaseFence(0);
else {
mLayers[i]->mReleaseFence = hwcCheckFenceDebug(this, FENCE_TYPE_SRC_RELEASE, FENCE_IP_DPP,
hwc_dup(config.rel_fence, this,
FENCE_TYPE_SRC_RELEASE, FENCE_IP_LAYER));
}
mLayers[i]->mM2mMPP->resetSrcReleaseFence();
#ifdef DISABLE_FENCE
mLayers[i]->mM2mMPP->setDstAcquireFence(-1);
#else
DISPLAY_LOGD(eDebugFence, "m2m : win_index(%d), releaseFencefd(%d)",
mLayers[i]->mWindowIndex, config.rel_fence);
if (config.rel_fence > 0) {
release_fd = config.rel_fence;
if (release_fd >= 0) {
setFenceName(release_fd,
this, FENCE_TYPE_DST_ACQUIRE, FENCE_IP_DPP, FENCE_FROM, true);
mLayers[i]->mM2mMPP->setDstAcquireFence(release_fd);
} else {
DISPLAY_LOGE("fail to dup, ret(%d, %s)", errno, strerror(errno));
mLayers[i]->mM2mMPP->setDstAcquireFence(-1);
}
} else {
mLayers[i]->mM2mMPP->setDstAcquireFence(-1);
}
DISPLAY_LOGD(eDebugFence, "mM2mMPP is used, layer[%zu].releaseFencefd(%d)",
i, mLayers[i]->mReleaseFence);
#endif
} else {
#ifdef DISABLE_FENCE
mLayers[i]->mReleaseFence = -1;
#else
DISPLAY_LOGD(eDebugFence, "other : win_index(%d), releaseFencefd(%d)",
mLayers[i]->mWindowIndex, config.rel_fence);
if (config.rel_fence > 0) {
release_fd = hwcCheckFenceDebug(this, FENCE_TYPE_SRC_RELEASE, FENCE_IP_DPP, config.rel_fence);
if (release_fd >= 0)
mLayers[i]->mReleaseFence = release_fd;
else {
DISPLAY_LOGE("fail to dup, ret(%d, %s)", errno, strerror(errno));
mLayers[i]->mReleaseFence = -1;
}
} else {
mLayers[i]->mReleaseFence = -1;
}
DISPLAY_LOGD(eDebugFence, "Direct overlay layer[%zu].releaseFencefd(%d)",
i, mLayers[i]->mReleaseFence);
#endif
}
}
if (mExynosCompositionInfo.mHasCompositionLayer) {
if (mExynosCompositionInfo.mM2mMPP == NULL)
{
errString.appendFormat("There is exynos composition, but m2mMPP is NULL\n");
goto err;
}
if (mUseDpu &&
((mExynosCompositionInfo.mWindowIndex < 0) ||
(mExynosCompositionInfo.mWindowIndex >= (int32_t)mDpuData.configs.size()))) {
errString.appendFormat("%s:: exynosComposition has invalid window index(%d)\n",
__func__, mExynosCompositionInfo.mWindowIndex);
goto err;
}
exynos_win_config_data &config = mDpuData.configs[mExynosCompositionInfo.mWindowIndex];
for (int i = mExynosCompositionInfo.mFirstIndex; i <= mExynosCompositionInfo.mLastIndex; i++) {
/* break when only framebuffer target is assigned on ExynosCompositor */
if (i == -1)
break;
if (mLayers[i]->mExynosCompositionType != HWC2_COMPOSITION_EXYNOS) {
errString.appendFormat("%d layer compositionType is not exynos(%d)\n", i, mLayers[i]->mExynosCompositionType);
goto err;
}
if (mExynosCompositionInfo.mM2mMPP->mUseM2MSrcFence)
mLayers[i]->mReleaseFence =
mExynosCompositionInfo.mM2mMPP->getSrcReleaseFence(i-mExynosCompositionInfo.mFirstIndex);
else {
mLayers[i]->mReleaseFence =
hwc_dup(config.rel_fence,
this, FENCE_TYPE_SRC_RELEASE, FENCE_IP_LAYER);
}
DISPLAY_LOGD(eDebugFence, "exynos composition layer[%d].releaseFencefd(%d)",
i, mLayers[i]->mReleaseFence);
}
mExynosCompositionInfo.mM2mMPP->resetSrcReleaseFence();
if(mUseDpu) {
#ifdef DISABLE_FENCE
mExynosCompositionInfo.mM2mMPP->setDstAcquireFence(-1);
#else
if (config.rel_fence > 0) {
setFenceName(config.rel_fence,
this, FENCE_TYPE_DST_ACQUIRE, FENCE_IP_DPP, FENCE_FROM, true);
mExynosCompositionInfo.mM2mMPP->setDstAcquireFence(config.rel_fence);
} else {
mExynosCompositionInfo.mM2mMPP->setDstAcquireFence(-1);
}
#endif
}
}
return 0;
err:
printDebugInfos(errString);
closeFences();
mDisplayInterface->setForcePanic();
return -EINVAL;
}
/**
* If display uses outbuf and outbuf is invalid, this function return false.
* Otherwise, this function return true.
* If outbuf is invalid, display should handle fence of layers.
*/
bool ExynosDisplay::checkFrameValidation() {
return true;
}
int32_t ExynosDisplay::acceptDisplayChanges() {
int32_t type = 0;
if (mRenderingState != RENDERING_STATE_VALIDATED) {
DISPLAY_LOGE("%s:: display is not validated : %d", __func__, mRenderingState);
return HWC2_ERROR_NOT_VALIDATED;
}
for (size_t i = 0; i < mLayers.size(); i++) {
if (mLayers[i] != NULL) {
HDEBUGLOGD(eDebugDefault, "%s, Layer %zu : %d, %d", __func__, i,
mLayers[i]->mExynosCompositionType, mLayers[i]->mValidateCompositionType);
type = getLayerCompositionTypeForValidationType(i);
/* update compositionType
* SF updates their state and doesn't call back into HWC HAL
*/
mLayers[i]->mCompositionType = type;
mLayers[i]->mExynosCompositionType = mLayers[i]->mValidateCompositionType;
}
else {
HWC_LOGE(this, "Layer %zu is NULL", i);
}
}
mRenderingState = RENDERING_STATE_ACCEPTED_CHANGE;
return HWC2_ERROR_NONE;
}
int32_t ExynosDisplay::createLayer(hwc2_layer_t* outLayer) {
Mutex::Autolock lock(mDRMutex);
if (mPlugState == false) {
DISPLAY_LOGI("%s : skip createLayer. Display is already disconnected", __func__);
return HWC2_ERROR_BAD_DISPLAY;
}
/* TODO : Implementation here */
ExynosLayer *layer = new ExynosLayer(this);
/* TODO : Sort sequence should be added to somewhere */
mLayers.add((ExynosLayer*)layer);
/* TODO : Set z-order to max, check outLayer address? */
layer->setLayerZOrder(1000);
*outLayer = (hwc2_layer_t)layer;
setGeometryChanged(GEOMETRY_DISPLAY_LAYER_ADDED);
return HWC2_ERROR_NONE;
}
int32_t ExynosDisplay::getActiveConfig(hwc2_config_t* outConfig)
{
Mutex::Autolock lock(mDisplayMutex);
return getActiveConfigInternal(outConfig);
}
int32_t ExynosDisplay::getActiveConfigInternal(hwc2_config_t* outConfig)
{
*outConfig = mActiveConfig;
return HWC2_ERROR_NONE;
}
int32_t ExynosDisplay::getLayerCompositionTypeForValidationType(uint32_t layerIndex)
{
int32_t type = -1;
if (layerIndex >= mLayers.size())
{
DISPLAY_LOGE("invalid layer index (%d)", layerIndex);
return type;
}
if ((mLayers[layerIndex]->mValidateCompositionType == HWC2_COMPOSITION_CLIENT) &&
(mClientCompositionInfo.mSkipFlag) &&
(mClientCompositionInfo.mFirstIndex <= (int32_t)layerIndex) &&
((int32_t)layerIndex <= mClientCompositionInfo.mLastIndex)) {
type = HWC2_COMPOSITION_DEVICE;
} else if (mLayers[layerIndex]->mValidateCompositionType == HWC2_COMPOSITION_EXYNOS) {
type = HWC2_COMPOSITION_DEVICE;
} else if ((mLayers[layerIndex]->mCompositionType == HWC2_COMPOSITION_CURSOR) &&
(mLayers[layerIndex]->mValidateCompositionType == HWC2_COMPOSITION_DEVICE)) {
if (mDisplayControl.cursorSupport == true)
type = HWC2_COMPOSITION_CURSOR;
else
type = HWC2_COMPOSITION_DEVICE;
} else if ((mLayers[layerIndex]->mCompositionType == HWC2_COMPOSITION_SOLID_COLOR) &&
(mLayers[layerIndex]->mValidateCompositionType == HWC2_COMPOSITION_DEVICE)) {
type = HWC2_COMPOSITION_SOLID_COLOR;
} else {
type = mLayers[layerIndex]->mValidateCompositionType;
}
return type;
}
int32_t ExynosDisplay::getChangedCompositionTypes(
uint32_t* outNumElements, hwc2_layer_t* outLayers,
int32_t* /*hwc2_composition_t*/ outTypes) {
if (mRenderingState != RENDERING_STATE_VALIDATED) {
DISPLAY_LOGE("%s:: display is not validated : %d", __func__, mRenderingState);
return HWC2_ERROR_NOT_VALIDATED;
}
uint32_t count = 0;
int32_t type = 0;
for (size_t i = 0; i < mLayers.size(); i++) {
DISPLAY_LOGD(eDebugHWC, "[%zu] layer: mCompositionType(%d), mValidateCompositionType(%d), mExynosCompositionType(%d), skipFlag(%d)",
i, mLayers[i]->mCompositionType, mLayers[i]->mValidateCompositionType,
mLayers[i]->mExynosCompositionType, mClientCompositionInfo.mSkipFlag);
type = getLayerCompositionTypeForValidationType(i);
if (type != mLayers[i]->mCompositionType) {
if (outLayers == NULL || outTypes == NULL) {
count++;
}
else {
if (count < *outNumElements) {
outLayers[count] = (hwc2_layer_t)mLayers[i];
outTypes[count] = type;
count++;
} else {
DISPLAY_LOGE("array size is not valid (%d, %d)", count, *outNumElements);
String8 errString;
errString.appendFormat("array size is not valid (%d, %d)", count, *outNumElements);
printDebugInfos(errString);
return -1;
}
}
}
}
if ((outLayers == NULL) || (outTypes == NULL))
*outNumElements = count;
return HWC2_ERROR_NONE;
}
int32_t ExynosDisplay::getClientTargetSupport(
uint32_t width, uint32_t height,
int32_t /*android_pixel_format_t*/ format,
int32_t /*android_dataspace_t*/ dataspace) {
if (width != mXres)
return HWC2_ERROR_UNSUPPORTED;
if (height != mYres)
return HWC2_ERROR_UNSUPPORTED;
if (format != HAL_PIXEL_FORMAT_RGBA_8888)
return HWC2_ERROR_UNSUPPORTED;
if (dataspace != HAL_DATASPACE_UNKNOWN)
return HWC2_ERROR_UNSUPPORTED;
return HWC2_ERROR_NONE;
}
int32_t ExynosDisplay::getColorModes(
uint32_t* outNumModes,
int32_t* /*android_color_mode_t*/ outModes) {
return mDisplayInterface->getColorModes(outNumModes, outModes);
}
int32_t ExynosDisplay::getDisplayAttribute(
hwc2_config_t config,
int32_t /*hwc2_attribute_t*/ attribute, int32_t* outValue) {
switch (attribute) {
case HWC2_ATTRIBUTE_VSYNC_PERIOD:
*outValue = mDisplayConfigs[config].vsyncPeriod;
break;
case HWC2_ATTRIBUTE_WIDTH:
*outValue = mDisplayConfigs[config].width;
break;
case HWC2_ATTRIBUTE_HEIGHT:
*outValue = mDisplayConfigs[config].height;
break;
case HWC2_ATTRIBUTE_DPI_X:
*outValue = mDisplayConfigs[config].Xdpi;
break;
case HWC2_ATTRIBUTE_DPI_Y:
*outValue = mDisplayConfigs[config].Ydpi;
break;
case HWC2_ATTRIBUTE_CONFIG_GROUP:
*outValue = mDisplayConfigs[config].groupId;
break;
default:
ALOGE("unknown display attribute %u", attribute);
return HWC2_ERROR_BAD_CONFIG;
}
return HWC2_ERROR_NONE;
}
int32_t ExynosDisplay::getDisplayConfigs(
uint32_t* outNumConfigs,
hwc2_config_t* outConfigs) {
return mDisplayInterface->getDisplayConfigs(outNumConfigs, outConfigs);
}
int32_t ExynosDisplay::getDisplayName(uint32_t* outSize, char* outName)
{
if (outName == NULL) {
*outSize = mDisplayName.size();
return HWC2_ERROR_NONE;
}
uint32_t strSize = mDisplayName.size();
if (*outSize < strSize) {
DISPLAY_LOGE("Invalide outSize(%d), mDisplayName.size(%d)",
*outSize, strSize);
strSize = *outSize;
}
std::strncpy(outName, mDisplayName, strSize);
*outSize = strSize;
return HWC2_ERROR_NONE;
}
int32_t ExynosDisplay::getDisplayRequests(
int32_t* /*hwc2_display_request_t*/ outDisplayRequests,
uint32_t* outNumElements, hwc2_layer_t* outLayers,
int32_t* /*hwc2_layer_request_t*/ outLayerRequests) {
/*
* This function doesn't check mRenderingState
* This can be called in the below rendering state
* RENDERING_STATE_PRESENTED: when it is called by canSkipValidate()
* RENDERING_STATE_ACCEPTED_CHANGE: when it is called by SF
* RENDERING_STATE_VALIDATED: when it is called by validateDisplay()
*/
String8 errString;
*outDisplayRequests = 0;
uint32_t requestNum = 0;
if (mClientCompositionInfo.mHasCompositionLayer == true) {
if ((mClientCompositionInfo.mFirstIndex < 0) ||
(mClientCompositionInfo.mFirstIndex >= (int)mLayers.size()) ||
(mClientCompositionInfo.mLastIndex < 0) ||
(mClientCompositionInfo.mLastIndex >= (int)mLayers.size())) {
errString.appendFormat("%s:: mClientCompositionInfo.mHasCompositionLayer is true "
"but index is not valid (firstIndex: %d, lastIndex: %d)\n",
__func__, mClientCompositionInfo.mFirstIndex,
mClientCompositionInfo.mLastIndex);
goto err;
}
for (int32_t i = mClientCompositionInfo.mFirstIndex; i < mClientCompositionInfo.mLastIndex; i++) {
ExynosLayer *layer = mLayers[i];
if (layer->mOverlayPriority >= ePriorityHigh) {
if ((outLayers != NULL) && (outLayerRequests != NULL)) {
if (requestNum >= *outNumElements)
return -1;
outLayers[requestNum] = (hwc2_layer_t)layer;
outLayerRequests[requestNum] = HWC2_LAYER_REQUEST_CLEAR_CLIENT_TARGET;
}
requestNum++;
}
}
}
if ((outLayers == NULL) || (outLayerRequests == NULL))
*outNumElements = requestNum;
return HWC2_ERROR_NONE;
err:
printDebugInfos(errString);
*outNumElements = 0;
mDisplayInterface->setForcePanic();
return -EINVAL;
}
int32_t ExynosDisplay::getDisplayType(
int32_t* /*hwc2_display_type_t*/ outType) {
switch (mType) {
case HWC_DISPLAY_PRIMARY:
case HWC_DISPLAY_EXTERNAL:
*outType = HWC2_DISPLAY_TYPE_PHYSICAL;
return HWC2_ERROR_NONE;
case HWC_DISPLAY_VIRTUAL:
*outType = HWC2_DISPLAY_TYPE_VIRTUAL;
return HWC2_ERROR_NONE;
default:
DISPLAY_LOGE("Invalid display type(%d)", mType);
*outType = HWC2_DISPLAY_TYPE_INVALID;
return HWC2_ERROR_NONE;
}
}
int32_t ExynosDisplay::getDozeSupport(
int32_t* outSupport) {
if (mDisplayInterface->isDozeModeAvailable()) {
*outSupport = 1;
} else {
*outSupport = 0;
}
return 0;
}
int32_t ExynosDisplay::getReleaseFences(
uint32_t* outNumElements,
hwc2_layer_t* outLayers, int32_t* outFences) {
Mutex::Autolock lock(mDisplayMutex);
if (outLayers == NULL || outFences == NULL)
{
uint32_t deviceLayerNum = 0;
deviceLayerNum = mLayers.size();
*outNumElements = deviceLayerNum;
} else {
uint32_t deviceLayerNum = 0;
for (size_t i = 0; i < mLayers.size(); i++) {
outLayers[deviceLayerNum] = (hwc2_layer_t)mLayers[i];
outFences[deviceLayerNum] = mLayers[i]->mReleaseFence;
/*
* layer's release fence will be closed by caller of this function.
* HWC should not close this fence after this function is returned.
*/
mLayers[i]->mReleaseFence = -1;
DISPLAY_LOGD(eDebugHWC, "[%zu] layer deviceLayerNum(%d), release fence: %d", i, deviceLayerNum, outFences[deviceLayerNum]);
deviceLayerNum++;
if (mLayers[i]->mExynosCompositionType == HWC2_COMPOSITION_EXYNOS) {
setFenceName(mLayers[i]->mReleaseFence, FENCE_LAYER_RELEASE_DPP);
} else if (mLayers[i]->mCompositionType == HWC2_COMPOSITION_CLIENT) {
setFenceName(mLayers[i]->mReleaseFence, FENCE_LAYER_RELEASE_DPP);
} else {
setFenceName(mLayers[i]->mReleaseFence, FENCE_LAYER_RELEASE_DPP);
}
}
}
return 0;
}
int32_t ExynosDisplay::canSkipValidate() {
if (exynosHWCControl.skipResourceAssign == 0)
return SKIP_ERR_CONFIG_DISABLED;
/* This is first frame. validateDisplay can't be skipped */
if (mRenderingState == RENDERING_STATE_NONE)
return SKIP_ERR_FIRST_FRAME;
if (mDevice->mGeometryChanged != 0) {
/* validateDisplay() should be called */
return SKIP_ERR_GEOMETRY_CHAGNED;
} else {
for (uint32_t i = 0; i < mLayers.size(); i++) {
if (getLayerCompositionTypeForValidationType(i) ==
HWC2_COMPOSITION_CLIENT) {
return SKIP_ERR_HAS_CLIENT_COMP;
}
}
if ((mClientCompositionInfo.mSkipStaticInitFlag == true) &&
(mClientCompositionInfo.mSkipFlag == true)) {
if (skipStaticLayerChanged(mClientCompositionInfo) == true)
return SKIP_ERR_SKIP_STATIC_CHANGED;
}
/*
* If there is hwc2_layer_request_t
* validateDisplay() can't be skipped
*/
int32_t displayRequests = 0;
uint32_t outNumRequests = 0;
if ((getDisplayRequests(&displayRequests, &outNumRequests, NULL, NULL) != NO_ERROR) ||
(outNumRequests != 0))
return SKIP_ERR_HAS_REQUEST;
}
return NO_ERROR;
}
int32_t ExynosDisplay::presentDisplay(int32_t* outRetireFence) {
ATRACE_CALL();
gettimeofday(&updateTimeInfo.lastPresentTime, NULL);
int ret = 0;
String8 errString;
Mutex::Autolock lock(mDisplayMutex);
if (mPauseDisplay) {
closeFencesForSkipFrame(RENDERING_STATE_PRESENTED);
*outRetireFence = -1;
mRenderingState = RENDERING_STATE_PRESENTED;
return HWC2_ERROR_NONE;
}
/*
* buffer handle, dataspace were set by setClientTarget() after validateDisplay
* ExynosImage should be set again according to changed handle and dataspace
*/
exynos_image src_img;
exynos_image dst_img;
setCompositionTargetExynosImage(COMPOSITION_CLIENT, &src_img, &dst_img);
mClientCompositionInfo.setExynosImage(src_img, dst_img);
mClientCompositionInfo.setExynosMidImage(dst_img);
funcReturnCallback presentRetCallback([&]() {
if (ret != HWC2_ERROR_NOT_VALIDATED)
presentPostProcessing();
});
if (mSkipFrame) {
ALOGI("[%d] presentDisplay is skipped by mSkipFrame", mDisplayId);
closeFencesForSkipFrame(RENDERING_STATE_PRESENTED);
setGeometryChanged(GEOMETRY_DISPLAY_FORCE_VALIDATE);
*outRetireFence = -1;
for (size_t i=0; i < mLayers.size(); i++) {
mLayers[i]->mReleaseFence = -1;
}
if (mRenderingState == RENDERING_STATE_NONE) {
ALOGD("\tThis is the first frame after power on");
ret = HWC2_ERROR_NONE;
} else {
ALOGD("\tThis is the second frame after power on");
ret = HWC2_ERROR_NOT_VALIDATED;
}
mRenderingState = RENDERING_STATE_PRESENTED;
mDevice->invalidate();
return ret;
}
if (mRenderingState != RENDERING_STATE_ACCEPTED_CHANGE) {
/*
* presentDisplay() can be called before validateDisplay()
* when HWC2_CAPABILITY_SKIP_VALIDATE is supported
*/
#ifndef HWC_SKIP_VALIDATE
DISPLAY_LOGE("%s:: Skip validate is not supported. Invalid rendering state : %d", __func__, mRenderingState);
goto err;
#endif
if ((mRenderingState != RENDERING_STATE_NONE) &&
(mRenderingState != RENDERING_STATE_PRESENTED)) {
DISPLAY_LOGE("%s:: invalid rendering state : %d", __func__, mRenderingState);
goto err;
}
if (mDevice->canSkipValidate() == false)
goto not_validated;
else {
// Reset current frame flags for Fence Tracer
resetFenceCurFlag(this);
for (size_t i=0; i < mLayers.size(); i++) {
// Layer's acquire fence from SF
setFenceInfo(mLayers[i]->mAcquireFence, this,
FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_LAYER, FENCE_FROM);
}
DISPLAY_LOGD(eDebugSkipValidate, "validate is skipped");
}
if (updateColorConversionInfo() != NO_ERROR) {
DISPLAY_LOGE("%s:: updateColorConversionInfo() fail, ret(%d)",
__func__, ret);
goto err;
}
if (mDisplayControl.earlyStartMPP == true) {
/*
* HWC should update performanceInfo when validate is skipped
* HWC excludes the layer from performance calculation
* if there is no buffer update. (using ExynosMPP::canSkipProcessing())
* Therefore performanceInfo should be calculated again if the buffer is updated.
*/
if ((ret = mDevice->mResourceManager->deliverPerformanceInfo()) != NO_ERROR) {
DISPLAY_LOGE("deliverPerformanceInfo() error (%d) in validateSkip case", ret);
}
startPostProcessing();
}
}
mDpuData.reset();
if ((mLayers.size() == 0) &&
(mDisplayId != HWC_DISPLAY_VIRTUAL)) {
clearDisplay(mDpuData.enable_readback);
*outRetireFence = -1;
mLastRetireFence = fence_close(mLastRetireFence, this,
FENCE_TYPE_RETIRE, FENCE_IP_DPP);
mRenderingState = RENDERING_STATE_PRESENTED;
ret = 0;
return ret;
}
if (!checkFrameValidation()) {
clearDisplay();
*outRetireFence = -1;
mLastRetireFence = fence_close(mLastRetireFence, this,
FENCE_TYPE_RETIRE, FENCE_IP_DPP);
mRenderingState = RENDERING_STATE_PRESENTED;
return ret;
}
if ((mDisplayControl.earlyStartMPP == false) &&
((ret = doExynosComposition()) != NO_ERROR)) {
errString.appendFormat("exynosComposition fail (%d)\n", ret);
goto err;
}
// loop for all layer
for (size_t i=0; i < mLayers.size(); i++) {
/* mAcquireFence is updated, Update image info */
struct exynos_image srcImg, dstImg, midImg;
mLayers[i]->setSrcExynosImage(&srcImg);
mLayers[i]->setDstExynosImage(&dstImg);
mLayers[i]->setExynosImage(srcImg, dstImg);
if (mLayers[i]->mExynosCompositionType == HWC2_COMPOSITION_CLIENT) {
mLayers[i]->mReleaseFence = -1;
mLayers[i]->mAcquireFence =
fence_close(mLayers[i]->mAcquireFence, this,
FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_LAYER);
} else if (mLayers[i]->mExynosCompositionType == HWC2_COMPOSITION_EXYNOS) {
continue;
} else {
if (mLayers[i]->mOtfMPP != NULL) {
mLayers[i]->mOtfMPP->requestHWStateChange(MPP_HW_STATE_RUNNING);
}
if ((mDisplayControl.earlyStartMPP == false) &&
(mLayers[i]->mExynosCompositionType == HWC2_COMPOSITION_DEVICE) &&
(mLayers[i]->mM2mMPP != NULL)) {
ExynosMPP *m2mMpp = mLayers[i]->mM2mMPP;
srcImg = mLayers[i]->mSrcImg;
midImg = mLayers[i]->mMidImg;
m2mMpp->requestHWStateChange(MPP_HW_STATE_RUNNING);
if ((ret = m2mMpp->doPostProcessing(srcImg, midImg)) != NO_ERROR) {
HWC_LOGE(this, "%s:: doPostProcessing() failed, layer(%zu), ret(%d)",
__func__, i, ret);
errString.appendFormat("%s:: doPostProcessing() failed, layer(%zu), ret(%d)\n",
__func__, i, ret);
goto err;
} else {
/* This should be closed by lib for each resource */
mLayers[i]->mAcquireFence = -1;
}
}
}
}
if ((ret = setWinConfigData()) != NO_ERROR) {
errString.appendFormat("setWinConfigData fail (%d)\n", ret);
goto err;
}
if ((ret = handleStaticLayers(mClientCompositionInfo)) != NO_ERROR) {
mClientCompositionInfo.mSkipStaticInitFlag = false;
errString.appendFormat("handleStaticLayers error\n");
goto err;
}
handleWindowUpdate();
setDisplayWinConfigData();
if ((ret = deliverWinConfigData()) != NO_ERROR) {
HWC_LOGE(this, "%s:: fail to deliver win_config (%d)", __func__, ret);
if (mDpuData.retire_fence > 0)
fence_close(mDpuData.retire_fence, this, FENCE_TYPE_RETIRE, FENCE_IP_DPP);
mDpuData.retire_fence = -1;
}
setReleaseFences();
if (mDpuData.retire_fence != -1) {
#ifdef DISABLE_FENCE
if (mDpuData.retire_fence >= 0)
fence_close(mDpuData.retire_fence, this, FENCE_TYPE_RETIRE, FENCE_IP_DPP);
*outRetireFence = -1;
#else
*outRetireFence =
hwcCheckFenceDebug(this, FENCE_TYPE_RETIRE, FENCE_IP_DPP, mDpuData.retire_fence);
#endif
setFenceInfo(mDpuData.retire_fence, this,
FENCE_TYPE_RETIRE, FENCE_IP_LAYER, FENCE_TO);
} else
*outRetireFence = -1;
/* Update last retire fence */
mLastRetireFence = fence_close(mLastRetireFence, this, FENCE_TYPE_RETIRE, FENCE_IP_DPP);
mLastRetireFence = hwc_dup((*outRetireFence), this, FENCE_TYPE_RETIRE, FENCE_IP_DPP);
setFenceName(mLastRetireFence, this, FENCE_TYPE_RETIRE, FENCE_IP_DPP, FENCE_DUP, true);
setFenceName(mLastRetireFence, FENCE_RETIRE);
increaseMPPDstBufIndex();
/* Check all of acquireFence are closed */
for (size_t i=0; i < mLayers.size(); i++) {
if (mLayers[i]->mAcquireFence != -1) {
DISPLAY_LOGE("layer[%zu] fence(%d) type(%d, %d, %d) is not closed",
i, mLayers[i]->mAcquireFence,
mLayers[i]->mCompositionType,
mLayers[i]->mExynosCompositionType,
mLayers[i]->mValidateCompositionType);
if (mLayers[i]->mM2mMPP != NULL)
DISPLAY_LOGE("\t%s is assigned", mLayers[i]->mM2mMPP->mName.string());
if (mLayers[i]->mAcquireFence > 0)
fence_close(mLayers[i]->mAcquireFence, this,
FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_LAYER);
mLayers[i]->mAcquireFence = -1;
}
}
if (mExynosCompositionInfo.mAcquireFence >= 0) {
DISPLAY_LOGE("mExynosCompositionInfo mAcquireFence(%d) is not initialized", mExynosCompositionInfo.mAcquireFence);
fence_close(mExynosCompositionInfo.mAcquireFence, this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_G2D);
mExynosCompositionInfo.mAcquireFence = -1;
}
if (mClientCompositionInfo.mAcquireFence >= 0) {
DISPLAY_LOGE("mClientCompositionInfo mAcquireFence(%d) is not initialized", mClientCompositionInfo.mAcquireFence);
fence_close(mClientCompositionInfo.mAcquireFence, this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_FB);
mClientCompositionInfo.mAcquireFence = -1;
}
/* All of release fences are tranferred */
for (size_t i=0; i < mLayers.size(); i++) {
setFenceInfo(mLayers[i]->mReleaseFence, this,
FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_LAYER, FENCE_TO);
}
doPostProcessing();
if (!mDevice->validateFences(this)){
String8 errString;
errString.appendFormat("%s:: validate fence failed. \n", __func__);
printDebugInfos(errString);
}
mDpuData.reset();
mRenderingState = RENDERING_STATE_PRESENTED;
return ret;
err:
printDebugInfos(errString);
closeFences();
*outRetireFence = -1;
mLastRetireFence = -1;
mRenderingState = RENDERING_STATE_PRESENTED;
setGeometryChanged(GEOMETRY_ERROR_CASE);
mLastDpuData.reset();
mClientCompositionInfo.mSkipStaticInitFlag = false;
mExynosCompositionInfo.mSkipStaticInitFlag = false;
mDpuData.reset();
if (!mDevice->validateFences(this)){
errString.appendFormat("%s:: validate fence failed. \n", __func__);
printDebugInfos(errString);
}
mDisplayInterface->setForcePanic();
ret = -EINVAL;
return ret;
not_validated:
DISPLAY_LOGD(eDebugSkipValidate, "display need validate");
mRenderingState = RENDERING_STATE_NONE;
ret = HWC2_ERROR_NOT_VALIDATED;
return ret;
}
int32_t ExynosDisplay::presentPostProcessing()
{
setReadbackBufferInternal(NULL, -1);
if (mDpuData.enable_readback)
mDevice->signalReadbackDone();
mDpuData.enable_readback = false;
return NO_ERROR;
}
int32_t ExynosDisplay::setActiveConfig(hwc2_config_t config)
{
Mutex::Autolock lock(mDisplayMutex);
return setActiveConfigInternal(config);
}
int32_t ExynosDisplay::setActiveConfigInternal(hwc2_config_t config)
{
if(isBadConfig(config))
return HWC2_ERROR_BAD_CONFIG;
/* Don't skip when power was off */
if(!mSkipFrame && needNotChangeConfig(config))
return HWC2_ERROR_NONE;
ALOGI("%s : %dx%d, %dms, %d Xdpi, %d Ydpi", __func__,
mXres, mYres, mVsyncPeriod, mXdpi, mYdpi);
ALOGI("(requested %d) : %dx%d, %dms, %d Xdpi, %d Ydpi", config,
mDisplayConfigs[config].width, mDisplayConfigs[config].height, mDisplayConfigs[config].vsyncPeriod,
mDisplayConfigs[config].Xdpi, mDisplayConfigs[config].Ydpi);
if (mDisplayInterface->setActiveConfig(config) < 0) {
ALOGE("%s bad config request", __func__);
return HWC2_ERROR_BAD_CONFIG;
}
updateInternalDisplayConfigVariables(config);
return HWC2_ERROR_NONE;
}
int32_t ExynosDisplay::setClientTarget(
buffer_handle_t target,
int32_t acquireFence, int32_t /*android_dataspace_t*/ dataspace) {
private_handle_t *handle = NULL;
if (target != NULL)
handle = private_handle_t::dynamicCast(target);
#ifdef DISABLE_FENCE
if (acquireFence >= 0)
fence_close(acquireFence, this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_FB);
acquireFence = -1;
#endif
acquireFence = hwcCheckFenceDebug(this, FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_FB, acquireFence);
if (handle == NULL) {
DISPLAY_LOGD(eDebugOverlaySupported, "ClientTarget is NULL, skipStaic (%d)",
mClientCompositionInfo.mSkipFlag);
if (mClientCompositionInfo.mSkipFlag == false) {
DISPLAY_LOGE("ClientTarget is NULL");
DISPLAY_LOGE("\t%s:: mRenderingState(%d)",__func__, mRenderingState);
}
} else {
DISPLAY_LOGD(eDebugOverlaySupported, "ClientTarget handle: %p [fd: %d, %d, %d]",
handle, handle->fd, handle->fd1, handle->fd2);
if ((mClientCompositionInfo.mSkipFlag == true) &&
((mClientCompositionInfo.mLastWinConfigData.fd_idma[0] != handle->fd) ||
(mClientCompositionInfo.mLastWinConfigData.fd_idma[1] != handle->fd1) ||
(mClientCompositionInfo.mLastWinConfigData.fd_idma[2] != handle->fd2))) {
String8 errString;
DISPLAY_LOGE("skip flag is enabled but buffer is updated lastConfig[%d, %d, %d], handle[%d, %d, %d]\n",
mClientCompositionInfo.mLastWinConfigData.fd_idma[0],
mClientCompositionInfo.mLastWinConfigData.fd_idma[1],
mClientCompositionInfo.mLastWinConfigData.fd_idma[2],
handle->fd, handle->fd1, handle->fd2);
DISPLAY_LOGE("last win config");
for (size_t i = 0; i < mLastDpuData.configs.size(); i++) {
errString.appendFormat("config[%zu]\n", i);
dumpConfig(errString, mLastDpuData.configs[i]);
DISPLAY_LOGE("\t%s", errString.string());
errString.clear();
}
errString.appendFormat("%s:: skip flag is enabled but buffer is updated\n",
__func__);
printDebugInfos(errString);
}
}
mClientCompositionInfo.setTargetBuffer(this, handle, acquireFence, (android_dataspace)dataspace);
setFenceInfo(acquireFence, this,
FENCE_TYPE_SRC_RELEASE, FENCE_IP_FB, FENCE_FROM);
if (handle) {
mClientCompositionInfo.mCompressed = isAFBCCompressed(handle);
}
return 0;
}
int32_t ExynosDisplay::setColorTransform(
const float* matrix,
int32_t /*android_color_transform_t*/ hint) {
if ((hint < HAL_COLOR_TRANSFORM_IDENTITY) ||
(hint > HAL_COLOR_TRANSFORM_CORRECT_TRITANOPIA))
return HWC2_ERROR_BAD_PARAMETER;
ALOGI("%s:: %d, %d", __func__, mColorTransformHint, hint);
if (mColorTransformHint != hint)
setGeometryChanged(GEOMETRY_DISPLAY_COLOR_TRANSFORM_CHANGED);
mColorTransformHint = hint;
#ifdef HWC_SUPPORT_COLOR_TRANSFORM
int ret = mDisplayInterface->setColorTransform(matrix, hint);
if (ret < 0)
mColorTransformHint = ret;
return ret;
#else
return HWC2_ERROR_NONE;
#endif
}
int32_t ExynosDisplay::setColorMode(
int32_t /*android_color_mode_t*/ mode) {
if (mDisplayInterface->setColorMode(mode) < 0) {
if (mode == HAL_COLOR_MODE_NATIVE)
return HWC2_ERROR_NONE;
ALOGE("%s:: is not supported", __func__);
return HWC2_ERROR_UNSUPPORTED;
}
ALOGI("%s:: %d, %d", __func__, mColorMode, mode);
if (mColorMode != mode)
setGeometryChanged(GEOMETRY_DISPLAY_COLOR_MODE_CHANGED);
mColorMode = (android_color_mode_t)mode;
return HWC2_ERROR_NONE;
}
int32_t ExynosDisplay::getRenderIntents(int32_t mode, uint32_t* outNumIntents,
int32_t* /*android_render_intent_v1_1_t*/ outIntents)
{
ALOGI("%s:: mode(%d), outNum(%d), outIntents(%p)",
__func__, mode, *outNumIntents, outIntents);
return mDisplayInterface->getRenderIntents(mode, outNumIntents, outIntents);
}
int32_t ExynosDisplay::setColorModeWithRenderIntent(int32_t /*android_color_mode_t*/ mode,
int32_t /*android_render_intent_v1_1_t */ intent)
{
ALOGI("%s:: mode(%d), intent(%d)", __func__, mode, intent);
return mDisplayInterface->setColorModeWithRenderIntent(mode, intent);
}
int32_t ExynosDisplay::getDisplayIdentificationData(uint8_t* outPort,
uint32_t* outDataSize, uint8_t* outData)
{
return mDisplayInterface->getDisplayIdentificationData(outPort, outDataSize, outData);
}
int32_t ExynosDisplay::getDisplayCapabilities(uint32_t* outNumCapabilities,
uint32_t* outCapabilities)
{
/* If each display has their own capabilities,
* this should be described in display module codes */
uint32_t capabilityNum = 0;
if (mBrightnessFd != NULL)
capabilityNum++;
if (mDisplayInterface->isDozeModeAvailable()) {
capabilityNum++;
}
if (outCapabilities == NULL) {
*outNumCapabilities = capabilityNum;
return HWC2_ERROR_NONE;
}
if (capabilityNum != *outNumCapabilities) {
ALOGE("%s:: invalid outNumCapabilities(%d), should be(%d)", __func__, *outNumCapabilities, capabilityNum);
return HWC2_ERROR_BAD_PARAMETER;
}
uint32_t index = 0;
if (mBrightnessFd != NULL)
outCapabilities[index++] = HWC2_DISPLAY_CAPABILITY_BRIGHTNESS;
if (mDisplayInterface->isDozeModeAvailable()) {
outCapabilities[index++] = HWC2_DISPLAY_CAPABILITY_DOZE;
}
return HWC2_ERROR_NONE;
}
int32_t ExynosDisplay::getDisplayBrightnessSupport(bool* outSupport)
{
if (mBrightnessFd == NULL) {
*outSupport = false;
} else {
*outSupport = true;
}
return HWC2_ERROR_NONE;
}
int32_t ExynosDisplay::setDisplayBrightness(float brightness)
{
if (mBrightnessFd == NULL)
return HWC2_ERROR_UNSUPPORTED;
char val[MAX_BRIGHTNESS_LEN]= {0};
uint32_t scaledBrightness = static_cast<uint32_t>(round(brightness * mMaxBrightness));
int32_t ret = 0;
if ((ret = snprintf(val, MAX_BRIGHTNESS_LEN, "%d", scaledBrightness)) > 0) {
fwrite(val, sizeof(val), 1, mBrightnessFd);
if (ferror(mBrightnessFd)){
ALOGE("brightness write failed");
clearerr(mBrightnessFd);
}
rewind(mBrightnessFd);
} else {
ALOGE("Fail to set brightness, ret(%d), brightness(%f, %d)",
ret, brightness, scaledBrightness);
}
return HWC2_ERROR_NONE;
}
int32_t ExynosDisplay::getDisplayConnectionType(uint32_t* outType)
{
if (mType == HWC_DISPLAY_PRIMARY)
*outType = HWC2_DISPLAY_CONNECTION_TYPE_INTERNAL;
else if (mType == HWC_DISPLAY_EXTERNAL)
*outType = HWC2_DISPLAY_CONNECTION_TYPE_EXTERNAL;
else
return HWC2_ERROR_BAD_DISPLAY;
return HWC2_ERROR_NONE;
}
int32_t ExynosDisplay::getDisplayVsyncPeriod(hwc2_vsync_period_t* __unused outVsyncPeriod)
{
return HWC2_ERROR_NONE;
}
int32_t ExynosDisplay::setActiveConfigWithConstraints(hwc2_config_t __unused config,
hwc_vsync_period_change_constraints_t* __unused vsyncPeriodChangeConstraints,
hwc_vsync_period_change_timeline_t* __unused outTimeline)
{
return HWC2_ERROR_NONE;
}
int32_t ExynosDisplay::setAutoLowLatencyMode(bool __unused on)
{
return HWC2_ERROR_UNSUPPORTED;
}
int32_t ExynosDisplay::getSupportedContentTypes(uint32_t* __unused outNumSupportedContentTypes,
uint32_t* __unused outSupportedContentTypes)
{
if (outSupportedContentTypes == NULL)
outNumSupportedContentTypes = 0;
return HWC2_ERROR_NONE;
}
int32_t ExynosDisplay::setContentType(int32_t /* hwc2_content_type_t */ contentType)
{
if (contentType == HWC2_CONTENT_TYPE_NONE)
return HWC2_ERROR_NONE;
return HWC2_ERROR_UNSUPPORTED;
}
int32_t ExynosDisplay::getClientTargetProperty(hwc_client_target_property_t* outClientTargetProperty)
{
outClientTargetProperty->pixelFormat = HAL_PIXEL_FORMAT_RGBA_8888;
outClientTargetProperty->dataspace = HAL_DATASPACE_UNKNOWN;
return HWC2_ERROR_NONE;
}
bool ExynosDisplay::isBadConfig(hwc2_config_t config)
{
/* Check invalid config */
const auto its = mDisplayConfigs.find(config);
if (its == mDisplayConfigs.end()) {
ALOGE("%s, invalid config : %d", __func__, config);
return true;
}
return false;
}
bool ExynosDisplay::needNotChangeConfig(hwc2_config_t config)
{
/* getting current config and compare */
/* If same value, return */
if (mActiveConfig == config) {
ALOGI("%s, Same config change requested : %d", __func__, config);
return true;
}
return false;
}
int32_t ExynosDisplay::updateInternalDisplayConfigVariables(hwc2_config_t config)
{
mActiveConfig = config;
/* Update internal variables */
getDisplayAttribute(mActiveConfig, HWC2_ATTRIBUTE_WIDTH, (int32_t*)&mXres);
getDisplayAttribute(mActiveConfig, HWC2_ATTRIBUTE_HEIGHT, (int32_t*)&mYres);
getDisplayAttribute(mActiveConfig, HWC2_ATTRIBUTE_DPI_X, (int32_t*)&mXdpi);
getDisplayAttribute(mActiveConfig, HWC2_ATTRIBUTE_DPI_Y, (int32_t*)&mYdpi);
getDisplayAttribute(mActiveConfig, HWC2_ATTRIBUTE_VSYNC_PERIOD, (int32_t*)&mVsyncPeriod);
return NO_ERROR;
}
int32_t ExynosDisplay::setOutputBuffer( buffer_handle_t __unused buffer, int32_t __unused releaseFence)
{
return HWC2_ERROR_NONE;
}
int ExynosDisplay::clearDisplay(bool readback) {
const int ret = mDisplayInterface->clearDisplay(readback);
if (ret)
DISPLAY_LOGE("fail to clear display");
mClientCompositionInfo.mSkipStaticInitFlag = false;
mClientCompositionInfo.mSkipFlag = false;
mLastDpuData.reset();
/* Update last retire fence */
mLastRetireFence = fence_close(mLastRetireFence, this, FENCE_TYPE_RETIRE, FENCE_IP_DPP);
return ret;
}
int32_t ExynosDisplay::setPowerMode(
int32_t /*hwc2_power_mode_t*/ mode) {
Mutex::Autolock lock(mDisplayMutex);
if (!mDisplayInterface->isDozeModeAvailable() &&
(mode == HWC2_POWER_MODE_DOZE || mode == HWC2_POWER_MODE_DOZE_SUSPEND)) {
return HWC2_ERROR_UNSUPPORTED;
}
if (mode == HWC_POWER_MODE_OFF) {
mDevice->mPrimaryBlank = true;
clearDisplay();
ALOGV("HWC2: Clear display (power off)");
} else {
mDevice->mPrimaryBlank = false;
}
if (mode == HWC_POWER_MODE_OFF)
mDREnable = false;
else
mDREnable = mDRDefault;
// check the dynamic recomposition thread by following display power status;
mDevice->checkDynamicRecompositionThread();
/* TODO: Call display interface */
mDisplayInterface->setPowerMode(mode);
ALOGD("%s:: mode(%d))", __func__, mode);
mPowerModeState = (hwc2_power_mode_t)mode;
if (mode == HWC_POWER_MODE_OFF) {
/* It should be called from validate() when the screen is on */
mSkipFrame = true;
setGeometryChanged(GEOMETRY_DISPLAY_POWER_OFF);
if ((mRenderingState >= RENDERING_STATE_VALIDATED) &&
(mRenderingState < RENDERING_STATE_PRESENTED))
closeFencesForSkipFrame(RENDERING_STATE_VALIDATED);
mRenderingState = RENDERING_STATE_NONE;
} else {
setGeometryChanged(GEOMETRY_DISPLAY_POWER_ON);
}
return HWC2_ERROR_NONE;
}
int32_t ExynosDisplay::setVsyncEnabled(
int32_t /*hwc2_vsync_t*/ enabled) {
uint32_t val = 0;
// ALOGD("HWC2 : %s : %d %d", __func__, __LINE__, enabled);
if (enabled < 0 || enabled > HWC2_VSYNC_DISABLE)
return HWC2_ERROR_BAD_PARAMETER;
if (enabled == HWC2_VSYNC_ENABLE) {
gettimeofday(&updateTimeInfo.lastEnableVsyncTime, NULL);
val = 1;
} else {
gettimeofday(&updateTimeInfo.lastDisableVsyncTime, NULL);
}
if (mDisplayInterface->setVsyncEnabled(val) < 0) {
HWC_LOGE(this, "vsync ioctl failed errno : %d", errno);
return HWC2_ERROR_BAD_DISPLAY;
}
mVsyncState = (hwc2_vsync_t)enabled;
return HWC2_ERROR_NONE;
}
int32_t ExynosDisplay::validateDisplay(
uint32_t* outNumTypes, uint32_t* outNumRequests) {
ATRACE_CALL();
gettimeofday(&updateTimeInfo.lastValidateTime, NULL);
Mutex::Autolock lock(mDisplayMutex);
if (mPauseDisplay)
return HWC2_ERROR_NONE;
int ret = NO_ERROR;
bool validateError = false;
mUpdateEventCnt++;
mUpdateCallCnt++;
mLastUpdateTimeStamp = systemTime(SYSTEM_TIME_MONOTONIC);
if (mLayers.size() == 0)
DISPLAY_LOGI("%s:: validateDisplay layer size is 0", __func__);
if (mLayers.size() > 1)
mLayers.vector_sort();
// Reset current frame flags for Fence Tracer
resetFenceCurFlag(this);
doPreProcessing();
checkLayerFps();
if (exynosHWCControl.useDynamicRecomp == true && mDREnable)
checkDynamicReCompMode();
if (exynosHWCControl.useDynamicRecomp == true &&
mDevice->isDynamicRecompositionThreadAlive() == false &&
mDevice->mDRLoopStatus == false) {
mDevice->dynamicRecompositionThreadCreate();
}
for (size_t i=0; i < mLayers.size(); i++) {
// Layer's acquire fence from SF
setFenceInfo(mLayers[i]->mAcquireFence, this,
FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_LAYER, FENCE_FROM);
}
if ((ret = mResourceManager->assignResource(this)) != NO_ERROR) {
validateError = true;
HWC_LOGE(this, "%s:: assignResource() fail, display(%d), ret(%d)", __func__, mDisplayId, ret);
String8 errString;
errString.appendFormat("%s:: assignResource() fail, display(%d), ret(%d)\n",
__func__, mDisplayId, ret);
printDebugInfos(errString);
mDisplayInterface->setForcePanic();
}
if ((ret = updateColorConversionInfo()) != NO_ERROR) {
validateError = true;
DISPLAY_LOGE("%s:: updateColorConversionInfo() fail, ret(%d)",
__func__, ret);
}
if ((ret = skipStaticLayers(mClientCompositionInfo)) != NO_ERROR) {
validateError = true;
HWC_LOGE(this, "%s:: skipStaticLayers() fail, display(%d), ret(%d)", __func__, mDisplayId, ret);
} else {
if ((mClientCompositionInfo.mHasCompositionLayer) &&
(mClientCompositionInfo.mSkipFlag == false)) {
/* Initialize compositionType */
for (size_t i = (size_t)mClientCompositionInfo.mFirstIndex; i <= (size_t)mClientCompositionInfo.mLastIndex; i++) {
if (mLayers[i]->mOverlayPriority >= ePriorityHigh)
continue;
mLayers[i]->mValidateCompositionType = HWC2_COMPOSITION_CLIENT;
}
}
}
mRenderingState = RENDERING_STATE_VALIDATED;
/*
* HWC should update performanceInfo even if assignResource is skipped
* HWC excludes the layer from performance calculation
* if there is no buffer update. (using ExynosMPP::canSkipProcessing())
* Therefore performanceInfo should be calculated again if only the buffer is updated.
*/
if ((ret = mDevice->mResourceManager->deliverPerformanceInfo()) != NO_ERROR) {
HWC_LOGE(NULL,"%s:: deliverPerformanceInfo() error (%d)",
__func__, ret);
}
if ((validateError == false) && (mDisplayControl.earlyStartMPP == true)) {
if ((ret = startPostProcessing()) != NO_ERROR)
validateError = true;
}
if (validateError) {
setGeometryChanged(GEOMETRY_ERROR_CASE);
mClientCompositionInfo.mSkipStaticInitFlag = false;
mExynosCompositionInfo.mSkipStaticInitFlag = false;
mResourceManager->resetAssignedResources(this, true);
mClientCompositionInfo.initializeInfos(this);
mExynosCompositionInfo.initializeInfos(this);
for (uint32_t i = 0; i < mLayers.size(); i++) {
ExynosLayer *layer = mLayers[i];
layer->mOverlayInfo |= eResourceAssignFail;
layer->mValidateCompositionType = HWC2_COMPOSITION_CLIENT;
addClientCompositionLayer(i);
}
mResourceManager->assignCompositionTarget(this, COMPOSITION_CLIENT);
mResourceManager->assignWindow(this);
}
int32_t displayRequests = 0;
if ((ret = getChangedCompositionTypes(outNumTypes, NULL, NULL)) != NO_ERROR) {
HWC_LOGE(this, "%s:: getChangedCompositionTypes() fail, display(%d), ret(%d)", __func__, mDisplayId, ret);
setGeometryChanged(GEOMETRY_ERROR_CASE);
}
if ((ret = getDisplayRequests(&displayRequests, outNumRequests, NULL, NULL)) != NO_ERROR) {
HWC_LOGE(this, "%s:: getDisplayRequests() fail, display(%d), ret(%d)", __func__, mDisplayId, ret);
setGeometryChanged(GEOMETRY_ERROR_CASE);
}
mSkipFrame = false;
if ((*outNumTypes == 0) && (*outNumRequests == 0))
return HWC2_ERROR_NONE;
return HWC2_ERROR_HAS_CHANGES;
}
int32_t ExynosDisplay::startPostProcessing()
{
int ret = NO_ERROR;
String8 errString;
float assignedCapacity = mResourceManager->getAssignedCapacity(MPP_G2D);
if (assignedCapacity > (mResourceManager->getM2MCapa(MPP_G2D) * MPP_CAPA_OVER_THRESHOLD)) {
errString.appendFormat("Assigned capacity for exynos composition is over restriction (%f)",
assignedCapacity);
goto err;
}
if ((ret = doExynosComposition()) != NO_ERROR) {
errString.appendFormat("exynosComposition fail (%d)\n", ret);
goto err;
}
// loop for all layer
for (size_t i=0; i < mLayers.size(); i++) {
if((mLayers[i]->mValidateCompositionType == HWC2_COMPOSITION_DEVICE) &&
(mLayers[i]->mM2mMPP != NULL)) {
/* mAcquireFence is updated, Update image info */
struct exynos_image srcImg, dstImg, midImg;
mLayers[i]->setSrcExynosImage(&srcImg);
mLayers[i]->setDstExynosImage(&dstImg);
mLayers[i]->setExynosImage(srcImg, dstImg);
ExynosMPP *m2mMpp = mLayers[i]->mM2mMPP;
srcImg = mLayers[i]->mSrcImg;
midImg = mLayers[i]->mMidImg;
m2mMpp->requestHWStateChange(MPP_HW_STATE_RUNNING);
if ((ret = m2mMpp->doPostProcessing(srcImg, midImg)) != NO_ERROR) {
DISPLAY_LOGE("%s:: doPostProcessing() failed, layer(%zu), ret(%d)",
__func__, i, ret);
errString.appendFormat("%s:: doPostProcessing() failed, layer(%zu), ret(%d)\n",
__func__, i, ret);
goto err;
} else {
/* This should be closed by lib for each resource */
mLayers[i]->mAcquireFence = -1;
}
}
}
return ret;
err:
printDebugInfos(errString);
closeFences();
mDisplayInterface->setForcePanic();
return -EINVAL;
}
int32_t ExynosDisplay::setCursorPositionAsync(uint32_t x_pos, uint32_t y_pos) {
mDisplayInterface->setCursorPositionAsync(x_pos, y_pos);
return HWC2_ERROR_NONE;
}
void ExynosDisplay::dumpConfig(const exynos_win_config_data &c)
{
DISPLAY_LOGD(eDebugWinConfig|eDebugSkipStaicLayer, "\tstate = %u", c.state);
if (c.state == c.WIN_STATE_COLOR) {
DISPLAY_LOGD(eDebugWinConfig|eDebugSkipStaicLayer,
"\t\tx = %d, y = %d, width = %d, height = %d, color = %u, alpha = %f\n",
c.dst.x, c.dst.y, c.dst.w, c.dst.h, c.color, c.plane_alpha);
} else/* if (c.state != c.WIN_STATE_DISABLED) */{
DISPLAY_LOGD(eDebugWinConfig|eDebugSkipStaicLayer, "\t\tfd = (%d, %d, %d), acq_fence = %d, rel_fence = %d "
"src_f_w = %u, src_f_h = %u, src_x = %d, src_y = %d, src_w = %u, src_h = %u, "
"dst_f_w = %u, dst_f_h = %u, dst_x = %d, dst_y = %d, dst_w = %u, dst_h = %u, "
"format = %u, pa = %f, transform = %d, dataspace = 0x%8x, hdr_enable = %d, blending = %u, "
"protection = %u, compression = %d, compression_src = %d, transparent(x:%d, y:%d, w:%d, h:%d), "
"block(x:%d, y:%d, w:%d, h:%d)",
c.fd_idma[0], c.fd_idma[1], c.fd_idma[2],
c.acq_fence, c.rel_fence,
c.src.f_w, c.src.f_h, c.src.x, c.src.y, c.src.w, c.src.h,
c.dst.f_w, c.dst.f_h, c.dst.x, c.dst.y, c.dst.w, c.dst.h,
c.format, c.plane_alpha, c.transform, c.dataspace, c.hdr_enable,
c.blending, c.protection, c.compression, c.comp_src,
c.transparent_area.x, c.transparent_area.y, c.transparent_area.w, c.transparent_area.h,
c.opaque_area.x, c.opaque_area.y, c.opaque_area.w, c.opaque_area.h);
}
}
void ExynosDisplay::dump(String8& result)
{
Mutex::Autolock lock(mDisplayMutex);
result.appendFormat("[%s] display information size: %d x %d, vsyncState: %d, colorMode: %d, colorTransformHint: %d\n",
mDisplayName.string(),
mXres, mYres, mVsyncState, mColorMode, mColorTransformHint);
mClientCompositionInfo.dump(result);
mExynosCompositionInfo.dump(result);
result.appendFormat("PanelGammaSource (%d)\n\n", GetCurrentPanelGammaSource());
for (uint32_t i = 0; i < mLayers.size(); i++) {
ExynosLayer *layer = mLayers[i];
layer->dump(result);
}
result.appendFormat("\n");
}
void ExynosDisplay::dumpConfig(String8 &result, const exynos_win_config_data &c)
{
result.appendFormat("\tstate = %u\n", c.state);
if (c.state == c.WIN_STATE_COLOR) {
result.appendFormat("\t\tx = %d, y = %d, width = %d, height = %d, color = %u, alpha = %f\n",
c.dst.x, c.dst.y, c.dst.w, c.dst.h, c.color, c.plane_alpha);
} else/* if (c.state != c.WIN_STATE_DISABLED) */{
result.appendFormat("\t\tfd = (%d, %d, %d), acq_fence = %d, rel_fence = %d "
"src_f_w = %u, src_f_h = %u, src_x = %d, src_y = %d, src_w = %u, src_h = %u, "
"dst_f_w = %u, dst_f_h = %u, dst_x = %d, dst_y = %d, dst_w = %u, dst_h = %u, "
"format = %u, pa = %f, transform = %d, dataspace = 0x%8x, hdr_enable = %d, blending = %u, "
"protection = %u, compression = %d, compression_src = %d, transparent(x:%d, y:%d, w:%d, h:%d), "
"block(x:%d, y:%d, w:%d, h:%d)\n",
c.fd_idma[0], c.fd_idma[1], c.fd_idma[2],
c.acq_fence, c.rel_fence,
c.src.f_w, c.src.f_h, c.src.x, c.src.y, c.src.w, c.src.h,
c.dst.f_w, c.dst.f_h, c.dst.x, c.dst.y, c.dst.w, c.dst.h,
c.format, c.plane_alpha, c.transform, c.dataspace, c.hdr_enable, c.blending, c.protection,
c.compression, c.comp_src,
c.transparent_area.x, c.transparent_area.y, c.transparent_area.w, c.transparent_area.h,
c.opaque_area.x, c.opaque_area.y, c.opaque_area.w, c.opaque_area.h);
}
}
void ExynosDisplay::printConfig(exynos_win_config_data &c)
{
ALOGD("\tstate = %u", c.state);
if (c.state == c.WIN_STATE_COLOR) {
ALOGD("\t\tx = %d, y = %d, width = %d, height = %d, color = %u, alpha = %f\n",
c.dst.x, c.dst.y, c.dst.w, c.dst.h, c.color, c.plane_alpha);
} else/* if (c.state != c.WIN_STATE_DISABLED) */{
ALOGD("\t\tfd = (%d, %d, %d), acq_fence = %d, rel_fence = %d "
"src_f_w = %u, src_f_h = %u, src_x = %d, src_y = %d, src_w = %u, src_h = %u, "
"dst_f_w = %u, dst_f_h = %u, dst_x = %d, dst_y = %d, dst_w = %u, dst_h = %u, "
"format = %u, pa = %f, transform = %d, dataspace = 0x%8x, hdr_enable = %d, blending = %u, "
"protection = %u, compression = %d, compression_src = %d, transparent(x:%d, y:%d, w:%d, h:%d), "
"block(x:%d, y:%d, w:%d, h:%d)",
c.fd_idma[0], c.fd_idma[1], c.fd_idma[2],
c.acq_fence, c.rel_fence,
c.src.f_w, c.src.f_h, c.src.x, c.src.y, c.src.w, c.src.h,
c.dst.f_w, c.dst.f_h, c.dst.x, c.dst.y, c.dst.w, c.dst.h,
c.format, c.plane_alpha, c.transform, c.dataspace, c.hdr_enable, c.blending, c.protection,
c.compression, c.comp_src,
c.transparent_area.x, c.transparent_area.y, c.transparent_area.w, c.transparent_area.h,
c.opaque_area.x, c.opaque_area.y, c.opaque_area.w, c.opaque_area.h);
}
}
int32_t ExynosDisplay::setCompositionTargetExynosImage(uint32_t targetType, exynos_image *src_img, exynos_image *dst_img)
{
ExynosCompositionInfo compositionInfo;
if (targetType == COMPOSITION_CLIENT)
compositionInfo = mClientCompositionInfo;
else if (targetType == COMPOSITION_EXYNOS)
compositionInfo = mExynosCompositionInfo;
else
return -EINVAL;
src_img->fullWidth = mXres;
src_img->fullHeight = mYres;
/* To do */
/* Fb crop should be set hear */
src_img->x = 0;
src_img->y = 0;
src_img->w = mXres;
src_img->h = mYres;
if (compositionInfo.mTargetBuffer != NULL) {
src_img->bufferHandle = compositionInfo.mTargetBuffer;
src_img->format = compositionInfo.mTargetBuffer->format;
#ifdef GRALLOC_VERSION1
src_img->usageFlags = compositionInfo.mTargetBuffer->producer_usage;
#else
src_img->usageFlags = compositionInfo.mTargetBuffer->flags;
#endif
} else {
src_img->bufferHandle = NULL;
src_img->format = HAL_PIXEL_FORMAT_RGBA_8888;
src_img->usageFlags = 0;
}
src_img->layerFlags = 0x0;
src_img->acquireFenceFd = compositionInfo.mAcquireFence;
src_img->releaseFenceFd = -1;
src_img->dataSpace = compositionInfo.mDataSpace;
src_img->blending = HWC2_BLEND_MODE_PREMULTIPLIED;
src_img->transform = 0;
src_img->compressed = compositionInfo.mCompressed;
src_img->planeAlpha = 1;
src_img->zOrder = 0;
if ((targetType == COMPOSITION_CLIENT) && (mType == HWC_DISPLAY_VIRTUAL)) {
if (compositionInfo.mLastIndex < mExynosCompositionInfo.mLastIndex)
src_img->zOrder = 0;
else
src_img->zOrder = 1000;
}
dst_img->fullWidth = mXres;
dst_img->fullHeight = mYres;
/* To do */
/* Fb crop should be set hear */
dst_img->x = 0;
dst_img->y = 0;
dst_img->w = mXres;
dst_img->h = mYres;
dst_img->bufferHandle = NULL;
dst_img->format = HAL_PIXEL_FORMAT_RGBA_8888;
dst_img->usageFlags = 0;
dst_img->layerFlags = 0x0;
dst_img->acquireFenceFd = -1;
dst_img->releaseFenceFd = -1;
dst_img->dataSpace = src_img->dataSpace;
if (mColorMode != HAL_COLOR_MODE_NATIVE)
dst_img->dataSpace = colorModeToDataspace(mColorMode);
dst_img->blending = HWC2_BLEND_MODE_NONE;
dst_img->transform = 0;
dst_img->compressed = compositionInfo.mCompressed;
dst_img->planeAlpha = 1;
dst_img->zOrder = src_img->zOrder;
return NO_ERROR;
}
int32_t ExynosDisplay::initializeValidateInfos()
{
mCursorIndex = -1;
for (uint32_t i = 0; i < mLayers.size(); i++) {
ExynosLayer *layer = mLayers[i];
layer->mValidateCompositionType = HWC2_COMPOSITION_INVALID;
layer->mOverlayInfo = 0;
if ((mDisplayControl.cursorSupport == true) &&
(mLayers[i]->mCompositionType == HWC2_COMPOSITION_CURSOR))
mCursorIndex = i;
}
exynos_image src_img;
exynos_image dst_img;
mClientCompositionInfo.initializeInfos(this);
setCompositionTargetExynosImage(COMPOSITION_CLIENT, &src_img, &dst_img);
mClientCompositionInfo.setExynosImage(src_img, dst_img);
mExynosCompositionInfo.initializeInfos(this);
setCompositionTargetExynosImage(COMPOSITION_EXYNOS, &src_img, &dst_img);
mExynosCompositionInfo.setExynosImage(src_img, dst_img);
return NO_ERROR;
}
int32_t ExynosDisplay::addClientCompositionLayer(uint32_t layerIndex)
{
bool exynosCompositionChanged = false;
int32_t ret = NO_ERROR;
DISPLAY_LOGD(eDebugResourceManager, "[%d] layer is added to client composition", layerIndex);
if (mClientCompositionInfo.mHasCompositionLayer == false) {
mClientCompositionInfo.mFirstIndex = layerIndex;
mClientCompositionInfo.mLastIndex = layerIndex;
mClientCompositionInfo.mHasCompositionLayer = true;
return EXYNOS_ERROR_CHANGED;
} else {
mClientCompositionInfo.mFirstIndex = min(mClientCompositionInfo.mFirstIndex, (int32_t)layerIndex);
mClientCompositionInfo.mLastIndex = max(mClientCompositionInfo.mLastIndex, (int32_t)layerIndex);
}
DISPLAY_LOGD(eDebugResourceManager, "\tClient composition range [%d] - [%d]",
mClientCompositionInfo.mFirstIndex, mClientCompositionInfo.mLastIndex);
if ((mClientCompositionInfo.mFirstIndex < 0) || (mClientCompositionInfo.mLastIndex < 0))
{
HWC_LOGE(this, "%s:: mClientCompositionInfo.mHasCompositionLayer is true "
"but index is not valid (firstIndex: %d, lastIndex: %d)",
__func__, mClientCompositionInfo.mFirstIndex,
mClientCompositionInfo.mLastIndex);
return -EINVAL;
}
/* handle sandwiched layers */
for (uint32_t i = (uint32_t)mClientCompositionInfo.mFirstIndex + 1; i < (uint32_t)mClientCompositionInfo.mLastIndex; i++) {
ExynosLayer *layer = mLayers[i];
if (layer->mOverlayPriority >= ePriorityHigh) {
DISPLAY_LOGD(eDebugResourceManager, "\t[%d] layer has high or max priority (%d)", i, layer->mOverlayPriority);
continue;
}
if (layer->mValidateCompositionType != HWC2_COMPOSITION_CLIENT)
{
DISPLAY_LOGD(eDebugResourceManager, "\t[%d] layer changed", i);
if (layer->mValidateCompositionType == HWC2_COMPOSITION_EXYNOS)
exynosCompositionChanged = true;
else {
if (layer->mValidateCompositionType == HWC2_COMPOSITION_DEVICE) mWindowNumUsed--;
}
layer->resetAssignedResource();
layer->mValidateCompositionType = HWC2_COMPOSITION_CLIENT;
layer->mOverlayInfo |= eSandwitchedBetweenGLES;
}
}
/* Check Exynos Composition info is changed */
if (exynosCompositionChanged) {
DISPLAY_LOGD(eDebugResourceManager, "exynos composition [%d] - [%d] is changed",
mExynosCompositionInfo.mFirstIndex, mExynosCompositionInfo.mLastIndex);
uint32_t newFirstIndex = ~0;
int32_t newLastIndex = -1;
if ((mExynosCompositionInfo.mFirstIndex < 0) || (mExynosCompositionInfo.mLastIndex < 0))
{
HWC_LOGE(this, "%s:: mExynosCompositionInfo.mHasCompositionLayer should be true(%d) "
"but index is not valid (firstIndex: %d, lastIndex: %d)",
__func__, mExynosCompositionInfo.mHasCompositionLayer,
mExynosCompositionInfo.mFirstIndex,
mExynosCompositionInfo.mLastIndex);
return -EINVAL;
}
for (uint32_t i = 0; i < mLayers.size(); i++)
{
ExynosLayer *exynosLayer = mLayers[i];
if (exynosLayer->mValidateCompositionType == HWC2_COMPOSITION_EXYNOS) {
newFirstIndex = min(newFirstIndex, i);
newLastIndex = max(newLastIndex, (int32_t)i);
}
}
DISPLAY_LOGD(eDebugResourceManager, "changed exynos composition [%d] - [%d]",
newFirstIndex, newLastIndex);
/* There is no exynos composition layer */
if (newFirstIndex == (uint32_t)~0)
{
mExynosCompositionInfo.initializeInfos(this);
ret = EXYNOS_ERROR_CHANGED;
} else {
mExynosCompositionInfo.mFirstIndex = newFirstIndex;
mExynosCompositionInfo.mLastIndex = newLastIndex;
}
}
DISPLAY_LOGD(eDebugResourceManager, "\tresult changeFlag(0x%8x)", ret);
DISPLAY_LOGD(eDebugResourceManager, "\tClient composition(%d) range [%d] - [%d]",
mClientCompositionInfo.mHasCompositionLayer,
mClientCompositionInfo.mFirstIndex, mClientCompositionInfo.mLastIndex);
DISPLAY_LOGD(eDebugResourceManager, "\tExynos composition(%d) range [%d] - [%d]",
mExynosCompositionInfo.mHasCompositionLayer,
mExynosCompositionInfo.mFirstIndex, mExynosCompositionInfo.mLastIndex);
return ret;
}
int32_t ExynosDisplay::removeClientCompositionLayer(uint32_t layerIndex)
{
int32_t ret = NO_ERROR;
DISPLAY_LOGD(eDebugResourceManager, "[%d] - [%d] [%d] layer is removed from client composition",
mClientCompositionInfo.mFirstIndex, mClientCompositionInfo.mLastIndex,
layerIndex);
/* Only first layer or last layer can be removed */
if ((mClientCompositionInfo.mHasCompositionLayer == false) ||
((mClientCompositionInfo.mFirstIndex != (int32_t)layerIndex) &&
(mClientCompositionInfo.mLastIndex != (int32_t)layerIndex))) {
DISPLAY_LOGE("removeClientCompositionLayer() error, [%d] - [%d], layer[%d]",
mClientCompositionInfo.mFirstIndex, mClientCompositionInfo.mLastIndex,
layerIndex);
return -EINVAL;
}
if (mClientCompositionInfo.mFirstIndex == mClientCompositionInfo.mLastIndex) {
ExynosMPP *otfMPP = mClientCompositionInfo.mOtfMPP;
if (otfMPP != NULL)
otfMPP->resetAssignedState();
else {
DISPLAY_LOGE("mClientCompositionInfo.mOtfMPP is NULL");
return -EINVAL;
}
mClientCompositionInfo.initializeInfos(this);
mWindowNumUsed--;
} else if ((int32_t)layerIndex == mClientCompositionInfo.mFirstIndex)
mClientCompositionInfo.mFirstIndex++;
else
mClientCompositionInfo.mLastIndex--;
DISPLAY_LOGD(eDebugResourceManager, "\tClient composition(%d) range [%d] - [%d]",
mClientCompositionInfo.mHasCompositionLayer,
mClientCompositionInfo.mFirstIndex, mClientCompositionInfo.mLastIndex);
return ret;
}
int32_t ExynosDisplay::addExynosCompositionLayer(uint32_t layerIndex)
{
bool invalidFlag = false;
int32_t changeFlag = NO_ERROR;
int ret = 0;
int32_t startIndex;
int32_t endIndex;
DISPLAY_LOGD(eDebugResourceManager, "[%d] layer is added to exynos composition", layerIndex);
if (mExynosCompositionInfo.mHasCompositionLayer == false) {
mExynosCompositionInfo.mFirstIndex = layerIndex;
mExynosCompositionInfo.mLastIndex = layerIndex;
mExynosCompositionInfo.mHasCompositionLayer = true;
return EXYNOS_ERROR_CHANGED;
} else {
mExynosCompositionInfo.mFirstIndex = min(mExynosCompositionInfo.mFirstIndex, (int32_t)layerIndex);
mExynosCompositionInfo.mLastIndex = max(mExynosCompositionInfo.mLastIndex, (int32_t)layerIndex);
}
DISPLAY_LOGD(eDebugResourceManager, "\tExynos composition range [%d] - [%d]",
mExynosCompositionInfo.mFirstIndex, mExynosCompositionInfo.mLastIndex);
ExynosMPP *m2mMPP = mExynosCompositionInfo.mM2mMPP;
if (m2mMPP == NULL) {
DISPLAY_LOGE("exynosComposition m2mMPP is NULL");
return -EINVAL;
}
startIndex = mExynosCompositionInfo.mFirstIndex;
endIndex = mExynosCompositionInfo.mLastIndex;
if ((startIndex < 0) || (endIndex < 0) ||
(startIndex >= (int32_t)mLayers.size()) || (endIndex >= (int32_t)mLayers.size())) {
DISPLAY_LOGE("exynosComposition invalid index (%d), (%d)", startIndex, endIndex);
return -EINVAL;
}
int32_t maxPriorityIndex = -1;
uint32_t highPriorityIndex = 0;
uint32_t highPriorityNum = 0;
int32_t highPriorityCheck = 0;
std::vector<int32_t> highPriority;
highPriority.assign(mLayers.size(), -1);
/* handle sandwiched layers */
for (int32_t i = startIndex; i <= endIndex; i++) {
ExynosLayer *layer = mLayers[i];
if (layer == NULL) {
DISPLAY_LOGE("layer[%d] layer is null", i);
continue;
}
if (layer->mOverlayPriority == ePriorityMax &&
m2mMPP->mLogicalType == MPP_LOGICAL_G2D_COMBO) {
DISPLAY_LOGD(eDebugResourceManager, "\tG2D will be assgined for only [%d] layer", i);
invalidFlag = true;
maxPriorityIndex = i;
continue;
}
if (layer->mOverlayPriority >= ePriorityHigh)
{
DISPLAY_LOGD(eDebugResourceManager, "\t[%d] layer has high priority", i);
highPriority[highPriorityIndex++] = i;
highPriorityNum++;
continue;
}
if (layer->mValidateCompositionType == HWC2_COMPOSITION_EXYNOS)
continue;
exynos_image src_img;
exynos_image dst_img;
layer->setSrcExynosImage(&src_img);
layer->setDstExynosImage(&dst_img);
layer->setExynosMidImage(dst_img);
bool isAssignable = false;
if ((layer->mSupportedMPPFlag & m2mMPP->mLogicalType) != 0)
isAssignable = m2mMPP->isAssignable(this, src_img, dst_img);
if (layer->mValidateCompositionType == HWC2_COMPOSITION_CLIENT)
{
DISPLAY_LOGD(eDebugResourceManager, "\t[%d] layer is client composition", i);
invalidFlag = true;
} else if (((layer->mSupportedMPPFlag & m2mMPP->mLogicalType) == 0) ||
(isAssignable == false))
{
DISPLAY_LOGD(eDebugResourceManager, "\t[%d] layer is not supported by G2D", i);
invalidFlag = true;
layer->resetAssignedResource();
layer->mValidateCompositionType = HWC2_COMPOSITION_CLIENT;
if ((ret = addClientCompositionLayer(i)) < 0)
return ret;
changeFlag |= ret;
} else if ((layer->mValidateCompositionType == HWC2_COMPOSITION_DEVICE) ||
(layer->mValidateCompositionType == HWC2_COMPOSITION_INVALID)) {
DISPLAY_LOGD(eDebugResourceManager, "\t[%d] layer changed", i);
layer->mOverlayInfo |= eSandwitchedBetweenEXYNOS;
layer->resetAssignedResource();
if ((ret = m2mMPP->assignMPP(this, layer)) != NO_ERROR)
{
HWC_LOGE(this, "%s:: %s MPP assignMPP() error (%d)",
__func__, m2mMPP->mName.string(), ret);
return ret;
}
if (layer->mValidateCompositionType == HWC2_COMPOSITION_DEVICE) mWindowNumUsed--;
layer->mValidateCompositionType = HWC2_COMPOSITION_EXYNOS;
mExynosCompositionInfo.mFirstIndex = min(mExynosCompositionInfo.mFirstIndex, (int32_t)i);
mExynosCompositionInfo.mLastIndex = max(mExynosCompositionInfo.mLastIndex, (int32_t)i);
} else {
DISPLAY_LOGD(eDebugResourceManager, "\t[%d] layer has known type (%d)", i,
layer->mValidateCompositionType);
}
}
if (invalidFlag) {
DISPLAY_LOGD(eDebugResourceManager, "\tClient composition range [%d] - [%d]",
mClientCompositionInfo.mFirstIndex, mClientCompositionInfo.mLastIndex);
DISPLAY_LOGD(eDebugResourceManager, "\tExynos composition range [%d] - [%d], highPriorityNum[%d]",
mExynosCompositionInfo.mFirstIndex, mExynosCompositionInfo.mLastIndex, highPriorityNum);
if (m2mMPP->mLogicalType == MPP_LOGICAL_G2D_COMBO && maxPriorityIndex >= 0) {
startIndex = mExynosCompositionInfo.mFirstIndex;
endIndex = mExynosCompositionInfo.mLastIndex;
for (int32_t i = startIndex; i <= endIndex; i++) {
if (mLayers[i]->mOverlayPriority == ePriorityMax ||
mLayers[i]->mValidateCompositionType == HWC2_COMPOSITION_CLIENT)
continue;
mLayers[i]->resetAssignedResource();
mLayers[i]->mValidateCompositionType = HWC2_COMPOSITION_CLIENT;
if ((ret = addClientCompositionLayer(i)) < 0)
return ret;
changeFlag |= ret;
}
if (mLayers[maxPriorityIndex]->mValidateCompositionType
!= HWC2_COMPOSITION_EXYNOS) {
mLayers[maxPriorityIndex]->mValidateCompositionType = HWC2_COMPOSITION_EXYNOS;
mLayers[maxPriorityIndex]->resetAssignedResource();
if ((ret = m2mMPP->assignMPP(this, mLayers[maxPriorityIndex])) != NO_ERROR)
{
ALOGE("%s:: %s MPP assignMPP() error (%d)",
__func__, m2mMPP->mName.string(), ret);
return ret;
}
}
mExynosCompositionInfo.mFirstIndex = maxPriorityIndex;
mExynosCompositionInfo.mLastIndex = maxPriorityIndex;
}
/* Check if exynos comosition nests GLES composition */
if ((mClientCompositionInfo.mHasCompositionLayer) &&
(mExynosCompositionInfo.mFirstIndex < mClientCompositionInfo.mFirstIndex) &&
(mClientCompositionInfo.mFirstIndex < mExynosCompositionInfo.mLastIndex) &&
(mExynosCompositionInfo.mFirstIndex < mClientCompositionInfo.mLastIndex) &&
(mClientCompositionInfo.mLastIndex < mExynosCompositionInfo.mLastIndex)) {
if ((mClientCompositionInfo.mFirstIndex - mExynosCompositionInfo.mFirstIndex) <
(mExynosCompositionInfo.mLastIndex - mClientCompositionInfo.mLastIndex)) {
mLayers[mExynosCompositionInfo.mFirstIndex]->resetAssignedResource();
mLayers[mExynosCompositionInfo.mFirstIndex]->mValidateCompositionType = HWC2_COMPOSITION_CLIENT;
if ((ret = addClientCompositionLayer(mExynosCompositionInfo.mFirstIndex)) < 0)
return ret;
mExynosCompositionInfo.mFirstIndex = mClientCompositionInfo.mLastIndex + 1;
changeFlag |= ret;
} else {
mLayers[mExynosCompositionInfo.mLastIndex]->resetAssignedResource();
mLayers[mExynosCompositionInfo.mLastIndex]->mValidateCompositionType = HWC2_COMPOSITION_CLIENT;
if ((ret = addClientCompositionLayer(mExynosCompositionInfo.mLastIndex)) < 0)
return ret;
mExynosCompositionInfo.mLastIndex = (mClientCompositionInfo.mFirstIndex - 1);
changeFlag |= ret;
}
}
}
if (highPriorityNum > 0 && (m2mMPP->mLogicalType != MPP_LOGICAL_G2D_COMBO)) {
for (uint32_t i = 0; i < highPriorityNum; i++) {
if ((int32_t)highPriority[i] == mExynosCompositionInfo.mFirstIndex)
mExynosCompositionInfo.mFirstIndex++;
else if ((int32_t)highPriority[i] == mExynosCompositionInfo.mLastIndex)
mExynosCompositionInfo.mLastIndex--;
}
}
if ((mExynosCompositionInfo.mFirstIndex < 0) ||
(mExynosCompositionInfo.mFirstIndex >= (int)mLayers.size()) ||
(mExynosCompositionInfo.mLastIndex < 0) ||
(mExynosCompositionInfo.mLastIndex >= (int)mLayers.size()) ||
(mExynosCompositionInfo.mFirstIndex > mExynosCompositionInfo.mLastIndex))
{
DISPLAY_LOGD(eDebugResourceManager, "\texynos composition is disabled, because of invalid index (%d, %d), size(%zu)",
mExynosCompositionInfo.mFirstIndex, mExynosCompositionInfo.mLastIndex, mLayers.size());
mExynosCompositionInfo.initializeInfos(this);
changeFlag = EXYNOS_ERROR_CHANGED;
}
for (uint32_t i = 0; i < highPriorityNum; i++) {
if ((mExynosCompositionInfo.mFirstIndex < (int32_t)highPriority[i]) &&
((int32_t)highPriority[i] < mExynosCompositionInfo.mLastIndex)) {
highPriorityCheck = 1;
break;
}
}
if (highPriorityCheck && (m2mMPP->mLogicalType != MPP_LOGICAL_G2D_COMBO)) {
startIndex = mExynosCompositionInfo.mFirstIndex;
endIndex = mExynosCompositionInfo.mLastIndex;
DISPLAY_LOGD(eDebugResourceManager, "\texynos composition is disabled because of sandwitched max priority layer (%d, %d)",
mExynosCompositionInfo.mFirstIndex, mExynosCompositionInfo.mLastIndex);
for (int32_t i = startIndex; i <= endIndex; i++) {
int32_t checkPri = 0;
for (uint32_t j = 0; j < highPriorityNum; j++) {
if (i == (int32_t)highPriority[j]) {
checkPri = 1;
break;
}
}
if (checkPri)
continue;
mLayers[i]->resetAssignedResource();
mLayers[i]->mValidateCompositionType = HWC2_COMPOSITION_CLIENT;
if ((ret = addClientCompositionLayer(i)) < 0)
HWC_LOGE(this, "%d layer: addClientCompositionLayer() fail", i);
}
mExynosCompositionInfo.initializeInfos(this);
changeFlag = EXYNOS_ERROR_CHANGED;
}
DISPLAY_LOGD(eDebugResourceManager, "\tresult changeFlag(0x%8x)", changeFlag);
DISPLAY_LOGD(eDebugResourceManager, "\tClient composition range [%d] - [%d]",
mClientCompositionInfo.mFirstIndex, mClientCompositionInfo.mLastIndex);
DISPLAY_LOGD(eDebugResourceManager, "\tExynos composition range [%d] - [%d]",
mExynosCompositionInfo.mFirstIndex, mExynosCompositionInfo.mLastIndex);
return changeFlag;
}
bool ExynosDisplay::windowUpdateExceptions()
{
if (mExynosCompositionInfo.mHasCompositionLayer) {
DISPLAY_LOGD(eDebugWindowUpdate, "has exynos composition");
return true;
}
if (mClientCompositionInfo.mHasCompositionLayer) {
DISPLAY_LOGD(eDebugWindowUpdate, "has client composition");
return true;
}
for (size_t i = 0; i < mLayers.size(); i++) {
if (mLayers[i]->mM2mMPP != NULL) return true;
if (mLayers[i]->mLayerBuffer == NULL) return true;
if (mLayers[i]->mTransform != 0) return true;
}
for (size_t i = 0; i < mDpuData.configs.size(); i++) {
exynos_win_config_data &config = mDpuData.configs[i];
if (config.state == config.WIN_STATE_BUFFER) {
if (config.src.w/config.dst.w != 1 || config.src.h/config.dst.h != 1) {
DISPLAY_LOGD(eDebugWindowUpdate, "Skip reason : scaled");
return true;
}
}
}
return false;
}
int ExynosDisplay::handleWindowUpdate()
{
int ret = NO_ERROR;
// TODO will be implemented
unsigned int excp;
mDpuData.enable_win_update = false;
/* Init with full size */
mDpuData.win_update_region.x = 0;
mDpuData.win_update_region.w = mXres;
mDpuData.win_update_region.y = 0;
mDpuData.win_update_region.h = mYres;
if (exynosHWCControl.windowUpdate != 1) return 0;
if (mGeometryChanged != 0) {
DISPLAY_LOGD(eDebugWindowUpdate, "GEOMETRY chnaged 0x%" PRIx64 "",
mGeometryChanged);
return 0;
}
if ((mCursorIndex >= 0) && (mCursorIndex < (int32_t)mLayers.size())) {
ExynosLayer *layer = mLayers[mCursorIndex];
/* Cursor layer is enabled */
if (layer->mExynosCompositionType == HWC2_COMPOSITION_DEVICE) {
return 0;
}
}
/* exceptions */
if (windowUpdateExceptions())
return 0;
hwc_rect mergedRect = {(int)mXres, (int)mYres, 0, 0};
hwc_rect damageRect = {(int)mXres, (int)mYres, 0, 0};
for (size_t i = 0; i < mLayers.size(); i++) {
excp = getLayerRegion(mLayers[i], &damageRect, eDamageRegionByDamage);
if (excp == eDamageRegionPartial) {
DISPLAY_LOGD(eDebugWindowUpdate, "layer(%zu) partial : %d, %d, %d, %d", i,
damageRect.left, damageRect.top, damageRect.right, damageRect.bottom);
mergedRect = expand(mergedRect, damageRect);
}
else if (excp == eDamageRegionSkip) {
int32_t windowIndex = mLayers[i]->mWindowIndex;
if ((ret = checkConfigDstChanged(mDpuData, mLastDpuData, windowIndex)) < 0) {
return 0;
} else if (ret > 0) {
damageRect.left = mLayers[i]->mDisplayFrame.left;
damageRect.right = mLayers[i]->mDisplayFrame.right;
damageRect.top = mLayers[i]->mDisplayFrame.top;
damageRect.bottom = mLayers[i]->mDisplayFrame.bottom;
DISPLAY_LOGD(eDebugWindowUpdate, "Skip layer (origin) : %d, %d, %d, %d",
damageRect.left, damageRect.top, damageRect.right, damageRect.bottom);
mergedRect = expand(mergedRect, damageRect);
hwc_rect prevDst = {mLastDpuData.configs[i].dst.x, mLastDpuData.configs[i].dst.y,
mLastDpuData.configs[i].dst.x + (int)mLastDpuData.configs[i].dst.w,
mLastDpuData.configs[i].dst.y + (int)mLastDpuData.configs[i].dst.h};
mergedRect = expand(mergedRect, prevDst);
} else {
DISPLAY_LOGD(eDebugWindowUpdate, "layer(%zu) skip", i);
continue;
}
}
else if (excp == eDamageRegionFull) {
damageRect.left = mLayers[i]->mDisplayFrame.left;
damageRect.top = mLayers[i]->mDisplayFrame.top;
damageRect.right = mLayers[i]->mDisplayFrame.right;
damageRect.bottom = mLayers[i]->mDisplayFrame.bottom;
DISPLAY_LOGD(eDebugWindowUpdate, "Full layer update : %d, %d, %d, %d", mLayers[i]->mDisplayFrame.left,
mLayers[i]->mDisplayFrame.top, mLayers[i]->mDisplayFrame.right, mLayers[i]->mDisplayFrame.bottom);
mergedRect = expand(mergedRect, damageRect);
}
else {
DISPLAY_LOGD(eDebugWindowUpdate, "Partial canceled, Skip reason (layer %zu) : %d", i, excp);
return 0;
}
}
if (mergedRect.left == (int32_t)mXres && mergedRect.right == 0 &&
mergedRect.top == (int32_t)mYres && mergedRect.bottom == 0) {
DISPLAY_LOGD(eDebugWindowUpdate, "Partial canceled, All layer skiped" );
return 0;
}
DISPLAY_LOGD(eDebugWindowUpdate, "Partial(origin) : %d, %d, %d, %d",
mergedRect.left, mergedRect.top, mergedRect.right, mergedRect.bottom);
if (mergedRect.left < 0) mergedRect.left = 0;
if (mergedRect.right > (int32_t)mXres) mergedRect.right = mXres;
if (mergedRect.top < 0) mergedRect.top = 0;
if (mergedRect.bottom > (int32_t)mYres) mergedRect.bottom = mYres;
if (mergedRect.left == 0 && mergedRect.right == (int32_t)mXres &&
mergedRect.top == 0 && mergedRect.bottom == (int32_t)mYres) {
DISPLAY_LOGD(eDebugWindowUpdate, "Partial : Full size");
mDpuData.enable_win_update = true;
mDpuData.win_update_region.x = 0;
mDpuData.win_update_region.w = mXres;
mDpuData.win_update_region.y = 0;
mDpuData.win_update_region.h = mYres;
DISPLAY_LOGD(eDebugWindowUpdate, "window update end ------------------");
return 0;
}
mDpuData.enable_win_update = true;
mDpuData.win_update_region.x = mergedRect.left;
mDpuData.win_update_region.w = WIDTH(mergedRect);
mDpuData.win_update_region.y = mergedRect.top;
mDpuData.win_update_region.h = HEIGHT(mergedRect);
DISPLAY_LOGD(eDebugWindowUpdate, "window update end ------------------");
return 0;
}
unsigned int ExynosDisplay::getLayerRegion(ExynosLayer *layer, hwc_rect *rect_area, uint32_t regionType) {
android::Vector <hwc_rect_t> hwcRects;
size_t numRects = 0;
rect_area->left = INT_MAX;
rect_area->top = INT_MAX;
rect_area->right = rect_area->bottom = 0;
hwcRects = layer->mDamageRects;
numRects = layer->mDamageNum;
if ((numRects == 0) || (hwcRects.size() == 0))
return eDamageRegionFull;
if ((numRects == 1) && (hwcRects[0].left == 0) && (hwcRects[0].top == 0) &&
(hwcRects[0].right == 0) && (hwcRects[0].bottom == 0))
return eDamageRegionSkip;
switch (regionType) {
case eDamageRegionByDamage:
for (size_t j = 0; j < hwcRects.size(); j++) {
hwc_rect_t rect;
if ((hwcRects[j].left < 0) || (hwcRects[j].top < 0) ||
(hwcRects[j].right < 0) || (hwcRects[j].bottom < 0) ||
(hwcRects[j].left >= hwcRects[j].right) || (hwcRects[j].top >= hwcRects[j].bottom) ||
(hwcRects[j].right - hwcRects[j].left > WIDTH(layer->mSourceCrop)) ||
(hwcRects[j].bottom - hwcRects[j].top > HEIGHT(layer->mSourceCrop))) {
rect_area->left = INT_MAX;
rect_area->top = INT_MAX;
rect_area->right = rect_area->bottom = 0;
return eDamageRegionFull;
}
rect.left = layer->mDisplayFrame.left + hwcRects[j].left - layer->mSourceCrop.left;
rect.top = layer->mDisplayFrame.top + hwcRects[j].top - layer->mSourceCrop.top;
rect.right = layer->mDisplayFrame.left + hwcRects[j].right - layer->mSourceCrop.left;
rect.bottom = layer->mDisplayFrame.top + hwcRects[j].bottom - layer->mSourceCrop.top;
DISPLAY_LOGD(eDebugWindowUpdate, "Display frame : %d, %d, %d, %d", layer->mDisplayFrame.left,
layer->mDisplayFrame.top, layer->mDisplayFrame.right, layer->mDisplayFrame.bottom);
DISPLAY_LOGD(eDebugWindowUpdate, "hwcRects : %d, %d, %d, %d", hwcRects[j].left,
hwcRects[j].top, hwcRects[j].right, hwcRects[j].bottom);
adjustRect(rect, INT_MAX, INT_MAX);
/* Get sums of rects */
*rect_area = expand(*rect_area, rect);
}
return eDamageRegionPartial;
break;
case eDamageRegionByLayer:
if (layer->mLastLayerBuffer != layer->mLayerBuffer)
return eDamageRegionFull;
else
return eDamageRegionSkip;
break;
default:
HWC_LOGE(this, "%s:: Invalid regionType (%d)", __func__, regionType);
return eDamageRegionError;
break;
}
return eDamageRegionFull;
}
uint32_t ExynosDisplay::getRestrictionIndex(int halFormat)
{
if (isFormatRgb(halFormat))
return RESTRICTION_RGB;
else
return RESTRICTION_YUV;
}
void ExynosDisplay::closeFencesForSkipFrame(rendering_state renderingState)
{
for (size_t i=0; i < mLayers.size(); i++) {
if (mLayers[i]->mAcquireFence != -1) {
mLayers[i]->mAcquireFence = fence_close(mLayers[i]->mAcquireFence, this,
FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_LAYER);
}
}
if (mDpuData.readback_info.rel_fence >= 0) {
mDpuData.readback_info.rel_fence =
fence_close(mDpuData.readback_info.rel_fence, this,
FENCE_TYPE_READBACK_RELEASE, FENCE_IP_FB);
}
if (mDpuData.readback_info.acq_fence >= 0) {
mDpuData.readback_info.acq_fence =
fence_close(mDpuData.readback_info.acq_fence, this,
FENCE_TYPE_READBACK_ACQUIRE, FENCE_IP_DPP);
}
if (renderingState >= RENDERING_STATE_VALIDATED) {
if (mDisplayControl.earlyStartMPP == true) {
if (mExynosCompositionInfo.mHasCompositionLayer) {
/*
* m2mMPP's release fence for dst buffer was set to
* mExynosCompositionInfo.mAcquireFence by startPostProcessing()
* in validate time.
* This fence should be passed to display driver
* but it wont't because this frame will not be presented.
* So fence should be closed.
*/
mExynosCompositionInfo.mAcquireFence = fence_close(mExynosCompositionInfo.mAcquireFence,
this, FENCE_TYPE_DST_RELEASE, FENCE_IP_G2D);
}
for (size_t i = 0; i < mLayers.size(); i++) {
exynos_image outImage;
ExynosMPP* m2mMPP = mLayers[i]->mM2mMPP;
if ((mLayers[i]->mValidateCompositionType == HWC2_COMPOSITION_DEVICE) &&
(m2mMPP != NULL) &&
(m2mMPP->mAssignedDisplay == this) &&
(m2mMPP->getDstImageInfo(&outImage) == NO_ERROR)) {
if (m2mMPP->mPhysicalType == MPP_MSC) {
fence_close(outImage.releaseFenceFd, this, FENCE_TYPE_DST_RELEASE, FENCE_IP_MSC);
} else if (m2mMPP->mPhysicalType == MPP_G2D) {
ALOGD("close(%d)", outImage.releaseFenceFd);
fence_close(outImage.releaseFenceFd, this, FENCE_TYPE_DST_RELEASE, FENCE_IP_G2D);
} else {
DISPLAY_LOGE("[%zu] layer has invalid mppType(%d)", i, m2mMPP->mPhysicalType);
fence_close(outImage.releaseFenceFd, this, FENCE_TYPE_DST_RELEASE, FENCE_IP_ALL);
}
m2mMPP->resetDstReleaseFence();
ALOGD("reset buf[%d], %d", m2mMPP->mCurrentDstBuf,
m2mMPP->mDstImgs[m2mMPP->mCurrentDstBuf].acrylicReleaseFenceFd);
}
}
}
}
if (renderingState >= RENDERING_STATE_PRESENTED) {
/* mAcquireFence is set after validate */
mClientCompositionInfo.mAcquireFence = fence_close(mClientCompositionInfo.mAcquireFence, this,
FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_FB);
}
}
void ExynosDisplay::closeFences()
{
for (size_t i = 0; i < mDpuData.configs.size(); i++) {
if (mDpuData.configs[i].acq_fence != -1)
fence_close(mDpuData.configs[i].acq_fence, this,
FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_DPP);
mDpuData.configs[i].acq_fence = -1;
if (mDpuData.configs[i].rel_fence >= 0)
fence_close(mDpuData.configs[i].rel_fence, this,
FENCE_TYPE_SRC_RELEASE, FENCE_IP_DPP);
mDpuData.configs[i].rel_fence = -1;
}
for (size_t i = 0; i < mLayers.size(); i++) {
if (mLayers[i]->mReleaseFence > 0) {
fence_close(mLayers[i]->mReleaseFence, this,
FENCE_TYPE_SRC_RELEASE, FENCE_IP_LAYER);
mLayers[i]->mReleaseFence = -1;
}
if ((mLayers[i]->mExynosCompositionType == HWC2_COMPOSITION_DEVICE) &&
(mLayers[i]->mM2mMPP != NULL)) {
mLayers[i]->mM2mMPP->closeFences();
}
}
if (mExynosCompositionInfo.mHasCompositionLayer) {
if (mExynosCompositionInfo.mM2mMPP == NULL)
{
DISPLAY_LOGE("There is exynos composition, but m2mMPP is NULL");
return;
}
mExynosCompositionInfo.mM2mMPP->closeFences();
}
for (size_t i=0; i < mLayers.size(); i++) {
if (mLayers[i]->mAcquireFence != -1) {
mLayers[i]->mAcquireFence = fence_close(mLayers[i]->mAcquireFence, this,
FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_LAYER);
}
}
mExynosCompositionInfo.mAcquireFence = fence_close(mExynosCompositionInfo.mAcquireFence, this,
FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_G2D);
mClientCompositionInfo.mAcquireFence = fence_close(mClientCompositionInfo.mAcquireFence, this,
FENCE_TYPE_SRC_ACQUIRE, FENCE_IP_FB);
if (mDpuData.retire_fence > 0)
fence_close(mDpuData.retire_fence, this, FENCE_TYPE_RETIRE, FENCE_IP_DPP);
mDpuData.retire_fence = -1;
mLastRetireFence = fence_close(mLastRetireFence, this, FENCE_TYPE_RETIRE, FENCE_IP_DPP);
if (mDpuData.readback_info.rel_fence >= 0) {
mDpuData.readback_info.rel_fence =
fence_close(mDpuData.readback_info.rel_fence, this,
FENCE_TYPE_READBACK_RELEASE, FENCE_IP_FB);
}
if (mDpuData.readback_info.acq_fence >= 0) {
mDpuData.readback_info.acq_fence =
fence_close(mDpuData.readback_info.acq_fence, this,
FENCE_TYPE_READBACK_ACQUIRE, FENCE_IP_DPP);
}
}
void ExynosDisplay::setHWCControl(uint32_t ctrl, int32_t val)
{
switch (ctrl) {
case HWC_CTL_ENABLE_COMPOSITION_CROP:
mDisplayControl.enableCompositionCrop = (unsigned int)val;
break;
case HWC_CTL_ENABLE_EXYNOSCOMPOSITION_OPT:
mDisplayControl.enableExynosCompositionOptimization = (unsigned int)val;
break;
case HWC_CTL_ENABLE_CLIENTCOMPOSITION_OPT:
mDisplayControl.enableClientCompositionOptimization = (unsigned int)val;
break;
case HWC_CTL_USE_MAX_G2D_SRC:
mDisplayControl.useMaxG2DSrc = (unsigned int)val;
break;
case HWC_CTL_ENABLE_HANDLE_LOW_FPS:
mDisplayControl.handleLowFpsLayers = (unsigned int)val;
break;
case HWC_CTL_ENABLE_EARLY_START_MPP:
mDisplayControl.earlyStartMPP = (unsigned int)val;
break;
default:
ALOGE("%s: unsupported HWC_CTL (%d)", __func__, ctrl);
break;
}
}
int32_t ExynosDisplay::getHdrCapabilities(uint32_t* outNumTypes,
int32_t* outTypes, float* outMaxLuminance,
float* outMaxAverageLuminance, float* outMinLuminance)
{
DISPLAY_LOGD(eDebugHWC, "HWC2: %s, %d", __func__, __LINE__);
if (outNumTypes == NULL || outMaxLuminance == NULL ||
outMaxAverageLuminance == NULL || outMinLuminance == NULL) {
return HWC2_ERROR_BAD_PARAMETER;
}
if (outTypes == NULL) {
/*
* This function is always called twice.
* outTypes is NULL in the first call and
* outType is valid pointer in the second call.
* Get information only in the first call.
* Use saved information in the second call.
*/
if (mDisplayInterface->updateHdrCapabilities() != NO_ERROR)
return HWC2_ERROR_BAD_CONFIG;
}
*outMaxLuminance = mMaxLuminance;
*outMaxAverageLuminance = mMaxAverageLuminance;
*outMinLuminance = mMinLuminance;
if (outTypes == NULL) {
*outNumTypes = mHdrTypes.size();
} else {
if (*outNumTypes != mHdrTypes.size()) {
ALOGE("%s:: Invalid parameter (outNumTypes: %d, mHdrTypes size: %zu",
__func__, *outNumTypes, mHdrTypes.size());
return HWC2_ERROR_BAD_PARAMETER;
}
for(uint32_t i = 0; i < *outNumTypes; i++) {
outTypes[i] = mHdrTypes[i];
}
}
return HWC2_ERROR_NONE;
}
// Support DDI scalser
void ExynosDisplay::setDDIScalerEnable(int __unused width, int __unused height) {
}
int ExynosDisplay::getDDIScalerMode(int __unused width, int __unused height) {
return 1; // WQHD
}
void ExynosDisplay::increaseMPPDstBufIndex() {
for (size_t i=0; i < mLayers.size(); i++) {
if((mLayers[i]->mExynosCompositionType == HWC2_COMPOSITION_DEVICE) &&
(mLayers[i]->mM2mMPP != NULL)) {
mLayers[i]->mM2mMPP->increaseDstBuffIndex();
}
}
if ((mExynosCompositionInfo.mHasCompositionLayer) &&
(mExynosCompositionInfo.mM2mMPP != NULL)) {
mExynosCompositionInfo.mM2mMPP->increaseDstBuffIndex();
}
}
int32_t ExynosDisplay::getReadbackBufferAttributes(int32_t* /*android_pixel_format_t*/ outFormat,
int32_t* /*android_dataspace_t*/ outDataspace)
{
int32_t ret = mDisplayInterface->getReadbackBufferAttributes(outFormat, outDataspace);
if (ret == NO_ERROR) {
/* Interface didn't specific set dataspace */
if (*outDataspace == HAL_DATASPACE_UNKNOWN)
*outDataspace = colorModeToDataspace(mColorMode);
/* Set default value */
if (*outDataspace == HAL_DATASPACE_UNKNOWN)
*outDataspace = HAL_DATASPACE_V0_SRGB;
mDisplayControl.readbackSupport = true;
ALOGI("readback info: format(0x%8x), dataspace(0x%8x)", *outFormat, *outDataspace);
} else {
mDisplayControl.readbackSupport = false;
ALOGI("readback is not supported, ret(%d)", ret);
ret = HWC2_ERROR_UNSUPPORTED;
}
return ret;
}
int32_t ExynosDisplay::setReadbackBuffer(buffer_handle_t buffer, int32_t releaseFence)
{
Mutex::Autolock lock(mDisplayMutex);
int32_t ret = NO_ERROR;
if (buffer == nullptr)
return HWC2_ERROR_BAD_PARAMETER;
if (mDisplayControl.readbackSupport) {
mDpuData.enable_readback = true;
} else {
DISPLAY_LOGE("readback is not supported but setReadbackBuffer is called, buffer(%p), releaseFence(%d)",
buffer, releaseFence);
if (releaseFence >= 0)
releaseFence = fence_close(releaseFence, this,
FENCE_TYPE_READBACK_RELEASE, FENCE_IP_FB);
mDpuData.enable_readback = false;
ret = HWC2_ERROR_UNSUPPORTED;
}
setReadbackBufferInternal(buffer, releaseFence);
return ret;
}
void ExynosDisplay::setReadbackBufferInternal(buffer_handle_t buffer, int32_t releaseFence)
{
if (mDpuData.readback_info.rel_fence >= 0) {
mDpuData.readback_info.rel_fence =
fence_close(mDpuData.readback_info.rel_fence, this,
FENCE_TYPE_READBACK_RELEASE, FENCE_IP_FB);
DISPLAY_LOGE("previous readback release fence is not delivered to display device");
}
if (releaseFence >= 0) {
setFenceInfo(releaseFence, this, FENCE_TYPE_READBACK_RELEASE,
FENCE_IP_FB, FENCE_FROM);
}
mDpuData.readback_info.rel_fence = releaseFence;
private_handle_t *handle = NULL;
if (buffer != NULL)
handle = private_handle_t::dynamicCast(buffer);
mDpuData.readback_info.handle = handle;
}
int32_t ExynosDisplay::getReadbackBufferFence(int32_t* outFence)
{
/*
* acq_fence was not set or
* it was already closed by error or frame skip
*/
if (mDpuData.readback_info.acq_fence < 0) {
*outFence = -1;
return HWC2_ERROR_UNSUPPORTED;
}
*outFence = mDpuData.readback_info.acq_fence;
/* Fence will be closed by caller of this function */
mDpuData.readback_info.acq_fence = -1;
return NO_ERROR;
}
int32_t ExynosDisplay::setReadbackBufferAcqFence(int32_t acqFence) {
if (mDpuData.readback_info.acq_fence >= 0) {
mDpuData.readback_info.acq_fence =
fence_close(mDpuData.readback_info.acq_fence, this,
FENCE_TYPE_READBACK_ACQUIRE, FENCE_IP_DPP);
DISPLAY_LOGE("previous readback out fence is not delivered to framework");
}
mDpuData.readback_info.acq_fence = acqFence;
if (acqFence >= 0) {
/*
* Requtester of readback will get acqFence after presentDisplay
* so validateFences should not check this fence
* in presentDisplay so this function sets pendingAllowed parameter.
*/
setFenceInfo(acqFence, this, FENCE_TYPE_READBACK_ACQUIRE,
FENCE_IP_DPP, FENCE_FROM, true);
}
return NO_ERROR;
}
void ExynosDisplay::initDisplayInterface(uint32_t __unused interfaceType)
{
mDisplayInterface = std::make_unique<ExynosDisplayInterface>();
mDisplayInterface->init(this);
}
void ExynosDisplay::traceLayerTypes() {
size_t g2d_count = 0;
size_t dpu_count = 0;
size_t gpu_count = 0;
size_t skip_count = 0;
for(auto const& layer: mLayers) {
switch (layer->mExynosCompositionType) {
case HWC2_COMPOSITION_EXYNOS:
g2d_count++;
break;
case HWC2_COMPOSITION_CLIENT:
if (layer->mCompositionType == HWC2_COMPOSITION_DEVICE) {
skip_count++;
} else {
gpu_count++;
}
break;
case HWC2_COMPOSITION_DEVICE:
dpu_count++;
break;
default:
break;
}
}
ATRACE_INT("HWComposer: DPU Layer", dpu_count);
ATRACE_INT("HWComposer: G2D Layer", g2d_count);
ATRACE_INT("HWComposer: GPU Layer", gpu_count);
ATRACE_INT("HWComposer: Cached Layer", skip_count);
ATRACE_INT("HWComposer: Total Layer", mLayers.size());
}