1f803ec63SDaniel Baluta // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 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 31f0862123SShengjiu Wang #define MICFIL_NUM_RATES 7 32f0862123SShengjiu Wang #define MICFIL_CLK_SRC_NUM 3 33f0862123SShengjiu Wang /* clock source ids */ 34f0862123SShengjiu Wang #define MICFIL_AUDIO_PLL1 0 35f0862123SShengjiu Wang #define MICFIL_AUDIO_PLL2 1 36f0862123SShengjiu Wang #define MICFIL_CLK_EXT3 2 37f0862123SShengjiu Wang 38bea1d61dSSascha Hauer enum quality { 39bea1d61dSSascha Hauer QUALITY_HIGH, 40bea1d61dSSascha Hauer QUALITY_MEDIUM, 41bea1d61dSSascha Hauer QUALITY_LOW, 42bea1d61dSSascha Hauer QUALITY_VLOW0, 43bea1d61dSSascha Hauer QUALITY_VLOW1, 44bea1d61dSSascha Hauer QUALITY_VLOW2, 45bea1d61dSSascha Hauer }; 46bea1d61dSSascha Hauer 4747a70e6fSCosmin Samoila struct fsl_micfil { 4847a70e6fSCosmin Samoila struct platform_device *pdev; 4947a70e6fSCosmin Samoila struct regmap *regmap; 5047a70e6fSCosmin Samoila const struct fsl_micfil_soc_data *soc; 51b5cf28f7SShengjiu Wang struct clk *busclk; 5247a70e6fSCosmin Samoila struct clk *mclk; 5393f54100SShengjiu Wang struct clk *pll8k_clk; 5493f54100SShengjiu Wang struct clk *pll11k_clk; 55f0862123SShengjiu Wang struct clk *clk_src[MICFIL_CLK_SRC_NUM]; 5647a70e6fSCosmin Samoila struct snd_dmaengine_dai_dma_data dma_params_rx; 572495ba26SSascha Hauer struct sdma_peripheral_config sdmacfg; 5829dbfeecSShengjiu Wang struct snd_soc_card *card; 59f0862123SShengjiu Wang struct snd_pcm_hw_constraint_list constraint_rates; 60f0862123SShengjiu Wang unsigned int constraint_rates_list[MICFIL_NUM_RATES]; 6147a70e6fSCosmin Samoila unsigned int dataline; 6247a70e6fSCosmin Samoila char name[32]; 6347a70e6fSCosmin Samoila int irq[MICFIL_IRQ_LINES]; 64bea1d61dSSascha Hauer enum quality quality; 653b13b143SShengjiu Wang int dc_remover; 6629dbfeecSShengjiu Wang int vad_init_mode; 6729dbfeecSShengjiu Wang int vad_enabled; 6829dbfeecSShengjiu Wang int vad_detected; 6936736505SChancel Liu struct fsl_micfil_verid verid; 7036736505SChancel Liu struct fsl_micfil_param param; 7147a70e6fSCosmin Samoila }; 7247a70e6fSCosmin Samoila 7347a70e6fSCosmin Samoila struct fsl_micfil_soc_data { 7447a70e6fSCosmin Samoila unsigned int fifos; 7547a70e6fSCosmin Samoila unsigned int fifo_depth; 7647a70e6fSCosmin Samoila unsigned int dataline; 7747a70e6fSCosmin Samoila bool imx; 7877a7a6e9SChancel Liu bool use_edma; 7936736505SChancel Liu bool use_verid; 808b339bebSChancel Liu bool volume_sx; 81cb05dac1SShengjiu Wang u64 formats; 8247a70e6fSCosmin Samoila }; 8347a70e6fSCosmin Samoila 8447a70e6fSCosmin Samoila static struct fsl_micfil_soc_data fsl_micfil_imx8mm = { 8547a70e6fSCosmin Samoila .imx = true, 8647a70e6fSCosmin Samoila .fifos = 8, 8747a70e6fSCosmin Samoila .fifo_depth = 8, 8847a70e6fSCosmin Samoila .dataline = 0xf, 89cb05dac1SShengjiu Wang .formats = SNDRV_PCM_FMTBIT_S16_LE, 908b339bebSChancel Liu .volume_sx = true, 91cb05dac1SShengjiu Wang }; 92cb05dac1SShengjiu Wang 93cb05dac1SShengjiu Wang static struct fsl_micfil_soc_data fsl_micfil_imx8mp = { 94cb05dac1SShengjiu Wang .imx = true, 95cb05dac1SShengjiu Wang .fifos = 8, 96cb05dac1SShengjiu Wang .fifo_depth = 32, 97cb05dac1SShengjiu Wang .dataline = 0xf, 98cb05dac1SShengjiu Wang .formats = SNDRV_PCM_FMTBIT_S32_LE, 998b339bebSChancel Liu .volume_sx = false, 10047a70e6fSCosmin Samoila }; 10147a70e6fSCosmin Samoila 102a10a5254SChancel Liu static struct fsl_micfil_soc_data fsl_micfil_imx93 = { 103a10a5254SChancel Liu .imx = true, 104a10a5254SChancel Liu .fifos = 8, 105a10a5254SChancel Liu .fifo_depth = 32, 106a10a5254SChancel Liu .dataline = 0xf, 107a10a5254SChancel Liu .formats = SNDRV_PCM_FMTBIT_S32_LE, 10877a7a6e9SChancel Liu .use_edma = true, 10936736505SChancel Liu .use_verid = true, 1108b339bebSChancel Liu .volume_sx = false, 111a10a5254SChancel Liu }; 112a10a5254SChancel Liu 11347a70e6fSCosmin Samoila static const struct of_device_id fsl_micfil_dt_ids[] = { 11447a70e6fSCosmin Samoila { .compatible = "fsl,imx8mm-micfil", .data = &fsl_micfil_imx8mm }, 115cb05dac1SShengjiu Wang { .compatible = "fsl,imx8mp-micfil", .data = &fsl_micfil_imx8mp }, 116a10a5254SChancel Liu { .compatible = "fsl,imx93-micfil", .data = &fsl_micfil_imx93 }, 11747a70e6fSCosmin Samoila {} 11847a70e6fSCosmin Samoila }; 11947a70e6fSCosmin Samoila MODULE_DEVICE_TABLE(of, fsl_micfil_dt_ids); 12047a70e6fSCosmin Samoila 12147a70e6fSCosmin Samoila static const char * const micfil_quality_select_texts[] = { 122bea1d61dSSascha Hauer [QUALITY_HIGH] = "High", 123bea1d61dSSascha Hauer [QUALITY_MEDIUM] = "Medium", 124bea1d61dSSascha Hauer [QUALITY_LOW] = "Low", 125bea1d61dSSascha Hauer [QUALITY_VLOW0] = "VLow0", 126bea1d61dSSascha Hauer [QUALITY_VLOW1] = "Vlow1", 127bea1d61dSSascha Hauer [QUALITY_VLOW2] = "Vlow2", 12847a70e6fSCosmin Samoila }; 12947a70e6fSCosmin Samoila 13047a70e6fSCosmin Samoila static const struct soc_enum fsl_micfil_quality_enum = 131bea1d61dSSascha Hauer SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_quality_select_texts), 13247a70e6fSCosmin Samoila micfil_quality_select_texts); 13347a70e6fSCosmin Samoila 13447a70e6fSCosmin Samoila static DECLARE_TLV_DB_SCALE(gain_tlv, 0, 100, 0); 13547a70e6fSCosmin Samoila 136bea1d61dSSascha Hauer static int micfil_set_quality(struct fsl_micfil *micfil) 137bea1d61dSSascha Hauer { 138bea1d61dSSascha Hauer u32 qsel; 139bea1d61dSSascha Hauer 140bea1d61dSSascha Hauer switch (micfil->quality) { 141bea1d61dSSascha Hauer case QUALITY_HIGH: 142bea1d61dSSascha Hauer qsel = MICFIL_QSEL_HIGH_QUALITY; 143bea1d61dSSascha Hauer break; 144bea1d61dSSascha Hauer case QUALITY_MEDIUM: 145bea1d61dSSascha Hauer qsel = MICFIL_QSEL_MEDIUM_QUALITY; 146bea1d61dSSascha Hauer break; 147bea1d61dSSascha Hauer case QUALITY_LOW: 148bea1d61dSSascha Hauer qsel = MICFIL_QSEL_LOW_QUALITY; 149bea1d61dSSascha Hauer break; 150bea1d61dSSascha Hauer case QUALITY_VLOW0: 151bea1d61dSSascha Hauer qsel = MICFIL_QSEL_VLOW0_QUALITY; 152bea1d61dSSascha Hauer break; 153bea1d61dSSascha Hauer case QUALITY_VLOW1: 154bea1d61dSSascha Hauer qsel = MICFIL_QSEL_VLOW1_QUALITY; 155bea1d61dSSascha Hauer break; 156bea1d61dSSascha Hauer case QUALITY_VLOW2: 157bea1d61dSSascha Hauer qsel = MICFIL_QSEL_VLOW2_QUALITY; 158bea1d61dSSascha Hauer break; 159bea1d61dSSascha Hauer } 160bea1d61dSSascha Hauer 161bea1d61dSSascha Hauer return regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2, 162bea1d61dSSascha Hauer MICFIL_CTRL2_QSEL, 163bea1d61dSSascha Hauer FIELD_PREP(MICFIL_CTRL2_QSEL, qsel)); 164bea1d61dSSascha Hauer } 165bea1d61dSSascha Hauer 166bea1d61dSSascha Hauer static int micfil_quality_get(struct snd_kcontrol *kcontrol, 167bea1d61dSSascha Hauer struct snd_ctl_elem_value *ucontrol) 168bea1d61dSSascha Hauer { 169bea1d61dSSascha Hauer struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); 170bea1d61dSSascha Hauer struct fsl_micfil *micfil = snd_soc_component_get_drvdata(cmpnt); 171bea1d61dSSascha Hauer 172bea1d61dSSascha Hauer ucontrol->value.integer.value[0] = micfil->quality; 173bea1d61dSSascha Hauer 174bea1d61dSSascha Hauer return 0; 175bea1d61dSSascha Hauer } 176bea1d61dSSascha Hauer 177bea1d61dSSascha Hauer static int micfil_quality_set(struct snd_kcontrol *kcontrol, 178bea1d61dSSascha Hauer struct snd_ctl_elem_value *ucontrol) 179bea1d61dSSascha Hauer { 180bea1d61dSSascha Hauer struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); 181bea1d61dSSascha Hauer struct fsl_micfil *micfil = snd_soc_component_get_drvdata(cmpnt); 182bea1d61dSSascha Hauer 183bea1d61dSSascha Hauer micfil->quality = ucontrol->value.integer.value[0]; 184bea1d61dSSascha Hauer 185bea1d61dSSascha Hauer return micfil_set_quality(micfil); 186bea1d61dSSascha Hauer } 187bea1d61dSSascha Hauer 18829dbfeecSShengjiu Wang static const char * const micfil_hwvad_enable[] = { 18929dbfeecSShengjiu Wang "Disable (Record only)", 19029dbfeecSShengjiu Wang "Enable (Record with Vad)", 19129dbfeecSShengjiu Wang }; 19229dbfeecSShengjiu Wang 19329dbfeecSShengjiu Wang static const char * const micfil_hwvad_init_mode[] = { 19429dbfeecSShengjiu Wang "Envelope mode", "Energy mode", 19529dbfeecSShengjiu Wang }; 19629dbfeecSShengjiu Wang 19729dbfeecSShengjiu Wang static const char * const micfil_hwvad_hpf_texts[] = { 19829dbfeecSShengjiu Wang "Filter bypass", 19929dbfeecSShengjiu Wang "Cut-off @1750Hz", 20029dbfeecSShengjiu Wang "Cut-off @215Hz", 20129dbfeecSShengjiu Wang "Cut-off @102Hz", 20229dbfeecSShengjiu Wang }; 20329dbfeecSShengjiu Wang 20429dbfeecSShengjiu Wang /* 20529dbfeecSShengjiu Wang * DC Remover Control 20629dbfeecSShengjiu Wang * Filter Bypassed 1 1 20729dbfeecSShengjiu Wang * Cut-off @21Hz 0 0 20829dbfeecSShengjiu Wang * Cut-off @83Hz 0 1 20929dbfeecSShengjiu Wang * Cut-off @152HZ 1 0 21029dbfeecSShengjiu Wang */ 21129dbfeecSShengjiu Wang static const char * const micfil_dc_remover_texts[] = { 21229dbfeecSShengjiu Wang "Cut-off @21Hz", "Cut-off @83Hz", 21329dbfeecSShengjiu Wang "Cut-off @152Hz", "Bypass", 21429dbfeecSShengjiu Wang }; 21529dbfeecSShengjiu Wang 21629dbfeecSShengjiu Wang static const struct soc_enum hwvad_enable_enum = 21729dbfeecSShengjiu Wang SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_hwvad_enable), 21829dbfeecSShengjiu Wang micfil_hwvad_enable); 21929dbfeecSShengjiu Wang static const struct soc_enum hwvad_init_mode_enum = 22029dbfeecSShengjiu Wang SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_hwvad_init_mode), 22129dbfeecSShengjiu Wang micfil_hwvad_init_mode); 22229dbfeecSShengjiu Wang static const struct soc_enum hwvad_hpf_enum = 22329dbfeecSShengjiu Wang SOC_ENUM_SINGLE(REG_MICFIL_VAD0_CTRL2, 0, 22429dbfeecSShengjiu Wang ARRAY_SIZE(micfil_hwvad_hpf_texts), 22529dbfeecSShengjiu Wang micfil_hwvad_hpf_texts); 22629dbfeecSShengjiu Wang static const struct soc_enum fsl_micfil_dc_remover_enum = 22729dbfeecSShengjiu Wang SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_dc_remover_texts), 22829dbfeecSShengjiu Wang micfil_dc_remover_texts); 22929dbfeecSShengjiu Wang 23029dbfeecSShengjiu Wang static int micfil_put_dc_remover_state(struct snd_kcontrol *kcontrol, 23129dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 23229dbfeecSShengjiu Wang { 23329dbfeecSShengjiu Wang struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 23429dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 23529dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 23629dbfeecSShengjiu Wang unsigned int *item = ucontrol->value.enumerated.item; 23729dbfeecSShengjiu Wang int val = snd_soc_enum_item_to_val(e, item[0]); 23829dbfeecSShengjiu Wang int i = 0, ret = 0; 23929dbfeecSShengjiu Wang u32 reg_val = 0; 24029dbfeecSShengjiu Wang 24129dbfeecSShengjiu Wang if (val < 0 || val > 3) 24229dbfeecSShengjiu Wang return -EINVAL; 24329dbfeecSShengjiu Wang 24429dbfeecSShengjiu Wang micfil->dc_remover = val; 24529dbfeecSShengjiu Wang 24629dbfeecSShengjiu Wang /* Calculate total value for all channels */ 24729dbfeecSShengjiu Wang for (i = 0; i < MICFIL_OUTPUT_CHANNELS; i++) 24829dbfeecSShengjiu Wang reg_val |= val << MICFIL_DC_CHX_SHIFT(i); 24929dbfeecSShengjiu Wang 25029dbfeecSShengjiu Wang /* Update DC Remover mode for all channels */ 25129dbfeecSShengjiu Wang ret = snd_soc_component_update_bits(comp, REG_MICFIL_DC_CTRL, 25229dbfeecSShengjiu Wang MICFIL_DC_CTRL_CONFIG, reg_val); 25329dbfeecSShengjiu Wang if (ret < 0) 25429dbfeecSShengjiu Wang return ret; 25529dbfeecSShengjiu Wang 25629dbfeecSShengjiu Wang return 0; 25729dbfeecSShengjiu Wang } 25829dbfeecSShengjiu Wang 25929dbfeecSShengjiu Wang static int micfil_get_dc_remover_state(struct snd_kcontrol *kcontrol, 26029dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 26129dbfeecSShengjiu Wang { 26229dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 26329dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 26429dbfeecSShengjiu Wang 26529dbfeecSShengjiu Wang ucontrol->value.enumerated.item[0] = micfil->dc_remover; 26629dbfeecSShengjiu Wang 26729dbfeecSShengjiu Wang return 0; 26829dbfeecSShengjiu Wang } 26929dbfeecSShengjiu Wang 27029dbfeecSShengjiu Wang static int hwvad_put_enable(struct snd_kcontrol *kcontrol, 27129dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 27229dbfeecSShengjiu Wang { 27329dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 27429dbfeecSShengjiu Wang struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 27529dbfeecSShengjiu Wang unsigned int *item = ucontrol->value.enumerated.item; 27629dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 27729dbfeecSShengjiu Wang int val = snd_soc_enum_item_to_val(e, item[0]); 27829dbfeecSShengjiu Wang 27929dbfeecSShengjiu Wang micfil->vad_enabled = val; 28029dbfeecSShengjiu Wang 28129dbfeecSShengjiu Wang return 0; 28229dbfeecSShengjiu Wang } 28329dbfeecSShengjiu Wang 28429dbfeecSShengjiu Wang static int hwvad_get_enable(struct snd_kcontrol *kcontrol, 28529dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 28629dbfeecSShengjiu Wang { 28729dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 28829dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 28929dbfeecSShengjiu Wang 29029dbfeecSShengjiu Wang ucontrol->value.enumerated.item[0] = micfil->vad_enabled; 29129dbfeecSShengjiu Wang 29229dbfeecSShengjiu Wang return 0; 29329dbfeecSShengjiu Wang } 29429dbfeecSShengjiu Wang 29529dbfeecSShengjiu Wang static int hwvad_put_init_mode(struct snd_kcontrol *kcontrol, 29629dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 29729dbfeecSShengjiu Wang { 29829dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 29929dbfeecSShengjiu Wang struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 30029dbfeecSShengjiu Wang unsigned int *item = ucontrol->value.enumerated.item; 30129dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 30229dbfeecSShengjiu Wang int val = snd_soc_enum_item_to_val(e, item[0]); 30329dbfeecSShengjiu Wang 30429dbfeecSShengjiu Wang /* 0 - Envelope-based Mode 30529dbfeecSShengjiu Wang * 1 - Energy-based Mode 30629dbfeecSShengjiu Wang */ 30729dbfeecSShengjiu Wang micfil->vad_init_mode = val; 30829dbfeecSShengjiu Wang 30929dbfeecSShengjiu Wang return 0; 31029dbfeecSShengjiu Wang } 31129dbfeecSShengjiu Wang 31229dbfeecSShengjiu Wang static int hwvad_get_init_mode(struct snd_kcontrol *kcontrol, 31329dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 31429dbfeecSShengjiu Wang { 31529dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 31629dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 31729dbfeecSShengjiu Wang 31829dbfeecSShengjiu Wang ucontrol->value.enumerated.item[0] = micfil->vad_init_mode; 31929dbfeecSShengjiu Wang 32029dbfeecSShengjiu Wang return 0; 32129dbfeecSShengjiu Wang } 32229dbfeecSShengjiu Wang 32329dbfeecSShengjiu Wang static int hwvad_detected(struct snd_kcontrol *kcontrol, 32429dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 32529dbfeecSShengjiu Wang { 32629dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 32729dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 32829dbfeecSShengjiu Wang 32929dbfeecSShengjiu Wang ucontrol->value.enumerated.item[0] = micfil->vad_detected; 33029dbfeecSShengjiu Wang 33129dbfeecSShengjiu Wang return 0; 33229dbfeecSShengjiu Wang } 33329dbfeecSShengjiu Wang 3348b339bebSChancel Liu static const struct snd_kcontrol_new fsl_micfil_volume_controls[] = { 3358b339bebSChancel Liu SOC_SINGLE_TLV("CH0 Volume", REG_MICFIL_OUT_CTRL, 3368b339bebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(0), 0xF, 0, gain_tlv), 3378b339bebSChancel Liu SOC_SINGLE_TLV("CH1 Volume", REG_MICFIL_OUT_CTRL, 3388b339bebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(1), 0xF, 0, gain_tlv), 3398b339bebSChancel Liu SOC_SINGLE_TLV("CH2 Volume", REG_MICFIL_OUT_CTRL, 3408b339bebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(2), 0xF, 0, gain_tlv), 3418b339bebSChancel Liu SOC_SINGLE_TLV("CH3 Volume", REG_MICFIL_OUT_CTRL, 3428b339bebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(3), 0xF, 0, gain_tlv), 3438b339bebSChancel Liu SOC_SINGLE_TLV("CH4 Volume", REG_MICFIL_OUT_CTRL, 3448b339bebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(4), 0xF, 0, gain_tlv), 3458b339bebSChancel Liu SOC_SINGLE_TLV("CH5 Volume", REG_MICFIL_OUT_CTRL, 3468b339bebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(5), 0xF, 0, gain_tlv), 3478b339bebSChancel Liu SOC_SINGLE_TLV("CH6 Volume", REG_MICFIL_OUT_CTRL, 3488b339bebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(6), 0xF, 0, gain_tlv), 3498b339bebSChancel Liu SOC_SINGLE_TLV("CH7 Volume", REG_MICFIL_OUT_CTRL, 3508b339bebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(7), 0xF, 0, gain_tlv), 3518b339bebSChancel Liu }; 3528b339bebSChancel Liu 3538b339bebSChancel Liu static const struct snd_kcontrol_new fsl_micfil_volume_sx_controls[] = { 35447a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH0 Volume", REG_MICFIL_OUT_CTRL, 355cdfa92ebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(0), 0x8, 0xF, gain_tlv), 35647a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH1 Volume", REG_MICFIL_OUT_CTRL, 357cdfa92ebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(1), 0x8, 0xF, gain_tlv), 35847a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH2 Volume", REG_MICFIL_OUT_CTRL, 359cdfa92ebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(2), 0x8, 0xF, gain_tlv), 36047a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH3 Volume", REG_MICFIL_OUT_CTRL, 361cdfa92ebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(3), 0x8, 0xF, gain_tlv), 36247a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH4 Volume", REG_MICFIL_OUT_CTRL, 363cdfa92ebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(4), 0x8, 0xF, gain_tlv), 36447a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH5 Volume", REG_MICFIL_OUT_CTRL, 365cdfa92ebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(5), 0x8, 0xF, gain_tlv), 36647a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH6 Volume", REG_MICFIL_OUT_CTRL, 367cdfa92ebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(6), 0x8, 0xF, gain_tlv), 36847a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH7 Volume", REG_MICFIL_OUT_CTRL, 369cdfa92ebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(7), 0x8, 0xF, gain_tlv), 3708b339bebSChancel Liu }; 3718b339bebSChancel Liu 3728b339bebSChancel Liu static const struct snd_kcontrol_new fsl_micfil_snd_controls[] = { 37347a70e6fSCosmin Samoila SOC_ENUM_EXT("MICFIL Quality Select", 37447a70e6fSCosmin Samoila fsl_micfil_quality_enum, 375bea1d61dSSascha Hauer micfil_quality_get, micfil_quality_set), 37629dbfeecSShengjiu Wang SOC_ENUM_EXT("HWVAD Enablement Switch", hwvad_enable_enum, 37729dbfeecSShengjiu Wang hwvad_get_enable, hwvad_put_enable), 37829dbfeecSShengjiu Wang SOC_ENUM_EXT("HWVAD Initialization Mode", hwvad_init_mode_enum, 37929dbfeecSShengjiu Wang hwvad_get_init_mode, hwvad_put_init_mode), 38029dbfeecSShengjiu Wang SOC_ENUM("HWVAD High-Pass Filter", hwvad_hpf_enum), 38129dbfeecSShengjiu Wang SOC_SINGLE("HWVAD ZCD Switch", REG_MICFIL_VAD0_ZCD, 0, 1, 0), 38229dbfeecSShengjiu Wang SOC_SINGLE("HWVAD ZCD Auto Threshold Switch", 38329dbfeecSShengjiu Wang REG_MICFIL_VAD0_ZCD, 2, 1, 0), 38429dbfeecSShengjiu Wang SOC_ENUM_EXT("MICFIL DC Remover Control", fsl_micfil_dc_remover_enum, 38529dbfeecSShengjiu Wang micfil_get_dc_remover_state, micfil_put_dc_remover_state), 38629dbfeecSShengjiu Wang SOC_SINGLE("HWVAD Input Gain", REG_MICFIL_VAD0_CTRL2, 8, 15, 0), 38729dbfeecSShengjiu Wang SOC_SINGLE("HWVAD Sound Gain", REG_MICFIL_VAD0_SCONFIG, 0, 15, 0), 38829dbfeecSShengjiu Wang SOC_SINGLE("HWVAD Noise Gain", REG_MICFIL_VAD0_NCONFIG, 0, 15, 0), 38929dbfeecSShengjiu Wang SOC_SINGLE_RANGE("HWVAD Detector Frame Time", REG_MICFIL_VAD0_CTRL2, 16, 0, 63, 0), 39029dbfeecSShengjiu Wang SOC_SINGLE("HWVAD Detector Initialization Time", REG_MICFIL_VAD0_CTRL1, 8, 31, 0), 39129dbfeecSShengjiu Wang SOC_SINGLE("HWVAD Noise Filter Adjustment", REG_MICFIL_VAD0_NCONFIG, 8, 31, 0), 39229dbfeecSShengjiu Wang SOC_SINGLE("HWVAD ZCD Threshold", REG_MICFIL_VAD0_ZCD, 16, 1023, 0), 39329dbfeecSShengjiu Wang SOC_SINGLE("HWVAD ZCD Adjustment", REG_MICFIL_VAD0_ZCD, 8, 15, 0), 39429dbfeecSShengjiu Wang SOC_SINGLE("HWVAD ZCD And Behavior Switch", 39529dbfeecSShengjiu Wang REG_MICFIL_VAD0_ZCD, 4, 1, 0), 39629dbfeecSShengjiu Wang SOC_SINGLE_BOOL_EXT("VAD Detected", 0, hwvad_detected, NULL), 39747a70e6fSCosmin Samoila }; 39847a70e6fSCosmin Samoila 39936736505SChancel Liu static int fsl_micfil_use_verid(struct device *dev) 40036736505SChancel Liu { 40136736505SChancel Liu struct fsl_micfil *micfil = dev_get_drvdata(dev); 40236736505SChancel Liu unsigned int val; 40336736505SChancel Liu int ret; 40436736505SChancel Liu 40536736505SChancel Liu if (!micfil->soc->use_verid) 40636736505SChancel Liu return 0; 40736736505SChancel Liu 40836736505SChancel Liu ret = regmap_read(micfil->regmap, REG_MICFIL_VERID, &val); 40936736505SChancel Liu if (ret < 0) 41036736505SChancel Liu return ret; 41136736505SChancel Liu 41236736505SChancel Liu dev_dbg(dev, "VERID: 0x%016X\n", val); 41336736505SChancel Liu 41436736505SChancel Liu micfil->verid.version = val & 41536736505SChancel Liu (MICFIL_VERID_MAJOR_MASK | MICFIL_VERID_MINOR_MASK); 41636736505SChancel Liu micfil->verid.version >>= MICFIL_VERID_MINOR_SHIFT; 41736736505SChancel Liu micfil->verid.feature = val & MICFIL_VERID_FEATURE_MASK; 41836736505SChancel Liu 41936736505SChancel Liu ret = regmap_read(micfil->regmap, REG_MICFIL_PARAM, &val); 42036736505SChancel Liu if (ret < 0) 42136736505SChancel Liu return ret; 42236736505SChancel Liu 42336736505SChancel Liu dev_dbg(dev, "PARAM: 0x%016X\n", val); 42436736505SChancel Liu 42536736505SChancel Liu micfil->param.hwvad_num = (val & MICFIL_PARAM_NUM_HWVAD_MASK) >> 42636736505SChancel Liu MICFIL_PARAM_NUM_HWVAD_SHIFT; 42736736505SChancel Liu micfil->param.hwvad_zcd = val & MICFIL_PARAM_HWVAD_ZCD; 42836736505SChancel Liu micfil->param.hwvad_energy_mode = val & MICFIL_PARAM_HWVAD_ENERGY_MODE; 42936736505SChancel Liu micfil->param.hwvad = val & MICFIL_PARAM_HWVAD; 43036736505SChancel Liu micfil->param.dc_out_bypass = val & MICFIL_PARAM_DC_OUT_BYPASS; 43136736505SChancel Liu micfil->param.dc_in_bypass = val & MICFIL_PARAM_DC_IN_BYPASS; 43236736505SChancel Liu micfil->param.low_power = val & MICFIL_PARAM_LOW_POWER; 43336736505SChancel Liu micfil->param.fil_out_width = val & MICFIL_PARAM_FIL_OUT_WIDTH; 43436736505SChancel Liu micfil->param.fifo_ptrwid = (val & MICFIL_PARAM_FIFO_PTRWID_MASK) >> 43536736505SChancel Liu MICFIL_PARAM_FIFO_PTRWID_SHIFT; 43636736505SChancel Liu micfil->param.npair = (val & MICFIL_PARAM_NPAIR_MASK) >> 43736736505SChancel Liu MICFIL_PARAM_NPAIR_SHIFT; 43836736505SChancel Liu 43936736505SChancel Liu return 0; 44036736505SChancel Liu } 44136736505SChancel Liu 44247a70e6fSCosmin Samoila /* The SRES is a self-negated bit which provides the CPU with the 44347a70e6fSCosmin Samoila * capability to initialize the PDM Interface module through the 44447a70e6fSCosmin Samoila * slave-bus interface. This bit always reads as zero, and this 44547a70e6fSCosmin Samoila * bit is only effective when MDIS is cleared 44647a70e6fSCosmin Samoila */ 44747a70e6fSCosmin Samoila static int fsl_micfil_reset(struct device *dev) 44847a70e6fSCosmin Samoila { 44947a70e6fSCosmin Samoila struct fsl_micfil *micfil = dev_get_drvdata(dev); 45047a70e6fSCosmin Samoila int ret; 45147a70e6fSCosmin Samoila 452d46c2127SSascha Hauer ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1, 453d46c2127SSascha Hauer MICFIL_CTRL1_MDIS); 4542c602c7eSSascha Hauer if (ret) 45547a70e6fSCosmin Samoila return ret; 45647a70e6fSCosmin Samoila 457d46c2127SSascha Hauer ret = regmap_set_bits(micfil->regmap, REG_MICFIL_CTRL1, 45847a70e6fSCosmin Samoila MICFIL_CTRL1_SRES); 4592c602c7eSSascha Hauer if (ret) 46047a70e6fSCosmin Samoila return ret; 46147a70e6fSCosmin Samoila 462292709b9SShengjiu Wang /* 463292709b9SShengjiu Wang * SRES is self-cleared bit, but REG_MICFIL_CTRL1 is defined 464292709b9SShengjiu Wang * as non-volatile register, so SRES still remain in regmap 465292709b9SShengjiu Wang * cache after set, that every update of REG_MICFIL_CTRL1, 466292709b9SShengjiu Wang * software reset happens. so clear it explicitly. 467292709b9SShengjiu Wang */ 468292709b9SShengjiu Wang ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1, 469292709b9SShengjiu Wang MICFIL_CTRL1_SRES); 470292709b9SShengjiu Wang if (ret) 471292709b9SShengjiu Wang return ret; 472292709b9SShengjiu Wang 473b776c4a4SShengjiu Wang /* 474b776c4a4SShengjiu Wang * Set SRES should clear CHnF flags, But even add delay here 475b776c4a4SShengjiu Wang * the CHnF may not be cleared sometimes, so clear CHnF explicitly. 476b776c4a4SShengjiu Wang */ 477b776c4a4SShengjiu Wang ret = regmap_write_bits(micfil->regmap, REG_MICFIL_STAT, 0xFF, 0xFF); 478b776c4a4SShengjiu Wang if (ret) 479b776c4a4SShengjiu Wang return ret; 480b776c4a4SShengjiu Wang 48147a70e6fSCosmin Samoila return 0; 48247a70e6fSCosmin Samoila } 48347a70e6fSCosmin Samoila 48447a70e6fSCosmin Samoila static int fsl_micfil_startup(struct snd_pcm_substream *substream, 48547a70e6fSCosmin Samoila struct snd_soc_dai *dai) 48647a70e6fSCosmin Samoila { 48747a70e6fSCosmin Samoila struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai); 488f0862123SShengjiu Wang unsigned int rates[MICFIL_NUM_RATES] = {8000, 11025, 16000, 22050, 32000, 44100, 48000}; 489f0862123SShengjiu Wang int i, j, k = 0; 490f0862123SShengjiu Wang u64 clk_rate; 49147a70e6fSCosmin Samoila 49247a70e6fSCosmin Samoila if (!micfil) { 49311106cb3STang Bin dev_err(dai->dev, "micfil dai priv_data not set\n"); 49447a70e6fSCosmin Samoila return -EINVAL; 49547a70e6fSCosmin Samoila } 49647a70e6fSCosmin Samoila 497f0862123SShengjiu Wang micfil->constraint_rates.list = micfil->constraint_rates_list; 498f0862123SShengjiu Wang micfil->constraint_rates.count = 0; 499f0862123SShengjiu Wang 500f0862123SShengjiu Wang for (j = 0; j < MICFIL_NUM_RATES; j++) { 501f0862123SShengjiu Wang for (i = 0; i < MICFIL_CLK_SRC_NUM; i++) { 502f0862123SShengjiu Wang clk_rate = clk_get_rate(micfil->clk_src[i]); 503f0862123SShengjiu Wang if (clk_rate != 0 && do_div(clk_rate, rates[j]) == 0) { 504f0862123SShengjiu Wang micfil->constraint_rates_list[k++] = rates[j]; 505f0862123SShengjiu Wang micfil->constraint_rates.count++; 506f0862123SShengjiu Wang break; 507f0862123SShengjiu Wang } 508f0862123SShengjiu Wang } 509f0862123SShengjiu Wang } 510f0862123SShengjiu Wang 511f0862123SShengjiu Wang if (micfil->constraint_rates.count > 0) 512f0862123SShengjiu Wang snd_pcm_hw_constraint_list(substream->runtime, 0, 513f0862123SShengjiu Wang SNDRV_PCM_HW_PARAM_RATE, 514f0862123SShengjiu Wang &micfil->constraint_rates); 515f0862123SShengjiu Wang 51647a70e6fSCosmin Samoila return 0; 51747a70e6fSCosmin Samoila } 51847a70e6fSCosmin Samoila 51929dbfeecSShengjiu Wang /* Enable/disable hwvad interrupts */ 52029dbfeecSShengjiu Wang static int fsl_micfil_configure_hwvad_interrupts(struct fsl_micfil *micfil, int enable) 52129dbfeecSShengjiu Wang { 52229dbfeecSShengjiu Wang u32 vadie_reg = enable ? MICFIL_VAD0_CTRL1_IE : 0; 52329dbfeecSShengjiu Wang u32 vaderie_reg = enable ? MICFIL_VAD0_CTRL1_ERIE : 0; 52429dbfeecSShengjiu Wang 52529dbfeecSShengjiu Wang /* Voice Activity Detector Error Interruption */ 52629dbfeecSShengjiu Wang regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 52729dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_ERIE, vaderie_reg); 52829dbfeecSShengjiu Wang 52929dbfeecSShengjiu Wang /* Voice Activity Detector Interruption */ 53029dbfeecSShengjiu Wang regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 53129dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_IE, vadie_reg); 53229dbfeecSShengjiu Wang 53329dbfeecSShengjiu Wang return 0; 53429dbfeecSShengjiu Wang } 53529dbfeecSShengjiu Wang 53629dbfeecSShengjiu Wang /* Configuration done only in energy-based initialization mode */ 53729dbfeecSShengjiu Wang static int fsl_micfil_init_hwvad_energy_mode(struct fsl_micfil *micfil) 53829dbfeecSShengjiu Wang { 53929dbfeecSShengjiu Wang /* Keep the VADFRENDIS bitfield cleared. */ 54029dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2, 54129dbfeecSShengjiu Wang MICFIL_VAD0_CTRL2_FRENDIS); 54229dbfeecSShengjiu Wang 54329dbfeecSShengjiu Wang /* Keep the VADPREFEN bitfield cleared. */ 54429dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2, 54529dbfeecSShengjiu Wang MICFIL_VAD0_CTRL2_PREFEN); 54629dbfeecSShengjiu Wang 54729dbfeecSShengjiu Wang /* Keep the VADSFILEN bitfield cleared. */ 54829dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG, 54929dbfeecSShengjiu Wang MICFIL_VAD0_SCONFIG_SFILEN); 55029dbfeecSShengjiu Wang 55129dbfeecSShengjiu Wang /* Keep the VADSMAXEN bitfield cleared. */ 55229dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG, 55329dbfeecSShengjiu Wang MICFIL_VAD0_SCONFIG_SMAXEN); 55429dbfeecSShengjiu Wang 55529dbfeecSShengjiu Wang /* Keep the VADNFILAUTO bitfield asserted. */ 55629dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 55729dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NFILAUT); 55829dbfeecSShengjiu Wang 55929dbfeecSShengjiu Wang /* Keep the VADNMINEN bitfield cleared. */ 56029dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 56129dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NMINEN); 56229dbfeecSShengjiu Wang 56329dbfeecSShengjiu Wang /* Keep the VADNDECEN bitfield cleared. */ 56429dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 56529dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NDECEN); 56629dbfeecSShengjiu Wang 56729dbfeecSShengjiu Wang /* Keep the VADNOREN bitfield cleared. */ 56829dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 56929dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NOREN); 57029dbfeecSShengjiu Wang 57129dbfeecSShengjiu Wang return 0; 57229dbfeecSShengjiu Wang } 57329dbfeecSShengjiu Wang 57429dbfeecSShengjiu Wang /* Configuration done only in envelope-based initialization mode */ 57529dbfeecSShengjiu Wang static int fsl_micfil_init_hwvad_envelope_mode(struct fsl_micfil *micfil) 57629dbfeecSShengjiu Wang { 57729dbfeecSShengjiu Wang /* Assert the VADFRENDIS bitfield */ 57829dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2, 57929dbfeecSShengjiu Wang MICFIL_VAD0_CTRL2_FRENDIS); 58029dbfeecSShengjiu Wang 58129dbfeecSShengjiu Wang /* Assert the VADPREFEN bitfield. */ 58229dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2, 58329dbfeecSShengjiu Wang MICFIL_VAD0_CTRL2_PREFEN); 58429dbfeecSShengjiu Wang 58529dbfeecSShengjiu Wang /* Assert the VADSFILEN bitfield. */ 58629dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG, 58729dbfeecSShengjiu Wang MICFIL_VAD0_SCONFIG_SFILEN); 58829dbfeecSShengjiu Wang 58929dbfeecSShengjiu Wang /* Assert the VADSMAXEN bitfield. */ 59029dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG, 59129dbfeecSShengjiu Wang MICFIL_VAD0_SCONFIG_SMAXEN); 59229dbfeecSShengjiu Wang 59329dbfeecSShengjiu Wang /* Clear the VADNFILAUTO bitfield */ 59429dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 59529dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NFILAUT); 59629dbfeecSShengjiu Wang 59729dbfeecSShengjiu Wang /* Assert the VADNMINEN bitfield. */ 59829dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 59929dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NMINEN); 60029dbfeecSShengjiu Wang 60129dbfeecSShengjiu Wang /* Assert the VADNDECEN bitfield. */ 60229dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 60329dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NDECEN); 60429dbfeecSShengjiu Wang 60529dbfeecSShengjiu Wang /* Assert VADNOREN bitfield. */ 60629dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 60729dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NOREN); 60829dbfeecSShengjiu Wang 60929dbfeecSShengjiu Wang return 0; 61029dbfeecSShengjiu Wang } 61129dbfeecSShengjiu Wang 61229dbfeecSShengjiu Wang /* 61329dbfeecSShengjiu Wang * Hardware Voice Active Detection: The HWVAD takes data from the input 61429dbfeecSShengjiu Wang * of a selected PDM microphone to detect if there is any 61529dbfeecSShengjiu Wang * voice activity. When a voice activity is detected, an interrupt could 61629dbfeecSShengjiu Wang * be delivered to the system. Initialization in section 8.4: 61729dbfeecSShengjiu Wang * Can work in two modes: 61829dbfeecSShengjiu Wang * -> Eneveope-based mode (section 8.4.1) 61929dbfeecSShengjiu Wang * -> Energy-based mode (section 8.4.2) 62029dbfeecSShengjiu Wang * 62129dbfeecSShengjiu Wang * It is important to remark that the HWVAD detector could be enabled 62229dbfeecSShengjiu Wang * or reset only when the MICFIL isn't running i.e. when the BSY_FIL 62329dbfeecSShengjiu Wang * bit in STAT register is cleared 62429dbfeecSShengjiu Wang */ 62529dbfeecSShengjiu Wang static int fsl_micfil_hwvad_enable(struct fsl_micfil *micfil) 62629dbfeecSShengjiu Wang { 62729dbfeecSShengjiu Wang int ret; 62829dbfeecSShengjiu Wang 62929dbfeecSShengjiu Wang micfil->vad_detected = 0; 63029dbfeecSShengjiu Wang 63129dbfeecSShengjiu Wang /* envelope-based specific initialization */ 63229dbfeecSShengjiu Wang if (micfil->vad_init_mode == MICFIL_HWVAD_ENVELOPE_MODE) 63329dbfeecSShengjiu Wang ret = fsl_micfil_init_hwvad_envelope_mode(micfil); 63429dbfeecSShengjiu Wang else 63529dbfeecSShengjiu Wang ret = fsl_micfil_init_hwvad_energy_mode(micfil); 63629dbfeecSShengjiu Wang if (ret) 63729dbfeecSShengjiu Wang return ret; 63829dbfeecSShengjiu Wang 63929dbfeecSShengjiu Wang /* Voice Activity Detector Internal Filters Initialization*/ 64029dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 64129dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_ST10); 64229dbfeecSShengjiu Wang 64329dbfeecSShengjiu Wang /* Voice Activity Detector Internal Filter */ 64429dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 64529dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_ST10); 64629dbfeecSShengjiu Wang 64729dbfeecSShengjiu Wang /* Enable Interrupts */ 64829dbfeecSShengjiu Wang ret = fsl_micfil_configure_hwvad_interrupts(micfil, 1); 64929dbfeecSShengjiu Wang if (ret) 65029dbfeecSShengjiu Wang return ret; 65129dbfeecSShengjiu Wang 65229dbfeecSShengjiu Wang /* Voice Activity Detector Reset */ 65329dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 65429dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_RST); 65529dbfeecSShengjiu Wang 65629dbfeecSShengjiu Wang /* Voice Activity Detector Enabled */ 65729dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 65829dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_EN); 65929dbfeecSShengjiu Wang 66029dbfeecSShengjiu Wang return 0; 66129dbfeecSShengjiu Wang } 66229dbfeecSShengjiu Wang 66329dbfeecSShengjiu Wang static int fsl_micfil_hwvad_disable(struct fsl_micfil *micfil) 66429dbfeecSShengjiu Wang { 66529dbfeecSShengjiu Wang struct device *dev = &micfil->pdev->dev; 66629dbfeecSShengjiu Wang int ret = 0; 66729dbfeecSShengjiu Wang 66829dbfeecSShengjiu Wang /* Disable HWVAD */ 66929dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 67029dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_EN); 67129dbfeecSShengjiu Wang 67229dbfeecSShengjiu Wang /* Disable hwvad interrupts */ 67329dbfeecSShengjiu Wang ret = fsl_micfil_configure_hwvad_interrupts(micfil, 0); 67429dbfeecSShengjiu Wang if (ret) 67529dbfeecSShengjiu Wang dev_err(dev, "Failed to disable interrupts\n"); 67629dbfeecSShengjiu Wang 67729dbfeecSShengjiu Wang return ret; 67829dbfeecSShengjiu Wang } 67929dbfeecSShengjiu Wang 68047a70e6fSCosmin Samoila static int fsl_micfil_trigger(struct snd_pcm_substream *substream, int cmd, 68147a70e6fSCosmin Samoila struct snd_soc_dai *dai) 68247a70e6fSCosmin Samoila { 68347a70e6fSCosmin Samoila struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai); 68447a70e6fSCosmin Samoila struct device *dev = &micfil->pdev->dev; 68547a70e6fSCosmin Samoila int ret; 68647a70e6fSCosmin Samoila 68747a70e6fSCosmin Samoila switch (cmd) { 68847a70e6fSCosmin Samoila case SNDRV_PCM_TRIGGER_START: 68947a70e6fSCosmin Samoila case SNDRV_PCM_TRIGGER_RESUME: 69047a70e6fSCosmin Samoila case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 69147a70e6fSCosmin Samoila ret = fsl_micfil_reset(dev); 69247a70e6fSCosmin Samoila if (ret) { 69347a70e6fSCosmin Samoila dev_err(dev, "failed to soft reset\n"); 69447a70e6fSCosmin Samoila return ret; 69547a70e6fSCosmin Samoila } 69647a70e6fSCosmin Samoila 69747a70e6fSCosmin Samoila /* DMA Interrupt Selection - DISEL bits 69847a70e6fSCosmin Samoila * 00 - DMA and IRQ disabled 69947a70e6fSCosmin Samoila * 01 - DMA req enabled 70047a70e6fSCosmin Samoila * 10 - IRQ enabled 70147a70e6fSCosmin Samoila * 11 - reserved 70247a70e6fSCosmin Samoila */ 70347a70e6fSCosmin Samoila ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1, 70417f2142bSSascha Hauer MICFIL_CTRL1_DISEL, 70517f2142bSSascha Hauer FIELD_PREP(MICFIL_CTRL1_DISEL, MICFIL_CTRL1_DISEL_DMA)); 7062c602c7eSSascha Hauer if (ret) 70747a70e6fSCosmin Samoila return ret; 70847a70e6fSCosmin Samoila 70947a70e6fSCosmin Samoila /* Enable the module */ 710d46c2127SSascha Hauer ret = regmap_set_bits(micfil->regmap, REG_MICFIL_CTRL1, 71147a70e6fSCosmin Samoila MICFIL_CTRL1_PDMIEN); 7122c602c7eSSascha Hauer if (ret) 71347a70e6fSCosmin Samoila return ret; 71447a70e6fSCosmin Samoila 71529dbfeecSShengjiu Wang if (micfil->vad_enabled) 71629dbfeecSShengjiu Wang fsl_micfil_hwvad_enable(micfil); 71729dbfeecSShengjiu Wang 71847a70e6fSCosmin Samoila break; 71947a70e6fSCosmin Samoila case SNDRV_PCM_TRIGGER_STOP: 72047a70e6fSCosmin Samoila case SNDRV_PCM_TRIGGER_SUSPEND: 72147a70e6fSCosmin Samoila case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 72229dbfeecSShengjiu Wang if (micfil->vad_enabled) 72329dbfeecSShengjiu Wang fsl_micfil_hwvad_disable(micfil); 72429dbfeecSShengjiu Wang 72547a70e6fSCosmin Samoila /* Disable the module */ 726d46c2127SSascha Hauer ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1, 727d46c2127SSascha Hauer MICFIL_CTRL1_PDMIEN); 7282c602c7eSSascha Hauer if (ret) 72947a70e6fSCosmin Samoila return ret; 73047a70e6fSCosmin Samoila 73147a70e6fSCosmin Samoila ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1, 73217f2142bSSascha Hauer MICFIL_CTRL1_DISEL, 73317f2142bSSascha Hauer FIELD_PREP(MICFIL_CTRL1_DISEL, MICFIL_CTRL1_DISEL_DISABLE)); 7342c602c7eSSascha Hauer if (ret) 73547a70e6fSCosmin Samoila return ret; 73647a70e6fSCosmin Samoila break; 73747a70e6fSCosmin Samoila default: 73847a70e6fSCosmin Samoila return -EINVAL; 73947a70e6fSCosmin Samoila } 74047a70e6fSCosmin Samoila return 0; 74147a70e6fSCosmin Samoila } 74247a70e6fSCosmin Samoila 74393f54100SShengjiu Wang static int fsl_micfil_reparent_rootclk(struct fsl_micfil *micfil, unsigned int sample_rate) 74493f54100SShengjiu Wang { 74593f54100SShengjiu Wang struct device *dev = &micfil->pdev->dev; 74693f54100SShengjiu Wang u64 ratio = sample_rate; 74793f54100SShengjiu Wang struct clk *clk; 74893f54100SShengjiu Wang int ret; 74993f54100SShengjiu Wang 75093f54100SShengjiu Wang /* Get root clock */ 75193f54100SShengjiu Wang clk = micfil->mclk; 75293f54100SShengjiu Wang 75393f54100SShengjiu Wang /* Disable clock first, for it was enabled by pm_runtime */ 75493f54100SShengjiu Wang clk_disable_unprepare(clk); 75593f54100SShengjiu Wang fsl_asoc_reparent_pll_clocks(dev, clk, micfil->pll8k_clk, 75693f54100SShengjiu Wang micfil->pll11k_clk, ratio); 75793f54100SShengjiu Wang ret = clk_prepare_enable(clk); 75893f54100SShengjiu Wang if (ret) 75993f54100SShengjiu Wang return ret; 76093f54100SShengjiu Wang 76193f54100SShengjiu Wang return 0; 76293f54100SShengjiu Wang } 76393f54100SShengjiu Wang 76447a70e6fSCosmin Samoila static int fsl_micfil_hw_params(struct snd_pcm_substream *substream, 76547a70e6fSCosmin Samoila struct snd_pcm_hw_params *params, 76647a70e6fSCosmin Samoila struct snd_soc_dai *dai) 76747a70e6fSCosmin Samoila { 76847a70e6fSCosmin Samoila struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai); 76947a70e6fSCosmin Samoila unsigned int channels = params_channels(params); 77047a70e6fSCosmin Samoila unsigned int rate = params_rate(params); 771cc5ef57dSSascha Hauer int clk_div = 8; 772cc5ef57dSSascha Hauer int osr = MICFIL_OSR_DEFAULT; 77347a70e6fSCosmin Samoila int ret; 77447a70e6fSCosmin Samoila 77547a70e6fSCosmin Samoila /* 1. Disable the module */ 776d46c2127SSascha Hauer ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1, 777d46c2127SSascha Hauer MICFIL_CTRL1_PDMIEN); 7782c602c7eSSascha Hauer if (ret) 77947a70e6fSCosmin Samoila return ret; 78047a70e6fSCosmin Samoila 78147a70e6fSCosmin Samoila /* enable channels */ 78247a70e6fSCosmin Samoila ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1, 78347a70e6fSCosmin Samoila 0xFF, ((1 << channels) - 1)); 7842c602c7eSSascha Hauer if (ret) 78547a70e6fSCosmin Samoila return ret; 78647a70e6fSCosmin Samoila 78793f54100SShengjiu Wang ret = fsl_micfil_reparent_rootclk(micfil, rate); 78893f54100SShengjiu Wang if (ret) 78993f54100SShengjiu Wang return ret; 79093f54100SShengjiu Wang 791cc5ef57dSSascha Hauer ret = clk_set_rate(micfil->mclk, rate * clk_div * osr * 8); 792cc5ef57dSSascha Hauer if (ret) 79347a70e6fSCosmin Samoila return ret; 794cc5ef57dSSascha Hauer 795cc5ef57dSSascha Hauer ret = micfil_set_quality(micfil); 796cc5ef57dSSascha Hauer if (ret) 797cc5ef57dSSascha Hauer return ret; 798cc5ef57dSSascha Hauer 799cc5ef57dSSascha Hauer ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2, 800cc5ef57dSSascha Hauer MICFIL_CTRL2_CLKDIV | MICFIL_CTRL2_CICOSR, 801cc5ef57dSSascha Hauer FIELD_PREP(MICFIL_CTRL2_CLKDIV, clk_div) | 802cc5ef57dSSascha Hauer FIELD_PREP(MICFIL_CTRL2_CICOSR, 16 - osr)); 80347a70e6fSCosmin Samoila 80429dbfeecSShengjiu Wang /* Configure CIC OSR in VADCICOSR */ 80529dbfeecSShengjiu Wang regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 80629dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_CICOSR, 80729dbfeecSShengjiu Wang FIELD_PREP(MICFIL_VAD0_CTRL1_CICOSR, 16 - osr)); 80829dbfeecSShengjiu Wang 80929dbfeecSShengjiu Wang /* Configure source channel in VADCHSEL */ 81029dbfeecSShengjiu Wang regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 81129dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_CHSEL, 81229dbfeecSShengjiu Wang FIELD_PREP(MICFIL_VAD0_CTRL1_CHSEL, (channels - 1))); 81329dbfeecSShengjiu Wang 8142495ba26SSascha Hauer micfil->dma_params_rx.peripheral_config = &micfil->sdmacfg; 8152495ba26SSascha Hauer micfil->dma_params_rx.peripheral_size = sizeof(micfil->sdmacfg); 8162495ba26SSascha Hauer micfil->sdmacfg.n_fifos_src = channels; 8172495ba26SSascha Hauer micfil->sdmacfg.sw_done = true; 81847a70e6fSCosmin Samoila micfil->dma_params_rx.maxburst = channels * MICFIL_DMA_MAXBURST_RX; 81977a7a6e9SChancel Liu if (micfil->soc->use_edma) 82077a7a6e9SChancel Liu micfil->dma_params_rx.maxburst = channels; 82147a70e6fSCosmin Samoila 82247a70e6fSCosmin Samoila return 0; 82347a70e6fSCosmin Samoila } 82447a70e6fSCosmin Samoila 82547a70e6fSCosmin Samoila static int fsl_micfil_dai_probe(struct snd_soc_dai *cpu_dai) 82647a70e6fSCosmin Samoila { 82747a70e6fSCosmin Samoila struct fsl_micfil *micfil = dev_get_drvdata(cpu_dai->dev); 8283b13b143SShengjiu Wang struct device *dev = cpu_dai->dev; 8293b13b143SShengjiu Wang unsigned int val = 0; 8303b13b143SShengjiu Wang int ret, i; 83147a70e6fSCosmin Samoila 8323b13b143SShengjiu Wang micfil->quality = QUALITY_VLOW0; 83329dbfeecSShengjiu Wang micfil->card = cpu_dai->component->card; 83447a70e6fSCosmin Samoila 8353b13b143SShengjiu Wang /* set default gain to 2 */ 8363b13b143SShengjiu Wang regmap_write(micfil->regmap, REG_MICFIL_OUT_CTRL, 0x22222222); 8373b13b143SShengjiu Wang 8383b13b143SShengjiu Wang /* set DC Remover in bypass mode*/ 8393b13b143SShengjiu Wang for (i = 0; i < MICFIL_OUTPUT_CHANNELS; i++) 8403b13b143SShengjiu Wang val |= MICFIL_DC_BYPASS << MICFIL_DC_CHX_SHIFT(i); 8413b13b143SShengjiu Wang ret = regmap_update_bits(micfil->regmap, REG_MICFIL_DC_CTRL, 8423b13b143SShengjiu Wang MICFIL_DC_CTRL_CONFIG, val); 8433b13b143SShengjiu Wang if (ret) { 8443b13b143SShengjiu Wang dev_err(dev, "failed to set DC Remover mode bits\n"); 8453b13b143SShengjiu Wang return ret; 8463b13b143SShengjiu Wang } 8473b13b143SShengjiu Wang micfil->dc_remover = MICFIL_DC_BYPASS; 84847a70e6fSCosmin Samoila 84947a70e6fSCosmin Samoila snd_soc_dai_init_dma_data(cpu_dai, NULL, 85047a70e6fSCosmin Samoila &micfil->dma_params_rx); 85147a70e6fSCosmin Samoila 85247a70e6fSCosmin Samoila /* FIFO Watermark Control - FIFOWMK*/ 85347a70e6fSCosmin Samoila ret = regmap_update_bits(micfil->regmap, REG_MICFIL_FIFO_CTRL, 85417f2142bSSascha Hauer MICFIL_FIFO_CTRL_FIFOWMK, 85517f2142bSSascha Hauer FIELD_PREP(MICFIL_FIFO_CTRL_FIFOWMK, micfil->soc->fifo_depth - 1)); 8562c602c7eSSascha Hauer if (ret) 85747a70e6fSCosmin Samoila return ret; 85847a70e6fSCosmin Samoila 85947a70e6fSCosmin Samoila return 0; 86047a70e6fSCosmin Samoila } 86147a70e6fSCosmin Samoila 8628b339bebSChancel Liu static int fsl_micfil_component_probe(struct snd_soc_component *component) 8638b339bebSChancel Liu { 8648b339bebSChancel Liu struct fsl_micfil *micfil = snd_soc_component_get_drvdata(component); 8658b339bebSChancel Liu 8668b339bebSChancel Liu if (micfil->soc->volume_sx) 8678b339bebSChancel Liu snd_soc_add_component_controls(component, fsl_micfil_volume_sx_controls, 8688b339bebSChancel Liu ARRAY_SIZE(fsl_micfil_volume_sx_controls)); 8698b339bebSChancel Liu else 8708b339bebSChancel Liu snd_soc_add_component_controls(component, fsl_micfil_volume_controls, 8718b339bebSChancel Liu ARRAY_SIZE(fsl_micfil_volume_controls)); 8728b339bebSChancel Liu 8738b339bebSChancel Liu return 0; 8748b339bebSChancel Liu } 8758b339bebSChancel Liu 876ac27ca16SKuninori Morimoto static const struct snd_soc_dai_ops fsl_micfil_dai_ops = { 87747a70e6fSCosmin Samoila .probe = fsl_micfil_dai_probe, 878ac27ca16SKuninori Morimoto .startup = fsl_micfil_startup, 879ac27ca16SKuninori Morimoto .trigger = fsl_micfil_trigger, 880ac27ca16SKuninori Morimoto .hw_params = fsl_micfil_hw_params, 881ac27ca16SKuninori Morimoto }; 882ac27ca16SKuninori Morimoto 883ac27ca16SKuninori Morimoto static struct snd_soc_dai_driver fsl_micfil_dai = { 88447a70e6fSCosmin Samoila .capture = { 88547a70e6fSCosmin Samoila .stream_name = "CPU-Capture", 88647a70e6fSCosmin Samoila .channels_min = 1, 88747a70e6fSCosmin Samoila .channels_max = 8, 88899c08cdbSSascha Hauer .rates = SNDRV_PCM_RATE_8000_48000, 88999c08cdbSSascha Hauer .formats = SNDRV_PCM_FMTBIT_S16_LE, 89047a70e6fSCosmin Samoila }, 89147a70e6fSCosmin Samoila .ops = &fsl_micfil_dai_ops, 89247a70e6fSCosmin Samoila }; 89347a70e6fSCosmin Samoila 89447a70e6fSCosmin Samoila static const struct snd_soc_component_driver fsl_micfil_component = { 89547a70e6fSCosmin Samoila .name = "fsl-micfil-dai", 8968b339bebSChancel Liu .probe = fsl_micfil_component_probe, 89747a70e6fSCosmin Samoila .controls = fsl_micfil_snd_controls, 89847a70e6fSCosmin Samoila .num_controls = ARRAY_SIZE(fsl_micfil_snd_controls), 899978bd27cSShengjiu Wang .legacy_dai_naming = 1, 90047a70e6fSCosmin Samoila }; 90147a70e6fSCosmin Samoila 90247a70e6fSCosmin Samoila /* REGMAP */ 90347a70e6fSCosmin Samoila static const struct reg_default fsl_micfil_reg_defaults[] = { 90447a70e6fSCosmin Samoila {REG_MICFIL_CTRL1, 0x00000000}, 90547a70e6fSCosmin Samoila {REG_MICFIL_CTRL2, 0x00000000}, 90647a70e6fSCosmin Samoila {REG_MICFIL_STAT, 0x00000000}, 90747a70e6fSCosmin Samoila {REG_MICFIL_FIFO_CTRL, 0x00000007}, 90847a70e6fSCosmin Samoila {REG_MICFIL_FIFO_STAT, 0x00000000}, 90947a70e6fSCosmin Samoila {REG_MICFIL_DATACH0, 0x00000000}, 91047a70e6fSCosmin Samoila {REG_MICFIL_DATACH1, 0x00000000}, 91147a70e6fSCosmin Samoila {REG_MICFIL_DATACH2, 0x00000000}, 91247a70e6fSCosmin Samoila {REG_MICFIL_DATACH3, 0x00000000}, 91347a70e6fSCosmin Samoila {REG_MICFIL_DATACH4, 0x00000000}, 91447a70e6fSCosmin Samoila {REG_MICFIL_DATACH5, 0x00000000}, 91547a70e6fSCosmin Samoila {REG_MICFIL_DATACH6, 0x00000000}, 91647a70e6fSCosmin Samoila {REG_MICFIL_DATACH7, 0x00000000}, 91747a70e6fSCosmin Samoila {REG_MICFIL_DC_CTRL, 0x00000000}, 91847a70e6fSCosmin Samoila {REG_MICFIL_OUT_CTRL, 0x00000000}, 91947a70e6fSCosmin Samoila {REG_MICFIL_OUT_STAT, 0x00000000}, 92047a70e6fSCosmin Samoila {REG_MICFIL_VAD0_CTRL1, 0x00000000}, 92147a70e6fSCosmin Samoila {REG_MICFIL_VAD0_CTRL2, 0x000A0000}, 92247a70e6fSCosmin Samoila {REG_MICFIL_VAD0_STAT, 0x00000000}, 92347a70e6fSCosmin Samoila {REG_MICFIL_VAD0_SCONFIG, 0x00000000}, 92447a70e6fSCosmin Samoila {REG_MICFIL_VAD0_NCONFIG, 0x80000000}, 92547a70e6fSCosmin Samoila {REG_MICFIL_VAD0_NDATA, 0x00000000}, 92647a70e6fSCosmin Samoila {REG_MICFIL_VAD0_ZCD, 0x00000004}, 92747a70e6fSCosmin Samoila }; 92847a70e6fSCosmin Samoila 92947a70e6fSCosmin Samoila static bool fsl_micfil_readable_reg(struct device *dev, unsigned int reg) 93047a70e6fSCosmin Samoila { 93147a70e6fSCosmin Samoila switch (reg) { 93247a70e6fSCosmin Samoila case REG_MICFIL_CTRL1: 93347a70e6fSCosmin Samoila case REG_MICFIL_CTRL2: 93447a70e6fSCosmin Samoila case REG_MICFIL_STAT: 93547a70e6fSCosmin Samoila case REG_MICFIL_FIFO_CTRL: 93647a70e6fSCosmin Samoila case REG_MICFIL_FIFO_STAT: 93747a70e6fSCosmin Samoila case REG_MICFIL_DATACH0: 93847a70e6fSCosmin Samoila case REG_MICFIL_DATACH1: 93947a70e6fSCosmin Samoila case REG_MICFIL_DATACH2: 94047a70e6fSCosmin Samoila case REG_MICFIL_DATACH3: 94147a70e6fSCosmin Samoila case REG_MICFIL_DATACH4: 94247a70e6fSCosmin Samoila case REG_MICFIL_DATACH5: 94347a70e6fSCosmin Samoila case REG_MICFIL_DATACH6: 94447a70e6fSCosmin Samoila case REG_MICFIL_DATACH7: 94547a70e6fSCosmin Samoila case REG_MICFIL_DC_CTRL: 94647a70e6fSCosmin Samoila case REG_MICFIL_OUT_CTRL: 94747a70e6fSCosmin Samoila case REG_MICFIL_OUT_STAT: 94851d765f7SChancel Liu case REG_MICFIL_FSYNC_CTRL: 94951d765f7SChancel Liu case REG_MICFIL_VERID: 95051d765f7SChancel Liu case REG_MICFIL_PARAM: 95147a70e6fSCosmin Samoila case REG_MICFIL_VAD0_CTRL1: 95247a70e6fSCosmin Samoila case REG_MICFIL_VAD0_CTRL2: 95347a70e6fSCosmin Samoila case REG_MICFIL_VAD0_STAT: 95447a70e6fSCosmin Samoila case REG_MICFIL_VAD0_SCONFIG: 95547a70e6fSCosmin Samoila case REG_MICFIL_VAD0_NCONFIG: 95647a70e6fSCosmin Samoila case REG_MICFIL_VAD0_NDATA: 95747a70e6fSCosmin Samoila case REG_MICFIL_VAD0_ZCD: 95847a70e6fSCosmin Samoila return true; 95947a70e6fSCosmin Samoila default: 96047a70e6fSCosmin Samoila return false; 96147a70e6fSCosmin Samoila } 96247a70e6fSCosmin Samoila } 96347a70e6fSCosmin Samoila 96447a70e6fSCosmin Samoila static bool fsl_micfil_writeable_reg(struct device *dev, unsigned int reg) 96547a70e6fSCosmin Samoila { 96647a70e6fSCosmin Samoila switch (reg) { 96747a70e6fSCosmin Samoila case REG_MICFIL_CTRL1: 96847a70e6fSCosmin Samoila case REG_MICFIL_CTRL2: 96947a70e6fSCosmin Samoila case REG_MICFIL_STAT: /* Write 1 to Clear */ 97047a70e6fSCosmin Samoila case REG_MICFIL_FIFO_CTRL: 97147a70e6fSCosmin Samoila case REG_MICFIL_FIFO_STAT: /* Write 1 to Clear */ 97247a70e6fSCosmin Samoila case REG_MICFIL_DC_CTRL: 97347a70e6fSCosmin Samoila case REG_MICFIL_OUT_CTRL: 97447a70e6fSCosmin Samoila case REG_MICFIL_OUT_STAT: /* Write 1 to Clear */ 97551d765f7SChancel Liu case REG_MICFIL_FSYNC_CTRL: 97647a70e6fSCosmin Samoila case REG_MICFIL_VAD0_CTRL1: 97747a70e6fSCosmin Samoila case REG_MICFIL_VAD0_CTRL2: 97847a70e6fSCosmin Samoila case REG_MICFIL_VAD0_STAT: /* Write 1 to Clear */ 97947a70e6fSCosmin Samoila case REG_MICFIL_VAD0_SCONFIG: 98047a70e6fSCosmin Samoila case REG_MICFIL_VAD0_NCONFIG: 98147a70e6fSCosmin Samoila case REG_MICFIL_VAD0_ZCD: 98247a70e6fSCosmin Samoila return true; 98347a70e6fSCosmin Samoila default: 98447a70e6fSCosmin Samoila return false; 98547a70e6fSCosmin Samoila } 98647a70e6fSCosmin Samoila } 98747a70e6fSCosmin Samoila 98847a70e6fSCosmin Samoila static bool fsl_micfil_volatile_reg(struct device *dev, unsigned int reg) 98947a70e6fSCosmin Samoila { 99047a70e6fSCosmin Samoila switch (reg) { 99147a70e6fSCosmin Samoila case REG_MICFIL_STAT: 99247a70e6fSCosmin Samoila case REG_MICFIL_DATACH0: 99347a70e6fSCosmin Samoila case REG_MICFIL_DATACH1: 99447a70e6fSCosmin Samoila case REG_MICFIL_DATACH2: 99547a70e6fSCosmin Samoila case REG_MICFIL_DATACH3: 99647a70e6fSCosmin Samoila case REG_MICFIL_DATACH4: 99747a70e6fSCosmin Samoila case REG_MICFIL_DATACH5: 99847a70e6fSCosmin Samoila case REG_MICFIL_DATACH6: 99947a70e6fSCosmin Samoila case REG_MICFIL_DATACH7: 100051d765f7SChancel Liu case REG_MICFIL_VERID: 100151d765f7SChancel Liu case REG_MICFIL_PARAM: 100247a70e6fSCosmin Samoila case REG_MICFIL_VAD0_STAT: 100347a70e6fSCosmin Samoila case REG_MICFIL_VAD0_NDATA: 100447a70e6fSCosmin Samoila return true; 100547a70e6fSCosmin Samoila default: 100647a70e6fSCosmin Samoila return false; 100747a70e6fSCosmin Samoila } 100847a70e6fSCosmin Samoila } 100947a70e6fSCosmin Samoila 101047a70e6fSCosmin Samoila static const struct regmap_config fsl_micfil_regmap_config = { 101147a70e6fSCosmin Samoila .reg_bits = 32, 101247a70e6fSCosmin Samoila .reg_stride = 4, 101347a70e6fSCosmin Samoila .val_bits = 32, 101447a70e6fSCosmin Samoila 101547a70e6fSCosmin Samoila .max_register = REG_MICFIL_VAD0_ZCD, 101647a70e6fSCosmin Samoila .reg_defaults = fsl_micfil_reg_defaults, 101747a70e6fSCosmin Samoila .num_reg_defaults = ARRAY_SIZE(fsl_micfil_reg_defaults), 101847a70e6fSCosmin Samoila .readable_reg = fsl_micfil_readable_reg, 101947a70e6fSCosmin Samoila .volatile_reg = fsl_micfil_volatile_reg, 102047a70e6fSCosmin Samoila .writeable_reg = fsl_micfil_writeable_reg, 102147a70e6fSCosmin Samoila .cache_type = REGCACHE_RBTREE, 102247a70e6fSCosmin Samoila }; 102347a70e6fSCosmin Samoila 102447a70e6fSCosmin Samoila /* END OF REGMAP */ 102547a70e6fSCosmin Samoila 102647a70e6fSCosmin Samoila static irqreturn_t micfil_isr(int irq, void *devid) 102747a70e6fSCosmin Samoila { 102847a70e6fSCosmin Samoila struct fsl_micfil *micfil = (struct fsl_micfil *)devid; 102947a70e6fSCosmin Samoila struct platform_device *pdev = micfil->pdev; 103047a70e6fSCosmin Samoila u32 stat_reg; 103147a70e6fSCosmin Samoila u32 fifo_stat_reg; 103247a70e6fSCosmin Samoila u32 ctrl1_reg; 103347a70e6fSCosmin Samoila bool dma_enabled; 103447a70e6fSCosmin Samoila int i; 103547a70e6fSCosmin Samoila 103647a70e6fSCosmin Samoila regmap_read(micfil->regmap, REG_MICFIL_STAT, &stat_reg); 103747a70e6fSCosmin Samoila regmap_read(micfil->regmap, REG_MICFIL_CTRL1, &ctrl1_reg); 103847a70e6fSCosmin Samoila regmap_read(micfil->regmap, REG_MICFIL_FIFO_STAT, &fifo_stat_reg); 103947a70e6fSCosmin Samoila 104017f2142bSSascha Hauer dma_enabled = FIELD_GET(MICFIL_CTRL1_DISEL, ctrl1_reg) == MICFIL_CTRL1_DISEL_DMA; 104147a70e6fSCosmin Samoila 104247a70e6fSCosmin Samoila /* Channel 0-7 Output Data Flags */ 104347a70e6fSCosmin Samoila for (i = 0; i < MICFIL_OUTPUT_CHANNELS; i++) { 104417f2142bSSascha Hauer if (stat_reg & MICFIL_STAT_CHXF(i)) 104547a70e6fSCosmin Samoila dev_dbg(&pdev->dev, 104647a70e6fSCosmin Samoila "Data available in Data Channel %d\n", i); 104747a70e6fSCosmin Samoila /* if DMA is not enabled, field must be written with 1 104847a70e6fSCosmin Samoila * to clear 104947a70e6fSCosmin Samoila */ 105047a70e6fSCosmin Samoila if (!dma_enabled) 105147a70e6fSCosmin Samoila regmap_write_bits(micfil->regmap, 105247a70e6fSCosmin Samoila REG_MICFIL_STAT, 105317f2142bSSascha Hauer MICFIL_STAT_CHXF(i), 1054*537f3276SShengjiu Wang MICFIL_STAT_CHXF(i)); 105547a70e6fSCosmin Samoila } 105647a70e6fSCosmin Samoila 105747a70e6fSCosmin Samoila for (i = 0; i < MICFIL_FIFO_NUM; i++) { 105817f2142bSSascha Hauer if (fifo_stat_reg & MICFIL_FIFO_STAT_FIFOX_OVER(i)) 105947a70e6fSCosmin Samoila dev_dbg(&pdev->dev, 106047a70e6fSCosmin Samoila "FIFO Overflow Exception flag for channel %d\n", 106147a70e6fSCosmin Samoila i); 106247a70e6fSCosmin Samoila 106317f2142bSSascha Hauer if (fifo_stat_reg & MICFIL_FIFO_STAT_FIFOX_UNDER(i)) 106447a70e6fSCosmin Samoila dev_dbg(&pdev->dev, 106547a70e6fSCosmin Samoila "FIFO Underflow Exception flag for channel %d\n", 106647a70e6fSCosmin Samoila i); 106747a70e6fSCosmin Samoila } 106847a70e6fSCosmin Samoila 106947a70e6fSCosmin Samoila return IRQ_HANDLED; 107047a70e6fSCosmin Samoila } 107147a70e6fSCosmin Samoila 107247a70e6fSCosmin Samoila static irqreturn_t micfil_err_isr(int irq, void *devid) 107347a70e6fSCosmin Samoila { 107447a70e6fSCosmin Samoila struct fsl_micfil *micfil = (struct fsl_micfil *)devid; 107547a70e6fSCosmin Samoila struct platform_device *pdev = micfil->pdev; 107647a70e6fSCosmin Samoila u32 stat_reg; 107747a70e6fSCosmin Samoila 107847a70e6fSCosmin Samoila regmap_read(micfil->regmap, REG_MICFIL_STAT, &stat_reg); 107947a70e6fSCosmin Samoila 1080bd2cffd1SSascha Hauer if (stat_reg & MICFIL_STAT_BSY_FIL) 108147a70e6fSCosmin Samoila dev_dbg(&pdev->dev, "isr: Decimation Filter is running\n"); 108247a70e6fSCosmin Samoila 1083bd2cffd1SSascha Hauer if (stat_reg & MICFIL_STAT_FIR_RDY) 108447a70e6fSCosmin Samoila dev_dbg(&pdev->dev, "isr: FIR Filter Data ready\n"); 108547a70e6fSCosmin Samoila 1086bd2cffd1SSascha Hauer if (stat_reg & MICFIL_STAT_LOWFREQF) { 108747a70e6fSCosmin Samoila dev_dbg(&pdev->dev, "isr: ipg_clk_app is too low\n"); 108847a70e6fSCosmin Samoila regmap_write_bits(micfil->regmap, REG_MICFIL_STAT, 1089*537f3276SShengjiu Wang MICFIL_STAT_LOWFREQF, MICFIL_STAT_LOWFREQF); 109047a70e6fSCosmin Samoila } 109147a70e6fSCosmin Samoila 109247a70e6fSCosmin Samoila return IRQ_HANDLED; 109347a70e6fSCosmin Samoila } 109447a70e6fSCosmin Samoila 109529dbfeecSShengjiu Wang static irqreturn_t voice_detected_fn(int irq, void *devid) 109629dbfeecSShengjiu Wang { 109729dbfeecSShengjiu Wang struct fsl_micfil *micfil = (struct fsl_micfil *)devid; 109829dbfeecSShengjiu Wang struct snd_kcontrol *kctl; 109929dbfeecSShengjiu Wang 110029dbfeecSShengjiu Wang if (!micfil->card) 110129dbfeecSShengjiu Wang return IRQ_HANDLED; 110229dbfeecSShengjiu Wang 110329dbfeecSShengjiu Wang kctl = snd_soc_card_get_kcontrol(micfil->card, "VAD Detected"); 110429dbfeecSShengjiu Wang if (!kctl) 110529dbfeecSShengjiu Wang return IRQ_HANDLED; 110629dbfeecSShengjiu Wang 110729dbfeecSShengjiu Wang if (micfil->vad_detected) 110829dbfeecSShengjiu Wang snd_ctl_notify(micfil->card->snd_card, 110929dbfeecSShengjiu Wang SNDRV_CTL_EVENT_MASK_VALUE, 111029dbfeecSShengjiu Wang &kctl->id); 111129dbfeecSShengjiu Wang 111229dbfeecSShengjiu Wang return IRQ_HANDLED; 111329dbfeecSShengjiu Wang } 111429dbfeecSShengjiu Wang 111529dbfeecSShengjiu Wang static irqreturn_t hwvad_isr(int irq, void *devid) 111629dbfeecSShengjiu Wang { 111729dbfeecSShengjiu Wang struct fsl_micfil *micfil = (struct fsl_micfil *)devid; 111829dbfeecSShengjiu Wang struct device *dev = &micfil->pdev->dev; 111929dbfeecSShengjiu Wang u32 vad0_reg; 112029dbfeecSShengjiu Wang int ret; 112129dbfeecSShengjiu Wang 112229dbfeecSShengjiu Wang regmap_read(micfil->regmap, REG_MICFIL_VAD0_STAT, &vad0_reg); 112329dbfeecSShengjiu Wang 112429dbfeecSShengjiu Wang /* 112529dbfeecSShengjiu Wang * The only difference between MICFIL_VAD0_STAT_EF and 112629dbfeecSShengjiu Wang * MICFIL_VAD0_STAT_IF is that the former requires Write 112729dbfeecSShengjiu Wang * 1 to Clear. Since both flags are set, it is enough 112829dbfeecSShengjiu Wang * to only read one of them 112929dbfeecSShengjiu Wang */ 113029dbfeecSShengjiu Wang if (vad0_reg & MICFIL_VAD0_STAT_IF) { 113129dbfeecSShengjiu Wang /* Write 1 to clear */ 113229dbfeecSShengjiu Wang regmap_write_bits(micfil->regmap, REG_MICFIL_VAD0_STAT, 113329dbfeecSShengjiu Wang MICFIL_VAD0_STAT_IF, 113429dbfeecSShengjiu Wang MICFIL_VAD0_STAT_IF); 113529dbfeecSShengjiu Wang 113629dbfeecSShengjiu Wang micfil->vad_detected = 1; 113729dbfeecSShengjiu Wang } 113829dbfeecSShengjiu Wang 113929dbfeecSShengjiu Wang ret = fsl_micfil_hwvad_disable(micfil); 114029dbfeecSShengjiu Wang if (ret) 114129dbfeecSShengjiu Wang dev_err(dev, "Failed to disable hwvad\n"); 114229dbfeecSShengjiu Wang 114329dbfeecSShengjiu Wang return IRQ_WAKE_THREAD; 114429dbfeecSShengjiu Wang } 114529dbfeecSShengjiu Wang 114629dbfeecSShengjiu Wang static irqreturn_t hwvad_err_isr(int irq, void *devid) 114729dbfeecSShengjiu Wang { 114829dbfeecSShengjiu Wang struct fsl_micfil *micfil = (struct fsl_micfil *)devid; 114929dbfeecSShengjiu Wang struct device *dev = &micfil->pdev->dev; 115029dbfeecSShengjiu Wang u32 vad0_reg; 115129dbfeecSShengjiu Wang 115229dbfeecSShengjiu Wang regmap_read(micfil->regmap, REG_MICFIL_VAD0_STAT, &vad0_reg); 115329dbfeecSShengjiu Wang 115429dbfeecSShengjiu Wang if (vad0_reg & MICFIL_VAD0_STAT_INSATF) 115529dbfeecSShengjiu Wang dev_dbg(dev, "voice activity input overflow/underflow detected\n"); 115629dbfeecSShengjiu Wang 115729dbfeecSShengjiu Wang return IRQ_HANDLED; 115829dbfeecSShengjiu Wang } 115929dbfeecSShengjiu Wang 116036736505SChancel Liu static int fsl_micfil_runtime_suspend(struct device *dev); 116136736505SChancel Liu static int fsl_micfil_runtime_resume(struct device *dev); 116236736505SChancel Liu 116347a70e6fSCosmin Samoila static int fsl_micfil_probe(struct platform_device *pdev) 116447a70e6fSCosmin Samoila { 116547a70e6fSCosmin Samoila struct device_node *np = pdev->dev.of_node; 116647a70e6fSCosmin Samoila struct fsl_micfil *micfil; 116747a70e6fSCosmin Samoila struct resource *res; 116847a70e6fSCosmin Samoila void __iomem *regs; 116947a70e6fSCosmin Samoila int ret, i; 117047a70e6fSCosmin Samoila 117147a70e6fSCosmin Samoila micfil = devm_kzalloc(&pdev->dev, sizeof(*micfil), GFP_KERNEL); 117247a70e6fSCosmin Samoila if (!micfil) 117347a70e6fSCosmin Samoila return -ENOMEM; 117447a70e6fSCosmin Samoila 117547a70e6fSCosmin Samoila micfil->pdev = pdev; 11767eb10bfbSJustin Stitt strscpy(micfil->name, np->name, sizeof(micfil->name)); 117747a70e6fSCosmin Samoila 1178d7388718SFabio Estevam micfil->soc = of_device_get_match_data(&pdev->dev); 117947a70e6fSCosmin Samoila 118047a70e6fSCosmin Samoila /* ipg_clk is used to control the registers 118147a70e6fSCosmin Samoila * ipg_clk_app is used to operate the filter 118247a70e6fSCosmin Samoila */ 118347a70e6fSCosmin Samoila micfil->mclk = devm_clk_get(&pdev->dev, "ipg_clk_app"); 118447a70e6fSCosmin Samoila if (IS_ERR(micfil->mclk)) { 118547a70e6fSCosmin Samoila dev_err(&pdev->dev, "failed to get core clock: %ld\n", 118647a70e6fSCosmin Samoila PTR_ERR(micfil->mclk)); 118747a70e6fSCosmin Samoila return PTR_ERR(micfil->mclk); 118847a70e6fSCosmin Samoila } 118947a70e6fSCosmin Samoila 1190b5cf28f7SShengjiu Wang micfil->busclk = devm_clk_get(&pdev->dev, "ipg_clk"); 1191b5cf28f7SShengjiu Wang if (IS_ERR(micfil->busclk)) { 1192b5cf28f7SShengjiu Wang dev_err(&pdev->dev, "failed to get ipg clock: %ld\n", 1193b5cf28f7SShengjiu Wang PTR_ERR(micfil->busclk)); 1194b5cf28f7SShengjiu Wang return PTR_ERR(micfil->busclk); 1195b5cf28f7SShengjiu Wang } 1196b5cf28f7SShengjiu Wang 119793f54100SShengjiu Wang fsl_asoc_get_pll_clocks(&pdev->dev, &micfil->pll8k_clk, 119893f54100SShengjiu Wang &micfil->pll11k_clk); 119993f54100SShengjiu Wang 1200f0862123SShengjiu Wang micfil->clk_src[MICFIL_AUDIO_PLL1] = micfil->pll8k_clk; 1201f0862123SShengjiu Wang micfil->clk_src[MICFIL_AUDIO_PLL2] = micfil->pll11k_clk; 1202f0862123SShengjiu Wang micfil->clk_src[MICFIL_CLK_EXT3] = devm_clk_get(&pdev->dev, "clkext3"); 1203f0862123SShengjiu Wang if (IS_ERR(micfil->clk_src[MICFIL_CLK_EXT3])) 1204f0862123SShengjiu Wang micfil->clk_src[MICFIL_CLK_EXT3] = NULL; 1205f0862123SShengjiu Wang 120647a70e6fSCosmin Samoila /* init regmap */ 1207d9bf1e79SYang Yingliang regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 120847a70e6fSCosmin Samoila if (IS_ERR(regs)) 120947a70e6fSCosmin Samoila return PTR_ERR(regs); 121047a70e6fSCosmin Samoila 1211b5cf28f7SShengjiu Wang micfil->regmap = devm_regmap_init_mmio(&pdev->dev, 121247a70e6fSCosmin Samoila regs, 121347a70e6fSCosmin Samoila &fsl_micfil_regmap_config); 121447a70e6fSCosmin Samoila if (IS_ERR(micfil->regmap)) { 121547a70e6fSCosmin Samoila dev_err(&pdev->dev, "failed to init MICFIL regmap: %ld\n", 121647a70e6fSCosmin Samoila PTR_ERR(micfil->regmap)); 121747a70e6fSCosmin Samoila return PTR_ERR(micfil->regmap); 121847a70e6fSCosmin Samoila } 121947a70e6fSCosmin Samoila 122047a70e6fSCosmin Samoila /* dataline mask for RX */ 122147a70e6fSCosmin Samoila ret = of_property_read_u32_index(np, 122247a70e6fSCosmin Samoila "fsl,dataline", 122347a70e6fSCosmin Samoila 0, 122447a70e6fSCosmin Samoila &micfil->dataline); 122547a70e6fSCosmin Samoila if (ret) 122647a70e6fSCosmin Samoila micfil->dataline = 1; 122747a70e6fSCosmin Samoila 122847a70e6fSCosmin Samoila if (micfil->dataline & ~micfil->soc->dataline) { 122947a70e6fSCosmin Samoila dev_err(&pdev->dev, "dataline setting error, Mask is 0x%X\n", 123047a70e6fSCosmin Samoila micfil->soc->dataline); 123147a70e6fSCosmin Samoila return -EINVAL; 123247a70e6fSCosmin Samoila } 123347a70e6fSCosmin Samoila 123447a70e6fSCosmin Samoila /* get IRQs */ 123547a70e6fSCosmin Samoila for (i = 0; i < MICFIL_IRQ_LINES; i++) { 123647a70e6fSCosmin Samoila micfil->irq[i] = platform_get_irq(pdev, i); 123783b35f45STang Bin if (micfil->irq[i] < 0) 123847a70e6fSCosmin Samoila return micfil->irq[i]; 123947a70e6fSCosmin Samoila } 124047a70e6fSCosmin Samoila 1241a62ed960SFabio Estevam /* Digital Microphone interface interrupt */ 124247a70e6fSCosmin Samoila ret = devm_request_irq(&pdev->dev, micfil->irq[0], 1243cbd090faSSascha Hauer micfil_isr, IRQF_SHARED, 124447a70e6fSCosmin Samoila micfil->name, micfil); 124547a70e6fSCosmin Samoila if (ret) { 124647a70e6fSCosmin Samoila dev_err(&pdev->dev, "failed to claim mic interface irq %u\n", 124747a70e6fSCosmin Samoila micfil->irq[0]); 124847a70e6fSCosmin Samoila return ret; 124947a70e6fSCosmin Samoila } 125047a70e6fSCosmin Samoila 1251a62ed960SFabio Estevam /* Digital Microphone interface error interrupt */ 125247a70e6fSCosmin Samoila ret = devm_request_irq(&pdev->dev, micfil->irq[1], 1253cbd090faSSascha Hauer micfil_err_isr, IRQF_SHARED, 125447a70e6fSCosmin Samoila micfil->name, micfil); 125547a70e6fSCosmin Samoila if (ret) { 125647a70e6fSCosmin Samoila dev_err(&pdev->dev, "failed to claim mic interface error irq %u\n", 125747a70e6fSCosmin Samoila micfil->irq[1]); 125847a70e6fSCosmin Samoila return ret; 125947a70e6fSCosmin Samoila } 126047a70e6fSCosmin Samoila 126129dbfeecSShengjiu Wang /* Digital Microphone interface voice activity detector event */ 126229dbfeecSShengjiu Wang ret = devm_request_threaded_irq(&pdev->dev, micfil->irq[2], 126329dbfeecSShengjiu Wang hwvad_isr, voice_detected_fn, 126429dbfeecSShengjiu Wang IRQF_SHARED, micfil->name, micfil); 126529dbfeecSShengjiu Wang if (ret) { 126629dbfeecSShengjiu Wang dev_err(&pdev->dev, "failed to claim hwvad event irq %u\n", 126729dbfeecSShengjiu Wang micfil->irq[0]); 126829dbfeecSShengjiu Wang return ret; 126929dbfeecSShengjiu Wang } 127029dbfeecSShengjiu Wang 127129dbfeecSShengjiu Wang /* Digital Microphone interface voice activity detector error */ 127229dbfeecSShengjiu Wang ret = devm_request_irq(&pdev->dev, micfil->irq[3], 127329dbfeecSShengjiu Wang hwvad_err_isr, IRQF_SHARED, 127429dbfeecSShengjiu Wang micfil->name, micfil); 127529dbfeecSShengjiu Wang if (ret) { 127629dbfeecSShengjiu Wang dev_err(&pdev->dev, "failed to claim hwvad error irq %u\n", 127729dbfeecSShengjiu Wang micfil->irq[1]); 127829dbfeecSShengjiu Wang return ret; 127929dbfeecSShengjiu Wang } 128029dbfeecSShengjiu Wang 128147a70e6fSCosmin Samoila micfil->dma_params_rx.chan_name = "rx"; 128247a70e6fSCosmin Samoila micfil->dma_params_rx.addr = res->start + REG_MICFIL_DATACH0; 128347a70e6fSCosmin Samoila micfil->dma_params_rx.maxburst = MICFIL_DMA_MAXBURST_RX; 128447a70e6fSCosmin Samoila 128547a70e6fSCosmin Samoila platform_set_drvdata(pdev, micfil); 128647a70e6fSCosmin Samoila 128747a70e6fSCosmin Samoila pm_runtime_enable(&pdev->dev); 128836736505SChancel Liu if (!pm_runtime_enabled(&pdev->dev)) { 128936736505SChancel Liu ret = fsl_micfil_runtime_resume(&pdev->dev); 129036736505SChancel Liu if (ret) 129136736505SChancel Liu goto err_pm_disable; 129236736505SChancel Liu } 129336736505SChancel Liu 129436736505SChancel Liu ret = pm_runtime_resume_and_get(&pdev->dev); 129536736505SChancel Liu if (ret < 0) 129636736505SChancel Liu goto err_pm_get_sync; 129736736505SChancel Liu 129836736505SChancel Liu /* Get micfil version */ 129936736505SChancel Liu ret = fsl_micfil_use_verid(&pdev->dev); 130036736505SChancel Liu if (ret < 0) 130136736505SChancel Liu dev_warn(&pdev->dev, "Error reading MICFIL version: %d\n", ret); 130236736505SChancel Liu 130336736505SChancel Liu ret = pm_runtime_put_sync(&pdev->dev); 130436736505SChancel Liu if (ret < 0 && ret != -ENOSYS) 130536736505SChancel Liu goto err_pm_get_sync; 130636736505SChancel Liu 1307b5cf28f7SShengjiu Wang regcache_cache_only(micfil->regmap, true); 130847a70e6fSCosmin Samoila 13090adf2920SShengjiu Wang /* 13100adf2920SShengjiu Wang * Register platform component before registering cpu dai for there 13110adf2920SShengjiu Wang * is not defer probe for platform component in snd_soc_add_pcm_runtime(). 13120adf2920SShengjiu Wang */ 13130adf2920SShengjiu Wang ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); 13140adf2920SShengjiu Wang if (ret) { 13150adf2920SShengjiu Wang dev_err(&pdev->dev, "failed to pcm register\n"); 131617955abaSShengjiu Wang goto err_pm_disable; 13170adf2920SShengjiu Wang } 13180adf2920SShengjiu Wang 1319cb05dac1SShengjiu Wang fsl_micfil_dai.capture.formats = micfil->soc->formats; 1320cb05dac1SShengjiu Wang 132147a70e6fSCosmin Samoila ret = devm_snd_soc_register_component(&pdev->dev, &fsl_micfil_component, 132247a70e6fSCosmin Samoila &fsl_micfil_dai, 1); 132347a70e6fSCosmin Samoila if (ret) { 132447a70e6fSCosmin Samoila dev_err(&pdev->dev, "failed to register component %s\n", 132547a70e6fSCosmin Samoila fsl_micfil_component.name); 132617955abaSShengjiu Wang goto err_pm_disable; 132747a70e6fSCosmin Samoila } 132847a70e6fSCosmin Samoila 132947a70e6fSCosmin Samoila return ret; 133017955abaSShengjiu Wang 133136736505SChancel Liu err_pm_get_sync: 133236736505SChancel Liu if (!pm_runtime_status_suspended(&pdev->dev)) 133336736505SChancel Liu fsl_micfil_runtime_suspend(&pdev->dev); 133417955abaSShengjiu Wang err_pm_disable: 133517955abaSShengjiu Wang pm_runtime_disable(&pdev->dev); 133617955abaSShengjiu Wang 133717955abaSShengjiu Wang return ret; 133817955abaSShengjiu Wang } 133917955abaSShengjiu Wang 134017955abaSShengjiu Wang static void fsl_micfil_remove(struct platform_device *pdev) 134117955abaSShengjiu Wang { 134217955abaSShengjiu Wang pm_runtime_disable(&pdev->dev); 134347a70e6fSCosmin Samoila } 134447a70e6fSCosmin Samoila 134536736505SChancel Liu static int fsl_micfil_runtime_suspend(struct device *dev) 134647a70e6fSCosmin Samoila { 134747a70e6fSCosmin Samoila struct fsl_micfil *micfil = dev_get_drvdata(dev); 134847a70e6fSCosmin Samoila 134947a70e6fSCosmin Samoila regcache_cache_only(micfil->regmap, true); 135047a70e6fSCosmin Samoila 135147a70e6fSCosmin Samoila clk_disable_unprepare(micfil->mclk); 1352b5cf28f7SShengjiu Wang clk_disable_unprepare(micfil->busclk); 135347a70e6fSCosmin Samoila 135447a70e6fSCosmin Samoila return 0; 135547a70e6fSCosmin Samoila } 135647a70e6fSCosmin Samoila 135736736505SChancel Liu static int fsl_micfil_runtime_resume(struct device *dev) 135847a70e6fSCosmin Samoila { 135947a70e6fSCosmin Samoila struct fsl_micfil *micfil = dev_get_drvdata(dev); 136047a70e6fSCosmin Samoila int ret; 136147a70e6fSCosmin Samoila 1362b5cf28f7SShengjiu Wang ret = clk_prepare_enable(micfil->busclk); 136347a70e6fSCosmin Samoila if (ret < 0) 136447a70e6fSCosmin Samoila return ret; 136547a70e6fSCosmin Samoila 1366b5cf28f7SShengjiu Wang ret = clk_prepare_enable(micfil->mclk); 1367b5cf28f7SShengjiu Wang if (ret < 0) { 1368b5cf28f7SShengjiu Wang clk_disable_unprepare(micfil->busclk); 1369b5cf28f7SShengjiu Wang return ret; 1370b5cf28f7SShengjiu Wang } 1371b5cf28f7SShengjiu Wang 137247a70e6fSCosmin Samoila regcache_cache_only(micfil->regmap, false); 137347a70e6fSCosmin Samoila regcache_mark_dirty(micfil->regmap); 137447a70e6fSCosmin Samoila regcache_sync(micfil->regmap); 137547a70e6fSCosmin Samoila 137647a70e6fSCosmin Samoila return 0; 137747a70e6fSCosmin Samoila } 137847a70e6fSCosmin Samoila 137947a70e6fSCosmin Samoila static const struct dev_pm_ops fsl_micfil_pm_ops = { 138047a70e6fSCosmin Samoila SET_RUNTIME_PM_OPS(fsl_micfil_runtime_suspend, 138147a70e6fSCosmin Samoila fsl_micfil_runtime_resume, 138247a70e6fSCosmin Samoila NULL) 1383a38a4090SChancel Liu SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 1384a38a4090SChancel Liu pm_runtime_force_resume) 138547a70e6fSCosmin Samoila }; 138647a70e6fSCosmin Samoila 138747a70e6fSCosmin Samoila static struct platform_driver fsl_micfil_driver = { 138847a70e6fSCosmin Samoila .probe = fsl_micfil_probe, 138917955abaSShengjiu Wang .remove_new = fsl_micfil_remove, 139047a70e6fSCosmin Samoila .driver = { 139147a70e6fSCosmin Samoila .name = "fsl-micfil-dai", 139247a70e6fSCosmin Samoila .pm = &fsl_micfil_pm_ops, 139347a70e6fSCosmin Samoila .of_match_table = fsl_micfil_dt_ids, 139447a70e6fSCosmin Samoila }, 139547a70e6fSCosmin Samoila }; 139647a70e6fSCosmin Samoila module_platform_driver(fsl_micfil_driver); 139747a70e6fSCosmin Samoila 139847a70e6fSCosmin Samoila MODULE_AUTHOR("Cosmin-Gabriel Samoila <cosmin.samoila@nxp.com>"); 139947a70e6fSCosmin Samoila MODULE_DESCRIPTION("NXP PDM Microphone Interface (MICFIL) driver"); 1400f803ec63SDaniel Baluta MODULE_LICENSE("Dual BSD/GPL"); 1401