/* * 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 #include "hwc_vpuclient.h" #include #include "hwc_fbupdate.h" #include using namespace vpu; using namespace android; using namespace overlay::utils; namespace ovutils = overlay::utils; namespace qhwc { VPUClient::VPUClient(hwc_context_t *ctx) { mVPULib = dlopen("libvpu.so", RTLD_NOW); 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); } void setLayer(hwc_layer_1_t *layer, Layer *vLayer) { // 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; } 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(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(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; 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) { if (!mVPU) return 0; return mVPU->processCommand(command, inParcel, outParcel); } }; // namespace qhwc