diff options
author | Guennadi Liakhovetski <g.liakhovetski@gmx.de> | 2011-08-25 12:26:53 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2011-11-03 16:28:33 -0400 |
commit | 1d3564d91f94d0b598304eb6ebe3b83a83176f7a (patch) | |
tree | 585a122d4ca32f02d65cb24267c5570842567e95 /drivers/dma/ipu | |
parent | 2d86401c2cbfce9f99b08ba168bdb60b2eb7796e (diff) |
[media] dmaengine: ipu-idmac: add support for the DMA_PAUSE control
To support multi-size buffers in the mx3_camera V4L2 driver we have to be
able to stop DMA on a channel without releasing descriptors and completely
halting the hardware. Use the DMA_PAUSE control to implement this mode.
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Acked-by: Vinod Koul <vinod.koul@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/dma/ipu')
-rw-r--r-- | drivers/dma/ipu/ipu_idmac.c | 65 |
1 files changed, 42 insertions, 23 deletions
diff --git a/drivers/dma/ipu/ipu_idmac.c b/drivers/dma/ipu/ipu_idmac.c index 6815905a772f..ddc2a1331822 100644 --- a/drivers/dma/ipu/ipu_idmac.c +++ b/drivers/dma/ipu/ipu_idmac.c | |||
@@ -1307,6 +1307,7 @@ static irqreturn_t idmac_interrupt(int irq, void *dev_id) | |||
1307 | ipu_submit_buffer(ichan, descnew, sgnew, ichan->active_buffer) < 0) { | 1307 | ipu_submit_buffer(ichan, descnew, sgnew, ichan->active_buffer) < 0) { |
1308 | callback = descnew->txd.callback; | 1308 | callback = descnew->txd.callback; |
1309 | callback_param = descnew->txd.callback_param; | 1309 | callback_param = descnew->txd.callback_param; |
1310 | list_del_init(&descnew->list); | ||
1310 | spin_unlock(&ichan->lock); | 1311 | spin_unlock(&ichan->lock); |
1311 | if (callback) | 1312 | if (callback) |
1312 | callback(callback_param); | 1313 | callback(callback_param); |
@@ -1428,39 +1429,58 @@ static int __idmac_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, | |||
1428 | { | 1429 | { |
1429 | struct idmac_channel *ichan = to_idmac_chan(chan); | 1430 | struct idmac_channel *ichan = to_idmac_chan(chan); |
1430 | struct idmac *idmac = to_idmac(chan->device); | 1431 | struct idmac *idmac = to_idmac(chan->device); |
1432 | struct ipu *ipu = to_ipu(idmac); | ||
1433 | struct list_head *list, *tmp; | ||
1431 | unsigned long flags; | 1434 | unsigned long flags; |
1432 | int i; | 1435 | int i; |
1433 | 1436 | ||
1434 | /* Only supports DMA_TERMINATE_ALL */ | 1437 | switch (cmd) { |
1435 | if (cmd != DMA_TERMINATE_ALL) | 1438 | case DMA_PAUSE: |
1436 | return -ENXIO; | 1439 | spin_lock_irqsave(&ipu->lock, flags); |
1440 | ipu_ic_disable_task(ipu, chan->chan_id); | ||
1437 | 1441 | ||
1438 | ipu_disable_channel(idmac, ichan, | 1442 | /* Return all descriptors into "prepared" state */ |
1439 | ichan->status >= IPU_CHANNEL_ENABLED); | 1443 | list_for_each_safe(list, tmp, &ichan->queue) |
1444 | list_del_init(list); | ||
1440 | 1445 | ||
1441 | tasklet_disable(&to_ipu(idmac)->tasklet); | 1446 | ichan->sg[0] = NULL; |
1447 | ichan->sg[1] = NULL; | ||
1442 | 1448 | ||
1443 | /* ichan->queue is modified in ISR, have to spinlock */ | 1449 | spin_unlock_irqrestore(&ipu->lock, flags); |
1444 | spin_lock_irqsave(&ichan->lock, flags); | ||
1445 | list_splice_init(&ichan->queue, &ichan->free_list); | ||
1446 | 1450 | ||
1447 | if (ichan->desc) | 1451 | ichan->status = IPU_CHANNEL_INITIALIZED; |
1448 | for (i = 0; i < ichan->n_tx_desc; i++) { | 1452 | break; |
1449 | struct idmac_tx_desc *desc = ichan->desc + i; | 1453 | case DMA_TERMINATE_ALL: |
1450 | if (list_empty(&desc->list)) | 1454 | ipu_disable_channel(idmac, ichan, |
1451 | /* Descriptor was prepared, but not submitted */ | 1455 | ichan->status >= IPU_CHANNEL_ENABLED); |
1452 | list_add(&desc->list, &ichan->free_list); | ||
1453 | 1456 | ||
1454 | async_tx_clear_ack(&desc->txd); | 1457 | tasklet_disable(&ipu->tasklet); |
1455 | } | ||
1456 | 1458 | ||
1457 | ichan->sg[0] = NULL; | 1459 | /* ichan->queue is modified in ISR, have to spinlock */ |
1458 | ichan->sg[1] = NULL; | 1460 | spin_lock_irqsave(&ichan->lock, flags); |
1459 | spin_unlock_irqrestore(&ichan->lock, flags); | 1461 | list_splice_init(&ichan->queue, &ichan->free_list); |
1460 | 1462 | ||
1461 | tasklet_enable(&to_ipu(idmac)->tasklet); | 1463 | if (ichan->desc) |
1464 | for (i = 0; i < ichan->n_tx_desc; i++) { | ||
1465 | struct idmac_tx_desc *desc = ichan->desc + i; | ||
1466 | if (list_empty(&desc->list)) | ||
1467 | /* Descriptor was prepared, but not submitted */ | ||
1468 | list_add(&desc->list, &ichan->free_list); | ||
1462 | 1469 | ||
1463 | ichan->status = IPU_CHANNEL_INITIALIZED; | 1470 | async_tx_clear_ack(&desc->txd); |
1471 | } | ||
1472 | |||
1473 | ichan->sg[0] = NULL; | ||
1474 | ichan->sg[1] = NULL; | ||
1475 | spin_unlock_irqrestore(&ichan->lock, flags); | ||
1476 | |||
1477 | tasklet_enable(&ipu->tasklet); | ||
1478 | |||
1479 | ichan->status = IPU_CHANNEL_INITIALIZED; | ||
1480 | break; | ||
1481 | default: | ||
1482 | return -ENOSYS; | ||
1483 | } | ||
1464 | 1484 | ||
1465 | return 0; | 1485 | return 0; |
1466 | } | 1486 | } |
@@ -1663,7 +1683,6 @@ static void __exit ipu_idmac_exit(struct ipu *ipu) | |||
1663 | struct idmac_channel *ichan = ipu->channel + i; | 1683 | struct idmac_channel *ichan = ipu->channel + i; |
1664 | 1684 | ||
1665 | idmac_control(&ichan->dma_chan, DMA_TERMINATE_ALL, 0); | 1685 | idmac_control(&ichan->dma_chan, DMA_TERMINATE_ALL, 0); |
1666 | idmac_prep_slave_sg(&ichan->dma_chan, NULL, 0, DMA_NONE, 0); | ||
1667 | } | 1686 | } |
1668 | 1687 | ||
1669 | dma_async_device_unregister(&idmac->dma); | 1688 | dma_async_device_unregister(&idmac->dma); |