Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3b88bc52 authored by Takashi Iwai's avatar Takashi Iwai
Browse files

Merge branch 'topic/pcm-jiffies-check' into for-linus

* topic/pcm-jiffies-check:
  ALSA: pcm - A helper function to compose PCM stream name for debug prints
  ALSA: pcm - Fix update of runtime->hw_ptr_interrupt
  ALSA: pcm - Fix a typo in hw_ptr update check
  ALSA: PCM midlevel: lower jiffies check margin using runtime->delay value
  ALSA: PCM midlevel: Do not update hw_ptr_jiffies when hw_ptr is not changed
  ALSA: PCM midlevel: introduce mask for xrun_debug() macro
  ALSA: PCM midlevel: improve fifo_size handling
parents eabaf063 c0070110
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -255,6 +255,7 @@ typedef int __bitwise snd_pcm_subformat_t;
#define SNDRV_PCM_INFO_HALF_DUPLEX	0x00100000	/* only half duplex */
#define SNDRV_PCM_INFO_JOINT_DUPLEX	0x00200000	/* playback and capture stream are somewhat correlated */
#define SNDRV_PCM_INFO_SYNC_START	0x00400000	/* pcm support some kind of sync go */
#define SNDRV_PCM_INFO_FIFO_IN_FRAMES	0x80000000	/* internal kernel flag - FIFO size is in frames */

typedef int __bitwise snd_pcm_state_t;
#define	SNDRV_PCM_STATE_OPEN		((__force snd_pcm_state_t) 0) /* stream is open */
+1 −0
Original line number Diff line number Diff line
@@ -98,6 +98,7 @@ struct snd_pcm_ops {
#define SNDRV_PCM_IOCTL1_INFO		1
#define SNDRV_PCM_IOCTL1_CHANNEL_INFO	2
#define SNDRV_PCM_IOCTL1_GSTATE		3
#define SNDRV_PCM_IOCTL1_FIFO_SIZE	4

#define SNDRV_PCM_TRIGGER_STOP		0
#define SNDRV_PCM_TRIGGER_START		1
+70 −19
Original line number Diff line number Diff line
@@ -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)	\
		if (xrun_debug(substream, 2))		\
			dump_stack();			\
	} while (0)

static void xrun(struct snd_pcm_substream *substream)
static void pcm_debug_name(struct snd_pcm_substream *substream,
			   char *name, size_t len)
{
	snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
	if (xrun_debug(substream)) {
		snd_printd(KERN_DEBUG "XRUN: pcmC%dD%d%c\n",
	snprintf(name, len, "pcmC%dD%d%c:%d",
		 substream->pcm->card->number,
		 substream->pcm->device,
			   substream->stream ? 'c' : 'p');
		 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, 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;
}
+12 −3
Original line number Diff line number Diff line
@@ -312,9 +312,18 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream,

	hw = &substream->runtime->hw;
	if (!params->info)
		params->info = hw->info;
	if (!params->fifo_size)
		params->fifo_size = hw->fifo_size;
		params->info = hw->info & ~SNDRV_PCM_INFO_FIFO_IN_FRAMES;
	if (!params->fifo_size) {
		if (snd_mask_min(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT]) ==
		    snd_mask_max(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT]) &&
                    snd_mask_min(&params->masks[SNDRV_PCM_HW_PARAM_CHANNELS]) ==
                    snd_mask_max(&params->masks[SNDRV_PCM_HW_PARAM_CHANNELS])) {
			changed = substream->ops->ioctl(substream,
					SNDRV_PCM_IOCTL1_FIFO_SIZE, params);
			if (params < 0)
				return changed;
		}
	}
	params->rmask = 0;
	return 0;
}