From 8bea869c5e56234990e6bad92a543437115bfc18 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 27 Apr 2009 09:44:40 +0200 Subject: ALSA: PCM midlevel: improve fifo_size handling Move the fifo_size assignment to hw->ioctl callback to allow lowlevel drivers overwrite the default behaviour. fifo_size is in frames not bytes as specified in asound.h and alsa-lib's documentation, but most hardware have fixed byte based FIFOs. Introduce internal SNDRV_PCM_INFO_FIFO_IN_FRAMES. Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai --- include/sound/asound.h | 1 + include/sound/pcm.h | 1 + sound/core/pcm_lib.c | 19 +++++++++++++++++++ sound/core/pcm_native.c | 15 ++++++++++++--- 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/include/sound/asound.h b/include/sound/asound.h index 6add80fc251..82aed3f4753 100644 --- a/include/sound/asound.h +++ b/include/sound/asound.h @@ -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 */ diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 267effddb07..8a69b5c1e1c 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -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 diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index d659995ac3a..adc2b0bd113 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1524,6 +1524,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 @@ -1545,6 +1562,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; } diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 45dc53fcfa2..84da3ba17c8 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -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(¶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; return 0; } -- cgit v1.2.3 From c62a01ad6e746fae9c93f51ea67e0abfd8d94b58 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 28 May 2009 11:21:52 +0200 Subject: ALSA: PCM midlevel: introduce mask for xrun_debug() macro For debugging purposes, it is better to separate actions. Bit-values: 1: show bad PCM ring buffer pointer 2: show also stack (to debug kernel latency issues) 4: check pointer against system jiffies Example: 5: show bad PCM ring buffer pointer and do jiffies check Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai --- sound/core/pcm_lib.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index adc2b0bd113..25cb36710ef 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -126,20 +126,20 @@ 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) \ - dump_stack(); \ +#define dump_stack_on_xrun(substream) do { \ + if (xrun_debug(substream, 2)) \ + dump_stack(); \ } while (0) static void xrun(struct snd_pcm_substream *substream) { snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); - if (xrun_debug(substream)) { + if (xrun_debug(substream, 1)) { snd_printd(KERN_DEBUG "XRUN: pcmC%dD%d%c\n", substream->pcm->card->number, substream->pcm->device, @@ -197,7 +197,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); \ } \ @@ -251,7 +251,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. @@ -342,7 +342,7 @@ 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) && + if (xrun_debug(substream, 4) && ((delta * HZ) / runtime->rate) > jdelta + HZ/100) { hw_ptr_error(substream, "hw_ptr skipping! " -- cgit v1.2.3 From 13f040f9e55d41e92e485389123654971e03b819 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 28 May 2009 11:31:20 +0200 Subject: ALSA: PCM midlevel: Do not update hw_ptr_jiffies when hw_ptr is not changed Some hardware might have bigger FIFOs and DMA pointer value will be updated in large chunks. Do not update hw_ptr_jiffies and position timestamp when hw_ptr value was not changed. Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai --- sound/core/pcm_lib.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 25cb36710ef..0f299a5ad6d 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -138,6 +138,10 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram 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)) { snd_printd(KERN_DEBUG "XRUN: pcmC%dD%d%c\n", @@ -154,8 +158,6 @@ 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 */ @@ -298,10 +300,15 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) 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); } @@ -356,9 +363,14 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) 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); } -- cgit v1.2.3 From a4444da31ec92f89cd6923579c20a9c240439cfc Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 28 May 2009 12:31:56 +0200 Subject: ALSA: PCM midlevel: lower jiffies check margin using runtime->delay value When hardware has large FIFO, it is necessary to lower jiffies margin by count of queued samples. Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai --- sound/core/pcm_lib.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 0f299a5ad6d..dd9126b92c1 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -263,6 +263,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 / @@ -349,8 +352,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, 4) && - ((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", @@ -359,6 +366,7 @@ 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); -- cgit v1.2.3 From d86bf92313bfd47885a92c7de63bde392d585f95 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Sat, 6 Jun 2009 18:32:06 +0200 Subject: ALSA: pcm - Fix a typo in hw_ptr update check Fix a typo in the commit 13f040f9e55d41e92e485389123654971e03b819 ALSA: PCM midlevel: Do not update hw_ptr_jiffies when hw_ptr is not changed which causes obvious problems with PA. Signed-off-by: Takashi Iwai --- sound/core/pcm_lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index dd9126b92c1..bf34603a7a3 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -371,7 +371,7 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) runtime->silence_size > 0) snd_pcm_playback_silence(substream, new_hw_ptr); - if (runtime->status->hw_ptr != new_hw_ptr) + if (runtime->status->hw_ptr == new_hw_ptr) return 0; runtime->hw_ptr_base = hw_base; -- cgit v1.2.3 From ab1863fc9bc18c806338564124b1e5e7e3ef53d1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 7 Jun 2009 12:09:17 +0200 Subject: ALSA: pcm - Fix update of runtime->hw_ptr_interrupt The commit 13f040f9e55d41e92e485389123654971e03b819 made another regression, the missing update of runtime->hw_ptr_interrupt. Since this field is only checked in snd_pcmupdate__hw_ptr_interrupt(), not in snd_pcm_update_hw_ptr(), it must be updated before the hw_ptr change check. Signed-off-by: Takashi Iwai --- sound/core/pcm_lib.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index bf34603a7a3..adb306fd552 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -299,6 +299,8 @@ 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); @@ -309,7 +311,6 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) 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); -- cgit v1.2.3 From c00701101b82f2bc61dfc259748ec6e5288af6a9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 8 Jun 2009 15:58:48 +0200 Subject: ALSA: pcm - A helper function to compose PCM stream name for debug prints Use a common helper function for the PCM stream name displayed in XRUN and buffer-pointer debug prints. Signed-off-by: Takashi Iwai --- sound/core/pcm_lib.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index adb306fd552..2288fa07bf5 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -136,6 +136,16 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram dump_stack(); \ } while (0) +static void pcm_debug_name(struct snd_pcm_substream *substream, + char *name, size_t len) +{ + snprintf(name, len, "pcmC%dD%d%c:%d", + substream->pcm->card->number, + substream->pcm->device, + substream->stream ? 'c' : 'p', + substream->number); +} + static void xrun(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -144,10 +154,9 @@ static void xrun(struct snd_pcm_substream *substream) snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); if (xrun_debug(substream, 1)) { - snd_printd(KERN_DEBUG "XRUN: pcmC%dD%d%c\n", - substream->pcm->card->number, - substream->pcm->device, - substream->stream ? 'c' : 'p'); + char name[16]; + pcm_debug_name(substream, name, sizeof(name)); + snd_printd(KERN_DEBUG "XRUN: %s\n", name); dump_stack_on_xrun(substream); } } @@ -163,9 +172,11 @@ snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream, 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; -- cgit v1.2.3