diff options
Diffstat (limited to 'sound/soc/kirkwood')
-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: |