[alsa-devel] [PATCH v3 0/5] ALSA: snd-usb: add support for DSD
In contrast to the first two approaches, this one keeps the userspace unaware of the actual transport on the bus.
More specifically, the DSD_U16_LE format may be implemented to actually send DOP samples to the device, depending on the quirks entries.
Also, as there is quite some confusion in both data file formats as well as hardware sample formats about whether to stuff the oldest bit in the MSB or the LSB of a byte, ALSA defines that now to be in the MSB, as far as the ALSA userspace layer is concerned. The driver learned functions to re-encode the bytes according to what the hardware supports.
I also wrote a very basic test tool which I published here:
https://github.com/zonque/alsa-dsd-player
Thanks, Daniel
Daniel Mack (5): ALSA: add DSD formats ALSA: snd-usb: use ep->stride from urb callbacks ALSA: snd-usb: add support for DSD DOP stream transport ALSA: snd-usb: add support for bit-reversed byte formats ALSA: snd-usb: add quirks handler for DSD streams
include/sound/pcm.h | 2 + include/uapi/sound/asound.h | 4 +- sound/core/pcm.c | 2 + sound/core/pcm_misc.c | 8 +++ sound/usb/card.h | 8 +++ sound/usb/endpoint.c | 9 ++++ sound/usb/format.c | 3 ++ sound/usb/pcm.c | 118 ++++++++++++++++++++++++++++++++++++------- sound/usb/quirks.c | 28 ++++++++++ sound/usb/quirks.h | 4 ++ 10 files changed, 167 insertions(+), 19 deletions(-)
This patch adds two formats for Direct Stream Digital (DSD), a pulse-density encoding format which is described here: https://en.wikipedia.org/wiki/Direct_Stream_Digital
DSD operates on 2.8, 5.6 or 11.2MHz sample rates and as a 1-bit stream.
The two new types added by this patch describe streams that are capable of handling DSD samples in DOP format as 8-bit or in 16-bit (or at a x8 or x16 data rate, respectively).
DSD itself specifies samples in *bit*, while DOP and ALSA handle them as *bytes*. Hence, a factor of 8 or 16 has to be applied for the sample rare configuration, according to the following table:
configured hardware 176.4KHz 352.8kHz 705.6KHz <---- sample rate
8-bit 2.8MHz 5.6MHz 16-bit 2.8Mhz 5.6MHz 11.2MHz
`-----------------------------' actual DSD sample rates
Signed-off-by: Daniel Mack zonque@gmail.com --- include/sound/pcm.h | 2 ++ include/uapi/sound/asound.h | 4 +++- sound/core/pcm.c | 2 ++ sound/core/pcm_misc.c | 8 ++++++++ 4 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/include/sound/pcm.h b/include/sound/pcm.h index aa7b0a8..1b0c648 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -181,6 +181,8 @@ struct snd_pcm_ops { #define SNDRV_PCM_FMTBIT_G723_24_1B _SNDRV_PCM_FMTBIT(G723_24_1B) #define SNDRV_PCM_FMTBIT_G723_40 _SNDRV_PCM_FMTBIT(G723_40) #define SNDRV_PCM_FMTBIT_G723_40_1B _SNDRV_PCM_FMTBIT(G723_40_1B) +#define SNDRV_PCM_FMTBIT_DSD_U8 _SNDRV_PCM_FMTBIT(DSD_U8) +#define SNDRV_PCM_FMTBIT_DSD_U16_LE _SNDRV_PCM_FMTBIT(DSD_U16_LE)
#ifdef SNDRV_LITTLE_ENDIAN #define SNDRV_PCM_FMTBIT_S16 SNDRV_PCM_FMTBIT_S16_LE diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index 1774a5c..e3983d5 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -214,7 +214,9 @@ typedef int __bitwise snd_pcm_format_t; #define SNDRV_PCM_FORMAT_G723_24_1B ((__force snd_pcm_format_t) 45) /* 1 sample in 1 byte */ #define SNDRV_PCM_FORMAT_G723_40 ((__force snd_pcm_format_t) 46) /* 8 Samples in 5 bytes */ #define SNDRV_PCM_FORMAT_G723_40_1B ((__force snd_pcm_format_t) 47) /* 1 sample in 1 byte */ -#define SNDRV_PCM_FORMAT_LAST SNDRV_PCM_FORMAT_G723_40_1B +#define SNDRV_PCM_FORMAT_DSD_U8 ((__force snd_pcm_format_t) 48) /* DSD, 1-byte samples DSD (x8) */ +#define SNDRV_PCM_FORMAT_DSD_U16_LE ((__force snd_pcm_format_t) 49) /* DSD, 2-byte samples DSD (x16), little endian */ +#define SNDRV_PCM_FORMAT_LAST SNDRV_PCM_FORMAT_DSD_U16_LE
#ifdef SNDRV_LITTLE_ENDIAN #define SNDRV_PCM_FORMAT_S16 SNDRV_PCM_FORMAT_S16_LE diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 578327e..17f45e8 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -209,6 +209,8 @@ static char *snd_pcm_format_names[] = { FORMAT(G723_24_1B), FORMAT(G723_40), FORMAT(G723_40_1B), + FORMAT(DSD_U8), + FORMAT(DSD_U16_LE), };
const char *snd_pcm_format_name(snd_pcm_format_t format) diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c index b875b19..43f24cc 100644 --- a/sound/core/pcm_misc.c +++ b/sound/core/pcm_misc.c @@ -140,6 +140,14 @@ static struct pcm_format_data pcm_formats[(INT)SNDRV_PCM_FORMAT_LAST+1] = { .width = 5, .phys = 5, .le = -1, .signd = -1, .silence = {}, }, + [SNDRV_PCM_FORMAT_DSD_U8] = { + .width = 8, .phys = 8, .le = 1, .signd = 0, + .silence = {}, + }, + [SNDRV_PCM_FORMAT_DSD_U16_LE] = { + .width = 16, .phys = 16, .le = 1, .signd = 0, + .silence = {}, + }, /* FIXME: the following three formats are not defined properly yet */ [SNDRV_PCM_FORMAT_MPEG] = { .le = -1, .signd = -1,
For normal PCM transfer, this change has no effect, as the endpoint's stride is always frame_bits/8. For DSD DOP streams, however, which is added later, the hardware stride differs from the software stride, and the endpoint has the correct information in these cases.
Signed-off-by: Daniel Mack zonque@gmail.com --- sound/usb/pcm.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 815a37d..6f85202 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -1236,8 +1236,8 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, counts = snd_usb_endpoint_next_packet_size(ep);
/* set up descriptor */ - urb->iso_frame_desc[i].offset = frames * stride; - urb->iso_frame_desc[i].length = counts * stride; + urb->iso_frame_desc[i].offset = frames * ep->stride; + urb->iso_frame_desc[i].length = counts * ep->stride; frames += counts; urb->number_of_packets++; subs->transfer_done += counts; @@ -1251,14 +1251,14 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, frames -= subs->transfer_done; counts -= subs->transfer_done; urb->iso_frame_desc[i].length = - counts * stride; + counts * ep->stride; subs->transfer_done = 0; } i++; if (i < ctx->packets) { /* add a transfer delimiter */ urb->iso_frame_desc[i].offset = - frames * stride; + frames * ep->stride; urb->iso_frame_desc[i].length = 0; urb->number_of_packets++; } @@ -1269,7 +1269,7 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, !snd_usb_endpoint_implicit_feedback_sink(subs->data_endpoint)) /* finish at the period boundary */ break; } - bytes = frames * stride; + bytes = frames * ep->stride; if (subs->hwptr_done + bytes > runtime->buffer_size * stride) { /* err, the transferred area goes over buffer boundary. */ unsigned int bytes1 = @@ -1310,8 +1310,8 @@ static void retire_playback_urb(struct snd_usb_substream *subs, { unsigned long flags; struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime; - int stride = runtime->frame_bits >> 3; - int processed = urb->transfer_buffer_length / stride; + struct snd_usb_endpoint *ep = subs->data_endpoint; + int processed = urb->transfer_buffer_length / ep->stride; int est_delay;
/* ignore the delay accounting when procssed=0 is given, i.e.
In order to provide a compatibility way for pushing DSD samples through ordinary PCM channels, the "DoP open Standard" was invented. See http://www.dsd-guide.com for the official document.
The host is required to stuff DSD marker bytes (0x05, 0xfa, alternating) in the MSB of 24 bit wide samples on the bus, in addition to the 16 bits of actual DSD sample payload.
To support this, the hardware and software stride logic in the driver has to be tweaked a bit, as we make the userspace believe we're operating on 16 bit samples, while we in fact push one more byte per channel down to the hardware.
The DOP runtime information is stored in struct snd_usb_substream, so we can keep track of our state across multiple calls to prepare_playback_urb_dsd_dop().
Signed-off-by: Daniel Mack zonque@gmail.com --- sound/usb/card.h | 7 ++++ sound/usb/endpoint.c | 9 ++++++ sound/usb/pcm.c | 87 +++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 92 insertions(+), 11 deletions(-)
diff --git a/sound/usb/card.h b/sound/usb/card.h index 8a751b4..2c05cb7 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -28,6 +28,7 @@ struct audioformat { unsigned int *rate_table; /* rate table */ unsigned char clock; /* associated clock */ struct snd_pcm_chmap_elem *chmap; /* (optional) channel map */ + bool dsd_dop; /* add DOP headers in case of DSD samples */ };
struct snd_usb_substream; @@ -138,6 +139,12 @@ struct snd_usb_substream {
int last_frame_number; /* stored frame number */ int last_delay; /* stored delay */ + + struct { + int marker; + int channel; + int byte_idx; + } dsd_dop; };
struct snd_usb_stream { diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 7e9c55a..32d0b41 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, int is_playback = usb_pipeout(ep->pipe); int frame_bits = snd_pcm_format_physical_width(pcm_format) * channels;
+ if (pcm_format == SNDRV_PCM_FORMAT_DSD_U16_LE && fmt->dsd_dop) { + /* + * When operating in DSD DOP mode, the size of a sample frame + * in hardware differs from the actual physical format width + * because we need to make room for the DOP markers. + */ + frame_bits += channels << 3; + } + ep->datainterval = fmt->datainterval; ep->stride = frame_bits >> 3; ep->silence_value = pcm_format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0; diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 6f85202..b5eae93 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) 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); }
@@ -1214,6 +1220,56 @@ static void retire_capture_urb(struct snd_usb_substream *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) { @@ -1270,19 +1326,28 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, 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;
There is quite some confusion around the bit-ordering in DSD samples, and no general agreement that defines whether hardware is supposed to expect the oldest sample in the MSB or the LSB of a byte.
ALSA will hence set the rule that on the software API layer, bytes always carry the oldest bit in the most significant bit of a byte, and the driver has to translate that at runtime in order to match the hardware layout.
This patch adds support for this by adding a boolean flag to the audio format struct.
Signed-off-by: Daniel Mack zonque@gmail.com --- sound/usb/card.h | 1 + sound/usb/pcm.c | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/sound/usb/card.h b/sound/usb/card.h index 2c05cb7..fcae411 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -29,6 +29,7 @@ struct audioformat { unsigned char clock; /* associated clock */ struct snd_pcm_chmap_elem *chmap; /* (optional) channel map */ bool dsd_dop; /* add DOP headers in case of DSD samples */ + bool dsd_bitrev; /* reverse the bits of each DSD sample */ };
struct snd_usb_substream; diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index b5eae93..c31980c 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -16,6 +16,7 @@
#include <linux/init.h> #include <linux/slab.h> +#include <linux/bitrev.h> #include <linux/ratelimit.h> #include <linux/usb.h> #include <linux/usb/audio.h> @@ -1264,7 +1265,12 @@ static inline void fill_playback_urb_dsd_dop(struct snd_usb_substream *subs, } else { /* stuff the DSD payload */ int idx = (src_idx + subs->dsd_dop.byte_idx - 1) % wrap; - dst[dst_idx++] = src[idx]; + + if (subs->cur_audiofmt->dsd_bitrev) + dst[dst_idx++] = bitrev8(src[idx]); + else + dst[dst_idx++] = src[idx]; + subs->hwptr_done++; } } @@ -1330,6 +1336,17 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, 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 if (unlikely(subs->pcm_format == SNDRV_PCM_FORMAT_DSD_U8 && + subs->cur_audiofmt->dsd_bitrev)) { + /* bit-reverse the bytes */ + u8 *buf = urb->transfer_buffer; + for (i = 0; i < bytes; i++) { + int idx = (subs->hwptr_done + i) + % (runtime->buffer_size * stride); + buf[i] = bitrev8(runtime->dma_area[idx]); + } + + subs->hwptr_done += bytes; } else { /* usual PCM */ if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
Unfortunately, none of the UAC standards provides a way to identify DSD (Direct Stream Digital) formats. Hence, this patch adds a quirks handler to identify USB interfaces that are capable of handling DSD.
That quirks handler can augment the already parsed formats bit-field, by any of the new SNDRV_PCM_FMTBIT_DSD_{U8_U16} and setting the dsd_dop flag in the audio format, if the driver should take care for the DOP byte stuffing.
The only devices that are known to work with this are the ones with a 'Playback Designs' vendor id.
Signed-off-by: Daniel Mack zonque@gmail.com --- sound/usb/format.c | 3 +++ sound/usb/quirks.c | 28 ++++++++++++++++++++++++++++ sound/usb/quirks.h | 4 ++++ 3 files changed, 35 insertions(+)
diff --git a/sound/usb/format.c b/sound/usb/format.c index 20c7751..020ede0 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -136,6 +136,9 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip, snd_printk(KERN_INFO "%d:%u:%d : unsupported format bits %#x\n", chip->dev->devnum, fp->iface, fp->altsetting, format); } + + pcm_formats |= snd_usb_interface_dsd_format_quirks(chip, fp, sample_bytes); + return pcm_formats; }
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 2927981..4c6366c 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -915,3 +915,31 @@ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe, mdelay(20); }
+/* + * snd_usb_interface_dsd_format_quirks() is called from format.c to + * augment the PCM format bit-field for DSD types. The UAC standards + * don't have a designated bit field to denote DSD-capable interfaces, + * hence all hardware that is known to support this format has to be + * listed here. + */ +u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, + struct audioformat *fp, + unsigned int sample_bytes) +{ + /* Playback Designs */ + if (le16_to_cpu(chip->dev->descriptor.idVendor) == 0x23ba) { + switch (fp->altsetting) { + case 1: + fp->dsd_dop = true; + return SNDRV_PCM_FMTBIT_DSD_U16_LE; + case 2: + fp->dsd_bitrev = true; + return SNDRV_PCM_FMTBIT_DSD_U8; + case 3: + fp->dsd_bitrev = true; + return SNDRV_PCM_FMTBIT_DSD_U16_LE; + } + } + + return 0; +} diff --git a/sound/usb/quirks.h b/sound/usb/quirks.h index 7c3681f..665e972 100644 --- a/sound/usb/quirks.h +++ b/sound/usb/quirks.h @@ -31,4 +31,8 @@ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size);
+u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, + struct audioformat *fp, + unsigned int sample_bytes); + #endif /* __USBAUDIO_QUIRKS_H */
At Wed, 17 Apr 2013 00:01:35 +0800, Daniel Mack wrote:
In contrast to the first two approaches, this one keeps the userspace unaware of the actual transport on the bus.
More specifically, the DSD_U16_LE format may be implemented to actually send DOP samples to the device, depending on the quirks entries.
Also, as there is quite some confusion in both data file formats as well as hardware sample formats about whether to stuff the oldest bit in the MSB or the LSB of a byte, ALSA defines that now to be in the MSB, as far as the ALSA userspace layer is concerned. The driver learned functions to re-encode the bytes according to what the hardware supports.
I also wrote a very basic test tool which I published here:
Maybe it'd be good to merge it into alsa-lib/test directory or alsa-utils (or alsa-tools).
Takashi
Thanks, Daniel
Daniel Mack (5): ALSA: add DSD formats ALSA: snd-usb: use ep->stride from urb callbacks ALSA: snd-usb: add support for DSD DOP stream transport ALSA: snd-usb: add support for bit-reversed byte formats ALSA: snd-usb: add quirks handler for DSD streams
include/sound/pcm.h | 2 + include/uapi/sound/asound.h | 4 +- sound/core/pcm.c | 2 + sound/core/pcm_misc.c | 8 +++ sound/usb/card.h | 8 +++ sound/usb/endpoint.c | 9 ++++ sound/usb/format.c | 3 ++ sound/usb/pcm.c | 118 ++++++++++++++++++++++++++++++++++++------- sound/usb/quirks.c | 28 ++++++++++ sound/usb/quirks.h | 4 ++ 10 files changed, 167 insertions(+), 19 deletions(-)
-- 1.7.10.4
At Wed, 17 Apr 2013 00:01:35 +0800, Daniel Mack wrote:
In contrast to the first two approaches, this one keeps the userspace unaware of the actual transport on the bus.
More specifically, the DSD_U16_LE format may be implemented to actually send DOP samples to the device, depending on the quirks entries.
Also, as there is quite some confusion in both data file formats as well as hardware sample formats about whether to stuff the oldest bit in the MSB or the LSB of a byte, ALSA defines that now to be in the MSB, as far as the ALSA userspace layer is concerned. The driver learned functions to re-encode the bytes according to what the hardware supports.
I also wrote a very basic test tool which I published here:
https://github.com/zonque/alsa-dsd-player
Thanks, Daniel
Daniel Mack (5): ALSA: add DSD formats ALSA: snd-usb: use ep->stride from urb callbacks ALSA: snd-usb: add support for DSD DOP stream transport ALSA: snd-usb: add support for bit-reversed byte formats ALSA: snd-usb: add quirks handler for DSD streams
All patches look OK to me through a quick glance, so I'm going to apply them later, unless anyone has objection.
thanks,
Takashi
include/sound/pcm.h | 2 + include/uapi/sound/asound.h | 4 +- sound/core/pcm.c | 2 + sound/core/pcm_misc.c | 8 +++ sound/usb/card.h | 8 +++ sound/usb/endpoint.c | 9 ++++ sound/usb/format.c | 3 ++ sound/usb/pcm.c | 118 ++++++++++++++++++++++++++++++++++++------- sound/usb/quirks.c | 28 ++++++++++ sound/usb/quirks.h | 4 ++ 10 files changed, 167 insertions(+), 19 deletions(-)
-- 1.7.10.4
On 17.04.2013 12:59, Takashi Iwai wrote:
At Wed, 17 Apr 2013 00:01:35 +0800, Daniel Mack wrote:
In contrast to the first two approaches, this one keeps the userspace unaware of the actual transport on the bus.
More specifically, the DSD_U16_LE format may be implemented to actually send DOP samples to the device, depending on the quirks entries.
Also, as there is quite some confusion in both data file formats as well as hardware sample formats about whether to stuff the oldest bit in the MSB or the LSB of a byte, ALSA defines that now to be in the MSB, as far as the ALSA userspace layer is concerned. The driver learned functions to re-encode the bytes according to what the hardware supports.
I also wrote a very basic test tool which I published here:
https://github.com/zonque/alsa-dsd-player
Thanks, Daniel
Daniel Mack (5): ALSA: add DSD formats ALSA: snd-usb: use ep->stride from urb callbacks ALSA: snd-usb: add support for DSD DOP stream transport ALSA: snd-usb: add support for bit-reversed byte formats ALSA: snd-usb: add quirks handler for DSD streams
All patches look OK to me through a quick glance, so I'm going to apply them later, unless anyone has objection.
I'd like to wait for another day or so for Jussi to state his oppinion. He seems to be the only one on this list who has ever dealt with DSD on Linux, so we should listen to him ;)
Thanks, Daniel
On 04/17/2013 02:03 PM, Daniel Mack wrote:
I'd like to wait for another day or so for Jussi to state his oppinion.
Looks good to me. Hopefully the necessary details about bit ordering find their way to the ALSA API docs.
I was left thinking about how this:
- if (pcm_format == SNDRV_PCM_FORMAT_DSD_U16_LE && fmt->dsd_dop) {
/*
* When operating in DSD DOP mode, the size of a sample frame
* in hardware differs from the actual physical format width
* because we need to make room for the DOP markers.
*/
frame_bits += channels << 3;
- }
relates to case where the endpoint uses 32-bit int sample format and thus needs a zero pad byte for DoP...
On 17.04.2013 19:01, Jussi Laako wrote:
On 04/17/2013 02:03 PM, Daniel Mack wrote:
I'd like to wait for another day or so for Jussi to state his oppinion.
Looks good to me. Hopefully the necessary details about bit ordering find their way to the ALSA API docs.
You volunteer for sending a patch? :)
I was left thinking about how this:
- if (pcm_format == SNDRV_PCM_FORMAT_DSD_U16_LE && fmt->dsd_dop) {
/*
* When operating in DSD DOP mode, the size of a sample frame
* in hardware differs from the actual physical format width
* because we need to make room for the DOP markers.
*/
frame_bits += channels << 3;
- }
relates to case where the endpoint uses 32-bit int sample format and thus needs a zero pad byte for DoP...
Ok, that's a case I haven't though of yet, but we can easily extend that logic once such a device is used by anyone. For now, this entire logic is only relevant for one series of devices, and for that, we know the parameters exactly.
The important thing is that we don't have to break the userspace API, but I think we're safe here, right?
Thanks, Daniel
On 04/17/2013 08:04 PM, Daniel Mack wrote:
You volunteer for sending a patch? :)
I have to admit I have no idea from where and how ALSA API docs are generated.
Ok, that's a case I haven't though of yet, but we can easily extend that logic once such a device is used by anyone. For now, this entire logic is only relevant for one series of devices, and for that, we know the parameters exactly.
Over 50% of devices I've seen use 32-bit sample formats for DoP, including the one I regularly use with Linux. That's why I asked...
Currently most popular OEM blocks are XMOS and M2Tech hiFace which both use 32-bit sample format.
The important thing is that we don't have to break the userspace API, but I think we're safe here, right?
Yes, it looks good to me.
At Wed, 17 Apr 2013 20:13:45 +0300, Jussi Laako wrote:
On 04/17/2013 08:04 PM, Daniel Mack wrote:
You volunteer for sending a patch? :)
I have to admit I have no idea from where and how ALSA API docs are generated.
Ok, that's a case I haven't though of yet, but we can easily extend that logic once such a device is used by anyone. For now, this entire logic is only relevant for one series of devices, and for that, we know the parameters exactly.
Over 50% of devices I've seen use 32-bit sample formats for DoP, including the one I regularly use with Linux. That's why I asked...
Currently most popular OEM blocks are XMOS and M2Tech hiFace which both use 32-bit sample format.
The important thing is that we don't have to break the userspace API, but I think we're safe here, right?
Yes, it looks good to me.
OK, then I merge the patches as is. Let's continue refining the code on git for-next branch.
thanks,
Takashi
participants (3)
-
Daniel Mack
-
Jussi Laako
-
Takashi Iwai