/*
// 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 <common/utils/HwcTrace.h>
#include <Hwcomposer.h>
#include <BufferManager.h>
#include <ips/anniedale/AnnRGBPlane.h>
#include <ips/tangier/TngGrallocBuffer.h>
#include <ips/common/PixelFormat.h>

namespace android {
namespace intel {

AnnRGBPlane::AnnRGBPlane(int index, int type, int disp)
    : DisplayPlane(index, type, disp)
{
    CTRACE();
    memset(&mContext, 0, sizeof(mContext));
}

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

bool AnnRGBPlane::enable()
{
    return enablePlane(true);
}

bool AnnRGBPlane::disable()
{
    return enablePlane(false);
}

bool AnnRGBPlane::reset()
{
    while (!mScalingBufferMap.isEmpty()) {
        uint32_t handle = mScalingBufferMap.valueAt(0);
        Hwcomposer::getInstance().getBufferManager()->freeGrallocBuffer(handle);
        mScalingBufferMap.removeItemsAt(0);
    }

    return DisplayPlane::reset();
}

bool AnnRGBPlane::flip(void*)
{
    if (mForceScaling) {
        BufferManager *bm = Hwcomposer::getInstance().getBufferManager();
        if (!bm->blitGrallocBuffer(mScalingSource, mScalingTarget, mDisplayCrop, 0)) {
            ELOGTRACE("Failed to blit RGB buffer.");
            return false;
        }
    }

    return true;
}

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

void AnnRGBPlane::setZOrderConfig(ZOrderConfig& /* config */, void * /* nativeConfig */)
{
    CTRACE();
}

bool AnnRGBPlane::setDataBuffer(uint32_t handle)
{
    if (!handle) {
        if (!mForceScaling) {
            setFramebufferTarget(handle);
            return true;
        } else {
            ELOGTRACE("Invalid handle while scaling is required.");
            return false;
        }
    }

    if (mForceScaling) {
        BufferManager *bm = Hwcomposer::getInstance().getBufferManager();
        ssize_t index = mScalingBufferMap.indexOfKey(handle);
        if (index < 0) {
            mScalingTarget = bm->allocGrallocBuffer(
                    mDisplayWidth,
                    mDisplayHeight,
                    HAL_PIXEL_FORMAT_RGBA_8888,
                    GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE);
            if (!mScalingTarget) {
                ELOGTRACE("Failed to allocate gralloc buffer.");
                return false;
            }

            if (mScalingBufferMap.size() >= MAX_SCALING_BUF_COUNT) {
                while (!mScalingBufferMap.isEmpty()) {
                    uint32_t handle = mScalingBufferMap.valueAt(0);
                    bm->freeGrallocBuffer(handle);
                    mScalingBufferMap.removeItemsAt(0);
                }
            }

            mScalingBufferMap.add(handle, mScalingTarget);
        } else {
            mScalingTarget = mScalingBufferMap.valueAt(index);
        }
        mScalingSource = handle;
        handle = mScalingTarget;
    }

    TngGrallocBuffer tmpBuf(handle);
    uint32_t usage;
    bool ret;

    ALOGTRACE("handle = %#x", handle);

    usage = tmpBuf.getUsage();
    if (GRALLOC_USAGE_HW_FB & usage) {
        setFramebufferTarget(handle);
        return true;
    }

    // use primary as a sprite
    ret = DisplayPlane::setDataBuffer(handle);
    if (ret == false) {
        ELOGTRACE("failed to set data buffer");
        return ret;
    }

    return true;
}

bool AnnRGBPlane::setDataBuffer(BufferMapper& mapper)
{
    int bpp;
    int srcX, srcY, srcW, srcH;
    int dstX, dstY, dstW, dstH;
    uint32_t spriteFormat;
    uint32_t stride;
    uint32_t linoff;
    uint32_t planeAlpha;
    drmModeModeInfoPtr mode = &mModeInfo;

    CTRACE();

    // setup plane position
    dstX = mPosition.x;
    dstY = mPosition.y;
    dstW = mPosition.w;
    dstH = mPosition.h;

    checkPosition(dstX, dstY, dstW, dstH);

    // setup plane format
    if (!PixelFormat::convertFormat(mapper.getFormat(), spriteFormat, bpp)) {
        ELOGTRACE("unsupported format %#x", mapper.getFormat());
        return false;
    }

    // setup stride and source buffer crop
    srcX = mapper.getCrop().x;
    srcY = mapper.getCrop().y;
    srcW = mapper.getWidth();
    srcH = mapper.getHeight();
    stride = mapper.getStride().rgb.stride;

    if (mPanelOrientation == PANEL_ORIENTATION_180)
        linoff = srcY * stride + srcX * bpp + (mapper.getCrop().h  - 1) * stride + (mapper.getCrop().w - 1) * bpp;
    else
        linoff = srcY * stride + srcX * bpp;

    // unlikely happen, but still we need make sure linoff is valid
    if (linoff > (stride * mapper.getHeight())) {
        ELOGTRACE("invalid source crop");
        return false;
    }

    // update context
    if (mType == PLANE_SPRITE)
        mContext.type = DC_SPRITE_PLANE;
    else
        mContext.type = DC_PRIMARY_PLANE;

    // setup plane alpha
    if (0 < mPlaneAlpha && mPlaneAlpha < 0xff) {
       planeAlpha = mPlaneAlpha | 0x80000000;
    } else {
       // disable plane alpha to offload HW
       planeAlpha = 0xff;
    }

    mContext.ctx.sp_ctx.index = mIndex;
    mContext.ctx.sp_ctx.pipe = mDevice;
    mContext.ctx.sp_ctx.cntr = spriteFormat | 0x80000000;
    mContext.ctx.sp_ctx.linoff = linoff;
    mContext.ctx.sp_ctx.stride = stride;

    // turn off premultipled alpha blending for HWC_BLENDING_COVERAGE
    if (mBlending == HWC_BLENDING_COVERAGE) {
        mContext.ctx.sp_ctx.cntr |= (0x1 << 23);
    }

    if (mPanelOrientation == PANEL_ORIENTATION_180)
        mContext.ctx.sp_ctx.cntr |= (0x1 << 15);

    if (mapper.isCompression()) {
        mContext.ctx.sp_ctx.stride = align_to(srcW, 32) * 4;
        mContext.ctx.sp_ctx.linoff = (align_to(srcW, 32) * srcH / 64) - 1;
        mContext.ctx.sp_ctx.tileoff = (srcY & 0xfff) << 16 | (srcX & 0xfff);
        mContext.ctx.sp_ctx.cntr |= (0x1 << 11);
    }

    mContext.ctx.sp_ctx.surf = mapper.getGttOffsetInPage(0) << 12;

    if (mPanelOrientation == PANEL_ORIENTATION_180) {
        if (mode->vdisplay && mode->hdisplay)
            mContext.ctx.sp_ctx.pos = ((mode->vdisplay - dstY - dstH) & 0xfff) << 16 | ((mode->hdisplay - dstX - dstW) & 0xfff);
        else
            mContext.ctx.sp_ctx.pos = (dstY & 0xfff) << 16 | (dstX & 0xfff);
    } else {
        mContext.ctx.sp_ctx.pos = (dstY & 0xfff) << 16 | (dstX & 0xfff);
    }

    mContext.ctx.sp_ctx.size =
        ((dstH - 1) & 0xfff) << 16 | ((dstW - 1) & 0xfff);
    mContext.ctx.sp_ctx.contalpa = planeAlpha;
    mContext.ctx.sp_ctx.update_mask = SPRITE_UPDATE_ALL;

    VLOGTRACE("type = %d, index = %d, cntr = %#x, linoff = %#x, stride = %#x,"
          "surf = %#x, pos = %#x, size = %#x, contalpa = %#x", mType, mIndex,
          mContext.ctx.sp_ctx.cntr,
          mContext.ctx.sp_ctx.linoff,
          mContext.ctx.sp_ctx.stride,
          mContext.ctx.sp_ctx.surf,
          mContext.ctx.sp_ctx.pos,
          mContext.ctx.sp_ctx.size,
          mContext.ctx.sp_ctx.contalpa);
    return true;
}

bool AnnRGBPlane::enablePlane(bool enabled)
{
    RETURN_FALSE_IF_NOT_INIT();

    struct drm_psb_register_rw_arg arg;
    memset(&arg, 0, sizeof(struct drm_psb_register_rw_arg));
    if (enabled) {
        arg.plane_enable_mask = 1;
    } else {
        arg.plane_disable_mask = 1;
    }

    if (mType == PLANE_SPRITE)
        arg.plane.type = DC_SPRITE_PLANE;
    else
        arg.plane.type = DC_PRIMARY_PLANE;

    arg.plane.index = mIndex;
    arg.plane.ctx = 0;

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

    return true;
}

bool AnnRGBPlane::isDisabled()
{
    RETURN_FALSE_IF_NOT_INIT();

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

    if (mType == PLANE_SPRITE)
        arg.plane.type = DC_SPRITE_PLANE;
    else
        arg.plane.type = DC_PRIMARY_PLANE;

    arg.get_plane_state_mask = 1;
    arg.plane.index = mIndex;
    arg.plane.ctx = 0;

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

    return arg.plane.ctx == PSB_DC_PLANE_DISABLED;
}

void AnnRGBPlane::postFlip()
{
    // prevent mUpdateMasks from being reset
    // skipping flip may cause flicking
}

void AnnRGBPlane::setFramebufferTarget(uint32_t handle)
{
    uint32_t stride;
    uint32_t planeAlpha;

    CTRACE();

    // do not need to update the buffer handle
    if (mCurrentDataBuffer != handle)
        mUpdateMasks |= PLANE_BUFFER_CHANGED;
    else
        mUpdateMasks &= ~PLANE_BUFFER_CHANGED;

    // if no update then do Not need set data buffer
    if (!mUpdateMasks)
        return;

    // don't need to map data buffer for primary plane
    if (mType == PLANE_SPRITE)
        mContext.type = DC_SPRITE_PLANE;
    else
        mContext.type = DC_PRIMARY_PLANE;

    stride = align_to((4 * align_to(mPosition.w, 32)), 64);

    if (0 < mPlaneAlpha && mPlaneAlpha < 0xff) {
       planeAlpha = mPlaneAlpha | 0x80000000;
    } else {
       // disable plane alpha to offload HW
       planeAlpha = 0xff;
    }

    // FIXME: use sprite context for sprite plane
    mContext.ctx.prim_ctx.update_mask = SPRITE_UPDATE_ALL;
    mContext.ctx.prim_ctx.index = mIndex;
    mContext.ctx.prim_ctx.pipe = mDevice;

    if (mPanelOrientation == PANEL_ORIENTATION_180)
        mContext.ctx.prim_ctx.linoff = (mPosition.h  - 1) * stride + (mPosition.w - 1) * 4;
    else
        mContext.ctx.prim_ctx.linoff = 0;

    mContext.ctx.prim_ctx.stride = stride;
    mContext.ctx.prim_ctx.tileoff = 0;
    mContext.ctx.prim_ctx.pos = 0;
    mContext.ctx.prim_ctx.size =
        ((mPosition.h - 1) & 0xfff) << 16 | ((mPosition.w - 1) & 0xfff);
    mContext.ctx.prim_ctx.surf = 0;
    mContext.ctx.prim_ctx.contalpa = planeAlpha;
    mContext.ctx.prim_ctx.cntr = PixelFormat::PLANE_PIXEL_FORMAT_BGRA8888;
    mContext.ctx.prim_ctx.cntr |= 0x80000000;

    // turn off premultipled alpha blending for HWC_BLENDING_COVERAGE
    if (mBlending == HWC_BLENDING_COVERAGE) {
        mContext.ctx.prim_ctx.cntr |= (0x1 << 23);
    }

    if (mPanelOrientation == PANEL_ORIENTATION_180)
        mContext.ctx.prim_ctx.cntr |= (0x1 << 15);

    VLOGTRACE("type = %d, index = %d, cntr = %#x, linoff = %#x, stride = %#x,"
          "surf = %#x, pos = %#x, size = %#x, contalpa = %#x", mType, mIndex,
          mContext.ctx.prim_ctx.cntr,
          mContext.ctx.prim_ctx.linoff,
          mContext.ctx.prim_ctx.stride,
          mContext.ctx.prim_ctx.surf,
          mContext.ctx.prim_ctx.pos,
          mContext.ctx.prim_ctx.size,
          mContext.ctx.sp_ctx.contalpa);

    mCurrentDataBuffer = handle;
}

} // namespace intel
} // namespace android