xref: /linux/sound/arm/pxa2xx-pcm-lib.c (revision 58ceb57ec1be928bec2faeca11fe0752f930669d)
1a6d77317SDmitry Baryshkov /*
2a6d77317SDmitry Baryshkov  * This program is free software; you can redistribute it and/or modify
3a6d77317SDmitry Baryshkov  * it under the terms of the GNU General Public License version 2 as
4a6d77317SDmitry Baryshkov  * published by the Free Software Foundation.
5a6d77317SDmitry Baryshkov  */
6a6d77317SDmitry Baryshkov 
75a0e3ad6STejun Heo #include <linux/slab.h>
8a6d77317SDmitry Baryshkov #include <linux/module.h>
9a6d77317SDmitry Baryshkov #include <linux/dma-mapping.h>
10d65a1458SDaniel Mack #include <linux/dmaengine.h>
11*58ceb57eSDaniel Mack #include <linux/dma/pxa-dma.h>
12a6d77317SDmitry Baryshkov 
13a6d77317SDmitry Baryshkov #include <sound/core.h>
14a6d77317SDmitry Baryshkov #include <sound/pcm.h>
15a6d77317SDmitry Baryshkov #include <sound/pcm_params.h>
16a6d77317SDmitry Baryshkov #include <sound/pxa2xx-lib.h>
17d65a1458SDaniel Mack #include <sound/dmaengine_pcm.h>
18a6d77317SDmitry Baryshkov 
19a6d77317SDmitry Baryshkov #include "pxa2xx-pcm.h"
20a6d77317SDmitry Baryshkov 
21a6d77317SDmitry Baryshkov static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
22a6d77317SDmitry Baryshkov 	.info			= SNDRV_PCM_INFO_MMAP |
23a6d77317SDmitry Baryshkov 				  SNDRV_PCM_INFO_MMAP_VALID |
24a6d77317SDmitry Baryshkov 				  SNDRV_PCM_INFO_INTERLEAVED |
25a6d77317SDmitry Baryshkov 				  SNDRV_PCM_INFO_PAUSE |
26a6d77317SDmitry Baryshkov 				  SNDRV_PCM_INFO_RESUME,
27a6d77317SDmitry Baryshkov 	.formats		= SNDRV_PCM_FMTBIT_S16_LE |
28a6d77317SDmitry Baryshkov 					SNDRV_PCM_FMTBIT_S24_LE |
29a6d77317SDmitry Baryshkov 					SNDRV_PCM_FMTBIT_S32_LE,
30a6d77317SDmitry Baryshkov 	.period_bytes_min	= 32,
31a6d77317SDmitry Baryshkov 	.period_bytes_max	= 8192 - 32,
32a6d77317SDmitry Baryshkov 	.periods_min		= 1,
33*58ceb57eSDaniel Mack 	.periods_max		= 256,
34a6d77317SDmitry Baryshkov 	.buffer_bytes_max	= 128 * 1024,
35a6d77317SDmitry Baryshkov 	.fifo_size		= 32,
36a6d77317SDmitry Baryshkov };
37a6d77317SDmitry Baryshkov 
38a6d77317SDmitry Baryshkov int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
39a6d77317SDmitry Baryshkov 				struct snd_pcm_hw_params *params)
40a6d77317SDmitry Baryshkov {
41*58ceb57eSDaniel Mack 	struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
42*58ceb57eSDaniel Mack 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
43*58ceb57eSDaniel Mack 	struct snd_dmaengine_dai_dma_data *dma_params;
44*58ceb57eSDaniel Mack 	struct dma_slave_config config;
45*58ceb57eSDaniel Mack 	int ret;
46d65a1458SDaniel Mack 
47*58ceb57eSDaniel Mack 	dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
48*58ceb57eSDaniel Mack 	if (!dma_params)
49*58ceb57eSDaniel Mack 		return 0;
50d65a1458SDaniel Mack 
51*58ceb57eSDaniel Mack 	ret = snd_hwparams_to_dma_slave_config(substream, params, &config);
52*58ceb57eSDaniel Mack 	if (ret)
53*58ceb57eSDaniel Mack 		return ret;
54*58ceb57eSDaniel Mack 
55*58ceb57eSDaniel Mack 	snd_dmaengine_pcm_set_config_from_dai_data(substream,
56*58ceb57eSDaniel Mack 			snd_soc_dai_get_dma_data(rtd->cpu_dai, substream),
57*58ceb57eSDaniel Mack 			&config);
58*58ceb57eSDaniel Mack 
59*58ceb57eSDaniel Mack 	ret = dmaengine_slave_config(chan, &config);
60*58ceb57eSDaniel Mack 	if (ret)
61*58ceb57eSDaniel Mack 		return ret;
62a6d77317SDmitry Baryshkov 
63a6d77317SDmitry Baryshkov 	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
64a6d77317SDmitry Baryshkov 
65a6d77317SDmitry Baryshkov 	return 0;
66a6d77317SDmitry Baryshkov }
67a6d77317SDmitry Baryshkov EXPORT_SYMBOL(__pxa2xx_pcm_hw_params);
68a6d77317SDmitry Baryshkov 
69a6d77317SDmitry Baryshkov int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream)
70a6d77317SDmitry Baryshkov {
71a6d77317SDmitry Baryshkov 	snd_pcm_set_runtime_buffer(substream, NULL);
72a6d77317SDmitry Baryshkov 	return 0;
73a6d77317SDmitry Baryshkov }
74a6d77317SDmitry Baryshkov EXPORT_SYMBOL(__pxa2xx_pcm_hw_free);
75a6d77317SDmitry Baryshkov 
76a6d77317SDmitry Baryshkov int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
77a6d77317SDmitry Baryshkov {
78*58ceb57eSDaniel Mack 	return snd_dmaengine_pcm_trigger(substream, cmd);
79a6d77317SDmitry Baryshkov }
80a6d77317SDmitry Baryshkov EXPORT_SYMBOL(pxa2xx_pcm_trigger);
81a6d77317SDmitry Baryshkov 
82a6d77317SDmitry Baryshkov snd_pcm_uframes_t
83a6d77317SDmitry Baryshkov pxa2xx_pcm_pointer(struct snd_pcm_substream *substream)
84a6d77317SDmitry Baryshkov {
85*58ceb57eSDaniel Mack 	return snd_dmaengine_pcm_pointer(substream);
86a6d77317SDmitry Baryshkov }
87a6d77317SDmitry Baryshkov EXPORT_SYMBOL(pxa2xx_pcm_pointer);
88a6d77317SDmitry Baryshkov 
89a6d77317SDmitry Baryshkov int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream)
90a6d77317SDmitry Baryshkov {
91a6d77317SDmitry Baryshkov 	return 0;
92a6d77317SDmitry Baryshkov }
93a6d77317SDmitry Baryshkov EXPORT_SYMBOL(__pxa2xx_pcm_prepare);
94a6d77317SDmitry Baryshkov 
95a6d77317SDmitry Baryshkov int __pxa2xx_pcm_open(struct snd_pcm_substream *substream)
96a6d77317SDmitry Baryshkov {
97*58ceb57eSDaniel Mack 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
98a6d77317SDmitry Baryshkov 	struct snd_pcm_runtime *runtime = substream->runtime;
99*58ceb57eSDaniel Mack 	struct snd_dmaengine_dai_dma_data *dma_params;
100a6d77317SDmitry Baryshkov 	int ret;
101a6d77317SDmitry Baryshkov 
102a6d77317SDmitry Baryshkov 	runtime->hw = pxa2xx_pcm_hardware;
103a6d77317SDmitry Baryshkov 
104*58ceb57eSDaniel Mack 	dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
105*58ceb57eSDaniel Mack 	if (!dma_params)
106*58ceb57eSDaniel Mack 		return 0;
107*58ceb57eSDaniel Mack 
108a6d77317SDmitry Baryshkov 	/*
109a6d77317SDmitry Baryshkov 	 * For mysterious reasons (and despite what the manual says)
110a6d77317SDmitry Baryshkov 	 * playback samples are lost if the DMA count is not a multiple
111a6d77317SDmitry Baryshkov 	 * of the DMA burst size.  Let's add a rule to enforce that.
112a6d77317SDmitry Baryshkov 	 */
113a6d77317SDmitry Baryshkov 	ret = snd_pcm_hw_constraint_step(runtime, 0,
114a6d77317SDmitry Baryshkov 		SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
115a6d77317SDmitry Baryshkov 	if (ret)
116*58ceb57eSDaniel Mack 		return ret;
117a6d77317SDmitry Baryshkov 
118a6d77317SDmitry Baryshkov 	ret = snd_pcm_hw_constraint_step(runtime, 0,
119a6d77317SDmitry Baryshkov 		SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
120a6d77317SDmitry Baryshkov 	if (ret)
121*58ceb57eSDaniel Mack 		return ret;
122a6d77317SDmitry Baryshkov 
123a6d77317SDmitry Baryshkov 	ret = snd_pcm_hw_constraint_integer(runtime,
124a6d77317SDmitry Baryshkov 					    SNDRV_PCM_HW_PARAM_PERIODS);
125a6d77317SDmitry Baryshkov 	if (ret < 0)
126a6d77317SDmitry Baryshkov 		return ret;
127*58ceb57eSDaniel Mack 
128*58ceb57eSDaniel Mack 	return snd_dmaengine_pcm_open_request_chan(substream,
129*58ceb57eSDaniel Mack 					pxad_filter_fn,
130*58ceb57eSDaniel Mack 					dma_params->filter_data);
131a6d77317SDmitry Baryshkov }
132a6d77317SDmitry Baryshkov EXPORT_SYMBOL(__pxa2xx_pcm_open);
133a6d77317SDmitry Baryshkov 
134a6d77317SDmitry Baryshkov int __pxa2xx_pcm_close(struct snd_pcm_substream *substream)
135a6d77317SDmitry Baryshkov {
136*58ceb57eSDaniel Mack 	return snd_dmaengine_pcm_close_release_chan(substream);
137a6d77317SDmitry Baryshkov }
138a6d77317SDmitry Baryshkov EXPORT_SYMBOL(__pxa2xx_pcm_close);
139a6d77317SDmitry Baryshkov 
140a6d77317SDmitry Baryshkov int pxa2xx_pcm_mmap(struct snd_pcm_substream *substream,
141a6d77317SDmitry Baryshkov 	struct vm_area_struct *vma)
142a6d77317SDmitry Baryshkov {
143a6d77317SDmitry Baryshkov 	struct snd_pcm_runtime *runtime = substream->runtime;
144a6d77317SDmitry Baryshkov 	return dma_mmap_writecombine(substream->pcm->card->dev, vma,
145a6d77317SDmitry Baryshkov 				     runtime->dma_area,
146a6d77317SDmitry Baryshkov 				     runtime->dma_addr,
147a6d77317SDmitry Baryshkov 				     runtime->dma_bytes);
148a6d77317SDmitry Baryshkov }
149a6d77317SDmitry Baryshkov EXPORT_SYMBOL(pxa2xx_pcm_mmap);
150a6d77317SDmitry Baryshkov 
151a6d77317SDmitry Baryshkov int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
152a6d77317SDmitry Baryshkov {
153a6d77317SDmitry Baryshkov 	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
154a6d77317SDmitry Baryshkov 	struct snd_dma_buffer *buf = &substream->dma_buffer;
155a6d77317SDmitry Baryshkov 	size_t size = pxa2xx_pcm_hardware.buffer_bytes_max;
156a6d77317SDmitry Baryshkov 	buf->dev.type = SNDRV_DMA_TYPE_DEV;
157a6d77317SDmitry Baryshkov 	buf->dev.dev = pcm->card->dev;
158a6d77317SDmitry Baryshkov 	buf->private_data = NULL;
159a6d77317SDmitry Baryshkov 	buf->area = dma_alloc_writecombine(pcm->card->dev, size,
160a6d77317SDmitry Baryshkov 					   &buf->addr, GFP_KERNEL);
161a6d77317SDmitry Baryshkov 	if (!buf->area)
162a6d77317SDmitry Baryshkov 		return -ENOMEM;
163a6d77317SDmitry Baryshkov 	buf->bytes = size;
164a6d77317SDmitry Baryshkov 	return 0;
165a6d77317SDmitry Baryshkov }
166a6d77317SDmitry Baryshkov EXPORT_SYMBOL(pxa2xx_pcm_preallocate_dma_buffer);
167a6d77317SDmitry Baryshkov 
168a6d77317SDmitry Baryshkov void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
169a6d77317SDmitry Baryshkov {
170a6d77317SDmitry Baryshkov 	struct snd_pcm_substream *substream;
171a6d77317SDmitry Baryshkov 	struct snd_dma_buffer *buf;
172a6d77317SDmitry Baryshkov 	int stream;
173a6d77317SDmitry Baryshkov 
174a6d77317SDmitry Baryshkov 	for (stream = 0; stream < 2; stream++) {
175a6d77317SDmitry Baryshkov 		substream = pcm->streams[stream].substream;
176a6d77317SDmitry Baryshkov 		if (!substream)
177a6d77317SDmitry Baryshkov 			continue;
178a6d77317SDmitry Baryshkov 		buf = &substream->dma_buffer;
179a6d77317SDmitry Baryshkov 		if (!buf->area)
180a6d77317SDmitry Baryshkov 			continue;
181a6d77317SDmitry Baryshkov 		dma_free_writecombine(pcm->card->dev, buf->bytes,
182a6d77317SDmitry Baryshkov 				      buf->area, buf->addr);
183a6d77317SDmitry Baryshkov 		buf->area = NULL;
184a6d77317SDmitry Baryshkov 	}
185a6d77317SDmitry Baryshkov }
186a6d77317SDmitry Baryshkov EXPORT_SYMBOL(pxa2xx_pcm_free_dma_buffers);
187a6d77317SDmitry Baryshkov 
188a6d77317SDmitry Baryshkov MODULE_AUTHOR("Nicolas Pitre");
189a6d77317SDmitry Baryshkov MODULE_DESCRIPTION("Intel PXA2xx sound library");
190a6d77317SDmitry Baryshkov MODULE_LICENSE("GPL");
191