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

//------------------------------------------------------------------------------
//  Include Files
//------------------------------------------------------------------------------
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/vmalloc.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/pinctrl/consumer.h> /* pinctrl API header*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/wait.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/time.h>
#include <linux/random.h>
#include "linux/kern_levels.h"
#include "linux/timekeeping.h"
#include "mtk-reserved-memory.h"
#include "voc_common_reg.h"
#include "voc_mic_cap_iface.h"
#include "clk-dtv.h"   //CLK_PM

//------------------------------------------------------------------------------
// Macros output log
//------------------------------------------------------------------------------
#define mic_cap_iface_err(fmt, ...)		do {if (mic_cap_iface_log_level >= LOGLEVEL_ERR) \
			pr_err(fmt, ##__VA_ARGS__); \
			} while (0)
#define mic_cap_iface_warn(fmt, ...)	do {if (mic_cap_iface_log_level >= LOGLEVEL_WARNING) \
			pr_warn(fmt, ##__VA_ARGS__); \
			} while (0)
#define mic_cap_iface_notice(fmt, ...)	do {if (mic_cap_ifacelog_level >= LOGLEVEL_NOTICE) \
			pr_notice(fmt, ##__VA_ARGS__); \
			} while (0)
#define mic_cap_iface_info(fmt, ...)	do {if (mic_cap_iface_log_level >= LOGLEVEL_INFO) \
			pr_info(fmt, ##__VA_ARGS__); \
			} while (0)
#define mic_cap_iface_debug(fmt, ...)	do {if (mic_cap_iface_log_level >= LOGLEVEL_DEBUG) \
			pr_info(fmt, ##__VA_ARGS__); \
			} while (0)
//------------------------------------------------------------------------------
//  Macros
//------------------------------------------------------------------------------
#define VOICE_MIU_WORD_BYTE_SIZE                (0x10)
#define VOICE_DMA_INTERNAL_FIFO_SIZE            (8)
#define VOICE_SIN_FREQ_VALUE                    (0x2021) //0x5021
#define VOICE_AUDMA_W1_CFG00_VALUE              (0x020B)
#define VOICE_VREC_CTRl06_VALUE	                (0x011F)
#define VOICE_DMIC_CTRL00_VALUE                 (0x0000)
#define VOICE_ATTR_MASK                         (0xFFFF)

#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)

//marix
#define VOICE_REG_MATRIX                        (0xC6)
#define VOICE_REG_MATRIX_MIC_IDX                (0x0)
#define VOICE_REG_MATRIX_REF_IDX                (0x8)
#define VOICE_REG_MATRIX_MASK                   (0xF)
#define VOICE_REG_MATRIX_SHIFT                  (0x4)

//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)

//cfg1
#define VOICE_REG_WR_BASE_ADDR_LO_POS           (0)
#define VOICE_REG_WR_BASE_ADDR_LO_MSK           (0xFFFF << VOICE_REG_WR_BASE_ADDR_LO_POS)

//cfg2
#define VOICE_REG_WR_BASE_ADDR_HI_OFFSET        (16)
#define VOICE_REG_WR_BASE_ADDR_HI_POS           (0)
#define VOICE_REG_WR_BASE_ADDR_HI_MSK           (0xFFFF << VOICE_REG_WR_BASE_ADDR_HI_POS)

//cfg3
#define VOICE_REG_WR_BUFF_SIZE_POS              (0)
#define VOICE_REG_WR_BUFF_SIZE_MSK              (0xFFFF << VOICE_REG_WR_BUFF_SIZE_POS)

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

//cfg5
#define VOICE_REG_WR_OVERRUN_TH_POS             (0)
#define VOICE_REG_WR_OVERRUN_TH_MSK             (0xFFFF << VOICE_REG_WR_OVERRUN_TH_POS)

//cfg8
#define VOICE_REG_WR_LOCAL_FULL_INT_EN          (1 << 2)
#define VOICE_REG_WR_OVERRUN_INT_EN             (1 << 1)
#define VOICE_REG_WR_FULL_INT_EN                (1 << 0)

//sts1
#define VOICE_REG_WR_LOCALBUF_FULL_STATUS       (1 << 9)
#define VOICE_REG_WR_FULL_STATUS                (1 << 8)
#define VOICE_REG_WR_OVERRUN_FLAG               (1 << 1)
#define VOICE_REG_WR_FULL_FLAG                  (1 << 0)

//BCK mode & drive
#define MIC_BCK_MODE_MIN                        (0)
#define MIC_BCK_MODE_MAX                        (10)
#define MIC_BCK_DRIVE_MIN                       (0)
#define MIC_BCK_DRIVE_MAX                       (3)
#define MIC_DEFAULT_BCK_MODE                    (9) //3M
#define MIC_DEFAULT_BCK_DRIVE                   (2) //4mA

//hpf
#define MIC_HPF_COEF_RANGE_MAX                  (9)
#define MIC_HPF_COEF_RANGE_MIN                  (-2)
#define MIC_HPF_COEF_RANGE_BASE                 (3)
#define MIC_HPF_EN_2STAGE                       (1<<1)
#define MIC_HPF_BYP                             (1<<0)
#define MIC_HPF_N_POS                           (8)
#define MIC_HPF_N_MSK                           (0xF << MIC_HPF_N_POS)

//gain
#define MIC_GAIN_FACTOR                         (-1)
#define MIC_GAIN_MAX                            (480)
#define MIC_GAIN_MASK                           (0xFFF)
#define MIC_GAIN_SHIFT                          (15)

//pinctrl
#define MIC_PINCTRL_NAME_PDM      "voc_pin_pdm"
#define MIC_PINCTRL_NAME_I2S      "voc_pin_i2s"
#define MIC_PINCTRL_NAME_I2S_TDM  "voc_pin_i2s_tdm"

//clock
#define MIC_OFFSET_CLKGEN1    (0x200)

//mic
#define MIC_HANDLE_ID_RANGE     (1000)
#define MIC_CHANNEL_2              (2)
#define MIC_CHANNEL_4              (4)
#define MIC_CHANNEL_6              (6)
#define MIC_CHANNEL_8              (8)
#define MIC_SAMPLE_RATE_8K      (8000)
#define MIC_SAMPLE_RATE_16K    (16000)
#define MIC_SAMPLE_RATE_32K    (32000)
#define MIC_SAMPLE_RATE_48K    (48000)
#define MIC_BIT_WIDTH_16          (16)
#define MIC_BIT_WIDTH_24          (24)
#define MIC_BIT_WIDTH_32          (32)
#define MIC_BITS_PER_BYTE          (8)
#define MIC_STR2LONG_BASE_DECIMAL (10)

//Time
#define MIC_MS_PER_SECOND        (1000)
#define MIC_US_PER_MS            (1000)
#define MIC_NS_PER_US            (1000)
#define MIC_NS_PER_MS            (1000 * 1000)
#define MIC_US_PER_SECOND        (MIC_MS_PER_SECOND * MIC_US_PER_MS)

#define MIC_DMA_INT_MS           (16)
#define MIC_DMA_MAX_MS           (64)
#define MIC_DMA_MAX_BYTES        ((MIC_BIT_WIDTH_32 / MIC_BITS_PER_BYTE) * MIC_CHANNEL_8)
#define MIX_DMA_MAX_SAMPLE       (MIC_SAMPLE_RATE_48K / MIC_MS_PER_SECOND)
#define MIC_DMA_MAX_SIZE         (MIX_DMA_MAX_SAMPLE * MIC_DMA_MAX_BYTES * MIC_DMA_MAX_MS)
//------------------------------------------------------------------------------
// Enumerate And Structure
//------------------------------------------------------------------------------
enum {
	CLKGEN0 = 0,
	CLKGEN1
};

enum {
	MIC_INPUT_PDM = 0,
	MIC_INPUT_I2S_RX_MASTER,
	MIC_INPUT_I2S_RX_SLAVE,
	MIC_INPUT_I2S_TDM_RX_MASTER,
	MIC_INPUT_I2S_TDM_RX_SLAVE,
	MIC_INPUT_MAX,
};

enum {
	MIC_TIMER_WAIT_EVENT,
	MIC_TIMER_TRIGGER_EVENT,
	MIC_TIMER_STOP_EVENT,
};

struct voc_mic_iface_reg_t {
	phys_addr_t paganini;
	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;
	phys_addr_t block_arbiter_gp1;
	phys_addr_t vivaldi2;
	phys_addr_t MALIBOX;
	phys_addr_t ckgen00_pm;
	phys_addr_t ckgen01_pm;
	phys_addr_t vad_0;
	phys_addr_t vrec;
	phys_addr_t coin_cpu;
	phys_addr_t vad_1;
	phys_addr_t pm_ioctl;
};

struct voc_mic_iface_setting_t {
	int handle;
	int irq;
	void (*interrupt_callback)(struct mic_iface_intsts_t status);
	struct task_struct *mic_mute_task;
	wait_queue_head_t kthread_waitq;
	struct mutex mic_lock;
	uint32_t init_version;
	uint32_t sample_rate;
	uint8_t max_channels;
	uint8_t channels;
	uint8_t bit_width;
	uint16_t mic_input_mux;
	uint64_t dma_dram_write_base;
	uint64_t dma_paddr;
	uint8_t *dma_vaddr;
	uint32_t miu_base;
	uint32_t dma_size;
	uint16_t gain;
	uint16_t bck_mode;
	uint16_t bck_drive;
	uint16_t onoff;
	uint16_t mute;
	uint16_t sinegen;
	uint16_t incgen;
	uint16_t hpf_mode;
	int32_t hpf_coeff;
	int merged_channel;
	int irq_request;
	int overrun_int_enable;
	int mapping_mode;
	int timer_notify;
	int status;
	uint32_t irq_affinity;
};

//------------------------------------------------------------------------------
//	Local Variables
//------------------------------------------------------------------------------
static struct voc_mic_iface_reg_t g_voc_iface_reg;
static struct voc_mic_iface_setting_t g_voc_iface_setting;
static int mic_cap_iface_log_level;

static uint8_t *dma_buf_addr;
static uint8_t *dma_wr_ptr;
static uint32_t dma_buf_size;
static uint32_t dma_mute_level;
static uint32_t dma_unmute_remain_level;
struct timespec64 dma_mute_timestamp;
//------------------------------------------------------------------------------
//  Clock & Pinctrl Function
//------------------------------------------------------------------------------
static void voc_mic_cap_iface_clock_set(int clk_bank,
	uint32_t reg_addr_8bit,	uint16_t value,	uint32_t start,	uint32_t end)
{
	uint32_t cpu_addr = 0;

	if (clk_bank == CLKGEN1)
		cpu_addr += MIC_OFFSET_CLKGEN1;

	cpu_addr += OFFSET8(reg_addr_8bit); //8bit mode to cpu addr

	clock_write(CLK_PM, cpu_addr, value, start, end);
}

static int voc_mic_cap_iface_pinctrl_init(struct device_node *node, char *pinctrl_name)
{
	struct platform_device *pdev;
	struct pinctrl_state *default_state;
	struct pinctrl *pinctrl;

	pdev = of_find_device_by_node(node);
	if (!pdev)
		return -ENODEV;

	/* get pinctrl handle */
	pinctrl = devm_pinctrl_get(&(pdev->dev));
	if (IS_ERR_OR_NULL(pinctrl))
		return PTR_ERR(pinctrl);

	/* retrieves a state handle from a pinctrl handle */
	default_state = pinctrl_lookup_state(pinctrl, pinctrl_name);
	if (IS_ERR_OR_NULL(default_state)) {
		devm_pinctrl_put(pinctrl);
		return PTR_ERR(default_state);
	}

	/* select/activate/program a pinctrl state to HW */
	pinctrl_select_state(pinctrl, default_state);
	devm_pinctrl_put(pinctrl);
	return 0;
}

static irqreturn_t voc_mic_cap_iface_irq_handler(int irq, void *p)
{
	struct mic_iface_intsts_t status;
	uint16_t ctrl;
	uint16_t value;

	ctrl = INREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_8));
	value = INREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_STS_1));

	status.full = 0;
	if ((ctrl & VOICE_REG_WR_FULL_INT_EN) > 0 &&
		(value & VOICE_REG_WR_FULL_FLAG) > 0)
		status.full = 1;

	status.overrun = 0;
	if ((ctrl & VOICE_REG_WR_OVERRUN_INT_EN) > 0 &&
		(value & VOICE_REG_WR_OVERRUN_FLAG) > 0)
		status.overrun  = 1;

	status.timer = INREG16(g_voc_iface_reg.paganini + OFFSET8(VOC_REG_0X8E)) & BIT0;

	if (g_voc_iface_setting.status == MIC_STATUS_STARTED &&
		g_voc_iface_setting.interrupt_callback)
		g_voc_iface_setting.interrupt_callback(status);
	else {
		if (status.full) {
			value = INREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0));
			value |= VOICE_REG_WR_FULL_FLAG_CLR;
			OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0), value);
		}
		if (status.overrun) {
			value = INREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_8));
			value |= VOICE_REG_WR_OVERRUN_INT_EN;
			OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_8), value);
		}
		if (status.timer) {
			INSREG8(g_voc_iface_reg.paganini + OFFSET8(VOC_REG_0X8C), BIT3, BIT3);
			INSREG8(g_voc_iface_reg.paganini + OFFSET8(VOC_REG_0X8C), BIT3, 0);
		}
	}

	if (status.full)
		g_voc_iface_setting.status = MIC_STATUS_XRUN;

	return IRQ_HANDLED;
}

static int voc_mic_cap_iface_irq_request(void)
{
	struct platform_device *pdev;
	struct device_node *np;
	int ret = 0;
	int cpu_num = 0;

	np = of_find_compatible_node(NULL, NULL, "mediatek,mtk-mic-cap-iface");
	if (!np)
		return -EINVAL;

	pdev = of_find_device_by_node(np);
	if (!pdev) {
		of_node_put(np);
		return -ENODEV;
	}

	g_voc_iface_setting.irq = irq_of_parse_and_map(np, 0);
	//g_voc_iface_setting.irq = of_irq_get(np, 0);

	/* irq affinity */
	if (of_property_read_u32(np, "irq-affinity", &g_voc_iface_setting.irq_affinity))
		g_voc_iface_setting.irq_affinity = U32_MAX;
	else {
		mic_cap_iface_info("irq_affinity (%d)\n",
			g_voc_iface_setting.irq_affinity);
	}

	of_node_put(np);
	if (g_voc_iface_setting.irq < 0)
		return -EINVAL;

	mic_cap_iface_debug("[%s] request irq:%d\n", __func__, g_voc_iface_setting.irq);
	ret = devm_request_irq(&(pdev->dev), g_voc_iface_setting.irq, voc_mic_cap_iface_irq_handler,
			IRQF_TRIGGER_HIGH, "voice_irq", NULL);

	if (ret < 0)
		return ret;

	cpu_num = num_online_cpus();
	if (cpu_num > g_voc_iface_setting.irq_affinity) {
		if (irq_set_affinity_hint(g_voc_iface_setting.irq,
			cpumask_of(g_voc_iface_setting.irq_affinity))) {
			mic_cap_iface_warn("[%s] set irq affinity failed\n",
				__func__);
		}
	}

	return ret;
}

static int voc_mic_cap_iface_irq_release(void)
{
	struct platform_device *pdev;
	struct device_node *np;
	int cpu_num = 0;

	np = of_find_compatible_node(NULL, NULL, "mediatek,mtk-mic-cap-iface");
	if (!np)
		return -EINVAL;

	pdev = of_find_device_by_node(np);
	if (!pdev) {
		of_node_put(np);
		return -ENODEV;
	}

	of_node_put(np);

	cpu_num = num_online_cpus();
	if (cpu_num > g_voc_iface_setting.irq_affinity) {
		if (irq_set_affinity_hint(g_voc_iface_setting.irq, NULL)) {
			mic_cap_iface_warn("[%s] clear irq affinity failed\n",
				__func__);
		}
	}

	devm_free_irq(&(pdev->dev), g_voc_iface_setting.irq, NULL);
	return 0;
}

static uint64_t voc_mic_cap_iface_ktime_to_us(struct timespec64 *ktime)
{
	uint64_t value;

	value = ktime->tv_sec * MIC_US_PER_SECOND;
	value +=  ktime->tv_nsec / MIC_NS_PER_US;
	return value;
}
//------------------------------------------------------------------------------
//  Function
//------------------------------------------------------------------------------
static void voc_mic_cap_iface_dmic_update_bck(uint16_t mute)
{
	uint16_t value;
	uint8_t reset;

	value = INREG16(g_voc_iface_reg.vrec + OFFSET8(VOC_REG_0X08));
	if (mute) {
		value = value & VOC_REG_0XF0; //Clear
		reset = BIT7;
	} else {
		value = (value & VOC_REG_0XF0) + g_voc_iface_setting.bck_mode;
		reset = 0;
	}

	// Mute
	OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOC_REG_0X08), value);

	if (g_voc_iface_setting.status == MIC_STATUS_STARTED)
		INSREG8(g_voc_iface_reg.vrec + OFFSET8(VOC_REG_0X61), BIT7, reset);
}

static int voc_mic_cap_iface_mute_trigger(void *arg)
{
	struct timespec64 tmp_timestamp;
	uintptr_t cur_pos, start_pos;
	uint64_t time_interval;
	uint16_t samples_per_ms;
	uint32_t mute_data_size;
	uint32_t remain_data_size;
	uint32_t next_mute_pos;
	int unmute_trigger = 0;

	for (;;) {
		wait_event_interruptible(g_voc_iface_setting.kthread_waitq,
				kthread_should_stop() || g_voc_iface_setting.timer_notify);

		if (kthread_should_stop() ||
			g_voc_iface_setting.timer_notify == MIC_TIMER_STOP_EVENT)
			break;

		mutex_lock(&(g_voc_iface_setting.mic_lock));
		if (g_voc_iface_setting.status == MIC_STATUS_STARTED) {
			//Updtae mute level count
			samples_per_ms = g_voc_iface_setting.sample_rate / MIC_MS_PER_SECOND;
			ktime_get_ts64(&tmp_timestamp);
			time_interval = voc_mic_cap_iface_ktime_to_us(&tmp_timestamp) -
				voc_mic_cap_iface_ktime_to_us(&dma_mute_timestamp);
			do_div(time_interval, MIC_US_PER_MS);
			mute_data_size = samples_per_ms * g_voc_iface_setting.channels *
				(g_voc_iface_setting.bit_width / MIC_BITS_PER_BYTE) * time_interval;
			dma_mute_level += mute_data_size;

			//Check unmask bck point
			cur_pos = (uintptr_t)dma_wr_ptr;
			start_pos = (uintptr_t)dma_buf_addr;
			next_mute_pos = (cur_pos - start_pos) + dma_mute_level;
			remain_data_size = samples_per_ms * g_voc_iface_setting.channels *
				(g_voc_iface_setting.bit_width / MIC_BITS_PER_BYTE);
			if (dma_unmute_remain_level > 0 && unmute_trigger == 0 &&
				next_mute_pos >= (dma_buf_size - remain_data_size)) {
				voc_mic_cap_iface_dmic_update_bck(0);
				unmute_trigger = 1;
			} else if (dma_unmute_remain_level == 0)
				unmute_trigger = 0;

			mic_cap_iface_debug("[%s] mute level count:%u\n", __func__, dma_mute_level);
			timespec64_add_ns(&dma_mute_timestamp, time_interval * MIC_NS_PER_MS);
		}
		mutex_unlock(&(g_voc_iface_setting.mic_lock));
		usleep_range(MIC_US_PER_MS, MIC_US_PER_MS + 1);
	}

	return 0;
}

static void voc_mic_cap_iface_mcu_read_trigger(uint32_t buf_size)
{
	uint16_t value;

	if (g_voc_iface_setting.mute) {
		dma_mute_level -= (buf_size > dma_mute_level) ? dma_mute_level : buf_size;
		return;
	} else if (dma_unmute_remain_level > 0) {
		dma_mute_level -= (buf_size > dma_mute_level) ? dma_mute_level : buf_size;
		if (buf_size >= dma_unmute_remain_level) {
			g_voc_iface_setting.timer_notify = MIC_TIMER_WAIT_EVENT;
			dma_unmute_remain_level = 0;
		} else
			dma_unmute_remain_level -= buf_size;
		return;
	}

	//Update read size
	value = (buf_size / VOICE_MIU_WORD_BYTE_SIZE) & VOICE_REG_WR_SIZE_MSK;
	OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_4), value);

	//Trigger update
	value = INREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0));
	value |= VOICE_REG_WR_TRIG;
	OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0), value);

	value &= ~VOICE_REG_WR_TRIG;
	OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0), value);

	if (g_voc_iface_setting.overrun_int_enable) {
		value = INREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_8));
		value &= ~VOICE_REG_WR_OVERRUN_INT_EN;
		OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_8), value);
	}
}

static int voc_mic_cap_iface_alloc_dmem(void)
{
	int ret = 0;
	unsigned char *ptr = NULL;
	uint32_t mem_alloc_size = 0;
	uint32_t mem_free_size = g_voc_iface_setting.dma_size;
	phys_addr_t mem_cur_pos = g_voc_iface_setting.dma_paddr;

	mem_alloc_size = (g_voc_iface_setting.sample_rate *	g_voc_iface_setting.channels *
					(g_voc_iface_setting.bit_width / MIC_BITS_PER_BYTE) *
					MIC_DMA_MAX_MS) / MIC_MS_PER_SECOND;

	if (mem_free_size && mem_free_size > mem_alloc_size) {
		ptr = (unsigned char *)ioremap_wc(mem_cur_pos + g_voc_iface_setting.miu_base,
								mem_alloc_size);
		if (!ptr) {
			g_voc_iface_setting.dma_vaddr = NULL;
			ret = -EFAULT;
		} else {
			g_voc_iface_setting.dma_vaddr = (uint8_t *)ptr;
			dma_buf_addr = g_voc_iface_setting.dma_vaddr;
			dma_wr_ptr = dma_buf_addr;
			dma_buf_size = mem_alloc_size;
			dma_mute_level = 0;
			dma_unmute_remain_level = 0;
		}
	} else {
		g_voc_iface_setting.dma_vaddr = NULL;
		ret = -ENOMEM;
	}

	return ret;
}

static void voc_mic_cap_iface_copy_data(void *dst, void *src, size_t size)
{
	if (g_voc_iface_setting.mapping_mode) {
		if (g_voc_iface_setting.mute || dma_unmute_remain_level > 0)
			memset(src, 0, size);
	} else {
		if (g_voc_iface_setting.mute || dma_unmute_remain_level > 0)
			memset(dst, 0, size);
		else
			memcpy(dst, src, size);
	}
}

static int voc_mic_cap_iface_set_sample_rate(uint32_t sample_rate)
{
	uint8_t pdm_mode = 0;

	switch (sample_rate) {
	case MIC_SAMPLE_RATE_8K:
		pdm_mode = VOC_VAL_0;
		break;
	case MIC_SAMPLE_RATE_16K:
		pdm_mode = VOC_VAL_1;
		break;
	case MIC_SAMPLE_RATE_32K:
		pdm_mode = VOC_VAL_2;
		break;
	case MIC_SAMPLE_RATE_48K:
		pdm_mode = VOC_VAL_3;
		break;
	default:
		return -EINVAL;
	}

	pdm_mode <<= VOC_VAL_4;

	INSREG8(g_voc_iface_reg.vrec + OFFSET8(VOC_REG_0X08), VOC_REG_0XF0, pdm_mode);
	return 0;
}

static int voc_mic_cap_iface_set_bit_width(uint8_t bit_width)
{
	uint16_t sel;

	switch (bit_width) {
	case MIC_BIT_WIDTH_16:
		sel = 0;
		break;
	case MIC_BIT_WIDTH_24:
	case MIC_BIT_WIDTH_32:
		sel = VOICE_REG_WR_BIT_MODE;
		break;
	default:
		return -EINVAL;
	}
	INSREG16(g_voc_iface_reg.vrec + OFFSET8(VOC_REG_0X60), VOICE_REG_WR_BIT_MODE, sel);
	if (g_voc_iface_setting.mic_input_mux >= MIC_INPUT_I2S_RX_MASTER)
		INSREG16(g_voc_iface_reg.paganini + OFFSET8(VOC_REG_0X60),
					VOICE_REG_WR_BIT_MODE, sel);

	return 0;
}

static void voc_mic_cap_iface_set_matrix(uint16_t mic, uint16_t ref)
{
	uint16_t value = 0;
	uint16_t mask = 0;
	uint16_t reg = VOICE_REG_MATRIX;
	int offset = 0;
	int i = 0;

	mic_cap_iface_info("merged mic(%d), ref(%d)\n", mic, ref);
	//set mic matrix first
	for (i = 0; i < mic; i++) {
		reg = VOICE_REG_MATRIX + ((offset >> VOC_VAL_2) << VOC_VAL_1);
		value = VOICE_REG_MATRIX_MIC_IDX + offset;
		value = value << (VOICE_REG_MATRIX_SHIFT * (offset % VOC_VAL_4));
		mask = VOICE_REG_MATRIX_MASK;
		mask = mask << (VOICE_REG_MATRIX_SHIFT * (offset % VOC_VAL_4));
		INSREG16(g_voc_iface_reg.paganini + OFFSET8(reg), mask, value);
		offset++;
	}

	//set ref matrix after mic matrix
	for (i = 0; i < ref; i++) {
		reg = VOICE_REG_MATRIX + ((offset >> VOC_VAL_2) << VOC_VAL_1);
		value = VOICE_REG_MATRIX_REF_IDX + (offset - mic);
		value = value << (VOICE_REG_MATRIX_SHIFT * (offset % VOC_VAL_4));
		mask = VOICE_REG_MATRIX_MASK;
		mask = mask << (VOICE_REG_MATRIX_SHIFT * (offset % VOC_VAL_4));
		INSREG16(g_voc_iface_reg.paganini + OFFSET8(reg), mask, value);
		offset++;
	}
}

static int voc_mic_cap_iface_set_channel(uint8_t channels)
{
	uint16_t chn = 0;
	uint16_t merged_chn = 0;
	uint8_t pdm_ch = VOC_REG_0X20;
	uint8_t pdm_filter_en = VOC_REG_0X13;
	uint8_t pdm_reset_filter = VOC_REG_0X0F;
	uint8_t mic_channel = 0;

	mic_channel = (channels - g_voc_iface_setting.merged_channel);
	if (mic_channel > g_voc_iface_setting.max_channels)
		return -EINVAL;

	switch (mic_channel) {
	case MIC_CHANNEL_2:
		pdm_ch = VOC_REG_0X20;
		chn = VOC_VAL_0;
		break;
	case MIC_CHANNEL_4:
		pdm_ch = VOC_REG_0X20;
		chn = VOC_VAL_1;
		break;
	case MIC_CHANNEL_6:
		pdm_filter_en = VOC_REG_0X17;
		pdm_reset_filter = VOC_REG_0X0C;
		pdm_ch = VOC_REG_0X30;
		chn = VOC_VAL_2;
		break;
	case MIC_CHANNEL_8:
		pdm_filter_en = VOC_REG_0X1F;
		pdm_reset_filter = VOC_REG_0X00;
		pdm_ch = VOC_REG_0X30;
		chn = VOC_VAL_3;
		break;
	default:
		return -EINVAL;
	}

	if (g_voc_iface_setting.merged_channel > 0)
		merged_chn = (g_voc_iface_setting.merged_channel)/MIC_CHANNEL_2;

	//PDM filter
	INSREG8(g_voc_iface_reg.vrec + OFFSET8(VOC_REG_0X0C), VOC_REG_0X1F, pdm_filter_en);
	INSREG8(g_voc_iface_reg.vrec + OFFSET8(VOC_REG_0X41), VOC_REG_0X0F, pdm_reset_filter);
	INSREG8(g_voc_iface_reg.vrec + OFFSET8(VOC_REG_0X02), VOC_REG_0X30, pdm_ch);

	//match ch
	INSREG8(g_voc_iface_reg.vrec + OFFSET8(VOC_REG_0X80), VOC_REG_0X07, chn + merged_chn);
	if (g_voc_iface_setting.mic_input_mux >= MIC_INPUT_I2S_RX_MASTER)
		OUTREG8(g_voc_iface_reg.paganini + OFFSET8(VOC_REG_0X28), chn);

	//set matrix
	if (g_voc_iface_setting.init_version)
		voc_mic_cap_iface_set_matrix(mic_channel, g_voc_iface_setting.merged_channel);

	return 0;
}

static void voc_mic_cap_iface_set_mic_i2s_common(uint8_t is_tdm)
{
	uint8_t mode = (is_tdm) ? VOC_REG_0X20 : VOC_REG_0X00;

	//i2s common part setting
	INSREG8(g_voc_iface_reg.vrec       + OFFSET8(VOC_REG_0XA1), VOC_REG_0X01, VOC_REG_0X01);
	INSREG8(g_voc_iface_reg.paganini   + OFFSET8(VOC_REG_0X30), VOC_REG_0X01, VOC_REG_0X01);
	INSREG8(g_voc_iface_reg.paganini   + OFFSET8(VOC_REG_0X30), VOC_REG_0X10, VOC_REG_0X10);
	INSREG8(g_voc_iface_reg.paganini   + OFFSET8(VOC_REG_0X30), VOC_REG_0X40, VOC_REG_0X40);
	INSREG8(g_voc_iface_reg.paganini   + OFFSET8(VOC_REG_0XEC), VOC_REG_0X02, VOC_REG_0X02);
	INSREG8(g_voc_iface_reg.paganini   + OFFSET8(VOC_REG_0X61), VOC_REG_0X80, VOC_REG_0X80);
	INSREG8(g_voc_iface_reg.paganini   + OFFSET8(VOC_REG_0X60), VOC_REG_0X40, VOC_REG_0X00);
	INSREG8(g_voc_iface_reg.paganini   + OFFSET8(VOC_REG_0X61), VOC_REG_0X01, VOC_REG_0X01);
	INSREG8(g_voc_iface_reg.paganini   + OFFSET8(VOC_REG_0X61), VOC_REG_0X20, mode);
	INSREG8(g_voc_iface_reg.ckgen00_pm + OFFSET8(VOC_REG_0X16), VOC_REG_0X1F, VOC_REG_0X04);
	INSREG8(g_voc_iface_reg.ckgen00_pm + OFFSET8(VOC_REG_0X1E), VOC_REG_0X1F, VOC_REG_0X04);
	INSREG8(g_voc_iface_reg.ckgen00_pm + OFFSET8(VOC_REG_0X10), VOC_REG_0X1F, VOC_REG_0X08);
	INSREG8(g_voc_iface_reg.paganini   + OFFSET8(VOC_REG_0X60), VOC_REG_0X80, VOC_REG_0X00);
	INSREG8(g_voc_iface_reg.paganini   + OFFSET8(VOC_REG_0X60), VOC_REG_0X80, VOC_REG_0X00);
	INSREG8(g_voc_iface_reg.paganini   + OFFSET8(VOC_REG_0X61), VOC_REG_0X02, VOC_REG_0X00);
	INSREG8(g_voc_iface_reg.vrec       + OFFSET8(VOC_REG_0X04), VOC_REG_0X04, VOC_REG_0X04);
	INSREG8(g_voc_iface_reg.paganini   + OFFSET8(VOC_REG_0X60), VOC_REG_0X20, VOC_REG_0X00);
	INSREG8(g_voc_iface_reg.paganini   + OFFSET8(VOC_REG_0X62), VOC_REG_0XE0, VOC_REG_0X00);
	INSREG8(g_voc_iface_reg.paganini   + OFFSET8(VOC_REG_0X63), VOC_REG_0X03, VOC_REG_0X00);
	INSREG8(g_voc_iface_reg.ckgen00_pm + OFFSET8(VOC_REG_0X16), VOC_REG_0X1C, VOC_REG_0X04);
	OUTREG8(g_voc_iface_reg.paganini   + OFFSET8(VOC_REG_0XE8), VOC_REG_0X00);
	OUTREG8(g_voc_iface_reg.paganini   + OFFSET8(VOC_REG_0XE9), VOC_REG_0X00);
	OUTREG8(g_voc_iface_reg.paganini   + OFFSET8(VOC_REG_0XEA), VOC_REG_0X79);
	OUTREG8(g_voc_iface_reg.paganini   + OFFSET8(VOC_REG_0XEB), VOC_REG_0X69);

	//REG_CODEC_RX_NF_SYNTH_TRIG
	INSREG8(g_voc_iface_reg.paganini   + OFFSET8(VOC_REG_0XEC), VOC_REG_0X01, VOC_REG_0X00);
	INSREG8(g_voc_iface_reg.paganini   + OFFSET8(VOC_REG_0XEC), VOC_REG_0X01, VOC_REG_0X01);
	INSREG8(g_voc_iface_reg.paganini   + OFFSET8(VOC_REG_0XEC), VOC_REG_0X01, VOC_REG_0X00);

	INSREG8(g_voc_iface_reg.paganini   + OFFSET8(VOC_REG_0X3C), VOC_REG_0X01, VOC_REG_0X00);
	INSREG8(g_voc_iface_reg.vrec       + OFFSET8(VOC_REG_0X0D), VOC_REG_0X04, VOC_REG_0X00);
	INSREG8(g_voc_iface_reg.paganini   + OFFSET8(VOC_REG_0X3C), VOC_REG_0X0C, VOC_REG_0X08);
}

static void voc_mic_cap_iface_set_mic_i2s_master(void)
{
	INSREG8(g_voc_iface_reg.ckgen00_pm + OFFSET8(VOC_REG_0X0E), VOC_REG_0X1F, VOC_REG_0X0C);
	INSREG8(g_voc_iface_reg.paganini   + OFFSET8(VOC_REG_0X61), VOC_REG_0X40, VOC_REG_0X40);
	INSREG8(g_voc_iface_reg.paganini   + OFFSET8(VOC_REG_0X23), VOC_REG_0X80, VOC_REG_0X00);
}

static void voc_mic_cap_iface_set_mic_i2s_slave(void)
{
	INSREG8(g_voc_iface_reg.ckgen00_pm + OFFSET8(VOC_REG_0X0E), VOC_REG_0X1C, VOC_REG_0X04);
	INSREG8(g_voc_iface_reg.paganini   + OFFSET8(VOC_REG_0X61), VOC_REG_0X40, VOC_REG_0X00);
	INSREG8(g_voc_iface_reg.paganini   + OFFSET8(VOC_REG_0X23), VOC_REG_0X80, VOC_REG_0X80);
}

static void voc_mic_cap_iface_input_switch(uint16_t input_mode)
{
	switch (input_mode) {
	case MIC_INPUT_PDM:
		INSREG8(g_voc_iface_reg.vrec + OFFSET8(VOC_REG_0X04), VOC_REG_0X10, VOC_REG_0X00);
		break;
	case MIC_INPUT_I2S_RX_MASTER:
		INSREG8(g_voc_iface_reg.vrec + OFFSET8(VOC_REG_0X04), VOC_REG_0X10, VOC_REG_0X10);
		voc_mic_cap_iface_set_mic_i2s_common(0);
		voc_mic_cap_iface_set_mic_i2s_master();
		break;
	case MIC_INPUT_I2S_RX_SLAVE:
		INSREG8(g_voc_iface_reg.vrec + OFFSET8(VOC_REG_0X04), VOC_REG_0X10, VOC_REG_0X10);
		voc_mic_cap_iface_set_mic_i2s_common(0);
		voc_mic_cap_iface_set_mic_i2s_slave();
		break;
	case MIC_INPUT_I2S_TDM_RX_MASTER:
		INSREG8(g_voc_iface_reg.vrec + OFFSET8(VOC_REG_0X04), VOC_REG_0X10, VOC_REG_0X10);
		voc_mic_cap_iface_set_mic_i2s_common(1);
		voc_mic_cap_iface_set_mic_i2s_master();
		break;
	case MIC_INPUT_I2S_TDM_RX_SLAVE:
		INSREG8(g_voc_iface_reg.vrec + OFFSET8(VOC_REG_0X04), VOC_REG_0X10, VOC_REG_0X10);
		voc_mic_cap_iface_set_mic_i2s_common(1);
		voc_mic_cap_iface_set_mic_i2s_slave();
		break;
	default:
		mic_cap_iface_warn("[%s] Invalid input switch(%d)\n", __func__, input_mode);
	}
}

static int voc_mic_cap_iface_input_mode(uint16_t input_mode)
{
	struct device_node *np;
	int ret = 0;

	if (g_voc_iface_setting.status != MIC_STATUS_INITED)
		return -EINVAL;

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

	switch (input_mode) {
	case MIC_INPUT_PDM:
		ret = voc_mic_cap_iface_pinctrl_init(np, MIC_PINCTRL_NAME_PDM);
		break;
	case MIC_INPUT_I2S_RX_MASTER:
	case MIC_INPUT_I2S_RX_SLAVE:
		ret = voc_mic_cap_iface_pinctrl_init(np, MIC_PINCTRL_NAME_I2S);
		break;
	case MIC_INPUT_I2S_TDM_RX_MASTER:
	case MIC_INPUT_I2S_TDM_RX_SLAVE:
		ret = voc_mic_cap_iface_pinctrl_init(np, MIC_PINCTRL_NAME_I2S_TDM);
		break;
	default:
		mic_cap_iface_err("Invalid pinctrl(%d)\n", input_mode);
		ret = -EINVAL;
		break;
	}

	if (ret == 0) {
		g_voc_iface_setting.mic_input_mux = input_mode;
		voc_mic_cap_iface_input_switch(input_mode);
	}

	of_node_put(np);
	return ret;
}

static int voc_mic_cap_iface_set_bck_mode(uint16_t bck_mode)
{
	if (bck_mode > MIC_BCK_MODE_MAX)
		return -EINVAL;

	if (g_voc_iface_setting.status != MIC_STATUS_INITED)
		return -EFAULT;

	INSREG16(g_voc_iface_reg.vrec + OFFSET8(VOC_REG_0X08), VOC_REG_0X0F, bck_mode);

	g_voc_iface_setting.bck_mode = bck_mode;
	return 0;
}

static int voc_mic_cap_iface_set_bck_drive(uint16_t bck_drive)
{
	if (bck_drive > MIC_BCK_DRIVE_MAX)
		return -EINVAL;

	if (g_voc_iface_setting.status != MIC_STATUS_INITED)
		return -EFAULT;

	INSREG16(g_voc_iface_reg.pm_ioctl + OFFSET8(VOC_REG_0X80), VOC_REG_0X03, bck_drive);

	g_voc_iface_setting.bck_drive = bck_drive;
	return 0;
}

static int voc_mic_cap_iface_set_hpf(int32_t coeff)
{
	uint16_t config_value;

	if (coeff < MIC_HPF_COEF_RANGE_MIN || coeff > MIC_HPF_COEF_RANGE_MAX)
		return -EINVAL;

	config_value = INREG16(g_voc_iface_reg.vrec + OFFSET8(VOC_REG_0XA6));

	//-2~9 mapping to 1~12
	coeff += MIC_HPF_COEF_RANGE_BASE;
	config_value &= ~MIC_HPF_N_MSK;
	config_value |= (coeff << MIC_HPF_N_POS);

	OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOC_REG_0XA6), config_value);

	g_voc_iface_setting.hpf_coeff = coeff;
	return 0;
}


static int voc_mic_cap_iface_dmic_gain(uint16_t gain)
{
	uint16_t value;
	int16_t gain_factor;

	if (gain > MIC_GAIN_MAX)
		return -EINVAL;

	gain_factor = gain * MIC_GAIN_FACTOR;
	INSREG16(g_voc_iface_reg.vrec + OFFSET8(VOC_REG_0XAE), 0x01, 0x01);

	value = ((1 << MIC_GAIN_SHIFT) | (gain_factor & MIC_GAIN_MASK));
	OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOC_REG_0XB2), value);

	value = (gain_factor & MIC_GAIN_MASK);
	OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOC_REG_0XB2), value);

	g_voc_iface_setting.gain = gain;
	return 0;
}

static int voc_mic_cap_iface_dmic_mute(uint16_t mute)
{
	uintptr_t cur_pos, start_pos;
	uint16_t wr_levelbytesize;
	uint16_t value;

	if (mute) {
		if (dma_unmute_remain_level == 0) {
			// Check current level cnt
			value = INREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0));
			value |= VOICE_REG_WR_LEVEL_CNT_MASK;
			OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0), value);

			value = INREG16(g_voc_iface_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_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0));
			value &= ~VOICE_REG_WR_LEVEL_CNT_MASK;
			OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0), value);
		} else {
			wr_levelbytesize = dma_mute_level;
			dma_unmute_remain_level = 0;
		}
		voc_mic_cap_iface_dmic_update_bck(mute);
	} else {
		cur_pos = (uintptr_t)dma_wr_ptr;
		start_pos = (uintptr_t)dma_buf_addr;
		dma_unmute_remain_level = dma_buf_size - (cur_pos - start_pos);
		if (dma_unmute_remain_level == 0 ||
			dma_unmute_remain_level == dma_buf_size) {
			dma_unmute_remain_level = 0;
			voc_mic_cap_iface_dmic_update_bck(mute);
		}
	}

	if (g_voc_iface_setting.status == MIC_STATUS_STARTED) {
		if (mute) {
			dma_mute_level = wr_levelbytesize;
			ktime_get_ts64(&dma_mute_timestamp);
			g_voc_iface_setting.timer_notify = MIC_TIMER_TRIGGER_EVENT;
			wake_up_interruptible(&(g_voc_iface_setting.kthread_waitq));
		} else if (dma_unmute_remain_level == 0) {
			dma_mute_level = 0;
			g_voc_iface_setting.timer_notify = MIC_TIMER_WAIT_EVENT;
		}
	}

	g_voc_iface_setting.mute = mute;
	return 0;
}

static int voc_mic_cap_iface_dmic_switch(uint16_t enable)
{
	if ((g_voc_iface_setting.status != MIC_STATUS_INITED) &&
		(g_voc_iface_setting.status != MIC_STATUS_STOPPED))
		return -EFAULT;

	voc_mic_cap_iface_dmic_mute(!enable);

	//reg_ckg_dig_mic
	if (g_voc_iface_setting.init_version == 1)
		INSREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0X30)),
				VOC_REG_0X01, (enable & VOC_VAL_1));
	else
		voc_mic_cap_iface_clock_set(CLKGEN1,
				VOC_REG_0X14, enable, VOC_VAL_0, VOC_VAL_0);

	g_voc_iface_setting.onoff = enable;
	return 0;
}

static int voc_mic_cap_iface_enable_hpf(uint16_t stage)
{
	uint16_t config_value;

	config_value = INREG16(g_voc_iface_reg.vrec + OFFSET8(VOC_REG_0XA6));

	if (stage == 0)
		config_value |= MIC_HPF_BYP;
	else if (stage == 1)
		config_value &= ~(MIC_HPF_BYP | MIC_HPF_EN_2STAGE);
	else if (stage == VOC_VAL_2) {
		config_value &= ~MIC_HPF_BYP;
		config_value |= MIC_HPF_EN_2STAGE;
	} else
		return -EINVAL;

	OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOC_REG_0XA6), config_value);

	g_voc_iface_setting.hpf_mode = stage;
	return 0;
}

static int voc_mic_cap_iface_enable_sinegen(uint16_t enable)
{
	uint16_t value;

	if (enable)
		value = VOICE_SIN_FREQ_VALUE;
	else
		value = 0;

	//[15:12] SIN_FREQ_SEL
	OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOC_REG_0X06), value);

	g_voc_iface_setting.sinegen = enable;
	return 0;
}

static int voc_mic_cap_iface_enable_incgen(uint16_t enable)
{
	uint8_t value1 = 0;
	uint8_t value2 = 0;

	if (enable) {
		value1 = 0x01;
		value2 = VOC_BIT_0XFF;
	}

	INSREG8(g_voc_iface_reg.vrec + OFFSET8(VOC_REG_0X06), VOC_REG_0X30, VOC_REG_0X00);
	INSREG8(g_voc_iface_reg.vrec + OFFSET8(VOC_REG_0XA1), VOC_REG_0X01, value1);
	OUTREG8(g_voc_iface_reg.vrec + OFFSET8(VOC_REG_0X15), value2);
	INSREG8(g_voc_iface_reg.vrec + OFFSET8(VOC_REG_0X11), VOC_REG_0X08, VOC_REG_0X00);

	g_voc_iface_setting.incgen = enable;
	return 0;
}

static int voc_mic_cap_iface_set_merged_channel(uint16_t channel)
{
	if (g_voc_iface_setting.init_version == 0 ||
		(channel > VOC_VAL_8) ||
		(channel % VOC_VAL_2) != 0)
		return -EINVAL;

	g_voc_iface_setting.merged_channel = channel;
	return 0;
}

static int voc_mic_cap_iface_dts_setting_init(void)
{
	struct device_node *np;
	char *mic_channel = NULL;
	unsigned long ch = MIC_CHANNEL_4;
	int ret = 0;

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

	ret = of_property_read_u32(np, "mic-init-version", &g_voc_iface_setting.init_version);
	if (ret < 0)
		return -EFAULT;

	ret = of_property_read_string(np, "channel", (const char **)&mic_channel);
	if ((ret == 0) && (mic_channel != NULL)) {
		ret = kstrtol(mic_channel, MIC_STR2LONG_BASE_DECIMAL, &ch);
		if (ret < 0 || ch > MIC_CHANNEL_8) {
			mic_cap_iface_warn("Convert mic channel failed:(%s,%lu)!!\n",
				mic_channel, ch);
			ch = MIC_CHANNEL_4;
		}
	}
	g_voc_iface_setting.max_channels = (uint8_t)ch;

	of_node_put(np);
	return ret;
}

static int voc_mic_cap_iface_dts_pad_init(void)
{
	struct device_node *np;
	int ret = 0;

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

	ret = of_property_read_u16(np, "mic-input-mux", &g_voc_iface_setting.mic_input_mux);
	if ((ret < 0) || (g_voc_iface_setting.mic_input_mux >= MIC_INPUT_MAX))
		g_voc_iface_setting.mic_input_mux = 0;

	switch (g_voc_iface_setting.mic_input_mux) {
	case MIC_INPUT_PDM:
		ret = voc_mic_cap_iface_pinctrl_init(np, MIC_PINCTRL_NAME_PDM);
		break;
	case MIC_INPUT_I2S_RX_MASTER:
	case MIC_INPUT_I2S_RX_SLAVE:
		ret = voc_mic_cap_iface_pinctrl_init(np, MIC_PINCTRL_NAME_I2S);
		break;
	case MIC_INPUT_I2S_TDM_RX_MASTER:
	case MIC_INPUT_I2S_TDM_RX_SLAVE:
		ret = voc_mic_cap_iface_pinctrl_init(np, MIC_PINCTRL_NAME_I2S_TDM);
		break;
	default:
		mic_cap_iface_err("Invalid pinctrl(%d)\n", g_voc_iface_setting.mic_input_mux);
		ret = -EINVAL;
		break;
	}

	of_node_put(np);
	return ret;
}

static int voc_mic_cap_iface_dts_reg_init(void)
{
	struct device_node *np;
	int ret = 0;

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

	g_voc_iface_reg.paganini	 = (phys_addr_t)of_iomap(np, 0); //0x432
	g_voc_iface_reg.pm_top	 = (phys_addr_t)of_iomap(np, VOC_VAL_2); //0x101
	g_voc_iface_reg.pm_pmux   = (phys_addr_t)of_iomap(np, VOC_VAL_8); //0x110
	g_voc_iface_reg.ckgen00_pm = (phys_addr_t)of_iomap(np, VOC_VAL_11); //0x0c
	g_voc_iface_reg.ckgen01_pm = (phys_addr_t)of_iomap(np, VOC_VAL_12); //0x0d
	g_voc_iface_reg.vad_0		 = (phys_addr_t)of_iomap(np, VOC_VAL_13); //0x430
	g_voc_iface_reg.vrec		 = (phys_addr_t)of_iomap(np, VOC_VAL_14); //0x431
	g_voc_iface_reg.pm_ioctl	   = (phys_addr_t)of_iomap(np, VOC_VAL_19); //0x115
	of_node_put(np);
	if ((g_voc_iface_reg.paganini   == 0) ||
		(g_voc_iface_reg.pm_top     == 0) ||
		(g_voc_iface_reg.pm_pmux    == 0) ||
		(g_voc_iface_reg.ckgen00_pm == 0) ||
		(g_voc_iface_reg.ckgen01_pm == 0) ||
		(g_voc_iface_reg.vad_0      == 0) ||
		(g_voc_iface_reg.vrec       == 0) ||
		(g_voc_iface_reg.pm_ioctl   == 0))
		ret = -EFAULT;

	return ret;
}

static int voc_mic_cap_iface_memory_init(void)
{
	struct device_node *np;
	__be32 *p = NULL;
	struct of_mmap_info_data of_mmap_info = {0};
	uint64_t dma_dram_write_base = 0;
	uint32_t len = 0;

	// 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_iface_setting.miu_base = be32_to_cpup(p);

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

	if (of_property_read_u64(np, "dma-dram-write-base", &dma_dram_write_base)) {
		of_node_put(np);
		return -EFAULT;
	}
	g_voc_iface_setting.dma_dram_write_base = dma_dram_write_base;

	if (of_mtk_get_reserved_memory_info_by_idx(np, 0, &of_mmap_info) != 0) {
		of_node_put(np);
		return -EFAULT;
	}

	g_voc_iface_setting.dma_paddr = (uint64_t)of_mmap_info.start_bus_address
						- g_voc_iface_setting.miu_base;
	g_voc_iface_setting.dma_size = MIC_DMA_MAX_SIZE;
	if (MIC_DMA_MAX_SIZE > (uint32_t)of_mmap_info.buffer_size)
		g_voc_iface_setting.dma_size = (uint32_t)of_mmap_info.buffer_size;

	of_node_put(np);
	return 0;
}

static void voc_mic_cap_iface_ip_init(void)
{
	if (g_voc_iface_setting.init_version == 1) {
		voc_mic_cap_iface_clock_set(CLKGEN0,
			VOC_REG_0X2E, VOC_REG_0X00, VOC_VAL_0, VOC_VAL_0);
		voc_mic_cap_iface_clock_set(CLKGEN1,
			VOC_REG_0X4E, VOC_REG_0X01, VOC_VAL_4, VOC_VAL_4);
		voc_mic_cap_iface_clock_set(CLKGEN1,
			VOC_REG_0X4E, VOC_REG_0X01, VOC_VAL_14, VOC_VAL_14);
		voc_mic_cap_iface_clock_set(CLKGEN1,
			VOC_REG_0X50, VOC_REG_0X01, VOC_VAL_0, VOC_VAL_0);
		voc_mic_cap_iface_clock_set(CLKGEN1,
			VOC_REG_0X30, VOC_REG_0X01, VOC_VAL_0, VOC_VAL_0);
		INSREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0X30)),
			VOC_REG_0X02, VOC_REG_0X02);

		//vrec dma setting
		OUTREG16((g_voc_iface_reg.vrec)|(OFFSET(VOC_REG_0X6A)), BIT8);
		OUTREG16((g_voc_iface_reg.vrec)|(OFFSET(VOC_REG_0X60)), BIT15);
		OUTREG16((g_voc_iface_reg.vrec)|(OFFSET(VOC_REG_0X80)), BIT0);
		OUTREG16((g_voc_iface_reg.vrec)|(OFFSET(VOC_REG_0X0C)),
			VOICE_VREC_CTRl06_VALUE);
		OUTREG16((g_voc_iface_reg.paganini)|(OFFSET(VOC_REG_0X22)), BIT14);

		//VREC
		INSREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X02)),
			VOC_REG_0X09, VOC_REG_0X09);
		INSREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0XA6)),
			VOC_REG_0X01, VOC_REG_0X01);
		INSREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0XA1)),
			VOC_REG_0X0F, VOC_REG_0X01);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X0C)), VOC_REG_0X1F);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X0D)), VOC_REG_0X81);
		INSREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X08)),
			VOC_REG_0X11, VOC_REG_0X01);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X02)), VOC_REG_0X29);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X03)), VOC_REG_0X00);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X20)), VOC_REG_0X00);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X21)), VOC_REG_0X00);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X40)), VOC_REG_0X09);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X41)), VOC_REG_0X0F);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X0C)), VOC_REG_0X13);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X0D)), VOC_REG_0X01);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X20)), VOC_REG_0X00);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X21)), VOC_REG_0X00);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X22)), VOC_REG_0X03);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X23)), VOC_REG_0X00);
		OUTREG16((g_voc_iface_reg.vrec)|(OFFSET(VOC_REG_0X24)), BIT15);
		INSREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X21)),
			VOC_REG_0X01, VOC_REG_0X01);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X26)), VOC_REG_0X03);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X27)), VOC_REG_0X00);
		OUTREG16((g_voc_iface_reg.vrec)|(OFFSET(VOC_REG_0X28)), BIT15);
		INSREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X21)),
			VOC_REG_0X02, VOC_REG_0X02);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X2A)), VOC_REG_0X03);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X2B)), VOC_REG_0X00);
		OUTREG16((g_voc_iface_reg.vrec)|(OFFSET(VOC_REG_0X2C)), BIT15);
		INSREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X21)),
			VOC_REG_0X04, VOC_REG_0X04);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X2E)), VOC_REG_0X03);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X2F)), VOC_REG_0X00);
		OUTREG16((g_voc_iface_reg.vrec)|(OFFSET(VOC_REG_0X30)), BIT15);
		INSREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X21)),
			VOC_REG_0X08, VOC_REG_0X08);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X42)), VOC_REG_0X01);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X44)), VOC_REG_0X01);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X46)), VOC_REG_0X01);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X48)), VOC_REG_0X01);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X08)), VOC_REG_0X19);
		INSREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X40)),
			VOC_REG_0X09, VOC_REG_0X00);
		INSREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X40)),
			VOC_REG_0X09, VOC_REG_0X01);
	} else {
		// AC ON
		OUTREG16((g_voc_iface_reg.paganini)|(OFFSET(VOC_REG_0XE0)), (BIT15|BIT10));

		// CLK DISABLE
		voc_mic_cap_iface_clock_set(CLKGEN1,
			VOC_REG_0X1E, VOC_REG_0X00, VOC_VAL_0, VOC_VAL_0);
		voc_mic_cap_iface_clock_set(CLKGEN1,
			VOC_REG_0X0C, VOC_REG_0X00, VOC_VAL_0, VOC_VAL_0);
		voc_mic_cap_iface_clock_set(CLKGEN1,
			VOC_REG_0X02, VOC_REG_0X00, VOC_VAL_0, VOC_VAL_0);
		voc_mic_cap_iface_clock_set(CLKGEN1,
			VOC_REG_0X06, VOC_REG_0X00, VOC_VAL_0, VOC_VAL_0);
		voc_mic_cap_iface_clock_set(CLKGEN1,
			VOC_REG_0X08, VOC_REG_0X00, VOC_VAL_0, VOC_VAL_0);
		voc_mic_cap_iface_clock_set(CLKGEN1,
			VOC_REG_0X0A, VOC_REG_0X00, VOC_VAL_0, VOC_VAL_0);
		INSREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0XE6)),
			VOC_REG_0X02, VOC_REG_0X00);
		INSREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0XF3)),
			VOC_REG_0X02, VOC_REG_0X02);
		INSREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0XF7)),
			VOC_REG_0X02, VOC_REG_0X02);

		// CLK SELECT
		voc_mic_cap_iface_clock_set(CLKGEN0,
			VOC_REG_0X16, VOC_REG_0X08, VOC_VAL_0, VOC_VAL_4);
		voc_mic_cap_iface_clock_set(CLKGEN0,
			VOC_REG_0X0E, VOC_REG_0X04, VOC_VAL_0, VOC_VAL_4);
		voc_mic_cap_iface_clock_set(CLKGEN0,
			VOC_REG_0X1E, VOC_REG_0X08, VOC_VAL_0, VOC_VAL_3);
		voc_mic_cap_iface_clock_set(CLKGEN0,
			VOC_REG_0X10, VOC_REG_0X10, VOC_VAL_0, VOC_VAL_4);
		voc_mic_cap_iface_clock_set(CLKGEN0,
			VOC_REG_0X26, VOC_REG_0X04, VOC_VAL_0, VOC_VAL_2);
		INSREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0XEC)),
			VOC_REG_0X02, VOC_REG_0X02);
		INSREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0X61)),
			VOC_REG_0X80, VOC_REG_0X80);

		// CM4 TSV & SYSTICK
		voc_mic_cap_iface_clock_set(CLKGEN0,
			VOC_REG_0X20, VOC_REG_0X04, VOC_VAL_0, VOC_VAL_4);
		voc_mic_cap_iface_clock_set(CLKGEN0,
			VOC_REG_0X1C, VOC_REG_0X04, VOC_VAL_0, VOC_VAL_3);
		voc_mic_cap_iface_clock_set(CLKGEN0,
			VOC_REG_0X00, VOC_REG_0X04, VOC_VAL_0, VOC_VAL_2);
		voc_mic_cap_iface_clock_set(CLKGEN0,
			VOC_REG_0X04, VOC_REG_0X04, VOC_VAL_0, VOC_VAL_3);
		voc_mic_cap_iface_clock_set(CLKGEN0,
			VOC_REG_0X02, VOC_REG_0X04, VOC_VAL_0, VOC_VAL_2);
		voc_mic_cap_iface_clock_set(CLKGEN0,
			VOC_REG_0X0A, VOC_REG_0X04, VOC_VAL_0, VOC_VAL_2);
		voc_mic_cap_iface_clock_set(CLKGEN0,
			VOC_REG_0X06, VOC_REG_0X04, VOC_VAL_0, VOC_VAL_2);
		INSREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0XF6)),
			VOC_REG_0X3F, VOC_REG_0X1F);
		INSREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0XF7)),
			VOC_REG_0X81, VOC_REG_0X00);
		INSREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0XF7)),
			VOC_REG_0X81, VOC_REG_0X01);
		INSREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0XF7)),
			VOC_REG_0X81, VOC_REG_0X00);

		// IMI CLK	XTAIL -> DFS OFF -> PLL -> DFS_ON
		voc_mic_cap_iface_clock_set(CLKGEN0,
			VOC_REG_0X18, VOC_REG_0X04, VOC_VAL_0, VOC_VAL_3);
		voc_mic_cap_iface_clock_set(CLKGEN0,
			VOC_REG_0X1A, VOC_REG_0X04, VOC_VAL_0, VOC_VAL_3);
		INSREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0XF2)),
			VOC_REG_0X3F, VOC_REG_0X1F);
		INSREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0XF3)),
			VOC_REG_0X81, VOC_REG_0X00);
		INSREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0XF3)),
			VOC_REG_0X81, VOC_REG_0X01);
		INSREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0XF3)),
			VOC_REG_0X81, VOC_REG_0X00);

		// CLK Release
		voc_mic_cap_iface_clock_set(CLKGEN1,
			VOC_REG_0X1E, VOC_REG_0X01, VOC_VAL_0, VOC_VAL_0);
		voc_mic_cap_iface_clock_set(CLKGEN1,
			VOC_REG_0X0C, VOC_REG_0X01, VOC_VAL_0, VOC_VAL_0);
		voc_mic_cap_iface_clock_set(CLKGEN1,
			VOC_REG_0X02, VOC_REG_0X01, VOC_VAL_0, VOC_VAL_0);
		voc_mic_cap_iface_clock_set(CLKGEN1,
			VOC_REG_0X06, VOC_REG_0X01, VOC_VAL_0, VOC_VAL_0);
		voc_mic_cap_iface_clock_set(CLKGEN1,
			VOC_REG_0X08, VOC_REG_0X01, VOC_VAL_0, VOC_VAL_0);
		voc_mic_cap_iface_clock_set(CLKGEN1,
			VOC_REG_0X0A, VOC_REG_0X01, VOC_VAL_0, VOC_VAL_0);
		INSREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0XE6)),
			VOC_REG_0X02, VOC_REG_0X02);

		// DMIC BCK Gen Clk
		voc_mic_cap_iface_clock_set(CLKGEN0,
			VOC_REG_0X22, VOC_REG_0X0C, VOC_VAL_0, VOC_VAL_3);
		voc_mic_cap_iface_clock_set(CLKGEN0,
			VOC_REG_0X12, VOC_REG_0X08, VOC_VAL_0, VOC_VAL_3);
		INSREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0XE0)),
			VOC_REG_0X01, VOC_REG_0X01);
		INSREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0XE0)),
			VOC_REG_0XF8, VOC_REG_0X40);
		OUTREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0XE1)), VOC_REG_0X09);

		//vrec dma setting
		OUTREG16((g_voc_iface_reg.vrec)|(OFFSET(VOC_REG_0X62)), BIT8);
		OUTREG16((g_voc_iface_reg.vrec)|(OFFSET(VOC_REG_0X6A)), BIT8);
		OUTREG16((g_voc_iface_reg.vrec)|(OFFSET(VOC_REG_0X60)), BIT15);
		OUTREG16((g_voc_iface_reg.vrec)|(OFFSET(VOC_REG_0X60)),
			VOICE_AUDMA_W1_CFG00_VALUE);
		OUTREG16((g_voc_iface_reg.vrec)|(OFFSET(VOC_REG_0X80)), BIT0);
		OUTREG16((g_voc_iface_reg.vrec)|(OFFSET(VOC_REG_0X22)), BIT0);
		OUTREG16((g_voc_iface_reg.vrec)|(OFFSET(VOC_REG_0X24)), BIT15);
		OUTREG16((g_voc_iface_reg.vrec)|(OFFSET(VOC_REG_0X26)), BIT0);
		OUTREG16((g_voc_iface_reg.vrec)|(OFFSET(VOC_REG_0X28)), BIT15);
		OUTREG16((g_voc_iface_reg.vrec)|(OFFSET(VOC_REG_0X2A)), BIT0);
		OUTREG16((g_voc_iface_reg.vrec)|(OFFSET(VOC_REG_0X2C)), BIT15);
		OUTREG16((g_voc_iface_reg.vrec)|(OFFSET(VOC_REG_0X2E)), BIT0);
		OUTREG16((g_voc_iface_reg.vrec)|(OFFSET(VOC_REG_0X30)), BIT15);
		OUTREG16((g_voc_iface_reg.vrec)|(OFFSET(VOC_REG_0X20)),
			VOICE_DMIC_CTRL00_VALUE);
		OUTREG16((g_voc_iface_reg.vrec)|(OFFSET(VOC_REG_0X0C)),
			VOICE_VREC_CTRl06_VALUE);
		OUTREG16((g_voc_iface_reg.paganini)|(OFFSET(VOC_REG_0X22)), BIT14);

		//VREC
		INSREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X02)),
			VOC_REG_0X09, VOC_REG_0X09);
		INSREG8((g_voc_iface_reg.vad_0)|(OFFSET8(VOC_REG_0X14)),
			VOC_REG_0X01, VOC_REG_0X01);
		INSREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0X3B)),
			VOC_REG_0X0C, VOC_REG_0X04);
		INSREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0XA6)),
			VOC_REG_0X01, VOC_REG_0X01);
		INSREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0XA1)),
			VOC_REG_0X0F, VOC_REG_0X01);
		INSREG8((g_voc_iface_reg.vad_0)|(OFFSET8(VOC_REG_0X03)),
			VOC_REG_0XC0, VOC_REG_0XC0);
		INSREG8((g_voc_iface_reg.vad_0)|(OFFSET8(VOC_REG_0X23)),
			VOC_REG_0X80, VOC_REG_0X80);
		INSREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0X3A)),
			VOC_REG_0X02, VOC_REG_0X02);
		INSREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0X3A)),
			VOC_REG_0X01, VOC_REG_0X01);
		INSREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0X3C)),
			VOC_REG_0X01, VOC_REG_0X01);
		INSREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0X3C)),
			VOC_REG_0X0C, VOC_REG_0X00);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X0C)), VOC_REG_0X1F);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X0D)), VOC_REG_0X81);
		INSREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X08)),
			VOC_REG_0X11, VOC_REG_0X01);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X02)), VOC_REG_0X29);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X03)), VOC_REG_0X00);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X20)), VOC_REG_0X00);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X21)), VOC_REG_0X00);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X40)), VOC_REG_0X09);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X41)), VOC_REG_0X0F);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X0C)), VOC_REG_0X13);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X0D)), VOC_REG_0X05);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X22)), VOC_REG_0X01);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X23)), VOC_REG_0X00);
		INSREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X21)),
			VOC_REG_0X01, VOC_REG_0X01);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X26)), VOC_REG_0X01);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X27)), VOC_REG_0X00);
		INSREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X21)),
			VOC_REG_0X02, VOC_REG_0X02);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X2A)), VOC_REG_0X01);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X2B)), VOC_REG_0X00);
		INSREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X21)),
			VOC_REG_0X04, VOC_REG_0X04);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X2C)), VOC_REG_0X01);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X2D)), VOC_REG_0X00);
		INSREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X21)),
			VOC_REG_0X08, VOC_REG_0X08);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X42)), VOC_REG_0X01);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X44)), VOC_REG_0X01);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X46)), VOC_REG_0X01);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X48)), VOC_REG_0X01);
		OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X08)), VOC_REG_0X19);
		INSREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X40)),
			VOC_REG_0X09, VOC_REG_0X00);
		INSREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0X40)),
			VOC_REG_0X09, VOC_REG_0X01);
	}

	// PPCS REG setting
	OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0XA0)), VOC_REG_0X00);
	OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0XA1)), VOC_REG_0X00);
	OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0XA2)), VOC_REG_0X21);
	OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0XA3)), VOC_REG_0X43);
	OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0XA4)), VOC_REG_0X65);
	OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0XA5)), VOC_REG_0X87);
	OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0XA6)), VOC_REG_0X0A);
	OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0XA7)), VOC_REG_0X73);
	OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0XA8)), VOC_REG_0X00);
	OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0XA9)), VOC_REG_0X00);
	OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0XAA)), VOC_REG_0X00);
	OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0XAB)), VOC_REG_0X00);
	OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0XAC)), VOC_REG_0X00);
	OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0XAD)), VOC_REG_0X00);
	OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0XAE)), VOC_REG_0X06);
	OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0XAF)), VOC_REG_0X07);
	OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0XB0)), VOC_REG_0X00);
	OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0XB1)), VOC_REG_0X00);
	OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0XB2)), VOC_REG_0X00);
	OUTREG8((g_voc_iface_reg.vrec)|(OFFSET8(VOC_REG_0XB3)), VOC_REG_0X00);

	//dma setting
	OUTREG16((g_voc_iface_reg.vrec)|(OFFSET(VOC_REG_0X6A)), BIT8);
	OUTREG16((g_voc_iface_reg.vrec)|(OFFSET(VOC_REG_0X60)), BIT15);
	OUTREG16((g_voc_iface_reg.vrec)|(OFFSET(VOC_REG_0X60)), (BIT0|BIT3));

	if (g_voc_iface_setting.init_version == 1) {
		INSREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0X30)),
			VOC_REG_0X10, VOC_REG_0X10);
		INSREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0X30)),
			VOC_REG_0X40, VOC_REG_0X40);
		INSREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0XEC)),
			VOC_REG_0X02, VOC_REG_0X02);
		INSREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0X61)),
			VOC_REG_0X80, VOC_REG_0X80);

		INSREG16((g_voc_iface_reg.vrec)|(OFFSET(VOC_REG_0X04)), BIT0, BIT0);
	} else {
		//i2s tdm rx setting
		INSREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0X30)),
			VOC_REG_0X01, VOC_REG_0X01);
		INSREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0X30)),
			VOC_REG_0X10, VOC_REG_0X10);
		INSREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0X30)),
			VOC_REG_0X40, VOC_REG_0X40);
		INSREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0XEC)),
			VOC_REG_0X02, VOC_REG_0X02);
		INSREG8((g_voc_iface_reg.paganini)|(OFFSET8(VOC_REG_0X61)),
			VOC_REG_0X80, VOC_REG_0X80);

		INSREG16((g_voc_iface_reg.vrec)|(OFFSET(VOC_REG_0X04)), BIT0, BIT0);

		//DC On
		voc_mic_cap_iface_clock_set(CLKGEN0,
			VOC_REG_0X1A, VOC_REG_0X08, VOC_VAL_0, VOC_VAL_3);
	}
}

int voc_mic_cap_iface_init(void)
{
	int ret = 0;

	if (g_voc_iface_setting.status > MIC_STATUS_NONE &&
		g_voc_iface_setting.status < MIC_STATUS_UNKNOWN)
		return 0;

	mic_cap_iface_log_level = LOGLEVEL_ERR;
	memset(&g_voc_iface_setting, 0, sizeof(struct voc_mic_iface_setting_t));
	g_voc_iface_setting.bck_mode = MIC_DEFAULT_BCK_MODE;
	g_voc_iface_setting.bck_drive = MIC_DEFAULT_BCK_DRIVE;
	g_voc_iface_setting.max_channels = MIC_CHANNEL_4;
	g_voc_iface_setting.channels = MIC_CHANNEL_4;
	g_voc_iface_setting.bit_width   = MIC_BIT_WIDTH_16;
	g_voc_iface_setting.sample_rate = MIC_SAMPLE_RATE_16K;
	mutex_init(&(g_voc_iface_setting.mic_lock));

	ret = voc_mic_cap_iface_dts_setting_init();
	if (ret < 0) {
		mic_cap_iface_err("[%s] dts setting init failed(%d)\n", __func__, ret);
		return ret;
	}

	ret = voc_mic_cap_iface_dts_pad_init();
	if (ret < 0) {
		mic_cap_iface_err("[%s] dts pad init failed(%d)\n", __func__, ret);
		return ret;
	}

	ret = voc_mic_cap_iface_dts_reg_init();
	if (ret < 0) {
		mic_cap_iface_err("[%s] dts reg init failed(%d)\n", __func__, ret);
		return ret;
	}

	ret = voc_mic_cap_iface_memory_init();
	if (ret < 0) {
		mic_cap_iface_err("[%s] memory init failed(%d)\n", __func__, ret);
		return ret;
	}

	voc_mic_cap_iface_ip_init();

	init_waitqueue_head(&(g_voc_iface_setting.kthread_waitq));
	g_voc_iface_setting.status = MIC_STATUS_INITED;

	voc_mic_cap_iface_input_switch(g_voc_iface_setting.mic_input_mux);
	voc_mic_cap_iface_dmic_switch(g_voc_iface_setting.onoff);
	return ret;
}
EXPORT_SYMBOL_GPL(voc_mic_cap_iface_init);

int voc_mic_cap_iface_open(uint32_t sample_rate, uint32_t bit_width, uint32_t channels)
{
	int ret = 0;
	unsigned int id;

	if (g_voc_iface_setting.handle > 0)
		return -EBUSY;

	if (g_voc_iface_setting.bck_mode == 0 ||
		g_voc_iface_setting.onoff == 0)
		return -EFAULT;

	mic_cap_iface_info("[%s]open (%u,%u,%u)\n", __func__,
		sample_rate, bit_width, channels);

	mutex_lock(&(g_voc_iface_setting.mic_lock));
	ret = voc_mic_cap_iface_set_sample_rate(sample_rate);
	if (ret < 0)
		goto voc_mic_cap_iface_open_failed;
	g_voc_iface_setting.sample_rate = sample_rate;

	ret = voc_mic_cap_iface_set_bit_width(bit_width);
	if (ret < 0)
		goto voc_mic_cap_iface_open_failed;
	g_voc_iface_setting.bit_width = bit_width;

	ret = voc_mic_cap_iface_set_channel(channels);
	if (ret < 0)
		goto voc_mic_cap_iface_open_failed;

	ret = voc_mic_cap_iface_irq_request();
	if (ret < 0) {
		mic_cap_iface_err("[%s]irq request failed(%d)\n", __func__, ret);
		goto voc_mic_cap_iface_open_failed;
	}

	g_voc_iface_setting.timer_notify = MIC_TIMER_WAIT_EVENT;
	g_voc_iface_setting.mic_mute_task = kthread_run(voc_mic_cap_iface_mute_trigger,
		NULL, "microphone capture interface mute trigger");
	if (IS_ERR(g_voc_iface_setting.mic_mute_task)) {
		ret = IS_ERR(g_voc_iface_setting.mic_mute_task);
		mic_cap_iface_err("[%s]ktrhead create failed(%d)\n", __func__, ret);
		goto voc_mic_cap_iface_open_failed;
	}

	g_voc_iface_setting.irq_request = 1;
	g_voc_iface_setting.channels = channels;
	g_voc_iface_setting.interrupt_callback = NULL;

	get_random_bytes(&id, sizeof(id));
	g_voc_iface_setting.handle = id % MIC_HANDLE_ID_RANGE;
	g_voc_iface_setting.status = MIC_STATUS_STOPPED;
	ret = g_voc_iface_setting.handle;
voc_mic_cap_iface_open_failed:
	mutex_unlock(&(g_voc_iface_setting.mic_lock));
	return ret;
}
EXPORT_SYMBOL_GPL(voc_mic_cap_iface_open);

int voc_mic_cap_iface_close(int handler)
{
	if (handler != g_voc_iface_setting.handle)
		return -EINVAL;

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

	if ((g_voc_iface_setting.status == MIC_STATUS_STARTED) ||
		(g_voc_iface_setting.status == MIC_STATUS_XRUN))
		voc_mic_cap_iface_stop_channel(handler);

	mutex_lock(&(g_voc_iface_setting.mic_lock));
	g_voc_iface_setting.mapping_mode = 0;
	g_voc_iface_setting.merged_channel = 0;
	dma_buf_addr = NULL;
	dma_wr_ptr = NULL;
	dma_buf_size = 0;

	if (g_voc_iface_setting.dma_vaddr) {
		iounmap((void *)g_voc_iface_setting.dma_vaddr);
		g_voc_iface_setting.dma_vaddr = NULL;
	}

	if (g_voc_iface_setting.irq_request == 1) {
		voc_mic_cap_iface_irq_release();
		g_voc_iface_setting.irq_request = 0;
	}

	g_voc_iface_setting.timer_notify = MIC_TIMER_STOP_EVENT;
	wake_up_interruptible(&(g_voc_iface_setting.kthread_waitq));

	g_voc_iface_setting.handle = 0;
	g_voc_iface_setting.status = MIC_STATUS_INITED;
	mutex_unlock(&(g_voc_iface_setting.mic_lock));
	return 0;
}
EXPORT_SYMBOL_GPL(voc_mic_cap_iface_close);

int voc_mic_cap_iface_set_buf_addr(int handler, int map_mode,
	void *phyAddr, void *virtAddr, uint32_t size)
{
	uint16_t miu_addr_lo, miu_addr_hi, miu_size;
	uint64_t u64addr = (uint64_t)phyAddr;
	uint16_t value;

	if ((handler != g_voc_iface_setting.handle) ||
		(g_voc_iface_setting.status == MIC_STATUS_STARTED) ||
		(g_voc_iface_setting.status == MIC_STATUS_XRUN))
		return -EBUSY;

	if (phyAddr == NULL || virtAddr == NULL || size == 0)
		return -EINVAL;

	//mapping with dma dram write base
	u64addr = u64addr + g_voc_iface_setting.dma_dram_write_base;

	mic_cap_iface_info("[%s] map mode:%d, phyAddr:%llx(%llx) (%llx), virtAddr:%lx, size:%u\n",
		__func__, map_mode, (uint64_t)phyAddr, u64addr,
		g_voc_iface_setting.dma_dram_write_base, (uintptr_t)virtAddr, size);

	mutex_lock(&(g_voc_iface_setting.mic_lock));
	do_div(u64addr, VOICE_MIU_WORD_BYTE_SIZE);
	miu_addr_lo = u64addr & VOICE_REG_WR_BASE_ADDR_LO_MSK;
	miu_addr_hi = (u64addr >> VOICE_REG_WR_BASE_ADDR_HI_OFFSET)
					& VOICE_REG_WR_BASE_ADDR_HI_MSK;
	miu_size = (size / VOICE_MIU_WORD_BYTE_SIZE) & VOICE_REG_WR_BUFF_SIZE_MSK;

	OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_1), miu_addr_lo);
	OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_2), miu_addr_hi);
	OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_3), miu_size);

	//reset dma if not reset dma
	value = INREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0));
	if ((value & VOICE_REG_SW_RST_DMA) == 0) {
		OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_4), 0x0000);

		value = INREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0));
		value |= VOICE_REG_SW_RST_DMA;
		OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0), value);
		mdelay(VOC_VAL_1);

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

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

	//set virtual buffer address & position
	dma_buf_addr = (uint8_t *)virtAddr;
	dma_wr_ptr = (uint8_t *)virtAddr;
	dma_buf_size = size;
	dma_mute_level = 0;

	//set mapping mode
	g_voc_iface_setting.mapping_mode = map_mode;
	mutex_unlock(&(g_voc_iface_setting.mic_lock));
	return 0;
}
EXPORT_SYMBOL_GPL(voc_mic_cap_iface_set_buf_addr);

int voc_mic_cap_iface_get_status(int handler)
{
	if (handler != g_voc_iface_setting.handle)
		return -EINVAL;
	return g_voc_iface_setting.status;
}
EXPORT_SYMBOL_GPL(voc_mic_cap_iface_get_status);

int voc_mic_cap_iface_set_attr(enum mic_iface_cmd cmd, void *param)
{
	int ret = 0;
	int32_t value;

	if (param == NULL)
		return -EINVAL;

	value = (*((int32_t *)param));

	mic_cap_iface_info("[%s] set attr cmd(%d) value(%d)\n", __func__, cmd, value);
	switch (cmd) {
	case MIC_INPUT_MODE:
		ret = voc_mic_cap_iface_input_mode((uint16_t)(value & VOICE_ATTR_MASK));
		break;
	case MIC_SWITCH:
		ret = voc_mic_cap_iface_dmic_switch((uint16_t)(value & VOICE_ATTR_MASK));
		break;
	case MIC_BCK_MODE:
		ret = voc_mic_cap_iface_set_bck_mode((uint16_t)(value & VOICE_ATTR_MASK));
		break;
	case MIC_BCK_DRIVE:
		ret = voc_mic_cap_iface_set_bck_drive((uint16_t)(value & VOICE_ATTR_MASK));
		break;
	case MIC_GAIN_STEP:
		ret = voc_mic_cap_iface_dmic_gain((uint16_t)(value & VOICE_ATTR_MASK));
		break;
	case MIC_MUTE_MODE:
		mutex_lock(&(g_voc_iface_setting.mic_lock));
		ret = voc_mic_cap_iface_dmic_mute((uint16_t)(value & VOICE_ATTR_MASK));
		mutex_unlock(&(g_voc_iface_setting.mic_lock));
		break;
	case MIC_HPF_CONFIG:
		ret = voc_mic_cap_iface_set_hpf(value);
		break;
	case MIC_HPF_MODE:
		ret = voc_mic_cap_iface_enable_hpf((uint16_t)(value & VOICE_ATTR_MASK));
		break;
	case MIC_SINEGEN_MODE:
		ret = voc_mic_cap_iface_enable_sinegen((uint16_t)(value & VOICE_ATTR_MASK));
		break;
	case MIC_INCGEN_MODE:
		ret = voc_mic_cap_iface_enable_incgen((uint16_t)(value & VOICE_ATTR_MASK));
		break;
	case MIC_MERGED_CHANNEL:
		ret = voc_mic_cap_iface_set_merged_channel((uint16_t)(value & VOICE_ATTR_MASK));
		break;
	case MIC_SAMPLE_RATE:
	case MIC_BIT_WIDTH:
	case MIC_CHANNELS:
		return -EFAULT;
	default:
		return -EINVAL;
	}

	return ret;
}
EXPORT_SYMBOL_GPL(voc_mic_cap_iface_set_attr);

int voc_mic_cap_iface_get_attr(enum mic_iface_cmd cmd, void *param)
{
	int32_t value = 0;

	if (param == NULL)
		return -EINVAL;

	mic_cap_iface_info("[%s] get attr cmd(%d)\n", __func__, cmd);
	switch (cmd) {
	case MIC_INPUT_MODE:
		value = g_voc_iface_setting.mic_input_mux;
		break;
	case MIC_SWITCH:
		value = g_voc_iface_setting.onoff;
		break;
	case MIC_BCK_MODE:
		value = g_voc_iface_setting.bck_mode;
		break;
	case MIC_BCK_DRIVE:
		value = g_voc_iface_setting.bck_drive;
		break;
	case MIC_SAMPLE_RATE:
		value = g_voc_iface_setting.sample_rate;
		break;
	case MIC_BIT_WIDTH:
		value = g_voc_iface_setting.bit_width;
		break;
	case MIC_CHANNELS:
		value = g_voc_iface_setting.channels;
		break;
	case MIC_GAIN_STEP:
		value = g_voc_iface_setting.gain;
		break;
	case MIC_MUTE_MODE:
		value = g_voc_iface_setting.mute;
		break;
	case MIC_HPF_CONFIG:
		value = g_voc_iface_setting.hpf_coeff;
		break;
	case MIC_HPF_MODE:
		value = g_voc_iface_setting.hpf_mode;
		break;
	case MIC_SINEGEN_MODE:
		value = g_voc_iface_setting.sinegen;
		break;
	case MIC_INCGEN_MODE:
		value = g_voc_iface_setting.incgen;
		break;
	case MIC_MERGED_CHANNEL:
		value = g_voc_iface_setting.merged_channel;
		break;
	default:
		return -EINVAL;
	}

	(*((int32_t *)param)) = value;
	return 0;
}
EXPORT_SYMBOL_GPL(voc_mic_cap_iface_get_attr);

int voc_mic_cap_iface_start_channel(int handler)
{
	uint16_t miu_addr_lo, miu_addr_hi, miu_size;
	uint64_t u64addr;
	uint16_t value;
	int ret = 0;

	if (handler != g_voc_iface_setting.handle)
		return -EINVAL;

	if (g_voc_iface_setting.status == MIC_STATUS_INITED ||
		g_voc_iface_setting.status == MIC_STATUS_XRUN)
		return -EFAULT;

	if (g_voc_iface_setting.status == MIC_STATUS_STARTED)
		return 0;

	mic_cap_iface_info("[%s] start(%d) (%d)\n", __func__,
		g_voc_iface_setting.mute,
		g_voc_iface_setting.mapping_mode);

	mutex_lock(&(g_voc_iface_setting.mic_lock));
	if (dma_buf_addr == NULL) {
		ret = voc_mic_cap_iface_alloc_dmem();
		if (ret < 0) {
			mutex_unlock(&(g_voc_iface_setting.mic_lock));
			mic_cap_iface_err("[%s] alloc memory failed(%d)\n", __func__, ret);
			return ret;
		}

		u64addr = g_voc_iface_setting.dma_paddr + g_voc_iface_setting.dma_dram_write_base;
		mic_cap_iface_debug("[%s] alloc memory (%llx[%llx],%llx,%u)\n", __func__,
			g_voc_iface_setting.dma_paddr, u64addr, g_voc_iface_setting.dma_paddr,
			dma_buf_size);

		do_div(u64addr, VOICE_MIU_WORD_BYTE_SIZE);
		miu_addr_lo = u64addr & VOICE_REG_WR_BASE_ADDR_LO_MSK;
		miu_addr_hi = (u64addr >> VOICE_REG_WR_BASE_ADDR_HI_OFFSET)
						& VOICE_REG_WR_BASE_ADDR_HI_MSK;
		miu_size = (dma_buf_size / VOICE_MIU_WORD_BYTE_SIZE) & VOICE_REG_WR_BUFF_SIZE_MSK;
		OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_1), miu_addr_lo);
		OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_2), miu_addr_hi);
		OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_3), miu_size);

		//reset dma
		OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_4), 0x0000);

		value = INREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0));
		value |= VOICE_REG_SW_RST_DMA;
		OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0), value);
		mdelay(VOC_VAL_1);
		value &= ~VOICE_REG_SW_RST_DMA;
		OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0), value);

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

	//DC on
	voc_mic_cap_iface_clock_set(CLKGEN0, VOC_REG_0X1A, VOC_REG_0X08, VOC_VAL_0, VOC_VAL_3);

	//Reset full/overrun interrupt status if needed
	value = INREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0));
	value |= VOICE_REG_WR_FULL_FLAG_CLR;
	OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0), value);
	value &= ~VOICE_REG_WR_FULL_FLAG_CLR;
	OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0), value);
	if (g_voc_iface_setting.overrun_int_enable) {
		value = INREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_8));
		value &= ~VOICE_REG_WR_OVERRUN_INT_EN;
		OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_8), value);
	}

	// VDMA & AEC Common enable
	INSREG8(g_voc_iface_reg.vrec	   + OFFSET8(VOC_REG_0X84), 0x01, 0x01);
	INSREG8(g_voc_iface_reg.vrec	   + OFFSET8(VOC_REG_0X60), BIT1, BIT1); // VDMA En
	INSREG8(g_voc_iface_reg.paganini + OFFSET8(VOC_REG_0X24), 0x01, 0x01); // VDMA_VLD_SEL
	INSREG8(g_voc_iface_reg.vrec	   + OFFSET8(VOC_REG_0X02), BIT3, 0x00); // VDMA Pause
	INSREG8(g_voc_iface_reg.vrec     + OFFSET8(VOC_REG_0X61), BIT7, 0x00); // VDMA_ENG_RESET

	ktime_get_ts64(&dma_mute_timestamp);
	g_voc_iface_setting.status = MIC_STATUS_STARTED;
	if (g_voc_iface_setting.mute) {
		g_voc_iface_setting.timer_notify = MIC_TIMER_TRIGGER_EVENT;
		wake_up_interruptible(&(g_voc_iface_setting.kthread_waitq));
	}
	mutex_unlock(&(g_voc_iface_setting.mic_lock));
	return ret;
}
EXPORT_SYMBOL_GPL(voc_mic_cap_iface_start_channel);

int voc_mic_cap_iface_stop_channel(int handler)
{
	if (handler != g_voc_iface_setting.handle)
		return -EINVAL;

	if (g_voc_iface_setting.status == MIC_STATUS_INITED)
		return -EFAULT;

	if (g_voc_iface_setting.status == MIC_STATUS_STOPPED)
		return 0;

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

	mutex_lock(&(g_voc_iface_setting.mic_lock));
	// VDMA & AEC Common enable
	INSREG8(g_voc_iface_reg.vrec     + OFFSET8(VOC_REG_0X84), 0x01, 0x00);
	INSREG8(g_voc_iface_reg.vrec     + OFFSET8(VOC_REG_0X60), BIT1, 0x00); // VDMA En
	INSREG8(g_voc_iface_reg.vrec     + OFFSET8(VOC_REG_0X02), BIT3, BIT3); // VDMA Pause
	INSREG8(g_voc_iface_reg.vrec     + OFFSET8(VOC_REG_0X61), BIT7, BIT7); // VDMA_ENG_RESET

	dma_mute_level = 0;
	dma_unmute_remain_level = 0;
	dma_wr_ptr = dma_buf_addr;
	g_voc_iface_setting.timer_notify = MIC_TIMER_WAIT_EVENT;
	g_voc_iface_setting.status = MIC_STATUS_STOPPED;
	mutex_unlock(&(g_voc_iface_setting.mic_lock));
	return 0;
}
EXPORT_SYMBOL_GPL(voc_mic_cap_iface_stop_channel);

uint32_t voc_mic_cap_iface_get_level_count(int handler)
{
	uint16_t value;
	uint32_t wr_levelbytesize;

	if (handler != g_voc_iface_setting.handle)
		return 0;

	mutex_lock(&(g_voc_iface_setting.mic_lock));
	if (g_voc_iface_setting.mute || dma_unmute_remain_level > 0) {
		wr_levelbytesize = dma_mute_level;
		mutex_unlock(&(g_voc_iface_setting.mic_lock));
		return wr_levelbytesize;
	}

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

	value = INREG16(g_voc_iface_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_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0));
	value &= ~VOICE_REG_WR_LEVEL_CNT_MASK;
	OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0), value);

	mutex_unlock(&(g_voc_iface_setting.mic_lock));
	return wr_levelbytesize;
}
EXPORT_SYMBOL_GPL(voc_mic_cap_iface_get_level_count);

uint32_t voc_mic_cap_iface_read_data(int handler, uint8_t *dst_buf, uint32_t buf_size)
{
	uint32_t read_size, remain_size, dma_data_size;
	int mapping_mode = g_voc_iface_setting.mapping_mode;

	dma_data_size = voc_mic_cap_iface_get_level_count(handler);
	if (dma_data_size == 0)
		return 0;

	mutex_lock(&(g_voc_iface_setting.mic_lock));
	if (((mapping_mode == 0) && (dst_buf == NULL)) ||
		((dma_wr_ptr == NULL) || (dma_buf_addr == NULL)) ||
		((dma_buf_size == 0) || (buf_size == 0))) {
		mutex_unlock(&(g_voc_iface_setting.mic_lock));
		return 0;
	}

	if (buf_size > dma_data_size)
		buf_size = dma_data_size;

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

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

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

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

	voc_mic_cap_iface_mcu_read_trigger(buf_size);
	mutex_unlock(&(g_voc_iface_setting.mic_lock));
	return buf_size;
}
EXPORT_SYMBOL_GPL(voc_mic_cap_iface_read_data);

int voc_mic_cap_iface_register_interrupt_callback(int handler, void *callback)
{
	if (handler != g_voc_iface_setting.handle)
		return -EINVAL;

	if (callback == NULL)
		return -EINVAL;

	if (g_voc_iface_setting.status == MIC_STATUS_INITED ||
		g_voc_iface_setting.status == MIC_STATUS_XRUN)
		return -EFAULT;

	mic_cap_iface_info("[%s] interrupt callback registered\n", __func__);

	g_voc_iface_setting.interrupt_callback = callback;
	return 0;
}
EXPORT_SYMBOL_GPL(voc_mic_cap_iface_register_interrupt_callback);

int voc_mic_cap_iface_enable_interrupt(int handler, struct mic_iface_intclr_t mode)
{
	uint16_t value;
	uint16_t timer_value;
	uint16_t overrun_int_enable;
	uint16_t overrun_default_thres;
	uint16_t overrun_thres;

	if (handler != g_voc_iface_setting.handle)
		return -EINVAL;

	mic_cap_iface_info("[%s] interrupt mode: %d orverrun:%d full:%d timer:%d\n", __func__,
			mode.mode, mode.overrun_flag, mode.full_flag, mode.timer_flag);

	timer_value = 0;
	overrun_int_enable = 0;
	overrun_thres = 0;
	value = INREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_8));
	value |= VOICE_REG_WR_LOCAL_FULL_INT_EN; //1:mask, 0:unmask
	value |= VOICE_REG_WR_OVERRUN_INT_EN;
	value |= VOICE_REG_WR_FULL_INT_EN;
	if (mode.mode) {
		if (mode.overrun_flag) {
			value &= ~VOICE_REG_WR_OVERRUN_INT_EN;
			overrun_default_thres = (g_voc_iface_setting.sample_rate *
				g_voc_iface_setting.bit_width/MIC_BITS_PER_BYTE *
				g_voc_iface_setting.channels * MIC_DMA_INT_MS) / MIC_MS_PER_SECOND;
			if (mode.overrun_threshold > 0)
				overrun_default_thres = mode.overrun_threshold;
			overrun_thres = (overrun_default_thres / VOICE_MIU_WORD_BYTE_SIZE) &
						VOICE_REG_WR_OVERRUN_TH_MSK;

			//lvl count include local buffer size
			overrun_thres += VOICE_DMA_INTERNAL_FIFO_SIZE;
			overrun_int_enable = 1;
		}

		if (mode.full_flag)
			value &= ~VOICE_REG_WR_FULL_INT_EN;

		if (mode.timer_flag)
			timer_value = BIT1;
	}

	if (overrun_thres > 0)
		OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_5), overrun_thres);

	OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_8), value);
	INSREG8(g_voc_iface_reg.paganini + OFFSET8(VOC_REG_0X8C), BIT1, timer_value);

	g_voc_iface_setting.overrun_int_enable = overrun_int_enable;

	return 0;
}
EXPORT_SYMBOL_GPL(voc_mic_cap_iface_enable_interrupt);

int voc_mic_cap_iface_clear_interrupt_status(int handler, enum mic_iface_intclr intclr)
{
	uint16_t value;

	if (handler != g_voc_iface_setting.handle)
		return -EINVAL;

	if (intclr == MIC_INT_FULL) {
		value = INREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0));

		value |= VOICE_REG_WR_FULL_FLAG_CLR;
		OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0), value);
		value &= ~VOICE_REG_WR_FULL_FLAG_CLR;
		OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_0), value);
	}

	if (intclr == MIC_INT_OVERRUN) {
		value = INREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_8));
		value |= VOICE_REG_WR_OVERRUN_INT_EN;
		OUTREG16(g_voc_iface_reg.vrec + OFFSET8(VOICE_DMA_W1_CFG_8), value);
	}

	if (intclr == MIC_INT_TIMER) {
		INSREG8(g_voc_iface_reg.paganini + OFFSET8(VOC_REG_0X8C), BIT3, BIT3);
		INSREG8(g_voc_iface_reg.paganini + OFFSET8(VOC_REG_0X8C), BIT3, 0);
	}

	return 0;
}
EXPORT_SYMBOL_GPL(voc_mic_cap_iface_clear_interrupt_status);

int voc_mic_cap_iface_set_log_level(int level)
{
	mic_cap_iface_log_level = level;
	return 0;
}
EXPORT_SYMBOL_GPL(voc_mic_cap_iface_set_log_level);

MODULE_DESCRIPTION("Mediatek microphone capture interface");
MODULE_LICENSE("GPL v2");
