summaryrefslogtreecommitdiff
path: root/sound/usb/stream.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/usb/stream.c')
-rw-r--r--sound/usb/stream.c66
1 files changed, 58 insertions, 8 deletions
diff --git a/sound/usb/stream.c b/sound/usb/stream.c
index 729afd808cc4..c0567fa1e84b 100644
--- a/sound/usb/stream.c
+++ b/sound/usb/stream.c
@@ -37,6 +37,7 @@
#include "format.h"
#include "clock.h"
#include "stream.h"
+#include "power.h"
/*
* free a substream
@@ -53,6 +54,7 @@ static void free_substream(struct snd_usb_substream *subs)
kfree(fp);
}
kfree(subs->rate_list.list);
+ kfree(subs->str_pd);
}
@@ -82,7 +84,8 @@ static void snd_usb_audio_pcm_free(struct snd_pcm *pcm)
static void snd_usb_init_substream(struct snd_usb_stream *as,
int stream,
- struct audioformat *fp)
+ struct audioformat *fp,
+ struct snd_usb_power_domain *pd)
{
struct snd_usb_substream *subs = &as->substream[stream];
@@ -107,6 +110,9 @@ static void snd_usb_init_substream(struct snd_usb_stream *as,
if (fp->channels > subs->channels_max)
subs->channels_max = fp->channels;
+ if (pd)
+ subs->str_pd = pd;
+
snd_usb_preallocate_buffer(subs);
}
@@ -468,9 +474,11 @@ snd_pcm_chmap_elem *convert_chmap_v3(struct uac3_cluster_header_descriptor
* fmt_list and will be freed on the chip instance release. do not free
* fp or do remove it from the substream fmt_list to avoid double-free.
*/
-int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
- int stream,
- struct audioformat *fp)
+static int __snd_usb_add_audio_stream(struct snd_usb_audio *chip,
+ int stream,
+ struct audioformat *fp,
+ struct snd_usb_power_domain *pd)
+
{
struct snd_usb_stream *as;
struct snd_usb_substream *subs;
@@ -498,7 +506,7 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
err = snd_pcm_new_stream(as->pcm, stream, 1);
if (err < 0)
return err;
- snd_usb_init_substream(as, stream, fp);
+ snd_usb_init_substream(as, stream, fp, pd);
return add_chmap(as->pcm, stream, subs);
}
@@ -526,7 +534,7 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
else
strcpy(pcm->name, "USB Audio");
- snd_usb_init_substream(as, stream, fp);
+ snd_usb_init_substream(as, stream, fp, pd);
/*
* Keep using head insertion for M-Audio Audiophile USB (tm) which has a
@@ -544,6 +552,21 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
return add_chmap(pcm, stream, &as->substream[stream]);
}
+int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
+ int stream,
+ struct audioformat *fp)
+{
+ return __snd_usb_add_audio_stream(chip, stream, fp, NULL);
+}
+
+static int snd_usb_add_audio_stream_v3(struct snd_usb_audio *chip,
+ int stream,
+ struct audioformat *fp,
+ struct snd_usb_power_domain *pd)
+{
+ return __snd_usb_add_audio_stream(chip, stream, fp, pd);
+}
+
static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip,
struct usb_host_interface *alts,
int protocol, int iface_no)
@@ -819,6 +842,7 @@ found_clock:
static struct audioformat *
snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip,
struct usb_host_interface *alts,
+ struct snd_usb_power_domain **pd_out,
int iface_no, int altset_idx,
int altno, int stream)
{
@@ -829,6 +853,7 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip,
struct uac3_as_header_descriptor *as = NULL;
struct uac3_hc_descriptor_header hc_header;
struct snd_pcm_chmap_elem *chmap;
+ struct snd_usb_power_domain *pd;
unsigned char badd_profile;
u64 badd_formats = 0;
unsigned int num_channels;
@@ -1008,12 +1033,28 @@ found_clock:
fp->rate_max = UAC3_BADD_SAMPLING_RATE;
fp->rates = SNDRV_PCM_RATE_CONTINUOUS;
+ pd = kzalloc(sizeof(pd), GFP_KERNEL);
+ if (!pd) {
+ kfree(fp->rate_table);
+ kfree(fp);
+ return NULL;
+ }
+ pd->pd_id = (stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ UAC3_BADD_PD_ID10 : UAC3_BADD_PD_ID11;
+ pd->pd_d1d0_rec = UAC3_BADD_PD_RECOVER_D1D0;
+ pd->pd_d2d0_rec = UAC3_BADD_PD_RECOVER_D2D0;
+
} else {
fp->attributes = parse_uac_endpoint_attributes(chip, alts,
UAC_VERSION_3,
iface_no);
+
+ pd = snd_usb_find_power_domain(chip->ctrl_intf,
+ as->bTerminalLink);
+
/* ok, let's parse further... */
if (snd_usb_parse_audio_format_v3(chip, fp, as, stream) < 0) {
+ kfree(pd);
kfree(fp->chmap);
kfree(fp->rate_table);
kfree(fp);
@@ -1021,6 +1062,9 @@ found_clock:
}
}
+ if (pd)
+ *pd_out = pd;
+
return fp;
}
@@ -1032,6 +1076,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
struct usb_interface_descriptor *altsd;
int i, altno, err, stream;
struct audioformat *fp = NULL;
+ struct snd_usb_power_domain *pd = NULL;
int num, protocol;
dev = chip->dev;
@@ -1114,7 +1159,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
break;
}
case UAC_VERSION_3:
- fp = snd_usb_get_audioformat_uac3(chip, alts,
+ fp = snd_usb_get_audioformat_uac3(chip, alts, &pd,
iface_no, i, altno, stream);
break;
}
@@ -1125,9 +1170,14 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
return PTR_ERR(fp);
dev_dbg(&dev->dev, "%u:%d: add audio endpoint %#x\n", iface_no, altno, fp->endpoint);
- err = snd_usb_add_audio_stream(chip, stream, fp);
+ if (protocol == UAC_VERSION_3)
+ err = snd_usb_add_audio_stream_v3(chip, stream, fp, pd);
+ else
+ err = snd_usb_add_audio_stream(chip, stream, fp);
+
if (err < 0) {
list_del(&fp->list); /* unlink for avoiding double-free */
+ kfree(pd);
kfree(fp->rate_table);
kfree(fp->chmap);
kfree(fp);