diff options
Diffstat (limited to 'sound/core/pcm_lib.c')
| -rw-r--r-- | sound/core/pcm_lib.c | 89 | 
1 files changed, 70 insertions, 19 deletions
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index a7482874c451..333e4dd29450 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -127,24 +127,37 @@ 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) +#define xrun_debug(substream, mask)	((substream)->pstr->xrun_debug & (mask))  #else -#define xrun_debug(substream)	0 +#define xrun_debug(substream, mask)	0  #endif -#define dump_stack_on_xrun(substream) do {	\ -		if (xrun_debug(substream) > 1)	\ -			dump_stack();		\ +#define dump_stack_on_xrun(substream) do {		\ +		if (xrun_debug(substream, 2))		\ +			dump_stack();			\  	} while (0) +static void pcm_debug_name(struct snd_pcm_substream *substream, +			   char *name, size_t len) +{ +	snprintf(name, len, "pcmC%dD%d%c:%d", +		 substream->pcm->card->number, +		 substream->pcm->device, +		 substream->stream ? 'c' : 'p', +		 substream->number); +} +  static void xrun(struct snd_pcm_substream *substream)  { +	struct snd_pcm_runtime *runtime = substream->runtime; + +	if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) +		snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);  	snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); -	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 (xrun_debug(substream, 1)) { +		char name[16]; +		pcm_debug_name(substream, name, sizeof(name)); +		snd_printd(KERN_DEBUG "XRUN: %s\n", name);  		dump_stack_on_xrun(substream);  	}  } @@ -155,16 +168,16 @@ snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream,  {  	snd_pcm_uframes_t pos; -	if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) -		snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);  	pos = substream->ops->pointer(substream);  	if (pos == SNDRV_PCM_POS_XRUN)  		return pos; /* XRUN */  	if (pos >= runtime->buffer_size) {  		if (printk_ratelimit()) { -			snd_printd(KERN_ERR  "BUG: stream = %i, pos = 0x%lx, " +			char name[16]; +			pcm_debug_name(substream, name, sizeof(name)); +			snd_printd(KERN_ERR  "BUG: %s, pos = 0x%lx, "  				   "buffer size = 0x%lx, period size = 0x%lx\n", -				   substream->stream, pos, runtime->buffer_size, +				   name, pos, runtime->buffer_size,  				   runtime->period_size);  		}  		pos = 0; @@ -198,7 +211,7 @@ static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream,  #define hw_ptr_error(substream, fmt, args...)				\  	do {								\ -		if (xrun_debug(substream)) {				\ +		if (xrun_debug(substream, 1)) {				\  			if (printk_ratelimit()) {			\  				snd_printd("PCM: " fmt, ##args);	\  			}						\ @@ -252,7 +265,7 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)  	}  	/* Do jiffies check only in xrun_debug mode */ -	if (!xrun_debug(substream)) +	if (!xrun_debug(substream, 4))  		goto no_jiffies_check;  	/* Skip the jiffies check for hardwares with BATCH flag. @@ -262,6 +275,9 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)  	if (runtime->hw.info & SNDRV_PCM_INFO_BATCH)  		goto no_jiffies_check;  	hdelta = new_hw_ptr - old_hw_ptr; +	if (hdelta < runtime->delay) +		goto no_jiffies_check; +	hdelta -= runtime->delay;  	jdelta = jiffies - runtime->hw_ptr_jiffies;  	if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) {  		delta = jdelta / @@ -295,14 +311,20 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)  		hw_ptr_interrupt =  			new_hw_ptr - new_hw_ptr % runtime->period_size;  	} +	runtime->hw_ptr_interrupt = hw_ptr_interrupt; +  	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&  	    runtime->silence_size > 0)  		snd_pcm_playback_silence(substream, new_hw_ptr); +	if (runtime->status->hw_ptr == new_hw_ptr) +		return 0; +  	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; +	if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) +		snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);  	return snd_pcm_update_hw_ptr_post(substream, runtime);  } @@ -343,8 +365,12 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)  		new_hw_ptr = hw_base + pos;  	}  	/* Do jiffies check only in xrun_debug mode */ -	if (xrun_debug(substream) && -	    ((delta * HZ) / runtime->rate) > jdelta + HZ/100) { +	if (!xrun_debug(substream, 4)) +		goto no_jiffies_check; +	if (delta < runtime->delay) +		goto no_jiffies_check; +	delta -= runtime->delay; +	if (((delta * HZ) / runtime->rate) > jdelta + HZ/100) {  		hw_ptr_error(substream,  			     "hw_ptr skipping! "  			     "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu)\n", @@ -353,13 +379,19 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)  			     ((delta * HZ) / runtime->rate));  		return 0;  	} + no_jiffies_check:  	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&  	    runtime->silence_size > 0)  		snd_pcm_playback_silence(substream, new_hw_ptr); +	if (runtime->status->hw_ptr == new_hw_ptr) +		return 0; +  	runtime->hw_ptr_base = hw_base;  	runtime->status->hw_ptr = new_hw_ptr;  	runtime->hw_ptr_jiffies = jiffies; +	if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) +		snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);  	return snd_pcm_update_hw_ptr_post(substream, runtime);  } @@ -1525,6 +1557,23 @@ static int snd_pcm_lib_ioctl_channel_info(struct snd_pcm_substream *substream,  	return 0;  } +static int snd_pcm_lib_ioctl_fifo_size(struct snd_pcm_substream *substream, +				       void *arg) +{ +	struct snd_pcm_hw_params *params = arg; +	snd_pcm_format_t format; +	int channels, width; + +	params->fifo_size = substream->runtime->hw.fifo_size; +	if (!(substream->runtime->hw.info & SNDRV_PCM_INFO_FIFO_IN_FRAMES)) { +		format = params_format(params); +		channels = params_channels(params); +		width = snd_pcm_format_physical_width(format); +		params->fifo_size /= width * channels; +	} +	return 0; +} +  /**   * snd_pcm_lib_ioctl - a generic PCM ioctl callback   * @substream: the pcm substream instance @@ -1546,6 +1595,8 @@ int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,  		return snd_pcm_lib_ioctl_reset(substream, arg);  	case SNDRV_PCM_IOCTL1_CHANNEL_INFO:  		return snd_pcm_lib_ioctl_channel_info(substream, arg); +	case SNDRV_PCM_IOCTL1_FIFO_SIZE: +		return snd_pcm_lib_ioctl_fifo_size(substream, arg);  	}  	return -ENXIO;  }  | 
