/*
 * Copyright 2024-2025 Sony Corporation
 */

#include "sony_panel.h"
#include "sony_panel_lcd.h"

#include <dm/device.h>
#include <dm/uclass.h>
#include <linux/delay.h>
#include <stdio.h>
#include <utility.h>
#include <i2c.h>

#define STATUS_READ_MSG_NUM         (2)
#define STATUS_READ_CMD_DATA_NUM    (7)
#define STATUS_READ_READ_DATA_NUM   (2)
#define VSYNC_FREERUN_MODE_DATA_NUM (6)
#define FLAT_ON_MODE_DATA_NUM       (6)
#define FLAT_LEVEL_DATA_NUM         (7)
#define I2C_DELAY_TIME              (10)

struct backlight_control_i2c_info_st {
	uint32_t wait_time_from_panel_enable_to_flat_on;
	uint32_t wait_time_from_panel_on_to_flat_on;
	uint32_t wait_time_max;
	uint32_t retry_num;
	uint32_t address;
	uint32_t busnum;
	uint32_t status_read_cmd_data[STATUS_READ_CMD_DATA_NUM];
	uint32_t expected_status_read_val;
	uint32_t expected_status_read_checksum;
	uint32_t free_run_mode_cmd;
	uint32_t free_run_mode_data[VSYNC_FREERUN_MODE_DATA_NUM];
	uint32_t flat_on_mode_cmd;
	uint32_t flat_on_mode_data[FLAT_ON_MODE_DATA_NUM];
	bool is_exist_flat_level;
	uint32_t flat_level_cmd;
	uint32_t flat_level_data[FLAT_LEVEL_DATA_NUM];
};

static int load_backlight_dt(struct backlight_control_i2c_info_st *p_info)
{
	int ret = 0;
	ofnode node;

	node = ofnode_by_compatible(ofnode_null(), "sony,backlight");
	if (!ofnode_valid(node)) {
		printf("BACKLIGHT: Fail to get backlight dts!!!!!\n");
		return -1;
	}

	ret = ofnode_read_u32(node, "wait_time_from_panel_enable_to_flat_on", &p_info->wait_time_from_panel_enable_to_flat_on);
	if (ret != 0) {
		printf("%s: Fail to get [wait_time_from_panel_enable_to_flat_on]!!!!!\n", __func__);
		return -1;
	}
	ret = ofnode_read_u32(node, "wait_time_from_panel_on_to_flat_on", &p_info->wait_time_from_panel_on_to_flat_on);
	if (ret != 0) {
		printf("%s: Fail to get [wait_time_from_panel_on_to_flat_on]!!!!!\n", __func__);
		return -1;
	}
	ret = ofnode_read_u32(node, "wait_time_max", &p_info->wait_time_max);
	if (ret != 0) {
		printf("%s: Fail to get [wait_time_max]!!!!!\n", __func__);
		return -1;
	}
	ret = ofnode_read_u32(node, "retry_num", &p_info->retry_num);
	if (ret != 0) {
		printf("%s: Fail to get [retry_num]!!!!!\n", __func__);
		return -1;
	}
	ret = ofnode_read_u32(node, "address", &p_info->address);
	if (ret != 0) {
		printf("%s: Fail to get [address]!!!!!\n", __func__);
		return -1;
	}
	ret = ofnode_read_u32(node, "busnum", &p_info->busnum);
	if (ret != 0) {
		printf("%s: Fail to get [busnum]!!!!!\n", __func__);
		return -1;
	}
	ret = ofnode_read_u32_array(node, "status_read_cmd_data", p_info->status_read_cmd_data, STATUS_READ_CMD_DATA_NUM);
	if (ret != 0) {
		printf("%s: Fail to get [status_read_cmd_data]!!!!!\n", __func__);
		return -1;
	}
	ret = ofnode_read_u32(node, "expected_status_read_val", &p_info->expected_status_read_val);
	if (ret != 0) {
		printf("%s: Fail to get [expected_status_read_val]!!!!!\n", __func__);
		return -1;
	}
	ret = ofnode_read_u32(node, "expected_status_read_checksum", &p_info->expected_status_read_checksum);
	if (ret != 0) {
		printf("%s: Fail to get [expected_status_read_checksum]!!!!!\n", __func__);
		return -1;
	}
	ret = ofnode_read_u32(node, "free_run_mode_cmd", &p_info->free_run_mode_cmd);
	if (ret != 0) {
		printf("%s: Fail to get [free_run_mode_cmd]!!!!!\n", __func__);
		return -1;
	}
	ret = ofnode_read_u32_array(node, "free_run_mode_data", p_info->free_run_mode_data, VSYNC_FREERUN_MODE_DATA_NUM);
	if (ret != 0) {
		printf("%s: Fail to get [free_run_mode_data]!!!!!\n", __func__);
		return -1;
	}
	ret = ofnode_read_u32(node, "flat_on_mode_cmd", &p_info->flat_on_mode_cmd);
	if (ret != 0) {
		printf("%s: Fail to get [flat_on_mode_cmd]!!!!!\n", __func__);
		return -1;
	}
	ret = ofnode_read_u32_array(node, "flat_on_mode_data", p_info->flat_on_mode_data, FLAT_ON_MODE_DATA_NUM);
	if (ret != 0) {
		printf("%s: Fail to get [flat_on_mode_data]!!!!!\n", __func__);
		return -1;
	}

	ret = ofnode_read_u32(node, "flat_level_cmd", &p_info->flat_level_cmd);
	if (ret == 0) {
		p_info->is_exist_flat_level = true;

		ret = ofnode_read_u32_array(node, "flat_level_data", p_info->flat_level_data, FLAT_LEVEL_DATA_NUM);
		if (ret != 0) {
			printf("%s: Fail to get [flat_level_data]!!!!!\n", __func__);
			return -1;
		}
	} else {
		p_info->is_exist_flat_level = false;
	}

	return 0;
}

int panel_lcdLogoBacklightControl(void)
{
	struct udevice *dev;
	struct udevice *bus;
	int ret = 0;
	int wait_time = 0;
	uint8_t set_status_read_cmd_data[STATUS_READ_CMD_DATA_NUM] = { 0 };
	uint8_t buf_for_status_read[STATUS_READ_READ_DATA_NUM] = { 0 };
	uint8_t set_vsync_freerun_mode_data[VSYNC_FREERUN_MODE_DATA_NUM] = { 0 };
	uint8_t set_flat_on_mode_data[FLAT_ON_MODE_DATA_NUM] = { 0 };
	uint8_t set_flat_level_data[FLAT_LEVEL_DATA_NUM] = { 0 };
	struct backlight_control_i2c_info_st info = { 0 };

	ret = load_backlight_dt(&info);
	if (ret != 0)
		return -1;
	struct i2c_msg status_read_msg[STATUS_READ_MSG_NUM] = {
		{
			.addr = info.address,
			.flags = 0,
			.len = STATUS_READ_CMD_DATA_NUM,
			.buf = set_status_read_cmd_data,
		},
		{
			.addr = info.address,
			.flags = I2C_M_RD,
			.len = STATUS_READ_READ_DATA_NUM,
			.buf = buf_for_status_read,
		},
	};

	// The data is obtained from DTS as uint32_t, but the dm_i2c_write function repacks it for use as uint8_t.
	for (int i = 0; i < STATUS_READ_CMD_DATA_NUM; i++)
		set_status_read_cmd_data[i] = (uint8_t)info.status_read_cmd_data[i];
	for (int i = 0; i < VSYNC_FREERUN_MODE_DATA_NUM; i++)
		set_vsync_freerun_mode_data[i] = (uint8_t)info.free_run_mode_data[i];
	for (int i = 0; i < FLAT_ON_MODE_DATA_NUM; i++)
		set_flat_on_mode_data[i] = (uint8_t)info.flat_on_mode_data[i];
	for (int i = 0; i < FLAT_LEVEL_DATA_NUM; i++)
		set_flat_level_data[i] = (uint8_t)info.flat_level_data[i];

	uclass_get_device_by_seq(UCLASS_I2C, info.busnum, &bus);
	for (int i = 0; i < info.retry_num; i++) {
		if (i2c_get_chip(bus, info.address, 0x01, &dev) == 0)
			break;
		printf("[%s] Fail i2c get chip\n", __func__);
		if (i == info.retry_num - 1)
			return -1;
	}

	mdelay(info.wait_time_from_panel_enable_to_flat_on);

	uint64_t current = get_timer(0);
	uint64_t panel_on_time = paneldet_getPanelOnTime();

	printf("[%s] panel_on_time = %llu, current = %llu\n", __func__, panel_on_time, current);

	if (panel_on_time + info.wait_time_from_panel_on_to_flat_on > current)
		mdelay(panel_on_time + info.wait_time_from_panel_on_to_flat_on - current);

	while (wait_time < info.wait_time_max) {
		// dm_i2c_xfer is used, because it does not use offset
		ret = dm_i2c_xfer(dev, &status_read_msg[0], 1);
		if (ret == 0)
			ret = dm_i2c_xfer(dev, &status_read_msg[1], 1);

		if (ret == 0) {
			if ((buf_for_status_read[0] == info.expected_status_read_val) &&
				(buf_for_status_read[1] == info.expected_status_read_checksum)) {
				printf("[%s] BCM Device bootup completed. wait_time=%d\n", __func__, wait_time);
				break;
			}
		}

		mdelay(I2C_DELAY_TIME);
		wait_time += I2C_DELAY_TIME;
	}
	if (!(wait_time < info.wait_time_max)) {
		printf("[%s] BCM Device bootup NOT completed\n", __func__);
		return -1;
	}

	for (int i = 0; i < info.retry_num; i++) {
		ret = dm_i2c_write(dev, info.free_run_mode_cmd, set_vsync_freerun_mode_data, VSYNC_FREERUN_MODE_DATA_NUM);
		if (ret == 0) {
			mdelay(I2C_DELAY_TIME);
			break;
		}

		printf("[%s] Fail set vsync freerun mode, ret=%d\n", __func__, ret);
		mdelay(I2C_DELAY_TIME);
		if (i == info.retry_num - 1)
			return -1;
	}

	if (info.is_exist_flat_level) {
		for (int i = 0; i < info.retry_num; i++) {
			ret = dm_i2c_write(dev, info.flat_level_cmd, set_flat_level_data, FLAT_LEVEL_DATA_NUM);
			if (ret == 0) {
				mdelay(I2C_DELAY_TIME);
				break;
			}

			printf("[%s] Fail set flat level, ret=%d\n", __func__, ret);
			mdelay(I2C_DELAY_TIME);
			if (i == info.retry_num - 1)
				return -1;
		}
	}

	for (int i = 0; i < info.retry_num; i++) {
		ret = dm_i2c_write(dev, info.flat_on_mode_cmd, set_flat_on_mode_data, FLAT_ON_MODE_DATA_NUM);
		if (ret == 0) {
			mdelay(I2C_DELAY_TIME);
			break;
		}

		printf("[%s] Fail set flat on mode, ret=%d\n", __func__, ret);
		mdelay(I2C_DELAY_TIME);
		if (i == info.retry_num - 1)
			return -1;
	}

	return 0;
}
