blob: 5d962d98a2da5963610b771c88593bf3f678965a [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.
*/
#include "ExynosDevice.h"
#include "ExynosDisplay.h"
#include "ExynosLayer.h"
#include "ExynosPrimaryDisplayModule.h"
#include "ExynosResourceManagerModule.h"
#include "ExynosExternalDisplayModule.h"
#include "ExynosVirtualDisplayModule.h"
#include "ExynosHWCDebug.h"
#include "ExynosHWCHelper.h"
/**
* ExynosDevice implementation
*/
class ExynosDevice;
extern void vsync_callback(hwc2_callback_data_t callbackData,
hwc2_display_t displayId, int64_t timestamp);
extern uint32_t mFenceLogSize;
int hwcDebug;
int hwcFenceDebug[FENCE_IP_ALL];
struct exynos_hwc_control exynosHWCControl;
struct update_time_info updateTimeInfo;
char fence_names[FENCE_MAX][32];
GrallocWrapper::Mapper* ExynosDevice::mMapper = NULL;
GrallocWrapper::Allocator* ExynosDevice::mAllocator = NULL;
void handle_vsync_event(ExynosDevice *dev) {
int err = 0;
if ((dev == NULL) || (dev->mCallbackInfos[HWC2_CALLBACK_VSYNC].funcPointer == NULL))
return;
dev->compareVsyncPeriod();
hwc2_callback_data_t callbackData =
dev->mCallbackInfos[HWC2_CALLBACK_VSYNC].callbackData;
HWC2_PFN_VSYNC callbackFunc =
(HWC2_PFN_VSYNC)dev->mCallbackInfos[HWC2_CALLBACK_VSYNC].funcPointer;
err = lseek(dev->mVsyncFd, 0, SEEK_SET);
if (err < 0 ) {
ExynosDisplay *display = (ExynosDisplay*)dev->getDisplay(HWC_DISPLAY_PRIMARY);
if (display != NULL) {
if (display->mVsyncState == HWC2_VSYNC_ENABLE)
ALOGE("error seeking to vsync timestamp: %s", strerror(errno));
}
return;
}
if (callbackData != NULL && callbackFunc != NULL) {
/** Vsync read **/
char buf[4096];
err = read(dev->mVsyncFd , buf, sizeof(buf));
if (err < 0) {
ALOGE("error reading vsync timestamp: %s", strerror(errno));
return;
}
if (dev->mVsyncDisplay != HWC_DISPLAY_PRIMARY) {
// Vsync of primary display is not used
return;
}
dev->mTimestamp = strtoull(buf, NULL, 0);
gettimeofday(&updateTimeInfo.lastUeventTime, NULL);
/** Vsync callback **/
callbackFunc(callbackData, HWC_DISPLAY_PRIMARY, dev->mTimestamp);
}
}
void handle_external_vsync_event(ExynosDevice *dev) {
int err = 0;
if ((dev == NULL) || (dev->mCallbackInfos[HWC2_CALLBACK_VSYNC].funcPointer == NULL))
return;
dev->compareVsyncPeriod();
hwc2_callback_data_t callbackData =
dev->mCallbackInfos[HWC2_CALLBACK_VSYNC].callbackData;
HWC2_PFN_VSYNC callbackFunc =
(HWC2_PFN_VSYNC)dev->mCallbackInfos[HWC2_CALLBACK_VSYNC].funcPointer;
err = lseek(dev->mExtVsyncFd, 0, SEEK_SET);
if(err < 0 ) {
ExynosExternalDisplay *display = (ExynosExternalDisplay*)dev->getDisplay(HWC_DISPLAY_EXTERNAL);
if (display->mHpdStatus) {
ALOGE("error seeking to vsync timestamp: %s", strerror(errno));
}
return;
}
if (callbackData != NULL && callbackFunc != NULL) {
/** Vsync read **/
char buf[4096];
err = read(dev->mExtVsyncFd , buf, sizeof(buf));
if (err < 0) {
ALOGE("error reading vsync timestamp: %s", strerror(errno));
return;
}
if (dev->mVsyncDisplay != HWC_DISPLAY_EXTERNAL) {
// Vsync of external display is not used
return;
}
dev->mTimestamp = strtoull(buf, NULL, 0);
/** Vsync callback **/
callbackFunc(callbackData, HWC_DISPLAY_PRIMARY, dev->mTimestamp);
}
}
void *hwc_eventHndler_thread(void *data) {
/** uevent init **/
char uevent_desc[4096];
memset(uevent_desc, 0, sizeof(uevent_desc));
ExynosDevice *dev = (ExynosDevice*)data;
setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
uevent_init();
/** Vsync init. **/
char devname[MAX_DEV_NAME + 1];
devname[MAX_DEV_NAME] = '\0';
strncpy(devname, VSYNC_DEV_PREFIX, MAX_DEV_NAME);
strlcat(devname, VSYNC_DEV_NAME, MAX_DEV_NAME);
dev->mVsyncFd = open(devname, O_RDONLY);
char devname_ext[MAX_DEV_NAME + 1];
devname_ext[MAX_DEV_NAME] = '\0';
strncpy(devname_ext, VSYNC_DEV_PREFIX, MAX_DEV_NAME);
strlcat(devname_ext, VSYNC_DEV_NAME_EXT, MAX_DEV_NAME);
dev->mExtVsyncFd = open(devname_ext, O_RDONLY);
char ueventname_ext[MAX_DEV_NAME + 1];
ueventname_ext[MAX_DEV_NAME] = '\0';
sprintf(ueventname_ext, DP_UEVENT_NAME, DP_LINK_NAME);
ALOGI("uevent name of ext: %s", ueventname_ext);
if (dev->mVsyncFd < 0) {
ALOGI("Failed to open vsync attribute at %s", devname);
devname[strlen(VSYNC_DEV_PREFIX)] = '\0';
strlcat(devname, VSYNC_DEV_MIDDLE, MAX_DEV_NAME);
strlcat(devname, VSYNC_DEV_NAME, MAX_DEV_NAME);
ALOGI("Retrying with %s", devname);
dev->mVsyncFd = open(devname, O_RDONLY);
ALOGI("dev->mVsyncFd %d", dev->mVsyncFd);
}
if (dev->mExtVsyncFd < 0) {
ALOGI("Failed to open vsync attribute at %s", devname_ext);
devname_ext[strlen(VSYNC_DEV_PREFIX)] = '\0';
strlcat(devname_ext, VSYNC_DEV_MIDDLE, MAX_DEV_NAME);
strlcat(devname_ext, VSYNC_DEV_NAME_EXT, MAX_DEV_NAME);
ALOGI("Retrying with %s", devname_ext);
dev->mExtVsyncFd = open(devname_ext, O_RDONLY);
ALOGI("dev->mExtVsyncFd %d", dev->mExtVsyncFd);
}
/** Poll definitions **/
/** TODO : Hotplug here **/
struct pollfd fds[3];
fds[0].fd = dev->mVsyncFd;
fds[0].events = POLLPRI;
fds[1].fd = uevent_get_fd();
fds[1].events = POLLIN;
fds[2].fd = dev->mExtVsyncFd;
fds[2].events = POLLPRI;
/** Polling events **/
while (true) {
int err = poll(fds, 3, -1);
if (err > 0) {
if (fds[0].revents & POLLPRI) {
handle_vsync_event((ExynosDevice*)dev);
}
else if (fds[1].revents & POLLIN) {
uevent_next_event(uevent_desc, sizeof(uevent_desc) - 2);
bool dp_status = !strcmp(uevent_desc, ueventname_ext);
if (dp_status) {
ExynosExternalDisplayModule *display = (ExynosExternalDisplayModule*)dev->getDisplay(HWC_DISPLAY_EXTERNAL);
display->handleHotplugEvent();
}
}
else if (fds[2].revents & POLLPRI) {
handle_external_vsync_event((ExynosDevice*)dev);
}
}
else if (err == -1) {
if (errno == EINTR)
break;
ALOGE("error in vsync thread: %s", strerror(errno));
}
}
return NULL;
}
ExynosDevice::ExynosDevice()
: mGeometryChanged(0),
mDRThread(0),
mVsyncFd(-1),
mExtVsyncFd(-1),
mVsyncDisplay(HWC_DISPLAY_PRIMARY),
mTimestamp(0),
mDisplayMode(0)
{
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;
ALOGD("HWC2 : %s : %d ", __func__, __LINE__);
int ret = 0;
mResourceManager = new ExynosResourceManagerModule(this);
ExynosPrimaryDisplayModule *primary_display = new ExynosPrimaryDisplayModule(HWC_DISPLAY_PRIMARY, this);
primary_display->mPlugState = true;
ExynosMPP::mainDisplayWidth = primary_display->mXres;
if (ExynosMPP::mainDisplayWidth <= 0) {
ExynosMPP::mainDisplayWidth = 1440;
}
ExynosMPP::mainDisplayHeight = primary_display->mYres;
if (ExynosMPP::mainDisplayHeight <= 0) {
ExynosMPP::mainDisplayHeight = 2560;
}
ExynosExternalDisplayModule *external_display = new ExynosExternalDisplayModule(HWC_DISPLAY_EXTERNAL, this);
ExynosVirtualDisplayModule *virtual_display = new ExynosVirtualDisplayModule(HWC_DISPLAY_VIRTUAL, this);
mNumVirtualDisplay = 0;
mResourceManager->updateRestrictions(primary_display->mDisplayFd);
mDisplays.add((ExynosDisplay*) primary_display);
mDisplays.add((ExynosDisplay*) external_display);
mDisplays.add((ExynosDisplay*) virtual_display);
memset(mCallbackInfos, 0, sizeof(mCallbackInfos));
#ifndef FORCE_DISABLE_DR
exynosHWCControl.useDynamicRecomp = (primary_display->mDREnable) || (external_display->mDREnable) ||
(virtual_display->mDREnable);
#else
exynosHWCControl.useDynamicRecomp = false;
#endif
dynamicRecompositionThreadCreate();
/** Event handler thread creation **/
/* TODO : Check last argument */
ret = pthread_create(&mEventHandlerThread, NULL, hwc_eventHndler_thread, this);
if (ret) {
ALOGE("failed to start vsync thread: %s", strerror(ret));
ret = -ret;
}
hwcDebug = 0;
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);
}
String8 saveString;
saveString.appendFormat("ExynosDevice is initialized");
uint32_t errFileSize = saveErrorLog(saveString);
ALOGI("Initial errlog size: %d bytes\n", errFileSize);
}
ExynosDevice::~ExynosDevice() {
ExynosDisplay *primary_display = getDisplay(HWC_DISPLAY_PRIMARY);
/* TODO kill threads here */
pthread_kill(mEventHandlerThread, SIGTERM);
pthread_join(mEventHandlerThread, NULL);
pthread_kill(mDRThread, SIGTERM);
pthread_join(mDRThread, NULL);
if (mMapper != NULL)
delete mMapper;
if (mAllocator != NULL)
delete mAllocator;
delete primary_display;
}
bool ExynosDevice::isFirstValidate()
{
for (uint32_t i = 0; i < mDisplays.size(); i++) {
if ((mDisplays[i]->mDisplayId != HWC_DISPLAY_VIRTUAL) &&
(mDisplays[i]->mPowerModeState == (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]->mDisplayId != HWC_DISPLAY_VIRTUAL) &&
(mDisplays[i]->mPowerModeState == (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::isDynamicRecompositionThreadAlive()
{
android_atomic_acquire_load(&mDRThreadStatus);
return (mDRThreadStatus > 0);
}
void ExynosDevice::checkDynamicRecompositionThread()
{
// 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;
}
mDRLoopStatus = false;
pthread_join(mDRThread, 0);
}
}
void ExynosDevice::dynamicRecompositionThreadCreate()
{
if (exynosHWCControl.useDynamicRecomp == true) {
/* pthread_create shouldn't have been failed. But, ignore if some error was occurred */
if (pthread_create(&mDRThread, NULL, dynamicRecompositionThreadLoop, this) != 0) {
ALOGE("%s: failed to start hwc_dynamicrecomp_thread thread:", __func__);
mDRLoopStatus = false;
} else {
mDRLoopStatus = true;
}
}
}
void *ExynosDevice::dynamicRecompositionThreadLoop(void *data)
{
ExynosDevice *dev = (ExynosDevice *)data;
ExynosDisplay *display[HWC_NUM_DISPLAY_TYPES];
uint64_t event_cnt[HWC_NUM_DISPLAY_TYPES];
for (uint32_t i = 0; i < HWC_NUM_DISPLAY_TYPES; i++) {
display[i] = (ExynosDisplay *)dev->getDisplay(i);
event_cnt[i] = 0;
}
android_atomic_inc(&(dev->mDRThreadStatus));
while (dev->mDRLoopStatus) {
uint32_t result = 0;
for (uint32_t i = 0; i < HWC_NUM_DISPLAY_TYPES; i++)
event_cnt[i] = display[i]->mUpdateEventCnt;
/*
* If there is no update for more than 100ms, favor the 3D composition mode.
* If all other conditions are met, mode will be switched to 3D composition.
*/
usleep(100000);
for (uint32_t i = 0; i < HWC_NUM_DISPLAY_TYPES; 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);
result = 1;
}
}
}
if (result)
dev->invalidate();
}
android_atomic_dec(&(dev->mDRThreadStatus));
return NULL;
}
/**
* @param display
* @return ExynosDisplay
*/
ExynosDisplay* ExynosDevice::getDisplay(uint32_t display) {
uint32_t physical_display_num = HWC_NUM_DISPLAY_TYPES - 1;
if (mDisplays.isEmpty()) {
goto err;
}
if ((display <= physical_display_num) && (mDisplays[display]->mDisplayId == display)) {
return (ExynosDisplay*)mDisplays[display];
} else {
for (size_t i = (physical_display_num + 1); i < mDisplays.size(); i++) {
if (mDisplays[i]->mDisplayId == display) {
return (ExynosDisplay*)mDisplays[i];
}
}
}
err:
ALOGE("mDisplays.size(%zu), requested display(%d)",
mDisplays.size(), display);
return NULL;
}
/**
* Device Functions for HWC 2.0
*/
int32_t ExynosDevice::createVirtualDisplay(
uint32_t __unused width, uint32_t __unused height, int32_t* /*android_pixel_format_t*/ __unused format, ExynosDisplay* __unused display) {
((ExynosVirtualDisplay*)display)->createVirtualDisplay(width, height, format);
return 0;
}
/**
* @param *display
* @return int32_t
*/
int32_t ExynosDevice::destroyVirtualDisplay(ExynosDisplay* __unused display) {
((ExynosVirtualDisplay *)display)->destroyVirtualDisplay();
return 0;
}
void ExynosDevice::dump(uint32_t *outSize, char *outBuffer) {
/* TODO : Dump here */
if (outSize == NULL) {
ALOGE("%s:: outSize is null", __func__);
return;
}
ExynosDisplay *display = mDisplays[HWC_DISPLAY_PRIMARY];
ExynosDisplay *external_display = mDisplays[HWC_DISPLAY_EXTERNAL];
ExynosDisplay *virtual_display = mDisplays[HWC_DISPLAY_VIRTUAL];
android::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);
display->dump(result);
if (external_display->mPlugState == true) {
external_display->dump(result);
}
if (virtual_display->mPlugState == true) {
virtual_display->dump(result);
}
if (outBuffer == NULL) {
*outSize = (uint32_t)result.length();
} else {
if (*outSize == 0) {
ALOGE("%s:: outSize is 0", __func__);
return;
}
uint32_t copySize = *outSize;
if (*outSize > result.size())
copySize = (uint32_t)result.size();
ALOGI("HWC dump:: resultSize(%zu), outSize(%d), copySize(%d)", result.size(), *outSize, copySize);
strlcpy(outBuffer, result.string(), copySize);
}
return;
}
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_VSYNC)
return HWC2_ERROR_BAD_PARAMETER;
mCallbackInfos[descriptor].callbackData = callbackData;
mCallbackInfos[descriptor].funcPointer = point;
/* Call hotplug callback for primary display*/
if (descriptor == HWC2_CALLBACK_HOTPLUG) {
HWC2_PFN_HOTPLUG callbackFunc =
(HWC2_PFN_HOTPLUG)mCallbackInfos[descriptor].funcPointer;
if (callbackFunc != NULL)
callbackFunc(callbackData, HWC_DISPLAY_PRIMARY, HWC2_CONNECTION_CONNECTED);
}
if (descriptor == HWC2_CALLBACK_VSYNC)
mResourceManager->doPreProcessing();
return HWC2_ERROR_NONE;
}
void ExynosDevice::invalidate()
{
HWC2_PFN_REFRESH callbackFunc =
(HWC2_PFN_REFRESH)mCallbackInfos[HWC2_CALLBACK_REFRESH].funcPointer;
if (callbackFunc != NULL)
callbackFunc(mCallbackInfos[HWC2_CALLBACK_REFRESH].callbackData,
HWC_DISPLAY_PRIMARY);
else
ALOGE("%s:: refresh callback is not registered", __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 display, 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);
invalidate();
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);
invalidate();
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);
invalidate();
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);
invalidate();
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);
invalidate();
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);
invalidate();
break;
// Support DDI scalser {
case HWC_CTL_DDI_RESOLUTION_CHANGE:
ALOGI("%s::HWC_CTL_DDI_RESOLUTION_CHANGE mode=%d", __func__, val);
exynosDisplay = (ExynosDisplay*)getDisplay(display);
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);
invalidate();
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(display);
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);
invalidate();
break;
case HWC_CTL_DYNAMIC_RECOMP:
ALOGI("%s::HWC_CTL_DYNAMIC_RECOMP on/off = %d", __func__, val);
this->setDynamicRecomposition((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(unsigned int on)
{
exynosHWCControl.useDynamicRecomp = on;
}
uint32_t ExynosDevice::checkConnection(uint32_t display)
{
int ret = 0;
ExynosExternalDisplay *external_display = (ExynosExternalDisplay *)mDisplays[HWC_DISPLAY_EXTERNAL];
ExynosVirtualDisplay *virtual_display = NULL;
virtual_display = (ExynosVirtualDisplay *)mDisplays[HWC_DISPLAY_VIRTUAL];
switch(display) {
case HWC_DISPLAY_PRIMARY:
return 1;
case HWC_DISPLAY_EXTERNAL:
if (external_display->mDisplayFd > 0)
return 1;
else
return 0;
case HWC_DISPLAY_VIRTUAL:
if (virtual_display->mPlugState)
return 1;
else
return 0;
default:
return 0;
}
return ret;
}
void ExynosDevice::getCapabilities(uint32_t *outCount, int32_t* outCapabilities)
{
#ifdef HWC_SKIP_VALIDATE
if (outCapabilities == NULL) {
*outCount = 1;
return;
}
outCapabilities[0] = HWC2_CAPABILITY_SKIP_VALIDATE;
#else
*outCount = 0;
#endif
return;
}
void ExynosDevice::getAllocator(GrallocWrapper::Mapper** mapper, GrallocWrapper::Allocator** allocator)
{
if ((mMapper == NULL) && (mAllocator == NULL)) {
ALOGI("%s:: Allocator is created", __func__);
mMapper = new GrallocWrapper::Mapper();
mAllocator = new GrallocWrapper::Allocator(*mMapper);
}
*mapper = mMapper;
*allocator = mAllocator;
}
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) {
/*
* 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) {
HDEBUGLOGD(eDebugSkipValidate, "Display[%d] can't skip validate (%d), renderingState(%d), geometryChanged(0x%" PRIx64 ")",
mDisplays[i]->mType, ret,
mDisplays[i]->mRenderingState, mGeometryChanged);
return false;
} else {
HDEBUGLOGD(eDebugSkipValidate, "Display[%d] can skip validate (%d), renderingState(%d), geometryChanged(0x%" PRIx64 ")",
mDisplays[i]->mType, ret,
mDisplays[i]->mRenderingState, mGeometryChanged);
}
}
}
return true;
}
bool ExynosDevice::validateFences(ExynosDisplay *display) {
if (!validateFencePerFrame(display)) {
String8 errString;
errString.appendFormat("You should doubt fence leak!\n");
ALOGE("%s", errString.string());
saveFenceTrace(display);
return false;
}
if (fenceWarn(display, MAX_FENCE_THRESHOLD)) {
String8 errString;
errString.appendFormat("Fence leak!\n");
printLeakFds(display);
ALOGE("Fence leak! --");
saveFenceTrace(display);
return false;
}
if (exynosHWCControl.doFenceFileDump) {
ALOGE("Fence file dump !");
if (mFenceLogSize != 0)
ALOGE("Fence file not empty!");
saveFenceTrace(display);
exynosHWCControl.doFenceFileDump = false;
}
return true;
}
void ExynosDevice::compareVsyncPeriod() {
ExynosDisplay *primary_display = mDisplays[HWC_DISPLAY_PRIMARY];
ExynosDisplay *external_display = mDisplays[HWC_DISPLAY_EXTERNAL];
mVsyncDisplay = HWC_DISPLAY_PRIMARY;
if (external_display->mPowerModeState == HWC2_POWER_MODE_OFF) {
return;
} else if (primary_display->mPowerModeState == HWC2_POWER_MODE_OFF) {
mVsyncDisplay = HWC_DISPLAY_EXTERNAL;
return;
} else if (((primary_display->mPowerModeState == HWC2_POWER_MODE_DOZE) ||
(primary_display->mPowerModeState == HWC2_POWER_MODE_DOZE_SUSPEND)) &&
(external_display->mVsyncPeriod >= DOZE_VSYNC_PERIOD)) { /*30fps*/
mVsyncDisplay = HWC_DISPLAY_EXTERNAL;
return;
} else if (primary_display->mVsyncPeriod <= external_display->mVsyncPeriod) {
mVsyncDisplay = HWC_DISPLAY_EXTERNAL;
return;
}
return;
}