diff options
| -rw-r--r-- | sound/soc/kirkwood/kirkwood-i2s.c | 67 |
1 files changed, 38 insertions, 29 deletions
diff --git a/sound/soc/kirkwood/kirkwood-i2s.c b/sound/soc/kirkwood/kirkwood-i2s.c index 485af80923de..826306dfb72b 100644 --- a/sound/soc/kirkwood/kirkwood-i2s.c +++ b/sound/soc/kirkwood/kirkwood-i2s.c | |||
| @@ -180,67 +180,76 @@ static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream, | |||
| 180 | int cmd, struct snd_soc_dai *dai) | 180 | int cmd, struct snd_soc_dai *dai) |
| 181 | { | 181 | { |
| 182 | struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai); | 182 | struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai); |
| 183 | unsigned long value; | 183 | uint32_t ctl, value; |
| 184 | 184 | ||
| 185 | /* | 185 | ctl = readl(priv->io + KIRKWOOD_PLAYCTL); |
| 186 | * specs says KIRKWOOD_PLAYCTL must be read 2 times before | 186 | if (ctl & KIRKWOOD_PLAYCTL_PAUSE) { |
| 187 | * changing it. So read 1 time here and 1 later. | 187 | unsigned timeout = 5000; |
| 188 | */ | 188 | /* |
| 189 | value = readl(priv->io + KIRKWOOD_PLAYCTL); | 189 | * The Armada510 spec says that if we enter pause mode, the |
| 190 | * busy bit must be read back as clear _twice_. Make sure | ||
| 191 | * we respect that otherwise we get DMA underruns. | ||
| 192 | */ | ||
| 193 | do { | ||
| 194 | value = ctl; | ||
| 195 | ctl = readl(priv->io + KIRKWOOD_PLAYCTL); | ||
| 196 | if (!((ctl | value) & KIRKWOOD_PLAYCTL_PLAY_BUSY)) | ||
| 197 | break; | ||
| 198 | udelay(1); | ||
| 199 | } while (timeout--); | ||
| 200 | |||
| 201 | if ((ctl | value) & KIRKWOOD_PLAYCTL_PLAY_BUSY) | ||
| 202 | dev_notice(dai->dev, "timed out waiting for busy to deassert: %08x\n", | ||
| 203 | ctl); | ||
| 204 | } | ||
| 190 | 205 | ||
| 191 | switch (cmd) { | 206 | switch (cmd) { |
| 192 | case SNDRV_PCM_TRIGGER_START: | 207 | case SNDRV_PCM_TRIGGER_START: |
| 193 | /* stop audio, enable interrupts */ | 208 | /* stop audio, enable interrupts */ |
| 194 | value = readl(priv->io + KIRKWOOD_PLAYCTL); | 209 | ctl |= KIRKWOOD_PLAYCTL_PAUSE; |
| 195 | value |= KIRKWOOD_PLAYCTL_PAUSE; | 210 | writel(ctl, priv->io + KIRKWOOD_PLAYCTL); |
| 196 | writel(value, priv->io + KIRKWOOD_PLAYCTL); | ||
| 197 | 211 | ||
| 198 | value = readl(priv->io + KIRKWOOD_INT_MASK); | 212 | value = readl(priv->io + KIRKWOOD_INT_MASK); |
| 199 | value |= KIRKWOOD_INT_CAUSE_PLAY_BYTES; | 213 | value |= KIRKWOOD_INT_CAUSE_PLAY_BYTES; |
| 200 | writel(value, priv->io + KIRKWOOD_INT_MASK); | 214 | writel(value, priv->io + KIRKWOOD_INT_MASK); |
| 201 | 215 | ||
| 202 | /* configure audio & enable i2s playback */ | 216 | /* configure audio & enable i2s playback */ |
| 203 | value = readl(priv->io + KIRKWOOD_PLAYCTL); | 217 | ctl &= ~KIRKWOOD_PLAYCTL_BURST_MASK; |
| 204 | value &= ~KIRKWOOD_PLAYCTL_BURST_MASK; | 218 | ctl &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE |
| 205 | value &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE | ||
| 206 | | KIRKWOOD_PLAYCTL_SPDIF_EN); | 219 | | KIRKWOOD_PLAYCTL_SPDIF_EN); |
| 207 | 220 | ||
| 208 | if (priv->burst == 32) | 221 | if (priv->burst == 32) |
| 209 | value |= KIRKWOOD_PLAYCTL_BURST_32; | 222 | ctl |= KIRKWOOD_PLAYCTL_BURST_32; |
| 210 | else | 223 | else |
| 211 | value |= KIRKWOOD_PLAYCTL_BURST_128; | 224 | ctl |= KIRKWOOD_PLAYCTL_BURST_128; |
| 212 | value |= KIRKWOOD_PLAYCTL_I2S_EN; | 225 | ctl |= KIRKWOOD_PLAYCTL_I2S_EN; |
| 213 | writel(value, priv->io + KIRKWOOD_PLAYCTL); | 226 | writel(ctl, priv->io + KIRKWOOD_PLAYCTL); |
| 214 | break; | 227 | break; |
| 215 | 228 | ||
| 216 | case SNDRV_PCM_TRIGGER_STOP: | 229 | case SNDRV_PCM_TRIGGER_STOP: |
| 217 | /* stop audio, disable interrupts */ | 230 | /* stop audio, disable interrupts */ |
| 218 | value = readl(priv->io + KIRKWOOD_PLAYCTL); | 231 | ctl |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE; |
| 219 | value |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE; | 232 | writel(ctl, priv->io + KIRKWOOD_PLAYCTL); |
| 220 | writel(value, priv->io + KIRKWOOD_PLAYCTL); | ||
| 221 | 233 | ||
| 222 | value = readl(priv->io + KIRKWOOD_INT_MASK); | 234 | value = readl(priv->io + KIRKWOOD_INT_MASK); |
| 223 | value &= ~KIRKWOOD_INT_CAUSE_PLAY_BYTES; | 235 | value &= ~KIRKWOOD_INT_CAUSE_PLAY_BYTES; |
| 224 | writel(value, priv->io + KIRKWOOD_INT_MASK); | 236 | writel(value, priv->io + KIRKWOOD_INT_MASK); |
| 225 | 237 | ||
| 226 | /* disable all playbacks */ | 238 | /* disable all playbacks */ |
| 227 | value = readl(priv->io + KIRKWOOD_PLAYCTL); | 239 | ctl &= ~(KIRKWOOD_PLAYCTL_I2S_EN | KIRKWOOD_PLAYCTL_SPDIF_EN); |
| 228 | value &= ~(KIRKWOOD_PLAYCTL_I2S_EN | KIRKWOOD_PLAYCTL_SPDIF_EN); | 240 | writel(ctl, priv->io + KIRKWOOD_PLAYCTL); |
| 229 | writel(value, priv->io + KIRKWOOD_PLAYCTL); | ||
| 230 | break; | 241 | break; |
| 231 | 242 | ||
| 232 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | 243 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
| 233 | case SNDRV_PCM_TRIGGER_SUSPEND: | 244 | case SNDRV_PCM_TRIGGER_SUSPEND: |
| 234 | value = readl(priv->io + KIRKWOOD_PLAYCTL); | 245 | ctl |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE; |
| 235 | value |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE; | 246 | writel(ctl, priv->io + KIRKWOOD_PLAYCTL); |
| 236 | writel(value, priv->io + KIRKWOOD_PLAYCTL); | ||
| 237 | break; | 247 | break; |
| 238 | 248 | ||
| 239 | case SNDRV_PCM_TRIGGER_RESUME: | 249 | case SNDRV_PCM_TRIGGER_RESUME: |
| 240 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | 250 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
| 241 | value = readl(priv->io + KIRKWOOD_PLAYCTL); | 251 | ctl &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE); |
| 242 | value &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE); | 252 | writel(ctl, priv->io + KIRKWOOD_PLAYCTL); |
| 243 | writel(value, priv->io + KIRKWOOD_PLAYCTL); | ||
| 244 | break; | 253 | break; |
| 245 | 254 | ||
| 246 | default: | 255 | default: |
