eee6309c4f
- During video playback, when the external display is connected, start the overlay channel on the right framebuffer. - set the correct position for the new external display channel - fix minor bug in the function getAspectRatioPosition for external display CRs-fixed: 336674 (cherry picked from commit 6afc95ee1197718332461c8f0ddf9cf5f94cc7aa) Change-Id: Ifa30dfea349c2a0f133790ab2dc91527d53152f8
2289 lines
72 KiB
C++
Executable File
2289 lines
72 KiB
C++
Executable File
/*
|
|
* Copyright (C) 2008 The Android Open Source Project
|
|
* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "overlayLib.h"
|
|
#include "gralloc_priv.h"
|
|
|
|
#define INTERLACE_MASK 0x80
|
|
#define DEBUG_OVERLAY true
|
|
/* Helper functions */
|
|
static inline size_t ALIGN(size_t x, size_t align) {
|
|
return (x + align-1) & ~(align-1);
|
|
}
|
|
|
|
using namespace overlay;
|
|
using android::sp;
|
|
using gralloc::IMemAlloc;
|
|
using gralloc::IonController;
|
|
using gralloc::alloc_data;
|
|
|
|
#ifdef HDMI_AS_PRIMARY
|
|
bool Overlay::sHDMIAsPrimary = true;
|
|
#else
|
|
bool Overlay::sHDMIAsPrimary = false;
|
|
#endif
|
|
|
|
template <class Type>
|
|
void swapWidthHeight(Type& width, Type& height) {
|
|
Type tmp = width;
|
|
width = height;
|
|
height = tmp;
|
|
}
|
|
|
|
int overlay::get_mdp_format(int format) {
|
|
switch (format) {
|
|
case HAL_PIXEL_FORMAT_RGBA_8888 :
|
|
return MDP_RGBA_8888;
|
|
case HAL_PIXEL_FORMAT_BGRA_8888:
|
|
return MDP_BGRA_8888;
|
|
case HAL_PIXEL_FORMAT_RGB_565:
|
|
return MDP_RGB_565;
|
|
case HAL_PIXEL_FORMAT_RGBX_8888:
|
|
return MDP_RGBX_8888;
|
|
case HAL_PIXEL_FORMAT_YCbCr_422_SP:
|
|
return MDP_Y_CBCR_H2V1;
|
|
case HAL_PIXEL_FORMAT_YCbCr_420_SP:
|
|
return MDP_Y_CRCB_H2V2;
|
|
case HAL_PIXEL_FORMAT_YCrCb_420_SP:
|
|
return MDP_Y_CBCR_H2V2;
|
|
case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED:
|
|
return MDP_Y_CRCB_H2V2_TILE;
|
|
case HAL_PIXEL_FORMAT_YV12:
|
|
return MDP_Y_CR_CB_GH2V2;
|
|
default:
|
|
LOGE("%s: unknown color format [0x%x]", __FUNCTION__, format);
|
|
return -1;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int overlay::get_mdp_orientation(int value) {
|
|
switch(value) {
|
|
case 0: return 0;
|
|
case HAL_TRANSFORM_FLIP_V: return MDP_FLIP_UD;
|
|
case HAL_TRANSFORM_FLIP_H: return MDP_FLIP_LR;
|
|
case HAL_TRANSFORM_ROT_90: return MDP_ROT_90;
|
|
case HAL_TRANSFORM_ROT_90|HAL_TRANSFORM_FLIP_V:
|
|
return MDP_ROT_90|MDP_FLIP_LR;
|
|
case HAL_TRANSFORM_ROT_90|HAL_TRANSFORM_FLIP_H:
|
|
return MDP_ROT_90|MDP_FLIP_UD;
|
|
case HAL_TRANSFORM_ROT_180: return MDP_ROT_180;
|
|
case HAL_TRANSFORM_ROT_270: return MDP_ROT_270;
|
|
default:
|
|
LOGE("%s: invalid rotation value (value = 0x%x",
|
|
__FUNCTION__, value);
|
|
return -1;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
// Rotator - input to output mapping
|
|
int overlay::get_rot_output_format(int format) {
|
|
switch (format) {
|
|
case MDP_Y_CRCB_H2V2_TILE:
|
|
return MDP_Y_CRCB_H2V2;
|
|
case MDP_Y_CB_CR_H2V2:
|
|
return MDP_Y_CBCR_H2V2;
|
|
case MDP_Y_CR_CB_GH2V2:
|
|
return MDP_Y_CRCB_H2V2;
|
|
default:
|
|
return format;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
// This function normalizes the crop values to be all even
|
|
void overlay::normalize_crop(uint32_t& xy, uint32_t& wh) {
|
|
|
|
if (xy & 0x0001) {
|
|
// x or y is odd, increment it's value
|
|
xy += 1;
|
|
// Since we've incremented x(y), we need to decrement
|
|
// w(h) accordingly
|
|
if (wh & 0x0001) {
|
|
// w or h is odd, decrement it by 1, to make it even
|
|
EVEN_OUT(wh);
|
|
} else {
|
|
// w(h) is already even, hence we decrement by 2
|
|
wh -=2;
|
|
}
|
|
} else {
|
|
EVEN_OUT(wh);
|
|
}
|
|
}
|
|
|
|
#define LOG_TAG "OverlayLIB"
|
|
static void reportError(const char* message) {
|
|
LOGE( "%s", message);
|
|
}
|
|
|
|
void overlay::dump(mdp_overlay& mOVInfo) {
|
|
if (!DEBUG_OVERLAY)
|
|
return;
|
|
LOGE("mOVInfo:");
|
|
LOGE("src: width %d height %d format %s user_data[0] %d", mOVInfo.src.width,
|
|
mOVInfo.src.height, getFormatString(mOVInfo.src.format),
|
|
mOVInfo.user_data[0]);
|
|
LOGE("src_rect: x %d y %d w %d h %d", mOVInfo.src_rect.x,
|
|
mOVInfo.src_rect.y, mOVInfo.src_rect.w, mOVInfo.src_rect.h);
|
|
LOGE("dst_rect: x %d y %d w %d h %d", mOVInfo.dst_rect.x,
|
|
mOVInfo.dst_rect.y, mOVInfo.dst_rect.w, mOVInfo.dst_rect.h);
|
|
LOGE("z_order %d is_fg %d alpha %d transp_mask %d flags %x id %d",
|
|
mOVInfo.z_order, mOVInfo.is_fg, mOVInfo.alpha, mOVInfo.transp_mask,
|
|
mOVInfo.flags, mOVInfo.id);
|
|
}
|
|
|
|
void overlay::dump(msm_rotator_img_info& mRotInfo) {
|
|
if (!DEBUG_OVERLAY)
|
|
return;
|
|
LOGE("mRotInfo:");
|
|
LOGE("session_id %d dst_x %d dst_y %d rotations %d enable %d",
|
|
mRotInfo.session_id, mRotInfo.dst_x, mRotInfo.dst_y,
|
|
mRotInfo.rotations, mRotInfo.enable);
|
|
LOGE("src: width %d height %d format %s", mRotInfo.src.width,
|
|
mRotInfo.src.height, getFormatString(mRotInfo.src.format));
|
|
LOGE("dst: width %d height %d format %s", mRotInfo.dst.width,
|
|
mRotInfo.dst.height, getFormatString(mRotInfo.src.format));
|
|
LOGE("src_rect: x %d y %d w %d h %d", mRotInfo.src_rect.x,
|
|
mRotInfo.src_rect.y, mRotInfo.src_rect.w, mRotInfo.src_rect.h);
|
|
}
|
|
|
|
const char* overlay::getFormatString(int format){
|
|
static const char* formats[] = {
|
|
"MDP_RGB_565",
|
|
"MDP_XRGB_8888",
|
|
"MDP_Y_CBCR_H2V2",
|
|
"MDP_ARGB_8888",
|
|
"MDP_RGB_888",
|
|
"MDP_Y_CRCB_H2V2",
|
|
"MDP_YCRYCB_H2V1",
|
|
"MDP_Y_CRCB_H2V1",
|
|
"MDP_Y_CBCR_H2V1",
|
|
"MDP_RGBA_8888",
|
|
"MDP_BGRA_8888",
|
|
"MDP_RGBX_8888",
|
|
"MDP_Y_CRCB_H2V2_TILE",
|
|
"MDP_Y_CBCR_H2V2_TILE",
|
|
"MDP_Y_CR_CB_H2V2",
|
|
"MDP_Y_CR_CB_GH2V2",
|
|
"MDP_Y_CB_CR_H2V2",
|
|
"MDP_Y_CRCB_H1V1",
|
|
"MDP_Y_CBCR_H1V1",
|
|
"MDP_IMGTYPE_LIMIT",
|
|
"MDP_BGR_565",
|
|
"MDP_FB_FORMAT",
|
|
"MDP_IMGTYPE_LIMIT2"
|
|
};
|
|
return formats[format];
|
|
}
|
|
ZOrderManager* ZOrderManager::sInstance = 0;
|
|
FrameBufferInfo* FrameBufferInfo::sFBInfoInstance = 0;
|
|
|
|
int ZOrderManager::getZ(int fbnum){
|
|
int zorder = NO_PIPE;;
|
|
Mutex::Autolock objLock(mObjMutex);
|
|
if(mPipesInuse == mMaxPipes) {
|
|
LOGE("No free pipes available.. inUse = %d ", mPipesInuse);
|
|
return NO_PIPE;
|
|
}
|
|
switch(fbnum) {
|
|
case FRAMEBUFFER_0:
|
|
for (int i = 0;i < NUM_CHANNELS; i++) {
|
|
if(mFB0Pipes[i] == false) {
|
|
mFB0Pipes[i]= true;
|
|
zorder = i;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case FRAMEBUFFER_1:
|
|
case FRAMEBUFFER_2:
|
|
for (int i = 0;i < mMaxPipes; i++) {
|
|
if(mFB1Pipes[i] == false) {
|
|
mFB1Pipes[i]= true;
|
|
zorder = i;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
LOGE("getZ: Invalid framebuffer..");
|
|
break;
|
|
}
|
|
mPipesInuse++;
|
|
LOGE("getZ: return zorder = %d for fbdev = %d, pipesinUse = %d",
|
|
zorder, fbnum, mPipesInuse);
|
|
return zorder;
|
|
}
|
|
|
|
void ZOrderManager::decZ(int fbnum, int zorder){
|
|
Mutex::Autolock objLock(mObjMutex);
|
|
switch(fbnum) {
|
|
case FRAMEBUFFER_0:
|
|
LOG_ASSERT(!mFB0Pipes[zorder],"channel with ZOrder does not exist");
|
|
LOGE("decZ: freeing the pipe with zorder = %d for fbdev = %d", zorder, fbnum);
|
|
mFB0Pipes[zorder] = false;
|
|
break;
|
|
case FRAMEBUFFER_1:
|
|
case FRAMEBUFFER_2:
|
|
LOG_ASSERT(!mFB1Pipes[zorder],"channel with ZOrder does not exist");
|
|
LOGE("decZ: freeing the pipe with zorder = %d for fbdev = %d", zorder, fbnum);
|
|
mFB1Pipes[zorder] = false;
|
|
break;
|
|
default:
|
|
LOGE("decZ: Invalid framebuffer ");
|
|
break;
|
|
}
|
|
if(mPipesInuse > 0)
|
|
mPipesInuse--;
|
|
LOGE("decZ: Pipes in use = %d", mPipesInuse);
|
|
}
|
|
|
|
bool overlay::isHDMIConnected () {
|
|
char value[PROPERTY_VALUE_MAX];
|
|
property_get("hw.hdmiON", value, "0");
|
|
int isHDMI = atoi(value);
|
|
return isHDMI ? true : false;
|
|
}
|
|
|
|
bool overlay::is3DTV() {
|
|
char is3DTV = '0';
|
|
FILE *fp = fopen(EDID_3D_INFO_FILE, "r");
|
|
if (fp) {
|
|
fread(&is3DTV, 1, 1, fp);
|
|
fclose(fp);
|
|
}
|
|
LOGI("3DTV EDID flag: %c", is3DTV);
|
|
return (is3DTV == '0') ? false : true;
|
|
}
|
|
|
|
bool overlay::isPanel3D() {
|
|
int fd = open("/dev/graphics/fb0", O_RDWR, 0);
|
|
if (fd < 0) {
|
|
reportError("Can't open framebuffer 0");
|
|
return false;
|
|
}
|
|
fb_fix_screeninfo finfo;
|
|
if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1) {
|
|
reportError("FBIOGET_FSCREENINFO on fb0 failed");
|
|
close(fd);
|
|
fd = -1;
|
|
return false;
|
|
}
|
|
close(fd);
|
|
return (FB_TYPE_3D_PANEL == finfo.type) ? true : false;
|
|
}
|
|
|
|
bool overlay::usePanel3D() {
|
|
if (Overlay::sHDMIAsPrimary)
|
|
return is3DTV();
|
|
|
|
if(!isPanel3D())
|
|
return false;
|
|
char value[PROPERTY_VALUE_MAX];
|
|
property_get("persist.user.panel3D", value, "0");
|
|
int usePanel3D = atoi(value);
|
|
return usePanel3D ? true : false;
|
|
}
|
|
|
|
bool overlay::send3DInfoPacket (unsigned int format3D) {
|
|
FILE *fp = fopen(FORMAT_3D_FILE, "wb");
|
|
if (fp) {
|
|
fprintf(fp, "%d", format3D);
|
|
fclose(fp);
|
|
fp = NULL;
|
|
return true;
|
|
}
|
|
LOGE("%s:no sysfs entry for setting 3d mode!", __FUNCTION__);
|
|
return false;
|
|
}
|
|
|
|
bool overlay::enableBarrier (unsigned int orientation) {
|
|
FILE *fp = fopen(BARRIER_FILE, "wb");
|
|
if (fp) {
|
|
fprintf(fp, "%d", orientation);
|
|
fclose(fp);
|
|
fp = NULL;
|
|
return true;
|
|
}
|
|
LOGE("%s:no sysfs entry for enabling barriers on 3D panel!", __FUNCTION__);
|
|
return false;
|
|
}
|
|
|
|
int overlay::getColorFormat(int format)
|
|
{
|
|
if (format == HAL_PIXEL_FORMAT_YV12)
|
|
return format;
|
|
else if (format & INTERLACE_MASK)
|
|
return format ^ HAL_PIXEL_FORMAT_INTERLACE;
|
|
else
|
|
return COLOR_FORMAT(format);
|
|
}
|
|
|
|
bool overlay::isInterlacedContent(int format)
|
|
{
|
|
if ((format != HAL_PIXEL_FORMAT_YV12) &&
|
|
(format & INTERLACE_MASK))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
unsigned int overlay::getOverlayConfig (unsigned int format3D, bool poll,
|
|
bool isHDMI) {
|
|
bool isTV3D = false;
|
|
unsigned int curState = 0;
|
|
if (poll)
|
|
isHDMI = isHDMIConnected();
|
|
if (isHDMI) {
|
|
LOGD("%s: HDMI connected... checking the TV type", __FUNCTION__);
|
|
if (format3D) {
|
|
if (is3DTV())
|
|
curState = OV_3D_VIDEO_3D_TV;
|
|
else
|
|
curState = OV_3D_VIDEO_2D_TV;
|
|
} else
|
|
curState = OV_2D_VIDEO_ON_TV;
|
|
} else {
|
|
LOGD("%s: HDMI not connected...", __FUNCTION__);
|
|
if(format3D) {
|
|
if (usePanel3D())
|
|
curState = OV_3D_VIDEO_3D_PANEL;
|
|
else
|
|
curState = OV_3D_VIDEO_2D_PANEL;
|
|
}
|
|
else
|
|
curState = OV_2D_VIDEO_ON_PANEL;
|
|
}
|
|
return curState;
|
|
}
|
|
|
|
/* clears any VG pipes allocated to the fb devices */
|
|
int overlay::initOverlay() {
|
|
msmfb_mixer_info_req req;
|
|
mdp_mixer_info *minfo = NULL;
|
|
char name[64];
|
|
int fd = -1;
|
|
for(int i = 0; i < NUM_FB_DEVICES; i++) {
|
|
snprintf(name, 64, FB_DEVICE_TEMPLATE, i);
|
|
LOGD("initoverlay:: opening the device:: %s", name);
|
|
fd = open(name, O_RDWR, 0);
|
|
if(fd < 0) {
|
|
LOGE("cannot open framebuffer(%d)", i);
|
|
return -1;
|
|
}
|
|
//Get the mixer configuration */
|
|
req.mixer_num = i;
|
|
if (ioctl(fd, MSMFB_MIXER_INFO, &req) == -1) {
|
|
LOGE("ERROR: MSMFB_MIXER_INFO ioctl failed");
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
minfo = req.info;
|
|
for (int j = 0; j < req.cnt; j++) {
|
|
LOGD("ndx=%d num=%d z_order=%d", minfo->pndx, minfo->pnum,
|
|
minfo->z_order);
|
|
// except the RGB base layer with z_order of -1, clear any
|
|
// other pipes connected to mixer.
|
|
if((minfo->z_order) != -1) {
|
|
int index = minfo->pndx;
|
|
LOGD("Unset overlay with index: %d at mixer %d", index, i);
|
|
if(ioctl(fd, MSMFB_OVERLAY_UNSET, &index) == -1) {
|
|
LOGE("ERROR: MSMFB_OVERLAY_UNSET failed");
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
}
|
|
minfo++;
|
|
}
|
|
close(fd);
|
|
fd = -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
Overlay::Overlay() : mChannelUP(false), mExternalDisplay(false),
|
|
mS3DFormat(0), mCroppedSrcWidth(0),
|
|
mCroppedSrcHeight(0), mState(-1) {
|
|
mOVBufferInfo.width = mOVBufferInfo.height = 0;
|
|
mOVBufferInfo.format = mOVBufferInfo.size = 0;
|
|
mOVBufferInfo.secure = false;
|
|
}
|
|
|
|
Overlay::~Overlay() {
|
|
closeChannel();
|
|
}
|
|
|
|
int Overlay::getFBWidth(int channel) const {
|
|
return objOvCtrlChannel[channel].getFBWidth();
|
|
}
|
|
|
|
int Overlay::getFBHeight(int channel) const {
|
|
return objOvCtrlChannel[channel].getFBHeight();
|
|
}
|
|
|
|
bool Overlay::startChannel(const overlay_buffer_info& info, int fbnum,
|
|
bool norot, bool uichannel,
|
|
unsigned int format3D, int channel,
|
|
int flags, int num_buffers) {
|
|
int zorder = 0;
|
|
int format = getColorFormat(info.format);
|
|
mCroppedSrcWidth = info.width;
|
|
mCroppedSrcHeight = info.height;
|
|
if (format3D)
|
|
zorder = channel;
|
|
if (mState == -1)
|
|
mState = OV_UI_MIRROR_TV;
|
|
|
|
mChannelUP = objOvCtrlChannel[channel].startControlChannel(info.width,
|
|
info.height, format, fbnum,
|
|
norot, uichannel,
|
|
format3D, zorder, flags);
|
|
if (!mChannelUP) {
|
|
LOGE("startChannel for fb%d failed", fbnum);
|
|
return mChannelUP;
|
|
}
|
|
objOvCtrlChannel[channel].setSize(info.size);
|
|
return objOvDataChannel[channel].startDataChannel(objOvCtrlChannel[channel], fbnum,
|
|
norot, info.secure, uichannel, num_buffers);
|
|
}
|
|
|
|
bool Overlay::closeChannel() {
|
|
|
|
if (!mChannelUP)
|
|
return true;
|
|
|
|
if(mS3DFormat) {
|
|
if (mExternalDisplay)
|
|
overlay::send3DInfoPacket(0);
|
|
else if (mState == OV_3D_VIDEO_3D_PANEL) {
|
|
if (sHDMIAsPrimary)
|
|
overlay::send3DInfoPacket(0);
|
|
else
|
|
enableBarrier(0);
|
|
}
|
|
}
|
|
for (int i = 0; i < NUM_CHANNELS; i++) {
|
|
objOvCtrlChannel[i].closeControlChannel();
|
|
objOvDataChannel[i].closeDataChannel();
|
|
}
|
|
mChannelUP = false;
|
|
mS3DFormat = 0;
|
|
mOVBufferInfo.width = 0;
|
|
mOVBufferInfo.height = 0;
|
|
mOVBufferInfo.format = 0;
|
|
mOVBufferInfo.size = 0;
|
|
mOVBufferInfo.secure = false;
|
|
mState = -1;
|
|
return true;
|
|
}
|
|
|
|
void Overlay::closeExternalChannel() {
|
|
if (objOvCtrlChannel[VG1_PIPE].isChannelUP()) {
|
|
objOvCtrlChannel[VG1_PIPE].closeControlChannel();
|
|
objOvDataChannel[VG1_PIPE].closeDataChannel();
|
|
}
|
|
}
|
|
|
|
bool Overlay::getPosition(int& x, int& y, uint32_t& w, uint32_t& h, int channel) {
|
|
return objOvCtrlChannel[channel].getPosition(x, y, w, h);
|
|
}
|
|
|
|
bool Overlay::getOrientation(int& orientation, int channel) const {
|
|
return objOvCtrlChannel[channel].getOrientation(orientation);
|
|
}
|
|
|
|
bool Overlay::setDeviceOrientation(int orientation) {
|
|
// Use this to calculate the position on HDMI
|
|
mDevOrientation = orientation;
|
|
return true;
|
|
}
|
|
|
|
bool Overlay::setPosition(int x, int y, uint32_t w, uint32_t h) {
|
|
bool ret = false;
|
|
overlay_rect secDest;
|
|
overlay_rect priDest;
|
|
int currX, currY;
|
|
uint32_t currW, currH;
|
|
// Set even destination co-ordinates
|
|
EVEN_OUT(x); EVEN_OUT(y);
|
|
EVEN_OUT(w); EVEN_OUT(h);
|
|
objOvCtrlChannel[VG0_PIPE].getPosition(currX, currY, currW, currH);
|
|
priDest.x = x, priDest.y = y;
|
|
priDest.w = w, priDest.h = h;
|
|
if(x != currX || y != currY || w != currW || h != currH) {
|
|
switch (mState) {
|
|
case OV_UI_MIRROR_TV:
|
|
case OV_2D_VIDEO_ON_PANEL:
|
|
case OV_3D_VIDEO_2D_PANEL:
|
|
return setChannelPosition(x, y, w, h, VG0_PIPE);
|
|
break;
|
|
case OV_2D_VIDEO_ON_TV:
|
|
if (FrameBufferInfo::getInstance()->canSupportTrueMirroring()) {
|
|
objOvCtrlChannel[VG1_PIPE].getAspectRatioPosition(
|
|
mCroppedSrcWidth, mCroppedSrcHeight, mDevOrientation,
|
|
&priDest, &secDest);
|
|
} else {
|
|
objOvCtrlChannel[VG1_PIPE].getAspectRatioPosition(
|
|
mCroppedSrcWidth, mCroppedSrcHeight, &secDest);
|
|
}
|
|
setChannelPosition(secDest.x, secDest.y, secDest.w, secDest.h,
|
|
VG1_PIPE);
|
|
return setChannelPosition(x, y, w, h, VG0_PIPE);
|
|
break;
|
|
case OV_3D_VIDEO_3D_PANEL:
|
|
for (int i = 0; i < NUM_CHANNELS; i++) {
|
|
if (sHDMIAsPrimary)
|
|
objOvCtrlChannel[i].getPositionS3D(i, mS3DFormat, &secDest);
|
|
else {
|
|
if (!objOvCtrlChannel[i].useVirtualFB()) {
|
|
LOGE("%s: failed virtual fb for channel %d", __FUNCTION__, i);
|
|
return false;
|
|
}
|
|
objOvCtrlChannel[i].getPositionS3D(i, 0x1, &secDest);
|
|
}
|
|
if(!setChannelPosition(secDest.x, secDest.y, secDest.w,
|
|
secDest.h, i)) {
|
|
LOGE("%s: failed for channel %d", __FUNCTION__, i);
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
case OV_3D_VIDEO_2D_TV:
|
|
case OV_3D_VIDEO_3D_TV:
|
|
for (int i = 0; i < NUM_CHANNELS; i++) {
|
|
ret = objOvCtrlChannel[i].getPositionS3D(i, mS3DFormat,
|
|
&secDest);
|
|
if (!ret)
|
|
ret = setChannelPosition(x, y, w, h, i);
|
|
else
|
|
ret = setChannelPosition(secDest.x, secDest.y, secDest.w,
|
|
secDest.h, i);
|
|
if (!ret) {
|
|
LOGE("%s: failed for channel %d", __FUNCTION__, i);
|
|
return ret;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
LOGE("%s:Unknown state %d", __FUNCTION__, mState);
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Overlay::setChannelPosition(int x, int y, uint32_t w, uint32_t h, int channel) {
|
|
return objOvCtrlChannel[channel].setPosition(x, y, w, h);
|
|
}
|
|
|
|
bool Overlay::updateOverlaySource(const overlay_buffer_info& info, int orientation,
|
|
int flags) {
|
|
bool ret = false;
|
|
int currentFlags = 0;
|
|
|
|
bool needUpdateFlags = false;
|
|
if (objOvCtrlChannel[0].isChannelUP()) {
|
|
needUpdateFlags = objOvCtrlChannel[0].doFlagsNeedUpdate(flags);
|
|
}
|
|
|
|
bool geometryChanged = true;
|
|
if (info.width == mOVBufferInfo.width &&
|
|
info.height == mOVBufferInfo.height &&
|
|
info.format == mOVBufferInfo.format) {
|
|
geometryChanged = false;
|
|
}
|
|
|
|
if (sHDMIAsPrimary)
|
|
needUpdateFlags = false;
|
|
|
|
if ((false == needUpdateFlags) && (false == geometryChanged)) {
|
|
return true;
|
|
}
|
|
|
|
// Disable rotation for the HDMI channels
|
|
int orientHdmi = 0;
|
|
int orientPrimary = sHDMIAsPrimary ? 0 : orientation;
|
|
int orient[2] = {orientPrimary, orientHdmi};
|
|
// disable waitForVsync on HDMI, since we call the wait ioctl
|
|
int ovFlagsExternal = 0;
|
|
int ovFlagsPrimary = sHDMIAsPrimary ? (flags |= WAIT_FOR_VSYNC): flags;
|
|
int ovFlags[2] = {flags, ovFlagsExternal};
|
|
switch(mState) {
|
|
case OV_3D_VIDEO_3D_PANEL:
|
|
orient[1] = sHDMIAsPrimary ? 0 : orientation;
|
|
break;
|
|
case OV_3D_VIDEO_3D_TV:
|
|
orient[0] = 0;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
int numChannelsToUpdate = NUM_CHANNELS;
|
|
if (!geometryChanged) {
|
|
// Only update the primary channel - we only need to update the
|
|
// wait/no-wait flags
|
|
if (objOvCtrlChannel[0].isChannelUP()) {
|
|
return objOvCtrlChannel[0].updateOverlayFlags(flags);
|
|
}
|
|
}
|
|
|
|
// Set the overlay source info
|
|
for (int i = 0; i < NUM_CHANNELS; i++) {
|
|
if (objOvCtrlChannel[i].isChannelUP()) {
|
|
ret = objOvCtrlChannel[i].updateOverlaySource(info, orient[i], ovFlags[i]);
|
|
if (!ret) {
|
|
LOGE("objOvCtrlChannel[%d].updateOverlaySource failed", i);
|
|
return false;
|
|
}
|
|
objOvCtrlChannel[i].setSize(info.size);
|
|
ret = objOvDataChannel[i].updateDataChannel(info.size);
|
|
}
|
|
}
|
|
if (ret) {
|
|
mOVBufferInfo = info;
|
|
} else
|
|
LOGE("update failed");
|
|
return ret;
|
|
}
|
|
|
|
bool Overlay::getAspectRatioPosition(int w, int h, overlay_rect *rect, int channel) {
|
|
return objOvCtrlChannel[channel].getAspectRatioPosition(w, h, rect);
|
|
}
|
|
|
|
int Overlay::getS3DFormat(int format) {
|
|
// The S3D is part of the HAL_PIXEL_FORMAT_YV12 value. Add
|
|
// an explicit check for the format
|
|
if (format == HAL_PIXEL_FORMAT_YV12) {
|
|
return 0;
|
|
}
|
|
int format3D = FORMAT_3D(format);
|
|
int fIn3D = FORMAT_3D_INPUT(format3D); // MSB 2 bytes are input format
|
|
int fOut3D = FORMAT_3D_OUTPUT(format3D); // LSB 2 bytes are output format
|
|
format3D = fIn3D | fOut3D;
|
|
if (!fIn3D) {
|
|
format3D |= fOut3D << SHIFT_3D; //Set the input format
|
|
}
|
|
if (!fOut3D) {
|
|
format3D |= fIn3D >> SHIFT_3D; //Set the output format
|
|
}
|
|
return format3D;
|
|
}
|
|
|
|
bool Overlay::setSource(const overlay_buffer_info& info, int orientation,
|
|
int hdmiConnected, int flags, int num_buffers) {
|
|
// Separate the color format from the 3D format.
|
|
// If there is 3D content; the effective format passed by the client is:
|
|
// effectiveFormat = 3D_IN | 3D_OUT | ColorFormat
|
|
int newState = mState;
|
|
bool stateChange = false, ret = true;
|
|
bool isHDMIStateChange = (mExternalDisplay != hdmiConnected) && (mState != -1);
|
|
unsigned int format3D = getS3DFormat(info.format);
|
|
int colorFormat = getColorFormat(info.format);
|
|
if (isHDMIStateChange || -1 == mState) {
|
|
// we were mirroring UI. Also HDMI state stored was stale
|
|
newState = getOverlayConfig (format3D, false, hdmiConnected);
|
|
stateChange = (mState == newState) ? false : true;
|
|
}
|
|
|
|
if (stateChange) {
|
|
mExternalDisplay = hdmiConnected;
|
|
mState = newState;
|
|
mS3DFormat = format3D;
|
|
if (mState == OV_3D_VIDEO_2D_PANEL || mState == OV_3D_VIDEO_2D_TV) {
|
|
LOGI("3D content on 2D display: set the output format as monoscopic");
|
|
mS3DFormat = FORMAT_3D_INPUT(format3D) | HAL_3D_OUT_MONOSCOPIC_MASK;
|
|
}
|
|
// We always enable the rotator for the primary.
|
|
bool noRot = false;
|
|
bool uiChannel = false;
|
|
int fbnum = 0;
|
|
switch(mState) {
|
|
case OV_2D_VIDEO_ON_PANEL:
|
|
if(isHDMIStateChange) {
|
|
//close HDMI Only
|
|
closeExternalChannel();
|
|
break;
|
|
}
|
|
case OV_3D_VIDEO_2D_PANEL:
|
|
closeChannel();
|
|
return startChannel(info, FRAMEBUFFER_0, noRot, false,
|
|
mS3DFormat, VG0_PIPE, flags, num_buffers);
|
|
break;
|
|
case OV_3D_VIDEO_3D_PANEL:
|
|
closeChannel();
|
|
if (sHDMIAsPrimary) {
|
|
noRot = true;
|
|
flags |= WAIT_FOR_VSYNC;
|
|
send3DInfoPacket(mS3DFormat & OUTPUT_MASK_3D);
|
|
}
|
|
for (int i=0; i<NUM_CHANNELS; i++) {
|
|
if(!startChannel(info, FRAMEBUFFER_0, noRot, uiChannel,
|
|
mS3DFormat, i, flags, num_buffers)) {
|
|
LOGE("%s:failed to open channel %d", __FUNCTION__, i);
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
case OV_2D_VIDEO_ON_TV:
|
|
if(isHDMIStateChange) {
|
|
//start only HDMI channel
|
|
noRot = true;
|
|
bool waitForVsync = true;
|
|
// External display connected, start corresponding channel
|
|
// mExternalDisplay will hold the fbnum
|
|
if(!startChannel(info, mExternalDisplay, noRot, false, mS3DFormat,
|
|
VG1_PIPE, waitForVsync, num_buffers)) {
|
|
LOGE("%s:failed to open channel %d", __func__, VG1_PIPE);
|
|
return false;
|
|
}
|
|
int currX, currY;
|
|
uint32_t currW, currH;
|
|
overlay_rect priDest;
|
|
overlay_rect secDest;
|
|
objOvCtrlChannel[VG0_PIPE].getPosition(currX, currY, currW, currH);
|
|
priDest.x = currX, priDest.y = currY;
|
|
priDest.w = currW, priDest.h = currH;
|
|
if (FrameBufferInfo::getInstance()->canSupportTrueMirroring()) {
|
|
objOvCtrlChannel[VG1_PIPE].getAspectRatioPosition(
|
|
mCroppedSrcWidth, mCroppedSrcHeight, mDevOrientation,
|
|
&priDest, &secDest);
|
|
} else {
|
|
objOvCtrlChannel[VG1_PIPE].getAspectRatioPosition(
|
|
mCroppedSrcWidth, mCroppedSrcHeight, &secDest);
|
|
}
|
|
return setChannelPosition(secDest.x, secDest.y, secDest.w, secDest.h, VG1_PIPE);
|
|
}
|
|
case OV_3D_VIDEO_2D_TV:
|
|
closeChannel();
|
|
for (int i=0; i<NUM_CHANNELS; i++) {
|
|
fbnum = i;
|
|
//start two channels for one for primary and external.
|
|
if (fbnum) {
|
|
// Disable rotation for external
|
|
noRot = true;
|
|
//set fbnum to hdmiConnected, which holds the ext display
|
|
fbnum = hdmiConnected;
|
|
flags &= ~WAIT_FOR_VSYNC;
|
|
}
|
|
if(!startChannel(info, fbnum, noRot, false, mS3DFormat,
|
|
i, flags, num_buffers)) {
|
|
LOGE("%s:failed to open channel %d", __FUNCTION__, i);
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
break;
|
|
case OV_3D_VIDEO_3D_TV:
|
|
closeChannel();
|
|
for (int i=0; i<NUM_CHANNELS; i++) {
|
|
if(!startChannel(info, FRAMEBUFFER_1, true, false,
|
|
mS3DFormat, i, flags, num_buffers)) {
|
|
LOGE("%s:failed to open channel %d", __FUNCTION__, i);
|
|
return false;
|
|
}
|
|
send3DInfoPacket(mS3DFormat & OUTPUT_MASK_3D);
|
|
}
|
|
break;
|
|
default:
|
|
LOGE("%s:Unknown state %d", __FUNCTION__, mState);
|
|
break;
|
|
}
|
|
} else {
|
|
ret = updateOverlaySource(info, orientation, flags);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Overlay::setCrop(uint32_t x, uint32_t y, uint32_t w, uint32_t h) {
|
|
if (!mChannelUP) {
|
|
LOGE("%s: channel not set", __FUNCTION__);
|
|
return false;
|
|
}
|
|
overlay_rect rect, inRect;
|
|
inRect.x = x; inRect.y = y; inRect.w = w; inRect.h = h;
|
|
mCroppedSrcWidth = w;
|
|
mCroppedSrcHeight = h;
|
|
switch (mState) {
|
|
case OV_UI_MIRROR_TV:
|
|
case OV_2D_VIDEO_ON_PANEL:
|
|
return setChannelCrop(x, y, w, h, VG0_PIPE);
|
|
break;
|
|
case OV_3D_VIDEO_2D_PANEL:
|
|
objOvDataChannel[VG0_PIPE].getCropS3D(&inRect, VG0_PIPE, mS3DFormat, &rect);
|
|
return setChannelCrop(rect.x, rect.y, rect.w, rect.h, VG0_PIPE);
|
|
break;
|
|
case OV_2D_VIDEO_ON_TV:
|
|
for (int i=0; i<NUM_CHANNELS; i++) {
|
|
if(!setChannelCrop(x, y, w, h, i)) {
|
|
LOGE("%s: failed for pipe %d", __FUNCTION__, i);
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
case OV_3D_VIDEO_3D_PANEL:
|
|
case OV_3D_VIDEO_2D_TV:
|
|
case OV_3D_VIDEO_3D_TV:
|
|
for (int i=0; i<NUM_CHANNELS; i++) {
|
|
objOvDataChannel[i].getCropS3D(&inRect, i, mS3DFormat, &rect);
|
|
if(!setChannelCrop(rect.x, rect.y, rect.w, rect.h, i)) {
|
|
LOGE("%s: failed for pipe %d", __FUNCTION__, i);
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
LOGE("%s:Unknown state %d", __FUNCTION__, mState);
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Overlay::setChannelCrop(uint32_t x, uint32_t y, uint32_t w, uint32_t h, int channel) {
|
|
return objOvDataChannel[channel].setCrop(x, y, w, h);
|
|
}
|
|
|
|
bool Overlay::updateOverlayFlags(int flags) {
|
|
return objOvCtrlChannel[VG0_PIPE].updateOverlayFlags(flags);
|
|
}
|
|
|
|
bool Overlay::setTransform(int value) {
|
|
int barrier = 0;
|
|
switch (mState) {
|
|
case OV_UI_MIRROR_TV:
|
|
case OV_2D_VIDEO_ON_PANEL:
|
|
case OV_3D_VIDEO_2D_PANEL:
|
|
return objOvCtrlChannel[VG0_PIPE].setTransform(value);
|
|
break;
|
|
case OV_2D_VIDEO_ON_TV:
|
|
case OV_3D_VIDEO_2D_TV:
|
|
case OV_3D_VIDEO_3D_TV:
|
|
for (int i=0; i<NUM_CHANNELS; i++) {
|
|
if(!objOvCtrlChannel[i].setTransform(value)) {
|
|
LOGE("%s:failed for channel %d", __FUNCTION__, i);
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
case OV_3D_VIDEO_3D_PANEL:
|
|
switch (value) {
|
|
case HAL_TRANSFORM_ROT_90:
|
|
case HAL_TRANSFORM_ROT_270:
|
|
barrier = BARRIER_LANDSCAPE;
|
|
break;
|
|
default:
|
|
barrier = BARRIER_PORTRAIT;
|
|
break;
|
|
if(!enableBarrier(barrier))
|
|
LOGE("%s:failed to enable barriers for 3D video", __FUNCTION__);
|
|
}
|
|
for (int i=0; i<NUM_CHANNELS; i++) {
|
|
if(!objOvCtrlChannel[i].setTransform(value)) {
|
|
LOGE("%s:failed for channel %d", __FUNCTION__, i);
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
LOGE("%s:Unknown state %d", __FUNCTION__, mState);
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Overlay::setFd(int fd, int channel) {
|
|
return objOvDataChannel[channel].setFd(fd);
|
|
}
|
|
|
|
bool Overlay::queueBuffer(uint32_t offset, int channel) {
|
|
return objOvDataChannel[channel].queueBuffer(offset);
|
|
}
|
|
#if 0
|
|
bool Overlay::waitForHdmiVsync(int channel) {
|
|
return objOvDataChannel[channel].waitForHdmiVsync();
|
|
}
|
|
#endif
|
|
bool Overlay::queueBuffer(buffer_handle_t buffer) {
|
|
private_handle_t const* hnd = reinterpret_cast
|
|
<private_handle_t const*>(buffer);
|
|
if (!hnd) {
|
|
LOGE("Overlay::queueBuffer invalid handle");
|
|
return false;
|
|
}
|
|
const size_t offset = hnd->offset;
|
|
const int fd = hnd->fd;
|
|
switch (mState) {
|
|
case OV_UI_MIRROR_TV:
|
|
case OV_2D_VIDEO_ON_PANEL:
|
|
case OV_3D_VIDEO_2D_PANEL:
|
|
if(!queueBuffer(fd, offset, VG0_PIPE)) {
|
|
LOGE("%s:failed for channel 0", __FUNCTION__);
|
|
return false;
|
|
}
|
|
break;
|
|
case OV_2D_VIDEO_ON_TV:
|
|
case OV_3D_VIDEO_3D_PANEL:
|
|
case OV_3D_VIDEO_2D_TV:
|
|
case OV_3D_VIDEO_3D_TV:
|
|
for (int i=NUM_CHANNELS-1; i>=0; i--) {
|
|
if(!queueBuffer(fd, offset, i)) {
|
|
LOGE("%s:failed for channel %d", __FUNCTION__, i);
|
|
return false;
|
|
}
|
|
}
|
|
#if 0
|
|
//Wait for HDMI done..
|
|
if(!waitForHdmiVsync(VG1_PIPE)) {
|
|
LOGE("%s: waitforHdmiVsync failed", __FUNCTION__);
|
|
return false;
|
|
}
|
|
#endif
|
|
break;
|
|
default:
|
|
LOGE("%s:Unknown state %d", __FUNCTION__, mState);
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Overlay::queueBuffer(int fd, uint32_t offset, int channel) {
|
|
bool ret = false;
|
|
ret = setFd(fd, channel);
|
|
if(!ret) {
|
|
LOGE("Overlay::queueBuffer channel %d setFd failed", channel);
|
|
return false;
|
|
}
|
|
ret = queueBuffer(offset, channel);
|
|
if(!ret) {
|
|
LOGE("Overlay::queueBuffer channel %d queueBuffer failed", channel);
|
|
return false;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
OverlayControlChannel::OverlayControlChannel() : mNoRot(false), mFD(-1), mRotFD(-1),
|
|
mFormat3D(0), mIsChannelUpdated(true) {
|
|
memset(&mOVInfo, 0, sizeof(mOVInfo));
|
|
memset(&m3DOVInfo, 0, sizeof(m3DOVInfo));
|
|
memset(&mRotInfo, 0, sizeof(mRotInfo));
|
|
}
|
|
|
|
|
|
OverlayControlChannel::~OverlayControlChannel() {
|
|
closeControlChannel();
|
|
}
|
|
|
|
bool OverlayControlChannel::getAspectRatioPosition(int w, int h, overlay_rect *rect)
|
|
{
|
|
int width = w, height = h, x, y;
|
|
int fbWidth = getFBWidth();
|
|
int fbHeight = getFBHeight();
|
|
// width and height for YUV TILE format
|
|
int tempWidth = w, tempHeight = h;
|
|
/* Calculate the width and height if it is YUV TILE format*/
|
|
if(getFormat() == HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED) {
|
|
tempWidth = w - ( (((w-1)/64 +1)*64) - w);
|
|
tempHeight = h - ((((h-1)/32 +1)*32) - h);
|
|
}
|
|
if (width * fbHeight > fbWidth * height) {
|
|
height = fbWidth * height / width;
|
|
EVEN_OUT(height);
|
|
width = fbWidth;
|
|
} else if (width * fbHeight < fbWidth * height) {
|
|
width = fbHeight * width / height;
|
|
EVEN_OUT(width);
|
|
height = fbHeight;
|
|
} else {
|
|
width = fbWidth;
|
|
height = fbHeight;
|
|
}
|
|
/* Scaling of upto a max of 8 times supported */
|
|
if(width >(tempWidth * HW_OVERLAY_MAGNIFICATION_LIMIT)){
|
|
width = HW_OVERLAY_MAGNIFICATION_LIMIT * tempWidth;
|
|
}
|
|
if(height >(tempHeight*HW_OVERLAY_MAGNIFICATION_LIMIT)) {
|
|
height = HW_OVERLAY_MAGNIFICATION_LIMIT * tempHeight;
|
|
}
|
|
if (width > fbWidth) width = fbWidth;
|
|
if (height > fbHeight) height = fbHeight;
|
|
|
|
char value[PROPERTY_VALUE_MAX];
|
|
property_get("hw.actionsafe.width", value, "0");
|
|
float asWidth = atof(value);
|
|
property_get("hw.actionsafe.height", value, "0");
|
|
float asHeight = atof(value);
|
|
width = width * (1.0f - asWidth / 100.0f);
|
|
height = height * (1.0f - asHeight / 100.0f);
|
|
|
|
x = (fbWidth - width) / 2;
|
|
y = (fbHeight - height) / 2;
|
|
rect->x = x;
|
|
rect->y = y;
|
|
rect->w = width;
|
|
rect->h = height;
|
|
return true;
|
|
}
|
|
|
|
|
|
// This function gets the destination position for Seconday display
|
|
// based on the position and aspect ratio of the primary
|
|
bool OverlayControlChannel::getAspectRatioPosition(int w, int h, int orientation,
|
|
overlay_rect *inRect, overlay_rect *outRect) {
|
|
float priWidth = FrameBufferInfo::getInstance()->getWidth();
|
|
float priHeight = FrameBufferInfo::getInstance()->getHeight();
|
|
float fbWidth = getFBWidth();
|
|
float fbHeight = getFBHeight();
|
|
float wRatio = 1.0;
|
|
float hRatio = 1.0;
|
|
float xRatio = 1.0;
|
|
float yRatio = 1.0;
|
|
|
|
int xPos = 0;
|
|
int yPos = 0;
|
|
int tmp = 0;
|
|
overlay_rect rect;
|
|
switch(orientation) {
|
|
case MDP_ROT_NOP:
|
|
case MDP_ROT_180:
|
|
getAspectRatioPosition((int)priWidth, (int)priHeight, &rect);
|
|
xPos = rect.x;
|
|
yPos = rect.y;
|
|
fbWidth = rect.w;
|
|
fbHeight = rect.h;
|
|
|
|
if(orientation == MDP_ROT_180) {
|
|
inRect->x = priWidth - (inRect->x + inRect->w);
|
|
inRect->y = priHeight - (inRect->y + inRect->h);
|
|
}
|
|
break;
|
|
case MDP_ROT_90:
|
|
case MDP_ROT_270:
|
|
if(orientation == MDP_ROT_90) {
|
|
tmp = inRect->y;
|
|
inRect->y = priWidth - (inRect->x + inRect->w);
|
|
inRect->x = tmp;
|
|
}
|
|
else if(orientation == MDP_ROT_270) {
|
|
tmp = inRect->x;
|
|
inRect->x = priHeight - (inRect->y + inRect->h);
|
|
inRect->y = tmp;
|
|
}
|
|
//Swap the destination width/height
|
|
swapWidthHeight(inRect->w, inRect->h);
|
|
// Swap width/height for primary
|
|
swapWidthHeight(priWidth, priHeight);
|
|
getAspectRatioPosition((int)priWidth, (int)priHeight, &rect);
|
|
xPos = rect.x;
|
|
yPos = rect.y;
|
|
fbWidth = rect.w;
|
|
fbHeight = rect.h;
|
|
break;
|
|
default:
|
|
LOGE("In %s: Unknown Orientation", __FUNCTION__);
|
|
break;
|
|
}
|
|
//Calculate the position...
|
|
xRatio = inRect->x/priWidth;
|
|
yRatio = inRect->y/priHeight;
|
|
|
|
wRatio = inRect->w/priWidth;
|
|
hRatio = inRect->h/priHeight;
|
|
outRect->x = (xRatio * fbWidth) + xPos;
|
|
outRect->y = (yRatio * fbHeight) + yPos;
|
|
|
|
outRect->w = (wRatio * fbWidth);
|
|
outRect->h = hRatio * fbHeight;
|
|
LOGD("Calculated AS Position for HDMI: X= %d, y = %d w = %d h = %d",
|
|
outRect->x, outRect->y,outRect->w, outRect->h);
|
|
return true;
|
|
}
|
|
|
|
|
|
bool OverlayControlChannel::getPositionS3D(int channel, int format, overlay_rect *rect) {
|
|
int wDisp = getFBWidth();
|
|
int hDisp = getFBHeight();
|
|
switch (format & OUTPUT_MASK_3D) {
|
|
case HAL_3D_OUT_SIDE_BY_SIDE_MASK:
|
|
if (channel == VG0_PIPE) {
|
|
rect->x = 0;
|
|
rect->y = 0;
|
|
rect->w = wDisp/2;
|
|
rect->h = hDisp;
|
|
} else {
|
|
rect->x = wDisp/2;
|
|
rect->y = 0;
|
|
rect->w = wDisp/2;
|
|
rect->h = hDisp;
|
|
}
|
|
break;
|
|
case HAL_3D_OUT_TOP_BOTTOM_MASK:
|
|
if (channel == VG0_PIPE) {
|
|
rect->x = 0;
|
|
rect->y = 0;
|
|
rect->w = wDisp;
|
|
rect->h = hDisp/2;
|
|
} else {
|
|
rect->x = 0;
|
|
rect->y = hDisp/2;
|
|
rect->w = wDisp;
|
|
rect->h = hDisp/2;
|
|
}
|
|
break;
|
|
case HAL_3D_OUT_MONOSCOPIC_MASK:
|
|
if (channel == VG1_PIPE) {
|
|
rect->x = 0;
|
|
rect->y = 0;
|
|
rect->w = wDisp;
|
|
rect->h = hDisp;
|
|
}
|
|
else
|
|
return false;
|
|
break;
|
|
case HAL_3D_OUT_INTERLEAVE_MASK:
|
|
break;
|
|
default:
|
|
reportError("Unsupported 3D output format");
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool OverlayControlChannel::openDevices(int fbnum) {
|
|
if (fbnum < 0)
|
|
return false;
|
|
|
|
char dev_name[64];
|
|
snprintf(dev_name, 64, FB_DEVICE_TEMPLATE, fbnum);
|
|
mFD = open(dev_name, O_RDWR, 0);
|
|
if (mFD < 0) {
|
|
reportError("Cant open framebuffer ");
|
|
return false;
|
|
}
|
|
|
|
fb_fix_screeninfo finfo;
|
|
if (ioctl(mFD, FBIOGET_FSCREENINFO, &finfo) == -1) {
|
|
reportError("FBIOGET_FSCREENINFO on fb1 failed");
|
|
close(mFD);
|
|
mFD = -1;
|
|
return false;
|
|
}
|
|
|
|
fb_var_screeninfo vinfo;
|
|
if (ioctl(mFD, FBIOGET_VSCREENINFO, &vinfo) == -1) {
|
|
reportError("FBIOGET_VSCREENINFO on fb1 failed");
|
|
close(mFD);
|
|
mFD = -1;
|
|
return false;
|
|
}
|
|
mFBWidth = vinfo.xres;
|
|
mFBHeight = vinfo.yres;
|
|
mFBbpp = vinfo.bits_per_pixel;
|
|
mFBystride = finfo.line_length;
|
|
|
|
if (!mNoRot) {
|
|
mRotFD = open("/dev/msm_rotator", O_RDWR, 0);
|
|
if (mRotFD < 0) {
|
|
reportError("Cant open rotator device");
|
|
close(mFD);
|
|
mFD = -1;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool OverlayControlChannel::setOverlayInformation(const overlay_buffer_info& info,
|
|
int zorder, int flags, int requestType) {
|
|
int w = info.width;
|
|
int h = info.height;
|
|
int format = info.format;
|
|
|
|
mOVInfo.src.width = w;
|
|
mOVInfo.src.height = h;
|
|
mOVInfo.src_rect.x = 0;
|
|
mOVInfo.src_rect.y = 0;
|
|
mOVInfo.dst_rect.x = 0;
|
|
mOVInfo.dst_rect.y = 0;
|
|
mOVInfo.dst_rect.w = w;
|
|
mOVInfo.dst_rect.h = h;
|
|
if(format == MDP_Y_CRCB_H2V2_TILE) {
|
|
if (mNoRot) {
|
|
mOVInfo.src_rect.w = w - ((((w-1)/64 +1)*64) - w);
|
|
mOVInfo.src_rect.h = h - ((((h-1)/32 +1)*32) - h);
|
|
} else {
|
|
mOVInfo.src_rect.w = w;
|
|
mOVInfo.src_rect.h = h;
|
|
mOVInfo.src.width = (((w-1)/64 +1)*64);
|
|
mOVInfo.src.height = (((h-1)/32 +1)*32);
|
|
mOVInfo.src_rect.x = mOVInfo.src.width - w;
|
|
mOVInfo.src_rect.y = mOVInfo.src.height - h;
|
|
}
|
|
} else {
|
|
mOVInfo.src_rect.w = w;
|
|
mOVInfo.src_rect.h = h;
|
|
}
|
|
|
|
mOVInfo.src.format = format;
|
|
int dst_w = w;
|
|
int dst_h = h;
|
|
|
|
if (dst_w > mFBWidth) {
|
|
dst_w = mFBWidth;
|
|
dst_h = dst_h * mFBWidth / w;
|
|
}
|
|
if (dst_h > mFBHeight) {
|
|
dst_h = mFBHeight;
|
|
dst_w = dst_w * mFBHeight / h;
|
|
}
|
|
mOVInfo.dst_rect.w = dst_w;
|
|
mOVInfo.dst_rect.h = dst_h;
|
|
mOVInfo.user_data[0] = 0;
|
|
if (requestType == NEW_REQUEST) {
|
|
mOVInfo.id = MSMFB_NEW_REQUEST;
|
|
mOVInfo.z_order = zorder;
|
|
mOVInfo.alpha = 0xff;
|
|
mOVInfo.transp_mask = 0xffffffff;
|
|
}
|
|
mOVInfo.flags = 0;
|
|
if (info.secure) {
|
|
flags |= SECURE_OVERLAY_SESSION;
|
|
} else {
|
|
flags &= ~SECURE_OVERLAY_SESSION;
|
|
}
|
|
setInformationFromFlags(flags, mOVInfo);
|
|
mOVInfo.dpp.sharp_strength = 0;
|
|
return true;
|
|
}
|
|
|
|
void OverlayControlChannel::setInformationFromFlags(int flags, mdp_overlay& ov)
|
|
{
|
|
if (flags & INTERLACED_CONTENT) {
|
|
mOVInfo.flags |= MDP_DEINTERLACE;
|
|
} else {
|
|
mOVInfo.flags &= ~MDP_DEINTERLACE;
|
|
}
|
|
|
|
if ((flags & WAIT_FOR_VSYNC) == 0)
|
|
mOVInfo.flags |= MDP_OV_PLAY_NOWAIT;
|
|
else
|
|
mOVInfo.flags &= ~MDP_OV_PLAY_NOWAIT;
|
|
|
|
if(flags & SECURE_OVERLAY_SESSION)
|
|
mOVInfo.flags |= MDP_SECURE_OVERLAY_SESSION;
|
|
else
|
|
mOVInfo.flags &= ~MDP_SECURE_OVERLAY_SESSION;
|
|
|
|
//set the default sharpening settings
|
|
mOVInfo.flags |= MDP_SHARPENING;
|
|
|
|
if (flags & DISABLE_FRAMEBUFFER_FETCH)
|
|
mOVInfo.is_fg = 1;
|
|
else
|
|
mOVInfo.is_fg = 0;
|
|
|
|
if (flags & OVERLAY_PIPE_SHARE) {
|
|
mOVInfo.flags |= MDP_OV_PIPE_SHARE;
|
|
} else {
|
|
mOVInfo.flags &= ~MDP_OV_PIPE_SHARE;
|
|
}
|
|
}
|
|
|
|
bool OverlayControlChannel::doFlagsNeedUpdate(int flags) {
|
|
bool needUpdate = false;
|
|
|
|
if ((flags & WAIT_FOR_VSYNC) == 0) {
|
|
if (!(mOVInfo.flags & MDP_OV_PLAY_NOWAIT)) {
|
|
needUpdate = true;
|
|
}
|
|
}
|
|
if (flags & WAIT_FOR_VSYNC) {
|
|
if (mOVInfo.flags & MDP_OV_PLAY_NOWAIT) {
|
|
needUpdate = true;
|
|
}
|
|
}
|
|
|
|
if ((flags & DISABLE_FRAMEBUFFER_FETCH) == 0) {
|
|
if (mOVInfo.is_fg == 1) {
|
|
needUpdate = true;
|
|
}
|
|
}
|
|
if (flags & DISABLE_FRAMEBUFFER_FETCH) {
|
|
if (mOVInfo.is_fg == 0) {
|
|
needUpdate = true;
|
|
}
|
|
}
|
|
return needUpdate;
|
|
}
|
|
|
|
bool OverlayControlChannel::startOVRotatorSessions(
|
|
const overlay_buffer_info& info,
|
|
int requestType) {
|
|
bool ret = true;
|
|
int w = info.width;
|
|
int h = info.height;
|
|
int format = info.format;
|
|
|
|
if (!mNoRot) {
|
|
mRotInfo.src.format = format;
|
|
mRotInfo.src.width = w;
|
|
mRotInfo.src.height = h;
|
|
mRotInfo.src_rect.w = w;
|
|
mRotInfo.src_rect.h = h;
|
|
mRotInfo.dst.width = w;
|
|
mRotInfo.dst.height = h;
|
|
if(format == MDP_Y_CRCB_H2V2_TILE) {
|
|
mRotInfo.src.width = (((w-1)/64 +1)*64);
|
|
mRotInfo.src.height = (((h-1)/32 +1)*32);
|
|
mRotInfo.src_rect.w = (((w-1)/64 +1)*64);
|
|
mRotInfo.src_rect.h = (((h-1)/32 +1)*32);
|
|
mRotInfo.dst.width = (((w-1)/64 +1)*64);
|
|
mRotInfo.dst.height = (((h-1)/32 +1)*32);
|
|
mRotInfo.dst.format = MDP_Y_CRCB_H2V2;
|
|
}
|
|
mRotInfo.dst.format = get_rot_output_format(format);
|
|
mRotInfo.dst_x = 0;
|
|
mRotInfo.dst_y = 0;
|
|
mRotInfo.src_rect.x = 0;
|
|
mRotInfo.src_rect.y = 0;
|
|
mRotInfo.rotations = 0;
|
|
|
|
if (requestType == NEW_REQUEST) {
|
|
mRotInfo.enable = 0;
|
|
if(mUIChannel)
|
|
mRotInfo.enable = 1;
|
|
mRotInfo.session_id = 0;
|
|
} else
|
|
mRotInfo.enable = 1;
|
|
|
|
int result = ioctl(mRotFD, MSM_ROTATOR_IOCTL_START, &mRotInfo);
|
|
if (result) {
|
|
reportError("Rotator session failed");
|
|
dump(mRotInfo);
|
|
ret = false;
|
|
}
|
|
}
|
|
|
|
if (ret && ioctl(mFD, MSMFB_OVERLAY_SET, &mOVInfo)) {
|
|
reportError("startOVRotatorSessions, Overlay set failed");
|
|
dump(mOVInfo);
|
|
ret = false;
|
|
}
|
|
|
|
if (!ret)
|
|
closeControlChannel();
|
|
else
|
|
mIsChannelUpdated = true;
|
|
return ret;
|
|
}
|
|
|
|
bool OverlayControlChannel::updateOverlaySource(const overlay_buffer_info& info,
|
|
int orientation, int flags)
|
|
{
|
|
int colorFormat = getColorFormat(info.format);
|
|
int hw_format = get_mdp_format(colorFormat);
|
|
overlay_buffer_info ovBufInfo;
|
|
ovBufInfo.width = info.width;
|
|
ovBufInfo.height = info.height;
|
|
ovBufInfo.format = hw_format;
|
|
|
|
if (isInterlacedContent(info.format)) {
|
|
flags |= INTERLACED_CONTENT;
|
|
}
|
|
if (!setOverlayInformation(ovBufInfo, 0, flags,
|
|
UPDATE_REQUEST))
|
|
return false;
|
|
|
|
return startOVRotatorSessions(ovBufInfo, UPDATE_REQUEST);
|
|
}
|
|
|
|
bool OverlayControlChannel::startControlChannel(int w, int h,
|
|
int format, int fbnum, bool norot,
|
|
bool uichannel,
|
|
unsigned int format3D, int zorder,
|
|
int flags) {
|
|
mNoRot = norot;
|
|
mFormat = format;
|
|
mUIChannel = uichannel;
|
|
mFBNum = fbnum;
|
|
fb_fix_screeninfo finfo;
|
|
fb_var_screeninfo vinfo;
|
|
int hw_format;
|
|
int colorFormat = format;
|
|
// The interlace mask is part of the HAL_PIXEL_FORMAT_YV12 value. Add
|
|
// an explicit check for the format
|
|
if (isInterlacedContent(format)) {
|
|
flags |= MDP_DEINTERLACE;
|
|
|
|
// Get the actual format
|
|
colorFormat = format ^ HAL_PIXEL_FORMAT_INTERLACE;
|
|
}
|
|
hw_format = get_mdp_format(colorFormat);
|
|
if (hw_format < 0) {
|
|
reportError("Unsupported format");
|
|
return false;
|
|
}
|
|
|
|
mFormat3D = format3D;
|
|
if ( !mFormat3D || (mFormat3D & HAL_3D_OUT_MONOSCOPIC_MASK) ) {
|
|
// Set the share bit for sharing the VG pipe
|
|
flags |= OVERLAY_PIPE_SHARE;
|
|
}
|
|
//do not set the PIPE SHARE bit for true mirroring
|
|
if(uichannel && FrameBufferInfo::getInstance()->canSupportTrueMirroring())
|
|
flags &= ~OVERLAY_PIPE_SHARE;
|
|
if (!openDevices(fbnum))
|
|
return false;
|
|
|
|
//get Z order
|
|
zorder = ZOrderManager::getInstance()->getZ(fbnum);
|
|
if (zorder == NO_PIPE)
|
|
return false;
|
|
|
|
overlay_buffer_info ovBufInfo;
|
|
ovBufInfo.width = w;
|
|
ovBufInfo.height = h;
|
|
ovBufInfo.format = hw_format;
|
|
if (!setOverlayInformation(ovBufInfo, zorder, flags, NEW_REQUEST))
|
|
return false;
|
|
|
|
return startOVRotatorSessions(ovBufInfo, NEW_REQUEST);
|
|
}
|
|
|
|
bool OverlayControlChannel::closeControlChannel() {
|
|
if (!isChannelUP())
|
|
return true;
|
|
|
|
if (!mNoRot && mRotFD > 0) {
|
|
ioctl(mRotFD, MSM_ROTATOR_IOCTL_FINISH, &(mRotInfo.session_id));
|
|
close(mRotFD);
|
|
mRotFD = -1;
|
|
}
|
|
|
|
int ovid = mOVInfo.id;
|
|
ioctl(mFD, MSMFB_OVERLAY_UNSET, &ovid);
|
|
if (m3DOVInfo.is_3d) {
|
|
m3DOVInfo.is_3d = 0;
|
|
ioctl(mFD, MSMFB_OVERLAY_3D, &m3DOVInfo);
|
|
}
|
|
|
|
close(mFD);
|
|
|
|
if(NO_PIPE != mOVInfo.z_order){
|
|
ZOrderManager::getInstance()->decZ(mFBNum, mOVInfo.z_order);
|
|
}
|
|
memset(&mOVInfo, 0, sizeof(mOVInfo));
|
|
memset(&mRotInfo, 0, sizeof(mRotInfo));
|
|
memset(&m3DOVInfo, 0, sizeof(m3DOVInfo));
|
|
|
|
mOVInfo.z_order = NO_PIPE;
|
|
mFD = -1;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool OverlayControlChannel::updateOverlayFlags(int flags) {
|
|
if ((flags & WAIT_FOR_VSYNC) == 0)
|
|
mOVInfo.flags |= MDP_OV_PLAY_NOWAIT;
|
|
else
|
|
mOVInfo.flags &= ~MDP_OV_PLAY_NOWAIT;
|
|
|
|
if (flags & DISABLE_FRAMEBUFFER_FETCH)
|
|
mOVInfo.is_fg = 1;
|
|
else
|
|
mOVInfo.is_fg = 0;
|
|
|
|
if (ioctl(mFD, MSMFB_OVERLAY_SET, &mOVInfo)) {
|
|
LOGE("%s: OVERLAY_SET failed", __FUNCTION__);
|
|
dump(mOVInfo);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool OverlayControlChannel::setPosition(int x, int y, uint32_t w, uint32_t h) {
|
|
|
|
if (!isChannelUP() ||
|
|
(x < 0) || (y < 0) || ((x + w) > mFBWidth) ||
|
|
((y + h) > mFBHeight)) {
|
|
reportError("setPosition failed");
|
|
LOGW("x %d y %d (x+w) %d (y+h) %d FBWidth %d FBHeight %d", x, y, x+w, y+h,
|
|
mFBWidth,mFBHeight);
|
|
return false;
|
|
}
|
|
if( x != mOVInfo.dst_rect.x || y != mOVInfo.dst_rect.y ||
|
|
w != mOVInfo.dst_rect.w || h != mOVInfo.dst_rect.h ) {
|
|
mdp_overlay ov;
|
|
ov.id = mOVInfo.id;
|
|
if (ioctl(mFD, MSMFB_OVERLAY_GET, &ov)) {
|
|
reportError("setPosition, overlay GET failed");
|
|
return false;
|
|
}
|
|
|
|
/* Scaling of upto a max of 8 times supported */
|
|
if(w >(ov.src_rect.w * HW_OVERLAY_MAGNIFICATION_LIMIT)){
|
|
w = HW_OVERLAY_MAGNIFICATION_LIMIT * ov.src_rect.w;
|
|
x = (mFBWidth - w) / 2;
|
|
}
|
|
if(h >(ov.src_rect.h * HW_OVERLAY_MAGNIFICATION_LIMIT)) {
|
|
h = HW_OVERLAY_MAGNIFICATION_LIMIT * ov.src_rect.h;
|
|
y = (mFBHeight - h) / 2;
|
|
}
|
|
ov.dst_rect.x = x;
|
|
ov.dst_rect.y = y;
|
|
ov.dst_rect.w = w;
|
|
ov.dst_rect.h = h;
|
|
if (ioctl(mFD, MSMFB_OVERLAY_SET, &ov)) {
|
|
reportError("setPosition, Overlay SET failed");
|
|
dump(ov);
|
|
return false;
|
|
}
|
|
mOVInfo = ov;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void OverlayControlChannel::swapOVRotWidthHeight() {
|
|
int tmp = mOVInfo.src.width;
|
|
mOVInfo.src.width = mOVInfo.src.height;
|
|
mOVInfo.src.height = tmp;
|
|
|
|
tmp = mOVInfo.src_rect.h;
|
|
mOVInfo.src_rect.h = mOVInfo.src_rect.w;
|
|
mOVInfo.src_rect.w = tmp;
|
|
|
|
tmp = mRotInfo.dst.width;
|
|
mRotInfo.dst.width = mRotInfo.dst.height;
|
|
mRotInfo.dst.height = tmp;
|
|
}
|
|
|
|
bool OverlayControlChannel::useVirtualFB() {
|
|
if(!m3DOVInfo.is_3d) {
|
|
m3DOVInfo.is_3d = 1;
|
|
mFBWidth *= 2;
|
|
mFBHeight /= 2;
|
|
m3DOVInfo.width = mFBWidth;
|
|
m3DOVInfo.height = mFBHeight;
|
|
return ioctl(mFD, MSMFB_OVERLAY_3D, &m3DOVInfo) ? false : true;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool OverlayControlChannel::setTransform(int value, bool fetch) {
|
|
if (!isChannelUP()) {
|
|
LOGE("%s: channel is not up", __FUNCTION__);
|
|
return false;
|
|
}
|
|
|
|
mdp_overlay ov = mOVInfo;
|
|
if (fetch && ioctl(mFD, MSMFB_OVERLAY_GET, &ov)) {
|
|
reportError("setParameter, overlay GET failed");
|
|
return false;
|
|
}
|
|
mOVInfo = ov;
|
|
if (!mIsChannelUpdated) {
|
|
int orientation = get_mdp_orientation(value);
|
|
if (orientation == mOVInfo.user_data[0]) {
|
|
return true;
|
|
}
|
|
}
|
|
mIsChannelUpdated = false;
|
|
|
|
int val = mOVInfo.user_data[0];
|
|
if (mNoRot)
|
|
return true;
|
|
|
|
int rot = value;
|
|
|
|
switch(rot) {
|
|
case 0:
|
|
case HAL_TRANSFORM_FLIP_H:
|
|
case HAL_TRANSFORM_FLIP_V:
|
|
{
|
|
if (val == MDP_ROT_90) {
|
|
int tmp = mOVInfo.src_rect.y;
|
|
mOVInfo.src_rect.y = mOVInfo.src.width -
|
|
(mOVInfo.src_rect.x + mOVInfo.src_rect.w);
|
|
mOVInfo.src_rect.x = tmp;
|
|
swapOVRotWidthHeight();
|
|
}
|
|
else if (val == MDP_ROT_270) {
|
|
int tmp = mOVInfo.src_rect.x;
|
|
mOVInfo.src_rect.x = mOVInfo.src.height - (
|
|
mOVInfo.src_rect.y + mOVInfo.src_rect.h);
|
|
mOVInfo.src_rect.y = tmp;
|
|
swapOVRotWidthHeight();
|
|
}
|
|
break;
|
|
}
|
|
case HAL_TRANSFORM_ROT_90:
|
|
case (HAL_TRANSFORM_ROT_90|HAL_TRANSFORM_FLIP_H):
|
|
case (HAL_TRANSFORM_ROT_90|HAL_TRANSFORM_FLIP_V):
|
|
{
|
|
if (val == MDP_ROT_270) {
|
|
mOVInfo.src_rect.x = mOVInfo.src.width - (
|
|
mOVInfo.src_rect.x + mOVInfo.src_rect.w);
|
|
mOVInfo.src_rect.y = mOVInfo.src.height - (
|
|
mOVInfo.src_rect.y + mOVInfo.src_rect.h);
|
|
}
|
|
else if (val == MDP_ROT_NOP || val == MDP_ROT_180) {
|
|
int tmp = mOVInfo.src_rect.x;
|
|
mOVInfo.src_rect.x = mOVInfo.src.height -
|
|
(mOVInfo.src_rect.y + mOVInfo.src_rect.h);
|
|
mOVInfo.src_rect.y = tmp;
|
|
swapOVRotWidthHeight();
|
|
}
|
|
break;
|
|
}
|
|
case HAL_TRANSFORM_ROT_180:
|
|
{
|
|
if (val == MDP_ROT_270) {
|
|
int tmp = mOVInfo.src_rect.y;
|
|
mOVInfo.src_rect.y = mOVInfo.src.width -
|
|
(mOVInfo.src_rect.x + mOVInfo.src_rect.w);
|
|
mOVInfo.src_rect.x = tmp;
|
|
swapOVRotWidthHeight();
|
|
}
|
|
else if (val == MDP_ROT_90) {
|
|
int tmp = mOVInfo.src_rect.x;
|
|
mOVInfo.src_rect.x = mOVInfo.src.height - (
|
|
mOVInfo.src_rect.y + mOVInfo.src_rect.h);
|
|
mOVInfo.src_rect.y = tmp;
|
|
swapOVRotWidthHeight();
|
|
}
|
|
break;
|
|
}
|
|
case HAL_TRANSFORM_ROT_270:
|
|
{
|
|
if (val == MDP_ROT_90) {
|
|
mOVInfo.src_rect.y = mOVInfo.src.height -
|
|
(mOVInfo.src_rect.y + mOVInfo.src_rect.h);
|
|
mOVInfo.src_rect.x = mOVInfo.src.width -
|
|
(mOVInfo.src_rect.x + mOVInfo.src_rect.w);
|
|
}
|
|
else if (val == MDP_ROT_NOP || val == MDP_ROT_180) {
|
|
int tmp = mOVInfo.src_rect.y;
|
|
mOVInfo.src_rect.y = mOVInfo.src.width - (
|
|
mOVInfo.src_rect.x + mOVInfo.src_rect.w);
|
|
mOVInfo.src_rect.x = tmp;
|
|
swapOVRotWidthHeight();
|
|
}
|
|
break;
|
|
}
|
|
default: return false;
|
|
}
|
|
|
|
int mdp_rotation = get_mdp_orientation(rot);
|
|
if (mdp_rotation == -1)
|
|
return false;
|
|
|
|
mOVInfo.user_data[0] = mdp_rotation;
|
|
mRotInfo.rotations = mOVInfo.user_data[0];
|
|
|
|
/* Rotator always outputs non-tiled formats.
|
|
If rotator is used, set Overlay input to non-tiled
|
|
Else, overlay input remains tiled */
|
|
if (mOVInfo.user_data[0]) {
|
|
mOVInfo.src.format = get_rot_output_format(mRotInfo.src.format);
|
|
mRotInfo.enable = 1;
|
|
}
|
|
else {
|
|
//We can switch between rotator ON and OFF. Reset overlay
|
|
//i/p format whenever this happens
|
|
if(mRotInfo.dst.format == mOVInfo.src.format)
|
|
mOVInfo.src.format = mRotInfo.src.format;
|
|
mRotInfo.enable = 0;
|
|
//Always enable rotation for UI mirror usecase
|
|
if(mUIChannel)
|
|
mRotInfo.enable = 1;
|
|
}
|
|
|
|
if (ioctl(mRotFD, MSM_ROTATOR_IOCTL_START, &mRotInfo)) {
|
|
reportError("setTransform, rotator start failed");
|
|
return false;
|
|
}
|
|
|
|
if ((mOVInfo.user_data[0] == MDP_ROT_90) ||
|
|
(mOVInfo.user_data[0] == MDP_ROT_270))
|
|
mOVInfo.flags |= MDP_SOURCE_ROTATED_90;
|
|
else
|
|
mOVInfo.flags &= ~MDP_SOURCE_ROTATED_90;
|
|
|
|
if (ioctl(mFD, MSMFB_OVERLAY_SET, &mOVInfo)) {
|
|
reportError("setTransform, overlay set failed");
|
|
dump(mOVInfo);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool OverlayControlChannel::getPosition(int& x, int& y,
|
|
uint32_t& w, uint32_t& h) {
|
|
if (!isChannelUP())
|
|
return false;
|
|
//mOVInfo has the current Overlay Position
|
|
x = mOVInfo.dst_rect.x;
|
|
y = mOVInfo.dst_rect.y;
|
|
w = mOVInfo.dst_rect.w;
|
|
h = mOVInfo.dst_rect.h;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool OverlayControlChannel::getOrientation(int& orientation) const {
|
|
if (!isChannelUP())
|
|
return false;
|
|
// mOVInfo has the current orientation
|
|
orientation = mOVInfo.user_data[0];
|
|
return true;
|
|
}
|
|
bool OverlayControlChannel::getOvSessionID(int& sessionID) const {
|
|
if (!isChannelUP())
|
|
return false;
|
|
sessionID = mOVInfo.id;
|
|
return true;
|
|
}
|
|
|
|
bool OverlayControlChannel::getRotSessionID(int& sessionID) const {
|
|
if (!isChannelUP())
|
|
return false;
|
|
sessionID = mRotInfo.session_id;
|
|
return true;
|
|
}
|
|
|
|
bool OverlayControlChannel::getSize(int& size) const {
|
|
if (!isChannelUP())
|
|
return false;
|
|
size = mSize;
|
|
return true;
|
|
}
|
|
|
|
OverlayDataChannel::OverlayDataChannel() : mNoRot(false), mFD(-1), mRotFD(-1),
|
|
mPmemFD(-1), mPmemAddr(0), mUpdateDataChannel(false)
|
|
{
|
|
//XXX: getInstance(false) implies that it should only
|
|
// use the kernel allocator. Change it to something
|
|
// more descriptive later.
|
|
mAlloc = gralloc::IAllocController::getInstance(false);
|
|
}
|
|
|
|
OverlayDataChannel::~OverlayDataChannel() {
|
|
closeDataChannel();
|
|
}
|
|
|
|
bool OverlayDataChannel::startDataChannel(
|
|
const OverlayControlChannel& objOvCtrlChannel,
|
|
int fbnum, bool norot, bool secure, bool uichannel,
|
|
int num_buffers) {
|
|
int ovid, rotid, size;
|
|
mNoRot = norot;
|
|
mSecure = secure;
|
|
memset(&mOvData, 0, sizeof(mOvData));
|
|
memset(&mOvDataRot, 0, sizeof(mOvDataRot));
|
|
memset(&mRotData, 0, sizeof(mRotData));
|
|
if (objOvCtrlChannel.getOvSessionID(ovid) &&
|
|
objOvCtrlChannel.getRotSessionID(rotid) &&
|
|
objOvCtrlChannel.getSize(size)) {
|
|
return startDataChannel(ovid, rotid, size, fbnum,
|
|
norot, uichannel, num_buffers);
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
bool OverlayDataChannel::openDevices(int fbnum, bool uichannel, int num_buffers) {
|
|
if (fbnum < 0)
|
|
return false;
|
|
char dev_name[64];
|
|
snprintf(dev_name, 64, FB_DEVICE_TEMPLATE, fbnum);
|
|
|
|
mFD = open(dev_name, O_RDWR, 0);
|
|
if (mFD < 0) {
|
|
reportError("Cant open framebuffer ");
|
|
return false;
|
|
}
|
|
if (!mNoRot) {
|
|
mRotFD = open("/dev/msm_rotator", O_RDWR, 0);
|
|
if (mRotFD < 0) {
|
|
reportError("Cant open rotator device");
|
|
close(mFD);
|
|
mFD = -1;
|
|
return false;
|
|
}
|
|
|
|
return mapRotatorMemory(num_buffers, uichannel, NEW_REQUEST);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool OverlayDataChannel::mapRotatorMemory(int num_buffers, bool uiChannel, int requestType)
|
|
{
|
|
mPmemAddr = MAP_FAILED;
|
|
|
|
alloc_data data;
|
|
data.base = 0;
|
|
data.fd = -1;
|
|
data.offset = 0;
|
|
data.size = mPmemOffset * num_buffers;
|
|
data.align = getpagesize();
|
|
data.uncached = true;
|
|
|
|
int allocFlags = GRALLOC_USAGE_PRIVATE_MM_HEAP |
|
|
GRALLOC_USAGE_PRIVATE_WRITEBACK_HEAP|
|
|
GRALLOC_USAGE_PRIVATE_DO_NOT_MAP;
|
|
|
|
if(mSecure) {
|
|
allocFlags |= GRALLOC_USAGE_PROTECTED;
|
|
} else {
|
|
allocFlags |= GRALLOC_USAGE_PRIVATE_ADSP_HEAP |
|
|
GRALLOC_USAGE_PRIVATE_IOMMU_HEAP;
|
|
if((requestType == NEW_REQUEST) && !uiChannel)
|
|
allocFlags |= GRALLOC_USAGE_PRIVATE_SMI_HEAP;
|
|
}
|
|
|
|
int err = mAlloc->allocate(data, allocFlags, 0);
|
|
if(err) {
|
|
reportError("Cant allocate rotatory memory");
|
|
close(mFD);
|
|
mFD = -1;
|
|
close(mRotFD);
|
|
mRotFD = -1;
|
|
return false;
|
|
}
|
|
mPmemFD = data.fd;
|
|
mPmemAddr = data.base;
|
|
mBufferType = data.allocType;
|
|
#if 0
|
|
// Set this flag if source memory is fb
|
|
if(uiChannel)
|
|
mRotData.src.flags |= MDP_MEMORY_ID_TYPE_FB;
|
|
#endif
|
|
mOvDataRot.data.memory_id = mPmemFD;
|
|
mRotData.dst.memory_id = mPmemFD;
|
|
mRotData.dst.offset = 0;
|
|
mNumBuffers = num_buffers;
|
|
mCurrentItem = 0;
|
|
for (int i = 0; i < num_buffers; i++)
|
|
mRotOffset[i] = i * mPmemOffset;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool OverlayDataChannel::updateDataChannel(int size) {
|
|
mUpdateDataChannel = true;
|
|
mNewPmemOffset = size;
|
|
return true;
|
|
}
|
|
|
|
bool OverlayDataChannel::startDataChannel(int ovid, int rotid, int size,
|
|
int fbnum, bool norot,
|
|
bool uichannel, int num_buffers) {
|
|
memset(&mOvData, 0, sizeof(mOvData));
|
|
memset(&mOvDataRot, 0, sizeof(mOvDataRot));
|
|
memset(&mRotData, 0, sizeof(mRotData));
|
|
mNoRot = norot;
|
|
mOvData.data.memory_id = -1;
|
|
mOvData.id = ovid;
|
|
mOvDataRot = mOvData;
|
|
mPmemOffset = size;
|
|
mRotData.session_id = rotid;
|
|
mNumBuffers = 0;
|
|
mCurrentItem = 0;
|
|
|
|
return openDevices(fbnum, uichannel, num_buffers);
|
|
}
|
|
|
|
bool OverlayDataChannel::closeDataChannel() {
|
|
if (!isChannelUP())
|
|
return true;
|
|
|
|
if (!mNoRot && mRotFD > 0) {
|
|
sp<IMemAlloc> memalloc = mAlloc->getAllocator(mBufferType);
|
|
memalloc->free_buffer(mPmemAddr, mPmemOffset * mNumBuffers, 0, mPmemFD);
|
|
close(mPmemFD);
|
|
mPmemFD = -1;
|
|
close(mRotFD);
|
|
mRotFD = -1;
|
|
}
|
|
close(mFD);
|
|
mFD = -1;
|
|
memset(&mOvData, 0, sizeof(mOvData));
|
|
memset(&mOvDataRot, 0, sizeof(mOvDataRot));
|
|
memset(&mRotData, 0, sizeof(mRotData));
|
|
|
|
mNumBuffers = 0;
|
|
mCurrentItem = 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool OverlayDataChannel::setFd(int fd) {
|
|
mOvData.data.memory_id = fd;
|
|
return true;
|
|
}
|
|
|
|
bool OverlayDataChannel::queueBuffer(uint32_t offset) {
|
|
if ((!isChannelUP()) || mOvData.data.memory_id < 0) {
|
|
reportError("QueueBuffer failed, either channel is not set or no file descriptor to read from");
|
|
return false;
|
|
}
|
|
|
|
int oldPmemFD = -1;
|
|
void* oldPmemAddr = MAP_FAILED;
|
|
uint32_t oldPmemOffset = -1;
|
|
bool result;
|
|
if (!mNoRot) {
|
|
if (mUpdateDataChannel) {
|
|
oldPmemFD = mPmemFD;
|
|
oldPmemAddr = mPmemAddr;
|
|
oldPmemOffset = mPmemOffset;
|
|
mPmemOffset = mNewPmemOffset;
|
|
mNewPmemOffset = -1;
|
|
// Map the new PMEM memory
|
|
result = mapRotatorMemory(mNumBuffers, 0, UPDATE_REQUEST);
|
|
if (!result) {
|
|
LOGE("queueBuffer: mapRotatorMemory failed");
|
|
return false;
|
|
}
|
|
mUpdateDataChannel = false;
|
|
}
|
|
}
|
|
|
|
result = queue(offset);
|
|
|
|
// Unmap the old PMEM memory after the queueBuffer has returned
|
|
if (oldPmemFD != -1 && oldPmemAddr != MAP_FAILED) {
|
|
sp<IMemAlloc> memalloc = mAlloc->getAllocator(mBufferType);
|
|
memalloc->free_buffer(oldPmemAddr, oldPmemOffset * mNumBuffers, 0, oldPmemFD);
|
|
oldPmemFD = -1;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool OverlayDataChannel::queue(uint32_t offset) {
|
|
msmfb_overlay_data *odPtr;
|
|
mOvData.data.offset = offset;
|
|
odPtr = &mOvData;
|
|
if (!mNoRot) {
|
|
mRotData.src.memory_id = mOvData.data.memory_id;
|
|
mRotData.src.offset = offset;
|
|
mRotData.dst.offset = (mRotData.dst.offset) ? 0 : mPmemOffset;
|
|
mRotData.dst.offset = mRotOffset[mCurrentItem];
|
|
mCurrentItem = (mCurrentItem + 1) % mNumBuffers;
|
|
|
|
int result = ioctl(mRotFD,
|
|
MSM_ROTATOR_IOCTL_ROTATE, &mRotData);
|
|
|
|
if (!result) {
|
|
mOvDataRot.data.offset = (uint32_t) mRotData.dst.offset;
|
|
odPtr = &mOvDataRot;
|
|
}
|
|
}
|
|
|
|
if (ioctl(mFD, MSMFB_OVERLAY_PLAY, odPtr)) {
|
|
reportError("overlay play failed.");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
#if 0
|
|
bool OverlayDataChannel::waitForHdmiVsync() {
|
|
if (!isChannelUP()) {
|
|
LOGE("%s: channel not up", __FUNCTION__);
|
|
return false;
|
|
}
|
|
if (ioctl(mFD, MSMFB_OVERLAY_PLAY_WAIT, &mOvData)) {
|
|
LOGE("%s: MSMFB_OVERLAY_PLAY_WAIT failed", __FUNCTION__);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
#endif
|
|
bool OverlayDataChannel::getCropS3D(overlay_rect *inRect, int channel, int format,
|
|
overlay_rect *rect) {
|
|
// for the 3D usecase extract channels from a frame
|
|
switch (format & INPUT_MASK_3D) {
|
|
case HAL_3D_IN_SIDE_BY_SIDE_L_R:
|
|
if(channel == 0) {
|
|
rect->x = 0;
|
|
rect->y = 0;
|
|
rect->w = inRect->w/2;
|
|
rect->h = inRect->h;
|
|
} else {
|
|
rect->x = inRect->w/2;
|
|
rect->y = 0;
|
|
rect->w = inRect->w/2;
|
|
rect->h = inRect->h;
|
|
}
|
|
break;
|
|
case HAL_3D_IN_SIDE_BY_SIDE_R_L:
|
|
if(channel == 1) {
|
|
rect->x = 0;
|
|
rect->y = 0;
|
|
rect->w = inRect->w/2;
|
|
rect->h = inRect->h;
|
|
} else {
|
|
rect->x = inRect->w/2;
|
|
rect->y = 0;
|
|
rect->w = inRect->w/2;
|
|
rect->h = inRect->h;
|
|
}
|
|
break;
|
|
case HAL_3D_IN_TOP_BOTTOM:
|
|
if(channel == 0) {
|
|
rect->x = 0;
|
|
rect->y = 0;
|
|
rect->w = inRect->w;
|
|
rect->h = inRect->h/2;
|
|
} else {
|
|
rect->x = 0;
|
|
rect->y = inRect->h/2;
|
|
rect->w = inRect->w;
|
|
rect->h = inRect->h/2;
|
|
}
|
|
break;
|
|
case HAL_3D_IN_INTERLEAVE:
|
|
break;
|
|
default:
|
|
reportError("Unsupported 3D format...");
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool OverlayDataChannel::setCrop(uint32_t x, uint32_t y, uint32_t w, uint32_t h) {
|
|
if (!isChannelUP()) {
|
|
reportError("Channel not set");
|
|
return false;
|
|
}
|
|
|
|
mdp_overlay ov;
|
|
ov.id = mOvData.id;
|
|
if (ioctl(mFD, MSMFB_OVERLAY_GET, &ov)) {
|
|
reportError("setCrop, overlay GET failed");
|
|
return false;
|
|
}
|
|
|
|
if ((ov.user_data[0] == MDP_ROT_90) ||
|
|
(ov.user_data[0] == (MDP_ROT_90 | MDP_FLIP_UD)) ||
|
|
(ov.user_data[0] == (MDP_ROT_90 | MDP_FLIP_LR))){
|
|
if (ov.src.width < (y + h))
|
|
return false;
|
|
|
|
uint32_t tmp = x;
|
|
x = ov.src.width - (y + h);
|
|
y = tmp;
|
|
|
|
tmp = w;
|
|
w = h;
|
|
h = tmp;
|
|
}
|
|
else if (ov.user_data[0] == MDP_ROT_270) {
|
|
if (ov.src.height < (x + w))
|
|
return false;
|
|
|
|
uint32_t tmp = y;
|
|
y = ov.src.height - (x + w);
|
|
x = tmp;
|
|
|
|
tmp = w;
|
|
w = h;
|
|
h = tmp;
|
|
}
|
|
else if(ov.user_data[0] == MDP_ROT_180) {
|
|
if ((ov.src.height < (y + h)) || (ov.src.width < ( x + w)))
|
|
return false;
|
|
|
|
x = ov.src.width - (x + w);
|
|
y = ov.src.height - (y + h);
|
|
}
|
|
|
|
|
|
if ((ov.src_rect.x == x) &&
|
|
(ov.src_rect.y == y) &&
|
|
(ov.src_rect.w == w) &&
|
|
(ov.src_rect.h == h))
|
|
return true;
|
|
|
|
normalize_crop(x, w);
|
|
normalize_crop(y, h);
|
|
|
|
ov.src_rect.x = x;
|
|
ov.src_rect.y = y;
|
|
ov.src_rect.w = w;
|
|
ov.src_rect.h = h;
|
|
|
|
/* Scaling of upto a max of 8 times supported */
|
|
if(ov.dst_rect.w >(ov.src_rect.w * HW_OVERLAY_MAGNIFICATION_LIMIT)){
|
|
ov.dst_rect.w = HW_OVERLAY_MAGNIFICATION_LIMIT * ov.src_rect.w;
|
|
}
|
|
if(ov.dst_rect.h >(ov.src_rect.h * HW_OVERLAY_MAGNIFICATION_LIMIT)) {
|
|
ov.dst_rect.h = HW_OVERLAY_MAGNIFICATION_LIMIT * ov.src_rect.h;
|
|
}
|
|
if (ioctl(mFD, MSMFB_OVERLAY_SET, &ov)) {
|
|
reportError("setCrop, overlay set error");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/* setVisualParam can be called to set the configuration value of a post
|
|
* processing feature (HUE,SATURATION,BRIGHTNESS,CONTRAST,SMOOTHING/SHARPENING)
|
|
* for the first 4, the setting will stay set until the parameter is changed
|
|
* by another call to setVisualParam with that same paramType */
|
|
void Overlay::setVisualParam(int8_t paramType, float paramValue) {
|
|
switch (mState) {
|
|
case OV_UI_MIRROR_TV:
|
|
case OV_2D_VIDEO_ON_PANEL:
|
|
case OV_3D_VIDEO_2D_PANEL:
|
|
// set the parameter value for the given parameter type.
|
|
if(!objOvCtrlChannel[VG0_PIPE].setVisualParam(paramType, paramValue)) {
|
|
LOGE("Failed to set param %d for value %f", paramType, paramValue);
|
|
}
|
|
break;
|
|
case OV_2D_VIDEO_ON_TV:
|
|
case OV_3D_VIDEO_3D_PANEL:
|
|
case OV_3D_VIDEO_2D_TV:
|
|
case OV_3D_VIDEO_3D_TV:
|
|
for (int i=0; i<NUM_CHANNELS; i++) {
|
|
//setting the value for the given parameter on each pipe (i.e. for
|
|
//both video pipes)
|
|
if(!objOvCtrlChannel[i].setVisualParam(paramType, paramValue)) {
|
|
LOGE("Failed to set param %d for value %f", paramType, paramValue);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Finalizes the parameter value in the hsic_cfg structure*/
|
|
int OverlayControlChannel::commitVisualParam(int8_t paramType, float paramValue) {
|
|
#ifdef USES_POST_PROCESSING
|
|
switch(paramType) {
|
|
case SET_HUE:
|
|
//API expects param within range -180 - 180
|
|
CAP_RANGE(paramValue, HUE_RANGE, -HUE_RANGE);
|
|
hsic_cfg.hue = (int32_t) paramValue;
|
|
break;
|
|
case SET_BRIGHTNESS:
|
|
//API expects param within range -255 - 255
|
|
CAP_RANGE(paramValue, BRIGHTNESS_RANGE, -BRIGHTNESS_RANGE);
|
|
hsic_cfg.intensity = (int32_t) paramValue;
|
|
break;
|
|
case SET_SATURATION:
|
|
//API expects param within range -1 - 1
|
|
CAP_RANGE(paramValue, CON_SAT_RANGE, -CON_SAT_RANGE);
|
|
hsic_cfg.sat = paramValue;
|
|
break;
|
|
case SET_CONTRAST:
|
|
//API expects param within range -1 - 1
|
|
CAP_RANGE(paramValue, CON_SAT_RANGE, -CON_SAT_RANGE);
|
|
hsic_cfg.contrast = paramValue;
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
return 0;
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
/* Converts paramValue to the expected range for each paramType, */
|
|
bool OverlayControlChannel::setVisualParam(int8_t paramType, float paramValue)
|
|
{
|
|
if (!isChannelUP()) {
|
|
LOGE("%s: Channel not set", __FUNCTION__);
|
|
return false;
|
|
}
|
|
|
|
bool setFlag = false;
|
|
|
|
//Sharpness values range from -128 to 127
|
|
//Integer values must be converted accordingly
|
|
|
|
int8_t value;
|
|
if (paramType == SET_SHARPNESS) {
|
|
//binding paramValue to the limits of its range.
|
|
CAP_RANGE(paramValue, SHARPNESS_RANGE, -SHARPNESS_RANGE);
|
|
value = paramValue * NUM_SHARPNESS_VALS - (NUM_SHARPNESS_VALS / 2);
|
|
}
|
|
|
|
uint32_t block = MDP_BLOCK_MAX;
|
|
|
|
//tranlate mOVInfo.id into block type for pp_conv
|
|
switch(mOVInfo.id) {
|
|
case 3:
|
|
// 3 is the pipe_ndx given when OVERLAY_PIPE_VG1 is used
|
|
block = MDP_BLOCK_VG_1;
|
|
break;
|
|
case 4:
|
|
// 4 is the pipe_ndx given when OVERLAY_PIPE_VG2 is used
|
|
block = MDP_BLOCK_VG_2;
|
|
break;
|
|
default:
|
|
LOGE("%s: Invalid HSIC overlay id",__FUNCTION__);
|
|
}
|
|
|
|
//save the paramValue to hsic_cfg
|
|
commitVisualParam(paramType, paramValue);
|
|
#ifdef USES_POST_PROCESSING
|
|
//calling our user space library to configure the post processing color
|
|
//conversion (does Hue, Saturation, Brightness, and Contrast adjustment)
|
|
display_pp_conv_set_cfg(block, &hsic_cfg);
|
|
#endif
|
|
mdp_overlay overlay;
|
|
|
|
switch(paramType) {
|
|
case SET_NONE:
|
|
return true;
|
|
case SET_SHARPNESS:
|
|
if (ioctl(mFD, MSMFB_OVERLAY_GET, &overlay)) {
|
|
reportError("setVisualParam, overlay GET failed");
|
|
return false;
|
|
}
|
|
if (overlay.dpp.sharp_strength != value) {
|
|
mOVInfo.flags |= MDP_SHARPENING;
|
|
mOVInfo.dpp.sharp_strength = value;
|
|
setFlag = true;
|
|
}
|
|
break;
|
|
case RESET_ALL:
|
|
//set all visual params to a default value
|
|
//passed in from the app
|
|
mOVInfo.flags |= MDP_SHARPENING;
|
|
mOVInfo.dpp.sharp_strength = value;
|
|
setFlag = true;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
if (setFlag) {
|
|
if (ioctl(mFD, MSMFB_OVERLAY_SET, &mOVInfo)) {
|
|
reportError("setVisualParam, overlay set failed");
|
|
dump(mOVInfo);
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|