mirror of
https://github.com/torvalds/linux.git
synced 2025-01-01 07:42:07 +00:00
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
This commit is contained in:
commit
3b88bc5229
@ -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 {
|
|||||||
#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
|
|||||||
}
|
}
|
||||||
|
|
||||||
#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,
|
|||||||
{
|
{
|
||||||
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,
|
|||||||
|
|
||||||
#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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
((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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
|
|
||||||
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(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT]) ==
|
||||||
|
snd_mask_max(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT]) &&
|
||||||
|
snd_mask_min(¶ms->masks[SNDRV_PCM_HW_PARAM_CHANNELS]) ==
|
||||||
|
snd_mask_max(¶ms->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;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user