diff options
Diffstat (limited to 'sound/core/pcm_lib.c')
| -rw-r--r-- | sound/core/pcm_lib.c | 47 | 
1 files changed, 39 insertions, 8 deletions
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index fbb2e391591..63d088f2265 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -209,9 +209,11 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)  {  	struct snd_pcm_runtime *runtime = substream->runtime;  	snd_pcm_uframes_t pos; -	snd_pcm_uframes_t new_hw_ptr, hw_ptr_interrupt, hw_base; -	snd_pcm_sframes_t delta; +	snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_ptr_interrupt, hw_base; +	snd_pcm_sframes_t hdelta, delta; +	unsigned long jdelta; +	old_hw_ptr = runtime->status->hw_ptr;  	pos = snd_pcm_update_hw_ptr_pos(substream, runtime);  	if (pos == SNDRV_PCM_POS_XRUN) {  		xrun(substream); @@ -247,7 +249,30 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)  			new_hw_ptr = hw_base + pos;  		}  	} -	if (delta > runtime->period_size) { +	hdelta = new_hw_ptr - old_hw_ptr; +	jdelta = jiffies - runtime->hw_ptr_jiffies; +	if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) { +		delta = jdelta / +			(((runtime->period_size * HZ) / runtime->rate) +								+ HZ/100); +		hw_ptr_error(substream, +			     "hw_ptr skipping! [Q] " +			     "(pos=%ld, delta=%ld, period=%ld, " +			     "jdelta=%lu/%lu/%lu)\n", +			     (long)pos, (long)hdelta, +			     (long)runtime->period_size, jdelta, +			     ((hdelta * HZ) / runtime->rate), delta); +		hw_ptr_interrupt = runtime->hw_ptr_interrupt + +				   runtime->period_size * delta; +		if (hw_ptr_interrupt >= runtime->boundary) +			hw_ptr_interrupt -= runtime->boundary; +		/* rebase to interrupt position */ +		hw_base = new_hw_ptr = hw_ptr_interrupt; +		/* align hw_base to buffer_size */ +		hw_base -= hw_base % runtime->buffer_size; +		delta = 0; +	} +	if (delta > runtime->period_size + runtime->period_size / 2) {  		hw_ptr_error(substream,  			     "Lost interrupts? "  			     "(stream=%i, delta=%ld, intr_ptr=%ld)\n", @@ -263,6 +288,7 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)  	runtime->hw_ptr_base = hw_base;  	runtime->status->hw_ptr = new_hw_ptr; +	runtime->hw_ptr_jiffies = jiffies;  	runtime->hw_ptr_interrupt = hw_ptr_interrupt;  	return snd_pcm_update_hw_ptr_post(substream, runtime); @@ -275,6 +301,7 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)  	snd_pcm_uframes_t pos;  	snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base;  	snd_pcm_sframes_t delta; +	unsigned long jdelta;  	old_hw_ptr = runtime->status->hw_ptr;  	pos = snd_pcm_update_hw_ptr_pos(substream, runtime); @@ -286,14 +313,15 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)  	new_hw_ptr = hw_base + pos;  	delta = new_hw_ptr - old_hw_ptr; +	jdelta = jiffies - runtime->hw_ptr_jiffies;  	if (delta < 0) {  		delta += runtime->buffer_size;  		if (delta < 0) {  			hw_ptr_error(substream,   				     "Unexpected hw_pointer value [2] " -				     "(stream=%i, pos=%ld, old_ptr=%ld)\n", +				     "(stream=%i, pos=%ld, old_ptr=%ld, jdelta=%li)\n",  				     substream->stream, (long)pos, -				     (long)old_hw_ptr); +				     (long)old_hw_ptr, jdelta);  			return 0;  		}  		hw_base += runtime->buffer_size; @@ -301,12 +329,13 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)  			hw_base = 0;  		new_hw_ptr = hw_base + pos;  	} -	if (delta > runtime->period_size && runtime->periods > 1) { +	if (((delta * HZ) / runtime->rate) > jdelta + HZ/100) {  		hw_ptr_error(substream,  			     "hw_ptr skipping! " -			     "(pos=%ld, delta=%ld, period=%ld)\n", +			     "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu)\n",  			     (long)pos, (long)delta, -			     (long)runtime->period_size); +			     (long)runtime->period_size, jdelta, +			     ((delta * HZ) / runtime->rate));  		return 0;  	}  	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && @@ -315,6 +344,7 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)  	runtime->hw_ptr_base = hw_base;  	runtime->status->hw_ptr = new_hw_ptr; +	runtime->hw_ptr_jiffies = jiffies;  	return snd_pcm_update_hw_ptr_post(substream, runtime);  } @@ -1441,6 +1471,7 @@ static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream,  		runtime->status->hw_ptr %= runtime->buffer_size;  	else  		runtime->status->hw_ptr = 0; +	runtime->hw_ptr_jiffies = jiffies;  	snd_pcm_stream_unlock_irqrestore(substream, flags);  	return 0;  }  | 
