diff options
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/atmel/mchp-pdmc.c | 53 | ||||
-rw-r--r-- | sound/soc/soc-pcm.c | 27 |
2 files changed, 70 insertions, 10 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; } diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 005b179a770a..5eb056b942ce 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1088,22 +1088,39 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - int ret = -EINVAL, _ret = 0; + struct snd_soc_component *component; + int ret = -EINVAL, _ret = 0, start_dma_last = 0, i; int rollback = 0; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + /* Do we need to start dma last? */ + for_each_rtd_components(rtd, i, component) { + if (component->driver->start_dma_last) { + start_dma_last = 1; + break; + } + } + ret = snd_soc_link_trigger(substream, cmd, 0); if (ret < 0) goto start_err; - ret = snd_soc_pcm_component_trigger(substream, cmd, 0); - if (ret < 0) - goto start_err; + if (start_dma_last) { + ret = snd_soc_pcm_dai_trigger(substream, cmd, 0); + if (ret < 0) + goto start_err; + + ret = snd_soc_pcm_component_trigger(substream, cmd, 0); + } else { + ret = snd_soc_pcm_component_trigger(substream, cmd, 0); + if (ret < 0) + goto start_err; - ret = snd_soc_pcm_dai_trigger(substream, cmd, 0); + ret = snd_soc_pcm_dai_trigger(substream, cmd, 0); + } start_err: if (ret < 0) rollback = 1; |