From nobody Thu Apr 9 07:17:16 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A10182E889C; Tue, 10 Mar 2026 14:32:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773153149; cv=none; b=Ff411v8xPZZ/HgicSoJrekhPsY+vqt6wgpZ/ErP3U1yQhtc5GlpFrum2dVnhc+f5R9VbagKh47GaZsxtkyHC9qrMYAzuEn/2Nju3+xg3lp/gq+vvTD2/2LoXE/Dmw+pWrU9cA1S/cGeqvFm2n2fjnZTCjLz/4yqhcUXBNhp2s8w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773153149; c=relaxed/simple; bh=6ehyyMN+Vttl2/dmkN2/Dpo2Hk5GuIV7SClRqfw7fbM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Hm/YehU+cmToo90V77D4/Vn0ewD0uVRxQ1IZLg54ZFUv5qF9BzuevwhpL376sX6Sz5bO15U8U2TEBbHariFw+xeGft+ncI13Zdu+sDBEXQ5BOqbp+6o18eboTXlSP2WHHqpn1uJIaU7BuG+0kqhmArIevwY0sIf9iWo/veyz2lE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=GSI6FPYv; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="GSI6FPYv" Received: by smtp.kernel.org (Postfix) with ESMTPS id 37F64C2BCB1; Tue, 10 Mar 2026 14:32:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1773153149; bh=6ehyyMN+Vttl2/dmkN2/Dpo2Hk5GuIV7SClRqfw7fbM=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=GSI6FPYvEtSbQ0qv85y7wAbZ85FYP595Fh36ongxEaGGLn4LbYR0LauxCl6ZwsVIV Vx65h/mtzjWdzLfVlHbsrSWjIvD5Bcs3dMfxC1Y9zP2Di6BV/nh3EL5vvrtH6Tzg6S 4y1nmZ0mUxTIatvaBL8qEnTg5MwjvBh/VE+syIQ9bg/Whyp6PuPTD39PPct83AZwiY mrGbrf/4dtScKaMj/YfjKgbX7rUoNiDSOG5U+ct8yNUE9DsAT8bcvM2jB6tLDHpaJr FbQn3Tnhmtlz3gxBpRVUEBWPUP1i1IEFAU2Z/eSEFziiDzcrMJ0FQPVbqVfxlVSVht aGNb2BUKgRNPw== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 27D15EB1060; Tue, 10 Mar 2026 14:32:29 +0000 (UTC) From: Radu Sabau via B4 Relay Date: Tue, 10 Mar 2026 16:32:25 +0200 Subject: [PATCH v2 4/4] iio: adc: ad4691: add SPI offload support Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260310-ad4692-multichannel-sar-adc-driver-v2-4-d9bb8aeb5e17@analog.com> References: <20260310-ad4692-multichannel-sar-adc-driver-v2-0-d9bb8aeb5e17@analog.com> In-Reply-To: <20260310-ad4692-multichannel-sar-adc-driver-v2-0-d9bb8aeb5e17@analog.com> To: Lars-Peter Clausen , Michael Hennerich , Jonathan Cameron , David Lechner , =?utf-8?q?Nuno_S=C3=A1?= , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= , Liam Girdwood , Mark Brown , Linus Walleij , Bartosz Golaszewski Cc: linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org, linux-gpio@vger.kernel.org, Radu Sabau X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1773153147; l=18450; i=radu.sabau@analog.com; s=20260220; h=from:subject:message-id; bh=BHgIa8pe6A8XMJvEo+AxZy5Cw3LLOU1NleJ4RT2NNoA=; b=Q/yPNk0DAT4z68vxkEwQKoqZtxKG/l6e454OJkJ78fAiEt5plSzXcuxnJxD2/GMlX8fxEZnVt BZuhR63eSzOAlIYW6gr6/F7JOjeSl94/KqIscOPilyGe1cRNpNTmIcC X-Developer-Key: i=radu.sabau@analog.com; a=ed25519; pk=lDPQHgn9jTdt0vo58Na9lLxLaE2mb330if71Cn+EvFU= X-Endpoint-Received: by B4 Relay for radu.sabau@analog.com/20260220 with auth_id=642 X-Original-From: Radu Sabau Reply-To: radu.sabau@analog.com From: Radu Sabau Add SPI offload support to enable DMA-based, CPU-independent data acquisition using the SPI Engine offload framework. When an SPI offload is available (devm_spi_offload_get() succeeds), the driver registers a DMA engine IIO buffer and uses dedicated buffer setup operations. If no offload is available the existing software triggered buffer path is used unchanged. Both CNV Clock Mode and Manual Mode support offload, but use different trigger mechanisms: CNV Clock Mode: the SPI Engine is triggered by the ADC's DATA_READY signal on GP0. For this mode the driver acts as both an SPI offload consumer (DMA RX stream, message optimization) and a trigger source provider: it registers the GP0/DATA_READY output via devm_spi_offload_trigger_register() so the offload framework can match the '#trigger-source-cells' phandle from the device tree and automatically fire the SPI Engine DMA transfer at end-of-conversion. The pre-built SPI message reads all active channels from the AVG_IN accumulator registers (2-byte address + 2-byte data per channel, one 4-byte transfer each) followed by a state reset word to re-arm the accumulator for the next cycle. Manual Mode: the SPI Engine is triggered by a periodic trigger at the configured sampling frequency. The pre-built SPI message uses the pipelined CNV-on-CS protocol: N+1 4-byte transfers are issued for N active channels (the first result is discarded as garbage from the pipeline flush) and the remaining N results are captured by DMA. All offload transfers use 32-bit frames (bits_per_word=3D32, len=3D4) for DMA word alignment. In Manual Mode the 4-byte DMA word layout is [dummy(8), data_hi(8), data_lo(8), extra(8)]; the channel scan type storagebits=3D32, shift=3D8, realbits=3D16 correctly extracts the 16-bit ADC result from the middle two bytes. Kconfig gains a dependency on IIO_BUFFER_DMAENGINE. Signed-off-by: Radu Sabau --- drivers/iio/adc/Kconfig | 1 + drivers/iio/adc/ad4691.c | 398 +++++++++++++++++++++++++++++++++++++++++++= ++-- 2 files changed, 389 insertions(+), 10 deletions(-) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index d498f16c0816..93f090e9a562 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -144,6 +144,7 @@ config AD4691 depends on SPI select IIO_BUFFER select IIO_TRIGGERED_BUFFER + select IIO_BUFFER_DMAENGINE select REGMAP help Say yes here to build support for Analog Devices AD4691 Family MuxSAR diff --git a/drivers/iio/adc/ad4691.c b/drivers/iio/adc/ad4691.c index 8b3caf0334ba..2ed384cfc1b9 100644 --- a/drivers/iio/adc/ad4691.c +++ b/drivers/iio/adc/ad4691.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -22,11 +23,15 @@ #include #include #include +#include +#include #include #include #include =20 #include +#include +#include #include =20 #include @@ -47,6 +52,7 @@ */ #define AD4691_MANUAL_MODE_STD_FREQ(x, y) ((y) / (36 * ((x) + 1))) #define AD4691_BITS_PER_XFER 24 +#define AD4691_OFFLOAD_BITS_PER_WORD 32 #define AD4691_CNV_DUTY_CYCLE_NS 380 #define AD4691_MAX_CONV_PERIOD_US 800 =20 @@ -252,6 +258,16 @@ struct ad4691_state { struct hrtimer sampling_timer; ktime_t sampling_period; =20 + struct spi_offload *offload; + struct spi_offload_trigger *offload_trigger; + struct spi_offload_trigger *offload_trigger_periodic; + u64 offload_trigger_hz; + struct spi_message offload_msg; + /* Max 16 channel transfers + 1 state reset or NOOP */ + struct spi_transfer offload_xfer[17]; + /* TX commands for manual and accumulator modes */ + u32 offload_tx_cmd[17]; + u32 offload_tx_reset; /* * DMA (thus cache coherency maintenance) may require the * transfer buffers to live in their own cache lines. @@ -265,6 +281,65 @@ struct ad4691_state { } scan __aligned(IIO_DMA_MINALIGN); }; =20 +static const struct spi_offload_config ad4691_offload_config =3D { + .capability_flags =3D SPI_OFFLOAD_CAP_TRIGGER | + SPI_OFFLOAD_CAP_RX_STREAM_DMA, +}; + +static bool ad4691_offload_trigger_match(struct spi_offload_trigger *trigg= er, + enum spi_offload_trigger_type type, + u64 *args, u32 nargs) +{ + if (type !=3D SPI_OFFLOAD_TRIGGER_DATA_READY) + return false; + + /* + * Requires 2 args: + * args[0] is the trigger event (BUSY or DATA_READY). + * args[1] is the GPIO pin number (only GP0 supported). + */ + if (nargs !=3D 2) + return false; + + if (args[0] !=3D AD4691_TRIGGER_EVENT_BUSY && + args[0] !=3D AD4691_TRIGGER_EVENT_DATA_READY) + return false; + + if (args[1] !=3D AD4691_TRIGGER_PIN_GP0) + return false; + + return true; +} + +static int ad4691_offload_trigger_request(struct spi_offload_trigger *trig= ger, + enum spi_offload_trigger_type type, + u64 *args, u32 nargs) +{ + /* + * GP0 is configured as DATA_READY or BUSY in ad4691_config() + * based on the ADC mode. No additional configuration needed here. + */ + if (nargs !=3D 2) + return -EINVAL; + + return 0; +} + +static int ad4691_offload_trigger_validate(struct spi_offload_trigger *tri= gger, + struct spi_offload_trigger_config *config) +{ + if (config->type !=3D SPI_OFFLOAD_TRIGGER_DATA_READY) + return -EINVAL; + + return 0; +} + +static const struct spi_offload_trigger_ops ad4691_offload_trigger_ops =3D= { + .match =3D ad4691_offload_trigger_match, + .request =3D ad4691_offload_trigger_request, + .validate =3D ad4691_offload_trigger_validate, +}; + static void ad4691_disable_pwm(void *data) { struct pwm_device *pwm =3D data; @@ -442,6 +517,9 @@ static int ad4691_transfer(struct ad4691_state *st, int= command, static int ad4691_get_sampling_freq(struct ad4691_state *st) { if (st->adc_mode =3D=3D AD4691_MANUAL_MODE) { + /* Offload uses periodic trigger, non-offload uses hrtimer */ + if (st->offload) + return st->offload_trigger_hz; return DIV_ROUND_CLOSEST_ULL(NSEC_PER_SEC, ktime_to_ns(st->sampling_period)); } @@ -497,6 +575,7 @@ static int ad4691_pwm_get(struct spi_device *spi, struc= t ad4691_state *st) static int ad4691_set_sampling_freq(struct iio_dev *indio_dev, unsigned in= t freq) { struct ad4691_state *st =3D iio_priv(indio_dev); + int ret; =20 IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); =20 @@ -506,12 +585,31 @@ static int ad4691_set_sampling_freq(struct iio_dev *i= ndio_dev, unsigned int freq guard(mutex)(&st->lock); =20 if (st->adc_mode =3D=3D AD4691_MANUAL_MODE) { - if (!freq || freq > st->chip->max_rate) - return -EINVAL; - - st->sampling_period =3D ns_to_ktime(DIV_ROUND_CLOSEST_ULL - (NSEC_PER_SEC, freq)); - return 0; + /* For offload mode, validate and store frequency for periodic trigger */ + if (st->offload) { + struct spi_offload_trigger_config config =3D { + .type =3D SPI_OFFLOAD_TRIGGER_PERIODIC, + .periodic =3D { + .frequency_hz =3D freq, + }, + }; + + ret =3D spi_offload_trigger_validate(st->offload_trigger_periodic, + &config); + if (ret) + return ret; + + st->offload_trigger_hz =3D config.periodic.frequency_hz; + return 0; + } else { + /* Non-offload: update hrtimer sampling period */ + if (!freq || freq > st->chip->max_rate) + return -EINVAL; + + st->sampling_period =3D ns_to_ktime(DIV_ROUND_CLOSEST_ULL + (NSEC_PER_SEC, freq)); + return 0; + } } =20 if (!st->conv_trigger) @@ -787,6 +885,224 @@ static const struct iio_buffer_setup_ops ad4691_buffe= r_setup_ops =3D { .postdisable =3D &ad4691_buffer_postdisable, }; =20 +static int ad4691_offload_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad4691_state *st =3D iio_priv(indio_dev); + struct spi_offload_trigger_config config =3D { }; + struct spi_offload_trigger *trigger; + struct spi_transfer *xfer =3D st->offload_xfer; + int ret, num_xfers =3D 0; + int active_chans[16]; + unsigned int bit; + int n_active =3D 0; + int i; + + memset(xfer, 0, sizeof(st->offload_xfer)); + + /* Collect active channels in scan order */ + iio_for_each_active_channel(indio_dev, bit) + active_chans[n_active++] =3D bit; + + ret =3D ad4691_enter_conversion_mode(st); + if (ret) + return ret; + + /* + * MANUAL_MODE uses a periodic (PWM) trigger and reads directly from + * the ADC. CNV_CLOCK_MODE uses the DATA_READY trigger and reads from + * accumulators. + */ + if (st->adc_mode =3D=3D AD4691_MANUAL_MODE) { + config.type =3D SPI_OFFLOAD_TRIGGER_PERIODIC; + config.periodic.frequency_hz =3D st->offload_trigger_hz; + trigger =3D st->offload_trigger_periodic; + if (!trigger) + return -EINVAL; + } else { + ret =3D regmap_write(st->regmap, AD4691_STATE_RESET_REG, + AD4691_STATE_RESET_ALL); + if (ret) + return ret; + + /* Configure accumulator masks - 0 =3D enabled, 1 =3D masked */ + ret =3D regmap_write(st->regmap, AD4691_ACC_MASK1_REG, + ~(*indio_dev->active_scan_mask) & 0xFF); + if (ret) + return ret; + + ret =3D regmap_write(st->regmap, AD4691_ACC_MASK2_REG, + ~(*indio_dev->active_scan_mask >> 8) & 0xFF); + if (ret) + return ret; + + /* Configure sequencer with active channels */ + ret =3D regmap_write(st->regmap, AD4691_STD_SEQ_CONFIG, + *indio_dev->active_scan_mask); + if (ret) + return ret; + + iio_for_each_active_channel(indio_dev, bit) { + ret =3D regmap_write(st->regmap, AD4691_ACC_COUNT_LIMIT(bit), + AD4691_ACC_COUNT_VAL); + if (ret) + return ret; + } + + config.type =3D SPI_OFFLOAD_TRIGGER_DATA_READY; + trigger =3D st->offload_trigger; + } + + if (st->adc_mode =3D=3D AD4691_MANUAL_MODE) { + /* + * Manual mode with CNV tied to CS: Each CS toggle triggers a + * conversion AND reads the previous conversion result (pipeline). + */ + for (i =3D 0; i < n_active; i++) { + st->offload_tx_cmd[num_xfers] =3D AD4691_ADC_CHAN(active_chans[i]) << 2= 4; + xfer[num_xfers].tx_buf =3D &st->offload_tx_cmd[num_xfers]; + xfer[num_xfers].len =3D 4; + xfer[num_xfers].bits_per_word =3D 32; + xfer[num_xfers].speed_hz =3D st->spi->max_speed_hz; + xfer[num_xfers].cs_change =3D 1; + xfer[num_xfers].cs_change_delay.value =3D 1000; + xfer[num_xfers].cs_change_delay.unit =3D SPI_DELAY_UNIT_NSECS; + /* First transfer RX is garbage - don't capture it */ + if (num_xfers) + xfer[num_xfers].offload_flags =3D SPI_OFFLOAD_XFER_RX_STREAM; + num_xfers++; + } + + /* Final NOOP to flush pipeline and get last channel's data */ + st->offload_tx_cmd[num_xfers] =3D AD4691_NOOP << 24; + xfer[num_xfers].tx_buf =3D &st->offload_tx_cmd[num_xfers]; + xfer[num_xfers].len =3D 4; + xfer[num_xfers].bits_per_word =3D 32; + xfer[num_xfers].speed_hz =3D st->spi->max_speed_hz; + xfer[num_xfers].cs_change =3D 0; + xfer[num_xfers].offload_flags =3D SPI_OFFLOAD_XFER_RX_STREAM; + num_xfers++; + } else { + /* + * CNV_CLOCK_MODE: single transfer per channel (2-byte cmd + + * 2-byte data =3D 4 bytes, one 32-bit SPI Engine DMA word). + * AVG_IN registers are used; RX layout: [cmd_hi, cmd_lo, d_hi, d_lo] + */ + for (i =3D 0; i < n_active; i++) { + unsigned int reg; + int ch =3D active_chans[i]; + + reg =3D AD4691_AVG_IN(ch); + st->offload_tx_cmd[ch] =3D + ((reg >> 8) | 0x80) << 24 | + (reg & 0xFF) << 16; + xfer[num_xfers].tx_buf =3D &st->offload_tx_cmd[ch]; + xfer[num_xfers].len =3D 4; + xfer[num_xfers].bits_per_word =3D 32; + xfer[num_xfers].speed_hz =3D st->spi->max_speed_hz; + xfer[num_xfers].offload_flags =3D SPI_OFFLOAD_XFER_RX_STREAM; + xfer[num_xfers].cs_change =3D 1; + num_xfers++; + } + + /* + * State reset: clear accumulator so DATA_READY can fire again. + * With bits_per_word=3D32, SPI engine transmits MSB first. + */ + st->offload_tx_reset =3D ((AD4691_STATE_RESET_REG >> 8) << 24) | + ((AD4691_STATE_RESET_REG & 0xFF) << 16) | + (0x01 << 8); + + xfer[num_xfers].tx_buf =3D &st->offload_tx_reset; + xfer[num_xfers].len =3D 4; + xfer[num_xfers].bits_per_word =3D 32; + xfer[num_xfers].speed_hz =3D st->spi->max_speed_hz; + xfer[num_xfers].cs_change =3D 0; + num_xfers++; + } + + if (num_xfers =3D=3D 0) + return -EINVAL; + + /* + * For MANUAL_MODE, validate that the trigger frequency is low enough + * for all SPI transfers to complete. Each transfer is 32 bits. + * Add 50% margin for CS setup/hold and other overhead. + */ + if (st->adc_mode =3D=3D AD4691_MANUAL_MODE) { + u64 min_period_ns; + u64 trigger_period_ns; + + /* Time for all transfers in nanoseconds, with 50% overhead margin */ + min_period_ns =3D div64_u64((u64)num_xfers * AD4691_OFFLOAD_BITS_PER_WOR= D * + NSEC_PER_SEC * 3, + st->spi->max_speed_hz * 2); + + trigger_period_ns =3D div64_u64(NSEC_PER_SEC, st->offload_trigger_hz); + + if (trigger_period_ns < min_period_ns) + return -EINVAL; + } + + spi_message_init_with_transfers(&st->offload_msg, xfer, num_xfers); + st->offload_msg.offload =3D st->offload; + + ret =3D spi_optimize_message(st->spi, &st->offload_msg); + if (ret) + return ret; + + /* + * For CNV_CLOCK_MODE, start conversions before enabling the trigger. + * If the trigger is enabled first, the SPI engine blocks waiting for + * DATA_READY, and any subsequent SPI write times out. + * + * MANUAL_MODE: CNV is tied to CS; conversion starts with each transfer. + */ + if (st->adc_mode =3D=3D AD4691_CNV_CLOCK_MODE) { + ret =3D ad4691_sampling_enable(st, true); + if (ret) + goto err_unoptimize_message; + } + + ret =3D spi_offload_trigger_enable(st->offload, trigger, &config); + if (ret) + goto err_sampling_disable; + + return 0; + +err_sampling_disable: + if (st->adc_mode =3D=3D AD4691_CNV_CLOCK_MODE) + ad4691_sampling_enable(st, false); +err_unoptimize_message: + spi_unoptimize_message(&st->offload_msg); + return ret; +} + +static int ad4691_offload_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad4691_state *st =3D iio_priv(indio_dev); + struct spi_offload_trigger *trigger; + int ret; + + trigger =3D (st->adc_mode =3D=3D AD4691_MANUAL_MODE) ? + st->offload_trigger_periodic : st->offload_trigger; + + spi_offload_trigger_disable(st->offload, trigger); + spi_unoptimize_message(&st->offload_msg); + + if (st->adc_mode =3D=3D AD4691_CNV_CLOCK_MODE) { + ret =3D ad4691_sampling_enable(st, false); + if (ret) + return ret; + } + + return ad4691_exit_conversion_mode(st); +} + +static const struct iio_buffer_setup_ops ad4691_offload_buffer_setup_ops = =3D { + .postenable =3D &ad4691_offload_buffer_postenable, + .predisable =3D &ad4691_offload_buffer_predisable, +}; + static irqreturn_t ad4691_irq(int irq, void *private) { struct iio_dev *indio_dev =3D private; @@ -981,6 +1297,54 @@ static int ad4691_config(struct ad4691_state *st) return regmap_write(st->regmap, AD4691_GPIO_MODE1_REG, AD4691_ADC_BUSY); } =20 +static int ad4691_setup_offload(struct iio_dev *indio_dev, + struct ad4691_state *st) +{ + struct device *dev =3D &st->spi->dev; + struct dma_chan *rx_dma; + int ret; + + if (st->adc_mode =3D=3D AD4691_MANUAL_MODE) { + st->offload_trigger_periodic =3D devm_spi_offload_trigger_get(dev, + st->offload, SPI_OFFLOAD_TRIGGER_PERIODIC); + if (IS_ERR(st->offload_trigger_periodic)) + return dev_err_probe(dev, + PTR_ERR(st->offload_trigger_periodic), + "failed to get periodic offload trigger\n"); + + st->offload_trigger_hz =3D AD4691_MANUAL_MODE_STD_FREQ(st->chip->num_cha= nnels, + st->spi->max_speed_hz); + } else { + struct spi_offload_trigger_info trigger_info =3D { + .fwnode =3D dev_fwnode(dev), + .ops =3D &ad4691_offload_trigger_ops, + .priv =3D st, + }; + + ret =3D devm_spi_offload_trigger_register(dev, &trigger_info); + if (ret) + return dev_err_probe(dev, ret, + "failed to register offload trigger\n"); + + st->offload_trigger =3D devm_spi_offload_trigger_get(dev, + st->offload, SPI_OFFLOAD_TRIGGER_DATA_READY); + if (IS_ERR(st->offload_trigger)) + return dev_err_probe(dev, PTR_ERR(st->offload_trigger), + "failed to get offload trigger\n"); + } + + rx_dma =3D devm_spi_offload_rx_stream_request_dma_chan(dev, st->offload); + if (IS_ERR(rx_dma)) + return dev_err_probe(dev, PTR_ERR(rx_dma), + "failed to get offload RX DMA\n"); + + indio_dev->modes =3D INDIO_DIRECT_MODE | INDIO_BUFFER_HARDWARE; + indio_dev->setup_ops =3D &ad4691_offload_buffer_setup_ops; + + return devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, + rx_dma, IIO_BUFFER_DIRECTION_IN); +} + static int ad4691_setup_triggered_buffer(struct iio_dev *indio_dev, struct ad4691_state *st) { @@ -1064,6 +1428,14 @@ static int ad4691_probe(struct spi_device *spi) return dev_err_probe(dev, PTR_ERR(st->regmap), "Failed to initialize regmap\n"); =20 + st->offload =3D devm_spi_offload_get(dev, spi, &ad4691_offload_config); + if (IS_ERR(st->offload)) { + if (PTR_ERR(st->offload) !=3D -ENODEV) + return dev_err_probe(dev, PTR_ERR(st->offload), + "failed to get SPI offload\n"); + st->offload =3D NULL; + } + st->chip =3D spi_get_device_match_data(spi); if (!st->chip) return dev_err_probe(dev, -ENODEV, "Could not find chip info\n"); @@ -1088,10 +1460,15 @@ static int ad4691_probe(struct spi_device *spi) ? st->chip->manual_channels : st->chip->channels; indio_dev->num_channels =3D st->chip->num_channels; =20 - ret =3D ad4691_setup_triggered_buffer(indio_dev, st); - if (ret) - return ret; - + if (st->offload) { + ret =3D ad4691_setup_offload(indio_dev, st); + if (ret) + return ret; + } else { + ret =3D ad4691_setup_triggered_buffer(indio_dev, st); + if (ret) + return ret; + } return devm_iio_device_register(dev, indio_dev); } =20 @@ -1126,3 +1503,4 @@ module_spi_driver(ad4691_driver); MODULE_AUTHOR("Radu Sabau "); MODULE_DESCRIPTION("Analog Devices AD4691 Family ADC Driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_DMA_BUFFER"); --=20 2.43.0