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