ASoC: rockchip: i2s-tdm: Fix 1-Bit offset case

There is a ASYNC bridge between HCLK and SCLK domain.

On AUPLL case, we found RX data shift ahead 1-Bit on
TRCM-TX sometime (0.00x%), but it success on GPLL(50w+).

The root cause:

HCLK Domain: Config the XFER-3, pull up SIGNAL to HIGH.

SCLK Domain: SCLK_RX/TX samples the HIGH level to start.

Because HCLK Domain is async to SCLK Domain, So, there
is a risk that RX samels the HIGH Level, but RX not.
(at the edge XFER from LOW to HIGH)

Solution:

1, Gate the SCLK
2, Config the XFER-3
3, Ungate the SCLK

Thus, TX/RX Always samples the right Level at the same
time.

After this patch, Test passed over 50w+.

Test Script:

  #!/bin/sh

  count=0

  killall aplay
  sleep 1

  while true
  do
      yes `echo -en "\x11\x11\x22\x22"` | tr -d '\n' | \
      aplay -D hw:2,0 --period-size=1024 --buffer-size=4096 -r 192000 -c 2 -f s16_le &>/dev/null &
      sleep 0.1
      rxd=`io -4 0xfe470028 | awk '{print $2}'`
      echo "[$count]: $rxd"
      if [ "$rxd" != "22221111" ]; then
          echo "FAIL: mismatch: $rxd, expected: 22221111"
          break
      fi
      count=$((count + 1))
      killall aplay
      sleep .1
  done

Signed-off-by: Sugar Zhang <sugar.zhang@rock-chips.com>
Change-Id: I10258b92d2f62ab36b5ccc55e8bcc752f3af9d4f
This commit is contained in:
Sugar Zhang
2024-08-05 14:27:29 +08:00
committed by Tao Huang
parent 36730a6e09
commit 3f4436826e

View File

@@ -986,6 +986,48 @@ static int rockchip_i2s_tdm_slave_one_frame_start(struct rk_i2s_tdm_dev *i2s_tdm
return 0;
}
static int rockchip_i2s_tdm_xfer_with_gate(struct rk_i2s_tdm_dev *i2s_tdm)
{
struct clk *mclk = NULL;
switch (i2s_tdm->clk_trcm) {
case I2S_CKR_TRCM_TXONLY:
mclk = i2s_tdm->mclk_tx;
break;
case I2S_CKR_TRCM_RXONLY:
mclk = i2s_tdm->mclk_rx;
break;
default:
dev_err(i2s_tdm->dev, "Must use in TRCM mode.\n");
return -EINVAL;
}
rockchip_utils_clk_gate_endisable(i2s_tdm->dev, mclk, 0);
udelay(10);
regmap_update_bits(i2s_tdm->regmap, I2S_XFER,
I2S_XFER_TXS_MASK |
I2S_XFER_RXS_MASK,
I2S_XFER_TXS_START |
I2S_XFER_RXS_START);
udelay(10);
rockchip_utils_clk_gate_endisable(i2s_tdm->dev, mclk, 1);
return 0;
}
static int rockchip_i2s_tdm_trcm_xfer(struct rk_i2s_tdm_dev *i2s_tdm)
{
/* No need to do GATE for HAVE_SYNC_RESET case */
if (i2s_tdm->soc_data && i2s_tdm->soc_data->src_clk_ctrl)
return regmap_update_bits(i2s_tdm->regmap, I2S_XFER,
I2S_XFER_TXS_MASK |
I2S_XFER_RXS_MASK,
I2S_XFER_TXS_START |
I2S_XFER_RXS_START);
return rockchip_i2s_tdm_xfer_with_gate(i2s_tdm);
}
static void rockchip_i2s_tdm_xfer_start(struct rk_i2s_tdm_dev *i2s_tdm,
int stream)
{
@@ -1000,11 +1042,7 @@ static void rockchip_i2s_tdm_xfer_start(struct rk_i2s_tdm_dev *i2s_tdm,
if (i2s_tdm->clk_trcm) {
rockchip_i2s_tdm_reset_assert(i2s_tdm);
regmap_update_bits(i2s_tdm->regmap, I2S_XFER,
I2S_XFER_TXS_MASK |
I2S_XFER_RXS_MASK,
I2S_XFER_TXS_START |
I2S_XFER_RXS_START);
rockchip_i2s_tdm_trcm_xfer(i2s_tdm);
rockchip_i2s_tdm_reset_deassert(i2s_tdm);
} else if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
regmap_update_bits(i2s_tdm->regmap, I2S_XFER,