// 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>.
*/
/* 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
/* Audio specific */ unsignedint spi_freq; /* SPI bus clock frequency */ unsignedint sample_freq; /* Sample frequency after filter decimation */ int (*cb)(constvoid *data, size_t size, void *cb_priv); void *cb_priv;
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 { constchar *name; unsignedint jextsel;
};
staticint 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;
}
staticint stm32_dfsdm_compute_osrs(struct stm32_dfsdm_filter *fl, unsignedint fast, unsignedint oversamp)
{ unsignedint i, d, fosr, iosr;
u64 res, max; int bits, shift; unsignedint m = 1; /* multiplication factor */ unsignedint 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; elseif (fl->ford == DFSDM_FASTSINC_ORDER)
d = fosr * (iosr + 3) + 2; else
d = fosr * (iosr - 1 + p) + p;
if (d > oversamp) break; elseif (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;
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;
/* * 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));
/* 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;
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;
/* * 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 * (unsignedint)(sizeof(u32)));
adc->buf_sz = min(rx_buf_sz, watermark * 2 * adc->nconv);
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 */ unsignedint i = adc->buf_sz - state.residue; unsignedint size;
/* Return available bytes */ if (i >= adc->bufi)
size = i - adc->bufi; else
size = adc->buf_sz + i - adc->bufi;
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++;
}
}
staticvoid 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.
*/
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);
}
staticint 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;
/** * 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)(constvoid *data, size_t size, void *private), void *private)
{ struct stm32_dfsdm_adc *adc;
if (!iio_dev) return -EINVAL;
adc = iio_priv(iio_dev);
if (time_left == 0)
ret = -ETIMEDOUT; elseif (time_left < 0)
ret = time_left; 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;
}
staticint 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]; unsignedint 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: if (!iio_device_claim_direct(indio_dev)) return -EBUSY;
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(indio_dev); return ret;
case IIO_CHAN_INFO_SAMP_FREQ: if (!val) return -EINVAL;
if (!iio_device_claim_direct(indio_dev)) return -EBUSY;
ret = dfsdm_adc_set_samp_freq(indio_dev, val, spi_freq);
iio_device_release_direct(indio_dev); return ret;
}
return -EINVAL;
}
staticint __stm32_dfsdm_read_info_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val)
{ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); int ret = 0;
if (adc->hwc)
ret = iio_hw_consumer_enable(adc->hwc); if (adc->backend)
ret = iio_backend_enable(adc->backend[chan->scan_index]); if (ret < 0) {
dev_err(&indio_dev->dev, "%s: IIO enable failed (channel %d)\n",
__func__, chan->channel); return ret;
}
ret = stm32_dfsdm_single_conv(indio_dev, chan, val); if (adc->hwc)
iio_hw_consumer_disable(adc->hwc); if (adc->backend)
iio_backend_disable(adc->backend[chan->scan_index]); if (ret < 0) {
dev_err(&indio_dev->dev, "%s: Conversion failed (channel %d)\n",
__func__, chan->channel); return ret;
}
return 0;
}
staticint 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);
struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id]; struct stm32_dfsdm_filter_osr *flo = &fl->flo[fl->fast];
u32 max = flo->max << (flo->lshift - chan->scan_type.shift); int idx = chan->scan_index; int ret;
if (flo->lshift < chan->scan_type.shift)
max = flo->max >> (chan->scan_type.shift - flo->lshift);
switch (mask) { case IIO_CHAN_INFO_RAW: if (!iio_device_claim_direct(indio_dev)) return -EBUSY;
ret = __stm32_dfsdm_read_info_raw(indio_dev, chan, val);
iio_device_release_direct(indio_dev); if (ret) return ret; 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;
case IIO_CHAN_INFO_SCALE: /* * Scale is expressed in mV. * When fast mode is disabled, actual resolution may be lower * than 2^n, where n = realbits - 1. * This leads to underestimating the input voltage. * To compensate this deviation, the voltage reference can be * corrected with a factor = realbits resolution / actual max
*/ if (adc->backend) {
ret = iio_backend_read_scale(adc->backend[idx], chan, val, NULL); if (ret < 0) return ret;
case IIO_CHAN_INFO_OFFSET: /* * DFSDM output data are in the range [-2^n, 2^n], * with n = realbits - 1. * - Differential modulator: * Offset correspond to SD modulator offset. * - Single ended modulator: * Input is in [0V, Vref] range, * where 0V corresponds to -2^n, and Vref to 2^n. * Add 2^n to offset. (i.e. middle of input range) * offset = offset(sd) * vref / res(sd) * max / vref.
*/ if (adc->backend) {
ret = iio_backend_read_offset(adc->backend[idx], chan, val, NULL); if (ret < 0) return ret;
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_set_bits(regmap, DFSDM_ICR(adc->fl_id),
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
*/ staticconststruct 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,
},
{ }
};
/* If st,adc-channels is defined legacy binding is used. Else assume generic binding. */
num_ch = of_property_count_u32_elems(indio_dev->dev.of_node, "st,adc-channels"); if (num_ch == 1)
legacy = true;
ch = devm_kzalloc(&indio_dev->dev, sizeof(*ch), GFP_KERNEL); if (!ch) return -ENOMEM;
/* * 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;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.