]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - sound/pci/es1938.c
ALSA: hda - Show missing GPIO unsol bits
[linux-2.6-omap-h63xx.git] / sound / pci / es1938.c
index fbe3da73eaffa47373d2cfe804456f3186b67a7a..4cd9a1faaecc186732b1ad911ebfd74e3c59e77e 100644 (file)
@@ -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;
 }
@@ -837,7 +860,8 @@ static int snd_es1938_capture_copy(struct snd_pcm_substream *substream,
        struct es1938 *chip = snd_pcm_substream_chip(substream);
        pos <<= chip->dma1_shift;
        count <<= chip->dma1_shift;
-       snd_assert(pos + count <= chip->dma1_size, return -EINVAL);
+       if (snd_BUG_ON(pos + count > chip->dma1_size))
+               return -EINVAL;
        if (pos + count < chip->dma1_size) {
                if (copy_to_user(dst, runtime->dma_area + pos + 1, count))
                        return -EFAULT;
@@ -1465,7 +1489,6 @@ static int es1938_suspend(struct pci_dev *pci, pm_message_t state)
 
        outb(0x00, SLIO_REG(chip, IRQCONTROL)); /* disable irqs */
        if (chip->irq >= 0) {
-               synchronize_irq(chip->irq);
                free_irq(chip->irq, chip);
                chip->irq = -1;
        }
@@ -1555,10 +1578,8 @@ static int snd_es1938_free(struct es1938 *chip)
 
        snd_es1938_free_gameport(chip);
 
-       if (chip->irq >= 0) {
-               synchronize_irq(chip->irq);
+       if (chip->irq >= 0)
                free_irq(chip->irq, chip);
-       }
        pci_release_regions(chip->pci);
        pci_disable_device(chip->pci);
        kfree(chip);