display: External display refactor
- cleans up external library
- add separate library for virtual display
- process virtual updates in its separate path
in hwc.
- Acquire blank mutex lock for one complete drawing
cycle
Change-Id: Ib984c578464a131ecdb27ee48960f58d68b7a5a7
diff --git a/libhwcomposer/Android.mk b/libhwcomposer/Android.mk
index 87a7815..d47e4b0 100644
--- a/libhwcomposer/Android.mk
+++ b/libhwcomposer/Android.mk
@@ -11,7 +11,7 @@
LOCAL_SHARED_LIBRARIES := $(common_libs) libEGL liboverlay \
libexternal libqdutils libhardware_legacy \
libdl libmemalloc libqservice libsync \
- libbinder libmedia libskia
+ libbinder libmedia libskia libvirtual
LOCAL_CFLAGS := $(common_flags) -DLOG_TAG=\"qdhwcomposer\"
LOCAL_ADDITIONAL_DEPENDENCIES := $(common_deps)
LOCAL_SRC_FILES := hwc.cpp \
diff --git a/libhwcomposer/hwc.cpp b/libhwcomposer/hwc.cpp
index 09fef80..558cc29 100644
--- a/libhwcomposer/hwc.cpp
+++ b/libhwcomposer/hwc.cpp
@@ -67,6 +67,17 @@
}
};
+/* In case of proprietary WFD session, we are fooling SF by piggybacking on
+ * HDMI display ID for virtual. This helper is needed to differentiate their
+ * paths in HAL.
+ * TODO: Not needed once we have WFD client working on top of Google API's */
+
+static int getHWCDpy(hwc_context_t *ctx, int dpy) {
+ if(dpy == HWC_DISPLAY_EXTERNAL && ctx->mVirtualonExtActive)
+ return HWC_DISPLAY_VIRTUAL;
+ return dpy;
+}
+
/*
* Save callback functions registered to HWC
*/
@@ -125,9 +136,10 @@
struct mdp_display_commit commit_info;
memset(&commit_info, 0, sizeof(struct mdp_display_commit));
commit_info.flags = MDP_DISPLAY_COMMIT_OVERLAY;
- if(ioctl(ctx->dpyAttr[dpy].fd, MSMFB_DISPLAY_COMMIT, &commit_info) == -1) {
- ALOGE("%s: MSMFB_DISPLAY_COMMIT for dpy %d failed", __FUNCTION__,dpy);
- return -errno;
+ if(ioctl(ctx->dpyAttr[dpy].fd, MSMFB_DISPLAY_COMMIT, &commit_info) < 0){
+ ALOGE("%s: MSMFB_DISPLAY_COMMIT for dpy %d failed", __FUNCTION__,
+ dpy);
+ return -errno;
}
return 0;
}
@@ -157,9 +169,9 @@
}
static int hwc_prepare_external(hwc_composer_device_1 *dev,
- hwc_display_contents_1_t *list, int dpy) {
-
+ hwc_display_contents_1_t *list) {
hwc_context_t* ctx = (hwc_context_t*)(dev);
+ const int dpy = HWC_DISPLAY_EXTERNAL;
if (LIKELY(list && list->numHwLayers > 1) &&
ctx->dpyAttr[dpy].isActive &&
@@ -169,19 +181,13 @@
hwc_layer_1_t *fbLayer = &list->hwLayers[last];
if(!ctx->dpyAttr[dpy].isPause) {
if(fbLayer->handle) {
- ctx->mExtDispConfiguring = false;
+ ctx->dpyAttr[dpy].isConfiguring = false;
setListStats(ctx, list, dpy);
if(ctx->mMDPComp[dpy]->prepare(ctx, list) < 0) {
const int fbZ = 0;
ctx->mFBUpdate[dpy]->prepare(ctx, list, fbZ);
}
- /* Temporarily commenting out C2D until we support partial
- copybit composition for mixed mode MDP
-
- if((fbZOrder >= 0) && ctx->mCopyBit[dpy])
- ctx->mCopyBit[dpy]->prepare(ctx, list, dpy);
- */
if(ctx->listStats[dpy].isDisplayAnimating) {
// Mark all app layers as HWC_OVERLAY for external during
// animation, so that SF doesnt draw it on FB
@@ -202,12 +208,54 @@
return 0;
}
+static int hwc_prepare_virtual(hwc_composer_device_1 *dev,
+ hwc_display_contents_1_t *list) {
+
+ hwc_context_t* ctx = (hwc_context_t*)(dev);
+ const int dpy = HWC_DISPLAY_VIRTUAL;
+
+ if (LIKELY(list && list->numHwLayers > 1) &&
+ ctx->dpyAttr[dpy].isActive &&
+ ctx->dpyAttr[dpy].connected) {
+ reset_layer_prop(ctx, dpy, list->numHwLayers - 1);
+ uint32_t last = list->numHwLayers - 1;
+ hwc_layer_1_t *fbLayer = &list->hwLayers[last];
+ if(!ctx->dpyAttr[dpy].isPause) {
+ if(fbLayer->handle) {
+ ctx->dpyAttr[dpy].isConfiguring = false;
+ setListStats(ctx, list, dpy);
+ if(ctx->mMDPComp[dpy]->prepare(ctx, list) < 0) {
+ const int fbZ = 0;
+ ctx->mFBUpdate[dpy]->prepare(ctx, list, fbZ);
+ }
+
+ if(ctx->listStats[dpy].isDisplayAnimating) {
+ // Mark all app layers as HWC_OVERLAY for virtual during
+ // animation, so that SF doesnt draw it on FB
+ for(int i = 0 ;i < ctx->listStats[dpy].numAppLayers; i++) {
+ hwc_layer_1_t *layer = &list->hwLayers[i];
+ layer->compositionType = HWC_OVERLAY;
+ }
+ }
+ }
+ } else {
+ // Virtual Display is in Pause state.
+ // ToDo:
+ // Mark all application layers as OVERLAY so that
+ // GPU will not compose. This is done for power
+ // optimization
+ }
+ }
+ return 0;
+}
+
+
static int hwc_prepare(hwc_composer_device_1 *dev, size_t numDisplays,
hwc_display_contents_1_t** displays)
{
int ret = 0;
hwc_context_t* ctx = (hwc_context_t*)(dev);
- Locker::Autolock _bl(ctx->mBlankLock);
+ ctx->mBlankLock.lock();
//Will be unlocked at the end of set
ctx->mExtLock.lock();
reset(ctx, numDisplays, displays);
@@ -220,13 +268,16 @@
for (int32_t i = numDisplays; i >= 0; i--) {
hwc_display_contents_1_t *list = displays[i];
- switch(i) {
+ int dpy = getHWCDpy(ctx, i);
+ switch(dpy) {
case HWC_DISPLAY_PRIMARY:
ret = hwc_prepare_primary(dev, list);
break;
case HWC_DISPLAY_EXTERNAL:
+ ret = hwc_prepare_external(dev, list);
+ break;
case HWC_DISPLAY_VIRTUAL:
- ret = hwc_prepare_external(dev, list, i);
+ ret = hwc_prepare_virtual(dev, list);
break;
default:
ret = -EINVAL;
@@ -278,7 +329,7 @@
hwc_context_t* ctx = (hwc_context_t*)(dev);
Locker::Autolock _l(ctx->mBlankLock);
- int ret = 0;
+ int ret = 0, value = 0;
ALOGD_IF(BLANK_DEBUG, "%s: %s display: %d", __FUNCTION__,
blank==1 ? "Blanking":"Unblanking", dpy);
if(blank) {
@@ -291,57 +342,44 @@
overlay::Writeback::clear();
}
switch(dpy) {
- case HWC_DISPLAY_PRIMARY:
- if(blank) {
- ret = ioctl(ctx->dpyAttr[dpy].fd, FBIOBLANK,FB_BLANK_POWERDOWN);
+ case HWC_DISPLAY_PRIMARY:
+ value = blank ? FB_BLANK_POWERDOWN : FB_BLANK_UNBLANK;
+ if(ioctl(ctx->dpyAttr[dpy].fd, FBIOBLANK, value) < 0 ) {
+ ALOGE("%s: Failed to handle blank event(%d) for Primary!!",
+ __FUNCTION__, blank );
+ return -1;
+ }
- if(ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].connected == true) {
- // Surfaceflinger does not send Blank/unblank event to hwc
- // for virtual display, handle it explicitly when blank for
- // primary is invoked, so that any pipes unset get committed
- if (display_commit(ctx, HWC_DISPLAY_VIRTUAL) < 0) {
- ret = -1;
- ALOGE("%s:post failed for virtual display !!",
- __FUNCTION__);
- } else {
- ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].isActive = !blank;
- }
- }
- } else {
- ret = ioctl(ctx->dpyAttr[dpy].fd, FBIOBLANK, FB_BLANK_UNBLANK);
- if(ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].connected == true) {
- ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].isActive = !blank;
- }
- }
- break;
- case HWC_DISPLAY_EXTERNAL:
- if(blank) {
- // call external framebuffer commit on blank,
- // so that any pipe unsets gets committed
- if (display_commit(ctx, dpy) < 0) {
- ret = -1;
- ALOGE("%s:post failed for external display !! ",
- __FUNCTION__);
- }
- } else {
- }
- break;
- default:
- return -EINVAL;
+ if(!blank) {
+ // Enable HPD here, as during bootup unblank is called
+ // when SF is completely initialized
+ ctx->mExtDisplay->setHPD(1);
+ }
+
+ /* Since SF is not aware of VIRTUAL DISPLAY being handle by HWC,
+ * it wont send blank / unblank events for it. We piggyback on
+ * PRIMARY DISPLAY events to release mdp pips and
+ * activate/deactive VIRTUAL DISPLAY */
+
+ if(ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].connected) {
+ if(blank)
+ display_commit(ctx, HWC_DISPLAY_VIRTUAL);
+ ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].isActive = !blank;
+ }
+ break;
+ case HWC_DISPLAY_EXTERNAL:
+ if(blank) {
+ display_commit(ctx, HWC_DISPLAY_EXTERNAL);
+ }
+ break;
+ default:
+ return -EINVAL;
}
- // Enable HPD here, as during bootup unblank is called
- // when SF is completely initialized
- ctx->mExtDisplay->setHPD(1);
- if(ret == 0){
- ctx->dpyAttr[dpy].isActive = !blank;
- } else {
- ALOGE("%s: Failed in %s display: %d error:%s", __FUNCTION__,
- blank==1 ? "blanking":"unblanking", dpy, strerror(errno));
- return ret;
- }
+
+ ctx->dpyAttr[dpy].isActive = !blank;
ALOGD_IF(BLANK_DEBUG, "%s: Done %s display: %d", __FUNCTION__,
- blank==1 ? "blanking":"unblanking", dpy);
+ blank ? "blanking":"unblanking", dpy);
return 0;
}
@@ -417,11 +455,14 @@
}
static int hwc_set_external(hwc_context_t *ctx,
- hwc_display_contents_1_t* list, int dpy)
+ hwc_display_contents_1_t* list)
{
ATRACE_CALL();
int ret = 0;
+ const int dpy = HWC_DISPLAY_EXTERNAL;
+
+
if (LIKELY(list) && ctx->dpyAttr[dpy].isActive &&
ctx->dpyAttr[dpy].connected) {
@@ -474,26 +515,92 @@
return ret;
}
+static int hwc_set_virtual(hwc_context_t *ctx,
+ hwc_display_contents_1_t* list)
+{
+ ATRACE_CALL();
+ int ret = 0;
+ const int dpy = HWC_DISPLAY_VIRTUAL;
+
+ if (LIKELY(list) && ctx->dpyAttr[dpy].isActive &&
+ ctx->dpyAttr[dpy].connected) {
+ if(!ctx->dpyAttr[dpy].isPause) {
+ uint32_t last = list->numHwLayers - 1;
+ hwc_layer_1_t *fbLayer = &list->hwLayers[last];
+ int fd = -1; //FenceFD from the Copybit(valid in async mode)
+ bool copybitDone = false;
+ if(ctx->mCopyBit[dpy])
+ copybitDone = ctx->mCopyBit[dpy]->draw(ctx, list, dpy, &fd);
+
+ if(list->numHwLayers > 1)
+ hwc_sync(ctx, list, dpy, fd);
+
+ // Dump the layers for virtual
+ if(ctx->mHwcDebug[dpy])
+ ctx->mHwcDebug[dpy]->dumpLayers(list);
+
+ if (!ctx->mMDPComp[dpy]->draw(ctx, list)) {
+ ALOGE("%s: MDPComp draw failed", __FUNCTION__);
+ ret = -1;
+ }
+
+ int extOnlyLayerIndex =
+ ctx->listStats[dpy].extOnlyLayerIndex;
+
+ private_handle_t *hnd = (private_handle_t *)fbLayer->handle;
+ if(extOnlyLayerIndex!= -1) {
+ hwc_layer_1_t *extLayer = &list->hwLayers[extOnlyLayerIndex];
+ hnd = (private_handle_t *)extLayer->handle;
+ } else if(copybitDone) {
+ hnd = ctx->mCopyBit[dpy]->getCurrentRenderBuffer();
+ }
+
+ if(hnd && !isYuvBuffer(hnd)) {
+ if (!ctx->mFBUpdate[dpy]->draw(ctx, hnd)) {
+ ALOGE("%s: FBUpdate::draw fail!", __FUNCTION__);
+ ret = -1;
+ }
+ }
+ }
+
+ if (display_commit(ctx, dpy) < 0) {
+ ALOGE("%s: display commit fail!", __FUNCTION__);
+ ret = -1;
+ }
+ }
+
+ closeAcquireFds(list);
+
+ if (list && !ctx->mVirtualonExtActive) {
+ // SF assumes HWC waits for the acquire fence and returns a new fence
+ // that signals when we're done. Since we don't wait, and also don't
+ // touch the buffer, we can just handle the acquire fence back to SF
+ // as the retire fence.
+ list->retireFenceFd = list->outbufAcquireFenceFd;
+ }
+
+ return ret;
+}
+
+
static int hwc_set(hwc_composer_device_1 *dev,
size_t numDisplays,
hwc_display_contents_1_t** displays)
{
int ret = 0;
hwc_context_t* ctx = (hwc_context_t*)(dev);
- Locker::Autolock _bl(ctx->mBlankLock);
for (uint32_t i = 0; i <= numDisplays; i++) {
hwc_display_contents_1_t* list = displays[i];
- switch(i) {
+ int dpy = getHWCDpy(ctx, i);
+ switch(dpy) {
case HWC_DISPLAY_PRIMARY:
ret = hwc_set_primary(ctx, list);
break;
case HWC_DISPLAY_EXTERNAL:
+ ret = hwc_set_external(ctx, list);
+ break;
case HWC_DISPLAY_VIRTUAL:
- /* ToDo: We are using hwc_set_external path for both External and
- Virtual displays on HWC1.1. Eventually, we will have
- separate functions when we move to HWC1.2
- */
- ret = hwc_set_external(ctx, list, i);
+ ret = hwc_set_virtual(ctx, list);
break;
default:
ret = -EINVAL;
@@ -506,6 +613,7 @@
ctx->mVideoTransFlag = false;
//Was locked at the beginning of prepare
ctx->mExtLock.unlock();
+ ctx->mBlankLock.unlock();
return ret;
}
@@ -513,6 +621,7 @@
uint32_t* configs, size_t* numConfigs) {
int ret = 0;
hwc_context_t* ctx = (hwc_context_t*)(dev);
+ disp = getHWCDpy(ctx, disp);
//in 1.1 there is no way to choose a config, report as config id # 0
//This config is passed to getDisplayAttributes. Ignore for now.
switch(disp) {
@@ -542,8 +651,9 @@
uint32_t config, const uint32_t* attributes, int32_t* values) {
hwc_context_t* ctx = (hwc_context_t*)(dev);
+ disp = getHWCDpy(ctx, disp);
//If hotpluggable displays(i.e, HDMI, WFD) are inactive return error
- if( (disp >= HWC_DISPLAY_EXTERNAL) && !ctx->dpyAttr[disp].connected) {
+ if( (disp != HWC_DISPLAY_PRIMARY) && !ctx->dpyAttr[disp].connected) {
return -1;
}
diff --git a/libhwcomposer/hwc_ad.cpp b/libhwcomposer/hwc_ad.cpp
index ef5980c..9ac8958 100644
--- a/libhwcomposer/hwc_ad.cpp
+++ b/libhwcomposer/hwc_ad.cpp
@@ -143,7 +143,7 @@
const hwc_display_contents_1_t* list) {
mDoable = false;
if(mFeatureEnabled &&
- !ctx->mExtDisplay->isExternalConnected() &&
+ !ctx->mExtDisplay->isConnected() &&
ctx->listStats[HWC_DISPLAY_PRIMARY].yuvCount == 1) {
int nYuvIndex = ctx->listStats[HWC_DISPLAY_PRIMARY].yuvIndices[0];
const hwc_layer_1_t* layer = &list->hwLayers[nYuvIndex];
diff --git a/libhwcomposer/hwc_mdpcomp.cpp b/libhwcomposer/hwc_mdpcomp.cpp
index 31b9645..5c0ad4d 100644
--- a/libhwcomposer/hwc_mdpcomp.cpp
+++ b/libhwcomposer/hwc_mdpcomp.cpp
@@ -20,6 +20,7 @@
#include "hwc_mdpcomp.h"
#include <sys/ioctl.h>
#include "external.h"
+#include "virtual.h"
#include "qdMetaData.h"
#include "mdp_version.h"
#include "hwc_fbupdate.h"
@@ -56,7 +57,8 @@
{
Locker::Autolock _l(mMdpCompLock);
dumpsys_log(buf,"HWC Map for Dpy: %s \n",
- mDpy ? "\"EXTERNAL\"" : "\"PRIMARY\"");
+ (mDpy == 0) ? "\"PRIMARY\"" :
+ (mDpy == 1) ? "\"EXTERNAL\"" : "\"VIRTUAL\"");
dumpsys_log(buf,"PREV_FRAME: layerCount:%2d mdpCount:%2d \
cacheCount:%2d \n", mCachedFrame.layerCount,
mCachedFrame.mdpCount, mCachedFrame.cacheCount);
@@ -378,13 +380,13 @@
ALOGD_IF(isDebug(),"%s: MDP Comp. not enabled.", __FUNCTION__);
ret = false;
} else if(qdutils::MDPVersion::getInstance().is8x26() &&
- ctx->mVideoTransFlag &&
- ctx->mExtDisplay->isExternalConnected()) {
+ ctx->mVideoTransFlag && ctx->mVirtualDisplay->isConnected()) {
//1 Padding round to shift pipes across mixers
ALOGD_IF(isDebug(),"%s: MDP Comp. video transition padding round",
__FUNCTION__);
ret = false;
- } else if(ctx->mExtDispConfiguring) {
+ } else if(ctx->dpyAttr[HWC_DISPLAY_EXTERNAL].isConfiguring ||
+ ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].isConfiguring) {
ALOGD_IF( isDebug(),"%s: External Display connection is pending",
__FUNCTION__);
ret = false;
diff --git a/libhwcomposer/hwc_uevents.cpp b/libhwcomposer/hwc_uevents.cpp
index c658224..1d127f0 100644
--- a/libhwcomposer/hwc_uevents.cpp
+++ b/libhwcomposer/hwc_uevents.cpp
@@ -31,6 +31,7 @@
#include "hwc_copybit.h"
#include "comptype.h"
#include "external.h"
+#include "virtual.h"
#include "mdp_version.h"
namespace qhwc {
@@ -45,22 +46,20 @@
EXTERNAL_RESUME
};
-static bool isHDMI(const char* str)
-{
- if(strcasestr("change@/devices/virtual/switch/hdmi", str))
- return true;
- return false;
-}
-
-static void setup(hwc_context_t* ctx, int dpy, bool usecopybit)
+static void setup(hwc_context_t* ctx, int dpy)
{
const int rSplit = 0; //Even split for external if at all
ctx->mFBUpdate[dpy] = IFBUpdate::getObject(ctx->dpyAttr[dpy].xres,
rSplit, dpy);
ctx->mMDPComp[dpy] = MDPComp::getObject(ctx->dpyAttr[dpy].xres,
rSplit, dpy);
- if(usecopybit)
+ int compositionType =
+ qdutils::QCCompositionType::getInstance().getCompositionType();
+ if (compositionType & (qdutils::COMPOSITION_TYPE_DYN |
+ qdutils::COMPOSITION_TYPE_MDP |
+ qdutils::COMPOSITION_TYPE_C2D)) {
ctx->mCopyBit[dpy] = new CopyBit();
+ }
}
static void clear(hwc_context_t* ctx, int dpy)
@@ -79,183 +78,203 @@
}
}
+/* Parse uevent data for devices which we are interested */
+static int getConnectedDisplay(const char* strUdata)
+{
+ if(strcasestr("change@/devices/virtual/switch/hdmi", strUdata))
+ return HWC_DISPLAY_EXTERNAL;
+ if(strcasestr("change@/devices/virtual/switch/wfd", strUdata))
+ return HWC_DISPLAY_VIRTUAL;
+ return -1;
+}
+
+/* Parse uevent data for action requested for the display */
+static int getConnectedState(const char* strUdata, int len)
+{
+ const char* iter_str = strUdata;
+ while(((iter_str - strUdata) <= len) && (*iter_str)) {
+ char* pstr = strstr(iter_str, "SWITCH_STATE=");
+ if (pstr != NULL) {
+ return (atoi(pstr + strlen("SWITCH_STATE=")));
+ }
+ iter_str += strlen(iter_str)+1;
+ }
+ return -1;
+}
+
static void handle_uevent(hwc_context_t* ctx, const char* udata, int len)
{
- int vsync = 0;
- int64_t timestamp = 0;
- const char *str = udata;
- bool usecopybit = false;
- int compositionType =
- qdutils::QCCompositionType::getInstance().getCompositionType();
-
- if (compositionType & (qdutils::COMPOSITION_TYPE_DYN |
- qdutils::COMPOSITION_TYPE_MDP |
- qdutils::COMPOSITION_TYPE_C2D)) {
- usecopybit = true;
- }
- if(!strcasestr("change@/devices/virtual/switch/hdmi", str) &&
- !strcasestr("change@/devices/virtual/switch/wfd", str)) {
- ALOGD_IF(UEVENT_DEBUG, "%s: Not Ext Disp Event ", __FUNCTION__);
+ int dpy = getConnectedDisplay(udata);
+ if(dpy < 0) {
+ ALOGD_IF(UEVENT_DEBUG, "%s: Not disp Event ", __FUNCTION__);
return;
}
- int connected = -1; // initial value - will be set to 1/0 based on hotplug
- int extDpyNum = HWC_DISPLAY_EXTERNAL;
- char property[PROPERTY_VALUE_MAX];
- if((property_get("persist.sys.wfd.virtual", property, NULL) > 0) &&
- (!strncmp(property, "1", PROPERTY_VALUE_MAX ) ||
- (!strncasecmp(property,"true", PROPERTY_VALUE_MAX )))) {
- // This means we are using Google API to trigger WFD Display
- extDpyNum = HWC_DISPLAY_VIRTUAL;
- }
+ int switch_state = getConnectedState(udata, len);
- int dpy = isHDMI(str) ? HWC_DISPLAY_EXTERNAL : extDpyNum;
+ ALOGE_IF(UEVENT_DEBUG,"%s: uevent recieved: %s switch state: %d",
+ __FUNCTION__,udata, switch_state);
- // parse HDMI/WFD switch state for connect/disconnect
- // for HDMI:
- // The event will be of the form:
- // change@/devices/virtual/switch/hdmi ACTION=change
- // SWITCH_STATE=1 or SWITCH_STATE=0
- while(*str) {
- if (!strncmp(str, "SWITCH_STATE=", strlen("SWITCH_STATE="))) {
- connected = atoi(str + strlen("SWITCH_STATE="));
- //Disabled until SF calls unblank
- ctx->dpyAttr[HWC_DISPLAY_EXTERNAL].isActive = false;
- //Ignored for Virtual Displays
- //ToDo: we can do this in a much better way
- ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].isActive = true;
- break;
- }
- str += strlen(str) + 1;
- if (str - udata >= len)
- break;
- }
- ALOGD_IF(UEVENT_DEBUG, "Received str:%s",udata);
-
- if(connected != EXTERNAL_ONLINE) {
- if(ctx->mExtDisplay->ignoreRequest(udata)) {
- ALOGD_IF(UEVENT_DEBUG,"No need to process this connection request"
- "str:%s",udata);
- ctx->dpyAttr[dpy].isActive = true;
- return;
- }
- }
-
- // update extDpyNum
- ctx->mExtDisplay->setExtDpyNum(dpy);
- switch(connected) {
- case EXTERNAL_OFFLINE:
- { // disconnect event
- const char *s1 = udata + strlen(HWC_UEVENT_SWITCH_STR);
- if(!strncmp(s1,"hdmi",strlen(s1))) {
- ctx->mExtDisplay->teardownHDMIDisplay();
- }else if(!strncmp(s1,"wfd",strlen(s1))) {
- ctx->mExtDisplay->teardownWFDDisplay();
- }
- Locker::Autolock _l(ctx->mExtLock);
- clear(ctx, dpy);
- ALOGD("%s sending hotplug: connected = %d and dpy:%d",
- __FUNCTION__, connected, dpy);
- ctx->dpyAttr[dpy].connected = false;
- //hwc comp could be on
- ctx->proc->hotplug(ctx->proc, dpy, connected);
+ switch(switch_state) {
+ case EXTERNAL_OFFLINE:
+ {
+ /* Display not connected */
+ if(!ctx->dpyAttr[dpy].connected){
+ ALOGE_IF(UEVENT_DEBUG,"%s: Ignoring EXTERNAL_OFFLINE event"
+ "for display: %d", __FUNCTION__, dpy);
break;
}
- case EXTERNAL_ONLINE:
- { // connect case
- {
- //Force composition to give up resources like pipes and
- //close fb. For example if assertive display is going on,
- //fb2 could be open, thus connecting Layer Mixer#0 to
- //WriteBack module. If HDMI attempts to open fb1, the driver
- //will try to attach Layer Mixer#0 to HDMI INT, which will
- //fail, since Layer Mixer#0 is still connected to WriteBack.
- //This block will force composition to close fb2 in above
- //example.
- Locker::Autolock _l(ctx->mExtLock);
- ctx->mExtDispConfiguring = true;
- ctx->dpyAttr[dpy].connected = false;
- ctx->proc->invalidate(ctx->proc);
- }
- //2 cycles for slower content
- usleep(ctx->dpyAttr[HWC_DISPLAY_PRIMARY].vsync_period
- * 2 / 1000);
- const char *s1 = udata + strlen(HWC_UEVENT_SWITCH_STR);
- if(!strncmp(s1,"hdmi",strlen(s1))) {
- // hdmi online event..!
- // check if WFD is configured
- if(ctx->mExtDisplay->isWFDActive()) {
- ALOGD_IF(UEVENT_DEBUG,"Received HDMI connection request"
- "when WFD is active");
- // teardown Active WFD Display
- ctx->mExtDisplay->teardownWFDDisplay();
+
+ Locker::Autolock _l(ctx->mExtLock);
+ clear(ctx, dpy);
+ ctx->dpyAttr[dpy].connected = false;
+ ctx->dpyAttr[dpy].isActive = false;
+
+ if(dpy == HWC_DISPLAY_EXTERNAL) {
+ ctx->mExtDisplay->teardown();
+ } else {
+ ctx->mVirtualDisplay->teardown();
+ }
+
+ /* We need to send hotplug to SF only when we are disconnecting
+ * (1) HDMI OR (2) proprietary WFD session */
+ if(dpy == HWC_DISPLAY_EXTERNAL ||
+ ctx->mVirtualonExtActive) {
+ ALOGE_IF(UEVENT_DEBUG,"%s:Sending EXTERNAL OFFLINE hotplug"
+ "event", __FUNCTION__);
+ ctx->proc->hotplug(ctx->proc, HWC_DISPLAY_EXTERNAL,
+ EXTERNAL_OFFLINE);
+ }
+ break;
+ }
+ case EXTERNAL_ONLINE:
+ {
+ /* Display already connected */
+ if(ctx->dpyAttr[dpy].connected) {
+ ALOGE_IF(UEVENT_DEBUG,"%s: Ignoring EXTERNAL_ONLINE event"
+ "for display: %d", __FUNCTION__, dpy);
+ break;
+ }
+ {
+ //Force composition to give up resources like pipes and
+ //close fb. For example if assertive display is going on,
+ //fb2 could be open, thus connecting Layer Mixer#0 to
+ //WriteBack module. If HDMI attempts to open fb1, the driver
+ //will try to attach Layer Mixer#0 to HDMI INT, which will
+ //fail, since Layer Mixer#0 is still connected to WriteBack.
+ //This block will force composition to close fb2 in above
+ //example.
+ Locker::Autolock _l(ctx->mExtLock);
+ ctx->dpyAttr[dpy].isConfiguring = true;
+ ctx->proc->invalidate(ctx->proc);
+ }
+ //2 cycles for slower content
+ usleep(ctx->dpyAttr[HWC_DISPLAY_PRIMARY].vsync_period
+ * 2 / 1000);
+
+ if(dpy == HWC_DISPLAY_EXTERNAL) {
+ if(ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].connected) {
+ ALOGD_IF(UEVENT_DEBUG,"Received HDMI connection request"
+ "when WFD is active");
+ {
+ Locker::Autolock _l(ctx->mExtLock);
+ clear(ctx, HWC_DISPLAY_VIRTUAL);
+ ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].connected = false;
+ ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].isActive = false;
+ }
+
+ ctx->mVirtualDisplay->teardown();
+
+ /* Need to send hotplug only when connected WFD in
+ * proprietary path */
+ if(ctx->mVirtualonExtActive) {
+ ALOGE_IF(UEVENT_DEBUG,"%s: Sending EXTERNAL OFFLINE"
+ "hotplug event", __FUNCTION__);
+ ctx->proc->hotplug(ctx->proc, HWC_DISPLAY_EXTERNAL,
+ EXTERNAL_OFFLINE);
{
Locker::Autolock _l(ctx->mExtLock);
- clear(ctx, dpy);
- //send hotplug disconnect event
- ALOGD_IF(UEVENT_DEBUG, "sending hotplug: disconnect"
- "for WFD");
- // hwc comp could be on
- ctx->proc->hotplug(ctx->proc, dpy, EXTERNAL_OFFLINE);
+ ctx->mVirtualonExtActive = false;
}
- //Invalidate
- ctx->proc->invalidate(ctx->proc);
- //wait for 1 second
- ALOGE_IF(UEVENT_DEBUG, "wait for 1 second -- padding"
- "round");
- sleep(1);
}
- ctx->mExtDisplay->configureHDMIDisplay();
- } else if(!strncmp(s1,"wfd",strlen(s1))) {
- // wfd online event..!
- ctx->mExtDisplay->configureWFDDisplay();
+ /* Wait for few frames for SF to tear down
+ * the WFD session. */
+ usleep(ctx->dpyAttr[HWC_DISPLAY_PRIMARY].vsync_period
+ * 2 / 1000);
}
+ ctx->mExtDisplay->configure();
+ } else {
{
Locker::Autolock _l(ctx->mExtLock);
- ctx->dpyAttr[dpy].isPause = false;
- setup(ctx, dpy, usecopybit);
- ALOGD("%s sending hotplug: connected = %d", __FUNCTION__,
- connected);
- ctx->dpyAttr[dpy].connected = true;
- ctx->proc->hotplug(ctx->proc, dpy, connected);
+ /* TRUE only when we are on proprietary WFD session */
+ ctx->mVirtualonExtActive = true;
+ char property[PROPERTY_VALUE_MAX];
+ if((property_get("persist.sys.wfd.virtual",
+ property, NULL) > 0) &&
+ (!strncmp(property, "1", PROPERTY_VALUE_MAX ) ||
+ (!strncasecmp(property,"true", PROPERTY_VALUE_MAX )))) {
+ // This means we are on Google's WFD session
+ ctx->mVirtualonExtActive = false;
+ }
}
- break;
+ ctx->mVirtualDisplay->configure();
}
- case EXTERNAL_PAUSE:
- { // pause case
- ALOGD("%s Received Pause event",__FUNCTION__);
- Locker::Autolock _l(ctx->mExtLock);
- ctx->dpyAttr[dpy].isActive = true;
- ctx->dpyAttr[dpy].isPause = true;
- break;
+
+ Locker::Autolock _l(ctx->mExtLock);
+ setup(ctx, dpy);
+ ctx->dpyAttr[dpy].isPause = false;
+ ctx->dpyAttr[dpy].connected = true;
+ ctx->dpyAttr[dpy].isConfiguring = true;
+
+ if(dpy == HWC_DISPLAY_VIRTUAL) {
+ /* We wont be getting unblank for VIRTUAL DISPLAY and its
+ * always guaranteed from WFD stack that CONNECT uevent for
+ * VIRTUAL DISPLAY will be triggered before creating
+ * surface for the same. */
+ ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].isActive = true;
}
- case EXTERNAL_RESUME:
- { // resume case
- ALOGD("%s Received resume event",__FUNCTION__);
- //Treat Resume as Online event
- //Since external didnt have any pipes, force primary to give up
- //its pipes; we don't allow inter-mixer pipe transfers.
- {
- Locker::Autolock _l(ctx->mExtLock);
- ctx->mExtDispConfiguring = true;
- ctx->dpyAttr[dpy].isActive = true;
- ctx->proc->invalidate(ctx->proc);
- }
- usleep(ctx->dpyAttr[HWC_DISPLAY_PRIMARY].vsync_period
- * 2 / 1000);
- //At this point external has all the pipes it would need.
- {
- Locker::Autolock _l(ctx->mExtLock);
- ctx->dpyAttr[dpy].isPause = false;
- ctx->proc->invalidate(ctx->proc);
- }
- break;
+
+ if(dpy == HWC_DISPLAY_EXTERNAL ||
+ ctx->mVirtualonExtActive) {
+ ALOGE_IF(UEVENT_DEBUG, "%s: Sending EXTERNAL_OFFLINE ONLINE"
+ "hotplug event", __FUNCTION__);
+ ctx->proc->hotplug(ctx->proc,HWC_DISPLAY_EXTERNAL,
+ EXTERNAL_ONLINE);
}
- default:
+ break;
+ }
+ case EXTERNAL_PAUSE:
+ {
+ ctx->dpyAttr[dpy].isActive = true;
+ ctx->dpyAttr[dpy].isPause = true;
+ break;
+ }
+ case EXTERNAL_RESUME:
+ {
+ //Treat Resume as Online event
+ //Since external didnt have any pipes, force primary to give up
+ //its pipes; we don't allow inter-mixer pipe transfers.
{
- ALOGE("ignore event and connected:%d",connected);
- break;
+ Locker::Autolock _l(ctx->mExtLock);
+ ctx->dpyAttr[dpy].isConfiguring = true;
+ ctx->dpyAttr[dpy].isActive = true;
+ ctx->proc->invalidate(ctx->proc);
}
+ usleep(ctx->dpyAttr[HWC_DISPLAY_PRIMARY].vsync_period
+ * 2 / 1000);
+ {
+ //At this point external has all the pipes it would need.
+ Locker::Autolock _l(ctx->mExtLock);
+ ctx->dpyAttr[dpy].isPause = false;
+ ctx->proc->invalidate(ctx->proc);
+ }
+ break;
+ }
+ default:
+ {
+ ALOGE("%s: Invalid state to swtich:%d", __FUNCTION__, switch_state);
+ break;
+ }
}
}
diff --git a/libhwcomposer/hwc_utils.cpp b/libhwcomposer/hwc_utils.cpp
index 2c02e5b..0e2eb2e 100644
--- a/libhwcomposer/hwc_utils.cpp
+++ b/libhwcomposer/hwc_utils.cpp
@@ -36,6 +36,7 @@
#include "hwc_copybit.h"
#include "hwc_dump_layers.h"
#include "external.h"
+#include "virtual.h"
#include "hwc_qclient.h"
#include "QService.h"
#include "comptype.h"
@@ -152,10 +153,17 @@
}
ctx->mExtDisplay = new ExternalDisplay(ctx);
+ ctx->mVirtualDisplay = new VirtualDisplay(ctx);
+ ctx->mVirtualonExtActive = false;
+ ctx->dpyAttr[HWC_DISPLAY_EXTERNAL].isActive = false;
+ ctx->dpyAttr[HWC_DISPLAY_EXTERNAL].connected = false;
+ ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].isActive = false;
+ ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].connected = false;
ctx->mMDPComp[HWC_DISPLAY_PRIMARY] =
MDPComp::getObject(ctx->dpyAttr[HWC_DISPLAY_PRIMARY].xres,
rightSplit, HWC_DISPLAY_PRIMARY);
+ ctx->dpyAttr[HWC_DISPLAY_PRIMARY].connected = true;
for (uint32_t i = 0; i < HWC_NUM_DISPLAY_TYPES; i++) {
ctx->mHwcDebug[i] = new HwcDebug(i);
@@ -167,7 +175,6 @@
ctx->vstate.enable = false;
ctx->vstate.fakevsync = false;
- ctx->mExtDispConfiguring = false;
ctx->mExtOrientation = 0;
//Right now hwc starts the service but anybody could do it, or it could be
@@ -1183,7 +1190,7 @@
bool canUseRotator(hwc_context_t *ctx) {
if(qdutils::MDPVersion::getInstance().is8x26() &&
- ctx->mExtDisplay->isExternalConnected()) {
+ ctx->mVirtualDisplay->isConnected()) {
return false;
}
if(ctx->mMDP.version == qdutils::MDP_V3_0_4)
@@ -1214,7 +1221,7 @@
return;
}
//External connected
- if(ctx->mExtDisplay->isExternalConnected()) {
+ if(ctx->mExtDisplay->isConnected()|| ctx->mVirtualDisplay->isConnected()) {
return;
}
//Decimation necessary, cannot use BWC. H/W requirement.
diff --git a/libhwcomposer/hwc_utils.h b/libhwcomposer/hwc_utils.h
index c4fd345..9a7ef0d 100644
--- a/libhwcomposer/hwc_utils.h
+++ b/libhwcomposer/hwc_utils.h
@@ -50,6 +50,7 @@
//fwrd decl
class QueuedBufferStore;
class ExternalDisplay;
+class VirtualDisplay;
class IFBUpdate;
class IVideoOverlay;
class MDPComp;
@@ -79,6 +80,9 @@
// In pause state, composition is bypassed
// used for WFD displays only
bool isPause;
+ // To trigger padding round to clean up mdp
+ // pipes
+ bool isConfiguring;
};
struct ListStats {
@@ -315,6 +319,7 @@
qhwc::IFBUpdate *mFBUpdate[HWC_NUM_DISPLAY_TYPES];
// External display related information
qhwc::ExternalDisplay *mExtDisplay;
+ qhwc::VirtualDisplay *mVirtualDisplay;
qhwc::MDPInfo mMDP;
qhwc::VsyncState vstate;
qhwc::DisplayAttributes dpyAttr[HWC_NUM_DISPLAY_TYPES];
@@ -333,8 +338,8 @@
int mPrevTransformVideo;
//Securing in progress indicator
bool mSecuring;
- //External Display configuring progress indicator
- bool mExtDispConfiguring;
+ //WFD on proprietary stack
+ bool mVirtualonExtActive;
//Display in secure mode indicator
bool mSecureMode;
//Lock to prevent set from being called while blanking
diff --git a/libhwcomposer/hwc_vsync.cpp b/libhwcomposer/hwc_vsync.cpp
index e4da4f7..7189a01 100644
--- a/libhwcomposer/hwc_vsync.cpp
+++ b/libhwcomposer/hwc_vsync.cpp
@@ -29,6 +29,7 @@
#include "hwc_utils.h"
#include "string.h"
#include "external.h"
+#include "overlay.h"
namespace qhwc {
@@ -63,7 +64,7 @@
struct pollfd pfd[2];
int fb_fd[2];
- uint64_t timestamp[2];
+ uint64_t timestamp[2] = {0,0};
int num_displays;
char property[PROPERTY_VALUE_MAX];
@@ -77,7 +78,7 @@
logvsync = true;
}
- if (ctx->mExtDisplay->getHDMIIndex() > 0)
+ if (ctx->mExtDisplay->isConnected())
num_displays = 2;
else
num_displays = 1;
@@ -87,7 +88,8 @@
snprintf(vsync_node_path, sizeof(vsync_node_path),
"/sys/class/graphics/fb%d/vsync_event",
dpy == HWC_DISPLAY_PRIMARY ? 0 :
- ctx->mExtDisplay->getHDMIIndex());
+ overlay::Overlay::getInstance()->
+ getFbForDpy(HWC_DISPLAY_EXTERNAL));
ALOGI("%s: Reading vsync for dpy=%d from %s", __FUNCTION__, dpy,
vsync_node_path);
fb_fd[dpy] = open(vsync_node_path, O_RDONLY);