11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Digital Audio (PCM) abstract layer 3c1017a4cSJaroslav Kysela * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 41da177e4SLinus Torvalds * Abramo Bagnara <abramo@alsa-project.org> 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 81da177e4SLinus Torvalds * it under the terms of the GNU General Public License as published by 91da177e4SLinus Torvalds * the Free Software Foundation; either version 2 of the License, or 101da177e4SLinus Torvalds * (at your option) any later version. 111da177e4SLinus Torvalds * 121da177e4SLinus Torvalds * This program is distributed in the hope that it will be useful, 131da177e4SLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 141da177e4SLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 151da177e4SLinus Torvalds * GNU General Public License for more details. 161da177e4SLinus Torvalds * 171da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License 181da177e4SLinus Torvalds * along with this program; if not, write to the Free Software 191da177e4SLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 201da177e4SLinus Torvalds * 211da177e4SLinus Torvalds */ 221da177e4SLinus Torvalds 231da177e4SLinus Torvalds #include <linux/slab.h> 24174cd4b1SIngo Molnar #include <linux/sched/signal.h> 251da177e4SLinus Torvalds #include <linux/time.h> 263f7440a6STakashi Iwai #include <linux/math64.h> 27d81a6d71SPaul Gortmaker #include <linux/export.h> 281da177e4SLinus Torvalds #include <sound/core.h> 291da177e4SLinus Torvalds #include <sound/control.h> 302d3391ecSTakashi Iwai #include <sound/tlv.h> 311da177e4SLinus Torvalds #include <sound/info.h> 321da177e4SLinus Torvalds #include <sound/pcm.h> 331da177e4SLinus Torvalds #include <sound/pcm_params.h> 341da177e4SLinus Torvalds #include <sound/timer.h> 351da177e4SLinus Torvalds 362c4842d3STakashi Sakamoto #include "pcm_local.h" 372c4842d3STakashi Sakamoto 38f5914908STakashi Iwai #ifdef CONFIG_SND_PCM_XRUN_DEBUG 39f5914908STakashi Iwai #define CREATE_TRACE_POINTS 40f5914908STakashi Iwai #include "pcm_trace.h" 41f5914908STakashi Iwai #else 42f5914908STakashi Iwai #define trace_hwptr(substream, pos, in_interrupt) 43f5914908STakashi Iwai #define trace_xrun(substream) 44f5914908STakashi Iwai #define trace_hw_ptr_error(substream, reason) 45fccf5388STakashi Sakamoto #define trace_applptr(substream, prev, curr) 46f5914908STakashi Iwai #endif 47f5914908STakashi Iwai 48a9cd29e7STakashi Iwai static int fill_silence_frames(struct snd_pcm_substream *substream, 49a9cd29e7STakashi Iwai snd_pcm_uframes_t off, snd_pcm_uframes_t frames); 50a9cd29e7STakashi Iwai 511da177e4SLinus Torvalds /* 521da177e4SLinus Torvalds * fill ring buffer with silence 531da177e4SLinus Torvalds * runtime->silence_start: starting pointer to silence area 541da177e4SLinus Torvalds * runtime->silence_filled: size filled with silence 551da177e4SLinus Torvalds * runtime->silence_threshold: threshold from application 561da177e4SLinus Torvalds * runtime->silence_size: maximal size from application 571da177e4SLinus Torvalds * 581da177e4SLinus Torvalds * when runtime->silence_size >= runtime->boundary - fill processed area with silence immediately 591da177e4SLinus Torvalds */ 60877211f5STakashi Iwai void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_uframes_t new_hw_ptr) 611da177e4SLinus Torvalds { 62877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 631da177e4SLinus Torvalds snd_pcm_uframes_t frames, ofs, transfer; 6429d1a873STakashi Iwai int err; 651da177e4SLinus Torvalds 661da177e4SLinus Torvalds if (runtime->silence_size < runtime->boundary) { 671da177e4SLinus Torvalds snd_pcm_sframes_t noise_dist, n; 68aa30db06STakashi Iwai snd_pcm_uframes_t appl_ptr = READ_ONCE(runtime->control->appl_ptr); 69aa30db06STakashi Iwai if (runtime->silence_start != appl_ptr) { 70aa30db06STakashi Iwai n = appl_ptr - runtime->silence_start; 711da177e4SLinus Torvalds if (n < 0) 721da177e4SLinus Torvalds n += runtime->boundary; 731da177e4SLinus Torvalds if ((snd_pcm_uframes_t)n < runtime->silence_filled) 741da177e4SLinus Torvalds runtime->silence_filled -= n; 751da177e4SLinus Torvalds else 761da177e4SLinus Torvalds runtime->silence_filled = 0; 77aa30db06STakashi Iwai runtime->silence_start = appl_ptr; 781da177e4SLinus Torvalds } 79235475cbSTakashi Iwai if (runtime->silence_filled >= runtime->buffer_size) 801da177e4SLinus Torvalds return; 811da177e4SLinus Torvalds noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silence_filled; 821da177e4SLinus Torvalds if (noise_dist >= (snd_pcm_sframes_t) runtime->silence_threshold) 831da177e4SLinus Torvalds return; 841da177e4SLinus Torvalds frames = runtime->silence_threshold - noise_dist; 851da177e4SLinus Torvalds if (frames > runtime->silence_size) 861da177e4SLinus Torvalds frames = runtime->silence_size; 871da177e4SLinus Torvalds } else { 881da177e4SLinus Torvalds if (new_hw_ptr == ULONG_MAX) { /* initialization */ 891da177e4SLinus Torvalds snd_pcm_sframes_t avail = snd_pcm_playback_hw_avail(runtime); 909e216e8aSJaroslav Kysela if (avail > runtime->buffer_size) 919e216e8aSJaroslav Kysela avail = runtime->buffer_size; 921da177e4SLinus Torvalds runtime->silence_filled = avail > 0 ? avail : 0; 931da177e4SLinus Torvalds runtime->silence_start = (runtime->status->hw_ptr + 941da177e4SLinus Torvalds runtime->silence_filled) % 951da177e4SLinus Torvalds runtime->boundary; 961da177e4SLinus Torvalds } else { 971da177e4SLinus Torvalds ofs = runtime->status->hw_ptr; 981da177e4SLinus Torvalds frames = new_hw_ptr - ofs; 991da177e4SLinus Torvalds if ((snd_pcm_sframes_t)frames < 0) 1001da177e4SLinus Torvalds frames += runtime->boundary; 1011da177e4SLinus Torvalds runtime->silence_filled -= frames; 1021da177e4SLinus Torvalds if ((snd_pcm_sframes_t)runtime->silence_filled < 0) { 1031da177e4SLinus Torvalds runtime->silence_filled = 0; 1049a826ddbSClemens Ladisch runtime->silence_start = new_hw_ptr; 1051da177e4SLinus Torvalds } else { 1069a826ddbSClemens Ladisch runtime->silence_start = ofs; 1071da177e4SLinus Torvalds } 1081da177e4SLinus Torvalds } 1091da177e4SLinus Torvalds frames = runtime->buffer_size - runtime->silence_filled; 1101da177e4SLinus Torvalds } 1117eaa943cSTakashi Iwai if (snd_BUG_ON(frames > runtime->buffer_size)) 1127eaa943cSTakashi Iwai return; 1131da177e4SLinus Torvalds if (frames == 0) 1141da177e4SLinus Torvalds return; 1159a826ddbSClemens Ladisch ofs = runtime->silence_start % runtime->buffer_size; 1161da177e4SLinus Torvalds while (frames > 0) { 1171da177e4SLinus Torvalds transfer = ofs + frames > runtime->buffer_size ? runtime->buffer_size - ofs : frames; 118a9cd29e7STakashi Iwai err = fill_silence_frames(substream, ofs, transfer); 1197eaa943cSTakashi Iwai snd_BUG_ON(err < 0); 1201da177e4SLinus Torvalds runtime->silence_filled += transfer; 1211da177e4SLinus Torvalds frames -= transfer; 1221da177e4SLinus Torvalds ofs = 0; 1231da177e4SLinus Torvalds } 1241da177e4SLinus Torvalds } 1251da177e4SLinus Torvalds 126acb03d44SEliot Blennerhassett #ifdef CONFIG_SND_DEBUG 127acb03d44SEliot Blennerhassett void snd_pcm_debug_name(struct snd_pcm_substream *substream, 128c0070110STakashi Iwai char *name, size_t len) 1291da177e4SLinus Torvalds { 130c0070110STakashi Iwai snprintf(name, len, "pcmC%dD%d%c:%d", 1311da177e4SLinus Torvalds substream->pcm->card->number, 1321da177e4SLinus Torvalds substream->pcm->device, 133c0070110STakashi Iwai substream->stream ? 'c' : 'p', 134c0070110STakashi Iwai substream->number); 135c0070110STakashi Iwai } 136acb03d44SEliot Blennerhassett EXPORT_SYMBOL(snd_pcm_debug_name); 137acb03d44SEliot Blennerhassett #endif 138c0070110STakashi Iwai 1394d96eb25SJaroslav Kysela #define XRUN_DEBUG_BASIC (1<<0) 1404d96eb25SJaroslav Kysela #define XRUN_DEBUG_STACK (1<<1) /* dump also stack */ 1414d96eb25SJaroslav Kysela #define XRUN_DEBUG_JIFFIESCHECK (1<<2) /* do jiffies check */ 1424d96eb25SJaroslav Kysela 1434d96eb25SJaroslav Kysela #ifdef CONFIG_SND_PCM_XRUN_DEBUG 1444d96eb25SJaroslav Kysela 1454d96eb25SJaroslav Kysela #define xrun_debug(substream, mask) \ 1464d96eb25SJaroslav Kysela ((substream)->pstr->xrun_debug & (mask)) 1470f17014bSJarkko Nikula #else 1480f17014bSJarkko Nikula #define xrun_debug(substream, mask) 0 1490f17014bSJarkko Nikula #endif 1504d96eb25SJaroslav Kysela 1514d96eb25SJaroslav Kysela #define dump_stack_on_xrun(substream) do { \ 1524d96eb25SJaroslav Kysela if (xrun_debug(substream, XRUN_DEBUG_STACK)) \ 1534d96eb25SJaroslav Kysela dump_stack(); \ 1544d96eb25SJaroslav Kysela } while (0) 1554d96eb25SJaroslav Kysela 1569cd641edSTakashi Iwai /* call with stream lock held */ 1579cd641edSTakashi Iwai void __snd_pcm_xrun(struct snd_pcm_substream *substream) 1581da177e4SLinus Torvalds { 15913f040f9SJaroslav Kysela struct snd_pcm_runtime *runtime = substream->runtime; 16013f040f9SJaroslav Kysela 161f5914908STakashi Iwai trace_xrun(substream); 16213f040f9SJaroslav Kysela if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) 16313f040f9SJaroslav Kysela snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); 1641da177e4SLinus Torvalds snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); 165741b20cfSJaroslav Kysela if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { 166c0070110STakashi Iwai char name[16]; 167acb03d44SEliot Blennerhassett snd_pcm_debug_name(substream, name, sizeof(name)); 16809e56df8STakashi Iwai pcm_warn(substream->pcm, "XRUN: %s\n", name); 169ed3da3d9STakashi Iwai dump_stack_on_xrun(substream); 1701da177e4SLinus Torvalds } 1711da177e4SLinus Torvalds } 1721da177e4SLinus Torvalds 1730f17014bSJarkko Nikula #ifdef CONFIG_SND_PCM_XRUN_DEBUG 174f5914908STakashi Iwai #define hw_ptr_error(substream, in_interrupt, reason, fmt, args...) \ 1754d96eb25SJaroslav Kysela do { \ 176f5914908STakashi Iwai trace_hw_ptr_error(substream, reason); \ 1774d96eb25SJaroslav Kysela if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \ 178f5914908STakashi Iwai pr_err_ratelimited("ALSA: PCM: [%c] " reason ": " fmt, \ 179f5914908STakashi Iwai (in_interrupt) ? 'Q' : 'P', ##args); \ 1804d96eb25SJaroslav Kysela dump_stack_on_xrun(substream); \ 1814d96eb25SJaroslav Kysela } \ 1824d96eb25SJaroslav Kysela } while (0) 1834d96eb25SJaroslav Kysela 1844d96eb25SJaroslav Kysela #else /* ! CONFIG_SND_PCM_XRUN_DEBUG */ 1854d96eb25SJaroslav Kysela 1864d96eb25SJaroslav Kysela #define hw_ptr_error(substream, fmt, args...) do { } while (0) 1874d96eb25SJaroslav Kysela 1884d96eb25SJaroslav Kysela #endif 1894d96eb25SJaroslav Kysela 1901250932eSJaroslav Kysela int snd_pcm_update_state(struct snd_pcm_substream *substream, 191877211f5STakashi Iwai struct snd_pcm_runtime *runtime) 1921da177e4SLinus Torvalds { 1931da177e4SLinus Torvalds snd_pcm_uframes_t avail; 1941da177e4SLinus Torvalds 195763e5067STakashi Iwai avail = snd_pcm_avail(substream); 1961da177e4SLinus Torvalds if (avail > runtime->avail_max) 1971da177e4SLinus Torvalds runtime->avail_max = avail; 1984cdc115fSTakashi Iwai if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { 1994cdc115fSTakashi Iwai if (avail >= runtime->buffer_size) { 2001da177e4SLinus Torvalds snd_pcm_drain_done(substream); 2014cdc115fSTakashi Iwai return -EPIPE; 2024cdc115fSTakashi Iwai } 2034cdc115fSTakashi Iwai } else { 2044cdc115fSTakashi Iwai if (avail >= runtime->stop_threshold) { 2059cd641edSTakashi Iwai __snd_pcm_xrun(substream); 2061da177e4SLinus Torvalds return -EPIPE; 2071da177e4SLinus Torvalds } 2084cdc115fSTakashi Iwai } 2095daeba34SDavid Dillow if (runtime->twake) { 2105daeba34SDavid Dillow if (avail >= runtime->twake) 2115daeba34SDavid Dillow wake_up(&runtime->tsleep); 2125daeba34SDavid Dillow } else if (avail >= runtime->control->avail_min) 2135daeba34SDavid Dillow wake_up(&runtime->sleep); 2141da177e4SLinus Torvalds return 0; 2151da177e4SLinus Torvalds } 2161da177e4SLinus Torvalds 2173179f620SPierre-Louis Bossart static void update_audio_tstamp(struct snd_pcm_substream *substream, 2183179f620SPierre-Louis Bossart struct timespec *curr_tstamp, 2193179f620SPierre-Louis Bossart struct timespec *audio_tstamp) 2203179f620SPierre-Louis Bossart { 2213179f620SPierre-Louis Bossart struct snd_pcm_runtime *runtime = substream->runtime; 2223179f620SPierre-Louis Bossart u64 audio_frames, audio_nsecs; 2233179f620SPierre-Louis Bossart struct timespec driver_tstamp; 2243179f620SPierre-Louis Bossart 2253179f620SPierre-Louis Bossart if (runtime->tstamp_mode != SNDRV_PCM_TSTAMP_ENABLE) 2263179f620SPierre-Louis Bossart return; 2273179f620SPierre-Louis Bossart 2283179f620SPierre-Louis Bossart if (!(substream->ops->get_time_info) || 2293179f620SPierre-Louis Bossart (runtime->audio_tstamp_report.actual_type == 2303179f620SPierre-Louis Bossart SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)) { 2313179f620SPierre-Louis Bossart 2323179f620SPierre-Louis Bossart /* 2333179f620SPierre-Louis Bossart * provide audio timestamp derived from pointer position 2343179f620SPierre-Louis Bossart * add delay only if requested 2353179f620SPierre-Louis Bossart */ 2363179f620SPierre-Louis Bossart 2373179f620SPierre-Louis Bossart audio_frames = runtime->hw_ptr_wrap + runtime->status->hw_ptr; 2383179f620SPierre-Louis Bossart 2393179f620SPierre-Louis Bossart if (runtime->audio_tstamp_config.report_delay) { 2403179f620SPierre-Louis Bossart if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 2413179f620SPierre-Louis Bossart audio_frames -= runtime->delay; 2423179f620SPierre-Louis Bossart else 2433179f620SPierre-Louis Bossart audio_frames += runtime->delay; 2443179f620SPierre-Louis Bossart } 2453179f620SPierre-Louis Bossart audio_nsecs = div_u64(audio_frames * 1000000000LL, 2463179f620SPierre-Louis Bossart runtime->rate); 2473179f620SPierre-Louis Bossart *audio_tstamp = ns_to_timespec(audio_nsecs); 2483179f620SPierre-Louis Bossart } 24920e3f985SHenrik Eriksson if (!timespec_equal(&runtime->status->audio_tstamp, audio_tstamp)) { 2503179f620SPierre-Louis Bossart runtime->status->audio_tstamp = *audio_tstamp; 2513179f620SPierre-Louis Bossart runtime->status->tstamp = *curr_tstamp; 25220e3f985SHenrik Eriksson } 2533179f620SPierre-Louis Bossart 2543179f620SPierre-Louis Bossart /* 2553179f620SPierre-Louis Bossart * re-take a driver timestamp to let apps detect if the reference tstamp 2563179f620SPierre-Louis Bossart * read by low-level hardware was provided with a delay 2573179f620SPierre-Louis Bossart */ 2583179f620SPierre-Louis Bossart snd_pcm_gettime(substream->runtime, (struct timespec *)&driver_tstamp); 2593179f620SPierre-Louis Bossart runtime->driver_tstamp = driver_tstamp; 2603179f620SPierre-Louis Bossart } 2613179f620SPierre-Louis Bossart 262f240406bSJaroslav Kysela static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, 263f240406bSJaroslav Kysela unsigned int in_interrupt) 2641da177e4SLinus Torvalds { 265877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 2661da177e4SLinus Torvalds snd_pcm_uframes_t pos; 267f240406bSJaroslav Kysela snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base; 268bbf6ad13SJaroslav Kysela snd_pcm_sframes_t hdelta, delta; 269bbf6ad13SJaroslav Kysela unsigned long jdelta; 2703509a03fSPierre-Louis Bossart unsigned long curr_jiffies; 2713509a03fSPierre-Louis Bossart struct timespec curr_tstamp; 2724eeaaeaeSPierre-Louis Bossart struct timespec audio_tstamp; 2730e8014d7SPierre-Louis Bossart int crossed_boundary = 0; 2741da177e4SLinus Torvalds 275bbf6ad13SJaroslav Kysela old_hw_ptr = runtime->status->hw_ptr; 2763509a03fSPierre-Louis Bossart 2773509a03fSPierre-Louis Bossart /* 2783509a03fSPierre-Louis Bossart * group pointer, time and jiffies reads to allow for more 2793509a03fSPierre-Louis Bossart * accurate correlations/corrections. 2803509a03fSPierre-Louis Bossart * The values are stored at the end of this routine after 2813509a03fSPierre-Louis Bossart * corrections for hw_ptr position 2823509a03fSPierre-Louis Bossart */ 283f240406bSJaroslav Kysela pos = substream->ops->pointer(substream); 2843509a03fSPierre-Louis Bossart curr_jiffies = jiffies; 2854eeaaeaeSPierre-Louis Bossart if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { 2863179f620SPierre-Louis Bossart if ((substream->ops->get_time_info) && 2873179f620SPierre-Louis Bossart (runtime->audio_tstamp_config.type_requested != SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)) { 2883179f620SPierre-Louis Bossart substream->ops->get_time_info(substream, &curr_tstamp, 2893179f620SPierre-Louis Bossart &audio_tstamp, 2903179f620SPierre-Louis Bossart &runtime->audio_tstamp_config, 2913179f620SPierre-Louis Bossart &runtime->audio_tstamp_report); 2923509a03fSPierre-Louis Bossart 2933179f620SPierre-Louis Bossart /* re-test in case tstamp type is not supported in hardware and was demoted to DEFAULT */ 2943179f620SPierre-Louis Bossart if (runtime->audio_tstamp_report.actual_type == SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT) 2953179f620SPierre-Louis Bossart snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp); 2963179f620SPierre-Louis Bossart } else 2973179f620SPierre-Louis Bossart snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp); 2984eeaaeaeSPierre-Louis Bossart } 2994eeaaeaeSPierre-Louis Bossart 3001da177e4SLinus Torvalds if (pos == SNDRV_PCM_POS_XRUN) { 3019cd641edSTakashi Iwai __snd_pcm_xrun(substream); 3021da177e4SLinus Torvalds return -EPIPE; 3031da177e4SLinus Torvalds } 304f240406bSJaroslav Kysela if (pos >= runtime->buffer_size) { 30509e56df8STakashi Iwai if (printk_ratelimit()) { 306cedb8118STakashi Iwai char name[16]; 307acb03d44SEliot Blennerhassett snd_pcm_debug_name(substream, name, sizeof(name)); 30809e56df8STakashi Iwai pcm_err(substream->pcm, 3090ab1ace8STakashi Iwai "invalid position: %s, pos = %ld, buffer size = %ld, period size = %ld\n", 310f240406bSJaroslav Kysela name, pos, runtime->buffer_size, 311f240406bSJaroslav Kysela runtime->period_size); 312cedb8118STakashi Iwai } 313f240406bSJaroslav Kysela pos = 0; 314f240406bSJaroslav Kysela } 315f240406bSJaroslav Kysela pos -= pos % runtime->min_align; 316f5914908STakashi Iwai trace_hwptr(substream, pos, in_interrupt); 317ed3da3d9STakashi Iwai hw_base = runtime->hw_ptr_base; 318ed3da3d9STakashi Iwai new_hw_ptr = hw_base + pos; 319f240406bSJaroslav Kysela if (in_interrupt) { 320f240406bSJaroslav Kysela /* we know that one period was processed */ 321f240406bSJaroslav Kysela /* delta = "expected next hw_ptr" for in_interrupt != 0 */ 322e7636925SJaroslav Kysela delta = runtime->hw_ptr_interrupt + runtime->period_size; 323f240406bSJaroslav Kysela if (delta > new_hw_ptr) { 324bd76af0fSJaroslav Kysela /* check for double acknowledged interrupts */ 3253509a03fSPierre-Louis Bossart hdelta = curr_jiffies - runtime->hw_ptr_jiffies; 32613a98839SKoro Chen if (hdelta > runtime->hw_ptr_buffer_jiffies/2 + 1) { 327f240406bSJaroslav Kysela hw_base += runtime->buffer_size; 3280e8014d7SPierre-Louis Bossart if (hw_base >= runtime->boundary) { 329f240406bSJaroslav Kysela hw_base = 0; 3300e8014d7SPierre-Louis Bossart crossed_boundary++; 3310e8014d7SPierre-Louis Bossart } 332f240406bSJaroslav Kysela new_hw_ptr = hw_base + pos; 333f240406bSJaroslav Kysela goto __delta; 334ded652f7STakashi Iwai } 335f240406bSJaroslav Kysela } 336bd76af0fSJaroslav Kysela } 337f240406bSJaroslav Kysela /* new_hw_ptr might be lower than old_hw_ptr in case when */ 338f240406bSJaroslav Kysela /* pointer crosses the end of the ring buffer */ 339f240406bSJaroslav Kysela if (new_hw_ptr < old_hw_ptr) { 340ed3da3d9STakashi Iwai hw_base += runtime->buffer_size; 3410e8014d7SPierre-Louis Bossart if (hw_base >= runtime->boundary) { 342ed3da3d9STakashi Iwai hw_base = 0; 3430e8014d7SPierre-Louis Bossart crossed_boundary++; 3440e8014d7SPierre-Louis Bossart } 345ed3da3d9STakashi Iwai new_hw_ptr = hw_base + pos; 3461da177e4SLinus Torvalds } 347f240406bSJaroslav Kysela __delta: 348b406e610SClemens Ladisch delta = new_hw_ptr - old_hw_ptr; 349b406e610SClemens Ladisch if (delta < 0) 350b406e610SClemens Ladisch delta += runtime->boundary; 351ab69a490SClemens Ladisch 35259ff878fSClemens Ladisch if (runtime->no_period_wakeup) { 35312ff414eSKelly Anderson snd_pcm_sframes_t xrun_threshold; 35459ff878fSClemens Ladisch /* 35559ff878fSClemens Ladisch * Without regular period interrupts, we have to check 35659ff878fSClemens Ladisch * the elapsed time to detect xruns. 35759ff878fSClemens Ladisch */ 3583509a03fSPierre-Louis Bossart jdelta = curr_jiffies - runtime->hw_ptr_jiffies; 35947228e48SClemens Ladisch if (jdelta < runtime->hw_ptr_buffer_jiffies / 2) 36047228e48SClemens Ladisch goto no_delta_check; 36159ff878fSClemens Ladisch hdelta = jdelta - delta * HZ / runtime->rate; 36212ff414eSKelly Anderson xrun_threshold = runtime->hw_ptr_buffer_jiffies / 2 + 1; 36312ff414eSKelly Anderson while (hdelta > xrun_threshold) { 36459ff878fSClemens Ladisch delta += runtime->buffer_size; 36559ff878fSClemens Ladisch hw_base += runtime->buffer_size; 3660e8014d7SPierre-Louis Bossart if (hw_base >= runtime->boundary) { 36759ff878fSClemens Ladisch hw_base = 0; 3680e8014d7SPierre-Louis Bossart crossed_boundary++; 3690e8014d7SPierre-Louis Bossart } 37059ff878fSClemens Ladisch new_hw_ptr = hw_base + pos; 37159ff878fSClemens Ladisch hdelta -= runtime->hw_ptr_buffer_jiffies; 37259ff878fSClemens Ladisch } 373ab69a490SClemens Ladisch goto no_delta_check; 37459ff878fSClemens Ladisch } 375ab69a490SClemens Ladisch 376f240406bSJaroslav Kysela /* something must be really wrong */ 3777b3a177bSJaroslav Kysela if (delta >= runtime->buffer_size + runtime->period_size) { 378f5914908STakashi Iwai hw_ptr_error(substream, in_interrupt, "Unexpected hw_ptr", 379f5914908STakashi Iwai "(stream=%i, pos=%ld, new_hw_ptr=%ld, old_hw_ptr=%ld)\n", 380f240406bSJaroslav Kysela substream->stream, (long)pos, 381f240406bSJaroslav Kysela (long)new_hw_ptr, (long)old_hw_ptr); 382f240406bSJaroslav Kysela return 0; 3831da177e4SLinus Torvalds } 384c87d9732STakashi Iwai 385c87d9732STakashi Iwai /* Do jiffies check only in xrun_debug mode */ 386741b20cfSJaroslav Kysela if (!xrun_debug(substream, XRUN_DEBUG_JIFFIESCHECK)) 387c87d9732STakashi Iwai goto no_jiffies_check; 388c87d9732STakashi Iwai 3893e5b5016STakashi Iwai /* Skip the jiffies check for hardwares with BATCH flag. 3903e5b5016STakashi Iwai * Such hardware usually just increases the position at each IRQ, 3913e5b5016STakashi Iwai * thus it can't give any strange position. 3923e5b5016STakashi Iwai */ 3933e5b5016STakashi Iwai if (runtime->hw.info & SNDRV_PCM_INFO_BATCH) 3943e5b5016STakashi Iwai goto no_jiffies_check; 395f240406bSJaroslav Kysela hdelta = delta; 396a4444da3SJaroslav Kysela if (hdelta < runtime->delay) 397a4444da3SJaroslav Kysela goto no_jiffies_check; 398a4444da3SJaroslav Kysela hdelta -= runtime->delay; 3993509a03fSPierre-Louis Bossart jdelta = curr_jiffies - runtime->hw_ptr_jiffies; 400bbf6ad13SJaroslav Kysela if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) { 401bbf6ad13SJaroslav Kysela delta = jdelta / 402bbf6ad13SJaroslav Kysela (((runtime->period_size * HZ) / runtime->rate) 403bbf6ad13SJaroslav Kysela + HZ/100); 404f240406bSJaroslav Kysela /* move new_hw_ptr according jiffies not pos variable */ 405f240406bSJaroslav Kysela new_hw_ptr = old_hw_ptr; 406ed69c6a8SJaroslav Kysela hw_base = delta; 407f240406bSJaroslav Kysela /* use loop to avoid checks for delta overflows */ 408f240406bSJaroslav Kysela /* the delta value is small or zero in most cases */ 409f240406bSJaroslav Kysela while (delta > 0) { 410f240406bSJaroslav Kysela new_hw_ptr += runtime->period_size; 4110e8014d7SPierre-Louis Bossart if (new_hw_ptr >= runtime->boundary) { 412f240406bSJaroslav Kysela new_hw_ptr -= runtime->boundary; 4130e8014d7SPierre-Louis Bossart crossed_boundary--; 4140e8014d7SPierre-Louis Bossart } 415f240406bSJaroslav Kysela delta--; 416f240406bSJaroslav Kysela } 417f240406bSJaroslav Kysela /* align hw_base to buffer_size */ 418f5914908STakashi Iwai hw_ptr_error(substream, in_interrupt, "hw_ptr skipping", 419f5914908STakashi Iwai "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu/%lu, hw_ptr=%ld/%ld)\n", 420bbf6ad13SJaroslav Kysela (long)pos, (long)hdelta, 421bbf6ad13SJaroslav Kysela (long)runtime->period_size, jdelta, 422ed69c6a8SJaroslav Kysela ((hdelta * HZ) / runtime->rate), hw_base, 423f240406bSJaroslav Kysela (unsigned long)old_hw_ptr, 424f240406bSJaroslav Kysela (unsigned long)new_hw_ptr); 425ed69c6a8SJaroslav Kysela /* reset values to proper state */ 426ed69c6a8SJaroslav Kysela delta = 0; 427ed69c6a8SJaroslav Kysela hw_base = new_hw_ptr - (new_hw_ptr % runtime->buffer_size); 428bbf6ad13SJaroslav Kysela } 4293e5b5016STakashi Iwai no_jiffies_check: 430bbf6ad13SJaroslav Kysela if (delta > runtime->period_size + runtime->period_size / 2) { 431f5914908STakashi Iwai hw_ptr_error(substream, in_interrupt, 432f5914908STakashi Iwai "Lost interrupts?", 433f5914908STakashi Iwai "(stream=%i, delta=%ld, new_hw_ptr=%ld, old_hw_ptr=%ld)\n", 434ed3da3d9STakashi Iwai substream->stream, (long)delta, 435f240406bSJaroslav Kysela (long)new_hw_ptr, 436f240406bSJaroslav Kysela (long)old_hw_ptr); 4371da177e4SLinus Torvalds } 438f240406bSJaroslav Kysela 439ab69a490SClemens Ladisch no_delta_check: 4403179f620SPierre-Louis Bossart if (runtime->status->hw_ptr == new_hw_ptr) { 4413179f620SPierre-Louis Bossart update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp); 442f240406bSJaroslav Kysela return 0; 4433179f620SPierre-Louis Bossart } 444ab1863fcSTakashi Iwai 4451da177e4SLinus Torvalds if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && 4461da177e4SLinus Torvalds runtime->silence_size > 0) 4471da177e4SLinus Torvalds snd_pcm_playback_silence(substream, new_hw_ptr); 4481da177e4SLinus Torvalds 449e7636925SJaroslav Kysela if (in_interrupt) { 450ead4046bSClemens Ladisch delta = new_hw_ptr - runtime->hw_ptr_interrupt; 451ead4046bSClemens Ladisch if (delta < 0) 452ead4046bSClemens Ladisch delta += runtime->boundary; 453ead4046bSClemens Ladisch delta -= (snd_pcm_uframes_t)delta % runtime->period_size; 454ead4046bSClemens Ladisch runtime->hw_ptr_interrupt += delta; 455ead4046bSClemens Ladisch if (runtime->hw_ptr_interrupt >= runtime->boundary) 456ead4046bSClemens Ladisch runtime->hw_ptr_interrupt -= runtime->boundary; 457e7636925SJaroslav Kysela } 458ed3da3d9STakashi Iwai runtime->hw_ptr_base = hw_base; 4591da177e4SLinus Torvalds runtime->status->hw_ptr = new_hw_ptr; 4603509a03fSPierre-Louis Bossart runtime->hw_ptr_jiffies = curr_jiffies; 4610e8014d7SPierre-Louis Bossart if (crossed_boundary) { 4620e8014d7SPierre-Louis Bossart snd_BUG_ON(crossed_boundary != 1); 4630e8014d7SPierre-Louis Bossart runtime->hw_ptr_wrap += runtime->boundary; 4640e8014d7SPierre-Louis Bossart } 4651da177e4SLinus Torvalds 4663179f620SPierre-Louis Bossart update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp); 4674eeaaeaeSPierre-Louis Bossart 4681250932eSJaroslav Kysela return snd_pcm_update_state(substream, runtime); 4691da177e4SLinus Torvalds } 4701da177e4SLinus Torvalds 4711da177e4SLinus Torvalds /* CAUTION: call it with irq disabled */ 472877211f5STakashi Iwai int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) 4731da177e4SLinus Torvalds { 474f240406bSJaroslav Kysela return snd_pcm_update_hw_ptr0(substream, 0); 4751da177e4SLinus Torvalds } 4761da177e4SLinus Torvalds 4771da177e4SLinus Torvalds /** 4781da177e4SLinus Torvalds * snd_pcm_set_ops - set the PCM operators 4791da177e4SLinus Torvalds * @pcm: the pcm instance 4801da177e4SLinus Torvalds * @direction: stream direction, SNDRV_PCM_STREAM_XXX 4811da177e4SLinus Torvalds * @ops: the operator table 4821da177e4SLinus Torvalds * 4831da177e4SLinus Torvalds * Sets the given PCM operators to the pcm instance. 4841da177e4SLinus Torvalds */ 485e6c2e7ebSLars-Peter Clausen void snd_pcm_set_ops(struct snd_pcm *pcm, int direction, 486e6c2e7ebSLars-Peter Clausen const struct snd_pcm_ops *ops) 4871da177e4SLinus Torvalds { 488877211f5STakashi Iwai struct snd_pcm_str *stream = &pcm->streams[direction]; 489877211f5STakashi Iwai struct snd_pcm_substream *substream; 4901da177e4SLinus Torvalds 4911da177e4SLinus Torvalds for (substream = stream->substream; substream != NULL; substream = substream->next) 4921da177e4SLinus Torvalds substream->ops = ops; 4931da177e4SLinus Torvalds } 494e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_set_ops); 4951da177e4SLinus Torvalds 4961da177e4SLinus Torvalds /** 4971da177e4SLinus Torvalds * snd_pcm_sync - set the PCM sync id 4981da177e4SLinus Torvalds * @substream: the pcm substream 4991da177e4SLinus Torvalds * 5001da177e4SLinus Torvalds * Sets the PCM sync identifier for the card. 5011da177e4SLinus Torvalds */ 502877211f5STakashi Iwai void snd_pcm_set_sync(struct snd_pcm_substream *substream) 5031da177e4SLinus Torvalds { 504877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 5051da177e4SLinus Torvalds 5061da177e4SLinus Torvalds runtime->sync.id32[0] = substream->pcm->card->number; 5071da177e4SLinus Torvalds runtime->sync.id32[1] = -1; 5081da177e4SLinus Torvalds runtime->sync.id32[2] = -1; 5091da177e4SLinus Torvalds runtime->sync.id32[3] = -1; 5101da177e4SLinus Torvalds } 511e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_set_sync); 512e88e8ae6STakashi Iwai 5131da177e4SLinus Torvalds /* 5141da177e4SLinus Torvalds * Standard ioctl routine 5151da177e4SLinus Torvalds */ 5161da177e4SLinus Torvalds 5171da177e4SLinus Torvalds static inline unsigned int div32(unsigned int a, unsigned int b, 5181da177e4SLinus Torvalds unsigned int *r) 5191da177e4SLinus Torvalds { 5201da177e4SLinus Torvalds if (b == 0) { 5211da177e4SLinus Torvalds *r = 0; 5221da177e4SLinus Torvalds return UINT_MAX; 5231da177e4SLinus Torvalds } 5241da177e4SLinus Torvalds *r = a % b; 5251da177e4SLinus Torvalds return a / b; 5261da177e4SLinus Torvalds } 5271da177e4SLinus Torvalds 5281da177e4SLinus Torvalds static inline unsigned int div_down(unsigned int a, unsigned int b) 5291da177e4SLinus Torvalds { 5301da177e4SLinus Torvalds if (b == 0) 5311da177e4SLinus Torvalds return UINT_MAX; 5321da177e4SLinus Torvalds return a / b; 5331da177e4SLinus Torvalds } 5341da177e4SLinus Torvalds 5351da177e4SLinus Torvalds static inline unsigned int div_up(unsigned int a, unsigned int b) 5361da177e4SLinus Torvalds { 5371da177e4SLinus Torvalds unsigned int r; 5381da177e4SLinus Torvalds unsigned int q; 5391da177e4SLinus Torvalds if (b == 0) 5401da177e4SLinus Torvalds return UINT_MAX; 5411da177e4SLinus Torvalds q = div32(a, b, &r); 5421da177e4SLinus Torvalds if (r) 5431da177e4SLinus Torvalds ++q; 5441da177e4SLinus Torvalds return q; 5451da177e4SLinus Torvalds } 5461da177e4SLinus Torvalds 5471da177e4SLinus Torvalds static inline unsigned int mul(unsigned int a, unsigned int b) 5481da177e4SLinus Torvalds { 5491da177e4SLinus Torvalds if (a == 0) 5501da177e4SLinus Torvalds return 0; 5511da177e4SLinus Torvalds if (div_down(UINT_MAX, a) < b) 5521da177e4SLinus Torvalds return UINT_MAX; 5531da177e4SLinus Torvalds return a * b; 5541da177e4SLinus Torvalds } 5551da177e4SLinus Torvalds 5561da177e4SLinus Torvalds static inline unsigned int muldiv32(unsigned int a, unsigned int b, 5571da177e4SLinus Torvalds unsigned int c, unsigned int *r) 5581da177e4SLinus Torvalds { 5591da177e4SLinus Torvalds u_int64_t n = (u_int64_t) a * b; 5601da177e4SLinus Torvalds if (c == 0) { 5611da177e4SLinus Torvalds *r = 0; 5621da177e4SLinus Torvalds return UINT_MAX; 5631da177e4SLinus Torvalds } 5643f7440a6STakashi Iwai n = div_u64_rem(n, c, r); 5651da177e4SLinus Torvalds if (n >= UINT_MAX) { 5661da177e4SLinus Torvalds *r = 0; 5671da177e4SLinus Torvalds return UINT_MAX; 5681da177e4SLinus Torvalds } 5691da177e4SLinus Torvalds return n; 5701da177e4SLinus Torvalds } 5711da177e4SLinus Torvalds 5721da177e4SLinus Torvalds /** 5731da177e4SLinus Torvalds * snd_interval_refine - refine the interval value of configurator 5741da177e4SLinus Torvalds * @i: the interval value to refine 5751da177e4SLinus Torvalds * @v: the interval value to refer to 5761da177e4SLinus Torvalds * 5771da177e4SLinus Torvalds * Refines the interval value with the reference value. 5781da177e4SLinus Torvalds * The interval is changed to the range satisfying both intervals. 5791da177e4SLinus Torvalds * The interval status (min, max, integer, etc.) are evaluated. 5801da177e4SLinus Torvalds * 581eb7c06e8SYacine Belkadi * Return: Positive if the value is changed, zero if it's not changed, or a 582eb7c06e8SYacine Belkadi * negative error code. 5831da177e4SLinus Torvalds */ 584877211f5STakashi Iwai int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v) 5851da177e4SLinus Torvalds { 5861da177e4SLinus Torvalds int changed = 0; 5877eaa943cSTakashi Iwai if (snd_BUG_ON(snd_interval_empty(i))) 5887eaa943cSTakashi Iwai return -EINVAL; 5891da177e4SLinus Torvalds if (i->min < v->min) { 5901da177e4SLinus Torvalds i->min = v->min; 5911da177e4SLinus Torvalds i->openmin = v->openmin; 5921da177e4SLinus Torvalds changed = 1; 5931da177e4SLinus Torvalds } else if (i->min == v->min && !i->openmin && v->openmin) { 5941da177e4SLinus Torvalds i->openmin = 1; 5951da177e4SLinus Torvalds changed = 1; 5961da177e4SLinus Torvalds } 5971da177e4SLinus Torvalds if (i->max > v->max) { 5981da177e4SLinus Torvalds i->max = v->max; 5991da177e4SLinus Torvalds i->openmax = v->openmax; 6001da177e4SLinus Torvalds changed = 1; 6011da177e4SLinus Torvalds } else if (i->max == v->max && !i->openmax && v->openmax) { 6021da177e4SLinus Torvalds i->openmax = 1; 6031da177e4SLinus Torvalds changed = 1; 6041da177e4SLinus Torvalds } 6051da177e4SLinus Torvalds if (!i->integer && v->integer) { 6061da177e4SLinus Torvalds i->integer = 1; 6071da177e4SLinus Torvalds changed = 1; 6081da177e4SLinus Torvalds } 6091da177e4SLinus Torvalds if (i->integer) { 6101da177e4SLinus Torvalds if (i->openmin) { 6111da177e4SLinus Torvalds i->min++; 6121da177e4SLinus Torvalds i->openmin = 0; 6131da177e4SLinus Torvalds } 6141da177e4SLinus Torvalds if (i->openmax) { 6151da177e4SLinus Torvalds i->max--; 6161da177e4SLinus Torvalds i->openmax = 0; 6171da177e4SLinus Torvalds } 6181da177e4SLinus Torvalds } else if (!i->openmin && !i->openmax && i->min == i->max) 6191da177e4SLinus Torvalds i->integer = 1; 6201da177e4SLinus Torvalds if (snd_interval_checkempty(i)) { 6211da177e4SLinus Torvalds snd_interval_none(i); 6221da177e4SLinus Torvalds return -EINVAL; 6231da177e4SLinus Torvalds } 6241da177e4SLinus Torvalds return changed; 6251da177e4SLinus Torvalds } 626e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_interval_refine); 627e88e8ae6STakashi Iwai 628877211f5STakashi Iwai static int snd_interval_refine_first(struct snd_interval *i) 6291da177e4SLinus Torvalds { 6307eaa943cSTakashi Iwai if (snd_BUG_ON(snd_interval_empty(i))) 6317eaa943cSTakashi Iwai return -EINVAL; 6321da177e4SLinus Torvalds if (snd_interval_single(i)) 6331da177e4SLinus Torvalds return 0; 6341da177e4SLinus Torvalds i->max = i->min; 6351da177e4SLinus Torvalds i->openmax = i->openmin; 6361da177e4SLinus Torvalds if (i->openmax) 6371da177e4SLinus Torvalds i->max++; 6381da177e4SLinus Torvalds return 1; 6391da177e4SLinus Torvalds } 6401da177e4SLinus Torvalds 641877211f5STakashi Iwai static int snd_interval_refine_last(struct snd_interval *i) 6421da177e4SLinus Torvalds { 6437eaa943cSTakashi Iwai if (snd_BUG_ON(snd_interval_empty(i))) 6447eaa943cSTakashi Iwai return -EINVAL; 6451da177e4SLinus Torvalds if (snd_interval_single(i)) 6461da177e4SLinus Torvalds return 0; 6471da177e4SLinus Torvalds i->min = i->max; 6481da177e4SLinus Torvalds i->openmin = i->openmax; 6491da177e4SLinus Torvalds if (i->openmin) 6501da177e4SLinus Torvalds i->min--; 6511da177e4SLinus Torvalds return 1; 6521da177e4SLinus Torvalds } 6531da177e4SLinus Torvalds 654877211f5STakashi Iwai void snd_interval_mul(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c) 6551da177e4SLinus Torvalds { 6561da177e4SLinus Torvalds if (a->empty || b->empty) { 6571da177e4SLinus Torvalds snd_interval_none(c); 6581da177e4SLinus Torvalds return; 6591da177e4SLinus Torvalds } 6601da177e4SLinus Torvalds c->empty = 0; 6611da177e4SLinus Torvalds c->min = mul(a->min, b->min); 6621da177e4SLinus Torvalds c->openmin = (a->openmin || b->openmin); 6631da177e4SLinus Torvalds c->max = mul(a->max, b->max); 6641da177e4SLinus Torvalds c->openmax = (a->openmax || b->openmax); 6651da177e4SLinus Torvalds c->integer = (a->integer && b->integer); 6661da177e4SLinus Torvalds } 6671da177e4SLinus Torvalds 6681da177e4SLinus Torvalds /** 6691da177e4SLinus Torvalds * snd_interval_div - refine the interval value with division 670df8db936STakashi Iwai * @a: dividend 671df8db936STakashi Iwai * @b: divisor 672df8db936STakashi Iwai * @c: quotient 6731da177e4SLinus Torvalds * 6741da177e4SLinus Torvalds * c = a / b 6751da177e4SLinus Torvalds * 6761da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 6771da177e4SLinus Torvalds */ 678877211f5STakashi Iwai void snd_interval_div(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c) 6791da177e4SLinus Torvalds { 6801da177e4SLinus Torvalds unsigned int r; 6811da177e4SLinus Torvalds if (a->empty || b->empty) { 6821da177e4SLinus Torvalds snd_interval_none(c); 6831da177e4SLinus Torvalds return; 6841da177e4SLinus Torvalds } 6851da177e4SLinus Torvalds c->empty = 0; 6861da177e4SLinus Torvalds c->min = div32(a->min, b->max, &r); 6871da177e4SLinus Torvalds c->openmin = (r || a->openmin || b->openmax); 6881da177e4SLinus Torvalds if (b->min > 0) { 6891da177e4SLinus Torvalds c->max = div32(a->max, b->min, &r); 6901da177e4SLinus Torvalds if (r) { 6911da177e4SLinus Torvalds c->max++; 6921da177e4SLinus Torvalds c->openmax = 1; 6931da177e4SLinus Torvalds } else 6941da177e4SLinus Torvalds c->openmax = (a->openmax || b->openmin); 6951da177e4SLinus Torvalds } else { 6961da177e4SLinus Torvalds c->max = UINT_MAX; 6971da177e4SLinus Torvalds c->openmax = 0; 6981da177e4SLinus Torvalds } 6991da177e4SLinus Torvalds c->integer = 0; 7001da177e4SLinus Torvalds } 7011da177e4SLinus Torvalds 7021da177e4SLinus Torvalds /** 7031da177e4SLinus Torvalds * snd_interval_muldivk - refine the interval value 704df8db936STakashi Iwai * @a: dividend 1 705df8db936STakashi Iwai * @b: dividend 2 706df8db936STakashi Iwai * @k: divisor (as integer) 707df8db936STakashi Iwai * @c: result 7081da177e4SLinus Torvalds * 7091da177e4SLinus Torvalds * c = a * b / k 7101da177e4SLinus Torvalds * 7111da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 7121da177e4SLinus Torvalds */ 713877211f5STakashi Iwai void snd_interval_muldivk(const struct snd_interval *a, const struct snd_interval *b, 714877211f5STakashi Iwai unsigned int k, struct snd_interval *c) 7151da177e4SLinus Torvalds { 7161da177e4SLinus Torvalds unsigned int r; 7171da177e4SLinus Torvalds if (a->empty || b->empty) { 7181da177e4SLinus Torvalds snd_interval_none(c); 7191da177e4SLinus Torvalds return; 7201da177e4SLinus Torvalds } 7211da177e4SLinus Torvalds c->empty = 0; 7221da177e4SLinus Torvalds c->min = muldiv32(a->min, b->min, k, &r); 7231da177e4SLinus Torvalds c->openmin = (r || a->openmin || b->openmin); 7241da177e4SLinus Torvalds c->max = muldiv32(a->max, b->max, k, &r); 7251da177e4SLinus Torvalds if (r) { 7261da177e4SLinus Torvalds c->max++; 7271da177e4SLinus Torvalds c->openmax = 1; 7281da177e4SLinus Torvalds } else 7291da177e4SLinus Torvalds c->openmax = (a->openmax || b->openmax); 7301da177e4SLinus Torvalds c->integer = 0; 7311da177e4SLinus Torvalds } 7321da177e4SLinus Torvalds 7331da177e4SLinus Torvalds /** 7341da177e4SLinus Torvalds * snd_interval_mulkdiv - refine the interval value 735df8db936STakashi Iwai * @a: dividend 1 736df8db936STakashi Iwai * @k: dividend 2 (as integer) 737df8db936STakashi Iwai * @b: divisor 738df8db936STakashi Iwai * @c: result 7391da177e4SLinus Torvalds * 7401da177e4SLinus Torvalds * c = a * k / b 7411da177e4SLinus Torvalds * 7421da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 7431da177e4SLinus Torvalds */ 744877211f5STakashi Iwai void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k, 745877211f5STakashi Iwai const struct snd_interval *b, struct snd_interval *c) 7461da177e4SLinus Torvalds { 7471da177e4SLinus Torvalds unsigned int r; 7481da177e4SLinus Torvalds if (a->empty || b->empty) { 7491da177e4SLinus Torvalds snd_interval_none(c); 7501da177e4SLinus Torvalds return; 7511da177e4SLinus Torvalds } 7521da177e4SLinus Torvalds c->empty = 0; 7531da177e4SLinus Torvalds c->min = muldiv32(a->min, k, b->max, &r); 7541da177e4SLinus Torvalds c->openmin = (r || a->openmin || b->openmax); 7551da177e4SLinus Torvalds if (b->min > 0) { 7561da177e4SLinus Torvalds c->max = muldiv32(a->max, k, b->min, &r); 7571da177e4SLinus Torvalds if (r) { 7581da177e4SLinus Torvalds c->max++; 7591da177e4SLinus Torvalds c->openmax = 1; 7601da177e4SLinus Torvalds } else 7611da177e4SLinus Torvalds c->openmax = (a->openmax || b->openmin); 7621da177e4SLinus Torvalds } else { 7631da177e4SLinus Torvalds c->max = UINT_MAX; 7641da177e4SLinus Torvalds c->openmax = 0; 7651da177e4SLinus Torvalds } 7661da177e4SLinus Torvalds c->integer = 0; 7671da177e4SLinus Torvalds } 7681da177e4SLinus Torvalds 7691da177e4SLinus Torvalds /* ---- */ 7701da177e4SLinus Torvalds 7711da177e4SLinus Torvalds 7721da177e4SLinus Torvalds /** 7731da177e4SLinus Torvalds * snd_interval_ratnum - refine the interval value 774df8db936STakashi Iwai * @i: interval to refine 775df8db936STakashi Iwai * @rats_count: number of ratnum_t 776df8db936STakashi Iwai * @rats: ratnum_t array 777df8db936STakashi Iwai * @nump: pointer to store the resultant numerator 778df8db936STakashi Iwai * @denp: pointer to store the resultant denominator 7791da177e4SLinus Torvalds * 780eb7c06e8SYacine Belkadi * Return: Positive if the value is changed, zero if it's not changed, or a 781eb7c06e8SYacine Belkadi * negative error code. 7821da177e4SLinus Torvalds */ 783877211f5STakashi Iwai int snd_interval_ratnum(struct snd_interval *i, 784e5e113cfSLars-Peter Clausen unsigned int rats_count, const struct snd_ratnum *rats, 7851da177e4SLinus Torvalds unsigned int *nump, unsigned int *denp) 7861da177e4SLinus Torvalds { 7878374e24cSKrzysztof Helt unsigned int best_num, best_den; 7888374e24cSKrzysztof Helt int best_diff; 7891da177e4SLinus Torvalds unsigned int k; 790877211f5STakashi Iwai struct snd_interval t; 7911da177e4SLinus Torvalds int err; 7928374e24cSKrzysztof Helt unsigned int result_num, result_den; 7938374e24cSKrzysztof Helt int result_diff; 7941da177e4SLinus Torvalds 7951da177e4SLinus Torvalds best_num = best_den = best_diff = 0; 7961da177e4SLinus Torvalds for (k = 0; k < rats_count; ++k) { 7971da177e4SLinus Torvalds unsigned int num = rats[k].num; 7981da177e4SLinus Torvalds unsigned int den; 7991da177e4SLinus Torvalds unsigned int q = i->min; 8001da177e4SLinus Torvalds int diff; 8011da177e4SLinus Torvalds if (q == 0) 8021da177e4SLinus Torvalds q = 1; 80340962d7cSKrzysztof Helt den = div_up(num, q); 8041da177e4SLinus Torvalds if (den < rats[k].den_min) 8051da177e4SLinus Torvalds continue; 8061da177e4SLinus Torvalds if (den > rats[k].den_max) 8071da177e4SLinus Torvalds den = rats[k].den_max; 8081da177e4SLinus Torvalds else { 8091da177e4SLinus Torvalds unsigned int r; 8101da177e4SLinus Torvalds r = (den - rats[k].den_min) % rats[k].den_step; 8111da177e4SLinus Torvalds if (r != 0) 8121da177e4SLinus Torvalds den -= r; 8131da177e4SLinus Torvalds } 8141da177e4SLinus Torvalds diff = num - q * den; 8158374e24cSKrzysztof Helt if (diff < 0) 8168374e24cSKrzysztof Helt diff = -diff; 8171da177e4SLinus Torvalds if (best_num == 0 || 8181da177e4SLinus Torvalds diff * best_den < best_diff * den) { 8191da177e4SLinus Torvalds best_diff = diff; 8201da177e4SLinus Torvalds best_den = den; 8211da177e4SLinus Torvalds best_num = num; 8221da177e4SLinus Torvalds } 8231da177e4SLinus Torvalds } 8241da177e4SLinus Torvalds if (best_den == 0) { 8251da177e4SLinus Torvalds i->empty = 1; 8261da177e4SLinus Torvalds return -EINVAL; 8271da177e4SLinus Torvalds } 8281da177e4SLinus Torvalds t.min = div_down(best_num, best_den); 8291da177e4SLinus Torvalds t.openmin = !!(best_num % best_den); 8301da177e4SLinus Torvalds 8318374e24cSKrzysztof Helt result_num = best_num; 8328374e24cSKrzysztof Helt result_diff = best_diff; 8338374e24cSKrzysztof Helt result_den = best_den; 8341da177e4SLinus Torvalds best_num = best_den = best_diff = 0; 8351da177e4SLinus Torvalds for (k = 0; k < rats_count; ++k) { 8361da177e4SLinus Torvalds unsigned int num = rats[k].num; 8371da177e4SLinus Torvalds unsigned int den; 8381da177e4SLinus Torvalds unsigned int q = i->max; 8391da177e4SLinus Torvalds int diff; 8401da177e4SLinus Torvalds if (q == 0) { 8411da177e4SLinus Torvalds i->empty = 1; 8421da177e4SLinus Torvalds return -EINVAL; 8431da177e4SLinus Torvalds } 84440962d7cSKrzysztof Helt den = div_down(num, q); 8451da177e4SLinus Torvalds if (den > rats[k].den_max) 8461da177e4SLinus Torvalds continue; 8471da177e4SLinus Torvalds if (den < rats[k].den_min) 8481da177e4SLinus Torvalds den = rats[k].den_min; 8491da177e4SLinus Torvalds else { 8501da177e4SLinus Torvalds unsigned int r; 8511da177e4SLinus Torvalds r = (den - rats[k].den_min) % rats[k].den_step; 8521da177e4SLinus Torvalds if (r != 0) 8531da177e4SLinus Torvalds den += rats[k].den_step - r; 8541da177e4SLinus Torvalds } 8551da177e4SLinus Torvalds diff = q * den - num; 8568374e24cSKrzysztof Helt if (diff < 0) 8578374e24cSKrzysztof Helt diff = -diff; 8581da177e4SLinus Torvalds if (best_num == 0 || 8591da177e4SLinus Torvalds diff * best_den < best_diff * den) { 8601da177e4SLinus Torvalds best_diff = diff; 8611da177e4SLinus Torvalds best_den = den; 8621da177e4SLinus Torvalds best_num = num; 8631da177e4SLinus Torvalds } 8641da177e4SLinus Torvalds } 8651da177e4SLinus Torvalds if (best_den == 0) { 8661da177e4SLinus Torvalds i->empty = 1; 8671da177e4SLinus Torvalds return -EINVAL; 8681da177e4SLinus Torvalds } 8691da177e4SLinus Torvalds t.max = div_up(best_num, best_den); 8701da177e4SLinus Torvalds t.openmax = !!(best_num % best_den); 8711da177e4SLinus Torvalds t.integer = 0; 8721da177e4SLinus Torvalds err = snd_interval_refine(i, &t); 8731da177e4SLinus Torvalds if (err < 0) 8741da177e4SLinus Torvalds return err; 8751da177e4SLinus Torvalds 8761da177e4SLinus Torvalds if (snd_interval_single(i)) { 8778374e24cSKrzysztof Helt if (best_diff * result_den < result_diff * best_den) { 8788374e24cSKrzysztof Helt result_num = best_num; 8798374e24cSKrzysztof Helt result_den = best_den; 8808374e24cSKrzysztof Helt } 8811da177e4SLinus Torvalds if (nump) 8828374e24cSKrzysztof Helt *nump = result_num; 8831da177e4SLinus Torvalds if (denp) 8848374e24cSKrzysztof Helt *denp = result_den; 8851da177e4SLinus Torvalds } 8861da177e4SLinus Torvalds return err; 8871da177e4SLinus Torvalds } 888e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_interval_ratnum); 889e88e8ae6STakashi Iwai 8901da177e4SLinus Torvalds /** 8911da177e4SLinus Torvalds * snd_interval_ratden - refine the interval value 892df8db936STakashi Iwai * @i: interval to refine 893877211f5STakashi Iwai * @rats_count: number of struct ratden 894877211f5STakashi Iwai * @rats: struct ratden array 895df8db936STakashi Iwai * @nump: pointer to store the resultant numerator 896df8db936STakashi Iwai * @denp: pointer to store the resultant denominator 8971da177e4SLinus Torvalds * 898eb7c06e8SYacine Belkadi * Return: Positive if the value is changed, zero if it's not changed, or a 899eb7c06e8SYacine Belkadi * negative error code. 9001da177e4SLinus Torvalds */ 901877211f5STakashi Iwai static int snd_interval_ratden(struct snd_interval *i, 902e5e113cfSLars-Peter Clausen unsigned int rats_count, 903e5e113cfSLars-Peter Clausen const struct snd_ratden *rats, 9041da177e4SLinus Torvalds unsigned int *nump, unsigned int *denp) 9051da177e4SLinus Torvalds { 9061da177e4SLinus Torvalds unsigned int best_num, best_diff, best_den; 9071da177e4SLinus Torvalds unsigned int k; 908877211f5STakashi Iwai struct snd_interval t; 9091da177e4SLinus Torvalds int err; 9101da177e4SLinus Torvalds 9111da177e4SLinus Torvalds best_num = best_den = best_diff = 0; 9121da177e4SLinus Torvalds for (k = 0; k < rats_count; ++k) { 9131da177e4SLinus Torvalds unsigned int num; 9141da177e4SLinus Torvalds unsigned int den = rats[k].den; 9151da177e4SLinus Torvalds unsigned int q = i->min; 9161da177e4SLinus Torvalds int diff; 9171da177e4SLinus Torvalds num = mul(q, den); 9181da177e4SLinus Torvalds if (num > rats[k].num_max) 9191da177e4SLinus Torvalds continue; 9201da177e4SLinus Torvalds if (num < rats[k].num_min) 9211da177e4SLinus Torvalds num = rats[k].num_max; 9221da177e4SLinus Torvalds else { 9231da177e4SLinus Torvalds unsigned int r; 9241da177e4SLinus Torvalds r = (num - rats[k].num_min) % rats[k].num_step; 9251da177e4SLinus Torvalds if (r != 0) 9261da177e4SLinus Torvalds num += rats[k].num_step - r; 9271da177e4SLinus Torvalds } 9281da177e4SLinus Torvalds diff = num - q * den; 9291da177e4SLinus Torvalds if (best_num == 0 || 9301da177e4SLinus Torvalds diff * best_den < best_diff * den) { 9311da177e4SLinus Torvalds best_diff = diff; 9321da177e4SLinus Torvalds best_den = den; 9331da177e4SLinus Torvalds best_num = num; 9341da177e4SLinus Torvalds } 9351da177e4SLinus Torvalds } 9361da177e4SLinus Torvalds if (best_den == 0) { 9371da177e4SLinus Torvalds i->empty = 1; 9381da177e4SLinus Torvalds return -EINVAL; 9391da177e4SLinus Torvalds } 9401da177e4SLinus Torvalds t.min = div_down(best_num, best_den); 9411da177e4SLinus Torvalds t.openmin = !!(best_num % best_den); 9421da177e4SLinus Torvalds 9431da177e4SLinus Torvalds best_num = best_den = best_diff = 0; 9441da177e4SLinus Torvalds for (k = 0; k < rats_count; ++k) { 9451da177e4SLinus Torvalds unsigned int num; 9461da177e4SLinus Torvalds unsigned int den = rats[k].den; 9471da177e4SLinus Torvalds unsigned int q = i->max; 9481da177e4SLinus Torvalds int diff; 9491da177e4SLinus Torvalds num = mul(q, den); 9501da177e4SLinus Torvalds if (num < rats[k].num_min) 9511da177e4SLinus Torvalds continue; 9521da177e4SLinus Torvalds if (num > rats[k].num_max) 9531da177e4SLinus Torvalds num = rats[k].num_max; 9541da177e4SLinus Torvalds else { 9551da177e4SLinus Torvalds unsigned int r; 9561da177e4SLinus Torvalds r = (num - rats[k].num_min) % rats[k].num_step; 9571da177e4SLinus Torvalds if (r != 0) 9581da177e4SLinus Torvalds num -= r; 9591da177e4SLinus Torvalds } 9601da177e4SLinus Torvalds diff = q * den - num; 9611da177e4SLinus Torvalds if (best_num == 0 || 9621da177e4SLinus Torvalds diff * best_den < best_diff * den) { 9631da177e4SLinus Torvalds best_diff = diff; 9641da177e4SLinus Torvalds best_den = den; 9651da177e4SLinus Torvalds best_num = num; 9661da177e4SLinus Torvalds } 9671da177e4SLinus Torvalds } 9681da177e4SLinus Torvalds if (best_den == 0) { 9691da177e4SLinus Torvalds i->empty = 1; 9701da177e4SLinus Torvalds return -EINVAL; 9711da177e4SLinus Torvalds } 9721da177e4SLinus Torvalds t.max = div_up(best_num, best_den); 9731da177e4SLinus Torvalds t.openmax = !!(best_num % best_den); 9741da177e4SLinus Torvalds t.integer = 0; 9751da177e4SLinus Torvalds err = snd_interval_refine(i, &t); 9761da177e4SLinus Torvalds if (err < 0) 9771da177e4SLinus Torvalds return err; 9781da177e4SLinus Torvalds 9791da177e4SLinus Torvalds if (snd_interval_single(i)) { 9801da177e4SLinus Torvalds if (nump) 9811da177e4SLinus Torvalds *nump = best_num; 9821da177e4SLinus Torvalds if (denp) 9831da177e4SLinus Torvalds *denp = best_den; 9841da177e4SLinus Torvalds } 9851da177e4SLinus Torvalds return err; 9861da177e4SLinus Torvalds } 9871da177e4SLinus Torvalds 9881da177e4SLinus Torvalds /** 9891da177e4SLinus Torvalds * snd_interval_list - refine the interval value from the list 9901da177e4SLinus Torvalds * @i: the interval value to refine 9911da177e4SLinus Torvalds * @count: the number of elements in the list 9921da177e4SLinus Torvalds * @list: the value list 9931da177e4SLinus Torvalds * @mask: the bit-mask to evaluate 9941da177e4SLinus Torvalds * 9951da177e4SLinus Torvalds * Refines the interval value from the list. 9961da177e4SLinus Torvalds * When mask is non-zero, only the elements corresponding to bit 1 are 9971da177e4SLinus Torvalds * evaluated. 9981da177e4SLinus Torvalds * 999eb7c06e8SYacine Belkadi * Return: Positive if the value is changed, zero if it's not changed, or a 1000eb7c06e8SYacine Belkadi * negative error code. 10011da177e4SLinus Torvalds */ 10024af87a93SMark Brown int snd_interval_list(struct snd_interval *i, unsigned int count, 10034af87a93SMark Brown const unsigned int *list, unsigned int mask) 10041da177e4SLinus Torvalds { 10051da177e4SLinus Torvalds unsigned int k; 1006b1ddaf68SClemens Ladisch struct snd_interval list_range; 10070981a260STakashi Iwai 10080981a260STakashi Iwai if (!count) { 10090981a260STakashi Iwai i->empty = 1; 10100981a260STakashi Iwai return -EINVAL; 10110981a260STakashi Iwai } 1012b1ddaf68SClemens Ladisch snd_interval_any(&list_range); 1013b1ddaf68SClemens Ladisch list_range.min = UINT_MAX; 1014b1ddaf68SClemens Ladisch list_range.max = 0; 10151da177e4SLinus Torvalds for (k = 0; k < count; k++) { 10161da177e4SLinus Torvalds if (mask && !(mask & (1 << k))) 10171da177e4SLinus Torvalds continue; 1018b1ddaf68SClemens Ladisch if (!snd_interval_test(i, list[k])) 10191da177e4SLinus Torvalds continue; 1020b1ddaf68SClemens Ladisch list_range.min = min(list_range.min, list[k]); 1021b1ddaf68SClemens Ladisch list_range.max = max(list_range.max, list[k]); 10221da177e4SLinus Torvalds } 1023b1ddaf68SClemens Ladisch return snd_interval_refine(i, &list_range); 10241da177e4SLinus Torvalds } 1025e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_interval_list); 1026e88e8ae6STakashi Iwai 1027f66f898eSPeter Rosin /** 1028f66f898eSPeter Rosin * snd_interval_ranges - refine the interval value from the list of ranges 1029f66f898eSPeter Rosin * @i: the interval value to refine 1030f66f898eSPeter Rosin * @count: the number of elements in the list of ranges 1031f66f898eSPeter Rosin * @ranges: the ranges list 1032f66f898eSPeter Rosin * @mask: the bit-mask to evaluate 1033f66f898eSPeter Rosin * 1034f66f898eSPeter Rosin * Refines the interval value from the list of ranges. 1035f66f898eSPeter Rosin * When mask is non-zero, only the elements corresponding to bit 1 are 1036f66f898eSPeter Rosin * evaluated. 1037f66f898eSPeter Rosin * 1038f66f898eSPeter Rosin * Return: Positive if the value is changed, zero if it's not changed, or a 1039f66f898eSPeter Rosin * negative error code. 1040f66f898eSPeter Rosin */ 1041f66f898eSPeter Rosin int snd_interval_ranges(struct snd_interval *i, unsigned int count, 1042f66f898eSPeter Rosin const struct snd_interval *ranges, unsigned int mask) 1043f66f898eSPeter Rosin { 1044f66f898eSPeter Rosin unsigned int k; 1045f66f898eSPeter Rosin struct snd_interval range_union; 1046f66f898eSPeter Rosin struct snd_interval range; 1047f66f898eSPeter Rosin 1048f66f898eSPeter Rosin if (!count) { 1049f66f898eSPeter Rosin snd_interval_none(i); 1050f66f898eSPeter Rosin return -EINVAL; 1051f66f898eSPeter Rosin } 1052f66f898eSPeter Rosin snd_interval_any(&range_union); 1053f66f898eSPeter Rosin range_union.min = UINT_MAX; 1054f66f898eSPeter Rosin range_union.max = 0; 1055f66f898eSPeter Rosin for (k = 0; k < count; k++) { 1056f66f898eSPeter Rosin if (mask && !(mask & (1 << k))) 1057f66f898eSPeter Rosin continue; 1058f66f898eSPeter Rosin snd_interval_copy(&range, &ranges[k]); 1059f66f898eSPeter Rosin if (snd_interval_refine(&range, i) < 0) 1060f66f898eSPeter Rosin continue; 1061f66f898eSPeter Rosin if (snd_interval_empty(&range)) 1062f66f898eSPeter Rosin continue; 1063f66f898eSPeter Rosin 1064f66f898eSPeter Rosin if (range.min < range_union.min) { 1065f66f898eSPeter Rosin range_union.min = range.min; 1066f66f898eSPeter Rosin range_union.openmin = 1; 1067f66f898eSPeter Rosin } 1068f66f898eSPeter Rosin if (range.min == range_union.min && !range.openmin) 1069f66f898eSPeter Rosin range_union.openmin = 0; 1070f66f898eSPeter Rosin if (range.max > range_union.max) { 1071f66f898eSPeter Rosin range_union.max = range.max; 1072f66f898eSPeter Rosin range_union.openmax = 1; 1073f66f898eSPeter Rosin } 1074f66f898eSPeter Rosin if (range.max == range_union.max && !range.openmax) 1075f66f898eSPeter Rosin range_union.openmax = 0; 1076f66f898eSPeter Rosin } 1077f66f898eSPeter Rosin return snd_interval_refine(i, &range_union); 1078f66f898eSPeter Rosin } 1079f66f898eSPeter Rosin EXPORT_SYMBOL(snd_interval_ranges); 1080f66f898eSPeter Rosin 10810f519b62SClemens Ladisch static int snd_interval_step(struct snd_interval *i, unsigned int step) 10821da177e4SLinus Torvalds { 10831da177e4SLinus Torvalds unsigned int n; 10841da177e4SLinus Torvalds int changed = 0; 10850f519b62SClemens Ladisch n = i->min % step; 10861da177e4SLinus Torvalds if (n != 0 || i->openmin) { 10871da177e4SLinus Torvalds i->min += step - n; 1088df1e4719SClemens Ladisch i->openmin = 0; 10891da177e4SLinus Torvalds changed = 1; 10901da177e4SLinus Torvalds } 10910f519b62SClemens Ladisch n = i->max % step; 10921da177e4SLinus Torvalds if (n != 0 || i->openmax) { 10931da177e4SLinus Torvalds i->max -= n; 1094df1e4719SClemens Ladisch i->openmax = 0; 10951da177e4SLinus Torvalds changed = 1; 10961da177e4SLinus Torvalds } 10971da177e4SLinus Torvalds if (snd_interval_checkempty(i)) { 10981da177e4SLinus Torvalds i->empty = 1; 10991da177e4SLinus Torvalds return -EINVAL; 11001da177e4SLinus Torvalds } 11011da177e4SLinus Torvalds return changed; 11021da177e4SLinus Torvalds } 11031da177e4SLinus Torvalds 11041da177e4SLinus Torvalds /* Info constraints helpers */ 11051da177e4SLinus Torvalds 11061da177e4SLinus Torvalds /** 11071da177e4SLinus Torvalds * snd_pcm_hw_rule_add - add the hw-constraint rule 11081da177e4SLinus Torvalds * @runtime: the pcm runtime instance 11091da177e4SLinus Torvalds * @cond: condition bits 11101da177e4SLinus Torvalds * @var: the variable to evaluate 11111da177e4SLinus Torvalds * @func: the evaluation function 11121da177e4SLinus Torvalds * @private: the private data pointer passed to function 11131da177e4SLinus Torvalds * @dep: the dependent variables 11141da177e4SLinus Torvalds * 1115eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 11161da177e4SLinus Torvalds */ 1117877211f5STakashi Iwai int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond, 11181da177e4SLinus Torvalds int var, 11191da177e4SLinus Torvalds snd_pcm_hw_rule_func_t func, void *private, 11201da177e4SLinus Torvalds int dep, ...) 11211da177e4SLinus Torvalds { 1122877211f5STakashi Iwai struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; 1123877211f5STakashi Iwai struct snd_pcm_hw_rule *c; 11241da177e4SLinus Torvalds unsigned int k; 11251da177e4SLinus Torvalds va_list args; 11261da177e4SLinus Torvalds va_start(args, dep); 11271da177e4SLinus Torvalds if (constrs->rules_num >= constrs->rules_all) { 1128877211f5STakashi Iwai struct snd_pcm_hw_rule *new; 11291da177e4SLinus Torvalds unsigned int new_rules = constrs->rules_all + 16; 113009b9ddfaSTakashi Iwai new = krealloc(constrs->rules, new_rules * sizeof(*c), 113109b9ddfaSTakashi Iwai GFP_KERNEL); 113287a1c8aaSJesper Juhl if (!new) { 113387a1c8aaSJesper Juhl va_end(args); 11341da177e4SLinus Torvalds return -ENOMEM; 113587a1c8aaSJesper Juhl } 11361da177e4SLinus Torvalds constrs->rules = new; 11371da177e4SLinus Torvalds constrs->rules_all = new_rules; 11381da177e4SLinus Torvalds } 11391da177e4SLinus Torvalds c = &constrs->rules[constrs->rules_num]; 11401da177e4SLinus Torvalds c->cond = cond; 11411da177e4SLinus Torvalds c->func = func; 11421da177e4SLinus Torvalds c->var = var; 11431da177e4SLinus Torvalds c->private = private; 11441da177e4SLinus Torvalds k = 0; 11451da177e4SLinus Torvalds while (1) { 114687a1c8aaSJesper Juhl if (snd_BUG_ON(k >= ARRAY_SIZE(c->deps))) { 114787a1c8aaSJesper Juhl va_end(args); 11487eaa943cSTakashi Iwai return -EINVAL; 114987a1c8aaSJesper Juhl } 11501da177e4SLinus Torvalds c->deps[k++] = dep; 11511da177e4SLinus Torvalds if (dep < 0) 11521da177e4SLinus Torvalds break; 11531da177e4SLinus Torvalds dep = va_arg(args, int); 11541da177e4SLinus Torvalds } 11551da177e4SLinus Torvalds constrs->rules_num++; 11561da177e4SLinus Torvalds va_end(args); 11571da177e4SLinus Torvalds return 0; 11581da177e4SLinus Torvalds } 1159e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_rule_add); 1160e88e8ae6STakashi Iwai 11611da177e4SLinus Torvalds /** 11621c85cc64SRandy Dunlap * snd_pcm_hw_constraint_mask - apply the given bitmap mask constraint 1163df8db936STakashi Iwai * @runtime: PCM runtime instance 1164df8db936STakashi Iwai * @var: hw_params variable to apply the mask 1165df8db936STakashi Iwai * @mask: the bitmap mask 1166df8db936STakashi Iwai * 11671c85cc64SRandy Dunlap * Apply the constraint of the given bitmap mask to a 32-bit mask parameter. 1168eb7c06e8SYacine Belkadi * 1169eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 11701da177e4SLinus Torvalds */ 1171877211f5STakashi Iwai int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, 11721da177e4SLinus Torvalds u_int32_t mask) 11731da177e4SLinus Torvalds { 1174877211f5STakashi Iwai struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; 1175877211f5STakashi Iwai struct snd_mask *maskp = constrs_mask(constrs, var); 11761da177e4SLinus Torvalds *maskp->bits &= mask; 11771da177e4SLinus Torvalds memset(maskp->bits + 1, 0, (SNDRV_MASK_MAX-32) / 8); /* clear rest */ 11781da177e4SLinus Torvalds if (*maskp->bits == 0) 11791da177e4SLinus Torvalds return -EINVAL; 11801da177e4SLinus Torvalds return 0; 11811da177e4SLinus Torvalds } 11821da177e4SLinus Torvalds 11831da177e4SLinus Torvalds /** 11841c85cc64SRandy Dunlap * snd_pcm_hw_constraint_mask64 - apply the given bitmap mask constraint 1185df8db936STakashi Iwai * @runtime: PCM runtime instance 1186df8db936STakashi Iwai * @var: hw_params variable to apply the mask 1187df8db936STakashi Iwai * @mask: the 64bit bitmap mask 1188df8db936STakashi Iwai * 11891c85cc64SRandy Dunlap * Apply the constraint of the given bitmap mask to a 64-bit mask parameter. 1190eb7c06e8SYacine Belkadi * 1191eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 11921da177e4SLinus Torvalds */ 1193877211f5STakashi Iwai int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, 11941da177e4SLinus Torvalds u_int64_t mask) 11951da177e4SLinus Torvalds { 1196877211f5STakashi Iwai struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; 1197877211f5STakashi Iwai struct snd_mask *maskp = constrs_mask(constrs, var); 11981da177e4SLinus Torvalds maskp->bits[0] &= (u_int32_t)mask; 11991da177e4SLinus Torvalds maskp->bits[1] &= (u_int32_t)(mask >> 32); 12001da177e4SLinus Torvalds memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */ 12011da177e4SLinus Torvalds if (! maskp->bits[0] && ! maskp->bits[1]) 12021da177e4SLinus Torvalds return -EINVAL; 12031da177e4SLinus Torvalds return 0; 12041da177e4SLinus Torvalds } 120563a5d4c6SMark Brown EXPORT_SYMBOL(snd_pcm_hw_constraint_mask64); 12061da177e4SLinus Torvalds 12071da177e4SLinus Torvalds /** 12081c85cc64SRandy Dunlap * snd_pcm_hw_constraint_integer - apply an integer constraint to an interval 1209df8db936STakashi Iwai * @runtime: PCM runtime instance 1210df8db936STakashi Iwai * @var: hw_params variable to apply the integer constraint 1211df8db936STakashi Iwai * 1212df8db936STakashi Iwai * Apply the constraint of integer to an interval parameter. 1213eb7c06e8SYacine Belkadi * 1214eb7c06e8SYacine Belkadi * Return: Positive if the value is changed, zero if it's not changed, or a 1215eb7c06e8SYacine Belkadi * negative error code. 12161da177e4SLinus Torvalds */ 1217877211f5STakashi Iwai int snd_pcm_hw_constraint_integer(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var) 12181da177e4SLinus Torvalds { 1219877211f5STakashi Iwai struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; 12201da177e4SLinus Torvalds return snd_interval_setinteger(constrs_interval(constrs, var)); 12211da177e4SLinus Torvalds } 1222e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_integer); 1223e88e8ae6STakashi Iwai 12241da177e4SLinus Torvalds /** 12251c85cc64SRandy Dunlap * snd_pcm_hw_constraint_minmax - apply a min/max range constraint to an interval 1226df8db936STakashi Iwai * @runtime: PCM runtime instance 1227df8db936STakashi Iwai * @var: hw_params variable to apply the range 1228df8db936STakashi Iwai * @min: the minimal value 1229df8db936STakashi Iwai * @max: the maximal value 1230df8db936STakashi Iwai * 1231df8db936STakashi Iwai * Apply the min/max range constraint to an interval parameter. 1232eb7c06e8SYacine Belkadi * 1233eb7c06e8SYacine Belkadi * Return: Positive if the value is changed, zero if it's not changed, or a 1234eb7c06e8SYacine Belkadi * negative error code. 12351da177e4SLinus Torvalds */ 1236877211f5STakashi Iwai int snd_pcm_hw_constraint_minmax(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, 12371da177e4SLinus Torvalds unsigned int min, unsigned int max) 12381da177e4SLinus Torvalds { 1239877211f5STakashi Iwai struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; 1240877211f5STakashi Iwai struct snd_interval t; 12411da177e4SLinus Torvalds t.min = min; 12421da177e4SLinus Torvalds t.max = max; 12431da177e4SLinus Torvalds t.openmin = t.openmax = 0; 12441da177e4SLinus Torvalds t.integer = 0; 12451da177e4SLinus Torvalds return snd_interval_refine(constrs_interval(constrs, var), &t); 12461da177e4SLinus Torvalds } 1247e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_minmax); 1248e88e8ae6STakashi Iwai 1249877211f5STakashi Iwai static int snd_pcm_hw_rule_list(struct snd_pcm_hw_params *params, 1250877211f5STakashi Iwai struct snd_pcm_hw_rule *rule) 12511da177e4SLinus Torvalds { 1252877211f5STakashi Iwai struct snd_pcm_hw_constraint_list *list = rule->private; 12531da177e4SLinus Torvalds return snd_interval_list(hw_param_interval(params, rule->var), list->count, list->list, list->mask); 12541da177e4SLinus Torvalds } 12551da177e4SLinus Torvalds 12561da177e4SLinus Torvalds 12571da177e4SLinus Torvalds /** 12581c85cc64SRandy Dunlap * snd_pcm_hw_constraint_list - apply a list of constraints to a parameter 1259df8db936STakashi Iwai * @runtime: PCM runtime instance 1260df8db936STakashi Iwai * @cond: condition bits 1261df8db936STakashi Iwai * @var: hw_params variable to apply the list constraint 1262df8db936STakashi Iwai * @l: list 1263df8db936STakashi Iwai * 1264df8db936STakashi Iwai * Apply the list of constraints to an interval parameter. 1265eb7c06e8SYacine Belkadi * 1266eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 12671da177e4SLinus Torvalds */ 1268877211f5STakashi Iwai int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime, 12691da177e4SLinus Torvalds unsigned int cond, 12701da177e4SLinus Torvalds snd_pcm_hw_param_t var, 12711464189fSMark Brown const struct snd_pcm_hw_constraint_list *l) 12721da177e4SLinus Torvalds { 12731da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 12741464189fSMark Brown snd_pcm_hw_rule_list, (void *)l, 12751da177e4SLinus Torvalds var, -1); 12761da177e4SLinus Torvalds } 1277e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_list); 1278e88e8ae6STakashi Iwai 1279f66f898eSPeter Rosin static int snd_pcm_hw_rule_ranges(struct snd_pcm_hw_params *params, 1280f66f898eSPeter Rosin struct snd_pcm_hw_rule *rule) 1281f66f898eSPeter Rosin { 1282f66f898eSPeter Rosin struct snd_pcm_hw_constraint_ranges *r = rule->private; 1283f66f898eSPeter Rosin return snd_interval_ranges(hw_param_interval(params, rule->var), 1284f66f898eSPeter Rosin r->count, r->ranges, r->mask); 1285f66f898eSPeter Rosin } 1286f66f898eSPeter Rosin 1287f66f898eSPeter Rosin 1288f66f898eSPeter Rosin /** 1289f66f898eSPeter Rosin * snd_pcm_hw_constraint_ranges - apply list of range constraints to a parameter 1290f66f898eSPeter Rosin * @runtime: PCM runtime instance 1291f66f898eSPeter Rosin * @cond: condition bits 1292f66f898eSPeter Rosin * @var: hw_params variable to apply the list of range constraints 1293f66f898eSPeter Rosin * @r: ranges 1294f66f898eSPeter Rosin * 1295f66f898eSPeter Rosin * Apply the list of range constraints to an interval parameter. 1296f66f898eSPeter Rosin * 1297f66f898eSPeter Rosin * Return: Zero if successful, or a negative error code on failure. 1298f66f898eSPeter Rosin */ 1299f66f898eSPeter Rosin int snd_pcm_hw_constraint_ranges(struct snd_pcm_runtime *runtime, 1300f66f898eSPeter Rosin unsigned int cond, 1301f66f898eSPeter Rosin snd_pcm_hw_param_t var, 1302f66f898eSPeter Rosin const struct snd_pcm_hw_constraint_ranges *r) 1303f66f898eSPeter Rosin { 1304f66f898eSPeter Rosin return snd_pcm_hw_rule_add(runtime, cond, var, 1305f66f898eSPeter Rosin snd_pcm_hw_rule_ranges, (void *)r, 1306f66f898eSPeter Rosin var, -1); 1307f66f898eSPeter Rosin } 1308f66f898eSPeter Rosin EXPORT_SYMBOL(snd_pcm_hw_constraint_ranges); 1309f66f898eSPeter Rosin 1310877211f5STakashi Iwai static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params, 1311877211f5STakashi Iwai struct snd_pcm_hw_rule *rule) 13121da177e4SLinus Torvalds { 1313e5e113cfSLars-Peter Clausen const struct snd_pcm_hw_constraint_ratnums *r = rule->private; 13141da177e4SLinus Torvalds unsigned int num = 0, den = 0; 13151da177e4SLinus Torvalds int err; 13161da177e4SLinus Torvalds err = snd_interval_ratnum(hw_param_interval(params, rule->var), 13171da177e4SLinus Torvalds r->nrats, r->rats, &num, &den); 13181da177e4SLinus Torvalds if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { 13191da177e4SLinus Torvalds params->rate_num = num; 13201da177e4SLinus Torvalds params->rate_den = den; 13211da177e4SLinus Torvalds } 13221da177e4SLinus Torvalds return err; 13231da177e4SLinus Torvalds } 13241da177e4SLinus Torvalds 13251da177e4SLinus Torvalds /** 13261c85cc64SRandy Dunlap * snd_pcm_hw_constraint_ratnums - apply ratnums constraint to a parameter 1327df8db936STakashi Iwai * @runtime: PCM runtime instance 1328df8db936STakashi Iwai * @cond: condition bits 1329df8db936STakashi Iwai * @var: hw_params variable to apply the ratnums constraint 1330877211f5STakashi Iwai * @r: struct snd_ratnums constriants 1331eb7c06e8SYacine Belkadi * 1332eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 13331da177e4SLinus Torvalds */ 1334877211f5STakashi Iwai int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime, 13351da177e4SLinus Torvalds unsigned int cond, 13361da177e4SLinus Torvalds snd_pcm_hw_param_t var, 1337e5e113cfSLars-Peter Clausen const struct snd_pcm_hw_constraint_ratnums *r) 13381da177e4SLinus Torvalds { 13391da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 1340e5e113cfSLars-Peter Clausen snd_pcm_hw_rule_ratnums, (void *)r, 13411da177e4SLinus Torvalds var, -1); 13421da177e4SLinus Torvalds } 1343e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums); 1344e88e8ae6STakashi Iwai 1345877211f5STakashi Iwai static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params, 1346877211f5STakashi Iwai struct snd_pcm_hw_rule *rule) 13471da177e4SLinus Torvalds { 1348e5e113cfSLars-Peter Clausen const struct snd_pcm_hw_constraint_ratdens *r = rule->private; 13491da177e4SLinus Torvalds unsigned int num = 0, den = 0; 13501da177e4SLinus Torvalds int err = snd_interval_ratden(hw_param_interval(params, rule->var), 13511da177e4SLinus Torvalds r->nrats, r->rats, &num, &den); 13521da177e4SLinus Torvalds if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { 13531da177e4SLinus Torvalds params->rate_num = num; 13541da177e4SLinus Torvalds params->rate_den = den; 13551da177e4SLinus Torvalds } 13561da177e4SLinus Torvalds return err; 13571da177e4SLinus Torvalds } 13581da177e4SLinus Torvalds 13591da177e4SLinus Torvalds /** 13601c85cc64SRandy Dunlap * snd_pcm_hw_constraint_ratdens - apply ratdens constraint to a parameter 1361df8db936STakashi Iwai * @runtime: PCM runtime instance 1362df8db936STakashi Iwai * @cond: condition bits 1363df8db936STakashi Iwai * @var: hw_params variable to apply the ratdens constraint 1364877211f5STakashi Iwai * @r: struct snd_ratdens constriants 1365eb7c06e8SYacine Belkadi * 1366eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 13671da177e4SLinus Torvalds */ 1368877211f5STakashi Iwai int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime, 13691da177e4SLinus Torvalds unsigned int cond, 13701da177e4SLinus Torvalds snd_pcm_hw_param_t var, 1371e5e113cfSLars-Peter Clausen const struct snd_pcm_hw_constraint_ratdens *r) 13721da177e4SLinus Torvalds { 13731da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 1374e5e113cfSLars-Peter Clausen snd_pcm_hw_rule_ratdens, (void *)r, 13751da177e4SLinus Torvalds var, -1); 13761da177e4SLinus Torvalds } 1377e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_ratdens); 1378e88e8ae6STakashi Iwai 1379877211f5STakashi Iwai static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params, 1380877211f5STakashi Iwai struct snd_pcm_hw_rule *rule) 13811da177e4SLinus Torvalds { 13821da177e4SLinus Torvalds unsigned int l = (unsigned long) rule->private; 13831da177e4SLinus Torvalds int width = l & 0xffff; 13841da177e4SLinus Torvalds unsigned int msbits = l >> 16; 1385b55f9fdcSTakashi Sakamoto const struct snd_interval *i = 1386b55f9fdcSTakashi Sakamoto hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); 13878ef9df55SLars-Peter Clausen 13888ef9df55SLars-Peter Clausen if (!snd_interval_single(i)) 13898ef9df55SLars-Peter Clausen return 0; 13908ef9df55SLars-Peter Clausen 13918ef9df55SLars-Peter Clausen if ((snd_interval_value(i) == width) || 13928ef9df55SLars-Peter Clausen (width == 0 && snd_interval_value(i) > msbits)) 139319f52faeSLars-Peter Clausen params->msbits = min_not_zero(params->msbits, msbits); 13948ef9df55SLars-Peter Clausen 13951da177e4SLinus Torvalds return 0; 13961da177e4SLinus Torvalds } 13971da177e4SLinus Torvalds 13981da177e4SLinus Torvalds /** 13991c85cc64SRandy Dunlap * snd_pcm_hw_constraint_msbits - add a hw constraint msbits rule 1400df8db936STakashi Iwai * @runtime: PCM runtime instance 1401df8db936STakashi Iwai * @cond: condition bits 1402df8db936STakashi Iwai * @width: sample bits width 1403df8db936STakashi Iwai * @msbits: msbits width 1404eb7c06e8SYacine Belkadi * 14058ef9df55SLars-Peter Clausen * This constraint will set the number of most significant bits (msbits) if a 14068ef9df55SLars-Peter Clausen * sample format with the specified width has been select. If width is set to 0 14078ef9df55SLars-Peter Clausen * the msbits will be set for any sample format with a width larger than the 14088ef9df55SLars-Peter Clausen * specified msbits. 14098ef9df55SLars-Peter Clausen * 1410eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 14111da177e4SLinus Torvalds */ 1412877211f5STakashi Iwai int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime, 14131da177e4SLinus Torvalds unsigned int cond, 14141da177e4SLinus Torvalds unsigned int width, 14151da177e4SLinus Torvalds unsigned int msbits) 14161da177e4SLinus Torvalds { 14171da177e4SLinus Torvalds unsigned long l = (msbits << 16) | width; 14181da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, -1, 14191da177e4SLinus Torvalds snd_pcm_hw_rule_msbits, 14201da177e4SLinus Torvalds (void*) l, 14211da177e4SLinus Torvalds SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); 14221da177e4SLinus Torvalds } 1423e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_msbits); 1424e88e8ae6STakashi Iwai 1425877211f5STakashi Iwai static int snd_pcm_hw_rule_step(struct snd_pcm_hw_params *params, 1426877211f5STakashi Iwai struct snd_pcm_hw_rule *rule) 14271da177e4SLinus Torvalds { 14281da177e4SLinus Torvalds unsigned long step = (unsigned long) rule->private; 14290f519b62SClemens Ladisch return snd_interval_step(hw_param_interval(params, rule->var), step); 14301da177e4SLinus Torvalds } 14311da177e4SLinus Torvalds 14321da177e4SLinus Torvalds /** 14331c85cc64SRandy Dunlap * snd_pcm_hw_constraint_step - add a hw constraint step rule 1434df8db936STakashi Iwai * @runtime: PCM runtime instance 1435df8db936STakashi Iwai * @cond: condition bits 1436df8db936STakashi Iwai * @var: hw_params variable to apply the step constraint 1437df8db936STakashi Iwai * @step: step size 1438eb7c06e8SYacine Belkadi * 1439eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 14401da177e4SLinus Torvalds */ 1441877211f5STakashi Iwai int snd_pcm_hw_constraint_step(struct snd_pcm_runtime *runtime, 14421da177e4SLinus Torvalds unsigned int cond, 14431da177e4SLinus Torvalds snd_pcm_hw_param_t var, 14441da177e4SLinus Torvalds unsigned long step) 14451da177e4SLinus Torvalds { 14461da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 14471da177e4SLinus Torvalds snd_pcm_hw_rule_step, (void *) step, 14481da177e4SLinus Torvalds var, -1); 14491da177e4SLinus Torvalds } 1450e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_step); 1451e88e8ae6STakashi Iwai 1452877211f5STakashi Iwai static int snd_pcm_hw_rule_pow2(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) 14531da177e4SLinus Torvalds { 145467c39317SMarcin Ślusarz static unsigned int pow2_sizes[] = { 14551da177e4SLinus Torvalds 1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7, 14561da177e4SLinus Torvalds 1<<8, 1<<9, 1<<10, 1<<11, 1<<12, 1<<13, 1<<14, 1<<15, 14571da177e4SLinus Torvalds 1<<16, 1<<17, 1<<18, 1<<19, 1<<20, 1<<21, 1<<22, 1<<23, 14581da177e4SLinus Torvalds 1<<24, 1<<25, 1<<26, 1<<27, 1<<28, 1<<29, 1<<30 14591da177e4SLinus Torvalds }; 14601da177e4SLinus Torvalds return snd_interval_list(hw_param_interval(params, rule->var), 14611da177e4SLinus Torvalds ARRAY_SIZE(pow2_sizes), pow2_sizes, 0); 14621da177e4SLinus Torvalds } 14631da177e4SLinus Torvalds 14641da177e4SLinus Torvalds /** 14651c85cc64SRandy Dunlap * snd_pcm_hw_constraint_pow2 - add a hw constraint power-of-2 rule 1466df8db936STakashi Iwai * @runtime: PCM runtime instance 1467df8db936STakashi Iwai * @cond: condition bits 1468df8db936STakashi Iwai * @var: hw_params variable to apply the power-of-2 constraint 1469eb7c06e8SYacine Belkadi * 1470eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 14711da177e4SLinus Torvalds */ 1472877211f5STakashi Iwai int snd_pcm_hw_constraint_pow2(struct snd_pcm_runtime *runtime, 14731da177e4SLinus Torvalds unsigned int cond, 14741da177e4SLinus Torvalds snd_pcm_hw_param_t var) 14751da177e4SLinus Torvalds { 14761da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 14771da177e4SLinus Torvalds snd_pcm_hw_rule_pow2, NULL, 14781da177e4SLinus Torvalds var, -1); 14791da177e4SLinus Torvalds } 1480e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_pow2); 1481e88e8ae6STakashi Iwai 1482d5b702a6SClemens Ladisch static int snd_pcm_hw_rule_noresample_func(struct snd_pcm_hw_params *params, 1483d5b702a6SClemens Ladisch struct snd_pcm_hw_rule *rule) 1484d5b702a6SClemens Ladisch { 1485d5b702a6SClemens Ladisch unsigned int base_rate = (unsigned int)(uintptr_t)rule->private; 1486d5b702a6SClemens Ladisch struct snd_interval *rate; 1487d5b702a6SClemens Ladisch 1488d5b702a6SClemens Ladisch rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); 1489d5b702a6SClemens Ladisch return snd_interval_list(rate, 1, &base_rate, 0); 1490d5b702a6SClemens Ladisch } 1491d5b702a6SClemens Ladisch 1492d5b702a6SClemens Ladisch /** 1493d5b702a6SClemens Ladisch * snd_pcm_hw_rule_noresample - add a rule to allow disabling hw resampling 1494d5b702a6SClemens Ladisch * @runtime: PCM runtime instance 1495d5b702a6SClemens Ladisch * @base_rate: the rate at which the hardware does not resample 1496eb7c06e8SYacine Belkadi * 1497eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 1498d5b702a6SClemens Ladisch */ 1499d5b702a6SClemens Ladisch int snd_pcm_hw_rule_noresample(struct snd_pcm_runtime *runtime, 1500d5b702a6SClemens Ladisch unsigned int base_rate) 1501d5b702a6SClemens Ladisch { 1502d5b702a6SClemens Ladisch return snd_pcm_hw_rule_add(runtime, SNDRV_PCM_HW_PARAMS_NORESAMPLE, 1503d5b702a6SClemens Ladisch SNDRV_PCM_HW_PARAM_RATE, 1504d5b702a6SClemens Ladisch snd_pcm_hw_rule_noresample_func, 1505d5b702a6SClemens Ladisch (void *)(uintptr_t)base_rate, 1506d5b702a6SClemens Ladisch SNDRV_PCM_HW_PARAM_RATE, -1); 1507d5b702a6SClemens Ladisch } 1508d5b702a6SClemens Ladisch EXPORT_SYMBOL(snd_pcm_hw_rule_noresample); 1509d5b702a6SClemens Ladisch 1510877211f5STakashi Iwai static void _snd_pcm_hw_param_any(struct snd_pcm_hw_params *params, 1511123992f7SAdrian Bunk snd_pcm_hw_param_t var) 15121da177e4SLinus Torvalds { 15131da177e4SLinus Torvalds if (hw_is_mask(var)) { 15141da177e4SLinus Torvalds snd_mask_any(hw_param_mask(params, var)); 15151da177e4SLinus Torvalds params->cmask |= 1 << var; 15161da177e4SLinus Torvalds params->rmask |= 1 << var; 15171da177e4SLinus Torvalds return; 15181da177e4SLinus Torvalds } 15191da177e4SLinus Torvalds if (hw_is_interval(var)) { 15201da177e4SLinus Torvalds snd_interval_any(hw_param_interval(params, var)); 15211da177e4SLinus Torvalds params->cmask |= 1 << var; 15221da177e4SLinus Torvalds params->rmask |= 1 << var; 15231da177e4SLinus Torvalds return; 15241da177e4SLinus Torvalds } 15251da177e4SLinus Torvalds snd_BUG(); 15261da177e4SLinus Torvalds } 15271da177e4SLinus Torvalds 1528877211f5STakashi Iwai void _snd_pcm_hw_params_any(struct snd_pcm_hw_params *params) 15291da177e4SLinus Torvalds { 15301da177e4SLinus Torvalds unsigned int k; 15311da177e4SLinus Torvalds memset(params, 0, sizeof(*params)); 15321da177e4SLinus Torvalds for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) 15331da177e4SLinus Torvalds _snd_pcm_hw_param_any(params, k); 15341da177e4SLinus Torvalds for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) 15351da177e4SLinus Torvalds _snd_pcm_hw_param_any(params, k); 15361da177e4SLinus Torvalds params->info = ~0U; 15371da177e4SLinus Torvalds } 1538e88e8ae6STakashi Iwai EXPORT_SYMBOL(_snd_pcm_hw_params_any); 15391da177e4SLinus Torvalds 15401da177e4SLinus Torvalds /** 15411c85cc64SRandy Dunlap * snd_pcm_hw_param_value - return @params field @var value 1542df8db936STakashi Iwai * @params: the hw_params instance 1543df8db936STakashi Iwai * @var: parameter to retrieve 15441c85cc64SRandy Dunlap * @dir: pointer to the direction (-1,0,1) or %NULL 15451da177e4SLinus Torvalds * 1546eb7c06e8SYacine Belkadi * Return: The value for field @var if it's fixed in configuration space 1547eb7c06e8SYacine Belkadi * defined by @params. -%EINVAL otherwise. 15481da177e4SLinus Torvalds */ 1549e88e8ae6STakashi Iwai int snd_pcm_hw_param_value(const struct snd_pcm_hw_params *params, 15501da177e4SLinus Torvalds snd_pcm_hw_param_t var, int *dir) 15511da177e4SLinus Torvalds { 15521da177e4SLinus Torvalds if (hw_is_mask(var)) { 1553877211f5STakashi Iwai const struct snd_mask *mask = hw_param_mask_c(params, var); 15541da177e4SLinus Torvalds if (!snd_mask_single(mask)) 15551da177e4SLinus Torvalds return -EINVAL; 15561da177e4SLinus Torvalds if (dir) 15571da177e4SLinus Torvalds *dir = 0; 15581da177e4SLinus Torvalds return snd_mask_value(mask); 15591da177e4SLinus Torvalds } 15601da177e4SLinus Torvalds if (hw_is_interval(var)) { 1561877211f5STakashi Iwai const struct snd_interval *i = hw_param_interval_c(params, var); 15621da177e4SLinus Torvalds if (!snd_interval_single(i)) 15631da177e4SLinus Torvalds return -EINVAL; 15641da177e4SLinus Torvalds if (dir) 15651da177e4SLinus Torvalds *dir = i->openmin; 15661da177e4SLinus Torvalds return snd_interval_value(i); 15671da177e4SLinus Torvalds } 15681da177e4SLinus Torvalds return -EINVAL; 15691da177e4SLinus Torvalds } 1570e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_param_value); 15711da177e4SLinus Torvalds 1572877211f5STakashi Iwai void _snd_pcm_hw_param_setempty(struct snd_pcm_hw_params *params, 15731da177e4SLinus Torvalds snd_pcm_hw_param_t var) 15741da177e4SLinus Torvalds { 15751da177e4SLinus Torvalds if (hw_is_mask(var)) { 15761da177e4SLinus Torvalds snd_mask_none(hw_param_mask(params, var)); 15771da177e4SLinus Torvalds params->cmask |= 1 << var; 15781da177e4SLinus Torvalds params->rmask |= 1 << var; 15791da177e4SLinus Torvalds } else if (hw_is_interval(var)) { 15801da177e4SLinus Torvalds snd_interval_none(hw_param_interval(params, var)); 15811da177e4SLinus Torvalds params->cmask |= 1 << var; 15821da177e4SLinus Torvalds params->rmask |= 1 << var; 15831da177e4SLinus Torvalds } else { 15841da177e4SLinus Torvalds snd_BUG(); 15851da177e4SLinus Torvalds } 15861da177e4SLinus Torvalds } 1587e88e8ae6STakashi Iwai EXPORT_SYMBOL(_snd_pcm_hw_param_setempty); 15881da177e4SLinus Torvalds 1589877211f5STakashi Iwai static int _snd_pcm_hw_param_first(struct snd_pcm_hw_params *params, 15901da177e4SLinus Torvalds snd_pcm_hw_param_t var) 15911da177e4SLinus Torvalds { 15921da177e4SLinus Torvalds int changed; 15931da177e4SLinus Torvalds if (hw_is_mask(var)) 15941da177e4SLinus Torvalds changed = snd_mask_refine_first(hw_param_mask(params, var)); 15951da177e4SLinus Torvalds else if (hw_is_interval(var)) 15961da177e4SLinus Torvalds changed = snd_interval_refine_first(hw_param_interval(params, var)); 15972f4ca8e5STakashi Iwai else 15981da177e4SLinus Torvalds return -EINVAL; 15997a0a8716STakashi Iwai if (changed > 0) { 16001da177e4SLinus Torvalds params->cmask |= 1 << var; 16011da177e4SLinus Torvalds params->rmask |= 1 << var; 16021da177e4SLinus Torvalds } 16031da177e4SLinus Torvalds return changed; 16041da177e4SLinus Torvalds } 16051da177e4SLinus Torvalds 16061da177e4SLinus Torvalds 16071da177e4SLinus Torvalds /** 16081c85cc64SRandy Dunlap * snd_pcm_hw_param_first - refine config space and return minimum value 1609df8db936STakashi Iwai * @pcm: PCM instance 1610df8db936STakashi Iwai * @params: the hw_params instance 1611df8db936STakashi Iwai * @var: parameter to retrieve 16121c85cc64SRandy Dunlap * @dir: pointer to the direction (-1,0,1) or %NULL 16131da177e4SLinus Torvalds * 16141c85cc64SRandy Dunlap * Inside configuration space defined by @params remove from @var all 16151da177e4SLinus Torvalds * values > minimum. Reduce configuration space accordingly. 1616eb7c06e8SYacine Belkadi * 1617eb7c06e8SYacine Belkadi * Return: The minimum, or a negative error code on failure. 16181da177e4SLinus Torvalds */ 1619e88e8ae6STakashi Iwai int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm, 1620877211f5STakashi Iwai struct snd_pcm_hw_params *params, 16211da177e4SLinus Torvalds snd_pcm_hw_param_t var, int *dir) 16221da177e4SLinus Torvalds { 16231da177e4SLinus Torvalds int changed = _snd_pcm_hw_param_first(params, var); 16241da177e4SLinus Torvalds if (changed < 0) 16251da177e4SLinus Torvalds return changed; 16261da177e4SLinus Torvalds if (params->rmask) { 16271da177e4SLinus Torvalds int err = snd_pcm_hw_refine(pcm, params); 1628fe08f34dSTakashi Iwai if (err < 0) 16297eaa943cSTakashi Iwai return err; 16301da177e4SLinus Torvalds } 16311da177e4SLinus Torvalds return snd_pcm_hw_param_value(params, var, dir); 16321da177e4SLinus Torvalds } 1633e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_param_first); 1634e88e8ae6STakashi Iwai 1635877211f5STakashi Iwai static int _snd_pcm_hw_param_last(struct snd_pcm_hw_params *params, 16361da177e4SLinus Torvalds snd_pcm_hw_param_t var) 16371da177e4SLinus Torvalds { 16381da177e4SLinus Torvalds int changed; 16391da177e4SLinus Torvalds if (hw_is_mask(var)) 16401da177e4SLinus Torvalds changed = snd_mask_refine_last(hw_param_mask(params, var)); 16411da177e4SLinus Torvalds else if (hw_is_interval(var)) 16421da177e4SLinus Torvalds changed = snd_interval_refine_last(hw_param_interval(params, var)); 16432f4ca8e5STakashi Iwai else 16441da177e4SLinus Torvalds return -EINVAL; 16457a0a8716STakashi Iwai if (changed > 0) { 16461da177e4SLinus Torvalds params->cmask |= 1 << var; 16471da177e4SLinus Torvalds params->rmask |= 1 << var; 16481da177e4SLinus Torvalds } 16491da177e4SLinus Torvalds return changed; 16501da177e4SLinus Torvalds } 16511da177e4SLinus Torvalds 16521da177e4SLinus Torvalds 16531da177e4SLinus Torvalds /** 16541c85cc64SRandy Dunlap * snd_pcm_hw_param_last - refine config space and return maximum value 1655df8db936STakashi Iwai * @pcm: PCM instance 1656df8db936STakashi Iwai * @params: the hw_params instance 1657df8db936STakashi Iwai * @var: parameter to retrieve 16581c85cc64SRandy Dunlap * @dir: pointer to the direction (-1,0,1) or %NULL 16591da177e4SLinus Torvalds * 16601c85cc64SRandy Dunlap * Inside configuration space defined by @params remove from @var all 16611da177e4SLinus Torvalds * values < maximum. Reduce configuration space accordingly. 1662eb7c06e8SYacine Belkadi * 1663eb7c06e8SYacine Belkadi * Return: The maximum, or a negative error code on failure. 16641da177e4SLinus Torvalds */ 1665e88e8ae6STakashi Iwai int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm, 1666877211f5STakashi Iwai struct snd_pcm_hw_params *params, 16671da177e4SLinus Torvalds snd_pcm_hw_param_t var, int *dir) 16681da177e4SLinus Torvalds { 16691da177e4SLinus Torvalds int changed = _snd_pcm_hw_param_last(params, var); 16701da177e4SLinus Torvalds if (changed < 0) 16711da177e4SLinus Torvalds return changed; 16721da177e4SLinus Torvalds if (params->rmask) { 16731da177e4SLinus Torvalds int err = snd_pcm_hw_refine(pcm, params); 1674fe08f34dSTakashi Iwai if (err < 0) 16757eaa943cSTakashi Iwai return err; 16761da177e4SLinus Torvalds } 16771da177e4SLinus Torvalds return snd_pcm_hw_param_value(params, var, dir); 16781da177e4SLinus Torvalds } 1679e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_param_last); 16801da177e4SLinus Torvalds 1681877211f5STakashi Iwai static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream, 16821da177e4SLinus Torvalds void *arg) 16831da177e4SLinus Torvalds { 1684877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 16851da177e4SLinus Torvalds unsigned long flags; 16861da177e4SLinus Torvalds snd_pcm_stream_lock_irqsave(substream, flags); 16871da177e4SLinus Torvalds if (snd_pcm_running(substream) && 16881da177e4SLinus Torvalds snd_pcm_update_hw_ptr(substream) >= 0) 16891da177e4SLinus Torvalds runtime->status->hw_ptr %= runtime->buffer_size; 16900e8014d7SPierre-Louis Bossart else { 16911da177e4SLinus Torvalds runtime->status->hw_ptr = 0; 16920e8014d7SPierre-Louis Bossart runtime->hw_ptr_wrap = 0; 16930e8014d7SPierre-Louis Bossart } 16941da177e4SLinus Torvalds snd_pcm_stream_unlock_irqrestore(substream, flags); 16951da177e4SLinus Torvalds return 0; 16961da177e4SLinus Torvalds } 16971da177e4SLinus Torvalds 1698877211f5STakashi Iwai static int snd_pcm_lib_ioctl_channel_info(struct snd_pcm_substream *substream, 16991da177e4SLinus Torvalds void *arg) 17001da177e4SLinus Torvalds { 1701877211f5STakashi Iwai struct snd_pcm_channel_info *info = arg; 1702877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 17031da177e4SLinus Torvalds int width; 17041da177e4SLinus Torvalds if (!(runtime->info & SNDRV_PCM_INFO_MMAP)) { 17051da177e4SLinus Torvalds info->offset = -1; 17061da177e4SLinus Torvalds return 0; 17071da177e4SLinus Torvalds } 17081da177e4SLinus Torvalds width = snd_pcm_format_physical_width(runtime->format); 17091da177e4SLinus Torvalds if (width < 0) 17101da177e4SLinus Torvalds return width; 17111da177e4SLinus Torvalds info->offset = 0; 17121da177e4SLinus Torvalds switch (runtime->access) { 17131da177e4SLinus Torvalds case SNDRV_PCM_ACCESS_MMAP_INTERLEAVED: 17141da177e4SLinus Torvalds case SNDRV_PCM_ACCESS_RW_INTERLEAVED: 17151da177e4SLinus Torvalds info->first = info->channel * width; 17161da177e4SLinus Torvalds info->step = runtime->channels * width; 17171da177e4SLinus Torvalds break; 17181da177e4SLinus Torvalds case SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED: 17191da177e4SLinus Torvalds case SNDRV_PCM_ACCESS_RW_NONINTERLEAVED: 17201da177e4SLinus Torvalds { 17211da177e4SLinus Torvalds size_t size = runtime->dma_bytes / runtime->channels; 17221da177e4SLinus Torvalds info->first = info->channel * size * 8; 17231da177e4SLinus Torvalds info->step = width; 17241da177e4SLinus Torvalds break; 17251da177e4SLinus Torvalds } 17261da177e4SLinus Torvalds default: 17271da177e4SLinus Torvalds snd_BUG(); 17281da177e4SLinus Torvalds break; 17291da177e4SLinus Torvalds } 17301da177e4SLinus Torvalds return 0; 17311da177e4SLinus Torvalds } 17321da177e4SLinus Torvalds 17338bea869cSJaroslav Kysela static int snd_pcm_lib_ioctl_fifo_size(struct snd_pcm_substream *substream, 17348bea869cSJaroslav Kysela void *arg) 17358bea869cSJaroslav Kysela { 17368bea869cSJaroslav Kysela struct snd_pcm_hw_params *params = arg; 17378bea869cSJaroslav Kysela snd_pcm_format_t format; 1738a9960e6aSClemens Ladisch int channels; 1739a9960e6aSClemens Ladisch ssize_t frame_size; 17408bea869cSJaroslav Kysela 17418bea869cSJaroslav Kysela params->fifo_size = substream->runtime->hw.fifo_size; 17428bea869cSJaroslav Kysela if (!(substream->runtime->hw.info & SNDRV_PCM_INFO_FIFO_IN_FRAMES)) { 17438bea869cSJaroslav Kysela format = params_format(params); 17448bea869cSJaroslav Kysela channels = params_channels(params); 1745a9960e6aSClemens Ladisch frame_size = snd_pcm_format_size(format, channels); 1746a9960e6aSClemens Ladisch if (frame_size > 0) 1747a9960e6aSClemens Ladisch params->fifo_size /= (unsigned)frame_size; 17488bea869cSJaroslav Kysela } 17498bea869cSJaroslav Kysela return 0; 17508bea869cSJaroslav Kysela } 17518bea869cSJaroslav Kysela 17521da177e4SLinus Torvalds /** 17531da177e4SLinus Torvalds * snd_pcm_lib_ioctl - a generic PCM ioctl callback 17541da177e4SLinus Torvalds * @substream: the pcm substream instance 17551da177e4SLinus Torvalds * @cmd: ioctl command 17561da177e4SLinus Torvalds * @arg: ioctl argument 17571da177e4SLinus Torvalds * 17581da177e4SLinus Torvalds * Processes the generic ioctl commands for PCM. 17591da177e4SLinus Torvalds * Can be passed as the ioctl callback for PCM ops. 17601da177e4SLinus Torvalds * 1761eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 17621da177e4SLinus Torvalds */ 1763877211f5STakashi Iwai int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream, 17641da177e4SLinus Torvalds unsigned int cmd, void *arg) 17651da177e4SLinus Torvalds { 17661da177e4SLinus Torvalds switch (cmd) { 17671da177e4SLinus Torvalds case SNDRV_PCM_IOCTL1_RESET: 17681da177e4SLinus Torvalds return snd_pcm_lib_ioctl_reset(substream, arg); 17691da177e4SLinus Torvalds case SNDRV_PCM_IOCTL1_CHANNEL_INFO: 17701da177e4SLinus Torvalds return snd_pcm_lib_ioctl_channel_info(substream, arg); 17718bea869cSJaroslav Kysela case SNDRV_PCM_IOCTL1_FIFO_SIZE: 17728bea869cSJaroslav Kysela return snd_pcm_lib_ioctl_fifo_size(substream, arg); 17731da177e4SLinus Torvalds } 17741da177e4SLinus Torvalds return -ENXIO; 17751da177e4SLinus Torvalds } 1776e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_lib_ioctl); 1777e88e8ae6STakashi Iwai 17781da177e4SLinus Torvalds /** 17791da177e4SLinus Torvalds * snd_pcm_period_elapsed - update the pcm status for the next period 17801da177e4SLinus Torvalds * @substream: the pcm substream instance 17811da177e4SLinus Torvalds * 17821da177e4SLinus Torvalds * This function is called from the interrupt handler when the 17831da177e4SLinus Torvalds * PCM has processed the period size. It will update the current 178431e8960bSTakashi Iwai * pointer, wake up sleepers, etc. 17851da177e4SLinus Torvalds * 17861da177e4SLinus Torvalds * Even if more than one periods have elapsed since the last call, you 17871da177e4SLinus Torvalds * have to call this only once. 17881da177e4SLinus Torvalds */ 1789877211f5STakashi Iwai void snd_pcm_period_elapsed(struct snd_pcm_substream *substream) 17901da177e4SLinus Torvalds { 1791877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 17921da177e4SLinus Torvalds unsigned long flags; 17931da177e4SLinus Torvalds 17947eaa943cSTakashi Iwai if (PCM_RUNTIME_CHECK(substream)) 17957eaa943cSTakashi Iwai return; 17961da177e4SLinus Torvalds runtime = substream->runtime; 17971da177e4SLinus Torvalds 17981da177e4SLinus Torvalds snd_pcm_stream_lock_irqsave(substream, flags); 17991da177e4SLinus Torvalds if (!snd_pcm_running(substream) || 1800f240406bSJaroslav Kysela snd_pcm_update_hw_ptr0(substream, 1) < 0) 18011da177e4SLinus Torvalds goto _end; 18021da177e4SLinus Torvalds 180390bbaf66SJie Yang #ifdef CONFIG_SND_PCM_TIMER 18041da177e4SLinus Torvalds if (substream->timer_running) 18051da177e4SLinus Torvalds snd_timer_interrupt(substream->timer, 1); 180690bbaf66SJie Yang #endif 18071da177e4SLinus Torvalds _end: 18081da177e4SLinus Torvalds kill_fasync(&runtime->fasync, SIGIO, POLL_IN); 18093aa02cb6STakashi Iwai snd_pcm_stream_unlock_irqrestore(substream, flags); 18101da177e4SLinus Torvalds } 1811e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_period_elapsed); 1812e88e8ae6STakashi Iwai 181313075510STakashi Iwai /* 181413075510STakashi Iwai * Wait until avail_min data becomes available 181513075510STakashi Iwai * Returns a negative error code if any error occurs during operation. 181613075510STakashi Iwai * The available space is stored on availp. When err = 0 and avail = 0 181713075510STakashi Iwai * on the capture stream, it indicates the stream is in DRAINING state. 181813075510STakashi Iwai */ 18195daeba34SDavid Dillow static int wait_for_avail(struct snd_pcm_substream *substream, 182013075510STakashi Iwai snd_pcm_uframes_t *availp) 182113075510STakashi Iwai { 182213075510STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 182313075510STakashi Iwai int is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 1824ac6424b9SIngo Molnar wait_queue_entry_t wait; 182513075510STakashi Iwai int err = 0; 182613075510STakashi Iwai snd_pcm_uframes_t avail = 0; 1827f2b3614cSTakashi Iwai long wait_time, tout; 182813075510STakashi Iwai 1829763437a9SArjan van de Ven init_waitqueue_entry(&wait, current); 1830763437a9SArjan van de Ven set_current_state(TASK_INTERRUPTIBLE); 1831763437a9SArjan van de Ven add_wait_queue(&runtime->tsleep, &wait); 1832763437a9SArjan van de Ven 1833f2b3614cSTakashi Iwai if (runtime->no_period_wakeup) 1834f2b3614cSTakashi Iwai wait_time = MAX_SCHEDULE_TIMEOUT; 1835f2b3614cSTakashi Iwai else { 1836d64c5cf8SLiam Girdwood /* use wait time from substream if available */ 1837d64c5cf8SLiam Girdwood if (substream->wait_time) { 1838d64c5cf8SLiam Girdwood wait_time = substream->wait_time; 1839d64c5cf8SLiam Girdwood } else { 1840f2b3614cSTakashi Iwai wait_time = 10; 1841d64c5cf8SLiam Girdwood 1842f2b3614cSTakashi Iwai if (runtime->rate) { 1843d64c5cf8SLiam Girdwood long t = runtime->period_size * 2 / 1844d64c5cf8SLiam Girdwood runtime->rate; 1845f2b3614cSTakashi Iwai wait_time = max(t, wait_time); 1846f2b3614cSTakashi Iwai } 1847f2b3614cSTakashi Iwai wait_time = msecs_to_jiffies(wait_time * 1000); 1848f2b3614cSTakashi Iwai } 1849d64c5cf8SLiam Girdwood } 1850763437a9SArjan van de Ven 185113075510STakashi Iwai for (;;) { 185213075510STakashi Iwai if (signal_pending(current)) { 185313075510STakashi Iwai err = -ERESTARTSYS; 185413075510STakashi Iwai break; 185513075510STakashi Iwai } 1856763437a9SArjan van de Ven 1857763437a9SArjan van de Ven /* 1858763437a9SArjan van de Ven * We need to check if space became available already 1859763437a9SArjan van de Ven * (and thus the wakeup happened already) first to close 1860763437a9SArjan van de Ven * the race of space already having become available. 1861763437a9SArjan van de Ven * This check must happen after been added to the waitqueue 1862763437a9SArjan van de Ven * and having current state be INTERRUPTIBLE. 1863763437a9SArjan van de Ven */ 1864763e5067STakashi Iwai avail = snd_pcm_avail(substream); 1865763437a9SArjan van de Ven if (avail >= runtime->twake) 1866763437a9SArjan van de Ven break; 186713075510STakashi Iwai snd_pcm_stream_unlock_irq(substream); 1868763437a9SArjan van de Ven 1869763437a9SArjan van de Ven tout = schedule_timeout(wait_time); 1870763437a9SArjan van de Ven 187113075510STakashi Iwai snd_pcm_stream_lock_irq(substream); 1872763437a9SArjan van de Ven set_current_state(TASK_INTERRUPTIBLE); 187313075510STakashi Iwai switch (runtime->status->state) { 187413075510STakashi Iwai case SNDRV_PCM_STATE_SUSPENDED: 187513075510STakashi Iwai err = -ESTRPIPE; 187613075510STakashi Iwai goto _endloop; 187713075510STakashi Iwai case SNDRV_PCM_STATE_XRUN: 187813075510STakashi Iwai err = -EPIPE; 187913075510STakashi Iwai goto _endloop; 188013075510STakashi Iwai case SNDRV_PCM_STATE_DRAINING: 188113075510STakashi Iwai if (is_playback) 188213075510STakashi Iwai err = -EPIPE; 188313075510STakashi Iwai else 188413075510STakashi Iwai avail = 0; /* indicate draining */ 188513075510STakashi Iwai goto _endloop; 188613075510STakashi Iwai case SNDRV_PCM_STATE_OPEN: 188713075510STakashi Iwai case SNDRV_PCM_STATE_SETUP: 188813075510STakashi Iwai case SNDRV_PCM_STATE_DISCONNECTED: 188913075510STakashi Iwai err = -EBADFD; 189013075510STakashi Iwai goto _endloop; 1891ed697e1aSJongHo Kim case SNDRV_PCM_STATE_PAUSED: 1892ed697e1aSJongHo Kim continue; 189313075510STakashi Iwai } 189413075510STakashi Iwai if (!tout) { 189509e56df8STakashi Iwai pcm_dbg(substream->pcm, 189609e56df8STakashi Iwai "%s write error (DMA or IRQ trouble?)\n", 189713075510STakashi Iwai is_playback ? "playback" : "capture"); 189813075510STakashi Iwai err = -EIO; 189913075510STakashi Iwai break; 190013075510STakashi Iwai } 190113075510STakashi Iwai } 190213075510STakashi Iwai _endloop: 1903763437a9SArjan van de Ven set_current_state(TASK_RUNNING); 1904c91a988dSJaroslav Kysela remove_wait_queue(&runtime->tsleep, &wait); 190513075510STakashi Iwai *availp = avail; 190613075510STakashi Iwai return err; 190713075510STakashi Iwai } 190813075510STakashi Iwai 19099f600630STakashi Iwai typedef int (*pcm_transfer_f)(struct snd_pcm_substream *substream, 19109f600630STakashi Iwai int channel, unsigned long hwoff, 19119f600630STakashi Iwai void *buf, unsigned long bytes); 1912bdc4acf7STakashi Iwai 19139f600630STakashi Iwai typedef int (*pcm_copy_f)(struct snd_pcm_substream *, snd_pcm_uframes_t, void *, 19149f600630STakashi Iwai snd_pcm_uframes_t, snd_pcm_uframes_t, pcm_transfer_f); 19159f600630STakashi Iwai 19169f600630STakashi Iwai /* calculate the target DMA-buffer position to be written/read */ 19179f600630STakashi Iwai static void *get_dma_ptr(struct snd_pcm_runtime *runtime, 19189f600630STakashi Iwai int channel, unsigned long hwoff) 19191da177e4SLinus Torvalds { 19209f600630STakashi Iwai return runtime->dma_area + hwoff + 19219f600630STakashi Iwai channel * (runtime->dma_bytes / runtime->channels); 19221da177e4SLinus Torvalds } 19239f600630STakashi Iwai 19245c7264cfSTakashi Iwai /* default copy_user ops for write; used for both interleaved and non- modes */ 19255c7264cfSTakashi Iwai static int default_write_copy(struct snd_pcm_substream *substream, 19269f600630STakashi Iwai int channel, unsigned long hwoff, 19275c7264cfSTakashi Iwai void *buf, unsigned long bytes) 19289f600630STakashi Iwai { 19299f600630STakashi Iwai if (copy_from_user(get_dma_ptr(substream->runtime, channel, hwoff), 19305c7264cfSTakashi Iwai (void __user *)buf, bytes)) 19311da177e4SLinus Torvalds return -EFAULT; 19321da177e4SLinus Torvalds return 0; 19331da177e4SLinus Torvalds } 19341da177e4SLinus Torvalds 193568541213STakashi Iwai /* default copy_kernel ops for write */ 193668541213STakashi Iwai static int default_write_copy_kernel(struct snd_pcm_substream *substream, 193768541213STakashi Iwai int channel, unsigned long hwoff, 193868541213STakashi Iwai void *buf, unsigned long bytes) 193968541213STakashi Iwai { 194068541213STakashi Iwai memcpy(get_dma_ptr(substream->runtime, channel, hwoff), buf, bytes); 194168541213STakashi Iwai return 0; 194268541213STakashi Iwai } 19431da177e4SLinus Torvalds 19449f600630STakashi Iwai /* fill silence instead of copy data; called as a transfer helper 19459f600630STakashi Iwai * from __snd_pcm_lib_write() or directly from noninterleaved_copy() when 19469f600630STakashi Iwai * a NULL buffer is passed 19479f600630STakashi Iwai */ 19489f600630STakashi Iwai static int fill_silence(struct snd_pcm_substream *substream, int channel, 19499f600630STakashi Iwai unsigned long hwoff, void *buf, unsigned long bytes) 19501da177e4SLinus Torvalds { 1951877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 19521da177e4SLinus Torvalds 19539f600630STakashi Iwai if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) 19541da177e4SLinus Torvalds return 0; 19559f600630STakashi Iwai if (substream->ops->fill_silence) 19569f600630STakashi Iwai return substream->ops->fill_silence(substream, channel, 19579f600630STakashi Iwai hwoff, bytes); 19581da177e4SLinus Torvalds 19599f600630STakashi Iwai snd_pcm_format_set_silence(runtime->format, 19609f600630STakashi Iwai get_dma_ptr(runtime, channel, hwoff), 19619f600630STakashi Iwai bytes_to_samples(runtime, bytes)); 19629f600630STakashi Iwai return 0; 19631da177e4SLinus Torvalds } 19641da177e4SLinus Torvalds 19655c7264cfSTakashi Iwai /* default copy_user ops for read; used for both interleaved and non- modes */ 19665c7264cfSTakashi Iwai static int default_read_copy(struct snd_pcm_substream *substream, 19675c7264cfSTakashi Iwai int channel, unsigned long hwoff, 19685c7264cfSTakashi Iwai void *buf, unsigned long bytes) 19695c7264cfSTakashi Iwai { 19705c7264cfSTakashi Iwai if (copy_to_user((void __user *)buf, 19715c7264cfSTakashi Iwai get_dma_ptr(substream->runtime, channel, hwoff), 19725c7264cfSTakashi Iwai bytes)) 19735c7264cfSTakashi Iwai return -EFAULT; 19745c7264cfSTakashi Iwai return 0; 19751da177e4SLinus Torvalds } 19761da177e4SLinus Torvalds 197768541213STakashi Iwai /* default copy_kernel ops for read */ 197868541213STakashi Iwai static int default_read_copy_kernel(struct snd_pcm_substream *substream, 197968541213STakashi Iwai int channel, unsigned long hwoff, 198068541213STakashi Iwai void *buf, unsigned long bytes) 198168541213STakashi Iwai { 198268541213STakashi Iwai memcpy(buf, get_dma_ptr(substream->runtime, channel, hwoff), bytes); 198368541213STakashi Iwai return 0; 198468541213STakashi Iwai } 198568541213STakashi Iwai 19869f600630STakashi Iwai /* call transfer function with the converted pointers and sizes; 19879f600630STakashi Iwai * for interleaved mode, it's one shot for all samples 19889f600630STakashi Iwai */ 19899f600630STakashi Iwai static int interleaved_copy(struct snd_pcm_substream *substream, 19909f600630STakashi Iwai snd_pcm_uframes_t hwoff, void *data, 19919f600630STakashi Iwai snd_pcm_uframes_t off, 19929f600630STakashi Iwai snd_pcm_uframes_t frames, 19939f600630STakashi Iwai pcm_transfer_f transfer) 19949f600630STakashi Iwai { 19959f600630STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 19969f600630STakashi Iwai 19979f600630STakashi Iwai /* convert to bytes */ 19989f600630STakashi Iwai hwoff = frames_to_bytes(runtime, hwoff); 19999f600630STakashi Iwai off = frames_to_bytes(runtime, off); 20009f600630STakashi Iwai frames = frames_to_bytes(runtime, frames); 20019f600630STakashi Iwai return transfer(substream, 0, hwoff, data + off, frames); 20029f600630STakashi Iwai } 20039f600630STakashi Iwai 20049f600630STakashi Iwai /* call transfer function with the converted pointers and sizes for each 20059f600630STakashi Iwai * non-interleaved channel; when buffer is NULL, silencing instead of copying 20069f600630STakashi Iwai */ 20079f600630STakashi Iwai static int noninterleaved_copy(struct snd_pcm_substream *substream, 20089f600630STakashi Iwai snd_pcm_uframes_t hwoff, void *data, 20099f600630STakashi Iwai snd_pcm_uframes_t off, 20109f600630STakashi Iwai snd_pcm_uframes_t frames, 20119f600630STakashi Iwai pcm_transfer_f transfer) 20129f600630STakashi Iwai { 20139f600630STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 20149f600630STakashi Iwai int channels = runtime->channels; 20159f600630STakashi Iwai void **bufs = data; 20169f600630STakashi Iwai int c, err; 20179f600630STakashi Iwai 20189f600630STakashi Iwai /* convert to bytes; note that it's not frames_to_bytes() here. 20199f600630STakashi Iwai * in non-interleaved mode, we copy for each channel, thus 20209f600630STakashi Iwai * each copy is n_samples bytes x channels = whole frames. 20219f600630STakashi Iwai */ 2022bdc4acf7STakashi Iwai off = samples_to_bytes(runtime, off); 2023bdc4acf7STakashi Iwai frames = samples_to_bytes(runtime, frames); 20249f600630STakashi Iwai hwoff = samples_to_bytes(runtime, hwoff); 2025bdc4acf7STakashi Iwai for (c = 0; c < channels; ++c, ++bufs) { 20269f600630STakashi Iwai if (!data || !*bufs) 20279f600630STakashi Iwai err = fill_silence(substream, c, hwoff, NULL, frames); 20289f600630STakashi Iwai else 20299f600630STakashi Iwai err = transfer(substream, c, hwoff, *bufs + off, 2030bdc4acf7STakashi Iwai frames); 20311da177e4SLinus Torvalds if (err < 0) 2032bdc4acf7STakashi Iwai return err; 20331da177e4SLinus Torvalds } 2034bdc4acf7STakashi Iwai return 0; 20351da177e4SLinus Torvalds } 2036bdc4acf7STakashi Iwai 2037a9cd29e7STakashi Iwai /* fill silence on the given buffer position; 2038a9cd29e7STakashi Iwai * called from snd_pcm_playback_silence() 2039a9cd29e7STakashi Iwai */ 2040a9cd29e7STakashi Iwai static int fill_silence_frames(struct snd_pcm_substream *substream, 2041a9cd29e7STakashi Iwai snd_pcm_uframes_t off, snd_pcm_uframes_t frames) 2042a9cd29e7STakashi Iwai { 2043a9cd29e7STakashi Iwai if (substream->runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || 2044a9cd29e7STakashi Iwai substream->runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) 2045a9cd29e7STakashi Iwai return interleaved_copy(substream, off, NULL, 0, frames, 2046a9cd29e7STakashi Iwai fill_silence); 2047a9cd29e7STakashi Iwai else 2048a9cd29e7STakashi Iwai return noninterleaved_copy(substream, off, NULL, 0, frames, 2049a9cd29e7STakashi Iwai fill_silence); 20501da177e4SLinus Torvalds } 20511da177e4SLinus Torvalds 20527eaa943cSTakashi Iwai /* sanity-check for read/write methods */ 20537eaa943cSTakashi Iwai static int pcm_sanity_check(struct snd_pcm_substream *substream) 20547eaa943cSTakashi Iwai { 20557eaa943cSTakashi Iwai struct snd_pcm_runtime *runtime; 20567eaa943cSTakashi Iwai if (PCM_RUNTIME_CHECK(substream)) 20577eaa943cSTakashi Iwai return -ENXIO; 20587eaa943cSTakashi Iwai runtime = substream->runtime; 2059bdc4acf7STakashi Iwai if (snd_BUG_ON(!substream->ops->copy_user && !runtime->dma_area)) 20607eaa943cSTakashi Iwai return -EINVAL; 20617eaa943cSTakashi Iwai if (runtime->status->state == SNDRV_PCM_STATE_OPEN) 20627eaa943cSTakashi Iwai return -EBADFD; 20637eaa943cSTakashi Iwai return 0; 20647eaa943cSTakashi Iwai } 20657eaa943cSTakashi Iwai 20666ba63929STakashi Iwai static int pcm_accessible_state(struct snd_pcm_runtime *runtime) 20671da177e4SLinus Torvalds { 20686ba63929STakashi Iwai switch (runtime->status->state) { 20696ba63929STakashi Iwai case SNDRV_PCM_STATE_PREPARED: 20706ba63929STakashi Iwai case SNDRV_PCM_STATE_RUNNING: 20716ba63929STakashi Iwai case SNDRV_PCM_STATE_PAUSED: 20726ba63929STakashi Iwai return 0; 20736ba63929STakashi Iwai case SNDRV_PCM_STATE_XRUN: 20746ba63929STakashi Iwai return -EPIPE; 20756ba63929STakashi Iwai case SNDRV_PCM_STATE_SUSPENDED: 20766ba63929STakashi Iwai return -ESTRPIPE; 20776ba63929STakashi Iwai default: 20786ba63929STakashi Iwai return -EBADFD; 20796ba63929STakashi Iwai } 20801da177e4SLinus Torvalds } 20811da177e4SLinus Torvalds 208266e01a5cSTakashi Sakamoto /* update to the given appl_ptr and call ack callback if needed; 208366e01a5cSTakashi Sakamoto * when an error is returned, take back to the original value 208466e01a5cSTakashi Sakamoto */ 208566e01a5cSTakashi Sakamoto int pcm_lib_apply_appl_ptr(struct snd_pcm_substream *substream, 208666e01a5cSTakashi Sakamoto snd_pcm_uframes_t appl_ptr) 20871da177e4SLinus Torvalds { 2088877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 208966e01a5cSTakashi Sakamoto snd_pcm_uframes_t old_appl_ptr = runtime->control->appl_ptr; 209066e01a5cSTakashi Sakamoto int ret; 209166e01a5cSTakashi Sakamoto 2092f8ff2f28STakashi Iwai if (old_appl_ptr == appl_ptr) 2093f8ff2f28STakashi Iwai return 0; 2094f8ff2f28STakashi Iwai 209566e01a5cSTakashi Sakamoto runtime->control->appl_ptr = appl_ptr; 209666e01a5cSTakashi Sakamoto if (substream->ops->ack) { 209766e01a5cSTakashi Sakamoto ret = substream->ops->ack(substream); 209866e01a5cSTakashi Sakamoto if (ret < 0) { 209966e01a5cSTakashi Sakamoto runtime->control->appl_ptr = old_appl_ptr; 210066e01a5cSTakashi Sakamoto return ret; 21011da177e4SLinus Torvalds } 21021da177e4SLinus Torvalds } 2103fccf5388STakashi Sakamoto 2104fccf5388STakashi Sakamoto trace_applptr(substream, old_appl_ptr, appl_ptr); 2105fccf5388STakashi Sakamoto 21061da177e4SLinus Torvalds return 0; 21071da177e4SLinus Torvalds } 21081da177e4SLinus Torvalds 21095c7264cfSTakashi Iwai /* the common loop for read/write data */ 21105c7264cfSTakashi Iwai snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream, 2111c48f12eeSTakashi Iwai void *data, bool interleaved, 211268541213STakashi Iwai snd_pcm_uframes_t size, bool in_kernel) 21131da177e4SLinus Torvalds { 2114877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 21151da177e4SLinus Torvalds snd_pcm_uframes_t xfer = 0; 21161da177e4SLinus Torvalds snd_pcm_uframes_t offset = 0; 21170910c216STakashi Iwai snd_pcm_uframes_t avail; 21189f600630STakashi Iwai pcm_copy_f writer; 21199f600630STakashi Iwai pcm_transfer_f transfer; 2120c48f12eeSTakashi Iwai bool nonblock; 21215c7264cfSTakashi Iwai bool is_playback; 2122c48f12eeSTakashi Iwai int err; 2123c48f12eeSTakashi Iwai 2124c48f12eeSTakashi Iwai err = pcm_sanity_check(substream); 2125c48f12eeSTakashi Iwai if (err < 0) 2126c48f12eeSTakashi Iwai return err; 2127c48f12eeSTakashi Iwai 21285c7264cfSTakashi Iwai is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 2129c48f12eeSTakashi Iwai if (interleaved) { 2130c48f12eeSTakashi Iwai if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && 2131c48f12eeSTakashi Iwai runtime->channels > 1) 2132c48f12eeSTakashi Iwai return -EINVAL; 21339f600630STakashi Iwai writer = interleaved_copy; 2134c48f12eeSTakashi Iwai } else { 2135c48f12eeSTakashi Iwai if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) 2136c48f12eeSTakashi Iwai return -EINVAL; 21379f600630STakashi Iwai writer = noninterleaved_copy; 21389f600630STakashi Iwai } 21399f600630STakashi Iwai 21409f600630STakashi Iwai if (!data) { 21415c7264cfSTakashi Iwai if (is_playback) 21429f600630STakashi Iwai transfer = fill_silence; 21435c7264cfSTakashi Iwai else 21445c7264cfSTakashi Iwai return -EINVAL; 214568541213STakashi Iwai } else if (in_kernel) { 214668541213STakashi Iwai if (substream->ops->copy_kernel) 214768541213STakashi Iwai transfer = substream->ops->copy_kernel; 214868541213STakashi Iwai else 214968541213STakashi Iwai transfer = is_playback ? 215068541213STakashi Iwai default_write_copy_kernel : default_read_copy_kernel; 21519f600630STakashi Iwai } else { 21529f600630STakashi Iwai if (substream->ops->copy_user) 21539f600630STakashi Iwai transfer = (pcm_transfer_f)substream->ops->copy_user; 21549f600630STakashi Iwai else 21555c7264cfSTakashi Iwai transfer = is_playback ? 21565c7264cfSTakashi Iwai default_write_copy : default_read_copy; 2157c48f12eeSTakashi Iwai } 21581da177e4SLinus Torvalds 21591da177e4SLinus Torvalds if (size == 0) 21601da177e4SLinus Torvalds return 0; 21611da177e4SLinus Torvalds 2162c48f12eeSTakashi Iwai nonblock = !!(substream->f_flags & O_NONBLOCK); 2163c48f12eeSTakashi Iwai 21641da177e4SLinus Torvalds snd_pcm_stream_lock_irq(substream); 21656ba63929STakashi Iwai err = pcm_accessible_state(runtime); 21661da177e4SLinus Torvalds if (err < 0) 21671da177e4SLinus Torvalds goto _end_unlock; 21681da177e4SLinus Torvalds 21695c7264cfSTakashi Iwai if (!is_playback && 21705c7264cfSTakashi Iwai runtime->status->state == SNDRV_PCM_STATE_PREPARED && 21715c7264cfSTakashi Iwai size >= runtime->start_threshold) { 21725c7264cfSTakashi Iwai err = snd_pcm_start(substream); 21735c7264cfSTakashi Iwai if (err < 0) 21741da177e4SLinus Torvalds goto _end_unlock; 21751da177e4SLinus Torvalds } 21761da177e4SLinus Torvalds 21775daeba34SDavid Dillow runtime->twake = runtime->control->avail_min ? : 1; 217831e8960bSTakashi Iwai if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) 21791da177e4SLinus Torvalds snd_pcm_update_hw_ptr(substream); 2180763e5067STakashi Iwai avail = snd_pcm_avail(substream); 21810910c216STakashi Iwai while (size > 0) { 21820910c216STakashi Iwai snd_pcm_uframes_t frames, appl_ptr, appl_ofs; 21830910c216STakashi Iwai snd_pcm_uframes_t cont; 2184d948035aSTakashi Iwai if (!avail) { 21855c7264cfSTakashi Iwai if (!is_playback && 21865c7264cfSTakashi Iwai runtime->status->state == SNDRV_PCM_STATE_DRAINING) { 218713075510STakashi Iwai snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); 21881da177e4SLinus Torvalds goto _end_unlock; 21891da177e4SLinus Torvalds } 21901da177e4SLinus Torvalds if (nonblock) { 21911da177e4SLinus Torvalds err = -EAGAIN; 21921da177e4SLinus Torvalds goto _end_unlock; 21931da177e4SLinus Torvalds } 21945daeba34SDavid Dillow runtime->twake = min_t(snd_pcm_uframes_t, size, 21955daeba34SDavid Dillow runtime->control->avail_min ? : 1); 21965daeba34SDavid Dillow err = wait_for_avail(substream, &avail); 219713075510STakashi Iwai if (err < 0) 21981da177e4SLinus Torvalds goto _end_unlock; 219913075510STakashi Iwai if (!avail) 220013075510STakashi Iwai continue; /* draining */ 22011da177e4SLinus Torvalds } 22021da177e4SLinus Torvalds frames = size > avail ? avail : size; 2203aa30db06STakashi Iwai appl_ptr = READ_ONCE(runtime->control->appl_ptr); 2204aa30db06STakashi Iwai appl_ofs = appl_ptr % runtime->buffer_size; 2205aa30db06STakashi Iwai cont = runtime->buffer_size - appl_ofs; 22061da177e4SLinus Torvalds if (frames > cont) 22071da177e4SLinus Torvalds frames = cont; 22087eaa943cSTakashi Iwai if (snd_BUG_ON(!frames)) { 2209c91a988dSJaroslav Kysela runtime->twake = 0; 22107eaa943cSTakashi Iwai snd_pcm_stream_unlock_irq(substream); 22117eaa943cSTakashi Iwai return -EINVAL; 22127eaa943cSTakashi Iwai } 22131da177e4SLinus Torvalds snd_pcm_stream_unlock_irq(substream); 22149f600630STakashi Iwai err = writer(substream, appl_ofs, data, offset, frames, 22159f600630STakashi Iwai transfer); 22161da177e4SLinus Torvalds snd_pcm_stream_lock_irq(substream); 22171250932eSJaroslav Kysela if (err < 0) 22181250932eSJaroslav Kysela goto _end_unlock; 22196ba63929STakashi Iwai err = pcm_accessible_state(runtime); 22206ba63929STakashi Iwai if (err < 0) 22211da177e4SLinus Torvalds goto _end_unlock; 22221da177e4SLinus Torvalds appl_ptr += frames; 22231da177e4SLinus Torvalds if (appl_ptr >= runtime->boundary) 22241da177e4SLinus Torvalds appl_ptr -= runtime->boundary; 222566e01a5cSTakashi Sakamoto err = pcm_lib_apply_appl_ptr(substream, appl_ptr); 222666e01a5cSTakashi Sakamoto if (err < 0) 222766e01a5cSTakashi Sakamoto goto _end_unlock; 22281da177e4SLinus Torvalds 22291da177e4SLinus Torvalds offset += frames; 22301da177e4SLinus Torvalds size -= frames; 22311da177e4SLinus Torvalds xfer += frames; 22320910c216STakashi Iwai avail -= frames; 22335c7264cfSTakashi Iwai if (is_playback && 22345c7264cfSTakashi Iwai runtime->status->state == SNDRV_PCM_STATE_PREPARED && 22351da177e4SLinus Torvalds snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) { 22361da177e4SLinus Torvalds err = snd_pcm_start(substream); 22371da177e4SLinus Torvalds if (err < 0) 22381da177e4SLinus Torvalds goto _end_unlock; 22391da177e4SLinus Torvalds } 22401da177e4SLinus Torvalds } 22411da177e4SLinus Torvalds _end_unlock: 2242c91a988dSJaroslav Kysela runtime->twake = 0; 22431250932eSJaroslav Kysela if (xfer > 0 && err >= 0) 22441250932eSJaroslav Kysela snd_pcm_update_state(substream, runtime); 22451da177e4SLinus Torvalds snd_pcm_stream_unlock_irq(substream); 22461da177e4SLinus Torvalds return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; 22471da177e4SLinus Torvalds } 22485c7264cfSTakashi Iwai EXPORT_SYMBOL(__snd_pcm_lib_xfer); 22492d3391ecSTakashi Iwai 22502d3391ecSTakashi Iwai /* 22512d3391ecSTakashi Iwai * standard channel mapping helpers 22522d3391ecSTakashi Iwai */ 22532d3391ecSTakashi Iwai 22542d3391ecSTakashi Iwai /* default channel maps for multi-channel playbacks, up to 8 channels */ 22552d3391ecSTakashi Iwai const struct snd_pcm_chmap_elem snd_pcm_std_chmaps[] = { 22562d3391ecSTakashi Iwai { .channels = 1, 22575efbc261STakashi Iwai .map = { SNDRV_CHMAP_MONO } }, 22582d3391ecSTakashi Iwai { .channels = 2, 22592d3391ecSTakashi Iwai .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, 22602d3391ecSTakashi Iwai { .channels = 4, 22612d3391ecSTakashi Iwai .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, 22622d3391ecSTakashi Iwai SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, 22632d3391ecSTakashi Iwai { .channels = 6, 22642d3391ecSTakashi Iwai .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, 22652d3391ecSTakashi Iwai SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, 22662d3391ecSTakashi Iwai SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } }, 22672d3391ecSTakashi Iwai { .channels = 8, 22682d3391ecSTakashi Iwai .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, 22692d3391ecSTakashi Iwai SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, 22702d3391ecSTakashi Iwai SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE, 22712d3391ecSTakashi Iwai SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } }, 22722d3391ecSTakashi Iwai { } 22732d3391ecSTakashi Iwai }; 22742d3391ecSTakashi Iwai EXPORT_SYMBOL_GPL(snd_pcm_std_chmaps); 22752d3391ecSTakashi Iwai 22762d3391ecSTakashi Iwai /* alternative channel maps with CLFE <-> surround swapped for 6/8 channels */ 22772d3391ecSTakashi Iwai const struct snd_pcm_chmap_elem snd_pcm_alt_chmaps[] = { 22782d3391ecSTakashi Iwai { .channels = 1, 22795efbc261STakashi Iwai .map = { SNDRV_CHMAP_MONO } }, 22802d3391ecSTakashi Iwai { .channels = 2, 22812d3391ecSTakashi Iwai .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, 22822d3391ecSTakashi Iwai { .channels = 4, 22832d3391ecSTakashi Iwai .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, 22842d3391ecSTakashi Iwai SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, 22852d3391ecSTakashi Iwai { .channels = 6, 22862d3391ecSTakashi Iwai .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, 22872d3391ecSTakashi Iwai SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE, 22882d3391ecSTakashi Iwai SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, 22892d3391ecSTakashi Iwai { .channels = 8, 22902d3391ecSTakashi Iwai .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, 22912d3391ecSTakashi Iwai SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE, 22922d3391ecSTakashi Iwai SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, 22932d3391ecSTakashi Iwai SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } }, 22942d3391ecSTakashi Iwai { } 22952d3391ecSTakashi Iwai }; 22962d3391ecSTakashi Iwai EXPORT_SYMBOL_GPL(snd_pcm_alt_chmaps); 22972d3391ecSTakashi Iwai 22982d3391ecSTakashi Iwai static bool valid_chmap_channels(const struct snd_pcm_chmap *info, int ch) 22992d3391ecSTakashi Iwai { 23002d3391ecSTakashi Iwai if (ch > info->max_channels) 23012d3391ecSTakashi Iwai return false; 23022d3391ecSTakashi Iwai return !info->channel_mask || (info->channel_mask & (1U << ch)); 23032d3391ecSTakashi Iwai } 23042d3391ecSTakashi Iwai 23052d3391ecSTakashi Iwai static int pcm_chmap_ctl_info(struct snd_kcontrol *kcontrol, 23062d3391ecSTakashi Iwai struct snd_ctl_elem_info *uinfo) 23072d3391ecSTakashi Iwai { 23082d3391ecSTakashi Iwai struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); 23092d3391ecSTakashi Iwai 23102d3391ecSTakashi Iwai uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 23112d3391ecSTakashi Iwai uinfo->count = 0; 23122d3391ecSTakashi Iwai uinfo->count = info->max_channels; 23132d3391ecSTakashi Iwai uinfo->value.integer.min = 0; 23142d3391ecSTakashi Iwai uinfo->value.integer.max = SNDRV_CHMAP_LAST; 23152d3391ecSTakashi Iwai return 0; 23162d3391ecSTakashi Iwai } 23172d3391ecSTakashi Iwai 23182d3391ecSTakashi Iwai /* get callback for channel map ctl element 23192d3391ecSTakashi Iwai * stores the channel position firstly matching with the current channels 23202d3391ecSTakashi Iwai */ 23212d3391ecSTakashi Iwai static int pcm_chmap_ctl_get(struct snd_kcontrol *kcontrol, 23222d3391ecSTakashi Iwai struct snd_ctl_elem_value *ucontrol) 23232d3391ecSTakashi Iwai { 23242d3391ecSTakashi Iwai struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); 23252d3391ecSTakashi Iwai unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 23262d3391ecSTakashi Iwai struct snd_pcm_substream *substream; 23272d3391ecSTakashi Iwai const struct snd_pcm_chmap_elem *map; 23282d3391ecSTakashi Iwai 23292deaeaf1STakashi Iwai if (!info->chmap) 23302d3391ecSTakashi Iwai return -EINVAL; 23312d3391ecSTakashi Iwai substream = snd_pcm_chmap_substream(info, idx); 23322d3391ecSTakashi Iwai if (!substream) 23332d3391ecSTakashi Iwai return -ENODEV; 23342d3391ecSTakashi Iwai memset(ucontrol->value.integer.value, 0, 23352d3391ecSTakashi Iwai sizeof(ucontrol->value.integer.value)); 23362d3391ecSTakashi Iwai if (!substream->runtime) 23372d3391ecSTakashi Iwai return 0; /* no channels set */ 23382d3391ecSTakashi Iwai for (map = info->chmap; map->channels; map++) { 23392d3391ecSTakashi Iwai int i; 23402d3391ecSTakashi Iwai if (map->channels == substream->runtime->channels && 23412d3391ecSTakashi Iwai valid_chmap_channels(info, map->channels)) { 23422d3391ecSTakashi Iwai for (i = 0; i < map->channels; i++) 23432d3391ecSTakashi Iwai ucontrol->value.integer.value[i] = map->map[i]; 23442d3391ecSTakashi Iwai return 0; 23452d3391ecSTakashi Iwai } 23462d3391ecSTakashi Iwai } 23472d3391ecSTakashi Iwai return -EINVAL; 23482d3391ecSTakashi Iwai } 23492d3391ecSTakashi Iwai 23502d3391ecSTakashi Iwai /* tlv callback for channel map ctl element 23512d3391ecSTakashi Iwai * expands the pre-defined channel maps in a form of TLV 23522d3391ecSTakashi Iwai */ 23532d3391ecSTakashi Iwai static int pcm_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, 23542d3391ecSTakashi Iwai unsigned int size, unsigned int __user *tlv) 23552d3391ecSTakashi Iwai { 23562d3391ecSTakashi Iwai struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); 23572d3391ecSTakashi Iwai const struct snd_pcm_chmap_elem *map; 23582d3391ecSTakashi Iwai unsigned int __user *dst; 23592d3391ecSTakashi Iwai int c, count = 0; 23602d3391ecSTakashi Iwai 23612deaeaf1STakashi Iwai if (!info->chmap) 23622d3391ecSTakashi Iwai return -EINVAL; 23632d3391ecSTakashi Iwai if (size < 8) 23642d3391ecSTakashi Iwai return -ENOMEM; 23652d3391ecSTakashi Iwai if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv)) 23662d3391ecSTakashi Iwai return -EFAULT; 23672d3391ecSTakashi Iwai size -= 8; 23682d3391ecSTakashi Iwai dst = tlv + 2; 23692d3391ecSTakashi Iwai for (map = info->chmap; map->channels; map++) { 23702d3391ecSTakashi Iwai int chs_bytes = map->channels * 4; 23712d3391ecSTakashi Iwai if (!valid_chmap_channels(info, map->channels)) 23722d3391ecSTakashi Iwai continue; 23732d3391ecSTakashi Iwai if (size < 8) 23742d3391ecSTakashi Iwai return -ENOMEM; 23752d3391ecSTakashi Iwai if (put_user(SNDRV_CTL_TLVT_CHMAP_FIXED, dst) || 23762d3391ecSTakashi Iwai put_user(chs_bytes, dst + 1)) 23772d3391ecSTakashi Iwai return -EFAULT; 23782d3391ecSTakashi Iwai dst += 2; 23792d3391ecSTakashi Iwai size -= 8; 23802d3391ecSTakashi Iwai count += 8; 23812d3391ecSTakashi Iwai if (size < chs_bytes) 23822d3391ecSTakashi Iwai return -ENOMEM; 23832d3391ecSTakashi Iwai size -= chs_bytes; 23842d3391ecSTakashi Iwai count += chs_bytes; 23852d3391ecSTakashi Iwai for (c = 0; c < map->channels; c++) { 23862d3391ecSTakashi Iwai if (put_user(map->map[c], dst)) 23872d3391ecSTakashi Iwai return -EFAULT; 23882d3391ecSTakashi Iwai dst++; 23892d3391ecSTakashi Iwai } 23902d3391ecSTakashi Iwai } 23912d3391ecSTakashi Iwai if (put_user(count, tlv + 1)) 23922d3391ecSTakashi Iwai return -EFAULT; 23932d3391ecSTakashi Iwai return 0; 23942d3391ecSTakashi Iwai } 23952d3391ecSTakashi Iwai 23962d3391ecSTakashi Iwai static void pcm_chmap_ctl_private_free(struct snd_kcontrol *kcontrol) 23972d3391ecSTakashi Iwai { 23982d3391ecSTakashi Iwai struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); 23992d3391ecSTakashi Iwai info->pcm->streams[info->stream].chmap_kctl = NULL; 24002d3391ecSTakashi Iwai kfree(info); 24012d3391ecSTakashi Iwai } 24022d3391ecSTakashi Iwai 24032d3391ecSTakashi Iwai /** 24042d3391ecSTakashi Iwai * snd_pcm_add_chmap_ctls - create channel-mapping control elements 24052d3391ecSTakashi Iwai * @pcm: the assigned PCM instance 24062d3391ecSTakashi Iwai * @stream: stream direction 24072d3391ecSTakashi Iwai * @chmap: channel map elements (for query) 24082d3391ecSTakashi Iwai * @max_channels: the max number of channels for the stream 24092d3391ecSTakashi Iwai * @private_value: the value passed to each kcontrol's private_value field 24102d3391ecSTakashi Iwai * @info_ret: store struct snd_pcm_chmap instance if non-NULL 24112d3391ecSTakashi Iwai * 24122d3391ecSTakashi Iwai * Create channel-mapping control elements assigned to the given PCM stream(s). 2413eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error value. 24142d3391ecSTakashi Iwai */ 24152d3391ecSTakashi Iwai int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream, 24162d3391ecSTakashi Iwai const struct snd_pcm_chmap_elem *chmap, 24172d3391ecSTakashi Iwai int max_channels, 24182d3391ecSTakashi Iwai unsigned long private_value, 24192d3391ecSTakashi Iwai struct snd_pcm_chmap **info_ret) 24202d3391ecSTakashi Iwai { 24212d3391ecSTakashi Iwai struct snd_pcm_chmap *info; 24222d3391ecSTakashi Iwai struct snd_kcontrol_new knew = { 24232d3391ecSTakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_PCM, 24242d3391ecSTakashi Iwai .access = SNDRV_CTL_ELEM_ACCESS_READ | 24252d3391ecSTakashi Iwai SNDRV_CTL_ELEM_ACCESS_TLV_READ | 24262d3391ecSTakashi Iwai SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, 24272d3391ecSTakashi Iwai .info = pcm_chmap_ctl_info, 24282d3391ecSTakashi Iwai .get = pcm_chmap_ctl_get, 24292d3391ecSTakashi Iwai .tlv.c = pcm_chmap_ctl_tlv, 24302d3391ecSTakashi Iwai }; 24312d3391ecSTakashi Iwai int err; 24322d3391ecSTakashi Iwai 24338d879be8STakashi Iwai if (WARN_ON(pcm->streams[stream].chmap_kctl)) 24348d879be8STakashi Iwai return -EBUSY; 24352d3391ecSTakashi Iwai info = kzalloc(sizeof(*info), GFP_KERNEL); 24362d3391ecSTakashi Iwai if (!info) 24372d3391ecSTakashi Iwai return -ENOMEM; 24382d3391ecSTakashi Iwai info->pcm = pcm; 24392d3391ecSTakashi Iwai info->stream = stream; 24402d3391ecSTakashi Iwai info->chmap = chmap; 24412d3391ecSTakashi Iwai info->max_channels = max_channels; 24422d3391ecSTakashi Iwai if (stream == SNDRV_PCM_STREAM_PLAYBACK) 24432d3391ecSTakashi Iwai knew.name = "Playback Channel Map"; 24442d3391ecSTakashi Iwai else 24452d3391ecSTakashi Iwai knew.name = "Capture Channel Map"; 24462d3391ecSTakashi Iwai knew.device = pcm->device; 24472d3391ecSTakashi Iwai knew.count = pcm->streams[stream].substream_count; 24482d3391ecSTakashi Iwai knew.private_value = private_value; 24492d3391ecSTakashi Iwai info->kctl = snd_ctl_new1(&knew, info); 24502d3391ecSTakashi Iwai if (!info->kctl) { 24512d3391ecSTakashi Iwai kfree(info); 24522d3391ecSTakashi Iwai return -ENOMEM; 24532d3391ecSTakashi Iwai } 24542d3391ecSTakashi Iwai info->kctl->private_free = pcm_chmap_ctl_private_free; 24552d3391ecSTakashi Iwai err = snd_ctl_add(pcm->card, info->kctl); 24562d3391ecSTakashi Iwai if (err < 0) 24572d3391ecSTakashi Iwai return err; 24582d3391ecSTakashi Iwai pcm->streams[stream].chmap_kctl = info->kctl; 24592d3391ecSTakashi Iwai if (info_ret) 24602d3391ecSTakashi Iwai *info_ret = info; 24612d3391ecSTakashi Iwai return 0; 24622d3391ecSTakashi Iwai } 24632d3391ecSTakashi Iwai EXPORT_SYMBOL_GPL(snd_pcm_add_chmap_ctls); 2464