summaryrefslogtreecommitdiff
path: root/sound/usb
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2021-09-29 11:08:36 +0300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2021-12-08 11:04:36 +0300
commit8f0a376b2eaac189e4ad72fcea05175d9c5f81b2 (patch)
tree5661aab06ca3a55a9bc5a7ae4c808cd92d5a96ab /sound/usb
parenta2547651bc896f95a3680a6a0a27401e7c7a1080 (diff)
downloadlinux-8f0a376b2eaac189e4ad72fcea05175d9c5f81b2.tar.xz
ALSA: usb-audio: Restrict rates for the shared clocks
commit 4e7cf1fbb34ecb472c073980458cbe413afd4d64 upstream. When a single clock source is shared among several endpoints, we have to keep the same rate on all active endpoints as long as the clock is being used. For dealing with such a case, this patch adds one more check in the hw params constraint for the rate to take the shared clocks into account. The current rate is evaluated from the endpoint list that applies the same clock source. BugLink: https://bugzilla.suse.com/show_bug.cgi?id=1190418 Link: https://lore.kernel.org/r/20210929080844.11583-2-tiwai@suse.de Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'sound/usb')
-rw-r--r--sound/usb/card.h1
-rw-r--r--sound/usb/endpoint.c21
-rw-r--r--sound/usb/endpoint.h1
-rw-r--r--sound/usb/pcm.c9
4 files changed, 32 insertions, 0 deletions
diff --git a/sound/usb/card.h b/sound/usb/card.h
index 860faaf249ea..746a765b2437 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -137,6 +137,7 @@ struct snd_usb_endpoint {
unsigned int cur_period_frames;
unsigned int cur_period_bytes;
unsigned int cur_buffer_periods;
+ unsigned char cur_clock;
spinlock_t lock;
struct list_head list;
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index ba2d7e688420..06241568abf7 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -726,6 +726,7 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip,
ep->cur_period_frames = params_period_size(params);
ep->cur_period_bytes = ep->cur_period_frames * ep->cur_frame_bytes;
ep->cur_buffer_periods = params_periods(params);
+ ep->cur_clock = fp->clock;
if (ep->type == SND_USB_ENDPOINT_TYPE_SYNC)
endpoint_set_syncinterval(chip, ep);
@@ -837,6 +838,7 @@ void snd_usb_endpoint_close(struct snd_usb_audio *chip,
ep->altsetting = 0;
ep->cur_audiofmt = NULL;
ep->cur_rate = 0;
+ ep->cur_clock = 0;
ep->iface_ref = NULL;
usb_audio_dbg(chip, "EP 0x%x closed\n", ep->ep_num);
}
@@ -1344,6 +1346,25 @@ unlock:
return err;
}
+/* get the current rate set to the given clock by any endpoint */
+int snd_usb_endpoint_get_clock_rate(struct snd_usb_audio *chip, int clock)
+{
+ struct snd_usb_endpoint *ep;
+ int rate = 0;
+
+ if (!clock)
+ return 0;
+ mutex_lock(&chip->mutex);
+ list_for_each_entry(ep, &chip->ep_list, list) {
+ if (ep->cur_clock == clock && ep->cur_rate) {
+ rate = ep->cur_rate;
+ break;
+ }
+ }
+ mutex_unlock(&chip->mutex);
+ return rate;
+}
+
/**
* snd_usb_endpoint_start: start an snd_usb_endpoint
*
diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h
index a668f675b52b..a1099ec37e1c 100644
--- a/sound/usb/endpoint.h
+++ b/sound/usb/endpoint.h
@@ -19,6 +19,7 @@ void snd_usb_endpoint_close(struct snd_usb_audio *chip,
struct snd_usb_endpoint *ep);
int snd_usb_endpoint_configure(struct snd_usb_audio *chip,
struct snd_usb_endpoint *ep);
+int snd_usb_endpoint_get_clock_rate(struct snd_usb_audio *chip, int clock);
bool snd_usb_endpoint_compatible(struct snd_usb_audio *chip,
struct snd_usb_endpoint *ep,
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 5dc9266180e3..19392117de9e 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -734,6 +734,7 @@ static int hw_rule_rate(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
struct snd_usb_substream *subs = rule->private;
+ struct snd_usb_audio *chip = subs->stream->chip;
const struct audioformat *fp;
struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
unsigned int rmin, rmax, r;
@@ -745,6 +746,14 @@ static int hw_rule_rate(struct snd_pcm_hw_params *params,
list_for_each_entry(fp, &subs->fmt_list, list) {
if (!hw_check_valid_format(subs, params, fp))
continue;
+ r = snd_usb_endpoint_get_clock_rate(chip, fp->clock);
+ if (r > 0) {
+ if (!snd_interval_test(it, r))
+ continue;
+ rmin = min(rmin, r);
+ rmax = max(rmax, r);
+ continue;
+ }
if (fp->rate_table && fp->nr_rates) {
for (i = 0; i < fp->nr_rates; i++) {
r = fp->rate_table[i];