diff options
Diffstat (limited to 'sound/soc/atmel/mchp-pdmc.c')
-rw-r--r-- | sound/soc/atmel/mchp-pdmc.c | 53 |
1 files changed, 48 insertions, 5 deletions
diff --git a/sound/soc/atmel/mchp-pdmc.c b/sound/soc/atmel/mchp-pdmc.c index cf4084dcbd5e..1aed3baa9369 100644 --- a/sound/soc/atmel/mchp-pdmc.c +++ b/sound/soc/atmel/mchp-pdmc.c @@ -114,6 +114,7 @@ struct mchp_pdmc { struct clk *gclk; u32 pdmcen; u32 suspend_irq; + u32 startup_delay_us; int mic_no; int sinc_order; bool audio_filter_en; @@ -425,6 +426,7 @@ static const struct snd_soc_component_driver mchp_pdmc_dai_component = { .open = &mchp_pdmc_open, .close = &mchp_pdmc_close, .legacy_dai_naming = 1, + .start_dma_last = 1, }; static const unsigned int mchp_pdmc_1mic[] = {1}; @@ -632,6 +634,29 @@ static int mchp_pdmc_hw_params(struct snd_pcm_substream *substream, return 0; } +static void mchp_pdmc_noise_filter_workaround(struct mchp_pdmc *dd) +{ + u32 tmp, steps = 16; + + /* + * PDMC doesn't wait for microphones' startup time thus the acquisition + * may start before the microphones are ready leading to poc noises at + * the beginning of capture. To avoid this, we need to wait 50ms (in + * normal startup procedure) or 150 ms (worst case after resume from sleep + * states) after microphones are enabled and then clear the FIFOs (by + * reading the RHR 16 times) and possible interrupts before continuing. + * Also, for this to work the DMA needs to be started after interrupts + * are enabled. + */ + usleep_range(dd->startup_delay_us, dd->startup_delay_us + 5); + + while (steps--) + regmap_read(dd->regmap, MCHP_PDMC_RHR, &tmp); + + /* Clear interrupts. */ + regmap_read(dd->regmap, MCHP_PDMC_ISR, &tmp); +} + static int mchp_pdmc_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { @@ -644,15 +669,17 @@ static int mchp_pdmc_trigger(struct snd_pcm_substream *substream, switch (cmd) { case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_START: - /* Enable overrun and underrun error interrupts */ - regmap_write(dd->regmap, MCHP_PDMC_IER, dd->suspend_irq | - MCHP_PDMC_IR_RXOVR | MCHP_PDMC_IR_RXUDR); - dd->suspend_irq = 0; - fallthrough; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: snd_soc_component_update_bits(cpu, MCHP_PDMC_MR, MCHP_PDMC_MR_PDMCEN_MASK, dd->pdmcen); + + mchp_pdmc_noise_filter_workaround(dd); + + /* Enable interrupts. */ + regmap_write(dd->regmap, MCHP_PDMC_IER, dd->suspend_irq | + MCHP_PDMC_IR_RXOVR | MCHP_PDMC_IR_RXUDR); + dd->suspend_irq = 0; break; case SNDRV_PCM_TRIGGER_SUSPEND: regmap_read(dd->regmap, MCHP_PDMC_IMR, &dd->suspend_irq); @@ -796,6 +823,7 @@ static bool mchp_pdmc_readable_reg(struct device *dev, unsigned int reg) case MCHP_PDMC_CFGR: case MCHP_PDMC_IMR: case MCHP_PDMC_ISR: + case MCHP_PDMC_RHR: case MCHP_PDMC_VER: return true; default: @@ -817,6 +845,17 @@ static bool mchp_pdmc_writeable_reg(struct device *dev, unsigned int reg) } } +static bool mchp_pdmc_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MCHP_PDMC_ISR: + case MCHP_PDMC_RHR: + return true; + default: + return false; + } +} + static bool mchp_pdmc_precious_reg(struct device *dev, unsigned int reg) { switch (reg) { @@ -836,6 +875,7 @@ static const struct regmap_config mchp_pdmc_regmap_config = { .readable_reg = mchp_pdmc_readable_reg, .writeable_reg = mchp_pdmc_writeable_reg, .precious_reg = mchp_pdmc_precious_reg, + .volatile_reg = mchp_pdmc_volatile_reg, .cache_type = REGCACHE_FLAT, }; @@ -918,6 +958,9 @@ static int mchp_pdmc_dt_init(struct mchp_pdmc *dd) dd->channel_mic_map[i].clk_edge = edge; } + dd->startup_delay_us = 150000; + of_property_read_u32(np, "microchip,startup-delay-us", &dd->startup_delay_us); + return 0; } |