Commit 29088fef authored by Clemens Ladisch's avatar Clemens Ladisch Committed by Takashi Iwai

ALSA: usb-audio: support multiple formats with audio class v2 devices

Change the parser to correctly handle v2 descriptors with multiple
format bits set.
Signed-off-by: default avatarClemens Ladisch <clemens@ladisch.de>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 015eb0b0
...@@ -37,19 +37,20 @@ ...@@ -37,19 +37,20 @@
* @format: the format tag (wFormatTag) * @format: the format tag (wFormatTag)
* @fmt: the format type descriptor * @fmt: the format type descriptor
*/ */
static int parse_audio_format_i_type(struct snd_usb_audio *chip, static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
struct audioformat *fp, struct audioformat *fp,
int format, void *_fmt, int format, void *_fmt,
int protocol) int protocol)
{ {
int pcm_format, i;
int sample_width, sample_bytes; int sample_width, sample_bytes;
u64 pcm_formats;
switch (protocol) { switch (protocol) {
case UAC_VERSION_1: { case UAC_VERSION_1: {
struct uac_format_type_i_discrete_descriptor *fmt = _fmt; struct uac_format_type_i_discrete_descriptor *fmt = _fmt;
sample_width = fmt->bBitResolution; sample_width = fmt->bBitResolution;
sample_bytes = fmt->bSubframeSize; sample_bytes = fmt->bSubframeSize;
format = 1 << format;
break; break;
} }
...@@ -57,24 +58,7 @@ static int parse_audio_format_i_type(struct snd_usb_audio *chip, ...@@ -57,24 +58,7 @@ static int parse_audio_format_i_type(struct snd_usb_audio *chip,
struct uac_format_type_i_ext_descriptor *fmt = _fmt; struct uac_format_type_i_ext_descriptor *fmt = _fmt;
sample_width = fmt->bBitResolution; sample_width = fmt->bBitResolution;
sample_bytes = fmt->bSubslotSize; sample_bytes = fmt->bSubslotSize;
format <<= 1;
/*
* FIXME
* USB audio class v2 devices specify a bitmap of possible
* audio formats rather than one fix value. For now, we just
* pick one of them and report that as the only possible
* value for this setting.
* The bit allocation map is in fact compatible to the
* wFormatTag of the v1 AS streaming descriptors, which is why
* we can simply map the matrix.
*/
for (i = 0; i < 5; i++)
if (format & (1UL << i)) {
format = i + 1;
break;
}
break; break;
} }
...@@ -82,15 +66,15 @@ static int parse_audio_format_i_type(struct snd_usb_audio *chip, ...@@ -82,15 +66,15 @@ static int parse_audio_format_i_type(struct snd_usb_audio *chip,
return -EINVAL; return -EINVAL;
} }
/* FIXME: correct endianess and sign? */ pcm_formats = 0;
pcm_format = -1;
switch (format) { if (format == 0 || format == (1 << UAC_FORMAT_TYPE_I_UNDEFINED)) {
case UAC_FORMAT_TYPE_I_UNDEFINED: /* some devices don't define this correctly... */ /* some devices don't define this correctly... */
snd_printdd(KERN_INFO "%d:%u:%d : format type 0 is detected, processed as PCM\n", snd_printdd(KERN_INFO "%d:%u:%d : format type 0 is detected, processed as PCM\n",
chip->dev->devnum, fp->iface, fp->altsetting); chip->dev->devnum, fp->iface, fp->altsetting);
/* fall-through */ format = 1 << UAC_FORMAT_TYPE_I_PCM;
case UAC_FORMAT_TYPE_I_PCM: }
if (format & (1 << UAC_FORMAT_TYPE_I_PCM)) {
if (sample_width > sample_bytes * 8) { if (sample_width > sample_bytes * 8) {
snd_printk(KERN_INFO "%d:%u:%d : sample bitwidth %d in over sample bytes %d\n", snd_printk(KERN_INFO "%d:%u:%d : sample bitwidth %d in over sample bytes %d\n",
chip->dev->devnum, fp->iface, fp->altsetting, chip->dev->devnum, fp->iface, fp->altsetting,
...@@ -99,22 +83,22 @@ static int parse_audio_format_i_type(struct snd_usb_audio *chip, ...@@ -99,22 +83,22 @@ static int parse_audio_format_i_type(struct snd_usb_audio *chip,
/* check the format byte size */ /* check the format byte size */
switch (sample_bytes) { switch (sample_bytes) {
case 1: case 1:
pcm_format = SNDRV_PCM_FORMAT_S8; pcm_formats |= SNDRV_PCM_FMTBIT_S8;
break; break;
case 2: case 2:
if (snd_usb_is_big_endian_format(chip, fp)) if (snd_usb_is_big_endian_format(chip, fp))
pcm_format = SNDRV_PCM_FORMAT_S16_BE; /* grrr, big endian!! */ pcm_formats |= SNDRV_PCM_FMTBIT_S16_BE; /* grrr, big endian!! */
else else
pcm_format = SNDRV_PCM_FORMAT_S16_LE; pcm_formats |= SNDRV_PCM_FMTBIT_S16_LE;
break; break;
case 3: case 3:
if (snd_usb_is_big_endian_format(chip, fp)) if (snd_usb_is_big_endian_format(chip, fp))
pcm_format = SNDRV_PCM_FORMAT_S24_3BE; /* grrr, big endian!! */ pcm_formats |= SNDRV_PCM_FMTBIT_S24_3BE; /* grrr, big endian!! */
else else
pcm_format = SNDRV_PCM_FORMAT_S24_3LE; pcm_formats |= SNDRV_PCM_FMTBIT_S24_3LE;
break; break;
case 4: case 4:
pcm_format = SNDRV_PCM_FORMAT_S32_LE; pcm_formats |= SNDRV_PCM_FMTBIT_S32_LE;
break; break;
default: default:
snd_printk(KERN_INFO "%d:%u:%d : unsupported sample bitwidth %d in %d bytes\n", snd_printk(KERN_INFO "%d:%u:%d : unsupported sample bitwidth %d in %d bytes\n",
...@@ -122,30 +106,29 @@ static int parse_audio_format_i_type(struct snd_usb_audio *chip, ...@@ -122,30 +106,29 @@ static int parse_audio_format_i_type(struct snd_usb_audio *chip,
sample_width, sample_bytes); sample_width, sample_bytes);
break; break;
} }
break; }
case UAC_FORMAT_TYPE_I_PCM8: if (format & (1 << UAC_FORMAT_TYPE_I_PCM8)) {
pcm_format = SNDRV_PCM_FORMAT_U8;
/* Dallas DS4201 workaround: it advertises U8 format, but really /* Dallas DS4201 workaround: it advertises U8 format, but really
supports S8. */ supports S8. */
if (chip->usb_id == USB_ID(0x04fa, 0x4201)) if (chip->usb_id == USB_ID(0x04fa, 0x4201))
pcm_format = SNDRV_PCM_FORMAT_S8; pcm_formats |= SNDRV_PCM_FMTBIT_S8;
break; else
case UAC_FORMAT_TYPE_I_IEEE_FLOAT: pcm_formats |= SNDRV_PCM_FMTBIT_U8;
pcm_format = SNDRV_PCM_FORMAT_FLOAT_LE; }
break; if (format & (1 << UAC_FORMAT_TYPE_I_IEEE_FLOAT)) {
case UAC_FORMAT_TYPE_I_ALAW: pcm_formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
pcm_format = SNDRV_PCM_FORMAT_A_LAW; }
break; if (format & (1 << UAC_FORMAT_TYPE_I_ALAW)) {
case UAC_FORMAT_TYPE_I_MULAW: pcm_formats |= SNDRV_PCM_FMTBIT_A_LAW;
pcm_format = SNDRV_PCM_FORMAT_MU_LAW; }
break; if (format & (1 << UAC_FORMAT_TYPE_I_MULAW)) {
default: pcm_formats |= SNDRV_PCM_FMTBIT_MU_LAW;
snd_printk(KERN_INFO "%d:%u:%d : unsupported format type %d\n", }
if (format & ~0x3f) {
snd_printk(KERN_INFO "%d:%u:%d : unsupported format bits %#x\n",
chip->dev->devnum, fp->iface, fp->altsetting, format); chip->dev->devnum, fp->iface, fp->altsetting, format);
break;
} }
return pcm_format; return pcm_formats;
} }
...@@ -317,14 +300,14 @@ static int parse_audio_format_i(struct snd_usb_audio *chip, ...@@ -317,14 +300,14 @@ static int parse_audio_format_i(struct snd_usb_audio *chip,
default: default:
pcm_format = SNDRV_PCM_FORMAT_S16_LE; pcm_format = SNDRV_PCM_FORMAT_S16_LE;
} }
fp->formats = 1uLL << pcm_format;
} else { } else {
pcm_format = parse_audio_format_i_type(chip, fp, format, fmt, protocol); fp->formats = parse_audio_format_i_type(chip, fp, format,
if (pcm_format < 0) fmt, protocol);
if (!fp->formats)
return -1; return -1;
} }
fp->formats = 1uLL << pcm_format;
/* gather possible sample rates */ /* gather possible sample rates */
/* audio class v1 reports possible sample rates as part of the /* audio class v1 reports possible sample rates as part of the
* proprietary class specific descriptor. * proprietary class specific descriptor.
......
...@@ -2203,7 +2203,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), ...@@ -2203,7 +2203,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.ifnum = 1, .ifnum = 1,
.type = QUIRK_AUDIO_FIXED_ENDPOINT, .type = QUIRK_AUDIO_FIXED_ENDPOINT,
.data = &(const struct audioformat) { .data = &(const struct audioformat) {
.format = SNDRV_PCM_FORMAT_S24_3BE, .formats = SNDRV_PCM_FMTBIT_S24_3BE,
.channels = 2, .channels = 2,
.iface = 1, .iface = 1,
.altsetting = 1, .altsetting = 1,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment