Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gerg/m68knommu
[firefly-linux-kernel-4.4.55.git] / sound / usb / pcm.c
index ee8dd9efb3ef6a0169dae16b29b757960a80703c..9245f52d43bdecfeb710b6cfc6ae99b3202aae90 100644 (file)
@@ -1387,8 +1387,8 @@ static inline void fill_playback_urb_dsd_dop(struct snd_usb_substream *subs,
                subs->hwptr_done -= runtime->buffer_size * stride;
 }
 
-static void copy_to_urb(struct snd_usb_substream *subs,
-                      struct urb *urb, int stride, unsigned int bytes)
+static void copy_to_urb(struct snd_usb_substream *subs, struct urb *urb,
+                       int offset, int stride, unsigned int bytes)
 {
        struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
 
@@ -1396,12 +1396,12 @@ static void copy_to_urb(struct snd_usb_substream *subs,
                /* err, the transferred area goes over buffer boundary. */
                unsigned int bytes1 =
                        runtime->buffer_size * stride - subs->hwptr_done;
-               memcpy(urb->transfer_buffer,
+               memcpy(urb->transfer_buffer + offset,
                       runtime->dma_area + subs->hwptr_done, bytes1);
-               memcpy(urb->transfer_buffer + bytes1,
+               memcpy(urb->transfer_buffer + offset + bytes1,
                       runtime->dma_area, bytes - bytes1);
        } else {
-               memcpy(urb->transfer_buffer,
+               memcpy(urb->transfer_buffer + offset,
                       runtime->dma_area + subs->hwptr_done, bytes);
        }
        subs->hwptr_done += bytes;
@@ -1409,6 +1409,32 @@ static void copy_to_urb(struct snd_usb_substream *subs,
                subs->hwptr_done -= runtime->buffer_size * stride;
 }
 
+static unsigned int copy_to_urb_quirk(struct snd_usb_substream *subs,
+                                     struct urb *urb, int stride,
+                                     unsigned int bytes)
+{
+       __le32 packet_length;
+       int i;
+
+       /* Put __le32 length descriptor at start of each packet. */
+       for (i = 0; i < urb->number_of_packets; i++) {
+               unsigned int length = urb->iso_frame_desc[i].length;
+               unsigned int offset = urb->iso_frame_desc[i].offset;
+
+               packet_length = cpu_to_le32(length);
+               offset += i * sizeof(packet_length);
+               urb->iso_frame_desc[i].offset = offset;
+               urb->iso_frame_desc[i].length += sizeof(packet_length);
+               memcpy(urb->transfer_buffer + offset,
+                      &packet_length, sizeof(packet_length));
+               copy_to_urb(subs, urb, offset + sizeof(packet_length),
+                           stride, length);
+       }
+       /* Adjust transfer size accordingly. */
+       bytes += urb->number_of_packets * sizeof(packet_length);
+       return bytes;
+}
+
 static void prepare_playback_urb(struct snd_usb_substream *subs,
                                 struct urb *urb)
 {
@@ -1488,7 +1514,11 @@ static void prepare_playback_urb(struct snd_usb_substream *subs,
                        subs->hwptr_done -= runtime->buffer_size * stride;
        } else {
                /* usual PCM */
-               copy_to_urb(subs, urb, stride, bytes);
+               if (!subs->tx_length_quirk)
+                       copy_to_urb(subs, urb, 0, stride, bytes);
+               else
+                       bytes = copy_to_urb_quirk(subs, urb, stride, bytes);
+                       /* bytes is now amount of outgoing data */
        }
 
        /* update delay with exact number of samples queued */