ALSA: usb-audio: AudioStreaming Power Domain parsing

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>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Jorge Sanjuan 2018-07-31 13:28:43 +01:00 committed by Takashi Iwai
parent 11785ef532
commit 7edf3b5e6a
2 changed files with 60 additions and 8 deletions

View File

@ -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 */

View File

@ -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);