xref: /openbmc/linux/sound/soc/fsl/fsl_micfil.c (revision a38a4090e2c400c6c49c584cda6f28c73c08f5f1)
147a70e6fSCosmin Samoila // SPDX-License-Identifier: GPL-2.0
247a70e6fSCosmin Samoila // Copyright 2018 NXP
347a70e6fSCosmin Samoila 
417f2142bSSascha Hauer #include <linux/bitfield.h>
547a70e6fSCosmin Samoila #include <linux/clk.h>
647a70e6fSCosmin Samoila #include <linux/device.h>
747a70e6fSCosmin Samoila #include <linux/interrupt.h>
847a70e6fSCosmin Samoila #include <linux/kobject.h>
947a70e6fSCosmin Samoila #include <linux/kernel.h>
1047a70e6fSCosmin Samoila #include <linux/module.h>
1147a70e6fSCosmin Samoila #include <linux/of.h>
1247a70e6fSCosmin Samoila #include <linux/of_address.h>
1347a70e6fSCosmin Samoila #include <linux/of_irq.h>
1447a70e6fSCosmin Samoila #include <linux/of_platform.h>
1547a70e6fSCosmin Samoila #include <linux/pm_runtime.h>
1647a70e6fSCosmin Samoila #include <linux/regmap.h>
1747a70e6fSCosmin Samoila #include <linux/sysfs.h>
1847a70e6fSCosmin Samoila #include <linux/types.h>
192495ba26SSascha Hauer #include <linux/dma/imx-dma.h>
2047a70e6fSCosmin Samoila #include <sound/dmaengine_pcm.h>
2147a70e6fSCosmin Samoila #include <sound/pcm.h>
2247a70e6fSCosmin Samoila #include <sound/soc.h>
2347a70e6fSCosmin Samoila #include <sound/tlv.h>
2447a70e6fSCosmin Samoila #include <sound/core.h>
2547a70e6fSCosmin Samoila 
2647a70e6fSCosmin Samoila #include "fsl_micfil.h"
2793f54100SShengjiu Wang #include "fsl_utils.h"
2847a70e6fSCosmin Samoila 
29fb855b8dSSascha Hauer #define MICFIL_OSR_DEFAULT	16
30fb855b8dSSascha Hauer 
31bea1d61dSSascha Hauer enum quality {
32bea1d61dSSascha Hauer 	QUALITY_HIGH,
33bea1d61dSSascha Hauer 	QUALITY_MEDIUM,
34bea1d61dSSascha Hauer 	QUALITY_LOW,
35bea1d61dSSascha Hauer 	QUALITY_VLOW0,
36bea1d61dSSascha Hauer 	QUALITY_VLOW1,
37bea1d61dSSascha Hauer 	QUALITY_VLOW2,
38bea1d61dSSascha Hauer };
39bea1d61dSSascha Hauer 
4047a70e6fSCosmin Samoila struct fsl_micfil {
4147a70e6fSCosmin Samoila 	struct platform_device *pdev;
4247a70e6fSCosmin Samoila 	struct regmap *regmap;
4347a70e6fSCosmin Samoila 	const struct fsl_micfil_soc_data *soc;
44b5cf28f7SShengjiu Wang 	struct clk *busclk;
4547a70e6fSCosmin Samoila 	struct clk *mclk;
4693f54100SShengjiu Wang 	struct clk *pll8k_clk;
4793f54100SShengjiu Wang 	struct clk *pll11k_clk;
4847a70e6fSCosmin Samoila 	struct snd_dmaengine_dai_dma_data dma_params_rx;
492495ba26SSascha Hauer 	struct sdma_peripheral_config sdmacfg;
5029dbfeecSShengjiu Wang 	struct snd_soc_card *card;
5147a70e6fSCosmin Samoila 	unsigned int dataline;
5247a70e6fSCosmin Samoila 	char name[32];
5347a70e6fSCosmin Samoila 	int irq[MICFIL_IRQ_LINES];
54bea1d61dSSascha Hauer 	enum quality quality;
553b13b143SShengjiu Wang 	int dc_remover;
5629dbfeecSShengjiu Wang 	int vad_init_mode;
5729dbfeecSShengjiu Wang 	int vad_enabled;
5829dbfeecSShengjiu Wang 	int vad_detected;
5936736505SChancel Liu 	struct fsl_micfil_verid verid;
6036736505SChancel Liu 	struct fsl_micfil_param param;
6147a70e6fSCosmin Samoila };
6247a70e6fSCosmin Samoila 
6347a70e6fSCosmin Samoila struct fsl_micfil_soc_data {
6447a70e6fSCosmin Samoila 	unsigned int fifos;
6547a70e6fSCosmin Samoila 	unsigned int fifo_depth;
6647a70e6fSCosmin Samoila 	unsigned int dataline;
6747a70e6fSCosmin Samoila 	bool imx;
6877a7a6e9SChancel Liu 	bool use_edma;
6936736505SChancel Liu 	bool use_verid;
70cb05dac1SShengjiu Wang 	u64  formats;
7147a70e6fSCosmin Samoila };
7247a70e6fSCosmin Samoila 
7347a70e6fSCosmin Samoila static struct fsl_micfil_soc_data fsl_micfil_imx8mm = {
7447a70e6fSCosmin Samoila 	.imx = true,
7547a70e6fSCosmin Samoila 	.fifos = 8,
7647a70e6fSCosmin Samoila 	.fifo_depth = 8,
7747a70e6fSCosmin Samoila 	.dataline =  0xf,
78cb05dac1SShengjiu Wang 	.formats = SNDRV_PCM_FMTBIT_S16_LE,
79cb05dac1SShengjiu Wang };
80cb05dac1SShengjiu Wang 
81cb05dac1SShengjiu Wang static struct fsl_micfil_soc_data fsl_micfil_imx8mp = {
82cb05dac1SShengjiu Wang 	.imx = true,
83cb05dac1SShengjiu Wang 	.fifos = 8,
84cb05dac1SShengjiu Wang 	.fifo_depth = 32,
85cb05dac1SShengjiu Wang 	.dataline =  0xf,
86cb05dac1SShengjiu Wang 	.formats = SNDRV_PCM_FMTBIT_S32_LE,
8747a70e6fSCosmin Samoila };
8847a70e6fSCosmin Samoila 
89a10a5254SChancel Liu static struct fsl_micfil_soc_data fsl_micfil_imx93 = {
90a10a5254SChancel Liu 	.imx = true,
91a10a5254SChancel Liu 	.fifos = 8,
92a10a5254SChancel Liu 	.fifo_depth = 32,
93a10a5254SChancel Liu 	.dataline =  0xf,
94a10a5254SChancel Liu 	.formats = SNDRV_PCM_FMTBIT_S32_LE,
9577a7a6e9SChancel Liu 	.use_edma = true,
9636736505SChancel Liu 	.use_verid = true,
97a10a5254SChancel Liu };
98a10a5254SChancel Liu 
9947a70e6fSCosmin Samoila static const struct of_device_id fsl_micfil_dt_ids[] = {
10047a70e6fSCosmin Samoila 	{ .compatible = "fsl,imx8mm-micfil", .data = &fsl_micfil_imx8mm },
101cb05dac1SShengjiu Wang 	{ .compatible = "fsl,imx8mp-micfil", .data = &fsl_micfil_imx8mp },
102a10a5254SChancel Liu 	{ .compatible = "fsl,imx93-micfil", .data = &fsl_micfil_imx93 },
10347a70e6fSCosmin Samoila 	{}
10447a70e6fSCosmin Samoila };
10547a70e6fSCosmin Samoila MODULE_DEVICE_TABLE(of, fsl_micfil_dt_ids);
10647a70e6fSCosmin Samoila 
10747a70e6fSCosmin Samoila static const char * const micfil_quality_select_texts[] = {
108bea1d61dSSascha Hauer 	[QUALITY_HIGH] = "High",
109bea1d61dSSascha Hauer 	[QUALITY_MEDIUM] = "Medium",
110bea1d61dSSascha Hauer 	[QUALITY_LOW] = "Low",
111bea1d61dSSascha Hauer 	[QUALITY_VLOW0] = "VLow0",
112bea1d61dSSascha Hauer 	[QUALITY_VLOW1] = "Vlow1",
113bea1d61dSSascha Hauer 	[QUALITY_VLOW2] = "Vlow2",
11447a70e6fSCosmin Samoila };
11547a70e6fSCosmin Samoila 
11647a70e6fSCosmin Samoila static const struct soc_enum fsl_micfil_quality_enum =
117bea1d61dSSascha Hauer 	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_quality_select_texts),
11847a70e6fSCosmin Samoila 			    micfil_quality_select_texts);
11947a70e6fSCosmin Samoila 
12047a70e6fSCosmin Samoila static DECLARE_TLV_DB_SCALE(gain_tlv, 0, 100, 0);
12147a70e6fSCosmin Samoila 
122bea1d61dSSascha Hauer static int micfil_set_quality(struct fsl_micfil *micfil)
123bea1d61dSSascha Hauer {
124bea1d61dSSascha Hauer 	u32 qsel;
125bea1d61dSSascha Hauer 
126bea1d61dSSascha Hauer 	switch (micfil->quality) {
127bea1d61dSSascha Hauer 	case QUALITY_HIGH:
128bea1d61dSSascha Hauer 		qsel = MICFIL_QSEL_HIGH_QUALITY;
129bea1d61dSSascha Hauer 		break;
130bea1d61dSSascha Hauer 	case QUALITY_MEDIUM:
131bea1d61dSSascha Hauer 		qsel = MICFIL_QSEL_MEDIUM_QUALITY;
132bea1d61dSSascha Hauer 		break;
133bea1d61dSSascha Hauer 	case QUALITY_LOW:
134bea1d61dSSascha Hauer 		qsel = MICFIL_QSEL_LOW_QUALITY;
135bea1d61dSSascha Hauer 		break;
136bea1d61dSSascha Hauer 	case QUALITY_VLOW0:
137bea1d61dSSascha Hauer 		qsel = MICFIL_QSEL_VLOW0_QUALITY;
138bea1d61dSSascha Hauer 		break;
139bea1d61dSSascha Hauer 	case QUALITY_VLOW1:
140bea1d61dSSascha Hauer 		qsel = MICFIL_QSEL_VLOW1_QUALITY;
141bea1d61dSSascha Hauer 		break;
142bea1d61dSSascha Hauer 	case QUALITY_VLOW2:
143bea1d61dSSascha Hauer 		qsel = MICFIL_QSEL_VLOW2_QUALITY;
144bea1d61dSSascha Hauer 		break;
145bea1d61dSSascha Hauer 	}
146bea1d61dSSascha Hauer 
147bea1d61dSSascha Hauer 	return regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2,
148bea1d61dSSascha Hauer 				  MICFIL_CTRL2_QSEL,
149bea1d61dSSascha Hauer 				  FIELD_PREP(MICFIL_CTRL2_QSEL, qsel));
150bea1d61dSSascha Hauer }
151bea1d61dSSascha Hauer 
152bea1d61dSSascha Hauer static int micfil_quality_get(struct snd_kcontrol *kcontrol,
153bea1d61dSSascha Hauer 			      struct snd_ctl_elem_value *ucontrol)
154bea1d61dSSascha Hauer {
155bea1d61dSSascha Hauer 	struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
156bea1d61dSSascha Hauer 	struct fsl_micfil *micfil = snd_soc_component_get_drvdata(cmpnt);
157bea1d61dSSascha Hauer 
158bea1d61dSSascha Hauer 	ucontrol->value.integer.value[0] = micfil->quality;
159bea1d61dSSascha Hauer 
160bea1d61dSSascha Hauer 	return 0;
161bea1d61dSSascha Hauer }
162bea1d61dSSascha Hauer 
163bea1d61dSSascha Hauer static int micfil_quality_set(struct snd_kcontrol *kcontrol,
164bea1d61dSSascha Hauer 			      struct snd_ctl_elem_value *ucontrol)
165bea1d61dSSascha Hauer {
166bea1d61dSSascha Hauer 	struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
167bea1d61dSSascha Hauer 	struct fsl_micfil *micfil = snd_soc_component_get_drvdata(cmpnt);
168bea1d61dSSascha Hauer 
169bea1d61dSSascha Hauer 	micfil->quality = ucontrol->value.integer.value[0];
170bea1d61dSSascha Hauer 
171bea1d61dSSascha Hauer 	return micfil_set_quality(micfil);
172bea1d61dSSascha Hauer }
173bea1d61dSSascha Hauer 
17429dbfeecSShengjiu Wang static const char * const micfil_hwvad_enable[] = {
17529dbfeecSShengjiu Wang 	"Disable (Record only)",
17629dbfeecSShengjiu Wang 	"Enable (Record with Vad)",
17729dbfeecSShengjiu Wang };
17829dbfeecSShengjiu Wang 
17929dbfeecSShengjiu Wang static const char * const micfil_hwvad_init_mode[] = {
18029dbfeecSShengjiu Wang 	"Envelope mode", "Energy mode",
18129dbfeecSShengjiu Wang };
18229dbfeecSShengjiu Wang 
18329dbfeecSShengjiu Wang static const char * const micfil_hwvad_hpf_texts[] = {
18429dbfeecSShengjiu Wang 	"Filter bypass",
18529dbfeecSShengjiu Wang 	"Cut-off @1750Hz",
18629dbfeecSShengjiu Wang 	"Cut-off @215Hz",
18729dbfeecSShengjiu Wang 	"Cut-off @102Hz",
18829dbfeecSShengjiu Wang };
18929dbfeecSShengjiu Wang 
19029dbfeecSShengjiu Wang /*
19129dbfeecSShengjiu Wang  * DC Remover Control
19229dbfeecSShengjiu Wang  * Filter Bypassed	1 1
19329dbfeecSShengjiu Wang  * Cut-off @21Hz	0 0
19429dbfeecSShengjiu Wang  * Cut-off @83Hz	0 1
19529dbfeecSShengjiu Wang  * Cut-off @152HZ	1 0
19629dbfeecSShengjiu Wang  */
19729dbfeecSShengjiu Wang static const char * const micfil_dc_remover_texts[] = {
19829dbfeecSShengjiu Wang 	"Cut-off @21Hz", "Cut-off @83Hz",
19929dbfeecSShengjiu Wang 	"Cut-off @152Hz", "Bypass",
20029dbfeecSShengjiu Wang };
20129dbfeecSShengjiu Wang 
20229dbfeecSShengjiu Wang static const struct soc_enum hwvad_enable_enum =
20329dbfeecSShengjiu Wang 	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_hwvad_enable),
20429dbfeecSShengjiu Wang 			    micfil_hwvad_enable);
20529dbfeecSShengjiu Wang static const struct soc_enum hwvad_init_mode_enum =
20629dbfeecSShengjiu Wang 	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_hwvad_init_mode),
20729dbfeecSShengjiu Wang 			    micfil_hwvad_init_mode);
20829dbfeecSShengjiu Wang static const struct soc_enum hwvad_hpf_enum =
20929dbfeecSShengjiu Wang 	SOC_ENUM_SINGLE(REG_MICFIL_VAD0_CTRL2, 0,
21029dbfeecSShengjiu Wang 			ARRAY_SIZE(micfil_hwvad_hpf_texts),
21129dbfeecSShengjiu Wang 			micfil_hwvad_hpf_texts);
21229dbfeecSShengjiu Wang static const struct soc_enum fsl_micfil_dc_remover_enum =
21329dbfeecSShengjiu Wang 	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_dc_remover_texts),
21429dbfeecSShengjiu Wang 			    micfil_dc_remover_texts);
21529dbfeecSShengjiu Wang 
21629dbfeecSShengjiu Wang static int micfil_put_dc_remover_state(struct snd_kcontrol *kcontrol,
21729dbfeecSShengjiu Wang 				       struct snd_ctl_elem_value *ucontrol)
21829dbfeecSShengjiu Wang {
21929dbfeecSShengjiu Wang 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
22029dbfeecSShengjiu Wang 	struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
22129dbfeecSShengjiu Wang 	struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp);
22229dbfeecSShengjiu Wang 	unsigned int *item = ucontrol->value.enumerated.item;
22329dbfeecSShengjiu Wang 	int val = snd_soc_enum_item_to_val(e, item[0]);
22429dbfeecSShengjiu Wang 	int i = 0, ret = 0;
22529dbfeecSShengjiu Wang 	u32 reg_val = 0;
22629dbfeecSShengjiu Wang 
22729dbfeecSShengjiu Wang 	if (val < 0 || val > 3)
22829dbfeecSShengjiu Wang 		return -EINVAL;
22929dbfeecSShengjiu Wang 
23029dbfeecSShengjiu Wang 	micfil->dc_remover = val;
23129dbfeecSShengjiu Wang 
23229dbfeecSShengjiu Wang 	/* Calculate total value for all channels */
23329dbfeecSShengjiu Wang 	for (i = 0; i < MICFIL_OUTPUT_CHANNELS; i++)
23429dbfeecSShengjiu Wang 		reg_val |= val << MICFIL_DC_CHX_SHIFT(i);
23529dbfeecSShengjiu Wang 
23629dbfeecSShengjiu Wang 	/* Update DC Remover mode for all channels */
23729dbfeecSShengjiu Wang 	ret = snd_soc_component_update_bits(comp, REG_MICFIL_DC_CTRL,
23829dbfeecSShengjiu Wang 					    MICFIL_DC_CTRL_CONFIG, reg_val);
23929dbfeecSShengjiu Wang 	if (ret < 0)
24029dbfeecSShengjiu Wang 		return ret;
24129dbfeecSShengjiu Wang 
24229dbfeecSShengjiu Wang 	return 0;
24329dbfeecSShengjiu Wang }
24429dbfeecSShengjiu Wang 
24529dbfeecSShengjiu Wang static int micfil_get_dc_remover_state(struct snd_kcontrol *kcontrol,
24629dbfeecSShengjiu Wang 				       struct snd_ctl_elem_value *ucontrol)
24729dbfeecSShengjiu Wang {
24829dbfeecSShengjiu Wang 	struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
24929dbfeecSShengjiu Wang 	struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp);
25029dbfeecSShengjiu Wang 
25129dbfeecSShengjiu Wang 	ucontrol->value.enumerated.item[0] = micfil->dc_remover;
25229dbfeecSShengjiu Wang 
25329dbfeecSShengjiu Wang 	return 0;
25429dbfeecSShengjiu Wang }
25529dbfeecSShengjiu Wang 
25629dbfeecSShengjiu Wang static int hwvad_put_enable(struct snd_kcontrol *kcontrol,
25729dbfeecSShengjiu Wang 			    struct snd_ctl_elem_value *ucontrol)
25829dbfeecSShengjiu Wang {
25929dbfeecSShengjiu Wang 	struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
26029dbfeecSShengjiu Wang 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
26129dbfeecSShengjiu Wang 	unsigned int *item = ucontrol->value.enumerated.item;
26229dbfeecSShengjiu Wang 	struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp);
26329dbfeecSShengjiu Wang 	int val = snd_soc_enum_item_to_val(e, item[0]);
26429dbfeecSShengjiu Wang 
26529dbfeecSShengjiu Wang 	micfil->vad_enabled = val;
26629dbfeecSShengjiu Wang 
26729dbfeecSShengjiu Wang 	return 0;
26829dbfeecSShengjiu Wang }
26929dbfeecSShengjiu Wang 
27029dbfeecSShengjiu Wang static int hwvad_get_enable(struct snd_kcontrol *kcontrol,
27129dbfeecSShengjiu Wang 			    struct snd_ctl_elem_value *ucontrol)
27229dbfeecSShengjiu Wang {
27329dbfeecSShengjiu Wang 	struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
27429dbfeecSShengjiu Wang 	struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp);
27529dbfeecSShengjiu Wang 
27629dbfeecSShengjiu Wang 	ucontrol->value.enumerated.item[0] = micfil->vad_enabled;
27729dbfeecSShengjiu Wang 
27829dbfeecSShengjiu Wang 	return 0;
27929dbfeecSShengjiu Wang }
28029dbfeecSShengjiu Wang 
28129dbfeecSShengjiu Wang static int hwvad_put_init_mode(struct snd_kcontrol *kcontrol,
28229dbfeecSShengjiu Wang 			       struct snd_ctl_elem_value *ucontrol)
28329dbfeecSShengjiu Wang {
28429dbfeecSShengjiu Wang 	struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
28529dbfeecSShengjiu Wang 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
28629dbfeecSShengjiu Wang 	unsigned int *item = ucontrol->value.enumerated.item;
28729dbfeecSShengjiu Wang 	struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp);
28829dbfeecSShengjiu Wang 	int val = snd_soc_enum_item_to_val(e, item[0]);
28929dbfeecSShengjiu Wang 
29029dbfeecSShengjiu Wang 	/* 0 - Envelope-based Mode
29129dbfeecSShengjiu Wang 	 * 1 - Energy-based Mode
29229dbfeecSShengjiu Wang 	 */
29329dbfeecSShengjiu Wang 	micfil->vad_init_mode = val;
29429dbfeecSShengjiu Wang 
29529dbfeecSShengjiu Wang 	return 0;
29629dbfeecSShengjiu Wang }
29729dbfeecSShengjiu Wang 
29829dbfeecSShengjiu Wang static int hwvad_get_init_mode(struct snd_kcontrol *kcontrol,
29929dbfeecSShengjiu Wang 			       struct snd_ctl_elem_value *ucontrol)
30029dbfeecSShengjiu Wang {
30129dbfeecSShengjiu Wang 	struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
30229dbfeecSShengjiu Wang 	struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp);
30329dbfeecSShengjiu Wang 
30429dbfeecSShengjiu Wang 	ucontrol->value.enumerated.item[0] = micfil->vad_init_mode;
30529dbfeecSShengjiu Wang 
30629dbfeecSShengjiu Wang 	return 0;
30729dbfeecSShengjiu Wang }
30829dbfeecSShengjiu Wang 
30929dbfeecSShengjiu Wang static int hwvad_detected(struct snd_kcontrol *kcontrol,
31029dbfeecSShengjiu Wang 			  struct snd_ctl_elem_value *ucontrol)
31129dbfeecSShengjiu Wang {
31229dbfeecSShengjiu Wang 	struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
31329dbfeecSShengjiu Wang 	struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp);
31429dbfeecSShengjiu Wang 
31529dbfeecSShengjiu Wang 	ucontrol->value.enumerated.item[0] = micfil->vad_detected;
31629dbfeecSShengjiu Wang 
31729dbfeecSShengjiu Wang 	return 0;
31829dbfeecSShengjiu Wang }
31929dbfeecSShengjiu Wang 
32047a70e6fSCosmin Samoila static const struct snd_kcontrol_new fsl_micfil_snd_controls[] = {
32147a70e6fSCosmin Samoila 	SOC_SINGLE_SX_TLV("CH0 Volume", REG_MICFIL_OUT_CTRL,
322cdfa92ebSChancel Liu 			  MICFIL_OUTGAIN_CHX_SHIFT(0), 0x8, 0xF, gain_tlv),
32347a70e6fSCosmin Samoila 	SOC_SINGLE_SX_TLV("CH1 Volume", REG_MICFIL_OUT_CTRL,
324cdfa92ebSChancel Liu 			  MICFIL_OUTGAIN_CHX_SHIFT(1), 0x8, 0xF, gain_tlv),
32547a70e6fSCosmin Samoila 	SOC_SINGLE_SX_TLV("CH2 Volume", REG_MICFIL_OUT_CTRL,
326cdfa92ebSChancel Liu 			  MICFIL_OUTGAIN_CHX_SHIFT(2), 0x8, 0xF, gain_tlv),
32747a70e6fSCosmin Samoila 	SOC_SINGLE_SX_TLV("CH3 Volume", REG_MICFIL_OUT_CTRL,
328cdfa92ebSChancel Liu 			  MICFIL_OUTGAIN_CHX_SHIFT(3), 0x8, 0xF, gain_tlv),
32947a70e6fSCosmin Samoila 	SOC_SINGLE_SX_TLV("CH4 Volume", REG_MICFIL_OUT_CTRL,
330cdfa92ebSChancel Liu 			  MICFIL_OUTGAIN_CHX_SHIFT(4), 0x8, 0xF, gain_tlv),
33147a70e6fSCosmin Samoila 	SOC_SINGLE_SX_TLV("CH5 Volume", REG_MICFIL_OUT_CTRL,
332cdfa92ebSChancel Liu 			  MICFIL_OUTGAIN_CHX_SHIFT(5), 0x8, 0xF, gain_tlv),
33347a70e6fSCosmin Samoila 	SOC_SINGLE_SX_TLV("CH6 Volume", REG_MICFIL_OUT_CTRL,
334cdfa92ebSChancel Liu 			  MICFIL_OUTGAIN_CHX_SHIFT(6), 0x8, 0xF, gain_tlv),
33547a70e6fSCosmin Samoila 	SOC_SINGLE_SX_TLV("CH7 Volume", REG_MICFIL_OUT_CTRL,
336cdfa92ebSChancel Liu 			  MICFIL_OUTGAIN_CHX_SHIFT(7), 0x8, 0xF, gain_tlv),
33747a70e6fSCosmin Samoila 	SOC_ENUM_EXT("MICFIL Quality Select",
33847a70e6fSCosmin Samoila 		     fsl_micfil_quality_enum,
339bea1d61dSSascha Hauer 		     micfil_quality_get, micfil_quality_set),
34029dbfeecSShengjiu Wang 	SOC_ENUM_EXT("HWVAD Enablement Switch", hwvad_enable_enum,
34129dbfeecSShengjiu Wang 		     hwvad_get_enable, hwvad_put_enable),
34229dbfeecSShengjiu Wang 	SOC_ENUM_EXT("HWVAD Initialization Mode", hwvad_init_mode_enum,
34329dbfeecSShengjiu Wang 		     hwvad_get_init_mode, hwvad_put_init_mode),
34429dbfeecSShengjiu Wang 	SOC_ENUM("HWVAD High-Pass Filter", hwvad_hpf_enum),
34529dbfeecSShengjiu Wang 	SOC_SINGLE("HWVAD ZCD Switch", REG_MICFIL_VAD0_ZCD, 0, 1, 0),
34629dbfeecSShengjiu Wang 	SOC_SINGLE("HWVAD ZCD Auto Threshold Switch",
34729dbfeecSShengjiu Wang 		   REG_MICFIL_VAD0_ZCD, 2, 1, 0),
34829dbfeecSShengjiu Wang 	SOC_ENUM_EXT("MICFIL DC Remover Control", fsl_micfil_dc_remover_enum,
34929dbfeecSShengjiu Wang 		     micfil_get_dc_remover_state, micfil_put_dc_remover_state),
35029dbfeecSShengjiu Wang 	SOC_SINGLE("HWVAD Input Gain", REG_MICFIL_VAD0_CTRL2, 8, 15, 0),
35129dbfeecSShengjiu Wang 	SOC_SINGLE("HWVAD Sound Gain", REG_MICFIL_VAD0_SCONFIG, 0, 15, 0),
35229dbfeecSShengjiu Wang 	SOC_SINGLE("HWVAD Noise Gain", REG_MICFIL_VAD0_NCONFIG, 0, 15, 0),
35329dbfeecSShengjiu Wang 	SOC_SINGLE_RANGE("HWVAD Detector Frame Time", REG_MICFIL_VAD0_CTRL2, 16, 0, 63, 0),
35429dbfeecSShengjiu Wang 	SOC_SINGLE("HWVAD Detector Initialization Time", REG_MICFIL_VAD0_CTRL1, 8, 31, 0),
35529dbfeecSShengjiu Wang 	SOC_SINGLE("HWVAD Noise Filter Adjustment", REG_MICFIL_VAD0_NCONFIG, 8, 31, 0),
35629dbfeecSShengjiu Wang 	SOC_SINGLE("HWVAD ZCD Threshold", REG_MICFIL_VAD0_ZCD, 16, 1023, 0),
35729dbfeecSShengjiu Wang 	SOC_SINGLE("HWVAD ZCD Adjustment", REG_MICFIL_VAD0_ZCD, 8, 15, 0),
35829dbfeecSShengjiu Wang 	SOC_SINGLE("HWVAD ZCD And Behavior Switch",
35929dbfeecSShengjiu Wang 		   REG_MICFIL_VAD0_ZCD, 4, 1, 0),
36029dbfeecSShengjiu Wang 	SOC_SINGLE_BOOL_EXT("VAD Detected", 0, hwvad_detected, NULL),
36147a70e6fSCosmin Samoila };
36247a70e6fSCosmin Samoila 
36336736505SChancel Liu static int fsl_micfil_use_verid(struct device *dev)
36436736505SChancel Liu {
36536736505SChancel Liu 	struct fsl_micfil *micfil = dev_get_drvdata(dev);
36636736505SChancel Liu 	unsigned int val;
36736736505SChancel Liu 	int ret;
36836736505SChancel Liu 
36936736505SChancel Liu 	if (!micfil->soc->use_verid)
37036736505SChancel Liu 		return 0;
37136736505SChancel Liu 
37236736505SChancel Liu 	ret = regmap_read(micfil->regmap, REG_MICFIL_VERID, &val);
37336736505SChancel Liu 	if (ret < 0)
37436736505SChancel Liu 		return ret;
37536736505SChancel Liu 
37636736505SChancel Liu 	dev_dbg(dev, "VERID: 0x%016X\n", val);
37736736505SChancel Liu 
37836736505SChancel Liu 	micfil->verid.version = val &
37936736505SChancel Liu 		(MICFIL_VERID_MAJOR_MASK | MICFIL_VERID_MINOR_MASK);
38036736505SChancel Liu 	micfil->verid.version >>= MICFIL_VERID_MINOR_SHIFT;
38136736505SChancel Liu 	micfil->verid.feature = val & MICFIL_VERID_FEATURE_MASK;
38236736505SChancel Liu 
38336736505SChancel Liu 	ret = regmap_read(micfil->regmap, REG_MICFIL_PARAM, &val);
38436736505SChancel Liu 	if (ret < 0)
38536736505SChancel Liu 		return ret;
38636736505SChancel Liu 
38736736505SChancel Liu 	dev_dbg(dev, "PARAM: 0x%016X\n", val);
38836736505SChancel Liu 
38936736505SChancel Liu 	micfil->param.hwvad_num = (val & MICFIL_PARAM_NUM_HWVAD_MASK) >>
39036736505SChancel Liu 		MICFIL_PARAM_NUM_HWVAD_SHIFT;
39136736505SChancel Liu 	micfil->param.hwvad_zcd = val & MICFIL_PARAM_HWVAD_ZCD;
39236736505SChancel Liu 	micfil->param.hwvad_energy_mode = val & MICFIL_PARAM_HWVAD_ENERGY_MODE;
39336736505SChancel Liu 	micfil->param.hwvad = val & MICFIL_PARAM_HWVAD;
39436736505SChancel Liu 	micfil->param.dc_out_bypass = val & MICFIL_PARAM_DC_OUT_BYPASS;
39536736505SChancel Liu 	micfil->param.dc_in_bypass = val & MICFIL_PARAM_DC_IN_BYPASS;
39636736505SChancel Liu 	micfil->param.low_power = val & MICFIL_PARAM_LOW_POWER;
39736736505SChancel Liu 	micfil->param.fil_out_width = val & MICFIL_PARAM_FIL_OUT_WIDTH;
39836736505SChancel Liu 	micfil->param.fifo_ptrwid = (val & MICFIL_PARAM_FIFO_PTRWID_MASK) >>
39936736505SChancel Liu 		MICFIL_PARAM_FIFO_PTRWID_SHIFT;
40036736505SChancel Liu 	micfil->param.npair = (val & MICFIL_PARAM_NPAIR_MASK) >>
40136736505SChancel Liu 		MICFIL_PARAM_NPAIR_SHIFT;
40236736505SChancel Liu 
40336736505SChancel Liu 	return 0;
40436736505SChancel Liu }
40536736505SChancel Liu 
40647a70e6fSCosmin Samoila /* The SRES is a self-negated bit which provides the CPU with the
40747a70e6fSCosmin Samoila  * capability to initialize the PDM Interface module through the
40847a70e6fSCosmin Samoila  * slave-bus interface. This bit always reads as zero, and this
40947a70e6fSCosmin Samoila  * bit is only effective when MDIS is cleared
41047a70e6fSCosmin Samoila  */
41147a70e6fSCosmin Samoila static int fsl_micfil_reset(struct device *dev)
41247a70e6fSCosmin Samoila {
41347a70e6fSCosmin Samoila 	struct fsl_micfil *micfil = dev_get_drvdata(dev);
41447a70e6fSCosmin Samoila 	int ret;
41547a70e6fSCosmin Samoila 
416d46c2127SSascha Hauer 	ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1,
417d46c2127SSascha Hauer 				MICFIL_CTRL1_MDIS);
4182c602c7eSSascha Hauer 	if (ret)
41947a70e6fSCosmin Samoila 		return ret;
42047a70e6fSCosmin Samoila 
421d46c2127SSascha Hauer 	ret = regmap_set_bits(micfil->regmap, REG_MICFIL_CTRL1,
42247a70e6fSCosmin Samoila 			      MICFIL_CTRL1_SRES);
4232c602c7eSSascha Hauer 	if (ret)
42447a70e6fSCosmin Samoila 		return ret;
42547a70e6fSCosmin Samoila 
426292709b9SShengjiu Wang 	/*
427292709b9SShengjiu Wang 	 * SRES is self-cleared bit, but REG_MICFIL_CTRL1 is defined
428292709b9SShengjiu Wang 	 * as non-volatile register, so SRES still remain in regmap
429292709b9SShengjiu Wang 	 * cache after set, that every update of REG_MICFIL_CTRL1,
430292709b9SShengjiu Wang 	 * software reset happens. so clear it explicitly.
431292709b9SShengjiu Wang 	 */
432292709b9SShengjiu Wang 	ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1,
433292709b9SShengjiu Wang 				MICFIL_CTRL1_SRES);
434292709b9SShengjiu Wang 	if (ret)
435292709b9SShengjiu Wang 		return ret;
436292709b9SShengjiu Wang 
437b776c4a4SShengjiu Wang 	/*
438b776c4a4SShengjiu Wang 	 * Set SRES should clear CHnF flags, But even add delay here
439b776c4a4SShengjiu Wang 	 * the CHnF may not be cleared sometimes, so clear CHnF explicitly.
440b776c4a4SShengjiu Wang 	 */
441b776c4a4SShengjiu Wang 	ret = regmap_write_bits(micfil->regmap, REG_MICFIL_STAT, 0xFF, 0xFF);
442b776c4a4SShengjiu Wang 	if (ret)
443b776c4a4SShengjiu Wang 		return ret;
444b776c4a4SShengjiu Wang 
44547a70e6fSCosmin Samoila 	return 0;
44647a70e6fSCosmin Samoila }
44747a70e6fSCosmin Samoila 
44847a70e6fSCosmin Samoila static int fsl_micfil_startup(struct snd_pcm_substream *substream,
44947a70e6fSCosmin Samoila 			      struct snd_soc_dai *dai)
45047a70e6fSCosmin Samoila {
45147a70e6fSCosmin Samoila 	struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai);
45247a70e6fSCosmin Samoila 
45347a70e6fSCosmin Samoila 	if (!micfil) {
45411106cb3STang Bin 		dev_err(dai->dev, "micfil dai priv_data not set\n");
45547a70e6fSCosmin Samoila 		return -EINVAL;
45647a70e6fSCosmin Samoila 	}
45747a70e6fSCosmin Samoila 
45847a70e6fSCosmin Samoila 	return 0;
45947a70e6fSCosmin Samoila }
46047a70e6fSCosmin Samoila 
46129dbfeecSShengjiu Wang /* Enable/disable hwvad interrupts */
46229dbfeecSShengjiu Wang static int fsl_micfil_configure_hwvad_interrupts(struct fsl_micfil *micfil, int enable)
46329dbfeecSShengjiu Wang {
46429dbfeecSShengjiu Wang 	u32 vadie_reg = enable ? MICFIL_VAD0_CTRL1_IE : 0;
46529dbfeecSShengjiu Wang 	u32 vaderie_reg = enable ? MICFIL_VAD0_CTRL1_ERIE : 0;
46629dbfeecSShengjiu Wang 
46729dbfeecSShengjiu Wang 	/* Voice Activity Detector Error Interruption */
46829dbfeecSShengjiu Wang 	regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1,
46929dbfeecSShengjiu Wang 			   MICFIL_VAD0_CTRL1_ERIE, vaderie_reg);
47029dbfeecSShengjiu Wang 
47129dbfeecSShengjiu Wang 	/* Voice Activity Detector Interruption */
47229dbfeecSShengjiu Wang 	regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1,
47329dbfeecSShengjiu Wang 			   MICFIL_VAD0_CTRL1_IE, vadie_reg);
47429dbfeecSShengjiu Wang 
47529dbfeecSShengjiu Wang 	return 0;
47629dbfeecSShengjiu Wang }
47729dbfeecSShengjiu Wang 
47829dbfeecSShengjiu Wang /* Configuration done only in energy-based initialization mode */
47929dbfeecSShengjiu Wang static int fsl_micfil_init_hwvad_energy_mode(struct fsl_micfil *micfil)
48029dbfeecSShengjiu Wang {
48129dbfeecSShengjiu Wang 	/* Keep the VADFRENDIS bitfield cleared. */
48229dbfeecSShengjiu Wang 	regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2,
48329dbfeecSShengjiu Wang 			  MICFIL_VAD0_CTRL2_FRENDIS);
48429dbfeecSShengjiu Wang 
48529dbfeecSShengjiu Wang 	/* Keep the VADPREFEN bitfield cleared. */
48629dbfeecSShengjiu Wang 	regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2,
48729dbfeecSShengjiu Wang 			  MICFIL_VAD0_CTRL2_PREFEN);
48829dbfeecSShengjiu Wang 
48929dbfeecSShengjiu Wang 	/* Keep the VADSFILEN bitfield cleared. */
49029dbfeecSShengjiu Wang 	regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG,
49129dbfeecSShengjiu Wang 			  MICFIL_VAD0_SCONFIG_SFILEN);
49229dbfeecSShengjiu Wang 
49329dbfeecSShengjiu Wang 	/* Keep the VADSMAXEN bitfield cleared. */
49429dbfeecSShengjiu Wang 	regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG,
49529dbfeecSShengjiu Wang 			  MICFIL_VAD0_SCONFIG_SMAXEN);
49629dbfeecSShengjiu Wang 
49729dbfeecSShengjiu Wang 	/* Keep the VADNFILAUTO bitfield asserted. */
49829dbfeecSShengjiu Wang 	regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG,
49929dbfeecSShengjiu Wang 			MICFIL_VAD0_NCONFIG_NFILAUT);
50029dbfeecSShengjiu Wang 
50129dbfeecSShengjiu Wang 	/* Keep the VADNMINEN bitfield cleared. */
50229dbfeecSShengjiu Wang 	regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG,
50329dbfeecSShengjiu Wang 			  MICFIL_VAD0_NCONFIG_NMINEN);
50429dbfeecSShengjiu Wang 
50529dbfeecSShengjiu Wang 	/* Keep the VADNDECEN bitfield cleared. */
50629dbfeecSShengjiu Wang 	regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG,
50729dbfeecSShengjiu Wang 			  MICFIL_VAD0_NCONFIG_NDECEN);
50829dbfeecSShengjiu Wang 
50929dbfeecSShengjiu Wang 	/* Keep the VADNOREN bitfield cleared. */
51029dbfeecSShengjiu Wang 	regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG,
51129dbfeecSShengjiu Wang 			  MICFIL_VAD0_NCONFIG_NOREN);
51229dbfeecSShengjiu Wang 
51329dbfeecSShengjiu Wang 	return 0;
51429dbfeecSShengjiu Wang }
51529dbfeecSShengjiu Wang 
51629dbfeecSShengjiu Wang /* Configuration done only in envelope-based initialization mode */
51729dbfeecSShengjiu Wang static int fsl_micfil_init_hwvad_envelope_mode(struct fsl_micfil *micfil)
51829dbfeecSShengjiu Wang {
51929dbfeecSShengjiu Wang 	/* Assert the VADFRENDIS bitfield */
52029dbfeecSShengjiu Wang 	regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2,
52129dbfeecSShengjiu Wang 			MICFIL_VAD0_CTRL2_FRENDIS);
52229dbfeecSShengjiu Wang 
52329dbfeecSShengjiu Wang 	/* Assert the VADPREFEN bitfield. */
52429dbfeecSShengjiu Wang 	regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2,
52529dbfeecSShengjiu Wang 			MICFIL_VAD0_CTRL2_PREFEN);
52629dbfeecSShengjiu Wang 
52729dbfeecSShengjiu Wang 	/* Assert the VADSFILEN bitfield. */
52829dbfeecSShengjiu Wang 	regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG,
52929dbfeecSShengjiu Wang 			MICFIL_VAD0_SCONFIG_SFILEN);
53029dbfeecSShengjiu Wang 
53129dbfeecSShengjiu Wang 	/* Assert the VADSMAXEN bitfield. */
53229dbfeecSShengjiu Wang 	regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG,
53329dbfeecSShengjiu Wang 			MICFIL_VAD0_SCONFIG_SMAXEN);
53429dbfeecSShengjiu Wang 
53529dbfeecSShengjiu Wang 	/* Clear the VADNFILAUTO bitfield */
53629dbfeecSShengjiu Wang 	regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG,
53729dbfeecSShengjiu Wang 			  MICFIL_VAD0_NCONFIG_NFILAUT);
53829dbfeecSShengjiu Wang 
53929dbfeecSShengjiu Wang 	/* Assert the VADNMINEN bitfield. */
54029dbfeecSShengjiu Wang 	regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG,
54129dbfeecSShengjiu Wang 			MICFIL_VAD0_NCONFIG_NMINEN);
54229dbfeecSShengjiu Wang 
54329dbfeecSShengjiu Wang 	/* Assert the VADNDECEN bitfield. */
54429dbfeecSShengjiu Wang 	regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG,
54529dbfeecSShengjiu Wang 			MICFIL_VAD0_NCONFIG_NDECEN);
54629dbfeecSShengjiu Wang 
54729dbfeecSShengjiu Wang 	/* Assert VADNOREN bitfield. */
54829dbfeecSShengjiu Wang 	regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG,
54929dbfeecSShengjiu Wang 			MICFIL_VAD0_NCONFIG_NOREN);
55029dbfeecSShengjiu Wang 
55129dbfeecSShengjiu Wang 	return 0;
55229dbfeecSShengjiu Wang }
55329dbfeecSShengjiu Wang 
55429dbfeecSShengjiu Wang /*
55529dbfeecSShengjiu Wang  * Hardware Voice Active Detection: The HWVAD takes data from the input
55629dbfeecSShengjiu Wang  * of a selected PDM microphone to detect if there is any
55729dbfeecSShengjiu Wang  * voice activity. When a voice activity is detected, an interrupt could
55829dbfeecSShengjiu Wang  * be delivered to the system. Initialization in section 8.4:
55929dbfeecSShengjiu Wang  * Can work in two modes:
56029dbfeecSShengjiu Wang  *  -> Eneveope-based mode (section 8.4.1)
56129dbfeecSShengjiu Wang  *  -> Energy-based mode (section 8.4.2)
56229dbfeecSShengjiu Wang  *
56329dbfeecSShengjiu Wang  * It is important to remark that the HWVAD detector could be enabled
56429dbfeecSShengjiu Wang  * or reset only when the MICFIL isn't running i.e. when the BSY_FIL
56529dbfeecSShengjiu Wang  * bit in STAT register is cleared
56629dbfeecSShengjiu Wang  */
56729dbfeecSShengjiu Wang static int fsl_micfil_hwvad_enable(struct fsl_micfil *micfil)
56829dbfeecSShengjiu Wang {
56929dbfeecSShengjiu Wang 	int ret;
57029dbfeecSShengjiu Wang 
57129dbfeecSShengjiu Wang 	micfil->vad_detected = 0;
57229dbfeecSShengjiu Wang 
57329dbfeecSShengjiu Wang 	/* envelope-based specific initialization */
57429dbfeecSShengjiu Wang 	if (micfil->vad_init_mode == MICFIL_HWVAD_ENVELOPE_MODE)
57529dbfeecSShengjiu Wang 		ret = fsl_micfil_init_hwvad_envelope_mode(micfil);
57629dbfeecSShengjiu Wang 	else
57729dbfeecSShengjiu Wang 		ret = fsl_micfil_init_hwvad_energy_mode(micfil);
57829dbfeecSShengjiu Wang 	if (ret)
57929dbfeecSShengjiu Wang 		return ret;
58029dbfeecSShengjiu Wang 
58129dbfeecSShengjiu Wang 	/* Voice Activity Detector Internal Filters Initialization*/
58229dbfeecSShengjiu Wang 	regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1,
58329dbfeecSShengjiu Wang 			MICFIL_VAD0_CTRL1_ST10);
58429dbfeecSShengjiu Wang 
58529dbfeecSShengjiu Wang 	/* Voice Activity Detector Internal Filter */
58629dbfeecSShengjiu Wang 	regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1,
58729dbfeecSShengjiu Wang 			  MICFIL_VAD0_CTRL1_ST10);
58829dbfeecSShengjiu Wang 
58929dbfeecSShengjiu Wang 	/* Enable Interrupts */
59029dbfeecSShengjiu Wang 	ret = fsl_micfil_configure_hwvad_interrupts(micfil, 1);
59129dbfeecSShengjiu Wang 	if (ret)
59229dbfeecSShengjiu Wang 		return ret;
59329dbfeecSShengjiu Wang 
59429dbfeecSShengjiu Wang 	/* Voice Activity Detector Reset */
59529dbfeecSShengjiu Wang 	regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1,
59629dbfeecSShengjiu Wang 			MICFIL_VAD0_CTRL1_RST);
59729dbfeecSShengjiu Wang 
59829dbfeecSShengjiu Wang 	/* Voice Activity Detector Enabled */
59929dbfeecSShengjiu Wang 	regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1,
60029dbfeecSShengjiu Wang 			MICFIL_VAD0_CTRL1_EN);
60129dbfeecSShengjiu Wang 
60229dbfeecSShengjiu Wang 	return 0;
60329dbfeecSShengjiu Wang }
60429dbfeecSShengjiu Wang 
60529dbfeecSShengjiu Wang static int fsl_micfil_hwvad_disable(struct fsl_micfil *micfil)
60629dbfeecSShengjiu Wang {
60729dbfeecSShengjiu Wang 	struct device *dev = &micfil->pdev->dev;
60829dbfeecSShengjiu Wang 	int ret = 0;
60929dbfeecSShengjiu Wang 
61029dbfeecSShengjiu Wang 	/* Disable HWVAD */
61129dbfeecSShengjiu Wang 	regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1,
61229dbfeecSShengjiu Wang 			  MICFIL_VAD0_CTRL1_EN);
61329dbfeecSShengjiu Wang 
61429dbfeecSShengjiu Wang 	/* Disable hwvad interrupts */
61529dbfeecSShengjiu Wang 	ret = fsl_micfil_configure_hwvad_interrupts(micfil, 0);
61629dbfeecSShengjiu Wang 	if (ret)
61729dbfeecSShengjiu Wang 		dev_err(dev, "Failed to disable interrupts\n");
61829dbfeecSShengjiu Wang 
61929dbfeecSShengjiu Wang 	return ret;
62029dbfeecSShengjiu Wang }
62129dbfeecSShengjiu Wang 
62247a70e6fSCosmin Samoila static int fsl_micfil_trigger(struct snd_pcm_substream *substream, int cmd,
62347a70e6fSCosmin Samoila 			      struct snd_soc_dai *dai)
62447a70e6fSCosmin Samoila {
62547a70e6fSCosmin Samoila 	struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai);
62647a70e6fSCosmin Samoila 	struct device *dev = &micfil->pdev->dev;
62747a70e6fSCosmin Samoila 	int ret;
62847a70e6fSCosmin Samoila 
62947a70e6fSCosmin Samoila 	switch (cmd) {
63047a70e6fSCosmin Samoila 	case SNDRV_PCM_TRIGGER_START:
63147a70e6fSCosmin Samoila 	case SNDRV_PCM_TRIGGER_RESUME:
63247a70e6fSCosmin Samoila 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
63347a70e6fSCosmin Samoila 		ret = fsl_micfil_reset(dev);
63447a70e6fSCosmin Samoila 		if (ret) {
63547a70e6fSCosmin Samoila 			dev_err(dev, "failed to soft reset\n");
63647a70e6fSCosmin Samoila 			return ret;
63747a70e6fSCosmin Samoila 		}
63847a70e6fSCosmin Samoila 
63947a70e6fSCosmin Samoila 		/* DMA Interrupt Selection - DISEL bits
64047a70e6fSCosmin Samoila 		 * 00 - DMA and IRQ disabled
64147a70e6fSCosmin Samoila 		 * 01 - DMA req enabled
64247a70e6fSCosmin Samoila 		 * 10 - IRQ enabled
64347a70e6fSCosmin Samoila 		 * 11 - reserved
64447a70e6fSCosmin Samoila 		 */
64547a70e6fSCosmin Samoila 		ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1,
64617f2142bSSascha Hauer 				MICFIL_CTRL1_DISEL,
64717f2142bSSascha Hauer 				FIELD_PREP(MICFIL_CTRL1_DISEL, MICFIL_CTRL1_DISEL_DMA));
6482c602c7eSSascha Hauer 		if (ret)
64947a70e6fSCosmin Samoila 			return ret;
65047a70e6fSCosmin Samoila 
65147a70e6fSCosmin Samoila 		/* Enable the module */
652d46c2127SSascha Hauer 		ret = regmap_set_bits(micfil->regmap, REG_MICFIL_CTRL1,
65347a70e6fSCosmin Samoila 				      MICFIL_CTRL1_PDMIEN);
6542c602c7eSSascha Hauer 		if (ret)
65547a70e6fSCosmin Samoila 			return ret;
65647a70e6fSCosmin Samoila 
65729dbfeecSShengjiu Wang 		if (micfil->vad_enabled)
65829dbfeecSShengjiu Wang 			fsl_micfil_hwvad_enable(micfil);
65929dbfeecSShengjiu Wang 
66047a70e6fSCosmin Samoila 		break;
66147a70e6fSCosmin Samoila 	case SNDRV_PCM_TRIGGER_STOP:
66247a70e6fSCosmin Samoila 	case SNDRV_PCM_TRIGGER_SUSPEND:
66347a70e6fSCosmin Samoila 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
66429dbfeecSShengjiu Wang 		if (micfil->vad_enabled)
66529dbfeecSShengjiu Wang 			fsl_micfil_hwvad_disable(micfil);
66629dbfeecSShengjiu Wang 
66747a70e6fSCosmin Samoila 		/* Disable the module */
668d46c2127SSascha Hauer 		ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1,
669d46c2127SSascha Hauer 					MICFIL_CTRL1_PDMIEN);
6702c602c7eSSascha Hauer 		if (ret)
67147a70e6fSCosmin Samoila 			return ret;
67247a70e6fSCosmin Samoila 
67347a70e6fSCosmin Samoila 		ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1,
67417f2142bSSascha Hauer 				MICFIL_CTRL1_DISEL,
67517f2142bSSascha Hauer 				FIELD_PREP(MICFIL_CTRL1_DISEL, MICFIL_CTRL1_DISEL_DISABLE));
6762c602c7eSSascha Hauer 		if (ret)
67747a70e6fSCosmin Samoila 			return ret;
67847a70e6fSCosmin Samoila 		break;
67947a70e6fSCosmin Samoila 	default:
68047a70e6fSCosmin Samoila 		return -EINVAL;
68147a70e6fSCosmin Samoila 	}
68247a70e6fSCosmin Samoila 	return 0;
68347a70e6fSCosmin Samoila }
68447a70e6fSCosmin Samoila 
68593f54100SShengjiu Wang static int fsl_micfil_reparent_rootclk(struct fsl_micfil *micfil, unsigned int sample_rate)
68693f54100SShengjiu Wang {
68793f54100SShengjiu Wang 	struct device *dev = &micfil->pdev->dev;
68893f54100SShengjiu Wang 	u64 ratio = sample_rate;
68993f54100SShengjiu Wang 	struct clk *clk;
69093f54100SShengjiu Wang 	int ret;
69193f54100SShengjiu Wang 
69293f54100SShengjiu Wang 	/* Get root clock */
69393f54100SShengjiu Wang 	clk = micfil->mclk;
69493f54100SShengjiu Wang 
69593f54100SShengjiu Wang 	/* Disable clock first, for it was enabled by pm_runtime */
69693f54100SShengjiu Wang 	clk_disable_unprepare(clk);
69793f54100SShengjiu Wang 	fsl_asoc_reparent_pll_clocks(dev, clk, micfil->pll8k_clk,
69893f54100SShengjiu Wang 				     micfil->pll11k_clk, ratio);
69993f54100SShengjiu Wang 	ret = clk_prepare_enable(clk);
70093f54100SShengjiu Wang 	if (ret)
70193f54100SShengjiu Wang 		return ret;
70293f54100SShengjiu Wang 
70393f54100SShengjiu Wang 	return 0;
70493f54100SShengjiu Wang }
70593f54100SShengjiu Wang 
70647a70e6fSCosmin Samoila static int fsl_micfil_hw_params(struct snd_pcm_substream *substream,
70747a70e6fSCosmin Samoila 				struct snd_pcm_hw_params *params,
70847a70e6fSCosmin Samoila 				struct snd_soc_dai *dai)
70947a70e6fSCosmin Samoila {
71047a70e6fSCosmin Samoila 	struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai);
71147a70e6fSCosmin Samoila 	unsigned int channels = params_channels(params);
71247a70e6fSCosmin Samoila 	unsigned int rate = params_rate(params);
713cc5ef57dSSascha Hauer 	int clk_div = 8;
714cc5ef57dSSascha Hauer 	int osr = MICFIL_OSR_DEFAULT;
71547a70e6fSCosmin Samoila 	int ret;
71647a70e6fSCosmin Samoila 
71747a70e6fSCosmin Samoila 	/* 1. Disable the module */
718d46c2127SSascha Hauer 	ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1,
719d46c2127SSascha Hauer 				MICFIL_CTRL1_PDMIEN);
7202c602c7eSSascha Hauer 	if (ret)
72147a70e6fSCosmin Samoila 		return ret;
72247a70e6fSCosmin Samoila 
72347a70e6fSCosmin Samoila 	/* enable channels */
72447a70e6fSCosmin Samoila 	ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1,
72547a70e6fSCosmin Samoila 				 0xFF, ((1 << channels) - 1));
7262c602c7eSSascha Hauer 	if (ret)
72747a70e6fSCosmin Samoila 		return ret;
72847a70e6fSCosmin Samoila 
72993f54100SShengjiu Wang 	ret = fsl_micfil_reparent_rootclk(micfil, rate);
73093f54100SShengjiu Wang 	if (ret)
73193f54100SShengjiu Wang 		return ret;
73293f54100SShengjiu Wang 
733cc5ef57dSSascha Hauer 	ret = clk_set_rate(micfil->mclk, rate * clk_div * osr * 8);
734cc5ef57dSSascha Hauer 	if (ret)
73547a70e6fSCosmin Samoila 		return ret;
736cc5ef57dSSascha Hauer 
737cc5ef57dSSascha Hauer 	ret = micfil_set_quality(micfil);
738cc5ef57dSSascha Hauer 	if (ret)
739cc5ef57dSSascha Hauer 		return ret;
740cc5ef57dSSascha Hauer 
741cc5ef57dSSascha Hauer 	ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2,
742cc5ef57dSSascha Hauer 				 MICFIL_CTRL2_CLKDIV | MICFIL_CTRL2_CICOSR,
743cc5ef57dSSascha Hauer 				 FIELD_PREP(MICFIL_CTRL2_CLKDIV, clk_div) |
744cc5ef57dSSascha Hauer 				 FIELD_PREP(MICFIL_CTRL2_CICOSR, 16 - osr));
74547a70e6fSCosmin Samoila 
74629dbfeecSShengjiu Wang 	/* Configure CIC OSR in VADCICOSR */
74729dbfeecSShengjiu Wang 	regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1,
74829dbfeecSShengjiu Wang 			   MICFIL_VAD0_CTRL1_CICOSR,
74929dbfeecSShengjiu Wang 			   FIELD_PREP(MICFIL_VAD0_CTRL1_CICOSR, 16 - osr));
75029dbfeecSShengjiu Wang 
75129dbfeecSShengjiu Wang 	/* Configure source channel in VADCHSEL */
75229dbfeecSShengjiu Wang 	regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1,
75329dbfeecSShengjiu Wang 			   MICFIL_VAD0_CTRL1_CHSEL,
75429dbfeecSShengjiu Wang 			   FIELD_PREP(MICFIL_VAD0_CTRL1_CHSEL, (channels - 1)));
75529dbfeecSShengjiu Wang 
7562495ba26SSascha Hauer 	micfil->dma_params_rx.peripheral_config = &micfil->sdmacfg;
7572495ba26SSascha Hauer 	micfil->dma_params_rx.peripheral_size = sizeof(micfil->sdmacfg);
7582495ba26SSascha Hauer 	micfil->sdmacfg.n_fifos_src = channels;
7592495ba26SSascha Hauer 	micfil->sdmacfg.sw_done = true;
76047a70e6fSCosmin Samoila 	micfil->dma_params_rx.maxburst = channels * MICFIL_DMA_MAXBURST_RX;
76177a7a6e9SChancel Liu 	if (micfil->soc->use_edma)
76277a7a6e9SChancel Liu 		micfil->dma_params_rx.maxburst = channels;
76347a70e6fSCosmin Samoila 
76447a70e6fSCosmin Samoila 	return 0;
76547a70e6fSCosmin Samoila }
76647a70e6fSCosmin Samoila 
76738d89a56SRikard Falkeborn static const struct snd_soc_dai_ops fsl_micfil_dai_ops = {
76847a70e6fSCosmin Samoila 	.startup = fsl_micfil_startup,
76947a70e6fSCosmin Samoila 	.trigger = fsl_micfil_trigger,
77047a70e6fSCosmin Samoila 	.hw_params = fsl_micfil_hw_params,
77147a70e6fSCosmin Samoila };
77247a70e6fSCosmin Samoila 
77347a70e6fSCosmin Samoila static int fsl_micfil_dai_probe(struct snd_soc_dai *cpu_dai)
77447a70e6fSCosmin Samoila {
77547a70e6fSCosmin Samoila 	struct fsl_micfil *micfil = dev_get_drvdata(cpu_dai->dev);
7763b13b143SShengjiu Wang 	struct device *dev = cpu_dai->dev;
7773b13b143SShengjiu Wang 	unsigned int val = 0;
7783b13b143SShengjiu Wang 	int ret, i;
77947a70e6fSCosmin Samoila 
7803b13b143SShengjiu Wang 	micfil->quality = QUALITY_VLOW0;
78129dbfeecSShengjiu Wang 	micfil->card = cpu_dai->component->card;
78247a70e6fSCosmin Samoila 
7833b13b143SShengjiu Wang 	/* set default gain to 2 */
7843b13b143SShengjiu Wang 	regmap_write(micfil->regmap, REG_MICFIL_OUT_CTRL, 0x22222222);
7853b13b143SShengjiu Wang 
7863b13b143SShengjiu Wang 	/* set DC Remover in bypass mode*/
7873b13b143SShengjiu Wang 	for (i = 0; i < MICFIL_OUTPUT_CHANNELS; i++)
7883b13b143SShengjiu Wang 		val |= MICFIL_DC_BYPASS << MICFIL_DC_CHX_SHIFT(i);
7893b13b143SShengjiu Wang 	ret = regmap_update_bits(micfil->regmap, REG_MICFIL_DC_CTRL,
7903b13b143SShengjiu Wang 				 MICFIL_DC_CTRL_CONFIG, val);
7913b13b143SShengjiu Wang 	if (ret) {
7923b13b143SShengjiu Wang 		dev_err(dev, "failed to set DC Remover mode bits\n");
7933b13b143SShengjiu Wang 		return ret;
7943b13b143SShengjiu Wang 	}
7953b13b143SShengjiu Wang 	micfil->dc_remover = MICFIL_DC_BYPASS;
79647a70e6fSCosmin Samoila 
79747a70e6fSCosmin Samoila 	snd_soc_dai_init_dma_data(cpu_dai, NULL,
79847a70e6fSCosmin Samoila 				  &micfil->dma_params_rx);
79947a70e6fSCosmin Samoila 
80047a70e6fSCosmin Samoila 	/* FIFO Watermark Control - FIFOWMK*/
80147a70e6fSCosmin Samoila 	ret = regmap_update_bits(micfil->regmap, REG_MICFIL_FIFO_CTRL,
80217f2142bSSascha Hauer 			MICFIL_FIFO_CTRL_FIFOWMK,
80317f2142bSSascha Hauer 			FIELD_PREP(MICFIL_FIFO_CTRL_FIFOWMK, micfil->soc->fifo_depth - 1));
8042c602c7eSSascha Hauer 	if (ret)
80547a70e6fSCosmin Samoila 		return ret;
80647a70e6fSCosmin Samoila 
80747a70e6fSCosmin Samoila 	return 0;
80847a70e6fSCosmin Samoila }
80947a70e6fSCosmin Samoila 
81047a70e6fSCosmin Samoila static struct snd_soc_dai_driver fsl_micfil_dai = {
81147a70e6fSCosmin Samoila 	.probe = fsl_micfil_dai_probe,
81247a70e6fSCosmin Samoila 	.capture = {
81347a70e6fSCosmin Samoila 		.stream_name = "CPU-Capture",
81447a70e6fSCosmin Samoila 		.channels_min = 1,
81547a70e6fSCosmin Samoila 		.channels_max = 8,
81699c08cdbSSascha Hauer 		.rates = SNDRV_PCM_RATE_8000_48000,
81799c08cdbSSascha Hauer 		.formats = SNDRV_PCM_FMTBIT_S16_LE,
81847a70e6fSCosmin Samoila 	},
81947a70e6fSCosmin Samoila 	.ops = &fsl_micfil_dai_ops,
82047a70e6fSCosmin Samoila };
82147a70e6fSCosmin Samoila 
82247a70e6fSCosmin Samoila static const struct snd_soc_component_driver fsl_micfil_component = {
82347a70e6fSCosmin Samoila 	.name		= "fsl-micfil-dai",
82447a70e6fSCosmin Samoila 	.controls       = fsl_micfil_snd_controls,
82547a70e6fSCosmin Samoila 	.num_controls   = ARRAY_SIZE(fsl_micfil_snd_controls),
826978bd27cSShengjiu Wang 	.legacy_dai_naming      = 1,
82747a70e6fSCosmin Samoila };
82847a70e6fSCosmin Samoila 
82947a70e6fSCosmin Samoila /* REGMAP */
83047a70e6fSCosmin Samoila static const struct reg_default fsl_micfil_reg_defaults[] = {
83147a70e6fSCosmin Samoila 	{REG_MICFIL_CTRL1,		0x00000000},
83247a70e6fSCosmin Samoila 	{REG_MICFIL_CTRL2,		0x00000000},
83347a70e6fSCosmin Samoila 	{REG_MICFIL_STAT,		0x00000000},
83447a70e6fSCosmin Samoila 	{REG_MICFIL_FIFO_CTRL,		0x00000007},
83547a70e6fSCosmin Samoila 	{REG_MICFIL_FIFO_STAT,		0x00000000},
83647a70e6fSCosmin Samoila 	{REG_MICFIL_DATACH0,		0x00000000},
83747a70e6fSCosmin Samoila 	{REG_MICFIL_DATACH1,		0x00000000},
83847a70e6fSCosmin Samoila 	{REG_MICFIL_DATACH2,		0x00000000},
83947a70e6fSCosmin Samoila 	{REG_MICFIL_DATACH3,		0x00000000},
84047a70e6fSCosmin Samoila 	{REG_MICFIL_DATACH4,		0x00000000},
84147a70e6fSCosmin Samoila 	{REG_MICFIL_DATACH5,		0x00000000},
84247a70e6fSCosmin Samoila 	{REG_MICFIL_DATACH6,		0x00000000},
84347a70e6fSCosmin Samoila 	{REG_MICFIL_DATACH7,		0x00000000},
84447a70e6fSCosmin Samoila 	{REG_MICFIL_DC_CTRL,		0x00000000},
84547a70e6fSCosmin Samoila 	{REG_MICFIL_OUT_CTRL,		0x00000000},
84647a70e6fSCosmin Samoila 	{REG_MICFIL_OUT_STAT,		0x00000000},
84747a70e6fSCosmin Samoila 	{REG_MICFIL_VAD0_CTRL1,		0x00000000},
84847a70e6fSCosmin Samoila 	{REG_MICFIL_VAD0_CTRL2,		0x000A0000},
84947a70e6fSCosmin Samoila 	{REG_MICFIL_VAD0_STAT,		0x00000000},
85047a70e6fSCosmin Samoila 	{REG_MICFIL_VAD0_SCONFIG,	0x00000000},
85147a70e6fSCosmin Samoila 	{REG_MICFIL_VAD0_NCONFIG,	0x80000000},
85247a70e6fSCosmin Samoila 	{REG_MICFIL_VAD0_NDATA,		0x00000000},
85347a70e6fSCosmin Samoila 	{REG_MICFIL_VAD0_ZCD,		0x00000004},
85447a70e6fSCosmin Samoila };
85547a70e6fSCosmin Samoila 
85647a70e6fSCosmin Samoila static bool fsl_micfil_readable_reg(struct device *dev, unsigned int reg)
85747a70e6fSCosmin Samoila {
85847a70e6fSCosmin Samoila 	switch (reg) {
85947a70e6fSCosmin Samoila 	case REG_MICFIL_CTRL1:
86047a70e6fSCosmin Samoila 	case REG_MICFIL_CTRL2:
86147a70e6fSCosmin Samoila 	case REG_MICFIL_STAT:
86247a70e6fSCosmin Samoila 	case REG_MICFIL_FIFO_CTRL:
86347a70e6fSCosmin Samoila 	case REG_MICFIL_FIFO_STAT:
86447a70e6fSCosmin Samoila 	case REG_MICFIL_DATACH0:
86547a70e6fSCosmin Samoila 	case REG_MICFIL_DATACH1:
86647a70e6fSCosmin Samoila 	case REG_MICFIL_DATACH2:
86747a70e6fSCosmin Samoila 	case REG_MICFIL_DATACH3:
86847a70e6fSCosmin Samoila 	case REG_MICFIL_DATACH4:
86947a70e6fSCosmin Samoila 	case REG_MICFIL_DATACH5:
87047a70e6fSCosmin Samoila 	case REG_MICFIL_DATACH6:
87147a70e6fSCosmin Samoila 	case REG_MICFIL_DATACH7:
87247a70e6fSCosmin Samoila 	case REG_MICFIL_DC_CTRL:
87347a70e6fSCosmin Samoila 	case REG_MICFIL_OUT_CTRL:
87447a70e6fSCosmin Samoila 	case REG_MICFIL_OUT_STAT:
87551d765f7SChancel Liu 	case REG_MICFIL_FSYNC_CTRL:
87651d765f7SChancel Liu 	case REG_MICFIL_VERID:
87751d765f7SChancel Liu 	case REG_MICFIL_PARAM:
87847a70e6fSCosmin Samoila 	case REG_MICFIL_VAD0_CTRL1:
87947a70e6fSCosmin Samoila 	case REG_MICFIL_VAD0_CTRL2:
88047a70e6fSCosmin Samoila 	case REG_MICFIL_VAD0_STAT:
88147a70e6fSCosmin Samoila 	case REG_MICFIL_VAD0_SCONFIG:
88247a70e6fSCosmin Samoila 	case REG_MICFIL_VAD0_NCONFIG:
88347a70e6fSCosmin Samoila 	case REG_MICFIL_VAD0_NDATA:
88447a70e6fSCosmin Samoila 	case REG_MICFIL_VAD0_ZCD:
88547a70e6fSCosmin Samoila 		return true;
88647a70e6fSCosmin Samoila 	default:
88747a70e6fSCosmin Samoila 		return false;
88847a70e6fSCosmin Samoila 	}
88947a70e6fSCosmin Samoila }
89047a70e6fSCosmin Samoila 
89147a70e6fSCosmin Samoila static bool fsl_micfil_writeable_reg(struct device *dev, unsigned int reg)
89247a70e6fSCosmin Samoila {
89347a70e6fSCosmin Samoila 	switch (reg) {
89447a70e6fSCosmin Samoila 	case REG_MICFIL_CTRL1:
89547a70e6fSCosmin Samoila 	case REG_MICFIL_CTRL2:
89647a70e6fSCosmin Samoila 	case REG_MICFIL_STAT:		/* Write 1 to Clear */
89747a70e6fSCosmin Samoila 	case REG_MICFIL_FIFO_CTRL:
89847a70e6fSCosmin Samoila 	case REG_MICFIL_FIFO_STAT:	/* Write 1 to Clear */
89947a70e6fSCosmin Samoila 	case REG_MICFIL_DC_CTRL:
90047a70e6fSCosmin Samoila 	case REG_MICFIL_OUT_CTRL:
90147a70e6fSCosmin Samoila 	case REG_MICFIL_OUT_STAT:	/* Write 1 to Clear */
90251d765f7SChancel Liu 	case REG_MICFIL_FSYNC_CTRL:
90347a70e6fSCosmin Samoila 	case REG_MICFIL_VAD0_CTRL1:
90447a70e6fSCosmin Samoila 	case REG_MICFIL_VAD0_CTRL2:
90547a70e6fSCosmin Samoila 	case REG_MICFIL_VAD0_STAT:	/* Write 1 to Clear */
90647a70e6fSCosmin Samoila 	case REG_MICFIL_VAD0_SCONFIG:
90747a70e6fSCosmin Samoila 	case REG_MICFIL_VAD0_NCONFIG:
90847a70e6fSCosmin Samoila 	case REG_MICFIL_VAD0_ZCD:
90947a70e6fSCosmin Samoila 		return true;
91047a70e6fSCosmin Samoila 	default:
91147a70e6fSCosmin Samoila 		return false;
91247a70e6fSCosmin Samoila 	}
91347a70e6fSCosmin Samoila }
91447a70e6fSCosmin Samoila 
91547a70e6fSCosmin Samoila static bool fsl_micfil_volatile_reg(struct device *dev, unsigned int reg)
91647a70e6fSCosmin Samoila {
91747a70e6fSCosmin Samoila 	switch (reg) {
91847a70e6fSCosmin Samoila 	case REG_MICFIL_STAT:
91947a70e6fSCosmin Samoila 	case REG_MICFIL_DATACH0:
92047a70e6fSCosmin Samoila 	case REG_MICFIL_DATACH1:
92147a70e6fSCosmin Samoila 	case REG_MICFIL_DATACH2:
92247a70e6fSCosmin Samoila 	case REG_MICFIL_DATACH3:
92347a70e6fSCosmin Samoila 	case REG_MICFIL_DATACH4:
92447a70e6fSCosmin Samoila 	case REG_MICFIL_DATACH5:
92547a70e6fSCosmin Samoila 	case REG_MICFIL_DATACH6:
92647a70e6fSCosmin Samoila 	case REG_MICFIL_DATACH7:
92751d765f7SChancel Liu 	case REG_MICFIL_VERID:
92851d765f7SChancel Liu 	case REG_MICFIL_PARAM:
92947a70e6fSCosmin Samoila 	case REG_MICFIL_VAD0_STAT:
93047a70e6fSCosmin Samoila 	case REG_MICFIL_VAD0_NDATA:
93147a70e6fSCosmin Samoila 		return true;
93247a70e6fSCosmin Samoila 	default:
93347a70e6fSCosmin Samoila 		return false;
93447a70e6fSCosmin Samoila 	}
93547a70e6fSCosmin Samoila }
93647a70e6fSCosmin Samoila 
93747a70e6fSCosmin Samoila static const struct regmap_config fsl_micfil_regmap_config = {
93847a70e6fSCosmin Samoila 	.reg_bits = 32,
93947a70e6fSCosmin Samoila 	.reg_stride = 4,
94047a70e6fSCosmin Samoila 	.val_bits = 32,
94147a70e6fSCosmin Samoila 
94247a70e6fSCosmin Samoila 	.max_register = REG_MICFIL_VAD0_ZCD,
94347a70e6fSCosmin Samoila 	.reg_defaults = fsl_micfil_reg_defaults,
94447a70e6fSCosmin Samoila 	.num_reg_defaults = ARRAY_SIZE(fsl_micfil_reg_defaults),
94547a70e6fSCosmin Samoila 	.readable_reg = fsl_micfil_readable_reg,
94647a70e6fSCosmin Samoila 	.volatile_reg = fsl_micfil_volatile_reg,
94747a70e6fSCosmin Samoila 	.writeable_reg = fsl_micfil_writeable_reg,
94847a70e6fSCosmin Samoila 	.cache_type = REGCACHE_RBTREE,
94947a70e6fSCosmin Samoila };
95047a70e6fSCosmin Samoila 
95147a70e6fSCosmin Samoila /* END OF REGMAP */
95247a70e6fSCosmin Samoila 
95347a70e6fSCosmin Samoila static irqreturn_t micfil_isr(int irq, void *devid)
95447a70e6fSCosmin Samoila {
95547a70e6fSCosmin Samoila 	struct fsl_micfil *micfil = (struct fsl_micfil *)devid;
95647a70e6fSCosmin Samoila 	struct platform_device *pdev = micfil->pdev;
95747a70e6fSCosmin Samoila 	u32 stat_reg;
95847a70e6fSCosmin Samoila 	u32 fifo_stat_reg;
95947a70e6fSCosmin Samoila 	u32 ctrl1_reg;
96047a70e6fSCosmin Samoila 	bool dma_enabled;
96147a70e6fSCosmin Samoila 	int i;
96247a70e6fSCosmin Samoila 
96347a70e6fSCosmin Samoila 	regmap_read(micfil->regmap, REG_MICFIL_STAT, &stat_reg);
96447a70e6fSCosmin Samoila 	regmap_read(micfil->regmap, REG_MICFIL_CTRL1, &ctrl1_reg);
96547a70e6fSCosmin Samoila 	regmap_read(micfil->regmap, REG_MICFIL_FIFO_STAT, &fifo_stat_reg);
96647a70e6fSCosmin Samoila 
96717f2142bSSascha Hauer 	dma_enabled = FIELD_GET(MICFIL_CTRL1_DISEL, ctrl1_reg) == MICFIL_CTRL1_DISEL_DMA;
96847a70e6fSCosmin Samoila 
96947a70e6fSCosmin Samoila 	/* Channel 0-7 Output Data Flags */
97047a70e6fSCosmin Samoila 	for (i = 0; i < MICFIL_OUTPUT_CHANNELS; i++) {
97117f2142bSSascha Hauer 		if (stat_reg & MICFIL_STAT_CHXF(i))
97247a70e6fSCosmin Samoila 			dev_dbg(&pdev->dev,
97347a70e6fSCosmin Samoila 				"Data available in Data Channel %d\n", i);
97447a70e6fSCosmin Samoila 		/* if DMA is not enabled, field must be written with 1
97547a70e6fSCosmin Samoila 		 * to clear
97647a70e6fSCosmin Samoila 		 */
97747a70e6fSCosmin Samoila 		if (!dma_enabled)
97847a70e6fSCosmin Samoila 			regmap_write_bits(micfil->regmap,
97947a70e6fSCosmin Samoila 					  REG_MICFIL_STAT,
98017f2142bSSascha Hauer 					  MICFIL_STAT_CHXF(i),
98147a70e6fSCosmin Samoila 					  1);
98247a70e6fSCosmin Samoila 	}
98347a70e6fSCosmin Samoila 
98447a70e6fSCosmin Samoila 	for (i = 0; i < MICFIL_FIFO_NUM; i++) {
98517f2142bSSascha Hauer 		if (fifo_stat_reg & MICFIL_FIFO_STAT_FIFOX_OVER(i))
98647a70e6fSCosmin Samoila 			dev_dbg(&pdev->dev,
98747a70e6fSCosmin Samoila 				"FIFO Overflow Exception flag for channel %d\n",
98847a70e6fSCosmin Samoila 				i);
98947a70e6fSCosmin Samoila 
99017f2142bSSascha Hauer 		if (fifo_stat_reg & MICFIL_FIFO_STAT_FIFOX_UNDER(i))
99147a70e6fSCosmin Samoila 			dev_dbg(&pdev->dev,
99247a70e6fSCosmin Samoila 				"FIFO Underflow Exception flag for channel %d\n",
99347a70e6fSCosmin Samoila 				i);
99447a70e6fSCosmin Samoila 	}
99547a70e6fSCosmin Samoila 
99647a70e6fSCosmin Samoila 	return IRQ_HANDLED;
99747a70e6fSCosmin Samoila }
99847a70e6fSCosmin Samoila 
99947a70e6fSCosmin Samoila static irqreturn_t micfil_err_isr(int irq, void *devid)
100047a70e6fSCosmin Samoila {
100147a70e6fSCosmin Samoila 	struct fsl_micfil *micfil = (struct fsl_micfil *)devid;
100247a70e6fSCosmin Samoila 	struct platform_device *pdev = micfil->pdev;
100347a70e6fSCosmin Samoila 	u32 stat_reg;
100447a70e6fSCosmin Samoila 
100547a70e6fSCosmin Samoila 	regmap_read(micfil->regmap, REG_MICFIL_STAT, &stat_reg);
100647a70e6fSCosmin Samoila 
1007bd2cffd1SSascha Hauer 	if (stat_reg & MICFIL_STAT_BSY_FIL)
100847a70e6fSCosmin Samoila 		dev_dbg(&pdev->dev, "isr: Decimation Filter is running\n");
100947a70e6fSCosmin Samoila 
1010bd2cffd1SSascha Hauer 	if (stat_reg & MICFIL_STAT_FIR_RDY)
101147a70e6fSCosmin Samoila 		dev_dbg(&pdev->dev, "isr: FIR Filter Data ready\n");
101247a70e6fSCosmin Samoila 
1013bd2cffd1SSascha Hauer 	if (stat_reg & MICFIL_STAT_LOWFREQF) {
101447a70e6fSCosmin Samoila 		dev_dbg(&pdev->dev, "isr: ipg_clk_app is too low\n");
101547a70e6fSCosmin Samoila 		regmap_write_bits(micfil->regmap, REG_MICFIL_STAT,
1016bd2cffd1SSascha Hauer 				  MICFIL_STAT_LOWFREQF, 1);
101747a70e6fSCosmin Samoila 	}
101847a70e6fSCosmin Samoila 
101947a70e6fSCosmin Samoila 	return IRQ_HANDLED;
102047a70e6fSCosmin Samoila }
102147a70e6fSCosmin Samoila 
102229dbfeecSShengjiu Wang static irqreturn_t voice_detected_fn(int irq, void *devid)
102329dbfeecSShengjiu Wang {
102429dbfeecSShengjiu Wang 	struct fsl_micfil *micfil = (struct fsl_micfil *)devid;
102529dbfeecSShengjiu Wang 	struct snd_kcontrol *kctl;
102629dbfeecSShengjiu Wang 
102729dbfeecSShengjiu Wang 	if (!micfil->card)
102829dbfeecSShengjiu Wang 		return IRQ_HANDLED;
102929dbfeecSShengjiu Wang 
103029dbfeecSShengjiu Wang 	kctl = snd_soc_card_get_kcontrol(micfil->card, "VAD Detected");
103129dbfeecSShengjiu Wang 	if (!kctl)
103229dbfeecSShengjiu Wang 		return IRQ_HANDLED;
103329dbfeecSShengjiu Wang 
103429dbfeecSShengjiu Wang 	if (micfil->vad_detected)
103529dbfeecSShengjiu Wang 		snd_ctl_notify(micfil->card->snd_card,
103629dbfeecSShengjiu Wang 			       SNDRV_CTL_EVENT_MASK_VALUE,
103729dbfeecSShengjiu Wang 			       &kctl->id);
103829dbfeecSShengjiu Wang 
103929dbfeecSShengjiu Wang 	return IRQ_HANDLED;
104029dbfeecSShengjiu Wang }
104129dbfeecSShengjiu Wang 
104229dbfeecSShengjiu Wang static irqreturn_t hwvad_isr(int irq, void *devid)
104329dbfeecSShengjiu Wang {
104429dbfeecSShengjiu Wang 	struct fsl_micfil *micfil = (struct fsl_micfil *)devid;
104529dbfeecSShengjiu Wang 	struct device *dev = &micfil->pdev->dev;
104629dbfeecSShengjiu Wang 	u32 vad0_reg;
104729dbfeecSShengjiu Wang 	int ret;
104829dbfeecSShengjiu Wang 
104929dbfeecSShengjiu Wang 	regmap_read(micfil->regmap, REG_MICFIL_VAD0_STAT, &vad0_reg);
105029dbfeecSShengjiu Wang 
105129dbfeecSShengjiu Wang 	/*
105229dbfeecSShengjiu Wang 	 * The only difference between MICFIL_VAD0_STAT_EF and
105329dbfeecSShengjiu Wang 	 * MICFIL_VAD0_STAT_IF is that the former requires Write
105429dbfeecSShengjiu Wang 	 * 1 to Clear. Since both flags are set, it is enough
105529dbfeecSShengjiu Wang 	 * to only read one of them
105629dbfeecSShengjiu Wang 	 */
105729dbfeecSShengjiu Wang 	if (vad0_reg & MICFIL_VAD0_STAT_IF) {
105829dbfeecSShengjiu Wang 		/* Write 1 to clear */
105929dbfeecSShengjiu Wang 		regmap_write_bits(micfil->regmap, REG_MICFIL_VAD0_STAT,
106029dbfeecSShengjiu Wang 				  MICFIL_VAD0_STAT_IF,
106129dbfeecSShengjiu Wang 				  MICFIL_VAD0_STAT_IF);
106229dbfeecSShengjiu Wang 
106329dbfeecSShengjiu Wang 		micfil->vad_detected = 1;
106429dbfeecSShengjiu Wang 	}
106529dbfeecSShengjiu Wang 
106629dbfeecSShengjiu Wang 	ret = fsl_micfil_hwvad_disable(micfil);
106729dbfeecSShengjiu Wang 	if (ret)
106829dbfeecSShengjiu Wang 		dev_err(dev, "Failed to disable hwvad\n");
106929dbfeecSShengjiu Wang 
107029dbfeecSShengjiu Wang 	return IRQ_WAKE_THREAD;
107129dbfeecSShengjiu Wang }
107229dbfeecSShengjiu Wang 
107329dbfeecSShengjiu Wang static irqreturn_t hwvad_err_isr(int irq, void *devid)
107429dbfeecSShengjiu Wang {
107529dbfeecSShengjiu Wang 	struct fsl_micfil *micfil = (struct fsl_micfil *)devid;
107629dbfeecSShengjiu Wang 	struct device *dev = &micfil->pdev->dev;
107729dbfeecSShengjiu Wang 	u32 vad0_reg;
107829dbfeecSShengjiu Wang 
107929dbfeecSShengjiu Wang 	regmap_read(micfil->regmap, REG_MICFIL_VAD0_STAT, &vad0_reg);
108029dbfeecSShengjiu Wang 
108129dbfeecSShengjiu Wang 	if (vad0_reg & MICFIL_VAD0_STAT_INSATF)
108229dbfeecSShengjiu Wang 		dev_dbg(dev, "voice activity input overflow/underflow detected\n");
108329dbfeecSShengjiu Wang 
108429dbfeecSShengjiu Wang 	return IRQ_HANDLED;
108529dbfeecSShengjiu Wang }
108629dbfeecSShengjiu Wang 
108736736505SChancel Liu static int fsl_micfil_runtime_suspend(struct device *dev);
108836736505SChancel Liu static int fsl_micfil_runtime_resume(struct device *dev);
108936736505SChancel Liu 
109047a70e6fSCosmin Samoila static int fsl_micfil_probe(struct platform_device *pdev)
109147a70e6fSCosmin Samoila {
109247a70e6fSCosmin Samoila 	struct device_node *np = pdev->dev.of_node;
109347a70e6fSCosmin Samoila 	struct fsl_micfil *micfil;
109447a70e6fSCosmin Samoila 	struct resource *res;
109547a70e6fSCosmin Samoila 	void __iomem *regs;
109647a70e6fSCosmin Samoila 	int ret, i;
109747a70e6fSCosmin Samoila 
109847a70e6fSCosmin Samoila 	micfil = devm_kzalloc(&pdev->dev, sizeof(*micfil), GFP_KERNEL);
109947a70e6fSCosmin Samoila 	if (!micfil)
110047a70e6fSCosmin Samoila 		return -ENOMEM;
110147a70e6fSCosmin Samoila 
110247a70e6fSCosmin Samoila 	micfil->pdev = pdev;
11037eb10bfbSJustin Stitt 	strscpy(micfil->name, np->name, sizeof(micfil->name));
110447a70e6fSCosmin Samoila 
1105d7388718SFabio Estevam 	micfil->soc = of_device_get_match_data(&pdev->dev);
110647a70e6fSCosmin Samoila 
110747a70e6fSCosmin Samoila 	/* ipg_clk is used to control the registers
110847a70e6fSCosmin Samoila 	 * ipg_clk_app is used to operate the filter
110947a70e6fSCosmin Samoila 	 */
111047a70e6fSCosmin Samoila 	micfil->mclk = devm_clk_get(&pdev->dev, "ipg_clk_app");
111147a70e6fSCosmin Samoila 	if (IS_ERR(micfil->mclk)) {
111247a70e6fSCosmin Samoila 		dev_err(&pdev->dev, "failed to get core clock: %ld\n",
111347a70e6fSCosmin Samoila 			PTR_ERR(micfil->mclk));
111447a70e6fSCosmin Samoila 		return PTR_ERR(micfil->mclk);
111547a70e6fSCosmin Samoila 	}
111647a70e6fSCosmin Samoila 
1117b5cf28f7SShengjiu Wang 	micfil->busclk = devm_clk_get(&pdev->dev, "ipg_clk");
1118b5cf28f7SShengjiu Wang 	if (IS_ERR(micfil->busclk)) {
1119b5cf28f7SShengjiu Wang 		dev_err(&pdev->dev, "failed to get ipg clock: %ld\n",
1120b5cf28f7SShengjiu Wang 			PTR_ERR(micfil->busclk));
1121b5cf28f7SShengjiu Wang 		return PTR_ERR(micfil->busclk);
1122b5cf28f7SShengjiu Wang 	}
1123b5cf28f7SShengjiu Wang 
112493f54100SShengjiu Wang 	fsl_asoc_get_pll_clocks(&pdev->dev, &micfil->pll8k_clk,
112593f54100SShengjiu Wang 				&micfil->pll11k_clk);
112693f54100SShengjiu Wang 
112747a70e6fSCosmin Samoila 	/* init regmap */
1128d9bf1e79SYang Yingliang 	regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
112947a70e6fSCosmin Samoila 	if (IS_ERR(regs))
113047a70e6fSCosmin Samoila 		return PTR_ERR(regs);
113147a70e6fSCosmin Samoila 
1132b5cf28f7SShengjiu Wang 	micfil->regmap = devm_regmap_init_mmio(&pdev->dev,
113347a70e6fSCosmin Samoila 					       regs,
113447a70e6fSCosmin Samoila 					       &fsl_micfil_regmap_config);
113547a70e6fSCosmin Samoila 	if (IS_ERR(micfil->regmap)) {
113647a70e6fSCosmin Samoila 		dev_err(&pdev->dev, "failed to init MICFIL regmap: %ld\n",
113747a70e6fSCosmin Samoila 			PTR_ERR(micfil->regmap));
113847a70e6fSCosmin Samoila 		return PTR_ERR(micfil->regmap);
113947a70e6fSCosmin Samoila 	}
114047a70e6fSCosmin Samoila 
114147a70e6fSCosmin Samoila 	/* dataline mask for RX */
114247a70e6fSCosmin Samoila 	ret = of_property_read_u32_index(np,
114347a70e6fSCosmin Samoila 					 "fsl,dataline",
114447a70e6fSCosmin Samoila 					 0,
114547a70e6fSCosmin Samoila 					 &micfil->dataline);
114647a70e6fSCosmin Samoila 	if (ret)
114747a70e6fSCosmin Samoila 		micfil->dataline = 1;
114847a70e6fSCosmin Samoila 
114947a70e6fSCosmin Samoila 	if (micfil->dataline & ~micfil->soc->dataline) {
115047a70e6fSCosmin Samoila 		dev_err(&pdev->dev, "dataline setting error, Mask is 0x%X\n",
115147a70e6fSCosmin Samoila 			micfil->soc->dataline);
115247a70e6fSCosmin Samoila 		return -EINVAL;
115347a70e6fSCosmin Samoila 	}
115447a70e6fSCosmin Samoila 
115547a70e6fSCosmin Samoila 	/* get IRQs */
115647a70e6fSCosmin Samoila 	for (i = 0; i < MICFIL_IRQ_LINES; i++) {
115747a70e6fSCosmin Samoila 		micfil->irq[i] = platform_get_irq(pdev, i);
115883b35f45STang Bin 		if (micfil->irq[i] < 0)
115947a70e6fSCosmin Samoila 			return micfil->irq[i];
116047a70e6fSCosmin Samoila 	}
116147a70e6fSCosmin Samoila 
1162a62ed960SFabio Estevam 	/* Digital Microphone interface interrupt */
116347a70e6fSCosmin Samoila 	ret = devm_request_irq(&pdev->dev, micfil->irq[0],
1164cbd090faSSascha Hauer 			       micfil_isr, IRQF_SHARED,
116547a70e6fSCosmin Samoila 			       micfil->name, micfil);
116647a70e6fSCosmin Samoila 	if (ret) {
116747a70e6fSCosmin Samoila 		dev_err(&pdev->dev, "failed to claim mic interface irq %u\n",
116847a70e6fSCosmin Samoila 			micfil->irq[0]);
116947a70e6fSCosmin Samoila 		return ret;
117047a70e6fSCosmin Samoila 	}
117147a70e6fSCosmin Samoila 
1172a62ed960SFabio Estevam 	/* Digital Microphone interface error interrupt */
117347a70e6fSCosmin Samoila 	ret = devm_request_irq(&pdev->dev, micfil->irq[1],
1174cbd090faSSascha Hauer 			       micfil_err_isr, IRQF_SHARED,
117547a70e6fSCosmin Samoila 			       micfil->name, micfil);
117647a70e6fSCosmin Samoila 	if (ret) {
117747a70e6fSCosmin Samoila 		dev_err(&pdev->dev, "failed to claim mic interface error irq %u\n",
117847a70e6fSCosmin Samoila 			micfil->irq[1]);
117947a70e6fSCosmin Samoila 		return ret;
118047a70e6fSCosmin Samoila 	}
118147a70e6fSCosmin Samoila 
118229dbfeecSShengjiu Wang 	/* Digital Microphone interface voice activity detector event */
118329dbfeecSShengjiu Wang 	ret = devm_request_threaded_irq(&pdev->dev, micfil->irq[2],
118429dbfeecSShengjiu Wang 					hwvad_isr, voice_detected_fn,
118529dbfeecSShengjiu Wang 					IRQF_SHARED, micfil->name, micfil);
118629dbfeecSShengjiu Wang 	if (ret) {
118729dbfeecSShengjiu Wang 		dev_err(&pdev->dev, "failed to claim hwvad event irq %u\n",
118829dbfeecSShengjiu Wang 			micfil->irq[0]);
118929dbfeecSShengjiu Wang 		return ret;
119029dbfeecSShengjiu Wang 	}
119129dbfeecSShengjiu Wang 
119229dbfeecSShengjiu Wang 	/* Digital Microphone interface voice activity detector error */
119329dbfeecSShengjiu Wang 	ret = devm_request_irq(&pdev->dev, micfil->irq[3],
119429dbfeecSShengjiu Wang 			       hwvad_err_isr, IRQF_SHARED,
119529dbfeecSShengjiu Wang 			       micfil->name, micfil);
119629dbfeecSShengjiu Wang 	if (ret) {
119729dbfeecSShengjiu Wang 		dev_err(&pdev->dev, "failed to claim hwvad error irq %u\n",
119829dbfeecSShengjiu Wang 			micfil->irq[1]);
119929dbfeecSShengjiu Wang 		return ret;
120029dbfeecSShengjiu Wang 	}
120129dbfeecSShengjiu Wang 
120247a70e6fSCosmin Samoila 	micfil->dma_params_rx.chan_name = "rx";
120347a70e6fSCosmin Samoila 	micfil->dma_params_rx.addr = res->start + REG_MICFIL_DATACH0;
120447a70e6fSCosmin Samoila 	micfil->dma_params_rx.maxburst = MICFIL_DMA_MAXBURST_RX;
120547a70e6fSCosmin Samoila 
120647a70e6fSCosmin Samoila 	platform_set_drvdata(pdev, micfil);
120747a70e6fSCosmin Samoila 
120847a70e6fSCosmin Samoila 	pm_runtime_enable(&pdev->dev);
120936736505SChancel Liu 	if (!pm_runtime_enabled(&pdev->dev)) {
121036736505SChancel Liu 		ret = fsl_micfil_runtime_resume(&pdev->dev);
121136736505SChancel Liu 		if (ret)
121236736505SChancel Liu 			goto err_pm_disable;
121336736505SChancel Liu 	}
121436736505SChancel Liu 
121536736505SChancel Liu 	ret = pm_runtime_resume_and_get(&pdev->dev);
121636736505SChancel Liu 	if (ret < 0)
121736736505SChancel Liu 		goto err_pm_get_sync;
121836736505SChancel Liu 
121936736505SChancel Liu 	/* Get micfil version */
122036736505SChancel Liu 	ret = fsl_micfil_use_verid(&pdev->dev);
122136736505SChancel Liu 	if (ret < 0)
122236736505SChancel Liu 		dev_warn(&pdev->dev, "Error reading MICFIL version: %d\n", ret);
122336736505SChancel Liu 
122436736505SChancel Liu 	ret = pm_runtime_put_sync(&pdev->dev);
122536736505SChancel Liu 	if (ret < 0 && ret != -ENOSYS)
122636736505SChancel Liu 		goto err_pm_get_sync;
122736736505SChancel Liu 
1228b5cf28f7SShengjiu Wang 	regcache_cache_only(micfil->regmap, true);
122947a70e6fSCosmin Samoila 
12300adf2920SShengjiu Wang 	/*
12310adf2920SShengjiu Wang 	 * Register platform component before registering cpu dai for there
12320adf2920SShengjiu Wang 	 * is not defer probe for platform component in snd_soc_add_pcm_runtime().
12330adf2920SShengjiu Wang 	 */
12340adf2920SShengjiu Wang 	ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
12350adf2920SShengjiu Wang 	if (ret) {
12360adf2920SShengjiu Wang 		dev_err(&pdev->dev, "failed to pcm register\n");
123717955abaSShengjiu Wang 		goto err_pm_disable;
12380adf2920SShengjiu Wang 	}
12390adf2920SShengjiu Wang 
1240cb05dac1SShengjiu Wang 	fsl_micfil_dai.capture.formats = micfil->soc->formats;
1241cb05dac1SShengjiu Wang 
124247a70e6fSCosmin Samoila 	ret = devm_snd_soc_register_component(&pdev->dev, &fsl_micfil_component,
124347a70e6fSCosmin Samoila 					      &fsl_micfil_dai, 1);
124447a70e6fSCosmin Samoila 	if (ret) {
124547a70e6fSCosmin Samoila 		dev_err(&pdev->dev, "failed to register component %s\n",
124647a70e6fSCosmin Samoila 			fsl_micfil_component.name);
124717955abaSShengjiu Wang 		goto err_pm_disable;
124847a70e6fSCosmin Samoila 	}
124947a70e6fSCosmin Samoila 
125047a70e6fSCosmin Samoila 	return ret;
125117955abaSShengjiu Wang 
125236736505SChancel Liu err_pm_get_sync:
125336736505SChancel Liu 	if (!pm_runtime_status_suspended(&pdev->dev))
125436736505SChancel Liu 		fsl_micfil_runtime_suspend(&pdev->dev);
125517955abaSShengjiu Wang err_pm_disable:
125617955abaSShengjiu Wang 	pm_runtime_disable(&pdev->dev);
125717955abaSShengjiu Wang 
125817955abaSShengjiu Wang 	return ret;
125917955abaSShengjiu Wang }
126017955abaSShengjiu Wang 
126117955abaSShengjiu Wang static void fsl_micfil_remove(struct platform_device *pdev)
126217955abaSShengjiu Wang {
126317955abaSShengjiu Wang 	pm_runtime_disable(&pdev->dev);
126447a70e6fSCosmin Samoila }
126547a70e6fSCosmin Samoila 
126636736505SChancel Liu static int fsl_micfil_runtime_suspend(struct device *dev)
126747a70e6fSCosmin Samoila {
126847a70e6fSCosmin Samoila 	struct fsl_micfil *micfil = dev_get_drvdata(dev);
126947a70e6fSCosmin Samoila 
127047a70e6fSCosmin Samoila 	regcache_cache_only(micfil->regmap, true);
127147a70e6fSCosmin Samoila 
127247a70e6fSCosmin Samoila 	clk_disable_unprepare(micfil->mclk);
1273b5cf28f7SShengjiu Wang 	clk_disable_unprepare(micfil->busclk);
127447a70e6fSCosmin Samoila 
127547a70e6fSCosmin Samoila 	return 0;
127647a70e6fSCosmin Samoila }
127747a70e6fSCosmin Samoila 
127836736505SChancel Liu static int fsl_micfil_runtime_resume(struct device *dev)
127947a70e6fSCosmin Samoila {
128047a70e6fSCosmin Samoila 	struct fsl_micfil *micfil = dev_get_drvdata(dev);
128147a70e6fSCosmin Samoila 	int ret;
128247a70e6fSCosmin Samoila 
1283b5cf28f7SShengjiu Wang 	ret = clk_prepare_enable(micfil->busclk);
128447a70e6fSCosmin Samoila 	if (ret < 0)
128547a70e6fSCosmin Samoila 		return ret;
128647a70e6fSCosmin Samoila 
1287b5cf28f7SShengjiu Wang 	ret = clk_prepare_enable(micfil->mclk);
1288b5cf28f7SShengjiu Wang 	if (ret < 0) {
1289b5cf28f7SShengjiu Wang 		clk_disable_unprepare(micfil->busclk);
1290b5cf28f7SShengjiu Wang 		return ret;
1291b5cf28f7SShengjiu Wang 	}
1292b5cf28f7SShengjiu Wang 
129347a70e6fSCosmin Samoila 	regcache_cache_only(micfil->regmap, false);
129447a70e6fSCosmin Samoila 	regcache_mark_dirty(micfil->regmap);
129547a70e6fSCosmin Samoila 	regcache_sync(micfil->regmap);
129647a70e6fSCosmin Samoila 
129747a70e6fSCosmin Samoila 	return 0;
129847a70e6fSCosmin Samoila }
129947a70e6fSCosmin Samoila 
130047a70e6fSCosmin Samoila static const struct dev_pm_ops fsl_micfil_pm_ops = {
130147a70e6fSCosmin Samoila 	SET_RUNTIME_PM_OPS(fsl_micfil_runtime_suspend,
130247a70e6fSCosmin Samoila 			   fsl_micfil_runtime_resume,
130347a70e6fSCosmin Samoila 			   NULL)
1304*a38a4090SChancel Liu 	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
1305*a38a4090SChancel Liu 				pm_runtime_force_resume)
130647a70e6fSCosmin Samoila };
130747a70e6fSCosmin Samoila 
130847a70e6fSCosmin Samoila static struct platform_driver fsl_micfil_driver = {
130947a70e6fSCosmin Samoila 	.probe = fsl_micfil_probe,
131017955abaSShengjiu Wang 	.remove_new = fsl_micfil_remove,
131147a70e6fSCosmin Samoila 	.driver = {
131247a70e6fSCosmin Samoila 		.name = "fsl-micfil-dai",
131347a70e6fSCosmin Samoila 		.pm = &fsl_micfil_pm_ops,
131447a70e6fSCosmin Samoila 		.of_match_table = fsl_micfil_dt_ids,
131547a70e6fSCosmin Samoila 	},
131647a70e6fSCosmin Samoila };
131747a70e6fSCosmin Samoila module_platform_driver(fsl_micfil_driver);
131847a70e6fSCosmin Samoila 
131947a70e6fSCosmin Samoila MODULE_AUTHOR("Cosmin-Gabriel Samoila <cosmin.samoila@nxp.com>");
132047a70e6fSCosmin Samoila MODULE_DESCRIPTION("NXP PDM Microphone Interface (MICFIL) driver");
132147a70e6fSCosmin Samoila MODULE_LICENSE("GPL v2");
1322