Merge remote-tracking branches 'asoc/topic/ts3a227e', 'asoc/topic/ts3a277e' and ...
[firefly-linux-kernel-4.4.55.git] / sound / core / pcm_lib.c
index ec9e7866177feff13db582abc6007fa6f4a40932..446c00bd908bed1931dad01ac0d4920ed8c000de 100644 (file)
@@ -1015,6 +1015,60 @@ int snd_interval_list(struct snd_interval *i, unsigned int count,
 
 EXPORT_SYMBOL(snd_interval_list);
 
+/**
+ * snd_interval_ranges - refine the interval value from the list of ranges
+ * @i: the interval value to refine
+ * @count: the number of elements in the list of ranges
+ * @ranges: the ranges list
+ * @mask: the bit-mask to evaluate
+ *
+ * Refines the interval value from the list of ranges.
+ * When mask is non-zero, only the elements corresponding to bit 1 are
+ * evaluated.
+ *
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
+ */
+int snd_interval_ranges(struct snd_interval *i, unsigned int count,
+                       const struct snd_interval *ranges, unsigned int mask)
+{
+       unsigned int k;
+       struct snd_interval range_union;
+       struct snd_interval range;
+
+       if (!count) {
+               snd_interval_none(i);
+               return -EINVAL;
+       }
+       snd_interval_any(&range_union);
+       range_union.min = UINT_MAX;
+       range_union.max = 0;
+       for (k = 0; k < count; k++) {
+               if (mask && !(mask & (1 << k)))
+                       continue;
+               snd_interval_copy(&range, &ranges[k]);
+               if (snd_interval_refine(&range, i) < 0)
+                       continue;
+               if (snd_interval_empty(&range))
+                       continue;
+
+               if (range.min < range_union.min) {
+                       range_union.min = range.min;
+                       range_union.openmin = 1;
+               }
+               if (range.min == range_union.min && !range.openmin)
+                       range_union.openmin = 0;
+               if (range.max > range_union.max) {
+                       range_union.max = range.max;
+                       range_union.openmax = 1;
+               }
+               if (range.max == range_union.max && !range.openmax)
+                       range_union.openmax = 0;
+       }
+       return snd_interval_refine(i, &range_union);
+}
+EXPORT_SYMBOL(snd_interval_ranges);
+
 static int snd_interval_step(struct snd_interval *i, unsigned int step)
 {
        unsigned int n;
@@ -1221,6 +1275,37 @@ int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime,
 
 EXPORT_SYMBOL(snd_pcm_hw_constraint_list);
 
+static int snd_pcm_hw_rule_ranges(struct snd_pcm_hw_params *params,
+                                 struct snd_pcm_hw_rule *rule)
+{
+       struct snd_pcm_hw_constraint_ranges *r = rule->private;
+       return snd_interval_ranges(hw_param_interval(params, rule->var),
+                                  r->count, r->ranges, r->mask);
+}
+
+
+/**
+ * snd_pcm_hw_constraint_ranges - apply list of range constraints to a parameter
+ * @runtime: PCM runtime instance
+ * @cond: condition bits
+ * @var: hw_params variable to apply the list of range constraints
+ * @r: ranges
+ *
+ * Apply the list of range constraints to an interval parameter.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ */
+int snd_pcm_hw_constraint_ranges(struct snd_pcm_runtime *runtime,
+                                unsigned int cond,
+                                snd_pcm_hw_param_t var,
+                                const struct snd_pcm_hw_constraint_ranges *r)
+{
+       return snd_pcm_hw_rule_add(runtime, cond, var,
+                                  snd_pcm_hw_rule_ranges, (void *)r,
+                                  var, -1);
+}
+EXPORT_SYMBOL(snd_pcm_hw_constraint_ranges);
+
 static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params,
                                   struct snd_pcm_hw_rule *rule)
 {