11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Digital Audio (PCM) abstract layer 31da177e4SLinus Torvalds * Copyright (c) by Jaroslav Kysela <perex@suse.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 <sound/driver.h> 241da177e4SLinus Torvalds #include <linux/slab.h> 251da177e4SLinus Torvalds #include <linux/time.h> 261da177e4SLinus Torvalds #include <sound/core.h> 271da177e4SLinus Torvalds #include <sound/control.h> 281da177e4SLinus Torvalds #include <sound/info.h> 291da177e4SLinus Torvalds #include <sound/pcm.h> 301da177e4SLinus Torvalds #include <sound/pcm_params.h> 311da177e4SLinus Torvalds #include <sound/timer.h> 321da177e4SLinus Torvalds 331da177e4SLinus Torvalds /* 341da177e4SLinus Torvalds * fill ring buffer with silence 351da177e4SLinus Torvalds * runtime->silence_start: starting pointer to silence area 361da177e4SLinus Torvalds * runtime->silence_filled: size filled with silence 371da177e4SLinus Torvalds * runtime->silence_threshold: threshold from application 381da177e4SLinus Torvalds * runtime->silence_size: maximal size from application 391da177e4SLinus Torvalds * 401da177e4SLinus Torvalds * when runtime->silence_size >= runtime->boundary - fill processed area with silence immediately 411da177e4SLinus Torvalds */ 421da177e4SLinus Torvalds void snd_pcm_playback_silence(snd_pcm_substream_t *substream, snd_pcm_uframes_t new_hw_ptr) 431da177e4SLinus Torvalds { 441da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 451da177e4SLinus Torvalds snd_pcm_uframes_t frames, ofs, transfer; 461da177e4SLinus Torvalds 471da177e4SLinus Torvalds if (runtime->silence_size < runtime->boundary) { 481da177e4SLinus Torvalds snd_pcm_sframes_t noise_dist, n; 491da177e4SLinus Torvalds if (runtime->silence_start != runtime->control->appl_ptr) { 501da177e4SLinus Torvalds n = runtime->control->appl_ptr - runtime->silence_start; 511da177e4SLinus Torvalds if (n < 0) 521da177e4SLinus Torvalds n += runtime->boundary; 531da177e4SLinus Torvalds if ((snd_pcm_uframes_t)n < runtime->silence_filled) 541da177e4SLinus Torvalds runtime->silence_filled -= n; 551da177e4SLinus Torvalds else 561da177e4SLinus Torvalds runtime->silence_filled = 0; 571da177e4SLinus Torvalds runtime->silence_start = runtime->control->appl_ptr; 581da177e4SLinus Torvalds } 591da177e4SLinus Torvalds if (runtime->silence_filled == runtime->buffer_size) 601da177e4SLinus Torvalds return; 611da177e4SLinus Torvalds snd_assert(runtime->silence_filled <= runtime->buffer_size, return); 621da177e4SLinus Torvalds noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silence_filled; 631da177e4SLinus Torvalds if (noise_dist >= (snd_pcm_sframes_t) runtime->silence_threshold) 641da177e4SLinus Torvalds return; 651da177e4SLinus Torvalds frames = runtime->silence_threshold - noise_dist; 661da177e4SLinus Torvalds if (frames > runtime->silence_size) 671da177e4SLinus Torvalds frames = runtime->silence_size; 681da177e4SLinus Torvalds } else { 691da177e4SLinus Torvalds if (new_hw_ptr == ULONG_MAX) { /* initialization */ 701da177e4SLinus Torvalds snd_pcm_sframes_t avail = snd_pcm_playback_hw_avail(runtime); 711da177e4SLinus Torvalds runtime->silence_filled = avail > 0 ? avail : 0; 721da177e4SLinus Torvalds runtime->silence_start = (runtime->status->hw_ptr + 731da177e4SLinus Torvalds runtime->silence_filled) % 741da177e4SLinus Torvalds runtime->boundary; 751da177e4SLinus Torvalds } else { 761da177e4SLinus Torvalds ofs = runtime->status->hw_ptr; 771da177e4SLinus Torvalds frames = new_hw_ptr - ofs; 781da177e4SLinus Torvalds if ((snd_pcm_sframes_t)frames < 0) 791da177e4SLinus Torvalds frames += runtime->boundary; 801da177e4SLinus Torvalds runtime->silence_filled -= frames; 811da177e4SLinus Torvalds if ((snd_pcm_sframes_t)runtime->silence_filled < 0) { 821da177e4SLinus Torvalds runtime->silence_filled = 0; 831da177e4SLinus Torvalds runtime->silence_start = (ofs + frames) - runtime->buffer_size; 841da177e4SLinus Torvalds } else { 851da177e4SLinus Torvalds runtime->silence_start = ofs - runtime->silence_filled; 861da177e4SLinus Torvalds } 871da177e4SLinus Torvalds if ((snd_pcm_sframes_t)runtime->silence_start < 0) 881da177e4SLinus Torvalds runtime->silence_start += runtime->boundary; 891da177e4SLinus Torvalds } 901da177e4SLinus Torvalds frames = runtime->buffer_size - runtime->silence_filled; 911da177e4SLinus Torvalds } 921da177e4SLinus Torvalds snd_assert(frames <= runtime->buffer_size, return); 931da177e4SLinus Torvalds if (frames == 0) 941da177e4SLinus Torvalds return; 951da177e4SLinus Torvalds ofs = (runtime->silence_start + runtime->silence_filled) % runtime->buffer_size; 961da177e4SLinus Torvalds while (frames > 0) { 971da177e4SLinus Torvalds transfer = ofs + frames > runtime->buffer_size ? runtime->buffer_size - ofs : frames; 981da177e4SLinus Torvalds if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || 991da177e4SLinus Torvalds runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) { 1001da177e4SLinus Torvalds if (substream->ops->silence) { 1011da177e4SLinus Torvalds int err; 1021da177e4SLinus Torvalds err = substream->ops->silence(substream, -1, ofs, transfer); 1031da177e4SLinus Torvalds snd_assert(err >= 0, ); 1041da177e4SLinus Torvalds } else { 1051da177e4SLinus Torvalds char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, ofs); 1061da177e4SLinus Torvalds snd_pcm_format_set_silence(runtime->format, hwbuf, transfer * runtime->channels); 1071da177e4SLinus Torvalds } 1081da177e4SLinus Torvalds } else { 1091da177e4SLinus Torvalds unsigned int c; 1101da177e4SLinus Torvalds unsigned int channels = runtime->channels; 1111da177e4SLinus Torvalds if (substream->ops->silence) { 1121da177e4SLinus Torvalds for (c = 0; c < channels; ++c) { 1131da177e4SLinus Torvalds int err; 1141da177e4SLinus Torvalds err = substream->ops->silence(substream, c, ofs, transfer); 1151da177e4SLinus Torvalds snd_assert(err >= 0, ); 1161da177e4SLinus Torvalds } 1171da177e4SLinus Torvalds } else { 1181da177e4SLinus Torvalds size_t dma_csize = runtime->dma_bytes / channels; 1191da177e4SLinus Torvalds for (c = 0; c < channels; ++c) { 1201da177e4SLinus Torvalds char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, ofs); 1211da177e4SLinus Torvalds snd_pcm_format_set_silence(runtime->format, hwbuf, transfer); 1221da177e4SLinus Torvalds } 1231da177e4SLinus Torvalds } 1241da177e4SLinus Torvalds } 1251da177e4SLinus Torvalds runtime->silence_filled += transfer; 1261da177e4SLinus Torvalds frames -= transfer; 1271da177e4SLinus Torvalds ofs = 0; 1281da177e4SLinus Torvalds } 1291da177e4SLinus Torvalds } 1301da177e4SLinus Torvalds 1311da177e4SLinus Torvalds static void xrun(snd_pcm_substream_t *substream) 1321da177e4SLinus Torvalds { 1331da177e4SLinus Torvalds snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); 1341da177e4SLinus Torvalds #ifdef CONFIG_SND_DEBUG 1351da177e4SLinus Torvalds if (substream->pstr->xrun_debug) { 1361da177e4SLinus Torvalds snd_printd(KERN_DEBUG "XRUN: pcmC%dD%d%c\n", 1371da177e4SLinus Torvalds substream->pcm->card->number, 1381da177e4SLinus Torvalds substream->pcm->device, 1391da177e4SLinus Torvalds substream->stream ? 'c' : 'p'); 1401da177e4SLinus Torvalds if (substream->pstr->xrun_debug > 1) 1411da177e4SLinus Torvalds dump_stack(); 1421da177e4SLinus Torvalds } 1431da177e4SLinus Torvalds #endif 1441da177e4SLinus Torvalds } 1451da177e4SLinus Torvalds 1461da177e4SLinus Torvalds static inline snd_pcm_uframes_t snd_pcm_update_hw_ptr_pos(snd_pcm_substream_t *substream, 1471da177e4SLinus Torvalds snd_pcm_runtime_t *runtime) 1481da177e4SLinus Torvalds { 1491da177e4SLinus Torvalds snd_pcm_uframes_t pos; 1501da177e4SLinus Torvalds 1511da177e4SLinus Torvalds pos = substream->ops->pointer(substream); 1521da177e4SLinus Torvalds if (pos == SNDRV_PCM_POS_XRUN) 1531da177e4SLinus Torvalds return pos; /* XRUN */ 1541da177e4SLinus Torvalds if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP) 1551da177e4SLinus Torvalds snd_timestamp_now((snd_timestamp_t*)&runtime->status->tstamp, runtime->tstamp_timespec); 1561da177e4SLinus Torvalds #ifdef CONFIG_SND_DEBUG 1571da177e4SLinus Torvalds if (pos >= runtime->buffer_size) { 1581da177e4SLinus Torvalds snd_printk(KERN_ERR "BUG: stream = %i, pos = 0x%lx, buffer size = 0x%lx, period size = 0x%lx\n", substream->stream, pos, runtime->buffer_size, runtime->period_size); 1591da177e4SLinus Torvalds } else 1601da177e4SLinus Torvalds #endif 1611da177e4SLinus Torvalds snd_runtime_check(pos < runtime->buffer_size, return 0); 1621da177e4SLinus Torvalds pos -= pos % runtime->min_align; 1631da177e4SLinus Torvalds return pos; 1641da177e4SLinus Torvalds } 1651da177e4SLinus Torvalds 1661da177e4SLinus Torvalds static inline int snd_pcm_update_hw_ptr_post(snd_pcm_substream_t *substream, 1671da177e4SLinus Torvalds snd_pcm_runtime_t *runtime) 1681da177e4SLinus Torvalds { 1691da177e4SLinus Torvalds snd_pcm_uframes_t avail; 1701da177e4SLinus Torvalds 1711da177e4SLinus Torvalds if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 1721da177e4SLinus Torvalds avail = snd_pcm_playback_avail(runtime); 1731da177e4SLinus Torvalds else 1741da177e4SLinus Torvalds avail = snd_pcm_capture_avail(runtime); 1751da177e4SLinus Torvalds if (avail > runtime->avail_max) 1761da177e4SLinus Torvalds runtime->avail_max = avail; 1771da177e4SLinus Torvalds if (avail >= runtime->stop_threshold) { 1781da177e4SLinus Torvalds if (substream->runtime->status->state == SNDRV_PCM_STATE_DRAINING) 1791da177e4SLinus Torvalds snd_pcm_drain_done(substream); 1801da177e4SLinus Torvalds else 1811da177e4SLinus Torvalds xrun(substream); 1821da177e4SLinus Torvalds return -EPIPE; 1831da177e4SLinus Torvalds } 1841da177e4SLinus Torvalds if (avail >= runtime->control->avail_min) 1851da177e4SLinus Torvalds wake_up(&runtime->sleep); 1861da177e4SLinus Torvalds return 0; 1871da177e4SLinus Torvalds } 1881da177e4SLinus Torvalds 1891da177e4SLinus Torvalds static inline int snd_pcm_update_hw_ptr_interrupt(snd_pcm_substream_t *substream) 1901da177e4SLinus Torvalds { 1911da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 1921da177e4SLinus Torvalds snd_pcm_uframes_t pos; 1931da177e4SLinus Torvalds snd_pcm_uframes_t new_hw_ptr, hw_ptr_interrupt; 1941da177e4SLinus Torvalds snd_pcm_sframes_t delta; 1951da177e4SLinus Torvalds 1961da177e4SLinus Torvalds pos = snd_pcm_update_hw_ptr_pos(substream, runtime); 1971da177e4SLinus Torvalds if (pos == SNDRV_PCM_POS_XRUN) { 1981da177e4SLinus Torvalds xrun(substream); 1991da177e4SLinus Torvalds return -EPIPE; 2001da177e4SLinus Torvalds } 2011da177e4SLinus Torvalds if (runtime->period_size == runtime->buffer_size) 2021da177e4SLinus Torvalds goto __next_buf; 2031da177e4SLinus Torvalds new_hw_ptr = runtime->hw_ptr_base + pos; 2041da177e4SLinus Torvalds hw_ptr_interrupt = runtime->hw_ptr_interrupt + runtime->period_size; 2051da177e4SLinus Torvalds 2061da177e4SLinus Torvalds delta = hw_ptr_interrupt - new_hw_ptr; 2071da177e4SLinus Torvalds if (delta > 0) { 2081da177e4SLinus Torvalds if ((snd_pcm_uframes_t)delta < runtime->buffer_size / 2) { 2091da177e4SLinus Torvalds #ifdef CONFIG_SND_DEBUG 2101da177e4SLinus Torvalds if (runtime->periods > 1 && substream->pstr->xrun_debug) { 2111da177e4SLinus Torvalds snd_printd(KERN_ERR "Unexpected hw_pointer value [1] (stream = %i, delta: -%ld, max jitter = %ld): wrong interrupt acknowledge?\n", substream->stream, (long) delta, runtime->buffer_size / 2); 2121da177e4SLinus Torvalds if (substream->pstr->xrun_debug > 1) 2131da177e4SLinus Torvalds dump_stack(); 2141da177e4SLinus Torvalds } 2151da177e4SLinus Torvalds #endif 2161da177e4SLinus Torvalds return 0; 2171da177e4SLinus Torvalds } 2181da177e4SLinus Torvalds __next_buf: 2191da177e4SLinus Torvalds runtime->hw_ptr_base += runtime->buffer_size; 2201da177e4SLinus Torvalds if (runtime->hw_ptr_base == runtime->boundary) 2211da177e4SLinus Torvalds runtime->hw_ptr_base = 0; 2221da177e4SLinus Torvalds new_hw_ptr = runtime->hw_ptr_base + pos; 2231da177e4SLinus Torvalds } 2241da177e4SLinus Torvalds 2251da177e4SLinus Torvalds if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && 2261da177e4SLinus Torvalds runtime->silence_size > 0) 2271da177e4SLinus Torvalds snd_pcm_playback_silence(substream, new_hw_ptr); 2281da177e4SLinus Torvalds 2291da177e4SLinus Torvalds runtime->status->hw_ptr = new_hw_ptr; 2301da177e4SLinus Torvalds runtime->hw_ptr_interrupt = new_hw_ptr - new_hw_ptr % runtime->period_size; 2311da177e4SLinus Torvalds 2321da177e4SLinus Torvalds return snd_pcm_update_hw_ptr_post(substream, runtime); 2331da177e4SLinus Torvalds } 2341da177e4SLinus Torvalds 2351da177e4SLinus Torvalds /* CAUTION: call it with irq disabled */ 2361da177e4SLinus Torvalds int snd_pcm_update_hw_ptr(snd_pcm_substream_t *substream) 2371da177e4SLinus Torvalds { 2381da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 2391da177e4SLinus Torvalds snd_pcm_uframes_t pos; 2401da177e4SLinus Torvalds snd_pcm_uframes_t old_hw_ptr, new_hw_ptr; 2411da177e4SLinus Torvalds snd_pcm_sframes_t delta; 2421da177e4SLinus Torvalds 2431da177e4SLinus Torvalds old_hw_ptr = runtime->status->hw_ptr; 2441da177e4SLinus Torvalds pos = snd_pcm_update_hw_ptr_pos(substream, runtime); 2451da177e4SLinus Torvalds if (pos == SNDRV_PCM_POS_XRUN) { 2461da177e4SLinus Torvalds xrun(substream); 2471da177e4SLinus Torvalds return -EPIPE; 2481da177e4SLinus Torvalds } 2491da177e4SLinus Torvalds new_hw_ptr = runtime->hw_ptr_base + pos; 2501da177e4SLinus Torvalds 2511da177e4SLinus Torvalds delta = old_hw_ptr - new_hw_ptr; 2521da177e4SLinus Torvalds if (delta > 0) { 2531da177e4SLinus Torvalds if ((snd_pcm_uframes_t)delta < runtime->buffer_size / 2) { 2541da177e4SLinus Torvalds #ifdef CONFIG_SND_DEBUG 2551da177e4SLinus Torvalds if (runtime->periods > 2 && substream->pstr->xrun_debug) { 2561da177e4SLinus Torvalds snd_printd(KERN_ERR "Unexpected hw_pointer value [2] (stream = %i, delta: -%ld, max jitter = %ld): wrong interrupt acknowledge?\n", substream->stream, (long) delta, runtime->buffer_size / 2); 2571da177e4SLinus Torvalds if (substream->pstr->xrun_debug > 1) 2581da177e4SLinus Torvalds dump_stack(); 2591da177e4SLinus Torvalds } 2601da177e4SLinus Torvalds #endif 2611da177e4SLinus Torvalds return 0; 2621da177e4SLinus Torvalds } 2631da177e4SLinus Torvalds runtime->hw_ptr_base += runtime->buffer_size; 2641da177e4SLinus Torvalds if (runtime->hw_ptr_base == runtime->boundary) 2651da177e4SLinus Torvalds runtime->hw_ptr_base = 0; 2661da177e4SLinus Torvalds new_hw_ptr = runtime->hw_ptr_base + pos; 2671da177e4SLinus Torvalds } 2681da177e4SLinus Torvalds if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && 2691da177e4SLinus Torvalds runtime->silence_size > 0) 2701da177e4SLinus Torvalds snd_pcm_playback_silence(substream, new_hw_ptr); 2711da177e4SLinus Torvalds 2721da177e4SLinus Torvalds runtime->status->hw_ptr = new_hw_ptr; 2731da177e4SLinus Torvalds 2741da177e4SLinus Torvalds return snd_pcm_update_hw_ptr_post(substream, runtime); 2751da177e4SLinus Torvalds } 2761da177e4SLinus Torvalds 2771da177e4SLinus Torvalds /** 2781da177e4SLinus Torvalds * snd_pcm_set_ops - set the PCM operators 2791da177e4SLinus Torvalds * @pcm: the pcm instance 2801da177e4SLinus Torvalds * @direction: stream direction, SNDRV_PCM_STREAM_XXX 2811da177e4SLinus Torvalds * @ops: the operator table 2821da177e4SLinus Torvalds * 2831da177e4SLinus Torvalds * Sets the given PCM operators to the pcm instance. 2841da177e4SLinus Torvalds */ 2851da177e4SLinus Torvalds void snd_pcm_set_ops(snd_pcm_t *pcm, int direction, snd_pcm_ops_t *ops) 2861da177e4SLinus Torvalds { 2871da177e4SLinus Torvalds snd_pcm_str_t *stream = &pcm->streams[direction]; 2881da177e4SLinus Torvalds snd_pcm_substream_t *substream; 2891da177e4SLinus Torvalds 2901da177e4SLinus Torvalds for (substream = stream->substream; substream != NULL; substream = substream->next) 2911da177e4SLinus Torvalds substream->ops = ops; 2921da177e4SLinus Torvalds } 2931da177e4SLinus Torvalds 2941da177e4SLinus Torvalds 2951da177e4SLinus Torvalds /** 2961da177e4SLinus Torvalds * snd_pcm_sync - set the PCM sync id 2971da177e4SLinus Torvalds * @substream: the pcm substream 2981da177e4SLinus Torvalds * 2991da177e4SLinus Torvalds * Sets the PCM sync identifier for the card. 3001da177e4SLinus Torvalds */ 3011da177e4SLinus Torvalds void snd_pcm_set_sync(snd_pcm_substream_t * substream) 3021da177e4SLinus Torvalds { 3031da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 3041da177e4SLinus Torvalds 3051da177e4SLinus Torvalds runtime->sync.id32[0] = substream->pcm->card->number; 3061da177e4SLinus Torvalds runtime->sync.id32[1] = -1; 3071da177e4SLinus Torvalds runtime->sync.id32[2] = -1; 3081da177e4SLinus Torvalds runtime->sync.id32[3] = -1; 3091da177e4SLinus Torvalds } 3101da177e4SLinus Torvalds 3111da177e4SLinus Torvalds /* 3121da177e4SLinus Torvalds * Standard ioctl routine 3131da177e4SLinus Torvalds */ 3141da177e4SLinus Torvalds 3151da177e4SLinus Torvalds /* Code taken from alsa-lib */ 3161da177e4SLinus Torvalds #define assert(a) snd_assert((a), return -EINVAL) 3171da177e4SLinus Torvalds 3181da177e4SLinus Torvalds static inline unsigned int div32(unsigned int a, unsigned int b, 3191da177e4SLinus Torvalds unsigned int *r) 3201da177e4SLinus Torvalds { 3211da177e4SLinus Torvalds if (b == 0) { 3221da177e4SLinus Torvalds *r = 0; 3231da177e4SLinus Torvalds return UINT_MAX; 3241da177e4SLinus Torvalds } 3251da177e4SLinus Torvalds *r = a % b; 3261da177e4SLinus Torvalds return a / b; 3271da177e4SLinus Torvalds } 3281da177e4SLinus Torvalds 3291da177e4SLinus Torvalds static inline unsigned int div_down(unsigned int a, unsigned int b) 3301da177e4SLinus Torvalds { 3311da177e4SLinus Torvalds if (b == 0) 3321da177e4SLinus Torvalds return UINT_MAX; 3331da177e4SLinus Torvalds return a / b; 3341da177e4SLinus Torvalds } 3351da177e4SLinus Torvalds 3361da177e4SLinus Torvalds static inline unsigned int div_up(unsigned int a, unsigned int b) 3371da177e4SLinus Torvalds { 3381da177e4SLinus Torvalds unsigned int r; 3391da177e4SLinus Torvalds unsigned int q; 3401da177e4SLinus Torvalds if (b == 0) 3411da177e4SLinus Torvalds return UINT_MAX; 3421da177e4SLinus Torvalds q = div32(a, b, &r); 3431da177e4SLinus Torvalds if (r) 3441da177e4SLinus Torvalds ++q; 3451da177e4SLinus Torvalds return q; 3461da177e4SLinus Torvalds } 3471da177e4SLinus Torvalds 3481da177e4SLinus Torvalds static inline unsigned int mul(unsigned int a, unsigned int b) 3491da177e4SLinus Torvalds { 3501da177e4SLinus Torvalds if (a == 0) 3511da177e4SLinus Torvalds return 0; 3521da177e4SLinus Torvalds if (div_down(UINT_MAX, a) < b) 3531da177e4SLinus Torvalds return UINT_MAX; 3541da177e4SLinus Torvalds return a * b; 3551da177e4SLinus Torvalds } 3561da177e4SLinus Torvalds 3571da177e4SLinus Torvalds static inline unsigned int muldiv32(unsigned int a, unsigned int b, 3581da177e4SLinus Torvalds unsigned int c, unsigned int *r) 3591da177e4SLinus Torvalds { 3601da177e4SLinus Torvalds u_int64_t n = (u_int64_t) a * b; 3611da177e4SLinus Torvalds if (c == 0) { 3621da177e4SLinus Torvalds snd_assert(n > 0, ); 3631da177e4SLinus Torvalds *r = 0; 3641da177e4SLinus Torvalds return UINT_MAX; 3651da177e4SLinus Torvalds } 3661da177e4SLinus Torvalds div64_32(&n, c, r); 3671da177e4SLinus Torvalds if (n >= UINT_MAX) { 3681da177e4SLinus Torvalds *r = 0; 3691da177e4SLinus Torvalds return UINT_MAX; 3701da177e4SLinus Torvalds } 3711da177e4SLinus Torvalds return n; 3721da177e4SLinus Torvalds } 3731da177e4SLinus Torvalds 3741da177e4SLinus Torvalds static int snd_interval_refine_min(snd_interval_t *i, unsigned int min, int openmin) 3751da177e4SLinus Torvalds { 3761da177e4SLinus Torvalds int changed = 0; 3771da177e4SLinus Torvalds assert(!snd_interval_empty(i)); 3781da177e4SLinus Torvalds if (i->min < min) { 3791da177e4SLinus Torvalds i->min = min; 3801da177e4SLinus Torvalds i->openmin = openmin; 3811da177e4SLinus Torvalds changed = 1; 3821da177e4SLinus Torvalds } else if (i->min == min && !i->openmin && openmin) { 3831da177e4SLinus Torvalds i->openmin = 1; 3841da177e4SLinus Torvalds changed = 1; 3851da177e4SLinus Torvalds } 3861da177e4SLinus Torvalds if (i->integer) { 3871da177e4SLinus Torvalds if (i->openmin) { 3881da177e4SLinus Torvalds i->min++; 3891da177e4SLinus Torvalds i->openmin = 0; 3901da177e4SLinus Torvalds } 3911da177e4SLinus Torvalds } 3921da177e4SLinus Torvalds if (snd_interval_checkempty(i)) { 3931da177e4SLinus Torvalds snd_interval_none(i); 3941da177e4SLinus Torvalds return -EINVAL; 3951da177e4SLinus Torvalds } 3961da177e4SLinus Torvalds return changed; 3971da177e4SLinus Torvalds } 3981da177e4SLinus Torvalds 3991da177e4SLinus Torvalds static int snd_interval_refine_max(snd_interval_t *i, unsigned int max, int openmax) 4001da177e4SLinus Torvalds { 4011da177e4SLinus Torvalds int changed = 0; 4021da177e4SLinus Torvalds assert(!snd_interval_empty(i)); 4031da177e4SLinus Torvalds if (i->max > max) { 4041da177e4SLinus Torvalds i->max = max; 4051da177e4SLinus Torvalds i->openmax = openmax; 4061da177e4SLinus Torvalds changed = 1; 4071da177e4SLinus Torvalds } else if (i->max == max && !i->openmax && openmax) { 4081da177e4SLinus Torvalds i->openmax = 1; 4091da177e4SLinus Torvalds changed = 1; 4101da177e4SLinus Torvalds } 4111da177e4SLinus Torvalds if (i->integer) { 4121da177e4SLinus Torvalds if (i->openmax) { 4131da177e4SLinus Torvalds i->max--; 4141da177e4SLinus Torvalds i->openmax = 0; 4151da177e4SLinus Torvalds } 4161da177e4SLinus Torvalds } 4171da177e4SLinus Torvalds if (snd_interval_checkempty(i)) { 4181da177e4SLinus Torvalds snd_interval_none(i); 4191da177e4SLinus Torvalds return -EINVAL; 4201da177e4SLinus Torvalds } 4211da177e4SLinus Torvalds return changed; 4221da177e4SLinus Torvalds } 4231da177e4SLinus Torvalds 4241da177e4SLinus Torvalds /** 4251da177e4SLinus Torvalds * snd_interval_refine - refine the interval value of configurator 4261da177e4SLinus Torvalds * @i: the interval value to refine 4271da177e4SLinus Torvalds * @v: the interval value to refer to 4281da177e4SLinus Torvalds * 4291da177e4SLinus Torvalds * Refines the interval value with the reference value. 4301da177e4SLinus Torvalds * The interval is changed to the range satisfying both intervals. 4311da177e4SLinus Torvalds * The interval status (min, max, integer, etc.) are evaluated. 4321da177e4SLinus Torvalds * 4331da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 4341da177e4SLinus Torvalds */ 4351da177e4SLinus Torvalds int snd_interval_refine(snd_interval_t *i, const snd_interval_t *v) 4361da177e4SLinus Torvalds { 4371da177e4SLinus Torvalds int changed = 0; 4381da177e4SLinus Torvalds assert(!snd_interval_empty(i)); 4391da177e4SLinus Torvalds if (i->min < v->min) { 4401da177e4SLinus Torvalds i->min = v->min; 4411da177e4SLinus Torvalds i->openmin = v->openmin; 4421da177e4SLinus Torvalds changed = 1; 4431da177e4SLinus Torvalds } else if (i->min == v->min && !i->openmin && v->openmin) { 4441da177e4SLinus Torvalds i->openmin = 1; 4451da177e4SLinus Torvalds changed = 1; 4461da177e4SLinus Torvalds } 4471da177e4SLinus Torvalds if (i->max > v->max) { 4481da177e4SLinus Torvalds i->max = v->max; 4491da177e4SLinus Torvalds i->openmax = v->openmax; 4501da177e4SLinus Torvalds changed = 1; 4511da177e4SLinus Torvalds } else if (i->max == v->max && !i->openmax && v->openmax) { 4521da177e4SLinus Torvalds i->openmax = 1; 4531da177e4SLinus Torvalds changed = 1; 4541da177e4SLinus Torvalds } 4551da177e4SLinus Torvalds if (!i->integer && v->integer) { 4561da177e4SLinus Torvalds i->integer = 1; 4571da177e4SLinus Torvalds changed = 1; 4581da177e4SLinus Torvalds } 4591da177e4SLinus Torvalds if (i->integer) { 4601da177e4SLinus Torvalds if (i->openmin) { 4611da177e4SLinus Torvalds i->min++; 4621da177e4SLinus Torvalds i->openmin = 0; 4631da177e4SLinus Torvalds } 4641da177e4SLinus Torvalds if (i->openmax) { 4651da177e4SLinus Torvalds i->max--; 4661da177e4SLinus Torvalds i->openmax = 0; 4671da177e4SLinus Torvalds } 4681da177e4SLinus Torvalds } else if (!i->openmin && !i->openmax && i->min == i->max) 4691da177e4SLinus Torvalds i->integer = 1; 4701da177e4SLinus Torvalds if (snd_interval_checkempty(i)) { 4711da177e4SLinus Torvalds snd_interval_none(i); 4721da177e4SLinus Torvalds return -EINVAL; 4731da177e4SLinus Torvalds } 4741da177e4SLinus Torvalds return changed; 4751da177e4SLinus Torvalds } 4761da177e4SLinus Torvalds 4771da177e4SLinus Torvalds static int snd_interval_refine_first(snd_interval_t *i) 4781da177e4SLinus Torvalds { 4791da177e4SLinus Torvalds assert(!snd_interval_empty(i)); 4801da177e4SLinus Torvalds if (snd_interval_single(i)) 4811da177e4SLinus Torvalds return 0; 4821da177e4SLinus Torvalds i->max = i->min; 4831da177e4SLinus Torvalds i->openmax = i->openmin; 4841da177e4SLinus Torvalds if (i->openmax) 4851da177e4SLinus Torvalds i->max++; 4861da177e4SLinus Torvalds return 1; 4871da177e4SLinus Torvalds } 4881da177e4SLinus Torvalds 4891da177e4SLinus Torvalds static int snd_interval_refine_last(snd_interval_t *i) 4901da177e4SLinus Torvalds { 4911da177e4SLinus Torvalds assert(!snd_interval_empty(i)); 4921da177e4SLinus Torvalds if (snd_interval_single(i)) 4931da177e4SLinus Torvalds return 0; 4941da177e4SLinus Torvalds i->min = i->max; 4951da177e4SLinus Torvalds i->openmin = i->openmax; 4961da177e4SLinus Torvalds if (i->openmin) 4971da177e4SLinus Torvalds i->min--; 4981da177e4SLinus Torvalds return 1; 4991da177e4SLinus Torvalds } 5001da177e4SLinus Torvalds 5011da177e4SLinus Torvalds static int snd_interval_refine_set(snd_interval_t *i, unsigned int val) 5021da177e4SLinus Torvalds { 5031da177e4SLinus Torvalds snd_interval_t t; 5041da177e4SLinus Torvalds t.empty = 0; 5051da177e4SLinus Torvalds t.min = t.max = val; 5061da177e4SLinus Torvalds t.openmin = t.openmax = 0; 5071da177e4SLinus Torvalds t.integer = 1; 5081da177e4SLinus Torvalds return snd_interval_refine(i, &t); 5091da177e4SLinus Torvalds } 5101da177e4SLinus Torvalds 5111da177e4SLinus Torvalds void snd_interval_mul(const snd_interval_t *a, const snd_interval_t *b, snd_interval_t *c) 5121da177e4SLinus Torvalds { 5131da177e4SLinus Torvalds if (a->empty || b->empty) { 5141da177e4SLinus Torvalds snd_interval_none(c); 5151da177e4SLinus Torvalds return; 5161da177e4SLinus Torvalds } 5171da177e4SLinus Torvalds c->empty = 0; 5181da177e4SLinus Torvalds c->min = mul(a->min, b->min); 5191da177e4SLinus Torvalds c->openmin = (a->openmin || b->openmin); 5201da177e4SLinus Torvalds c->max = mul(a->max, b->max); 5211da177e4SLinus Torvalds c->openmax = (a->openmax || b->openmax); 5221da177e4SLinus Torvalds c->integer = (a->integer && b->integer); 5231da177e4SLinus Torvalds } 5241da177e4SLinus Torvalds 5251da177e4SLinus Torvalds /** 5261da177e4SLinus Torvalds * snd_interval_div - refine the interval value with division 5271da177e4SLinus Torvalds * 5281da177e4SLinus Torvalds * c = a / b 5291da177e4SLinus Torvalds * 5301da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 5311da177e4SLinus Torvalds */ 5321da177e4SLinus Torvalds void snd_interval_div(const snd_interval_t *a, const snd_interval_t *b, snd_interval_t *c) 5331da177e4SLinus Torvalds { 5341da177e4SLinus Torvalds unsigned int r; 5351da177e4SLinus Torvalds if (a->empty || b->empty) { 5361da177e4SLinus Torvalds snd_interval_none(c); 5371da177e4SLinus Torvalds return; 5381da177e4SLinus Torvalds } 5391da177e4SLinus Torvalds c->empty = 0; 5401da177e4SLinus Torvalds c->min = div32(a->min, b->max, &r); 5411da177e4SLinus Torvalds c->openmin = (r || a->openmin || b->openmax); 5421da177e4SLinus Torvalds if (b->min > 0) { 5431da177e4SLinus Torvalds c->max = div32(a->max, b->min, &r); 5441da177e4SLinus Torvalds if (r) { 5451da177e4SLinus Torvalds c->max++; 5461da177e4SLinus Torvalds c->openmax = 1; 5471da177e4SLinus Torvalds } else 5481da177e4SLinus Torvalds c->openmax = (a->openmax || b->openmin); 5491da177e4SLinus Torvalds } else { 5501da177e4SLinus Torvalds c->max = UINT_MAX; 5511da177e4SLinus Torvalds c->openmax = 0; 5521da177e4SLinus Torvalds } 5531da177e4SLinus Torvalds c->integer = 0; 5541da177e4SLinus Torvalds } 5551da177e4SLinus Torvalds 5561da177e4SLinus Torvalds /** 5571da177e4SLinus Torvalds * snd_interval_muldivk - refine the interval value 5581da177e4SLinus Torvalds * 5591da177e4SLinus Torvalds * c = a * b / k 5601da177e4SLinus Torvalds * 5611da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 5621da177e4SLinus Torvalds */ 5631da177e4SLinus Torvalds void snd_interval_muldivk(const snd_interval_t *a, const snd_interval_t *b, 5641da177e4SLinus Torvalds unsigned int k, snd_interval_t *c) 5651da177e4SLinus Torvalds { 5661da177e4SLinus Torvalds unsigned int r; 5671da177e4SLinus Torvalds if (a->empty || b->empty) { 5681da177e4SLinus Torvalds snd_interval_none(c); 5691da177e4SLinus Torvalds return; 5701da177e4SLinus Torvalds } 5711da177e4SLinus Torvalds c->empty = 0; 5721da177e4SLinus Torvalds c->min = muldiv32(a->min, b->min, k, &r); 5731da177e4SLinus Torvalds c->openmin = (r || a->openmin || b->openmin); 5741da177e4SLinus Torvalds c->max = muldiv32(a->max, b->max, k, &r); 5751da177e4SLinus Torvalds if (r) { 5761da177e4SLinus Torvalds c->max++; 5771da177e4SLinus Torvalds c->openmax = 1; 5781da177e4SLinus Torvalds } else 5791da177e4SLinus Torvalds c->openmax = (a->openmax || b->openmax); 5801da177e4SLinus Torvalds c->integer = 0; 5811da177e4SLinus Torvalds } 5821da177e4SLinus Torvalds 5831da177e4SLinus Torvalds /** 5841da177e4SLinus Torvalds * snd_interval_mulkdiv - refine the interval value 5851da177e4SLinus Torvalds * 5861da177e4SLinus Torvalds * c = a * k / b 5871da177e4SLinus Torvalds * 5881da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 5891da177e4SLinus Torvalds */ 5901da177e4SLinus Torvalds void snd_interval_mulkdiv(const snd_interval_t *a, unsigned int k, 5911da177e4SLinus Torvalds const snd_interval_t *b, snd_interval_t *c) 5921da177e4SLinus Torvalds { 5931da177e4SLinus Torvalds unsigned int r; 5941da177e4SLinus Torvalds if (a->empty || b->empty) { 5951da177e4SLinus Torvalds snd_interval_none(c); 5961da177e4SLinus Torvalds return; 5971da177e4SLinus Torvalds } 5981da177e4SLinus Torvalds c->empty = 0; 5991da177e4SLinus Torvalds c->min = muldiv32(a->min, k, b->max, &r); 6001da177e4SLinus Torvalds c->openmin = (r || a->openmin || b->openmax); 6011da177e4SLinus Torvalds if (b->min > 0) { 6021da177e4SLinus Torvalds c->max = muldiv32(a->max, k, b->min, &r); 6031da177e4SLinus Torvalds if (r) { 6041da177e4SLinus Torvalds c->max++; 6051da177e4SLinus Torvalds c->openmax = 1; 6061da177e4SLinus Torvalds } else 6071da177e4SLinus Torvalds c->openmax = (a->openmax || b->openmin); 6081da177e4SLinus Torvalds } else { 6091da177e4SLinus Torvalds c->max = UINT_MAX; 6101da177e4SLinus Torvalds c->openmax = 0; 6111da177e4SLinus Torvalds } 6121da177e4SLinus Torvalds c->integer = 0; 6131da177e4SLinus Torvalds } 6141da177e4SLinus Torvalds 6151da177e4SLinus Torvalds #undef assert 6161da177e4SLinus Torvalds /* ---- */ 6171da177e4SLinus Torvalds 6181da177e4SLinus Torvalds 6191da177e4SLinus Torvalds /** 6201da177e4SLinus Torvalds * snd_interval_ratnum - refine the interval value 6211da177e4SLinus Torvalds * 6221da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 6231da177e4SLinus Torvalds */ 6241da177e4SLinus Torvalds int snd_interval_ratnum(snd_interval_t *i, 6251da177e4SLinus Torvalds unsigned int rats_count, ratnum_t *rats, 6261da177e4SLinus Torvalds unsigned int *nump, unsigned int *denp) 6271da177e4SLinus Torvalds { 6281da177e4SLinus Torvalds unsigned int best_num, best_diff, best_den; 6291da177e4SLinus Torvalds unsigned int k; 6301da177e4SLinus Torvalds snd_interval_t t; 6311da177e4SLinus Torvalds int err; 6321da177e4SLinus Torvalds 6331da177e4SLinus Torvalds best_num = best_den = best_diff = 0; 6341da177e4SLinus Torvalds for (k = 0; k < rats_count; ++k) { 6351da177e4SLinus Torvalds unsigned int num = rats[k].num; 6361da177e4SLinus Torvalds unsigned int den; 6371da177e4SLinus Torvalds unsigned int q = i->min; 6381da177e4SLinus Torvalds int diff; 6391da177e4SLinus Torvalds if (q == 0) 6401da177e4SLinus Torvalds q = 1; 6411da177e4SLinus Torvalds den = div_down(num, q); 6421da177e4SLinus Torvalds if (den < rats[k].den_min) 6431da177e4SLinus Torvalds continue; 6441da177e4SLinus Torvalds if (den > rats[k].den_max) 6451da177e4SLinus Torvalds den = rats[k].den_max; 6461da177e4SLinus Torvalds else { 6471da177e4SLinus Torvalds unsigned int r; 6481da177e4SLinus Torvalds r = (den - rats[k].den_min) % rats[k].den_step; 6491da177e4SLinus Torvalds if (r != 0) 6501da177e4SLinus Torvalds den -= r; 6511da177e4SLinus Torvalds } 6521da177e4SLinus Torvalds diff = num - q * den; 6531da177e4SLinus Torvalds if (best_num == 0 || 6541da177e4SLinus Torvalds diff * best_den < best_diff * den) { 6551da177e4SLinus Torvalds best_diff = diff; 6561da177e4SLinus Torvalds best_den = den; 6571da177e4SLinus Torvalds best_num = num; 6581da177e4SLinus Torvalds } 6591da177e4SLinus Torvalds } 6601da177e4SLinus Torvalds if (best_den == 0) { 6611da177e4SLinus Torvalds i->empty = 1; 6621da177e4SLinus Torvalds return -EINVAL; 6631da177e4SLinus Torvalds } 6641da177e4SLinus Torvalds t.min = div_down(best_num, best_den); 6651da177e4SLinus Torvalds t.openmin = !!(best_num % best_den); 6661da177e4SLinus Torvalds 6671da177e4SLinus Torvalds best_num = best_den = best_diff = 0; 6681da177e4SLinus Torvalds for (k = 0; k < rats_count; ++k) { 6691da177e4SLinus Torvalds unsigned int num = rats[k].num; 6701da177e4SLinus Torvalds unsigned int den; 6711da177e4SLinus Torvalds unsigned int q = i->max; 6721da177e4SLinus Torvalds int diff; 6731da177e4SLinus Torvalds if (q == 0) { 6741da177e4SLinus Torvalds i->empty = 1; 6751da177e4SLinus Torvalds return -EINVAL; 6761da177e4SLinus Torvalds } 6771da177e4SLinus Torvalds den = div_up(num, q); 6781da177e4SLinus Torvalds if (den > rats[k].den_max) 6791da177e4SLinus Torvalds continue; 6801da177e4SLinus Torvalds if (den < rats[k].den_min) 6811da177e4SLinus Torvalds den = rats[k].den_min; 6821da177e4SLinus Torvalds else { 6831da177e4SLinus Torvalds unsigned int r; 6841da177e4SLinus Torvalds r = (den - rats[k].den_min) % rats[k].den_step; 6851da177e4SLinus Torvalds if (r != 0) 6861da177e4SLinus Torvalds den += rats[k].den_step - r; 6871da177e4SLinus Torvalds } 6881da177e4SLinus Torvalds diff = q * den - num; 6891da177e4SLinus Torvalds if (best_num == 0 || 6901da177e4SLinus Torvalds diff * best_den < best_diff * den) { 6911da177e4SLinus Torvalds best_diff = diff; 6921da177e4SLinus Torvalds best_den = den; 6931da177e4SLinus Torvalds best_num = num; 6941da177e4SLinus Torvalds } 6951da177e4SLinus Torvalds } 6961da177e4SLinus Torvalds if (best_den == 0) { 6971da177e4SLinus Torvalds i->empty = 1; 6981da177e4SLinus Torvalds return -EINVAL; 6991da177e4SLinus Torvalds } 7001da177e4SLinus Torvalds t.max = div_up(best_num, best_den); 7011da177e4SLinus Torvalds t.openmax = !!(best_num % best_den); 7021da177e4SLinus Torvalds t.integer = 0; 7031da177e4SLinus Torvalds err = snd_interval_refine(i, &t); 7041da177e4SLinus Torvalds if (err < 0) 7051da177e4SLinus Torvalds return err; 7061da177e4SLinus Torvalds 7071da177e4SLinus Torvalds if (snd_interval_single(i)) { 7081da177e4SLinus Torvalds if (nump) 7091da177e4SLinus Torvalds *nump = best_num; 7101da177e4SLinus Torvalds if (denp) 7111da177e4SLinus Torvalds *denp = best_den; 7121da177e4SLinus Torvalds } 7131da177e4SLinus Torvalds return err; 7141da177e4SLinus Torvalds } 7151da177e4SLinus Torvalds 7161da177e4SLinus Torvalds /** 7171da177e4SLinus Torvalds * snd_interval_ratden - refine the interval value 7181da177e4SLinus Torvalds * 7191da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 7201da177e4SLinus Torvalds */ 7211da177e4SLinus Torvalds static int snd_interval_ratden(snd_interval_t *i, 7221da177e4SLinus Torvalds unsigned int rats_count, ratden_t *rats, 7231da177e4SLinus Torvalds unsigned int *nump, unsigned int *denp) 7241da177e4SLinus Torvalds { 7251da177e4SLinus Torvalds unsigned int best_num, best_diff, best_den; 7261da177e4SLinus Torvalds unsigned int k; 7271da177e4SLinus Torvalds snd_interval_t t; 7281da177e4SLinus Torvalds int err; 7291da177e4SLinus Torvalds 7301da177e4SLinus Torvalds best_num = best_den = best_diff = 0; 7311da177e4SLinus Torvalds for (k = 0; k < rats_count; ++k) { 7321da177e4SLinus Torvalds unsigned int num; 7331da177e4SLinus Torvalds unsigned int den = rats[k].den; 7341da177e4SLinus Torvalds unsigned int q = i->min; 7351da177e4SLinus Torvalds int diff; 7361da177e4SLinus Torvalds num = mul(q, den); 7371da177e4SLinus Torvalds if (num > rats[k].num_max) 7381da177e4SLinus Torvalds continue; 7391da177e4SLinus Torvalds if (num < rats[k].num_min) 7401da177e4SLinus Torvalds num = rats[k].num_max; 7411da177e4SLinus Torvalds else { 7421da177e4SLinus Torvalds unsigned int r; 7431da177e4SLinus Torvalds r = (num - rats[k].num_min) % rats[k].num_step; 7441da177e4SLinus Torvalds if (r != 0) 7451da177e4SLinus Torvalds num += rats[k].num_step - r; 7461da177e4SLinus Torvalds } 7471da177e4SLinus Torvalds diff = num - q * den; 7481da177e4SLinus Torvalds if (best_num == 0 || 7491da177e4SLinus Torvalds diff * best_den < best_diff * den) { 7501da177e4SLinus Torvalds best_diff = diff; 7511da177e4SLinus Torvalds best_den = den; 7521da177e4SLinus Torvalds best_num = num; 7531da177e4SLinus Torvalds } 7541da177e4SLinus Torvalds } 7551da177e4SLinus Torvalds if (best_den == 0) { 7561da177e4SLinus Torvalds i->empty = 1; 7571da177e4SLinus Torvalds return -EINVAL; 7581da177e4SLinus Torvalds } 7591da177e4SLinus Torvalds t.min = div_down(best_num, best_den); 7601da177e4SLinus Torvalds t.openmin = !!(best_num % best_den); 7611da177e4SLinus Torvalds 7621da177e4SLinus Torvalds best_num = best_den = best_diff = 0; 7631da177e4SLinus Torvalds for (k = 0; k < rats_count; ++k) { 7641da177e4SLinus Torvalds unsigned int num; 7651da177e4SLinus Torvalds unsigned int den = rats[k].den; 7661da177e4SLinus Torvalds unsigned int q = i->max; 7671da177e4SLinus Torvalds int diff; 7681da177e4SLinus Torvalds num = mul(q, den); 7691da177e4SLinus Torvalds if (num < rats[k].num_min) 7701da177e4SLinus Torvalds continue; 7711da177e4SLinus Torvalds if (num > rats[k].num_max) 7721da177e4SLinus Torvalds num = rats[k].num_max; 7731da177e4SLinus Torvalds else { 7741da177e4SLinus Torvalds unsigned int r; 7751da177e4SLinus Torvalds r = (num - rats[k].num_min) % rats[k].num_step; 7761da177e4SLinus Torvalds if (r != 0) 7771da177e4SLinus Torvalds num -= r; 7781da177e4SLinus Torvalds } 7791da177e4SLinus Torvalds diff = q * den - num; 7801da177e4SLinus Torvalds if (best_num == 0 || 7811da177e4SLinus Torvalds diff * best_den < best_diff * den) { 7821da177e4SLinus Torvalds best_diff = diff; 7831da177e4SLinus Torvalds best_den = den; 7841da177e4SLinus Torvalds best_num = num; 7851da177e4SLinus Torvalds } 7861da177e4SLinus Torvalds } 7871da177e4SLinus Torvalds if (best_den == 0) { 7881da177e4SLinus Torvalds i->empty = 1; 7891da177e4SLinus Torvalds return -EINVAL; 7901da177e4SLinus Torvalds } 7911da177e4SLinus Torvalds t.max = div_up(best_num, best_den); 7921da177e4SLinus Torvalds t.openmax = !!(best_num % best_den); 7931da177e4SLinus Torvalds t.integer = 0; 7941da177e4SLinus Torvalds err = snd_interval_refine(i, &t); 7951da177e4SLinus Torvalds if (err < 0) 7961da177e4SLinus Torvalds return err; 7971da177e4SLinus Torvalds 7981da177e4SLinus Torvalds if (snd_interval_single(i)) { 7991da177e4SLinus Torvalds if (nump) 8001da177e4SLinus Torvalds *nump = best_num; 8011da177e4SLinus Torvalds if (denp) 8021da177e4SLinus Torvalds *denp = best_den; 8031da177e4SLinus Torvalds } 8041da177e4SLinus Torvalds return err; 8051da177e4SLinus Torvalds } 8061da177e4SLinus Torvalds 8071da177e4SLinus Torvalds /** 8081da177e4SLinus Torvalds * snd_interval_list - refine the interval value from the list 8091da177e4SLinus Torvalds * @i: the interval value to refine 8101da177e4SLinus Torvalds * @count: the number of elements in the list 8111da177e4SLinus Torvalds * @list: the value list 8121da177e4SLinus Torvalds * @mask: the bit-mask to evaluate 8131da177e4SLinus Torvalds * 8141da177e4SLinus Torvalds * Refines the interval value from the list. 8151da177e4SLinus Torvalds * When mask is non-zero, only the elements corresponding to bit 1 are 8161da177e4SLinus Torvalds * evaluated. 8171da177e4SLinus Torvalds * 8181da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 8191da177e4SLinus Torvalds */ 8201da177e4SLinus Torvalds int snd_interval_list(snd_interval_t *i, unsigned int count, unsigned int *list, unsigned int mask) 8211da177e4SLinus Torvalds { 8221da177e4SLinus Torvalds unsigned int k; 8231da177e4SLinus Torvalds int changed = 0; 8241da177e4SLinus Torvalds for (k = 0; k < count; k++) { 8251da177e4SLinus Torvalds if (mask && !(mask & (1 << k))) 8261da177e4SLinus Torvalds continue; 8271da177e4SLinus Torvalds if (i->min == list[k] && !i->openmin) 8281da177e4SLinus Torvalds goto _l1; 8291da177e4SLinus Torvalds if (i->min < list[k]) { 8301da177e4SLinus Torvalds i->min = list[k]; 8311da177e4SLinus Torvalds i->openmin = 0; 8321da177e4SLinus Torvalds changed = 1; 8331da177e4SLinus Torvalds goto _l1; 8341da177e4SLinus Torvalds } 8351da177e4SLinus Torvalds } 8361da177e4SLinus Torvalds i->empty = 1; 8371da177e4SLinus Torvalds return -EINVAL; 8381da177e4SLinus Torvalds _l1: 8391da177e4SLinus Torvalds for (k = count; k-- > 0;) { 8401da177e4SLinus Torvalds if (mask && !(mask & (1 << k))) 8411da177e4SLinus Torvalds continue; 8421da177e4SLinus Torvalds if (i->max == list[k] && !i->openmax) 8431da177e4SLinus Torvalds goto _l2; 8441da177e4SLinus Torvalds if (i->max > list[k]) { 8451da177e4SLinus Torvalds i->max = list[k]; 8461da177e4SLinus Torvalds i->openmax = 0; 8471da177e4SLinus Torvalds changed = 1; 8481da177e4SLinus Torvalds goto _l2; 8491da177e4SLinus Torvalds } 8501da177e4SLinus Torvalds } 8511da177e4SLinus Torvalds i->empty = 1; 8521da177e4SLinus Torvalds return -EINVAL; 8531da177e4SLinus Torvalds _l2: 8541da177e4SLinus Torvalds if (snd_interval_checkempty(i)) { 8551da177e4SLinus Torvalds i->empty = 1; 8561da177e4SLinus Torvalds return -EINVAL; 8571da177e4SLinus Torvalds } 8581da177e4SLinus Torvalds return changed; 8591da177e4SLinus Torvalds } 8601da177e4SLinus Torvalds 8611da177e4SLinus Torvalds static int snd_interval_step(snd_interval_t *i, unsigned int min, unsigned int step) 8621da177e4SLinus Torvalds { 8631da177e4SLinus Torvalds unsigned int n; 8641da177e4SLinus Torvalds int changed = 0; 8651da177e4SLinus Torvalds n = (i->min - min) % step; 8661da177e4SLinus Torvalds if (n != 0 || i->openmin) { 8671da177e4SLinus Torvalds i->min += step - n; 8681da177e4SLinus Torvalds changed = 1; 8691da177e4SLinus Torvalds } 8701da177e4SLinus Torvalds n = (i->max - min) % step; 8711da177e4SLinus Torvalds if (n != 0 || i->openmax) { 8721da177e4SLinus Torvalds i->max -= n; 8731da177e4SLinus Torvalds changed = 1; 8741da177e4SLinus Torvalds } 8751da177e4SLinus Torvalds if (snd_interval_checkempty(i)) { 8761da177e4SLinus Torvalds i->empty = 1; 8771da177e4SLinus Torvalds return -EINVAL; 8781da177e4SLinus Torvalds } 8791da177e4SLinus Torvalds return changed; 8801da177e4SLinus Torvalds } 8811da177e4SLinus Torvalds 8821da177e4SLinus Torvalds /* Info constraints helpers */ 8831da177e4SLinus Torvalds 8841da177e4SLinus Torvalds /** 8851da177e4SLinus Torvalds * snd_pcm_hw_rule_add - add the hw-constraint rule 8861da177e4SLinus Torvalds * @runtime: the pcm runtime instance 8871da177e4SLinus Torvalds * @cond: condition bits 8881da177e4SLinus Torvalds * @var: the variable to evaluate 8891da177e4SLinus Torvalds * @func: the evaluation function 8901da177e4SLinus Torvalds * @private: the private data pointer passed to function 8911da177e4SLinus Torvalds * @dep: the dependent variables 8921da177e4SLinus Torvalds * 8931da177e4SLinus Torvalds * Returns zero if successful, or a negative error code on failure. 8941da177e4SLinus Torvalds */ 8951da177e4SLinus Torvalds int snd_pcm_hw_rule_add(snd_pcm_runtime_t *runtime, unsigned int cond, 8961da177e4SLinus Torvalds int var, 8971da177e4SLinus Torvalds snd_pcm_hw_rule_func_t func, void *private, 8981da177e4SLinus Torvalds int dep, ...) 8991da177e4SLinus Torvalds { 9001da177e4SLinus Torvalds snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints; 9011da177e4SLinus Torvalds snd_pcm_hw_rule_t *c; 9021da177e4SLinus Torvalds unsigned int k; 9031da177e4SLinus Torvalds va_list args; 9041da177e4SLinus Torvalds va_start(args, dep); 9051da177e4SLinus Torvalds if (constrs->rules_num >= constrs->rules_all) { 9061da177e4SLinus Torvalds snd_pcm_hw_rule_t *new; 9071da177e4SLinus Torvalds unsigned int new_rules = constrs->rules_all + 16; 9081da177e4SLinus Torvalds new = kcalloc(new_rules, sizeof(*c), GFP_KERNEL); 9091da177e4SLinus Torvalds if (!new) 9101da177e4SLinus Torvalds return -ENOMEM; 9111da177e4SLinus Torvalds if (constrs->rules) { 9121da177e4SLinus Torvalds memcpy(new, constrs->rules, 9131da177e4SLinus Torvalds constrs->rules_num * sizeof(*c)); 9141da177e4SLinus Torvalds kfree(constrs->rules); 9151da177e4SLinus Torvalds } 9161da177e4SLinus Torvalds constrs->rules = new; 9171da177e4SLinus Torvalds constrs->rules_all = new_rules; 9181da177e4SLinus Torvalds } 9191da177e4SLinus Torvalds c = &constrs->rules[constrs->rules_num]; 9201da177e4SLinus Torvalds c->cond = cond; 9211da177e4SLinus Torvalds c->func = func; 9221da177e4SLinus Torvalds c->var = var; 9231da177e4SLinus Torvalds c->private = private; 9241da177e4SLinus Torvalds k = 0; 9251da177e4SLinus Torvalds while (1) { 9261da177e4SLinus Torvalds snd_assert(k < ARRAY_SIZE(c->deps), return -EINVAL); 9271da177e4SLinus Torvalds c->deps[k++] = dep; 9281da177e4SLinus Torvalds if (dep < 0) 9291da177e4SLinus Torvalds break; 9301da177e4SLinus Torvalds dep = va_arg(args, int); 9311da177e4SLinus Torvalds } 9321da177e4SLinus Torvalds constrs->rules_num++; 9331da177e4SLinus Torvalds va_end(args); 9341da177e4SLinus Torvalds return 0; 9351da177e4SLinus Torvalds } 9361da177e4SLinus Torvalds 9371da177e4SLinus Torvalds /** 9381da177e4SLinus Torvalds * snd_pcm_hw_constraint_mask 9391da177e4SLinus Torvalds */ 9401da177e4SLinus Torvalds int snd_pcm_hw_constraint_mask(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var, 9411da177e4SLinus Torvalds u_int32_t mask) 9421da177e4SLinus Torvalds { 9431da177e4SLinus Torvalds snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints; 9441da177e4SLinus Torvalds snd_mask_t *maskp = constrs_mask(constrs, var); 9451da177e4SLinus Torvalds *maskp->bits &= mask; 9461da177e4SLinus Torvalds memset(maskp->bits + 1, 0, (SNDRV_MASK_MAX-32) / 8); /* clear rest */ 9471da177e4SLinus Torvalds if (*maskp->bits == 0) 9481da177e4SLinus Torvalds return -EINVAL; 9491da177e4SLinus Torvalds return 0; 9501da177e4SLinus Torvalds } 9511da177e4SLinus Torvalds 9521da177e4SLinus Torvalds /** 9531da177e4SLinus Torvalds * snd_pcm_hw_constraint_mask64 9541da177e4SLinus Torvalds */ 9551da177e4SLinus Torvalds int snd_pcm_hw_constraint_mask64(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var, 9561da177e4SLinus Torvalds u_int64_t mask) 9571da177e4SLinus Torvalds { 9581da177e4SLinus Torvalds snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints; 9591da177e4SLinus Torvalds snd_mask_t *maskp = constrs_mask(constrs, var); 9601da177e4SLinus Torvalds maskp->bits[0] &= (u_int32_t)mask; 9611da177e4SLinus Torvalds maskp->bits[1] &= (u_int32_t)(mask >> 32); 9621da177e4SLinus Torvalds memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */ 9631da177e4SLinus Torvalds if (! maskp->bits[0] && ! maskp->bits[1]) 9641da177e4SLinus Torvalds return -EINVAL; 9651da177e4SLinus Torvalds return 0; 9661da177e4SLinus Torvalds } 9671da177e4SLinus Torvalds 9681da177e4SLinus Torvalds /** 9691da177e4SLinus Torvalds * snd_pcm_hw_constraint_integer 9701da177e4SLinus Torvalds */ 9711da177e4SLinus Torvalds int snd_pcm_hw_constraint_integer(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var) 9721da177e4SLinus Torvalds { 9731da177e4SLinus Torvalds snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints; 9741da177e4SLinus Torvalds return snd_interval_setinteger(constrs_interval(constrs, var)); 9751da177e4SLinus Torvalds } 9761da177e4SLinus Torvalds 9771da177e4SLinus Torvalds /** 9781da177e4SLinus Torvalds * snd_pcm_hw_constraint_minmax 9791da177e4SLinus Torvalds */ 9801da177e4SLinus Torvalds int snd_pcm_hw_constraint_minmax(snd_pcm_runtime_t *runtime, snd_pcm_hw_param_t var, 9811da177e4SLinus Torvalds unsigned int min, unsigned int max) 9821da177e4SLinus Torvalds { 9831da177e4SLinus Torvalds snd_pcm_hw_constraints_t *constrs = &runtime->hw_constraints; 9841da177e4SLinus Torvalds snd_interval_t t; 9851da177e4SLinus Torvalds t.min = min; 9861da177e4SLinus Torvalds t.max = max; 9871da177e4SLinus Torvalds t.openmin = t.openmax = 0; 9881da177e4SLinus Torvalds t.integer = 0; 9891da177e4SLinus Torvalds return snd_interval_refine(constrs_interval(constrs, var), &t); 9901da177e4SLinus Torvalds } 9911da177e4SLinus Torvalds 9921da177e4SLinus Torvalds static int snd_pcm_hw_rule_list(snd_pcm_hw_params_t *params, 9931da177e4SLinus Torvalds snd_pcm_hw_rule_t *rule) 9941da177e4SLinus Torvalds { 9951da177e4SLinus Torvalds snd_pcm_hw_constraint_list_t *list = rule->private; 9961da177e4SLinus Torvalds return snd_interval_list(hw_param_interval(params, rule->var), list->count, list->list, list->mask); 9971da177e4SLinus Torvalds } 9981da177e4SLinus Torvalds 9991da177e4SLinus Torvalds 10001da177e4SLinus Torvalds /** 10011da177e4SLinus Torvalds * snd_pcm_hw_constraint_list 10021da177e4SLinus Torvalds */ 10031da177e4SLinus Torvalds int snd_pcm_hw_constraint_list(snd_pcm_runtime_t *runtime, 10041da177e4SLinus Torvalds unsigned int cond, 10051da177e4SLinus Torvalds snd_pcm_hw_param_t var, 10061da177e4SLinus Torvalds snd_pcm_hw_constraint_list_t *l) 10071da177e4SLinus Torvalds { 10081da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 10091da177e4SLinus Torvalds snd_pcm_hw_rule_list, l, 10101da177e4SLinus Torvalds var, -1); 10111da177e4SLinus Torvalds } 10121da177e4SLinus Torvalds 10131da177e4SLinus Torvalds static int snd_pcm_hw_rule_ratnums(snd_pcm_hw_params_t *params, 10141da177e4SLinus Torvalds snd_pcm_hw_rule_t *rule) 10151da177e4SLinus Torvalds { 10161da177e4SLinus Torvalds snd_pcm_hw_constraint_ratnums_t *r = rule->private; 10171da177e4SLinus Torvalds unsigned int num = 0, den = 0; 10181da177e4SLinus Torvalds int err; 10191da177e4SLinus Torvalds err = snd_interval_ratnum(hw_param_interval(params, rule->var), 10201da177e4SLinus Torvalds r->nrats, r->rats, &num, &den); 10211da177e4SLinus Torvalds if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { 10221da177e4SLinus Torvalds params->rate_num = num; 10231da177e4SLinus Torvalds params->rate_den = den; 10241da177e4SLinus Torvalds } 10251da177e4SLinus Torvalds return err; 10261da177e4SLinus Torvalds } 10271da177e4SLinus Torvalds 10281da177e4SLinus Torvalds /** 10291da177e4SLinus Torvalds * snd_pcm_hw_constraint_ratnums 10301da177e4SLinus Torvalds */ 10311da177e4SLinus Torvalds int snd_pcm_hw_constraint_ratnums(snd_pcm_runtime_t *runtime, 10321da177e4SLinus Torvalds unsigned int cond, 10331da177e4SLinus Torvalds snd_pcm_hw_param_t var, 10341da177e4SLinus Torvalds snd_pcm_hw_constraint_ratnums_t *r) 10351da177e4SLinus Torvalds { 10361da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 10371da177e4SLinus Torvalds snd_pcm_hw_rule_ratnums, r, 10381da177e4SLinus Torvalds var, -1); 10391da177e4SLinus Torvalds } 10401da177e4SLinus Torvalds 10411da177e4SLinus Torvalds static int snd_pcm_hw_rule_ratdens(snd_pcm_hw_params_t *params, 10421da177e4SLinus Torvalds snd_pcm_hw_rule_t *rule) 10431da177e4SLinus Torvalds { 10441da177e4SLinus Torvalds snd_pcm_hw_constraint_ratdens_t *r = rule->private; 10451da177e4SLinus Torvalds unsigned int num = 0, den = 0; 10461da177e4SLinus Torvalds int err = snd_interval_ratden(hw_param_interval(params, rule->var), 10471da177e4SLinus Torvalds r->nrats, r->rats, &num, &den); 10481da177e4SLinus Torvalds if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { 10491da177e4SLinus Torvalds params->rate_num = num; 10501da177e4SLinus Torvalds params->rate_den = den; 10511da177e4SLinus Torvalds } 10521da177e4SLinus Torvalds return err; 10531da177e4SLinus Torvalds } 10541da177e4SLinus Torvalds 10551da177e4SLinus Torvalds /** 10561da177e4SLinus Torvalds * snd_pcm_hw_constraint_ratdens 10571da177e4SLinus Torvalds */ 10581da177e4SLinus Torvalds int snd_pcm_hw_constraint_ratdens(snd_pcm_runtime_t *runtime, 10591da177e4SLinus Torvalds unsigned int cond, 10601da177e4SLinus Torvalds snd_pcm_hw_param_t var, 10611da177e4SLinus Torvalds snd_pcm_hw_constraint_ratdens_t *r) 10621da177e4SLinus Torvalds { 10631da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 10641da177e4SLinus Torvalds snd_pcm_hw_rule_ratdens, r, 10651da177e4SLinus Torvalds var, -1); 10661da177e4SLinus Torvalds } 10671da177e4SLinus Torvalds 10681da177e4SLinus Torvalds static int snd_pcm_hw_rule_msbits(snd_pcm_hw_params_t *params, 10691da177e4SLinus Torvalds snd_pcm_hw_rule_t *rule) 10701da177e4SLinus Torvalds { 10711da177e4SLinus Torvalds unsigned int l = (unsigned long) rule->private; 10721da177e4SLinus Torvalds int width = l & 0xffff; 10731da177e4SLinus Torvalds unsigned int msbits = l >> 16; 10741da177e4SLinus Torvalds snd_interval_t *i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); 10751da177e4SLinus Torvalds if (snd_interval_single(i) && snd_interval_value(i) == width) 10761da177e4SLinus Torvalds params->msbits = msbits; 10771da177e4SLinus Torvalds return 0; 10781da177e4SLinus Torvalds } 10791da177e4SLinus Torvalds 10801da177e4SLinus Torvalds /** 10811da177e4SLinus Torvalds * snd_pcm_hw_constraint_msbits 10821da177e4SLinus Torvalds */ 10831da177e4SLinus Torvalds int snd_pcm_hw_constraint_msbits(snd_pcm_runtime_t *runtime, 10841da177e4SLinus Torvalds unsigned int cond, 10851da177e4SLinus Torvalds unsigned int width, 10861da177e4SLinus Torvalds unsigned int msbits) 10871da177e4SLinus Torvalds { 10881da177e4SLinus Torvalds unsigned long l = (msbits << 16) | width; 10891da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, -1, 10901da177e4SLinus Torvalds snd_pcm_hw_rule_msbits, 10911da177e4SLinus Torvalds (void*) l, 10921da177e4SLinus Torvalds SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); 10931da177e4SLinus Torvalds } 10941da177e4SLinus Torvalds 10951da177e4SLinus Torvalds static int snd_pcm_hw_rule_step(snd_pcm_hw_params_t *params, 10961da177e4SLinus Torvalds snd_pcm_hw_rule_t *rule) 10971da177e4SLinus Torvalds { 10981da177e4SLinus Torvalds unsigned long step = (unsigned long) rule->private; 10991da177e4SLinus Torvalds return snd_interval_step(hw_param_interval(params, rule->var), 0, step); 11001da177e4SLinus Torvalds } 11011da177e4SLinus Torvalds 11021da177e4SLinus Torvalds /** 11031da177e4SLinus Torvalds * snd_pcm_hw_constraint_step 11041da177e4SLinus Torvalds */ 11051da177e4SLinus Torvalds int snd_pcm_hw_constraint_step(snd_pcm_runtime_t *runtime, 11061da177e4SLinus Torvalds unsigned int cond, 11071da177e4SLinus Torvalds snd_pcm_hw_param_t var, 11081da177e4SLinus Torvalds unsigned long step) 11091da177e4SLinus Torvalds { 11101da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 11111da177e4SLinus Torvalds snd_pcm_hw_rule_step, (void *) step, 11121da177e4SLinus Torvalds var, -1); 11131da177e4SLinus Torvalds } 11141da177e4SLinus Torvalds 11151da177e4SLinus Torvalds static int snd_pcm_hw_rule_pow2(snd_pcm_hw_params_t *params, snd_pcm_hw_rule_t *rule) 11161da177e4SLinus Torvalds { 11171da177e4SLinus Torvalds static int pow2_sizes[] = { 11181da177e4SLinus Torvalds 1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7, 11191da177e4SLinus Torvalds 1<<8, 1<<9, 1<<10, 1<<11, 1<<12, 1<<13, 1<<14, 1<<15, 11201da177e4SLinus Torvalds 1<<16, 1<<17, 1<<18, 1<<19, 1<<20, 1<<21, 1<<22, 1<<23, 11211da177e4SLinus Torvalds 1<<24, 1<<25, 1<<26, 1<<27, 1<<28, 1<<29, 1<<30 11221da177e4SLinus Torvalds }; 11231da177e4SLinus Torvalds return snd_interval_list(hw_param_interval(params, rule->var), 11241da177e4SLinus Torvalds ARRAY_SIZE(pow2_sizes), pow2_sizes, 0); 11251da177e4SLinus Torvalds } 11261da177e4SLinus Torvalds 11271da177e4SLinus Torvalds /** 11281da177e4SLinus Torvalds * snd_pcm_hw_constraint_pow2 11291da177e4SLinus Torvalds */ 11301da177e4SLinus Torvalds int snd_pcm_hw_constraint_pow2(snd_pcm_runtime_t *runtime, 11311da177e4SLinus Torvalds unsigned int cond, 11321da177e4SLinus Torvalds snd_pcm_hw_param_t var) 11331da177e4SLinus Torvalds { 11341da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 11351da177e4SLinus Torvalds snd_pcm_hw_rule_pow2, NULL, 11361da177e4SLinus Torvalds var, -1); 11371da177e4SLinus Torvalds } 11381da177e4SLinus Torvalds 11391da177e4SLinus Torvalds /* To use the same code we have in alsa-lib */ 11401da177e4SLinus Torvalds #define snd_pcm_t snd_pcm_substream_t 11411da177e4SLinus Torvalds #define assert(i) snd_assert((i), return -EINVAL) 11421da177e4SLinus Torvalds #ifndef INT_MIN 11431da177e4SLinus Torvalds #define INT_MIN ((int)((unsigned int)INT_MAX+1)) 11441da177e4SLinus Torvalds #endif 11451da177e4SLinus Torvalds 1146123992f7SAdrian Bunk static void _snd_pcm_hw_param_any(snd_pcm_hw_params_t *params, 1147123992f7SAdrian Bunk snd_pcm_hw_param_t var) 11481da177e4SLinus Torvalds { 11491da177e4SLinus Torvalds if (hw_is_mask(var)) { 11501da177e4SLinus Torvalds snd_mask_any(hw_param_mask(params, var)); 11511da177e4SLinus Torvalds params->cmask |= 1 << var; 11521da177e4SLinus Torvalds params->rmask |= 1 << var; 11531da177e4SLinus Torvalds return; 11541da177e4SLinus Torvalds } 11551da177e4SLinus Torvalds if (hw_is_interval(var)) { 11561da177e4SLinus Torvalds snd_interval_any(hw_param_interval(params, var)); 11571da177e4SLinus Torvalds params->cmask |= 1 << var; 11581da177e4SLinus Torvalds params->rmask |= 1 << var; 11591da177e4SLinus Torvalds return; 11601da177e4SLinus Torvalds } 11611da177e4SLinus Torvalds snd_BUG(); 11621da177e4SLinus Torvalds } 11631da177e4SLinus Torvalds 116462144100STakashi Iwai #if 0 11651da177e4SLinus Torvalds /** 11661da177e4SLinus Torvalds * snd_pcm_hw_param_any 11671da177e4SLinus Torvalds */ 11681da177e4SLinus Torvalds int snd_pcm_hw_param_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, 11691da177e4SLinus Torvalds snd_pcm_hw_param_t var) 11701da177e4SLinus Torvalds { 11711da177e4SLinus Torvalds _snd_pcm_hw_param_any(params, var); 11721da177e4SLinus Torvalds return snd_pcm_hw_refine(pcm, params); 11731da177e4SLinus Torvalds } 1174123992f7SAdrian Bunk #endif /* 0 */ 11751da177e4SLinus Torvalds 11761da177e4SLinus Torvalds void _snd_pcm_hw_params_any(snd_pcm_hw_params_t *params) 11771da177e4SLinus Torvalds { 11781da177e4SLinus Torvalds unsigned int k; 11791da177e4SLinus Torvalds memset(params, 0, sizeof(*params)); 11801da177e4SLinus Torvalds for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) 11811da177e4SLinus Torvalds _snd_pcm_hw_param_any(params, k); 11821da177e4SLinus Torvalds for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) 11831da177e4SLinus Torvalds _snd_pcm_hw_param_any(params, k); 11841da177e4SLinus Torvalds params->info = ~0U; 11851da177e4SLinus Torvalds } 11861da177e4SLinus Torvalds 118762144100STakashi Iwai #if 0 11881da177e4SLinus Torvalds /** 11891da177e4SLinus Torvalds * snd_pcm_hw_params_any 11901da177e4SLinus Torvalds * 11911da177e4SLinus Torvalds * Fill PARAMS with full configuration space boundaries 11921da177e4SLinus Torvalds */ 11931da177e4SLinus Torvalds int snd_pcm_hw_params_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) 11941da177e4SLinus Torvalds { 11951da177e4SLinus Torvalds _snd_pcm_hw_params_any(params); 11961da177e4SLinus Torvalds return snd_pcm_hw_refine(pcm, params); 11971da177e4SLinus Torvalds } 1198123992f7SAdrian Bunk #endif /* 0 */ 11991da177e4SLinus Torvalds 12001da177e4SLinus Torvalds /** 12011da177e4SLinus Torvalds * snd_pcm_hw_param_value 12021da177e4SLinus Torvalds * 12031da177e4SLinus Torvalds * Return the value for field PAR if it's fixed in configuration space 12041da177e4SLinus Torvalds * defined by PARAMS. Return -EINVAL otherwise 12051da177e4SLinus Torvalds */ 1206123992f7SAdrian Bunk static int snd_pcm_hw_param_value(const snd_pcm_hw_params_t *params, 12071da177e4SLinus Torvalds snd_pcm_hw_param_t var, int *dir) 12081da177e4SLinus Torvalds { 12091da177e4SLinus Torvalds if (hw_is_mask(var)) { 12101da177e4SLinus Torvalds const snd_mask_t *mask = hw_param_mask_c(params, var); 12111da177e4SLinus Torvalds if (!snd_mask_single(mask)) 12121da177e4SLinus Torvalds return -EINVAL; 12131da177e4SLinus Torvalds if (dir) 12141da177e4SLinus Torvalds *dir = 0; 12151da177e4SLinus Torvalds return snd_mask_value(mask); 12161da177e4SLinus Torvalds } 12171da177e4SLinus Torvalds if (hw_is_interval(var)) { 12181da177e4SLinus Torvalds const snd_interval_t *i = hw_param_interval_c(params, var); 12191da177e4SLinus Torvalds if (!snd_interval_single(i)) 12201da177e4SLinus Torvalds return -EINVAL; 12211da177e4SLinus Torvalds if (dir) 12221da177e4SLinus Torvalds *dir = i->openmin; 12231da177e4SLinus Torvalds return snd_interval_value(i); 12241da177e4SLinus Torvalds } 12251da177e4SLinus Torvalds assert(0); 12261da177e4SLinus Torvalds return -EINVAL; 12271da177e4SLinus Torvalds } 12281da177e4SLinus Torvalds 12291da177e4SLinus Torvalds /** 12301da177e4SLinus Torvalds * snd_pcm_hw_param_value_min 12311da177e4SLinus Torvalds * 12321da177e4SLinus Torvalds * Return the minimum value for field PAR. 12331da177e4SLinus Torvalds */ 12341da177e4SLinus Torvalds unsigned int snd_pcm_hw_param_value_min(const snd_pcm_hw_params_t *params, 12351da177e4SLinus Torvalds snd_pcm_hw_param_t var, int *dir) 12361da177e4SLinus Torvalds { 12371da177e4SLinus Torvalds if (hw_is_mask(var)) { 12381da177e4SLinus Torvalds if (dir) 12391da177e4SLinus Torvalds *dir = 0; 12401da177e4SLinus Torvalds return snd_mask_min(hw_param_mask_c(params, var)); 12411da177e4SLinus Torvalds } 12421da177e4SLinus Torvalds if (hw_is_interval(var)) { 12431da177e4SLinus Torvalds const snd_interval_t *i = hw_param_interval_c(params, var); 12441da177e4SLinus Torvalds if (dir) 12451da177e4SLinus Torvalds *dir = i->openmin; 12461da177e4SLinus Torvalds return snd_interval_min(i); 12471da177e4SLinus Torvalds } 12481da177e4SLinus Torvalds assert(0); 12491da177e4SLinus Torvalds return -EINVAL; 12501da177e4SLinus Torvalds } 12511da177e4SLinus Torvalds 12521da177e4SLinus Torvalds /** 12531da177e4SLinus Torvalds * snd_pcm_hw_param_value_max 12541da177e4SLinus Torvalds * 12551da177e4SLinus Torvalds * Return the maximum value for field PAR. 12561da177e4SLinus Torvalds */ 12571da177e4SLinus Torvalds unsigned int snd_pcm_hw_param_value_max(const snd_pcm_hw_params_t *params, 12581da177e4SLinus Torvalds snd_pcm_hw_param_t var, int *dir) 12591da177e4SLinus Torvalds { 12601da177e4SLinus Torvalds if (hw_is_mask(var)) { 12611da177e4SLinus Torvalds if (dir) 12621da177e4SLinus Torvalds *dir = 0; 12631da177e4SLinus Torvalds return snd_mask_max(hw_param_mask_c(params, var)); 12641da177e4SLinus Torvalds } 12651da177e4SLinus Torvalds if (hw_is_interval(var)) { 12661da177e4SLinus Torvalds const snd_interval_t *i = hw_param_interval_c(params, var); 12671da177e4SLinus Torvalds if (dir) 12681da177e4SLinus Torvalds *dir = - (int) i->openmax; 12691da177e4SLinus Torvalds return snd_interval_max(i); 12701da177e4SLinus Torvalds } 12711da177e4SLinus Torvalds assert(0); 12721da177e4SLinus Torvalds return -EINVAL; 12731da177e4SLinus Torvalds } 12741da177e4SLinus Torvalds 12751da177e4SLinus Torvalds void _snd_pcm_hw_param_setempty(snd_pcm_hw_params_t *params, 12761da177e4SLinus Torvalds snd_pcm_hw_param_t var) 12771da177e4SLinus Torvalds { 12781da177e4SLinus Torvalds if (hw_is_mask(var)) { 12791da177e4SLinus Torvalds snd_mask_none(hw_param_mask(params, var)); 12801da177e4SLinus Torvalds params->cmask |= 1 << var; 12811da177e4SLinus Torvalds params->rmask |= 1 << var; 12821da177e4SLinus Torvalds } else if (hw_is_interval(var)) { 12831da177e4SLinus Torvalds snd_interval_none(hw_param_interval(params, var)); 12841da177e4SLinus Torvalds params->cmask |= 1 << var; 12851da177e4SLinus Torvalds params->rmask |= 1 << var; 12861da177e4SLinus Torvalds } else { 12871da177e4SLinus Torvalds snd_BUG(); 12881da177e4SLinus Torvalds } 12891da177e4SLinus Torvalds } 12901da177e4SLinus Torvalds 12911da177e4SLinus Torvalds int _snd_pcm_hw_param_setinteger(snd_pcm_hw_params_t *params, 12921da177e4SLinus Torvalds snd_pcm_hw_param_t var) 12931da177e4SLinus Torvalds { 12941da177e4SLinus Torvalds int changed; 12951da177e4SLinus Torvalds assert(hw_is_interval(var)); 12961da177e4SLinus Torvalds changed = snd_interval_setinteger(hw_param_interval(params, var)); 12971da177e4SLinus Torvalds if (changed) { 12981da177e4SLinus Torvalds params->cmask |= 1 << var; 12991da177e4SLinus Torvalds params->rmask |= 1 << var; 13001da177e4SLinus Torvalds } 13011da177e4SLinus Torvalds return changed; 13021da177e4SLinus Torvalds } 13031da177e4SLinus Torvalds 130462144100STakashi Iwai #if 0 13051da177e4SLinus Torvalds /** 13061da177e4SLinus Torvalds * snd_pcm_hw_param_setinteger 13071da177e4SLinus Torvalds * 13081da177e4SLinus Torvalds * Inside configuration space defined by PARAMS remove from PAR all 13091da177e4SLinus Torvalds * non integer values. Reduce configuration space accordingly. 13101da177e4SLinus Torvalds * Return -EINVAL if the configuration space is empty 13111da177e4SLinus Torvalds */ 13121da177e4SLinus Torvalds int snd_pcm_hw_param_setinteger(snd_pcm_t *pcm, 13131da177e4SLinus Torvalds snd_pcm_hw_params_t *params, 13141da177e4SLinus Torvalds snd_pcm_hw_param_t var) 13151da177e4SLinus Torvalds { 13161da177e4SLinus Torvalds int changed = _snd_pcm_hw_param_setinteger(params, var); 13171da177e4SLinus Torvalds if (changed < 0) 13181da177e4SLinus Torvalds return changed; 13191da177e4SLinus Torvalds if (params->rmask) { 13201da177e4SLinus Torvalds int err = snd_pcm_hw_refine(pcm, params); 13211da177e4SLinus Torvalds if (err < 0) 13221da177e4SLinus Torvalds return err; 13231da177e4SLinus Torvalds } 13241da177e4SLinus Torvalds return 0; 13251da177e4SLinus Torvalds } 1326123992f7SAdrian Bunk #endif /* 0 */ 13271da177e4SLinus Torvalds 1328123992f7SAdrian Bunk static int _snd_pcm_hw_param_first(snd_pcm_hw_params_t *params, 13291da177e4SLinus Torvalds snd_pcm_hw_param_t var) 13301da177e4SLinus Torvalds { 13311da177e4SLinus Torvalds int changed; 13321da177e4SLinus Torvalds if (hw_is_mask(var)) 13331da177e4SLinus Torvalds changed = snd_mask_refine_first(hw_param_mask(params, var)); 13341da177e4SLinus Torvalds else if (hw_is_interval(var)) 13351da177e4SLinus Torvalds changed = snd_interval_refine_first(hw_param_interval(params, var)); 13361da177e4SLinus Torvalds else { 13371da177e4SLinus Torvalds assert(0); 13381da177e4SLinus Torvalds return -EINVAL; 13391da177e4SLinus Torvalds } 13401da177e4SLinus Torvalds if (changed) { 13411da177e4SLinus Torvalds params->cmask |= 1 << var; 13421da177e4SLinus Torvalds params->rmask |= 1 << var; 13431da177e4SLinus Torvalds } 13441da177e4SLinus Torvalds return changed; 13451da177e4SLinus Torvalds } 13461da177e4SLinus Torvalds 13471da177e4SLinus Torvalds 13481da177e4SLinus Torvalds /** 13491da177e4SLinus Torvalds * snd_pcm_hw_param_first 13501da177e4SLinus Torvalds * 13511da177e4SLinus Torvalds * Inside configuration space defined by PARAMS remove from PAR all 13521da177e4SLinus Torvalds * values > minimum. Reduce configuration space accordingly. 13531da177e4SLinus Torvalds * Return the minimum. 13541da177e4SLinus Torvalds */ 1355123992f7SAdrian Bunk static int snd_pcm_hw_param_first(snd_pcm_t *pcm, 13561da177e4SLinus Torvalds snd_pcm_hw_params_t *params, 13571da177e4SLinus Torvalds snd_pcm_hw_param_t var, int *dir) 13581da177e4SLinus Torvalds { 13591da177e4SLinus Torvalds int changed = _snd_pcm_hw_param_first(params, var); 13601da177e4SLinus Torvalds if (changed < 0) 13611da177e4SLinus Torvalds return changed; 13621da177e4SLinus Torvalds if (params->rmask) { 13631da177e4SLinus Torvalds int err = snd_pcm_hw_refine(pcm, params); 13641da177e4SLinus Torvalds assert(err >= 0); 13651da177e4SLinus Torvalds } 13661da177e4SLinus Torvalds return snd_pcm_hw_param_value(params, var, dir); 13671da177e4SLinus Torvalds } 13681da177e4SLinus Torvalds 1369123992f7SAdrian Bunk static int _snd_pcm_hw_param_last(snd_pcm_hw_params_t *params, 13701da177e4SLinus Torvalds snd_pcm_hw_param_t var) 13711da177e4SLinus Torvalds { 13721da177e4SLinus Torvalds int changed; 13731da177e4SLinus Torvalds if (hw_is_mask(var)) 13741da177e4SLinus Torvalds changed = snd_mask_refine_last(hw_param_mask(params, var)); 13751da177e4SLinus Torvalds else if (hw_is_interval(var)) 13761da177e4SLinus Torvalds changed = snd_interval_refine_last(hw_param_interval(params, var)); 13771da177e4SLinus Torvalds else { 13781da177e4SLinus Torvalds assert(0); 13791da177e4SLinus Torvalds return -EINVAL; 13801da177e4SLinus Torvalds } 13811da177e4SLinus Torvalds if (changed) { 13821da177e4SLinus Torvalds params->cmask |= 1 << var; 13831da177e4SLinus Torvalds params->rmask |= 1 << var; 13841da177e4SLinus Torvalds } 13851da177e4SLinus Torvalds return changed; 13861da177e4SLinus Torvalds } 13871da177e4SLinus Torvalds 13881da177e4SLinus Torvalds 13891da177e4SLinus Torvalds /** 13901da177e4SLinus Torvalds * snd_pcm_hw_param_last 13911da177e4SLinus Torvalds * 13921da177e4SLinus Torvalds * Inside configuration space defined by PARAMS remove from PAR all 13931da177e4SLinus Torvalds * values < maximum. Reduce configuration space accordingly. 13941da177e4SLinus Torvalds * Return the maximum. 13951da177e4SLinus Torvalds */ 1396123992f7SAdrian Bunk static int snd_pcm_hw_param_last(snd_pcm_t *pcm, 13971da177e4SLinus Torvalds snd_pcm_hw_params_t *params, 13981da177e4SLinus Torvalds snd_pcm_hw_param_t var, int *dir) 13991da177e4SLinus Torvalds { 14001da177e4SLinus Torvalds int changed = _snd_pcm_hw_param_last(params, var); 14011da177e4SLinus Torvalds if (changed < 0) 14021da177e4SLinus Torvalds return changed; 14031da177e4SLinus Torvalds if (params->rmask) { 14041da177e4SLinus Torvalds int err = snd_pcm_hw_refine(pcm, params); 14051da177e4SLinus Torvalds assert(err >= 0); 14061da177e4SLinus Torvalds } 14071da177e4SLinus Torvalds return snd_pcm_hw_param_value(params, var, dir); 14081da177e4SLinus Torvalds } 14091da177e4SLinus Torvalds 14101da177e4SLinus Torvalds int _snd_pcm_hw_param_min(snd_pcm_hw_params_t *params, 14111da177e4SLinus Torvalds snd_pcm_hw_param_t var, unsigned int val, int dir) 14121da177e4SLinus Torvalds { 14131da177e4SLinus Torvalds int changed; 14141da177e4SLinus Torvalds int open = 0; 14151da177e4SLinus Torvalds if (dir) { 14161da177e4SLinus Torvalds if (dir > 0) { 14171da177e4SLinus Torvalds open = 1; 14181da177e4SLinus Torvalds } else if (dir < 0) { 14191da177e4SLinus Torvalds if (val > 0) { 14201da177e4SLinus Torvalds open = 1; 14211da177e4SLinus Torvalds val--; 14221da177e4SLinus Torvalds } 14231da177e4SLinus Torvalds } 14241da177e4SLinus Torvalds } 14251da177e4SLinus Torvalds if (hw_is_mask(var)) 14261da177e4SLinus Torvalds changed = snd_mask_refine_min(hw_param_mask(params, var), val + !!open); 14271da177e4SLinus Torvalds else if (hw_is_interval(var)) 14281da177e4SLinus Torvalds changed = snd_interval_refine_min(hw_param_interval(params, var), val, open); 14291da177e4SLinus Torvalds else { 14301da177e4SLinus Torvalds assert(0); 14311da177e4SLinus Torvalds return -EINVAL; 14321da177e4SLinus Torvalds } 14331da177e4SLinus Torvalds if (changed) { 14341da177e4SLinus Torvalds params->cmask |= 1 << var; 14351da177e4SLinus Torvalds params->rmask |= 1 << var; 14361da177e4SLinus Torvalds } 14371da177e4SLinus Torvalds return changed; 14381da177e4SLinus Torvalds } 14391da177e4SLinus Torvalds 14401da177e4SLinus Torvalds /** 14411da177e4SLinus Torvalds * snd_pcm_hw_param_min 14421da177e4SLinus Torvalds * 14431da177e4SLinus Torvalds * Inside configuration space defined by PARAMS remove from PAR all 14441da177e4SLinus Torvalds * values < VAL. Reduce configuration space accordingly. 14451da177e4SLinus Torvalds * Return new minimum or -EINVAL if the configuration space is empty 14461da177e4SLinus Torvalds */ 1447123992f7SAdrian Bunk static int snd_pcm_hw_param_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, 1448123992f7SAdrian Bunk snd_pcm_hw_param_t var, unsigned int val, 1449123992f7SAdrian Bunk int *dir) 14501da177e4SLinus Torvalds { 14511da177e4SLinus Torvalds int changed = _snd_pcm_hw_param_min(params, var, val, dir ? *dir : 0); 14521da177e4SLinus Torvalds if (changed < 0) 14531da177e4SLinus Torvalds return changed; 14541da177e4SLinus Torvalds if (params->rmask) { 14551da177e4SLinus Torvalds int err = snd_pcm_hw_refine(pcm, params); 14561da177e4SLinus Torvalds if (err < 0) 14571da177e4SLinus Torvalds return err; 14581da177e4SLinus Torvalds } 14591da177e4SLinus Torvalds return snd_pcm_hw_param_value_min(params, var, dir); 14601da177e4SLinus Torvalds } 14611da177e4SLinus Torvalds 1462123992f7SAdrian Bunk static int _snd_pcm_hw_param_max(snd_pcm_hw_params_t *params, 1463123992f7SAdrian Bunk snd_pcm_hw_param_t var, unsigned int val, 1464123992f7SAdrian Bunk int dir) 14651da177e4SLinus Torvalds { 14661da177e4SLinus Torvalds int changed; 14671da177e4SLinus Torvalds int open = 0; 14681da177e4SLinus Torvalds if (dir) { 14691da177e4SLinus Torvalds if (dir < 0) { 14701da177e4SLinus Torvalds open = 1; 14711da177e4SLinus Torvalds } else if (dir > 0) { 14721da177e4SLinus Torvalds open = 1; 14731da177e4SLinus Torvalds val++; 14741da177e4SLinus Torvalds } 14751da177e4SLinus Torvalds } 14761da177e4SLinus Torvalds if (hw_is_mask(var)) { 14771da177e4SLinus Torvalds if (val == 0 && open) { 14781da177e4SLinus Torvalds snd_mask_none(hw_param_mask(params, var)); 14791da177e4SLinus Torvalds changed = -EINVAL; 14801da177e4SLinus Torvalds } else 14811da177e4SLinus Torvalds changed = snd_mask_refine_max(hw_param_mask(params, var), val - !!open); 14821da177e4SLinus Torvalds } else if (hw_is_interval(var)) 14831da177e4SLinus Torvalds changed = snd_interval_refine_max(hw_param_interval(params, var), val, open); 14841da177e4SLinus Torvalds else { 14851da177e4SLinus Torvalds assert(0); 14861da177e4SLinus Torvalds return -EINVAL; 14871da177e4SLinus Torvalds } 14881da177e4SLinus Torvalds if (changed) { 14891da177e4SLinus Torvalds params->cmask |= 1 << var; 14901da177e4SLinus Torvalds params->rmask |= 1 << var; 14911da177e4SLinus Torvalds } 14921da177e4SLinus Torvalds return changed; 14931da177e4SLinus Torvalds } 14941da177e4SLinus Torvalds 14951da177e4SLinus Torvalds /** 14961da177e4SLinus Torvalds * snd_pcm_hw_param_max 14971da177e4SLinus Torvalds * 14981da177e4SLinus Torvalds * Inside configuration space defined by PARAMS remove from PAR all 14991da177e4SLinus Torvalds * values >= VAL + 1. Reduce configuration space accordingly. 15001da177e4SLinus Torvalds * Return new maximum or -EINVAL if the configuration space is empty 15011da177e4SLinus Torvalds */ 1502123992f7SAdrian Bunk static int snd_pcm_hw_param_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, 1503123992f7SAdrian Bunk snd_pcm_hw_param_t var, unsigned int val, 1504123992f7SAdrian Bunk int *dir) 15051da177e4SLinus Torvalds { 15061da177e4SLinus Torvalds int changed = _snd_pcm_hw_param_max(params, var, val, dir ? *dir : 0); 15071da177e4SLinus Torvalds if (changed < 0) 15081da177e4SLinus Torvalds return changed; 15091da177e4SLinus Torvalds if (params->rmask) { 15101da177e4SLinus Torvalds int err = snd_pcm_hw_refine(pcm, params); 15111da177e4SLinus Torvalds if (err < 0) 15121da177e4SLinus Torvalds return err; 15131da177e4SLinus Torvalds } 15141da177e4SLinus Torvalds return snd_pcm_hw_param_value_max(params, var, dir); 15151da177e4SLinus Torvalds } 15161da177e4SLinus Torvalds 15171da177e4SLinus Torvalds int _snd_pcm_hw_param_set(snd_pcm_hw_params_t *params, 15181da177e4SLinus Torvalds snd_pcm_hw_param_t var, unsigned int val, int dir) 15191da177e4SLinus Torvalds { 15201da177e4SLinus Torvalds int changed; 15211da177e4SLinus Torvalds if (hw_is_mask(var)) { 15221da177e4SLinus Torvalds snd_mask_t *m = hw_param_mask(params, var); 15231da177e4SLinus Torvalds if (val == 0 && dir < 0) { 15241da177e4SLinus Torvalds changed = -EINVAL; 15251da177e4SLinus Torvalds snd_mask_none(m); 15261da177e4SLinus Torvalds } else { 15271da177e4SLinus Torvalds if (dir > 0) 15281da177e4SLinus Torvalds val++; 15291da177e4SLinus Torvalds else if (dir < 0) 15301da177e4SLinus Torvalds val--; 15311da177e4SLinus Torvalds changed = snd_mask_refine_set(hw_param_mask(params, var), val); 15321da177e4SLinus Torvalds } 15331da177e4SLinus Torvalds } else if (hw_is_interval(var)) { 15341da177e4SLinus Torvalds snd_interval_t *i = hw_param_interval(params, var); 15351da177e4SLinus Torvalds if (val == 0 && dir < 0) { 15361da177e4SLinus Torvalds changed = -EINVAL; 15371da177e4SLinus Torvalds snd_interval_none(i); 15381da177e4SLinus Torvalds } else if (dir == 0) 15391da177e4SLinus Torvalds changed = snd_interval_refine_set(i, val); 15401da177e4SLinus Torvalds else { 15411da177e4SLinus Torvalds snd_interval_t t; 15421da177e4SLinus Torvalds t.openmin = 1; 15431da177e4SLinus Torvalds t.openmax = 1; 15441da177e4SLinus Torvalds t.empty = 0; 15451da177e4SLinus Torvalds t.integer = 0; 15461da177e4SLinus Torvalds if (dir < 0) { 15471da177e4SLinus Torvalds t.min = val - 1; 15481da177e4SLinus Torvalds t.max = val; 15491da177e4SLinus Torvalds } else { 15501da177e4SLinus Torvalds t.min = val; 15511da177e4SLinus Torvalds t.max = val+1; 15521da177e4SLinus Torvalds } 15531da177e4SLinus Torvalds changed = snd_interval_refine(i, &t); 15541da177e4SLinus Torvalds } 15551da177e4SLinus Torvalds } else { 15561da177e4SLinus Torvalds assert(0); 15571da177e4SLinus Torvalds return -EINVAL; 15581da177e4SLinus Torvalds } 15591da177e4SLinus Torvalds if (changed) { 15601da177e4SLinus Torvalds params->cmask |= 1 << var; 15611da177e4SLinus Torvalds params->rmask |= 1 << var; 15621da177e4SLinus Torvalds } 15631da177e4SLinus Torvalds return changed; 15641da177e4SLinus Torvalds } 15651da177e4SLinus Torvalds 15661da177e4SLinus Torvalds /** 15671da177e4SLinus Torvalds * snd_pcm_hw_param_set 15681da177e4SLinus Torvalds * 15691da177e4SLinus Torvalds * Inside configuration space defined by PARAMS remove from PAR all 15701da177e4SLinus Torvalds * values != VAL. Reduce configuration space accordingly. 15711da177e4SLinus Torvalds * Return VAL or -EINVAL if the configuration space is empty 15721da177e4SLinus Torvalds */ 15731da177e4SLinus Torvalds int snd_pcm_hw_param_set(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, 15741da177e4SLinus Torvalds snd_pcm_hw_param_t var, unsigned int val, int dir) 15751da177e4SLinus Torvalds { 15761da177e4SLinus Torvalds int changed = _snd_pcm_hw_param_set(params, var, val, dir); 15771da177e4SLinus Torvalds if (changed < 0) 15781da177e4SLinus Torvalds return changed; 15791da177e4SLinus Torvalds if (params->rmask) { 15801da177e4SLinus Torvalds int err = snd_pcm_hw_refine(pcm, params); 15811da177e4SLinus Torvalds if (err < 0) 15821da177e4SLinus Torvalds return err; 15831da177e4SLinus Torvalds } 15841da177e4SLinus Torvalds return snd_pcm_hw_param_value(params, var, NULL); 15851da177e4SLinus Torvalds } 15861da177e4SLinus Torvalds 15871da177e4SLinus Torvalds int _snd_pcm_hw_param_mask(snd_pcm_hw_params_t *params, 15881da177e4SLinus Torvalds snd_pcm_hw_param_t var, const snd_mask_t *val) 15891da177e4SLinus Torvalds { 15901da177e4SLinus Torvalds int changed; 15911da177e4SLinus Torvalds assert(hw_is_mask(var)); 15921da177e4SLinus Torvalds changed = snd_mask_refine(hw_param_mask(params, var), val); 15931da177e4SLinus Torvalds if (changed) { 15941da177e4SLinus Torvalds params->cmask |= 1 << var; 15951da177e4SLinus Torvalds params->rmask |= 1 << var; 15961da177e4SLinus Torvalds } 15971da177e4SLinus Torvalds return changed; 15981da177e4SLinus Torvalds } 15991da177e4SLinus Torvalds 16001da177e4SLinus Torvalds /** 16011da177e4SLinus Torvalds * snd_pcm_hw_param_mask 16021da177e4SLinus Torvalds * 16031da177e4SLinus Torvalds * Inside configuration space defined by PARAMS remove from PAR all values 16041da177e4SLinus Torvalds * not contained in MASK. Reduce configuration space accordingly. 16051da177e4SLinus Torvalds * This function can be called only for SNDRV_PCM_HW_PARAM_ACCESS, 16061da177e4SLinus Torvalds * SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT. 16071da177e4SLinus Torvalds * Return 0 on success or -EINVAL 16081da177e4SLinus Torvalds * if the configuration space is empty 16091da177e4SLinus Torvalds */ 16101da177e4SLinus Torvalds int snd_pcm_hw_param_mask(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, 16111da177e4SLinus Torvalds snd_pcm_hw_param_t var, const snd_mask_t *val) 16121da177e4SLinus Torvalds { 16131da177e4SLinus Torvalds int changed = _snd_pcm_hw_param_mask(params, var, val); 16141da177e4SLinus Torvalds if (changed < 0) 16151da177e4SLinus Torvalds return changed; 16161da177e4SLinus Torvalds if (params->rmask) { 16171da177e4SLinus Torvalds int err = snd_pcm_hw_refine(pcm, params); 16181da177e4SLinus Torvalds if (err < 0) 16191da177e4SLinus Torvalds return err; 16201da177e4SLinus Torvalds } 16211da177e4SLinus Torvalds return 0; 16221da177e4SLinus Torvalds } 16231da177e4SLinus Torvalds 16241da177e4SLinus Torvalds static int boundary_sub(int a, int adir, 16251da177e4SLinus Torvalds int b, int bdir, 16261da177e4SLinus Torvalds int *c, int *cdir) 16271da177e4SLinus Torvalds { 16281da177e4SLinus Torvalds adir = adir < 0 ? -1 : (adir > 0 ? 1 : 0); 16291da177e4SLinus Torvalds bdir = bdir < 0 ? -1 : (bdir > 0 ? 1 : 0); 16301da177e4SLinus Torvalds *c = a - b; 16311da177e4SLinus Torvalds *cdir = adir - bdir; 16321da177e4SLinus Torvalds if (*cdir == -2) { 16331da177e4SLinus Torvalds assert(*c > INT_MIN); 16341da177e4SLinus Torvalds (*c)--; 16351da177e4SLinus Torvalds } else if (*cdir == 2) { 16361da177e4SLinus Torvalds assert(*c < INT_MAX); 16371da177e4SLinus Torvalds (*c)++; 16381da177e4SLinus Torvalds } 16391da177e4SLinus Torvalds return 0; 16401da177e4SLinus Torvalds } 16411da177e4SLinus Torvalds 16421da177e4SLinus Torvalds static int boundary_lt(unsigned int a, int adir, 16431da177e4SLinus Torvalds unsigned int b, int bdir) 16441da177e4SLinus Torvalds { 16451da177e4SLinus Torvalds assert(a > 0 || adir >= 0); 16461da177e4SLinus Torvalds assert(b > 0 || bdir >= 0); 16471da177e4SLinus Torvalds if (adir < 0) { 16481da177e4SLinus Torvalds a--; 16491da177e4SLinus Torvalds adir = 1; 16501da177e4SLinus Torvalds } else if (adir > 0) 16511da177e4SLinus Torvalds adir = 1; 16521da177e4SLinus Torvalds if (bdir < 0) { 16531da177e4SLinus Torvalds b--; 16541da177e4SLinus Torvalds bdir = 1; 16551da177e4SLinus Torvalds } else if (bdir > 0) 16561da177e4SLinus Torvalds bdir = 1; 16571da177e4SLinus Torvalds return a < b || (a == b && adir < bdir); 16581da177e4SLinus Torvalds } 16591da177e4SLinus Torvalds 16601da177e4SLinus Torvalds /* Return 1 if min is nearer to best than max */ 16611da177e4SLinus Torvalds static int boundary_nearer(int min, int mindir, 16621da177e4SLinus Torvalds int best, int bestdir, 16631da177e4SLinus Torvalds int max, int maxdir) 16641da177e4SLinus Torvalds { 16651da177e4SLinus Torvalds int dmin, dmindir; 16661da177e4SLinus Torvalds int dmax, dmaxdir; 16671da177e4SLinus Torvalds boundary_sub(best, bestdir, min, mindir, &dmin, &dmindir); 16681da177e4SLinus Torvalds boundary_sub(max, maxdir, best, bestdir, &dmax, &dmaxdir); 16691da177e4SLinus Torvalds return boundary_lt(dmin, dmindir, dmax, dmaxdir); 16701da177e4SLinus Torvalds } 16711da177e4SLinus Torvalds 16721da177e4SLinus Torvalds /** 16731da177e4SLinus Torvalds * snd_pcm_hw_param_near 16741da177e4SLinus Torvalds * 16751da177e4SLinus Torvalds * Inside configuration space defined by PARAMS set PAR to the available value 16761da177e4SLinus Torvalds * nearest to VAL. Reduce configuration space accordingly. 16771da177e4SLinus Torvalds * This function cannot be called for SNDRV_PCM_HW_PARAM_ACCESS, 16781da177e4SLinus Torvalds * SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT. 16791da177e4SLinus Torvalds * Return the value found. 16801da177e4SLinus Torvalds */ 16811da177e4SLinus Torvalds int snd_pcm_hw_param_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, 16821da177e4SLinus Torvalds snd_pcm_hw_param_t var, unsigned int best, int *dir) 16831da177e4SLinus Torvalds { 16841da177e4SLinus Torvalds snd_pcm_hw_params_t *save = NULL; 16851da177e4SLinus Torvalds int v; 16861da177e4SLinus Torvalds unsigned int saved_min; 16871da177e4SLinus Torvalds int last = 0; 16881da177e4SLinus Torvalds int min, max; 16891da177e4SLinus Torvalds int mindir, maxdir; 16901da177e4SLinus Torvalds int valdir = dir ? *dir : 0; 16911da177e4SLinus Torvalds /* FIXME */ 16921da177e4SLinus Torvalds if (best > INT_MAX) 16931da177e4SLinus Torvalds best = INT_MAX; 16941da177e4SLinus Torvalds min = max = best; 16951da177e4SLinus Torvalds mindir = maxdir = valdir; 16961da177e4SLinus Torvalds if (maxdir > 0) 16971da177e4SLinus Torvalds maxdir = 0; 16981da177e4SLinus Torvalds else if (maxdir == 0) 16991da177e4SLinus Torvalds maxdir = -1; 17001da177e4SLinus Torvalds else { 17011da177e4SLinus Torvalds maxdir = 1; 17021da177e4SLinus Torvalds max--; 17031da177e4SLinus Torvalds } 17041da177e4SLinus Torvalds save = kmalloc(sizeof(*save), GFP_KERNEL); 17051da177e4SLinus Torvalds if (save == NULL) 17061da177e4SLinus Torvalds return -ENOMEM; 17071da177e4SLinus Torvalds *save = *params; 17081da177e4SLinus Torvalds saved_min = min; 17091da177e4SLinus Torvalds min = snd_pcm_hw_param_min(pcm, params, var, min, &mindir); 17101da177e4SLinus Torvalds if (min >= 0) { 17111da177e4SLinus Torvalds snd_pcm_hw_params_t *params1; 17121da177e4SLinus Torvalds if (max < 0) 17131da177e4SLinus Torvalds goto _end; 17141da177e4SLinus Torvalds if ((unsigned int)min == saved_min && mindir == valdir) 17151da177e4SLinus Torvalds goto _end; 17161da177e4SLinus Torvalds params1 = kmalloc(sizeof(*params1), GFP_KERNEL); 17171da177e4SLinus Torvalds if (params1 == NULL) { 17181da177e4SLinus Torvalds kfree(save); 17191da177e4SLinus Torvalds return -ENOMEM; 17201da177e4SLinus Torvalds } 17211da177e4SLinus Torvalds *params1 = *save; 17221da177e4SLinus Torvalds max = snd_pcm_hw_param_max(pcm, params1, var, max, &maxdir); 17231da177e4SLinus Torvalds if (max < 0) { 17241da177e4SLinus Torvalds kfree(params1); 17251da177e4SLinus Torvalds goto _end; 17261da177e4SLinus Torvalds } 17271da177e4SLinus Torvalds if (boundary_nearer(max, maxdir, best, valdir, min, mindir)) { 17281da177e4SLinus Torvalds *params = *params1; 17291da177e4SLinus Torvalds last = 1; 17301da177e4SLinus Torvalds } 17311da177e4SLinus Torvalds kfree(params1); 17321da177e4SLinus Torvalds } else { 17331da177e4SLinus Torvalds *params = *save; 17341da177e4SLinus Torvalds max = snd_pcm_hw_param_max(pcm, params, var, max, &maxdir); 17351da177e4SLinus Torvalds assert(max >= 0); 17361da177e4SLinus Torvalds last = 1; 17371da177e4SLinus Torvalds } 17381da177e4SLinus Torvalds _end: 17391da177e4SLinus Torvalds kfree(save); 17401da177e4SLinus Torvalds if (last) 17411da177e4SLinus Torvalds v = snd_pcm_hw_param_last(pcm, params, var, dir); 17421da177e4SLinus Torvalds else 17431da177e4SLinus Torvalds v = snd_pcm_hw_param_first(pcm, params, var, dir); 17441da177e4SLinus Torvalds assert(v >= 0); 17451da177e4SLinus Torvalds return v; 17461da177e4SLinus Torvalds } 17471da177e4SLinus Torvalds 17481da177e4SLinus Torvalds /** 17491da177e4SLinus Torvalds * snd_pcm_hw_param_choose 17501da177e4SLinus Torvalds * 17511da177e4SLinus Torvalds * Choose one configuration from configuration space defined by PARAMS 17521da177e4SLinus Torvalds * The configuration chosen is that obtained fixing in this order: 17531da177e4SLinus Torvalds * first access, first format, first subformat, min channels, 17541da177e4SLinus Torvalds * min rate, min period time, max buffer size, min tick time 17551da177e4SLinus Torvalds */ 17561da177e4SLinus Torvalds int snd_pcm_hw_params_choose(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) 17571da177e4SLinus Torvalds { 17581da177e4SLinus Torvalds int err; 17591da177e4SLinus Torvalds 17601da177e4SLinus Torvalds err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_ACCESS, NULL); 17611da177e4SLinus Torvalds assert(err >= 0); 17621da177e4SLinus Torvalds 17631da177e4SLinus Torvalds err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_FORMAT, NULL); 17641da177e4SLinus Torvalds assert(err >= 0); 17651da177e4SLinus Torvalds 17661da177e4SLinus Torvalds err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_SUBFORMAT, NULL); 17671da177e4SLinus Torvalds assert(err >= 0); 17681da177e4SLinus Torvalds 17691da177e4SLinus Torvalds err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_CHANNELS, NULL); 17701da177e4SLinus Torvalds assert(err >= 0); 17711da177e4SLinus Torvalds 17721da177e4SLinus Torvalds err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_RATE, NULL); 17731da177e4SLinus Torvalds assert(err >= 0); 17741da177e4SLinus Torvalds 17751da177e4SLinus Torvalds err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_PERIOD_TIME, NULL); 17761da177e4SLinus Torvalds assert(err >= 0); 17771da177e4SLinus Torvalds 17781da177e4SLinus Torvalds err = snd_pcm_hw_param_last(pcm, params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, NULL); 17791da177e4SLinus Torvalds assert(err >= 0); 17801da177e4SLinus Torvalds 17811da177e4SLinus Torvalds err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_TICK_TIME, NULL); 17821da177e4SLinus Torvalds assert(err >= 0); 17831da177e4SLinus Torvalds 17841da177e4SLinus Torvalds return 0; 17851da177e4SLinus Torvalds } 17861da177e4SLinus Torvalds 17871da177e4SLinus Torvalds #undef snd_pcm_t 17881da177e4SLinus Torvalds #undef assert 17891da177e4SLinus Torvalds 17901da177e4SLinus Torvalds static int snd_pcm_lib_ioctl_reset(snd_pcm_substream_t *substream, 17911da177e4SLinus Torvalds void *arg) 17921da177e4SLinus Torvalds { 17931da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 17941da177e4SLinus Torvalds unsigned long flags; 17951da177e4SLinus Torvalds snd_pcm_stream_lock_irqsave(substream, flags); 17961da177e4SLinus Torvalds if (snd_pcm_running(substream) && 17971da177e4SLinus Torvalds snd_pcm_update_hw_ptr(substream) >= 0) 17981da177e4SLinus Torvalds runtime->status->hw_ptr %= runtime->buffer_size; 17991da177e4SLinus Torvalds else 18001da177e4SLinus Torvalds runtime->status->hw_ptr = 0; 18011da177e4SLinus Torvalds snd_pcm_stream_unlock_irqrestore(substream, flags); 18021da177e4SLinus Torvalds return 0; 18031da177e4SLinus Torvalds } 18041da177e4SLinus Torvalds 18051da177e4SLinus Torvalds static int snd_pcm_lib_ioctl_channel_info(snd_pcm_substream_t *substream, 18061da177e4SLinus Torvalds void *arg) 18071da177e4SLinus Torvalds { 18081da177e4SLinus Torvalds snd_pcm_channel_info_t *info = arg; 18091da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 18101da177e4SLinus Torvalds int width; 18111da177e4SLinus Torvalds if (!(runtime->info & SNDRV_PCM_INFO_MMAP)) { 18121da177e4SLinus Torvalds info->offset = -1; 18131da177e4SLinus Torvalds return 0; 18141da177e4SLinus Torvalds } 18151da177e4SLinus Torvalds width = snd_pcm_format_physical_width(runtime->format); 18161da177e4SLinus Torvalds if (width < 0) 18171da177e4SLinus Torvalds return width; 18181da177e4SLinus Torvalds info->offset = 0; 18191da177e4SLinus Torvalds switch (runtime->access) { 18201da177e4SLinus Torvalds case SNDRV_PCM_ACCESS_MMAP_INTERLEAVED: 18211da177e4SLinus Torvalds case SNDRV_PCM_ACCESS_RW_INTERLEAVED: 18221da177e4SLinus Torvalds info->first = info->channel * width; 18231da177e4SLinus Torvalds info->step = runtime->channels * width; 18241da177e4SLinus Torvalds break; 18251da177e4SLinus Torvalds case SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED: 18261da177e4SLinus Torvalds case SNDRV_PCM_ACCESS_RW_NONINTERLEAVED: 18271da177e4SLinus Torvalds { 18281da177e4SLinus Torvalds size_t size = runtime->dma_bytes / runtime->channels; 18291da177e4SLinus Torvalds info->first = info->channel * size * 8; 18301da177e4SLinus Torvalds info->step = width; 18311da177e4SLinus Torvalds break; 18321da177e4SLinus Torvalds } 18331da177e4SLinus Torvalds default: 18341da177e4SLinus Torvalds snd_BUG(); 18351da177e4SLinus Torvalds break; 18361da177e4SLinus Torvalds } 18371da177e4SLinus Torvalds return 0; 18381da177e4SLinus Torvalds } 18391da177e4SLinus Torvalds 18401da177e4SLinus Torvalds /** 18411da177e4SLinus Torvalds * snd_pcm_lib_ioctl - a generic PCM ioctl callback 18421da177e4SLinus Torvalds * @substream: the pcm substream instance 18431da177e4SLinus Torvalds * @cmd: ioctl command 18441da177e4SLinus Torvalds * @arg: ioctl argument 18451da177e4SLinus Torvalds * 18461da177e4SLinus Torvalds * Processes the generic ioctl commands for PCM. 18471da177e4SLinus Torvalds * Can be passed as the ioctl callback for PCM ops. 18481da177e4SLinus Torvalds * 18491da177e4SLinus Torvalds * Returns zero if successful, or a negative error code on failure. 18501da177e4SLinus Torvalds */ 18511da177e4SLinus Torvalds int snd_pcm_lib_ioctl(snd_pcm_substream_t *substream, 18521da177e4SLinus Torvalds unsigned int cmd, void *arg) 18531da177e4SLinus Torvalds { 18541da177e4SLinus Torvalds switch (cmd) { 18551da177e4SLinus Torvalds case SNDRV_PCM_IOCTL1_INFO: 18561da177e4SLinus Torvalds return 0; 18571da177e4SLinus Torvalds case SNDRV_PCM_IOCTL1_RESET: 18581da177e4SLinus Torvalds return snd_pcm_lib_ioctl_reset(substream, arg); 18591da177e4SLinus Torvalds case SNDRV_PCM_IOCTL1_CHANNEL_INFO: 18601da177e4SLinus Torvalds return snd_pcm_lib_ioctl_channel_info(substream, arg); 18611da177e4SLinus Torvalds } 18621da177e4SLinus Torvalds return -ENXIO; 18631da177e4SLinus Torvalds } 18641da177e4SLinus Torvalds 18651da177e4SLinus Torvalds /* 18661da177e4SLinus Torvalds * Conditions 18671da177e4SLinus Torvalds */ 18681da177e4SLinus Torvalds 18691da177e4SLinus Torvalds static void snd_pcm_system_tick_set(snd_pcm_substream_t *substream, 18701da177e4SLinus Torvalds unsigned long ticks) 18711da177e4SLinus Torvalds { 18721da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 18731da177e4SLinus Torvalds if (ticks == 0) 18741da177e4SLinus Torvalds del_timer(&runtime->tick_timer); 18751da177e4SLinus Torvalds else { 18761da177e4SLinus Torvalds ticks += (1000000 / HZ) - 1; 18771da177e4SLinus Torvalds ticks /= (1000000 / HZ); 18781da177e4SLinus Torvalds mod_timer(&runtime->tick_timer, jiffies + ticks); 18791da177e4SLinus Torvalds } 18801da177e4SLinus Torvalds } 18811da177e4SLinus Torvalds 18821da177e4SLinus Torvalds /* Temporary alias */ 18831da177e4SLinus Torvalds void snd_pcm_tick_set(snd_pcm_substream_t *substream, unsigned long ticks) 18841da177e4SLinus Torvalds { 18851da177e4SLinus Torvalds snd_pcm_system_tick_set(substream, ticks); 18861da177e4SLinus Torvalds } 18871da177e4SLinus Torvalds 18881da177e4SLinus Torvalds void snd_pcm_tick_prepare(snd_pcm_substream_t *substream) 18891da177e4SLinus Torvalds { 18901da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 18911da177e4SLinus Torvalds snd_pcm_uframes_t frames = ULONG_MAX; 18921da177e4SLinus Torvalds snd_pcm_uframes_t avail, dist; 18931da177e4SLinus Torvalds unsigned int ticks; 18941da177e4SLinus Torvalds u_int64_t n; 18951da177e4SLinus Torvalds u_int32_t r; 18961da177e4SLinus Torvalds if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 18971da177e4SLinus Torvalds if (runtime->silence_size >= runtime->boundary) { 18981da177e4SLinus Torvalds frames = 1; 18991da177e4SLinus Torvalds } else if (runtime->silence_size > 0 && 19001da177e4SLinus Torvalds runtime->silence_filled < runtime->buffer_size) { 19011da177e4SLinus Torvalds snd_pcm_sframes_t noise_dist; 19021da177e4SLinus Torvalds noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silence_filled; 19031da177e4SLinus Torvalds snd_assert(noise_dist <= (snd_pcm_sframes_t)runtime->silence_threshold, ); 19041da177e4SLinus Torvalds frames = noise_dist - runtime->silence_threshold; 19051da177e4SLinus Torvalds } 19061da177e4SLinus Torvalds avail = snd_pcm_playback_avail(runtime); 19071da177e4SLinus Torvalds } else { 19081da177e4SLinus Torvalds avail = snd_pcm_capture_avail(runtime); 19091da177e4SLinus Torvalds } 19101da177e4SLinus Torvalds if (avail < runtime->control->avail_min) { 19111da177e4SLinus Torvalds snd_pcm_sframes_t n = runtime->control->avail_min - avail; 19121da177e4SLinus Torvalds if (n > 0 && frames > (snd_pcm_uframes_t)n) 19131da177e4SLinus Torvalds frames = n; 19141da177e4SLinus Torvalds } 19151da177e4SLinus Torvalds if (avail < runtime->buffer_size) { 19161da177e4SLinus Torvalds snd_pcm_sframes_t n = runtime->buffer_size - avail; 19171da177e4SLinus Torvalds if (n > 0 && frames > (snd_pcm_uframes_t)n) 19181da177e4SLinus Torvalds frames = n; 19191da177e4SLinus Torvalds } 19201da177e4SLinus Torvalds if (frames == ULONG_MAX) { 19211da177e4SLinus Torvalds snd_pcm_tick_set(substream, 0); 19221da177e4SLinus Torvalds return; 19231da177e4SLinus Torvalds } 19241da177e4SLinus Torvalds dist = runtime->status->hw_ptr - runtime->hw_ptr_base; 19251da177e4SLinus Torvalds /* Distance to next interrupt */ 19261da177e4SLinus Torvalds dist = runtime->period_size - dist % runtime->period_size; 19271da177e4SLinus Torvalds if (dist <= frames) { 19281da177e4SLinus Torvalds snd_pcm_tick_set(substream, 0); 19291da177e4SLinus Torvalds return; 19301da177e4SLinus Torvalds } 19311da177e4SLinus Torvalds /* the base time is us */ 19321da177e4SLinus Torvalds n = frames; 19331da177e4SLinus Torvalds n *= 1000000; 19341da177e4SLinus Torvalds div64_32(&n, runtime->tick_time * runtime->rate, &r); 19351da177e4SLinus Torvalds ticks = n + (r > 0 ? 1 : 0); 19361da177e4SLinus Torvalds if (ticks < runtime->sleep_min) 19371da177e4SLinus Torvalds ticks = runtime->sleep_min; 19381da177e4SLinus Torvalds snd_pcm_tick_set(substream, (unsigned long) ticks); 19391da177e4SLinus Torvalds } 19401da177e4SLinus Torvalds 19411da177e4SLinus Torvalds void snd_pcm_tick_elapsed(snd_pcm_substream_t *substream) 19421da177e4SLinus Torvalds { 19431da177e4SLinus Torvalds snd_pcm_runtime_t *runtime; 19441da177e4SLinus Torvalds unsigned long flags; 19451da177e4SLinus Torvalds 19461da177e4SLinus Torvalds snd_assert(substream != NULL, return); 19471da177e4SLinus Torvalds runtime = substream->runtime; 19481da177e4SLinus Torvalds snd_assert(runtime != NULL, return); 19491da177e4SLinus Torvalds 19501da177e4SLinus Torvalds snd_pcm_stream_lock_irqsave(substream, flags); 19511da177e4SLinus Torvalds if (!snd_pcm_running(substream) || 19521da177e4SLinus Torvalds snd_pcm_update_hw_ptr(substream) < 0) 19531da177e4SLinus Torvalds goto _end; 19541da177e4SLinus Torvalds if (runtime->sleep_min) 19551da177e4SLinus Torvalds snd_pcm_tick_prepare(substream); 19561da177e4SLinus Torvalds _end: 19571da177e4SLinus Torvalds snd_pcm_stream_unlock_irqrestore(substream, flags); 19581da177e4SLinus Torvalds } 19591da177e4SLinus Torvalds 19601da177e4SLinus Torvalds /** 19611da177e4SLinus Torvalds * snd_pcm_period_elapsed - update the pcm status for the next period 19621da177e4SLinus Torvalds * @substream: the pcm substream instance 19631da177e4SLinus Torvalds * 19641da177e4SLinus Torvalds * This function is called from the interrupt handler when the 19651da177e4SLinus Torvalds * PCM has processed the period size. It will update the current 19661da177e4SLinus Torvalds * pointer, set up the tick, wake up sleepers, etc. 19671da177e4SLinus Torvalds * 19681da177e4SLinus Torvalds * Even if more than one periods have elapsed since the last call, you 19691da177e4SLinus Torvalds * have to call this only once. 19701da177e4SLinus Torvalds */ 19711da177e4SLinus Torvalds void snd_pcm_period_elapsed(snd_pcm_substream_t *substream) 19721da177e4SLinus Torvalds { 19731da177e4SLinus Torvalds snd_pcm_runtime_t *runtime; 19741da177e4SLinus Torvalds unsigned long flags; 19751da177e4SLinus Torvalds 19761da177e4SLinus Torvalds snd_assert(substream != NULL, return); 19771da177e4SLinus Torvalds runtime = substream->runtime; 19781da177e4SLinus Torvalds snd_assert(runtime != NULL, return); 19791da177e4SLinus Torvalds 19801da177e4SLinus Torvalds if (runtime->transfer_ack_begin) 19811da177e4SLinus Torvalds runtime->transfer_ack_begin(substream); 19821da177e4SLinus Torvalds 19831da177e4SLinus Torvalds snd_pcm_stream_lock_irqsave(substream, flags); 19841da177e4SLinus Torvalds if (!snd_pcm_running(substream) || 19851da177e4SLinus Torvalds snd_pcm_update_hw_ptr_interrupt(substream) < 0) 19861da177e4SLinus Torvalds goto _end; 19871da177e4SLinus Torvalds 19881da177e4SLinus Torvalds if (substream->timer_running) 19891da177e4SLinus Torvalds snd_timer_interrupt(substream->timer, 1); 19901da177e4SLinus Torvalds if (runtime->sleep_min) 19911da177e4SLinus Torvalds snd_pcm_tick_prepare(substream); 19921da177e4SLinus Torvalds _end: 19931da177e4SLinus Torvalds snd_pcm_stream_unlock_irqrestore(substream, flags); 19941da177e4SLinus Torvalds if (runtime->transfer_ack_end) 19951da177e4SLinus Torvalds runtime->transfer_ack_end(substream); 19961da177e4SLinus Torvalds kill_fasync(&runtime->fasync, SIGIO, POLL_IN); 19971da177e4SLinus Torvalds } 19981da177e4SLinus Torvalds 19991da177e4SLinus Torvalds static int snd_pcm_lib_write_transfer(snd_pcm_substream_t *substream, 20001da177e4SLinus Torvalds unsigned int hwoff, 20011da177e4SLinus Torvalds unsigned long data, unsigned int off, 20021da177e4SLinus Torvalds snd_pcm_uframes_t frames) 20031da177e4SLinus Torvalds { 20041da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 20051da177e4SLinus Torvalds int err; 20061da177e4SLinus Torvalds char __user *buf = (char __user *) data + frames_to_bytes(runtime, off); 20071da177e4SLinus Torvalds if (substream->ops->copy) { 20081da177e4SLinus Torvalds if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0) 20091da177e4SLinus Torvalds return err; 20101da177e4SLinus Torvalds } else { 20111da177e4SLinus Torvalds char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); 20121da177e4SLinus Torvalds snd_assert(runtime->dma_area, return -EFAULT); 20131da177e4SLinus Torvalds if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames))) 20141da177e4SLinus Torvalds return -EFAULT; 20151da177e4SLinus Torvalds } 20161da177e4SLinus Torvalds return 0; 20171da177e4SLinus Torvalds } 20181da177e4SLinus Torvalds 20191da177e4SLinus Torvalds typedef int (*transfer_f)(snd_pcm_substream_t *substream, unsigned int hwoff, 20201da177e4SLinus Torvalds unsigned long data, unsigned int off, 20211da177e4SLinus Torvalds snd_pcm_uframes_t size); 20221da177e4SLinus Torvalds 20231da177e4SLinus Torvalds static snd_pcm_sframes_t snd_pcm_lib_write1(snd_pcm_substream_t *substream, 20241da177e4SLinus Torvalds unsigned long data, 20251da177e4SLinus Torvalds snd_pcm_uframes_t size, 20261da177e4SLinus Torvalds int nonblock, 20271da177e4SLinus Torvalds transfer_f transfer) 20281da177e4SLinus Torvalds { 20291da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 20301da177e4SLinus Torvalds snd_pcm_uframes_t xfer = 0; 20311da177e4SLinus Torvalds snd_pcm_uframes_t offset = 0; 20321da177e4SLinus Torvalds int err = 0; 20331da177e4SLinus Torvalds 20341da177e4SLinus Torvalds if (size == 0) 20351da177e4SLinus Torvalds return 0; 20361da177e4SLinus Torvalds if (size > runtime->xfer_align) 20371da177e4SLinus Torvalds size -= size % runtime->xfer_align; 20381da177e4SLinus Torvalds 20391da177e4SLinus Torvalds snd_pcm_stream_lock_irq(substream); 20401da177e4SLinus Torvalds switch (runtime->status->state) { 20411da177e4SLinus Torvalds case SNDRV_PCM_STATE_PREPARED: 20421da177e4SLinus Torvalds case SNDRV_PCM_STATE_RUNNING: 20431da177e4SLinus Torvalds case SNDRV_PCM_STATE_PAUSED: 20441da177e4SLinus Torvalds break; 20451da177e4SLinus Torvalds case SNDRV_PCM_STATE_XRUN: 20461da177e4SLinus Torvalds err = -EPIPE; 20471da177e4SLinus Torvalds goto _end_unlock; 20481da177e4SLinus Torvalds case SNDRV_PCM_STATE_SUSPENDED: 20491da177e4SLinus Torvalds err = -ESTRPIPE; 20501da177e4SLinus Torvalds goto _end_unlock; 20511da177e4SLinus Torvalds default: 20521da177e4SLinus Torvalds err = -EBADFD; 20531da177e4SLinus Torvalds goto _end_unlock; 20541da177e4SLinus Torvalds } 20551da177e4SLinus Torvalds 20561da177e4SLinus Torvalds while (size > 0) { 20571da177e4SLinus Torvalds snd_pcm_uframes_t frames, appl_ptr, appl_ofs; 20581da177e4SLinus Torvalds snd_pcm_uframes_t avail; 20591da177e4SLinus Torvalds snd_pcm_uframes_t cont; 20601da177e4SLinus Torvalds if (runtime->sleep_min == 0 && runtime->status->state == SNDRV_PCM_STATE_RUNNING) 20611da177e4SLinus Torvalds snd_pcm_update_hw_ptr(substream); 20621da177e4SLinus Torvalds avail = snd_pcm_playback_avail(runtime); 20631da177e4SLinus Torvalds if (((avail < runtime->control->avail_min && size > avail) || 20641da177e4SLinus Torvalds (size >= runtime->xfer_align && avail < runtime->xfer_align))) { 20651da177e4SLinus Torvalds wait_queue_t wait; 20661da177e4SLinus Torvalds enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED } state; 20671da177e4SLinus Torvalds long tout; 20681da177e4SLinus Torvalds 20691da177e4SLinus Torvalds if (nonblock) { 20701da177e4SLinus Torvalds err = -EAGAIN; 20711da177e4SLinus Torvalds goto _end_unlock; 20721da177e4SLinus Torvalds } 20731da177e4SLinus Torvalds 20741da177e4SLinus Torvalds init_waitqueue_entry(&wait, current); 20751da177e4SLinus Torvalds add_wait_queue(&runtime->sleep, &wait); 20761da177e4SLinus Torvalds while (1) { 20771da177e4SLinus Torvalds if (signal_pending(current)) { 20781da177e4SLinus Torvalds state = SIGNALED; 20791da177e4SLinus Torvalds break; 20801da177e4SLinus Torvalds } 20811da177e4SLinus Torvalds set_current_state(TASK_INTERRUPTIBLE); 20821da177e4SLinus Torvalds snd_pcm_stream_unlock_irq(substream); 20831da177e4SLinus Torvalds tout = schedule_timeout(10 * HZ); 20841da177e4SLinus Torvalds snd_pcm_stream_lock_irq(substream); 20851da177e4SLinus Torvalds if (tout == 0) { 20861da177e4SLinus Torvalds if (runtime->status->state != SNDRV_PCM_STATE_PREPARED && 20871da177e4SLinus Torvalds runtime->status->state != SNDRV_PCM_STATE_PAUSED) { 20881da177e4SLinus Torvalds state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED; 20891da177e4SLinus Torvalds break; 20901da177e4SLinus Torvalds } 20911da177e4SLinus Torvalds } 20921da177e4SLinus Torvalds switch (runtime->status->state) { 20931da177e4SLinus Torvalds case SNDRV_PCM_STATE_XRUN: 20941da177e4SLinus Torvalds case SNDRV_PCM_STATE_DRAINING: 20951da177e4SLinus Torvalds state = ERROR; 20961da177e4SLinus Torvalds goto _end_loop; 20971da177e4SLinus Torvalds case SNDRV_PCM_STATE_SUSPENDED: 20981da177e4SLinus Torvalds state = SUSPENDED; 20991da177e4SLinus Torvalds goto _end_loop; 21001da177e4SLinus Torvalds default: 21011da177e4SLinus Torvalds break; 21021da177e4SLinus Torvalds } 21031da177e4SLinus Torvalds avail = snd_pcm_playback_avail(runtime); 21041da177e4SLinus Torvalds if (avail >= runtime->control->avail_min) { 21051da177e4SLinus Torvalds state = READY; 21061da177e4SLinus Torvalds break; 21071da177e4SLinus Torvalds } 21081da177e4SLinus Torvalds } 21091da177e4SLinus Torvalds _end_loop: 21101da177e4SLinus Torvalds remove_wait_queue(&runtime->sleep, &wait); 21111da177e4SLinus Torvalds 21121da177e4SLinus Torvalds switch (state) { 21131da177e4SLinus Torvalds case ERROR: 21141da177e4SLinus Torvalds err = -EPIPE; 21151da177e4SLinus Torvalds goto _end_unlock; 21161da177e4SLinus Torvalds case SUSPENDED: 21171da177e4SLinus Torvalds err = -ESTRPIPE; 21181da177e4SLinus Torvalds goto _end_unlock; 21191da177e4SLinus Torvalds case SIGNALED: 21201da177e4SLinus Torvalds err = -ERESTARTSYS; 21211da177e4SLinus Torvalds goto _end_unlock; 21221da177e4SLinus Torvalds case EXPIRED: 21231da177e4SLinus Torvalds snd_printd("playback write error (DMA or IRQ trouble?)\n"); 21241da177e4SLinus Torvalds err = -EIO; 21251da177e4SLinus Torvalds goto _end_unlock; 21261da177e4SLinus Torvalds default: 21271da177e4SLinus Torvalds break; 21281da177e4SLinus Torvalds } 21291da177e4SLinus Torvalds } 21301da177e4SLinus Torvalds if (avail > runtime->xfer_align) 21311da177e4SLinus Torvalds avail -= avail % runtime->xfer_align; 21321da177e4SLinus Torvalds frames = size > avail ? avail : size; 21331da177e4SLinus Torvalds cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; 21341da177e4SLinus Torvalds if (frames > cont) 21351da177e4SLinus Torvalds frames = cont; 21361da177e4SLinus Torvalds snd_assert(frames != 0, snd_pcm_stream_unlock_irq(substream); return -EINVAL); 21371da177e4SLinus Torvalds appl_ptr = runtime->control->appl_ptr; 21381da177e4SLinus Torvalds appl_ofs = appl_ptr % runtime->buffer_size; 21391da177e4SLinus Torvalds snd_pcm_stream_unlock_irq(substream); 21401da177e4SLinus Torvalds if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0) 21411da177e4SLinus Torvalds goto _end; 21421da177e4SLinus Torvalds snd_pcm_stream_lock_irq(substream); 21431da177e4SLinus Torvalds switch (runtime->status->state) { 21441da177e4SLinus Torvalds case SNDRV_PCM_STATE_XRUN: 21451da177e4SLinus Torvalds err = -EPIPE; 21461da177e4SLinus Torvalds goto _end_unlock; 21471da177e4SLinus Torvalds case SNDRV_PCM_STATE_SUSPENDED: 21481da177e4SLinus Torvalds err = -ESTRPIPE; 21491da177e4SLinus Torvalds goto _end_unlock; 21501da177e4SLinus Torvalds default: 21511da177e4SLinus Torvalds break; 21521da177e4SLinus Torvalds } 21531da177e4SLinus Torvalds appl_ptr += frames; 21541da177e4SLinus Torvalds if (appl_ptr >= runtime->boundary) 21551da177e4SLinus Torvalds appl_ptr -= runtime->boundary; 21561da177e4SLinus Torvalds runtime->control->appl_ptr = appl_ptr; 21571da177e4SLinus Torvalds if (substream->ops->ack) 21581da177e4SLinus Torvalds substream->ops->ack(substream); 21591da177e4SLinus Torvalds 21601da177e4SLinus Torvalds offset += frames; 21611da177e4SLinus Torvalds size -= frames; 21621da177e4SLinus Torvalds xfer += frames; 21631da177e4SLinus Torvalds if (runtime->status->state == SNDRV_PCM_STATE_PREPARED && 21641da177e4SLinus Torvalds snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) { 21651da177e4SLinus Torvalds err = snd_pcm_start(substream); 21661da177e4SLinus Torvalds if (err < 0) 21671da177e4SLinus Torvalds goto _end_unlock; 21681da177e4SLinus Torvalds } 21691da177e4SLinus Torvalds if (runtime->sleep_min && 21701da177e4SLinus Torvalds runtime->status->state == SNDRV_PCM_STATE_RUNNING) 21711da177e4SLinus Torvalds snd_pcm_tick_prepare(substream); 21721da177e4SLinus Torvalds } 21731da177e4SLinus Torvalds _end_unlock: 21741da177e4SLinus Torvalds snd_pcm_stream_unlock_irq(substream); 21751da177e4SLinus Torvalds _end: 21761da177e4SLinus Torvalds return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; 21771da177e4SLinus Torvalds } 21781da177e4SLinus Torvalds 21791da177e4SLinus Torvalds snd_pcm_sframes_t snd_pcm_lib_write(snd_pcm_substream_t *substream, const void __user *buf, snd_pcm_uframes_t size) 21801da177e4SLinus Torvalds { 21811da177e4SLinus Torvalds snd_pcm_runtime_t *runtime; 21821da177e4SLinus Torvalds int nonblock; 21831da177e4SLinus Torvalds 21841da177e4SLinus Torvalds snd_assert(substream != NULL, return -ENXIO); 21851da177e4SLinus Torvalds runtime = substream->runtime; 21861da177e4SLinus Torvalds snd_assert(runtime != NULL, return -ENXIO); 21871da177e4SLinus Torvalds snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); 21881da177e4SLinus Torvalds if (runtime->status->state == SNDRV_PCM_STATE_OPEN) 21891da177e4SLinus Torvalds return -EBADFD; 21901da177e4SLinus Torvalds 21911da177e4SLinus Torvalds snd_assert(substream->ffile != NULL, return -ENXIO); 21921da177e4SLinus Torvalds nonblock = !!(substream->ffile->f_flags & O_NONBLOCK); 21931da177e4SLinus Torvalds #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) 21941da177e4SLinus Torvalds if (substream->oss.oss) { 21951da177e4SLinus Torvalds snd_pcm_oss_setup_t *setup = substream->oss.setup; 21961da177e4SLinus Torvalds if (setup != NULL) { 21971da177e4SLinus Torvalds if (setup->nonblock) 21981da177e4SLinus Torvalds nonblock = 1; 21991da177e4SLinus Torvalds else if (setup->block) 22001da177e4SLinus Torvalds nonblock = 0; 22011da177e4SLinus Torvalds } 22021da177e4SLinus Torvalds } 22031da177e4SLinus Torvalds #endif 22041da177e4SLinus Torvalds 22051da177e4SLinus Torvalds if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && 22061da177e4SLinus Torvalds runtime->channels > 1) 22071da177e4SLinus Torvalds return -EINVAL; 22081da177e4SLinus Torvalds return snd_pcm_lib_write1(substream, (unsigned long)buf, size, nonblock, 22091da177e4SLinus Torvalds snd_pcm_lib_write_transfer); 22101da177e4SLinus Torvalds } 22111da177e4SLinus Torvalds 22121da177e4SLinus Torvalds static int snd_pcm_lib_writev_transfer(snd_pcm_substream_t *substream, 22131da177e4SLinus Torvalds unsigned int hwoff, 22141da177e4SLinus Torvalds unsigned long data, unsigned int off, 22151da177e4SLinus Torvalds snd_pcm_uframes_t frames) 22161da177e4SLinus Torvalds { 22171da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 22181da177e4SLinus Torvalds int err; 22191da177e4SLinus Torvalds void __user **bufs = (void __user **)data; 22201da177e4SLinus Torvalds int channels = runtime->channels; 22211da177e4SLinus Torvalds int c; 22221da177e4SLinus Torvalds if (substream->ops->copy) { 22231da177e4SLinus Torvalds snd_assert(substream->ops->silence != NULL, return -EINVAL); 22241da177e4SLinus Torvalds for (c = 0; c < channels; ++c, ++bufs) { 22251da177e4SLinus Torvalds if (*bufs == NULL) { 22261da177e4SLinus Torvalds if ((err = substream->ops->silence(substream, c, hwoff, frames)) < 0) 22271da177e4SLinus Torvalds return err; 22281da177e4SLinus Torvalds } else { 22291da177e4SLinus Torvalds char __user *buf = *bufs + samples_to_bytes(runtime, off); 22301da177e4SLinus Torvalds if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0) 22311da177e4SLinus Torvalds return err; 22321da177e4SLinus Torvalds } 22331da177e4SLinus Torvalds } 22341da177e4SLinus Torvalds } else { 22351da177e4SLinus Torvalds /* default transfer behaviour */ 22361da177e4SLinus Torvalds size_t dma_csize = runtime->dma_bytes / channels; 22371da177e4SLinus Torvalds snd_assert(runtime->dma_area, return -EFAULT); 22381da177e4SLinus Torvalds for (c = 0; c < channels; ++c, ++bufs) { 22391da177e4SLinus Torvalds char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff); 22401da177e4SLinus Torvalds if (*bufs == NULL) { 22411da177e4SLinus Torvalds snd_pcm_format_set_silence(runtime->format, hwbuf, frames); 22421da177e4SLinus Torvalds } else { 22431da177e4SLinus Torvalds char __user *buf = *bufs + samples_to_bytes(runtime, off); 22441da177e4SLinus Torvalds if (copy_from_user(hwbuf, buf, samples_to_bytes(runtime, frames))) 22451da177e4SLinus Torvalds return -EFAULT; 22461da177e4SLinus Torvalds } 22471da177e4SLinus Torvalds } 22481da177e4SLinus Torvalds } 22491da177e4SLinus Torvalds return 0; 22501da177e4SLinus Torvalds } 22511da177e4SLinus Torvalds 22521da177e4SLinus Torvalds snd_pcm_sframes_t snd_pcm_lib_writev(snd_pcm_substream_t *substream, 22531da177e4SLinus Torvalds void __user **bufs, 22541da177e4SLinus Torvalds snd_pcm_uframes_t frames) 22551da177e4SLinus Torvalds { 22561da177e4SLinus Torvalds snd_pcm_runtime_t *runtime; 22571da177e4SLinus Torvalds int nonblock; 22581da177e4SLinus Torvalds 22591da177e4SLinus Torvalds snd_assert(substream != NULL, return -ENXIO); 22601da177e4SLinus Torvalds runtime = substream->runtime; 22611da177e4SLinus Torvalds snd_assert(runtime != NULL, return -ENXIO); 22621da177e4SLinus Torvalds snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); 22631da177e4SLinus Torvalds if (runtime->status->state == SNDRV_PCM_STATE_OPEN) 22641da177e4SLinus Torvalds return -EBADFD; 22651da177e4SLinus Torvalds 22661da177e4SLinus Torvalds snd_assert(substream->ffile != NULL, return -ENXIO); 22671da177e4SLinus Torvalds nonblock = !!(substream->ffile->f_flags & O_NONBLOCK); 22681da177e4SLinus Torvalds #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) 22691da177e4SLinus Torvalds if (substream->oss.oss) { 22701da177e4SLinus Torvalds snd_pcm_oss_setup_t *setup = substream->oss.setup; 22711da177e4SLinus Torvalds if (setup != NULL) { 22721da177e4SLinus Torvalds if (setup->nonblock) 22731da177e4SLinus Torvalds nonblock = 1; 22741da177e4SLinus Torvalds else if (setup->block) 22751da177e4SLinus Torvalds nonblock = 0; 22761da177e4SLinus Torvalds } 22771da177e4SLinus Torvalds } 22781da177e4SLinus Torvalds #endif 22791da177e4SLinus Torvalds 22801da177e4SLinus Torvalds if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) 22811da177e4SLinus Torvalds return -EINVAL; 22821da177e4SLinus Torvalds return snd_pcm_lib_write1(substream, (unsigned long)bufs, frames, 22831da177e4SLinus Torvalds nonblock, snd_pcm_lib_writev_transfer); 22841da177e4SLinus Torvalds } 22851da177e4SLinus Torvalds 22861da177e4SLinus Torvalds static int snd_pcm_lib_read_transfer(snd_pcm_substream_t *substream, 22871da177e4SLinus Torvalds unsigned int hwoff, 22881da177e4SLinus Torvalds unsigned long data, unsigned int off, 22891da177e4SLinus Torvalds snd_pcm_uframes_t frames) 22901da177e4SLinus Torvalds { 22911da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 22921da177e4SLinus Torvalds int err; 22931da177e4SLinus Torvalds char __user *buf = (char __user *) data + frames_to_bytes(runtime, off); 22941da177e4SLinus Torvalds if (substream->ops->copy) { 22951da177e4SLinus Torvalds if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0) 22961da177e4SLinus Torvalds return err; 22971da177e4SLinus Torvalds } else { 22981da177e4SLinus Torvalds char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); 22991da177e4SLinus Torvalds snd_assert(runtime->dma_area, return -EFAULT); 23001da177e4SLinus Torvalds if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames))) 23011da177e4SLinus Torvalds return -EFAULT; 23021da177e4SLinus Torvalds } 23031da177e4SLinus Torvalds return 0; 23041da177e4SLinus Torvalds } 23051da177e4SLinus Torvalds 23061da177e4SLinus Torvalds static snd_pcm_sframes_t snd_pcm_lib_read1(snd_pcm_substream_t *substream, 23071da177e4SLinus Torvalds unsigned long data, 23081da177e4SLinus Torvalds snd_pcm_uframes_t size, 23091da177e4SLinus Torvalds int nonblock, 23101da177e4SLinus Torvalds transfer_f transfer) 23111da177e4SLinus Torvalds { 23121da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 23131da177e4SLinus Torvalds snd_pcm_uframes_t xfer = 0; 23141da177e4SLinus Torvalds snd_pcm_uframes_t offset = 0; 23151da177e4SLinus Torvalds int err = 0; 23161da177e4SLinus Torvalds 23171da177e4SLinus Torvalds if (size == 0) 23181da177e4SLinus Torvalds return 0; 23191da177e4SLinus Torvalds if (size > runtime->xfer_align) 23201da177e4SLinus Torvalds size -= size % runtime->xfer_align; 23211da177e4SLinus Torvalds 23221da177e4SLinus Torvalds snd_pcm_stream_lock_irq(substream); 23231da177e4SLinus Torvalds switch (runtime->status->state) { 23241da177e4SLinus Torvalds case SNDRV_PCM_STATE_PREPARED: 23251da177e4SLinus Torvalds if (size >= runtime->start_threshold) { 23261da177e4SLinus Torvalds err = snd_pcm_start(substream); 23271da177e4SLinus Torvalds if (err < 0) 23281da177e4SLinus Torvalds goto _end_unlock; 23291da177e4SLinus Torvalds } 23301da177e4SLinus Torvalds break; 23311da177e4SLinus Torvalds case SNDRV_PCM_STATE_DRAINING: 23321da177e4SLinus Torvalds case SNDRV_PCM_STATE_RUNNING: 23331da177e4SLinus Torvalds case SNDRV_PCM_STATE_PAUSED: 23341da177e4SLinus Torvalds break; 23351da177e4SLinus Torvalds case SNDRV_PCM_STATE_XRUN: 23361da177e4SLinus Torvalds err = -EPIPE; 23371da177e4SLinus Torvalds goto _end_unlock; 23381da177e4SLinus Torvalds case SNDRV_PCM_STATE_SUSPENDED: 23391da177e4SLinus Torvalds err = -ESTRPIPE; 23401da177e4SLinus Torvalds goto _end_unlock; 23411da177e4SLinus Torvalds default: 23421da177e4SLinus Torvalds err = -EBADFD; 23431da177e4SLinus Torvalds goto _end_unlock; 23441da177e4SLinus Torvalds } 23451da177e4SLinus Torvalds 23461da177e4SLinus Torvalds while (size > 0) { 23471da177e4SLinus Torvalds snd_pcm_uframes_t frames, appl_ptr, appl_ofs; 23481da177e4SLinus Torvalds snd_pcm_uframes_t avail; 23491da177e4SLinus Torvalds snd_pcm_uframes_t cont; 23501da177e4SLinus Torvalds if (runtime->sleep_min == 0 && runtime->status->state == SNDRV_PCM_STATE_RUNNING) 23511da177e4SLinus Torvalds snd_pcm_update_hw_ptr(substream); 23521da177e4SLinus Torvalds __draining: 23531da177e4SLinus Torvalds avail = snd_pcm_capture_avail(runtime); 23541da177e4SLinus Torvalds if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { 23551da177e4SLinus Torvalds if (avail < runtime->xfer_align) { 23561da177e4SLinus Torvalds err = -EPIPE; 23571da177e4SLinus Torvalds goto _end_unlock; 23581da177e4SLinus Torvalds } 23591da177e4SLinus Torvalds } else if ((avail < runtime->control->avail_min && size > avail) || 23601da177e4SLinus Torvalds (size >= runtime->xfer_align && avail < runtime->xfer_align)) { 23611da177e4SLinus Torvalds wait_queue_t wait; 23621da177e4SLinus Torvalds enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED } state; 23631da177e4SLinus Torvalds long tout; 23641da177e4SLinus Torvalds 23651da177e4SLinus Torvalds if (nonblock) { 23661da177e4SLinus Torvalds err = -EAGAIN; 23671da177e4SLinus Torvalds goto _end_unlock; 23681da177e4SLinus Torvalds } 23691da177e4SLinus Torvalds 23701da177e4SLinus Torvalds init_waitqueue_entry(&wait, current); 23711da177e4SLinus Torvalds add_wait_queue(&runtime->sleep, &wait); 23721da177e4SLinus Torvalds while (1) { 23731da177e4SLinus Torvalds if (signal_pending(current)) { 23741da177e4SLinus Torvalds state = SIGNALED; 23751da177e4SLinus Torvalds break; 23761da177e4SLinus Torvalds } 23771da177e4SLinus Torvalds set_current_state(TASK_INTERRUPTIBLE); 23781da177e4SLinus Torvalds snd_pcm_stream_unlock_irq(substream); 23791da177e4SLinus Torvalds tout = schedule_timeout(10 * HZ); 23801da177e4SLinus Torvalds snd_pcm_stream_lock_irq(substream); 23811da177e4SLinus Torvalds if (tout == 0) { 23821da177e4SLinus Torvalds if (runtime->status->state != SNDRV_PCM_STATE_PREPARED && 23831da177e4SLinus Torvalds runtime->status->state != SNDRV_PCM_STATE_PAUSED) { 23841da177e4SLinus Torvalds state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED; 23851da177e4SLinus Torvalds break; 23861da177e4SLinus Torvalds } 23871da177e4SLinus Torvalds } 23881da177e4SLinus Torvalds switch (runtime->status->state) { 23891da177e4SLinus Torvalds case SNDRV_PCM_STATE_XRUN: 23901da177e4SLinus Torvalds state = ERROR; 23911da177e4SLinus Torvalds goto _end_loop; 23921da177e4SLinus Torvalds case SNDRV_PCM_STATE_SUSPENDED: 23931da177e4SLinus Torvalds state = SUSPENDED; 23941da177e4SLinus Torvalds goto _end_loop; 23951da177e4SLinus Torvalds case SNDRV_PCM_STATE_DRAINING: 23961da177e4SLinus Torvalds goto __draining; 23971da177e4SLinus Torvalds default: 23981da177e4SLinus Torvalds break; 23991da177e4SLinus Torvalds } 24001da177e4SLinus Torvalds avail = snd_pcm_capture_avail(runtime); 24011da177e4SLinus Torvalds if (avail >= runtime->control->avail_min) { 24021da177e4SLinus Torvalds state = READY; 24031da177e4SLinus Torvalds break; 24041da177e4SLinus Torvalds } 24051da177e4SLinus Torvalds } 24061da177e4SLinus Torvalds _end_loop: 24071da177e4SLinus Torvalds remove_wait_queue(&runtime->sleep, &wait); 24081da177e4SLinus Torvalds 24091da177e4SLinus Torvalds switch (state) { 24101da177e4SLinus Torvalds case ERROR: 24111da177e4SLinus Torvalds err = -EPIPE; 24121da177e4SLinus Torvalds goto _end_unlock; 24131da177e4SLinus Torvalds case SUSPENDED: 24141da177e4SLinus Torvalds err = -ESTRPIPE; 24151da177e4SLinus Torvalds goto _end_unlock; 24161da177e4SLinus Torvalds case SIGNALED: 24171da177e4SLinus Torvalds err = -ERESTARTSYS; 24181da177e4SLinus Torvalds goto _end_unlock; 24191da177e4SLinus Torvalds case EXPIRED: 24201da177e4SLinus Torvalds snd_printd("capture read error (DMA or IRQ trouble?)\n"); 24211da177e4SLinus Torvalds err = -EIO; 24221da177e4SLinus Torvalds goto _end_unlock; 24231da177e4SLinus Torvalds default: 24241da177e4SLinus Torvalds break; 24251da177e4SLinus Torvalds } 24261da177e4SLinus Torvalds } 24271da177e4SLinus Torvalds if (avail > runtime->xfer_align) 24281da177e4SLinus Torvalds avail -= avail % runtime->xfer_align; 24291da177e4SLinus Torvalds frames = size > avail ? avail : size; 24301da177e4SLinus Torvalds cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; 24311da177e4SLinus Torvalds if (frames > cont) 24321da177e4SLinus Torvalds frames = cont; 24331da177e4SLinus Torvalds snd_assert(frames != 0, snd_pcm_stream_unlock_irq(substream); return -EINVAL); 24341da177e4SLinus Torvalds appl_ptr = runtime->control->appl_ptr; 24351da177e4SLinus Torvalds appl_ofs = appl_ptr % runtime->buffer_size; 24361da177e4SLinus Torvalds snd_pcm_stream_unlock_irq(substream); 24371da177e4SLinus Torvalds if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0) 24381da177e4SLinus Torvalds goto _end; 24391da177e4SLinus Torvalds snd_pcm_stream_lock_irq(substream); 24401da177e4SLinus Torvalds switch (runtime->status->state) { 24411da177e4SLinus Torvalds case SNDRV_PCM_STATE_XRUN: 24421da177e4SLinus Torvalds err = -EPIPE; 24431da177e4SLinus Torvalds goto _end_unlock; 24441da177e4SLinus Torvalds case SNDRV_PCM_STATE_SUSPENDED: 24451da177e4SLinus Torvalds err = -ESTRPIPE; 24461da177e4SLinus Torvalds goto _end_unlock; 24471da177e4SLinus Torvalds default: 24481da177e4SLinus Torvalds break; 24491da177e4SLinus Torvalds } 24501da177e4SLinus Torvalds appl_ptr += frames; 24511da177e4SLinus Torvalds if (appl_ptr >= runtime->boundary) 24521da177e4SLinus Torvalds appl_ptr -= runtime->boundary; 24531da177e4SLinus Torvalds runtime->control->appl_ptr = appl_ptr; 24541da177e4SLinus Torvalds if (substream->ops->ack) 24551da177e4SLinus Torvalds substream->ops->ack(substream); 24561da177e4SLinus Torvalds 24571da177e4SLinus Torvalds offset += frames; 24581da177e4SLinus Torvalds size -= frames; 24591da177e4SLinus Torvalds xfer += frames; 24601da177e4SLinus Torvalds if (runtime->sleep_min && 24611da177e4SLinus Torvalds runtime->status->state == SNDRV_PCM_STATE_RUNNING) 24621da177e4SLinus Torvalds snd_pcm_tick_prepare(substream); 24631da177e4SLinus Torvalds } 24641da177e4SLinus Torvalds _end_unlock: 24651da177e4SLinus Torvalds snd_pcm_stream_unlock_irq(substream); 24661da177e4SLinus Torvalds _end: 24671da177e4SLinus Torvalds return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; 24681da177e4SLinus Torvalds } 24691da177e4SLinus Torvalds 24701da177e4SLinus Torvalds snd_pcm_sframes_t snd_pcm_lib_read(snd_pcm_substream_t *substream, void __user *buf, snd_pcm_uframes_t size) 24711da177e4SLinus Torvalds { 24721da177e4SLinus Torvalds snd_pcm_runtime_t *runtime; 24731da177e4SLinus Torvalds int nonblock; 24741da177e4SLinus Torvalds 24751da177e4SLinus Torvalds snd_assert(substream != NULL, return -ENXIO); 24761da177e4SLinus Torvalds runtime = substream->runtime; 24771da177e4SLinus Torvalds snd_assert(runtime != NULL, return -ENXIO); 24781da177e4SLinus Torvalds snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); 24791da177e4SLinus Torvalds if (runtime->status->state == SNDRV_PCM_STATE_OPEN) 24801da177e4SLinus Torvalds return -EBADFD; 24811da177e4SLinus Torvalds 24821da177e4SLinus Torvalds snd_assert(substream->ffile != NULL, return -ENXIO); 24831da177e4SLinus Torvalds nonblock = !!(substream->ffile->f_flags & O_NONBLOCK); 24841da177e4SLinus Torvalds #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) 24851da177e4SLinus Torvalds if (substream->oss.oss) { 24861da177e4SLinus Torvalds snd_pcm_oss_setup_t *setup = substream->oss.setup; 24871da177e4SLinus Torvalds if (setup != NULL) { 24881da177e4SLinus Torvalds if (setup->nonblock) 24891da177e4SLinus Torvalds nonblock = 1; 24901da177e4SLinus Torvalds else if (setup->block) 24911da177e4SLinus Torvalds nonblock = 0; 24921da177e4SLinus Torvalds } 24931da177e4SLinus Torvalds } 24941da177e4SLinus Torvalds #endif 24951da177e4SLinus Torvalds if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED) 24961da177e4SLinus Torvalds return -EINVAL; 24971da177e4SLinus Torvalds return snd_pcm_lib_read1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_read_transfer); 24981da177e4SLinus Torvalds } 24991da177e4SLinus Torvalds 25001da177e4SLinus Torvalds static int snd_pcm_lib_readv_transfer(snd_pcm_substream_t *substream, 25011da177e4SLinus Torvalds unsigned int hwoff, 25021da177e4SLinus Torvalds unsigned long data, unsigned int off, 25031da177e4SLinus Torvalds snd_pcm_uframes_t frames) 25041da177e4SLinus Torvalds { 25051da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 25061da177e4SLinus Torvalds int err; 25071da177e4SLinus Torvalds void __user **bufs = (void __user **)data; 25081da177e4SLinus Torvalds int channels = runtime->channels; 25091da177e4SLinus Torvalds int c; 25101da177e4SLinus Torvalds if (substream->ops->copy) { 25111da177e4SLinus Torvalds for (c = 0; c < channels; ++c, ++bufs) { 25121da177e4SLinus Torvalds char __user *buf; 25131da177e4SLinus Torvalds if (*bufs == NULL) 25141da177e4SLinus Torvalds continue; 25151da177e4SLinus Torvalds buf = *bufs + samples_to_bytes(runtime, off); 25161da177e4SLinus Torvalds if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0) 25171da177e4SLinus Torvalds return err; 25181da177e4SLinus Torvalds } 25191da177e4SLinus Torvalds } else { 25201da177e4SLinus Torvalds snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels; 25211da177e4SLinus Torvalds snd_assert(runtime->dma_area, return -EFAULT); 25221da177e4SLinus Torvalds for (c = 0; c < channels; ++c, ++bufs) { 25231da177e4SLinus Torvalds char *hwbuf; 25241da177e4SLinus Torvalds char __user *buf; 25251da177e4SLinus Torvalds if (*bufs == NULL) 25261da177e4SLinus Torvalds continue; 25271da177e4SLinus Torvalds 25281da177e4SLinus Torvalds hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff); 25291da177e4SLinus Torvalds buf = *bufs + samples_to_bytes(runtime, off); 25301da177e4SLinus Torvalds if (copy_to_user(buf, hwbuf, samples_to_bytes(runtime, frames))) 25311da177e4SLinus Torvalds return -EFAULT; 25321da177e4SLinus Torvalds } 25331da177e4SLinus Torvalds } 25341da177e4SLinus Torvalds return 0; 25351da177e4SLinus Torvalds } 25361da177e4SLinus Torvalds 25371da177e4SLinus Torvalds snd_pcm_sframes_t snd_pcm_lib_readv(snd_pcm_substream_t *substream, 25381da177e4SLinus Torvalds void __user **bufs, 25391da177e4SLinus Torvalds snd_pcm_uframes_t frames) 25401da177e4SLinus Torvalds { 25411da177e4SLinus Torvalds snd_pcm_runtime_t *runtime; 25421da177e4SLinus Torvalds int nonblock; 25431da177e4SLinus Torvalds 25441da177e4SLinus Torvalds snd_assert(substream != NULL, return -ENXIO); 25451da177e4SLinus Torvalds runtime = substream->runtime; 25461da177e4SLinus Torvalds snd_assert(runtime != NULL, return -ENXIO); 25471da177e4SLinus Torvalds snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); 25481da177e4SLinus Torvalds if (runtime->status->state == SNDRV_PCM_STATE_OPEN) 25491da177e4SLinus Torvalds return -EBADFD; 25501da177e4SLinus Torvalds 25511da177e4SLinus Torvalds snd_assert(substream->ffile != NULL, return -ENXIO); 25521da177e4SLinus Torvalds nonblock = !!(substream->ffile->f_flags & O_NONBLOCK); 25531da177e4SLinus Torvalds #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) 25541da177e4SLinus Torvalds if (substream->oss.oss) { 25551da177e4SLinus Torvalds snd_pcm_oss_setup_t *setup = substream->oss.setup; 25561da177e4SLinus Torvalds if (setup != NULL) { 25571da177e4SLinus Torvalds if (setup->nonblock) 25581da177e4SLinus Torvalds nonblock = 1; 25591da177e4SLinus Torvalds else if (setup->block) 25601da177e4SLinus Torvalds nonblock = 0; 25611da177e4SLinus Torvalds } 25621da177e4SLinus Torvalds } 25631da177e4SLinus Torvalds #endif 25641da177e4SLinus Torvalds 25651da177e4SLinus Torvalds if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) 25661da177e4SLinus Torvalds return -EINVAL; 25671da177e4SLinus Torvalds return snd_pcm_lib_read1(substream, (unsigned long)bufs, frames, nonblock, snd_pcm_lib_readv_transfer); 25681da177e4SLinus Torvalds } 25691da177e4SLinus Torvalds 25701da177e4SLinus Torvalds /* 25711da177e4SLinus Torvalds * Exported symbols 25721da177e4SLinus Torvalds */ 25731da177e4SLinus Torvalds 25741da177e4SLinus Torvalds EXPORT_SYMBOL(snd_interval_refine); 25751da177e4SLinus Torvalds EXPORT_SYMBOL(snd_interval_list); 25761da177e4SLinus Torvalds EXPORT_SYMBOL(snd_interval_ratnum); 25771da177e4SLinus Torvalds EXPORT_SYMBOL(_snd_pcm_hw_params_any); 25781da177e4SLinus Torvalds EXPORT_SYMBOL(_snd_pcm_hw_param_min); 25791da177e4SLinus Torvalds EXPORT_SYMBOL(_snd_pcm_hw_param_set); 25801da177e4SLinus Torvalds EXPORT_SYMBOL(_snd_pcm_hw_param_setempty); 25811da177e4SLinus Torvalds EXPORT_SYMBOL(_snd_pcm_hw_param_setinteger); 25821da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_hw_param_value_min); 25831da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_hw_param_value_max); 25841da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_hw_param_mask); 25851da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_hw_param_first); 25861da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_hw_param_last); 25871da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_hw_param_near); 25881da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_hw_param_set); 25891da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_hw_refine); 25901da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_hw_constraints_init); 25911da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_hw_constraints_complete); 25921da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_hw_constraint_list); 25931da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_hw_constraint_step); 25941da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums); 25951da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_hw_constraint_ratdens); 25961da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_hw_constraint_msbits); 25971da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_hw_constraint_minmax); 25981da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_hw_constraint_integer); 25991da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_hw_constraint_pow2); 26001da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_hw_rule_add); 26011da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_set_ops); 26021da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_set_sync); 26031da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_lib_ioctl); 26041da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_stop); 26051da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_period_elapsed); 26061da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_lib_write); 26071da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_lib_read); 26081da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_lib_writev); 26091da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_lib_readv); 26101da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_lib_buffer_bytes); 26111da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_lib_period_bytes); 26121da177e4SLinus Torvalds /* pcm_memory.c */ 26131da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_lib_preallocate_free_for_all); 26141da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages); 26151da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all); 26161da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page); 26171da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_lib_malloc_pages); 26181da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_lib_free_pages); 2619