aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/omap/omap-pcm.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/omap/omap-pcm.c')
-rw-r--r--sound/soc/omap/omap-pcm.c269
1 files changed, 59 insertions, 210 deletions
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c
index 74da4b7209d0..a2636f6b8362 100644
--- a/sound/soc/omap/omap-pcm.c
+++ b/sound/soc/omap/omap-pcm.c
@@ -25,12 +25,13 @@
25#include <linux/dma-mapping.h> 25#include <linux/dma-mapping.h>
26#include <linux/slab.h> 26#include <linux/slab.h>
27#include <linux/module.h> 27#include <linux/module.h>
28#include <linux/omap-dma.h>
28#include <sound/core.h> 29#include <sound/core.h>
29#include <sound/pcm.h> 30#include <sound/pcm.h>
30#include <sound/pcm_params.h> 31#include <sound/pcm_params.h>
32#include <sound/dmaengine_pcm.h>
31#include <sound/soc.h> 33#include <sound/soc.h>
32 34
33#include <plat/dma.h>
34#include "omap-pcm.h" 35#include "omap-pcm.h"
35 36
36static const struct snd_pcm_hardware omap_pcm_hardware = { 37static const struct snd_pcm_hardware omap_pcm_hardware = {
@@ -49,61 +50,34 @@ static const struct snd_pcm_hardware omap_pcm_hardware = {
49 .buffer_bytes_max = 128 * 1024, 50 .buffer_bytes_max = 128 * 1024,
50}; 51};
51 52
52struct omap_runtime_data { 53static int omap_pcm_get_dma_buswidth(int num_bits)
53 spinlock_t lock;
54 struct omap_pcm_dma_data *dma_data;
55 int dma_ch;
56 int period_index;
57};
58
59static void omap_pcm_dma_irq(int ch, u16 stat, void *data)
60{ 54{
61 struct snd_pcm_substream *substream = data; 55 int buswidth;
62 struct snd_pcm_runtime *runtime = substream->runtime;
63 struct omap_runtime_data *prtd = runtime->private_data;
64 unsigned long flags;
65
66 if ((cpu_is_omap1510())) {
67 /*
68 * OMAP1510 doesn't fully support DMA progress counter
69 * and there is no software emulation implemented yet,
70 * so have to maintain our own progress counters
71 * that can be used by omap_pcm_pointer() instead.
72 */
73 spin_lock_irqsave(&prtd->lock, flags);
74 if ((stat == OMAP_DMA_LAST_IRQ) &&
75 (prtd->period_index == runtime->periods - 1)) {
76 /* we are in sync, do nothing */
77 spin_unlock_irqrestore(&prtd->lock, flags);
78 return;
79 }
80 if (prtd->period_index >= 0) {
81 if (stat & OMAP_DMA_BLOCK_IRQ) {
82 /* end of buffer reached, loop back */
83 prtd->period_index = 0;
84 } else if (stat & OMAP_DMA_LAST_IRQ) {
85 /* update the counter for the last period */
86 prtd->period_index = runtime->periods - 1;
87 } else if (++prtd->period_index >= runtime->periods) {
88 /* end of buffer missed? loop back */
89 prtd->period_index = 0;
90 }
91 }
92 spin_unlock_irqrestore(&prtd->lock, flags);
93 }
94 56
95 snd_pcm_period_elapsed(substream); 57 switch (num_bits) {
58 case 16:
59 buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
60 break;
61 case 32:
62 buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
63 break;
64 default:
65 buswidth = -EINVAL;
66 break;
67 }
68 return buswidth;
96} 69}
97 70
71
98/* this may get called several times by oss emulation */ 72/* this may get called several times by oss emulation */
99static int omap_pcm_hw_params(struct snd_pcm_substream *substream, 73static int omap_pcm_hw_params(struct snd_pcm_substream *substream,
100 struct snd_pcm_hw_params *params) 74 struct snd_pcm_hw_params *params)
101{ 75{
102 struct snd_pcm_runtime *runtime = substream->runtime; 76 struct snd_pcm_runtime *runtime = substream->runtime;
103 struct snd_soc_pcm_runtime *rtd = substream->private_data; 77 struct snd_soc_pcm_runtime *rtd = substream->private_data;
104 struct omap_runtime_data *prtd = runtime->private_data;
105 struct omap_pcm_dma_data *dma_data; 78 struct omap_pcm_dma_data *dma_data;
106 79 struct dma_slave_config config;
80 struct dma_chan *chan;
107 int err = 0; 81 int err = 0;
108 82
109 dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); 83 dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
@@ -116,195 +90,78 @@ static int omap_pcm_hw_params(struct snd_pcm_substream *substream,
116 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); 90 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
117 runtime->dma_bytes = params_buffer_bytes(params); 91 runtime->dma_bytes = params_buffer_bytes(params);
118 92
119 if (prtd->dma_data) 93 chan = snd_dmaengine_pcm_get_chan(substream);
120 return 0; 94 if (!chan)
121 prtd->dma_data = dma_data; 95 return -EINVAL;
122 err = omap_request_dma(dma_data->dma_req, dma_data->name,
123 omap_pcm_dma_irq, substream, &prtd->dma_ch);
124 if (!err) {
125 /*
126 * Link channel with itself so DMA doesn't need any
127 * reprogramming while looping the buffer
128 */
129 omap_dma_link_lch(prtd->dma_ch, prtd->dma_ch);
130 }
131
132 return err;
133}
134
135static int omap_pcm_hw_free(struct snd_pcm_substream *substream)
136{
137 struct snd_pcm_runtime *runtime = substream->runtime;
138 struct omap_runtime_data *prtd = runtime->private_data;
139
140 if (prtd->dma_data == NULL)
141 return 0;
142 96
143 omap_dma_unlink_lch(prtd->dma_ch, prtd->dma_ch); 97 /* fills in addr_width and direction */
144 omap_free_dma(prtd->dma_ch); 98 err = snd_hwparams_to_dma_slave_config(substream, params, &config);
145 prtd->dma_data = NULL; 99 if (err)
100 return err;
146 101
147 snd_pcm_set_runtime_buffer(substream, NULL); 102 /* Override the *_dma addr_width if requested by the DAI driver */
103 if (dma_data->data_type) {
104 int buswidth = omap_pcm_get_dma_buswidth(dma_data->data_type);
148 105
149 return 0; 106 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
150} 107 config.dst_addr_width = buswidth;
108 else
109 config.src_addr_width = buswidth;
110 }
151 111
152static int omap_pcm_get_dma_type(int num_bits) 112 config.src_addr = dma_data->port_addr;
153{ 113 config.dst_addr = dma_data->port_addr;
154 int data_type; 114 config.src_maxburst = dma_data->packet_size;
115 config.dst_maxburst = dma_data->packet_size;
155 116
156 switch (num_bits) { 117 return dmaengine_slave_config(chan, &config);
157 case 16:
158 data_type = OMAP_DMA_DATA_TYPE_S16;
159 break;
160 case 32:
161 data_type = OMAP_DMA_DATA_TYPE_S32;
162 break;
163 default:
164 data_type = -EINVAL;
165 break;
166 }
167 return data_type;
168} 118}
169 119
170static int omap_pcm_prepare(struct snd_pcm_substream *substream) 120static int omap_pcm_hw_free(struct snd_pcm_substream *substream)
171{ 121{
172 struct snd_pcm_runtime *runtime = substream->runtime; 122 snd_pcm_set_runtime_buffer(substream, NULL);
173 struct omap_runtime_data *prtd = runtime->private_data;
174 struct omap_pcm_dma_data *dma_data = prtd->dma_data;
175 struct omap_dma_channel_params dma_params;
176 int bytes;
177
178 /* return if this is a bufferless transfer e.g.
179 * codec <--> BT codec or GSM modem -- lg FIXME */
180 if (!prtd->dma_data)
181 return 0;
182
183 memset(&dma_params, 0, sizeof(dma_params));
184
185 if (dma_data->data_type)
186 dma_params.data_type = omap_pcm_get_dma_type(
187 dma_data->data_type);
188 else
189 dma_params.data_type = omap_pcm_get_dma_type(
190 snd_pcm_format_physical_width(runtime->format));
191
192 if (dma_params.data_type < 0)
193 return dma_params.data_type;
194
195 dma_params.trigger = dma_data->dma_req;
196
197 if (dma_data->packet_size)
198 dma_params.sync_mode = OMAP_DMA_SYNC_PACKET;
199 else
200 dma_params.sync_mode = OMAP_DMA_SYNC_ELEMENT;
201
202 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
203 dma_params.src_amode = OMAP_DMA_AMODE_POST_INC;
204 dma_params.dst_amode = OMAP_DMA_AMODE_CONSTANT;
205 dma_params.src_or_dst_synch = OMAP_DMA_DST_SYNC;
206 dma_params.src_start = runtime->dma_addr;
207 dma_params.dst_start = dma_data->port_addr;
208 dma_params.dst_port = OMAP_DMA_PORT_MPUI;
209 dma_params.dst_fi = dma_data->packet_size;
210 } else {
211 dma_params.src_amode = OMAP_DMA_AMODE_CONSTANT;
212 dma_params.dst_amode = OMAP_DMA_AMODE_POST_INC;
213 dma_params.src_or_dst_synch = OMAP_DMA_SRC_SYNC;
214 dma_params.src_start = dma_data->port_addr;
215 dma_params.dst_start = runtime->dma_addr;
216 dma_params.src_port = OMAP_DMA_PORT_MPUI;
217 dma_params.src_fi = dma_data->packet_size;
218 }
219 /*
220 * Set DMA transfer frame size equal to ALSA period size and frame
221 * count as no. of ALSA periods. Then with DMA frame interrupt enabled,
222 * we can transfer the whole ALSA buffer with single DMA transfer but
223 * still can get an interrupt at each period bounary
224 */
225 bytes = snd_pcm_lib_period_bytes(substream);
226 dma_params.elem_count = bytes >> dma_params.data_type;
227 dma_params.frame_count = runtime->periods;
228 omap_set_dma_params(prtd->dma_ch, &dma_params);
229
230 if ((cpu_is_omap1510()))
231 omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ |
232 OMAP_DMA_LAST_IRQ | OMAP_DMA_BLOCK_IRQ);
233 else if (!substream->runtime->no_period_wakeup)
234 omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ);
235 else {
236 /*
237 * No period wakeup:
238 * we need to disable BLOCK_IRQ, which is enabled by the omap
239 * dma core at request dma time.
240 */
241 omap_disable_dma_irq(prtd->dma_ch, OMAP_DMA_BLOCK_IRQ);
242 }
243
244 if (!(cpu_class_is_omap1())) {
245 omap_set_dma_src_burst_mode(prtd->dma_ch,
246 OMAP_DMA_DATA_BURST_16);
247 omap_set_dma_dest_burst_mode(prtd->dma_ch,
248 OMAP_DMA_DATA_BURST_16);
249 }
250
251 return 0; 123 return 0;
252} 124}
253 125
254static int omap_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 126static int omap_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
255{ 127{
256 struct snd_pcm_runtime *runtime = substream->runtime; 128 struct snd_soc_pcm_runtime *rtd = substream->private_data;
257 struct omap_runtime_data *prtd = runtime->private_data; 129 struct omap_pcm_dma_data *dma_data;
258 struct omap_pcm_dma_data *dma_data = prtd->dma_data;
259 unsigned long flags;
260 int ret = 0; 130 int ret = 0;
261 131
262 spin_lock_irqsave(&prtd->lock, flags); 132 dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
133
263 switch (cmd) { 134 switch (cmd) {
264 case SNDRV_PCM_TRIGGER_START: 135 case SNDRV_PCM_TRIGGER_START:
265 case SNDRV_PCM_TRIGGER_RESUME: 136 case SNDRV_PCM_TRIGGER_RESUME:
266 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 137 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
267 prtd->period_index = 0;
268 /* Configure McBSP internal buffer usage */ 138 /* Configure McBSP internal buffer usage */
269 if (dma_data->set_threshold) 139 if (dma_data->set_threshold)
270 dma_data->set_threshold(substream); 140 dma_data->set_threshold(substream);
271
272 omap_start_dma(prtd->dma_ch);
273 break; 141 break;
274 142
275 case SNDRV_PCM_TRIGGER_STOP: 143 case SNDRV_PCM_TRIGGER_STOP:
276 case SNDRV_PCM_TRIGGER_SUSPEND: 144 case SNDRV_PCM_TRIGGER_SUSPEND:
277 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 145 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
278 prtd->period_index = -1;
279 omap_stop_dma(prtd->dma_ch);
280 break; 146 break;
281 default: 147 default:
282 ret = -EINVAL; 148 ret = -EINVAL;
283 } 149 }
284 spin_unlock_irqrestore(&prtd->lock, flags); 150
151 if (ret == 0)
152 ret = snd_dmaengine_pcm_trigger(substream, cmd);
285 153
286 return ret; 154 return ret;
287} 155}
288 156
289static snd_pcm_uframes_t omap_pcm_pointer(struct snd_pcm_substream *substream) 157static snd_pcm_uframes_t omap_pcm_pointer(struct snd_pcm_substream *substream)
290{ 158{
291 struct snd_pcm_runtime *runtime = substream->runtime;
292 struct omap_runtime_data *prtd = runtime->private_data;
293 dma_addr_t ptr;
294 snd_pcm_uframes_t offset; 159 snd_pcm_uframes_t offset;
295 160
296 if (cpu_is_omap1510()) { 161 if (cpu_is_omap1510())
297 offset = prtd->period_index * runtime->period_size; 162 offset = snd_dmaengine_pcm_pointer_no_residue(substream);
298 } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { 163 else
299 ptr = omap_get_dma_dst_pos(prtd->dma_ch); 164 offset = snd_dmaengine_pcm_pointer(substream);
300 offset = bytes_to_frames(runtime, ptr - runtime->dma_addr);
301 } else {
302 ptr = omap_get_dma_src_pos(prtd->dma_ch);
303 offset = bytes_to_frames(runtime, ptr - runtime->dma_addr);
304 }
305
306 if (offset >= runtime->buffer_size)
307 offset = 0;
308 165
309 return offset; 166 return offset;
310} 167}
@@ -312,7 +169,8 @@ static snd_pcm_uframes_t omap_pcm_pointer(struct snd_pcm_substream *substream)
312static int omap_pcm_open(struct snd_pcm_substream *substream) 169static int omap_pcm_open(struct snd_pcm_substream *substream)
313{ 170{
314 struct snd_pcm_runtime *runtime = substream->runtime; 171 struct snd_pcm_runtime *runtime = substream->runtime;
315 struct omap_runtime_data *prtd; 172 struct snd_soc_pcm_runtime *rtd = substream->private_data;
173 struct omap_pcm_dma_data *dma_data;
316 int ret; 174 int ret;
317 175
318 snd_soc_set_runtime_hwparams(substream, &omap_pcm_hardware); 176 snd_soc_set_runtime_hwparams(substream, &omap_pcm_hardware);
@@ -321,25 +179,17 @@ static int omap_pcm_open(struct snd_pcm_substream *substream)
321 ret = snd_pcm_hw_constraint_integer(runtime, 179 ret = snd_pcm_hw_constraint_integer(runtime,
322 SNDRV_PCM_HW_PARAM_PERIODS); 180 SNDRV_PCM_HW_PARAM_PERIODS);
323 if (ret < 0) 181 if (ret < 0)
324 goto out; 182 return ret;
325
326 prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
327 if (prtd == NULL) {
328 ret = -ENOMEM;
329 goto out;
330 }
331 spin_lock_init(&prtd->lock);
332 runtime->private_data = prtd;
333 183
334out: 184 dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
185 ret = snd_dmaengine_pcm_open(substream, omap_dma_filter_fn,
186 &dma_data->dma_req);
335 return ret; 187 return ret;
336} 188}
337 189
338static int omap_pcm_close(struct snd_pcm_substream *substream) 190static int omap_pcm_close(struct snd_pcm_substream *substream)
339{ 191{
340 struct snd_pcm_runtime *runtime = substream->runtime; 192 snd_dmaengine_pcm_close(substream);
341
342 kfree(runtime->private_data);
343 return 0; 193 return 0;
344} 194}
345 195
@@ -360,7 +210,6 @@ static struct snd_pcm_ops omap_pcm_ops = {
360 .ioctl = snd_pcm_lib_ioctl, 210 .ioctl = snd_pcm_lib_ioctl,
361 .hw_params = omap_pcm_hw_params, 211 .hw_params = omap_pcm_hw_params,
362 .hw_free = omap_pcm_hw_free, 212 .hw_free = omap_pcm_hw_free,
363 .prepare = omap_pcm_prepare,
364 .trigger = omap_pcm_trigger, 213 .trigger = omap_pcm_trigger,
365 .pointer = omap_pcm_pointer, 214 .pointer = omap_pcm_pointer,
366 .mmap = omap_pcm_mmap, 215 .mmap = omap_pcm_mmap,