display: Add support for MDP Composition
This change
1) implements MDP Composition upto 3 layers
2) adds invalidator support to fall back to FB
composition during idle screen condition.
Change-Id: I55f27321fd0df096b353d66aaad1cc720cd4b84b
Acked-by: Jeykumar Sankaran <jsanka@codeaurora.org>
diff --git a/libhwcomposer/Android.mk b/libhwcomposer/Android.mk
index 9808aa5..05d033e 100644
--- a/libhwcomposer/Android.mk
+++ b/libhwcomposer/Android.mk
@@ -11,5 +11,6 @@
LOCAL_ADDITIONAL_DEPENDENCIES := $(common_deps)
LOCAL_SRC_FILES := hwc.cpp hwc_video.cpp hwc_utils.cpp \
hwc_uimirror.cpp hwc_external.cpp \
- hwc_uevents.cpp hwc_copybit.cpp
+ hwc_uevents.cpp hwc_copybit.cpp \
+ hwc_mdpcomp.cpp
include $(BUILD_SHARED_LIBRARY)
diff --git a/libhwcomposer/hwc.cpp b/libhwcomposer/hwc.cpp
index ba87f90..c28cf03 100644
--- a/libhwcomposer/hwc.cpp
+++ b/libhwcomposer/hwc.cpp
@@ -30,6 +30,7 @@
#include "hwc_uimirror.h"
#include "hwc_copybit.h"
#include "hwc_external.h"
+#include "hwc_mdpcomp.h"
using namespace qhwc;
@@ -85,6 +86,8 @@
//Nothing here
} else if(UIMirrorOverlay::prepare(ctx, list)) {
ctx->overlayInUse = true;
+ } else if(MDPComp::configure(dev, list)) {
+ ctx->overlayInUse = true;
} else if (0) {
//Other features
ctx->overlayInUse = true;
@@ -153,6 +156,7 @@
if (LIKELY(list)) {
VideoOverlay::draw(ctx, list);
CopyBit::draw(ctx, list, (EGLDisplay)dpy, (EGLSurface)sur);
+ MDPComp::draw(ctx, list);
EGLBoolean sucess = eglSwapBuffers((EGLDisplay)dpy, (EGLSurface)sur);
UIMirrorOverlay::draw(ctx);
if(ctx->mExtDisplay->getExternalDisplay())
diff --git a/libhwcomposer/hwc_mdpcomp.cpp b/libhwcomposer/hwc_mdpcomp.cpp
new file mode 100755
index 0000000..8107400
--- /dev/null
+++ b/libhwcomposer/hwc_mdpcomp.cpp
@@ -0,0 +1,829 @@
+/*
+ * Copyright (C) 2012, Code Aurora Forum. All rights reserved.
+ * Not a Contribution, Apache license notifications and license are retained
+ * for attribution purposes only.
+ *
+ * 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 "hwc_mdpcomp.h"
+#include "hwc_qbuf.h"
+#include "hwc_external.h"
+
+#define SUPPORT_4LAYER 0
+
+namespace qhwc {
+
+/****** Class PipeMgr ***********/
+
+void inline PipeMgr::reset() {
+ mVGPipes = MAX_VG;
+ mVGUsed = 0;
+ mVGIndex = 0;
+ mRGBPipes = MAX_RGB;
+ mRGBUsed = 0;
+ mRGBIndex = MAX_VG;
+ mTotalAvail = mVGPipes + mRGBPipes;
+ memset(&mStatus, 0x0 , sizeof(int)*mTotalAvail);
+}
+
+int PipeMgr::req_for_pipe(int pipe_req) {
+
+ switch(pipe_req) {
+ case PIPE_REQ_VG: //VG
+ if(mVGPipes){
+ mVGPipes--;
+ mVGUsed++;
+ mTotalAvail--;
+ return PIPE_REQ_VG;
+ }
+ case PIPE_REQ_RGB: // RGB
+ if(mRGBPipes) {
+ mRGBPipes--;
+ mRGBUsed++;
+ mTotalAvail--;
+ return PIPE_REQ_RGB;
+ }
+ return PIPE_NONE;
+ case PIPE_REQ_FB: //FB
+ if(mRGBPipes) {
+ mRGBPipes--;
+ mRGBUsed++;
+ mTotalAvail--;
+ mStatus[VAR_INDEX] = PIPE_IN_FB_MODE;
+ return PIPE_REQ_FB;
+ }
+ default:
+ break;
+ };
+ return PIPE_NONE;
+}
+
+int PipeMgr::assign_pipe(int pipe_pref) {
+ switch(pipe_pref) {
+ case PIPE_REQ_VG: //VG
+ if(mVGUsed) {
+ mVGUsed--;
+ mStatus[mVGIndex] = PIPE_IN_COMP_MODE;
+ return mVGIndex++;
+ }
+ case PIPE_REQ_RGB: //RGB
+ if(mRGBUsed) {
+ mRGBUsed--;
+ mStatus[mRGBIndex] = PIPE_IN_COMP_MODE;
+ return mRGBIndex++;
+ }
+ default:
+ ALOGE("%s: PipeMgr:invalid case in pipe_mgr_assign",
+ __FUNCTION__);
+ return -1;
+ };
+}
+
+/****** Class MDPComp ***********/
+
+MDPComp::State MDPComp::sMDPCompState = MDPCOMP_OFF;
+struct MDPComp::frame_info MDPComp::sCurrentFrame;
+PipeMgr MDPComp::sPipeMgr;
+IdleInvalidator *MDPComp::idleInvalidator = NULL;
+bool MDPComp::sIdleFallBack = false;
+bool MDPComp::sDebugLogs = false;
+int MDPComp::sSkipCount = 0;
+int MDPComp::sMaxLayers = 0;
+
+bool MDPComp::deinit() {
+ //XXX: Tear down MDP comp state
+ return true;
+}
+
+void MDPComp::timeout_handler(void *udata) {
+ struct hwc_context_t* ctx = (struct hwc_context_t*)(udata);
+
+ if(!ctx) {
+ ALOGE("%s: received empty data in timer callback", __FUNCTION__);
+ return;
+ }
+
+ hwc_procs* proc = (hwc_procs*)ctx->device.reserved_proc[0];
+
+ if(!proc) {
+ ALOGE("%s: HWC proc not registered", __FUNCTION__);
+ return;
+ }
+ sIdleFallBack = true;
+ /* Trigger SF to redraw the current frame */
+ proc->invalidate(proc);
+}
+
+void MDPComp::reset( hwc_context_t *ctx, hwc_layer_list_t* list ) {
+ sCurrentFrame.count = 0;
+ free(sCurrentFrame.pipe_layer);
+ sCurrentFrame.pipe_layer = NULL;
+
+ //Reset MDP pipes
+ sPipeMgr.reset();
+ sPipeMgr.setStatus(VAR_INDEX, PIPE_IN_FB_MODE);
+
+#if SUPPORT_4LAYER
+ configure_var_pipe(ctx);
+#endif
+
+ //Reset flags and states
+ unsetMDPCompLayerFlags(ctx, list);
+ if(sMDPCompState == MDPCOMP_ON) {
+ sMDPCompState = MDPCOMP_OFF_PENDING;
+ }
+}
+
+void MDPComp::setLayerIndex(hwc_layer_t* layer, const int pipe_index)
+{
+ layer->flags &= ~HWC_MDPCOMP_INDEX_MASK;
+ layer->flags |= pipe_index << MDPCOMP_INDEX_OFFSET;
+}
+
+int MDPComp::getLayerIndex(hwc_layer_t* layer)
+{
+ int byp_index = -1;
+
+ if(layer->flags & HWC_MDPCOMP) {
+ byp_index = ((layer->flags & HWC_MDPCOMP_INDEX_MASK) >>
+ MDPCOMP_INDEX_OFFSET);
+ byp_index = (byp_index < sMaxLayers ? byp_index : -1 );
+ }
+ return byp_index;
+}
+void MDPComp::print_info(hwc_layer_t* layer)
+{
+ hwc_rect_t sourceCrop = layer->sourceCrop;
+ hwc_rect_t displayFrame = layer->displayFrame;
+
+ int s_l = sourceCrop.left;
+ int s_t = sourceCrop.top;
+ int s_r = sourceCrop.right;
+ int s_b = sourceCrop.bottom;
+
+ int d_l = displayFrame.left;
+ int d_t = displayFrame.top;
+ int d_r = displayFrame.right;
+ int d_b = displayFrame.bottom;
+
+ ALOGD_IF(isDebug(), "src:[%d,%d,%d,%d] (%d x %d) \
+ dst:[%d,%d,%d,%d] (%d x %d)",
+ s_l, s_t, s_r, s_b, (s_r - s_l), (s_b - s_t),
+ d_l, d_t, d_r, d_b, (d_r - d_l), (d_b - d_t));
+}
+/*
+ * Configures pipe(s) for MDP composition
+ */
+int MDPComp::prepare(hwc_context_t *ctx, hwc_layer_t *layer,
+ mdp_pipe_info& mdp_info) {
+
+ int nPipeIndex = mdp_info.index;
+
+ if (ctx) {
+
+ private_handle_t *hnd = (private_handle_t *)layer->handle;
+
+ overlay::Overlay& ov = *(ctx->mOverlay);
+
+ if(!hnd) {
+ ALOGE("%s: layer handle is NULL", __FUNCTION__);
+ return -1;
+ }
+
+
+ int hw_w = ctx->mFbDev->width;
+ int hw_h = ctx->mFbDev->height;
+
+
+ hwc_rect_t sourceCrop = layer->sourceCrop;
+ hwc_rect_t displayFrame = layer->displayFrame;
+
+ const int src_w = sourceCrop.right - sourceCrop.left;
+ const int src_h = sourceCrop.bottom - sourceCrop.top;
+
+ hwc_rect_t crop = sourceCrop;
+ int crop_w = crop.right - crop.left;
+ int crop_h = crop.bottom - crop.top;
+
+ hwc_rect_t dst = displayFrame;
+ int dst_w = dst.right - dst.left;
+ int dst_h = dst.bottom - dst.top;
+
+ //REDUNDANT ??
+ if(hnd != NULL &&
+ (hnd->flags & private_handle_t::PRIV_FLAGS_NONCONTIGUOUS_MEM )) {
+ ALOGE("%s: failed due to non-pmem memory",__FUNCTION__);
+ return -1;
+ }
+
+ if(dst.left < 0 || dst.top < 0 ||
+ dst.right > hw_w || dst.bottom > hw_h) {
+ ALOGD_IF(isDebug(),"%s: Destination has negative coordinates",
+ __FUNCTION__);
+
+ qhwc::calculate_crop_rects(crop, dst, hw_w, hw_h);
+
+ //Update calulated width and height
+ crop_w = crop.right - crop.left;
+ crop_h = crop.bottom - crop.top;
+
+ dst_w = dst.right - dst.left;
+ dst_h = dst.bottom - dst.top;
+ }
+
+ if( (dst_w > hw_w)|| (dst_h > hw_h)) {
+ ALOGD_IF(isDebug(),"%s: Dest rect exceeds FB", __FUNCTION__);
+ print_info(layer);
+ dst_w = hw_w;
+ dst_h = hw_h;
+ }
+
+ // Determine pipe to set based on pipe index
+ ovutils::eDest dest = ovutils::OV_PIPE_ALL;
+ if (nPipeIndex == 0) {
+ dest = ovutils::OV_PIPE0;
+ } else if (nPipeIndex == 1) {
+ dest = ovutils::OV_PIPE1;
+ } else if (nPipeIndex == 2) {
+ dest = ovutils::OV_PIPE2;
+ }
+
+ ovutils::eZorder zOrder = ovutils::ZORDER_0;
+
+ if(mdp_info.z_order == 0 ) {
+ zOrder = ovutils::ZORDER_0;
+ } else if(mdp_info.z_order == 1 ) {
+ zOrder = ovutils::ZORDER_1;
+ } else if(mdp_info.z_order == 2 ) {
+ zOrder = ovutils::ZORDER_2;
+ }
+
+ // Order order order
+ // setSource - just setting source
+ // setParameter - changes src w/h/f accordingly
+ // setCrop - ROI - src_rect
+ // setPosition - dst_rect
+ // commit - commit changes to mdp driver
+ // queueBuffer - not here, happens when draw is called
+
+ ovutils::eTransform orient =
+ static_cast<ovutils::eTransform>(layer->transform);
+
+ ov.setTransform(orient, dest);
+ ovutils::Whf info(hnd->width, hnd->height, hnd->format, hnd->size);
+ ovutils::eMdpFlags mdpFlags = mdp_info.isVG ? ovutils::OV_MDP_PIPE_SHARE
+ : ovutils::OV_MDP_FLAGS_NONE;
+ ovutils::eIsFg isFG = mdp_info.isFG ? ovutils::IS_FG_SET
+ : ovutils::IS_FG_OFF;
+ ovutils::PipeArgs parg(mdpFlags,
+ info,
+ zOrder,
+ isFG,
+ ovutils::ROT_FLAG_DISABLED);
+
+ ovutils::PipeArgs pargs[MAX_PIPES] = { parg, parg, parg };
+ if (!ov.setSource(pargs, dest)) {
+ ALOGE("%s: setSource failed", __FUNCTION__);
+ return -1;
+ }
+
+ ovutils::Dim dcrop(crop.left, crop.top, crop_w, crop_h);
+ if (!ov.setCrop(dcrop, dest)) {
+ ALOGE("%s: setCrop failed", __FUNCTION__);
+ return -1;
+ }
+
+ ovutils::Dim dim(dst.left, dst.top, dst_w, dst_h);
+ if (!ov.setPosition(dim, dest)) {
+ ALOGE("%s: setPosition failed", __FUNCTION__);
+ return -1;
+ }
+
+ ALOGD_IF(isDebug(),"%s: MDP set: crop[%d,%d,%d,%d] dst[%d,%d,%d,%d] \
+ nPipe: %d isFG: %d zorder: %d",__FUNCTION__, dcrop.x,
+ dcrop.y,dcrop.w, dcrop.h, dim.x, dim.y, dim.w, dim.h,
+ nPipeIndex,mdp_info.isFG, mdp_info.z_order);
+
+ if (!ov.commit(dest)) {
+ ALOGE("%s: commit failed", __FUNCTION__);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * MDPComp not possible when
+ * 1. We have more than sMaxLayers
+ * 2. External display connected
+ * 3. Composition is triggered by
+ * Idle timer expiry
+ * 4. Rotation is needed
+ * 5. Overlay in use
+ */
+
+bool MDPComp::is_doable(hwc_composer_device_t *dev,
+ const hwc_layer_list_t* list) {
+ hwc_context_t* ctx = (hwc_context_t*)(dev);
+
+ if(!ctx) {
+ ALOGE("%s: hwc context is NULL", __FUNCTION__);
+ return false;
+ }
+
+ //Number of layers
+ if(list->numHwLayers < 1 || list->numHwLayers > sMaxLayers) {
+ ALOGD_IF(isDebug(), "%s: Unsupported number of layers",__FUNCTION__);
+ return false;
+ }
+
+ //Disable MDPComp when ext display connected
+ if(ctx->mExtDisplay->getExternalDisplay()) {
+ ALOGD_IF(isDebug(), "%s: External display connected.", __FUNCTION__);
+ }
+
+ //FB composition on idle timeout
+ if(sIdleFallBack) {
+ ALOGD_IF(isDebug(), "%s: idle fallback",__FUNCTION__);
+ return false;
+ }
+
+ //MDP composition is not efficient if rotation is needed.
+ for(unsigned int i = 0; i < list->numHwLayers; ++i) {
+ if(list->hwLayers[i].transform) {
+ ALOGD_IF(isDebug(), "%s: orientation involved",__FUNCTION__);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void MDPComp::setMDPCompLayerFlags(hwc_layer_list_t* list) {
+
+ for(int index = 0 ; index < sCurrentFrame.count; index++ )
+ {
+ int layer_index = sCurrentFrame.pipe_layer[index].layer_index;
+ if(layer_index >= 0) {
+ hwc_layer_t* layer = &(list->hwLayers[layer_index]);
+
+ layer->flags |= HWC_MDPCOMP;
+ layer->compositionType = HWC_OVERLAY;
+ layer->hints |= HWC_HINT_CLEAR_FB;
+ }
+ }
+}
+
+void MDPComp::get_layer_info(hwc_layer_t* layer, int& flags) {
+
+ private_handle_t* hnd = (private_handle_t*)layer->handle;
+
+ if(layer->flags & HWC_SKIP_LAYER) {
+ flags |= MDPCOMP_LAYER_SKIP;
+ } else if(hnd != NULL &&
+ (hnd->flags & private_handle_t::PRIV_FLAGS_NONCONTIGUOUS_MEM )) {
+ flags |= MDPCOMP_LAYER_UNSUPPORTED_MEM;
+ }
+
+ if(layer->blending != HWC_BLENDING_NONE)
+ flags |= MDPCOMP_LAYER_BLEND;
+
+ int dst_w, dst_h;
+ getLayerResolution(layer, dst_w, dst_h);
+
+ hwc_rect_t sourceCrop = layer->sourceCrop;
+ const int src_w = sourceCrop.right - sourceCrop.left;
+ const int src_h = sourceCrop.bottom - sourceCrop.top;
+ if(((src_w > dst_w) || (src_h > dst_h))) {
+ flags |= MDPCOMP_LAYER_DOWNSCALE;
+ }
+}
+
+int MDPComp::mark_layers(hwc_layer_list_t* list, layer_mdp_info* layer_info,
+ frame_info& current_frame) {
+
+ int layer_count = list->numHwLayers;
+
+ if(layer_count > sMaxLayers) {
+ if(!sPipeMgr.req_for_pipe(PIPE_REQ_FB)) {
+ ALOGE("%s: binding var pipe to FB failed!!", __FUNCTION__);
+ return 0;
+ }
+ }
+
+ //Parse layers from higher z-order
+ for(int index = layer_count - 1 ; index >= 0; index-- ) {
+ hwc_layer_t* layer = &list->hwLayers[index];
+
+ int layer_prop = 0;
+ get_layer_info(layer, layer_prop);
+
+ ALOGD_IF(isDebug(),"%s: prop for layer [%d]: %x", __FUNCTION__,
+ index, layer_prop);
+
+ //Both in cases of NON-CONTIGUOUS memory or SKIP layer,
+ //current version of mdp composition falls back completely to FB
+ //composition.
+ //TO DO: Support dual mode composition
+
+ if(layer_prop & MDPCOMP_LAYER_UNSUPPORTED_MEM) {
+ ALOGD_IF(isDebug(), "%s: Non contigous memory",__FUNCTION__);
+ return MDPCOMP_ABORT;
+ }
+
+ if(layer_prop & MDPCOMP_LAYER_SKIP) {
+ ALOGD_IF(isDebug(), "%s:skip layer",__FUNCTION__);
+ return MDPCOMP_ABORT;
+ }
+
+ //Request for MDP pipes
+ int pipe_pref = PIPE_REQ_VG;
+
+ if((layer_prop & MDPCOMP_LAYER_DOWNSCALE) &&
+ (layer_prop & MDPCOMP_LAYER_BLEND)) {
+ pipe_pref = PIPE_REQ_RGB;
+ }
+
+ int allocated_pipe = sPipeMgr.req_for_pipe( pipe_pref);
+ if(allocated_pipe) {
+ layer_info[index].can_use_mdp = true;
+ layer_info[index].pipe_pref = allocated_pipe;
+ current_frame.count++;
+ }else {
+ ALOGE("%s: pipe marking in mark layer fails for : %d",
+ __FUNCTION__, allocated_pipe);
+ return MDPCOMP_FAILURE;
+ }
+ }
+ return MDPCOMP_SUCCESS;
+}
+
+void MDPComp::reset_layer_mdp_info(layer_mdp_info* layer_info, int count) {
+ for(int i = 0 ; i < count; i++ ) {
+ layer_info[i].can_use_mdp = false;
+ layer_info[i].pipe_pref = PIPE_NONE;
+ }
+}
+
+bool MDPComp::alloc_layer_pipes(hwc_layer_list_t* list,
+ layer_mdp_info* layer_info, frame_info& current_frame) {
+
+ int layer_count = list->numHwLayers;
+ int mdp_count = current_frame.count;
+ int fallback_count = layer_count - mdp_count;
+ int frame_pipe_count = 0;
+
+ ALOGD_IF(isDebug(), "%s: dual mode: %d total count: %d \
+ mdp count: %d fallback count: %d",
+ __FUNCTION__, (layer_count != mdp_count),
+ layer_count, mdp_count, fallback_count);
+
+ for(int index = 0 ; index < layer_count ; index++ ) {
+ hwc_layer_t* layer = &list->hwLayers[index];
+
+ if(layer_info[index].can_use_mdp) {
+ pipe_layer_pair& info = current_frame.pipe_layer[frame_pipe_count];
+ mdp_pipe_info& pipe_info = info.pipe_index;
+
+ pipe_info.index = sPipeMgr.assign_pipe(layer_info[index].pipe_pref);
+ pipe_info.isVG = (layer_info[index].pipe_pref == PIPE_REQ_VG);
+ pipe_info.isFG = (frame_pipe_count == 0);
+ /* if VAR pipe is attached to FB, FB will be updated with
+ VSYNC WAIT flag, so no need to set VSYNC WAIT for any
+ bypass pipes. if not, set VSYNC WAIT to the last updating pipe*/
+ pipe_info.vsync_wait =
+ (sPipeMgr.getStatus(VAR_INDEX) == PIPE_IN_FB_MODE) ? false:
+ (frame_pipe_count == (mdp_count - 1));
+ /* All the layers composed on FB will have MDP zorder 0, so start
+ assigning from 1*/
+ pipe_info.z_order = index -
+ (fallback_count ? fallback_count - 1 : fallback_count);
+
+ info.layer_index = index;
+ frame_pipe_count++;
+ }
+ }
+ return 1;
+}
+
+//returns array of layers and their allocated pipes
+bool MDPComp::parse_and_allocate(hwc_context_t* ctx, hwc_layer_list_t* list,
+ frame_info& current_frame ) {
+
+ int layer_count = list->numHwLayers;
+
+ /* clear pipe status */
+ sPipeMgr.reset();
+
+ layer_mdp_info* bp_layer_info = (layer_mdp_info*)
+ malloc(sizeof(layer_mdp_info)* layer_count);
+
+ reset_layer_mdp_info(bp_layer_info, layer_count);
+
+ /* iterate through layer list to mark candidate */
+ if(mark_layers(list, bp_layer_info, current_frame) == MDPCOMP_ABORT) {
+ free(bp_layer_info);
+ current_frame.count = 0;
+ ALOGE_IF(isDebug(), "%s:mark_layers failed!!", __FUNCTION__);
+ return false;
+ }
+ current_frame.pipe_layer = (pipe_layer_pair*)
+ malloc(sizeof(pipe_layer_pair) * current_frame.count);
+
+ /* allocate MDP pipes for marked layers */
+ alloc_layer_pipes( list, bp_layer_info, current_frame);
+
+ free(bp_layer_info);
+ return true;
+}
+#if SUPPORT_4LAYER
+int MDPComp::configure_var_pipe(hwc_context_t* ctx) {
+
+ if(!ctx) {
+ ALOGE("%s: invalid context", __FUNCTION__);
+ return -1;
+ }
+
+ framebuffer_device_t *fbDev = ctx->fbDev;
+ if (!fbDev) {
+ ALOGE("%s: fbDev is NULL", __FUNCTION__);
+ return -1;
+ }
+
+ int new_mode = -1, cur_mode;
+ fbDev->perform(fbDev,EVENT_GET_VAR_PIPE_MODE, (void*)&cur_mode);
+
+ if(sPipeMgr.getStatus(VAR_INDEX) == PIPE_IN_FB_MODE) {
+ new_mode = VAR_PIPE_FB_ATTACH;
+ } else if(sPipeMgr.getStatus(VAR_INDEX) == PIPE_IN_BYP_MODE) {
+ new_mode = VAR_PIPE_FB_DETACH;
+ fbDev->perform(fbDev,EVENT_WAIT_POSTBUFFER,NULL);
+ }
+
+ ALOGD_IF(isDebug(),"%s: old_mode: %d new_mode: %d", __FUNCTION__,
+ cur_mode, new_mode);
+
+ if((new_mode != cur_mode) && (new_mode >= 0)) {
+ if(fbDev->perform(fbDev,EVENT_SET_VAR_PIPE_MODE,(void*)&new_mode) < 0) {
+ ALOGE("%s: Setting var pipe mode failed", __FUNCTION__);
+ }
+ }
+
+ return 0;
+}
+#endif
+
+bool MDPComp::setup(hwc_context_t* ctx, hwc_layer_list_t* list) {
+ int nPipeIndex, vsync_wait, isFG;
+ int numHwLayers = list->numHwLayers;
+
+ frame_info ¤t_frame = sCurrentFrame;
+ current_frame.count = 0;
+
+ if(!ctx) {
+ ALOGE("%s: invalid context", __FUNCTION__);
+ return -1;
+ }
+
+ framebuffer_device_t *fbDev = ctx->mFbDev;
+ if (!fbDev) {
+ ALOGE("%s: fbDev is NULL", __FUNCTION__);
+ return -1;
+ }
+
+ if(!parse_and_allocate(ctx, list, current_frame)) {
+#if SUPPORT_4LAYER
+ int mode = VAR_PIPE_FB_ATTACH;
+ if(fbDev->perform(fbDev,EVENT_SET_VAR_PIPE_MODE,(void*)&mode) < 0 ) {
+ ALOGE("%s: setting var pipe mode failed", __FUNCTION__);
+ }
+#endif
+ ALOGD_IF(isDebug(), "%s: Falling back to FB", __FUNCTION__);
+ return false;
+ }
+#if SUPPORT_4LAYER
+ configure_var_pipe(ctx);
+#endif
+
+ overlay::Overlay& ov = *(ctx->mOverlay);
+ ovutils::eOverlayState state = ov.getState();
+
+ if (current_frame.count == 1) {
+ state = ovutils::OV_BYPASS_1_LAYER;
+ } else if (current_frame.count == 2) {
+ state = ovutils::OV_BYPASS_2_LAYER;
+ } else if (current_frame.count == 3) {
+ state = ovutils::OV_BYPASS_3_LAYER;
+ }
+
+ ov.setState(state);
+
+
+ for (int index = 0 ; index < current_frame.count; index++) {
+ int layer_index = current_frame.pipe_layer[index].layer_index;
+ hwc_layer_t* layer = &list->hwLayers[layer_index];
+ mdp_pipe_info& cur_pipe = current_frame.pipe_layer[index].pipe_index;
+
+ if( prepare(ctx, layer, cur_pipe) != 0 ) {
+ ALOGD_IF(isDebug(), "%s: MDPComp failed to configure overlay for \
+ layer %d with pipe index:%d",__FUNCTION__,
+ index, cur_pipe.index);
+ return false;
+ } else {
+ setLayerIndex(layer, index);
+ }
+ }
+ return true;
+}
+
+void MDPComp::unsetMDPCompLayerFlags(hwc_context_t* ctx, hwc_layer_list_t* list)
+{
+ if (!list)
+ return;
+
+ for (int index = 0 ; index < sCurrentFrame.count; index++) {
+ int l_index = sCurrentFrame.pipe_layer[index].layer_index;
+ if(list->hwLayers[l_index].flags & HWC_MDPCOMP) {
+ list->hwLayers[l_index].flags &= ~HWC_MDPCOMP;
+ }
+ }
+}
+
+int MDPComp::draw(hwc_context_t *ctx, hwc_layer_list_t* list) {
+
+ if(!isEnabled()) {
+ ALOGD_IF(isDebug(),"%s: MDP Comp. not enabled",__FUNCTION__);
+ return 0;
+ }
+
+ if(!ctx || !list) {
+ ALOGE("%s: invalid contxt or list",__FUNCTION__);
+ return -1;
+ }
+
+ overlay::Overlay& ov = *(ctx->mOverlay);
+
+ for(unsigned int i = 0; i < list->numHwLayers; i++ )
+ {
+ hwc_layer_t *layer = &list->hwLayers[i];
+
+ if(!(layer->flags & HWC_MDPCOMP)) {
+ ALOGD_IF(isDebug(), "%s: Layer Not flagged for MDP comp",
+ __FUNCTION__);
+ continue;
+ }
+
+ int data_index = getLayerIndex(layer);
+ mdp_pipe_info& pipe_info =
+ sCurrentFrame.pipe_layer[data_index].pipe_index;
+ int index = pipe_info.index;
+
+ if(index < 0) {
+ ALOGE("%s: Invalid pipe index (%d)", __FUNCTION__, index);
+ return -1;
+ }
+
+ /* reset Invalidator */
+ if(idleInvalidator)
+ idleInvalidator->markForSleep();
+
+ ovutils::eDest dest;
+
+ if (index == 0) {
+ dest = ovutils::OV_PIPE0;
+ } else if (index == 1) {
+ dest = ovutils::OV_PIPE1;
+ } else if (index == 2) {
+ dest = ovutils::OV_PIPE2;
+ }
+
+ if (ctx ) {
+ private_handle_t *hnd = (private_handle_t *)layer->handle;
+ if(!hnd) {
+ ALOGE("%s handle null", __FUNCTION__);
+ return -1;
+ }
+
+ //lock buffer before queue
+ //XXX: Handle lock failure
+ ctx->qbuf->lockAndAdd(hnd);
+
+ ALOGD_IF(isDebug(),"%s: MDP Comp: Drawing layer: %p hnd: %p \
+ using pipe: %d", __FUNCTION__, layer,
+ hnd, index );
+
+ if (!ov.queueBuffer(hnd->fd, hnd->offset, dest)) {
+ ALOGE("%s: queueBuffer failed for external", __FUNCTION__);
+ return -1;
+ }
+ }
+ layer->flags &= ~HWC_MDPCOMP;
+ layer->flags |= HWC_MDPCOMP_INDEX_MASK;
+ }
+ return 0;
+}
+
+bool MDPComp::init(hwc_context_t *dev) {
+
+ if(!dev) {
+ ALOGE("%s: Invalid hwc context!!",__FUNCTION__);
+ return false;
+ }
+
+#if SUPPORT_4LAYER
+ if(MAX_MDPCOMP_LAYERS > MAX_STATIC_PIPES) {
+ framebuffer_device_t *fbDev = dev->fbDevice;
+ if(fbDev == NULL) {
+ ALOGE("%s: FATAL: framebuffer device is NULL", __FUNCTION__);
+ return false;
+ }
+
+ //Receive VAR pipe object from framebuffer
+ if(fbDev->perform(fbDev,EVENT_GET_VAR_PIPE,(void*)&ov) < 0) {
+ ALOGE("%s: FATAL: getVariablePipe failed!!", __FUNCTION__);
+ return false;
+ }
+
+ sPipeMgr.setStatus(VAR_INDEX, PIPE_IN_FB_MODE);
+ }
+#endif
+ char property[PROPERTY_VALUE_MAX];
+
+ sMaxLayers = 0;
+ if(property_get("debug.mdpcomp.maxlayer", property, NULL) > 0) {
+ if(atoi(property) != 0)
+ sMaxLayers = atoi(property);
+ }
+
+ sDebugLogs = false;
+ if(property_get("debug.mdpcomp.logs", property, NULL) > 0) {
+ if(atoi(property) != 0)
+ sDebugLogs = true;
+ }
+
+ unsigned long idle_timeout = DEFAULT_IDLE_TIME;
+ if(property_get("debug.mdpcomp.idletime", property, NULL) > 0) {
+ if(atoi(property) != 0)
+ idle_timeout = atoi(property);
+ }
+
+ //create Idle Invalidator
+ idleInvalidator = IdleInvalidator::getInstance();
+
+ if(idleInvalidator == NULL) {
+ ALOGE("%s: failed to instantiate idleInvalidator object", __FUNCTION__);
+ } else {
+ idleInvalidator->init(timeout_handler, dev, idle_timeout);
+ }
+ return true;
+}
+
+bool MDPComp::configure(hwc_composer_device_t *dev, hwc_layer_list_t* list) {
+
+ if(!isEnabled()) {
+ ALOGD_IF(isDebug(),"%s: MDP Comp. not enabled.", __FUNCTION__);
+ return false;
+ }
+
+ hwc_context_t* ctx = (hwc_context_t*)(dev);
+
+ bool isMDPCompUsed = true;
+ bool doable = is_doable(dev, list);
+
+ if(doable) {
+ if(setup(ctx, list)) {
+ setMDPCompLayerFlags(list);
+ sMDPCompState = MDPCOMP_ON;
+ } else {
+ ALOGD_IF(isDebug(),"%s: MDP Comp Failed",__FUNCTION__);
+ isMDPCompUsed = false;
+ }
+ } else {
+ ALOGD_IF( isDebug(),"%s: MDP Comp not possible[%d]",__FUNCTION__,
+ doable);
+ isMDPCompUsed = false;
+ }
+
+ //Reset states
+ if(!isMDPCompUsed) {
+ //Reset current frame
+ reset(ctx, list);
+ }
+
+ sIdleFallBack = false;
+
+ return isMDPCompUsed;
+}
+}; //namespace
+
diff --git a/libhwcomposer/hwc_mdpcomp.h b/libhwcomposer/hwc_mdpcomp.h
new file mode 100755
index 0000000..199204c
--- /dev/null
+++ b/libhwcomposer/hwc_mdpcomp.h
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2012, Code Aurora Forum. All rights reserved.
+ * Not a Contribution, Apache license notifications and license are retained
+ * for attribution purposes only.
+ *
+ * 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.
+ */
+
+#ifndef HWC_MDP_COMP
+#define HWC_MDP_COMP
+
+#include <hwc_utils.h>
+#include <idle_invalidator.h>
+#include <cutils/properties.h>
+#include <overlay.h>
+
+#define MAX_STATIC_PIPES 3
+#define MDPCOMP_INDEX_OFFSET 4
+#define DEFAULT_IDLE_TIME 2000
+
+#define MAX_VG 2
+#define MAX_RGB 2
+#define VAR_INDEX 3
+#define MAX_PIPES (MAX_VG + MAX_RGB)
+#define HWC_MDPCOMP_INDEX_MASK 0x00000030
+
+
+//struct hwc_context_t;
+
+namespace qhwc {
+
+// pipe status
+enum {
+ PIPE_UNASSIGNED = 0,
+ PIPE_IN_FB_MODE,
+ PIPE_IN_COMP_MODE,
+};
+
+// pipe request
+enum {
+ PIPE_NONE = 0,
+ PIPE_REQ_VG,
+ PIPE_REQ_RGB,
+ PIPE_REQ_FB,
+};
+
+// MDP Comp Status
+enum {
+ MDPCOMP_SUCCESS = 0,
+ MDPCOMP_FAILURE,
+ MDPCOMP_ABORT,
+};
+
+//This class manages the status of 4 MDP pipes and keeps
+//track of Variable pipe mode.
+class PipeMgr {
+
+public:
+ PipeMgr() { reset();}
+ //reset pipemgr params
+ void reset();
+
+ //Based on the preference received, pipe mgr
+ //allocates the best available pipe to handle
+ //the case
+ int req_for_pipe(int pipe_req);
+
+ //Allocate requested pipe and update availablity
+ int assign_pipe(int pipe_pref);
+
+ // Get/Set pipe status
+ void setStatus(int pipe_index, int pipe_status) {
+ mStatus[pipe_index] = pipe_status;
+ }
+ int getStatus(int pipe_index) {
+ return mStatus[pipe_index];
+ }
+private:
+ int mVGPipes;
+ int mVGUsed;
+ int mVGIndex;
+ int mRGBPipes;
+ int mRGBUsed;
+ int mRGBIndex;
+ int mTotalAvail;
+ int mStatus[MAX_PIPES];
+};
+
+
+class MDPComp {
+ enum State {
+ MDPCOMP_ON = 0,
+ MDPCOMP_OFF,
+ MDPCOMP_OFF_PENDING,
+ };
+
+ enum {
+ MDPCOMP_LAYER_BLEND = 1,
+ MDPCOMP_LAYER_DOWNSCALE = 2,
+ MDPCOMP_LAYER_SKIP = 4,
+ MDPCOMP_LAYER_UNSUPPORTED_MEM = 8,
+ };
+
+ struct mdp_pipe_info {
+ int index;
+ int z_order;
+ bool isVG;
+ bool isFG;
+ bool vsync_wait;
+ };
+
+ struct pipe_layer_pair {
+ int layer_index;
+ mdp_pipe_info pipe_index;
+ native_handle_t* handle;
+ };
+
+ struct frame_info {
+ int count;
+ struct pipe_layer_pair* pipe_layer;
+
+ };
+
+ struct layer_mdp_info {
+ bool can_use_mdp;
+ int pipe_pref;
+ };
+
+ static State sMDPCompState;
+ static IdleInvalidator *idleInvalidator;
+ static struct frame_info sCurrentFrame;
+ static PipeMgr sPipeMgr;
+ static int sSkipCount;
+ static int sMaxLayers;
+ static bool sDebugLogs;
+ static bool sIdleFallBack;
+
+public:
+ /* Handler to invoke frame redraw on Idle Timer expiry */
+ static void timeout_handler(void *udata);
+
+ /* configure/tear-down MDPComp params*/
+ static bool init(hwc_context_t *ctx);
+ static bool deinit();
+
+ /*sets up mdp comp for the current frame */
+ static bool configure(hwc_composer_device_t *ctx, hwc_layer_list_t* list);
+
+ /* draw */
+ static int draw(hwc_context_t *ctx, hwc_layer_list_t *list);
+
+ /* store frame stats */
+ static void setStats(int skipCt) { sSkipCount = skipCt;};
+
+private:
+
+ /* get/set pipe index associated with overlay layers */
+ static void setLayerIndex(hwc_layer_t* layer, const int pipe_index);
+ static int getLayerIndex(hwc_layer_t* layer);
+
+ /* set/reset flags for MDPComp */
+ static void setMDPCompLayerFlags(hwc_layer_list_t* list);
+ static void unsetMDPCompLayerFlags(hwc_context_t* ctx,
+ hwc_layer_list_t* list);
+
+ static void print_info(hwc_layer_t* layer);
+
+ /* configure's overlay pipes for the frame */
+ static int prepare(hwc_context_t *ctx, hwc_layer_t *layer,
+ mdp_pipe_info& mdp_info);
+
+ /* checks for conditions where mdpcomp is not possible */
+ static bool is_doable(hwc_composer_device_t *dev,
+ const hwc_layer_list_t* list);
+
+ static bool setup(hwc_context_t* ctx, hwc_layer_list_t* list);
+
+ /* parses layer for properties affecting mdp comp */
+ static void get_layer_info(hwc_layer_t* layer, int& flags);
+
+ /* iterates through layer list to choose candidate to use overlay */
+ static int mark_layers(hwc_layer_list_t* list, layer_mdp_info* layer_info,
+ frame_info& current_frame);
+ static bool parse_and_allocate(hwc_context_t* ctx, hwc_layer_list_t* list,
+ frame_info& current_frame );
+
+ /* clears layer info struct */
+ static void reset_layer_mdp_info(layer_mdp_info* layer_mdp_info,int count);
+
+ /* allocates pipes to selected candidates */
+ static bool alloc_layer_pipes(hwc_layer_list_t* list,
+ layer_mdp_info* layer_info,
+ frame_info& current_frame);
+ /* updates variable pipe mode for the current frame */
+ static int configure_var_pipe(hwc_context_t* ctx);
+
+ /* get/set states */
+ static State get_state() { return sMDPCompState; };
+ static void set_state(State state) { sMDPCompState = state; };
+
+ /* reset state */
+ static void reset( hwc_context_t *ctx, hwc_layer_list_t* list );
+
+ /* Is feature enabled */
+ static bool isEnabled() { return sMaxLayers ? true : false; };
+ /* Is debug enabled */
+ static bool isDebug() { return sDebugLogs ? true : false; };
+};
+}; //namespace
+#endif
diff --git a/libhwcomposer/hwc_utils.cpp b/libhwcomposer/hwc_utils.cpp
index 870a758..0b62a98 100644
--- a/libhwcomposer/hwc_utils.cpp
+++ b/libhwcomposer/hwc_utils.cpp
@@ -22,6 +22,7 @@
#include "hwc_qbuf.h"
#include "hwc_copybit.h"
#include "hwc_external.h"
+#include "hwc_mdpcomp.h"
namespace qhwc {
@@ -43,6 +44,7 @@
ctx->hasOverlay = qdutils::MDPVersion::getInstance().hasOverlay();
ctx->mCopybitEngine = CopybitEngine::getInstance();
ctx->mExtDisplay = new ExternalDisplay(ctx);
+ MDPComp::init(ctx);
init_uevent_thread(ctx);
@@ -103,6 +105,7 @@
int yuvCount = 0;
int yuvLayerIndex = -1;
bool isYuvLayerSkip = false;
+ int skipCount = 0;
for (size_t i = 0; i < list->numHwLayers; i++) {
private_handle_t *hnd =
@@ -120,11 +123,13 @@
if(yuvLayerIndex != -1 && yuvLayerIndex < (ssize_t)i) {
isYuvLayerSkip = true;
}
+ skipCount++;
}
}
VideoOverlay::setStats(yuvCount, yuvLayerIndex, isYuvLayerSkip);
CopyBit::setStats(yuvCount, yuvLayerIndex, isYuvLayerSkip);
+ MDPComp::setStats(skipCount);
ctx->numHwLayers = list->numHwLayers;
return;
diff --git a/libhwcomposer/hwc_utils.h b/libhwcomposer/hwc_utils.h
index 1e405f7..a6c1446 100644
--- a/libhwcomposer/hwc_utils.h
+++ b/libhwcomposer/hwc_utils.h
@@ -50,6 +50,12 @@
HWC_USE_COPYBIT // This layer is to be handled by copybit
};
+enum {
+ HWC_MDPCOMP = 0x00000002,
+ HWC_LAYER_RESERVED_0 = 0x00000004,
+ HWC_LAYER_RESERVED_1 = 0x00000008
+};
+
class ExternalDisplay;
class CopybitEngine;
@@ -81,6 +87,13 @@
// Initialize uevent thread
void init_uevent_thread(hwc_context_t* ctx);
+inline void getLayerResolution(const hwc_layer_t* layer,
+ int& width, int& height)
+{
+ hwc_rect_t displayFrame = layer->displayFrame;
+ width = displayFrame.right - displayFrame.left;
+ height = displayFrame.bottom - displayFrame.top;
+}
}; //qhwc namespace
// -----------------------------------------------------------------------------