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 --- sound/core/pcm_lib.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'sound/core/pcm_lib.c') diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index d659995ac3ac..adc2b0bd1132 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; } -- 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(-) (limited to 'sound/core/pcm_lib.c') diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index adc2b0bd1132..25cb36710ef4 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(-) (limited to 'sound/core/pcm_lib.c') diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 25cb36710ef4..0f299a5ad6da 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(-) (limited to 'sound/core/pcm_lib.c') diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 0f299a5ad6da..dd9126b92c19 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 3f7440a6b771169e1f11fa582e53a4259b682809 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 5 Jun 2009 17:40:04 +0200 Subject: ALSA: Clean up 64bit division functions Replace the house-made div64_32() with the standard div_u64*() functions. Signed-off-by: Takashi Iwai --- include/sound/pcm.h | 74 ----------------------------------------------- sound/core/oss/pcm_oss.c | 5 ++-- sound/core/pcm_lib.c | 3 +- sound/pci/rme9652/hdsp.c | 7 ++--- sound/pci/rme9652/hdspm.c | 4 +-- 5 files changed, 9 insertions(+), 84 deletions(-) (limited to 'sound/core/pcm_lib.c') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index c17296891617..0caf71e16944 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -486,80 +486,6 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream); void snd_pcm_vma_notify_data(void *client, void *data); int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file, struct vm_area_struct *area); -#if BITS_PER_LONG >= 64 - -static inline void div64_32(u_int64_t *n, u_int32_t div, u_int32_t *rem) -{ - *rem = *n % div; - *n /= div; -} - -#elif defined(i386) - -static inline void div64_32(u_int64_t *n, u_int32_t div, u_int32_t *rem) -{ - u_int32_t low, high; - low = *n & 0xffffffff; - high = *n >> 32; - if (high) { - u_int32_t high1 = high % div; - high /= div; - asm("divl %2":"=a" (low), "=d" (*rem):"rm" (div), "a" (low), "d" (high1)); - *n = (u_int64_t)high << 32 | low; - } else { - *n = low / div; - *rem = low % div; - } -} -#else - -static inline void divl(u_int32_t high, u_int32_t low, - u_int32_t div, - u_int32_t *q, u_int32_t *r) -{ - u_int64_t n = (u_int64_t)high << 32 | low; - u_int64_t d = (u_int64_t)div << 31; - u_int32_t q1 = 0; - int c = 32; - while (n > 0xffffffffU) { - q1 <<= 1; - if (n >= d) { - n -= d; - q1 |= 1; - } - d >>= 1; - c--; - } - q1 <<= c; - if (n) { - low = n; - *q = q1 | (low / div); - *r = low % div; - } else { - *r = 0; - *q = q1; - } - return; -} - -static inline void div64_32(u_int64_t *n, u_int32_t div, u_int32_t *rem) -{ - u_int32_t low, high; - low = *n & 0xffffffff; - high = *n >> 32; - if (high) { - u_int32_t high1 = high % div; - u_int32_t low1 = low; - high /= div; - divl(high1, low1, div, &low, rem); - *n = (u_int64_t)high << 32 | low; - } else { - *n = low / div; - *rem = low % div; - } -} -#endif - /* * PCM library */ diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index dda000b9684c..dbe406b82591 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -617,9 +618,7 @@ static long snd_pcm_oss_bytes(struct snd_pcm_substream *substream, long frames) #else { u64 bsize = (u64)runtime->oss.buffer_bytes * (u64)bytes; - u32 rem; - div64_32(&bsize, buffer_size, &rem); - return (long)bsize; + return div_u64(bsize, buffer_size); } #endif } diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index d659995ac3ac..a7482874c451 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -452,7 +453,7 @@ static inline unsigned int muldiv32(unsigned int a, unsigned int b, *r = 0; return UINT_MAX; } - div64_32(&n, c, r); + n = div_u64_rem(n, c, r); if (n >= UINT_MAX) { *r = 0; return UINT_MAX; diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 314e73531bd1..bcfdbb5ebc40 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -1047,7 +1048,6 @@ static int hdsp_set_interrupt_interval(struct hdsp *s, unsigned int frames) static void hdsp_set_dds_value(struct hdsp *hdsp, int rate) { u64 n; - u32 r; if (rate >= 112000) rate /= 4; @@ -1055,7 +1055,7 @@ static void hdsp_set_dds_value(struct hdsp *hdsp, int rate) rate /= 2; n = DDS_NUMERATOR; - div64_32(&n, rate, &r); + n = div_u64(n, rate); /* n should be less than 2^32 for being written to FREQ register */ snd_BUG_ON(n >> 32); /* HDSP_freqReg and HDSP_resetPointer are the same, so keep the DDS @@ -3097,7 +3097,6 @@ static int snd_hdsp_get_adat_sync_check(struct snd_kcontrol *kcontrol, struct sn static int hdsp_dds_offset(struct hdsp *hdsp) { u64 n; - u32 r; unsigned int dds_value = hdsp->dds_value; int system_sample_rate = hdsp->system_sample_rate; @@ -3109,7 +3108,7 @@ static int hdsp_dds_offset(struct hdsp *hdsp) * dds_value = n / rate * rate = n / dds_value */ - div64_32(&n, dds_value, &r); + n = div_u64(n, dds_value); if (system_sample_rate >= 112000) n *= 4; else if (system_sample_rate >= 56000) diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index bac2dc0c5d85..0dce331a2a3b 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -831,7 +832,6 @@ static int hdspm_set_interrupt_interval(struct hdspm * s, unsigned int frames) static void hdspm_set_dds_value(struct hdspm *hdspm, int rate) { u64 n; - u32 r; if (rate >= 112000) rate /= 4; @@ -844,7 +844,7 @@ static void hdspm_set_dds_value(struct hdspm *hdspm, int rate) */ /* n = 104857600000000ULL; */ /* = 2^20 * 10^8 */ n = 110100480000000ULL; /* Value checked for AES32 and MADI */ - div64_32(&n, rate, &r); + n = div_u64(n, rate); /* n should be less than 2^32 for being written to FREQ register */ snd_BUG_ON(n >> 32); hdspm_write(hdspm, HDSPM_freqReg, (u32)n); -- 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(-) (limited to 'sound/core/pcm_lib.c') diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index dd9126b92c19..bf34603a7a38 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(-) (limited to 'sound/core/pcm_lib.c') diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index bf34603a7a38..adb306fd5525 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(-) (limited to 'sound/core/pcm_lib.c') diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index adb306fd5525..2288fa07bf59 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