runtime->private_data = subs;
        subs->pcm_substream = substream;
        /* runtime PM is also done there */
+
+       /* initialize DSD/DOP context */
+       subs->dsd_dop.byte_idx = 0;
+       subs->dsd_dop.channel = 0;
+       subs->dsd_dop.marker = 1;
+
        return setup_hw_info(runtime, subs);
 }
 
                snd_pcm_period_elapsed(subs->pcm_substream);
 }
 
+static inline void fill_playback_urb_dsd_dop(struct snd_usb_substream *subs,
+                                            struct urb *urb, unsigned int bytes)
+{
+       struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
+       unsigned int stride = runtime->frame_bits >> 3;
+       unsigned int dst_idx = 0;
+       unsigned int src_idx = subs->hwptr_done;
+       unsigned int wrap = runtime->buffer_size * stride;
+       u8 *dst = urb->transfer_buffer;
+       u8 *src = runtime->dma_area;
+       u8 marker[] = { 0x05, 0xfa };
+
+       /*
+        * The DSP DOP format defines a way to transport DSD samples over
+        * normal PCM data endpoints. It requires stuffing of marker bytes
+        * (0x05 and 0xfa, alternating per sample frame), and then expects
+        * 2 additional bytes of actual payload. The whole frame is stored
+        * LSB.
+        *
+        * Hence, for a stereo transport, the buffer layout looks like this,
+        * where L refers to left channel samples and R to right.
+        *
+        *   L1 L2 0x05   R1 R2 0x05   L3 L4 0xfa  R3 R4 0xfa
+        *   L5 L6 0x05   R5 R6 0x05   L7 L8 0xfa  R7 R8 0xfa
+        *   .....
+        *
+        */
+
+       while (bytes--) {
+               if (++subs->dsd_dop.byte_idx == 3) {
+                       /* frame boundary? */
+                       dst[dst_idx++] = marker[subs->dsd_dop.marker];
+                       src_idx += 2;
+                       subs->dsd_dop.byte_idx = 0;
+
+                       if (++subs->dsd_dop.channel % runtime->channels == 0) {
+                               /* alternate the marker */
+                               subs->dsd_dop.marker++;
+                               subs->dsd_dop.marker %= ARRAY_SIZE(marker);
+                               subs->dsd_dop.channel = 0;
+                       }
+               } else {
+                       /* stuff the DSD payload */
+                       int idx = (src_idx + subs->dsd_dop.byte_idx - 1) % wrap;
+                       dst[dst_idx++] = src[idx];
+                       subs->hwptr_done++;
+               }
+       }
+}
+
 static void prepare_playback_urb(struct snd_usb_substream *subs,
                                 struct urb *urb)
 {
                        break;
        }
        bytes = frames * ep->stride;
-       if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
-               /* err, the transferred area goes over buffer boundary. */
-               unsigned int bytes1 =
-                       runtime->buffer_size * stride - subs->hwptr_done;
-               memcpy(urb->transfer_buffer,
-                      runtime->dma_area + subs->hwptr_done, bytes1);
-               memcpy(urb->transfer_buffer + bytes1,
-                      runtime->dma_area, bytes - bytes1);
+
+       if (unlikely(subs->pcm_format == SNDRV_PCM_FORMAT_DSD_U16_LE &&
+                    subs->cur_audiofmt->dsd_dop)) {
+               fill_playback_urb_dsd_dop(subs, urb, bytes);
        } else {
-               memcpy(urb->transfer_buffer,
-                      runtime->dma_area + subs->hwptr_done, bytes);
+               /* usual PCM */
+               if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
+                       /* err, the transferred area goes over buffer boundary. */
+                       unsigned int bytes1 =
+                               runtime->buffer_size * stride - subs->hwptr_done;
+                       memcpy(urb->transfer_buffer,
+                              runtime->dma_area + subs->hwptr_done, bytes1);
+                       memcpy(urb->transfer_buffer + bytes1,
+                              runtime->dma_area, bytes - bytes1);
+               } else {
+                       memcpy(urb->transfer_buffer,
+                              runtime->dma_area + subs->hwptr_done, bytes);
+               }
+
+               subs->hwptr_done += bytes;
        }
-       subs->hwptr_done += bytes;
+
        if (subs->hwptr_done >= runtime->buffer_size * stride)
                subs->hwptr_done -= runtime->buffer_size * stride;