diff options
Diffstat (limited to 'sound/usb/line6/pcm.c')
-rw-r--r-- | sound/usb/line6/pcm.c | 588 |
1 files changed, 588 insertions, 0 deletions
diff --git a/sound/usb/line6/pcm.c b/sound/usb/line6/pcm.c new file mode 100644 index 000000000000..8461d6bf992f --- /dev/null +++ b/sound/usb/line6/pcm.c | |||
@@ -0,0 +1,588 @@ | |||
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 | int err; | ||
49 | |||
50 | if (line6pcm->impulse_volume == value) | ||
51 | return 0; | ||
52 | |||
53 | line6pcm->impulse_volume = value; | ||
54 | if (value > 0) { | ||
55 | err = line6_pcm_acquire(line6pcm, LINE6_STREAM_IMPULSE); | ||
56 | if (err < 0) { | ||
57 | line6pcm->impulse_volume = 0; | ||
58 | line6_pcm_release(line6pcm, LINE6_STREAM_IMPULSE); | ||
59 | return err; | ||
60 | } | ||
61 | } else { | ||
62 | line6_pcm_release(line6pcm, LINE6_STREAM_IMPULSE); | ||
63 | } | ||
64 | return 1; | ||
65 | } | ||
66 | |||
67 | /* impulse response period controls */ | ||
68 | static int snd_line6_impulse_period_info(struct snd_kcontrol *kcontrol, | ||
69 | struct snd_ctl_elem_info *uinfo) | ||
70 | { | ||
71 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
72 | uinfo->count = 1; | ||
73 | uinfo->value.integer.min = 0; | ||
74 | uinfo->value.integer.max = 2000; | ||
75 | return 0; | ||
76 | } | ||
77 | |||
78 | static int snd_line6_impulse_period_get(struct snd_kcontrol *kcontrol, | ||
79 | struct snd_ctl_elem_value *ucontrol) | ||
80 | { | ||
81 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); | ||
82 | |||
83 | ucontrol->value.integer.value[0] = line6pcm->impulse_period; | ||
84 | return 0; | ||
85 | } | ||
86 | |||
87 | static int snd_line6_impulse_period_put(struct snd_kcontrol *kcontrol, | ||
88 | struct snd_ctl_elem_value *ucontrol) | ||
89 | { | ||
90 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); | ||
91 | int value = ucontrol->value.integer.value[0]; | ||
92 | |||
93 | if (line6pcm->impulse_period == value) | ||
94 | return 0; | ||
95 | |||
96 | line6pcm->impulse_period = value; | ||
97 | return 1; | ||
98 | } | ||
99 | |||
100 | /* | ||
101 | Unlink all currently active URBs. | ||
102 | */ | ||
103 | static void line6_unlink_audio_urbs(struct snd_line6_pcm *line6pcm, | ||
104 | struct line6_pcm_stream *pcms) | ||
105 | { | ||
106 | int i; | ||
107 | |||
108 | for (i = 0; i < LINE6_ISO_BUFFERS; i++) { | ||
109 | if (test_bit(i, &pcms->active_urbs)) { | ||
110 | if (!test_and_set_bit(i, &pcms->unlink_urbs)) | ||
111 | usb_unlink_urb(pcms->urbs[i]); | ||
112 | } | ||
113 | } | ||
114 | } | ||
115 | |||
116 | /* | ||
117 | Wait until unlinking of all currently active URBs has been finished. | ||
118 | */ | ||
119 | static void line6_wait_clear_audio_urbs(struct snd_line6_pcm *line6pcm, | ||
120 | struct line6_pcm_stream *pcms) | ||
121 | { | ||
122 | int timeout = HZ; | ||
123 | int i; | ||
124 | int alive; | ||
125 | |||
126 | do { | ||
127 | alive = 0; | ||
128 | for (i = 0; i < LINE6_ISO_BUFFERS; i++) { | ||
129 | if (test_bit(i, &pcms->active_urbs)) | ||
130 | alive++; | ||
131 | } | ||
132 | if (!alive) | ||
133 | break; | ||
134 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
135 | schedule_timeout(1); | ||
136 | } while (--timeout > 0); | ||
137 | if (alive) | ||
138 | dev_err(line6pcm->line6->ifcdev, | ||
139 | "timeout: still %d active urbs..\n", alive); | ||
140 | } | ||
141 | |||
142 | static inline struct line6_pcm_stream * | ||
143 | get_stream(struct snd_line6_pcm *line6pcm, int direction) | ||
144 | { | ||
145 | return (direction == SNDRV_PCM_STREAM_PLAYBACK) ? | ||
146 | &line6pcm->out : &line6pcm->in; | ||
147 | } | ||
148 | |||
149 | /* allocate a buffer if not opened yet; | ||
150 | * call this in line6pcm.state_change mutex | ||
151 | */ | ||
152 | static int line6_buffer_acquire(struct snd_line6_pcm *line6pcm, | ||
153 | struct line6_pcm_stream *pstr, int type) | ||
154 | { | ||
155 | /* Invoked multiple times in a row so allocate once only */ | ||
156 | if (!test_and_set_bit(type, &pstr->opened) && !pstr->buffer) { | ||
157 | pstr->buffer = kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * | ||
158 | line6pcm->max_packet_size, GFP_KERNEL); | ||
159 | if (!pstr->buffer) | ||
160 | return -ENOMEM; | ||
161 | } | ||
162 | return 0; | ||
163 | } | ||
164 | |||
165 | /* free a buffer if all streams are closed; | ||
166 | * call this in line6pcm.state_change mutex | ||
167 | */ | ||
168 | static void line6_buffer_release(struct snd_line6_pcm *line6pcm, | ||
169 | struct line6_pcm_stream *pstr, int type) | ||
170 | { | ||
171 | |||
172 | clear_bit(type, &pstr->opened); | ||
173 | if (!pstr->opened) { | ||
174 | line6_wait_clear_audio_urbs(line6pcm, pstr); | ||
175 | kfree(pstr->buffer); | ||
176 | pstr->buffer = NULL; | ||
177 | } | ||
178 | } | ||
179 | |||
180 | /* start a PCM stream */ | ||
181 | static int line6_stream_start(struct snd_line6_pcm *line6pcm, int direction, | ||
182 | int type) | ||
183 | { | ||
184 | unsigned long flags; | ||
185 | struct line6_pcm_stream *pstr = get_stream(line6pcm, direction); | ||
186 | int ret = 0; | ||
187 | |||
188 | spin_lock_irqsave(&pstr->lock, flags); | ||
189 | if (!test_and_set_bit(type, &pstr->running)) { | ||
190 | if (pstr->active_urbs || pstr->unlink_urbs) { | ||
191 | ret = -EBUSY; | ||
192 | goto error; | ||
193 | } | ||
194 | |||
195 | pstr->count = 0; | ||
196 | /* Submit all currently available URBs */ | ||
197 | if (direction == SNDRV_PCM_STREAM_PLAYBACK) | ||
198 | ret = line6_submit_audio_out_all_urbs(line6pcm); | ||
199 | else | ||
200 | ret = line6_submit_audio_in_all_urbs(line6pcm); | ||
201 | } | ||
202 | error: | ||
203 | if (ret < 0) | ||
204 | clear_bit(type, &pstr->running); | ||
205 | spin_unlock_irqrestore(&pstr->lock, flags); | ||
206 | return ret; | ||
207 | } | ||
208 | |||
209 | /* stop a PCM stream; this doesn't sync with the unlinked URBs */ | ||
210 | static void line6_stream_stop(struct snd_line6_pcm *line6pcm, int direction, | ||
211 | int type) | ||
212 | { | ||
213 | unsigned long flags; | ||
214 | struct line6_pcm_stream *pstr = get_stream(line6pcm, direction); | ||
215 | |||
216 | spin_lock_irqsave(&pstr->lock, flags); | ||
217 | clear_bit(type, &pstr->running); | ||
218 | if (!pstr->running) { | ||
219 | line6_unlink_audio_urbs(line6pcm, pstr); | ||
220 | if (direction == SNDRV_PCM_STREAM_CAPTURE) { | ||
221 | line6pcm->prev_fbuf = NULL; | ||
222 | line6pcm->prev_fsize = 0; | ||
223 | } | ||
224 | } | ||
225 | spin_unlock_irqrestore(&pstr->lock, flags); | ||
226 | } | ||
227 | |||
228 | /* common PCM trigger callback */ | ||
229 | int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd) | ||
230 | { | ||
231 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
232 | struct snd_pcm_substream *s; | ||
233 | int err; | ||
234 | |||
235 | clear_bit(LINE6_FLAG_PREPARED, &line6pcm->flags); | ||
236 | |||
237 | snd_pcm_group_for_each_entry(s, substream) { | ||
238 | if (s->pcm->card != substream->pcm->card) | ||
239 | continue; | ||
240 | |||
241 | switch (cmd) { | ||
242 | case SNDRV_PCM_TRIGGER_START: | ||
243 | case SNDRV_PCM_TRIGGER_RESUME: | ||
244 | err = line6_stream_start(line6pcm, s->stream, | ||
245 | LINE6_STREAM_PCM); | ||
246 | if (err < 0) | ||
247 | return err; | ||
248 | break; | ||
249 | |||
250 | case SNDRV_PCM_TRIGGER_STOP: | ||
251 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
252 | line6_stream_stop(line6pcm, s->stream, | ||
253 | LINE6_STREAM_PCM); | ||
254 | break; | ||
255 | |||
256 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
257 | if (s->stream != SNDRV_PCM_STREAM_PLAYBACK) | ||
258 | return -EINVAL; | ||
259 | set_bit(LINE6_FLAG_PAUSE_PLAYBACK, &line6pcm->flags); | ||
260 | break; | ||
261 | |||
262 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
263 | if (s->stream != SNDRV_PCM_STREAM_PLAYBACK) | ||
264 | return -EINVAL; | ||
265 | clear_bit(LINE6_FLAG_PAUSE_PLAYBACK, &line6pcm->flags); | ||
266 | break; | ||
267 | |||
268 | default: | ||
269 | return -EINVAL; | ||
270 | } | ||
271 | } | ||
272 | |||
273 | return 0; | ||
274 | } | ||
275 | |||
276 | /* common PCM pointer callback */ | ||
277 | snd_pcm_uframes_t snd_line6_pointer(struct snd_pcm_substream *substream) | ||
278 | { | ||
279 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
280 | struct line6_pcm_stream *pstr = get_stream(line6pcm, substream->stream); | ||
281 | |||
282 | return pstr->pos_done; | ||
283 | } | ||
284 | |||
285 | /* Acquire and start duplex streams: | ||
286 | * type is either LINE6_STREAM_IMPULSE or LINE6_STREAM_MONITOR | ||
287 | */ | ||
288 | int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int type) | ||
289 | { | ||
290 | struct line6_pcm_stream *pstr; | ||
291 | int ret = 0, dir; | ||
292 | |||
293 | mutex_lock(&line6pcm->state_mutex); | ||
294 | for (dir = 0; dir < 2; dir++) { | ||
295 | pstr = get_stream(line6pcm, dir); | ||
296 | ret = line6_buffer_acquire(line6pcm, pstr, type); | ||
297 | if (ret < 0) | ||
298 | goto error; | ||
299 | if (!pstr->running) | ||
300 | line6_wait_clear_audio_urbs(line6pcm, pstr); | ||
301 | } | ||
302 | for (dir = 0; dir < 2; dir++) { | ||
303 | ret = line6_stream_start(line6pcm, dir, type); | ||
304 | if (ret < 0) | ||
305 | goto error; | ||
306 | } | ||
307 | error: | ||
308 | mutex_unlock(&line6pcm->state_mutex); | ||
309 | if (ret < 0) | ||
310 | line6_pcm_release(line6pcm, type); | ||
311 | return ret; | ||
312 | } | ||
313 | EXPORT_SYMBOL_GPL(line6_pcm_acquire); | ||
314 | |||
315 | /* Stop and release duplex streams */ | ||
316 | void line6_pcm_release(struct snd_line6_pcm *line6pcm, int type) | ||
317 | { | ||
318 | struct line6_pcm_stream *pstr; | ||
319 | int dir; | ||
320 | |||
321 | mutex_lock(&line6pcm->state_mutex); | ||
322 | for (dir = 0; dir < 2; dir++) | ||
323 | line6_stream_stop(line6pcm, dir, type); | ||
324 | for (dir = 0; dir < 2; dir++) { | ||
325 | pstr = get_stream(line6pcm, dir); | ||
326 | line6_buffer_release(line6pcm, pstr, type); | ||
327 | } | ||
328 | mutex_unlock(&line6pcm->state_mutex); | ||
329 | } | ||
330 | EXPORT_SYMBOL_GPL(line6_pcm_release); | ||
331 | |||
332 | /* common PCM hw_params callback */ | ||
333 | int snd_line6_hw_params(struct snd_pcm_substream *substream, | ||
334 | struct snd_pcm_hw_params *hw_params) | ||
335 | { | ||
336 | int ret; | ||
337 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
338 | struct line6_pcm_stream *pstr = get_stream(line6pcm, substream->stream); | ||
339 | |||
340 | mutex_lock(&line6pcm->state_mutex); | ||
341 | ret = line6_buffer_acquire(line6pcm, pstr, LINE6_STREAM_PCM); | ||
342 | if (ret < 0) | ||
343 | goto error; | ||
344 | |||
345 | ret = snd_pcm_lib_malloc_pages(substream, | ||
346 | params_buffer_bytes(hw_params)); | ||
347 | if (ret < 0) { | ||
348 | line6_buffer_release(line6pcm, pstr, LINE6_STREAM_PCM); | ||
349 | goto error; | ||
350 | } | ||
351 | |||
352 | pstr->period = params_period_bytes(hw_params); | ||
353 | error: | ||
354 | mutex_unlock(&line6pcm->state_mutex); | ||
355 | return ret; | ||
356 | } | ||
357 | |||
358 | /* common PCM hw_free callback */ | ||
359 | int snd_line6_hw_free(struct snd_pcm_substream *substream) | ||
360 | { | ||
361 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
362 | struct line6_pcm_stream *pstr = get_stream(line6pcm, substream->stream); | ||
363 | |||
364 | mutex_lock(&line6pcm->state_mutex); | ||
365 | line6_buffer_release(line6pcm, pstr, LINE6_STREAM_PCM); | ||
366 | mutex_unlock(&line6pcm->state_mutex); | ||
367 | return snd_pcm_lib_free_pages(substream); | ||
368 | } | ||
369 | |||
370 | |||
371 | /* control info callback */ | ||
372 | static int snd_line6_control_playback_info(struct snd_kcontrol *kcontrol, | ||
373 | struct snd_ctl_elem_info *uinfo) | ||
374 | { | ||
375 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
376 | uinfo->count = 2; | ||
377 | uinfo->value.integer.min = 0; | ||
378 | uinfo->value.integer.max = 256; | ||
379 | return 0; | ||
380 | } | ||
381 | |||
382 | /* control get callback */ | ||
383 | static int snd_line6_control_playback_get(struct snd_kcontrol *kcontrol, | ||
384 | struct snd_ctl_elem_value *ucontrol) | ||
385 | { | ||
386 | int i; | ||
387 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); | ||
388 | |||
389 | for (i = 0; i < 2; i++) | ||
390 | ucontrol->value.integer.value[i] = line6pcm->volume_playback[i]; | ||
391 | |||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | /* control put callback */ | ||
396 | static int snd_line6_control_playback_put(struct snd_kcontrol *kcontrol, | ||
397 | struct snd_ctl_elem_value *ucontrol) | ||
398 | { | ||
399 | int i, changed = 0; | ||
400 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); | ||
401 | |||
402 | for (i = 0; i < 2; i++) | ||
403 | if (line6pcm->volume_playback[i] != | ||
404 | ucontrol->value.integer.value[i]) { | ||
405 | line6pcm->volume_playback[i] = | ||
406 | ucontrol->value.integer.value[i]; | ||
407 | changed = 1; | ||
408 | } | ||
409 | |||
410 | return changed; | ||
411 | } | ||
412 | |||
413 | /* control definition */ | ||
414 | static struct snd_kcontrol_new line6_controls[] = { | ||
415 | { | ||
416 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
417 | .name = "PCM Playback Volume", | ||
418 | .info = snd_line6_control_playback_info, | ||
419 | .get = snd_line6_control_playback_get, | ||
420 | .put = snd_line6_control_playback_put | ||
421 | }, | ||
422 | { | ||
423 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
424 | .name = "Impulse Response Volume", | ||
425 | .info = snd_line6_impulse_volume_info, | ||
426 | .get = snd_line6_impulse_volume_get, | ||
427 | .put = snd_line6_impulse_volume_put | ||
428 | }, | ||
429 | { | ||
430 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
431 | .name = "Impulse Response Period", | ||
432 | .info = snd_line6_impulse_period_info, | ||
433 | .get = snd_line6_impulse_period_get, | ||
434 | .put = snd_line6_impulse_period_put | ||
435 | }, | ||
436 | }; | ||
437 | |||
438 | /* | ||
439 | Cleanup the PCM device. | ||
440 | */ | ||
441 | static void cleanup_urbs(struct line6_pcm_stream *pcms) | ||
442 | { | ||
443 | int i; | ||
444 | |||
445 | for (i = 0; i < LINE6_ISO_BUFFERS; i++) { | ||
446 | if (pcms->urbs[i]) { | ||
447 | usb_kill_urb(pcms->urbs[i]); | ||
448 | usb_free_urb(pcms->urbs[i]); | ||
449 | } | ||
450 | } | ||
451 | } | ||
452 | |||
453 | static void line6_cleanup_pcm(struct snd_pcm *pcm) | ||
454 | { | ||
455 | struct snd_line6_pcm *line6pcm = snd_pcm_chip(pcm); | ||
456 | |||
457 | cleanup_urbs(&line6pcm->out); | ||
458 | cleanup_urbs(&line6pcm->in); | ||
459 | kfree(line6pcm); | ||
460 | } | ||
461 | |||
462 | /* create a PCM device */ | ||
463 | static int snd_line6_new_pcm(struct usb_line6 *line6, struct snd_pcm **pcm_ret) | ||
464 | { | ||
465 | struct snd_pcm *pcm; | ||
466 | int err; | ||
467 | |||
468 | err = snd_pcm_new(line6->card, (char *)line6->properties->name, | ||
469 | 0, 1, 1, pcm_ret); | ||
470 | if (err < 0) | ||
471 | return err; | ||
472 | pcm = *pcm_ret; | ||
473 | strcpy(pcm->name, line6->properties->name); | ||
474 | |||
475 | /* set operators */ | ||
476 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, | ||
477 | &snd_line6_playback_ops); | ||
478 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_line6_capture_ops); | ||
479 | |||
480 | /* pre-allocation of buffers */ | ||
481 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, | ||
482 | snd_dma_continuous_data | ||
483 | (GFP_KERNEL), 64 * 1024, | ||
484 | 128 * 1024); | ||
485 | return 0; | ||
486 | } | ||
487 | |||
488 | /* | ||
489 | Sync with PCM stream stops. | ||
490 | */ | ||
491 | void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm) | ||
492 | { | ||
493 | line6_unlink_audio_urbs(line6pcm, &line6pcm->out); | ||
494 | line6_unlink_audio_urbs(line6pcm, &line6pcm->in); | ||
495 | line6_wait_clear_audio_urbs(line6pcm, &line6pcm->out); | ||
496 | line6_wait_clear_audio_urbs(line6pcm, &line6pcm->in); | ||
497 | } | ||
498 | |||
499 | /* | ||
500 | Create and register the PCM device and mixer entries. | ||
501 | Create URBs for playback and capture. | ||
502 | */ | ||
503 | int line6_init_pcm(struct usb_line6 *line6, | ||
504 | struct line6_pcm_properties *properties) | ||
505 | { | ||
506 | int i, err; | ||
507 | unsigned ep_read = line6->properties->ep_audio_r; | ||
508 | unsigned ep_write = line6->properties->ep_audio_w; | ||
509 | struct snd_pcm *pcm; | ||
510 | struct snd_line6_pcm *line6pcm; | ||
511 | |||
512 | if (!(line6->properties->capabilities & LINE6_CAP_PCM)) | ||
513 | return 0; /* skip PCM initialization and report success */ | ||
514 | |||
515 | err = snd_line6_new_pcm(line6, &pcm); | ||
516 | if (err < 0) | ||
517 | return err; | ||
518 | |||
519 | line6pcm = kzalloc(sizeof(*line6pcm), GFP_KERNEL); | ||
520 | if (!line6pcm) | ||
521 | return -ENOMEM; | ||
522 | |||
523 | mutex_init(&line6pcm->state_mutex); | ||
524 | line6pcm->pcm = pcm; | ||
525 | line6pcm->properties = properties; | ||
526 | line6pcm->volume_playback[0] = line6pcm->volume_playback[1] = 255; | ||
527 | line6pcm->volume_monitor = 255; | ||
528 | line6pcm->line6 = line6; | ||
529 | |||
530 | /* Read and write buffers are sized identically, so choose minimum */ | ||
531 | line6pcm->max_packet_size = min( | ||
532 | usb_maxpacket(line6->usbdev, | ||
533 | usb_rcvisocpipe(line6->usbdev, ep_read), 0), | ||
534 | usb_maxpacket(line6->usbdev, | ||
535 | usb_sndisocpipe(line6->usbdev, ep_write), 1)); | ||
536 | |||
537 | spin_lock_init(&line6pcm->out.lock); | ||
538 | spin_lock_init(&line6pcm->in.lock); | ||
539 | line6pcm->impulse_period = LINE6_IMPULSE_DEFAULT_PERIOD; | ||
540 | |||
541 | line6->line6pcm = line6pcm; | ||
542 | |||
543 | pcm->private_data = line6pcm; | ||
544 | pcm->private_free = line6_cleanup_pcm; | ||
545 | |||
546 | err = line6_create_audio_out_urbs(line6pcm); | ||
547 | if (err < 0) | ||
548 | return err; | ||
549 | |||
550 | err = line6_create_audio_in_urbs(line6pcm); | ||
551 | if (err < 0) | ||
552 | return err; | ||
553 | |||
554 | /* mixer: */ | ||
555 | for (i = 0; i < ARRAY_SIZE(line6_controls); i++) { | ||
556 | err = snd_ctl_add(line6->card, | ||
557 | snd_ctl_new1(&line6_controls[i], line6pcm)); | ||
558 | if (err < 0) | ||
559 | return err; | ||
560 | } | ||
561 | |||
562 | return 0; | ||
563 | } | ||
564 | EXPORT_SYMBOL_GPL(line6_init_pcm); | ||
565 | |||
566 | /* prepare pcm callback */ | ||
567 | int snd_line6_prepare(struct snd_pcm_substream *substream) | ||
568 | { | ||
569 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | ||
570 | struct line6_pcm_stream *pstr = get_stream(line6pcm, substream->stream); | ||
571 | |||
572 | mutex_lock(&line6pcm->state_mutex); | ||
573 | if (!pstr->running) | ||
574 | line6_wait_clear_audio_urbs(line6pcm, pstr); | ||
575 | |||
576 | if (!test_and_set_bit(LINE6_FLAG_PREPARED, &line6pcm->flags)) { | ||
577 | line6pcm->out.count = 0; | ||
578 | line6pcm->out.pos = 0; | ||
579 | line6pcm->out.pos_done = 0; | ||
580 | line6pcm->out.bytes = 0; | ||
581 | line6pcm->in.count = 0; | ||
582 | line6pcm->in.pos_done = 0; | ||
583 | line6pcm->in.bytes = 0; | ||
584 | } | ||
585 | |||
586 | mutex_unlock(&line6pcm->state_mutex); | ||
587 | return 0; | ||
588 | } | ||