aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHermann Lauer <Hermann.Lauer@iwr.uni-heidelberg.de>2008-01-30 02:25:13 -0500
committerMercurial server <hg@alsa0.alsa-project.org>2008-01-31 11:30:23 -0500
commit19e2e3c30485ba78a653dc521ed9e1f2b6a8bee1 (patch)
treecd68c33c408bddc8c9f5a6cc44ec2eaf0d5c019c
parent4979bca9dcfe4c21c26f378ce446c912fc583ac1 (diff)
[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 <Hermann.Lauer@iwr.uni-heidelberg.de> Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Jaroslav Kysela <perex@perex.cz>
-rw-r--r--sound/pci/es1938.c27
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*/
772static 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)
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}