blob: 6904efceea48cae7896d8051bdc67aa1dc4ee2cf [file] [log] [blame]
/*
* 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 <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(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<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;
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