Skip to content

Commit 11d4e47

Browse files
committed
ASoC: mchp-pdmc: fix poc noises when starting
Merge series from Claudiu Beznea <claudiu.beznea@microchip.com>: To start capture on Microchip PDMC the enable bits for each supported microphone need to be set. After this bit is set the PDMC starts to receive data from microphones and it considers this data as valid data. Thus if microphones are not ready the PDMC captures anyway data from its lines. This data is interpreted by the human ear as poc noises. To avoid this the following software workaround need to be applied when starting capture: 1/ enable PDMC channel 2/ wait 150ms 3/ execute 16 dummy reads from RHR 4/ clear interrupts 5/ enable interrupts 6/ enable DMA channel For this workaround to work step 6 need to be executed at the end. For step 6 was added patch 1/3 from this series. With this, component DAI driver sets its struct snd_soc_component_driver::start_dma_last = 1 and proper action is taken based on this flag when starting DAI trigger vs DMA.
2 parents b201929 + c5682e2 commit 11d4e47

4 files changed

Lines changed: 78 additions & 10 deletions

File tree

Documentation/devicetree/bindings/sound/microchip,sama7g5-pdmc.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ properties:
6767
maxItems: 4
6868
uniqueItems: true
6969

70+
microchip,startup-delay-us:
71+
description: |
72+
Specifies the delay in microseconds that needs to be applied after
73+
enabling the PDMC microphones to avoid unwanted noise due to microphones
74+
not being ready.
75+
7076
required:
7177
- compatible
7278
- reg

include/sound/soc-component.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,8 @@ struct snd_soc_component_driver {
190190
bool use_dai_pcm_id; /* use DAI link PCM ID as PCM device number */
191191
int be_pcm_base; /* base device ID for all BE PCMs */
192192

193+
unsigned int start_dma_last;
194+
193195
#ifdef CONFIG_DEBUG_FS
194196
const char *debugfs_prefix;
195197
#endif

sound/soc/atmel/mchp-pdmc.c

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ struct mchp_pdmc {
114114
struct clk *gclk;
115115
u32 pdmcen;
116116
u32 suspend_irq;
117+
u32 startup_delay_us;
117118
int mic_no;
118119
int sinc_order;
119120
bool audio_filter_en;
@@ -425,6 +426,7 @@ static const struct snd_soc_component_driver mchp_pdmc_dai_component = {
425426
.open = &mchp_pdmc_open,
426427
.close = &mchp_pdmc_close,
427428
.legacy_dai_naming = 1,
429+
.start_dma_last = 1,
428430
};
429431

430432
static const unsigned int mchp_pdmc_1mic[] = {1};
@@ -632,6 +634,29 @@ static int mchp_pdmc_hw_params(struct snd_pcm_substream *substream,
632634
return 0;
633635
}
634636

637+
static void mchp_pdmc_noise_filter_workaround(struct mchp_pdmc *dd)
638+
{
639+
u32 tmp, steps = 16;
640+
641+
/*
642+
* PDMC doesn't wait for microphones' startup time thus the acquisition
643+
* may start before the microphones are ready leading to poc noises at
644+
* the beginning of capture. To avoid this, we need to wait 50ms (in
645+
* normal startup procedure) or 150 ms (worst case after resume from sleep
646+
* states) after microphones are enabled and then clear the FIFOs (by
647+
* reading the RHR 16 times) and possible interrupts before continuing.
648+
* Also, for this to work the DMA needs to be started after interrupts
649+
* are enabled.
650+
*/
651+
usleep_range(dd->startup_delay_us, dd->startup_delay_us + 5);
652+
653+
while (steps--)
654+
regmap_read(dd->regmap, MCHP_PDMC_RHR, &tmp);
655+
656+
/* Clear interrupts. */
657+
regmap_read(dd->regmap, MCHP_PDMC_ISR, &tmp);
658+
}
659+
635660
static int mchp_pdmc_trigger(struct snd_pcm_substream *substream,
636661
int cmd, struct snd_soc_dai *dai)
637662
{
@@ -644,15 +669,17 @@ static int mchp_pdmc_trigger(struct snd_pcm_substream *substream,
644669
switch (cmd) {
645670
case SNDRV_PCM_TRIGGER_RESUME:
646671
case SNDRV_PCM_TRIGGER_START:
647-
/* Enable overrun and underrun error interrupts */
648-
regmap_write(dd->regmap, MCHP_PDMC_IER, dd->suspend_irq |
649-
MCHP_PDMC_IR_RXOVR | MCHP_PDMC_IR_RXUDR);
650-
dd->suspend_irq = 0;
651-
fallthrough;
652672
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
653673
snd_soc_component_update_bits(cpu, MCHP_PDMC_MR,
654674
MCHP_PDMC_MR_PDMCEN_MASK,
655675
dd->pdmcen);
676+
677+
mchp_pdmc_noise_filter_workaround(dd);
678+
679+
/* Enable interrupts. */
680+
regmap_write(dd->regmap, MCHP_PDMC_IER, dd->suspend_irq |
681+
MCHP_PDMC_IR_RXOVR | MCHP_PDMC_IR_RXUDR);
682+
dd->suspend_irq = 0;
656683
break;
657684
case SNDRV_PCM_TRIGGER_SUSPEND:
658685
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)
796823
case MCHP_PDMC_CFGR:
797824
case MCHP_PDMC_IMR:
798825
case MCHP_PDMC_ISR:
826+
case MCHP_PDMC_RHR:
799827
case MCHP_PDMC_VER:
800828
return true;
801829
default:
@@ -817,6 +845,17 @@ static bool mchp_pdmc_writeable_reg(struct device *dev, unsigned int reg)
817845
}
818846
}
819847

848+
static bool mchp_pdmc_volatile_reg(struct device *dev, unsigned int reg)
849+
{
850+
switch (reg) {
851+
case MCHP_PDMC_ISR:
852+
case MCHP_PDMC_RHR:
853+
return true;
854+
default:
855+
return false;
856+
}
857+
}
858+
820859
static bool mchp_pdmc_precious_reg(struct device *dev, unsigned int reg)
821860
{
822861
switch (reg) {
@@ -836,6 +875,7 @@ static const struct regmap_config mchp_pdmc_regmap_config = {
836875
.readable_reg = mchp_pdmc_readable_reg,
837876
.writeable_reg = mchp_pdmc_writeable_reg,
838877
.precious_reg = mchp_pdmc_precious_reg,
878+
.volatile_reg = mchp_pdmc_volatile_reg,
839879
.cache_type = REGCACHE_FLAT,
840880
};
841881

@@ -918,6 +958,9 @@ static int mchp_pdmc_dt_init(struct mchp_pdmc *dd)
918958
dd->channel_mic_map[i].clk_edge = edge;
919959
}
920960

961+
dd->startup_delay_us = 150000;
962+
of_property_read_u32(np, "microchip,startup-delay-us", &dd->startup_delay_us);
963+
921964
return 0;
922965
}
923966

sound/soc/soc-pcm.c

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,22 +1088,39 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
10881088
static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
10891089
{
10901090
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
1091-
int ret = -EINVAL, _ret = 0;
1091+
struct snd_soc_component *component;
1092+
int ret = -EINVAL, _ret = 0, start_dma_last = 0, i;
10921093
int rollback = 0;
10931094

10941095
switch (cmd) {
10951096
case SNDRV_PCM_TRIGGER_START:
10961097
case SNDRV_PCM_TRIGGER_RESUME:
10971098
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
1099+
/* Do we need to start dma last? */
1100+
for_each_rtd_components(rtd, i, component) {
1101+
if (component->driver->start_dma_last) {
1102+
start_dma_last = 1;
1103+
break;
1104+
}
1105+
}
1106+
10981107
ret = snd_soc_link_trigger(substream, cmd, 0);
10991108
if (ret < 0)
11001109
goto start_err;
11011110

1102-
ret = snd_soc_pcm_component_trigger(substream, cmd, 0);
1103-
if (ret < 0)
1104-
goto start_err;
1111+
if (start_dma_last) {
1112+
ret = snd_soc_pcm_dai_trigger(substream, cmd, 0);
1113+
if (ret < 0)
1114+
goto start_err;
1115+
1116+
ret = snd_soc_pcm_component_trigger(substream, cmd, 0);
1117+
} else {
1118+
ret = snd_soc_pcm_component_trigger(substream, cmd, 0);
1119+
if (ret < 0)
1120+
goto start_err;
11051121

1106-
ret = snd_soc_pcm_dai_trigger(substream, cmd, 0);
1122+
ret = snd_soc_pcm_dai_trigger(substream, cmd, 0);
1123+
}
11071124
start_err:
11081125
if (ret < 0)
11091126
rollback = 1;

0 commit comments

Comments
 (0)