Files
android_vendor_qcom_opensou…/libhwcomposer/hwc_utils.cpp
Ramkumar Radhakrishnan 9d7bc31a00 hwc: Support for windowboxing feature on external
1. This feature is targeted to remove any window/pillar boxing on
   external display when zoom mode is enabled.
2. Preserve the aspect ratio of the external display by cropping and
   upscaling the tagged video layer during video playback on external.
3. User can tag the layers to be displayed on external display.
4. Set sys.hwc.windowbox_feature to true to enable this feature

Assumptions & Limitation:
    1. Tagged layers for external display will also be displayed on
       primary display along with untagged layers
    2. When zoom in mode is enabled, source crop left and source crop
       top of a tagged video layer is always assumed to be set to
       positive integer value. So pinch zoom feature or zooming in top
       left corner of the video use case cannot be supported.
    3. Except tagged video layers, all other tagged UI layers will
       use GPU for composition on external display, So all tagged UI
       layers cannot be secure layer.
    4. Rotation animation cannot be supported for this feature.

Change-Id: I8b934cf616ec23b4359d0120f9a291178a2781c6
2014-10-14 19:54:32 -07:00

2618 lines
92 KiB
C++

/*
* Copyright (C) 2010 The Android Open Source Project
* Copyright (C) 2012-2014, 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.
*/
#define ATRACE_TAG (ATRACE_TAG_GRAPHICS | ATRACE_TAG_HAL)
#define HWC_UTILS_DEBUG 0
#include <math.h>
#include <sys/ioctl.h>
#include <linux/fb.h>
#include <binder/IServiceManager.h>
#include <EGL/egl.h>
#include <cutils/properties.h>
#include <utils/Trace.h>
#include <gralloc_priv.h>
#include <overlay.h>
#include <overlayRotator.h>
#include <overlayWriteback.h>
#include "hwc_utils.h"
#include "hwc_mdpcomp.h"
#include "hwc_fbupdate.h"
#include "hwc_ad.h"
#include "mdp_version.h"
#include "hwc_copybit.h"
#include "hwc_dump_layers.h"
#include "hdmi.h"
#include "hwc_qclient.h"
#include "QService.h"
#include "comptype.h"
#include "hwc_virtual.h"
#include "qd_utils.h"
#include <sys/sysinfo.h>
using namespace qClient;
using namespace qService;
using namespace android;
using namespace overlay;
using namespace overlay::utils;
namespace ovutils = overlay::utils;
#ifdef QCOM_BSP
#ifdef __cplusplus
extern "C" {
#endif
EGLAPI EGLBoolean eglGpuPerfHintQCOM(EGLDisplay dpy, EGLContext ctx,
EGLint *attrib_list);
#define EGL_GPU_HINT_1 0x32D0
#define EGL_GPU_HINT_2 0x32D1
#define EGL_GPU_LEVEL_0 0x0
#define EGL_GPU_LEVEL_1 0x1
#define EGL_GPU_LEVEL_2 0x2
#define EGL_GPU_LEVEL_3 0x3
#define EGL_GPU_LEVEL_4 0x4
#define EGL_GPU_LEVEL_5 0x5
#ifdef __cplusplus
}
#endif
#endif
#define PROP_DEFAULT_APPBUFFER "ro.sf.default_app_buffer"
#define MAX_RAM_SIZE 512*1024*1024
#define qHD_WIDTH 540
namespace qhwc {
//Std refresh rates for digital videos- 24p, 30p and 48p
uint32_t stdRefreshRates[] = { 30, 24, 48 };
bool isValidResolution(hwc_context_t *ctx, uint32_t xres, uint32_t yres)
{
return !((xres > qdutils::MDPVersion::getInstance().getMaxMixerWidth() &&
!isDisplaySplit(ctx, HWC_DISPLAY_PRIMARY)) ||
(xres < MIN_DISPLAY_XRES || yres < MIN_DISPLAY_YRES));
}
void changeResolution(hwc_context_t *ctx, int xres_orig, int yres_orig,
int width, int height) {
//Store original display resolution.
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].xres_new = xres_orig;
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].yres_new = yres_orig;
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].customFBSize = false;
char property[PROPERTY_VALUE_MAX] = {'\0'};
char *yptr = NULL;
if (property_get("debug.hwc.fbsize", property, NULL) > 0) {
yptr = strcasestr(property,"x");
int xres_new = atoi(property);
int yres_new = atoi(yptr + 1);
if (isValidResolution(ctx,xres_new,yres_new) &&
xres_new != xres_orig && yres_new != yres_orig) {
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].xres_new = xres_new;
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].yres_new = yres_new;
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].customFBSize = true;
//Caluculate DPI according to changed resolution.
float xdpi = ((float)xres_new * 25.4f) / (float)width;
float ydpi = ((float)yres_new * 25.4f) / (float)height;
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].xdpi = xdpi;
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].ydpi = ydpi;
}
}
}
// Initialize hdmi display attributes based on
// hdmi display class state
void updateDisplayInfo(hwc_context_t* ctx, int dpy) {
ctx->dpyAttr[dpy].fd = ctx->mHDMIDisplay->getFd();
ctx->dpyAttr[dpy].xres = ctx->mHDMIDisplay->getWidth();
ctx->dpyAttr[dpy].yres = ctx->mHDMIDisplay->getHeight();
ctx->dpyAttr[dpy].mMDPScalingMode = ctx->mHDMIDisplay->getMDPScalingMode();
ctx->dpyAttr[dpy].vsync_period = ctx->mHDMIDisplay->getVsyncPeriod();
ctx->mViewFrame[dpy].left = 0;
ctx->mViewFrame[dpy].top = 0;
ctx->mViewFrame[dpy].right = ctx->dpyAttr[dpy].xres;
ctx->mViewFrame[dpy].bottom = ctx->dpyAttr[dpy].yres;
}
// Reset hdmi display attributes and list stats structures
void resetDisplayInfo(hwc_context_t* ctx, int dpy) {
memset(&(ctx->dpyAttr[dpy]), 0, sizeof(ctx->dpyAttr[dpy]));
memset(&(ctx->listStats[dpy]), 0, sizeof(ctx->listStats[dpy]));
// We reset the fd to -1 here but External display class is responsible
// for it when the display is disconnected. This is handled as part of
// EXTERNAL_OFFLINE event.
ctx->dpyAttr[dpy].fd = -1;
}
// Initialize composition resources
void initCompositionResources(hwc_context_t* ctx, int dpy) {
ctx->mFBUpdate[dpy] = IFBUpdate::getObject(ctx, dpy);
ctx->mMDPComp[dpy] = MDPComp::getObject(ctx, dpy);
}
void destroyCompositionResources(hwc_context_t* ctx, int dpy) {
if(ctx->mFBUpdate[dpy]) {
delete ctx->mFBUpdate[dpy];
ctx->mFBUpdate[dpy] = NULL;
}
if(ctx->mMDPComp[dpy]) {
delete ctx->mMDPComp[dpy];
ctx->mMDPComp[dpy] = NULL;
}
}
static int openFramebufferDevice(hwc_context_t *ctx)
{
struct fb_fix_screeninfo finfo;
struct fb_var_screeninfo info;
int fb_fd = openFb(HWC_DISPLAY_PRIMARY);
if(fb_fd < 0) {
ALOGE("%s: Error Opening FB : %s", __FUNCTION__, strerror(errno));
return -errno;
}
if (ioctl(fb_fd, FBIOGET_VSCREENINFO, &info) == -1) {
ALOGE("%s:Error in ioctl FBIOGET_VSCREENINFO: %s", __FUNCTION__,
strerror(errno));
close(fb_fd);
return -errno;
}
if (int(info.width) <= 0 || int(info.height) <= 0) {
// the driver doesn't return that information
// default to 160 dpi
info.width = (int)(((float)info.xres * 25.4f)/160.0f + 0.5f);
info.height = (int)(((float)info.yres * 25.4f)/160.0f + 0.5f);
}
float xdpi = ((float)info.xres * 25.4f) / (float)info.width;
float ydpi = ((float)info.yres * 25.4f) / (float)info.height;
#ifdef MSMFB_METADATA_GET
struct msmfb_metadata metadata;
memset(&metadata, 0 , sizeof(metadata));
metadata.op = metadata_op_frame_rate;
if (ioctl(fb_fd, MSMFB_METADATA_GET, &metadata) == -1) {
ALOGE("%s:Error retrieving panel frame rate: %s", __FUNCTION__,
strerror(errno));
close(fb_fd);
return -errno;
}
float fps = (float)metadata.data.panel_frame_rate;
#else
//XXX: Remove reserved field usage on all baselines
//The reserved[3] field is used to store FPS by the driver.
float fps = info.reserved[3] & 0xFF;
#endif
if (ioctl(fb_fd, FBIOGET_FSCREENINFO, &finfo) == -1) {
ALOGE("%s:Error in ioctl FBIOGET_FSCREENINFO: %s", __FUNCTION__,
strerror(errno));
close(fb_fd);
return -errno;
}
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].fd = fb_fd;
//xres, yres may not be 32 aligned
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].stride = finfo.line_length /(info.xres/8);
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].xres = info.xres;
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].yres = info.yres;
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].xdpi = xdpi;
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].ydpi = ydpi;
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].refreshRate = (uint32_t)fps;
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].dynRefreshRate = (uint32_t)fps;
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].vsync_period =
(uint32_t)(1000000000l / fps);
//To change resolution of primary display
changeResolution(ctx, info.xres, info.yres, info.width, info.height);
//Unblank primary on first boot
if(ioctl(fb_fd, FBIOBLANK,FB_BLANK_UNBLANK) < 0) {
ALOGE("%s: Failed to unblank display", __FUNCTION__);
return -errno;
}
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].isActive = true;
return 0;
}
static void changeDefaultAppBufferCount() {
struct sysinfo info;
unsigned long int ramSize = 0;
if (!sysinfo(&info)) {
ramSize = info.totalram ;
}
int fb_fd = -1;
struct fb_var_screeninfo sInfo ={0};
fb_fd = open("/dev/graphics/fb0", O_RDONLY);
if (fb_fd >=0) {
ioctl(fb_fd, FBIOGET_VSCREENINFO, &sInfo);
close(fb_fd);
}
if ((ramSize && ramSize < MAX_RAM_SIZE) &&
(sInfo.xres && sInfo.xres <= qHD_WIDTH )) {
property_set(PROP_DEFAULT_APPBUFFER, "2");
}
}
void initContext(hwc_context_t *ctx)
{
openFramebufferDevice(ctx);
char value[PROPERTY_VALUE_MAX];
ctx->mMDP.version = qdutils::MDPVersion::getInstance().getMDPVersion();
ctx->mMDP.hasOverlay = qdutils::MDPVersion::getInstance().hasOverlay();
ctx->mMDP.panel = qdutils::MDPVersion::getInstance().getPanelType();
overlay::Overlay::initOverlay();
ctx->mOverlay = overlay::Overlay::getInstance();
ctx->mRotMgr = RotMgr::getInstance();
//default_app_buffer for ferrum
if (ctx->mMDP.version == qdutils::MDP_V3_0_5) {
changeDefaultAppBufferCount();
}
// Initialize composition objects for the primary display
initCompositionResources(ctx, HWC_DISPLAY_PRIMARY);
// Check if the target supports copybit compostion (dyn/mdp) to
// decide if we need to open the copybit module.
int compositionType =
qdutils::QCCompositionType::getInstance().getCompositionType();
// Only MDP copybit is used
if ((compositionType & (qdutils::COMPOSITION_TYPE_DYN |
qdutils::COMPOSITION_TYPE_MDP)) &&
((qdutils::MDPVersion::getInstance().getMDPVersion() ==
qdutils::MDP_V3_0_4) ||
(qdutils::MDPVersion::getInstance().getMDPVersion() ==
qdutils::MDP_V3_0_5))) {
ctx->mCopyBit[HWC_DISPLAY_PRIMARY] = new CopyBit(ctx,
HWC_DISPLAY_PRIMARY);
}
ctx->mHDMIDisplay = new HDMIDisplay();
// Send the primary resolution to the hdmi display class
// to be used for MDP scaling functionality
uint32_t priW = ctx->dpyAttr[HWC_DISPLAY_PRIMARY].xres;
uint32_t priH = ctx->dpyAttr[HWC_DISPLAY_PRIMARY].yres;
ctx->mHDMIDisplay->setPrimaryAttributes(priW, priH);
ctx->mHWCVirtual = new HWCVirtualVDS();
ctx->dpyAttr[HWC_DISPLAY_EXTERNAL].isActive = false;
ctx->dpyAttr[HWC_DISPLAY_EXTERNAL].connected = false;
ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].isActive = false;
ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].connected = false;
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].mMDPScalingMode= false;
ctx->dpyAttr[HWC_DISPLAY_EXTERNAL].mMDPScalingMode = false;
ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].mMDPScalingMode = false;
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].connected = true;
//Initialize the primary display viewFrame info
ctx->mViewFrame[HWC_DISPLAY_PRIMARY].left = 0;
ctx->mViewFrame[HWC_DISPLAY_PRIMARY].top = 0;
ctx->mViewFrame[HWC_DISPLAY_PRIMARY].right =
(int)ctx->dpyAttr[HWC_DISPLAY_PRIMARY].xres;
ctx->mViewFrame[HWC_DISPLAY_PRIMARY].bottom =
(int)ctx->dpyAttr[HWC_DISPLAY_PRIMARY].yres;
for (uint32_t i = 0; i < HWC_NUM_DISPLAY_TYPES; i++) {
ctx->mHwcDebug[i] = new HwcDebug(i);
ctx->mLayerRotMap[i] = new LayerRotMap();
ctx->mAnimationState[i] = ANIMATION_STOPPED;
ctx->dpyAttr[i].mActionSafePresent = false;
ctx->dpyAttr[i].mAsWidthRatio = 0;
ctx->dpyAttr[i].mAsHeightRatio = 0;
}
for (uint32_t i = 0; i < HWC_NUM_DISPLAY_TYPES; i++) {
ctx->mPrevHwLayerCount[i] = 0;
}
MDPComp::init(ctx);
ctx->mAD = new AssertiveDisplay(ctx);
ctx->vstate.enable = false;
ctx->vstate.fakevsync = false;
ctx->mExtOrientation = 0;
ctx->numActiveDisplays = 1;
//Right now hwc starts the service but anybody could do it, or it could be
//independent process as well.
QService::init();
sp<IQClient> client = new QClient(ctx);
android::sp<qService::IQService> qservice_sp = interface_cast<IQService>(
defaultServiceManager()->getService(
String16("display.qservice")));
if (qservice_sp.get()) {
qservice_sp->connect(client);
} else {
ALOGE("%s: Failed to acquire service pointer", __FUNCTION__);
return ;
}
// Initialize device orientation to its default orientation
ctx->deviceOrientation = 0;
ctx->mBufferMirrorMode = false;
ctx->enableABC = false;
property_get("debug.sf.hwc.canUseABC", value, "0");
ctx->enableABC = atoi(value) ? true : false;
// Initialize gpu perfomance hint related parameters
property_get("sys.hwc.gpu_perf_mode", value, "0");
#ifdef QCOM_BSP
ctx->mGPUHintInfo.mGpuPerfModeEnable = atoi(value)? true : false;
ctx->mGPUHintInfo.mEGLDisplay = NULL;
ctx->mGPUHintInfo.mEGLContext = NULL;
ctx->mGPUHintInfo.mCompositionState = COMPOSITION_STATE_MDP;
ctx->mGPUHintInfo.mCurrGPUPerfMode = EGL_GPU_LEVEL_0;
#endif
// Read the system property to determine if windowboxing feature is enabled.
ctx->mWindowboxFeature = false;
if(property_get("sys.hwc.windowbox_feature", value, "false")
&& !strcmp(value, "true")) {
ctx->mWindowboxFeature = true;
}
memset(&(ctx->mPtorInfo), 0, sizeof(ctx->mPtorInfo));
ALOGI("Initializing Qualcomm Hardware Composer");
ALOGI("MDP version: %d", ctx->mMDP.version);
}
void closeContext(hwc_context_t *ctx)
{
if(ctx->mOverlay) {
delete ctx->mOverlay;
ctx->mOverlay = NULL;
}
if(ctx->mRotMgr) {
delete ctx->mRotMgr;
ctx->mRotMgr = NULL;
}
for(int i = 0; i < HWC_NUM_DISPLAY_TYPES; i++) {
if(ctx->mCopyBit[i]) {
delete ctx->mCopyBit[i];
ctx->mCopyBit[i] = NULL;
}
}
if(ctx->dpyAttr[HWC_DISPLAY_PRIMARY].fd) {
close(ctx->dpyAttr[HWC_DISPLAY_PRIMARY].fd);
ctx->dpyAttr[HWC_DISPLAY_PRIMARY].fd = -1;
}
if(ctx->mHDMIDisplay) {
delete ctx->mHDMIDisplay;
ctx->mHDMIDisplay = NULL;
}
for(int i = 0; i < HWC_NUM_DISPLAY_TYPES; i++) {
destroyCompositionResources(ctx, i);
if(ctx->mHwcDebug[i]) {
delete ctx->mHwcDebug[i];
ctx->mHwcDebug[i] = NULL;
}
if(ctx->mLayerRotMap[i]) {
delete ctx->mLayerRotMap[i];
ctx->mLayerRotMap[i] = NULL;
}
}
if(ctx->mHWCVirtual) {
delete ctx->mHWCVirtual;
ctx->mHWCVirtual = NULL;
}
if(ctx->mAD) {
delete ctx->mAD;
ctx->mAD = NULL;
}
}
//Helper to roundoff the refreshrates
static uint32_t roundOff(uint32_t refreshRate) {
int count = (int) (sizeof(stdRefreshRates)/sizeof(stdRefreshRates[0]));
uint32_t rate = refreshRate;
for(int i=0; i< count; i++) {
if(abs(stdRefreshRates[i] - refreshRate) < 2) {
// Most likely used for video, the fps can fluctuate
// Ex: b/w 29 and 30 for 30 fps clip
rate = stdRefreshRates[i];
break;
}
}
return rate;
}
//Helper func to set the dyn fps
void setRefreshRate(hwc_context_t* ctx, int dpy, uint32_t refreshRate) {
//Update only if different
if(!ctx || refreshRate == ctx->dpyAttr[dpy].dynRefreshRate)
return;
const int fbNum = Overlay::getFbForDpy(dpy);
char sysfsPath[qdutils::MAX_SYSFS_FILE_PATH];
snprintf (sysfsPath, sizeof(sysfsPath),
"/sys/class/graphics/fb%d/dynamic_fps", fbNum);
int fd = open(sysfsPath, O_WRONLY);
if(fd >= 0) {
char str[64];
snprintf(str, sizeof(str), "%d", refreshRate);
ssize_t ret = write(fd, str, strlen(str));
if(ret < 0) {
ALOGE("%s: Failed to write %d with error %s",
__FUNCTION__, refreshRate, strerror(errno));
} else {
ctx->dpyAttr[dpy].dynRefreshRate = refreshRate;
ALOGD_IF(HWC_UTILS_DEBUG, "%s: Wrote %d to dynamic_fps",
__FUNCTION__, refreshRate);
}
close(fd);
} else {
ALOGE("%s: Failed to open %s with error %s", __FUNCTION__, sysfsPath,
strerror(errno));
}
}
void dumpsys_log(android::String8& buf, const char* fmt, ...)
{
va_list varargs;
va_start(varargs, fmt);
buf.appendFormatV(fmt, varargs);
va_end(varargs);
}
int getExtOrientation(hwc_context_t* ctx) {
int extOrient = ctx->mExtOrientation;
if(ctx->mBufferMirrorMode)
extOrient = getMirrorModeOrientation(ctx);
return extOrient;
}
/* Calculates the destination position based on the action safe rectangle */
void getActionSafePosition(hwc_context_t *ctx, int dpy, hwc_rect_t& rect) {
// Position
int x = rect.left, y = rect.top;
int w = rect.right - rect.left;
int h = rect.bottom - rect.top;
if(!ctx->dpyAttr[dpy].mActionSafePresent)
return;
// Read action safe properties
int asWidthRatio = ctx->dpyAttr[dpy].mAsWidthRatio;
int asHeightRatio = ctx->dpyAttr[dpy].mAsHeightRatio;
float wRatio = 1.0;
float hRatio = 1.0;
float xRatio = 1.0;
float yRatio = 1.0;
uint32_t fbWidth = ctx->dpyAttr[dpy].xres;
uint32_t fbHeight = ctx->dpyAttr[dpy].yres;
if(ctx->dpyAttr[dpy].mMDPScalingMode) {
// if MDP scaling mode is enabled for external, need to query
// the actual width and height, as that is the physical w & h
ctx->mHDMIDisplay->getAttributes(fbWidth, fbHeight);
}
// Since external is rotated 90, need to swap width/height
int extOrient = getExtOrientation(ctx);
if(extOrient & HWC_TRANSFORM_ROT_90)
swap(fbWidth, fbHeight);
float asX = 0;
float asY = 0;
float asW = (float)fbWidth;
float asH = (float)fbHeight;
// based on the action safe ratio, get the Action safe rectangle
asW = ((float)fbWidth * (1.0f - (float)asWidthRatio / 100.0f));
asH = ((float)fbHeight * (1.0f - (float)asHeightRatio / 100.0f));
asX = ((float)fbWidth - asW) / 2;
asY = ((float)fbHeight - asH) / 2;
// calculate the position ratio
xRatio = (float)x/(float)fbWidth;
yRatio = (float)y/(float)fbHeight;
wRatio = (float)w/(float)fbWidth;
hRatio = (float)h/(float)fbHeight;
//Calculate the position...
x = int((xRatio * asW) + asX);
y = int((yRatio * asH) + asY);
w = int(wRatio * asW);
h = int(hRatio * asH);
// Convert it back to hwc_rect_t
rect.left = x;
rect.top = y;
rect.right = w + rect.left;
rect.bottom = h + rect.top;
return;
}
// This function gets the destination position for Seconday display
// based on the position and aspect ratio with orientation
void getAspectRatioPosition(hwc_context_t* ctx, int dpy, int extOrientation,
hwc_rect_t& inRect, hwc_rect_t& outRect) {
// Physical display resolution
float fbWidth = (float)ctx->dpyAttr[dpy].xres;
float fbHeight = (float)ctx->dpyAttr[dpy].yres;
//display position(x,y,w,h) in correct aspectratio after rotation
int xPos = 0;
int yPos = 0;
float width = fbWidth;
float height = fbHeight;
// Width/Height used for calculation, after rotation
float actualWidth = fbWidth;
float actualHeight = fbHeight;
float wRatio = 1.0;
float hRatio = 1.0;
float xRatio = 1.0;
float yRatio = 1.0;
hwc_rect_t rect = {0, 0, (int)fbWidth, (int)fbHeight};
Dim inPos(inRect.left, inRect.top, inRect.right - inRect.left,
inRect.bottom - inRect.top);
Dim outPos(outRect.left, outRect.top, outRect.right - outRect.left,
outRect.bottom - outRect.top);
Whf whf((uint32_t)fbWidth, (uint32_t)fbHeight, 0);
eTransform extorient = static_cast<eTransform>(extOrientation);
// To calculate the destination co-ordinates in the new orientation
preRotateSource(extorient, whf, inPos);
if(extOrientation & HAL_TRANSFORM_ROT_90) {
// Swap width/height for input position
swapWidthHeight(actualWidth, actualHeight);
qdutils::getAspectRatioPosition((int)fbWidth, (int)fbHeight,
(int)actualWidth, (int)actualHeight, rect);
xPos = rect.left;
yPos = rect.top;
width = float(rect.right - rect.left);
height = float(rect.bottom - rect.top);
}
xRatio = (float)((float)inPos.x/actualWidth);
yRatio = (float)((float)inPos.y/actualHeight);
wRatio = (float)((float)inPos.w/actualWidth);
hRatio = (float)((float)inPos.h/actualHeight);
//Calculate the pos9ition...
outPos.x = uint32_t((xRatio * width) + (float)xPos);
outPos.y = uint32_t((yRatio * height) + (float)yPos);
outPos.w = uint32_t(wRatio * width);
outPos.h = uint32_t(hRatio * height);
ALOGD_IF(HWC_UTILS_DEBUG, "%s: Calculated AspectRatio Position: x = %d,"
"y = %d w = %d h = %d", __FUNCTION__, outPos.x, outPos.y,
outPos.w, outPos.h);
// For sidesync, the dest fb will be in portrait orientation, and the crop
// will be updated to avoid the black side bands, and it will be upscaled
// to fit the dest RB, so recalculate
// the position based on the new width and height
if ((extOrientation & HWC_TRANSFORM_ROT_90) &&
isOrientationPortrait(ctx)) {
hwc_rect_t r = {0, 0, 0, 0};
//Calculate the position
xRatio = (float)(outPos.x - xPos)/width;
// GetaspectRatio -- tricky to get the correct aspect ratio
// But we need to do this.
qdutils::getAspectRatioPosition((int)width, (int)height,
(int)width,(int)height, r);
xPos = r.left;
yPos = r.top;
float tempHeight = float(r.bottom - r.top);
yRatio = (float)yPos/height;
wRatio = (float)outPos.w/width;
hRatio = tempHeight/height;
//Map the coordinates back to Framebuffer domain
outPos.x = uint32_t(xRatio * fbWidth);
outPos.y = uint32_t(yRatio * fbHeight);
outPos.w = uint32_t(wRatio * fbWidth);
outPos.h = uint32_t(hRatio * fbHeight);
ALOGD_IF(HWC_UTILS_DEBUG, "%s: Calculated AspectRatio for device in"
"portrait: x = %d,y = %d w = %d h = %d", __FUNCTION__,
outPos.x, outPos.y,
outPos.w, outPos.h);
}
if(ctx->dpyAttr[dpy].mMDPScalingMode) {
uint32_t extW = 0, extH = 0;
if(dpy == HWC_DISPLAY_EXTERNAL) {
ctx->mHDMIDisplay->getAttributes(extW, extH);
} else if(dpy == HWC_DISPLAY_VIRTUAL) {
extW = ctx->mHWCVirtual->getScalingWidth();
extH = ctx->mHWCVirtual->getScalingHeight();
}
ALOGD_IF(HWC_UTILS_DEBUG, "%s: Scaling mode extW=%d extH=%d",
__FUNCTION__, extW, extH);
fbWidth = (float)ctx->dpyAttr[dpy].xres;
fbHeight = (float)ctx->dpyAttr[dpy].yres;
//Calculate the position...
xRatio = (float)outPos.x/fbWidth;
yRatio = (float)outPos.y/fbHeight;
wRatio = (float)outPos.w/fbWidth;
hRatio = (float)outPos.h/fbHeight;
outPos.x = uint32_t(xRatio * (float)extW);
outPos.y = uint32_t(yRatio * (float)extH);
outPos.w = uint32_t(wRatio * (float)extW);
outPos.h = uint32_t(hRatio * (float)extH);
}
// Convert Dim to hwc_rect_t
outRect.left = outPos.x;
outRect.top = outPos.y;
outRect.right = outPos.x + outPos.w;
outRect.bottom = outPos.y + outPos.h;
return;
}
bool isPrimaryPortrait(hwc_context_t *ctx) {
int fbWidth = ctx->dpyAttr[HWC_DISPLAY_PRIMARY].xres;
int fbHeight = ctx->dpyAttr[HWC_DISPLAY_PRIMARY].yres;
if(fbWidth < fbHeight) {
return true;
}
return false;
}
bool isOrientationPortrait(hwc_context_t *ctx) {
if(isPrimaryPortrait(ctx)) {
return !(ctx->deviceOrientation & 0x1);
}
return (ctx->deviceOrientation & 0x1);
}
void calcExtDisplayPosition(hwc_context_t *ctx,
private_handle_t *hnd,
int dpy,
hwc_rect_t& sourceCrop,
hwc_rect_t& displayFrame,
int& transform,
ovutils::eTransform& orient) {
// Swap width and height when there is a 90deg transform
int extOrient = getExtOrientation(ctx);
if(dpy && ctx->mOverlay->isUIScalingOnExternalSupported()) {
if(!isYuvBuffer(hnd)) {
if(extOrient & HWC_TRANSFORM_ROT_90) {
int dstWidth = ctx->dpyAttr[dpy].xres;
int dstHeight = ctx->dpyAttr[dpy].yres;;
int srcWidth = ctx->dpyAttr[HWC_DISPLAY_PRIMARY].xres;
int srcHeight = ctx->dpyAttr[HWC_DISPLAY_PRIMARY].yres;
if(!isPrimaryPortrait(ctx)) {
swap(srcWidth, srcHeight);
} // Get Aspect Ratio for external
qdutils::getAspectRatioPosition(dstWidth, dstHeight, srcWidth,
srcHeight, displayFrame);
// Crop - this is needed, because for sidesync, the dest fb will
// be in portrait orientation, so update the crop to not show the
// black side bands.
if (isOrientationPortrait(ctx)) {
sourceCrop = displayFrame;
displayFrame.left = 0;
displayFrame.top = 0;
displayFrame.right = dstWidth;
displayFrame.bottom = dstHeight;
}
}
if(ctx->dpyAttr[dpy].mMDPScalingMode) {
uint32_t extW = 0, extH = 0;
// if MDP scaling mode is enabled, map the co-ordinates to new
// domain(downscaled)
float fbWidth = (float)ctx->dpyAttr[dpy].xres;
float fbHeight = (float)ctx->dpyAttr[dpy].yres;
// query MDP configured attributes
if(dpy == HWC_DISPLAY_EXTERNAL) {
ctx->mHDMIDisplay->getAttributes(extW, extH);
} else if(dpy == HWC_DISPLAY_VIRTUAL) {
extW = ctx->mHWCVirtual->getScalingWidth();
extH = ctx->mHWCVirtual->getScalingHeight();
}
ALOGD_IF(HWC_UTILS_DEBUG, "%s: Scaling mode extW=%d extH=%d",
__FUNCTION__, extW, extH);
//Calculate the ratio...
float wRatio = ((float)extW)/fbWidth;
float hRatio = ((float)extH)/fbHeight;
//convert Dim to hwc_rect_t
displayFrame.left = int(wRatio*(float)displayFrame.left);
displayFrame.top = int(hRatio*(float)displayFrame.top);
displayFrame.right = int(wRatio*(float)displayFrame.right);
displayFrame.bottom = int(hRatio*(float)displayFrame.bottom);
ALOGD_IF(DEBUG_MDPDOWNSCALE, "Calculated external display frame"
" for MDPDownscale feature [%d %d %d %d]",
displayFrame.left, displayFrame.top,
displayFrame.right, displayFrame.bottom);
}
}else {
if(extOrient || ctx->dpyAttr[dpy].mMDPScalingMode) {
getAspectRatioPosition(ctx, dpy, extOrient,
displayFrame, displayFrame);
}
}
// If there is a external orientation set, use that
if(extOrient) {
transform = extOrient;
orient = static_cast<ovutils::eTransform >(extOrient);
}
// Calculate the actionsafe dimensions for External(dpy = 1 or 2)
getActionSafePosition(ctx, dpy, displayFrame);
}
}
/* Returns the orientation which needs to be set on External for
* SideSync/Buffer Mirrormode
*/
int getMirrorModeOrientation(hwc_context_t *ctx) {
int extOrientation = 0;
int deviceOrientation = ctx->deviceOrientation;
if(!isPrimaryPortrait(ctx))
deviceOrientation = (deviceOrientation + 1) % 4;
if (deviceOrientation == 0)
extOrientation = HWC_TRANSFORM_ROT_270;
else if (deviceOrientation == 1)//90
extOrientation = 0;
else if (deviceOrientation == 2)//180
extOrientation = HWC_TRANSFORM_ROT_90;
else if (deviceOrientation == 3)//270
extOrientation = HWC_TRANSFORM_FLIP_V | HWC_TRANSFORM_FLIP_H;
return extOrientation;
}
/* Get External State names */
const char* getExternalDisplayState(uint32_t external_state) {
static const char* externalStates[EXTERNAL_MAXSTATES] = {0};
externalStates[EXTERNAL_OFFLINE] = STR(EXTERNAL_OFFLINE);
externalStates[EXTERNAL_ONLINE] = STR(EXTERNAL_ONLINE);
externalStates[EXTERNAL_PAUSE] = STR(EXTERNAL_PAUSE);
externalStates[EXTERNAL_RESUME] = STR(EXTERNAL_RESUME);
if(external_state >= EXTERNAL_MAXSTATES) {
return "EXTERNAL_INVALID";
}
return externalStates[external_state];
}
bool isDownscaleRequired(hwc_layer_1_t const* layer) {
hwc_rect_t displayFrame = layer->displayFrame;
hwc_rect_t sourceCrop = integerizeSourceCrop(layer->sourceCropf);
int dst_w, dst_h, src_w, src_h;
dst_w = displayFrame.right - displayFrame.left;
dst_h = displayFrame.bottom - displayFrame.top;
src_w = sourceCrop.right - sourceCrop.left;
src_h = sourceCrop.bottom - sourceCrop.top;
if(((src_w > dst_w) || (src_h > dst_h)))
return true;
return false;
}
bool needsScaling(hwc_layer_1_t const* layer) {
int dst_w, dst_h, src_w, src_h;
hwc_rect_t displayFrame = layer->displayFrame;
hwc_rect_t sourceCrop = integerizeSourceCrop(layer->sourceCropf);
dst_w = displayFrame.right - displayFrame.left;
dst_h = displayFrame.bottom - displayFrame.top;
src_w = sourceCrop.right - sourceCrop.left;
src_h = sourceCrop.bottom - sourceCrop.top;
if(((src_w != dst_w) || (src_h != dst_h)))
return true;
return false;
}
// Checks if layer needs scaling with split
bool needsScalingWithSplit(hwc_context_t* ctx, hwc_layer_1_t const* layer,
const int& dpy) {
int src_width_l, src_height_l;
int src_width_r, src_height_r;
int dst_width_l, dst_height_l;
int dst_width_r, dst_height_r;
int hw_w = ctx->dpyAttr[dpy].xres;
int hw_h = ctx->dpyAttr[dpy].yres;
hwc_rect_t cropL, dstL, cropR, dstR;
const int lSplit = getLeftSplit(ctx, dpy);
hwc_rect_t sourceCrop = integerizeSourceCrop(layer->sourceCropf);
hwc_rect_t displayFrame = layer->displayFrame;
private_handle_t *hnd = (private_handle_t *)layer->handle;
cropL = sourceCrop;
dstL = displayFrame;
hwc_rect_t scissorL = { 0, 0, lSplit, hw_h };
scissorL = getIntersection(ctx->mViewFrame[dpy], scissorL);
qhwc::calculate_crop_rects(cropL, dstL, scissorL, 0);
cropR = sourceCrop;
dstR = displayFrame;
hwc_rect_t scissorR = { lSplit, 0, hw_w, hw_h };
scissorR = getIntersection(ctx->mViewFrame[dpy], scissorR);
qhwc::calculate_crop_rects(cropR, dstR, scissorR, 0);
// Sanitize Crop to stitch
sanitizeSourceCrop(cropL, cropR, hnd);
// Calculate the left dst
dst_width_l = dstL.right - dstL.left;
dst_height_l = dstL.bottom - dstL.top;
src_width_l = cropL.right - cropL.left;
src_height_l = cropL.bottom - cropL.top;
// check if there is any scaling on the left
if(((src_width_l != dst_width_l) || (src_height_l != dst_height_l)))
return true;
// Calculate the right dst
dst_width_r = dstR.right - dstR.left;
dst_height_r = dstR.bottom - dstR.top;
src_width_r = cropR.right - cropR.left;
src_height_r = cropR.bottom - cropR.top;
// check if there is any scaling on the right
if(((src_width_r != dst_width_r) || (src_height_r != dst_height_r)))
return true;
return false;
}
bool isAlphaScaled(hwc_layer_1_t const* layer) {
if(needsScaling(layer) && isAlphaPresent(layer)) {
return true;
}
return false;
}
bool isAlphaPresent(hwc_layer_1_t const* layer) {
private_handle_t *hnd = (private_handle_t *)layer->handle;
if(hnd) {
int format = hnd->format;
switch(format) {
case HAL_PIXEL_FORMAT_RGBA_8888:
case HAL_PIXEL_FORMAT_BGRA_8888:
// In any more formats with Alpha go here..
return true;
default : return false;
}
}
return false;
}
static void trimLayer(hwc_context_t *ctx, const int& dpy, const int& transform,
hwc_rect_t& crop, hwc_rect_t& dst) {
int hw_w = ctx->dpyAttr[dpy].xres;
int hw_h = ctx->dpyAttr[dpy].yres;
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 };
scissor = getIntersection(ctx->mViewFrame[dpy], scissor);
qhwc::calculate_crop_rects(crop, dst, scissor, transform);
}
}
static void trimList(hwc_context_t *ctx, hwc_display_contents_1_t *list,
const int& dpy) {
for(uint32_t i = 0; i < list->numHwLayers - 1; i++) {
hwc_layer_1_t *layer = &list->hwLayers[i];
hwc_rect_t crop = integerizeSourceCrop(layer->sourceCropf);
int transform = (list->hwLayers[i].flags & HWC_COLOR_FILL) ? 0 :
list->hwLayers[i].transform;
trimLayer(ctx, dpy,
transform,
(hwc_rect_t&)crop,
(hwc_rect_t&)list->hwLayers[i].displayFrame);
layer->sourceCropf.left = (float)crop.left;
layer->sourceCropf.right = (float)crop.right;
layer->sourceCropf.top = (float)crop.top;
layer->sourceCropf.bottom = (float)crop.bottom;
}
}
void setListStats(hwc_context_t *ctx,
hwc_display_contents_1_t *list, int dpy) {
const int prevYuvCount = ctx->listStats[dpy].yuvCount;
memset(&ctx->listStats[dpy], 0, sizeof(ListStats));
ctx->listStats[dpy].numAppLayers = (int)list->numHwLayers - 1;
ctx->listStats[dpy].fbLayerIndex = (int)list->numHwLayers - 1;
ctx->listStats[dpy].skipCount = 0;
ctx->listStats[dpy].preMultipliedAlpha = false;
ctx->listStats[dpy].isSecurePresent = false;
ctx->listStats[dpy].yuvCount = 0;
char property[PROPERTY_VALUE_MAX];
ctx->listStats[dpy].isDisplayAnimating = false;
ctx->listStats[dpy].secureUI = false;
ctx->listStats[dpy].yuv4k2kCount = 0;
ctx->dpyAttr[dpy].mActionSafePresent = isActionSafePresent(ctx, dpy);
ctx->listStats[dpy].renderBufIndexforABC = -1;
ctx->listStats[dpy].secureRGBCount = 0;
ctx->listStats[dpy].refreshRateRequest = ctx->dpyAttr[dpy].refreshRate;
uint32_t refreshRate = 0;
qdutils::MDPVersion& mdpHw = qdutils::MDPVersion::getInstance();
ctx->mAIVVideoMode[dpy] = false;
resetROI(ctx, dpy);
trimList(ctx, list, dpy);
optimizeLayerRects(list);
for (size_t i = 0; i < (size_t)ctx->listStats[dpy].numAppLayers; i++) {
hwc_layer_1_t const* layer = &list->hwLayers[i];
private_handle_t *hnd = (private_handle_t *)layer->handle;
#ifdef QCOM_BSP
if(ctx->mWindowboxFeature && dpy && isAIVVideoLayer(layer)) {
ctx->mAIVVideoMode[dpy] = true;
}
if (layer->flags & HWC_SCREENSHOT_ANIMATOR_LAYER) {
ctx->listStats[dpy].isDisplayAnimating = true;
}
if(isSecureDisplayBuffer(hnd)) {
ctx->listStats[dpy].secureUI = true;
}
#endif
// continue if number of app layers exceeds MAX_NUM_APP_LAYERS
if(ctx->listStats[dpy].numAppLayers > MAX_NUM_APP_LAYERS)
continue;
//reset yuv indices
ctx->listStats[dpy].yuvIndices[i] = -1;
ctx->listStats[dpy].yuv4k2kIndices[i] = -1;
if (isSecureBuffer(hnd)) {
ctx->listStats[dpy].isSecurePresent = true;
if(not isYuvBuffer(hnd)) {
// cache secureRGB layer parameters like we cache for YUV layers
int& secureRGBCount = ctx->listStats[dpy].secureRGBCount;
ctx->listStats[dpy].secureRGBIndices[secureRGBCount] = (int)i;
secureRGBCount++;
}
}
if (isSkipLayer(&list->hwLayers[i])) {
ctx->listStats[dpy].skipCount++;
}
if (UNLIKELY(isYuvBuffer(hnd))) {
int& yuvCount = ctx->listStats[dpy].yuvCount;
ctx->listStats[dpy].yuvIndices[yuvCount] = (int)i;
yuvCount++;
if(UNLIKELY(isYUVSplitNeeded(hnd))){
int& yuv4k2kCount = ctx->listStats[dpy].yuv4k2kCount;
ctx->listStats[dpy].yuv4k2kIndices[yuv4k2kCount] = (int)i;
yuv4k2kCount++;
}
}
if(layer->blending == HWC_BLENDING_PREMULT)
ctx->listStats[dpy].preMultipliedAlpha = true;
#ifdef DYNAMIC_FPS
if (dpy == HWC_DISPLAY_PRIMARY && mdpHw.isDynFpsSupported()) {
//dyn fps: get refreshrate from metadata
//Support multiple refresh rates if they are same
//else set to default
MetaData_t *mdata = hnd ? (MetaData_t *)hnd->base_metadata : NULL;
if (mdata && (mdata->operation & UPDATE_REFRESH_RATE)) {
// Valid refreshRate in metadata and within the range
uint32_t rate = roundOff(mdata->refreshrate);
if((rate >= mdpHw.getMinFpsSupported() &&
rate <= mdpHw.getMaxFpsSupported())) {
if (!refreshRate) {
refreshRate = rate;
} else if(refreshRate != rate) {
// multiple refreshrate requests, set to default
refreshRate = ctx->dpyAttr[dpy].refreshRate;
}
}
}
}
#endif
}
if(ctx->listStats[dpy].yuvCount > 0) {
if (property_get("hw.cabl.yuv", property, NULL) > 0) {
if (atoi(property) != 1) {
property_set("hw.cabl.yuv", "1");
}
}
} else {
if (property_get("hw.cabl.yuv", property, NULL) > 0) {
if (atoi(property) != 0) {
property_set("hw.cabl.yuv", "0");
}
}
}
//The marking of video begin/end is useful on some targets where we need
//to have a padding round to be able to shift pipes across mixers.
if(prevYuvCount != ctx->listStats[dpy].yuvCount) {
ctx->mVideoTransFlag = true;
}
if(dpy == HWC_DISPLAY_PRIMARY) {
ctx->mAD->markDoable(ctx, list);
//Store the requested fresh rate
ctx->listStats[dpy].refreshRateRequest = refreshRate ?
refreshRate : ctx->dpyAttr[dpy].refreshRate;
}
}
static void calc_cut(double& leftCutRatio, double& topCutRatio,
double& rightCutRatio, double& bottomCutRatio, int orient) {
if(orient & HAL_TRANSFORM_FLIP_H) {
swap(leftCutRatio, rightCutRatio);
}
if(orient & HAL_TRANSFORM_FLIP_V) {
swap(topCutRatio, bottomCutRatio);
}
if(orient & HAL_TRANSFORM_ROT_90) {
//Anti clock swapping
double tmpCutRatio = leftCutRatio;
leftCutRatio = topCutRatio;
topCutRatio = rightCutRatio;
rightCutRatio = bottomCutRatio;
bottomCutRatio = tmpCutRatio;
}
}
bool isSecuring(hwc_context_t* ctx, hwc_layer_1_t const* layer) {
if((ctx->mMDP.version < qdutils::MDSS_V5) &&
(ctx->mMDP.version > qdutils::MDP_V3_0) &&
ctx->mSecuring) {
return true;
}
if (isSecureModePolicy(ctx->mMDP.version)) {
private_handle_t *hnd = (private_handle_t *)layer->handle;
if(ctx->mSecureMode) {
if (! isSecureBuffer(hnd)) {
ALOGD_IF(HWC_UTILS_DEBUG,"%s:Securing Turning ON ...",
__FUNCTION__);
return true;
}
} else {
if (isSecureBuffer(hnd)) {
ALOGD_IF(HWC_UTILS_DEBUG,"%s:Securing Turning OFF ...",
__FUNCTION__);
return true;
}
}
}
return false;
}
bool isSecureModePolicy(int mdpVersion) {
if (mdpVersion < qdutils::MDSS_V5)
return true;
else
return false;
}
bool isRotatorSupportedFormat(private_handle_t *hnd) {
// Following rotator src formats are supported by mdp driver
// TODO: Add more formats in future, if mdp driver adds support
switch(hnd->format) {
case HAL_PIXEL_FORMAT_RGBA_8888:
case HAL_PIXEL_FORMAT_RGB_565:
case HAL_PIXEL_FORMAT_RGB_888:
case HAL_PIXEL_FORMAT_BGRA_8888:
return true;
default:
return false;
}
return false;
}
bool isRotationDoable(hwc_context_t *ctx, private_handle_t *hnd) {
// Rotate layers, if it is YUV type or rendered by CPU and not
// for the MDP versions below MDP5
if((isCPURendered(hnd) && isRotatorSupportedFormat(hnd) &&
!ctx->mMDP.version < qdutils::MDSS_V5)
|| isYuvBuffer(hnd)) {
return true;
}
return false;
}
// returns true if Action safe dimensions are set and target supports Actionsafe
bool isActionSafePresent(hwc_context_t *ctx, int dpy) {
// if external supports underscan, do nothing
// it will be taken care in the driver
// Disable Action safe for 8974 due to HW limitation for downscaling
// layers with overlapped region
// Disable Actionsafe for non HDMI displays.
if(!(dpy == HWC_DISPLAY_EXTERNAL) ||
qdutils::MDPVersion::getInstance().is8x74v2() ||
ctx->mHDMIDisplay->isCEUnderscanSupported()) {
return false;
}
char value[PROPERTY_VALUE_MAX];
// Read action safe properties
property_get("persist.sys.actionsafe.width", value, "0");
ctx->dpyAttr[dpy].mAsWidthRatio = atoi(value);
property_get("persist.sys.actionsafe.height", value, "0");
ctx->dpyAttr[dpy].mAsHeightRatio = atoi(value);
if(!ctx->dpyAttr[dpy].mAsWidthRatio && !ctx->dpyAttr[dpy].mAsHeightRatio) {
//No action safe ratio set, return
return false;
}
return true;
}
int getBlending(int blending) {
switch(blending) {
case HWC_BLENDING_NONE:
return overlay::utils::OVERLAY_BLENDING_OPAQUE;
case HWC_BLENDING_PREMULT:
return overlay::utils::OVERLAY_BLENDING_PREMULT;
case HWC_BLENDING_COVERAGE :
default:
return overlay::utils::OVERLAY_BLENDING_COVERAGE;
}
}
//Crops source buffer against destination and FB boundaries
void calculate_crop_rects(hwc_rect_t& crop, hwc_rect_t& dst,
const hwc_rect_t& scissor, int orient) {
int& crop_l = crop.left;
int& crop_t = crop.top;
int& crop_r = crop.right;
int& crop_b = crop.bottom;
int crop_w = crop.right - crop.left;
int crop_h = crop.bottom - crop.top;
int& dst_l = dst.left;
int& dst_t = dst.top;
int& dst_r = dst.right;
int& dst_b = dst.bottom;
int dst_w = abs(dst.right - dst.left);
int dst_h = abs(dst.bottom - dst.top);
const int& sci_l = scissor.left;
const int& sci_t = scissor.top;
const int& sci_r = scissor.right;
const int& sci_b = scissor.bottom;
double leftCutRatio = 0.0, rightCutRatio = 0.0, topCutRatio = 0.0,
bottomCutRatio = 0.0;
if(dst_l < sci_l) {
leftCutRatio = (double)(sci_l - dst_l) / (double)dst_w;
dst_l = sci_l;
}
if(dst_r > sci_r) {
rightCutRatio = (double)(dst_r - sci_r) / (double)dst_w;
dst_r = sci_r;
}
if(dst_t < sci_t) {
topCutRatio = (double)(sci_t - dst_t) / (double)dst_h;
dst_t = sci_t;
}
if(dst_b > sci_b) {
bottomCutRatio = (double)(dst_b - sci_b) / (double)dst_h;
dst_b = sci_b;
}
calc_cut(leftCutRatio, topCutRatio, rightCutRatio, bottomCutRatio, orient);
crop_l += (int)round((double)crop_w * leftCutRatio);
crop_t += (int)round((double)crop_h * topCutRatio);
crop_r -= (int)round((double)crop_w * rightCutRatio);
crop_b -= (int)round((double)crop_h * bottomCutRatio);
}
bool areLayersIntersecting(const hwc_layer_1_t* layer1,
const hwc_layer_1_t* layer2) {
hwc_rect_t irect = getIntersection(layer1->displayFrame,
layer2->displayFrame);
return isValidRect(irect);
}
bool isSameRect(const hwc_rect& rect1, const hwc_rect& rect2)
{
return ((rect1.left == rect2.left) && (rect1.top == rect2.top) &&
(rect1.right == rect2.right) && (rect1.bottom == rect2.bottom));
}
bool isValidRect(const hwc_rect& rect)
{
return ((rect.bottom > rect.top) && (rect.right > rect.left)) ;
}
bool operator ==(const hwc_rect_t& lhs, const hwc_rect_t& rhs) {
if(lhs.left == rhs.left && lhs.top == rhs.top &&
lhs.right == rhs.right && lhs.bottom == rhs.bottom )
return true ;
return false;
}
hwc_rect_t moveRect(const hwc_rect_t& rect, const int& x_off, const int& y_off)
{
hwc_rect_t res;
if(!isValidRect(rect))
return (hwc_rect_t){0, 0, 0, 0};
res.left = rect.left + x_off;
res.top = rect.top + y_off;
res.right = rect.right + x_off;
res.bottom = rect.bottom + y_off;
return res;
}
/* computes the intersection of two rects */
hwc_rect_t getIntersection(const hwc_rect_t& rect1, const hwc_rect_t& rect2)
{
hwc_rect_t res;
if(!isValidRect(rect1) || !isValidRect(rect2)){
return (hwc_rect_t){0, 0, 0, 0};
}
res.left = max(rect1.left, rect2.left);
res.top = max(rect1.top, rect2.top);
res.right = min(rect1.right, rect2.right);
res.bottom = min(rect1.bottom, rect2.bottom);
if(!isValidRect(res))
return (hwc_rect_t){0, 0, 0, 0};
return res;
}
/* computes the union of two rects */
hwc_rect_t getUnion(const hwc_rect &rect1, const hwc_rect &rect2)
{
hwc_rect_t res;
if(!isValidRect(rect1)){
return rect2;
}
if(!isValidRect(rect2)){
return rect1;
}
res.left = min(rect1.left, rect2.left);
res.top = min(rect1.top, rect2.top);
res.right = max(rect1.right, rect2.right);
res.bottom = max(rect1.bottom, rect2.bottom);
return res;
}
/* Not a geometrical rect deduction. Deducts rect2 from rect1 only if it results
* a single rect */
hwc_rect_t deductRect(const hwc_rect_t& rect1, const hwc_rect_t& rect2) {
hwc_rect_t res = rect1;
if((rect1.left == rect2.left) && (rect1.right == rect2.right)) {
if((rect1.top == rect2.top) && (rect2.bottom <= rect1.bottom))
res.top = rect2.bottom;
else if((rect1.bottom == rect2.bottom)&& (rect2.top >= rect1.top))
res.bottom = rect2.top;
}
else if((rect1.top == rect2.top) && (rect1.bottom == rect2.bottom)) {
if((rect1.left == rect2.left) && (rect2.right <= rect1.right))
res.left = rect2.right;
else if((rect1.right == rect2.right)&& (rect2.left >= rect1.left))
res.right = rect2.left;
}
return res;
}
void optimizeLayerRects(const hwc_display_contents_1_t *list) {
int i= (int)list->numHwLayers-2;
while(i > 0) {
//see if there is no blending required.
//If it is opaque see if we can substract this region from below
//layers.
if(list->hwLayers[i].blending == HWC_BLENDING_NONE) {
int j= i-1;
hwc_rect_t& topframe =
(hwc_rect_t&)list->hwLayers[i].displayFrame;
while(j >= 0) {
if(!needsScaling(&list->hwLayers[j])) {
hwc_layer_1_t* layer = (hwc_layer_1_t*)&list->hwLayers[j];
hwc_rect_t& bottomframe = layer->displayFrame;
hwc_rect_t bottomCrop =
integerizeSourceCrop(layer->sourceCropf);
int transform = (layer->flags & HWC_COLOR_FILL) ? 0 :
layer->transform;
hwc_rect_t irect = getIntersection(bottomframe, topframe);
if(isValidRect(irect)) {
hwc_rect_t dest_rect;
//if intersection is valid rect, deduct it
dest_rect = deductRect(bottomframe, irect);
qhwc::calculate_crop_rects(bottomCrop, bottomframe,
dest_rect, transform);
//Update layer sourceCropf
layer->sourceCropf.left =(float)bottomCrop.left;
layer->sourceCropf.top = (float)bottomCrop.top;
layer->sourceCropf.right = (float)bottomCrop.right;
layer->sourceCropf.bottom = (float)bottomCrop.bottom;
#ifdef QCOM_BSP
//Update layer dirtyRect
layer->dirtyRect = getIntersection(bottomCrop,
layer->dirtyRect);
#endif
}
}
j--;
}
}
i--;
}
}
void getNonWormholeRegion(hwc_display_contents_1_t* list,
hwc_rect_t& nwr)
{
size_t last = list->numHwLayers - 1;
hwc_rect_t fbDisplayFrame = list->hwLayers[last].displayFrame;
//Initiliaze nwr to first frame
nwr.left = list->hwLayers[0].displayFrame.left;
nwr.top = list->hwLayers[0].displayFrame.top;
nwr.right = list->hwLayers[0].displayFrame.right;
nwr.bottom = list->hwLayers[0].displayFrame.bottom;
for (size_t i = 1; i < last; i++) {
hwc_rect_t displayFrame = list->hwLayers[i].displayFrame;
nwr = getUnion(nwr, displayFrame);
}
//Intersect with the framebuffer
nwr = getIntersection(nwr, fbDisplayFrame);
}
bool isExternalActive(hwc_context_t* ctx) {
return ctx->dpyAttr[HWC_DISPLAY_EXTERNAL].isActive;
}
void closeAcquireFds(hwc_display_contents_1_t* list) {
if(LIKELY(list)) {
for(uint32_t i = 0; i < list->numHwLayers; i++) {
//Close the acquireFenceFds
//HWC_FRAMEBUFFER are -1 already by SF, rest we close.
if(list->hwLayers[i].acquireFenceFd >= 0) {
close(list->hwLayers[i].acquireFenceFd);
list->hwLayers[i].acquireFenceFd = -1;
}
}
//Writeback
if(list->outbufAcquireFenceFd >= 0) {
close(list->outbufAcquireFenceFd);
list->outbufAcquireFenceFd = -1;
}
}
}
int hwc_sync(hwc_context_t *ctx, hwc_display_contents_1_t* list, int dpy,
int fd) {
ATRACE_CALL();
int ret = 0;
int acquireFd[MAX_NUM_APP_LAYERS];
int count = 0;
int releaseFd = -1;
int retireFd = -1;
int fbFd = -1;
bool swapzero = false;
struct mdp_buf_sync data;
memset(&data, 0, sizeof(data));
data.acq_fen_fd = acquireFd;
data.rel_fen_fd = &releaseFd;
data.retire_fen_fd = &retireFd;
data.flags = MDP_BUF_SYNC_FLAG_RETIRE_FENCE;
char property[PROPERTY_VALUE_MAX];
if(property_get("debug.egl.swapinterval", property, "1") > 0) {
if(atoi(property) == 0)
swapzero = true;
}
bool isExtAnimating = false;
if(dpy)
isExtAnimating = ctx->listStats[dpy].isDisplayAnimating;
//Send acquireFenceFds to rotator
for(uint32_t i = 0; i < ctx->mLayerRotMap[dpy]->getCount(); i++) {
int rotFd = ctx->mRotMgr->getRotDevFd();
int rotReleaseFd = -1;
overlay::Rotator* currRot = ctx->mLayerRotMap[dpy]->getRot(i);
hwc_layer_1_t* currLayer = ctx->mLayerRotMap[dpy]->getLayer(i);
if((currRot == NULL) || (currLayer == NULL)) {
continue;
}
struct mdp_buf_sync rotData;
memset(&rotData, 0, sizeof(rotData));
rotData.acq_fen_fd =
&currLayer->acquireFenceFd;
rotData.rel_fen_fd = &rotReleaseFd; //driver to populate this
rotData.session_id = currRot->getSessId();
if(currLayer->acquireFenceFd >= 0) {
rotData.acq_fen_fd_cnt = 1; //1 ioctl call per rot session
}
int ret = 0;
if(not ctx->mLayerRotMap[dpy]->isRotCached(i))
ret = ioctl(rotFd, MSMFB_BUFFER_SYNC, &rotData);
if(ret < 0) {
ALOGE("%s: ioctl MSMFB_BUFFER_SYNC failed for rot sync, err=%s",
__FUNCTION__, strerror(errno));
close(rotReleaseFd);
} else {
close(currLayer->acquireFenceFd);
//For MDP to wait on.
currLayer->acquireFenceFd =
dup(rotReleaseFd);
//A buffer is free to be used by producer as soon as its copied to
//rotator
currLayer->releaseFenceFd =
rotReleaseFd;
}
}
//Accumulate acquireFenceFds for MDP Overlays
if(list->outbufAcquireFenceFd >= 0) {
//Writeback output buffer
acquireFd[count++] = list->outbufAcquireFenceFd;
}
for(uint32_t i = 0; i < list->numHwLayers; i++) {
if(((isAbcInUse(ctx)== true ) ||
(list->hwLayers[i].compositionType == HWC_OVERLAY)) &&
list->hwLayers[i].acquireFenceFd >= 0) {
if(UNLIKELY(swapzero))
acquireFd[count++] = -1;
// if ABC is enabled for more than one layer.
// renderBufIndexforABC will work as FB.Hence
// set the acquireFD from fd - which is coming from copybit
else if(fd >= 0 && (isAbcInUse(ctx) == true)) {
if(ctx->listStats[dpy].renderBufIndexforABC ==(int32_t)i)
acquireFd[count++] = fd;
else
continue;
} else
acquireFd[count++] = list->hwLayers[i].acquireFenceFd;
}
if(list->hwLayers[i].compositionType == HWC_FRAMEBUFFER_TARGET) {
if(UNLIKELY(swapzero))
acquireFd[count++] = -1;
else if(fd >= 0) {
//set the acquireFD from fd - which is coming from c2d
acquireFd[count++] = fd;
// Buffer sync IOCTL should be async when using c2d fence is
// used
data.flags &= ~MDP_BUF_SYNC_FLAG_WAIT;
} else if(list->hwLayers[i].acquireFenceFd >= 0)
acquireFd[count++] = list->hwLayers[i].acquireFenceFd;
}
}
if ((fd >= 0) && !dpy && ctx->mPtorInfo.isActive()) {
// Acquire c2d fence of Overlap render buffer
acquireFd[count++] = fd;
}
data.acq_fen_fd_cnt = count;
fbFd = ctx->dpyAttr[dpy].fd;
//Waits for acquire fences, returns a release fence
if(LIKELY(!swapzero)) {
ret = ioctl(fbFd, MSMFB_BUFFER_SYNC, &data);
}
if(ret < 0) {
ALOGE("%s: ioctl MSMFB_BUFFER_SYNC failed, err=%s",
__FUNCTION__, strerror(errno));
ALOGE("%s: acq_fen_fd_cnt=%d flags=%d fd=%d dpy=%d numHwLayers=%zu",
__FUNCTION__, data.acq_fen_fd_cnt, data.flags, fbFd,
dpy, list->numHwLayers);
close(releaseFd);
releaseFd = -1;
close(retireFd);
retireFd = -1;
}
for(uint32_t i = 0; i < list->numHwLayers; i++) {
if(list->hwLayers[i].compositionType == HWC_OVERLAY ||
#ifdef QCOM_BSP
list->hwLayers[i].compositionType == HWC_BLIT ||
#endif
list->hwLayers[i].compositionType == HWC_FRAMEBUFFER_TARGET) {
//Populate releaseFenceFds.
if(UNLIKELY(swapzero)) {
list->hwLayers[i].releaseFenceFd = -1;
} else if(isExtAnimating) {
// Release all the app layer fds immediately,
// if animation is in progress.
list->hwLayers[i].releaseFenceFd = -1;
} else if(list->hwLayers[i].releaseFenceFd < 0 ) {
#ifdef QCOM_BSP
//If rotator has not already populated this field
// & if it's a not VPU layer
// if ABC is enabled for more than one layer
if(fd >= 0 && (isAbcInUse(ctx) == true) &&
ctx->listStats[dpy].renderBufIndexforABC !=(int32_t)i){
list->hwLayers[i].releaseFenceFd = dup(fd);
} else if((list->hwLayers[i].compositionType == HWC_BLIT)&&
(isAbcInUse(ctx) == false)){
//For Blit, the app layers should be released when the Blit
//is complete. This fd was passed from copybit->draw
list->hwLayers[i].releaseFenceFd = dup(fd);
} else
#endif
{
list->hwLayers[i].releaseFenceFd = dup(releaseFd);
}
}
}
}
if(fd >= 0) {
close(fd);
fd = -1;
}
if (ctx->mCopyBit[dpy]) {
if (!dpy && ctx->mPtorInfo.isActive())
ctx->mCopyBit[dpy]->setReleaseFdSync(releaseFd);
else
ctx->mCopyBit[dpy]->setReleaseFd(releaseFd);
}
//Signals when MDP finishes reading rotator buffers.
ctx->mLayerRotMap[dpy]->setReleaseFd(releaseFd);
close(releaseFd);
releaseFd = -1;
if(UNLIKELY(swapzero)) {
list->retireFenceFd = -1;
} else {
list->retireFenceFd = retireFd;
}
return ret;
}
void setMdpFlags(hwc_context_t *ctx, hwc_layer_1_t *layer,
ovutils::eMdpFlags &mdpFlags,
int rotDownscale, int transform) {
private_handle_t *hnd = (private_handle_t *)layer->handle;
MetaData_t *metadata = hnd ? (MetaData_t *)hnd->base_metadata : NULL;
if(layer->blending == HWC_BLENDING_PREMULT) {
ovutils::setMdpFlags(mdpFlags,
ovutils::OV_MDP_BLEND_FG_PREMULT);
}
if(metadata && (metadata->operation & PP_PARAM_INTERLACED) &&
metadata->interlaced) {
ovutils::setMdpFlags(mdpFlags,
ovutils::OV_MDP_DEINTERLACE);
}
// Mark MDP flags with SECURE_OVERLAY_SESSION for driver
if(isSecureBuffer(hnd)) {
ovutils::setMdpFlags(mdpFlags,
ovutils::OV_MDP_SECURE_OVERLAY_SESSION);
}
if(isSecureDisplayBuffer(hnd)) {
// Mark MDP flags with SECURE_DISPLAY_OVERLAY_SESSION for driver
ovutils::setMdpFlags(mdpFlags,
ovutils::OV_MDP_SECURE_DISPLAY_OVERLAY_SESSION);
}
//Pre-rotation will be used using rotator.
if(has90Transform(layer) && isRotationDoable(ctx, hnd)) {
ovutils::setMdpFlags(mdpFlags,
ovutils::OV_MDP_SOURCE_ROTATED_90);
}
//No 90 component and no rot-downscale then flips done by MDP
//If we use rot then it might as well do flips
if(!(transform & HWC_TRANSFORM_ROT_90) && !rotDownscale) {
if(transform & HWC_TRANSFORM_FLIP_H) {
ovutils::setMdpFlags(mdpFlags, ovutils::OV_MDP_FLIP_H);
}
if(transform & HWC_TRANSFORM_FLIP_V) {
ovutils::setMdpFlags(mdpFlags, ovutils::OV_MDP_FLIP_V);
}
}
if(metadata &&
((metadata->operation & PP_PARAM_HSIC)
|| (metadata->operation & PP_PARAM_IGC)
|| (metadata->operation & PP_PARAM_SHARP2))) {
ovutils::setMdpFlags(mdpFlags, ovutils::OV_MDP_PP_EN);
}
}
int configRotator(Rotator *rot, Whf& whf,
hwc_rect_t& crop, const eMdpFlags& mdpFlags,
const eTransform& orient, const int& downscale) {
// Fix alignments for TILED format
if(whf.format == MDP_Y_CRCB_H2V2_TILE ||
whf.format == MDP_Y_CBCR_H2V2_TILE) {
whf.w = utils::alignup(whf.w, 64);
whf.h = utils::alignup(whf.h, 32);
}
rot->setSource(whf);
if (qdutils::MDPVersion::getInstance().getMDPVersion() >=
qdutils::MDSS_V5) {
Dim rotCrop(crop.left, crop.top, crop.right - crop.left,
crop.bottom - crop.top);
rot->setCrop(rotCrop);
}
rot->setFlags(mdpFlags);
rot->setTransform(orient);
rot->setDownscale(downscale);
if(!rot->commit()) return -1;
return 0;
}
int configMdp(Overlay *ov, const PipeArgs& parg,
const eTransform& orient, const hwc_rect_t& crop,
const hwc_rect_t& pos, const MetaData_t *metadata,
const eDest& dest) {
ov->setSource(parg, dest);
ov->setTransform(orient, dest);
int crop_w = crop.right - crop.left;
int crop_h = crop.bottom - crop.top;
Dim dcrop(crop.left, crop.top, crop_w, crop_h);
ov->setCrop(dcrop, dest);
int posW = pos.right - pos.left;
int posH = pos.bottom - pos.top;
Dim position(pos.left, pos.top, posW, posH);
ov->setPosition(position, dest);
if (metadata)
ov->setVisualParams(*metadata, dest);
if (!ov->commit(dest)) {
return -1;
}
return 0;
}
int configColorLayer(hwc_context_t *ctx, hwc_layer_1_t *layer,
const int& dpy, eMdpFlags& mdpFlags, eZorder& z,
const eDest& dest) {
hwc_rect_t dst = layer->displayFrame;
trimLayer(ctx, dpy, 0, dst, dst);
int w = ctx->dpyAttr[dpy].xres;
int h = ctx->dpyAttr[dpy].yres;
int dst_w = dst.right - dst.left;
int dst_h = dst.bottom - dst.top;
uint32_t color = layer->transform;
Whf whf(w, h, getMdpFormat(HAL_PIXEL_FORMAT_RGBA_8888), 0);
ovutils::setMdpFlags(mdpFlags, ovutils::OV_MDP_SOLID_FILL);
if (layer->blending == HWC_BLENDING_PREMULT)
ovutils::setMdpFlags(mdpFlags, ovutils::OV_MDP_BLEND_FG_PREMULT);
PipeArgs parg(mdpFlags, whf, z, static_cast<eRotFlags>(0),
layer->planeAlpha,
(ovutils::eBlending) getBlending(layer->blending));
// Configure MDP pipe for Color layer
Dim pos(dst.left, dst.top, dst_w, dst_h);
ctx->mOverlay->setSource(parg, dest);
ctx->mOverlay->setColor(color, dest);
ctx->mOverlay->setTransform(0, dest);
ctx->mOverlay->setCrop(pos, dest);
ctx->mOverlay->setPosition(pos, dest);
if (!ctx->mOverlay->commit(dest)) {
ALOGE("%s: Configure color layer failed!", __FUNCTION__);
return -1;
}
return 0;
}
void updateSource(eTransform& orient, Whf& whf,
hwc_rect_t& crop, Rotator *rot) {
Dim transformedCrop(crop.left, crop.top,
crop.right - crop.left,
crop.bottom - crop.top);
if (qdutils::MDPVersion::getInstance().getMDPVersion() >=
qdutils::MDSS_V5) {
//B-family rotator internally could modify destination dimensions if
//downscaling is supported
whf = rot->getDstWhf();
transformedCrop = rot->getDstDimensions();
} else {
//A-family rotator rotates entire buffer irrespective of crop, forcing
//us to recompute the crop based on transform
orient = static_cast<eTransform>(ovutils::getMdpOrient(orient));
preRotateSource(orient, whf, transformedCrop);
}
crop.left = transformedCrop.x;
crop.top = transformedCrop.y;
crop.right = transformedCrop.x + transformedCrop.w;
crop.bottom = transformedCrop.y + transformedCrop.h;
}
int getRotDownscale(hwc_context_t *ctx, const hwc_layer_1_t *layer) {
if(not qdutils::MDPVersion::getInstance().isRotDownscaleEnabled()) {
return 0;
}
int downscale = 0;
hwc_rect_t crop = integerizeSourceCrop(layer->sourceCropf);
hwc_rect_t dst = layer->displayFrame;
private_handle_t *hnd = (private_handle_t *)layer->handle;
if(not hnd) {
return 0;
}
MetaData_t *metadata = (MetaData_t *)hnd->base_metadata;
bool isInterlaced = metadata && (metadata->operation & PP_PARAM_INTERLACED)
&& metadata->interlaced;
int transform = layer->transform;
uint32_t format = ovutils::getMdpFormat(hnd->format, isTileRendered(hnd));
if(isYuvBuffer(hnd)) {
if(ctx->mMDP.version >= qdutils::MDP_V4_2 &&
ctx->mMDP.version < qdutils::MDSS_V5) {
downscale = Rotator::getDownscaleFactor(crop.right - crop.left,
crop.bottom - crop.top, dst.right - dst.left,
dst.bottom - dst.top, format, isInterlaced);
} else {
Dim adjCrop(crop.left, crop.top, crop.right - crop.left,
crop.bottom - crop.top);
Dim pos(dst.left, dst.top, dst.right - dst.left,
dst.bottom - dst.top);
if(transform & HAL_TRANSFORM_ROT_90) {
swap(adjCrop.w, adjCrop.h);
}
downscale = Rotator::getDownscaleFactor(adjCrop.w, adjCrop.h, pos.w,
pos.h, format, isInterlaced);
}
}
return downscale;
}
bool isZoomModeEnabled(hwc_rect_t crop) {
// This does not work for zooming in top left corner of the image
return(crop.top > 0 || crop.left > 0);
}
void updateCropAIVVideoMode(hwc_context_t *ctx, hwc_rect_t& crop, int dpy) {
ALOGD_IF(HWC_UTILS_DEBUG, "dpy %d Source crop [%d %d %d %d]", dpy,
crop.left, crop.top, crop.right, crop.bottom);
if(isZoomModeEnabled(crop)) {
Dim srcCrop(crop.left, crop.top,
crop.right - crop.left,
crop.bottom - crop.top);
int extW = ctx->dpyAttr[dpy].xres;
int extH = ctx->dpyAttr[dpy].yres;
//Crop the original video in order to fit external display aspect ratio
if(srcCrop.w * extH < extW * srcCrop.h) {
int offset = (srcCrop.h - ((srcCrop.w * extH) / extW)) / 2;
crop.top += offset;
crop.bottom -= offset;
} else {
int offset = (srcCrop.w - ((extW * srcCrop.h) / extH)) / 2;
crop.left += offset;
crop.right -= offset;
}
ALOGD_IF(HWC_UTILS_DEBUG, "External Resolution [%d %d] dpy %d Modified"
" source crop [%d %d %d %d]", extW, extH, dpy,
crop.left, crop.top, crop.right, crop.bottom);
}
}
void updateDestAIVVideoMode(hwc_context_t *ctx, hwc_rect_t crop,
hwc_rect_t& dst, int dpy) {
ALOGD_IF(HWC_UTILS_DEBUG, "dpy %d Destination position [%d %d %d %d]", dpy,
dst.left, dst.top, dst.right, dst.bottom);
Dim srcCrop(crop.left, crop.top,
crop.right - crop.left,
crop.bottom - crop.top);
int extW = ctx->dpyAttr[dpy].xres;
int extH = ctx->dpyAttr[dpy].yres;
// Set the destination coordinates of external display to full screen,
// when zoom in mode is enabled or video aspect ratio matches with the
// external display aspect ratio
if((srcCrop.w * extH == extW * srcCrop.h) || (isZoomModeEnabled(crop))) {
dst.left = 0;
dst.top = 0;
dst.right = extW;
dst.bottom = extH;
}
ALOGD_IF(HWC_UTILS_DEBUG, "External Resolution [%d %d] dpy %d Modified"
" Destination position [%d %d %d %d] Source crop [%d %d %d %d]",
extW, extH, dpy, dst.left, dst.top, dst.right, dst.bottom,
crop.left, crop.top, crop.right, crop.bottom);
}
void updateExtDisplayCoordinates(hwc_context_t *ctx, hwc_rect_t& crop,
hwc_rect_t& dst, int dpy) {
updateCropAIVVideoMode(ctx, crop, dpy);
updateDestAIVVideoMode(ctx, crop, dst, dpy);
}
int configureNonSplit(hwc_context_t *ctx, hwc_layer_1_t *layer,
const int& dpy, eMdpFlags& mdpFlags, eZorder& z,
const eDest& dest, Rotator **rot) {
private_handle_t *hnd = (private_handle_t *)layer->handle;
if(!hnd) {
if (layer->flags & HWC_COLOR_FILL) {
// Configure Color layer
return configColorLayer(ctx, layer, dpy, mdpFlags, z, dest);
}
ALOGE("%s: layer handle is NULL", __FUNCTION__);
return -1;
}
MetaData_t *metadata = (MetaData_t *)hnd->base_metadata;
hwc_rect_t crop = integerizeSourceCrop(layer->sourceCropf);
hwc_rect_t dst = layer->displayFrame;
int transform = layer->transform;
eTransform orient = static_cast<eTransform>(transform);
int rotFlags = ovutils::ROT_FLAGS_NONE;
uint32_t format = ovutils::getMdpFormat(hnd->format, isTileRendered(hnd));
Whf whf(getWidth(hnd), getHeight(hnd), format, (uint32_t)hnd->size);
// Handle R/B swap
if (layer->flags & HWC_FORMAT_RB_SWAP) {
if (hnd->format == HAL_PIXEL_FORMAT_RGBA_8888)
whf.format = getMdpFormat(HAL_PIXEL_FORMAT_BGRA_8888);
else if (hnd->format == HAL_PIXEL_FORMAT_RGBX_8888)
whf.format = getMdpFormat(HAL_PIXEL_FORMAT_BGRX_8888);
}
// update source crop and destination position of AIV video layer.
if(ctx->mAIVVideoMode[dpy] && isYuvBuffer(hnd)) {
updateExtDisplayCoordinates(ctx, crop, dst, dpy);
}
calcExtDisplayPosition(ctx, hnd, dpy, crop, dst, transform, orient);
int downscale = getRotDownscale(ctx, layer);
setMdpFlags(ctx, layer, mdpFlags, downscale, transform);
//if 90 component or downscale, use rot
if((has90Transform(layer) or downscale) and isRotationDoable(ctx, hnd)) {
*rot = ctx->mRotMgr->getNext();
if(*rot == NULL) return -1;
ctx->mLayerRotMap[dpy]->add(layer, *rot);
// BWC is not tested for other formats So enable it only for YUV format
if(!dpy && isYuvBuffer(hnd))
BwcPM::setBwc(crop, dst, transform, downscale, mdpFlags);
//Configure rotator for pre-rotation
if(configRotator(*rot, whf, crop, mdpFlags, orient, downscale) < 0) {
ALOGE("%s: configRotator failed!", __FUNCTION__);
return -1;
}
updateSource(orient, whf, crop, *rot);
rotFlags |= ROT_PREROTATED;
}
//For the mdp, since either we are pre-rotating or MDP does flips
orient = OVERLAY_TRANSFORM_0;
transform = 0;
PipeArgs parg(mdpFlags, whf, z,
static_cast<eRotFlags>(rotFlags), layer->planeAlpha,
(ovutils::eBlending) getBlending(layer->blending));
if(configMdp(ctx->mOverlay, parg, orient, crop, dst, metadata, dest) < 0) {
ALOGE("%s: commit failed for low res panel", __FUNCTION__);
return -1;
}
return 0;
}
//Helper to 1) Ensure crops dont have gaps 2) Ensure L and W are even
void sanitizeSourceCrop(hwc_rect_t& cropL, hwc_rect_t& cropR,
private_handle_t *hnd) {
if(cropL.right - cropL.left) {
if(isYuvBuffer(hnd)) {
//Always safe to even down left
ovutils::even_floor(cropL.left);
//If right is even, automatically width is even, since left is
//already even
ovutils::even_floor(cropL.right);
}
//Make sure there are no gaps between left and right splits if the layer
//is spread across BOTH halves
if(cropR.right - cropR.left) {
cropR.left = cropL.right;
}
}
if(cropR.right - cropR.left) {
if(isYuvBuffer(hnd)) {
//Always safe to even down left
ovutils::even_floor(cropR.left);
//If right is even, automatically width is even, since left is
//already even
ovutils::even_floor(cropR.right);
}
}
}
int configureSplit(hwc_context_t *ctx, hwc_layer_1_t *layer,
const int& dpy, eMdpFlags& mdpFlagsL, eZorder& z,
const eDest& lDest, const eDest& rDest,
Rotator **rot) {
private_handle_t *hnd = (private_handle_t *)layer->handle;
if(!hnd) {
ALOGE("%s: layer handle is NULL", __FUNCTION__);
return -1;
}
MetaData_t *metadata = (MetaData_t *)hnd->base_metadata;
int hw_w = ctx->dpyAttr[dpy].xres;
int hw_h = ctx->dpyAttr[dpy].yres;
hwc_rect_t crop = integerizeSourceCrop(layer->sourceCropf);
hwc_rect_t dst = layer->displayFrame;
int transform = layer->transform;
eTransform orient = static_cast<eTransform>(transform);
int rotFlags = ROT_FLAGS_NONE;
uint32_t format = ovutils::getMdpFormat(hnd->format, isTileRendered(hnd));
Whf whf(getWidth(hnd), getHeight(hnd), format, (uint32_t)hnd->size);
// Handle R/B swap
if (layer->flags & HWC_FORMAT_RB_SWAP) {
if (hnd->format == HAL_PIXEL_FORMAT_RGBA_8888)
whf.format = getMdpFormat(HAL_PIXEL_FORMAT_BGRA_8888);
else if (hnd->format == HAL_PIXEL_FORMAT_RGBX_8888)
whf.format = getMdpFormat(HAL_PIXEL_FORMAT_BGRX_8888);
}
// update source crop and destination position of AIV video layer.
if(ctx->mAIVVideoMode[dpy] && isYuvBuffer(hnd)) {
updateExtDisplayCoordinates(ctx, crop, dst, dpy);
}
/* Calculate the external display position based on MDP downscale,
ActionSafe, and extorientation features. */
calcExtDisplayPosition(ctx, hnd, dpy, crop, dst, transform, orient);
int downscale = getRotDownscale(ctx, layer);
setMdpFlags(ctx, layer, mdpFlagsL, downscale, transform);
if(lDest != OV_INVALID && rDest != OV_INVALID) {
//Enable overfetch
setMdpFlags(mdpFlagsL, OV_MDSS_MDP_DUAL_PIPE);
}
//Will do something only if feature enabled and conditions suitable
//hollow call otherwise
if(ctx->mAD->prepare(ctx, crop, whf, hnd)) {
overlay::Writeback *wb = overlay::Writeback::getInstance();
whf.format = wb->getOutputFormat();
}
if((has90Transform(layer) or downscale) and isRotationDoable(ctx, hnd)) {
(*rot) = ctx->mRotMgr->getNext();
if((*rot) == NULL) return -1;
ctx->mLayerRotMap[dpy]->add(layer, *rot);
//Configure rotator for pre-rotation
if(configRotator(*rot, whf, crop, mdpFlagsL, orient, downscale) < 0) {
ALOGE("%s: configRotator failed!", __FUNCTION__);
return -1;
}
updateSource(orient, whf, crop, *rot);
rotFlags |= ROT_PREROTATED;
}
eMdpFlags mdpFlagsR = mdpFlagsL;
setMdpFlags(mdpFlagsR, OV_MDSS_MDP_RIGHT_MIXER);
hwc_rect_t tmp_cropL = {0}, tmp_dstL = {0};
hwc_rect_t tmp_cropR = {0}, tmp_dstR = {0};
const int lSplit = getLeftSplit(ctx, dpy);
// Calculate Left rects
if(dst.left < lSplit) {
tmp_cropL = crop;
tmp_dstL = dst;
hwc_rect_t scissor = {0, 0, lSplit, hw_h };
scissor = getIntersection(ctx->mViewFrame[dpy], scissor);
qhwc::calculate_crop_rects(tmp_cropL, tmp_dstL, scissor, 0);
}
// Calculate Right rects
if(dst.right > lSplit) {
tmp_cropR = crop;
tmp_dstR = dst;
hwc_rect_t scissor = {lSplit, 0, hw_w, hw_h };
scissor = getIntersection(ctx->mViewFrame[dpy], scissor);
qhwc::calculate_crop_rects(tmp_cropR, tmp_dstR, scissor, 0);
}
sanitizeSourceCrop(tmp_cropL, tmp_cropR, hnd);
//When buffer is H-flipped, contents of mixer config also needs to swapped
//Not needed if the layer is confined to one half of the screen.
//If rotator has been used then it has also done the flips, so ignore them.
if((orient & OVERLAY_TRANSFORM_FLIP_H) && (dst.left < lSplit) &&
(dst.right > lSplit) && (*rot) == NULL) {
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;
}
//For the mdp, since either we are pre-rotating or MDP does flips
orient = OVERLAY_TRANSFORM_0;
transform = 0;
//configure left mixer
if(lDest != OV_INVALID) {
PipeArgs pargL(mdpFlagsL, whf, z,
static_cast<eRotFlags>(rotFlags), layer->planeAlpha,
(ovutils::eBlending) getBlending(layer->blending));
if(configMdp(ctx->mOverlay, pargL, orient,
tmp_cropL, tmp_dstL, metadata, lDest) < 0) {
ALOGE("%s: commit failed for left mixer config", __FUNCTION__);
return -1;
}
}
//configure right mixer
if(rDest != OV_INVALID) {
PipeArgs pargR(mdpFlagsR, whf, z,
static_cast<eRotFlags>(rotFlags),
layer->planeAlpha,
(ovutils::eBlending) getBlending(layer->blending));
tmp_dstR.right = tmp_dstR.right - lSplit;
tmp_dstR.left = tmp_dstR.left - lSplit;
if(configMdp(ctx->mOverlay, pargR, orient,
tmp_cropR, tmp_dstR, metadata, rDest) < 0) {
ALOGE("%s: commit failed for right mixer config", __FUNCTION__);
return -1;
}
}
return 0;
}
int configureSourceSplit(hwc_context_t *ctx, hwc_layer_1_t *layer,
const int& dpy, eMdpFlags& mdpFlagsL, eZorder& z,
const eDest& lDest, const eDest& rDest,
Rotator **rot) {
private_handle_t *hnd = (private_handle_t *)layer->handle;
if(!hnd) {
ALOGE("%s: layer handle is NULL", __FUNCTION__);
return -1;
}
MetaData_t *metadata = (MetaData_t *)hnd->base_metadata;
hwc_rect_t crop = integerizeSourceCrop(layer->sourceCropf);;
hwc_rect_t dst = layer->displayFrame;
int transform = layer->transform;
eTransform orient = static_cast<eTransform>(transform);
const int downscale = 0;
int rotFlags = ROT_FLAGS_NONE;
//Splitting only YUV layer on primary panel needs different zorders
//for both layers as both the layers are configured to single mixer
eZorder lz = z;
eZorder rz = (eZorder)(z + 1);
Whf whf(getWidth(hnd), getHeight(hnd),
getMdpFormat(hnd->format), (uint32_t)hnd->size);
// update source crop and destination position of AIV video layer.
if(ctx->mAIVVideoMode[dpy] && isYuvBuffer(hnd)) {
updateExtDisplayCoordinates(ctx, crop, dst, dpy);
}
/* Calculate the external display position based on MDP downscale,
ActionSafe, and extorientation features. */
calcExtDisplayPosition(ctx, hnd, dpy, crop, dst, transform, orient);
setMdpFlags(ctx, layer, mdpFlagsL, 0, transform);
trimLayer(ctx, dpy, transform, crop, dst);
if(has90Transform(layer) && isRotationDoable(ctx, hnd)) {
(*rot) = ctx->mRotMgr->getNext();
if((*rot) == NULL) return -1;
ctx->mLayerRotMap[dpy]->add(layer, *rot);
// BWC is not tested for other formats So enable it only for YUV format
if(!dpy && isYuvBuffer(hnd))
BwcPM::setBwc(crop, dst, transform, downscale, mdpFlagsL);
//Configure rotator for pre-rotation
if(configRotator(*rot, whf, crop, mdpFlagsL, orient, downscale) < 0) {
ALOGE("%s: configRotator failed!", __FUNCTION__);
return -1;
}
updateSource(orient, whf, crop, *rot);
rotFlags |= ROT_PREROTATED;
}
eMdpFlags mdpFlagsR = mdpFlagsL;
int lSplit = dst.left + (dst.right - dst.left)/2;
hwc_rect_t tmp_cropL = {0}, tmp_dstL = {0};
hwc_rect_t tmp_cropR = {0}, tmp_dstR = {0};
if(lDest != OV_INVALID) {
tmp_cropL = crop;
tmp_dstL = dst;
hwc_rect_t scissor = {dst.left, dst.top, lSplit, dst.bottom };
qhwc::calculate_crop_rects(tmp_cropL, tmp_dstL, scissor, 0);
}
if(rDest != OV_INVALID) {
tmp_cropR = crop;
tmp_dstR = dst;
hwc_rect_t scissor = {lSplit, dst.top, dst.right, dst.bottom };
qhwc::calculate_crop_rects(tmp_cropR, tmp_dstR, scissor, 0);
}
sanitizeSourceCrop(tmp_cropL, tmp_cropR, hnd);
//When buffer is H-flipped, contents of mixer config also needs to swapped
//Not needed if the layer is confined to one half of the screen.
//If rotator has been used then it has also done the flips, so ignore them.
if((orient & OVERLAY_TRANSFORM_FLIP_H) && lDest != OV_INVALID
&& rDest != OV_INVALID && (*rot) == NULL) {
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;
}
//For the mdp, since either we are pre-rotating or MDP does flips
orient = OVERLAY_TRANSFORM_0;
transform = 0;
//configure left half
if(lDest != OV_INVALID) {
PipeArgs pargL(mdpFlagsL, whf, lz,
static_cast<eRotFlags>(rotFlags), layer->planeAlpha,
(ovutils::eBlending) getBlending(layer->blending));
if(configMdp(ctx->mOverlay, pargL, orient,
tmp_cropL, tmp_dstL, metadata, lDest) < 0) {
ALOGE("%s: commit failed for left half config", __FUNCTION__);
return -1;
}
}
//configure right half
if(rDest != OV_INVALID) {
PipeArgs pargR(mdpFlagsR, whf, rz,
static_cast<eRotFlags>(rotFlags),
layer->planeAlpha,
(ovutils::eBlending) getBlending(layer->blending));
if(configMdp(ctx->mOverlay, pargR, orient,
tmp_cropR, tmp_dstR, metadata, rDest) < 0) {
ALOGE("%s: commit failed for right half config", __FUNCTION__);
return -1;
}
}
return 0;
}
bool canUseRotator(hwc_context_t *ctx, int dpy) {
if(ctx->mOverlay->isDMAMultiplexingSupported() &&
isSecondaryConnected(ctx) &&
!ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].isPause) {
/* mdss driver on certain targets support multiplexing of DMA pipe
* in LINE and BLOCK modes for writeback panels.
*/
if(dpy == HWC_DISPLAY_PRIMARY)
return false;
}
if((ctx->mMDP.version == qdutils::MDP_V3_0_4)
||(ctx->mMDP.version == qdutils::MDP_V3_0_5))
return false;
return true;
}
int getLeftSplit(hwc_context_t *ctx, const int& dpy) {
//Default even split for all displays with high res
int lSplit = ctx->dpyAttr[dpy].xres / 2;
if(dpy == HWC_DISPLAY_PRIMARY &&
qdutils::MDPVersion::getInstance().getLeftSplit()) {
//Override if split published by driver for primary
lSplit = qdutils::MDPVersion::getInstance().getLeftSplit();
}
return lSplit;
}
bool isDisplaySplit(hwc_context_t* ctx, int dpy) {
qdutils::MDPVersion& mdpHw = qdutils::MDPVersion::getInstance();
if(ctx->dpyAttr[dpy].xres > mdpHw.getMaxMixerWidth()) {
return true;
}
//For testing we could split primary via device tree values
if(dpy == HWC_DISPLAY_PRIMARY && mdpHw.getRightSplit()) {
return true;
}
return false;
}
//clear prev layer prop flags and realloc for current frame
void reset_layer_prop(hwc_context_t* ctx, int dpy, int numAppLayers) {
if(ctx->layerProp[dpy]) {
delete[] ctx->layerProp[dpy];
ctx->layerProp[dpy] = NULL;
}
ctx->layerProp[dpy] = new LayerProp[numAppLayers];
}
bool isAbcInUse(hwc_context_t *ctx){
return (ctx->enableABC && ctx->listStats[0].renderBufIndexforABC == 0);
}
void dumpBuffer(private_handle_t *ohnd, char *bufferName) {
if (ohnd != NULL && ohnd->base) {
char dumpFilename[PATH_MAX];
bool bResult = false;
int width = getWidth(ohnd);
int height = getHeight(ohnd);
int format = ohnd->format;
//dummy aligned w & h.
int alW = 0, alH = 0;
int size = getBufferSizeAndDimensions(width, height, format, alW, alH);
snprintf(dumpFilename, sizeof(dumpFilename), "/data/%s.%s.%dx%d.raw",
bufferName,
overlay::utils::getFormatString(utils::getMdpFormat(format)),
width, height);
FILE* fp = fopen(dumpFilename, "w+");
if (NULL != fp) {
bResult = (bool) fwrite((void*)ohnd->base, size, 1, fp);
fclose(fp);
}
ALOGD("Buffer[%s] Dump to %s: %s",
bufferName, dumpFilename, bResult ? "Success" : "Fail");
}
}
bool isGLESComp(hwc_context_t *ctx,
hwc_display_contents_1_t* list) {
int numAppLayers = ctx->listStats[HWC_DISPLAY_PRIMARY].numAppLayers;
for(int index = 0; index < numAppLayers; index++) {
hwc_layer_1_t* layer = &(list->hwLayers[index]);
if(layer->compositionType == HWC_FRAMEBUFFER)
return true;
}
return false;
}
void setGPUHint(hwc_context_t* ctx, hwc_display_contents_1_t* list) {
struct gpu_hint_info *gpuHint = &ctx->mGPUHintInfo;
if(!gpuHint->mGpuPerfModeEnable || !ctx || !list)
return;
#ifdef QCOM_BSP
/* Set the GPU hint flag to high for MIXED/GPU composition only for
first frame after MDP -> GPU/MIXED mode transition. Set the GPU
hint to default if the previous composition is GPU or current GPU
composition is due to idle fallback */
if(!gpuHint->mEGLDisplay || !gpuHint->mEGLContext) {
gpuHint->mEGLDisplay = eglGetCurrentDisplay();
if(!gpuHint->mEGLDisplay) {
ALOGW("%s Warning: EGL current display is NULL", __FUNCTION__);
return;
}
gpuHint->mEGLContext = eglGetCurrentContext();
if(!gpuHint->mEGLContext) {
ALOGW("%s Warning: EGL current context is NULL", __FUNCTION__);
return;
}
}
if(isGLESComp(ctx, list)) {
if(gpuHint->mCompositionState != COMPOSITION_STATE_GPU
&& !MDPComp::isIdleFallback()) {
EGLint attr_list[] = {EGL_GPU_HINT_1,
EGL_GPU_LEVEL_3,
EGL_NONE };
if((gpuHint->mCurrGPUPerfMode != EGL_GPU_LEVEL_3) &&
!eglGpuPerfHintQCOM(gpuHint->mEGLDisplay,
gpuHint->mEGLContext, attr_list)) {
ALOGW("eglGpuPerfHintQCOM failed for Built in display");
} else {
gpuHint->mCurrGPUPerfMode = EGL_GPU_LEVEL_3;
gpuHint->mCompositionState = COMPOSITION_STATE_GPU;
}
} else {
EGLint attr_list[] = {EGL_GPU_HINT_1,
EGL_GPU_LEVEL_0,
EGL_NONE };
if((gpuHint->mCurrGPUPerfMode != EGL_GPU_LEVEL_0) &&
!eglGpuPerfHintQCOM(gpuHint->mEGLDisplay,
gpuHint->mEGLContext, attr_list)) {
ALOGW("eglGpuPerfHintQCOM failed for Built in display");
} else {
gpuHint->mCurrGPUPerfMode = EGL_GPU_LEVEL_0;
}
if(MDPComp::isIdleFallback()) {
gpuHint->mCompositionState = COMPOSITION_STATE_IDLE_FALLBACK;
}
}
} else {
/* set the GPU hint flag to default for MDP composition */
EGLint attr_list[] = {EGL_GPU_HINT_1,
EGL_GPU_LEVEL_0,
EGL_NONE };
if((gpuHint->mCurrGPUPerfMode != EGL_GPU_LEVEL_0) &&
!eglGpuPerfHintQCOM(gpuHint->mEGLDisplay,
gpuHint->mEGLContext, attr_list)) {
ALOGW("eglGpuPerfHintQCOM failed for Built in display");
} else {
gpuHint->mCurrGPUPerfMode = EGL_GPU_LEVEL_0;
}
gpuHint->mCompositionState = COMPOSITION_STATE_MDP;
}
#endif
}
bool isPeripheral(const hwc_rect_t& rect1, const hwc_rect_t& rect2) {
// To be peripheral, 3 boundaries should match.
uint8_t eqBounds = 0;
if (rect1.left == rect2.left)
eqBounds++;
if (rect1.top == rect2.top)
eqBounds++;
if (rect1.right == rect2.right)
eqBounds++;
if (rect1.bottom == rect2.bottom)
eqBounds++;
return (eqBounds == 3);
}
void BwcPM::setBwc(const hwc_rect_t& crop, const hwc_rect_t& dst,
const int& transform,const int& downscale,
ovutils::eMdpFlags& mdpFlags) {
//BWC not supported with rot-downscale
if(downscale) return;
//Target doesnt support Bwc
qdutils::MDPVersion& mdpHw = qdutils::MDPVersion::getInstance();
if(!mdpHw.supportsBWC()) {
return;
}
int src_w = crop.right - crop.left;
int src_h = crop.bottom - crop.top;
int dst_w = dst.right - dst.left;
int dst_h = dst.bottom - dst.top;
if(transform & HAL_TRANSFORM_ROT_90) {
swap(src_w, src_h);
}
//src width > MAX mixer supported dim
if(src_w > (int) qdutils::MDPVersion::getInstance().getMaxMixerWidth()) {
return;
}
//Decimation necessary, cannot use BWC. H/W requirement.
if(qdutils::MDPVersion::getInstance().supportsDecimation()) {
uint8_t horzDeci = 0;
uint8_t vertDeci = 0;
ovutils::getDecimationFactor(src_w, src_h, dst_w, dst_h, horzDeci,
vertDeci);
if(horzDeci || vertDeci) return;
}
//Property
char value[PROPERTY_VALUE_MAX];
property_get("debug.disable.bwc", value, "0");
if(atoi(value)) return;
ovutils::setMdpFlags(mdpFlags, ovutils::OV_MDSS_MDP_BWC_EN);
}
void LayerRotMap::add(hwc_layer_1_t* layer, Rotator *rot) {
if(mCount >= RotMgr::MAX_ROT_SESS) return;
mLayer[mCount] = layer;
mRot[mCount] = rot;
mCount++;
}
void LayerRotMap::reset() {
for (int i = 0; i < RotMgr::MAX_ROT_SESS; i++) {
mLayer[i] = 0;
mRot[i] = 0;
}
mCount = 0;
}
void LayerRotMap::clear() {
RotMgr::getInstance()->markUnusedTop(mCount);
reset();
}
bool LayerRotMap::isRotCached(uint32_t index) const {
overlay::Rotator* rot = getRot(index);
hwc_layer_1_t* layer = getLayer(index);
if(rot and layer and layer->handle) {
private_handle_t *hnd = (private_handle_t *)(layer->handle);
return (rot->isRotCached(hnd->fd,(uint32_t)(hnd->offset)));
}
return false;
}
void LayerRotMap::setReleaseFd(const int& fence) {
for(uint32_t i = 0; i < mCount; i++) {
if(mRot[i] and mLayer[i] and mLayer[i]->handle) {
/* Ensure that none of the above (Rotator-instance,
* layer and layer-handle) are NULL*/
if(isRotCached(i))
mRot[i]->setPrevBufReleaseFd(dup(fence));
else
mRot[i]->setCurrBufReleaseFd(dup(fence));
}
}
}
void resetROI(hwc_context_t *ctx, const int dpy) {
const int fbXRes = (int)ctx->dpyAttr[dpy].xres;
const int fbYRes = (int)ctx->dpyAttr[dpy].yres;
if(isDisplaySplit(ctx, dpy)) {
const int lSplit = getLeftSplit(ctx, dpy);
ctx->listStats[dpy].lRoi = (struct hwc_rect){0, 0, lSplit, fbYRes};
ctx->listStats[dpy].rRoi = (struct hwc_rect){lSplit, 0, fbXRes, fbYRes};
} else {
ctx->listStats[dpy].lRoi = (struct hwc_rect){0, 0,fbXRes, fbYRes};
ctx->listStats[dpy].rRoi = (struct hwc_rect){0, 0, 0, 0};
}
}
hwc_rect_t getSanitizeROI(struct hwc_rect roi, hwc_rect boundary)
{
if(!isValidRect(roi))
return roi;
struct hwc_rect t_roi = roi;
const int LEFT_ALIGN = qdutils::MDPVersion::getInstance().getLeftAlign();
const int WIDTH_ALIGN = qdutils::MDPVersion::getInstance().getWidthAlign();
const int TOP_ALIGN = qdutils::MDPVersion::getInstance().getTopAlign();
const int HEIGHT_ALIGN = qdutils::MDPVersion::getInstance().getHeightAlign();
const int MIN_WIDTH = qdutils::MDPVersion::getInstance().getMinROIWidth();
const int MIN_HEIGHT = qdutils::MDPVersion::getInstance().getMinROIHeight();
/* Align to minimum width recommended by the panel */
if((t_roi.right - t_roi.left) < MIN_WIDTH) {
if((t_roi.left + MIN_WIDTH) > boundary.right)
t_roi.left = t_roi.right - MIN_WIDTH;
else
t_roi.right = t_roi.left + MIN_WIDTH;
}
/* Align to minimum height recommended by the panel */
if((t_roi.bottom - t_roi.top) < MIN_HEIGHT) {
if((t_roi.top + MIN_HEIGHT) > boundary.bottom)
t_roi.top = t_roi.bottom - MIN_HEIGHT;
else
t_roi.bottom = t_roi.top + MIN_HEIGHT;
}
/* Align left and width to meet panel restrictions */
if(LEFT_ALIGN)
t_roi.left = t_roi.left - (t_roi.left % LEFT_ALIGN);
if(WIDTH_ALIGN) {
int width = t_roi.right - t_roi.left;
width = WIDTH_ALIGN * ((width + (WIDTH_ALIGN - 1)) / WIDTH_ALIGN);
t_roi.right = t_roi.left + width;
if(t_roi.right > boundary.right) {
t_roi.right = boundary.right;
t_roi.left = t_roi.right - width;
if(LEFT_ALIGN)
t_roi.left = t_roi.left - (t_roi.left % LEFT_ALIGN);
}
}
/* Align top and height to meet panel restrictions */
if(TOP_ALIGN)
t_roi.top = t_roi.top - (t_roi.top % TOP_ALIGN);
if(HEIGHT_ALIGN) {
int height = t_roi.bottom - t_roi.top;
height = HEIGHT_ALIGN * ((height + (HEIGHT_ALIGN - 1)) / HEIGHT_ALIGN);
t_roi.bottom = t_roi.top + height;
if(t_roi.bottom > boundary.bottom) {
t_roi.bottom = boundary.bottom;
t_roi.top = t_roi.bottom - height;
if(TOP_ALIGN)
t_roi.top = t_roi.top - (t_roi.top % TOP_ALIGN);
}
}
return t_roi;
}
void handle_pause(hwc_context_t* ctx, int dpy) {
if(ctx->mHWCVirtual) {
ctx->mHWCVirtual->pause(ctx, dpy);
}
return;
}
void handle_resume(hwc_context_t* ctx, int dpy) {
if(ctx->mHWCVirtual) {
ctx->mHWCVirtual->resume(ctx, dpy);
}
return;
}
};//namespace qhwc