Skip to content

Commit 37b3e56

Browse files
committed
ALSA: usb-audio: More refactoring of hw constraint rules
Although we applied a workaround for the hw constraints code with the implicit feedback sync, it still has a potential problem. Namely, as the code treats only the first matching (sync) endpoint, it might be too restrictive when multiple endpoints are listed in the substream's format list. This patch is another attempt to improve the hw constraint handling for the implicit feedback sync. The code is rewritten and the sync EP handling for the rate and the format is put inside the fmt_list loop in each hw_rule_*() function instead of the additional rules. The rules for the period size and periods are extended to loop over the fmt_list like others, and they apply the constraints only if needed. Link: https://lore.kernel.org/r/[email protected] Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Takashi Iwai <[email protected]>
1 parent d463ac1 commit 37b3e56

File tree

1 file changed

+131
-87
lines changed

1 file changed

+131
-87
lines changed

sound/usb/pcm.c

Lines changed: 131 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -789,11 +789,27 @@ static int apply_hw_params_minmax(struct snd_interval *it, unsigned int rmin,
789789
return changed;
790790
}
791791

792+
/* get the specified endpoint object that is being used by other streams
793+
* (i.e. the parameter is locked)
794+
*/
795+
static const struct snd_usb_endpoint *
796+
get_endpoint_in_use(struct snd_usb_audio *chip, int endpoint,
797+
const struct snd_usb_endpoint *ref_ep)
798+
{
799+
const struct snd_usb_endpoint *ep;
800+
801+
ep = snd_usb_get_endpoint(chip, endpoint);
802+
if (ep && ep->cur_audiofmt && (ep != ref_ep || ep->opened > 1))
803+
return ep;
804+
return NULL;
805+
}
806+
792807
static int hw_rule_rate(struct snd_pcm_hw_params *params,
793808
struct snd_pcm_hw_rule *rule)
794809
{
795810
struct snd_usb_substream *subs = rule->private;
796811
struct snd_usb_audio *chip = subs->stream->chip;
812+
const struct snd_usb_endpoint *ep;
797813
const struct audioformat *fp;
798814
struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
799815
unsigned int rmin, rmax, r;
@@ -805,6 +821,29 @@ static int hw_rule_rate(struct snd_pcm_hw_params *params,
805821
list_for_each_entry(fp, &subs->fmt_list, list) {
806822
if (!hw_check_valid_format(subs, params, fp))
807823
continue;
824+
825+
ep = get_endpoint_in_use(chip, fp->endpoint,
826+
subs->data_endpoint);
827+
if (ep) {
828+
hwc_debug("rate limit %d for ep#%x\n",
829+
ep->cur_rate, fp->endpoint);
830+
rmin = min(rmin, ep->cur_rate);
831+
rmax = max(rmax, ep->cur_rate);
832+
continue;
833+
}
834+
835+
if (fp->implicit_fb) {
836+
ep = get_endpoint_in_use(chip, fp->sync_ep,
837+
subs->sync_endpoint);
838+
if (ep) {
839+
hwc_debug("rate limit %d for sync_ep#%x\n",
840+
ep->cur_rate, fp->sync_ep);
841+
rmin = min(rmin, ep->cur_rate);
842+
rmax = max(rmax, ep->cur_rate);
843+
continue;
844+
}
845+
}
846+
808847
r = snd_usb_endpoint_get_clock_rate(chip, fp->clock);
809848
if (r > 0) {
810849
if (!snd_interval_test(it, r))
@@ -874,6 +913,8 @@ static int hw_rule_format(struct snd_pcm_hw_params *params,
874913
struct snd_pcm_hw_rule *rule)
875914
{
876915
struct snd_usb_substream *subs = rule->private;
916+
struct snd_usb_audio *chip = subs->stream->chip;
917+
const struct snd_usb_endpoint *ep;
877918
const struct audioformat *fp;
878919
struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
879920
u64 fbits;
@@ -883,6 +924,27 @@ static int hw_rule_format(struct snd_pcm_hw_params *params,
883924
list_for_each_entry(fp, &subs->fmt_list, list) {
884925
if (!hw_check_valid_format(subs, params, fp))
885926
continue;
927+
928+
ep = get_endpoint_in_use(chip, fp->endpoint,
929+
subs->data_endpoint);
930+
if (ep) {
931+
hwc_debug("format limit %d for ep#%x\n",
932+
ep->cur_format, fp->endpoint);
933+
fbits |= pcm_format_to_bits(ep->cur_format);
934+
continue;
935+
}
936+
937+
if (fp->implicit_fb) {
938+
ep = get_endpoint_in_use(chip, fp->sync_ep,
939+
subs->sync_endpoint);
940+
if (ep) {
941+
hwc_debug("format limit %d for sync_ep#%x\n",
942+
ep->cur_format, fp->sync_ep);
943+
fbits |= pcm_format_to_bits(ep->cur_format);
944+
continue;
945+
}
946+
}
947+
886948
fbits |= fp->formats;
887949
}
888950
return apply_hw_params_format_bits(fmt, fbits);
@@ -915,103 +977,95 @@ static int hw_rule_period_time(struct snd_pcm_hw_params *params,
915977
return apply_hw_params_minmax(it, pmin, UINT_MAX);
916978
}
917979

918-
/* get the EP or the sync EP for implicit fb when it's already set up */
919-
static const struct snd_usb_endpoint *
920-
get_sync_ep_from_substream(struct snd_usb_substream *subs)
980+
/* additional hw constraints for implicit feedback mode */
981+
static int hw_rule_period_size_implicit_fb(struct snd_pcm_hw_params *params,
982+
struct snd_pcm_hw_rule *rule)
921983
{
984+
struct snd_usb_substream *subs = rule->private;
922985
struct snd_usb_audio *chip = subs->stream->chip;
923986
const struct audioformat *fp;
924987
const struct snd_usb_endpoint *ep;
988+
struct snd_interval *it;
989+
unsigned int rmin, rmax;
925990

991+
it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
992+
hwc_debug("hw_rule_period_size: (%u,%u)\n", it->min, it->max);
993+
rmin = UINT_MAX;
994+
rmax = 0;
926995
list_for_each_entry(fp, &subs->fmt_list, list) {
927-
ep = snd_usb_get_endpoint(chip, fp->endpoint);
928-
if (ep && ep->cur_audiofmt) {
929-
/* if EP is already opened solely for this substream,
930-
* we still allow us to change the parameter; otherwise
931-
* this substream has to follow the existing parameter
932-
*/
933-
if (ep->cur_audiofmt != subs->cur_audiofmt || ep->opened > 1)
934-
return ep;
935-
}
936-
if (!fp->implicit_fb)
996+
if (!hw_check_valid_format(subs, params, fp))
997+
continue;
998+
ep = get_endpoint_in_use(chip, fp->endpoint,
999+
subs->data_endpoint);
1000+
if (ep) {
1001+
hwc_debug("period size limit %d for ep#%x\n",
1002+
ep->cur_period_frames, fp->endpoint);
1003+
rmin = min(rmin, ep->cur_period_frames);
1004+
rmax = max(rmax, ep->cur_period_frames);
9371005
continue;
938-
/* for the implicit fb, check the sync ep as well */
939-
ep = snd_usb_get_endpoint(chip, fp->sync_ep);
940-
if (ep && ep->cur_audiofmt) {
941-
/* ditto, if the sync (data) ep is used by others,
942-
* this stream is restricted by the sync ep
943-
*/
944-
if (ep != subs->sync_endpoint || ep->opened > 1)
945-
return ep;
9461006
}
947-
}
948-
return NULL;
949-
}
9501007

951-
/* additional hw constraints for implicit feedback mode */
952-
static int hw_rule_format_implicit_fb(struct snd_pcm_hw_params *params,
953-
struct snd_pcm_hw_rule *rule)
954-
{
955-
struct snd_usb_substream *subs = rule->private;
956-
const struct snd_usb_endpoint *ep;
957-
struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
958-
959-
ep = get_sync_ep_from_substream(subs);
960-
if (!ep)
961-
return 0;
962-
963-
hwc_debug("applying %s\n", __func__);
964-
return apply_hw_params_format_bits(fmt, pcm_format_to_bits(ep->cur_format));
965-
}
966-
967-
static int hw_rule_rate_implicit_fb(struct snd_pcm_hw_params *params,
968-
struct snd_pcm_hw_rule *rule)
969-
{
970-
struct snd_usb_substream *subs = rule->private;
971-
const struct snd_usb_endpoint *ep;
972-
struct snd_interval *it;
973-
974-
ep = get_sync_ep_from_substream(subs);
975-
if (!ep)
976-
return 0;
977-
978-
hwc_debug("applying %s\n", __func__);
979-
it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
980-
return apply_hw_params_minmax(it, ep->cur_rate, ep->cur_rate);
981-
}
982-
983-
static int hw_rule_period_size_implicit_fb(struct snd_pcm_hw_params *params,
984-
struct snd_pcm_hw_rule *rule)
985-
{
986-
struct snd_usb_substream *subs = rule->private;
987-
const struct snd_usb_endpoint *ep;
988-
struct snd_interval *it;
989-
990-
ep = get_sync_ep_from_substream(subs);
991-
if (!ep)
992-
return 0;
1008+
if (fp->implicit_fb) {
1009+
ep = get_endpoint_in_use(chip, fp->sync_ep,
1010+
subs->sync_endpoint);
1011+
if (ep) {
1012+
hwc_debug("period size limit %d for sync_ep#%x\n",
1013+
ep->cur_period_frames, fp->sync_ep);
1014+
rmin = min(rmin, ep->cur_period_frames);
1015+
rmax = max(rmax, ep->cur_period_frames);
1016+
continue;
1017+
}
1018+
}
1019+
}
9931020

994-
hwc_debug("applying %s\n", __func__);
995-
it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
996-
return apply_hw_params_minmax(it, ep->cur_period_frames,
997-
ep->cur_period_frames);
1021+
if (!rmax)
1022+
return 0; /* no limit by implicit fb */
1023+
return apply_hw_params_minmax(it, rmin, rmax);
9981024
}
9991025

10001026
static int hw_rule_periods_implicit_fb(struct snd_pcm_hw_params *params,
10011027
struct snd_pcm_hw_rule *rule)
10021028
{
10031029
struct snd_usb_substream *subs = rule->private;
1030+
struct snd_usb_audio *chip = subs->stream->chip;
1031+
const struct audioformat *fp;
10041032
const struct snd_usb_endpoint *ep;
10051033
struct snd_interval *it;
1034+
unsigned int rmin, rmax;
10061035

1007-
ep = get_sync_ep_from_substream(subs);
1008-
if (!ep)
1009-
return 0;
1010-
1011-
hwc_debug("applying %s\n", __func__);
10121036
it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIODS);
1013-
return apply_hw_params_minmax(it, ep->cur_buffer_periods,
1014-
ep->cur_buffer_periods);
1037+
hwc_debug("hw_rule_periods: (%u,%u)\n", it->min, it->max);
1038+
rmin = UINT_MAX;
1039+
rmax = 0;
1040+
list_for_each_entry(fp, &subs->fmt_list, list) {
1041+
if (!hw_check_valid_format(subs, params, fp))
1042+
continue;
1043+
ep = get_endpoint_in_use(chip, fp->endpoint,
1044+
subs->data_endpoint);
1045+
if (ep) {
1046+
hwc_debug("periods limit %d for ep#%x\n",
1047+
ep->cur_buffer_periods, fp->endpoint);
1048+
rmin = min(rmin, ep->cur_buffer_periods);
1049+
rmax = max(rmax, ep->cur_buffer_periods);
1050+
continue;
1051+
}
1052+
1053+
if (fp->implicit_fb) {
1054+
ep = get_endpoint_in_use(chip, fp->sync_ep,
1055+
subs->sync_endpoint);
1056+
if (ep) {
1057+
hwc_debug("periods limit %d for sync_ep#%x\n",
1058+
ep->cur_buffer_periods, fp->sync_ep);
1059+
rmin = min(rmin, ep->cur_buffer_periods);
1060+
rmax = max(rmax, ep->cur_buffer_periods);
1061+
continue;
1062+
}
1063+
}
1064+
}
1065+
1066+
if (!rmax)
1067+
return 0; /* no limit by implicit fb */
1068+
return apply_hw_params_minmax(it, rmin, rmax);
10151069
}
10161070

10171071
/*
@@ -1120,16 +1174,6 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
11201174
return err;
11211175

11221176
/* additional hw constraints for implicit fb */
1123-
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
1124-
hw_rule_format_implicit_fb, subs,
1125-
SNDRV_PCM_HW_PARAM_FORMAT, -1);
1126-
if (err < 0)
1127-
return err;
1128-
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
1129-
hw_rule_rate_implicit_fb, subs,
1130-
SNDRV_PCM_HW_PARAM_RATE, -1);
1131-
if (err < 0)
1132-
return err;
11331177
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
11341178
hw_rule_period_size_implicit_fb, subs,
11351179
SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);

0 commit comments

Comments
 (0)