Commit d807500a authored by Takashi Iwai's avatar Takashi Iwai

Merge branch 'topic/pcm-cleanup' into for-linus

parents c7ccfd06 8b22d943
...@@ -364,7 +364,6 @@ struct snd_pcm_substream { ...@@ -364,7 +364,6 @@ struct snd_pcm_substream {
/* -- timer section -- */ /* -- timer section -- */
struct snd_timer *timer; /* timer */ struct snd_timer *timer; /* timer */
unsigned timer_running: 1; /* time is running */ unsigned timer_running: 1; /* time is running */
spinlock_t timer_lock;
/* -- next substream -- */ /* -- next substream -- */
struct snd_pcm_substream *next; struct snd_pcm_substream *next;
/* -- linked substreams -- */ /* -- linked substreams -- */
......
...@@ -667,7 +667,6 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count) ...@@ -667,7 +667,6 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
spin_lock_init(&substream->self_group.lock); spin_lock_init(&substream->self_group.lock);
INIT_LIST_HEAD(&substream->self_group.substreams); INIT_LIST_HEAD(&substream->self_group.substreams);
list_add_tail(&substream->link_list, &substream->self_group.substreams); list_add_tail(&substream->link_list, &substream->self_group.substreams);
spin_lock_init(&substream->timer_lock);
atomic_set(&substream->mmap_count, 0); atomic_set(&substream->mmap_count, 0);
prev = substream; prev = substream;
} }
......
...@@ -125,23 +125,32 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram ...@@ -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) static void xrun(struct snd_pcm_substream *substream)
{ {
snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
#ifdef CONFIG_SND_PCM_XRUN_DEBUG if (xrun_debug(substream)) {
if (substream->pstr->xrun_debug) {
snd_printd(KERN_DEBUG "XRUN: pcmC%dD%d%c\n", 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');
if (substream->pstr->xrun_debug > 1) dump_stack_on_xrun(substream);
dump_stack();
} }
#endif
} }
static inline snd_pcm_uframes_t snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream, static snd_pcm_uframes_t
struct snd_pcm_runtime *runtime) snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime)
{ {
snd_pcm_uframes_t pos; 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 ...@@ -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); pos = substream->ops->pointer(substream);
if (pos == SNDRV_PCM_POS_XRUN) if (pos == SNDRV_PCM_POS_XRUN)
return pos; /* XRUN */ return pos; /* XRUN */
#ifdef CONFIG_SND_DEBUG
if (pos >= runtime->buffer_size) { 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; pos -= pos % runtime->min_align;
return pos; return pos;
} }
static inline int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream, static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime) struct snd_pcm_runtime *runtime)
{ {
snd_pcm_uframes_t avail; snd_pcm_uframes_t avail;
...@@ -182,11 +195,21 @@ static inline int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream ...@@ -182,11 +195,21 @@ static inline int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream
return 0; 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; struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_uframes_t pos; 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; snd_pcm_sframes_t delta;
pos = snd_pcm_update_hw_ptr_pos(substream, runtime); 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 ...@@ -194,36 +217,53 @@ static inline int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *subs
xrun(substream); xrun(substream);
return -EPIPE; return -EPIPE;
} }
if (runtime->period_size == runtime->buffer_size) hw_base = runtime->hw_ptr_base;
goto __next_buf; new_hw_ptr = hw_base + pos;
new_hw_ptr = runtime->hw_ptr_base + pos;
hw_ptr_interrupt = runtime->hw_ptr_interrupt + runtime->period_size; hw_ptr_interrupt = runtime->hw_ptr_interrupt + runtime->period_size;
delta = new_hw_ptr - hw_ptr_interrupt;
delta = hw_ptr_interrupt - new_hw_ptr; if (hw_ptr_interrupt >= runtime->boundary) {
if (delta > 0) { hw_ptr_interrupt -= runtime->boundary;
if ((snd_pcm_uframes_t)delta < runtime->buffer_size / 2) { if (hw_base < runtime->boundary / 2)
#ifdef CONFIG_SND_PCM_XRUN_DEBUG /* hw_base was already lapped; recalc delta */
if (runtime->periods > 1 && substream->pstr->xrun_debug) { delta = new_hw_ptr - hw_ptr_interrupt;
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) if (delta < 0) {
dump_stack(); delta += runtime->buffer_size;
} if (delta < 0) {
#endif hw_ptr_error(substream,
return 0; "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 && 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);
runtime->hw_ptr_base = hw_base;
runtime->status->hw_ptr = new_hw_ptr; 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); return snd_pcm_update_hw_ptr_post(substream, runtime);
} }
...@@ -233,7 +273,7 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) ...@@ -233,7 +273,7 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
{ {
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_uframes_t pos; 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; snd_pcm_sframes_t delta;
old_hw_ptr = runtime->status->hw_ptr; old_hw_ptr = runtime->status->hw_ptr;
...@@ -242,29 +282,38 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) ...@@ -242,29 +282,38 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
xrun(substream); xrun(substream);
return -EPIPE; return -EPIPE;
} }
new_hw_ptr = runtime->hw_ptr_base + pos; hw_base = runtime->hw_ptr_base;
new_hw_ptr = hw_base + pos;
delta = old_hw_ptr - new_hw_ptr;
if (delta > 0) { delta = new_hw_ptr - old_hw_ptr;
if ((snd_pcm_uframes_t)delta < runtime->buffer_size / 2) { if (delta < 0) {
#ifdef CONFIG_SND_PCM_XRUN_DEBUG delta += runtime->buffer_size;
if (runtime->periods > 2 && substream->pstr->xrun_debug) { if (delta < 0) {
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); hw_ptr_error(substream,
if (substream->pstr->xrun_debug > 1) "Unexpected hw_pointer value [2] "
dump_stack(); "(stream=%i, pos=%ld, old_ptr=%ld)\n",
} substream->stream, (long)pos,
#endif (long)old_hw_ptr);
return 0; return 0;
} }
runtime->hw_ptr_base += runtime->buffer_size; hw_base += runtime->buffer_size;
if (runtime->hw_ptr_base == runtime->boundary) if (hw_base >= runtime->boundary)
runtime->hw_ptr_base = 0; hw_base = 0;
new_hw_ptr = runtime->hw_ptr_base + pos; 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 && 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);
runtime->hw_ptr_base = hw_base;
runtime->status->hw_ptr = new_hw_ptr; runtime->status->hw_ptr = new_hw_ptr;
return snd_pcm_update_hw_ptr_post(substream, runtime); return snd_pcm_update_hw_ptr_post(substream, runtime);
......
...@@ -85,25 +85,19 @@ static unsigned long snd_pcm_timer_resolution(struct snd_timer * timer) ...@@ -85,25 +85,19 @@ static unsigned long snd_pcm_timer_resolution(struct snd_timer * timer)
static int snd_pcm_timer_start(struct snd_timer * timer) static int snd_pcm_timer_start(struct snd_timer * timer)
{ {
unsigned long flags;
struct snd_pcm_substream *substream; struct snd_pcm_substream *substream;
substream = snd_timer_chip(timer); substream = snd_timer_chip(timer);
spin_lock_irqsave(&substream->timer_lock, flags);
substream->timer_running = 1; substream->timer_running = 1;
spin_unlock_irqrestore(&substream->timer_lock, flags);
return 0; return 0;
} }
static int snd_pcm_timer_stop(struct snd_timer * timer) static int snd_pcm_timer_stop(struct snd_timer * timer)
{ {
unsigned long flags;
struct snd_pcm_substream *substream; struct snd_pcm_substream *substream;
substream = snd_timer_chip(timer); substream = snd_timer_chip(timer);
spin_lock_irqsave(&substream->timer_lock, flags);
substream->timer_running = 0; substream->timer_running = 0;
spin_unlock_irqrestore(&substream->timer_lock, flags);
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