1 // SPDX-License-Identifier: GPL-2.0-only
2 /* gain-time-scale conversion helpers for IIO light sensors
4 * Copyright (c) 2023 Matti Vaittinen <mazziesaccount@gmail.com>
7 #include <linux/device.h>
8 #include <linux/errno.h>
9 #include <linux/export.h>
10 #include <linux/minmax.h>
11 #include <linux/module.h>
12 #include <linux/overflow.h>
13 #include <linux/slab.h>
14 #include <linux/sort.h>
15 #include <linux/types.h>
16 #include <linux/units.h>
18 #include <linux/iio/iio-gts-helper.h>
19 #include <linux/iio/types.h>
22 * iio_gts_get_gain - Convert scale to total gain
24 * Internal helper for converting scale to total gain.
26 * @max: Maximum linearized scale. As an example, when scale is created
27 * in magnitude of NANOs and max scale is 64.1 - The linearized
28 * scale is 64 100 000 000.
29 * @scale: Linearized scale to compute the gain for.
31 * Return: (floored) gain corresponding to the scale. -EINVAL if scale
34 static int iio_gts_get_gain(const u64 max, const u64 scale)
39 if (scale > full || !scale)
42 if (U64_MAX - full < scale) {
43 /* Risk of overflow */
44 if (full - scale < scale)
51 while (full > scale * (u64)tmp)
58 * gain_get_scale_fraction - get the gain or time based on scale and known one
60 * @max: Maximum linearized scale. As an example, when scale is created
61 * in magnitude of NANOs and max scale is 64.1 - The linearized
62 * scale is 64 100 000 000.
63 * @scale: Linearized scale to compute the gain/time for.
64 * @known: Either integration time or gain depending on which one is known
65 * @unknown: Pointer to variable where the computed gain/time is stored
67 * Internal helper for computing unknown fraction of total gain.
68 * Compute either gain or time based on scale and either the gain or time
69 * depending on which one is known.
71 * Return: 0 on success.
73 static int gain_get_scale_fraction(const u64 max, u64 scale, int known,
78 tot_gain = iio_gts_get_gain(max, scale);
82 *unknown = tot_gain / known;
84 /* We require total gain to be exact multiple of known * unknown */
85 if (!*unknown || *unknown * known != tot_gain)
91 static int iio_gts_delinearize(u64 lin_scale, unsigned long scaler,
92 int *scale_whole, int *scale_nano)
102 frac = do_div(lin_scale, scaler);
104 *scale_whole = lin_scale;
105 *scale_nano = frac * (NANO / scaler);
110 static int iio_gts_linearize(int scale_whole, int scale_nano,
111 unsigned long scaler, u64 *lin_scale)
114 * Expect scale to be (mostly) NANO or MICRO. Divide divider instead of
115 * multiplication followed by division to avoid overflow.
117 if (scaler > NANO || !scaler)
120 *lin_scale = (u64)scale_whole * (u64)scaler +
121 (u64)(scale_nano / (NANO / scaler));
127 * iio_gts_total_gain_to_scale - convert gain to scale
128 * @gts: Gain time scale descriptor
129 * @total_gain: the gain to be converted
130 * @scale_int: Pointer to integral part of the scale (typically val1)
131 * @scale_nano: Pointer to fractional part of the scale (nano or ppb)
133 * Convert the total gain value to scale. NOTE: This does not separate gain
134 * generated by HW-gain or integration time. It is up to caller to decide what
135 * part of the total gain is due to integration time and what due to HW-gain.
137 * Return: 0 on success. Negative errno on failure.
139 int iio_gts_total_gain_to_scale(struct iio_gts *gts, int total_gain,
140 int *scale_int, int *scale_nano)
144 tmp = gts->max_scale;
146 do_div(tmp, total_gain);
148 return iio_gts_delinearize(tmp, NANO, scale_int, scale_nano);
150 EXPORT_SYMBOL_NS_GPL(iio_gts_total_gain_to_scale, IIO_GTS_HELPER);
153 * iio_gts_purge_avail_scale_table - free-up the available scale tables
154 * @gts: Gain time scale descriptor
156 * Free the space reserved by iio_gts_build_avail_scale_table().
158 static void iio_gts_purge_avail_scale_table(struct iio_gts *gts)
162 if (gts->per_time_avail_scale_tables) {
163 for (i = 0; i < gts->num_itime; i++)
164 kfree(gts->per_time_avail_scale_tables[i]);
166 kfree(gts->per_time_avail_scale_tables);
167 gts->per_time_avail_scale_tables = NULL;
170 kfree(gts->avail_all_scales_table);
171 gts->avail_all_scales_table = NULL;
173 gts->num_avail_all_scales = 0;
176 static int iio_gts_gain_cmp(const void *a, const void *b)
178 return *(int *)a - *(int *)b;
181 static int gain_to_scaletables(struct iio_gts *gts, int **gains, int **scales)
183 int ret, i, j, new_idx, time_idx;
187 for (i = 0; i < gts->num_itime; i++) {
189 * Sort the tables for nice output and for easier finding of
192 sort(gains[i], gts->num_hwgain, sizeof(int), iio_gts_gain_cmp,
195 /* Convert gains to scales */
196 for (j = 0; j < gts->num_hwgain; j++) {
197 ret = iio_gts_total_gain_to_scale(gts, gains[i][j],
199 &scales[i][2 * j + 1]);
205 gain_bytes = array_size(gts->num_hwgain, sizeof(int));
206 all_gains = kcalloc(gts->num_itime, gain_bytes, GFP_KERNEL);
211 * We assume all the gains for same integration time were unique.
212 * It is likely the first time table had greatest time multiplier as
213 * the times are in the order of preference and greater times are
214 * usually preferred. Hence we start from the last table which is likely
215 * to have the smallest total gains.
217 time_idx = gts->num_itime - 1;
218 memcpy(all_gains, gains[time_idx], gain_bytes);
219 new_idx = gts->num_hwgain;
222 for (j = 0; j < gts->num_hwgain; j++) {
223 int candidate = gains[time_idx][j];
226 if (candidate > all_gains[new_idx - 1]) {
227 all_gains[new_idx] = candidate;
232 for (chk = 0; chk < new_idx; chk++)
233 if (candidate <= all_gains[chk])
236 if (candidate == all_gains[chk])
239 memmove(&all_gains[chk + 1], &all_gains[chk],
240 (new_idx - chk) * sizeof(int));
241 all_gains[chk] = candidate;
246 gts->avail_all_scales_table = kcalloc(new_idx, 2 * sizeof(int),
248 if (!gts->avail_all_scales_table) {
252 gts->num_avail_all_scales = new_idx;
254 for (i = 0; i < gts->num_avail_all_scales; i++) {
255 ret = iio_gts_total_gain_to_scale(gts, all_gains[i],
256 >s->avail_all_scales_table[i * 2],
257 >s->avail_all_scales_table[i * 2 + 1]);
260 kfree(gts->avail_all_scales_table);
261 gts->num_avail_all_scales = 0;
273 * iio_gts_build_avail_scale_table - create tables of available scales
274 * @gts: Gain time scale descriptor
276 * Build the tables which can represent the available scales based on the
277 * originally given gain and time tables. When both time and gain tables are
278 * given this results:
279 * 1. A set of tables representing available scales for each supported
281 * 2. A single table listing all the unique scales that any combination of
282 * supported gains and times can provide.
284 * NOTE: Space allocated for the tables must be freed using
285 * iio_gts_purge_avail_scale_table() when the tables are no longer needed.
287 * Return: 0 on success.
289 static int iio_gts_build_avail_scale_table(struct iio_gts *gts)
291 int **per_time_gains, **per_time_scales, i, j, ret = -ENOMEM;
293 per_time_gains = kcalloc(gts->num_itime, sizeof(*per_time_gains), GFP_KERNEL);
297 per_time_scales = kcalloc(gts->num_itime, sizeof(*per_time_scales), GFP_KERNEL);
298 if (!per_time_scales)
301 for (i = 0; i < gts->num_itime; i++) {
302 per_time_scales[i] = kcalloc(gts->num_hwgain, 2 * sizeof(int),
304 if (!per_time_scales[i])
307 per_time_gains[i] = kcalloc(gts->num_hwgain, sizeof(int),
309 if (!per_time_gains[i]) {
310 kfree(per_time_scales[i]);
314 for (j = 0; j < gts->num_hwgain; j++)
315 per_time_gains[i][j] = gts->hwgain_table[j].gain *
316 gts->itime_table[i].mul;
319 ret = gain_to_scaletables(gts, per_time_gains, per_time_scales);
323 kfree(per_time_gains);
324 gts->per_time_avail_scale_tables = per_time_scales;
330 kfree(per_time_scales[i]);
331 kfree(per_time_gains[i]);
333 kfree(per_time_scales);
335 kfree(per_time_gains);
341 * iio_gts_build_avail_time_table - build table of available integration times
342 * @gts: Gain time scale descriptor
344 * Build the table which can represent the available times to be returned
345 * to users using the read_avail-callback.
347 * NOTE: Space allocated for the tables must be freed using
348 * iio_gts_purge_avail_time_table() when the tables are no longer needed.
350 * Return: 0 on success.
352 static int iio_gts_build_avail_time_table(struct iio_gts *gts)
354 int *times, i, j, idx = 0;
359 times = kcalloc(gts->num_itime, sizeof(int), GFP_KERNEL);
363 /* Sort times from all tables to one and remove duplicates */
364 for (i = gts->num_itime - 1; i >= 0; i--) {
365 int new = gts->itime_table[i].time_us;
367 if (times[idx] < new) {
372 for (j = 0; j <= idx; j++) {
373 if (times[j] > new) {
374 memmove(×[j + 1], ×[j],
375 (idx - j) * sizeof(int));
381 gts->avail_time_tables = times;
383 * This is just to survive a unlikely corner-case where times in the
384 * given time table were not unique. Else we could just trust the
387 gts->num_avail_time_tables = idx;
393 * iio_gts_purge_avail_time_table - free-up the available integration time table
394 * @gts: Gain time scale descriptor
396 * Free the space reserved by iio_gts_build_avail_time_table().
398 static void iio_gts_purge_avail_time_table(struct iio_gts *gts)
400 if (gts->num_avail_time_tables) {
401 kfree(gts->avail_time_tables);
402 gts->avail_time_tables = NULL;
403 gts->num_avail_time_tables = 0;
408 * iio_gts_build_avail_tables - create tables of available scales and int times
409 * @gts: Gain time scale descriptor
411 * Build the tables which can represent the available scales and available
412 * integration times. Availability tables are built based on the originally
413 * given gain and given time tables.
415 * When both time and gain tables are
416 * given this results:
417 * 1. A set of sorted tables representing available scales for each supported
419 * 2. A single sorted table listing all the unique scales that any combination
420 * of supported gains and times can provide.
421 * 3. A sorted table of supported integration times
423 * After these tables are built one can use the iio_gts_all_avail_scales(),
424 * iio_gts_avail_scales_for_time() and iio_gts_avail_times() helpers to
425 * implement the read_avail operations.
427 * NOTE: Space allocated for the tables must be freed using
428 * iio_gts_purge_avail_tables() when the tables are no longer needed.
430 * Return: 0 on success.
432 static int iio_gts_build_avail_tables(struct iio_gts *gts)
436 ret = iio_gts_build_avail_scale_table(gts);
440 ret = iio_gts_build_avail_time_table(gts);
442 iio_gts_purge_avail_scale_table(gts);
448 * iio_gts_purge_avail_tables - free-up the availability tables
449 * @gts: Gain time scale descriptor
451 * Free the space reserved by iio_gts_build_avail_tables(). Frees both the
452 * integration time and scale tables.
454 static void iio_gts_purge_avail_tables(struct iio_gts *gts)
456 iio_gts_purge_avail_time_table(gts);
457 iio_gts_purge_avail_scale_table(gts);
460 static void devm_iio_gts_avail_all_drop(void *res)
462 iio_gts_purge_avail_tables(res);
466 * devm_iio_gts_build_avail_tables - manged add availability tables
467 * @dev: Pointer to the device whose lifetime tables are bound
468 * @gts: Gain time scale descriptor
470 * Build the tables which can represent the available scales and available
471 * integration times. Availability tables are built based on the originally
472 * given gain and given time tables.
474 * When both time and gain tables are given this results:
475 * 1. A set of sorted tables representing available scales for each supported
477 * 2. A single sorted table listing all the unique scales that any combination
478 * of supported gains and times can provide.
479 * 3. A sorted table of supported integration times
481 * After these tables are built one can use the iio_gts_all_avail_scales(),
482 * iio_gts_avail_scales_for_time() and iio_gts_avail_times() helpers to
483 * implement the read_avail operations.
485 * The tables are automatically released upon device detach.
487 * Return: 0 on success.
489 static int devm_iio_gts_build_avail_tables(struct device *dev,
494 ret = iio_gts_build_avail_tables(gts);
498 return devm_add_action_or_reset(dev, devm_iio_gts_avail_all_drop, gts);
501 static int sanity_check_time(const struct iio_itime_sel_mul *t)
503 if (t->sel < 0 || t->time_us < 0 || t->mul <= 0)
509 static int sanity_check_gain(const struct iio_gain_sel_pair *g)
511 if (g->sel < 0 || g->gain <= 0)
517 static int iio_gts_sanity_check(struct iio_gts *gts)
521 if (!gts->num_hwgain && !gts->num_itime)
524 for (t = 0; t < gts->num_itime; t++) {
525 ret = sanity_check_time(>s->itime_table[t]);
530 for (g = 0; g < gts->num_hwgain; g++) {
531 ret = sanity_check_gain(>s->hwgain_table[g]);
536 for (g = 0; g < gts->num_hwgain; g++) {
537 for (t = 0; t < gts->num_itime; t++) {
540 gain = gts->hwgain_table[g].gain;
541 mul = gts->itime_table[t].mul;
543 if (check_mul_overflow(gain, mul, &res))
551 static int iio_init_iio_gts(int max_scale_int, int max_scale_nano,
552 const struct iio_gain_sel_pair *gain_tbl, int num_gain,
553 const struct iio_itime_sel_mul *tim_tbl, int num_times,
558 memset(gts, 0, sizeof(*gts));
560 ret = iio_gts_linearize(max_scale_int, max_scale_nano, NANO,
565 gts->hwgain_table = gain_tbl;
566 gts->num_hwgain = num_gain;
567 gts->itime_table = tim_tbl;
568 gts->num_itime = num_times;
570 return iio_gts_sanity_check(gts);
574 * devm_iio_init_iio_gts - Initialize the gain-time-scale helper
575 * @dev: Pointer to the device whose lifetime gts resources are
577 * @max_scale_int: integer part of the maximum scale value
578 * @max_scale_nano: fraction part of the maximum scale value
579 * @gain_tbl: table describing supported gains
580 * @num_gain: number of gains in the gain table
581 * @tim_tbl: table describing supported integration times. Provide
582 * the integration time table sorted so that the preferred
583 * integration time is in the first array index. The search
585 * iio_gts_find_time_and_gain_sel_for_scale() start search
586 * from first provided time.
587 * @num_times: number of times in the time table
588 * @gts: pointer to the helper struct
590 * Initialize the gain-time-scale helper for use. Note, gains, times, selectors
591 * and multipliers must be positive. Negative values are reserved for error
592 * checking. The total gain (maximum gain * maximum time multiplier) must not
593 * overflow int. The allocated resources will be released upon device detach.
595 * Return: 0 on success.
597 int devm_iio_init_iio_gts(struct device *dev, int max_scale_int, int max_scale_nano,
598 const struct iio_gain_sel_pair *gain_tbl, int num_gain,
599 const struct iio_itime_sel_mul *tim_tbl, int num_times,
604 ret = iio_init_iio_gts(max_scale_int, max_scale_nano, gain_tbl,
605 num_gain, tim_tbl, num_times, gts);
609 return devm_iio_gts_build_avail_tables(dev, gts);
611 EXPORT_SYMBOL_NS_GPL(devm_iio_init_iio_gts, IIO_GTS_HELPER);
614 * iio_gts_all_avail_scales - helper for listing all available scales
615 * @gts: Gain time scale descriptor
616 * @vals: Returned array of supported scales
617 * @type: Type of returned scale values
618 * @length: Amount of returned values in array
620 * Return: a value suitable to be returned from read_avail or a negative error.
622 int iio_gts_all_avail_scales(struct iio_gts *gts, const int **vals, int *type,
625 if (!gts->num_avail_all_scales)
628 *vals = gts->avail_all_scales_table;
629 *type = IIO_VAL_INT_PLUS_NANO;
630 *length = gts->num_avail_all_scales * 2;
632 return IIO_AVAIL_LIST;
634 EXPORT_SYMBOL_NS_GPL(iio_gts_all_avail_scales, IIO_GTS_HELPER);
637 * iio_gts_avail_scales_for_time - list scales for integration time
638 * @gts: Gain time scale descriptor
639 * @time: Integration time for which the scales are listed
640 * @vals: Returned array of supported scales
641 * @type: Type of returned scale values
642 * @length: Amount of returned values in array
644 * Drivers which do not allow scale setting to change integration time can
645 * use this helper to list only the scales which are valid for given integration
648 * Return: a value suitable to be returned from read_avail or a negative error.
650 int iio_gts_avail_scales_for_time(struct iio_gts *gts, int time,
651 const int **vals, int *type, int *length)
655 for (i = 0; i < gts->num_itime; i++)
656 if (gts->itime_table[i].time_us == time)
659 if (i == gts->num_itime)
662 *vals = gts->per_time_avail_scale_tables[i];
663 *type = IIO_VAL_INT_PLUS_NANO;
664 *length = gts->num_hwgain * 2;
666 return IIO_AVAIL_LIST;
668 EXPORT_SYMBOL_NS_GPL(iio_gts_avail_scales_for_time, IIO_GTS_HELPER);
671 * iio_gts_avail_times - helper for listing available integration times
672 * @gts: Gain time scale descriptor
673 * @vals: Returned array of supported times
674 * @type: Type of returned scale values
675 * @length: Amount of returned values in array
677 * Return: a value suitable to be returned from read_avail or a negative error.
679 int iio_gts_avail_times(struct iio_gts *gts, const int **vals, int *type,
682 if (!gts->num_avail_time_tables)
685 *vals = gts->avail_time_tables;
687 *length = gts->num_avail_time_tables;
689 return IIO_AVAIL_LIST;
691 EXPORT_SYMBOL_NS_GPL(iio_gts_avail_times, IIO_GTS_HELPER);
694 * iio_gts_find_sel_by_gain - find selector corresponding to a HW-gain
695 * @gts: Gain time scale descriptor
696 * @gain: HW-gain for which matching selector is searched for
698 * Return: a selector matching given HW-gain or -EINVAL if selector was
701 int iio_gts_find_sel_by_gain(struct iio_gts *gts, int gain)
705 for (i = 0; i < gts->num_hwgain; i++)
706 if (gts->hwgain_table[i].gain == gain)
707 return gts->hwgain_table[i].sel;
711 EXPORT_SYMBOL_NS_GPL(iio_gts_find_sel_by_gain, IIO_GTS_HELPER);
714 * iio_gts_find_gain_by_sel - find HW-gain corresponding to a selector
715 * @gts: Gain time scale descriptor
716 * @sel: selector for which matching HW-gain is searched for
718 * Return: a HW-gain matching given selector or -EINVAL if HW-gain was not
721 int iio_gts_find_gain_by_sel(struct iio_gts *gts, int sel)
725 for (i = 0; i < gts->num_hwgain; i++)
726 if (gts->hwgain_table[i].sel == sel)
727 return gts->hwgain_table[i].gain;
731 EXPORT_SYMBOL_NS_GPL(iio_gts_find_gain_by_sel, IIO_GTS_HELPER);
734 * iio_gts_get_min_gain - find smallest valid HW-gain
735 * @gts: Gain time scale descriptor
737 * Return: The smallest HW-gain -EINVAL if no HW-gains were in the tables.
739 int iio_gts_get_min_gain(struct iio_gts *gts)
741 int i, min = -EINVAL;
743 for (i = 0; i < gts->num_hwgain; i++) {
744 int gain = gts->hwgain_table[i].gain;
749 min = min(min, gain);
754 EXPORT_SYMBOL_NS_GPL(iio_gts_get_min_gain, IIO_GTS_HELPER);
757 * iio_find_closest_gain_low - Find the closest lower matching gain
758 * @gts: Gain time scale descriptor
759 * @gain: HW-gain for which the closest match is searched
760 * @in_range: indicate if the @gain was actually in the range of
763 * Search for closest supported gain that is lower than or equal to the
764 * gain given as a parameter. This is usable for drivers which do not require
765 * user to request exact matching gain but rather for rounding to a supported
766 * gain value which is equal or lower (setting lower gain is typical for
767 * avoiding saturation)
769 * Return: The closest matching supported gain or -EINVAL if @gain
770 * was smaller than the smallest supported gain.
772 int iio_find_closest_gain_low(struct iio_gts *gts, int gain, bool *in_range)
779 for (i = 0; i < gts->num_hwgain; i++) {
780 if (gain == gts->hwgain_table[i].gain) {
785 if (gain > gts->hwgain_table[i].gain) {
787 diff = gain - gts->hwgain_table[i].gain;
790 int tmp = gain - gts->hwgain_table[i].gain;
799 * We found valid HW-gain which is greater than
800 * reference. So, unless we return a failure below we
801 * will have found an in-range gain
806 /* The requested gain was smaller than anything we support */
813 return gts->hwgain_table[best].gain;
815 EXPORT_SYMBOL_NS_GPL(iio_find_closest_gain_low, IIO_GTS_HELPER);
817 static int iio_gts_get_int_time_gain_multiplier_by_sel(struct iio_gts *gts,
820 const struct iio_itime_sel_mul *time;
822 time = iio_gts_find_itime_by_sel(gts, sel);
830 * iio_gts_find_gain_for_scale_using_time - Find gain by time and scale
831 * @gts: Gain time scale descriptor
832 * @time_sel: Integration time selector corresponding to the time gain is
834 * @scale_int: Integral part of the scale (typically val1)
835 * @scale_nano: Fractional part of the scale (nano or ppb)
836 * @gain: Pointer to value where gain is stored.
838 * In some cases the light sensors may want to find a gain setting which
839 * corresponds given scale and integration time. Sensors which fill the
840 * gain and time tables may use this helper to retrieve the gain.
842 * Return: 0 on success. -EINVAL if gain matching the parameters is not
845 static int iio_gts_find_gain_for_scale_using_time(struct iio_gts *gts, int time_sel,
846 int scale_int, int scale_nano,
852 ret = iio_gts_linearize(scale_int, scale_nano, NANO, &scale_linear);
856 ret = iio_gts_get_int_time_gain_multiplier_by_sel(gts, time_sel);
862 ret = gain_get_scale_fraction(gts->max_scale, scale_linear, mul, gain);
866 if (!iio_gts_valid_gain(gts, *gain))
873 * iio_gts_find_gain_sel_for_scale_using_time - Fetch gain selector.
874 * @gts: Gain time scale descriptor
875 * @time_sel: Integration time selector corresponding to the time gain is
877 * @scale_int: Integral part of the scale (typically val1)
878 * @scale_nano: Fractional part of the scale (nano or ppb)
879 * @gain_sel: Pointer to value where gain selector is stored.
881 * See iio_gts_find_gain_for_scale_using_time() for more information
883 int iio_gts_find_gain_sel_for_scale_using_time(struct iio_gts *gts, int time_sel,
884 int scale_int, int scale_nano,
889 ret = iio_gts_find_gain_for_scale_using_time(gts, time_sel, scale_int,
894 ret = iio_gts_find_sel_by_gain(gts, gain);
902 EXPORT_SYMBOL_NS_GPL(iio_gts_find_gain_sel_for_scale_using_time, IIO_GTS_HELPER);
904 static int iio_gts_get_total_gain(struct iio_gts *gts, int gain, int time)
906 const struct iio_itime_sel_mul *itime;
908 if (!iio_gts_valid_gain(gts, gain))
914 itime = iio_gts_find_itime_by_time(gts, time);
918 return gain * itime->mul;
921 static int iio_gts_get_scale_linear(struct iio_gts *gts, int gain, int time,
927 total_gain = iio_gts_get_total_gain(gts, gain, time);
931 tmp = gts->max_scale;
933 do_div(tmp, total_gain);
941 * iio_gts_get_scale - get scale based on integration time and HW-gain
942 * @gts: Gain time scale descriptor
943 * @gain: HW-gain for which the scale is computed
944 * @time: Integration time for which the scale is computed
945 * @scale_int: Integral part of the scale (typically val1)
946 * @scale_nano: Fractional part of the scale (nano or ppb)
948 * Compute scale matching the integration time and HW-gain given as parameter.
950 * Return: 0 on success.
952 int iio_gts_get_scale(struct iio_gts *gts, int gain, int time, int *scale_int,
958 ret = iio_gts_get_scale_linear(gts, gain, time, &lin_scale);
962 return iio_gts_delinearize(lin_scale, NANO, scale_int, scale_nano);
964 EXPORT_SYMBOL_NS_GPL(iio_gts_get_scale, IIO_GTS_HELPER);
967 * iio_gts_find_new_gain_sel_by_old_gain_time - compensate for time change
968 * @gts: Gain time scale descriptor
969 * @old_gain: Previously set gain
970 * @old_time_sel: Selector corresponding previously set time
971 * @new_time_sel: Selector corresponding new time to be set
972 * @new_gain: Pointer to value where new gain is to be written
974 * We may want to mitigate the scale change caused by setting a new integration
975 * time (for a light sensor) by also updating the (HW)gain. This helper computes
976 * new gain value to maintain the scale with new integration time.
978 * Return: 0 if an exactly matching supported new gain was found. When a
979 * non-zero value is returned, the @new_gain will be set to a negative or
980 * positive value. The negative value means that no gain could be computed.
981 * Positive value will be the "best possible new gain there could be". There
982 * can be two reasons why finding the "best possible" new gain is not deemed
983 * successful. 1) This new value cannot be supported by the hardware. 2) The new
984 * gain required to maintain the scale would not be an integer. In this case,
985 * the "best possible" new gain will be a floored optimal gain, which may or
986 * may not be supported by the hardware.
988 int iio_gts_find_new_gain_sel_by_old_gain_time(struct iio_gts *gts,
989 int old_gain, int old_time_sel,
990 int new_time_sel, int *new_gain)
992 const struct iio_itime_sel_mul *itime_old, *itime_new;
998 itime_old = iio_gts_find_itime_by_sel(gts, old_time_sel);
1002 itime_new = iio_gts_find_itime_by_sel(gts, new_time_sel);
1006 ret = iio_gts_get_scale_linear(gts, old_gain, itime_old->time_us,
1011 ret = gain_get_scale_fraction(gts->max_scale, scale, itime_new->mul,
1016 if (!iio_gts_valid_gain(gts, *new_gain))
1021 EXPORT_SYMBOL_NS_GPL(iio_gts_find_new_gain_sel_by_old_gain_time, IIO_GTS_HELPER);
1024 * iio_gts_find_new_gain_by_old_gain_time - compensate for time change
1025 * @gts: Gain time scale descriptor
1026 * @old_gain: Previously set gain
1027 * @old_time: Selector corresponding previously set time
1028 * @new_time: Selector corresponding new time to be set
1029 * @new_gain: Pointer to value where new gain is to be written
1031 * We may want to mitigate the scale change caused by setting a new integration
1032 * time (for a light sensor) by also updating the (HW)gain. This helper computes
1033 * new gain value to maintain the scale with new integration time.
1035 * Return: 0 if an exactly matching supported new gain was found. When a
1036 * non-zero value is returned, the @new_gain will be set to a negative or
1037 * positive value. The negative value means that no gain could be computed.
1038 * Positive value will be the "best possible new gain there could be". There
1039 * can be two reasons why finding the "best possible" new gain is not deemed
1040 * successful. 1) This new value cannot be supported by the hardware. 2) The new
1041 * gain required to maintain the scale would not be an integer. In this case,
1042 * the "best possible" new gain will be a floored optimal gain, which may or
1043 * may not be supported by the hardware.
1045 int iio_gts_find_new_gain_by_old_gain_time(struct iio_gts *gts, int old_gain,
1046 int old_time, int new_time,
1049 const struct iio_itime_sel_mul *itime_new;
1055 itime_new = iio_gts_find_itime_by_time(gts, new_time);
1059 ret = iio_gts_get_scale_linear(gts, old_gain, old_time, &scale);
1063 ret = gain_get_scale_fraction(gts->max_scale, scale, itime_new->mul,
1068 if (!iio_gts_valid_gain(gts, *new_gain))
1073 EXPORT_SYMBOL_NS_GPL(iio_gts_find_new_gain_by_old_gain_time, IIO_GTS_HELPER);
1075 MODULE_LICENSE("GPL");
1076 MODULE_AUTHOR("Matti Vaittinen <mazziesaccount@gmail.com>");
1077 MODULE_DESCRIPTION("IIO light sensor gain-time-scale helpers");