linux-stable/sound/soc
Luca Ceresoli d23e49f4e4 ASoC: rockchip: i2s-tdm: Fix inaccurate sampling rates
[ Upstream commit 9e2ab4b18e ]

The sample rates set by the rockchip_i2s_tdm driver in master mode are
inaccurate up to 5% in several cases, due to the driver logic to configure
clocks and a nasty interaction with the Common Clock Framework.

To understand what happens, here is the relevant section of the clock tree
(slightly simplified), along with the names used in the driver:

       vpll0 _OR_ vpll1               "mclk_root"
          clk_i2s2_8ch_tx_src         "mclk_parent"
             clk_i2s2_8ch_tx_mux
                clk_i2s2_8ch_tx       "mclk" or "mclk_tx"

This is what happens when playing back e.g. at 192 kHz using
audio-graph-card (when recording the same applies, only s/tx/rx/):

 0. at probe, rockchip_i2s_tdm_set_sysclk() stores the passed frequency in
    i2s_tdm->mclk_tx_freq (*) which is 50176000, and that is never modified
    afterwards

 1. when playback is started, rockchip_i2s_tdm_hw_params() is called and
    does the following two calls

 2. rockchip_i2s_tdm_calibrate_mclk():

    2a. selects mclk_root0 (vpll0) as a parent for mclk_parent
        (mclk_tx_src), which is OK because the vpll0 rate is a good for
        192000 (and sumbultiple) rates

    2b. sets the mclk_root frequency based on ppm calibration computations

    2c. sets mclk_tx_src to 49152000 (= 256 * 192000), which is also OK as
        it is a multiple of the required bit clock

 3. rockchip_i2s_tdm_set_mclk()

    3a. calls clk_set_rate() to set the rate of mclk_tx (clk_i2s2_8ch_tx)
        to the value of i2s_tdm->mclk_tx_freq (*), i.e. 50176000 which is
        not a multiple of the sampling frequency -- this is not OK

        3a1. clk_set_rate() reacts by reparenting clk_i2s2_8ch_tx_src to
             vpll1 -- this is not OK because the default vpll1 rate can be
	     divided to get 44.1 kHz and related rates, not 192 kHz

The result is that the driver does a lot of ad-hoc decisions about clocks
and ends up in using the wrong parent at an unoptimal rate.

Step 0 is one part of the problem: unless the card driver calls set_sysclk
at each stream start, whatever rate is set in mclk_tx_freq during boot will
be taken and used until reboot. Moreover the driver does not care if its
value is not a multiple of any audio frequency.

Another part of the problem is that the whole reparenting and clock rate
setting logic is conflicting with the CCF algorithms to achieve largely the
same goal: selecting the best parent and setting the closest clock
rate. And it turns out that only calling once clk_set_rate() on
clk_i2s2_8ch_tx picks the correct vpll and sets the correct rate.

The fix is based on removing the custom logic in the driver to select the
parent and set the various clocks, and just let the Clock Framework do it
all. As a side effect, the set_sysclk() op becomes useless because we now
let the CCF compute the appropriate value for the sampling rate.  It also
implies that the whole calibration logic is now dead code and so it is
removed along with the "PCM Clock Compensation in PPM" kcontrol, which has
always been broken anyway. The handling of the 4 optional clocks also
becomes dead code and is removed.

The actual rates have been tested playing 30 seconds of audio at various
sampling rates before and after this change using sox:

    time play -r <sample_rate> -n synth 30 sine 950 gain -3

The time reported in the table below is the 'real' value reported by the
'time' command in the above command line.

     rate        before     after
   ---------     ------     ------
     8000 Hz     30.60s     30.63s
    11025 Hz     30.45s     30.51s
    16000 Hz     30.47s     30.50s
    22050 Hz     30.78s     30.41s
    32000 Hz     31.02s     30.43s
    44100 Hz     30.78s     30.41s
    48000 Hz     29.81s     30.45s
    88200 Hz     30.78s     30.41s
    96000 Hz     29.79s     30.42s
   176400 Hz     27.40s     30.41s
   192000 Hz     29.79s     30.42s

While the tests are running the clock tree confirms that:

 * without the patch, vpll1 is always used and clk_i2s2_8ch_tx always
   produces 50176000 Hz, which cannot be divided for most audio rates
   except the slowest ones, generating inaccurate rates
 * with the patch:
   - for 192000 Hz vpll0 is used
   - for 176400 Hz vpll1 is used
   - clk_i2s2_8ch_tx always produces (256 * <rate>) Hz

Tested on the RK3308 using the internal audio codec.

Fixes: 081068fd64 ("ASoC: rockchip: add support for i2s-tdm controller")
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
Link: https://msgid.link/r/20240305-rk3308-audio-codec-v4-1-312acdbe628f@bootlin.com
Signed-off-by: Mark Brown <broonie@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
2024-03-26 18:20:58 -04:00
..
adi
amd ASoC: amd: acp: Add missing error handling in sof-mach 2024-03-26 18:20:45 -04:00
apple ASoC: apple: mca: Improve handling of unavailable DMA channels 2023-03-11 13:55:31 +01:00
atmel ASoC: atmel: Fix the 8K sample parameter in I2SC master 2023-09-13 09:42:22 +02:00
au1x
bcm
cirrus
codecs ASoC: wm8962: Fix up incorrect error message in wm8962_set_fll 2024-03-26 18:20:27 -04:00
dwc ASoC: dwc: move DMA init to snd_soc_dai_driver probe() 2023-06-21 16:00:53 +02:00
fsl ASoC: fsl_rpmsg: Fix error handler with pm_runtime_enable 2024-01-10 17:10:23 +01:00
generic ASoC: simple-card: fixup asoc_simple_probe() error handling 2023-11-08 14:10:57 +01:00
hisilicon
img
intel ASoC: Intel: bytcr_rt5640: Add an extra entry for the Chuwi Vi8 tablet 2024-03-26 18:20:27 -04:00
jz4740 ASoC: jz4740-i2s: Handle independent FIFO flush bits 2023-01-07 11:11:51 +01:00
kirkwood ASoC: kirkwood: Iterate over array indexes instead of using pointer math 2023-03-10 09:33:58 +01:00
mediatek ASoC: mediatek: sof-common: Add NULL check for normal_link string 2024-01-25 15:27:48 -08:00
meson ASoC: meson: axg-tdm-interface: add frame rate constraint 2024-03-26 18:20:51 -04:00
mxs
pxa ASoC: pxa: fix a memory leak in probe() 2023-10-25 12:03:15 +02:00
qcom ASoC: qcom: q6apm: do not close GPR port before closing graph 2023-07-27 08:50:37 +02:00
rockchip ASoC: rockchip: i2s-tdm: Fix inaccurate sampling rates 2024-03-26 18:20:58 -04:00
samsung
sh ASoC: Use of_property_read_bool() for boolean properties 2023-10-19 23:08:51 +02:00
sof ASoC: SOF: Add some bounds checking to firmware data 2024-03-26 18:20:48 -04:00
spear
sprd
sti
stm
sunxi ASoC: sunxi: sun4i-spdif: Add support for Allwinner H616 2024-03-01 13:26:27 +01:00
tegra ASoC: tegra: Fix redundant PLLA and PLLA_OUT0 updates 2023-10-10 22:00:34 +02:00
ti ASoC: ti: omap-mcbsp: Fix runtime PM underflow warnings 2023-11-28 17:07:02 +00:00
uniphier
ux500
xilinx
xtensa
Kconfig
Makefile
soc-ac97.c
soc-acpi.c
soc-card.c
soc-component.c
soc-compress.c ASoC: soc-compress: Inherit atomicity from DAI link for Compress FE 2023-05-11 23:03:28 +09:00
soc-core.c
soc-dai.c
soc-dapm.c ASoC: dapm: fix clock get name 2023-11-20 11:52:18 +01:00
soc-devres.c
soc-generic-dmaengine-pcm.c
soc-jack.c
soc-link.c
soc-ops.c ASoC: ops: add correct range check for limiting volume 2024-01-20 11:50:06 +01:00
soc-pcm.c ASoC: soc-pcm: test if a BE can be prepared 2023-06-21 16:00:53 +02:00
soc-topology-test.c
soc-topology.c ASoC: topology: Properly access value coming from topology file 2023-03-10 09:33:18 +01:00
soc-utils-test.c
soc-utils.c ASoC: soc-utils: Export snd_soc_dai_is_dummy() symbol 2023-10-10 22:00:34 +02:00