Skip to content

Commit

Permalink
ASoC: mchp-pdmc: fix poc noise at capture startup
Browse files Browse the repository at this point in the history
Microchip PDMC IP doesn't filter microphone noises on startup. By default,
it captures data received from digital microphones after
the MCHP_PDMC_MR.EN bits are set. Thus when enable is set on PDMC side the
digital microphones might not be ready yet and PDMC captures data from then
in this time. This data captured is poc noise. To avoid this the software
workaround is to the following:
1/ enable PDMC channel
2/ wait 150ms (on SAMA7G5-EK setup)
3/ execute 16 dummy reads from RHR
4/ clear interrupts
5/ enable interrupts
6/ enable DMA channel

Fixes: 5029165 ("ASoC: atmel: mchp-pdmc: add PDMC driver")
Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
Link: https://lore.kernel.org/r/20230228110145.3770525-4-claudiu.beznea@microchip.com
Signed-off-by: Mark Brown <broonie@kernel.org>
  • Loading branch information
Claudiu Beznea authored and Mark Brown committed Feb 28, 2023
1 parent 143a2f0 commit c5682e2
Showing 1 changed file with 48 additions and 5 deletions.
53 changes: 48 additions & 5 deletions sound/soc/atmel/mchp-pdmc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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};
Expand Down Expand Up @@ -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)
{
Expand All @@ -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);
Expand Down Expand Up @@ -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:
Expand All @@ -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) {
Expand All @@ -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,
};

Expand Down Expand Up @@ -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;
}

Expand Down

0 comments on commit c5682e2

Please sign in to comment.