Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

iio: light: Add gain-time-scale helpers

Some light sensors can adjust both the HW-gain and integration time.
There are cases where adjusting the integration time has similar impact
to the scale of the reported values as gain setting has.

IIO users do typically expect to handle scale by a single writable 'scale'
entry. Driver should then adjust the gain/time accordingly.

It however is difficult for a driver to know whether it should change
gain or integration time to meet the requested scale. Usually it is
preferred to have longer integration time which usually improves
accuracy, but there may be use-cases where long measurement times can be
an issue. Thus it can be preferable to allow also changing the
integration time - but mitigate the scale impact by also changing the gain
underneath. Eg, if integration time change doubles the measured values,
the driver can reduce the HW-gain to half.

The theory of the computations of gain-time-scale is simple. However,
some people (undersigned) got that implemented wrong for more than once.

Add some gain-time-scale helpers in order to not dublicate errors in all
drivers needing these computations.

Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
Link: https://lore.kernel.org/r/268d418e7cffcdaa2ece6738478bbc57692c213e.1680263956.git.mazziesaccount@gmail.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

Matti Vaittinen and committed by
Jonathan Cameron
38416c28 b27f0b40

+1287
+3
drivers/iio/Kconfig
··· 30 30 (e.g. software triggers). For more info see 31 31 Documentation/iio/iio_configfs.rst. 32 32 33 + config IIO_GTS_HELPER 34 + tristate 35 + 33 36 config IIO_TRIGGER 34 37 bool "Enable triggered sampling support" 35 38 help
+1
drivers/iio/Makefile
··· 9 9 industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o 10 10 11 11 obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o 12 + obj-$(CONFIG_IIO_GTS_HELPER) += industrialio-gts-helper.o 12 13 obj-$(CONFIG_IIO_SW_DEVICE) += industrialio-sw-device.o 13 14 obj-$(CONFIG_IIO_SW_TRIGGER) += industrialio-sw-trigger.o 14 15 obj-$(CONFIG_IIO_TRIGGERED_EVENT) += industrialio-triggered-event.o
+1077
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");
+206
include/linux/iio/iio-gts-helper.h
··· 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 + #ifndef __IIO_GTS_HELPER__ 8 + #define __IIO_GTS_HELPER__ 9 + 10 + #include <linux/types.h> 11 + 12 + struct device; 13 + 14 + /** 15 + * struct iio_gain_sel_pair - gain - selector values 16 + * 17 + * In many cases devices like light sensors allow setting signal amplification 18 + * (gain) using a register interface. This structure describes amplification 19 + * and corresponding selector (register value) 20 + * 21 + * @gain: Gain (multiplication) value. Gain must be positive, negative 22 + * values are reserved for error handling. 23 + * @sel: Selector (usually register value) used to indicate this gain. 24 + * NOTE: Only selectors >= 0 supported. 25 + */ 26 + struct iio_gain_sel_pair { 27 + int gain; 28 + int sel; 29 + }; 30 + 31 + /** 32 + * struct iio_itime_sel_mul - integration time description 33 + * 34 + * In many cases devices like light sensors allow setting the duration of 35 + * collecting data. Typically this duration has also an impact to the magnitude 36 + * of measured values (gain). This structure describes the relation of 37 + * integration time and amplification as well as corresponding selector 38 + * (register value). 39 + * 40 + * An example could be a sensor allowing 50, 100, 200 and 400 mS times. The 41 + * respective multiplication values could be 50 mS => 1, 100 mS => 2, 42 + * 200 mS => 4 and 400 mS => 8 assuming the impact of integration time would be 43 + * linear in a way that when collecting data for 50 mS caused value X, doubling 44 + * the data collection time caused value 2X etc. 45 + * 46 + * @time_us: Integration time in microseconds. Time values must be positive, 47 + * negative values are reserved for error handling. 48 + * @sel: Selector (usually register value) used to indicate this time 49 + * NOTE: Only selectors >= 0 supported. 50 + * @mul: Multiplication to the values caused by this time. 51 + * NOTE: Only multipliers > 0 supported. 52 + */ 53 + struct iio_itime_sel_mul { 54 + int time_us; 55 + int sel; 56 + int mul; 57 + }; 58 + 59 + struct iio_gts { 60 + u64 max_scale; 61 + const struct iio_gain_sel_pair *hwgain_table; 62 + int num_hwgain; 63 + const struct iio_itime_sel_mul *itime_table; 64 + int num_itime; 65 + int **per_time_avail_scale_tables; 66 + int *avail_all_scales_table; 67 + int num_avail_all_scales; 68 + int *avail_time_tables; 69 + int num_avail_time_tables; 70 + }; 71 + 72 + #define GAIN_SCALE_GAIN(_gain, _sel) \ 73 + { \ 74 + .gain = (_gain), \ 75 + .sel = (_sel), \ 76 + } 77 + 78 + #define GAIN_SCALE_ITIME_US(_itime, _sel, _mul) \ 79 + { \ 80 + .time_us = (_itime), \ 81 + .sel = (_sel), \ 82 + .mul = (_mul), \ 83 + } 84 + 85 + static inline const struct iio_itime_sel_mul * 86 + iio_gts_find_itime_by_time(struct iio_gts *gts, int time) 87 + { 88 + int i; 89 + 90 + if (!gts->num_itime) 91 + return NULL; 92 + 93 + for (i = 0; i < gts->num_itime; i++) 94 + if (gts->itime_table[i].time_us == time) 95 + return &gts->itime_table[i]; 96 + 97 + return NULL; 98 + } 99 + 100 + static inline const struct iio_itime_sel_mul * 101 + iio_gts_find_itime_by_sel(struct iio_gts *gts, int sel) 102 + { 103 + int i; 104 + 105 + for (i = 0; i < gts->num_itime; i++) 106 + if (gts->itime_table[i].sel == sel) 107 + return &gts->itime_table[i]; 108 + 109 + return NULL; 110 + } 111 + 112 + int devm_iio_init_iio_gts(struct device *dev, int max_scale_int, int max_scale_nano, 113 + const struct iio_gain_sel_pair *gain_tbl, int num_gain, 114 + const struct iio_itime_sel_mul *tim_tbl, int num_times, 115 + struct iio_gts *gts); 116 + /** 117 + * iio_gts_find_int_time_by_sel - find integration time matching a selector 118 + * @gts: Gain time scale descriptor 119 + * @sel: selector for which matching integration time is searched for 120 + * 121 + * Return: integration time matching given selector or -EINVAL if 122 + * integration time was not found. 123 + */ 124 + static inline int iio_gts_find_int_time_by_sel(struct iio_gts *gts, int sel) 125 + { 126 + const struct iio_itime_sel_mul *itime; 127 + 128 + itime = iio_gts_find_itime_by_sel(gts, sel); 129 + if (!itime) 130 + return -EINVAL; 131 + 132 + return itime->time_us; 133 + } 134 + 135 + /** 136 + * iio_gts_find_sel_by_int_time - find selector matching integration time 137 + * @gts: Gain time scale descriptor 138 + * @gain: HW-gain for which matching selector is searched for 139 + * 140 + * Return: a selector matching given integration time or -EINVAL if 141 + * selector was not found. 142 + */ 143 + static inline int iio_gts_find_sel_by_int_time(struct iio_gts *gts, int time) 144 + { 145 + const struct iio_itime_sel_mul *itime; 146 + 147 + itime = iio_gts_find_itime_by_time(gts, time); 148 + if (!itime) 149 + return -EINVAL; 150 + 151 + return itime->sel; 152 + } 153 + 154 + /** 155 + * iio_gts_valid_time - check if given integration time is valid 156 + * @gts: Gain time scale descriptor 157 + * @time_us: Integration time to check 158 + * 159 + * Return: True if given time is supported by device. False if not. 160 + */ 161 + static inline bool iio_gts_valid_time(struct iio_gts *gts, int time_us) 162 + { 163 + return iio_gts_find_itime_by_time(gts, time_us) != NULL; 164 + } 165 + 166 + int iio_gts_find_sel_by_gain(struct iio_gts *gts, int gain); 167 + 168 + /** 169 + * iio_gts_valid_gain - check if given HW-gain is valid 170 + * @gts: Gain time scale descriptor 171 + * @gain: HW-gain to check 172 + * 173 + * Return: True if given time is supported by device. False if not. 174 + */ 175 + static inline bool iio_gts_valid_gain(struct iio_gts *gts, int gain) 176 + { 177 + return iio_gts_find_sel_by_gain(gts, gain) >= 0; 178 + } 179 + 180 + int iio_find_closest_gain_low(struct iio_gts *gts, int gain, bool *in_range); 181 + int iio_gts_find_gain_by_sel(struct iio_gts *gts, int sel); 182 + int iio_gts_get_min_gain(struct iio_gts *gts); 183 + int iio_gts_find_int_time_by_sel(struct iio_gts *gts, int sel); 184 + int iio_gts_find_sel_by_int_time(struct iio_gts *gts, int time); 185 + 186 + int iio_gts_total_gain_to_scale(struct iio_gts *gts, int total_gain, 187 + int *scale_int, int *scale_nano); 188 + int iio_gts_find_gain_sel_for_scale_using_time(struct iio_gts *gts, int time_sel, 189 + int scale_int, int scale_nano, 190 + int *gain_sel); 191 + int iio_gts_get_scale(struct iio_gts *gts, int gain, int time, int *scale_int, 192 + int *scale_nano); 193 + int iio_gts_find_new_gain_sel_by_old_gain_time(struct iio_gts *gts, 194 + int old_gain, int old_time_sel, 195 + int new_time_sel, int *new_gain); 196 + int iio_gts_find_new_gain_by_old_gain_time(struct iio_gts *gts, int old_gain, 197 + int old_time, int new_time, 198 + int *new_gain); 199 + int iio_gts_avail_times(struct iio_gts *gts, const int **vals, int *type, 200 + int *length); 201 + int iio_gts_all_avail_scales(struct iio_gts *gts, const int **vals, int *type, 202 + int *length); 203 + int iio_gts_avail_scales_for_time(struct iio_gts *gts, int time, 204 + const int **vals, int *type, int *length); 205 + 206 + #endif