// SPDX-License-Identifier: GPL-2.0
/*
 * This file is the ADC part of the STM32 DFSDM driver
 *
 * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
 * Author: Arnaud Pouliquen <arnaud.pouliquen@st.com>.
 */

#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/iio/adc/stm32-dfsdm-adc.h>
#include <linux/iio/buffer.h>
#include <linux/iio/hw-consumer.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/timer/stm32-lptim-trigger.h>
#include <linux/iio/timer/stm32-timer-trigger.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>

#include "stm32-dfsdm.h"

#define DFSDM_DMA_BUFFER_SIZE (4 * PAGE_SIZE)

/* Conversion timeout */
#define DFSDM_TIMEOUT_US 100000
#define DFSDM_TIMEOUT (msecs_to_jiffies(DFSDM_TIMEOUT_US / 1000))

/* Oversampling attribute default */
#define DFSDM_DEFAULT_OVERSAMPLING  100

/* Oversampling max values */
#define DFSDM_MAX_INT_OVERSAMPLING 256
#define DFSDM_MAX_FL_OVERSAMPLING 1024

/* Limit filter output resolution to 31 bits. (i.e. sample range is +/-2^30) */
#define DFSDM_DATA_MAX BIT(30)
/*
 * Data are output as two's complement data in a 24 bit field.
 * Data from filters are in the range +/-2^(n-1)
 * 2^(n-1) maximum positive value cannot be coded in 2's complement n bits
 * An extra bit is required to avoid wrap-around of the binary code for 2^(n-1)
 * So, the resolution of samples from filter is actually limited to 23 bits
 */
#define DFSDM_DATA_RES 24

/* Filter configuration */
#define DFSDM_CR1_CFG_MASK (DFSDM_CR1_RCH_MASK | DFSDM_CR1_RCONT_MASK | \
			    DFSDM_CR1_RSYNC_MASK | DFSDM_CR1_JSYNC_MASK | \
			    DFSDM_CR1_JSCAN_MASK)

enum sd_converter_type {
	DFSDM_AUDIO,
	DFSDM_IIO,
};

struct stm32_dfsdm_dev_data {
	int type;
	int (*init)(struct device *dev, struct iio_dev *indio_dev);
	unsigned int num_channels;
	const struct regmap_config *regmap_cfg;
};

struct stm32_dfsdm_adc {
	struct stm32_dfsdm *dfsdm;
	const struct stm32_dfsdm_dev_data *dev_data;
	unsigned int fl_id;
	unsigned int nconv;
	unsigned long smask;

	/* ADC specific */
	unsigned int oversamp;
	struct iio_hw_consumer *hwc;
	struct completion completion;
	u32 *buffer;

	/* Audio specific */
	unsigned int spi_freq;  /* SPI bus clock frequency */
	unsigned int sample_freq; /* Sample frequency after filter decimation */
	int (*cb)(const void *data, size_t size, void *cb_priv);
	void *cb_priv;

	/* DMA */
	u8 *rx_buf;
	unsigned int bufi; /* Buffer current position */
	unsigned int buf_sz; /* Buffer size */
	struct dma_chan	*dma_chan;
	dma_addr_t dma_buf;
};

struct stm32_dfsdm_str2field {
	const char	*name;
	unsigned int	val;
};

/* DFSDM channel serial interface type */
static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_type[] = {
	{ "SPI_R", 0 }, /* SPI with data on rising edge */
	{ "SPI_F", 1 }, /* SPI with data on falling edge */
	{ "MANCH_R", 2 }, /* Manchester codec, rising edge = logic 0 */
	{ "MANCH_F", 3 }, /* Manchester codec, falling edge = logic 1 */
	{},
};

/* DFSDM channel clock source */
static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_src[] = {
	/* External SPI clock (CLKIN x) */
	{ "CLKIN", DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL },
	/* Internal SPI clock (CLKOUT) */
	{ "CLKOUT", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL },
	/* Internal SPI clock divided by 2 (falling edge) */
	{ "CLKOUT_F", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING },
	/* Internal SPI clock divided by 2 (falling edge) */
	{ "CLKOUT_R", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING },
	{},
};

static int stm32_dfsdm_str2val(const char *str,
			       const struct stm32_dfsdm_str2field *list)
{
	const struct stm32_dfsdm_str2field *p = list;

	for (p = list; p && p->name; p++)
		if (!strcmp(p->name, str))
			return p->val;

	return -EINVAL;
}

/**
 * struct stm32_dfsdm_trig_info - DFSDM trigger info
 * @name:		name of the trigger, corresponding to its source
 * @jextsel:		trigger signal selection
 */
struct stm32_dfsdm_trig_info {
	const char *name;
	unsigned int jextsel;
};

/* hardware injected trigger enable, edge selection */
enum stm32_dfsdm_jexten {
	STM32_DFSDM_JEXTEN_DISABLED,
	STM32_DFSDM_JEXTEN_RISING_EDGE,
	STM32_DFSDM_JEXTEN_FALLING_EDGE,
	STM32_DFSDM_EXTEN_BOTH_EDGES,
};

static const struct stm32_dfsdm_trig_info stm32_dfsdm_trigs[] = {
	{ TIM1_TRGO, 0 },
	{ TIM1_TRGO2, 1 },
	{ TIM8_TRGO, 2 },
	{ TIM8_TRGO2, 3 },
	{ TIM3_TRGO, 4 },
	{ TIM4_TRGO, 5 },
	{ TIM16_OC1, 6 },
	{ TIM6_TRGO, 7 },
	{ TIM7_TRGO, 8 },
	{ LPTIM1_OUT, 26 },
	{ LPTIM2_OUT, 27 },
	{ LPTIM3_OUT, 28 },
	{},
};

static int stm32_dfsdm_get_jextsel(struct iio_dev *indio_dev,
				   struct iio_trigger *trig)
{
	int i;

	/* lookup triggers registered by stm32 timer trigger driver */
	for (i = 0; stm32_dfsdm_trigs[i].name; i++) {
		/**
		 * Checking both stm32 timer trigger type and trig name
		 * should be safe against arbitrary trigger names.
		 */
		if ((is_stm32_timer_trigger(trig) ||
		     is_stm32_lptim_trigger(trig)) &&
		    !strcmp(stm32_dfsdm_trigs[i].name, trig->name)) {
			return stm32_dfsdm_trigs[i].jextsel;
		}
	}

	return -EINVAL;
}

static int stm32_dfsdm_compute_osrs(struct stm32_dfsdm_filter *fl,
				    unsigned int fast, unsigned int oversamp)
{
	unsigned int i, d, fosr, iosr;
	u64 res, max;
	int bits, shift;
	unsigned int m = 1;	/* multiplication factor */
	unsigned int p = fl->ford;	/* filter order (ford) */
	struct stm32_dfsdm_filter_osr *flo = &fl->flo[fast];

	pr_debug("Requested oversampling: %d\n", oversamp);
	/*
	 * This function tries to compute filter oversampling and integrator
	 * oversampling, base on oversampling ratio requested by user.
	 *
	 * Decimation d depends on the filter order and the oversampling ratios.
	 * ford: filter order
	 * fosr: filter over sampling ratio
	 * iosr: integrator over sampling ratio
	 */
	if (fl->ford == DFSDM_FASTSINC_ORDER) {
		m = 2;
		p = 2;
	}

	/*
	 * Look for filter and integrator oversampling ratios which allows
	 * to maximize data output resolution.
	 */
	for (fosr = 1; fosr <= DFSDM_MAX_FL_OVERSAMPLING; fosr++) {
		for (iosr = 1; iosr <= DFSDM_MAX_INT_OVERSAMPLING; iosr++) {
			if (fast)
				d = fosr * iosr;
			else if (fl->ford == DFSDM_FASTSINC_ORDER)
				d = fosr * (iosr + 3) + 2;
			else
				d = fosr * (iosr - 1 + p) + p;

			if (d > oversamp)
				break;
			else if (d != oversamp)
				continue;
			/*
			 * Check resolution (limited to signed 32 bits)
			 *   res <= 2^31
			 * Sincx filters:
			 *   res = m * fosr^p x iosr (with m=1, p=ford)
			 * FastSinc filter
			 *   res = m * fosr^p x iosr (with m=2, p=2)
			 */
			res = fosr;
			for (i = p - 1; i > 0; i--) {
				res = res * (u64)fosr;
				if (res > DFSDM_DATA_MAX)
					break;
			}
			if (res > DFSDM_DATA_MAX)
				continue;

			res = res * (u64)m * (u64)iosr;
			if (res > DFSDM_DATA_MAX)
				continue;

			if (res >= flo->res) {
				flo->res = res;
				flo->fosr = fosr;
				flo->iosr = iosr;

				bits = fls(flo->res);
				/* 8 LBSs in data register contain chan info */
				max = flo->res << 8;

				/* if resolution is not a power of two */
				if (flo->res > BIT(bits - 1))
					bits++;
				else
					max--;

				shift = DFSDM_DATA_RES - bits;
				/*
				 * Compute right/left shift
				 * Right shift is performed by hardware
				 * when transferring samples to data register.
				 * Left shift is done by software on buffer
				 */
				if (shift > 0) {
					/* Resolution is lower than 24 bits */
					flo->rshift = 0;
					flo->lshift = shift;
				} else {
					/*
					 * If resolution is 24 bits or more,
					 * max positive value may be ambiguous
					 * (equal to max negative value as sign
					 * bit is dropped).
					 * Reduce resolution to 23 bits (rshift)
					 * to keep the sign on bit 23 and treat
					 * saturation before rescaling on 24
					 * bits (lshift).
					 */
					flo->rshift = 1 - shift;
					flo->lshift = 1;
					max >>= flo->rshift;
				}
				flo->max = (s32)max;
				flo->bits = bits;

				pr_debug("fast %d, fosr %d, iosr %d, res 0x%llx/%d bits, rshift %d, lshift %d\n",
					 fast, flo->fosr, flo->iosr,
					 flo->res, bits, flo->rshift,
					 flo->lshift);
			}
		}
	}

	if (!flo->res)
		return -EINVAL;

	return 0;
}

static int stm32_dfsdm_compute_all_osrs(struct iio_dev *indio_dev,
					unsigned int oversamp)
{
	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
	struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
	int ret0, ret1;

	memset(&fl->flo[0], 0, sizeof(fl->flo[0]));
	memset(&fl->flo[1], 0, sizeof(fl->flo[1]));

	ret0 = stm32_dfsdm_compute_osrs(fl, 0, oversamp);
	ret1 = stm32_dfsdm_compute_osrs(fl, 1, oversamp);
	if (ret0 < 0 && ret1 < 0) {
		dev_err(&indio_dev->dev,
			"Filter parameters not found: errors %d/%d\n",
			ret0, ret1);
		return -EINVAL;
	}

	return 0;
}

static int stm32_dfsdm_start_channel(struct iio_dev *indio_dev)
{
	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
	struct regmap *regmap = adc->dfsdm->regmap;
	const struct iio_chan_spec *chan;
	unsigned int bit;
	int ret;

	for_each_set_bit(bit, &adc->smask, sizeof(adc->smask) * BITS_PER_BYTE) {
		chan = indio_dev->channels + bit;
		ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(chan->channel),
					 DFSDM_CHCFGR1_CHEN_MASK,
					 DFSDM_CHCFGR1_CHEN(1));
		if (ret < 0)
			return ret;
	}

	return 0;
}

static void stm32_dfsdm_stop_channel(struct iio_dev *indio_dev)
{
	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
	struct regmap *regmap = adc->dfsdm->regmap;
	const struct iio_chan_spec *chan;
	unsigned int bit;

	for_each_set_bit(bit, &adc->smask, sizeof(adc->smask) * BITS_PER_BYTE) {
		chan = indio_dev->channels + bit;
		regmap_update_bits(regmap, DFSDM_CHCFGR1(chan->channel),
				   DFSDM_CHCFGR1_CHEN_MASK,
				   DFSDM_CHCFGR1_CHEN(0));
	}
}

static int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
				      struct stm32_dfsdm_channel *ch)
{
	unsigned int id = ch->id;
	struct regmap *regmap = dfsdm->regmap;
	int ret;

	ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
				 DFSDM_CHCFGR1_SITP_MASK,
				 DFSDM_CHCFGR1_SITP(ch->type));
	if (ret < 0)
		return ret;
	ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
				 DFSDM_CHCFGR1_SPICKSEL_MASK,
				 DFSDM_CHCFGR1_SPICKSEL(ch->src));
	if (ret < 0)
		return ret;
	return regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
				  DFSDM_CHCFGR1_CHINSEL_MASK,
				  DFSDM_CHCFGR1_CHINSEL(ch->alt_si));
}

static int stm32_dfsdm_start_filter(struct stm32_dfsdm_adc *adc,
				    unsigned int fl_id,
				    struct iio_trigger *trig)
{
	struct stm32_dfsdm *dfsdm = adc->dfsdm;
	int ret;

	/* Enable filter */
	ret = regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
				 DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(1));
	if (ret < 0)
		return ret;

	/* Nothing more to do for injected (scan mode/triggered) conversions */
	if (adc->nconv > 1 || trig)
		return 0;

	/* Software start (single or continuous) regular conversion */
	return regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
				  DFSDM_CR1_RSWSTART_MASK,
				  DFSDM_CR1_RSWSTART(1));
}

static void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm,
				    unsigned int fl_id)
{
	/* Disable conversion */
	regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
			   DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(0));
}

static int stm32_dfsdm_filter_set_trig(struct iio_dev *indio_dev,
				       unsigned int fl_id,
				       struct iio_trigger *trig)
{
	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
	struct regmap *regmap = adc->dfsdm->regmap;
	u32 jextsel = 0, jexten = STM32_DFSDM_JEXTEN_DISABLED;
	int ret;

	if (trig) {
		ret = stm32_dfsdm_get_jextsel(indio_dev, trig);
		if (ret < 0)
			return ret;

		/* set trigger source and polarity (default to rising edge) */
		jextsel = ret;
		jexten = STM32_DFSDM_JEXTEN_RISING_EDGE;
	}

	ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id),
				 DFSDM_CR1_JEXTSEL_MASK | DFSDM_CR1_JEXTEN_MASK,
				 DFSDM_CR1_JEXTSEL(jextsel) |
				 DFSDM_CR1_JEXTEN(jexten));
	if (ret < 0)
		return ret;

	return 0;
}

static int stm32_dfsdm_channels_configure(struct iio_dev *indio_dev,
					  unsigned int fl_id,
					  struct iio_trigger *trig)
{
	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
	struct regmap *regmap = adc->dfsdm->regmap;
	struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[fl_id];
	struct stm32_dfsdm_filter_osr *flo = &fl->flo[0];
	const struct iio_chan_spec *chan;
	unsigned int bit;
	int ret;

	fl->fast = 0;

	/*
	 * In continuous mode, use fast mode configuration,
	 * if it provides a better resolution.
	 */
	if (adc->nconv == 1 && !trig && iio_buffer_enabled(indio_dev)) {
		if (fl->flo[1].res >= fl->flo[0].res) {
			fl->fast = 1;
			flo = &fl->flo[1];
		}
	}

	if (!flo->res)
		return -EINVAL;

	dev_dbg(&indio_dev->dev, "Samples actual resolution: %d bits",
		min(flo->bits, (u32)DFSDM_DATA_RES - 1));

	for_each_set_bit(bit, &adc->smask,
			 sizeof(adc->smask) * BITS_PER_BYTE) {
		chan = indio_dev->channels + bit;

		ret = regmap_update_bits(regmap,
					 DFSDM_CHCFGR2(chan->channel),
					 DFSDM_CHCFGR2_DTRBS_MASK,
					 DFSDM_CHCFGR2_DTRBS(flo->rshift));
		if (ret)
			return ret;
	}

	return 0;
}

static int stm32_dfsdm_filter_configure(struct iio_dev *indio_dev,
					unsigned int fl_id,
					struct iio_trigger *trig)
{
	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
	struct regmap *regmap = adc->dfsdm->regmap;
	struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[fl_id];
	struct stm32_dfsdm_filter_osr *flo = &fl->flo[fl->fast];
	u32 cr1;
	const struct iio_chan_spec *chan;
	unsigned int bit, jchg = 0;
	int ret;

	/* Average integrator oversampling */
	ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_IOSR_MASK,
				 DFSDM_FCR_IOSR(flo->iosr - 1));
	if (ret)
		return ret;

	/* Filter order and Oversampling */
	ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FOSR_MASK,
				 DFSDM_FCR_FOSR(flo->fosr - 1));
	if (ret)
		return ret;

	ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FORD_MASK,
				 DFSDM_FCR_FORD(fl->ford));
	if (ret)
		return ret;

	ret = stm32_dfsdm_filter_set_trig(indio_dev, fl_id, trig);
	if (ret)
		return ret;

	ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id),
				 DFSDM_CR1_FAST_MASK,
				 DFSDM_CR1_FAST(fl->fast));
	if (ret)
		return ret;

	/*
	 * DFSDM modes configuration W.R.T audio/iio type modes
	 * ----------------------------------------------------------------
	 * Modes         | regular |  regular     | injected | injected   |
	 *               |         |  continuous  |          | + scan     |
	 * --------------|---------|--------------|----------|------------|
	 * single conv   |    x    |              |          |            |
	 * (1 chan)      |         |              |          |            |
	 * --------------|---------|--------------|----------|------------|
	 * 1 Audio chan	 |         | sample freq  |          |            |
	 *               |         | or sync_mode |          |            |
	 * --------------|---------|--------------|----------|------------|
	 * 1 IIO chan	 |         | sample freq  | trigger  |            |
	 *               |         | or sync_mode |          |            |
	 * --------------|---------|--------------|----------|------------|
	 * 2+ IIO chans  |         |              |          | trigger or |
	 *               |         |              |          | sync_mode  |
	 * ----------------------------------------------------------------
	 */
	if (adc->nconv == 1 && !trig) {
		bit = __ffs(adc->smask);
		chan = indio_dev->channels + bit;

		/* Use regular conversion for single channel without trigger */
		cr1 = DFSDM_CR1_RCH(chan->channel);

		/* Continuous conversions triggered by SPI clk in buffer mode */
		if (iio_buffer_enabled(indio_dev))
			cr1 |= DFSDM_CR1_RCONT(1);

		cr1 |= DFSDM_CR1_RSYNC(fl->sync_mode);
	} else {
		/* Use injected conversion for multiple channels */
		for_each_set_bit(bit, &adc->smask,
				 sizeof(adc->smask) * BITS_PER_BYTE) {
			chan = indio_dev->channels + bit;
			jchg |= BIT(chan->channel);
		}
		ret = regmap_write(regmap, DFSDM_JCHGR(fl_id), jchg);
		if (ret < 0)
			return ret;

		/* Use scan mode for multiple channels */
		cr1 = DFSDM_CR1_JSCAN((adc->nconv > 1) ? 1 : 0);

		/*
		 * Continuous conversions not supported in injected mode,
		 * either use:
		 * - conversions in sync with filter 0
		 * - triggered conversions
		 */
		if (!fl->sync_mode && !trig)
			return -EINVAL;
		cr1 |= DFSDM_CR1_JSYNC(fl->sync_mode);
	}

	return regmap_update_bits(regmap, DFSDM_CR1(fl_id), DFSDM_CR1_CFG_MASK,
				  cr1);
}

static int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
					struct iio_dev *indio_dev,
					struct iio_chan_spec *ch)
{
	struct stm32_dfsdm_channel *df_ch;
	const char *of_str;
	int chan_idx = ch->scan_index;
	int ret, val;

	ret = of_property_read_u32_index(indio_dev->dev.of_node,
					 "st,adc-channels", chan_idx,
					 &ch->channel);
	if (ret < 0) {
		dev_err(&indio_dev->dev,
			" Error parsing 'st,adc-channels' for idx %d\n",
			chan_idx);
		return ret;
	}
	if (ch->channel >= dfsdm->num_chs) {
		dev_err(&indio_dev->dev,
			" Error bad channel number %d (max = %d)\n",
			ch->channel, dfsdm->num_chs);
		return -EINVAL;
	}

	ret = of_property_read_string_index(indio_dev->dev.of_node,
					    "st,adc-channel-names", chan_idx,
					    &ch->datasheet_name);
	if (ret < 0) {
		dev_err(&indio_dev->dev,
			" Error parsing 'st,adc-channel-names' for idx %d\n",
			chan_idx);
		return ret;
	}

	df_ch =  &dfsdm->ch_list[ch->channel];
	df_ch->id = ch->channel;

	ret = of_property_read_string_index(indio_dev->dev.of_node,
					    "st,adc-channel-types", chan_idx,
					    &of_str);
	if (!ret) {
		val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_type);
		if (val < 0)
			return val;
	} else {
		val = 0;
	}
	df_ch->type = val;

	ret = of_property_read_string_index(indio_dev->dev.of_node,
					    "st,adc-channel-clk-src", chan_idx,
					    &of_str);
	if (!ret) {
		val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_src);
		if (val < 0)
			return val;
	} else {
		val = 0;
	}
	df_ch->src = val;

	ret = of_property_read_u32_index(indio_dev->dev.of_node,
					 "st,adc-alt-channel", chan_idx,
					 &df_ch->alt_si);
	if (ret < 0)
		df_ch->alt_si = 0;

	return 0;
}

static ssize_t dfsdm_adc_audio_get_spiclk(struct iio_dev *indio_dev,
					  uintptr_t priv,
					  const struct iio_chan_spec *chan,
					  char *buf)
{
	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);

	return snprintf(buf, PAGE_SIZE, "%d\n", adc->spi_freq);
}

static int dfsdm_adc_set_samp_freq(struct iio_dev *indio_dev,
				   unsigned int sample_freq,
				   unsigned int spi_freq)
{
	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
	unsigned int oversamp;
	int ret;

	oversamp = DIV_ROUND_CLOSEST(spi_freq, sample_freq);
	if (spi_freq % sample_freq)
		dev_dbg(&indio_dev->dev,
			"Rate not accurate. requested (%u), actual (%u)\n",
			sample_freq, spi_freq / oversamp);

	ret = stm32_dfsdm_compute_all_osrs(indio_dev, oversamp);
	if (ret < 0)
		return ret;

	adc->sample_freq = spi_freq / oversamp;
	adc->oversamp = oversamp;

	return 0;
}

static ssize_t dfsdm_adc_audio_set_spiclk(struct iio_dev *indio_dev,
					  uintptr_t priv,
					  const struct iio_chan_spec *chan,
					  const char *buf, size_t len)
{
	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
	struct stm32_dfsdm_channel *ch = &adc->dfsdm->ch_list[chan->channel];
	unsigned int sample_freq = adc->sample_freq;
	unsigned int spi_freq;
	int ret;

	dev_err(&indio_dev->dev, "enter %s\n", __func__);
	/* If DFSDM is master on SPI, SPI freq can not be updated */
	if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
		return -EPERM;

	ret = kstrtoint(buf, 0, &spi_freq);
	if (ret)
		return ret;

	if (!spi_freq)
		return -EINVAL;

	if (sample_freq) {
		ret = dfsdm_adc_set_samp_freq(indio_dev, sample_freq, spi_freq);
		if (ret < 0)
			return ret;
	}
	adc->spi_freq = spi_freq;

	return len;
}

static int stm32_dfsdm_start_conv(struct iio_dev *indio_dev,
				  struct iio_trigger *trig)
{
	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
	struct regmap *regmap = adc->dfsdm->regmap;
	int ret;

	ret = stm32_dfsdm_channels_configure(indio_dev, adc->fl_id, trig);
	if (ret < 0)
		return ret;

	ret = stm32_dfsdm_start_channel(indio_dev);
	if (ret < 0)
		return ret;

	ret = stm32_dfsdm_filter_configure(indio_dev, adc->fl_id, trig);
	if (ret < 0)
		goto stop_channels;

	ret = stm32_dfsdm_start_filter(adc, adc->fl_id, trig);
	if (ret < 0)
		goto filter_unconfigure;

	return 0;

filter_unconfigure:
	regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
			   DFSDM_CR1_CFG_MASK, 0);
stop_channels:
	stm32_dfsdm_stop_channel(indio_dev);

	return ret;
}

static void stm32_dfsdm_stop_conv(struct iio_dev *indio_dev)
{
	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
	struct regmap *regmap = adc->dfsdm->regmap;

	stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id);

	regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
			   DFSDM_CR1_CFG_MASK, 0);

	stm32_dfsdm_stop_channel(indio_dev);
}

static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev,
				     unsigned int val)
{
	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
	unsigned int watermark = DFSDM_DMA_BUFFER_SIZE / 2;
	unsigned int rx_buf_sz = DFSDM_DMA_BUFFER_SIZE;

	/*
	 * DMA cyclic transfers are used, buffer is split into two periods.
	 * There should be :
	 * - always one buffer (period) DMA is working on
	 * - one buffer (period) driver pushed to ASoC side.
	 */
	watermark = min(watermark, val * (unsigned int)(sizeof(u32)));
	adc->buf_sz = min(rx_buf_sz, watermark * 2 * adc->nconv);

	return 0;
}

static unsigned int stm32_dfsdm_adc_dma_residue(struct stm32_dfsdm_adc *adc)
{
	struct dma_tx_state state;
	enum dma_status status;

	status = dmaengine_tx_status(adc->dma_chan,
				     adc->dma_chan->cookie,
				     &state);
	if (status == DMA_IN_PROGRESS) {
		/* Residue is size in bytes from end of buffer */
		unsigned int i = adc->buf_sz - state.residue;
		unsigned int size;

		/* Return available bytes */
		if (i >= adc->bufi)
			size = i - adc->bufi;
		else
			size = adc->buf_sz + i - adc->bufi;

		return size;
	}

	return 0;
}

static inline void stm32_dfsdm_process_data(struct stm32_dfsdm_adc *adc,
					    s32 *buffer)
{
	struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
	struct stm32_dfsdm_filter_osr *flo = &fl->flo[fl->fast];
	unsigned int i = adc->nconv;
	s32 *ptr = buffer;

	while (i--) {
		/* Mask 8 LSB that contains the channel ID */
		*ptr &= 0xFFFFFF00;
		/* Convert 2^(n-1) sample to 2^(n-1)-1 to avoid wrap-around */
		if (*ptr > flo->max)
			*ptr -= 1;
		/*
		 * Samples from filter are retrieved with 23 bits resolution
		 * or less. Shift left to align MSB on 24 bits.
		 */
		*ptr <<= flo->lshift;

		ptr++;
	}
}

static void stm32_dfsdm_dma_buffer_done(void *data)
{
	struct iio_dev *indio_dev = data;
	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
	int available = stm32_dfsdm_adc_dma_residue(adc);
	size_t old_pos;

	/*
	 * FIXME: In Kernel interface does not support cyclic DMA buffer,and
	 * offers only an interface to push data samples per samples.
	 * For this reason IIO buffer interface is not used and interface is
	 * bypassed using a private callback registered by ASoC.
	 * This should be a temporary solution waiting a cyclic DMA engine
	 * support in IIO.
	 */

	dev_dbg(&indio_dev->dev, "pos = %d, available = %d\n",
		adc->bufi, available);
	old_pos = adc->bufi;

	while (available >= indio_dev->scan_bytes) {
		s32 *buffer = (s32 *)&adc->rx_buf[adc->bufi];

		stm32_dfsdm_process_data(adc, buffer);

		available -= indio_dev->scan_bytes;
		adc->bufi += indio_dev->scan_bytes;
		if (adc->bufi >= adc->buf_sz) {
			if (adc->cb)
				adc->cb(&adc->rx_buf[old_pos],
					 adc->buf_sz - old_pos, adc->cb_priv);
			adc->bufi = 0;
			old_pos = 0;
		}
		/*
		 * In DMA mode the trigger services of IIO are not used
		 * (e.g. no call to iio_trigger_poll).
		 * Calling irq handler associated to the hardware trigger is not
		 * relevant as the conversions have already been done. Data
		 * transfers are performed directly in DMA callback instead.
		 * This implementation avoids to call trigger irq handler that
		 * may sleep, in an atomic context (DMA irq handler context).
		 */
		if (adc->dev_data->type == DFSDM_IIO)
			iio_push_to_buffers(indio_dev, buffer);
	}
	if (adc->cb)
		adc->cb(&adc->rx_buf[old_pos], adc->bufi - old_pos,
			adc->cb_priv);
}

static int stm32_dfsdm_adc_dma_start(struct iio_dev *indio_dev)
{
	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
	/*
	 * The DFSDM supports half-word transfers. However, for 16 bits record,
	 * 4 bytes buswidth is kept, to avoid losing samples LSBs when left
	 * shift is required.
	 */
	struct dma_slave_config config = {
		.src_addr = (dma_addr_t)adc->dfsdm->phys_base,
		.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
	};
	struct dma_async_tx_descriptor *desc;
	dma_cookie_t cookie;
	int ret;

	if (!adc->dma_chan)
		return -EINVAL;

	dev_dbg(&indio_dev->dev, "size=%d watermark=%d\n",
		adc->buf_sz, adc->buf_sz / 2);

	if (adc->nconv == 1 && !indio_dev->trig)
		config.src_addr += DFSDM_RDATAR(adc->fl_id);
	else
		config.src_addr += DFSDM_JDATAR(adc->fl_id);
	ret = dmaengine_slave_config(adc->dma_chan, &config);
	if (ret)
		return ret;

	/* Prepare a DMA cyclic transaction */
	desc = dmaengine_prep_dma_cyclic(adc->dma_chan,
					 adc->dma_buf,
					 adc->buf_sz, adc->buf_sz / 2,
					 DMA_DEV_TO_MEM,
					 DMA_PREP_INTERRUPT);
	if (!desc)
		return -EBUSY;

	desc->callback = stm32_dfsdm_dma_buffer_done;
	desc->callback_param = indio_dev;

	cookie = dmaengine_submit(desc);
	ret = dma_submit_error(cookie);
	if (ret)
		goto err_stop_dma;

	/* Issue pending DMA requests */
	dma_async_issue_pending(adc->dma_chan);

	if (adc->nconv == 1 && !indio_dev->trig) {
		/* Enable regular DMA transfer*/
		ret = regmap_update_bits(adc->dfsdm->regmap,
					 DFSDM_CR1(adc->fl_id),
					 DFSDM_CR1_RDMAEN_MASK,
					 DFSDM_CR1_RDMAEN_MASK);
	} else {
		/* Enable injected DMA transfer*/
		ret = regmap_update_bits(adc->dfsdm->regmap,
					 DFSDM_CR1(adc->fl_id),
					 DFSDM_CR1_JDMAEN_MASK,
					 DFSDM_CR1_JDMAEN_MASK);
	}

	if (ret < 0)
		goto err_stop_dma;

	return 0;

err_stop_dma:
	dmaengine_terminate_all(adc->dma_chan);

	return ret;
}

static void stm32_dfsdm_adc_dma_stop(struct iio_dev *indio_dev)
{
	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);

	if (!adc->dma_chan)
		return;

	regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR1(adc->fl_id),
			   DFSDM_CR1_RDMAEN_MASK | DFSDM_CR1_JDMAEN_MASK, 0);
	dmaengine_terminate_all(adc->dma_chan);
}

static int stm32_dfsdm_update_scan_mode(struct iio_dev *indio_dev,
					const unsigned long *scan_mask)
{
	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);

	adc->nconv = bitmap_weight(scan_mask, indio_dev->masklength);
	adc->smask = *scan_mask;

	dev_dbg(&indio_dev->dev, "nconv=%d mask=%lx\n", adc->nconv, *scan_mask);

	return 0;
}

static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
{
	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
	int ret;

	/* Reset adc buffer index */
	adc->bufi = 0;

	if (adc->hwc) {
		ret = iio_hw_consumer_enable(adc->hwc);
		if (ret < 0)
			return ret;
	}

	ret = stm32_dfsdm_start_dfsdm(adc->dfsdm);
	if (ret < 0)
		goto err_stop_hwc;

	ret = stm32_dfsdm_adc_dma_start(indio_dev);
	if (ret) {
		dev_err(&indio_dev->dev, "Can't start DMA\n");
		goto stop_dfsdm;
	}

	ret = stm32_dfsdm_start_conv(indio_dev, indio_dev->trig);
	if (ret) {
		dev_err(&indio_dev->dev, "Can't start conversion\n");
		goto err_stop_dma;
	}

	return 0;

err_stop_dma:
	stm32_dfsdm_adc_dma_stop(indio_dev);
stop_dfsdm:
	stm32_dfsdm_stop_dfsdm(adc->dfsdm);
err_stop_hwc:
	if (adc->hwc)
		iio_hw_consumer_disable(adc->hwc);

	return ret;
}

static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
{
	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);

	stm32_dfsdm_stop_conv(indio_dev);

	stm32_dfsdm_adc_dma_stop(indio_dev);

	stm32_dfsdm_stop_dfsdm(adc->dfsdm);

	if (adc->hwc)
		iio_hw_consumer_disable(adc->hwc);

	return 0;
}

static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = {
	.postenable = &stm32_dfsdm_postenable,
	.predisable = &stm32_dfsdm_predisable,
};

/**
 * stm32_dfsdm_get_buff_cb() - register a callback that will be called when
 *                             DMA transfer period is achieved.
 *
 * @iio_dev: Handle to IIO device.
 * @cb: Pointer to callback function:
 *      - data: pointer to data buffer
 *      - size: size in byte of the data buffer
 *      - private: pointer to consumer private structure.
 * @private: Pointer to consumer private structure.
 */
int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
			    int (*cb)(const void *data, size_t size,
				      void *private),
			    void *private)
{
	struct stm32_dfsdm_adc *adc;

	if (!iio_dev)
		return -EINVAL;
	adc = iio_priv(iio_dev);

	adc->cb = cb;
	adc->cb_priv = private;

	return 0;
}
EXPORT_SYMBOL_GPL(stm32_dfsdm_get_buff_cb);

/**
 * stm32_dfsdm_release_buff_cb - unregister buffer callback
 *
 * @iio_dev: Handle to IIO device.
 */
int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev)
{
	struct stm32_dfsdm_adc *adc;

	if (!iio_dev)
		return -EINVAL;
	adc = iio_priv(iio_dev);

	adc->cb = NULL;
	adc->cb_priv = NULL;

	return 0;
}
EXPORT_SYMBOL_GPL(stm32_dfsdm_release_buff_cb);

static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
				   const struct iio_chan_spec *chan, int *res)
{
	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
	long timeout;
	int ret;

	reinit_completion(&adc->completion);

	adc->buffer = res;

	ret = stm32_dfsdm_start_dfsdm(adc->dfsdm);
	if (ret < 0)
		return ret;

	ret = regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
				 DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(1));
	if (ret < 0)
		goto stop_dfsdm;

	adc->nconv = 1;
	adc->smask = BIT(chan->scan_index);
	ret = stm32_dfsdm_start_conv(indio_dev, NULL);
	if (ret < 0) {
		regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
				   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
		goto stop_dfsdm;
	}

	timeout = wait_for_completion_interruptible_timeout(&adc->completion,
							    DFSDM_TIMEOUT);

	/* Mask IRQ for regular conversion achievement*/
	regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));

	if (timeout == 0)
		ret = -ETIMEDOUT;
	else if (timeout < 0)
		ret = timeout;
	else
		ret = IIO_VAL_INT;

	stm32_dfsdm_stop_conv(indio_dev);

	stm32_dfsdm_process_data(adc, res);

stop_dfsdm:
	stm32_dfsdm_stop_dfsdm(adc->dfsdm);

	return ret;
}

static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,
				 struct iio_chan_spec const *chan,
				 int val, int val2, long mask)
{
	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
	struct stm32_dfsdm_channel *ch = &adc->dfsdm->ch_list[chan->channel];
	unsigned int spi_freq;
	int ret = -EINVAL;

	switch (ch->src) {
	case DFSDM_CHANNEL_SPI_CLOCK_INTERNAL:
		spi_freq = adc->dfsdm->spi_master_freq;
		break;
	case DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING:
	case DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING:
		spi_freq = adc->dfsdm->spi_master_freq / 2;
		break;
	default:
		spi_freq = adc->spi_freq;
	}

	switch (mask) {
	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
		ret = iio_device_claim_direct_mode(indio_dev);
		if (ret)
			return ret;

		ret = stm32_dfsdm_compute_all_osrs(indio_dev, val);
		if (!ret) {
			dev_dbg(&indio_dev->dev,
				"Sampling rate changed from (%u) to (%u)\n",
				adc->sample_freq, spi_freq / val);
			adc->oversamp = val;
			adc->sample_freq = spi_freq / val;
		}
		iio_device_release_direct_mode(indio_dev);
		return ret;

	case IIO_CHAN_INFO_SAMP_FREQ:
		if (!val)
			return -EINVAL;

		ret = iio_device_claim_direct_mode(indio_dev);
		if (ret)
			return ret;

		ret = dfsdm_adc_set_samp_freq(indio_dev, val, spi_freq);
		iio_device_release_direct_mode(indio_dev);
		return ret;
	}

	return -EINVAL;
}

static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
				struct iio_chan_spec const *chan, int *val,
				int *val2, long mask)
{
	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
	int ret;

	switch (mask) {
	case IIO_CHAN_INFO_RAW:
		ret = iio_device_claim_direct_mode(indio_dev);
		if (ret)
			return ret;
		ret = iio_hw_consumer_enable(adc->hwc);
		if (ret < 0) {
			dev_err(&indio_dev->dev,
				"%s: IIO enable failed (channel %d)\n",
				__func__, chan->channel);
			iio_device_release_direct_mode(indio_dev);
			return ret;
		}
		ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
		iio_hw_consumer_disable(adc->hwc);
		if (ret < 0) {
			dev_err(&indio_dev->dev,
				"%s: Conversion failed (channel %d)\n",
				__func__, chan->channel);
			iio_device_release_direct_mode(indio_dev);
			return ret;
		}
		iio_device_release_direct_mode(indio_dev);
		return IIO_VAL_INT;

	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
		*val = adc->oversamp;

		return IIO_VAL_INT;

	case IIO_CHAN_INFO_SAMP_FREQ:
		*val = adc->sample_freq;

		return IIO_VAL_INT;
	}

	return -EINVAL;
}

static int stm32_dfsdm_validate_trigger(struct iio_dev *indio_dev,
					struct iio_trigger *trig)
{
	return stm32_dfsdm_get_jextsel(indio_dev, trig) < 0 ? -EINVAL : 0;
}

static const struct iio_info stm32_dfsdm_info_audio = {
	.hwfifo_set_watermark = stm32_dfsdm_set_watermark,
	.read_raw = stm32_dfsdm_read_raw,
	.write_raw = stm32_dfsdm_write_raw,
	.update_scan_mode = stm32_dfsdm_update_scan_mode,
};

static const struct iio_info stm32_dfsdm_info_adc = {
	.hwfifo_set_watermark = stm32_dfsdm_set_watermark,
	.read_raw = stm32_dfsdm_read_raw,
	.write_raw = stm32_dfsdm_write_raw,
	.update_scan_mode = stm32_dfsdm_update_scan_mode,
	.validate_trigger = stm32_dfsdm_validate_trigger,
};

static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
{
	struct iio_dev *indio_dev = arg;
	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
	struct regmap *regmap = adc->dfsdm->regmap;
	unsigned int status, int_en;

	regmap_read(regmap, DFSDM_ISR(adc->fl_id), &status);
	regmap_read(regmap, DFSDM_CR2(adc->fl_id), &int_en);

	if (status & DFSDM_ISR_REOCF_MASK) {
		/* Read the data register clean the IRQ status */
		regmap_read(regmap, DFSDM_RDATAR(adc->fl_id), adc->buffer);
		complete(&adc->completion);
	}

	if (status & DFSDM_ISR_ROVRF_MASK) {
		if (int_en & DFSDM_CR2_ROVRIE_MASK)
			dev_warn(&indio_dev->dev, "Overrun detected\n");
		regmap_update_bits(regmap, DFSDM_ICR(adc->fl_id),
				   DFSDM_ICR_CLRROVRF_MASK,
				   DFSDM_ICR_CLRROVRF_MASK);
	}

	return IRQ_HANDLED;
}

/*
 * Define external info for SPI Frequency and audio sampling rate that can be
 * configured by ASoC driver through consumer.h API
 */
static const struct iio_chan_spec_ext_info dfsdm_adc_audio_ext_info[] = {
	/* spi_clk_freq : clock freq on SPI/manchester bus used by channel */
	{
		.name = "spi_clk_freq",
		.shared = IIO_SHARED_BY_TYPE,
		.read = dfsdm_adc_audio_get_spiclk,
		.write = dfsdm_adc_audio_set_spiclk,
	},
	{},
};

static void stm32_dfsdm_dma_release(struct iio_dev *indio_dev)
{
	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);

	if (adc->dma_chan) {
		dma_free_coherent(adc->dma_chan->device->dev,
				  DFSDM_DMA_BUFFER_SIZE,
				  adc->rx_buf, adc->dma_buf);
		dma_release_channel(adc->dma_chan);
	}
}

static int stm32_dfsdm_dma_request(struct device *dev,
				   struct iio_dev *indio_dev)
{
	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);

	adc->dma_chan = dma_request_chan(dev, "rx");
	if (IS_ERR(adc->dma_chan)) {
		int ret = PTR_ERR(adc->dma_chan);

		adc->dma_chan = NULL;
		return ret;
	}

	adc->rx_buf = dma_alloc_coherent(adc->dma_chan->device->dev,
					 DFSDM_DMA_BUFFER_SIZE,
					 &adc->dma_buf, GFP_KERNEL);
	if (!adc->rx_buf) {
		dma_release_channel(adc->dma_chan);
		return -ENOMEM;
	}

	indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
	indio_dev->setup_ops = &stm32_dfsdm_buffer_setup_ops;

	return 0;
}

static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
					 struct iio_chan_spec *ch)
{
	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
	int ret;

	ret = stm32_dfsdm_channel_parse_of(adc->dfsdm, indio_dev, ch);
	if (ret < 0)
		return ret;

	ch->type = IIO_VOLTAGE;
	ch->indexed = 1;

	/*
	 * IIO_CHAN_INFO_RAW: used to compute regular conversion
	 * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used to set oversampling
	 */
	ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
	ch->info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
					BIT(IIO_CHAN_INFO_SAMP_FREQ);

	if (adc->dev_data->type == DFSDM_AUDIO) {
		ch->ext_info = dfsdm_adc_audio_ext_info;
	} else {
		ch->scan_type.shift = 8;
	}
	ch->scan_type.sign = 's';
	ch->scan_type.realbits = 24;
	ch->scan_type.storagebits = 32;

	return stm32_dfsdm_chan_configure(adc->dfsdm,
					  &adc->dfsdm->ch_list[ch->channel]);
}

static int stm32_dfsdm_audio_init(struct device *dev, struct iio_dev *indio_dev)
{
	struct iio_chan_spec *ch;
	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
	struct stm32_dfsdm_channel *d_ch;
	int ret;

	ch = devm_kzalloc(&indio_dev->dev, sizeof(*ch), GFP_KERNEL);
	if (!ch)
		return -ENOMEM;

	ch->scan_index = 0;

	ret = stm32_dfsdm_adc_chan_init_one(indio_dev, ch);
	if (ret < 0) {
		dev_err(&indio_dev->dev, "Channels init failed\n");
		return ret;
	}
	ch->info_mask_separate = BIT(IIO_CHAN_INFO_SAMP_FREQ);

	d_ch = &adc->dfsdm->ch_list[ch->channel];
	if (d_ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
		adc->spi_freq = adc->dfsdm->spi_master_freq;

	indio_dev->num_channels = 1;
	indio_dev->channels = ch;

	return stm32_dfsdm_dma_request(dev, indio_dev);
}

static int stm32_dfsdm_adc_init(struct device *dev, struct iio_dev *indio_dev)
{
	struct iio_chan_spec *ch;
	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
	int num_ch;
	int ret, chan_idx;

	adc->oversamp = DFSDM_DEFAULT_OVERSAMPLING;
	ret = stm32_dfsdm_compute_all_osrs(indio_dev, adc->oversamp);
	if (ret < 0)
		return ret;

	num_ch = of_property_count_u32_elems(indio_dev->dev.of_node,
					     "st,adc-channels");
	if (num_ch < 0 || num_ch > adc->dfsdm->num_chs) {
		dev_err(&indio_dev->dev, "Bad st,adc-channels\n");
		return num_ch < 0 ? num_ch : -EINVAL;
	}

	/* Bind to SD modulator IIO device */
	adc->hwc = devm_iio_hw_consumer_alloc(&indio_dev->dev);
	if (IS_ERR(adc->hwc))
		return -EPROBE_DEFER;

	ch = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*ch),
			  GFP_KERNEL);
	if (!ch)
		return -ENOMEM;

	for (chan_idx = 0; chan_idx < num_ch; chan_idx++) {
		ch[chan_idx].scan_index = chan_idx;
		ret = stm32_dfsdm_adc_chan_init_one(indio_dev, &ch[chan_idx]);
		if (ret < 0) {
			dev_err(&indio_dev->dev, "Channels init failed\n");
			return ret;
		}
	}

	indio_dev->num_channels = num_ch;
	indio_dev->channels = ch;

	init_completion(&adc->completion);

	/* Optionally request DMA */
	ret = stm32_dfsdm_dma_request(dev, indio_dev);
	if (ret) {
		if (ret != -ENODEV)
			return dev_err_probe(dev, ret,
					     "DMA channel request failed with\n");

		dev_dbg(dev, "No DMA support\n");
		return 0;
	}

	ret = iio_triggered_buffer_setup(indio_dev,
					 &iio_pollfunc_store_time, NULL,
					 &stm32_dfsdm_buffer_setup_ops);
	if (ret) {
		stm32_dfsdm_dma_release(indio_dev);
		dev_err(&indio_dev->dev, "buffer setup failed\n");
		return ret;
	}

	/* lptimer/timer hardware triggers */
	indio_dev->modes |= INDIO_HARDWARE_TRIGGERED;

	return 0;
}

static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_adc_data = {
	.type = DFSDM_IIO,
	.init = stm32_dfsdm_adc_init,
};

static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_audio_data = {
	.type = DFSDM_AUDIO,
	.init = stm32_dfsdm_audio_init,
};

static const struct of_device_id stm32_dfsdm_adc_match[] = {
	{
		.compatible = "st,stm32-dfsdm-adc",
		.data = &stm32h7_dfsdm_adc_data,
	},
	{
		.compatible = "st,stm32-dfsdm-dmic",
		.data = &stm32h7_dfsdm_audio_data,
	},
	{}
};
MODULE_DEVICE_TABLE(of, stm32_dfsdm_adc_match);

static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct stm32_dfsdm_adc *adc;
	struct device_node *np = dev->of_node;
	const struct stm32_dfsdm_dev_data *dev_data;
	struct iio_dev *iio;
	char *name;
	int ret, irq, val;

	dev_data = of_device_get_match_data(dev);
	iio = devm_iio_device_alloc(dev, sizeof(*adc));
	if (!iio) {
		dev_err(dev, "%s: Failed to allocate IIO\n", __func__);
		return -ENOMEM;
	}

	adc = iio_priv(iio);
	adc->dfsdm = dev_get_drvdata(dev->parent);

	iio->dev.of_node = np;
	iio->modes = INDIO_DIRECT_MODE;

	platform_set_drvdata(pdev, iio);

	ret = of_property_read_u32(dev->of_node, "reg", &adc->fl_id);
	if (ret != 0 || adc->fl_id >= adc->dfsdm->num_fls) {
		dev_err(dev, "Missing or bad reg property\n");
		return -EINVAL;
	}

	name = devm_kzalloc(dev, sizeof("dfsdm-adc0"), GFP_KERNEL);
	if (!name)
		return -ENOMEM;
	if (dev_data->type == DFSDM_AUDIO) {
		iio->info = &stm32_dfsdm_info_audio;
		snprintf(name, sizeof("dfsdm-pdm0"), "dfsdm-pdm%d", adc->fl_id);
	} else {
		iio->info = &stm32_dfsdm_info_adc;
		snprintf(name, sizeof("dfsdm-adc0"), "dfsdm-adc%d", adc->fl_id);
	}
	iio->name = name;

	/*
	 * In a first step IRQs generated for channels are not treated.
	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
	 */
	irq = platform_get_irq(pdev, 0);
	if (irq < 0)
		return irq;

	ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
			       0, pdev->name, iio);
	if (ret < 0) {
		dev_err(dev, "Failed to request IRQ\n");
		return ret;
	}

	ret = of_property_read_u32(dev->of_node, "st,filter-order", &val);
	if (ret < 0) {
		dev_err(dev, "Failed to set filter order\n");
		return ret;
	}

	adc->dfsdm->fl_list[adc->fl_id].ford = val;

	ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val);
	if (!ret)
		adc->dfsdm->fl_list[adc->fl_id].sync_mode = val;

	adc->dev_data = dev_data;
	ret = dev_data->init(dev, iio);
	if (ret < 0)
		return ret;

	ret = iio_device_register(iio);
	if (ret < 0)
		goto err_cleanup;

	if (dev_data->type == DFSDM_AUDIO) {
		ret = of_platform_populate(np, NULL, NULL, dev);
		if (ret < 0) {
			dev_err(dev, "Failed to find an audio DAI\n");
			goto err_unregister;
		}
	}

	return 0;

err_unregister:
	iio_device_unregister(iio);
err_cleanup:
	stm32_dfsdm_dma_release(iio);

	return ret;
}

static int stm32_dfsdm_adc_remove(struct platform_device *pdev)
{
	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);

	if (adc->dev_data->type == DFSDM_AUDIO)
		of_platform_depopulate(&pdev->dev);
	iio_device_unregister(indio_dev);
	stm32_dfsdm_dma_release(indio_dev);

	return 0;
}

static int stm32_dfsdm_adc_suspend(struct device *dev)
{
	struct iio_dev *indio_dev = dev_get_drvdata(dev);

	if (iio_buffer_enabled(indio_dev))
		stm32_dfsdm_predisable(indio_dev);

	return 0;
}

static int stm32_dfsdm_adc_resume(struct device *dev)
{
	struct iio_dev *indio_dev = dev_get_drvdata(dev);
	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
	const struct iio_chan_spec *chan;
	struct stm32_dfsdm_channel *ch;
	int i, ret;

	/* restore channels configuration */
	for (i = 0; i < indio_dev->num_channels; i++) {
		chan = indio_dev->channels + i;
		ch = &adc->dfsdm->ch_list[chan->channel];
		ret = stm32_dfsdm_chan_configure(adc->dfsdm, ch);
		if (ret)
			return ret;
	}

	if (iio_buffer_enabled(indio_dev))
		stm32_dfsdm_postenable(indio_dev);

	return 0;
}

static DEFINE_SIMPLE_DEV_PM_OPS(stm32_dfsdm_adc_pm_ops,
				stm32_dfsdm_adc_suspend,
				stm32_dfsdm_adc_resume);

static struct platform_driver stm32_dfsdm_adc_driver = {
	.driver = {
		.name = "stm32-dfsdm-adc",
		.of_match_table = stm32_dfsdm_adc_match,
		.pm = pm_sleep_ptr(&stm32_dfsdm_adc_pm_ops),
	},
	.probe = stm32_dfsdm_adc_probe,
	.remove = stm32_dfsdm_adc_remove,
};
module_platform_driver(stm32_dfsdm_adc_driver);

MODULE_DESCRIPTION("STM32 sigma delta ADC");
MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
MODULE_LICENSE("GPL v2");
