aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/kirkwood/kirkwood-i2s.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/kirkwood/kirkwood-i2s.c')
-rw-r--r--sound/soc/kirkwood/kirkwood-i2s.c67
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: