/* * 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 #include #include #include #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 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); } if (mInterfaceType == INTERFACE_TYPE_DRM) { setVBlankOffDelay(1); } mDisplayOffAsync = property_get_bool("vendor.display.async_off.supported", false); initDeviceInterface(mInterfaceType); // registerRestrictions(); mResourceManager->updateRestrictions(); mResourceManager->initDisplays(mDisplays, mDisplayMap); char value[PROPERTY_VALUE_MAX]; property_get("vendor.display.lbe.supported", value, "0"); const bool lbe_supported = atoi(value) ? true : false; mNumPrimaryDisplays = 0; for (size_t i = 0; i < mDisplays.size(); i++) { if (mDisplays[i]->mType == HWC_DISPLAY_PRIMARY) { ++mNumPrimaryDisplays; auto iter = pixelDisplayIntfName.find(getDisplayId(HWC_DISPLAY_PRIMARY, i)); if (iter != pixelDisplayIntfName.end()) { PixelDisplayInit(mDisplays[i], iter->second); if (lbe_supported) { mDisplays[i]->initLbe(); } } } } } void ExynosDevice::initDeviceInterface(uint32_t interfaceType) { if (interfaceType == INTERFACE_TYPE_DRM) { mDeviceInterface = std::make_unique(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++; } } // Call handleHotplug() to capture the initial coldplug state of each display. // This is necessary because the hotplug uevent handler created in postInit() // below does not get always triggered when HWC is restarting. handleHotplug(); 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 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(result.length()); } else { if (*outSize == 0) { ALOGE("%s:: outSize is 0", __func__); return; } size_t copySize = min(static_cast(*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(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); } using DisplayHotplugEvent = aidl::android::hardware::graphics::common::DisplayHotplugEvent; DisplayHotplugEvent hotplug_event_to_aidl(bool connected, int hotplugErrorCode) { switch (hotplugErrorCode) { case 0: return connected ? DisplayHotplugEvent::CONNECTED : DisplayHotplugEvent::DISCONNECTED; case 1: return DisplayHotplugEvent::ERROR_INCOMPATIBLE_CABLE; default: return DisplayHotplugEvent::ERROR_UNKNOWN; } } ExynosDisplay* ExynosDevice::findPoweredOffPrimaryDisplay(ExynosDisplay* excludeDisplay) { for (auto disp : mDisplays) { if (disp != excludeDisplay && disp->mType == HWC_DISPLAY_PRIMARY && disp->mPowerModeState == HWC_POWER_MODE_OFF) return disp; } return nullptr; } void ExynosDevice::onHotPlug(uint32_t displayId, bool status, int hotplugErrorCode) { Mutex::Autolock lock(mDeviceCallbackMutex); // If we detect a hotplug of an external display on a foldable device, and we have a // primary/built-in display in a powered off state, we need to use the powered off display's // crtc/decon for the external display, to ensure color management is working properly // (color management is supported only on decon0/decon1). See b/329034082. ExynosDisplay* hotpluggedDisplay = getDisplay(displayId); if (mNumPrimaryDisplays >= 2 && hotpluggedDisplay && hotpluggedDisplay->mType == HWC_DISPLAY_EXTERNAL) { ALOGD("%s: display %s pluggedIn=%d hotplugErrorCode=%d", __func__, hotpluggedDisplay->mDisplayTraceName.c_str(), status, hotplugErrorCode); auto hotpluggedDisplayIntf = hotpluggedDisplay->mDisplayInterface.get(); ExynosDisplay* borrowedCrtcFrom = hotpluggedDisplayIntf->borrowedCrtcFrom(); if (status && hotplugErrorCode == 0) { // The external display has been connected successfully, check if we can find an // available decon for it, before we start initializing it. if (borrowedCrtcFrom) { ALOGW("%s: external display is already using decon of %s", __func__, borrowedCrtcFrom->mDisplayTraceName.c_str()); // Restore the original decon of the external display before proceeding. hotpluggedDisplayIntf->swapCrtcs(borrowedCrtcFrom); } ExynosDisplay* poweredOffPrimaryDisplay = findPoweredOffPrimaryDisplay(nullptr); if (poweredOffPrimaryDisplay) { hotpluggedDisplayIntf->swapCrtcs(poweredOffPrimaryDisplay); } else { // There is no powered off primary display/available decon at the moment, // this means hotplug of external display happened while the foldable device // was in dual concurrent display mode. We will try to switch decon assignment // for the external display later, when one of primary displays is turned off. ALOGD("onHotPlug: No powered off primary displays found!"); } } else { // The external display has been unplugged, or plugged in, but ran into an error. // Restore the original decon assigned to it, if we previously switched decons. if (borrowedCrtcFrom) { hotpluggedDisplayIntf->swapCrtcs(borrowedCrtcFrom); } } } // If the new HotplugEvent API is available, use it, otherwise fall back // to the old V2 API with onVsync hack, if necessary. const auto& hotplugEventCallback = mHwc3CallbackInfos.find(IComposerCallback::TRANSACTION_onHotplugEvent); if (hotplugEventCallback != mHwc3CallbackInfos.end()) { const auto& callbackInfo = hotplugEventCallback->second; if (callbackInfo.funcPointer != nullptr && callbackInfo.callbackData != nullptr) { auto callbackFunc = reinterpret_cast< void (*)(hwc2_callback_data_t callbackData, hwc2_display_t hwcDisplay, aidl::android::hardware::graphics::common::DisplayHotplugEvent)>( callbackInfo.funcPointer); callbackFunc(callbackInfo.callbackData, displayId, hotplug_event_to_aidl(status, hotplugErrorCode)); return; } } 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( 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(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(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(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( 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( mCallbackInfos[HWC2_CALLBACK_VSYNC_PERIOD_TIMING_CHANGED].funcPointer); callbackFunc(callbackData, displayId, timeline); } void ExynosDevice::onContentProtectionUpdated(uint32_t displayId, HdcpLevels hdcpLevels) { Mutex::Autolock lock(mDeviceCallbackMutex); // Workaround to pass content protection updates to SurfaceFlinger // without changing HWC HAL interface. if (isCallbackRegisteredLocked(HWC2_CALLBACK_VSYNC_2_4)) { ALOGI("%s: displayId=%u hdcpLevels=%s sending to SF via onVsync_2_4", __func__, displayId, hdcpLevels.toString().c_str()); hwc2_callback_data_t vsyncCallbackData = mCallbackInfos[HWC2_CALLBACK_VSYNC_2_4].callbackData; HWC2_PFN_VSYNC_2_4 vsyncCallbackFunc = reinterpret_cast( mCallbackInfos[HWC2_CALLBACK_VSYNC_2_4].funcPointer); int32_t connectedLevel = static_cast(hdcpLevels.connectedLevel); int32_t maxLevel = static_cast(hdcpLevels.maxLevel); int32_t timestampValue = (connectedLevel & 0xFF) | ((maxLevel & 0xFF) << 8); vsyncCallbackFunc(vsyncCallbackData, displayId, -timestampValue, ~1); } else ALOGW("%s: onVsync_2_4 is not registered, ignoring onContentProtectionUpdated", __func__); } 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(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(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(ext_hwc2_power_mode_t::PAUSE) || mode == static_cast(ext_hwc2_power_mode_t::RESUME)) { ret = mDisplays[i]->setPowerMode(mode); if (mode == static_cast(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(DisplayType::DISPLAY_PRIMARY) || type >= static_cast(DisplayType::DISPLAY_MAX)) { ALOGE("invalid display type %d", type); return HWC2_ERROR_BAD_PARAMETER; } if (source < static_cast(PanelGammaSource::GAMMA_DEFAULT) || source >= static_cast(PanelGammaSource::GAMMA_TYPES)) { ALOGE("invalid gamma source %d", source); return HWC2_ERROR_BAD_PARAMETER; } return mDisplays[display_id]->SetCurrentPanelGammaSource(static_cast(type), static_cast(source)); } void ExynosDevice::getLayerGenericMetadataKey(uint32_t __unused keyIndex, uint32_t* outKeyLength, char* __unused outKey, bool* __unused outMandatory) { *outKeyLength = 0; return; } void ExynosDevice::setVBlankOffDelay(const 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(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; } // Lock mDisplayMutex during hotplug processing. // Must-have for unplug handling so that in-flight calls to // validateDisplay() and presentDisplay() don't race with // the display being removed. Mutex::Autolock lock(mDisplays[i]->mDisplayMutex); 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, uint32_t refreshPeriod) { 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; ATRACE_INT("Refresh rate indicator callback", static_cast(std::nano::den / (refreshPeriod ?: vsyncPeriod))); auto callbackFunc = reinterpret_cast(callbackInfo.funcPointer); callbackFunc(callbackInfo.callbackData, displayId, vsyncPeriod, refreshPeriod ?: vsyncPeriod); }