iio: adc: stm32-dfsdm: improve sampling frequency accuracy
authorFabrice Gasnier <fabrice.gasnier@st.com>
Mon, 25 Mar 2019 14:24:01 +0000 (15:24 +0100)
committerJonathan Cameron <Jonathan.Cameron@huawei.com>
Thu, 4 Apr 2019 19:21:10 +0000 (20:21 +0100)
The sample frequency is driven using the oversampling ratio depending
on the SPI bus frequency.
Currently, oversampling ratio is computed by an entire division:
- spi_freq / sample_freq. This may result in inaccurate value.
Using DIV_ROUND_CLOSEST improves resulting sample frequency, which is
useful for audio that requests fixed rates (such as: 8, 16 or 32 kHz).
BTW, introduce new routine to re-factor sample frequency setting, and
move frequency accuracy message from warning to debug level.

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
drivers/iio/adc/stm32-dfsdm-adc.c

index 531ca7ef086ed6e8b8bf0a19210027d6181c2e22..051561c1357ad8be011cb7208b476327c10515c7 100644 (file)
@@ -558,13 +558,38 @@ static ssize_t dfsdm_adc_audio_get_spiclk(struct iio_dev *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);
+       struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
+       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_set_osrs(fl, 0, oversamp);
+       if (ret < 0) {
+               dev_err(&indio_dev->dev, "No filter parameters that match!\n");
+               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_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
        struct stm32_dfsdm_channel *ch = &adc->dfsdm->ch_list[chan->channel];
        unsigned int sample_freq = adc->sample_freq;
        unsigned int spi_freq;
@@ -583,17 +608,9 @@ static ssize_t dfsdm_adc_audio_set_spiclk(struct iio_dev *indio_dev,
                return -EINVAL;
 
        if (sample_freq) {
-               if (spi_freq % sample_freq)
-                       dev_warn(&indio_dev->dev,
-                                "Sampling rate not accurate (%d)\n",
-                                spi_freq / (spi_freq / sample_freq));
-
-               ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
-               if (ret < 0) {
-                       dev_err(&indio_dev->dev,
-                               "No filter parameters that match!\n");
+               ret = dfsdm_adc_set_samp_freq(indio_dev, sample_freq, spi_freq);
+               if (ret < 0)
                        return ret;
-               }
        }
        adc->spi_freq = spi_freq;
 
@@ -1068,22 +1085,9 @@ static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,
                        spi_freq = adc->spi_freq;
                }
 
-               if (spi_freq % val)
-                       dev_warn(&indio_dev->dev,
-                                "Sampling rate not accurate (%d)\n",
-                                spi_freq / (spi_freq / val));
-
-               ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / val));
-               if (ret < 0) {
-                       dev_err(&indio_dev->dev,
-                               "Not able to find parameter that match!\n");
-                       iio_device_release_direct_mode(indio_dev);
-                       return ret;
-               }
-               adc->sample_freq = val;
+               ret = dfsdm_adc_set_samp_freq(indio_dev, val, spi_freq);
                iio_device_release_direct_mode(indio_dev);
-
-               return 0;
+               return ret;
        }
 
        return -EINVAL;