]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
iio: dac: ds4424: support per-variant output range limits
authorOleksij Rempel <o.rempel@pengutronix.de>
Tue, 10 Feb 2026 13:51:07 +0000 (14:51 +0100)
committerJonathan Cameron <Jonathan.Cameron@huawei.com>
Sun, 22 Mar 2026 12:43:48 +0000 (12:43 +0000)
The DS4402/DS4404 variants operate with a 5-bit resolution (31 steps),
whereas the DS4422/DS4424 support 7-bit (127 steps).

Previously, the driver enforced a hardcoded 7-bit mask (DS4424_DAC_MASK)
for all variants. This allowed users to write values exceeding the 5-bit
range to DS4402/DS4404 devices, resulting in silent truncation or
undefined behavior.

Add a `result_mask` field to the chip_info structure to define the valid
data range for each variant. Use this mask to:
1. Correctly mask register values in read_raw().
2. Return -EINVAL in write_raw() if the input value exceeds the
   variant's capabilities.

Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
drivers/iio/dac/ds4424.c

index f88e92927bf2a1b108c52e0ebd132d887bc76b06..4eff052c2b6195283b31428c14a07f76323c8338 100644 (file)
@@ -22,6 +22,7 @@
 #define DS4424_MAX_DAC_CHANNELS                4
 
 #define DS4424_DAC_MASK                        GENMASK(6, 0)
+#define DS4404_DAC_MASK                        GENMASK(4, 0)
 #define DS4424_DAC_SOURCE              BIT(7)
 
 #define DS4424_DAC_ADDR(chan)   ((chan) + 0xf8)
 
 struct ds4424_chip_info {
        const char *name;
+       u8 result_mask;
        u8 num_channels;
 };
 
 static const struct ds4424_chip_info ds4402_info = {
        .name = "ds4402",
+       .result_mask = DS4404_DAC_MASK,
        .num_channels = DS4422_MAX_DAC_CHANNELS,
 };
 
 static const struct ds4424_chip_info ds4404_info = {
        .name = "ds4404",
+       .result_mask = DS4404_DAC_MASK,
        .num_channels = DS4424_MAX_DAC_CHANNELS,
 };
 
 static const struct ds4424_chip_info ds4422_info = {
        .name = "ds4422",
+       .result_mask = DS4424_DAC_MASK,
        .num_channels = DS4422_MAX_DAC_CHANNELS,
 };
 
 static const struct ds4424_chip_info ds4424_info = {
        .name = "ds4424",
+       .result_mask = DS4424_DAC_MASK,
        .num_channels = DS4424_MAX_DAC_CHANNELS,
 };
 
@@ -65,6 +71,7 @@ struct ds4424_data {
        uint8_t save[DS4424_MAX_DAC_CHANNELS];
        struct regulator *vcc_reg;
        uint8_t raw[DS4424_MAX_DAC_CHANNELS];
+       const struct ds4424_chip_info *chip_info;
 };
 
 static const struct iio_chan_spec ds4424_channels[] = {
@@ -115,6 +122,7 @@ static int ds4424_read_raw(struct iio_dev *indio_dev,
                           struct iio_chan_spec const *chan,
                           int *val, int *val2, long mask)
 {
+       struct ds4424_data *data = iio_priv(indio_dev);
        int ret, regval;
 
        switch (mask) {
@@ -127,7 +135,7 @@ static int ds4424_read_raw(struct iio_dev *indio_dev,
                        return ret;
                }
 
-               *val = regval & DS4424_DAC_MASK;
+               *val = regval & data->chip_info->result_mask;
                if (!(regval & DS4424_DAC_SOURCE))
                        *val = -*val;
 
@@ -142,6 +150,7 @@ static int ds4424_write_raw(struct iio_dev *indio_dev,
                             struct iio_chan_spec const *chan,
                             int val, int val2, long mask)
 {
+       struct ds4424_data *data = iio_priv(indio_dev);
        unsigned int abs_val;
 
        if (val2 != 0)
@@ -150,7 +159,7 @@ static int ds4424_write_raw(struct iio_dev *indio_dev,
        switch (mask) {
        case IIO_CHAN_INFO_RAW:
                abs_val = abs(val);
-               if (abs_val > DS4424_DAC_MASK)
+               if (abs_val > data->chip_info->result_mask)
                        return -EINVAL;
 
                /*
@@ -241,6 +250,7 @@ static int ds4424_probe(struct i2c_client *client)
        i2c_set_clientdata(client, indio_dev);
        data->client = client;
        indio_dev->name = chip_info->name;
+       data->chip_info = chip_info;
 
        data->vcc_reg = devm_regulator_get(&client->dev, "vcc");
        if (IS_ERR(data->vcc_reg))