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

//------------------------------------------------------------------------------
//  Include Files
//------------------------------------------------------------------------------
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/vmalloc.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/rpmsg.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/kthread.h>
#include <linux/time.h>
#include <sound/soc.h>
#include "mtk-reserved-memory.h"
#include "mtk_dbg.h"
#include "voc_common.h"
#include "voc_common_reg.h"
#include "voc_common_vbdma.h"
#include "voc_hal_paganini.h"

//------------------------------------------------------------------------------
//  Macros
//------------------------------------------------------------------------------
#define WAKEUP_PARAMETER_OFFSET               (0x1001000)
#define SEAMLESS_DMA_BUF_SIZE                   (0x10000)     //64KB
#define SEAMLESS_BUF_OFFSET                   (0x1400000)
#define SEAMLESS_BUF_SIZE                       (0x80000)     //512KB
#define REALIGN_BUF_SHIFT           (SEAMLESS_BUF_SIZE/2)     //half size for realign
#define REALIGN_BUF_SIZE                          (0x100)
#define SEAMLESS_COPY_TIMEOUT                      (1000)

#define VOC_OWL_ADDR_MASK                       (0xFFF0)
#define VOC_CHANNEL_2                           (2)
#define VOC_CHANNEL_4                           (4)
#define VOC_CHANNEL_6                           (6)
#define VOC_CHANNEL_8                           (8)
#define VOC_SAMPLE_RATE_8K                      (8000)
#define VOC_SAMPLE_RATE_16K                     (16000)
#define VOC_SAMPLE_RATE_32K                     (32000)
#define VOC_SAMPLE_RATE_48K                     (48000)
#define VOC_BIT_WIDTH_16                        (16)
#define VOC_BIT_WIDTH_24                        (24)
#define VOC_BIT_WIDTH_32                        (32)

//cfg0
#define VOICE_REG_SW_RST_DMA                    (1 << 15)
#define VOICE_REG_WR_BIT_MODE                   (1 << 9)
#define VOICE_REG_WR_FULL_FLAG_CLR              (1 << 7)
#define VOICE_REG_WR_LEVEL_CNT_MASK             (1 << 5)
#define VOICE_REG_WR_TRIG                       (1 << 4)
#define VOICE_REG_WR_SWAP                       (1 << 3)
#define VOICE_REG_WR_FREERUN                    (1 << 2)
#define VOICE_REG_WR_ENABLE                     (1 << 1)
#define VOICE_REG_MIU_REQUSTER_ENABLE           (1 << 0)

//cfg4
#define VOICE_REG_WR_SIZE_POS                   (0)
#define VOICE_REG_WR_SIZE_MSK                   (0xFFFF << VOICE_REG_WR_SIZE_POS)

#define VOICE_DMA_W1_CFG_0                      (0x60)
#define VOICE_DMA_W1_CFG_1                      (0x62)
#define VOICE_DMA_W1_CFG_2                      (0x64)
#define VOICE_DMA_W1_CFG_3                      (0x66)
#define VOICE_DMA_W1_CFG_4                      (0x68)
#define VOICE_DMA_W1_CFG_5                      (0x6A)
#define VOICE_DMA_W1_CFG_6                      (0x6C)
#define VOICE_DMA_W1_CFG_7                      (0x6E)
#define VOICE_DMA_W1_CFG_8                      (0x70)
#define VOICE_DMA_W1_CFG_9                      (0x72)
#define VOICE_DMA_W1_STS_0                      (0x74)
#define VOICE_DMA_W1_STS_1                      (0x76)
#define VOICE_DMA_W1_STS_2                      (0x78)
#define VOICE_DMA_W1_TST_0                      (0x7A)
#define VOICE_DMA_W1_TST_1                      (0x7C)
#define VOICE_DMA_W1_TST_2                      (0x7E)

#define VOICE_MIU_WORD_BYTE_SIZE                (0x10)
#define VOICE_DMA_INTERNAL_FIFO_SIZE            (8)
#define VOICE_SLT_TIME_LIMIT                    (5000)
//------------------------------------------------------------------------------
// Enumerate And Structure
//------------------------------------------------------------------------------
struct mtk_voice_setting_t {
	wait_queue_head_t kthread_waitq;
	struct task_struct *thread_task;
	uint32_t init_version;

	uint32_t miu_base;
	uint64_t owl_mem_cpu_paddr;
	uint8_t *owl_mem_cpu_vaddr;
	uint32_t owl_mem_size;

	uint32_t dma_offset;
	uint32_t seamless_offset;
	uint32_t seamless_size;

	uint32_t sample_rate;
	uint8_t mic_numbers;
	uint8_t bit_width;
	uint8_t wakeup_mode;

	bool wakeup_enable;
	bool keyword_wakeup;
	bool seamless_wr_enable;
	bool seamless_support;
};

struct mtk_voice_reg_t {
	phys_addr_t paganini;  //0x432
	phys_addr_t pm_sleep;
	phys_addr_t pm_top;
	phys_addr_t pm_misc;
	phys_addr_t cpu_int;
	phys_addr_t inturrpt;
	phys_addr_t viv_bdma;
	phys_addr_t pm_pmux;   //0x110
	phys_addr_t block_arbiter_gp1;
	phys_addr_t vivaldi2;
	phys_addr_t MALIBOX;
	phys_addr_t ckgen00_pm;//0x0c
	phys_addr_t ckgen01_pm;//0x0d
	phys_addr_t vad_0;     //0x430
	phys_addr_t vrec;      //0x431
	phys_addr_t coin_cpu;
	phys_addr_t vad_1;
	phys_addr_t pm_ioctl; //0x115
};

struct mtk_voice_wakeup_parameter {
	uint8_t keyword_flag;
	uint32_t dma_offset;
	uint32_t seamless_offset;
	uint32_t seamless_size;
};
//------------------------------------------------------------------------------
//	Global Variables
//------------------------------------------------------------------------------
static struct mtk_voice_reg_t g_voc_reg;
static struct mtk_voice_setting_t g_voc_setting;
//------------------------------------------------------------------------------
//	Local Variables
//------------------------------------------------------------------------------
static uint8_t *dma_virt_addr;
static uint8_t *dma_wr_ptr;
static uint32_t dma_buf_addr;
static uint32_t dma_buf_size;

static uint8_t *seamless_addr;
static uint8_t *seamless_rd_ptr;
static uint8_t *seamless_wr_ptr;
static uint32_t seamless_dma_offset;
static uint32_t seamless_wr_size;
//------------------------------------------------------------------------------
//  Function
//------------------------------------------------------------------------------
static int voc_hal_paganini_clear_seamless_wr_enable_flag(void)
{
	int count = 0;

	while (g_voc_setting.seamless_wr_enable) {
		if (vbdma_busy(0)) {
			voc_warn("[%s]vbdma busy happened, should care\n", __func__);
			vbdma_reset(0);
		}
		udelay(1);
		count++;
		if (count > SEAMLESS_COPY_TIMEOUT) {
			voc_err("[%s]wait seamless wr enable timeout\n", __func__);
			break;
		}
	}
	return 0;
}
//==============================================================================
static int voc_hal_paganini_clear_keyword_flag(void)
{
	struct mtk_voice_wakeup_parameter *parameter;

	g_voc_setting.keyword_wakeup = 0;
	if ((g_voc_setting.owl_mem_cpu_vaddr == 0) ||
		(g_voc_setting.seamless_support == 0))
		return 0;

	parameter = (struct mtk_voice_wakeup_parameter *)
		(g_voc_setting.owl_mem_cpu_vaddr + WAKEUP_PARAMETER_OFFSET);

	memset(parameter, 0, sizeof(struct mtk_voice_wakeup_parameter));
	return 0;
}
//==============================================================================
static int voc_hal_paganini_get_keyword_flag(void)
{
	struct mtk_voice_wakeup_parameter *parameter;

	if ((g_voc_setting.owl_mem_cpu_vaddr == 0) ||
		(g_voc_setting.wakeup_enable == 0))
		return 0;

	parameter = (struct mtk_voice_wakeup_parameter *)
		(g_voc_setting.owl_mem_cpu_vaddr + WAKEUP_PARAMETER_OFFSET);

	voc_info("[%s]keyword_flag:%d\n", __func__, parameter->keyword_flag);
	voc_info("[%s]offset:%x\n", __func__, parameter->dma_offset);
	voc_info("[%s]wptr:%x\n", __func__, parameter->seamless_offset);
	voc_info("[%s]size:%u\n", __func__, parameter->seamless_size);

	g_voc_setting.keyword_wakeup = parameter->keyword_flag;
	g_voc_setting.dma_offset = parameter->dma_offset;
	g_voc_setting.seamless_offset = parameter->seamless_offset;
	g_voc_setting.seamless_size = parameter->seamless_size;
	return g_voc_setting.keyword_wakeup;
}
//==============================================================================
static uint32_t voc_hal_paganini_get_wr_level_cnt_in_seamless(void)
{
	uint16_t value;
	uint32_t wr_levelbytesize;

	value = INREG16(g_voc_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0));
	value |= VOICE_REG_WR_LEVEL_CNT_MASK;
	OUTREG16(g_voc_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0), value);

	value = INREG16(g_voc_reg.vrec + OFFSET8(VOICE_DMA_W1_STS_0));
	value = ((value > VOICE_DMA_INTERNAL_FIFO_SIZE) ?
			(value - VOICE_DMA_INTERNAL_FIFO_SIZE) : 0);
	wr_levelbytesize = value * VOICE_MIU_WORD_BYTE_SIZE;

	value = INREG16(g_voc_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0));
	value &= ~VOICE_REG_WR_LEVEL_CNT_MASK;
	OUTREG16(g_voc_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0), value);

	value = INREG16(g_voc_reg.vrec + OFFSET8(VOC_REG_0X60));
	return wr_levelbytesize;
}
//==============================================================================
static void voc_hal_paganini_set_mcu_read(uint32_t buf_size)
{
	uint16_t value;

	value = (buf_size / VOICE_MIU_WORD_BYTE_SIZE) & VOICE_REG_WR_SIZE_MSK;
	OUTREG16(g_voc_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_4), value);
}
//==============================================================================
static void voc_hal_paganini_mcu_read_trigger(void)
{
	uint16_t value;

	value = INREG16(g_voc_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0));
	value |= VOICE_REG_WR_TRIG;
	OUTREG16(g_voc_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0), value);

	value &= ~VOICE_REG_WR_TRIG;
	OUTREG16(g_voc_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0), value);
}
//==============================================================================
static void voc_hal_paganini_copy_dma_data(uint64_t dst, size_t size)
{
	size_t remain_size = 0;
	size_t copy_size = 0;

	remain_size = size;
	if (g_voc_setting.dma_offset + remain_size > SEAMLESS_DMA_BUF_SIZE) {
		vbdma_copy(0, (uint64_t)g_voc_setting.dma_offset, dst+copy_size,
			SEAMLESS_DMA_BUF_SIZE-g_voc_setting.dma_offset, E_VBDMA_CPtoHK);
		copy_size += SEAMLESS_DMA_BUF_SIZE-g_voc_setting.dma_offset;
		remain_size -= SEAMLESS_DMA_BUF_SIZE-g_voc_setting.dma_offset;
		g_voc_setting.dma_offset = 0;
	}
	vbdma_copy(0, (uint64_t)g_voc_setting.dma_offset, dst+copy_size,
		remain_size, E_VBDMA_CPtoHK);
	g_voc_setting.dma_offset += remain_size;
	if (g_voc_setting.dma_offset == SEAMLESS_DMA_BUF_SIZE)
		g_voc_setting.dma_offset = 0;
}
//==============================================================================
static void voc_hal_paganini_realign_seamless(void)
{
	uint8_t *start, *end;
	uint8_t *src, *dst;

	if (g_voc_setting.owl_mem_cpu_vaddr == NULL)
		return;

	start = g_voc_setting.owl_mem_cpu_vaddr + SEAMLESS_BUF_OFFSET;
	end = start + SEAMLESS_BUF_SIZE - REALIGN_BUF_SHIFT;
	dst = start + REALIGN_BUF_SIZE;
	src = dst + REALIGN_BUF_SIZE;
	while (dst < end) {
		memcpy(dst, src, REALIGN_BUF_SIZE);
		dst += REALIGN_BUF_SIZE;
		src += REALIGN_BUF_SIZE<<1;
	}
	memset(dst, 0, REALIGN_BUF_SHIFT);
}
//==============================================================================
static void voc_hal_paganini_get_seamless_format(void)
{
	uint8_t value;

	value = INREG8(g_voc_reg.vrec + OFFSET8(VOC_REG_0X80));
	g_voc_setting.mic_numbers = ((value & (BIT0|BIT1))+VOC_VAL_1)*VOC_CHANNEL_2;

	if (INREG16(g_voc_reg.vrec + OFFSET8(VOC_REG_0X60)) & VOICE_REG_WR_BIT_MODE)
		g_voc_setting.bit_width  = VOC_BIT_WIDTH_32;
	else
		g_voc_setting.bit_width = VOC_BIT_WIDTH_16;

	value = INREG8(g_voc_reg.vrec + OFFSET8(VOC_REG_0X08));
	g_voc_setting.sample_rate = ((value>>VOC_VAL_4) & (BIT0|BIT1))*VOC_SAMPLE_RATE_16K;
}
//==============================================================================
static void voc_hal_paganini_copy_data(void *dst, void *src, size_t size)
{
	size_t remain_size = 0;
	size_t copy_size = 0;
	uint64_t dma_src;

	remain_size = size;
	if (seamless_wr_size) {
		if (remain_size > seamless_wr_size) {
			if (seamless_rd_ptr > seamless_wr_ptr) {
				copy_size = (seamless_addr + REALIGN_BUF_SHIFT) - seamless_rd_ptr;
				memcpy(src, seamless_rd_ptr, copy_size);
				memcpy(src+copy_size, seamless_addr, seamless_wr_ptr-seamless_addr);
				copy_size += seamless_wr_ptr-seamless_addr;
				remain_size = remain_size - copy_size;
			} else {
				copy_size = seamless_wr_ptr-seamless_rd_ptr;
				memcpy(src, seamless_rd_ptr, copy_size);
				remain_size = remain_size - copy_size;
			}
		} else {
			if (seamless_rd_ptr+remain_size > (seamless_addr + REALIGN_BUF_SHIFT)) {
				copy_size = (seamless_addr + REALIGN_BUF_SHIFT) - seamless_rd_ptr;
				memcpy(src, seamless_rd_ptr, copy_size);
				remain_size = remain_size - copy_size;
				seamless_rd_ptr = seamless_addr;
			}
			memcpy(src+copy_size, seamless_rd_ptr, remain_size);
			seamless_rd_ptr += remain_size;
			return;
		}
	}
	dma_src = dma_buf_addr + ((uint8_t *)src - dma_virt_addr);
	if (seamless_dma_offset + remain_size > SEAMLESS_DMA_BUF_SIZE) {
		vbdma_copy(0, (uint64_t)seamless_dma_offset, dma_src+copy_size,
			SEAMLESS_DMA_BUF_SIZE-seamless_dma_offset, E_VBDMA_CPtoHK);
		copy_size += SEAMLESS_DMA_BUF_SIZE-seamless_dma_offset;
		remain_size -= SEAMLESS_DMA_BUF_SIZE-seamless_dma_offset;
		seamless_dma_offset = 0;
	}
	vbdma_copy(0, (uint64_t)seamless_dma_offset, dma_src+copy_size,
		remain_size, E_VBDMA_CPtoHK);
	seamless_dma_offset += remain_size;
	if (seamless_dma_offset == SEAMLESS_DMA_BUF_SIZE)
		seamless_dma_offset = 0;
}
//==============================================================================
static int voc_hal_paganini_seamless_handler_thread(void *param)
{
	uint64_t paddr, paddr_wr_ptr;
	uint32_t level_cnt;
	uint32_t read_size;
	uint32_t remain_size;

	voc_info("[%s]\n", __func__);

	do {
		wait_event_interruptible(g_voc_setting.kthread_waitq,
					kthread_should_stop() ||
					g_voc_setting.seamless_wr_enable);

		if (kthread_should_stop() || !g_voc_setting.seamless_support) {
			voc_err("[%s]Receive kthread_should_stop\n", __func__);
			break;
		}

		if (g_voc_setting.seamless_wr_enable) {
			paddr = g_voc_setting.owl_mem_cpu_paddr -
					g_voc_setting.miu_base + SEAMLESS_BUF_OFFSET;
			paddr_wr_ptr = paddr + g_voc_setting.seamless_offset;
			level_cnt = voc_hal_paganini_get_wr_level_cnt_in_seamless();
			if (level_cnt > 0) {
				voc_info("[%s] level cnt:%u\n", __func__, level_cnt);
				remain_size = level_cnt;
				if (paddr_wr_ptr + level_cnt > paddr + REALIGN_BUF_SHIFT) {
					read_size = paddr + REALIGN_BUF_SHIFT - paddr_wr_ptr;
					remain_size = level_cnt - read_size;
					voc_hal_paganini_copy_dma_data(paddr_wr_ptr, read_size);
					paddr_wr_ptr = paddr;
				}
				voc_hal_paganini_copy_dma_data(paddr_wr_ptr, remain_size);
				voc_hal_paganini_set_mcu_read(level_cnt);
				voc_hal_paganini_mcu_read_trigger();
				paddr_wr_ptr += remain_size;
				if (paddr_wr_ptr == paddr + REALIGN_BUF_SHIFT)
					paddr_wr_ptr = paddr;
				g_voc_setting.seamless_offset = paddr_wr_ptr - paddr;
				g_voc_setting.seamless_size += level_cnt;
				if (g_voc_setting.seamless_size > REALIGN_BUF_SHIFT)
					g_voc_setting.seamless_size = REALIGN_BUF_SHIFT;
			}
		}
		g_voc_setting.seamless_wr_enable = 0;
	} while (1);

	return 0;
}
//==============================================================================
int voc_hal_paganini_dma_start_channel(void)
{
	INSREG8(g_voc_reg.vrec	   + OFFSET8(VOC_REG_0X84), 0x01, 0x01); // VDMA & AEC Common enable
	INSREG8(g_voc_reg.vrec	   + OFFSET8(VOC_REG_0X60), BIT1, BIT1); // VDMA En
	INSREG8(g_voc_reg.paganini + OFFSET8(VOC_REG_0X24), 0x01, 0x01); // VDMA_VLD_SEL
	INSREG8(g_voc_reg.vrec	   + OFFSET8(VOC_REG_0X02), BIT3, 0x00); // VDMA Pause
	INSREG8(g_voc_reg.vrec     + OFFSET8(VOC_REG_0X61), BIT7, 0x00); // VDMA_ENG_RESET
	return 0;
}
//==============================================================================
int voc_hal_paganini_dma_stop_channel(void)
{
	INSREG8(g_voc_reg.vrec     + OFFSET8(VOC_REG_0X84), 0x01, 0x00); // VDMA & AEC Common enable
	INSREG8(g_voc_reg.vrec     + OFFSET8(VOC_REG_0X60), BIT1, 0x00); // VDMA En
	INSREG8(g_voc_reg.vrec     + OFFSET8(VOC_REG_0X02), BIT3, BIT3); // VDMA Pause
	INSREG8(g_voc_reg.vrec     + OFFSET8(VOC_REG_0X61), BIT7, BIT7); // VDMA_ENG_RESET

	voc_hal_paganini_clear_keyword_flag();
	return 0;
}
//==============================================================================
uint8_t voc_hal_paganini_get_channel(void)
{
	return g_voc_setting.mic_numbers;
}
//==============================================================================
uint32_t voc_hal_paganini_get_sample_rate(void)
{
	return g_voc_setting.sample_rate;
}
//==============================================================================
uint8_t voc_hal_paganini_get_bit_width(void)
{
	return g_voc_setting.bit_width;
}
//==============================================================================
int voc_hal_paganini_dma_config_owl(void)
{
	uint16_t u16Val;

	if (g_voc_setting.owl_mem_cpu_paddr == 0) {
		voc_err("[%s]Not found owl memory map\n", __func__);
		return -EINVAL;
	}

	u16Val = (g_voc_setting.owl_mem_cpu_paddr >> VOC_VAL_16) & VOC_OWL_ADDR_MASK;
	INSREG16(g_voc_reg.paganini + OFFSET8(VOC_REG_0X3E), VOC_OWL_ADDR_MASK, u16Val);

	return 0;
}
//==============================================================================
int voc_hal_paganini_slt_test(int run_time)
{
	uint16_t backup_addr_lo, backup_addr_hi;

	if (g_voc_setting.init_version == 0)
		return -EFAULT;

	if (run_time > VOICE_SLT_TIME_LIMIT)
		return -EFAULT;

	backup_addr_lo = INREG16(g_voc_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_1));
	backup_addr_hi = INREG16(g_voc_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_2));
	OUTREG16(g_voc_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_1), VOC_REG_0X00);
	OUTREG16(g_voc_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_2), VOC_REG_0X00);
	voc_hal_paganini_dma_reset();

	//incgen on
	INSREG8(g_voc_reg.vrec + OFFSET8(VOC_REG_0X06), VOC_REG_0X30, VOC_REG_0X00);
	INSREG8(g_voc_reg.vrec + OFFSET8(VOC_REG_0XA1), VOC_REG_0X01, VOC_REG_0X01);
	OUTREG8(g_voc_reg.vrec + OFFSET8(VOC_REG_0X15), VOC_BIT_0XFF);
	INSREG8(g_voc_reg.vrec + OFFSET8(VOC_REG_0X11), VOC_REG_0X08, VOC_REG_0X00);

	//start channel
	INSREG8(g_voc_reg.vrec	   + OFFSET8(VOC_REG_0X84), BIT0, BIT0);
	INSREG8(g_voc_reg.vrec	   + OFFSET8(VOC_REG_0X60), BIT1, BIT1);
	INSREG8(g_voc_reg.paganini + OFFSET8(VOC_REG_0X24), BIT0, BIT0);
	INSREG8(g_voc_reg.vrec	   + OFFSET8(VOC_REG_0X02), BIT3, VOC_VAL_0);
	INSREG8(g_voc_reg.vrec     + OFFSET8(VOC_REG_0X61), BIT7, VOC_VAL_0);

	mdelay(run_time);

	//stop channel
	INSREG8(g_voc_reg.vrec     + OFFSET8(VOC_REG_0X84), BIT0, VOC_VAL_0);
	INSREG8(g_voc_reg.vrec     + OFFSET8(VOC_REG_0X60), BIT1, VOC_VAL_0);
	INSREG8(g_voc_reg.vrec     + OFFSET8(VOC_REG_0X02), BIT3, BIT3);
	INSREG8(g_voc_reg.vrec     + OFFSET8(VOC_REG_0X61), BIT7, BIT7);

	//incgen off
	INSREG8(g_voc_reg.vrec + OFFSET8(VOC_REG_0X06), VOC_REG_0X30, VOC_REG_0X00);
	INSREG8(g_voc_reg.vrec + OFFSET8(VOC_REG_0XA1), VOC_REG_0X01, VOC_REG_0X00);
	OUTREG8(g_voc_reg.vrec + OFFSET8(VOC_REG_0X15), VOC_REG_0X00);
	INSREG8(g_voc_reg.vrec + OFFSET8(VOC_REG_0X11), VOC_REG_0X08, VOC_REG_0X00);

	//vbdma copy
	vbdma_copy(0, (uint64_t)0, (uint64_t)dma_buf_addr, dma_buf_size, E_VBDMA_CPtoHK);

	OUTREG16(g_voc_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_1), backup_addr_lo);
	OUTREG16(g_voc_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_2), backup_addr_hi);
	voc_hal_paganini_dma_reset();
	return 0;
}
//==============================================================================
int voc_hal_paganini_enable_vq(bool en)
{
	if (g_voc_setting.owl_mem_cpu_vaddr != NULL)
		g_voc_setting.wakeup_enable = en;
	voc_info("[%s] wakeup_enable:%d(%d)\n", __func__, g_voc_setting.wakeup_enable, en);
	return 0;
}
//==============================================================================
int voc_hal_paganini_config_vq(uint8_t mode)
{
	g_voc_setting.wakeup_mode = mode;
	return 0;
}
//==============================================================================

int voc_hal_paganini_update_snd_model(
				dma_addr_t snd_model_paddr,
				uint32_t size)
{
	UNUSED(snd_model_paddr);
	UNUSED(size);
	return 0;
}
//==============================================================================
int voc_hal_paganini_get_hotword_ver(void)
{
	return 0;
}
//==============================================================================
int voc_hal_paganini_get_hotword_identifier(void)
{
	return 0;
}
//==============================================================================
int voc_hal_paganini_enable_seamless(bool en)
{
	uint16_t u16Val;

	if (!g_voc_setting.seamless_support)
		return 0;

	//Dummy:0x04313E
	voc_info("[%s:%d]enable :%d\n", __func__, __LINE__, (int)en);

	if (en)
		u16Val = 0x0001;
	else
		u16Val = 0x0000;

	INSREG16(g_voc_reg.paganini + OFFSET8(VOC_REG_0X3E), 0x0001, u16Val);

	return 0;
}
//==============================================================================
int voc_hal_paganini_get_seamless_mode(void)
{
	uint16_t u16Val;

	if (!g_voc_setting.seamless_support)
		return 0;

	u16Val = INREG16(g_voc_reg.paganini + OFFSET8(VOC_REG_0X3E));

	//Dummy:0x04313E
	voc_info("[%s:%d]value :%u\n", __func__, __LINE__, u16Val);
	return u16Val & BIT0;
}
//==============================================================================
int voc_hal_paganini_get_keyword_status(void)
{
	return g_voc_setting.keyword_wakeup;
}
//==============================================================================
int voc_hal_paganini_notify_status(int32_t status)
{
	if (status) {
		if (voc_hal_paganini_get_keyword_flag()) {
			if (g_voc_setting.seamless_support &&
				voc_hal_paganini_get_seamless_mode()) {
				voc_hal_paganini_get_seamless_format();
				voc_hal_paganini_realign_seamless();
				g_voc_setting.seamless_wr_enable = 1;
				wake_up_interruptible(&g_voc_setting.kthread_waitq);
			}
		}
	} else {
		if (g_voc_setting.seamless_support)
			voc_hal_paganini_clear_seamless_wr_enable_flag();
		voc_hal_paganini_clear_keyword_flag();
	}
	return 0;
}
//==============================================================================
int voc_hal_paganini_dma_get_reset_status(void)
{
	uint8_t value;

	value = INREG8(g_voc_reg.vrec + OFFSET8(VOC_REG_0X60));
	return value & BIT1;
}
//==============================================================================
void voc_hal_paganini_set_dma_buf_addr(uint32_t u32addr, uint32_t size)
{
	dma_buf_addr = u32addr;
}
//==============================================================================
void voc_hal_paganini_set_virtual_buf_addr(void *pAddr, uint32_t size)
{
	dma_virt_addr = dma_wr_ptr = (void *)pAddr;
	dma_buf_size = size;
	memset(pAddr, 0, size); //set 0 for mute case.
}
//==============================================================================
uint32_t voc_hal_paganini_dma_wr_level_cnt(void)
{
	uint16_t value;
	uint32_t wr_levelbytesize;

	value = INREG16(g_voc_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0));
	value |= VOICE_REG_WR_LEVEL_CNT_MASK;
	OUTREG16(g_voc_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0), value);

	value = INREG16(g_voc_reg.vrec + OFFSET8(VOICE_DMA_W1_STS_0));
	value = ((value > VOICE_DMA_INTERNAL_FIFO_SIZE) ?
			(value - VOICE_DMA_INTERNAL_FIFO_SIZE) : 0);
	wr_levelbytesize = value * VOICE_MIU_WORD_BYTE_SIZE;

	value = INREG16(g_voc_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0));
	value &= ~VOICE_REG_WR_LEVEL_CNT_MASK;
	OUTREG16(g_voc_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0), value);

	//voc_info("wr_levelbytesize =%x\n", wr_levelbytesize);
	return wr_levelbytesize + seamless_wr_size;
}
//==============================================================================
uint32_t voc_hal_paganini_read_dma_data(uint8_t *dst_buf, uint32_t buf_size)
{
	uint32_t read_size, remain_size, dma_data_size;

	if (!g_voc_setting.seamless_support)
		return 0;

	dma_data_size = voc_hal_paganini_dma_wr_level_cnt();
	if (dma_data_size == 0)
		return 0;

	if (buf_size > dma_data_size)
		buf_size = dma_data_size;

	if ((unsigned long)dma_wr_ptr + buf_size <= (unsigned long)dma_virt_addr + dma_buf_size) {
		read_size = buf_size;
		remain_size = 0;
		voc_hal_paganini_copy_data(dst_buf, dma_wr_ptr, read_size);
		dma_wr_ptr += read_size;

		if ((unsigned long)dma_wr_ptr == (unsigned long)dma_virt_addr + dma_buf_size)
			dma_wr_ptr = dma_virt_addr;

	} else {
		read_size = (unsigned long)dma_virt_addr + dma_buf_size - (unsigned long)dma_wr_ptr;
		remain_size = buf_size - read_size;
		voc_hal_paganini_copy_data(dst_buf, dma_wr_ptr, read_size);
		dma_wr_ptr = dma_virt_addr;
	}

	if (remain_size > 0) {
		voc_hal_paganini_copy_data(dst_buf + read_size, dma_wr_ptr, remain_size);
		dma_wr_ptr += remain_size;
	}

	if (seamless_wr_size && (buf_size <= seamless_wr_size)) {
		seamless_wr_size -= buf_size;
		return buf_size;
	}

	voc_hal_paganini_set_mcu_read(buf_size - seamless_wr_size);
	voc_hal_paganini_mcu_read_trigger();
	seamless_wr_size = 0;
	return buf_size;
}
//==============================================================================
int voc_hal_paganini_open(uint32_t sample_rate, uint32_t bit_width, uint32_t channels)
{
	if ((!g_voc_setting.seamless_support) ||
		(!g_voc_setting.keyword_wakeup) ||
		(!voc_hal_paganini_get_seamless_mode()))
		return -EINVAL;

	if ((sample_rate != g_voc_setting.sample_rate) ||
		(bit_width != g_voc_setting.bit_width) ||
		(channels != g_voc_setting.mic_numbers))
		return -EINVAL;

	voc_hal_paganini_clear_seamless_wr_enable_flag();
	seamless_dma_offset = g_voc_setting.dma_offset;
	seamless_wr_size = g_voc_setting.seamless_size;
	seamless_addr = g_voc_setting.owl_mem_cpu_vaddr + SEAMLESS_BUF_OFFSET;
	seamless_wr_ptr = seamless_addr + g_voc_setting.seamless_offset;
	if (seamless_wr_size > g_voc_setting.seamless_offset)
		seamless_rd_ptr = seamless_addr + REALIGN_BUF_SHIFT -
			(seamless_wr_size - g_voc_setting.seamless_offset);
	else
		seamless_rd_ptr = seamless_wr_ptr - seamless_wr_size;

	voc_info("[%s]seamless capture\n", __func__);
	return 0;
}
//==============================================================================
void voc_hal_paganini_dma_reset(void)
{
	uint16_t value;

	voc_info("[%s]\n", __func__);

	OUTREG16(g_voc_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_4), 0x0000);

	value = INREG16(g_voc_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0));
	value |= VOICE_REG_SW_RST_DMA;
	OUTREG16(g_voc_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0), value);

	mdelay(VOC_VAL_1);

	value &= ~VOICE_REG_SW_RST_DMA;
	OUTREG16(g_voc_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0), value);

	value = INREG16(g_voc_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0));
	value |= (VOICE_REG_WR_SWAP | VOICE_REG_MIU_REQUSTER_ENABLE);
	OUTREG16(g_voc_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0), value);

	/* reset pointer */
	dma_wr_ptr = dma_virt_addr;

	/* reset seamless */
	seamless_wr_size = 0;
	seamless_wr_ptr = g_voc_setting.owl_mem_cpu_vaddr + SEAMLESS_BUF_OFFSET;
	seamless_rd_ptr = g_voc_setting.owl_mem_cpu_vaddr + SEAMLESS_BUF_OFFSET;
}
//==============================================================================
static int voc_hal_paganini_setting_init(void)
{
	struct device_node *np;
	struct of_mmap_info_data of_mmap_info = {0};
	uint32_t init_version = 0;
	uint32_t owl_support = 0;
	uint32_t seamless_support = 0;
	uint32_t len = 0;
	__be32 *p = NULL;

	memset(&g_voc_setting, 0, sizeof(struct mtk_voice_setting_t));
	np = of_find_compatible_node(NULL, NULL, "mediatek,mtk-mic-machine");
	if (np) {
		if (of_property_read_u32(np, "mic-init-version", &init_version) == 0)
			g_voc_setting.init_version = init_version;

		if (of_property_read_u32(np, "owl_support", &owl_support) != 0)
			owl_support = 0;

		if (of_property_read_u32(np, "owl-seamless", &seamless_support) == 0)
			g_voc_setting.seamless_support = seamless_support;

		if (owl_support &&
			of_mtk_get_reserved_memory_info_by_idx(np, 1, &of_mmap_info) == 0) {
			g_voc_setting.owl_mem_cpu_paddr = (uint64_t)of_mmap_info.start_bus_address;
			g_voc_setting.owl_mem_size = (uint32_t)of_mmap_info.buffer_size;

			voc_info("owl_mem_cpu_paddr:0x%llx\n", g_voc_setting.owl_mem_cpu_paddr);
			voc_info("owl_mem_size:0x%x\n", g_voc_setting.owl_mem_size);
			g_voc_setting.owl_mem_cpu_vaddr = (u8 *)ioremap_wc(
				g_voc_setting.owl_mem_cpu_paddr,
				g_voc_setting.owl_mem_size);
		}
		of_node_put(np);
	}

	// find memory_info node in dts
	np = of_find_node_by_name(NULL, "memory_info");
	if (!np)
		return -ENODEV;

	// query cpu_emi0_base value from memory_info node
	p = (__be32 *)of_get_property(np, "cpu_emi0_base", &len);
	if (p == NULL) {
		of_node_put(np);
		return -EINVAL;
	}
	g_voc_setting.miu_base = be32_to_cpup(p);
	of_node_put(np);

	if (g_voc_setting.seamless_support) {
		init_waitqueue_head(&g_voc_setting.kthread_waitq);
		g_voc_setting.thread_task = kthread_run(voc_hal_paganini_seamless_handler_thread,
				0, "paganini_seamless_task");
	}

	g_voc_setting.mic_numbers = VOC_CHANNEL_4;
	g_voc_setting.bit_width   = VOC_BIT_WIDTH_16;
	g_voc_setting.sample_rate = VOC_SAMPLE_RATE_16K;
	g_voc_setting.wakeup_enable = 0;
	g_voc_setting.wakeup_mode = VOC_WAKEUP_MODE_OFF;
	return 0;
}
//==============================================================================
static int voc_hal_paganini_dts_init(void)
{
	int ret = 0;
	struct device_node *np;

	np = of_find_compatible_node(NULL, NULL, "mediatek,mtk-mic-dma");
	if (!np)
		return -EFAULT;

	g_voc_reg.paganini	 = (phys_addr_t)of_iomap(np, 0); //0x432
	g_voc_reg.viv_bdma   = (phys_addr_t)of_iomap(np, VOC_VAL_6);
	g_voc_reg.vrec		 = (phys_addr_t)of_iomap(np, VOC_VAL_14); //0x431
	of_node_put(np);
	if ((g_voc_reg.paganini   == 0) ||
		(g_voc_reg.viv_bdma   == 0) ||
		(g_voc_reg.vrec       == 0))
		return -EFAULT;

	return ret;
}

int voc_hal_paganini_init(void)
{
	int ret = 0;

	ret = voc_hal_paganini_setting_init();
	if (ret) {
		voc_err("[%s] can't parse setting\n", __func__);
		return ret;
	}
	ret = voc_hal_paganini_dts_init();
	if (ret) {
		voc_err("[%s] can't parse dts\n", __func__);
		return ret;
	}
	vbdma_get_base((void __iomem *)g_voc_reg.viv_bdma);
	return ret;
}

