aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKrzysztof Helt <krzysztof.h1@wp.pl>2009-10-11 06:48:00 -0400
committerTakashi Iwai <tiwai@suse.de>2009-10-11 12:03:13 -0400
commit8066e51ae7329220f459470a38387f8533e99141 (patch)
treebc3ffe555efdbd35ac91fdacdf9a9972f7886b5d
parent633c7e92bdd54ba939f2bd3b78c72e1e1a1dd077 (diff)
ALSA: snd_dma_pointer workaround for chipsets with buggy DMA
The chipsets with the isa_dma_bridge_buggy set do not stop DMA during DMA counter reads. The DMA counter is read in two 8-bit read steps on x86 platform. Sometimes, such reads happen during higher byte change so the lower byte is already decremented (rolled over) but the higher byte is not. It introduces an error that position is moved 256 bytes ahead of the true position. Thus, the next DMA position read can return a lower value then the previous read. If the DMA position is decreased (reversed) the ALSA subsystem is tricked into the playback underrun error and resets the playback. It results in a "pop" during a playback. Work around the issue by reading the counter twice and choosing a higher value. Signed-off-by: Krzysztof Helt <krzysztof.h1@wp.pl> Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r--sound/core/isadma.c10
1 files changed, 9 insertions, 1 deletions
diff --git a/sound/core/isadma.c b/sound/core/isadma.c
index 79f0f16af339..950e19ba91fc 100644
--- a/sound/core/isadma.c
+++ b/sound/core/isadma.c
@@ -85,16 +85,24 @@ EXPORT_SYMBOL(snd_dma_disable);
85unsigned int snd_dma_pointer(unsigned long dma, unsigned int size) 85unsigned int snd_dma_pointer(unsigned long dma, unsigned int size)
86{ 86{
87 unsigned long flags; 87 unsigned long flags;
88 unsigned int result; 88 unsigned int result, result1;
89 89
90 flags = claim_dma_lock(); 90 flags = claim_dma_lock();
91 clear_dma_ff(dma); 91 clear_dma_ff(dma);
92 if (!isa_dma_bridge_buggy) 92 if (!isa_dma_bridge_buggy)
93 disable_dma(dma); 93 disable_dma(dma);
94 result = get_dma_residue(dma); 94 result = get_dma_residue(dma);
95 /*
96 * HACK - read the counter again and choose higher value in order to
97 * avoid reading during counter lower byte roll over if the
98 * isa_dma_bridge_buggy is set.
99 */
100 result1 = get_dma_residue(dma);
95 if (!isa_dma_bridge_buggy) 101 if (!isa_dma_bridge_buggy)
96 enable_dma(dma); 102 enable_dma(dma);
97 release_dma_lock(flags); 103 release_dma_lock(flags);
104 if (unlikely(result < result1))
105 result = result1;
98#ifdef CONFIG_SND_DEBUG 106#ifdef CONFIG_SND_DEBUG
99 if (result > size) 107 if (result > size)
100 snd_printk(KERN_ERR "pointer (0x%x) for DMA #%ld is greater than transfer size (0x%x)\n", result, dma, size); 108 snd_printk(KERN_ERR "pointer (0x%x) for DMA #%ld is greater than transfer size (0x%x)\n", result, dma, size);