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

#include <common.h>
#include <audio_codec.h>
#include <div64.h>
#include <dm.h>
#include <i2c.h>
#include <i2s.h>
#include <sound.h>
#include <asm/gpio.h>

#include "mtk_ad82111.h"

u8 ampinittblad82111[] = {
//DataLen	Address	DataN	DataN+1...
	/* don't need to do sw reset here, hw reset is applied */
	1,	0x00,	0x01,//##State_Control_1
	1,	0x01,	0x81,//##State_Control_2
	1,	0x03,	0x18,//##Master_volume_control
	1,	0x04,	0x18,//##Channel_1_volume_control
	1,	0x05,	0x18,//##Channel_2_volume_control
	1,	0x06,	0xa2,//##Under_Voltage_selection_for_high_voltage_supply
	1,	0x07,	0x36,//##State_control_4
	1,	0x08,	0x6a,//##DRC_limiter_attack/release_rate	
	1,	0x10,	0x20,//##Top_8_bits_of_Attack_Threshold
	1,	0x11,	0x00,//##Middle_8_bits_of_Attack_Threshold
	1,	0x12,	0x00,//##Bottom_8_bits_of_Attack_Threshold
	1,	0x13,	0x7f,//##Top_8_bits_of_Power_Clipping
	1,	0x14,	0xff,//##Middle_8_bits_of_Power_Clipping
	1,	0x15,	0xff,//##Bottom_8_bits_of_Power_Clipping
	1,	0x16,	0x00,//##zero_detection_level_selection
	1,	0x17,	0x00,//##Volume_Fine_Tune
	1,	0x18,	0x40,//##DTC_setting
	1,	0x1a,	0x00,//##First_2_bits_of_Noise_Gate_Attack_Level
	1,	0x1b,	0x00,//##Second_8_bits_of_Noise_Gate_Attack_Level
	1,	0x1c,	0x1a,//##Third_8_bits_of_Noise_Gate_Attack_Level
	1,	0x1d,	0x00,//##Fourth_8_bits_of_Noise_Gate_Attack_Level
	1,	0x1e,	0x00,//##First_8_bits_of_Noise_Gate_Release_Level
	1,	0x1f,	0x00,//##Second_8_bits_of_Noise_Gate_Release_Level
	1,	0x20,	0x53,//##Third_8_bits_of_Noise_Gate_Release_Level
	1,	0x21,	0x00,//##Fourth_8_bits_of_Noise_Gate_Release_Level
	1,	0x22,	0x08,//##Top_8_bits_of_Release_Threshold
	1,	0x23,	0x00,//##Middle_8_bits_of_Release_Threshold
	1,	0x24,	0x00,//##Bottom_8_bits_of_Release_Threshold
	1,	0x25,	0x12,//##Device Number
	1,	0x26,	0x00,//##Top_8_bits_of_DRC_Energy_Coefficient
	1,	0x27,	0x10,//##Bottom_8_bits_of_DRC_Energy_Coefficient
	1,	0x28,	0x0e,//##Hires_application_filter
	1,	0x29,	0x00,//##Mono_Key_High_Byte
	1,	0x2a,	0x00,//##Mono_Key_Low_byte
	1,	0x2b,	0x1f,//##Top_8_bits_of_Prescale_Coefficient
	1,	0x2c,	0xa2,//##Middle_8_bits_of_Prescale_Coefficient
	1,	0x2d,	0x3a,//##Bottom_8_bits_of_Prescale_Coefficient
	1,	0x2e,	0x20,//##Top_8_bits_of_Prescale_Coefficient
	1,	0x2f,	0x00,//##Middle_8_bits_of_Prescale_Coefficient
	1,	0x30,	0x00,//##Bottom_8_bits_of_Prescale_Coefficient
	1,	0x31,	0x05,//##I2S_Data_Output_Selection_Register
	1,	0x32,	0x08,//##Top_8_bits_of_I2SOUT_Gain_Coefficient
	1,	0x33,	0x00,//##Middle_8_bits_of_I2SOUT_Gain_Coefficient
	1,	0x34,	0x00,//##Buttom_8_bits_of_I2SOUT_Gain_Coefficient
	1,	0x3a,	0x00,//##Duty_Control_for_Analog_Gain
	1,	0x3b,	0xfc,//##Error_Register
	1,	0x3c,	0xfc,//##Error_latch_register
	1,	0x3d,	0x00,//##Error_clear_register
	0x00,
};

u8 ampswresettblad82111[] = {
	1,	0x02,	0x00,
	0x00,
};

u8 ampnormaltblad82111[] = {
	1,	0x02,	0x10,
	0x00,
};

u8 ampmutetblad82111[] = {
	1,	0x02,	0x18,
	0x00,
};

u8 ampunmutetblad82111[] = {
	1,	0x02,	0x10,
	0x00,
};

static int mtk_amp_i2c_write(struct udevice *dev, u8 reg, u8 *buf, u8 size)
{
	struct amp_ad82111_priv *priv = dev_get_priv(dev);
	struct i2c_msg msgs;
	u8 msg_buf[10];
	int ret;
	int i;

	msg_buf[0] = reg;

	for (i = 1; i <= size; i++)
		msg_buf[i] = buf[i - 1];

	msgs.addr  = priv->i2c_addr;
	msgs.len   = size + 1;
	msgs.buf   = msg_buf;
	msgs.flags = I2C_M_STOP;

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

	return 0;
}

static int amp_write(struct udevice *dev, unsigned char *inittable)
{
	u8 *pu8str = inittable;
	u8 *buf;
	u8 reg;
	u8 size;
	int ret;

	do {
		size = *pu8str;
		pu8str++;

		reg = *pu8str;
		pu8str++;

		buf = pu8str;
		ret = mtk_amp_i2c_write(dev, reg, buf, size);
		if (ret < 0) {
			dev_err(dev, "[AMP 82111]i2c write fail %d\n", ret);
			return -1;
		}

		pu8str = pu8str + size;
	} while (*pu8str != 0);

	return 0;
}

static int amp_ad82111_powerdown(struct udevice *dev, bool enable)
{
	struct amp_ad82111_priv *priv = dev_get_priv(dev);

	if (dm_gpio_is_valid(&priv->amp_powerdown))
		dm_gpio_set_value(&priv->amp_powerdown, enable);

	return 0;
}

static int amp_ad82111_set_params(struct udevice *dev, int interface, int rate, int mclk_freq,
				  int bits_per_sample, uint channels)
{
	int ret;

	//wait t5 min time = 10ms
	mdelay(10);
	//pull high amp PD pin
	amp_ad82111_powerdown(dev, 0);
	//wait t9 min time = 20ms then we can start I2C command
	mdelay(20);
	//software reset amp, if no have hardware reset pin(24pin's ad82088)
	amp_write(dev, ampswresettblad82111);
	mdelay(5);
	//normal operation
	amp_write(dev, ampnormaltblad82111);
	mdelay(20);
	//set all channel mute during writing all of registers and RAM
	amp_write(dev, ampmutetblad82111);

	ret = amp_write(dev, ampinittblad82111);
	if (ret < 0) {
		dev_err(dev, "[AMP 82111]Unable to init AMP AD82111\n");
		return -EINVAL;
	}

	mdelay(2);
	//set all channel un-mute
	amp_write(dev, ampunmutetblad82111);

	return 0;
}

static int amp_ad82111_probe(struct udevice *dev)
{
	struct amp_ad82111_priv *priv = dev_get_priv(dev);
	int ret;

	if (dev_read_enabled(dev) == 0)
		return 0;

	if (device_get_uclass_id(dev->parent) != UCLASS_I2C) {
		dev_err(dev, "[AMP 82111]get i2c parent fail\n");
		return -EPROTONOSUPPORT;
	}

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

	ret = gpio_request_by_name(dev, "amppowerdown-gpios", 0, &priv->amp_powerdown, GPIOD_IS_OUT);
	if (ret) {
		dev_err(dev, "[AMP 82111]get amp power down gpio fail %d\n", ret);
		return -EINVAL;
	}

	return 0;
}

static const struct audio_codec_ops amp_ad82111_ops = {
	.set_params	= amp_ad82111_set_params,
};

static const struct udevice_id amp_ad82111_ids[] = {
	{ .compatible = "ESMT, ad82111" },
	{ }
};

U_BOOT_DRIVER(ad82111) = {
	.name		= "ad82111",
	.id		= UCLASS_AUDIO_CODEC,
	.of_match	= amp_ad82111_ids,
	.probe		= amp_ad82111_probe,
	.ops		= &amp_ad82111_ops,
	.priv_auto_alloc_size = sizeof(struct amp_ad82111_priv),
};
