// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
/*
 * Copyright (c) 2021 MediaTek Inc.
 */

#include <common.h>
#include <div64.h>
#include <dm.h>
#include <i2c.h>
#include <asm/gpio.h>
#include <malloc.h>
#include <string.h>
#include <sony_customer.h>
#include <debug_impl.h>
#include <environment.h>
#include <command.h>
#include <utility.h>
#include <dm/pinctrl.h>
#include <ecinfo.h>
#include <time.h>
#include <adc.h>

char panelID[PANEL_ID_LENGTH+5] = {0};
char vcomData[VCOM_VALID_DATA_LENGTH] = {0};
char vcomData_char[VCOM_VALID_DATA_LENGTH] = {0};
unsigned int vcomCrcResult = -1;
int panel_less = 0;
int panel_id_err = 1;
//int tcon_less = 0;
int s_boot_status = 0;
unsigned long start_time;
const char *paneless_prefix = "Y_PNLLESS_4K_T";

void is_panelless_disable_error_detect_to_bootargs(void)
{
	if (panelID != NULL) {
		if (strncmp(panelID, paneless_prefix, strlen(paneless_prefix))== 0) {
			UBOOT_INFO("panelID start with Y_PNLLESS_4K_T, disable error detection.\n");
			add_bootargs(SHUTDOWN_DISABLE_KEY, SHUTDOWN_DISABLE, 0);
		}
	}
	return;
}

void set_pnlless_id(void)
{
    if (panel_less) {//low for 60HZ, high for 120HZ
        strncpy(panelID, PNLLESS_ID_120HZ, PANEL_ID_LENGTH+4);
        panelID[PANEL_ID_LENGTH+4] = '\0';
    } else {
        strncpy(panelID, PNLLESS_ID_60HZ, PANEL_ID_LENGTH);
        panelID[PANEL_ID_LENGTH] = '\0';
    }
}

void customer_start_time(void)
{
	start_time = get_timer(0);
}

unsigned long customer_end_time(void)
{
	return (get_timer(0) - start_time);
}

unsigned long customer_panel_vcc_time(void)
{
	return (get_timer(0) - start_time);
}

// x8+x2+x1+x0->0x07
char cal_vcom_crc(char *data, unsigned char len)
{
	unsigned char i;
	char crc = 0x0;

	while (len--)
	{
		crc ^= *data++;
		for(i=8; i>0; --i)
		{
			if (crc & 0x80)
			{
				crc = (crc << 1) ^ 0x07;
			}else{
				crc = (crc << 1);
			}
		}
	}

	return crc;
}

int sony_spi_init_flash(void)
{
	char cmd[256];
	int ret = 0;
	//int count =0;

	memset(cmd, 0, sizeof(cmd));
	if (snprintf(cmd, sizeof(cmd), "sf probe %s %d %d", CONFIG_DEMURA_SPI_BUS, DEFAULT_SPI_SPEED, DEFAULT_SPI_MODE) < 0)
	{
		return SPI_FLASH_NOT_EXIST;
	}else{
		UBOOT_INFO("sf probe %s %d %d \n", CONFIG_DEMURA_SPI_BUS, DEFAULT_SPI_SPEED, DEFAULT_SPI_MODE);
	}
/*
	while (count < READ_TIMES) {
		ret = run_command(cmd, 0);
		if (ret != 0) {
			UBOOT_ERROR("command(%s) error, continue read times: %d / Max times: %d!!\n", cmd, count, READ_TIMES);
			ret = SPI_FLASH_NOT_EXIST;
		}
		else {
			UBOOT_INFO("command(%s) success!\n", cmd);
			break;
		}
		count++;
	}
*/
	ret = run_command(cmd, 0);
	if (ret != 0) {
		UBOOT_ERROR("command(%s) error!\n", cmd);
		ret = SPI_FLASH_NOT_EXIST;
	}
	else {
		UBOOT_INFO("command(%s) success!\n", cmd);
	}
	return ret;
}

int sony_spi_write_panel_id(struct udevice *dev, char* panel_id)
{
	char cmd[256];
	int len;

	if (sony_spi_init_flash() == SPI_FLASH_NOT_EXIST)
	{
		UBOOT_ERROR("spi flash not exist!\n");
		return SPI_FLASH_NOT_EXIST;
	}

	if (panel_id != NULL)
	{
		len = (int)strlen(panel_id);
		if (len > PANEL_ID_LENGTH+4)
		{
			len = PANEL_ID_LENGTH+4;
		}

		memset(cmd, 0, sizeof(cmd));
		if (snprintf(cmd, sizeof(cmd), "sf update %p 0x%x 0x%x", panel_id, PANEL_ID_OFFSET, len) < 0)
		{
			return -1;
		}else{
			UBOOT_INFO("sf read %p 0x%x 0x%x \n", panel_id, PANEL_ID_OFFSET, len);
		}
		if (run_command(cmd, 0) != 0)
		{
			UBOOT_ERROR("command(%s) error\n", cmd);
			return -1;
		}
	}

	return 0;
}

/*
static int sony_i2c_write(struct udevice *dev, __u8 *msg_buf, __u8 size)
{
	struct sony_customer_priv *priv = dev_get_priv(dev);
	struct i2c_msg msgs;

	msgs.addr  = priv->i2c_addr;
	msgs.buf   = msg_buf;
	msgs.len   = size;
	msgs.flags = 0;

	return dm_i2c_xfer(dev, &msgs, 1);
}

static int sony_i2c_read(struct udevice *dev, __u8 **msg_buf, __u8 size)
{
	struct sony_customer_priv *priv = dev_get_priv(dev);
	struct i2c_msg msgs;
	__u8 *buf;
	int ret;

	buf = (__u8 *)malloc(size);
	if (buf == NULL) {
		UBOOT_ERROR("[PANEL] i2c_raed Error: malloc failed!\n");
		return -1;
	}

	msgs.addr  = priv->i2c_addr;
	msgs.buf   = buf;
	msgs.len   = size;
	msgs.flags = I2C_M_RD;

	ret = dm_i2c_xfer(dev, &msgs, 1);
	if (ret < 0)
		return ret;

	strncpy((char *)*msg_buf, (char *)buf, size);
	free(buf);

	return 0;
}

int sony_i2c_write_panel_id(struct udevice *dev, char* panel_id)
{
	struct sony_customer_priv *priv = dev_get_priv(dev);
	__u8 msg_buf[PANEL_ID_LENGTH+REG_LENGTH+1] = {0};
	int ret, i;
	long len;

	//panel_b_int status
	//high: read/write eeprom
	//low: read eeprom
	//default: low
	dm_gpio_set_value(&priv->panel_b_int, 1);

	if (panel_id != NULL)
	{
		msg_buf[0] = PANEL_ID_REG_FIRST;
		msg_buf[1] = PANEL_ID_REG_SECOND;
		len = (long)strlen(panel_id);
		if (len > PANEL_ID_LENGTH)
		{
			len = PANEL_ID_LENGTH;
		}
		for(i=0; i<len; i++)
		{
			msg_buf[i+REG_LENGTH] = (__u8)*panel_id;
			panel_id++;
		}
		msg_buf[len+REG_LENGTH] = '\0';
		ret = sony_i2c_write(dev, msg_buf, len+REG_LENGTH+1);
		UBOOT_INFO("[PANEL] write panel id ret:%d\n", ret);
		if (ret < 0)
			return ret;
	}

	dm_gpio_set_value(&priv->panel_b_int, 0);

	return 0;
}

int sony_i2c_write_vcom_data(struct udevice *dev, long vcom_data)
{
	struct sony_customer_priv *priv = dev_get_priv(dev);
	__u8 msg_buf[VCOM_DATA_LENGTH+REG_LENGTH] = {0};
	int ret, i;

	//panel_b_int status
	//high: read/write eeprom
	//low: read eeprom
	//default: low
	dm_gpio_set_value(&priv->panel_b_int, 1);

	msg_buf[0] = VCOM_DATA_REG_FIRST;
	msg_buf[1] = VCOM_DATA_REG_SECOND;
	for(i=0; i<VCOM_DATA_LENGTH; i++)
	{
		msg_buf[i+REG_LENGTH] = (__u8)vcom_data;
	}
	ret = sony_i2c_write(dev, msg_buf, VCOM_DATA_LENGTH+REG_LENGTH);
	UBOOT_INFO("[PANEL] write vcom data ret:%d\n", ret);
	if (ret < 0)
		return ret;

	dm_gpio_set_value(&priv->panel_b_int, 0);

	return 0;
}
*/
static int sony_spi_read_panel_id(struct udevice *dev)
{
	char cmd[256];
	char *cur_msg_buf;
	char crc_data = 0, crc_result = 0;
	int i;

	if (sony_spi_init_flash() == SPI_FLASH_NOT_EXIST)
	{
		UBOOT_ERROR("command(%s) error!\n", cmd);
		return SPI_FLASH_NOT_EXIST;
	}else{
		cur_msg_buf = (char *)malloc(PANEL_ID_LENGTH+5);
		if (cur_msg_buf == NULL) {
			UBOOT_ERROR("[PANEL] panel_ID Error: malloc failed!\n");
			return -1;
		}
		memset(cur_msg_buf, 0, PANEL_ID_LENGTH+5);
		memset(cmd, 0, sizeof(cmd));
		if (snprintf(cmd, sizeof(cmd), "sf read %p 0x%x 0x%x", cur_msg_buf, PANEL_ID_OFFSET, PANEL_ID_LENGTH+4) < 0)
		{
			free(cur_msg_buf);
			return -1;
		}else{
			UBOOT_INFO("sf read %p 0x%x 0x%x \n", cur_msg_buf, PANEL_ID_OFFSET, PANEL_ID_LENGTH+4);
		}
		if (run_command(cmd, 0) != 0)
		{
			UBOOT_ERROR("command(%s) error\n", cmd);
			free(cur_msg_buf);
			return -1;
		}

		crc_data = cur_msg_buf[0];
		// Calculate CRC using panelID data in cur_msg_buf, do not assign panelID yet
		crc_result = cal_vcom_crc(&cur_msg_buf[4], PANEL_ID_LENGTH);

		UBOOT_INFO("[PANEL] get cur_msg_buf string :%s crc_result:%c(0x%x)\n", cur_msg_buf, crc_result, crc_result);

		// Only assign panelID if CRC check passes
		if (crc_result == crc_data) {
			for (i = 0; i < PANEL_ID_LENGTH; i++) {
				panelID[i] = cur_msg_buf[i + 4];
			}
			panelID[PANEL_ID_LENGTH] = '\0';
			free(cur_msg_buf);

			if (check_panel_id_format(panelID, PANEL_ID_LENGTH) == 0) {
				UBOOT_DEBUG("[PANEL] get panel ID string from spi flash panelID:%s\n", panelID);
				panel_id_err = 0;
				return 0;
			} else {
				UBOOT_ERROR("[PANEL] panel ID format invalid!\n");
				panel_id_err = 1;
				memset(panelID, 0, sizeof(panelID));
				return -1;
			}
		} else {
			UBOOT_ERROR("[PANEL] panel ID CRC check failed! CRC:0x%x, Data:0x%x\n", crc_result, crc_data);
			panel_id_err = 1;
			memset(panelID, 0, sizeof(panelID));
			free(cur_msg_buf);
			return -1;
		}
	}
}
#if 0
static int sony_i2c_read_panel_id(struct udevice *dev)
{
	__u8 msg_buf[REG_LENGTH] = {0};
	int ret;
	__u8 *pre_msg_buf;
	__u8 *cur_msg_buf;
	int i, j;
	int same_times = 0, panel_id_error = 1;
	long panel_id_length = 0;

	pre_msg_buf = (__u8 *)malloc(PANEL_ID_LENGTH);
	if (pre_msg_buf == NULL) {
		UBOOT_ERROR("[PANEL] panel_ID Error: malloc failed!\n");
		return -1;
	}
	memset(pre_msg_buf, 0, PANEL_ID_LENGTH);

	cur_msg_buf = (__u8 *)malloc(PANEL_ID_LENGTH+1);
	if (cur_msg_buf == NULL) {
		UBOOT_ERROR("[PANEL] panel_ID Error: malloc failed!\n");
		free(pre_msg_buf);
		return -1;
	}

	for(i=0; i<READ_TIMES; i++)
	{
		memset(cur_msg_buf, 0, PANEL_ID_LENGTH);
		msg_buf[0] = PANEL_ID_REG_FIRST;
		msg_buf[1] = PANEL_ID_REG_SECOND;

		ret = sony_i2c_write(dev, msg_buf, REG_LENGTH);
		if (ret < 0)
		{
			panel_id_error = 1;
			UBOOT_ERROR("[PANEL] i2c write Error: %d!\n", ret);
			continue;
		}

		ret = sony_i2c_read(dev, &cur_msg_buf, PANEL_ID_LENGTH);
		if (ret < 0)
		{
			UBOOT_ERROR("[PANEL] i2c read Error: %d!\n", ret);
			panel_id_error = 1;
			continue;
		}
		panel_id_length = strlen((char *)cur_msg_buf);
		if (panel_id_length > PANEL_ID_LENGTH)
			panel_id_length = PANEL_ID_LENGTH;
		UBOOT_INFO("[PANEL] get panel ID string :%s len:%ld \n",cur_msg_buf, panel_id_length);

		//Panel ID string length must more than 8
		if (((strncmp((char *)pre_msg_buf, (char *)cur_msg_buf, panel_id_length)) == 0) && (panel_id_length > 8))
		{
			same_times++;
			if(same_times == 2)
			{
				strncpy(panelID, (char *)cur_msg_buf, panel_id_length);
				UBOOT_INFO("[PANEL] read panel ID:%s \n", panelID);
				panel_id_error = 0;
				for(j=0; j<panel_id_length; j++)
				{
					UBOOT_INFO("[PANEL] read panel ID[%d]:0x%x \n", j, panelID[j]);
					if(((panelID[j] >= 0x30) && (panelID[j] <= 0x39))  ||  //0-9
					   ((panelID[j] >= 0x41) && (panelID[j] <= 0x5A)) ||   //A-Z
					   ((panelID[j] >= 0x61) && (panelID[j] <= 0x7A)) ||   //a-z
					   (panelID[j] == 0x5F))                              //'_'
					{
						continue;
					}else{
						panel_id_error = 1;
						break;
					}
				}
				if (panel_id_error == 0)
				{
					break;
				} else {
					same_times = 0;
					continue;
				}
			}
		}else{
			same_times = 0;
		}
		strncpy((char *)pre_msg_buf, (char *)cur_msg_buf, panel_id_length);
	}

	if (panel_id_error == 1)
	{
		add_bootargs(PANEL_ID_ERROR_KEY, PANEL_ID_ERROR, 0);
		UBOOT_INFO("[PANEL] could not get panel ID string\n");
	}else{
		add_bootargs(PANEL_ID_ERROR_KEY, PANEL_ID_NO_ERROR, 0);
	}

	free(cur_msg_buf);
	free(pre_msg_buf);

	return 0;
}

static int sony_i2c_read_vcom_data(struct udevice *dev)
{
	__u8 msg_buf[REG_LENGTH] = {0};
	int ret;
	__u8 *pre_msg_buf;
	__u8 *cur_msg_buf;
	int i;
	int same_times = 0;
	long vcom_data_length = 0;
	int vcom_offset = VCOM_DATA_OFFSET, vcom_size;
	char crcdata;

	pre_msg_buf = (__u8 *)malloc(VCOM_DATA_LENGTH);
	if (pre_msg_buf == NULL) {
		UBOOT_ERROR("[PANEL] panel_ID Error: malloc failed!\n");
		return -1;
	}
	memset(pre_msg_buf, 0, VCOM_DATA_LENGTH);

	cur_msg_buf = (__u8 *)malloc(VCOM_DATA_LENGTH+1);
	if (cur_msg_buf == NULL) {
		UBOOT_ERROR("[PANEL] panel_ID Error: malloc failed!\n");
		free(pre_msg_buf);
		return -1;
	}

	for(i=0; i<READ_TIMES; i++)
	{
		memset(cur_msg_buf, 0, VCOM_DATA_LENGTH);
		msg_buf[0] = VCOM_DATA_REG_FIRST;
		msg_buf[1] = VCOM_DATA_REG_SECOND;

		ret = sony_i2c_write(dev, msg_buf, REG_LENGTH);
		if (ret < 0)
			continue;

		ret = sony_i2c_read(dev, &cur_msg_buf, VCOM_DATA_LENGTH);
		if (ret < 0)
			continue;
		vcom_data_length = strlen((char *)cur_msg_buf);
		if (vcom_data_length > VCOM_DATA_LENGTH)
			vcom_data_length = VCOM_DATA_LENGTH;

		UBOOT_INFO("[PANEL] get VCOM DATA :0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x len:%ld\n", cur_msg_buf[0], cur_msg_buf[1], cur_msg_buf[2], cur_msg_buf[3], cur_msg_buf[4], cur_msg_buf[5], cur_msg_buf[6], cur_msg_buf[7], vcom_data_length);

		if (strncmp((char *)pre_msg_buf, (char *)cur_msg_buf, vcom_data_length) == 0)
		{
			same_times++;
			if(same_times == 2)
			{
				vcom_size = cur_msg_buf[0];
				for(i=0; i<vcom_size; i++)
				{
					vcomData[i] = cur_msg_buf[vcom_offset++];
					UBOOT_DEBUG("get vcom data vcomData_int[%d]:0x%x \n", i, vcomData[i]);
				}

				crcdata = cal_vcom_crc(vcomData, vcom_size);
				UBOOT_DEBUG("vcom check crcdata:0x%x    read crcdata:0x%x\n", crcdata, cur_msg_buf[VCOM_CRC_OFFSET]);
				if (crcdata == cur_msg_buf[VCOM_CRC_OFFSET])
				{
					vcomCrcResult = 0;
				}

				free(cur_msg_buf);
				free(pre_msg_buf);
				return 0;
			}
		}else{
			same_times = 0;
		}

		strncpy((char *)pre_msg_buf, (char *)cur_msg_buf, vcom_data_length);
	}

	UBOOT_DEBUG("[PANEL] could not get VCOM DATA\n");
	free(cur_msg_buf);
	free(pre_msg_buf);

	return 0;
}
#endif
void init_sony_panel_id(struct udevice *dev)
{
	UBOOT_INFO("init_sony_panel_id panel_less:%d,s_boot_status:%d\n", panel_less, s_boot_status);
    if (s_boot_status) {//s_boot high panelless
        set_pnlless_id();
        panel_id_err = 0;
    } else {
        int spi_ret = sony_spi_read_panel_id(dev);
        if (spi_ret == SPI_FLASH_NOT_EXIST) {
            UBOOT_ERROR("SPI read panelID fail!\n");
            //set_pnlless_id();
            panel_id_err = 1;
        } else if (strncmp(panelID, paneless_prefix, strlen(paneless_prefix))== 0){
            UBOOT_INFO("SPI read panelID start with Y_PNLLESS_4K_T.\n");
            set_pnlless_id();
        } else {
            UBOOT_DEBUG("SPI read panelID panelID:%s\n", panelID);
        }
    }

    add_bootargs(PANEL_ID_ERROR_KEY, panel_id_err ? PANEL_ID_ERROR : PANEL_ID_NO_ERROR, 0);

	if (panelID != NULL)
	{
		UBOOT_INFO("init_sony_panel_id panelID:%s\n", panelID);
		env_set(PANEL_ID, panelID);
		env_save();
	}
}

void init_sony_pmux_cust(struct udevice *dev, char *pinctrl_name)
{
	int ret;
	ret = pinctrl_select_state(dev, pinctrl_name);
	UBOOT_INFO("init_sony_pmux_cust pinctrl_name:%s ret:%d\n", pinctrl_name, ret);
}

#if 0
void init_sony_pmux(struct udevice *dev)
{
	int ret;
	char * ec_dimming_key = EC_DIMMING_CONFIG;
	char * ec_damp_key = EC_DAMP_CONFIG;
	char * ec_mic_key = EC_MIC_CONFIG;
	char * ec_tuner_key = EC_TUNER_CONFIG;
    char * ec_value = (char *) malloc(MAX_VALUE_LENGTH);

#if CONFIG_PINCTRL
	/*dimming pmux*/
	memset(ec_value, 0, MAX_VALUE_LENGTH);
	ret = get_ec_value(ec_dimming_key, ec_value, MAX_VALUE_LENGTH);
	if (ret < 0)
	{
		UBOOT_ERROR("Error: get ec value [%s] failure\n", ec_dimming_key);
		free(ec_value);
		return ;
	}
	UBOOT_DEBUG("ec_key:%s ec_value:%s!\n", ec_dimming_key, ec_value);
	if ( pm_check_back_ground_active() == 0)
	{
		if (strcmp(ec_value, EC_LOCAL_DIMMING_VALUE) == 0)
		{
			pinctrl_select_state(dev, PMUX_LOCAL_DIMMING);
		}else{
			pinctrl_select_state(dev, PMUX_ZERO_DIMMING);
		}
	}else{
		UBOOT_ERROR("QHB mode skip set pinmux\n");
	}

	/*damp pmux*/
	memset(ec_value, 0, MAX_VALUE_LENGTH);
	ret = get_ec_value(ec_damp_key, ec_value, MAX_VALUE_LENGTH);
	if (ret < 0)
	{
		UBOOT_ERROR("Error: get ec value [%s] failure\n", ec_damp_key);
		free(ec_value);
		return ;
	}
	UBOOT_DEBUG("ec_key:%s ec_value:%s!\n", ec_damp_key, ec_value);
	if ((strcmp(ec_value, EC_DAMP2_SW_VALUE) == 0) || (strcmp(ec_value, EC_DAMP2_TW_VALUE) == 0))
	{
		pinctrl_select_state(dev, PMUX_DAMP);
	}

	/*mic pmux*/
	memset(ec_value, 0, MAX_VALUE_LENGTH);
	ret = get_ec_value(ec_mic_key, ec_value, MAX_VALUE_LENGTH);
	if (ret < 0)
	{
		UBOOT_ERROR("Error: get ec value [%s] failure\n", ec_mic_key);
		free(ec_value);
		return ;
	}
	UBOOT_DEBUG("ec_key:%s ec_value:%s!\n", ec_mic_key, ec_value);
	if ((strcmp(ec_value, "True") == 0) || (strcmp(ec_value, "true") == 0))
	{
		pinctrl_select_state(dev, PMUX_MIC);
	}

	/*tuner pmux*/
	memset(ec_value, 0, MAX_VALUE_LENGTH);
	ret = get_ec_value(ec_tuner_key, ec_value, MAX_VALUE_LENGTH);
	if (ret < 0)
	{
		UBOOT_ERROR("Error: get ec value [%s] failure\n", ec_tuner_key);
		free(ec_value);
		return ;
	}
	UBOOT_DEBUG("ec_key:%s ec_value:%s!\n", ec_tuner_key, ec_value);

	if (strcmp(ec_value, EC_TUNER_T2S2_VALUE) == 0)
	{
		pinctrl_select_state(dev, PMUX_TUNER_T2S2);
	}else if (strcmp(ec_value, EC_TUNER_ATSC3_VALUE) == 0){
		pinctrl_select_state(dev, PMUX_TUNER_ATSC3);
	}

	UBOOT_DEBUG("tconless status:%d!\n", get_tconless_status());
	/*tconless pmux*/
	if (get_tconless_status())
	{
		pinctrl_select_state(dev, PMUX_TCONLESS);
	}

#endif
	return;
}
#endif

// vcom.json format
//	{
//		"PmicType": "01"
//		"Vcom_Data": "FFFF"
//		"VcomCheckSum": "FF"
//	}
void init_sony_vcom_data(struct udevice *dev)
{
	int vcom_size, vcom_data_len, i, ret;
	char crcCalData, crcData;
	char * ec_value = (char *) malloc(MAX_VALUE_LENGTH);
	char * ec_vcom_data_key = EC_VCOM_DATA_VALUE;
	char * ec_vcom_crc_key = EC_VCOM_CRC_VALUE;
	char * ec_vcom_json_type_key = EC_VCOM_JSON_TYPE_VALUE;
	char * buf = (char *) malloc(MAX_VALUE_LENGTH);

	ret = ExGetJsonValue("EC_VCOM", ec_vcom_json_type_key, ec_value, 0);
	if ((ret == 0) && (strcmp(ec_value, "") != 0) && (ec_value[0] != '\0'))
	{
		UBOOT_DEBUG("vcom.json %s:%s\n", ec_vcom_json_type_key, ec_value);
	} else {
		printf("Vcom type Json Input \"%s\" \"%s\"\n", ec_vcom_json_type_key, ec_value);
	}

	ret = ExGetJsonValue("EC_VCOM", ec_vcom_data_key, ec_value, 0);
	if ((ret == 0) && (strcmp(ec_value, "") != 0) && (ec_value[0] != '\0'))
	{
		vcom_data_len = strlen(ec_value);
		vcom_size = vcom_data_len/2 ;
		for (i=0; i<vcom_size; i++)
		{
			strncpy(buf, ec_value, 2);
			vcomData_char[i] = (char)simple_strtoul(buf, NULL, 16);
			UBOOT_DEBUG("vcomData_char[%d]:0x%x\n", i, vcomData_char[i]);
			ec_value += 2;
		}

		ret = ExGetJsonValue("EC_VCOM", ec_vcom_crc_key, ec_value, 0);
		if ((ret == 0) && (strcmp(ec_value, "") != 0) && (ec_value[0] != '\0'))
		{
			crcCalData = cal_vcom_crc(vcomData_char, vcom_size);
			crcData = (char)simple_strtoul(ec_value, NULL, 16);
			if (crcData == crcCalData)
			{
				vcomCrcResult = 0;
			}
			UBOOT_DEBUG("crcData:0x%x crcCalData:0x%x\n", crcData, crcCalData);
		} else {
			printf("Vcom CRC Json Input \"%s\" \"%s\"\n", ec_vcom_crc_key, ec_value);
		}
	} else {
		printf("Vcom Data Json Input \"%s\" \"%s\"\n", ec_vcom_data_key, ec_value);
	}
}

#if 0
// vcom.hex format
//       1         2          3         4      5     6     7
//  data size  reserved   reserved  reserved  data  data  crc
void init_sony_vcom_data(struct udevice *dev)
{
	unsigned char *file_buf, buf[5]={0};
	loff_t size;
	int i, j, k;
	bool first_blank=false;
	char vcomdata[VCOM_DATA_LENGTH] = {0}, crcdata;
	int vcom_offset = VCOM_DATA_OFFSET, vcom_size;

	file_buf = read_storage_file_to_memory(PERSIST_PART, VCOM_FILE_PATH, &size);
	if (file_buf == NULL)
	{
		UBOOT_ERROR("Error: Read persist /config/vcom.hex file to DRAM failure\n");
		file_buf = read_storage_file_to_memory(PERSIST_PART, VCOM_FILE_PATH_UP, &size);
		if (file_buf == NULL)
		{
			UBOOT_ERROR("Error: Read persist /config/VCOM.hex file to DRAM failure\n");
			return;
		}
	}

	j = 0;
	k = 0;
	for(i=0; i<size; i++)
	{
		if ((file_buf[i] != ' ') && (i != size-1))
		{
			buf[j] = file_buf[i];
			j++;
			first_blank = true;
		}else{
			if(first_blank == true)
			{
				vcomdata[k] = (char)simple_strtol((char *)buf, NULL, 0);
				memset(buf, 0, sizeof(buf));
				j = 0;
				k++;
				first_blank = false;
			}
		}
	}
	UBOOT_DEBUG("get vcom data vcomdata[0]:0x%x vcomdata[4]:0x%x vcomdata[5]:0x%x vcomdata[6]:0x%x \n", vcomdata[0], vcomdata[4], vcomdata[5], vcomdata[6]);

	vcom_size = vcomdata[0];
	for(i=0; i<vcom_size; i++)
	{
		vcomData_char[i] = vcomdata[vcom_offset++];
		UBOOT_DEBUG("get vcom data vcomData_int[%d]:0x%x \n", i, vcomData_char[i]);
	}

	crcdata = cal_vcom_crc(vcomData_char, vcom_size);
	UBOOT_DEBUG("vcom check crcdata:0x%x    read crcdata:0x%x\n", crcdata, vcomdata[VCOM_CRC_OFFSET]);
	if (crcdata == vcomdata[VCOM_CRC_OFFSET])
	{
		vcomCrcResult = 0;
	}
}
#endif

int check_panel_id_format(const char* str, int length)
{
    int i;
    for(i = 0; i < length; i++)
    {
        //UBOOT_DEBUG("[PANEL] read panel ID[%d]:0x%x \n", i, str[i]);
        if(((str[i] >= 0x30) && (str[i] <= 0x39))  ||  //0-9
           ((str[i] >= 0x41) && (str[i] <= 0x5A)) ||   //A-Z
           ((str[i] >= 0x61) && (str[i] <= 0x7A)) ||   //a-z
           (str[i] == 0x5F))                          //'_'
        {
            continue;
        }
        else
        {
            UBOOT_ERROR("[PANEL] panel ID[%d]:0x%x format invalid!\n", i, str[i]);
            return -1;
        }
    }
    UBOOT_DEBUG("[PANEL] panel ID format correct!\n");
    return 0;
}

char* get_panel_id(void)
{
    if (check_panel_id_format(panelID, PANEL_ID_LENGTH)==0) {
        return panelID;
    } else {
        return NULL;
    }
}

char* get_vcom_data_from_eeprom(void)
{
	if (vcomCrcResult == 0)
	{
		return vcomData;
	}else{
		return NULL;
	}
}

char* get_vcom_data_from_emmc(void)
{
	if (vcomCrcResult == 0)
	{
		return vcomData_char;
	}else{
		return NULL;
	}
}

int get_panelless_status(void)
{
	return panel_less;
}

//DVT board tconless status
// high : tcon
// low  : tconless
int get_tconless_status(void)
{
	return 0;//tcon_less;
}

void sony_customer_add_panel_size_info_to_bootargs(void)
{
	char PanelSizeInfo_bootcfg[128]= "androidboot.sodtb.PanelSizeInfo=0000000000";
	if (get_panel_size_info() != NULL)
	{
		if (snprintf(PanelSizeInfo_bootcfg, strlen(PanelSizeInfo_bootcfg)+1, "%s=%s", PANEL_SIZE_INFO, get_panel_size_info()) < 0){
			UBOOT_INFO("T7x cannot generate %s \n", PanelSizeInfo_bootcfg);
			return ;
		}
	}
	UBOOT_INFO("T7x PanelSizeInfo_bootcfg:%s \n", PanelSizeInfo_bootcfg);
	add_bootargs(PANEL_SIZE_INFO, PanelSizeInfo_bootcfg, 0);

	return ;
}

void set_panel_rw_status(struct udevice *dev, int value)
{
	struct sony_customer_priv *priv = dev_get_priv(dev);
	if (dm_gpio_is_valid(&priv->panel_b_int))
	{
		if (dm_gpio_get_value(&priv->panel_b_int) != value)
		{
			dm_gpio_set_value(&priv->panel_b_int, value);
		}else{
			UBOOT_DEBUG("[PANEL] panel protect gpio status not change\n");
		}
	}
}
/*
void set_pmic_enable_status(struct udevice *dev, int value)
{
	struct sony_customer_priv *priv = dev_get_priv(dev);
	if (dm_gpio_is_valid(&priv->trdy))
	{
		if (dm_gpio_get_value(&priv->trdy) != value)
		{
			dm_gpio_set_value(&priv->trdy, value);
		}else{
			UBOOT_DEBUG("[PANEL] pmic enable gpio status not change\n");
		}
	}
}
*/
bool is_sony_board(void)
{
	if (env_get(PANEL_ID) != NULL)
	{
		return true;
	}else{
		return false;
	}
}

unsigned int get_adc_value(int channel)
{
	struct udevice *dev, *normal_dev;
	unsigned int data;
	int ret;//, uV;

	ret = uclass_first_device_err(UCLASS_ADC, &dev);
	if (ret) {
		printf("No available ADC device\n");
		return CMD_RET_FAILURE;
	}

	do {
		UBOOT_INFO("adc device : %s\n", dev->name);
		normal_dev = dev;

		ret = uclass_next_device(&dev);
		if (ret)
			return CMD_RET_FAILURE;
	} while (dev);

	ret = adc_channel_single_shot(normal_dev->name, channel, &data);
	if (ret) {
		printf("Error getting single shot for device %s channel %d\n",
		       normal_dev->name, channel);
		return CMD_RET_FAILURE;
	}
/*
	if (!adc_raw_to_uV(normal_dev, data, &uV))
		UBOOT_INFO("%u, %d uV\n", data, uV);
	else
		UBOOT_INFO("%u\n", data);
*/

	return data;
}
bool enalbe_env_save_status = false;
void enable_env_save(bool status)
{
	enalbe_env_save_status = status;
}

bool get_env_save_status(void)
{
	return enalbe_env_save_status;
}

static int sony_customer_probe(struct udevice *dev)
{
	struct sony_customer_priv *priv = dev_get_priv(dev);
	struct gpio_desc bl_dimmer0;
	struct gpio_desc bl_dimmer1;
	struct gpio_desc bl_dimmer2;
	struct gpio_desc bl_dimmer_dc;
	//struct gpio_desc ews_pwr;
	//struct gpio_desc ci_en;
	struct gpio_desc s_wp;
//	struct gpio_desc wifi_sub_rst;
//	struct gpio_desc bt_sub_rst;
	int ret;
	char * force_panel = NULL;
	char buf[MAX_VALUE_LENGTH] = {0};
    char buf_demura[MAX_VALUE_LENGTH];
	char * ec_demura_fixed_mode = EC_DEMURA_FIXED_MODE;
	char * ec_value = (char *) malloc(MAX_VALUE_LENGTH);
	char tuner_value[6] = {0};
	unsigned int adc_value = 0;

	if (dev_read_enabled(dev) == 0)
	{
		UBOOT_ERROR("read not enabled\n");
		return 0;
	}

	if (device_get_uclass_id(dev->parent) != UCLASS_I2C) 
	{
		UBOOT_ERROR("get i2c parent fail\n");
        return -EPROTONOSUPPORT;
	}

	ret = gpio_request_by_name(dev, "panelless-gpios", 0, &priv->panel_less, GPIOD_IS_IN);
	if (ret) 
	{
		UBOOT_ERROR("get panel less gpio fail %d\n", ret);
		return -EINVAL;
	}
	if (dm_gpio_is_valid(&priv->panel_less))
	{
		panel_less = dm_gpio_get_value(&priv->panel_less);
		force_panel = env_get("force_panel");
		if (force_panel != NULL)
		{
			if (strcmp(force_panel, "1") == 0)
			{
				panel_less = 0;
			}else if (strcmp(force_panel, "0") == 0){
				panel_less = 1;
			}
		}
	}
/*
	ret = gpio_request_by_name(dev, "tconless-gpios", 0, &priv->tcon_less, GPIOD_IS_IN);
	if (ret) {
		UBOOT_ERROR("[PANEL] get tcon less gpio fail %d\n", ret);
		return -EINVAL;
	}
	if (dm_gpio_is_valid(&priv->tcon_less))
	{
		tcon_less = dm_gpio_get_value(&priv->tcon_less);
	}
*/
	ret = gpio_request_by_name(dev, "panel_b_int-gpios", 0, &priv->panel_b_int, GPIOD_IS_OUT);
	if (ret) {
		UBOOT_ERROR("get panel communication protect gpio fail %d\n", ret);
		return -EINVAL;
	}

	//ret = gpio_request_by_name(dev, "trdy-gpios", 0, &priv->trdy, GPIOD_IS_OUT);
	//if (ret) {
	//	UBOOT_ERROR("[PANEL] get PMIC enable gpio fail %d\n", ret);
	//	return -EINVAL;
	//}

	ret = gpio_request_by_name(dev, "bl_dimmer0-gpios", 0, &bl_dimmer0, GPIOD_IS_OUT);
	if (ret) {
		UBOOT_ERROR("get bl_dimmer0 gpio fail %d\n", ret);
		return -EINVAL;
	}

	ret = gpio_request_by_name(dev, "bl_dimmer1-gpios", 0, &bl_dimmer1, GPIOD_IS_OUT);
	if (ret) {
		UBOOT_ERROR("get bl_dimmer1 gpio fail %d\n", ret);
		return -EINVAL;
	}

	ret = gpio_request_by_name(dev, "bl_dimmer2-gpios", 0, &bl_dimmer2, GPIOD_IS_OUT);
	if (ret) {
		UBOOT_ERROR("get bl_dimmer2 gpio fail %d\n", ret);
		return -EINVAL;
	}

	ret = gpio_request_by_name(dev, "bl_dimmer_dc-gpios", 0, &bl_dimmer_dc, GPIOD_IS_OUT);
	if (ret) {
		UBOOT_ERROR("get bl_dimmer3 gpio fail %d\n", ret);
		return -EINVAL;
	}

	ret = gpio_request_by_name(dev, "err_led-gpios", 0, &priv->err_led, GPIOD_IS_OUT);
	if (ret) {
		UBOOT_ERROR("get err_led-gpios fail %d\n", ret);
		return -EINVAL;
	}

	ret = gpio_request_by_name(dev, "s_wp-gpios", 0, &s_wp, GPIOD_IS_OUT);
	if (ret) {
		UBOOT_ERROR("get s_wp-gpios fail %d\n", ret);
		return -EINVAL;
	}
/*
	ret = gpio_request_by_name(dev, "wifi_sub_rst-gpios", 0, &wifi_sub_rst, GPIOD_IS_OUT);
	if (ret) {
		UBOOT_ERROR("get wifi_sub_rst-gpios fail %d\n", ret);
		return -EINVAL;
	}

	ret = gpio_request_by_name(dev, "bt_sub_rst-gpios", 0, &bt_sub_rst, GPIOD_IS_OUT);
	if (ret) {
		UBOOT_ERROR("get bt_sub_rst-gpios fail %d\n", ret);
		return -EINVAL;
	}
*/
	ret = gpio_request_by_name(dev, "panel_sa_mode-gpios", 0, &priv->panel_sa_mode, GPIOD_IS_OUT);
	if (ret) {
		UBOOT_ERROR("get panel_sa-mode-gpios fail %d\n", ret);
		return -EINVAL;
	}else{
		dm_gpio_set_value(&priv->panel_sa_mode, 0);
	}

	ret = gpio_request_by_name(dev, "s_boot-gpios", 0, &priv->s_boot, GPIOD_IS_IN);
	if (ret) {
		UBOOT_ERROR("get s_boot-gpios fail %d\n", ret);
		return -EINVAL;
	}
	if (dm_gpio_is_valid(&priv->s_boot))
	{
		s_boot_status = dm_gpio_get_value(&priv->s_boot);
                UBOOT_INFO("get s_boot_status = %d\n", s_boot_status);
	}

/*
	ret = get_ec_value(EC_TUNER_CONFIG, ec_value, MAX_VALUE_LENGTH);
	if (ret < 0)
	{
		UBOOT_ERROR("Error: get ec value [%s] failure\n", EC_TUNER_CONFIG);
	}else{
		if (strcmp(ec_value, "var_jp_isdb") == 0)
		{
			ret = gpio_request_by_name(dev, "ci_en-gpios", 0, &ci_en, GPIOD_IS_OUT);
			if (ret) {
				UBOOT_ERROR("get ci_en gpio fail %d\n", ret);
				free(ec_value);
				return -EINVAL;
			}
			dm_gpio_set_value(&ci_en, 1);
			//add_bootargs("module_blacklist", "module_blacklist=mtk_pcmcia", false);
		}else {
			if (strcmp(ec_value, "var_ph_isdb") == 0){
				ret = gpio_request_by_name(dev, "ews_pwr-gpios", 0, &ews_pwr, GPIOD_IS_OUT);
				if (ret) {
					UBOOT_ERROR("get ews_pwr gpio fail %d\n", ret);
					free(ec_value);
					return -EINVAL;
				}
			}
			//del_bootargs("module_blacklist", false);
		}
	}
*/
	ret = get_ec_value(EC_COUNTRY_L_H_VARIATION, ec_value, MAX_VALUE_LENGTH);
	if (ret < 0)
	{
		UBOOT_ERROR("Error: get ec value [%s] failure\n", EC_COUNTRY_L_H_VARIATION);
	}else{
		if (snprintf(buf, MAX_VALUE_LENGTH, "country_list_handling_variation=%s", ec_value) < 0)
		{
			UBOOT_ERROR("FAIL to construct country_list_handling_variation\n");
		}
		else
		{
			if (add_bootargs(EC_COUNTRY_L_H_VARIATION, buf, 0))
			{
				UBOOT_ERROR("FAIL to add country_list_handling_variation to bootargs\n");
			}
		}
	}

	ret = get_ec_value(EC_ANALOG_UHF_CONFIG, ec_value, MAX_VALUE_LENGTH);
	if (ret < 0)
	{
		UBOOT_ERROR("Error: get ec value [%s] failure\n", EC_ANALOG_UHF_CONFIG);
	}else{
		tuner_value[0] = ec_value[0];
	}
	ret = get_ec_value(EC_ANALOG_VHF_HIGH_CONFIG, ec_value, MAX_VALUE_LENGTH);
	if (ret < 0)
	{
		UBOOT_ERROR("Error: get ec value [%s] failure\n", EC_ANALOG_VHF_HIGH_CONFIG);
	}else{
		tuner_value[1] = ec_value[0];
	}
	ret = get_ec_value(EC_ANALOG_VHF_LOW_CONFIG, ec_value, MAX_VALUE_LENGTH);
	if (ret < 0)
	{
		UBOOT_ERROR("Error: get ec value [%s] failure\n", EC_ANALOG_VHF_LOW_CONFIG);
	}else{
		tuner_value[2] = ec_value[0];
	}
	ret = get_ec_value(EC_DIGITAL_UHF_CONFIG, ec_value, MAX_VALUE_LENGTH);
	if (ret < 0)
	{
		UBOOT_ERROR("Error: get ec value [%s] failure\n", EC_DIGITAL_UHF_CONFIG);
	}else{
		tuner_value[3] = ec_value[0];
	}
	ret = get_ec_value(EC_DIGITAL_VHF_HIGH_CONFIG, ec_value, MAX_VALUE_LENGTH);
	if (ret < 0)
	{
		UBOOT_ERROR("Error: get ec value [%s] failure\n", EC_DIGITAL_VHF_HIGH_CONFIG);
	}else{
		tuner_value[4] = ec_value[0];
	}
	ret = get_ec_value(EC_DIGITAL_VHF_LOW_CONFIG, ec_value, MAX_VALUE_LENGTH);
	if (ret < 0)
	{
		UBOOT_ERROR("Error: get ec value [%s] failure\n", EC_DIGITAL_VHF_LOW_CONFIG);
	}else{
		tuner_value[5] = ec_value[0];
	}
	if (snprintf(buf, MAX_VALUE_LENGTH, "tuner-cxd686.tuner_config=%s", tuner_value) < 0)
	{
		UBOOT_ERROR("FAIL to construct tuner-cxd686\n");
	}
	else
	{
		if (add_bootargs("tuner-cxd686.tuner_config", buf, 0))
		{
			UBOOT_ERROR("FAIL to add tuner-cxd686\n");
		}
	}

    if (get_ec_value(ec_demura_fixed_mode, ec_value, MAX_VALUE_LENGTH) < 0)
    {
        UBOOT_DEBUG("cannot read %s from ec, \"%s\" return\n", ec_demura_fixed_mode, ec_value);
        env_set(ec_demura_fixed_mode, "0");
    } else {
		UBOOT_TRACE("new demura flag 1\n");
        if (snprintf(buf_demura, sizeof(ec_value) + 1, "%s", ec_value) < 0){
            UBOOT_DEBUG("cannot generate %s from %s\n", ec_demura_fixed_mode, ec_value);
			free(ec_value);
            return 1;
        }
		UBOOT_TRACE("new demura flag 2\n");
        env_set(ec_demura_fixed_mode, buf_demura);
		UBOOT_TRACE("new demura flag 3\n");
    }
	UBOOT_TRACE("tconless demura fixed mode: %s", ec_value);
/*
	ret = get_ec_value(EC_ENABLE_CH_EQ, ec_value, MAX_VALUE_LENGTH);
	if (ret < 0)
	{
		UBOOT_ERROR("Error: get ec value [%s] failure\n", EC_ENABLE_CH_EQ);
	}else{
		if (snprintf(buf, MAX_VALUE_LENGTH, "enable_ch_eq=%s", ec_value) < 0)
		{
			UBOOT_ERROR("FAIL to construct enable_ch_eq\n");
		}
		else
		{
			if (add_bootargs(EC_ENABLE_CH_EQ, buf, 0))
			{
				UBOOT_ERROR("FAIL to add enable_ch_eq to bootargs\n");
			}
		}
	}
*/
	//set pwm gpio to low
	dm_gpio_set_value(&bl_dimmer0, 0);
	dm_gpio_set_value(&bl_dimmer1, 0);
	dm_gpio_set_value(&bl_dimmer2, 0);
	dm_gpio_set_value(&bl_dimmer_dc, 0);

//	dm_gpio_set_value(&wifi_sub_rst, 1);
//	dm_gpio_set_value(&bt_sub_rst, 1);

	free(ec_value);

	if (!panel_less)
	{
		ret = dev_read_u32(dev, "reg", &priv->i2c_addr);
		if (ret) 
		{
			UBOOT_ERROR("[PANEL] can't get slave addr\n");
			return -EINVAL;
		}
	}

	adc_value = get_adc_value(HW_TYPE_DET_ADC_CHANNEL);
	UBOOT_INFO("HW_TYPE_DET adc_value:%d\n", adc_value);
	if ((adc_value >= VALID_THRESHOLD_0) && (adc_value <= VALID_THRESHOLD_030))
	{
		pinctrl_select_state(dev, "hw_type_det_WW");
	}

	adc_value = get_adc_value(BOARD_TYPE_SEL_ADC_CHANNEL);
	UBOOT_INFO("BOARD_TYPE_DET adc_value:%d\n", adc_value);
	if ((adc_value >= VALID_THRESHOLD_0) && (adc_value <= VALID_THRESHOLD_030))
	{
		pinctrl_select_state(dev, "board_type_sel_LDBE");
	}
	else if ((adc_value >= VALID_THRESHOLD_160) && (adc_value <= VALID_THRESHOLD_180))
	{
		pinctrl_select_state(dev, "board_type_sel_0D");
	}

	return 0;
}

static const struct sony_customer_ops customer_ops = {
	.init_panel_id	= init_sony_panel_id,
	.init_vcom_data	= init_sony_vcom_data,
	//.init_pmux	= init_sony_pmux,
	.init_pmux_cust	= init_sony_pmux_cust,
	.enable_panel_rw = set_panel_rw_status,
	//.enable_pmic = set_pmic_enable_status,
};

static const struct udevice_id sony_customer_ids[] = {
	{ .compatible = "sony, customer" },
	{ }
};

U_BOOT_DRIVER(sony_customer) = {
	.name		= "sony_customer",
	.id			= UCLASS_PANEL,
	.of_match	= sony_customer_ids,
	.probe		= sony_customer_probe,
	.ops		= &customer_ops,
	.priv_auto_alloc_size = sizeof(struct sony_customer_priv),
};
