Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.17.
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * helpers to map values in a linear range to range index
  4 *
  5 * Original idea borrowed from regulator framework
  6 *
  7 * It might be useful if we could support also inversely proportional ranges?
  8 * Copyright 2020 ROHM Semiconductors
  9 */
 10
 11#include <linux/errno.h>
 12#include <linux/export.h>
 13#include <linux/kernel.h>
 14#include <linux/linear_range.h>
 15#include <linux/module.h>
 16
 17/**
 18 * linear_range_values_in_range - return the amount of values in a range
 19 * @r:		pointer to linear range where values are counted
 20 *
 21 * Compute the amount of values in range pointed by @r. Note, values can
 22 * be all equal - range with selectors 0,...,2 with step 0 still contains
 23 * 3 values even though they are all equal.
 24 *
 25 * Return: the amount of values in range pointed by @r
 26 */
 27unsigned int linear_range_values_in_range(const struct linear_range *r)
 28{
 29	if (!r)
 30		return 0;
 31	return r->max_sel - r->min_sel + 1;
 32}
 33EXPORT_SYMBOL_GPL(linear_range_values_in_range);
 34
 35/**
 36 * linear_range_values_in_range_array - return the amount of values in ranges
 37 * @r:		pointer to array of linear ranges where values are counted
 38 * @ranges:	amount of ranges we include in computation.
 39 *
 40 * Compute the amount of values in ranges pointed by @r. Note, values can
 41 * be all equal - range with selectors 0,...,2 with step 0 still contains
 42 * 3 values even though they are all equal.
 43 *
 44 * Return: the amount of values in first @ranges ranges pointed by @r
 45 */
 46unsigned int linear_range_values_in_range_array(const struct linear_range *r,
 47						int ranges)
 48{
 49	int i, values_in_range = 0;
 50
 51	for (i = 0; i < ranges; i++) {
 52		int values;
 53
 54		values = linear_range_values_in_range(&r[i]);
 55		if (!values)
 56			return values;
 57
 58		values_in_range += values;
 59	}
 60	return values_in_range;
 61}
 62EXPORT_SYMBOL_GPL(linear_range_values_in_range_array);
 63
 64/**
 65 * linear_range_get_max_value - return the largest value in a range
 66 * @r:		pointer to linear range where value is looked from
 67 *
 68 * Return: the largest value in the given range
 69 */
 70unsigned int linear_range_get_max_value(const struct linear_range *r)
 71{
 72	return r->min + (r->max_sel - r->min_sel) * r->step;
 73}
 74EXPORT_SYMBOL_GPL(linear_range_get_max_value);
 75
 76/**
 77 * linear_range_get_value - fetch a value from given range
 78 * @r:		pointer to linear range where value is looked from
 79 * @selector:	selector for which the value is searched
 80 * @val:	address where found value is updated
 81 *
 82 * Search given ranges for value which matches given selector.
 83 *
 84 * Return: 0 on success, -EINVAL given selector is not found from any of the
 85 * ranges.
 86 */
 87int linear_range_get_value(const struct linear_range *r, unsigned int selector,
 88			   unsigned int *val)
 89{
 90	if (r->min_sel > selector || r->max_sel < selector)
 91		return -EINVAL;
 92
 93	*val = r->min + (selector - r->min_sel) * r->step;
 94
 95	return 0;
 96}
 97EXPORT_SYMBOL_GPL(linear_range_get_value);
 98
 99/**
100 * linear_range_get_value_array - fetch a value from array of ranges
101 * @r:		pointer to array of linear ranges where value is looked from
102 * @ranges:	amount of ranges in an array
103 * @selector:	selector for which the value is searched
104 * @val:	address where found value is updated
105 *
106 * Search through an array of ranges for value which matches given selector.
107 *
108 * Return: 0 on success, -EINVAL given selector is not found from any of the
109 * ranges.
110 */
111int linear_range_get_value_array(const struct linear_range *r, int ranges,
112				 unsigned int selector, unsigned int *val)
113{
114	int i;
115
116	for (i = 0; i < ranges; i++)
117		if (r[i].min_sel <= selector && r[i].max_sel >= selector)
118			return linear_range_get_value(&r[i], selector, val);
119
120	return -EINVAL;
121}
122EXPORT_SYMBOL_GPL(linear_range_get_value_array);
123
124/**
125 * linear_range_get_selector_low - return linear range selector for value
126 * @r:		pointer to linear range where selector is looked from
127 * @val:	value for which the selector is searched
128 * @selector:	address where found selector value is updated
129 * @found:	flag to indicate that given value was in the range
130 *
131 * Return selector for which range value is closest match for given
132 * input value. Value is matching if it is equal or smaller than given
133 * value. If given value is in the range, then @found is set true.
134 *
135 * Return: 0 on success, -EINVAL if range is invalid or does not contain
136 * value smaller or equal to given value
137 */
138int linear_range_get_selector_low(const struct linear_range *r,
139				  unsigned int val, unsigned int *selector,
140				  bool *found)
141{
142	*found = false;
143
144	if (r->min > val)
145		return -EINVAL;
146
147	if (linear_range_get_max_value(r) < val) {
148		*selector = r->max_sel;
149		return 0;
150	}
151
152	*found = true;
153
154	if (r->step == 0)
155		*selector = r->min_sel;
156	else
157		*selector = (val - r->min) / r->step + r->min_sel;
158
159	return 0;
160}
161EXPORT_SYMBOL_GPL(linear_range_get_selector_low);
162
163/**
164 * linear_range_get_selector_low_array - return linear range selector for value
165 * @r:		pointer to array of linear ranges where selector is looked from
166 * @ranges:	amount of ranges to scan from array
167 * @val:	value for which the selector is searched
168 * @selector:	address where found selector value is updated
169 * @found:	flag to indicate that given value was in the range
170 *
171 * Scan array of ranges for selector for which range value matches given
172 * input value. Value is matching if it is equal or smaller than given
173 * value. If given value is found to be in a range scanning is stopped and
174 * @found is set true. If a range with values smaller than given value is found
175 * but the range max is being smaller than given value, then the range's
176 * biggest selector is updated to @selector but scanning ranges is continued
177 * and @found is set to false.
178 *
179 * Return: 0 on success, -EINVAL if range array is invalid or does not contain
180 * range with a value smaller or equal to given value
181 */
182int linear_range_get_selector_low_array(const struct linear_range *r,
183					int ranges, unsigned int val,
184					unsigned int *selector, bool *found)
185{
186	int i;
187	int ret = -EINVAL;
188
189	for (i = 0; i < ranges; i++) {
190		int tmpret;
191
192		tmpret = linear_range_get_selector_low(&r[i], val, selector,
193						       found);
194		if (!tmpret)
195			ret = 0;
196
197		if (*found)
198			break;
199	}
200
201	return ret;
202}
203EXPORT_SYMBOL_GPL(linear_range_get_selector_low_array);
204
205/**
206 * linear_range_get_selector_high - return linear range selector for value
207 * @r:		pointer to linear range where selector is looked from
208 * @val:	value for which the selector is searched
209 * @selector:	address where found selector value is updated
210 * @found:	flag to indicate that given value was in the range
211 *
212 * Return selector for which range value is closest match for given
213 * input value. Value is matching if it is equal or higher than given
214 * value. If given value is in the range, then @found is set true.
215 *
216 * Return: 0 on success, -EINVAL if range is invalid or does not contain
217 * value greater or equal to given value
218 */
219int linear_range_get_selector_high(const struct linear_range *r,
220				   unsigned int val, unsigned int *selector,
221				   bool *found)
222{
223	*found = false;
224
225	if (linear_range_get_max_value(r) < val)
226		return -EINVAL;
227
228	if (r->min > val) {
229		*selector = r->min_sel;
230		return 0;
231	}
232
233	*found = true;
234
235	if (r->step == 0)
236		*selector = r->max_sel;
237	else
238		*selector = DIV_ROUND_UP(val - r->min, r->step) + r->min_sel;
239
240	return 0;
241}
242EXPORT_SYMBOL_GPL(linear_range_get_selector_high);
243
244MODULE_DESCRIPTION("linear-ranges helper");
245MODULE_LICENSE("GPL");