From: Peter Ujfalusi Date: Wed, 20 Mar 2013 09:47:13 +0000 (+0100) Subject: ASoC: omap-mcpdm: Fix for full duplex audio use case X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=81054b226b76145628670a962674ab312690ab86;p=linux-beck.git ASoC: omap-mcpdm: Fix for full duplex audio use case Due to HW limitation within OMAP McPDM IP uplink and downlink need to be started at the same time. This causes issues when we have two streams running, for example: arecord | aplay In this case the playback stream would have no channels enabled since at the capture start we are not aware that a playback is going to start. The workaround is to configure the other direction to stereo when the first stream is started. When the second stream is coming we check the new stream's number of channels against the pre-configured channels. If it does not match we stop and restart McPDM to update the configuration. This might result a small pop. If the coming stream is a match we do nothing in the McPDM driver. This workaround can handle most use cases without the need to restart McPDM. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/omap/omap-mcpdm.c index 6e19f440a28c..cd0e2ec90128 100644 --- a/sound/soc/omap/omap-mcpdm.c +++ b/sound/soc/omap/omap-mcpdm.c @@ -63,6 +63,9 @@ struct omap_mcpdm { /* McPDM dn offsets for rx1, and 2 channels */ u32 dn_rx_offset; + + /* McPDM needs to be restarted due to runtime reconfiguration */ + bool restart; }; /* @@ -149,7 +152,7 @@ static void omap_mcpdm_start(struct omap_mcpdm *mcpdm) static void omap_mcpdm_stop(struct omap_mcpdm *mcpdm) { u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL); - u32 link_mask = mcpdm->config[0].link_mask | mcpdm->config[1].link_mask; + u32 link_mask = MCPDM_PDM_DN_MASK | MCPDM_PDM_UP_MASK; ctrl |= (MCPDM_SW_DN_RST | MCPDM_SW_UP_RST); omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl); @@ -287,6 +290,8 @@ static void omap_mcpdm_dai_shutdown(struct snd_pcm_substream *substream, if (omap_mcpdm_active(mcpdm)) { omap_mcpdm_stop(mcpdm); omap_mcpdm_close_streams(mcpdm); + mcpdm->config[0].link_mask = 0; + mcpdm->config[1].link_mask = 0; } } @@ -334,11 +339,26 @@ static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream, /* Configure McPDM channels, and DMA packet size */ if (stream == SNDRV_PCM_STREAM_PLAYBACK) { link_mask <<= 3; + + /* If capture is not running assume a stereo stream to come */ + if (!mcpdm->config[!stream].link_mask) + mcpdm->config[!stream].link_mask = 0x3; + dma_data->packet_size = (MCPDM_DN_THRES_MAX - threshold) * channels; } else { + /* If playback is not running assume a stereo stream to come */ + if (!mcpdm->config[!stream].link_mask) + mcpdm->config[!stream].link_mask = (0x3 << 3); + dma_data->packet_size = threshold * channels; } + + /* Check if we need to restart McPDM with this stream */ + if (mcpdm->config[stream].link_mask && + mcpdm->config[stream].link_mask != link_mask) + mcpdm->restart = true; + mcpdm->config[stream].link_mask = link_mask; return 0; @@ -352,6 +372,11 @@ static int omap_mcpdm_prepare(struct snd_pcm_substream *substream, if (!omap_mcpdm_active(mcpdm)) { omap_mcpdm_start(mcpdm); omap_mcpdm_reg_dump(mcpdm); + } else if (mcpdm->restart) { + omap_mcpdm_stop(mcpdm); + omap_mcpdm_start(mcpdm); + mcpdm->restart = false; + omap_mcpdm_reg_dump(mcpdm); } return 0;