|
@@ -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;
|
|
@@ -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));
|
|
@@ -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;
|
|
|
}
|