|
@@ -577,3 +577,195 @@ s3c2410_dma_lastxfer(struct s3c2410_dma_chan *chan)
|
|
#define dmadbg2(x...)
|
|
#define dmadbg2(x...)
|
|
|
|
|
|
static irqreturn_t
|
|
static irqreturn_t
|
|
|
|
+s3c2410_dma_irq(int irq, void *devpw)
|
|
|
|
+{
|
|
|
|
+ struct s3c2410_dma_chan *chan = (struct s3c2410_dma_chan *)devpw;
|
|
|
|
+ struct s3c2410_dma_buf *buf;
|
|
|
|
+
|
|
|
|
+ buf = chan->curr;
|
|
|
|
+
|
|
|
|
+ dbg_showchan(chan);
|
|
|
|
+
|
|
|
|
+ /* modify the channel state */
|
|
|
|
+
|
|
|
|
+ switch (chan->load_state) {
|
|
|
|
+ case S3C2410_DMALOAD_1RUNNING:
|
|
|
|
+ /* TODO - if we are running only one buffer, we probably
|
|
|
|
+ * want to reload here, and then worry about the buffer
|
|
|
|
+ * callback */
|
|
|
|
+
|
|
|
|
+ chan->load_state = S3C2410_DMALOAD_NONE;
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case S3C2410_DMALOAD_1LOADED:
|
|
|
|
+ /* iirc, we should go back to NONE loaded here, we
|
|
|
|
+ * had a buffer, and it was never verified as being
|
|
|
|
+ * loaded.
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+ chan->load_state = S3C2410_DMALOAD_NONE;
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case S3C2410_DMALOAD_1LOADED_1RUNNING:
|
|
|
|
+ /* we'll worry about checking to see if another buffer is
|
|
|
|
+ * ready after we've called back the owner. This should
|
|
|
|
+ * ensure we do not wait around too long for the DMA
|
|
|
|
+ * engine to start the next transfer
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+ chan->load_state = S3C2410_DMALOAD_1LOADED;
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case S3C2410_DMALOAD_NONE:
|
|
|
|
+ printk(KERN_ERR "dma%d: IRQ with no loaded buffer?\n",
|
|
|
|
+ chan->number);
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ default:
|
|
|
|
+ printk(KERN_ERR "dma%d: IRQ in invalid load_state %d\n",
|
|
|
|
+ chan->number, chan->load_state);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (buf != NULL) {
|
|
|
|
+ /* update the chain to make sure that if we load any more
|
|
|
|
+ * buffers when we call the callback function, things should
|
|
|
|
+ * work properly */
|
|
|
|
+
|
|
|
|
+ chan->curr = buf->next;
|
|
|
|
+ buf->next = NULL;
|
|
|
|
+
|
|
|
|
+ if (buf->magic != BUF_MAGIC) {
|
|
|
|
+ printk(KERN_ERR "dma%d: %s: buf %p incorrect magic\n",
|
|
|
|
+ chan->number, __func__, buf);
|
|
|
|
+ return IRQ_HANDLED;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ s3c2410_dma_buffdone(chan, buf, S3C2410_RES_OK);
|
|
|
|
+
|
|
|
|
+ /* free resouces */
|
|
|
|
+ s3c2410_dma_freebuf(buf);
|
|
|
|
+ } else {
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* only reload if the channel is still running... our buffer done
|
|
|
|
+ * routine may have altered the state by requesting the dma channel
|
|
|
|
+ * to stop or shutdown... */
|
|
|
|
+
|
|
|
|
+ /* todo: check that when the channel is shut-down from inside this
|
|
|
|
+ * function, we cope with unsetting reload, etc */
|
|
|
|
+
|
|
|
|
+ if (chan->next != NULL && chan->state != S3C2410_DMA_IDLE) {
|
|
|
|
+ unsigned long flags;
|
|
|
|
+
|
|
|
|
+ switch (chan->load_state) {
|
|
|
|
+ case S3C2410_DMALOAD_1RUNNING:
|
|
|
|
+ /* don't need to do anything for this state */
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case S3C2410_DMALOAD_NONE:
|
|
|
|
+ /* can load buffer immediately */
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case S3C2410_DMALOAD_1LOADED:
|
|
|
|
+ if (s3c2410_dma_waitforload(chan, __LINE__) == 0) {
|
|
|
|
+ /* flag error? */
|
|
|
|
+ printk(KERN_ERR "dma%d: timeout waiting for load (%s)\n",
|
|
|
|
+ chan->number, __func__);
|
|
|
|
+ return IRQ_HANDLED;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case S3C2410_DMALOAD_1LOADED_1RUNNING:
|
|
|
|
+ goto no_load;
|
|
|
|
+
|
|
|
|
+ default:
|
|
|
|
+ printk(KERN_ERR "dma%d: unknown load_state in irq, %d\n",
|
|
|
|
+ chan->number, chan->load_state);
|
|
|
|
+ return IRQ_HANDLED;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ local_irq_save(flags);
|
|
|
|
+ s3c2410_dma_loadbuffer(chan, chan->next);
|
|
|
|
+ local_irq_restore(flags);
|
|
|
|
+ } else {
|
|
|
|
+ s3c2410_dma_lastxfer(chan);
|
|
|
|
+
|
|
|
|
+ /* see if we can stop this channel.. */
|
|
|
|
+ if (chan->load_state == S3C2410_DMALOAD_NONE) {
|
|
|
|
+ pr_debug("dma%d: end of transfer, stopping channel (%ld)\n",
|
|
|
|
+ chan->number, jiffies);
|
|
|
|
+ s3c2410_dma_ctrl(chan->number | DMACH_LOW_LEVEL,
|
|
|
|
+ S3C2410_DMAOP_STOP);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ no_load:
|
|
|
|
+ return IRQ_HANDLED;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static struct s3c2410_dma_chan *s3c2410_dma_map_channel(int channel);
|
|
|
|
+
|
|
|
|
+/* s3c2410_request_dma
|
|
|
|
+ *
|
|
|
|
+ * get control of an dma channel
|
|
|
|
+*/
|
|
|
|
+
|
|
|
|
+int s3c2410_dma_request(enum dma_ch channel,
|
|
|
|
+ struct s3c2410_dma_client *client,
|
|
|
|
+ void *dev)
|
|
|
|
+{
|
|
|
|
+ struct s3c2410_dma_chan *chan;
|
|
|
|
+ unsigned long flags;
|
|
|
|
+ int err;
|
|
|
|
+
|
|
|
|
+ pr_debug("dma%d: s3c2410_request_dma: client=%s, dev=%p\n",
|
|
|
|
+ channel, client->name, dev);
|
|
|
|
+
|
|
|
|
+ local_irq_save(flags);
|
|
|
|
+
|
|
|
|
+ chan = s3c2410_dma_map_channel(channel);
|
|
|
|
+ if (chan == NULL) {
|
|
|
|
+ local_irq_restore(flags);
|
|
|
|
+ return -EBUSY;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ dbg_showchan(chan);
|
|
|
|
+
|
|
|
|
+ chan->client = client;
|
|
|
|
+ chan->in_use = 1;
|
|
|
|
+
|
|
|
|
+ if (!chan->irq_claimed) {
|
|
|
|
+ pr_debug("dma%d: %s : requesting irq %d\n",
|
|
|
|
+ channel, __func__, chan->irq);
|
|
|
|
+
|
|
|
|
+ chan->irq_claimed = 1;
|
|
|
|
+ local_irq_restore(flags);
|
|
|
|
+
|
|
|
|
+ err = request_irq(chan->irq, s3c2410_dma_irq, IRQF_DISABLED,
|
|
|
|
+ client->name, (void *)chan);
|
|
|
|
+
|
|
|
|
+ local_irq_save(flags);
|
|
|
|
+
|
|
|
|
+ if (err) {
|
|
|
|
+ chan->in_use = 0;
|
|
|
|
+ chan->irq_claimed = 0;
|
|
|
|
+ local_irq_restore(flags);
|
|
|
|
+
|
|
|
|
+ printk(KERN_ERR "%s: cannot get IRQ %d for DMA %d\n",
|
|
|
|
+ client->name, chan->irq, chan->number);
|
|
|
|
+ return err;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ chan->irq_enabled = 1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ local_irq_restore(flags);
|
|
|
|
+
|
|
|
|
+ /* need to setup */
|
|
|
|
+
|
|
|
|
+ pr_debug("%s: channel initialised, %p\n", __func__, chan);
|
|
|
|
+
|
|
|
|
+ return chan->number | DMACH_LOW_LEVEL;
|
|
|
|
+}
|