Commit d1e1406c authored by Lars-Peter Clausen's avatar Lars-Peter Clausen Committed by Mark Brown

ASoC: generic-dmaengine-pcm: Add support for half-duplex

Some platforms which are half-duplex share the same DMA channel between the
playback and capture stream. Add support for this to the generic dmaengine PCM
driver.
Signed-off-by: default avatarLars-Peter Clausen <lars@metafoo.de>
Tested-by: default avatarShawn Guo <shawn.guo@linaro.org>
Signed-off-by: default avatarMark Brown <broonie@opensource.wolfsonmicro.com>
parent 22f38f79
...@@ -91,6 +91,11 @@ void snd_dmaengine_pcm_set_config_from_dai_data( ...@@ -91,6 +91,11 @@ void snd_dmaengine_pcm_set_config_from_dai_data(
* bytes that are still left to transfer. * bytes that are still left to transfer.
*/ */
#define SND_DMAENGINE_PCM_FLAG_NO_RESIDUE BIT(2) #define SND_DMAENGINE_PCM_FLAG_NO_RESIDUE BIT(2)
/*
* The PCM is half duplex and the DMA channel is shared between capture and
* playback.
*/
#define SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX BIT(3)
/** /**
* struct snd_dmaengine_pcm_config - Configuration data for dmaengine based PCM * struct snd_dmaengine_pcm_config - Configuration data for dmaengine based PCM
......
...@@ -29,7 +29,7 @@ struct dmaengine_pcm { ...@@ -29,7 +29,7 @@ struct dmaengine_pcm {
struct dma_chan *chan[SNDRV_PCM_STREAM_CAPTURE + 1]; struct dma_chan *chan[SNDRV_PCM_STREAM_CAPTURE + 1];
const struct snd_dmaengine_pcm_config *config; const struct snd_dmaengine_pcm_config *config;
struct snd_soc_platform platform; struct snd_soc_platform platform;
bool compat; unsigned int flags;
}; };
static struct dmaengine_pcm *soc_platform_to_pcm(struct snd_soc_platform *p) static struct dmaengine_pcm *soc_platform_to_pcm(struct snd_soc_platform *p)
...@@ -128,6 +128,9 @@ static struct dma_chan *dmaengine_pcm_compat_request_channel( ...@@ -128,6 +128,9 @@ static struct dma_chan *dmaengine_pcm_compat_request_channel(
{ {
struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform); struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) && pcm->chan[0])
return pcm->chan[0];
if (pcm->config->compat_request_channel) if (pcm->config->compat_request_channel)
return pcm->config->compat_request_channel(rtd, substream); return pcm->config->compat_request_channel(rtd, substream);
...@@ -148,7 +151,7 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) ...@@ -148,7 +151,7 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd)
if (!substream) if (!substream)
continue; continue;
if (!pcm->chan[i] && pcm->compat) { if (!pcm->chan[i] && (pcm->flags & SND_DMAENGINE_PCM_FLAG_COMPAT)) {
pcm->chan[i] = dmaengine_pcm_compat_request_channel(rtd, pcm->chan[i] = dmaengine_pcm_compat_request_channel(rtd,
substream); substream);
} }
...@@ -215,6 +218,25 @@ static const char * const dmaengine_pcm_dma_channel_names[] = { ...@@ -215,6 +218,25 @@ static const char * const dmaengine_pcm_dma_channel_names[] = {
[SNDRV_PCM_STREAM_CAPTURE] = "rx", [SNDRV_PCM_STREAM_CAPTURE] = "rx",
}; };
static void dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm,
struct device_node *of_node)
{
unsigned int i;
if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_DT) || !of_node)
return;
if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) {
pcm->chan[0] = of_dma_request_slave_channel(of_node, "tx_rx");
pcm->chan[1] = pcm->chan[0];
} else {
for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
pcm->chan[i] = of_dma_request_slave_channel(of_node,
dmaengine_pcm_dma_channel_names[i]);
}
}
}
/** /**
* snd_dmaengine_pcm_register - Register a dmaengine based PCM device * snd_dmaengine_pcm_register - Register a dmaengine based PCM device
* @dev: The parent device for the PCM device * @dev: The parent device for the PCM device
...@@ -225,23 +247,15 @@ int snd_dmaengine_pcm_register(struct device *dev, ...@@ -225,23 +247,15 @@ int snd_dmaengine_pcm_register(struct device *dev,
const struct snd_dmaengine_pcm_config *config, unsigned int flags) const struct snd_dmaengine_pcm_config *config, unsigned int flags)
{ {
struct dmaengine_pcm *pcm; struct dmaengine_pcm *pcm;
unsigned int i;
pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
if (!pcm) if (!pcm)
return -ENOMEM; return -ENOMEM;
pcm->config = config; pcm->config = config;
pcm->flags = flags;
if (flags & SND_DMAENGINE_PCM_FLAG_COMPAT) dmaengine_pcm_request_chan_of(pcm, dev->of_node);
pcm->compat = true;
if (!(flags & SND_DMAENGINE_PCM_FLAG_NO_DT) && dev->of_node) {
for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
pcm->chan[i] = of_dma_request_slave_channel(dev->of_node,
dmaengine_pcm_dma_channel_names[i]);
}
}
if (flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE) if (flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE)
return snd_soc_add_platform(dev, &pcm->platform, return snd_soc_add_platform(dev, &pcm->platform,
...@@ -272,8 +286,11 @@ void snd_dmaengine_pcm_unregister(struct device *dev) ...@@ -272,8 +286,11 @@ void snd_dmaengine_pcm_unregister(struct device *dev)
pcm = soc_platform_to_pcm(platform); pcm = soc_platform_to_pcm(platform);
for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) { for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
if (pcm->chan[i]) if (pcm->chan[i]) {
dma_release_channel(pcm->chan[i]); dma_release_channel(pcm->chan[i]);
if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
break;
}
} }
snd_soc_remove_platform(platform); snd_soc_remove_platform(platform);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment