/* SPDX-License-Identifier: LGPL-2.1-only OR BSD-3-Clause */
/******************************************************************************
 *
 * This file is provided under a dual license.  When you use or
 * distribute this software, you may choose to be licensed under
 * version 2.1 of the GNU Lesser General Public License 
 * ("LGPLv2.1 License") or BSD License.
 *
 * LGPLv2.1 License
 *
 * Copyright(C) 2019 MediaTek Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of version 2.1 of the GNU Lesser General Public 
 * License as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html for more
 * details.
 *
 * BSD LICENSE
 *
 * Copyright(C) 2019 MediaTek Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *  * Neither the name of the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *****************************************************************************/

#include "gfx_if.h"
#include "gfx_drvif.h"
#include "gfx_dif.h"
#include "gfx_hw.h"
#include "gfx_cmdque.h"
#include "gfx_debug.h"

#ifdef CC_FLIP_MIRROR_SUPPORT
#include "eeprom_if.h"
#include "drv_display.h"
#endif

#define VG_INVERSE_8    8
/*-----------------------------------------------------------------------------
 data declarations
 ----------------------------------------------------------------------------*/

#define ORG_SIGN    0x80000000
#define ORG_EXP     0x7f800000
#define ORG_MAN     0x007fffff

#define REDUCE_SIGN 0x80000000
#define REDUCE_EXP  0x7e000000
#define REDUCE_MAN  0x01fffe00
#define MAN_MASK    0x007fff80

#define REDUCE_NAN  0x7f000000
#define REDUCE_INF  0x7e000000

typedef union
{
    unsigned int i;
    float f;
} fnum;

//solve AX = B
void gaussian_elimination(double A[][8], double X[], double B[])
{
    int i, j, k;
    double ratio;
    int first_nonzero_row;
    double temp;

    int column_all_zero[8];

    for (j = 0; j < VG_INVERSE_8; j++)
    {
        column_all_zero[j] = 1;

        for (i = j; i < VG_INVERSE_8; i++)
        {
            if (A[i][j] != 0)
            {
                column_all_zero[j] = 0;
                first_nonzero_row = i;

                if (first_nonzero_row != j)
                {
                    for (k = 0; k < VG_INVERSE_8; k++)
                    {
                        temp = A[j][k];
                        A[j][k] = A[first_nonzero_row][k];
                        A[first_nonzero_row][k] = temp;
                    }
                    temp = B[j];
                    B[j] = B[first_nonzero_row];
                    B[first_nonzero_row] = temp;
                }

                break;
            }
        }

        if (!column_all_zero[j])
        {
            for (i = j + 1; i < VG_INVERSE_8; i++)
            {
                ratio = A[i][j] / A[j][j];

                for (k = j; k < VG_INVERSE_8; k++)
                {
                    A[i][k] -= A[j][k] * ratio;
                }
                B[i] -= B[j] * ratio;
            }
        }
    }

    for (i = VG_INVERSE_8 - 1; i >= 0; i--)
    {
        if (column_all_zero[i] == 1)
        {
            X[i] = 0;
        }
        else
        {
            for (j = i + 1; j < VG_INVERSE_8; j++)
            {
                B[i] -= A[i][j] * X[j];
            }
            X[i] = B[i] / A[i][i];
        }
    }
}

void derive_inverse_transform(float src_x[], float src_y[], float dst_x[],
        float dst_y[], float inv_matrix[][3])
{
    int i;

    double A[8][8];
    double X[8];
    double B[8];

    for (i = 0; i < 4; i++)
    {
        A[i][0] = dst_x[i];
        A[i][1] = dst_y[i];
        A[i][2] = 1;
        A[i][3] = 0;
        A[i][4] = 0;
        A[i][5] = 0;
        A[i][6] = -(dst_x[i] * src_x[i]);
        A[i][7] = -(dst_y[i] * src_x[i]);

        B[i] = src_x[i];

        A[i + 4][0] = 0;
        A[i + 4][1] = 0;
        A[i + 4][2] = 0;
        A[i + 4][3] = dst_x[i];
        A[i + 4][4] = dst_y[i];
        A[i + 4][5] = 1;
        A[i + 4][6] = -(dst_x[i] * src_y[i]);
        A[i + 4][7] = -(dst_y[i] * src_y[i]);

        B[i + 4] = src_y[i];
    }

    gaussian_elimination(A, X, B);

    for (i = 0; i < 8; i++)
    {
        inv_matrix[i / 3][i % 3] = (float) X[i];
    }
    inv_matrix[2][2] = 1;
}

unsigned int fnum_converter(float in)
{
    fnum fnum_in;
    fnum_in.f = in;

    unsigned int out;
    unsigned int sign;
    int exp_tmp;
    unsigned int man;
    unsigned int exp;

    fnum_in.f = in;
    sign = fnum_in.i & ORG_SIGN;
    //NAN or INF
    if ((fnum_in.i & ORG_EXP) == ORG_EXP)
    {
        if (fnum_in.i & ORG_MAN) //NAN
            out = REDUCE_NAN;
        else
            //INF
            out = REDUCE_INF | sign;

        return out;
    }

    exp_tmp = (((int) (fnum_in.i) & ORG_EXP) >> 23) - 96; //old bias 127, new bias31

    if (exp_tmp >= 63) //6 bits exponential = 63
    {
        if (sign)
            out = REDUCE_SIGN | REDUCE_INF;

        else
            out = REDUCE_INF;

        return out;
    }
    else if (exp_tmp <= 0) //min tie to 0, no denormal mode
        return 0;

    man = fnum_in.i & ORG_MAN;

    //Rounding
    if (man & 0x40)
        man += 0x80;

    man = (man & MAN_MASK) << 2; //original mantissa masked by 16 bits then left shit 2 bits to match 6.16

    exp = exp_tmp << 25;

    out = sign + exp + man;

    return out;
}
