aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/es1938.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci/es1938.c')
-rw-r--r--sound/pci/es1938.c28
1 files changed, 25 insertions, 3 deletions
diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c
index fb25abe68a02..1a314fa99c45 100644
--- a/sound/pci/es1938.c
+++ b/sound/pci/es1938.c
@@ -47,7 +47,6 @@
47*/ 47*/
48 48
49 49
50#include <sound/driver.h>
51#include <linux/init.h> 50#include <linux/init.h>
52#include <linux/interrupt.h> 51#include <linux/interrupt.h>
53#include <linux/pci.h> 52#include <linux/pci.h>
@@ -227,6 +226,7 @@ struct es1938 {
227 unsigned int dma2_start; 226 unsigned int dma2_start;
228 unsigned int dma1_shift; 227 unsigned int dma1_shift;
229 unsigned int dma2_shift; 228 unsigned int dma2_shift;
229 unsigned int last_capture_dmaaddr;
230 unsigned int active; 230 unsigned int active;
231 231
232 spinlock_t reg_lock; 232 spinlock_t reg_lock;
@@ -529,6 +529,7 @@ static void snd_es1938_capture_setdma(struct es1938 *chip)
529 outb(1, SLDM_REG(chip, DMAMASK)); 529 outb(1, SLDM_REG(chip, DMAMASK));
530 outb(0x14, SLDM_REG(chip, DMAMODE)); 530 outb(0x14, SLDM_REG(chip, DMAMODE));
531 outl(chip->dma1_start, SLDM_REG(chip, DMAADDR)); 531 outl(chip->dma1_start, SLDM_REG(chip, DMAADDR));
532 chip->last_capture_dmaaddr = chip->dma1_start;
532 outw(chip->dma1_size - 1, SLDM_REG(chip, DMACOUNT)); 533 outw(chip->dma1_size - 1, SLDM_REG(chip, DMACOUNT));
533 /* 3. Unmask DMA */ 534 /* 3. Unmask DMA */
534 outb(0, SLDM_REG(chip, DMAMASK)); 535 outb(0, SLDM_REG(chip, DMAMASK));
@@ -770,19 +771,40 @@ static int snd_es1938_playback_prepare(struct snd_pcm_substream *substream)
770 return -EINVAL; 771 return -EINVAL;
771} 772}
772 773
774/* during the incrementing of dma counters the DMA register reads sometimes
775 returns garbage. To ensure a valid hw pointer, the following checks which
776 should be very unlikely to fail are used:
777 - is the current DMA address in the valid DMA range ?
778 - is the sum of DMA address and DMA counter pointing to the last DMA byte ?
779 One can argue this could differ by one byte depending on which register is
780 updated first, so the implementation below allows for that.
781*/
773static snd_pcm_uframes_t snd_es1938_capture_pointer(struct snd_pcm_substream *substream) 782static snd_pcm_uframes_t snd_es1938_capture_pointer(struct snd_pcm_substream *substream)
774{ 783{
775 struct es1938 *chip = snd_pcm_substream_chip(substream); 784 struct es1938 *chip = snd_pcm_substream_chip(substream);
776 size_t ptr; 785 size_t ptr;
786#if 0
777 size_t old, new; 787 size_t old, new;
778#if 1
779 /* This stuff is *needed*, don't ask why - AB */ 788 /* This stuff is *needed*, don't ask why - AB */
780 old = inw(SLDM_REG(chip, DMACOUNT)); 789 old = inw(SLDM_REG(chip, DMACOUNT));
781 while ((new = inw(SLDM_REG(chip, DMACOUNT))) != old) 790 while ((new = inw(SLDM_REG(chip, DMACOUNT))) != old)
782 old = new; 791 old = new;
783 ptr = chip->dma1_size - 1 - new; 792 ptr = chip->dma1_size - 1 - new;
784#else 793#else
785 ptr = inl(SLDM_REG(chip, DMAADDR)) - chip->dma1_start; 794 size_t count;
795 unsigned int diff;
796
797 ptr = inl(SLDM_REG(chip, DMAADDR));
798 count = inw(SLDM_REG(chip, DMACOUNT));
799 diff = chip->dma1_start + chip->dma1_size - ptr - count;
800
801 if (diff > 3 || ptr < chip->dma1_start
802 || ptr >= chip->dma1_start+chip->dma1_size)
803 ptr = chip->last_capture_dmaaddr; /* bad, use last saved */
804 else
805 chip->last_capture_dmaaddr = ptr; /* good, remember it */
806
807 ptr -= chip->dma1_start;
786#endif 808#endif
787 return ptr >> chip->dma1_shift; 809 return ptr >> chip->dma1_shift;
788} 810}