aboutsummaryrefslogtreecommitdiffstats
path: root/sound/usb/pcm.c
diff options
context:
space:
mode:
authorDaniel Mack <zonque@gmail.com>2012-04-12 07:51:14 -0400
committerTakashi Iwai <tiwai@suse.de>2012-04-13 04:24:32 -0400
commitc75a8a7ae565d7cd9baa87a504ba9162e355b4b0 (patch)
tree19d3158213cbdbec05a901a5e253871991a2609a /sound/usb/pcm.c
parentd399ff9593e088d33fb38f5206c6427825892baa (diff)
ALSA: snd-usb: add support for implicit feedback
Implicit feedback is a streaming mode that does not rely on dedicated sync endpoints but uses the information provided by record streams to clock output streams. Now that the streaming logic is decoupled from the PCM streams, this is easy to implement. Signed-off-by: Daniel Mack <zonque@gmail.com> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/usb/pcm.c')
-rw-r--r--sound/usb/pcm.c37
1 files changed, 26 insertions, 11 deletions
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 0f107834c100..2d3a04d829b7 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -301,7 +301,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
301 struct usb_interface *iface; 301 struct usb_interface *iface;
302 unsigned int ep, attr; 302 unsigned int ep, attr;
303 int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK; 303 int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
304 int err; 304 int err, implicit_fb = 0;
305 305
306 iface = usb_ifnum_to_if(dev, fmt->iface); 306 iface = usb_ifnum_to_if(dev, fmt->iface);
307 if (WARN_ON(!iface)) 307 if (WARN_ON(!iface))
@@ -326,25 +326,34 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
326 * assume it as adaptive-out or sync-in. 326 * assume it as adaptive-out or sync-in.
327 */ 327 */
328 attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE; 328 attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE;
329 if (((is_playback && attr == USB_ENDPOINT_SYNC_ASYNC) || 329
330 (! is_playback && attr == USB_ENDPOINT_SYNC_ADAPTIVE)) && 330 switch (subs->stream->chip->usb_id) {
331 altsd->bNumEndpoints >= 2) { 331 case USB_ID(0x0763, 0x2080): /* M-Audio FastTrack Ultra */
332 switch (subs->stream->chip->usb_id) { 332 case USB_ID(0x0763, 0x2081):
333 case USB_ID(0x0763, 0x2080): /* M-Audio FastTrack Ultra */ 333 if (is_playback) {
334 case USB_ID(0x0763, 0x2081): 334 implicit_fb = 1;
335 ep = 0x81; 335 ep = 0x81;
336 iface = usb_ifnum_to_if(dev, 2); 336 iface = usb_ifnum_to_if(dev, 2);
337
338 if (!iface || iface->num_altsetting == 0)
339 return -EINVAL;
340
337 alts = &iface->altsetting[1]; 341 alts = &iface->altsetting[1];
338 goto add_sync_ep; 342 goto add_sync_ep;
339 } 343 }
344 }
340 345
346 if (((is_playback && attr == USB_ENDPOINT_SYNC_ASYNC) ||
347 (!is_playback && attr == USB_ENDPOINT_SYNC_ADAPTIVE)) &&
348 altsd->bNumEndpoints >= 2) {
341 /* check sync-pipe endpoint */ 349 /* check sync-pipe endpoint */
342 /* ... and check descriptor size before accessing bSynchAddress 350 /* ... and check descriptor size before accessing bSynchAddress
343 because there is a version of the SB Audigy 2 NX firmware lacking 351 because there is a version of the SB Audigy 2 NX firmware lacking
344 the audio fields in the endpoint descriptors */ 352 the audio fields in the endpoint descriptors */
345 if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != 0x01 || 353 if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != 0x01 ||
346 (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && 354 (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
347 get_endpoint(alts, 1)->bSynchAddress != 0)) { 355 get_endpoint(alts, 1)->bSynchAddress != 0 &&
356 !implicit_fb)) {
348 snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n", 357 snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n",
349 dev->devnum, fmt->iface, fmt->altsetting); 358 dev->devnum, fmt->iface, fmt->altsetting);
350 return -EINVAL; 359 return -EINVAL;
@@ -352,16 +361,22 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
352 ep = get_endpoint(alts, 1)->bEndpointAddress; 361 ep = get_endpoint(alts, 1)->bEndpointAddress;
353 if (get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && 362 if (get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
354 (( is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) || 363 (( is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) ||
355 (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) { 364 (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)) ||
365 ( is_playback && !implicit_fb))) {
356 snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n", 366 snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n",
357 dev->devnum, fmt->iface, fmt->altsetting); 367 dev->devnum, fmt->iface, fmt->altsetting);
358 return -EINVAL; 368 return -EINVAL;
359 } 369 }
370
371 implicit_fb = (get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_USAGE_MASK)
372 == USB_ENDPOINT_USAGE_IMPLICIT_FB;
373
360add_sync_ep: 374add_sync_ep:
361 subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip, 375 subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip,
362 alts, ep, !subs->direction, 376 alts, ep, !subs->direction,
363 SND_USB_ENDPOINT_TYPE_SYNC); 377 implicit_fb ?
364 378 SND_USB_ENDPOINT_TYPE_DATA :
379 SND_USB_ENDPOINT_TYPE_SYNC);
365 if (!subs->sync_endpoint) 380 if (!subs->sync_endpoint)
366 return -EINVAL; 381 return -EINVAL;
367 382