aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorLars-Peter Clausen <lars@metafoo.de>2013-04-20 13:29:00 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2013-04-22 06:27:04 -0400
commitd1e1406c6ed0b92200a7de2a09fbab65661dba3c (patch)
treeb29e9b0f9c068b97e3318a5dbafa0c75cf710399 /sound
parent22f38f792ec53e2a93be13ecb609bbe911ed8ff9 (diff)
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>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/soc-generic-dmaengine-pcm.c43
1 files changed, 30 insertions, 13 deletions
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 {
29 struct dma_chan *chan[SNDRV_PCM_STREAM_CAPTURE + 1]; 29 struct dma_chan *chan[SNDRV_PCM_STREAM_CAPTURE + 1];
30 const struct snd_dmaengine_pcm_config *config; 30 const struct snd_dmaengine_pcm_config *config;
31 struct snd_soc_platform platform; 31 struct snd_soc_platform platform;
32 bool compat; 32 unsigned int flags;
33}; 33};
34 34
35static struct dmaengine_pcm *soc_platform_to_pcm(struct snd_soc_platform *p) 35static 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{ 128{
129 struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform); 129 struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
130 130
131 if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) && pcm->chan[0])
132 return pcm->chan[0];
133
131 if (pcm->config->compat_request_channel) 134 if (pcm->config->compat_request_channel)
132 return pcm->config->compat_request_channel(rtd, substream); 135 return pcm->config->compat_request_channel(rtd, substream);
133 136
@@ -148,7 +151,7 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd)
148 if (!substream) 151 if (!substream)
149 continue; 152 continue;
150 153
151 if (!pcm->chan[i] && pcm->compat) { 154 if (!pcm->chan[i] && (pcm->flags & SND_DMAENGINE_PCM_FLAG_COMPAT)) {
152 pcm->chan[i] = dmaengine_pcm_compat_request_channel(rtd, 155 pcm->chan[i] = dmaengine_pcm_compat_request_channel(rtd,
153 substream); 156 substream);
154 } 157 }
@@ -215,6 +218,25 @@ static const char * const dmaengine_pcm_dma_channel_names[] = {
215 [SNDRV_PCM_STREAM_CAPTURE] = "rx", 218 [SNDRV_PCM_STREAM_CAPTURE] = "rx",
216}; 219};
217 220
221static void dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm,
222 struct device_node *of_node)
223{
224 unsigned int i;
225
226 if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_DT) || !of_node)
227 return;
228
229 if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) {
230 pcm->chan[0] = of_dma_request_slave_channel(of_node, "tx_rx");
231 pcm->chan[1] = pcm->chan[0];
232 } else {
233 for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
234 pcm->chan[i] = of_dma_request_slave_channel(of_node,
235 dmaengine_pcm_dma_channel_names[i]);
236 }
237 }
238}
239
218/** 240/**
219 * snd_dmaengine_pcm_register - Register a dmaengine based PCM device 241 * snd_dmaengine_pcm_register - Register a dmaengine based PCM device
220 * @dev: The parent device for the PCM device 242 * @dev: The parent device for the PCM device
@@ -225,23 +247,15 @@ int snd_dmaengine_pcm_register(struct device *dev,
225 const struct snd_dmaengine_pcm_config *config, unsigned int flags) 247 const struct snd_dmaengine_pcm_config *config, unsigned int flags)
226{ 248{
227 struct dmaengine_pcm *pcm; 249 struct dmaengine_pcm *pcm;
228 unsigned int i;
229 250
230 pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); 251 pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
231 if (!pcm) 252 if (!pcm)
232 return -ENOMEM; 253 return -ENOMEM;
233 254
234 pcm->config = config; 255 pcm->config = config;
256 pcm->flags = flags;
235 257
236 if (flags & SND_DMAENGINE_PCM_FLAG_COMPAT) 258 dmaengine_pcm_request_chan_of(pcm, dev->of_node);
237 pcm->compat = true;
238
239 if (!(flags & SND_DMAENGINE_PCM_FLAG_NO_DT) && dev->of_node) {
240 for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
241 pcm->chan[i] = of_dma_request_slave_channel(dev->of_node,
242 dmaengine_pcm_dma_channel_names[i]);
243 }
244 }
245 259
246 if (flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE) 260 if (flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE)
247 return snd_soc_add_platform(dev, &pcm->platform, 261 return snd_soc_add_platform(dev, &pcm->platform,
@@ -272,8 +286,11 @@ void snd_dmaengine_pcm_unregister(struct device *dev)
272 pcm = soc_platform_to_pcm(platform); 286 pcm = soc_platform_to_pcm(platform);
273 287
274 for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) { 288 for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
275 if (pcm->chan[i]) 289 if (pcm->chan[i]) {
276 dma_release_channel(pcm->chan[i]); 290 dma_release_channel(pcm->chan[i]);
291 if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
292 break;
293 }
277 } 294 }
278 295
279 snd_soc_remove_platform(platform); 296 snd_soc_remove_platform(platform);