diff options
| author | Guennadi Liakhovetski <g.liakhovetski@gmx.de> | 2011-08-25 13:26:53 -0300 | 
|---|---|---|
| committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2011-11-03 18:28:33 -0200 | 
| commit | 1d3564d91f94d0b598304eb6ebe3b83a83176f7a (patch) | |
| tree | 585a122d4ca32f02d65cb24267c5570842567e95 /drivers/dma/ipu | |
| parent | 2d86401c2cbfce9f99b08ba168bdb60b2eb7796e (diff) | |
| download | linux-1d3564d91f94d0b598304eb6ebe3b83a83176f7a.tar.bz2 | |
[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)  	    ipu_submit_buffer(ichan, descnew, sgnew, ichan->active_buffer) < 0) {  		callback = descnew->txd.callback;  		callback_param = descnew->txd.callback_param; +		list_del_init(&descnew->list);  		spin_unlock(&ichan->lock);  		if (callback)  			callback(callback_param); @@ -1428,39 +1429,58 @@ 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); +	struct ipu *ipu = to_ipu(idmac); +	struct list_head *list, *tmp;  	unsigned long flags;  	int i; -	/* Only supports DMA_TERMINATE_ALL */ -	if (cmd != DMA_TERMINATE_ALL) -		return -ENXIO; +	switch (cmd) { +	case DMA_PAUSE: +		spin_lock_irqsave(&ipu->lock, flags); +		ipu_ic_disable_task(ipu, chan->chan_id); -	ipu_disable_channel(idmac, ichan, -			    ichan->status >= IPU_CHANNEL_ENABLED); +		/* Return all descriptors into "prepared" state */ +		list_for_each_safe(list, tmp, &ichan->queue) +			list_del_init(list); -	tasklet_disable(&to_ipu(idmac)->tasklet); +		ichan->sg[0] = NULL; +		ichan->sg[1] = NULL; -	/* ichan->queue is modified in ISR, have to spinlock */ -	spin_lock_irqsave(&ichan->lock, flags); -	list_splice_init(&ichan->queue, &ichan->free_list); +		spin_unlock_irqrestore(&ipu->lock, flags); -	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); +		ichan->status = IPU_CHANNEL_INITIALIZED; +		break; +	case DMA_TERMINATE_ALL: +		ipu_disable_channel(idmac, ichan, +				    ichan->status >= IPU_CHANNEL_ENABLED); -			async_tx_clear_ack(&desc->txd); -		} +		tasklet_disable(&ipu->tasklet); -	ichan->sg[0] = NULL; -	ichan->sg[1] = NULL; -	spin_unlock_irqrestore(&ichan->lock, flags); +		/* ichan->queue is modified in ISR, have to spinlock */ +		spin_lock_irqsave(&ichan->lock, flags); +		list_splice_init(&ichan->queue, &ichan->free_list); -	tasklet_enable(&to_ipu(idmac)->tasklet); +		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); -	ichan->status = IPU_CHANNEL_INITIALIZED; +				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;  } @@ -1663,7 +1683,6 @@ static void __exit ipu_idmac_exit(struct ipu *ipu)  		struct idmac_channel *ichan = ipu->channel + i;  		idmac_control(&ichan->dma_chan, DMA_TERMINATE_ALL, 0); -		idmac_prep_slave_sg(&ichan->dma_chan, NULL, 0, DMA_NONE, 0);  	}  	dma_async_device_unregister(&idmac->dma); |