/**
 * @file  mstdrv.c
 * @brief MST driver
 *
 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
 *
 * This software is proprietary of Samsung Electronics.
 * No part of this software, either material or conceptual may be copied
 * or distributed, transmitted, transcribed, stored in a retrieval system
 * or translated into any human or computer language in any form by any means,
 * electronic, mechanical, manual or otherwise, or disclosed to third parties
 * without the express written permission of Samsung Electronics.
 */

#include "drStd.h"
#include "DrApi/DrApiCommon.h"
#include "DrApi/DrApiFastCall.h"
#include "drMobicore.h"
#include "regs-gpio-mst.h"
#include "dbg.h"
#include "gpio-mst.h"
#include "mstdrv.h"
#include "fcTIMA.h"
//#include "fcUART.h"

/* flag to stop MST transmit when NFC works */
uint8_t g_nfc_status = 0;

/* represent NFC ON/OFF */
#define CMD_ID_NFC_START	0x01
#define CMD_ID_NFC_END	0x02

/* MST gpio vector table number */
static uint8_t GPIO_MST_MD = 0;
static uint8_t GPIO_MST_PD = 1;
static uint8_t GPIO_MST_PWR_EN = 2;

/* GPIO high/low */
#define GPIO_LEVEL_LOW 0
#define GPIO_LEVEL_HIGH 1

uint8_t gpio_mst_md_pin = 0;
uint8_t gpio_mst_pd_pin = 0;
uint8_t gpio_mst_pwr_en_pin = 0;

/* function for MST service */
uint32_t fcNFCAction(fastcall_registers_t regs) {
    uint32_t cmd_id = regs[1];

    switch (cmd_id) {
    	case CMD_ID_NFC_START:
    		g_nfc_status = 1;
    		break;
    	case CMD_ID_NFC_END:
    		g_nfc_status = 0;
    		break;
    	default:
    		//we got a wrong cmd_id
    		return 5;
    }
	return g_nfc_status;
}

void gpio_set_mst_gpio_pin(uint32_t pin_conf) {
#if defined(CONFIG_EXYNOS7420)
	if(pin_conf == ZERO_IN_MODE){
		gpio_mst_md_pin = 2;
		gpio_mst_pd_pin = 2;
		gpio_mst_pwr_en_pin = 2;
	}else{ //IN MODE, but pin number is different
		gpio_mst_md_pin = 2;
		gpio_mst_pd_pin = 1;
		gpio_mst_pwr_en_pin = 2;
	}

#elif defined(CONFIG_EXYNOS7580)
	gpio_mst_md_pin = 0; //mst_en
	gpio_mst_pd_pin = 1; //mst_data
	gpio_mst_pwr_en_pin = 0;

#elif defined(CONFIG_EXYNOS7885)
	gpio_mst_md_pin = 4; //mst_en
	gpio_mst_pd_pin = 2; //mst_data
	gpio_mst_pwr_en_pin = 1;

#elif defined(CONFIG_EXYNOS7880)
	gpio_mst_md_pin = 0; //mst_en
	gpio_mst_pd_pin = 1; //mst_data
	gpio_mst_pwr_en_pin = 3;

#elif defined(CONFIG_EXYNOS7870)
	gpio_mst_md_pin = 0; //mst_en
	gpio_mst_pd_pin = 1; //mst_data
	gpio_mst_pwr_en_pin = 2;

#elif defined(CONFIG_EXYNOS8890)
	if(pin_conf == HERO_PHASE_EN_MODE){
		gpio_mst_md_pin = 0; //mst_en
		gpio_mst_pd_pin = 1; //mst_data
		gpio_mst_pwr_en_pin = 0;

		GPIO_MST_MD = 0;
		GPIO_MST_PD = 0;
		GPIO_MST_PWR_EN = 2;
	}else{ // for grace model
		gpio_mst_md_pin = 2;
		gpio_mst_pd_pin = 2;
		gpio_mst_pwr_en_pin = 0;

		GPIO_MST_MD = 1; // mst_en
		GPIO_MST_PD = 0; // mst_data
		GPIO_MST_PWR_EN = 2;
	}

#elif defined(CONFIG_EXYNOS8895)
	gpio_mst_md_pin = 1; //mst_en
	gpio_mst_pd_pin = 2; //mst_data
	gpio_mst_pwr_en_pin = 0;

#elif defined(CONFIG_EXYNOS9810)
	gpio_mst_md_pin = 7; //mst_en
	gpio_mst_pd_pin = 6; //mst_data
	gpio_mst_pwr_en_pin = 4;
#endif
}

/* _____________NOBLE,ZERO2 PROJECT ONLY START_____________ */
int trans_loop_positive(int mst_val, int mst_stat, unsigned int baud_rate){
	if(mst_stat){
		gpio_set_mst_data_positive(GPIO_LEVEL_LOW);
		if(mst_val){
			udelay(baud_rate/2);
			gpio_set_mst_data_positive(GPIO_LEVEL_HIGH);
			udelay(baud_rate/2);
			mst_stat = 1;
		}else{
			udelay(baud_rate);
			mst_stat = 0;
		}
	}else{
		gpio_set_mst_data_positive(GPIO_LEVEL_HIGH);
		if(mst_val){
			udelay(baud_rate/2);
			gpio_set_mst_data_positive(GPIO_LEVEL_LOW);
			udelay(baud_rate/2);
			mst_stat = 0;
		}else{
			udelay(baud_rate);
			mst_stat = 1;
		}
	}
	return mst_stat;
}

int trans_loop_negative(int mst_val, int mst_stat, unsigned int baud_rate)
{
	if(mst_stat){
		gpio_set_mst_data_negative(GPIO_LEVEL_LOW); //md base (md -> low, pd ->high)
		if(mst_val){
			udelay(baud_rate/2);
			gpio_set_mst_data_negative(GPIO_LEVEL_HIGH);
			udelay(baud_rate/2);
			mst_stat = 1;
		}else{
			udelay(baud_rate);
			mst_stat = 0;
		}
	}else{
		gpio_set_mst_data_negative(GPIO_LEVEL_HIGH);
		if(mst_val){
			udelay(baud_rate/2);
			gpio_set_mst_data_negative(GPIO_LEVEL_LOW);
			udelay(baud_rate/2);
			mst_stat = 0;
		}else{
			udelay(baud_rate);
			mst_stat = 1;
		}
	}
	return mst_stat;
}
/* _____________NOBLE,ZERO2 PROJECT ONLY END_____________ */

int trans_loop(int i)
{
    if(i) {
/* add chipset config when MST support phase/en mode */
#if defined(CONFIG_EXYNOS7420)
        gpio_set_mst_data(GPIO_LEVEL_LOW, GPIO_MST_MD, gpio_mst_md_pin); //mst_en
#endif
		gpio_set_mst_data(GPIO_LEVEL_HIGH, GPIO_MST_PD, gpio_mst_pd_pin); //mst_data
        return 0;
    } 
	else {
        gpio_set_mst_data(GPIO_LEVEL_LOW, GPIO_MST_PD, gpio_mst_pd_pin); //mst_data
/* add chipset config when MST support phase/en mode */
#if defined(CONFIG_EXYNOS7420)
		gpio_set_mst_data(GPIO_LEVEL_HIGH, GPIO_MST_MD, gpio_mst_md_pin); //mst_en
#endif
        return 1;
    }
}

int fcMstTransmit(fastcall_registers_t regs, unsigned char * input, uint32_t pin_conf, uint8_t system_rev){	
	unsigned int baud_rate = (unsigned int)*(input+2) * 256 + (unsigned int)*(input+3);
	unsigned int total_bit_length = (unsigned int)*(input+4) * 256 + (unsigned int)*(input+5);
	unsigned char * loop_va = (unsigned char*)(input+6);

	if(total_bit_length > MAX_MST_TOTAL_BIT)
		return MST_MAX_BIT_OVERFLOW;
			
	int loi = 0;
	int loj = 0;
	int count = 0;
	int trans_cnt = 0;
	static uint8_t pn_flag = 0; //mst positive_negative flag
	if((pin_conf == NOBLE_ZERO2_IN_MODE) && system_rev == 3){
		pn_flag = 1;
	}

/*
	int i;
	uart_psd("pin_conf = ", pin_conf);
	uart_psd("baud_rate = ", baud_rate);
	uart_psd("total_bit_length = ", total_bit_length);
	for(i=0; i<(total_bit_length/8)+1; i++)
		uart_psd(" : ",loop_va[i]);
*/
	gpio_set_mst_gpio_pin(pin_conf);
	gpio_set_mst_init(pin_conf, gpio_mst_md_pin, gpio_mst_pd_pin, gpio_mst_pwr_en_pin);

	gpio_set_mst_data(GPIO_LEVEL_LOW, GPIO_MST_MD, gpio_mst_md_pin);
	gpio_set_mst_data(GPIO_LEVEL_LOW, GPIO_MST_PD, gpio_mst_pd_pin);
#if !defined(TIMA_MST_MFC_CHIP)
	gpio_set_mst_data(GPIO_LEVEL_LOW, GPIO_MST_PWR_EN, gpio_mst_pwr_en_pin);		
	udelay(3000);
	gpio_set_mst_data(GPIO_LEVEL_HIGH, GPIO_MST_PWR_EN, gpio_mst_pwr_en_pin);
#endif
	udelay(3000);
/* add chipset config when MST support phase/en mode */
#if !defined(CONFIG_EXYNOS7420)
	gpio_set_mst_data(GPIO_LEVEL_HIGH, GPIO_MST_MD, gpio_mst_md_pin); //mst_en
#endif
	if((pin_conf == NOBLE_ZERO2_IN_MODE) && system_rev == 3)
		gpio_set_mst_data(GPIO_LEVEL_HIGH, GPIO_MST_PD, gpio_mst_pwr_en_pin);

	udelay(7000);

	loi = 0;
	count = 0;
	do{
		// check if an NFC is coming in
		if (g_nfc_status)
			goto nfc_exit;
		for(loj = 7 ; loj >= 0 ; loj--) {
			if(((loop_va[loi] & (1 << loj)) >> loj) == 1) {
				if(pin_conf != NOBLE_ZERO2_IN_MODE){
					trans_cnt = trans_loop(trans_cnt);
					udelay(baud_rate/2);
					trans_cnt = trans_loop(trans_cnt);
					udelay(baud_rate/2);
					count++;
				}else{ /* _____________NOBLE,ZERO2 PROJECT ONLY START_____________ */
					if(pn_flag == 1)
						trans_cnt = trans_loop_positive(GPIO_LEVEL_HIGH, trans_cnt, baud_rate);
					else
						trans_cnt = trans_loop_negative(GPIO_LEVEL_HIGH, trans_cnt, baud_rate);
				} /* _____________NOBLE,ZERO2 PROJECT ONLY END_____________ */
			}else{
				if(pin_conf != NOBLE_ZERO2_IN_MODE){
					trans_cnt = trans_loop(trans_cnt);
					udelay(baud_rate);
					count++;
				}else{ /* _____________NOBLE,ZERO2 PROJECT ONLY START_____________ */
					if(pn_flag == 1)
						trans_cnt = trans_loop_positive(GPIO_LEVEL_LOW, trans_cnt, baud_rate);
					else
						trans_cnt = trans_loop_negative(GPIO_LEVEL_LOW, trans_cnt, baud_rate);
				} /* _____________NOBLE,ZERO2 PROJECT ONLY END_____________ */
			}
			
			if(count == total_bit_length)
				break;
		}
		loi++;
	}
	while ( loi < (total_bit_length/8)+1 );

	gpio_set_mst_data(GPIO_LEVEL_LOW, GPIO_MST_MD, gpio_mst_md_pin);
	gpio_set_mst_data(GPIO_LEVEL_LOW, GPIO_MST_PD, gpio_mst_pd_pin);
#if !defined(TIMA_MST_MFC_CHIP)
	gpio_set_mst_data(GPIO_LEVEL_LOW, GPIO_MST_PWR_EN, gpio_mst_pwr_en_pin); 
#endif
	gpio_set_mst_exit(pin_conf, gpio_mst_md_pin, gpio_mst_pd_pin, gpio_mst_pwr_en_pin);
	udelay(3000);
	
	return MST_SUCCESS;

	nfc_exit:
	gpio_set_mst_data(GPIO_LEVEL_LOW, GPIO_MST_MD, gpio_mst_md_pin);
	gpio_set_mst_data(GPIO_LEVEL_LOW, GPIO_MST_PD, gpio_mst_pd_pin);
#if !defined(TIMA_MST_MFC_CHIP)
	gpio_set_mst_data(GPIO_LEVEL_LOW, GPIO_MST_PWR_EN, gpio_mst_pwr_en_pin); 
#endif
	gpio_set_mst_exit(pin_conf, gpio_mst_md_pin, gpio_mst_pd_pin, gpio_mst_pwr_en_pin);
	udelay(3000);
	
	// reset NFC flag here, just in case NFC app failed to do so
	g_nfc_status = 0;

	return MST_NFC_EXIT;
}

