diff options
Diffstat (limited to 'sound/core/pcm_lib.c')
| -rw-r--r-- | sound/core/pcm_lib.c | 155 | 
1 files changed, 102 insertions, 53 deletions
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 921691080f35..fbb2e391591e 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -125,23 +125,32 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram  	}  } +#ifdef CONFIG_SND_PCM_XRUN_DEBUG +#define xrun_debug(substream)	((substream)->pstr->xrun_debug) +#else +#define xrun_debug(substream)	0 +#endif + +#define dump_stack_on_xrun(substream) do {	\ +		if (xrun_debug(substream) > 1)	\ +			dump_stack();		\ +	} while (0) +  static void xrun(struct snd_pcm_substream *substream)  {  	snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); -#ifdef CONFIG_SND_PCM_XRUN_DEBUG -	if (substream->pstr->xrun_debug) { +	if (xrun_debug(substream)) {  		snd_printd(KERN_DEBUG "XRUN: pcmC%dD%d%c\n",  			   substream->pcm->card->number,  			   substream->pcm->device,  			   substream->stream ? 'c' : 'p'); -		if (substream->pstr->xrun_debug > 1) -			dump_stack(); +		dump_stack_on_xrun(substream);  	} -#endif  } -static inline snd_pcm_uframes_t snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream, -							  struct snd_pcm_runtime *runtime) +static snd_pcm_uframes_t +snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream, +			  struct snd_pcm_runtime *runtime)  {  	snd_pcm_uframes_t pos; @@ -150,17 +159,21 @@ static inline snd_pcm_uframes_t snd_pcm_update_hw_ptr_pos(struct snd_pcm_substre  	pos = substream->ops->pointer(substream);  	if (pos == SNDRV_PCM_POS_XRUN)  		return pos; /* XRUN */ -#ifdef CONFIG_SND_DEBUG  	if (pos >= runtime->buffer_size) { -		snd_printk(KERN_ERR  "BUG: stream = %i, pos = 0x%lx, buffer size = 0x%lx, period size = 0x%lx\n", substream->stream, pos, runtime->buffer_size, runtime->period_size); +		if (printk_ratelimit()) { +			snd_printd(KERN_ERR  "BUG: stream = %i, pos = 0x%lx, " +				   "buffer size = 0x%lx, period size = 0x%lx\n", +				   substream->stream, pos, runtime->buffer_size, +				   runtime->period_size); +		} +		pos = 0;  	} -#endif  	pos -= pos % runtime->min_align;  	return pos;  } -static inline int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream, -					     struct snd_pcm_runtime *runtime) +static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream, +				      struct snd_pcm_runtime *runtime)  {  	snd_pcm_uframes_t avail; @@ -182,11 +195,21 @@ static inline int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream  	return 0;  } -static inline int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) +#define hw_ptr_error(substream, fmt, args...)				\ +	do {								\ +		if (xrun_debug(substream)) {				\ +			if (printk_ratelimit()) {			\ +				snd_printd("PCM: " fmt, ##args);	\ +			}						\ +			dump_stack_on_xrun(substream);			\ +		}							\ +	} while (0) + +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; +	snd_pcm_uframes_t new_hw_ptr, hw_ptr_interrupt, hw_base;  	snd_pcm_sframes_t delta;  	pos = snd_pcm_update_hw_ptr_pos(substream, runtime); @@ -194,36 +217,53 @@ static inline int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *subs  		xrun(substream);  		return -EPIPE;  	} -	if (runtime->period_size == runtime->buffer_size) -		goto __next_buf; -	new_hw_ptr = runtime->hw_ptr_base + pos; +	hw_base = runtime->hw_ptr_base; +	new_hw_ptr = hw_base + pos;  	hw_ptr_interrupt = runtime->hw_ptr_interrupt + runtime->period_size; - -	delta = hw_ptr_interrupt - new_hw_ptr; -	if (delta > 0) { -		if ((snd_pcm_uframes_t)delta < runtime->buffer_size / 2) { -#ifdef CONFIG_SND_PCM_XRUN_DEBUG -			if (runtime->periods > 1 && substream->pstr->xrun_debug) { -				snd_printd(KERN_ERR "Unexpected hw_pointer value [1] (stream = %i, delta: -%ld, max jitter = %ld): wrong interrupt acknowledge?\n", substream->stream, (long) delta, runtime->buffer_size / 2); -				if (substream->pstr->xrun_debug > 1) -					dump_stack(); -			} -#endif -			return 0; +	delta = new_hw_ptr - hw_ptr_interrupt; +	if (hw_ptr_interrupt >= runtime->boundary) { +		hw_ptr_interrupt -= runtime->boundary; +		if (hw_base < runtime->boundary / 2) +			/* hw_base was already lapped; recalc delta */ +			delta = new_hw_ptr - hw_ptr_interrupt; +	} +	if (delta < 0) { +		delta += runtime->buffer_size; +		if (delta < 0) { +			hw_ptr_error(substream,  +				     "Unexpected hw_pointer value " +				     "(stream=%i, pos=%ld, intr_ptr=%ld)\n", +				     substream->stream, (long)pos, +				     (long)hw_ptr_interrupt); +			/* 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; +		} else { +			hw_base += runtime->buffer_size; +			if (hw_base >= runtime->boundary) +				hw_base = 0; +			new_hw_ptr = hw_base + pos;  		} -	      __next_buf: -		runtime->hw_ptr_base += runtime->buffer_size; -		if (runtime->hw_ptr_base == runtime->boundary) -			runtime->hw_ptr_base = 0; -		new_hw_ptr = runtime->hw_ptr_base + pos;  	} - +	if (delta > runtime->period_size) { +		hw_ptr_error(substream, +			     "Lost interrupts? " +			     "(stream=%i, delta=%ld, intr_ptr=%ld)\n", +			     substream->stream, (long)delta, +			     (long)hw_ptr_interrupt); +		/* rebase hw_ptr_interrupt */ +		hw_ptr_interrupt = +			new_hw_ptr - new_hw_ptr % runtime->period_size; +	}  	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&  	    runtime->silence_size > 0)  		snd_pcm_playback_silence(substream, new_hw_ptr); +	runtime->hw_ptr_base = hw_base;  	runtime->status->hw_ptr = new_hw_ptr; -	runtime->hw_ptr_interrupt = new_hw_ptr - new_hw_ptr % runtime->period_size; +	runtime->hw_ptr_interrupt = hw_ptr_interrupt;  	return snd_pcm_update_hw_ptr_post(substream, runtime);  } @@ -233,7 +273,7 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)  {  	struct snd_pcm_runtime *runtime = substream->runtime;  	snd_pcm_uframes_t pos; -	snd_pcm_uframes_t old_hw_ptr, new_hw_ptr; +	snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base;  	snd_pcm_sframes_t delta;  	old_hw_ptr = runtime->status->hw_ptr; @@ -242,29 +282,38 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)  		xrun(substream);  		return -EPIPE;  	} -	new_hw_ptr = runtime->hw_ptr_base + pos; - -	delta = old_hw_ptr - new_hw_ptr; -	if (delta > 0) { -		if ((snd_pcm_uframes_t)delta < runtime->buffer_size / 2) { -#ifdef CONFIG_SND_PCM_XRUN_DEBUG -			if (runtime->periods > 2 && substream->pstr->xrun_debug) { -				snd_printd(KERN_ERR "Unexpected hw_pointer value [2] (stream = %i, delta: -%ld, max jitter = %ld): wrong interrupt acknowledge?\n", substream->stream, (long) delta, runtime->buffer_size / 2); -				if (substream->pstr->xrun_debug > 1) -					dump_stack(); -			} -#endif +	hw_base = runtime->hw_ptr_base; +	new_hw_ptr = hw_base + pos; + +	delta = new_hw_ptr - old_hw_ptr; +	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", +				     substream->stream, (long)pos, +				     (long)old_hw_ptr);  			return 0;  		} -		runtime->hw_ptr_base += runtime->buffer_size; -		if (runtime->hw_ptr_base == runtime->boundary) -			runtime->hw_ptr_base = 0; -		new_hw_ptr = runtime->hw_ptr_base + pos; +		hw_base += runtime->buffer_size; +		if (hw_base >= runtime->boundary) +			hw_base = 0; +		new_hw_ptr = hw_base + pos; +	} +	if (delta > runtime->period_size && runtime->periods > 1) { +		hw_ptr_error(substream, +			     "hw_ptr skipping! " +			     "(pos=%ld, delta=%ld, period=%ld)\n", +			     (long)pos, (long)delta, +			     (long)runtime->period_size); +		return 0;  	}  	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&  	    runtime->silence_size > 0)  		snd_pcm_playback_silence(substream, new_hw_ptr); +	runtime->hw_ptr_base = hw_base;  	runtime->status->hw_ptr = new_hw_ptr;  	return snd_pcm_update_hw_ptr_post(substream, runtime);  | 
