iio: isl29018: Support fractional ALS scaling.
authorBryan Freed <bfreed@chromium.org>
Wed, 5 Sep 2012 19:55:00 +0000 (20:55 +0100)
committerJonathan Cameron <jic23@kernel.org>
Sat, 8 Sep 2012 09:25:10 +0000 (10:25 +0100)
The Industrial IO framework supports scaling ADC values by fractions,
but most drivers default to using whole numbers.
This change turns on fractional scaling in the isl29018 driver.

Signed-off-by: Bryan Freed <bfreed@chromium.org>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
drivers/staging/iio/light/isl29018.c

index 31d22f5591ca665089634a58ff687266127fd723..6ee5567d981314acf81a1a1ea3f825aafcb96f94 100644 (file)
@@ -63,6 +63,7 @@ struct isl29018_chip {
        struct regmap           *regmap;
        struct mutex            lock;
        unsigned int            lux_scale;
+       unsigned int            lux_uscale;
        unsigned int            range;
        unsigned int            adc_bit;
        int                     prox_scheme;
@@ -145,13 +146,22 @@ static int isl29018_read_sensor_input(struct isl29018_chip *chip, int mode)
 static int isl29018_read_lux(struct isl29018_chip *chip, int *lux)
 {
        int lux_data;
+       unsigned int data_x_range, lux_unshifted;
 
        lux_data = isl29018_read_sensor_input(chip, COMMMAND1_OPMODE_ALS_ONCE);
 
        if (lux_data < 0)
                return lux_data;
 
-       *lux = (lux_data * chip->range * chip->lux_scale) >> chip->adc_bit;
+       /* To support fractional scaling, separate the unshifted lux
+        * into two calculations: int scaling and micro-scaling.
+        * lux_uscale ranges from 0-999999, so about 20 bits.  Split
+        * the /1,000,000 in two to reduce the risk of over/underflow.
+        */
+       data_x_range = lux_data * chip->range;
+       lux_unshifted = data_x_range * chip->lux_scale;
+       lux_unshifted += data_x_range / 1000 * chip->lux_uscale / 1000;
+       *lux = lux_unshifted >> chip->adc_bit;
 
        return 0;
 }
@@ -339,6 +349,8 @@ static int isl29018_write_raw(struct iio_dev *indio_dev,
        mutex_lock(&chip->lock);
        if (mask == IIO_CHAN_INFO_CALIBSCALE && chan->type == IIO_LIGHT) {
                chip->lux_scale = val;
+               /* With no write_raw_get_fmt(), val2 is a MICRO fraction. */
+               chip->lux_uscale = val2;
                ret = 0;
        }
        mutex_unlock(&chip->lock);
@@ -379,7 +391,8 @@ static int isl29018_read_raw(struct iio_dev *indio_dev,
        case IIO_CHAN_INFO_CALIBSCALE:
                if (chan->type == IIO_LIGHT) {
                        *val = chip->lux_scale;
-                       ret = IIO_VAL_INT;
+                       *val2 = chip->lux_uscale;
+                       ret = IIO_VAL_INT_PLUS_MICRO;
                }
                break;
        default: