return changed;
 }
 
+/* get the specified endpoint object that is being used by other streams
+ * (i.e. the parameter is locked)
+ */
+static const struct snd_usb_endpoint *
+get_endpoint_in_use(struct snd_usb_audio *chip, int endpoint,
+                   const struct snd_usb_endpoint *ref_ep)
+{
+       const struct snd_usb_endpoint *ep;
+
+       ep = snd_usb_get_endpoint(chip, endpoint);
+       if (ep && ep->cur_audiofmt && (ep != ref_ep || ep->opened > 1))
+               return ep;
+       return NULL;
+}
+
 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 snd_usb_endpoint *ep;
        const struct audioformat *fp;
        struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
        unsigned int rmin, rmax, r;
        list_for_each_entry(fp, &subs->fmt_list, list) {
                if (!hw_check_valid_format(subs, params, fp))
                        continue;
+
+               ep = get_endpoint_in_use(chip, fp->endpoint,
+                                        subs->data_endpoint);
+               if (ep) {
+                       hwc_debug("rate limit %d for ep#%x\n",
+                                 ep->cur_rate, fp->endpoint);
+                       rmin = min(rmin, ep->cur_rate);
+                       rmax = max(rmax, ep->cur_rate);
+                       continue;
+               }
+
+               if (fp->implicit_fb) {
+                       ep = get_endpoint_in_use(chip, fp->sync_ep,
+                                                subs->sync_endpoint);
+                       if (ep) {
+                               hwc_debug("rate limit %d for sync_ep#%x\n",
+                                         ep->cur_rate, fp->sync_ep);
+                               rmin = min(rmin, ep->cur_rate);
+                               rmax = max(rmax, ep->cur_rate);
+                               continue;
+                       }
+               }
+
                r = snd_usb_endpoint_get_clock_rate(chip, fp->clock);
                if (r > 0) {
                        if (!snd_interval_test(it, r))
                          struct snd_pcm_hw_rule *rule)
 {
        struct snd_usb_substream *subs = rule->private;
+       struct snd_usb_audio *chip = subs->stream->chip;
+       const struct snd_usb_endpoint *ep;
        const struct audioformat *fp;
        struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
        u64 fbits;
        list_for_each_entry(fp, &subs->fmt_list, list) {
                if (!hw_check_valid_format(subs, params, fp))
                        continue;
+
+               ep = get_endpoint_in_use(chip, fp->endpoint,
+                                        subs->data_endpoint);
+               if (ep) {
+                       hwc_debug("format limit %d for ep#%x\n",
+                                 ep->cur_format, fp->endpoint);
+                       fbits |= pcm_format_to_bits(ep->cur_format);
+                       continue;
+               }
+
+               if (fp->implicit_fb) {
+                       ep = get_endpoint_in_use(chip, fp->sync_ep,
+                                                subs->sync_endpoint);
+                       if (ep) {
+                               hwc_debug("format limit %d for sync_ep#%x\n",
+                                         ep->cur_format, fp->sync_ep);
+                               fbits |= pcm_format_to_bits(ep->cur_format);
+                               continue;
+                       }
+               }
+
                fbits |= fp->formats;
        }
        return apply_hw_params_format_bits(fmt, fbits);
        return apply_hw_params_minmax(it, pmin, UINT_MAX);
 }
 
-/* get the EP or the sync EP for implicit fb when it's already set up */
-static const struct snd_usb_endpoint *
-get_sync_ep_from_substream(struct snd_usb_substream *subs)
+/* additional hw constraints for implicit feedback mode */
+static int hw_rule_period_size_implicit_fb(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;
        const struct snd_usb_endpoint *ep;
+       struct snd_interval *it;
+       unsigned int rmin, rmax;
 
+       it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+       hwc_debug("hw_rule_period_size: (%u,%u)\n", it->min, it->max);
+       rmin = UINT_MAX;
+       rmax = 0;
        list_for_each_entry(fp, &subs->fmt_list, list) {
-               ep = snd_usb_get_endpoint(chip, fp->endpoint);
-               if (ep && ep->cur_audiofmt) {
-                       /* if EP is already opened solely for this substream,
-                        * we still allow us to change the parameter; otherwise
-                        * this substream has to follow the existing parameter
-                        */
-                       if (ep->cur_audiofmt != subs->cur_audiofmt || ep->opened > 1)
-                               return ep;
-               }
-               if (!fp->implicit_fb)
+               if (!hw_check_valid_format(subs, params, fp))
+                       continue;
+               ep = get_endpoint_in_use(chip, fp->endpoint,
+                                        subs->data_endpoint);
+               if (ep) {
+                       hwc_debug("period size limit %d for ep#%x\n",
+                                 ep->cur_period_frames, fp->endpoint);
+                       rmin = min(rmin, ep->cur_period_frames);
+                       rmax = max(rmax, ep->cur_period_frames);
                        continue;
-               /* for the implicit fb, check the sync ep as well */
-               ep = snd_usb_get_endpoint(chip, fp->sync_ep);
-               if (ep && ep->cur_audiofmt) {
-                       /* ditto, if the sync (data) ep is used by others,
-                        * this stream is restricted by the sync ep
-                        */
-                       if (ep != subs->sync_endpoint || ep->opened > 1)
-                               return ep;
                }
-       }
-       return NULL;
-}
 
-/* additional hw constraints for implicit feedback mode */
-static int hw_rule_format_implicit_fb(struct snd_pcm_hw_params *params,
-                                     struct snd_pcm_hw_rule *rule)
-{
-       struct snd_usb_substream *subs = rule->private;
-       const struct snd_usb_endpoint *ep;
-       struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
-
-       ep = get_sync_ep_from_substream(subs);
-       if (!ep)
-               return 0;
-
-       hwc_debug("applying %s\n", __func__);
-       return apply_hw_params_format_bits(fmt, pcm_format_to_bits(ep->cur_format));
-}
-
-static int hw_rule_rate_implicit_fb(struct snd_pcm_hw_params *params,
-                                   struct snd_pcm_hw_rule *rule)
-{
-       struct snd_usb_substream *subs = rule->private;
-       const struct snd_usb_endpoint *ep;
-       struct snd_interval *it;
-
-       ep = get_sync_ep_from_substream(subs);
-       if (!ep)
-               return 0;
-
-       hwc_debug("applying %s\n", __func__);
-       it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
-       return apply_hw_params_minmax(it, ep->cur_rate, ep->cur_rate);
-}
-
-static int hw_rule_period_size_implicit_fb(struct snd_pcm_hw_params *params,
-                                          struct snd_pcm_hw_rule *rule)
-{
-       struct snd_usb_substream *subs = rule->private;
-       const struct snd_usb_endpoint *ep;
-       struct snd_interval *it;
-
-       ep = get_sync_ep_from_substream(subs);
-       if (!ep)
-               return 0;
+               if (fp->implicit_fb) {
+                       ep = get_endpoint_in_use(chip, fp->sync_ep,
+                                                subs->sync_endpoint);
+                       if (ep) {
+                               hwc_debug("period size limit %d for sync_ep#%x\n",
+                                         ep->cur_period_frames, fp->sync_ep);
+                               rmin = min(rmin, ep->cur_period_frames);
+                               rmax = max(rmax, ep->cur_period_frames);
+                               continue;
+                       }
+               }
+       }
 
-       hwc_debug("applying %s\n", __func__);
-       it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
-       return apply_hw_params_minmax(it, ep->cur_period_frames,
-                                     ep->cur_period_frames);
+       if (!rmax)
+               return 0; /* no limit by implicit fb */
+       return apply_hw_params_minmax(it, rmin, rmax);
 }
 
 static int hw_rule_periods_implicit_fb(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;
        const struct snd_usb_endpoint *ep;
        struct snd_interval *it;
+       unsigned int rmin, rmax;
 
-       ep = get_sync_ep_from_substream(subs);
-       if (!ep)
-               return 0;
-
-       hwc_debug("applying %s\n", __func__);
        it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIODS);
-       return apply_hw_params_minmax(it, ep->cur_buffer_periods,
-                                     ep->cur_buffer_periods);
+       hwc_debug("hw_rule_periods: (%u,%u)\n", it->min, it->max);
+       rmin = UINT_MAX;
+       rmax = 0;
+       list_for_each_entry(fp, &subs->fmt_list, list) {
+               if (!hw_check_valid_format(subs, params, fp))
+                       continue;
+               ep = get_endpoint_in_use(chip, fp->endpoint,
+                                        subs->data_endpoint);
+               if (ep) {
+                       hwc_debug("periods limit %d for ep#%x\n",
+                                 ep->cur_buffer_periods, fp->endpoint);
+                       rmin = min(rmin, ep->cur_buffer_periods);
+                       rmax = max(rmax, ep->cur_buffer_periods);
+                       continue;
+               }
+
+               if (fp->implicit_fb) {
+                       ep = get_endpoint_in_use(chip, fp->sync_ep,
+                                                subs->sync_endpoint);
+                       if (ep) {
+                               hwc_debug("periods limit %d for sync_ep#%x\n",
+                                         ep->cur_buffer_periods, fp->sync_ep);
+                               rmin = min(rmin, ep->cur_buffer_periods);
+                               rmax = max(rmax, ep->cur_buffer_periods);
+                               continue;
+                       }
+               }
+       }
+
+       if (!rmax)
+               return 0; /* no limit by implicit fb */
+       return apply_hw_params_minmax(it, rmin, rmax);
 }
 
 /*
                return err;
 
        /* additional hw constraints for implicit fb */
-       err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
-                                 hw_rule_format_implicit_fb, subs,
-                                 SNDRV_PCM_HW_PARAM_FORMAT, -1);
-       if (err < 0)
-               return err;
-       err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
-                                 hw_rule_rate_implicit_fb, subs,
-                                 SNDRV_PCM_HW_PARAM_RATE, -1);
-       if (err < 0)
-               return err;
        err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
                                  hw_rule_period_size_implicit_fb, subs,
                                  SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);