Commit 7edf3b5e authored by Jorge Sanjuan's avatar Jorge Sanjuan Committed by Takashi Iwai

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: default avatarJorge Sanjuan <jorge.sanjuan@codethink.co.uk>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 11785ef5
...@@ -37,6 +37,7 @@ struct audioformat { ...@@ -37,6 +37,7 @@ struct audioformat {
struct snd_usb_substream; struct snd_usb_substream;
struct snd_usb_endpoint; struct snd_usb_endpoint;
struct snd_usb_power_domain;
struct snd_urb_ctx { struct snd_urb_ctx {
struct urb *urb; struct urb *urb;
...@@ -115,6 +116,7 @@ struct snd_usb_substream { ...@@ -115,6 +116,7 @@ struct snd_usb_substream {
int interface; /* current interface */ int interface; /* current interface */
int endpoint; /* assigned endpoint */ int endpoint; /* assigned endpoint */
struct audioformat *cur_audiofmt; /* current audioformat pointer (for hw_params callback) */ 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) */ 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; /* current number of channels (for hw_params callback) */
unsigned int channels_max; /* max channels in the all audiofmts */ unsigned int channels_max; /* max channels in the all audiofmts */
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include "format.h" #include "format.h"
#include "clock.h" #include "clock.h"
#include "stream.h" #include "stream.h"
#include "power.h"
/* /*
* free a substream * free a substream
...@@ -53,6 +54,7 @@ static void free_substream(struct snd_usb_substream *subs) ...@@ -53,6 +54,7 @@ static void free_substream(struct snd_usb_substream *subs)
kfree(fp); kfree(fp);
} }
kfree(subs->rate_list.list); kfree(subs->rate_list.list);
kfree(subs->str_pd);
} }
...@@ -82,7 +84,8 @@ static void snd_usb_audio_pcm_free(struct snd_pcm *pcm) ...@@ -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, static void snd_usb_init_substream(struct snd_usb_stream *as,
int stream, int stream,
struct audioformat *fp) struct audioformat *fp,
struct snd_usb_power_domain *pd)
{ {
struct snd_usb_substream *subs = &as->substream[stream]; struct snd_usb_substream *subs = &as->substream[stream];
...@@ -107,6 +110,9 @@ static void snd_usb_init_substream(struct snd_usb_stream *as, ...@@ -107,6 +110,9 @@ static void snd_usb_init_substream(struct snd_usb_stream *as,
if (fp->channels > subs->channels_max) if (fp->channels > subs->channels_max)
subs->channels_max = fp->channels; subs->channels_max = fp->channels;
if (pd)
subs->str_pd = pd;
snd_usb_preallocate_buffer(subs); snd_usb_preallocate_buffer(subs);
} }
...@@ -468,9 +474,11 @@ snd_pcm_chmap_elem *convert_chmap_v3(struct uac3_cluster_header_descriptor ...@@ -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 * 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. * 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, static int __snd_usb_add_audio_stream(struct snd_usb_audio *chip,
int stream, int stream,
struct audioformat *fp) struct audioformat *fp,
struct snd_usb_power_domain *pd)
{ {
struct snd_usb_stream *as; struct snd_usb_stream *as;
struct snd_usb_substream *subs; struct snd_usb_substream *subs;
...@@ -498,7 +506,7 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip, ...@@ -498,7 +506,7 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
err = snd_pcm_new_stream(as->pcm, stream, 1); err = snd_pcm_new_stream(as->pcm, stream, 1);
if (err < 0) if (err < 0)
return err; return err;
snd_usb_init_substream(as, stream, fp); snd_usb_init_substream(as, stream, fp, pd);
return add_chmap(as->pcm, stream, subs); return add_chmap(as->pcm, stream, subs);
} }
...@@ -526,7 +534,7 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip, ...@@ -526,7 +534,7 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
else else
strcpy(pcm->name, "USB Audio"); 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 * 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, ...@@ -544,6 +552,21 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
return add_chmap(pcm, stream, &as->substream[stream]); 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, static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip,
struct usb_host_interface *alts, struct usb_host_interface *alts,
int protocol, int iface_no) int protocol, int iface_no)
...@@ -819,6 +842,7 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip, ...@@ -819,6 +842,7 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip,
static struct audioformat * static struct audioformat *
snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip, snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip,
struct usb_host_interface *alts, struct usb_host_interface *alts,
struct snd_usb_power_domain **pd_out,
int iface_no, int altset_idx, int iface_no, int altset_idx,
int altno, int stream) int altno, int stream)
{ {
...@@ -829,6 +853,7 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip, ...@@ -829,6 +853,7 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip,
struct uac3_as_header_descriptor *as = NULL; struct uac3_as_header_descriptor *as = NULL;
struct uac3_hc_descriptor_header hc_header; struct uac3_hc_descriptor_header hc_header;
struct snd_pcm_chmap_elem *chmap; struct snd_pcm_chmap_elem *chmap;
struct snd_usb_power_domain *pd;
unsigned char badd_profile; unsigned char badd_profile;
u64 badd_formats = 0; u64 badd_formats = 0;
unsigned int num_channels; unsigned int num_channels;
...@@ -1008,12 +1033,28 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip, ...@@ -1008,12 +1033,28 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip,
fp->rate_max = UAC3_BADD_SAMPLING_RATE; fp->rate_max = UAC3_BADD_SAMPLING_RATE;
fp->rates = SNDRV_PCM_RATE_CONTINUOUS; 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 { } else {
fp->attributes = parse_uac_endpoint_attributes(chip, alts, fp->attributes = parse_uac_endpoint_attributes(chip, alts,
UAC_VERSION_3, UAC_VERSION_3,
iface_no); iface_no);
pd = snd_usb_find_power_domain(chip->ctrl_intf,
as->bTerminalLink);
/* ok, let's parse further... */ /* ok, let's parse further... */
if (snd_usb_parse_audio_format_v3(chip, fp, as, stream) < 0) { if (snd_usb_parse_audio_format_v3(chip, fp, as, stream) < 0) {
kfree(pd);
kfree(fp->chmap); kfree(fp->chmap);
kfree(fp->rate_table); kfree(fp->rate_table);
kfree(fp); kfree(fp);
...@@ -1021,6 +1062,9 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip, ...@@ -1021,6 +1062,9 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip,
} }
} }
if (pd)
*pd_out = pd;
return fp; return fp;
} }
...@@ -1032,6 +1076,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) ...@@ -1032,6 +1076,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
struct usb_interface_descriptor *altsd; struct usb_interface_descriptor *altsd;
int i, altno, err, stream; int i, altno, err, stream;
struct audioformat *fp = NULL; struct audioformat *fp = NULL;
struct snd_usb_power_domain *pd = NULL;
int num, protocol; int num, protocol;
dev = chip->dev; dev = chip->dev;
...@@ -1114,7 +1159,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) ...@@ -1114,7 +1159,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
break; break;
} }
case UAC_VERSION_3: 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); iface_no, i, altno, stream);
break; break;
} }
...@@ -1125,9 +1170,14 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) ...@@ -1125,9 +1170,14 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
return PTR_ERR(fp); return PTR_ERR(fp);
dev_dbg(&dev->dev, "%u:%d: add audio endpoint %#x\n", iface_no, altno, fp->endpoint); 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) { if (err < 0) {
list_del(&fp->list); /* unlink for avoiding double-free */ list_del(&fp->list); /* unlink for avoiding double-free */
kfree(pd);
kfree(fp->rate_table); kfree(fp->rate_table);
kfree(fp->chmap); kfree(fp->chmap);
kfree(fp); kfree(fp);
......
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