|
@@ -769,3 +769,63 @@ int s3c2410_dma_request(enum dma_ch channel,
|
|
|
|
|
|
return chan->number | DMACH_LOW_LEVEL;
|
|
|
}
|
|
|
+
|
|
|
+EXPORT_SYMBOL(s3c2410_dma_request);
|
|
|
+
|
|
|
+/* s3c2410_dma_free
|
|
|
+ *
|
|
|
+ * release the given channel back to the system, will stop and flush
|
|
|
+ * any outstanding transfers, and ensure the channel is ready for the
|
|
|
+ * next claimant.
|
|
|
+ *
|
|
|
+ * Note, although a warning is currently printed if the freeing client
|
|
|
+ * info is not the same as the registrant's client info, the free is still
|
|
|
+ * allowed to go through.
|
|
|
+*/
|
|
|
+
|
|
|
+int s3c2410_dma_free(enum dma_ch channel, struct s3c2410_dma_client *client)
|
|
|
+{
|
|
|
+ struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ if (chan == NULL)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ local_irq_save(flags);
|
|
|
+
|
|
|
+ if (chan->client != client) {
|
|
|
+ printk(KERN_WARNING "dma%d: possible free from different client (channel %p, passed %p)\n",
|
|
|
+ channel, chan->client, client);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* sort out stopping and freeing the channel */
|
|
|
+
|
|
|
+ if (chan->state != S3C2410_DMA_IDLE) {
|
|
|
+ pr_debug("%s: need to stop dma channel %p\n",
|
|
|
+ __func__, chan);
|
|
|
+
|
|
|
+ /* possibly flush the channel */
|
|
|
+ s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STOP);
|
|
|
+ }
|
|
|
+
|
|
|
+ chan->client = NULL;
|
|
|
+ chan->in_use = 0;
|
|
|
+
|
|
|
+ if (chan->irq_claimed)
|
|
|
+ free_irq(chan->irq, (void *)chan);
|
|
|
+
|
|
|
+ chan->irq_claimed = 0;
|
|
|
+
|
|
|
+ if (!(channel & DMACH_LOW_LEVEL))
|
|
|
+ s3c_dma_chan_map[channel] = NULL;
|
|
|
+
|
|
|
+ local_irq_restore(flags);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+EXPORT_SYMBOL(s3c2410_dma_free);
|
|
|
+
|
|
|
+static int s3c2410_dma_dostop(struct s3c2410_dma_chan *chan)
|
|
|
+{
|
|
|
+ unsigned long flags;
|