| From c66234cfedfc3e6e3b62563a5f2c1562be09a35d Mon Sep 17 00:00:00 2001 |
| From: John Keeping <john@metanate.com> |
| Date: Mon, 8 Jan 2018 16:01:04 +0000 |
| Subject: ASoC: rockchip: i2s: fix playback after runtime resume |
| |
| From: John Keeping <john@metanate.com> |
| |
| 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 | 6 ++++++ |
| 1 file changed, 6 insertions(+) |
| |
| --- a/sound/soc/rockchip/rockchip_i2s.c |
| +++ b/sound/soc/rockchip/rockchip_i2s.c |
| @@ -504,6 +504,7 @@ static bool rockchip_i2s_rd_reg(struct d |
| 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(st |
| 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(st |
| static bool rockchip_i2s_precious_reg(struct device *dev, unsigned int reg) |
| { |
| switch (reg) { |
| + case I2S_RXDR: |
| + return true; |
| default: |
| return false; |
| } |