display: Add support for HW Cursor
- Retrieve cursor pipe info from driver
- Configure the layer marked with flag HWC_IS_CURSOR_LAYER to the
HWCursor using the fb_cursor ioctl.
- The config happens only when it satisfies the hw limitions of
cursor
- HWCursor is supported on primary display
- Since cursor configuration happens first, make use of drop
layer/count to handle other composition strategies
- Add support for hwc_setCursorPositionAsync as per HWC 1.4
Change-Id: I8663b6da89b0c2dd9b48af96d64a433b2b8a302c
diff --git a/libhwcomposer/hwc.cpp b/libhwcomposer/hwc.cpp
index 85e5d1b..b54cb84 100644
--- a/libhwcomposer/hwc.cpp
+++ b/libhwcomposer/hwc.cpp
@@ -29,6 +29,7 @@
#include <overlay.h>
#include <overlayRotator.h>
#include <overlayWriteback.h>
+#include <overlayCursor.h>
#include <mdp_version.h>
#include "hwc_utils.h"
#include "hwc_fbupdate.h"
@@ -443,6 +444,30 @@
return ret;
}
+static int hwc_setCursorPositionAsync(struct hwc_composer_device_1* dev,
+ int dpy, int x, int y) {
+ int ret = -1;
+ hwc_context_t* ctx = (hwc_context_t*)(dev);
+ switch(dpy) {
+ case HWC_DISPLAY_PRIMARY:
+ {
+ ATRACE_CALL();
+ HWCursor* hwCursor = HWCursor::getInstance();
+ ctx->mDrawLock.lock();
+ if (hwCursor->isCursorSet() &&
+ hwCursor->setPositionAsync(ctx->dpyAttr[dpy].fd, x, y)) {
+ ret = 0;
+ }
+ ctx->mDrawLock.unlock();
+ break;
+ }
+ default:
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+
static int hwc_setPowerMode(struct hwc_composer_device_1* dev, int dpy,
int mode)
{
@@ -462,6 +487,7 @@
ctx->mOverlay->configBegin();
ctx->mOverlay->configDone();
ctx->mRotMgr->clear();
+ HWCursor::getInstance()->free(ctx->dpyAttr[dpy].fd);
// If VDS is connected, do not clear WB object as it
// will end up detaching IOMMU. This is required
// to send black frame to WFD sink on power suspend.
@@ -922,17 +948,20 @@
if(ctx->mMDPComp[dpy])
ctx->mMDPComp[dpy]->dump(aBuf, ctx);
}
- char ovDump[2048] = {'\0'};
- ctx->mOverlay->getDump(ovDump, 2048);
+ char ovDump[3072] = {'\0'};
+ ctx->mOverlay->getDump(ovDump, 3072);
dumpsys_log(aBuf, ovDump);
ovDump[0] = '\0';
ctx->mRotMgr->getDump(ovDump, 1024);
dumpsys_log(aBuf, ovDump);
ovDump[0] = '\0';
- if(Writeback::getDump(ovDump, 1024)) {
+ if(Writeback::getDump(ovDump, 512)) {
dumpsys_log(aBuf, ovDump);
ovDump[0] = '\0';
}
+ HWCursor::getInstance()->getDump(ovDump, 512);
+ dumpsys_log(aBuf, ovDump);
+ ovDump[0] = '\0';
dumpsys_log(aBuf, "Copybit::isAbcInUse=%d\n\n",isAbcInUse(ctx) ? 1 : 0);
strlcpy(buff, aBuf.string(), buff_len);
}
@@ -1029,6 +1058,7 @@
dev->device.getDisplayAttributes = hwc_getDisplayAttributes;
dev->device.getActiveConfig = hwc_getActiveConfig;
dev->device.setActiveConfig = hwc_setActiveConfig;
+ dev->device.setCursorPositionAsync = hwc_setCursorPositionAsync;
*device = &dev->device.common;
status = 0;
}
diff --git a/libhwcomposer/hwc_mdpcomp.cpp b/libhwcomposer/hwc_mdpcomp.cpp
index 5572cc4..b619395 100644
--- a/libhwcomposer/hwc_mdpcomp.cpp
+++ b/libhwcomposer/hwc_mdpcomp.cpp
@@ -18,7 +18,6 @@
#include <math.h>
#include "hwc_mdpcomp.h"
-#include <sys/ioctl.h>
#include <dlfcn.h>
#include "hdmi.h"
#include "qdMetaData.h"
@@ -26,6 +25,7 @@
#include "hwc_fbupdate.h"
#include "hwc_ad.h"
#include <overlayRotator.h>
+#include <overlayCursor.h>
#include "hwc_copybit.h"
#include "qd_utils.h"
@@ -77,8 +77,8 @@
(mDpy == 0) ? "\"PRIMARY\"" :
(mDpy == 1) ? "\"EXTERNAL\"" : "\"VIRTUAL\"");
dumpsys_log(buf,"CURR_FRAME: layerCount:%2d mdpCount:%2d "
- "fbCount:%2d \n", mCurrentFrame.layerCount,
- mCurrentFrame.mdpCount, mCurrentFrame.fbCount);
+ "fbCount:%2d dropCount:%2d\n", mCurrentFrame.layerCount,
+ mCurrentFrame.mdpCount, mCurrentFrame.fbCount, mCurrentFrame.dropCount);
dumpsys_log(buf,"needsFBRedraw:%3s pipesUsed:%2d MaxPipesPerMixer: %d \n",
(mCurrentFrame.needsRedraw? "YES" : "NO"),
mCurrentFrame.mdpCount, sMaxPipesPerMixer);
@@ -106,7 +106,8 @@
(mCurrentFrame.isFBComposed[index] ? "YES" : "NO"),
mCurrentFrame.layerToMDP[index],
(mCurrentFrame.isFBComposed[index] ?
- (mCurrentFrame.drop[index] ? "DROP" :
+ (mCurrentFrame.drop[index] ?
+ ((mCurrentFrame.hwCursorIndex == index) ? "CURSOR": "DROP"):
(mCurrentFrame.needsRedraw ? "GLES" : "CACHE")) : "MDP"),
(mCurrentFrame.isFBComposed[index] ? mCurrentFrame.fbZ :
mCurrentFrame.mdpToLayer[mCurrentFrame.layerToMDP[index]].pipeInfo->zOrder));
@@ -301,7 +302,11 @@
/* Drop the layer when its already present in FB OR when it lies
* outside frame's ROI */
if(!mCurrentFrame.needsRedraw || mCurrentFrame.drop[index]) {
- layer->compositionType = HWC_OVERLAY;
+ if(index == mCurrentFrame.hwCursorIndex) {
+ layer->compositionType = HWC_CURSOR_OVERLAY;
+ } else {
+ layer->compositionType = HWC_OVERLAY;
+ }
}
}
}
@@ -946,6 +951,9 @@
hwc_display_contents_1_t* list) {
const int numAppLayers = ctx->listStats[mDpy].numAppLayers;
+ // PTOR does not qualify when there are layers dropped, but if
+ // dropped layer is only a cursor, PTOR could qualify
+ const int numNonCursorLayers = numAppLayers - mCurrentFrame.dropCount;
const int stagesForMDP = min(sMaxPipesPerMixer,
ctx->mOverlay->availablePipes(mDpy, Overlay::MIXER_DEFAULT));
@@ -955,10 +963,11 @@
return false;
}
- // Frame level checks
+ // Frame level checks - consider PTOR in case of dropCount only if the cursor
+ // layer is dropped, otherwise bail out of PTOR
if ((numAppLayers > stagesForMDP) || isSkipPresent(ctx, mDpy) ||
- isYuvPresent(ctx, mDpy) || mCurrentFrame.dropCount ||
- isSecurePresent(ctx, mDpy)) {
+ isYuvPresent(ctx, mDpy) || isSecurePresent(ctx, mDpy) ||
+ (mCurrentFrame.dropCount - (int)isCursorPresent(ctx, mDpy))) {
ALOGD_IF(isDebug(), "%s: Frame not supported!", __FUNCTION__);
return false;
}
@@ -983,7 +992,7 @@
memset(overlapRect, 0, sizeof(overlapRect));
int layerPixelCount, minPixelCount = 0;
int numPTORLayersFound = 0;
- for (int i = numAppLayers-1; (i >= 0 &&
+ for (int i = numNonCursorLayers - 1; (i >= 0 &&
numPTORLayersFound < MAX_PTOR_LAYERS); i--) {
hwc_layer_1_t* layer = &list->hwLayers[i];
hwc_rect_t crop = integerizeSourceCrop(layer->sourceCropf);
@@ -1028,9 +1037,9 @@
return false;
// Store the displayFrame and the sourceCrops of the layers
- hwc_rect_t displayFrame[numAppLayers];
- hwc_rect_t sourceCrop[numAppLayers];
- for(int i = 0; i < numAppLayers; i++) {
+ hwc_rect_t displayFrame[numNonCursorLayers];
+ hwc_rect_t sourceCrop[numNonCursorLayers];
+ for(int i = 0; i < numNonCursorLayers; i++) {
hwc_layer_1_t* layer = &list->hwLayers[i];
displayFrame[i] = layer->displayFrame;
sourceCrop[i] = integerizeSourceCrop(layer->sourceCropf);
@@ -1119,11 +1128,11 @@
}
}
- mCurrentFrame.mdpCount = numAppLayers;
+ mCurrentFrame.mdpCount = numNonCursorLayers;
mCurrentFrame.fbCount = 0;
mCurrentFrame.fbZ = -1;
- for (int j = 0; j < numAppLayers; j++) {
+ for (int j = 0; j < numNonCursorLayers; j++) {
if(isValidRect(list->hwLayers[j].displayFrame)) {
mCurrentFrame.isFBComposed[j] = false;
} else {
@@ -1135,7 +1144,7 @@
bool result = postHeuristicsHandling(ctx, list);
// Restore layer attributes
- for(int i = 0; i < numAppLayers; i++) {
+ for(int i = 0; i < numNonCursorLayers; i++) {
hwc_layer_1_t* layer = &list->hwLayers[i];
layer->displayFrame = displayFrame[i];
layer->sourceCropf.left = (float)sourceCrop[i].left;
@@ -1342,7 +1351,9 @@
hwc_display_contents_1_t* list){
if(!qdutils::MDPVersion::getInstance().isPartialUpdateEnabled() ||
isSkipPresent(ctx, mDpy) || (list->flags & HWC_GEOMETRY_CHANGED) ||
- !sIsPartialUpdateActive || mDpy ) {
+ isCursorPresent(ctx, mDpy) || !sIsPartialUpdateActive || mDpy) {
+ // On Async position update, the ROI becomes invalid, hence disable PU
+ // when cursor is present
return false;
}
if(ctx->listStats[mDpy].secureUI)
@@ -1910,7 +1921,20 @@
bool MDPComp::resourceCheck(hwc_context_t* ctx,
hwc_display_contents_1_t* list) {
const bool fbUsed = mCurrentFrame.fbCount;
- if(mCurrentFrame.mdpCount > sMaxPipesPerMixer - fbUsed) {
+ int cursorInUse = 0;
+ if(mDpy == HWC_DISPLAY_PRIMARY) {
+ // check if cursor is in use for primary
+ cursorInUse = HWCursor::getInstance()->isCursorSet();
+ }
+ int maxStages = qdutils::MDPVersion::getInstance().getBlendStages();
+ // HW Cursor needs one blending stage, account for that in the check below
+ // On high end targets(8994) has 8 blending stages, HAL is configured to use < 8.
+ // Make use of the remaining stages for HW Cursor so that the composition
+ // strategy would not fail due to this limitation.
+ if (maxStages > sMaxPipesPerMixer) {
+ cursorInUse = 0;
+ }
+ if(mCurrentFrame.mdpCount > (sMaxPipesPerMixer - fbUsed - cursorInUse)) {
ALOGD_IF(isDebug(), "%s: Exceeds MAX_PIPES_PER_MIXER",__FUNCTION__);
return false;
}
@@ -1985,6 +2009,52 @@
return true;
}
+static bool validForCursor(hwc_context_t* ctx, int dpy, hwc_layer_1_t* layer) {
+ private_handle_t *hnd = (private_handle_t *)layer->handle;
+ hwc_rect dst = layer->displayFrame;
+ hwc_rect src = integerizeSourceCrop(layer->sourceCropf);
+ int srcW = src.right - src.left;
+ int srcH = src.bottom - src.top;
+ int dstW = dst.right - dst.left;
+ int dstH = dst.bottom - dst.top;
+ qdutils::MDPVersion &mdpVersion = qdutils::MDPVersion::getInstance();
+ uint32_t maxCursorSize = mdpVersion.getMaxCursorSize();
+ uint32_t numHwCursors = mdpVersion.getCursorPipes();
+ bool primarySplit = isDisplaySplit(ctx, HWC_DISPLAY_PRIMARY);
+ uint32_t cursorPipesNeeded = 1; // One cursor pipe needed(default)
+ bool ret = false;
+
+ if(dpy > HWC_DISPLAY_PRIMARY) {
+ // Cursor not supported on secondary displays, as it involves scaling
+ // in most of the cases
+ return false;
+ } else if (isSkipLayer(layer)) {
+ return false;
+ // Checks for HW limitation
+ } else if (numHwCursors == 0 || maxCursorSize <= 0) {
+ return false;
+ } else if (needsScaling(layer)) {
+ return false;
+ } else if (layer->transform != 0) {
+ return false;
+ } else if (hnd->format != HAL_PIXEL_FORMAT_RGBA_8888) {
+ return false;
+ } else if (srcW > (int)maxCursorSize || srcH > (int)maxCursorSize) {
+ return false;
+ }
+
+ if (isDisplaySplit(ctx, dpy) && !mdpVersion.isSrcSplit()) {
+ // In case of split display with no srcSplit, the driver allocates two
+ // pipes to support async position update across mixers, hence
+ // need to account for that here.
+ cursorPipesNeeded = 2;
+ }
+ if (cursorPipesNeeded <= numHwCursors) {
+ ret = true;
+ }
+ return ret;
+}
+
// Checks only if videos or single layer(RGB) is updating
// which is used for setting dynamic fps or perf hint for single
// layer video playback
@@ -2029,9 +2099,10 @@
int ret = 0;
char property[PROPERTY_VALUE_MAX];
- if(!ctx || !list) {
- ALOGE("%s: Invalid context or list",__FUNCTION__);
+ if(!list) {
+ ALOGE("%s: Invalid list", __FUNCTION__);
mCachedFrame.reset();
+ freeHwCursor(ctx->dpyAttr[mDpy].fd, mDpy);
return -1;
}
@@ -2052,6 +2123,7 @@
mCurrentFrame.reset(numLayers);
memset(&mCurrentFrame.drop, 0, sizeof(mCurrentFrame.drop));
mCurrentFrame.dropCount = 0;
+ mCurrentFrame.hwCursorIndex = -1;
//Do not cache the information for next draw cycle.
if(numLayers > MAX_NUM_APP_LAYERS or (!numLayers)) {
@@ -2061,6 +2133,7 @@
#ifdef DYNAMIC_FPS
setDynRefreshRate(ctx, list);
#endif
+ freeHwCursor(ctx->dpyAttr[mDpy].fd, mDpy);
return -1;
}
@@ -2077,6 +2150,7 @@
#ifdef DYNAMIC_FPS
setDynRefreshRate(ctx, list);
#endif
+ freeHwCursor(ctx->dpyAttr[mDpy].fd, mDpy);
ret = -1;
return ret;
} else {
@@ -2106,6 +2180,23 @@
dropNonAIVLayers(ctx, list);
}
+ // Configure the cursor if present
+ int topIndex = ctx->listStats[mDpy].numAppLayers - 1;
+ if(ctx->listStats[mDpy].cursorLayerPresent &&
+ validForCursor(ctx, mDpy, &(list->hwLayers[topIndex]))) {
+ if(configHwCursor(ctx->dpyAttr[mDpy].fd, mDpy,
+ &(list->hwLayers[topIndex]))) {
+ // As cursor is configured, mark that layer as dropped, so that
+ // it wont be considered for composition by other strategies.
+ mCurrentFrame.hwCursorIndex = topIndex;
+ mCurrentFrame.drop[topIndex] = true;
+ mCurrentFrame.dropCount++;
+ }
+ } else {
+ // Release the hw cursor
+ freeHwCursor(ctx->dpyAttr[mDpy].fd, mDpy);
+ }
+
// if tryFullFrame fails, try to push all video and secure RGB layers
// to MDP for composition.
mModeOn = tryFullFrame(ctx, list) || tryMDPOnlyLayers(ctx, list) ||
@@ -2117,6 +2208,12 @@
reset(ctx);
memset(&mCurrentFrame.drop, 0, sizeof(mCurrentFrame.drop));
mCurrentFrame.dropCount = 0;
+ // Check if cursor is in use for primary and mark accordingly
+ if(!mDpy && HWCursor::getInstance()->isCursorSet()) {
+ int topIndex = ctx->listStats[mDpy].numAppLayers - 1;
+ hwc_layer_1_t *layer = &(list->hwLayers[topIndex]);
+ layer->compositionType = HWC_CURSOR_OVERLAY;
+ }
ret = -1;
ALOGE_IF(sSimulationFlags && (mDpy == HWC_DISPLAY_PRIMARY),
"MDP Composition Strategies Failed");
@@ -2131,6 +2228,8 @@
}
ALOGD_IF( isDebug(),"%s: MDP Comp not possible for this frame",
__FUNCTION__);
+ // Release the hw cursor
+ freeHwCursor(ctx->dpyAttr[mDpy].fd, mDpy);
ret = -1;
}
diff --git a/libhwcomposer/hwc_mdpcomp.h b/libhwcomposer/hwc_mdpcomp.h
index 676bf24..db68e82 100644
--- a/libhwcomposer/hwc_mdpcomp.h
+++ b/libhwcomposer/hwc_mdpcomp.h
@@ -119,6 +119,7 @@
bool needsRedraw;
int fbZ;
+ int hwCursorIndex;
/* c'tor */
FrameInfo();
diff --git a/libhwcomposer/hwc_utils.cpp b/libhwcomposer/hwc_utils.cpp
index a3cb196..51c208e 100644
--- a/libhwcomposer/hwc_utils.cpp
+++ b/libhwcomposer/hwc_utils.cpp
@@ -30,6 +30,7 @@
#include <overlay.h>
#include <overlayRotator.h>
#include <overlayWriteback.h>
+#include <overlayCursor.h>
#include "hwc_utils.h"
#include "hwc_mdpcomp.h"
#include "hwc_fbupdate.h"
@@ -1085,6 +1086,7 @@
ctx->listStats[dpy].renderBufIndexforABC = -1;
ctx->listStats[dpy].secureRGBCount = 0;
ctx->listStats[dpy].refreshRateRequest = ctx->dpyAttr[dpy].refreshRate;
+ ctx->listStats[dpy].cursorLayerPresent = false;
uint32_t refreshRate = 0;
qdutils::MDPVersion& mdpHw = qdutils::MDPVersion::getInstance();
int s3dFormat = HAL_NO_3D;
@@ -1116,6 +1118,12 @@
if(ctx->listStats[dpy].numAppLayers > MAX_NUM_APP_LAYERS)
continue;
+ // Valid cursor must be the top most layer
+ if((int)i == (ctx->listStats[dpy].numAppLayers - 1) &&
+ isCursorLayer(&list->hwLayers[i])) {
+ ctx->listStats[dpy].cursorLayerPresent = true;
+ }
+
//reset yuv indices
ctx->listStats[dpy].yuvIndices[i] = -1;
ctx->listStats[dpy].yuv4k2kIndices[i] = -1;
@@ -1942,6 +1950,46 @@
crop.bottom = transformedCrop.y + transformedCrop.h;
}
+bool configHwCursor(const int fd, int dpy, hwc_layer_1_t* layer) {
+ if(dpy > HWC_DISPLAY_PRIMARY) {
+ // HWCursor not supported on secondary displays
+ return false;
+ }
+ private_handle_t *hnd = (private_handle_t *)layer->handle;
+ hwc_rect dst = layer->displayFrame;
+ hwc_rect src = integerizeSourceCrop(layer->sourceCropf);
+ int srcW = src.right - src.left;
+ int srcH = src.bottom - src.top;
+ int dstW = dst.right - dst.left;
+ int dstH = dst.bottom - dst.top;
+
+ Whf whf(getWidth(hnd), getHeight(hnd), hnd->format);
+ Dim crop(src.left, src.top, srcW, srcH);
+ Dim dest(dst.left, dst.top, dstW, dstH);
+
+ ovutils::PipeArgs pargs(ovutils::OV_MDP_FLAGS_NONE,
+ whf,
+ Z_SYSTEM_ALLOC,
+ ovutils::ROT_FLAGS_NONE,
+ layer->planeAlpha,
+ (ovutils::eBlending)
+ getBlending(layer->blending));
+
+ ALOGD_IF(HWC_UTILS_DEBUG, "%s: CursorInfo: w = %d h = %d "
+ "crop [%d, %d, %d, %d] dst [%d, %d, %d, %d]", __FUNCTION__,
+ getWidth(hnd), getHeight(hnd), src.left, src.top, srcW, srcH,
+ dst.left, dst.top, dstW, dstH);
+
+ return HWCursor::getInstance()->config(fd, (void*)hnd->base, pargs,
+ crop, dest);
+}
+
+void freeHwCursor(const int fd, int dpy) {
+ if (dpy == HWC_DISPLAY_PRIMARY) {
+ HWCursor::getInstance()->free(fd);
+ }
+}
+
int getRotDownscale(hwc_context_t *ctx, const hwc_layer_1_t *layer) {
if(not qdutils::MDPVersion::getInstance().isRotDownscaleEnabled()) {
return 0;
diff --git a/libhwcomposer/hwc_utils.h b/libhwcomposer/hwc_utils.h
index af43514..7502310 100644
--- a/libhwcomposer/hwc_utils.h
+++ b/libhwcomposer/hwc_utils.h
@@ -165,6 +165,8 @@
uint32_t refreshRateRequest;
// Flag related to windowboxing feature
bool mAIVVideoMode;
+ // curser layer info
+ bool cursorLayerPresent;
};
//PTOR Comp info
@@ -414,6 +416,9 @@
void updateSource(ovutils::eTransform& orient, ovutils::Whf& whf,
hwc_rect_t& crop, overlay::Rotator *rot);
+bool configHwCursor(const int fd, int dpy, hwc_layer_1_t* layer);
+void freeHwCursor(const int fd, int dpy);
+
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);
@@ -497,6 +502,11 @@
return (UNLIKELY(l && (l->flags & HWC_AIV_CC)));
}
+static inline bool isCursorLayer(const hwc_layer_1_t* l) {
+ return (UNLIKELY(l && (l->flags & HWC_IS_CURSOR_LAYER)));
+}
+
+
// Returns true if the buffer is yuv
static inline bool isYuvBuffer(const private_handle_t* hnd) {
return (hnd && (hnd->bufferType == BUFFER_TYPE_VIDEO));
@@ -718,6 +728,10 @@
return ctx->listStats[dpy].isSecurePresent;
}
+static inline bool isCursorPresent (hwc_context_t *ctx, int dpy) {
+ return ctx->listStats[dpy].cursorLayerPresent;
+}
+
static inline bool isSecondaryConfiguring(hwc_context_t* ctx) {
return (ctx->dpyAttr[HWC_DISPLAY_EXTERNAL].isConfiguring ||
ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].isConfiguring);
diff --git a/liboverlay/Android.mk b/liboverlay/Android.mk
index aa1ea24..9ea7432 100644
--- a/liboverlay/Android.mk
+++ b/liboverlay/Android.mk
@@ -17,6 +17,7 @@
overlayMdpRot.cpp \
overlayMdssRot.cpp \
overlayWriteback.cpp \
+ overlayCursor.cpp \
pipes/overlayGenPipe.cpp
include $(BUILD_SHARED_LIBRARY)
diff --git a/liboverlay/overlayCursor.cpp b/liboverlay/overlayCursor.cpp
new file mode 100644
index 0000000..c22dd39
--- /dev/null
+++ b/liboverlay/overlayCursor.cpp
@@ -0,0 +1,147 @@
+/*
+* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+* * Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* * Redistributions in binary form must reproduce the above
+* copyright notice, this list of conditions and the following
+* disclaimer in the documentation and/or other materials provided
+* with the distribution.
+* * Neither the name of The Linux Foundation nor the names of its
+* contributors may be used to endorse or promote products derived
+* from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+//#include "overlay.h"
+#include "overlayCursor.h"
+#include "mdpWrapper.h"
+
+namespace overlay {
+
+HWCursor* HWCursor::sHwCursor = 0;
+
+//=========== class HWCursor =================================================
+HWCursor* HWCursor::getInstance() {
+ if (sHwCursor == NULL) {
+ sHwCursor = new HWCursor();
+ }
+ return sHwCursor;
+}
+
+bool HWCursor::config(const int fd, void* base, PipeArgs& pargs,
+ Dim& crop, Dim& dest) {
+ bool ret = true;
+ fb_cursor *cursor = &mfbCursor;
+ fb_image cursorImage;
+
+ cursor->set = FB_CUR_SETIMAGE | FB_CUR_SETPOS;
+ cursor->enable = (uint16_t)1;
+ cursor->rop = 0,
+ cursor->mask = NULL;
+ cursor->hot.x = (uint16_t)crop.x;
+ cursor->hot.y = (uint16_t)crop.y;
+
+ cursorImage.dx = dest.x;
+ cursorImage.dy = dest.y;
+ cursorImage.width = pargs.whf.w;
+ cursorImage.height = pargs.whf.h;
+ cursorImage.fg_color = pargs.planeAlpha; // Hint for PMA
+ cursorImage.bg_color = 0xffffff00; // RGBA
+ cursorImage.depth = 32;
+ cursorImage.data = (char*)base;
+
+ cursor->image = cursorImage;
+
+ if (!setCursor(fd)) {
+ ALOGE("%s: Failed to setup HW cursor", __FUNCTION__);
+ ret = false;
+ memset(cursor, 0, sizeof(fb_cursor));
+ }
+ return ret;
+}
+
+bool HWCursor::setPositionAsync(const int fd, int x, int y) {
+ bool ret = true;
+ if (isCursorSet()) {
+ fb_cursor *cursor = &mfbCursor;
+ cursor->set = FB_CUR_SETPOS;
+ cursor->image.dx = x;
+ cursor->image.dy = y;
+ if (!setCursor(fd)) {
+ ALOGE("%s: Failed to set position x = %d y = %d", __FUNCTION__, x, y);
+ ret = false;
+ }
+ }
+ return ret;
+}
+
+bool HWCursor::free(const int fd) {
+ fb_cursor *cursor = &mfbCursor;
+ fb_image cursorImage;
+ bool ret = true;
+
+ if(!cursor->enable) {
+ return ret;
+ }
+
+ cursor->enable = (uint16_t)0;
+
+ if (!setCursor(fd)) {
+ ALOGE("%s: Failed to free cursor hw", __FUNCTION__);
+ ret = false;
+ }
+ memset(cursor, 0, sizeof(fb_cursor));
+ return ret;
+}
+
+bool HWCursor::setCursor(const int fd) {
+ bool ret = true;
+ ATRACE_CALL();
+ fb_cursor *cursor = &mfbCursor;
+
+ if(fd <= 0) {
+ ALOGE("%s: Invalid fd", fd);
+ return false;
+ }
+
+ if (ioctl(fd, MSMFB_CURSOR, cursor) < 0) {
+ ALOGE("%s: Failed to call ioctl MSMFB_CURSOR err=%s\n", __FUNCTION__,
+ strerror(errno));
+ ret = false;
+ }
+ return ret;
+}
+
+void HWCursor::getDump(char* buf, size_t len) {
+ char cursordump[len];
+ fb_cursor* cursor = &mfbCursor;
+ if (cursor->enable) {
+ snprintf(cursordump, sizeof(cursordump),
+ "HWCursor on Primary: src w=%d h=%d\n"
+ "\tsrc_rect x=%d y=%d w=%d h=%d\n"
+ "\tdst_rect x=%d y=%d w=%d h=%d\n\n", cursor->image.width,
+ cursor->image.height, cursor->hot.x, cursor->hot.y,
+ cursor->image.width, cursor->image.height,
+ cursor->image.dx, cursor->image.dy, cursor->image.width,
+ cursor->image.height);
+ strlcat(buf, cursordump, len);
+ }
+
+}
+
+} //namespace overlay
diff --git a/liboverlay/overlayCursor.h b/liboverlay/overlayCursor.h
new file mode 100644
index 0000000..2754a8d
--- /dev/null
+++ b/liboverlay/overlayCursor.h
@@ -0,0 +1,60 @@
+/*
+* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+* * Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* * Redistributions in binary form must reproduce the above
+* copyright notice, this list of conditions and the following
+* disclaimer in the documentation and/or other materials provided
+* with the distribution.
+* * Neither the name of The Linux Foundation nor the names of its
+* contributors may be used to endorse or promote products derived
+* from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef OVERLAY_CURSOR_H
+#define OVERLAY_CURSOR_H
+
+#include "overlay.h"
+#include "overlayUtils.h"
+
+namespace overlay {
+
+using namespace overlay;
+using namespace overlay::utils;
+
+// HW Cursor Helper
+class HWCursor {
+public:
+ static HWCursor* getInstance();
+ bool config(const int fd, void* base, PipeArgs& pargs, Dim& crop,
+ Dim& dest);
+ bool setPositionAsync(const int fd, int x, int y);
+ bool free(const int fd);
+ bool isCursorSet() { return mfbCursor.enable; }
+ void getDump(char* buf, size_t len);
+private:
+ HWCursor() { memset(&mfbCursor, 0, sizeof(mfbCursor)); }
+ bool setCursor(const int fd);
+ fb_cursor mfbCursor;
+
+ static HWCursor* sHwCursor;
+};
+}
+
+#endif // OVERLAY_CURSOR_H
diff --git a/libqdutils/mdp_version.cpp b/libqdutils/mdp_version.cpp
index a6a1b85..c02e28d 100644
--- a/libqdutils/mdp_version.cpp
+++ b/libqdutils/mdp_version.cpp
@@ -91,6 +91,7 @@
mRGBPipes = 0;
mVGPipes = 0;
mDMAPipes = 0;
+ mCursorPipes = 0;
mFeatures = 0;
mMDPUpscale = 1;
mMDPDownscale = 1;
@@ -110,6 +111,7 @@
// Default width of MDSS SSPP. For layer resolutions beyond this, we drive
// using two SSPP's.
mMaxPipeWidth = 2048;
+ mMaxCursorSize = 0;
updatePanelInfo();
@@ -309,6 +311,10 @@
else if(!strncmp(tokens[0], "dma_pipes", strlen("dma_pipes"))) {
mDMAPipes = (uint8_t)atoi(tokens[1]);
}
+ else if(!strncmp(tokens[0], "cursor_pipes",
+ strlen("cursor_pipes"))) {
+ mCursorPipes = (uint8_t)atoi(tokens[1]);
+ }
else if(!strncmp(tokens[0], "blending_stages",
strlen("blending_stages"))) {
mBlendStages = (uint8_t)atoi(tokens[1]);
@@ -354,6 +360,9 @@
mRotDownscale = true;
}
}
+ } else if(!strncmp(tokens[0], "max_cursor_size",
+ strlen("max_cursor_size"))) {
+ mMaxCursorSize = atoi(tokens[1]);
}
}
}
@@ -389,8 +398,8 @@
}
ALOGD_IF(DEBUG, "%s: mMDPVersion: %d mMdpRev: %x mRGBPipes:%d,"
- "mVGPipes:%d", __FUNCTION__, mMDPVersion, mMdpRev,
- mRGBPipes, mVGPipes);
+ "mVGPipes:%d mCursorPipes:%d", __FUNCTION__, mMDPVersion,
+ mMdpRev, mRGBPipes, mVGPipes, mCursorPipes);
ALOGD_IF(DEBUG, "%s:mDMAPipes:%d \t mMDPDownscale:%d, mFeatures:%d",
__FUNCTION__, mDMAPipes, mMDPDownscale, mFeatures);
ALOGD_IF(DEBUG, "%s:mLowBw: %lu mHighBw: %lu", __FUNCTION__, mLowBw,
diff --git a/libqdutils/mdp_version.h b/libqdutils/mdp_version.h
index ad92c83..6ebe558 100644
--- a/libqdutils/mdp_version.h
+++ b/libqdutils/mdp_version.h
@@ -116,6 +116,7 @@
uint8_t getRGBPipes() { return mRGBPipes; }
uint8_t getVGPipes() { return mVGPipes; }
uint8_t getDMAPipes() { return mDMAPipes; }
+ uint8_t getCursorPipes() { return mCursorPipes; }
uint8_t getBlendStages() { return mBlendStages; }
bool supportsDecimation();
uint32_t getMaxMDPDownscale();
@@ -140,6 +141,7 @@
uint32_t getMaxFpsSupported() { return mPanelInfo.mMaxFps; }
uint32_t getMaxMixerWidth() const { return mMaxMixerWidth; }
uint32_t getMaxPipeWidth() const { return mMaxPipeWidth; }
+ uint32_t getMaxCursorSize() const { return mMaxCursorSize; }
bool hasMinCropWidthLimitation() const;
bool isSrcSplit() const;
bool isSrcSplitAlways() const;
@@ -166,6 +168,7 @@
uint8_t mRGBPipes;
uint8_t mVGPipes;
uint8_t mDMAPipes;
+ uint8_t mCursorPipes;
uint8_t mBlendStages;
uint32_t mFeatures;
uint32_t mMDPDownscale;
@@ -182,6 +185,7 @@
bool mRotDownscale;
uint32_t mMaxMixerWidth; //maximum x-res of a given mdss mixer.
uint32_t mMaxPipeWidth; //maximum x-res of the mdp pipe.
+ uint32_t mMaxCursorSize; //maximum size supported by hw cursor
};
}; //namespace qdutils
#endif //INCLUDE_LIBQCOMUTILS_MDPVER