usb: typec: mux: fix static inline syntax error
[platform/kernel/linux-starfive.git] / drivers / iio / industrialio-gts-helper.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* gain-time-scale conversion helpers for IIO light sensors
3  *
4  * Copyright (c) 2023 Matti Vaittinen <mazziesaccount@gmail.com>
5  */
6
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>
17
18 #include <linux/iio/iio-gts-helper.h>
19 #include <linux/iio/types.h>
20
21 /**
22  * iio_gts_get_gain - Convert scale to total gain
23  *
24  * Internal helper for converting scale to total gain.
25  *
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.
30  *
31  * Return:      (floored) gain corresponding to the scale. -EINVAL if scale
32  *              is invalid.
33  */
34 static int iio_gts_get_gain(const u64 max, const u64 scale)
35 {
36         u64 full = max;
37         int tmp = 1;
38
39         if (scale > full || !scale)
40                 return -EINVAL;
41
42         if (U64_MAX - full < scale) {
43                 /* Risk of overflow */
44                 if (full - scale < scale)
45                         return 1;
46
47                 full -= scale;
48                 tmp++;
49         }
50
51         while (full > scale * (u64)tmp)
52                 tmp++;
53
54         return tmp;
55 }
56
57 /**
58  * gain_get_scale_fraction - get the gain or time based on scale and known one
59  *
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
66  *
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.
70  *
71  * Return:      0 on success.
72  */
73 static int gain_get_scale_fraction(const u64 max, u64 scale, int known,
74                                    int *unknown)
75 {
76         int tot_gain;
77
78         tot_gain = iio_gts_get_gain(max, scale);
79         if (tot_gain < 0)
80                 return tot_gain;
81
82         *unknown = tot_gain / known;
83
84         /* We require total gain to be exact multiple of known * unknown */
85         if (!*unknown || *unknown * known != tot_gain)
86                 return -EINVAL;
87
88         return 0;
89 }
90
91 static int iio_gts_delinearize(u64 lin_scale, unsigned long scaler,
92                                int *scale_whole, int *scale_nano)
93 {
94         int frac;
95
96         if (scaler > NANO)
97                 return -EOVERFLOW;
98
99         if (!scaler)
100                 return -EINVAL;
101
102         frac = do_div(lin_scale, scaler);
103
104         *scale_whole = lin_scale;
105         *scale_nano = frac * (NANO / scaler);
106
107         return 0;
108 }
109
110 static int iio_gts_linearize(int scale_whole, int scale_nano,
111                              unsigned long scaler, u64 *lin_scale)
112 {
113         /*
114          * Expect scale to be (mostly) NANO or MICRO. Divide divider instead of
115          * multiplication followed by division to avoid overflow.
116          */
117         if (scaler > NANO || !scaler)
118                 return -EINVAL;
119
120         *lin_scale = (u64)scale_whole * (u64)scaler +
121                      (u64)(scale_nano / (NANO / scaler));
122
123         return 0;
124 }
125
126 /**
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)
132  *
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.
136  *
137  * Return: 0 on success. Negative errno on failure.
138  */
139 int iio_gts_total_gain_to_scale(struct iio_gts *gts, int total_gain,
140                                 int *scale_int, int *scale_nano)
141 {
142         u64 tmp;
143
144         tmp = gts->max_scale;
145
146         do_div(tmp, total_gain);
147
148         return iio_gts_delinearize(tmp, NANO, scale_int, scale_nano);
149 }
150 EXPORT_SYMBOL_NS_GPL(iio_gts_total_gain_to_scale, IIO_GTS_HELPER);
151
152 /**
153  * iio_gts_purge_avail_scale_table - free-up the available scale tables
154  * @gts:        Gain time scale descriptor
155  *
156  * Free the space reserved by iio_gts_build_avail_scale_table().
157  */
158 static void iio_gts_purge_avail_scale_table(struct iio_gts *gts)
159 {
160         int i;
161
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]);
165
166                 kfree(gts->per_time_avail_scale_tables);
167                 gts->per_time_avail_scale_tables = NULL;
168         }
169
170         kfree(gts->avail_all_scales_table);
171         gts->avail_all_scales_table = NULL;
172
173         gts->num_avail_all_scales = 0;
174 }
175
176 static int iio_gts_gain_cmp(const void *a, const void *b)
177 {
178         return *(int *)a - *(int *)b;
179 }
180
181 static int gain_to_scaletables(struct iio_gts *gts, int **gains, int **scales)
182 {
183         int ret, i, j, new_idx, time_idx;
184         int *all_gains;
185         size_t gain_bytes;
186
187         for (i = 0; i < gts->num_itime; i++) {
188                 /*
189                  * Sort the tables for nice output and for easier finding of
190                  * unique values.
191                  */
192                 sort(gains[i], gts->num_hwgain, sizeof(int), iio_gts_gain_cmp,
193                      NULL);
194
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],
198                                                           &scales[i][2 * j],
199                                                           &scales[i][2 * j + 1]);
200                         if (ret)
201                                 return ret;
202                 }
203         }
204
205         gain_bytes = array_size(gts->num_hwgain, sizeof(int));
206         all_gains = kcalloc(gts->num_itime, gain_bytes, GFP_KERNEL);
207         if (!all_gains)
208                 return -ENOMEM;
209
210         /*
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.
216          */
217         time_idx = gts->num_itime - 1;
218         memcpy(all_gains, gains[time_idx], gain_bytes);
219         new_idx = gts->num_hwgain;
220
221         while (time_idx--) {
222                 for (j = 0; j < gts->num_hwgain; j++) {
223                         int candidate = gains[time_idx][j];
224                         int chk;
225
226                         if (candidate > all_gains[new_idx - 1]) {
227                                 all_gains[new_idx] = candidate;
228                                 new_idx++;
229
230                                 continue;
231                         }
232                         for (chk = 0; chk < new_idx; chk++)
233                                 if (candidate <= all_gains[chk])
234                                         break;
235
236                         if (candidate == all_gains[chk])
237                                 continue;
238
239                         memmove(&all_gains[chk + 1], &all_gains[chk],
240                                 (new_idx - chk) * sizeof(int));
241                         all_gains[chk] = candidate;
242                         new_idx++;
243                 }
244         }
245
246         gts->avail_all_scales_table = kcalloc(new_idx, 2 * sizeof(int),
247                                               GFP_KERNEL);
248         if (!gts->avail_all_scales_table) {
249                 ret = -ENOMEM;
250                 goto free_out;
251         }
252         gts->num_avail_all_scales = new_idx;
253
254         for (i = 0; i < gts->num_avail_all_scales; i++) {
255                 ret = iio_gts_total_gain_to_scale(gts, all_gains[i],
256                                         &gts->avail_all_scales_table[i * 2],
257                                         &gts->avail_all_scales_table[i * 2 + 1]);
258
259                 if (ret) {
260                         kfree(gts->avail_all_scales_table);
261                         gts->num_avail_all_scales = 0;
262                         goto free_out;
263                 }
264         }
265
266 free_out:
267         kfree(all_gains);
268
269         return ret;
270 }
271
272 /**
273  * iio_gts_build_avail_scale_table - create tables of available scales
274  * @gts:        Gain time scale descriptor
275  *
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
280  *    integration time.
281  * 2. A single table listing all the unique scales that any combination of
282  *    supported gains and times can provide.
283  *
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.
286  *
287  * Return: 0 on success.
288  */
289 static int iio_gts_build_avail_scale_table(struct iio_gts *gts)
290 {
291         int **per_time_gains, **per_time_scales, i, j, ret = -ENOMEM;
292
293         per_time_gains = kcalloc(gts->num_itime, sizeof(*per_time_gains), GFP_KERNEL);
294         if (!per_time_gains)
295                 return ret;
296
297         per_time_scales = kcalloc(gts->num_itime, sizeof(*per_time_scales), GFP_KERNEL);
298         if (!per_time_scales)
299                 goto free_gains;
300
301         for (i = 0; i < gts->num_itime; i++) {
302                 per_time_scales[i] = kcalloc(gts->num_hwgain, 2 * sizeof(int),
303                                              GFP_KERNEL);
304                 if (!per_time_scales[i])
305                         goto err_free_out;
306
307                 per_time_gains[i] = kcalloc(gts->num_hwgain, sizeof(int),
308                                             GFP_KERNEL);
309                 if (!per_time_gains[i]) {
310                         kfree(per_time_scales[i]);
311                         goto err_free_out;
312                 }
313
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;
317         }
318
319         ret = gain_to_scaletables(gts, per_time_gains, per_time_scales);
320         if (ret)
321                 goto err_free_out;
322
323         kfree(per_time_gains);
324         gts->per_time_avail_scale_tables = per_time_scales;
325
326         return 0;
327
328 err_free_out:
329         for (i--; i; i--) {
330                 kfree(per_time_scales[i]);
331                 kfree(per_time_gains[i]);
332         }
333         kfree(per_time_scales);
334 free_gains:
335         kfree(per_time_gains);
336
337         return ret;
338 }
339
340 /**
341  * iio_gts_build_avail_time_table - build table of available integration times
342  * @gts:        Gain time scale descriptor
343  *
344  * Build the table which can represent the available times to be returned
345  * to users using the read_avail-callback.
346  *
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.
349  *
350  * Return: 0 on success.
351  */
352 static int iio_gts_build_avail_time_table(struct iio_gts *gts)
353 {
354         int *times, i, j, idx = 0;
355
356         if (!gts->num_itime)
357                 return 0;
358
359         times = kcalloc(gts->num_itime, sizeof(int), GFP_KERNEL);
360         if (!times)
361                 return -ENOMEM;
362
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;
366
367                 if (times[idx] < new) {
368                         times[idx++] = new;
369                         continue;
370                 }
371
372                 for (j = 0; j <= idx; j++) {
373                         if (times[j] > new) {
374                                 memmove(&times[j + 1], &times[j],
375                                         (idx - j) * sizeof(int));
376                                 times[j] = new;
377                                 idx++;
378                         }
379                 }
380         }
381         gts->avail_time_tables = times;
382         /*
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
385          * gts->num_itime.
386          */
387         gts->num_avail_time_tables = idx;
388
389         return 0;
390 }
391
392 /**
393  * iio_gts_purge_avail_time_table - free-up the available integration time table
394  * @gts:        Gain time scale descriptor
395  *
396  * Free the space reserved by iio_gts_build_avail_time_table().
397  */
398 static void iio_gts_purge_avail_time_table(struct iio_gts *gts)
399 {
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;
404         }
405 }
406
407 /**
408  * iio_gts_build_avail_tables - create tables of available scales and int times
409  * @gts:        Gain time scale descriptor
410  *
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.
414  *
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
418  *    integration time.
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
422  *
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.
426  *
427  * NOTE: Space allocated for the tables must be freed using
428  * iio_gts_purge_avail_tables() when the tables are no longer needed.
429  *
430  * Return: 0 on success.
431  */
432 static int iio_gts_build_avail_tables(struct iio_gts *gts)
433 {
434         int ret;
435
436         ret = iio_gts_build_avail_scale_table(gts);
437         if (ret)
438                 return ret;
439
440         ret = iio_gts_build_avail_time_table(gts);
441         if (ret)
442                 iio_gts_purge_avail_scale_table(gts);
443
444         return ret;
445 }
446
447 /**
448  * iio_gts_purge_avail_tables - free-up the availability tables
449  * @gts:        Gain time scale descriptor
450  *
451  * Free the space reserved by iio_gts_build_avail_tables(). Frees both the
452  * integration time and scale tables.
453  */
454 static void iio_gts_purge_avail_tables(struct iio_gts *gts)
455 {
456         iio_gts_purge_avail_time_table(gts);
457         iio_gts_purge_avail_scale_table(gts);
458 }
459
460 static void devm_iio_gts_avail_all_drop(void *res)
461 {
462         iio_gts_purge_avail_tables(res);
463 }
464
465 /**
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
469  *
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.
473  *
474  * When both time and gain tables are given this results:
475  * 1. A set of sorted tables representing available scales for each supported
476  *    integration time.
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
480  *
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.
484  *
485  * The tables are automatically released upon device detach.
486  *
487  * Return: 0 on success.
488  */
489 static int devm_iio_gts_build_avail_tables(struct device *dev,
490                                            struct iio_gts *gts)
491 {
492         int ret;
493
494         ret = iio_gts_build_avail_tables(gts);
495         if (ret)
496                 return ret;
497
498         return devm_add_action_or_reset(dev, devm_iio_gts_avail_all_drop, gts);
499 }
500
501 static int sanity_check_time(const struct iio_itime_sel_mul *t)
502 {
503         if (t->sel < 0 || t->time_us < 0 || t->mul <= 0)
504                 return -EINVAL;
505
506         return 0;
507 }
508
509 static int sanity_check_gain(const struct iio_gain_sel_pair *g)
510 {
511         if (g->sel < 0 || g->gain <= 0)
512                 return -EINVAL;
513
514         return 0;
515 }
516
517 static int iio_gts_sanity_check(struct iio_gts *gts)
518 {
519         int g, t, ret;
520
521         if (!gts->num_hwgain && !gts->num_itime)
522                 return -EINVAL;
523
524         for (t = 0; t < gts->num_itime; t++) {
525                 ret = sanity_check_time(&gts->itime_table[t]);
526                 if (ret)
527                         return ret;
528         }
529
530         for (g = 0; g < gts->num_hwgain; g++) {
531                 ret = sanity_check_gain(&gts->hwgain_table[g]);
532                 if (ret)
533                         return ret;
534         }
535
536         for (g = 0; g < gts->num_hwgain; g++) {
537                 for (t = 0; t < gts->num_itime; t++) {
538                         int gain, mul, res;
539
540                         gain = gts->hwgain_table[g].gain;
541                         mul = gts->itime_table[t].mul;
542
543                         if (check_mul_overflow(gain, mul, &res))
544                                 return -EOVERFLOW;
545                 }
546         }
547
548         return 0;
549 }
550
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,
554                         struct iio_gts *gts)
555 {
556         int ret;
557
558         memset(gts, 0, sizeof(*gts));
559
560         ret = iio_gts_linearize(max_scale_int, max_scale_nano, NANO,
561                                    &gts->max_scale);
562         if (ret)
563                 return ret;
564
565         gts->hwgain_table = gain_tbl;
566         gts->num_hwgain = num_gain;
567         gts->itime_table = tim_tbl;
568         gts->num_itime = num_times;
569
570         return iio_gts_sanity_check(gts);
571 }
572
573 /**
574  * devm_iio_init_iio_gts - Initialize the gain-time-scale helper
575  * @dev:                Pointer to the device whose lifetime gts resources are
576  *                      bound
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
584  *                      functions like the
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
589  *
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.
594  *
595  * Return: 0 on success.
596  */
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,
600                           struct iio_gts *gts)
601 {
602         int ret;
603
604         ret = iio_init_iio_gts(max_scale_int, max_scale_nano, gain_tbl,
605                                num_gain, tim_tbl, num_times, gts);
606         if (ret)
607                 return ret;
608
609         return devm_iio_gts_build_avail_tables(dev, gts);
610 }
611 EXPORT_SYMBOL_NS_GPL(devm_iio_init_iio_gts, IIO_GTS_HELPER);
612
613 /**
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
619  *
620  * Return: a value suitable to be returned from read_avail or a negative error.
621  */
622 int iio_gts_all_avail_scales(struct iio_gts *gts, const int **vals, int *type,
623                              int *length)
624 {
625         if (!gts->num_avail_all_scales)
626                 return -EINVAL;
627
628         *vals = gts->avail_all_scales_table;
629         *type = IIO_VAL_INT_PLUS_NANO;
630         *length = gts->num_avail_all_scales * 2;
631
632         return IIO_AVAIL_LIST;
633 }
634 EXPORT_SYMBOL_NS_GPL(iio_gts_all_avail_scales, IIO_GTS_HELPER);
635
636 /**
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
643  *
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
646  * time.
647  *
648  * Return: a value suitable to be returned from read_avail or a negative error.
649  */
650 int iio_gts_avail_scales_for_time(struct iio_gts *gts, int time,
651                                   const int **vals, int *type, int *length)
652 {
653         int i;
654
655         for (i = 0; i < gts->num_itime; i++)
656                 if (gts->itime_table[i].time_us == time)
657                         break;
658
659         if (i == gts->num_itime)
660                 return -EINVAL;
661
662         *vals = gts->per_time_avail_scale_tables[i];
663         *type = IIO_VAL_INT_PLUS_NANO;
664         *length = gts->num_hwgain * 2;
665
666         return IIO_AVAIL_LIST;
667 }
668 EXPORT_SYMBOL_NS_GPL(iio_gts_avail_scales_for_time, IIO_GTS_HELPER);
669
670 /**
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
676  *
677  * Return: a value suitable to be returned from read_avail or a negative error.
678  */
679 int iio_gts_avail_times(struct iio_gts *gts,  const int **vals, int *type,
680                         int *length)
681 {
682         if (!gts->num_avail_time_tables)
683                 return -EINVAL;
684
685         *vals = gts->avail_time_tables;
686         *type = IIO_VAL_INT;
687         *length = gts->num_avail_time_tables;
688
689         return IIO_AVAIL_LIST;
690 }
691 EXPORT_SYMBOL_NS_GPL(iio_gts_avail_times, IIO_GTS_HELPER);
692
693 /**
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
697  *
698  * Return:      a selector matching given HW-gain or -EINVAL if selector was
699  *              not found.
700  */
701 int iio_gts_find_sel_by_gain(struct iio_gts *gts, int gain)
702 {
703         int i;
704
705         for (i = 0; i < gts->num_hwgain; i++)
706                 if (gts->hwgain_table[i].gain == gain)
707                         return gts->hwgain_table[i].sel;
708
709         return -EINVAL;
710 }
711 EXPORT_SYMBOL_NS_GPL(iio_gts_find_sel_by_gain, IIO_GTS_HELPER);
712
713 /**
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
717  *
718  * Return:      a HW-gain matching given selector or -EINVAL if HW-gain was not
719  *              found.
720  */
721 int iio_gts_find_gain_by_sel(struct iio_gts *gts, int sel)
722 {
723         int i;
724
725         for (i = 0; i < gts->num_hwgain; i++)
726                 if (gts->hwgain_table[i].sel == sel)
727                         return gts->hwgain_table[i].gain;
728
729         return -EINVAL;
730 }
731 EXPORT_SYMBOL_NS_GPL(iio_gts_find_gain_by_sel, IIO_GTS_HELPER);
732
733 /**
734  * iio_gts_get_min_gain - find smallest valid HW-gain
735  * @gts:        Gain time scale descriptor
736  *
737  * Return:      The smallest HW-gain -EINVAL if no HW-gains were in the tables.
738  */
739 int iio_gts_get_min_gain(struct iio_gts *gts)
740 {
741         int i, min = -EINVAL;
742
743         for (i = 0; i < gts->num_hwgain; i++) {
744                 int gain = gts->hwgain_table[i].gain;
745
746                 if (min == -EINVAL)
747                         min = gain;
748                 else
749                         min = min(min, gain);
750         }
751
752         return min;
753 }
754 EXPORT_SYMBOL_NS_GPL(iio_gts_get_min_gain, IIO_GTS_HELPER);
755
756 /**
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
761  *              supported gains.
762  *
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)
768  *
769  * Return:      The closest matching supported gain or -EINVAL if @gain
770  *              was smaller than the smallest supported gain.
771  */
772 int iio_find_closest_gain_low(struct iio_gts *gts, int gain, bool *in_range)
773 {
774         int i, diff = 0;
775         int best = -1;
776
777         *in_range = false;
778
779         for (i = 0; i < gts->num_hwgain; i++) {
780                 if (gain == gts->hwgain_table[i].gain) {
781                         *in_range = true;
782                         return gain;
783                 }
784
785                 if (gain > gts->hwgain_table[i].gain) {
786                         if (!diff) {
787                                 diff = gain - gts->hwgain_table[i].gain;
788                                 best = i;
789                         } else {
790                                 int tmp = gain - gts->hwgain_table[i].gain;
791
792                                 if (tmp < diff) {
793                                         diff = tmp;
794                                         best = i;
795                                 }
796                         }
797                 } else {
798                         /*
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
802                          */
803                         *in_range = true;
804                 }
805         }
806         /* The requested gain was smaller than anything we support */
807         if (!diff) {
808                 *in_range = false;
809
810                 return -EINVAL;
811         }
812
813         return gts->hwgain_table[best].gain;
814 }
815 EXPORT_SYMBOL_NS_GPL(iio_find_closest_gain_low, IIO_GTS_HELPER);
816
817 static int iio_gts_get_int_time_gain_multiplier_by_sel(struct iio_gts *gts,
818                                                        int sel)
819 {
820         const struct iio_itime_sel_mul *time;
821
822         time = iio_gts_find_itime_by_sel(gts, sel);
823         if (!time)
824                 return -EINVAL;
825
826         return time->mul;
827 }
828
829 /**
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
833  *              searched for
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.
837  *
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.
841  *
842  * Return:      0 on success. -EINVAL if gain matching the parameters is not
843  *              found.
844  */
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,
847                                                   int *gain)
848 {
849         u64 scale_linear;
850         int ret, mul;
851
852         ret = iio_gts_linearize(scale_int, scale_nano, NANO, &scale_linear);
853         if (ret)
854                 return ret;
855
856         ret = iio_gts_get_int_time_gain_multiplier_by_sel(gts, time_sel);
857         if (ret < 0)
858                 return ret;
859
860         mul = ret;
861
862         ret = gain_get_scale_fraction(gts->max_scale, scale_linear, mul, gain);
863         if (ret)
864                 return ret;
865
866         if (!iio_gts_valid_gain(gts, *gain))
867                 return -EINVAL;
868
869         return 0;
870 }
871
872 /**
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
876  *              searched for
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.
880  *
881  * See iio_gts_find_gain_for_scale_using_time() for more information
882  */
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,
885                                                int *gain_sel)
886 {
887         int gain, ret;
888
889         ret = iio_gts_find_gain_for_scale_using_time(gts, time_sel, scale_int,
890                                                      scale_nano, &gain);
891         if (ret)
892                 return ret;
893
894         ret = iio_gts_find_sel_by_gain(gts, gain);
895         if (ret < 0)
896                 return ret;
897
898         *gain_sel = ret;
899
900         return 0;
901 }
902 EXPORT_SYMBOL_NS_GPL(iio_gts_find_gain_sel_for_scale_using_time, IIO_GTS_HELPER);
903
904 static int iio_gts_get_total_gain(struct iio_gts *gts, int gain, int time)
905 {
906         const struct iio_itime_sel_mul *itime;
907
908         if (!iio_gts_valid_gain(gts, gain))
909                 return -EINVAL;
910
911         if (!gts->num_itime)
912                 return gain;
913
914         itime = iio_gts_find_itime_by_time(gts, time);
915         if (!itime)
916                 return -EINVAL;
917
918         return gain * itime->mul;
919 }
920
921 static int iio_gts_get_scale_linear(struct iio_gts *gts, int gain, int time,
922                                     u64 *scale)
923 {
924         int total_gain;
925         u64 tmp;
926
927         total_gain = iio_gts_get_total_gain(gts, gain, time);
928         if (total_gain < 0)
929                 return total_gain;
930
931         tmp = gts->max_scale;
932
933         do_div(tmp, total_gain);
934
935         *scale = tmp;
936
937         return 0;
938 }
939
940 /**
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)
947  *
948  * Compute scale matching the integration time and HW-gain given as parameter.
949  *
950  * Return: 0 on success.
951  */
952 int iio_gts_get_scale(struct iio_gts *gts, int gain, int time, int *scale_int,
953                       int *scale_nano)
954 {
955         u64 lin_scale;
956         int ret;
957
958         ret = iio_gts_get_scale_linear(gts, gain, time, &lin_scale);
959         if (ret)
960                 return ret;
961
962         return iio_gts_delinearize(lin_scale, NANO, scale_int, scale_nano);
963 }
964 EXPORT_SYMBOL_NS_GPL(iio_gts_get_scale, IIO_GTS_HELPER);
965
966 /**
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
973  *
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.
977  *
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.
987  */
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)
991 {
992         const struct iio_itime_sel_mul *itime_old, *itime_new;
993         u64 scale;
994         int ret;
995
996         *new_gain = -1;
997
998         itime_old = iio_gts_find_itime_by_sel(gts, old_time_sel);
999         if (!itime_old)
1000                 return -EINVAL;
1001
1002         itime_new = iio_gts_find_itime_by_sel(gts, new_time_sel);
1003         if (!itime_new)
1004                 return -EINVAL;
1005
1006         ret = iio_gts_get_scale_linear(gts, old_gain, itime_old->time_us,
1007                                        &scale);
1008         if (ret)
1009                 return ret;
1010
1011         ret = gain_get_scale_fraction(gts->max_scale, scale, itime_new->mul,
1012                                       new_gain);
1013         if (ret)
1014                 return ret;
1015
1016         if (!iio_gts_valid_gain(gts, *new_gain))
1017                 return -EINVAL;
1018
1019         return 0;
1020 }
1021 EXPORT_SYMBOL_NS_GPL(iio_gts_find_new_gain_sel_by_old_gain_time, IIO_GTS_HELPER);
1022
1023 /**
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
1030  *
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.
1034  *
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.
1044  */
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,
1047                                            int *new_gain)
1048 {
1049         const struct iio_itime_sel_mul *itime_new;
1050         u64 scale;
1051         int ret;
1052
1053         *new_gain = -1;
1054
1055         itime_new = iio_gts_find_itime_by_time(gts, new_time);
1056         if (!itime_new)
1057                 return -EINVAL;
1058
1059         ret = iio_gts_get_scale_linear(gts, old_gain, old_time, &scale);
1060         if (ret)
1061                 return ret;
1062
1063         ret = gain_get_scale_fraction(gts->max_scale, scale, itime_new->mul,
1064                                       new_gain);
1065         if (ret)
1066                 return ret;
1067
1068         if (!iio_gts_valid_gain(gts, *new_gain))
1069                 return -EINVAL;
1070
1071         return 0;
1072 }
1073 EXPORT_SYMBOL_NS_GPL(iio_gts_find_new_gain_by_old_gain_time, IIO_GTS_HELPER);
1074
1075 MODULE_LICENSE("GPL");
1076 MODULE_AUTHOR("Matti Vaittinen <mazziesaccount@gmail.com>");
1077 MODULE_DESCRIPTION("IIO light sensor gain-time-scale helpers");