From: Janani Sunil <janani.sunil@analog.com>
To: "Nuno Sá" <nuno.sa@analog.com>,
"Michael Hennerich" <Michael.Hennerich@analog.com>,
"Jonathan Cameron" <jic23@kernel.org>,
"David Lechner" <dlechner@baylibre.com>,
"Andy Shevchenko" <andy@kernel.org>,
"Rob Herring" <robh@kernel.org>,
"Krzysztof Kozlowski" <krzk+dt@kernel.org>,
"Conor Dooley" <conor+dt@kernel.org>,
"Olivier Moysan" <olivier.moysan@foss.st.com>,
"Philipp Zabel" <p.zabel@pengutronix.de>,
"Linus Walleij" <linusw@kernel.org>,
"Bartosz Golaszewski" <brgl@kernel.org>,
"Jonathan Corbet" <corbet@lwn.net>,
"Shuah Khan" <skhan@linuxfoundation.org>,
"Michael Walle" <mwalle@kernel.org>,
"Randy Dunlap" <rdunlap@infradead.org>
Cc: linux@analog.com, linux-iio@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org,
jananisunil.dev@gmail.com,
"Uwe Kleine-König" <u.kleine-koenig@baylibre.com>,
"Janani Sunil" <janani.sunil@analog.com>,
"Jonathan Cameron" <jic23@kernel.org>
Subject: [PATCH v6 09/17] iio: adc: ad7768: Configure channel sampling profiles
Date: Fri, 4 Sep 2026 16:15:01 +0200 [thread overview]
Message-ID: <20260904-ad7768-driver-v6-9-e4378f946bfb@analog.com> (raw)
In-Reply-To: <20260904-ad7768-driver-v6-0-e4378f946bfb@analog.com>
At buffered capture setup, select the fastest compatible power mode for
the enabled channel rates. Group channels into the two hardware mode
profiles and program their decimation, mode selection, and data clock.
Signed-off-by: Janani Sunil <janani.sunil@analog.com>
---
drivers/iio/adc/ad7768.c | 162 +++++++++++++++++++++++++++++++++++++++--------
1 file changed, 135 insertions(+), 27 deletions(-)
diff --git a/drivers/iio/adc/ad7768.c b/drivers/iio/adc/ad7768.c
index 2020156eb6b6..868130ebcc8f 100644
--- a/drivers/iio/adc/ad7768.c
+++ b/drivers/iio/adc/ad7768.c
@@ -7,7 +7,9 @@
#include <linux/array_size.h>
#include <linux/bitfield.h>
+#include <linux/bitmap.h>
#include <linux/bitops.h>
+#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
@@ -16,6 +18,7 @@
#include <linux/math.h>
#include <linux/minmax.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
#include <linux/regmap.h>
@@ -33,7 +36,6 @@
#define AD7768_REG_CH_MODE(x) (0x01 + (x))
#define AD7768_CH_MODE_FILTER_TYPE_MSK BIT(3)
-#define AD7768_CH_MODE_FILTER_TYPE_WIDEBAND 0x0
#define AD7768_CH_MODE_DEC_RATE_MSK GENMASK(2, 0)
#define AD7768_REG_CH_MODE_SEL 0x03
@@ -104,6 +106,7 @@
#define AD7768_MAX_MCLK_FREQ_HZ (34 * HZ_PER_MHZ)
#define AD7768_MAX_FREQ_PER_MODE 6
#define AD7768_MAX_CHANNEL 8
+#define AD7768_NUM_CHANNEL_MODES 2
enum ad7768_clock_source {
AD7768_CLOCK_SOURCE_MCLK,
@@ -151,6 +154,8 @@ struct ad7768_chip_info {
struct ad7768_state {
struct regmap *regmap;
+ /* Protects device register access and configuration. */
+ struct mutex lock;
struct clk *mclk;
unsigned int datalines;
enum ad7768_clock_source clock_source;
@@ -204,6 +209,11 @@ static u8 ad7768_channel_mask(const struct ad7768_state *st, u8 ch)
return BIT(st->chip_info->chan_map[ch]);
}
+static u8 ad7768_channel_mode_mask(const struct ad7768_state *st, u8 ch)
+{
+ return BIT(ch) | ad7768_channel_mask(st, ch);
+}
+
static u8 ad7768_all_standby_mask(const struct ad7768_state *st)
{
return GENMASK(st->chip_info->num_channels - 1, 0);
@@ -460,14 +470,70 @@ static int ad7768_set_mode_decimation(struct ad7768_state *st,
freq_cfg->dec_rate));
}
-static int ad7768_update_scan_mode(struct iio_dev *indio_dev,
- const unsigned long *scan_mask)
+static int ad7768_set_lowest_noise_mode(struct ad7768_state *st,
+ const unsigned long *scan_mask)
+{
+ unsigned int mode_idx = ARRAY_SIZE(ad7768_power_modes);
+
+ /*
+ * The output data rate ranges overlap between the power modes. At a
+ * common ODR, the faster mode has lower noise, so prefer the fastest
+ * mode that supports every enabled channel.
+ */
+ while (mode_idx--) {
+ bool supported = true;
+ unsigned int channel;
+
+ for_each_set_bit(channel, scan_mask,
+ st->chip_info->num_channels) {
+ if (!ad7768_find_freq_config(st, mode_idx,
+ st->ch_freq[channel])) {
+ supported = false;
+ break;
+ }
+ }
+
+ if (supported)
+ return ad7768_set_power_mode(st, mode_idx);
+ }
+
+ return -EINVAL;
+}
+
+static int ad7768_find_matching_mode(const bool *mode_used,
+ const unsigned int *mode_freq,
+ unsigned int freq)
+{
+ for (unsigned int mode = 0; mode < AD7768_NUM_CHANNEL_MODES; mode++) {
+ if (!mode_used[mode] || mode_freq[mode] == freq)
+ return mode;
+ }
+
+ return -EINVAL;
+}
+
+static int ad7768_apply_channel_modes(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
{
+ unsigned int mode_freq[AD7768_NUM_CHANNEL_MODES] = { };
+ bool mode_used[AD7768_NUM_CHANNEL_MODES] = { };
struct ad7768_state *st = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
unsigned long channel_mask;
unsigned long standby_mask;
+ unsigned int max_freq;
+ unsigned int c;
int ret;
+ guard(mutex)(&st->lock);
+
+ ret = ad7768_set_lowest_noise_mode(st, scan_mask);
+ if (ret == -EINVAL)
+ return dev_err_probe(dev, ret,
+ "No power mode supports all ODRs\n");
+ if (ret)
+ return ret;
+
channel_mask = ad7768_all_standby_mask(st);
standby_mask = channel_mask & ~*scan_mask;
@@ -478,11 +544,66 @@ static int ad7768_update_scan_mode(struct iio_dev *indio_dev,
if (st->clock_source == AD7768_CLOCK_SOURCE_XTAL)
__clear_bit(st->chip_info->num_channels / 2, &standby_mask);
+ for_each_set_bit(c, scan_mask, st->chip_info->num_channels) {
+ unsigned int mask;
+ int mode;
+
+ mode = ad7768_find_matching_mode(mode_used, mode_freq,
+ st->ch_freq[c]);
+ if (mode < 0)
+ return dev_err_probe(dev, -EINVAL,
+ "Over %d channel modes required\n",
+ AD7768_NUM_CHANNEL_MODES);
+
+ mode_freq[mode] = st->ch_freq[c];
+ mode_used[mode] = true;
+
+ mask = ad7768_channel_mode_mask(st, c);
+ ret = regmap_assign_bits(st->regmap, AD7768_REG_CH_MODE_SEL,
+ mask, mode);
+ if (ret)
+ return ret;
+ }
+
ret = regmap_update_bits(st->regmap, AD7768_REG_CH_STANDBY,
channel_mask, standby_mask);
if (ret)
return ret;
+ max_freq = 0;
+ for (unsigned int mode = 0; mode < AD7768_NUM_CHANNEL_MODES; mode++) {
+ if (!mode_used[mode])
+ continue;
+
+ ret = ad7768_set_mode_decimation(st, mode_freq[mode], mode);
+ if (ret)
+ return ret;
+
+ ret = regmap_clear_bits(st->regmap, AD7768_REG_CH_MODE(mode),
+ AD7768_CH_MODE_FILTER_TYPE_MSK);
+ if (ret)
+ return ret;
+
+ max_freq = max(max_freq, mode_freq[mode]);
+ }
+
+ ret = ad7768_set_clk_divs(st, max_freq);
+ if (ret)
+ return ret;
+
+ return ad7768_sync(st);
+}
+
+static int ad7768_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = ad7768_apply_channel_modes(indio_dev, scan_mask);
+ if (ret)
+ return ret;
+
for (unsigned int c = 0; c < st->chip_info->num_channels; c++) {
if (test_bit(c, scan_mask))
ret = iio_backend_chan_enable(st->back, c);
@@ -639,11 +760,10 @@ static void ad7768_set_available_sampling_freqs(struct ad7768_state *st)
st->avail_freq[n_power_modes - 1].n_freqs--;
}
-static int ad7768_configure_capture(struct ad7768_state *st)
+static int ad7768_init_sampling_freqs(struct ad7768_state *st)
{
const struct ad7768_avail_freq *avail_freq;
unsigned int default_freq;
- unsigned int mode_config;
int ret;
ret = ad7768_set_power_mode(st, ARRAY_SIZE(ad7768_power_modes) - 1);
@@ -656,27 +776,7 @@ static int ad7768_configure_capture(struct ad7768_state *st)
channel < st->chip_info->num_channels; channel++)
st->ch_freq[channel] = default_freq;
- ret = ad7768_set_mode_decimation(st, default_freq, 0);
- if (ret)
- return ret;
-
- mode_config = FIELD_PREP(AD7768_CH_MODE_FILTER_TYPE_MSK,
- AD7768_CH_MODE_FILTER_TYPE_WIDEBAND);
- ret = regmap_update_bits(st->regmap, AD7768_REG_CH_MODE(0),
- AD7768_CH_MODE_FILTER_TYPE_MSK,
- mode_config);
- if (ret)
- return ret;
-
- ret = regmap_write(st->regmap, AD7768_REG_CH_MODE_SEL, 0);
- if (ret)
- return ret;
-
- ret = ad7768_set_clk_divs(st, default_freq);
- if (ret)
- return ret;
-
- return ad7768_sync(st);
+ return 0;
}
static int ad7768_parse_config(struct iio_dev *indio_dev,
@@ -798,7 +898,11 @@ static int ad7768_parse_config(struct iio_dev *indio_dev,
ad7768_set_available_sampling_freqs(st);
- return ad7768_configure_capture(st);
+ ret = ad7768_init_sampling_freqs(st);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to set power mode\n");
+
+ return 0;
}
static int ad7768_reset(struct ad7768_state *st)
@@ -954,6 +1058,10 @@ static int ad7768_probe(struct spi_device *spi)
st = iio_priv(indio_dev);
spi_set_drvdata(spi, st);
+ ret = devm_mutex_init(dev, &st->lock);
+ if (ret)
+ return ret;
+
st->chip_info = spi_get_device_match_data(spi);
if (!st->chip_info)
return dev_err_probe(dev, -ENODATA, "Failed to get match data\n");
--
2.43.0
next prev parent reply other threads:[~2026-09-04 14:16 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-04 14:14 [PATCH v6 00/17] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil
2026-09-04 14:14 ` [PATCH v6 01/17] iio: adc: adi-axi-adc: Initialize state mutex Janani Sunil
2026-09-04 14:14 ` [PATCH v6 02/17] dt-bindings: iio: adc: Add AD7768 Janani Sunil
2026-09-04 14:21 ` sashiko-bot
2026-09-04 17:08 ` Conor Dooley
2026-09-04 14:14 ` [PATCH v6 03/17] iio: backend: Add support for CRC Janani Sunil
2026-09-04 14:14 ` [PATCH v6 04/17] iio: adc: adi-axi-adc: " Janani Sunil
2026-09-04 14:24 ` sashiko-bot
2026-09-04 14:14 ` [PATCH v6 05/17] iio: adc: Add AD7768 and AD7768-4 core support Janani Sunil
2026-09-04 14:31 ` sashiko-bot
2026-09-04 14:14 ` [PATCH v6 06/17] iio: adc: ad7768: Validate master clock rate Janani Sunil
2026-09-04 14:14 ` [PATCH v6 07/17] iio: adc: ad7768: Add power mode helper Janani Sunil
2026-09-04 14:25 ` sashiko-bot
2026-09-04 14:15 ` [PATCH v6 08/17] iio: adc: ad7768: Derive output data rates Janani Sunil
2026-09-04 14:15 ` Janani Sunil [this message]
2026-09-04 14:45 ` [PATCH v6 09/17] iio: adc: ad7768: Configure channel sampling profiles sashiko-bot
2026-09-04 14:15 ` [PATCH v6 10/17] iio: adc: ad7768: Add sampling frequency controls Janani Sunil
2026-09-04 14:15 ` [PATCH v6 11/17] iio: adc: ad7768: Add per-channel filter controls Janani Sunil
2026-09-04 14:15 ` [PATCH v6 12/17] iio: adc: ad7768: Wait for digital filters to settle Janani Sunil
2026-09-04 14:15 ` [PATCH v6 13/17] iio: adc: ad7768: Add calibration controls Janani Sunil
2026-09-04 14:35 ` sashiko-bot
2026-09-04 14:15 ` [PATCH v6 14/17] iio: adc: ad7768: Add per-channel conversion delay Janani Sunil
2026-09-04 14:33 ` sashiko-bot
2026-09-04 14:15 ` [PATCH v6 15/17] iio: adc: ad7768: Add VCM regulator support Janani Sunil
2026-09-04 14:15 ` [PATCH v6 16/17] iio: adc: ad7768: Register GPIO auxiliary device Janani Sunil
2026-09-04 14:15 ` [PATCH v6 17/17] Documentation: iio: Add AD7768 Documentation Janani Sunil
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=20260904-ad7768-driver-v6-9-e4378f946bfb@analog.com \
--to=janani.sunil@analog.com \
--cc=Michael.Hennerich@analog.com \
--cc=andy@kernel.org \
--cc=brgl@kernel.org \
--cc=conor+dt@kernel.org \
--cc=corbet@lwn.net \
--cc=devicetree@vger.kernel.org \
--cc=dlechner@baylibre.com \
--cc=jananisunil.dev@gmail.com \
--cc=jic23@kernel.org \
--cc=krzk+dt@kernel.org \
--cc=linusw@kernel.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-gpio@vger.kernel.org \
--cc=linux-iio@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux@analog.com \
--cc=mwalle@kernel.org \
--cc=nuno.sa@analog.com \
--cc=olivier.moysan@foss.st.com \
--cc=p.zabel@pengutronix.de \
--cc=rdunlap@infradead.org \
--cc=robh@kernel.org \
--cc=skhan@linuxfoundation.org \
--cc=u.kleine-koenig@baylibre.com \
/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