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

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
...@@ -255,6 +255,7 @@ typedef int __bitwise snd_pcm_subformat_t; ...@@ -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_HALF_DUPLEX 0x00100000 /* only half duplex */
#define SNDRV_PCM_INFO_JOINT_DUPLEX 0x00200000 /* playback and capture stream are somewhat correlated */ #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_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; typedef int __bitwise snd_pcm_state_t;
#define SNDRV_PCM_STATE_OPEN ((__force snd_pcm_state_t) 0) /* stream is open */ #define SNDRV_PCM_STATE_OPEN ((__force snd_pcm_state_t) 0) /* stream is open */
......
...@@ -98,6 +98,7 @@ struct snd_pcm_ops { ...@@ -98,6 +98,7 @@ struct snd_pcm_ops {
#define SNDRV_PCM_IOCTL1_INFO 1 #define SNDRV_PCM_IOCTL1_INFO 1
#define SNDRV_PCM_IOCTL1_CHANNEL_INFO 2 #define SNDRV_PCM_IOCTL1_CHANNEL_INFO 2
#define SNDRV_PCM_IOCTL1_GSTATE 3 #define SNDRV_PCM_IOCTL1_GSTATE 3
#define SNDRV_PCM_IOCTL1_FIFO_SIZE 4
#define SNDRV_PCM_TRIGGER_STOP 0 #define SNDRV_PCM_TRIGGER_STOP 0
#define SNDRV_PCM_TRIGGER_START 1 #define SNDRV_PCM_TRIGGER_START 1
......
...@@ -127,24 +127,37 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram ...@@ -127,24 +127,37 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram
} }
#ifdef CONFIG_SND_PCM_XRUN_DEBUG #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 #else
#define xrun_debug(substream) 0 #define xrun_debug(substream, mask) 0
#endif #endif
#define dump_stack_on_xrun(substream) do { \ #define dump_stack_on_xrun(substream) do { \
if (xrun_debug(substream) > 1) \ if (xrun_debug(substream, 2)) \
dump_stack(); \ dump_stack(); \
} while (0) } 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); snprintf(name, len, "pcmC%dD%d%c:%d",
if (xrun_debug(substream)) {
snd_printd(KERN_DEBUG "XRUN: pcmC%dD%d%c\n",
substream->pcm->card->number, substream->pcm->card->number,
substream->pcm->device, 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); dump_stack_on_xrun(substream);
} }
} }
...@@ -155,16 +168,16 @@ snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream, ...@@ -155,16 +168,16 @@ snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream,
{ {
snd_pcm_uframes_t pos; 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); pos = substream->ops->pointer(substream);
if (pos == SNDRV_PCM_POS_XRUN) if (pos == SNDRV_PCM_POS_XRUN)
return pos; /* XRUN */ return pos; /* XRUN */
if (pos >= runtime->buffer_size) { if (pos >= runtime->buffer_size) {
if (printk_ratelimit()) { 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", "buffer size = 0x%lx, period size = 0x%lx\n",
substream->stream, pos, runtime->buffer_size, name, pos, runtime->buffer_size,
runtime->period_size); runtime->period_size);
} }
pos = 0; pos = 0;
...@@ -198,7 +211,7 @@ static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream, ...@@ -198,7 +211,7 @@ static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream,
#define hw_ptr_error(substream, fmt, args...) \ #define hw_ptr_error(substream, fmt, args...) \
do { \ do { \
if (xrun_debug(substream)) { \ if (xrun_debug(substream, 1)) { \
if (printk_ratelimit()) { \ if (printk_ratelimit()) { \
snd_printd("PCM: " fmt, ##args); \ snd_printd("PCM: " fmt, ##args); \
} \ } \
...@@ -252,7 +265,7 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) ...@@ -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 */ /* Do jiffies check only in xrun_debug mode */
if (!xrun_debug(substream)) if (!xrun_debug(substream, 4))
goto no_jiffies_check; goto no_jiffies_check;
/* Skip the jiffies check for hardwares with BATCH flag. /* 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) ...@@ -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) if (runtime->hw.info & SNDRV_PCM_INFO_BATCH)
goto no_jiffies_check; goto no_jiffies_check;
hdelta = new_hw_ptr - old_hw_ptr; hdelta = new_hw_ptr - old_hw_ptr;
if (hdelta < runtime->delay)
goto no_jiffies_check;
hdelta -= runtime->delay;
jdelta = jiffies - runtime->hw_ptr_jiffies; jdelta = jiffies - runtime->hw_ptr_jiffies;
if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) { if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) {
delta = jdelta / delta = jdelta /
...@@ -295,14 +311,20 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) ...@@ -295,14 +311,20 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)
hw_ptr_interrupt = hw_ptr_interrupt =
new_hw_ptr - new_hw_ptr % runtime->period_size; new_hw_ptr - new_hw_ptr % runtime->period_size;
} }
runtime->hw_ptr_interrupt = hw_ptr_interrupt;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
runtime->silence_size > 0) runtime->silence_size > 0)
snd_pcm_playback_silence(substream, new_hw_ptr); 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->hw_ptr_base = hw_base;
runtime->status->hw_ptr = new_hw_ptr; runtime->status->hw_ptr = new_hw_ptr;
runtime->hw_ptr_jiffies = jiffies; 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); return snd_pcm_update_hw_ptr_post(substream, runtime);
} }
...@@ -343,8 +365,12 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) ...@@ -343,8 +365,12 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
new_hw_ptr = hw_base + pos; new_hw_ptr = hw_base + pos;
} }
/* Do jiffies check only in xrun_debug mode */ /* Do jiffies check only in xrun_debug mode */
if (xrun_debug(substream) && if (!xrun_debug(substream, 4))
((delta * HZ) / runtime->rate) > jdelta + HZ/100) { 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_error(substream,
"hw_ptr skipping! " "hw_ptr skipping! "
"(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu)\n", "(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) ...@@ -353,13 +379,19 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
((delta * HZ) / runtime->rate)); ((delta * HZ) / runtime->rate));
return 0; return 0;
} }
no_jiffies_check:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
runtime->silence_size > 0) runtime->silence_size > 0)
snd_pcm_playback_silence(substream, new_hw_ptr); 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->hw_ptr_base = hw_base;
runtime->status->hw_ptr = new_hw_ptr; runtime->status->hw_ptr = new_hw_ptr;
runtime->hw_ptr_jiffies = jiffies; 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); 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, ...@@ -1525,6 +1557,23 @@ static int snd_pcm_lib_ioctl_channel_info(struct snd_pcm_substream *substream,
return 0; 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 * snd_pcm_lib_ioctl - a generic PCM ioctl callback
* @substream: the pcm substream instance * @substream: the pcm substream instance
...@@ -1546,6 +1595,8 @@ int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream, ...@@ -1546,6 +1595,8 @@ int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,
return snd_pcm_lib_ioctl_reset(substream, arg); return snd_pcm_lib_ioctl_reset(substream, arg);
case SNDRV_PCM_IOCTL1_CHANNEL_INFO: case SNDRV_PCM_IOCTL1_CHANNEL_INFO:
return snd_pcm_lib_ioctl_channel_info(substream, arg); 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; return -ENXIO;
} }
......
...@@ -312,9 +312,18 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, ...@@ -312,9 +312,18 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream,
hw = &substream->runtime->hw; hw = &substream->runtime->hw;
if (!params->info) if (!params->info)
params->info = hw->info; params->info = hw->info & ~SNDRV_PCM_INFO_FIFO_IN_FRAMES;
if (!params->fifo_size) if (!params->fifo_size) {
params->fifo_size = hw->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; params->rmask = 0;
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment