aboutsummaryrefslogtreecommitdiffstats
path: root/sound/usb
diff options
context:
space:
mode:
Diffstat (limited to 'sound/usb')
-rw-r--r--sound/usb/card.h7
-rw-r--r--sound/usb/endpoint.c9
-rw-r--r--sound/usb/pcm.c87
3 files changed, 92 insertions, 11 deletions
diff --git a/sound/usb/card.h b/sound/usb/card.h
index d32ea411545a..ac55477ce6dd 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -28,6 +28,7 @@ struct audioformat {
28 unsigned int *rate_table; /* rate table */ 28 unsigned int *rate_table; /* rate table */
29 unsigned char clock; /* associated clock */ 29 unsigned char clock; /* associated clock */
30 struct snd_pcm_chmap_elem *chmap; /* (optional) channel map */ 30 struct snd_pcm_chmap_elem *chmap; /* (optional) channel map */
31 bool dsd_dop; /* add DOP headers in case of DSD samples */
31}; 32};
32 33
33struct snd_usb_substream; 34struct snd_usb_substream;
@@ -139,6 +140,12 @@ struct snd_usb_substream {
139 140
140 int last_frame_number; /* stored frame number */ 141 int last_frame_number; /* stored frame number */
141 int last_delay; /* stored delay */ 142 int last_delay; /* stored delay */
143
144 struct {
145 int marker;
146 int channel;
147 int byte_idx;
148 } dsd_dop;
142}; 149};
143 150
144struct snd_usb_stream { 151struct snd_usb_stream {
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 7e9c55a73540..32d0b41a1ff6 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -578,6 +578,15 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep,
578 int is_playback = usb_pipeout(ep->pipe); 578 int is_playback = usb_pipeout(ep->pipe);
579 int frame_bits = snd_pcm_format_physical_width(pcm_format) * channels; 579 int frame_bits = snd_pcm_format_physical_width(pcm_format) * channels;
580 580
581 if (pcm_format == SNDRV_PCM_FORMAT_DSD_U16_LE && fmt->dsd_dop) {
582 /*
583 * When operating in DSD DOP mode, the size of a sample frame
584 * in hardware differs from the actual physical format width
585 * because we need to make room for the DOP markers.
586 */
587 frame_bits += channels << 3;
588 }
589
581 ep->datainterval = fmt->datainterval; 590 ep->datainterval = fmt->datainterval;
582 ep->stride = frame_bits >> 3; 591 ep->stride = frame_bits >> 3;
583 ep->silence_value = pcm_format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0; 592 ep->silence_value = pcm_format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0;
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 099c0fe0d1e1..4cd917cf058e 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -1120,6 +1120,12 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction)
1120 runtime->private_data = subs; 1120 runtime->private_data = subs;
1121 subs->pcm_substream = substream; 1121 subs->pcm_substream = substream;
1122 /* runtime PM is also done there */ 1122 /* runtime PM is also done there */
1123
1124 /* initialize DSD/DOP context */
1125 subs->dsd_dop.byte_idx = 0;
1126 subs->dsd_dop.channel = 0;
1127 subs->dsd_dop.marker = 1;
1128
1123 return setup_hw_info(runtime, subs); 1129 return setup_hw_info(runtime, subs);
1124} 1130}
1125 1131
@@ -1214,6 +1220,56 @@ static void retire_capture_urb(struct snd_usb_substream *subs,
1214 snd_pcm_period_elapsed(subs->pcm_substream); 1220 snd_pcm_period_elapsed(subs->pcm_substream);
1215} 1221}
1216 1222
1223static inline void fill_playback_urb_dsd_dop(struct snd_usb_substream *subs,
1224 struct urb *urb, unsigned int bytes)
1225{
1226 struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
1227 unsigned int stride = runtime->frame_bits >> 3;
1228 unsigned int dst_idx = 0;
1229 unsigned int src_idx = subs->hwptr_done;
1230 unsigned int wrap = runtime->buffer_size * stride;
1231 u8 *dst = urb->transfer_buffer;
1232 u8 *src = runtime->dma_area;
1233 u8 marker[] = { 0x05, 0xfa };
1234
1235 /*
1236 * The DSP DOP format defines a way to transport DSD samples over
1237 * normal PCM data endpoints. It requires stuffing of marker bytes
1238 * (0x05 and 0xfa, alternating per sample frame), and then expects
1239 * 2 additional bytes of actual payload. The whole frame is stored
1240 * LSB.
1241 *
1242 * Hence, for a stereo transport, the buffer layout looks like this,
1243 * where L refers to left channel samples and R to right.
1244 *
1245 * L1 L2 0x05 R1 R2 0x05 L3 L4 0xfa R3 R4 0xfa
1246 * L5 L6 0x05 R5 R6 0x05 L7 L8 0xfa R7 R8 0xfa
1247 * .....
1248 *
1249 */
1250
1251 while (bytes--) {
1252 if (++subs->dsd_dop.byte_idx == 3) {
1253 /* frame boundary? */
1254 dst[dst_idx++] = marker[subs->dsd_dop.marker];
1255 src_idx += 2;
1256 subs->dsd_dop.byte_idx = 0;
1257
1258 if (++subs->dsd_dop.channel % runtime->channels == 0) {
1259 /* alternate the marker */
1260 subs->dsd_dop.marker++;
1261 subs->dsd_dop.marker %= ARRAY_SIZE(marker);
1262 subs->dsd_dop.channel = 0;
1263 }
1264 } else {
1265 /* stuff the DSD payload */
1266 int idx = (src_idx + subs->dsd_dop.byte_idx - 1) % wrap;
1267 dst[dst_idx++] = src[idx];
1268 subs->hwptr_done++;
1269 }
1270 }
1271}
1272
1217static void prepare_playback_urb(struct snd_usb_substream *subs, 1273static void prepare_playback_urb(struct snd_usb_substream *subs,
1218 struct urb *urb) 1274 struct urb *urb)
1219{ 1275{
@@ -1270,19 +1326,28 @@ static void prepare_playback_urb(struct snd_usb_substream *subs,
1270 break; 1326 break;
1271 } 1327 }
1272 bytes = frames * ep->stride; 1328 bytes = frames * ep->stride;
1273 if (subs->hwptr_done + bytes > runtime->buffer_size * stride) { 1329
1274 /* err, the transferred area goes over buffer boundary. */ 1330 if (unlikely(subs->pcm_format == SNDRV_PCM_FORMAT_DSD_U16_LE &&
1275 unsigned int bytes1 = 1331 subs->cur_audiofmt->dsd_dop)) {
1276 runtime->buffer_size * stride - subs->hwptr_done; 1332 fill_playback_urb_dsd_dop(subs, urb, bytes);
1277 memcpy(urb->transfer_buffer,
1278 runtime->dma_area + subs->hwptr_done, bytes1);
1279 memcpy(urb->transfer_buffer + bytes1,
1280 runtime->dma_area, bytes - bytes1);
1281 } else { 1333 } else {
1282 memcpy(urb->transfer_buffer, 1334 /* usual PCM */
1283 runtime->dma_area + subs->hwptr_done, bytes); 1335 if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
1336 /* err, the transferred area goes over buffer boundary. */
1337 unsigned int bytes1 =
1338 runtime->buffer_size * stride - subs->hwptr_done;
1339 memcpy(urb->transfer_buffer,
1340 runtime->dma_area + subs->hwptr_done, bytes1);
1341 memcpy(urb->transfer_buffer + bytes1,
1342 runtime->dma_area, bytes - bytes1);
1343 } else {
1344 memcpy(urb->transfer_buffer,
1345 runtime->dma_area + subs->hwptr_done, bytes);
1346 }
1347
1348 subs->hwptr_done += bytes;
1284 } 1349 }
1285 subs->hwptr_done += bytes; 1350
1286 if (subs->hwptr_done >= runtime->buffer_size * stride) 1351 if (subs->hwptr_done >= runtime->buffer_size * stride)
1287 subs->hwptr_done -= runtime->buffer_size * stride; 1352 subs->hwptr_done -= runtime->buffer_size * stride;
1288 1353