/* * 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 irqreturn_t mxs_pcm_dma_irq(int irq, void *dev_id) { struct snd_pcm_substream *substream = dev_id; struct snd_pcm_runtime *runtime = substream->runtime; struct mxs_runtime_data *prtd = substream->runtime->private_data; struct mxs_dma_info dma_info; void *pdma; unsigned long prev_appl_offset, appl_count, cont, appl_ptr_bytes; mxs_dma_get_info(prtd->dma_ch, &dma_info); if (dma_info.status) { printk(KERN_WARNING "%s: DMA audio channel %d (%s) error\n", __func__, prtd->params->dma_ch, prtd->params->name); mxs_dma_ack_irq(prtd->dma_ch); } else { 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))) { appl_ptr_bytes = frames_to_bytes(runtime, runtime->control->appl_ptr); appl_count = appl_ptr_bytes - prtd->appl_ptr_bytes; prev_appl_offset = prtd->appl_ptr_bytes % prtd->dma_totsize; cont = prtd->dma_totsize - prev_appl_offset; if (appl_count > cont) { pdma = runtime->dma_area + prev_appl_offset; memmove(pdma + 1, pdma, cont - 1); pdma = runtime->dma_area; memmove(pdma + 1, pdma, appl_count - cont - 1); } else { pdma = runtime->dma_area + prev_appl_offset; memmove(pdma + 1, pdma, appl_count - 1); } prtd->appl_ptr_bytes = appl_ptr_bytes; } mxs_dma_ack_irq(prtd->dma_ch); snd_pcm_period_elapsed(substream); } return IRQ_HANDLED; }
static snd_pcm_uframes_t mxs_pcm_pointer(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; unsigned int offset; dma_addr_t pos; mxs_dma_get_info(prtd->params->dma_ch, &dma_info); pos = dma_info.buf_addr; offset = bytes_to_frames(runtime, pos - runtime->dma_addr); if (offset >= runtime->buffer_size) offset = 0; return offset; }