/*
// 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 <IDisplayDevice.h>
#include <DisplayPlaneManager.h>

namespace android {
namespace intel {

DisplayPlaneManager::DisplayPlaneManager()
    : mTotalPlaneCount(0),
      mPrimaryPlaneCount(DEFAULT_PRIMARY_PLANE_COUNT),
      mSpritePlaneCount(0),
      mOverlayPlaneCount(0),
      mInitialized(false)
{
    int i;

    for (i = 0; i < DisplayPlane::PLANE_MAX; i++) {
        mPlaneCount[i] = 0;
        mFreePlanes[i] = 0;
        mReclaimedPlanes[i] = 0;
    }
}

DisplayPlaneManager::~DisplayPlaneManager()
{
    WARN_IF_NOT_DEINIT();
}

void DisplayPlaneManager::deinitialize()
{
    int i;
    size_t j;

    for (i = 0; i < DisplayPlane::PLANE_MAX; i++) {
        for (j = 0; j < mPlanes[i].size(); j++) {
            // reset plane
            DisplayPlane *plane = mPlanes[i].itemAt(j);
            plane->reset();

            DEINIT_AND_DELETE_OBJ(plane);
        }
        mPlanes[i].clear();
    }

    mInitialized = false;
}

bool DisplayPlaneManager::initialize()
{
    int i, j;

    if (mInitialized) {
        WTRACE("object has been initialized");
        return true;
    }


    // calculate total plane number and free plane bitmaps
    mPlaneCount[DisplayPlane::PLANE_SPRITE] = mSpritePlaneCount;
    mPlaneCount[DisplayPlane::PLANE_OVERLAY] = mOverlayPlaneCount;
    mPlaneCount[DisplayPlane::PLANE_PRIMARY] = mPrimaryPlaneCount;
    mPlaneCount[DisplayPlane::PLANE_CURSOR] = mCursorPlaneCount;

    mTotalPlaneCount = mSpritePlaneCount+ mOverlayPlaneCount+ mPrimaryPlaneCount + mCursorPlaneCount;
    if (mTotalPlaneCount == 0) {
        ETRACE("plane count is not initialized");
        return false;
    }

    for (i = 0; i < DisplayPlane::PLANE_MAX; i++) {
        mFreePlanes[i] = ((1 << mPlaneCount[i]) - 1);
    }

    // allocate plane pools
    for (i = 0; i < DisplayPlane::PLANE_MAX; i++) {
        if (mPlaneCount[i]) {
            mPlanes[i].setCapacity(mPlaneCount[i]);

            for (j = 0; j < mPlaneCount[i]; j++) {
                DisplayPlane* plane = allocPlane(j, i);
                if (!plane) {
                    ETRACE("failed to allocate plane %d, type %d", j, i);
                    DEINIT_AND_RETURN_FALSE();
                }
                mPlanes[i].push_back(plane);
            }
        }
    }

    mInitialized = true;
    return true;
}

int DisplayPlaneManager::getPlane(uint32_t& mask)
{
    if (!mask)
        return -1;

    for (int i = 0; i < 32; i++) {
        int bit = (1 << i);
        if (bit & mask) {
            mask &= ~bit;
            return i;
        }
    }

    return -1;
}

void DisplayPlaneManager::putPlane(int index, uint32_t& mask)
{
    if (index < 0 || index >= 32)
        return;

    int bit = (1 << index);

    if (bit & mask) {
        WTRACE("bit %d was set", index);
        return;
    }

    mask |= bit;
}

int DisplayPlaneManager::getPlane(uint32_t& mask, int index)
{
    if (!mask || index < 0 || index > mTotalPlaneCount)
        return -1;

    int bit = (1 << index);
    if (bit & mask) {
        mask &= ~bit;
        return index;
    }

    return -1;
}

DisplayPlane* DisplayPlaneManager::getPlane(int type, int index)
{
    RETURN_NULL_IF_NOT_INIT();

    if (type < 0 || type >= DisplayPlane::PLANE_MAX) {
        ETRACE("Invalid plane type %d", type);
        return 0;
    }

    int freePlaneIndex = getPlane(mReclaimedPlanes[type], index);
    if (freePlaneIndex >= 0)
        return mPlanes[type].itemAt(freePlaneIndex);

    freePlaneIndex = getPlane(mFreePlanes[type], index);
    if (freePlaneIndex >= 0)
        return mPlanes[type].itemAt(freePlaneIndex);

    return 0;
}

DisplayPlane* DisplayPlaneManager::getAnyPlane(int type)
{
    RETURN_NULL_IF_NOT_INIT();

    if (type < 0 || type >= DisplayPlane::PLANE_MAX) {
        ETRACE("Invalid plane type %d", type);
        return 0;
    }

    int freePlaneIndex = getPlane(mReclaimedPlanes[type]);
    if (freePlaneIndex >= 0)
        return mPlanes[type].itemAt(freePlaneIndex);

    freePlaneIndex = getPlane(mFreePlanes[type]);
    if (freePlaneIndex >= 0)
        return mPlanes[type].itemAt(freePlaneIndex);

    return 0;
}

void DisplayPlaneManager::putPlane(int dsp, DisplayPlane& plane)
{
    int index;
    int type;

    RETURN_VOID_IF_NOT_INIT();

    index = plane.getIndex();
    type = plane.getType();

    if (type < 0 || type >= DisplayPlane::PLANE_MAX) {
        ETRACE("Invalid plane type %d", type);
        return;
    }

    putPlane(index, mFreePlanes[type]);
}

bool DisplayPlaneManager::isFreePlane(int type, int index)
{
    if (type < 0 || type >= DisplayPlane::PLANE_MAX) {
        ETRACE("Invalid plane type %d", type);
        return false;
    }

    int freePlanes = mFreePlanes[type] | mReclaimedPlanes[type];
    if ((freePlanes & (1 << index)) == 0)
        return false;

    return true;
}

int DisplayPlaneManager::getFreePlanes(int dsp, int type)
{
    RETURN_NULL_IF_NOT_INIT();

    if (dsp < 0 || dsp > IDisplayDevice::DEVICE_EXTERNAL) {
        ETRACE("Invalid display device %d", dsp);
        return 0;
    }

    if (type < 0 || type >= DisplayPlane::PLANE_MAX) {
        ETRACE("Invalid plane type %d", type);
        return 0;
    }


    uint32_t freePlanes = mFreePlanes[type] | mReclaimedPlanes[type];
    if (type == DisplayPlane::PLANE_PRIMARY ||
        type == DisplayPlane::PLANE_CURSOR) {
        return ((freePlanes & (1 << dsp)) == 0) ? 0 : 1;
    } else {
        int count = 0;
        for (int i = 0; i < 32; i++) {
            if ((1 << i) & freePlanes) {
                count++;
            }
        }
        return count;
    }
    return 0;
}

void DisplayPlaneManager::reclaimPlane(int dsp, DisplayPlane& plane)
{
    RETURN_VOID_IF_NOT_INIT();

    int index = plane.getIndex();
    int type = plane.getType();

    ATRACE("reclaimPlane = %d, type = %d", index, plane.getType());

    if (type < 0 || type >= DisplayPlane::PLANE_MAX) {
        ETRACE("Invalid plane type %d", type);
        return;
    }

    putPlane(index, mReclaimedPlanes[type]);

    // NOTE: don't invalidate plane's data cache here because the reclaimed
    // plane might be re-assigned to the same layer later
}

void DisplayPlaneManager::disableReclaimedPlanes()
{
    int i, j;
    bool ret;

    RETURN_VOID_IF_NOT_INIT();

    for (i = 0; i < DisplayPlane::PLANE_MAX; i++) {
        // disable reclaimed planes
        if (mReclaimedPlanes[i]) {
            for (j = 0; j < mPlaneCount[i]; j++) {
                int bit = (1 << j);
                if (mReclaimedPlanes[i] & bit) {
                    DisplayPlane* plane = mPlanes[i].itemAt(j);
                    // check plane state first
                    ret = plane->isDisabled();
                    // reset plane
                    if (ret)
                        ret = plane->reset();
                    if (ret) {
                        // only merge into free bitmap if it is successfully disabled and reset
                        // otherwise, plane will be disabled and reset again.
                        mFreePlanes[i] |=bit;
                        mReclaimedPlanes[i] &= ~bit;
                    }
                }
            }
        }
    }
}

bool DisplayPlaneManager::isOverlayPlanesDisabled()
{
    for (int i = 0; i < DisplayPlane::PLANE_MAX; i++) {
        for (int j = 0; j < mPlaneCount[i]; j++) {
            DisplayPlane* plane = (DisplayPlane *)mPlanes[i][j];
            if (plane && plane->getType() == DisplayPlane::PLANE_OVERLAY) {
                if (!plane->isDisabled())
                    return false;
            }
        }
    }

    return true;
}

void DisplayPlaneManager::dump(Dump& d)
{
    d.append("Display Plane Manager state:\n");
    d.append("-------------------------------------------------------------\n");
    d.append(" PLANE TYPE | COUNT |   FREE   | RECLAIMED \n");
    d.append("------------+-------+----------+-----------\n");
    d.append("    SPRITE  |  %2d   | %08x | %08x\n",
             mPlaneCount[DisplayPlane::PLANE_SPRITE],
             mFreePlanes[DisplayPlane::PLANE_SPRITE],
             mReclaimedPlanes[DisplayPlane::PLANE_SPRITE]);
    d.append("   OVERLAY  |  %2d   | %08x | %08x\n",
             mPlaneCount[DisplayPlane::PLANE_OVERLAY],
             mFreePlanes[DisplayPlane::PLANE_OVERLAY],
             mReclaimedPlanes[DisplayPlane::PLANE_OVERLAY]);
    d.append("   PRIMARY  |  %2d   | %08x | %08x\n",
             mPlaneCount[DisplayPlane::PLANE_PRIMARY],
             mFreePlanes[DisplayPlane::PLANE_PRIMARY],
             mReclaimedPlanes[DisplayPlane::PLANE_PRIMARY]);
    d.append("   CURSOR   |  %2d   | %08x | %08x\n",
             mPlaneCount[DisplayPlane::PLANE_CURSOR],
             mFreePlanes[DisplayPlane::PLANE_CURSOR],
             mReclaimedPlanes[DisplayPlane::PLANE_CURSOR]);
}

} // namespace intel
} // namespace android