Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=19e2e3c30485ba78a653dc521ed9e1f2b6a8bee1
Commit:     19e2e3c30485ba78a653dc521ed9e1f2b6a8bee1
Parent:     4979bca9dcfe4c21c26f378ce446c912fc583ac1
Author:     Hermann Lauer <[EMAIL PROTECTED]>
AuthorDate: Wed Jan 30 08:25:13 2008 +0100
Committer:  Mercurial server <[EMAIL PROTECTED]>
CommitDate: Thu Jan 31 17:30:23 2008 +0100

    [ALSA] es1938 - improve capture hw pointer reads
    
    With the Solo1 (es1938) I got a lot of xrun's during capture on my machine.
    Tracing that down it seems to be comming from reading ocassionaly bad hw
    pointers from the chip.  This patch uses more checking to avoid that false
    pointer reads.
    Failed reads are giving back the last good value read instead of spinning in
    a tight loop, which seems more appropriate to me in an interrupt. I think I
    saw this trick used in another driver
    
    Signed-off-by: Hermann Lauer <[EMAIL PROTECTED]>
    Signed-off-by: Takashi Iwai <[EMAIL PROTECTED]>
    Signed-off-by: Jaroslav Kysela <[EMAIL PROTECTED]>
---
 sound/pci/es1938.c |   27 +++++++++++++++++++++++++--
 1 files changed, 25 insertions(+), 2 deletions(-)

diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c
index fbe3da7..1a314fa 100644
--- a/sound/pci/es1938.c
+++ b/sound/pci/es1938.c
@@ -226,6 +226,7 @@ struct es1938 {
        unsigned int dma2_start;
        unsigned int dma1_shift;
        unsigned int dma2_shift;
+       unsigned int last_capture_dmaaddr;
        unsigned int active;
 
        spinlock_t reg_lock;
@@ -528,6 +529,7 @@ static void snd_es1938_capture_setdma(struct es1938 *chip)
        outb(1, SLDM_REG(chip, DMAMASK));
        outb(0x14, SLDM_REG(chip, DMAMODE));
        outl(chip->dma1_start, SLDM_REG(chip, DMAADDR));
+       chip->last_capture_dmaaddr = chip->dma1_start;
        outw(chip->dma1_size - 1, SLDM_REG(chip, DMACOUNT));
        /* 3. Unmask DMA */
        outb(0, SLDM_REG(chip, DMAMASK));
@@ -769,19 +771,40 @@ static int snd_es1938_playback_prepare(struct 
snd_pcm_substream *substream)
        return -EINVAL;
 }
 
+/* during the incrementing of dma counters the DMA register reads sometimes
+   returns garbage. To ensure a valid hw pointer, the following checks which
+   should be very unlikely to fail are used:
+   - is the current DMA address in the valid DMA range ?
+   - is the sum of DMA address and DMA counter pointing to the last DMA byte ?
+   One can argue this could differ by one byte depending on which register is
+   updated first, so the implementation below allows for that.
+*/
 static snd_pcm_uframes_t snd_es1938_capture_pointer(struct snd_pcm_substream 
*substream)
 {
        struct es1938 *chip = snd_pcm_substream_chip(substream);
        size_t ptr;
+#if 0
        size_t old, new;
-#if 1
        /* This stuff is *needed*, don't ask why - AB */
        old = inw(SLDM_REG(chip, DMACOUNT));
        while ((new = inw(SLDM_REG(chip, DMACOUNT))) != old)
                old = new;
        ptr = chip->dma1_size - 1 - new;
 #else
-       ptr = inl(SLDM_REG(chip, DMAADDR)) - chip->dma1_start;
+       size_t count;
+       unsigned int diff;
+
+       ptr = inl(SLDM_REG(chip, DMAADDR));
+       count = inw(SLDM_REG(chip, DMACOUNT));
+       diff = chip->dma1_start + chip->dma1_size - ptr - count;
+
+       if (diff > 3 || ptr < chip->dma1_start
+             || ptr >= chip->dma1_start+chip->dma1_size)
+         ptr = chip->last_capture_dmaaddr;            /* bad, use last saved */
+       else
+         chip->last_capture_dmaaddr = ptr;            /* good, remember it */
+
+       ptr -= chip->dma1_start;
 #endif
        return ptr >> chip->dma1_shift;
 }
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to