hwc: Support for windowboxing feature on external

1. This feature is targeted to remove any window/pillar boxing on
   external display when zoom mode is enabled.
2. Preserve the aspect ratio of the external display by cropping and
   upscaling the tagged video layer during video playback on external.
3. User can tag the layers to be displayed on external display.
4. Set sys.hwc.windowbox_feature to true to enable this feature

Assumptions & Limitation:
    1. Tagged layers for external display will also be displayed on
       primary display along with untagged layers
    2. When zoom in mode is enabled, source crop left and source crop
       top of a tagged video layer is always assumed to be set to
       positive integer value. So pinch zoom feature or zooming in top
       left corner of the video use case cannot be supported.
    3. Except tagged video layers, all other tagged UI layers will
       use GPU for composition on external display, So all tagged UI
       layers cannot be secure layer.
    4. Rotation animation cannot be supported for this feature.

Change-Id: I8b934cf616ec23b4359d0120f9a291178a2781c6
diff --git a/libhwcomposer/hwc_mdpcomp.cpp b/libhwcomposer/hwc_mdpcomp.cpp
index e24925a..5bd3392 100644
--- a/libhwcomposer/hwc_mdpcomp.cpp
+++ b/libhwcomposer/hwc_mdpcomp.cpp
@@ -710,6 +710,16 @@
     const int numAppLayers = ctx->listStats[mDpy].numAppLayers;
     int priDispW = ctx->dpyAttr[HWC_DISPLAY_PRIMARY].xres;
 
+    // This is a special mode which needs to be handled separately.
+    // Only AIV tagged layers will be displayed on external in this mode.
+    // This is applicable only for external display , Return false, so that
+    // this will be handled separately
+    if(ctx->mAIVVideoMode[mDpy]) {
+        ALOGD_IF(isDebug(), "%s: AIV Video Mode enabled dpy %d",
+            __FUNCTION__, mDpy);
+        return false;
+    }
+
     // No Idle fall back, if secure display or secure RGB layers are present or
     // if there's only a single layer being composed
     if(sIdleFallBack && (!ctx->listStats[mDpy].secureUI &&
@@ -1251,8 +1261,55 @@
     return true;
 }
 
+bool MDPComp::tryAIVVideoMode(hwc_context_t *ctx,
+        hwc_display_contents_1_t* list) {
+    if(sSimulationFlags & MDPCOMP_AVOID_AIV_VIDEO_MODE)
+        return false;
+    if(!ctx->mAIVVideoMode[mDpy]) {
+        return false;
+    }
+    int numAppLayers = ctx->listStats[mDpy].numAppLayers;
+
+    mCurrentFrame.reset(numAppLayers);
+    updateAIVLayers(ctx, list);
+    int mdpCount = mCurrentFrame.mdpCount;
+
+    if(mdpCount == 0) {
+        reset(ctx);
+        return false;
+    }
+
+    if(mCurrentFrame.fbCount) {
+        mCurrentFrame.fbZ = mCurrentFrame.mdpCount;
+    }
+
+    if(sEnableYUVsplit){
+        adjustForSourceSplit(ctx, list);
+    }
+
+    if(!postHeuristicsHandling(ctx, list)) {
+        ALOGD_IF(isDebug(), "post heuristic handling failed");
+        reset(ctx);
+        return false;
+    }
+
+    ALOGD_IF(sSimulationFlags,"%s: AIV_VIDEO_MODE_COMP SUCCEEDED",
+             __FUNCTION__);
+    return true;
+}
+
 bool MDPComp::tryVideoOnly(hwc_context_t *ctx,
         hwc_display_contents_1_t* list) {
+    // This is a special mode which needs to be handled separately.
+    // Only AIV tagged layers will be displayed on external in this mode.
+    // This is applicable only for external display , Return false, so that
+    // this will be handled separately
+    if(ctx->mAIVVideoMode[mDpy]) {
+        ALOGD_IF(isDebug(), "%s: AIV Video Mode enabled dpy %d",
+            __FUNCTION__, mDpy);
+        return false;
+    }
+
     const bool secureOnly = true;
     return videoOnlyComp(ctx, list, not secureOnly) or
             videoOnlyComp(ctx, list, secureOnly);
@@ -1302,6 +1359,16 @@
 /* if tryFullFrame fails, try to push all video and secure RGB layers to MDP */
 bool MDPComp::tryMDPOnlyLayers(hwc_context_t *ctx,
         hwc_display_contents_1_t* list) {
+    // This is a special mode which needs to be handled separately.
+    // Only AIV tagged layers will be displayed on external in this mode.
+    // This is applicable only for external display , Return false, so that
+    // this will be handled separately
+    if(ctx->mAIVVideoMode[mDpy]) {
+        ALOGD_IF(isDebug(), "%s: AIV Video Mode enabled dpy %d",
+            __FUNCTION__, mDpy);
+        return false;
+    }
+
     const bool secureOnly = true;
     return mdpOnlyLayersComp(ctx, list, not secureOnly) or
             mdpOnlyLayersComp(ctx, list, secureOnly);
@@ -1598,6 +1665,29 @@
             __FUNCTION__, frame.mdpCount, frame.fbCount, frame.dropCount);
 }
 
+// Mark AIV layers for composition and drop other non-AIV layers.
+void MDPComp::updateAIVLayers(hwc_context_t* ctx,
+                              hwc_display_contents_1_t* list) {
+    for (size_t i = 0; i < (size_t)ctx->listStats[mDpy].numAppLayers; i++) {
+        hwc_layer_1_t * layer = &list->hwLayers[i];
+        if(isAIVVideoLayer(layer)) {
+            if(isYUVDoable(ctx, layer)) {
+                mCurrentFrame.isFBComposed[i] = false;
+                mCurrentFrame.fbCount--;
+            }
+        } else if(!isAIVCCLayer(layer)) {
+            mCurrentFrame.dropCount++;
+            mCurrentFrame.drop[i] = true;
+        }
+    }
+    mCurrentFrame.fbCount -= mCurrentFrame.dropCount;
+    mCurrentFrame.mdpCount = mCurrentFrame.layerCount -
+            mCurrentFrame.fbCount - mCurrentFrame.dropCount;
+    ALOGD_IF(isDebug(),"%s: fb count: %d mdp count %d drop count %d",
+        __FUNCTION__, mCurrentFrame.fbCount, mCurrentFrame.mdpCount,
+        mCurrentFrame.dropCount);
+}
+
 void MDPComp::updateYUV(hwc_context_t* ctx, hwc_display_contents_1_t* list,
         bool secureOnly, FrameInfo& frame) {
     int nYuvCount = ctx->listStats[mDpy].yuvCount;
@@ -1877,7 +1967,7 @@
         // if tryFullFrame fails, try to push all video and secure RGB layers
         // to MDP for composition.
         mModeOn = tryFullFrame(ctx, list) || tryMDPOnlyLayers(ctx, list) ||
-                  tryVideoOnly(ctx, list);
+                  tryVideoOnly(ctx, list) || tryAIVVideoMode(ctx, list);
         if(mModeOn) {
             setMDPCompLayerFlags(ctx, list);
         } else {
@@ -2564,6 +2654,10 @@
         else if (hnd->format == HAL_PIXEL_FORMAT_RGBX_8888)
             whf.format = getMdpFormat(HAL_PIXEL_FORMAT_BGRX_8888);
     }
+    // update source crop and destination position of AIV video layer.
+    if(ctx->mAIVVideoMode[mDpy] &&isYuvBuffer(hnd)) {
+        updateExtDisplayCoordinates(ctx, crop, dst, mDpy);
+    }
     /* Calculate the external display position based on MDP downscale,
        ActionSafe, and extorientation features. */
     calcExtDisplayPosition(ctx, hnd, mDpy, crop, dst, transform, orient);
diff --git a/libhwcomposer/hwc_mdpcomp.h b/libhwcomposer/hwc_mdpcomp.h
index 3ed9186..5b47984 100644
--- a/libhwcomposer/hwc_mdpcomp.h
+++ b/libhwcomposer/hwc_mdpcomp.h
@@ -75,6 +75,7 @@
         MDPCOMP_AVOID_LOAD_MDP = 0x004,
         MDPCOMP_AVOID_VIDEO_ONLY = 0x008,
         MDPCOMP_AVOID_MDP_ONLY_LAYERS = 0x010,
+        MDPCOMP_AVOID_AIV_VIDEO_MODE = 0x020,
     };
 
     /* mdp pipe data */
@@ -187,6 +188,8 @@
     bool loadBasedComp(hwc_context_t *ctx, hwc_display_contents_1_t* list);
     /* Checks if its worth doing load based partial comp */
     bool isLoadBasedCompDoable(hwc_context_t *ctx);
+    /* checks for conditions where AIV layers cannot be bypassed */
+    bool tryAIVVideoMode(hwc_context_t *ctx, hwc_display_contents_1_t* list);
     /* checks for conditions where only video can be bypassed */
     bool tryVideoOnly(hwc_context_t *ctx, hwc_display_contents_1_t* list);
     bool videoOnlyComp(hwc_context_t *ctx, hwc_display_contents_1_t* list,
@@ -222,6 +225,9 @@
     bool intersectingUpdatingLayers(const hwc_display_contents_1_t* list,
             int fromIndex, int toIndex, int targetLayerIndex);
 
+    /* Mark AIV layers for composition and drop other non-AIV layers.*/
+    void updateAIVLayers(hwc_context_t* ctx, hwc_display_contents_1_t* list);
+
         /* updates cache map with YUV info */
     void updateYUV(hwc_context_t* ctx, hwc_display_contents_1_t* list,
             bool secureOnly, FrameInfo& frame);
diff --git a/libhwcomposer/hwc_utils.cpp b/libhwcomposer/hwc_utils.cpp
index c409db4..dcc7687 100644
--- a/libhwcomposer/hwc_utils.cpp
+++ b/libhwcomposer/hwc_utils.cpp
@@ -368,6 +368,13 @@
     ctx->mGPUHintInfo.mCompositionState = COMPOSITION_STATE_MDP;
     ctx->mGPUHintInfo.mCurrGPUPerfMode = EGL_GPU_LEVEL_0;
 #endif
+    // Read the system property to determine if windowboxing feature is enabled.
+    ctx->mWindowboxFeature = false;
+    if(property_get("sys.hwc.windowbox_feature", value, "false")
+            && !strcmp(value, "true")) {
+        ctx->mWindowboxFeature = true;
+    }
+
     memset(&(ctx->mPtorInfo), 0, sizeof(ctx->mPtorInfo));
     ALOGI("Initializing Qualcomm Hardware Composer");
     ALOGI("MDP version: %d", ctx->mMDP.version);
@@ -957,6 +964,7 @@
     uint32_t refreshRate = 0;
     qdutils::MDPVersion& mdpHw = qdutils::MDPVersion::getInstance();
 
+    ctx->mAIVVideoMode[dpy] = false;
     resetROI(ctx, dpy);
 
     trimList(ctx, list, dpy);
@@ -966,6 +974,9 @@
         private_handle_t *hnd = (private_handle_t *)layer->handle;
 
 #ifdef QCOM_BSP
+        if(ctx->mWindowboxFeature && dpy && isAIVVideoLayer(layer)) {
+            ctx->mAIVVideoMode[dpy] = true;
+        }
         if (layer->flags & HWC_SCREENSHOT_ANIMATOR_LAYER) {
             ctx->listStats[dpy].isDisplayAnimating = true;
         }
@@ -1815,6 +1826,66 @@
     return downscale;
 }
 
+bool isZoomModeEnabled(hwc_rect_t crop) {
+    // This does not work for zooming in top left corner of the image
+    return(crop.top > 0 || crop.left > 0);
+}
+
+void updateCropAIVVideoMode(hwc_context_t *ctx, hwc_rect_t& crop, int dpy) {
+    ALOGD_IF(HWC_UTILS_DEBUG, "dpy %d Source crop [%d %d %d %d]", dpy,
+             crop.left, crop.top, crop.right, crop.bottom);
+    if(isZoomModeEnabled(crop)) {
+        Dim srcCrop(crop.left, crop.top,
+                crop.right - crop.left,
+                crop.bottom - crop.top);
+        int extW = ctx->dpyAttr[dpy].xres;
+        int extH = ctx->dpyAttr[dpy].yres;
+        //Crop the original video in order to fit external display aspect ratio
+        if(srcCrop.w * extH < extW * srcCrop.h) {
+            int offset = (srcCrop.h - ((srcCrop.w * extH) / extW)) / 2;
+            crop.top += offset;
+            crop.bottom -= offset;
+        } else {
+            int offset = (srcCrop.w - ((extW * srcCrop.h) / extH)) / 2;
+            crop.left += offset;
+            crop.right -= offset;
+        }
+        ALOGD_IF(HWC_UTILS_DEBUG, "External Resolution [%d %d] dpy %d Modified"
+                 " source crop [%d %d %d %d]", extW, extH, dpy,
+                 crop.left, crop.top, crop.right, crop.bottom);
+    }
+}
+
+void updateDestAIVVideoMode(hwc_context_t *ctx, hwc_rect_t crop,
+                           hwc_rect_t& dst, int dpy) {
+    ALOGD_IF(HWC_UTILS_DEBUG, "dpy %d Destination position [%d %d %d %d]", dpy,
+             dst.left, dst.top, dst.right, dst.bottom);
+    Dim srcCrop(crop.left, crop.top,
+            crop.right - crop.left,
+            crop.bottom - crop.top);
+    int extW = ctx->dpyAttr[dpy].xres;
+    int extH = ctx->dpyAttr[dpy].yres;
+    // Set the destination coordinates of external display to full screen,
+    // when zoom in mode is enabled or video aspect ratio matches with the
+    // external display aspect ratio
+    if((srcCrop.w * extH == extW * srcCrop.h) || (isZoomModeEnabled(crop))) {
+        dst.left = 0;
+        dst.top = 0;
+        dst.right = extW;
+        dst.bottom = extH;
+    }
+    ALOGD_IF(HWC_UTILS_DEBUG, "External Resolution [%d %d] dpy %d Modified"
+             " Destination position [%d %d %d %d] Source crop [%d %d %d %d]",
+             extW, extH, dpy, dst.left, dst.top, dst.right, dst.bottom,
+             crop.left, crop.top, crop.right, crop.bottom);
+}
+
+void updateExtDisplayCoordinates(hwc_context_t *ctx, hwc_rect_t& crop,
+                           hwc_rect_t& dst, int dpy) {
+    updateCropAIVVideoMode(ctx, crop, dpy);
+    updateDestAIVVideoMode(ctx, crop, dst, dpy);
+}
+
 int configureNonSplit(hwc_context_t *ctx, hwc_layer_1_t *layer,
         const int& dpy, eMdpFlags& mdpFlags, eZorder& z,
         const eDest& dest, Rotator **rot) {
@@ -1847,7 +1918,10 @@
         else if (hnd->format == HAL_PIXEL_FORMAT_RGBX_8888)
             whf.format = getMdpFormat(HAL_PIXEL_FORMAT_BGRX_8888);
     }
-
+    // update source crop and destination position of AIV video layer.
+    if(ctx->mAIVVideoMode[dpy] && isYuvBuffer(hnd)) {
+        updateExtDisplayCoordinates(ctx, crop, dst, dpy);
+    }
     calcExtDisplayPosition(ctx, hnd, dpy, crop, dst, transform, orient);
     int downscale = getRotDownscale(ctx, layer);
     setMdpFlags(ctx, layer, mdpFlags, downscale, transform);
@@ -1942,7 +2016,12 @@
             whf.format = getMdpFormat(HAL_PIXEL_FORMAT_BGRX_8888);
     }
 
-    /* Calculate the external display position based on MDP scaling mode,
+    // update source crop and destination position of AIV video layer.
+    if(ctx->mAIVVideoMode[dpy] && isYuvBuffer(hnd)) {
+        updateExtDisplayCoordinates(ctx, crop, dst, dpy);
+    }
+
+    /* Calculate the external display position based on MDP downscale,
        ActionSafe, and extorientation features. */
     calcExtDisplayPosition(ctx, hnd, dpy, crop, dst, transform, orient);
     int downscale = getRotDownscale(ctx, layer);
@@ -2083,6 +2162,11 @@
     Whf whf(getWidth(hnd), getHeight(hnd),
             getMdpFormat(hnd->format), (uint32_t)hnd->size);
 
+    // update source crop and destination position of AIV video layer.
+    if(ctx->mAIVVideoMode[dpy] && isYuvBuffer(hnd)) {
+        updateExtDisplayCoordinates(ctx, crop, dst, dpy);
+    }
+
     /* Calculate the external display position based on MDP downscale,
        ActionSafe, and extorientation features. */
     calcExtDisplayPosition(ctx, hnd, dpy, crop, dst, transform, orient);
diff --git a/libhwcomposer/hwc_utils.h b/libhwcomposer/hwc_utils.h
index 3d97319..c443bf8 100644
--- a/libhwcomposer/hwc_utils.h
+++ b/libhwcomposer/hwc_utils.h
@@ -182,6 +182,12 @@
     HWC_COPYBIT = 0x00000002,
 };
 
+// AIV specific flags
+enum {
+    HWC_AIV_VIDEO = 0x80000000,
+    HWC_AIV_CC    = 0x40000000,
+};
+
 // HAL specific features
 enum {
     HWC_COLOR_FILL = 0x00000008,
@@ -372,6 +378,12 @@
 void updateSource(ovutils::eTransform& orient, ovutils::Whf& whf,
         hwc_rect_t& crop, overlay::Rotator *rot);
 
+bool isZoomModeEnabled(hwc_rect_t crop);
+void updateCropAIVVideoMode(hwc_context_t *ctx, hwc_rect_t& crop, int dpy);
+void updateDestAIVVideoMode(hwc_context_t *ctx, hwc_rect_t& dst, int dpy);
+void updateExtDisplayCoordinates(hwc_context_t *ctx, hwc_rect_t& crop,
+                           hwc_rect_t& dst, int dpy);
+
 //Routine to configure low resolution panels (<= 2048 width)
 int configureNonSplit(hwc_context_t *ctx, hwc_layer_1_t *layer, const int& dpy,
         ovutils::eMdpFlags& mdpFlags, ovutils::eZorder& z,
@@ -419,6 +431,14 @@
     return (UNLIKELY(l && (l->flags & HWC_SKIP_LAYER)));
 }
 
+static inline bool isAIVVideoLayer(const hwc_layer_1_t* l) {
+    return (UNLIKELY(l && (l->flags & HWC_AIV_VIDEO)));
+}
+
+static inline bool isAIVCCLayer(const hwc_layer_1_t* l) {
+    return (UNLIKELY(l && (l->flags & HWC_AIV_CC)));
+}
+
 // Returns true if the buffer is yuv
 static inline bool isYuvBuffer(const private_handle_t* hnd) {
     return (hnd && (hnd->bufferType == BUFFER_TYPE_VIDEO));
@@ -598,6 +618,9 @@
     bool mThermalBurstMode;
     //Layers out of ROI
     bool copybitDrop[MAX_NUM_APP_LAYERS];
+    // Flags related to windowboxing feature
+    bool mAIVVideoMode[HWC_NUM_DISPLAY_TYPES];
+    bool mWindowboxFeature;
 };
 
 namespace qhwc {