/* * Copyright (C) 2012-2013, The Linux Foundation. All rights reserved. * Not a Contribution, Apache license notifications and license are retained * for attribution purposes only. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "hwc_mdpcomp.h" #include #include "external.h" #include "qdMetaData.h" #include "mdp_version.h" namespace qhwc { namespace ovutils = overlay::utils; //==============MDPComp======================================================== IdleInvalidator *MDPComp::idleInvalidator = NULL; bool MDPComp::sIdleFallBack = false; bool MDPComp::sDebugLogs = false; bool MDPComp::sEnabled = false; MDPComp* MDPComp::getObject(const int& width) { if(width <= MAX_DISPLAY_DIM) { return new MDPCompLowRes(); } else { return new MDPCompHighRes(); } } void MDPComp::dump(android::String8& buf) { dumpsys_log(buf, " MDP Composition: "); dumpsys_log(buf, "MDPCompState=%d\n", mState); //XXX: Log more info } bool MDPComp::init(hwc_context_t *ctx) { if(!ctx) { ALOGE("%s: Invalid hwc context!!",__FUNCTION__); return false; } if(!setupBasePipe(ctx)) { ALOGE("%s: Failed to setup primary base pipe", __FUNCTION__); return false; } char property[PROPERTY_VALUE_MAX]; sEnabled = false; if((property_get("persist.hwc.mdpcomp.enable", property, NULL) > 0) && (!strncmp(property, "1", PROPERTY_VALUE_MAX ) || (!strncasecmp(property,"true", PROPERTY_VALUE_MAX )))) { sEnabled = true; } sDebugLogs = false; if(property_get("debug.mdpcomp.logs", property, NULL) > 0) { if(atoi(property) != 0) sDebugLogs = true; } unsigned long idle_timeout = DEFAULT_IDLE_TIME; if(property_get("debug.mdpcomp.idletime", property, NULL) > 0) { if(atoi(property) != 0) idle_timeout = atoi(property); } //create Idle Invalidator idleInvalidator = IdleInvalidator::getInstance(); if(idleInvalidator == NULL) { ALOGE("%s: failed to instantiate idleInvalidator object", __FUNCTION__); } else { idleInvalidator->init(timeout_handler, ctx, idle_timeout); } return true; } void MDPComp::timeout_handler(void *udata) { struct hwc_context_t* ctx = (struct hwc_context_t*)(udata); if(!ctx) { ALOGE("%s: received empty data in timer callback", __FUNCTION__); return; } if(!ctx->proc) { ALOGE("%s: HWC proc not registered", __FUNCTION__); return; } sIdleFallBack = true; /* Trigger SF to redraw the current frame */ ctx->proc->invalidate(ctx->proc); } void MDPComp::setMDPCompLayerFlags(hwc_context_t *ctx, hwc_display_contents_1_t* list) { const int dpy = HWC_DISPLAY_PRIMARY; LayerProp *layerProp = ctx->layerProp[dpy]; for(int index = 0; index < ctx->listStats[dpy].numAppLayers; index++ ) { hwc_layer_1_t* layer = &(list->hwLayers[index]); layerProp[index].mFlags |= HWC_MDPCOMP; layer->compositionType = HWC_OVERLAY; layer->hints |= HWC_HINT_CLEAR_FB; } } void MDPComp::unsetMDPCompLayerFlags(hwc_context_t* ctx, hwc_display_contents_1_t* list) { const int dpy = HWC_DISPLAY_PRIMARY; LayerProp *layerProp = ctx->layerProp[dpy]; for (int index = 0 ; index < ctx->listStats[dpy].numAppLayers; index++) { if(layerProp[index].mFlags & HWC_MDPCOMP) { layerProp[index].mFlags &= ~HWC_MDPCOMP; } if(list->hwLayers[index].compositionType == HWC_OVERLAY) { list->hwLayers[index].compositionType = HWC_FRAMEBUFFER; } } } /* * Sets up BORDERFILL as default base pipe and detaches RGB0. * Framebuffer is always updated using PLAY ioctl. */ bool MDPComp::setupBasePipe(hwc_context_t *ctx) { const int dpy = HWC_DISPLAY_PRIMARY; int fb_stride = ctx->dpyAttr[dpy].stride; int fb_width = ctx->dpyAttr[dpy].xres; int fb_height = ctx->dpyAttr[dpy].yres; int fb_fd = ctx->dpyAttr[dpy].fd; mdp_overlay ovInfo; msmfb_overlay_data ovData; memset(&ovInfo, 0, sizeof(mdp_overlay)); memset(&ovData, 0, sizeof(msmfb_overlay_data)); ovInfo.src.format = MDP_RGB_BORDERFILL; ovInfo.src.width = fb_width; ovInfo.src.height = fb_height; ovInfo.src_rect.w = fb_width; ovInfo.src_rect.h = fb_height; ovInfo.dst_rect.w = fb_width; ovInfo.dst_rect.h = fb_height; ovInfo.id = MSMFB_NEW_REQUEST; if (ioctl(fb_fd, MSMFB_OVERLAY_SET, &ovInfo) < 0) { ALOGE("Failed to call ioctl MSMFB_OVERLAY_SET err=%s", strerror(errno)); return false; } ovData.id = ovInfo.id; if (ioctl(fb_fd, MSMFB_OVERLAY_PLAY, &ovData) < 0) { ALOGE("Failed to call ioctl MSMFB_OVERLAY_PLAY err=%s", strerror(errno)); return false; } return true; } void MDPComp::reset(hwc_context_t *ctx, hwc_display_contents_1_t* list ) { //Reset flags and states unsetMDPCompLayerFlags(ctx, list); if(mCurrentFrame.pipeLayer) { for(int i = 0 ; i < mCurrentFrame.count; i++ ) { if(mCurrentFrame.pipeLayer[i].pipeInfo) { delete mCurrentFrame.pipeLayer[i].pipeInfo; mCurrentFrame.pipeLayer[i].pipeInfo = NULL; } } free(mCurrentFrame.pipeLayer); mCurrentFrame.pipeLayer = NULL; } mCurrentFrame.count = 0; } void MDPComp::setVidInfo(hwc_layer_1_t *layer, ovutils::eMdpFlags &mdpFlags) { private_handle_t *hnd = (private_handle_t *)layer->handle; MetaData_t *metadata = (MetaData_t *)hnd->base_metadata; if(isSecureBuffer(hnd)) { ovutils::setMdpFlags(mdpFlags, ovutils::OV_MDP_SECURE_OVERLAY_SESSION); } if(metadata && (metadata->operation & PP_PARAM_INTERLACED) && metadata->interlaced) { ovutils::setMdpFlags(mdpFlags, ovutils::OV_MDP_DEINTERLACE); } } bool MDPComp::isWidthValid(hwc_context_t *ctx, hwc_layer_1_t *layer) { const int dpy = HWC_DISPLAY_PRIMARY; private_handle_t *hnd = (private_handle_t *)layer->handle; if(!hnd) { ALOGE("%s: layer handle is NULL", __FUNCTION__); return false; } int hw_w = ctx->dpyAttr[dpy].xres; int hw_h = ctx->dpyAttr[dpy].yres; hwc_rect_t sourceCrop = layer->sourceCrop; hwc_rect_t displayFrame = layer->displayFrame; hwc_rect_t crop = sourceCrop; int crop_w = crop.right - crop.left; int crop_h = crop.bottom - crop.top; hwc_rect_t dst = displayFrame; int dst_w = dst.right - dst.left; int dst_h = dst.bottom - dst.top; if(dst.left < 0 || dst.top < 0 || dst.right > hw_w || dst.bottom > hw_h) { hwc_rect_t scissor = {0, 0, hw_w, hw_h }; qhwc::calculate_crop_rects(crop, dst, scissor, layer->transform); crop_w = crop.right - crop.left; crop_h = crop.bottom - crop.top; } //Workaround for MDP HW limitation in DSI command mode panels where //FPS will not go beyond 30 if buffers on RGB pipes are of width < 5 if(crop_w < 5) return false; return true; } ovutils::eDest MDPComp::getMdpPipe(hwc_context_t *ctx, ePipeType type) { const int dpy = HWC_DISPLAY_PRIMARY; overlay::Overlay& ov = *ctx->mOverlay; ovutils::eDest mdp_pipe = ovutils::OV_INVALID; switch(type) { case MDPCOMP_OV_DMA: mdp_pipe = ov.nextPipe(ovutils::OV_MDP_PIPE_DMA, dpy); if(mdp_pipe != ovutils::OV_INVALID) { return mdp_pipe; } case MDPCOMP_OV_ANY: case MDPCOMP_OV_RGB: mdp_pipe = ov.nextPipe(ovutils::OV_MDP_PIPE_RGB, dpy); if(mdp_pipe != ovutils::OV_INVALID) { return mdp_pipe; } if(type == MDPCOMP_OV_RGB) { //Requested only for RGB pipe break; } case MDPCOMP_OV_VG: return ov.nextPipe(ovutils::OV_MDP_PIPE_VG, dpy); default: ALOGE("%s: Invalid pipe type",__FUNCTION__); return ovutils::OV_INVALID; }; return ovutils::OV_INVALID; } bool MDPComp::isDoable(hwc_context_t *ctx, hwc_display_contents_1_t* list) { //Number of layers const int dpy = HWC_DISPLAY_PRIMARY; int numAppLayers = ctx->listStats[dpy].numAppLayers; overlay::Overlay& ov = *ctx->mOverlay; int availablePipes = ov.availablePipes(dpy); if(numAppLayers < 1 || numAppLayers > MAX_PIPES_PER_MIXER || pipesNeeded(ctx, list) > availablePipes) { ALOGD_IF(isDebug(), "%s: Unsupported number of layers",__FUNCTION__); return false; } if(ctx->mExtDispConfiguring) { ALOGD_IF( isDebug(),"%s: External Display connection is pending", __FUNCTION__); return false; } if(isSecuring(ctx)) { ALOGD_IF(isDebug(), "%s: MDP securing is active", __FUNCTION__); return false; } if(ctx->mSecureMode) return false; //Check for skip layers if(isSkipPresent(ctx, dpy)) { ALOGD_IF(isDebug(), "%s: Skip layers are present",__FUNCTION__); return false; } if(ctx->listStats[dpy].needsAlphaScale && ctx->mMDP.version < qdutils::MDSS_V5) { ALOGD_IF(isDebug(), "%s: frame needs alpha downscaling",__FUNCTION__); return false; } //FB composition on idle timeout if(sIdleFallBack) { sIdleFallBack = false; ALOGD_IF(isDebug(), "%s: idle fallback",__FUNCTION__); return false; } //MDP composition is not efficient if layer needs rotator. for(int i = 0; i < numAppLayers; ++i) { // As MDP h/w supports flip operation, use MDP comp only for // 180 transforms. Fail for any transform involving 90 (90, 270). hwc_layer_1_t* layer = &list->hwLayers[i]; private_handle_t *hnd = (private_handle_t *)layer->handle; if((layer->transform & HWC_TRANSFORM_ROT_90) && (!isYuvBuffer(hnd) || !canRotate())) { ALOGD_IF(isDebug(), "%s: orientation involved",__FUNCTION__); return false; } if(!isYuvBuffer(hnd) && !isWidthValid(ctx,layer)) { ALOGD_IF(isDebug(), "%s: Buffer is of invalid width",__FUNCTION__); return false; } } return true; } bool MDPComp::setup(hwc_context_t* ctx, hwc_display_contents_1_t* list) { const int dpy = HWC_DISPLAY_PRIMARY; if(!ctx) { ALOGE("%s: invalid context", __FUNCTION__); return -1; } if(!allocLayerPipes(ctx, list, mCurrentFrame)) { ALOGD_IF(isDebug(), "%s: Falling back to FB", __FUNCTION__); return false; } for (int index = 0 ; index < mCurrentFrame.count; index++) { hwc_layer_1_t* layer = &list->hwLayers[index]; MdpPipeInfo* cur_pipe = mCurrentFrame.pipeLayer[index].pipeInfo; if(configure(ctx, layer, cur_pipe) != 0 ) { ALOGD_IF(isDebug(), "%s: MDPComp failed to configure overlay for \ layer %d",__FUNCTION__, index); return false; } } return true; } bool MDPComp::prepare(hwc_context_t *ctx, hwc_display_contents_1_t* list) { if(!isEnabled()) { ALOGE_IF(isDebug(),"%s: MDP Comp. not enabled.", __FUNCTION__); return false; } overlay::Overlay& ov = *ctx->mOverlay; bool isMDPCompUsed = true; //reset old data reset(ctx, list); bool doable = isDoable(ctx, list); if(doable) { if(setup(ctx, list)) { setMDPCompLayerFlags(ctx, list); } else { ALOGD_IF(isDebug(),"%s: MDP Comp Failed",__FUNCTION__); isMDPCompUsed = false; } } else { ALOGD_IF( isDebug(),"%s: MDP Comp not possible[%d]",__FUNCTION__, doable); isMDPCompUsed = false; } //Reset states if(!isMDPCompUsed) { //Reset current frame reset(ctx, list); } mState = isMDPCompUsed ? MDPCOMP_ON : MDPCOMP_OFF; return isMDPCompUsed; } //=============MDPCompLowRes=================================================== /* * Configures pipe(s) for MDP composition */ int MDPCompLowRes::configure(hwc_context_t *ctx, hwc_layer_1_t *layer, MdpPipeInfo* mdpInfo) { const int dpy = HWC_DISPLAY_PRIMARY; private_handle_t *hnd = (private_handle_t *)layer->handle; overlay::Overlay& ov = *ctx->mOverlay; if(!hnd) { ALOGE("%s: layer handle is NULL", __FUNCTION__); return -1; } MdpPipeInfoLowRes& mdp_info = *(MdpPipeInfoLowRes*)mdpInfo; int hw_w = ctx->dpyAttr[dpy].xres; int hw_h = ctx->dpyAttr[dpy].yres; hwc_rect_t crop = layer->sourceCrop; hwc_rect_t dst = layer->displayFrame; int crop_w = crop.right - crop.left; int crop_h = crop.bottom - crop.top; int dst_w = dst.right - dst.left; int dst_h = dst.bottom - dst.top; if(dst.left < 0 || dst.top < 0 || dst.right > hw_w || dst.bottom > hw_h) { ALOGD_IF(isDebug(),"%s: Destination has negative coordinates", __FUNCTION__); hwc_rect_t scissor = {0, 0, hw_w, hw_h }; qhwc::calculate_crop_rects(crop, dst, scissor, layer->transform); //Update calulated width and height crop_w = crop.right - crop.left; crop_h = crop.bottom - crop.top; dst_w = dst.right - dst.left; dst_h = dst.bottom - dst.top; } if( (dst_w > hw_w)|| (dst_h > hw_h)) { ALOGD_IF(isDebug(),"%s: Dest rect exceeds FB", __FUNCTION__); dst_w = hw_w; dst_h = hw_h; } // Determine pipe to set based on pipe index ovutils::eDest dest = mdp_info.index; ovutils::eZorder zOrder = ovutils::ZORDER_0; if(mdp_info.zOrder == 0 ) { zOrder = ovutils::ZORDER_0; } else if(mdp_info.zOrder == 1 ) { zOrder = ovutils::ZORDER_1; } else if(mdp_info.zOrder == 2 ) { zOrder = ovutils::ZORDER_2; } else if(mdp_info.zOrder == 3) { zOrder = ovutils::ZORDER_3; } // Order order order // setSource - just setting source // setParameter - changes src w/h/f accordingly // setCrop - ROI - src_rect // setPosition - dst_rect // commit - commit changes to mdp driver // queueBuffer - not here, happens when draw is called ovutils::Whf info(hnd->width, hnd->height, hnd->format, hnd->size); ovutils::eMdpFlags mdpFlags = ovutils::OV_MDP_FLAGS_NONE; if(isYuvBuffer(hnd)) setVidInfo(layer, mdpFlags); ovutils::setMdpFlags(mdpFlags,ovutils::OV_MDP_BACKEND_COMPOSITION); if(layer->blending == HWC_BLENDING_PREMULT) { ovutils::setMdpFlags(mdpFlags, ovutils::OV_MDP_BLEND_FG_PREMULT); } ovutils::eTransform orient = overlay::utils::OVERLAY_TRANSFORM_0 ; if(!(layer->transform & HWC_TRANSFORM_ROT_90)) { if(layer->transform & HWC_TRANSFORM_FLIP_H) { ovutils::setMdpFlags(mdpFlags, ovutils::OV_MDP_FLIP_H); } if(layer->transform & HWC_TRANSFORM_FLIP_V) { ovutils::setMdpFlags(mdpFlags, ovutils::OV_MDP_FLIP_V); } } else { orient = static_cast(layer->transform); } ovutils::eRotFlags rotFlags = ovutils::ROT_FLAGS_NONE; if(isYuvBuffer(hnd) && (ctx->mMDP.version >= qdutils::MDP_V4_2 && ctx->mMDP.version < qdutils::MDSS_V5)) { rotFlags = ovutils::ROT_DOWNSCALE_ENABLED; } ovutils::PipeArgs parg(mdpFlags, info, zOrder, ovutils::IS_FG_OFF, rotFlags); ov.setSource(parg, dest); ov.setTransform(orient, dest); ovutils::Dim dcrop(crop.left, crop.top, crop_w, crop_h); ov.setCrop(dcrop, dest); ovutils::Dim dim(dst.left, dst.top, dst_w, dst_h); ov.setPosition(dim, dest); ALOGD_IF(isDebug(),"%s: MDP set: crop[%d,%d,%d,%d] dst[%d,%d,%d,%d] \ nPipe: %d zorder: %d",__FUNCTION__, dcrop.x, dcrop.y,dcrop.w, dcrop.h, dim.x, dim.y, dim.w, dim.h, mdp_info.index, mdp_info.zOrder); if (!ov.commit(dest)) { ALOGE("%s: commit failed", __FUNCTION__); return -1; } return 0; } int MDPCompLowRes::pipesNeeded(hwc_context_t *ctx, hwc_display_contents_1_t* list) { const int dpy = HWC_DISPLAY_PRIMARY; return ctx->listStats[dpy].numAppLayers; } bool MDPCompLowRes::allocLayerPipes(hwc_context_t *ctx, hwc_display_contents_1_t* list, FrameInfo& currentFrame) { const int dpy = HWC_DISPLAY_PRIMARY; overlay::Overlay& ov = *ctx->mOverlay; int layer_count = ctx->listStats[dpy].numAppLayers; currentFrame.count = layer_count; currentFrame.pipeLayer = (PipeLayerPair*) malloc(sizeof(PipeLayerPair) * currentFrame.count); if(isYuvPresent(ctx, dpy)) { int nYuvCount = ctx->listStats[dpy].yuvCount; for(int index = 0; index < nYuvCount; index ++) { int nYuvIndex = ctx->listStats[dpy].yuvIndices[index]; hwc_layer_1_t* layer = &list->hwLayers[nYuvIndex]; PipeLayerPair& info = currentFrame.pipeLayer[nYuvIndex]; info.pipeInfo = new MdpPipeInfoLowRes; MdpPipeInfoLowRes& pipe_info = *(MdpPipeInfoLowRes*)info.pipeInfo; pipe_info.index = getMdpPipe(ctx, MDPCOMP_OV_VG); if(pipe_info.index == ovutils::OV_INVALID) { ALOGD_IF(isDebug(), "%s: Unable to get pipe for Videos", __FUNCTION__); return false; } pipe_info.zOrder = nYuvIndex; } } for(int index = 0 ; index < layer_count ; index++ ) { hwc_layer_1_t* layer = &list->hwLayers[index]; private_handle_t *hnd = (private_handle_t *)layer->handle; if(isYuvBuffer(hnd)) continue; PipeLayerPair& info = currentFrame.pipeLayer[index]; info.pipeInfo = new MdpPipeInfoLowRes; MdpPipeInfoLowRes& pipe_info = *(MdpPipeInfoLowRes*)info.pipeInfo; pipe_info.index = getMdpPipe(ctx, MDPCOMP_OV_ANY); if(pipe_info.index == ovutils::OV_INVALID) { ALOGD_IF(isDebug(), "%s: Unable to get pipe for UI", __FUNCTION__); return false; } pipe_info.zOrder = index; } return true; } bool MDPCompLowRes::draw(hwc_context_t *ctx, hwc_display_contents_1_t* list) { if(!isEnabled() || !isUsed()) { ALOGD_IF(isDebug(),"%s: MDP Comp not configured", __FUNCTION__); return true; } if(!ctx || !list) { ALOGE("%s: invalid contxt or list",__FUNCTION__); return false; } /* reset Invalidator */ if(idleInvalidator) idleInvalidator->markForSleep(); const int dpy = HWC_DISPLAY_PRIMARY; overlay::Overlay& ov = *ctx->mOverlay; LayerProp *layerProp = ctx->layerProp[dpy]; int numHwLayers = ctx->listStats[dpy].numAppLayers; for(int i = 0; i < numHwLayers; i++ ) { hwc_layer_1_t *layer = &list->hwLayers[i]; if(!(layerProp[i].mFlags & HWC_MDPCOMP)) { continue; } MdpPipeInfoLowRes& pipe_info = *(MdpPipeInfoLowRes*)mCurrentFrame.pipeLayer[i].pipeInfo; ovutils::eDest dest = pipe_info.index; if(dest == ovutils::OV_INVALID) { ALOGE("%s: Invalid pipe index (%d)", __FUNCTION__, dest); return false; } if (ctx ) { private_handle_t *hnd = (private_handle_t *)layer->handle; if(!hnd) { ALOGE("%s handle null", __FUNCTION__); return false; } ALOGD_IF(isDebug(),"%s: MDP Comp: Drawing layer: %p hnd: %p \ using pipe: %d", __FUNCTION__, layer, hnd, dest ); if (!ov.queueBuffer(hnd->fd, hnd->offset, dest)) { ALOGE("%s: queueBuffer failed for external", __FUNCTION__); return false; } } layerProp[i].mFlags &= ~HWC_MDPCOMP; } return true; } //=============MDPCompHighRes=================================================== int MDPCompHighRes::pipesNeeded(hwc_context_t *ctx, hwc_display_contents_1_t* list) { const int dpy = HWC_DISPLAY_PRIMARY; int numAppLayers = ctx->listStats[dpy].numAppLayers; int pipesNeeded = 0; int hw_w = ctx->dpyAttr[dpy].xres; for(int i = 0; i < numAppLayers; ++i) { hwc_layer_1_t* layer = &list->hwLayers[i]; hwc_rect_t dst = layer->displayFrame; if(dst.left > hw_w/2) { pipesNeeded++; } else if(dst.right <= hw_w/2) { pipesNeeded++; } else { pipesNeeded += 2; } } return pipesNeeded; } bool MDPCompHighRes::acquireMDPPipes(hwc_context_t *ctx, hwc_layer_1_t* layer, MdpPipeInfoHighRes& pipe_info, ePipeType type) { const int dpy = HWC_DISPLAY_PRIMARY; int hw_w = ctx->dpyAttr[dpy].xres; hwc_rect_t dst = layer->displayFrame; if(dst.left > hw_w/2) { pipe_info.lIndex = ovutils::OV_INVALID; pipe_info.rIndex = getMdpPipe(ctx, type); if(pipe_info.rIndex == ovutils::OV_INVALID) return false; } else if (dst.right <= hw_w/2) { pipe_info.rIndex = ovutils::OV_INVALID; pipe_info.lIndex = getMdpPipe(ctx, type); if(pipe_info.lIndex == ovutils::OV_INVALID) return false; } else { pipe_info.rIndex = getMdpPipe(ctx, type); pipe_info.lIndex = getMdpPipe(ctx, type); if(pipe_info.rIndex == ovutils::OV_INVALID || pipe_info.lIndex == ovutils::OV_INVALID) return false; } return true; } bool MDPCompHighRes::allocLayerPipes(hwc_context_t *ctx, hwc_display_contents_1_t* list, FrameInfo& currentFrame) { const int dpy = HWC_DISPLAY_PRIMARY; overlay::Overlay& ov = *ctx->mOverlay; int layer_count = ctx->listStats[dpy].numAppLayers; currentFrame.count = layer_count; currentFrame.pipeLayer = (PipeLayerPair*) malloc(sizeof(PipeLayerPair) * currentFrame.count); if(isYuvPresent(ctx, dpy)) { int nYuvCount = ctx->listStats[dpy].yuvCount; for(int index = 0; index < nYuvCount; index ++) { int nYuvIndex = ctx->listStats[dpy].yuvIndices[index]; hwc_layer_1_t* layer = &list->hwLayers[nYuvIndex]; PipeLayerPair& info = currentFrame.pipeLayer[nYuvIndex]; info.pipeInfo = new MdpPipeInfoHighRes; MdpPipeInfoHighRes& pipe_info = *(MdpPipeInfoHighRes*)info.pipeInfo; if(!acquireMDPPipes(ctx, layer, pipe_info,MDPCOMP_OV_VG)) { ALOGD_IF(isDebug(),"%s: Unable to get pipe for videos", __FUNCTION__); //TODO: windback pipebook data on fail return false; } pipe_info.zOrder = nYuvIndex; } } for(int index = 0 ; index < layer_count ; index++ ) { hwc_layer_1_t* layer = &list->hwLayers[index]; private_handle_t *hnd = (private_handle_t *)layer->handle; if(isYuvBuffer(hnd)) continue; PipeLayerPair& info = currentFrame.pipeLayer[index]; info.pipeInfo = new MdpPipeInfoHighRes; MdpPipeInfoHighRes& pipe_info = *(MdpPipeInfoHighRes*)info.pipeInfo; ePipeType type = MDPCOMP_OV_ANY; if(!qhwc::needsScaling(layer) && !ctx->mDMAInUse && ctx->mMDP.version >= qdutils::MDSS_V5) type = MDPCOMP_OV_DMA; if(!acquireMDPPipes(ctx, layer, pipe_info, type)) { ALOGD_IF(isDebug(), "%s: Unable to get pipe for UI", __FUNCTION__); //TODO: windback pipebook data on fail return false; } pipe_info.zOrder = index; } return true; } /* * Configures pipe(s) for MDP composition */ int MDPCompHighRes::configure(hwc_context_t *ctx, hwc_layer_1_t *layer, MdpPipeInfo* mdpInfo) { const int dpy = HWC_DISPLAY_PRIMARY; private_handle_t *hnd = (private_handle_t *)layer->handle; overlay::Overlay& ov = *ctx->mOverlay; if(!hnd) { ALOGE("%s: layer handle is NULL", __FUNCTION__); return -1; } MdpPipeInfoHighRes& mdp_info = *(MdpPipeInfoHighRes*)mdpInfo; int hw_w = ctx->dpyAttr[dpy].xres; int hw_h = ctx->dpyAttr[dpy].yres; hwc_rect_t crop = layer->sourceCrop; hwc_rect_t dst = layer->displayFrame; int crop_w = crop.right - crop.left; int crop_h = crop.bottom - crop.top; int dst_w = dst.right - dst.left; int dst_h = dst.bottom - dst.top; if(dst.left < 0 || dst.top < 0 || dst.right > hw_w || dst.bottom > hw_h) { ALOGD_IF(isDebug(),"%s: Destination has negative coordinates", __FUNCTION__); hwc_rect_t scissor = {0, 0, hw_w, hw_h }; qhwc::calculate_crop_rects(crop, dst, scissor, 0); //Update calulated width and height crop_w = crop.right - crop.left; crop_h = crop.bottom - crop.top; dst_w = dst.right - dst.left; dst_h = dst.bottom - dst.top; } if( (dst_w > hw_w)|| (dst_h > hw_h)) { ALOGD_IF(isDebug(),"%s: Dest rect exceeds FB", __FUNCTION__); dst_w = hw_w; dst_h = hw_h; } // Determine pipe to set based on pipe index ovutils::eDest l_dest = mdp_info.lIndex; ovutils::eDest r_dest = mdp_info.rIndex; ovutils::eZorder zOrder = ovutils::ZORDER_0; if(mdp_info.zOrder == 0 ) { zOrder = ovutils::ZORDER_0; } else if(mdp_info.zOrder == 1 ) { zOrder = ovutils::ZORDER_1; } else if(mdp_info.zOrder == 2 ) { zOrder = ovutils::ZORDER_2; } else if(mdp_info.zOrder == 3) { zOrder = ovutils::ZORDER_3; } // Order order order // setSource - just setting source // setParameter - changes src w/h/f accordingly // setCrop - ROI - src_rect // setPosition - dst_rect // commit - commit changes to mdp driver // queueBuffer - not here, happens when draw is called ovutils::Whf info(hnd->width, hnd->height, hnd->format, hnd->size); ovutils::eMdpFlags mdpFlagsL = ovutils::OV_MDP_FLAGS_NONE; if(isYuvBuffer(hnd)) setVidInfo(layer, mdpFlagsL); ovutils::setMdpFlags(mdpFlagsL,ovutils::OV_MDP_BACKEND_COMPOSITION); if(layer->blending == HWC_BLENDING_PREMULT) { ovutils::setMdpFlags(mdpFlagsL, ovutils::OV_MDP_BLEND_FG_PREMULT); } ovutils::eTransform orient = overlay::utils::OVERLAY_TRANSFORM_0 ; if(!(layer->transform & HWC_TRANSFORM_ROT_90)) { if(layer->transform & HWC_TRANSFORM_FLIP_H) { ovutils::setMdpFlags(mdpFlagsL, ovutils::OV_MDP_FLIP_H); } if(layer->transform & HWC_TRANSFORM_FLIP_V) { ovutils::setMdpFlags(mdpFlagsL, ovutils::OV_MDP_FLIP_V); } } else { orient = static_cast(layer->transform); } ovutils::eMdpFlags mdpFlagsR = mdpFlagsL; ovutils::setMdpFlags(mdpFlagsR, ovutils::OV_MDSS_MDP_RIGHT_MIXER); hwc_rect_t tmp_cropL, tmp_dstL; hwc_rect_t tmp_cropR, tmp_dstR; if(l_dest != ovutils::OV_INVALID) { tmp_cropL = crop; tmp_dstL = dst; hwc_rect_t scissor = {0, 0, hw_w/2, hw_h }; qhwc::calculate_crop_rects(tmp_cropL, tmp_dstL, scissor, 0); } if(r_dest != ovutils::OV_INVALID) { tmp_cropR = crop; tmp_dstR = dst; hwc_rect_t scissor = {hw_w/2, 0, hw_w, hw_h }; qhwc::calculate_crop_rects(tmp_cropR, tmp_dstR, scissor, 0); } //When buffer is flipped, contents of mixer config also needs to swapped. //Not needed if the layer is confined to one half of the screen. if(layer->transform & HWC_TRANSFORM_FLIP_V && l_dest != ovutils::OV_INVALID && r_dest != ovutils::OV_INVALID ) { hwc_rect_t new_cropR; new_cropR.left = tmp_cropL.left; new_cropR.right = new_cropR.left + (tmp_cropR.right - tmp_cropR.left); hwc_rect_t new_cropL; new_cropL.left = new_cropR.right; new_cropL.right = tmp_cropR.right; tmp_cropL.left = new_cropL.left; tmp_cropL.right = new_cropL.right; tmp_cropR.left = new_cropR.left; tmp_cropR.right = new_cropR.right; ALOGD_IF(isDebug(),"rects on V flip: \ cropL(%d,%d,%d,%d) dstL(%d,%d,%d,%d) \ cropR(%d,%d,%d,%d) dstR(%d,%d,%d,%d)", tmp_cropL.left, tmp_cropL.top, tmp_cropL.right, tmp_cropL.bottom, tmp_dstL.left, tmp_dstL.top, tmp_dstL.right, tmp_dstL.bottom, tmp_cropR.left, tmp_cropR.top, tmp_cropR.right, tmp_cropR.bottom, tmp_dstR.left, tmp_dstR.top, tmp_dstR.right, tmp_dstR.bottom); } //**** configure left mixer **** if(l_dest != ovutils::OV_INVALID) { ovutils::PipeArgs pargL(mdpFlagsL, info, zOrder, ovutils::IS_FG_OFF, ovutils::ROT_FLAGS_NONE); ov.setSource(pargL, l_dest); ov.setTransform(orient, l_dest); ovutils::Dim dcropL(tmp_cropL.left, tmp_cropL.top, tmp_cropL.right - tmp_cropL.left, tmp_cropL.bottom - tmp_cropL.top); ov.setCrop(dcropL, l_dest); ovutils::Dim dimL(tmp_dstL.left , tmp_dstL.top, tmp_dstL.right - tmp_dstL.left, tmp_dstL.bottom - tmp_dstL.top); ov.setPosition(dimL, l_dest); ALOGD_IF(isDebug(),"%s: MDP set: LEFT: \ crop[%d,%d,%d,%d] dst[%d,%d,%d,%d] pipeIndexL: %d zorder: %d", __FUNCTION__, dcropL.x, dcropL.y,dcropL.w, dcropL.h, dimL.x, dimL.y, dimL.w, dimL.h, mdp_info.lIndex, mdp_info.zOrder); if (!ov.commit(l_dest)) { ALOGE("%s: commit failed for left mixer config", __FUNCTION__); return -1; } } //**** configure right mixer **** if(r_dest != ovutils::OV_INVALID) { ovutils::PipeArgs pargR(mdpFlagsR, info, zOrder, ovutils::IS_FG_OFF, ovutils::ROT_FLAGS_NONE); ov.setSource(pargR, r_dest); ov.setTransform(orient, r_dest); ovutils::Dim dcropR(tmp_cropR.left, tmp_cropR.top, tmp_cropR.right - tmp_cropR.left, tmp_cropR.bottom - tmp_cropR.top); ov.setCrop(dcropR, r_dest); ovutils::Dim dimR(tmp_dstR.left - hw_w/2, tmp_dstR.top, tmp_dstR.right - tmp_dstR.left, tmp_dstR.bottom - tmp_dstR.top); ov.setPosition(dimR, r_dest); ALOGD_IF(isDebug(),"%s: MDP set: RIGHT: \ crop[%d,%d,%d,%d] dst[%d,%d,%d,%d] pipeIndexR: %d zorder: %d", __FUNCTION__, dcropR.x, dcropR.y,dcropR.w, dcropR.h, dimR.x, dimR.y, dimR.w, dimR.h, mdp_info.rIndex, mdp_info.zOrder); if (!ov.commit(r_dest)) { ALOGE("%s: commit failed for right mixer config", __FUNCTION__); return -1; } } return 0; } bool MDPCompHighRes::draw(hwc_context_t *ctx, hwc_display_contents_1_t* list) { if(!isEnabled() || !isUsed()) { ALOGD_IF(isDebug(),"%s: MDP Comp not configured", __FUNCTION__); return true; } if(!ctx || !list) { ALOGE("%s: invalid contxt or list",__FUNCTION__); return false; } /* reset Invalidator */ if(idleInvalidator) idleInvalidator->markForSleep(); const int dpy = HWC_DISPLAY_PRIMARY; overlay::Overlay& ov = *ctx->mOverlay; LayerProp *layerProp = ctx->layerProp[dpy]; int numHwLayers = ctx->listStats[dpy].numAppLayers; for(int i = 0; i < numHwLayers; i++ ) { hwc_layer_1_t *layer = &list->hwLayers[i]; if(!(layerProp[i].mFlags & HWC_MDPCOMP)) { continue; } MdpPipeInfoHighRes& pipe_info = *(MdpPipeInfoHighRes*)mCurrentFrame.pipeLayer[i].pipeInfo; ovutils::eDest indexL = pipe_info.lIndex; ovutils::eDest indexR = pipe_info.rIndex; //************* play left mixer ********** if(indexL != ovutils::OV_INVALID) { ovutils::eDest destL = (ovutils::eDest)indexL; if (ctx ) { private_handle_t *hnd = (private_handle_t *)layer->handle; if(!hnd) { ALOGE("%s handle null", __FUNCTION__); return false; } ALOGD_IF(isDebug(),"%s: MDP Comp: Drawing layer: %p hnd: %p \ using pipe: %d", __FUNCTION__, layer, hnd, indexL ); if (!ov.queueBuffer(hnd->fd, hnd->offset, destL)) { ALOGE("%s: queueBuffer failed for external", __FUNCTION__); return false; } } } //************* play right mixer ********** if(indexR != ovutils::OV_INVALID) { ovutils::eDest destR = (ovutils::eDest)indexR; if (ctx ) { private_handle_t *hnd = (private_handle_t *)layer->handle; if(!hnd) { ALOGE("%s handle null", __FUNCTION__); return false; } ALOGD_IF(isDebug(),"%s: MDP Comp: Drawing layer: %p hnd: %p \ using pipe: %d", __FUNCTION__, layer, hnd, indexR ); if (!ov.queueBuffer(hnd->fd, hnd->offset, destR)) { ALOGE("%s: queueBuffer failed for external", __FUNCTION__); return false; } } } layerProp[i].mFlags &= ~HWC_MDPCOMP; } return true; } }; //namespace