[alsa-devel] es1938 - patch trying to improve capture hw pointer reads
Hermann.Lauer at iwr.uni-heidelberg.de
Wed Jan 23 01:35:12 CET 2008
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. Appended
is a patch against linux-18.104.22.168 which 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.
On my machine xruns seems to be gone with that patch.
If anybody is interested, mmaped capture seems also to be doable with some tricks, still testing this...
Netzwerkadministration/Zentrale Dienste, Interdiziplinaeres
Zentrum fuer wissenschaftliches Rechnen der Universitaet Heidelberg
IWR; INF 368; 69120 Heidelberg; Tel: (06221)54-8236 Fax: -5224
Email: Hermann.Lauer at iwr.uni-heidelberg.de
-------------- next part --------------
--- linux-22.214.171.124/sound/pci/es1938.c.trydma 2008-01-14 23:46:57.000000000 +0100
+++ linux-126.96.36.199/sound/pci/es1938.c 2008-01-23 01:03:08.000000000 +0100
@@ -227,6 +227,8 @@
unsigned int dma2_start;
unsigned int dma1_shift;
unsigned int dma2_shift;
+ void* last_capture_dmaaddr;
unsigned int active;
@@ -775,14 +777,38 @@
struct es1938 *chip = snd_pcm_substream_chip(substream);
size_t old, new;
/* 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;
- ptr = inl(SLDM_REG(chip, DMAADDR)) - chip->dma1_start;
+ /* 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 implemention below allows for that.
+ size_t stat,count;
+ unsigned int diff;
+ struct snd_pcm_runtime *runtime;
+ runtime = substream->runtime;
+ ptr = inl(SLDM_REG(chip, DMAADDR));
+ count= inw(SLDM_REG(chip, DMACOUNT));
+ /* printk("Es1938debug - capture_pointer %x,diff=%x,fb=%x DMA count: %x, base: %x, status: %x\n",
+ ptr,diff,((unsigned char*)(runtime->dma_area)-1),count, inl(SLDM_REG(chip, DMAADDR)), inl(SLDM_REG(chip, DMASTATUS)));
+ if (diff>3 || ptr < chip->dma1_start
+ || ptr >= chip->dma1_start+chip->dma1_size)
+ ptr=chip->last_capture_dmaaddr; /* bad, use last saved instead */
+ else chip->last_capture_dmaaddr=ptr; /* good, remember it */
+ ptr -= chip->dma1_start;
return ptr >> chip->dma1_shift;
More information about the Alsa-devel