summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars-Peter Clausen <lars@metafoo.de>2013-04-20 21:29:00 +0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2013-04-22 14:27:04 +0400
commitd1e1406c6ed0b92200a7de2a09fbab65661dba3c (patch)
treeb29e9b0f9c068b97e3318a5dbafa0c75cf710399
parent22f38f792ec53e2a93be13ecb609bbe911ed8ff9 (diff)
downloadlinux-d1e1406c6ed0b92200a7de2a09fbab65661dba3c.tar.xz
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: Lars-Peter Clausen <lars@metafoo.de> Tested-by: Shawn Guo <shawn.guo@linaro.org> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
-rw-r--r--include/sound/dmaengine_pcm.h5
-rw-r--r--sound/soc/soc-generic-dmaengine-pcm.c43
2 files changed, 35 insertions, 13 deletions
diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h
index b1d1150c1d60..f11c35cd5532 100644
--- a/include/sound/dmaengine_pcm.h
+++ b/include/sound/dmaengine_pcm.h
@@ -91,6 +91,11 @@ void snd_dmaengine_pcm_set_config_from_dai_data(
* bytes that are still left to transfer.
*/
#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
diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c
index ae0c37e66ae0..5fd5ed4c0a96 100644
--- a/sound/soc/soc-generic-dmaengine-pcm.c
+++ b/sound/soc/soc-generic-dmaengine-pcm.c
@@ -29,7 +29,7 @@ struct dmaengine_pcm {
struct dma_chan *chan[SNDRV_PCM_STREAM_CAPTURE + 1];
const struct snd_dmaengine_pcm_config *config;
struct snd_soc_platform platform;
- bool compat;
+ unsigned int flags;
};
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(
{
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)
return pcm->config->compat_request_channel(rtd, substream);
@@ -148,7 +151,7 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd)
if (!substream)
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,
substream);
}
@@ -215,6 +218,25 @@ static const char * const dmaengine_pcm_dma_channel_names[] = {
[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
* @dev: The parent device for the PCM device
@@ -225,23 +247,15 @@ int snd_dmaengine_pcm_register(struct device *dev,
const struct snd_dmaengine_pcm_config *config, unsigned int flags)
{
struct dmaengine_pcm *pcm;
- unsigned int i;
pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
if (!pcm)
return -ENOMEM;
pcm->config = config;
+ pcm->flags = flags;
- if (flags & SND_DMAENGINE_PCM_FLAG_COMPAT)
- 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]);
- }
- }
+ dmaengine_pcm_request_chan_of(pcm, dev->of_node);
if (flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE)
return snd_soc_add_platform(dev, &pcm->platform,
@@ -272,8 +286,11 @@ void snd_dmaengine_pcm_unregister(struct device *dev)
pcm = soc_platform_to_pcm(platform);
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]);
+ if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
+ break;
+ }
}
snd_soc_remove_platform(platform);