static struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg( struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, enum dma_transfer_direction direction, unsigned long flags, void *context) { struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); struct fsl_edma_desc *fsl_desc; struct scatterlist *sg; u32 src_addr, dst_addr, last_sg, nbytes; u16 soff, doff, iter; int i; if (!is_slave_direction(fsl_chan->fsc.dir)) return NULL; fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len); if (!fsl_desc) return NULL; fsl_desc->iscyclic = false; nbytes = fsl_chan->fsc.addr_width * fsl_chan->fsc.burst; for_each_sg(sgl, sg, sg_len, i) { /* get next sg's physical address */ last_sg = fsl_desc->tcd[(i + 1) % sg_len].ptcd; if (fsl_chan->fsc.dir == DMA_MEM_TO_DEV) { src_addr = sg_dma_address(sg); dst_addr = fsl_chan->fsc.dev_addr; soff = fsl_chan->fsc.addr_width; doff = 0; } else { src_addr = fsl_chan->fsc.dev_addr; dst_addr = sg_dma_address(sg); soff = 0; doff = fsl_chan->fsc.addr_width; } iter = sg_dma_len(sg) / nbytes; if (i < sg_len - 1) { last_sg = fsl_desc->tcd[(i + 1)].ptcd; fill_tcd_params(fsl_chan->edma, fsl_desc->tcd[i].vtcd, src_addr, dst_addr, fsl_chan->fsc.attr, soff, nbytes, 0, iter, iter, doff, last_sg, false, false, true); } else { last_sg = 0; fill_tcd_params(fsl_chan->edma, fsl_desc->tcd[i].vtcd, src_addr, dst_addr, fsl_chan->fsc.attr, soff, nbytes, 0, iter, iter, doff, last_sg, true, true, false); } }
static int edma_engine_config(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; struct mvf_pcm_runtime_data *iprtd = runtime->private_data; u32 size = frames_to_bytes(runtime, runtime->period_size); struct imx_pcm_dma_params *dma_params; u32 sg_addr; int i; dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); iprtd->dma_buf_phys = runtime->dma_addr; iprtd->dma_buf_next = iprtd->dma_buf_phys; iprtd->dma_buf_end = iprtd->dma_buf_phys + runtime->periods * size; sg_addr = iprtd->tcd_buf_phys; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { iprtd->src_addr = iprtd->dma_buf_next; iprtd->dst_addr = dma_params->dma_addr; iprtd->soffset = 2; iprtd->doffset = 0; } else { iprtd->src_addr = dma_params->dma_addr; iprtd->dst_addr = iprtd->dma_buf_next; iprtd->soffset = 0; iprtd->doffset = 2; } mcf_edma_set_tcd_params(iprtd->tcd_chan, iprtd->src_addr, iprtd->dst_addr, MCF_EDMA_TCD_ATTR_SSIZE_16BIT | MCF_EDMA_TCD_ATTR_DSIZE_16BIT, iprtd->soffset, 4, 0, size / 4, size / 4, iprtd->doffset, sg_addr, 1, 0, 1); for (i = 0; i < TCD_NUMBER; i++) { iprtd->dma_buf_next += size; if (iprtd->dma_buf_next >= iprtd->dma_buf_end) iprtd->dma_buf_next = iprtd->dma_buf_phys; sg_addr = iprtd->tcd_buf_phys + ((i + 1) % TCD_NUMBER) * sizeof(struct edma_tcd); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) iprtd->src_addr = iprtd->dma_buf_next; else iprtd->dst_addr = iprtd->dma_buf_next; fill_tcd_params(&iprtd->tcd[i], iprtd->src_addr, iprtd->dst_addr, MCF_EDMA_TCD_ATTR_SSIZE_16BIT | MCF_EDMA_TCD_ATTR_DSIZE_16BIT, iprtd->soffset, 4, 0, size / 4, size / 4, iprtd->doffset, sg_addr, 1, 0, 1); } return 0; }
static struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic( struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, size_t period_len, enum dma_transfer_direction direction, unsigned long flags) { struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); struct fsl_edma_desc *fsl_desc; dma_addr_t dma_buf_next; int sg_len, i; u32 src_addr, dst_addr, last_sg, nbytes; u16 soff, doff, iter; if (!is_slave_direction(fsl_chan->fsc.dir)) return NULL; sg_len = buf_len / period_len; fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len); if (!fsl_desc) return NULL; fsl_desc->iscyclic = true; dma_buf_next = dma_addr; nbytes = fsl_chan->fsc.addr_width * fsl_chan->fsc.burst; iter = period_len / nbytes; for (i = 0; i < sg_len; i++) { if (dma_buf_next >= dma_addr + buf_len) dma_buf_next = dma_addr; /* get next sg's physical address */ last_sg = fsl_desc->tcd[(i + 1) % sg_len].ptcd; if (fsl_chan->fsc.dir == DMA_MEM_TO_DEV) { src_addr = dma_buf_next; dst_addr = fsl_chan->fsc.dev_addr; soff = fsl_chan->fsc.addr_width; doff = 0; } else { src_addr = fsl_chan->fsc.dev_addr; dst_addr = dma_buf_next; soff = 0; doff = fsl_chan->fsc.addr_width; } fill_tcd_params(fsl_chan->edma, fsl_desc->tcd[i].vtcd, src_addr, dst_addr, fsl_chan->fsc.attr, soff, nbytes, 0, iter, iter, doff, last_sg, true, false, true); dma_buf_next += period_len; } return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, flags); }