|
@@ -196,3 +196,179 @@ s3c2410_dma_waitforload(struct s3c2410_dma_chan *chan, int line)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/* s3c2410_dma_loadbuffer
|
|
|
+ *
|
|
|
+ * load a buffer, and update the channel state
|
|
|
+*/
|
|
|
+
|
|
|
+static inline int
|
|
|
+s3c2410_dma_loadbuffer(struct s3c2410_dma_chan *chan,
|
|
|
+ struct s3c2410_dma_buf *buf)
|
|
|
+{
|
|
|
+ unsigned long reload;
|
|
|
+
|
|
|
+ if (buf == NULL) {
|
|
|
+ dmawarn("buffer is NULL\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ pr_debug("s3c2410_chan_loadbuffer: loading buff %p (0x%08lx,0x%06x)\n",
|
|
|
+ buf, (unsigned long)buf->data, buf->size);
|
|
|
+
|
|
|
+ /* check the state of the channel before we do anything */
|
|
|
+
|
|
|
+ if (chan->load_state == S3C2410_DMALOAD_1LOADED) {
|
|
|
+ dmawarn("load_state is S3C2410_DMALOAD_1LOADED\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (chan->load_state == S3C2410_DMALOAD_1LOADED_1RUNNING) {
|
|
|
+ dmawarn("state is S3C2410_DMALOAD_1LOADED_1RUNNING\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ /* it would seem sensible if we are the last buffer to not bother
|
|
|
+ * with the auto-reload bit, so that the DMA engine will not try
|
|
|
+ * and load another transfer after this one has finished...
|
|
|
+ */
|
|
|
+ if (chan->load_state == S3C2410_DMALOAD_NONE) {
|
|
|
+ pr_debug("load_state is none, checking for noreload (next=%p)\n",
|
|
|
+ buf->next);
|
|
|
+ reload = (buf->next == NULL) ? S3C2410_DCON_NORELOAD : 0;
|
|
|
+ } else {
|
|
|
+ //pr_debug("load_state is %d => autoreload\n", chan->load_state);
|
|
|
+ reload = S3C2410_DCON_AUTORELOAD;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((buf->data & 0xf0000000) != 0x30000000) {
|
|
|
+ dmawarn("dmaload: buffer is %p\n", (void *)buf->data);
|
|
|
+ }
|
|
|
+
|
|
|
+ writel(buf->data, chan->addr_reg);
|
|
|
+
|
|
|
+ dma_wrreg(chan, S3C2410_DMA_DCON,
|
|
|
+ chan->dcon | reload | (buf->size/chan->xfer_unit));
|
|
|
+
|
|
|
+ chan->next = buf->next;
|
|
|
+
|
|
|
+ /* update the state of the channel */
|
|
|
+
|
|
|
+ switch (chan->load_state) {
|
|
|
+ case S3C2410_DMALOAD_NONE:
|
|
|
+ chan->load_state = S3C2410_DMALOAD_1LOADED;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case S3C2410_DMALOAD_1RUNNING:
|
|
|
+ chan->load_state = S3C2410_DMALOAD_1LOADED_1RUNNING;
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ dmawarn("dmaload: unknown state %d in loadbuffer\n",
|
|
|
+ chan->load_state);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* s3c2410_dma_call_op
|
|
|
+ *
|
|
|
+ * small routine to call the op routine with the given op if it has been
|
|
|
+ * registered
|
|
|
+*/
|
|
|
+
|
|
|
+static void
|
|
|
+s3c2410_dma_call_op(struct s3c2410_dma_chan *chan, enum s3c2410_chan_op op)
|
|
|
+{
|
|
|
+ if (chan->op_fn != NULL) {
|
|
|
+ (chan->op_fn)(chan, op);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* s3c2410_dma_buffdone
|
|
|
+ *
|
|
|
+ * small wrapper to check if callback routine needs to be called, and
|
|
|
+ * if so, call it
|
|
|
+*/
|
|
|
+
|
|
|
+static inline void
|
|
|
+s3c2410_dma_buffdone(struct s3c2410_dma_chan *chan, struct s3c2410_dma_buf *buf,
|
|
|
+ enum s3c2410_dma_buffresult result)
|
|
|
+{
|
|
|
+#if 0
|
|
|
+ pr_debug("callback_fn=%p, buf=%p, id=%p, size=%d, result=%d\n",
|
|
|
+ chan->callback_fn, buf, buf->id, buf->size, result);
|
|
|
+#endif
|
|
|
+
|
|
|
+ if (chan->callback_fn != NULL) {
|
|
|
+ (chan->callback_fn)(chan, buf->id, buf->size, result);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* s3c2410_dma_start
|
|
|
+ *
|
|
|
+ * start a dma channel going
|
|
|
+*/
|
|
|
+
|
|
|
+static int s3c2410_dma_start(struct s3c2410_dma_chan *chan)
|
|
|
+{
|
|
|
+ unsigned long tmp;
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ pr_debug("s3c2410_start_dma: channel=%d\n", chan->number);
|
|
|
+
|
|
|
+ local_irq_save(flags);
|
|
|
+
|
|
|
+ if (chan->state == S3C2410_DMA_RUNNING) {
|
|
|
+ pr_debug("s3c2410_start_dma: already running (%d)\n", chan->state);
|
|
|
+ local_irq_restore(flags);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ chan->state = S3C2410_DMA_RUNNING;
|
|
|
+
|
|
|
+ /* check whether there is anything to load, and if not, see
|
|
|
+ * if we can find anything to load
|
|
|
+ */
|
|
|
+
|
|
|
+ if (chan->load_state == S3C2410_DMALOAD_NONE) {
|
|
|
+ if (chan->next == NULL) {
|
|
|
+ printk(KERN_ERR "dma%d: channel has nothing loaded\n",
|
|
|
+ chan->number);
|
|
|
+ chan->state = S3C2410_DMA_IDLE;
|
|
|
+ local_irq_restore(flags);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ s3c2410_dma_loadbuffer(chan, chan->next);
|
|
|
+ }
|
|
|
+
|
|
|
+ dbg_showchan(chan);
|
|
|
+
|
|
|
+ /* enable the channel */
|
|
|
+
|
|
|
+ if (!chan->irq_enabled) {
|
|
|
+ enable_irq(chan->irq);
|
|
|
+ chan->irq_enabled = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* start the channel going */
|
|
|
+
|
|
|
+ tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG);
|
|
|
+ tmp &= ~S3C2410_DMASKTRIG_STOP;
|
|
|
+ tmp |= S3C2410_DMASKTRIG_ON;
|
|
|
+ dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp);
|
|
|
+
|
|
|
+ pr_debug("dma%d: %08lx to DMASKTRIG\n", chan->number, tmp);
|
|
|
+
|
|
|
+#if 0
|
|
|
+ /* the dma buffer loads should take care of clearing the AUTO
|
|
|
+ * reloading feature */
|
|
|
+ tmp = dma_rdreg(chan, S3C2410_DMA_DCON);
|
|
|
+ tmp &= ~S3C2410_DCON_NORELOAD;
|
|
|
+ dma_wrreg(chan, S3C2410_DMA_DCON, tmp);
|
|
|
+#endif
|
|
|
+
|
|
|
+ s3c2410_dma_call_op(chan, S3C2410_DMAOP_START);
|
|
|
+
|
|
|
+ dbg_showchan(chan);
|
|
|
+
|
|
|
+ /* if we've only loaded one buffer onto the channel, then chec
|