/* Copyright 2021 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

/* Nipperkin board-specific configuration */

#include "adc.h"
#include "base_fw_config.h"
#include "board_fw_config.h"
#include "button.h"
#include "chipset.h"
#include "common.h"
#include "cros_board_info.h"
#include "driver/retimer/pi3hdx1204.h"
#include "driver/retimer/ps8811.h"
#include "driver/retimer/ps8818.h"
#include "driver/temp_sensor/sb_tsi.h"
#include "driver/temp_sensor/pct2075.h"
#include "extpower.h"
#include "gpio.h"
#include "hooks.h"
#include "keyboard_scan.h"
#include "lid_switch.h"
#include "power.h"
#include "power_button.h"
#include "switch.h"
#include "tablet_mode.h"
#include "temp_sensor.h"
#include "temp_sensor/thermistor.h"
#include "thermal.h"
#include "timer.h"
#include "usb_mux.h"

static void hdmi_hpd_interrupt(enum gpio_signal signal);

#include "gpio_list.h" /* Must come after other header files. */

/*
 * We have total 30 pins for keyboard connecter {-1, -1} mean
 * the N/A pin that don't consider it and reserve index 0 area
 * that we don't have pin 0.
 */
const int keyboard_factory_scan_pins[][2] = {
	{-1, -1}, {0, 5}, {1, 1}, {1, 0}, {0, 6},
	{0, 7}, {-1, -1}, {-1, -1}, {1, 4}, {1, 3},
	{-1, -1}, {1, 6}, {1, 7}, {3, 1}, {2, 0},
	{1, 5}, {2, 6}, {2, 7}, {2, 1}, {2, 4},
	{2, 5}, {1, 2}, {2, 3}, {2, 2}, {3, 0},
	{-1, -1}, {0, 4}, {-1, -1}, {8, 2}, {-1, -1},
	{-1, -1},
};
const int keyboard_factory_scan_pins_used =
		ARRAY_SIZE(keyboard_factory_scan_pins);

__override enum ec_error_list
board_a1_ps8811_retimer_init(const struct usb_mux *me)
{
	return EC_SUCCESS;
}

/*
 * PS8818 set mux board tuning.
 * Adds in board specific gain and DP lane count configuration
 * TODO(b/179036200): Adjust PS8818 tuning for guybrush reference
 */
__override int board_c1_ps8818_mux_set(const struct usb_mux *me,
				    mux_state_t mux_state)
{
	int rv = EC_SUCCESS;

	/* USB specific config */
	if (mux_state & USB_PD_MUX_USB_ENABLED) {
		/* Boost the USB gain */
		rv = ps8818_i2c_field_update8(me,
					PS8818_REG_PAGE1,
					PS8818_REG1_APTX1EQ_10G_LEVEL,
					PS8818_EQ_LEVEL_UP_MASK,
					PS8818_EQ_LEVEL_UP_19DB);
		if (rv)
			return rv;

		rv = ps8818_i2c_field_update8(me,
					PS8818_REG_PAGE1,
					PS8818_REG1_APTX2EQ_10G_LEVEL,
					PS8818_EQ_LEVEL_UP_MASK,
					PS8818_EQ_LEVEL_UP_19DB);
		if (rv)
			return rv;

		rv = ps8818_i2c_field_update8(me,
					PS8818_REG_PAGE1,
					PS8818_REG1_APTX1EQ_5G_LEVEL,
					PS8818_EQ_LEVEL_UP_MASK,
					PS8818_EQ_LEVEL_UP_19DB);
		if (rv)
			return rv;

		rv = ps8818_i2c_field_update8(me,
					PS8818_REG_PAGE1,
					PS8818_REG1_APTX2EQ_5G_LEVEL,
					PS8818_EQ_LEVEL_UP_MASK,
					PS8818_EQ_LEVEL_UP_19DB);
		if (rv)
			return rv;

		/* Set the RX input termination */
		rv = ps8818_i2c_field_update8(me,
					PS8818_REG_PAGE1,
					PS8818_REG1_RX_PHY,
					PS8818_RX_INPUT_TERM_MASK,
					PS8818_RX_INPUT_TERM_112_OHM);
		if (rv)
			return rv;
	}

	/* DP specific config */
	if (mux_state & USB_PD_MUX_DP_ENABLED) {
		/* Boost the DP gain */
		rv = ps8818_i2c_field_update8(me,
					PS8818_REG_PAGE1,
					PS8818_REG1_DPEQ_LEVEL,
					PS8818_DPEQ_LEVEL_UP_MASK,
					PS8818_DPEQ_LEVEL_UP_19DB);
		if (rv)
			return rv;

		/* Enable HPD on the DB */
		ioex_set_level(IOEX_USB_C1_IN_HPD, 1);
	} else {
		/* Disable HPD on the DB */
		ioex_set_level(IOEX_USB_C1_IN_HPD, 0);
	}

	return rv;
}

static void board_init(void)
{
	if (get_board_version() > 1)
		gpio_enable_interrupt(GPIO_HPD_EC_IN);
}
DECLARE_HOOK(HOOK_INIT, board_init, HOOK_PRIO_DEFAULT);

static void board_chipset_startup(void)
{
	if (get_board_version() > 1)
		pct2075_init();
}
DECLARE_HOOK(HOOK_CHIPSET_STARTUP, board_chipset_startup,
	     HOOK_PRIO_DEFAULT);

int board_get_soc_temp_k(int idx, int *temp_k)
{
	if (chipset_in_state(CHIPSET_STATE_HARD_OFF))
		return EC_ERROR_NOT_POWERED;

	return pct2075_get_val_k(idx, temp_k);
}

int board_get_soc_temp_mk(int *temp_mk)
{
	if (chipset_in_state(CHIPSET_STATE_HARD_OFF))
		return EC_ERROR_NOT_POWERED;

	return pct2075_get_val_mk(PCT2075_SOC, temp_mk);
}

int board_get_ambient_temp_mk(int *temp_mk)
{
	if (chipset_in_state(CHIPSET_STATE_HARD_OFF))
		return EC_ERROR_NOT_POWERED;

	return pct2075_get_val_mk(PCT2075_AMB, temp_mk);
}

/* ADC Channels */
const struct adc_t adc_channels[] = {
	[ADC_TEMP_SENSOR_MEMORY] = {
		.name = "MEMORY",
		.input_ch = NPCX_ADC_CH0,
		.factor_mul = ADC_MAX_VOLT,
		.factor_div = ADC_READ_MAX + 1,
		.shift = 0,
	},
	[ADC_TEMP_SENSOR_CHARGER] = {
		.name = "CHARGER",
		.input_ch = NPCX_ADC_CH1,
		.factor_mul = ADC_MAX_VOLT,
		.factor_div = ADC_READ_MAX + 1,
		.shift = 0,
	},
	[ADC_TEMP_SENSOR_5V_REGULATOR] = {
		.name = "5V_REGULATOR",
		.input_ch = NPCX_ADC_CH2,
		.factor_mul = ADC_MAX_VOLT,
		.factor_div = ADC_READ_MAX + 1,
		.shift = 0,
	},
	[ADC_CORE_IMON1] = {
		.name = "CORE_I",
		.input_ch = NPCX_ADC_CH3,
		.factor_mul = ADC_MAX_VOLT,
		.factor_div = ADC_READ_MAX + 1,
		.shift = 0,
	},
	[ADC_SOC_IMON2] = {
		.name = "SOC_I",
		.input_ch = NPCX_ADC_CH4,
		.factor_mul = ADC_MAX_VOLT,
		.factor_div = ADC_READ_MAX + 1,
		.shift = 0,
	},
};
BUILD_ASSERT(ARRAY_SIZE(adc_channels) == ADC_CH_COUNT);

/* Temp Sensors */
static int board_get_temp(int, int *);

const struct pct2075_sensor_t pct2075_sensors[] = {
	{ I2C_PORT_SENSOR, PCT2075_I2C_ADDR_FLAGS0 },
	{ I2C_PORT_SENSOR, PCT2075_I2C_ADDR_FLAGS7 },
};
BUILD_ASSERT(ARRAY_SIZE(pct2075_sensors) == PCT2075_COUNT);

const struct temp_sensor_t temp_sensors[] = {
	[TEMP_SENSOR_SOC] = {
		.name = "SOC",
		.type = TEMP_SENSOR_TYPE_BOARD,
		.read = board_get_soc_temp_k,
		.idx = PCT2075_SOC,
	},
	[TEMP_SENSOR_CHARGER] = {
		.name = "Charger",
		.type = TEMP_SENSOR_TYPE_BOARD,
		.read = get_temp_3v3_30k9_47k_4050b,
		.idx = ADC_TEMP_SENSOR_CHARGER,
	},
	[TEMP_SENSOR_MEMORY] = {
		.name = "Memory",
		.type = TEMP_SENSOR_TYPE_BOARD,
		.read = board_get_temp,
		.idx = ADC_TEMP_SENSOR_MEMORY,
	},
	[TEMP_SENSOR_5V_REGULATOR] = {
		.name = "5V_REGULATOR",
		.type = TEMP_SENSOR_TYPE_BOARD,
		.read = board_get_temp,
		.idx = ADC_TEMP_SENSOR_5V_REGULATOR,
	},
	[TEMP_SENSOR_CPU] = {
		.name = "CPU",
		.type = TEMP_SENSOR_TYPE_CPU,
		.read = sb_tsi_get_val,
		.idx = 0,
	},
	[TEMP_SENSOR_AMBIENT] = {
		.name = "Ambient",
		.type = TEMP_SENSOR_TYPE_BOARD,
		.read = pct2075_get_val_k,
		.idx = PCT2075_AMB,
	},
};
BUILD_ASSERT(ARRAY_SIZE(temp_sensors) == TEMP_SENSOR_COUNT);

struct ec_thermal_config thermal_params[TEMP_SENSOR_COUNT] = {
	[TEMP_SENSOR_SOC] = {
		.temp_host = {
			[EC_TEMP_THRESH_HIGH] = C_TO_K(80),
			[EC_TEMP_THRESH_HALT] = C_TO_K(83),
		},
		.temp_host_release = {
			[EC_TEMP_THRESH_HIGH] = C_TO_K(75),
		},
	},
	[TEMP_SENSOR_CHARGER] = {
		.temp_host = {
			[EC_TEMP_THRESH_HIGH] = C_TO_K(77),
			[EC_TEMP_THRESH_HALT] = C_TO_K(81),
		},
		.temp_host_release = {
			[EC_TEMP_THRESH_HIGH] = C_TO_K(72),
		},
	},
	[TEMP_SENSOR_MEMORY] = {
		.temp_host = {
			[EC_TEMP_THRESH_HIGH] = C_TO_K(80),
			[EC_TEMP_THRESH_HALT] = C_TO_K(83),
		},
		.temp_host_release = {
			[EC_TEMP_THRESH_HIGH] = C_TO_K(75),
		},
	},
	[TEMP_SENSOR_5V_REGULATOR] = {
		.temp_host = {
			[EC_TEMP_THRESH_HIGH] = C_TO_K(54),
			[EC_TEMP_THRESH_HALT] = C_TO_K(57),
		},
		.temp_host_release = {
			[EC_TEMP_THRESH_HIGH] = C_TO_K(47),
		},
	},
	[TEMP_SENSOR_CPU] = {
		.temp_host = {
			[EC_TEMP_THRESH_HIGH] = C_TO_K(100),
			[EC_TEMP_THRESH_HALT] = C_TO_K(105),
		},
		.temp_host_release = {
			[EC_TEMP_THRESH_HIGH] = C_TO_K(80),
		},
	},
	/*
	 * Note: Leave ambient entries at 0, both as it does not represent a
	 * hotspot and as not all boards have this sensor
	 */
};
BUILD_ASSERT(ARRAY_SIZE(thermal_params) == TEMP_SENSOR_COUNT);

static int board_get_temp(int idx, int *temp_k)
{
	if (chipset_in_state(CHIPSET_STATE_HARD_OFF))
		return EC_ERROR_NOT_POWERED;
	return get_temp_3v3_30k9_47k_4050b(idx, temp_k);
}

static int check_hdmi_hpd_status(void)
{
	if (get_board_version() > 1)
		return gpio_get_level(GPIO_HPD_EC_IN);
	else
		return true;
}

/* Called on AP resume to S0 */
static void board_chipset_resume(void)
{
	ioex_set_level(IOEX_USB_A1_PD_R_L, 1);
	ioex_set_level(IOEX_HDMI_DATA_EN, 1);
	ioex_set_level(IOEX_EN_PWR_HDMI, 1);
	msleep(PI3HDX1204_POWER_ON_DELAY_MS);
	pi3hdx1204_enable(I2C_PORT_TCPC1,
		PI3HDX1204_I2C_ADDR_FLAGS,
		check_hdmi_hpd_status());
}
DECLARE_HOOK(HOOK_CHIPSET_RESUME, board_chipset_resume, HOOK_PRIO_DEFAULT);

/* Called on AP suspend */
static void board_chipset_suspend(void)
{
	pi3hdx1204_enable(I2C_PORT_TCPC1,
		PI3HDX1204_I2C_ADDR_FLAGS, 0);
	ioex_set_level(IOEX_EN_PWR_HDMI, 0);
	ioex_set_level(IOEX_HDMI_DATA_EN, 0);
	ioex_set_level(IOEX_USB_A1_PD_R_L, 0);
}
DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, board_chipset_suspend, HOOK_PRIO_DEFAULT);

/*
 * With privacy screen, with keyboard backlight
 */
static const struct ec_response_keybd_config keybd_w_privacy_w_kblight = {
	.num_top_row_keys = 13,
	.action_keys = {
		TK_BACK,			/* T1 */
		TK_REFRESH,			/* T2 */
		TK_FULLSCREEN,			/* T3 */
		TK_OVERVIEW,			/* T4 */
		TK_SNAPSHOT,			/* T5 */
		TK_BRIGHTNESS_DOWN,		/* T6 */
		TK_BRIGHTNESS_UP,		/* T7 */
		TK_PRIVACY_SCRN_TOGGLE,		/* T8 */
		TK_KBD_BKLIGHT_TOGGLE,		/* T9 */
		TK_MICMUTE,			/* T10 */
		TK_VOL_MUTE,			/* T11 */
		TK_VOL_DOWN,			/* T12 */
		TK_VOL_UP,			/* T13 */
	},
	.capabilities = KEYBD_CAP_SCRNLOCK_KEY,
};

/*
 * Without privacy screen, with keyboard backlight
 */
static const struct ec_response_keybd_config keybd_wo_privacy_w_kblight = {
	.num_top_row_keys = 13,
	.action_keys = {
		TK_BACK,			/* T1 */
		TK_REFRESH,			/* T2 */
		TK_FULLSCREEN,			/* T3 */
		TK_OVERVIEW,			/* T4 */
		TK_SNAPSHOT,			/* T5 */
		TK_BRIGHTNESS_DOWN,		/* T6 */
		TK_BRIGHTNESS_UP,		/* T7 */
		TK_KBD_BKLIGHT_TOGGLE,		/* T8 */
		TK_PLAY_PAUSE,			/* T9 */
		TK_MICMUTE,			/* T10 */
		TK_VOL_MUTE,			/* T11 */
		TK_VOL_DOWN,			/* T12 */
		TK_VOL_UP,			/* T13 */
	},
	.capabilities = KEYBD_CAP_SCRNLOCK_KEY,
};

/*
 * With privacy screen, without keyboard backlight
 */
static const struct ec_response_keybd_config keybd_w_privacy_wo_kblight = {
	.num_top_row_keys = 13,
	.action_keys = {
		TK_BACK,			/* T1 */
		TK_REFRESH,			/* T2 */
		TK_FULLSCREEN,			/* T3 */
		TK_OVERVIEW,			/* T4 */
		TK_SNAPSHOT,			/* T5 */
		TK_BRIGHTNESS_DOWN,		/* T6 */
		TK_BRIGHTNESS_UP,		/* T7 */
		TK_PRIVACY_SCRN_TOGGLE,		/* T8 */
		TK_PLAY_PAUSE,			/* T9 */
		TK_MICMUTE,			/* T10 */
		TK_VOL_MUTE,			/* T11 */
		TK_VOL_DOWN,			/* T12 */
		TK_VOL_UP,			/* T13 */
	},
	.capabilities = KEYBD_CAP_SCRNLOCK_KEY,
};

/*
 * Without privacy screen, without keyboard backlight
 */
static const struct ec_response_keybd_config keybd_wo_privacy_wo_kblight = {
	.num_top_row_keys = 13,
	.action_keys = {
		TK_BACK,			/* T1 */
		TK_REFRESH,			/* T2 */
		TK_FULLSCREEN,			/* T3 */
		TK_OVERVIEW,			/* T4 */
		TK_SNAPSHOT,			/* T5 */
		TK_BRIGHTNESS_DOWN,		/* T6 */
		TK_BRIGHTNESS_UP,		/* T7 */
		TK_PREV_TRACK,			/* T8 */
		TK_PLAY_PAUSE,			/* T9 */
		TK_MICMUTE,			/* T10 */
		TK_VOL_MUTE,			/* T11 */
		TK_VOL_DOWN,			/* T12 */
		TK_VOL_UP,			/* T13 */
	},
	.capabilities = KEYBD_CAP_SCRNLOCK_KEY,
};

__override const struct ec_response_keybd_config *
board_vivaldi_keybd_config(void)
{
	if (board_has_privacy_panel() && board_has_kblight())
		return &keybd_w_privacy_w_kblight;
	else if (!board_has_privacy_panel() && board_has_kblight())
		return &keybd_wo_privacy_w_kblight;
	else if (board_has_privacy_panel() && !board_has_kblight())
		return &keybd_w_privacy_wo_kblight;
	else
		return &keybd_wo_privacy_wo_kblight;
}

const struct pi3hdx1204_tuning pi3hdx1204_tuning = {
	.eq_ch0_ch1_offset = PI3HDX1204_EQ_DB710,
	.eq_ch2_ch3_offset = PI3HDX1204_EQ_DB710,
	.vod_offset = PI3HDX1204_VOD_115_ALL_CHANNELS,
	.de_offset = PI3HDX1204_DE_DB_MINUS5,
};

static void hdmi_hpd_handler(void)
{
	int hpd = check_hdmi_hpd_status();

	ccprints("HDMI HPD %d", hpd);
	pi3hdx1204_enable(I2C_PORT_TCPC1,
			  PI3HDX1204_I2C_ADDR_FLAGS,
			  chipset_in_or_transitioning_to_state(CHIPSET_STATE_ON)
			  && hpd);
}
DECLARE_DEFERRED(hdmi_hpd_handler);

static void hdmi_hpd_interrupt(enum gpio_signal signal)
{
	/* Debounce for 2 msec */
	hook_call_deferred(&hdmi_hpd_handler_data, (2 * MSEC));
}
