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: Clemens Ladisch <clemens@ladisch.de> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
committed by
Takashi Iwai
parent
015eb0b081
commit
29088fef3e
@@ -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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
/* 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,
|
|||||||
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;
|
|
||||||
case UAC_FORMAT_TYPE_I_ALAW:
|
|
||||||
pcm_format = SNDRV_PCM_FORMAT_A_LAW;
|
|
||||||
break;
|
|
||||||
case UAC_FORMAT_TYPE_I_MULAW:
|
|
||||||
pcm_format = SNDRV_PCM_FORMAT_MU_LAW;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
snd_printk(KERN_INFO "%d:%u:%d : unsupported format type %d\n",
|
|
||||||
chip->dev->devnum, fp->iface, fp->altsetting, format);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return pcm_format;
|
if (format & (1 << UAC_FORMAT_TYPE_I_IEEE_FLOAT)) {
|
||||||
|
pcm_formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
|
||||||
|
}
|
||||||
|
if (format & (1 << UAC_FORMAT_TYPE_I_ALAW)) {
|
||||||
|
pcm_formats |= SNDRV_PCM_FMTBIT_A_LAW;
|
||||||
|
}
|
||||||
|
if (format & (1 << UAC_FORMAT_TYPE_I_MULAW)) {
|
||||||
|
pcm_formats |= SNDRV_PCM_FMTBIT_MU_LAW;
|
||||||
|
}
|
||||||
|
if (format & ~0x3f) {
|
||||||
|
snd_printk(KERN_INFO "%d:%u:%d : unsupported format bits %#x\n",
|
||||||
|
chip->dev->devnum, fp->iface, fp->altsetting, format);
|
||||||
|
}
|
||||||
|
return pcm_formats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -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"),
|
|||||||
.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,
|
||||||
|
Reference in New Issue
Block a user