|  | // 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, | 
|  | }, | 
|  | {} | 
|  | }; | 
|  |  | 
|  | 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"); |