From: Nikhil Gautam <nikhilgtr@gmail.com>
To: linux-iio@vger.kernel.org
Cc: jic23@kernel.org, dlechner@baylibre.com,
Nikhil Gautam <nikhilgtr@gmail.com>
Subject: [PATCH v4 3/3] iio: dac: mcp4821: add configurable gain and fix scale handling
Date: Mon, 13 Apr 2026 15:14:49 +0530 [thread overview]
Message-ID: <20260413094449.18837-4-nikhilgtr@gmail.com> (raw)
In-Reply-To: <20260413094449.18837-1-nikhilgtr@gmail.com>
Add support for configuring the DAC gain using the GA bit and
update scale handling to follow the IIO ABI.
The MCP4821 supports two gain settings:
- 1x gain → 2.048V full-scale
- 2x gain → 4.096V full-scale
Scale is now exposed via IIO_CHAN_INFO_SCALE and reflects the
selected gain. The driver returns scale using
IIO_VAL_FRACTIONAL_LOG2 and provides scale_available with the
corresponding values.
Writes to the scale attribute are validated and mapped to the
appropriate gain setting. Only supported scale values are accepted,
ensuring consistency between scale and scale_available.
Signed-off-by: Nikhil Gautam <nikhilgtr@gmail.com>
---
v4:
- Split changes into separate patches
- Fix scale handling to comply with IIO ABI
- Use IIO_VAL_FRACTIONAL_LOG2 for scale and scale_available
- Ensure scale_available matches scale
- Handle sysfs write inputs correctly (INT+MICRO)
- Reject invalid scale values
v3:
- Restore NULL check in indio_dev allocation
v2:
- Use IIO_CHAN_INFO_SCALE instead of CALIBSCALE
- Fix error handling and cleanup
---
drivers/iio/dac/mcp4821.c | 103 ++++++++++++++++++++++++++++++--------
1 file changed, 83 insertions(+), 20 deletions(-)
diff --git a/drivers/iio/dac/mcp4821.c b/drivers/iio/dac/mcp4821.c
index 6b732db1cf49..db659b5be8f8 100644
--- a/drivers/iio/dac/mcp4821.c
+++ b/drivers/iio/dac/mcp4821.c
@@ -12,7 +12,6 @@
* MCP48x2: https://ww1.microchip.com/downloads/en/DeviceDoc/20002249B.pdf
*
* TODO:
- * - Configurable gain
* - Regulator control
*/
@@ -26,10 +25,30 @@
#include <linux/unaligned.h>
#define MCP4821_ACTIVE_MODE BIT(12)
+#define MCP4821_GAIN_ENABLE BIT(13)
#define MCP4802_SECOND_CHAN BIT(15)
-/* DAC uses an internal Voltage reference of 4.096V at a gain of 2x */
-#define MCP4821_2X_GAIN_VREF_MV 4096
+/* DAC uses an internal Voltage reference of 2.048V */
+#define MCP4821_VREF_MV 2048
+
+/*
+ * MCP48xx DAC output:
+ *
+ * Vout = (Vref * D / 2^N) * G
+ *
+ * where:
+ * - Vref = 2.048V (internal reference)
+ * - N = DAC resolution (12 bits for MCP4821)
+ * - G = gain selection:
+ * 1x when GA bit = 1
+ * 2x when GA bit = 0 (default)
+ *
+ * Therefore full-scale voltage is:
+ * - 1x gain: 2.048V
+ * - 2x gain: 4.096V
+ *
+ * Scale = Vfull-scale / 2^N
+ */
enum mcp4821_supported_device_ids {
ID_MCP4801,
@@ -43,6 +62,7 @@ enum mcp4821_supported_device_ids {
struct mcp4821_state {
struct spi_device *spi;
u16 dac_value[2];
+ int gain;
};
struct mcp4821_chip_info {
@@ -57,6 +77,7 @@ struct mcp4821_chip_info {
.channel = (channel_id), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \
.scan_type = { \
.realbits = (resolution), \
.shift = 12 - (resolution), \
@@ -122,8 +143,9 @@ static int mcp4821_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_RAW:
*val = state->dac_value[chan->channel];
return IIO_VAL_INT;
+
case IIO_CHAN_INFO_SCALE:
- *val = MCP4821_2X_GAIN_VREF_MV;
+ *val = MCP4821_VREF_MV * state->gain;
*val2 = chan->scan_type.realbits;
return IIO_VAL_FRACTIONAL_LOG2;
default:
@@ -140,34 +162,72 @@ static int mcp4821_write_raw(struct iio_dev *indio_dev,
__be16 write_buffer;
int ret;
- if (val2 != 0)
- return -EINVAL;
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
- if (val < 0 || val >= BIT(chan->scan_type.realbits))
- return -EINVAL;
+ if (val2 != 0)
+ return -EINVAL;
- if (mask != IIO_CHAN_INFO_RAW)
- return -EINVAL;
+ if (val < 0 || val >= BIT(chan->scan_type.realbits))
+ return -EINVAL;
+
+ write_val = MCP4821_ACTIVE_MODE | val << chan->scan_type.shift;
+ if (chan->channel)
+ write_val |= MCP4802_SECOND_CHAN;
- write_val = MCP4821_ACTIVE_MODE | val << chan->scan_type.shift;
- if (chan->channel)
- write_val |= MCP4802_SECOND_CHAN;
+ /* GA bit = 1 -> 1x gain */
+ if (state->gain == 1)
+ write_val |= MCP4821_GAIN_ENABLE;
- write_buffer = cpu_to_be16(write_val);
- ret = spi_write(state->spi, &write_buffer, sizeof(write_buffer));
- if (ret) {
- dev_err(&state->spi->dev, "Failed to write to device: %d", ret);
- return ret;
+ write_buffer = cpu_to_be16(write_val);
+ ret = spi_write(state->spi, &write_buffer, sizeof(write_buffer));
+ if (ret) {
+ dev_err(&state->spi->dev, "Failed to write to device: %d", ret);
+ return ret;
+ }
+
+ state->dac_value[chan->channel] = val;
+ return 0;
+
+ case IIO_CHAN_INFO_SCALE:
+
+ if (val == 0 && val2 == 500000)
+ state->gain = 1;
+ else if (val == 1 && val2 == 0)
+ state->gain = 2;
+ else
+ return -EINVAL;
+ return 0;
+
+ default:
+ return -EINVAL;
}
+}
- state->dac_value[chan->channel] = val;
+static const int mcp4821_gain_avail[] = {
+ MCP4821_VREF_MV, 12,
+ MCP4821_VREF_MV * 2, 12,};
- return 0;
+static int mcp4821_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long info)
+{
+ switch (info) {
+ case IIO_CHAN_INFO_SCALE:
+ *vals = mcp4821_gain_avail;
+ *type = IIO_VAL_FRACTIONAL_LOG2;
+ *length = ARRAY_SIZE(mcp4821_gain_avail);
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
}
static const struct iio_info mcp4821_info = {
.read_raw = &mcp4821_read_raw,
.write_raw = &mcp4821_write_raw,
+ .read_avail = &mcp4821_read_avail,
};
static int mcp4821_probe(struct spi_device *spi)
@@ -183,6 +243,9 @@ static int mcp4821_probe(struct spi_device *spi)
state = iio_priv(indio_dev);
state->spi = spi;
+ /* default gain is 2x as GA bit is active low*/
+ state->gain = 2;
+
info = spi_get_device_match_data(spi);
indio_dev->name = info->name;
indio_dev->info = &mcp4821_info;
--
2.43.0
next prev parent reply other threads:[~2026-04-13 9:45 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-13 9:44 [PATCH v4 0/3] iio: dac: mcp4821: add gain support and fix scale handling Nikhil Gautam
2026-04-13 9:44 ` [PATCH v4 1/3] iio: dac: mcp4821: fix spelling mistake in enum name Nikhil Gautam
2026-04-13 9:44 ` [PATCH v4 2/3] iio: dac: mcp4821: move state initialization outside switch Nikhil Gautam
2026-04-13 14:33 ` David Lechner
2026-04-13 9:44 ` Nikhil Gautam [this message]
2026-04-13 14:46 ` [PATCH v4 3/3] iio: dac: mcp4821: add configurable gain and fix scale handling David Lechner
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260413094449.18837-4-nikhilgtr@gmail.com \
--to=nikhilgtr@gmail.com \
--cc=dlechner@baylibre.com \
--cc=jic23@kernel.org \
--cc=linux-iio@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox