aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRicard Wanderlof <ricard.wanderlof@axis.com>2015-10-19 02:52:53 -0400
committerTakashi Iwai <tiwai@suse.de>2015-10-19 06:38:09 -0400
commite05704467736231199503e5a21c587e7ec36b829 (patch)
tree669b563156bd36cb3aeffe706917605596438949
parentb97a936910c8d668d25d60acbf62aea0d2ff587e (diff)
ALSA: USB-audio: Add quirk for Zoom R16/24 playback
The Zoom R16/24 have a nonstandard playback format where each isochronous packet contains a length descriptor in the first four bytes. (Curiously, capture data does not contain this and requires no quirk.) The quirk involves adding the extra length descriptor whenever outgoing isochronous packets are generated, both in pcm.c (outgoing audio) and endpoint.c (silent data). In order to make the quirk as unintrusive as possible, for pcm.c:prepare_playback_urb(), the isochronous packet descriptors are initially set up in the same way no matter if the quirk is enabled or not. Once it is time to actually copy the data into the outgoing packet buffer (together with the added length descriptors) the isochronous descriptors are adjusted in order take the increased payload length into account. For endpoint.c:prepare_silent_urb() it makes more sense to modify the actual function, partly because the function is less complex to start with and partly because it is not as time-critical as prepare_playback_urb() (whose bulk is run with interrupts disabled), so the (minute) additional time spent in the non-quirk case is motivated by the simplicity of having a single function for all cases. The quirk is controlled by the new tx_length_quirk member in struct snd_usb_substream and struct snd_usb_audio, which is conveyed to pcm.c and endpoint.c from quirks.c in a similar manner to the txfr_quirk member in the same structs. In contrast to txfr_quirk however, the quirk is enabled directly in quirks.c:create_standard_audio_quirk() by checking the USB ID in that function. Another option would be to introduce a new QUIRK_AUDIO_ZOOM_INTERFACE or somesuch, which would have made the quirk very plain to see in the quirk table, but it was felt that the additional code needed to implement it this way would just make the implementation more complex with no real gain. Tested with a Zoom R16, both by doing capture and playback separately using arecord and aplay (8 channel capture and 2 channel playback, respectively), as well as capture and playback together using Ardour, as well as Audacity and Qtractor together with jackd. The R24 is reportedly compatible with the R16 when used as an audio interface. Both devices share the same USB ID and have the same number of inputs (8) and outputs (2). Therefore "R16/24" is mentioned throughout the patch. Regression tested using an Edirol UA-5 in both class compliant (16-bit) and "advanced" (24 bit, forces the use of quirks) modes. Signed-off-by: Ricard Wanderlof <ricardw@axis.com> Tested-by: Panu Matilainen <pmatilai@laiskiainen.org> Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r--sound/usb/card.h1
-rw-r--r--sound/usb/endpoint.c25
-rw-r--r--sound/usb/pcm.c32
-rw-r--r--sound/usb/quirks-table.h7
-rw-r--r--sound/usb/quirks.c3
-rw-r--r--sound/usb/stream.c1
-rw-r--r--sound/usb/usbaudio.h1
7 files changed, 61 insertions, 9 deletions
diff --git a/sound/usb/card.h b/sound/usb/card.h
index ef580b43f1e3..71778ca4b26a 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -122,6 +122,7 @@ struct snd_usb_substream {
122 unsigned int buffer_periods; /* current periods per buffer */ 122 unsigned int buffer_periods; /* current periods per buffer */
123 unsigned int altset_idx; /* USB data format: index of alternate setting */ 123 unsigned int altset_idx; /* USB data format: index of alternate setting */
124 unsigned int txfr_quirk:1; /* allow sub-frame alignment */ 124 unsigned int txfr_quirk:1; /* allow sub-frame alignment */
125 unsigned int tx_length_quirk:1; /* add length specifier to transfers */
125 unsigned int fmt_type; /* USB audio format type (1-3) */ 126 unsigned int fmt_type; /* USB audio format type (1-3) */
126 unsigned int pkt_offset_adj; /* Bytes to drop from beginning of packets (for non-compliant devices) */ 127 unsigned int pkt_offset_adj; /* Bytes to drop from beginning of packets (for non-compliant devices) */
127 128
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 825a06ce83a9..0cc64bd4d0a4 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -188,9 +188,17 @@ static void prepare_silent_urb(struct snd_usb_endpoint *ep,
188{ 188{
189 struct urb *urb = ctx->urb; 189 struct urb *urb = ctx->urb;
190 unsigned int offs = 0; 190 unsigned int offs = 0;
191 unsigned int extra = 0;
192 __le32 packet_length;
191 int i; 193 int i;
192 194
195 /* For tx_length_quirk, put packet length at start of packet */
196 if (ep->chip->tx_length_quirk)
197 extra = sizeof(packet_length);
198
193 for (i = 0; i < ctx->packets; ++i) { 199 for (i = 0; i < ctx->packets; ++i) {
200 unsigned int offset;
201 unsigned int length;
194 int counts; 202 int counts;
195 203
196 if (ctx->packet_size[i]) 204 if (ctx->packet_size[i])
@@ -198,15 +206,22 @@ static void prepare_silent_urb(struct snd_usb_endpoint *ep,
198 else 206 else
199 counts = snd_usb_endpoint_next_packet_size(ep); 207 counts = snd_usb_endpoint_next_packet_size(ep);
200 208
201 urb->iso_frame_desc[i].offset = offs * ep->stride; 209 length = counts * ep->stride; /* number of silent bytes */
202 urb->iso_frame_desc[i].length = counts * ep->stride; 210 offset = offs * ep->stride + extra * i;
211 urb->iso_frame_desc[i].offset = offset;
212 urb->iso_frame_desc[i].length = length + extra;
213 if (extra) {
214 packet_length = cpu_to_le32(length);
215 memcpy(urb->transfer_buffer + offset,
216 &packet_length, sizeof(packet_length));
217 }
218 memset(urb->transfer_buffer + offset + extra,
219 ep->silence_value, length);
203 offs += counts; 220 offs += counts;
204 } 221 }
205 222
206 urb->number_of_packets = ctx->packets; 223 urb->number_of_packets = ctx->packets;
207 urb->transfer_buffer_length = offs * ep->stride; 224 urb->transfer_buffer_length = offs * ep->stride + ctx->packets * extra;
208 memset(urb->transfer_buffer, ep->silence_value,
209 offs * ep->stride);
210} 225}
211 226
212/* 227/*
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index e3c5bc0df69d..9245f52d43bd 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -1409,6 +1409,32 @@ static void copy_to_urb(struct snd_usb_substream *subs, struct urb *urb,
1409 subs->hwptr_done -= runtime->buffer_size * stride; 1409 subs->hwptr_done -= runtime->buffer_size * stride;
1410} 1410}
1411 1411
1412static unsigned int copy_to_urb_quirk(struct snd_usb_substream *subs,
1413 struct urb *urb, int stride,
1414 unsigned int bytes)
1415{
1416 __le32 packet_length;
1417 int i;
1418
1419 /* Put __le32 length descriptor at start of each packet. */
1420 for (i = 0; i < urb->number_of_packets; i++) {
1421 unsigned int length = urb->iso_frame_desc[i].length;
1422 unsigned int offset = urb->iso_frame_desc[i].offset;
1423
1424 packet_length = cpu_to_le32(length);
1425 offset += i * sizeof(packet_length);
1426 urb->iso_frame_desc[i].offset = offset;
1427 urb->iso_frame_desc[i].length += sizeof(packet_length);
1428 memcpy(urb->transfer_buffer + offset,
1429 &packet_length, sizeof(packet_length));
1430 copy_to_urb(subs, urb, offset + sizeof(packet_length),
1431 stride, length);
1432 }
1433 /* Adjust transfer size accordingly. */
1434 bytes += urb->number_of_packets * sizeof(packet_length);
1435 return bytes;
1436}
1437
1412static void prepare_playback_urb(struct snd_usb_substream *subs, 1438static void prepare_playback_urb(struct snd_usb_substream *subs,
1413 struct urb *urb) 1439 struct urb *urb)
1414{ 1440{
@@ -1488,7 +1514,11 @@ static void prepare_playback_urb(struct snd_usb_substream *subs,
1488 subs->hwptr_done -= runtime->buffer_size * stride; 1514 subs->hwptr_done -= runtime->buffer_size * stride;
1489 } else { 1515 } else {
1490 /* usual PCM */ 1516 /* usual PCM */
1491 copy_to_urb(subs, urb, 0, stride, bytes); 1517 if (!subs->tx_length_quirk)
1518 copy_to_urb(subs, urb, 0, stride, bytes);
1519 else
1520 bytes = copy_to_urb_quirk(subs, urb, stride, bytes);
1521 /* bytes is now amount of outgoing data */
1492 } 1522 }
1493 1523
1494 /* update delay with exact number of samples queued */ 1524 /* update delay with exact number of samples queued */
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index 99de06100395..4d3848ce4cff 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -3193,8 +3193,9 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
3193 * ZOOM R16/24 in audio interface mode. 3193 * ZOOM R16/24 in audio interface mode.
3194 * Mixer descriptors are garbage, further quirks will be needed 3194 * Mixer descriptors are garbage, further quirks will be needed
3195 * to make any of it functional, thus disabled for now. 3195 * to make any of it functional, thus disabled for now.
3196 * Playback stream appears to start and run fine but no sound 3196 * Playback requires an extra four byte LE length indicator
3197 * is produced, so also disabled for now. 3197 * at the start of each isochronous packet. This quirk is
3198 * enabled in create_standard_audio_quirk().
3198 */ 3199 */
3199 USB_DEVICE(0x1686, 0x00dd), 3200 USB_DEVICE(0x1686, 0x00dd),
3200 .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { 3201 .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
@@ -3209,7 +3210,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
3209 { 3210 {
3210 /* Playback */ 3211 /* Playback */
3211 .ifnum = 1, 3212 .ifnum = 1,
3212 .type = QUIRK_IGNORE_INTERFACE, 3213 .type = QUIRK_AUDIO_STANDARD_INTERFACE,
3213 }, 3214 },
3214 { 3215 {
3215 /* Capture */ 3216 /* Capture */
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 00ebc0ca008e..4897ea171194 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -115,6 +115,9 @@ static int create_standard_audio_quirk(struct snd_usb_audio *chip,
115 struct usb_interface_descriptor *altsd; 115 struct usb_interface_descriptor *altsd;
116 int err; 116 int err;
117 117
118 if (chip->usb_id == USB_ID(0x1686, 0x00dd)) /* Zoom R16/24 */
119 chip->tx_length_quirk = 1;
120
118 alts = &iface->altsetting[0]; 121 alts = &iface->altsetting[0];
119 altsd = get_iface_desc(alts); 122 altsd = get_iface_desc(alts);
120 err = snd_usb_parse_audio_interface(chip, altsd->bInterfaceNumber); 123 err = snd_usb_parse_audio_interface(chip, altsd->bInterfaceNumber);
diff --git a/sound/usb/stream.c b/sound/usb/stream.c
index 970086015cde..8ee14f2365e7 100644
--- a/sound/usb/stream.c
+++ b/sound/usb/stream.c
@@ -92,6 +92,7 @@ static void snd_usb_init_substream(struct snd_usb_stream *as,
92 subs->direction = stream; 92 subs->direction = stream;
93 subs->dev = as->chip->dev; 93 subs->dev = as->chip->dev;
94 subs->txfr_quirk = as->chip->txfr_quirk; 94 subs->txfr_quirk = as->chip->txfr_quirk;
95 subs->tx_length_quirk = as->chip->tx_length_quirk;
95 subs->speed = snd_usb_get_speed(subs->dev); 96 subs->speed = snd_usb_get_speed(subs->dev);
96 subs->pkt_offset_adj = 0; 97 subs->pkt_offset_adj = 0;
97 98
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 33a176437e2e..15a12715bd05 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -43,6 +43,7 @@ struct snd_usb_audio {
43 atomic_t usage_count; 43 atomic_t usage_count;
44 wait_queue_head_t shutdown_wait; 44 wait_queue_head_t shutdown_wait;
45 unsigned int txfr_quirk:1; /* Subframe boundaries on transfers */ 45 unsigned int txfr_quirk:1; /* Subframe boundaries on transfers */
46 unsigned int tx_length_quirk:1; /* Put length specifier in transfers */
46 47
47 int num_interfaces; 48 int num_interfaces;
48 int num_suspended_intf; 49 int num_suspended_intf;