diff options
Diffstat (limited to 'sound')
-rw-r--r-- | 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 fbe3da73eaff..1a314fa99c45 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c | |||
@@ -226,6 +226,7 @@ struct es1938 { | |||
226 | unsigned int dma2_start; | 226 | unsigned int dma2_start; |
227 | unsigned int dma1_shift; | 227 | unsigned int dma1_shift; |
228 | unsigned int dma2_shift; | 228 | unsigned int dma2_shift; |
229 | unsigned int last_capture_dmaaddr; | ||
229 | unsigned int active; | 230 | unsigned int active; |
230 | 231 | ||
231 | spinlock_t reg_lock; | 232 | spinlock_t reg_lock; |
@@ -528,6 +529,7 @@ static void snd_es1938_capture_setdma(struct es1938 *chip) | |||
528 | outb(1, SLDM_REG(chip, DMAMASK)); | 529 | outb(1, SLDM_REG(chip, DMAMASK)); |
529 | outb(0x14, SLDM_REG(chip, DMAMODE)); | 530 | outb(0x14, SLDM_REG(chip, DMAMODE)); |
530 | 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; | ||
531 | outw(chip->dma1_size - 1, SLDM_REG(chip, DMACOUNT)); | 533 | outw(chip->dma1_size - 1, SLDM_REG(chip, DMACOUNT)); |
532 | /* 3. Unmask DMA */ | 534 | /* 3. Unmask DMA */ |
533 | outb(0, SLDM_REG(chip, DMAMASK)); | 535 | outb(0, SLDM_REG(chip, DMAMASK)); |
@@ -769,19 +771,40 @@ static int snd_es1938_playback_prepare(struct snd_pcm_substream *substream) | |||
769 | return -EINVAL; | 771 | return -EINVAL; |
770 | } | 772 | } |
771 | 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 | */ | ||
772 | static snd_pcm_uframes_t snd_es1938_capture_pointer(struct snd_pcm_substream *substream) | 782 | static snd_pcm_uframes_t snd_es1938_capture_pointer(struct snd_pcm_substream *substream) |
773 | { | 783 | { |
774 | struct es1938 *chip = snd_pcm_substream_chip(substream); | 784 | struct es1938 *chip = snd_pcm_substream_chip(substream); |
775 | size_t ptr; | 785 | size_t ptr; |
786 | #if 0 | ||
776 | size_t old, new; | 787 | size_t old, new; |
777 | #if 1 | ||
778 | /* This stuff is *needed*, don't ask why - AB */ | 788 | /* This stuff is *needed*, don't ask why - AB */ |
779 | old = inw(SLDM_REG(chip, DMACOUNT)); | 789 | old = inw(SLDM_REG(chip, DMACOUNT)); |
780 | while ((new = inw(SLDM_REG(chip, DMACOUNT))) != old) | 790 | while ((new = inw(SLDM_REG(chip, DMACOUNT))) != old) |
781 | old = new; | 791 | old = new; |
782 | ptr = chip->dma1_size - 1 - new; | 792 | ptr = chip->dma1_size - 1 - new; |
783 | #else | 793 | #else |
784 | 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; | ||
785 | #endif | 808 | #endif |
786 | return ptr >> chip->dma1_shift; | 809 | return ptr >> chip->dma1_shift; |
787 | } | 810 | } |