|
|
/* |
|
|
// Copyright (c) 2014 Intel Corporation |
|
|
// |
|
|
// 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 <HwcTrace.h> |
|
|
#include <IDisplayDevice.h> |
|
|
#include <DisplayQuery.h> |
|
|
#include <BufferManager.h> |
|
|
#include <DisplayPlaneManager.h> |
|
|
#include <Hwcomposer.h> |
|
|
#include <DisplayAnalyzer.h> |
|
|
#include <cutils/properties.h> |
|
|
#include <GraphicBuffer.h> |
|
|
#include <ExternalDevice.h> |
|
|
#include <VirtualDevice.h> |
|
|
|
|
|
namespace android { |
|
|
namespace intel { |
|
|
|
|
|
DisplayAnalyzer::DisplayAnalyzer() |
|
|
: mInitialized(false), |
|
|
mVideoExtModeEnabled(true), |
|
|
mVideoExtModeEligible(false), |
|
|
mVideoExtModeActive(false), |
|
|
mBlankDevice(false), |
|
|
mOverlayAllowed(true), |
|
|
mActiveInputState(true), |
|
|
mIgnoreVideoSkipFlag(false), |
|
|
mProtectedVideoSession(false), |
|
|
mCachedNumDisplays(0), |
|
|
mCachedDisplays(0), |
|
|
mPendingEvents(), |
|
|
mEventMutex(), |
|
|
mEventHandledCondition() |
|
|
{ |
|
|
} |
|
|
|
|
|
DisplayAnalyzer::~DisplayAnalyzer() |
|
|
{ |
|
|
} |
|
|
|
|
|
bool DisplayAnalyzer::initialize() |
|
|
{ |
|
|
// by default video extended mode is enabled |
|
|
char prop[PROPERTY_VALUE_MAX]; |
|
|
if (property_get("hwc.video.extmode.enable", prop, "1") > 0) { |
|
|
mVideoExtModeEnabled = atoi(prop) ? true : false; |
|
|
} |
|
|
mVideoExtModeEligible = false; |
|
|
mVideoExtModeActive = false; |
|
|
mBlankDevice = false; |
|
|
mOverlayAllowed = true; |
|
|
mActiveInputState = true; |
|
|
mIgnoreVideoSkipFlag = false; |
|
|
mProtectedVideoSession = false; |
|
|
mCachedNumDisplays = 0; |
|
|
mCachedDisplays = 0; |
|
|
mPendingEvents.clear(); |
|
|
mVideoStateMap.clear(); |
|
|
mInitialized = true; |
|
|
|
|
|
return true; |
|
|
} |
|
|
|
|
|
void DisplayAnalyzer::deinitialize() |
|
|
{ |
|
|
mPendingEvents.clear(); |
|
|
mVideoStateMap.clear(); |
|
|
mInitialized = false; |
|
|
} |
|
|
|
|
|
void DisplayAnalyzer::analyzeContents( |
|
|
size_t numDisplays, hwc_display_contents_1_t** displays) |
|
|
{ |
|
|
// cache and use them only in this context during analysis |
|
|
mCachedNumDisplays = numDisplays; |
|
|
mCachedDisplays = displays; |
|
|
|
|
|
handlePendingEvents(); |
|
|
|
|
|
if (mVideoExtModeEnabled) { |
|
|
handleVideoExtMode(); |
|
|
} |
|
|
|
|
|
if (mBlankDevice) { |
|
|
// this will make sure device is blanked after geometry changes. |
|
|
// blank event is only processed once |
|
|
blankSecondaryDevice(); |
|
|
} |
|
|
} |
|
|
|
|
|
void DisplayAnalyzer::handleVideoExtMode() |
|
|
{ |
|
|
bool eligible = mVideoExtModeEligible; |
|
|
checkVideoExtMode(); |
|
|
if (eligible == mVideoExtModeEligible) { |
|
|
if (mVideoExtModeActive) { |
|
|
// need to mark all layers |
|
|
setCompositionType(0, HWC_OVERLAY, false); |
|
|
} |
|
|
return; |
|
|
} |
|
|
|
|
|
if (mVideoExtModeEligible) { |
|
|
if (mActiveInputState) { |
|
|
VTRACE("input is active"); |
|
|
} else { |
|
|
enterVideoExtMode(); |
|
|
} |
|
|
} else { |
|
|
exitVideoExtMode(); |
|
|
} |
|
|
} |
|
|
|
|
|
void DisplayAnalyzer::checkVideoExtMode() |
|
|
{ |
|
|
if (mVideoStateMap.size() != 1) { |
|
|
mVideoExtModeEligible = false; |
|
|
return; |
|
|
} |
|
|
|
|
|
Hwcomposer *hwc = &Hwcomposer::getInstance(); |
|
|
|
|
|
ExternalDevice *eDev = static_cast<ExternalDevice *>(hwc->getDisplayDevice(IDisplayDevice::DEVICE_EXTERNAL)); |
|
|
VirtualDevice *vDev = static_cast<VirtualDevice *>(hwc->getDisplayDevice(IDisplayDevice::DEVICE_VIRTUAL)); |
|
|
|
|
|
if ((!eDev || !eDev->isConnected()) && (!vDev || !vDev->isFrameServerActive())) { |
|
|
mVideoExtModeEligible = false; |
|
|
return; |
|
|
} |
|
|
|
|
|
bool geometryChanged = false; |
|
|
int activeDisplays = 0; |
|
|
|
|
|
hwc_display_contents_1_t *content = NULL; |
|
|
for (int i = 0; i < (int)mCachedNumDisplays; i++) { |
|
|
content = mCachedDisplays[i]; |
|
|
if (content == NULL) { |
|
|
continue; |
|
|
} |
|
|
activeDisplays++; |
|
|
if (content->flags & HWC_GEOMETRY_CHANGED) { |
|
|
geometryChanged = true; |
|
|
} |
|
|
} |
|
|
|
|
|
if (activeDisplays <= 1) { |
|
|
mVideoExtModeEligible = false; |
|
|
return; |
|
|
} |
|
|
|
|
|
// video state update event may come later than geometry change event. |
|
|
// in that case, video extended mode is not detected properly. |
|
|
#if 0 |
|
|
if (geometryChanged == false) { |
|
|
// use previous analysis result |
|
|
return; |
|
|
} |
|
|
#endif |
|
|
// reset eligibility of video extended mode |
|
|
mVideoExtModeEligible = false; |
|
|
|
|
|
// check if there is video layer in the primary device |
|
|
content = mCachedDisplays[0]; |
|
|
if (content == NULL) { |
|
|
return; |
|
|
} |
|
|
|
|
|
buffer_handle_t videoHandle = 0; |
|
|
bool videoLayerExist = false; |
|
|
bool videoFullScreenOnPrimary = false; |
|
|
bool isVideoLayerSkipped = false; |
|
|
|
|
|
// exclude the frame buffer target layer |
|
|
for (int j = 0; j < (int)content->numHwLayers - 1; j++) { |
|
|
videoLayerExist = isVideoLayer(content->hwLayers[j]); |
|
|
if (videoLayerExist) { |
|
|
if ((content->hwLayers[j].flags & HWC_SKIP_LAYER)) { |
|
|
isVideoLayerSkipped = true; |
|
|
} |
|
|
videoHandle = content->hwLayers[j].handle; |
|
|
videoFullScreenOnPrimary = isVideoFullScreen(0, content->hwLayers[j]); |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
if (videoLayerExist == false) { |
|
|
// no video layer is found in the primary layer |
|
|
return; |
|
|
} |
|
|
|
|
|
// check whether video layer exists in external device or virtual device |
|
|
// TODO: video may exist in virtual device but no in external device or vice versa |
|
|
// TODO: multiple video layers are not addressed here |
|
|
for (int i = 1; i < (int)mCachedNumDisplays; i++) { |
|
|
content = mCachedDisplays[i]; |
|
|
if (content == NULL) { |
|
|
continue; |
|
|
} |
|
|
|
|
|
// exclude the frame buffer target layer |
|
|
for (int j = 0; j < (int)content->numHwLayers - 1; j++) { |
|
|
if (content->hwLayers[j].handle == videoHandle) { |
|
|
isVideoLayerSkipped |= (content->hwLayers[j].flags & HWC_SKIP_LAYER); |
|
|
VTRACE("video layer exists in device %d", i); |
|
|
if (isVideoLayerSkipped || videoFullScreenOnPrimary){ |
|
|
VTRACE("Video ext mode eligible, %d, %d", |
|
|
isVideoLayerSkipped, videoFullScreenOnPrimary); |
|
|
mVideoExtModeEligible = true; |
|
|
} else { |
|
|
mVideoExtModeEligible = isVideoFullScreen(i, content->hwLayers[j]); |
|
|
} |
|
|
return; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
bool DisplayAnalyzer::isVideoExtModeActive() |
|
|
{ |
|
|
return mVideoExtModeActive; |
|
|
} |
|
|
|
|
|
bool DisplayAnalyzer::isVideoExtModeEnabled() |
|
|
{ |
|
|
#if 1 |
|
|
// enable it for run-time debugging purpose. |
|
|
char prop[PROPERTY_VALUE_MAX]; |
|
|
if (property_get("hwc.video.extmode.enable", prop, "1") > 0) { |
|
|
mVideoExtModeEnabled = atoi(prop) ? true : false; |
|
|
} |
|
|
ITRACE("video extended mode enabled: %d", mVideoExtModeEnabled); |
|
|
#endif |
|
|
|
|
|
return mVideoExtModeEnabled; |
|
|
} |
|
|
|
|
|
bool DisplayAnalyzer::isVideoLayer(hwc_layer_1_t &layer) |
|
|
{ |
|
|
bool ret = false; |
|
|
BufferManager *bm = Hwcomposer::getInstance().getBufferManager(); |
|
|
if (!layer.handle) { |
|
|
return false; |
|
|
} |
|
|
DataBuffer *buffer = bm->lockDataBuffer(layer.handle); |
|
|
if (!buffer) { |
|
|
ETRACE("failed to get buffer"); |
|
|
} else { |
|
|
ret = DisplayQuery::isVideoFormat(buffer->getFormat()); |
|
|
bm->unlockDataBuffer(buffer); |
|
|
} |
|
|
return ret; |
|
|
} |
|
|
|
|
|
bool DisplayAnalyzer::isVideoFullScreen(int device, hwc_layer_1_t &layer) |
|
|
{ |
|
|
IDisplayDevice *displayDevice = Hwcomposer::getInstance().getDisplayDevice(device); |
|
|
if (!displayDevice) { |
|
|
return false; |
|
|
} |
|
|
int width = 0, height = 0; |
|
|
if (!displayDevice->getDisplaySize(&width, &height)) { |
|
|
return false; |
|
|
} |
|
|
|
|
|
VTRACE("video left %d, right %d, top %d, bottom %d, device width %d, height %d", |
|
|
layer.displayFrame.left, layer.displayFrame.right, |
|
|
layer.displayFrame.top, layer.displayFrame.bottom, |
|
|
width, height); |
|
|
|
|
|
// full-screen defintion: |
|
|
// width of target display frame == width of target device, with 1 pixel of tolerance, or |
|
|
// Height of target display frame == height of target device, with 1 pixel of tolerance, or |
|
|
// width * height of display frame > 90% of width * height of display device, or |
|
|
// any of above condition is met on either primary display or secondary display |
|
|
int dstW = layer.displayFrame.right - layer.displayFrame.left; |
|
|
int dstH = layer.displayFrame.bottom - layer.displayFrame.top; |
|
|
|
|
|
if (abs(dstW - width) > 1 && |
|
|
abs(dstH - height) > 1 && |
|
|
dstW * dstH * 10 < width * height * 9) { |
|
|
VTRACE("video is not full-screen"); |
|
|
return false; |
|
|
} |
|
|
return true; |
|
|
} |
|
|
|
|
|
bool DisplayAnalyzer::isOverlayAllowed() |
|
|
{ |
|
|
return mOverlayAllowed; |
|
|
} |
|
|
|
|
|
int DisplayAnalyzer::getVideoInstances() |
|
|
{ |
|
|
return (int)mVideoStateMap.size(); |
|
|
} |
|
|
|
|
|
void DisplayAnalyzer::postHotplugEvent(bool connected) |
|
|
{ |
|
|
if (!connected) { |
|
|
// enable vsync on the primary device immediately |
|
|
Hwcomposer::getInstance().getVsyncManager()->enableDynamicVsync(true); |
|
|
} |
|
|
|
|
|
// handle hotplug event (vsync switch) asynchronously |
|
|
Event e; |
|
|
e.type = HOTPLUG_EVENT; |
|
|
e.bValue = connected; |
|
|
postEvent(e); |
|
|
Hwcomposer::getInstance().invalidate(); |
|
|
} |
|
|
|
|
|
void DisplayAnalyzer::postVideoEvent(int instanceID, int state) |
|
|
{ |
|
|
Event e; |
|
|
e.type = VIDEO_EVENT; |
|
|
e.videoEvent.instanceID = instanceID; |
|
|
e.videoEvent.state = state; |
|
|
postEvent(e); |
|
|
if ((state == VIDEO_PLAYBACK_STARTING) || |
|
|
(state == VIDEO_PLAYBACK_STOPPING && mProtectedVideoSession)) { |
|
|
Hwcomposer::getInstance().invalidate(); |
|
|
mOverlayAllowed = false; |
|
|
hwc_display_contents_1_t *content = NULL; |
|
|
for (int i = 0; i < (int)mCachedNumDisplays; i++) { |
|
|
setCompositionType(i, HWC_FRAMEBUFFER, true); |
|
|
} |
|
|
// wait for up to 100ms until overlay is disabled. |
|
|
int loop = 0; |
|
|
while (loop++ < 6) { |
|
|
if (Hwcomposer::getInstance().getPlaneManager()->isOverlayPlanesDisabled()) |
|
|
break; |
|
|
usleep(16700); |
|
|
} |
|
|
if (loop >= 6) { |
|
|
WTRACE("timeout disabling overlay "); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
void DisplayAnalyzer::postBlankEvent(bool blank) |
|
|
{ |
|
|
Event e; |
|
|
e.type = BLANK_EVENT; |
|
|
e.bValue = blank; |
|
|
postEvent(e); |
|
|
Hwcomposer::getInstance().invalidate(); |
|
|
} |
|
|
|
|
|
void DisplayAnalyzer::postInputEvent(bool active) |
|
|
{ |
|
|
Event e; |
|
|
e.type = INPUT_EVENT; |
|
|
e.bValue = active; |
|
|
postEvent(e); |
|
|
Hwcomposer::getInstance().invalidate(); |
|
|
} |
|
|
|
|
|
void DisplayAnalyzer::postIdleEntryEvent(void) |
|
|
{ |
|
|
Event e; |
|
|
e.type = IDLE_ENTRY_EVENT; |
|
|
e.nValue = 0; |
|
|
postEvent(e); |
|
|
} |
|
|
|
|
|
void DisplayAnalyzer::postEvent(Event& e) |
|
|
{ |
|
|
Mutex::Autolock lock(mEventMutex); |
|
|
mPendingEvents.add(e); |
|
|
} |
|
|
|
|
|
bool DisplayAnalyzer::getEvent(Event& e) |
|
|
{ |
|
|
Mutex::Autolock lock(mEventMutex); |
|
|
if (mPendingEvents.size() == 0) { |
|
|
return false; |
|
|
} |
|
|
e = mPendingEvents[0]; |
|
|
mPendingEvents.removeAt(0); |
|
|
return true; |
|
|
} |
|
|
|
|
|
void DisplayAnalyzer::handlePendingEvents() |
|
|
{ |
|
|
// handle one event per analysis to avoid blocking surface flinger |
|
|
// some event may take lengthy time to process |
|
|
Event e; |
|
|
if (!getEvent(e)) { |
|
|
return; |
|
|
} |
|
|
|
|
|
switch (e.type) { |
|
|
case HOTPLUG_EVENT: |
|
|
handleHotplugEvent(e.bValue); |
|
|
break; |
|
|
case BLANK_EVENT: |
|
|
handleBlankEvent(e.bValue); |
|
|
break; |
|
|
case VIDEO_EVENT: |
|
|
handleVideoEvent(e.videoEvent.instanceID, e.videoEvent.state); |
|
|
break; |
|
|
case TIMING_EVENT: |
|
|
handleTimingEvent(); |
|
|
break; |
|
|
case INPUT_EVENT: |
|
|
handleInputEvent(e.bValue); |
|
|
break; |
|
|
case DPMS_EVENT: |
|
|
handleDpmsEvent(e.nValue); |
|
|
break; |
|
|
case IDLE_ENTRY_EVENT: |
|
|
handleIdleEntryEvent(e.nValue); |
|
|
break; |
|
|
case IDLE_EXIT_EVENT: |
|
|
handleIdleExitEvent(); |
|
|
break; |
|
|
case VIDEO_CHECK_EVENT: |
|
|
handleVideoCheckEvent(); |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
void DisplayAnalyzer::handleHotplugEvent(bool connected) |
|
|
{ |
|
|
Hwcomposer *hwc = &Hwcomposer::getInstance(); |
|
|
if (connected) { |
|
|
if (mVideoStateMap.size() == 1) { |
|
|
// Some video apps wouldn't update video state again when plugin HDMI |
|
|
// and fail to reset refresh rate |
|
|
ExternalDevice *dev = NULL; |
|
|
dev = (ExternalDevice *)hwc->getDisplayDevice(IDisplayDevice::DEVICE_EXTERNAL); |
|
|
if (!dev || !dev->isConnected()) { |
|
|
ITRACE("External device isn't connected"); |
|
|
return; |
|
|
} |
|
|
if (hwc->getMultiDisplayObserver()->isExternalDeviceTimingFixed()) { |
|
|
VTRACE("Timing of external device is fixed."); |
|
|
return; |
|
|
} |
|
|
VideoSourceInfo info; |
|
|
int instanceID = mVideoStateMap.keyAt(0); |
|
|
status_t err = hwc->getMultiDisplayObserver()->getVideoSourceInfo( |
|
|
instanceID, &info); |
|
|
if (err == NO_ERROR) { |
|
|
int hz = dev->getRefreshRate(); |
|
|
if (hz > 0 && info.frameRate > 0 && hz != info.frameRate) { |
|
|
ITRACE("Old Hz %d, new one %d", hz, info.frameRate); |
|
|
dev->setRefreshRate(info.frameRate); |
|
|
} else |
|
|
WTRACE("Old Hz %d is invalid, %d", hz, info.frameRate); |
|
|
} |
|
|
} |
|
|
} else { |
|
|
if (mVideoStateMap.size() == 1) { |
|
|
// Reset input state if HDMI is plug out to |
|
|
// avoid entering extended mode immediately after HDMI is plug in |
|
|
mActiveInputState = true; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
void DisplayAnalyzer::handleBlankEvent(bool blank) |
|
|
{ |
|
|
mBlankDevice = blank; |
|
|
// force geometry changed in the secondary device to reset layer composition type |
|
|
for (int i = 0; i < (int)mCachedNumDisplays; i++) { |
|
|
if (i == IDisplayDevice::DEVICE_PRIMARY) { |
|
|
continue; |
|
|
} |
|
|
if (mCachedDisplays[i]) { |
|
|
mCachedDisplays[i]->flags |= HWC_GEOMETRY_CHANGED; |
|
|
} |
|
|
} |
|
|
blankSecondaryDevice(); |
|
|
} |
|
|
|
|
|
void DisplayAnalyzer::handleTimingEvent() |
|
|
{ |
|
|
// check whether external device is connected, reset refresh rate to match video frame rate |
|
|
// if video is in playing state or reset refresh rate to default preferred one if video is not |
|
|
// at playing state |
|
|
Hwcomposer *hwc = &Hwcomposer::getInstance(); |
|
|
ExternalDevice *dev = NULL; |
|
|
dev = (ExternalDevice *)hwc->getDisplayDevice(IDisplayDevice::DEVICE_EXTERNAL); |
|
|
if (!dev) { |
|
|
return; |
|
|
} |
|
|
|
|
|
if (!dev->isConnected()) { |
|
|
return; |
|
|
} |
|
|
|
|
|
if (hwc->getMultiDisplayObserver()->isExternalDeviceTimingFixed()) { |
|
|
VTRACE("Timing of external device is fixed."); |
|
|
return; |
|
|
} |
|
|
|
|
|
int hz = 0; |
|
|
if (mVideoStateMap.size() == 1) { |
|
|
VideoSourceInfo info; |
|
|
int instanceID = mVideoStateMap.keyAt(0); |
|
|
status_t err = hwc->getMultiDisplayObserver()->getVideoSourceInfo( |
|
|
instanceID, &info); |
|
|
if (err == NO_ERROR) { |
|
|
hz = info.frameRate; |
|
|
} |
|
|
} |
|
|
|
|
|
dev->setRefreshRate(hz); |
|
|
} |
|
|
|
|
|
void DisplayAnalyzer::handleVideoEvent(int instanceID, int state) |
|
|
{ |
|
|
mVideoStateMap.removeItem(instanceID); |
|
|
if (state != VIDEO_PLAYBACK_STOPPED) { |
|
|
mVideoStateMap.add(instanceID, state); |
|
|
} |
|
|
|
|
|
Hwcomposer *hwc = &Hwcomposer::getInstance(); |
|
|
|
|
|
// sanity check |
|
|
if (hwc->getMultiDisplayObserver()->getVideoSessionNumber() != |
|
|
(int)mVideoStateMap.size()) { |
|
|
WTRACE("session number does not match!!"); |
|
|
mVideoStateMap.clear(); |
|
|
if (state != VIDEO_PLAYBACK_STOPPED) { |
|
|
mVideoStateMap.add(instanceID, state); |
|
|
} |
|
|
} |
|
|
|
|
|
// check if composition type needs to be reset |
|
|
bool reset = false; |
|
|
if ((state == VIDEO_PLAYBACK_STARTING) || |
|
|
(state == VIDEO_PLAYBACK_STOPPING && mProtectedVideoSession)) { |
|
|
// if video is in starting or stopping stage, overlay use is temporarily not allowed to |
|
|
// avoid scrambed RGB overlay if video is protected. |
|
|
mOverlayAllowed = false; |
|
|
reset = true; |
|
|
} else { |
|
|
reset = !mOverlayAllowed; |
|
|
mOverlayAllowed = true; |
|
|
} |
|
|
|
|
|
if (reset) { |
|
|
hwc_display_contents_1_t *content = NULL; |
|
|
for (int i = 0; i < (int)mCachedNumDisplays; i++) { |
|
|
setCompositionType(i, HWC_FRAMEBUFFER, true); |
|
|
} |
|
|
} |
|
|
|
|
|
if (mVideoStateMap.size() == 0) { |
|
|
// reset active input state after video playback stops. |
|
|
// MDS should update input state in 5 seconds after video playback starts |
|
|
mActiveInputState = true; |
|
|
} |
|
|
|
|
|
mProtectedVideoSession = false; |
|
|
if (state == VIDEO_PLAYBACK_STARTED) { |
|
|
VideoSourceInfo info; |
|
|
hwc->getMultiDisplayObserver()->getVideoSourceInfo( |
|
|
getFirstVideoInstanceSessionID(), &info); |
|
|
mProtectedVideoSession = info.isProtected; |
|
|
} |
|
|
// Setting timing immediately, |
|
|
// Don't posthone to next circle |
|
|
handleTimingEvent(); |
|
|
|
|
|
handleVideoCheckEvent(); |
|
|
} |
|
|
|
|
|
void DisplayAnalyzer::blankSecondaryDevice() |
|
|
{ |
|
|
hwc_display_contents_1_t *content = NULL; |
|
|
hwc_layer_1 *layer = NULL; |
|
|
for (int i = 0; i < (int)mCachedNumDisplays; i++) { |
|
|
if (i == IDisplayDevice::DEVICE_PRIMARY) { |
|
|
continue; |
|
|
} |
|
|
content = mCachedDisplays[i]; |
|
|
if (content == NULL) { |
|
|
continue; |
|
|
} |
|
|
|
|
|
for (int j = 0; j < (int)content->numHwLayers - 1; j++) { |
|
|
layer = &content->hwLayers[j]; |
|
|
if (!layer) { |
|
|
continue; |
|
|
} |
|
|
if (mBlankDevice) { |
|
|
layer->hints |= HWC_HINT_CLEAR_FB; |
|
|
layer->flags &= ~HWC_SKIP_LAYER; |
|
|
layer->compositionType = HWC_OVERLAY; |
|
|
} else { |
|
|
layer->hints &= ~HWC_HINT_CLEAR_FB; |
|
|
layer->compositionType = HWC_FRAMEBUFFER; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
void DisplayAnalyzer::handleInputEvent(bool active) |
|
|
{ |
|
|
if (active == mActiveInputState) { |
|
|
WTRACE("same input state: %d", active); |
|
|
} |
|
|
mActiveInputState = active; |
|
|
if (!mVideoExtModeEligible) { |
|
|
ITRACE("not eligible for video extended mode"); |
|
|
return; |
|
|
} |
|
|
|
|
|
if (active) { |
|
|
exitVideoExtMode(); |
|
|
} else { |
|
|
enterVideoExtMode(); |
|
|
} |
|
|
} |
|
|
|
|
|
void DisplayAnalyzer::handleDpmsEvent(int delayCount) |
|
|
{ |
|
|
if (mActiveInputState || !mVideoExtModeEligible) { |
|
|
ITRACE("aborting display power off in video extended mode"); |
|
|
return; |
|
|
} |
|
|
|
|
|
if (delayCount < DELAY_BEFORE_DPMS_OFF) { |
|
|
Event e; |
|
|
e.type = DPMS_EVENT; |
|
|
e.nValue = delayCount + 1; |
|
|
postEvent(e); |
|
|
Hwcomposer::getInstance().invalidate(); |
|
|
return; |
|
|
} |
|
|
|
|
|
if (Hwcomposer::getInstance().getVsyncManager()->getVsyncSource() == |
|
|
IDisplayDevice::DEVICE_PRIMARY) { |
|
|
Hwcomposer::getInstance().getDrm()->setDpmsMode( |
|
|
IDisplayDevice::DEVICE_PRIMARY, |
|
|
IDisplayDevice::DEVICE_DISPLAY_STANDBY); |
|
|
ETRACE("primary display is source of vsync, we only dim backlight"); |
|
|
return; |
|
|
} |
|
|
|
|
|
// panel can't be powered off as touch panel shares the power supply with LCD. |
|
|
DTRACE("primary display coupled with touch on Saltbay, only dim backlight"); |
|
|
Hwcomposer::getInstance().getDrm()->setDpmsMode( |
|
|
IDisplayDevice::DEVICE_PRIMARY, |
|
|
IDisplayDevice::DEVICE_DISPLAY_STANDBY); |
|
|
//IDisplayDevice::DEVICE_DISPLAY_OFF); |
|
|
return; |
|
|
} |
|
|
|
|
|
|
|
|
void DisplayAnalyzer::handleIdleEntryEvent(int count) |
|
|
{ |
|
|
DTRACE("handling idle entry event, count %d", count); |
|
|
if (hasProtectedLayer()) { |
|
|
ITRACE("Ignoring idle entry as protected layer exists."); |
|
|
setCompositionType(0, HWC_FRAMEBUFFER, true); |
|
|
return; |
|
|
} |
|
|
|
|
|
// stop idle entry if external device is connected |
|
|
if (mCachedDisplays && mCachedDisplays[IDisplayDevice::DEVICE_EXTERNAL]) { |
|
|
ITRACE("Ignoring idle entry as external device is connected."); |
|
|
setCompositionType(0, HWC_FRAMEBUFFER, true); |
|
|
return; |
|
|
} |
|
|
|
|
|
// stop idle entry if video playback is active |
|
|
// TODO: remove this check for Annidale |
|
|
if (mVideoStateMap.size() > 0) { |
|
|
ITRACE("Ignoring idle entry as video session is active."); |
|
|
setCompositionType(0, HWC_FRAMEBUFFER, true); |
|
|
return; |
|
|
} |
|
|
|
|
|
setCompositionType(0, HWC_FORCE_FRAMEBUFFER, true); |
|
|
|
|
|
// next prepare/set will exit idle state. |
|
|
Event e; |
|
|
e.type = IDLE_EXIT_EVENT; |
|
|
postEvent(e); |
|
|
} |
|
|
|
|
|
void DisplayAnalyzer::handleIdleExitEvent() |
|
|
{ |
|
|
DTRACE("handling idle exit event"); |
|
|
|
|
|
setCompositionType(0, HWC_FRAMEBUFFER, true); |
|
|
} |
|
|
|
|
|
void DisplayAnalyzer::handleVideoCheckEvent() |
|
|
{ |
|
|
// check if the first seen video layer on secondary device (HDMI/WFD) is marked as skipped |
|
|
// it is assumed video is always skipped if the first seen video layer is skipped |
|
|
// this is to workaround secure video layer transmitted over non secure output |
|
|
// and HWC_SKIP_LAYER set during rotation animation. |
|
|
mIgnoreVideoSkipFlag = false; |
|
|
|
|
|
if (mVideoStateMap.size() != 1 || |
|
|
mCachedNumDisplays <= 1) { |
|
|
return; |
|
|
} |
|
|
|
|
|
intptr_t videoHandles[mCachedNumDisplays]; |
|
|
for (int i = 0; i < (int)mCachedNumDisplays; i++) { |
|
|
videoHandles[i] = 0; |
|
|
hwc_display_contents_1_t *content = mCachedDisplays[i]; |
|
|
if (content == NULL) { |
|
|
continue; |
|
|
} |
|
|
for (int j = 0; j < (int)content->numHwLayers - 1; j++) { |
|
|
if (isVideoLayer(content->hwLayers[j])) { |
|
|
videoHandles[i] = (intptr_t)content->hwLayers[j].handle; |
|
|
if (i > 0) { |
|
|
mIgnoreVideoSkipFlag = !(content->hwLayers[j].flags & HWC_SKIP_LAYER); |
|
|
ITRACE("Ignoring video HWC_SKIP_LAYER: %d on output %d", mIgnoreVideoSkipFlag, i); |
|
|
return; |
|
|
} |
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
if (videoHandles[0]) { |
|
|
WTRACE("Video is on the primary panel only"); |
|
|
return; |
|
|
} |
|
|
|
|
|
// video state map indicates video session is active and there is secondary |
|
|
// display, need to continue checking as video is not found in the buffers yet |
|
|
Event e; |
|
|
e.type = VIDEO_CHECK_EVENT; |
|
|
postEvent(e); |
|
|
} |
|
|
|
|
|
void DisplayAnalyzer::enterVideoExtMode() |
|
|
{ |
|
|
if (mVideoExtModeActive) { |
|
|
WTRACE("already in video extended mode."); |
|
|
return; |
|
|
} |
|
|
|
|
|
ITRACE("entering video extended mode..."); |
|
|
mVideoExtModeActive = true; |
|
|
Hwcomposer::getInstance().getVsyncManager()->resetVsyncSource(); |
|
|
|
|
|
setCompositionType(0, HWC_OVERLAY, true); |
|
|
|
|
|
// Do not power off primary display immediately as flip is asynchronous |
|
|
Event e; |
|
|
e.type = DPMS_EVENT; |
|
|
e.nValue = 0; |
|
|
postEvent(e); |
|
|
Hwcomposer::getInstance().invalidate(); |
|
|
} |
|
|
|
|
|
void DisplayAnalyzer::exitVideoExtMode() |
|
|
{ |
|
|
if (!mVideoExtModeActive) { |
|
|
WTRACE("Not in video extended mode"); |
|
|
return; |
|
|
} |
|
|
|
|
|
ITRACE("exiting video extended mode..."); |
|
|
|
|
|
mVideoExtModeActive = false; |
|
|
|
|
|
Hwcomposer::getInstance().getDrm()->setDpmsMode( |
|
|
IDisplayDevice::DEVICE_PRIMARY, |
|
|
IDisplayDevice::DEVICE_DISPLAY_ON); |
|
|
|
|
|
Hwcomposer::getInstance().getVsyncManager()->resetVsyncSource(); |
|
|
|
|
|
setCompositionType(0, HWC_FRAMEBUFFER, true); |
|
|
} |
|
|
|
|
|
bool DisplayAnalyzer::isPresentationLayer(hwc_layer_1_t &layer) |
|
|
{ |
|
|
if (layer.handle == NULL) { |
|
|
return false; |
|
|
} |
|
|
if (mCachedDisplays == NULL) { |
|
|
return false; |
|
|
} |
|
|
// check if the given layer exists in the primary device |
|
|
hwc_display_contents_1_t *content = mCachedDisplays[0]; |
|
|
if (content == NULL) { |
|
|
return false; |
|
|
} |
|
|
for (size_t i = 0; i < content->numHwLayers - 1; i++) { |
|
|
if (content->hwLayers[i].handle == layer.handle) { |
|
|
VTRACE("Layer exists for Primary device"); |
|
|
return false; |
|
|
} |
|
|
} |
|
|
return true; |
|
|
} |
|
|
|
|
|
bool DisplayAnalyzer::hasProtectedLayer() |
|
|
{ |
|
|
DataBuffer * buffer = NULL; |
|
|
hwc_display_contents_1_t *content = NULL; |
|
|
BufferManager *bm = Hwcomposer::getInstance().getBufferManager(); |
|
|
|
|
|
if (bm == NULL){ |
|
|
return false; |
|
|
} |
|
|
|
|
|
if (mCachedDisplays == NULL) { |
|
|
return false; |
|
|
} |
|
|
// check if the given layer exists in the primary device |
|
|
for (int index = 0; index < (int)mCachedNumDisplays; index++) { |
|
|
content = mCachedDisplays[index]; |
|
|
if (content == NULL) { |
|
|
continue; |
|
|
} |
|
|
|
|
|
for (size_t i = 0; i < content->numHwLayers - 1; i++) { |
|
|
if (isProtectedLayer(content->hwLayers[i])) |
|
|
return true; |
|
|
} |
|
|
} |
|
|
|
|
|
return false; |
|
|
} |
|
|
|
|
|
bool DisplayAnalyzer::isProtectedLayer(hwc_layer_1_t &layer) |
|
|
{ |
|
|
if (!layer.handle) { |
|
|
return false; |
|
|
} |
|
|
bool ret = false; |
|
|
BufferManager *bm = Hwcomposer::getInstance().getBufferManager(); |
|
|
DataBuffer *buffer = bm->lockDataBuffer(layer.handle); |
|
|
if (!buffer) { |
|
|
ETRACE("failed to get buffer"); |
|
|
} else { |
|
|
ret = GraphicBuffer::isProtectedBuffer((GraphicBuffer*)buffer); |
|
|
bm->unlockDataBuffer(buffer); |
|
|
} |
|
|
return ret; |
|
|
} |
|
|
|
|
|
bool DisplayAnalyzer::ignoreVideoSkipFlag() |
|
|
{ |
|
|
return mIgnoreVideoSkipFlag; |
|
|
} |
|
|
|
|
|
void DisplayAnalyzer::setCompositionType(hwc_display_contents_1_t *display, int type) |
|
|
{ |
|
|
for (size_t i = 0; i < display->numHwLayers - 1; i++) { |
|
|
hwc_layer_1_t *layer = &display->hwLayers[i]; |
|
|
if (layer) layer->compositionType = type; |
|
|
} |
|
|
} |
|
|
|
|
|
void DisplayAnalyzer::setCompositionType(int device, int type, bool reset) |
|
|
{ |
|
|
hwc_display_contents_1_t *content = mCachedDisplays[device]; |
|
|
if (content == NULL) { |
|
|
ETRACE("Invalid device %d", device); |
|
|
return; |
|
|
} |
|
|
|
|
|
// don't need to set geometry changed if layers are just needed to be marked |
|
|
if (reset) { |
|
|
content->flags |= HWC_GEOMETRY_CHANGED; |
|
|
} |
|
|
|
|
|
setCompositionType(content, type); |
|
|
} |
|
|
|
|
|
int DisplayAnalyzer::getFirstVideoInstanceSessionID() { |
|
|
if (mVideoStateMap.size() >= 1) { |
|
|
return mVideoStateMap.keyAt(0); |
|
|
} |
|
|
return -1; |
|
|
} |
|
|
|
|
|
} // namespace intel |
|
|
} // namespace android |
|
|
|
|
|
|