/*
// 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 <Hwcomposer.h>
#include <BufferManager.h>
#include <tangier/TngSpritePlane.h>
#include <common/PixelFormat.h>

namespace android {
namespace intel {

TngSpritePlane::TngSpritePlane(int index, int disp)
    : SpritePlaneBase(index, disp)
{
    CTRACE();
    memset(&mContext, 0, sizeof(mContext));
}

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

bool TngSpritePlane::setDataBuffer(BufferMapper& mapper)
{
    int bpp;
    int srcX, srcY;
    int dstX, dstY, dstW, dstH;
    uint32_t spriteFormat;
    uint32_t stride;
    uint32_t linoff;
    uint32_t planeAlpha;

    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)) {
        ETRACE("unsupported format %#x", mapper.getFormat());
        return false;
    }

    // setup stride and source buffer crop
    srcX = mapper.getCrop().x;
    srcY = mapper.getCrop().y;
    stride = mapper.getStride().rgb.stride;
#ifdef ENABLE_ROTATION_180
    linoff = (mapper.getCrop().h + srcY - 1) * stride + (srcX + mapper.getCrop().w - 1) * bpp;
#else
    linoff = srcY * stride + srcX * bpp;
#endif

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

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

    // update context
    mContext.type = DC_SPRITE_PLANE;
    mContext.ctx.sp_ctx.index = mIndex;
    mContext.ctx.sp_ctx.pipe = mDevice;
    // none blending and BRGA format layer,set format to BGRX8888
    if (mBlending == HWC_BLENDING_NONE && spriteFormat == PixelFormat::PLANE_PIXEL_FORMAT_BGRA8888)
	mContext.ctx.sp_ctx.cntr = PixelFormat::PLANE_PIXEL_FORMAT_BGRX8888
					| 0x80000000;
    else
	mContext.ctx.sp_ctx.cntr = spriteFormat | 0x80000000;
    mContext.ctx.sp_ctx.linoff = linoff;
    mContext.ctx.sp_ctx.stride = stride;
    mContext.ctx.sp_ctx.surf = mapper.getGttOffsetInPage(0) << 12;
    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;
    mContext.gtt_key = (uint64_t)mapper.getCpuAddress(0);
#ifdef ENABLE_ROTATION_180
    mContext.ctx.sp_ctx.cntr |= 1 << 15;
#endif
    VTRACE("cntr = %#x, linoff = %#x, stride = %#x,"
          "surf = %#x, pos = %#x, size = %#x, contalpa = %#x",
          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;
}

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

bool TngSpritePlane::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_SPRITE_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("sprite enabling (%d) failed with error code %d", enabled, ret);
        return false;
    }

    Hwcomposer& hwc = Hwcomposer::getInstance();
    DisplayPlaneManager *pm = hwc.getPlaneManager();
    void *config = pm->getZOrderConfig();
    if (config != NULL) {
        struct intel_dc_plane_zorder *zorder =  (struct intel_dc_plane_zorder *)config;
        zorder->abovePrimary = 0;
    }

    return true;

}

bool TngSpritePlane::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 == DisplayPlane::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) {
        WTRACE("plane state query failed with error code %d", ret);
        return false;
    }

    return arg.plane.ctx == PSB_DC_PLANE_DISABLED;
}

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

    mAbovePrimary = false;

    int primaryIndex = -1;
    int spriteIndex = -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_SPRITE) {
            spriteIndex = i;
        }
    }

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

    struct intel_dc_plane_zorder *zorder =
        (struct intel_dc_plane_zorder *)nativeConfig;
    zorder->abovePrimary = mAbovePrimary ? 1 : 0;
}
} // namespace intel
} // namespace android