summaryrefslogtreecommitdiff
path: root/sound/usb/format.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/usb/format.c')
-rw-r--r--sound/usb/format.c93
1 files changed, 65 insertions, 28 deletions
diff --git a/sound/usb/format.c b/sound/usb/format.c
index 405dc0bf6678..ec95a063beb1 100644
--- a/sound/usb/format.c
+++ b/sound/usb/format.c
@@ -39,8 +39,12 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
case UAC_VERSION_1:
default: {
struct uac_format_type_i_discrete_descriptor *fmt = _fmt;
- if (format >= 64)
- return 0; /* invalid format */
+ if (format >= 64) {
+ usb_audio_info(chip,
+ "%u:%d: invalid format type 0x%llx is detected, processed as PCM\n",
+ fp->iface, fp->altsetting, format);
+ format = UAC_FORMAT_TYPE_I_PCM;
+ }
sample_width = fmt->bBitResolution;
sample_bytes = fmt->bSubframeSize;
format = 1ULL << format;
@@ -56,6 +60,8 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
pcm_formats |= SNDRV_PCM_FMTBIT_SPECIAL;
/* flag potentially raw DSD capable altsettings */
fp->dsd_raw = true;
+ /* clear special format bit to avoid "unsupported format" msg below */
+ format &= ~UAC2_FORMAT_TYPE_I_RAW_DATA;
}
format <<= 1;
@@ -67,8 +73,11 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
sample_width = as->bBitResolution;
sample_bytes = as->bSubslotSize;
- if (format & UAC3_FORMAT_TYPE_I_RAW_DATA)
+ if (format & UAC3_FORMAT_TYPE_I_RAW_DATA) {
pcm_formats |= SNDRV_PCM_FMTBIT_SPECIAL;
+ /* clear special format bit to avoid "unsupported format" msg below */
+ format &= ~UAC3_FORMAT_TYPE_I_RAW_DATA;
+ }
format <<= 1;
break;
@@ -76,15 +85,16 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
}
fp->fmt_bits = sample_width;
+ fp->fmt_sz = sample_bytes;
if ((pcm_formats == 0) &&
- (format == 0 || format == (1 << UAC_FORMAT_TYPE_I_UNDEFINED))) {
+ (format == 0 || format == BIT(UAC_FORMAT_TYPE_I_UNDEFINED))) {
/* some devices don't define this correctly... */
usb_audio_info(chip, "%u:%d : format type 0 is detected, processed as PCM\n",
fp->iface, fp->altsetting);
- format = 1 << UAC_FORMAT_TYPE_I_PCM;
+ format = BIT(UAC_FORMAT_TYPE_I_PCM);
}
- if (format & (1 << UAC_FORMAT_TYPE_I_PCM)) {
+ if (format & BIT(UAC_FORMAT_TYPE_I_PCM)) {
if (((chip->usb_id == USB_ID(0x0582, 0x0016)) ||
/* Edirol SD-90 */
(chip->usb_id == USB_ID(0x0582, 0x000c))) &&
@@ -124,7 +134,7 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
break;
}
}
- if (format & (1 << UAC_FORMAT_TYPE_I_PCM8)) {
+ if (format & BIT(UAC_FORMAT_TYPE_I_PCM8)) {
/* Dallas DS4201 workaround: it advertises U8 format, but really
supports S8. */
if (chip->usb_id == USB_ID(0x04fa, 0x4201))
@@ -132,15 +142,12 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
else
pcm_formats |= SNDRV_PCM_FMTBIT_U8;
}
- if (format & (1 << UAC_FORMAT_TYPE_I_IEEE_FLOAT)) {
+ if (format & BIT(UAC_FORMAT_TYPE_I_IEEE_FLOAT))
pcm_formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
- }
- if (format & (1 << UAC_FORMAT_TYPE_I_ALAW)) {
+ if (format & BIT(UAC_FORMAT_TYPE_I_ALAW))
pcm_formats |= SNDRV_PCM_FMTBIT_A_LAW;
- }
- if (format & (1 << UAC_FORMAT_TYPE_I_MULAW)) {
+ if (format & BIT(UAC_FORMAT_TYPE_I_MULAW))
pcm_formats |= SNDRV_PCM_FMTBIT_MU_LAW;
- }
if (format & ~0x3f) {
usb_audio_info(chip,
"%u:%d : unsupported format bits %#llx\n",
@@ -254,7 +261,8 @@ static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audiof
}
/* Jabra Evolve 65 headset */
- if (chip->usb_id == USB_ID(0x0b0e, 0x030b)) {
+ if (chip->usb_id == USB_ID(0x0b0e, 0x030b) ||
+ chip->usb_id == USB_ID(0x0b0e, 0x030c)) {
/* only 48kHz for playback while keeping 16kHz for capture */
if (fp->nr_rates != 1)
return set_fixed_rate(fp, 48000, SNDRV_PCM_RATE_48000);
@@ -302,16 +310,14 @@ static bool focusrite_valid_sample_rate(struct snd_usb_audio *chip,
struct audioformat *fp,
unsigned int rate)
{
- struct usb_interface *iface;
struct usb_host_interface *alts;
unsigned char *fmt;
unsigned int max_rate;
- iface = usb_ifnum_to_if(chip->dev, fp->iface);
- if (!iface)
+ alts = snd_usb_get_host_interface(chip, fp->iface, fp->altsetting);
+ if (!alts)
return true;
- alts = &iface->altsetting[fp->altset_idx];
fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen,
NULL, UAC_FORMAT_TYPE);
if (!fmt)
@@ -320,20 +326,24 @@ static bool focusrite_valid_sample_rate(struct snd_usb_audio *chip,
if (fmt[0] == 10) { /* bLength */
max_rate = combine_quad(&fmt[6]);
- /* Validate max rate */
- if (max_rate != 48000 &&
- max_rate != 96000 &&
- max_rate != 192000 &&
- max_rate != 384000) {
-
+ switch (max_rate) {
+ case 192000:
+ if (rate == 176400 || rate == 192000)
+ return true;
+ fallthrough;
+ case 96000:
+ if (rate == 88200 || rate == 96000)
+ return true;
+ fallthrough;
+ case 48000:
+ return (rate == 44100 || rate == 48000);
+ default:
usb_audio_info(chip,
"%u:%d : unexpected max rate: %u\n",
fp->iface, fp->altsetting, max_rate);
return true;
}
-
- return rate <= max_rate;
}
return true;
@@ -378,6 +388,10 @@ static int parse_uac2_sample_rate_range(struct snd_usb_audio *chip,
if (chip->usb_id == USB_ID(0x194f, 0x010c) &&
!s1810c_valid_sample_rate(fp, rate))
goto skip_rate;
+ /* Filter out invalid rates on Presonus Studio 1824c */
+ if (chip->usb_id == USB_ID(0x194f, 0x010d) &&
+ !s1810c_valid_sample_rate(fp, rate))
+ goto skip_rate;
/* Filter out invalid rates on Focusrite devices */
if (USB_ID_VENDOR(chip->usb_id) == 0x1235 &&
@@ -419,6 +433,7 @@ static int line6_parse_audio_format_rates_quirk(struct snd_usb_audio *chip,
case USB_ID(0x0e41, 0x4248): /* Line6 Helix >= fw 2.82 */
case USB_ID(0x0e41, 0x4249): /* Line6 Helix Rack >= fw 2.82 */
case USB_ID(0x0e41, 0x424a): /* Line6 Helix LT >= fw 2.82 */
+ case USB_ID(0x0e41, 0x424b): /* Line6 Pod Go */
case USB_ID(0x19f7, 0x0011): /* Rode Rodecaster Pro */
return set_fixed_rate(fp, 48000, SNDRV_PCM_RATE_48000);
}
@@ -465,9 +480,11 @@ static int validate_sample_rate_table_v2v3(struct snd_usb_audio *chip,
int clock)
{
struct usb_device *dev = chip->dev;
+ struct usb_host_interface *alts;
unsigned int *table;
unsigned int nr_rates;
int i, err;
+ u32 bmControls;
/* performing the rate verification may lead to unexpected USB bus
* behavior afterwards by some unknown reason. Do this only for the
@@ -476,6 +493,24 @@ static int validate_sample_rate_table_v2v3(struct snd_usb_audio *chip,
if (!(chip->quirk_flags & QUIRK_FLAG_VALIDATE_RATES))
return 0; /* don't perform the validation as default */
+ alts = snd_usb_get_host_interface(chip, fp->iface, fp->altsetting);
+ if (!alts)
+ return 0;
+
+ if (fp->protocol == UAC_VERSION_3) {
+ struct uac3_as_header_descriptor *as = snd_usb_find_csint_desc(
+ alts->extra, alts->extralen, NULL, UAC_AS_GENERAL);
+ bmControls = le32_to_cpu(as->bmControls);
+ } else {
+ struct uac2_as_header_descriptor *as = snd_usb_find_csint_desc(
+ alts->extra, alts->extralen, NULL, UAC_AS_GENERAL);
+ bmControls = as->bmControls;
+ }
+
+ if (!uac_v2v3_control_is_readable(bmControls,
+ UAC2_AS_VAL_ALT_SETTINGS))
+ return 0;
+
table = kcalloc(fp->nr_rates, sizeof(*table), GFP_KERNEL);
if (!table)
return -ENOMEM;
@@ -523,7 +558,9 @@ static int parse_audio_format_rates_v2v3(struct snd_usb_audio *chip,
unsigned char tmp[2], *data;
int nr_triplets, data_size, ret = 0, ret_l6;
int clock = snd_usb_clock_find_source(chip, fp, false);
+ struct usb_host_interface *ctrl_intf;
+ ctrl_intf = snd_usb_find_ctrl_interface(chip, fp->iface);
if (clock < 0) {
dev_err(&dev->dev,
"%s(): unable to find clock source (clock %d)\n",
@@ -535,7 +572,7 @@ static int parse_audio_format_rates_v2v3(struct snd_usb_audio *chip,
ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_RANGE,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
UAC2_CS_CONTROL_SAM_FREQ << 8,
- snd_usb_ctrl_intf(chip) | (clock << 8),
+ snd_usb_ctrl_intf(ctrl_intf) | (clock << 8),
tmp, sizeof(tmp));
if (ret < 0) {
@@ -570,7 +607,7 @@ static int parse_audio_format_rates_v2v3(struct snd_usb_audio *chip,
ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_RANGE,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
UAC2_CS_CONTROL_SAM_FREQ << 8,
- snd_usb_ctrl_intf(chip) | (clock << 8),
+ snd_usb_ctrl_intf(ctrl_intf) | (clock << 8),
data, data_size);
if (ret < 0) {