blob: e432d34074c9906aa1a001b300da5714a94cbba1 [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 "ExynosDevice.h"
#include <aidl/android/hardware/graphics/composer3/IComposerCallback.h>
#include <sync/sync.h>
#include <sys/mman.h>
#include <unistd.h>
#include "BrightnessController.h"
#include "ExynosDeviceDrmInterface.h"
#include "ExynosDisplay.h"
#include "ExynosExternalDisplayModule.h"
#include "ExynosHWCDebug.h"
#include "ExynosHWCHelper.h"
#include "ExynosLayer.h"
#include "ExynosPrimaryDisplayModule.h"
#include "ExynosResourceManagerModule.h"
#include "ExynosVirtualDisplayModule.h"
#include "VendorGraphicBuffer.h"
using namespace vendor::graphics;
using namespace SOC_VERSION;
using aidl::android::hardware::graphics::composer3::IComposerCallback;
/**
* ExynosDevice implementation
*/
class ExynosDevice;
extern void PixelDisplayInit(ExynosDisplay *exynos_display, const std::string_view instance_str);
static const std::map<const uint32_t, const std::string_view> pixelDisplayIntfName =
{{getDisplayId(HWC_DISPLAY_PRIMARY, 0), "default"},
#ifdef USES_IDISPLAY_INTF_SEC
{getDisplayId(HWC_DISPLAY_PRIMARY, 1), "secondary"}
#endif
};
int hwcDebug;
int hwcFenceDebug[FENCE_IP_ALL];
struct exynos_hwc_control exynosHWCControl;
struct update_time_info updateTimeInfo;
char fence_names[FENCE_MAX][32];
uint32_t getDeviceInterfaceType()
{
if (access(DRM_DEVICE_PATH, F_OK) == NO_ERROR)
return INTERFACE_TYPE_DRM;
else
return INTERFACE_TYPE_FB;
}
ExynosDevice::ExynosDevice(bool vrrApiSupported)
: mGeometryChanged(0),
mVsyncFd(-1),
mExtVsyncFd(-1),
mVsyncDisplayId(getDisplayId(HWC_DISPLAY_PRIMARY, 0)),
mTimestamp(0),
mDisplayMode(0),
mInterfaceType(INTERFACE_TYPE_FB),
mIsInTUI(false),
mVrrApiSupported(vrrApiSupported) {
exynosHWCControl.forceGpu = false;
exynosHWCControl.windowUpdate = true;
exynosHWCControl.forcePanic = false;
exynosHWCControl.skipStaticLayers = true;
exynosHWCControl.skipM2mProcessing = true;
exynosHWCControl.skipResourceAssign = true;
exynosHWCControl.multiResolution = true;
exynosHWCControl.dumpMidBuf = false;
exynosHWCControl.displayMode = DISPLAY_MODE_NUM;
exynosHWCControl.setDDIScaler = false;
exynosHWCControl.skipWinConfig = false;
exynosHWCControl.skipValidate = true;
exynosHWCControl.doFenceFileDump = false;
exynosHWCControl.fenceTracer = 0;
exynosHWCControl.sysFenceLogging = false;
exynosHWCControl.useDynamicRecomp = false;
hwcDebug = 0;
mInterfaceType = getDeviceInterfaceType();
ALOGD("HWC2 : %s : interface type(%d)", __func__, mInterfaceType);
/*
* This order should not be changed
* new ExynosResourceManager ->
* create displays and add them to the list ->
* initDeviceInterface() ->
* ExynosResourceManager::updateRestrictions()
*/
mResourceManager = new ExynosResourceManagerModule(this);
for (size_t i = 0; i < AVAILABLE_DISPLAY_UNITS.size(); i++) {
exynos_display_t display_t = AVAILABLE_DISPLAY_UNITS[i];
ExynosDisplay *exynos_display = NULL;
ALOGD("Create display[%zu] type: %d, index: %d", i, display_t.type, display_t.index);
switch(display_t.type) {
case HWC_DISPLAY_PRIMARY:
exynos_display =
(ExynosDisplay *)(new ExynosPrimaryDisplayModule(display_t.index, this,
display_t.display_name));
if(display_t.index == 0) {
exynos_display->mPlugState = true;
ExynosMPP::mainDisplayWidth = exynos_display->mXres;
if (ExynosMPP::mainDisplayWidth <= 0) {
ExynosMPP::mainDisplayWidth = 1440;
}
ExynosMPP::mainDisplayHeight = exynos_display->mYres;
if (ExynosMPP::mainDisplayHeight <= 0) {
ExynosMPP::mainDisplayHeight = 2560;
}
}
break;
case HWC_DISPLAY_EXTERNAL:
exynos_display =
(ExynosDisplay *)(new ExynosExternalDisplayModule(display_t.index, this,
display_t.display_name));
break;
case HWC_DISPLAY_VIRTUAL:
exynos_display =
(ExynosDisplay *)(new ExynosVirtualDisplayModule(display_t.index, this,
display_t.display_name));
mNumVirtualDisplay = 0;
break;
default:
ALOGE("Unsupported display type(%d)", display_t.type);
break;
}
exynos_display->mDeconNodeName.appendFormat("%s", display_t.decon_node_name.c_str());
mDisplays.add(exynos_display);
mDisplayMap.insert(std::make_pair(exynos_display->mDisplayId, exynos_display));
#ifndef FORCE_DISABLE_DR
if (exynos_display->mDRDefault) exynosHWCControl.useDynamicRecomp = true;
#endif
}
memset(mCallbackInfos, 0, sizeof(mCallbackInfos));
dynamicRecompositionThreadCreate();
for (uint32_t i = 0; i < FENCE_IP_ALL; i++)
hwcFenceDebug[i] = 0;
for (uint32_t i = 0; i < FENCE_MAX; i++) {
memset(fence_names[i], 0, sizeof(fence_names[0]));
sprintf(fence_names[i], "_%2dh", i);
}
for (auto it : mDisplays) {
std::string displayName = std::string(it->mDisplayName.c_str());
it->mErrLogFileWriter.setPrefixName(displayName + "_hwc_error_log");
it->mDebugDumpFileWriter.setPrefixName(displayName + "_hwc_debug");
it->mFenceFileWriter.setPrefixName(displayName + "_hwc_fence_state");
String8 saveString;
saveString.appendFormat("ExynosDisplay %s is initialized", it->mDisplayName.c_str());
saveErrorLog(saveString, it);
}
initDeviceInterface(mInterfaceType);
// registerRestrictions();
mResourceManager->updateRestrictions();
mResourceManager->initDisplays(mDisplays, mDisplayMap);
mResourceManager->initDisplaysTDMInfo();
if (mInterfaceType == INTERFACE_TYPE_DRM) {
setVBlankOffDelay(1);
}
char value[PROPERTY_VALUE_MAX];
property_get("vendor.display.lbe.supported", value, "0");
const bool lbe_supported = atoi(value) ? true : false;
for (size_t i = 0; i < mDisplays.size(); i++) {
if (mDisplays[i]->mType == HWC_DISPLAY_PRIMARY) {
auto iter = pixelDisplayIntfName.find(getDisplayId(HWC_DISPLAY_PRIMARY, i));
if (iter != pixelDisplayIntfName.end()) {
PixelDisplayInit(mDisplays[i], iter->second);
if (lbe_supported) {
mDisplays[i]->initLbe();
}
}
}
}
mDisplayOffAsync = property_get_bool("vendor.display.async_off.supported", false);
}
void ExynosDevice::initDeviceInterface(uint32_t interfaceType)
{
if (interfaceType == INTERFACE_TYPE_DRM) {
mDeviceInterface = std::make_unique<ExynosDeviceDrmInterface>(this);
} else {
LOG_ALWAYS_FATAL("%s::Unknown interface type(%d)",
__func__, interfaceType);
}
mDeviceInterface->init(this);
/* Remove display when display interface is not valid */
for (uint32_t i = 0; i < mDisplays.size();) {
ExynosDisplay* display = mDisplays[i];
display->initDisplayInterface(interfaceType);
if (mDeviceInterface->initDisplayInterface(display->mDisplayInterface) != NO_ERROR) {
ALOGD("Remove display[%d], Failed to initialize display interface", i);
mDisplays.removeAt(i);
mDisplayMap.erase(display->mDisplayId);
delete display;
} else {
i++;
}
}
mDeviceInterface->postInit();
}
ExynosDevice::~ExynosDevice() {
mDRLoopStatus = false;
mDRThread.join();
for(auto& display : mDisplays) {
delete display;
}
mDisplays.clear();
}
bool ExynosDevice::isFirstValidate()
{
for (uint32_t i = 0; i < mDisplays.size(); i++) {
if ((mDisplays[i]->mType != HWC_DISPLAY_VIRTUAL) &&
(!mDisplays[i]->mPowerModeState.has_value() ||
(mDisplays[i]->mPowerModeState.value() == (hwc2_power_mode_t)HWC_POWER_MODE_OFF)))
continue;
if ((mDisplays[i]->mPlugState == true) &&
((mDisplays[i]->mRenderingState != RENDERING_STATE_NONE) &&
(mDisplays[i]->mRenderingState != RENDERING_STATE_PRESENTED)))
return false;
}
return true;
}
bool ExynosDevice::isLastValidate(ExynosDisplay *display)
{
for (uint32_t i = 0; i < mDisplays.size(); i++) {
if (mDisplays[i] == display)
continue;
if ((mDisplays[i]->mType != HWC_DISPLAY_VIRTUAL) &&
(!mDisplays[i]->mPowerModeState.has_value() ||
(mDisplays[i]->mPowerModeState.value() == (hwc2_power_mode_t)HWC_POWER_MODE_OFF)))
continue;
if ((mDisplays[i]->mPlugState == true) &&
(mDisplays[i]->mRenderingState != RENDERING_STATE_VALIDATED) &&
(mDisplays[i]->mRenderingState != RENDERING_STATE_ACCEPTED_CHANGE))
return false;
}
return true;
}
bool ExynosDevice::hasOtherDisplayOn(ExynosDisplay *display) {
for (uint32_t i = 0; i < mDisplays.size(); i++) {
if (mDisplays[i] == display) continue;
if ((mDisplays[i]->mType != HWC_DISPLAY_VIRTUAL) &&
mDisplays[i]->mPowerModeState.has_value() &&
(mDisplays[i]->mPowerModeState.value() != (hwc2_power_mode_t)HWC_POWER_MODE_OFF))
return true;
}
return false;
}
bool ExynosDevice::isDynamicRecompositionThreadAlive()
{
android_atomic_acquire_load(&mDRThreadStatus);
return (mDRThreadStatus > 0);
}
void ExynosDevice::checkDynamicRecompositionThread()
{
ATRACE_CALL();
// If thread was destroyed, create thread and run. (resume status)
if (isDynamicRecompositionThreadAlive() == false) {
for (uint32_t i = 0; i < mDisplays.size(); i++) {
if (mDisplays[i]->mDREnable) {
dynamicRecompositionThreadCreate();
return;
}
}
} else {
// If thread is running and all displays turnned off DR, destroy the thread.
for (uint32_t i = 0; i < mDisplays.size(); i++) {
if (mDisplays[i]->mDREnable)
return;
}
ALOGI("Destroying dynamic recomposition thread");
mDRLoopStatus = false;
mDRWakeUpCondition.notify_one();
mDRThread.join();
}
}
void ExynosDevice::dynamicRecompositionThreadCreate()
{
if (exynosHWCControl.useDynamicRecomp == true) {
ALOGI("Creating dynamic recomposition thread");
mDRLoopStatus = true;
mDRThread = std::thread(&dynamicRecompositionThreadLoop, this);
}
}
void *ExynosDevice::dynamicRecompositionThreadLoop(void *data)
{
ExynosDevice *dev = (ExynosDevice *)data;
ExynosDisplay *display[dev->mDisplays.size()];
uint64_t event_cnt[dev->mDisplays.size()];
for (uint32_t i = 0; i < dev->mDisplays.size(); i++) {
display[i] = dev->mDisplays[i];
event_cnt[i] = 0;
}
android_atomic_inc(&(dev->mDRThreadStatus));
while (dev->mDRLoopStatus) {
for (uint32_t i = 0; i < dev->mDisplays.size(); i++)
event_cnt[i] = display[i]->mUpdateEventCnt;
/*
* If there is no update for more than 5s, favor the client composition mode.
* If all other conditions are met, mode will be switched to client composition.
*/
{
std::unique_lock<std::mutex> lock(dev->mDRWakeUpMutex);
dev->mDRWakeUpCondition.wait_for(lock, std::chrono::seconds(5));
if (!dev->mDRLoopStatus) {
break;
}
}
for (uint32_t i = 0; i < dev->mDisplays.size(); i++) {
if (display[i]->mDREnable &&
display[i]->mPlugState == true &&
event_cnt[i] == display[i]->mUpdateEventCnt) {
if (display[i]->checkDynamicReCompMode() == DEVICE_2_CLIENT) {
display[i]->mUpdateEventCnt = 0;
display[i]->setGeometryChanged(GEOMETRY_DISPLAY_DYNAMIC_RECOMPOSITION);
dev->onRefresh(display[i]->mDisplayId);
}
}
}
}
android_atomic_dec(&(dev->mDRThreadStatus));
return NULL;
}
/**
* Device Functions for HWC 2.0
*/
int32_t ExynosDevice::createVirtualDisplay(
uint32_t width, uint32_t height, int32_t* /*android_pixel_format_t*/ format, ExynosDisplay* display) {
((ExynosVirtualDisplay*)display)->createVirtualDisplay(width, height, format);
return 0;
}
/**
* @param *display
* @return int32_t
*/
int32_t ExynosDevice::destroyVirtualDisplay(ExynosDisplay* display) {
((ExynosVirtualDisplay *)display)->destroyVirtualDisplay();
return 0;
}
void ExynosDevice::dump(uint32_t *outSize, char *outBuffer) {
if (outSize == NULL) {
ALOGE("%s:: outSize is null", __func__);
return;
}
String8 result;
dump(result);
if (outBuffer == NULL) {
*outSize = static_cast<uint32_t>(result.length());
} else {
if (*outSize == 0) {
ALOGE("%s:: outSize is 0", __func__);
return;
}
size_t copySize = min(static_cast<size_t>(*outSize), result.size());
ALOGI("HWC dump:: resultSize(%zu), outSize(%d), copySize(%zu)", result.size(), *outSize,
copySize);
strlcpy(outBuffer, result.c_str(), copySize);
}
}
void ExynosDevice::dump(String8 &result) {
result.append("\n\n");
struct tm* localTime = (struct tm*)localtime((time_t*)&updateTimeInfo.lastUeventTime.tv_sec);
result.appendFormat("lastUeventTime(%02d:%02d:%02d.%03lu) lastTimestamp(%" PRIu64 ")\n",
localTime->tm_hour, localTime->tm_min,
localTime->tm_sec, updateTimeInfo.lastUeventTime.tv_usec/1000, mTimestamp);
localTime = (struct tm*)localtime((time_t*)&updateTimeInfo.lastEnableVsyncTime.tv_sec);
result.appendFormat("lastEnableVsyncTime(%02d:%02d:%02d.%03lu)\n",
localTime->tm_hour, localTime->tm_min,
localTime->tm_sec, updateTimeInfo.lastEnableVsyncTime.tv_usec/1000);
localTime = (struct tm*)localtime((time_t*)&updateTimeInfo.lastDisableVsyncTime.tv_sec);
result.appendFormat("lastDisableVsyncTime(%02d:%02d:%02d.%03lu)\n",
localTime->tm_hour, localTime->tm_min,
localTime->tm_sec, updateTimeInfo.lastDisableVsyncTime.tv_usec/1000);
localTime = (struct tm*)localtime((time_t*)&updateTimeInfo.lastValidateTime.tv_sec);
result.appendFormat("lastValidateTime(%02d:%02d:%02d.%03lu)\n",
localTime->tm_hour, localTime->tm_min,
localTime->tm_sec, updateTimeInfo.lastValidateTime.tv_usec/1000);
localTime = (struct tm*)localtime((time_t*)&updateTimeInfo.lastPresentTime.tv_sec);
result.appendFormat("lastPresentTime(%02d:%02d:%02d.%03lu)\n",
localTime->tm_hour, localTime->tm_min,
localTime->tm_sec, updateTimeInfo.lastPresentTime.tv_usec/1000);
result.appendFormat("\n");
mResourceManager->dump(result);
result.appendFormat("special plane num: %d:\n", getSpecialPlaneNum());
for (uint32_t index = 0; index < getSpecialPlaneNum(); index++) {
result.appendFormat("\tindex: %d attribute 0x%" PRIx64 "\n", getSpecialPlaneId(index),
getSpecialPlaneAttr(index));
}
result.append("\n");
for (size_t i = 0;i < mDisplays.size(); i++) {
ExynosDisplay *display = mDisplays[i];
if (display->mPlugState == true)
display->dump(result);
}
}
uint32_t ExynosDevice::getMaxVirtualDisplayCount() {
#ifdef USES_VIRTUAL_DISPLAY
return 1;
#else
return 0;
#endif
}
int32_t ExynosDevice::registerCallback (
int32_t descriptor, hwc2_callback_data_t callbackData,
hwc2_function_pointer_t point) {
if (descriptor < 0 || descriptor > HWC2_CALLBACK_SEAMLESS_POSSIBLE)
return HWC2_ERROR_BAD_PARAMETER;
Mutex::Autolock lock(mDeviceCallbackMutex);
mCallbackInfos[descriptor].callbackData = callbackData;
mCallbackInfos[descriptor].funcPointer = point;
/* Call hotplug callback for primary display*/
if (descriptor == HWC2_CALLBACK_HOTPLUG) {
HWC2_PFN_HOTPLUG callbackFunc =
reinterpret_cast<HWC2_PFN_HOTPLUG>(mCallbackInfos[descriptor].funcPointer);
if (callbackFunc != nullptr) {
for (auto it : mDisplays) {
if (it->mPlugState)
callbackFunc(callbackData, getDisplayId(it->mType, it->mIndex),
HWC2_CONNECTION_CONNECTED);
}
} else {
// unregistering callback can be used as a sign of ComposerClient's death
for (auto it : mDisplays) {
it->cleanupAfterClientDeath();
}
}
}
/* TODO(b/265244856): called by register callback vsync. it's only hwc2. */
if (descriptor == HWC2_CALLBACK_VSYNC)
mResourceManager->doPreProcessing();
return HWC2_ERROR_NONE;
}
bool ExynosDevice::isCallbackRegisteredLocked(int32_t descriptor) {
if (descriptor < 0 || descriptor > HWC2_CALLBACK_SEAMLESS_POSSIBLE) {
ALOGE("%s:: %d callback is unknown", __func__, descriptor);
return false;
}
if (mCallbackInfos[descriptor].callbackData == nullptr ||
mCallbackInfos[descriptor].funcPointer == nullptr) {
ALOGE("%s:: %d callback is not registered", __func__, descriptor);
return false;
}
return true;
}
bool ExynosDevice::isCallbackAvailable(int32_t descriptor) {
Mutex::Autolock lock(mDeviceCallbackMutex);
return isCallbackRegisteredLocked(descriptor);
}
void ExynosDevice::onHotPlug(uint32_t displayId, bool status, int hotplugErrorCode) {
Mutex::Autolock lock(mDeviceCallbackMutex);
if (!isCallbackRegisteredLocked(HWC2_CALLBACK_HOTPLUG)) return;
if (hotplugErrorCode) {
// We need to pass the error code to SurfaceFlinger, but we cannot modify the HWC
// HAL interface, so for now we'll send the hotplug error via a onVsync callback with
// a negative time value indicating the hotplug error.
if (isCallbackRegisteredLocked(HWC2_CALLBACK_VSYNC_2_4)) {
ALOGD("%s: hotplugErrorCode=%d sending to SF via onVsync_2_4", __func__,
hotplugErrorCode);
hwc2_callback_data_t vsyncCallbackData =
mCallbackInfos[HWC2_CALLBACK_VSYNC_2_4].callbackData;
HWC2_PFN_VSYNC_2_4 vsyncCallbackFunc = reinterpret_cast<HWC2_PFN_VSYNC_2_4>(
mCallbackInfos[HWC2_CALLBACK_VSYNC_2_4].funcPointer);
vsyncCallbackFunc(vsyncCallbackData, displayId, -hotplugErrorCode, ~0);
return;
} else {
ALOGW("%s: onVsync_2_4 is not registered, ignoring hotplugErrorCode=%d", __func__,
hotplugErrorCode);
return;
}
}
hwc2_callback_data_t callbackData = mCallbackInfos[HWC2_CALLBACK_HOTPLUG].callbackData;
HWC2_PFN_HOTPLUG callbackFunc =
reinterpret_cast<HWC2_PFN_HOTPLUG>(mCallbackInfos[HWC2_CALLBACK_HOTPLUG].funcPointer);
callbackFunc(callbackData, displayId,
status ? HWC2_CONNECTION_CONNECTED : HWC2_CONNECTION_DISCONNECTED);
}
void ExynosDevice::onRefreshDisplays() {
for (auto& display : mDisplays) {
onRefresh(display->mDisplayId);
}
}
void ExynosDevice::onRefresh(uint32_t displayId) {
Mutex::Autolock lock(mDeviceCallbackMutex);
if (!isCallbackRegisteredLocked(HWC2_CALLBACK_REFRESH)) return;
if (!checkDisplayConnection(displayId)) return;
ExynosDisplay *display = (ExynosDisplay *)getDisplay(displayId);
if (!display->mPowerModeState.has_value() ||
(display->mPowerModeState.value() == (hwc2_power_mode_t)HWC_POWER_MODE_OFF))
return;
hwc2_callback_data_t callbackData = mCallbackInfos[HWC2_CALLBACK_REFRESH].callbackData;
HWC2_PFN_REFRESH callbackFunc =
reinterpret_cast<HWC2_PFN_REFRESH>(mCallbackInfos[HWC2_CALLBACK_REFRESH].funcPointer);
callbackFunc(callbackData, displayId);
}
void ExynosDevice::onVsync(uint32_t displayId, int64_t timestamp) {
Mutex::Autolock lock(mDeviceCallbackMutex);
if (!isCallbackRegisteredLocked(HWC2_CALLBACK_VSYNC)) return;
hwc2_callback_data_t callbackData = mCallbackInfos[HWC2_CALLBACK_VSYNC].callbackData;
HWC2_PFN_VSYNC callbackFunc =
reinterpret_cast<HWC2_PFN_VSYNC>(mCallbackInfos[HWC2_CALLBACK_VSYNC].funcPointer);
callbackFunc(callbackData, displayId, timestamp);
}
bool ExynosDevice::onVsync_2_4(uint32_t displayId, int64_t timestamp, uint32_t vsyncPeriod) {
Mutex::Autolock lock(mDeviceCallbackMutex);
if (!isCallbackRegisteredLocked(HWC2_CALLBACK_VSYNC_2_4)) return false;
hwc2_callback_data_t callbackData = mCallbackInfos[HWC2_CALLBACK_VSYNC_2_4].callbackData;
HWC2_PFN_VSYNC_2_4 callbackFunc = reinterpret_cast<HWC2_PFN_VSYNC_2_4>(
mCallbackInfos[HWC2_CALLBACK_VSYNC_2_4].funcPointer);
callbackFunc(callbackData, displayId, timestamp, vsyncPeriod);
return true;
}
void ExynosDevice::onVsyncPeriodTimingChanged(uint32_t displayId,
hwc_vsync_period_change_timeline_t *timeline) {
Mutex::Autolock lock(mDeviceCallbackMutex);
if (!timeline) {
ALOGE("vsync period change timeline is null");
return;
}
if (!isCallbackRegisteredLocked(HWC2_CALLBACK_VSYNC_PERIOD_TIMING_CHANGED)) return;
hwc2_callback_data_t callbackData =
mCallbackInfos[HWC2_CALLBACK_VSYNC_PERIOD_TIMING_CHANGED].callbackData;
HWC2_PFN_VSYNC_PERIOD_TIMING_CHANGED callbackFunc =
reinterpret_cast<HWC2_PFN_VSYNC_PERIOD_TIMING_CHANGED>(
mCallbackInfos[HWC2_CALLBACK_VSYNC_PERIOD_TIMING_CHANGED].funcPointer);
callbackFunc(callbackData, displayId, timeline);
}
void ExynosDevice::setHWCDebug(unsigned int debug)
{
hwcDebug = debug;
}
uint32_t ExynosDevice::getHWCDebug()
{
return hwcDebug;
}
void ExynosDevice::setHWCFenceDebug(uint32_t typeNum, uint32_t ipNum, uint32_t mode)
{
if (typeNum > FENCE_TYPE_ALL || typeNum < 0 || ipNum > FENCE_IP_ALL || ipNum < 0
|| mode > 1 || mode < 0) {
ALOGE("%s:: input is not valid type(%u), IP(%u), mode(%d)", __func__, typeNum, ipNum, mode);
return;
}
uint32_t value = 0;
if (typeNum == FENCE_TYPE_ALL)
value = (1 << FENCE_TYPE_ALL) - 1;
else
value = 1 << typeNum;
if (ipNum == FENCE_IP_ALL) {
for (uint32_t i = 0; i < FENCE_IP_ALL; i++) {
if (mode)
hwcFenceDebug[i] |= value;
else
hwcFenceDebug[i] &= (~value);
}
} else {
if (mode)
hwcFenceDebug[ipNum] |= value;
else
hwcFenceDebug[ipNum] &= (~value);
}
}
void ExynosDevice::getHWCFenceDebug()
{
for (uint32_t i = 0; i < FENCE_IP_ALL; i++)
ALOGE("[HWCFenceDebug] IP_Number(%d) : Debug(%x)", i, hwcFenceDebug[i]);
}
void ExynosDevice::setHWCControl(uint32_t displayId, uint32_t ctrl, int32_t val) {
ExynosDisplay *exynosDisplay = NULL;
switch (ctrl) {
case HWC_CTL_FORCE_GPU:
ALOGI("%s::HWC_CTL_FORCE_GPU on/off=%d", __func__, val);
exynosHWCControl.forceGpu = (unsigned int)val;
setGeometryChanged(GEOMETRY_DEVICE_CONFIG_CHANGED);
onRefresh(displayId);
break;
case HWC_CTL_WINDOW_UPDATE:
ALOGI("%s::HWC_CTL_WINDOW_UPDATE on/off=%d", __func__, val);
exynosHWCControl.windowUpdate = (unsigned int)val;
setGeometryChanged(GEOMETRY_DEVICE_CONFIG_CHANGED);
onRefresh(displayId);
break;
case HWC_CTL_FORCE_PANIC:
ALOGI("%s::HWC_CTL_FORCE_PANIC on/off=%d", __func__, val);
exynosHWCControl.forcePanic = (unsigned int)val;
setGeometryChanged(GEOMETRY_DEVICE_CONFIG_CHANGED);
break;
case HWC_CTL_SKIP_STATIC:
ALOGI("%s::HWC_CTL_SKIP_STATIC on/off=%d", __func__, val);
exynosHWCControl.skipStaticLayers = (unsigned int)val;
setGeometryChanged(GEOMETRY_DEVICE_CONFIG_CHANGED);
break;
case HWC_CTL_SKIP_M2M_PROCESSING:
ALOGI("%s::HWC_CTL_SKIP_M2M_PROCESSING on/off=%d", __func__, val);
exynosHWCControl.skipM2mProcessing = (unsigned int)val;
setGeometryChanged(GEOMETRY_DEVICE_CONFIG_CHANGED);
break;
case HWC_CTL_SKIP_RESOURCE_ASSIGN:
ALOGI("%s::HWC_CTL_SKIP_RESOURCE_ASSIGN on/off=%d", __func__, val);
exynosHWCControl.skipResourceAssign = (unsigned int)val;
setGeometryChanged(GEOMETRY_DEVICE_CONFIG_CHANGED);
onRefreshDisplays();
break;
case HWC_CTL_SKIP_VALIDATE:
ALOGI("%s::HWC_CTL_SKIP_VALIDATE on/off=%d", __func__, val);
exynosHWCControl.skipValidate = (unsigned int)val;
setGeometryChanged(GEOMETRY_DEVICE_CONFIG_CHANGED);
onRefreshDisplays();
break;
case HWC_CTL_DUMP_MID_BUF:
ALOGI("%s::HWC_CTL_DUMP_MID_BUF on/off=%d", __func__, val);
exynosHWCControl.dumpMidBuf = (unsigned int)val;
setGeometryChanged(GEOMETRY_DEVICE_CONFIG_CHANGED);
onRefreshDisplays();
break;
case HWC_CTL_CAPTURE_READBACK:
captureScreenWithReadback(displayId);
break;
case HWC_CTL_DISPLAY_MODE:
ALOGI("%s::HWC_CTL_DISPLAY_MODE mode=%d", __func__, val);
setDisplayMode((uint32_t)val);
setGeometryChanged(GEOMETRY_DEVICE_CONFIG_CHANGED);
onRefreshDisplays();
break;
// Support DDI scalser {
case HWC_CTL_DDI_RESOLUTION_CHANGE:
ALOGI("%s::HWC_CTL_DDI_RESOLUTION_CHANGE mode=%d", __func__, val);
exynosDisplay = (ExynosDisplay *)getDisplay(displayId);
uint32_t width, height;
/* TODO: Add branch here for each resolution/index */
switch(val) {
case 1:
case 2:
case 3:
default:
width = 1440; height = 2960;
break;
}
if (exynosDisplay == NULL) {
for (uint32_t i = 0; i < mDisplays.size(); i++) {
mDisplays[i]->setDDIScalerEnable(width, height);
}
} else {
exynosDisplay->setDDIScalerEnable(width, height);
}
setGeometryChanged(GEOMETRY_DISPLAY_RESOLUTION_CHANGED);
onRefreshDisplays();
break;
// } Support DDI scaler
case HWC_CTL_ENABLE_COMPOSITION_CROP:
case HWC_CTL_ENABLE_EXYNOSCOMPOSITION_OPT:
case HWC_CTL_ENABLE_CLIENTCOMPOSITION_OPT:
case HWC_CTL_USE_MAX_G2D_SRC:
case HWC_CTL_ENABLE_HANDLE_LOW_FPS:
case HWC_CTL_ENABLE_EARLY_START_MPP:
exynosDisplay = (ExynosDisplay *)getDisplay(displayId);
if (exynosDisplay == NULL) {
for (uint32_t i = 0; i < mDisplays.size(); i++) {
mDisplays[i]->setHWCControl(ctrl, val);
}
} else {
exynosDisplay->setHWCControl(ctrl, val);
}
setGeometryChanged(GEOMETRY_DEVICE_CONFIG_CHANGED);
onRefreshDisplays();
break;
case HWC_CTL_DYNAMIC_RECOMP:
ALOGI("%s::HWC_CTL_DYNAMIC_RECOMP on/off = %d", __func__, val);
setDynamicRecomposition(displayId, (unsigned int)val);
break;
case HWC_CTL_ENABLE_FENCE_TRACER:
ALOGI("%s::HWC_CTL_ENABLE_FENCE_TRACER on/off=%d", __func__, val);
exynosHWCControl.fenceTracer = (unsigned int)val;
break;
case HWC_CTL_SYS_FENCE_LOGGING:
ALOGI("%s::HWC_CTL_SYS_FENCE_LOGGING on/off=%d", __func__, val);
exynosHWCControl.sysFenceLogging = (unsigned int)val;
break;
case HWC_CTL_DO_FENCE_FILE_DUMP:
ALOGI("%s::HWC_CTL_DO_FENCE_FILE_DUMP on/off=%d", __func__, val);
exynosHWCControl.doFenceFileDump = (unsigned int)val;
break;
default:
ALOGE("%s: unsupported HWC_CTL (%d)", __func__, ctrl);
break;
}
}
void ExynosDevice::setDisplayMode(uint32_t displayMode)
{
exynosHWCControl.displayMode = displayMode;
}
void ExynosDevice::setDynamicRecomposition(uint32_t displayId, unsigned int on) {
exynosHWCControl.useDynamicRecomp = on;
ExynosDisplay *display = getDisplay(displayId);
if (display) {
display->mDRDefault = on;
display->mDREnable = on;
onRefresh(displayId);
}
}
bool ExynosDevice::checkDisplayConnection(uint32_t displayId)
{
ExynosDisplay *display = getDisplay(displayId);
if (!display)
return false;
else
return display->mPlugState;
}
bool ExynosDevice::checkNonInternalConnection()
{
for (uint32_t i = 0; i < mDisplays.size(); i++) {
switch(mDisplays[i]->mType) {
case HWC_DISPLAY_PRIMARY:
break;
case HWC_DISPLAY_EXTERNAL:
case HWC_DISPLAY_VIRTUAL:
if (mDisplays[i]->mPlugState)
return true;
break;
default:
break;
}
}
return false;
}
void ExynosDevice::getCapabilitiesLegacy(uint32_t *outCount, int32_t *outCapabilities) {
uint32_t capabilityNum = 0;
#ifdef HWC_SUPPORT_COLOR_TRANSFORM
capabilityNum++;
#endif
#ifndef HWC_NO_SUPPORT_SKIP_VALIDATE
capabilityNum++;
#endif
if (outCapabilities == NULL) {
*outCount = capabilityNum;
return;
}
if (capabilityNum != *outCount) {
ALOGE("%s:: invalid outCount(%d), should be(%d)", __func__, *outCount, capabilityNum);
return;
}
#if defined(HWC_SUPPORT_COLOR_TRANSFORM) || !defined(HWC_NO_SUPPORT_SKIP_VALIDATE)
uint32_t index = 0;
#endif
#ifdef HWC_SUPPORT_COLOR_TRANSFORM
outCapabilities[index++] = HWC2_CAPABILITY_SKIP_CLIENT_COLOR_TRANSFORM;
#endif
#ifndef HWC_NO_SUPPORT_SKIP_VALIDATE
outCapabilities[index++] = HWC2_CAPABILITY_SKIP_VALIDATE;
#endif
return;
}
void ExynosDevice::getCapabilities(uint32_t *outCount, int32_t *outCapabilities) {
uint32_t capabilityNum = 0;
#ifdef HWC_SUPPORT_COLOR_TRANSFORM
capabilityNum++;
#endif
if (outCapabilities == NULL) {
*outCount = capabilityNum;
return;
}
if (capabilityNum != *outCount) {
ALOGE("%s:: invalid outCount(%d), should be(%d)", __func__, *outCount, capabilityNum);
return;
}
#if defined(HWC_SUPPORT_COLOR_TRANSFORM)
uint32_t index = 0;
outCapabilities[index++] = HWC2_CAPABILITY_SKIP_CLIENT_COLOR_TRANSFORM;
#endif
return;
}
void ExynosDevice::clearGeometryChanged()
{
mGeometryChanged = 0;
}
bool ExynosDevice::canSkipValidate()
{
/*
* This should be called by presentDisplay()
* when presentDisplay() is called without validateDisplay() call
*/
int ret = 0;
if (exynosHWCControl.skipValidate == false)
return false;
for (uint32_t i = 0; i < mDisplays.size(); i++) {
/*
* Check all displays.
* Resource assignment can have problem if validateDisplay is skipped
* on only some displays.
* All display's validateDisplay should be skipped or all display's validateDisplay
* should not be skipped.
*/
if (mDisplays[i]->mPlugState && mDisplays[i]->mPowerModeState.has_value() &&
mDisplays[i]->mPowerModeState.value() != HWC2_POWER_MODE_OFF) {
/*
* presentDisplay is called without validateDisplay.
* Call functions that should be called in validateDiplay
*/
mDisplays[i]->doPreProcessing();
mDisplays[i]->checkLayerFps();
if ((ret = mDisplays[i]->canSkipValidate()) != NO_ERROR) {
ALOGD_AND_ATRACE_NAME(eDebugSkipValidate,
"Display[%d] can't skip validate (%d), renderingState(%d), "
"geometryChanged(0x%" PRIx64 ")",
mDisplays[i]->mDisplayId, ret, mDisplays[i]->mRenderingState,
mGeometryChanged);
return false;
} else {
HDEBUGLOGD(eDebugSkipValidate, "Display[%d] can skip validate (%d), renderingState(%d), geometryChanged(0x%" PRIx64 ")",
mDisplays[i]->mDisplayId, ret,
mDisplays[i]->mRenderingState, mGeometryChanged);
}
}
}
return true;
}
bool ExynosDevice::validateFences(ExynosDisplay *display) {
return mFenceTracker.validateFences(display);
}
void ExynosDevice::compareVsyncPeriod() {
/* TODO(b/265244856): to clarify what purpose of the function */
ExynosDisplay *primary_display = getDisplay(getDisplayId(HWC_DISPLAY_PRIMARY, 0));
ExynosDisplay *external_display = getDisplay(getDisplayId(HWC_DISPLAY_EXTERNAL, 0));
mVsyncDisplayId = getDisplayId(HWC_DISPLAY_PRIMARY, 0);
if ((external_display == nullptr) ||
(!external_display->mPowerModeState.has_value() ||
(external_display->mPowerModeState.value() == HWC2_POWER_MODE_OFF))) {
return;
} else if (!primary_display->mPowerModeState.has_value() ||
(primary_display->mPowerModeState.value() == HWC2_POWER_MODE_OFF)) {
mVsyncDisplayId = getDisplayId(HWC_DISPLAY_EXTERNAL, 0);
return;
} else if (primary_display->mPowerModeState.has_value() &&
((primary_display->mPowerModeState.value() == HWC2_POWER_MODE_DOZE) ||
(primary_display->mPowerModeState.value() == HWC2_POWER_MODE_DOZE_SUSPEND)) &&
(external_display->mVsyncPeriod >= DOZE_VSYNC_PERIOD)) { /*30fps*/
mVsyncDisplayId = getDisplayId(HWC_DISPLAY_EXTERNAL, 0);
return;
} else if (primary_display->mVsyncPeriod <= external_display->mVsyncPeriod) {
mVsyncDisplayId = getDisplayId(HWC_DISPLAY_EXTERNAL, 0);
return;
}
return;
}
ExynosDevice::captureReadbackClass::captureReadbackClass(
ExynosDevice *device) :
mDevice(device)
{
if (device == nullptr)
return;
}
ExynosDevice::captureReadbackClass::~captureReadbackClass()
{
VendorGraphicBufferMapper& gMapper(VendorGraphicBufferMapper::get());
if (mBuffer != nullptr)
gMapper.freeBuffer(mBuffer);
if (mDevice != nullptr)
mDevice->clearWaitingReadbackReqDone();
}
int32_t ExynosDevice::captureReadbackClass::allocBuffer(
uint32_t format, uint32_t w, uint32_t h)
{
VendorGraphicBufferAllocator& gAllocator(VendorGraphicBufferAllocator::get());
uint32_t dstStride = 0;
uint64_t usage = static_cast<uint64_t>(GRALLOC1_CONSUMER_USAGE_HWCOMPOSER |
GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN);
status_t error = NO_ERROR;
error = gAllocator.allocate(w, h, format, 1, usage, &mBuffer, &dstStride, "HWC");
if ((error != NO_ERROR) || (mBuffer == nullptr)) {
ALOGE("failed to allocate destination buffer(%dx%d): %d",
w, h, error);
return static_cast<int32_t>(error);
}
return NO_ERROR;
}
void ExynosDevice::captureReadbackClass::saveToFile(const String8 &fileName)
{
if (mBuffer == nullptr) {
ALOGE("%s:: buffer is null", __func__);
return;
}
char filePath[MAX_DEV_NAME] = {0};
VendorGraphicBufferMeta gmeta(mBuffer);
snprintf(filePath, MAX_DEV_NAME,
"%s/%s", WRITEBACK_CAPTURE_PATH, fileName.c_str());
FILE *fp = fopen(filePath, "w");
if (fp) {
uint32_t writeSize =
gmeta.stride * gmeta.vstride * formatToBpp(gmeta.format)/8;
void *writebackData = mmap(0, writeSize,
PROT_READ|PROT_WRITE, MAP_SHARED, gmeta.fd, 0);
if (writebackData != MAP_FAILED && writebackData != NULL) {
size_t result = fwrite(writebackData, writeSize, 1, fp);
munmap(writebackData, writeSize);
ALOGD("Success to write %zu data, size(%d)", result, writeSize);
} else {
ALOGE("Fail to mmap");
}
fclose(fp);
} else {
ALOGE("Fail to open %s", filePath);
}
}
void ExynosDevice::signalReadbackDone()
{
if (mIsWaitingReadbackReqDone) {
Mutex::Autolock lock(mCaptureMutex);
mCaptureCondition.signal();
}
}
void ExynosDevice::captureScreenWithReadback(uint32_t displayId) {
ExynosDisplay *display = getDisplay(displayId);
if (display == nullptr) {
ALOGE("There is no display(%d)", displayId);
return;
}
int32_t outFormat;
int32_t outDataspace;
int32_t ret = 0;
if ((ret = display->getReadbackBufferAttributes(
&outFormat, &outDataspace)) != HWC2_ERROR_NONE) {
ALOGE("getReadbackBufferAttributes fail, ret(%d)", ret);
return;
}
captureReadbackClass captureClass(this);
if ((ret = captureClass.allocBuffer(outFormat, display->mXres, display->mYres))
!= NO_ERROR) {
return;
}
mIsWaitingReadbackReqDone = true;
if (display->setReadbackBuffer(captureClass.getBuffer(), -1, true) != HWC2_ERROR_NONE) {
ALOGE("setReadbackBuffer fail");
return;
}
/* Update screen */
onRefresh(displayId);
/* Wait for handling readback */
uint32_t waitPeriod = display->mVsyncPeriod * 3;
{
Mutex::Autolock lock(mCaptureMutex);
status_t err = mCaptureCondition.waitRelative(
mCaptureMutex, us2ns(waitPeriod));
if (err == TIMED_OUT) {
ALOGE("timeout, readback is not requested");
return;
} else if (err != NO_ERROR) {
ALOGE("error waiting for readback request: %s (%d)", strerror(-err), err);
return;
} else {
ALOGD("readback request is done");
}
}
int32_t fence = -1;
if (display->getReadbackBufferFence(&fence) != HWC2_ERROR_NONE) {
ALOGE("getReadbackBufferFence fail");
return;
}
if (sync_wait(fence, 1000) < 0) {
ALOGE("sync wait error, fence(%d)", fence);
}
hwcFdClose(fence);
String8 fileName;
time_t curTime = time(NULL);
struct tm *tm = localtime(&curTime);
fileName.appendFormat("capture_format%d_%dx%d_%04d-%02d-%02d_%02d_%02d_%02d.raw",
outFormat, display->mXres, display->mYres,
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
captureClass.saveToFile(fileName);
}
int32_t ExynosDevice::setDisplayDeviceMode(int32_t display_id, int32_t mode)
{
int32_t ret = HWC2_ERROR_NONE;
for (size_t i = 0; i < mDisplays.size(); i++) {
if (mDisplays[i]->mType == HWC_DISPLAY_PRIMARY && mDisplays[i]->mDisplayId == display_id) {
if (mode == static_cast<int32_t>(ext_hwc2_power_mode_t::PAUSE) ||
mode == static_cast<int32_t>(ext_hwc2_power_mode_t::RESUME)) {
ret = mDisplays[i]->setPowerMode(mode);
if (mode == static_cast<int32_t>(ext_hwc2_power_mode_t::RESUME) &&
ret == HWC2_ERROR_NONE) {
onRefresh(display_id);
}
return ret;
} else {
return HWC2_ERROR_UNSUPPORTED;
}
}
}
return HWC2_ERROR_UNSUPPORTED;
}
int32_t ExynosDevice::setPanelGammaTableSource(int32_t display_id, int32_t type, int32_t source) {
if (display_id < HWC_DISPLAY_PRIMARY || display_id >= HWC_NUM_DISPLAY_TYPES) {
ALOGE("invalid display %d", display_id);
return HWC2_ERROR_BAD_DISPLAY;
}
if (type < static_cast<int32_t>(DisplayType::DISPLAY_PRIMARY) ||
type >= static_cast<int32_t>(DisplayType::DISPLAY_MAX)) {
ALOGE("invalid display type %d", type);
return HWC2_ERROR_BAD_PARAMETER;
}
if (source < static_cast<int32_t>(PanelGammaSource::GAMMA_DEFAULT) ||
source >= static_cast<int32_t>(PanelGammaSource::GAMMA_TYPES)) {
ALOGE("invalid gamma source %d", source);
return HWC2_ERROR_BAD_PARAMETER;
}
return mDisplays[display_id]->SetCurrentPanelGammaSource(static_cast<DisplayType>(type),
static_cast<PanelGammaSource>(source));
}
void ExynosDevice::getLayerGenericMetadataKey(uint32_t __unused keyIndex,
uint32_t* outKeyLength, char* __unused outKey, bool* __unused outMandatory)
{
*outKeyLength = 0;
return;
}
void ExynosDevice::setVBlankOffDelay(int vblankOffDelay) {
static constexpr const char *kVblankOffDelayPath = "/sys/module/drm/parameters/vblankoffdelay";
writeIntToFile(kVblankOffDelayPath, vblankOffDelay);
}
uint32_t ExynosDevice::getWindowPlaneNum()
{
/*
* ExynosDevice supports DPU Window Composition.
* The number of windows can be composited is depends on the number of DPP planes.
*/
return mDeviceInterface->getNumDPPChs();
}
uint32_t ExynosDevice::getSpecialPlaneNum()
{
/*
* ExynosDevice might support something special purpose planes.
* These planes are different with DPP planes.
*/
return mDeviceInterface->getNumSPPChs();
}
uint32_t ExynosDevice::getSpecialPlaneNum(uint32_t /*displayId*/) {
/*
* TODO: create the query function for each display
*/
return mDeviceInterface->getNumSPPChs();
}
uint32_t ExynosDevice::getSpecialPlaneId(uint32_t index)
{
return mDeviceInterface->getSPPChId(index);
}
uint64_t ExynosDevice::getSpecialPlaneAttr(uint32_t index)
{
return mDeviceInterface->getSPPChAttr(index);
}
int32_t ExynosDevice::registerHwc3Callback(uint32_t descriptor, hwc2_callback_data_t callbackData,
hwc2_function_pointer_t point) {
Mutex::Autolock lock(mDeviceCallbackMutex);
mHwc3CallbackInfos[descriptor].callbackData = callbackData;
mHwc3CallbackInfos[descriptor].funcPointer = point;
return HWC2_ERROR_NONE;
}
void ExynosDevice::onVsyncIdle(hwc2_display_t displayId) {
Mutex::Autolock lock(mDeviceCallbackMutex);
const auto &idleCallback = mHwc3CallbackInfos.find(IComposerCallback::TRANSACTION_onVsyncIdle);
if (idleCallback == mHwc3CallbackInfos.end()) return;
const auto &callbackInfo = idleCallback->second;
if (callbackInfo.funcPointer == nullptr || callbackInfo.callbackData == nullptr) return;
auto callbackFunc =
reinterpret_cast<void (*)(hwc2_callback_data_t callbackData,
hwc2_display_t hwcDisplay)>(callbackInfo.funcPointer);
callbackFunc(callbackInfo.callbackData, displayId);
}
void ExynosDevice::handleHotplug() {
bool hpdStatus = false;
for (size_t i = 0; i < mDisplays.size(); i++) {
if (mDisplays[i] == nullptr) {
continue;
}
if (mDisplays[i]->checkHotplugEventUpdated(hpdStatus)) {
mDisplays[i]->handleHotplugEvent(hpdStatus);
mDisplays[i]->hotplug();
mDisplays[i]->invalidate();
}
}
}
void ExynosDevice::onRefreshRateChangedDebug(hwc2_display_t displayId, uint32_t vsyncPeriod) {
Mutex::Autolock lock(mDeviceCallbackMutex);
const auto &refreshRateCallback =
mHwc3CallbackInfos.find(IComposerCallback::TRANSACTION_onRefreshRateChangedDebug);
if (refreshRateCallback == mHwc3CallbackInfos.end()) return;
const auto &callbackInfo = refreshRateCallback->second;
if (callbackInfo.funcPointer == nullptr || callbackInfo.callbackData == nullptr) return;
auto callbackFunc =
reinterpret_cast<void (*)(hwc2_callback_data_t callbackData, hwc2_display_t hwcDisplay,
hwc2_vsync_period_t)>(callbackInfo.funcPointer);
callbackFunc(callbackInfo.callbackData, displayId, vsyncPeriod);
}