diff options
author | Stephen Rothwell <sfr@canb.auug.org.au> | 2010-04-15 11:53:45 +1000 |
---|---|---|
committer | Stephen Rothwell <sfr@canb.auug.org.au> | 2010-04-15 11:53:45 +1000 |
commit | 8d6cbbbc631495e71e078135be8ad182bee8567d (patch) | |
tree | 91d01d8e05ffd127af65ac27e675a7978c80509f | |
parent | 576c90cefc6780b32e035218f0f742e9b9f9d561 (diff) | |
parent | 6a3cd3ea48584d14f60dce0b3c4e9e4428beb0fe (diff) |
Merge remote branch 'async_tx/next'
-rw-r--r-- | arch/arm/mach-u300/include/mach/coh901318.h | 21 | ||||
-rw-r--r-- | drivers/dma/Kconfig | 7 | ||||
-rw-r--r-- | drivers/dma/Makefile | 1 | ||||
-rw-r--r-- | drivers/dma/at_hdmac.c | 34 | ||||
-rw-r--r-- | drivers/dma/coh901318.c | 262 | ||||
-rw-r--r-- | drivers/dma/dmaengine.c | 4 | ||||
-rw-r--r-- | drivers/dma/dw_dmac.c | 23 | ||||
-rw-r--r-- | drivers/dma/fsldma.c | 28 | ||||
-rw-r--r-- | drivers/dma/ioat/dma.c | 12 | ||||
-rw-r--r-- | drivers/dma/ioat/dma.h | 18 | ||||
-rw-r--r-- | drivers/dma/ioat/dma_v2.c | 2 | ||||
-rw-r--r-- | drivers/dma/ioat/dma_v3.c | 20 | ||||
-rw-r--r-- | drivers/dma/iop-adma.c | 39 | ||||
-rw-r--r-- | drivers/dma/ipu/ipu_idmac.c | 32 | ||||
-rw-r--r-- | drivers/dma/mpc512x_dma.c | 13 | ||||
-rw-r--r-- | drivers/dma/mv_xor.c | 25 | ||||
-rw-r--r-- | drivers/dma/ppc4xx/adma.c | 19 | ||||
-rw-r--r-- | drivers/dma/shdma.c | 26 | ||||
-rw-r--r-- | drivers/dma/timb_dma.c | 859 | ||||
-rw-r--r-- | drivers/dma/txx9dmac.c | 22 | ||||
-rw-r--r-- | drivers/mmc/host/atmel-mci.c | 2 | ||||
-rw-r--r-- | drivers/serial/sh-sci.c | 2 | ||||
-rw-r--r-- | drivers/video/mx3fb.c | 3 | ||||
-rw-r--r-- | include/linux/dmaengine.h | 66 | ||||
-rw-r--r-- | include/linux/timb_dma.h | 55 | ||||
-rw-r--r-- | sound/soc/txx9/txx9aclc.c | 6 |
26 files changed, 1329 insertions, 272 deletions
diff --git a/arch/arm/mach-u300/include/mach/coh901318.h b/arch/arm/mach-u300/include/mach/coh901318.h index b8155b4e5ffa..193da2df732c 100644 --- a/arch/arm/mach-u300/include/mach/coh901318.h +++ b/arch/arm/mach-u300/include/mach/coh901318.h @@ -103,27 +103,6 @@ struct coh901318_platform { }; /** - * coh901318_get_bytes_left() - Get number of bytes left on a current transfer - * @chan: dma channel handle - * return number of bytes left, or negative on error - */ -u32 coh901318_get_bytes_left(struct dma_chan *chan); - -/** - * coh901318_stop() - Stops dma transfer - * @chan: dma channel handle - * return 0 on success otherwise negative value - */ -void coh901318_stop(struct dma_chan *chan); - -/** - * coh901318_continue() - Resumes a stopped dma transfer - * @chan: dma channel handle - * return 0 on success otherwise negative value - */ -void coh901318_continue(struct dma_chan *chan); - -/** * coh901318_filter_id() - DMA channel filter function * @chan: dma channel handle * @chan_id: id of dma channel to be filter out diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 9d8ca990dde6..18181f4300bf 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -149,6 +149,13 @@ config AMCC_PPC440SPE_ADMA help Enable support for the AMCC PPC440SPe RAID engines. +config TIMB_DMA + tristate "Timberdale FPGA DMA support" + depends on MFD_TIMBERDALE || HAS_IOMEM + select DMA_ENGINE + help + Enable support for the Timberdale FPGA DMA engine. + config ARCH_HAS_ASYNC_TX_FIND_CHANNEL bool diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 22bba3d5e2b6..40c627d8f73b 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -20,3 +20,4 @@ obj-$(CONFIG_TXX9_DMAC) += txx9dmac.o obj-$(CONFIG_SH_DMAE) += shdma.o obj-$(CONFIG_COH901318) += coh901318.o coh901318_lli.o obj-$(CONFIG_AMCC_PPC440SPE_ADMA) += ppc4xx/ +obj-$(CONFIG_TIMB_DMA) += timb_dma.o diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 278cf5bceef2..6693b130c4ff 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -760,13 +760,17 @@ err_desc_get: return NULL; } -static void atc_terminate_all(struct dma_chan *chan) +static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd) { struct at_dma_chan *atchan = to_at_dma_chan(chan); struct at_dma *atdma = to_at_dma(chan->device); struct at_desc *desc, *_desc; LIST_HEAD(list); + /* Only supports DMA_TERMINATE_ALL */ + if (cmd != DMA_TERMINATE_ALL) + return -ENXIO; + /* * This is only called when something went wrong elsewhere, so * we don't really care about the data. Just disable the @@ -790,32 +794,30 @@ static void atc_terminate_all(struct dma_chan *chan) /* Flush all pending and queued descriptors */ list_for_each_entry_safe(desc, _desc, &list, desc_node) atc_chain_complete(atchan, desc); + + return 0; } /** - * atc_is_tx_complete - poll for transaction completion + * atc_tx_status - poll for transaction completion * @chan: DMA channel * @cookie: transaction identifier to check status of - * @done: if not %NULL, updated with last completed transaction - * @used: if not %NULL, updated with last used transaction + * @txstate: if not %NULL updated with transaction state * - * If @done and @used are passed in, upon return they reflect the driver + * If @txstate is passed in, upon return it reflect the driver * internal state and can be used with dma_async_is_complete() to check * the status of multiple cookies without re-checking hardware state. */ static enum dma_status -atc_is_tx_complete(struct dma_chan *chan, +atc_tx_status(struct dma_chan *chan, dma_cookie_t cookie, - dma_cookie_t *done, dma_cookie_t *used) + struct dma_tx_state *txstate) { struct at_dma_chan *atchan = to_at_dma_chan(chan); dma_cookie_t last_used; dma_cookie_t last_complete; enum dma_status ret; - dev_vdbg(chan2dev(chan), "is_tx_complete: %d (d%d, u%d)\n", - cookie, done ? *done : 0, used ? *used : 0); - spin_lock_bh(&atchan->lock); last_complete = atchan->completed_cookie; @@ -833,10 +835,10 @@ atc_is_tx_complete(struct dma_chan *chan, spin_unlock_bh(&atchan->lock); - if (done) - *done = last_complete; - if (used) - *used = last_used; + dma_set_tx_state(txstate, last_complete, last_used, 0); + dev_vdbg(chan2dev(chan), "tx_status: %d (d%d, u%d)\n", + cookie, last_complete ? last_complete : 0, + last_used ? last_used : 0); return ret; } @@ -1082,7 +1084,7 @@ static int __init at_dma_probe(struct platform_device *pdev) /* set base routines */ atdma->dma_common.device_alloc_chan_resources = atc_alloc_chan_resources; atdma->dma_common.device_free_chan_resources = atc_free_chan_resources; - atdma->dma_common.device_is_tx_complete = atc_is_tx_complete; + atdma->dma_common.device_tx_status = atc_tx_status; atdma->dma_common.device_issue_pending = atc_issue_pending; atdma->dma_common.dev = &pdev->dev; @@ -1092,7 +1094,7 @@ static int __init at_dma_probe(struct platform_device *pdev) if (dma_has_cap(DMA_SLAVE, atdma->dma_common.cap_mask)) { atdma->dma_common.device_prep_slave_sg = atc_prep_slave_sg; - atdma->dma_common.device_terminate_all = atc_terminate_all; + atdma->dma_common.device_control = atc_control; } dma_writel(atdma, EN, AT_DMA_ENABLE); diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c index 1656fdcdb6c2..4233440741a2 100644 --- a/drivers/dma/coh901318.c +++ b/drivers/dma/coh901318.c @@ -37,7 +37,7 @@ struct coh901318_desc { struct list_head node; struct scatterlist *sg; unsigned int sg_len; - struct coh901318_lli *data; + struct coh901318_lli *lli; enum dma_data_direction dir; unsigned long flags; }; @@ -283,7 +283,7 @@ static int coh901318_start(struct coh901318_chan *cohc) } static int coh901318_prep_linked_list(struct coh901318_chan *cohc, - struct coh901318_lli *data) + struct coh901318_lli *lli) { int channel = cohc->id; void __iomem *virtbase = cohc->base->virtbase; @@ -292,18 +292,18 @@ static int coh901318_prep_linked_list(struct coh901318_chan *cohc, COH901318_CX_STAT_SPACING*channel) & COH901318_CX_STAT_ACTIVE); - writel(data->src_addr, + writel(lli->src_addr, virtbase + COH901318_CX_SRC_ADDR + COH901318_CX_SRC_ADDR_SPACING * channel); - writel(data->dst_addr, virtbase + + writel(lli->dst_addr, virtbase + COH901318_CX_DST_ADDR + COH901318_CX_DST_ADDR_SPACING * channel); - writel(data->link_addr, virtbase + COH901318_CX_LNK_ADDR + + writel(lli->link_addr, virtbase + COH901318_CX_LNK_ADDR + COH901318_CX_LNK_ADDR_SPACING * channel); - writel(data->control, virtbase + COH901318_CX_CTRL + + writel(lli->control, virtbase + COH901318_CX_CTRL + COH901318_CX_CTRL_SPACING * channel); return 0; @@ -408,33 +408,107 @@ coh901318_first_queued(struct coh901318_chan *cohc) return d; } +static inline u32 coh901318_get_bytes_in_lli(struct coh901318_lli *in_lli) +{ + struct coh901318_lli *lli = in_lli; + u32 bytes = 0; + + while (lli) { + bytes += lli->control & COH901318_CX_CTRL_TC_VALUE_MASK; + lli = lli->virt_link_addr; + } + return bytes; +} + /* - * DMA start/stop controls + * Get the number of bytes left to transfer on this channel, + * it is unwise to call this before stopping the channel for + * absolute measures, but for a rough guess you can still call + * it. */ -u32 coh901318_get_bytes_left(struct dma_chan *chan) +static u32 coh901318_get_bytes_left(struct dma_chan *chan) { - unsigned long flags; - u32 ret; struct coh901318_chan *cohc = to_coh901318_chan(chan); + struct coh901318_desc *cohd; + struct list_head *pos; + unsigned long flags; + u32 left = 0; + int i = 0; spin_lock_irqsave(&cohc->lock, flags); - /* Read transfer count value */ - ret = readl(cohc->base->virtbase + - COH901318_CX_CTRL+COH901318_CX_CTRL_SPACING * - cohc->id) & COH901318_CX_CTRL_TC_VALUE_MASK; + /* + * If there are many queued jobs, we iterate and add the + * size of them all. We take a special look on the first + * job though, since it is probably active. + */ + list_for_each(pos, &cohc->active) { + /* + * The first job in the list will be working on the + * hardware. The job can be stopped but still active, + * so that the transfer counter is somewhere inside + * the buffer. + */ + cohd = list_entry(pos, struct coh901318_desc, node); + + if (i == 0) { + struct coh901318_lli *lli; + dma_addr_t ladd; + + /* Read current transfer count value */ + left = readl(cohc->base->virtbase + + COH901318_CX_CTRL + + COH901318_CX_CTRL_SPACING * cohc->id) & + COH901318_CX_CTRL_TC_VALUE_MASK; + + /* See if the transfer is linked... */ + ladd = readl(cohc->base->virtbase + + COH901318_CX_LNK_ADDR + + COH901318_CX_LNK_ADDR_SPACING * + cohc->id) & + ~COH901318_CX_LNK_LINK_IMMEDIATE; + /* Single transaction */ + if (!ladd) + continue; + + /* + * Linked transaction, follow the lli, find the + * currently processing lli, and proceed to the next + */ + lli = cohd->lli; + while (lli && lli->link_addr != ladd) + lli = lli->virt_link_addr; + + if (lli) + lli = lli->virt_link_addr; + + /* + * Follow remaining lli links around to count the total + * number of bytes left + */ + left += coh901318_get_bytes_in_lli(lli); + } else { + left += coh901318_get_bytes_in_lli(cohd->lli); + } + i++; + } + + /* Also count bytes in the queued jobs */ + list_for_each(pos, &cohc->queue) { + cohd = list_entry(pos, struct coh901318_desc, node); + left += coh901318_get_bytes_in_lli(cohd->lli); + } spin_unlock_irqrestore(&cohc->lock, flags); - return ret; + return left; } -EXPORT_SYMBOL(coh901318_get_bytes_left); - -/* Stops a transfer without losing data. Enables power save. - Use this function in conjunction with coh901318_continue(..) -*/ -void coh901318_stop(struct dma_chan *chan) +/* + * Pauses a transfer without losing data. Enables power save. + * Use this function in conjunction with coh901318_resume. + */ +static void coh901318_pause(struct dma_chan *chan) { u32 val; unsigned long flags; @@ -475,12 +549,11 @@ void coh901318_stop(struct dma_chan *chan) spin_unlock_irqrestore(&cohc->lock, flags); } -EXPORT_SYMBOL(coh901318_stop); -/* Continues a transfer that has been stopped via 300_dma_stop(..). +/* Resumes a transfer that has been stopped via 300_dma_stop(..). Power save is handled. */ -void coh901318_continue(struct dma_chan *chan) +static void coh901318_resume(struct dma_chan *chan) { u32 val; unsigned long flags; @@ -506,7 +579,6 @@ void coh901318_continue(struct dma_chan *chan) spin_unlock_irqrestore(&cohc->lock, flags); } -EXPORT_SYMBOL(coh901318_continue); bool coh901318_filter_id(struct dma_chan *chan, void *chan_id) { @@ -565,29 +637,30 @@ static int coh901318_config(struct coh901318_chan *cohc, */ static struct coh901318_desc *coh901318_queue_start(struct coh901318_chan *cohc) { - struct coh901318_desc *cohd_que; + struct coh901318_desc *cohd; - /* start queued jobs, if any + /* + * start queued jobs, if any * TODO: transmit all queued jobs in one go */ - cohd_que = coh901318_first_queued(cohc); + cohd = coh901318_first_queued(cohc); - if (cohd_que != NULL) { + if (cohd != NULL) { /* Remove from queue */ - coh901318_desc_remove(cohd_que); + coh901318_desc_remove(cohd); /* initiate DMA job */ cohc->busy = 1; - coh901318_desc_submit(cohc, cohd_que); + coh901318_desc_submit(cohc, cohd); - coh901318_prep_linked_list(cohc, cohd_que->data); + coh901318_prep_linked_list(cohc, cohd->lli); - /* start dma job */ + /* start dma job on this channel */ coh901318_start(cohc); } - return cohd_que; + return cohd; } /* @@ -622,7 +695,7 @@ static void dma_tasklet(unsigned long data) cohc->completed = cohd_fin->desc.cookie; /* release the lli allocation and remove the descriptor */ - coh901318_lli_free(&cohc->base->pool, &cohd_fin->data); + coh901318_lli_free(&cohc->base->pool, &cohd_fin->lli); /* return desc to free-list */ coh901318_desc_remove(cohd_fin); @@ -666,23 +739,44 @@ static void dma_tasklet(unsigned long data) /* called from interrupt context */ static void dma_tc_handle(struct coh901318_chan *cohc) { - BUG_ON(!cohc->allocated && (list_empty(&cohc->active) || - list_empty(&cohc->queue))); - - if (!cohc->allocated) + /* + * If the channel is not allocated, then we shouldn't have + * any TC interrupts on it. + */ + if (!cohc->allocated) { + dev_err(COHC_2_DEV(cohc), "spurious interrupt from " + "unallocated channel\n"); return; + } spin_lock(&cohc->lock); + /* + * When we reach this point, at least one queue item + * should have been moved over from cohc->queue to + * cohc->active and run to completion, that is why we're + * getting a terminal count interrupt is it not? + * If you get this BUG() the most probable cause is that + * the individual nodes in the lli chain have IRQ enabled, + * so check your platform config for lli chain ctrl. + */ + BUG_ON(list_empty(&cohc->active)); + cohc->nbr_active_done++; + /* + * This attempt to take a job from cohc->queue, put it + * into cohc->active and start it. + */ if (coh901318_queue_start(cohc) == NULL) cohc->busy = 0; - BUG_ON(list_empty(&cohc->active)); - spin_unlock(&cohc->lock); + /* + * This tasklet will remove items from cohc->active + * and thus terminates them. + */ if (cohc_chan_conf(cohc)->priority_high) tasklet_hi_schedule(&cohc->tasklet); else @@ -809,6 +903,7 @@ static irqreturn_t dma_irq_handler(int irq, void *dev_id) static int coh901318_alloc_chan_resources(struct dma_chan *chan) { struct coh901318_chan *cohc = to_coh901318_chan(chan); + unsigned long flags; dev_vdbg(COHC_2_DEV(cohc), "[%s] DMA channel %d\n", __func__, cohc->id); @@ -816,11 +911,15 @@ static int coh901318_alloc_chan_resources(struct dma_chan *chan) if (chan->client_count > 1) return -EBUSY; + spin_lock_irqsave(&cohc->lock, flags); + coh901318_config(cohc, NULL); cohc->allocated = 1; cohc->completed = chan->cookie = 1; + spin_unlock_irqrestore(&cohc->lock, flags); + return 1; } @@ -843,7 +942,7 @@ coh901318_free_chan_resources(struct dma_chan *chan) spin_unlock_irqrestore(&cohc->lock, flags); - chan->device->device_terminate_all(chan); + chan->device->device_control(chan, DMA_TERMINATE_ALL); } @@ -870,7 +969,7 @@ static struct dma_async_tx_descriptor * coh901318_prep_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, size_t size, unsigned long flags) { - struct coh901318_lli *data; + struct coh901318_lli *lli; struct coh901318_desc *cohd; unsigned long flg; struct coh901318_chan *cohc = to_coh901318_chan(chan); @@ -892,23 +991,23 @@ coh901318_prep_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, if ((lli_len << MAX_DMA_PACKET_SIZE_SHIFT) < size) lli_len++; - data = coh901318_lli_alloc(&cohc->base->pool, lli_len); + lli = coh901318_lli_alloc(&cohc->base->pool, lli_len); - if (data == NULL) + if (lli == NULL) goto err; ret = coh901318_lli_fill_memcpy( - &cohc->base->pool, data, src, size, dest, + &cohc->base->pool, lli, src, size, dest, cohc_chan_param(cohc)->ctrl_lli_chained, ctrl_last); if (ret) goto err; - COH_DBG(coh901318_list_print(cohc, data)); + COH_DBG(coh901318_list_print(cohc, lli)); /* Pick a descriptor to handle this transfer */ cohd = coh901318_desc_get(cohc); - cohd->data = data; + cohd->lli = lli; cohd->flags = flags; cohd->desc.tx_submit = coh901318_tx_submit; @@ -926,7 +1025,7 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, unsigned long flags) { struct coh901318_chan *cohc = to_coh901318_chan(chan); - struct coh901318_lli *data; + struct coh901318_lli *lli; struct coh901318_desc *cohd; const struct coh901318_params *params; struct scatterlist *sg; @@ -999,13 +1098,13 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, } pr_debug("Allocate %d lli:s for this transfer\n", len); - data = coh901318_lli_alloc(&cohc->base->pool, len); + lli = coh901318_lli_alloc(&cohc->base->pool, len); - if (data == NULL) + if (lli == NULL) goto err_dma_alloc; - /* initiate allocated data list */ - ret = coh901318_lli_fill_sg(&cohc->base->pool, data, sgl, sg_len, + /* initiate allocated lli list */ + ret = coh901318_lli_fill_sg(&cohc->base->pool, lli, sgl, sg_len, cohc_dev_addr(cohc), ctrl_chained, ctrl, @@ -1014,14 +1113,14 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, if (ret) goto err_lli_fill; - COH_DBG(coh901318_list_print(cohc, data)); + COH_DBG(coh901318_list_print(cohc, lli)); /* Pick a descriptor to handle this transfer */ cohd = coh901318_desc_get(cohc); cohd->dir = direction; cohd->flags = flags; cohd->desc.tx_submit = coh901318_tx_submit; - cohd->data = data; + cohd->lli = lli; spin_unlock_irqrestore(&cohc->lock, flg); @@ -1035,9 +1134,8 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, } static enum dma_status -coh901318_is_tx_complete(struct dma_chan *chan, - dma_cookie_t cookie, dma_cookie_t *done, - dma_cookie_t *used) +coh901318_tx_status(struct dma_chan *chan, dma_cookie_t cookie, + struct dma_tx_state *txstate) { struct coh901318_chan *cohc = to_coh901318_chan(chan); dma_cookie_t last_used; @@ -1049,10 +1147,10 @@ coh901318_is_tx_complete(struct dma_chan *chan, ret = dma_async_is_complete(cookie, last_complete, last_used); - if (done) - *done = last_complete; - if (used) - *used = last_used; + dma_set_tx_state(txstate, last_complete, last_used, + coh901318_get_bytes_left(chan)); + if (ret == DMA_IN_PROGRESS && cohc->stopped) + ret = DMA_PAUSED; return ret; } @@ -1065,23 +1163,41 @@ coh901318_issue_pending(struct dma_chan *chan) spin_lock_irqsave(&cohc->lock, flags); - /* Busy means that pending jobs are already being processed */ + /* + * Busy means that pending jobs are already being processed, + * and then there is no point in starting the queue: the + * terminal count interrupt on the channel will take the next + * job on the queue and execute it anyway. + */ if (!cohc->busy) coh901318_queue_start(cohc); spin_unlock_irqrestore(&cohc->lock, flags); } -static void -coh901318_terminate_all(struct dma_chan *chan) +static int +coh901318_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd) { unsigned long flags; struct coh901318_chan *cohc = to_coh901318_chan(chan); struct coh901318_desc *cohd; void __iomem *virtbase = cohc->base->virtbase; - coh901318_stop(chan); + if (cmd == DMA_PAUSE) { + coh901318_pause(chan); + return 0; + } + + if (cmd == DMA_RESUME) { + coh901318_resume(chan); + return 0; + } + if (cmd != DMA_TERMINATE_ALL) + return -ENXIO; + + /* The remainder of this function terminates the transfer */ + coh901318_pause(chan); spin_lock_irqsave(&cohc->lock, flags); /* Clear any pending BE or TC interrupt */ @@ -1099,7 +1215,7 @@ coh901318_terminate_all(struct dma_chan *chan) while ((cohd = coh901318_first_active_get(cohc))) { /* release the lli allocation*/ - coh901318_lli_free(&cohc->base->pool, &cohd->data); + coh901318_lli_free(&cohc->base->pool, &cohd->lli); /* return desc to free-list */ coh901318_desc_remove(cohd); @@ -1108,7 +1224,7 @@ coh901318_terminate_all(struct dma_chan *chan) while ((cohd = coh901318_first_queued(cohc))) { /* release the lli allocation*/ - coh901318_lli_free(&cohc->base->pool, &cohd->data); + coh901318_lli_free(&cohc->base->pool, &cohd->lli); /* return desc to free-list */ coh901318_desc_remove(cohd); @@ -1120,6 +1236,8 @@ coh901318_terminate_all(struct dma_chan *chan) cohc->busy = 0; spin_unlock_irqrestore(&cohc->lock, flags); + + return 0; } void coh901318_base_init(struct dma_device *dma, const int *pick_chans, struct coh901318_base *base) @@ -1235,9 +1353,9 @@ static int __init coh901318_probe(struct platform_device *pdev) base->dma_slave.device_alloc_chan_resources = coh901318_alloc_chan_resources; base->dma_slave.device_free_chan_resources = coh901318_free_chan_resources; base->dma_slave.device_prep_slave_sg = coh901318_prep_slave_sg; - base->dma_slave.device_is_tx_complete = coh901318_is_tx_complete; + base->dma_slave.device_tx_status = coh901318_tx_status; base->dma_slave.device_issue_pending = coh901318_issue_pending; - base->dma_slave.device_terminate_all = coh901318_terminate_all; + base->dma_slave.device_control = coh901318_control; base->dma_slave.dev = &pdev->dev; err = dma_async_device_register(&base->dma_slave); @@ -1255,9 +1373,9 @@ static int __init coh901318_probe(struct platform_device *pdev) base->dma_memcpy.device_alloc_chan_resources = coh901318_alloc_chan_resources; base->dma_memcpy.device_free_chan_resources = coh901318_free_chan_resources; base->dma_memcpy.device_prep_dma_memcpy = coh901318_prep_memcpy; - base->dma_memcpy.device_is_tx_complete = coh901318_is_tx_complete; + base->dma_memcpy.device_tx_status = coh901318_tx_status; base->dma_memcpy.device_issue_pending = coh901318_issue_pending; - base->dma_memcpy.device_terminate_all = coh901318_terminate_all; + base->dma_memcpy.device_control = coh901318_control; base->dma_memcpy.dev = &pdev->dev; /* * This controller can only access address at even 32bit boundaries, diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index d18b5d069d7e..9d88ab74b767 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -695,11 +695,11 @@ int dma_async_device_register(struct dma_device *device) BUG_ON(dma_has_cap(DMA_SLAVE, device->cap_mask) && !device->device_prep_slave_sg); BUG_ON(dma_has_cap(DMA_SLAVE, device->cap_mask) && - !device->device_terminate_all); + !device->device_control); BUG_ON(!device->device_alloc_chan_resources); BUG_ON(!device->device_free_chan_resources); - BUG_ON(!device->device_is_tx_complete); + BUG_ON(!device->device_tx_status); BUG_ON(!device->device_issue_pending); BUG_ON(!device->dev); diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index d28369f7afd2..18fb5b41cedf 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -781,13 +781,17 @@ err_desc_get: return NULL; } -static void dwc_terminate_all(struct dma_chan *chan) +static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd) { struct dw_dma_chan *dwc = to_dw_dma_chan(chan); struct dw_dma *dw = to_dw_dma(chan->device); struct dw_desc *desc, *_desc; LIST_HEAD(list); + /* Only supports DMA_TERMINATE_ALL */ + if (cmd != DMA_TERMINATE_ALL) + return -ENXIO; + /* * This is only called when something went wrong elsewhere, so * we don't really care about the data. Just disable the @@ -810,12 +814,14 @@ static void dwc_terminate_all(struct dma_chan *chan) /* Flush all pending and queued descriptors */ list_for_each_entry_safe(desc, _desc, &list, desc_node) dwc_descriptor_complete(dwc, desc); + + return 0; } static enum dma_status -dwc_is_tx_complete(struct dma_chan *chan, - dma_cookie_t cookie, - dma_cookie_t *done, dma_cookie_t *used) +dwc_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, + struct dma_tx_state *txstate) { struct dw_dma_chan *dwc = to_dw_dma_chan(chan); dma_cookie_t last_used; @@ -835,10 +841,7 @@ dwc_is_tx_complete(struct dma_chan *chan, ret = dma_async_is_complete(cookie, last_complete, last_used); } - if (done) - *done = last_complete; - if (used) - *used = last_used; + dma_set_tx_state(txstate, last_complete, last_used, 0); return ret; } @@ -1338,9 +1341,9 @@ static int __init dw_probe(struct platform_device *pdev) dw->dma.device_prep_dma_memcpy = dwc_prep_dma_memcpy; dw->dma.device_prep_slave_sg = dwc_prep_slave_sg; - dw->dma.device_terminate_all = dwc_terminate_all; + dw->dma.device_control = dwc_control; - dw->dma.device_is_tx_complete = dwc_is_tx_complete; + dw->dma.device_tx_status = dwc_tx_status; dw->dma.device_issue_pending = dwc_issue_pending; dma_writel(dw, CFG, DW_CFG_DMA_EN); diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index 88f470f0d820..6a853e992a1c 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -775,13 +775,18 @@ fail: return NULL; } -static void fsl_dma_device_terminate_all(struct dma_chan *dchan) +static int fsl_dma_device_control(struct dma_chan *dchan, + enum dma_ctrl_cmd cmd) { struct fsldma_chan *chan; unsigned long flags; + /* Only supports DMA_TERMINATE_ALL */ + if (cmd != DMA_TERMINATE_ALL) + return -ENXIO; + if (!dchan) - return; + return -EINVAL; chan = to_fsl_chan(dchan); @@ -795,6 +800,8 @@ static void fsl_dma_device_terminate_all(struct dma_chan *dchan) fsldma_free_desc_list(chan, &chan->ld_running); spin_unlock_irqrestore(&chan->desc_lock, flags); + + return 0; } /** @@ -965,13 +972,12 @@ static void fsl_dma_memcpy_issue_pending(struct dma_chan *dchan) } /** - * fsl_dma_is_complete - Determine the DMA status + * fsl_tx_status - Determine the DMA status * @chan : Freescale DMA channel */ -static enum dma_status fsl_dma_is_complete(struct dma_chan *dchan, +static enum dma_status fsl_tx_status(struct dma_chan *dchan, dma_cookie_t cookie, - dma_cookie_t *done, - dma_cookie_t *used) + struct dma_tx_state *txstate) { struct fsldma_chan *chan = to_fsl_chan(dchan); dma_cookie_t last_used; @@ -982,11 +988,7 @@ static enum dma_status fsl_dma_is_complete(struct dma_chan *dchan, last_used = dchan->cookie; last_complete = chan->completed_cookie; - if (done) - *done = last_complete; - - if (used) - *used = last_used; + dma_set_tx_state(txstate, last_complete, last_used, 0); return dma_async_is_complete(cookie, last_complete, last_used); } @@ -1330,10 +1332,10 @@ static int __devinit fsldma_of_probe(struct of_device *op, fdev->common.device_free_chan_resources = fsl_dma_free_chan_resources; fdev->common.device_prep_dma_interrupt = fsl_dma_prep_interrupt; fdev->common.device_prep_dma_memcpy = fsl_dma_prep_memcpy; - fdev->common.device_is_tx_complete = fsl_dma_is_complete; + fdev->common.device_tx_status = fsl_tx_status; fdev->common.device_issue_pending = fsl_dma_memcpy_issue_pending; fdev->common.device_prep_slave_sg = fsl_dma_prep_slave_sg; - fdev->common.device_terminate_all = fsl_dma_device_terminate_all; + fdev->common.device_control = fsl_dma_device_control; fdev->common.dev = &op->dev; dev_set_drvdata(&op->dev, fdev); diff --git a/drivers/dma/ioat/dma.c b/drivers/dma/ioat/dma.c index 3e5a8005c62b..c9213ead4a26 100644 --- a/drivers/dma/ioat/dma.c +++ b/drivers/dma/ioat/dma.c @@ -727,18 +727,18 @@ static void ioat1_timer_event(unsigned long data) } enum dma_status -ioat_is_dma_complete(struct dma_chan *c, dma_cookie_t cookie, - dma_cookie_t *done, dma_cookie_t *used) +ioat_dma_tx_status(struct dma_chan *c, dma_cookie_t cookie, + struct dma_tx_state *txstate) { struct ioat_chan_common *chan = to_chan_common(c); struct ioatdma_device *device = chan->device; - if (ioat_is_complete(c, cookie, done, used) == DMA_SUCCESS) + if (ioat_tx_status(c, cookie, txstate) == DMA_SUCCESS) return DMA_SUCCESS; device->cleanup_fn((unsigned long) c); - return ioat_is_complete(c, cookie, done, used); + return ioat_tx_status(c, cookie, txstate); } static void ioat1_dma_start_null_desc(struct ioat_dma_chan *ioat) @@ -858,7 +858,7 @@ int __devinit ioat_dma_self_test(struct ioatdma_device *device) tmo = wait_for_completion_timeout(&cmp, msecs_to_jiffies(3000)); if (tmo == 0 || - dma->device_is_tx_complete(dma_chan, cookie, NULL, NULL) + dma->device_tx_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { dev_err(dev, "Self-test copy timed out, disabling\n"); err = -ENODEV; @@ -1199,7 +1199,7 @@ int __devinit ioat1_dma_probe(struct ioatdma_device *device, int dca) dma->device_issue_pending = ioat1_dma_memcpy_issue_pending; dma->device_alloc_chan_resources = ioat1_dma_alloc_chan_resources; dma->device_free_chan_resources = ioat1_dma_free_chan_resources; - dma->device_is_tx_complete = ioat_is_dma_complete; + dma->device_tx_status = ioat_dma_tx_status; err = ioat_probe(device); if (err) diff --git a/drivers/dma/ioat/dma.h b/drivers/dma/ioat/dma.h index 86b97ac8774e..26f48ef94c5c 100644 --- a/drivers/dma/ioat/dma.h +++ b/drivers/dma/ioat/dma.h @@ -142,15 +142,14 @@ static inline struct ioat_dma_chan *to_ioat_chan(struct dma_chan *c) } /** - * ioat_is_complete - poll the status of an ioat transaction + * ioat_tx_status - poll the status of an ioat transaction * @c: channel handle * @cookie: transaction identifier - * @done: if set, updated with last completed transaction - * @used: if set, updated with last used transaction + * @txstate: if set, updated with the transaction state */ static inline enum dma_status -ioat_is_complete(struct dma_chan *c, dma_cookie_t cookie, - dma_cookie_t *done, dma_cookie_t *used) +ioat_tx_status(struct dma_chan *c, dma_cookie_t cookie, + struct dma_tx_state *txstate) { struct ioat_chan_common *chan = to_chan_common(c); dma_cookie_t last_used; @@ -159,10 +158,7 @@ ioat_is_complete(struct dma_chan *c, dma_cookie_t cookie, last_used = c->cookie; last_complete = chan->completed_cookie; - if (done) - *done = last_complete; - if (used) - *used = last_used; + dma_set_tx_state(txstate, last_complete, last_used, 0); return dma_async_is_complete(cookie, last_complete, last_used); } @@ -338,8 +334,8 @@ struct dca_provider * __devinit ioat_dca_init(struct pci_dev *pdev, unsigned long ioat_get_current_completion(struct ioat_chan_common *chan); void ioat_init_channel(struct ioatdma_device *device, struct ioat_chan_common *chan, int idx); -enum dma_status ioat_is_dma_complete(struct dma_chan *c, dma_cookie_t cookie, - dma_cookie_t *done, dma_cookie_t *used); +enum dma_status ioat_dma_tx_status(struct dma_chan *c, dma_cookie_t cookie, + struct dma_tx_state *txstate); void ioat_dma_unmap(struct ioat_chan_common *chan, enum dma_ctrl_flags flags, size_t len, struct ioat_dma_descriptor *hw); bool ioat_cleanup_preamble(struct ioat_chan_common *chan, diff --git a/drivers/dma/ioat/dma_v2.c b/drivers/dma/ioat/dma_v2.c index b5ae56c211e6..2d02f6d6fbe4 100644 --- a/drivers/dma/ioat/dma_v2.c +++ b/drivers/dma/ioat/dma_v2.c @@ -855,7 +855,7 @@ int __devinit ioat2_dma_probe(struct ioatdma_device *device, int dca) dma->device_issue_pending = ioat2_issue_pending; dma->device_alloc_chan_resources = ioat2_alloc_chan_resources; dma->device_free_chan_resources = ioat2_free_chan_resources; - dma->device_is_tx_complete = ioat_is_dma_complete; + dma->device_tx_status = ioat_tx_status; err = ioat_probe(device); if (err) diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c index 6740e319c9cf..720c3ed04e54 100644 --- a/drivers/dma/ioat/dma_v3.c +++ b/drivers/dma/ioat/dma_v3.c @@ -439,17 +439,17 @@ static void ioat3_timer_event(unsigned long data) } static enum dma_status -ioat3_is_complete(struct dma_chan *c, dma_cookie_t cookie, - dma_cookie_t *done, dma_cookie_t *used) +ioat3_tx_status(struct dma_chan *c, dma_cookie_t cookie, + struct dma_tx_state *txstate) { struct ioat2_dma_chan *ioat = to_ioat2_chan(c); - if (ioat_is_complete(c, cookie, done, used) == DMA_SUCCESS) + if (ioat_tx_status(c, cookie, txstate) == DMA_SUCCESS) return DMA_SUCCESS; ioat3_cleanup_poll(ioat); - return ioat_is_complete(c, cookie, done, used); + return ioat_tx_status(c, cookie, txstate); } static struct dma_async_tx_descriptor * @@ -977,7 +977,7 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) tmo = wait_for_completion_timeout(&cmp, msecs_to_jiffies(3000)); - if (dma->device_is_tx_complete(dma_chan, cookie, NULL, NULL) != DMA_SUCCESS) { + if (dma->device_tx_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { dev_err(dev, "Self-test xor timed out\n"); err = -ENODEV; goto free_resources; @@ -1031,7 +1031,7 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) tmo = wait_for_completion_timeout(&cmp, msecs_to_jiffies(3000)); - if (dma->device_is_tx_complete(dma_chan, cookie, NULL, NULL) != DMA_SUCCESS) { + if (dma->device_tx_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { dev_err(dev, "Self-test validate timed out\n"); err = -ENODEV; goto free_resources; @@ -1072,7 +1072,7 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) tmo = wait_for_completion_timeout(&cmp, msecs_to_jiffies(3000)); - if (dma->device_is_tx_complete(dma_chan, cookie, NULL, NULL) != DMA_SUCCESS) { + if (dma->device_tx_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { dev_err(dev, "Self-test memset timed out\n"); err = -ENODEV; goto free_resources; @@ -1115,7 +1115,7 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) tmo = wait_for_completion_timeout(&cmp, msecs_to_jiffies(3000)); - if (dma->device_is_tx_complete(dma_chan, cookie, NULL, NULL) != DMA_SUCCESS) { + if (dma->device_tx_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { dev_err(dev, "Self-test 2nd validate timed out\n"); err = -ENODEV; goto free_resources; @@ -1259,11 +1259,11 @@ int __devinit ioat3_dma_probe(struct ioatdma_device *device, int dca) if (is_raid_device) { - dma->device_is_tx_complete = ioat3_is_complete; + dma->device_tx_status = ioat3_tx_status; device->cleanup_fn = ioat3_cleanup_event; device->timer_fn = ioat3_timer_event; } else { - dma->device_is_tx_complete = ioat_is_dma_complete; + dma->device_tx_status = ioat_dma_tx_status; device->cleanup_fn = ioat2_cleanup_event; device->timer_fn = ioat2_timer_event; } diff --git a/drivers/dma/iop-adma.c b/drivers/dma/iop-adma.c index 1ebc801678b0..161c452923b8 100644 --- a/drivers/dma/iop-adma.c +++ b/drivers/dma/iop-adma.c @@ -894,14 +894,14 @@ static void iop_adma_free_chan_resources(struct dma_chan *chan) } /** - * iop_adma_is_complete - poll the status of an ADMA transaction + * iop_adma_status - poll the status of an ADMA transaction * @chan: ADMA channel handle * @cookie: ADMA transaction identifier + * @txstate: a holder for the current state of the channel or NULL */ -static enum dma_status iop_adma_is_complete(struct dma_chan *chan, +static enum dma_status iop_adma_status(struct dma_chan *chan, dma_cookie_t cookie, - dma_cookie_t *done, - dma_cookie_t *used) + struct dma_tx_state *txstate) { struct iop_adma_chan *iop_chan = to_iop_adma_chan(chan); dma_cookie_t last_used; @@ -910,12 +910,7 @@ static enum dma_status iop_adma_is_complete(struct dma_chan *chan, last_used = chan->cookie; last_complete = iop_chan->completed_cookie; - - if (done) - *done = last_complete; - if (used) - *used = last_used; - + dma_set_tx_state(txstate, last_complete, last_used, 0); ret = dma_async_is_complete(cookie, last_complete, last_used); if (ret == DMA_SUCCESS) return ret; @@ -924,11 +919,7 @@ static enum dma_status iop_adma_is_complete(struct dma_chan *chan, last_used = chan->cookie; last_complete = iop_chan->completed_cookie; - - if (done) - *done = last_complete; - if (used) - *used = last_used; + dma_set_tx_state(txstate, last_complete, last_used, 0); return dma_async_is_complete(cookie, last_complete, last_used); } @@ -1043,7 +1034,7 @@ static int __devinit iop_adma_memcpy_self_test(struct iop_adma_device *device) iop_adma_issue_pending(dma_chan); msleep(1); - if (iop_adma_is_complete(dma_chan, cookie, NULL, NULL) != + if (iop_adma_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { dev_printk(KERN_ERR, dma_chan->device->dev, "Self-test copy timed out, disabling\n"); @@ -1143,7 +1134,7 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device) iop_adma_issue_pending(dma_chan); msleep(8); - if (iop_adma_is_complete(dma_chan, cookie, NULL, NULL) != + if (iop_adma_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { dev_printk(KERN_ERR, dma_chan->device->dev, "Self-test xor timed out, disabling\n"); @@ -1190,7 +1181,7 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device) iop_adma_issue_pending(dma_chan); msleep(8); - if (iop_adma_is_complete(dma_chan, cookie, NULL, NULL) != DMA_SUCCESS) { + if (iop_adma_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { dev_printk(KERN_ERR, dma_chan->device->dev, "Self-test zero sum timed out, disabling\n"); err = -ENODEV; @@ -1214,7 +1205,7 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device) iop_adma_issue_pending(dma_chan); msleep(8); - if (iop_adma_is_complete(dma_chan, cookie, NULL, NULL) != DMA_SUCCESS) { + if (iop_adma_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { dev_printk(KERN_ERR, dma_chan->device->dev, "Self-test memset timed out, disabling\n"); err = -ENODEV; @@ -1246,7 +1237,7 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device) iop_adma_issue_pending(dma_chan); msleep(8); - if (iop_adma_is_complete(dma_chan, cookie, NULL, NULL) != DMA_SUCCESS) { + if (iop_adma_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { dev_printk(KERN_ERR, dma_chan->device->dev, "Self-test non-zero sum timed out, disabling\n"); err = -ENODEV; @@ -1341,7 +1332,7 @@ iop_adma_pq_zero_sum_self_test(struct iop_adma_device *device) iop_adma_issue_pending(dma_chan); msleep(8); - if (iop_adma_is_complete(dma_chan, cookie, NULL, NULL) != + if (iop_adma_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { dev_err(dev, "Self-test pq timed out, disabling\n"); err = -ENODEV; @@ -1378,7 +1369,7 @@ iop_adma_pq_zero_sum_self_test(struct iop_adma_device *device) iop_adma_issue_pending(dma_chan); msleep(8); - if (iop_adma_is_complete(dma_chan, cookie, NULL, NULL) != + if (iop_adma_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { dev_err(dev, "Self-test pq-zero-sum timed out, disabling\n"); err = -ENODEV; @@ -1410,7 +1401,7 @@ iop_adma_pq_zero_sum_self_test(struct iop_adma_device *device) iop_adma_issue_pending(dma_chan); msleep(8); - if (iop_adma_is_complete(dma_chan, cookie, NULL, NULL) != + if (iop_adma_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { dev_err(dev, "Self-test !pq-zero-sum timed out, disabling\n"); err = -ENODEV; @@ -1508,7 +1499,7 @@ static int __devinit iop_adma_probe(struct platform_device *pdev) /* set base routines */ dma_dev->device_alloc_chan_resources = iop_adma_alloc_chan_resources; dma_dev->device_free_chan_resources = iop_adma_free_chan_resources; - dma_dev->device_is_tx_complete = iop_adma_is_complete; + dma_dev->device_tx_status = iop_adma_status; dma_dev->device_issue_pending = iop_adma_issue_pending; dma_dev->dev = &pdev->dev; diff --git a/drivers/dma/ipu/ipu_idmac.c b/drivers/dma/ipu/ipu_idmac.c index 2a446397c884..246a6143e4a7 100644 --- a/drivers/dma/ipu/ipu_idmac.c +++ b/drivers/dma/ipu/ipu_idmac.c @@ -1472,13 +1472,17 @@ static void idmac_issue_pending(struct dma_chan *chan) */ } -static void __idmac_terminate_all(struct dma_chan *chan) +static int __idmac_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd) { struct idmac_channel *ichan = to_idmac_chan(chan); struct idmac *idmac = to_idmac(chan->device); unsigned long flags; int i; + /* Only supports DMA_TERMINATE_ALL */ + if (cmd != DMA_TERMINATE_ALL) + return -ENXIO; + ipu_disable_channel(idmac, ichan, ichan->status >= IPU_CHANNEL_ENABLED); @@ -1505,17 +1509,22 @@ static void __idmac_terminate_all(struct dma_chan *chan) tasklet_enable(&to_ipu(idmac)->tasklet); ichan->status = IPU_CHANNEL_INITIALIZED; + + return 0; } -static void idmac_terminate_all(struct dma_chan *chan) +static int idmac_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd) { struct idmac_channel *ichan = to_idmac_chan(chan); + int ret; mutex_lock(&ichan->chan_mutex); - __idmac_terminate_all(chan); + ret = __idmac_control(chan, cmd); mutex_unlock(&ichan->chan_mutex); + + return ret; } #ifdef DEBUG @@ -1607,7 +1616,7 @@ static void idmac_free_chan_resources(struct dma_chan *chan) mutex_lock(&ichan->chan_mutex); - __idmac_terminate_all(chan); + __idmac_control(chan, DMA_TERMINATE_ALL); if (ichan->status > IPU_CHANNEL_FREE) { #ifdef DEBUG @@ -1637,15 +1646,12 @@ static void idmac_free_chan_resources(struct dma_chan *chan) tasklet_schedule(&to_ipu(idmac)->tasklet); } -static enum dma_status idmac_is_tx_complete(struct dma_chan *chan, - dma_cookie_t cookie, dma_cookie_t *done, dma_cookie_t *used) +static enum dma_status idmac_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, struct dma_tx_state *txstate) { struct idmac_channel *ichan = to_idmac_chan(chan); - if (done) - *done = ichan->completed; - if (used) - *used = chan->cookie; + dma_set_tx_state(txstate, ichan->completed, chan->cookie, 0); if (cookie != chan->cookie) return DMA_ERROR; return DMA_SUCCESS; @@ -1664,12 +1670,12 @@ static int __init ipu_idmac_init(struct ipu *ipu) dma->dev = ipu->dev; dma->device_alloc_chan_resources = idmac_alloc_chan_resources; dma->device_free_chan_resources = idmac_free_chan_resources; - dma->device_is_tx_complete = idmac_is_tx_complete; + dma->device_tx_status = idmac_tx_status; dma->device_issue_pending = idmac_issue_pending; /* Compulsory for DMA_SLAVE fields */ dma->device_prep_slave_sg = idmac_prep_slave_sg; - dma->device_terminate_all = idmac_terminate_all; + dma->device_control = idmac_control; INIT_LIST_HEAD(&dma->channels); for (i = 0; i < IPU_CHANNELS_NUM; i++) { @@ -1703,7 +1709,7 @@ static void __exit ipu_idmac_exit(struct ipu *ipu) for (i = 0; i < IPU_CHANNELS_NUM; i++) { struct idmac_channel *ichan = ipu->channel + i; - idmac_terminate_all(&ichan->dma_chan); + idmac_control(&ichan->dma_chan, DMA_TERMINATE_ALL); idmac_prep_slave_sg(&ichan->dma_chan, NULL, 0, DMA_NONE, 0); } diff --git a/drivers/dma/mpc512x_dma.c b/drivers/dma/mpc512x_dma.c index bbbd58566625..84326c961d4b 100644 --- a/drivers/dma/mpc512x_dma.c +++ b/drivers/dma/mpc512x_dma.c @@ -541,8 +541,8 @@ static void mpc_dma_issue_pending(struct dma_chan *chan) /* Check request completion status */ static enum dma_status -mpc_dma_is_tx_complete(struct dma_chan *chan, dma_cookie_t cookie, - dma_cookie_t *done, dma_cookie_t *used) +mpc_dma_tx_status(struct dma_chan *chan, dma_cookie_t cookie, + struct dma_tx_state *txstate) { struct mpc_dma_chan *mchan = dma_chan_to_mpc_dma_chan(chan); unsigned long flags; @@ -554,12 +554,7 @@ mpc_dma_is_tx_complete(struct dma_chan *chan, dma_cookie_t cookie, last_complete = mchan->completed_cookie; spin_unlock_irqrestore(&mchan->lock, flags); - if (done) - *done = last_complete; - - if (used) - *used = last_used; - + dma_set_tx_state(txstate, last_complete, last_used, 0); return dma_async_is_complete(cookie, last_complete, last_used); } @@ -694,7 +689,7 @@ static int __devinit mpc_dma_probe(struct of_device *op, dma->device_alloc_chan_resources = mpc_dma_alloc_chan_resources; dma->device_free_chan_resources = mpc_dma_free_chan_resources; dma->device_issue_pending = mpc_dma_issue_pending; - dma->device_is_tx_complete = mpc_dma_is_tx_complete; + dma->device_tx_status = mpc_dma_tx_status; dma->device_prep_dma_memcpy = mpc_dma_prep_memcpy; INIT_LIST_HEAD(&dma->channels); diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index e2fd34da64f2..86c5ae9fde34 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -810,14 +810,14 @@ static void mv_xor_free_chan_resources(struct dma_chan *chan) } /** - * mv_xor_is_complete - poll the status of an XOR transaction + * mv_xor_status - poll the status of an XOR transaction * @chan: XOR channel handle * @cookie: XOR transaction identifier + * @txstate: XOR transactions state holder (or NULL) */ -static enum dma_status mv_xor_is_complete(struct dma_chan *chan, +static enum dma_status mv_xor_status(struct dma_chan *chan, dma_cookie_t cookie, - dma_cookie_t *done, - dma_cookie_t *used) + struct dma_tx_state *txstate) { struct mv_xor_chan *mv_chan = to_mv_xor_chan(chan); dma_cookie_t last_used; @@ -827,10 +827,7 @@ static enum dma_status mv_xor_is_complete(struct dma_chan *chan, last_used = chan->cookie; last_complete = mv_chan->completed_cookie; mv_chan->is_complete_cookie = cookie; - if (done) - *done = last_complete; - if (used) - *used = last_used; + dma_set_tx_state(txstate, last_complete, last_used, 0); ret = dma_async_is_complete(cookie, last_complete, last_used); if (ret == DMA_SUCCESS) { @@ -842,11 +839,7 @@ static enum dma_status mv_xor_is_complete(struct dma_chan *chan, last_used = chan->cookie; last_complete = mv_chan->completed_cookie; - if (done) - *done = last_complete; - if (used) - *used = last_used; - + dma_set_tx_state(txstate, last_complete, last_used, 0); return dma_async_is_complete(cookie, last_complete, last_used); } @@ -975,7 +968,7 @@ static int __devinit mv_xor_memcpy_self_test(struct mv_xor_device *device) async_tx_ack(tx); msleep(1); - if (mv_xor_is_complete(dma_chan, cookie, NULL, NULL) != + if (mv_xor_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { dev_printk(KERN_ERR, dma_chan->device->dev, "Self-test copy timed out, disabling\n"); @@ -1073,7 +1066,7 @@ mv_xor_xor_self_test(struct mv_xor_device *device) async_tx_ack(tx); msleep(8); - if (mv_xor_is_complete(dma_chan, cookie, NULL, NULL) != + if (mv_xor_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { dev_printk(KERN_ERR, dma_chan->device->dev, "Self-test xor timed out, disabling\n"); @@ -1168,7 +1161,7 @@ static int __devinit mv_xor_probe(struct platform_device *pdev) /* set base routines */ dma_dev->device_alloc_chan_resources = mv_xor_alloc_chan_resources; dma_dev->device_free_chan_resources = mv_xor_free_chan_resources; - dma_dev->device_is_tx_complete = mv_xor_is_complete; + dma_dev->device_tx_status = mv_xor_status; dma_dev->device_issue_pending = mv_xor_issue_pending; dma_dev->dev = &pdev->dev; diff --git a/drivers/dma/ppc4xx/adma.c b/drivers/dma/ppc4xx/adma.c index d44626fa35ad..c6079fcca13f 100644 --- a/drivers/dma/ppc4xx/adma.c +++ b/drivers/dma/ppc4xx/adma.c @@ -3935,12 +3935,13 @@ static void ppc440spe_adma_free_chan_resources(struct dma_chan *chan) } /** - * ppc440spe_adma_is_complete - poll the status of an ADMA transaction + * ppc440spe_adma_tx_status - poll the status of an ADMA transaction * @chan: ADMA channel handle * @cookie: ADMA transaction identifier + * @txstate: a holder for the current state of the channel */ -static enum dma_status ppc440spe_adma_is_complete(struct dma_chan *chan, - dma_cookie_t cookie, dma_cookie_t *done, dma_cookie_t *used) +static enum dma_status ppc440spe_adma_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, struct dma_tx_state *txstate) { struct ppc440spe_adma_chan *ppc440spe_chan; dma_cookie_t last_used; @@ -3951,10 +3952,7 @@ static enum dma_status ppc440spe_adma_is_complete(struct dma_chan *chan, last_used = chan->cookie; last_complete = ppc440spe_chan->completed_cookie; - if (done) - *done = last_complete; - if (used) - *used = last_used; + dma_set_tx_state(txstate, last_complete, last_used, 0); ret = dma_async_is_complete(cookie, last_complete, last_used); if (ret == DMA_SUCCESS) @@ -3965,10 +3963,7 @@ static enum dma_status ppc440spe_adma_is_complete(struct dma_chan *chan, last_used = chan->cookie; last_complete = ppc440spe_chan->completed_cookie; - if (done) - *done = last_complete; - if (used) - *used = last_used; + dma_set_tx_state(txstate, last_complete, last_used, 0); return dma_async_is_complete(cookie, last_complete, last_used); } @@ -4180,7 +4175,7 @@ static void ppc440spe_adma_init_capabilities(struct ppc440spe_adma_device *adev) ppc440spe_adma_alloc_chan_resources; adev->common.device_free_chan_resources = ppc440spe_adma_free_chan_resources; - adev->common.device_is_tx_complete = ppc440spe_adma_is_complete; + adev->common.device_tx_status = ppc440spe_adma_tx_status; adev->common.device_issue_pending = ppc440spe_adma_issue_pending; /* Set prep routines based on capability */ diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c index ed3ef22e68a4..e65ec6e72a5d 100644 --- a/drivers/dma/shdma.c +++ b/drivers/dma/shdma.c @@ -580,12 +580,16 @@ static struct dma_async_tx_descriptor *sh_dmae_prep_slave_sg( direction, flags); } -static void sh_dmae_terminate_all(struct dma_chan *chan) +static int sh_dmae_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd) { struct sh_dmae_chan *sh_chan = to_sh_chan(chan); + /* Only supports DMA_TERMINATE_ALL */ + if (cmd != DMA_TERMINATE_ALL) + return -ENXIO; + if (!chan) - return; + return -EINVAL; dmae_halt(sh_chan); @@ -601,6 +605,8 @@ static void sh_dmae_terminate_all(struct dma_chan *chan) spin_unlock_bh(&sh_chan->desc_lock); sh_dmae_chan_ld_cleanup(sh_chan, true); + + return 0; } static dma_async_tx_callback __ld_cleanup(struct sh_dmae_chan *sh_chan, bool all) @@ -732,10 +738,9 @@ static void sh_dmae_memcpy_issue_pending(struct dma_chan *chan) sh_chan_xfer_ld_queue(sh_chan); } -static enum dma_status sh_dmae_is_complete(struct dma_chan *chan, +static enum dma_status sh_dmae_tx_status(struct dma_chan *chan, dma_cookie_t cookie, - dma_cookie_t *done, - dma_cookie_t *used) + struct dma_tx_state *txstate) { struct sh_dmae_chan *sh_chan = to_sh_chan(chan); dma_cookie_t last_used; @@ -747,12 +752,7 @@ static enum dma_status sh_dmae_is_complete(struct dma_chan *chan, last_used = chan->cookie; last_complete = sh_chan->completed_cookie; BUG_ON(last_complete < 0); - - if (done) - *done = last_complete; - - if (used) - *used = last_used; + dma_set_tx_state(txstate, last_complete, last_used, 0); spin_lock_bh(&sh_chan->desc_lock); @@ -1024,12 +1024,12 @@ static int __init sh_dmae_probe(struct platform_device *pdev) = sh_dmae_alloc_chan_resources; shdev->common.device_free_chan_resources = sh_dmae_free_chan_resources; shdev->common.device_prep_dma_memcpy = sh_dmae_prep_memcpy; - shdev->common.device_is_tx_complete = sh_dmae_is_complete; + shdev->common.device_tx_status = sh_dmae_tx_status; shdev->common.device_issue_pending = sh_dmae_memcpy_issue_pending; /* Compulsory for DMA_SLAVE fields */ shdev->common.device_prep_slave_sg = sh_dmae_prep_slave_sg; - shdev->common.device_terminate_all = sh_dmae_terminate_all; + shdev->common.device_control = sh_dmae_control; shdev->common.dev = &pdev->dev; /* Default transfer size of 32 bytes requires 32-byte alignment */ diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c new file mode 100644 index 000000000000..44b346d8d319 --- /dev/null +++ b/drivers/dma/timb_dma.c @@ -0,0 +1,859 @@ +/* + * timb_dma.c timberdale FPGA DMA driver + * Copyright (c) 2010 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Supports: + * Timberdale FPGA DMA engine + */ + +#include <linux/dmaengine.h> +#include <linux/dma-mapping.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include <linux/timb_dma.h> + +#define DRIVER_NAME "timb-dma" + +/* Global DMA registers */ +#define TIMBDMA_ACR 0x34 +#define TIMBDMA_32BIT_ADDR 0x01 + +#define TIMBDMA_ISR 0x080000 +#define TIMBDMA_IPR 0x080004 +#define TIMBDMA_IER 0x080008 + +/* Channel specific registers */ +/* RX instances base addresses are 0x00, 0x40, 0x80 ... + * TX instances base addresses are 0x18, 0x58, 0x98 ... + */ +#define TIMBDMA_INSTANCE_OFFSET 0x40 +#define TIMBDMA_INSTANCE_TX_OFFSET 0x18 + +/* RX registers, relative the instance base */ +#define TIMBDMA_OFFS_RX_DHAR 0x00 +#define TIMBDMA_OFFS_RX_DLAR 0x04 +#define TIMBDMA_OFFS_RX_LR 0x0C +#define TIMBDMA_OFFS_RX_BLR 0x10 +#define TIMBDMA_OFFS_RX_ER 0x14 +#define TIMBDMA_RX_EN 0x01 +/* bytes per Row, video specific register + * which is placed after the TX registers... + */ +#define TIMBDMA_OFFS_RX_BPRR 0x30 + +/* TX registers, relative the instance base */ +#define TIMBDMA_OFFS_TX_DHAR 0x00 +#define TIMBDMA_OFFS_TX_DLAR 0x04 +#define TIMBDMA_OFFS_TX_BLR 0x0C +#define TIMBDMA_OFFS_TX_LR 0x14 + + +#define TIMB_DMA_DESC_SIZE 8 + +struct timb_dma_desc { + struct list_head desc_node; + struct dma_async_tx_descriptor txd; + u8 *desc_list; + unsigned int desc_list_len; + bool interrupt; +}; + +struct timb_dma_chan { + struct dma_chan chan; + void __iomem *membase; + spinlock_t lock; /* Used to protect data structures, + especially the lists and descriptors, + from races between the tasklet and calls + from above */ + dma_cookie_t last_completed_cookie; + bool ongoing; + struct list_head active_list; + struct list_head queue; + struct list_head free_list; + unsigned int bytes_per_line; + enum dma_data_direction direction; + unsigned int descs; /* Descriptors to allocate */ + unsigned int desc_elems; /* number of elems per descriptor */ +}; + +struct timb_dma { + struct dma_device dma; + void __iomem *membase; + struct tasklet_struct tasklet; + struct timb_dma_chan channels[0]; +}; + +static struct device *chan2dev(struct dma_chan *chan) +{ + return &chan->dev->device; +} +static struct device *chan2dmadev(struct dma_chan *chan) +{ + return chan2dev(chan)->parent->parent; +} + +static struct timb_dma *tdchantotd(struct timb_dma_chan *td_chan) +{ + int id = td_chan->chan.chan_id; + return (struct timb_dma *)((u8 *)td_chan - + id * sizeof(struct timb_dma_chan) - sizeof(struct timb_dma)); +} + +/* Must be called with the spinlock held */ +static void __td_enable_chan_irq(struct timb_dma_chan *td_chan) +{ + int id = td_chan->chan.chan_id; + struct timb_dma *td = tdchantotd(td_chan); + u32 ier; + + /* enable interrupt for this channel */ + ier = ioread32(td->membase + TIMBDMA_IER); + ier |= 1 << id; + dev_dbg(chan2dev(&td_chan->chan), "Enabling irq: %d, IER: 0x%x\n", id, + ier); + iowrite32(ier, td->membase + TIMBDMA_IER); +} + +/* Should be called with the spinlock held */ +static bool __td_dma_done_ack(struct timb_dma_chan *td_chan) +{ + int id = td_chan->chan.chan_id; + struct timb_dma *td = (struct timb_dma *)((u8 *)td_chan - + id * sizeof(struct timb_dma_chan) - sizeof(struct timb_dma)); + u32 isr; + bool done = false; + + dev_dbg(chan2dev(&td_chan->chan), "Checking irq: %d, td: %p\n", id, td); + + isr = ioread32(td->membase + TIMBDMA_ISR) & (1 << id); + if (isr) { + iowrite32(isr, td->membase + TIMBDMA_ISR); + done = true; + } + + return done; +} + +static void __td_unmap_desc(struct timb_dma_chan *td_chan, const u8 *dma_desc, + bool single) +{ + dma_addr_t addr; + int len; + + addr = (dma_desc[7] << 24) | (dma_desc[6] << 16) | (dma_desc[5] << 8) | + dma_desc[4]; + + len = (dma_desc[3] << 8) | dma_desc[2]; + + if (single) + dma_unmap_single(chan2dev(&td_chan->chan), addr, len, + td_chan->direction); + else + dma_unmap_page(chan2dev(&td_chan->chan), addr, len, + td_chan->direction); +} + +static void __td_unmap_descs(struct timb_dma_desc *td_desc, bool single) +{ + struct timb_dma_chan *td_chan = container_of(td_desc->txd.chan, + struct timb_dma_chan, chan); + u8 *descs; + + for (descs = td_desc->desc_list; ; descs += TIMB_DMA_DESC_SIZE) { + __td_unmap_desc(td_chan, descs, single); + if (descs[0] & 0x02) + break; + } +} + +static int td_fill_desc(struct timb_dma_chan *td_chan, u8 *dma_desc, + struct scatterlist *sg, bool last) +{ + if (sg_dma_len(sg) > USHORT_MAX) { + dev_err(chan2dev(&td_chan->chan), "Too big sg element\n"); + return -EINVAL; + } + + /* length must be word aligned */ + if (sg_dma_len(sg) % sizeof(u32)) { + dev_err(chan2dev(&td_chan->chan), "Incorrect length: %d\n", + sg_dma_len(sg)); + return -EINVAL; + } + + dev_dbg(chan2dev(&td_chan->chan), "desc: %p, addr: %p\n", + dma_desc, (void *)sg_dma_address(sg)); + + dma_desc[7] = (sg_dma_address(sg) >> 24) & 0xff; + dma_desc[6] = (sg_dma_address(sg) >> 16) & 0xff; + dma_desc[5] = (sg_dma_address(sg) >> 8) & 0xff; + dma_desc[4] = (sg_dma_address(sg) >> 0) & 0xff; + + dma_desc[3] = (sg_dma_len(sg) >> 8) & 0xff; + dma_desc[2] = (sg_dma_len(sg) >> 0) & 0xff; + + dma_desc[1] = 0x00; + dma_desc[0] = 0x21 | (last ? 0x02 : 0); /* tran, valid */ + + return 0; +} + +/* Must be called with the spinlock held */ +static void __td_start_dma(struct timb_dma_chan *td_chan) +{ + struct timb_dma_desc *td_desc; + + if (td_chan->ongoing) { + dev_err(chan2dev(&td_chan->chan), + "Transfer already ongoing\n"); + return; + } + + td_desc = list_entry(td_chan->active_list.next, struct timb_dma_desc, + desc_node); + + dev_dbg(chan2dev(&td_chan->chan), + "td_chan: %p, chan: %d, membase: %p\n", + td_chan, td_chan->chan.chan_id, td_chan->membase); + + if (td_chan->direction == DMA_FROM_DEVICE) { + + /* descriptor address */ + iowrite32(0, td_chan->membase + TIMBDMA_OFFS_RX_DHAR); + iowrite32(td_desc->txd.phys, td_chan->membase + + TIMBDMA_OFFS_RX_DLAR); + /* Bytes per line */ + iowrite32(td_chan->bytes_per_line, td_chan->membase + + TIMBDMA_OFFS_RX_BPRR); + /* enable RX */ + iowrite32(TIMBDMA_RX_EN, td_chan->membase + TIMBDMA_OFFS_RX_ER); + } else { + /* address high */ + iowrite32(0, td_chan->membase + TIMBDMA_OFFS_TX_DHAR); + iowrite32(td_desc->txd.phys, td_chan->membase + + TIMBDMA_OFFS_TX_DLAR); + } + + td_chan->ongoing = true; + + if (td_desc->interrupt) + __td_enable_chan_irq(td_chan); +} + +static void __td_finish(struct timb_dma_chan *td_chan) +{ + dma_async_tx_callback callback; + void *param; + struct dma_async_tx_descriptor *txd; + struct timb_dma_desc *td_desc; + + /* can happen if the descriptor is canceled */ + if (list_empty(&td_chan->active_list)) + return; + + td_desc = list_entry(td_chan->active_list.next, struct timb_dma_desc, + desc_node); + txd = &td_desc->txd; + + dev_dbg(chan2dev(&td_chan->chan), "descriptor %u complete\n", + txd->cookie); + + /* make sure to stop the transfer */ + if (td_chan->direction == DMA_FROM_DEVICE) + iowrite32(0, td_chan->membase + TIMBDMA_OFFS_RX_ER); +/* Currently no support for stopping DMA transfers + else + iowrite32(0, td_chan->membase + TIMBDMA_OFFS_TX_DLAR); +*/ + td_chan->last_completed_cookie = txd->cookie; + td_chan->ongoing = false; + + callback = txd->callback; + param = txd->callback_param; + + list_move(&td_desc->desc_node, &td_chan->free_list); + + if (!(txd->flags & DMA_COMPL_SKIP_SRC_UNMAP)) + __td_unmap_descs(td_desc, + txd->flags & DMA_COMPL_SRC_UNMAP_SINGLE); + + /* + * The API requires that no submissions are done from a + * callback, so we don't need to drop the lock here + */ + if (callback) + callback(param); +} + +static u32 __td_ier_mask(struct timb_dma *td) +{ + int i; + u32 ret = 0; + + for (i = 0; i < td->dma.chancnt; i++) { + struct timb_dma_chan *td_chan = td->channels + i; + if (td_chan->ongoing) { + struct timb_dma_desc *td_desc = + list_entry(td_chan->active_list.next, + struct timb_dma_desc, desc_node); + if (td_desc->interrupt) + ret |= 1 << i; + } + } + + return ret; +} + +static void __td_start_next(struct timb_dma_chan *td_chan) +{ + struct timb_dma_desc *td_desc; + + BUG_ON(list_empty(&td_chan->queue)); + BUG_ON(td_chan->ongoing); + + td_desc = list_entry(td_chan->queue.next, struct timb_dma_desc, + desc_node); + + dev_dbg(chan2dev(&td_chan->chan), "%s: started %u\n", + __func__, td_desc->txd.cookie); + + list_move(&td_desc->desc_node, &td_chan->active_list); + __td_start_dma(td_chan); +} + +static dma_cookie_t td_tx_submit(struct dma_async_tx_descriptor *txd) +{ + struct timb_dma_desc *td_desc = container_of(txd, struct timb_dma_desc, + txd); + struct timb_dma_chan *td_chan = container_of(txd->chan, + struct timb_dma_chan, chan); + dma_cookie_t cookie; + + spin_lock_bh(&td_chan->lock); + + cookie = txd->chan->cookie; + if (++cookie < 0) + cookie = 1; + txd->chan->cookie = cookie; + txd->cookie = cookie; + + if (list_empty(&td_chan->active_list)) { + dev_dbg(chan2dev(txd->chan), "%s: started %u\n", __func__, + txd->cookie); + list_add_tail(&td_desc->desc_node, &td_chan->active_list); + __td_start_dma(td_chan); + } else { + dev_dbg(chan2dev(txd->chan), "tx_submit: queued %u\n", + txd->cookie); + + list_add_tail(&td_desc->desc_node, &td_chan->queue); + } + + spin_unlock_bh(&td_chan->lock); + + return cookie; +} + +static struct timb_dma_desc *td_alloc_init_desc(struct timb_dma_chan *td_chan) +{ + struct dma_chan *chan = &td_chan->chan; + struct timb_dma_desc *td_desc; + int err; + + td_desc = kzalloc(sizeof(struct timb_dma_desc), GFP_KERNEL); + if (!td_desc) { + dev_err(chan2dev(chan), "Failed to alloc descriptor\n"); + goto err; + } + + td_desc->desc_list_len = td_chan->desc_elems * TIMB_DMA_DESC_SIZE; + + td_desc->desc_list = kzalloc(td_desc->desc_list_len, GFP_KERNEL); + if (!td_desc->desc_list) { + dev_err(chan2dev(chan), "Failed to alloc descriptor\n"); + goto err; + } + + dma_async_tx_descriptor_init(&td_desc->txd, chan); + td_desc->txd.tx_submit = td_tx_submit; + td_desc->txd.flags = DMA_CTRL_ACK; + + td_desc->txd.phys = dma_map_single(chan2dmadev(chan), + td_desc->desc_list, td_desc->desc_list_len, DMA_TO_DEVICE); + + err = dma_mapping_error(chan2dmadev(chan), td_desc->txd.phys); + if (err) { + dev_err(chan2dev(chan), "DMA mapping error: %d\n", err); + goto err; + } + + return td_desc; +err: + kfree(td_desc->desc_list); + kfree(td_desc); + + return NULL; + +} + +static void td_free_desc(struct timb_dma_desc *td_desc) +{ + dev_dbg(chan2dev(td_desc->txd.chan), "Freeing desc: %p\n", td_desc); + dma_unmap_single(chan2dmadev(td_desc->txd.chan), td_desc->txd.phys, + td_desc->desc_list_len, DMA_TO_DEVICE); + + kfree(td_desc->desc_list); + kfree(td_desc); +} + +static void td_desc_put(struct timb_dma_chan *td_chan, + struct timb_dma_desc *td_desc) +{ + dev_dbg(chan2dev(&td_chan->chan), "Putting desc: %p\n", td_desc); + + spin_lock_bh(&td_chan->lock); + list_add(&td_desc->desc_node, &td_chan->free_list); + spin_unlock_bh(&td_chan->lock); +} + +static struct timb_dma_desc *td_desc_get(struct timb_dma_chan *td_chan) +{ + struct timb_dma_desc *td_desc, *_td_desc; + struct timb_dma_desc *ret = NULL; + + spin_lock_bh(&td_chan->lock); + list_for_each_entry_safe(td_desc, _td_desc, &td_chan->free_list, + desc_node) { + if (async_tx_test_ack(&td_desc->txd)) { + list_del(&td_desc->desc_node); + ret = td_desc; + break; + } + dev_dbg(chan2dev(&td_chan->chan), "desc %p not ACKed\n", + td_desc); + } + spin_unlock_bh(&td_chan->lock); + + return ret; +} + +static int td_alloc_chan_resources(struct dma_chan *chan) +{ + struct timb_dma_chan *td_chan = + container_of(chan, struct timb_dma_chan, chan); + int i; + + dev_dbg(chan2dev(chan), "%s: entry\n", __func__); + + BUG_ON(!list_empty(&td_chan->free_list)); + for (i = 0; i < td_chan->descs; i++) { + struct timb_dma_desc *td_desc = td_alloc_init_desc(td_chan); + if (!td_desc) { + if (i) + break; + else { + dev_err(chan2dev(chan), + "Couldnt allocate any descriptors\n"); + return -ENOMEM; + } + } + + td_desc_put(td_chan, td_desc); + } + + spin_lock_bh(&td_chan->lock); + td_chan->last_completed_cookie = 1; + chan->cookie = 1; + spin_unlock_bh(&td_chan->lock); + + return 0; +} + +static void td_free_chan_resources(struct dma_chan *chan) +{ + struct timb_dma_chan *td_chan = + container_of(chan, struct timb_dma_chan, chan); + struct timb_dma_desc *td_desc, *_td_desc; + LIST_HEAD(list); + + dev_dbg(chan2dev(chan), "%s: Entry\n", __func__); + + /* check that all descriptors are free */ + BUG_ON(!list_empty(&td_chan->active_list)); + BUG_ON(!list_empty(&td_chan->queue)); + + spin_lock_bh(&td_chan->lock); + list_splice_init(&td_chan->free_list, &list); + spin_unlock_bh(&td_chan->lock); + + list_for_each_entry_safe(td_desc, _td_desc, &list, desc_node) { + dev_dbg(chan2dev(chan), "%s: Freeing desc: %p\n", __func__, + td_desc); + td_free_desc(td_desc); + } +} + +static enum dma_status td_tx_status(struct dma_chan *chan, dma_cookie_t cookie, + struct dma_tx_state *txstate) +{ + struct timb_dma_chan *td_chan = + container_of(chan, struct timb_dma_chan, chan); + dma_cookie_t last_used; + dma_cookie_t last_complete; + int ret; + + dev_dbg(chan2dev(chan), "%s: Entry\n", __func__); + + last_complete = td_chan->last_completed_cookie; + last_used = chan->cookie; + + ret = dma_async_is_complete(cookie, last_complete, last_used); + + dma_set_tx_state(txstate, last_complete, last_used, 0); + + dev_dbg(chan2dev(chan), + "%s: exit, ret: %d, last_complete: %d, last_used: %d\n", + __func__, ret, last_complete, last_used); + + return ret; +} + +static void td_issue_pending(struct dma_chan *chan) +{ + struct timb_dma_chan *td_chan = + container_of(chan, struct timb_dma_chan, chan); + + dev_dbg(chan2dev(chan), "%s: Entry\n", __func__); + spin_lock_bh(&td_chan->lock); + + if (!list_empty(&td_chan->active_list)) + /* transfer ongoing */ + if (__td_dma_done_ack(td_chan)) + __td_finish(td_chan); + + if (list_empty(&td_chan->active_list) && !list_empty(&td_chan->queue)) + __td_start_next(td_chan); + + spin_unlock_bh(&td_chan->lock); +} + +static struct dma_async_tx_descriptor *td_prep_slave_sg(struct dma_chan *chan, + struct scatterlist *sgl, unsigned int sg_len, + enum dma_data_direction direction, unsigned long flags) +{ + struct timb_dma_chan *td_chan = + container_of(chan, struct timb_dma_chan, chan); + struct timb_dma_desc *td_desc; + struct scatterlist *sg; + unsigned int i; + unsigned int desc_usage = 0; + + if (!sgl || !sg_len) { + dev_err(chan2dev(chan), "%s: No SG list\n", __func__); + return NULL; + } + + /* even channels are for RX, odd for TX */ + if (td_chan->direction != direction) { + dev_err(chan2dev(chan), + "Requesting channel in wrong direction\n"); + return NULL; + } + + td_desc = td_desc_get(td_chan); + if (!td_desc) { + dev_err(chan2dev(chan), "Not enough descriptors available\n"); + return NULL; + } + + td_desc->interrupt = (flags & DMA_PREP_INTERRUPT) != 0; + + for_each_sg(sgl, sg, sg_len, i) { + int err; + if (desc_usage > td_desc->desc_list_len) { + dev_err(chan2dev(chan), "No descriptor space\n"); + return NULL; + } + + err = td_fill_desc(td_chan, td_desc->desc_list + desc_usage, sg, + i == (sg_len - 1)); + if (err) { + dev_err(chan2dev(chan), "Failed to update desc: %d\n", + err); + td_desc_put(td_chan, td_desc); + return NULL; + } + desc_usage += TIMB_DMA_DESC_SIZE; + } + + dma_sync_single_for_device(chan2dmadev(chan), td_desc->txd.phys, + td_desc->desc_list_len, DMA_TO_DEVICE); + + return &td_desc->txd; +} + +static int td_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd) +{ + struct timb_dma_chan *td_chan = + container_of(chan, struct timb_dma_chan, chan); + struct timb_dma_desc *td_desc, *_td_desc; + + dev_dbg(chan2dev(chan), "%s: Entry\n", __func__); + + if (cmd != DMA_TERMINATE_ALL) + return -ENXIO; + + /* first the easy part, put the queue into the free list */ + spin_lock_bh(&td_chan->lock); + list_for_each_entry_safe(td_desc, _td_desc, &td_chan->queue, + desc_node) + list_move(&td_desc->desc_node, &td_chan->free_list); + + /* now tear down the runnning */ + __td_finish(td_chan); + spin_unlock_bh(&td_chan->lock); + + return 0; +} + +static void td_tasklet(unsigned long data) +{ + struct timb_dma *td = (struct timb_dma *)data; + u32 isr; + u32 ipr; + u32 ier; + int i; + + isr = ioread32(td->membase + TIMBDMA_ISR); + ipr = isr & __td_ier_mask(td); + + /* ack the interrupts */ + iowrite32(ipr, td->membase + TIMBDMA_ISR); + + for (i = 0; i < td->dma.chancnt; i++) + if (ipr & (1 << i)) { + struct timb_dma_chan *td_chan = td->channels + i; + spin_lock(&td_chan->lock); + __td_finish(td_chan); + if (!list_empty(&td_chan->queue)) + __td_start_next(td_chan); + spin_unlock(&td_chan->lock); + } + + ier = __td_ier_mask(td); + iowrite32(ier, td->membase + TIMBDMA_IER); +} + + +static irqreturn_t td_irq(int irq, void *devid) +{ + struct timb_dma *td = devid; + u32 ipr = ioread32(td->membase + TIMBDMA_IPR); + + if (ipr) { + /* disable interrupts, will be re-enabled in tasklet */ + iowrite32(0, td->membase + TIMBDMA_IER); + + tasklet_schedule(&td->tasklet); + + return IRQ_HANDLED; + } else + return IRQ_NONE; +} + + +static int __devinit td_probe(struct platform_device *pdev) +{ + struct timb_dma_platform_data *pdata = pdev->dev.platform_data; + struct timb_dma *td; + struct resource *iomem; + int irq; + int err; + int i; + + if (!pdata) { + dev_err(&pdev->dev, "No platform data\n"); + return -EINVAL; + } + + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!iomem) + return -EINVAL; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + if (!request_mem_region(iomem->start, resource_size(iomem), + DRIVER_NAME)) + return -EBUSY; + + td = kzalloc(sizeof(struct timb_dma) + + sizeof(struct timb_dma_chan) * pdata->nr_channels, GFP_KERNEL); + if (!td) { + err = -ENOMEM; + goto err_release_region; + } + + dev_dbg(&pdev->dev, "Allocated TD: %p\n", td); + + td->membase = ioremap(iomem->start, resource_size(iomem)); + if (!td->membase) { + dev_err(&pdev->dev, "Failed to remap I/O memory\n"); + err = -ENOMEM; + goto err_free_mem; + } + + /* 32bit addressing */ + iowrite32(TIMBDMA_32BIT_ADDR, td->membase + TIMBDMA_ACR); + + /* disable and clear any interrupts */ + iowrite32(0x0, td->membase + TIMBDMA_IER); + iowrite32(0xFFFFFFFF, td->membase + TIMBDMA_ISR); + + tasklet_init(&td->tasklet, td_tasklet, (unsigned long)td); + + err = request_irq(irq, td_irq, IRQF_SHARED, DRIVER_NAME, td); + if (err) { + dev_err(&pdev->dev, "Failed to request IRQ\n"); + goto err_tasklet_kill; + } + + td->dma.device_alloc_chan_resources = td_alloc_chan_resources; + td->dma.device_free_chan_resources = td_free_chan_resources; + td->dma.device_tx_status = td_tx_status; + td->dma.device_issue_pending = td_issue_pending; + + dma_cap_set(DMA_SLAVE, td->dma.cap_mask); + dma_cap_set(DMA_PRIVATE, td->dma.cap_mask); + td->dma.device_prep_slave_sg = td_prep_slave_sg; + td->dma.device_control = td_control; + + td->dma.dev = &pdev->dev; + + INIT_LIST_HEAD(&td->dma.channels); + + for (i = 0; i < pdata->nr_channels; i++, td->dma.chancnt++) { + struct timb_dma_chan *td_chan = &td->channels[i]; + struct timb_dma_platform_data_channel *pchan = + pdata->channels + i; + + /* even channels are RX, odd are TX */ + if (((i % 2) && pchan->rx) || (!(i % 2) && !pchan->rx)) { + dev_err(&pdev->dev, "Wrong channel configuration\n"); + err = -EINVAL; + goto err_tasklet_kill; + } + + td_chan->chan.device = &td->dma; + td_chan->chan.cookie = 1; + td_chan->chan.chan_id = i; + spin_lock_init(&td_chan->lock); + INIT_LIST_HEAD(&td_chan->active_list); + INIT_LIST_HEAD(&td_chan->queue); + INIT_LIST_HEAD(&td_chan->free_list); + + td_chan->descs = pchan->descriptors; + td_chan->desc_elems = pchan->descriptor_elements; + td_chan->bytes_per_line = pchan->bytes_per_line; + td_chan->direction = pchan->rx ? DMA_FROM_DEVICE : + DMA_TO_DEVICE; + + td_chan->membase = td->membase + + (i / 2) * TIMBDMA_INSTANCE_OFFSET + + (pchan->rx ? 0 : TIMBDMA_INSTANCE_TX_OFFSET); + + dev_dbg(&pdev->dev, "Chan: %d, membase: %p\n", + i, td_chan->membase); + + list_add_tail(&td_chan->chan.device_node, &td->dma.channels); + } + + err = dma_async_device_register(&td->dma); + if (err) { + dev_err(&pdev->dev, "Failed to register async device\n"); + goto err_free_irq; + } + + platform_set_drvdata(pdev, td); + + dev_dbg(&pdev->dev, "Probe result: %d\n", err); + return err; + +err_free_irq: + free_irq(irq, td); +err_tasklet_kill: + tasklet_kill(&td->tasklet); + iounmap(td->membase); +err_free_mem: + kfree(td); +err_release_region: + release_mem_region(iomem->start, resource_size(iomem)); + + return err; + +} + +static int __devexit td_remove(struct platform_device *pdev) +{ + struct timb_dma *td = platform_get_drvdata(pdev); + struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + int irq = platform_get_irq(pdev, 0); + + dma_async_device_unregister(&td->dma); + free_irq(irq, td); + tasklet_kill(&td->tasklet); + iounmap(td->membase); + kfree(td); + release_mem_region(iomem->start, resource_size(iomem)); + + platform_set_drvdata(pdev, NULL); + + dev_dbg(&pdev->dev, "Removed...\n"); + return 0; +} + +static struct platform_driver td_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = td_probe, + .remove = __exit_p(td_remove), +}; + +static int __init td_init(void) +{ + return platform_driver_register(&td_driver); +} +module_init(td_init); + +static void __exit td_exit(void) +{ + platform_driver_unregister(&td_driver); +} +module_exit(td_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Timberdale DMA controller driver"); +MODULE_AUTHOR("Pelagicore AB <info@pelagicore.com>"); +MODULE_ALIAS("platform:"DRIVER_NAME); diff --git a/drivers/dma/txx9dmac.c b/drivers/dma/txx9dmac.c index 3ebc61067e54..e523737639aa 100644 --- a/drivers/dma/txx9dmac.c +++ b/drivers/dma/txx9dmac.c @@ -938,12 +938,16 @@ txx9dmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, return &first->txd; } -static void txx9dmac_terminate_all(struct dma_chan *chan) +static int txx9dmac_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd) { struct txx9dmac_chan *dc = to_txx9dmac_chan(chan); struct txx9dmac_desc *desc, *_desc; LIST_HEAD(list); + /* Only supports DMA_TERMINATE_ALL */ + if (cmd != DMA_TERMINATE_ALL) + return -EINVAL; + dev_vdbg(chan2dev(chan), "terminate_all\n"); spin_lock_bh(&dc->lock); @@ -958,12 +962,13 @@ static void txx9dmac_terminate_all(struct dma_chan *chan) /* Flush all pending and queued descriptors */ list_for_each_entry_safe(desc, _desc, &list, desc_node) txx9dmac_descriptor_complete(dc, desc); + + return 0; } static enum dma_status -txx9dmac_is_tx_complete(struct dma_chan *chan, - dma_cookie_t cookie, - dma_cookie_t *done, dma_cookie_t *used) +txx9dmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie, + struct dma_tx_state *txstate) { struct txx9dmac_chan *dc = to_txx9dmac_chan(chan); dma_cookie_t last_used; @@ -985,10 +990,7 @@ txx9dmac_is_tx_complete(struct dma_chan *chan, ret = dma_async_is_complete(cookie, last_complete, last_used); } - if (done) - *done = last_complete; - if (used) - *used = last_used; + dma_set_tx_state(txstate, last_complete, last_used, 0); return ret; } @@ -1153,8 +1155,8 @@ static int __init txx9dmac_chan_probe(struct platform_device *pdev) dc->dma.dev = &pdev->dev; dc->dma.device_alloc_chan_resources = txx9dmac_alloc_chan_resources; dc->dma.device_free_chan_resources = txx9dmac_free_chan_resources; - dc->dma.device_terminate_all = txx9dmac_terminate_all; - dc->dma.device_is_tx_complete = txx9dmac_is_tx_complete; + dc->dma.device_control = txx9dmac_control; + dc->dma.device_tx_status = txx9dmac_tx_status; dc->dma.device_issue_pending = txx9dmac_issue_pending; if (pdata && pdata->memcpy_chan == ch) { dc->dma.device_prep_dma_memcpy = txx9dmac_prep_dma_memcpy; diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 88be37d9e9a5..52f50685ac0e 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -579,7 +579,7 @@ static void atmci_stop_dma(struct atmel_mci *host) struct dma_chan *chan = host->data_chan; if (chan) { - chan->device->device_terminate_all(chan); + chan->device->device_control(chan, DMA_TERMINATE_ALL); atmci_dma_cleanup(host); } else { /* Data transfer was stopped by the interrupt handler */ diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index 8d993c4cceac..d12ddc313cb7 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -1087,7 +1087,7 @@ static void work_fn_rx(struct work_struct *work) unsigned long flags; int count; - chan->device->device_terminate_all(chan); + chan->device->device_control(chan, DMA_TERMINATE_ALL); dev_dbg(port->dev, "Read %u bytes with cookie %d\n", sh_desc->partial, sh_desc->cookie); diff --git a/drivers/video/mx3fb.c b/drivers/video/mx3fb.c index 772ba3f45e6f..3aa50bc276eb 100644 --- a/drivers/video/mx3fb.c +++ b/drivers/video/mx3fb.c @@ -387,7 +387,8 @@ static void sdc_disable_channel(struct mx3fb_info *mx3_fbi) spin_unlock_irqrestore(&mx3fb->lock, flags); - mx3_fbi->txd->chan->device->device_terminate_all(mx3_fbi->txd->chan); + mx3_fbi->txd->chan->device->device_control(mx3_fbi->txd->chan, + DMA_TERMINATE_ALL); mx3_fbi->txd = NULL; mx3_fbi->cookie = -EINVAL; } diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 20ea12c86fd0..50b7b3e0d572 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -40,11 +40,13 @@ typedef s32 dma_cookie_t; * enum dma_status - DMA transaction status * @DMA_SUCCESS: transaction completed successfully * @DMA_IN_PROGRESS: transaction not yet processed + * @DMA_PAUSED: transaction is paused * @DMA_ERROR: transaction failed */ enum dma_status { DMA_SUCCESS, DMA_IN_PROGRESS, + DMA_PAUSED, DMA_ERROR, }; @@ -107,6 +109,19 @@ enum dma_ctrl_flags { }; /** + * enum dma_ctrl_cmd - DMA operations that can optionally be exercised + * on a running channel. + * @DMA_TERMINATE_ALL: terminate all ongoing transfers + * @DMA_PAUSE: pause ongoing transfers + * @DMA_RESUME: resume paused transfer + */ +enum dma_ctrl_cmd { + DMA_TERMINATE_ALL, + DMA_PAUSE, + DMA_RESUME, +}; + +/** * enum sum_check_bits - bit position of pq_check_flags */ enum sum_check_bits { @@ -236,6 +251,21 @@ struct dma_async_tx_descriptor { }; /** + * struct dma_tx_state - filled in to report the status of + * a transfer. + * @last: last completed DMA cookie + * @used: last issued DMA cookie (i.e. the one in progress) + * @residue: the remaining number of bytes left to transmit + * on the selected transfer for states DMA_IN_PROGRESS and + * DMA_PAUSED if this is implemented in the driver, else 0 + */ +struct dma_tx_state { + dma_cookie_t last; + dma_cookie_t used; + u32 residue; +}; + +/** * struct dma_device - info on the entity supplying DMA services * @chancnt: how many DMA channels are supported * @privatecnt: how many DMA channels are requested by dma_request_channel @@ -261,8 +291,12 @@ struct dma_async_tx_descriptor { * @device_prep_dma_memset: prepares a memset operation * @device_prep_dma_interrupt: prepares an end of chain interrupt operation * @device_prep_slave_sg: prepares a slave dma operation - * @device_terminate_all: terminate all pending operations - * @device_is_tx_complete: poll for transaction completion + * @device_control: manipulate all pending operations on a channel, returns + * zero or error code + * @device_tx_status: poll for transaction completion, the optional + * txstate parameter can be supplied with a pointer to get a + * struct with auxilary transfer status information, otherwise the call + * will just return a simple status code * @device_issue_pending: push pending transactions to hardware */ struct dma_device { @@ -313,11 +347,11 @@ struct dma_device { struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, enum dma_data_direction direction, unsigned long flags); - void (*device_terminate_all)(struct dma_chan *chan); + int (*device_control)(struct dma_chan *chan, enum dma_ctrl_cmd cmd); - enum dma_status (*device_is_tx_complete)(struct dma_chan *chan, - dma_cookie_t cookie, dma_cookie_t *last, - dma_cookie_t *used); + enum dma_status (*device_tx_status)(struct dma_chan *chan, + dma_cookie_t cookie, + struct dma_tx_state *txstate); void (*device_issue_pending)(struct dma_chan *chan); }; @@ -558,7 +592,15 @@ static inline void dma_async_issue_pending(struct dma_chan *chan) static inline enum dma_status dma_async_is_tx_complete(struct dma_chan *chan, dma_cookie_t cookie, dma_cookie_t *last, dma_cookie_t *used) { - return chan->device->device_is_tx_complete(chan, cookie, last, used); + struct dma_tx_state state; + enum dma_status status; + + status = chan->device->device_tx_status(chan, cookie, &state); + if (last) + *last = state.last; + if (used) + *used = state.used; + return status; } #define dma_async_memcpy_complete(chan, cookie, last, used)\ @@ -586,6 +628,16 @@ static inline enum dma_status dma_async_is_complete(dma_cookie_t cookie, return DMA_IN_PROGRESS; } +static inline void +dma_set_tx_state(struct dma_tx_state *st, dma_cookie_t last, dma_cookie_t used, u32 residue) +{ + if (st) { + st->last = last; + st->used = used; + st->residue = residue; + } +} + enum dma_status dma_sync_wait(struct dma_chan *chan, dma_cookie_t cookie); #ifdef CONFIG_DMA_ENGINE enum dma_status dma_wait_for_async_tx(struct dma_async_tx_descriptor *tx); diff --git a/include/linux/timb_dma.h b/include/linux/timb_dma.h new file mode 100644 index 000000000000..bb043e970b96 --- /dev/null +++ b/include/linux/timb_dma.h @@ -0,0 +1,55 @@ +/* + * timb_dma.h timberdale FPGA DMA driver defines + * Copyright (c) 2010 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Supports: + * Timberdale FPGA DMA engine + */ + +#ifndef _LINUX_TIMB_DMA_H +#define _LINUX_TIMB_DMA_H + +/** + * struct timb_dma_platform_data_channel - Description of each individual + * DMA channel for the timberdale DMA driver + * @rx: true if this channel handles data in the direction to + * the CPU. + * @bytes_per_line: Number of bytes per line, this is specific for channels + * handling video data. For other channels this shall be left to 0. + * @descriptors: Number of descriptors to allocate for this channel. + * @descriptor_elements: Number of elements in each descriptor. + * + */ +struct timb_dma_platform_data_channel { + bool rx; + unsigned int bytes_per_line; + unsigned int descriptors; + unsigned int descriptor_elements; +}; + +/** + * struct timb_dma_platform_data - Platform data of the timberdale DMA driver + * @nr_channels: Number of defined channels in the channels array. + * @channels: Definition of the each channel. + * + */ +struct timb_dma_platform_data { + unsigned nr_channels; + struct timb_dma_platform_data_channel channels[32]; +}; + +#endif diff --git a/sound/soc/txx9/txx9aclc.c b/sound/soc/txx9/txx9aclc.c index 49cc7ea9a518..7082cb8045f2 100644 --- a/sound/soc/txx9/txx9aclc.c +++ b/sound/soc/txx9/txx9aclc.c @@ -160,7 +160,7 @@ static void txx9aclc_dma_tasklet(unsigned long data) void __iomem *base = drvdata->base; spin_unlock_irqrestore(&dmadata->dma_lock, flags); - chan->device->device_terminate_all(chan); + chan->device->device_control(chan, DMA_TERMINATE_ALL); /* first time */ for (i = 0; i < NR_DMA_CHAIN; i++) { desc = txx9aclc_dma_submit(dmadata, @@ -268,7 +268,7 @@ static int txx9aclc_pcm_close(struct snd_pcm_substream *substream) struct dma_chan *chan = dmadata->dma_chan; dmadata->frag_count = -1; - chan->device->device_terminate_all(chan); + chan->device->device_control(chan, DMA_TERMINATE_ALL); return 0; } @@ -397,7 +397,7 @@ static int txx9aclc_pcm_remove(struct platform_device *pdev) struct dma_chan *chan = dmadata->dma_chan; if (chan) { dmadata->frag_count = -1; - chan->device->device_terminate_all(chan); + chan->device->device_control(chan, DMA_TERMINATE_ALL); dma_release_channel(chan); } dev->dmadata[i].dma_chan = NULL; |