]> git.itanic.dy.fi Git - linux-stable/commitdiff
ASoC: rockchip: i2s: fix playback after runtime resume
authorJohn Keeping <john@metanate.com>
Mon, 8 Jan 2018 16:01:04 +0000 (16:01 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 16 Feb 2018 19:06:52 +0000 (20:06 +0100)
commit c66234cfedfc3e6e3b62563a5f2c1562be09a35d upstream.

When restoring registers during runtime resume, we must not write to
I2S_TXDR which is the transmit FIFO as this queues up a sample to be
output and pushes all of the output channels down by one.

This can be demonstrated with the speaker-test utility:

for i in a b c; do speaker-test -c 2 -s 1; done

which should play a test through the left speaker three times but if the
I2S hardware starts runtime suspended the first sample will be played
through the right speaker.

Fix this by marking I2S_TXDR as volatile (which also requires marking it
as readble, even though it technically isn't).  This seems to be the
most robust fix, the alternative of giving I2S_TXDR a default value is
more fragile since it does not prevent regcache writing to the register
in all circumstances.

While here, also fix the configuration of I2S_RXDR and I2S_FIFOLR; these
are not writable so they do not suffer from the same problem as I2S_TXDR
but reading from I2S_RXDR does suffer from a similar problem.

Fixes: f0447f6cbb20 ("ASoC: rockchip: i2s: restore register during runtime_suspend/resume cycle", 2016-09-07)
Signed-off-by: John Keeping <john@metanate.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
sound/soc/rockchip/rockchip_i2s.c

index 908211e1d6fcb46eb8e0d7d8401a892967ba156a..eb27f6c24bf7fbd8f12a4bfeadbe9a0f283543a1 100644 (file)
@@ -504,6 +504,7 @@ static bool rockchip_i2s_rd_reg(struct device *dev, unsigned int reg)
        case I2S_INTCR:
        case I2S_XFER:
        case I2S_CLR:
+       case I2S_TXDR:
        case I2S_RXDR:
        case I2S_FIFOLR:
        case I2S_INTSR:
@@ -518,6 +519,9 @@ static bool rockchip_i2s_volatile_reg(struct device *dev, unsigned int reg)
        switch (reg) {
        case I2S_INTSR:
        case I2S_CLR:
+       case I2S_FIFOLR:
+       case I2S_TXDR:
+       case I2S_RXDR:
                return true;
        default:
                return false;
@@ -527,6 +531,8 @@ static bool rockchip_i2s_volatile_reg(struct device *dev, unsigned int reg)
 static bool rockchip_i2s_precious_reg(struct device *dev, unsigned int reg)
 {
        switch (reg) {
+       case I2S_RXDR:
+               return true;
        default:
                return false;
        }