From 681b84e17747e1c208e8e1acc54cc5e612da84d1 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Fri, 18 Dec 2009 09:29:00 +0100 Subject: sound: pcm: add vmalloc buffer helper functions There are now five copies of the code to allocate a PCM buffer using vmalloc(). Add a sixth in the core so that the others can be removed. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/core/pcm_memory.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) (limited to 'sound/core') diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c index caa7796bc2f..d9727c74b2e 100644 --- a/sound/core/pcm_memory.c +++ b/sound/core/pcm_memory.c @@ -434,3 +434,57 @@ int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream) } EXPORT_SYMBOL(snd_pcm_lib_free_pages); + +int _snd_pcm_lib_alloc_vmalloc_buffer(struct snd_pcm_substream *substream, + size_t size, gfp_t gfp_flags) +{ + struct snd_pcm_runtime *runtime; + + if (PCM_RUNTIME_CHECK(substream)) + return -EINVAL; + runtime = substream->runtime; + if (runtime->dma_area) { + if (runtime->dma_bytes >= size) + return 0; /* already large enough */ + vfree(runtime->dma_area); + } + runtime->dma_area = __vmalloc(size, gfp_flags, PAGE_KERNEL); + if (!runtime->dma_area) + return -ENOMEM; + runtime->dma_bytes = size; + return 1; +} +EXPORT_SYMBOL(_snd_pcm_lib_alloc_vmalloc_buffer); + +/** + * snd_pcm_lib_free_vmalloc_buffer - free vmalloc buffer + * @substream: the substream with a buffer allocated by + * snd_pcm_lib_alloc_vmalloc_buffer() + */ +int snd_pcm_lib_free_vmalloc_buffer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime; + + if (PCM_RUNTIME_CHECK(substream)) + return -EINVAL; + runtime = substream->runtime; + vfree(runtime->dma_area); + runtime->dma_area = NULL; + return 0; +} +EXPORT_SYMBOL(snd_pcm_lib_free_vmalloc_buffer); + +/** + * snd_pcm_lib_get_vmalloc_page - map vmalloc buffer offset to page struct + * @substream: the substream with a buffer allocated by + * snd_pcm_lib_alloc_vmalloc_buffer() + * @offset: offset in the buffer + * + * This function is to be used as the page callback in the PCM ops. + */ +struct page *snd_pcm_lib_get_vmalloc_page(struct snd_pcm_substream *substream, + unsigned long offset) +{ + return vmalloc_to_page(substream->runtime->dma_area + offset); +} +EXPORT_SYMBOL(snd_pcm_lib_get_vmalloc_page); -- cgit v1.2.3-18-g5258 From ee7c343c0134bf126b4235e65c407711b77174da Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 21 Dec 2009 12:41:37 +0100 Subject: ALSA: pcm - Add missing inclusion of linux/vmalloc.h Signed-off-by: Takashi Iwai --- sound/core/pcm_memory.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/core') diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c index d9727c74b2e..d6d49d6651f 100644 --- a/sound/core/pcm_memory.c +++ b/sound/core/pcm_memory.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3-18-g5258 From 8374e24c23448cabf6e78db2c83841c56c5df1e1 Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Mon, 21 Dec 2009 17:07:08 +0100 Subject: ALSA: refine rate selection in snd_interval_ratnum() Refine the rate selection by choosing the rate closer to the requested one in case of selecting single frequency. Previously, the higher rate was always selected. Also, fix problem with the best_diff unsigned int value wrapping (turning negative). Signed-off-by: Krzysztof Helt Signed-off-by: Takashi Iwai --- sound/core/pcm_lib.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'sound/core') diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index a27545b23ee..b07cc361afb 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -745,10 +745,13 @@ int snd_interval_ratnum(struct snd_interval *i, unsigned int rats_count, struct snd_ratnum *rats, unsigned int *nump, unsigned int *denp) { - unsigned int best_num, best_diff, best_den; + unsigned int best_num, best_den; + int best_diff; unsigned int k; struct snd_interval t; int err; + unsigned int result_num, result_den; + int result_diff; best_num = best_den = best_diff = 0; for (k = 0; k < rats_count; ++k) { @@ -770,6 +773,8 @@ int snd_interval_ratnum(struct snd_interval *i, den -= r; } diff = num - q * den; + if (diff < 0) + diff = -diff; if (best_num == 0 || diff * best_den < best_diff * den) { best_diff = diff; @@ -784,6 +789,9 @@ int snd_interval_ratnum(struct snd_interval *i, t.min = div_down(best_num, best_den); t.openmin = !!(best_num % best_den); + result_num = best_num; + result_diff = best_diff; + result_den = best_den; best_num = best_den = best_diff = 0; for (k = 0; k < rats_count; ++k) { unsigned int num = rats[k].num; @@ -806,6 +814,8 @@ int snd_interval_ratnum(struct snd_interval *i, den += rats[k].den_step - r; } diff = q * den - num; + if (diff < 0) + diff = -diff; if (best_num == 0 || diff * best_den < best_diff * den) { best_diff = diff; @@ -825,10 +835,14 @@ int snd_interval_ratnum(struct snd_interval *i, return err; if (snd_interval_single(i)) { + if (best_diff * result_den < result_diff * best_den) { + result_num = best_num; + result_den = best_den; + } if (nump) - *nump = best_num; + *nump = result_num; if (denp) - *denp = best_den; + *denp = result_den; } return err; } -- cgit v1.2.3-18-g5258 From 741b20cfb9109760937f403d18d731bfde31f56f Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 17 Dec 2009 17:34:39 +0100 Subject: ALSA: pcm_lib.c - convert second xrun_debug() parameter to use defines To increase code readability, convert send xrun_debug() argument to use defines. Signed-off-by: Jaroslav Kysela --- sound/core/pcm_lib.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) (limited to 'sound/core') diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 30f410832a2..9621236b2fe 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -126,15 +126,22 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram } } +#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 */ + #ifdef CONFIG_SND_PCM_XRUN_DEBUG -#define xrun_debug(substream, mask) ((substream)->pstr->xrun_debug & (mask)) +#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(); \ +#define dump_stack_on_xrun(substream) do { \ + if (xrun_debug(substream, XRUN_DEBUG_STACK)) \ + dump_stack(); \ } while (0) static void pcm_debug_name(struct snd_pcm_substream *substream, @@ -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); @@ -215,7 +222,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, 1)) { \ + if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \ if (printk_ratelimit()) { \ snd_printd("PCM: " fmt, ##args); \ } \ @@ -237,7 +244,7 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) xrun(substream); return -EPIPE; } - if (xrun_debug(substream, 8)) { + if (xrun_debug(substream, XRUN_DEBUG_PERIODUPDATE)) { char name[16]; pcm_debug_name(substream, name, sizeof(name)); snd_printd("period_update: %s: pos=0x%x/0x%x/0x%x, " @@ -290,7 +297,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, 4)) + if (!xrun_debug(substream, XRUN_DEBUG_JIFFIESCHECK)) goto no_jiffies_check; /* Skip the jiffies check for hardwares with BATCH flag. @@ -369,7 +376,7 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) xrun(substream); return -EPIPE; } - if (xrun_debug(substream, 16)) { + if (xrun_debug(substream, XRUN_DEBUG_HWPTRUPDATE)) { char name[16]; pcm_debug_name(substream, name, sizeof(name)); snd_printd("hw_update: %s: pos=0x%x/0x%x/0x%x, " @@ -403,7 +410,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, 4)) + if (!xrun_debug(substream, XRUN_DEBUG_JIFFIESCHECK)) goto no_jiffies_check; if (delta < runtime->delay) goto no_jiffies_check; -- cgit v1.2.3-18-g5258 From 4d96eb255c53ab5e39b37fd4d484ea3dc39ab456 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Sun, 20 Dec 2009 11:47:57 +0100 Subject: ALSA: pcm_lib - add possibility to log last 10 DMA ring buffer positions In some debug cases, it might be usefull to see previous ring buffer positions to determine position problems from the lowlevel drivers. Signed-off-by: Jaroslav Kysela --- sound/core/pcm.c | 4 ++ sound/core/pcm_lib.c | 140 ++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 121 insertions(+), 23 deletions(-) (limited to 'sound/core') diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 6884ae031f6..df57a0e30bf 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -921,6 +921,10 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream) snd_free_pages((void*)runtime->control, PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control))); kfree(runtime->hw_constraints.rules); +#ifdef CONFIG_SND_PCM_XRUN_DEBUG + if (runtime->hwptr_log) + kfree(runtime->hwptr_log); +#endif kfree(runtime); substream->runtime = NULL; put_pid(substream->pid); diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 9621236b2fe..1990afb8a73 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -126,34 +126,34 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram } } +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); +} + #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)) -#else -#define xrun_debug(substream, mask) 0 -#endif #define dump_stack_on_xrun(substream) do { \ if (xrun_debug(substream, XRUN_DEBUG_STACK)) \ 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; @@ -169,6 +169,102 @@ static void xrun(struct snd_pcm_substream *substream) } } +#define hw_ptr_error(substream, fmt, args...) \ + do { \ + if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \ + 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; + snd_pcm_uframes_t hw_ptr_interrupt; +}; + +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; + } + 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; + entry->hw_ptr_interrupt = runtime->hw_ptr_interrupt;; + log->idx = (log->idx + 1) % XRUN_LOG_CNT; +} + +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=0x%lx/0x%lx/0x%lx, " + "hwptr=0x%lx, hw_base=0x%lx, hw_intr=0x%lx\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, + (unsigned long)entry->hw_ptr_interrupt); + 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 + static snd_pcm_uframes_t snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime) @@ -182,6 +278,7 @@ snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream, if (printk_ratelimit()) { char name[16]; pcm_debug_name(substream, name, sizeof(name)); + xrun_log_show(substream); snd_printd(KERN_ERR "BUG: %s, pos = 0x%lx, " "buffer size = 0x%lx, period size = 0x%lx\n", name, pos, runtime->buffer_size, @@ -190,6 +287,8 @@ snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream, pos = 0; } pos -= pos % runtime->min_align; + if (xrun_debug(substream, XRUN_DEBUG_LOG)) + xrun_log(substream, pos); return pos; } @@ -220,16 +319,6 @@ static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream, return 0; } -#define hw_ptr_error(substream, fmt, args...) \ - do { \ - if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \ - 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; @@ -270,6 +359,7 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) if (runtime->periods == 1 || new_hw_ptr < old_hw_ptr) delta += runtime->buffer_size; if (delta < 0) { + xrun_log_show(substream); hw_ptr_error(substream, "Unexpected hw_pointer value " "(stream=%i, pos=%ld, intr_ptr=%ld)\n", @@ -315,6 +405,7 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) delta = jdelta / (((runtime->period_size * HZ) / runtime->rate) + HZ/100); + xrun_log_show(substream); hw_ptr_error(substream, "hw_ptr skipping! [Q] " "(pos=%ld, delta=%ld, period=%ld, " @@ -334,6 +425,7 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) } no_jiffies_check: if (delta > runtime->period_size + runtime->period_size / 2) { + xrun_log_show(substream); hw_ptr_error(substream, "Lost interrupts? " "(stream=%i, delta=%ld, intr_ptr=%ld)\n", @@ -397,6 +489,7 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) if (delta < 0) { delta += runtime->buffer_size; if (delta < 0) { + xrun_log_show(substream); hw_ptr_error(substream, "Unexpected hw_pointer value [2] " "(stream=%i, pos=%ld, old_ptr=%ld, jdelta=%li)\n", @@ -416,6 +509,7 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) goto no_jiffies_check; delta -= runtime->delay; if (((delta * HZ) / runtime->rate) > jdelta + HZ/100) { + xrun_log_show(substream); hw_ptr_error(substream, "hw_ptr skipping! " "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu)\n", -- cgit v1.2.3-18-g5258 From f240406babfe1526998e10583ea5eccc2676a433 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 5 Jan 2010 17:19:34 +0100 Subject: ALSA: pcm_lib - cleanup & merge hw_ptr update functions Do general cleanup in snd_pcm_update_hw_ptr*() routines and merge them. The main change is hw_ptr_interrupt variable removal to simplify code logic. This variable can be computed directly from hw_ptr. Ensure that updated hw_ptr is not lower than previous one (it was possible with old code in some obscure situations when interrupt was delayed or the lowlevel driver returns wrong ring buffer position value). Signed-off-by: Jaroslav Kysela --- sound/core/oss/pcm_oss.c | 32 ++++-- sound/core/pcm_lib.c | 279 ++++++++++++++++------------------------------- sound/core/pcm_native.c | 2 - 3 files changed, 120 insertions(+), 193 deletions(-) (limited to 'sound/core') diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index d9c96353121..255ad910077 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -632,6 +632,13 @@ static long snd_pcm_alsa_frames(struct snd_pcm_substream *substream, long bytes) return bytes_to_frames(runtime, (buffer_size * bytes) / runtime->oss.buffer_bytes); } +static inline +snd_pcm_uframes_t get_hw_ptr_period(struct snd_pcm_runtime *runtime) +{ + snd_pcm_uframes_t ptr = runtime->status->hw_ptr; + return ptr - (ptr % runtime->period_size); +} + /* define extended formats in the recent OSS versions (if any) */ /* linear formats */ #define AFMT_S32_LE 0x00001000 @@ -1102,7 +1109,7 @@ static int snd_pcm_oss_prepare(struct snd_pcm_substream *substream) return err; } runtime->oss.prepare = 0; - runtime->oss.prev_hw_ptr_interrupt = 0; + runtime->oss.prev_hw_ptr_period = 0; runtime->oss.period_ptr = 0; runtime->oss.buffer_used = 0; @@ -1950,7 +1957,8 @@ static int snd_pcm_oss_get_caps(struct snd_pcm_oss_file *pcm_oss_file) return result; } -static void snd_pcm_oss_simulate_fill(struct snd_pcm_substream *substream, snd_pcm_uframes_t hw_ptr) +static void snd_pcm_oss_simulate_fill(struct snd_pcm_substream *substream, + snd_pcm_uframes_t hw_ptr) { struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t appl_ptr; @@ -1986,7 +1994,8 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr if (runtime->oss.trigger) goto _skip1; if (atomic_read(&psubstream->mmap_count)) - snd_pcm_oss_simulate_fill(psubstream, runtime->hw_ptr_interrupt); + snd_pcm_oss_simulate_fill(psubstream, + get_hw_ptr_period(runtime)); runtime->oss.trigger = 1; runtime->start_threshold = 1; cmd = SNDRV_PCM_IOCTL_START; @@ -2105,11 +2114,12 @@ static int snd_pcm_oss_get_ptr(struct snd_pcm_oss_file *pcm_oss_file, int stream info.ptr = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr % runtime->buffer_size); if (atomic_read(&substream->mmap_count)) { snd_pcm_sframes_t n; - n = (delay = runtime->hw_ptr_interrupt) - runtime->oss.prev_hw_ptr_interrupt; + delay = get_hw_ptr_period(runtime); + n = delay - runtime->oss.prev_hw_ptr_period; if (n < 0) n += runtime->boundary; info.blocks = n / runtime->period_size; - runtime->oss.prev_hw_ptr_interrupt = delay; + runtime->oss.prev_hw_ptr_period = delay; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) snd_pcm_oss_simulate_fill(substream, delay); info.bytes = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr) & INT_MAX; @@ -2673,18 +2683,22 @@ static int snd_pcm_oss_playback_ready(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; if (atomic_read(&substream->mmap_count)) - return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt; + return runtime->oss.prev_hw_ptr_period != + get_hw_ptr_period(runtime); else - return snd_pcm_playback_avail(runtime) >= runtime->oss.period_frames; + return snd_pcm_playback_avail(runtime) >= + runtime->oss.period_frames; } static int snd_pcm_oss_capture_ready(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; if (atomic_read(&substream->mmap_count)) - return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt; + return runtime->oss.prev_hw_ptr_period != + get_hw_ptr_period(runtime); else - return snd_pcm_capture_avail(runtime) >= runtime->oss.period_frames; + return snd_pcm_capture_avail(runtime) >= + runtime->oss.period_frames; } static unsigned int snd_pcm_oss_poll(struct file *file, poll_table * wait) diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 1990afb8a73..70a4f7428d7 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -172,6 +172,7 @@ static void xrun(struct snd_pcm_substream *substream) #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); \ } \ @@ -188,7 +189,6 @@ struct hwptr_log_entry { snd_pcm_uframes_t buffer_size; snd_pcm_uframes_t old_hw_ptr; snd_pcm_uframes_t hw_ptr_base; - snd_pcm_uframes_t hw_ptr_interrupt; }; struct snd_pcm_hwptr_log { @@ -220,7 +220,6 @@ static void xrun_log(struct snd_pcm_substream *substream, entry->buffer_size = runtime->buffer_size;; entry->old_hw_ptr = runtime->status->hw_ptr; entry->hw_ptr_base = runtime->hw_ptr_base; - entry->hw_ptr_interrupt = runtime->hw_ptr_interrupt;; log->idx = (log->idx + 1) % XRUN_LOG_CNT; } @@ -241,14 +240,13 @@ static void xrun_log_show(struct snd_pcm_substream *substream) entry = &log->entries[idx]; if (entry->period_size == 0) break; - snd_printd("hwptr log: %s: j=%lu, pos=0x%lx/0x%lx/0x%lx, " - "hwptr=0x%lx, hw_base=0x%lx, hw_intr=0x%lx\n", + 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, - (unsigned long)entry->hw_ptr_interrupt); + (unsigned long)entry->hw_ptr_base); idx++; idx %= XRUN_LOG_CNT; } @@ -265,33 +263,6 @@ static void xrun_log_show(struct snd_pcm_substream *substream) #endif -static snd_pcm_uframes_t -snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream, - struct snd_pcm_runtime *runtime) -{ - snd_pcm_uframes_t pos; - - 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)); - xrun_log_show(substream); - 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; - } - pos -= pos % runtime->min_align; - if (xrun_debug(substream, XRUN_DEBUG_LOG)) - xrun_log(substream, pos); - return pos; -} - static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime) { @@ -319,72 +290,88 @@ static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream, return 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, XRUN_DEBUG_PERIODUPDATE)) { - 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) { - xrun_log_show(substream); - 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) { + 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, XRUN_DEBUG_JIFFIESCHECK)) @@ -396,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; @@ -405,45 +392,49 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) delta = jdelta / (((runtime->period_size * HZ) / runtime->rate) + HZ/100); - xrun_log_show(substream); + /* 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) { - xrun_log_show(substream); 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; @@ -456,83 +447,7 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) /* 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, XRUN_DEBUG_HWPTRUPDATE)) { - 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) { - xrun_log_show(substream); - 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, XRUN_DEBUG_JIFFIESCHECK)) - goto no_jiffies_check; - if (delta < runtime->delay) - goto no_jiffies_check; - delta -= runtime->delay; - if (((delta * HZ) / runtime->rate) > jdelta + HZ/100) { - xrun_log_show(substream); - 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); } /** @@ -1744,7 +1659,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) diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 29ab46a12e1..8e777f71717 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -1247,8 +1247,6 @@ static int snd_pcm_do_reset(struct snd_pcm_substream *substream, int state) if (err < 0) return err; runtime->hw_ptr_base = 0; - runtime->hw_ptr_interrupt = runtime->status->hw_ptr - - runtime->status->hw_ptr % runtime->period_size; runtime->silence_start = runtime->status->hw_ptr; runtime->silence_filled = 0; return 0; -- cgit v1.2.3-18-g5258 From 1250932e48d3b698415b1f04775433cf1da688d6 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 7 Jan 2010 15:36:31 +0100 Subject: ALSA: pcm_lib - optimize wake_up() calls for PCM I/O As noted by pl bossart , the PCM I/O routines (snd_pcm_lib_write1, snd_pcm_lib_read1) should block wake_up() calls until all samples are not processed. Signed-off-by: Jaroslav Kysela --- sound/core/pcm_lib.c | 30 ++++++++++++++++++++---------- sound/core/pcm_native.c | 6 ++++-- 2 files changed, 24 insertions(+), 12 deletions(-) (limited to 'sound/core') diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 70a4f7428d7..a63226232ef 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -263,8 +263,8 @@ static void xrun_log_show(struct snd_pcm_substream *substream) #endif -static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream, - struct snd_pcm_runtime *runtime) +int snd_pcm_update_state(struct snd_pcm_substream *substream, + struct snd_pcm_runtime *runtime) { snd_pcm_uframes_t avail; @@ -285,7 +285,7 @@ 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; } @@ -441,7 +441,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, 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 */ @@ -1792,6 +1792,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; @@ -1813,15 +1814,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; @@ -1850,8 +1853,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; } @@ -2009,6 +2014,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; @@ -2037,15 +2043,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; @@ -2068,8 +2076,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; } diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 8e777f71717..27284f62836 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -516,6 +516,7 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream, struct snd_pcm_sw_params *params) { struct snd_pcm_runtime *runtime; + int err; if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; @@ -540,6 +541,7 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream, if (params->silence_threshold > runtime->buffer_size) return -EINVAL; } + err = 0; snd_pcm_stream_lock_irq(substream); runtime->tstamp_mode = params->tstamp_mode; runtime->period_step = params->period_step; @@ -553,10 +555,10 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && runtime->silence_size > 0) snd_pcm_playback_silence(substream, ULONG_MAX); - wake_up(&runtime->sleep); + err = snd_pcm_update_state(substream, runtime); } snd_pcm_stream_unlock_irq(substream); - return 0; + return err; } static int snd_pcm_sw_params_user(struct snd_pcm_substream *substream, -- cgit v1.2.3-18-g5258 From 7b3a177b0d4f92b3431b8dca777313a07533a710 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 8 Jan 2010 08:43:01 +0100 Subject: ALSA: pcm_lib: fix "something must be really wrong" condition When runtime->periods == 1 or when pointer crosses end of ring buffer, the delta might be greater than buffer_size. Signed-off-by: Jaroslav Kysela --- sound/core/pcm_lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/core') diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index a63226232ef..c7b35b20e65 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -362,7 +362,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, (unsigned long)runtime->hw_ptr_base); } /* something must be really wrong */ - if (delta >= runtime->buffer_size) { + 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, " -- cgit v1.2.3-18-g5258 From ed69c6a8eef679f2783848ed624897a937a434ac Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 13 Jan 2010 08:12:31 +0100 Subject: ALSA: pcm_lib - fix wrong delta print for jiffies check The previous jiffies delta was 0 in all cases. Use hw_ptr variable to store and print original value. Signed-off-by: Jaroslav Kysela --- sound/core/pcm_lib.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'sound/core') diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 0ee7e807c96..5417f7dce83 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -394,6 +394,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, + HZ/100); /* move new_hw_ptr according jiffies not pos variable */ new_hw_ptr = old_hw_ptr; + hw_base = delta; /* use loop to avoid checks for delta overflows */ /* the delta value is small or zero in most cases */ while (delta > 0) { @@ -403,8 +404,6 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, 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! %s" "(pos=%ld, delta=%ld, period=%ld, " @@ -412,9 +411,12 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, in_interrupt ? "[Q] " : "", (long)pos, (long)hdelta, (long)runtime->period_size, jdelta, - ((hdelta * HZ) / runtime->rate), delta, + ((hdelta * HZ) / runtime->rate), hw_base, (unsigned long)old_hw_ptr, (unsigned long)new_hw_ptr); + /* reset values to proper state */ + delta = 0; + hw_base = new_hw_ptr - (new_hw_ptr % runtime->buffer_size); } no_jiffies_check: if (delta > runtime->period_size + runtime->period_size / 2) { -- cgit v1.2.3-18-g5258 From d1458279bf9c575a52fd22818ca19c463f380aba Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 14 Jan 2010 09:16:52 +0100 Subject: ALSA: Add snd_pci_quirk_lookup_id() Added a new function to look up a quirk entry with the given PCI SSID instead of a pci device pointer. This can be used when the searched ID is overridden for debugging or such a purpose. Signed-off-by: Takashi Iwai --- sound/core/misc.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) (limited to 'sound/core') diff --git a/sound/core/misc.c b/sound/core/misc.c index 23a032c6d48..3da4f92427d 100644 --- a/sound/core/misc.c +++ b/sound/core/misc.c @@ -101,8 +101,9 @@ EXPORT_SYMBOL_GPL(__snd_printk); #ifdef CONFIG_PCI #include /** - * snd_pci_quirk_lookup - look up a PCI SSID quirk list - * @pci: pci_dev handle + * snd_pci_quirk_lookup_id - look up a PCI SSID quirk list + * @vendor: PCI SSV id + * @device: PCI SSD id * @list: quirk list, terminated by a null entry * * Look through the given quirk list and finds a matching entry @@ -112,18 +113,39 @@ EXPORT_SYMBOL_GPL(__snd_printk); * Returns the matched entry pointer, or NULL if nothing matched. */ const struct snd_pci_quirk * -snd_pci_quirk_lookup(struct pci_dev *pci, const struct snd_pci_quirk *list) +snd_pci_quirk_lookup_id(u16 vendor, u16 device, + const struct snd_pci_quirk *list) { const struct snd_pci_quirk *q; for (q = list; q->subvendor; q++) { - if (q->subvendor != pci->subsystem_vendor) + if (q->subvendor != vendor) continue; if (!q->subdevice || - (pci->subsystem_device & q->subdevice_mask) == q->subdevice) + (device & q->subdevice_mask) == q->subdevice) return q; } return NULL; } +EXPORT_SYMBOL(snd_pci_quirk_lookup_id); + +/** + * snd_pci_quirk_lookup - look up a PCI SSID quirk list + * @pci: pci_dev handle + * @list: quirk list, terminated by a null entry + * + * Look through the given quirk list and finds a matching entry + * with the same PCI SSID. When subdevice is 0, all subdevice + * values may match. + * + * Returns the matched entry pointer, or NULL if nothing matched. + */ +const struct snd_pci_quirk * +snd_pci_quirk_lookup(struct pci_dev *pci, const struct snd_pci_quirk *list) +{ + return snd_pci_quirk_lookup_id(pci->subsystem_vendor, + pci->subsystem_device, + list); +} EXPORT_SYMBOL(snd_pci_quirk_lookup); #endif -- cgit v1.2.3-18-g5258 From 3e879d7bac705be4813a0ec9560cbe31db4b269f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 18 Jan 2010 14:49:50 +0100 Subject: ALSA: pcm - Remove unneeded ifdef pgprot_noncached Signed-off-by: Takashi Iwai --- sound/core/pcm_native.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'sound/core') diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index a870fe69657..5df0d21f18b 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -3162,9 +3162,7 @@ int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream, long size; unsigned long offset; -#ifdef pgprot_noncached area->vm_page_prot = pgprot_noncached(area->vm_page_prot); -#endif area->vm_flags |= VM_IO; size = area->vm_end - area->vm_start; offset = area->vm_pgoff << PAGE_SHIFT; -- cgit v1.2.3-18-g5258 From c32d977b8157bf67cdf47729ce7dd054a26eb534 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 18 Jan 2010 14:58:57 +0100 Subject: ALSA: pcm - Call pgprot_noncached() for vmalloc'ed buffers pgprot_noncached() can be set for vmalloc'ed buffers safely, and we'd need non-cached behavior more or less, even for the intermediate ring- buffers. Now snd_pcm_lib_mmap_vmalloc() is added as the common PCM mmap callback that is coupled with snd_pcm_lib_alloc_vmalloc_buffer() & co. Signed-off-by: Takashi Iwai --- sound/core/pcm_native.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'sound/core') diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 5df0d21f18b..88fff44702a 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -3176,6 +3176,15 @@ int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream, EXPORT_SYMBOL(snd_pcm_lib_mmap_iomem); #endif /* SNDRV_PCM_INFO_MMAP */ +/* mmap callback with pgprot_noncached */ +int snd_pcm_lib_mmap_noncached(struct snd_pcm_substream *substream, + struct vm_area_struct *area) +{ + area->vm_page_prot = pgprot_noncached(area->vm_page_prot); + return snd_pcm_default_mmap(substream, area); +} +EXPORT_SYMBOL(snd_pcm_lib_mmap_noncached); + /* * mmap DMA buffer */ -- cgit v1.2.3-18-g5258 From a32f66746c635ebf2341d99b3d4c0cc1c11b2cbf Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 18 Jan 2010 15:40:56 +0100 Subject: sound: seq_timer: simplify snd_seq_timer_set_tick_resolution() parameters As snd_seq_timer_set_tick_resolution() is always called with the same three fields of struct snd_seq_timer, it suffices to give that as the only parameter. Signed-off-by: Clemens Ladisch Signed-off-by: Jaroslav Kysela --- sound/core/seq/seq_timer.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) (limited to 'sound/core') diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c index f745c317d6a..160b1bd0cd6 100644 --- a/sound/core/seq/seq_timer.c +++ b/sound/core/seq/seq_timer.c @@ -33,22 +33,21 @@ #define SKEW_BASE 0x10000 /* 16bit shift */ -static void snd_seq_timer_set_tick_resolution(struct snd_seq_timer_tick *tick, - int tempo, int ppq) +static void snd_seq_timer_set_tick_resolution(struct snd_seq_timer *tmr) { - if (tempo < 1000000) - tick->resolution = (tempo * 1000) / ppq; + if (tmr->tempo < 1000000) + tmr->tick.resolution = (tmr->tempo * 1000) / tmr->ppq; else { /* might overflow.. */ unsigned int s; - s = tempo % ppq; - s = (s * 1000) / ppq; - tick->resolution = (tempo / ppq) * 1000; - tick->resolution += s; + s = tmr->tempo % tmr->ppq; + s = (s * 1000) / tmr->ppq; + tmr->tick.resolution = (tmr->tempo / tmr->ppq) * 1000; + tmr->tick.resolution += s; } - if (tick->resolution <= 0) - tick->resolution = 1; - snd_seq_timer_update_tick(tick, 0); + if (tmr->tick.resolution <= 0) + tmr->tick.resolution = 1; + snd_seq_timer_update_tick(&tmr->tick, 0); } /* create new timer (constructor) */ @@ -96,7 +95,7 @@ void snd_seq_timer_defaults(struct snd_seq_timer * tmr) /* setup defaults */ tmr->ppq = 96; /* 96 PPQ */ tmr->tempo = 500000; /* 120 BPM */ - snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq); + snd_seq_timer_set_tick_resolution(tmr); tmr->running = 0; tmr->type = SNDRV_SEQ_TIMER_ALSA; @@ -180,7 +179,7 @@ int snd_seq_timer_set_tempo(struct snd_seq_timer * tmr, int tempo) spin_lock_irqsave(&tmr->lock, flags); if ((unsigned int)tempo != tmr->tempo) { tmr->tempo = tempo; - snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq); + snd_seq_timer_set_tick_resolution(tmr); } spin_unlock_irqrestore(&tmr->lock, flags); return 0; @@ -205,7 +204,7 @@ int snd_seq_timer_set_ppq(struct snd_seq_timer * tmr, int ppq) } tmr->ppq = ppq; - snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq); + snd_seq_timer_set_tick_resolution(tmr); spin_unlock_irqrestore(&tmr->lock, flags); return 0; } -- cgit v1.2.3-18-g5258 From c91a988dc6551c66418690e36b2a23cdb0255da8 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 21 Jan 2010 10:32:15 +0100 Subject: ALSA: pcm_core: Fix wake_up() optimization This change fixes the "ALSA: pcm_lib - optimize wake_up() calls for PCM I/O" commit. New sleeping queue is introduced to separate user space and kernel space wake_ups. runtime->nowake is renamed to twake (transfer wake). Signed-off-by: Jaroslav Kysela --- sound/core/pcm.c | 1 + sound/core/pcm_lib.c | 20 ++++++++++---------- sound/core/pcm_native.c | 3 +++ 3 files changed, 14 insertions(+), 10 deletions(-) (limited to 'sound/core') diff --git a/sound/core/pcm.c b/sound/core/pcm.c index df57a0e30bf..0d428d0896d 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -894,6 +894,7 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, memset((void*)runtime->control, 0, size); init_waitqueue_head(&runtime->sleep); + init_waitqueue_head(&runtime->tsleep); runtime->status->state = SNDRV_PCM_STATE_OPEN; diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 5417f7dce83..e2a817eac2a 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -285,8 +285,8 @@ int snd_pcm_update_state(struct snd_pcm_substream *substream, return -EPIPE; } } - if (!runtime->nowake && avail >= runtime->control->avail_min) - wake_up(&runtime->sleep); + if (avail >= runtime->control->avail_min) + wake_up(runtime->twake ? &runtime->tsleep : &runtime->sleep); return 0; } @@ -1692,7 +1692,7 @@ static int wait_for_avail_min(struct snd_pcm_substream *substream, long tout; init_waitqueue_entry(&wait, current); - add_wait_queue(&runtime->sleep, &wait); + add_wait_queue(&runtime->tsleep, &wait); for (;;) { if (signal_pending(current)) { err = -ERESTARTSYS; @@ -1735,7 +1735,7 @@ static int wait_for_avail_min(struct snd_pcm_substream *substream, break; } _endloop: - remove_wait_queue(&runtime->sleep, &wait); + remove_wait_queue(&runtime->tsleep, &wait); *availp = avail; return err; } @@ -1794,7 +1794,7 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, goto _end_unlock; } - runtime->nowake = 1; + runtime->twake = 1; while (size > 0) { snd_pcm_uframes_t frames, appl_ptr, appl_ofs; snd_pcm_uframes_t avail; @@ -1816,7 +1816,7 @@ 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; + runtime->twake = 0; snd_pcm_stream_unlock_irq(substream); return -EINVAL; } @@ -1855,7 +1855,7 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, } } _end_unlock: - runtime->nowake = 0; + runtime->twake = 0; if (xfer > 0 && err >= 0) snd_pcm_update_state(substream, runtime); snd_pcm_stream_unlock_irq(substream); @@ -2016,7 +2016,7 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, goto _end_unlock; } - runtime->nowake = 1; + runtime->twake = 1; while (size > 0) { snd_pcm_uframes_t frames, appl_ptr, appl_ofs; snd_pcm_uframes_t avail; @@ -2045,7 +2045,7 @@ 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; + runtime->twake = 0; snd_pcm_stream_unlock_irq(substream); return -EINVAL; } @@ -2078,7 +2078,7 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, xfer += frames; } _end_unlock: - runtime->nowake = 0; + runtime->twake = 0; if (xfer > 0 && err >= 0) snd_pcm_update_state(substream, runtime); snd_pcm_stream_unlock_irq(substream); diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 27284f62836..56ec35e8510 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -919,6 +919,7 @@ static void snd_pcm_post_stop(struct snd_pcm_substream *substream, int state) runtime->status->state = state; } wake_up(&runtime->sleep); + wake_up(&runtime->tsleep); } static struct action_ops snd_pcm_action_stop = { @@ -1004,6 +1005,7 @@ static void snd_pcm_post_pause(struct snd_pcm_substream *substream, int push) SNDRV_TIMER_EVENT_MPAUSE, &runtime->trigger_tstamp); wake_up(&runtime->sleep); + wake_up(&runtime->tsleep); } else { runtime->status->state = SNDRV_PCM_STATE_RUNNING; if (substream->timer) @@ -1061,6 +1063,7 @@ static void snd_pcm_post_suspend(struct snd_pcm_substream *substream, int state) runtime->status->suspended_state = runtime->status->state; runtime->status->state = SNDRV_PCM_STATE_SUSPENDED; wake_up(&runtime->sleep); + wake_up(&runtime->tsleep); } static struct action_ops snd_pcm_action_suspend = { -- cgit v1.2.3-18-g5258 From e7636925789b042ff9d98c51d48392e8c5549480 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 26 Jan 2010 17:08:24 +0100 Subject: ALSA: pcm_lib - return back hw_ptr_interrupt Clemens Ladisch noted for hw_ptr_removal in "cleanup & merge hw_ptr update functions" commit: "It is possible for the status/delay ioctls to be called when the sound card's pointer register alreay shows a position at the beginning of the new period, but immediately before the interrupt is actually executed. (This happens regularly on a SMP machine with mplayer.) When that happens, the code thinks that the position must be at least one period ahead of the current position and drops an entire buffer of data." Return back the hw_ptr_interrupt variable. The last interrupt pointer is always computed from the latest hw_ptr instead of tracking it separately (in this case all hw_ptr checks and modifications might influence also hw_ptr_interrupt and it is difficult to keep it consistent). Signed-off-by: Jaroslav Kysela --- sound/core/oss/pcm_oss.c | 3 +-- sound/core/pcm_lib.c | 7 +++++-- sound/core/pcm_native.c | 2 ++ 3 files changed, 8 insertions(+), 4 deletions(-) (limited to 'sound/core') diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 255ad910077..82d4e3329b3 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -635,8 +635,7 @@ static long snd_pcm_alsa_frames(struct snd_pcm_substream *substream, long bytes) static inline snd_pcm_uframes_t get_hw_ptr_period(struct snd_pcm_runtime *runtime) { - snd_pcm_uframes_t ptr = runtime->status->hw_ptr; - return ptr - (ptr % runtime->period_size); + return runtime->hw_ptr_interrupt; } /* define extended formats in the recent OSS versions (if any) */ diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index e2a817eac2a..aa54195ef3b 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -325,8 +325,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, 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; + delta = runtime->hw_ptr_interrupt + runtime->period_size; if (delta > new_hw_ptr) { hw_base += runtime->buffer_size; if (hw_base >= runtime->boundary) @@ -437,6 +436,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, runtime->silence_size > 0) snd_pcm_playback_silence(substream, new_hw_ptr); + if (in_interrupt) { + runtime->hw_ptr_interrupt = new_hw_ptr - + (new_hw_ptr % runtime->period_size); + } runtime->hw_ptr_base = hw_base; runtime->status->hw_ptr = new_hw_ptr; runtime->hw_ptr_jiffies = jiffies; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 56ec35e8510..7a002db512b 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -1252,6 +1252,8 @@ static int snd_pcm_do_reset(struct snd_pcm_substream *substream, int state) if (err < 0) return err; runtime->hw_ptr_base = 0; + runtime->hw_ptr_interrupt = runtime->status->hw_ptr - + runtime->status->hw_ptr % runtime->period_size; runtime->silence_start = runtime->status->hw_ptr; runtime->silence_filled = 0; return 0; -- cgit v1.2.3-18-g5258 From 7910b4a1db63fefc3d291853d33c34c5b6352e8e Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 27 Jan 2010 18:10:13 +0100 Subject: ALSA: pcm_native - fix runtime->boundary calculation The code in pcm_lib updating runtime->hw_ptr_interrupt expects that runtime->boundary is divisible with runtime->period_size. Thanks are going to Clemens Ladisch for the notice. Fix the runtime->boundary calculation using buffer_size * period_size as base and find a least common multiple for 32bit platforms when the expression might overflow. Signed-off-by: Jaroslav Kysela --- sound/core/pcm_native.c | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) (limited to 'sound/core') diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 7a002db512b..9cbaf90d3d8 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -366,6 +367,38 @@ static int period_to_usecs(struct snd_pcm_runtime *runtime) return usecs; } +static int calc_boundary(struct snd_pcm_runtime *runtime) +{ + u_int64_t boundary; + + boundary = (u_int64_t)runtime->buffer_size * + (u_int64_t)runtime->period_size; +#if BITS_PER_LONG < 64 + /* try to find lowest common multiple for buffer and period */ + if (boundary > LONG_MAX - runtime->buffer_size) { + u_int32_t remainder = -1; + u_int32_t divident = runtime->buffer_size; + u_int32_t divisor = runtime->period_size; + while (remainder) { + remainder = divident % divisor; + if (remainder) { + divident = divisor; + divisor = remainder; + } + } + boundary = div_u64(boundary, divisor); + if (boundary > LONG_MAX - runtime->buffer_size) + return -ERANGE; + } +#endif + if (boundary == 0) + return -ERANGE; + runtime->boundary = boundary; + while (runtime->boundary * 2 <= LONG_MAX - runtime->buffer_size) + runtime->boundary *= 2; + return 0; +} + static int snd_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -441,9 +474,9 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, runtime->stop_threshold = runtime->buffer_size; runtime->silence_threshold = 0; runtime->silence_size = 0; - runtime->boundary = runtime->buffer_size; - while (runtime->boundary * 2 <= LONG_MAX - runtime->buffer_size) - runtime->boundary *= 2; + err = calc_boundary(runtime); + if (err < 0) + goto _error; snd_pcm_timer_resolution_change(substream); runtime->status->state = SNDRV_PCM_STATE_SETUP; -- cgit v1.2.3-18-g5258 From a75d7a4cf50d1cee14d8c9aaa2b971232d10f2c1 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 1 Feb 2010 13:29:50 +0100 Subject: sound: control: actually allow TLV command access Creating a control with TLV_COMMAND access was not possible because snd_ctl_new1() forgot to include it in the mask of allowable access bits. Signed-off-by: Clemens Ladisch Signed-off-by: Jaroslav Kysela --- sound/core/control.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'sound/core') diff --git a/sound/core/control.c b/sound/core/control.c index 268ab747122..6a4764dcb18 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -237,8 +237,9 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol, access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE| SNDRV_CTL_ELEM_ACCESS_INACTIVE| - SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE| - SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)); + SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE| + SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND| + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)); kctl.info = ncontrol->info; kctl.get = ncontrol->get; kctl.put = ncontrol->put; -- cgit v1.2.3-18-g5258 From 6123637fafbf445cc9ce5774dc9516da0b2daa88 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 1 Feb 2010 13:30:56 +0100 Subject: sound: control: fix mi