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; 5947a70e6fSCosmin Samoila }; 6047a70e6fSCosmin Samoila 6147a70e6fSCosmin Samoila struct fsl_micfil_soc_data { 6247a70e6fSCosmin Samoila unsigned int fifos; 6347a70e6fSCosmin Samoila unsigned int fifo_depth; 6447a70e6fSCosmin Samoila unsigned int dataline; 6547a70e6fSCosmin Samoila bool imx; 6677a7a6e9SChancel Liu bool use_edma; 67cb05dac1SShengjiu Wang u64 formats; 6847a70e6fSCosmin Samoila }; 6947a70e6fSCosmin Samoila 7047a70e6fSCosmin Samoila static struct fsl_micfil_soc_data fsl_micfil_imx8mm = { 7147a70e6fSCosmin Samoila .imx = true, 7247a70e6fSCosmin Samoila .fifos = 8, 7347a70e6fSCosmin Samoila .fifo_depth = 8, 7447a70e6fSCosmin Samoila .dataline = 0xf, 75cb05dac1SShengjiu Wang .formats = SNDRV_PCM_FMTBIT_S16_LE, 76cb05dac1SShengjiu Wang }; 77cb05dac1SShengjiu Wang 78cb05dac1SShengjiu Wang static struct fsl_micfil_soc_data fsl_micfil_imx8mp = { 79cb05dac1SShengjiu Wang .imx = true, 80cb05dac1SShengjiu Wang .fifos = 8, 81cb05dac1SShengjiu Wang .fifo_depth = 32, 82cb05dac1SShengjiu Wang .dataline = 0xf, 83cb05dac1SShengjiu Wang .formats = SNDRV_PCM_FMTBIT_S32_LE, 8447a70e6fSCosmin Samoila }; 8547a70e6fSCosmin Samoila 86a10a5254SChancel Liu static struct fsl_micfil_soc_data fsl_micfil_imx93 = { 87a10a5254SChancel Liu .imx = true, 88a10a5254SChancel Liu .fifos = 8, 89a10a5254SChancel Liu .fifo_depth = 32, 90a10a5254SChancel Liu .dataline = 0xf, 91a10a5254SChancel Liu .formats = SNDRV_PCM_FMTBIT_S32_LE, 9277a7a6e9SChancel Liu .use_edma = true, 93a10a5254SChancel Liu }; 94a10a5254SChancel Liu 9547a70e6fSCosmin Samoila static const struct of_device_id fsl_micfil_dt_ids[] = { 9647a70e6fSCosmin Samoila { .compatible = "fsl,imx8mm-micfil", .data = &fsl_micfil_imx8mm }, 97cb05dac1SShengjiu Wang { .compatible = "fsl,imx8mp-micfil", .data = &fsl_micfil_imx8mp }, 98a10a5254SChancel Liu { .compatible = "fsl,imx93-micfil", .data = &fsl_micfil_imx93 }, 9947a70e6fSCosmin Samoila {} 10047a70e6fSCosmin Samoila }; 10147a70e6fSCosmin Samoila MODULE_DEVICE_TABLE(of, fsl_micfil_dt_ids); 10247a70e6fSCosmin Samoila 10347a70e6fSCosmin Samoila static const char * const micfil_quality_select_texts[] = { 104bea1d61dSSascha Hauer [QUALITY_HIGH] = "High", 105bea1d61dSSascha Hauer [QUALITY_MEDIUM] = "Medium", 106bea1d61dSSascha Hauer [QUALITY_LOW] = "Low", 107bea1d61dSSascha Hauer [QUALITY_VLOW0] = "VLow0", 108bea1d61dSSascha Hauer [QUALITY_VLOW1] = "Vlow1", 109bea1d61dSSascha Hauer [QUALITY_VLOW2] = "Vlow2", 11047a70e6fSCosmin Samoila }; 11147a70e6fSCosmin Samoila 11247a70e6fSCosmin Samoila static const struct soc_enum fsl_micfil_quality_enum = 113bea1d61dSSascha Hauer SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_quality_select_texts), 11447a70e6fSCosmin Samoila micfil_quality_select_texts); 11547a70e6fSCosmin Samoila 11647a70e6fSCosmin Samoila static DECLARE_TLV_DB_SCALE(gain_tlv, 0, 100, 0); 11747a70e6fSCosmin Samoila 118bea1d61dSSascha Hauer static int micfil_set_quality(struct fsl_micfil *micfil) 119bea1d61dSSascha Hauer { 120bea1d61dSSascha Hauer u32 qsel; 121bea1d61dSSascha Hauer 122bea1d61dSSascha Hauer switch (micfil->quality) { 123bea1d61dSSascha Hauer case QUALITY_HIGH: 124bea1d61dSSascha Hauer qsel = MICFIL_QSEL_HIGH_QUALITY; 125bea1d61dSSascha Hauer break; 126bea1d61dSSascha Hauer case QUALITY_MEDIUM: 127bea1d61dSSascha Hauer qsel = MICFIL_QSEL_MEDIUM_QUALITY; 128bea1d61dSSascha Hauer break; 129bea1d61dSSascha Hauer case QUALITY_LOW: 130bea1d61dSSascha Hauer qsel = MICFIL_QSEL_LOW_QUALITY; 131bea1d61dSSascha Hauer break; 132bea1d61dSSascha Hauer case QUALITY_VLOW0: 133bea1d61dSSascha Hauer qsel = MICFIL_QSEL_VLOW0_QUALITY; 134bea1d61dSSascha Hauer break; 135bea1d61dSSascha Hauer case QUALITY_VLOW1: 136bea1d61dSSascha Hauer qsel = MICFIL_QSEL_VLOW1_QUALITY; 137bea1d61dSSascha Hauer break; 138bea1d61dSSascha Hauer case QUALITY_VLOW2: 139bea1d61dSSascha Hauer qsel = MICFIL_QSEL_VLOW2_QUALITY; 140bea1d61dSSascha Hauer break; 141bea1d61dSSascha Hauer } 142bea1d61dSSascha Hauer 143bea1d61dSSascha Hauer return regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2, 144bea1d61dSSascha Hauer MICFIL_CTRL2_QSEL, 145bea1d61dSSascha Hauer FIELD_PREP(MICFIL_CTRL2_QSEL, qsel)); 146bea1d61dSSascha Hauer } 147bea1d61dSSascha Hauer 148bea1d61dSSascha Hauer static int micfil_quality_get(struct snd_kcontrol *kcontrol, 149bea1d61dSSascha Hauer struct snd_ctl_elem_value *ucontrol) 150bea1d61dSSascha Hauer { 151bea1d61dSSascha Hauer struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); 152bea1d61dSSascha Hauer struct fsl_micfil *micfil = snd_soc_component_get_drvdata(cmpnt); 153bea1d61dSSascha Hauer 154bea1d61dSSascha Hauer ucontrol->value.integer.value[0] = micfil->quality; 155bea1d61dSSascha Hauer 156bea1d61dSSascha Hauer return 0; 157bea1d61dSSascha Hauer } 158bea1d61dSSascha Hauer 159bea1d61dSSascha Hauer static int micfil_quality_set(struct snd_kcontrol *kcontrol, 160bea1d61dSSascha Hauer struct snd_ctl_elem_value *ucontrol) 161bea1d61dSSascha Hauer { 162bea1d61dSSascha Hauer struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); 163bea1d61dSSascha Hauer struct fsl_micfil *micfil = snd_soc_component_get_drvdata(cmpnt); 164bea1d61dSSascha Hauer 165bea1d61dSSascha Hauer micfil->quality = ucontrol->value.integer.value[0]; 166bea1d61dSSascha Hauer 167bea1d61dSSascha Hauer return micfil_set_quality(micfil); 168bea1d61dSSascha Hauer } 169bea1d61dSSascha Hauer 17029dbfeecSShengjiu Wang static const char * const micfil_hwvad_enable[] = { 17129dbfeecSShengjiu Wang "Disable (Record only)", 17229dbfeecSShengjiu Wang "Enable (Record with Vad)", 17329dbfeecSShengjiu Wang }; 17429dbfeecSShengjiu Wang 17529dbfeecSShengjiu Wang static const char * const micfil_hwvad_init_mode[] = { 17629dbfeecSShengjiu Wang "Envelope mode", "Energy mode", 17729dbfeecSShengjiu Wang }; 17829dbfeecSShengjiu Wang 17929dbfeecSShengjiu Wang static const char * const micfil_hwvad_hpf_texts[] = { 18029dbfeecSShengjiu Wang "Filter bypass", 18129dbfeecSShengjiu Wang "Cut-off @1750Hz", 18229dbfeecSShengjiu Wang "Cut-off @215Hz", 18329dbfeecSShengjiu Wang "Cut-off @102Hz", 18429dbfeecSShengjiu Wang }; 18529dbfeecSShengjiu Wang 18629dbfeecSShengjiu Wang /* 18729dbfeecSShengjiu Wang * DC Remover Control 18829dbfeecSShengjiu Wang * Filter Bypassed 1 1 18929dbfeecSShengjiu Wang * Cut-off @21Hz 0 0 19029dbfeecSShengjiu Wang * Cut-off @83Hz 0 1 19129dbfeecSShengjiu Wang * Cut-off @152HZ 1 0 19229dbfeecSShengjiu Wang */ 19329dbfeecSShengjiu Wang static const char * const micfil_dc_remover_texts[] = { 19429dbfeecSShengjiu Wang "Cut-off @21Hz", "Cut-off @83Hz", 19529dbfeecSShengjiu Wang "Cut-off @152Hz", "Bypass", 19629dbfeecSShengjiu Wang }; 19729dbfeecSShengjiu Wang 19829dbfeecSShengjiu Wang static const struct soc_enum hwvad_enable_enum = 19929dbfeecSShengjiu Wang SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_hwvad_enable), 20029dbfeecSShengjiu Wang micfil_hwvad_enable); 20129dbfeecSShengjiu Wang static const struct soc_enum hwvad_init_mode_enum = 20229dbfeecSShengjiu Wang SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_hwvad_init_mode), 20329dbfeecSShengjiu Wang micfil_hwvad_init_mode); 20429dbfeecSShengjiu Wang static const struct soc_enum hwvad_hpf_enum = 20529dbfeecSShengjiu Wang SOC_ENUM_SINGLE(REG_MICFIL_VAD0_CTRL2, 0, 20629dbfeecSShengjiu Wang ARRAY_SIZE(micfil_hwvad_hpf_texts), 20729dbfeecSShengjiu Wang micfil_hwvad_hpf_texts); 20829dbfeecSShengjiu Wang static const struct soc_enum fsl_micfil_dc_remover_enum = 20929dbfeecSShengjiu Wang SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_dc_remover_texts), 21029dbfeecSShengjiu Wang micfil_dc_remover_texts); 21129dbfeecSShengjiu Wang 21229dbfeecSShengjiu Wang static int micfil_put_dc_remover_state(struct snd_kcontrol *kcontrol, 21329dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 21429dbfeecSShengjiu Wang { 21529dbfeecSShengjiu Wang struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 21629dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 21729dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 21829dbfeecSShengjiu Wang unsigned int *item = ucontrol->value.enumerated.item; 21929dbfeecSShengjiu Wang int val = snd_soc_enum_item_to_val(e, item[0]); 22029dbfeecSShengjiu Wang int i = 0, ret = 0; 22129dbfeecSShengjiu Wang u32 reg_val = 0; 22229dbfeecSShengjiu Wang 22329dbfeecSShengjiu Wang if (val < 0 || val > 3) 22429dbfeecSShengjiu Wang return -EINVAL; 22529dbfeecSShengjiu Wang 22629dbfeecSShengjiu Wang micfil->dc_remover = val; 22729dbfeecSShengjiu Wang 22829dbfeecSShengjiu Wang /* Calculate total value for all channels */ 22929dbfeecSShengjiu Wang for (i = 0; i < MICFIL_OUTPUT_CHANNELS; i++) 23029dbfeecSShengjiu Wang reg_val |= val << MICFIL_DC_CHX_SHIFT(i); 23129dbfeecSShengjiu Wang 23229dbfeecSShengjiu Wang /* Update DC Remover mode for all channels */ 23329dbfeecSShengjiu Wang ret = snd_soc_component_update_bits(comp, REG_MICFIL_DC_CTRL, 23429dbfeecSShengjiu Wang MICFIL_DC_CTRL_CONFIG, reg_val); 23529dbfeecSShengjiu Wang if (ret < 0) 23629dbfeecSShengjiu Wang return ret; 23729dbfeecSShengjiu Wang 23829dbfeecSShengjiu Wang return 0; 23929dbfeecSShengjiu Wang } 24029dbfeecSShengjiu Wang 24129dbfeecSShengjiu Wang static int micfil_get_dc_remover_state(struct snd_kcontrol *kcontrol, 24229dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 24329dbfeecSShengjiu Wang { 24429dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 24529dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 24629dbfeecSShengjiu Wang 24729dbfeecSShengjiu Wang ucontrol->value.enumerated.item[0] = micfil->dc_remover; 24829dbfeecSShengjiu Wang 24929dbfeecSShengjiu Wang return 0; 25029dbfeecSShengjiu Wang } 25129dbfeecSShengjiu Wang 25229dbfeecSShengjiu Wang static int hwvad_put_enable(struct snd_kcontrol *kcontrol, 25329dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 25429dbfeecSShengjiu Wang { 25529dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 25629dbfeecSShengjiu Wang struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 25729dbfeecSShengjiu Wang unsigned int *item = ucontrol->value.enumerated.item; 25829dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 25929dbfeecSShengjiu Wang int val = snd_soc_enum_item_to_val(e, item[0]); 26029dbfeecSShengjiu Wang 26129dbfeecSShengjiu Wang micfil->vad_enabled = val; 26229dbfeecSShengjiu Wang 26329dbfeecSShengjiu Wang return 0; 26429dbfeecSShengjiu Wang } 26529dbfeecSShengjiu Wang 26629dbfeecSShengjiu Wang static int hwvad_get_enable(struct snd_kcontrol *kcontrol, 26729dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 26829dbfeecSShengjiu Wang { 26929dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 27029dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 27129dbfeecSShengjiu Wang 27229dbfeecSShengjiu Wang ucontrol->value.enumerated.item[0] = micfil->vad_enabled; 27329dbfeecSShengjiu Wang 27429dbfeecSShengjiu Wang return 0; 27529dbfeecSShengjiu Wang } 27629dbfeecSShengjiu Wang 27729dbfeecSShengjiu Wang static int hwvad_put_init_mode(struct snd_kcontrol *kcontrol, 27829dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 27929dbfeecSShengjiu Wang { 28029dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 28129dbfeecSShengjiu Wang struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 28229dbfeecSShengjiu Wang unsigned int *item = ucontrol->value.enumerated.item; 28329dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 28429dbfeecSShengjiu Wang int val = snd_soc_enum_item_to_val(e, item[0]); 28529dbfeecSShengjiu Wang 28629dbfeecSShengjiu Wang /* 0 - Envelope-based Mode 28729dbfeecSShengjiu Wang * 1 - Energy-based Mode 28829dbfeecSShengjiu Wang */ 28929dbfeecSShengjiu Wang micfil->vad_init_mode = val; 29029dbfeecSShengjiu Wang 29129dbfeecSShengjiu Wang return 0; 29229dbfeecSShengjiu Wang } 29329dbfeecSShengjiu Wang 29429dbfeecSShengjiu Wang static int hwvad_get_init_mode(struct snd_kcontrol *kcontrol, 29529dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 29629dbfeecSShengjiu Wang { 29729dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 29829dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 29929dbfeecSShengjiu Wang 30029dbfeecSShengjiu Wang ucontrol->value.enumerated.item[0] = micfil->vad_init_mode; 30129dbfeecSShengjiu Wang 30229dbfeecSShengjiu Wang return 0; 30329dbfeecSShengjiu Wang } 30429dbfeecSShengjiu Wang 30529dbfeecSShengjiu Wang static int hwvad_detected(struct snd_kcontrol *kcontrol, 30629dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 30729dbfeecSShengjiu Wang { 30829dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 30929dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 31029dbfeecSShengjiu Wang 31129dbfeecSShengjiu Wang ucontrol->value.enumerated.item[0] = micfil->vad_detected; 31229dbfeecSShengjiu Wang 31329dbfeecSShengjiu Wang return 0; 31429dbfeecSShengjiu Wang } 31529dbfeecSShengjiu Wang 31647a70e6fSCosmin Samoila static const struct snd_kcontrol_new fsl_micfil_snd_controls[] = { 31747a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH0 Volume", REG_MICFIL_OUT_CTRL, 318*cdfa92ebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(0), 0x8, 0xF, gain_tlv), 31947a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH1 Volume", REG_MICFIL_OUT_CTRL, 320*cdfa92ebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(1), 0x8, 0xF, gain_tlv), 32147a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH2 Volume", REG_MICFIL_OUT_CTRL, 322*cdfa92ebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(2), 0x8, 0xF, gain_tlv), 32347a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH3 Volume", REG_MICFIL_OUT_CTRL, 324*cdfa92ebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(3), 0x8, 0xF, gain_tlv), 32547a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH4 Volume", REG_MICFIL_OUT_CTRL, 326*cdfa92ebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(4), 0x8, 0xF, gain_tlv), 32747a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH5 Volume", REG_MICFIL_OUT_CTRL, 328*cdfa92ebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(5), 0x8, 0xF, gain_tlv), 32947a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH6 Volume", REG_MICFIL_OUT_CTRL, 330*cdfa92ebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(6), 0x8, 0xF, gain_tlv), 33147a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH7 Volume", REG_MICFIL_OUT_CTRL, 332*cdfa92ebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(7), 0x8, 0xF, gain_tlv), 33347a70e6fSCosmin Samoila SOC_ENUM_EXT("MICFIL Quality Select", 33447a70e6fSCosmin Samoila fsl_micfil_quality_enum, 335bea1d61dSSascha Hauer micfil_quality_get, micfil_quality_set), 33629dbfeecSShengjiu Wang SOC_ENUM_EXT("HWVAD Enablement Switch", hwvad_enable_enum, 33729dbfeecSShengjiu Wang hwvad_get_enable, hwvad_put_enable), 33829dbfeecSShengjiu Wang SOC_ENUM_EXT("HWVAD Initialization Mode", hwvad_init_mode_enum, 33929dbfeecSShengjiu Wang hwvad_get_init_mode, hwvad_put_init_mode), 34029dbfeecSShengjiu Wang SOC_ENUM("HWVAD High-Pass Filter", hwvad_hpf_enum), 34129dbfeecSShengjiu Wang SOC_SINGLE("HWVAD ZCD Switch", REG_MICFIL_VAD0_ZCD, 0, 1, 0), 34229dbfeecSShengjiu Wang SOC_SINGLE("HWVAD ZCD Auto Threshold Switch", 34329dbfeecSShengjiu Wang REG_MICFIL_VAD0_ZCD, 2, 1, 0), 34429dbfeecSShengjiu Wang SOC_ENUM_EXT("MICFIL DC Remover Control", fsl_micfil_dc_remover_enum, 34529dbfeecSShengjiu Wang micfil_get_dc_remover_state, micfil_put_dc_remover_state), 34629dbfeecSShengjiu Wang SOC_SINGLE("HWVAD Input Gain", REG_MICFIL_VAD0_CTRL2, 8, 15, 0), 34729dbfeecSShengjiu Wang SOC_SINGLE("HWVAD Sound Gain", REG_MICFIL_VAD0_SCONFIG, 0, 15, 0), 34829dbfeecSShengjiu Wang SOC_SINGLE("HWVAD Noise Gain", REG_MICFIL_VAD0_NCONFIG, 0, 15, 0), 34929dbfeecSShengjiu Wang SOC_SINGLE_RANGE("HWVAD Detector Frame Time", REG_MICFIL_VAD0_CTRL2, 16, 0, 63, 0), 35029dbfeecSShengjiu Wang SOC_SINGLE("HWVAD Detector Initialization Time", REG_MICFIL_VAD0_CTRL1, 8, 31, 0), 35129dbfeecSShengjiu Wang SOC_SINGLE("HWVAD Noise Filter Adjustment", REG_MICFIL_VAD0_NCONFIG, 8, 31, 0), 35229dbfeecSShengjiu Wang SOC_SINGLE("HWVAD ZCD Threshold", REG_MICFIL_VAD0_ZCD, 16, 1023, 0), 35329dbfeecSShengjiu Wang SOC_SINGLE("HWVAD ZCD Adjustment", REG_MICFIL_VAD0_ZCD, 8, 15, 0), 35429dbfeecSShengjiu Wang SOC_SINGLE("HWVAD ZCD And Behavior Switch", 35529dbfeecSShengjiu Wang REG_MICFIL_VAD0_ZCD, 4, 1, 0), 35629dbfeecSShengjiu Wang SOC_SINGLE_BOOL_EXT("VAD Detected", 0, hwvad_detected, NULL), 35747a70e6fSCosmin Samoila }; 35847a70e6fSCosmin Samoila 35947a70e6fSCosmin Samoila /* The SRES is a self-negated bit which provides the CPU with the 36047a70e6fSCosmin Samoila * capability to initialize the PDM Interface module through the 36147a70e6fSCosmin Samoila * slave-bus interface. This bit always reads as zero, and this 36247a70e6fSCosmin Samoila * bit is only effective when MDIS is cleared 36347a70e6fSCosmin Samoila */ 36447a70e6fSCosmin Samoila static int fsl_micfil_reset(struct device *dev) 36547a70e6fSCosmin Samoila { 36647a70e6fSCosmin Samoila struct fsl_micfil *micfil = dev_get_drvdata(dev); 36747a70e6fSCosmin Samoila int ret; 36847a70e6fSCosmin Samoila 369d46c2127SSascha Hauer ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1, 370d46c2127SSascha Hauer MICFIL_CTRL1_MDIS); 3712c602c7eSSascha Hauer if (ret) 37247a70e6fSCosmin Samoila return ret; 37347a70e6fSCosmin Samoila 374d46c2127SSascha Hauer ret = regmap_set_bits(micfil->regmap, REG_MICFIL_CTRL1, 37547a70e6fSCosmin Samoila MICFIL_CTRL1_SRES); 3762c602c7eSSascha Hauer if (ret) 37747a70e6fSCosmin Samoila return ret; 37847a70e6fSCosmin Samoila 379292709b9SShengjiu Wang /* 380292709b9SShengjiu Wang * SRES is self-cleared bit, but REG_MICFIL_CTRL1 is defined 381292709b9SShengjiu Wang * as non-volatile register, so SRES still remain in regmap 382292709b9SShengjiu Wang * cache after set, that every update of REG_MICFIL_CTRL1, 383292709b9SShengjiu Wang * software reset happens. so clear it explicitly. 384292709b9SShengjiu Wang */ 385292709b9SShengjiu Wang ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1, 386292709b9SShengjiu Wang MICFIL_CTRL1_SRES); 387292709b9SShengjiu Wang if (ret) 388292709b9SShengjiu Wang return ret; 389292709b9SShengjiu Wang 390b776c4a4SShengjiu Wang /* 391b776c4a4SShengjiu Wang * Set SRES should clear CHnF flags, But even add delay here 392b776c4a4SShengjiu Wang * the CHnF may not be cleared sometimes, so clear CHnF explicitly. 393b776c4a4SShengjiu Wang */ 394b776c4a4SShengjiu Wang ret = regmap_write_bits(micfil->regmap, REG_MICFIL_STAT, 0xFF, 0xFF); 395b776c4a4SShengjiu Wang if (ret) 396b776c4a4SShengjiu Wang return ret; 397b776c4a4SShengjiu Wang 39847a70e6fSCosmin Samoila return 0; 39947a70e6fSCosmin Samoila } 40047a70e6fSCosmin Samoila 40147a70e6fSCosmin Samoila static int fsl_micfil_startup(struct snd_pcm_substream *substream, 40247a70e6fSCosmin Samoila struct snd_soc_dai *dai) 40347a70e6fSCosmin Samoila { 40447a70e6fSCosmin Samoila struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai); 40547a70e6fSCosmin Samoila 40647a70e6fSCosmin Samoila if (!micfil) { 40711106cb3STang Bin dev_err(dai->dev, "micfil dai priv_data not set\n"); 40847a70e6fSCosmin Samoila return -EINVAL; 40947a70e6fSCosmin Samoila } 41047a70e6fSCosmin Samoila 41147a70e6fSCosmin Samoila return 0; 41247a70e6fSCosmin Samoila } 41347a70e6fSCosmin Samoila 41429dbfeecSShengjiu Wang /* Enable/disable hwvad interrupts */ 41529dbfeecSShengjiu Wang static int fsl_micfil_configure_hwvad_interrupts(struct fsl_micfil *micfil, int enable) 41629dbfeecSShengjiu Wang { 41729dbfeecSShengjiu Wang u32 vadie_reg = enable ? MICFIL_VAD0_CTRL1_IE : 0; 41829dbfeecSShengjiu Wang u32 vaderie_reg = enable ? MICFIL_VAD0_CTRL1_ERIE : 0; 41929dbfeecSShengjiu Wang 42029dbfeecSShengjiu Wang /* Voice Activity Detector Error Interruption */ 42129dbfeecSShengjiu Wang regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 42229dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_ERIE, vaderie_reg); 42329dbfeecSShengjiu Wang 42429dbfeecSShengjiu Wang /* Voice Activity Detector Interruption */ 42529dbfeecSShengjiu Wang regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 42629dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_IE, vadie_reg); 42729dbfeecSShengjiu Wang 42829dbfeecSShengjiu Wang return 0; 42929dbfeecSShengjiu Wang } 43029dbfeecSShengjiu Wang 43129dbfeecSShengjiu Wang /* Configuration done only in energy-based initialization mode */ 43229dbfeecSShengjiu Wang static int fsl_micfil_init_hwvad_energy_mode(struct fsl_micfil *micfil) 43329dbfeecSShengjiu Wang { 43429dbfeecSShengjiu Wang /* Keep the VADFRENDIS bitfield cleared. */ 43529dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2, 43629dbfeecSShengjiu Wang MICFIL_VAD0_CTRL2_FRENDIS); 43729dbfeecSShengjiu Wang 43829dbfeecSShengjiu Wang /* Keep the VADPREFEN bitfield cleared. */ 43929dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2, 44029dbfeecSShengjiu Wang MICFIL_VAD0_CTRL2_PREFEN); 44129dbfeecSShengjiu Wang 44229dbfeecSShengjiu Wang /* Keep the VADSFILEN bitfield cleared. */ 44329dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG, 44429dbfeecSShengjiu Wang MICFIL_VAD0_SCONFIG_SFILEN); 44529dbfeecSShengjiu Wang 44629dbfeecSShengjiu Wang /* Keep the VADSMAXEN bitfield cleared. */ 44729dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG, 44829dbfeecSShengjiu Wang MICFIL_VAD0_SCONFIG_SMAXEN); 44929dbfeecSShengjiu Wang 45029dbfeecSShengjiu Wang /* Keep the VADNFILAUTO bitfield asserted. */ 45129dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 45229dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NFILAUT); 45329dbfeecSShengjiu Wang 45429dbfeecSShengjiu Wang /* Keep the VADNMINEN bitfield cleared. */ 45529dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 45629dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NMINEN); 45729dbfeecSShengjiu Wang 45829dbfeecSShengjiu Wang /* Keep the VADNDECEN bitfield cleared. */ 45929dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 46029dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NDECEN); 46129dbfeecSShengjiu Wang 46229dbfeecSShengjiu Wang /* Keep the VADNOREN bitfield cleared. */ 46329dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 46429dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NOREN); 46529dbfeecSShengjiu Wang 46629dbfeecSShengjiu Wang return 0; 46729dbfeecSShengjiu Wang } 46829dbfeecSShengjiu Wang 46929dbfeecSShengjiu Wang /* Configuration done only in envelope-based initialization mode */ 47029dbfeecSShengjiu Wang static int fsl_micfil_init_hwvad_envelope_mode(struct fsl_micfil *micfil) 47129dbfeecSShengjiu Wang { 47229dbfeecSShengjiu Wang /* Assert the VADFRENDIS bitfield */ 47329dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2, 47429dbfeecSShengjiu Wang MICFIL_VAD0_CTRL2_FRENDIS); 47529dbfeecSShengjiu Wang 47629dbfeecSShengjiu Wang /* Assert the VADPREFEN bitfield. */ 47729dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2, 47829dbfeecSShengjiu Wang MICFIL_VAD0_CTRL2_PREFEN); 47929dbfeecSShengjiu Wang 48029dbfeecSShengjiu Wang /* Assert the VADSFILEN bitfield. */ 48129dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG, 48229dbfeecSShengjiu Wang MICFIL_VAD0_SCONFIG_SFILEN); 48329dbfeecSShengjiu Wang 48429dbfeecSShengjiu Wang /* Assert the VADSMAXEN bitfield. */ 48529dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG, 48629dbfeecSShengjiu Wang MICFIL_VAD0_SCONFIG_SMAXEN); 48729dbfeecSShengjiu Wang 48829dbfeecSShengjiu Wang /* Clear the VADNFILAUTO bitfield */ 48929dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 49029dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NFILAUT); 49129dbfeecSShengjiu Wang 49229dbfeecSShengjiu Wang /* Assert the VADNMINEN bitfield. */ 49329dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 49429dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NMINEN); 49529dbfeecSShengjiu Wang 49629dbfeecSShengjiu Wang /* Assert the VADNDECEN bitfield. */ 49729dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 49829dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NDECEN); 49929dbfeecSShengjiu Wang 50029dbfeecSShengjiu Wang /* Assert VADNOREN bitfield. */ 50129dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 50229dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NOREN); 50329dbfeecSShengjiu Wang 50429dbfeecSShengjiu Wang return 0; 50529dbfeecSShengjiu Wang } 50629dbfeecSShengjiu Wang 50729dbfeecSShengjiu Wang /* 50829dbfeecSShengjiu Wang * Hardware Voice Active Detection: The HWVAD takes data from the input 50929dbfeecSShengjiu Wang * of a selected PDM microphone to detect if there is any 51029dbfeecSShengjiu Wang * voice activity. When a voice activity is detected, an interrupt could 51129dbfeecSShengjiu Wang * be delivered to the system. Initialization in section 8.4: 51229dbfeecSShengjiu Wang * Can work in two modes: 51329dbfeecSShengjiu Wang * -> Eneveope-based mode (section 8.4.1) 51429dbfeecSShengjiu Wang * -> Energy-based mode (section 8.4.2) 51529dbfeecSShengjiu Wang * 51629dbfeecSShengjiu Wang * It is important to remark that the HWVAD detector could be enabled 51729dbfeecSShengjiu Wang * or reset only when the MICFIL isn't running i.e. when the BSY_FIL 51829dbfeecSShengjiu Wang * bit in STAT register is cleared 51929dbfeecSShengjiu Wang */ 52029dbfeecSShengjiu Wang static int fsl_micfil_hwvad_enable(struct fsl_micfil *micfil) 52129dbfeecSShengjiu Wang { 52229dbfeecSShengjiu Wang int ret; 52329dbfeecSShengjiu Wang 52429dbfeecSShengjiu Wang micfil->vad_detected = 0; 52529dbfeecSShengjiu Wang 52629dbfeecSShengjiu Wang /* envelope-based specific initialization */ 52729dbfeecSShengjiu Wang if (micfil->vad_init_mode == MICFIL_HWVAD_ENVELOPE_MODE) 52829dbfeecSShengjiu Wang ret = fsl_micfil_init_hwvad_envelope_mode(micfil); 52929dbfeecSShengjiu Wang else 53029dbfeecSShengjiu Wang ret = fsl_micfil_init_hwvad_energy_mode(micfil); 53129dbfeecSShengjiu Wang if (ret) 53229dbfeecSShengjiu Wang return ret; 53329dbfeecSShengjiu Wang 53429dbfeecSShengjiu Wang /* Voice Activity Detector Internal Filters Initialization*/ 53529dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 53629dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_ST10); 53729dbfeecSShengjiu Wang 53829dbfeecSShengjiu Wang /* Voice Activity Detector Internal Filter */ 53929dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 54029dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_ST10); 54129dbfeecSShengjiu Wang 54229dbfeecSShengjiu Wang /* Enable Interrupts */ 54329dbfeecSShengjiu Wang ret = fsl_micfil_configure_hwvad_interrupts(micfil, 1); 54429dbfeecSShengjiu Wang if (ret) 54529dbfeecSShengjiu Wang return ret; 54629dbfeecSShengjiu Wang 54729dbfeecSShengjiu Wang /* Voice Activity Detector Reset */ 54829dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 54929dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_RST); 55029dbfeecSShengjiu Wang 55129dbfeecSShengjiu Wang /* Voice Activity Detector Enabled */ 55229dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 55329dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_EN); 55429dbfeecSShengjiu Wang 55529dbfeecSShengjiu Wang return 0; 55629dbfeecSShengjiu Wang } 55729dbfeecSShengjiu Wang 55829dbfeecSShengjiu Wang static int fsl_micfil_hwvad_disable(struct fsl_micfil *micfil) 55929dbfeecSShengjiu Wang { 56029dbfeecSShengjiu Wang struct device *dev = &micfil->pdev->dev; 56129dbfeecSShengjiu Wang int ret = 0; 56229dbfeecSShengjiu Wang 56329dbfeecSShengjiu Wang /* Disable HWVAD */ 56429dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 56529dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_EN); 56629dbfeecSShengjiu Wang 56729dbfeecSShengjiu Wang /* Disable hwvad interrupts */ 56829dbfeecSShengjiu Wang ret = fsl_micfil_configure_hwvad_interrupts(micfil, 0); 56929dbfeecSShengjiu Wang if (ret) 57029dbfeecSShengjiu Wang dev_err(dev, "Failed to disable interrupts\n"); 57129dbfeecSShengjiu Wang 57229dbfeecSShengjiu Wang return ret; 57329dbfeecSShengjiu Wang } 57429dbfeecSShengjiu Wang 57547a70e6fSCosmin Samoila static int fsl_micfil_trigger(struct snd_pcm_substream *substream, int cmd, 57647a70e6fSCosmin Samoila struct snd_soc_dai *dai) 57747a70e6fSCosmin Samoila { 57847a70e6fSCosmin Samoila struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai); 57947a70e6fSCosmin Samoila struct device *dev = &micfil->pdev->dev; 58047a70e6fSCosmin Samoila int ret; 58147a70e6fSCosmin Samoila 58247a70e6fSCosmin Samoila switch (cmd) { 58347a70e6fSCosmin Samoila case SNDRV_PCM_TRIGGER_START: 58447a70e6fSCosmin Samoila case SNDRV_PCM_TRIGGER_RESUME: 58547a70e6fSCosmin Samoila case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 58647a70e6fSCosmin Samoila ret = fsl_micfil_reset(dev); 58747a70e6fSCosmin Samoila if (ret) { 58847a70e6fSCosmin Samoila dev_err(dev, "failed to soft reset\n"); 58947a70e6fSCosmin Samoila return ret; 59047a70e6fSCosmin Samoila } 59147a70e6fSCosmin Samoila 59247a70e6fSCosmin Samoila /* DMA Interrupt Selection - DISEL bits 59347a70e6fSCosmin Samoila * 00 - DMA and IRQ disabled 59447a70e6fSCosmin Samoila * 01 - DMA req enabled 59547a70e6fSCosmin Samoila * 10 - IRQ enabled 59647a70e6fSCosmin Samoila * 11 - reserved 59747a70e6fSCosmin Samoila */ 59847a70e6fSCosmin Samoila ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1, 59917f2142bSSascha Hauer MICFIL_CTRL1_DISEL, 60017f2142bSSascha Hauer FIELD_PREP(MICFIL_CTRL1_DISEL, MICFIL_CTRL1_DISEL_DMA)); 6012c602c7eSSascha Hauer if (ret) 60247a70e6fSCosmin Samoila return ret; 60347a70e6fSCosmin Samoila 60447a70e6fSCosmin Samoila /* Enable the module */ 605d46c2127SSascha Hauer ret = regmap_set_bits(micfil->regmap, REG_MICFIL_CTRL1, 60647a70e6fSCosmin Samoila MICFIL_CTRL1_PDMIEN); 6072c602c7eSSascha Hauer if (ret) 60847a70e6fSCosmin Samoila return ret; 60947a70e6fSCosmin Samoila 61029dbfeecSShengjiu Wang if (micfil->vad_enabled) 61129dbfeecSShengjiu Wang fsl_micfil_hwvad_enable(micfil); 61229dbfeecSShengjiu Wang 61347a70e6fSCosmin Samoila break; 61447a70e6fSCosmin Samoila case SNDRV_PCM_TRIGGER_STOP: 61547a70e6fSCosmin Samoila case SNDRV_PCM_TRIGGER_SUSPEND: 61647a70e6fSCosmin Samoila case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 61729dbfeecSShengjiu Wang if (micfil->vad_enabled) 61829dbfeecSShengjiu Wang fsl_micfil_hwvad_disable(micfil); 61929dbfeecSShengjiu Wang 62047a70e6fSCosmin Samoila /* Disable the module */ 621d46c2127SSascha Hauer ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1, 622d46c2127SSascha Hauer MICFIL_CTRL1_PDMIEN); 6232c602c7eSSascha Hauer if (ret) 62447a70e6fSCosmin Samoila return ret; 62547a70e6fSCosmin Samoila 62647a70e6fSCosmin Samoila ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1, 62717f2142bSSascha Hauer MICFIL_CTRL1_DISEL, 62817f2142bSSascha Hauer FIELD_PREP(MICFIL_CTRL1_DISEL, MICFIL_CTRL1_DISEL_DISABLE)); 6292c602c7eSSascha Hauer if (ret) 63047a70e6fSCosmin Samoila return ret; 63147a70e6fSCosmin Samoila break; 63247a70e6fSCosmin Samoila default: 63347a70e6fSCosmin Samoila return -EINVAL; 63447a70e6fSCosmin Samoila } 63547a70e6fSCosmin Samoila return 0; 63647a70e6fSCosmin Samoila } 63747a70e6fSCosmin Samoila 63893f54100SShengjiu Wang static int fsl_micfil_reparent_rootclk(struct fsl_micfil *micfil, unsigned int sample_rate) 63993f54100SShengjiu Wang { 64093f54100SShengjiu Wang struct device *dev = &micfil->pdev->dev; 64193f54100SShengjiu Wang u64 ratio = sample_rate; 64293f54100SShengjiu Wang struct clk *clk; 64393f54100SShengjiu Wang int ret; 64493f54100SShengjiu Wang 64593f54100SShengjiu Wang /* Get root clock */ 64693f54100SShengjiu Wang clk = micfil->mclk; 64793f54100SShengjiu Wang 64893f54100SShengjiu Wang /* Disable clock first, for it was enabled by pm_runtime */ 64993f54100SShengjiu Wang clk_disable_unprepare(clk); 65093f54100SShengjiu Wang fsl_asoc_reparent_pll_clocks(dev, clk, micfil->pll8k_clk, 65193f54100SShengjiu Wang micfil->pll11k_clk, ratio); 65293f54100SShengjiu Wang ret = clk_prepare_enable(clk); 65393f54100SShengjiu Wang if (ret) 65493f54100SShengjiu Wang return ret; 65593f54100SShengjiu Wang 65693f54100SShengjiu Wang return 0; 65793f54100SShengjiu Wang } 65893f54100SShengjiu Wang 65947a70e6fSCosmin Samoila static int fsl_micfil_hw_params(struct snd_pcm_substream *substream, 66047a70e6fSCosmin Samoila struct snd_pcm_hw_params *params, 66147a70e6fSCosmin Samoila struct snd_soc_dai *dai) 66247a70e6fSCosmin Samoila { 66347a70e6fSCosmin Samoila struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai); 66447a70e6fSCosmin Samoila unsigned int channels = params_channels(params); 66547a70e6fSCosmin Samoila unsigned int rate = params_rate(params); 666cc5ef57dSSascha Hauer int clk_div = 8; 667cc5ef57dSSascha Hauer int osr = MICFIL_OSR_DEFAULT; 66847a70e6fSCosmin Samoila int ret; 66947a70e6fSCosmin Samoila 67047a70e6fSCosmin Samoila /* 1. Disable the module */ 671d46c2127SSascha Hauer ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1, 672d46c2127SSascha Hauer MICFIL_CTRL1_PDMIEN); 6732c602c7eSSascha Hauer if (ret) 67447a70e6fSCosmin Samoila return ret; 67547a70e6fSCosmin Samoila 67647a70e6fSCosmin Samoila /* enable channels */ 67747a70e6fSCosmin Samoila ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1, 67847a70e6fSCosmin Samoila 0xFF, ((1 << channels) - 1)); 6792c602c7eSSascha Hauer if (ret) 68047a70e6fSCosmin Samoila return ret; 68147a70e6fSCosmin Samoila 68293f54100SShengjiu Wang ret = fsl_micfil_reparent_rootclk(micfil, rate); 68393f54100SShengjiu Wang if (ret) 68493f54100SShengjiu Wang return ret; 68593f54100SShengjiu Wang 686cc5ef57dSSascha Hauer ret = clk_set_rate(micfil->mclk, rate * clk_div * osr * 8); 687cc5ef57dSSascha Hauer if (ret) 68847a70e6fSCosmin Samoila return ret; 689cc5ef57dSSascha Hauer 690cc5ef57dSSascha Hauer ret = micfil_set_quality(micfil); 691cc5ef57dSSascha Hauer if (ret) 692cc5ef57dSSascha Hauer return ret; 693cc5ef57dSSascha Hauer 694cc5ef57dSSascha Hauer ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2, 695cc5ef57dSSascha Hauer MICFIL_CTRL2_CLKDIV | MICFIL_CTRL2_CICOSR, 696cc5ef57dSSascha Hauer FIELD_PREP(MICFIL_CTRL2_CLKDIV, clk_div) | 697cc5ef57dSSascha Hauer FIELD_PREP(MICFIL_CTRL2_CICOSR, 16 - osr)); 69847a70e6fSCosmin Samoila 69929dbfeecSShengjiu Wang /* Configure CIC OSR in VADCICOSR */ 70029dbfeecSShengjiu Wang regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 70129dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_CICOSR, 70229dbfeecSShengjiu Wang FIELD_PREP(MICFIL_VAD0_CTRL1_CICOSR, 16 - osr)); 70329dbfeecSShengjiu Wang 70429dbfeecSShengjiu Wang /* Configure source channel in VADCHSEL */ 70529dbfeecSShengjiu Wang regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 70629dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_CHSEL, 70729dbfeecSShengjiu Wang FIELD_PREP(MICFIL_VAD0_CTRL1_CHSEL, (channels - 1))); 70829dbfeecSShengjiu Wang 7092495ba26SSascha Hauer micfil->dma_params_rx.peripheral_config = &micfil->sdmacfg; 7102495ba26SSascha Hauer micfil->dma_params_rx.peripheral_size = sizeof(micfil->sdmacfg); 7112495ba26SSascha Hauer micfil->sdmacfg.n_fifos_src = channels; 7122495ba26SSascha Hauer micfil->sdmacfg.sw_done = true; 71347a70e6fSCosmin Samoila micfil->dma_params_rx.maxburst = channels * MICFIL_DMA_MAXBURST_RX; 71477a7a6e9SChancel Liu if (micfil->soc->use_edma) 71577a7a6e9SChancel Liu micfil->dma_params_rx.maxburst = channels; 71647a70e6fSCosmin Samoila 71747a70e6fSCosmin Samoila return 0; 71847a70e6fSCosmin Samoila } 71947a70e6fSCosmin Samoila 72038d89a56SRikard Falkeborn static const struct snd_soc_dai_ops fsl_micfil_dai_ops = { 72147a70e6fSCosmin Samoila .startup = fsl_micfil_startup, 72247a70e6fSCosmin Samoila .trigger = fsl_micfil_trigger, 72347a70e6fSCosmin Samoila .hw_params = fsl_micfil_hw_params, 72447a70e6fSCosmin Samoila }; 72547a70e6fSCosmin Samoila 72647a70e6fSCosmin Samoila static int fsl_micfil_dai_probe(struct snd_soc_dai *cpu_dai) 72747a70e6fSCosmin Samoila { 72847a70e6fSCosmin Samoila struct fsl_micfil *micfil = dev_get_drvdata(cpu_dai->dev); 7293b13b143SShengjiu Wang struct device *dev = cpu_dai->dev; 7303b13b143SShengjiu Wang unsigned int val = 0; 7313b13b143SShengjiu Wang int ret, i; 73247a70e6fSCosmin Samoila 7333b13b143SShengjiu Wang micfil->quality = QUALITY_VLOW0; 73429dbfeecSShengjiu Wang micfil->card = cpu_dai->component->card; 73547a70e6fSCosmin Samoila 7363b13b143SShengjiu Wang /* set default gain to 2 */ 7373b13b143SShengjiu Wang regmap_write(micfil->regmap, REG_MICFIL_OUT_CTRL, 0x22222222); 7383b13b143SShengjiu Wang 7393b13b143SShengjiu Wang /* set DC Remover in bypass mode*/ 7403b13b143SShengjiu Wang for (i = 0; i < MICFIL_OUTPUT_CHANNELS; i++) 7413b13b143SShengjiu Wang val |= MICFIL_DC_BYPASS << MICFIL_DC_CHX_SHIFT(i); 7423b13b143SShengjiu Wang ret = regmap_update_bits(micfil->regmap, REG_MICFIL_DC_CTRL, 7433b13b143SShengjiu Wang MICFIL_DC_CTRL_CONFIG, val); 7443b13b143SShengjiu Wang if (ret) { 7453b13b143SShengjiu Wang dev_err(dev, "failed to set DC Remover mode bits\n"); 7463b13b143SShengjiu Wang return ret; 7473b13b143SShengjiu Wang } 7483b13b143SShengjiu Wang micfil->dc_remover = MICFIL_DC_BYPASS; 74947a70e6fSCosmin Samoila 75047a70e6fSCosmin Samoila snd_soc_dai_init_dma_data(cpu_dai, NULL, 75147a70e6fSCosmin Samoila &micfil->dma_params_rx); 75247a70e6fSCosmin Samoila 75347a70e6fSCosmin Samoila /* FIFO Watermark Control - FIFOWMK*/ 75447a70e6fSCosmin Samoila ret = regmap_update_bits(micfil->regmap, REG_MICFIL_FIFO_CTRL, 75517f2142bSSascha Hauer MICFIL_FIFO_CTRL_FIFOWMK, 75617f2142bSSascha Hauer FIELD_PREP(MICFIL_FIFO_CTRL_FIFOWMK, micfil->soc->fifo_depth - 1)); 7572c602c7eSSascha Hauer if (ret) 75847a70e6fSCosmin Samoila return ret; 75947a70e6fSCosmin Samoila 76047a70e6fSCosmin Samoila return 0; 76147a70e6fSCosmin Samoila } 76247a70e6fSCosmin Samoila 76347a70e6fSCosmin Samoila static struct snd_soc_dai_driver fsl_micfil_dai = { 76447a70e6fSCosmin Samoila .probe = fsl_micfil_dai_probe, 76547a70e6fSCosmin Samoila .capture = { 76647a70e6fSCosmin Samoila .stream_name = "CPU-Capture", 76747a70e6fSCosmin Samoila .channels_min = 1, 76847a70e6fSCosmin Samoila .channels_max = 8, 76999c08cdbSSascha Hauer .rates = SNDRV_PCM_RATE_8000_48000, 77099c08cdbSSascha Hauer .formats = SNDRV_PCM_FMTBIT_S16_LE, 77147a70e6fSCosmin Samoila }, 77247a70e6fSCosmin Samoila .ops = &fsl_micfil_dai_ops, 77347a70e6fSCosmin Samoila }; 77447a70e6fSCosmin Samoila 77547a70e6fSCosmin Samoila static const struct snd_soc_component_driver fsl_micfil_component = { 77647a70e6fSCosmin Samoila .name = "fsl-micfil-dai", 77747a70e6fSCosmin Samoila .controls = fsl_micfil_snd_controls, 77847a70e6fSCosmin Samoila .num_controls = ARRAY_SIZE(fsl_micfil_snd_controls), 779978bd27cSShengjiu Wang .legacy_dai_naming = 1, 78047a70e6fSCosmin Samoila }; 78147a70e6fSCosmin Samoila 78247a70e6fSCosmin Samoila /* REGMAP */ 78347a70e6fSCosmin Samoila static const struct reg_default fsl_micfil_reg_defaults[] = { 78447a70e6fSCosmin Samoila {REG_MICFIL_CTRL1, 0x00000000}, 78547a70e6fSCosmin Samoila {REG_MICFIL_CTRL2, 0x00000000}, 78647a70e6fSCosmin Samoila {REG_MICFIL_STAT, 0x00000000}, 78747a70e6fSCosmin Samoila {REG_MICFIL_FIFO_CTRL, 0x00000007}, 78847a70e6fSCosmin Samoila {REG_MICFIL_FIFO_STAT, 0x00000000}, 78947a70e6fSCosmin Samoila {REG_MICFIL_DATACH0, 0x00000000}, 79047a70e6fSCosmin Samoila {REG_MICFIL_DATACH1, 0x00000000}, 79147a70e6fSCosmin Samoila {REG_MICFIL_DATACH2, 0x00000000}, 79247a70e6fSCosmin Samoila {REG_MICFIL_DATACH3, 0x00000000}, 79347a70e6fSCosmin Samoila {REG_MICFIL_DATACH4, 0x00000000}, 79447a70e6fSCosmin Samoila {REG_MICFIL_DATACH5, 0x00000000}, 79547a70e6fSCosmin Samoila {REG_MICFIL_DATACH6, 0x00000000}, 79647a70e6fSCosmin Samoila {REG_MICFIL_DATACH7, 0x00000000}, 79747a70e6fSCosmin Samoila {REG_MICFIL_DC_CTRL, 0x00000000}, 79847a70e6fSCosmin Samoila {REG_MICFIL_OUT_CTRL, 0x00000000}, 79947a70e6fSCosmin Samoila {REG_MICFIL_OUT_STAT, 0x00000000}, 80047a70e6fSCosmin Samoila {REG_MICFIL_VAD0_CTRL1, 0x00000000}, 80147a70e6fSCosmin Samoila {REG_MICFIL_VAD0_CTRL2, 0x000A0000}, 80247a70e6fSCosmin Samoila {REG_MICFIL_VAD0_STAT, 0x00000000}, 80347a70e6fSCosmin Samoila {REG_MICFIL_VAD0_SCONFIG, 0x00000000}, 80447a70e6fSCosmin Samoila {REG_MICFIL_VAD0_NCONFIG, 0x80000000}, 80547a70e6fSCosmin Samoila {REG_MICFIL_VAD0_NDATA, 0x00000000}, 80647a70e6fSCosmin Samoila {REG_MICFIL_VAD0_ZCD, 0x00000004}, 80747a70e6fSCosmin Samoila }; 80847a70e6fSCosmin Samoila 80947a70e6fSCosmin Samoila static bool fsl_micfil_readable_reg(struct device *dev, unsigned int reg) 81047a70e6fSCosmin Samoila { 81147a70e6fSCosmin Samoila switch (reg) { 81247a70e6fSCosmin Samoila case REG_MICFIL_CTRL1: 81347a70e6fSCosmin Samoila case REG_MICFIL_CTRL2: 81447a70e6fSCosmin Samoila case REG_MICFIL_STAT: 81547a70e6fSCosmin Samoila case REG_MICFIL_FIFO_CTRL: 81647a70e6fSCosmin Samoila case REG_MICFIL_FIFO_STAT: 81747a70e6fSCosmin Samoila case REG_MICFIL_DATACH0: 81847a70e6fSCosmin Samoila case REG_MICFIL_DATACH1: 81947a70e6fSCosmin Samoila case REG_MICFIL_DATACH2: 82047a70e6fSCosmin Samoila case REG_MICFIL_DATACH3: 82147a70e6fSCosmin Samoila case REG_MICFIL_DATACH4: 82247a70e6fSCosmin Samoila case REG_MICFIL_DATACH5: 82347a70e6fSCosmin Samoila case REG_MICFIL_DATACH6: 82447a70e6fSCosmin Samoila case REG_MICFIL_DATACH7: 82547a70e6fSCosmin Samoila case REG_MICFIL_DC_CTRL: 82647a70e6fSCosmin Samoila case REG_MICFIL_OUT_CTRL: 82747a70e6fSCosmin Samoila case REG_MICFIL_OUT_STAT: 82847a70e6fSCosmin Samoila case REG_MICFIL_VAD0_CTRL1: 82947a70e6fSCosmin Samoila case REG_MICFIL_VAD0_CTRL2: 83047a70e6fSCosmin Samoila case REG_MICFIL_VAD0_STAT: 83147a70e6fSCosmin Samoila case REG_MICFIL_VAD0_SCONFIG: 83247a70e6fSCosmin Samoila case REG_MICFIL_VAD0_NCONFIG: 83347a70e6fSCosmin Samoila case REG_MICFIL_VAD0_NDATA: 83447a70e6fSCosmin Samoila case REG_MICFIL_VAD0_ZCD: 83547a70e6fSCosmin Samoila return true; 83647a70e6fSCosmin Samoila default: 83747a70e6fSCosmin Samoila return false; 83847a70e6fSCosmin Samoila } 83947a70e6fSCosmin Samoila } 84047a70e6fSCosmin Samoila 84147a70e6fSCosmin Samoila static bool fsl_micfil_writeable_reg(struct device *dev, unsigned int reg) 84247a70e6fSCosmin Samoila { 84347a70e6fSCosmin Samoila switch (reg) { 84447a70e6fSCosmin Samoila case REG_MICFIL_CTRL1: 84547a70e6fSCosmin Samoila case REG_MICFIL_CTRL2: 84647a70e6fSCosmin Samoila case REG_MICFIL_STAT: /* Write 1 to Clear */ 84747a70e6fSCosmin Samoila case REG_MICFIL_FIFO_CTRL: 84847a70e6fSCosmin Samoila case REG_MICFIL_FIFO_STAT: /* Write 1 to Clear */ 84947a70e6fSCosmin Samoila case REG_MICFIL_DC_CTRL: 85047a70e6fSCosmin Samoila case REG_MICFIL_OUT_CTRL: 85147a70e6fSCosmin Samoila case REG_MICFIL_OUT_STAT: /* Write 1 to Clear */ 85247a70e6fSCosmin Samoila case REG_MICFIL_VAD0_CTRL1: 85347a70e6fSCosmin Samoila case REG_MICFIL_VAD0_CTRL2: 85447a70e6fSCosmin Samoila case REG_MICFIL_VAD0_STAT: /* Write 1 to Clear */ 85547a70e6fSCosmin Samoila case REG_MICFIL_VAD0_SCONFIG: 85647a70e6fSCosmin Samoila case REG_MICFIL_VAD0_NCONFIG: 85747a70e6fSCosmin Samoila case REG_MICFIL_VAD0_ZCD: 85847a70e6fSCosmin Samoila return true; 85947a70e6fSCosmin Samoila default: 86047a70e6fSCosmin Samoila return false; 86147a70e6fSCosmin Samoila } 86247a70e6fSCosmin Samoila } 86347a70e6fSCosmin Samoila 86447a70e6fSCosmin Samoila static bool fsl_micfil_volatile_reg(struct device *dev, unsigned int reg) 86547a70e6fSCosmin Samoila { 86647a70e6fSCosmin Samoila switch (reg) { 86747a70e6fSCosmin Samoila case REG_MICFIL_STAT: 86847a70e6fSCosmin Samoila case REG_MICFIL_DATACH0: 86947a70e6fSCosmin Samoila case REG_MICFIL_DATACH1: 87047a70e6fSCosmin Samoila case REG_MICFIL_DATACH2: 87147a70e6fSCosmin Samoila case REG_MICFIL_DATACH3: 87247a70e6fSCosmin Samoila case REG_MICFIL_DATACH4: 87347a70e6fSCosmin Samoila case REG_MICFIL_DATACH5: 87447a70e6fSCosmin Samoila case REG_MICFIL_DATACH6: 87547a70e6fSCosmin Samoila case REG_MICFIL_DATACH7: 87647a70e6fSCosmin Samoila case REG_MICFIL_VAD0_STAT: 87747a70e6fSCosmin Samoila case REG_MICFIL_VAD0_NDATA: 87847a70e6fSCosmin Samoila return true; 87947a70e6fSCosmin Samoila default: 88047a70e6fSCosmin Samoila return false; 88147a70e6fSCosmin Samoila } 88247a70e6fSCosmin Samoila } 88347a70e6fSCosmin Samoila 88447a70e6fSCosmin Samoila static const struct regmap_config fsl_micfil_regmap_config = { 88547a70e6fSCosmin Samoila .reg_bits = 32, 88647a70e6fSCosmin Samoila .reg_stride = 4, 88747a70e6fSCosmin Samoila .val_bits = 32, 88847a70e6fSCosmin Samoila 88947a70e6fSCosmin Samoila .max_register = REG_MICFIL_VAD0_ZCD, 89047a70e6fSCosmin Samoila .reg_defaults = fsl_micfil_reg_defaults, 89147a70e6fSCosmin Samoila .num_reg_defaults = ARRAY_SIZE(fsl_micfil_reg_defaults), 89247a70e6fSCosmin Samoila .readable_reg = fsl_micfil_readable_reg, 89347a70e6fSCosmin Samoila .volatile_reg = fsl_micfil_volatile_reg, 89447a70e6fSCosmin Samoila .writeable_reg = fsl_micfil_writeable_reg, 89547a70e6fSCosmin Samoila .cache_type = REGCACHE_RBTREE, 89647a70e6fSCosmin Samoila }; 89747a70e6fSCosmin Samoila 89847a70e6fSCosmin Samoila /* END OF REGMAP */ 89947a70e6fSCosmin Samoila 90047a70e6fSCosmin Samoila static irqreturn_t micfil_isr(int irq, void *devid) 90147a70e6fSCosmin Samoila { 90247a70e6fSCosmin Samoila struct fsl_micfil *micfil = (struct fsl_micfil *)devid; 90347a70e6fSCosmin Samoila struct platform_device *pdev = micfil->pdev; 90447a70e6fSCosmin Samoila u32 stat_reg; 90547a70e6fSCosmin Samoila u32 fifo_stat_reg; 90647a70e6fSCosmin Samoila u32 ctrl1_reg; 90747a70e6fSCosmin Samoila bool dma_enabled; 90847a70e6fSCosmin Samoila int i; 90947a70e6fSCosmin Samoila 91047a70e6fSCosmin Samoila regmap_read(micfil->regmap, REG_MICFIL_STAT, &stat_reg); 91147a70e6fSCosmin Samoila regmap_read(micfil->regmap, REG_MICFIL_CTRL1, &ctrl1_reg); 91247a70e6fSCosmin Samoila regmap_read(micfil->regmap, REG_MICFIL_FIFO_STAT, &fifo_stat_reg); 91347a70e6fSCosmin Samoila 91417f2142bSSascha Hauer dma_enabled = FIELD_GET(MICFIL_CTRL1_DISEL, ctrl1_reg) == MICFIL_CTRL1_DISEL_DMA; 91547a70e6fSCosmin Samoila 91647a70e6fSCosmin Samoila /* Channel 0-7 Output Data Flags */ 91747a70e6fSCosmin Samoila for (i = 0; i < MICFIL_OUTPUT_CHANNELS; i++) { 91817f2142bSSascha Hauer if (stat_reg & MICFIL_STAT_CHXF(i)) 91947a70e6fSCosmin Samoila dev_dbg(&pdev->dev, 92047a70e6fSCosmin Samoila "Data available in Data Channel %d\n", i); 92147a70e6fSCosmin Samoila /* if DMA is not enabled, field must be written with 1 92247a70e6fSCosmin Samoila * to clear 92347a70e6fSCosmin Samoila */ 92447a70e6fSCosmin Samoila if (!dma_enabled) 92547a70e6fSCosmin Samoila regmap_write_bits(micfil->regmap, 92647a70e6fSCosmin Samoila REG_MICFIL_STAT, 92717f2142bSSascha Hauer MICFIL_STAT_CHXF(i), 92847a70e6fSCosmin Samoila 1); 92947a70e6fSCosmin Samoila } 93047a70e6fSCosmin Samoila 93147a70e6fSCosmin Samoila for (i = 0; i < MICFIL_FIFO_NUM; i++) { 93217f2142bSSascha Hauer if (fifo_stat_reg & MICFIL_FIFO_STAT_FIFOX_OVER(i)) 93347a70e6fSCosmin Samoila dev_dbg(&pdev->dev, 93447a70e6fSCosmin Samoila "FIFO Overflow Exception flag for channel %d\n", 93547a70e6fSCosmin Samoila i); 93647a70e6fSCosmin Samoila 93717f2142bSSascha Hauer if (fifo_stat_reg & MICFIL_FIFO_STAT_FIFOX_UNDER(i)) 93847a70e6fSCosmin Samoila dev_dbg(&pdev->dev, 93947a70e6fSCosmin Samoila "FIFO Underflow Exception flag for channel %d\n", 94047a70e6fSCosmin Samoila i); 94147a70e6fSCosmin Samoila } 94247a70e6fSCosmin Samoila 94347a70e6fSCosmin Samoila return IRQ_HANDLED; 94447a70e6fSCosmin Samoila } 94547a70e6fSCosmin Samoila 94647a70e6fSCosmin Samoila static irqreturn_t micfil_err_isr(int irq, void *devid) 94747a70e6fSCosmin Samoila { 94847a70e6fSCosmin Samoila struct fsl_micfil *micfil = (struct fsl_micfil *)devid; 94947a70e6fSCosmin Samoila struct platform_device *pdev = micfil->pdev; 95047a70e6fSCosmin Samoila u32 stat_reg; 95147a70e6fSCosmin Samoila 95247a70e6fSCosmin Samoila regmap_read(micfil->regmap, REG_MICFIL_STAT, &stat_reg); 95347a70e6fSCosmin Samoila 954bd2cffd1SSascha Hauer if (stat_reg & MICFIL_STAT_BSY_FIL) 95547a70e6fSCosmin Samoila dev_dbg(&pdev->dev, "isr: Decimation Filter is running\n"); 95647a70e6fSCosmin Samoila 957bd2cffd1SSascha Hauer if (stat_reg & MICFIL_STAT_FIR_RDY) 95847a70e6fSCosmin Samoila dev_dbg(&pdev->dev, "isr: FIR Filter Data ready\n"); 95947a70e6fSCosmin Samoila 960bd2cffd1SSascha Hauer if (stat_reg & MICFIL_STAT_LOWFREQF) { 96147a70e6fSCosmin Samoila dev_dbg(&pdev->dev, "isr: ipg_clk_app is too low\n"); 96247a70e6fSCosmin Samoila regmap_write_bits(micfil->regmap, REG_MICFIL_STAT, 963bd2cffd1SSascha Hauer MICFIL_STAT_LOWFREQF, 1); 96447a70e6fSCosmin Samoila } 96547a70e6fSCosmin Samoila 96647a70e6fSCosmin Samoila return IRQ_HANDLED; 96747a70e6fSCosmin Samoila } 96847a70e6fSCosmin Samoila 96929dbfeecSShengjiu Wang static irqreturn_t voice_detected_fn(int irq, void *devid) 97029dbfeecSShengjiu Wang { 97129dbfeecSShengjiu Wang struct fsl_micfil *micfil = (struct fsl_micfil *)devid; 97229dbfeecSShengjiu Wang struct snd_kcontrol *kctl; 97329dbfeecSShengjiu Wang 97429dbfeecSShengjiu Wang if (!micfil->card) 97529dbfeecSShengjiu Wang return IRQ_HANDLED; 97629dbfeecSShengjiu Wang 97729dbfeecSShengjiu Wang kctl = snd_soc_card_get_kcontrol(micfil->card, "VAD Detected"); 97829dbfeecSShengjiu Wang if (!kctl) 97929dbfeecSShengjiu Wang return IRQ_HANDLED; 98029dbfeecSShengjiu Wang 98129dbfeecSShengjiu Wang if (micfil->vad_detected) 98229dbfeecSShengjiu Wang snd_ctl_notify(micfil->card->snd_card, 98329dbfeecSShengjiu Wang SNDRV_CTL_EVENT_MASK_VALUE, 98429dbfeecSShengjiu Wang &kctl->id); 98529dbfeecSShengjiu Wang 98629dbfeecSShengjiu Wang return IRQ_HANDLED; 98729dbfeecSShengjiu Wang } 98829dbfeecSShengjiu Wang 98929dbfeecSShengjiu Wang static irqreturn_t hwvad_isr(int irq, void *devid) 99029dbfeecSShengjiu Wang { 99129dbfeecSShengjiu Wang struct fsl_micfil *micfil = (struct fsl_micfil *)devid; 99229dbfeecSShengjiu Wang struct device *dev = &micfil->pdev->dev; 99329dbfeecSShengjiu Wang u32 vad0_reg; 99429dbfeecSShengjiu Wang int ret; 99529dbfeecSShengjiu Wang 99629dbfeecSShengjiu Wang regmap_read(micfil->regmap, REG_MICFIL_VAD0_STAT, &vad0_reg); 99729dbfeecSShengjiu Wang 99829dbfeecSShengjiu Wang /* 99929dbfeecSShengjiu Wang * The only difference between MICFIL_VAD0_STAT_EF and 100029dbfeecSShengjiu Wang * MICFIL_VAD0_STAT_IF is that the former requires Write 100129dbfeecSShengjiu Wang * 1 to Clear. Since both flags are set, it is enough 100229dbfeecSShengjiu Wang * to only read one of them 100329dbfeecSShengjiu Wang */ 100429dbfeecSShengjiu Wang if (vad0_reg & MICFIL_VAD0_STAT_IF) { 100529dbfeecSShengjiu Wang /* Write 1 to clear */ 100629dbfeecSShengjiu Wang regmap_write_bits(micfil->regmap, REG_MICFIL_VAD0_STAT, 100729dbfeecSShengjiu Wang MICFIL_VAD0_STAT_IF, 100829dbfeecSShengjiu Wang MICFIL_VAD0_STAT_IF); 100929dbfeecSShengjiu Wang 101029dbfeecSShengjiu Wang micfil->vad_detected = 1; 101129dbfeecSShengjiu Wang } 101229dbfeecSShengjiu Wang 101329dbfeecSShengjiu Wang ret = fsl_micfil_hwvad_disable(micfil); 101429dbfeecSShengjiu Wang if (ret) 101529dbfeecSShengjiu Wang dev_err(dev, "Failed to disable hwvad\n"); 101629dbfeecSShengjiu Wang 101729dbfeecSShengjiu Wang return IRQ_WAKE_THREAD; 101829dbfeecSShengjiu Wang } 101929dbfeecSShengjiu Wang 102029dbfeecSShengjiu Wang static irqreturn_t hwvad_err_isr(int irq, void *devid) 102129dbfeecSShengjiu Wang { 102229dbfeecSShengjiu Wang struct fsl_micfil *micfil = (struct fsl_micfil *)devid; 102329dbfeecSShengjiu Wang struct device *dev = &micfil->pdev->dev; 102429dbfeecSShengjiu Wang u32 vad0_reg; 102529dbfeecSShengjiu Wang 102629dbfeecSShengjiu Wang regmap_read(micfil->regmap, REG_MICFIL_VAD0_STAT, &vad0_reg); 102729dbfeecSShengjiu Wang 102829dbfeecSShengjiu Wang if (vad0_reg & MICFIL_VAD0_STAT_INSATF) 102929dbfeecSShengjiu Wang dev_dbg(dev, "voice activity input overflow/underflow detected\n"); 103029dbfeecSShengjiu Wang 103129dbfeecSShengjiu Wang return IRQ_HANDLED; 103229dbfeecSShengjiu Wang } 103329dbfeecSShengjiu Wang 103447a70e6fSCosmin Samoila static int fsl_micfil_probe(struct platform_device *pdev) 103547a70e6fSCosmin Samoila { 103647a70e6fSCosmin Samoila struct device_node *np = pdev->dev.of_node; 103747a70e6fSCosmin Samoila struct fsl_micfil *micfil; 103847a70e6fSCosmin Samoila struct resource *res; 103947a70e6fSCosmin Samoila void __iomem *regs; 104047a70e6fSCosmin Samoila int ret, i; 104147a70e6fSCosmin Samoila 104247a70e6fSCosmin Samoila micfil = devm_kzalloc(&pdev->dev, sizeof(*micfil), GFP_KERNEL); 104347a70e6fSCosmin Samoila if (!micfil) 104447a70e6fSCosmin Samoila return -ENOMEM; 104547a70e6fSCosmin Samoila 104647a70e6fSCosmin Samoila micfil->pdev = pdev; 104747a70e6fSCosmin Samoila strncpy(micfil->name, np->name, sizeof(micfil->name) - 1); 104847a70e6fSCosmin Samoila 1049d7388718SFabio Estevam micfil->soc = of_device_get_match_data(&pdev->dev); 105047a70e6fSCosmin Samoila 105147a70e6fSCosmin Samoila /* ipg_clk is used to control the registers 105247a70e6fSCosmin Samoila * ipg_clk_app is used to operate the filter 105347a70e6fSCosmin Samoila */ 105447a70e6fSCosmin Samoila micfil->mclk = devm_clk_get(&pdev->dev, "ipg_clk_app"); 105547a70e6fSCosmin Samoila if (IS_ERR(micfil->mclk)) { 105647a70e6fSCosmin Samoila dev_err(&pdev->dev, "failed to get core clock: %ld\n", 105747a70e6fSCosmin Samoila PTR_ERR(micfil->mclk)); 105847a70e6fSCosmin Samoila return PTR_ERR(micfil->mclk); 105947a70e6fSCosmin Samoila } 106047a70e6fSCosmin Samoila 1061b5cf28f7SShengjiu Wang micfil->busclk = devm_clk_get(&pdev->dev, "ipg_clk"); 1062b5cf28f7SShengjiu Wang if (IS_ERR(micfil->busclk)) { 1063b5cf28f7SShengjiu Wang dev_err(&pdev->dev, "failed to get ipg clock: %ld\n", 1064b5cf28f7SShengjiu Wang PTR_ERR(micfil->busclk)); 1065b5cf28f7SShengjiu Wang return PTR_ERR(micfil->busclk); 1066b5cf28f7SShengjiu Wang } 1067b5cf28f7SShengjiu Wang 106893f54100SShengjiu Wang fsl_asoc_get_pll_clocks(&pdev->dev, &micfil->pll8k_clk, 106993f54100SShengjiu Wang &micfil->pll11k_clk); 107093f54100SShengjiu Wang 107147a70e6fSCosmin Samoila /* init regmap */ 1072d9bf1e79SYang Yingliang regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 107347a70e6fSCosmin Samoila if (IS_ERR(regs)) 107447a70e6fSCosmin Samoila return PTR_ERR(regs); 107547a70e6fSCosmin Samoila 1076b5cf28f7SShengjiu Wang micfil->regmap = devm_regmap_init_mmio(&pdev->dev, 107747a70e6fSCosmin Samoila regs, 107847a70e6fSCosmin Samoila &fsl_micfil_regmap_config); 107947a70e6fSCosmin Samoila if (IS_ERR(micfil->regmap)) { 108047a70e6fSCosmin Samoila dev_err(&pdev->dev, "failed to init MICFIL regmap: %ld\n", 108147a70e6fSCosmin Samoila PTR_ERR(micfil->regmap)); 108247a70e6fSCosmin Samoila return PTR_ERR(micfil->regmap); 108347a70e6fSCosmin Samoila } 108447a70e6fSCosmin Samoila 108547a70e6fSCosmin Samoila /* dataline mask for RX */ 108647a70e6fSCosmin Samoila ret = of_property_read_u32_index(np, 108747a70e6fSCosmin Samoila "fsl,dataline", 108847a70e6fSCosmin Samoila 0, 108947a70e6fSCosmin Samoila &micfil->dataline); 109047a70e6fSCosmin Samoila if (ret) 109147a70e6fSCosmin Samoila micfil->dataline = 1; 109247a70e6fSCosmin Samoila 109347a70e6fSCosmin Samoila if (micfil->dataline & ~micfil->soc->dataline) { 109447a70e6fSCosmin Samoila dev_err(&pdev->dev, "dataline setting error, Mask is 0x%X\n", 109547a70e6fSCosmin Samoila micfil->soc->dataline); 109647a70e6fSCosmin Samoila return -EINVAL; 109747a70e6fSCosmin Samoila } 109847a70e6fSCosmin Samoila 109947a70e6fSCosmin Samoila /* get IRQs */ 110047a70e6fSCosmin Samoila for (i = 0; i < MICFIL_IRQ_LINES; i++) { 110147a70e6fSCosmin Samoila micfil->irq[i] = platform_get_irq(pdev, i); 110283b35f45STang Bin if (micfil->irq[i] < 0) 110347a70e6fSCosmin Samoila return micfil->irq[i]; 110447a70e6fSCosmin Samoila } 110547a70e6fSCosmin Samoila 1106a62ed960SFabio Estevam /* Digital Microphone interface interrupt */ 110747a70e6fSCosmin Samoila ret = devm_request_irq(&pdev->dev, micfil->irq[0], 1108cbd090faSSascha Hauer micfil_isr, IRQF_SHARED, 110947a70e6fSCosmin Samoila micfil->name, micfil); 111047a70e6fSCosmin Samoila if (ret) { 111147a70e6fSCosmin Samoila dev_err(&pdev->dev, "failed to claim mic interface irq %u\n", 111247a70e6fSCosmin Samoila micfil->irq[0]); 111347a70e6fSCosmin Samoila return ret; 111447a70e6fSCosmin Samoila } 111547a70e6fSCosmin Samoila 1116a62ed960SFabio Estevam /* Digital Microphone interface error interrupt */ 111747a70e6fSCosmin Samoila ret = devm_request_irq(&pdev->dev, micfil->irq[1], 1118cbd090faSSascha Hauer micfil_err_isr, IRQF_SHARED, 111947a70e6fSCosmin Samoila micfil->name, micfil); 112047a70e6fSCosmin Samoila if (ret) { 112147a70e6fSCosmin Samoila dev_err(&pdev->dev, "failed to claim mic interface error irq %u\n", 112247a70e6fSCosmin Samoila micfil->irq[1]); 112347a70e6fSCosmin Samoila return ret; 112447a70e6fSCosmin Samoila } 112547a70e6fSCosmin Samoila 112629dbfeecSShengjiu Wang /* Digital Microphone interface voice activity detector event */ 112729dbfeecSShengjiu Wang ret = devm_request_threaded_irq(&pdev->dev, micfil->irq[2], 112829dbfeecSShengjiu Wang hwvad_isr, voice_detected_fn, 112929dbfeecSShengjiu Wang IRQF_SHARED, micfil->name, micfil); 113029dbfeecSShengjiu Wang if (ret) { 113129dbfeecSShengjiu Wang dev_err(&pdev->dev, "failed to claim hwvad event irq %u\n", 113229dbfeecSShengjiu Wang micfil->irq[0]); 113329dbfeecSShengjiu Wang return ret; 113429dbfeecSShengjiu Wang } 113529dbfeecSShengjiu Wang 113629dbfeecSShengjiu Wang /* Digital Microphone interface voice activity detector error */ 113729dbfeecSShengjiu Wang ret = devm_request_irq(&pdev->dev, micfil->irq[3], 113829dbfeecSShengjiu Wang hwvad_err_isr, IRQF_SHARED, 113929dbfeecSShengjiu Wang micfil->name, micfil); 114029dbfeecSShengjiu Wang if (ret) { 114129dbfeecSShengjiu Wang dev_err(&pdev->dev, "failed to claim hwvad error irq %u\n", 114229dbfeecSShengjiu Wang micfil->irq[1]); 114329dbfeecSShengjiu Wang return ret; 114429dbfeecSShengjiu Wang } 114529dbfeecSShengjiu Wang 114647a70e6fSCosmin Samoila micfil->dma_params_rx.chan_name = "rx"; 114747a70e6fSCosmin Samoila micfil->dma_params_rx.addr = res->start + REG_MICFIL_DATACH0; 114847a70e6fSCosmin Samoila micfil->dma_params_rx.maxburst = MICFIL_DMA_MAXBURST_RX; 114947a70e6fSCosmin Samoila 115047a70e6fSCosmin Samoila platform_set_drvdata(pdev, micfil); 115147a70e6fSCosmin Samoila 115247a70e6fSCosmin Samoila pm_runtime_enable(&pdev->dev); 1153b5cf28f7SShengjiu Wang regcache_cache_only(micfil->regmap, true); 115447a70e6fSCosmin Samoila 11550adf2920SShengjiu Wang /* 11560adf2920SShengjiu Wang * Register platform component before registering cpu dai for there 11570adf2920SShengjiu Wang * is not defer probe for platform component in snd_soc_add_pcm_runtime(). 11580adf2920SShengjiu Wang */ 11590adf2920SShengjiu Wang ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); 11600adf2920SShengjiu Wang if (ret) { 11610adf2920SShengjiu Wang dev_err(&pdev->dev, "failed to pcm register\n"); 11620adf2920SShengjiu Wang return ret; 11630adf2920SShengjiu Wang } 11640adf2920SShengjiu Wang 1165cb05dac1SShengjiu Wang fsl_micfil_dai.capture.formats = micfil->soc->formats; 1166cb05dac1SShengjiu Wang 116747a70e6fSCosmin Samoila ret = devm_snd_soc_register_component(&pdev->dev, &fsl_micfil_component, 116847a70e6fSCosmin Samoila &fsl_micfil_dai, 1); 116947a70e6fSCosmin Samoila if (ret) { 117047a70e6fSCosmin Samoila dev_err(&pdev->dev, "failed to register component %s\n", 117147a70e6fSCosmin Samoila fsl_micfil_component.name); 117247a70e6fSCosmin Samoila } 117347a70e6fSCosmin Samoila 117447a70e6fSCosmin Samoila return ret; 117547a70e6fSCosmin Samoila } 117647a70e6fSCosmin Samoila 117747a70e6fSCosmin Samoila static int __maybe_unused fsl_micfil_runtime_suspend(struct device *dev) 117847a70e6fSCosmin Samoila { 117947a70e6fSCosmin Samoila struct fsl_micfil *micfil = dev_get_drvdata(dev); 118047a70e6fSCosmin Samoila 118147a70e6fSCosmin Samoila regcache_cache_only(micfil->regmap, true); 118247a70e6fSCosmin Samoila 118347a70e6fSCosmin Samoila clk_disable_unprepare(micfil->mclk); 1184b5cf28f7SShengjiu Wang clk_disable_unprepare(micfil->busclk); 118547a70e6fSCosmin Samoila 118647a70e6fSCosmin Samoila return 0; 118747a70e6fSCosmin Samoila } 118847a70e6fSCosmin Samoila 118947a70e6fSCosmin Samoila static int __maybe_unused fsl_micfil_runtime_resume(struct device *dev) 119047a70e6fSCosmin Samoila { 119147a70e6fSCosmin Samoila struct fsl_micfil *micfil = dev_get_drvdata(dev); 119247a70e6fSCosmin Samoila int ret; 119347a70e6fSCosmin Samoila 1194b5cf28f7SShengjiu Wang ret = clk_prepare_enable(micfil->busclk); 119547a70e6fSCosmin Samoila if (ret < 0) 119647a70e6fSCosmin Samoila return ret; 119747a70e6fSCosmin Samoila 1198b5cf28f7SShengjiu Wang ret = clk_prepare_enable(micfil->mclk); 1199b5cf28f7SShengjiu Wang if (ret < 0) { 1200b5cf28f7SShengjiu Wang clk_disable_unprepare(micfil->busclk); 1201b5cf28f7SShengjiu Wang return ret; 1202b5cf28f7SShengjiu Wang } 1203b5cf28f7SShengjiu Wang 120447a70e6fSCosmin Samoila regcache_cache_only(micfil->regmap, false); 120547a70e6fSCosmin Samoila regcache_mark_dirty(micfil->regmap); 120647a70e6fSCosmin Samoila regcache_sync(micfil->regmap); 120747a70e6fSCosmin Samoila 120847a70e6fSCosmin Samoila return 0; 120947a70e6fSCosmin Samoila } 121047a70e6fSCosmin Samoila 121147a70e6fSCosmin Samoila static int __maybe_unused fsl_micfil_suspend(struct device *dev) 121247a70e6fSCosmin Samoila { 121347a70e6fSCosmin Samoila pm_runtime_force_suspend(dev); 121447a70e6fSCosmin Samoila 121547a70e6fSCosmin Samoila return 0; 121647a70e6fSCosmin Samoila } 121747a70e6fSCosmin Samoila 121847a70e6fSCosmin Samoila static int __maybe_unused fsl_micfil_resume(struct device *dev) 121947a70e6fSCosmin Samoila { 122047a70e6fSCosmin Samoila pm_runtime_force_resume(dev); 122147a70e6fSCosmin Samoila 122247a70e6fSCosmin Samoila return 0; 122347a70e6fSCosmin Samoila } 122447a70e6fSCosmin Samoila 122547a70e6fSCosmin Samoila static const struct dev_pm_ops fsl_micfil_pm_ops = { 122647a70e6fSCosmin Samoila SET_RUNTIME_PM_OPS(fsl_micfil_runtime_suspend, 122747a70e6fSCosmin Samoila fsl_micfil_runtime_resume, 122847a70e6fSCosmin Samoila NULL) 122947a70e6fSCosmin Samoila SET_SYSTEM_SLEEP_PM_OPS(fsl_micfil_suspend, 123047a70e6fSCosmin Samoila fsl_micfil_resume) 123147a70e6fSCosmin Samoila }; 123247a70e6fSCosmin Samoila 123347a70e6fSCosmin Samoila static struct platform_driver fsl_micfil_driver = { 123447a70e6fSCosmin Samoila .probe = fsl_micfil_probe, 123547a70e6fSCosmin Samoila .driver = { 123647a70e6fSCosmin Samoila .name = "fsl-micfil-dai", 123747a70e6fSCosmin Samoila .pm = &fsl_micfil_pm_ops, 123847a70e6fSCosmin Samoila .of_match_table = fsl_micfil_dt_ids, 123947a70e6fSCosmin Samoila }, 124047a70e6fSCosmin Samoila }; 124147a70e6fSCosmin Samoila module_platform_driver(fsl_micfil_driver); 124247a70e6fSCosmin Samoila 124347a70e6fSCosmin Samoila MODULE_AUTHOR("Cosmin-Gabriel Samoila <cosmin.samoila@nxp.com>"); 124447a70e6fSCosmin Samoila MODULE_DESCRIPTION("NXP PDM Microphone Interface (MICFIL) driver"); 124547a70e6fSCosmin Samoila MODULE_LICENSE("GPL v2"); 1246