diff mbox series

[v2,2/4] ALSA: usb-audio: AudioStreaming Power Domain parsing

Message ID 20180730092336.18741-3-jorge.sanjuan@codethink.co.uk (mailing list archive)
State New, archived
Headers show
Series usb-audio: Add UAC3 Power Domains | expand

Commit Message

Jorge Sanjuan July 30, 2018, 9:23 a.m. UTC
Power Domains in the UAC3 spec are mainly intended to be
associated to an Input or Output Terminal so the host
changes the power state of the entire capture or playback
path within the topology.

This patch adds support for finding Power Domains associated
to an Audio Streaming Interface (bTerminalLink) and adds a
reference to them in the usb audio substreams (snd_usb_substream).

Signed-off-by: Jorge Sanjuan <jorge.sanjuan@codethink.co.uk>
---
 sound/usb/card.h   |  2 ++
 sound/usb/stream.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 60 insertions(+), 8 deletions(-)
diff mbox series

Patch

diff --git a/sound/usb/card.h b/sound/usb/card.h
index 9b41b7dda84f..ac785d15ced4 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -37,6 +37,7 @@  struct audioformat {
 
 struct snd_usb_substream;
 struct snd_usb_endpoint;
+struct snd_usb_power_domain;
 
 struct snd_urb_ctx {
 	struct urb *urb;
@@ -115,6 +116,7 @@  struct snd_usb_substream {
 	int interface;	/* current interface */
 	int endpoint;	/* assigned endpoint */
 	struct audioformat *cur_audiofmt;	/* current audioformat pointer (for hw_params callback) */
+	struct snd_usb_power_domain *str_pd;	/* UAC3 Power Domain for streaming path */
 	snd_pcm_format_t pcm_format;	/* current audio format (for hw_params callback) */
 	unsigned int channels;		/* current number of channels (for hw_params callback) */
 	unsigned int channels_max;	/* max channels in the all audiofmts */
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 @@  snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip,
 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 @@  snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip,
 		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 @@  snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip,
 		}
 	}
 
+	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);