C++程序  |  835行  |  24.6 KB

/*
// Copyright (c) 2014 Intel Corporation 
//
// 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 <math.h>
#include <HwcTrace.h>
#include <Drm.h>
#include <Hwcomposer.h>
#include <anniedale/AnnOverlayPlane.h>
#include <tangier/TngGrallocBuffer.h>

// FIXME: remove it
#include <OMX_IVCommon.h>
#include <OMX_IntelVideoExt.h>

namespace android {
namespace intel {

AnnOverlayPlane::AnnOverlayPlane(int index, int disp)
    : OverlayPlaneBase(index, disp),
      mRotationBufProvider(NULL),
      mRotationConfig(0),
      mZOrderConfig(0),
      mUseOverlayRotation(true)
{
    CTRACE();

    memset(&mContext, 0, sizeof(mContext));
}

AnnOverlayPlane::~AnnOverlayPlane()
{
    CTRACE();
}

void AnnOverlayPlane::setZOrderConfig(ZOrderConfig& zorderConfig,
                                            void *nativeConfig)
{
    long slot = (long)nativeConfig;

    CTRACE();

    switch (slot) {
    case 0:
        mZOrderConfig = 0;
        break;
    case 1:
        mZOrderConfig = (1 << 8);
        break;
    case 2:
        mZOrderConfig = (2 << 8);
        break;
    case 3:
        mZOrderConfig = (3 << 8);
        break;
    default:
        ETRACE("Invalid overlay plane zorder %ld", slot);
        return;
    }
}

bool AnnOverlayPlane::reset()
{
    OverlayPlaneBase::reset();
    if (mRotationBufProvider) {
        mRotationBufProvider->reset();
    }
    return true;
}

bool AnnOverlayPlane::enable()
{
    RETURN_FALSE_IF_NOT_INIT();

    // by default always use overlay rotation
    mUseOverlayRotation = true;

    if (mContext.ctx.ov_ctx.ovadd & (0x1 << 15))
        return true;

    mContext.ctx.ov_ctx.ovadd |= (0x1 << 15);

    // flush
    flush(PLANE_ENABLE);

    return true;
}

bool AnnOverlayPlane::disable()
{
    RETURN_FALSE_IF_NOT_INIT();

    if (!(mContext.ctx.ov_ctx.ovadd & (0x1 << 15)))
        return true;

    mContext.ctx.ov_ctx.ovadd &= ~(0x1 << 15);

    mContext.ctx.ov_ctx.ovadd &= ~(0x300);

    mContext.ctx.ov_ctx.ovadd |= mPipeConfig;

    // flush
    flush(PLANE_DISABLE);

    return true;
}

void AnnOverlayPlane::postFlip()
{
    // when using AnnOverlayPlane through AnnDisplayPlane as proxy, postFlip is never
    // called so mUpdateMasks is never reset.
    // When using AnnOverlayPlane directly, postFlip is invoked and mUpdateMasks is reset
    // post-flip.

    // need to check why mUpdateMasks = 0 causes video freeze.

    //DisplayPlane::postFlip();
}


void AnnOverlayPlane::resetBackBuffer(int buf)
{
    CTRACE();

    if (!mBackBuffer[buf] || !mBackBuffer[buf]->buf)
        return;

    OverlayBackBufferBlk *backBuffer = mBackBuffer[buf]->buf;

    memset(backBuffer, 0, sizeof(OverlayBackBufferBlk));

    // reset overlay
    backBuffer->OCLRC0 = (OVERLAY_INIT_CONTRAST << 18) |
                         (OVERLAY_INIT_BRIGHTNESS & 0xff);
    backBuffer->OCLRC1 = OVERLAY_INIT_SATURATION;
    backBuffer->DCLRKV = OVERLAY_INIT_COLORKEY;
    backBuffer->DCLRKM = OVERLAY_INIT_COLORKEYMASK;
    backBuffer->OCONFIG = 0;
    backBuffer->OCONFIG |= (0x1 << 27);
    // use 3 line buffers
    backBuffer->OCONFIG |= 0x1;
    backBuffer->SCHRKEN &= ~(0x7 << 24);
    backBuffer->SCHRKEN |= 0xff;
}

bool AnnOverlayPlane::bufferOffsetSetup(BufferMapper& mapper)
{
    CTRACE();

    OverlayBackBufferBlk *backBuffer = mBackBuffer[mCurrent]->buf;
    if (!backBuffer) {
        ETRACE("invalid back buffer");
        return false;
    }

    uint32_t format = mapper.getFormat();
    uint32_t gttOffsetInBytes = (mapper.getGttOffsetInPage(0) << 12);
    uint32_t yStride = mapper.getStride().yuv.yStride;
    uint32_t uvStride = mapper.getStride().yuv.uvStride;
    uint32_t w = mapper.getWidth();
    uint32_t h = mapper.getHeight();
    uint32_t srcX= mapper.getCrop().x;
    uint32_t srcY= mapper.getCrop().y;
    uint32_t ySurface, uSurface, vSurface;
    uint32_t yTileOffsetX, yTileOffsetY;
    uint32_t uTileOffsetX, uTileOffsetY;
    uint32_t vTileOffsetX, vTileOffsetY;

    // clear original format setting
    backBuffer->OCMD &= ~(0xf << 10);
    backBuffer->OCMD &= ~OVERLAY_MEMORY_LAYOUT_TILED;

    backBuffer->OBUF_0Y = 0;
    backBuffer->OBUF_0V = 0;
    backBuffer->OBUF_0U = 0;
    // Y/U/V plane must be 4k bytes aligned.
    ySurface = gttOffsetInBytes;
    if (mIsProtectedBuffer) {
        // temporary workaround until vsync event logic is corrected.
        // it seems that overlay buffer update and renderring can be overlapped,
        // as such encryption bit may be cleared during HW rendering
        ySurface |= 0x01;
    }

    switch(format) {
    case HAL_PIXEL_FORMAT_YV12:    // YV12
        vSurface = ySurface + yStride * h;
        uSurface = vSurface + uvStride * (h / 2);
        yTileOffsetX = srcX;
        yTileOffsetY = srcY;
        uTileOffsetX = srcX / 2;
        uTileOffsetY = srcY / 2;
        vTileOffsetX = uTileOffsetX;
        vTileOffsetY = uTileOffsetY;
        backBuffer->OCMD |= OVERLAY_FORMAT_PLANAR_YUV420;
        break;
    case HAL_PIXEL_FORMAT_I420:    // I420
        uSurface = ySurface + yStride * h;
        vSurface = uSurface + uvStride * (h / 2);
        yTileOffsetX = srcX;
        yTileOffsetY = srcY;
        uTileOffsetX = srcX / 2;
        uTileOffsetY = srcY / 2;
        vTileOffsetX = uTileOffsetX;
        vTileOffsetY = uTileOffsetY;
        backBuffer->OCMD |= OVERLAY_FORMAT_PLANAR_YUV420;
        break;
    case HAL_PIXEL_FORMAT_NV12:    // NV12
        uSurface = ySurface;
        vSurface = ySurface;
        backBuffer->OBUF_0U = yStride * h;
        yTileOffsetX = srcX;
        yTileOffsetY = srcY;
        uTileOffsetX = srcX / 2;
        uTileOffsetY = srcY / 2 + h;
        vTileOffsetX = uTileOffsetX;
        vTileOffsetY = uTileOffsetY;
        backBuffer->OCMD |= OVERLAY_FORMAT_PLANAR_NV12_2;
        break;
    // NOTE: this is the decoded video format, align the height to 32B
    //as it's defined by video driver
    case OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar:    // NV12
        uSurface = ySurface + yStride * align_to(h, 32);
        vSurface = ySurface + yStride * align_to(h, 32);
        yTileOffsetX = srcX;
        yTileOffsetY = srcY;
        uTileOffsetX = srcX;
        uTileOffsetY = srcY / 2;
        vTileOffsetX = uTileOffsetX;
        vTileOffsetY = uTileOffsetY;
        backBuffer->OCMD |= OVERLAY_FORMAT_PLANAR_NV12_2;
        break;
    case OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar_Tiled:  //NV12_tiled
        uSurface = ySurface + yStride * align_to(h, 32);
        vSurface = ySurface + yStride * align_to(h, 32);
        yTileOffsetX = srcX;
        yTileOffsetY = srcY;
        uTileOffsetX = srcX;
        uTileOffsetY = srcY / 2;
        vTileOffsetX = uTileOffsetX;
        vTileOffsetY = uTileOffsetY;
        backBuffer->OCMD |= OVERLAY_FORMAT_PLANAR_NV12_2;
        backBuffer->OCMD |= OVERLAY_MEMORY_LAYOUT_TILED;
        break;
    case HAL_PIXEL_FORMAT_YUY2:    // YUY2
        uSurface = ySurface;
        vSurface = ySurface;
        yTileOffsetX = srcX;
        yTileOffsetY = srcY;
        uTileOffsetX = yTileOffsetX;
        uTileOffsetY = yTileOffsetY;
        vTileOffsetX = yTileOffsetX;
        vTileOffsetY = yTileOffsetY;
        backBuffer->OCMD |= OVERLAY_FORMAT_PACKED_YUV422;
        backBuffer->OCMD |= OVERLAY_PACKED_ORDER_YUY2;
        break;
    case HAL_PIXEL_FORMAT_UYVY:    // UYVY
        uSurface = ySurface;
        vSurface = ySurface;
        yTileOffsetX = srcX;
        yTileOffsetY = srcY;
        uTileOffsetX = yTileOffsetX;
        uTileOffsetY = yTileOffsetY;
        vTileOffsetX = yTileOffsetX;
        vTileOffsetY = yTileOffsetY;
        backBuffer->OCMD |= OVERLAY_FORMAT_PACKED_YUV422;
        backBuffer->OCMD |= OVERLAY_PACKED_ORDER_UYVY;
        break;
    default:
        ETRACE("unsupported format %d", format);
        return false;
    }

    backBuffer->OSTART_0Y = ySurface;
    backBuffer->OSTART_0U = uSurface;
    backBuffer->OSTART_0V = vSurface;
    backBuffer->OBUF_0Y += srcY * yStride + srcX;
    backBuffer->OBUF_0V += (srcY / 2) * uvStride + srcX;
    backBuffer->OBUF_0U += (srcY / 2) * uvStride + srcX;
    backBuffer->OTILEOFF_0Y = yTileOffsetY << 16 | yTileOffsetX;
    backBuffer->OTILEOFF_0U = uTileOffsetY << 16 | uTileOffsetX;
    backBuffer->OTILEOFF_0V = vTileOffsetY << 16 | vTileOffsetX;

    VTRACE("done. offset (%d, %d, %d)",
          backBuffer->OBUF_0Y,
          backBuffer->OBUF_0U,
          backBuffer->OBUF_0V);

    return true;
}

bool AnnOverlayPlane::scalingSetup(BufferMapper& mapper)
{
    int xscaleInt, xscaleFract, yscaleInt, yscaleFract;
    int xscaleIntUV, xscaleFractUV;
    int yscaleIntUV, yscaleFractUV;
    // UV is half the size of Y -- YUV420
    int uvratio = 2;
    uint32_t newval;
    coeffRec xcoeffY[N_HORIZ_Y_TAPS * N_PHASES];
    coeffRec xcoeffUV[N_HORIZ_UV_TAPS * N_PHASES];
    coeffRec ycoeffY[N_VERT_Y_TAPS * N_PHASES];
    coeffRec ycoeffUV[N_VERT_UV_TAPS * N_PHASES];
    int i, j, pos;
    bool scaleChanged = false;
    int x, y, w, h;
    int deinterlace_factor = 1;
    drmModeModeInfoPtr mode = &mModeInfo;

    OverlayBackBufferBlk *backBuffer = mBackBuffer[mCurrent]->buf;
    if (!backBuffer) {
        ETRACE("invalid back buffer");
        return false;
    }

    if (mPanelOrientation == PANEL_ORIENTATION_180) {
        if (mode->hdisplay)
            x = mode->hdisplay - mPosition.x - mPosition.w;
        else
            x = mPosition.x;
        if (mode->vdisplay)
            y = mode->vdisplay - mPosition.y - mPosition.h;
        else
            y = mPosition.y;
    } else {
        x = mPosition.x;
        y = mPosition.y;
    }

    w = mPosition.w;
    h = mPosition.h;

    // check position
    checkPosition(x, y, w, h);
    VTRACE("final position (%d, %d, %d, %d)", x, y, w, h);

    if ((w <= 0) || (h <= 0)) {
         ETRACE("invalid dst width/height");
         return false;
    }

    // setup dst position
    backBuffer->DWINPOS = (y << 16) | x;
    backBuffer->DWINSZ = (h << 16) | w;

    uint32_t srcWidth = mapper.getCrop().w;
    uint32_t srcHeight = mapper.getCrop().h;
    uint32_t dstWidth = w;
    uint32_t dstHeight = h;

    if (mBobDeinterlace && !mTransform)
        deinterlace_factor = 2;

    VTRACE("src (%dx%d), dst (%dx%d), transform %d",
          srcWidth, srcHeight,
          dstWidth, dstHeight,
          mTransform);

    // switch destination width/height for scale factor calculation
    // for 90/270 transformation
    if (mUseOverlayRotation && ((mTransform == HWC_TRANSFORM_ROT_90) ||
        (mTransform == HWC_TRANSFORM_ROT_270))) {
        uint32_t tmp = srcHeight;
        srcHeight = srcWidth;
        srcWidth = tmp;
    }

     // Y down-scale factor as a multiple of 4096
    if (srcWidth == dstWidth && srcHeight == dstHeight) {
        xscaleFract = (1 << 12);
        yscaleFract = (1 << 12) / deinterlace_factor;
    } else {
        xscaleFract = ((srcWidth - 1) << 12) / dstWidth;
        yscaleFract = ((srcHeight - 1) << 12) / (dstHeight * deinterlace_factor);
    }

    // Calculate the UV scaling factor
    xscaleFractUV = xscaleFract / uvratio;
    yscaleFractUV = yscaleFract / uvratio;


    // To keep the relative Y and UV ratios exact, round the Y scales
    // to a multiple of the Y/UV ratio.
    xscaleFract = xscaleFractUV * uvratio;
    yscaleFract = yscaleFractUV * uvratio;

    // Integer (un-multiplied) values
    xscaleInt = xscaleFract >> 12;
    yscaleInt = yscaleFract >> 12;

    xscaleIntUV = xscaleFractUV >> 12;
    yscaleIntUV = yscaleFractUV >> 12;

    // Check scaling ratio
    if (xscaleInt > INTEL_OVERLAY_MAX_SCALING_RATIO) {
        ETRACE("xscaleInt > %d", INTEL_OVERLAY_MAX_SCALING_RATIO);
        return false;
    }

    // shouldn't get here
    if (xscaleIntUV > INTEL_OVERLAY_MAX_SCALING_RATIO) {
        ETRACE("xscaleIntUV > %d", INTEL_OVERLAY_MAX_SCALING_RATIO);
        return false;
    }

    newval = (xscaleInt << 15) |
    ((xscaleFract & 0xFFF) << 3) | ((yscaleFract & 0xFFF) << 20);
    if (newval != backBuffer->YRGBSCALE) {
        scaleChanged = true;
        backBuffer->YRGBSCALE = newval;
    }

    newval = (xscaleIntUV << 15) | ((xscaleFractUV & 0xFFF) << 3) |
    ((yscaleFractUV & 0xFFF) << 20);
    if (newval != backBuffer->UVSCALE) {
        scaleChanged = true;
        backBuffer->UVSCALE = newval;
    }

    newval = yscaleInt << 16 | yscaleIntUV;
    if (newval != backBuffer->UVSCALEV) {
        scaleChanged = true;
        backBuffer->UVSCALEV = newval;
    }

    // Recalculate coefficients if the scaling changed
    // Only Horizontal coefficients so far.
    if (scaleChanged) {
        double fHCutoffY;
        double fHCutoffUV;
        double fVCutoffY;
        double fVCutoffUV;

        fHCutoffY = xscaleFract / 4096.0;
        fHCutoffUV = xscaleFractUV / 4096.0;
        fVCutoffY = yscaleFract / 4096.0;
        fVCutoffUV = yscaleFractUV / 4096.0;

        // Limit to between 1.0 and 3.0
        if (fHCutoffY < MIN_CUTOFF_FREQ)
            fHCutoffY = MIN_CUTOFF_FREQ;
        if (fHCutoffY > MAX_CUTOFF_FREQ)
            fHCutoffY = MAX_CUTOFF_FREQ;
        if (fHCutoffUV < MIN_CUTOFF_FREQ)
            fHCutoffUV = MIN_CUTOFF_FREQ;
        if (fHCutoffUV > MAX_CUTOFF_FREQ)
            fHCutoffUV = MAX_CUTOFF_FREQ;

        if (fVCutoffY < MIN_CUTOFF_FREQ)
            fVCutoffY = MIN_CUTOFF_FREQ;
        if (fVCutoffY > MAX_CUTOFF_FREQ)
            fVCutoffY = MAX_CUTOFF_FREQ;
        if (fVCutoffUV < MIN_CUTOFF_FREQ)
            fVCutoffUV = MIN_CUTOFF_FREQ;
        if (fVCutoffUV > MAX_CUTOFF_FREQ)
            fVCutoffUV = MAX_CUTOFF_FREQ;

        updateCoeff(N_HORIZ_Y_TAPS, fHCutoffY, true, true, xcoeffY);
        updateCoeff(N_HORIZ_UV_TAPS, fHCutoffUV, true, false, xcoeffUV);
        updateCoeff(N_VERT_Y_TAPS, fVCutoffY, false, true, ycoeffY);
        updateCoeff(N_VERT_UV_TAPS, fVCutoffUV, false, false, ycoeffUV);

        for (i = 0; i < N_PHASES; i++) {
            for (j = 0; j < N_HORIZ_Y_TAPS; j++) {
                pos = i * N_HORIZ_Y_TAPS + j;
                backBuffer->Y_HCOEFS[pos] =
                        (xcoeffY[pos].sign << 15 |
                         xcoeffY[pos].exponent << 12 |
                         xcoeffY[pos].mantissa);
            }
        }
        for (i = 0; i < N_PHASES; i++) {
            for (j = 0; j < N_HORIZ_UV_TAPS; j++) {
                pos = i * N_HORIZ_UV_TAPS + j;
                backBuffer->UV_HCOEFS[pos] =
                         (xcoeffUV[pos].sign << 15 |
                          xcoeffUV[pos].exponent << 12 |
                          xcoeffUV[pos].mantissa);
            }
        }

        for (i = 0; i < N_PHASES; i++) {
            for (j = 0; j < N_VERT_Y_TAPS; j++) {
                pos = i * N_VERT_Y_TAPS + j;
                backBuffer->Y_VCOEFS[pos] =
                        (ycoeffY[pos].sign << 15 |
                         ycoeffY[pos].exponent << 12 |
                         ycoeffY[pos].mantissa);
            }
        }
        for (i = 0; i < N_PHASES; i++) {
            for (j = 0; j < N_VERT_UV_TAPS; j++) {
                pos = i * N_VERT_UV_TAPS + j;
                backBuffer->UV_VCOEFS[pos] =
                         (ycoeffUV[pos].sign << 15 |
                          ycoeffUV[pos].exponent << 12 |
                          ycoeffUV[pos].mantissa);
            }
        }
    }

    XTRACE();
    return true;
}

void AnnOverlayPlane::setTransform(int transform)
{
    RETURN_VOID_IF_NOT_INIT();

    if (mPanelOrientation == PANEL_ORIENTATION_180)
       transform ^=  HWC_TRANSFORM_ROT_180;

    DisplayPlane::setTransform(transform);

    // setup transform config
    switch (mTransform) {
    case HWC_TRANSFORM_ROT_90:
        mRotationConfig = (0x1 << 10);
        break;
    case HWC_TRANSFORM_ROT_180:
        mRotationConfig = (0x2 << 10);
        break;
    case HWC_TRANSFORM_ROT_270:
        mRotationConfig = (0x3 << 10);
        break;
    case 0:
        mRotationConfig = 0;
        break;
    default:
        ETRACE("Invalid transform %d", mTransform);
        mRotationConfig = 0;
        break;
    }
}

// HSD 4645510:
// This is a SOC limition, that when source buffer width range is
// in (960, 1024] - one cache line length, and rotation bit is set
// in portrait mode, video will show distortion.
bool AnnOverlayPlane::isSettingRotBitAllowed()
{
    uint32_t width = mSrcCrop.w;

    if ((width > 960 && width <= 1024) &&
            (mTransform == 0 || mTransform == HAL_TRANSFORM_ROT_180))
        return false;
    return true;
}

bool AnnOverlayPlane::flip(void *ctx)
{
    uint32_t ovadd = 0;

    RETURN_FALSE_IF_NOT_INIT();

    if (!DisplayPlane::flip(ctx)) {
        ETRACE("failed to flip display plane.");
        return false;
    }

    // update back buffer address
    ovadd = (mBackBuffer[mCurrent]->gttOffsetInPage << 12);

    // enable rotation mode and setup rotation config
    // if video is interlaced, cannot use overlay rotation
    if (mIndex == 0 && !mBobDeinterlace) {
        if (isSettingRotBitAllowed())
            ovadd |= (1 << 12);
        ovadd |= mRotationConfig;
    }

    // setup z-order config
    ovadd |= mZOrderConfig;

    // load coefficients
    ovadd |= 0x1;

    // enable overlay
    ovadd |= (1 << 15);

    mContext.type = DC_OVERLAY_PLANE;
    mContext.ctx.ov_ctx.ovadd = ovadd;
    mContext.ctx.ov_ctx.index = mIndex;
    mContext.ctx.ov_ctx.pipe = mDevice;
    mContext.ctx.ov_ctx.ovadd |= mPipeConfig;

    // move to next back buffer
    mCurrent = (mCurrent + 1) % OVERLAY_BACK_BUFFER_COUNT;

    VTRACE("ovadd = %#x, index = %d, device = %d",
          mContext.ctx.ov_ctx.ovadd,
          mIndex,
          mDevice);

    return true;
}

void* AnnOverlayPlane::getContext() const
{
    CTRACE();
    return (void *)&mContext;
}

bool AnnOverlayPlane::setDataBuffer(BufferMapper& mapper)
{
    if (OverlayPlaneBase::setDataBuffer(mapper) == false) {
        return false;
    }

    signalVideoRotation(mapper);

    if (mIsProtectedBuffer) {
        // Bit 0: Decryption request, only allowed to change on a synchronous flip
        // This request will be qualified with the separate decryption enable bit for OV
        mBackBuffer[mCurrent]->buf->OSTART_0Y |= 0x1;
        mBackBuffer[mCurrent]->buf->OSTART_1Y |= 0x1;
    }

    mContext.gtt_key = (unsigned long)mapper.getCpuAddress(0);

    return true;
}

bool AnnOverlayPlane::initialize(uint32_t bufferCount)
{
    if (!OverlayPlaneBase::initialize(bufferCount)) {
        ETRACE("failed to initialize OverlayPlaneBase");
        return false;
    }

    // setup rotation buffer
    mRotationBufProvider = new RotationBufferProvider(mWsbm);
    if (!mRotationBufProvider || !mRotationBufProvider->initialize()) {
        DEINIT_AND_RETURN_FALSE("failed to initialize RotationBufferProvider");
    }
    return true;
}

void AnnOverlayPlane::deinitialize()
{
    DEINIT_AND_DELETE_OBJ(mRotationBufProvider);
    OverlayPlaneBase::deinitialize();
}

bool AnnOverlayPlane::rotatedBufferReady(BufferMapper& mapper, BufferMapper* &rotatedMapper)
{
    struct VideoPayloadBuffer *payload;
    uint32_t format;
    // only NV12_VED has rotated buffer
    format = mapper.getFormat();
    if (format != OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar &&
        format != OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar_Tiled) {
        ETRACE("invalid video format %#x", format);
        return false;
    }

    payload = (struct VideoPayloadBuffer *)mapper.getCpuAddress(SUB_BUFFER1);
    // check payload
    if (!payload) {
        ETRACE("no payload found");
        return false;
    }

    if (payload->force_output_method == FORCE_OUTPUT_GPU) {
        ETRACE("Output method is not supported!");
        return false;
    }

    if (payload->client_transform != mTransform ||
        mBobDeinterlace) {
        if (!mRotationBufProvider->setupRotationBuffer(payload, mTransform)) {
            DTRACE("failed to setup rotation buffer");
            return false;
        }
    }

    rotatedMapper = getTTMMapper(mapper, payload);
    return true;
}

void AnnOverlayPlane::signalVideoRotation(BufferMapper& mapper)
{
    struct VideoPayloadBuffer *payload;
    uint32_t format;

    // check if it's video layer
    format = mapper.getFormat();
    if (format != OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar &&
        format != OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar_Tiled) {
        return;
    }

    payload = (struct VideoPayloadBuffer *)mapper.getCpuAddress(SUB_BUFFER1);
    if (!payload) {
        ETRACE("no payload found");
        return;
    }

    /* if use overlay rotation, signal decoder to stop rotation */
    if (mUseOverlayRotation) {
        if (payload->client_transform) {
            WTRACE("signal decoder to stop generate rotation buffer");
            payload->hwc_timestamp = systemTime();
            payload->layer_transform = 0;
        }
    } else {
        /* if overlay rotation cannot be used, signal decoder to start rotation */
        if (payload->client_transform != mTransform) {
            WTRACE("signal decoder to generate rotation buffer with transform %d", mTransform);
            payload->hwc_timestamp = systemTime();
            payload->layer_transform = mTransform;
        }
    }
}

bool AnnOverlayPlane::useOverlayRotation(BufferMapper& mapper)
{
    if (mTransform == 0)
        return true;

    if (!isSettingRotBitAllowed()) {
        mUseOverlayRotation = false;
        mRotationConfig = 0;
        return false;
    }

    // workaround limitation of overlay rotation by falling back to use VA rotated buffer
    bool fallback = false;
    float scaleX = (float)mSrcCrop.w / mPosition.w;
    float scaleY = (float)mSrcCrop.h / mPosition.h;
    if (mTransform == HAL_TRANSFORM_ROT_270 || mTransform == HAL_TRANSFORM_ROT_90) {
        scaleX = (float)mSrcCrop.w / mPosition.h;
        scaleY = (float)mSrcCrop.h / mPosition.w;
    }
    if (scaleX >= 3 || scaleY >= 3) {
        if (mUseOverlayRotation) {
            DTRACE("overlay rotation with scaling >= 3, use VA rotated buffer");
        }
        fallback = true;
    } else if ((int)mSrcCrop.x & 63) {
        if (mUseOverlayRotation) {
            DTRACE("offset is not 64 bytes aligned, use VA rotated buffer");
        }
        fallback = true;
    }
#if 0
    else if (mTransform != HAL_TRANSFORM_ROT_180 && scaleX != scaleY) {
        if (mUseOverlayRotation) {
            DTRACE("overlay rotation with uneven scaling, use VA rotated buffer");
        }
        fallback = true;
    }
#endif

    // per DC spec, if video is 1080(H)x1920(V), the buffer
    // need 1920 of 64-pixel strip if using hw rotation.
    // fallback to video ration buffer in such case.
    if (mSrcCrop.w == 1080 && mSrcCrop.h == 1920 && mTransform != 0) {
        DTRACE("1080(H)x1920(V) cannot use hw rotation, use VA rotated buffer");
        fallback = true;
    }

    if (fallback || mBobDeinterlace) {
        mUseOverlayRotation = false;
        mRotationConfig = 0;
    } else {
        mUseOverlayRotation = true;
    }
    return mUseOverlayRotation;
}

bool AnnOverlayPlane::scaledBufferReady(BufferMapper& mapper, BufferMapper* &scaledMapper, VideoPayloadBuffer *payload)
{
    mUseScaledBuffer = (payload->scaling_khandle != 0);

    if (mUseScaledBuffer) {
        mapper.setCrop(mapper.getCrop().x, mapper.getCrop().y, payload->scaling_width, payload->scaling_height);
        scaledMapper = getTTMMapper(mapper, payload);
        return (scaledMapper != 0);
    }

    return mUseScaledBuffer;
}

bool AnnOverlayPlane::flush(uint32_t flags)
{
    RETURN_FALSE_IF_NOT_INIT();
    ATRACE("flags = %#x, type = %d, index = %d", flags, mType, mIndex);

    if (!(flags & PLANE_ENABLE) && !(flags & PLANE_DISABLE)) {
        ETRACE("invalid flush flags.");
        return false;
    }

    struct drm_psb_register_rw_arg arg;
    memset(&arg, 0, sizeof(struct drm_psb_register_rw_arg));

    if (flags & PLANE_DISABLE)
        arg.plane_disable_mask = 1;
    else if (flags & PLANE_ENABLE)
        arg.plane_enable_mask = 1;

    arg.plane.type = DC_OVERLAY_PLANE;
    arg.plane.index = mIndex;
    arg.plane.ctx = mContext.ctx.ov_ctx.ovadd;
    if (flags & PLANE_DISABLE) {
        DTRACE("disabling overlay %d on device %d", mIndex, mDevice);
    }

    // issue ioctl
    Drm *drm = Hwcomposer::getInstance().getDrm();
    bool ret = drm->writeReadIoctl(DRM_PSB_REGISTER_RW, &arg, sizeof(arg));
    if (ret == false) {
        WTRACE("overlay update failed with error code %d", ret);
        return false;
    }

    return true;
}

} // namespace intel
} // namespace android