Skip to content

Commit

Permalink
[ALSA] es1938 - improve capture hw pointer reads
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
hlauer0 authored and Mercurial server committed Jan 31, 2008
1 parent 4979bca commit 19e2e3c
Showing 1 changed file with 25 additions and 2 deletions.
27 changes: 25 additions & 2 deletions sound/pci/es1938.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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;
}
Expand Down

0 comments on commit 19e2e3c

Please sign in to comment.