11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Digital Audio (PCM) abstract layer 3c1017a4cSJaroslav Kysela * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 41da177e4SLinus Torvalds * Abramo Bagnara <abramo@alsa-project.org> 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 81da177e4SLinus Torvalds * it under the terms of the GNU General Public License as published by 91da177e4SLinus Torvalds * the Free Software Foundation; either version 2 of the License, or 101da177e4SLinus Torvalds * (at your option) any later version. 111da177e4SLinus Torvalds * 121da177e4SLinus Torvalds * This program is distributed in the hope that it will be useful, 131da177e4SLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 141da177e4SLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 151da177e4SLinus Torvalds * GNU General Public License for more details. 161da177e4SLinus Torvalds * 171da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License 181da177e4SLinus Torvalds * along with this program; if not, write to the Free Software 191da177e4SLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 201da177e4SLinus Torvalds * 211da177e4SLinus Torvalds */ 221da177e4SLinus Torvalds 231da177e4SLinus Torvalds #include <linux/slab.h> 241da177e4SLinus Torvalds #include <linux/time.h> 253f7440a6STakashi Iwai #include <linux/math64.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 */ 42877211f5STakashi Iwai void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_uframes_t new_hw_ptr) 431da177e4SLinus Torvalds { 44877211f5STakashi Iwai struct snd_pcm_runtime *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 } 59235475cbSTakashi Iwai if (runtime->silence_filled >= runtime->buffer_size) 601da177e4SLinus Torvalds return; 611da177e4SLinus Torvalds noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silence_filled; 621da177e4SLinus Torvalds if (noise_dist >= (snd_pcm_sframes_t) runtime->silence_threshold) 631da177e4SLinus Torvalds return; 641da177e4SLinus Torvalds frames = runtime->silence_threshold - noise_dist; 651da177e4SLinus Torvalds if (frames > runtime->silence_size) 661da177e4SLinus Torvalds frames = runtime->silence_size; 671da177e4SLinus Torvalds } else { 681da177e4SLinus Torvalds if (new_hw_ptr == ULONG_MAX) { /* initialization */ 691da177e4SLinus Torvalds snd_pcm_sframes_t avail = snd_pcm_playback_hw_avail(runtime); 701da177e4SLinus Torvalds runtime->silence_filled = avail > 0 ? avail : 0; 711da177e4SLinus Torvalds runtime->silence_start = (runtime->status->hw_ptr + 721da177e4SLinus Torvalds runtime->silence_filled) % 731da177e4SLinus Torvalds runtime->boundary; 741da177e4SLinus Torvalds } else { 751da177e4SLinus Torvalds ofs = runtime->status->hw_ptr; 761da177e4SLinus Torvalds frames = new_hw_ptr - ofs; 771da177e4SLinus Torvalds if ((snd_pcm_sframes_t)frames < 0) 781da177e4SLinus Torvalds frames += runtime->boundary; 791da177e4SLinus Torvalds runtime->silence_filled -= frames; 801da177e4SLinus Torvalds if ((snd_pcm_sframes_t)runtime->silence_filled < 0) { 811da177e4SLinus Torvalds runtime->silence_filled = 0; 829a826ddbSClemens Ladisch runtime->silence_start = new_hw_ptr; 831da177e4SLinus Torvalds } else { 849a826ddbSClemens Ladisch runtime->silence_start = ofs; 851da177e4SLinus Torvalds } 861da177e4SLinus Torvalds } 871da177e4SLinus Torvalds frames = runtime->buffer_size - runtime->silence_filled; 881da177e4SLinus Torvalds } 897eaa943cSTakashi Iwai if (snd_BUG_ON(frames > runtime->buffer_size)) 907eaa943cSTakashi Iwai return; 911da177e4SLinus Torvalds if (frames == 0) 921da177e4SLinus Torvalds return; 939a826ddbSClemens Ladisch ofs = runtime->silence_start % runtime->buffer_size; 941da177e4SLinus Torvalds while (frames > 0) { 951da177e4SLinus Torvalds transfer = ofs + frames > runtime->buffer_size ? runtime->buffer_size - ofs : frames; 961da177e4SLinus Torvalds if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || 971da177e4SLinus Torvalds runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) { 981da177e4SLinus Torvalds if (substream->ops->silence) { 991da177e4SLinus Torvalds int err; 1001da177e4SLinus Torvalds err = substream->ops->silence(substream, -1, ofs, transfer); 1017eaa943cSTakashi Iwai snd_BUG_ON(err < 0); 1021da177e4SLinus Torvalds } else { 1031da177e4SLinus Torvalds char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, ofs); 1041da177e4SLinus Torvalds snd_pcm_format_set_silence(runtime->format, hwbuf, transfer * runtime->channels); 1051da177e4SLinus Torvalds } 1061da177e4SLinus Torvalds } else { 1071da177e4SLinus Torvalds unsigned int c; 1081da177e4SLinus Torvalds unsigned int channels = runtime->channels; 1091da177e4SLinus Torvalds if (substream->ops->silence) { 1101da177e4SLinus Torvalds for (c = 0; c < channels; ++c) { 1111da177e4SLinus Torvalds int err; 1121da177e4SLinus Torvalds err = substream->ops->silence(substream, c, ofs, transfer); 1137eaa943cSTakashi Iwai snd_BUG_ON(err < 0); 1141da177e4SLinus Torvalds } 1151da177e4SLinus Torvalds } else { 1161da177e4SLinus Torvalds size_t dma_csize = runtime->dma_bytes / channels; 1171da177e4SLinus Torvalds for (c = 0; c < channels; ++c) { 1181da177e4SLinus Torvalds char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, ofs); 1191da177e4SLinus Torvalds snd_pcm_format_set_silence(runtime->format, hwbuf, transfer); 1201da177e4SLinus Torvalds } 1211da177e4SLinus Torvalds } 1221da177e4SLinus Torvalds } 1231da177e4SLinus Torvalds runtime->silence_filled += transfer; 1241da177e4SLinus Torvalds frames -= transfer; 1251da177e4SLinus Torvalds ofs = 0; 1261da177e4SLinus Torvalds } 1271da177e4SLinus Torvalds } 1281da177e4SLinus Torvalds 129ed3da3d9STakashi Iwai #ifdef CONFIG_SND_PCM_XRUN_DEBUG 130ed3da3d9STakashi Iwai #define xrun_debug(substream) ((substream)->pstr->xrun_debug) 131ed3da3d9STakashi Iwai #else 132ed3da3d9STakashi Iwai #define xrun_debug(substream) 0 133ed3da3d9STakashi Iwai #endif 134ed3da3d9STakashi Iwai 135ed3da3d9STakashi Iwai #define dump_stack_on_xrun(substream) do { \ 136ed3da3d9STakashi Iwai if (xrun_debug(substream) > 1) \ 137ed3da3d9STakashi Iwai dump_stack(); \ 138ed3da3d9STakashi Iwai } while (0) 139ed3da3d9STakashi Iwai 140877211f5STakashi Iwai static void xrun(struct snd_pcm_substream *substream) 1411da177e4SLinus Torvalds { 1421da177e4SLinus Torvalds snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); 143ed3da3d9STakashi Iwai if (xrun_debug(substream)) { 1441da177e4SLinus Torvalds snd_printd(KERN_DEBUG "XRUN: pcmC%dD%d%c\n", 1451da177e4SLinus Torvalds substream->pcm->card->number, 1461da177e4SLinus Torvalds substream->pcm->device, 1471da177e4SLinus Torvalds substream->stream ? 'c' : 'p'); 148ed3da3d9STakashi Iwai dump_stack_on_xrun(substream); 1491da177e4SLinus Torvalds } 1501da177e4SLinus Torvalds } 1511da177e4SLinus Torvalds 15298204646STakashi Iwai static snd_pcm_uframes_t 15398204646STakashi Iwai snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream, 154877211f5STakashi Iwai struct snd_pcm_runtime *runtime) 1551da177e4SLinus Torvalds { 1561da177e4SLinus Torvalds snd_pcm_uframes_t pos; 1571da177e4SLinus Torvalds 1588c121586SJaroslav Kysela if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) 1598c121586SJaroslav Kysela snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); 1601da177e4SLinus Torvalds pos = substream->ops->pointer(substream); 1611da177e4SLinus Torvalds if (pos == SNDRV_PCM_POS_XRUN) 1621da177e4SLinus Torvalds return pos; /* XRUN */ 1631da177e4SLinus Torvalds if (pos >= runtime->buffer_size) { 1645f513e11STakashi Iwai if (printk_ratelimit()) { 1655f513e11STakashi Iwai snd_printd(KERN_ERR "BUG: stream = %i, pos = 0x%lx, " 1665f513e11STakashi Iwai "buffer size = 0x%lx, period size = 0x%lx\n", 1675f513e11STakashi Iwai substream->stream, pos, runtime->buffer_size, 1685f513e11STakashi Iwai runtime->period_size); 1697c22f1aaSTakashi Iwai } 1705f513e11STakashi Iwai pos = 0; 1715f513e11STakashi Iwai } 1721da177e4SLinus Torvalds pos -= pos % runtime->min_align; 1731da177e4SLinus Torvalds return pos; 1741da177e4SLinus Torvalds } 1751da177e4SLinus Torvalds 17698204646STakashi Iwai static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream, 177877211f5STakashi Iwai struct snd_pcm_runtime *runtime) 1781da177e4SLinus Torvalds { 1791da177e4SLinus Torvalds snd_pcm_uframes_t avail; 1801da177e4SLinus Torvalds 1811da177e4SLinus Torvalds if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 1821da177e4SLinus Torvalds avail = snd_pcm_playback_avail(runtime); 1831da177e4SLinus Torvalds else 1841da177e4SLinus Torvalds avail = snd_pcm_capture_avail(runtime); 1851da177e4SLinus Torvalds if (avail > runtime->avail_max) 1861da177e4SLinus Torvalds runtime->avail_max = avail; 1871da177e4SLinus Torvalds if (avail >= runtime->stop_threshold) { 1881da177e4SLinus Torvalds if (substream->runtime->status->state == SNDRV_PCM_STATE_DRAINING) 1891da177e4SLinus Torvalds snd_pcm_drain_done(substream); 1901da177e4SLinus Torvalds else 1911da177e4SLinus Torvalds xrun(substream); 1921da177e4SLinus Torvalds return -EPIPE; 1931da177e4SLinus Torvalds } 1941da177e4SLinus Torvalds if (avail >= runtime->control->avail_min) 1951da177e4SLinus Torvalds wake_up(&runtime->sleep); 1961da177e4SLinus Torvalds return 0; 1971da177e4SLinus Torvalds } 1981da177e4SLinus Torvalds 199ed3da3d9STakashi Iwai #define hw_ptr_error(substream, fmt, args...) \ 200ed3da3d9STakashi Iwai do { \ 201ed3da3d9STakashi Iwai if (xrun_debug(substream)) { \ 202ed3da3d9STakashi Iwai if (printk_ratelimit()) { \ 203cad377acSTakashi Iwai snd_printd("PCM: " fmt, ##args); \ 204ed3da3d9STakashi Iwai } \ 205ed3da3d9STakashi Iwai dump_stack_on_xrun(substream); \ 206ed3da3d9STakashi Iwai } \ 207ed3da3d9STakashi Iwai } while (0) 208ed3da3d9STakashi Iwai 20998204646STakashi Iwai static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) 2101da177e4SLinus Torvalds { 211877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 2121da177e4SLinus Torvalds snd_pcm_uframes_t pos; 213bbf6ad13SJaroslav Kysela snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_ptr_interrupt, hw_base; 214bbf6ad13SJaroslav Kysela snd_pcm_sframes_t hdelta, delta; 215bbf6ad13SJaroslav Kysela unsigned long jdelta; 2161da177e4SLinus Torvalds 217bbf6ad13SJaroslav Kysela old_hw_ptr = runtime->status->hw_ptr; 2181da177e4SLinus Torvalds pos = snd_pcm_update_hw_ptr_pos(substream, runtime); 2191da177e4SLinus Torvalds if (pos == SNDRV_PCM_POS_XRUN) { 2201da177e4SLinus Torvalds xrun(substream); 2211da177e4SLinus Torvalds return -EPIPE; 2221da177e4SLinus Torvalds } 223ed3da3d9STakashi Iwai hw_base = runtime->hw_ptr_base; 224ed3da3d9STakashi Iwai new_hw_ptr = hw_base + pos; 2251da177e4SLinus Torvalds hw_ptr_interrupt = runtime->hw_ptr_interrupt + runtime->period_size; 226ed3da3d9STakashi Iwai delta = new_hw_ptr - hw_ptr_interrupt; 227ded652f7STakashi Iwai if (hw_ptr_interrupt >= runtime->boundary) { 2288b22d943STakashi Iwai hw_ptr_interrupt -= runtime->boundary; 2298b22d943STakashi Iwai if (hw_base < runtime->boundary / 2) 2308b22d943STakashi Iwai /* hw_base was already lapped; recalc delta */ 231ded652f7STakashi Iwai delta = new_hw_ptr - hw_ptr_interrupt; 232ded652f7STakashi Iwai } 233ed3da3d9STakashi Iwai if (delta < 0) { 234ed3da3d9STakashi Iwai delta += runtime->buffer_size; 235ed3da3d9STakashi Iwai if (delta < 0) { 236ed3da3d9STakashi Iwai hw_ptr_error(substream, 237ed3da3d9STakashi Iwai "Unexpected hw_pointer value " 238ed3da3d9STakashi Iwai "(stream=%i, pos=%ld, intr_ptr=%ld)\n", 239ed3da3d9STakashi Iwai substream->stream, (long)pos, 240ed3da3d9STakashi Iwai (long)hw_ptr_interrupt); 241ed3da3d9STakashi Iwai /* rebase to interrupt position */ 242ed3da3d9STakashi Iwai hw_base = new_hw_ptr = hw_ptr_interrupt; 243ded652f7STakashi Iwai /* align hw_base to buffer_size */ 244ded652f7STakashi Iwai hw_base -= hw_base % runtime->buffer_size; 245ed3da3d9STakashi Iwai delta = 0; 246ed3da3d9STakashi Iwai } else { 247ed3da3d9STakashi Iwai hw_base += runtime->buffer_size; 2488b22d943STakashi Iwai if (hw_base >= runtime->boundary) 249ed3da3d9STakashi Iwai hw_base = 0; 250ed3da3d9STakashi Iwai new_hw_ptr = hw_base + pos; 2511da177e4SLinus Torvalds } 2521da177e4SLinus Torvalds } 253c87d9732STakashi Iwai 254c87d9732STakashi Iwai /* Do jiffies check only in xrun_debug mode */ 255c87d9732STakashi Iwai if (!xrun_debug(substream)) 256c87d9732STakashi Iwai goto no_jiffies_check; 257c87d9732STakashi Iwai 2583e5b5016STakashi Iwai /* Skip the jiffies check for hardwares with BATCH flag. 2593e5b5016STakashi Iwai * Such hardware usually just increases the position at each IRQ, 2603e5b5016STakashi Iwai * thus it can't give any strange position. 2613e5b5016STakashi Iwai */ 2623e5b5016STakashi Iwai if (runtime->hw.info & SNDRV_PCM_INFO_BATCH) 2633e5b5016STakashi Iwai goto no_jiffies_check; 264bbf6ad13SJaroslav Kysela hdelta = new_hw_ptr - old_hw_ptr; 265bbf6ad13SJaroslav Kysela jdelta = jiffies - runtime->hw_ptr_jiffies; 266bbf6ad13SJaroslav Kysela if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) { 267bbf6ad13SJaroslav Kysela delta = jdelta / 268bbf6ad13SJaroslav Kysela (((runtime->period_size * HZ) / runtime->rate) 269bbf6ad13SJaroslav Kysela + HZ/100); 270bbf6ad13SJaroslav Kysela hw_ptr_error(substream, 271bbf6ad13SJaroslav Kysela "hw_ptr skipping! [Q] " 272bbf6ad13SJaroslav Kysela "(pos=%ld, delta=%ld, period=%ld, " 273bbf6ad13SJaroslav Kysela "jdelta=%lu/%lu/%lu)\n", 274bbf6ad13SJaroslav Kysela (long)pos, (long)hdelta, 275bbf6ad13SJaroslav Kysela (long)runtime->period_size, jdelta, 276bbf6ad13SJaroslav Kysela ((hdelta * HZ) / runtime->rate), delta); 277bbf6ad13SJaroslav Kysela hw_ptr_interrupt = runtime->hw_ptr_interrupt + 278bbf6ad13SJaroslav Kysela runtime->period_size * delta; 279bbf6ad13SJaroslav Kysela if (hw_ptr_interrupt >= runtime->boundary) 280bbf6ad13SJaroslav Kysela hw_ptr_interrupt -= runtime->boundary; 281bbf6ad13SJaroslav Kysela /* rebase to interrupt position */ 282bbf6ad13SJaroslav Kysela hw_base = new_hw_ptr = hw_ptr_interrupt; 283bbf6ad13SJaroslav Kysela /* align hw_base to buffer_size */ 284bbf6ad13SJaroslav Kysela hw_base -= hw_base % runtime->buffer_size; 285bbf6ad13SJaroslav Kysela delta = 0; 286bbf6ad13SJaroslav Kysela } 2873e5b5016STakashi Iwai no_jiffies_check: 288bbf6ad13SJaroslav Kysela if (delta > runtime->period_size + runtime->period_size / 2) { 289ed3da3d9STakashi Iwai hw_ptr_error(substream, 290ed3da3d9STakashi Iwai "Lost interrupts? " 291ed3da3d9STakashi Iwai "(stream=%i, delta=%ld, intr_ptr=%ld)\n", 292ed3da3d9STakashi Iwai substream->stream, (long)delta, 293ed3da3d9STakashi Iwai (long)hw_ptr_interrupt); 294ed3da3d9STakashi Iwai /* rebase hw_ptr_interrupt */ 295ed3da3d9STakashi Iwai hw_ptr_interrupt = 296ed3da3d9STakashi Iwai new_hw_ptr - new_hw_ptr % runtime->period_size; 2971da177e4SLinus Torvalds } 2981da177e4SLinus Torvalds if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && 2991da177e4SLinus Torvalds runtime->silence_size > 0) 3001da177e4SLinus Torvalds snd_pcm_playback_silence(substream, new_hw_ptr); 3011da177e4SLinus Torvalds 302ed3da3d9STakashi Iwai runtime->hw_ptr_base = hw_base; 3031da177e4SLinus Torvalds runtime->status->hw_ptr = new_hw_ptr; 304bbf6ad13SJaroslav Kysela runtime->hw_ptr_jiffies = jiffies; 305ed3da3d9STakashi Iwai runtime->hw_ptr_interrupt = hw_ptr_interrupt; 3061da177e4SLinus Torvalds 3071da177e4SLinus Torvalds return snd_pcm_update_hw_ptr_post(substream, runtime); 3081da177e4SLinus Torvalds } 3091da177e4SLinus Torvalds 3101da177e4SLinus Torvalds /* CAUTION: call it with irq disabled */ 311877211f5STakashi Iwai int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) 3121da177e4SLinus Torvalds { 313877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 3141da177e4SLinus Torvalds snd_pcm_uframes_t pos; 315ed3da3d9STakashi Iwai snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base; 3161da177e4SLinus Torvalds snd_pcm_sframes_t delta; 317bbf6ad13SJaroslav Kysela unsigned long jdelta; 3181da177e4SLinus Torvalds 3191da177e4SLinus Torvalds old_hw_ptr = runtime->status->hw_ptr; 3201da177e4SLinus Torvalds pos = snd_pcm_update_hw_ptr_pos(substream, runtime); 3211da177e4SLinus Torvalds if (pos == SNDRV_PCM_POS_XRUN) { 3221da177e4SLinus Torvalds xrun(substream); 3231da177e4SLinus Torvalds return -EPIPE; 3241da177e4SLinus Torvalds } 325ed3da3d9STakashi Iwai hw_base = runtime->hw_ptr_base; 326ed3da3d9STakashi Iwai new_hw_ptr = hw_base + pos; 3271da177e4SLinus Torvalds 328ed3da3d9STakashi Iwai delta = new_hw_ptr - old_hw_ptr; 329bbf6ad13SJaroslav Kysela jdelta = jiffies - runtime->hw_ptr_jiffies; 330ed3da3d9STakashi Iwai if (delta < 0) { 331ed3da3d9STakashi Iwai delta += runtime->buffer_size; 332ed3da3d9STakashi Iwai if (delta < 0) { 333ed3da3d9STakashi Iwai hw_ptr_error(substream, 334ed3da3d9STakashi Iwai "Unexpected hw_pointer value [2] " 335bbf6ad13SJaroslav Kysela "(stream=%i, pos=%ld, old_ptr=%ld, jdelta=%li)\n", 336ed3da3d9STakashi Iwai substream->stream, (long)pos, 337bbf6ad13SJaroslav Kysela (long)old_hw_ptr, jdelta); 3381da177e4SLinus Torvalds return 0; 3391da177e4SLinus Torvalds } 340ed3da3d9STakashi Iwai hw_base += runtime->buffer_size; 3418b22d943STakashi Iwai if (hw_base >= runtime->boundary) 342ed3da3d9STakashi Iwai hw_base = 0; 343ed3da3d9STakashi Iwai new_hw_ptr = hw_base + pos; 344ed3da3d9STakashi Iwai } 345c87d9732STakashi Iwai /* Do jiffies check only in xrun_debug mode */ 346c87d9732STakashi Iwai if (xrun_debug(substream) && 347c87d9732STakashi Iwai ((delta * HZ) / runtime->rate) > jdelta + HZ/100) { 348ed3da3d9STakashi Iwai hw_ptr_error(substream, 349ed3da3d9STakashi Iwai "hw_ptr skipping! " 350bbf6ad13SJaroslav Kysela "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu)\n", 351ed3da3d9STakashi Iwai (long)pos, (long)delta, 352bbf6ad13SJaroslav Kysela (long)runtime->period_size, jdelta, 353bbf6ad13SJaroslav Kysela ((delta * HZ) / runtime->rate)); 354ed3da3d9STakashi Iwai return 0; 3551da177e4SLinus Torvalds } 3561da177e4SLinus Torvalds if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && 3571da177e4SLinus Torvalds runtime->silence_size > 0) 3581da177e4SLinus Torvalds snd_pcm_playback_silence(substream, new_hw_ptr); 3591da177e4SLinus Torvalds 360ed3da3d9STakashi Iwai runtime->hw_ptr_base = hw_base; 3611da177e4SLinus Torvalds runtime->status->hw_ptr = new_hw_ptr; 362bbf6ad13SJaroslav Kysela runtime->hw_ptr_jiffies = jiffies; 3631da177e4SLinus Torvalds 3641da177e4SLinus Torvalds return snd_pcm_update_hw_ptr_post(substream, runtime); 3651da177e4SLinus Torvalds } 3661da177e4SLinus Torvalds 3671da177e4SLinus Torvalds /** 3681da177e4SLinus Torvalds * snd_pcm_set_ops - set the PCM operators 3691da177e4SLinus Torvalds * @pcm: the pcm instance 3701da177e4SLinus Torvalds * @direction: stream direction, SNDRV_PCM_STREAM_XXX 3711da177e4SLinus Torvalds * @ops: the operator table 3721da177e4SLinus Torvalds * 3731da177e4SLinus Torvalds * Sets the given PCM operators to the pcm instance. 3741da177e4SLinus Torvalds */ 375877211f5STakashi Iwai void snd_pcm_set_ops(struct snd_pcm *pcm, int direction, struct snd_pcm_ops *ops) 3761da177e4SLinus Torvalds { 377877211f5STakashi Iwai struct snd_pcm_str *stream = &pcm->streams[direction]; 378877211f5STakashi Iwai struct snd_pcm_substream *substream; 3791da177e4SLinus Torvalds 3801da177e4SLinus Torvalds for (substream = stream->substream; substream != NULL; substream = substream->next) 3811da177e4SLinus Torvalds substream->ops = ops; 3821da177e4SLinus Torvalds } 3831da177e4SLinus Torvalds 384e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_set_ops); 3851da177e4SLinus Torvalds 3861da177e4SLinus Torvalds /** 3871da177e4SLinus Torvalds * snd_pcm_sync - set the PCM sync id 3881da177e4SLinus Torvalds * @substream: the pcm substream 3891da177e4SLinus Torvalds * 3901da177e4SLinus Torvalds * Sets the PCM sync identifier for the card. 3911da177e4SLinus Torvalds */ 392877211f5STakashi Iwai void snd_pcm_set_sync(struct snd_pcm_substream *substream) 3931da177e4SLinus Torvalds { 394877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 3951da177e4SLinus Torvalds 3961da177e4SLinus Torvalds runtime->sync.id32[0] = substream->pcm->card->number; 3971da177e4SLinus Torvalds runtime->sync.id32[1] = -1; 3981da177e4SLinus Torvalds runtime->sync.id32[2] = -1; 3991da177e4SLinus Torvalds runtime->sync.id32[3] = -1; 4001da177e4SLinus Torvalds } 4011da177e4SLinus Torvalds 402e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_set_sync); 403e88e8ae6STakashi Iwai 4041da177e4SLinus Torvalds /* 4051da177e4SLinus Torvalds * Standard ioctl routine 4061da177e4SLinus Torvalds */ 4071da177e4SLinus Torvalds 4081da177e4SLinus Torvalds static inline unsigned int div32(unsigned int a, unsigned int b, 4091da177e4SLinus Torvalds unsigned int *r) 4101da177e4SLinus Torvalds { 4111da177e4SLinus Torvalds if (b == 0) { 4121da177e4SLinus Torvalds *r = 0; 4131da177e4SLinus Torvalds return UINT_MAX; 4141da177e4SLinus Torvalds } 4151da177e4SLinus Torvalds *r = a % b; 4161da177e4SLinus Torvalds return a / b; 4171da177e4SLinus Torvalds } 4181da177e4SLinus Torvalds 4191da177e4SLinus Torvalds static inline unsigned int div_down(unsigned int a, unsigned int b) 4201da177e4SLinus Torvalds { 4211da177e4SLinus Torvalds if (b == 0) 4221da177e4SLinus Torvalds return UINT_MAX; 4231da177e4SLinus Torvalds return a / b; 4241da177e4SLinus Torvalds } 4251da177e4SLinus Torvalds 4261da177e4SLinus Torvalds static inline unsigned int div_up(unsigned int a, unsigned int b) 4271da177e4SLinus Torvalds { 4281da177e4SLinus Torvalds unsigned int r; 4291da177e4SLinus Torvalds unsigned int q; 4301da177e4SLinus Torvalds if (b == 0) 4311da177e4SLinus Torvalds return UINT_MAX; 4321da177e4SLinus Torvalds q = div32(a, b, &r); 4331da177e4SLinus Torvalds if (r) 4341da177e4SLinus Torvalds ++q; 4351da177e4SLinus Torvalds return q; 4361da177e4SLinus Torvalds } 4371da177e4SLinus Torvalds 4381da177e4SLinus Torvalds static inline unsigned int mul(unsigned int a, unsigned int b) 4391da177e4SLinus Torvalds { 4401da177e4SLinus Torvalds if (a == 0) 4411da177e4SLinus Torvalds return 0; 4421da177e4SLinus Torvalds if (div_down(UINT_MAX, a) < b) 4431da177e4SLinus Torvalds return UINT_MAX; 4441da177e4SLinus Torvalds return a * b; 4451da177e4SLinus Torvalds } 4461da177e4SLinus Torvalds 4471da177e4SLinus Torvalds static inline unsigned int muldiv32(unsigned int a, unsigned int b, 4481da177e4SLinus Torvalds unsigned int c, unsigned int *r) 4491da177e4SLinus Torvalds { 4501da177e4SLinus Torvalds u_int64_t n = (u_int64_t) a * b; 4511da177e4SLinus Torvalds if (c == 0) { 4527eaa943cSTakashi Iwai snd_BUG_ON(!n); 4531da177e4SLinus Torvalds *r = 0; 4541da177e4SLinus Torvalds return UINT_MAX; 4551da177e4SLinus Torvalds } 4563f7440a6STakashi Iwai n = div_u64_rem(n, c, r); 4571da177e4SLinus Torvalds if (n >= UINT_MAX) { 4581da177e4SLinus Torvalds *r = 0; 4591da177e4SLinus Torvalds return UINT_MAX; 4601da177e4SLinus Torvalds } 4611da177e4SLinus Torvalds return n; 4621da177e4SLinus Torvalds } 4631da177e4SLinus Torvalds 4641da177e4SLinus Torvalds /** 4651da177e4SLinus Torvalds * snd_interval_refine - refine the interval value of configurator 4661da177e4SLinus Torvalds * @i: the interval value to refine 4671da177e4SLinus Torvalds * @v: the interval value to refer to 4681da177e4SLinus Torvalds * 4691da177e4SLinus Torvalds * Refines the interval value with the reference value. 4701da177e4SLinus Torvalds * The interval is changed to the range satisfying both intervals. 4711da177e4SLinus Torvalds * The interval status (min, max, integer, etc.) are evaluated. 4721da177e4SLinus Torvalds * 4731da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 4741da177e4SLinus Torvalds */ 475877211f5STakashi Iwai int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v) 4761da177e4SLinus Torvalds { 4771da177e4SLinus Torvalds int changed = 0; 4787eaa943cSTakashi Iwai if (snd_BUG_ON(snd_interval_empty(i))) 4797eaa943cSTakashi Iwai return -EINVAL; 4801da177e4SLinus Torvalds if (i->min < v->min) { 4811da177e4SLinus Torvalds i->min = v->min; 4821da177e4SLinus Torvalds i->openmin = v->openmin; 4831da177e4SLinus Torvalds changed = 1; 4841da177e4SLinus Torvalds } else if (i->min == v->min && !i->openmin && v->openmin) { 4851da177e4SLinus Torvalds i->openmin = 1; 4861da177e4SLinus Torvalds changed = 1; 4871da177e4SLinus Torvalds } 4881da177e4SLinus Torvalds if (i->max > v->max) { 4891da177e4SLinus Torvalds i->max = v->max; 4901da177e4SLinus Torvalds i->openmax = v->openmax; 4911da177e4SLinus Torvalds changed = 1; 4921da177e4SLinus Torvalds } else if (i->max == v->max && !i->openmax && v->openmax) { 4931da177e4SLinus Torvalds i->openmax = 1; 4941da177e4SLinus Torvalds changed = 1; 4951da177e4SLinus Torvalds } 4961da177e4SLinus Torvalds if (!i->integer && v->integer) { 4971da177e4SLinus Torvalds i->integer = 1; 4981da177e4SLinus Torvalds changed = 1; 4991da177e4SLinus Torvalds } 5001da177e4SLinus Torvalds if (i->integer) { 5011da177e4SLinus Torvalds if (i->openmin) { 5021da177e4SLinus Torvalds i->min++; 5031da177e4SLinus Torvalds i->openmin = 0; 5041da177e4SLinus Torvalds } 5051da177e4SLinus Torvalds if (i->openmax) { 5061da177e4SLinus Torvalds i->max--; 5071da177e4SLinus Torvalds i->openmax = 0; 5081da177e4SLinus Torvalds } 5091da177e4SLinus Torvalds } else if (!i->openmin && !i->openmax && i->min == i->max) 5101da177e4SLinus Torvalds i->integer = 1; 5111da177e4SLinus Torvalds if (snd_interval_checkempty(i)) { 5121da177e4SLinus Torvalds snd_interval_none(i); 5131da177e4SLinus Torvalds return -EINVAL; 5141da177e4SLinus Torvalds } 5151da177e4SLinus Torvalds return changed; 5161da177e4SLinus Torvalds } 5171da177e4SLinus Torvalds 518e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_interval_refine); 519e88e8ae6STakashi Iwai 520877211f5STakashi Iwai static int snd_interval_refine_first(struct snd_interval *i) 5211da177e4SLinus Torvalds { 5227eaa943cSTakashi Iwai if (snd_BUG_ON(snd_interval_empty(i))) 5237eaa943cSTakashi Iwai return -EINVAL; 5241da177e4SLinus Torvalds if (snd_interval_single(i)) 5251da177e4SLinus Torvalds return 0; 5261da177e4SLinus Torvalds i->max = i->min; 5271da177e4SLinus Torvalds i->openmax = i->openmin; 5281da177e4SLinus Torvalds if (i->openmax) 5291da177e4SLinus Torvalds i->max++; 5301da177e4SLinus Torvalds return 1; 5311da177e4SLinus Torvalds } 5321da177e4SLinus Torvalds 533877211f5STakashi Iwai static int snd_interval_refine_last(struct snd_interval *i) 5341da177e4SLinus Torvalds { 5357eaa943cSTakashi Iwai if (snd_BUG_ON(snd_interval_empty(i))) 5367eaa943cSTakashi Iwai return -EINVAL; 5371da177e4SLinus Torvalds if (snd_interval_single(i)) 5381da177e4SLinus Torvalds return 0; 5391da177e4SLinus Torvalds i->min = i->max; 5401da177e4SLinus Torvalds i->openmin = i->openmax; 5411da177e4SLinus Torvalds if (i->openmin) 5421da177e4SLinus Torvalds i->min--; 5431da177e4SLinus Torvalds return 1; 5441da177e4SLinus Torvalds } 5451da177e4SLinus Torvalds 546877211f5STakashi Iwai void snd_interval_mul(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c) 5471da177e4SLinus Torvalds { 5481da177e4SLinus Torvalds if (a->empty || b->empty) { 5491da177e4SLinus Torvalds snd_interval_none(c); 5501da177e4SLinus Torvalds return; 5511da177e4SLinus Torvalds } 5521da177e4SLinus Torvalds c->empty = 0; 5531da177e4SLinus Torvalds c->min = mul(a->min, b->min); 5541da177e4SLinus Torvalds c->openmin = (a->openmin || b->openmin); 5551da177e4SLinus Torvalds c->max = mul(a->max, b->max); 5561da177e4SLinus Torvalds c->openmax = (a->openmax || b->openmax); 5571da177e4SLinus Torvalds c->integer = (a->integer && b->integer); 5581da177e4SLinus Torvalds } 5591da177e4SLinus Torvalds 5601da177e4SLinus Torvalds /** 5611da177e4SLinus Torvalds * snd_interval_div - refine the interval value with division 562df8db936STakashi Iwai * @a: dividend 563df8db936STakashi Iwai * @b: divisor 564df8db936STakashi Iwai * @c: quotient 5651da177e4SLinus Torvalds * 5661da177e4SLinus Torvalds * c = a / b 5671da177e4SLinus Torvalds * 5681da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 5691da177e4SLinus Torvalds */ 570877211f5STakashi Iwai void snd_interval_div(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *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 = div32(a->min, b->max, &r); 5791da177e4SLinus Torvalds c->openmin = (r || a->openmin || b->openmax); 5801da177e4SLinus Torvalds if (b->min > 0) { 5811da177e4SLinus Torvalds c->max = div32(a->max, b->min, &r); 5821da177e4SLinus Torvalds if (r) { 5831da177e4SLinus Torvalds c->max++; 5841da177e4SLinus Torvalds c->openmax = 1; 5851da177e4SLinus Torvalds } else 5861da177e4SLinus Torvalds c->openmax = (a->openmax || b->openmin); 5871da177e4SLinus Torvalds } else { 5881da177e4SLinus Torvalds c->max = UINT_MAX; 5891da177e4SLinus Torvalds c->openmax = 0; 5901da177e4SLinus Torvalds } 5911da177e4SLinus Torvalds c->integer = 0; 5921da177e4SLinus Torvalds } 5931da177e4SLinus Torvalds 5941da177e4SLinus Torvalds /** 5951da177e4SLinus Torvalds * snd_interval_muldivk - refine the interval value 596df8db936STakashi Iwai * @a: dividend 1 597df8db936STakashi Iwai * @b: dividend 2 598df8db936STakashi Iwai * @k: divisor (as integer) 599df8db936STakashi Iwai * @c: result 6001da177e4SLinus Torvalds * 6011da177e4SLinus Torvalds * c = a * b / k 6021da177e4SLinus Torvalds * 6031da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 6041da177e4SLinus Torvalds */ 605877211f5STakashi Iwai void snd_interval_muldivk(const struct snd_interval *a, const struct snd_interval *b, 606877211f5STakashi Iwai unsigned int k, struct snd_interval *c) 6071da177e4SLinus Torvalds { 6081da177e4SLinus Torvalds unsigned int r; 6091da177e4SLinus Torvalds if (a->empty || b->empty) { 6101da177e4SLinus Torvalds snd_interval_none(c); 6111da177e4SLinus Torvalds return; 6121da177e4SLinus Torvalds } 6131da177e4SLinus Torvalds c->empty = 0; 6141da177e4SLinus Torvalds c->min = muldiv32(a->min, b->min, k, &r); 6151da177e4SLinus Torvalds c->openmin = (r || a->openmin || b->openmin); 6161da177e4SLinus Torvalds c->max = muldiv32(a->max, b->max, k, &r); 6171da177e4SLinus Torvalds if (r) { 6181da177e4SLinus Torvalds c->max++; 6191da177e4SLinus Torvalds c->openmax = 1; 6201da177e4SLinus Torvalds } else 6211da177e4SLinus Torvalds c->openmax = (a->openmax || b->openmax); 6221da177e4SLinus Torvalds c->integer = 0; 6231da177e4SLinus Torvalds } 6241da177e4SLinus Torvalds 6251da177e4SLinus Torvalds /** 6261da177e4SLinus Torvalds * snd_interval_mulkdiv - refine the interval value 627df8db936STakashi Iwai * @a: dividend 1 628df8db936STakashi Iwai * @k: dividend 2 (as integer) 629df8db936STakashi Iwai * @b: divisor 630df8db936STakashi Iwai * @c: result 6311da177e4SLinus Torvalds * 6321da177e4SLinus Torvalds * c = a * k / b 6331da177e4SLinus Torvalds * 6341da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 6351da177e4SLinus Torvalds */ 636877211f5STakashi Iwai void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k, 637877211f5STakashi Iwai const struct snd_interval *b, struct snd_interval *c) 6381da177e4SLinus Torvalds { 6391da177e4SLinus Torvalds unsigned int r; 6401da177e4SLinus Torvalds if (a->empty || b->empty) { 6411da177e4SLinus Torvalds snd_interval_none(c); 6421da177e4SLinus Torvalds return; 6431da177e4SLinus Torvalds } 6441da177e4SLinus Torvalds c->empty = 0; 6451da177e4SLinus Torvalds c->min = muldiv32(a->min, k, b->max, &r); 6461da177e4SLinus Torvalds c->openmin = (r || a->openmin || b->openmax); 6471da177e4SLinus Torvalds if (b->min > 0) { 6481da177e4SLinus Torvalds c->max = muldiv32(a->max, k, b->min, &r); 6491da177e4SLinus Torvalds if (r) { 6501da177e4SLinus Torvalds c->max++; 6511da177e4SLinus Torvalds c->openmax = 1; 6521da177e4SLinus Torvalds } else 6531da177e4SLinus Torvalds c->openmax = (a->openmax || b->openmin); 6541da177e4SLinus Torvalds } else { 6551da177e4SLinus Torvalds c->max = UINT_MAX; 6561da177e4SLinus Torvalds c->openmax = 0; 6571da177e4SLinus Torvalds } 6581da177e4SLinus Torvalds c->integer = 0; 6591da177e4SLinus Torvalds } 6601da177e4SLinus Torvalds 6611da177e4SLinus Torvalds /* ---- */ 6621da177e4SLinus Torvalds 6631da177e4SLinus Torvalds 6641da177e4SLinus Torvalds /** 6651da177e4SLinus Torvalds * snd_interval_ratnum - refine the interval value 666df8db936STakashi Iwai * @i: interval to refine 667df8db936STakashi Iwai * @rats_count: number of ratnum_t 668df8db936STakashi Iwai * @rats: ratnum_t array 669df8db936STakashi Iwai * @nump: pointer to store the resultant numerator 670df8db936STakashi Iwai * @denp: pointer to store the resultant denominator 6711da177e4SLinus Torvalds * 6721da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 6731da177e4SLinus Torvalds */ 674877211f5STakashi Iwai int snd_interval_ratnum(struct snd_interval *i, 675877211f5STakashi Iwai unsigned int rats_count, struct snd_ratnum *rats, 6761da177e4SLinus Torvalds unsigned int *nump, unsigned int *denp) 6771da177e4SLinus Torvalds { 6781da177e4SLinus Torvalds unsigned int best_num, best_diff, best_den; 6791da177e4SLinus Torvalds unsigned int k; 680877211f5STakashi Iwai struct snd_interval t; 6811da177e4SLinus Torvalds int err; 6821da177e4SLinus Torvalds 6831da177e4SLinus Torvalds best_num = best_den = best_diff = 0; 6841da177e4SLinus Torvalds for (k = 0; k < rats_count; ++k) { 6851da177e4SLinus Torvalds unsigned int num = rats[k].num; 6861da177e4SLinus Torvalds unsigned int den; 6871da177e4SLinus Torvalds unsigned int q = i->min; 6881da177e4SLinus Torvalds int diff; 6891da177e4SLinus Torvalds if (q == 0) 6901da177e4SLinus Torvalds q = 1; 6911da177e4SLinus Torvalds den = div_down(num, q); 6921da177e4SLinus Torvalds if (den < rats[k].den_min) 6931da177e4SLinus Torvalds continue; 6941da177e4SLinus Torvalds if (den > rats[k].den_max) 6951da177e4SLinus Torvalds den = rats[k].den_max; 6961da177e4SLinus Torvalds else { 6971da177e4SLinus Torvalds unsigned int r; 6981da177e4SLinus Torvalds r = (den - rats[k].den_min) % rats[k].den_step; 6991da177e4SLinus Torvalds if (r != 0) 7001da177e4SLinus Torvalds den -= r; 7011da177e4SLinus Torvalds } 7021da177e4SLinus Torvalds diff = num - q * den; 7031da177e4SLinus Torvalds if (best_num == 0 || 7041da177e4SLinus Torvalds diff * best_den < best_diff * den) { 7051da177e4SLinus Torvalds best_diff = diff; 7061da177e4SLinus Torvalds best_den = den; 7071da177e4SLinus Torvalds best_num = num; 7081da177e4SLinus Torvalds } 7091da177e4SLinus Torvalds } 7101da177e4SLinus Torvalds if (best_den == 0) { 7111da177e4SLinus Torvalds i->empty = 1; 7121da177e4SLinus Torvalds return -EINVAL; 7131da177e4SLinus Torvalds } 7141da177e4SLinus Torvalds t.min = div_down(best_num, best_den); 7151da177e4SLinus Torvalds t.openmin = !!(best_num % best_den); 7161da177e4SLinus Torvalds 7171da177e4SLinus Torvalds best_num = best_den = best_diff = 0; 7181da177e4SLinus Torvalds for (k = 0; k < rats_count; ++k) { 7191da177e4SLinus Torvalds unsigned int num = rats[k].num; 7201da177e4SLinus Torvalds unsigned int den; 7211da177e4SLinus Torvalds unsigned int q = i->max; 7221da177e4SLinus Torvalds int diff; 7231da177e4SLinus Torvalds if (q == 0) { 7241da177e4SLinus Torvalds i->empty = 1; 7251da177e4SLinus Torvalds return -EINVAL; 7261da177e4SLinus Torvalds } 7271da177e4SLinus Torvalds den = div_up(num, q); 7281da177e4SLinus Torvalds if (den > rats[k].den_max) 7291da177e4SLinus Torvalds continue; 7301da177e4SLinus Torvalds if (den < rats[k].den_min) 7311da177e4SLinus Torvalds den = rats[k].den_min; 7321da177e4SLinus Torvalds else { 7331da177e4SLinus Torvalds unsigned int r; 7341da177e4SLinus Torvalds r = (den - rats[k].den_min) % rats[k].den_step; 7351da177e4SLinus Torvalds if (r != 0) 7361da177e4SLinus Torvalds den += rats[k].den_step - r; 7371da177e4SLinus Torvalds } 7381da177e4SLinus Torvalds diff = q * den - num; 7391da177e4SLinus Torvalds if (best_num == 0 || 7401da177e4SLinus Torvalds diff * best_den < best_diff * den) { 7411da177e4SLinus Torvalds best_diff = diff; 7421da177e4SLinus Torvalds best_den = den; 7431da177e4SLinus Torvalds best_num = num; 7441da177e4SLinus Torvalds } 7451da177e4SLinus Torvalds } 7461da177e4SLinus Torvalds if (best_den == 0) { 7471da177e4SLinus Torvalds i->empty = 1; 7481da177e4SLinus Torvalds return -EINVAL; 7491da177e4SLinus Torvalds } 7501da177e4SLinus Torvalds t.max = div_up(best_num, best_den); 7511da177e4SLinus Torvalds t.openmax = !!(best_num % best_den); 7521da177e4SLinus Torvalds t.integer = 0; 7531da177e4SLinus Torvalds err = snd_interval_refine(i, &t); 7541da177e4SLinus Torvalds if (err < 0) 7551da177e4SLinus Torvalds return err; 7561da177e4SLinus Torvalds 7571da177e4SLinus Torvalds if (snd_interval_single(i)) { 7581da177e4SLinus Torvalds if (nump) 7591da177e4SLinus Torvalds *nump = best_num; 7601da177e4SLinus Torvalds if (denp) 7611da177e4SLinus Torvalds *denp = best_den; 7621da177e4SLinus Torvalds } 7631da177e4SLinus Torvalds return err; 7641da177e4SLinus Torvalds } 7651da177e4SLinus Torvalds 766e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_interval_ratnum); 767e88e8ae6STakashi Iwai 7681da177e4SLinus Torvalds /** 7691da177e4SLinus Torvalds * snd_interval_ratden - refine the interval value 770df8db936STakashi Iwai * @i: interval to refine 771877211f5STakashi Iwai * @rats_count: number of struct ratden 772877211f5STakashi Iwai * @rats: struct ratden array 773df8db936STakashi Iwai * @nump: pointer to store the resultant numerator 774df8db936STakashi Iwai * @denp: pointer to store the resultant denominator 7751da177e4SLinus Torvalds * 7761da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 7771da177e4SLinus Torvalds */ 778877211f5STakashi Iwai static int snd_interval_ratden(struct snd_interval *i, 779877211f5STakashi Iwai unsigned int rats_count, struct snd_ratden *rats, 7801da177e4SLinus Torvalds unsigned int *nump, unsigned int *denp) 7811da177e4SLinus Torvalds { 7821da177e4SLinus Torvalds unsigned int best_num, best_diff, best_den; 7831da177e4SLinus Torvalds unsigned int k; 784877211f5STakashi Iwai struct snd_interval t; 7851da177e4SLinus Torvalds int err; 7861da177e4SLinus Torvalds 7871da177e4SLinus Torvalds best_num = best_den = best_diff = 0; 7881da177e4SLinus Torvalds for (k = 0; k < rats_count; ++k) { 7891da177e4SLinus Torvalds unsigned int num; 7901da177e4SLinus Torvalds unsigned int den = rats[k].den; 7911da177e4SLinus Torvalds unsigned int q = i->min; 7921da177e4SLinus Torvalds int diff; 7931da177e4SLinus Torvalds num = mul(q, den); 7941da177e4SLinus Torvalds if (num > rats[k].num_max) 7951da177e4SLinus Torvalds continue; 7961da177e4SLinus Torvalds if (num < rats[k].num_min) 7971da177e4SLinus Torvalds num = rats[k].num_max; 7981da177e4SLinus Torvalds else { 7991da177e4SLinus Torvalds unsigned int r; 8001da177e4SLinus Torvalds r = (num - rats[k].num_min) % rats[k].num_step; 8011da177e4SLinus Torvalds if (r != 0) 8021da177e4SLinus Torvalds num += rats[k].num_step - r; 8031da177e4SLinus Torvalds } 8041da177e4SLinus Torvalds diff = num - q * den; 8051da177e4SLinus Torvalds if (best_num == 0 || 8061da177e4SLinus Torvalds diff * best_den < best_diff * den) { 8071da177e4SLinus Torvalds best_diff = diff; 8081da177e4SLinus Torvalds best_den = den; 8091da177e4SLinus Torvalds best_num = num; 8101da177e4SLinus Torvalds } 8111da177e4SLinus Torvalds } 8121da177e4SLinus Torvalds if (best_den == 0) { 8131da177e4SLinus Torvalds i->empty = 1; 8141da177e4SLinus Torvalds return -EINVAL; 8151da177e4SLinus Torvalds } 8161da177e4SLinus Torvalds t.min = div_down(best_num, best_den); 8171da177e4SLinus Torvalds t.openmin = !!(best_num % best_den); 8181da177e4SLinus Torvalds 8191da177e4SLinus Torvalds best_num = best_den = best_diff = 0; 8201da177e4SLinus Torvalds for (k = 0; k < rats_count; ++k) { 8211da177e4SLinus Torvalds unsigned int num; 8221da177e4SLinus Torvalds unsigned int den = rats[k].den; 8231da177e4SLinus Torvalds unsigned int q = i->max; 8241da177e4SLinus Torvalds int diff; 8251da177e4SLinus Torvalds num = mul(q, den); 8261da177e4SLinus Torvalds if (num < rats[k].num_min) 8271da177e4SLinus Torvalds continue; 8281da177e4SLinus Torvalds if (num > rats[k].num_max) 8291da177e4SLinus Torvalds num = rats[k].num_max; 8301da177e4SLinus Torvalds else { 8311da177e4SLinus Torvalds unsigned int r; 8321da177e4SLinus Torvalds r = (num - rats[k].num_min) % rats[k].num_step; 8331da177e4SLinus Torvalds if (r != 0) 8341da177e4SLinus Torvalds num -= r; 8351da177e4SLinus Torvalds } 8361da177e4SLinus Torvalds diff = q * den - num; 8371da177e4SLinus Torvalds if (best_num == 0 || 8381da177e4SLinus Torvalds diff * best_den < best_diff * den) { 8391da177e4SLinus Torvalds best_diff = diff; 8401da177e4SLinus Torvalds best_den = den; 8411da177e4SLinus Torvalds best_num = num; 8421da177e4SLinus Torvalds } 8431da177e4SLinus Torvalds } 8441da177e4SLinus Torvalds if (best_den == 0) { 8451da177e4SLinus Torvalds i->empty = 1; 8461da177e4SLinus Torvalds return -EINVAL; 8471da177e4SLinus Torvalds } 8481da177e4SLinus Torvalds t.max = div_up(best_num, best_den); 8491da177e4SLinus Torvalds t.openmax = !!(best_num % best_den); 8501da177e4SLinus Torvalds t.integer = 0; 8511da177e4SLinus Torvalds err = snd_interval_refine(i, &t); 8521da177e4SLinus Torvalds if (err < 0) 8531da177e4SLinus Torvalds return err; 8541da177e4SLinus Torvalds 8551da177e4SLinus Torvalds if (snd_interval_single(i)) { 8561da177e4SLinus Torvalds if (nump) 8571da177e4SLinus Torvalds *nump = best_num; 8581da177e4SLinus Torvalds if (denp) 8591da177e4SLinus Torvalds *denp = best_den; 8601da177e4SLinus Torvalds } 8611da177e4SLinus Torvalds return err; 8621da177e4SLinus Torvalds } 8631da177e4SLinus Torvalds 8641da177e4SLinus Torvalds /** 8651da177e4SLinus Torvalds * snd_interval_list - refine the interval value from the list 8661da177e4SLinus Torvalds * @i: the interval value to refine 8671da177e4SLinus Torvalds * @count: the number of elements in the list 8681da177e4SLinus Torvalds * @list: the value list 8691da177e4SLinus Torvalds * @mask: the bit-mask to evaluate 8701da177e4SLinus Torvalds * 8711da177e4SLinus Torvalds * Refines the interval value from the list. 8721da177e4SLinus Torvalds * When mask is non-zero, only the elements corresponding to bit 1 are 8731da177e4SLinus Torvalds * evaluated. 8741da177e4SLinus Torvalds * 8751da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 8761da177e4SLinus Torvalds */ 877877211f5STakashi Iwai int snd_interval_list(struct snd_interval *i, unsigned int count, unsigned int *list, unsigned int mask) 8781da177e4SLinus Torvalds { 8791da177e4SLinus Torvalds unsigned int k; 8801da177e4SLinus Torvalds int changed = 0; 8810981a260STakashi Iwai 8820981a260STakashi Iwai if (!count) { 8830981a260STakashi Iwai i->empty = 1; 8840981a260STakashi Iwai return -EINVAL; 8850981a260STakashi Iwai } 8861da177e4SLinus Torvalds for (k = 0; k < count; k++) { 8871da177e4SLinus Torvalds if (mask && !(mask & (1 << k))) 8881da177e4SLinus Torvalds continue; 8891da177e4SLinus Torvalds if (i->min == list[k] && !i->openmin) 8901da177e4SLinus Torvalds goto _l1; 8911da177e4SLinus Torvalds if (i->min < list[k]) { 8921da177e4SLinus Torvalds i->min = list[k]; 8931da177e4SLinus Torvalds i->openmin = 0; 8941da177e4SLinus Torvalds changed = 1; 8951da177e4SLinus Torvalds goto _l1; 8961da177e4SLinus Torvalds } 8971da177e4SLinus Torvalds } 8981da177e4SLinus Torvalds i->empty = 1; 8991da177e4SLinus Torvalds return -EINVAL; 9001da177e4SLinus Torvalds _l1: 9011da177e4SLinus Torvalds for (k = count; k-- > 0;) { 9021da177e4SLinus Torvalds if (mask && !(mask & (1 << k))) 9031da177e4SLinus Torvalds continue; 9041da177e4SLinus Torvalds if (i->max == list[k] && !i->openmax) 9051da177e4SLinus Torvalds goto _l2; 9061da177e4SLinus Torvalds if (i->max > list[k]) { 9071da177e4SLinus Torvalds i->max = list[k]; 9081da177e4SLinus Torvalds i->openmax = 0; 9091da177e4SLinus Torvalds changed = 1; 9101da177e4SLinus Torvalds goto _l2; 9111da177e4SLinus Torvalds } 9121da177e4SLinus Torvalds } 9131da177e4SLinus Torvalds i->empty = 1; 9141da177e4SLinus Torvalds return -EINVAL; 9151da177e4SLinus Torvalds _l2: 9161da177e4SLinus Torvalds if (snd_interval_checkempty(i)) { 9171da177e4SLinus Torvalds i->empty = 1; 9181da177e4SLinus Torvalds return -EINVAL; 9191da177e4SLinus Torvalds } 9201da177e4SLinus Torvalds return changed; 9211da177e4SLinus Torvalds } 9221da177e4SLinus Torvalds 923e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_interval_list); 924e88e8ae6STakashi Iwai 925877211f5STakashi Iwai static int snd_interval_step(struct snd_interval *i, unsigned int min, unsigned int step) 9261da177e4SLinus Torvalds { 9271da177e4SLinus Torvalds unsigned int n; 9281da177e4SLinus Torvalds int changed = 0; 9291da177e4SLinus Torvalds n = (i->min - min) % step; 9301da177e4SLinus Torvalds if (n != 0 || i->openmin) { 9311da177e4SLinus Torvalds i->min += step - n; 9321da177e4SLinus Torvalds changed = 1; 9331da177e4SLinus Torvalds } 9341da177e4SLinus Torvalds n = (i->max - min) % step; 9351da177e4SLinus Torvalds if (n != 0 || i->openmax) { 9361da177e4SLinus Torvalds i->max -= n; 9371da177e4SLinus Torvalds changed = 1; 9381da177e4SLinus Torvalds } 9391da177e4SLinus Torvalds if (snd_interval_checkempty(i)) { 9401da177e4SLinus Torvalds i->empty = 1; 9411da177e4SLinus Torvalds return -EINVAL; 9421da177e4SLinus Torvalds } 9431da177e4SLinus Torvalds return changed; 9441da177e4SLinus Torvalds } 9451da177e4SLinus Torvalds 9461da177e4SLinus Torvalds /* Info constraints helpers */ 9471da177e4SLinus Torvalds 9481da177e4SLinus Torvalds /** 9491da177e4SLinus Torvalds * snd_pcm_hw_rule_add - add the hw-constraint rule 9501da177e4SLinus Torvalds * @runtime: the pcm runtime instance 9511da177e4SLinus Torvalds * @cond: condition bits 9521da177e4SLinus Torvalds * @var: the variable to evaluate 9531da177e4SLinus Torvalds * @func: the evaluation function 9541da177e4SLinus Torvalds * @private: the private data pointer passed to function 9551da177e4SLinus Torvalds * @dep: the dependent variables 9561da177e4SLinus Torvalds * 9571da177e4SLinus Torvalds * Returns zero if successful, or a negative error code on failure. 9581da177e4SLinus Torvalds */ 959877211f5STakashi Iwai int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond, 9601da177e4SLinus Torvalds int var, 9611da177e4SLinus Torvalds snd_pcm_hw_rule_func_t func, void *private, 9621da177e4SLinus Torvalds int dep, ...) 9631da177e4SLinus Torvalds { 964877211f5STakashi Iwai struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; 965877211f5STakashi Iwai struct snd_pcm_hw_rule *c; 9661da177e4SLinus Torvalds unsigned int k; 9671da177e4SLinus Torvalds va_list args; 9681da177e4SLinus Torvalds va_start(args, dep); 9691da177e4SLinus Torvalds if (constrs->rules_num >= constrs->rules_all) { 970877211f5STakashi Iwai struct snd_pcm_hw_rule *new; 9711da177e4SLinus Torvalds unsigned int new_rules = constrs->rules_all + 16; 9721da177e4SLinus Torvalds new = kcalloc(new_rules, sizeof(*c), GFP_KERNEL); 9731da177e4SLinus Torvalds if (!new) 9741da177e4SLinus Torvalds return -ENOMEM; 9751da177e4SLinus Torvalds if (constrs->rules) { 9761da177e4SLinus Torvalds memcpy(new, constrs->rules, 9771da177e4SLinus Torvalds constrs->rules_num * sizeof(*c)); 9781da177e4SLinus Torvalds kfree(constrs->rules); 9791da177e4SLinus Torvalds } 9801da177e4SLinus Torvalds constrs->rules = new; 9811da177e4SLinus Torvalds constrs->rules_all = new_rules; 9821da177e4SLinus Torvalds } 9831da177e4SLinus Torvalds c = &constrs->rules[constrs->rules_num]; 9841da177e4SLinus Torvalds c->cond = cond; 9851da177e4SLinus Torvalds c->func = func; 9861da177e4SLinus Torvalds c->var = var; 9871da177e4SLinus Torvalds c->private = private; 9881da177e4SLinus Torvalds k = 0; 9891da177e4SLinus Torvalds while (1) { 9907eaa943cSTakashi Iwai if (snd_BUG_ON(k >= ARRAY_SIZE(c->deps))) 9917eaa943cSTakashi Iwai return -EINVAL; 9921da177e4SLinus Torvalds c->deps[k++] = dep; 9931da177e4SLinus Torvalds if (dep < 0) 9941da177e4SLinus Torvalds break; 9951da177e4SLinus Torvalds dep = va_arg(args, int); 9961da177e4SLinus Torvalds } 9971da177e4SLinus Torvalds constrs->rules_num++; 9981da177e4SLinus Torvalds va_end(args); 9991da177e4SLinus Torvalds return 0; 10001da177e4SLinus Torvalds } 10011da177e4SLinus Torvalds 1002e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_rule_add); 1003e88e8ae6STakashi Iwai 10041da177e4SLinus Torvalds /** 10051c85cc64SRandy Dunlap * snd_pcm_hw_constraint_mask - apply the given bitmap mask constraint 1006df8db936STakashi Iwai * @runtime: PCM runtime instance 1007df8db936STakashi Iwai * @var: hw_params variable to apply the mask 1008df8db936STakashi Iwai * @mask: the bitmap mask 1009df8db936STakashi Iwai * 10101c85cc64SRandy Dunlap * Apply the constraint of the given bitmap mask to a 32-bit mask parameter. 10111da177e4SLinus Torvalds */ 1012877211f5STakashi Iwai int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, 10131da177e4SLinus Torvalds u_int32_t mask) 10141da177e4SLinus Torvalds { 1015877211f5STakashi Iwai struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; 1016877211f5STakashi Iwai struct snd_mask *maskp = constrs_mask(constrs, var); 10171da177e4SLinus Torvalds *maskp->bits &= mask; 10181da177e4SLinus Torvalds memset(maskp->bits + 1, 0, (SNDRV_MASK_MAX-32) / 8); /* clear rest */ 10191da177e4SLinus Torvalds if (*maskp->bits == 0) 10201da177e4SLinus Torvalds return -EINVAL; 10211da177e4SLinus Torvalds return 0; 10221da177e4SLinus Torvalds } 10231da177e4SLinus Torvalds 10241da177e4SLinus Torvalds /** 10251c85cc64SRandy Dunlap * snd_pcm_hw_constraint_mask64 - apply the given bitmap mask constraint 1026df8db936STakashi Iwai * @runtime: PCM runtime instance 1027df8db936STakashi Iwai * @var: hw_params variable to apply the mask 1028df8db936STakashi Iwai * @mask: the 64bit bitmap mask 1029df8db936STakashi Iwai * 10301c85cc64SRandy Dunlap * Apply the constraint of the given bitmap mask to a 64-bit mask parameter. 10311da177e4SLinus Torvalds */ 1032877211f5STakashi Iwai int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, 10331da177e4SLinus Torvalds u_int64_t mask) 10341da177e4SLinus Torvalds { 1035877211f5STakashi Iwai struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; 1036877211f5STakashi Iwai struct snd_mask *maskp = constrs_mask(constrs, var); 10371da177e4SLinus Torvalds maskp->bits[0] &= (u_int32_t)mask; 10381da177e4SLinus Torvalds maskp->bits[1] &= (u_int32_t)(mask >> 32); 10391da177e4SLinus Torvalds memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */ 10401da177e4SLinus Torvalds if (! maskp->bits[0] && ! maskp->bits[1]) 10411da177e4SLinus Torvalds return -EINVAL; 10421da177e4SLinus Torvalds return 0; 10431da177e4SLinus Torvalds } 10441da177e4SLinus Torvalds 10451da177e4SLinus Torvalds /** 10461c85cc64SRandy Dunlap * snd_pcm_hw_constraint_integer - apply an integer constraint to an interval 1047df8db936STakashi Iwai * @runtime: PCM runtime instance 1048df8db936STakashi Iwai * @var: hw_params variable to apply the integer constraint 1049df8db936STakashi Iwai * 1050df8db936STakashi Iwai * Apply the constraint of integer to an interval parameter. 10511da177e4SLinus Torvalds */ 1052877211f5STakashi Iwai int snd_pcm_hw_constraint_integer(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var) 10531da177e4SLinus Torvalds { 1054877211f5STakashi Iwai struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; 10551da177e4SLinus Torvalds return snd_interval_setinteger(constrs_interval(constrs, var)); 10561da177e4SLinus Torvalds } 10571da177e4SLinus Torvalds 1058e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_integer); 1059e88e8ae6STakashi Iwai 10601da177e4SLinus Torvalds /** 10611c85cc64SRandy Dunlap * snd_pcm_hw_constraint_minmax - apply a min/max range constraint to an interval 1062df8db936STakashi Iwai * @runtime: PCM runtime instance 1063df8db936STakashi Iwai * @var: hw_params variable to apply the range 1064df8db936STakashi Iwai * @min: the minimal value 1065df8db936STakashi Iwai * @max: the maximal value 1066df8db936STakashi Iwai * 1067df8db936STakashi Iwai * Apply the min/max range constraint to an interval parameter. 10681da177e4SLinus Torvalds */ 1069877211f5STakashi Iwai int snd_pcm_hw_constraint_minmax(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, 10701da177e4SLinus Torvalds unsigned int min, unsigned int max) 10711da177e4SLinus Torvalds { 1072877211f5STakashi Iwai struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; 1073877211f5STakashi Iwai struct snd_interval t; 10741da177e4SLinus Torvalds t.min = min; 10751da177e4SLinus Torvalds t.max = max; 10761da177e4SLinus Torvalds t.openmin = t.openmax = 0; 10771da177e4SLinus Torvalds t.integer = 0; 10781da177e4SLinus Torvalds return snd_interval_refine(constrs_interval(constrs, var), &t); 10791da177e4SLinus Torvalds } 10801da177e4SLinus Torvalds 1081e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_minmax); 1082e88e8ae6STakashi Iwai 1083877211f5STakashi Iwai static int snd_pcm_hw_rule_list(struct snd_pcm_hw_params *params, 1084877211f5STakashi Iwai struct snd_pcm_hw_rule *rule) 10851da177e4SLinus Torvalds { 1086877211f5STakashi Iwai struct snd_pcm_hw_constraint_list *list = rule->private; 10871da177e4SLinus Torvalds return snd_interval_list(hw_param_interval(params, rule->var), list->count, list->list, list->mask); 10881da177e4SLinus Torvalds } 10891da177e4SLinus Torvalds 10901da177e4SLinus Torvalds 10911da177e4SLinus Torvalds /** 10921c85cc64SRandy Dunlap * snd_pcm_hw_constraint_list - apply a list of constraints to a parameter 1093df8db936STakashi Iwai * @runtime: PCM runtime instance 1094df8db936STakashi Iwai * @cond: condition bits 1095df8db936STakashi Iwai * @var: hw_params variable to apply the list constraint 1096df8db936STakashi Iwai * @l: list 1097df8db936STakashi Iwai * 1098df8db936STakashi Iwai * Apply the list of constraints to an interval parameter. 10991da177e4SLinus Torvalds */ 1100877211f5STakashi Iwai int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime, 11011da177e4SLinus Torvalds unsigned int cond, 11021da177e4SLinus Torvalds snd_pcm_hw_param_t var, 1103877211f5STakashi Iwai struct snd_pcm_hw_constraint_list *l) 11041da177e4SLinus Torvalds { 11051da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 11061da177e4SLinus Torvalds snd_pcm_hw_rule_list, l, 11071da177e4SLinus Torvalds var, -1); 11081da177e4SLinus Torvalds } 11091da177e4SLinus Torvalds 1110e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_list); 1111e88e8ae6STakashi Iwai 1112877211f5STakashi Iwai static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params, 1113877211f5STakashi Iwai struct snd_pcm_hw_rule *rule) 11141da177e4SLinus Torvalds { 1115877211f5STakashi Iwai struct snd_pcm_hw_constraint_ratnums *r = rule->private; 11161da177e4SLinus Torvalds unsigned int num = 0, den = 0; 11171da177e4SLinus Torvalds int err; 11181da177e4SLinus Torvalds err = snd_interval_ratnum(hw_param_interval(params, rule->var), 11191da177e4SLinus Torvalds r->nrats, r->rats, &num, &den); 11201da177e4SLinus Torvalds if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { 11211da177e4SLinus Torvalds params->rate_num = num; 11221da177e4SLinus Torvalds params->rate_den = den; 11231da177e4SLinus Torvalds } 11241da177e4SLinus Torvalds return err; 11251da177e4SLinus Torvalds } 11261da177e4SLinus Torvalds 11271da177e4SLinus Torvalds /** 11281c85cc64SRandy Dunlap * snd_pcm_hw_constraint_ratnums - apply ratnums constraint to a parameter 1129df8db936STakashi Iwai * @runtime: PCM runtime instance 1130df8db936STakashi Iwai * @cond: condition bits 1131df8db936STakashi Iwai * @var: hw_params variable to apply the ratnums constraint 1132877211f5STakashi Iwai * @r: struct snd_ratnums constriants 11331da177e4SLinus Torvalds */ 1134877211f5STakashi Iwai int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime, 11351da177e4SLinus Torvalds unsigned int cond, 11361da177e4SLinus Torvalds snd_pcm_hw_param_t var, 1137877211f5STakashi Iwai struct snd_pcm_hw_constraint_ratnums *r) 11381da177e4SLinus Torvalds { 11391da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 11401da177e4SLinus Torvalds snd_pcm_hw_rule_ratnums, r, 11411da177e4SLinus Torvalds var, -1); 11421da177e4SLinus Torvalds } 11431da177e4SLinus Torvalds 1144e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums); 1145e88e8ae6STakashi Iwai 1146877211f5STakashi Iwai static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params, 1147877211f5STakashi Iwai struct snd_pcm_hw_rule *rule) 11481da177e4SLinus Torvalds { 1149877211f5STakashi Iwai struct snd_pcm_hw_constraint_ratdens *r = rule->private; 11501da177e4SLinus Torvalds unsigned int num = 0, den = 0; 11511da177e4SLinus Torvalds int err = snd_interval_ratden(hw_param_interval(params, rule->var), 11521da177e4SLinus Torvalds r->nrats, r->rats, &num, &den); 11531da177e4SLinus Torvalds if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { 11541da177e4SLinus Torvalds params->rate_num = num; 11551da177e4SLinus Torvalds params->rate_den = den; 11561da177e4SLinus Torvalds } 11571da177e4SLinus Torvalds return err; 11581da177e4SLinus Torvalds } 11591da177e4SLinus Torvalds 11601da177e4SLinus Torvalds /** 11611c85cc64SRandy Dunlap * snd_pcm_hw_constraint_ratdens - apply ratdens constraint to a parameter 1162df8db936STakashi Iwai * @runtime: PCM runtime instance 1163df8db936STakashi Iwai * @cond: condition bits 1164df8db936STakashi Iwai * @var: hw_params variable to apply the ratdens constraint 1165877211f5STakashi Iwai * @r: struct snd_ratdens constriants 11661da177e4SLinus Torvalds */ 1167877211f5STakashi Iwai int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime, 11681da177e4SLinus Torvalds unsigned int cond, 11691da177e4SLinus Torvalds snd_pcm_hw_param_t var, 1170877211f5STakashi Iwai struct snd_pcm_hw_constraint_ratdens *r) 11711da177e4SLinus Torvalds { 11721da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 11731da177e4SLinus Torvalds snd_pcm_hw_rule_ratdens, r, 11741da177e4SLinus Torvalds var, -1); 11751da177e4SLinus Torvalds } 11761da177e4SLinus Torvalds 1177e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_ratdens); 1178e88e8ae6STakashi Iwai 1179877211f5STakashi Iwai static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params, 1180877211f5STakashi Iwai struct snd_pcm_hw_rule *rule) 11811da177e4SLinus Torvalds { 11821da177e4SLinus Torvalds unsigned int l = (unsigned long) rule->private; 11831da177e4SLinus Torvalds int width = l & 0xffff; 11841da177e4SLinus Torvalds unsigned int msbits = l >> 16; 1185877211f5STakashi Iwai struct snd_interval *i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); 11861da177e4SLinus Torvalds if (snd_interval_single(i) && snd_interval_value(i) == width) 11871da177e4SLinus Torvalds params->msbits = msbits; 11881da177e4SLinus Torvalds return 0; 11891da177e4SLinus Torvalds } 11901da177e4SLinus Torvalds 11911da177e4SLinus Torvalds /** 11921c85cc64SRandy Dunlap * snd_pcm_hw_constraint_msbits - add a hw constraint msbits rule 1193df8db936STakashi Iwai * @runtime: PCM runtime instance 1194df8db936STakashi Iwai * @cond: condition bits 1195df8db936STakashi Iwai * @width: sample bits width 1196df8db936STakashi Iwai * @msbits: msbits width 11971da177e4SLinus Torvalds */ 1198877211f5STakashi Iwai int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime, 11991da177e4SLinus Torvalds unsigned int cond, 12001da177e4SLinus Torvalds unsigned int width, 12011da177e4SLinus Torvalds unsigned int msbits) 12021da177e4SLinus Torvalds { 12031da177e4SLinus Torvalds unsigned long l = (msbits << 16) | width; 12041da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, -1, 12051da177e4SLinus Torvalds snd_pcm_hw_rule_msbits, 12061da177e4SLinus Torvalds (void*) l, 12071da177e4SLinus Torvalds SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); 12081da177e4SLinus Torvalds } 12091da177e4SLinus Torvalds 1210e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_msbits); 1211e88e8ae6STakashi Iwai 1212877211f5STakashi Iwai static int snd_pcm_hw_rule_step(struct snd_pcm_hw_params *params, 1213877211f5STakashi Iwai struct snd_pcm_hw_rule *rule) 12141da177e4SLinus Torvalds { 12151da177e4SLinus Torvalds unsigned long step = (unsigned long) rule->private; 12161da177e4SLinus Torvalds return snd_interval_step(hw_param_interval(params, rule->var), 0, step); 12171da177e4SLinus Torvalds } 12181da177e4SLinus Torvalds 12191da177e4SLinus Torvalds /** 12201c85cc64SRandy Dunlap * snd_pcm_hw_constraint_step - add a hw constraint step rule 1221df8db936STakashi Iwai * @runtime: PCM runtime instance 1222df8db936STakashi Iwai * @cond: condition bits 1223df8db936STakashi Iwai * @var: hw_params variable to apply the step constraint 1224df8db936STakashi Iwai * @step: step size 12251da177e4SLinus Torvalds */ 1226877211f5STakashi Iwai int snd_pcm_hw_constraint_step(struct snd_pcm_runtime *runtime, 12271da177e4SLinus Torvalds unsigned int cond, 12281da177e4SLinus Torvalds snd_pcm_hw_param_t var, 12291da177e4SLinus Torvalds unsigned long step) 12301da177e4SLinus Torvalds { 12311da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 12321da177e4SLinus Torvalds snd_pcm_hw_rule_step, (void *) step, 12331da177e4SLinus Torvalds var, -1); 12341da177e4SLinus Torvalds } 12351da177e4SLinus Torvalds 1236e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_step); 1237e88e8ae6STakashi Iwai 1238877211f5STakashi Iwai static int snd_pcm_hw_rule_pow2(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) 12391da177e4SLinus Torvalds { 124067c39317SMarcin Ślusarz static unsigned int pow2_sizes[] = { 12411da177e4SLinus Torvalds 1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7, 12421da177e4SLinus Torvalds 1<<8, 1<<9, 1<<10, 1<<11, 1<<12, 1<<13, 1<<14, 1<<15, 12431da177e4SLinus Torvalds 1<<16, 1<<17, 1<<18, 1<<19, 1<<20, 1<<21, 1<<22, 1<<23, 12441da177e4SLinus Torvalds 1<<24, 1<<25, 1<<26, 1<<27, 1<<28, 1<<29, 1<<30 12451da177e4SLinus Torvalds }; 12461da177e4SLinus Torvalds return snd_interval_list(hw_param_interval(params, rule->var), 12471da177e4SLinus Torvalds ARRAY_SIZE(pow2_sizes), pow2_sizes, 0); 12481da177e4SLinus Torvalds } 12491da177e4SLinus Torvalds 12501da177e4SLinus Torvalds /** 12511c85cc64SRandy Dunlap * snd_pcm_hw_constraint_pow2 - add a hw constraint power-of-2 rule 1252df8db936STakashi Iwai * @runtime: PCM runtime instance 1253df8db936STakashi Iwai * @cond: condition bits 1254df8db936STakashi Iwai * @var: hw_params variable to apply the power-of-2 constraint 12551da177e4SLinus Torvalds */ 1256877211f5STakashi Iwai int snd_pcm_hw_constraint_pow2(struct snd_pcm_runtime *runtime, 12571da177e4SLinus Torvalds unsigned int cond, 12581da177e4SLinus Torvalds snd_pcm_hw_param_t var) 12591da177e4SLinus Torvalds { 12601da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 12611da177e4SLinus Torvalds snd_pcm_hw_rule_pow2, NULL, 12621da177e4SLinus Torvalds var, -1); 12631da177e4SLinus Torvalds } 12641da177e4SLinus Torvalds 1265e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_pow2); 1266e88e8ae6STakashi Iwai 1267877211f5STakashi Iwai static void _snd_pcm_hw_param_any(struct snd_pcm_hw_params *params, 1268123992f7SAdrian Bunk snd_pcm_hw_param_t var) 12691da177e4SLinus Torvalds { 12701da177e4SLinus Torvalds if (hw_is_mask(var)) { 12711da177e4SLinus Torvalds snd_mask_any(hw_param_mask(params, var)); 12721da177e4SLinus Torvalds params->cmask |= 1 << var; 12731da177e4SLinus Torvalds params->rmask |= 1 << var; 12741da177e4SLinus Torvalds return; 12751da177e4SLinus Torvalds } 12761da177e4SLinus Torvalds if (hw_is_interval(var)) { 12771da177e4SLinus Torvalds snd_interval_any(hw_param_interval(params, var)); 12781da177e4SLinus Torvalds params->cmask |= 1 << var; 12791da177e4SLinus Torvalds params->rmask |= 1 << var; 12801da177e4SLinus Torvalds return; 12811da177e4SLinus Torvalds } 12821da177e4SLinus Torvalds snd_BUG(); 12831da177e4SLinus Torvalds } 12841da177e4SLinus Torvalds 1285877211f5STakashi Iwai void _snd_pcm_hw_params_any(struct snd_pcm_hw_params *params) 12861da177e4SLinus Torvalds { 12871da177e4SLinus Torvalds unsigned int k; 12881da177e4SLinus Torvalds memset(params, 0, sizeof(*params)); 12891da177e4SLinus Torvalds for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) 12901da177e4SLinus Torvalds _snd_pcm_hw_param_any(params, k); 12911da177e4SLinus Torvalds for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) 12921da177e4SLinus Torvalds _snd_pcm_hw_param_any(params, k); 12931da177e4SLinus Torvalds params->info = ~0U; 12941da177e4SLinus Torvalds } 12951da177e4SLinus Torvalds 1296e88e8ae6STakashi Iwai EXPORT_SYMBOL(_snd_pcm_hw_params_any); 12971da177e4SLinus Torvalds 12981da177e4SLinus Torvalds /** 12991c85cc64SRandy Dunlap * snd_pcm_hw_param_value - return @params field @var value 1300df8db936STakashi Iwai * @params: the hw_params instance 1301df8db936STakashi Iwai * @var: parameter to retrieve 13021c85cc64SRandy Dunlap * @dir: pointer to the direction (-1,0,1) or %NULL 13031da177e4SLinus Torvalds * 13041c85cc64SRandy Dunlap * Return the value for field @var if it's fixed in configuration space 13051c85cc64SRandy Dunlap * defined by @params. Return -%EINVAL otherwise. 13061da177e4SLinus Torvalds */ 1307e88e8ae6STakashi Iwai int snd_pcm_hw_param_value(const struct snd_pcm_hw_params *params, 13081da177e4SLinus Torvalds snd_pcm_hw_param_t var, int *dir) 13091da177e4SLinus Torvalds { 13101da177e4SLinus Torvalds if (hw_is_mask(var)) { 1311877211f5STakashi Iwai const struct snd_mask *mask = hw_param_mask_c(params, var); 13121da177e4SLinus Torvalds if (!snd_mask_single(mask)) 13131da177e4SLinus Torvalds return -EINVAL; 13141da177e4SLinus Torvalds if (dir) 13151da177e4SLinus Torvalds *dir = 0; 13161da177e4SLinus Torvalds return snd_mask_value(mask); 13171da177e4SLinus Torvalds } 13181da177e4SLinus Torvalds if (hw_is_interval(var)) { 1319877211f5STakashi Iwai const struct snd_interval *i = hw_param_interval_c(params, var); 13201da177e4SLinus Torvalds if (!snd_interval_single(i)) 13211da177e4SLinus Torvalds return -EINVAL; 13221da177e4SLinus Torvalds if (dir) 13231da177e4SLinus Torvalds *dir = i->openmin; 13241da177e4SLinus Torvalds return snd_interval_value(i); 13251da177e4SLinus Torvalds } 13261da177e4SLinus Torvalds return -EINVAL; 13271da177e4SLinus Torvalds } 13281da177e4SLinus Torvalds 1329e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_param_value); 13301da177e4SLinus Torvalds 1331877211f5STakashi Iwai void _snd_pcm_hw_param_setempty(struct snd_pcm_hw_params *params, 13321da177e4SLinus Torvalds snd_pcm_hw_param_t var) 13331da177e4SLinus Torvalds { 13341da177e4SLinus Torvalds if (hw_is_mask(var)) { 13351da177e4SLinus Torvalds snd_mask_none(hw_param_mask(params, var)); 13361da177e4SLinus Torvalds params->cmask |= 1 << var; 13371da177e4SLinus Torvalds params->rmask |= 1 << var; 13381da177e4SLinus Torvalds } else if (hw_is_interval(var)) { 13391da177e4SLinus Torvalds snd_interval_none(hw_param_interval(params, var)); 13401da177e4SLinus Torvalds params->cmask |= 1 << var; 13411da177e4SLinus Torvalds params->rmask |= 1 << var; 13421da177e4SLinus Torvalds } else { 13431da177e4SLinus Torvalds snd_BUG(); 13441da177e4SLinus Torvalds } 13451da177e4SLinus Torvalds } 13461da177e4SLinus Torvalds 1347e88e8ae6STakashi Iwai EXPORT_SYMBOL(_snd_pcm_hw_param_setempty); 13481da177e4SLinus Torvalds 1349877211f5STakashi Iwai static int _snd_pcm_hw_param_first(struct snd_pcm_hw_params *params, 13501da177e4SLinus Torvalds snd_pcm_hw_param_t var) 13511da177e4SLinus Torvalds { 13521da177e4SLinus Torvalds int changed; 13531da177e4SLinus Torvalds if (hw_is_mask(var)) 13541da177e4SLinus Torvalds changed = snd_mask_refine_first(hw_param_mask(params, var)); 13551da177e4SLinus Torvalds else if (hw_is_interval(var)) 13561da177e4SLinus Torvalds changed = snd_interval_refine_first(hw_param_interval(params, var)); 13572f4ca8e5STakashi Iwai else 13581da177e4SLinus Torvalds return -EINVAL; 13591da177e4SLinus Torvalds if (changed) { 13601da177e4SLinus Torvalds params->cmask |= 1 << var; 13611da177e4SLinus Torvalds params->rmask |= 1 << var; 13621da177e4SLinus Torvalds } 13631da177e4SLinus Torvalds return changed; 13641da177e4SLinus Torvalds } 13651da177e4SLinus Torvalds 13661da177e4SLinus Torvalds 13671da177e4SLinus Torvalds /** 13681c85cc64SRandy Dunlap * snd_pcm_hw_param_first - refine config space and return minimum value 1369df8db936STakashi Iwai * @pcm: PCM instance 1370df8db936STakashi Iwai * @params: the hw_params instance 1371df8db936STakashi Iwai * @var: parameter to retrieve 13721c85cc64SRandy Dunlap * @dir: pointer to the direction (-1,0,1) or %NULL 13731da177e4SLinus Torvalds * 13741c85cc64SRandy Dunlap * Inside configuration space defined by @params remove from @var all 13751da177e4SLinus Torvalds * values > minimum. Reduce configuration space accordingly. 13761da177e4SLinus Torvalds * Return the minimum. 13771da177e4SLinus Torvalds */ 1378e88e8ae6STakashi Iwai int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm, 1379877211f5STakashi Iwai struct snd_pcm_hw_params *params, 13801da177e4SLinus Torvalds snd_pcm_hw_param_t var, int *dir) 13811da177e4SLinus Torvalds { 13821da177e4SLinus Torvalds int changed = _snd_pcm_hw_param_first(params, var); 13831da177e4SLinus Torvalds if (changed < 0) 13841da177e4SLinus Torvalds return changed; 13851da177e4SLinus Torvalds if (params->rmask) { 13861da177e4SLinus Torvalds int err = snd_pcm_hw_refine(pcm, params); 13877eaa943cSTakashi Iwai if (snd_BUG_ON(err < 0)) 13887eaa943cSTakashi Iwai return err; 13891da177e4SLinus Torvalds } 13901da177e4SLinus Torvalds return snd_pcm_hw_param_value(params, var, dir); 13911da177e4SLinus Torvalds } 13921da177e4SLinus Torvalds 1393e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_param_first); 1394e88e8ae6STakashi Iwai 1395877211f5STakashi Iwai static int _snd_pcm_hw_param_last(struct snd_pcm_hw_params *params, 13961da177e4SLinus Torvalds snd_pcm_hw_param_t var) 13971da177e4SLinus Torvalds { 13981da177e4SLinus Torvalds int changed; 13991da177e4SLinus Torvalds if (hw_is_mask(var)) 14001da177e4SLinus Torvalds changed = snd_mask_refine_last(hw_param_mask(params, var)); 14011da177e4SLinus Torvalds else if (hw_is_interval(var)) 14021da177e4SLinus Torvalds changed = snd_interval_refine_last(hw_param_interval(params, var)); 14032f4ca8e5STakashi Iwai else 14041da177e4SLinus Torvalds return -EINVAL; 14051da177e4SLinus Torvalds if (changed) { 14061da177e4SLinus Torvalds params->cmask |= 1 << var; 14071da177e4SLinus Torvalds params->rmask |= 1 << var; 14081da177e4SLinus Torvalds } 14091da177e4SLinus Torvalds return changed; 14101da177e4SLinus Torvalds } 14111da177e4SLinus Torvalds 14121da177e4SLinus Torvalds 14131da177e4SLinus Torvalds /** 14141c85cc64SRandy Dunlap * snd_pcm_hw_param_last - refine config space and return maximum value 1415df8db936STakashi Iwai * @pcm: PCM instance 1416df8db936STakashi Iwai * @params: the hw_params instance 1417df8db936STakashi Iwai * @var: parameter to retrieve 14181c85cc64SRandy Dunlap * @dir: pointer to the direction (-1,0,1) or %NULL 14191da177e4SLinus Torvalds * 14201c85cc64SRandy Dunlap * Inside configuration space defined by @params remove from @var all 14211da177e4SLinus Torvalds * values < maximum. Reduce configuration space accordingly. 14221da177e4SLinus Torvalds * Return the maximum. 14231da177e4SLinus Torvalds */ 1424e88e8ae6STakashi Iwai int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm, 1425877211f5STakashi Iwai struct snd_pcm_hw_params *params, 14261da177e4SLinus Torvalds snd_pcm_hw_param_t var, int *dir) 14271da177e4SLinus Torvalds { 14281da177e4SLinus Torvalds int changed = _snd_pcm_hw_param_last(params, var); 14291da177e4SLinus Torvalds if (changed < 0) 14301da177e4SLinus Torvalds return changed; 14311da177e4SLinus Torvalds if (params->rmask) { 14321da177e4SLinus Torvalds int err = snd_pcm_hw_refine(pcm, params); 14337eaa943cSTakashi Iwai if (snd_BUG_ON(err < 0)) 14347eaa943cSTakashi Iwai return err; 14351da177e4SLinus Torvalds } 14361da177e4SLinus Torvalds return snd_pcm_hw_param_value(params, var, dir); 14371da177e4SLinus Torvalds } 14381da177e4SLinus Torvalds 1439e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_param_last); 14401da177e4SLinus Torvalds 14411da177e4SLinus Torvalds /** 14421c85cc64SRandy Dunlap * snd_pcm_hw_param_choose - choose a configuration defined by @params 1443df8db936STakashi Iwai * @pcm: PCM instance 1444df8db936STakashi Iwai * @params: the hw_params instance 14451da177e4SLinus Torvalds * 14461c85cc64SRandy Dunlap * Choose one configuration from configuration space defined by @params. 14471da177e4SLinus Torvalds * The configuration chosen is that obtained fixing in this order: 14481da177e4SLinus Torvalds * first access, first format, first subformat, min channels, 14491da177e4SLinus Torvalds * min rate, min period time, max buffer size, min tick time 14501da177e4SLinus Torvalds */ 14512f4ca8e5STakashi Iwai int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm, 14522f4ca8e5STakashi Iwai struct snd_pcm_hw_params *params) 14531da177e4SLinus Torvalds { 14542f4ca8e5STakashi Iwai static int vars[] = { 14552f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_ACCESS, 14562f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_FORMAT, 14572f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_SUBFORMAT, 14582f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_CHANNELS, 14592f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_RATE, 14602f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_PERIOD_TIME, 14612f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 14622f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_TICK_TIME, 14632f4ca8e5STakashi Iwai -1 14642f4ca8e5STakashi Iwai }; 14652f4ca8e5STakashi Iwai int err, *v; 14661da177e4SLinus Torvalds 14672f4ca8e5STakashi Iwai for (v = vars; *v != -1; v++) { 14682f4ca8e5STakashi Iwai if (*v != SNDRV_PCM_HW_PARAM_BUFFER_SIZE) 14692f4ca8e5STakashi Iwai err = snd_pcm_hw_param_first(pcm, params, *v, NULL); 14702f4ca8e5STakashi Iwai else 14712f4ca8e5STakashi Iwai err = snd_pcm_hw_param_last(pcm, params, *v, NULL); 14727eaa943cSTakashi Iwai if (snd_BUG_ON(err < 0)) 14737eaa943cSTakashi Iwai return err; 14742f4ca8e5STakashi Iwai } 14751da177e4SLinus Torvalds return 0; 14761da177e4SLinus Torvalds } 14771da177e4SLinus Torvalds 1478877211f5STakashi Iwai static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream, 14791da177e4SLinus Torvalds void *arg) 14801da177e4SLinus Torvalds { 1481877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 14821da177e4SLinus Torvalds unsigned long flags; 14831da177e4SLinus Torvalds snd_pcm_stream_lock_irqsave(substream, flags); 14841da177e4SLinus Torvalds if (snd_pcm_running(substream) && 14851da177e4SLinus Torvalds snd_pcm_update_hw_ptr(substream) >= 0) 14861da177e4SLinus Torvalds runtime->status->hw_ptr %= runtime->buffer_size; 14871da177e4SLinus Torvalds else 14881da177e4SLinus Torvalds runtime->status->hw_ptr = 0; 14891da177e4SLinus Torvalds snd_pcm_stream_unlock_irqrestore(substream, flags); 14901da177e4SLinus Torvalds return 0; 14911da177e4SLinus Torvalds } 14921da177e4SLinus Torvalds 1493877211f5STakashi Iwai static int snd_pcm_lib_ioctl_channel_info(struct snd_pcm_substream *substream, 14941da177e4SLinus Torvalds void *arg) 14951da177e4SLinus Torvalds { 1496877211f5STakashi Iwai struct snd_pcm_channel_info *info = arg; 1497877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 14981da177e4SLinus Torvalds int width; 14991da177e4SLinus Torvalds if (!(runtime->info & SNDRV_PCM_INFO_MMAP)) { 15001da177e4SLinus Torvalds info->offset = -1; 15011da177e4SLinus Torvalds return 0; 15021da177e4SLinus Torvalds } 15031da177e4SLinus Torvalds width = snd_pcm_format_physical_width(runtime->format); 15041da177e4SLinus Torvalds if (width < 0) 15051da177e4SLinus Torvalds return width; 15061da177e4SLinus Torvalds info->offset = 0; 15071da177e4SLinus Torvalds switch (runtime->access) { 15081da177e4SLinus Torvalds case SNDRV_PCM_ACCESS_MMAP_INTERLEAVED: 15091da177e4SLinus Torvalds case SNDRV_PCM_ACCESS_RW_INTERLEAVED: 15101da177e4SLinus Torvalds info->first = info->channel * width; 15111da177e4SLinus Torvalds info->step = runtime->channels * width; 15121da177e4SLinus Torvalds break; 15131da177e4SLinus Torvalds case SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED: 15141da177e4SLinus Torvalds case SNDRV_PCM_ACCESS_RW_NONINTERLEAVED: 15151da177e4SLinus Torvalds { 15161da177e4SLinus Torvalds size_t size = runtime->dma_bytes / runtime->channels; 15171da177e4SLinus Torvalds info->first = info->channel * size * 8; 15181da177e4SLinus Torvalds info->step = width; 15191da177e4SLinus Torvalds break; 15201da177e4SLinus Torvalds } 15211da177e4SLinus Torvalds default: 15221da177e4SLinus Torvalds snd_BUG(); 15231da177e4SLinus Torvalds break; 15241da177e4SLinus Torvalds } 15251da177e4SLinus Torvalds return 0; 15261da177e4SLinus Torvalds } 15271da177e4SLinus Torvalds 15281da177e4SLinus Torvalds /** 15291da177e4SLinus Torvalds * snd_pcm_lib_ioctl - a generic PCM ioctl callback 15301da177e4SLinus Torvalds * @substream: the pcm substream instance 15311da177e4SLinus Torvalds * @cmd: ioctl command 15321da177e4SLinus Torvalds * @arg: ioctl argument 15331da177e4SLinus Torvalds * 15341da177e4SLinus Torvalds * Processes the generic ioctl commands for PCM. 15351da177e4SLinus Torvalds * Can be passed as the ioctl callback for PCM ops. 15361da177e4SLinus Torvalds * 15371da177e4SLinus Torvalds * Returns zero if successful, or a negative error code on failure. 15381da177e4SLinus Torvalds */ 1539877211f5STakashi Iwai int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream, 15401da177e4SLinus Torvalds unsigned int cmd, void *arg) 15411da177e4SLinus Torvalds { 15421da177e4SLinus Torvalds switch (cmd) { 15431da177e4SLinus Torvalds case SNDRV_PCM_IOCTL1_INFO: 15441da177e4SLinus Torvalds return 0; 15451da177e4SLinus Torvalds case SNDRV_PCM_IOCTL1_RESET: 15461da177e4SLinus Torvalds return snd_pcm_lib_ioctl_reset(substream, arg); 15471da177e4SLinus Torvalds case SNDRV_PCM_IOCTL1_CHANNEL_INFO: 15481da177e4SLinus Torvalds return snd_pcm_lib_ioctl_channel_info(substream, arg); 15491da177e4SLinus Torvalds } 15501da177e4SLinus Torvalds return -ENXIO; 15511da177e4SLinus Torvalds } 15521da177e4SLinus Torvalds 1553e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_lib_ioctl); 1554e88e8ae6STakashi Iwai 15551da177e4SLinus Torvalds /** 15561da177e4SLinus Torvalds * snd_pcm_period_elapsed - update the pcm status for the next period 15571da177e4SLinus Torvalds * @substream: the pcm substream instance 15581da177e4SLinus Torvalds * 15591da177e4SLinus Torvalds * This function is called from the interrupt handler when the 15601da177e4SLinus Torvalds * PCM has processed the period size. It will update the current 156131e8960bSTakashi Iwai * pointer, wake up sleepers, etc. 15621da177e4SLinus Torvalds * 15631da177e4SLinus Torvalds * Even if more than one periods have elapsed since the last call, you 15641da177e4SLinus Torvalds * have to call this only once. 15651da177e4SLinus Torvalds */ 1566877211f5STakashi Iwai void snd_pcm_period_elapsed(struct snd_pcm_substream *substream) 15671da177e4SLinus Torvalds { 1568877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 15691da177e4SLinus Torvalds unsigned long flags; 15701da177e4SLinus Torvalds 15717eaa943cSTakashi Iwai if (PCM_RUNTIME_CHECK(substream)) 15727eaa943cSTakashi Iwai return; 15731da177e4SLinus Torvalds runtime = substream->runtime; 15741da177e4SLinus Torvalds 15751da177e4SLinus Torvalds if (runtime->transfer_ack_begin) 15761da177e4SLinus Torvalds runtime->transfer_ack_begin(substream); 15771da177e4SLinus Torvalds 15781da177e4SLinus Torvalds snd_pcm_stream_lock_irqsave(substream, flags); 15791da177e4SLinus Torvalds if (!snd_pcm_running(substream) || 15801da177e4SLinus Torvalds snd_pcm_update_hw_ptr_interrupt(substream) < 0) 15811da177e4SLinus Torvalds goto _end; 15821da177e4SLinus Torvalds 15831da177e4SLinus Torvalds if (substream->timer_running) 15841da177e4SLinus Torvalds snd_timer_interrupt(substream->timer, 1); 15851da177e4SLinus Torvalds _end: 15861da177e4SLinus Torvalds snd_pcm_stream_unlock_irqrestore(substream, flags); 15871da177e4SLinus Torvalds if (runtime->transfer_ack_end) 15881da177e4SLinus Torvalds runtime->transfer_ack_end(substream); 15891da177e4SLinus Torvalds kill_fasync(&runtime->fasync, SIGIO, POLL_IN); 15901da177e4SLinus Torvalds } 15911da177e4SLinus Torvalds 1592e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_period_elapsed); 1593e88e8ae6STakashi Iwai 159413075510STakashi Iwai /* 159513075510STakashi Iwai * Wait until avail_min data becomes available 159613075510STakashi Iwai * Returns a negative error code if any error occurs during operation. 159713075510STakashi Iwai * The available space is stored on availp. When err = 0 and avail = 0 159813075510STakashi Iwai * on the capture stream, it indicates the stream is in DRAINING state. 159913075510STakashi Iwai */ 160013075510STakashi Iwai static int wait_for_avail_min(struct snd_pcm_substream *substream, 160113075510STakashi Iwai snd_pcm_uframes_t *availp) 160213075510STakashi Iwai { 160313075510STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 160413075510STakashi Iwai int is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 160513075510STakashi Iwai wait_queue_t wait; 160613075510STakashi Iwai int err = 0; 160713075510STakashi Iwai snd_pcm_uframes_t avail = 0; 160813075510STakashi Iwai long tout; 160913075510STakashi Iwai 161013075510STakashi Iwai init_waitqueue_entry(&wait, current); 161113075510STakashi Iwai add_wait_queue(&runtime->sleep, &wait); 161213075510STakashi Iwai for (;;) { 161313075510STakashi Iwai if (signal_pending(current)) { 161413075510STakashi Iwai err = -ERESTARTSYS; 161513075510STakashi Iwai break; 161613075510STakashi Iwai } 161713075510STakashi Iwai set_current_state(TASK_INTERRUPTIBLE); 161813075510STakashi Iwai snd_pcm_stream_unlock_irq(substream); 161913075510STakashi Iwai tout = schedule_timeout(msecs_to_jiffies(10000)); 162013075510STakashi Iwai snd_pcm_stream_lock_irq(substream); 162113075510STakashi Iwai switch (runtime->status->state) { 162213075510STakashi Iwai case SNDRV_PCM_STATE_SUSPENDED: 162313075510STakashi Iwai err = -ESTRPIPE; 162413075510STakashi Iwai goto _endloop; 162513075510STakashi Iwai case SNDRV_PCM_STATE_XRUN: 162613075510STakashi Iwai err = -EPIPE; 162713075510STakashi Iwai goto _endloop; 162813075510STakashi Iwai case SNDRV_PCM_STATE_DRAINING: 162913075510STakashi Iwai if (is_playback) 163013075510STakashi Iwai err = -EPIPE; 163113075510STakashi Iwai else 163213075510STakashi Iwai avail = 0; /* indicate draining */ 163313075510STakashi Iwai goto _endloop; 163413075510STakashi Iwai case SNDRV_PCM_STATE_OPEN: 163513075510STakashi Iwai case SNDRV_PCM_STATE_SETUP: 163613075510STakashi Iwai case SNDRV_PCM_STATE_DISCONNECTED: 163713075510STakashi Iwai err = -EBADFD; 163813075510STakashi Iwai goto _endloop; 163913075510STakashi Iwai } 164013075510STakashi Iwai if (!tout) { 164113075510STakashi Iwai snd_printd("%s write error (DMA or IRQ trouble?)\n", 164213075510STakashi Iwai is_playback ? "playback" : "capture"); 164313075510STakashi Iwai err = -EIO; 164413075510STakashi Iwai break; 164513075510STakashi Iwai } 164613075510STakashi Iwai if (is_playback) 164713075510STakashi Iwai avail = snd_pcm_playback_avail(runtime); 164813075510STakashi Iwai else 164913075510STakashi Iwai avail = snd_pcm_capture_avail(runtime); 165013075510STakashi Iwai if (avail >= runtime->control->avail_min) 165113075510STakashi Iwai break; 165213075510STakashi Iwai } 165313075510STakashi Iwai _endloop: 165413075510STakashi Iwai remove_wait_queue(&runtime->sleep, &wait); 165513075510STakashi Iwai *availp = avail; 165613075510STakashi Iwai return err; 165713075510STakashi Iwai } 165813075510STakashi Iwai 1659877211f5STakashi Iwai static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream, 16601da177e4SLinus Torvalds unsigned int hwoff, 16611da177e4SLinus Torvalds unsigned long data, unsigned int off, 16621da177e4SLinus Torvalds snd_pcm_uframes_t frames) 16631da177e4SLinus Torvalds { 1664877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 16651da177e4SLinus Torvalds int err; 16661da177e4SLinus Torvalds char __user *buf = (char __user *) data + frames_to_bytes(runtime, off); 16671da177e4SLinus Torvalds if (substream->ops->copy) { 16681da177e4SLinus Torvalds if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0) 16691da177e4SLinus Torvalds return err; 16701da177e4SLinus Torvalds } else { 16711da177e4SLinus Torvalds char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); 16721da177e4SLinus Torvalds if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames))) 16731da177e4SLinus Torvalds return -EFAULT; 16741da177e4SLinus Torvalds } 16751da177e4SLinus Torvalds return 0; 16761da177e4SLinus Torvalds } 16771da177e4SLinus Torvalds 1678877211f5STakashi Iwai typedef int (*transfer_f)(struct snd_pcm_substream *substream, unsigned int hwoff, 16791da177e4SLinus Torvalds unsigned long data, unsigned int off, 16801da177e4SLinus Torvalds snd_pcm_uframes_t size); 16811da177e4SLinus Torvalds 1682877211f5STakashi Iwai static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, 16831da177e4SLinus Torvalds unsigned long data, 16841da177e4SLinus Torvalds snd_pcm_uframes_t size, 16851da177e4SLinus Torvalds int nonblock, 16861da177e4SLinus Torvalds transfer_f transfer) 16871da177e4SLinus Torvalds { 1688877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 16891da177e4SLinus Torvalds snd_pcm_uframes_t xfer = 0; 16901da177e4SLinus Torvalds snd_pcm_uframes_t offset = 0; 16911da177e4SLinus Torvalds int err = 0; 16921da177e4SLinus Torvalds 16931da177e4SLinus Torvalds if (size == 0) 16941da177e4SLinus Torvalds return 0; 16951da177e4SLinus Torvalds 16961da177e4SLinus Torvalds snd_pcm_stream_lock_irq(substream); 16971da177e4SLinus Torvalds switch (runtime->status->state) { 16981da177e4SLinus Torvalds case SNDRV_PCM_STATE_PREPARED: 16991da177e4SLinus Torvalds case SNDRV_PCM_STATE_RUNNING: 17001da177e4SLinus Torvalds case SNDRV_PCM_STATE_PAUSED: 17011da177e4SLinus Torvalds break; 17021da177e4SLinus Torvalds case SNDRV_PCM_STATE_XRUN: 17031da177e4SLinus Torvalds err = -EPIPE; 17041da177e4SLinus Torvalds goto _end_unlock; 17051da177e4SLinus Torvalds case SNDRV_PCM_STATE_SUSPENDED: 17061da177e4SLinus Torvalds err = -ESTRPIPE; 17071da177e4SLinus Torvalds goto _end_unlock; 17081da177e4SLinus Torvalds default: 17091da177e4SLinus Torvalds err = -EBADFD; 17101da177e4SLinus Torvalds goto _end_unlock; 17111da177e4SLinus Torvalds } 17121da177e4SLinus Torvalds 17131da177e4SLinus Torvalds while (size > 0) { 17141da177e4SLinus Torvalds snd_pcm_uframes_t frames, appl_ptr, appl_ofs; 17151da177e4SLinus Torvalds snd_pcm_uframes_t avail; 17161da177e4SLinus Torvalds snd_pcm_uframes_t cont; 171731e8960bSTakashi Iwai if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) 17181da177e4SLinus Torvalds snd_pcm_update_hw_ptr(substream); 17191da177e4SLinus Torvalds avail = snd_pcm_playback_avail(runtime); 172013075510STakashi Iwai if (!avail) { 17211da177e4SLinus Torvalds if (nonblock) { 17221da177e4SLinus Torvalds err = -EAGAIN; 17231da177e4SLinus Torvalds goto _end_unlock; 17241da177e4SLinus Torvalds } 172513075510STakashi Iwai err = wait_for_avail_min(substream, &avail); 172613075510STakashi Iwai if (err < 0) 17271da177e4SLinus Torvalds goto _end_unlock; 17281da177e4SLinus Torvalds } 17291da177e4SLinus Torvalds frames = size > avail ? avail : size; 17301da177e4SLinus Torvalds cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; 17311da177e4SLinus Torvalds if (frames > cont) 17321da177e4SLinus Torvalds frames = cont; 17337eaa943cSTakashi Iwai if (snd_BUG_ON(!frames)) { 17347eaa943cSTakashi Iwai snd_pcm_stream_unlock_irq(substream); 17357eaa943cSTakashi Iwai return -EINVAL; 17367eaa943cSTakashi Iwai } 17371da177e4SLinus Torvalds appl_ptr = runtime->control->appl_ptr; 17381da177e4SLinus Torvalds appl_ofs = appl_ptr % runtime->buffer_size; 17391da177e4SLinus Torvalds snd_pcm_stream_unlock_irq(substream); 17401da177e4SLinus Torvalds if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0) 17411da177e4SLinus Torvalds goto _end; 17421da177e4SLinus Torvalds snd_pcm_stream_lock_irq(substream); 17431da177e4SLinus Torvalds switch (runtime->status->state) { 17441da177e4SLinus Torvalds case SNDRV_PCM_STATE_XRUN: 17451da177e4SLinus Torvalds err = -EPIPE; 17461da177e4SLinus Torvalds goto _end_unlock; 17471da177e4SLinus Torvalds case SNDRV_PCM_STATE_SUSPENDED: 17481da177e4SLinus Torvalds err = -ESTRPIPE; 17491da177e4SLinus Torvalds goto _end_unlock; 17501da177e4SLinus Torvalds default: 17511da177e4SLinus Torvalds break; 17521da177e4SLinus Torvalds } 17531da177e4SLinus Torvalds appl_ptr += frames; 17541da177e4SLinus Torvalds if (appl_ptr >= runtime->boundary) 17551da177e4SLinus Torvalds appl_ptr -= runtime->boundary; 17561da177e4SLinus Torvalds runtime->control->appl_ptr = appl_ptr; 17571da177e4SLinus Torvalds if (substream->ops->ack) 17581da177e4SLinus Torvalds substream->ops->ack(substream); 17591da177e4SLinus Torvalds 17601da177e4SLinus Torvalds offset += frames; 17611da177e4SLinus Torvalds size -= frames; 17621da177e4SLinus Torvalds xfer += frames; 17631da177e4SLinus Torvalds if (runtime->status->state == SNDRV_PCM_STATE_PREPARED && 17641da177e4SLinus Torvalds snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) { 17651da177e4SLinus Torvalds err = snd_pcm_start(substream); 17661da177e4SLinus Torvalds if (err < 0) 17671da177e4SLinus Torvalds goto _end_unlock; 17681da177e4SLinus Torvalds } 17691da177e4SLinus Torvalds } 17701da177e4SLinus Torvalds _end_unlock: 17711da177e4SLinus Torvalds snd_pcm_stream_unlock_irq(substream); 17721da177e4SLinus Torvalds _end: 17731da177e4SLinus Torvalds return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; 17741da177e4SLinus Torvalds } 17751da177e4SLinus Torvalds 17767eaa943cSTakashi Iwai /* sanity-check for read/write methods */ 17777eaa943cSTakashi Iwai static int pcm_sanity_check(struct snd_pcm_substream *substream) 17787eaa943cSTakashi Iwai { 17797eaa943cSTakashi Iwai struct snd_pcm_runtime *runtime; 17807eaa943cSTakashi Iwai if (PCM_RUNTIME_CHECK(substream)) 17817eaa943cSTakashi Iwai return -ENXIO; 17827eaa943cSTakashi Iwai runtime = substream->runtime; 17837eaa943cSTakashi Iwai if (snd_BUG_ON(!substream->ops->copy && !runtime->dma_area)) 17847eaa943cSTakashi Iwai return -EINVAL; 17857eaa943cSTakashi Iwai if (runtime->status->state == SNDRV_PCM_STATE_OPEN) 17867eaa943cSTakashi Iwai return -EBADFD; 17877eaa943cSTakashi Iwai return 0; 17887eaa943cSTakashi Iwai } 17897eaa943cSTakashi Iwai 1790877211f5STakashi Iwai snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const void __user *buf, snd_pcm_uframes_t size) 17911da177e4SLinus Torvalds { 1792877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 17931da177e4SLinus Torvalds int nonblock; 17947eaa943cSTakashi Iwai int err; 17951da177e4SLinus Torvalds 17967eaa943cSTakashi Iwai err = pcm_sanity_check(substream); 17977eaa943cSTakashi Iwai if (err < 0) 17987eaa943cSTakashi Iwai return err; 17991da177e4SLinus Torvalds runtime = substream->runtime; 18000df63e44STakashi Iwai nonblock = !!(substream->f_flags & O_NONBLOCK); 18011da177e4SLinus Torvalds 18021da177e4SLinus Torvalds if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && 18031da177e4SLinus Torvalds runtime->channels > 1) 18041da177e4SLinus Torvalds return -EINVAL; 18051da177e4SLinus Torvalds return snd_pcm_lib_write1(substream, (unsigned long)buf, size, nonblock, 18061da177e4SLinus Torvalds snd_pcm_lib_write_transfer); 18071da177e4SLinus Torvalds } 18081da177e4SLinus Torvalds 1809e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_lib_write); 1810e88e8ae6STakashi Iwai 1811877211f5STakashi Iwai static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream, 18121da177e4SLinus Torvalds unsigned int hwoff, 18131da177e4SLinus Torvalds unsigned long data, unsigned int off, 18141da177e4SLinus Torvalds snd_pcm_uframes_t frames) 18151da177e4SLinus Torvalds { 1816877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 18171da177e4SLinus Torvalds int err; 18181da177e4SLinus Torvalds void __user **bufs = (void __user **)data; 18191da177e4SLinus Torvalds int channels = runtime->channels; 18201da177e4SLinus Torvalds int c; 18211da177e4SLinus Torvalds if (substream->ops->copy) { 18227eaa943cSTakashi Iwai if (snd_BUG_ON(!substream->ops->silence)) 18237eaa943cSTakashi Iwai return -EINVAL; 18241da177e4SLinus Torvalds for (c = 0; c < channels; ++c, ++bufs) { 18251da177e4SLinus Torvalds if (*bufs == NULL) { 18261da177e4SLinus Torvalds if ((err = substream->ops->silence(substream, c, hwoff, frames)) < 0) 18271da177e4SLinus Torvalds return err; 18281da177e4SLinus Torvalds } else { 18291da177e4SLinus Torvalds char __user *buf = *bufs + samples_to_bytes(runtime, off); 18301da177e4SLinus Torvalds if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0) 18311da177e4SLinus Torvalds return err; 18321da177e4SLinus Torvalds } 18331da177e4SLinus Torvalds } 18341da177e4SLinus Torvalds } else { 18351da177e4SLinus Torvalds /* default transfer behaviour */ 18361da177e4SLinus Torvalds size_t dma_csize = runtime->dma_bytes / channels; 18371da177e4SLinus Torvalds for (c = 0; c < channels; ++c, ++bufs) { 18381da177e4SLinus Torvalds char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff); 18391da177e4SLinus Torvalds if (*bufs == NULL) { 18401da177e4SLinus Torvalds snd_pcm_format_set_silence(runtime->format, hwbuf, frames); 18411da177e4SLinus Torvalds } else { 18421da177e4SLinus Torvalds char __user *buf = *bufs + samples_to_bytes(runtime, off); 18431da177e4SLinus Torvalds if (copy_from_user(hwbuf, buf, samples_to_bytes(runtime, frames))) 18441da177e4SLinus Torvalds return -EFAULT; 18451da177e4SLinus Torvalds } 18461da177e4SLinus Torvalds } 18471da177e4SLinus Torvalds } 18481da177e4SLinus Torvalds return 0; 18491da177e4SLinus Torvalds } 18501da177e4SLinus Torvalds 1851877211f5STakashi Iwai snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream, 18521da177e4SLinus Torvalds void __user **bufs, 18531da177e4SLinus Torvalds snd_pcm_uframes_t frames) 18541da177e4SLinus Torvalds { 1855877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 18561da177e4SLinus Torvalds int nonblock; 18577eaa943cSTakashi Iwai int err; 18581da177e4SLinus Torvalds 18597eaa943cSTakashi Iwai err = pcm_sanity_check(substream); 18607eaa943cSTakashi Iwai if (err < 0) 18617eaa943cSTakashi Iwai return err; 18621da177e4SLinus Torvalds runtime = substream->runtime; 18630df63e44STakashi Iwai nonblock = !!(substream->f_flags & O_NONBLOCK); 18641da177e4SLinus Torvalds 18651da177e4SLinus Torvalds if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) 18661da177e4SLinus Torvalds return -EINVAL; 18671da177e4SLinus Torvalds return snd_pcm_lib_write1(substream, (unsigned long)bufs, frames, 18681da177e4SLinus Torvalds nonblock, snd_pcm_lib_writev_transfer); 18691da177e4SLinus Torvalds } 18701da177e4SLinus Torvalds 1871e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_lib_writev); 1872e88e8ae6STakashi Iwai 1873877211f5STakashi Iwai static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream, 18741da177e4SLinus Torvalds unsigned int hwoff, 18751da177e4SLinus Torvalds unsigned long data, unsigned int off, 18761da177e4SLinus Torvalds snd_pcm_uframes_t frames) 18771da177e4SLinus Torvalds { 1878877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 18791da177e4SLinus Torvalds int err; 18801da177e4SLinus Torvalds char __user *buf = (char __user *) data + frames_to_bytes(runtime, off); 18811da177e4SLinus Torvalds if (substream->ops->copy) { 18821da177e4SLinus Torvalds if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0) 18831da177e4SLinus Torvalds return err; 18841da177e4SLinus Torvalds } else { 18851da177e4SLinus Torvalds char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); 18861da177e4SLinus Torvalds if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames))) 18871da177e4SLinus Torvalds return -EFAULT; 18881da177e4SLinus Torvalds } 18891da177e4SLinus Torvalds return 0; 18901da177e4SLinus Torvalds } 18911da177e4SLinus Torvalds 1892877211f5STakashi Iwai static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, 18931da177e4SLinus Torvalds unsigned long data, 18941da177e4SLinus Torvalds snd_pcm_uframes_t size, 18951da177e4SLinus Torvalds int nonblock, 18961da177e4SLinus Torvalds transfer_f transfer) 18971da177e4SLinus Torvalds { 1898877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 18991da177e4SLinus Torvalds snd_pcm_uframes_t xfer = 0; 19001da177e4SLinus Torvalds snd_pcm_uframes_t offset = 0; 19011da177e4SLinus Torvalds int err = 0; 19021da177e4SLinus Torvalds 19031da177e4SLinus Torvalds if (size == 0) 19041da177e4SLinus Torvalds return 0; 19051da177e4SLinus Torvalds 19061da177e4SLinus Torvalds snd_pcm_stream_lock_irq(substream); 19071da177e4SLinus Torvalds switch (runtime->status->state) { 19081da177e4SLinus Torvalds case SNDRV_PCM_STATE_PREPARED: 19091da177e4SLinus Torvalds if (size >= runtime->start_threshold) { 19101da177e4SLinus Torvalds err = snd_pcm_start(substream); 19111da177e4SLinus Torvalds if (err < 0) 19121da177e4SLinus Torvalds goto _end_unlock; 19131da177e4SLinus Torvalds } 19141da177e4SLinus Torvalds break; 19151da177e4SLinus Torvalds case SNDRV_PCM_STATE_DRAINING: 19161da177e4SLinus Torvalds case SNDRV_PCM_STATE_RUNNING: 19171da177e4SLinus Torvalds case SNDRV_PCM_STATE_PAUSED: 19181da177e4SLinus Torvalds break; 19191da177e4SLinus Torvalds case SNDRV_PCM_STATE_XRUN: 19201da177e4SLinus Torvalds err = -EPIPE; 19211da177e4SLinus Torvalds goto _end_unlock; 19221da177e4SLinus Torvalds case SNDRV_PCM_STATE_SUSPENDED: 19231da177e4SLinus Torvalds err = -ESTRPIPE; 19241da177e4SLinus Torvalds goto _end_unlock; 19251da177e4SLinus Torvalds default: 19261da177e4SLinus Torvalds err = -EBADFD; 19271da177e4SLinus Torvalds goto _end_unlock; 19281da177e4SLinus Torvalds } 19291da177e4SLinus Torvalds 19301da177e4SLinus Torvalds while (size > 0) { 19311da177e4SLinus Torvalds snd_pcm_uframes_t frames, appl_ptr, appl_ofs; 19321da177e4SLinus Torvalds snd_pcm_uframes_t avail; 19331da177e4SLinus Torvalds snd_pcm_uframes_t cont; 193431e8960bSTakashi Iwai if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) 19351da177e4SLinus Torvalds snd_pcm_update_hw_ptr(substream); 19361da177e4SLinus Torvalds avail = snd_pcm_capture_avail(runtime); 1937d948035aSTakashi Iwai if (!avail) { 193813075510STakashi Iwai if (runtime->status->state == 193913075510STakashi Iwai SNDRV_PCM_STATE_DRAINING) { 194013075510STakashi Iwai snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); 19411da177e4SLinus Torvalds goto _end_unlock; 19421da177e4SLinus Torvalds } 19431da177e4SLinus Torvalds if (nonblock) { 19441da177e4SLinus Torvalds err = -EAGAIN; 19451da177e4SLinus Torvalds goto _end_unlock; 19461da177e4SLinus Torvalds } 194713075510STakashi Iwai err = wait_for_avail_min(substream, &avail); 194813075510STakashi Iwai if (err < 0) 19491da177e4SLinus Torvalds goto _end_unlock; 195013075510STakashi Iwai if (!avail) 195113075510STakashi Iwai continue; /* draining */ 19521da177e4SLinus Torvalds } 19531da177e4SLinus Torvalds frames = size > avail ? avail : size; 19541da177e4SLinus Torvalds cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; 19551da177e4SLinus Torvalds if (frames > cont) 19561da177e4SLinus Torvalds frames = cont; 19577eaa943cSTakashi Iwai if (snd_BUG_ON(!frames)) { 19587eaa943cSTakashi Iwai snd_pcm_stream_unlock_irq(substream); 19597eaa943cSTakashi Iwai return -EINVAL; 19607eaa943cSTakashi Iwai } 19611da177e4SLinus Torvalds appl_ptr = runtime->control->appl_ptr; 19621da177e4SLinus Torvalds appl_ofs = appl_ptr % runtime->buffer_size; 19631da177e4SLinus Torvalds snd_pcm_stream_unlock_irq(substream); 19641da177e4SLinus Torvalds if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0) 19651da177e4SLinus Torvalds goto _end; 19661da177e4SLinus Torvalds snd_pcm_stream_lock_irq(substream); 19671da177e4SLinus Torvalds switch (runtime->status->state) { 19681da177e4SLinus Torvalds case SNDRV_PCM_STATE_XRUN: 19691da177e4SLinus Torvalds err = -EPIPE; 19701da177e4SLinus Torvalds goto _end_unlock; 19711da177e4SLinus Torvalds case SNDRV_PCM_STATE_SUSPENDED: 19721da177e4SLinus Torvalds err = -ESTRPIPE; 19731da177e4SLinus Torvalds goto _end_unlock; 19741da177e4SLinus Torvalds default: 19751da177e4SLinus Torvalds break; 19761da177e4SLinus Torvalds } 19771da177e4SLinus Torvalds appl_ptr += frames; 19781da177e4SLinus Torvalds if (appl_ptr >= runtime->boundary) 19791da177e4SLinus Torvalds appl_ptr -= runtime->boundary; 19801da177e4SLinus Torvalds runtime->control->appl_ptr = appl_ptr; 19811da177e4SLinus Torvalds if (substream->ops->ack) 19821da177e4SLinus Torvalds substream->ops->ack(substream); 19831da177e4SLinus Torvalds 19841da177e4SLinus Torvalds offset += frames; 19851da177e4SLinus Torvalds size -= frames; 19861da177e4SLinus Torvalds xfer += frames; 19871da177e4SLinus Torvalds } 19881da177e4SLinus Torvalds _end_unlock: 19891da177e4SLinus Torvalds snd_pcm_stream_unlock_irq(substream); 19901da177e4SLinus Torvalds _end: 19911da177e4SLinus Torvalds return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; 19921da177e4SLinus Torvalds } 19931da177e4SLinus Torvalds 1994877211f5STakashi Iwai snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, void __user *buf, snd_pcm_uframes_t size) 19951da177e4SLinus Torvalds { 1996877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 19971da177e4SLinus Torvalds int nonblock; 19987eaa943cSTakashi Iwai int err; 19991da177e4SLinus Torvalds 20007eaa943cSTakashi Iwai err = pcm_sanity_check(substream); 20017eaa943cSTakashi Iwai if (err < 0) 20027eaa943cSTakashi Iwai return err; 20031da177e4SLinus Torvalds runtime = substream->runtime; 20040df63e44STakashi Iwai nonblock = !!(substream->f_flags & O_NONBLOCK); 20051da177e4SLinus Torvalds if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED) 20061da177e4SLinus Torvalds return -EINVAL; 20071da177e4SLinus Torvalds return snd_pcm_lib_read1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_read_transfer); 20081da177e4SLinus Torvalds } 20091da177e4SLinus Torvalds 2010e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_lib_read); 2011e88e8ae6STakashi Iwai 2012877211f5STakashi Iwai static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream, 20131da177e4SLinus Torvalds unsigned int hwoff, 20141da177e4SLinus Torvalds unsigned long data, unsigned int off, 20151da177e4SLinus Torvalds snd_pcm_uframes_t frames) 20161da177e4SLinus Torvalds { 2017877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 20181da177e4SLinus Torvalds int err; 20191da177e4SLinus Torvalds void __user **bufs = (void __user **)data; 20201da177e4SLinus Torvalds int channels = runtime->channels; 20211da177e4SLinus Torvalds int c; 20221da177e4SLinus Torvalds if (substream->ops->copy) { 20231da177e4SLinus Torvalds for (c = 0; c < channels; ++c, ++bufs) { 20241da177e4SLinus Torvalds char __user *buf; 20251da177e4SLinus Torvalds if (*bufs == NULL) 20261da177e4SLinus Torvalds continue; 20271da177e4SLinus Torvalds buf = *bufs + samples_to_bytes(runtime, off); 20281da177e4SLinus Torvalds if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0) 20291da177e4SLinus Torvalds return err; 20301da177e4SLinus Torvalds } 20311da177e4SLinus Torvalds } else { 20321da177e4SLinus Torvalds snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels; 20331da177e4SLinus Torvalds for (c = 0; c < channels; ++c, ++bufs) { 20341da177e4SLinus Torvalds char *hwbuf; 20351da177e4SLinus Torvalds char __user *buf; 20361da177e4SLinus Torvalds if (*bufs == NULL) 20371da177e4SLinus Torvalds continue; 20381da177e4SLinus Torvalds 20391da177e4SLinus Torvalds hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff); 20401da177e4SLinus Torvalds buf = *bufs + samples_to_bytes(runtime, off); 20411da177e4SLinus Torvalds if (copy_to_user(buf, hwbuf, samples_to_bytes(runtime, frames))) 20421da177e4SLinus Torvalds return -EFAULT; 20431da177e4SLinus Torvalds } 20441da177e4SLinus Torvalds } 20451da177e4SLinus Torvalds return 0; 20461da177e4SLinus Torvalds } 20471da177e4SLinus Torvalds 2048877211f5STakashi Iwai snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream, 20491da177e4SLinus Torvalds void __user **bufs, 20501da177e4SLinus Torvalds snd_pcm_uframes_t frames) 20511da177e4SLinus Torvalds { 2052877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 20531da177e4SLinus Torvalds int nonblock; 20547eaa943cSTakashi Iwai int err; 20551da177e4SLinus Torvalds 20567eaa943cSTakashi Iwai err = pcm_sanity_check(substream); 20577eaa943cSTakashi Iwai if (err < 0) 20587eaa943cSTakashi Iwai return err; 20591da177e4SLinus Torvalds runtime = substream->runtime; 20601da177e4SLinus Torvalds if (runtime->status->state == SNDRV_PCM_STATE_OPEN) 20611da177e4SLinus Torvalds return -EBADFD; 20621da177e4SLinus Torvalds 20630df63e44STakashi Iwai nonblock = !!(substream->f_flags & O_NONBLOCK); 20641da177e4SLinus Torvalds if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) 20651da177e4SLinus Torvalds return -EINVAL; 20661da177e4SLinus Torvalds return snd_pcm_lib_read1(substream, (unsigned long)bufs, frames, nonblock, snd_pcm_lib_readv_transfer); 20671da177e4SLinus Torvalds } 20681da177e4SLinus Torvalds 20691da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_lib_readv); 2070