Commit 9a2fe9b8 authored by Ruslan Bilovol's avatar Ruslan Bilovol Committed by Takashi Iwai

ALSA: usb: initial USB Audio Device Class 3.0 support

Recently released USB Audio Class 3.0 specification
introduces many significant changes comparing to
previous versions, like
 - new Power Domains, support for LPM/L1
 - new Cluster descriptor
 - changed layout of all class-specific descriptors
 - new High Capability descriptors
 - New class-specific String descriptors
 - new and removed units
 - additional sources for interrupts
 - removed Type II Audio Data Formats
 - ... and many other things (check spec)

It also provides backward compatibility through
multiple configurations, as well as requires
mandatory support for BADD (Basic Audio Device
Definition) on each ADC3.0 compliant device

This patch adds initial support of UAC3 specification
that is enough for Generic I/O Profile (BAOF, BAIF)
device support from BADD document.
Signed-off-by: default avatarRuslan Bilovol <ruslan.bilovol@gmail.com>
Reviewed-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent ceb18f51
...@@ -34,12 +34,12 @@ ...@@ -34,12 +34,12 @@
* *
*/ */
static inline bool uac2_control_is_readable(u32 bmControls, u8 control) static inline bool uac_v2v3_control_is_readable(u32 bmControls, u8 control)
{ {
return (bmControls >> (control * 2)) & 0x1; return (bmControls >> (control * 2)) & 0x1;
} }
static inline bool uac2_control_is_writeable(u32 bmControls, u8 control) static inline bool uac_v2v3_control_is_writeable(u32 bmControls, u8 control)
{ {
return (bmControls >> (control * 2)) & 0x2; return (bmControls >> (control * 2)) & 0x2;
} }
......
This diff is collapsed.
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
/* bInterfaceProtocol values to denote the version of the standard used */ /* bInterfaceProtocol values to denote the version of the standard used */
#define UAC_VERSION_1 0x00 #define UAC_VERSION_1 0x00
#define UAC_VERSION_2 0x20 #define UAC_VERSION_2 0x20
#define UAC_VERSION_3 0x30
/* A.2 Audio Interface Subclass Codes */ /* A.2 Audio Interface Subclass Codes */
#define USB_SUBCLASS_AUDIOCONTROL 0x01 #define USB_SUBCLASS_AUDIOCONTROL 0x01
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
* Alan Cox (alan@lxorguk.ukuu.org.uk) * Alan Cox (alan@lxorguk.ukuu.org.uk)
* Thomas Sailer (sailer@ife.ee.ethz.ch) * Thomas Sailer (sailer@ife.ee.ethz.ch)
* *
* Audio Class 3.0 support by Ruslan Bilovol <ruslan.bilovol@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
...@@ -44,6 +45,7 @@ ...@@ -44,6 +45,7 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/usb/audio.h> #include <linux/usb/audio.h>
#include <linux/usb/audio-v2.h> #include <linux/usb/audio-v2.h>
#include <linux/usb/audio-v3.h>
#include <linux/module.h> #include <linux/module.h>
#include <sound/control.h> #include <sound/control.h>
...@@ -281,7 +283,8 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) ...@@ -281,7 +283,8 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
break; break;
} }
case UAC_VERSION_2: { case UAC_VERSION_2:
case UAC_VERSION_3: {
struct usb_interface_assoc_descriptor *assoc = struct usb_interface_assoc_descriptor *assoc =
usb_ifnum_to_if(dev, ctrlif)->intf_assoc; usb_ifnum_to_if(dev, ctrlif)->intf_assoc;
...@@ -301,7 +304,7 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) ...@@ -301,7 +304,7 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
} }
if (!assoc) { if (!assoc) {
dev_err(&dev->dev, "Audio class v2 interfaces need an interface association\n"); dev_err(&dev->dev, "Audio class v2/v3 interfaces need an interface association\n");
return -EINVAL; return -EINVAL;
} }
......
...@@ -22,7 +22,7 @@ struct audioformat { ...@@ -22,7 +22,7 @@ struct audioformat {
unsigned char endpoint; /* endpoint */ unsigned char endpoint; /* endpoint */
unsigned char ep_attr; /* endpoint attributes */ unsigned char ep_attr; /* endpoint attributes */
unsigned char datainterval; /* log_2 of data packet interval */ unsigned char datainterval; /* log_2 of data packet interval */
unsigned char protocol; /* UAC_VERSION_1/2 */ unsigned char protocol; /* UAC_VERSION_1/2/3 */
unsigned int maxpacksize; /* max. packet size */ unsigned int maxpacksize; /* max. packet size */
unsigned int rates; /* rate bitmasks */ unsigned int rates; /* rate bitmasks */
unsigned int rate_min, rate_max; /* min/max rates */ unsigned int rate_min, rate_max; /* min/max rates */
......
This diff is collapsed.
...@@ -6,7 +6,7 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface, ...@@ -6,7 +6,7 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
struct usb_host_interface *alts, struct usb_host_interface *alts,
struct audioformat *fmt, int rate); struct audioformat *fmt, int rate);
int snd_usb_clock_find_source(struct snd_usb_audio *chip, int entity_id, int snd_usb_clock_find_source(struct snd_usb_audio *chip, int protocol,
bool validate); int entity_id, bool validate);
#endif /* __USBAUDIO_CLOCK_H */ #endif /* __USBAUDIO_CLOCK_H */
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/usb/audio.h> #include <linux/usb/audio.h>
#include <linux/usb/audio-v2.h> #include <linux/usb/audio-v2.h>
#include <linux/usb/audio-v3.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/pcm.h> #include <sound/pcm.h>
...@@ -39,11 +40,11 @@ ...@@ -39,11 +40,11 @@
* @dev: usb device * @dev: usb device
* @fp: audioformat record * @fp: audioformat record
* @format: the format tag (wFormatTag) * @format: the format tag (wFormatTag)
* @fmt: the format type descriptor * @fmt: the format type descriptor (v1/v2) or AudioStreaming descriptor (v3)
*/ */
static u64 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,
unsigned int format, void *_fmt) u64 format, void *_fmt)
{ {
int sample_width, sample_bytes; int sample_width, sample_bytes;
u64 pcm_formats = 0; u64 pcm_formats = 0;
...@@ -69,6 +70,18 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip, ...@@ -69,6 +70,18 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
format <<= 1; format <<= 1;
break; break;
} }
case UAC_VERSION_3: {
struct uac3_as_header_descriptor *as = _fmt;
sample_width = as->bBitResolution;
sample_bytes = as->bSubslotSize;
if (format & UAC3_FORMAT_TYPE_I_RAW_DATA)
pcm_formats |= SNDRV_PCM_FMTBIT_SPECIAL;
format <<= 1;
break;
}
} }
if ((pcm_formats == 0) && if ((pcm_formats == 0) &&
...@@ -137,7 +150,7 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip, ...@@ -137,7 +150,7 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
} }
if (format & ~0x3f) { if (format & ~0x3f) {
usb_audio_info(chip, usb_audio_info(chip,
"%u:%d : unsupported format bits %#x\n", "%u:%d : unsupported format bits %#llx\n",
fp->iface, fp->altsetting, format); fp->iface, fp->altsetting, format);
} }
...@@ -281,15 +294,16 @@ static int parse_uac2_sample_rate_range(struct snd_usb_audio *chip, ...@@ -281,15 +294,16 @@ static int parse_uac2_sample_rate_range(struct snd_usb_audio *chip,
/* /*
* parse the format descriptor and stores the possible sample rates * parse the format descriptor and stores the possible sample rates
* on the audioformat table (audio class v2). * on the audioformat table (audio class v2 and v3).
*/ */
static int parse_audio_format_rates_v2(struct snd_usb_audio *chip, static int parse_audio_format_rates_v2v3(struct snd_usb_audio *chip,
struct audioformat *fp) struct audioformat *fp)
{ {
struct usb_device *dev = chip->dev; struct usb_device *dev = chip->dev;
unsigned char tmp[2], *data; unsigned char tmp[2], *data;
int nr_triplets, data_size, ret = 0; int nr_triplets, data_size, ret = 0;
int clock = snd_usb_clock_find_source(chip, fp->clock, false); int clock = snd_usb_clock_find_source(chip, fp->protocol,
fp->clock, false);
if (clock < 0) { if (clock < 0) {
dev_err(&dev->dev, dev_err(&dev->dev,
...@@ -368,13 +382,30 @@ static int parse_audio_format_rates_v2(struct snd_usb_audio *chip, ...@@ -368,13 +382,30 @@ static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
* parse the format type I and III descriptors * parse the format type I and III descriptors
*/ */
static int parse_audio_format_i(struct snd_usb_audio *chip, static int parse_audio_format_i(struct snd_usb_audio *chip,
struct audioformat *fp, unsigned int format, struct audioformat *fp, u64 format,
struct uac_format_type_i_continuous_descriptor *fmt) void *_fmt)
{ {
snd_pcm_format_t pcm_format; snd_pcm_format_t pcm_format;
unsigned int fmt_type;
int ret; int ret;
if (fmt->bFormatType == UAC_FORMAT_TYPE_III) { switch (fp->protocol) {
default:
case UAC_VERSION_1:
case UAC_VERSION_2: {
struct uac_format_type_i_continuous_descriptor *fmt = _fmt;
fmt_type = fmt->bFormatType;
break;
}
case UAC_VERSION_3: {
/* fp->fmt_type is already set in this case */
fmt_type = fp->fmt_type;
break;
}
}
if (fmt_type == UAC_FORMAT_TYPE_III) {
/* FIXME: the format type is really IECxxx /* FIXME: the format type is really IECxxx
* but we give normal PCM format to get the existing * but we give normal PCM format to get the existing
* apps working... * apps working...
...@@ -393,7 +424,7 @@ static int parse_audio_format_i(struct snd_usb_audio *chip, ...@@ -393,7 +424,7 @@ static int parse_audio_format_i(struct snd_usb_audio *chip,
} }
fp->formats = pcm_format_to_bits(pcm_format); fp->formats = pcm_format_to_bits(pcm_format);
} else { } else {
fp->formats = parse_audio_format_i_type(chip, fp, format, fmt); fp->formats = parse_audio_format_i_type(chip, fp, format, _fmt);
if (!fp->formats) if (!fp->formats)
return -EINVAL; return -EINVAL;
} }
...@@ -405,15 +436,20 @@ static int parse_audio_format_i(struct snd_usb_audio *chip, ...@@ -405,15 +436,20 @@ static int parse_audio_format_i(struct snd_usb_audio *chip,
*/ */
switch (fp->protocol) { switch (fp->protocol) {
default: default:
case UAC_VERSION_1: case UAC_VERSION_1: {
struct uac_format_type_i_continuous_descriptor *fmt = _fmt;
fp->channels = fmt->bNrChannels; fp->channels = fmt->bNrChannels;
ret = parse_audio_format_rates_v1(chip, fp, (unsigned char *) fmt, 7); ret = parse_audio_format_rates_v1(chip, fp, (unsigned char *) fmt, 7);
break; break;
}
case UAC_VERSION_2: case UAC_VERSION_2:
case UAC_VERSION_3: {
/* fp->channels is already set in this case */ /* fp->channels is already set in this case */
ret = parse_audio_format_rates_v2(chip, fp); ret = parse_audio_format_rates_v2v3(chip, fp);
break; break;
} }
}
if (fp->channels < 1) { if (fp->channels < 1) {
usb_audio_err(chip, usb_audio_err(chip,
...@@ -430,7 +466,7 @@ static int parse_audio_format_i(struct snd_usb_audio *chip, ...@@ -430,7 +466,7 @@ static int parse_audio_format_i(struct snd_usb_audio *chip,
*/ */
static int parse_audio_format_ii(struct snd_usb_audio *chip, static int parse_audio_format_ii(struct snd_usb_audio *chip,
struct audioformat *fp, struct audioformat *fp,
int format, void *_fmt) u64 format, void *_fmt)
{ {
int brate, framesize, ret; int brate, framesize, ret;
...@@ -445,7 +481,7 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip, ...@@ -445,7 +481,7 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip,
break; break;
default: default:
usb_audio_info(chip, usb_audio_info(chip,
"%u:%d : unknown format tag %#x is detected. processed as MPEG.\n", "%u:%d : unknown format tag %#llx is detected. processed as MPEG.\n",
fp->iface, fp->altsetting, format); fp->iface, fp->altsetting, format);
fp->formats = SNDRV_PCM_FMTBIT_MPEG; fp->formats = SNDRV_PCM_FMTBIT_MPEG;
break; break;
...@@ -470,7 +506,7 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip, ...@@ -470,7 +506,7 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip,
framesize = le16_to_cpu(fmt->wSamplesPerFrame); framesize = le16_to_cpu(fmt->wSamplesPerFrame);
usb_audio_info(chip, "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize); usb_audio_info(chip, "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize);
fp->frame_size = framesize; fp->frame_size = framesize;
ret = parse_audio_format_rates_v2(chip, fp); ret = parse_audio_format_rates_v2v3(chip, fp);
break; break;
} }
} }
...@@ -479,7 +515,7 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip, ...@@ -479,7 +515,7 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip,
} }
int snd_usb_parse_audio_format(struct snd_usb_audio *chip, int snd_usb_parse_audio_format(struct snd_usb_audio *chip,
struct audioformat *fp, unsigned int format, struct audioformat *fp, u64 format,
struct uac_format_type_i_continuous_descriptor *fmt, struct uac_format_type_i_continuous_descriptor *fmt,
int stream) int stream)
{ {
...@@ -520,3 +556,26 @@ int snd_usb_parse_audio_format(struct snd_usb_audio *chip, ...@@ -520,3 +556,26 @@ int snd_usb_parse_audio_format(struct snd_usb_audio *chip,
return 0; return 0;
} }
int snd_usb_parse_audio_format_v3(struct snd_usb_audio *chip,
struct audioformat *fp,
struct uac3_as_header_descriptor *as,
int stream)
{
u64 format = le64_to_cpu(as->bmFormats);
int err;
/*
* Type I format bits are D0..D6
* This test works because type IV is not supported
*/
if (format & 0x7f)
fp->fmt_type = UAC_FORMAT_TYPE_I;
else
fp->fmt_type = UAC_FORMAT_TYPE_III;
err = parse_audio_format_i(chip, fp, format, as);
if (err < 0)
return err;
return 0;
}
...@@ -3,8 +3,12 @@ ...@@ -3,8 +3,12 @@
#define __USBAUDIO_FORMAT_H #define __USBAUDIO_FORMAT_H
int snd_usb_parse_audio_format(struct snd_usb_audio *chip, int snd_usb_parse_audio_format(struct snd_usb_audio *chip,
struct audioformat *fp, unsigned int format, struct audioformat *fp, u64 format,
struct uac_format_type_i_continuous_descriptor *fmt, struct uac_format_type_i_continuous_descriptor *fmt,
int stream); int stream);
int snd_usb_parse_audio_format_v3(struct snd_usb_audio *chip,
struct audioformat *fp,
struct uac3_as_header_descriptor *as,
int stream);
#endif /* __USBAUDIO_FORMAT_H */ #endif /* __USBAUDIO_FORMAT_H */
This diff is collapsed.
This diff is collapsed.
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