ASoC: rockchip: i2s-tdm: Add support for FIFO-XRUN detection
This is useful for performance debug, throwing a WARNNING if
FIFO-XRUN detected and then do snd_pcm_stop_xrun to stop stream
and notify user XRUN state.
TESTCASE:
On the 192K-8CH-32BIT situation, FIFO full time is 83us (16 / 192000).
Considering WATERLEVEL-16: 41.6us, WATERLEVEL-24: 62.5us.
if any path (I2S<->DMA<->BUS<->DDR) blocked over this duration,
FIFO xrun raised.
This testcase injects a DMC-DVFS (> 80us) to reproduce FIFO-XRUN.
Suggested to disable DMC-DVFS or switch to DMC-DVFS-SCENE policy
to fix this on this situation.
OTOH, optimizing DMC-DVFS (<40us) or place audio buffer into SRAM
is also an alternative.
x.sh:
/#!/bin/sh
echo performance > /sys/devices/system/cpu/cpufreq/policy0/scaling_governor
echo performance > /sys/devices/system/cpu/cpufreq/policy4/scaling_governor
echo performance > /sys/devices/system/cpu/cpufreq/policy6/scaling_governor
freqs=`cat /sys/devices/platform/dmc/devfreq/dmc/available_frequencies`
echo "userspace" > /sys/devices/platform/dmc/devfreq/dmc/governor
aplay -D hw:3,0 --period-size=1024 --buffer-size=4096 -r 192000 -c 8 \
-f s32_le /dev/zero &
arecord -D hw:1,0 --period-size=1024 --buffer-size=4096 -r 192000 -c 8 \
-f s32_le /dev/zero &
sleep 1
for f in $freqs
do
echo "dmc: set_freq $f"
echo $f > /sys/devices/platform/dmc/devfreq/dmc/userspace/set_freq
sleep 1
done
killall aplay
killall arecord
Result:
root@RK3588:/data# ./x.sh
Playing raw data '/dev/zero' : Signed 32 bit Little Endian, Rate 192000 Hz, Channels 8
Recording WAVE '/dev/zero' : Signed 32 bit Little Endian, Rate 192000 Hz, Channels 8
dmc: set_freq 528000000
[ 69.222572] rockchip-i2s-tdm fddf0000.i2s: TX FIFO Underrun
[ 69.222615] rockchip-i2s-tdm fe470000.i2s: RX FIFO Overrun
dmc: set_freq 1068000000
[ 70.241013] rockchip-i2s-tdm fddf0000.i2s: TX FIFO Underrun
[ 70.241109] rockchip-i2s-tdm fe470000.i2s: RX FIFO Overrun
dmc: set_freq 1560000000
[ 71.259416] rockchip-i2s-tdm fddf0000.i2s: TX FIFO Underrun
[ 71.259452] rockchip-i2s-tdm fe470000.i2s: RX FIFO Overrun
dmc: set_freq 2112000000
[ 72.277841] rockchip-i2s-tdm fddf0000.i2s: TX FIFO Underrun
[ 72.277875] rockchip-i2s-tdm fe470000.i2s: RX FIFO Overrun
Signed-off-by: Sugar Zhang <sugar.zhang@rock-chips.com>
Change-Id: I4416c3424f3778560cadb94f585c0acb06eb3f40
This commit is contained in:
@@ -85,6 +85,7 @@ struct rk_i2s_tdm_dev {
|
||||
struct regmap *grf;
|
||||
struct snd_dmaengine_dai_dma_data capture_dma_data;
|
||||
struct snd_dmaengine_dai_dma_data playback_dma_data;
|
||||
struct snd_pcm_substream *substreams[SNDRV_PCM_STREAM_LAST + 1];
|
||||
struct reset_control *tx_reset;
|
||||
struct reset_control *rx_reset;
|
||||
const struct rk_i2s_soc_data *soc_data;
|
||||
@@ -438,9 +439,32 @@ static void rockchip_i2s_tdm_tx_fifo_padding(struct rk_i2s_tdm_dev *i2s_tdm, boo
|
||||
regmap_write(i2s_tdm->regmap, I2S_TXDR, 0x0);
|
||||
}
|
||||
|
||||
static void rockchip_i2s_tdm_fifo_xrun_detect(struct rk_i2s_tdm_dev *i2s_tdm,
|
||||
int stream, bool en)
|
||||
{
|
||||
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
/* clear irq status which was asserted before TXUIE enabled */
|
||||
regmap_update_bits(i2s_tdm->regmap, I2S_INTCR,
|
||||
I2S_INTCR_TXUIC, I2S_INTCR_TXUIC);
|
||||
regmap_update_bits(i2s_tdm->regmap, I2S_INTCR,
|
||||
I2S_INTCR_TXUIE_MASK,
|
||||
I2S_INTCR_TXUIE(en));
|
||||
} else {
|
||||
/* clear irq status which was asserted before RXOIE enabled */
|
||||
regmap_update_bits(i2s_tdm->regmap, I2S_INTCR,
|
||||
I2S_INTCR_RXOIC, I2S_INTCR_RXOIC);
|
||||
regmap_update_bits(i2s_tdm->regmap, I2S_INTCR,
|
||||
I2S_INTCR_RXOIE_MASK,
|
||||
I2S_INTCR_RXOIE(en));
|
||||
}
|
||||
}
|
||||
|
||||
static void rockchip_i2s_tdm_dma_ctrl(struct rk_i2s_tdm_dev *i2s_tdm,
|
||||
int stream, bool en)
|
||||
{
|
||||
if (!en)
|
||||
rockchip_i2s_tdm_fifo_xrun_detect(i2s_tdm, stream, 0);
|
||||
|
||||
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
if (i2s_tdm->quirks & QUIRK_HDMI_PATH)
|
||||
rockchip_i2s_tdm_tx_fifo_padding(i2s_tdm, en);
|
||||
@@ -463,6 +487,9 @@ static void rockchip_i2s_tdm_dma_ctrl(struct rk_i2s_tdm_dev *i2s_tdm,
|
||||
I2S_DMACR_RDE_MASK,
|
||||
I2S_DMACR_RDE(en));
|
||||
}
|
||||
|
||||
if (en)
|
||||
rockchip_i2s_tdm_fifo_xrun_detect(i2s_tdm, stream, 1);
|
||||
}
|
||||
|
||||
static void rockchip_i2s_tdm_xfer_start(struct rk_i2s_tdm_dev *i2s_tdm,
|
||||
@@ -1466,6 +1493,11 @@ static int rockchip_i2s_tdm_startup(struct snd_pcm_substream *substream,
|
||||
{
|
||||
struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
if (i2s_tdm->substreams[substream->stream])
|
||||
return -EBUSY;
|
||||
|
||||
i2s_tdm->substreams[substream->stream] = substream;
|
||||
|
||||
/*
|
||||
* Suggested to stop audio source before HDMI configure to make
|
||||
* sure audio data integrity on HDMI-PATH-ALWAYS-ON situation.
|
||||
@@ -1479,8 +1511,17 @@ static int rockchip_i2s_tdm_startup(struct snd_pcm_substream *substream,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rockchip_i2s_tdm_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
i2s_tdm->substreams[substream->stream] = NULL;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops rockchip_i2s_tdm_dai_ops = {
|
||||
.startup = rockchip_i2s_tdm_startup,
|
||||
.shutdown = rockchip_i2s_tdm_shutdown,
|
||||
.hw_params = rockchip_i2s_tdm_hw_params,
|
||||
.set_sysclk = rockchip_i2s_tdm_set_sysclk,
|
||||
.set_fmt = rockchip_i2s_tdm_set_fmt,
|
||||
@@ -1542,6 +1583,7 @@ static bool rockchip_i2s_tdm_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case I2S_TXFIFOLR:
|
||||
case I2S_INTCR:
|
||||
case I2S_INTSR:
|
||||
case I2S_CLR:
|
||||
case I2S_TXDR:
|
||||
@@ -1932,6 +1974,34 @@ static const struct snd_dlp_config dconfig = {
|
||||
.get_fifo_count = rockchip_i2s_tdm_get_fifo_count,
|
||||
};
|
||||
|
||||
static irqreturn_t rockchip_i2s_tdm_isr(int irq, void *devid)
|
||||
{
|
||||
struct rk_i2s_tdm_dev *i2s_tdm = (struct rk_i2s_tdm_dev *)devid;
|
||||
struct snd_pcm_substream *substream;
|
||||
u32 val;
|
||||
|
||||
regmap_read(i2s_tdm->regmap, I2S_INTSR, &val);
|
||||
if (val & I2S_INTSR_TXUI_ACT) {
|
||||
dev_warn_ratelimited(i2s_tdm->dev, "TX FIFO Underrun\n");
|
||||
regmap_update_bits(i2s_tdm->regmap, I2S_INTCR,
|
||||
I2S_INTCR_TXUIC, I2S_INTCR_TXUIC);
|
||||
substream = i2s_tdm->substreams[SNDRV_PCM_STREAM_PLAYBACK];
|
||||
if (substream)
|
||||
snd_pcm_stop_xrun(substream);
|
||||
}
|
||||
|
||||
if (val & I2S_INTSR_RXOI_ACT) {
|
||||
dev_warn_ratelimited(i2s_tdm->dev, "RX FIFO Overrun\n");
|
||||
regmap_update_bits(i2s_tdm->regmap, I2S_INTCR,
|
||||
I2S_INTCR_RXOIC, I2S_INTCR_RXOIC);
|
||||
substream = i2s_tdm->substreams[SNDRV_PCM_STREAM_CAPTURE];
|
||||
if (substream)
|
||||
snd_pcm_stop_xrun(substream);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int rockchip_i2s_tdm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
@@ -1943,7 +2013,7 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev)
|
||||
#ifdef HAVE_SYNC_RESET
|
||||
bool sync;
|
||||
#endif
|
||||
int ret, val, i;
|
||||
int ret, val, i, irq;
|
||||
|
||||
ret = rockchip_i2s_tdm_dai_prepare(pdev, &soc_dai);
|
||||
if (ret)
|
||||
@@ -2076,6 +2146,16 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(i2s_tdm->regmap))
|
||||
return PTR_ERR(i2s_tdm->regmap);
|
||||
|
||||
irq = platform_get_irq_optional(pdev, 0);
|
||||
if (irq > 0) {
|
||||
ret = devm_request_irq(&pdev->dev, irq, rockchip_i2s_tdm_isr,
|
||||
IRQF_SHARED, node->name, i2s_tdm);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to request irq %u\n", irq);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
i2s_tdm->playback_dma_data.addr = res->start + I2S_TXDR;
|
||||
i2s_tdm->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
i2s_tdm->playback_dma_data.maxburst = MAXBURST_PER_FIFO;
|
||||
|
||||
@@ -166,8 +166,8 @@
|
||||
#define I2S_INTCR_RFT(x) (((x) - 1) << I2S_INTCR_RFT_SHIFT)
|
||||
#define I2S_INTCR_RXOIC BIT(18)
|
||||
#define I2S_INTCR_RXOIE_SHIFT 17
|
||||
#define I2S_INTCR_RXOIE_DISABLE (0 << I2S_INTCR_RXOIE_SHIFT)
|
||||
#define I2S_INTCR_RXOIE_ENABLE (1 << I2S_INTCR_RXOIE_SHIFT)
|
||||
#define I2S_INTCR_RXOIE_MASK (1 << I2S_INTCR_RXOIE_SHIFT)
|
||||
#define I2S_INTCR_RXOIE(x) ((x) << I2S_INTCR_RXOIE_SHIFT)
|
||||
#define I2S_INTCR_RXFIE_SHIFT 16
|
||||
#define I2S_INTCR_RXFIE_DISABLE (0 << I2S_INTCR_RXFIE_SHIFT)
|
||||
#define I2S_INTCR_RXFIE_ENABLE (1 << I2S_INTCR_RXFIE_SHIFT)
|
||||
@@ -176,8 +176,8 @@
|
||||
#define I2S_INTCR_TFT_MASK (0x1f << I2S_INTCR_TFT_SHIFT)
|
||||
#define I2S_INTCR_TXUIC BIT(2)
|
||||
#define I2S_INTCR_TXUIE_SHIFT 1
|
||||
#define I2S_INTCR_TXUIE_DISABLE (0 << I2S_INTCR_TXUIE_SHIFT)
|
||||
#define I2S_INTCR_TXUIE_ENABLE (1 << I2S_INTCR_TXUIE_SHIFT)
|
||||
#define I2S_INTCR_TXUIE_MASK (1 << I2S_INTCR_TXUIE_SHIFT)
|
||||
#define I2S_INTCR_TXUIE(x) ((x) << I2S_INTCR_TXUIE_SHIFT)
|
||||
|
||||
/*
|
||||
* INTSR
|
||||
|
||||
Reference in New Issue
Block a user