/*
// 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 <tangier/TngOverlayPlane.h>
#include <tangier/TngGrallocBuffer.h>

namespace android {
namespace intel {

TngOverlayPlane::TngOverlayPlane(int index, int disp)
    : OverlayPlaneBase(index, disp),
      mRotationBufProvider(NULL)
{
    CTRACE();

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

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

bool TngOverlayPlane::flip(void *ctx)
{
    RETURN_FALSE_IF_NOT_INIT();

    if (!DisplayPlane::flip(ctx))
        return false;

    mContext.type = DC_OVERLAY_PLANE;
    mContext.ctx.ov_ctx.ovadd = 0x0;
    mContext.ctx.ov_ctx.ovadd = (mBackBuffer[mCurrent]->gttOffsetInPage << 12);
    mContext.ctx.ov_ctx.index = mIndex;
    mContext.ctx.ov_ctx.pipe = mDevice;
    mContext.ctx.ov_ctx.ovadd |= mPipeConfig;
    mContext.ctx.ov_ctx.ovadd |= 0x1;

    // 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;
}

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

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

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

    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 = (uint64_t)mapper.getCpuAddress(0);
    return true;
}

bool TngOverlayPlane::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 TngOverlayPlane::deinitialize()
{
    DEINIT_AND_DELETE_OBJ(mRotationBufProvider);
    OverlayPlaneBase::deinitialize();
}

bool TngOverlayPlane::rotatedBufferReady(BufferMapper& mapper, BufferMapper* &rotatedMapper)
{
    struct VideoPayloadBuffer *payload;
    VideoPayloadBuffer buffer_info;
    uint32_t format;
    // only NV12_VED has rotated buffer
    format = mapper.getFormat();

    if (format != OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar &&
        format != OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar_Tiled &&
        format != HAL_PIXEL_FORMAT_NV12) {
        ETRACE("Invalid video format %#x", format);
        return false;
    }

    payload = (struct VideoPayloadBuffer *)mapper.getCpuAddress(SUB_BUFFER1);

    if (payload == NULL && format == HAL_PIXEL_FORMAT_NV12) {
         // need to populate buffer_info
        void *p = mapper.getCpuAddress(SUB_BUFFER0);
        if (!p) {
            ETRACE("failed to get buffer user pointer");
            return false;
        }

        bool ret = mRotationBufProvider->prepareBufferInfo(mapper.getWidth(),
                                                mapper.getHeight(),
                                                mapper.getStride().yuv.yStride,
                                                &buffer_info, p);
        if (ret == false) {
            ETRACE("failed to prepare buffer info");
            return false;
        }
        payload = &buffer_info;
    }

    // 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) {
        payload->hwc_timestamp = systemTime();
        payload->layer_transform = mTransform;
        if (!mRotationBufProvider->setupRotationBuffer(payload, mTransform)) {
            ETRACE("failed to setup rotation buffer");
            return false;
        }
    }

    rotatedMapper = getTTMMapper(mapper, payload);

    return true;
}

bool TngOverlayPlane::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))
        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 = (mBackBuffer[mCurrent]->gttOffsetInPage << 12);
    // pipe select
    arg.plane.ctx |= mPipeConfig;

    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