diff options
author | Peter Rosin <peda@axentia.se> | 2015-01-28 09:16:06 -0500 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2015-01-28 14:27:15 -0500 |
commit | f66f898e953d56779367a6cbc39cbb4808b208c0 (patch) | |
tree | d0a7c9d3d6f9349080d5beca852723cd1574a1f6 | |
parent | ba5295e55dd941425b10924f4f5c7af6eac4a1cb (diff) |
ALSA: pcm: Add snd_interval_ranges() and snd_pcm_hw_constraint_ranges()
Add helper functions to allow drivers to specify several disjoint
ranges for a variable. In particular, there is a codec (PCM512x) that
has a hole in its supported range of rates, due to PLL and divider
restrictions.
This is like snd_pcm_hw_constraint_list(), but for ranges instead of
points.
Signed-off-by: Peter Rosin <peda@axentia.se>
Reviewed-by: Lars-Peter Clausen <lars@metafoo.de>
Reviewed-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r-- | include/sound/pcm.h | 12 | ||||
-rw-r--r-- | sound/core/pcm_lib.c | 85 |
2 files changed, 97 insertions, 0 deletions
diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 1e7f74acc2ec..04fc037e0555 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h | |||
@@ -275,6 +275,12 @@ struct snd_pcm_hw_constraint_list { | |||
275 | unsigned int mask; | 275 | unsigned int mask; |
276 | }; | 276 | }; |
277 | 277 | ||
278 | struct snd_pcm_hw_constraint_ranges { | ||
279 | unsigned int count; | ||
280 | const struct snd_interval *ranges; | ||
281 | unsigned int mask; | ||
282 | }; | ||
283 | |||
278 | struct snd_pcm_hwptr_log; | 284 | struct snd_pcm_hwptr_log; |
279 | 285 | ||
280 | struct snd_pcm_runtime { | 286 | struct snd_pcm_runtime { |
@@ -910,6 +916,8 @@ void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k, | |||
910 | const struct snd_interval *b, struct snd_interval *c); | 916 | const struct snd_interval *b, struct snd_interval *c); |
911 | int snd_interval_list(struct snd_interval *i, unsigned int count, | 917 | int snd_interval_list(struct snd_interval *i, unsigned int count, |
912 | const unsigned int *list, unsigned int mask); | 918 | const unsigned int *list, unsigned int mask); |
919 | int snd_interval_ranges(struct snd_interval *i, unsigned int count, | ||
920 | const struct snd_interval *list, unsigned int mask); | ||
913 | int snd_interval_ratnum(struct snd_interval *i, | 921 | int snd_interval_ratnum(struct snd_interval *i, |
914 | unsigned int rats_count, struct snd_ratnum *rats, | 922 | unsigned int rats_count, struct snd_ratnum *rats, |
915 | unsigned int *nump, unsigned int *denp); | 923 | unsigned int *nump, unsigned int *denp); |
@@ -934,6 +942,10 @@ int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime, | |||
934 | unsigned int cond, | 942 | unsigned int cond, |
935 | snd_pcm_hw_param_t var, | 943 | snd_pcm_hw_param_t var, |
936 | const struct snd_pcm_hw_constraint_list *l); | 944 | const struct snd_pcm_hw_constraint_list *l); |
945 | int snd_pcm_hw_constraint_ranges(struct snd_pcm_runtime *runtime, | ||
946 | unsigned int cond, | ||
947 | snd_pcm_hw_param_t var, | ||
948 | const struct snd_pcm_hw_constraint_ranges *r); | ||
937 | int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime, | 949 | int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime, |
938 | unsigned int cond, | 950 | unsigned int cond, |
939 | snd_pcm_hw_param_t var, | 951 | snd_pcm_hw_param_t var, |
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index ec9e7866177f..446c00bd908b 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c | |||
@@ -1015,6 +1015,60 @@ int snd_interval_list(struct snd_interval *i, unsigned int count, | |||
1015 | 1015 | ||
1016 | EXPORT_SYMBOL(snd_interval_list); | 1016 | EXPORT_SYMBOL(snd_interval_list); |
1017 | 1017 | ||
1018 | /** | ||
1019 | * snd_interval_ranges - refine the interval value from the list of ranges | ||
1020 | * @i: the interval value to refine | ||
1021 | * @count: the number of elements in the list of ranges | ||
1022 | * @ranges: the ranges list | ||
1023 | * @mask: the bit-mask to evaluate | ||
1024 | * | ||
1025 | * Refines the interval value from the list of ranges. | ||
1026 | * When mask is non-zero, only the elements corresponding to bit 1 are | ||
1027 | * evaluated. | ||
1028 | * | ||
1029 | * Return: Positive if the value is changed, zero if it's not changed, or a | ||
1030 | * negative error code. | ||
1031 | */ | ||
1032 | int snd_interval_ranges(struct snd_interval *i, unsigned int count, | ||
1033 | const struct snd_interval *ranges, unsigned int mask) | ||
1034 | { | ||
1035 | unsigned int k; | ||
1036 | struct snd_interval range_union; | ||
1037 | struct snd_interval range; | ||
1038 | |||
1039 | if (!count) { | ||
1040 | snd_interval_none(i); | ||
1041 | return -EINVAL; | ||
1042 | } | ||
1043 | snd_interval_any(&range_union); | ||
1044 | range_union.min = UINT_MAX; | ||
1045 | range_union.max = 0; | ||
1046 | for (k = 0; k < count; k++) { | ||
1047 | if (mask && !(mask & (1 << k))) | ||
1048 | continue; | ||
1049 | snd_interval_copy(&range, &ranges[k]); | ||
1050 | if (snd_interval_refine(&range, i) < 0) | ||
1051 | continue; | ||
1052 | if (snd_interval_empty(&range)) | ||
1053 | continue; | ||
1054 | |||
1055 | if (range.min < range_union.min) { | ||
1056 | range_union.min = range.min; | ||
1057 | range_union.openmin = 1; | ||
1058 | } | ||
1059 | if (range.min == range_union.min && !range.openmin) | ||
1060 | range_union.openmin = 0; | ||
1061 | if (range.max > range_union.max) { | ||
1062 | range_union.max = range.max; | ||
1063 | range_union.openmax = 1; | ||
1064 | } | ||
1065 | if (range.max == range_union.max && !range.openmax) | ||
1066 | range_union.openmax = 0; | ||
1067 | } | ||
1068 | return snd_interval_refine(i, &range_union); | ||
1069 | } | ||
1070 | EXPORT_SYMBOL(snd_interval_ranges); | ||
1071 | |||
1018 | static int snd_interval_step(struct snd_interval *i, unsigned int step) | 1072 | static int snd_interval_step(struct snd_interval *i, unsigned int step) |
1019 | { | 1073 | { |
1020 | unsigned int n; | 1074 | unsigned int n; |
@@ -1221,6 +1275,37 @@ int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime, | |||
1221 | 1275 | ||
1222 | EXPORT_SYMBOL(snd_pcm_hw_constraint_list); | 1276 | EXPORT_SYMBOL(snd_pcm_hw_constraint_list); |
1223 | 1277 | ||
1278 | static int snd_pcm_hw_rule_ranges(struct snd_pcm_hw_params *params, | ||
1279 | struct snd_pcm_hw_rule *rule) | ||
1280 | { | ||
1281 | struct snd_pcm_hw_constraint_ranges *r = rule->private; | ||
1282 | return snd_interval_ranges(hw_param_interval(params, rule->var), | ||
1283 | r->count, r->ranges, r->mask); | ||
1284 | } | ||
1285 | |||
1286 | |||
1287 | /** | ||
1288 | * snd_pcm_hw_constraint_ranges - apply list of range constraints to a parameter | ||
1289 | * @runtime: PCM runtime instance | ||
1290 | * @cond: condition bits | ||
1291 | * @var: hw_params variable to apply the list of range constraints | ||
1292 | * @r: ranges | ||
1293 | * | ||
1294 | * Apply the list of range constraints to an interval parameter. | ||
1295 | * | ||
1296 | * Return: Zero if successful, or a negative error code on failure. | ||
1297 | */ | ||
1298 | int snd_pcm_hw_constraint_ranges(struct snd_pcm_runtime *runtime, | ||
1299 | unsigned int cond, | ||
1300 | snd_pcm_hw_param_t var, | ||
1301 | const struct snd_pcm_hw_constraint_ranges *r) | ||
1302 | { | ||
1303 | return snd_pcm_hw_rule_add(runtime, cond, var, | ||
1304 | snd_pcm_hw_rule_ranges, (void *)r, | ||
1305 | var, -1); | ||
1306 | } | ||
1307 | EXPORT_SYMBOL(snd_pcm_hw_constraint_ranges); | ||
1308 | |||
1224 | static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params, | 1309 | static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params, |
1225 | struct snd_pcm_hw_rule *rule) | 1310 | struct snd_pcm_hw_rule *rule) |
1226 | { | 1311 | { |