// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
 * Copyright (c) 2019 Amlogic, Inc. All rights reserved.
 */

#include <linux/of_device.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/of_address.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#include <linux/amlogic/irblaster.h>
#include <linux/amlogic/irblaster_consumer.h>
#include <linux/pm_runtime.h>
#include <linux/pm_domain.h>

/* Amlogic AO_IR_BLASTER_ADDR0 bits */
#define BLASTER_BUSY				BIT(26)
#define BLASTER_FIFO_FULL			BIT(25)
#define BLASTER_FIFO_EMPTY			BIT(24)
#define BLASTER_FIFO_LEVEL			(0xff << 16)
#define BLASTER_MODULATOR_TB_SYSTEM_CLOCK	(0x0 << 12)
#define BLASTER_MODULATOR_TB_XTAL3_TICK		(0x1 << 12)
#define BLASTER_MODULATOR_TB_1US_TICK		(0x2 << 12)
#define BLASTER_MODULATOR_TB_10US_TICK		(0x3 << 12)
#define BLASTER_SLOW_CLOCK_DIV			(0xff << 4)
#define BLASTER_SLOW_CLOCK_MODE			BIT(3)
#define BLASTER_INIT_HIGH			BIT(2)
#define BLASTER_INIT_LOW			BIT(1)
#define BLASTER_ENABLE				BIT(0)

/* Amlogic AO_IR_BLASTER_ADDR1 bits */
#define BLASTER_MODULATION_LOW_COUNT(c)		((c) << 16)
#define BLASTER_MODULATION_HIGH_COUNT(c)	((c) << 0)

/* Amlogic AO_IR_BLASTER_ADDR2 bits */
#define BLASTER_WRITE_FIFO			BIT(16)
#define BLASTER_MODULATION_ENABLE		BIT(12)
#define BLASTER_TIMEBASE_1US			(0x0 << 10)
#define BLASTER_TIMEBASE_10US			(0x1 << 10)
#define BLASTER_TIMEBASE_100US			(0x2 << 10)
#define BLASTER_TIMEBASE_MODULATION_CLOCK	(0x3 << 10)

/* Amlogic AO_IR_BLASTER_ADDR3 bits */
#define BLASTER_FIFO_THD_PENDING		BIT(16)
#define BLASTER_FIFO_IRQ_ENABLE			BIT(8)
#define BLASTER_FIFO_IRQ_THRESHOLD(c)		(((c) & 0xff) << 0)

#define DEFAULT_CARRIER_FREQ			(38000)
#define DEFAULT_DUTY_CYCLE			(50)
#define LIMIT_DUTY				(25)
#define MAX_DUTY				(75)
#define LIMIT_FREQ				(25000)
#define MAX_FREQ				(60000)
#define COUNT_DELAY_MASK			(0X3ff)
#define TIMEBASE_SHIFT				(10)
#define BLASTER_KFIFO_SIZE			(4)

#define AO_IR_BLASTER_ADDR0			(0x0)
#define AO_IR_BLASTER_ADDR1			(0x4)
#define AO_IR_BLASTER_ADDR2			(0x8)
#define AO_IR_BLASTER_ADDR3			(0xc)
#define AO_RTI_GEN_CTNL_REG0			(0x0)

#define CONSUMERIR_TRANSMIT			0x5500
#define GET_CARRIER				0x5501
#define SET_CARRIER				0x5502
#define SET_DUTYCYCLE				0x5503
#define BLASTER_TIMEBASE_COUNT			(BIT(10) - 1)
#define BLASTER_DEBUG_HIGH			(2)
#define	BLASTER_DELAY_MS			25

struct meson_irblaster_dev {
	struct device *dev;
	struct work_struct blaster_work;
	struct irblaster_chip chip;
	struct completion blaster_completion;
	unsigned int count;
	unsigned int irq;
	unsigned int buffer_size;
	unsigned int *buffer;
	spinlock_t irblaster_lock; /* use to send data */
	void __iomem	*reg_base;
	void __iomem	*reset_base;
};

#define IROUTDebug(fmt, x...)

static void meson_irblaster_tasklet(unsigned long data);
DECLARE_TASKLET_DISABLED_OLD(irblaster_tasklet, meson_irblaster_tasklet);

static struct meson_irblaster_dev *
to_meson_irblaster(struct irblaster_chip *chip)
{
	return container_of(chip, struct meson_irblaster_dev, chip);
}

static void blaster_initialize(struct meson_irblaster_dev *dev)
{
	unsigned int carrier_cycle = DIV_ROUND_CLOSEST(1000,
				  (dev->chip.state.freq / 1000));
	unsigned int high_ct, low_ct;

	/*
	 *1. disable ir blaster
	 *2. set the modulator_tb = 2'10; mpeg_1uS_tick 1us
	 *3. set initializes the output to be high
	 */
	writel_relaxed((~BLASTER_ENABLE) & (BLASTER_MODULATOR_TB_1US_TICK |
		BLASTER_INIT_HIGH), dev->reg_base + AO_IR_BLASTER_ADDR0);
	/*
	 *1. set mod_high_count = 13
	 *2. set mod_low_count = 13
	 *3. 60khz-8us, 38k-13us
	 */
	high_ct = carrier_cycle * dev->chip.state.duty / 100;
	low_ct = carrier_cycle - high_ct;
	writel_relaxed((BLASTER_MODULATION_LOW_COUNT(low_ct - 1) |
		BLASTER_MODULATION_HIGH_COUNT(high_ct - 1)),
			dev->reg_base + AO_IR_BLASTER_ADDR1);
	/*mask initialize output to be high*/
	writel_relaxed(readl_relaxed(dev->reg_base + AO_IR_BLASTER_ADDR0) &
			~BLASTER_INIT_HIGH,
			dev->reg_base + AO_IR_BLASTER_ADDR0);
	/*
	 *1. set fifo irq enable
	 *2. set fifo irq threshold
	 */
	writel_relaxed(BLASTER_FIFO_IRQ_ENABLE |
		BLASTER_FIFO_IRQ_THRESHOLD(8),
		dev->reg_base + AO_IR_BLASTER_ADDR3);
	/*enable irblaster*/
	writel_relaxed(readl_relaxed(dev->reg_base + AO_IR_BLASTER_ADDR0) |
		BLASTER_ENABLE,
		dev->reg_base + AO_IR_BLASTER_ADDR0);
}

static int write_to_fifo(struct meson_irblaster_dev *dev,
			 unsigned int hightime,
			 unsigned int lowtime)
{
	unsigned int count_delay;
	unsigned int cycle = DIV_ROUND_CLOSEST(1000,
			     (dev->chip.state.freq / 1000));
	u32 val;
	int n = 0;
	int tb[3] = {
		1, 10, 100
	};

	/*
	 * hightime: modulator signal.
	 * MODULATOR_TB:
	 *	00:	system clock
	 *	01:	mpeg_xtal3_tick
	 *	10:	mpeg_1uS_tick
	 *	11:	mpeg_10uS_tick
	 *
	 * AO_IR_BLASTER_ADDR2
	 * bit12: output level(or modulation enable/disable:1=enable)
	 * bit[11:10]: Timebase :
	 *			00=1us
	 *			01=10us
	 *			10=100us
	 *			11=Modulator clock
	 * bit[9:0]: Count of timebase units to delay
	 */

	count_delay = ((hightime + cycle / 2) / cycle) - 1;
	if (count_delay < 1024) {
		count_delay &= COUNT_DELAY_MASK;
		val = (BLASTER_WRITE_FIFO | BLASTER_MODULATION_ENABLE |
			BLASTER_TIMEBASE_MODULATION_CLOCK | (count_delay << 0));
	} else {
		count_delay = (((hightime + 100 / 2) / 100) - 1)
			       & COUNT_DELAY_MASK;
		val = (BLASTER_WRITE_FIFO | BLASTER_MODULATION_ENABLE |
			BLASTER_TIMEBASE_100US | (count_delay << 0));
	}

	writel_relaxed(val, dev->reg_base + AO_IR_BLASTER_ADDR2);

	/*
	 * lowtime<1024,n=0,timebase=1us
	 * 1024<=lowtime<10240,n=1,timebase=10us
	 * 10240<=lowtime,n=2,timebase=100us
	 */
	n = lowtime >> 10;
	if (n > 0 && n < 10)
		n = 1;
	else if (n >= 10)
		n = 2;
	lowtime = (lowtime + (tb[n] >> 1)) / tb[n];
	count_delay = (lowtime - 1) & COUNT_DELAY_MASK;
	val = (BLASTER_WRITE_FIFO & (~BLASTER_MODULATION_ENABLE)) |
		(n << TIMEBASE_SHIFT) | (count_delay << 0);
	writel_relaxed(val, dev->reg_base + AO_IR_BLASTER_ADDR2);

	return 0;
}

static int write_to_high_fifo(struct meson_irblaster_dev *dev,
			      unsigned int hightime)
{
	unsigned int count_delay;
	unsigned int cycle = DIV_ROUND_CLOSEST(1000,
			     (dev->chip.state.freq / 1000));
	u32 val;

	/*
	 * hightime: modulator signal.
	 * MODULATOR_TB:
	 *      00:     system clock
	 *      01:     mpeg_xtal3_tick
	 *      10:     mpeg_1uS_tick
	 *      11:     mpeg_10uS_tick
	 *
	 * AO_IR_BLASTER_ADDR2
	 * bit12: output level(or modulation enable/disable:1=enable)
	 * bit[11:10]: Timebase :
	 *                      00=1us
	 *                      01=10us
	 *                      10=100us
	 *                      11=Modulator clock
	 * bit[9:0]: Count of timebase units to delay
	 */

	count_delay = (((hightime + cycle / 2) / cycle) - 1) & COUNT_DELAY_MASK;
	val = (BLASTER_WRITE_FIFO | BLASTER_MODULATION_ENABLE |
	       BLASTER_TIMEBASE_MODULATION_CLOCK | (count_delay << 0));
	writel_relaxed(val, dev->reg_base + AO_IR_BLASTER_ADDR2);

	return 0;
}

static void send_all_data(struct meson_irblaster_dev *dev)
{
	int i, j;
	unsigned int *pdata = NULL;
	unsigned long flags;
	unsigned int cycle, max_time, high_ct, low_ct;

	cycle = DIV_ROUND_CLOSEST(1000, (dev->chip.state.freq / 1000));
	high_ct = cycle * dev->chip.state.duty / 100;
	low_ct = cycle - high_ct;
	max_time = (high_ct + low_ct) * BLASTER_TIMEBASE_COUNT;
	IROUTDebug("send data max_time = %d\n", max_time);

	pdata = &dev->buffer[dev->count];
	spin_lock_irqsave(&dev->irblaster_lock, flags);
	for (i = 0; (i < 120) && (dev->count < dev->buffer_size);) {
		if (*pdata > max_time) {
			for (j = 0; j < (*pdata / max_time); j++) {
				write_to_high_fifo(dev, max_time);
				i++;
			}
			write_to_fifo(dev, *pdata % max_time, *(pdata + 1));
		} else {
			write_to_fifo(dev, *pdata, *(pdata + 1));
		}

		pdata += 2;
		dev->count += 2;
		i += 2;
	}
	spin_unlock_irqrestore(&dev->irblaster_lock, flags);
}

int meson_irblaster_send(struct irblaster_chip *chip,
			 unsigned int *data,
			 unsigned int len)
{
	int ret, i, sum_time = 0;
	unsigned int high_ct, low_ct, cycle, long_len = 0;
	struct meson_irblaster_dev *irblaster_dev = to_meson_irblaster(chip);

	pm_runtime_get_sync(chip->dev);
	init_completion(&irblaster_dev->blaster_completion);

	/*
	 * 1. set mod_high_count = 13
	 * 2. set mod_low_count = 13
	 * 3. 60khz-8us, 38k-13us
	 */
	cycle = DIV_ROUND_CLOSEST(1000,
				  (irblaster_dev->chip.state.freq / 1000));
	high_ct = cycle * irblaster_dev->chip.state.duty / 100;
	low_ct = cycle - high_ct;
	writel_relaxed((BLASTER_MODULATION_LOW_COUNT(low_ct - 1) |
		BLASTER_MODULATION_HIGH_COUNT(high_ct - 1)),
			irblaster_dev->reg_base + AO_IR_BLASTER_ADDR1);

	for (i = 0; i < len; i++) {
		sum_time += data[i];
		if (irblaster_debug) {
			if (irblaster_debug >= BLASTER_DEBUG_HIGH)
				IROUTDebug("ir[%d] = %d\n", i, data[i]);
			if (data[i] > (high_ct + low_ct)
			   * BLASTER_TIMEBASE_COUNT)
				long_len += DIV_ROUND_UP(data[i],
					(high_ct + low_ct) *
					BLASTER_TIMEBASE_COUNT);
		}
	}

	irblaster_dev->buffer = data;
	irblaster_dev->buffer_size = len;
	irblaster_dev->count = 0;

	send_all_data(irblaster_dev);
	IROUTDebug("wait time = %dus len = %d fifosize = %d\n",
		   sum_time, irblaster_dev->buffer_size, len + long_len);
	ret = wait_for_completion_interruptible_timeout
			(&irblaster_dev->blaster_completion,
			msecs_to_jiffies(sum_time / 1000 + BLASTER_DELAY_MS));
	if (!ret) {
		pr_err("failed to send all data ret = %d\n", ret);
		return -ETIMEDOUT;
	}
	IROUTDebug("send finish!\n");
	pm_runtime_put_sync(chip->dev);

	return 0;
}

static struct irblaster_ops meson_irblaster_ops = {
	.send = meson_irblaster_send,
};

static void meson_irblaster_tasklet(unsigned long data)
{
	struct meson_irblaster_dev *dev = (struct meson_irblaster_dev *)data;

	if (dev->count < dev->buffer_size)
		send_all_data(dev);
}

static irqreturn_t meson_blaster_interrupt(int irq, void *dev_id)
{
	struct meson_irblaster_dev *dev = dev_id;

	if (irblaster_debug >= BLASTER_DEBUG_HIGH)
		IROUTDebug("enter irq count = %d size = %d\n",
			   dev->count, dev->buffer_size);
	/*clear pending bit*/
	writel_relaxed(readl_relaxed(dev->reg_base + AO_IR_BLASTER_ADDR3) &
			(~BLASTER_FIFO_THD_PENDING),
				dev->reg_base + AO_IR_BLASTER_ADDR3);

	if (dev->count >= dev->buffer_size) {
		complete(&dev->blaster_completion);
		return IRQ_HANDLED;
	}

	tasklet_schedule(&irblaster_tasklet);
	return IRQ_HANDLED;
}

static int  meson_irblaster_probe(struct platform_device *pdev)
{
	struct meson_irblaster_dev *irblaster_dev = NULL;
	struct resource *reg_mem = NULL;
	void __iomem *reg_base = NULL;
	int err, ret;

	if (!pdev->dev.of_node) {
		dev_err(&pdev->dev, "pdev->dev.of_node == NULL!\n");
		return -EINVAL;
	}

	irblaster_dev = devm_kzalloc(&pdev->dev,
				     sizeof(struct meson_irblaster_dev),
				     GFP_KERNEL);
	if (!irblaster_dev)
		return -ENOMEM;

	spin_lock_init(&irblaster_dev->irblaster_lock);
	platform_set_drvdata(pdev, irblaster_dev);
	irblaster_dev->dev = &pdev->dev;

	reg_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!IS_ERR_OR_NULL(reg_mem)) {
		reg_base = devm_ioremap_resource(&pdev->dev, reg_mem);
		if (IS_ERR(reg_base)) {
			dev_err(&pdev->dev, "reg0: cannot obtain I/O memory region.\n");
			return PTR_ERR(reg_base);
		}
	} else {
		dev_err(&pdev->dev, "get IORESOURCE_MEM error.\n");
		return PTR_ERR(reg_base);
	}

	irblaster_dev->irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
	if (!irblaster_dev->irq) {
		dev_err(&pdev->dev, "irq: Failed to request irq number.\n");
		return -ENODEV;
	}

	init_completion(&irblaster_dev->blaster_completion);
	ret = devm_request_irq(&pdev->dev, irblaster_dev->irq,
			       meson_blaster_interrupt,
			       IRQF_TRIGGER_RISING,
			       dev_name(&pdev->dev),
			       irblaster_dev);
	if (ret) {
		pr_err("Failed to request irq.\n");
		return ret;
	}
	/* init irblaster sysfs */
	ret = irblaster_sysfs_init();
	if (ret) {
		pr_err("Failed to init irblaster sysfs.\n");
		return ret;
	}
	irblaster_dev->reg_base = reg_base;
	irblaster_dev->chip.dev = &pdev->dev;
	irblaster_dev->chip.ops = &meson_irblaster_ops;
	irblaster_dev->chip.of_irblaster_n_cells = 2;
	irblaster_dev->chip.state.freq = DEFAULT_CARRIER_FREQ;
	irblaster_dev->chip.state.duty = DEFAULT_DUTY_CYCLE;

	err = irblasterchip_add(&irblaster_dev->chip);
	if (err < 0) {
		dev_err(&pdev->dev, "failed to register irblaster chip: %d\n",
			err);
		return err;
	}

	irblaster_tasklet.data = (unsigned long)irblaster_dev;
	tasklet_enable(&irblaster_tasklet);
	/*initial blaster*/
	blaster_initialize(irblaster_dev);
	pm_runtime_enable(&pdev->dev);

	return 0;
}

static int meson_irblaster_remove(struct platform_device *pdev)
{
	struct meson_irblaster_dev *irblaster_dev = platform_get_drvdata(pdev);

	tasklet_disable(&irblaster_tasklet);
	tasklet_kill(&irblaster_tasklet);
	irblasterchip_remove(&irblaster_dev->chip);

	return 0;
}

static int meson_irblaster_runtime_suspend(struct device *dev)
{
	return 0;
}

static int meson_irblaster_runtime_resume(struct device *dev)
{
	struct meson_irblaster_dev *irblaster_dev = dev_get_drvdata(dev);

	/*initial blaster*/
	blaster_initialize(irblaster_dev);
	return 0;
}

static const struct of_device_id irblaster_dt_match[] = {
	{
		.compatible = "amlogic, meson_irblaster",
	},
	{},
};
MODULE_DEVICE_TABLE(of, irblaster_dt_match);

static const struct dev_pm_ops meson_irblaster_pm_ops = {
	SET_RUNTIME_PM_OPS(meson_irblaster_runtime_suspend,
			   meson_irblaster_runtime_resume, NULL)
};

static struct platform_driver meson_irblaster_driver = {
	.probe = meson_irblaster_probe,
	.remove = meson_irblaster_remove,
	.driver = {
		.name = "meson_irblaster",
		.owner  = THIS_MODULE,
		.of_match_table = irblaster_dt_match,
		.pm = &meson_irblaster_pm_ops,
	},
};

static int __init meson_irblaster_init(void)
{
	return platform_driver_register(&meson_irblaster_driver);
}

static void __exit meson_irblaster_exit(void)
{
	platform_driver_unregister(&meson_irblaster_driver);
}

fs_initcall_sync(meson_irblaster_init);
module_exit(meson_irblaster_exit);
MODULE_AUTHOR("Amlogic, Inc.");
MODULE_DESCRIPTION("Amlogic ir blaster driver");
MODULE_LICENSE("GPL");
