ASoC: rockchip: i2s-tdm; Fix frame sync duty cycle

There maybe a risk that FRAME sync is not 50% duty cycle
on TRCM mode, which will cause sound noise.

This patch wraps XFER-start with reset to fix this issue.

- Hold reset
- Enable XFER
- Release reset

e.g. RK3308

Enable TRCM:

  &i2s_8ch_0 {
      assigned-clocks = <&cru SCLK_I2S0_8CH_RX>;
      assigned-clock-parents = <&cru SCLK_I2S0_8CH_TX_MUX>;
      rockchip,clk-trcm = <1>;
  }

StressTest:

  /#!/bin/sh

  count=0

  while true
  do
      yes `echo -en "\x11\x11\x22\x22"` | tr -d '\n' | \
      aplay -D hw:1,0 --period-size=1024 --buffer-size=4096 -r 192000 -c 2 -f s16_le &>/dev/null &
      sleep 2
      rxd=`io -4 0xff300028 | 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

Result:

Before:

[35]: 22221111
[36]: 22221111
[37]: 11110888
FAIL: mismatch: 11110888, expected: 22221111

[253]: 22221111
[254]: 22221111
[255]: 11110888
FAIL: mismatch: 11110888, expected: 22221111

[319]: 22221111
[320]: 22221111
[321]: 11110888
FAIL: mismatch: 11110888, expected: 22221111

After this patch:

PASS on 100000+ loop count.

[ 99998]: 22221111
[ 99999]: 22221111
[100000]: 22221111
[100001]: 22221111
...

Change-Id: I77931c529acb43ef0bf7468e2c74ac9ff419218e
Signed-off-by: Sugar Zhang <sugar.zhang@rock-chips.com>
This commit is contained in:
Sugar Zhang
2022-06-30 22:54:38 +08:00
parent 4ea755ab3b
commit efc8faa7b3

View File

@@ -181,14 +181,30 @@ static inline void __raw_writeq(u64 val, volatile void __iomem *addr)
#define writeq(v,c) ({ __iowmb(); __raw_writeq((__force u64) cpu_to_le64(v), c); })
#endif
static void rockchip_snd_xfer_reset_assert(struct rk_i2s_tdm_dev *i2s_tdm,
int tx_bank, int tx_offset,
int rx_bank, int rx_offset)
static void rockchip_snd_xfer_reset_assert(struct rk_i2s_tdm_dev *i2s_tdm)
{
int tx_bank, rx_bank, tx_offset, rx_offset, tx_id, rx_id;
void __iomem *cru_reset, *addr;
unsigned long flags;
u64 val;
if (!i2s_tdm->cru_base || !i2s_tdm->soc_data || !i2s_tdm->is_master_mode)
return;
tx_id = i2s_tdm->tx_reset_id;
rx_id = i2s_tdm->rx_reset_id;
if (tx_id < 0 || rx_id < 0)
return;
tx_bank = tx_id / 16;
tx_offset = tx_id % 16;
rx_bank = rx_id / 16;
rx_offset = rx_id % 16;
dev_dbg(i2s_tdm->dev,
"tx_bank: %d, rx_bank: %d,tx_offset: %d, rx_offset: %d\n",
tx_bank, rx_bank, tx_offset, rx_offset);
cru_reset = i2s_tdm->cru_base + i2s_tdm->soc_data->softrst_offset;
switch (abs(tx_bank - rx_bank)) {
@@ -228,14 +244,30 @@ static void rockchip_snd_xfer_reset_assert(struct rk_i2s_tdm_dev *i2s_tdm,
udelay(10);
}
static void rockchip_snd_xfer_reset_deassert(struct rk_i2s_tdm_dev *i2s_tdm,
int tx_bank, int tx_offset,
int rx_bank, int rx_offset)
static void rockchip_snd_xfer_reset_deassert(struct rk_i2s_tdm_dev *i2s_tdm)
{
int tx_bank, rx_bank, tx_offset, rx_offset, tx_id, rx_id;
void __iomem *cru_reset, *addr;
unsigned long flags;
u64 val;
if (!i2s_tdm->cru_base || !i2s_tdm->soc_data || !i2s_tdm->is_master_mode)
return;
tx_id = i2s_tdm->tx_reset_id;
rx_id = i2s_tdm->rx_reset_id;
if (tx_id < 0 || rx_id < 0)
return;
tx_bank = tx_id / 16;
tx_offset = tx_id % 16;
rx_bank = rx_id / 16;
rx_offset = rx_id % 16;
dev_dbg(i2s_tdm->dev,
"tx_bank: %d, rx_bank: %d,tx_offset: %d, rx_offset: %d\n",
tx_bank, rx_bank, tx_offset, rx_offset);
cru_reset = i2s_tdm->cru_base + i2s_tdm->soc_data->softrst_offset;
switch (abs(tx_bank - rx_bank)) {
@@ -280,31 +312,16 @@ static void rockchip_snd_xfer_reset_deassert(struct rk_i2s_tdm_dev *i2s_tdm,
*/
static void rockchip_snd_xfer_sync_reset(struct rk_i2s_tdm_dev *i2s_tdm)
{
int tx_id, rx_id;
int tx_bank, rx_bank, tx_offset, rx_offset;
if (!i2s_tdm->cru_base || !i2s_tdm->soc_data || !i2s_tdm->is_master_mode)
return;
tx_id = i2s_tdm->tx_reset_id;
rx_id = i2s_tdm->rx_reset_id;
if (tx_id < 0 || rx_id < 0)
return;
tx_bank = tx_id / 16;
tx_offset = tx_id % 16;
rx_bank = rx_id / 16;
rx_offset = rx_id % 16;
dev_dbg(i2s_tdm->dev,
"tx_bank: %d, rx_bank: %d,tx_offset: %d, rx_offset: %d\n",
tx_bank, rx_bank, tx_offset, rx_offset);
rockchip_snd_xfer_reset_assert(i2s_tdm, tx_bank, tx_offset,
rx_bank, rx_offset);
rockchip_snd_xfer_reset_deassert(i2s_tdm, tx_bank, tx_offset,
rx_bank, rx_offset);
rockchip_snd_xfer_reset_assert(i2s_tdm);
rockchip_snd_xfer_reset_deassert(i2s_tdm);
}
#else
static inline void rockchip_snd_xfer_reset_assert(struct rk_i2s_tdm_dev *i2s_tdm)
{
}
static inline void rockchip_snd_xfer_reset_deassert(struct rk_i2s_tdm_dev *i2s_tdm)
{
}
static inline void rockchip_snd_xfer_sync_reset(struct rk_i2s_tdm_dev *i2s_tdm)
{
}
@@ -331,12 +348,13 @@ static void rockchip_snd_txrxctrl(struct snd_pcm_substream *substream,
I2S_DMACR_RDE_ENABLE);
if (atomic_inc_return(&i2s_tdm->refcount) == 1) {
rockchip_snd_xfer_sync_reset(i2s_tdm);
rockchip_snd_xfer_reset_assert(i2s_tdm);
regmap_update_bits(i2s_tdm->regmap, I2S_XFER,
I2S_XFER_TXS_START |
I2S_XFER_RXS_START,
I2S_XFER_TXS_START |
I2S_XFER_RXS_START);
rockchip_snd_xfer_reset_deassert(i2s_tdm);
}
} else {
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
@@ -699,11 +717,13 @@ static void rockchip_i2s_tdm_xfer_resume(struct snd_pcm_substream *substream,
I2S_DMACR_RDE_ENABLE,
I2S_DMACR_RDE_ENABLE);
rockchip_snd_xfer_reset_assert(i2s_tdm);
regmap_update_bits(i2s_tdm->regmap, I2S_XFER,
I2S_XFER_TXS_START |
I2S_XFER_RXS_START,
I2S_XFER_TXS_START |
I2S_XFER_RXS_START);
rockchip_snd_xfer_reset_deassert(i2s_tdm);
}
static int rockchip_i2s_tdm_clk_set_rate(struct rk_i2s_tdm_dev *i2s_tdm,