|
@@ -302,3 +302,125 @@ int s3c2410_dma_ctrl(enum dma_ch channel, enum s3c2410_chan_op op)
|
|
|
struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
|
|
|
|
|
|
WARN_ON(!chan);
|
|
|
+ if (!chan)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ switch (op) {
|
|
|
+ case S3C2410_DMAOP_START:
|
|
|
+ return s3c64xx_dma_start(chan);
|
|
|
+
|
|
|
+ case S3C2410_DMAOP_STOP:
|
|
|
+ return s3c64xx_dma_stop(chan);
|
|
|
+
|
|
|
+ case S3C2410_DMAOP_FLUSH:
|
|
|
+ return s3c64xx_dma_flush(chan);
|
|
|
+
|
|
|
+ /* believe PAUSE/RESUME are no-ops */
|
|
|
+ case S3C2410_DMAOP_PAUSE:
|
|
|
+ case S3C2410_DMAOP_RESUME:
|
|
|
+ case S3C2410_DMAOP_STARTED:
|
|
|
+ case S3C2410_DMAOP_TIMEOUT:
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ return -ENOENT;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(s3c2410_dma_ctrl);
|
|
|
+
|
|
|
+/* s3c2410_dma_enque
|
|
|
+ *
|
|
|
+ */
|
|
|
+
|
|
|
+int s3c2410_dma_enqueue(enum dma_ch channel, void *id,
|
|
|
+ dma_addr_t data, int size)
|
|
|
+{
|
|
|
+ struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
|
|
|
+ struct s3c64xx_dma_buff *next;
|
|
|
+ struct s3c64xx_dma_buff *buff;
|
|
|
+ struct pl080s_lli *lli;
|
|
|
+ unsigned long flags;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ WARN_ON(!chan);
|
|
|
+ if (!chan)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ buff = kzalloc(sizeof(struct s3c64xx_dma_buff), GFP_ATOMIC);
|
|
|
+ if (!buff) {
|
|
|
+ printk(KERN_ERR "%s: no memory for buffer\n", __func__);
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ lli = dma_pool_alloc(dma_pool, GFP_ATOMIC, &buff->lli_dma);
|
|
|
+ if (!lli) {
|
|
|
+ printk(KERN_ERR "%s: no memory for lli\n", __func__);
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto err_buff;
|
|
|
+ }
|
|
|
+
|
|
|
+ pr_debug("%s: buff %p, dp %08x lli (%p, %08x) %d\n",
|
|
|
+ __func__, buff, data, lli, (u32)buff->lli_dma, size);
|
|
|
+
|
|
|
+ buff->lli = lli;
|
|
|
+ buff->pw = id;
|
|
|
+
|
|
|
+ s3c64xx_dma_fill_lli(chan, lli, data, size);
|
|
|
+
|
|
|
+ local_irq_save(flags);
|
|
|
+
|
|
|
+ if ((next = chan->next) != NULL) {
|
|
|
+ struct s3c64xx_dma_buff *end = chan->end;
|
|
|
+ struct pl080s_lli *endlli = end->lli;
|
|
|
+
|
|
|
+ pr_debug("enquing onto channel\n");
|
|
|
+
|
|
|
+ end->next = buff;
|
|
|
+ endlli->next_lli = buff->lli_dma;
|
|
|
+
|
|
|
+ if (chan->flags & S3C2410_DMAF_CIRCULAR) {
|
|
|
+ struct s3c64xx_dma_buff *curr = chan->curr;
|
|
|
+ lli->next_lli = curr->lli_dma;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (next == chan->curr) {
|
|
|
+ writel(buff->lli_dma, chan->regs + PL080_CH_LLI);
|
|
|
+ chan->next = buff;
|
|
|
+ }
|
|
|
+
|
|
|
+ show_lli(endlli);
|
|
|
+ chan->end = buff;
|
|
|
+ } else {
|
|
|
+ pr_debug("enquing onto empty channel\n");
|
|
|
+
|
|
|
+ chan->curr = buff;
|
|
|
+ chan->next = buff;
|
|
|
+ chan->end = buff;
|
|
|
+
|
|
|
+ s3c64xx_lli_to_regs(chan, lli);
|
|
|
+ }
|
|
|
+
|
|
|
+ local_irq_restore(flags);
|
|
|
+
|
|
|
+ show_lli(lli);
|
|
|
+
|
|
|
+ dbg_showchan(chan);
|
|
|
+ dbg_showbuffs(chan);
|
|
|
+ return 0;
|
|
|
+
|
|
|
+err_buff:
|
|
|
+ kfree(buff);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+EXPORT_SYMBOL(s3c2410_dma_enqueue);
|
|
|
+
|
|
|
+
|
|
|
+int s3c2410_dma_devconfig(enum dma_ch channel,
|
|
|
+ enum dma_data_direction source,
|
|
|
+ unsigned long devaddr)
|
|
|
+{
|
|
|
+ struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
|
|
|
+ u32 peripheral;
|
|
|
+ u32 config = 0;
|
|
|
+
|
|
|
+ pr_debug("%s: channel %d, source %d, dev %08lx, chan %p\n",
|