diff options
author | Lars-Peter Clausen <lars@metafoo.de> | 2013-04-20 13:29:00 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2013-04-22 06:27:04 -0400 |
commit | d1e1406c6ed0b92200a7de2a09fbab65661dba3c (patch) | |
tree | b29e9b0f9c068b97e3318a5dbafa0c75cf710399 /sound | |
parent | 22f38f792ec53e2a93be13ecb609bbe911ed8ff9 (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.c | 43 |
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 | ||
35 | static struct dmaengine_pcm *soc_platform_to_pcm(struct snd_soc_platform *p) | 35 | 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 | { | 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 | ||
221 | static 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); |