/* * Stop circular DMA descriptor list * We should not stop DMA in a middle of current transaction once we receive * stop request from ALSA core. This function finds the next DMA descriptor * and set it up to decrement DMA channel semaphore. So the current transaction * is the last data transfer. */ static void mxs_pcm_stop(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct mxs_runtime_data *prtd = runtime->private_data; struct mxs_dma_info dma_info; int desc; int periods_num = prtd->dma_totsize / prtd->dma_period; /* Freez DMA channel for a moment */ mxs_dma_freeze(prtd->dma_ch); mxs_dma_get_info(prtd->dma_ch, &dma_info); desc = (dma_info.buf_addr - runtime->dma_addr) / prtd->dma_period; if (desc >= periods_num) desc = 0; else if (desc < 0) desc = 0; /* Set up the next descriptor to decrement DMA channel sempahore */ prtd->dma_desc_array[(desc + 1)%periods_num]->cmd.cmd.bits.bytes = 0; prtd->dma_desc_array[(desc + 1)%periods_num]->cmd.cmd.bits.pio_words = \ 0; prtd->dma_desc_array[(desc + 1)%periods_num]->cmd.cmd.bits.dec_sem = 1; prtd->dma_desc_array[(desc + 1)%periods_num]->cmd.cmd.bits.irq = 0; prtd->dma_desc_array[(desc + 1)%periods_num]->cmd.cmd.bits.command = \ NO_DMA_XFER; mxs_dma_unfreeze(prtd->dma_ch); mxs_dma_disable(prtd->dma_ch); }
static int mxs_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_pcm_runtime *runtime = substream->runtime; struct mxs_runtime_data *prtd = substream->runtime->private_data; int ret = 0; switch (cmd) { case SNDRV_PCM_TRIGGER_START: if ((prtd->params->dma_ch == MXS_DMA_CHANNEL_AHB_APBX_SPDIF) && (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) && (runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) && ((prtd->format == SNDRV_PCM_FORMAT_S24_LE) || (prtd->format == SNDRV_PCM_FORMAT_S20_3LE))) { prtd->appl_ptr_bytes = frames_to_bytes(runtime, runtime->control->appl_ptr); memmove(runtime->dma_area + 1, runtime->dma_area, prtd->appl_ptr_bytes - 1); } mxs_dma_enable(prtd->dma_ch); break; case SNDRV_PCM_TRIGGER_STOP: mxs_pcm_stop(substream); break; case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: mxs_dma_unfreeze(prtd->dma_ch); break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: mxs_dma_freeze(prtd->dma_ch); break; default: ret = -EINVAL; break; } return ret; }