|
@@ -225,6 +225,7 @@ int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
|
|
|
else
|
|
|
subs->freqn = get_usb_high_speed_rate(rate);
|
|
|
subs->freqm = subs->freqn;
|
|
|
+ subs->freqshift = INT_MIN;
|
|
|
/* calculate max. frequency */
|
|
|
if (subs->maxpacksize) {
|
|
|
/* whatever fits into a max. size packet */
|
|
@@ -513,11 +514,10 @@ static int retire_paused_capture_urb(struct snd_usb_substream *subs,
|
|
|
|
|
|
|
|
|
/*
|
|
|
- * prepare urb for full speed playback sync pipe
|
|
|
+ * prepare urb for playback sync pipe
|
|
|
*
|
|
|
* set up the offset and length to receive the current frequency.
|
|
|
*/
|
|
|
-
|
|
|
static int prepare_playback_sync_urb(struct snd_usb_substream *subs,
|
|
|
struct snd_pcm_runtime *runtime,
|
|
|
struct urb *urb)
|
|
@@ -525,103 +525,78 @@ static int prepare_playback_sync_urb(struct snd_usb_substream *subs,
|
|
|
struct snd_urb_ctx *ctx = urb->context;
|
|
|
|
|
|
urb->dev = ctx->subs->dev; /* we need to set this at each time */
|
|
|
- urb->iso_frame_desc[0].length = 3;
|
|
|
+ urb->iso_frame_desc[0].length = min(4u, ctx->subs->syncmaxsize);
|
|
|
urb->iso_frame_desc[0].offset = 0;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * prepare urb for high speed playback sync pipe
|
|
|
+ * process after playback sync complete
|
|
|
*
|
|
|
- * set up the offset and length to receive the current frequency.
|
|
|
- */
|
|
|
-
|
|
|
-static int prepare_playback_sync_urb_hs(struct snd_usb_substream *subs,
|
|
|
- struct snd_pcm_runtime *runtime,
|
|
|
- struct urb *urb)
|
|
|
-{
|
|
|
- struct snd_urb_ctx *ctx = urb->context;
|
|
|
-
|
|
|
- urb->dev = ctx->subs->dev; /* we need to set this at each time */
|
|
|
- urb->iso_frame_desc[0].length = 4;
|
|
|
- urb->iso_frame_desc[0].offset = 0;
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * process after full speed playback sync complete
|
|
|
- *
|
|
|
- * retrieve the current 10.14 frequency from pipe, and set it.
|
|
|
- * the value is referred in prepare_playback_urb().
|
|
|
+ * Full speed devices report feedback values in 10.14 format as samples per
|
|
|
+ * frame, high speed devices in 16.16 format as samples per microframe.
|
|
|
+ * Because the Audio Class 1 spec was written before USB 2.0, many high speed
|
|
|
+ * devices use a wrong interpretation, some others use an entirely different
|
|
|
+ * format. Therefore, we cannot predict what format any particular device uses
|
|
|
+ * and must detect it automatically.
|
|
|
*/
|
|
|
static int retire_playback_sync_urb(struct snd_usb_substream *subs,
|
|
|
struct snd_pcm_runtime *runtime,
|
|
|
struct urb *urb)
|
|
|
{
|
|
|
unsigned int f;
|
|
|
+ int shift;
|
|
|
unsigned long flags;
|
|
|
|
|
|
- if (urb->iso_frame_desc[0].status == 0 &&
|
|
|
- urb->iso_frame_desc[0].actual_length == 3) {
|
|
|
- f = combine_triple((u8*)urb->transfer_buffer) << 2;
|
|
|
- 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;
|
|
|
-}
|
|
|
+ if (urb->iso_frame_desc[0].status != 0 ||
|
|
|
+ urb->iso_frame_desc[0].actual_length < 3)
|
|
|
+ return 0;
|
|
|
|
|
|
-/*
|
|
|
- * process after high speed playback sync complete
|
|
|
- *
|
|
|
- * retrieve the current 12.13 frequency from pipe, and set it.
|
|
|
- * the value is referred in prepare_playback_urb().
|
|
|
- */
|
|
|
-static int retire_playback_sync_urb_hs(struct snd_usb_substream *subs,
|
|
|
- struct snd_pcm_runtime *runtime,
|
|
|
- struct urb *urb)
|
|
|
-{
|
|
|
- unsigned int f;
|
|
|
- unsigned long flags;
|
|
|
+ f = le32_to_cpup(urb->transfer_buffer);
|
|
|
+ if (urb->iso_frame_desc[0].actual_length == 3)
|
|
|
+ f &= 0x00ffffff;
|
|
|
+ else
|
|
|
+ f &= 0x0fffffff;
|
|
|
+ if (f == 0)
|
|
|
+ return 0;
|
|
|
|
|
|
- if (urb->iso_frame_desc[0].status == 0 &&
|
|
|
- urb->iso_frame_desc[0].actual_length == 4) {
|
|
|
- f = combine_quad((u8*)urb->transfer_buffer) & 0x0fffffff;
|
|
|
- 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);
|
|
|
+ if (unlikely(subs->freqshift == INT_MIN)) {
|
|
|
+ /*
|
|
|
+ * The first time we see a feedback value, determine its format
|
|
|
+ * by shifting it left or right until it matches the nominal
|
|
|
+ * frequency value. This assumes that the feedback does not
|
|
|
+ * differ from the nominal value more than +50% or -25%.
|
|
|
+ */
|
|
|
+ shift = 0;
|
|
|
+ while (f < subs->freqn - subs->freqn / 4) {
|
|
|
+ f <<= 1;
|
|
|
+ shift++;
|
|
|
+ }
|
|
|
+ while (f > subs->freqn + subs->freqn / 2) {
|
|
|
+ f >>= 1;
|
|
|
+ shift--;
|
|
|
}
|
|
|
+ subs->freqshift = shift;
|
|
|
}
|
|
|
+ else if (subs->freqshift >= 0)
|
|
|
+ f <<= subs->freqshift;
|
|
|
+ else
|
|
|
+ f >>= -subs->freqshift;
|
|
|
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * process after E-Mu 0202/0404/Tracker Pre 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);
|
|
|
- }
|
|
|
+ if (likely(f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax)) {
|
|
|
+ /*
|
|
|
+ * If the frequency looks valid, set it.
|
|
|
+ * This value is referred to in prepare_playback_urb().
|
|
|
+ */
|
|
|
+ spin_lock_irqsave(&subs->lock, flags);
|
|
|
+ subs->freqm = f;
|
|
|
+ spin_unlock_irqrestore(&subs->lock, flags);
|
|
|
+ } else {
|
|
|
+ /*
|
|
|
+ * Out of range; maybe the shift value is wrong.
|
|
|
+ * Reset it so that we autodetect again the next time.
|
|
|
+ */
|
|
|
+ subs->freqshift = INT_MIN;
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
@@ -878,21 +853,6 @@ static struct snd_urb_ops audio_urb_ops[2] = {
|
|
|
},
|
|
|
};
|
|
|
|
|
|
-static struct snd_urb_ops audio_urb_ops_high_speed[2] = {
|
|
|
- {
|
|
|
- .prepare = prepare_nodata_playback_urb,
|
|
|
- .retire = retire_playback_urb,
|
|
|
- .prepare_sync = prepare_playback_sync_urb_hs,
|
|
|
- .retire_sync = retire_playback_sync_urb_hs,
|
|
|
- },
|
|
|
- {
|
|
|
- .prepare = prepare_capture_urb,
|
|
|
- .retire = retire_capture_urb,
|
|
|
- .prepare_sync = prepare_capture_sync_urb_hs,
|
|
|
- .retire_sync = retire_capture_sync_urb,
|
|
|
- },
|
|
|
-};
|
|
|
-
|
|
|
/*
|
|
|
* initialize the substream instance.
|
|
|
*/
|
|
@@ -909,23 +869,9 @@ void snd_usb_init_substream(struct snd_usb_stream *as,
|
|
|
subs->direction = stream;
|
|
|
subs->dev = as->chip->dev;
|
|
|
subs->txfr_quirk = as->chip->txfr_quirk;
|
|
|
- if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL) {
|
|
|
- subs->ops = audio_urb_ops[stream];
|
|
|
- } 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 */
|
|
|
- case USB_ID(0x041e, 0x3f0a): /* E-Mu Tracker Pre */
|
|
|
- subs->ops.retire_sync = retire_playback_sync_urb_hs_emu;
|
|
|
- break;
|
|
|
- case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra 8 */
|
|
|
- case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */
|
|
|
- subs->ops.prepare_sync = prepare_playback_sync_urb;
|
|
|
- subs->ops.retire_sync = retire_playback_sync_urb;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
+ subs->ops = audio_urb_ops[stream];
|
|
|
+ if (snd_usb_get_speed(subs->dev) >= USB_SPEED_HIGH)
|
|
|
+ subs->ops.prepare_sync = prepare_capture_sync_urb_hs;
|
|
|
|
|
|
snd_usb_set_pcm_ops(as->pcm, stream);
|
|
|
|