xref: /linux/drivers/iio/industrialio-gts-helper.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
138416c28SMatti Vaittinen // SPDX-License-Identifier: GPL-2.0-only
238416c28SMatti Vaittinen /* gain-time-scale conversion helpers for IIO light sensors
338416c28SMatti Vaittinen  *
438416c28SMatti Vaittinen  * Copyright (c) 2023 Matti Vaittinen <mazziesaccount@gmail.com>
538416c28SMatti Vaittinen  */
638416c28SMatti Vaittinen 
738416c28SMatti Vaittinen #include <linux/device.h>
838416c28SMatti Vaittinen #include <linux/errno.h>
938416c28SMatti Vaittinen #include <linux/export.h>
1038416c28SMatti Vaittinen #include <linux/minmax.h>
1138416c28SMatti Vaittinen #include <linux/module.h>
1238416c28SMatti Vaittinen #include <linux/overflow.h>
1338416c28SMatti Vaittinen #include <linux/slab.h>
1438416c28SMatti Vaittinen #include <linux/sort.h>
1538416c28SMatti Vaittinen #include <linux/types.h>
1638416c28SMatti Vaittinen #include <linux/units.h>
1738416c28SMatti Vaittinen 
1838416c28SMatti Vaittinen #include <linux/iio/iio-gts-helper.h>
1938416c28SMatti Vaittinen #include <linux/iio/types.h>
2038416c28SMatti Vaittinen 
2138416c28SMatti Vaittinen /**
2238416c28SMatti Vaittinen  * iio_gts_get_gain - Convert scale to total gain
2338416c28SMatti Vaittinen  *
2438416c28SMatti Vaittinen  * Internal helper for converting scale to total gain.
2538416c28SMatti Vaittinen  *
2638416c28SMatti Vaittinen  * @max:	Maximum linearized scale. As an example, when scale is created
2738416c28SMatti Vaittinen  *		in magnitude of NANOs and max scale is 64.1 - The linearized
2838416c28SMatti Vaittinen  *		scale is 64 100 000 000.
2938416c28SMatti Vaittinen  * @scale:	Linearized scale to compute the gain for.
3038416c28SMatti Vaittinen  *
3138416c28SMatti Vaittinen  * Return:	(floored) gain corresponding to the scale. -EINVAL if scale
3238416c28SMatti Vaittinen  *		is invalid.
3338416c28SMatti Vaittinen  */
iio_gts_get_gain(const u64 max,const u64 scale)3438416c28SMatti Vaittinen static int iio_gts_get_gain(const u64 max, const u64 scale)
3538416c28SMatti Vaittinen {
3638416c28SMatti Vaittinen 	u64 full = max;
3738416c28SMatti Vaittinen 
3838416c28SMatti Vaittinen 	if (scale > full || !scale)
3938416c28SMatti Vaittinen 		return -EINVAL;
4038416c28SMatti Vaittinen 
41bb76cc45SMatti Vaittinen 	return div64_u64(full, scale);
4238416c28SMatti Vaittinen }
4338416c28SMatti Vaittinen 
4438416c28SMatti Vaittinen /**
4538416c28SMatti Vaittinen  * gain_get_scale_fraction - get the gain or time based on scale and known one
4638416c28SMatti Vaittinen  *
4738416c28SMatti Vaittinen  * @max:	Maximum linearized scale. As an example, when scale is created
4838416c28SMatti Vaittinen  *		in magnitude of NANOs and max scale is 64.1 - The linearized
4938416c28SMatti Vaittinen  *		scale is 64 100 000 000.
5038416c28SMatti Vaittinen  * @scale:	Linearized scale to compute the gain/time for.
5138416c28SMatti Vaittinen  * @known:	Either integration time or gain depending on which one is known
5238416c28SMatti Vaittinen  * @unknown:	Pointer to variable where the computed gain/time is stored
5338416c28SMatti Vaittinen  *
5438416c28SMatti Vaittinen  * Internal helper for computing unknown fraction of total gain.
5538416c28SMatti Vaittinen  * Compute either gain or time based on scale and either the gain or time
5638416c28SMatti Vaittinen  * depending on which one is known.
5738416c28SMatti Vaittinen  *
5838416c28SMatti Vaittinen  * Return:	0 on success.
5938416c28SMatti Vaittinen  */
gain_get_scale_fraction(const u64 max,u64 scale,int known,int * unknown)6038416c28SMatti Vaittinen static int gain_get_scale_fraction(const u64 max, u64 scale, int known,
6138416c28SMatti Vaittinen 				   int *unknown)
6238416c28SMatti Vaittinen {
6338416c28SMatti Vaittinen 	int tot_gain;
6438416c28SMatti Vaittinen 
6538416c28SMatti Vaittinen 	tot_gain = iio_gts_get_gain(max, scale);
6638416c28SMatti Vaittinen 	if (tot_gain < 0)
6738416c28SMatti Vaittinen 		return tot_gain;
6838416c28SMatti Vaittinen 
6938416c28SMatti Vaittinen 	*unknown = tot_gain / known;
7038416c28SMatti Vaittinen 
7138416c28SMatti Vaittinen 	/* We require total gain to be exact multiple of known * unknown */
7238416c28SMatti Vaittinen 	if (!*unknown || *unknown * known != tot_gain)
7338416c28SMatti Vaittinen 		return -EINVAL;
7438416c28SMatti Vaittinen 
7538416c28SMatti Vaittinen 	return 0;
7638416c28SMatti Vaittinen }
7738416c28SMatti Vaittinen 
iio_gts_delinearize(u64 lin_scale,unsigned long scaler,int * scale_whole,int * scale_nano)7838416c28SMatti Vaittinen static int iio_gts_delinearize(u64 lin_scale, unsigned long scaler,
7938416c28SMatti Vaittinen 			       int *scale_whole, int *scale_nano)
8038416c28SMatti Vaittinen {
8138416c28SMatti Vaittinen 	int frac;
8238416c28SMatti Vaittinen 
8338416c28SMatti Vaittinen 	if (scaler > NANO)
8438416c28SMatti Vaittinen 		return -EOVERFLOW;
8538416c28SMatti Vaittinen 
8638416c28SMatti Vaittinen 	if (!scaler)
8738416c28SMatti Vaittinen 		return -EINVAL;
8838416c28SMatti Vaittinen 
8938416c28SMatti Vaittinen 	frac = do_div(lin_scale, scaler);
9038416c28SMatti Vaittinen 
9138416c28SMatti Vaittinen 	*scale_whole = lin_scale;
9238416c28SMatti Vaittinen 	*scale_nano = frac * (NANO / scaler);
9338416c28SMatti Vaittinen 
9438416c28SMatti Vaittinen 	return 0;
9538416c28SMatti Vaittinen }
9638416c28SMatti Vaittinen 
iio_gts_linearize(int scale_whole,int scale_nano,unsigned long scaler,u64 * lin_scale)9738416c28SMatti Vaittinen static int iio_gts_linearize(int scale_whole, int scale_nano,
9838416c28SMatti Vaittinen 			     unsigned long scaler, u64 *lin_scale)
9938416c28SMatti Vaittinen {
10038416c28SMatti Vaittinen 	/*
10138416c28SMatti Vaittinen 	 * Expect scale to be (mostly) NANO or MICRO. Divide divider instead of
10238416c28SMatti Vaittinen 	 * multiplication followed by division to avoid overflow.
10338416c28SMatti Vaittinen 	 */
10438416c28SMatti Vaittinen 	if (scaler > NANO || !scaler)
10538416c28SMatti Vaittinen 		return -EINVAL;
10638416c28SMatti Vaittinen 
10738416c28SMatti Vaittinen 	*lin_scale = (u64)scale_whole * (u64)scaler +
10838416c28SMatti Vaittinen 		     (u64)(scale_nano / (NANO / scaler));
10938416c28SMatti Vaittinen 
11038416c28SMatti Vaittinen 	return 0;
11138416c28SMatti Vaittinen }
11238416c28SMatti Vaittinen 
11338416c28SMatti Vaittinen /**
11438416c28SMatti Vaittinen  * iio_gts_total_gain_to_scale - convert gain to scale
11538416c28SMatti Vaittinen  * @gts:	Gain time scale descriptor
11638416c28SMatti Vaittinen  * @total_gain:	the gain to be converted
11738416c28SMatti Vaittinen  * @scale_int:	Pointer to integral part of the scale (typically val1)
11838416c28SMatti Vaittinen  * @scale_nano:	Pointer to fractional part of the scale (nano or ppb)
11938416c28SMatti Vaittinen  *
12038416c28SMatti Vaittinen  * Convert the total gain value to scale. NOTE: This does not separate gain
12138416c28SMatti Vaittinen  * generated by HW-gain or integration time. It is up to caller to decide what
12238416c28SMatti Vaittinen  * part of the total gain is due to integration time and what due to HW-gain.
12338416c28SMatti Vaittinen  *
12438416c28SMatti Vaittinen  * Return: 0 on success. Negative errno on failure.
12538416c28SMatti Vaittinen  */
iio_gts_total_gain_to_scale(struct iio_gts * gts,int total_gain,int * scale_int,int * scale_nano)12638416c28SMatti Vaittinen int iio_gts_total_gain_to_scale(struct iio_gts *gts, int total_gain,
12738416c28SMatti Vaittinen 				int *scale_int, int *scale_nano)
12838416c28SMatti Vaittinen {
12938416c28SMatti Vaittinen 	u64 tmp;
13038416c28SMatti Vaittinen 
13138416c28SMatti Vaittinen 	tmp = gts->max_scale;
13238416c28SMatti Vaittinen 
13338416c28SMatti Vaittinen 	do_div(tmp, total_gain);
13438416c28SMatti Vaittinen 
13538416c28SMatti Vaittinen 	return iio_gts_delinearize(tmp, NANO, scale_int, scale_nano);
13638416c28SMatti Vaittinen }
13738416c28SMatti Vaittinen EXPORT_SYMBOL_NS_GPL(iio_gts_total_gain_to_scale, IIO_GTS_HELPER);
13838416c28SMatti Vaittinen 
13938416c28SMatti Vaittinen /**
14038416c28SMatti Vaittinen  * iio_gts_purge_avail_scale_table - free-up the available scale tables
14138416c28SMatti Vaittinen  * @gts:	Gain time scale descriptor
14238416c28SMatti Vaittinen  *
14338416c28SMatti Vaittinen  * Free the space reserved by iio_gts_build_avail_scale_table().
14438416c28SMatti Vaittinen  */
iio_gts_purge_avail_scale_table(struct iio_gts * gts)14538416c28SMatti Vaittinen static void iio_gts_purge_avail_scale_table(struct iio_gts *gts)
14638416c28SMatti Vaittinen {
14738416c28SMatti Vaittinen 	int i;
14838416c28SMatti Vaittinen 
14938416c28SMatti Vaittinen 	if (gts->per_time_avail_scale_tables) {
15038416c28SMatti Vaittinen 		for (i = 0; i < gts->num_itime; i++)
15138416c28SMatti Vaittinen 			kfree(gts->per_time_avail_scale_tables[i]);
15238416c28SMatti Vaittinen 
15338416c28SMatti Vaittinen 		kfree(gts->per_time_avail_scale_tables);
15438416c28SMatti Vaittinen 		gts->per_time_avail_scale_tables = NULL;
15538416c28SMatti Vaittinen 	}
15638416c28SMatti Vaittinen 
15738416c28SMatti Vaittinen 	kfree(gts->avail_all_scales_table);
15838416c28SMatti Vaittinen 	gts->avail_all_scales_table = NULL;
15938416c28SMatti Vaittinen 
16038416c28SMatti Vaittinen 	gts->num_avail_all_scales = 0;
16138416c28SMatti Vaittinen }
16238416c28SMatti Vaittinen 
iio_gts_gain_cmp(const void * a,const void * b)16338416c28SMatti Vaittinen static int iio_gts_gain_cmp(const void *a, const void *b)
16438416c28SMatti Vaittinen {
16538416c28SMatti Vaittinen 	return *(int *)a - *(int *)b;
16638416c28SMatti Vaittinen }
16738416c28SMatti Vaittinen 
gain_to_scaletables(struct iio_gts * gts,int ** gains,int ** scales)16838416c28SMatti Vaittinen static int gain_to_scaletables(struct iio_gts *gts, int **gains, int **scales)
16938416c28SMatti Vaittinen {
17038416c28SMatti Vaittinen 	int ret, i, j, new_idx, time_idx;
17138416c28SMatti Vaittinen 	int *all_gains;
17238416c28SMatti Vaittinen 	size_t gain_bytes;
17338416c28SMatti Vaittinen 
17438416c28SMatti Vaittinen 	for (i = 0; i < gts->num_itime; i++) {
17538416c28SMatti Vaittinen 		/*
17638416c28SMatti Vaittinen 		 * Sort the tables for nice output and for easier finding of
17738416c28SMatti Vaittinen 		 * unique values.
17838416c28SMatti Vaittinen 		 */
17938416c28SMatti Vaittinen 		sort(gains[i], gts->num_hwgain, sizeof(int), iio_gts_gain_cmp,
18038416c28SMatti Vaittinen 		     NULL);
18138416c28SMatti Vaittinen 
18238416c28SMatti Vaittinen 		/* Convert gains to scales */
18338416c28SMatti Vaittinen 		for (j = 0; j < gts->num_hwgain; j++) {
18438416c28SMatti Vaittinen 			ret = iio_gts_total_gain_to_scale(gts, gains[i][j],
18538416c28SMatti Vaittinen 							  &scales[i][2 * j],
18638416c28SMatti Vaittinen 							  &scales[i][2 * j + 1]);
18738416c28SMatti Vaittinen 			if (ret)
18838416c28SMatti Vaittinen 				return ret;
18938416c28SMatti Vaittinen 		}
19038416c28SMatti Vaittinen 	}
19138416c28SMatti Vaittinen 
19238416c28SMatti Vaittinen 	gain_bytes = array_size(gts->num_hwgain, sizeof(int));
19338416c28SMatti Vaittinen 	all_gains = kcalloc(gts->num_itime, gain_bytes, GFP_KERNEL);
19438416c28SMatti Vaittinen 	if (!all_gains)
19538416c28SMatti Vaittinen 		return -ENOMEM;
19638416c28SMatti Vaittinen 
19738416c28SMatti Vaittinen 	/*
19838416c28SMatti Vaittinen 	 * We assume all the gains for same integration time were unique.
19938416c28SMatti Vaittinen 	 * It is likely the first time table had greatest time multiplier as
20038416c28SMatti Vaittinen 	 * the times are in the order of preference and greater times are
20138416c28SMatti Vaittinen 	 * usually preferred. Hence we start from the last table which is likely
20238416c28SMatti Vaittinen 	 * to have the smallest total gains.
20338416c28SMatti Vaittinen 	 */
20438416c28SMatti Vaittinen 	time_idx = gts->num_itime - 1;
20538416c28SMatti Vaittinen 	memcpy(all_gains, gains[time_idx], gain_bytes);
20638416c28SMatti Vaittinen 	new_idx = gts->num_hwgain;
20738416c28SMatti Vaittinen 
20838416c28SMatti Vaittinen 	while (time_idx--) {
20938416c28SMatti Vaittinen 		for (j = 0; j < gts->num_hwgain; j++) {
21038416c28SMatti Vaittinen 			int candidate = gains[time_idx][j];
21138416c28SMatti Vaittinen 			int chk;
21238416c28SMatti Vaittinen 
21338416c28SMatti Vaittinen 			if (candidate > all_gains[new_idx - 1]) {
21438416c28SMatti Vaittinen 				all_gains[new_idx] = candidate;
21538416c28SMatti Vaittinen 				new_idx++;
21638416c28SMatti Vaittinen 
21738416c28SMatti Vaittinen 				continue;
21838416c28SMatti Vaittinen 			}
21938416c28SMatti Vaittinen 			for (chk = 0; chk < new_idx; chk++)
22038416c28SMatti Vaittinen 				if (candidate <= all_gains[chk])
22138416c28SMatti Vaittinen 					break;
22238416c28SMatti Vaittinen 
22338416c28SMatti Vaittinen 			if (candidate == all_gains[chk])
22438416c28SMatti Vaittinen 				continue;
22538416c28SMatti Vaittinen 
22638416c28SMatti Vaittinen 			memmove(&all_gains[chk + 1], &all_gains[chk],
22738416c28SMatti Vaittinen 				(new_idx - chk) * sizeof(int));
22838416c28SMatti Vaittinen 			all_gains[chk] = candidate;
22938416c28SMatti Vaittinen 			new_idx++;
23038416c28SMatti Vaittinen 		}
23138416c28SMatti Vaittinen 	}
23238416c28SMatti Vaittinen 
23338416c28SMatti Vaittinen 	gts->avail_all_scales_table = kcalloc(new_idx, 2 * sizeof(int),
23438416c28SMatti Vaittinen 					      GFP_KERNEL);
23538416c28SMatti Vaittinen 	if (!gts->avail_all_scales_table) {
23638416c28SMatti Vaittinen 		ret = -ENOMEM;
23738416c28SMatti Vaittinen 		goto free_out;
23838416c28SMatti Vaittinen 	}
23938416c28SMatti Vaittinen 	gts->num_avail_all_scales = new_idx;
24038416c28SMatti Vaittinen 
24138416c28SMatti Vaittinen 	for (i = 0; i < gts->num_avail_all_scales; i++) {
24238416c28SMatti Vaittinen 		ret = iio_gts_total_gain_to_scale(gts, all_gains[i],
24338416c28SMatti Vaittinen 					&gts->avail_all_scales_table[i * 2],
24438416c28SMatti Vaittinen 					&gts->avail_all_scales_table[i * 2 + 1]);
24538416c28SMatti Vaittinen 
24638416c28SMatti Vaittinen 		if (ret) {
24738416c28SMatti Vaittinen 			kfree(gts->avail_all_scales_table);
24838416c28SMatti Vaittinen 			gts->num_avail_all_scales = 0;
24938416c28SMatti Vaittinen 			goto free_out;
25038416c28SMatti Vaittinen 		}
25138416c28SMatti Vaittinen 	}
25238416c28SMatti Vaittinen 
25338416c28SMatti Vaittinen free_out:
25438416c28SMatti Vaittinen 	kfree(all_gains);
25538416c28SMatti Vaittinen 
25638416c28SMatti Vaittinen 	return ret;
25738416c28SMatti Vaittinen }
25838416c28SMatti Vaittinen 
25938416c28SMatti Vaittinen /**
26038416c28SMatti Vaittinen  * iio_gts_build_avail_scale_table - create tables of available scales
26138416c28SMatti Vaittinen  * @gts:	Gain time scale descriptor
26238416c28SMatti Vaittinen  *
26338416c28SMatti Vaittinen  * Build the tables which can represent the available scales based on the
26438416c28SMatti Vaittinen  * originally given gain and time tables. When both time and gain tables are
26538416c28SMatti Vaittinen  * given this results:
26638416c28SMatti Vaittinen  * 1. A set of tables representing available scales for each supported
26738416c28SMatti Vaittinen  *    integration time.
26838416c28SMatti Vaittinen  * 2. A single table listing all the unique scales that any combination of
26938416c28SMatti Vaittinen  *    supported gains and times can provide.
27038416c28SMatti Vaittinen  *
27138416c28SMatti Vaittinen  * NOTE: Space allocated for the tables must be freed using
27238416c28SMatti Vaittinen  * iio_gts_purge_avail_scale_table() when the tables are no longer needed.
27338416c28SMatti Vaittinen  *
27438416c28SMatti Vaittinen  * Return: 0 on success.
27538416c28SMatti Vaittinen  */
iio_gts_build_avail_scale_table(struct iio_gts * gts)27638416c28SMatti Vaittinen static int iio_gts_build_avail_scale_table(struct iio_gts *gts)
27738416c28SMatti Vaittinen {
27838416c28SMatti Vaittinen 	int **per_time_gains, **per_time_scales, i, j, ret = -ENOMEM;
27938416c28SMatti Vaittinen 
28038416c28SMatti Vaittinen 	per_time_gains = kcalloc(gts->num_itime, sizeof(*per_time_gains), GFP_KERNEL);
28138416c28SMatti Vaittinen 	if (!per_time_gains)
28238416c28SMatti Vaittinen 		return ret;
28338416c28SMatti Vaittinen 
28438416c28SMatti Vaittinen 	per_time_scales = kcalloc(gts->num_itime, sizeof(*per_time_scales), GFP_KERNEL);
28538416c28SMatti Vaittinen 	if (!per_time_scales)
28638416c28SMatti Vaittinen 		goto free_gains;
28738416c28SMatti Vaittinen 
28838416c28SMatti Vaittinen 	for (i = 0; i < gts->num_itime; i++) {
28938416c28SMatti Vaittinen 		per_time_scales[i] = kcalloc(gts->num_hwgain, 2 * sizeof(int),
29038416c28SMatti Vaittinen 					     GFP_KERNEL);
29138416c28SMatti Vaittinen 		if (!per_time_scales[i])
29238416c28SMatti Vaittinen 			goto err_free_out;
29338416c28SMatti Vaittinen 
29438416c28SMatti Vaittinen 		per_time_gains[i] = kcalloc(gts->num_hwgain, sizeof(int),
29538416c28SMatti Vaittinen 					    GFP_KERNEL);
29638416c28SMatti Vaittinen 		if (!per_time_gains[i]) {
29738416c28SMatti Vaittinen 			kfree(per_time_scales[i]);
29838416c28SMatti Vaittinen 			goto err_free_out;
29938416c28SMatti Vaittinen 		}
30038416c28SMatti Vaittinen 
30138416c28SMatti Vaittinen 		for (j = 0; j < gts->num_hwgain; j++)
30238416c28SMatti Vaittinen 			per_time_gains[i][j] = gts->hwgain_table[j].gain *
30338416c28SMatti Vaittinen 					       gts->itime_table[i].mul;
30438416c28SMatti Vaittinen 	}
30538416c28SMatti Vaittinen 
30638416c28SMatti Vaittinen 	ret = gain_to_scaletables(gts, per_time_gains, per_time_scales);
30738416c28SMatti Vaittinen 	if (ret)
30838416c28SMatti Vaittinen 		goto err_free_out;
30938416c28SMatti Vaittinen 
31038416c28SMatti Vaittinen 	kfree(per_time_gains);
31138416c28SMatti Vaittinen 	gts->per_time_avail_scale_tables = per_time_scales;
31238416c28SMatti Vaittinen 
31338416c28SMatti Vaittinen 	return 0;
31438416c28SMatti Vaittinen 
31538416c28SMatti Vaittinen err_free_out:
31638416c28SMatti Vaittinen 	for (i--; i; i--) {
31738416c28SMatti Vaittinen 		kfree(per_time_scales[i]);
31838416c28SMatti Vaittinen 		kfree(per_time_gains[i]);
31938416c28SMatti Vaittinen 	}
32038416c28SMatti Vaittinen 	kfree(per_time_scales);
32138416c28SMatti Vaittinen free_gains:
32238416c28SMatti Vaittinen 	kfree(per_time_gains);
32338416c28SMatti Vaittinen 
32438416c28SMatti Vaittinen 	return ret;
32538416c28SMatti Vaittinen }
32638416c28SMatti Vaittinen 
iio_gts_us_to_int_micro(int * time_us,int * int_micro_times,int num_times)327e6506513SMatti Vaittinen static void iio_gts_us_to_int_micro(int *time_us, int *int_micro_times,
328e6506513SMatti Vaittinen 				    int num_times)
329e6506513SMatti Vaittinen {
330e6506513SMatti Vaittinen 	int i;
331e6506513SMatti Vaittinen 
332e6506513SMatti Vaittinen 	for (i = 0; i < num_times; i++) {
333e6506513SMatti Vaittinen 		int_micro_times[i * 2] = time_us[i] / 1000000;
334e6506513SMatti Vaittinen 		int_micro_times[i * 2 + 1] = time_us[i] % 1000000;
335e6506513SMatti Vaittinen 	}
336e6506513SMatti Vaittinen }
337e6506513SMatti Vaittinen 
33838416c28SMatti Vaittinen /**
33938416c28SMatti Vaittinen  * iio_gts_build_avail_time_table - build table of available integration times
34038416c28SMatti Vaittinen  * @gts:	Gain time scale descriptor
34138416c28SMatti Vaittinen  *
34238416c28SMatti Vaittinen  * Build the table which can represent the available times to be returned
34338416c28SMatti Vaittinen  * to users using the read_avail-callback.
34438416c28SMatti Vaittinen  *
34538416c28SMatti Vaittinen  * NOTE: Space allocated for the tables must be freed using
34638416c28SMatti Vaittinen  * iio_gts_purge_avail_time_table() when the tables are no longer needed.
34738416c28SMatti Vaittinen  *
34838416c28SMatti Vaittinen  * Return: 0 on success.
34938416c28SMatti Vaittinen  */
iio_gts_build_avail_time_table(struct iio_gts * gts)35038416c28SMatti Vaittinen static int iio_gts_build_avail_time_table(struct iio_gts *gts)
35138416c28SMatti Vaittinen {
352e6506513SMatti Vaittinen 	int *times, i, j, idx = 0, *int_micro_times;
35338416c28SMatti Vaittinen 
35438416c28SMatti Vaittinen 	if (!gts->num_itime)
35538416c28SMatti Vaittinen 		return 0;
35638416c28SMatti Vaittinen 
35738416c28SMatti Vaittinen 	times = kcalloc(gts->num_itime, sizeof(int), GFP_KERNEL);
35838416c28SMatti Vaittinen 	if (!times)
35938416c28SMatti Vaittinen 		return -ENOMEM;
36038416c28SMatti Vaittinen 
36138416c28SMatti Vaittinen 	/* Sort times from all tables to one and remove duplicates */
36238416c28SMatti Vaittinen 	for (i = gts->num_itime - 1; i >= 0; i--) {
36338416c28SMatti Vaittinen 		int new = gts->itime_table[i].time_us;
36438416c28SMatti Vaittinen 
365*5acc3f97SChenyuan Yang 		if (idx == 0 || times[idx - 1] < new) {
36638416c28SMatti Vaittinen 			times[idx++] = new;
36738416c28SMatti Vaittinen 			continue;
36838416c28SMatti Vaittinen 		}
36938416c28SMatti Vaittinen 
370*5acc3f97SChenyuan Yang 		for (j = 0; j < idx; j++) {
371*5acc3f97SChenyuan Yang 			if (times[j] == new)
372*5acc3f97SChenyuan Yang 				break;
37338416c28SMatti Vaittinen 			if (times[j] > new) {
37438416c28SMatti Vaittinen 				memmove(&times[j + 1], &times[j],
37538416c28SMatti Vaittinen 					(idx - j) * sizeof(int));
37638416c28SMatti Vaittinen 				times[j] = new;
37738416c28SMatti Vaittinen 				idx++;
378*5acc3f97SChenyuan Yang 				break;
37938416c28SMatti Vaittinen 			}
38038416c28SMatti Vaittinen 		}
38138416c28SMatti Vaittinen 	}
382e6506513SMatti Vaittinen 
383e6506513SMatti Vaittinen 	/* create a list of times formatted as list of IIO_VAL_INT_PLUS_MICRO */
384e6506513SMatti Vaittinen 	int_micro_times = kcalloc(idx, sizeof(int) * 2, GFP_KERNEL);
385e6506513SMatti Vaittinen 	if (int_micro_times) {
38638416c28SMatti Vaittinen 		/*
387e6506513SMatti Vaittinen 		 * This is just to survive a unlikely corner-case where times in
388e6506513SMatti Vaittinen 		 * the given time table were not unique. Else we could just
389e6506513SMatti Vaittinen 		 * trust the gts->num_itime.
39038416c28SMatti Vaittinen 		 */
39138416c28SMatti Vaittinen 		gts->num_avail_time_tables = idx;
392e6506513SMatti Vaittinen 		iio_gts_us_to_int_micro(times, int_micro_times, idx);
393e6506513SMatti Vaittinen 	}
394e6506513SMatti Vaittinen 
395e6506513SMatti Vaittinen 	gts->avail_time_tables = int_micro_times;
396e6506513SMatti Vaittinen 	kfree(times);
397e6506513SMatti Vaittinen 
398e6506513SMatti Vaittinen 	if (!int_micro_times)
399e6506513SMatti Vaittinen 		return -ENOMEM;
40038416c28SMatti Vaittinen 
40138416c28SMatti Vaittinen 	return 0;
40238416c28SMatti Vaittinen }
40338416c28SMatti Vaittinen 
40438416c28SMatti Vaittinen /**
40538416c28SMatti Vaittinen  * iio_gts_purge_avail_time_table - free-up the available integration time table
40638416c28SMatti Vaittinen  * @gts:	Gain time scale descriptor
40738416c28SMatti Vaittinen  *
40838416c28SMatti Vaittinen  * Free the space reserved by iio_gts_build_avail_time_table().
40938416c28SMatti Vaittinen  */
iio_gts_purge_avail_time_table(struct iio_gts * gts)41038416c28SMatti Vaittinen static void iio_gts_purge_avail_time_table(struct iio_gts *gts)
41138416c28SMatti Vaittinen {
41238416c28SMatti Vaittinen 	if (gts->num_avail_time_tables) {
41338416c28SMatti Vaittinen 		kfree(gts->avail_time_tables);
41438416c28SMatti Vaittinen 		gts->avail_time_tables = NULL;
41538416c28SMatti Vaittinen 		gts->num_avail_time_tables = 0;
41638416c28SMatti Vaittinen 	}
41738416c28SMatti Vaittinen }
41838416c28SMatti Vaittinen 
41938416c28SMatti Vaittinen /**
42038416c28SMatti Vaittinen  * iio_gts_build_avail_tables - create tables of available scales and int times
42138416c28SMatti Vaittinen  * @gts:	Gain time scale descriptor
42238416c28SMatti Vaittinen  *
42338416c28SMatti Vaittinen  * Build the tables which can represent the available scales and available
42438416c28SMatti Vaittinen  * integration times. Availability tables are built based on the originally
42538416c28SMatti Vaittinen  * given gain and given time tables.
42638416c28SMatti Vaittinen  *
42738416c28SMatti Vaittinen  * When both time and gain tables are
42838416c28SMatti Vaittinen  * given this results:
42938416c28SMatti Vaittinen  * 1. A set of sorted tables representing available scales for each supported
43038416c28SMatti Vaittinen  *    integration time.
43138416c28SMatti Vaittinen  * 2. A single sorted table listing all the unique scales that any combination
43238416c28SMatti Vaittinen  *    of supported gains and times can provide.
43338416c28SMatti Vaittinen  * 3. A sorted table of supported integration times
43438416c28SMatti Vaittinen  *
43538416c28SMatti Vaittinen  * After these tables are built one can use the iio_gts_all_avail_scales(),
43638416c28SMatti Vaittinen  * iio_gts_avail_scales_for_time() and iio_gts_avail_times() helpers to
43738416c28SMatti Vaittinen  * implement the read_avail operations.
43838416c28SMatti Vaittinen  *
43938416c28SMatti Vaittinen  * NOTE: Space allocated for the tables must be freed using
44038416c28SMatti Vaittinen  * iio_gts_purge_avail_tables() when the tables are no longer needed.
44138416c28SMatti Vaittinen  *
44238416c28SMatti Vaittinen  * Return: 0 on success.
44338416c28SMatti Vaittinen  */
iio_gts_build_avail_tables(struct iio_gts * gts)44438416c28SMatti Vaittinen static int iio_gts_build_avail_tables(struct iio_gts *gts)
44538416c28SMatti Vaittinen {
44638416c28SMatti Vaittinen 	int ret;
44738416c28SMatti Vaittinen 
44838416c28SMatti Vaittinen 	ret = iio_gts_build_avail_scale_table(gts);
44938416c28SMatti Vaittinen 	if (ret)
45038416c28SMatti Vaittinen 		return ret;
45138416c28SMatti Vaittinen 
45238416c28SMatti Vaittinen 	ret = iio_gts_build_avail_time_table(gts);
45338416c28SMatti Vaittinen 	if (ret)
45438416c28SMatti Vaittinen 		iio_gts_purge_avail_scale_table(gts);
45538416c28SMatti Vaittinen 
45638416c28SMatti Vaittinen 	return ret;
45738416c28SMatti Vaittinen }
45838416c28SMatti Vaittinen 
45938416c28SMatti Vaittinen /**
46038416c28SMatti Vaittinen  * iio_gts_purge_avail_tables - free-up the availability tables
46138416c28SMatti Vaittinen  * @gts:	Gain time scale descriptor
46238416c28SMatti Vaittinen  *
46338416c28SMatti Vaittinen  * Free the space reserved by iio_gts_build_avail_tables(). Frees both the
46438416c28SMatti Vaittinen  * integration time and scale tables.
46538416c28SMatti Vaittinen  */
iio_gts_purge_avail_tables(struct iio_gts * gts)46638416c28SMatti Vaittinen static void iio_gts_purge_avail_tables(struct iio_gts *gts)
46738416c28SMatti Vaittinen {
46838416c28SMatti Vaittinen 	iio_gts_purge_avail_time_table(gts);
46938416c28SMatti Vaittinen 	iio_gts_purge_avail_scale_table(gts);
47038416c28SMatti Vaittinen }
47138416c28SMatti Vaittinen 
devm_iio_gts_avail_all_drop(void * res)47238416c28SMatti Vaittinen static void devm_iio_gts_avail_all_drop(void *res)
47338416c28SMatti Vaittinen {
47438416c28SMatti Vaittinen 	iio_gts_purge_avail_tables(res);
47538416c28SMatti Vaittinen }
47638416c28SMatti Vaittinen 
47738416c28SMatti Vaittinen /**
47838416c28SMatti Vaittinen  * devm_iio_gts_build_avail_tables - manged add availability tables
47938416c28SMatti Vaittinen  * @dev:	Pointer to the device whose lifetime tables are bound
48038416c28SMatti Vaittinen  * @gts:	Gain time scale descriptor
48138416c28SMatti Vaittinen  *
48238416c28SMatti Vaittinen  * Build the tables which can represent the available scales and available
48338416c28SMatti Vaittinen  * integration times. Availability tables are built based on the originally
48438416c28SMatti Vaittinen  * given gain and given time tables.
48538416c28SMatti Vaittinen  *
48638416c28SMatti Vaittinen  * When both time and gain tables are given this results:
48738416c28SMatti Vaittinen  * 1. A set of sorted tables representing available scales for each supported
48838416c28SMatti Vaittinen  *    integration time.
48938416c28SMatti Vaittinen  * 2. A single sorted table listing all the unique scales that any combination
49038416c28SMatti Vaittinen  *    of supported gains and times can provide.
49138416c28SMatti Vaittinen  * 3. A sorted table of supported integration times
49238416c28SMatti Vaittinen  *
49338416c28SMatti Vaittinen  * After these tables are built one can use the iio_gts_all_avail_scales(),
49438416c28SMatti Vaittinen  * iio_gts_avail_scales_for_time() and iio_gts_avail_times() helpers to
49538416c28SMatti Vaittinen  * implement the read_avail operations.
49638416c28SMatti Vaittinen  *
49738416c28SMatti Vaittinen  * The tables are automatically released upon device detach.
49838416c28SMatti Vaittinen  *
49938416c28SMatti Vaittinen  * Return: 0 on success.
50038416c28SMatti Vaittinen  */
devm_iio_gts_build_avail_tables(struct device * dev,struct iio_gts * gts)50138416c28SMatti Vaittinen static int devm_iio_gts_build_avail_tables(struct device *dev,
50238416c28SMatti Vaittinen 					   struct iio_gts *gts)
50338416c28SMatti Vaittinen {
50438416c28SMatti Vaittinen 	int ret;
50538416c28SMatti Vaittinen 
50638416c28SMatti Vaittinen 	ret = iio_gts_build_avail_tables(gts);
50738416c28SMatti Vaittinen 	if (ret)
50838416c28SMatti Vaittinen 		return ret;
50938416c28SMatti Vaittinen 
51038416c28SMatti Vaittinen 	return devm_add_action_or_reset(dev, devm_iio_gts_avail_all_drop, gts);
51138416c28SMatti Vaittinen }
51238416c28SMatti Vaittinen 
sanity_check_time(const struct iio_itime_sel_mul * t)51338416c28SMatti Vaittinen static int sanity_check_time(const struct iio_itime_sel_mul *t)
51438416c28SMatti Vaittinen {
51538416c28SMatti Vaittinen 	if (t->sel < 0 || t->time_us < 0 || t->mul <= 0)
51638416c28SMatti Vaittinen 		return -EINVAL;
51738416c28SMatti Vaittinen 
51838416c28SMatti Vaittinen 	return 0;
51938416c28SMatti Vaittinen }
52038416c28SMatti Vaittinen 
sanity_check_gain(const struct iio_gain_sel_pair * g)52138416c28SMatti Vaittinen static int sanity_check_gain(const struct iio_gain_sel_pair *g)
52238416c28SMatti Vaittinen {
52338416c28SMatti Vaittinen 	if (g->sel < 0 || g->gain <= 0)
52438416c28SMatti Vaittinen 		return -EINVAL;
52538416c28SMatti Vaittinen 
52638416c28SMatti Vaittinen 	return 0;
52738416c28SMatti Vaittinen }
52838416c28SMatti Vaittinen 
iio_gts_sanity_check(struct iio_gts * gts)52938416c28SMatti Vaittinen static int iio_gts_sanity_check(struct iio_gts *gts)
53038416c28SMatti Vaittinen {
53138416c28SMatti Vaittinen 	int g, t, ret;
53238416c28SMatti Vaittinen 
53338416c28SMatti Vaittinen 	if (!gts->num_hwgain && !gts->num_itime)
53438416c28SMatti Vaittinen 		return -EINVAL;
53538416c28SMatti Vaittinen 
53638416c28SMatti Vaittinen 	for (t = 0; t < gts->num_itime; t++) {
53738416c28SMatti Vaittinen 		ret = sanity_check_time(&gts->itime_table[t]);
53838416c28SMatti Vaittinen 		if (ret)
53938416c28SMatti Vaittinen 			return ret;
54038416c28SMatti Vaittinen 	}
54138416c28SMatti Vaittinen 
54238416c28SMatti Vaittinen 	for (g = 0; g < gts->num_hwgain; g++) {
54338416c28SMatti Vaittinen 		ret = sanity_check_gain(&gts->hwgain_table[g]);
54438416c28SMatti Vaittinen 		if (ret)
54538416c28SMatti Vaittinen 			return ret;
54638416c28SMatti Vaittinen 	}
54738416c28SMatti Vaittinen 
54838416c28SMatti Vaittinen 	for (g = 0; g < gts->num_hwgain; g++) {
54938416c28SMatti Vaittinen 		for (t = 0; t < gts->num_itime; t++) {
55038416c28SMatti Vaittinen 			int gain, mul, res;
55138416c28SMatti Vaittinen 
55238416c28SMatti Vaittinen 			gain = gts->hwgain_table[g].gain;
55338416c28SMatti Vaittinen 			mul = gts->itime_table[t].mul;
55438416c28SMatti Vaittinen 
55538416c28SMatti Vaittinen 			if (check_mul_overflow(gain, mul, &res))
55638416c28SMatti Vaittinen 				return -EOVERFLOW;
55738416c28SMatti Vaittinen 		}
55838416c28SMatti Vaittinen 	}
55938416c28SMatti Vaittinen 
56038416c28SMatti Vaittinen 	return 0;
56138416c28SMatti Vaittinen }
56238416c28SMatti Vaittinen 
iio_init_iio_gts(int max_scale_int,int max_scale_nano,const struct iio_gain_sel_pair * gain_tbl,int num_gain,const struct iio_itime_sel_mul * tim_tbl,int num_times,struct iio_gts * gts)56338416c28SMatti Vaittinen static int iio_init_iio_gts(int max_scale_int, int max_scale_nano,
56438416c28SMatti Vaittinen 			const struct iio_gain_sel_pair *gain_tbl, int num_gain,
56538416c28SMatti Vaittinen 			const struct iio_itime_sel_mul *tim_tbl, int num_times,
56638416c28SMatti Vaittinen 			struct iio_gts *gts)
56738416c28SMatti Vaittinen {
56838416c28SMatti Vaittinen 	int ret;
56938416c28SMatti Vaittinen 
57038416c28SMatti Vaittinen 	memset(gts, 0, sizeof(*gts));
57138416c28SMatti Vaittinen 
57238416c28SMatti Vaittinen 	ret = iio_gts_linearize(max_scale_int, max_scale_nano, NANO,
57338416c28SMatti Vaittinen 				   &gts->max_scale);
57438416c28SMatti Vaittinen 	if (ret)
57538416c28SMatti Vaittinen 		return ret;
57638416c28SMatti Vaittinen 
57738416c28SMatti Vaittinen 	gts->hwgain_table = gain_tbl;
57838416c28SMatti Vaittinen 	gts->num_hwgain = num_gain;
57938416c28SMatti Vaittinen 	gts->itime_table = tim_tbl;
58038416c28SMatti Vaittinen 	gts->num_itime = num_times;
58138416c28SMatti Vaittinen 
58238416c28SMatti Vaittinen 	return iio_gts_sanity_check(gts);
58338416c28SMatti Vaittinen }
58438416c28SMatti Vaittinen 
58538416c28SMatti Vaittinen /**
58638416c28SMatti Vaittinen  * devm_iio_init_iio_gts - Initialize the gain-time-scale helper
58738416c28SMatti Vaittinen  * @dev:		Pointer to the device whose lifetime gts resources are
58838416c28SMatti Vaittinen  *			bound
58938416c28SMatti Vaittinen  * @max_scale_int:	integer part of the maximum scale value
59038416c28SMatti Vaittinen  * @max_scale_nano:	fraction part of the maximum scale value
59138416c28SMatti Vaittinen  * @gain_tbl:		table describing supported gains
59238416c28SMatti Vaittinen  * @num_gain:		number of gains in the gain table
59338416c28SMatti Vaittinen  * @tim_tbl:		table describing supported integration times. Provide
59438416c28SMatti Vaittinen  *			the integration time table sorted so that the preferred
59538416c28SMatti Vaittinen  *			integration time is in the first array index. The search
59638416c28SMatti Vaittinen  *			functions like the
59738416c28SMatti Vaittinen  *			iio_gts_find_time_and_gain_sel_for_scale() start search
59838416c28SMatti Vaittinen  *			from first provided time.
59938416c28SMatti Vaittinen  * @num_times:		number of times in the time table
60038416c28SMatti Vaittinen  * @gts:		pointer to the helper struct
60138416c28SMatti Vaittinen  *
60238416c28SMatti Vaittinen  * Initialize the gain-time-scale helper for use. Note, gains, times, selectors
60338416c28SMatti Vaittinen  * and multipliers must be positive. Negative values are reserved for error
60438416c28SMatti Vaittinen  * checking. The total gain (maximum gain * maximum time multiplier) must not
60538416c28SMatti Vaittinen  * overflow int. The allocated resources will be released upon device detach.
60638416c28SMatti Vaittinen  *
60738416c28SMatti Vaittinen  * Return: 0 on success.
60838416c28SMatti Vaittinen  */
devm_iio_init_iio_gts(struct device * dev,int max_scale_int,int max_scale_nano,const struct iio_gain_sel_pair * gain_tbl,int num_gain,const struct iio_itime_sel_mul * tim_tbl,int num_times,struct iio_gts * gts)60938416c28SMatti Vaittinen int devm_iio_init_iio_gts(struct device *dev, int max_scale_int, int max_scale_nano,
61038416c28SMatti Vaittinen 			  const struct iio_gain_sel_pair *gain_tbl, int num_gain,
61138416c28SMatti Vaittinen 			  const struct iio_itime_sel_mul *tim_tbl, int num_times,
61238416c28SMatti Vaittinen 			  struct iio_gts *gts)
61338416c28SMatti Vaittinen {
61438416c28SMatti Vaittinen 	int ret;
61538416c28SMatti Vaittinen 
61638416c28SMatti Vaittinen 	ret = iio_init_iio_gts(max_scale_int, max_scale_nano, gain_tbl,
61738416c28SMatti Vaittinen 			       num_gain, tim_tbl, num_times, gts);
61838416c28SMatti Vaittinen 	if (ret)
61938416c28SMatti Vaittinen 		return ret;
62038416c28SMatti Vaittinen 
62138416c28SMatti Vaittinen 	return devm_iio_gts_build_avail_tables(dev, gts);
62238416c28SMatti Vaittinen }
62338416c28SMatti Vaittinen EXPORT_SYMBOL_NS_GPL(devm_iio_init_iio_gts, IIO_GTS_HELPER);
62438416c28SMatti Vaittinen 
62538416c28SMatti Vaittinen /**
62638416c28SMatti Vaittinen  * iio_gts_all_avail_scales - helper for listing all available scales
62738416c28SMatti Vaittinen  * @gts:	Gain time scale descriptor
62838416c28SMatti Vaittinen  * @vals:	Returned array of supported scales
62938416c28SMatti Vaittinen  * @type:	Type of returned scale values
63038416c28SMatti Vaittinen  * @length:	Amount of returned values in array
63138416c28SMatti Vaittinen  *
63238416c28SMatti Vaittinen  * Return: a value suitable to be returned from read_avail or a negative error.
63338416c28SMatti Vaittinen  */
iio_gts_all_avail_scales(struct iio_gts * gts,const int ** vals,int * type,int * length)63438416c28SMatti Vaittinen int iio_gts_all_avail_scales(struct iio_gts *gts, const int **vals, int *type,
63538416c28SMatti Vaittinen 			     int *length)
63638416c28SMatti Vaittinen {
63738416c28SMatti Vaittinen 	if (!gts->num_avail_all_scales)
63838416c28SMatti Vaittinen 		return -EINVAL;
63938416c28SMatti Vaittinen 
64038416c28SMatti Vaittinen 	*vals = gts->avail_all_scales_table;
64138416c28SMatti Vaittinen 	*type = IIO_VAL_INT_PLUS_NANO;
64238416c28SMatti Vaittinen 	*length = gts->num_avail_all_scales * 2;
64338416c28SMatti Vaittinen 
64438416c28SMatti Vaittinen 	return IIO_AVAIL_LIST;
64538416c28SMatti Vaittinen }
64638416c28SMatti Vaittinen EXPORT_SYMBOL_NS_GPL(iio_gts_all_avail_scales, IIO_GTS_HELPER);
64738416c28SMatti Vaittinen 
64838416c28SMatti Vaittinen /**
64938416c28SMatti Vaittinen  * iio_gts_avail_scales_for_time - list scales for integration time
65038416c28SMatti Vaittinen  * @gts:	Gain time scale descriptor
65138416c28SMatti Vaittinen  * @time:	Integration time for which the scales are listed
65238416c28SMatti Vaittinen  * @vals:	Returned array of supported scales
65338416c28SMatti Vaittinen  * @type:	Type of returned scale values
65438416c28SMatti Vaittinen  * @length:	Amount of returned values in array
65538416c28SMatti Vaittinen  *
65638416c28SMatti Vaittinen  * Drivers which do not allow scale setting to change integration time can
65738416c28SMatti Vaittinen  * use this helper to list only the scales which are valid for given integration
65838416c28SMatti Vaittinen  * time.
65938416c28SMatti Vaittinen  *
66038416c28SMatti Vaittinen  * Return: a value suitable to be returned from read_avail or a negative error.
66138416c28SMatti Vaittinen  */
iio_gts_avail_scales_for_time(struct iio_gts * gts,int time,const int ** vals,int * type,int * length)66238416c28SMatti Vaittinen int iio_gts_avail_scales_for_time(struct iio_gts *gts, int time,
66338416c28SMatti Vaittinen 				  const int **vals, int *type, int *length)
66438416c28SMatti Vaittinen {
66538416c28SMatti Vaittinen 	int i;
66638416c28SMatti Vaittinen 
66738416c28SMatti Vaittinen 	for (i = 0; i < gts->num_itime; i++)
66838416c28SMatti Vaittinen 		if (gts->itime_table[i].time_us == time)
66938416c28SMatti Vaittinen 			break;
67038416c28SMatti Vaittinen 
67138416c28SMatti Vaittinen 	if (i == gts->num_itime)
67238416c28SMatti Vaittinen 		return -EINVAL;
67338416c28SMatti Vaittinen 
67438416c28SMatti Vaittinen 	*vals = gts->per_time_avail_scale_tables[i];
67538416c28SMatti Vaittinen 	*type = IIO_VAL_INT_PLUS_NANO;
67638416c28SMatti Vaittinen 	*length = gts->num_hwgain * 2;
67738416c28SMatti Vaittinen 
67838416c28SMatti Vaittinen 	return IIO_AVAIL_LIST;
67938416c28SMatti Vaittinen }
68038416c28SMatti Vaittinen EXPORT_SYMBOL_NS_GPL(iio_gts_avail_scales_for_time, IIO_GTS_HELPER);
68138416c28SMatti Vaittinen 
68238416c28SMatti Vaittinen /**
68338416c28SMatti Vaittinen  * iio_gts_avail_times - helper for listing available integration times
68438416c28SMatti Vaittinen  * @gts:	Gain time scale descriptor
68538416c28SMatti Vaittinen  * @vals:	Returned array of supported times
68638416c28SMatti Vaittinen  * @type:	Type of returned scale values
68738416c28SMatti Vaittinen  * @length:	Amount of returned values in array
68838416c28SMatti Vaittinen  *
68938416c28SMatti Vaittinen  * Return: a value suitable to be returned from read_avail or a negative error.
69038416c28SMatti Vaittinen  */
iio_gts_avail_times(struct iio_gts * gts,const int ** vals,int * type,int * length)69138416c28SMatti Vaittinen int iio_gts_avail_times(struct iio_gts *gts,  const int **vals, int *type,
69238416c28SMatti Vaittinen 			int *length)
69338416c28SMatti Vaittinen {
69438416c28SMatti Vaittinen 	if (!gts->num_avail_time_tables)
69538416c28SMatti Vaittinen 		return -EINVAL;
69638416c28SMatti Vaittinen 
69738416c28SMatti Vaittinen 	*vals = gts->avail_time_tables;
698e6506513SMatti Vaittinen 	*type = IIO_VAL_INT_PLUS_MICRO;
699e6506513SMatti Vaittinen 	*length = gts->num_avail_time_tables * 2;
70038416c28SMatti Vaittinen 
70138416c28SMatti Vaittinen 	return IIO_AVAIL_LIST;
70238416c28SMatti Vaittinen }
70338416c28SMatti Vaittinen EXPORT_SYMBOL_NS_GPL(iio_gts_avail_times, IIO_GTS_HELPER);
70438416c28SMatti Vaittinen 
70538416c28SMatti Vaittinen /**
70638416c28SMatti Vaittinen  * iio_gts_find_sel_by_gain - find selector corresponding to a HW-gain
70738416c28SMatti Vaittinen  * @gts:	Gain time scale descriptor
70838416c28SMatti Vaittinen  * @gain:	HW-gain for which matching selector is searched for
70938416c28SMatti Vaittinen  *
71038416c28SMatti Vaittinen  * Return:	a selector matching given HW-gain or -EINVAL if selector was
71138416c28SMatti Vaittinen  *		not found.
71238416c28SMatti Vaittinen  */
iio_gts_find_sel_by_gain(struct iio_gts * gts,int gain)71338416c28SMatti Vaittinen int iio_gts_find_sel_by_gain(struct iio_gts *gts, int gain)
71438416c28SMatti Vaittinen {
71538416c28SMatti Vaittinen 	int i;
71638416c28SMatti Vaittinen 
71738416c28SMatti Vaittinen 	for (i = 0; i < gts->num_hwgain; i++)
71838416c28SMatti Vaittinen 		if (gts->hwgain_table[i].gain == gain)
71938416c28SMatti Vaittinen 			return gts->hwgain_table[i].sel;
72038416c28SMatti Vaittinen 
72138416c28SMatti Vaittinen 	return -EINVAL;
72238416c28SMatti Vaittinen }
72338416c28SMatti Vaittinen EXPORT_SYMBOL_NS_GPL(iio_gts_find_sel_by_gain, IIO_GTS_HELPER);
72438416c28SMatti Vaittinen 
72538416c28SMatti Vaittinen /**
72638416c28SMatti Vaittinen  * iio_gts_find_gain_by_sel - find HW-gain corresponding to a selector
72738416c28SMatti Vaittinen  * @gts:	Gain time scale descriptor
72838416c28SMatti Vaittinen  * @sel:	selector for which matching HW-gain is searched for
72938416c28SMatti Vaittinen  *
73038416c28SMatti Vaittinen  * Return:	a HW-gain matching given selector or -EINVAL if HW-gain was not
73138416c28SMatti Vaittinen  *		found.
73238416c28SMatti Vaittinen  */
iio_gts_find_gain_by_sel(struct iio_gts * gts,int sel)73338416c28SMatti Vaittinen int iio_gts_find_gain_by_sel(struct iio_gts *gts, int sel)
73438416c28SMatti Vaittinen {
73538416c28SMatti Vaittinen 	int i;
73638416c28SMatti Vaittinen 
73738416c28SMatti Vaittinen 	for (i = 0; i < gts->num_hwgain; i++)
73838416c28SMatti Vaittinen 		if (gts->hwgain_table[i].sel == sel)
73938416c28SMatti Vaittinen 			return gts->hwgain_table[i].gain;
74038416c28SMatti Vaittinen 
74138416c28SMatti Vaittinen 	return -EINVAL;
74238416c28SMatti Vaittinen }
74338416c28SMatti Vaittinen EXPORT_SYMBOL_NS_GPL(iio_gts_find_gain_by_sel, IIO_GTS_HELPER);
74438416c28SMatti Vaittinen 
74538416c28SMatti Vaittinen /**
74638416c28SMatti Vaittinen  * iio_gts_get_min_gain - find smallest valid HW-gain
74738416c28SMatti Vaittinen  * @gts:	Gain time scale descriptor
74838416c28SMatti Vaittinen  *
74938416c28SMatti Vaittinen  * Return:	The smallest HW-gain -EINVAL if no HW-gains were in the tables.
75038416c28SMatti Vaittinen  */
iio_gts_get_min_gain(struct iio_gts * gts)75138416c28SMatti Vaittinen int iio_gts_get_min_gain(struct iio_gts *gts)
75238416c28SMatti Vaittinen {
75338416c28SMatti Vaittinen 	int i, min = -EINVAL;
75438416c28SMatti Vaittinen 
75538416c28SMatti Vaittinen 	for (i = 0; i < gts->num_hwgain; i++) {
75638416c28SMatti Vaittinen 		int gain = gts->hwgain_table[i].gain;
75738416c28SMatti Vaittinen 
75838416c28SMatti Vaittinen 		if (min == -EINVAL)
75938416c28SMatti Vaittinen 			min = gain;
76038416c28SMatti Vaittinen 		else
76138416c28SMatti Vaittinen 			min = min(min, gain);
76238416c28SMatti Vaittinen 	}
76338416c28SMatti Vaittinen 
76438416c28SMatti Vaittinen 	return min;
76538416c28SMatti Vaittinen }
76638416c28SMatti Vaittinen EXPORT_SYMBOL_NS_GPL(iio_gts_get_min_gain, IIO_GTS_HELPER);
76738416c28SMatti Vaittinen 
76838416c28SMatti Vaittinen /**
76938416c28SMatti Vaittinen  * iio_find_closest_gain_low - Find the closest lower matching gain
77038416c28SMatti Vaittinen  * @gts:	Gain time scale descriptor
77138416c28SMatti Vaittinen  * @gain:	HW-gain for which the closest match is searched
77238416c28SMatti Vaittinen  * @in_range:	indicate if the @gain was actually in the range of
77338416c28SMatti Vaittinen  *		supported gains.
77438416c28SMatti Vaittinen  *
77538416c28SMatti Vaittinen  * Search for closest supported gain that is lower than or equal to the
77638416c28SMatti Vaittinen  * gain given as a parameter. This is usable for drivers which do not require
77738416c28SMatti Vaittinen  * user to request exact matching gain but rather for rounding to a supported
77838416c28SMatti Vaittinen  * gain value which is equal or lower (setting lower gain is typical for
77938416c28SMatti Vaittinen  * avoiding saturation)
78038416c28SMatti Vaittinen  *
78138416c28SMatti Vaittinen  * Return:	The closest matching supported gain or -EINVAL if @gain
78238416c28SMatti Vaittinen  *		was smaller than the smallest supported gain.
78338416c28SMatti Vaittinen  */
iio_find_closest_gain_low(struct iio_gts * gts,int gain,bool * in_range)78438416c28SMatti Vaittinen int iio_find_closest_gain_low(struct iio_gts *gts, int gain, bool *in_range)
78538416c28SMatti Vaittinen {
78638416c28SMatti Vaittinen 	int i, diff = 0;
78738416c28SMatti Vaittinen 	int best = -1;
78838416c28SMatti Vaittinen 
78938416c28SMatti Vaittinen 	*in_range = false;
79038416c28SMatti Vaittinen 
79138416c28SMatti Vaittinen 	for (i = 0; i < gts->num_hwgain; i++) {
79238416c28SMatti Vaittinen 		if (gain == gts->hwgain_table[i].gain) {
79338416c28SMatti Vaittinen 			*in_range = true;
79438416c28SMatti Vaittinen 			return gain;
79538416c28SMatti Vaittinen 		}
79638416c28SMatti Vaittinen 
79738416c28SMatti Vaittinen 		if (gain > gts->hwgain_table[i].gain) {
79838416c28SMatti Vaittinen 			if (!diff) {
79938416c28SMatti Vaittinen 				diff = gain - gts->hwgain_table[i].gain;
80038416c28SMatti Vaittinen 				best = i;
80138416c28SMatti Vaittinen 			} else {
80238416c28SMatti Vaittinen 				int tmp = gain - gts->hwgain_table[i].gain;
80338416c28SMatti Vaittinen 
80438416c28SMatti Vaittinen 				if (tmp < diff) {
80538416c28SMatti Vaittinen 					diff = tmp;
80638416c28SMatti Vaittinen 					best = i;
80738416c28SMatti Vaittinen 				}
80838416c28SMatti Vaittinen 			}
80938416c28SMatti Vaittinen 		} else {
81038416c28SMatti Vaittinen 			/*
81138416c28SMatti Vaittinen 			 * We found valid HW-gain which is greater than
81238416c28SMatti Vaittinen 			 * reference. So, unless we return a failure below we
81338416c28SMatti Vaittinen 			 * will have found an in-range gain
81438416c28SMatti Vaittinen 			 */
81538416c28SMatti Vaittinen 			*in_range = true;
81638416c28SMatti Vaittinen 		}
81738416c28SMatti Vaittinen 	}
81838416c28SMatti Vaittinen 	/* The requested gain was smaller than anything we support */
81938416c28SMatti Vaittinen 	if (!diff) {
82038416c28SMatti Vaittinen 		*in_range = false;
82138416c28SMatti Vaittinen 
82238416c28SMatti Vaittinen 		return -EINVAL;
82338416c28SMatti Vaittinen 	}
82438416c28SMatti Vaittinen 
82538416c28SMatti Vaittinen 	return gts->hwgain_table[best].gain;
82638416c28SMatti Vaittinen }
82738416c28SMatti Vaittinen EXPORT_SYMBOL_NS_GPL(iio_find_closest_gain_low, IIO_GTS_HELPER);
82838416c28SMatti Vaittinen 
iio_gts_get_int_time_gain_multiplier_by_sel(struct iio_gts * gts,int sel)82938416c28SMatti Vaittinen static int iio_gts_get_int_time_gain_multiplier_by_sel(struct iio_gts *gts,
83038416c28SMatti Vaittinen 						       int sel)
83138416c28SMatti Vaittinen {
83238416c28SMatti Vaittinen 	const struct iio_itime_sel_mul *time;
83338416c28SMatti Vaittinen 
83438416c28SMatti Vaittinen 	time = iio_gts_find_itime_by_sel(gts, sel);
83538416c28SMatti Vaittinen 	if (!time)
83638416c28SMatti Vaittinen 		return -EINVAL;
83738416c28SMatti Vaittinen 
83838416c28SMatti Vaittinen 	return time->mul;
83938416c28SMatti Vaittinen }
84038416c28SMatti Vaittinen 
84138416c28SMatti Vaittinen /**
84238416c28SMatti Vaittinen  * iio_gts_find_gain_for_scale_using_time - Find gain by time and scale
84338416c28SMatti Vaittinen  * @gts:	Gain time scale descriptor
84438416c28SMatti Vaittinen  * @time_sel:	Integration time selector corresponding to the time gain is
84538416c28SMatti Vaittinen  *		searched for
84638416c28SMatti Vaittinen  * @scale_int:	Integral part of the scale (typically val1)
84738416c28SMatti Vaittinen  * @scale_nano:	Fractional part of the scale (nano or ppb)
84838416c28SMatti Vaittinen  * @gain:	Pointer to value where gain is stored.
84938416c28SMatti Vaittinen  *
85038416c28SMatti Vaittinen  * In some cases the light sensors may want to find a gain setting which
85138416c28SMatti Vaittinen  * corresponds given scale and integration time. Sensors which fill the
85238416c28SMatti Vaittinen  * gain and time tables may use this helper to retrieve the gain.
85338416c28SMatti Vaittinen  *
85438416c28SMatti Vaittinen  * Return:	0 on success. -EINVAL if gain matching the parameters is not
85538416c28SMatti Vaittinen  *		found.
85638416c28SMatti Vaittinen  */
iio_gts_find_gain_for_scale_using_time(struct iio_gts * gts,int time_sel,int scale_int,int scale_nano,int * gain)85738416c28SMatti Vaittinen static int iio_gts_find_gain_for_scale_using_time(struct iio_gts *gts, int time_sel,
85838416c28SMatti Vaittinen 						  int scale_int, int scale_nano,
85938416c28SMatti Vaittinen 						  int *gain)
86038416c28SMatti Vaittinen {
86138416c28SMatti Vaittinen 	u64 scale_linear;
86238416c28SMatti Vaittinen 	int ret, mul;
86338416c28SMatti Vaittinen 
86438416c28SMatti Vaittinen 	ret = iio_gts_linearize(scale_int, scale_nano, NANO, &scale_linear);
86538416c28SMatti Vaittinen 	if (ret)
86638416c28SMatti Vaittinen 		return ret;
86738416c28SMatti Vaittinen 
86838416c28SMatti Vaittinen 	ret = iio_gts_get_int_time_gain_multiplier_by_sel(gts, time_sel);
86938416c28SMatti Vaittinen 	if (ret < 0)
87038416c28SMatti Vaittinen 		return ret;
87138416c28SMatti Vaittinen 
87238416c28SMatti Vaittinen 	mul = ret;
87338416c28SMatti Vaittinen 
87438416c28SMatti Vaittinen 	ret = gain_get_scale_fraction(gts->max_scale, scale_linear, mul, gain);
87538416c28SMatti Vaittinen 	if (ret)
87638416c28SMatti Vaittinen 		return ret;
87738416c28SMatti Vaittinen 
87838416c28SMatti Vaittinen 	if (!iio_gts_valid_gain(gts, *gain))
87938416c28SMatti Vaittinen 		return -EINVAL;
88038416c28SMatti Vaittinen 
88138416c28SMatti Vaittinen 	return 0;
88238416c28SMatti Vaittinen }
88338416c28SMatti Vaittinen 
88438416c28SMatti Vaittinen /**
88538416c28SMatti Vaittinen  * iio_gts_find_gain_sel_for_scale_using_time - Fetch gain selector.
88638416c28SMatti Vaittinen  * @gts:	Gain time scale descriptor
88738416c28SMatti Vaittinen  * @time_sel:	Integration time selector corresponding to the time gain is
88838416c28SMatti Vaittinen  *		searched for
88938416c28SMatti Vaittinen  * @scale_int:	Integral part of the scale (typically val1)
89038416c28SMatti Vaittinen  * @scale_nano:	Fractional part of the scale (nano or ppb)
89138416c28SMatti Vaittinen  * @gain_sel:	Pointer to value where gain selector is stored.
89238416c28SMatti Vaittinen  *
89338416c28SMatti Vaittinen  * See iio_gts_find_gain_for_scale_using_time() for more information
89438416c28SMatti Vaittinen  */
iio_gts_find_gain_sel_for_scale_using_time(struct iio_gts * gts,int time_sel,int scale_int,int scale_nano,int * gain_sel)89538416c28SMatti Vaittinen int iio_gts_find_gain_sel_for_scale_using_time(struct iio_gts *gts, int time_sel,
89638416c28SMatti Vaittinen 					       int scale_int, int scale_nano,
89738416c28SMatti Vaittinen 					       int *gain_sel)
89838416c28SMatti Vaittinen {
89938416c28SMatti Vaittinen 	int gain, ret;
90038416c28SMatti Vaittinen 
90138416c28SMatti Vaittinen 	ret = iio_gts_find_gain_for_scale_using_time(gts, time_sel, scale_int,
90238416c28SMatti Vaittinen 						     scale_nano, &gain);
90338416c28SMatti Vaittinen 	if (ret)
90438416c28SMatti Vaittinen 		return ret;
90538416c28SMatti Vaittinen 
90638416c28SMatti Vaittinen 	ret = iio_gts_find_sel_by_gain(gts, gain);
90738416c28SMatti Vaittinen 	if (ret < 0)
90838416c28SMatti Vaittinen 		return ret;
90938416c28SMatti Vaittinen 
91038416c28SMatti Vaittinen 	*gain_sel = ret;
91138416c28SMatti Vaittinen 
91238416c28SMatti Vaittinen 	return 0;
91338416c28SMatti Vaittinen }
91438416c28SMatti Vaittinen EXPORT_SYMBOL_NS_GPL(iio_gts_find_gain_sel_for_scale_using_time, IIO_GTS_HELPER);
91538416c28SMatti Vaittinen 
iio_gts_get_total_gain(struct iio_gts * gts,int gain,int time)91638416c28SMatti Vaittinen static int iio_gts_get_total_gain(struct iio_gts *gts, int gain, int time)
91738416c28SMatti Vaittinen {
91838416c28SMatti Vaittinen 	const struct iio_itime_sel_mul *itime;
91938416c28SMatti Vaittinen 
92038416c28SMatti Vaittinen 	if (!iio_gts_valid_gain(gts, gain))
92138416c28SMatti Vaittinen 		return -EINVAL;
92238416c28SMatti Vaittinen 
92338416c28SMatti Vaittinen 	if (!gts->num_itime)
92438416c28SMatti Vaittinen 		return gain;
92538416c28SMatti Vaittinen 
92638416c28SMatti Vaittinen 	itime = iio_gts_find_itime_by_time(gts, time);
92738416c28SMatti Vaittinen 	if (!itime)
92838416c28SMatti Vaittinen 		return -EINVAL;
92938416c28SMatti Vaittinen 
93038416c28SMatti Vaittinen 	return gain * itime->mul;
93138416c28SMatti Vaittinen }
93238416c28SMatti Vaittinen 
iio_gts_get_scale_linear(struct iio_gts * gts,int gain,int time,u64 * scale)93338416c28SMatti Vaittinen static int iio_gts_get_scale_linear(struct iio_gts *gts, int gain, int time,
93438416c28SMatti Vaittinen 				    u64 *scale)
93538416c28SMatti Vaittinen {
93638416c28SMatti Vaittinen 	int total_gain;
93738416c28SMatti Vaittinen 	u64 tmp;
93838416c28SMatti Vaittinen 
93938416c28SMatti Vaittinen 	total_gain = iio_gts_get_total_gain(gts, gain, time);
94038416c28SMatti Vaittinen 	if (total_gain < 0)
94138416c28SMatti Vaittinen 		return total_gain;
94238416c28SMatti Vaittinen 
94338416c28SMatti Vaittinen 	tmp = gts->max_scale;
94438416c28SMatti Vaittinen 
94538416c28SMatti Vaittinen 	do_div(tmp, total_gain);
94638416c28SMatti Vaittinen 
94738416c28SMatti Vaittinen 	*scale = tmp;
94838416c28SMatti Vaittinen 
94938416c28SMatti Vaittinen 	return 0;
95038416c28SMatti Vaittinen }
95138416c28SMatti Vaittinen 
95238416c28SMatti Vaittinen /**
95338416c28SMatti Vaittinen  * iio_gts_get_scale - get scale based on integration time and HW-gain
95438416c28SMatti Vaittinen  * @gts:	Gain time scale descriptor
95538416c28SMatti Vaittinen  * @gain:	HW-gain for which the scale is computed
95638416c28SMatti Vaittinen  * @time:	Integration time for which the scale is computed
95738416c28SMatti Vaittinen  * @scale_int:	Integral part of the scale (typically val1)
95838416c28SMatti Vaittinen  * @scale_nano:	Fractional part of the scale (nano or ppb)
95938416c28SMatti Vaittinen  *
96038416c28SMatti Vaittinen  * Compute scale matching the integration time and HW-gain given as parameter.
96138416c28SMatti Vaittinen  *
96238416c28SMatti Vaittinen  * Return: 0 on success.
96338416c28SMatti Vaittinen  */
iio_gts_get_scale(struct iio_gts * gts,int gain,int time,int * scale_int,int * scale_nano)96438416c28SMatti Vaittinen int iio_gts_get_scale(struct iio_gts *gts, int gain, int time, int *scale_int,
96538416c28SMatti Vaittinen 		      int *scale_nano)
96638416c28SMatti Vaittinen {
96738416c28SMatti Vaittinen 	u64 lin_scale;
96838416c28SMatti Vaittinen 	int ret;
96938416c28SMatti Vaittinen 
97038416c28SMatti Vaittinen 	ret = iio_gts_get_scale_linear(gts, gain, time, &lin_scale);
97138416c28SMatti Vaittinen 	if (ret)
97238416c28SMatti Vaittinen 		return ret;
97338416c28SMatti Vaittinen 
97438416c28SMatti Vaittinen 	return iio_gts_delinearize(lin_scale, NANO, scale_int, scale_nano);
97538416c28SMatti Vaittinen }
97638416c28SMatti Vaittinen EXPORT_SYMBOL_NS_GPL(iio_gts_get_scale, IIO_GTS_HELPER);
97738416c28SMatti Vaittinen 
97838416c28SMatti Vaittinen /**
97938416c28SMatti Vaittinen  * iio_gts_find_new_gain_sel_by_old_gain_time - compensate for time change
98038416c28SMatti Vaittinen  * @gts:		Gain time scale descriptor
98138416c28SMatti Vaittinen  * @old_gain:		Previously set gain
98238416c28SMatti Vaittinen  * @old_time_sel:	Selector corresponding previously set time
98338416c28SMatti Vaittinen  * @new_time_sel:	Selector corresponding new time to be set
98438416c28SMatti Vaittinen  * @new_gain:		Pointer to value where new gain is to be written
98538416c28SMatti Vaittinen  *
98638416c28SMatti Vaittinen  * We may want to mitigate the scale change caused by setting a new integration
98738416c28SMatti Vaittinen  * time (for a light sensor) by also updating the (HW)gain. This helper computes
98838416c28SMatti Vaittinen  * new gain value to maintain the scale with new integration time.
98938416c28SMatti Vaittinen  *
99038416c28SMatti Vaittinen  * Return: 0 if an exactly matching supported new gain was found. When a
99138416c28SMatti Vaittinen  * non-zero value is returned, the @new_gain will be set to a negative or
99238416c28SMatti Vaittinen  * positive value. The negative value means that no gain could be computed.
99338416c28SMatti Vaittinen  * Positive value will be the "best possible new gain there could be". There
99438416c28SMatti Vaittinen  * can be two reasons why finding the "best possible" new gain is not deemed
99538416c28SMatti Vaittinen  * successful. 1) This new value cannot be supported by the hardware. 2) The new
99638416c28SMatti Vaittinen  * gain required to maintain the scale would not be an integer. In this case,
99738416c28SMatti Vaittinen  * the "best possible" new gain will be a floored optimal gain, which may or
99838416c28SMatti Vaittinen  * may not be supported by the hardware.
99938416c28SMatti Vaittinen  */
iio_gts_find_new_gain_sel_by_old_gain_time(struct iio_gts * gts,int old_gain,int old_time_sel,int new_time_sel,int * new_gain)100038416c28SMatti Vaittinen int iio_gts_find_new_gain_sel_by_old_gain_time(struct iio_gts *gts,
100138416c28SMatti Vaittinen 					       int old_gain, int old_time_sel,
100238416c28SMatti Vaittinen 					       int new_time_sel, int *new_gain)
100338416c28SMatti Vaittinen {
100438416c28SMatti Vaittinen 	const struct iio_itime_sel_mul *itime_old, *itime_new;
100538416c28SMatti Vaittinen 	u64 scale;
100638416c28SMatti Vaittinen 	int ret;
100738416c28SMatti Vaittinen 
100838416c28SMatti Vaittinen 	*new_gain = -1;
100938416c28SMatti Vaittinen 
101038416c28SMatti Vaittinen 	itime_old = iio_gts_find_itime_by_sel(gts, old_time_sel);
101138416c28SMatti Vaittinen 	if (!itime_old)
101238416c28SMatti Vaittinen 		return -EINVAL;
101338416c28SMatti Vaittinen 
101438416c28SMatti Vaittinen 	itime_new = iio_gts_find_itime_by_sel(gts, new_time_sel);
101538416c28SMatti Vaittinen 	if (!itime_new)
101638416c28SMatti Vaittinen 		return -EINVAL;
101738416c28SMatti Vaittinen 
101838416c28SMatti Vaittinen 	ret = iio_gts_get_scale_linear(gts, old_gain, itime_old->time_us,
101938416c28SMatti Vaittinen 				       &scale);
102038416c28SMatti Vaittinen 	if (ret)
102138416c28SMatti Vaittinen 		return ret;
102238416c28SMatti Vaittinen 
102338416c28SMatti Vaittinen 	ret = gain_get_scale_fraction(gts->max_scale, scale, itime_new->mul,
102438416c28SMatti Vaittinen 				      new_gain);
102538416c28SMatti Vaittinen 	if (ret)
102638416c28SMatti Vaittinen 		return ret;
102738416c28SMatti Vaittinen 
102838416c28SMatti Vaittinen 	if (!iio_gts_valid_gain(gts, *new_gain))
102938416c28SMatti Vaittinen 		return -EINVAL;
103038416c28SMatti Vaittinen 
103138416c28SMatti Vaittinen 	return 0;
103238416c28SMatti Vaittinen }
103338416c28SMatti Vaittinen EXPORT_SYMBOL_NS_GPL(iio_gts_find_new_gain_sel_by_old_gain_time, IIO_GTS_HELPER);
103438416c28SMatti Vaittinen 
103538416c28SMatti Vaittinen /**
103638416c28SMatti Vaittinen  * iio_gts_find_new_gain_by_old_gain_time - compensate for time change
103738416c28SMatti Vaittinen  * @gts:		Gain time scale descriptor
103838416c28SMatti Vaittinen  * @old_gain:		Previously set gain
103938416c28SMatti Vaittinen  * @old_time:		Selector corresponding previously set time
104038416c28SMatti Vaittinen  * @new_time:		Selector corresponding new time to be set
104138416c28SMatti Vaittinen  * @new_gain:		Pointer to value where new gain is to be written
104238416c28SMatti Vaittinen  *
104338416c28SMatti Vaittinen  * We may want to mitigate the scale change caused by setting a new integration
104438416c28SMatti Vaittinen  * time (for a light sensor) by also updating the (HW)gain. This helper computes
104538416c28SMatti Vaittinen  * new gain value to maintain the scale with new integration time.
104638416c28SMatti Vaittinen  *
104738416c28SMatti Vaittinen  * Return: 0 if an exactly matching supported new gain was found. When a
104838416c28SMatti Vaittinen  * non-zero value is returned, the @new_gain will be set to a negative or
104938416c28SMatti Vaittinen  * positive value. The negative value means that no gain could be computed.
105038416c28SMatti Vaittinen  * Positive value will be the "best possible new gain there could be". There
105138416c28SMatti Vaittinen  * can be two reasons why finding the "best possible" new gain is not deemed
105238416c28SMatti Vaittinen  * successful. 1) This new value cannot be supported by the hardware. 2) The new
105338416c28SMatti Vaittinen  * gain required to maintain the scale would not be an integer. In this case,
105438416c28SMatti Vaittinen  * the "best possible" new gain will be a floored optimal gain, which may or
105538416c28SMatti Vaittinen  * may not be supported by the hardware.
105638416c28SMatti Vaittinen  */
iio_gts_find_new_gain_by_old_gain_time(struct iio_gts * gts,int old_gain,int old_time,int new_time,int * new_gain)105738416c28SMatti Vaittinen int iio_gts_find_new_gain_by_old_gain_time(struct iio_gts *gts, int old_gain,
105838416c28SMatti Vaittinen 					   int old_time, int new_time,
105938416c28SMatti Vaittinen 					   int *new_gain)
106038416c28SMatti Vaittinen {
106138416c28SMatti Vaittinen 	const struct iio_itime_sel_mul *itime_new;
106238416c28SMatti Vaittinen 	u64 scale;
106338416c28SMatti Vaittinen 	int ret;
106438416c28SMatti Vaittinen 
106538416c28SMatti Vaittinen 	*new_gain = -1;
106638416c28SMatti Vaittinen 
106738416c28SMatti Vaittinen 	itime_new = iio_gts_find_itime_by_time(gts, new_time);
106838416c28SMatti Vaittinen 	if (!itime_new)
106938416c28SMatti Vaittinen 		return -EINVAL;
107038416c28SMatti Vaittinen 
107138416c28SMatti Vaittinen 	ret = iio_gts_get_scale_linear(gts, old_gain, old_time, &scale);
107238416c28SMatti Vaittinen 	if (ret)
107338416c28SMatti Vaittinen 		return ret;
107438416c28SMatti Vaittinen 
107538416c28SMatti Vaittinen 	ret = gain_get_scale_fraction(gts->max_scale, scale, itime_new->mul,
107638416c28SMatti Vaittinen 				      new_gain);
107738416c28SMatti Vaittinen 	if (ret)
107838416c28SMatti Vaittinen 		return ret;
107938416c28SMatti Vaittinen 
108038416c28SMatti Vaittinen 	if (!iio_gts_valid_gain(gts, *new_gain))
108138416c28SMatti Vaittinen 		return -EINVAL;
108238416c28SMatti Vaittinen 
108338416c28SMatti Vaittinen 	return 0;
108438416c28SMatti Vaittinen }
108538416c28SMatti Vaittinen EXPORT_SYMBOL_NS_GPL(iio_gts_find_new_gain_by_old_gain_time, IIO_GTS_HELPER);
108638416c28SMatti Vaittinen 
108738416c28SMatti Vaittinen MODULE_LICENSE("GPL");
108838416c28SMatti Vaittinen MODULE_AUTHOR("Matti Vaittinen <mazziesaccount@gmail.com>");
108938416c28SMatti Vaittinen MODULE_DESCRIPTION("IIO light sensor gain-time-scale helpers");
1090