diff options
author | Zohaib Alam <zalam@codeaurora.org> | 2013-09-28 03:38:20 -0400 |
---|---|---|
committer | Zohaib Alam <zalam@codeaurora.org> | 2014-01-31 14:59:19 -0500 |
commit | 1bb656178eb1bb7a7f400060253a5a40e742c2f6 (patch) | |
tree | 7bc107b9324a9be01d33bad5b943ac45929b1d37 | |
parent | ac067305709612b6287a2db33aae36597911ef20 (diff) | |
download | display-1bb656178eb1bb7a7f400060253a5a40e742c2f6.tar.gz |
hwc: vpuclient: vpuClient implementation
Vpuclient is the client side for VFM in hwc. It follows the
similar pattern of prepare and draw. It has 2 step prepare
including: setVpuSession and prepare. The setVpuSession function
passes all the layers from the SF list to VFM, which marks
the layers that it can support. After this, the layer
allocation/configuration is done, and finally in prepare the
allocated pipes are passed down to VFM. The draw function
passes the handle to the VFM to draw the video layer.
Change-Id: I5d8795de35ed98716f7fa4cd48506b488cb3cb5d
-rw-r--r-- | common.mk | 3 | ||||
-rw-r--r-- | libhwcomposer/Android.mk | 4 | ||||
-rw-r--r-- | libhwcomposer/hwc.cpp | 26 | ||||
-rw-r--r-- | libhwcomposer/hwc_qclient.cpp | 10 | ||||
-rw-r--r-- | libhwcomposer/hwc_utils.cpp | 41 | ||||
-rw-r--r-- | libhwcomposer/hwc_utils.h | 1 | ||||
-rw-r--r-- | libhwcomposer/hwc_vpuclient.cpp | 989 | ||||
-rw-r--r-- | libhwcomposer/hwc_vpuclient.h | 91 |
8 files changed, 1080 insertions, 85 deletions
@@ -36,8 +36,7 @@ ifeq ($(call is-board-platform-in-list, msm8974 msm8226 msm8610 apq8084 \ common_flags += -DMDSS_TARGET endif -ifeq ($(call is-board-platform-in-list, mpq8092 msm_bronze msm8916), true) - #XXX: Replace with check from MDP when available +ifeq ($(call is-board-platform-in-list, mpq8092), true) common_flags += -DVPU_TARGET endif diff --git a/libhwcomposer/Android.mk b/libhwcomposer/Android.mk index 72e064e2..9749c697 100644 --- a/libhwcomposer/Android.mk +++ b/libhwcomposer/Android.mk @@ -25,10 +25,8 @@ LOCAL_SRC_FILES := hwc.cpp \ hwc_dump_layers.cpp \ hwc_ad.cpp \ hwc_virtual.cpp - -ifeq ($(call is-board-platform-in-list, mpq8092 msm_bronze msm8916), true) +ifeq ($(call is-board-platform-in-list, mpq8092), true) LOCAL_SRC_FILES += hwc_vpuclient.cpp endif - include $(BUILD_SHARED_LIBRARY) diff --git a/libhwcomposer/hwc.cpp b/libhwcomposer/hwc.cpp index b45a7cdb..431757db 100644 --- a/libhwcomposer/hwc.cpp +++ b/libhwcomposer/hwc.cpp @@ -180,6 +180,7 @@ static int hwc_prepare_primary(hwc_composer_device_1 *dev, ATRACE_CALL(); hwc_context_t* ctx = (hwc_context_t*)(dev); const int dpy = HWC_DISPLAY_PRIMARY; + bool fbComp = false; if (LIKELY(list && list->numHwLayers > 1) && ctx->dpyAttr[dpy].isActive) { @@ -188,13 +189,19 @@ static int hwc_prepare_primary(hwc_composer_device_1 *dev, reset_layer_prop(ctx, dpy, list->numHwLayers - 1); setListStats(ctx, list, dpy); + + if (ctx->mVPUClient == NULL) + fbComp = (ctx->mMDPComp[dpy]->prepare(ctx, list) < 0); #ifdef VPU_TARGET - ctx->mVPUClient->prepare(ctx, list); + else + fbComp = (ctx->mVPUClient->prepare(ctx, dpy, list) < 0); #endif - if(ctx->mMDPComp[dpy]->prepare(ctx, list) < 0) { + + if (fbComp) { const int fbZ = 0; ctx->mFBUpdate[dpy]->prepare(ctx, list, fbZ); } + if (ctx->mMDP.version < qdutils::MDP_V4_0) { if(ctx->mCopyBit[dpy]) ctx->mCopyBit[dpy]->prepare(ctx, list, dpy); @@ -487,13 +494,15 @@ static int hwc_set_primary(hwc_context_t *ctx, hwc_display_contents_1_t* list) { if(ctx->mHwcDebug[dpy]) ctx->mHwcDebug[dpy]->dumpLayers(list); - if (!ctx->mMDPComp[dpy]->draw(ctx, list)) { + if (ctx->mVPUClient != NULL) { +#ifdef VPU_TARGET + ctx->mVPUClient->predraw(ctx, dpy, list); +#endif + } + else if (!ctx->mMDPComp[dpy]->draw(ctx, list)) { ALOGE("%s: MDPComp draw failed", __FUNCTION__); ret = -1; } -#ifdef VPU_TARGET - ctx->mVPUClient->draw(ctx, list); -#endif //TODO We dont check for SKIP flag on this layer because we need PAN //always. Last layer is always FB @@ -514,6 +523,11 @@ static int hwc_set_primary(hwc_context_t *ctx, hwc_display_contents_1_t* list) { ALOGE("%s: display commit fail for %d dpy!", __FUNCTION__, dpy); ret = -1; } + +#ifdef VPU_TARGET + if (ctx->mVPUClient != NULL) + ctx->mVPUClient->draw(ctx, dpy, list); +#endif } closeAcquireFds(list); diff --git a/libhwcomposer/hwc_qclient.cpp b/libhwcomposer/hwc_qclient.cpp index 8c99d64f..8c5b0798 100644 --- a/libhwcomposer/hwc_qclient.cpp +++ b/libhwcomposer/hwc_qclient.cpp @@ -31,6 +31,7 @@ #include <IQService.h> #include <hwc_utils.h> #include <hwc_vpuclient.h> +#include <mdp_version.h> #define QCLIENT_DEBUG 0 @@ -94,16 +95,19 @@ static android::status_t screenRefresh(hwc_context_t *ctx) { return result; } +#ifdef VPU_TARGET static android::status_t vpuCommand(hwc_context_t *ctx, uint32_t command, const Parcel* inParcel, Parcel* outParcel) { status_t result = NO_INIT; -#ifdef VPU_TARGET - result = ctx->mVPUClient->processCommand(command, inParcel, outParcel); +#ifdef QCOM_BSP + if(qdutils::MDPVersion::getInstance().is8092()) + result = ctx->mVPUClient->processCommand(command, inParcel, outParcel); #endif return result; } +#endif static void setExtOrientation(hwc_context_t *ctx, uint32_t orientation) { ctx->mExtOrientation = orientation; @@ -181,10 +185,12 @@ status_t QClient::notifyCallback(uint32_t command, const Parcel* inParcel, Parcel* outParcel) { status_t ret = NO_ERROR; +#ifdef VPU_TARGET if (command > IQService::VPU_COMMAND_LIST_START && command < IQService::VPU_COMMAND_LIST_END) { return vpuCommand(mHwcContext, command, inParcel, outParcel); } +#endif switch(command) { case IQService::SECURING: diff --git a/libhwcomposer/hwc_utils.cpp b/libhwcomposer/hwc_utils.cpp index ececa70f..f6476f58 100644 --- a/libhwcomposer/hwc_utils.cpp +++ b/libhwcomposer/hwc_utils.cpp @@ -232,8 +232,11 @@ void initContext(hwc_context_t *ctx) // Initialize device orientation to its default orientation ctx->deviceOrientation = 0; ctx->mBufferMirrorMode = false; + ctx->mVPUClient = NULL; + #ifdef VPU_TARGET - ctx->mVPUClient = new VPUClient(); + if(qdutils::MDPVersion::getInstance().is8092()) + ctx->mVPUClient = new VPUClient(ctx); #endif ALOGI("Initializing Qualcomm Hardware Composer"); @@ -270,9 +273,8 @@ void closeContext(hwc_context_t *ctx) } #ifdef VPU_TARGET - if(ctx->mVPUClient) { + if(ctx->mVPUClient != NULL) delete ctx->mVPUClient; - } #endif for(int i = 0; i < HWC_NUM_DISPLAY_TYPES; i++) { @@ -1302,6 +1304,7 @@ int hwc_sync(hwc_context_t *ctx, hwc_display_contents_1_t* list, int dpy, dpy, list->numHwLayers); } + LayerProp *layerProp = ctx->layerProp[dpy]; for(uint32_t i = 0; i < list->numHwLayers; i++) { if(list->hwLayers[i].compositionType == HWC_OVERLAY || list->hwLayers[i].compositionType == HWC_BLIT || @@ -1313,8 +1316,10 @@ int hwc_sync(hwc_context_t *ctx, hwc_display_contents_1_t* list, int dpy, // Release all the app layer fds immediately, // if animation is in progress. list->hwLayers[i].releaseFenceFd = -1; - } else if(list->hwLayers[i].releaseFenceFd < 0) { - //If rotator has not already populated this field. + } else if(list->hwLayers[i].releaseFenceFd < 0 && + !(layerProp[i].mFlags & HWC_VPUCOMP)) { + //If rotator has not already populated this field + // & if it's a not VPU layer list->hwLayers[i].releaseFenceFd = dup(releaseFd); } } @@ -1361,8 +1366,10 @@ void setMdpFlags(hwc_layer_1_t *layer, ovutils::setMdpFlags(mdpFlags, ovutils::OV_MDP_SECURE_OVERLAY_SESSION); } + // in mpq, deinterlacing is done in vpu if(metadata && (metadata->operation & PP_PARAM_INTERLACED) && - metadata->interlaced) { + metadata->interlaced && + (!qdutils::MDPVersion::getInstance().is8092())) { ovutils::setMdpFlags(mdpFlags, ovutils::OV_MDP_DEINTERLACE); } @@ -1549,6 +1556,17 @@ int configureNonSplit(hwc_context_t *ctx, hwc_layer_1_t *layer, int rotFlags = ovutils::ROT_FLAGS_NONE; uint32_t format = ovutils::getMdpFormat(hnd->format, isTileRendered(hnd)); Whf whf(getWidth(hnd), getHeight(hnd), format, hnd->size); + LayerProp *layerProp = ctx->layerProp[dpy]; + +#ifdef VPU_TARGET + if(ctx->mVPUClient != NULL && + ctx->mVPUClient->supportedVPULayer(dpy, layer)) { + whf.format = getMdpFormat( + ctx->mVPUClient->getLayerFormat(dpy, layer)); + whf.w = ctx->mVPUClient->getWidth(dpy, layer); + whf.h = ctx->mVPUClient->getHeight(dpy, layer); + } +#endif // Handle R/B swap if (layer->flags & HWC_FORMAT_RB_SWAP) { @@ -1656,6 +1674,17 @@ int configureSplit(hwc_context_t *ctx, hwc_layer_1_t *layer, int rotFlags = ROT_FLAGS_NONE; uint32_t format = ovutils::getMdpFormat(hnd->format, isTileRendered(hnd)); Whf whf(getWidth(hnd), getHeight(hnd), format, hnd->size); + LayerProp *layerProp = ctx->layerProp[dpy]; + +#ifdef VPU_TARGET + if(ctx->mVPUClient != NULL && + ctx->mVPUClient->supportedVPULayer(dpy, layer)) { + whf.format = getMdpFormat( + ctx->mVPUClient->getLayerFormat(dpy, layer)); + whf.w = ctx->mVPUClient->getWidth(dpy, layer); + whf.h = ctx->mVPUClient->getHeight(dpy, layer); + } +#endif // Handle R/B swap if (layer->flags & HWC_FORMAT_RB_SWAP) { diff --git a/libhwcomposer/hwc_utils.h b/libhwcomposer/hwc_utils.h index 9ccfa1b7..ce1abebf 100644 --- a/libhwcomposer/hwc_utils.h +++ b/libhwcomposer/hwc_utils.h @@ -145,6 +145,7 @@ struct BwcPM { enum { HWC_MDPCOMP = 0x00000001, HWC_COPYBIT = 0x00000002, + HWC_VPUCOMP = 0x00000004, }; // HAL specific features diff --git a/libhwcomposer/hwc_vpuclient.cpp b/libhwcomposer/hwc_vpuclient.cpp index 23c68417..6904efce 100644 --- a/libhwcomposer/hwc_vpuclient.cpp +++ b/libhwcomposer/hwc_vpuclient.cpp @@ -1,93 +1,964 @@ /* -* Copyright (c) 2013 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. -*/ + * Copyright (c) 2013-2014 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. INNO 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 INCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING INANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ #include <dlfcn.h> #include "hwc_vpuclient.h" -#include "hwc_utils.h" -#include <vpu/vpu.h> #include <binder/Parcel.h> - +#include "hwc_fbupdate.h" +#include <vpu/vpu.h> using namespace vpu; using namespace android; +using namespace overlay::utils; +namespace ovutils = overlay::utils; + namespace qhwc { -VPUClient::VPUClient() +VPUClient::VPUClient(hwc_context_t *ctx) { mVPULib = dlopen("libvpu.so", RTLD_NOW); - VPU* (*init)(); - *(void **) &init = dlsym(mVPULib, "getObject"); - if(init) - mVPU = init(); - else - mVPU = NULL; + VPU* (*getObject)(); + + mVPU = NULL; + if (mVPULib == NULL) { + ALOGE("%s: Cannot open libvpu.so object", __FUNCTION__); + return; + } + + *(void **) &getObject = dlsym(mVPULib, "getObject"); + if (getObject) { + mVPU = getObject(); + ALOGI("Initializing VPU client.."); + + // calling vpu init + if (mVPU->init() == NO_ERROR) { + // passing display attributes to libvpu + ALOGD_IF(isDebug(), "%s: VFM init successful!", __FUNCTION__); + + DispAttr_t attr; + attr.width = ctx->dpyAttr[HWC_DISPLAY_PRIMARY].xres; + attr.height = ctx->dpyAttr[HWC_DISPLAY_PRIMARY].yres; + attr.fp100s = (ctx->dpyAttr[HWC_DISPLAY_PRIMARY].vsync_period) ? + 1000000000/(ctx->dpyAttr[HWC_DISPLAY_PRIMARY].vsync_period/100):0; + mVPU->setDisplayAttr((DISPLAY_ID)HWC_DISPLAY_PRIMARY, attr); + + ALOGD_IF(isDebug(),"%s: Display attr: width:%d height:%d fp100s:%d", + __FUNCTION__, attr.width, attr.height, attr.fp100s); + + // memsetting the pipe structure to 0 + memset(mProp, 0, sizeof(mProp)); + + mDebugLogs = 0; + // enable logs + char property[PROPERTY_VALUE_MAX]; + if ( property_get("debug.vpuclient.logs", property, NULL) > 0 ) + mDebugLogs = atoi(property); + + // allocating memory for LayerList + for (int i = 0; i < HWC_NUM_DISPLAY_TYPES; ++i) + vList[i] = (LayerList*) malloc(sizeof(LayerList)); + } + else { + ALOGE("Error: VPU init failed!"); + mVPU = NULL; + } + } } VPUClient::~VPUClient() { + // freeing LayerList + for (int i = 0; i < HWC_NUM_DISPLAY_TYPES; ++i) { + if (vList[i]) + free(vList[i]); + } + void (*destroy) (VPU*); *(void **) &destroy = dlsym(mVPULib, "deleteObject"); dlclose(mVPULib); } -int VPUClient::prepare(hwc_context_t *ctx, - hwc_display_contents_1_t* list) +void setLayer(hwc_layer_1_t *layer, Layer *vLayer) { - int err = 0; - if(!mVPU) - return err; - // * Check VPU status - // * Check session availability - // * Other individual checks - // Do not pass hwc context/list - // Mark buffers to be drawn for VPU - return err; + // setting handle info in vLayer + vLayer->handle = (private_handle_t *)(layer->handle); + + if (vLayer->handle) { + vLayer->srcStride.width = getWidth(vLayer->handle); + vLayer->srcStride.height = getHeight(vLayer->handle); + } + + // setting source crop + hwc_rect_t sourceRect = integerizeSourceCrop(layer->sourceCropf); + vLayer->srcRect.left = sourceRect.left; + vLayer->srcRect.top = sourceRect.top; + vLayer->srcRect.right = sourceRect.right; + vLayer->srcRect.bottom = sourceRect.bottom; + + // setting destination crop + vLayer->tgtRect.left = layer->displayFrame.left; + vLayer->tgtRect.top = layer->displayFrame.top; + vLayer->tgtRect.right = layer->displayFrame.right; + vLayer->tgtRect.bottom = layer->displayFrame.bottom; + + if (layer->flags & HWC_GEOMETRY_CHANGED) + vLayer->inFlags |= GEOMETRY_CHANGED; + + vLayer->acquireFenceFd = layer->acquireFenceFd; + + if (layer->compositionType == HWC_FRAMEBUFFER_TARGET || isSkipLayer(layer)) + vLayer->inFlags |= SKIP_LAYER; +} + +int VPUClient::setupVpuSession(hwc_context_t *ctx, int display, + hwc_display_contents_1_t* list) +{ + memset(vList[display], 0, sizeof(LayerList)); + memset(mProp, 0, sizeof(mProp)); + mNumVpuLayers = 0; + + // setting up the layer + LayerList *vpuList = vList[display]; + vpuList->numLayers = list->numHwLayers; + for (unsigned int i=0; i<(list->numHwLayers); ++i) { + hwc_layer_1_t *layer = &list->hwLayers[i]; + Layer *vLayer = &vpuList->layers[i]; + VpuLayerProp* prop = &mProp[display][i]; + + // Storing the sourceCropf, as it's going to be changed for overlay Set + // will be restored after overlay set in prepare. + prop->sourceCropf = layer->sourceCropf; + + // filling up the vpu list + setLayer(layer, vLayer); + ALOGD_IF(isDebug2(), "%s:Done setting lyr:%d for VFM", __FUNCTION__, i); + } + + if (mVPU->setupVpuSession((DISPLAY_ID)display, vpuList) != NO_ERROR) { + //error in vpu prepare + ALOGE("%s: ERROR in VPU::setupVpuSession", __FUNCTION__); + return -1; + } + ALOGD_IF(isDebug2(), "%s: Done VFM: setupVpuSession", __FUNCTION__); + + mGpuFallback = true; + LayerProp *layerProp = ctx->layerProp[display]; + // check if the pipeID is already set for this layer, then will need to + // ensure that it is reserved in overlay + for (unsigned int i=0; i<(vpuList->numLayers); ++i) { + hwc_layer_1_t *layer = &list->hwLayers[i]; + Layer *vLayer = &vpuList->layers[i]; + VpuLayerProp* prop = &mProp[display][i]; + + if (vLayer->outFlags & VPU_LAYER) { + ALOGD_IF(isDebug(), "%s: VPU supported layer:%d", __FUNCTION__, i); + + mNumVpuLayers++; + mGpuFallback = false; + // Reserving the pipe used in last iteration for the same layer + if ((vLayer->outFlags & RESERVE_PREV_PIPES) && + vLayer->sDestPipes.numPipes > 0) { + prop->pipeCount = vLayer->sDestPipes.numPipes; + if (prop->pipeCount == 1) { + setPipeId(prop, vLayer->sDestPipes.pipe[0]); + ALOGD_IF(isDebug(), "%s: VPU: Reserved pipe:%d", + __FUNCTION__, prop->pipeID[0]); + } + else if (prop->pipeCount == 2) { + setPipeId(prop, vLayer->sDestPipes.pipe[0], + vLayer->sDestPipes.pipe[1]); + ALOGD_IF(isDebug(), "%s: VPU: Reserved lpipe:%d, rpipe:%d", + __FUNCTION__, prop->pipeID[0], prop->pipeID[1]); + } + else { + ALOGE("%s: Invalid pipeCount for resevation", __FUNCTION__); + } + } + else { + ALOGD_IF(isDebug(), "%s: 1st vid frame for VPU", __FUNCTION__); + prop->firstBuffer = true; + } + + // marking the layer pipes for vpu. + prop->vpuLayer = true; + prop->layer = layer; + layer->flags |= HWC_VPU_PIPE; + + // getting image width and height + prop->width = layer->displayFrame.right - layer->displayFrame.left; + prop->height = layer->displayFrame.bottom - layer->displayFrame.top; + + //setting source crop = dest crop (only for layers drawn by vpu, + // since we know it will be scaled up/down by vpu) + layer->sourceCropf.left = 0.0; + layer->sourceCropf.top = 0.0; + layer->sourceCropf.right = (float) prop->width; + layer->sourceCropf.bottom = (float) prop->height; + + // setting the flag so that mdpComp wont recognize it as the MDPCOMP + layerProp[i].mFlags |= HWC_VPUCOMP; + + // TODO: need to get the proper solution for color fill + + // storing locally the vpu supported format from VFM + prop->format = vLayer->vpuOutPixFmt; + ALOGD_IF(isDebug(), "%s: MDP: sourceCropf: w:%d h:%d format:%d", + __FUNCTION__, prop->width, prop->height, prop->format); + } + } + return 0; +} + +bool VPUClient::allocResLayerPipes(hwc_context_t* ctx, int dpy, + hwc_display_contents_1_t* list) +{ + overlay::Overlay& ov = *ctx->mOverlay; + for (unsigned int i=0; i<(list->numHwLayers); ++i) { + int pipeid = -1; + VpuLayerProp* prop = &mProp[dpy][i]; + + // checking if there is already a reserved pipe for this layer + // then use the same allocated pipe for this layer + getPipeId(prop, pipeid); + + if (pipeid != -1) { + // there is a reserved pipe for this layer. + ovutils::eDest dest = ov.reservePipe(pipeid); + if (dest == ovutils::OV_INVALID) { + ALOGE("%s: Unable to get reserved pipe: layer#%d", + __FUNCTION__, i); + return false; + } + + // setting dest locally + setDest(prop, dest); + ALOGD_IF(isDebug(), "%s: Reserving pipe:%d, dest:%d ", __FUNCTION__, + pipeid, dest); + } + else { + ALOGD_IF(isDebug2(), "%s: No reserved pipe for layer:%d", + __FUNCTION__, i); + } + } + return true; } -int VPUClient::draw(hwc_context_t *ctx, - hwc_display_contents_1_t* list) +bool VPUClient::allocLayerPipes(hwc_context_t* ctx, int dpy, + hwc_display_contents_1_t* list) +{ + // checking if the pipes are reserved for any layer, + // if yes, then updating the index of the pipes + if (!allocResLayerPipes(ctx, dpy, list)) { + ALOGE("%s: Reserved pipe alloc failed", __FUNCTION__); + return false; + } + + for (unsigned int i=0; i<(list->numHwLayers); ++i) { + hwc_layer_1_t* layer = &list->hwLayers[i]; + private_handle_t *hnd = (private_handle_t *)layer->handle; + VpuLayerProp* prop = &mProp[dpy][i]; + int pipe = -1; + overlay::Overlay& ov = *ctx->mOverlay; + + // only care about the layers supported by VPU + if (!prop->vpuLayer) + continue; + + // continue if this layer has reserved pipe + getPipeId(prop, pipe); + if (pipe != -1) + continue; + + ovutils::eDest dest = ov.nextPipe(ovutils::OV_MDP_PIPE_VG, dpy, + overlay::Overlay::MIXER_DEFAULT); + if (dest == ovutils::OV_INVALID) { + ALOGE("%s: Unable to allocate pipe for layer#%d", __FUNCTION__, i); + return false; + } + + // setting dest locally + setDest(prop, dest); + ALOGD_IF(isDebug(), "%s: Newly allocated pipe_dest:%d", __FUNCTION__, + dest); + } + return true; +} + +bool VPUClient::allocResLayerPipesSplit(hwc_context_t* ctx, int dpy, + hwc_display_contents_1_t* list) +{ + overlay::Overlay& ov = *ctx->mOverlay; + for (unsigned int i=0; i<(list->numHwLayers); ++i) { + int lpipeid = -1; + int rpipeid = -1; + VpuLayerProp* prop = &mProp[dpy][i]; + + // checking if there is already a reserved pipe for this layer + // then use the same allocated pipe for this layer + getPipeId(prop, lpipeid, rpipeid); + + if (lpipeid != -1 && rpipeid != -1) { + ovutils::eDest ldest = ov.reservePipe(lpipeid); + if (ldest == ovutils::OV_INVALID) { + ALOGD_IF(isDebug(), "%s: Unable to get reserved pipe-lsplit: " + "layer#%d", __FUNCTION__, i); + return false; + } + + ovutils::eDest rdest = ov.reservePipe(rpipeid); + if (rdest == ovutils::OV_INVALID) { + ALOGD_IF(isDebug(), "%s: Unable to get reserved pipe-rsplit: " + "layer#%d", __FUNCTION__, i); + return false; + } + + setDest(prop, ldest, rdest); + ALOGD_IF(isDebug(), "%s: Reserve lpipe:%d, ldest:%d, rpipe:%d, " + "rdest:%d", __FUNCTION__, lpipeid, ldest, rpipeid, rdest); + } + else if (lpipeid != -1 || rpipeid != -1) { + ALOGE("%s: Bug: only one pipe reserved!", __FUNCTION__); + return false; + } + } + return true; +} + +bool VPUClient::allocLayerPipesSplit(hwc_context_t* ctx, int dpy, + hwc_display_contents_1_t* list) +{ + // checking if the pipes are reserved for any layer, + // if yes, then updating the index of the pipes + if (!allocResLayerPipesSplit(ctx, dpy, list)) { + ALOGE("%s: Reserved pipe alloc failed", __FUNCTION__); + return false; + } + + for (unsigned int i=0; i<(list->numHwLayers); ++i) { + hwc_layer_1_t* layer = &list->hwLayers[i]; + private_handle_t *hnd = (private_handle_t *)layer->handle; + VpuLayerProp* prop = &mProp[dpy][i]; + int lpipe, rpipe; + overlay::Overlay& ov = *ctx->mOverlay; + + // only care about the layers supported by VPU + if (!prop->vpuLayer) + continue; + + // only care about the layers supported by VPU + getPipeId(prop, lpipe, rpipe); + if (lpipe != -1 && rpipe != -1) + continue; + + ovutils::eDest ldest = ov.nextPipe(ovutils::OV_MDP_PIPE_VG, dpy, + overlay::Overlay::MIXER_LEFT); + if (ldest == ovutils::OV_INVALID) { + ALOGE("%s: Unable to allocate pipe for layer#%d", __FUNCTION__, i); + return false; + } + + ovutils::eDest rdest = ov.nextPipe(ovutils::OV_MDP_PIPE_VG, dpy, + overlay::Overlay::MIXER_RIGHT); + if (rdest == ovutils::OV_INVALID) { + ALOGE("%s: Unable to allocate pipe for layer#%d", __FUNCTION__, i); + return false; + } + + // setting dests locally + setDest(prop, ldest, rdest); + ALOGD_IF(isDebug(), "%s: Newly allocated ldest:%d rdest:%d", + __FUNCTION__, ldest, rdest); + } + return true; +} + +bool VPUClient::configureLayers(hwc_context_t* ctx, int dpy, + hwc_display_contents_1_t* list) +{ + for (unsigned int i=0; i<(list->numHwLayers); ++i) { + VpuLayerProp* prop = &mProp[dpy][i]; + hwc_layer_1_t* layer = &list->hwLayers[i]; + + if (!prop->vpuLayer) + continue; + + eMdpFlags mdpFlags = OV_MDP_BACKEND_COMPOSITION; + eZorder zOrder = static_cast<eZorder>(i); + eIsFg isFg = IS_FG_OFF; + setPipeCount(prop, 1); + eDest dest = (eDest) getDest(prop, 0); + + ALOGD_IF(isDebug(),"%s: configuring: layer:%p z_order:%d dest_pipe:%d", + __FUNCTION__, layer, zOrder, dest); + + if (configureNonSplit(ctx, layer, dpy, mdpFlags, zOrder, isFg, + dest, NULL)) { + ALOGE("%s: Failed to configure overlay for layer %d", + __FUNCTION__, i); + return false; + } + ALOGD_IF(isDebug2(), "%s: layer:%d configured!", __FUNCTION__, i); + + // Pipe is successfully allocated for this layer; retrieving it from + // overlay + int pipeId = ctx->mOverlay->getPipeId((eDest) getDest(prop, 0)); + setPipeId(prop, pipeId); + + ALOGD_IF(isDebug(), "%s: allocated pipe:%d layer:%d", __FUNCTION__, + pipeId, i); + } + return true; +} + +bool VPUClient::configureLayersSplit(hwc_context_t* ctx, int dpy, + hwc_display_contents_1_t* list) +{ + for (unsigned int i=0; i<(list->numHwLayers); ++i) { + VpuLayerProp* prop = &mProp[dpy][i]; + hwc_layer_1_t* layer = &list->hwLayers[i]; + + if (!prop->vpuLayer) + continue; + + eMdpFlags mdpFlags = OV_MDP_BACKEND_COMPOSITION; + eZorder zOrder = static_cast<eZorder>(i); + eIsFg isFg = IS_FG_OFF; + setPipeCount(prop, 2); + eDest ldest = (eDest) getDest(prop, 0); + eDest rdest = (eDest) getDest(prop, 1); + + ALOGD_IF(isDebug(),"%s: configuring: layer:%p z_order:%d dest_pipeL:%d" + "dest_pipeR:%d",__FUNCTION__, layer, zOrder, ldest, rdest); + + if (configureSplit(ctx, layer, dpy, mdpFlags, zOrder, isFg, ldest, + rdest, NULL)) { + ALOGE("%s: Failed to configure overlay for layer %d", + __FUNCTION__, i); + return false; + } + ALOGD_IF(isDebug2(), "%s: layer:%d configured!", __FUNCTION__, i); + + // Pipe is successfully allocated for this layer; retrieving it from + // overlay + int lpipeId = ctx->mOverlay->getPipeId((eDest) getDest(prop, 0)); + int rpipeId = ctx->mOverlay->getPipeId((eDest) getDest(prop, 1)); + setPipeId(prop, lpipeId, rpipeId); + + ALOGD_IF(isDebug(), "%s: allocated l-pipe:%d - r-pipe:%d for layer:%d", + __FUNCTION__, lpipeId, rpipeId, i); + } + return true; +} + +void VPUClient::setMDPCompLayerFlags(hwc_context_t *ctx, int dpy, + hwc_display_contents_1_t* list) +{ + LayerProp *layerProp = ctx->layerProp[dpy]; + + // disableGpu only disables gpu for video layer. The expected behavior is to + // show a blank screen in case VPU doesnt support a video layer, and gpu + // fallback is disabled by the user. + bool disableGpu = false; + char property[PROPERTY_VALUE_MAX]; + if ((property_get("persist.hwc.noGpuFallback", property, NULL) > 0) && + (!strncmp(property, "1", PROPERTY_VALUE_MAX ) || + (!strncasecmp(property,"true", PROPERTY_VALUE_MAX )))) { + ALOGD_IF(isDebug(), "%s: GPU fallback is disabled through prop", + __FUNCTION__); + disableGpu = true; + } + + // no layers are supported by vpu + if (mGpuFallback && !disableGpu) { + ALOGD_IF(isDebug(), "%s: No VPU supported layers - Falling back to GPU", + __FUNCTION__); + return; + } + + for (unsigned int i=0; i<(list->numHwLayers); ++i) { + hwc_layer_1_t* layer = &(list->hwLayers[i]); + VpuLayerProp* prop = &mProp[dpy][i]; + private_handle_t *hnd = (private_handle_t *)layer->handle; + + // mark vpu layers as HWC_OVERLAY, and those video layers that + // are not supported by vpu and gpu fallback is disabled by the + // user. + if (prop->vpuLayer || (isYuvBuffer(hnd) && disableGpu)) { + layer->compositionType = HWC_OVERLAY; + layer->hints |= HWC_HINT_CLEAR_FB; + ALOGD_IF(isDebug(), "%s: Marking layer:%d as overlay", + __FUNCTION__, i); + } + } +} + +int VPUClient::prepare(hwc_context_t *ctx, int display, + hwc_display_contents_1_t* list) +{ + if (!mVPU) { + return -1; + } + + const int numLayers = ctx->listStats[display].numAppLayers; + //number of app layers exceeds MAX_NUM_APP_LAYERS fall back to GPU + //do not cache the information for next draw cycle. + if (numLayers > MAX_NUM_APP_LAYERS) { + ALOGE("%s: Number of App layers exceeded the limit ",__FUNCTION__); + return -1; + } + + if (setupVpuSession(ctx, display, list)) { + ALOGD_IF(isDebug(), "%s: Vpu session setup failed! ",__FUNCTION__); + return -1; + } + + LayerProp *layerProp = ctx->layerProp[display]; + bool isSplit = isDisplaySplit(ctx, display); + ALOGD_IF(isDebug2(), "%s: Split Pipe:%d ", __FUNCTION__, + isSplit ? 1 : 0); + + // setting up the layer + LayerList *vpuList = vList[display]; + vpuList->numLayers = list->numHwLayers; + + // Prepare FB Update at z-0 + if (numLayers > mNumVpuLayers) { + if (!ctx->mFBUpdate[display]->prepare(ctx, list, mNumVpuLayers)) { + ALOGD_IF(isDebug(), "%s configure framebuffer failed", + __FUNCTION__); + return -1; + } + } + + // Allocate pipe for layers + if (!isSplit ? !allocLayerPipes(ctx, display, list) : + !allocLayerPipesSplit(ctx, display, list)) { + ALOGD_IF(isDebug(), "%s: Unable to allocate MDP pipes", __FUNCTION__); + return -1; + } + + // Configure layers + if (!isSplit ? !configureLayers(ctx, display, list) : + !configureLayersSplit(ctx, display, list)) { + ALOGD_IF(isDebug(), "%s: Unable to configure MDP pipes", __FUNCTION__); + return -1; + } + + // Set layer flags for MDP/VPU composition + setMDPCompLayerFlags(ctx, display, list); + + for (unsigned int i=0; i<(list->numHwLayers); ++i) { + VpuLayerProp* prop = &mProp[display][i]; + + if (!prop->vpuLayer) + continue; + + hwc_layer_1_t *layer = &list->hwLayers[i]; + Layer *vLayer = &vpuList->layers[i]; + + // re-storing the sourceCropf, as it was changed in setVpuSession for + // overlay set + layer->sourceCropf = prop->sourceCropf; + + // updating the pipe info inside vfm list + if ( prop->pipeCount > 0 && prop->pipeCount <= MAX_PIPES_PER_LAYER ) { + vLayer->sDestPipes.numPipes = prop->pipeCount; + + for (int j=0; j < prop->pipeCount; ++j) { + // Setting pipe for VPU + vLayer->sDestPipes.pipe[j] = prop->pipeID[j]; + } + } + } + + if (mVPU->prepare((DISPLAY_ID)display, vpuList) != NO_ERROR) { + //error in vpu prepare + ALOGE("%s: ERROR in VPU::prepare", __func__); + return -1; + } + return 0; +} + +bool VPUClient::queueHandle(hwc_context_t* ctx, VpuLayerProp* prop, + private_handle_t* hnd) +{ + overlay::Overlay& ov = *ctx->mOverlay; + ovutils::eDest dest = (eDest) getDest(prop, 0); + + int fd = hnd->fd; + uint32_t offset = hnd->offset; + + if (dest != ovutils::OV_INVALID) { + if (!ov.queueBuffer(fd, offset, dest)) { + ALOGE("%s: queueBuffer failed", __FUNCTION__); + return false; + } + else { + ALOGD_IF(isDebug(), "%s: Queue handle successful: hnd:0x%x " + "dest:%d", __FUNCTION__, (unsigned int) hnd, dest); + } + } + else { + ALOGE("%s: Invalid Dest: dest:%d", __FUNCTION__, dest); + return false; + } + return true; +} + +bool VPUClient::queueHandleSplit(hwc_context_t* ctx, VpuLayerProp* prop, + private_handle_t* hnd) +{ + overlay::Overlay& ov = *ctx->mOverlay; + ovutils::eDest ldest = (eDest) getDest(prop, 0); + ovutils::eDest rdest = (eDest) getDest(prop, 1); + + int fd = hnd->fd; + uint32_t offset = hnd->offset; + + // play left mixer + if (ldest != ovutils::OV_INVALID) { + ALOGD_IF(isDebug(), "%s: Queuing left mixer", __FUNCTION__); + if (!ov.queueBuffer(fd, offset, ldest)) { + ALOGE("%s: queueBuffer failed for left mixer ", __FUNCTION__); + return false; + } + else { + ALOGD_IF(isDebug(), "%s: Queue left-handle successful: hnd:0x%x " + "ldest:%d", __FUNCTION__, (unsigned int) hnd, ldest); + } + } + else { + ALOGE("%s: Invalid l-Split Dest", __FUNCTION__); + return false; + } + + // play right mixer + if (rdest != ovutils::OV_INVALID) { + ALOGD_IF(isDebug(), "%s: Queuing right mixer", __FUNCTION__); + if (!ov.queueBuffer(fd, offset, rdest)) { + ALOGE("%s: queueBuffer failed for right mixer ", __FUNCTION__); + return false; + } + else { + ALOGD_IF(isDebug(), "%s: Queue right-handle successful: hnd:0x%x " + "rdest:%d", __FUNCTION__, (unsigned int) hnd, rdest); + } + } + else { + ALOGE("%s: Invalid r-Split Dest", __FUNCTION__); + return false; + } + return true; +} + +bool VPUClient::drawDummyLayers(hwc_context_t* ctx, int dpy, + hwc_display_contents_1_t* list) { int err = 0; - if(!mVPU) - return err; - // Queue buffers to VPU - return err; + for (unsigned int i=0; i<(list->numHwLayers); ++i) { + VpuLayerProp* prop = &mProp[dpy][i]; + + if (!prop->vpuLayer) + continue; + + // displaying blank screen for the first frame + if (prop->firstBuffer) { + ALOGD_IF(isDebug(), "%s: Displaying first (blank) frame", + __FUNCTION__); + prop->firstBuffer = false; + + if (mHnd[dpy][i] != NULL) + free_buffer(mHnd[dpy][i]); + + // TO-FIX: out dummy buffer is currently allocated based on + // RGB888 format + err = alloc_buffer(&mHnd[dpy][i], prop->width, prop->height, + HAL_PIXEL_FORMAT_RGB_888, GRALLOC_USAGE_PRIVATE_IOMMU_HEAP); + if (err == -1) { + ALOGE("%s: Dummy buffer allocation failed!", __FUNCTION__); + return false; + } + + private_handle_t* hnd = mHnd[dpy][i]; + if (prop->format == HAL_PIXEL_FORMAT_RGB_888) { + ALOGD_IF(isDebug(), "%s: Format: RGB888", __FUNCTION__); + memset((void*)hnd->base, 0x0, hnd->size); + } + else if (prop->format == + HAL_PIXEL_FORMAT_YCbCr_422_I_10BIT_COMPRESSED) { + ALOGD_IF(isDebug(), "%s: Format: 10BIT_BWC", __FUNCTION__); + memset((void*)hnd->base, 0xaa, hnd->size); + } + else { + ALOGE("%s: Error! Wrong VPU out format - layer:%d", + __FUNCTION__, i); + return false; + } + + bool isSplit = isDisplaySplit(ctx, dpy); + if (!isSplit ? !queueHandle(ctx, prop, hnd) : + !queueHandleSplit(ctx, prop, hnd)) { + ALOGD_IF(isDebug(), "%s: Error in queue handle: layer:%d", + __FUNCTION__, i); + return false; + } + else { + ALOGD_IF(isDebug(), "%s: queue handle successful: hnd:0x%x " + "layer:%d", __FUNCTION__, (unsigned int) hnd, i); + } + } + } + return true; +} + +int VPUClient::predraw(hwc_context_t *ctx, int display, + hwc_display_contents_1_t* list) +{ + if (!mVPU) { + return -1; + } + + if (!ctx || !list) { + ALOGE("%s: invalid contxt or list",__FUNCTION__); + return -1; + } + + if (ctx->listStats[display].numAppLayers > MAX_NUM_APP_LAYERS) { + ALOGE("%s: Exceeding max layer count", __FUNCTION__); + return -1; + } + + // Although all the video layers are composed through VPU, but still need to + // queue the first buffer (blank screen) to mdp in order to initialize the + // settings + if (!drawDummyLayers(ctx, display, list)) { + ALOGE("%s: Failed to draw the first layer through overlay", + __FUNCTION__); + return -1; + } + return 0; +} + +int VPUClient::draw(hwc_context_t *ctx, int display, + hwc_display_contents_1_t* list) +{ + if (!mVPU) { + return -1; + } + + LayerList *vpuList = vList[display]; + vpuList->numLayers = list->numHwLayers; + + for (unsigned int i=0; i<(list->numHwLayers); ++i) { + hwc_layer_1_t *layer = &list->hwLayers[i]; + Layer *vLayer = &vpuList->layers[i]; + + // setting layer info again for the update content. + setLayer(layer, vLayer); + } + + // queuing the buffer to VPU + if (mVPU->draw((DISPLAY_ID)display, vpuList) != NO_ERROR) { + //error in vpu draw + ALOGE("%s: ERROR in VPU::draw", __func__); + return -1; + } + + ALOGD_IF(isDebug2(), "%s: Done VFM draw", __FUNCTION__); + + LayerProp *layerProp = ctx->layerProp[display]; + // setting releaseFenceFd for the vpu layer + for (unsigned int i=0; i<(vpuList->numLayers); ++i) { + + VpuLayerProp* prop = &mProp[display][i]; + if (!prop->vpuLayer) + continue; + + hwc_layer_1_t *layer = &list->hwLayers[i]; + Layer *vLayer = &vpuList->layers[i]; + + // TODO: Fix properly once the releaseFenceFd is implemented + layer->releaseFenceFd = vLayer->releaseFenceFd; + ALOGD_IF(isDebug(), "%s: releaseFd:%d for layer:%d", __FUNCTION__, + layer->releaseFenceFd, i); + } + return 0; +} + +int VPUClient::getLayerIdx(int dpy, hwc_layer_1_t *layer) +{ + for (int i=0; i < MAX_NUM_APP_LAYERS; ++i) { + VpuLayerProp* prop = &mProp[dpy][i]; + + if (!prop->vpuLayer) + continue; + + if (prop->layer == layer) { + ALOGD_IF(isDebug2(), "%s: OUT - dpy:%d", __FUNCTION__, dpy); + return i; + } + } + return -1; +} + +int VPUClient::getLayerFormat(int dpy, hwc_layer_1_t *layer) +{ + if (!mVPU) { + return -1; + } + + int idx = -1; + if ((idx = getLayerIdx(dpy, layer)) == -1) { + ALOGE("%s: Layer not found!", __FUNCTION__); + return -1; + } + + VpuLayerProp* prop = &mProp[dpy][idx]; + ALOGD_IF(isDebug(), "%s: layer:%d format:0x%x", __FUNCTION__, idx, + (unsigned int) prop->format); + + return prop->format; +} + +int VPUClient::getWidth(int dpy, hwc_layer_1_t *layer) +{ + if (!mVPU) { + return -1; + } + + int idx = -1; + if ((idx = getLayerIdx(dpy, layer)) == -1) { + ALOGE("%s: Layer not found!", __FUNCTION__); + return -1; + } + + VpuLayerProp* prop = &mProp[dpy][idx]; + ALOGD_IF(isDebug(), "%s: layer:%d width:%d", __FUNCTION__, idx, + prop->width); + + return prop->width; +} + +int VPUClient::getHeight(int dpy, hwc_layer_1_t *layer) +{ + if (!mVPU) { + return -1; + } + + int idx = -1; + if ((idx = getLayerIdx(dpy, layer)) == -1) { + ALOGE("%s: Layer not found!", __FUNCTION__); + return -1; + } + + VpuLayerProp* prop = &mProp[dpy][idx]; + ALOGD_IF(isDebug(), "%s: layer:%d height:%d", __FUNCTION__, idx, + prop->height); + + return prop->height; +} + +// TODO: getter function has side-effect. Need to cleanup +void VPUClient::getPipeId(VpuLayerProp* prop, int &pipe) +{ + pipe = (prop->pipeCount == 1) ? (prop->pipeID[0]) : -1; +} + +void VPUClient::getPipeId(VpuLayerProp* prop, int &lPipe, int &rPipe) +{ + lPipe = (prop->pipeCount == 2) ? (prop->pipeID[0]) : -1; + rPipe = (prop->pipeCount == 2) ? (prop->pipeID[1]) : -1; +} + +int VPUClient::getDest(VpuLayerProp* prop, int pipenum) +{ + return (prop->pipeCount > 0) ? (prop->dest[pipenum]) : -1; +} + +void VPUClient::setPipeCount(VpuLayerProp* prop, int count) +{ + prop->pipeCount = count; +} + +void VPUClient::setPipeId(VpuLayerProp* prop, int lPipeId, int rPipeId) +{ + prop->pipeCount = 2; + prop->pipeID[0] = lPipeId; + prop->pipeID[1] = rPipeId; +} + +void VPUClient::setPipeId(VpuLayerProp* prop, int pipeId) +{ + prop->pipeCount = 1; + prop->pipeID[0] = pipeId; +} + +void VPUClient::setDest(VpuLayerProp* prop, int lDest, int rDest) +{ + prop->dest[0] = lDest; + prop->dest[1] = rDest; +} + +void VPUClient::setDest(VpuLayerProp* prop, int dest) +{ + prop->dest[0] = dest; +} + +bool VPUClient::supportedVPULayer(VpuLayerProp* prop) +{ + if (!prop->vpuLayer) + return false; + + return true; +} + +bool VPUClient::supportedVPULayer(int dpy, hwc_layer_1_t *layer) +{ + if (!mVPU) { + return false; + } + + int idx = -1; + if ((idx = getLayerIdx(dpy, layer)) == -1) { + ALOGD_IF(isDebug(), "%s: Layer not found!", __FUNCTION__); + return false; + } + return true; } int VPUClient::processCommand(uint32_t command, - const Parcel* inParcel, Parcel* outParcel) + const Parcel* inParcel, Parcel* outParcel) { - if(!mVPU) + if (!mVPU) return 0; - //XXX: Enable when VPU enables it - //return mVPU->processCommand(command, inParcel, outParcel); - return 0; + + return mVPU->processCommand(command, inParcel, outParcel); } }; // namespace qhwc diff --git a/libhwcomposer/hwc_vpuclient.h b/libhwcomposer/hwc_vpuclient.h index 99855172..bb2a4b65 100644 --- a/libhwcomposer/hwc_vpuclient.h +++ b/libhwcomposer/hwc_vpuclient.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013 The Linux Foundation. All rights reserved. +* Copyright (c) 2013-2014 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 @@ -31,13 +31,20 @@ #define HWC_VPU_H #include <sys/types.h> +#include "hwc_utils.h" + +#define MAX_PIPES_PER_LAYER 2 //Forward declarations struct hwc_display_contents_1; typedef struct hwc_display_contents_1 hwc_display_contents_1_t; +struct hwc_layer_1; +typedef struct hwc_layer_1 hwc_layer_1_t; struct hwc_context_t; + namespace vpu { class VPU; +struct LayerList; }; namespace android { class Parcel; @@ -47,21 +54,91 @@ namespace qhwc { class VPUClient { public: - VPUClient(); + VPUClient(hwc_context_t *ctx); ~VPUClient(); - int prepare(hwc_context_t *ctx, hwc_display_contents_1_t* list); - - int draw(hwc_context_t *ctx, hwc_display_contents_1_t* list); - + int setupVpuSession(hwc_context_t *ctx, int display, + hwc_display_contents_1_t* list); + int prepare(hwc_context_t *ctx, int display, + hwc_display_contents_1_t* list); + int predraw(hwc_context_t *ctx, int display, + hwc_display_contents_1_t* list); + int draw(hwc_context_t *ctx, int display, + hwc_display_contents_1_t* list); int processCommand(uint32_t command, - const android::Parcel* inParcel, android::Parcel* outParcel); + const android::Parcel* inParcel, android::Parcel* outParcel); + int getLayerFormat(int dpy, hwc_layer_1_t *layer); + int getWidth(int dpy, hwc_layer_1_t *layer); + int getHeight(int dpy, hwc_layer_1_t *layer); + bool supportedVPULayer(int dpy, hwc_layer_1_t *layer); private: vpu::VPU *mVPU; void* mVPULib; + /* VpuLayerProp struct: + * This struct corresponds to only one layer + * pipeCount: number of pipes required for a layer + * pipeID[]: pipe ids corresponding to the layer + */ + struct VpuLayerProp { + int format; + int width; + int height; + int pipeCount; + bool vpuLayer; + bool firstBuffer; + hwc_frect_t sourceCropf; + hwc_layer_1_t *layer; + int pipeID[MAX_PIPES_PER_LAYER]; + int dest[MAX_PIPES_PER_LAYER]; + }; + int mNumVpuLayers; /* total num of vpu supported layers */ + int mGpuFallback; /* all layers are not supported by vpu */ + + VpuLayerProp mProp[HWC_NUM_DISPLAY_TYPES][MAX_NUM_APP_LAYERS]; + int mDebugLogs; + private_handle_t *mHnd[HWC_NUM_DISPLAY_TYPES][MAX_NUM_APP_LAYERS]; + vpu::LayerList *vList[HWC_NUM_DISPLAY_TYPES]; + + /* Private debug functions */ + int32_t isDebug() { return (mDebugLogs >= 1); } + int32_t isDebug2() { return (mDebugLogs >= 2); } + + /* Private Get/Set functions */ + int getLayerIdx(int dpy, hwc_layer_1_t *layer); + void getPipeId(VpuLayerProp* prop, int &pipe); + void getPipeId(VpuLayerProp* prop, int &lPipe, int &rPipe); + int getDest(VpuLayerProp* prop, int pipenum); + void setPipeCount(VpuLayerProp* prop, int count); + void setPipeId(VpuLayerProp* prop, int lPipeId, int rPipeId); + void setPipeId(VpuLayerProp* prop, int pipeId); + void setDest(VpuLayerProp* prop, int lDest, int rDest); + void setDest(VpuLayerProp* prop, int dest); + + /* Private implementations */ + bool supportedVPULayer(VpuLayerProp* prop); + bool allocResLayerPipes(hwc_context_t* ctx, int dpy, + hwc_display_contents_1_t* list); + bool allocLayerPipes(hwc_context_t* ctx, int dpy, + hwc_display_contents_1_t* list); + bool allocResLayerPipesSplit(hwc_context_t* ctx, int dpy, + hwc_display_contents_1_t* list); + bool allocLayerPipesSplit(hwc_context_t* ctx, int dpy, + hwc_display_contents_1_t* list); + bool configureLayers(hwc_context_t* ctx, int dpy, + hwc_display_contents_1_t* list); + bool configureLayersSplit(hwc_context_t* ctx, int dpy, + hwc_display_contents_1_t* list); + void setMDPCompLayerFlags(hwc_context_t *ctx, int dpy, + hwc_display_contents_1_t* list); + bool drawDummyLayers(hwc_context_t* ctx, int dpy, + hwc_display_contents_1_t* list); + bool queueHandle(hwc_context_t* ctx, VpuLayerProp* prop, + private_handle_t* hnd); + bool queueHandleSplit(hwc_context_t* ctx, VpuLayerProp* prop, + private_handle_t* hnd); }; // class VPU }; // namespace qhwc #endif /* end of include guard: HWC_VPU_H */ |