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 31bea1d61dSSascha Hauer enum quality { 32bea1d61dSSascha Hauer QUALITY_HIGH, 33bea1d61dSSascha Hauer QUALITY_MEDIUM, 34bea1d61dSSascha Hauer QUALITY_LOW, 35bea1d61dSSascha Hauer QUALITY_VLOW0, 36bea1d61dSSascha Hauer QUALITY_VLOW1, 37bea1d61dSSascha Hauer QUALITY_VLOW2, 38bea1d61dSSascha Hauer }; 39bea1d61dSSascha Hauer 4047a70e6fSCosmin Samoila struct fsl_micfil { 4147a70e6fSCosmin Samoila struct platform_device *pdev; 4247a70e6fSCosmin Samoila struct regmap *regmap; 4347a70e6fSCosmin Samoila const struct fsl_micfil_soc_data *soc; 44b5cf28f7SShengjiu Wang struct clk *busclk; 4547a70e6fSCosmin Samoila struct clk *mclk; 4693f54100SShengjiu Wang struct clk *pll8k_clk; 4793f54100SShengjiu Wang struct clk *pll11k_clk; 4847a70e6fSCosmin Samoila struct snd_dmaengine_dai_dma_data dma_params_rx; 492495ba26SSascha Hauer struct sdma_peripheral_config sdmacfg; 5029dbfeecSShengjiu Wang struct snd_soc_card *card; 5147a70e6fSCosmin Samoila unsigned int dataline; 5247a70e6fSCosmin Samoila char name[32]; 5347a70e6fSCosmin Samoila int irq[MICFIL_IRQ_LINES]; 54bea1d61dSSascha Hauer enum quality quality; 553b13b143SShengjiu Wang int dc_remover; 5629dbfeecSShengjiu Wang int vad_init_mode; 5729dbfeecSShengjiu Wang int vad_enabled; 5829dbfeecSShengjiu Wang int vad_detected; 5936736505SChancel Liu struct fsl_micfil_verid verid; 6036736505SChancel Liu struct fsl_micfil_param param; 6147a70e6fSCosmin Samoila }; 6247a70e6fSCosmin Samoila 6347a70e6fSCosmin Samoila struct fsl_micfil_soc_data { 6447a70e6fSCosmin Samoila unsigned int fifos; 6547a70e6fSCosmin Samoila unsigned int fifo_depth; 6647a70e6fSCosmin Samoila unsigned int dataline; 6747a70e6fSCosmin Samoila bool imx; 6877a7a6e9SChancel Liu bool use_edma; 6936736505SChancel Liu bool use_verid; 70*8b339bebSChancel Liu bool volume_sx; 71cb05dac1SShengjiu Wang u64 formats; 7247a70e6fSCosmin Samoila }; 7347a70e6fSCosmin Samoila 7447a70e6fSCosmin Samoila static struct fsl_micfil_soc_data fsl_micfil_imx8mm = { 7547a70e6fSCosmin Samoila .imx = true, 7647a70e6fSCosmin Samoila .fifos = 8, 7747a70e6fSCosmin Samoila .fifo_depth = 8, 7847a70e6fSCosmin Samoila .dataline = 0xf, 79cb05dac1SShengjiu Wang .formats = SNDRV_PCM_FMTBIT_S16_LE, 80*8b339bebSChancel Liu .volume_sx = true, 81cb05dac1SShengjiu Wang }; 82cb05dac1SShengjiu Wang 83cb05dac1SShengjiu Wang static struct fsl_micfil_soc_data fsl_micfil_imx8mp = { 84cb05dac1SShengjiu Wang .imx = true, 85cb05dac1SShengjiu Wang .fifos = 8, 86cb05dac1SShengjiu Wang .fifo_depth = 32, 87cb05dac1SShengjiu Wang .dataline = 0xf, 88cb05dac1SShengjiu Wang .formats = SNDRV_PCM_FMTBIT_S32_LE, 89*8b339bebSChancel Liu .volume_sx = false, 9047a70e6fSCosmin Samoila }; 9147a70e6fSCosmin Samoila 92a10a5254SChancel Liu static struct fsl_micfil_soc_data fsl_micfil_imx93 = { 93a10a5254SChancel Liu .imx = true, 94a10a5254SChancel Liu .fifos = 8, 95a10a5254SChancel Liu .fifo_depth = 32, 96a10a5254SChancel Liu .dataline = 0xf, 97a10a5254SChancel Liu .formats = SNDRV_PCM_FMTBIT_S32_LE, 9877a7a6e9SChancel Liu .use_edma = true, 9936736505SChancel Liu .use_verid = true, 100*8b339bebSChancel Liu .volume_sx = false, 101a10a5254SChancel Liu }; 102a10a5254SChancel Liu 10347a70e6fSCosmin Samoila static const struct of_device_id fsl_micfil_dt_ids[] = { 10447a70e6fSCosmin Samoila { .compatible = "fsl,imx8mm-micfil", .data = &fsl_micfil_imx8mm }, 105cb05dac1SShengjiu Wang { .compatible = "fsl,imx8mp-micfil", .data = &fsl_micfil_imx8mp }, 106a10a5254SChancel Liu { .compatible = "fsl,imx93-micfil", .data = &fsl_micfil_imx93 }, 10747a70e6fSCosmin Samoila {} 10847a70e6fSCosmin Samoila }; 10947a70e6fSCosmin Samoila MODULE_DEVICE_TABLE(of, fsl_micfil_dt_ids); 11047a70e6fSCosmin Samoila 11147a70e6fSCosmin Samoila static const char * const micfil_quality_select_texts[] = { 112bea1d61dSSascha Hauer [QUALITY_HIGH] = "High", 113bea1d61dSSascha Hauer [QUALITY_MEDIUM] = "Medium", 114bea1d61dSSascha Hauer [QUALITY_LOW] = "Low", 115bea1d61dSSascha Hauer [QUALITY_VLOW0] = "VLow0", 116bea1d61dSSascha Hauer [QUALITY_VLOW1] = "Vlow1", 117bea1d61dSSascha Hauer [QUALITY_VLOW2] = "Vlow2", 11847a70e6fSCosmin Samoila }; 11947a70e6fSCosmin Samoila 12047a70e6fSCosmin Samoila static const struct soc_enum fsl_micfil_quality_enum = 121bea1d61dSSascha Hauer SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_quality_select_texts), 12247a70e6fSCosmin Samoila micfil_quality_select_texts); 12347a70e6fSCosmin Samoila 12447a70e6fSCosmin Samoila static DECLARE_TLV_DB_SCALE(gain_tlv, 0, 100, 0); 12547a70e6fSCosmin Samoila 126bea1d61dSSascha Hauer static int micfil_set_quality(struct fsl_micfil *micfil) 127bea1d61dSSascha Hauer { 128bea1d61dSSascha Hauer u32 qsel; 129bea1d61dSSascha Hauer 130bea1d61dSSascha Hauer switch (micfil->quality) { 131bea1d61dSSascha Hauer case QUALITY_HIGH: 132bea1d61dSSascha Hauer qsel = MICFIL_QSEL_HIGH_QUALITY; 133bea1d61dSSascha Hauer break; 134bea1d61dSSascha Hauer case QUALITY_MEDIUM: 135bea1d61dSSascha Hauer qsel = MICFIL_QSEL_MEDIUM_QUALITY; 136bea1d61dSSascha Hauer break; 137bea1d61dSSascha Hauer case QUALITY_LOW: 138bea1d61dSSascha Hauer qsel = MICFIL_QSEL_LOW_QUALITY; 139bea1d61dSSascha Hauer break; 140bea1d61dSSascha Hauer case QUALITY_VLOW0: 141bea1d61dSSascha Hauer qsel = MICFIL_QSEL_VLOW0_QUALITY; 142bea1d61dSSascha Hauer break; 143bea1d61dSSascha Hauer case QUALITY_VLOW1: 144bea1d61dSSascha Hauer qsel = MICFIL_QSEL_VLOW1_QUALITY; 145bea1d61dSSascha Hauer break; 146bea1d61dSSascha Hauer case QUALITY_VLOW2: 147bea1d61dSSascha Hauer qsel = MICFIL_QSEL_VLOW2_QUALITY; 148bea1d61dSSascha Hauer break; 149bea1d61dSSascha Hauer } 150bea1d61dSSascha Hauer 151bea1d61dSSascha Hauer return regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2, 152bea1d61dSSascha Hauer MICFIL_CTRL2_QSEL, 153bea1d61dSSascha Hauer FIELD_PREP(MICFIL_CTRL2_QSEL, qsel)); 154bea1d61dSSascha Hauer } 155bea1d61dSSascha Hauer 156bea1d61dSSascha Hauer static int micfil_quality_get(struct snd_kcontrol *kcontrol, 157bea1d61dSSascha Hauer struct snd_ctl_elem_value *ucontrol) 158bea1d61dSSascha Hauer { 159bea1d61dSSascha Hauer struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); 160bea1d61dSSascha Hauer struct fsl_micfil *micfil = snd_soc_component_get_drvdata(cmpnt); 161bea1d61dSSascha Hauer 162bea1d61dSSascha Hauer ucontrol->value.integer.value[0] = micfil->quality; 163bea1d61dSSascha Hauer 164bea1d61dSSascha Hauer return 0; 165bea1d61dSSascha Hauer } 166bea1d61dSSascha Hauer 167bea1d61dSSascha Hauer static int micfil_quality_set(struct snd_kcontrol *kcontrol, 168bea1d61dSSascha Hauer struct snd_ctl_elem_value *ucontrol) 169bea1d61dSSascha Hauer { 170bea1d61dSSascha Hauer struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); 171bea1d61dSSascha Hauer struct fsl_micfil *micfil = snd_soc_component_get_drvdata(cmpnt); 172bea1d61dSSascha Hauer 173bea1d61dSSascha Hauer micfil->quality = ucontrol->value.integer.value[0]; 174bea1d61dSSascha Hauer 175bea1d61dSSascha Hauer return micfil_set_quality(micfil); 176bea1d61dSSascha Hauer } 177bea1d61dSSascha Hauer 17829dbfeecSShengjiu Wang static const char * const micfil_hwvad_enable[] = { 17929dbfeecSShengjiu Wang "Disable (Record only)", 18029dbfeecSShengjiu Wang "Enable (Record with Vad)", 18129dbfeecSShengjiu Wang }; 18229dbfeecSShengjiu Wang 18329dbfeecSShengjiu Wang static const char * const micfil_hwvad_init_mode[] = { 18429dbfeecSShengjiu Wang "Envelope mode", "Energy mode", 18529dbfeecSShengjiu Wang }; 18629dbfeecSShengjiu Wang 18729dbfeecSShengjiu Wang static const char * const micfil_hwvad_hpf_texts[] = { 18829dbfeecSShengjiu Wang "Filter bypass", 18929dbfeecSShengjiu Wang "Cut-off @1750Hz", 19029dbfeecSShengjiu Wang "Cut-off @215Hz", 19129dbfeecSShengjiu Wang "Cut-off @102Hz", 19229dbfeecSShengjiu Wang }; 19329dbfeecSShengjiu Wang 19429dbfeecSShengjiu Wang /* 19529dbfeecSShengjiu Wang * DC Remover Control 19629dbfeecSShengjiu Wang * Filter Bypassed 1 1 19729dbfeecSShengjiu Wang * Cut-off @21Hz 0 0 19829dbfeecSShengjiu Wang * Cut-off @83Hz 0 1 19929dbfeecSShengjiu Wang * Cut-off @152HZ 1 0 20029dbfeecSShengjiu Wang */ 20129dbfeecSShengjiu Wang static const char * const micfil_dc_remover_texts[] = { 20229dbfeecSShengjiu Wang "Cut-off @21Hz", "Cut-off @83Hz", 20329dbfeecSShengjiu Wang "Cut-off @152Hz", "Bypass", 20429dbfeecSShengjiu Wang }; 20529dbfeecSShengjiu Wang 20629dbfeecSShengjiu Wang static const struct soc_enum hwvad_enable_enum = 20729dbfeecSShengjiu Wang SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_hwvad_enable), 20829dbfeecSShengjiu Wang micfil_hwvad_enable); 20929dbfeecSShengjiu Wang static const struct soc_enum hwvad_init_mode_enum = 21029dbfeecSShengjiu Wang SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_hwvad_init_mode), 21129dbfeecSShengjiu Wang micfil_hwvad_init_mode); 21229dbfeecSShengjiu Wang static const struct soc_enum hwvad_hpf_enum = 21329dbfeecSShengjiu Wang SOC_ENUM_SINGLE(REG_MICFIL_VAD0_CTRL2, 0, 21429dbfeecSShengjiu Wang ARRAY_SIZE(micfil_hwvad_hpf_texts), 21529dbfeecSShengjiu Wang micfil_hwvad_hpf_texts); 21629dbfeecSShengjiu Wang static const struct soc_enum fsl_micfil_dc_remover_enum = 21729dbfeecSShengjiu Wang SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_dc_remover_texts), 21829dbfeecSShengjiu Wang micfil_dc_remover_texts); 21929dbfeecSShengjiu Wang 22029dbfeecSShengjiu Wang static int micfil_put_dc_remover_state(struct snd_kcontrol *kcontrol, 22129dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 22229dbfeecSShengjiu Wang { 22329dbfeecSShengjiu Wang struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 22429dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 22529dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 22629dbfeecSShengjiu Wang unsigned int *item = ucontrol->value.enumerated.item; 22729dbfeecSShengjiu Wang int val = snd_soc_enum_item_to_val(e, item[0]); 22829dbfeecSShengjiu Wang int i = 0, ret = 0; 22929dbfeecSShengjiu Wang u32 reg_val = 0; 23029dbfeecSShengjiu Wang 23129dbfeecSShengjiu Wang if (val < 0 || val > 3) 23229dbfeecSShengjiu Wang return -EINVAL; 23329dbfeecSShengjiu Wang 23429dbfeecSShengjiu Wang micfil->dc_remover = val; 23529dbfeecSShengjiu Wang 23629dbfeecSShengjiu Wang /* Calculate total value for all channels */ 23729dbfeecSShengjiu Wang for (i = 0; i < MICFIL_OUTPUT_CHANNELS; i++) 23829dbfeecSShengjiu Wang reg_val |= val << MICFIL_DC_CHX_SHIFT(i); 23929dbfeecSShengjiu Wang 24029dbfeecSShengjiu Wang /* Update DC Remover mode for all channels */ 24129dbfeecSShengjiu Wang ret = snd_soc_component_update_bits(comp, REG_MICFIL_DC_CTRL, 24229dbfeecSShengjiu Wang MICFIL_DC_CTRL_CONFIG, reg_val); 24329dbfeecSShengjiu Wang if (ret < 0) 24429dbfeecSShengjiu Wang return ret; 24529dbfeecSShengjiu Wang 24629dbfeecSShengjiu Wang return 0; 24729dbfeecSShengjiu Wang } 24829dbfeecSShengjiu Wang 24929dbfeecSShengjiu Wang static int micfil_get_dc_remover_state(struct snd_kcontrol *kcontrol, 25029dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 25129dbfeecSShengjiu Wang { 25229dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 25329dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 25429dbfeecSShengjiu Wang 25529dbfeecSShengjiu Wang ucontrol->value.enumerated.item[0] = micfil->dc_remover; 25629dbfeecSShengjiu Wang 25729dbfeecSShengjiu Wang return 0; 25829dbfeecSShengjiu Wang } 25929dbfeecSShengjiu Wang 26029dbfeecSShengjiu Wang static int hwvad_put_enable(struct snd_kcontrol *kcontrol, 26129dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 26229dbfeecSShengjiu Wang { 26329dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 26429dbfeecSShengjiu Wang struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 26529dbfeecSShengjiu Wang unsigned int *item = ucontrol->value.enumerated.item; 26629dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 26729dbfeecSShengjiu Wang int val = snd_soc_enum_item_to_val(e, item[0]); 26829dbfeecSShengjiu Wang 26929dbfeecSShengjiu Wang micfil->vad_enabled = val; 27029dbfeecSShengjiu Wang 27129dbfeecSShengjiu Wang return 0; 27229dbfeecSShengjiu Wang } 27329dbfeecSShengjiu Wang 27429dbfeecSShengjiu Wang static int hwvad_get_enable(struct snd_kcontrol *kcontrol, 27529dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 27629dbfeecSShengjiu Wang { 27729dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 27829dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 27929dbfeecSShengjiu Wang 28029dbfeecSShengjiu Wang ucontrol->value.enumerated.item[0] = micfil->vad_enabled; 28129dbfeecSShengjiu Wang 28229dbfeecSShengjiu Wang return 0; 28329dbfeecSShengjiu Wang } 28429dbfeecSShengjiu Wang 28529dbfeecSShengjiu Wang static int hwvad_put_init_mode(struct snd_kcontrol *kcontrol, 28629dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 28729dbfeecSShengjiu Wang { 28829dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 28929dbfeecSShengjiu Wang struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 29029dbfeecSShengjiu Wang unsigned int *item = ucontrol->value.enumerated.item; 29129dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 29229dbfeecSShengjiu Wang int val = snd_soc_enum_item_to_val(e, item[0]); 29329dbfeecSShengjiu Wang 29429dbfeecSShengjiu Wang /* 0 - Envelope-based Mode 29529dbfeecSShengjiu Wang * 1 - Energy-based Mode 29629dbfeecSShengjiu Wang */ 29729dbfeecSShengjiu Wang micfil->vad_init_mode = val; 29829dbfeecSShengjiu Wang 29929dbfeecSShengjiu Wang return 0; 30029dbfeecSShengjiu Wang } 30129dbfeecSShengjiu Wang 30229dbfeecSShengjiu Wang static int hwvad_get_init_mode(struct snd_kcontrol *kcontrol, 30329dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 30429dbfeecSShengjiu Wang { 30529dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 30629dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 30729dbfeecSShengjiu Wang 30829dbfeecSShengjiu Wang ucontrol->value.enumerated.item[0] = micfil->vad_init_mode; 30929dbfeecSShengjiu Wang 31029dbfeecSShengjiu Wang return 0; 31129dbfeecSShengjiu Wang } 31229dbfeecSShengjiu Wang 31329dbfeecSShengjiu Wang static int hwvad_detected(struct snd_kcontrol *kcontrol, 31429dbfeecSShengjiu Wang struct snd_ctl_elem_value *ucontrol) 31529dbfeecSShengjiu Wang { 31629dbfeecSShengjiu Wang struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); 31729dbfeecSShengjiu Wang struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); 31829dbfeecSShengjiu Wang 31929dbfeecSShengjiu Wang ucontrol->value.enumerated.item[0] = micfil->vad_detected; 32029dbfeecSShengjiu Wang 32129dbfeecSShengjiu Wang return 0; 32229dbfeecSShengjiu Wang } 32329dbfeecSShengjiu Wang 324*8b339bebSChancel Liu static const struct snd_kcontrol_new fsl_micfil_volume_controls[] = { 325*8b339bebSChancel Liu SOC_SINGLE_TLV("CH0 Volume", REG_MICFIL_OUT_CTRL, 326*8b339bebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(0), 0xF, 0, gain_tlv), 327*8b339bebSChancel Liu SOC_SINGLE_TLV("CH1 Volume", REG_MICFIL_OUT_CTRL, 328*8b339bebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(1), 0xF, 0, gain_tlv), 329*8b339bebSChancel Liu SOC_SINGLE_TLV("CH2 Volume", REG_MICFIL_OUT_CTRL, 330*8b339bebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(2), 0xF, 0, gain_tlv), 331*8b339bebSChancel Liu SOC_SINGLE_TLV("CH3 Volume", REG_MICFIL_OUT_CTRL, 332*8b339bebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(3), 0xF, 0, gain_tlv), 333*8b339bebSChancel Liu SOC_SINGLE_TLV("CH4 Volume", REG_MICFIL_OUT_CTRL, 334*8b339bebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(4), 0xF, 0, gain_tlv), 335*8b339bebSChancel Liu SOC_SINGLE_TLV("CH5 Volume", REG_MICFIL_OUT_CTRL, 336*8b339bebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(5), 0xF, 0, gain_tlv), 337*8b339bebSChancel Liu SOC_SINGLE_TLV("CH6 Volume", REG_MICFIL_OUT_CTRL, 338*8b339bebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(6), 0xF, 0, gain_tlv), 339*8b339bebSChancel Liu SOC_SINGLE_TLV("CH7 Volume", REG_MICFIL_OUT_CTRL, 340*8b339bebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(7), 0xF, 0, gain_tlv), 341*8b339bebSChancel Liu }; 342*8b339bebSChancel Liu 343*8b339bebSChancel Liu static const struct snd_kcontrol_new fsl_micfil_volume_sx_controls[] = { 34447a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH0 Volume", REG_MICFIL_OUT_CTRL, 345cdfa92ebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(0), 0x8, 0xF, gain_tlv), 34647a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH1 Volume", REG_MICFIL_OUT_CTRL, 347cdfa92ebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(1), 0x8, 0xF, gain_tlv), 34847a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH2 Volume", REG_MICFIL_OUT_CTRL, 349cdfa92ebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(2), 0x8, 0xF, gain_tlv), 35047a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH3 Volume", REG_MICFIL_OUT_CTRL, 351cdfa92ebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(3), 0x8, 0xF, gain_tlv), 35247a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH4 Volume", REG_MICFIL_OUT_CTRL, 353cdfa92ebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(4), 0x8, 0xF, gain_tlv), 35447a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH5 Volume", REG_MICFIL_OUT_CTRL, 355cdfa92ebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(5), 0x8, 0xF, gain_tlv), 35647a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH6 Volume", REG_MICFIL_OUT_CTRL, 357cdfa92ebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(6), 0x8, 0xF, gain_tlv), 35847a70e6fSCosmin Samoila SOC_SINGLE_SX_TLV("CH7 Volume", REG_MICFIL_OUT_CTRL, 359cdfa92ebSChancel Liu MICFIL_OUTGAIN_CHX_SHIFT(7), 0x8, 0xF, gain_tlv), 360*8b339bebSChancel Liu }; 361*8b339bebSChancel Liu 362*8b339bebSChancel Liu static const struct snd_kcontrol_new fsl_micfil_snd_controls[] = { 36347a70e6fSCosmin Samoila SOC_ENUM_EXT("MICFIL Quality Select", 36447a70e6fSCosmin Samoila fsl_micfil_quality_enum, 365bea1d61dSSascha Hauer micfil_quality_get, micfil_quality_set), 36629dbfeecSShengjiu Wang SOC_ENUM_EXT("HWVAD Enablement Switch", hwvad_enable_enum, 36729dbfeecSShengjiu Wang hwvad_get_enable, hwvad_put_enable), 36829dbfeecSShengjiu Wang SOC_ENUM_EXT("HWVAD Initialization Mode", hwvad_init_mode_enum, 36929dbfeecSShengjiu Wang hwvad_get_init_mode, hwvad_put_init_mode), 37029dbfeecSShengjiu Wang SOC_ENUM("HWVAD High-Pass Filter", hwvad_hpf_enum), 37129dbfeecSShengjiu Wang SOC_SINGLE("HWVAD ZCD Switch", REG_MICFIL_VAD0_ZCD, 0, 1, 0), 37229dbfeecSShengjiu Wang SOC_SINGLE("HWVAD ZCD Auto Threshold Switch", 37329dbfeecSShengjiu Wang REG_MICFIL_VAD0_ZCD, 2, 1, 0), 37429dbfeecSShengjiu Wang SOC_ENUM_EXT("MICFIL DC Remover Control", fsl_micfil_dc_remover_enum, 37529dbfeecSShengjiu Wang micfil_get_dc_remover_state, micfil_put_dc_remover_state), 37629dbfeecSShengjiu Wang SOC_SINGLE("HWVAD Input Gain", REG_MICFIL_VAD0_CTRL2, 8, 15, 0), 37729dbfeecSShengjiu Wang SOC_SINGLE("HWVAD Sound Gain", REG_MICFIL_VAD0_SCONFIG, 0, 15, 0), 37829dbfeecSShengjiu Wang SOC_SINGLE("HWVAD Noise Gain", REG_MICFIL_VAD0_NCONFIG, 0, 15, 0), 37929dbfeecSShengjiu Wang SOC_SINGLE_RANGE("HWVAD Detector Frame Time", REG_MICFIL_VAD0_CTRL2, 16, 0, 63, 0), 38029dbfeecSShengjiu Wang SOC_SINGLE("HWVAD Detector Initialization Time", REG_MICFIL_VAD0_CTRL1, 8, 31, 0), 38129dbfeecSShengjiu Wang SOC_SINGLE("HWVAD Noise Filter Adjustment", REG_MICFIL_VAD0_NCONFIG, 8, 31, 0), 38229dbfeecSShengjiu Wang SOC_SINGLE("HWVAD ZCD Threshold", REG_MICFIL_VAD0_ZCD, 16, 1023, 0), 38329dbfeecSShengjiu Wang SOC_SINGLE("HWVAD ZCD Adjustment", REG_MICFIL_VAD0_ZCD, 8, 15, 0), 38429dbfeecSShengjiu Wang SOC_SINGLE("HWVAD ZCD And Behavior Switch", 38529dbfeecSShengjiu Wang REG_MICFIL_VAD0_ZCD, 4, 1, 0), 38629dbfeecSShengjiu Wang SOC_SINGLE_BOOL_EXT("VAD Detected", 0, hwvad_detected, NULL), 38747a70e6fSCosmin Samoila }; 38847a70e6fSCosmin Samoila 38936736505SChancel Liu static int fsl_micfil_use_verid(struct device *dev) 39036736505SChancel Liu { 39136736505SChancel Liu struct fsl_micfil *micfil = dev_get_drvdata(dev); 39236736505SChancel Liu unsigned int val; 39336736505SChancel Liu int ret; 39436736505SChancel Liu 39536736505SChancel Liu if (!micfil->soc->use_verid) 39636736505SChancel Liu return 0; 39736736505SChancel Liu 39836736505SChancel Liu ret = regmap_read(micfil->regmap, REG_MICFIL_VERID, &val); 39936736505SChancel Liu if (ret < 0) 40036736505SChancel Liu return ret; 40136736505SChancel Liu 40236736505SChancel Liu dev_dbg(dev, "VERID: 0x%016X\n", val); 40336736505SChancel Liu 40436736505SChancel Liu micfil->verid.version = val & 40536736505SChancel Liu (MICFIL_VERID_MAJOR_MASK | MICFIL_VERID_MINOR_MASK); 40636736505SChancel Liu micfil->verid.version >>= MICFIL_VERID_MINOR_SHIFT; 40736736505SChancel Liu micfil->verid.feature = val & MICFIL_VERID_FEATURE_MASK; 40836736505SChancel Liu 40936736505SChancel Liu ret = regmap_read(micfil->regmap, REG_MICFIL_PARAM, &val); 41036736505SChancel Liu if (ret < 0) 41136736505SChancel Liu return ret; 41236736505SChancel Liu 41336736505SChancel Liu dev_dbg(dev, "PARAM: 0x%016X\n", val); 41436736505SChancel Liu 41536736505SChancel Liu micfil->param.hwvad_num = (val & MICFIL_PARAM_NUM_HWVAD_MASK) >> 41636736505SChancel Liu MICFIL_PARAM_NUM_HWVAD_SHIFT; 41736736505SChancel Liu micfil->param.hwvad_zcd = val & MICFIL_PARAM_HWVAD_ZCD; 41836736505SChancel Liu micfil->param.hwvad_energy_mode = val & MICFIL_PARAM_HWVAD_ENERGY_MODE; 41936736505SChancel Liu micfil->param.hwvad = val & MICFIL_PARAM_HWVAD; 42036736505SChancel Liu micfil->param.dc_out_bypass = val & MICFIL_PARAM_DC_OUT_BYPASS; 42136736505SChancel Liu micfil->param.dc_in_bypass = val & MICFIL_PARAM_DC_IN_BYPASS; 42236736505SChancel Liu micfil->param.low_power = val & MICFIL_PARAM_LOW_POWER; 42336736505SChancel Liu micfil->param.fil_out_width = val & MICFIL_PARAM_FIL_OUT_WIDTH; 42436736505SChancel Liu micfil->param.fifo_ptrwid = (val & MICFIL_PARAM_FIFO_PTRWID_MASK) >> 42536736505SChancel Liu MICFIL_PARAM_FIFO_PTRWID_SHIFT; 42636736505SChancel Liu micfil->param.npair = (val & MICFIL_PARAM_NPAIR_MASK) >> 42736736505SChancel Liu MICFIL_PARAM_NPAIR_SHIFT; 42836736505SChancel Liu 42936736505SChancel Liu return 0; 43036736505SChancel Liu } 43136736505SChancel Liu 43247a70e6fSCosmin Samoila /* The SRES is a self-negated bit which provides the CPU with the 43347a70e6fSCosmin Samoila * capability to initialize the PDM Interface module through the 43447a70e6fSCosmin Samoila * slave-bus interface. This bit always reads as zero, and this 43547a70e6fSCosmin Samoila * bit is only effective when MDIS is cleared 43647a70e6fSCosmin Samoila */ 43747a70e6fSCosmin Samoila static int fsl_micfil_reset(struct device *dev) 43847a70e6fSCosmin Samoila { 43947a70e6fSCosmin Samoila struct fsl_micfil *micfil = dev_get_drvdata(dev); 44047a70e6fSCosmin Samoila int ret; 44147a70e6fSCosmin Samoila 442d46c2127SSascha Hauer ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1, 443d46c2127SSascha Hauer MICFIL_CTRL1_MDIS); 4442c602c7eSSascha Hauer if (ret) 44547a70e6fSCosmin Samoila return ret; 44647a70e6fSCosmin Samoila 447d46c2127SSascha Hauer ret = regmap_set_bits(micfil->regmap, REG_MICFIL_CTRL1, 44847a70e6fSCosmin Samoila MICFIL_CTRL1_SRES); 4492c602c7eSSascha Hauer if (ret) 45047a70e6fSCosmin Samoila return ret; 45147a70e6fSCosmin Samoila 452292709b9SShengjiu Wang /* 453292709b9SShengjiu Wang * SRES is self-cleared bit, but REG_MICFIL_CTRL1 is defined 454292709b9SShengjiu Wang * as non-volatile register, so SRES still remain in regmap 455292709b9SShengjiu Wang * cache after set, that every update of REG_MICFIL_CTRL1, 456292709b9SShengjiu Wang * software reset happens. so clear it explicitly. 457292709b9SShengjiu Wang */ 458292709b9SShengjiu Wang ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1, 459292709b9SShengjiu Wang MICFIL_CTRL1_SRES); 460292709b9SShengjiu Wang if (ret) 461292709b9SShengjiu Wang return ret; 462292709b9SShengjiu Wang 463b776c4a4SShengjiu Wang /* 464b776c4a4SShengjiu Wang * Set SRES should clear CHnF flags, But even add delay here 465b776c4a4SShengjiu Wang * the CHnF may not be cleared sometimes, so clear CHnF explicitly. 466b776c4a4SShengjiu Wang */ 467b776c4a4SShengjiu Wang ret = regmap_write_bits(micfil->regmap, REG_MICFIL_STAT, 0xFF, 0xFF); 468b776c4a4SShengjiu Wang if (ret) 469b776c4a4SShengjiu Wang return ret; 470b776c4a4SShengjiu Wang 47147a70e6fSCosmin Samoila return 0; 47247a70e6fSCosmin Samoila } 47347a70e6fSCosmin Samoila 47447a70e6fSCosmin Samoila static int fsl_micfil_startup(struct snd_pcm_substream *substream, 47547a70e6fSCosmin Samoila struct snd_soc_dai *dai) 47647a70e6fSCosmin Samoila { 47747a70e6fSCosmin Samoila struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai); 47847a70e6fSCosmin Samoila 47947a70e6fSCosmin Samoila if (!micfil) { 48011106cb3STang Bin dev_err(dai->dev, "micfil dai priv_data not set\n"); 48147a70e6fSCosmin Samoila return -EINVAL; 48247a70e6fSCosmin Samoila } 48347a70e6fSCosmin Samoila 48447a70e6fSCosmin Samoila return 0; 48547a70e6fSCosmin Samoila } 48647a70e6fSCosmin Samoila 48729dbfeecSShengjiu Wang /* Enable/disable hwvad interrupts */ 48829dbfeecSShengjiu Wang static int fsl_micfil_configure_hwvad_interrupts(struct fsl_micfil *micfil, int enable) 48929dbfeecSShengjiu Wang { 49029dbfeecSShengjiu Wang u32 vadie_reg = enable ? MICFIL_VAD0_CTRL1_IE : 0; 49129dbfeecSShengjiu Wang u32 vaderie_reg = enable ? MICFIL_VAD0_CTRL1_ERIE : 0; 49229dbfeecSShengjiu Wang 49329dbfeecSShengjiu Wang /* Voice Activity Detector Error Interruption */ 49429dbfeecSShengjiu Wang regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 49529dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_ERIE, vaderie_reg); 49629dbfeecSShengjiu Wang 49729dbfeecSShengjiu Wang /* Voice Activity Detector Interruption */ 49829dbfeecSShengjiu Wang regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 49929dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_IE, vadie_reg); 50029dbfeecSShengjiu Wang 50129dbfeecSShengjiu Wang return 0; 50229dbfeecSShengjiu Wang } 50329dbfeecSShengjiu Wang 50429dbfeecSShengjiu Wang /* Configuration done only in energy-based initialization mode */ 50529dbfeecSShengjiu Wang static int fsl_micfil_init_hwvad_energy_mode(struct fsl_micfil *micfil) 50629dbfeecSShengjiu Wang { 50729dbfeecSShengjiu Wang /* Keep the VADFRENDIS bitfield cleared. */ 50829dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2, 50929dbfeecSShengjiu Wang MICFIL_VAD0_CTRL2_FRENDIS); 51029dbfeecSShengjiu Wang 51129dbfeecSShengjiu Wang /* Keep the VADPREFEN bitfield cleared. */ 51229dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2, 51329dbfeecSShengjiu Wang MICFIL_VAD0_CTRL2_PREFEN); 51429dbfeecSShengjiu Wang 51529dbfeecSShengjiu Wang /* Keep the VADSFILEN bitfield cleared. */ 51629dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG, 51729dbfeecSShengjiu Wang MICFIL_VAD0_SCONFIG_SFILEN); 51829dbfeecSShengjiu Wang 51929dbfeecSShengjiu Wang /* Keep the VADSMAXEN bitfield cleared. */ 52029dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG, 52129dbfeecSShengjiu Wang MICFIL_VAD0_SCONFIG_SMAXEN); 52229dbfeecSShengjiu Wang 52329dbfeecSShengjiu Wang /* Keep the VADNFILAUTO bitfield asserted. */ 52429dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 52529dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NFILAUT); 52629dbfeecSShengjiu Wang 52729dbfeecSShengjiu Wang /* Keep the VADNMINEN bitfield cleared. */ 52829dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 52929dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NMINEN); 53029dbfeecSShengjiu Wang 53129dbfeecSShengjiu Wang /* Keep the VADNDECEN bitfield cleared. */ 53229dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 53329dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NDECEN); 53429dbfeecSShengjiu Wang 53529dbfeecSShengjiu Wang /* Keep the VADNOREN bitfield cleared. */ 53629dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 53729dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NOREN); 53829dbfeecSShengjiu Wang 53929dbfeecSShengjiu Wang return 0; 54029dbfeecSShengjiu Wang } 54129dbfeecSShengjiu Wang 54229dbfeecSShengjiu Wang /* Configuration done only in envelope-based initialization mode */ 54329dbfeecSShengjiu Wang static int fsl_micfil_init_hwvad_envelope_mode(struct fsl_micfil *micfil) 54429dbfeecSShengjiu Wang { 54529dbfeecSShengjiu Wang /* Assert the VADFRENDIS bitfield */ 54629dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2, 54729dbfeecSShengjiu Wang MICFIL_VAD0_CTRL2_FRENDIS); 54829dbfeecSShengjiu Wang 54929dbfeecSShengjiu Wang /* Assert the VADPREFEN bitfield. */ 55029dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2, 55129dbfeecSShengjiu Wang MICFIL_VAD0_CTRL2_PREFEN); 55229dbfeecSShengjiu Wang 55329dbfeecSShengjiu Wang /* Assert the VADSFILEN bitfield. */ 55429dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG, 55529dbfeecSShengjiu Wang MICFIL_VAD0_SCONFIG_SFILEN); 55629dbfeecSShengjiu Wang 55729dbfeecSShengjiu Wang /* Assert the VADSMAXEN bitfield. */ 55829dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG, 55929dbfeecSShengjiu Wang MICFIL_VAD0_SCONFIG_SMAXEN); 56029dbfeecSShengjiu Wang 56129dbfeecSShengjiu Wang /* Clear the VADNFILAUTO bitfield */ 56229dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 56329dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NFILAUT); 56429dbfeecSShengjiu Wang 56529dbfeecSShengjiu Wang /* Assert the VADNMINEN bitfield. */ 56629dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 56729dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NMINEN); 56829dbfeecSShengjiu Wang 56929dbfeecSShengjiu Wang /* Assert the VADNDECEN bitfield. */ 57029dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 57129dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NDECEN); 57229dbfeecSShengjiu Wang 57329dbfeecSShengjiu Wang /* Assert VADNOREN bitfield. */ 57429dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, 57529dbfeecSShengjiu Wang MICFIL_VAD0_NCONFIG_NOREN); 57629dbfeecSShengjiu Wang 57729dbfeecSShengjiu Wang return 0; 57829dbfeecSShengjiu Wang } 57929dbfeecSShengjiu Wang 58029dbfeecSShengjiu Wang /* 58129dbfeecSShengjiu Wang * Hardware Voice Active Detection: The HWVAD takes data from the input 58229dbfeecSShengjiu Wang * of a selected PDM microphone to detect if there is any 58329dbfeecSShengjiu Wang * voice activity. When a voice activity is detected, an interrupt could 58429dbfeecSShengjiu Wang * be delivered to the system. Initialization in section 8.4: 58529dbfeecSShengjiu Wang * Can work in two modes: 58629dbfeecSShengjiu Wang * -> Eneveope-based mode (section 8.4.1) 58729dbfeecSShengjiu Wang * -> Energy-based mode (section 8.4.2) 58829dbfeecSShengjiu Wang * 58929dbfeecSShengjiu Wang * It is important to remark that the HWVAD detector could be enabled 59029dbfeecSShengjiu Wang * or reset only when the MICFIL isn't running i.e. when the BSY_FIL 59129dbfeecSShengjiu Wang * bit in STAT register is cleared 59229dbfeecSShengjiu Wang */ 59329dbfeecSShengjiu Wang static int fsl_micfil_hwvad_enable(struct fsl_micfil *micfil) 59429dbfeecSShengjiu Wang { 59529dbfeecSShengjiu Wang int ret; 59629dbfeecSShengjiu Wang 59729dbfeecSShengjiu Wang micfil->vad_detected = 0; 59829dbfeecSShengjiu Wang 59929dbfeecSShengjiu Wang /* envelope-based specific initialization */ 60029dbfeecSShengjiu Wang if (micfil->vad_init_mode == MICFIL_HWVAD_ENVELOPE_MODE) 60129dbfeecSShengjiu Wang ret = fsl_micfil_init_hwvad_envelope_mode(micfil); 60229dbfeecSShengjiu Wang else 60329dbfeecSShengjiu Wang ret = fsl_micfil_init_hwvad_energy_mode(micfil); 60429dbfeecSShengjiu Wang if (ret) 60529dbfeecSShengjiu Wang return ret; 60629dbfeecSShengjiu Wang 60729dbfeecSShengjiu Wang /* Voice Activity Detector Internal Filters Initialization*/ 60829dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 60929dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_ST10); 61029dbfeecSShengjiu Wang 61129dbfeecSShengjiu Wang /* Voice Activity Detector Internal Filter */ 61229dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 61329dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_ST10); 61429dbfeecSShengjiu Wang 61529dbfeecSShengjiu Wang /* Enable Interrupts */ 61629dbfeecSShengjiu Wang ret = fsl_micfil_configure_hwvad_interrupts(micfil, 1); 61729dbfeecSShengjiu Wang if (ret) 61829dbfeecSShengjiu Wang return ret; 61929dbfeecSShengjiu Wang 62029dbfeecSShengjiu Wang /* Voice Activity Detector Reset */ 62129dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 62229dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_RST); 62329dbfeecSShengjiu Wang 62429dbfeecSShengjiu Wang /* Voice Activity Detector Enabled */ 62529dbfeecSShengjiu Wang regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 62629dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_EN); 62729dbfeecSShengjiu Wang 62829dbfeecSShengjiu Wang return 0; 62929dbfeecSShengjiu Wang } 63029dbfeecSShengjiu Wang 63129dbfeecSShengjiu Wang static int fsl_micfil_hwvad_disable(struct fsl_micfil *micfil) 63229dbfeecSShengjiu Wang { 63329dbfeecSShengjiu Wang struct device *dev = &micfil->pdev->dev; 63429dbfeecSShengjiu Wang int ret = 0; 63529dbfeecSShengjiu Wang 63629dbfeecSShengjiu Wang /* Disable HWVAD */ 63729dbfeecSShengjiu Wang regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 63829dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_EN); 63929dbfeecSShengjiu Wang 64029dbfeecSShengjiu Wang /* Disable hwvad interrupts */ 64129dbfeecSShengjiu Wang ret = fsl_micfil_configure_hwvad_interrupts(micfil, 0); 64229dbfeecSShengjiu Wang if (ret) 64329dbfeecSShengjiu Wang dev_err(dev, "Failed to disable interrupts\n"); 64429dbfeecSShengjiu Wang 64529dbfeecSShengjiu Wang return ret; 64629dbfeecSShengjiu Wang } 64729dbfeecSShengjiu Wang 64847a70e6fSCosmin Samoila static int fsl_micfil_trigger(struct snd_pcm_substream *substream, int cmd, 64947a70e6fSCosmin Samoila struct snd_soc_dai *dai) 65047a70e6fSCosmin Samoila { 65147a70e6fSCosmin Samoila struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai); 65247a70e6fSCosmin Samoila struct device *dev = &micfil->pdev->dev; 65347a70e6fSCosmin Samoila int ret; 65447a70e6fSCosmin Samoila 65547a70e6fSCosmin Samoila switch (cmd) { 65647a70e6fSCosmin Samoila case SNDRV_PCM_TRIGGER_START: 65747a70e6fSCosmin Samoila case SNDRV_PCM_TRIGGER_RESUME: 65847a70e6fSCosmin Samoila case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 65947a70e6fSCosmin Samoila ret = fsl_micfil_reset(dev); 66047a70e6fSCosmin Samoila if (ret) { 66147a70e6fSCosmin Samoila dev_err(dev, "failed to soft reset\n"); 66247a70e6fSCosmin Samoila return ret; 66347a70e6fSCosmin Samoila } 66447a70e6fSCosmin Samoila 66547a70e6fSCosmin Samoila /* DMA Interrupt Selection - DISEL bits 66647a70e6fSCosmin Samoila * 00 - DMA and IRQ disabled 66747a70e6fSCosmin Samoila * 01 - DMA req enabled 66847a70e6fSCosmin Samoila * 10 - IRQ enabled 66947a70e6fSCosmin Samoila * 11 - reserved 67047a70e6fSCosmin Samoila */ 67147a70e6fSCosmin Samoila ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1, 67217f2142bSSascha Hauer MICFIL_CTRL1_DISEL, 67317f2142bSSascha Hauer FIELD_PREP(MICFIL_CTRL1_DISEL, MICFIL_CTRL1_DISEL_DMA)); 6742c602c7eSSascha Hauer if (ret) 67547a70e6fSCosmin Samoila return ret; 67647a70e6fSCosmin Samoila 67747a70e6fSCosmin Samoila /* Enable the module */ 678d46c2127SSascha Hauer ret = regmap_set_bits(micfil->regmap, REG_MICFIL_CTRL1, 67947a70e6fSCosmin Samoila MICFIL_CTRL1_PDMIEN); 6802c602c7eSSascha Hauer if (ret) 68147a70e6fSCosmin Samoila return ret; 68247a70e6fSCosmin Samoila 68329dbfeecSShengjiu Wang if (micfil->vad_enabled) 68429dbfeecSShengjiu Wang fsl_micfil_hwvad_enable(micfil); 68529dbfeecSShengjiu Wang 68647a70e6fSCosmin Samoila break; 68747a70e6fSCosmin Samoila case SNDRV_PCM_TRIGGER_STOP: 68847a70e6fSCosmin Samoila case SNDRV_PCM_TRIGGER_SUSPEND: 68947a70e6fSCosmin Samoila case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 69029dbfeecSShengjiu Wang if (micfil->vad_enabled) 69129dbfeecSShengjiu Wang fsl_micfil_hwvad_disable(micfil); 69229dbfeecSShengjiu Wang 69347a70e6fSCosmin Samoila /* Disable the module */ 694d46c2127SSascha Hauer ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1, 695d46c2127SSascha Hauer MICFIL_CTRL1_PDMIEN); 6962c602c7eSSascha Hauer if (ret) 69747a70e6fSCosmin Samoila return ret; 69847a70e6fSCosmin Samoila 69947a70e6fSCosmin Samoila ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1, 70017f2142bSSascha Hauer MICFIL_CTRL1_DISEL, 70117f2142bSSascha Hauer FIELD_PREP(MICFIL_CTRL1_DISEL, MICFIL_CTRL1_DISEL_DISABLE)); 7022c602c7eSSascha Hauer if (ret) 70347a70e6fSCosmin Samoila return ret; 70447a70e6fSCosmin Samoila break; 70547a70e6fSCosmin Samoila default: 70647a70e6fSCosmin Samoila return -EINVAL; 70747a70e6fSCosmin Samoila } 70847a70e6fSCosmin Samoila return 0; 70947a70e6fSCosmin Samoila } 71047a70e6fSCosmin Samoila 71193f54100SShengjiu Wang static int fsl_micfil_reparent_rootclk(struct fsl_micfil *micfil, unsigned int sample_rate) 71293f54100SShengjiu Wang { 71393f54100SShengjiu Wang struct device *dev = &micfil->pdev->dev; 71493f54100SShengjiu Wang u64 ratio = sample_rate; 71593f54100SShengjiu Wang struct clk *clk; 71693f54100SShengjiu Wang int ret; 71793f54100SShengjiu Wang 71893f54100SShengjiu Wang /* Get root clock */ 71993f54100SShengjiu Wang clk = micfil->mclk; 72093f54100SShengjiu Wang 72193f54100SShengjiu Wang /* Disable clock first, for it was enabled by pm_runtime */ 72293f54100SShengjiu Wang clk_disable_unprepare(clk); 72393f54100SShengjiu Wang fsl_asoc_reparent_pll_clocks(dev, clk, micfil->pll8k_clk, 72493f54100SShengjiu Wang micfil->pll11k_clk, ratio); 72593f54100SShengjiu Wang ret = clk_prepare_enable(clk); 72693f54100SShengjiu Wang if (ret) 72793f54100SShengjiu Wang return ret; 72893f54100SShengjiu Wang 72993f54100SShengjiu Wang return 0; 73093f54100SShengjiu Wang } 73193f54100SShengjiu Wang 73247a70e6fSCosmin Samoila static int fsl_micfil_hw_params(struct snd_pcm_substream *substream, 73347a70e6fSCosmin Samoila struct snd_pcm_hw_params *params, 73447a70e6fSCosmin Samoila struct snd_soc_dai *dai) 73547a70e6fSCosmin Samoila { 73647a70e6fSCosmin Samoila struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai); 73747a70e6fSCosmin Samoila unsigned int channels = params_channels(params); 73847a70e6fSCosmin Samoila unsigned int rate = params_rate(params); 739cc5ef57dSSascha Hauer int clk_div = 8; 740cc5ef57dSSascha Hauer int osr = MICFIL_OSR_DEFAULT; 74147a70e6fSCosmin Samoila int ret; 74247a70e6fSCosmin Samoila 74347a70e6fSCosmin Samoila /* 1. Disable the module */ 744d46c2127SSascha Hauer ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1, 745d46c2127SSascha Hauer MICFIL_CTRL1_PDMIEN); 7462c602c7eSSascha Hauer if (ret) 74747a70e6fSCosmin Samoila return ret; 74847a70e6fSCosmin Samoila 74947a70e6fSCosmin Samoila /* enable channels */ 75047a70e6fSCosmin Samoila ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1, 75147a70e6fSCosmin Samoila 0xFF, ((1 << channels) - 1)); 7522c602c7eSSascha Hauer if (ret) 75347a70e6fSCosmin Samoila return ret; 75447a70e6fSCosmin Samoila 75593f54100SShengjiu Wang ret = fsl_micfil_reparent_rootclk(micfil, rate); 75693f54100SShengjiu Wang if (ret) 75793f54100SShengjiu Wang return ret; 75893f54100SShengjiu Wang 759cc5ef57dSSascha Hauer ret = clk_set_rate(micfil->mclk, rate * clk_div * osr * 8); 760cc5ef57dSSascha Hauer if (ret) 76147a70e6fSCosmin Samoila return ret; 762cc5ef57dSSascha Hauer 763cc5ef57dSSascha Hauer ret = micfil_set_quality(micfil); 764cc5ef57dSSascha Hauer if (ret) 765cc5ef57dSSascha Hauer return ret; 766cc5ef57dSSascha Hauer 767cc5ef57dSSascha Hauer ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2, 768cc5ef57dSSascha Hauer MICFIL_CTRL2_CLKDIV | MICFIL_CTRL2_CICOSR, 769cc5ef57dSSascha Hauer FIELD_PREP(MICFIL_CTRL2_CLKDIV, clk_div) | 770cc5ef57dSSascha Hauer FIELD_PREP(MICFIL_CTRL2_CICOSR, 16 - osr)); 77147a70e6fSCosmin Samoila 77229dbfeecSShengjiu Wang /* Configure CIC OSR in VADCICOSR */ 77329dbfeecSShengjiu Wang regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 77429dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_CICOSR, 77529dbfeecSShengjiu Wang FIELD_PREP(MICFIL_VAD0_CTRL1_CICOSR, 16 - osr)); 77629dbfeecSShengjiu Wang 77729dbfeecSShengjiu Wang /* Configure source channel in VADCHSEL */ 77829dbfeecSShengjiu Wang regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, 77929dbfeecSShengjiu Wang MICFIL_VAD0_CTRL1_CHSEL, 78029dbfeecSShengjiu Wang FIELD_PREP(MICFIL_VAD0_CTRL1_CHSEL, (channels - 1))); 78129dbfeecSShengjiu Wang 7822495ba26SSascha Hauer micfil->dma_params_rx.peripheral_config = &micfil->sdmacfg; 7832495ba26SSascha Hauer micfil->dma_params_rx.peripheral_size = sizeof(micfil->sdmacfg); 7842495ba26SSascha Hauer micfil->sdmacfg.n_fifos_src = channels; 7852495ba26SSascha Hauer micfil->sdmacfg.sw_done = true; 78647a70e6fSCosmin Samoila micfil->dma_params_rx.maxburst = channels * MICFIL_DMA_MAXBURST_RX; 78777a7a6e9SChancel Liu if (micfil->soc->use_edma) 78877a7a6e9SChancel Liu micfil->dma_params_rx.maxburst = channels; 78947a70e6fSCosmin Samoila 79047a70e6fSCosmin Samoila return 0; 79147a70e6fSCosmin Samoila } 79247a70e6fSCosmin Samoila 79347a70e6fSCosmin Samoila static int fsl_micfil_dai_probe(struct snd_soc_dai *cpu_dai) 79447a70e6fSCosmin Samoila { 79547a70e6fSCosmin Samoila struct fsl_micfil *micfil = dev_get_drvdata(cpu_dai->dev); 7963b13b143SShengjiu Wang struct device *dev = cpu_dai->dev; 7973b13b143SShengjiu Wang unsigned int val = 0; 7983b13b143SShengjiu Wang int ret, i; 79947a70e6fSCosmin Samoila 8003b13b143SShengjiu Wang micfil->quality = QUALITY_VLOW0; 80129dbfeecSShengjiu Wang micfil->card = cpu_dai->component->card; 80247a70e6fSCosmin Samoila 8033b13b143SShengjiu Wang /* set default gain to 2 */ 8043b13b143SShengjiu Wang regmap_write(micfil->regmap, REG_MICFIL_OUT_CTRL, 0x22222222); 8053b13b143SShengjiu Wang 8063b13b143SShengjiu Wang /* set DC Remover in bypass mode*/ 8073b13b143SShengjiu Wang for (i = 0; i < MICFIL_OUTPUT_CHANNELS; i++) 8083b13b143SShengjiu Wang val |= MICFIL_DC_BYPASS << MICFIL_DC_CHX_SHIFT(i); 8093b13b143SShengjiu Wang ret = regmap_update_bits(micfil->regmap, REG_MICFIL_DC_CTRL, 8103b13b143SShengjiu Wang MICFIL_DC_CTRL_CONFIG, val); 8113b13b143SShengjiu Wang if (ret) { 8123b13b143SShengjiu Wang dev_err(dev, "failed to set DC Remover mode bits\n"); 8133b13b143SShengjiu Wang return ret; 8143b13b143SShengjiu Wang } 8153b13b143SShengjiu Wang micfil->dc_remover = MICFIL_DC_BYPASS; 81647a70e6fSCosmin Samoila 81747a70e6fSCosmin Samoila snd_soc_dai_init_dma_data(cpu_dai, NULL, 81847a70e6fSCosmin Samoila &micfil->dma_params_rx); 81947a70e6fSCosmin Samoila 82047a70e6fSCosmin Samoila /* FIFO Watermark Control - FIFOWMK*/ 82147a70e6fSCosmin Samoila ret = regmap_update_bits(micfil->regmap, REG_MICFIL_FIFO_CTRL, 82217f2142bSSascha Hauer MICFIL_FIFO_CTRL_FIFOWMK, 82317f2142bSSascha Hauer FIELD_PREP(MICFIL_FIFO_CTRL_FIFOWMK, micfil->soc->fifo_depth - 1)); 8242c602c7eSSascha Hauer if (ret) 82547a70e6fSCosmin Samoila return ret; 82647a70e6fSCosmin Samoila 82747a70e6fSCosmin Samoila return 0; 82847a70e6fSCosmin Samoila } 82947a70e6fSCosmin Samoila 830*8b339bebSChancel Liu static int fsl_micfil_component_probe(struct snd_soc_component *component) 831*8b339bebSChancel Liu { 832*8b339bebSChancel Liu struct fsl_micfil *micfil = snd_soc_component_get_drvdata(component); 833*8b339bebSChancel Liu 834*8b339bebSChancel Liu if (micfil->soc->volume_sx) 835*8b339bebSChancel Liu snd_soc_add_component_controls(component, fsl_micfil_volume_sx_controls, 836*8b339bebSChancel Liu ARRAY_SIZE(fsl_micfil_volume_sx_controls)); 837*8b339bebSChancel Liu else 838*8b339bebSChancel Liu snd_soc_add_component_controls(component, fsl_micfil_volume_controls, 839*8b339bebSChancel Liu ARRAY_SIZE(fsl_micfil_volume_controls)); 840*8b339bebSChancel Liu 841*8b339bebSChancel Liu return 0; 842*8b339bebSChancel Liu } 843*8b339bebSChancel Liu 844ac27ca16SKuninori Morimoto static const struct snd_soc_dai_ops fsl_micfil_dai_ops = { 84547a70e6fSCosmin Samoila .probe = fsl_micfil_dai_probe, 846ac27ca16SKuninori Morimoto .startup = fsl_micfil_startup, 847ac27ca16SKuninori Morimoto .trigger = fsl_micfil_trigger, 848ac27ca16SKuninori Morimoto .hw_params = fsl_micfil_hw_params, 849ac27ca16SKuninori Morimoto }; 850ac27ca16SKuninori Morimoto 851ac27ca16SKuninori Morimoto static struct snd_soc_dai_driver fsl_micfil_dai = { 85247a70e6fSCosmin Samoila .capture = { 85347a70e6fSCosmin Samoila .stream_name = "CPU-Capture", 85447a70e6fSCosmin Samoila .channels_min = 1, 85547a70e6fSCosmin Samoila .channels_max = 8, 85699c08cdbSSascha Hauer .rates = SNDRV_PCM_RATE_8000_48000, 85799c08cdbSSascha Hauer .formats = SNDRV_PCM_FMTBIT_S16_LE, 85847a70e6fSCosmin Samoila }, 85947a70e6fSCosmin Samoila .ops = &fsl_micfil_dai_ops, 86047a70e6fSCosmin Samoila }; 86147a70e6fSCosmin Samoila 86247a70e6fSCosmin Samoila static const struct snd_soc_component_driver fsl_micfil_component = { 86347a70e6fSCosmin Samoila .name = "fsl-micfil-dai", 864*8b339bebSChancel Liu .probe = fsl_micfil_component_probe, 86547a70e6fSCosmin Samoila .controls = fsl_micfil_snd_controls, 86647a70e6fSCosmin Samoila .num_controls = ARRAY_SIZE(fsl_micfil_snd_controls), 867978bd27cSShengjiu Wang .legacy_dai_naming = 1, 86847a70e6fSCosmin Samoila }; 86947a70e6fSCosmin Samoila 87047a70e6fSCosmin Samoila /* REGMAP */ 87147a70e6fSCosmin Samoila static const struct reg_default fsl_micfil_reg_defaults[] = { 87247a70e6fSCosmin Samoila {REG_MICFIL_CTRL1, 0x00000000}, 87347a70e6fSCosmin Samoila {REG_MICFIL_CTRL2, 0x00000000}, 87447a70e6fSCosmin Samoila {REG_MICFIL_STAT, 0x00000000}, 87547a70e6fSCosmin Samoila {REG_MICFIL_FIFO_CTRL, 0x00000007}, 87647a70e6fSCosmin Samoila {REG_MICFIL_FIFO_STAT, 0x00000000}, 87747a70e6fSCosmin Samoila {REG_MICFIL_DATACH0, 0x00000000}, 87847a70e6fSCosmin Samoila {REG_MICFIL_DATACH1, 0x00000000}, 87947a70e6fSCosmin Samoila {REG_MICFIL_DATACH2, 0x00000000}, 88047a70e6fSCosmin Samoila {REG_MICFIL_DATACH3, 0x00000000}, 88147a70e6fSCosmin Samoila {REG_MICFIL_DATACH4, 0x00000000}, 88247a70e6fSCosmin Samoila {REG_MICFIL_DATACH5, 0x00000000}, 88347a70e6fSCosmin Samoila {REG_MICFIL_DATACH6, 0x00000000}, 88447a70e6fSCosmin Samoila {REG_MICFIL_DATACH7, 0x00000000}, 88547a70e6fSCosmin Samoila {REG_MICFIL_DC_CTRL, 0x00000000}, 88647a70e6fSCosmin Samoila {REG_MICFIL_OUT_CTRL, 0x00000000}, 88747a70e6fSCosmin Samoila {REG_MICFIL_OUT_STAT, 0x00000000}, 88847a70e6fSCosmin Samoila {REG_MICFIL_VAD0_CTRL1, 0x00000000}, 88947a70e6fSCosmin Samoila {REG_MICFIL_VAD0_CTRL2, 0x000A0000}, 89047a70e6fSCosmin Samoila {REG_MICFIL_VAD0_STAT, 0x00000000}, 89147a70e6fSCosmin Samoila {REG_MICFIL_VAD0_SCONFIG, 0x00000000}, 89247a70e6fSCosmin Samoila {REG_MICFIL_VAD0_NCONFIG, 0x80000000}, 89347a70e6fSCosmin Samoila {REG_MICFIL_VAD0_NDATA, 0x00000000}, 89447a70e6fSCosmin Samoila {REG_MICFIL_VAD0_ZCD, 0x00000004}, 89547a70e6fSCosmin Samoila }; 89647a70e6fSCosmin Samoila 89747a70e6fSCosmin Samoila static bool fsl_micfil_readable_reg(struct device *dev, unsigned int reg) 89847a70e6fSCosmin Samoila { 89947a70e6fSCosmin Samoila switch (reg) { 90047a70e6fSCosmin Samoila case REG_MICFIL_CTRL1: 90147a70e6fSCosmin Samoila case REG_MICFIL_CTRL2: 90247a70e6fSCosmin Samoila case REG_MICFIL_STAT: 90347a70e6fSCosmin Samoila case REG_MICFIL_FIFO_CTRL: 90447a70e6fSCosmin Samoila case REG_MICFIL_FIFO_STAT: 90547a70e6fSCosmin Samoila case REG_MICFIL_DATACH0: 90647a70e6fSCosmin Samoila case REG_MICFIL_DATACH1: 90747a70e6fSCosmin Samoila case REG_MICFIL_DATACH2: 90847a70e6fSCosmin Samoila case REG_MICFIL_DATACH3: 90947a70e6fSCosmin Samoila case REG_MICFIL_DATACH4: 91047a70e6fSCosmin Samoila case REG_MICFIL_DATACH5: 91147a70e6fSCosmin Samoila case REG_MICFIL_DATACH6: 91247a70e6fSCosmin Samoila case REG_MICFIL_DATACH7: 91347a70e6fSCosmin Samoila case REG_MICFIL_DC_CTRL: 91447a70e6fSCosmin Samoila case REG_MICFIL_OUT_CTRL: 91547a70e6fSCosmin Samoila case REG_MICFIL_OUT_STAT: 91651d765f7SChancel Liu case REG_MICFIL_FSYNC_CTRL: 91751d765f7SChancel Liu case REG_MICFIL_VERID: 91851d765f7SChancel Liu case REG_MICFIL_PARAM: 91947a70e6fSCosmin Samoila case REG_MICFIL_VAD0_CTRL1: 92047a70e6fSCosmin Samoila case REG_MICFIL_VAD0_CTRL2: 92147a70e6fSCosmin Samoila case REG_MICFIL_VAD0_STAT: 92247a70e6fSCosmin Samoila case REG_MICFIL_VAD0_SCONFIG: 92347a70e6fSCosmin Samoila case REG_MICFIL_VAD0_NCONFIG: 92447a70e6fSCosmin Samoila case REG_MICFIL_VAD0_NDATA: 92547a70e6fSCosmin Samoila case REG_MICFIL_VAD0_ZCD: 92647a70e6fSCosmin Samoila return true; 92747a70e6fSCosmin Samoila default: 92847a70e6fSCosmin Samoila return false; 92947a70e6fSCosmin Samoila } 93047a70e6fSCosmin Samoila } 93147a70e6fSCosmin Samoila 93247a70e6fSCosmin Samoila static bool fsl_micfil_writeable_reg(struct device *dev, unsigned int reg) 93347a70e6fSCosmin Samoila { 93447a70e6fSCosmin Samoila switch (reg) { 93547a70e6fSCosmin Samoila case REG_MICFIL_CTRL1: 93647a70e6fSCosmin Samoila case REG_MICFIL_CTRL2: 93747a70e6fSCosmin Samoila case REG_MICFIL_STAT: /* Write 1 to Clear */ 93847a70e6fSCosmin Samoila case REG_MICFIL_FIFO_CTRL: 93947a70e6fSCosmin Samoila case REG_MICFIL_FIFO_STAT: /* Write 1 to Clear */ 94047a70e6fSCosmin Samoila case REG_MICFIL_DC_CTRL: 94147a70e6fSCosmin Samoila case REG_MICFIL_OUT_CTRL: 94247a70e6fSCosmin Samoila case REG_MICFIL_OUT_STAT: /* Write 1 to Clear */ 94351d765f7SChancel Liu case REG_MICFIL_FSYNC_CTRL: 94447a70e6fSCosmin Samoila case REG_MICFIL_VAD0_CTRL1: 94547a70e6fSCosmin Samoila case REG_MICFIL_VAD0_CTRL2: 94647a70e6fSCosmin Samoila case REG_MICFIL_VAD0_STAT: /* Write 1 to Clear */ 94747a70e6fSCosmin Samoila case REG_MICFIL_VAD0_SCONFIG: 94847a70e6fSCosmin Samoila case REG_MICFIL_VAD0_NCONFIG: 94947a70e6fSCosmin Samoila case REG_MICFIL_VAD0_ZCD: 95047a70e6fSCosmin Samoila return true; 95147a70e6fSCosmin Samoila default: 95247a70e6fSCosmin Samoila return false; 95347a70e6fSCosmin Samoila } 95447a70e6fSCosmin Samoila } 95547a70e6fSCosmin Samoila 95647a70e6fSCosmin Samoila static bool fsl_micfil_volatile_reg(struct device *dev, unsigned int reg) 95747a70e6fSCosmin Samoila { 95847a70e6fSCosmin Samoila switch (reg) { 95947a70e6fSCosmin Samoila case REG_MICFIL_STAT: 96047a70e6fSCosmin Samoila case REG_MICFIL_DATACH0: 96147a70e6fSCosmin Samoila case REG_MICFIL_DATACH1: 96247a70e6fSCosmin Samoila case REG_MICFIL_DATACH2: 96347a70e6fSCosmin Samoila case REG_MICFIL_DATACH3: 96447a70e6fSCosmin Samoila case REG_MICFIL_DATACH4: 96547a70e6fSCosmin Samoila case REG_MICFIL_DATACH5: 96647a70e6fSCosmin Samoila case REG_MICFIL_DATACH6: 96747a70e6fSCosmin Samoila case REG_MICFIL_DATACH7: 96851d765f7SChancel Liu case REG_MICFIL_VERID: 96951d765f7SChancel Liu case REG_MICFIL_PARAM: 97047a70e6fSCosmin Samoila case REG_MICFIL_VAD0_STAT: 97147a70e6fSCosmin Samoila case REG_MICFIL_VAD0_NDATA: 97247a70e6fSCosmin Samoila return true; 97347a70e6fSCosmin Samoila default: 97447a70e6fSCosmin Samoila return false; 97547a70e6fSCosmin Samoila } 97647a70e6fSCosmin Samoila } 97747a70e6fSCosmin Samoila 97847a70e6fSCosmin Samoila static const struct regmap_config fsl_micfil_regmap_config = { 97947a70e6fSCosmin Samoila .reg_bits = 32, 98047a70e6fSCosmin Samoila .reg_stride = 4, 98147a70e6fSCosmin Samoila .val_bits = 32, 98247a70e6fSCosmin Samoila 98347a70e6fSCosmin Samoila .max_register = REG_MICFIL_VAD0_ZCD, 98447a70e6fSCosmin Samoila .reg_defaults = fsl_micfil_reg_defaults, 98547a70e6fSCosmin Samoila .num_reg_defaults = ARRAY_SIZE(fsl_micfil_reg_defaults), 98647a70e6fSCosmin Samoila .readable_reg = fsl_micfil_readable_reg, 98747a70e6fSCosmin Samoila .volatile_reg = fsl_micfil_volatile_reg, 98847a70e6fSCosmin Samoila .writeable_reg = fsl_micfil_writeable_reg, 98947a70e6fSCosmin Samoila .cache_type = REGCACHE_RBTREE, 99047a70e6fSCosmin Samoila }; 99147a70e6fSCosmin Samoila 99247a70e6fSCosmin Samoila /* END OF REGMAP */ 99347a70e6fSCosmin Samoila 99447a70e6fSCosmin Samoila static irqreturn_t micfil_isr(int irq, void *devid) 99547a70e6fSCosmin Samoila { 99647a70e6fSCosmin Samoila struct fsl_micfil *micfil = (struct fsl_micfil *)devid; 99747a70e6fSCosmin Samoila struct platform_device *pdev = micfil->pdev; 99847a70e6fSCosmin Samoila u32 stat_reg; 99947a70e6fSCosmin Samoila u32 fifo_stat_reg; 100047a70e6fSCosmin Samoila u32 ctrl1_reg; 100147a70e6fSCosmin Samoila bool dma_enabled; 100247a70e6fSCosmin Samoila int i; 100347a70e6fSCosmin Samoila 100447a70e6fSCosmin Samoila regmap_read(micfil->regmap, REG_MICFIL_STAT, &stat_reg); 100547a70e6fSCosmin Samoila regmap_read(micfil->regmap, REG_MICFIL_CTRL1, &ctrl1_reg); 100647a70e6fSCosmin Samoila regmap_read(micfil->regmap, REG_MICFIL_FIFO_STAT, &fifo_stat_reg); 100747a70e6fSCosmin Samoila 100817f2142bSSascha Hauer dma_enabled = FIELD_GET(MICFIL_CTRL1_DISEL, ctrl1_reg) == MICFIL_CTRL1_DISEL_DMA; 100947a70e6fSCosmin Samoila 101047a70e6fSCosmin Samoila /* Channel 0-7 Output Data Flags */ 101147a70e6fSCosmin Samoila for (i = 0; i < MICFIL_OUTPUT_CHANNELS; i++) { 101217f2142bSSascha Hauer if (stat_reg & MICFIL_STAT_CHXF(i)) 101347a70e6fSCosmin Samoila dev_dbg(&pdev->dev, 101447a70e6fSCosmin Samoila "Data available in Data Channel %d\n", i); 101547a70e6fSCosmin Samoila /* if DMA is not enabled, field must be written with 1 101647a70e6fSCosmin Samoila * to clear 101747a70e6fSCosmin Samoila */ 101847a70e6fSCosmin Samoila if (!dma_enabled) 101947a70e6fSCosmin Samoila regmap_write_bits(micfil->regmap, 102047a70e6fSCosmin Samoila REG_MICFIL_STAT, 102117f2142bSSascha Hauer MICFIL_STAT_CHXF(i), 102247a70e6fSCosmin Samoila 1); 102347a70e6fSCosmin Samoila } 102447a70e6fSCosmin Samoila 102547a70e6fSCosmin Samoila for (i = 0; i < MICFIL_FIFO_NUM; i++) { 102617f2142bSSascha Hauer if (fifo_stat_reg & MICFIL_FIFO_STAT_FIFOX_OVER(i)) 102747a70e6fSCosmin Samoila dev_dbg(&pdev->dev, 102847a70e6fSCosmin Samoila "FIFO Overflow Exception flag for channel %d\n", 102947a70e6fSCosmin Samoila i); 103047a70e6fSCosmin Samoila 103117f2142bSSascha Hauer if (fifo_stat_reg & MICFIL_FIFO_STAT_FIFOX_UNDER(i)) 103247a70e6fSCosmin Samoila dev_dbg(&pdev->dev, 103347a70e6fSCosmin Samoila "FIFO Underflow Exception flag for channel %d\n", 103447a70e6fSCosmin Samoila i); 103547a70e6fSCosmin Samoila } 103647a70e6fSCosmin Samoila 103747a70e6fSCosmin Samoila return IRQ_HANDLED; 103847a70e6fSCosmin Samoila } 103947a70e6fSCosmin Samoila 104047a70e6fSCosmin Samoila static irqreturn_t micfil_err_isr(int irq, void *devid) 104147a70e6fSCosmin Samoila { 104247a70e6fSCosmin Samoila struct fsl_micfil *micfil = (struct fsl_micfil *)devid; 104347a70e6fSCosmin Samoila struct platform_device *pdev = micfil->pdev; 104447a70e6fSCosmin Samoila u32 stat_reg; 104547a70e6fSCosmin Samoila 104647a70e6fSCosmin Samoila regmap_read(micfil->regmap, REG_MICFIL_STAT, &stat_reg); 104747a70e6fSCosmin Samoila 1048bd2cffd1SSascha Hauer if (stat_reg & MICFIL_STAT_BSY_FIL) 104947a70e6fSCosmin Samoila dev_dbg(&pdev->dev, "isr: Decimation Filter is running\n"); 105047a70e6fSCosmin Samoila 1051bd2cffd1SSascha Hauer if (stat_reg & MICFIL_STAT_FIR_RDY) 105247a70e6fSCosmin Samoila dev_dbg(&pdev->dev, "isr: FIR Filter Data ready\n"); 105347a70e6fSCosmin Samoila 1054bd2cffd1SSascha Hauer if (stat_reg & MICFIL_STAT_LOWFREQF) { 105547a70e6fSCosmin Samoila dev_dbg(&pdev->dev, "isr: ipg_clk_app is too low\n"); 105647a70e6fSCosmin Samoila regmap_write_bits(micfil->regmap, REG_MICFIL_STAT, 1057bd2cffd1SSascha Hauer MICFIL_STAT_LOWFREQF, 1); 105847a70e6fSCosmin Samoila } 105947a70e6fSCosmin Samoila 106047a70e6fSCosmin Samoila return IRQ_HANDLED; 106147a70e6fSCosmin Samoila } 106247a70e6fSCosmin Samoila 106329dbfeecSShengjiu Wang static irqreturn_t voice_detected_fn(int irq, void *devid) 106429dbfeecSShengjiu Wang { 106529dbfeecSShengjiu Wang struct fsl_micfil *micfil = (struct fsl_micfil *)devid; 106629dbfeecSShengjiu Wang struct snd_kcontrol *kctl; 106729dbfeecSShengjiu Wang 106829dbfeecSShengjiu Wang if (!micfil->card) 106929dbfeecSShengjiu Wang return IRQ_HANDLED; 107029dbfeecSShengjiu Wang 107129dbfeecSShengjiu Wang kctl = snd_soc_card_get_kcontrol(micfil->card, "VAD Detected"); 107229dbfeecSShengjiu Wang if (!kctl) 107329dbfeecSShengjiu Wang return IRQ_HANDLED; 107429dbfeecSShengjiu Wang 107529dbfeecSShengjiu Wang if (micfil->vad_detected) 107629dbfeecSShengjiu Wang snd_ctl_notify(micfil->card->snd_card, 107729dbfeecSShengjiu Wang SNDRV_CTL_EVENT_MASK_VALUE, 107829dbfeecSShengjiu Wang &kctl->id); 107929dbfeecSShengjiu Wang 108029dbfeecSShengjiu Wang return IRQ_HANDLED; 108129dbfeecSShengjiu Wang } 108229dbfeecSShengjiu Wang 108329dbfeecSShengjiu Wang static irqreturn_t hwvad_isr(int irq, void *devid) 108429dbfeecSShengjiu Wang { 108529dbfeecSShengjiu Wang struct fsl_micfil *micfil = (struct fsl_micfil *)devid; 108629dbfeecSShengjiu Wang struct device *dev = &micfil->pdev->dev; 108729dbfeecSShengjiu Wang u32 vad0_reg; 108829dbfeecSShengjiu Wang int ret; 108929dbfeecSShengjiu Wang 109029dbfeecSShengjiu Wang regmap_read(micfil->regmap, REG_MICFIL_VAD0_STAT, &vad0_reg); 109129dbfeecSShengjiu Wang 109229dbfeecSShengjiu Wang /* 109329dbfeecSShengjiu Wang * The only difference between MICFIL_VAD0_STAT_EF and 109429dbfeecSShengjiu Wang * MICFIL_VAD0_STAT_IF is that the former requires Write 109529dbfeecSShengjiu Wang * 1 to Clear. Since both flags are set, it is enough 109629dbfeecSShengjiu Wang * to only read one of them 109729dbfeecSShengjiu Wang */ 109829dbfeecSShengjiu Wang if (vad0_reg & MICFIL_VAD0_STAT_IF) { 109929dbfeecSShengjiu Wang /* Write 1 to clear */ 110029dbfeecSShengjiu Wang regmap_write_bits(micfil->regmap, REG_MICFIL_VAD0_STAT, 110129dbfeecSShengjiu Wang MICFIL_VAD0_STAT_IF, 110229dbfeecSShengjiu Wang MICFIL_VAD0_STAT_IF); 110329dbfeecSShengjiu Wang 110429dbfeecSShengjiu Wang micfil->vad_detected = 1; 110529dbfeecSShengjiu Wang } 110629dbfeecSShengjiu Wang 110729dbfeecSShengjiu Wang ret = fsl_micfil_hwvad_disable(micfil); 110829dbfeecSShengjiu Wang if (ret) 110929dbfeecSShengjiu Wang dev_err(dev, "Failed to disable hwvad\n"); 111029dbfeecSShengjiu Wang 111129dbfeecSShengjiu Wang return IRQ_WAKE_THREAD; 111229dbfeecSShengjiu Wang } 111329dbfeecSShengjiu Wang 111429dbfeecSShengjiu Wang static irqreturn_t hwvad_err_isr(int irq, void *devid) 111529dbfeecSShengjiu Wang { 111629dbfeecSShengjiu Wang struct fsl_micfil *micfil = (struct fsl_micfil *)devid; 111729dbfeecSShengjiu Wang struct device *dev = &micfil->pdev->dev; 111829dbfeecSShengjiu Wang u32 vad0_reg; 111929dbfeecSShengjiu Wang 112029dbfeecSShengjiu Wang regmap_read(micfil->regmap, REG_MICFIL_VAD0_STAT, &vad0_reg); 112129dbfeecSShengjiu Wang 112229dbfeecSShengjiu Wang if (vad0_reg & MICFIL_VAD0_STAT_INSATF) 112329dbfeecSShengjiu Wang dev_dbg(dev, "voice activity input overflow/underflow detected\n"); 112429dbfeecSShengjiu Wang 112529dbfeecSShengjiu Wang return IRQ_HANDLED; 112629dbfeecSShengjiu Wang } 112729dbfeecSShengjiu Wang 112836736505SChancel Liu static int fsl_micfil_runtime_suspend(struct device *dev); 112936736505SChancel Liu static int fsl_micfil_runtime_resume(struct device *dev); 113036736505SChancel Liu 113147a70e6fSCosmin Samoila static int fsl_micfil_probe(struct platform_device *pdev) 113247a70e6fSCosmin Samoila { 113347a70e6fSCosmin Samoila struct device_node *np = pdev->dev.of_node; 113447a70e6fSCosmin Samoila struct fsl_micfil *micfil; 113547a70e6fSCosmin Samoila struct resource *res; 113647a70e6fSCosmin Samoila void __iomem *regs; 113747a70e6fSCosmin Samoila int ret, i; 113847a70e6fSCosmin Samoila 113947a70e6fSCosmin Samoila micfil = devm_kzalloc(&pdev->dev, sizeof(*micfil), GFP_KERNEL); 114047a70e6fSCosmin Samoila if (!micfil) 114147a70e6fSCosmin Samoila return -ENOMEM; 114247a70e6fSCosmin Samoila 114347a70e6fSCosmin Samoila micfil->pdev = pdev; 11447eb10bfbSJustin Stitt strscpy(micfil->name, np->name, sizeof(micfil->name)); 114547a70e6fSCosmin Samoila 1146d7388718SFabio Estevam micfil->soc = of_device_get_match_data(&pdev->dev); 114747a70e6fSCosmin Samoila 114847a70e6fSCosmin Samoila /* ipg_clk is used to control the registers 114947a70e6fSCosmin Samoila * ipg_clk_app is used to operate the filter 115047a70e6fSCosmin Samoila */ 115147a70e6fSCosmin Samoila micfil->mclk = devm_clk_get(&pdev->dev, "ipg_clk_app"); 115247a70e6fSCosmin Samoila if (IS_ERR(micfil->mclk)) { 115347a70e6fSCosmin Samoila dev_err(&pdev->dev, "failed to get core clock: %ld\n", 115447a70e6fSCosmin Samoila PTR_ERR(micfil->mclk)); 115547a70e6fSCosmin Samoila return PTR_ERR(micfil->mclk); 115647a70e6fSCosmin Samoila } 115747a70e6fSCosmin Samoila 1158b5cf28f7SShengjiu Wang micfil->busclk = devm_clk_get(&pdev->dev, "ipg_clk"); 1159b5cf28f7SShengjiu Wang if (IS_ERR(micfil->busclk)) { 1160b5cf28f7SShengjiu Wang dev_err(&pdev->dev, "failed to get ipg clock: %ld\n", 1161b5cf28f7SShengjiu Wang PTR_ERR(micfil->busclk)); 1162b5cf28f7SShengjiu Wang return PTR_ERR(micfil->busclk); 1163b5cf28f7SShengjiu Wang } 1164b5cf28f7SShengjiu Wang 116593f54100SShengjiu Wang fsl_asoc_get_pll_clocks(&pdev->dev, &micfil->pll8k_clk, 116693f54100SShengjiu Wang &micfil->pll11k_clk); 116793f54100SShengjiu Wang 116847a70e6fSCosmin Samoila /* init regmap */ 1169d9bf1e79SYang Yingliang regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 117047a70e6fSCosmin Samoila if (IS_ERR(regs)) 117147a70e6fSCosmin Samoila return PTR_ERR(regs); 117247a70e6fSCosmin Samoila 1173b5cf28f7SShengjiu Wang micfil->regmap = devm_regmap_init_mmio(&pdev->dev, 117447a70e6fSCosmin Samoila regs, 117547a70e6fSCosmin Samoila &fsl_micfil_regmap_config); 117647a70e6fSCosmin Samoila if (IS_ERR(micfil->regmap)) { 117747a70e6fSCosmin Samoila dev_err(&pdev->dev, "failed to init MICFIL regmap: %ld\n", 117847a70e6fSCosmin Samoila PTR_ERR(micfil->regmap)); 117947a70e6fSCosmin Samoila return PTR_ERR(micfil->regmap); 118047a70e6fSCosmin Samoila } 118147a70e6fSCosmin Samoila 118247a70e6fSCosmin Samoila /* dataline mask for RX */ 118347a70e6fSCosmin Samoila ret = of_property_read_u32_index(np, 118447a70e6fSCosmin Samoila "fsl,dataline", 118547a70e6fSCosmin Samoila 0, 118647a70e6fSCosmin Samoila &micfil->dataline); 118747a70e6fSCosmin Samoila if (ret) 118847a70e6fSCosmin Samoila micfil->dataline = 1; 118947a70e6fSCosmin Samoila 119047a70e6fSCosmin Samoila if (micfil->dataline & ~micfil->soc->dataline) { 119147a70e6fSCosmin Samoila dev_err(&pdev->dev, "dataline setting error, Mask is 0x%X\n", 119247a70e6fSCosmin Samoila micfil->soc->dataline); 119347a70e6fSCosmin Samoila return -EINVAL; 119447a70e6fSCosmin Samoila } 119547a70e6fSCosmin Samoila 119647a70e6fSCosmin Samoila /* get IRQs */ 119747a70e6fSCosmin Samoila for (i = 0; i < MICFIL_IRQ_LINES; i++) { 119847a70e6fSCosmin Samoila micfil->irq[i] = platform_get_irq(pdev, i); 119983b35f45STang Bin if (micfil->irq[i] < 0) 120047a70e6fSCosmin Samoila return micfil->irq[i]; 120147a70e6fSCosmin Samoila } 120247a70e6fSCosmin Samoila 1203a62ed960SFabio Estevam /* Digital Microphone interface interrupt */ 120447a70e6fSCosmin Samoila ret = devm_request_irq(&pdev->dev, micfil->irq[0], 1205cbd090faSSascha Hauer micfil_isr, IRQF_SHARED, 120647a70e6fSCosmin Samoila micfil->name, micfil); 120747a70e6fSCosmin Samoila if (ret) { 120847a70e6fSCosmin Samoila dev_err(&pdev->dev, "failed to claim mic interface irq %u\n", 120947a70e6fSCosmin Samoila micfil->irq[0]); 121047a70e6fSCosmin Samoila return ret; 121147a70e6fSCosmin Samoila } 121247a70e6fSCosmin Samoila 1213a62ed960SFabio Estevam /* Digital Microphone interface error interrupt */ 121447a70e6fSCosmin Samoila ret = devm_request_irq(&pdev->dev, micfil->irq[1], 1215cbd090faSSascha Hauer micfil_err_isr, IRQF_SHARED, 121647a70e6fSCosmin Samoila micfil->name, micfil); 121747a70e6fSCosmin Samoila if (ret) { 121847a70e6fSCosmin Samoila dev_err(&pdev->dev, "failed to claim mic interface error irq %u\n", 121947a70e6fSCosmin Samoila micfil->irq[1]); 122047a70e6fSCosmin Samoila return ret; 122147a70e6fSCosmin Samoila } 122247a70e6fSCosmin Samoila 122329dbfeecSShengjiu Wang /* Digital Microphone interface voice activity detector event */ 122429dbfeecSShengjiu Wang ret = devm_request_threaded_irq(&pdev->dev, micfil->irq[2], 122529dbfeecSShengjiu Wang hwvad_isr, voice_detected_fn, 122629dbfeecSShengjiu Wang IRQF_SHARED, micfil->name, micfil); 122729dbfeecSShengjiu Wang if (ret) { 122829dbfeecSShengjiu Wang dev_err(&pdev->dev, "failed to claim hwvad event irq %u\n", 122929dbfeecSShengjiu Wang micfil->irq[0]); 123029dbfeecSShengjiu Wang return ret; 123129dbfeecSShengjiu Wang } 123229dbfeecSShengjiu Wang 123329dbfeecSShengjiu Wang /* Digital Microphone interface voice activity detector error */ 123429dbfeecSShengjiu Wang ret = devm_request_irq(&pdev->dev, micfil->irq[3], 123529dbfeecSShengjiu Wang hwvad_err_isr, IRQF_SHARED, 123629dbfeecSShengjiu Wang micfil->name, micfil); 123729dbfeecSShengjiu Wang if (ret) { 123829dbfeecSShengjiu Wang dev_err(&pdev->dev, "failed to claim hwvad error irq %u\n", 123929dbfeecSShengjiu Wang micfil->irq[1]); 124029dbfeecSShengjiu Wang return ret; 124129dbfeecSShengjiu Wang } 124229dbfeecSShengjiu Wang 124347a70e6fSCosmin Samoila micfil->dma_params_rx.chan_name = "rx"; 124447a70e6fSCosmin Samoila micfil->dma_params_rx.addr = res->start + REG_MICFIL_DATACH0; 124547a70e6fSCosmin Samoila micfil->dma_params_rx.maxburst = MICFIL_DMA_MAXBURST_RX; 124647a70e6fSCosmin Samoila 124747a70e6fSCosmin Samoila platform_set_drvdata(pdev, micfil); 124847a70e6fSCosmin Samoila 124947a70e6fSCosmin Samoila pm_runtime_enable(&pdev->dev); 125036736505SChancel Liu if (!pm_runtime_enabled(&pdev->dev)) { 125136736505SChancel Liu ret = fsl_micfil_runtime_resume(&pdev->dev); 125236736505SChancel Liu if (ret) 125336736505SChancel Liu goto err_pm_disable; 125436736505SChancel Liu } 125536736505SChancel Liu 125636736505SChancel Liu ret = pm_runtime_resume_and_get(&pdev->dev); 125736736505SChancel Liu if (ret < 0) 125836736505SChancel Liu goto err_pm_get_sync; 125936736505SChancel Liu 126036736505SChancel Liu /* Get micfil version */ 126136736505SChancel Liu ret = fsl_micfil_use_verid(&pdev->dev); 126236736505SChancel Liu if (ret < 0) 126336736505SChancel Liu dev_warn(&pdev->dev, "Error reading MICFIL version: %d\n", ret); 126436736505SChancel Liu 126536736505SChancel Liu ret = pm_runtime_put_sync(&pdev->dev); 126636736505SChancel Liu if (ret < 0 && ret != -ENOSYS) 126736736505SChancel Liu goto err_pm_get_sync; 126836736505SChancel Liu 1269b5cf28f7SShengjiu Wang regcache_cache_only(micfil->regmap, true); 127047a70e6fSCosmin Samoila 12710adf2920SShengjiu Wang /* 12720adf2920SShengjiu Wang * Register platform component before registering cpu dai for there 12730adf2920SShengjiu Wang * is not defer probe for platform component in snd_soc_add_pcm_runtime(). 12740adf2920SShengjiu Wang */ 12750adf2920SShengjiu Wang ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); 12760adf2920SShengjiu Wang if (ret) { 12770adf2920SShengjiu Wang dev_err(&pdev->dev, "failed to pcm register\n"); 127817955abaSShengjiu Wang goto err_pm_disable; 12790adf2920SShengjiu Wang } 12800adf2920SShengjiu Wang 1281cb05dac1SShengjiu Wang fsl_micfil_dai.capture.formats = micfil->soc->formats; 1282cb05dac1SShengjiu Wang 128347a70e6fSCosmin Samoila ret = devm_snd_soc_register_component(&pdev->dev, &fsl_micfil_component, 128447a70e6fSCosmin Samoila &fsl_micfil_dai, 1); 128547a70e6fSCosmin Samoila if (ret) { 128647a70e6fSCosmin Samoila dev_err(&pdev->dev, "failed to register component %s\n", 128747a70e6fSCosmin Samoila fsl_micfil_component.name); 128817955abaSShengjiu Wang goto err_pm_disable; 128947a70e6fSCosmin Samoila } 129047a70e6fSCosmin Samoila 129147a70e6fSCosmin Samoila return ret; 129217955abaSShengjiu Wang 129336736505SChancel Liu err_pm_get_sync: 129436736505SChancel Liu if (!pm_runtime_status_suspended(&pdev->dev)) 129536736505SChancel Liu fsl_micfil_runtime_suspend(&pdev->dev); 129617955abaSShengjiu Wang err_pm_disable: 129717955abaSShengjiu Wang pm_runtime_disable(&pdev->dev); 129817955abaSShengjiu Wang 129917955abaSShengjiu Wang return ret; 130017955abaSShengjiu Wang } 130117955abaSShengjiu Wang 130217955abaSShengjiu Wang static void fsl_micfil_remove(struct platform_device *pdev) 130317955abaSShengjiu Wang { 130417955abaSShengjiu Wang pm_runtime_disable(&pdev->dev); 130547a70e6fSCosmin Samoila } 130647a70e6fSCosmin Samoila 130736736505SChancel Liu static int fsl_micfil_runtime_suspend(struct device *dev) 130847a70e6fSCosmin Samoila { 130947a70e6fSCosmin Samoila struct fsl_micfil *micfil = dev_get_drvdata(dev); 131047a70e6fSCosmin Samoila 131147a70e6fSCosmin Samoila regcache_cache_only(micfil->regmap, true); 131247a70e6fSCosmin Samoila 131347a70e6fSCosmin Samoila clk_disable_unprepare(micfil->mclk); 1314b5cf28f7SShengjiu Wang clk_disable_unprepare(micfil->busclk); 131547a70e6fSCosmin Samoila 131647a70e6fSCosmin Samoila return 0; 131747a70e6fSCosmin Samoila } 131847a70e6fSCosmin Samoila 131936736505SChancel Liu static int fsl_micfil_runtime_resume(struct device *dev) 132047a70e6fSCosmin Samoila { 132147a70e6fSCosmin Samoila struct fsl_micfil *micfil = dev_get_drvdata(dev); 132247a70e6fSCosmin Samoila int ret; 132347a70e6fSCosmin Samoila 1324b5cf28f7SShengjiu Wang ret = clk_prepare_enable(micfil->busclk); 132547a70e6fSCosmin Samoila if (ret < 0) 132647a70e6fSCosmin Samoila return ret; 132747a70e6fSCosmin Samoila 1328b5cf28f7SShengjiu Wang ret = clk_prepare_enable(micfil->mclk); 1329b5cf28f7SShengjiu Wang if (ret < 0) { 1330b5cf28f7SShengjiu Wang clk_disable_unprepare(micfil->busclk); 1331b5cf28f7SShengjiu Wang return ret; 1332b5cf28f7SShengjiu Wang } 1333b5cf28f7SShengjiu Wang 133447a70e6fSCosmin Samoila regcache_cache_only(micfil->regmap, false); 133547a70e6fSCosmin Samoila regcache_mark_dirty(micfil->regmap); 133647a70e6fSCosmin Samoila regcache_sync(micfil->regmap); 133747a70e6fSCosmin Samoila 133847a70e6fSCosmin Samoila return 0; 133947a70e6fSCosmin Samoila } 134047a70e6fSCosmin Samoila 134147a70e6fSCosmin Samoila static const struct dev_pm_ops fsl_micfil_pm_ops = { 134247a70e6fSCosmin Samoila SET_RUNTIME_PM_OPS(fsl_micfil_runtime_suspend, 134347a70e6fSCosmin Samoila fsl_micfil_runtime_resume, 134447a70e6fSCosmin Samoila NULL) 1345a38a4090SChancel Liu SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 1346a38a4090SChancel Liu pm_runtime_force_resume) 134747a70e6fSCosmin Samoila }; 134847a70e6fSCosmin Samoila 134947a70e6fSCosmin Samoila static struct platform_driver fsl_micfil_driver = { 135047a70e6fSCosmin Samoila .probe = fsl_micfil_probe, 135117955abaSShengjiu Wang .remove_new = fsl_micfil_remove, 135247a70e6fSCosmin Samoila .driver = { 135347a70e6fSCosmin Samoila .name = "fsl-micfil-dai", 135447a70e6fSCosmin Samoila .pm = &fsl_micfil_pm_ops, 135547a70e6fSCosmin Samoila .of_match_table = fsl_micfil_dt_ids, 135647a70e6fSCosmin Samoila }, 135747a70e6fSCosmin Samoila }; 135847a70e6fSCosmin Samoila module_platform_driver(fsl_micfil_driver); 135947a70e6fSCosmin Samoila 136047a70e6fSCosmin Samoila MODULE_AUTHOR("Cosmin-Gabriel Samoila <cosmin.samoila@nxp.com>"); 136147a70e6fSCosmin Samoila MODULE_DESCRIPTION("NXP PDM Microphone Interface (MICFIL) driver"); 1362f803ec63SDaniel Baluta MODULE_LICENSE("Dual BSD/GPL"); 1363