|
@@ -126,17 +126,6 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-#ifdef CONFIG_SND_PCM_XRUN_DEBUG
|
|
|
-#define xrun_debug(substream, mask) ((substream)->pstr->xrun_debug & (mask))
|
|
|
-#else
|
|
|
-#define xrun_debug(substream, mask) 0
|
|
|
-#endif
|
|
|
-
|
|
|
-#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)
|
|
|
{
|
|
@@ -147,6 +136,24 @@ static void pcm_debug_name(struct snd_pcm_substream *substream,
|
|
|
substream->number);
|
|
|
}
|
|
|
|
|
|
+#define XRUN_DEBUG_BASIC (1<<0)
|
|
|
+#define XRUN_DEBUG_STACK (1<<1) /* dump also stack */
|
|
|
+#define XRUN_DEBUG_JIFFIESCHECK (1<<2) /* do jiffies check */
|
|
|
+#define XRUN_DEBUG_PERIODUPDATE (1<<3) /* full period update info */
|
|
|
+#define XRUN_DEBUG_HWPTRUPDATE (1<<4) /* full hwptr update info */
|
|
|
+#define XRUN_DEBUG_LOG (1<<5) /* show last 10 positions on err */
|
|
|
+#define XRUN_DEBUG_LOGONCE (1<<6) /* do above only once */
|
|
|
+
|
|
|
+#ifdef CONFIG_SND_PCM_XRUN_DEBUG
|
|
|
+
|
|
|
+#define xrun_debug(substream, mask) \
|
|
|
+ ((substream)->pstr->xrun_debug & (mask))
|
|
|
+
|
|
|
+#define dump_stack_on_xrun(substream) do { \
|
|
|
+ if (xrun_debug(substream, XRUN_DEBUG_STACK)) \
|
|
|
+ dump_stack(); \
|
|
|
+ } while (0)
|
|
|
+
|
|
|
static void xrun(struct snd_pcm_substream *substream)
|
|
|
{
|
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
@@ -154,7 +161,7 @@ static void xrun(struct snd_pcm_substream *substream)
|
|
|
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)) {
|
|
|
+ if (xrun_debug(substream, XRUN_DEBUG_BASIC)) {
|
|
|
char name[16];
|
|
|
pcm_debug_name(substream, name, sizeof(name));
|
|
|
snd_printd(KERN_DEBUG "XRUN: %s\n", name);
|
|
@@ -162,32 +169,102 @@ static void xrun(struct snd_pcm_substream *substream)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static snd_pcm_uframes_t
|
|
|
-snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream,
|
|
|
- struct snd_pcm_runtime *runtime)
|
|
|
-{
|
|
|
+#define hw_ptr_error(substream, fmt, args...) \
|
|
|
+ do { \
|
|
|
+ if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \
|
|
|
+ xrun_log_show(substream); \
|
|
|
+ if (printk_ratelimit()) { \
|
|
|
+ snd_printd("PCM: " fmt, ##args); \
|
|
|
+ } \
|
|
|
+ dump_stack_on_xrun(substream); \
|
|
|
+ } \
|
|
|
+ } while (0)
|
|
|
+
|
|
|
+#define XRUN_LOG_CNT 10
|
|
|
+
|
|
|
+struct hwptr_log_entry {
|
|
|
+ unsigned long jiffies;
|
|
|
snd_pcm_uframes_t pos;
|
|
|
+ snd_pcm_uframes_t period_size;
|
|
|
+ snd_pcm_uframes_t buffer_size;
|
|
|
+ snd_pcm_uframes_t old_hw_ptr;
|
|
|
+ snd_pcm_uframes_t hw_ptr_base;
|
|
|
+};
|
|
|
|
|
|
- pos = substream->ops->pointer(substream);
|
|
|
- if (pos == SNDRV_PCM_POS_XRUN)
|
|
|
- return pos; /* XRUN */
|
|
|
- if (pos >= runtime->buffer_size) {
|
|
|
- if (printk_ratelimit()) {
|
|
|
- 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",
|
|
|
- name, pos, runtime->buffer_size,
|
|
|
- runtime->period_size);
|
|
|
- }
|
|
|
- pos = 0;
|
|
|
+struct snd_pcm_hwptr_log {
|
|
|
+ unsigned int idx;
|
|
|
+ unsigned int hit: 1;
|
|
|
+ struct hwptr_log_entry entries[XRUN_LOG_CNT];
|
|
|
+};
|
|
|
+
|
|
|
+static void xrun_log(struct snd_pcm_substream *substream,
|
|
|
+ snd_pcm_uframes_t pos)
|
|
|
+{
|
|
|
+ struct snd_pcm_runtime *runtime = substream->runtime;
|
|
|
+ struct snd_pcm_hwptr_log *log = runtime->hwptr_log;
|
|
|
+ struct hwptr_log_entry *entry;
|
|
|
+
|
|
|
+ if (log == NULL) {
|
|
|
+ log = kzalloc(sizeof(*log), GFP_ATOMIC);
|
|
|
+ if (log == NULL)
|
|
|
+ return;
|
|
|
+ runtime->hwptr_log = log;
|
|
|
+ } else {
|
|
|
+ if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit)
|
|
|
+ return;
|
|
|
}
|
|
|
- pos -= pos % runtime->min_align;
|
|
|
- return pos;
|
|
|
+ entry = &log->entries[log->idx];
|
|
|
+ entry->jiffies = jiffies;
|
|
|
+ entry->pos = pos;
|
|
|
+ entry->period_size = runtime->period_size;
|
|
|
+ entry->buffer_size = runtime->buffer_size;;
|
|
|
+ entry->old_hw_ptr = runtime->status->hw_ptr;
|
|
|
+ entry->hw_ptr_base = runtime->hw_ptr_base;
|
|
|
+ log->idx = (log->idx + 1) % XRUN_LOG_CNT;
|
|
|
}
|
|
|
|
|
|
-static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream,
|
|
|
- struct snd_pcm_runtime *runtime)
|
|
|
+static void xrun_log_show(struct snd_pcm_substream *substream)
|
|
|
+{
|
|
|
+ struct snd_pcm_hwptr_log *log = substream->runtime->hwptr_log;
|
|
|
+ struct hwptr_log_entry *entry;
|
|
|
+ char name[16];
|
|
|
+ unsigned int idx;
|
|
|
+ int cnt;
|
|
|
+
|
|
|
+ if (log == NULL)
|
|
|
+ return;
|
|
|
+ if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit)
|
|
|
+ return;
|
|
|
+ pcm_debug_name(substream, name, sizeof(name));
|
|
|
+ for (cnt = 0, idx = log->idx; cnt < XRUN_LOG_CNT; cnt++) {
|
|
|
+ entry = &log->entries[idx];
|
|
|
+ if (entry->period_size == 0)
|
|
|
+ break;
|
|
|
+ snd_printd("hwptr log: %s: j=%lu, pos=%ld/%ld/%ld, "
|
|
|
+ "hwptr=%ld/%ld\n",
|
|
|
+ name, entry->jiffies, (unsigned long)entry->pos,
|
|
|
+ (unsigned long)entry->period_size,
|
|
|
+ (unsigned long)entry->buffer_size,
|
|
|
+ (unsigned long)entry->old_hw_ptr,
|
|
|
+ (unsigned long)entry->hw_ptr_base);
|
|
|
+ idx++;
|
|
|
+ idx %= XRUN_LOG_CNT;
|
|
|
+ }
|
|
|
+ log->hit = 1;
|
|
|
+}
|
|
|
+
|
|
|
+#else /* ! CONFIG_SND_PCM_XRUN_DEBUG */
|
|
|
+
|
|
|
+#define xrun_debug(substream, mask) 0
|
|
|
+#define xrun(substream) do { } while (0)
|
|
|
+#define hw_ptr_error(substream, fmt, args...) do { } while (0)
|
|
|
+#define xrun_log(substream, pos) do { } while (0)
|
|
|
+#define xrun_log_show(substream) do { } while (0)
|
|
|
+
|
|
|
+#endif
|
|
|
+
|
|
|
+int snd_pcm_update_state(struct snd_pcm_substream *substream,
|
|
|
+ struct snd_pcm_runtime *runtime)
|
|
|
{
|
|
|
snd_pcm_uframes_t avail;
|
|
|
|
|
@@ -208,89 +285,96 @@ static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream,
|
|
|
return -EPIPE;
|
|
|
}
|
|
|
}
|
|
|
- if (avail >= runtime->control->avail_min)
|
|
|
+ if (!runtime->nowake && avail >= runtime->control->avail_min)
|
|
|
wake_up(&runtime->sleep);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-#define hw_ptr_error(substream, fmt, args...) \
|
|
|
- do { \
|
|
|
- if (xrun_debug(substream, 1)) { \
|
|
|
- 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)
|
|
|
+static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
|
|
|
+ unsigned int in_interrupt)
|
|
|
{
|
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
|
snd_pcm_uframes_t pos;
|
|
|
- snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_ptr_interrupt, hw_base;
|
|
|
+ snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base;
|
|
|
snd_pcm_sframes_t hdelta, delta;
|
|
|
unsigned long jdelta;
|
|
|
|
|
|
old_hw_ptr = runtime->status->hw_ptr;
|
|
|
- pos = snd_pcm_update_hw_ptr_pos(substream, runtime);
|
|
|
+ pos = substream->ops->pointer(substream);
|
|
|
if (pos == SNDRV_PCM_POS_XRUN) {
|
|
|
xrun(substream);
|
|
|
return -EPIPE;
|
|
|
}
|
|
|
- if (xrun_debug(substream, 8)) {
|
|
|
- char name[16];
|
|
|
- pcm_debug_name(substream, name, sizeof(name));
|
|
|
- snd_printd("period_update: %s: pos=0x%x/0x%x/0x%x, "
|
|
|
- "hwptr=0x%lx, hw_base=0x%lx, hw_intr=0x%lx\n",
|
|
|
- name, (unsigned int)pos,
|
|
|
- (unsigned int)runtime->period_size,
|
|
|
- (unsigned int)runtime->buffer_size,
|
|
|
- (unsigned long)old_hw_ptr,
|
|
|
- (unsigned long)runtime->hw_ptr_base,
|
|
|
- (unsigned long)runtime->hw_ptr_interrupt);
|
|
|
+ if (pos >= runtime->buffer_size) {
|
|
|
+ if (printk_ratelimit()) {
|
|
|
+ char name[16];
|
|
|
+ pcm_debug_name(substream, name, sizeof(name));
|
|
|
+ xrun_log_show(substream);
|
|
|
+ snd_printd(KERN_ERR "BUG: %s, pos = %ld, "
|
|
|
+ "buffer size = %ld, period size = %ld\n",
|
|
|
+ name, pos, runtime->buffer_size,
|
|
|
+ runtime->period_size);
|
|
|
+ }
|
|
|
+ pos = 0;
|
|
|
}
|
|
|
+ pos -= pos % runtime->min_align;
|
|
|
+ if (xrun_debug(substream, XRUN_DEBUG_LOG))
|
|
|
+ xrun_log(substream, pos);
|
|
|
hw_base = runtime->hw_ptr_base;
|
|
|
new_hw_ptr = hw_base + pos;
|
|
|
- hw_ptr_interrupt = runtime->hw_ptr_interrupt + runtime->period_size;
|
|
|
- delta = new_hw_ptr - hw_ptr_interrupt;
|
|
|
- if (hw_ptr_interrupt >= runtime->boundary) {
|
|
|
- hw_ptr_interrupt -= runtime->boundary;
|
|
|
- if (hw_base < runtime->boundary / 2)
|
|
|
- /* hw_base was already lapped; recalc delta */
|
|
|
- delta = new_hw_ptr - hw_ptr_interrupt;
|
|
|
- }
|
|
|
- if (delta < 0) {
|
|
|
- if (runtime->periods == 1 || new_hw_ptr < old_hw_ptr)
|
|
|
- delta += runtime->buffer_size;
|
|
|
- if (delta < 0) {
|
|
|
- hw_ptr_error(substream,
|
|
|
- "Unexpected hw_pointer value "
|
|
|
- "(stream=%i, pos=%ld, intr_ptr=%ld)\n",
|
|
|
- substream->stream, (long)pos,
|
|
|
- (long)hw_ptr_interrupt);
|
|
|
-#if 1
|
|
|
- /* simply skipping the hwptr update seems more
|
|
|
- * robust in some cases, e.g. on VMware with
|
|
|
- * inaccurate timer source
|
|
|
- */
|
|
|
- return 0; /* skip this update */
|
|
|
-#else
|
|
|
- /* 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;
|
|
|
-#endif
|
|
|
- } else {
|
|
|
+ if (in_interrupt) {
|
|
|
+ /* we know that one period was processed */
|
|
|
+ /* delta = "expected next hw_ptr" for in_interrupt != 0 */
|
|
|
+ delta = old_hw_ptr - (old_hw_ptr % runtime->period_size)
|
|
|
+ + runtime->period_size;
|
|
|
+ if (delta > new_hw_ptr) {
|
|
|
hw_base += runtime->buffer_size;
|
|
|
if (hw_base >= runtime->boundary)
|
|
|
hw_base = 0;
|
|
|
new_hw_ptr = hw_base + pos;
|
|
|
+ goto __delta;
|
|
|
}
|
|
|
}
|
|
|
+ /* new_hw_ptr might be lower than old_hw_ptr in case when */
|
|
|
+ /* pointer crosses the end of the ring buffer */
|
|
|
+ if (new_hw_ptr < old_hw_ptr) {
|
|
|
+ hw_base += runtime->buffer_size;
|
|
|
+ if (hw_base >= runtime->boundary)
|
|
|
+ hw_base = 0;
|
|
|
+ new_hw_ptr = hw_base + pos;
|
|
|
+ }
|
|
|
+ __delta:
|
|
|
+ delta = (new_hw_ptr - old_hw_ptr) % runtime->boundary;
|
|
|
+ if (xrun_debug(substream, in_interrupt ?
|
|
|
+ XRUN_DEBUG_PERIODUPDATE : XRUN_DEBUG_HWPTRUPDATE)) {
|
|
|
+ char name[16];
|
|
|
+ pcm_debug_name(substream, name, sizeof(name));
|
|
|
+ snd_printd("%s_update: %s: pos=%u/%u/%u, "
|
|
|
+ "hwptr=%ld/%ld/%ld/%ld\n",
|
|
|
+ in_interrupt ? "period" : "hwptr",
|
|
|
+ name,
|
|
|
+ (unsigned int)pos,
|
|
|
+ (unsigned int)runtime->period_size,
|
|
|
+ (unsigned int)runtime->buffer_size,
|
|
|
+ (unsigned long)delta,
|
|
|
+ (unsigned long)old_hw_ptr,
|
|
|
+ (unsigned long)new_hw_ptr,
|
|
|
+ (unsigned long)runtime->hw_ptr_base);
|
|
|
+ }
|
|
|
+ /* something must be really wrong */
|
|
|
+ if (delta >= runtime->buffer_size + runtime->period_size) {
|
|
|
+ hw_ptr_error(substream,
|
|
|
+ "Unexpected hw_pointer value %s"
|
|
|
+ "(stream=%i, pos=%ld, new_hw_ptr=%ld, "
|
|
|
+ "old_hw_ptr=%ld)\n",
|
|
|
+ in_interrupt ? "[Q] " : "[P]",
|
|
|
+ substream->stream, (long)pos,
|
|
|
+ (long)new_hw_ptr, (long)old_hw_ptr);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
|
|
|
/* Do jiffies check only in xrun_debug mode */
|
|
|
- if (!xrun_debug(substream, 4))
|
|
|
+ if (!xrun_debug(substream, XRUN_DEBUG_JIFFIESCHECK))
|
|
|
goto no_jiffies_check;
|
|
|
|
|
|
/* Skip the jiffies check for hardwares with BATCH flag.
|
|
@@ -299,7 +383,7 @@ 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;
|
|
|
+ hdelta = delta;
|
|
|
if (hdelta < runtime->delay)
|
|
|
goto no_jiffies_check;
|
|
|
hdelta -= runtime->delay;
|
|
@@ -308,130 +392,62 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)
|
|
|
delta = jdelta /
|
|
|
(((runtime->period_size * HZ) / runtime->rate)
|
|
|
+ HZ/100);
|
|
|
+ /* move new_hw_ptr according jiffies not pos variable */
|
|
|
+ new_hw_ptr = old_hw_ptr;
|
|
|
+ /* use loop to avoid checks for delta overflows */
|
|
|
+ /* the delta value is small or zero in most cases */
|
|
|
+ while (delta > 0) {
|
|
|
+ new_hw_ptr += runtime->period_size;
|
|
|
+ if (new_hw_ptr >= runtime->boundary)
|
|
|
+ new_hw_ptr -= runtime->boundary;
|
|
|
+ delta--;
|
|
|
+ }
|
|
|
+ /* align hw_base to buffer_size */
|
|
|
+ hw_base = new_hw_ptr - (new_hw_ptr % runtime->buffer_size);
|
|
|
+ delta = 0;
|
|
|
hw_ptr_error(substream,
|
|
|
- "hw_ptr skipping! [Q] "
|
|
|
+ "hw_ptr skipping! %s"
|
|
|
"(pos=%ld, delta=%ld, period=%ld, "
|
|
|
- "jdelta=%lu/%lu/%lu)\n",
|
|
|
+ "jdelta=%lu/%lu/%lu, hw_ptr=%ld/%ld)\n",
|
|
|
+ in_interrupt ? "[Q] " : "",
|
|
|
(long)pos, (long)hdelta,
|
|
|
(long)runtime->period_size, jdelta,
|
|
|
- ((hdelta * HZ) / runtime->rate), delta);
|
|
|
- hw_ptr_interrupt = runtime->hw_ptr_interrupt +
|
|
|
- runtime->period_size * delta;
|
|
|
- if (hw_ptr_interrupt >= runtime->boundary)
|
|
|
- hw_ptr_interrupt -= runtime->boundary;
|
|
|
- /* 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;
|
|
|
+ ((hdelta * HZ) / runtime->rate), delta,
|
|
|
+ (unsigned long)old_hw_ptr,
|
|
|
+ (unsigned long)new_hw_ptr);
|
|
|
}
|
|
|
no_jiffies_check:
|
|
|
if (delta > runtime->period_size + runtime->period_size / 2) {
|
|
|
hw_ptr_error(substream,
|
|
|
- "Lost interrupts? "
|
|
|
- "(stream=%i, delta=%ld, intr_ptr=%ld)\n",
|
|
|
+ "Lost interrupts? %s"
|
|
|
+ "(stream=%i, delta=%ld, new_hw_ptr=%ld, "
|
|
|
+ "old_hw_ptr=%ld)\n",
|
|
|
+ in_interrupt ? "[Q] " : "",
|
|
|
substream->stream, (long)delta,
|
|
|
- (long)hw_ptr_interrupt);
|
|
|
- /* rebase hw_ptr_interrupt */
|
|
|
- hw_ptr_interrupt =
|
|
|
- new_hw_ptr - new_hw_ptr % runtime->period_size;
|
|
|
+ (long)new_hw_ptr,
|
|
|
+ (long)old_hw_ptr);
|
|
|
}
|
|
|
- runtime->hw_ptr_interrupt = hw_ptr_interrupt;
|
|
|
+
|
|
|
+ if (runtime->status->hw_ptr == new_hw_ptr)
|
|
|
+ return 0;
|
|
|
|
|
|
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);
|
|
|
+ return snd_pcm_update_state(substream, runtime);
|
|
|
}
|
|
|
|
|
|
/* CAUTION: call it with irq disabled */
|
|
|
int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
|
|
|
{
|
|
|
- struct snd_pcm_runtime *runtime = substream->runtime;
|
|
|
- snd_pcm_uframes_t pos;
|
|
|
- snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base;
|
|
|
- snd_pcm_sframes_t delta;
|
|
|
- unsigned long jdelta;
|
|
|
-
|
|
|
- old_hw_ptr = runtime->status->hw_ptr;
|
|
|
- pos = snd_pcm_update_hw_ptr_pos(substream, runtime);
|
|
|
- if (pos == SNDRV_PCM_POS_XRUN) {
|
|
|
- xrun(substream);
|
|
|
- return -EPIPE;
|
|
|
- }
|
|
|
- if (xrun_debug(substream, 16)) {
|
|
|
- char name[16];
|
|
|
- pcm_debug_name(substream, name, sizeof(name));
|
|
|
- snd_printd("hw_update: %s: pos=0x%x/0x%x/0x%x, "
|
|
|
- "hwptr=0x%lx, hw_base=0x%lx, hw_intr=0x%lx\n",
|
|
|
- name, (unsigned int)pos,
|
|
|
- (unsigned int)runtime->period_size,
|
|
|
- (unsigned int)runtime->buffer_size,
|
|
|
- (unsigned long)old_hw_ptr,
|
|
|
- (unsigned long)runtime->hw_ptr_base,
|
|
|
- (unsigned long)runtime->hw_ptr_interrupt);
|
|
|
- }
|
|
|
-
|
|
|
- hw_base = runtime->hw_ptr_base;
|
|
|
- new_hw_ptr = hw_base + pos;
|
|
|
-
|
|
|
- delta = new_hw_ptr - old_hw_ptr;
|
|
|
- jdelta = jiffies - runtime->hw_ptr_jiffies;
|
|
|
- if (delta < 0) {
|
|
|
- delta += runtime->buffer_size;
|
|
|
- if (delta < 0) {
|
|
|
- hw_ptr_error(substream,
|
|
|
- "Unexpected hw_pointer value [2] "
|
|
|
- "(stream=%i, pos=%ld, old_ptr=%ld, jdelta=%li)\n",
|
|
|
- substream->stream, (long)pos,
|
|
|
- (long)old_hw_ptr, jdelta);
|
|
|
- return 0;
|
|
|
- }
|
|
|
- hw_base += runtime->buffer_size;
|
|
|
- if (hw_base >= runtime->boundary)
|
|
|
- hw_base = 0;
|
|
|
- new_hw_ptr = hw_base + pos;
|
|
|
- }
|
|
|
- /* Do jiffies check only in xrun_debug mode */
|
|
|
- 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",
|
|
|
- (long)pos, (long)delta,
|
|
|
- (long)runtime->period_size, jdelta,
|
|
|
- ((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);
|
|
|
+ return snd_pcm_update_hw_ptr0(substream, 0);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -1657,7 +1673,7 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
|
|
|
|
|
|
snd_pcm_stream_lock_irqsave(substream, flags);
|
|
|
if (!snd_pcm_running(substream) ||
|
|
|
- snd_pcm_update_hw_ptr_interrupt(substream) < 0)
|
|
|
+ snd_pcm_update_hw_ptr0(substream, 1) < 0)
|
|
|
goto _end;
|
|
|
|
|
|
if (substream->timer_running)
|
|
@@ -1790,6 +1806,7 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream,
|
|
|
goto _end_unlock;
|
|
|
}
|
|
|
|
|
|
+ runtime->nowake = 1;
|
|
|
while (size > 0) {
|
|
|
snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
|
|
|
snd_pcm_uframes_t avail;
|
|
@@ -1811,15 +1828,17 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream,
|
|
|
if (frames > cont)
|
|
|
frames = cont;
|
|
|
if (snd_BUG_ON(!frames)) {
|
|
|
+ runtime->nowake = 0;
|
|
|
snd_pcm_stream_unlock_irq(substream);
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
appl_ptr = runtime->control->appl_ptr;
|
|
|
appl_ofs = appl_ptr % runtime->buffer_size;
|
|
|
snd_pcm_stream_unlock_irq(substream);
|
|
|
- if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0)
|
|
|
- goto _end;
|
|
|
+ err = transfer(substream, appl_ofs, data, offset, frames);
|
|
|
snd_pcm_stream_lock_irq(substream);
|
|
|
+ if (err < 0)
|
|
|
+ goto _end_unlock;
|
|
|
switch (runtime->status->state) {
|
|
|
case SNDRV_PCM_STATE_XRUN:
|
|
|
err = -EPIPE;
|
|
@@ -1848,8 +1867,10 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream,
|
|
|
}
|
|
|
}
|
|
|
_end_unlock:
|
|
|
+ runtime->nowake = 0;
|
|
|
+ if (xfer > 0 && err >= 0)
|
|
|
+ snd_pcm_update_state(substream, runtime);
|
|
|
snd_pcm_stream_unlock_irq(substream);
|
|
|
- _end:
|
|
|
return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
|
|
|
}
|
|
|
|
|
@@ -2007,6 +2028,7 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,
|
|
|
goto _end_unlock;
|
|
|
}
|
|
|
|
|
|
+ runtime->nowake = 1;
|
|
|
while (size > 0) {
|
|
|
snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
|
|
|
snd_pcm_uframes_t avail;
|
|
@@ -2035,15 +2057,17 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,
|
|
|
if (frames > cont)
|
|
|
frames = cont;
|
|
|
if (snd_BUG_ON(!frames)) {
|
|
|
+ runtime->nowake = 0;
|
|
|
snd_pcm_stream_unlock_irq(substream);
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
appl_ptr = runtime->control->appl_ptr;
|
|
|
appl_ofs = appl_ptr % runtime->buffer_size;
|
|
|
snd_pcm_stream_unlock_irq(substream);
|
|
|
- if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0)
|
|
|
- goto _end;
|
|
|
+ err = transfer(substream, appl_ofs, data, offset, frames);
|
|
|
snd_pcm_stream_lock_irq(substream);
|
|
|
+ if (err < 0)
|
|
|
+ goto _end_unlock;
|
|
|
switch (runtime->status->state) {
|
|
|
case SNDRV_PCM_STATE_XRUN:
|
|
|
err = -EPIPE;
|
|
@@ -2066,8 +2090,10 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,
|
|
|
xfer += frames;
|
|
|
}
|
|
|
_end_unlock:
|
|
|
+ runtime->nowake = 0;
|
|
|
+ if (xfer > 0 && err >= 0)
|
|
|
+ snd_pcm_update_state(substream, runtime);
|
|
|
snd_pcm_stream_unlock_irq(substream);
|
|
|
- _end:
|
|
|
return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
|
|
|
}
|
|
|
|