From mboxrd@z Thu Jan 1 00:00:00 1970 From: Vasily Khoruzhick Subject: [PATCH 1/2] s3c24xx: DMA: don't use autoreload feature Date: Wed, 18 Aug 2010 18:04:25 +0300 Message-ID: <1282143866-8068-2-git-send-email-anarsoul@gmail.com> References: <1282143866-8068-1-git-send-email-anarsoul@gmail.com> Return-path: Received: from mail-ew0-f46.google.com ([209.85.215.46]:60519 "EHLO mail-ew0-f46.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753650Ab0HRPGe (ORCPT ); Wed, 18 Aug 2010 11:06:34 -0400 Received: by ewy23 with SMTP id 23so423767ewy.19 for ; Wed, 18 Aug 2010 08:06:33 -0700 (PDT) In-Reply-To: <1282143866-8068-1-git-send-email-anarsoul@gmail.com> Sender: linux-samsung-soc-owner@vger.kernel.org List-Id: linux-samsung-soc@vger.kernel.org To: Ben Dooks , linux-arm-kernel@lists.infradead.org, linux-samsung-soc@vger.kernel.org, Thomas Kleffel , anarsoul@gmail.com Some integrated DMA-capable hardware doesn't like autoreload feature of s3c24xx DMA-engine, that's why s3cmci driver didn't work with DMA transfers enabled. I rewrote DMA driver not to use autoreload feature and removed all pre-loading features. Buffer re-load is fast enought to perform it in IRQ handler, and anyway I don't see any reason to waste CPU cycles on waiting for buffer load. Driver is much simplier now, it was tested with s3cmci and s3c24xx-i2s drivers on s3c2442 and s3c2410 SoCs and works just nice. Signed-off-by: Vasily Khoruzhick --- arch/arm/mach-s3c2410/include/mach/dma.h | 13 +- arch/arm/plat-s3c24xx/dma.c | 426 +++++------------------------- 2 files changed, 68 insertions(+), 371 deletions(-) diff --git a/arch/arm/mach-s3c2410/include/mach/dma.h b/arch/arm/mach-s3c2410/include/mach/dma.h index cf68136..198826f 100644 --- a/arch/arm/mach-s3c2410/include/mach/dma.h +++ b/arch/arm/mach-s3c2410/include/mach/dma.h @@ -79,16 +79,9 @@ enum s3c2410_dma_state { * * There are no buffers loaded (the channel should be inactive) * - * S3C2410_DMA_1LOADED - * - * There is one buffer loaded, however it has not been confirmed to be - * loaded by the DMA engine. This may be because the channel is not - * yet running, or the DMA driver decided that it was too costly to - * sit and wait for it to happen. - * * S3C2410_DMA_1RUNNING * - * The buffer has been confirmed running, and not finisged + * The buffer has been confirmed running, and not finished * * S3C2410_DMA_1LOADED_1RUNNING * @@ -98,9 +91,7 @@ enum s3c2410_dma_state { enum s3c2410_dma_loadst { S3C2410_DMALOAD_NONE, - S3C2410_DMALOAD_1LOADED, S3C2410_DMALOAD_1RUNNING, - S3C2410_DMALOAD_1LOADED_1RUNNING, }; @@ -129,6 +120,7 @@ struct s3c2410_dma_buf { dma_addr_t data; /* start of DMA data */ dma_addr_t ptr; /* where the DMA got to [1] */ void *id; /* client's id */ + unsigned int timestamp; }; /* [1] is this updated for both recv/send modes? */ @@ -189,6 +181,7 @@ struct s3c2410_dma_chan { struct s3c2410_dma_buf *curr; /* current dma buffer */ struct s3c2410_dma_buf *next; /* next buffer to load */ struct s3c2410_dma_buf *end; /* end of queue */ + spinlock_t queue_lock; /* system device */ struct sys_device dev; diff --git a/arch/arm/plat-s3c24xx/dma.c b/arch/arm/plat-s3c24xx/dma.c index 6ad274e..926fc9e 100644 --- a/arch/arm/plat-s3c24xx/dma.c +++ b/arch/arm/plat-s3c24xx/dma.c @@ -133,70 +133,6 @@ dmadbg_showregs(const char *fname, int line, struct s3c2410_dma_chan *chan) #define dbg_showchan(chan) do { } while(0) #endif /* CONFIG_S3C2410_DMA_DEBUG */ -/* s3c2410_dma_stats_timeout - * - * Update DMA stats from timeout info -*/ - -static void -s3c2410_dma_stats_timeout(struct s3c2410_dma_stats *stats, int val) -{ - if (stats == NULL) - return; - - if (val > stats->timeout_longest) - stats->timeout_longest = val; - if (val < stats->timeout_shortest) - stats->timeout_shortest = val; - - stats->timeout_avg += val; -} - -/* s3c2410_dma_waitforload - * - * wait for the DMA engine to load a buffer, and update the state accordingly -*/ - -static int -s3c2410_dma_waitforload(struct s3c2410_dma_chan *chan, int line) -{ - int timeout = chan->load_timeout; - int took; - - if (chan->load_state != S3C2410_DMALOAD_1LOADED) { - printk(KERN_ERR "dma%d: s3c2410_dma_waitforload() called in loadstate %d from line %d\n", chan->number, chan->load_state, line); - return 0; - } - - if (chan->stats != NULL) - chan->stats->loads++; - - while (--timeout > 0) { - if ((dma_rdreg(chan, S3C2410_DMA_DSTAT) << (32-20)) != 0) { - took = chan->load_timeout - timeout; - - s3c2410_dma_stats_timeout(chan->stats, took); - - switch (chan->load_state) { - case S3C2410_DMALOAD_1LOADED: - chan->load_state = S3C2410_DMALOAD_1RUNNING; - break; - - default: - printk(KERN_ERR "dma%d: unknown load_state in s3c2410_dma_waitforload() %d\n", chan->number, chan->load_state); - } - - return 1; - } - } - - if (chan->stats != NULL) { - chan->stats->timeout_failed++; - } - - return 0; -} - /* s3c2410_dma_loadbuffer * * load a buffer, and update the channel state @@ -206,8 +142,6 @@ 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; @@ -218,26 +152,9 @@ s3c2410_dma_loadbuffer(struct s3c2410_dma_chan *chan, /* 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 (chan->load_state != S3C2410_DMALOAD_NONE) + printk(KERN_ERR "dma%d: channel already has buffer loaded\n", + chan->number); if ((buf->data & 0xf0000000) != 0x30000000) { dmawarn("dmaload: buffer is %p\n", (void *)buf->data); @@ -246,26 +163,15 @@ s3c2410_dma_loadbuffer(struct s3c2410_dma_chan *chan, writel(buf->data, chan->addr_reg); dma_wrreg(chan, S3C2410_DMA_DCON, - chan->dcon | reload | (buf->size/chan->xfer_unit)); + chan->dcon | S3C2410_DCON_NORELOAD | + (buf->size/chan->xfer_unit)); - chan->next = buf->next; + chan->curr = buf; /* update the state of the channel */ + chan->load_state = S3C2410_DMALOAD_1RUNNING; - 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; - } + buf->timestamp = jiffies; return 0; } @@ -345,7 +251,6 @@ static int s3c2410_dma_start(struct s3c2410_dma_chan *chan) dbg_showchan(chan); /* enable the channel */ - if (!chan->irq_enabled) { enable_irq(chan->irq); chan->irq_enabled = 1; @@ -360,14 +265,6 @@ static int s3c2410_dma_start(struct s3c2410_dma_chan *chan) 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); @@ -377,43 +274,11 @@ static int s3c2410_dma_start(struct s3c2410_dma_chan *chan) * the first buffer is finished, the new one will be loaded onto * the channel */ - if (chan->next != NULL) { - if (chan->load_state == S3C2410_DMALOAD_1LOADED) { - - if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { - pr_debug("%s: buff not yet loaded, no more todo\n", - __func__); - } else { - chan->load_state = S3C2410_DMALOAD_1RUNNING; - s3c2410_dma_loadbuffer(chan, chan->next); - } - - } else if (chan->load_state == S3C2410_DMALOAD_1RUNNING) { - s3c2410_dma_loadbuffer(chan, chan->next); - } - } - - local_irq_restore(flags); return 0; } -/* s3c2410_dma_canload - * - * work out if we can queue another buffer into the DMA engine -*/ - -static int -s3c2410_dma_canload(struct s3c2410_dma_chan *chan) -{ - if (chan->load_state == S3C2410_DMALOAD_NONE || - chan->load_state == S3C2410_DMALOAD_1RUNNING) - return 1; - - return 0; -} - /* s3c2410_dma_enqueue * * queue an given buffer for dma transfer. @@ -462,47 +327,19 @@ int s3c2410_dma_enqueue(unsigned int channel, void *id, local_irq_save(flags); - if (chan->curr == NULL) { - /* we've got nothing loaded... */ - pr_debug("%s: buffer %p queued onto empty channel\n", - __func__, buf); - - chan->curr = buf; - chan->end = buf; - chan->next = NULL; + if (chan->end == NULL) { + pr_debug("dma%d: queued buffer onto empty channel\n", + chan->number); + chan->next = buf; + chan->end = buf; } else { - pr_debug("dma%d: %s: buffer %p queued onto non-empty channel\n", - chan->number, __func__, buf); - - if (chan->end == NULL) - pr_debug("dma%d: %s: %p not empty, and chan->end==NULL?\n", - chan->number, __func__, chan); - + pr_debug("dma%d: queued buffer onto non-empty channel\n", + chan->number); chan->end->next = buf; chan->end = buf; } - /* if necessary, update the next buffer field */ - if (chan->next == NULL) - chan->next = buf; - - /* check to see if we can load a buffer */ - if (chan->state == S3C2410_DMA_RUNNING) { - if (chan->load_state == S3C2410_DMALOAD_1LOADED && 1) { - if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { - printk(KERN_ERR "dma%d: loadbuffer:" - "timeout loading buffer\n", - chan->number); - dbg_showchan(chan); - local_irq_restore(flags); - return -EINVAL; - } - } - - while (s3c2410_dma_canload(chan) && chan->next != NULL) { - s3c2410_dma_loadbuffer(chan, chan->next); - } - } else if (chan->state == S3C2410_DMA_IDLE) { + if (chan->state == S3C2410_DMA_IDLE) { if (chan->flags & S3C2410_DMAF_AUTOSTART) { s3c2410_dma_ctrl(chan->number | DMACH_LOW_LEVEL, S3C2410_DMAOP_START); @@ -529,51 +366,6 @@ s3c2410_dma_freebuf(struct s3c2410_dma_buf *buf) } } -/* s3c2410_dma_lastxfer - * - * called when the system is out of buffers, to ensure that the channel - * is prepared for shutdown. -*/ - -static inline void -s3c2410_dma_lastxfer(struct s3c2410_dma_chan *chan) -{ -#if 0 - pr_debug("dma%d: s3c2410_dma_lastxfer: load_state %d\n", - chan->number, chan->load_state); -#endif - - switch (chan->load_state) { - case S3C2410_DMALOAD_NONE: - 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; - } - break; - - case S3C2410_DMALOAD_1LOADED_1RUNNING: - /* I belive in this case we do not have anything to do - * until the next buffer comes along, and we turn off the - * reload */ - return; - - default: - pr_debug("dma%d: lastxfer: unhandled load_state %d with no next\n", - chan->number, chan->load_state); - return; - - } - - /* hopefully this'll shut the damned thing up after the transfer... */ - dma_wrreg(chan, S3C2410_DMA_DCON, chan->dcon | S3C2410_DCON_NORELOAD); -} - - #define dmadbg2(x...) static irqreturn_t @@ -582,57 +374,25 @@ s3c2410_dma_irq(int irq, void *devpw) struct s3c2410_dma_chan *chan = (struct s3c2410_dma_chan *)devpw; struct s3c2410_dma_buf *buf; + /* Check for orphaned irq */ + if (chan->state == S3C2410_DMA_IDLE) + return IRQ_HANDLED; + 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. - */ - + if (chan->load_state == S3C2410_DMALOAD_1RUNNING) 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: + else 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; - } + chan->number); 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; + chan->curr = NULL; + chan->next = buf->next; buf->next = NULL; if (buf->magic != BUF_MAGIC) { @@ -640,12 +400,14 @@ s3c2410_dma_irq(int irq, void *devpw) chan->number, __func__, buf); return IRQ_HANDLED; } - + pr_debug("dma%d: transfer of size %d took %u ms\n", + chan->number, + buf->size, + jiffies_to_msecs(jiffies - buf->timestamp)); 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 @@ -655,53 +417,36 @@ s3c2410_dma_irq(int irq, void *devpw) /* 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; - } + if (chan->next != NULL) { + if (chan->state != S3C2410_DMA_IDLE) { + unsigned long flags; + unsigned long tmp; - local_irq_save(flags); - s3c2410_dma_loadbuffer(chan, chan->next); - local_irq_restore(flags); + pr_debug("%s: dma%d: continuing with next buffer\n", + __func__, chan->number); + local_irq_save(flags); + s3c2410_dma_loadbuffer(chan, chan->next); + tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); + tmp &= ~S3C2410_DMASKTRIG_STOP; + tmp |= S3C2410_DMASKTRIG_ON; + dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp); + local_irq_restore(flags); + } else + pr_debug("dma%d: buffdone callback stopped dma...\n", + chan->number); } else { - s3c2410_dma_lastxfer(chan); + /* No more buffers? So no queue */ + chan->end = NULL; /* 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", + if (chan->state != S3C2410_DMA_IDLE) { + pr_debug("dma%d: end of transfer, stopping channel (%lu)\n", chan->number, jiffies); s3c2410_dma_ctrl(chan->number | DMACH_LOW_LEVEL, S3C2410_DMAOP_STOP); } } - no_load: return IRQ_HANDLED; } @@ -840,9 +585,20 @@ static int s3c2410_dma_dostop(struct s3c2410_dma_chan *chan) s3c2410_dma_call_op(chan, S3C2410_DMAOP_STOP); tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); - tmp |= S3C2410_DMASKTRIG_STOP; - //tmp &= ~S3C2410_DMASKTRIG_ON; - dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp); + if (tmp & S3C2410_DMASKTRIG_ON) { + int retries = 1000; + tmp |= S3C2410_DMASKTRIG_STOP; + dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp); + + while (--retries) { + tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); + if (!(tmp & S3C2410_DMASKTRIG_ON)) + break; + } + + if (!retries) + pr_debug("dma%d: failed to stop??\n", chan->number); + } #if 0 /* should also clear interrupts, according to WinCE BSP */ @@ -860,22 +616,6 @@ static int s3c2410_dma_dostop(struct s3c2410_dma_chan *chan) return 0; } -static void s3c2410_dma_waitforstop(struct s3c2410_dma_chan *chan) -{ - unsigned long tmp; - unsigned int timeout = 0x10000; - - while (timeout-- > 0) { - tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); - - if (!(tmp & S3C2410_DMASKTRIG_ON)) - return; - } - - pr_debug("dma%d: failed to stop?\n", chan->number); -} - - /* s3c2410_dma_flush * * stop the channel, and remove all current and pending transfers @@ -917,8 +657,6 @@ static int s3c2410_dma_flush(struct s3c2410_dma_chan *chan) dbg_showregs(chan); - s3c2410_dma_waitforstop(chan); - #if 0 /* should also clear interrupts, according to WinCE BSP */ { @@ -939,38 +677,8 @@ static int s3c2410_dma_flush(struct s3c2410_dma_chan *chan) static int s3c2410_dma_started(struct s3c2410_dma_chan *chan) { - unsigned long flags; - - local_irq_save(flags); - - dbg_showchan(chan); - - /* if we've only loaded one buffer onto the channel, then chec - * to see if we have another, and if so, try and load it so when - * the first buffer is finished, the new one will be loaded onto - * the channel */ - - if (chan->next != NULL) { - if (chan->load_state == S3C2410_DMALOAD_1LOADED) { - - if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { - pr_debug("%s: buff not yet loaded, no more todo\n", - __func__); - } else { - chan->load_state = S3C2410_DMALOAD_1RUNNING; - s3c2410_dma_loadbuffer(chan, chan->next); - } - - } else if (chan->load_state == S3C2410_DMALOAD_1RUNNING) { - s3c2410_dma_loadbuffer(chan, chan->next); - } - } - - - local_irq_restore(flags); - + /* Do nothing */ return 0; - } int @@ -1045,16 +753,12 @@ int s3c2410_dma_config(unsigned int channel, case DMACH_PCM_IN: case DMACH_PCM_OUT: case DMACH_MIC_IN: + case DMACH_SDI: default: dcon |= S3C2410_DCON_HANDSHAKE; dcon |= S3C2410_DCON_SYNC_PCLK; break; - case DMACH_SDI: - /* note, ensure if need HANDSHAKE or not */ - dcon |= S3C2410_DCON_SYNC_PCLK; - break; - case DMACH_XD0: case DMACH_XD1: dcon |= S3C2410_DCON_HANDSHAKE; -- 1.7.2 From mboxrd@z Thu Jan 1 00:00:00 1970 From: anarsoul@gmail.com (Vasily Khoruzhick) Date: Wed, 18 Aug 2010 18:04:25 +0300 Subject: [PATCH 1/2] s3c24xx: DMA: don't use autoreload feature In-Reply-To: <1282143866-8068-1-git-send-email-anarsoul@gmail.com> References: <1282143866-8068-1-git-send-email-anarsoul@gmail.com> Message-ID: <1282143866-8068-2-git-send-email-anarsoul@gmail.com> To: linux-arm-kernel@lists.infradead.org List-Id: linux-arm-kernel.lists.infradead.org Some integrated DMA-capable hardware doesn't like autoreload feature of s3c24xx DMA-engine, that's why s3cmci driver didn't work with DMA transfers enabled. I rewrote DMA driver not to use autoreload feature and removed all pre-loading features. Buffer re-load is fast enought to perform it in IRQ handler, and anyway I don't see any reason to waste CPU cycles on waiting for buffer load. Driver is much simplier now, it was tested with s3cmci and s3c24xx-i2s drivers on s3c2442 and s3c2410 SoCs and works just nice. Signed-off-by: Vasily Khoruzhick --- arch/arm/mach-s3c2410/include/mach/dma.h | 13 +- arch/arm/plat-s3c24xx/dma.c | 426 +++++------------------------- 2 files changed, 68 insertions(+), 371 deletions(-) diff --git a/arch/arm/mach-s3c2410/include/mach/dma.h b/arch/arm/mach-s3c2410/include/mach/dma.h index cf68136..198826f 100644 --- a/arch/arm/mach-s3c2410/include/mach/dma.h +++ b/arch/arm/mach-s3c2410/include/mach/dma.h @@ -79,16 +79,9 @@ enum s3c2410_dma_state { * * There are no buffers loaded (the channel should be inactive) * - * S3C2410_DMA_1LOADED - * - * There is one buffer loaded, however it has not been confirmed to be - * loaded by the DMA engine. This may be because the channel is not - * yet running, or the DMA driver decided that it was too costly to - * sit and wait for it to happen. - * * S3C2410_DMA_1RUNNING * - * The buffer has been confirmed running, and not finisged + * The buffer has been confirmed running, and not finished * * S3C2410_DMA_1LOADED_1RUNNING * @@ -98,9 +91,7 @@ enum s3c2410_dma_state { enum s3c2410_dma_loadst { S3C2410_DMALOAD_NONE, - S3C2410_DMALOAD_1LOADED, S3C2410_DMALOAD_1RUNNING, - S3C2410_DMALOAD_1LOADED_1RUNNING, }; @@ -129,6 +120,7 @@ struct s3c2410_dma_buf { dma_addr_t data; /* start of DMA data */ dma_addr_t ptr; /* where the DMA got to [1] */ void *id; /* client's id */ + unsigned int timestamp; }; /* [1] is this updated for both recv/send modes? */ @@ -189,6 +181,7 @@ struct s3c2410_dma_chan { struct s3c2410_dma_buf *curr; /* current dma buffer */ struct s3c2410_dma_buf *next; /* next buffer to load */ struct s3c2410_dma_buf *end; /* end of queue */ + spinlock_t queue_lock; /* system device */ struct sys_device dev; diff --git a/arch/arm/plat-s3c24xx/dma.c b/arch/arm/plat-s3c24xx/dma.c index 6ad274e..926fc9e 100644 --- a/arch/arm/plat-s3c24xx/dma.c +++ b/arch/arm/plat-s3c24xx/dma.c @@ -133,70 +133,6 @@ dmadbg_showregs(const char *fname, int line, struct s3c2410_dma_chan *chan) #define dbg_showchan(chan) do { } while(0) #endif /* CONFIG_S3C2410_DMA_DEBUG */ -/* s3c2410_dma_stats_timeout - * - * Update DMA stats from timeout info -*/ - -static void -s3c2410_dma_stats_timeout(struct s3c2410_dma_stats *stats, int val) -{ - if (stats == NULL) - return; - - if (val > stats->timeout_longest) - stats->timeout_longest = val; - if (val < stats->timeout_shortest) - stats->timeout_shortest = val; - - stats->timeout_avg += val; -} - -/* s3c2410_dma_waitforload - * - * wait for the DMA engine to load a buffer, and update the state accordingly -*/ - -static int -s3c2410_dma_waitforload(struct s3c2410_dma_chan *chan, int line) -{ - int timeout = chan->load_timeout; - int took; - - if (chan->load_state != S3C2410_DMALOAD_1LOADED) { - printk(KERN_ERR "dma%d: s3c2410_dma_waitforload() called in loadstate %d from line %d\n", chan->number, chan->load_state, line); - return 0; - } - - if (chan->stats != NULL) - chan->stats->loads++; - - while (--timeout > 0) { - if ((dma_rdreg(chan, S3C2410_DMA_DSTAT) << (32-20)) != 0) { - took = chan->load_timeout - timeout; - - s3c2410_dma_stats_timeout(chan->stats, took); - - switch (chan->load_state) { - case S3C2410_DMALOAD_1LOADED: - chan->load_state = S3C2410_DMALOAD_1RUNNING; - break; - - default: - printk(KERN_ERR "dma%d: unknown load_state in s3c2410_dma_waitforload() %d\n", chan->number, chan->load_state); - } - - return 1; - } - } - - if (chan->stats != NULL) { - chan->stats->timeout_failed++; - } - - return 0; -} - /* s3c2410_dma_loadbuffer * * load a buffer, and update the channel state @@ -206,8 +142,6 @@ 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; @@ -218,26 +152,9 @@ s3c2410_dma_loadbuffer(struct s3c2410_dma_chan *chan, /* 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 (chan->load_state != S3C2410_DMALOAD_NONE) + printk(KERN_ERR "dma%d: channel already has buffer loaded\n", + chan->number); if ((buf->data & 0xf0000000) != 0x30000000) { dmawarn("dmaload: buffer is %p\n", (void *)buf->data); @@ -246,26 +163,15 @@ s3c2410_dma_loadbuffer(struct s3c2410_dma_chan *chan, writel(buf->data, chan->addr_reg); dma_wrreg(chan, S3C2410_DMA_DCON, - chan->dcon | reload | (buf->size/chan->xfer_unit)); + chan->dcon | S3C2410_DCON_NORELOAD | + (buf->size/chan->xfer_unit)); - chan->next = buf->next; + chan->curr = buf; /* update the state of the channel */ + chan->load_state = S3C2410_DMALOAD_1RUNNING; - 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; - } + buf->timestamp = jiffies; return 0; } @@ -345,7 +251,6 @@ static int s3c2410_dma_start(struct s3c2410_dma_chan *chan) dbg_showchan(chan); /* enable the channel */ - if (!chan->irq_enabled) { enable_irq(chan->irq); chan->irq_enabled = 1; @@ -360,14 +265,6 @@ static int s3c2410_dma_start(struct s3c2410_dma_chan *chan) 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); @@ -377,43 +274,11 @@ static int s3c2410_dma_start(struct s3c2410_dma_chan *chan) * the first buffer is finished, the new one will be loaded onto * the channel */ - if (chan->next != NULL) { - if (chan->load_state == S3C2410_DMALOAD_1LOADED) { - - if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { - pr_debug("%s: buff not yet loaded, no more todo\n", - __func__); - } else { - chan->load_state = S3C2410_DMALOAD_1RUNNING; - s3c2410_dma_loadbuffer(chan, chan->next); - } - - } else if (chan->load_state == S3C2410_DMALOAD_1RUNNING) { - s3c2410_dma_loadbuffer(chan, chan->next); - } - } - - local_irq_restore(flags); return 0; } -/* s3c2410_dma_canload - * - * work out if we can queue another buffer into the DMA engine -*/ - -static int -s3c2410_dma_canload(struct s3c2410_dma_chan *chan) -{ - if (chan->load_state == S3C2410_DMALOAD_NONE || - chan->load_state == S3C2410_DMALOAD_1RUNNING) - return 1; - - return 0; -} - /* s3c2410_dma_enqueue * * queue an given buffer for dma transfer. @@ -462,47 +327,19 @@ int s3c2410_dma_enqueue(unsigned int channel, void *id, local_irq_save(flags); - if (chan->curr == NULL) { - /* we've got nothing loaded... */ - pr_debug("%s: buffer %p queued onto empty channel\n", - __func__, buf); - - chan->curr = buf; - chan->end = buf; - chan->next = NULL; + if (chan->end == NULL) { + pr_debug("dma%d: queued buffer onto empty channel\n", + chan->number); + chan->next = buf; + chan->end = buf; } else { - pr_debug("dma%d: %s: buffer %p queued onto non-empty channel\n", - chan->number, __func__, buf); - - if (chan->end == NULL) - pr_debug("dma%d: %s: %p not empty, and chan->end==NULL?\n", - chan->number, __func__, chan); - + pr_debug("dma%d: queued buffer onto non-empty channel\n", + chan->number); chan->end->next = buf; chan->end = buf; } - /* if necessary, update the next buffer field */ - if (chan->next == NULL) - chan->next = buf; - - /* check to see if we can load a buffer */ - if (chan->state == S3C2410_DMA_RUNNING) { - if (chan->load_state == S3C2410_DMALOAD_1LOADED && 1) { - if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { - printk(KERN_ERR "dma%d: loadbuffer:" - "timeout loading buffer\n", - chan->number); - dbg_showchan(chan); - local_irq_restore(flags); - return -EINVAL; - } - } - - while (s3c2410_dma_canload(chan) && chan->next != NULL) { - s3c2410_dma_loadbuffer(chan, chan->next); - } - } else if (chan->state == S3C2410_DMA_IDLE) { + if (chan->state == S3C2410_DMA_IDLE) { if (chan->flags & S3C2410_DMAF_AUTOSTART) { s3c2410_dma_ctrl(chan->number | DMACH_LOW_LEVEL, S3C2410_DMAOP_START); @@ -529,51 +366,6 @@ s3c2410_dma_freebuf(struct s3c2410_dma_buf *buf) } } -/* s3c2410_dma_lastxfer - * - * called when the system is out of buffers, to ensure that the channel - * is prepared for shutdown. -*/ - -static inline void -s3c2410_dma_lastxfer(struct s3c2410_dma_chan *chan) -{ -#if 0 - pr_debug("dma%d: s3c2410_dma_lastxfer: load_state %d\n", - chan->number, chan->load_state); -#endif - - switch (chan->load_state) { - case S3C2410_DMALOAD_NONE: - 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; - } - break; - - case S3C2410_DMALOAD_1LOADED_1RUNNING: - /* I belive in this case we do not have anything to do - * until the next buffer comes along, and we turn off the - * reload */ - return; - - default: - pr_debug("dma%d: lastxfer: unhandled load_state %d with no next\n", - chan->number, chan->load_state); - return; - - } - - /* hopefully this'll shut the damned thing up after the transfer... */ - dma_wrreg(chan, S3C2410_DMA_DCON, chan->dcon | S3C2410_DCON_NORELOAD); -} - - #define dmadbg2(x...) static irqreturn_t @@ -582,57 +374,25 @@ s3c2410_dma_irq(int irq, void *devpw) struct s3c2410_dma_chan *chan = (struct s3c2410_dma_chan *)devpw; struct s3c2410_dma_buf *buf; + /* Check for orphaned irq */ + if (chan->state == S3C2410_DMA_IDLE) + return IRQ_HANDLED; + 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. - */ - + if (chan->load_state == S3C2410_DMALOAD_1RUNNING) 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: + else 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; - } + chan->number); 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; + chan->curr = NULL; + chan->next = buf->next; buf->next = NULL; if (buf->magic != BUF_MAGIC) { @@ -640,12 +400,14 @@ s3c2410_dma_irq(int irq, void *devpw) chan->number, __func__, buf); return IRQ_HANDLED; } - + pr_debug("dma%d: transfer of size %d took %u ms\n", + chan->number, + buf->size, + jiffies_to_msecs(jiffies - buf->timestamp)); 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 @@ -655,53 +417,36 @@ s3c2410_dma_irq(int irq, void *devpw) /* 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; - } + if (chan->next != NULL) { + if (chan->state != S3C2410_DMA_IDLE) { + unsigned long flags; + unsigned long tmp; - local_irq_save(flags); - s3c2410_dma_loadbuffer(chan, chan->next); - local_irq_restore(flags); + pr_debug("%s: dma%d: continuing with next buffer\n", + __func__, chan->number); + local_irq_save(flags); + s3c2410_dma_loadbuffer(chan, chan->next); + tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); + tmp &= ~S3C2410_DMASKTRIG_STOP; + tmp |= S3C2410_DMASKTRIG_ON; + dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp); + local_irq_restore(flags); + } else + pr_debug("dma%d: buffdone callback stopped dma...\n", + chan->number); } else { - s3c2410_dma_lastxfer(chan); + /* No more buffers? So no queue */ + chan->end = NULL; /* 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", + if (chan->state != S3C2410_DMA_IDLE) { + pr_debug("dma%d: end of transfer, stopping channel (%lu)\n", chan->number, jiffies); s3c2410_dma_ctrl(chan->number | DMACH_LOW_LEVEL, S3C2410_DMAOP_STOP); } } - no_load: return IRQ_HANDLED; } @@ -840,9 +585,20 @@ static int s3c2410_dma_dostop(struct s3c2410_dma_chan *chan) s3c2410_dma_call_op(chan, S3C2410_DMAOP_STOP); tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); - tmp |= S3C2410_DMASKTRIG_STOP; - //tmp &= ~S3C2410_DMASKTRIG_ON; - dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp); + if (tmp & S3C2410_DMASKTRIG_ON) { + int retries = 1000; + tmp |= S3C2410_DMASKTRIG_STOP; + dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp); + + while (--retries) { + tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); + if (!(tmp & S3C2410_DMASKTRIG_ON)) + break; + } + + if (!retries) + pr_debug("dma%d: failed to stop??\n", chan->number); + } #if 0 /* should also clear interrupts, according to WinCE BSP */ @@ -860,22 +616,6 @@ static int s3c2410_dma_dostop(struct s3c2410_dma_chan *chan) return 0; } -static void s3c2410_dma_waitforstop(struct s3c2410_dma_chan *chan) -{ - unsigned long tmp; - unsigned int timeout = 0x10000; - - while (timeout-- > 0) { - tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); - - if (!(tmp & S3C2410_DMASKTRIG_ON)) - return; - } - - pr_debug("dma%d: failed to stop?\n", chan->number); -} - - /* s3c2410_dma_flush * * stop the channel, and remove all current and pending transfers @@ -917,8 +657,6 @@ static int s3c2410_dma_flush(struct s3c2410_dma_chan *chan) dbg_showregs(chan); - s3c2410_dma_waitforstop(chan); - #if 0 /* should also clear interrupts, according to WinCE BSP */ { @@ -939,38 +677,8 @@ static int s3c2410_dma_flush(struct s3c2410_dma_chan *chan) static int s3c2410_dma_started(struct s3c2410_dma_chan *chan) { - unsigned long flags; - - local_irq_save(flags); - - dbg_showchan(chan); - - /* if we've only loaded one buffer onto the channel, then chec - * to see if we have another, and if so, try and load it so when - * the first buffer is finished, the new one will be loaded onto - * the channel */ - - if (chan->next != NULL) { - if (chan->load_state == S3C2410_DMALOAD_1LOADED) { - - if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { - pr_debug("%s: buff not yet loaded, no more todo\n", - __func__); - } else { - chan->load_state = S3C2410_DMALOAD_1RUNNING; - s3c2410_dma_loadbuffer(chan, chan->next); - } - - } else if (chan->load_state == S3C2410_DMALOAD_1RUNNING) { - s3c2410_dma_loadbuffer(chan, chan->next); - } - } - - - local_irq_restore(flags); - + /* Do nothing */ return 0; - } int @@ -1045,16 +753,12 @@ int s3c2410_dma_config(unsigned int channel, case DMACH_PCM_IN: case DMACH_PCM_OUT: case DMACH_MIC_IN: + case DMACH_SDI: default: dcon |= S3C2410_DCON_HANDSHAKE; dcon |= S3C2410_DCON_SYNC_PCLK; break; - case DMACH_SDI: - /* note, ensure if need HANDSHAKE or not */ - dcon |= S3C2410_DCON_SYNC_PCLK; - break; - case DMACH_XD0: case DMACH_XD1: dcon |= S3C2410_DCON_HANDSHAKE; -- 1.7.2