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>
This commit is contained in:
parent
d399ff9593
commit
c75a8a7ae5
|
@ -301,7 +301,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
|
||||||
struct usb_interface *iface;
|
struct usb_interface *iface;
|
||||||
unsigned int ep, attr;
|
unsigned int ep, attr;
|
||||||
int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
|
int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
|
||||||
int err;
|
int err, implicit_fb = 0;
|
||||||
|
|
||||||
iface = usb_ifnum_to_if(dev, fmt->iface);
|
iface = usb_ifnum_to_if(dev, fmt->iface);
|
||||||
if (WARN_ON(!iface))
|
if (WARN_ON(!iface))
|
||||||
|
@ -326,25 +326,34 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
|
||||||
* assume it as adaptive-out or sync-in.
|
* assume it as adaptive-out or sync-in.
|
||||||
*/
|
*/
|
||||||
attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE;
|
attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE;
|
||||||
if (((is_playback && attr == USB_ENDPOINT_SYNC_ASYNC) ||
|
|
||||||
(! is_playback && attr == USB_ENDPOINT_SYNC_ADAPTIVE)) &&
|
|
||||||
altsd->bNumEndpoints >= 2) {
|
|
||||||
switch (subs->stream->chip->usb_id) {
|
switch (subs->stream->chip->usb_id) {
|
||||||
case USB_ID(0x0763, 0x2080): /* M-Audio FastTrack Ultra */
|
case USB_ID(0x0763, 0x2080): /* M-Audio FastTrack Ultra */
|
||||||
case USB_ID(0x0763, 0x2081):
|
case USB_ID(0x0763, 0x2081):
|
||||||
|
if (is_playback) {
|
||||||
|
implicit_fb = 1;
|
||||||
ep = 0x81;
|
ep = 0x81;
|
||||||
iface = usb_ifnum_to_if(dev, 2);
|
iface = usb_ifnum_to_if(dev, 2);
|
||||||
|
|
||||||
|
if (!iface || iface->num_altsetting == 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
alts = &iface->altsetting[1];
|
alts = &iface->altsetting[1];
|
||||||
goto add_sync_ep;
|
goto add_sync_ep;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (((is_playback && attr == USB_ENDPOINT_SYNC_ASYNC) ||
|
||||||
|
(!is_playback && attr == USB_ENDPOINT_SYNC_ADAPTIVE)) &&
|
||||||
|
altsd->bNumEndpoints >= 2) {
|
||||||
/* check sync-pipe endpoint */
|
/* check sync-pipe endpoint */
|
||||||
/* ... and check descriptor size before accessing bSynchAddress
|
/* ... and check descriptor size before accessing bSynchAddress
|
||||||
because there is a version of the SB Audigy 2 NX firmware lacking
|
because there is a version of the SB Audigy 2 NX firmware lacking
|
||||||
the audio fields in the endpoint descriptors */
|
the audio fields in the endpoint descriptors */
|
||||||
if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != 0x01 ||
|
if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != 0x01 ||
|
||||||
(get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
|
(get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
|
||||||
get_endpoint(alts, 1)->bSynchAddress != 0)) {
|
get_endpoint(alts, 1)->bSynchAddress != 0 &&
|
||||||
|
!implicit_fb)) {
|
||||||
snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n",
|
snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n",
|
||||||
dev->devnum, fmt->iface, fmt->altsetting);
|
dev->devnum, fmt->iface, fmt->altsetting);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -352,16 +361,22 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
|
||||||
ep = get_endpoint(alts, 1)->bEndpointAddress;
|
ep = get_endpoint(alts, 1)->bEndpointAddress;
|
||||||
if (get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
|
if (get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
|
||||||
(( is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) ||
|
(( is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) ||
|
||||||
(!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) {
|
(!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)) ||
|
||||||
|
( is_playback && !implicit_fb))) {
|
||||||
snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n",
|
snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n",
|
||||||
dev->devnum, fmt->iface, fmt->altsetting);
|
dev->devnum, fmt->iface, fmt->altsetting);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
implicit_fb = (get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_USAGE_MASK)
|
||||||
|
== USB_ENDPOINT_USAGE_IMPLICIT_FB;
|
||||||
|
|
||||||
add_sync_ep:
|
add_sync_ep:
|
||||||
subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip,
|
subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip,
|
||||||
alts, ep, !subs->direction,
|
alts, ep, !subs->direction,
|
||||||
|
implicit_fb ?
|
||||||
|
SND_USB_ENDPOINT_TYPE_DATA :
|
||||||
SND_USB_ENDPOINT_TYPE_SYNC);
|
SND_USB_ENDPOINT_TYPE_SYNC);
|
||||||
|
|
||||||
if (!subs->sync_endpoint)
|
if (!subs->sync_endpoint)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue