/* * Copyright (C) 2008 The Android Open Source Project * Copyright (c) 2011-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 "overlayLibUI.h" #include "gralloc_priv.h" #define LOG_TAG "OverlayUI" using android::sp; using gralloc::IMemAlloc; using gralloc::alloc_data; namespace { /* helper functions */ void swapOVRotWidthHeight(msm_rotator_img_info& rotInfo, mdp_overlay& ovInfo) { int srcWidth = ovInfo.src.width; ovInfo.src.width = ovInfo.src.height; ovInfo.src.height = srcWidth; int srcRectWidth = ovInfo.src_rect.w; ovInfo.src_rect.w = ovInfo.src_rect.h; ovInfo.src_rect.h = srcRectWidth; int dstWidth = rotInfo.dst.width; rotInfo.dst.width = rotInfo.dst.height; rotInfo.dst.height = dstWidth; } bool isRGBType(int format) { bool ret = false; switch(format) { case MDP_RGBA_8888: case MDP_BGRA_8888: case MDP_RGBX_8888: case MDP_RGB_565: ret = true; break; default: ret = false; break; } return ret; } int getRGBBpp(int format) { int ret = -1; switch(format) { case MDP_RGBA_8888: case MDP_BGRA_8888: case MDP_RGBX_8888: ret = 4; break; case MDP_RGB_565: ret = 2; break; default: ret = -1; break; } return ret; } bool turnOFFVSync() { static int swapIntervalPropVal = -1; if (swapIntervalPropVal == -1) { char pval[PROPERTY_VALUE_MAX]; property_get("debug.gr.swapinterval", pval, "1"); swapIntervalPropVal = atoi(pval); } return (swapIntervalPropVal == 0); } }; namespace overlay { status_t Display::openDisplay(int fbnum) { if (mFD != NO_INIT) return NO_ERROR; status_t ret = NO_INIT; char dev_name[64]; snprintf(dev_name, 64, FB_DEVICE_TEMPLATE, fbnum); mFD = open(dev_name, O_RDWR, 0); if (mFD < 0) { LOGE("Failed to open FB %d", fbnum); return ret; } fb_var_screeninfo vinfo; if (ioctl(mFD, FBIOGET_VSCREENINFO, &vinfo)) { LOGE("FBIOGET_VSCREENINFO on failed on FB %d", fbnum); close(mFD); mFD = NO_INIT; return ret; } mFBWidth = vinfo.xres; mFBHeight = vinfo.yres; mFBBpp = vinfo.bits_per_pixel; ret = NO_ERROR; return ret; } void Display::closeDisplay() { close(mFD); mFD = NO_INIT; } Rotator::Rotator() : mFD(NO_INIT), mSessionID(NO_INIT), mPmemFD(NO_INIT) { mAlloc = gralloc::IAllocController::getInstance(false); } Rotator::~Rotator() { closeRotSession(); } status_t Rotator::startRotSession(msm_rotator_img_info& rotInfo, int size, int numBuffers) { status_t ret = NO_ERROR; if (mSessionID == NO_INIT && mFD == NO_INIT) { mNumBuffers = numBuffers; mFD = open("/dev/msm_rotator", O_RDWR, 0); if (mFD < 0) { LOGE("Couldnt open rotator device"); return NO_INIT; } if (ioctl(mFD, MSM_ROTATOR_IOCTL_START, &rotInfo)) { close(mFD); mFD = NO_INIT; return NO_INIT; } mSessionID = rotInfo.session_id; alloc_data data; data.base = 0; data.fd = -1; data.offset = 0; data.size = mSize * mNumBuffers; data.align = getpagesize(); data.uncached = true; int allocFlags = GRALLOC_USAGE_PRIVATE_MM_HEAP | GRALLOC_USAGE_PRIVATE_WRITEBACK_HEAP | GRALLOC_USAGE_PRIVATE_ADSP_HEAP | GRALLOC_USAGE_PRIVATE_IOMMU_HEAP | GRALLOC_USAGE_PRIVATE_SMI_HEAP | GRALLOC_USAGE_PRIVATE_DO_NOT_MAP; int err = mAlloc->allocate(data, allocFlags, 0); if(err) { LOGE("%s: Can't allocate rotator memory", __func__); closeRotSession(); return NO_INIT; } mPmemFD = data.fd; mPmemAddr = data.base; mBufferType = data.allocType; mCurrentItem = 0; for (int i = 0; i < mNumBuffers; i++) mRotOffset[i] = i * mSize; ret = NO_ERROR; } return ret; } status_t Rotator::closeRotSession() { if (mSessionID != NO_INIT && mFD != NO_INIT) { ioctl(mFD, MSM_ROTATOR_IOCTL_FINISH, &mSessionID); close(mFD); if (NO_INIT != mPmemFD) { sp memalloc = mAlloc->getAllocator(mBufferType); memalloc->free_buffer(mPmemAddr, mSize * mNumBuffers, 0, mPmemFD); close(mPmemFD); } } mFD = NO_INIT; mSessionID = NO_INIT; mPmemFD = NO_INIT; mPmemAddr = MAP_FAILED; return NO_ERROR; } status_t Rotator::rotateBuffer(msm_rotator_data_info& rotData) { status_t ret = NO_INIT; if (mSessionID != NO_INIT) { rotData.dst.memory_id = mPmemFD; rotData.dst.offset = mRotOffset[mCurrentItem]; rotData.session_id = mSessionID; mCurrentItem = (mCurrentItem + 1) % mNumBuffers; if (ioctl(mFD, MSM_ROTATOR_IOCTL_ROTATE, &rotData)) { LOGE("Rotator failed to rotate"); return BAD_VALUE; } return NO_ERROR; } return ret; } //===================== OverlayUI =================// OverlayUI::OverlayUI() : mChannelState(CLOSED), mOrientation(NO_INIT), mFBNum(NO_INIT), mZorder(NO_INIT), mWaitForVsync(false), mIsFg(false), mSessionID(NO_INIT) { memset(&mOvInfo, 0, sizeof(mOvInfo)); memset(&mRotInfo, 0, sizeof(mRotInfo)); } OverlayUI::~OverlayUI() { closeChannel(); } void OverlayUI::setSource(const overlay_buffer_info& info, int orientation) { status_t ret = NO_INIT; int format3D = FORMAT_3D(info.format); int colorFormat = COLOR_FORMAT(info.format); int format = get_mdp_format(colorFormat); if (format3D || !isRGBType(format)) { LOGE("%s: Unsupported format", __func__); return; } mSource.width = info.width; mSource.height = info.height; mSource.format = format; mSource.size = info.size; mOrientation = orientation; setupOvRotInfo(); } void OverlayUI::setDisplayParams(int fbNum, bool waitForVsync, bool isFg, int zorder, bool isVGPipe) { int flags = 0; mFBNum = fbNum; mOvInfo.is_fg = isFg; if(false == waitForVsync) flags |= MDP_OV_PLAY_NOWAIT; else flags &= ~MDP_OV_PLAY_NOWAIT; if(isVGPipe) flags |= MDP_OV_PIPE_SHARE; else flags &= ~MDP_OV_PIPE_SHARE; if (turnOFFVSync()) flags |= MDP_OV_PLAY_NOWAIT; mOvInfo.flags = flags; mOvInfo.z_order = zorder; mobjDisplay.openDisplay(mFBNum); } void OverlayUI::setPosition(int x, int y, int w, int h) { mOvInfo.dst_rect.x = x; mOvInfo.dst_rect.y = y; mOvInfo.dst_rect.w = w; mOvInfo.dst_rect.h = h; } void OverlayUI::setCrop(int x, int y, int w, int h) { mOvInfo.src_rect.x = x; mOvInfo.src_rect.y = y; mOvInfo.src_rect.w = w; mOvInfo.src_rect.h = h; } void OverlayUI::setupOvRotInfo() { int w = mSource.width; int h = mSource.height; int format = mSource.format; int srcw = (w + 31) & ~31; int srch = (h + 31) & ~31; mOvInfo.src.width = srcw; mOvInfo.src.height = srch; mOvInfo.src.format = format; mOvInfo.src_rect.w = w; mOvInfo.src_rect.h = h; mOvInfo.alpha = 0xff; mOvInfo.transp_mask = 0xffffffff; mRotInfo.src.format = format; mRotInfo.dst.format = format; mRotInfo.src.width = srcw; mRotInfo.src.height = srch; mRotInfo.src_rect.w = srcw; mRotInfo.src_rect.h = srch; mRotInfo.dst.width = srcw; mRotInfo.dst.height = srch; int rot = mOrientation; switch(rot) { case 0: case HAL_TRANSFORM_FLIP_H: case HAL_TRANSFORM_FLIP_V: rot = 0; break; case HAL_TRANSFORM_ROT_90: case (HAL_TRANSFORM_ROT_90|HAL_TRANSFORM_FLIP_H): case (HAL_TRANSFORM_ROT_90|HAL_TRANSFORM_FLIP_V): { 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(mRotInfo, mOvInfo); rot = HAL_TRANSFORM_ROT_90; break; } case HAL_TRANSFORM_ROT_180: break; case HAL_TRANSFORM_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(mRotInfo, mOvInfo); break; } default: break; } int mdp_rotation = overlay::get_mdp_orientation(rot); if (mdp_rotation < 0) mdp_rotation = 0; mOvInfo.user_data[0] = mdp_rotation; mRotInfo.rotations = mOvInfo.user_data[0]; if (mdp_rotation) mRotInfo.enable = 1; mOvInfo.dst_rect.w = mOvInfo.src_rect.w; mOvInfo.dst_rect.h = mOvInfo.src_rect.h; } status_t OverlayUI::commit() { status_t ret = BAD_VALUE; if(mChannelState != UP) mOvInfo.id = MSMFB_NEW_REQUEST; ret = startOVSession(); if (ret == NO_ERROR && mOrientation) { ret = mobjRotator.startRotSession(mRotInfo, mSource.size); } if (ret == NO_ERROR) { mChannelState = UP; } else { LOGE("start channel failed."); } return ret; } status_t OverlayUI::closeChannel() { if( mChannelState != UP ) { return NO_ERROR; } if(NO_ERROR != closeOVSession()) { LOGE("%s: closeOVSession() failed.", __FUNCTION__); return BAD_VALUE; } if(NO_ERROR != mobjRotator.closeRotSession()) { LOGE("%s: closeRotSession() failed.", __FUNCTION__); return BAD_VALUE; } mChannelState = CLOSED; memset(&mOvInfo, 0, sizeof(mOvInfo)); memset(&mRotInfo, 0, sizeof(mRotInfo)); return NO_ERROR; } status_t OverlayUI::startOVSession() { status_t ret = NO_INIT; mdp_overlay ovInfo = mOvInfo; if (ioctl(mobjDisplay.getFD(), MSMFB_OVERLAY_SET, &ovInfo)) { LOGE("%s: Overlay set failed", __FUNCTION__); ret = BAD_VALUE; } else { mSessionID = ovInfo.id; mOvInfo = ovInfo; ret = NO_ERROR; } return ret; } status_t OverlayUI::closeOVSession() { status_t ret = NO_ERROR; int err = 0; if(err = ioctl(mobjDisplay.getFD(), MSMFB_OVERLAY_UNSET, &mSessionID)) { LOGE("%s: MSMFB_OVERLAY_UNSET failed. (%d)", __FUNCTION__, err); ret = BAD_VALUE; } else { mobjDisplay.closeDisplay(); mSessionID = NO_INIT; } return ret; } status_t OverlayUI::queueBuffer(buffer_handle_t buffer) { status_t ret = NO_INIT; if (mChannelState != UP) return ret; msmfb_overlay_data ovData; memset(&ovData, 0, sizeof(ovData)); private_handle_t const* hnd = reinterpret_cast (buffer); ovData.data.memory_id = hnd->fd; ovData.data.offset = hnd->offset; if (mOrientation) { msm_rotator_data_info rotData; memset(&rotData, 0, sizeof(rotData)); rotData.src.memory_id = hnd->fd; rotData.src.offset = hnd->offset; if (mobjRotator.rotateBuffer(rotData) != NO_ERROR) { LOGE("Rotator failed.. "); return BAD_VALUE; } ovData.data.memory_id = rotData.dst.memory_id; ovData.data.offset = rotData.dst.offset; } ovData.id = mSessionID; if (ioctl(mobjDisplay.getFD(), MSMFB_OVERLAY_PLAY, &ovData)) { LOGE("Queuebuffer failed "); return BAD_VALUE; } return NO_ERROR; } };