diff options
Diffstat (limited to 'drivers/dma/ipu/ipu_idmac.c')
| -rw-r--r-- | drivers/dma/ipu/ipu_idmac.c | 274 |
1 files changed, 95 insertions, 179 deletions
diff --git a/drivers/dma/ipu/ipu_idmac.c b/drivers/dma/ipu/ipu_idmac.c index 9a5bc1a7389..128ca143486 100644 --- a/drivers/dma/ipu/ipu_idmac.c +++ b/drivers/dma/ipu/ipu_idmac.c @@ -9,6 +9,7 @@ * published by the Free Software Foundation. */ +#include <linux/dma-mapping.h> #include <linux/init.h> #include <linux/platform_device.h> #include <linux/err.h> @@ -20,9 +21,10 @@ #include <linux/string.h> #include <linux/interrupt.h> #include <linux/io.h> +#include <linux/module.h> +#include <linux/dma/ipu-dma.h> -#include <mach/ipu.h> - +#include "../dmaengine.h" #include "ipu_intern.h" #define FS_VF_IN_VALID 0x00000002 @@ -310,7 +312,7 @@ static void ipu_ch_param_set_size(union chan_param_mem *params, case IPU_PIX_FMT_RGB565: params->ip.bpp = 2; params->ip.pfs = 4; - params->ip.npb = 7; + params->ip.npb = 15; params->ip.sat = 2; /* SAT = 32-bit access */ params->ip.ofs0 = 0; /* Red bit offset */ params->ip.ofs1 = 5; /* Green bit offset */ @@ -348,6 +350,7 @@ static void ipu_ch_param_set_size(union chan_param_mem *params, break; case IPU_PIX_FMT_BGRA32: case IPU_PIX_FMT_BGR32: + case IPU_PIX_FMT_ABGR32: params->ip.bpp = 0; params->ip.pfs = 4; params->ip.npb = 7; @@ -376,20 +379,6 @@ static void ipu_ch_param_set_size(union chan_param_mem *params, params->ip.wid2 = 7; /* Blue bit width - 1 */ params->ip.wid3 = 7; /* Alpha bit width - 1 */ break; - case IPU_PIX_FMT_ABGR32: - params->ip.bpp = 0; - params->ip.pfs = 4; - params->ip.npb = 7; - params->ip.sat = 2; /* SAT = 32-bit access */ - params->ip.ofs0 = 8; /* Red bit offset */ - params->ip.ofs1 = 16; /* Green bit offset */ - params->ip.ofs2 = 24; /* Blue bit offset */ - params->ip.ofs3 = 0; /* Alpha bit offset */ - params->ip.wid0 = 7; /* Red bit width - 1 */ - params->ip.wid1 = 7; /* Green bit width - 1 */ - params->ip.wid2 = 7; /* Blue bit width - 1 */ - params->ip.wid3 = 7; /* Alpha bit width - 1 */ - break; case IPU_PIX_FMT_UYVY: params->ip.bpp = 2; params->ip.pfs = 6; @@ -433,12 +422,6 @@ static void ipu_ch_param_set_size(union chan_param_mem *params, params->pp.nsb = 1; } -static void ipu_ch_param_set_burst_size(union chan_param_mem *params, - uint16_t burst_pixels) -{ - params->pp.npb = burst_pixels - 1; -} - static void ipu_ch_param_set_buffer(union chan_param_mem *params, dma_addr_t buf0, dma_addr_t buf1) { @@ -701,23 +684,6 @@ static int ipu_init_channel_buffer(struct idmac_channel *ichan, ipu_ch_param_set_size(¶ms, pixel_fmt, width, height, stride_bytes); ipu_ch_param_set_buffer(¶ms, phyaddr_0, phyaddr_1); ipu_ch_param_set_rotation(¶ms, rot_mode); - /* Some channels (rotation) have restriction on burst length */ - switch (channel) { - case IDMAC_IC_7: /* Hangs with burst 8, 16, other values - invalid - Table 44-30 */ -/* - ipu_ch_param_set_burst_size(¶ms, 8); - */ - break; - case IDMAC_SDC_0: - case IDMAC_SDC_1: - /* In original code only IPU_PIX_FMT_RGB565 was setting burst */ - ipu_ch_param_set_burst_size(¶ms, 16); - break; - case IDMAC_IC_0: - default: - break; - } spin_lock_irqsave(&ipu->lock, flags); @@ -761,12 +727,10 @@ static void ipu_select_buffer(enum ipu_channel channel, int buffer_n) * @buffer_n: buffer number to update. * 0 or 1 are the only valid values. * @phyaddr: buffer physical address. - * @return: Returns 0 on success or negative error code on failure. This - * function will fail if the buffer is set to ready. */ /* Called under spin_lock(_irqsave)(&ichan->lock) */ -static int ipu_update_channel_buffer(struct idmac_channel *ichan, - int buffer_n, dma_addr_t phyaddr) +static void ipu_update_channel_buffer(struct idmac_channel *ichan, + int buffer_n, dma_addr_t phyaddr) { enum ipu_channel channel = ichan->dma_chan.chan_id; uint32_t reg; @@ -806,8 +770,6 @@ static int ipu_update_channel_buffer(struct idmac_channel *ichan, } spin_unlock_irqrestore(&ipu_data.lock, flags); - - return 0; } /* Called under spin_lock_irqsave(&ichan->lock) */ @@ -816,7 +778,6 @@ static int ipu_submit_buffer(struct idmac_channel *ichan, { unsigned int chan_id = ichan->dma_chan.chan_id; struct device *dev = &ichan->dma_chan.dev->device; - int ret; if (async_tx_test_ack(&desc->txd)) return -EINTR; @@ -827,14 +788,7 @@ static int ipu_submit_buffer(struct idmac_channel *ichan, * could make it conditional on status >= IPU_CHANNEL_ENABLED, but * doing it again shouldn't hurt either. */ - ret = ipu_update_channel_buffer(ichan, buf_idx, - sg_dma_address(sg)); - - if (ret < 0) { - dev_err(dev, "Updating sg %p on channel 0x%x buffer %d failed!\n", - sg, chan_id, buf_idx); - return ret; - } + ipu_update_channel_buffer(ichan, buf_idx, sg_dma_address(sg)); ipu_select_buffer(chan_id, buf_idx); dev_dbg(dev, "Updated sg %p on channel 0x%x buffer %d\n", @@ -912,14 +866,7 @@ static dma_cookie_t idmac_tx_submit(struct dma_async_tx_descriptor *tx) dev_dbg(dev, "Submitting sg %p\n", &desc->sg[0]); - cookie = ichan->dma_chan.cookie; - - if (++cookie < 0) - cookie = 1; - - /* from dmaengine.h: "last cookie value returned to client" */ - ichan->dma_chan.cookie = cookie; - tx->cookie = cookie; + cookie = dma_cookie_assign(tx); /* ipu->lock can be taken under ichan->lock, but not v.v. */ spin_lock_irqsave(&ichan->lock, flags); @@ -1170,29 +1117,6 @@ static int ipu_disable_channel(struct idmac *idmac, struct idmac_channel *ichan, reg = idmac_read_icreg(ipu, IDMAC_CHA_EN); idmac_write_icreg(ipu, reg & ~chan_mask, IDMAC_CHA_EN); - /* - * Problem (observed with channel DMAIC_7): after enabling the channel - * and initialising buffers, there comes an interrupt with current still - * pointing at buffer 0, whereas it should use buffer 0 first and only - * generate an interrupt when it is done, then current should already - * point to buffer 1. This spurious interrupt also comes on channel - * DMASDC_0. With DMAIC_7 normally, is we just leave the ISR after the - * first interrupt, there comes the second with current correctly - * pointing to buffer 1 this time. But sometimes this second interrupt - * doesn't come and the channel hangs. Clearing BUFx_RDY when disabling - * the channel seems to prevent the channel from hanging, but it doesn't - * prevent the spurious interrupt. This might also be unsafe. Think - * about the IDMAC controller trying to switch to a buffer, when we - * clear the ready bit, and re-enable it a moment later. - */ - reg = idmac_read_ipureg(ipu, IPU_CHA_BUF0_RDY); - idmac_write_ipureg(ipu, 0, IPU_CHA_BUF0_RDY); - idmac_write_ipureg(ipu, reg & ~(1UL << channel), IPU_CHA_BUF0_RDY); - - reg = idmac_read_ipureg(ipu, IPU_CHA_BUF1_RDY); - idmac_write_ipureg(ipu, 0, IPU_CHA_BUF1_RDY); - idmac_write_ipureg(ipu, reg & ~(1UL << channel), IPU_CHA_BUF1_RDY); - spin_unlock_irqrestore(&ipu->lock, flags); return 0; @@ -1271,33 +1195,6 @@ static irqreturn_t idmac_interrupt(int irq, void *dev_id) /* Other interrupts do not interfere with this channel */ spin_lock(&ichan->lock); - if (unlikely(chan_id != IDMAC_SDC_0 && chan_id != IDMAC_SDC_1 && - ((curbuf >> chan_id) & 1) == ichan->active_buffer && - !list_is_last(ichan->queue.next, &ichan->queue))) { - int i = 100; - - /* This doesn't help. See comment in ipu_disable_channel() */ - while (--i) { - curbuf = idmac_read_ipureg(&ipu_data, IPU_CHA_CUR_BUF); - if (((curbuf >> chan_id) & 1) != ichan->active_buffer) - break; - cpu_relax(); - } - - if (!i) { - spin_unlock(&ichan->lock); - dev_dbg(dev, - "IRQ on active buffer on channel %x, active " - "%d, ready %x, %x, current %x!\n", chan_id, - ichan->active_buffer, ready0, ready1, curbuf); - return IRQ_NONE; - } else - dev_dbg(dev, - "Buffer deactivated on channel %x, active " - "%d, ready %x, %x, current %x, rest %d!\n", chan_id, - ichan->active_buffer, ready0, ready1, curbuf, i); - } - if (unlikely((ichan->active_buffer && (ready1 >> chan_id) & 1) || (!ichan->active_buffer && (ready0 >> chan_id) & 1) )) { @@ -1335,8 +1232,10 @@ static irqreturn_t idmac_interrupt(int irq, void *dev_id) desc = list_entry(ichan->queue.next, struct idmac_tx_desc, list); descnew = desc; - dev_dbg(dev, "IDMAC irq %d, dma 0x%08x, next dma 0x%08x, current %d, curbuf 0x%08x\n", - irq, sg_dma_address(*sg), sgnext ? sg_dma_address(sgnext) : 0, ichan->active_buffer, curbuf); + dev_dbg(dev, "IDMAC irq %d, dma %#llx, next dma %#llx, current %d, curbuf %#x\n", + irq, (u64)sg_dma_address(*sg), + sgnext ? (u64)sg_dma_address(sgnext) : 0, + ichan->active_buffer, curbuf); /* Find the descriptor of sgnext */ sgnew = idmac_sg_next(ichan, &descnew, *sg); @@ -1379,17 +1278,19 @@ static irqreturn_t idmac_interrupt(int irq, void *dev_id) if (likely(sgnew) && ipu_submit_buffer(ichan, descnew, sgnew, ichan->active_buffer) < 0) { - callback = desc->txd.callback; - callback_param = desc->txd.callback_param; + callback = descnew->txd.callback; + callback_param = descnew->txd.callback_param; + list_del_init(&descnew->list); spin_unlock(&ichan->lock); - callback(callback_param); + if (callback) + callback(callback_param); spin_lock(&ichan->lock); } /* Flip the active buffer - even if update above failed */ ichan->active_buffer = !ichan->active_buffer; if (done) - ichan->completed = desc->txd.cookie; + dma_cookie_complete(&desc->txd); callback = desc->txd.callback; callback_param = desc->txd.callback_param; @@ -1435,7 +1336,8 @@ static void ipu_gc_tasklet(unsigned long arg) /* Allocate and initialise a transfer descriptor. */ static struct dma_async_tx_descriptor *idmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, - enum dma_data_direction direction, unsigned long tx_flags) + enum dma_transfer_direction direction, unsigned long tx_flags, + void *context) { struct idmac_channel *ichan = to_idmac_chan(chan); struct idmac_tx_desc *desc = NULL; @@ -1447,7 +1349,7 @@ static struct dma_async_tx_descriptor *idmac_prep_slave_sg(struct dma_chan *chan chan->chan_id != IDMAC_IC_7) return NULL; - if (direction != DMA_FROM_DEVICE && direction != DMA_TO_DEVICE) { + if (!is_slave_direction(direction)) { dev_err(chan->device->dev, "Invalid DMA direction %d!\n", direction); return NULL; } @@ -1496,50 +1398,80 @@ 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, + unsigned long arg) { struct idmac_channel *ichan = to_idmac_chan(chan); struct idmac *idmac = to_idmac(chan->device); + struct ipu *ipu = to_ipu(idmac); + struct list_head *list, *tmp; unsigned long flags; int i; - ipu_disable_channel(idmac, ichan, - ichan->status >= IPU_CHANNEL_ENABLED); + switch (cmd) { + case DMA_PAUSE: + spin_lock_irqsave(&ipu->lock, flags); + ipu_ic_disable_task(ipu, chan->chan_id); - tasklet_disable(&to_ipu(idmac)->tasklet); + /* Return all descriptors into "prepared" state */ + list_for_each_safe(list, tmp, &ichan->queue) + list_del_init(list); - /* ichan->queue is modified in ISR, have to spinlock */ - spin_lock_irqsave(&ichan->lock, flags); - list_splice_init(&ichan->queue, &ichan->free_list); + ichan->sg[0] = NULL; + ichan->sg[1] = NULL; - if (ichan->desc) - for (i = 0; i < ichan->n_tx_desc; i++) { - struct idmac_tx_desc *desc = ichan->desc + i; - if (list_empty(&desc->list)) - /* Descriptor was prepared, but not submitted */ - list_add(&desc->list, &ichan->free_list); + spin_unlock_irqrestore(&ipu->lock, flags); - async_tx_clear_ack(&desc->txd); - } + ichan->status = IPU_CHANNEL_INITIALIZED; + break; + case DMA_TERMINATE_ALL: + ipu_disable_channel(idmac, ichan, + ichan->status >= IPU_CHANNEL_ENABLED); - ichan->sg[0] = NULL; - ichan->sg[1] = NULL; - spin_unlock_irqrestore(&ichan->lock, flags); + tasklet_disable(&ipu->tasklet); - tasklet_enable(&to_ipu(idmac)->tasklet); + /* ichan->queue is modified in ISR, have to spinlock */ + spin_lock_irqsave(&ichan->lock, flags); + list_splice_init(&ichan->queue, &ichan->free_list); - ichan->status = IPU_CHANNEL_INITIALIZED; + if (ichan->desc) + for (i = 0; i < ichan->n_tx_desc; i++) { + struct idmac_tx_desc *desc = ichan->desc + i; + if (list_empty(&desc->list)) + /* Descriptor was prepared, but not submitted */ + list_add(&desc->list, &ichan->free_list); + + async_tx_clear_ack(&desc->txd); + } + + ichan->sg[0] = NULL; + ichan->sg[1] = NULL; + spin_unlock_irqrestore(&ichan->lock, flags); + + tasklet_enable(&ipu->tasklet); + + ichan->status = IPU_CHANNEL_INITIALIZED; + break; + default: + return -ENOSYS; + } + + return 0; } -static void idmac_terminate_all(struct dma_chan *chan) +static int idmac_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, + unsigned long arg) { struct idmac_channel *ichan = to_idmac_chan(chan); + int ret; mutex_lock(&ichan->chan_mutex); - __idmac_terminate_all(chan); + ret = __idmac_control(chan, cmd, arg); mutex_unlock(&ichan->chan_mutex); + + return ret; } #ifdef DEBUG @@ -1574,8 +1506,7 @@ static int idmac_alloc_chan_resources(struct dma_chan *chan) BUG_ON(chan->client_count > 1); WARN_ON(ichan->status != IPU_CHANNEL_FREE); - chan->cookie = 1; - ichan->completed = -ENXIO; + dma_cookie_init(chan); ret = ipu_irq_map(chan->chan_id); if (ret < 0) @@ -1631,7 +1562,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, 0); if (ichan->status > IPU_CHANNEL_FREE) { #ifdef DEBUG @@ -1661,18 +1592,10 @@ 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; - if (cookie != chan->cookie) - return DMA_ERROR; - return DMA_SUCCESS; + return dma_cookie_status(chan, cookie, txstate); } static int __init ipu_idmac_init(struct ipu *ipu) @@ -1688,12 +1611,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++) { @@ -1705,11 +1628,10 @@ static int __init ipu_idmac_init(struct ipu *ipu) ichan->status = IPU_CHANNEL_FREE; ichan->sec_chan_en = false; - ichan->completed = -ENXIO; snprintf(ichan->eof_name, sizeof(ichan->eof_name), "IDMAC EOF %d", i); dma_chan->device = &idmac->dma; - dma_chan->cookie = 1; + dma_cookie_init(dma_chan); dma_chan->chan_id = i; list_add_tail(&dma_chan->device_node, &dma->channels); } @@ -1719,7 +1641,7 @@ static int __init ipu_idmac_init(struct ipu *ipu) return dma_async_device_register(&idmac->dma); } -static void __exit ipu_idmac_exit(struct ipu *ipu) +static void ipu_idmac_exit(struct ipu *ipu) { int i; struct idmac *idmac = &ipu->idmac; @@ -1727,8 +1649,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_prep_slave_sg(&ichan->dma_chan, NULL, 0, DMA_NONE, 0); + idmac_control(&ichan->dma_chan, DMA_TERMINATE_ALL, 0); } dma_async_device_unregister(&idmac->dma); @@ -1740,7 +1661,6 @@ static void __exit ipu_idmac_exit(struct ipu *ipu) static int __init ipu_probe(struct platform_device *pdev) { - struct ipu_platform_data *pdata = pdev->dev.platform_data; struct resource *mem_ipu, *mem_ic; int ret; @@ -1748,7 +1668,7 @@ static int __init ipu_probe(struct platform_device *pdev) mem_ipu = platform_get_resource(pdev, IORESOURCE_MEM, 0); mem_ic = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!pdata || !mem_ipu || !mem_ic) + if (!mem_ipu || !mem_ic) return -EINVAL; ipu_data.dev = &pdev->dev; @@ -1765,22 +1685,19 @@ static int __init ipu_probe(struct platform_device *pdev) goto err_noirq; ipu_data.irq_err = ret; - ipu_data.irq_base = pdata->irq_base; - dev_dbg(&pdev->dev, "fn irq %u, err irq %u, irq-base %u\n", - ipu_data.irq_fn, ipu_data.irq_err, ipu_data.irq_base); + dev_dbg(&pdev->dev, "fn irq %u, err irq %u\n", + ipu_data.irq_fn, ipu_data.irq_err); /* Remap IPU common registers */ - ipu_data.reg_ipu = ioremap(mem_ipu->start, - mem_ipu->end - mem_ipu->start + 1); + ipu_data.reg_ipu = ioremap(mem_ipu->start, resource_size(mem_ipu)); if (!ipu_data.reg_ipu) { ret = -ENOMEM; goto err_ioremap_ipu; } /* Remap Image Converter and Image DMA Controller registers */ - ipu_data.reg_ic = ioremap(mem_ic->start, - mem_ic->end - mem_ic->start + 1); + ipu_data.reg_ic = ioremap(mem_ic->start, resource_size(mem_ic)); if (!ipu_data.reg_ic) { ret = -ENOMEM; goto err_ioremap_ic; @@ -1794,7 +1711,7 @@ static int __init ipu_probe(struct platform_device *pdev) } /* Make sure IPU HSP clock is running */ - clk_enable(ipu_data.ipu_clk); + clk_prepare_enable(ipu_data.ipu_clk); /* Disable all interrupts */ idmac_write_ipureg(&ipu_data, 0, IPU_INT_CTRL_1); @@ -1826,7 +1743,7 @@ static int __init ipu_probe(struct platform_device *pdev) err_idmac_init: err_attach_irq: ipu_irq_detach_irq(&ipu_data, pdev); - clk_disable(ipu_data.ipu_clk); + clk_disable_unprepare(ipu_data.ipu_clk); clk_put(ipu_data.ipu_clk); err_clk_get: iounmap(ipu_data.reg_ic); @@ -1838,18 +1755,17 @@ err_noirq: return ret; } -static int __exit ipu_remove(struct platform_device *pdev) +static int ipu_remove(struct platform_device *pdev) { struct ipu *ipu = platform_get_drvdata(pdev); ipu_idmac_exit(ipu); ipu_irq_detach_irq(ipu, pdev); - clk_disable(ipu->ipu_clk); + clk_disable_unprepare(ipu->ipu_clk); clk_put(ipu->ipu_clk); iounmap(ipu->reg_ic); iounmap(ipu->reg_ipu); tasklet_kill(&ipu->tasklet); - platform_set_drvdata(pdev, NULL); return 0; } @@ -1863,7 +1779,7 @@ static struct platform_driver ipu_platform_driver = { .name = "ipu-core", .owner = THIS_MODULE, }, - .remove = __exit_p(ipu_remove), + .remove = ipu_remove, }; static int __init ipu_init(void) |
