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; 50*29dbfeecSShengjiu 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; 56*29dbfeecSShengjiu Wang int vad_init_mode; 57*29dbfeecSShengjiu Wang int vad_enabled; 58*29dbfeecSShengjiu 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 8547a70e6fSCosmin Samoila static const struct of_device_id fsl_micfil_dt_ids[] = { 8647a70e6fSCosmin Samoila { .compatible = "fsl,imx8mm-micfil", .data = &fsl_micfil_imx8mm }, 87cb05dac1SShengjiu Wang { .compatible = "fsl,imx8mp-micfil", .data = &fsl_micfil_imx8mp }, 8847a70e6fSCosmin Samoila {} 8947a70e6fSCosmin Samoila }; 9047a70e6fSCosmin Samoila MODULE_DEVICE_TABLE(of, fsl_micfil_dt_ids); 9147a70e6fSCosmin Samoila 9247a70e6fSCosmin Samoila static const char * const micfil_quality_select_texts[] = { 93bea1d61dSSascha Hauer [QUALITY_HIGH] = "High", 94bea1d61dSSascha Hauer [QUALITY_MEDIUM] = "Medium", 95bea1d61dSSascha Hauer [QUALITY_LOW] = "Low", 96bea1d61dSSascha Hauer [QUALITY_VLOW0] = "VLow0", 97bea1d61dSSascha Hauer [QUALITY_VLOW1] = "Vlow1", 98bea1d61dSSascha Hauer [QUALITY_VLOW2] = "Vlow2", 9947a70e6fSCosmin Samoila }; 10047a70e6fSCosmin Samoila 10147a70e6fSCosmin Samoila static const struct soc_enum fsl_micfil_quality_enum = 102bea1d61dSSascha Hauer SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_quality_select_texts), 10347a70e6fSCosmin Samoila micfil_quality_select_texts); 10447a70e6fSCosmin Samoila 10547a70e6fSCosmin Samoila static DECLARE_TLV_DB_SCALE(gain_tlv, 0, 100, 0); 10647a70e6fSCosmin Samoila 107bea1d61dSSascha Hauer static int micfil_set_quality(struct fsl_micfil *micfil) 108bea1d61dSSascha Hauer { 109bea1d61dSSascha Hauer u32 qsel; 110bea1d61dSSascha Hauer 111bea1d61dSSascha Hauer switch (micfil->quality) { 112bea1d61dSSascha Hauer case QUALITY_HIGH: 113bea1d61dSSascha Hauer qsel = MICFIL_QSEL_HIGH_QUALITY; 114bea1d61dSSascha Hauer break; 115bea1d61dSSascha Hauer case QUALITY_MEDIUM: 116bea1d61dSSascha Hauer qsel = MICFIL_QSEL_MEDIUM_QUALITY; 117bea1d61dSSascha Hauer break; 118bea1d61dSSascha Hauer case QUALITY_LOW: 119bea1d61dSSascha Hauer qsel = MICFIL_QSEL_LOW_QUALITY; 120bea1d61dSSascha Hauer break; 121bea1d61dSSascha Hauer case QUALITY_VLOW0: 122bea1d61dSSascha Hauer qsel = MICFIL_QSEL_VLOW0_QUALITY; 123bea1d61dSSascha Hauer break; 124bea1d61dSSascha Hauer case QUALITY_VLOW1: 125bea1d61dSSascha Hauer qsel = MICFIL_QSEL_VLOW1_QUALITY; 126bea1d61dSSascha Hauer break; 127bea1d61dSSascha Hauer case QUALITY_VLOW2: 128bea1d61dSSascha Hauer qsel = MICFIL_QSEL_VLOW2_QUALITY; 129bea1d61dSSascha Hauer break; 130bea1d61dSSascha Hauer } 131bea1d61dSSascha Hauer 132bea1d61dSSascha Hauer return regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2, 133bea1d61dSSascha Hauer MICFIL_CTRL2_QSEL, 134bea1d61dSSascha Hauer FIELD_PREP(MICFIL_CTRL2_QSEL, qsel)); 135bea1d61dSSascha Hauer } 136bea1d61dSSascha Hauer 137bea1d61dSSascha Hauer static int micfil_quality_get(struct snd_kcontrol *kcontrol, 138bea1d61dSSascha Hauer struct snd_ctl_elem_value *ucontrol) 139bea1d61dSSascha Hauer { 140bea1d61dSSascha Hauer struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); 141bea1d61dSSascha Hauer struct fsl_micfil *micfil = snd_soc_component_get_drvdata(cmpnt); 142bea1d61dSSascha Hauer 143bea1d61dSSascha Hauer ucontrol->value.integer.value[0] = micfil->quality; 144bea1d61dSSascha Hauer 145bea1d61dSSascha Hauer return 0; 146bea1d61dSSascha Hauer } 147bea1d61dSSascha Hauer 148bea1d61dSSascha Hauer static int micfil_quality_set(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 micfil->quality = ucontrol->value.integer.value[0]; 155bea1d61dSSascha Hauer 156bea1d61dSSascha Hauer return micfil_set_quality(micfil); 157bea1d61dSSascha Hauer } 158bea1d61dSSascha Hauer 159*29dbfeecSShengjiu Wang static const char * const micfil_hwvad_enable[] = { 160*29dbfeecSShengjiu Wang "Disable (Record only)", 161*29dbfeecSShengjiu Wang "Enable (Record with Vad)", 162*29dbfeecSShengjiu Wang }; 163*29dbfeecSShengjiu Wang 164*29dbfeecSShengjiu Wang static const char * const micfil_hwvad_init_mode[] = { 165*29dbfeecSShengjiu Wang "Envelope mode", "Energy mode", 166*29dbfeecSShengjiu Wang }; 167*29dbfeecSShengjiu Wang 168*29dbfeecSShengjiu Wang static const char * const micfil_hwvad_hpf_texts[] = { 169*29dbfeecSShengjiu Wang "Filter bypass", 170*29dbfeecSShengjiu Wang "Cut-off @1750Hz", 171*29dbfeecSShengjiu Wang "Cut-off @215Hz", 172*29dbfeecSShengjiu Wang "Cut-off @102Hz", 173*29dbfeecSShengjiu Wang }; 174*29dbfeecSShengjiu Wang 175*29dbfeecSShengjiu Wang /* 176*29dbfeecSShengjiu Wang * DC Remover Control 177*29dbfeecSShengjiu Wang * Filter Bypassed 1 1 178*29dbfeecSShengjiu Wang * Cut-off @21Hz 0 0 179*29dbfeecSShengjiu Wang * Cut-off @83Hz 0 1 180*29dbfeecSShengjiu Wang * Cut-off @152HZ 1 0 181*29dbfeecSShengjiu Wang */ 182*29dbfeecSShengjiu Wang static const char * const micfil_dc_remover_texts[] = { 183*29dbfeecSShengjiu Wang "Cut-off @21Hz", "Cut-off @83Hz", 184*29dbfeecSShengjiu Wang "Cut-off @152Hz", "Bypass", 185*29dbfeecSShengjiu Wang }; 186*29dbfeecSShengjiu Wang 187*29dbfeecSShengjiu Wang static const struct soc_enum hwvad_enable_enum = 188*29dbfeecSShengjiu Wang SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_hwvad_enable), 189*29dbfeecSShengjiu Wang micfil_hwvad_enable); 190*29dbfeecSShengjiu Wang static const struct soc_enum hwvad_init_mode_enum = 191*29dbfeecSShengjiu Wang SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_hwvad_init_mode), 192*29dbfeecSShengjiu Wang micfil_hwvad_init_mode); 193*29dbfeecSShengjiu Wang static const struct soc_enum hwvad_hpf_enum = 194*29dbfeecSShengjiu Wang SOC_ENUM_SINGLE(REG_MICFIL_VAD0_CTRL2, 0, 195*29dbfeecSShengjiu Wang ARRAY_SIZE(micfil_hwvad_hpf_texts), 196*29dbfeecSShengjiu Wang micfil_hwvad_hpf_texts); 197*29dbfeecSShengjiu Wang static const struct soc_enum fsl_micfil_dc_remover_enum = 198*29dbfeecSShengjiu Wang SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_dc_remover_texts), 199*29dbfeecSShengjiu Wang micfil_dc_remover_texts); 200*29dbfeecSShengjiu Wang 201*29dbfeecSShengjiu Wang static int micfil_put_dc_remover_state(struct snd_kcontrol *kcontrol, 202*29dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 203*29dbfeecSShengjiu Wang { 204*29dbfeecSShengjiu Wang struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 205*29dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 206*29dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 207*29dbfeecSShengjiu Wang unsigned int *item = ucontrol->value.enumerated.item; 208*29dbfeecSShengjiu Wang int val = snd_soc_enum_item_to_val(e, item[0]); 209*29dbfeecSShengjiu Wang int i = 0, ret = 0; 210*29dbfeecSShengjiu Wang u32 reg_val = 0; 211*29dbfeecSShengjiu Wang 212*29dbfeecSShengjiu Wang if (val < 0 || val > 3) 213*29dbfeecSShengjiu Wang return -EINVAL; 214*29dbfeecSShengjiu Wang 215*29dbfeecSShengjiu Wang micfil->dc_remover = val; 216*29dbfeecSShengjiu Wang 217*29dbfeecSShengjiu Wang /* Calculate total value for all channels */ 218*29dbfeecSShengjiu Wang for (i = 0; i < MICFIL_OUTPUT_CHANNELS; i++) 219*29dbfeecSShengjiu Wang reg_val |= val << MICFIL_DC_CHX_SHIFT(i); 220*29dbfeecSShengjiu Wang 221*29dbfeecSShengjiu Wang /* Update DC Remover mode for all channels */ 222*29dbfeecSShengjiu Wang ret = snd_soc_component_update_bits(comp, REG_MICFIL_DC_CTRL, 223*29dbfeecSShengjiu Wang MICFIL_DC_CTRL_CONFIG, reg_val); 224*29dbfeecSShengjiu Wang if (ret < 0) 225*29dbfeecSShengjiu Wang return ret; 226*29dbfeecSShengjiu Wang 227*29dbfeecSShengjiu Wang return 0; 228*29dbfeecSShengjiu Wang } 229*29dbfeecSShengjiu Wang 230*29dbfeecSShengjiu Wang static int micfil_get_dc_remover_state(struct snd_kcontrol *kcontrol, 231*29dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 232*29dbfeecSShengjiu Wang { 233*29dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 234*29dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 235*29dbfeecSShengjiu Wang 236*29dbfeecSShengjiu Wang ucontrol->value.enumerated.item[0] = micfil->dc_remover; 237*29dbfeecSShengjiu Wang 238*29dbfeecSShengjiu Wang return 0; 239*29dbfeecSShengjiu Wang } 240*29dbfeecSShengjiu Wang 241*29dbfeecSShengjiu Wang static int hwvad_put_enable(struct snd_kcontrol *kcontrol, 242*29dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 243*29dbfeecSShengjiu Wang { 244*29dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 245*29dbfeecSShengjiu Wang struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 246*29dbfeecSShengjiu Wang unsigned int *item = ucontrol->value.enumerated.item; 247*29dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 248*29dbfeecSShengjiu Wang int val = snd_soc_enum_item_to_val(e, item[0]); 249*29dbfeecSShengjiu Wang 250*29dbfeecSShengjiu Wang micfil->vad_enabled = val; 251*29dbfeecSShengjiu Wang 252*29dbfeecSShengjiu Wang return 0; 253*29dbfeecSShengjiu Wang } 254*29dbfeecSShengjiu Wang 255*29dbfeecSShengjiu Wang static int hwvad_get_enable(struct snd_kcontrol *kcontrol, 256*29dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 257*29dbfeecSShengjiu Wang { 258*29dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 259*29dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 260*29dbfeecSShengjiu Wang 261*29dbfeecSShengjiu Wang ucontrol->value.enumerated.item[0] = micfil->vad_enabled; 262*29dbfeecSShengjiu Wang 263*29dbfeecSShengjiu Wang return 0; 264*29dbfeecSShengjiu Wang } 265*29dbfeecSShengjiu Wang 266*29dbfeecSShengjiu Wang static int hwvad_put_init_mode(struct snd_kcontrol *kcontrol, 267*29dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 268*29dbfeecSShengjiu Wang { 269*29dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 270*29dbfeecSShengjiu Wang struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 271*29dbfeecSShengjiu Wang unsigned int *item = ucontrol->value.enumerated.item; 272*29dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 273*29dbfeecSShengjiu Wang int val = snd_soc_enum_item_to_val(e, item[0]); 274*29dbfeecSShengjiu Wang 275*29dbfeecSShengjiu Wang /* 0 - Envelope-based Mode 276*29dbfeecSShengjiu Wang * 1 - Energy-based Mode 277*29dbfeecSShengjiu Wang */ 278*29dbfeecSShengjiu Wang micfil->vad_init_mode = val; 279*29dbfeecSShengjiu Wang 280*29dbfeecSShengjiu Wang return 0; 281*29dbfeecSShengjiu Wang } 282*29dbfeecSShengjiu Wang 283*29dbfeecSShengjiu Wang static int hwvad_get_init_mode(struct snd_kcontrol *kcontrol, 284*29dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 285*29dbfeecSShengjiu Wang { 286*29dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 287*29dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 288*29dbfeecSShengjiu Wang 289*29dbfeecSShengjiu Wang ucontrol->value.enumerated.item[0] = micfil->vad_init_mode; 290*29dbfeecSShengjiu Wang 291*29dbfeecSShengjiu Wang return 0; 292*29dbfeecSShengjiu Wang } 293*29dbfeecSShengjiu Wang 294*29dbfeecSShengjiu Wang static int hwvad_detected(struct snd_kcontrol *kcontrol, 295*29dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 296*29dbfeecSShengjiu Wang { 297*29dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 298*29dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 299*29dbfeecSShengjiu Wang 300*29dbfeecSShengjiu Wang ucontrol->value.enumerated.item[0] = micfil->vad_detected; 301*29dbfeecSShengjiu Wang 302*29dbfeecSShengjiu Wang return 0; 303*29dbfeecSShengjiu Wang } 304*29dbfeecSShengjiu Wang 30547a70e6fSCosmin Samoila static const struct snd_kcontrol_new fsl_micfil_snd_controls[] = { 30647a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH0 Volume", REG_MICFIL_OUT_CTRL, 30747a70e6fSCosmin Samoila MICFIL_OUTGAIN_CHX_SHIFT(0), 0xF, 0x7, gain_tlv), 30847a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH1 Volume", REG_MICFIL_OUT_CTRL, 30947a70e6fSCosmin Samoila MICFIL_OUTGAIN_CHX_SHIFT(1), 0xF, 0x7, gain_tlv), 31047a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH2 Volume", REG_MICFIL_OUT_CTRL, 31147a70e6fSCosmin Samoila MICFIL_OUTGAIN_CHX_SHIFT(2), 0xF, 0x7, gain_tlv), 31247a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH3 Volume", REG_MICFIL_OUT_CTRL, 31347a70e6fSCosmin Samoila MICFIL_OUTGAIN_CHX_SHIFT(3), 0xF, 0x7, gain_tlv), 31447a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH4 Volume", REG_MICFIL_OUT_CTRL, 31547a70e6fSCosmin Samoila MICFIL_OUTGAIN_CHX_SHIFT(4), 0xF, 0x7, gain_tlv), 31647a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH5 Volume", REG_MICFIL_OUT_CTRL, 31747a70e6fSCosmin Samoila MICFIL_OUTGAIN_CHX_SHIFT(5), 0xF, 0x7, gain_tlv), 31847a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH6 Volume", REG_MICFIL_OUT_CTRL, 31947a70e6fSCosmin Samoila MICFIL_OUTGAIN_CHX_SHIFT(6), 0xF, 0x7, gain_tlv), 32047a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH7 Volume", REG_MICFIL_OUT_CTRL, 32147a70e6fSCosmin Samoila MICFIL_OUTGAIN_CHX_SHIFT(7), 0xF, 0x7, gain_tlv), 32247a70e6fSCosmin Samoila SOC_ENUM_EXT("MICFIL Quality Select", 32347a70e6fSCosmin Samoila fsl_micfil_quality_enum, 324bea1d61dSSascha Hauer micfil_quality_get, micfil_quality_set), 325*29dbfeecSShengjiu Wang SOC_ENUM_EXT("HWVAD Enablement Switch", hwvad_enable_enum, 326*29dbfeecSShengjiu Wang hwvad_get_enable, hwvad_put_enable), 327*29dbfeecSShengjiu Wang SOC_ENUM_EXT("HWVAD Initialization Mode", hwvad_init_mode_enum, 328*29dbfeecSShengjiu Wang hwvad_get_init_mode, hwvad_put_init_mode), 329*29dbfeecSShengjiu Wang SOC_ENUM("HWVAD High-Pass Filter", hwvad_hpf_enum), 330*29dbfeecSShengjiu Wang SOC_SINGLE("HWVAD ZCD Switch", REG_MICFIL_VAD0_ZCD, 0, 1, 0), 331*29dbfeecSShengjiu Wang SOC_SINGLE("HWVAD ZCD Auto Threshold Switch", 332*29dbfeecSShengjiu Wang REG_MICFIL_VAD0_ZCD, 2, 1, 0), 333*29dbfeecSShengjiu Wang SOC_ENUM_EXT("MICFIL DC Remover Control", fsl_micfil_dc_remover_enum, 334*29dbfeecSShengjiu Wang micfil_get_dc_remover_state, micfil_put_dc_remover_state), 335*29dbfeecSShengjiu Wang SOC_SINGLE("HWVAD Input Gain", REG_MICFIL_VAD0_CTRL2, 8, 15, 0), 336*29dbfeecSShengjiu Wang SOC_SINGLE("HWVAD Sound Gain", REG_MICFIL_VAD0_SCONFIG, 0, 15, 0), 337*29dbfeecSShengjiu Wang SOC_SINGLE("HWVAD Noise Gain", REG_MICFIL_VAD0_NCONFIG, 0, 15, 0), 338*29dbfeecSShengjiu Wang SOC_SINGLE_RANGE("HWVAD Detector Frame Time", REG_MICFIL_VAD0_CTRL2, 16, 0, 63, 0), 339*29dbfeecSShengjiu Wang SOC_SINGLE("HWVAD Detector Initialization Time", REG_MICFIL_VAD0_CTRL1, 8, 31, 0), 340*29dbfeecSShengjiu Wang SOC_SINGLE("HWVAD Noise Filter Adjustment", REG_MICFIL_VAD0_NCONFIG, 8, 31, 0), 341*29dbfeecSShengjiu Wang SOC_SINGLE("HWVAD ZCD Threshold", REG_MICFIL_VAD0_ZCD, 16, 1023, 0), 342*29dbfeecSShengjiu Wang SOC_SINGLE("HWVAD ZCD Adjustment", REG_MICFIL_VAD0_ZCD, 8, 15, 0), 343*29dbfeecSShengjiu Wang SOC_SINGLE("HWVAD ZCD And Behavior Switch", 344*29dbfeecSShengjiu Wang REG_MICFIL_VAD0_ZCD, 4, 1, 0), 345*29dbfeecSShengjiu Wang SOC_SINGLE_BOOL_EXT("VAD Detected", 0, hwvad_detected, NULL), 34647a70e6fSCosmin Samoila }; 34747a70e6fSCosmin Samoila 34847a70e6fSCosmin Samoila /* The SRES is a self-negated bit which provides the CPU with the 34947a70e6fSCosmin Samoila * capability to initialize the PDM Interface module through the 35047a70e6fSCosmin Samoila * slave-bus interface. This bit always reads as zero, and this 35147a70e6fSCosmin Samoila * bit is only effective when MDIS is cleared 35247a70e6fSCosmin Samoila */ 35347a70e6fSCosmin Samoila static int fsl_micfil_reset(struct device *dev) 35447a70e6fSCosmin Samoila { 35547a70e6fSCosmin Samoila struct fsl_micfil *micfil = dev_get_drvdata(dev); 35647a70e6fSCosmin Samoila int ret; 35747a70e6fSCosmin Samoila 358d46c2127SSascha Hauer ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1, 359d46c2127SSascha Hauer MICFIL_CTRL1_MDIS); 3602c602c7eSSascha Hauer if (ret) 36147a70e6fSCosmin Samoila return ret; 36247a70e6fSCosmin Samoila 363d46c2127SSascha Hauer ret = regmap_set_bits(micfil->regmap, REG_MICFIL_CTRL1, 36447a70e6fSCosmin Samoila MICFIL_CTRL1_SRES); 3652c602c7eSSascha Hauer if (ret) 36647a70e6fSCosmin Samoila return ret; 36747a70e6fSCosmin Samoila 36847a70e6fSCosmin Samoila return 0; 36947a70e6fSCosmin Samoila } 37047a70e6fSCosmin Samoila 37147a70e6fSCosmin Samoila static int fsl_micfil_startup(struct snd_pcm_substream *substream, 37247a70e6fSCosmin Samoila struct snd_soc_dai *dai) 37347a70e6fSCosmin Samoila { 37447a70e6fSCosmin Samoila struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai); 37547a70e6fSCosmin Samoila 37647a70e6fSCosmin Samoila if (!micfil) { 37711106cb3STang Bin dev_err(dai->dev, "micfil dai priv_data not set\n"); 37847a70e6fSCosmin Samoila return -EINVAL; 37947a70e6fSCosmin Samoila } 38047a70e6fSCosmin Samoila 38147a70e6fSCosmin Samoila return 0; 38247a70e6fSCosmin Samoila } 38347a70e6fSCosmin Samoila 384*29dbfeecSShengjiu Wang /* Enable/disable hwvad interrupts */ 385*29dbfeecSShengjiu Wang static int fsl_micfil_configure_hwvad_interrupts(struct fsl_micfil *micfil, int enable) 386*29dbfeecSShengjiu Wang { 387*29dbfeecSShengjiu Wang u32 vadie_reg = enable ? MICFIL_VAD0_CTRL1_IE : 0; 388*29dbfeecSShengjiu Wang u32 vaderie_reg = enable ? MICFIL_VAD0_CTRL1_ERIE : 0; 389*29dbfeecSShengjiu Wang 390*29dbfeecSShengjiu Wang /* Voice Activity Detector Error Interruption */ 391*29dbfeecSShengjiu Wang regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 392*29dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_ERIE, vaderie_reg); 393*29dbfeecSShengjiu Wang 394*29dbfeecSShengjiu Wang /* Voice Activity Detector Interruption */ 395*29dbfeecSShengjiu Wang regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 396*29dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_IE, vadie_reg); 397*29dbfeecSShengjiu Wang 398*29dbfeecSShengjiu Wang return 0; 399*29dbfeecSShengjiu Wang } 400*29dbfeecSShengjiu Wang 401*29dbfeecSShengjiu Wang /* Configuration done only in energy-based initialization mode */ 402*29dbfeecSShengjiu Wang static int fsl_micfil_init_hwvad_energy_mode(struct fsl_micfil *micfil) 403*29dbfeecSShengjiu Wang { 404*29dbfeecSShengjiu Wang /* Keep the VADFRENDIS bitfield cleared. */ 405*29dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2, 406*29dbfeecSShengjiu Wang MICFIL_VAD0_CTRL2_FRENDIS); 407*29dbfeecSShengjiu Wang 408*29dbfeecSShengjiu Wang /* Keep the VADPREFEN bitfield cleared. */ 409*29dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2, 410*29dbfeecSShengjiu Wang MICFIL_VAD0_CTRL2_PREFEN); 411*29dbfeecSShengjiu Wang 412*29dbfeecSShengjiu Wang /* Keep the VADSFILEN bitfield cleared. */ 413*29dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG, 414*29dbfeecSShengjiu Wang MICFIL_VAD0_SCONFIG_SFILEN); 415*29dbfeecSShengjiu Wang 416*29dbfeecSShengjiu Wang /* Keep the VADSMAXEN bitfield cleared. */ 417*29dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG, 418*29dbfeecSShengjiu Wang MICFIL_VAD0_SCONFIG_SMAXEN); 419*29dbfeecSShengjiu Wang 420*29dbfeecSShengjiu Wang /* Keep the VADNFILAUTO bitfield asserted. */ 421*29dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 422*29dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NFILAUT); 423*29dbfeecSShengjiu Wang 424*29dbfeecSShengjiu Wang /* Keep the VADNMINEN bitfield cleared. */ 425*29dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 426*29dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NMINEN); 427*29dbfeecSShengjiu Wang 428*29dbfeecSShengjiu Wang /* Keep the VADNDECEN bitfield cleared. */ 429*29dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 430*29dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NDECEN); 431*29dbfeecSShengjiu Wang 432*29dbfeecSShengjiu Wang /* Keep the VADNOREN bitfield cleared. */ 433*29dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 434*29dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NOREN); 435*29dbfeecSShengjiu Wang 436*29dbfeecSShengjiu Wang return 0; 437*29dbfeecSShengjiu Wang } 438*29dbfeecSShengjiu Wang 439*29dbfeecSShengjiu Wang /* Configuration done only in envelope-based initialization mode */ 440*29dbfeecSShengjiu Wang static int fsl_micfil_init_hwvad_envelope_mode(struct fsl_micfil *micfil) 441*29dbfeecSShengjiu Wang { 442*29dbfeecSShengjiu Wang /* Assert the VADFRENDIS bitfield */ 443*29dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2, 444*29dbfeecSShengjiu Wang MICFIL_VAD0_CTRL2_FRENDIS); 445*29dbfeecSShengjiu Wang 446*29dbfeecSShengjiu Wang /* Assert the VADPREFEN bitfield. */ 447*29dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2, 448*29dbfeecSShengjiu Wang MICFIL_VAD0_CTRL2_PREFEN); 449*29dbfeecSShengjiu Wang 450*29dbfeecSShengjiu Wang /* Assert the VADSFILEN bitfield. */ 451*29dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG, 452*29dbfeecSShengjiu Wang MICFIL_VAD0_SCONFIG_SFILEN); 453*29dbfeecSShengjiu Wang 454*29dbfeecSShengjiu Wang /* Assert the VADSMAXEN bitfield. */ 455*29dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG, 456*29dbfeecSShengjiu Wang MICFIL_VAD0_SCONFIG_SMAXEN); 457*29dbfeecSShengjiu Wang 458*29dbfeecSShengjiu Wang /* Clear the VADNFILAUTO bitfield */ 459*29dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 460*29dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NFILAUT); 461*29dbfeecSShengjiu Wang 462*29dbfeecSShengjiu Wang /* Assert the VADNMINEN bitfield. */ 463*29dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 464*29dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NMINEN); 465*29dbfeecSShengjiu Wang 466*29dbfeecSShengjiu Wang /* Assert the VADNDECEN bitfield. */ 467*29dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 468*29dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NDECEN); 469*29dbfeecSShengjiu Wang 470*29dbfeecSShengjiu Wang /* Assert VADNOREN bitfield. */ 471*29dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 472*29dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NOREN); 473*29dbfeecSShengjiu Wang 474*29dbfeecSShengjiu Wang return 0; 475*29dbfeecSShengjiu Wang } 476*29dbfeecSShengjiu Wang 477*29dbfeecSShengjiu Wang /* 478*29dbfeecSShengjiu Wang * Hardware Voice Active Detection: The HWVAD takes data from the input 479*29dbfeecSShengjiu Wang * of a selected PDM microphone to detect if there is any 480*29dbfeecSShengjiu Wang * voice activity. When a voice activity is detected, an interrupt could 481*29dbfeecSShengjiu Wang * be delivered to the system. Initialization in section 8.4: 482*29dbfeecSShengjiu Wang * Can work in two modes: 483*29dbfeecSShengjiu Wang * -> Eneveope-based mode (section 8.4.1) 484*29dbfeecSShengjiu Wang * -> Energy-based mode (section 8.4.2) 485*29dbfeecSShengjiu Wang * 486*29dbfeecSShengjiu Wang * It is important to remark that the HWVAD detector could be enabled 487*29dbfeecSShengjiu Wang * or reset only when the MICFIL isn't running i.e. when the BSY_FIL 488*29dbfeecSShengjiu Wang * bit in STAT register is cleared 489*29dbfeecSShengjiu Wang */ 490*29dbfeecSShengjiu Wang static int fsl_micfil_hwvad_enable(struct fsl_micfil *micfil) 491*29dbfeecSShengjiu Wang { 492*29dbfeecSShengjiu Wang int ret; 493*29dbfeecSShengjiu Wang 494*29dbfeecSShengjiu Wang micfil->vad_detected = 0; 495*29dbfeecSShengjiu Wang 496*29dbfeecSShengjiu Wang /* envelope-based specific initialization */ 497*29dbfeecSShengjiu Wang if (micfil->vad_init_mode == MICFIL_HWVAD_ENVELOPE_MODE) 498*29dbfeecSShengjiu Wang ret = fsl_micfil_init_hwvad_envelope_mode(micfil); 499*29dbfeecSShengjiu Wang else 500*29dbfeecSShengjiu Wang ret = fsl_micfil_init_hwvad_energy_mode(micfil); 501*29dbfeecSShengjiu Wang if (ret) 502*29dbfeecSShengjiu Wang return ret; 503*29dbfeecSShengjiu Wang 504*29dbfeecSShengjiu Wang /* Voice Activity Detector Internal Filters Initialization*/ 505*29dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 506*29dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_ST10); 507*29dbfeecSShengjiu Wang 508*29dbfeecSShengjiu Wang /* Voice Activity Detector Internal Filter */ 509*29dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 510*29dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_ST10); 511*29dbfeecSShengjiu Wang 512*29dbfeecSShengjiu Wang /* Enable Interrupts */ 513*29dbfeecSShengjiu Wang ret = fsl_micfil_configure_hwvad_interrupts(micfil, 1); 514*29dbfeecSShengjiu Wang if (ret) 515*29dbfeecSShengjiu Wang return ret; 516*29dbfeecSShengjiu Wang 517*29dbfeecSShengjiu Wang /* Voice Activity Detector Reset */ 518*29dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 519*29dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_RST); 520*29dbfeecSShengjiu Wang 521*29dbfeecSShengjiu Wang /* Voice Activity Detector Enabled */ 522*29dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 523*29dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_EN); 524*29dbfeecSShengjiu Wang 525*29dbfeecSShengjiu Wang return 0; 526*29dbfeecSShengjiu Wang } 527*29dbfeecSShengjiu Wang 528*29dbfeecSShengjiu Wang static int fsl_micfil_hwvad_disable(struct fsl_micfil *micfil) 529*29dbfeecSShengjiu Wang { 530*29dbfeecSShengjiu Wang struct device *dev = &micfil->pdev->dev; 531*29dbfeecSShengjiu Wang int ret = 0; 532*29dbfeecSShengjiu Wang 533*29dbfeecSShengjiu Wang /* Disable HWVAD */ 534*29dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 535*29dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_EN); 536*29dbfeecSShengjiu Wang 537*29dbfeecSShengjiu Wang /* Disable hwvad interrupts */ 538*29dbfeecSShengjiu Wang ret = fsl_micfil_configure_hwvad_interrupts(micfil, 0); 539*29dbfeecSShengjiu Wang if (ret) 540*29dbfeecSShengjiu Wang dev_err(dev, "Failed to disable interrupts\n"); 541*29dbfeecSShengjiu Wang 542*29dbfeecSShengjiu Wang return ret; 543*29dbfeecSShengjiu Wang } 544*29dbfeecSShengjiu Wang 54547a70e6fSCosmin Samoila static int fsl_micfil_trigger(struct snd_pcm_substream *substream, int cmd, 54647a70e6fSCosmin Samoila struct snd_soc_dai *dai) 54747a70e6fSCosmin Samoila { 54847a70e6fSCosmin Samoila struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai); 54947a70e6fSCosmin Samoila struct device *dev = &micfil->pdev->dev; 55047a70e6fSCosmin Samoila int ret; 55147a70e6fSCosmin Samoila 55247a70e6fSCosmin Samoila switch (cmd) { 55347a70e6fSCosmin Samoila case SNDRV_PCM_TRIGGER_START: 55447a70e6fSCosmin Samoila case SNDRV_PCM_TRIGGER_RESUME: 55547a70e6fSCosmin Samoila case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 55647a70e6fSCosmin Samoila ret = fsl_micfil_reset(dev); 55747a70e6fSCosmin Samoila if (ret) { 55847a70e6fSCosmin Samoila dev_err(dev, "failed to soft reset\n"); 55947a70e6fSCosmin Samoila return ret; 56047a70e6fSCosmin Samoila } 56147a70e6fSCosmin Samoila 56247a70e6fSCosmin Samoila /* DMA Interrupt Selection - DISEL bits 56347a70e6fSCosmin Samoila * 00 - DMA and IRQ disabled 56447a70e6fSCosmin Samoila * 01 - DMA req enabled 56547a70e6fSCosmin Samoila * 10 - IRQ enabled 56647a70e6fSCosmin Samoila * 11 - reserved 56747a70e6fSCosmin Samoila */ 56847a70e6fSCosmin Samoila ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1, 56917f2142bSSascha Hauer MICFIL_CTRL1_DISEL, 57017f2142bSSascha Hauer FIELD_PREP(MICFIL_CTRL1_DISEL, MICFIL_CTRL1_DISEL_DMA)); 5712c602c7eSSascha Hauer if (ret) 57247a70e6fSCosmin Samoila return ret; 57347a70e6fSCosmin Samoila 57447a70e6fSCosmin Samoila /* Enable the module */ 575d46c2127SSascha Hauer ret = regmap_set_bits(micfil->regmap, REG_MICFIL_CTRL1, 57647a70e6fSCosmin Samoila MICFIL_CTRL1_PDMIEN); 5772c602c7eSSascha Hauer if (ret) 57847a70e6fSCosmin Samoila return ret; 57947a70e6fSCosmin Samoila 580*29dbfeecSShengjiu Wang if (micfil->vad_enabled) 581*29dbfeecSShengjiu Wang fsl_micfil_hwvad_enable(micfil); 582*29dbfeecSShengjiu Wang 58347a70e6fSCosmin Samoila break; 58447a70e6fSCosmin Samoila case SNDRV_PCM_TRIGGER_STOP: 58547a70e6fSCosmin Samoila case SNDRV_PCM_TRIGGER_SUSPEND: 58647a70e6fSCosmin Samoila case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 587*29dbfeecSShengjiu Wang if (micfil->vad_enabled) 588*29dbfeecSShengjiu Wang fsl_micfil_hwvad_disable(micfil); 589*29dbfeecSShengjiu Wang 59047a70e6fSCosmin Samoila /* Disable the module */ 591d46c2127SSascha Hauer ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1, 592d46c2127SSascha Hauer MICFIL_CTRL1_PDMIEN); 5932c602c7eSSascha Hauer if (ret) 59447a70e6fSCosmin Samoila return ret; 59547a70e6fSCosmin Samoila 59647a70e6fSCosmin Samoila ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1, 59717f2142bSSascha Hauer MICFIL_CTRL1_DISEL, 59817f2142bSSascha Hauer FIELD_PREP(MICFIL_CTRL1_DISEL, MICFIL_CTRL1_DISEL_DISABLE)); 5992c602c7eSSascha Hauer if (ret) 60047a70e6fSCosmin Samoila return ret; 60147a70e6fSCosmin Samoila break; 60247a70e6fSCosmin Samoila default: 60347a70e6fSCosmin Samoila return -EINVAL; 60447a70e6fSCosmin Samoila } 60547a70e6fSCosmin Samoila return 0; 60647a70e6fSCosmin Samoila } 60747a70e6fSCosmin Samoila 60893f54100SShengjiu Wang static int fsl_micfil_reparent_rootclk(struct fsl_micfil *micfil, unsigned int sample_rate) 60993f54100SShengjiu Wang { 61093f54100SShengjiu Wang struct device *dev = &micfil->pdev->dev; 61193f54100SShengjiu Wang u64 ratio = sample_rate; 61293f54100SShengjiu Wang struct clk *clk; 61393f54100SShengjiu Wang int ret; 61493f54100SShengjiu Wang 61593f54100SShengjiu Wang /* Get root clock */ 61693f54100SShengjiu Wang clk = micfil->mclk; 61793f54100SShengjiu Wang 61893f54100SShengjiu Wang /* Disable clock first, for it was enabled by pm_runtime */ 61993f54100SShengjiu Wang clk_disable_unprepare(clk); 62093f54100SShengjiu Wang fsl_asoc_reparent_pll_clocks(dev, clk, micfil->pll8k_clk, 62193f54100SShengjiu Wang micfil->pll11k_clk, ratio); 62293f54100SShengjiu Wang ret = clk_prepare_enable(clk); 62393f54100SShengjiu Wang if (ret) 62493f54100SShengjiu Wang return ret; 62593f54100SShengjiu Wang 62693f54100SShengjiu Wang return 0; 62793f54100SShengjiu Wang } 62893f54100SShengjiu Wang 62947a70e6fSCosmin Samoila static int fsl_micfil_hw_params(struct snd_pcm_substream *substream, 63047a70e6fSCosmin Samoila struct snd_pcm_hw_params *params, 63147a70e6fSCosmin Samoila struct snd_soc_dai *dai) 63247a70e6fSCosmin Samoila { 63347a70e6fSCosmin Samoila struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai); 63447a70e6fSCosmin Samoila unsigned int channels = params_channels(params); 63547a70e6fSCosmin Samoila unsigned int rate = params_rate(params); 636cc5ef57dSSascha Hauer int clk_div = 8; 637cc5ef57dSSascha Hauer int osr = MICFIL_OSR_DEFAULT; 63847a70e6fSCosmin Samoila int ret; 63947a70e6fSCosmin Samoila 64047a70e6fSCosmin Samoila /* 1. Disable the module */ 641d46c2127SSascha Hauer ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1, 642d46c2127SSascha Hauer MICFIL_CTRL1_PDMIEN); 6432c602c7eSSascha Hauer if (ret) 64447a70e6fSCosmin Samoila return ret; 64547a70e6fSCosmin Samoila 64647a70e6fSCosmin Samoila /* enable channels */ 64747a70e6fSCosmin Samoila ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1, 64847a70e6fSCosmin Samoila 0xFF, ((1 << channels) - 1)); 6492c602c7eSSascha Hauer if (ret) 65047a70e6fSCosmin Samoila return ret; 65147a70e6fSCosmin Samoila 65293f54100SShengjiu Wang ret = fsl_micfil_reparent_rootclk(micfil, rate); 65393f54100SShengjiu Wang if (ret) 65493f54100SShengjiu Wang return ret; 65593f54100SShengjiu Wang 656cc5ef57dSSascha Hauer ret = clk_set_rate(micfil->mclk, rate * clk_div * osr * 8); 657cc5ef57dSSascha Hauer if (ret) 65847a70e6fSCosmin Samoila return ret; 659cc5ef57dSSascha Hauer 660cc5ef57dSSascha Hauer ret = micfil_set_quality(micfil); 661cc5ef57dSSascha Hauer if (ret) 662cc5ef57dSSascha Hauer return ret; 663cc5ef57dSSascha Hauer 664cc5ef57dSSascha Hauer ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2, 665cc5ef57dSSascha Hauer MICFIL_CTRL2_CLKDIV | MICFIL_CTRL2_CICOSR, 666cc5ef57dSSascha Hauer FIELD_PREP(MICFIL_CTRL2_CLKDIV, clk_div) | 667cc5ef57dSSascha Hauer FIELD_PREP(MICFIL_CTRL2_CICOSR, 16 - osr)); 66847a70e6fSCosmin Samoila 669*29dbfeecSShengjiu Wang /* Configure CIC OSR in VADCICOSR */ 670*29dbfeecSShengjiu Wang regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 671*29dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_CICOSR, 672*29dbfeecSShengjiu Wang FIELD_PREP(MICFIL_VAD0_CTRL1_CICOSR, 16 - osr)); 673*29dbfeecSShengjiu Wang 674*29dbfeecSShengjiu Wang /* Configure source channel in VADCHSEL */ 675*29dbfeecSShengjiu Wang regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 676*29dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_CHSEL, 677*29dbfeecSShengjiu Wang FIELD_PREP(MICFIL_VAD0_CTRL1_CHSEL, (channels - 1))); 678*29dbfeecSShengjiu Wang 6792495ba26SSascha Hauer micfil->dma_params_rx.peripheral_config = &micfil->sdmacfg; 6802495ba26SSascha Hauer micfil->dma_params_rx.peripheral_size = sizeof(micfil->sdmacfg); 6812495ba26SSascha Hauer micfil->sdmacfg.n_fifos_src = channels; 6822495ba26SSascha Hauer micfil->sdmacfg.sw_done = true; 68347a70e6fSCosmin Samoila micfil->dma_params_rx.maxburst = channels * MICFIL_DMA_MAXBURST_RX; 68447a70e6fSCosmin Samoila 68547a70e6fSCosmin Samoila return 0; 68647a70e6fSCosmin Samoila } 68747a70e6fSCosmin Samoila 68838d89a56SRikard Falkeborn static const struct snd_soc_dai_ops fsl_micfil_dai_ops = { 68947a70e6fSCosmin Samoila .startup = fsl_micfil_startup, 69047a70e6fSCosmin Samoila .trigger = fsl_micfil_trigger, 69147a70e6fSCosmin Samoila .hw_params = fsl_micfil_hw_params, 69247a70e6fSCosmin Samoila }; 69347a70e6fSCosmin Samoila 69447a70e6fSCosmin Samoila static int fsl_micfil_dai_probe(struct snd_soc_dai *cpu_dai) 69547a70e6fSCosmin Samoila { 69647a70e6fSCosmin Samoila struct fsl_micfil *micfil = dev_get_drvdata(cpu_dai->dev); 6973b13b143SShengjiu Wang struct device *dev = cpu_dai->dev; 6983b13b143SShengjiu Wang unsigned int val = 0; 6993b13b143SShengjiu Wang int ret, i; 70047a70e6fSCosmin Samoila 7013b13b143SShengjiu Wang micfil->quality = QUALITY_VLOW0; 702*29dbfeecSShengjiu Wang micfil->card = cpu_dai->component->card; 70347a70e6fSCosmin Samoila 7043b13b143SShengjiu Wang /* set default gain to 2 */ 7053b13b143SShengjiu Wang regmap_write(micfil->regmap, REG_MICFIL_OUT_CTRL, 0x22222222); 7063b13b143SShengjiu Wang 7073b13b143SShengjiu Wang /* set DC Remover in bypass mode*/ 7083b13b143SShengjiu Wang for (i = 0; i < MICFIL_OUTPUT_CHANNELS; i++) 7093b13b143SShengjiu Wang val |= MICFIL_DC_BYPASS << MICFIL_DC_CHX_SHIFT(i); 7103b13b143SShengjiu Wang ret = regmap_update_bits(micfil->regmap, REG_MICFIL_DC_CTRL, 7113b13b143SShengjiu Wang MICFIL_DC_CTRL_CONFIG, val); 7123b13b143SShengjiu Wang if (ret) { 7133b13b143SShengjiu Wang dev_err(dev, "failed to set DC Remover mode bits\n"); 7143b13b143SShengjiu Wang return ret; 7153b13b143SShengjiu Wang } 7163b13b143SShengjiu Wang micfil->dc_remover = MICFIL_DC_BYPASS; 71747a70e6fSCosmin Samoila 71847a70e6fSCosmin Samoila snd_soc_dai_init_dma_data(cpu_dai, NULL, 71947a70e6fSCosmin Samoila &micfil->dma_params_rx); 72047a70e6fSCosmin Samoila 72147a70e6fSCosmin Samoila /* FIFO Watermark Control - FIFOWMK*/ 72247a70e6fSCosmin Samoila ret = regmap_update_bits(micfil->regmap, REG_MICFIL_FIFO_CTRL, 72317f2142bSSascha Hauer MICFIL_FIFO_CTRL_FIFOWMK, 72417f2142bSSascha Hauer FIELD_PREP(MICFIL_FIFO_CTRL_FIFOWMK, micfil->soc->fifo_depth - 1)); 7252c602c7eSSascha Hauer if (ret) 72647a70e6fSCosmin Samoila return ret; 72747a70e6fSCosmin Samoila 72847a70e6fSCosmin Samoila return 0; 72947a70e6fSCosmin Samoila } 73047a70e6fSCosmin Samoila 73147a70e6fSCosmin Samoila static struct snd_soc_dai_driver fsl_micfil_dai = { 73247a70e6fSCosmin Samoila .probe = fsl_micfil_dai_probe, 73347a70e6fSCosmin Samoila .capture = { 73447a70e6fSCosmin Samoila .stream_name = "CPU-Capture", 73547a70e6fSCosmin Samoila .channels_min = 1, 73647a70e6fSCosmin Samoila .channels_max = 8, 73799c08cdbSSascha Hauer .rates = SNDRV_PCM_RATE_8000_48000, 73899c08cdbSSascha Hauer .formats = SNDRV_PCM_FMTBIT_S16_LE, 73947a70e6fSCosmin Samoila }, 74047a70e6fSCosmin Samoila .ops = &fsl_micfil_dai_ops, 74147a70e6fSCosmin Samoila }; 74247a70e6fSCosmin Samoila 74347a70e6fSCosmin Samoila static const struct snd_soc_component_driver fsl_micfil_component = { 74447a70e6fSCosmin Samoila .name = "fsl-micfil-dai", 74547a70e6fSCosmin Samoila .controls = fsl_micfil_snd_controls, 74647a70e6fSCosmin Samoila .num_controls = ARRAY_SIZE(fsl_micfil_snd_controls), 747978bd27cSShengjiu Wang .legacy_dai_naming = 1, 74847a70e6fSCosmin Samoila }; 74947a70e6fSCosmin Samoila 75047a70e6fSCosmin Samoila /* REGMAP */ 75147a70e6fSCosmin Samoila static const struct reg_default fsl_micfil_reg_defaults[] = { 75247a70e6fSCosmin Samoila {REG_MICFIL_CTRL1, 0x00000000}, 75347a70e6fSCosmin Samoila {REG_MICFIL_CTRL2, 0x00000000}, 75447a70e6fSCosmin Samoila {REG_MICFIL_STAT, 0x00000000}, 75547a70e6fSCosmin Samoila {REG_MICFIL_FIFO_CTRL, 0x00000007}, 75647a70e6fSCosmin Samoila {REG_MICFIL_FIFO_STAT, 0x00000000}, 75747a70e6fSCosmin Samoila {REG_MICFIL_DATACH0, 0x00000000}, 75847a70e6fSCosmin Samoila {REG_MICFIL_DATACH1, 0x00000000}, 75947a70e6fSCosmin Samoila {REG_MICFIL_DATACH2, 0x00000000}, 76047a70e6fSCosmin Samoila {REG_MICFIL_DATACH3, 0x00000000}, 76147a70e6fSCosmin Samoila {REG_MICFIL_DATACH4, 0x00000000}, 76247a70e6fSCosmin Samoila {REG_MICFIL_DATACH5, 0x00000000}, 76347a70e6fSCosmin Samoila {REG_MICFIL_DATACH6, 0x00000000}, 76447a70e6fSCosmin Samoila {REG_MICFIL_DATACH7, 0x00000000}, 76547a70e6fSCosmin Samoila {REG_MICFIL_DC_CTRL, 0x00000000}, 76647a70e6fSCosmin Samoila {REG_MICFIL_OUT_CTRL, 0x00000000}, 76747a70e6fSCosmin Samoila {REG_MICFIL_OUT_STAT, 0x00000000}, 76847a70e6fSCosmin Samoila {REG_MICFIL_VAD0_CTRL1, 0x00000000}, 76947a70e6fSCosmin Samoila {REG_MICFIL_VAD0_CTRL2, 0x000A0000}, 77047a70e6fSCosmin Samoila {REG_MICFIL_VAD0_STAT, 0x00000000}, 77147a70e6fSCosmin Samoila {REG_MICFIL_VAD0_SCONFIG, 0x00000000}, 77247a70e6fSCosmin Samoila {REG_MICFIL_VAD0_NCONFIG, 0x80000000}, 77347a70e6fSCosmin Samoila {REG_MICFIL_VAD0_NDATA, 0x00000000}, 77447a70e6fSCosmin Samoila {REG_MICFIL_VAD0_ZCD, 0x00000004}, 77547a70e6fSCosmin Samoila }; 77647a70e6fSCosmin Samoila 77747a70e6fSCosmin Samoila static bool fsl_micfil_readable_reg(struct device *dev, unsigned int reg) 77847a70e6fSCosmin Samoila { 77947a70e6fSCosmin Samoila switch (reg) { 78047a70e6fSCosmin Samoila case REG_MICFIL_CTRL1: 78147a70e6fSCosmin Samoila case REG_MICFIL_CTRL2: 78247a70e6fSCosmin Samoila case REG_MICFIL_STAT: 78347a70e6fSCosmin Samoila case REG_MICFIL_FIFO_CTRL: 78447a70e6fSCosmin Samoila case REG_MICFIL_FIFO_STAT: 78547a70e6fSCosmin Samoila case REG_MICFIL_DATACH0: 78647a70e6fSCosmin Samoila case REG_MICFIL_DATACH1: 78747a70e6fSCosmin Samoila case REG_MICFIL_DATACH2: 78847a70e6fSCosmin Samoila case REG_MICFIL_DATACH3: 78947a70e6fSCosmin Samoila case REG_MICFIL_DATACH4: 79047a70e6fSCosmin Samoila case REG_MICFIL_DATACH5: 79147a70e6fSCosmin Samoila case REG_MICFIL_DATACH6: 79247a70e6fSCosmin Samoila case REG_MICFIL_DATACH7: 79347a70e6fSCosmin Samoila case REG_MICFIL_DC_CTRL: 79447a70e6fSCosmin Samoila case REG_MICFIL_OUT_CTRL: 79547a70e6fSCosmin Samoila case REG_MICFIL_OUT_STAT: 79647a70e6fSCosmin Samoila case REG_MICFIL_VAD0_CTRL1: 79747a70e6fSCosmin Samoila case REG_MICFIL_VAD0_CTRL2: 79847a70e6fSCosmin Samoila case REG_MICFIL_VAD0_STAT: 79947a70e6fSCosmin Samoila case REG_MICFIL_VAD0_SCONFIG: 80047a70e6fSCosmin Samoila case REG_MICFIL_VAD0_NCONFIG: 80147a70e6fSCosmin Samoila case REG_MICFIL_VAD0_NDATA: 80247a70e6fSCosmin Samoila case REG_MICFIL_VAD0_ZCD: 80347a70e6fSCosmin Samoila return true; 80447a70e6fSCosmin Samoila default: 80547a70e6fSCosmin Samoila return false; 80647a70e6fSCosmin Samoila } 80747a70e6fSCosmin Samoila } 80847a70e6fSCosmin Samoila 80947a70e6fSCosmin Samoila static bool fsl_micfil_writeable_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: /* Write 1 to Clear */ 81547a70e6fSCosmin Samoila case REG_MICFIL_FIFO_CTRL: 81647a70e6fSCosmin Samoila case REG_MICFIL_FIFO_STAT: /* Write 1 to Clear */ 81747a70e6fSCosmin Samoila case REG_MICFIL_DC_CTRL: 81847a70e6fSCosmin Samoila case REG_MICFIL_OUT_CTRL: 81947a70e6fSCosmin Samoila case REG_MICFIL_OUT_STAT: /* Write 1 to Clear */ 82047a70e6fSCosmin Samoila case REG_MICFIL_VAD0_CTRL1: 82147a70e6fSCosmin Samoila case REG_MICFIL_VAD0_CTRL2: 82247a70e6fSCosmin Samoila case REG_MICFIL_VAD0_STAT: /* Write 1 to Clear */ 82347a70e6fSCosmin Samoila case REG_MICFIL_VAD0_SCONFIG: 82447a70e6fSCosmin Samoila case REG_MICFIL_VAD0_NCONFIG: 82547a70e6fSCosmin Samoila case REG_MICFIL_VAD0_ZCD: 82647a70e6fSCosmin Samoila return true; 82747a70e6fSCosmin Samoila default: 82847a70e6fSCosmin Samoila return false; 82947a70e6fSCosmin Samoila } 83047a70e6fSCosmin Samoila } 83147a70e6fSCosmin Samoila 83247a70e6fSCosmin Samoila static bool fsl_micfil_volatile_reg(struct device *dev, unsigned int reg) 83347a70e6fSCosmin Samoila { 83447a70e6fSCosmin Samoila switch (reg) { 83547a70e6fSCosmin Samoila case REG_MICFIL_STAT: 83647a70e6fSCosmin Samoila case REG_MICFIL_DATACH0: 83747a70e6fSCosmin Samoila case REG_MICFIL_DATACH1: 83847a70e6fSCosmin Samoila case REG_MICFIL_DATACH2: 83947a70e6fSCosmin Samoila case REG_MICFIL_DATACH3: 84047a70e6fSCosmin Samoila case REG_MICFIL_DATACH4: 84147a70e6fSCosmin Samoila case REG_MICFIL_DATACH5: 84247a70e6fSCosmin Samoila case REG_MICFIL_DATACH6: 84347a70e6fSCosmin Samoila case REG_MICFIL_DATACH7: 84447a70e6fSCosmin Samoila case REG_MICFIL_VAD0_STAT: 84547a70e6fSCosmin Samoila case REG_MICFIL_VAD0_NDATA: 84647a70e6fSCosmin Samoila return true; 84747a70e6fSCosmin Samoila default: 84847a70e6fSCosmin Samoila return false; 84947a70e6fSCosmin Samoila } 85047a70e6fSCosmin Samoila } 85147a70e6fSCosmin Samoila 85247a70e6fSCosmin Samoila static const struct regmap_config fsl_micfil_regmap_config = { 85347a70e6fSCosmin Samoila .reg_bits = 32, 85447a70e6fSCosmin Samoila .reg_stride = 4, 85547a70e6fSCosmin Samoila .val_bits = 32, 85647a70e6fSCosmin Samoila 85747a70e6fSCosmin Samoila .max_register = REG_MICFIL_VAD0_ZCD, 85847a70e6fSCosmin Samoila .reg_defaults = fsl_micfil_reg_defaults, 85947a70e6fSCosmin Samoila .num_reg_defaults = ARRAY_SIZE(fsl_micfil_reg_defaults), 86047a70e6fSCosmin Samoila .readable_reg = fsl_micfil_readable_reg, 86147a70e6fSCosmin Samoila .volatile_reg = fsl_micfil_volatile_reg, 86247a70e6fSCosmin Samoila .writeable_reg = fsl_micfil_writeable_reg, 86347a70e6fSCosmin Samoila .cache_type = REGCACHE_RBTREE, 86447a70e6fSCosmin Samoila }; 86547a70e6fSCosmin Samoila 86647a70e6fSCosmin Samoila /* END OF REGMAP */ 86747a70e6fSCosmin Samoila 86847a70e6fSCosmin Samoila static irqreturn_t micfil_isr(int irq, void *devid) 86947a70e6fSCosmin Samoila { 87047a70e6fSCosmin Samoila struct fsl_micfil *micfil = (struct fsl_micfil *)devid; 87147a70e6fSCosmin Samoila struct platform_device *pdev = micfil->pdev; 87247a70e6fSCosmin Samoila u32 stat_reg; 87347a70e6fSCosmin Samoila u32 fifo_stat_reg; 87447a70e6fSCosmin Samoila u32 ctrl1_reg; 87547a70e6fSCosmin Samoila bool dma_enabled; 87647a70e6fSCosmin Samoila int i; 87747a70e6fSCosmin Samoila 87847a70e6fSCosmin Samoila regmap_read(micfil->regmap, REG_MICFIL_STAT, &stat_reg); 87947a70e6fSCosmin Samoila regmap_read(micfil->regmap, REG_MICFIL_CTRL1, &ctrl1_reg); 88047a70e6fSCosmin Samoila regmap_read(micfil->regmap, REG_MICFIL_FIFO_STAT, &fifo_stat_reg); 88147a70e6fSCosmin Samoila 88217f2142bSSascha Hauer dma_enabled = FIELD_GET(MICFIL_CTRL1_DISEL, ctrl1_reg) == MICFIL_CTRL1_DISEL_DMA; 88347a70e6fSCosmin Samoila 88447a70e6fSCosmin Samoila /* Channel 0-7 Output Data Flags */ 88547a70e6fSCosmin Samoila for (i = 0; i < MICFIL_OUTPUT_CHANNELS; i++) { 88617f2142bSSascha Hauer if (stat_reg & MICFIL_STAT_CHXF(i)) 88747a70e6fSCosmin Samoila dev_dbg(&pdev->dev, 88847a70e6fSCosmin Samoila "Data available in Data Channel %d\n", i); 88947a70e6fSCosmin Samoila /* if DMA is not enabled, field must be written with 1 89047a70e6fSCosmin Samoila * to clear 89147a70e6fSCosmin Samoila */ 89247a70e6fSCosmin Samoila if (!dma_enabled) 89347a70e6fSCosmin Samoila regmap_write_bits(micfil->regmap, 89447a70e6fSCosmin Samoila REG_MICFIL_STAT, 89517f2142bSSascha Hauer MICFIL_STAT_CHXF(i), 89647a70e6fSCosmin Samoila 1); 89747a70e6fSCosmin Samoila } 89847a70e6fSCosmin Samoila 89947a70e6fSCosmin Samoila for (i = 0; i < MICFIL_FIFO_NUM; i++) { 90017f2142bSSascha Hauer if (fifo_stat_reg & MICFIL_FIFO_STAT_FIFOX_OVER(i)) 90147a70e6fSCosmin Samoila dev_dbg(&pdev->dev, 90247a70e6fSCosmin Samoila "FIFO Overflow Exception flag for channel %d\n", 90347a70e6fSCosmin Samoila i); 90447a70e6fSCosmin Samoila 90517f2142bSSascha Hauer if (fifo_stat_reg & MICFIL_FIFO_STAT_FIFOX_UNDER(i)) 90647a70e6fSCosmin Samoila dev_dbg(&pdev->dev, 90747a70e6fSCosmin Samoila "FIFO Underflow Exception flag for channel %d\n", 90847a70e6fSCosmin Samoila i); 90947a70e6fSCosmin Samoila } 91047a70e6fSCosmin Samoila 91147a70e6fSCosmin Samoila return IRQ_HANDLED; 91247a70e6fSCosmin Samoila } 91347a70e6fSCosmin Samoila 91447a70e6fSCosmin Samoila static irqreturn_t micfil_err_isr(int irq, void *devid) 91547a70e6fSCosmin Samoila { 91647a70e6fSCosmin Samoila struct fsl_micfil *micfil = (struct fsl_micfil *)devid; 91747a70e6fSCosmin Samoila struct platform_device *pdev = micfil->pdev; 91847a70e6fSCosmin Samoila u32 stat_reg; 91947a70e6fSCosmin Samoila 92047a70e6fSCosmin Samoila regmap_read(micfil->regmap, REG_MICFIL_STAT, &stat_reg); 92147a70e6fSCosmin Samoila 922bd2cffd1SSascha Hauer if (stat_reg & MICFIL_STAT_BSY_FIL) 92347a70e6fSCosmin Samoila dev_dbg(&pdev->dev, "isr: Decimation Filter is running\n"); 92447a70e6fSCosmin Samoila 925bd2cffd1SSascha Hauer if (stat_reg & MICFIL_STAT_FIR_RDY) 92647a70e6fSCosmin Samoila dev_dbg(&pdev->dev, "isr: FIR Filter Data ready\n"); 92747a70e6fSCosmin Samoila 928bd2cffd1SSascha Hauer if (stat_reg & MICFIL_STAT_LOWFREQF) { 92947a70e6fSCosmin Samoila dev_dbg(&pdev->dev, "isr: ipg_clk_app is too low\n"); 93047a70e6fSCosmin Samoila regmap_write_bits(micfil->regmap, REG_MICFIL_STAT, 931bd2cffd1SSascha Hauer MICFIL_STAT_LOWFREQF, 1); 93247a70e6fSCosmin Samoila } 93347a70e6fSCosmin Samoila 93447a70e6fSCosmin Samoila return IRQ_HANDLED; 93547a70e6fSCosmin Samoila } 93647a70e6fSCosmin Samoila 937*29dbfeecSShengjiu Wang static irqreturn_t voice_detected_fn(int irq, void *devid) 938*29dbfeecSShengjiu Wang { 939*29dbfeecSShengjiu Wang struct fsl_micfil *micfil = (struct fsl_micfil *)devid; 940*29dbfeecSShengjiu Wang struct snd_kcontrol *kctl; 941*29dbfeecSShengjiu Wang 942*29dbfeecSShengjiu Wang if (!micfil->card) 943*29dbfeecSShengjiu Wang return IRQ_HANDLED; 944*29dbfeecSShengjiu Wang 945*29dbfeecSShengjiu Wang kctl = snd_soc_card_get_kcontrol(micfil->card, "VAD Detected"); 946*29dbfeecSShengjiu Wang if (!kctl) 947*29dbfeecSShengjiu Wang return IRQ_HANDLED; 948*29dbfeecSShengjiu Wang 949*29dbfeecSShengjiu Wang if (micfil->vad_detected) 950*29dbfeecSShengjiu Wang snd_ctl_notify(micfil->card->snd_card, 951*29dbfeecSShengjiu Wang SNDRV_CTL_EVENT_MASK_VALUE, 952*29dbfeecSShengjiu Wang &kctl->id); 953*29dbfeecSShengjiu Wang 954*29dbfeecSShengjiu Wang return IRQ_HANDLED; 955*29dbfeecSShengjiu Wang } 956*29dbfeecSShengjiu Wang 957*29dbfeecSShengjiu Wang static irqreturn_t hwvad_isr(int irq, void *devid) 958*29dbfeecSShengjiu Wang { 959*29dbfeecSShengjiu Wang struct fsl_micfil *micfil = (struct fsl_micfil *)devid; 960*29dbfeecSShengjiu Wang struct device *dev = &micfil->pdev->dev; 961*29dbfeecSShengjiu Wang u32 vad0_reg; 962*29dbfeecSShengjiu Wang int ret; 963*29dbfeecSShengjiu Wang 964*29dbfeecSShengjiu Wang regmap_read(micfil->regmap, REG_MICFIL_VAD0_STAT, &vad0_reg); 965*29dbfeecSShengjiu Wang 966*29dbfeecSShengjiu Wang /* 967*29dbfeecSShengjiu Wang * The only difference between MICFIL_VAD0_STAT_EF and 968*29dbfeecSShengjiu Wang * MICFIL_VAD0_STAT_IF is that the former requires Write 969*29dbfeecSShengjiu Wang * 1 to Clear. Since both flags are set, it is enough 970*29dbfeecSShengjiu Wang * to only read one of them 971*29dbfeecSShengjiu Wang */ 972*29dbfeecSShengjiu Wang if (vad0_reg & MICFIL_VAD0_STAT_IF) { 973*29dbfeecSShengjiu Wang /* Write 1 to clear */ 974*29dbfeecSShengjiu Wang regmap_write_bits(micfil->regmap, REG_MICFIL_VAD0_STAT, 975*29dbfeecSShengjiu Wang MICFIL_VAD0_STAT_IF, 976*29dbfeecSShengjiu Wang MICFIL_VAD0_STAT_IF); 977*29dbfeecSShengjiu Wang 978*29dbfeecSShengjiu Wang micfil->vad_detected = 1; 979*29dbfeecSShengjiu Wang } 980*29dbfeecSShengjiu Wang 981*29dbfeecSShengjiu Wang ret = fsl_micfil_hwvad_disable(micfil); 982*29dbfeecSShengjiu Wang if (ret) 983*29dbfeecSShengjiu Wang dev_err(dev, "Failed to disable hwvad\n"); 984*29dbfeecSShengjiu Wang 985*29dbfeecSShengjiu Wang return IRQ_WAKE_THREAD; 986*29dbfeecSShengjiu Wang } 987*29dbfeecSShengjiu Wang 988*29dbfeecSShengjiu Wang static irqreturn_t hwvad_err_isr(int irq, void *devid) 989*29dbfeecSShengjiu Wang { 990*29dbfeecSShengjiu Wang struct fsl_micfil *micfil = (struct fsl_micfil *)devid; 991*29dbfeecSShengjiu Wang struct device *dev = &micfil->pdev->dev; 992*29dbfeecSShengjiu Wang u32 vad0_reg; 993*29dbfeecSShengjiu Wang 994*29dbfeecSShengjiu Wang regmap_read(micfil->regmap, REG_MICFIL_VAD0_STAT, &vad0_reg); 995*29dbfeecSShengjiu Wang 996*29dbfeecSShengjiu Wang if (vad0_reg & MICFIL_VAD0_STAT_INSATF) 997*29dbfeecSShengjiu Wang dev_dbg(dev, "voice activity input overflow/underflow detected\n"); 998*29dbfeecSShengjiu Wang 999*29dbfeecSShengjiu Wang return IRQ_HANDLED; 1000*29dbfeecSShengjiu Wang } 1001*29dbfeecSShengjiu Wang 100247a70e6fSCosmin Samoila static int fsl_micfil_probe(struct platform_device *pdev) 100347a70e6fSCosmin Samoila { 100447a70e6fSCosmin Samoila struct device_node *np = pdev->dev.of_node; 100547a70e6fSCosmin Samoila struct fsl_micfil *micfil; 100647a70e6fSCosmin Samoila struct resource *res; 100747a70e6fSCosmin Samoila void __iomem *regs; 100847a70e6fSCosmin Samoila int ret, i; 100947a70e6fSCosmin Samoila 101047a70e6fSCosmin Samoila micfil = devm_kzalloc(&pdev->dev, sizeof(*micfil), GFP_KERNEL); 101147a70e6fSCosmin Samoila if (!micfil) 101247a70e6fSCosmin Samoila return -ENOMEM; 101347a70e6fSCosmin Samoila 101447a70e6fSCosmin Samoila micfil->pdev = pdev; 101547a70e6fSCosmin Samoila strncpy(micfil->name, np->name, sizeof(micfil->name) - 1); 101647a70e6fSCosmin Samoila 1017d7388718SFabio Estevam micfil->soc = of_device_get_match_data(&pdev->dev); 101847a70e6fSCosmin Samoila 101947a70e6fSCosmin Samoila /* ipg_clk is used to control the registers 102047a70e6fSCosmin Samoila * ipg_clk_app is used to operate the filter 102147a70e6fSCosmin Samoila */ 102247a70e6fSCosmin Samoila micfil->mclk = devm_clk_get(&pdev->dev, "ipg_clk_app"); 102347a70e6fSCosmin Samoila if (IS_ERR(micfil->mclk)) { 102447a70e6fSCosmin Samoila dev_err(&pdev->dev, "failed to get core clock: %ld\n", 102547a70e6fSCosmin Samoila PTR_ERR(micfil->mclk)); 102647a70e6fSCosmin Samoila return PTR_ERR(micfil->mclk); 102747a70e6fSCosmin Samoila } 102847a70e6fSCosmin Samoila 1029b5cf28f7SShengjiu Wang micfil->busclk = devm_clk_get(&pdev->dev, "ipg_clk"); 1030b5cf28f7SShengjiu Wang if (IS_ERR(micfil->busclk)) { 1031b5cf28f7SShengjiu Wang dev_err(&pdev->dev, "failed to get ipg clock: %ld\n", 1032b5cf28f7SShengjiu Wang PTR_ERR(micfil->busclk)); 1033b5cf28f7SShengjiu Wang return PTR_ERR(micfil->busclk); 1034b5cf28f7SShengjiu Wang } 1035b5cf28f7SShengjiu Wang 103693f54100SShengjiu Wang fsl_asoc_get_pll_clocks(&pdev->dev, &micfil->pll8k_clk, 103793f54100SShengjiu Wang &micfil->pll11k_clk); 103893f54100SShengjiu Wang 103947a70e6fSCosmin Samoila /* init regmap */ 1040d9bf1e79SYang Yingliang regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 104147a70e6fSCosmin Samoila if (IS_ERR(regs)) 104247a70e6fSCosmin Samoila return PTR_ERR(regs); 104347a70e6fSCosmin Samoila 1044b5cf28f7SShengjiu Wang micfil->regmap = devm_regmap_init_mmio(&pdev->dev, 104547a70e6fSCosmin Samoila regs, 104647a70e6fSCosmin Samoila &fsl_micfil_regmap_config); 104747a70e6fSCosmin Samoila if (IS_ERR(micfil->regmap)) { 104847a70e6fSCosmin Samoila dev_err(&pdev->dev, "failed to init MICFIL regmap: %ld\n", 104947a70e6fSCosmin Samoila PTR_ERR(micfil->regmap)); 105047a70e6fSCosmin Samoila return PTR_ERR(micfil->regmap); 105147a70e6fSCosmin Samoila } 105247a70e6fSCosmin Samoila 105347a70e6fSCosmin Samoila /* dataline mask for RX */ 105447a70e6fSCosmin Samoila ret = of_property_read_u32_index(np, 105547a70e6fSCosmin Samoila "fsl,dataline", 105647a70e6fSCosmin Samoila 0, 105747a70e6fSCosmin Samoila &micfil->dataline); 105847a70e6fSCosmin Samoila if (ret) 105947a70e6fSCosmin Samoila micfil->dataline = 1; 106047a70e6fSCosmin Samoila 106147a70e6fSCosmin Samoila if (micfil->dataline & ~micfil->soc->dataline) { 106247a70e6fSCosmin Samoila dev_err(&pdev->dev, "dataline setting error, Mask is 0x%X\n", 106347a70e6fSCosmin Samoila micfil->soc->dataline); 106447a70e6fSCosmin Samoila return -EINVAL; 106547a70e6fSCosmin Samoila } 106647a70e6fSCosmin Samoila 106747a70e6fSCosmin Samoila /* get IRQs */ 106847a70e6fSCosmin Samoila for (i = 0; i < MICFIL_IRQ_LINES; i++) { 106947a70e6fSCosmin Samoila micfil->irq[i] = platform_get_irq(pdev, i); 107083b35f45STang Bin if (micfil->irq[i] < 0) 107147a70e6fSCosmin Samoila return micfil->irq[i]; 107247a70e6fSCosmin Samoila } 107347a70e6fSCosmin Samoila 1074a62ed960SFabio Estevam /* Digital Microphone interface interrupt */ 107547a70e6fSCosmin Samoila ret = devm_request_irq(&pdev->dev, micfil->irq[0], 1076cbd090faSSascha Hauer micfil_isr, IRQF_SHARED, 107747a70e6fSCosmin Samoila micfil->name, micfil); 107847a70e6fSCosmin Samoila if (ret) { 107947a70e6fSCosmin Samoila dev_err(&pdev->dev, "failed to claim mic interface irq %u\n", 108047a70e6fSCosmin Samoila micfil->irq[0]); 108147a70e6fSCosmin Samoila return ret; 108247a70e6fSCosmin Samoila } 108347a70e6fSCosmin Samoila 1084a62ed960SFabio Estevam /* Digital Microphone interface error interrupt */ 108547a70e6fSCosmin Samoila ret = devm_request_irq(&pdev->dev, micfil->irq[1], 1086cbd090faSSascha Hauer micfil_err_isr, IRQF_SHARED, 108747a70e6fSCosmin Samoila micfil->name, micfil); 108847a70e6fSCosmin Samoila if (ret) { 108947a70e6fSCosmin Samoila dev_err(&pdev->dev, "failed to claim mic interface error irq %u\n", 109047a70e6fSCosmin Samoila micfil->irq[1]); 109147a70e6fSCosmin Samoila return ret; 109247a70e6fSCosmin Samoila } 109347a70e6fSCosmin Samoila 1094*29dbfeecSShengjiu Wang /* Digital Microphone interface voice activity detector event */ 1095*29dbfeecSShengjiu Wang ret = devm_request_threaded_irq(&pdev->dev, micfil->irq[2], 1096*29dbfeecSShengjiu Wang hwvad_isr, voice_detected_fn, 1097*29dbfeecSShengjiu Wang IRQF_SHARED, micfil->name, micfil); 1098*29dbfeecSShengjiu Wang if (ret) { 1099*29dbfeecSShengjiu Wang dev_err(&pdev->dev, "failed to claim hwvad event irq %u\n", 1100*29dbfeecSShengjiu Wang micfil->irq[0]); 1101*29dbfeecSShengjiu Wang return ret; 1102*29dbfeecSShengjiu Wang } 1103*29dbfeecSShengjiu Wang 1104*29dbfeecSShengjiu Wang /* Digital Microphone interface voice activity detector error */ 1105*29dbfeecSShengjiu Wang ret = devm_request_irq(&pdev->dev, micfil->irq[3], 1106*29dbfeecSShengjiu Wang hwvad_err_isr, IRQF_SHARED, 1107*29dbfeecSShengjiu Wang micfil->name, micfil); 1108*29dbfeecSShengjiu Wang if (ret) { 1109*29dbfeecSShengjiu Wang dev_err(&pdev->dev, "failed to claim hwvad error irq %u\n", 1110*29dbfeecSShengjiu Wang micfil->irq[1]); 1111*29dbfeecSShengjiu Wang return ret; 1112*29dbfeecSShengjiu Wang } 1113*29dbfeecSShengjiu Wang 111447a70e6fSCosmin Samoila micfil->dma_params_rx.chan_name = "rx"; 111547a70e6fSCosmin Samoila micfil->dma_params_rx.addr = res->start + REG_MICFIL_DATACH0; 111647a70e6fSCosmin Samoila micfil->dma_params_rx.maxburst = MICFIL_DMA_MAXBURST_RX; 111747a70e6fSCosmin Samoila 111847a70e6fSCosmin Samoila platform_set_drvdata(pdev, micfil); 111947a70e6fSCosmin Samoila 112047a70e6fSCosmin Samoila pm_runtime_enable(&pdev->dev); 1121b5cf28f7SShengjiu Wang regcache_cache_only(micfil->regmap, true); 112247a70e6fSCosmin Samoila 11230adf2920SShengjiu Wang /* 11240adf2920SShengjiu Wang * Register platform component before registering cpu dai for there 11250adf2920SShengjiu Wang * is not defer probe for platform component in snd_soc_add_pcm_runtime(). 11260adf2920SShengjiu Wang */ 11270adf2920SShengjiu Wang ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); 11280adf2920SShengjiu Wang if (ret) { 11290adf2920SShengjiu Wang dev_err(&pdev->dev, "failed to pcm register\n"); 11300adf2920SShengjiu Wang return ret; 11310adf2920SShengjiu Wang } 11320adf2920SShengjiu Wang 1133cb05dac1SShengjiu Wang fsl_micfil_dai.capture.formats = micfil->soc->formats; 1134cb05dac1SShengjiu Wang 113547a70e6fSCosmin Samoila ret = devm_snd_soc_register_component(&pdev->dev, &fsl_micfil_component, 113647a70e6fSCosmin Samoila &fsl_micfil_dai, 1); 113747a70e6fSCosmin Samoila if (ret) { 113847a70e6fSCosmin Samoila dev_err(&pdev->dev, "failed to register component %s\n", 113947a70e6fSCosmin Samoila fsl_micfil_component.name); 114047a70e6fSCosmin Samoila } 114147a70e6fSCosmin Samoila 114247a70e6fSCosmin Samoila return ret; 114347a70e6fSCosmin Samoila } 114447a70e6fSCosmin Samoila 114547a70e6fSCosmin Samoila static int __maybe_unused fsl_micfil_runtime_suspend(struct device *dev) 114647a70e6fSCosmin Samoila { 114747a70e6fSCosmin Samoila struct fsl_micfil *micfil = dev_get_drvdata(dev); 114847a70e6fSCosmin Samoila 114947a70e6fSCosmin Samoila regcache_cache_only(micfil->regmap, true); 115047a70e6fSCosmin Samoila 115147a70e6fSCosmin Samoila clk_disable_unprepare(micfil->mclk); 1152b5cf28f7SShengjiu Wang clk_disable_unprepare(micfil->busclk); 115347a70e6fSCosmin Samoila 115447a70e6fSCosmin Samoila return 0; 115547a70e6fSCosmin Samoila } 115647a70e6fSCosmin Samoila 115747a70e6fSCosmin Samoila static int __maybe_unused fsl_micfil_runtime_resume(struct device *dev) 115847a70e6fSCosmin Samoila { 115947a70e6fSCosmin Samoila struct fsl_micfil *micfil = dev_get_drvdata(dev); 116047a70e6fSCosmin Samoila int ret; 116147a70e6fSCosmin Samoila 1162b5cf28f7SShengjiu Wang ret = clk_prepare_enable(micfil->busclk); 116347a70e6fSCosmin Samoila if (ret < 0) 116447a70e6fSCosmin Samoila return ret; 116547a70e6fSCosmin Samoila 1166b5cf28f7SShengjiu Wang ret = clk_prepare_enable(micfil->mclk); 1167b5cf28f7SShengjiu Wang if (ret < 0) { 1168b5cf28f7SShengjiu Wang clk_disable_unprepare(micfil->busclk); 1169b5cf28f7SShengjiu Wang return ret; 1170b5cf28f7SShengjiu Wang } 1171b5cf28f7SShengjiu Wang 117247a70e6fSCosmin Samoila regcache_cache_only(micfil->regmap, false); 117347a70e6fSCosmin Samoila regcache_mark_dirty(micfil->regmap); 117447a70e6fSCosmin Samoila regcache_sync(micfil->regmap); 117547a70e6fSCosmin Samoila 117647a70e6fSCosmin Samoila return 0; 117747a70e6fSCosmin Samoila } 117847a70e6fSCosmin Samoila 117947a70e6fSCosmin Samoila static int __maybe_unused fsl_micfil_suspend(struct device *dev) 118047a70e6fSCosmin Samoila { 118147a70e6fSCosmin Samoila pm_runtime_force_suspend(dev); 118247a70e6fSCosmin Samoila 118347a70e6fSCosmin Samoila return 0; 118447a70e6fSCosmin Samoila } 118547a70e6fSCosmin Samoila 118647a70e6fSCosmin Samoila static int __maybe_unused fsl_micfil_resume(struct device *dev) 118747a70e6fSCosmin Samoila { 118847a70e6fSCosmin Samoila pm_runtime_force_resume(dev); 118947a70e6fSCosmin Samoila 119047a70e6fSCosmin Samoila return 0; 119147a70e6fSCosmin Samoila } 119247a70e6fSCosmin Samoila 119347a70e6fSCosmin Samoila static const struct dev_pm_ops fsl_micfil_pm_ops = { 119447a70e6fSCosmin Samoila SET_RUNTIME_PM_OPS(fsl_micfil_runtime_suspend, 119547a70e6fSCosmin Samoila fsl_micfil_runtime_resume, 119647a70e6fSCosmin Samoila NULL) 119747a70e6fSCosmin Samoila SET_SYSTEM_SLEEP_PM_OPS(fsl_micfil_suspend, 119847a70e6fSCosmin Samoila fsl_micfil_resume) 119947a70e6fSCosmin Samoila }; 120047a70e6fSCosmin Samoila 120147a70e6fSCosmin Samoila static struct platform_driver fsl_micfil_driver = { 120247a70e6fSCosmin Samoila .probe = fsl_micfil_probe, 120347a70e6fSCosmin Samoila .driver = { 120447a70e6fSCosmin Samoila .name = "fsl-micfil-dai", 120547a70e6fSCosmin Samoila .pm = &fsl_micfil_pm_ops, 120647a70e6fSCosmin Samoila .of_match_table = fsl_micfil_dt_ids, 120747a70e6fSCosmin Samoila }, 120847a70e6fSCosmin Samoila }; 120947a70e6fSCosmin Samoila module_platform_driver(fsl_micfil_driver); 121047a70e6fSCosmin Samoila 121147a70e6fSCosmin Samoila MODULE_AUTHOR("Cosmin-Gabriel Samoila <cosmin.samoila@nxp.com>"); 121247a70e6fSCosmin Samoila MODULE_DESCRIPTION("NXP PDM Microphone Interface (MICFIL) driver"); 121347a70e6fSCosmin Samoila MODULE_LICENSE("GPL v2"); 1214