/* SPDX-License-Identifier: GPL-2.0-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 of the GNU General Public License ("GPLv2 License")
 * or BSD License.
 *
 * GPLv2 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 of the GNU 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 http://www.gnu.org/licenses/gpl-2.0.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 <command.h>
#include <common.h>
#include <stdio.h>
#include <debug_impl.h>
#include <MsTypes.h>
//#include <ShareType.h>
#include <malloc.h>

#include <CommonDataType.h>
#include <dmalloc.h>
#include <read_text.h>
#include <demura.h>
#include <ms_utils.h>

#include <apiPNL.h>
#include <MDrvDemura.h>
#include <halDemura.h>
#include <convert_entry.h>
#include "vendor.h"
#include "crc_libs.h"
#include "demura_config.h"

#define SAMPLE_DAT_START    1024     // Byte
#define SAMPLE_DAT_LEN      4096     // Byte
#define FLASH_MAX_SIZE   (1024 * 1024)

typedef union
{
    MS_U32 block_data;
    unsigned char data[4];

    struct
    {
        int nReserved:8;
        int b3:6;
        int b2:6;
        int b1:6;
        int b0:6;
    };
}DATA;

static MS_BOOL Gen_Sf_Signature(CSOT_Himax_Demura_Header *phdr)
{
    char   strbuf[1024];
    MS_U32 sample_crc32;
    MS_U8  *buffer;

    buffer = phdr->lut_buffer;

    sample_crc32 = MDrv_CRC32_Cal_DeMura(buffer + SAMPLE_DAT_START, SAMPLE_DAT_LEN);
    if (sample_crc32 == 0xffffFFFF)
    {
        UBOOT_ERROR("MDrv_CRC32_Cal_DeMura error\n");
        return FALSE;
    }
    memset(strbuf, 0, sizeof(strbuf));
    snprintf(strbuf, sizeof(strbuf)-1, "0x%x:0x%x", phdr->TABLE_CRC, sample_crc32);
    env_set(ENV_DEMURA_SIG, strbuf);
    env_save();
    return TRUE;
}


MS_BOOL If_Need_Decode(void)
{
    UBOOT_TRACE("IN\n");

    char *sig_str = env_get(ENV_DEMURA_SIG);
    if (sig_str == NULL)
    {
        UBOOT_DEBUG("Empty Board, should decoding data\n");
        UBOOT_TRACE("OK\n");
        return TRUE;
    }
    else
    {
        MS_U16 lut_checksum_sf;
        MS_U32 lut_checksum_bd;
        MS_U32 sample_crc32, cal_crc32;
        MS_U8  buf[SAMPLE_DAT_LEN];

        lut_checksum_sf = get_lut_checksum();

        UBOOT_DEBUG("sig_str = %s\n", sig_str);
        if(2 == sscanf(sig_str, "0x%x:0x%x", &lut_checksum_bd, &sample_crc32))
        {
            UBOOT_DEBUG("lut_checksum_sf = 0x%x\n", (uint)lut_checksum_sf);
            UBOOT_DEBUG("lut_checksum_bd = 0x%x\n", (uint)lut_checksum_bd);
            UBOOT_DEBUG("sample_crc32  = 0x%x\n",   (uint)sample_crc32);

            if (read_spi_flash(buf, (CSOT_HIMAX_LUT_START + SAMPLE_DAT_START), SAMPLE_DAT_LEN) == TRUE)
            {
                cal_crc32 = MDrv_CRC32_Cal_DeMura(buf, SAMPLE_DAT_LEN);
                UBOOT_DEBUG("cal_crc32     = 0x%x\n", (uint)cal_crc32);
                if ((sample_crc32 == cal_crc32) && (lut_checksum_sf == lut_checksum_bd))
                {
                    UBOOT_DEBUG("Data Match, No Need to decode again\n");
                    UBOOT_TRACE("OK\n");
                    return FALSE;
                }
            }
        }

        UBOOT_TRACE("OK\n");
        return TRUE;
    }

    UBOOT_TRACE("OK\n");
    return FALSE;
}

static void dump_interface_info(interface_info *pDataInfo)
{
    printf("PLANE_NUM = %d  \n", pDataInfo->reg_dmc_plane_num);
    printf("H_BLOCK = %d, V_BLOCK = %d  \n", pDataInfo->reg_dmc_h_block, pDataInfo->reg_dmc_v_block);
    printf("RGB_MODE = %d   \n", pDataInfo->reg_dmc_rgb_mode);
    printf("PANEL_H_SIZE = %d   \n", pDataInfo->reg_dmc_panel_h_size);
    printf("BLACK_LIMIT = 0x%x, WHITE_LIMIT = 0x%x  \n", pDataInfo->reg_dmc_black_limit, pDataInfo->reg_dmc_white_limit);

    printf("PLANE_LEVEL 1 = 0x%x   \n", pDataInfo->reg_dmc_plane_level1);
    printf("PLANE_LEVEL 2 = 0x%x   \n", pDataInfo->reg_dmc_plane_level2);
    printf("PLANE_LEVEL 3 = 0x%x   \n", pDataInfo->reg_dmc_plane_level3);
    printf("PLANE_LEVEL 4 = 0x%x   \n", pDataInfo->reg_dmc_plane_level4);
    printf("PLANE_LEVEL 5 = 0x%x   \n", pDataInfo->reg_dmc_plane_level5);
    printf("PLANE_LEVEL 6 = 0x%x   \n", pDataInfo->reg_dmc_plane_level6);
    printf("PLANE_LEVEL 7 = 0x%x   \n", pDataInfo->reg_dmc_plane_level7);
    printf("PLANE_LEVEL 8 = 0x%x   \n", pDataInfo->reg_dmc_plane_level8);

    printf("DITHER_4_FRAME = %d, DITHER_RST_NUM = %d, DITHER_EN = %d    \n",
        pDataInfo->reg_dmc_dither_rst_e_4_frame, pDataInfo->reg_dmc_dither_pse_rst_num, pDataInfo->reg_dmc_dither_en);

    printf("ROI_EN = %d, ROI_H_START = %d, ROI_H_END = %d, ROI_V_START = %d, ROI_V_END = %d \n",
        pDataInfo->bROI_en, pDataInfo->iROI_hor_start_offset, pDataInfo->iROI_hor_end_offset,
        pDataInfo->iROI_ver_start_offset, pDataInfo->iROI_ver_end_offset);
}

static MS_BOOL CSOT_Himax_Decode_To_Mstar_Format(CSOT_Himax_Demura_Header *phdr, interface_info *pinfo)
{
    int i, j, k, data = 0;
    int table_addr = 0;
    int nLayer = 0, idx_image_size;
    MS_BOOL pol = 0;
    double LUT_data = 0.0;

    // assign LUT 12 bit format
    for (i = 0; i < phdr->DEMURA_TBL_V; i++)
    {
        for(k = 0; k < pinfo->reg_dmc_plane_num; k++)
        {
            for (j = 0; j < (phdr->DEMURA_TBL_H+3) ; j++)
            {				  
                if(j == 0)
                    pol = 0;
                
                // LUT index for the plane, H size = 481
                idx_image_size = i*phdr->DEMURA_TBL_H+j;

                // 12 bit data
                if(pol == 0) //8bit + 4bit
                {
                    data = ((MS_U16)phdr->lut_buffer[table_addr]) | (((MS_U16)phdr->lut_buffer[table_addr + 1]&0x0f)<<8);
                    pol = 1;
                    table_addr+=1;
                }
                else
                {
                    data = (((MS_U16)phdr->lut_buffer[table_addr] & 0xf0)>>4) | (((MS_U16)phdr->lut_buffer[table_addr + 1]&0xff)<<4);
                    pol = 0;
                    table_addr+=2;
                }

if(j > (phdr->DEMURA_TBL_H - 1)) // Skip dummy 3 nodes
{
}
else
{
                // 2048 is zero.
                data -= 2048;

                switch(k)
                {
                    case 0:
                        nLayer = pinfo->reg_dmc_plane_level1;
                        break;
                    case 1:
                        nLayer = pinfo->reg_dmc_plane_level2;
                        break;
                    case 2:
                        nLayer = pinfo->reg_dmc_plane_level3;
                        break;
                    case 3:
                        nLayer = pinfo->reg_dmc_plane_level4;
                        break;
                    case 4:
                        nLayer = pinfo->reg_dmc_plane_level5;
                        break;
                    case 5:
                        nLayer = pinfo->reg_dmc_plane_level6;
                        break;
                    case 6:
                        nLayer = pinfo->reg_dmc_plane_level7;
                        break;
                    case 7:
                        nLayer = pinfo->reg_dmc_plane_level8;
                        break;
                    default:
                        break;
                }

                LUT_data = ((double)data/4.0) + ((double)nLayer/4.0); // 10bit data + layer (12bit to 10bit)

                pinfo->Lut_in[k][idx_image_size].dbr   = (double)LUT_data;
                pinfo->Lut_in[k][idx_image_size].dbg   = (double)LUT_data;
                pinfo->Lut_in[k][idx_image_size].dbb   = (double)LUT_data;
                #if (defined (CONFIG_DEMURA_M7622) || defined (CONFIG_DEMURA_M7632))
                pinfo->Lut_in[k][idx_image_size].dbw   = (double)LUT_data;
                #endif

                //LUT_data = LUT_data>>4; // 14bit -> 10bit
                pinfo->Lut_in[k][idx_image_size].r   = (int)LUT_data;
                pinfo->Lut_in[k][idx_image_size].g   = (int)LUT_data;
                pinfo->Lut_in[k][idx_image_size].b   = (int)LUT_data;
                #if (defined (CONFIG_DEMURA_M7622) || defined (CONFIG_DEMURA_M7632))
                pinfo->Lut_in[k][idx_image_size].w   = (int)LUT_data;
                #endif

}
            }
        }
    }

    // Free buffer
    free(phdr->hdr_buffer);
    phdr->hdr_buffer = NULL;

    dfree(phdr->lut_buffer);
    phdr->lut_buffer = NULL;
    
    return TRUE;
}



MS_BOOL Decode_To_Mstar_Format(void *pdat_info, BinOutputInfo *pbin_info)
{
    MS_BOOL bRet;
    CSOT_Himax_Demura_Header header;

    #if defined (CONFIG_DEMURA_URSA13)
    interface_info *pDataInfo = (interface_info *)pdat_info;
    #elif (defined (CONFIG_DEMURA_M7622) || defined (CONFIG_DEMURA_M7632))
    interface_info *pDataInfo = (interface_info *)pdat_info;
    #else
    #error "Unkown DEMURA_URSA_TYPE !"
    #endif

    // Parse header information
    memset(&header, 0, sizeof(header));
    if (load_vendor_header(&header) != TRUE)
    {
        printf("load_vendor_header Error\n");
        return FALSE;
    }

    if (parse_vendor_header(&header) != TRUE)
    {
        printf("parse_vendor_header Error\n");
        return FALSE;
    }

    if (load_vendor_lut(&header) != TRUE)
    {
        printf("load_vendor_lut Error\n");
        return FALSE;
    }

    Gen_Sf_Signature(&header);
    // Alloc space for Lut_in
    header.DEMURA_TBL_H = 481;
    header.DEMURA_TBL_V = 271;
    int n_Hnode = header.DEMURA_TBL_H;
    int n_Vnode = header.DEMURA_TBL_V;

    pDataInfo->reg_dmc_h_block            = 3;//header.DEMURA_BLK_H;         //  3 bit
    pDataInfo->reg_dmc_v_block            = 3;//header.DEMURA_BLK_V;         //  3 bit

    pDataInfo->reg_dmc_plane_num              = CSOT_HIMAX_USE_PLANE_NUM;         //  4 bit
    pDataInfo->reg_dmc_rgb_mode               = 0;         //  1 bit
    pDataInfo->reg_dmc_panel_h_size           = 0xF00;       // 13 bit
    pDataInfo->reg_dmc_black_limit              = header.LOW_BOUND;       // 12 bit , format 10.2
    pDataInfo->reg_dmc_plane_level1           = header.PLANE0_LV;       // 12 bit , format 10.2
    pDataInfo->reg_dmc_plane_level2           = header.PLANE1_LV;       // 12 bit , format 10.2
    pDataInfo->reg_dmc_plane_level3           = header.PLANE2_LV;       // 12 bit , format 10.2
    pDataInfo->reg_dmc_plane_level4           = header.PLANE3_LV;       // 12 bit , format 10.2
    pDataInfo->reg_dmc_plane_level5           = 0;           // 12 bit , format 10.2
    pDataInfo->reg_dmc_plane_level6           = 0;           // 12 bit , format 10.2
    pDataInfo->reg_dmc_plane_level7           = 0;           // 12 bit , format 10.2
    pDataInfo->reg_dmc_plane_level8           = 0;           // 12 bit , format 10.2
    pDataInfo->reg_dmc_white_limit            = header.HIGH_BOUND;       // 12 bit , format 10.2
    pDataInfo->reg_dmc_dither_en              = 0x1;         //  2 bit , 0 : rounding , 1 : dither , 2~3 : truncate
    pDataInfo->reg_dmc_dither_rst_e_4_frame   = 0x0;         //  1 bit
    pDataInfo->reg_dmc_dither_pse_rst_num     = 0x0;         //  2 bit
    pDataInfo->bROI_en                        = 0x0;
    pDataInfo->iROI_hor_end_offset            = 0x0;
    pDataInfo->iROI_hor_start_offset          = 0x0;
    pDataInfo->iROI_ver_end_offset            = 0x0;
    pDataInfo->iROI_ver_start_offset          = 0x0;

    // Init Output message
    pbin_info->HNode          = n_Hnode;
    pbin_info->VNode          = n_Vnode;
    pbin_info->LevelCount     = pDataInfo->reg_dmc_plane_num;
    pbin_info->Blk_h_size     = 1 << (pDataInfo->reg_dmc_h_block);
    pbin_info->Blk_v_size     = 1 << (pDataInfo->reg_dmc_v_block);

    //dump_interface_info(pDataInfo);

    bRet = Alloc_LutIn_Space(pDataInfo, pbin_info);
    if (bRet == FALSE)
    {
        printf("Alloc_LutIn_Space error\n");
        return FALSE;
    }

    // Decode Vendor data
    bRet = CSOT_Himax_Decode_To_Mstar_Format(&header, (interface_info *)pDataInfo);
    if (bRet == FALSE)
    {
        printf("CSOT_Himax_Decode_To_Mstar_Format error\n");
        return FALSE;
    }

    #if defined (CONFIG_DEMURA_URSA13)
    MS_BOOL demura_enable[3]  = {TRUE, TRUE, TRUE};
    #else
    MS_BOOL demura_enable[4]  = {TRUE, TRUE, TRUE, TRUE};
    #endif

    pbin_info->reg_base_addr  =  0x7700;
    pbin_info->project_id     =  ((MSTAR_CHIP_ID << 16) + (1ULL << 15) + ID_CUS_MSTAR);
    pbin_info->data_type      =  IC_DRAM;
    pbin_info->Gain_type      =  10;
    pbin_info->Sep_type       =  pDataInfo->reg_dmc_rgb_mode;
    pbin_info->Build_Date     =  0x19021814;

    memcpy(pbin_info->channel_enable, demura_enable, sizeof(demura_enable));

    return TRUE;
}


