static void sdma_free_chan_resources(struct dma_chan *chan) { struct sdma_channel *sdmac = to_sdma_chan(chan); struct sdma_engine *sdma = sdmac->sdma; sdma_disable_channel(sdmac); if (sdmac->event_id0) sdma_event_disable(sdmac, sdmac->event_id0); if (sdmac->event_id1) sdma_event_disable(sdmac, sdmac->event_id1); sdmac->event_id0 = 0; sdmac->event_id1 = 0; sdma_set_channel_priority(sdmac, 0); dma_free_coherent(NULL, PAGE_SIZE, sdmac->bd, sdmac->bd_phys); clk_disable(sdma->clk); }
static int sdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg) { struct sdma_channel *sdmac = to_sdma_chan(chan); struct dma_slave_config *dmaengine_cfg = (void *)arg; switch (cmd) { case DMA_TERMINATE_ALL: sdma_disable_channel(sdmac); return 0; case DMA_SLAVE_CONFIG: sdmac->direction = dmaengine_cfg->direction; if (dmaengine_cfg->direction == DMA_DEV_TO_DEV) { sdmac->per_address = dmaengine_cfg->src_addr; sdmac->per_address2 = dmaengine_cfg->dst_addr; sdmac->watermark_level = 0; sdmac->watermark_level |= dmaengine_cfg->src_maxburst; sdmac->watermark_level |= dmaengine_cfg->dst_maxburst << 16; sdmac->word_size = dmaengine_cfg->dst_addr_width; } else if (dmaengine_cfg->direction == DMA_DEV_TO_MEM) { sdmac->per_address = dmaengine_cfg->src_addr; sdmac->watermark_level = dmaengine_cfg->src_maxburst; sdmac->word_size = dmaengine_cfg->src_addr_width; } else { sdmac->per_address = dmaengine_cfg->dst_addr; sdmac->watermark_level = dmaengine_cfg->dst_maxburst; sdmac->word_size = dmaengine_cfg->dst_addr_width; } sdmac->direction = dmaengine_cfg->direction; return sdma_config_channel(sdmac); default: return -ENOSYS; } return -EINVAL; }
static int sdma_alloc_chan_resources(struct dma_chan *chan) { struct sdma_channel *sdmac = to_sdma_chan(chan); struct imx_dma_data *data = chan->private; int prio, ret; if (!data) return -EINVAL; switch (data->priority) { case DMA_PRIO_HIGH: prio = 3; break; case DMA_PRIO_MEDIUM: prio = 2; break; case DMA_PRIO_LOW: default: prio = 1; break; } sdmac->peripheral_type = data->peripheral_type; sdmac->event_id0 = data->dma_request; ret = sdma_set_channel_priority(sdmac, prio); if (ret) return ret; ret = sdma_request_channel(sdmac); if (ret) return ret; dma_async_tx_descriptor_init(&sdmac->desc, chan); sdmac->desc.tx_submit = sdma_tx_submit; /* txd.flags will be overwritten in prep funcs */ sdmac->desc.flags = DMA_CTRL_ACK; return 0; }
static struct dma_async_tx_descriptor *sdma_prep_slave_sg( struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, enum dma_transfer_direction direction, unsigned long flags) { struct sdma_channel *sdmac = to_sdma_chan(chan); struct sdma_engine *sdma = sdmac->sdma; int ret, i, count; int channel = sdmac->channel; struct scatterlist *sg; if (sdmac->status == DMA_IN_PROGRESS) return NULL; sdmac->status = DMA_IN_PROGRESS; sdmac->flags = 0; dev_dbg(sdma->dev, "setting up %d entries for channel %d.\n", sg_len, channel); sdmac->direction = direction; ret = sdma_load_context(sdmac); if (ret) goto err_out; if (sg_len > NUM_BD) { dev_err(sdma->dev, "SDMA channel %d: maximum number of sg exceeded: %d > %d\n", channel, sg_len, NUM_BD); ret = -EINVAL; goto err_out; } sdmac->chn_count = 0; for_each_sg(sgl, sg, sg_len, i) { struct sdma_buffer_descriptor *bd = &sdmac->bd[i]; int param; bd->buffer_addr = sg->dma_address; count = sg->length; if (count > 0xffff) { dev_err(sdma->dev, "SDMA channel %d: maximum bytes for sg entry exceeded: %d > %d\n", channel, count, 0xffff); ret = -EINVAL; goto err_out; } bd->mode.count = count; sdmac->chn_count += count; if (sdmac->word_size > DMA_SLAVE_BUSWIDTH_4_BYTES) { ret = -EINVAL; goto err_out; } switch (sdmac->word_size) { case DMA_SLAVE_BUSWIDTH_4_BYTES: bd->mode.command = 0; if (count & 3 || sg->dma_address & 3) return NULL; break; case DMA_SLAVE_BUSWIDTH_2_BYTES: bd->mode.command = 2; if (count & 1 || sg->dma_address & 1) return NULL; break; case DMA_SLAVE_BUSWIDTH_1_BYTE: bd->mode.command = 1; break; default: return NULL; } param = BD_DONE | BD_EXTD | BD_CONT; if (i + 1 == sg_len) { param |= BD_INTR; param |= BD_LAST; param &= ~BD_CONT; } dev_dbg(sdma->dev, "entry %d: count: %d dma: 0x%08x %s%s\n", i, count, sg->dma_address, param & BD_WRAP ? "wrap" : "", param & BD_INTR ? " intr" : ""); bd->mode.status = param; } sdmac->num_bd = sg_len; sdma->channel_control[channel].current_bd_ptr = sdmac->bd_phys; return &sdmac->desc; err_out: sdmac->status = DMA_ERROR; return NULL; }
static struct dma_async_tx_descriptor *sdma_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) { struct sdma_channel *sdmac = to_sdma_chan(chan); struct sdma_engine *sdma = sdmac->sdma; int num_periods = buf_len / period_len; int channel = sdmac->channel; int ret, i = 0, buf = 0; dev_dbg(sdma->dev, "%s channel: %d\n", __func__, channel); if (sdmac->status == DMA_IN_PROGRESS) return NULL; sdmac->status = DMA_IN_PROGRESS; sdmac->flags |= IMX_DMA_SG_LOOP; sdmac->direction = direction; ret = sdma_load_context(sdmac); if (ret) goto err_out; if (num_periods > NUM_BD) { dev_err(sdma->dev, "SDMA channel %d: maximum number of sg exceeded: %d > %d\n", channel, num_periods, NUM_BD); goto err_out; } if (period_len > 0xffff) { dev_err(sdma->dev, "SDMA channel %d: maximum period size exceeded: %d > %d\n", channel, period_len, 0xffff); goto err_out; } while (buf < buf_len) { struct sdma_buffer_descriptor *bd = &sdmac->bd[i]; int param; bd->buffer_addr = dma_addr; bd->mode.count = period_len; if (sdmac->word_size > DMA_SLAVE_BUSWIDTH_4_BYTES) goto err_out; if (sdmac->word_size == DMA_SLAVE_BUSWIDTH_4_BYTES) bd->mode.command = 0; else bd->mode.command = sdmac->word_size; param = BD_DONE | BD_EXTD | BD_CONT | BD_INTR; if (i + 1 == num_periods) param |= BD_WRAP; dev_dbg(sdma->dev, "entry %d: count: %d dma: 0x%08x %s%s\n", i, period_len, dma_addr, param & BD_WRAP ? "wrap" : "", param & BD_INTR ? " intr" : ""); bd->mode.status = param; dma_addr += period_len; buf += period_len; i++; } sdmac->num_bd = num_periods; sdma->channel_control[channel].current_bd_ptr = sdmac->bd_phys; return &sdmac->desc; err_out: sdmac->status = DMA_ERROR; return NULL; }