diff options
Diffstat (limited to 'sound/usb/line6/pcm.c')
-rw-r--r-- | sound/usb/line6/pcm.c | 487 |
1 files changed, 487 insertions, 0 deletions
diff --git a/sound/usb/line6/pcm.c b/sound/usb/line6/pcm.c new file mode 100644 index 000000000000..8a6059adef69 --- /dev/null +++ b/sound/usb/line6/pcm.c | |||
@@ -0,0 +1,487 @@ | |||
1 | /* | ||
2 | * Line 6 Linux USB driver | ||
3 | * | ||
4 | * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License as | ||
8 | * published by the Free Software Foundation, version 2. | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include <linux/slab.h> | ||
13 | #include <linux/export.h> | ||
14 | #include <sound/core.h> | ||
15 | #include <sound/control.h> | ||
16 | #include <sound/pcm.h> | ||
17 | #include <sound/pcm_params.h> | ||
18 | |||
19 | #include "capture.h" | ||
20 | #include "driver.h" | ||
21 | #include "playback.h" | ||
22 | |||
23 | /* impulse response volume controls */ | ||
24 | static int snd_line6_impulse_volume_info(struct snd_kcontrol *kcontrol, | ||
25 | struct snd_ctl_elem_info *uinfo) | ||
26 | { | ||
27 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
28 | uinfo->count = 1; | ||
29 | uinfo->value.integer.min = 0; | ||
30 | uinfo->value.integer.max = 255; | ||
31 | return 0; | ||
32 | } | ||
33 | |||
34 | static int snd_line6_impulse_volume_get(struct snd_kcontrol *kcontrol, | ||
35 | struct snd_ctl_elem_value *ucontrol) | ||
36 | { | ||
37 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); | ||
38 | |||
39 | ucontrol->value.integer.value[0] = line6pcm->impulse_volume; | ||
40 | return 0; | ||
41 | } | ||
42 | |||
43 | static int snd_line6_impulse_volume_put(struct snd_kcontrol *kcontrol, | ||
44 | struct snd_ctl_elem_value *ucontrol) | ||
45 | { | ||
46 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); | ||
47 | int value = ucontrol->value.integer.value[0]; | ||
48 | |||
49 | if (line6pcm->impulse_volume == value) | ||
50 | return 0; | ||
51 | |||
52 | line6pcm->impulse_volume = value; | ||
53 | if (value > 0) | ||
54 | line6_pcm_acquire(line6pcm, LINE6_BITS_PCM_IMPULSE); | ||
55 | else | ||
56 | line6_pcm_release(line6pcm, LINE6_BITS_PCM_IMPULSE); | ||
57 | return 1; | ||
58 | } | ||
59 | |||
60 | /* impulse response period controls */ | ||
61 | static int snd_line6_impulse_period_info(struct snd_kcontrol *kcontrol, | ||
62 | struct snd_ctl_elem_info *uinfo) | ||
63 | { | ||
64 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
65 | uinfo->count = 1; | ||
66 | uinfo->value.integer.min = 0; | ||
67 | uinfo->value.integer.max = 2000; | ||
68 | return 0; | ||
69 | } | ||
70 | |||
71 | static int snd_line6_impulse_period_get(struct snd_kcontrol *kcontrol, | ||
72 | struct snd_ctl_elem_value *ucontrol) | ||
73 | { | ||
74 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); | ||
75 | |||
76 | ucontrol->value.integer.value[0] = line6pcm->impulse_period; | ||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | static int snd_line6_impulse_period_put(struct snd_kcontrol *kcontrol, | ||
81 | struct snd_ctl_elem_value *ucontrol) | ||
82 | { | ||
83 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); | ||
84 | int value = ucontrol->value.integer.value[0]; | ||
85 | |||
86 | if (line6pcm->impulse_period == value) | ||
87 | return 0; | ||
88 | |||
89 | line6pcm->impulse_period = value; | ||
90 | return 1; | ||
91 | } | ||
92 | |||
93 | static bool test_flags(unsigned long flags0, unsigned long flags1, | ||
94 | unsigned long mask) | ||
95 | { | ||
96 | return ((flags0 & mask) == 0) && ((flags1 & mask) != 0); | ||
97 | } | ||
98 | |||
99 | int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int channels) | ||
100 | { | ||
101 | unsigned long flags_old, flags_new, flags_final; | ||
102 | int err; | ||
103 | |||
104 | do { | ||
105 | flags_old = ACCESS_ONCE(line6pcm->flags); | ||
106 | flags_new = flags_old | channels; | ||
107 | } while (cmpxchg(&line6pcm->flags, flags_old, flags_new) != flags_old); | ||
108 | |||
109 | flags_final = flags_old; | ||
110 | |||
111 | line6pcm->prev_fbuf = NULL; | ||
112 | |||
113 | if (test_flags(flags_old, flags_new, LINE6_BITS_CAPTURE_BUFFER)) { | ||
114 | /* Invoked multiple times in a row so allocate once only */ | ||
115 | if (!line6pcm->buffer_in) { | ||
116 | line6pcm->buffer_in = | ||
117 | kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * | ||
118 | line6pcm->max_packet_size, GFP_KERNEL); | ||
119 | if (!line6pcm->buffer_in) { | ||
120 | err = -ENOMEM; | ||
121 | goto pcm_acquire_error; | ||
122 | } | ||
123 | |||
124 | flags_final |= channels & LINE6_BITS_CAPTURE_BUFFER; | ||
125 | } | ||
126 | } | ||
127 | |||
128 | if (test_flags(flags_old, flags_new, LINE6_BITS_CAPTURE_STREAM)) { | ||
129 | /* | ||
130 | Waiting for completion of active URBs in the stop handler is | ||
131 | a bug, we therefore report an error if capturing is restarted | ||
132 | too soon. | ||
133 | */ | ||
134 | if (line6pcm->active_urb_in | line6pcm->unlink_urb_in) { | ||
135 | dev_err(line6pcm->line6->ifcdev, "Device not yet ready\n"); | ||
136 | return -EBUSY; | ||
137 | } | ||
138 | |||
139 | line6pcm->count_in = 0; | ||
140 | line6pcm->prev_fsize = 0; | ||
141 | err = line6_submit_audio_in_all_urbs(line6pcm); | ||
142 | |||
143 | if (err < 0) | ||
144 | goto pcm_acquire_error; | ||
145 | |||
146 | flags_final |= channels & LINE6_BITS_CAPTURE_STREAM; | ||
147 | } | ||
148 | |||
149 | if (test_flags(flags_old, flags_new, LINE6_BITS_PLAYBACK_BUFFER)) { | ||
150 | /* Invoked multiple times in a row so allocate once only */ | ||
151 | if (!line6pcm->buffer_out) { | ||
152 | line6pcm->buffer_out = | ||
153 | kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * | ||
154 | line6pcm->max_packet_size, GFP_KERNEL); | ||
155 | if (!line6pcm->buffer_out) { | ||
156 | err = -ENOMEM; | ||
157 | goto pcm_acquire_error; | ||
158 | } | ||
159 | |||
160 | flags_final |= channels & LINE6_BITS_PLAYBACK_BUFFER; | ||
161 | } | ||
162 | } | ||
163 | |||
164 | if (test_flags(flags_old, flags_new, LINE6_BITS_PLAYBACK_STREAM)) { | ||
165 | /* | ||
166 | See comment above regarding PCM restart. | ||
167 | */ | ||
168 | if (line6pcm->active_urb_out | line6pcm->unlink_urb_out) { | ||
169 | dev_err(line6pcm->line6->ifcdev, "Device not yet ready\n"); | ||
170 | return -EBUSY; | ||
171 | } | ||
172 | |||
173 | line6pcm->count_out = 0; | ||
174 | err = line6_submit_audio_out_all_urbs(line6pcm); | ||
175 | |||
176 | if (err < 0) | ||
177 | goto pcm_acquire_error; | ||
178 | |||
179 | flags_final |= channels & LINE6_BITS_PLAYBACK_STREAM; | ||
180 | } | ||
181 | |||
182 | return 0; | ||
183 | |||
184 | pcm_acquire_error: | ||
185 | /* | ||
186 | If not all requested resources/streams could be obtained, release | ||
187 | those which were successfully obtained (if any). | ||
188 | */ | ||
189 | line6_pcm_release(line6pcm, flags_final & channels); | ||
190 | return err; | ||
191 | } | ||
192 | EXPORT_SYMBOL_GPL(line6_pcm_acquire); | ||
193 | |||
194 | int line6_pcm_release(struct snd_line6_pcm *line6pcm, int channels) | ||
195 | { | ||
196 | unsigned long flags_old, flags_new; | ||
197 | |||
198 | do { | ||
199 | flags_old = ACCESS_ONCE(line6pcm->flags); | ||
200 | flags_new = flags_old & ~channels; | ||
201 | } while (cmpxchg(&line6pcm->flags, flags_old, flags_new) != flags_old); | ||
202 | |||
203 | if (test_flags(flags_new, flags_old, LINE6_BITS_CAPTURE_STREAM)) | ||
204 | line6_unlink_audio_in_urbs(line6pcm); | ||
205 | |||
206 | if (test_flags(flags_new, flags_old, LINE6_BITS_CAPTURE_BUFFER)) { | ||
207 | line6_wait_clear_audio_in_urbs(line6pcm); | ||
208 | line6_free_capture_buffer(line6pcm); | ||
209 | } | ||
210 | |||
211 | if (test_flags(flags_new, flags_old, LINE6_BITS_PLAYBACK_STREAM)) | ||
212 | line6_unlink_audio_out_urbs(line6pcm); | ||
213 | |||
214 | if (test_flags(flags_new, flags_old, LINE6_BITS_PLAYBACK_BUFFER)) { | ||
215 | line6_wait_clear_audio_out_urbs(line6pcm); | ||
216 | line6_free_playback_buffer(line6pcm); | ||
217 | } | ||
218 | |||
219 | return 0; | ||
220 | } | ||
221 | EXPORT_SYMBOL_GPL(line6_pcm_release); | ||
222 | |||
223 | /* trigger callback */ | ||
224 | int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd) | ||
225 | { | ||
226 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
227 | struct snd_pcm_substream *s; | ||
228 | int err; | ||
229 | |||
230 | spin_lock(&line6pcm->lock_trigger); | ||
231 | clear_bit(LINE6_INDEX_PREPARED, &line6pcm->flags); | ||
232 | |||
233 | snd_pcm_group_for_each_entry(s, substream) { | ||
234 | if (s->pcm->card != substream->pcm->card) | ||
235 | continue; | ||
236 | switch (s->stream) { | ||
237 | case SNDRV_PCM_STREAM_PLAYBACK: | ||
238 | err = snd_line6_playback_trigger(line6pcm, cmd); | ||
239 | |||
240 | if (err < 0) { | ||
241 | spin_unlock(&line6pcm->lock_trigger); | ||
242 | return err; | ||
243 | } | ||
244 | |||
245 | break; | ||
246 | |||
247 | case SNDRV_PCM_STREAM_CAPTURE: | ||
248 | err = snd_line6_capture_trigger(line6pcm, cmd); | ||
249 | |||
250 | if (err < 0) { | ||
251 | spin_unlock(&line6pcm->lock_trigger); | ||
252 | return err; | ||
253 | } | ||
254 | |||
255 | break; | ||
256 | |||
257 | default: | ||
258 | dev_err(line6pcm->line6->ifcdev, | ||
259 | "Unknown stream direction %d\n", s->stream); | ||
260 | } | ||
261 | } | ||
262 | |||
263 | spin_unlock(&line6pcm->lock_trigger); | ||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | /* control info callback */ | ||
268 | static int snd_line6_control_playback_info(struct snd_kcontrol *kcontrol, | ||
269 | struct snd_ctl_elem_info *uinfo) | ||
270 | { | ||
271 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
272 | uinfo->count = 2; | ||
273 | uinfo->value.integer.min = 0; | ||
274 | uinfo->value.integer.max = 256; | ||
275 | return 0; | ||
276 | } | ||
277 | |||
278 | /* control get callback */ | ||
279 | static int snd_line6_control_playback_get(struct snd_kcontrol *kcontrol, | ||
280 | struct snd_ctl_elem_value *ucontrol) | ||
281 | { | ||
282 | int i; | ||
283 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); | ||
284 | |||
285 | for (i = 2; i--;) | ||
286 | ucontrol->value.integer.value[i] = line6pcm->volume_playback[i]; | ||
287 | |||
288 | return 0; | ||
289 | } | ||
290 | |||
291 | /* control put callback */ | ||
292 | static int snd_line6_control_playback_put(struct snd_kcontrol *kcontrol, | ||
293 | struct snd_ctl_elem_value *ucontrol) | ||
294 | { | ||
295 | int i, changed = 0; | ||
296 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); | ||
297 | |||
298 | for (i = 2; i--;) | ||
299 | if (line6pcm->volume_playback[i] != | ||
300 | ucontrol->value.integer.value[i]) { | ||
301 | line6pcm->volume_playback[i] = | ||
302 | ucontrol->value.integer.value[i]; | ||
303 | changed = 1; | ||
304 | } | ||
305 | |||
306 | return changed; | ||
307 | } | ||
308 | |||
309 | /* control definition */ | ||
310 | static struct snd_kcontrol_new line6_controls[] = { | ||
311 | { | ||
312 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
313 | .name = "PCM Playback Volume", | ||
314 | .info = snd_line6_control_playback_info, | ||
315 | .get = snd_line6_control_playback_get, | ||
316 | .put = snd_line6_control_playback_put | ||
317 | }, | ||
318 | { | ||
319 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
320 | .name = "Impulse Response Volume", | ||
321 | .info = snd_line6_impulse_volume_info, | ||
322 | .get = snd_line6_impulse_volume_get, | ||
323 | .put = snd_line6_impulse_volume_put | ||
324 | }, | ||
325 | { | ||
326 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
327 | .name = "Impulse Response Period", | ||
328 | .info = snd_line6_impulse_period_info, | ||
329 | .get = snd_line6_impulse_period_get, | ||
330 | .put = snd_line6_impulse_period_put | ||
331 | }, | ||
332 | }; | ||
333 | |||
334 | /* | ||
335 | Cleanup the PCM device. | ||
336 | */ | ||
337 | static void line6_cleanup_pcm(struct snd_pcm *pcm) | ||
338 | { | ||
339 | int i; | ||
340 | struct snd_line6_pcm *line6pcm = snd_pcm_chip(pcm); | ||
341 | |||
342 | for (i = LINE6_ISO_BUFFERS; i--;) { | ||
343 | if (line6pcm->urb_audio_out[i]) { | ||
344 | usb_kill_urb(line6pcm->urb_audio_out[i]); | ||
345 | usb_free_urb(line6pcm->urb_audio_out[i]); | ||
346 | } | ||
347 | if (line6pcm->urb_audio_in[i]) { | ||
348 | usb_kill_urb(line6pcm->urb_audio_in[i]); | ||
349 | usb_free_urb(line6pcm->urb_audio_in[i]); | ||
350 | } | ||
351 | } | ||
352 | kfree(line6pcm); | ||
353 | } | ||
354 | |||
355 | /* create a PCM device */ | ||
356 | static int snd_line6_new_pcm(struct usb_line6 *line6, struct snd_pcm **pcm_ret) | ||
357 | { | ||
358 | struct snd_pcm *pcm; | ||
359 | int err; | ||
360 | |||
361 | err = snd_pcm_new(line6->card, (char *)line6->properties->name, | ||
362 | 0, 1, 1, pcm_ret); | ||
363 | if (err < 0) | ||
364 | return err; | ||
365 | pcm = *pcm_ret; | ||
366 | strcpy(pcm->name, line6->properties->name); | ||
367 | |||
368 | /* set operators */ | ||
369 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, | ||
370 | &snd_line6_playback_ops); | ||
371 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_line6_capture_ops); | ||
372 | |||
373 | /* pre-allocation of buffers */ | ||
374 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, | ||
375 | snd_dma_continuous_data | ||
376 | (GFP_KERNEL), 64 * 1024, | ||
377 | 128 * 1024); | ||
378 | return 0; | ||
379 | } | ||
380 | |||
381 | /* | ||
382 | Sync with PCM stream stops. | ||
383 | */ | ||
384 | void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm) | ||
385 | { | ||
386 | line6_unlink_wait_clear_audio_out_urbs(line6pcm); | ||
387 | line6_unlink_wait_clear_audio_in_urbs(line6pcm); | ||
388 | } | ||
389 | |||
390 | /* | ||
391 | Create and register the PCM device and mixer entries. | ||
392 | Create URBs for playback and capture. | ||
393 | */ | ||
394 | int line6_init_pcm(struct usb_line6 *line6, | ||
395 | struct line6_pcm_properties *properties) | ||
396 | { | ||
397 | int i, err; | ||
398 | unsigned ep_read = line6->properties->ep_audio_r; | ||
399 | unsigned ep_write = line6->properties->ep_audio_w; | ||
400 | struct snd_pcm *pcm; | ||
401 | struct snd_line6_pcm *line6pcm; | ||
402 | |||
403 | if (!(line6->properties->capabilities & LINE6_CAP_PCM)) | ||
404 | return 0; /* skip PCM initialization and report success */ | ||
405 | |||
406 | err = snd_line6_new_pcm(line6, &pcm); | ||
407 | if (err < 0) | ||
408 | return err; | ||
409 | |||
410 | line6pcm = kzalloc(sizeof(*line6pcm), GFP_KERNEL); | ||
411 | if (!line6pcm) | ||
412 | return -ENOMEM; | ||
413 | |||
414 | line6pcm->pcm = pcm; | ||
415 | line6pcm->properties = properties; | ||
416 | line6pcm->volume_playback[0] = line6pcm->volume_playback[1] = 255; | ||
417 | line6pcm->volume_monitor = 255; | ||
418 | line6pcm->line6 = line6; | ||
419 | |||
420 | /* Read and write buffers are sized identically, so choose minimum */ | ||
421 | line6pcm->max_packet_size = min( | ||
422 | usb_maxpacket(line6->usbdev, | ||
423 | usb_rcvisocpipe(line6->usbdev, ep_read), 0), | ||
424 | usb_maxpacket(line6->usbdev, | ||
425 | usb_sndisocpipe(line6->usbdev, ep_write), 1)); | ||
426 | |||
427 | spin_lock_init(&line6pcm->lock_audio_out); | ||
428 | spin_lock_init(&line6pcm->lock_audio_in); | ||
429 | spin_lock_init(&line6pcm->lock_trigger); | ||
430 | line6pcm->impulse_period = LINE6_IMPULSE_DEFAULT_PERIOD; | ||
431 | |||
432 | line6->line6pcm = line6pcm; | ||
433 | |||
434 | pcm->private_data = line6pcm; | ||
435 | pcm->private_free = line6_cleanup_pcm; | ||
436 | |||
437 | err = line6_create_audio_out_urbs(line6pcm); | ||
438 | if (err < 0) | ||
439 | return err; | ||
440 | |||
441 | err = line6_create_audio_in_urbs(line6pcm); | ||
442 | if (err < 0) | ||
443 | return err; | ||
444 | |||
445 | /* mixer: */ | ||
446 | for (i = 0; i < ARRAY_SIZE(line6_controls); i++) { | ||
447 | err = snd_ctl_add(line6->card, | ||
448 | snd_ctl_new1(&line6_controls[i], line6pcm)); | ||
449 | if (err < 0) | ||
450 | return err; | ||
451 | } | ||
452 | |||
453 | return 0; | ||
454 | } | ||
455 | EXPORT_SYMBOL_GPL(line6_init_pcm); | ||
456 | |||
457 | /* prepare pcm callback */ | ||
458 | int snd_line6_prepare(struct snd_pcm_substream *substream) | ||
459 | { | ||
460 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
461 | |||
462 | switch (substream->stream) { | ||
463 | case SNDRV_PCM_STREAM_PLAYBACK: | ||
464 | if ((line6pcm->flags & LINE6_BITS_PLAYBACK_STREAM) == 0) | ||
465 | line6_unlink_wait_clear_audio_out_urbs(line6pcm); | ||
466 | |||
467 | break; | ||
468 | |||
469 | case SNDRV_PCM_STREAM_CAPTURE: | ||
470 | if ((line6pcm->flags & LINE6_BITS_CAPTURE_STREAM) == 0) | ||
471 | line6_unlink_wait_clear_audio_in_urbs(line6pcm); | ||
472 | |||
473 | break; | ||
474 | } | ||
475 | |||
476 | if (!test_and_set_bit(LINE6_INDEX_PREPARED, &line6pcm->flags)) { | ||
477 | line6pcm->count_out = 0; | ||
478 | line6pcm->pos_out = 0; | ||
479 | line6pcm->pos_out_done = 0; | ||
480 | line6pcm->bytes_out = 0; | ||
481 | line6pcm->count_in = 0; | ||
482 | line6pcm->pos_in_done = 0; | ||
483 | line6pcm->bytes_in = 0; | ||
484 | } | ||
485 | |||
486 | return 0; | ||
487 | } | ||