/*
// 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 <HwcTrace.h>
#include <Drm.h>
#include <tangier/TngPrimaryPlane.h>
#include <tangier/TngGrallocBuffer.h>
#include <common/PixelFormat.h>

namespace android {
namespace intel {

TngPrimaryPlane::TngPrimaryPlane(int index, int disp)
    : TngSpritePlane(index, disp)
{
    CTRACE();
    mType = PLANE_PRIMARY;
    mForceBottom = true;
    mAbovePrimary = false;
}

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

void TngPrimaryPlane::setFramebufferTarget(buffer_handle_t handle)
{
    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
    mContext.type = DC_PRIMARY_PLANE;
    mContext.ctx.prim_ctx.update_mask = SPRITE_UPDATE_ALL;
    mContext.ctx.prim_ctx.index = mIndex;
    mContext.ctx.prim_ctx.pipe = mDevice;
    mContext.ctx.prim_ctx.stride = align_to((4 * align_to(mPosition.w, 32)), 64);
#ifdef ENABLE_ROTATION_180
    mContext.ctx.prim_ctx.linoff = (mPosition.h - 1) * mContext.ctx.prim_ctx.stride + (mPosition.w  - 1)* 4;
#else
    mContext.ctx.prim_ctx.linoff = 0;
#endif
    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 = 0;

    mContext.ctx.prim_ctx.cntr = PixelFormat::PLANE_PIXEL_FORMAT_BGRA8888;
#ifdef ENABLE_ROTATION_180
    mContext.ctx.prim_ctx.cntr |= 0x80008000;
#else
    mContext.ctx.prim_ctx.cntr |= 0x80000000;
#endif
    mCurrentDataBuffer = handle;
}

bool TngPrimaryPlane::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;
    }
    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) {
        WTRACE("primary enabling (%d) failed with error code %d", enabled, ret);
        return false;
    }

    return true;

}

bool TngPrimaryPlane::setDataBuffer(buffer_handle_t handle)
{
    if (!handle) {
        setFramebufferTarget(handle);
        return true;
    }

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

    ATRACE("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) {
        ETRACE("failed to set data buffer");
        return ret;
    }

    mContext.type = DC_PRIMARY_PLANE;
    return true;
}

void TngPrimaryPlane::setZOrderConfig(ZOrderConfig& zorderConfig,
                                           void *nativeConfig)
{
    if (!nativeConfig) {
        ETRACE("Invalid parameter, no native config");
        return;
    }

    mForceBottom = false;

    int primaryIndex = -1;
    int overlayIndex = -1;
    // only consider force bottom when overlay is active
    for (size_t i = 0; i < zorderConfig.size(); i++) {
        DisplayPlane *plane = zorderConfig[i]->plane;
        if (plane->getType() == DisplayPlane::PLANE_PRIMARY)
            primaryIndex = i;
        if (plane->getType() == DisplayPlane::PLANE_OVERLAY) {
            overlayIndex = i;
        }
    }

    // if has overlay plane which is below primary plane
    if (overlayIndex > primaryIndex) {
        mForceBottom = true;
    }

    struct intel_dc_plane_zorder *zorder =
        (struct intel_dc_plane_zorder *)nativeConfig;
    zorder->forceBottom[mIndex] = mForceBottom ? 1 : 0;
}

bool TngPrimaryPlane::assignToDevice(int disp)
{
    DisplayPlane::assignToDevice(disp);
    return true;
}

} // namespace intel
} // namespace android