/*
* Copyright (C) 2021 ?2022 Texas Instruments Incorporated
*
* All rights reserved not granted herein.
* Limited License.
*
* Texas Instruments Incorporated grants a world-wide, royalty-free,
* non-exclusive license under copyrights and patents it now or
* hereafter owns or controls to make, have made, use, import, offer to
* sell and sell ("Utilize") this software subject to the terms herein.
* With respect to the foregoing patent license, such license is
* granted solely to the extent that any such patent is necessary to
* Utilize the software alone.  The patent license shall not apply to
* any combinations which include this software, other than
* combinations with devices manufactured by or for TI (?I Devices?.
* No hardware patent is licensed hereunder.
*
* Redistributions must preserve existing copyright notices and
* reproduce this license (including the above copyright notice and the
* disclaimer and (if applicable) source code license limitations
* below) in the documentation and/or other materials provided with the
* distribution
*
* Redistribution and use in binary form, without modification, are
* permitted provided that the following conditions are met:
*
*	* No reverse engineering, decompilation, or disassembly of this
*     software is permitted with respect to any software provided in
*     binary form.
*	* any redistribution and use are licensed by TI for use only with
*     TI Devices.
*	* Nothing shall obligate TI to provide you with source code for
*     the software licensed and provided to you in object code.
*
* If software source code is provided to you, modification and
* redistribution of the source code are permitted provided that the
* following conditions are met:
*
*   * any redistribution and use of the source code, including any
*     resulting derivative works, are licensed by TI for use only
*     with TI Devices.
*   * any redistribution and use of any object code compiled from the
*     source code and any resulting derivative works, are licensed by
*     TI for use only with TI Devices.
*
* Neither the name of Texas Instruments Incorporated nor the names of
* its suppliers may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* DISCLAIMER.
*
* THIS SOFTWARE IS PROVIDED BY TI AND TI? LICENSORS "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 TI AND TI? LICENSORS 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<stdio.h>
#include<stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "tasdevice.h"
#include "OSL_wrapper.h"
#include "tas2563_interface.h"
#include "tas2563.h"
#include "tas2563_ftc_lib.h"

#include "register_setting.h"
#ifdef PDM_ENABLE
#include "prm_pdm_tuning_mode.h"
#include "cfg0_pdm_music_COEFF.h"
#include "cfg1_pdm_calibration_COEFF.h"
#else
#include "prm_tuning_mode.h"
#include "cfg0_music_COEFF_prim.h"
#include "cfg2_Voice_COEFF_prim.h"
#include "cfg1_calibration_COEFF.h"
#endif
#define PIN_GPIO_60 (60)

static void *g_pTAS256X = NULL;

static t_libsccb_cfg h_tas256x_sccb[]={
	{
		.id = 0x4c, //TODO check tas256x i2c address
	},
};

static dspfw_runtime_info h_dspfw_runtime_info[]={
	{
		.mProfileId = -1,
		.bPrmOptimized = false,
	},
};

static smartamp_cfg h_smartamp_cfg[]={
	{
		.mnCurrentBook = -1,
		.mnCurrentPage = -1,
		.pMnDspfw_runtime_info = &h_dspfw_runtime_info[0],
		.pSccb_cfg = &h_tas256x_sccb[0]
	},
};

static void tas2563_write_calibrated_data(
	struct tas256x_priv* pTAS2563, int chn)
{
	struct os_interface* pOs_intf = pTAS2563->os_intf;
	int i = 0, rc = 0;
	unsigned char DevBlock[36];
	pTAS2563->nv_handle = &chn;
	/* write number of commands for calibration block */
	rc = pOs_intf->nv_read(DevBlock, sizeof(char), 36,
		pTAS2563->nv_handle );
	if(rc != 36) {
		dev_err("%s:%u:rc = %d read error\r\n",__func__,__LINE__,rc);
		return;
	}
	if(DevBlock[0] != 0x0) {
		for(i = 1; i < 36; i+=7) {
			rc = tas256x_dev_bulk_write(pTAS2563, chn,
				TAS256X_REG(DevBlock[i], DevBlock[i+1],
				DevBlock[i+2]),
				&DevBlock[i+3], 4);
			if(rc != 0) {
				dev_err("%s:%u:rc = %d read error\r\n",
					__func__,__LINE__,rc);
			}
		}
	} else {
		dev_info("%s:%u:No calibration can be read\r\n",
			__func__,__LINE__);
	}
}

int exTas256x_init(void)
{
	struct tas256x_priv* pTAS256X = NULL;
	int nRet = 0, chn = 0;
#ifdef PRM_OPTIMIZATION
	unsigned char *prmData = NULL;
	unsigned int len = 0;
#endif
	//number of dev
	unsigned char ndev = sizeof(h_smartamp_cfg)/sizeof(smartamp_cfg);
#ifdef PDM_ENABLE
	if(ndev != 1) {
		dev_err("%s:%u PDM mode only support one device!\n",__func__,
			__LINE__);
		nRet = -1;
		goto EXIT;
	}
#endif

	pTAS256X = (struct tas256x_priv*)calloc(1,
		sizeof(struct tas256x_priv));
	if (NULL == pTAS256X) {
		dev_err("%s:%u malloc error!\n",__func__, __LINE__);
		nRet = -1;
		goto EXIT;
	}

	pTAS256X->nv_handle = NULL; /*TODO:*/

	pTAS256X->ndev = ndev;

	pTAS256X->tasdevice = (void *)calloc(pTAS256X->ndev,
		sizeof(smartamp_cfg *));
	if(NULL == pTAS256X->tasdevice) {
		dev_err("%s:%u malloc error!\n",__func__, __LINE__);
		nRet = -1;
		goto EXIT;
	}
	for(chn = 0; chn < pTAS256X->ndev;chn++) {
		pTAS256X->tasdevice[chn] = &h_smartamp_cfg[chn];
	}

	pTAS256X->os_intf = tas256x_get_os_intf();

	g_pTAS256X = pTAS256X;
	//TODO:
	pTAS256X->os_intf->GPIO_config();

	for(chn = 0; chn < pTAS256X->ndev;chn++) {
#ifdef PRM_IN_NV
		prmData =
			pTAS256X->os_intf->prm_download(pTAS256X, chn, &len);
		if(prmData && len != 0) {
			smartamp_cfg *tasdev =
				(smartamp_cfg *)pTAS256X->tasdevice[chn];
			dspfw_runtime_info *pDspruntimeinfo
				= tasdev->pMnDspfw_runtime_info;
			tas256x_transmit_registers(pTAS256X, chn,
				(cfg_reg *)prmData, len);

			pDspruntimeinfo->bPrmOptimized = true;
			pTAS256X->os_intf->prm_remove(prmData);
		}
		prmData = NULL;
		len = 0;
#else
		tas256x_transmit_registers(pTAS256X, chn ,
			prm_tunning_mode_registers,
			sizeof(prm_tunning_mode_registers) /
			sizeof(prm_tunning_mode_registers[0]));

		tas2563_write_calibrated_data(pTAS256X,chn);
#endif
	}
EXIT:
	return nRet;
}

void exTas256x_deinit()
{
	struct tas256x_priv* pTAS256X = (struct tas256x_priv*)g_pTAS256X;

	if(pTAS256X != NULL) {
		if(NULL != pTAS256X->tasdevice) free(pTAS256X->tasdevice);
		free(pTAS256X);
	}
	g_pTAS256X = NULL;
}

void tas256x_dspon(struct tas256x_priv* pTAS256X,
	unsigned int profile)
{
	int i = 0;
	if(BYPASS == profile) return;
	for(i = 0; i < pTAS256X->ndev; i++) {
		smartamp_cfg *tasdev =
			(smartamp_cfg *)pTAS256X->tasdevice[i];
		dspfw_runtime_info *pDspruntimeinfo
			= tasdev->pMnDspfw_runtime_info;
		switch (profile) {
#ifdef PDM_ENABLE
		case MUSIC:
		case INDEPENDANT_CAPTURE:
		case MIXTURE_CAPTURE:
			if(!((pDspruntimeinfo->mProfileId == MUSIC)
				||(pDspruntimeinfo->mProfileId == INDEPENDANT_CAPTURE)
				||(pDspruntimeinfo->mProfileId == MIXTURE_CAPTURE))) {

				tas256x_transmit_registers(pTAS256X, i,
					cfg0_music_coeff_registers,
					sizeof(cfg0_music_coeff_registers) /
					sizeof(cfg0_music_coeff_registers[0]));

			}
#else
		case MUSIC:
			if(pDspruntimeinfo->mProfileId != MUSIC) {
				switch(i) {
#if defined STEREO
				case 0:
					tas256x_transmit_registers(pTAS256X, i,
						cfg0_music_coeff_registers_prim,
						sizeof(cfg0_music_coeff_registers_prim) /
						sizeof(cfg0_music_coeff_registers_prim[0]));
					break;
				case 1:
					tas256x_transmit_registers(pTAS256X, i,
						cfg0_music_coeff_registers_sec,
						sizeof(cfg0_music_coeff_registers_sec) /
						sizeof(cfg0_music_coeff_registers_sec[0]));
					break;
#elif defined WOOFER_TWEETERS
				case 0:
					tas256x_transmit_registers(pTAS256X, i,
						cfg0_music_coeff_registers_prim,
						sizeof(cfg0_music_coeff_registers_prim) /
						sizeof(cfg0_music_coeff_registers_prim[0]));
					break;
				case 1:
					tas256x_transmit_registers(pTAS256X, i,
						cfg0_music_coeff_registers_sec,
						sizeof(cfg0_music_coeff_registers_sec) /
						sizeof(cfg0_music_coeff_registers_sec[0]));
					break;
				case 2:
					tas256x_transmit_registers(pTAS256X, i,
						cfg0_music_coeff_registers_tert,
						sizeof(cfg0_music_coeff_registers_tert) /
						sizeof(cfg0_music_coeff_registers_tert[0]));
					break;
#elif defined FOUR_PAS
				case 0:
					tas256x_transmit_registers(pTAS256X, i,
						cfg0_music_coeff_registers_prim,
						sizeof(cfg0_music_coeff_registers_prim) /
						sizeof(cfg0_music_coeff_registers_prim[0]));
					break;
				case 1:
					tas256x_transmit_registers(pTAS256X, i,
						cfg0_music_coeff_registers_sec,
						sizeof(cfg0_music_coeff_registers_sec) /
						sizeof(cfg0_music_coeff_registers_sec[0]));
					break;
				case 2:
					tas256x_transmit_registers(pTAS256X, i,
						cfg0_music_coeff_registers_tert,
						sizeof(cfg0_music_coeff_registers_tert) /
						sizeof(cfg0_music_coeff_registers_tert[0]));
					break;
				case 3:
					tas256x_transmit_registers(pTAS256X, i,
						cfg0_music_coeff_registers_quat,
						sizeof(cfg0_music_coeff_registers_quat) /
						sizeof(cfg0_music_coeff_registers_quat[0]));
					break;
#else
				case 0:
					tas256x_transmit_registers(pTAS256X, i,
						cfg0_music_coeff_registers_prim,
						sizeof(cfg0_music_coeff_registers_prim) /
						sizeof(cfg0_music_coeff_registers_prim[0]));
					break;
#endif
				default:
					dev_err("%s:%u: no such channel(%d)\n", __func__,
						__LINE__, i);
					break;
				}
			}
#endif
			break;
#ifndef PDM_ENABLE
		case VOICE:
			if(pDspruntimeinfo->mProfileId != VOICE) {
				switch(i) {
#if defined STEREO
				case 0:
					tas256x_transmit_registers(pTAS256X, i,
						cfg2_voice_coeff_registers_prim,
						sizeof(cfg2_voice_coeff_registers_prim) /
						sizeof(cfg2_voice_coeff_registers_prim[0]));
					break;
				case 1:
					tas256x_transmit_registers(pTAS256X, i,
						cfg2_voice_coeff_registers_sec,
						sizeof(cfg2_voice_coeff_registers_sec) /
						sizeof(cfg2_voice_coeff_registers_sec[0]));
					break;
#elif defined WOOFER_TWEETERS
				case 0:
					tas256x_transmit_registers(pTAS256X, i,
						cfg2_voice_coeff_registers_prim,
						sizeof(cfg2_voice_coeff_registers_prim) /
						sizeof(cfg2_voice_coeff_registers_prim[0]));
					break;
				case 1:
					tas256x_transmit_registers(pTAS256X, i,
						cfg2_voice_coeff_registers_sec,
						sizeof(cfg2_voice_coeff_registers_sec) /
						sizeof(cfg2_voice_coeff_registers_sec[0]));
					break;
				case 2:
					tas256x_transmit_registers(pTAS256X, i,
						cfg2_voice_coeff_registers_tert,
						sizeof(cfg2_voice_coeff_registers_tert) /
						sizeof(cfg2_voice_coeff_registers_tert[0]));
					break;
#elif defined FOUR_PAS
				case 0:
					tas256x_transmit_registers(pTAS256X, i,
						cfg2_voice_coeff_registers_prim,
						sizeof(cfg2_voice_coeff_registers_prim) /
						sizeof(cfg2_voice_coeff_registers_prim[0]));
					break;
				case 1:
					tas256x_transmit_registers(pTAS256X, i,
						cfg2_voice_coeff_registers_sec,
						sizeof(cfg2_voice_coeff_registers_sec) /
						sizeof(cfg2_voice_coeff_registers_sec[0]));
					break;
				case 2:
					tas256x_transmit_registers(pTAS256X, i,
						cfg2_voice_coeff_registers_tert,
						sizeof(cfg2_voice_coeff_registers_tert) /
						sizeof(cfg2_voice_coeff_registers_tert[0]));
					break;
				case 3:
					tas256x_transmit_registers(pTAS256X, i,
						cfg2_voice_coeff_registers_quat,
						sizeof(cfg2_voice_coeff_registers_quat) /
						sizeof(cfg2_voice_coeff_registers_quat[0]));
					break;
#else
				case 0:
					tas256x_transmit_registers(pTAS256X, i,
						cfg2_voice_coeff_registers_prim,
						sizeof(cfg2_voice_coeff_registers_prim) /
						sizeof(cfg2_voice_coeff_registers_prim[0]));
					break;
#endif

				default:
					dev_err("%s:%u: no such channel(%d)\n", __func__,
						__LINE__, i);
					break;
				}
			}
			break;
#endif
		case CALIBRATION:
			if(pDspruntimeinfo->mProfileId != CALIBRATION)
				tas256x_transmit_registers(pTAS256X, i,
					cfg1_calibration_coeff_registers,
					sizeof(cfg1_calibration_coeff_registers) /
					sizeof(cfg1_calibration_coeff_registers[0]));
			break;
		default:
			dev_info("%s:%u: no such profile(%u)\n", __func__,
				__LINE__, profile);
		}
	}
}

void tas256x_poweron(struct tas256x_priv* pTAS256X,
	unsigned int profile)
{
	int i = 0;
	for(i = 0; i < pTAS256X->ndev; i++) {
		smartamp_cfg *tasdev =
			(smartamp_cfg *)pTAS256X->tasdevice[i];
		dspfw_runtime_info *pDspruntimeinfo
			= tasdev->pMnDspfw_runtime_info;
		switch (profile) {
		case MUSIC:
		case CALIBRATION:
			switch(i) {
			case 0:
				tas256x_transmit_registers(pTAS256X, i, power_up_prim,
					sizeof(power_up_prim) / sizeof(power_up_prim[0]));
				break;
			default:
				dev_err("%s:%u: no such channel(%d)\n", __func__,
					__LINE__, i);
				break;
			}
			break;
#ifndef PDM_ENABLE
		case VOICE:
			break;
#endif
#ifdef PDM_ENABLE
		case INDEPENDANT_CAPTURE:
			tas256x_transmit_registers(pTAS256X, i,
				standalone_record_on, sizeof(standalone_record_on) /
				sizeof(standalone_record_on[0]));
			break;
		case MIXTURE_CAPTURE:
			tas256x_transmit_registers(pTAS256X, i,
				mixture_playback_record,
				sizeof(mixture_playback_record) /
				sizeof(mixture_playback_record[0]));
			break;
#endif
		case BYPASS:
			tas256x_dev_update_bits(pTAS256X, i,TAS2563_REG_MISC_DSP,
				TAS2563_REG_MISC_DSP_ROM_MODE_MASK,
				TAS2563_REG_MISC_DSP_ROM_MODE_ROM_MODE);
			switch(i) {
			case 0:
				tas256x_transmit_registers(pTAS256X, i,
					bypass_power_up_prim,
					sizeof(bypass_power_up_prim) /
					sizeof(bypass_power_up_prim[0]));
				break;
#ifdef STEREO
			case 1:
				tas256x_transmit_registers(pTAS256X, i,
					bypass_power_up_sec,
					sizeof(bypass_power_up_sec) /
					sizeof(bypass_power_up_sec[0]));
				break;
#endif
			default:
				dev_err("%s:%u: no such channel(%d)\n", __func__,
					__LINE__, i);
				break;
			}
			break;
		}
		pDspruntimeinfo->mProfileId = profile;
	}
}

void *tasdevice_get_handle(void)
{
	return g_pTAS256X;
}

int exTas256x_speakeron(unsigned int profile)
{
	struct tas256x_priv* pTAS256X = (struct tas256x_priv*)g_pTAS256X;

	tas256x_dspon(pTAS256X, profile);

	tas256x_poweron(pTAS256X, profile);

	pTAS256X->dev_state = SMARTPA_POWERON;

	return 0;
}

void exTas256x_speakeroff()
{
	struct tas256x_priv* pTAS256X = (struct tas256x_priv*)g_pTAS256X;

	int chn=0;
	for(; chn < pTAS256X->ndev; chn++) {
		smartamp_cfg *tasdev =
			(smartamp_cfg *)pTAS256X->tasdevice[chn];
		dspfw_runtime_info *pDspruntimeinfo
			= tasdev->pMnDspfw_runtime_info;
#ifdef PDM_ENABLE
	if(INDEPENDANT_CAPTURE == pDspruntimeinfo->mProfileId
		||MIXTURE_CAPTURE == pDspruntimeinfo->mProfileId) {
		tas256x_dev_update_bits(pTAS256X,chn, TAS2563_PDM_CONFIG0,
		TAS2563_PDM_CONFIG0_PDM_GATE_PAD0_MASK,
		TAS2563_PDM_CONFIG0_PDM_GATE_PAD0_ENA);
	}
#endif
	tas256x_transmit_registers(pTAS256X,chn, shut_down,
	sizeof(shut_down) / sizeof(shut_down[0]));
	if(BYPASS == pDspruntimeinfo->mProfileId) {
		tas256x_dev_update_bits(pTAS256X,chn, TAS2563_REG_MISC_DSP,
		TAS2563_REG_MISC_DSP_ROM_MODE_MASK,
		TAS2563_REG_MISC_DSP_ROM_MODE_RAM_MODE);
	}
	}
	pTAS256X->dev_state = SMARTPA_POWEROFF;
	return;
}

/*For tas2563_wcsp, value in TAS256X_REG(0x0, 0x0, 0x7D) should be 0x0
  and TAS256X_REG(0x0, 0xFD, 0x7C) should always be 0x54.
  If 0x00 at TAS256X_REG(0x0, 0xFD, 0x7C) is read, unlock the page by
  writing 0x0d to TAS256x_reg(0x0, 0xfd, 0x0d). All writes/reads to
  page 0xfd needs to unlock the page by writing 0x0d to 0x0d register.
*/
int exTas256x_getchipid()
{
	struct tas256x_priv* pTAS256X = (struct tas256x_priv*)g_pTAS256X;
	int nPGID = 0, nDeviceID = 0, ret = 0;

	for(int chn=0;chn< pTAS256X->ndev;chn++)
	{
		ret = tas256x_dev_read(pTAS256X, chn, TAS2563_REG_REV_PGID,
			&nPGID);
		if(ret != 0) {
			dev_err("%s:%u: error\n", __func__, __LINE__);
		}
		//unlock Page0xFD
		ret = tas256x_dev_write(pTAS256X,chn, TAS2563_REG_UNLCK, 0xD);
		if(ret != 0) {
			dev_err("%s:%u: error\n", __func__, __LINE__);
		}
		ret = tas256x_dev_read(pTAS256X,chn, TAS2563_REG_DEVICEID,
			&nDeviceID);
		if(ret != 0) {
			dev_err("%s:%u: error\n", __func__, __LINE__);
		}
	}

	return ((nPGID << 8)|nDeviceID);
}

int main(void)
{
	int nRet = 0;
	nRet = exTas256x_init();
	if(nRet) {
		dev_err("%s:%u init error!\n",__func__, __LINE__);
		goto EXIT;
	}
	exTas256x_speakeron(BYPASS);

	exTas256x_deinit();
EXIT:
	return nRet;
}

/*
 * mask interrupts by disabling them
 */
static int tas_irq_mask(struct tas256x_priv *pTAS256X)
{

	//TODO

	return 0;
}

/*
 * unmask interrupts by enabling them again
 */
static int tas_irq_unmask(struct tas256x_priv *pTAS256X)
{
	//TODO:

	return 0;
}

void exTas256x_irq(void)
{
    int chn = 0;
	struct tas256x_priv* pTAS256X = (struct tas256x_priv*)g_pTAS256X;
	for(chn = 0; chn < pTAS256X->ndev;chn++) {
		smartamp_cfg *tasdev =
			(smartamp_cfg *)pTAS256X->tasdevice[chn];
		dspfw_runtime_info *pDspruntimeinfo
			= tasdev->pMnDspfw_runtime_info;
		pDspruntimeinfo->mProfileId = -1;
		tasdev->mnCurrentBook = -1;
		tasdev->mnCurrentPage = -1;
	}

	/*
	 * mask interrupts
	 * will be unmasked after handling interrupts in workqueue
	 */
	tas_irq_mask(pTAS256X);
	// TODO:
	tas_irq_unmask(pTAS256X);
	//TODO::

}
