diff options
author | Clemens Ladisch <clemens@ladisch.de> | 2008-02-25 13:01:00 +0300 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2008-02-29 13:28:15 +0300 |
commit | d513202efd5bb9974545ef1c7f951467b21eb3a5 (patch) | |
tree | 56dd0f3aa631713717b6e0415d410be99432e7c9 /sound/usb/usbaudio.c | |
parent | ee47fd12d73706edb2a10efd05d5eed15b4d1e08 (diff) | |
download | linux-d513202efd5bb9974545ef1c7f951467b21eb3a5.tar.xz |
[ALSA] usb-audio: add workaround for broken E-Mu frequency feedback
Add a workaround for the feedback pipe of E-Mu 0202/0404 USB devices
that reports the number of samples per packet instead of the number of
samples per microframe.
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/usb/usbaudio.c')
-rw-r--r-- | sound/usb/usbaudio.c | 38 |
1 files changed, 36 insertions, 2 deletions
diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index 8fa935665702..675672f313be 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -479,6 +479,33 @@ static int retire_playback_sync_urb_hs(struct snd_usb_substream *subs, return 0; } +/* + * process after E-Mu 0202/0404 high speed playback sync complete + * + * These devices return the number of samples per packet instead of the number + * of samples per microframe. + */ +static int retire_playback_sync_urb_hs_emu(struct snd_usb_substream *subs, + struct snd_pcm_runtime *runtime, + struct urb *urb) +{ + unsigned int f; + unsigned long flags; + + if (urb->iso_frame_desc[0].status == 0 && + urb->iso_frame_desc[0].actual_length == 4) { + f = combine_quad((u8*)urb->transfer_buffer) & 0x0fffffff; + f >>= subs->datainterval; + if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) { + spin_lock_irqsave(&subs->lock, flags); + subs->freqm = f; + spin_unlock_irqrestore(&subs->lock, flags); + } + } + + return 0; +} + /* determine the number of frames in the next packet */ static int snd_usb_audio_next_packet_size(struct snd_usb_substream *subs) { @@ -2219,10 +2246,17 @@ static void init_substream(struct snd_usb_stream *as, int stream, struct audiofo subs->stream = as; subs->direction = stream; subs->dev = as->chip->dev; - if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL) + if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL) { subs->ops = audio_urb_ops[stream]; - else + } else { subs->ops = audio_urb_ops_high_speed[stream]; + switch (as->chip->usb_id) { + case USB_ID(0x041e, 0x3f02): /* E-Mu 0202 USB */ + case USB_ID(0x041e, 0x3f04): /* E-Mu 0404 USB */ + subs->ops.retire_sync = retire_playback_sync_urb_hs_emu; + break; + } + } snd_pcm_set_ops(as->pcm, stream, stream == SNDRV_PCM_STREAM_PLAYBACK ? &snd_usb_playback_ops : &snd_usb_capture_ops); |