hwc: Allow HWC to support Virtual Display
This change allows virtual display to be composed
by HWC for supporting Google WFD App + QCOM WFD stack.
Change-Id: If8892230256e72fa34e3fb5ae715c3ad8cbd5b64
diff --git a/libexternal/external.cpp b/libexternal/external.cpp
index 883a39f..839d79a 100644
--- a/libexternal/external.cpp
+++ b/libexternal/external.cpp
@@ -168,11 +168,13 @@
ExternalDisplay::ExternalDisplay(hwc_context_t* ctx):mFd(-1),
mCurrentMode(-1), mConnected(0), mConnectedFbNum(0), mModeCount(0),
- mUnderscanSupported(false), mHwcContext(ctx), mHdmiFbNum(-1), mWfdFbNum(-1)
+ mUnderscanSupported(false), mHwcContext(ctx), mHdmiFbNum(-1),
+ mWfdFbNum(-1), mExtDpyNum(HWC_DISPLAY_EXTERNAL)
{
memset(&mVInfo, 0, sizeof(mVInfo));
//Determine the fb index for external display devices.
updateExtDispDevFbIndex();
+
}
void ExternalDisplay::setEDIDMode(int resMode) {
@@ -441,7 +443,7 @@
ALOGE("%s: %s is not available", __FUNCTION__,
msmFbDevicePath[fbNum-1]);
if(mHwcContext) {
- mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].fd = mFd;
+ mHwcContext->dpyAttr[mExtDpyNum].fd = mFd;
}
}
return (mFd > 0);
@@ -455,7 +457,7 @@
mFd = -1;
}
if(mHwcContext) {
- mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].fd = mFd;
+ mHwcContext->dpyAttr[mExtDpyNum].fd = mFd;
}
return (ret == 0);
}
@@ -639,7 +641,7 @@
// Store the external display
mConnected = connected;
mConnectedFbNum = extFbNum;
- mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].connected = connected;
+ mHwcContext->dpyAttr[mExtDpyNum].connected = connected;
// Update external fb number in Overlay context
overlay::Overlay::getInstance()->setExtFbNum(extFbNum);
}
@@ -688,6 +690,7 @@
{
if(mFd == -1)
return false;
+
struct mdp_display_commit ext_commit;
memset(&ext_commit, 0, sizeof(struct mdp_display_commit));
ext_commit.flags = MDP_DISPLAY_COMMIT_OVERLAY;
@@ -701,9 +704,9 @@
void ExternalDisplay::setDpyWfdAttr() {
if(mHwcContext) {
- mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].xres = mVInfo.xres;
- mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].yres = mVInfo.yres;
- mHwcContext->dpyAttr[HWC_DISPLAY_EXTERNAL].vsync_period =
+ mHwcContext->dpyAttr[mExtDpyNum].xres = mVInfo.xres;
+ mHwcContext->dpyAttr[mExtDpyNum].yres = mVInfo.yres;
+ mHwcContext->dpyAttr[mExtDpyNum].vsync_period =
1000000000l /60;
ALOGD_IF(DEBUG,"%s: wfd...connected..!",__FUNCTION__);
}
diff --git a/libexternal/external.h b/libexternal/external.h
index baf3598..39f8645 100644
--- a/libexternal/external.h
+++ b/libexternal/external.h
@@ -46,6 +46,7 @@
bool isCEUnderscanSupported() { return mUnderscanSupported; }
void setExternalDisplay(bool connected, int extFbNum = 0);
bool isExternalConnected() { return mConnected;};
+ void setExtDpyNum(int extDpyNum) { mExtDpyNum = extDpyNum;};
bool post();
void setHPD(uint32_t startEnd);
void setEDIDMode(int resMode);
@@ -92,6 +93,7 @@
fb_var_screeninfo mVInfo;
int mHdmiFbNum;
int mWfdFbNum;
+ int mExtDpyNum;
};
}; //qhwc
diff --git a/libhwcomposer/hwc.cpp b/libhwcomposer/hwc.cpp
index 187ec4a..519126f 100644
--- a/libhwcomposer/hwc.cpp
+++ b/libhwcomposer/hwc.cpp
@@ -84,7 +84,7 @@
static void reset(hwc_context_t *ctx, int numDisplays,
hwc_display_contents_1_t** displays) {
memset(ctx->listStats, 0, sizeof(ctx->listStats));
- for(int i = 0; i < HWC_NUM_DISPLAY_TYPES; i++){
+ for(int i = 0; i < MAX_DISPLAYS; i++) {
hwc_display_contents_1_t *list = displays[i];
// XXX:SurfaceFlinger no longer guarantees that this
// value is reset on every prepare. However, for the layer
@@ -148,25 +148,31 @@
}
static int hwc_prepare_external(hwc_composer_device_1 *dev,
- hwc_display_contents_1_t *list) {
+ hwc_display_contents_1_t *list, int dpy) {
hwc_context_t* ctx = (hwc_context_t*)(dev);
- const int dpy = HWC_DISPLAY_EXTERNAL;
if (LIKELY(list && list->numHwLayers > 1) &&
ctx->dpyAttr[dpy].isActive &&
ctx->dpyAttr[dpy].connected) {
-
uint32_t last = list->numHwLayers - 1;
- hwc_layer_1_t *fbLayer = &list->hwLayers[last];
- if(fbLayer->handle) {
- setListStats(ctx, list, dpy);
- reset_layer_prop(ctx, dpy);
- VideoOverlay::prepare(ctx, list, dpy);
- ctx->mFBUpdate[dpy]->prepare(ctx, fbLayer);
- ctx->mLayerCache[dpy]->updateLayerCache(list);
- if(ctx->mCopyBit[dpy])
- ctx->mCopyBit[dpy]->prepare(ctx, list, dpy);
- ctx->mExtDispConfiguring = false;
+ if(!ctx->dpyAttr[dpy].isPause) {
+ hwc_layer_1_t *fbLayer = &list->hwLayers[last];
+ if(fbLayer->handle) {
+ setListStats(ctx, list, dpy);
+ reset_layer_prop(ctx, dpy);
+ VideoOverlay::prepare(ctx, list, dpy);
+ ctx->mFBUpdate[dpy]->prepare(ctx, fbLayer);
+ ctx->mLayerCache[dpy]->updateLayerCache(list);
+ if(ctx->mCopyBit[dpy])
+ ctx->mCopyBit[dpy]->prepare(ctx, list, dpy);
+ ctx->mExtDispConfiguring = false;
+ }
+ } else {
+ // External 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;
@@ -182,14 +188,15 @@
ctx->mOverlay->configBegin();
- for (int32_t i = numDisplays - 1; i >= 0; i--) {
+ for (int32_t i = numDisplays; i >= 0; i--) {
hwc_display_contents_1_t *list = displays[i];
switch(i) {
case HWC_DISPLAY_PRIMARY:
ret = hwc_prepare_primary(dev, list);
break;
case HWC_DISPLAY_EXTERNAL:
- ret = hwc_prepare_external(dev, list);
+ case HWC_DISPLAY_VIRTUAL:
+ ret = hwc_prepare_external(dev, list, i);
break;
default:
ret = -EINVAL;
@@ -244,8 +251,9 @@
}
break;
case HWC_DISPLAY_EXTERNAL:
+ case HWC_DISPLAY_VIRTUAL:
if(blank) {
- // External Display post commits the changes to display
+ // External/Virtual Display post commits the changes to display
// Call this on blank, so that any pipe unsets gets committed
if (!ctx->mExtDisplay->post()) {
ret = -1;
@@ -342,13 +350,12 @@
}
static int hwc_set_external(hwc_context_t *ctx,
- hwc_display_contents_1_t* list) {
+ hwc_display_contents_1_t* list, int dpy) {
int ret = 0;
- const int dpy = HWC_DISPLAY_EXTERNAL;
-
Locker::Autolock _l(ctx->mExtSetLock);
if (LIKELY(list) && ctx->dpyAttr[dpy].isActive &&
+ !ctx->dpyAttr[dpy].isPause &&
ctx->dpyAttr[dpy].connected) {
uint32_t last = list->numHwLayers - 1;
hwc_layer_1_t *fbLayer = &list->hwLayers[last];
@@ -388,14 +395,19 @@
int ret = 0;
hwc_context_t* ctx = (hwc_context_t*)(dev);
Locker::Autolock _l(ctx->mBlankLock);
- for (uint32_t i = 0; i < numDisplays; i++) {
+ for (uint32_t i = 0; i <= numDisplays; i++) {
hwc_display_contents_1_t* list = displays[i];
switch(i) {
case HWC_DISPLAY_PRIMARY:
ret = hwc_set_primary(ctx, list);
break;
case HWC_DISPLAY_EXTERNAL:
- ret = hwc_set_external(ctx, list);
+ 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);
break;
default:
ret = -EINVAL;
diff --git a/libhwcomposer/hwc_uevents.cpp b/libhwcomposer/hwc_uevents.cpp
index 6b8f4e3..752aaa2 100644
--- a/libhwcomposer/hwc_uevents.cpp
+++ b/libhwcomposer/hwc_uevents.cpp
@@ -35,6 +35,21 @@
#define HWC_UEVENT_THREAD_NAME "hwcUeventThread"
+/* External Display states */
+enum {
+ EXTERNAL_OFFLINE = 0,
+ EXTERNAL_ONLINE,
+ EXTERNAL_PAUSE,
+ EXTERNAL_RESUME
+};
+
+static bool isHDMI(const char* str)
+{
+ if(strcasestr("change@/devices/virtual/switch/hdmi", str))
+ return true;
+ return false;
+}
+
static void handle_uevent(hwc_context_t* ctx, const char* udata, int len)
{
int vsync = 0;
@@ -48,7 +63,6 @@
qdutils::COMPOSITION_TYPE_MDP |
qdutils::COMPOSITION_TYPE_C2D)) {
usecopybit = true;
-
}
if(!strcasestr("change@/devices/virtual/switch/hdmi", str) &&
@@ -56,19 +70,35 @@
ALOGD_IF(UEVENT_DEBUG, "%s: Not Ext 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 dpy = isHDMI(str) ? HWC_DISPLAY_EXTERNAL : extDpyNum;
+
+ // update extDpyNum
+ ctx->mExtDisplay->setExtDpyNum(dpy);
+
// 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;
@@ -76,32 +106,63 @@
break;
}
- if(connected != -1) { //either we got switch_state connected or disconnect
- ctx->dpyAttr[HWC_DISPLAY_EXTERNAL].connected = connected;
- if (connected) {
- ctx->mExtDispConfiguring = true;
- ctx->mExtDisplay->processUEventOnline(udata);
- ctx->mFBUpdate[HWC_DISPLAY_EXTERNAL] =
- IFBUpdate::getObject(ctx->dpyAttr[HWC_DISPLAY_EXTERNAL].xres,
- HWC_DISPLAY_EXTERNAL);
- if(usecopybit)
- ctx->mCopyBit[HWC_DISPLAY_EXTERNAL] = new CopyBit();
- } else {
- ctx->mExtDisplay->processUEventOffline(udata);
- if(ctx->mFBUpdate[HWC_DISPLAY_EXTERNAL]) {
+ switch(connected) {
+ case EXTERNAL_OFFLINE:
+ { // disconnect event
+ ctx->mExtDisplay->processUEventOffline(udata);
+ if(ctx->mFBUpdate[dpy]) {
+ Locker::Autolock _l(ctx->mExtSetLock);
+ delete ctx->mFBUpdate[dpy];
+ ctx->mFBUpdate[dpy] = NULL;
+ }
+ if(ctx->mCopyBit[dpy]){
+ Locker::Autolock _l(ctx->mExtSetLock);
+ delete ctx->mCopyBit[dpy];
+ ctx->mCopyBit[dpy] = NULL;
+ }
+ ALOGD("%s sending hotplug: connected = %d and dpy:%d",
+ __FUNCTION__, connected, dpy);
+ ctx->dpyAttr[dpy].connected = false;
Locker::Autolock _l(ctx->mExtSetLock);
- delete ctx->mFBUpdate[HWC_DISPLAY_EXTERNAL];
- ctx->mFBUpdate[HWC_DISPLAY_EXTERNAL] = NULL;
+ //hwc comp could be on
+ ctx->proc->hotplug(ctx->proc, dpy, connected);
+ break;
}
- if(ctx->mCopyBit[HWC_DISPLAY_EXTERNAL]){
- Locker::Autolock _l(ctx->mExtSetLock);
- delete ctx->mCopyBit[HWC_DISPLAY_EXTERNAL];
- ctx->mCopyBit[HWC_DISPLAY_EXTERNAL] = NULL;
+ case EXTERNAL_ONLINE:
+ { // connect case
+ ctx->mExtDispConfiguring = true;
+ ctx->mExtDisplay->processUEventOnline(udata);
+ ctx->mFBUpdate[dpy] =
+ IFBUpdate::getObject(ctx->dpyAttr[dpy].xres, dpy);
+ ctx->dpyAttr[dpy].isPause = false;
+ if(usecopybit)
+ ctx->mCopyBit[dpy] = new CopyBit();
+ ALOGD("%s sending hotplug: connected = %d", __FUNCTION__,
+ connected);
+ ctx->dpyAttr[dpy].connected = true;
+ Locker::Autolock _l(ctx->mExtSetLock); //hwc comp could be on
+ ctx->proc->hotplug(ctx->proc, dpy, connected);
+ break;
}
- }
- ALOGD("%s sending hotplug: connected = %d", __FUNCTION__, connected);
- Locker::Autolock _l(ctx->mExtSetLock); //hwc comp could be on
- ctx->proc->hotplug(ctx->proc, HWC_DISPLAY_EXTERNAL, connected);
+ case EXTERNAL_PAUSE:
+ { // pause case
+ ALOGD("%s Received Pause event",__FUNCTION__);
+ ctx->dpyAttr[dpy].isActive = true;
+ ctx->dpyAttr[dpy].isPause = true;
+ break;
+ }
+ case EXTERNAL_RESUME:
+ { // resume case
+ ALOGD("%s Received resume event",__FUNCTION__);
+ ctx->dpyAttr[dpy].isActive = true;
+ ctx->dpyAttr[dpy].isPause = false;
+ break;
+ }
+ default:
+ {
+ ALOGE("ignore event and connected:%d",connected);
+ break;
+ }
}
}
diff --git a/libhwcomposer/hwc_utils.cpp b/libhwcomposer/hwc_utils.cpp
index e7c60d6..fe3401b 100644
--- a/libhwcomposer/hwc_utils.cpp
+++ b/libhwcomposer/hwc_utils.cpp
@@ -90,7 +90,7 @@
}
ctx->mExtDisplay = new ExternalDisplay(ctx);
- for (uint32_t i = 0; i < HWC_NUM_DISPLAY_TYPES; i++)
+ for (uint32_t i = 0; i < MAX_DISPLAYS; i++)
ctx->mLayerCache[i] = new LayerCache();
ctx->mMDPComp = MDPComp::getObject(ctx->dpyAttr[HWC_DISPLAY_PRIMARY].xres);
MDPComp::init(ctx);
@@ -119,7 +119,7 @@
ctx->mOverlay = NULL;
}
- for(int i = 0; i< HWC_NUM_DISPLAY_TYPES; i++) {
+ for(int i = 0; i < MAX_DISPLAYS; i++) {
if(ctx->mCopyBit[i]) {
delete ctx->mCopyBit[i];
ctx->mCopyBit[i] = NULL;
@@ -138,7 +138,7 @@
ctx->mExtDisplay = NULL;
}
- for(int i = 0; i < HWC_NUM_DISPLAY_TYPES; i++) {
+ for(int i = 0; i < MAX_DISPLAYS; i++) {
if(ctx->mFBUpdate[i]) {
delete ctx->mFBUpdate[i];
ctx->mFBUpdate[i] = NULL;
diff --git a/libhwcomposer/hwc_utils.h b/libhwcomposer/hwc_utils.h
index 525fd75..045ef7f 100644
--- a/libhwcomposer/hwc_utils.h
+++ b/libhwcomposer/hwc_utils.h
@@ -36,6 +36,10 @@
#define MAX_NUM_LAYERS 32
#define MAX_DISPLAY_DIM 2048
+// For support of virtual displays
+#define HWC_DISPLAY_VIRTUAL (HWC_DISPLAY_EXTERNAL+1)
+#define MAX_DISPLAYS (HWC_NUM_DISPLAY_TYPES+1)
+
//Fwrd decls
struct hwc_context_t;
struct framebuffer_device_t;
@@ -70,6 +74,9 @@
//Connected does not mean it ready to use.
//It should be active also. (UNBLANKED)
bool isActive;
+ // In pause state, composition is bypassed
+ // used for WFD displays only
+ bool isPause;
};
struct ListStats {
@@ -221,20 +228,20 @@
framebuffer_device_t *mFbDev;
//CopyBit objects
- qhwc::CopyBit *mCopyBit[HWC_NUM_DISPLAY_TYPES];
+ qhwc::CopyBit *mCopyBit[MAX_DISPLAYS];
//Overlay object - NULL for non overlay devices
overlay::Overlay *mOverlay;
//Primary and external FB updater
- qhwc::IFBUpdate *mFBUpdate[HWC_NUM_DISPLAY_TYPES];
+ qhwc::IFBUpdate *mFBUpdate[MAX_DISPLAYS];
// External display related information
qhwc::ExternalDisplay *mExtDisplay;
qhwc::MDPInfo mMDP;
- qhwc::DisplayAttributes dpyAttr[HWC_NUM_DISPLAY_TYPES];
- qhwc::ListStats listStats[HWC_NUM_DISPLAY_TYPES];
- qhwc::LayerCache *mLayerCache[HWC_NUM_DISPLAY_TYPES];
- qhwc::LayerProp *layerProp[HWC_NUM_DISPLAY_TYPES];
+ qhwc::DisplayAttributes dpyAttr[MAX_DISPLAYS];
+ qhwc::ListStats listStats[MAX_DISPLAYS];
+ qhwc::LayerCache *mLayerCache[MAX_DISPLAYS];
+ qhwc::LayerProp *layerProp[MAX_DISPLAYS];
qhwc::MDPComp *mMDPComp;
//Securing in progress indicator
diff --git a/libhwcomposer/hwc_video.h b/libhwcomposer/hwc_video.h
index 3612018..93cdaa5 100644
--- a/libhwcomposer/hwc_video.h
+++ b/libhwcomposer/hwc_video.h
@@ -45,12 +45,12 @@
//Marks layer flags if this feature is used
static void markFlags(hwc_layer_1_t *yuvLayer);
//Flags if this feature is on.
- static bool sIsModeOn[HWC_NUM_DISPLAY_TYPES];
- static ovutils::eDest sDest[HWC_NUM_DISPLAY_TYPES];
+ static bool sIsModeOn[MAX_DISPLAYS];
+ static ovutils::eDest sDest[MAX_DISPLAYS];
};
inline void VideoOverlay::reset() {
- for(uint32_t i = 0; i < HWC_NUM_DISPLAY_TYPES; i++) {
+ for(uint32_t i = 0; i < MAX_DISPLAYS; i++) {
sIsModeOn[i] = false;
sDest[i] = ovutils::OV_INVALID;
}