mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 10:58:48 +09:00
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:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user