[alsa-devel] [PATCH] snd_dma_pointer workaround for chipsets with buggy DMA

Takashi Iwai tiwai at suse.de
Sun Oct 11 18:13:59 CEST 2009


At Sun, 11 Oct 2009 12:48:00 +0200,
Krzysztof Helt wrote:
> 
> From: Krzysztof Helt <krzysztof.h1 at wp.pl>
> 
> 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 at wp.pl>
> ---
> This issue can be tested with the pcm tool from the alsa-lib's test package. 
> The command "pcm -c 2 -b 350000 -p 5800" can easily trigger the original error
> every ten seconds on my system.
> This work around greatly reduces the underrun errors for ogg123 and mpg321 
> on VIA Apollo (586) chipset + K6-2 533 MHz (from every few seconds to one per about
> 15 minutes - few songs).

Applied now.  The only affecting change is the call of enable_dma(),
but this should be OK.


thanks,

Takashi

> 
> 
>  sound/core/isadma.c |   10 +++++++++-
>  1 files changed, 9 insertions(+), 1 deletions(-)
> 
> diff --git a/sound/core/isadma.c b/sound/core/isadma.c
> index 79f0f16..950e19b 100644
> --- a/sound/core/isadma.c
> +++ b/sound/core/isadma.c
> @@ -85,16 +85,24 @@ EXPORT_SYMBOL(snd_dma_disable);
>  unsigned int snd_dma_pointer(unsigned long dma, unsigned int size)
>  {
>  	unsigned long flags;
> -	unsigned int result;
> +	unsigned int result, result1;
>  
>  	flags = claim_dma_lock();
>  	clear_dma_ff(dma);
>  	if (!isa_dma_bridge_buggy)
>  		disable_dma(dma);
>  	result = get_dma_residue(dma);
> +	/*
> +	 * HACK - read the counter again and choose higher value in order to
> +	 * avoid reading during counter lower byte roll over if the
> +	 * isa_dma_bridge_buggy is set.
> +	 */
> +	result1 = get_dma_residue(dma);
>  	if (!isa_dma_bridge_buggy)
>  		enable_dma(dma);
>  	release_dma_lock(flags);
> +	if (unlikely(result < result1))
> +		result = result1;
>  #ifdef CONFIG_SND_DEBUG
>  	if (result > size)
>  		snd_printk(KERN_ERR "pointer (0x%x) for DMA #%ld is greater than transfer size (0x%x)\n", result, dma, size);
> -- 
> 1.6.0.3
> 
> 
> 
> ---------------------------------------------------------------
> Zobacz jak pracuje sie na wysokosciach.
> Kliknij >>> http://link.interia.pl/f2384
> 


More information about the Alsa-devel mailing list