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; 66cb05dac1SShengjiu Wang u64 formats; 6747a70e6fSCosmin Samoila }; 6847a70e6fSCosmin Samoila 6947a70e6fSCosmin Samoila static struct fsl_micfil_soc_data fsl_micfil_imx8mm = { 7047a70e6fSCosmin Samoila .imx = true, 7147a70e6fSCosmin Samoila .fifos = 8, 7247a70e6fSCosmin Samoila .fifo_depth = 8, 7347a70e6fSCosmin Samoila .dataline = 0xf, 74cb05dac1SShengjiu Wang .formats = SNDRV_PCM_FMTBIT_S16_LE, 75cb05dac1SShengjiu Wang }; 76cb05dac1SShengjiu Wang 77cb05dac1SShengjiu Wang static struct fsl_micfil_soc_data fsl_micfil_imx8mp = { 78cb05dac1SShengjiu Wang .imx = true, 79cb05dac1SShengjiu Wang .fifos = 8, 80cb05dac1SShengjiu Wang .fifo_depth = 32, 81cb05dac1SShengjiu Wang .dataline = 0xf, 82cb05dac1SShengjiu Wang .formats = SNDRV_PCM_FMTBIT_S32_LE, 8347a70e6fSCosmin Samoila }; 8447a70e6fSCosmin Samoila 85*a10a5254SChancel Liu static struct fsl_micfil_soc_data fsl_micfil_imx93 = { 86*a10a5254SChancel Liu .imx = true, 87*a10a5254SChancel Liu .fifos = 8, 88*a10a5254SChancel Liu .fifo_depth = 32, 89*a10a5254SChancel Liu .dataline = 0xf, 90*a10a5254SChancel Liu .formats = SNDRV_PCM_FMTBIT_S32_LE, 91*a10a5254SChancel Liu }; 92*a10a5254SChancel Liu 9347a70e6fSCosmin Samoila static const struct of_device_id fsl_micfil_dt_ids[] = { 9447a70e6fSCosmin Samoila { .compatible = "fsl,imx8mm-micfil", .data = &fsl_micfil_imx8mm }, 95cb05dac1SShengjiu Wang { .compatible = "fsl,imx8mp-micfil", .data = &fsl_micfil_imx8mp }, 96*a10a5254SChancel Liu { .compatible = "fsl,imx93-micfil", .data = &fsl_micfil_imx93 }, 9747a70e6fSCosmin Samoila {} 9847a70e6fSCosmin Samoila }; 9947a70e6fSCosmin Samoila MODULE_DEVICE_TABLE(of, fsl_micfil_dt_ids); 10047a70e6fSCosmin Samoila 10147a70e6fSCosmin Samoila static const char * const micfil_quality_select_texts[] = { 102bea1d61dSSascha Hauer [QUALITY_HIGH] = "High", 103bea1d61dSSascha Hauer [QUALITY_MEDIUM] = "Medium", 104bea1d61dSSascha Hauer [QUALITY_LOW] = "Low", 105bea1d61dSSascha Hauer [QUALITY_VLOW0] = "VLow0", 106bea1d61dSSascha Hauer [QUALITY_VLOW1] = "Vlow1", 107bea1d61dSSascha Hauer [QUALITY_VLOW2] = "Vlow2", 10847a70e6fSCosmin Samoila }; 10947a70e6fSCosmin Samoila 11047a70e6fSCosmin Samoila static const struct soc_enum fsl_micfil_quality_enum = 111bea1d61dSSascha Hauer SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_quality_select_texts), 11247a70e6fSCosmin Samoila micfil_quality_select_texts); 11347a70e6fSCosmin Samoila 11447a70e6fSCosmin Samoila static DECLARE_TLV_DB_SCALE(gain_tlv, 0, 100, 0); 11547a70e6fSCosmin Samoila 116bea1d61dSSascha Hauer static int micfil_set_quality(struct fsl_micfil *micfil) 117bea1d61dSSascha Hauer { 118bea1d61dSSascha Hauer u32 qsel; 119bea1d61dSSascha Hauer 120bea1d61dSSascha Hauer switch (micfil->quality) { 121bea1d61dSSascha Hauer case QUALITY_HIGH: 122bea1d61dSSascha Hauer qsel = MICFIL_QSEL_HIGH_QUALITY; 123bea1d61dSSascha Hauer break; 124bea1d61dSSascha Hauer case QUALITY_MEDIUM: 125bea1d61dSSascha Hauer qsel = MICFIL_QSEL_MEDIUM_QUALITY; 126bea1d61dSSascha Hauer break; 127bea1d61dSSascha Hauer case QUALITY_LOW: 128bea1d61dSSascha Hauer qsel = MICFIL_QSEL_LOW_QUALITY; 129bea1d61dSSascha Hauer break; 130bea1d61dSSascha Hauer case QUALITY_VLOW0: 131bea1d61dSSascha Hauer qsel = MICFIL_QSEL_VLOW0_QUALITY; 132bea1d61dSSascha Hauer break; 133bea1d61dSSascha Hauer case QUALITY_VLOW1: 134bea1d61dSSascha Hauer qsel = MICFIL_QSEL_VLOW1_QUALITY; 135bea1d61dSSascha Hauer break; 136bea1d61dSSascha Hauer case QUALITY_VLOW2: 137bea1d61dSSascha Hauer qsel = MICFIL_QSEL_VLOW2_QUALITY; 138bea1d61dSSascha Hauer break; 139bea1d61dSSascha Hauer } 140bea1d61dSSascha Hauer 141bea1d61dSSascha Hauer return regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2, 142bea1d61dSSascha Hauer MICFIL_CTRL2_QSEL, 143bea1d61dSSascha Hauer FIELD_PREP(MICFIL_CTRL2_QSEL, qsel)); 144bea1d61dSSascha Hauer } 145bea1d61dSSascha Hauer 146bea1d61dSSascha Hauer static int micfil_quality_get(struct snd_kcontrol *kcontrol, 147bea1d61dSSascha Hauer struct snd_ctl_elem_value *ucontrol) 148bea1d61dSSascha Hauer { 149bea1d61dSSascha Hauer struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); 150bea1d61dSSascha Hauer struct fsl_micfil *micfil = snd_soc_component_get_drvdata(cmpnt); 151bea1d61dSSascha Hauer 152bea1d61dSSascha Hauer ucontrol->value.integer.value[0] = micfil->quality; 153bea1d61dSSascha Hauer 154bea1d61dSSascha Hauer return 0; 155bea1d61dSSascha Hauer } 156bea1d61dSSascha Hauer 157bea1d61dSSascha Hauer static int micfil_quality_set(struct snd_kcontrol *kcontrol, 158bea1d61dSSascha Hauer struct snd_ctl_elem_value *ucontrol) 159bea1d61dSSascha Hauer { 160bea1d61dSSascha Hauer struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); 161bea1d61dSSascha Hauer struct fsl_micfil *micfil = snd_soc_component_get_drvdata(cmpnt); 162bea1d61dSSascha Hauer 163bea1d61dSSascha Hauer micfil->quality = ucontrol->value.integer.value[0]; 164bea1d61dSSascha Hauer 165bea1d61dSSascha Hauer return micfil_set_quality(micfil); 166bea1d61dSSascha Hauer } 167bea1d61dSSascha Hauer 16829dbfeecSShengjiu Wang static const char * const micfil_hwvad_enable[] = { 16929dbfeecSShengjiu Wang "Disable (Record only)", 17029dbfeecSShengjiu Wang "Enable (Record with Vad)", 17129dbfeecSShengjiu Wang }; 17229dbfeecSShengjiu Wang 17329dbfeecSShengjiu Wang static const char * const micfil_hwvad_init_mode[] = { 17429dbfeecSShengjiu Wang "Envelope mode", "Energy mode", 17529dbfeecSShengjiu Wang }; 17629dbfeecSShengjiu Wang 17729dbfeecSShengjiu Wang static const char * const micfil_hwvad_hpf_texts[] = { 17829dbfeecSShengjiu Wang "Filter bypass", 17929dbfeecSShengjiu Wang "Cut-off @1750Hz", 18029dbfeecSShengjiu Wang "Cut-off @215Hz", 18129dbfeecSShengjiu Wang "Cut-off @102Hz", 18229dbfeecSShengjiu Wang }; 18329dbfeecSShengjiu Wang 18429dbfeecSShengjiu Wang /* 18529dbfeecSShengjiu Wang * DC Remover Control 18629dbfeecSShengjiu Wang * Filter Bypassed 1 1 18729dbfeecSShengjiu Wang * Cut-off @21Hz 0 0 18829dbfeecSShengjiu Wang * Cut-off @83Hz 0 1 18929dbfeecSShengjiu Wang * Cut-off @152HZ 1 0 19029dbfeecSShengjiu Wang */ 19129dbfeecSShengjiu Wang static const char * const micfil_dc_remover_texts[] = { 19229dbfeecSShengjiu Wang "Cut-off @21Hz", "Cut-off @83Hz", 19329dbfeecSShengjiu Wang "Cut-off @152Hz", "Bypass", 19429dbfeecSShengjiu Wang }; 19529dbfeecSShengjiu Wang 19629dbfeecSShengjiu Wang static const struct soc_enum hwvad_enable_enum = 19729dbfeecSShengjiu Wang SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_hwvad_enable), 19829dbfeecSShengjiu Wang micfil_hwvad_enable); 19929dbfeecSShengjiu Wang static const struct soc_enum hwvad_init_mode_enum = 20029dbfeecSShengjiu Wang SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_hwvad_init_mode), 20129dbfeecSShengjiu Wang micfil_hwvad_init_mode); 20229dbfeecSShengjiu Wang static const struct soc_enum hwvad_hpf_enum = 20329dbfeecSShengjiu Wang SOC_ENUM_SINGLE(REG_MICFIL_VAD0_CTRL2, 0, 20429dbfeecSShengjiu Wang ARRAY_SIZE(micfil_hwvad_hpf_texts), 20529dbfeecSShengjiu Wang micfil_hwvad_hpf_texts); 20629dbfeecSShengjiu Wang static const struct soc_enum fsl_micfil_dc_remover_enum = 20729dbfeecSShengjiu Wang SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_dc_remover_texts), 20829dbfeecSShengjiu Wang micfil_dc_remover_texts); 20929dbfeecSShengjiu Wang 21029dbfeecSShengjiu Wang static int micfil_put_dc_remover_state(struct snd_kcontrol *kcontrol, 21129dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 21229dbfeecSShengjiu Wang { 21329dbfeecSShengjiu Wang struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 21429dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 21529dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 21629dbfeecSShengjiu Wang unsigned int *item = ucontrol->value.enumerated.item; 21729dbfeecSShengjiu Wang int val = snd_soc_enum_item_to_val(e, item[0]); 21829dbfeecSShengjiu Wang int i = 0, ret = 0; 21929dbfeecSShengjiu Wang u32 reg_val = 0; 22029dbfeecSShengjiu Wang 22129dbfeecSShengjiu Wang if (val < 0 || val > 3) 22229dbfeecSShengjiu Wang return -EINVAL; 22329dbfeecSShengjiu Wang 22429dbfeecSShengjiu Wang micfil->dc_remover = val; 22529dbfeecSShengjiu Wang 22629dbfeecSShengjiu Wang /* Calculate total value for all channels */ 22729dbfeecSShengjiu Wang for (i = 0; i < MICFIL_OUTPUT_CHANNELS; i++) 22829dbfeecSShengjiu Wang reg_val |= val << MICFIL_DC_CHX_SHIFT(i); 22929dbfeecSShengjiu Wang 23029dbfeecSShengjiu Wang /* Update DC Remover mode for all channels */ 23129dbfeecSShengjiu Wang ret = snd_soc_component_update_bits(comp, REG_MICFIL_DC_CTRL, 23229dbfeecSShengjiu Wang MICFIL_DC_CTRL_CONFIG, reg_val); 23329dbfeecSShengjiu Wang if (ret < 0) 23429dbfeecSShengjiu Wang return ret; 23529dbfeecSShengjiu Wang 23629dbfeecSShengjiu Wang return 0; 23729dbfeecSShengjiu Wang } 23829dbfeecSShengjiu Wang 23929dbfeecSShengjiu Wang static int micfil_get_dc_remover_state(struct snd_kcontrol *kcontrol, 24029dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 24129dbfeecSShengjiu Wang { 24229dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 24329dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 24429dbfeecSShengjiu Wang 24529dbfeecSShengjiu Wang ucontrol->value.enumerated.item[0] = micfil->dc_remover; 24629dbfeecSShengjiu Wang 24729dbfeecSShengjiu Wang return 0; 24829dbfeecSShengjiu Wang } 24929dbfeecSShengjiu Wang 25029dbfeecSShengjiu Wang static int hwvad_put_enable(struct snd_kcontrol *kcontrol, 25129dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 25229dbfeecSShengjiu Wang { 25329dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 25429dbfeecSShengjiu Wang struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 25529dbfeecSShengjiu Wang unsigned int *item = ucontrol->value.enumerated.item; 25629dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 25729dbfeecSShengjiu Wang int val = snd_soc_enum_item_to_val(e, item[0]); 25829dbfeecSShengjiu Wang 25929dbfeecSShengjiu Wang micfil->vad_enabled = val; 26029dbfeecSShengjiu Wang 26129dbfeecSShengjiu Wang return 0; 26229dbfeecSShengjiu Wang } 26329dbfeecSShengjiu Wang 26429dbfeecSShengjiu Wang static int hwvad_get_enable(struct snd_kcontrol *kcontrol, 26529dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 26629dbfeecSShengjiu Wang { 26729dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 26829dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 26929dbfeecSShengjiu Wang 27029dbfeecSShengjiu Wang ucontrol->value.enumerated.item[0] = micfil->vad_enabled; 27129dbfeecSShengjiu Wang 27229dbfeecSShengjiu Wang return 0; 27329dbfeecSShengjiu Wang } 27429dbfeecSShengjiu Wang 27529dbfeecSShengjiu Wang static int hwvad_put_init_mode(struct snd_kcontrol *kcontrol, 27629dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 27729dbfeecSShengjiu Wang { 27829dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 27929dbfeecSShengjiu Wang struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 28029dbfeecSShengjiu Wang unsigned int *item = ucontrol->value.enumerated.item; 28129dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 28229dbfeecSShengjiu Wang int val = snd_soc_enum_item_to_val(e, item[0]); 28329dbfeecSShengjiu Wang 28429dbfeecSShengjiu Wang /* 0 - Envelope-based Mode 28529dbfeecSShengjiu Wang * 1 - Energy-based Mode 28629dbfeecSShengjiu Wang */ 28729dbfeecSShengjiu Wang micfil->vad_init_mode = val; 28829dbfeecSShengjiu Wang 28929dbfeecSShengjiu Wang return 0; 29029dbfeecSShengjiu Wang } 29129dbfeecSShengjiu Wang 29229dbfeecSShengjiu Wang static int hwvad_get_init_mode(struct snd_kcontrol *kcontrol, 29329dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 29429dbfeecSShengjiu Wang { 29529dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 29629dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 29729dbfeecSShengjiu Wang 29829dbfeecSShengjiu Wang ucontrol->value.enumerated.item[0] = micfil->vad_init_mode; 29929dbfeecSShengjiu Wang 30029dbfeecSShengjiu Wang return 0; 30129dbfeecSShengjiu Wang } 30229dbfeecSShengjiu Wang 30329dbfeecSShengjiu Wang static int hwvad_detected(struct snd_kcontrol *kcontrol, 30429dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 30529dbfeecSShengjiu Wang { 30629dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 30729dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 30829dbfeecSShengjiu Wang 30929dbfeecSShengjiu Wang ucontrol->value.enumerated.item[0] = micfil->vad_detected; 31029dbfeecSShengjiu Wang 31129dbfeecSShengjiu Wang return 0; 31229dbfeecSShengjiu Wang } 31329dbfeecSShengjiu Wang 31447a70e6fSCosmin Samoila static const struct snd_kcontrol_new fsl_micfil_snd_controls[] = { 31547a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH0 Volume", REG_MICFIL_OUT_CTRL, 31647a70e6fSCosmin Samoila MICFIL_OUTGAIN_CHX_SHIFT(0), 0xF, 0x7, gain_tlv), 31747a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH1 Volume", REG_MICFIL_OUT_CTRL, 31847a70e6fSCosmin Samoila MICFIL_OUTGAIN_CHX_SHIFT(1), 0xF, 0x7, gain_tlv), 31947a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH2 Volume", REG_MICFIL_OUT_CTRL, 32047a70e6fSCosmin Samoila MICFIL_OUTGAIN_CHX_SHIFT(2), 0xF, 0x7, gain_tlv), 32147a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH3 Volume", REG_MICFIL_OUT_CTRL, 32247a70e6fSCosmin Samoila MICFIL_OUTGAIN_CHX_SHIFT(3), 0xF, 0x7, gain_tlv), 32347a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH4 Volume", REG_MICFIL_OUT_CTRL, 32447a70e6fSCosmin Samoila MICFIL_OUTGAIN_CHX_SHIFT(4), 0xF, 0x7, gain_tlv), 32547a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH5 Volume", REG_MICFIL_OUT_CTRL, 32647a70e6fSCosmin Samoila MICFIL_OUTGAIN_CHX_SHIFT(5), 0xF, 0x7, gain_tlv), 32747a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH6 Volume", REG_MICFIL_OUT_CTRL, 32847a70e6fSCosmin Samoila MICFIL_OUTGAIN_CHX_SHIFT(6), 0xF, 0x7, gain_tlv), 32947a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH7 Volume", REG_MICFIL_OUT_CTRL, 33047a70e6fSCosmin Samoila MICFIL_OUTGAIN_CHX_SHIFT(7), 0xF, 0x7, gain_tlv), 33147a70e6fSCosmin Samoila SOC_ENUM_EXT("MICFIL Quality Select", 33247a70e6fSCosmin Samoila fsl_micfil_quality_enum, 333bea1d61dSSascha Hauer micfil_quality_get, micfil_quality_set), 33429dbfeecSShengjiu Wang SOC_ENUM_EXT("HWVAD Enablement Switch", hwvad_enable_enum, 33529dbfeecSShengjiu Wang hwvad_get_enable, hwvad_put_enable), 33629dbfeecSShengjiu Wang SOC_ENUM_EXT("HWVAD Initialization Mode", hwvad_init_mode_enum, 33729dbfeecSShengjiu Wang hwvad_get_init_mode, hwvad_put_init_mode), 33829dbfeecSShengjiu Wang SOC_ENUM("HWVAD High-Pass Filter", hwvad_hpf_enum), 33929dbfeecSShengjiu Wang SOC_SINGLE("HWVAD ZCD Switch", REG_MICFIL_VAD0_ZCD, 0, 1, 0), 34029dbfeecSShengjiu Wang SOC_SINGLE("HWVAD ZCD Auto Threshold Switch", 34129dbfeecSShengjiu Wang REG_MICFIL_VAD0_ZCD, 2, 1, 0), 34229dbfeecSShengjiu Wang SOC_ENUM_EXT("MICFIL DC Remover Control", fsl_micfil_dc_remover_enum, 34329dbfeecSShengjiu Wang micfil_get_dc_remover_state, micfil_put_dc_remover_state), 34429dbfeecSShengjiu Wang SOC_SINGLE("HWVAD Input Gain", REG_MICFIL_VAD0_CTRL2, 8, 15, 0), 34529dbfeecSShengjiu Wang SOC_SINGLE("HWVAD Sound Gain", REG_MICFIL_VAD0_SCONFIG, 0, 15, 0), 34629dbfeecSShengjiu Wang SOC_SINGLE("HWVAD Noise Gain", REG_MICFIL_VAD0_NCONFIG, 0, 15, 0), 34729dbfeecSShengjiu Wang SOC_SINGLE_RANGE("HWVAD Detector Frame Time", REG_MICFIL_VAD0_CTRL2, 16, 0, 63, 0), 34829dbfeecSShengjiu Wang SOC_SINGLE("HWVAD Detector Initialization Time", REG_MICFIL_VAD0_CTRL1, 8, 31, 0), 34929dbfeecSShengjiu Wang SOC_SINGLE("HWVAD Noise Filter Adjustment", REG_MICFIL_VAD0_NCONFIG, 8, 31, 0), 35029dbfeecSShengjiu Wang SOC_SINGLE("HWVAD ZCD Threshold", REG_MICFIL_VAD0_ZCD, 16, 1023, 0), 35129dbfeecSShengjiu Wang SOC_SINGLE("HWVAD ZCD Adjustment", REG_MICFIL_VAD0_ZCD, 8, 15, 0), 35229dbfeecSShengjiu Wang SOC_SINGLE("HWVAD ZCD And Behavior Switch", 35329dbfeecSShengjiu Wang REG_MICFIL_VAD0_ZCD, 4, 1, 0), 35429dbfeecSShengjiu Wang SOC_SINGLE_BOOL_EXT("VAD Detected", 0, hwvad_detected, NULL), 35547a70e6fSCosmin Samoila }; 35647a70e6fSCosmin Samoila 35747a70e6fSCosmin Samoila /* The SRES is a self-negated bit which provides the CPU with the 35847a70e6fSCosmin Samoila * capability to initialize the PDM Interface module through the 35947a70e6fSCosmin Samoila * slave-bus interface. This bit always reads as zero, and this 36047a70e6fSCosmin Samoila * bit is only effective when MDIS is cleared 36147a70e6fSCosmin Samoila */ 36247a70e6fSCosmin Samoila static int fsl_micfil_reset(struct device *dev) 36347a70e6fSCosmin Samoila { 36447a70e6fSCosmin Samoila struct fsl_micfil *micfil = dev_get_drvdata(dev); 36547a70e6fSCosmin Samoila int ret; 36647a70e6fSCosmin Samoila 367d46c2127SSascha Hauer ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1, 368d46c2127SSascha Hauer MICFIL_CTRL1_MDIS); 3692c602c7eSSascha Hauer if (ret) 37047a70e6fSCosmin Samoila return ret; 37147a70e6fSCosmin Samoila 372d46c2127SSascha Hauer ret = regmap_set_bits(micfil->regmap, REG_MICFIL_CTRL1, 37347a70e6fSCosmin Samoila MICFIL_CTRL1_SRES); 3742c602c7eSSascha Hauer if (ret) 37547a70e6fSCosmin Samoila return ret; 37647a70e6fSCosmin Samoila 37747a70e6fSCosmin Samoila return 0; 37847a70e6fSCosmin Samoila } 37947a70e6fSCosmin Samoila 38047a70e6fSCosmin Samoila static int fsl_micfil_startup(struct snd_pcm_substream *substream, 38147a70e6fSCosmin Samoila struct snd_soc_dai *dai) 38247a70e6fSCosmin Samoila { 38347a70e6fSCosmin Samoila struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai); 38447a70e6fSCosmin Samoila 38547a70e6fSCosmin Samoila if (!micfil) { 38611106cb3STang Bin dev_err(dai->dev, "micfil dai priv_data not set\n"); 38747a70e6fSCosmin Samoila return -EINVAL; 38847a70e6fSCosmin Samoila } 38947a70e6fSCosmin Samoila 39047a70e6fSCosmin Samoila return 0; 39147a70e6fSCosmin Samoila } 39247a70e6fSCosmin Samoila 39329dbfeecSShengjiu Wang /* Enable/disable hwvad interrupts */ 39429dbfeecSShengjiu Wang static int fsl_micfil_configure_hwvad_interrupts(struct fsl_micfil *micfil, int enable) 39529dbfeecSShengjiu Wang { 39629dbfeecSShengjiu Wang u32 vadie_reg = enable ? MICFIL_VAD0_CTRL1_IE : 0; 39729dbfeecSShengjiu Wang u32 vaderie_reg = enable ? MICFIL_VAD0_CTRL1_ERIE : 0; 39829dbfeecSShengjiu Wang 39929dbfeecSShengjiu Wang /* Voice Activity Detector Error Interruption */ 40029dbfeecSShengjiu Wang regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 40129dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_ERIE, vaderie_reg); 40229dbfeecSShengjiu Wang 40329dbfeecSShengjiu Wang /* Voice Activity Detector Interruption */ 40429dbfeecSShengjiu Wang regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 40529dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_IE, vadie_reg); 40629dbfeecSShengjiu Wang 40729dbfeecSShengjiu Wang return 0; 40829dbfeecSShengjiu Wang } 40929dbfeecSShengjiu Wang 41029dbfeecSShengjiu Wang /* Configuration done only in energy-based initialization mode */ 41129dbfeecSShengjiu Wang static int fsl_micfil_init_hwvad_energy_mode(struct fsl_micfil *micfil) 41229dbfeecSShengjiu Wang { 41329dbfeecSShengjiu Wang /* Keep the VADFRENDIS bitfield cleared. */ 41429dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2, 41529dbfeecSShengjiu Wang MICFIL_VAD0_CTRL2_FRENDIS); 41629dbfeecSShengjiu Wang 41729dbfeecSShengjiu Wang /* Keep the VADPREFEN bitfield cleared. */ 41829dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2, 41929dbfeecSShengjiu Wang MICFIL_VAD0_CTRL2_PREFEN); 42029dbfeecSShengjiu Wang 42129dbfeecSShengjiu Wang /* Keep the VADSFILEN bitfield cleared. */ 42229dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG, 42329dbfeecSShengjiu Wang MICFIL_VAD0_SCONFIG_SFILEN); 42429dbfeecSShengjiu Wang 42529dbfeecSShengjiu Wang /* Keep the VADSMAXEN bitfield cleared. */ 42629dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG, 42729dbfeecSShengjiu Wang MICFIL_VAD0_SCONFIG_SMAXEN); 42829dbfeecSShengjiu Wang 42929dbfeecSShengjiu Wang /* Keep the VADNFILAUTO bitfield asserted. */ 43029dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 43129dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NFILAUT); 43229dbfeecSShengjiu Wang 43329dbfeecSShengjiu Wang /* Keep the VADNMINEN bitfield cleared. */ 43429dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 43529dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NMINEN); 43629dbfeecSShengjiu Wang 43729dbfeecSShengjiu Wang /* Keep the VADNDECEN bitfield cleared. */ 43829dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 43929dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NDECEN); 44029dbfeecSShengjiu Wang 44129dbfeecSShengjiu Wang /* Keep the VADNOREN bitfield cleared. */ 44229dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 44329dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NOREN); 44429dbfeecSShengjiu Wang 44529dbfeecSShengjiu Wang return 0; 44629dbfeecSShengjiu Wang } 44729dbfeecSShengjiu Wang 44829dbfeecSShengjiu Wang /* Configuration done only in envelope-based initialization mode */ 44929dbfeecSShengjiu Wang static int fsl_micfil_init_hwvad_envelope_mode(struct fsl_micfil *micfil) 45029dbfeecSShengjiu Wang { 45129dbfeecSShengjiu Wang /* Assert the VADFRENDIS bitfield */ 45229dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2, 45329dbfeecSShengjiu Wang MICFIL_VAD0_CTRL2_FRENDIS); 45429dbfeecSShengjiu Wang 45529dbfeecSShengjiu Wang /* Assert the VADPREFEN bitfield. */ 45629dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2, 45729dbfeecSShengjiu Wang MICFIL_VAD0_CTRL2_PREFEN); 45829dbfeecSShengjiu Wang 45929dbfeecSShengjiu Wang /* Assert the VADSFILEN bitfield. */ 46029dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG, 46129dbfeecSShengjiu Wang MICFIL_VAD0_SCONFIG_SFILEN); 46229dbfeecSShengjiu Wang 46329dbfeecSShengjiu Wang /* Assert the VADSMAXEN bitfield. */ 46429dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG, 46529dbfeecSShengjiu Wang MICFIL_VAD0_SCONFIG_SMAXEN); 46629dbfeecSShengjiu Wang 46729dbfeecSShengjiu Wang /* Clear the VADNFILAUTO bitfield */ 46829dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 46929dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NFILAUT); 47029dbfeecSShengjiu Wang 47129dbfeecSShengjiu Wang /* Assert the VADNMINEN bitfield. */ 47229dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 47329dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NMINEN); 47429dbfeecSShengjiu Wang 47529dbfeecSShengjiu Wang /* Assert the VADNDECEN bitfield. */ 47629dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 47729dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NDECEN); 47829dbfeecSShengjiu Wang 47929dbfeecSShengjiu Wang /* Assert VADNOREN bitfield. */ 48029dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 48129dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NOREN); 48229dbfeecSShengjiu Wang 48329dbfeecSShengjiu Wang return 0; 48429dbfeecSShengjiu Wang } 48529dbfeecSShengjiu Wang 48629dbfeecSShengjiu Wang /* 48729dbfeecSShengjiu Wang * Hardware Voice Active Detection: The HWVAD takes data from the input 48829dbfeecSShengjiu Wang * of a selected PDM microphone to detect if there is any 48929dbfeecSShengjiu Wang * voice activity. When a voice activity is detected, an interrupt could 49029dbfeecSShengjiu Wang * be delivered to the system. Initialization in section 8.4: 49129dbfeecSShengjiu Wang * Can work in two modes: 49229dbfeecSShengjiu Wang * -> Eneveope-based mode (section 8.4.1) 49329dbfeecSShengjiu Wang * -> Energy-based mode (section 8.4.2) 49429dbfeecSShengjiu Wang * 49529dbfeecSShengjiu Wang * It is important to remark that the HWVAD detector could be enabled 49629dbfeecSShengjiu Wang * or reset only when the MICFIL isn't running i.e. when the BSY_FIL 49729dbfeecSShengjiu Wang * bit in STAT register is cleared 49829dbfeecSShengjiu Wang */ 49929dbfeecSShengjiu Wang static int fsl_micfil_hwvad_enable(struct fsl_micfil *micfil) 50029dbfeecSShengjiu Wang { 50129dbfeecSShengjiu Wang int ret; 50229dbfeecSShengjiu Wang 50329dbfeecSShengjiu Wang micfil->vad_detected = 0; 50429dbfeecSShengjiu Wang 50529dbfeecSShengjiu Wang /* envelope-based specific initialization */ 50629dbfeecSShengjiu Wang if (micfil->vad_init_mode == MICFIL_HWVAD_ENVELOPE_MODE) 50729dbfeecSShengjiu Wang ret = fsl_micfil_init_hwvad_envelope_mode(micfil); 50829dbfeecSShengjiu Wang else 50929dbfeecSShengjiu Wang ret = fsl_micfil_init_hwvad_energy_mode(micfil); 51029dbfeecSShengjiu Wang if (ret) 51129dbfeecSShengjiu Wang return ret; 51229dbfeecSShengjiu Wang 51329dbfeecSShengjiu Wang /* Voice Activity Detector Internal Filters Initialization*/ 51429dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 51529dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_ST10); 51629dbfeecSShengjiu Wang 51729dbfeecSShengjiu Wang /* Voice Activity Detector Internal Filter */ 51829dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 51929dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_ST10); 52029dbfeecSShengjiu Wang 52129dbfeecSShengjiu Wang /* Enable Interrupts */ 52229dbfeecSShengjiu Wang ret = fsl_micfil_configure_hwvad_interrupts(micfil, 1); 52329dbfeecSShengjiu Wang if (ret) 52429dbfeecSShengjiu Wang return ret; 52529dbfeecSShengjiu Wang 52629dbfeecSShengjiu Wang /* Voice Activity Detector Reset */ 52729dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 52829dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_RST); 52929dbfeecSShengjiu Wang 53029dbfeecSShengjiu Wang /* Voice Activity Detector Enabled */ 53129dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 53229dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_EN); 53329dbfeecSShengjiu Wang 53429dbfeecSShengjiu Wang return 0; 53529dbfeecSShengjiu Wang } 53629dbfeecSShengjiu Wang 53729dbfeecSShengjiu Wang static int fsl_micfil_hwvad_disable(struct fsl_micfil *micfil) 53829dbfeecSShengjiu Wang { 53929dbfeecSShengjiu Wang struct device *dev = &micfil->pdev->dev; 54029dbfeecSShengjiu Wang int ret = 0; 54129dbfeecSShengjiu Wang 54229dbfeecSShengjiu Wang /* Disable HWVAD */ 54329dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 54429dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_EN); 54529dbfeecSShengjiu Wang 54629dbfeecSShengjiu Wang /* Disable hwvad interrupts */ 54729dbfeecSShengjiu Wang ret = fsl_micfil_configure_hwvad_interrupts(micfil, 0); 54829dbfeecSShengjiu Wang if (ret) 54929dbfeecSShengjiu Wang dev_err(dev, "Failed to disable interrupts\n"); 55029dbfeecSShengjiu Wang 55129dbfeecSShengjiu Wang return ret; 55229dbfeecSShengjiu Wang } 55329dbfeecSShengjiu Wang 55447a70e6fSCosmin Samoila static int fsl_micfil_trigger(struct snd_pcm_substream *substream, int cmd, 55547a70e6fSCosmin Samoila struct snd_soc_dai *dai) 55647a70e6fSCosmin Samoila { 55747a70e6fSCosmin Samoila struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai); 55847a70e6fSCosmin Samoila struct device *dev = &micfil->pdev->dev; 55947a70e6fSCosmin Samoila int ret; 56047a70e6fSCosmin Samoila 56147a70e6fSCosmin Samoila switch (cmd) { 56247a70e6fSCosmin Samoila case SNDRV_PCM_TRIGGER_START: 56347a70e6fSCosmin Samoila case SNDRV_PCM_TRIGGER_RESUME: 56447a70e6fSCosmin Samoila case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 56547a70e6fSCosmin Samoila ret = fsl_micfil_reset(dev); 56647a70e6fSCosmin Samoila if (ret) { 56747a70e6fSCosmin Samoila dev_err(dev, "failed to soft reset\n"); 56847a70e6fSCosmin Samoila return ret; 56947a70e6fSCosmin Samoila } 57047a70e6fSCosmin Samoila 57147a70e6fSCosmin Samoila /* DMA Interrupt Selection - DISEL bits 57247a70e6fSCosmin Samoila * 00 - DMA and IRQ disabled 57347a70e6fSCosmin Samoila * 01 - DMA req enabled 57447a70e6fSCosmin Samoila * 10 - IRQ enabled 57547a70e6fSCosmin Samoila * 11 - reserved 57647a70e6fSCosmin Samoila */ 57747a70e6fSCosmin Samoila ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1, 57817f2142bSSascha Hauer MICFIL_CTRL1_DISEL, 57917f2142bSSascha Hauer FIELD_PREP(MICFIL_CTRL1_DISEL, MICFIL_CTRL1_DISEL_DMA)); 5802c602c7eSSascha Hauer if (ret) 58147a70e6fSCosmin Samoila return ret; 58247a70e6fSCosmin Samoila 58347a70e6fSCosmin Samoila /* Enable the module */ 584d46c2127SSascha Hauer ret = regmap_set_bits(micfil->regmap, REG_MICFIL_CTRL1, 58547a70e6fSCosmin Samoila MICFIL_CTRL1_PDMIEN); 5862c602c7eSSascha Hauer if (ret) 58747a70e6fSCosmin Samoila return ret; 58847a70e6fSCosmin Samoila 58929dbfeecSShengjiu Wang if (micfil->vad_enabled) 59029dbfeecSShengjiu Wang fsl_micfil_hwvad_enable(micfil); 59129dbfeecSShengjiu Wang 59247a70e6fSCosmin Samoila break; 59347a70e6fSCosmin Samoila case SNDRV_PCM_TRIGGER_STOP: 59447a70e6fSCosmin Samoila case SNDRV_PCM_TRIGGER_SUSPEND: 59547a70e6fSCosmin Samoila case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 59629dbfeecSShengjiu Wang if (micfil->vad_enabled) 59729dbfeecSShengjiu Wang fsl_micfil_hwvad_disable(micfil); 59829dbfeecSShengjiu Wang 59947a70e6fSCosmin Samoila /* Disable the module */ 600d46c2127SSascha Hauer ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1, 601d46c2127SSascha Hauer MICFIL_CTRL1_PDMIEN); 6022c602c7eSSascha Hauer if (ret) 60347a70e6fSCosmin Samoila return ret; 60447a70e6fSCosmin Samoila 60547a70e6fSCosmin Samoila ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1, 60617f2142bSSascha Hauer MICFIL_CTRL1_DISEL, 60717f2142bSSascha Hauer FIELD_PREP(MICFIL_CTRL1_DISEL, MICFIL_CTRL1_DISEL_DISABLE)); 6082c602c7eSSascha Hauer if (ret) 60947a70e6fSCosmin Samoila return ret; 61047a70e6fSCosmin Samoila break; 61147a70e6fSCosmin Samoila default: 61247a70e6fSCosmin Samoila return -EINVAL; 61347a70e6fSCosmin Samoila } 61447a70e6fSCosmin Samoila return 0; 61547a70e6fSCosmin Samoila } 61647a70e6fSCosmin Samoila 61793f54100SShengjiu Wang static int fsl_micfil_reparent_rootclk(struct fsl_micfil *micfil, unsigned int sample_rate) 61893f54100SShengjiu Wang { 61993f54100SShengjiu Wang struct device *dev = &micfil->pdev->dev; 62093f54100SShengjiu Wang u64 ratio = sample_rate; 62193f54100SShengjiu Wang struct clk *clk; 62293f54100SShengjiu Wang int ret; 62393f54100SShengjiu Wang 62493f54100SShengjiu Wang /* Get root clock */ 62593f54100SShengjiu Wang clk = micfil->mclk; 62693f54100SShengjiu Wang 62793f54100SShengjiu Wang /* Disable clock first, for it was enabled by pm_runtime */ 62893f54100SShengjiu Wang clk_disable_unprepare(clk); 62993f54100SShengjiu Wang fsl_asoc_reparent_pll_clocks(dev, clk, micfil->pll8k_clk, 63093f54100SShengjiu Wang micfil->pll11k_clk, ratio); 63193f54100SShengjiu Wang ret = clk_prepare_enable(clk); 63293f54100SShengjiu Wang if (ret) 63393f54100SShengjiu Wang return ret; 63493f54100SShengjiu Wang 63593f54100SShengjiu Wang return 0; 63693f54100SShengjiu Wang } 63793f54100SShengjiu Wang 63847a70e6fSCosmin Samoila static int fsl_micfil_hw_params(struct snd_pcm_substream *substream, 63947a70e6fSCosmin Samoila struct snd_pcm_hw_params *params, 64047a70e6fSCosmin Samoila struct snd_soc_dai *dai) 64147a70e6fSCosmin Samoila { 64247a70e6fSCosmin Samoila struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai); 64347a70e6fSCosmin Samoila unsigned int channels = params_channels(params); 64447a70e6fSCosmin Samoila unsigned int rate = params_rate(params); 645cc5ef57dSSascha Hauer int clk_div = 8; 646cc5ef57dSSascha Hauer int osr = MICFIL_OSR_DEFAULT; 64747a70e6fSCosmin Samoila int ret; 64847a70e6fSCosmin Samoila 64947a70e6fSCosmin Samoila /* 1. Disable the module */ 650d46c2127SSascha Hauer ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1, 651d46c2127SSascha Hauer MICFIL_CTRL1_PDMIEN); 6522c602c7eSSascha Hauer if (ret) 65347a70e6fSCosmin Samoila return ret; 65447a70e6fSCosmin Samoila 65547a70e6fSCosmin Samoila /* enable channels */ 65647a70e6fSCosmin Samoila ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1, 65747a70e6fSCosmin Samoila 0xFF, ((1 << channels) - 1)); 6582c602c7eSSascha Hauer if (ret) 65947a70e6fSCosmin Samoila return ret; 66047a70e6fSCosmin Samoila 66193f54100SShengjiu Wang ret = fsl_micfil_reparent_rootclk(micfil, rate); 66293f54100SShengjiu Wang if (ret) 66393f54100SShengjiu Wang return ret; 66493f54100SShengjiu Wang 665cc5ef57dSSascha Hauer ret = clk_set_rate(micfil->mclk, rate * clk_div * osr * 8); 666cc5ef57dSSascha Hauer if (ret) 66747a70e6fSCosmin Samoila return ret; 668cc5ef57dSSascha Hauer 669cc5ef57dSSascha Hauer ret = micfil_set_quality(micfil); 670cc5ef57dSSascha Hauer if (ret) 671cc5ef57dSSascha Hauer return ret; 672cc5ef57dSSascha Hauer 673cc5ef57dSSascha Hauer ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2, 674cc5ef57dSSascha Hauer MICFIL_CTRL2_CLKDIV | MICFIL_CTRL2_CICOSR, 675cc5ef57dSSascha Hauer FIELD_PREP(MICFIL_CTRL2_CLKDIV, clk_div) | 676cc5ef57dSSascha Hauer FIELD_PREP(MICFIL_CTRL2_CICOSR, 16 - osr)); 67747a70e6fSCosmin Samoila 67829dbfeecSShengjiu Wang /* Configure CIC OSR in VADCICOSR */ 67929dbfeecSShengjiu Wang regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 68029dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_CICOSR, 68129dbfeecSShengjiu Wang FIELD_PREP(MICFIL_VAD0_CTRL1_CICOSR, 16 - osr)); 68229dbfeecSShengjiu Wang 68329dbfeecSShengjiu Wang /* Configure source channel in VADCHSEL */ 68429dbfeecSShengjiu Wang regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 68529dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_CHSEL, 68629dbfeecSShengjiu Wang FIELD_PREP(MICFIL_VAD0_CTRL1_CHSEL, (channels - 1))); 68729dbfeecSShengjiu Wang 6882495ba26SSascha Hauer micfil->dma_params_rx.peripheral_config = &micfil->sdmacfg; 6892495ba26SSascha Hauer micfil->dma_params_rx.peripheral_size = sizeof(micfil->sdmacfg); 6902495ba26SSascha Hauer micfil->sdmacfg.n_fifos_src = channels; 6912495ba26SSascha Hauer micfil->sdmacfg.sw_done = true; 69247a70e6fSCosmin Samoila micfil->dma_params_rx.maxburst = channels * MICFIL_DMA_MAXBURST_RX; 69347a70e6fSCosmin Samoila 69447a70e6fSCosmin Samoila return 0; 69547a70e6fSCosmin Samoila } 69647a70e6fSCosmin Samoila 69738d89a56SRikard Falkeborn static const struct snd_soc_dai_ops fsl_micfil_dai_ops = { 69847a70e6fSCosmin Samoila .startup = fsl_micfil_startup, 69947a70e6fSCosmin Samoila .trigger = fsl_micfil_trigger, 70047a70e6fSCosmin Samoila .hw_params = fsl_micfil_hw_params, 70147a70e6fSCosmin Samoila }; 70247a70e6fSCosmin Samoila 70347a70e6fSCosmin Samoila static int fsl_micfil_dai_probe(struct snd_soc_dai *cpu_dai) 70447a70e6fSCosmin Samoila { 70547a70e6fSCosmin Samoila struct fsl_micfil *micfil = dev_get_drvdata(cpu_dai->dev); 7063b13b143SShengjiu Wang struct device *dev = cpu_dai->dev; 7073b13b143SShengjiu Wang unsigned int val = 0; 7083b13b143SShengjiu Wang int ret, i; 70947a70e6fSCosmin Samoila 7103b13b143SShengjiu Wang micfil->quality = QUALITY_VLOW0; 71129dbfeecSShengjiu Wang micfil->card = cpu_dai->component->card; 71247a70e6fSCosmin Samoila 7133b13b143SShengjiu Wang /* set default gain to 2 */ 7143b13b143SShengjiu Wang regmap_write(micfil->regmap, REG_MICFIL_OUT_CTRL, 0x22222222); 7153b13b143SShengjiu Wang 7163b13b143SShengjiu Wang /* set DC Remover in bypass mode*/ 7173b13b143SShengjiu Wang for (i = 0; i < MICFIL_OUTPUT_CHANNELS; i++) 7183b13b143SShengjiu Wang val |= MICFIL_DC_BYPASS << MICFIL_DC_CHX_SHIFT(i); 7193b13b143SShengjiu Wang ret = regmap_update_bits(micfil->regmap, REG_MICFIL_DC_CTRL, 7203b13b143SShengjiu Wang MICFIL_DC_CTRL_CONFIG, val); 7213b13b143SShengjiu Wang if (ret) { 7223b13b143SShengjiu Wang dev_err(dev, "failed to set DC Remover mode bits\n"); 7233b13b143SShengjiu Wang return ret; 7243b13b143SShengjiu Wang } 7253b13b143SShengjiu Wang micfil->dc_remover = MICFIL_DC_BYPASS; 72647a70e6fSCosmin Samoila 72747a70e6fSCosmin Samoila snd_soc_dai_init_dma_data(cpu_dai, NULL, 72847a70e6fSCosmin Samoila &micfil->dma_params_rx); 72947a70e6fSCosmin Samoila 73047a70e6fSCosmin Samoila /* FIFO Watermark Control - FIFOWMK*/ 73147a70e6fSCosmin Samoila ret = regmap_update_bits(micfil->regmap, REG_MICFIL_FIFO_CTRL, 73217f2142bSSascha Hauer MICFIL_FIFO_CTRL_FIFOWMK, 73317f2142bSSascha Hauer FIELD_PREP(MICFIL_FIFO_CTRL_FIFOWMK, micfil->soc->fifo_depth - 1)); 7342c602c7eSSascha Hauer if (ret) 73547a70e6fSCosmin Samoila return ret; 73647a70e6fSCosmin Samoila 73747a70e6fSCosmin Samoila return 0; 73847a70e6fSCosmin Samoila } 73947a70e6fSCosmin Samoila 74047a70e6fSCosmin Samoila static struct snd_soc_dai_driver fsl_micfil_dai = { 74147a70e6fSCosmin Samoila .probe = fsl_micfil_dai_probe, 74247a70e6fSCosmin Samoila .capture = { 74347a70e6fSCosmin Samoila .stream_name = "CPU-Capture", 74447a70e6fSCosmin Samoila .channels_min = 1, 74547a70e6fSCosmin Samoila .channels_max = 8, 74699c08cdbSSascha Hauer .rates = SNDRV_PCM_RATE_8000_48000, 74799c08cdbSSascha Hauer .formats = SNDRV_PCM_FMTBIT_S16_LE, 74847a70e6fSCosmin Samoila }, 74947a70e6fSCosmin Samoila .ops = &fsl_micfil_dai_ops, 75047a70e6fSCosmin Samoila }; 75147a70e6fSCosmin Samoila 75247a70e6fSCosmin Samoila static const struct snd_soc_component_driver fsl_micfil_component = { 75347a70e6fSCosmin Samoila .name = "fsl-micfil-dai", 75447a70e6fSCosmin Samoila .controls = fsl_micfil_snd_controls, 75547a70e6fSCosmin Samoila .num_controls = ARRAY_SIZE(fsl_micfil_snd_controls), 756978bd27cSShengjiu Wang .legacy_dai_naming = 1, 75747a70e6fSCosmin Samoila }; 75847a70e6fSCosmin Samoila 75947a70e6fSCosmin Samoila /* REGMAP */ 76047a70e6fSCosmin Samoila static const struct reg_default fsl_micfil_reg_defaults[] = { 76147a70e6fSCosmin Samoila {REG_MICFIL_CTRL1, 0x00000000}, 76247a70e6fSCosmin Samoila {REG_MICFIL_CTRL2, 0x00000000}, 76347a70e6fSCosmin Samoila {REG_MICFIL_STAT, 0x00000000}, 76447a70e6fSCosmin Samoila {REG_MICFIL_FIFO_CTRL, 0x00000007}, 76547a70e6fSCosmin Samoila {REG_MICFIL_FIFO_STAT, 0x00000000}, 76647a70e6fSCosmin Samoila {REG_MICFIL_DATACH0, 0x00000000}, 76747a70e6fSCosmin Samoila {REG_MICFIL_DATACH1, 0x00000000}, 76847a70e6fSCosmin Samoila {REG_MICFIL_DATACH2, 0x00000000}, 76947a70e6fSCosmin Samoila {REG_MICFIL_DATACH3, 0x00000000}, 77047a70e6fSCosmin Samoila {REG_MICFIL_DATACH4, 0x00000000}, 77147a70e6fSCosmin Samoila {REG_MICFIL_DATACH5, 0x00000000}, 77247a70e6fSCosmin Samoila {REG_MICFIL_DATACH6, 0x00000000}, 77347a70e6fSCosmin Samoila {REG_MICFIL_DATACH7, 0x00000000}, 77447a70e6fSCosmin Samoila {REG_MICFIL_DC_CTRL, 0x00000000}, 77547a70e6fSCosmin Samoila {REG_MICFIL_OUT_CTRL, 0x00000000}, 77647a70e6fSCosmin Samoila {REG_MICFIL_OUT_STAT, 0x00000000}, 77747a70e6fSCosmin Samoila {REG_MICFIL_VAD0_CTRL1, 0x00000000}, 77847a70e6fSCosmin Samoila {REG_MICFIL_VAD0_CTRL2, 0x000A0000}, 77947a70e6fSCosmin Samoila {REG_MICFIL_VAD0_STAT, 0x00000000}, 78047a70e6fSCosmin Samoila {REG_MICFIL_VAD0_SCONFIG, 0x00000000}, 78147a70e6fSCosmin Samoila {REG_MICFIL_VAD0_NCONFIG, 0x80000000}, 78247a70e6fSCosmin Samoila {REG_MICFIL_VAD0_NDATA, 0x00000000}, 78347a70e6fSCosmin Samoila {REG_MICFIL_VAD0_ZCD, 0x00000004}, 78447a70e6fSCosmin Samoila }; 78547a70e6fSCosmin Samoila 78647a70e6fSCosmin Samoila static bool fsl_micfil_readable_reg(struct device *dev, unsigned int reg) 78747a70e6fSCosmin Samoila { 78847a70e6fSCosmin Samoila switch (reg) { 78947a70e6fSCosmin Samoila case REG_MICFIL_CTRL1: 79047a70e6fSCosmin Samoila case REG_MICFIL_CTRL2: 79147a70e6fSCosmin Samoila case REG_MICFIL_STAT: 79247a70e6fSCosmin Samoila case REG_MICFIL_FIFO_CTRL: 79347a70e6fSCosmin Samoila case REG_MICFIL_FIFO_STAT: 79447a70e6fSCosmin Samoila case REG_MICFIL_DATACH0: 79547a70e6fSCosmin Samoila case REG_MICFIL_DATACH1: 79647a70e6fSCosmin Samoila case REG_MICFIL_DATACH2: 79747a70e6fSCosmin Samoila case REG_MICFIL_DATACH3: 79847a70e6fSCosmin Samoila case REG_MICFIL_DATACH4: 79947a70e6fSCosmin Samoila case REG_MICFIL_DATACH5: 80047a70e6fSCosmin Samoila case REG_MICFIL_DATACH6: 80147a70e6fSCosmin Samoila case REG_MICFIL_DATACH7: 80247a70e6fSCosmin Samoila case REG_MICFIL_DC_CTRL: 80347a70e6fSCosmin Samoila case REG_MICFIL_OUT_CTRL: 80447a70e6fSCosmin Samoila case REG_MICFIL_OUT_STAT: 80547a70e6fSCosmin Samoila case REG_MICFIL_VAD0_CTRL1: 80647a70e6fSCosmin Samoila case REG_MICFIL_VAD0_CTRL2: 80747a70e6fSCosmin Samoila case REG_MICFIL_VAD0_STAT: 80847a70e6fSCosmin Samoila case REG_MICFIL_VAD0_SCONFIG: 80947a70e6fSCosmin Samoila case REG_MICFIL_VAD0_NCONFIG: 81047a70e6fSCosmin Samoila case REG_MICFIL_VAD0_NDATA: 81147a70e6fSCosmin Samoila case REG_MICFIL_VAD0_ZCD: 81247a70e6fSCosmin Samoila return true; 81347a70e6fSCosmin Samoila default: 81447a70e6fSCosmin Samoila return false; 81547a70e6fSCosmin Samoila } 81647a70e6fSCosmin Samoila } 81747a70e6fSCosmin Samoila 81847a70e6fSCosmin Samoila static bool fsl_micfil_writeable_reg(struct device *dev, unsigned int reg) 81947a70e6fSCosmin Samoila { 82047a70e6fSCosmin Samoila switch (reg) { 82147a70e6fSCosmin Samoila case REG_MICFIL_CTRL1: 82247a70e6fSCosmin Samoila case REG_MICFIL_CTRL2: 82347a70e6fSCosmin Samoila case REG_MICFIL_STAT: /* Write 1 to Clear */ 82447a70e6fSCosmin Samoila case REG_MICFIL_FIFO_CTRL: 82547a70e6fSCosmin Samoila case REG_MICFIL_FIFO_STAT: /* Write 1 to Clear */ 82647a70e6fSCosmin Samoila case REG_MICFIL_DC_CTRL: 82747a70e6fSCosmin Samoila case REG_MICFIL_OUT_CTRL: 82847a70e6fSCosmin Samoila case REG_MICFIL_OUT_STAT: /* Write 1 to Clear */ 82947a70e6fSCosmin Samoila case REG_MICFIL_VAD0_CTRL1: 83047a70e6fSCosmin Samoila case REG_MICFIL_VAD0_CTRL2: 83147a70e6fSCosmin Samoila case REG_MICFIL_VAD0_STAT: /* Write 1 to Clear */ 83247a70e6fSCosmin Samoila case REG_MICFIL_VAD0_SCONFIG: 83347a70e6fSCosmin Samoila case REG_MICFIL_VAD0_NCONFIG: 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_volatile_reg(struct device *dev, unsigned int reg) 84247a70e6fSCosmin Samoila { 84347a70e6fSCosmin Samoila switch (reg) { 84447a70e6fSCosmin Samoila case REG_MICFIL_STAT: 84547a70e6fSCosmin Samoila case REG_MICFIL_DATACH0: 84647a70e6fSCosmin Samoila case REG_MICFIL_DATACH1: 84747a70e6fSCosmin Samoila case REG_MICFIL_DATACH2: 84847a70e6fSCosmin Samoila case REG_MICFIL_DATACH3: 84947a70e6fSCosmin Samoila case REG_MICFIL_DATACH4: 85047a70e6fSCosmin Samoila case REG_MICFIL_DATACH5: 85147a70e6fSCosmin Samoila case REG_MICFIL_DATACH6: 85247a70e6fSCosmin Samoila case REG_MICFIL_DATACH7: 85347a70e6fSCosmin Samoila case REG_MICFIL_VAD0_STAT: 85447a70e6fSCosmin Samoila case REG_MICFIL_VAD0_NDATA: 85547a70e6fSCosmin Samoila return true; 85647a70e6fSCosmin Samoila default: 85747a70e6fSCosmin Samoila return false; 85847a70e6fSCosmin Samoila } 85947a70e6fSCosmin Samoila } 86047a70e6fSCosmin Samoila 86147a70e6fSCosmin Samoila static const struct regmap_config fsl_micfil_regmap_config = { 86247a70e6fSCosmin Samoila .reg_bits = 32, 86347a70e6fSCosmin Samoila .reg_stride = 4, 86447a70e6fSCosmin Samoila .val_bits = 32, 86547a70e6fSCosmin Samoila 86647a70e6fSCosmin Samoila .max_register = REG_MICFIL_VAD0_ZCD, 86747a70e6fSCosmin Samoila .reg_defaults = fsl_micfil_reg_defaults, 86847a70e6fSCosmin Samoila .num_reg_defaults = ARRAY_SIZE(fsl_micfil_reg_defaults), 86947a70e6fSCosmin Samoila .readable_reg = fsl_micfil_readable_reg, 87047a70e6fSCosmin Samoila .volatile_reg = fsl_micfil_volatile_reg, 87147a70e6fSCosmin Samoila .writeable_reg = fsl_micfil_writeable_reg, 87247a70e6fSCosmin Samoila .cache_type = REGCACHE_RBTREE, 87347a70e6fSCosmin Samoila }; 87447a70e6fSCosmin Samoila 87547a70e6fSCosmin Samoila /* END OF REGMAP */ 87647a70e6fSCosmin Samoila 87747a70e6fSCosmin Samoila static irqreturn_t micfil_isr(int irq, void *devid) 87847a70e6fSCosmin Samoila { 87947a70e6fSCosmin Samoila struct fsl_micfil *micfil = (struct fsl_micfil *)devid; 88047a70e6fSCosmin Samoila struct platform_device *pdev = micfil->pdev; 88147a70e6fSCosmin Samoila u32 stat_reg; 88247a70e6fSCosmin Samoila u32 fifo_stat_reg; 88347a70e6fSCosmin Samoila u32 ctrl1_reg; 88447a70e6fSCosmin Samoila bool dma_enabled; 88547a70e6fSCosmin Samoila int i; 88647a70e6fSCosmin Samoila 88747a70e6fSCosmin Samoila regmap_read(micfil->regmap, REG_MICFIL_STAT, &stat_reg); 88847a70e6fSCosmin Samoila regmap_read(micfil->regmap, REG_MICFIL_CTRL1, &ctrl1_reg); 88947a70e6fSCosmin Samoila regmap_read(micfil->regmap, REG_MICFIL_FIFO_STAT, &fifo_stat_reg); 89047a70e6fSCosmin Samoila 89117f2142bSSascha Hauer dma_enabled = FIELD_GET(MICFIL_CTRL1_DISEL, ctrl1_reg) == MICFIL_CTRL1_DISEL_DMA; 89247a70e6fSCosmin Samoila 89347a70e6fSCosmin Samoila /* Channel 0-7 Output Data Flags */ 89447a70e6fSCosmin Samoila for (i = 0; i < MICFIL_OUTPUT_CHANNELS; i++) { 89517f2142bSSascha Hauer if (stat_reg & MICFIL_STAT_CHXF(i)) 89647a70e6fSCosmin Samoila dev_dbg(&pdev->dev, 89747a70e6fSCosmin Samoila "Data available in Data Channel %d\n", i); 89847a70e6fSCosmin Samoila /* if DMA is not enabled, field must be written with 1 89947a70e6fSCosmin Samoila * to clear 90047a70e6fSCosmin Samoila */ 90147a70e6fSCosmin Samoila if (!dma_enabled) 90247a70e6fSCosmin Samoila regmap_write_bits(micfil->regmap, 90347a70e6fSCosmin Samoila REG_MICFIL_STAT, 90417f2142bSSascha Hauer MICFIL_STAT_CHXF(i), 90547a70e6fSCosmin Samoila 1); 90647a70e6fSCosmin Samoila } 90747a70e6fSCosmin Samoila 90847a70e6fSCosmin Samoila for (i = 0; i < MICFIL_FIFO_NUM; i++) { 90917f2142bSSascha Hauer if (fifo_stat_reg & MICFIL_FIFO_STAT_FIFOX_OVER(i)) 91047a70e6fSCosmin Samoila dev_dbg(&pdev->dev, 91147a70e6fSCosmin Samoila "FIFO Overflow Exception flag for channel %d\n", 91247a70e6fSCosmin Samoila i); 91347a70e6fSCosmin Samoila 91417f2142bSSascha Hauer if (fifo_stat_reg & MICFIL_FIFO_STAT_FIFOX_UNDER(i)) 91547a70e6fSCosmin Samoila dev_dbg(&pdev->dev, 91647a70e6fSCosmin Samoila "FIFO Underflow Exception flag for channel %d\n", 91747a70e6fSCosmin Samoila i); 91847a70e6fSCosmin Samoila } 91947a70e6fSCosmin Samoila 92047a70e6fSCosmin Samoila return IRQ_HANDLED; 92147a70e6fSCosmin Samoila } 92247a70e6fSCosmin Samoila 92347a70e6fSCosmin Samoila static irqreturn_t micfil_err_isr(int irq, void *devid) 92447a70e6fSCosmin Samoila { 92547a70e6fSCosmin Samoila struct fsl_micfil *micfil = (struct fsl_micfil *)devid; 92647a70e6fSCosmin Samoila struct platform_device *pdev = micfil->pdev; 92747a70e6fSCosmin Samoila u32 stat_reg; 92847a70e6fSCosmin Samoila 92947a70e6fSCosmin Samoila regmap_read(micfil->regmap, REG_MICFIL_STAT, &stat_reg); 93047a70e6fSCosmin Samoila 931bd2cffd1SSascha Hauer if (stat_reg & MICFIL_STAT_BSY_FIL) 93247a70e6fSCosmin Samoila dev_dbg(&pdev->dev, "isr: Decimation Filter is running\n"); 93347a70e6fSCosmin Samoila 934bd2cffd1SSascha Hauer if (stat_reg & MICFIL_STAT_FIR_RDY) 93547a70e6fSCosmin Samoila dev_dbg(&pdev->dev, "isr: FIR Filter Data ready\n"); 93647a70e6fSCosmin Samoila 937bd2cffd1SSascha Hauer if (stat_reg & MICFIL_STAT_LOWFREQF) { 93847a70e6fSCosmin Samoila dev_dbg(&pdev->dev, "isr: ipg_clk_app is too low\n"); 93947a70e6fSCosmin Samoila regmap_write_bits(micfil->regmap, REG_MICFIL_STAT, 940bd2cffd1SSascha Hauer MICFIL_STAT_LOWFREQF, 1); 94147a70e6fSCosmin Samoila } 94247a70e6fSCosmin Samoila 94347a70e6fSCosmin Samoila return IRQ_HANDLED; 94447a70e6fSCosmin Samoila } 94547a70e6fSCosmin Samoila 94629dbfeecSShengjiu Wang static irqreturn_t voice_detected_fn(int irq, void *devid) 94729dbfeecSShengjiu Wang { 94829dbfeecSShengjiu Wang struct fsl_micfil *micfil = (struct fsl_micfil *)devid; 94929dbfeecSShengjiu Wang struct snd_kcontrol *kctl; 95029dbfeecSShengjiu Wang 95129dbfeecSShengjiu Wang if (!micfil->card) 95229dbfeecSShengjiu Wang return IRQ_HANDLED; 95329dbfeecSShengjiu Wang 95429dbfeecSShengjiu Wang kctl = snd_soc_card_get_kcontrol(micfil->card, "VAD Detected"); 95529dbfeecSShengjiu Wang if (!kctl) 95629dbfeecSShengjiu Wang return IRQ_HANDLED; 95729dbfeecSShengjiu Wang 95829dbfeecSShengjiu Wang if (micfil->vad_detected) 95929dbfeecSShengjiu Wang snd_ctl_notify(micfil->card->snd_card, 96029dbfeecSShengjiu Wang SNDRV_CTL_EVENT_MASK_VALUE, 96129dbfeecSShengjiu Wang &kctl->id); 96229dbfeecSShengjiu Wang 96329dbfeecSShengjiu Wang return IRQ_HANDLED; 96429dbfeecSShengjiu Wang } 96529dbfeecSShengjiu Wang 96629dbfeecSShengjiu Wang static irqreturn_t hwvad_isr(int irq, void *devid) 96729dbfeecSShengjiu Wang { 96829dbfeecSShengjiu Wang struct fsl_micfil *micfil = (struct fsl_micfil *)devid; 96929dbfeecSShengjiu Wang struct device *dev = &micfil->pdev->dev; 97029dbfeecSShengjiu Wang u32 vad0_reg; 97129dbfeecSShengjiu Wang int ret; 97229dbfeecSShengjiu Wang 97329dbfeecSShengjiu Wang regmap_read(micfil->regmap, REG_MICFIL_VAD0_STAT, &vad0_reg); 97429dbfeecSShengjiu Wang 97529dbfeecSShengjiu Wang /* 97629dbfeecSShengjiu Wang * The only difference between MICFIL_VAD0_STAT_EF and 97729dbfeecSShengjiu Wang * MICFIL_VAD0_STAT_IF is that the former requires Write 97829dbfeecSShengjiu Wang * 1 to Clear. Since both flags are set, it is enough 97929dbfeecSShengjiu Wang * to only read one of them 98029dbfeecSShengjiu Wang */ 98129dbfeecSShengjiu Wang if (vad0_reg & MICFIL_VAD0_STAT_IF) { 98229dbfeecSShengjiu Wang /* Write 1 to clear */ 98329dbfeecSShengjiu Wang regmap_write_bits(micfil->regmap, REG_MICFIL_VAD0_STAT, 98429dbfeecSShengjiu Wang MICFIL_VAD0_STAT_IF, 98529dbfeecSShengjiu Wang MICFIL_VAD0_STAT_IF); 98629dbfeecSShengjiu Wang 98729dbfeecSShengjiu Wang micfil->vad_detected = 1; 98829dbfeecSShengjiu Wang } 98929dbfeecSShengjiu Wang 99029dbfeecSShengjiu Wang ret = fsl_micfil_hwvad_disable(micfil); 99129dbfeecSShengjiu Wang if (ret) 99229dbfeecSShengjiu Wang dev_err(dev, "Failed to disable hwvad\n"); 99329dbfeecSShengjiu Wang 99429dbfeecSShengjiu Wang return IRQ_WAKE_THREAD; 99529dbfeecSShengjiu Wang } 99629dbfeecSShengjiu Wang 99729dbfeecSShengjiu Wang static irqreturn_t hwvad_err_isr(int irq, void *devid) 99829dbfeecSShengjiu Wang { 99929dbfeecSShengjiu Wang struct fsl_micfil *micfil = (struct fsl_micfil *)devid; 100029dbfeecSShengjiu Wang struct device *dev = &micfil->pdev->dev; 100129dbfeecSShengjiu Wang u32 vad0_reg; 100229dbfeecSShengjiu Wang 100329dbfeecSShengjiu Wang regmap_read(micfil->regmap, REG_MICFIL_VAD0_STAT, &vad0_reg); 100429dbfeecSShengjiu Wang 100529dbfeecSShengjiu Wang if (vad0_reg & MICFIL_VAD0_STAT_INSATF) 100629dbfeecSShengjiu Wang dev_dbg(dev, "voice activity input overflow/underflow detected\n"); 100729dbfeecSShengjiu Wang 100829dbfeecSShengjiu Wang return IRQ_HANDLED; 100929dbfeecSShengjiu Wang } 101029dbfeecSShengjiu Wang 101147a70e6fSCosmin Samoila static int fsl_micfil_probe(struct platform_device *pdev) 101247a70e6fSCosmin Samoila { 101347a70e6fSCosmin Samoila struct device_node *np = pdev->dev.of_node; 101447a70e6fSCosmin Samoila struct fsl_micfil *micfil; 101547a70e6fSCosmin Samoila struct resource *res; 101647a70e6fSCosmin Samoila void __iomem *regs; 101747a70e6fSCosmin Samoila int ret, i; 101847a70e6fSCosmin Samoila 101947a70e6fSCosmin Samoila micfil = devm_kzalloc(&pdev->dev, sizeof(*micfil), GFP_KERNEL); 102047a70e6fSCosmin Samoila if (!micfil) 102147a70e6fSCosmin Samoila return -ENOMEM; 102247a70e6fSCosmin Samoila 102347a70e6fSCosmin Samoila micfil->pdev = pdev; 102447a70e6fSCosmin Samoila strncpy(micfil->name, np->name, sizeof(micfil->name) - 1); 102547a70e6fSCosmin Samoila 1026d7388718SFabio Estevam micfil->soc = of_device_get_match_data(&pdev->dev); 102747a70e6fSCosmin Samoila 102847a70e6fSCosmin Samoila /* ipg_clk is used to control the registers 102947a70e6fSCosmin Samoila * ipg_clk_app is used to operate the filter 103047a70e6fSCosmin Samoila */ 103147a70e6fSCosmin Samoila micfil->mclk = devm_clk_get(&pdev->dev, "ipg_clk_app"); 103247a70e6fSCosmin Samoila if (IS_ERR(micfil->mclk)) { 103347a70e6fSCosmin Samoila dev_err(&pdev->dev, "failed to get core clock: %ld\n", 103447a70e6fSCosmin Samoila PTR_ERR(micfil->mclk)); 103547a70e6fSCosmin Samoila return PTR_ERR(micfil->mclk); 103647a70e6fSCosmin Samoila } 103747a70e6fSCosmin Samoila 1038b5cf28f7SShengjiu Wang micfil->busclk = devm_clk_get(&pdev->dev, "ipg_clk"); 1039b5cf28f7SShengjiu Wang if (IS_ERR(micfil->busclk)) { 1040b5cf28f7SShengjiu Wang dev_err(&pdev->dev, "failed to get ipg clock: %ld\n", 1041b5cf28f7SShengjiu Wang PTR_ERR(micfil->busclk)); 1042b5cf28f7SShengjiu Wang return PTR_ERR(micfil->busclk); 1043b5cf28f7SShengjiu Wang } 1044b5cf28f7SShengjiu Wang 104593f54100SShengjiu Wang fsl_asoc_get_pll_clocks(&pdev->dev, &micfil->pll8k_clk, 104693f54100SShengjiu Wang &micfil->pll11k_clk); 104793f54100SShengjiu Wang 104847a70e6fSCosmin Samoila /* init regmap */ 1049d9bf1e79SYang Yingliang regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 105047a70e6fSCosmin Samoila if (IS_ERR(regs)) 105147a70e6fSCosmin Samoila return PTR_ERR(regs); 105247a70e6fSCosmin Samoila 1053b5cf28f7SShengjiu Wang micfil->regmap = devm_regmap_init_mmio(&pdev->dev, 105447a70e6fSCosmin Samoila regs, 105547a70e6fSCosmin Samoila &fsl_micfil_regmap_config); 105647a70e6fSCosmin Samoila if (IS_ERR(micfil->regmap)) { 105747a70e6fSCosmin Samoila dev_err(&pdev->dev, "failed to init MICFIL regmap: %ld\n", 105847a70e6fSCosmin Samoila PTR_ERR(micfil->regmap)); 105947a70e6fSCosmin Samoila return PTR_ERR(micfil->regmap); 106047a70e6fSCosmin Samoila } 106147a70e6fSCosmin Samoila 106247a70e6fSCosmin Samoila /* dataline mask for RX */ 106347a70e6fSCosmin Samoila ret = of_property_read_u32_index(np, 106447a70e6fSCosmin Samoila "fsl,dataline", 106547a70e6fSCosmin Samoila 0, 106647a70e6fSCosmin Samoila &micfil->dataline); 106747a70e6fSCosmin Samoila if (ret) 106847a70e6fSCosmin Samoila micfil->dataline = 1; 106947a70e6fSCosmin Samoila 107047a70e6fSCosmin Samoila if (micfil->dataline & ~micfil->soc->dataline) { 107147a70e6fSCosmin Samoila dev_err(&pdev->dev, "dataline setting error, Mask is 0x%X\n", 107247a70e6fSCosmin Samoila micfil->soc->dataline); 107347a70e6fSCosmin Samoila return -EINVAL; 107447a70e6fSCosmin Samoila } 107547a70e6fSCosmin Samoila 107647a70e6fSCosmin Samoila /* get IRQs */ 107747a70e6fSCosmin Samoila for (i = 0; i < MICFIL_IRQ_LINES; i++) { 107847a70e6fSCosmin Samoila micfil->irq[i] = platform_get_irq(pdev, i); 107983b35f45STang Bin if (micfil->irq[i] < 0) 108047a70e6fSCosmin Samoila return micfil->irq[i]; 108147a70e6fSCosmin Samoila } 108247a70e6fSCosmin Samoila 1083a62ed960SFabio Estevam /* Digital Microphone interface interrupt */ 108447a70e6fSCosmin Samoila ret = devm_request_irq(&pdev->dev, micfil->irq[0], 1085cbd090faSSascha Hauer micfil_isr, IRQF_SHARED, 108647a70e6fSCosmin Samoila micfil->name, micfil); 108747a70e6fSCosmin Samoila if (ret) { 108847a70e6fSCosmin Samoila dev_err(&pdev->dev, "failed to claim mic interface irq %u\n", 108947a70e6fSCosmin Samoila micfil->irq[0]); 109047a70e6fSCosmin Samoila return ret; 109147a70e6fSCosmin Samoila } 109247a70e6fSCosmin Samoila 1093a62ed960SFabio Estevam /* Digital Microphone interface error interrupt */ 109447a70e6fSCosmin Samoila ret = devm_request_irq(&pdev->dev, micfil->irq[1], 1095cbd090faSSascha Hauer micfil_err_isr, IRQF_SHARED, 109647a70e6fSCosmin Samoila micfil->name, micfil); 109747a70e6fSCosmin Samoila if (ret) { 109847a70e6fSCosmin Samoila dev_err(&pdev->dev, "failed to claim mic interface error irq %u\n", 109947a70e6fSCosmin Samoila micfil->irq[1]); 110047a70e6fSCosmin Samoila return ret; 110147a70e6fSCosmin Samoila } 110247a70e6fSCosmin Samoila 110329dbfeecSShengjiu Wang /* Digital Microphone interface voice activity detector event */ 110429dbfeecSShengjiu Wang ret = devm_request_threaded_irq(&pdev->dev, micfil->irq[2], 110529dbfeecSShengjiu Wang hwvad_isr, voice_detected_fn, 110629dbfeecSShengjiu Wang IRQF_SHARED, micfil->name, micfil); 110729dbfeecSShengjiu Wang if (ret) { 110829dbfeecSShengjiu Wang dev_err(&pdev->dev, "failed to claim hwvad event irq %u\n", 110929dbfeecSShengjiu Wang micfil->irq[0]); 111029dbfeecSShengjiu Wang return ret; 111129dbfeecSShengjiu Wang } 111229dbfeecSShengjiu Wang 111329dbfeecSShengjiu Wang /* Digital Microphone interface voice activity detector error */ 111429dbfeecSShengjiu Wang ret = devm_request_irq(&pdev->dev, micfil->irq[3], 111529dbfeecSShengjiu Wang hwvad_err_isr, IRQF_SHARED, 111629dbfeecSShengjiu Wang micfil->name, micfil); 111729dbfeecSShengjiu Wang if (ret) { 111829dbfeecSShengjiu Wang dev_err(&pdev->dev, "failed to claim hwvad error irq %u\n", 111929dbfeecSShengjiu Wang micfil->irq[1]); 112029dbfeecSShengjiu Wang return ret; 112129dbfeecSShengjiu Wang } 112229dbfeecSShengjiu Wang 112347a70e6fSCosmin Samoila micfil->dma_params_rx.chan_name = "rx"; 112447a70e6fSCosmin Samoila micfil->dma_params_rx.addr = res->start + REG_MICFIL_DATACH0; 112547a70e6fSCosmin Samoila micfil->dma_params_rx.maxburst = MICFIL_DMA_MAXBURST_RX; 112647a70e6fSCosmin Samoila 112747a70e6fSCosmin Samoila platform_set_drvdata(pdev, micfil); 112847a70e6fSCosmin Samoila 112947a70e6fSCosmin Samoila pm_runtime_enable(&pdev->dev); 1130b5cf28f7SShengjiu Wang regcache_cache_only(micfil->regmap, true); 113147a70e6fSCosmin Samoila 11320adf2920SShengjiu Wang /* 11330adf2920SShengjiu Wang * Register platform component before registering cpu dai for there 11340adf2920SShengjiu Wang * is not defer probe for platform component in snd_soc_add_pcm_runtime(). 11350adf2920SShengjiu Wang */ 11360adf2920SShengjiu Wang ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); 11370adf2920SShengjiu Wang if (ret) { 11380adf2920SShengjiu Wang dev_err(&pdev->dev, "failed to pcm register\n"); 11390adf2920SShengjiu Wang return ret; 11400adf2920SShengjiu Wang } 11410adf2920SShengjiu Wang 1142cb05dac1SShengjiu Wang fsl_micfil_dai.capture.formats = micfil->soc->formats; 1143cb05dac1SShengjiu Wang 114447a70e6fSCosmin Samoila ret = devm_snd_soc_register_component(&pdev->dev, &fsl_micfil_component, 114547a70e6fSCosmin Samoila &fsl_micfil_dai, 1); 114647a70e6fSCosmin Samoila if (ret) { 114747a70e6fSCosmin Samoila dev_err(&pdev->dev, "failed to register component %s\n", 114847a70e6fSCosmin Samoila fsl_micfil_component.name); 114947a70e6fSCosmin Samoila } 115047a70e6fSCosmin Samoila 115147a70e6fSCosmin Samoila return ret; 115247a70e6fSCosmin Samoila } 115347a70e6fSCosmin Samoila 115447a70e6fSCosmin Samoila static int __maybe_unused fsl_micfil_runtime_suspend(struct device *dev) 115547a70e6fSCosmin Samoila { 115647a70e6fSCosmin Samoila struct fsl_micfil *micfil = dev_get_drvdata(dev); 115747a70e6fSCosmin Samoila 115847a70e6fSCosmin Samoila regcache_cache_only(micfil->regmap, true); 115947a70e6fSCosmin Samoila 116047a70e6fSCosmin Samoila clk_disable_unprepare(micfil->mclk); 1161b5cf28f7SShengjiu Wang clk_disable_unprepare(micfil->busclk); 116247a70e6fSCosmin Samoila 116347a70e6fSCosmin Samoila return 0; 116447a70e6fSCosmin Samoila } 116547a70e6fSCosmin Samoila 116647a70e6fSCosmin Samoila static int __maybe_unused fsl_micfil_runtime_resume(struct device *dev) 116747a70e6fSCosmin Samoila { 116847a70e6fSCosmin Samoila struct fsl_micfil *micfil = dev_get_drvdata(dev); 116947a70e6fSCosmin Samoila int ret; 117047a70e6fSCosmin Samoila 1171b5cf28f7SShengjiu Wang ret = clk_prepare_enable(micfil->busclk); 117247a70e6fSCosmin Samoila if (ret < 0) 117347a70e6fSCosmin Samoila return ret; 117447a70e6fSCosmin Samoila 1175b5cf28f7SShengjiu Wang ret = clk_prepare_enable(micfil->mclk); 1176b5cf28f7SShengjiu Wang if (ret < 0) { 1177b5cf28f7SShengjiu Wang clk_disable_unprepare(micfil->busclk); 1178b5cf28f7SShengjiu Wang return ret; 1179b5cf28f7SShengjiu Wang } 1180b5cf28f7SShengjiu Wang 118147a70e6fSCosmin Samoila regcache_cache_only(micfil->regmap, false); 118247a70e6fSCosmin Samoila regcache_mark_dirty(micfil->regmap); 118347a70e6fSCosmin Samoila regcache_sync(micfil->regmap); 118447a70e6fSCosmin Samoila 118547a70e6fSCosmin Samoila return 0; 118647a70e6fSCosmin Samoila } 118747a70e6fSCosmin Samoila 118847a70e6fSCosmin Samoila static int __maybe_unused fsl_micfil_suspend(struct device *dev) 118947a70e6fSCosmin Samoila { 119047a70e6fSCosmin Samoila pm_runtime_force_suspend(dev); 119147a70e6fSCosmin Samoila 119247a70e6fSCosmin Samoila return 0; 119347a70e6fSCosmin Samoila } 119447a70e6fSCosmin Samoila 119547a70e6fSCosmin Samoila static int __maybe_unused fsl_micfil_resume(struct device *dev) 119647a70e6fSCosmin Samoila { 119747a70e6fSCosmin Samoila pm_runtime_force_resume(dev); 119847a70e6fSCosmin Samoila 119947a70e6fSCosmin Samoila return 0; 120047a70e6fSCosmin Samoila } 120147a70e6fSCosmin Samoila 120247a70e6fSCosmin Samoila static const struct dev_pm_ops fsl_micfil_pm_ops = { 120347a70e6fSCosmin Samoila SET_RUNTIME_PM_OPS(fsl_micfil_runtime_suspend, 120447a70e6fSCosmin Samoila fsl_micfil_runtime_resume, 120547a70e6fSCosmin Samoila NULL) 120647a70e6fSCosmin Samoila SET_SYSTEM_SLEEP_PM_OPS(fsl_micfil_suspend, 120747a70e6fSCosmin Samoila fsl_micfil_resume) 120847a70e6fSCosmin Samoila }; 120947a70e6fSCosmin Samoila 121047a70e6fSCosmin Samoila static struct platform_driver fsl_micfil_driver = { 121147a70e6fSCosmin Samoila .probe = fsl_micfil_probe, 121247a70e6fSCosmin Samoila .driver = { 121347a70e6fSCosmin Samoila .name = "fsl-micfil-dai", 121447a70e6fSCosmin Samoila .pm = &fsl_micfil_pm_ops, 121547a70e6fSCosmin Samoila .of_match_table = fsl_micfil_dt_ids, 121647a70e6fSCosmin Samoila }, 121747a70e6fSCosmin Samoila }; 121847a70e6fSCosmin Samoila module_platform_driver(fsl_micfil_driver); 121947a70e6fSCosmin Samoila 122047a70e6fSCosmin Samoila MODULE_AUTHOR("Cosmin-Gabriel Samoila <cosmin.samoila@nxp.com>"); 122147a70e6fSCosmin Samoila MODULE_DESCRIPTION("NXP PDM Microphone Interface (MICFIL) driver"); 122247a70e6fSCosmin Samoila MODULE_LICENSE("GPL v2"); 1223