Here's how I implemented sync start/stop.......
/** * psc_dma_trigger: start and stop the DMA transfer. * * This function is called by ALSA to start, stop, pause, and resume the DMA * transfer of data. */ static int psc_dma_trigger(struct snd_pcm_substream *sub, int cmd) { struct snd_soc_pcm_runtime *rtd; struct psc_dma *psc_dma; struct snd_pcm_runtime *runtime; struct psc_dma_stream *s; struct snd_pcm_substream *substream; struct mpc52xx_psc __iomem *regs; u16 imr; unsigned long flags; int i;
snd_pcm_group_for_each_entry(substream, sub) {
rtd = substream->private_data; psc_dma = rtd->dai->cpu_dai->private_data; runtime = substream->runtime; regs = psc_dma->psc_regs;
dev_dbg(psc_dma->dev, "psc_dma_trigger(substream=%p, cmd=%i)" " stream_id=%i\n", sub, cmd, substream->pstr->stream);
if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) s = &psc_dma->capture; else s = &psc_dma->playback;
switch (cmd) { case SNDRV_PCM_TRIGGER_START: s->period_bytes = frames_to_bytes(runtime, runtime->period_size); s->period_start = virt_to_phys(runtime->dma_area); s->period_end = s->period_start + (s->period_bytes * runtime->periods); s->period_next_pt = s->period_start; s->period_current_pt = s->period_start; s->period_size = runtime->period_size; s->active = 1;
/* track appl_ptr so that we have a better chance of detecting * end of stream and not over running it. */ s->runtime = runtime; s->appl_ptr = s->runtime->control->appl_ptr - (runtime->period_size * runtime->periods);
/* Fill up the bestcomm bd queue and enable DMA. * This will begin filling the PSC's fifo. */ spin_lock_irqsave(&psc_dma->lock, flags);
if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) { bcom_gen_bd_rx_reset(s->bcom_task); for (i = 0; i < runtime->periods; i++) if (!bcom_queue_full(s->bcom_task)) psc_dma_bcom_enqueue_next_buffer(s); } else { bcom_gen_bd_tx_reset(s->bcom_task); psc_dma_bcom_enqueue_tx(s); }
bcom_enable(s->bcom_task); spin_unlock_irqrestore(&psc_dma->lock, flags);
out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT);
break;
case SNDRV_PCM_TRIGGER_STOP: s->active = 0;
spin_lock_irqsave(&psc_dma->lock, flags); bcom_disable(s->bcom_task); if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) bcom_gen_bd_rx_reset(s->bcom_task); else bcom_gen_bd_tx_reset(s->bcom_task); spin_unlock_irqrestore(&psc_dma->lock, flags);
break;
default: dev_dbg(psc_dma->dev, "invalid command\n"); return -EINVAL; } if (sub != substream) snd_pcm_trigger_done(substream, sub);
/* Update interrupt enable settings */ imr = 0; if (psc_dma->playback.active) imr |= MPC52xx_PSC_IMR_TXEMP; if (psc_dma->capture.active) imr |= MPC52xx_PSC_IMR_ORERR; out_be16(®s->isr_imr.imr, psc_dma->imr | imr); } return 0; }