regmap: add helper macro to set min/max range of register
[deliverable/linux.git] / sound / usb / usx2y / usx2yhwdeppcm.c
1 /*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15 */
16
17 /* USX2Y "rawusb" aka hwdep_pcm implementation
18
19 Its usb's unableness to atomically handle power of 2 period sized data chuncs
20 at standard samplerates,
21 what led to this part of the usx2y module:
22 It provides the alsa kernel half of the usx2y-alsa-jack driver pair.
23 The pair uses a hardware dependent alsa-device for mmaped pcm transport.
24 Advantage achieved:
25 The usb_hc moves pcm data from/into memory via DMA.
26 That memory is mmaped by jack's usx2y driver.
27 Jack's usx2y driver is the first/last to read/write pcm data.
28 Read/write is a combination of power of 2 period shaping and
29 float/int conversation.
30 Compared to mainline alsa/jack we leave out power of 2 period shaping inside
31 snd-usb-usx2y which needs memcpy() and additional buffers.
32 As a side effect possible unwanted pcm-data coruption resulting of
33 standard alsa's snd-usb-usx2y period shaping scheme falls away.
34 Result is sane jack operation at buffering schemes down to 128frames,
35 2 periods.
36 plain usx2y alsa mode is able to achieve 64frames, 4periods, but only at the
37 cost of easier triggered i.e. aeolus xruns (128 or 256frames,
38 2periods works but is useless cause of crackling).
39
40 This is a first "proof of concept" implementation.
41 Later, functionalities should migrate to more appropriate places:
42 Userland:
43 - The jackd could mmap its float-pcm buffers directly from alsa-lib.
44 - alsa-lib could provide power of 2 period sized shaping combined with int/float
45 conversation.
46 Currently the usx2y jack driver provides above 2 services.
47 Kernel:
48 - rawusb dma pcm buffer transport should go to snd-usb-lib, so also snd-usb-audio
49 devices can use it.
50 Currently rawusb dma pcm buffer transport (this file) is only available to snd-usb-usx2y.
51 */
52
53 #include <linux/delay.h>
54 #include <linux/gfp.h>
55 #include "usbusx2yaudio.c"
56
57 #if defined(USX2Y_NRPACKS_VARIABLE) || USX2Y_NRPACKS == 1
58
59 #include <sound/hwdep.h>
60
61
62 static int usX2Y_usbpcm_urb_capt_retire(struct snd_usX2Y_substream *subs)
63 {
64 struct urb *urb = subs->completed_urb;
65 struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
66 int i, lens = 0, hwptr_done = subs->hwptr_done;
67 struct usX2Ydev *usX2Y = subs->usX2Y;
68 if (0 > usX2Y->hwdep_pcm_shm->capture_iso_start) { //FIXME
69 int head = usX2Y->hwdep_pcm_shm->captured_iso_head + 1;
70 if (head >= ARRAY_SIZE(usX2Y->hwdep_pcm_shm->captured_iso))
71 head = 0;
72 usX2Y->hwdep_pcm_shm->capture_iso_start = head;
73 snd_printdd("cap start %i\n", head);
74 }
75 for (i = 0; i < nr_of_packs(); i++) {
76 if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */
77 snd_printk(KERN_ERR "active frame status %i. Most probably some hardware problem.\n", urb->iso_frame_desc[i].status);
78 return urb->iso_frame_desc[i].status;
79 }
80 lens += urb->iso_frame_desc[i].actual_length / usX2Y->stride;
81 }
82 if ((hwptr_done += lens) >= runtime->buffer_size)
83 hwptr_done -= runtime->buffer_size;
84 subs->hwptr_done = hwptr_done;
85 subs->transfer_done += lens;
86 /* update the pointer, call callback if necessary */
87 if (subs->transfer_done >= runtime->period_size) {
88 subs->transfer_done -= runtime->period_size;
89 snd_pcm_period_elapsed(subs->pcm_substream);
90 }
91 return 0;
92 }
93
94 static inline int usX2Y_iso_frames_per_buffer(struct snd_pcm_runtime *runtime,
95 struct usX2Ydev * usX2Y)
96 {
97 return (runtime->buffer_size * 1000) / usX2Y->rate + 1; //FIXME: so far only correct period_size == 2^x ?
98 }
99
100 /*
101 * prepare urb for playback data pipe
102 *
103 * we copy the data directly from the pcm buffer.
104 * the current position to be copied is held in hwptr field.
105 * since a urb can handle only a single linear buffer, if the total
106 * transferred area overflows the buffer boundary, we cannot send
107 * it directly from the buffer. thus the data is once copied to
108 * a temporary buffer and urb points to that.
109 */
110 static int usX2Y_hwdep_urb_play_prepare(struct snd_usX2Y_substream *subs,
111 struct urb *urb)
112 {
113 int count, counts, pack;
114 struct usX2Ydev *usX2Y = subs->usX2Y;
115 struct snd_usX2Y_hwdep_pcm_shm *shm = usX2Y->hwdep_pcm_shm;
116 struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
117
118 if (0 > shm->playback_iso_start) {
119 shm->playback_iso_start = shm->captured_iso_head -
120 usX2Y_iso_frames_per_buffer(runtime, usX2Y);
121 if (0 > shm->playback_iso_start)
122 shm->playback_iso_start += ARRAY_SIZE(shm->captured_iso);
123 shm->playback_iso_head = shm->playback_iso_start;
124 }
125
126 count = 0;
127 for (pack = 0; pack < nr_of_packs(); pack++) {
128 /* calculate the size of a packet */
129 counts = shm->captured_iso[shm->playback_iso_head].length / usX2Y->stride;
130 if (counts < 43 || counts > 50) {
131 snd_printk(KERN_ERR "should not be here with counts=%i\n", counts);
132 return -EPIPE;
133 }
134 /* set up descriptor */
135 urb->iso_frame_desc[pack].offset = shm->captured_iso[shm->playback_iso_head].offset;
136 urb->iso_frame_desc[pack].length = shm->captured_iso[shm->playback_iso_head].length;
137 if (atomic_read(&subs->state) != state_RUNNING)
138 memset((char *)urb->transfer_buffer + urb->iso_frame_desc[pack].offset, 0,
139 urb->iso_frame_desc[pack].length);
140 if (++shm->playback_iso_head >= ARRAY_SIZE(shm->captured_iso))
141 shm->playback_iso_head = 0;
142 count += counts;
143 }
144 urb->transfer_buffer_length = count * usX2Y->stride;
145 return 0;
146 }
147
148
149 static inline void usX2Y_usbpcm_urb_capt_iso_advance(struct snd_usX2Y_substream *subs,
150 struct urb *urb)
151 {
152 int pack;
153 for (pack = 0; pack < nr_of_packs(); ++pack) {
154 struct usb_iso_packet_descriptor *desc = urb->iso_frame_desc + pack;
155 if (NULL != subs) {
156 struct snd_usX2Y_hwdep_pcm_shm *shm = subs->usX2Y->hwdep_pcm_shm;
157 int head = shm->captured_iso_head + 1;
158 if (head >= ARRAY_SIZE(shm->captured_iso))
159 head = 0;
160 shm->captured_iso[head].frame = urb->start_frame + pack;
161 shm->captured_iso[head].offset = desc->offset;
162 shm->captured_iso[head].length = desc->actual_length;
163 shm->captured_iso_head = head;
164 shm->captured_iso_frames++;
165 }
166 if ((desc->offset += desc->length * NRURBS*nr_of_packs()) +
167 desc->length >= SSS)
168 desc->offset -= (SSS - desc->length);
169 }
170 }
171
172 static inline int usX2Y_usbpcm_usbframe_complete(struct snd_usX2Y_substream *capsubs,
173 struct snd_usX2Y_substream *capsubs2,
174 struct snd_usX2Y_substream *playbacksubs,
175 int frame)
176 {
177 int err, state;
178 struct urb *urb = playbacksubs->completed_urb;
179
180 state = atomic_read(&playbacksubs->state);
181 if (NULL != urb) {
182 if (state == state_RUNNING)
183 usX2Y_urb_play_retire(playbacksubs, urb);
184 else if (state >= state_PRERUNNING)
185 atomic_inc(&playbacksubs->state);
186 } else {
187 switch (state) {
188 case state_STARTING1:
189 urb = playbacksubs->urb[0];
190 atomic_inc(&playbacksubs->state);
191 break;
192 case state_STARTING2:
193 urb = playbacksubs->urb[1];
194 atomic_inc(&playbacksubs->state);
195 break;
196 }
197 }
198 if (urb) {
199 if ((err = usX2Y_hwdep_urb_play_prepare(playbacksubs, urb)) ||
200 (err = usX2Y_urb_submit(playbacksubs, urb, frame))) {
201 return err;
202 }
203 }
204
205 playbacksubs->completed_urb = NULL;
206
207 state = atomic_read(&capsubs->state);
208 if (state >= state_PREPARED) {
209 if (state == state_RUNNING) {
210 if ((err = usX2Y_usbpcm_urb_capt_retire(capsubs)))
211 return err;
212 } else if (state >= state_PRERUNNING)
213 atomic_inc(&capsubs->state);
214 usX2Y_usbpcm_urb_capt_iso_advance(capsubs, capsubs->completed_urb);
215 if (NULL != capsubs2)
216 usX2Y_usbpcm_urb_capt_iso_advance(NULL, capsubs2->completed_urb);
217 if ((err = usX2Y_urb_submit(capsubs, capsubs->completed_urb, frame)))
218 return err;
219 if (NULL != capsubs2)
220 if ((err = usX2Y_urb_submit(capsubs2, capsubs2->completed_urb, frame)))
221 return err;
222 }
223 capsubs->completed_urb = NULL;
224 if (NULL != capsubs2)
225 capsubs2->completed_urb = NULL;
226 return 0;
227 }
228
229
230 static void i_usX2Y_usbpcm_urb_complete(struct urb *urb)
231 {
232 struct snd_usX2Y_substream *subs = urb->context;
233 struct usX2Ydev *usX2Y = subs->usX2Y;
234 struct snd_usX2Y_substream *capsubs, *capsubs2, *playbacksubs;
235
236 if (unlikely(atomic_read(&subs->state) < state_PREPARED)) {
237 snd_printdd("hcd_frame=%i ep=%i%s status=%i start_frame=%i\n",
238 usb_get_current_frame_number(usX2Y->dev),
239 subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out",
240 urb->status, urb->start_frame);
241 return;
242 }
243 if (unlikely(urb->status)) {
244 usX2Y_error_urb_status(usX2Y, subs, urb);
245 return;
246 }
247 if (likely((urb->start_frame & 0xFFFF) == (usX2Y->wait_iso_frame & 0xFFFF)))
248 subs->completed_urb = urb;
249 else {
250 usX2Y_error_sequence(usX2Y, subs, urb);
251 return;
252 }
253
254 capsubs = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
255 capsubs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
256 playbacksubs = usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
257 if (capsubs->completed_urb && atomic_read(&capsubs->state) >= state_PREPARED &&
258 (NULL == capsubs2 || capsubs2->completed_urb) &&
259 (playbacksubs->completed_urb || atomic_read(&playbacksubs->state) < state_PREPARED)) {
260 if (!usX2Y_usbpcm_usbframe_complete(capsubs, capsubs2, playbacksubs, urb->start_frame))
261 usX2Y->wait_iso_frame += nr_of_packs();
262 else {
263 snd_printdd("\n");
264 usX2Y_clients_stop(usX2Y);
265 }
266 }
267 }
268
269
270 static void usX2Y_hwdep_urb_release(struct urb **urb)
271 {
272 usb_kill_urb(*urb);
273 usb_free_urb(*urb);
274 *urb = NULL;
275 }
276
277 /*
278 * release a substream
279 */
280 static void usX2Y_usbpcm_urbs_release(struct snd_usX2Y_substream *subs)
281 {
282 int i;
283 snd_printdd("snd_usX2Y_urbs_release() %i\n", subs->endpoint);
284 for (i = 0; i < NRURBS; i++)
285 usX2Y_hwdep_urb_release(subs->urb + i);
286 }
287
288 static void usX2Y_usbpcm_subs_startup_finish(struct usX2Ydev * usX2Y)
289 {
290 usX2Y_urbs_set_complete(usX2Y, i_usX2Y_usbpcm_urb_complete);
291 usX2Y->prepare_subs = NULL;
292 }
293
294 static void i_usX2Y_usbpcm_subs_startup(struct urb *urb)
295 {
296 struct snd_usX2Y_substream *subs = urb->context;
297 struct usX2Ydev *usX2Y = subs->usX2Y;
298 struct snd_usX2Y_substream *prepare_subs = usX2Y->prepare_subs;
299 if (NULL != prepare_subs &&
300 urb->start_frame == prepare_subs->urb[0]->start_frame) {
301 atomic_inc(&prepare_subs->state);
302 if (prepare_subs == usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE]) {
303 struct snd_usX2Y_substream *cap_subs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
304 if (cap_subs2 != NULL)
305 atomic_inc(&cap_subs2->state);
306 }
307 usX2Y_usbpcm_subs_startup_finish(usX2Y);
308 wake_up(&usX2Y->prepare_wait_queue);
309 }
310
311 i_usX2Y_usbpcm_urb_complete(urb);
312 }
313
314 /*
315 * initialize a substream's urbs
316 */
317 static int usX2Y_usbpcm_urbs_allocate(struct snd_usX2Y_substream *subs)
318 {
319 int i;
320 unsigned int pipe;
321 int is_playback = subs == subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
322 struct usb_device *dev = subs->usX2Y->dev;
323
324 pipe = is_playback ? usb_sndisocpipe(dev, subs->endpoint) :
325 usb_rcvisocpipe(dev, subs->endpoint);
326 subs->maxpacksize = usb_maxpacket(dev, pipe, is_playback);
327 if (!subs->maxpacksize)
328 return -EINVAL;
329
330 /* allocate and initialize data urbs */
331 for (i = 0; i < NRURBS; i++) {
332 struct urb **purb = subs->urb + i;
333 if (*purb) {
334 usb_kill_urb(*purb);
335 continue;
336 }
337 *purb = usb_alloc_urb(nr_of_packs(), GFP_KERNEL);
338 if (NULL == *purb) {
339 usX2Y_usbpcm_urbs_release(subs);
340 return -ENOMEM;
341 }
342 (*purb)->transfer_buffer = is_playback ?
343 subs->usX2Y->hwdep_pcm_shm->playback : (
344 subs->endpoint == 0x8 ?
345 subs->usX2Y->hwdep_pcm_shm->capture0x8 :
346 subs->usX2Y->hwdep_pcm_shm->capture0xA);
347
348 (*purb)->dev = dev;
349 (*purb)->pipe = pipe;
350 (*purb)->number_of_packets = nr_of_packs();
351 (*purb)->context = subs;
352 (*purb)->interval = 1;
353 (*purb)->complete = i_usX2Y_usbpcm_subs_startup;
354 }
355 return 0;
356 }
357
358 /*
359 * free the buffer
360 */
361 static int snd_usX2Y_usbpcm_hw_free(struct snd_pcm_substream *substream)
362 {
363 struct snd_pcm_runtime *runtime = substream->runtime;
364 struct snd_usX2Y_substream *subs = runtime->private_data,
365 *cap_subs2 = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
366 mutex_lock(&subs->usX2Y->prepare_mutex);
367 snd_printdd("snd_usX2Y_usbpcm_hw_free(%p)\n", substream);
368
369 if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) {
370 struct snd_usX2Y_substream *cap_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
371 atomic_set(&subs->state, state_STOPPED);
372 usX2Y_usbpcm_urbs_release(subs);
373 if (!cap_subs->pcm_substream ||
374 !cap_subs->pcm_substream->runtime ||
375 !cap_subs->pcm_substream->runtime->status ||
376 cap_subs->pcm_substream->runtime->status->state < SNDRV_PCM_STATE_PREPARED) {
377 atomic_set(&cap_subs->state, state_STOPPED);
378 if (NULL != cap_subs2)
379 atomic_set(&cap_subs2->state, state_STOPPED);
380 usX2Y_usbpcm_urbs_release(cap_subs);
381 if (NULL != cap_subs2)
382 usX2Y_usbpcm_urbs_release(cap_subs2);
383 }
384 } else {
385 struct snd_usX2Y_substream *playback_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
386 if (atomic_read(&playback_subs->state) < state_PREPARED) {
387 atomic_set(&subs->state, state_STOPPED);
388 if (NULL != cap_subs2)
389 atomic_set(&cap_subs2->state, state_STOPPED);
390 usX2Y_usbpcm_urbs_release(subs);
391 if (NULL != cap_subs2)
392 usX2Y_usbpcm_urbs_release(cap_subs2);
393 }
394 }
395 mutex_unlock(&subs->usX2Y->prepare_mutex);
396 return snd_pcm_lib_free_pages(substream);
397 }
398
399 static void usX2Y_usbpcm_subs_startup(struct snd_usX2Y_substream *subs)
400 {
401 struct usX2Ydev * usX2Y = subs->usX2Y;
402 usX2Y->prepare_subs = subs;
403 subs->urb[0]->start_frame = -1;
404 smp_wmb(); // Make sure above modifications are seen by i_usX2Y_subs_startup()
405 usX2Y_urbs_set_complete(usX2Y, i_usX2Y_usbpcm_subs_startup);
406 }
407
408 static int usX2Y_usbpcm_urbs_start(struct snd_usX2Y_substream *subs)
409 {
410 int p, u, err,
411 stream = subs->pcm_substream->stream;
412 struct usX2Ydev *usX2Y = subs->usX2Y;
413
414 if (SNDRV_PCM_STREAM_CAPTURE == stream) {
415 usX2Y->hwdep_pcm_shm->captured_iso_head = -1;
416 usX2Y->hwdep_pcm_shm->captured_iso_frames = 0;
417 }
418
419 for (p = 0; 3 >= (stream + p); p += 2) {
420 struct snd_usX2Y_substream *subs = usX2Y->subs[stream + p];
421 if (subs != NULL) {
422 if ((err = usX2Y_usbpcm_urbs_allocate(subs)) < 0)
423 return err;
424 subs->completed_urb = NULL;
425 }
426 }
427
428 for (p = 0; p < 4; p++) {
429 struct snd_usX2Y_substream *subs = usX2Y->subs[p];
430 if (subs != NULL && atomic_read(&subs->state) >= state_PREPARED)
431 goto start;
432 }
433
434 start:
435 usX2Y_usbpcm_subs_startup(subs);
436 for (u = 0; u < NRURBS; u++) {
437 for (p = 0; 3 >= (stream + p); p += 2) {
438 struct snd_usX2Y_substream *subs = usX2Y->subs[stream + p];
439 if (subs != NULL) {
440 struct urb *urb = subs->urb[u];
441 if (usb_pipein(urb->pipe)) {
442 unsigned long pack;
443 if (0 == u)
444 atomic_set(&subs->state, state_STARTING3);
445 urb->dev = usX2Y->dev;
446 for (pack = 0; pack < nr_of_packs(); pack++) {
447 urb->iso_frame_desc[pack].offset = subs->maxpacksize * (pack + u * nr_of_packs());
448 urb->iso_frame_desc[pack].length = subs->maxpacksize;
449 }
450 urb->transfer_buffer_length = subs->maxpacksize * nr_of_packs();
451 if ((err = usb_submit_urb(urb, GFP_KERNEL)) < 0) {
452 snd_printk (KERN_ERR "cannot usb_submit_urb() for urb %d, err = %d\n", u, err);
453 err = -EPIPE;
454 goto cleanup;
455 } else {
456 snd_printdd("%i\n", urb->start_frame);
457 if (u == 0)
458 usX2Y->wait_iso_frame = urb->start_frame;
459 }
460 urb->transfer_flags = 0;
461 } else {
462 atomic_set(&subs->state, state_STARTING1);
463 break;
464 }
465 }
466 }
467 }
468 err = 0;
469 wait_event(usX2Y->prepare_wait_queue, NULL == usX2Y->prepare_subs);
470 if (atomic_read(&subs->state) != state_PREPARED)
471 err = -EPIPE;
472
473 cleanup:
474 if (err) {
475 usX2Y_subs_startup_finish(usX2Y); // Call it now
476 usX2Y_clients_stop(usX2Y); // something is completely wroong > stop evrything
477 }
478 return err;
479 }
480
481 /*
482 * prepare callback
483 *
484 * set format and initialize urbs
485 */
486 static int snd_usX2Y_usbpcm_prepare(struct snd_pcm_substream *substream)
487 {
488 struct snd_pcm_runtime *runtime = substream->runtime;
489 struct snd_usX2Y_substream *subs = runtime->private_data;
490 struct usX2Ydev *usX2Y = subs->usX2Y;
491 struct snd_usX2Y_substream *capsubs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
492 int err = 0;
493 snd_printdd("snd_usX2Y_pcm_prepare(%p)\n", substream);
494
495 if (NULL == usX2Y->hwdep_pcm_shm) {
496 if (NULL == (usX2Y->hwdep_pcm_shm = snd_malloc_pages(sizeof(struct snd_usX2Y_hwdep_pcm_shm), GFP_KERNEL)))
497 return -ENOMEM;
498 memset(usX2Y->hwdep_pcm_shm, 0, sizeof(struct snd_usX2Y_hwdep_pcm_shm));
499 }
500
501 mutex_lock(&usX2Y->prepare_mutex);
502 usX2Y_subs_prepare(subs);
503 // Start hardware streams
504 // SyncStream first....
505 if (atomic_read(&capsubs->state) < state_PREPARED) {
506 if (usX2Y->format != runtime->format)
507 if ((err = usX2Y_format_set(usX2Y, runtime->format)) < 0)
508 goto up_prepare_mutex;
509 if (usX2Y->rate != runtime->rate)
510 if ((err = usX2Y_rate_set(usX2Y, runtime->rate)) < 0)
511 goto up_prepare_mutex;
512 snd_printdd("starting capture pipe for %s\n", subs == capsubs ?
513 "self" : "playpipe");
514 if (0 > (err = usX2Y_usbpcm_urbs_start(capsubs)))
515 goto up_prepare_mutex;
516 }
517
518 if (subs != capsubs) {
519 usX2Y->hwdep_pcm_shm->playback_iso_start = -1;
520 if (atomic_read(&subs->state) < state_PREPARED) {
521 while (usX2Y_iso_frames_per_buffer(runtime, usX2Y) >
522 usX2Y->hwdep_pcm_shm->captured_iso_frames) {
523 snd_printdd("Wait: iso_frames_per_buffer=%i,"
524 "captured_iso_frames=%i\n",
525 usX2Y_iso_frames_per_buffer(runtime, usX2Y),
526 usX2Y->hwdep_pcm_shm->captured_iso_frames);
527 if (msleep_interruptible(10)) {
528 err = -ERESTARTSYS;
529 goto up_prepare_mutex;
530 }
531 }
532 if (0 > (err = usX2Y_usbpcm_urbs_start(subs)))
533 goto up_prepare_mutex;
534 }
535 snd_printdd("Ready: iso_frames_per_buffer=%i,captured_iso_frames=%i\n",
536 usX2Y_iso_frames_per_buffer(runtime, usX2Y),
537 usX2Y->hwdep_pcm_shm->captured_iso_frames);
538 } else
539 usX2Y->hwdep_pcm_shm->capture_iso_start = -1;
540
541 up_prepare_mutex:
542 mutex_unlock(&usX2Y->prepare_mutex);
543 return err;
544 }
545
546 static struct snd_pcm_hardware snd_usX2Y_4c =
547 {
548 .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
549 SNDRV_PCM_INFO_BLOCK_TRANSFER |
550 SNDRV_PCM_INFO_MMAP_VALID),
551 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE,
552 .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
553 .rate_min = 44100,
554 .rate_max = 48000,
555 .channels_min = 2,
556 .channels_max = 4,
557 .buffer_bytes_max = (2*128*1024),
558 .period_bytes_min = 64,
559 .period_bytes_max = (128*1024),
560 .periods_min = 2,
561 .periods_max = 1024,
562 .fifo_size = 0
563 };
564
565
566
567 static int snd_usX2Y_usbpcm_open(struct snd_pcm_substream *substream)
568 {
569 struct snd_usX2Y_substream *subs = ((struct snd_usX2Y_substream **)
570 snd_pcm_substream_chip(substream))[substream->stream];
571 struct snd_pcm_runtime *runtime = substream->runtime;
572
573 if (!(subs->usX2Y->chip_status & USX2Y_STAT_CHIP_MMAP_PCM_URBS))
574 return -EBUSY;
575
576 runtime->hw = SNDRV_PCM_STREAM_PLAYBACK == substream->stream ? snd_usX2Y_2c :
577 (subs->usX2Y->subs[3] ? snd_usX2Y_4c : snd_usX2Y_2c);
578 runtime->private_data = subs;
579 subs->pcm_substream = substream;
580 snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 1000, 200000);
581 return 0;
582 }
583
584
585 static int snd_usX2Y_usbpcm_close(struct snd_pcm_substream *substream)
586 {
587 struct snd_pcm_runtime *runtime = substream->runtime;
588 struct snd_usX2Y_substream *subs = runtime->private_data;
589
590 subs->pcm_substream = NULL;
591 return 0;
592 }
593
594
595 static struct snd_pcm_ops snd_usX2Y_usbpcm_ops =
596 {
597 .open = snd_usX2Y_usbpcm_open,
598 .close = snd_usX2Y_usbpcm_close,
599 .ioctl = snd_pcm_lib_ioctl,
600 .hw_params = snd_usX2Y_pcm_hw_params,
601 .hw_free = snd_usX2Y_usbpcm_hw_free,
602 .prepare = snd_usX2Y_usbpcm_prepare,
603 .trigger = snd_usX2Y_pcm_trigger,
604 .pointer = snd_usX2Y_pcm_pointer,
605 };
606
607
608 static int usX2Y_pcms_lock_check(struct snd_card *card)
609 {
610 struct list_head *list;
611 struct snd_device *dev;
612 struct snd_pcm *pcm;
613 int err = 0;
614 list_for_each(list, &card->devices) {
615 dev = snd_device(list);
616 if (dev->type != SNDRV_DEV_PCM)
617 continue;
618 pcm = dev->device_data;
619 mutex_lock(&pcm->open_mutex);
620 }
621 list_for_each(list, &card->devices) {
622 int s;
623 dev = snd_device(list);
624 if (dev->type != SNDRV_DEV_PCM)
625 continue;
626 pcm = dev->device_data;
627 for (s = 0; s < 2; ++s) {
628 struct snd_pcm_substream *substream;
629 substream = pcm->streams[s].substream;
630 if (substream && SUBSTREAM_BUSY(substream))
631 err = -EBUSY;
632 }
633 }
634 return err;
635 }
636
637
638 static void usX2Y_pcms_unlock(struct snd_card *card)
639 {
640 struct list_head *list;
641 struct snd_device *dev;
642 struct snd_pcm *pcm;
643 list_for_each(list, &card->devices) {
644 dev = snd_device(list);
645 if (dev->type != SNDRV_DEV_PCM)
646 continue;
647 pcm = dev->device_data;
648 mutex_unlock(&pcm->open_mutex);
649 }
650 }
651
652
653 static int snd_usX2Y_hwdep_pcm_open(struct snd_hwdep *hw, struct file *file)
654 {
655 // we need to be the first
656 struct snd_card *card = hw->card;
657 int err = usX2Y_pcms_lock_check(card);
658 if (0 == err)
659 usX2Y(card)->chip_status |= USX2Y_STAT_CHIP_MMAP_PCM_URBS;
660 usX2Y_pcms_unlock(card);
661 return err;
662 }
663
664
665 static int snd_usX2Y_hwdep_pcm_release(struct snd_hwdep *hw, struct file *file)
666 {
667 struct snd_card *card = hw->card;
668 int err = usX2Y_pcms_lock_check(card);
669 if (0 == err)
670 usX2Y(hw->card)->chip_status &= ~USX2Y_STAT_CHIP_MMAP_PCM_URBS;
671 usX2Y_pcms_unlock(card);
672 return err;
673 }
674
675
676 static void snd_usX2Y_hwdep_pcm_vm_open(struct vm_area_struct *area)
677 {
678 }
679
680
681 static void snd_usX2Y_hwdep_pcm_vm_close(struct vm_area_struct *area)
682 {
683 }
684
685
686 static int snd_usX2Y_hwdep_pcm_vm_fault(struct vm_area_struct *area,
687 struct vm_fault *vmf)
688 {
689 unsigned long offset;
690 void *vaddr;
691
692 offset = vmf->pgoff << PAGE_SHIFT;
693 vaddr = (char*)((struct usX2Ydev *)area->vm_private_data)->hwdep_pcm_shm + offset;
694 vmf->page = virt_to_page(vaddr);
695 get_page(vmf->page);
696 return 0;
697 }
698
699
700 static const struct vm_operations_struct snd_usX2Y_hwdep_pcm_vm_ops = {
701 .open = snd_usX2Y_hwdep_pcm_vm_open,
702 .close = snd_usX2Y_hwdep_pcm_vm_close,
703 .fault = snd_usX2Y_hwdep_pcm_vm_fault,
704 };
705
706
707 static int snd_usX2Y_hwdep_pcm_mmap(struct snd_hwdep * hw, struct file *filp, struct vm_area_struct *area)
708 {
709 unsigned long size = (unsigned long)(area->vm_end - area->vm_start);
710 struct usX2Ydev *usX2Y = hw->private_data;
711
712 if (!(usX2Y->chip_status & USX2Y_STAT_CHIP_INIT))
713 return -EBUSY;
714
715 /* if userspace tries to mmap beyond end of our buffer, fail */
716 if (size > PAGE_ALIGN(sizeof(struct snd_usX2Y_hwdep_pcm_shm))) {
717 snd_printd("%lu > %lu\n", size, (unsigned long)sizeof(struct snd_usX2Y_hwdep_pcm_shm));
718 return -EINVAL;
719 }
720
721 if (!usX2Y->hwdep_pcm_shm) {
722 return -ENODEV;
723 }
724 area->vm_ops = &snd_usX2Y_hwdep_pcm_vm_ops;
725 area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
726 area->vm_private_data = hw->private_data;
727 return 0;
728 }
729
730
731 static void snd_usX2Y_hwdep_pcm_private_free(struct snd_hwdep *hwdep)
732 {
733 struct usX2Ydev *usX2Y = hwdep->private_data;
734 if (NULL != usX2Y->hwdep_pcm_shm)
735 snd_free_pages(usX2Y->hwdep_pcm_shm, sizeof(struct snd_usX2Y_hwdep_pcm_shm));
736 }
737
738
739 int usX2Y_hwdep_pcm_new(struct snd_card *card)
740 {
741 int err;
742 struct snd_hwdep *hw;
743 struct snd_pcm *pcm;
744 struct usb_device *dev = usX2Y(card)->dev;
745 if (1 != nr_of_packs())
746 return 0;
747
748 if ((err = snd_hwdep_new(card, SND_USX2Y_USBPCM_ID, 1, &hw)) < 0)
749 return err;
750
751 hw->iface = SNDRV_HWDEP_IFACE_USX2Y_PCM;
752 hw->private_data = usX2Y(card);
753 hw->private_free = snd_usX2Y_hwdep_pcm_private_free;
754 hw->ops.open = snd_usX2Y_hwdep_pcm_open;
755 hw->ops.release = snd_usX2Y_hwdep_pcm_release;
756 hw->ops.mmap = snd_usX2Y_hwdep_pcm_mmap;
757 hw->exclusive = 1;
758 sprintf(hw->name, "/proc/bus/usb/%03d/%03d/hwdeppcm", dev->bus->busnum, dev->devnum);
759
760 err = snd_pcm_new(card, NAME_ALLCAPS" hwdep Audio", 2, 1, 1, &pcm);
761 if (err < 0) {
762 return err;
763 }
764 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usX2Y_usbpcm_ops);
765 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usX2Y_usbpcm_ops);
766
767 pcm->private_data = usX2Y(card)->subs;
768 pcm->info_flags = 0;
769
770 sprintf(pcm->name, NAME_ALLCAPS" hwdep Audio");
771 if (0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
772 SNDRV_DMA_TYPE_CONTINUOUS,
773 snd_dma_continuous_data(GFP_KERNEL),
774 64*1024, 128*1024)) ||
775 0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
776 SNDRV_DMA_TYPE_CONTINUOUS,
777 snd_dma_continuous_data(GFP_KERNEL),
778 64*1024, 128*1024))) {
779 return err;
780 }
781
782
783 return 0;
784 }
785
786 #else
787
788 int usX2Y_hwdep_pcm_new(struct snd_card *card)
789 {
790 return 0;
791 }
792
793 #endif
This page took 0.064431 seconds and 5 git commands to generate.