Skip to content

Commit

Permalink
ALSA: usb-audio: Restrict rates for the shared clocks
Browse files Browse the repository at this point in the history
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/[email protected]
Signed-off-by: Takashi Iwai <[email protected]>
  • Loading branch information
tiwai committed Sep 30, 2021
1 parent c4ca387 commit 4e7cf1f
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 0 deletions.
1 change: 1 addition & 0 deletions sound/usb/card.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,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;
Expand Down
21 changes: 21 additions & 0 deletions sound/usb/endpoint.c
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,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);
Expand Down Expand Up @@ -833,6 +834,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);
}
Expand Down Expand Up @@ -1340,6 +1342,25 @@ int snd_usb_endpoint_configure(struct snd_usb_audio *chip,
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
*
Expand Down
1 change: 1 addition & 0 deletions sound/usb/endpoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
9 changes: 9 additions & 0 deletions sound/usb/pcm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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];
Expand Down

0 comments on commit 4e7cf1f

Please sign in to comment.