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> 26d81a6d71SPaul Gortmaker #include <linux/export.h> 271da177e4SLinus Torvalds #include <sound/core.h> 281da177e4SLinus Torvalds #include <sound/control.h> 291da177e4SLinus Torvalds #include <sound/info.h> 301da177e4SLinus Torvalds #include <sound/pcm.h> 311da177e4SLinus Torvalds #include <sound/pcm_params.h> 321da177e4SLinus Torvalds #include <sound/timer.h> 331da177e4SLinus Torvalds 341da177e4SLinus Torvalds /* 351da177e4SLinus Torvalds * fill ring buffer with silence 361da177e4SLinus Torvalds * runtime->silence_start: starting pointer to silence area 371da177e4SLinus Torvalds * runtime->silence_filled: size filled with silence 381da177e4SLinus Torvalds * runtime->silence_threshold: threshold from application 391da177e4SLinus Torvalds * runtime->silence_size: maximal size from application 401da177e4SLinus Torvalds * 411da177e4SLinus Torvalds * when runtime->silence_size >= runtime->boundary - fill processed area with silence immediately 421da177e4SLinus Torvalds */ 43877211f5STakashi Iwai void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_uframes_t new_hw_ptr) 441da177e4SLinus Torvalds { 45877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 461da177e4SLinus Torvalds snd_pcm_uframes_t frames, ofs, transfer; 471da177e4SLinus Torvalds 481da177e4SLinus Torvalds if (runtime->silence_size < runtime->boundary) { 491da177e4SLinus Torvalds snd_pcm_sframes_t noise_dist, n; 501da177e4SLinus Torvalds if (runtime->silence_start != runtime->control->appl_ptr) { 511da177e4SLinus Torvalds n = runtime->control->appl_ptr - runtime->silence_start; 521da177e4SLinus Torvalds if (n < 0) 531da177e4SLinus Torvalds n += runtime->boundary; 541da177e4SLinus Torvalds if ((snd_pcm_uframes_t)n < runtime->silence_filled) 551da177e4SLinus Torvalds runtime->silence_filled -= n; 561da177e4SLinus Torvalds else 571da177e4SLinus Torvalds runtime->silence_filled = 0; 581da177e4SLinus Torvalds runtime->silence_start = runtime->control->appl_ptr; 591da177e4SLinus Torvalds } 60235475cbSTakashi Iwai if (runtime->silence_filled >= runtime->buffer_size) 611da177e4SLinus Torvalds return; 621da177e4SLinus Torvalds noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silence_filled; 631da177e4SLinus Torvalds if (noise_dist >= (snd_pcm_sframes_t) runtime->silence_threshold) 641da177e4SLinus Torvalds return; 651da177e4SLinus Torvalds frames = runtime->silence_threshold - noise_dist; 661da177e4SLinus Torvalds if (frames > runtime->silence_size) 671da177e4SLinus Torvalds frames = runtime->silence_size; 681da177e4SLinus Torvalds } else { 691da177e4SLinus Torvalds if (new_hw_ptr == ULONG_MAX) { /* initialization */ 701da177e4SLinus Torvalds snd_pcm_sframes_t avail = snd_pcm_playback_hw_avail(runtime); 719e216e8aSJaroslav Kysela if (avail > runtime->buffer_size) 729e216e8aSJaroslav Kysela avail = runtime->buffer_size; 731da177e4SLinus Torvalds runtime->silence_filled = avail > 0 ? avail : 0; 741da177e4SLinus Torvalds runtime->silence_start = (runtime->status->hw_ptr + 751da177e4SLinus Torvalds runtime->silence_filled) % 761da177e4SLinus Torvalds runtime->boundary; 771da177e4SLinus Torvalds } else { 781da177e4SLinus Torvalds ofs = runtime->status->hw_ptr; 791da177e4SLinus Torvalds frames = new_hw_ptr - ofs; 801da177e4SLinus Torvalds if ((snd_pcm_sframes_t)frames < 0) 811da177e4SLinus Torvalds frames += runtime->boundary; 821da177e4SLinus Torvalds runtime->silence_filled -= frames; 831da177e4SLinus Torvalds if ((snd_pcm_sframes_t)runtime->silence_filled < 0) { 841da177e4SLinus Torvalds runtime->silence_filled = 0; 859a826ddbSClemens Ladisch runtime->silence_start = new_hw_ptr; 861da177e4SLinus Torvalds } else { 879a826ddbSClemens Ladisch runtime->silence_start = ofs; 881da177e4SLinus Torvalds } 891da177e4SLinus Torvalds } 901da177e4SLinus Torvalds frames = runtime->buffer_size - runtime->silence_filled; 911da177e4SLinus Torvalds } 927eaa943cSTakashi Iwai if (snd_BUG_ON(frames > runtime->buffer_size)) 937eaa943cSTakashi Iwai return; 941da177e4SLinus Torvalds if (frames == 0) 951da177e4SLinus Torvalds return; 969a826ddbSClemens Ladisch ofs = runtime->silence_start % runtime->buffer_size; 971da177e4SLinus Torvalds while (frames > 0) { 981da177e4SLinus Torvalds transfer = ofs + frames > runtime->buffer_size ? runtime->buffer_size - ofs : frames; 991da177e4SLinus Torvalds if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || 1001da177e4SLinus Torvalds runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) { 1011da177e4SLinus Torvalds if (substream->ops->silence) { 1021da177e4SLinus Torvalds int err; 1031da177e4SLinus Torvalds err = substream->ops->silence(substream, -1, ofs, transfer); 1047eaa943cSTakashi Iwai snd_BUG_ON(err < 0); 1051da177e4SLinus Torvalds } else { 1061da177e4SLinus Torvalds char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, ofs); 1071da177e4SLinus Torvalds snd_pcm_format_set_silence(runtime->format, hwbuf, transfer * runtime->channels); 1081da177e4SLinus Torvalds } 1091da177e4SLinus Torvalds } else { 1101da177e4SLinus Torvalds unsigned int c; 1111da177e4SLinus Torvalds unsigned int channels = runtime->channels; 1121da177e4SLinus Torvalds if (substream->ops->silence) { 1131da177e4SLinus Torvalds for (c = 0; c < channels; ++c) { 1141da177e4SLinus Torvalds int err; 1151da177e4SLinus Torvalds err = substream->ops->silence(substream, c, ofs, transfer); 1167eaa943cSTakashi Iwai snd_BUG_ON(err < 0); 1171da177e4SLinus Torvalds } 1181da177e4SLinus Torvalds } else { 1191da177e4SLinus Torvalds size_t dma_csize = runtime->dma_bytes / channels; 1201da177e4SLinus Torvalds for (c = 0; c < channels; ++c) { 1211da177e4SLinus Torvalds char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, ofs); 1221da177e4SLinus Torvalds snd_pcm_format_set_silence(runtime->format, hwbuf, transfer); 1231da177e4SLinus Torvalds } 1241da177e4SLinus Torvalds } 1251da177e4SLinus Torvalds } 1261da177e4SLinus Torvalds runtime->silence_filled += transfer; 1271da177e4SLinus Torvalds frames -= transfer; 1281da177e4SLinus Torvalds ofs = 0; 1291da177e4SLinus Torvalds } 1301da177e4SLinus Torvalds } 1311da177e4SLinus Torvalds 132acb03d44SEliot Blennerhassett #ifdef CONFIG_SND_DEBUG 133acb03d44SEliot Blennerhassett void snd_pcm_debug_name(struct snd_pcm_substream *substream, 134c0070110STakashi Iwai char *name, size_t len) 1351da177e4SLinus Torvalds { 136c0070110STakashi Iwai snprintf(name, len, "pcmC%dD%d%c:%d", 1371da177e4SLinus Torvalds substream->pcm->card->number, 1381da177e4SLinus Torvalds substream->pcm->device, 139c0070110STakashi Iwai substream->stream ? 'c' : 'p', 140c0070110STakashi Iwai substream->number); 141c0070110STakashi Iwai } 142acb03d44SEliot Blennerhassett EXPORT_SYMBOL(snd_pcm_debug_name); 143acb03d44SEliot Blennerhassett #endif 144c0070110STakashi Iwai 1454d96eb25SJaroslav Kysela #define XRUN_DEBUG_BASIC (1<<0) 1464d96eb25SJaroslav Kysela #define XRUN_DEBUG_STACK (1<<1) /* dump also stack */ 1474d96eb25SJaroslav Kysela #define XRUN_DEBUG_JIFFIESCHECK (1<<2) /* do jiffies check */ 1484d96eb25SJaroslav Kysela #define XRUN_DEBUG_PERIODUPDATE (1<<3) /* full period update info */ 1494d96eb25SJaroslav Kysela #define XRUN_DEBUG_HWPTRUPDATE (1<<4) /* full hwptr update info */ 1504d96eb25SJaroslav Kysela #define XRUN_DEBUG_LOG (1<<5) /* show last 10 positions on err */ 1514d96eb25SJaroslav Kysela #define XRUN_DEBUG_LOGONCE (1<<6) /* do above only once */ 1524d96eb25SJaroslav Kysela 1534d96eb25SJaroslav Kysela #ifdef CONFIG_SND_PCM_XRUN_DEBUG 1544d96eb25SJaroslav Kysela 1554d96eb25SJaroslav Kysela #define xrun_debug(substream, mask) \ 1564d96eb25SJaroslav Kysela ((substream)->pstr->xrun_debug & (mask)) 1570f17014bSJarkko Nikula #else 1580f17014bSJarkko Nikula #define xrun_debug(substream, mask) 0 1590f17014bSJarkko Nikula #endif 1604d96eb25SJaroslav Kysela 1614d96eb25SJaroslav Kysela #define dump_stack_on_xrun(substream) do { \ 1624d96eb25SJaroslav Kysela if (xrun_debug(substream, XRUN_DEBUG_STACK)) \ 1634d96eb25SJaroslav Kysela dump_stack(); \ 1644d96eb25SJaroslav Kysela } while (0) 1654d96eb25SJaroslav Kysela 1661da177e4SLinus Torvalds static void xrun(struct snd_pcm_substream *substream) 1671da177e4SLinus Torvalds { 16813f040f9SJaroslav Kysela struct snd_pcm_runtime *runtime = substream->runtime; 16913f040f9SJaroslav Kysela 17013f040f9SJaroslav Kysela if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) 17113f040f9SJaroslav Kysela snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); 1721da177e4SLinus Torvalds snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); 173741b20cfSJaroslav Kysela if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { 174c0070110STakashi Iwai char name[16]; 175acb03d44SEliot Blennerhassett snd_pcm_debug_name(substream, name, sizeof(name)); 176c0070110STakashi Iwai snd_printd(KERN_DEBUG "XRUN: %s\n", name); 177ed3da3d9STakashi Iwai dump_stack_on_xrun(substream); 1781da177e4SLinus Torvalds } 1791da177e4SLinus Torvalds } 1801da177e4SLinus Torvalds 1810f17014bSJarkko Nikula #ifdef CONFIG_SND_PCM_XRUN_DEBUG 1824d96eb25SJaroslav Kysela #define hw_ptr_error(substream, fmt, args...) \ 1834d96eb25SJaroslav Kysela do { \ 1844d96eb25SJaroslav Kysela if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \ 185f240406bSJaroslav Kysela xrun_log_show(substream); \ 1864d96eb25SJaroslav Kysela if (printk_ratelimit()) { \ 1874d96eb25SJaroslav Kysela snd_printd("PCM: " fmt, ##args); \ 1884d96eb25SJaroslav Kysela } \ 1894d96eb25SJaroslav Kysela dump_stack_on_xrun(substream); \ 1904d96eb25SJaroslav Kysela } \ 1914d96eb25SJaroslav Kysela } while (0) 1924d96eb25SJaroslav Kysela 1934d96eb25SJaroslav Kysela #define XRUN_LOG_CNT 10 1944d96eb25SJaroslav Kysela 1954d96eb25SJaroslav Kysela struct hwptr_log_entry { 196ec08b144SBen Gardiner unsigned int in_interrupt; 1974d96eb25SJaroslav Kysela unsigned long jiffies; 1981da177e4SLinus Torvalds snd_pcm_uframes_t pos; 1994d96eb25SJaroslav Kysela snd_pcm_uframes_t period_size; 2004d96eb25SJaroslav Kysela snd_pcm_uframes_t buffer_size; 2014d96eb25SJaroslav Kysela snd_pcm_uframes_t old_hw_ptr; 2024d96eb25SJaroslav Kysela snd_pcm_uframes_t hw_ptr_base; 2034d96eb25SJaroslav Kysela }; 2041da177e4SLinus Torvalds 2054d96eb25SJaroslav Kysela struct snd_pcm_hwptr_log { 2064d96eb25SJaroslav Kysela unsigned int idx; 2074d96eb25SJaroslav Kysela unsigned int hit: 1; 2084d96eb25SJaroslav Kysela struct hwptr_log_entry entries[XRUN_LOG_CNT]; 2094d96eb25SJaroslav Kysela }; 2104d96eb25SJaroslav Kysela 2114d96eb25SJaroslav Kysela static void xrun_log(struct snd_pcm_substream *substream, 212ec08b144SBen Gardiner snd_pcm_uframes_t pos, int in_interrupt) 2134d96eb25SJaroslav Kysela { 2144d96eb25SJaroslav Kysela struct snd_pcm_runtime *runtime = substream->runtime; 2154d96eb25SJaroslav Kysela struct snd_pcm_hwptr_log *log = runtime->hwptr_log; 2164d96eb25SJaroslav Kysela struct hwptr_log_entry *entry; 2174d96eb25SJaroslav Kysela 2184d96eb25SJaroslav Kysela if (log == NULL) { 2194d96eb25SJaroslav Kysela log = kzalloc(sizeof(*log), GFP_ATOMIC); 2204d96eb25SJaroslav Kysela if (log == NULL) 2214d96eb25SJaroslav Kysela return; 2224d96eb25SJaroslav Kysela runtime->hwptr_log = log; 2234d96eb25SJaroslav Kysela } else { 2244d96eb25SJaroslav Kysela if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit) 2254d96eb25SJaroslav Kysela return; 2264d96eb25SJaroslav Kysela } 2274d96eb25SJaroslav Kysela entry = &log->entries[log->idx]; 228ec08b144SBen Gardiner entry->in_interrupt = in_interrupt; 2294d96eb25SJaroslav Kysela entry->jiffies = jiffies; 2304d96eb25SJaroslav Kysela entry->pos = pos; 2314d96eb25SJaroslav Kysela entry->period_size = runtime->period_size; 232c80c1d54SJoe Perches entry->buffer_size = runtime->buffer_size; 2334d96eb25SJaroslav Kysela entry->old_hw_ptr = runtime->status->hw_ptr; 2344d96eb25SJaroslav Kysela entry->hw_ptr_base = runtime->hw_ptr_base; 2354d96eb25SJaroslav Kysela log->idx = (log->idx + 1) % XRUN_LOG_CNT; 2364d96eb25SJaroslav Kysela } 2374d96eb25SJaroslav Kysela 2384d96eb25SJaroslav Kysela static void xrun_log_show(struct snd_pcm_substream *substream) 2394d96eb25SJaroslav Kysela { 2404d96eb25SJaroslav Kysela struct snd_pcm_hwptr_log *log = substream->runtime->hwptr_log; 2414d96eb25SJaroslav Kysela struct hwptr_log_entry *entry; 242c0070110STakashi Iwai char name[16]; 2434d96eb25SJaroslav Kysela unsigned int idx; 2444d96eb25SJaroslav Kysela int cnt; 2454d96eb25SJaroslav Kysela 2464d96eb25SJaroslav Kysela if (log == NULL) 2474d96eb25SJaroslav Kysela return; 2484d96eb25SJaroslav Kysela if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit) 2494d96eb25SJaroslav Kysela return; 250acb03d44SEliot Blennerhassett snd_pcm_debug_name(substream, name, sizeof(name)); 2514d96eb25SJaroslav Kysela for (cnt = 0, idx = log->idx; cnt < XRUN_LOG_CNT; cnt++) { 2524d96eb25SJaroslav Kysela entry = &log->entries[idx]; 2534d96eb25SJaroslav Kysela if (entry->period_size == 0) 2544d96eb25SJaroslav Kysela break; 255ec08b144SBen Gardiner snd_printd("hwptr log: %s: %sj=%lu, pos=%ld/%ld/%ld, " 256f240406bSJaroslav Kysela "hwptr=%ld/%ld\n", 257ec08b144SBen Gardiner name, entry->in_interrupt ? "[Q] " : "", 258ec08b144SBen Gardiner entry->jiffies, 259ec08b144SBen Gardiner (unsigned long)entry->pos, 2604d96eb25SJaroslav Kysela (unsigned long)entry->period_size, 2614d96eb25SJaroslav Kysela (unsigned long)entry->buffer_size, 2624d96eb25SJaroslav Kysela (unsigned long)entry->old_hw_ptr, 263f240406bSJaroslav Kysela (unsigned long)entry->hw_ptr_base); 2644d96eb25SJaroslav Kysela idx++; 2654d96eb25SJaroslav Kysela idx %= XRUN_LOG_CNT; 2667c22f1aaSTakashi Iwai } 2674d96eb25SJaroslav Kysela log->hit = 1; 2681da177e4SLinus Torvalds } 2691da177e4SLinus Torvalds 2704d96eb25SJaroslav Kysela #else /* ! CONFIG_SND_PCM_XRUN_DEBUG */ 2714d96eb25SJaroslav Kysela 2724d96eb25SJaroslav Kysela #define hw_ptr_error(substream, fmt, args...) do { } while (0) 273217658f4SBen Gardiner #define xrun_log(substream, pos, in_interrupt) do { } while (0) 2744d96eb25SJaroslav Kysela #define xrun_log_show(substream) do { } while (0) 2754d96eb25SJaroslav Kysela 2764d96eb25SJaroslav Kysela #endif 2774d96eb25SJaroslav Kysela 2781250932eSJaroslav Kysela int snd_pcm_update_state(struct snd_pcm_substream *substream, 279877211f5STakashi Iwai struct snd_pcm_runtime *runtime) 2801da177e4SLinus Torvalds { 2811da177e4SLinus Torvalds snd_pcm_uframes_t avail; 2821da177e4SLinus Torvalds 2831da177e4SLinus Torvalds if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 2841da177e4SLinus Torvalds avail = snd_pcm_playback_avail(runtime); 2851da177e4SLinus Torvalds else 2861da177e4SLinus Torvalds avail = snd_pcm_capture_avail(runtime); 2871da177e4SLinus Torvalds if (avail > runtime->avail_max) 2881da177e4SLinus Torvalds runtime->avail_max = avail; 2894cdc115fSTakashi Iwai if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { 2904cdc115fSTakashi Iwai if (avail >= runtime->buffer_size) { 2911da177e4SLinus Torvalds snd_pcm_drain_done(substream); 2924cdc115fSTakashi Iwai return -EPIPE; 2934cdc115fSTakashi Iwai } 2944cdc115fSTakashi Iwai } else { 2954cdc115fSTakashi Iwai if (avail >= runtime->stop_threshold) { 2961da177e4SLinus Torvalds xrun(substream); 2971da177e4SLinus Torvalds return -EPIPE; 2981da177e4SLinus Torvalds } 2994cdc115fSTakashi Iwai } 3005daeba34SDavid Dillow if (runtime->twake) { 3015daeba34SDavid Dillow if (avail >= runtime->twake) 3025daeba34SDavid Dillow wake_up(&runtime->tsleep); 3035daeba34SDavid Dillow } else if (avail >= runtime->control->avail_min) 3045daeba34SDavid Dillow wake_up(&runtime->sleep); 3051da177e4SLinus Torvalds return 0; 3061da177e4SLinus Torvalds } 3071da177e4SLinus Torvalds 308f240406bSJaroslav Kysela static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, 309f240406bSJaroslav Kysela unsigned int in_interrupt) 3101da177e4SLinus Torvalds { 311877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 3121da177e4SLinus Torvalds snd_pcm_uframes_t pos; 313f240406bSJaroslav Kysela snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base; 314bbf6ad13SJaroslav Kysela snd_pcm_sframes_t hdelta, delta; 315bbf6ad13SJaroslav Kysela unsigned long jdelta; 3161da177e4SLinus Torvalds 317bbf6ad13SJaroslav Kysela old_hw_ptr = runtime->status->hw_ptr; 318f240406bSJaroslav Kysela pos = substream->ops->pointer(substream); 3191da177e4SLinus Torvalds if (pos == SNDRV_PCM_POS_XRUN) { 3201da177e4SLinus Torvalds xrun(substream); 3211da177e4SLinus Torvalds return -EPIPE; 3221da177e4SLinus Torvalds } 323f240406bSJaroslav Kysela if (pos >= runtime->buffer_size) { 324f240406bSJaroslav Kysela if (printk_ratelimit()) { 325cedb8118STakashi Iwai char name[16]; 326acb03d44SEliot Blennerhassett snd_pcm_debug_name(substream, name, sizeof(name)); 327f240406bSJaroslav Kysela xrun_log_show(substream); 328f240406bSJaroslav Kysela snd_printd(KERN_ERR "BUG: %s, pos = %ld, " 329f240406bSJaroslav Kysela "buffer size = %ld, period size = %ld\n", 330f240406bSJaroslav Kysela name, pos, runtime->buffer_size, 331f240406bSJaroslav Kysela runtime->period_size); 332cedb8118STakashi Iwai } 333f240406bSJaroslav Kysela pos = 0; 334f240406bSJaroslav Kysela } 335f240406bSJaroslav Kysela pos -= pos % runtime->min_align; 336f240406bSJaroslav Kysela if (xrun_debug(substream, XRUN_DEBUG_LOG)) 337ec08b144SBen Gardiner xrun_log(substream, pos, in_interrupt); 338ed3da3d9STakashi Iwai hw_base = runtime->hw_ptr_base; 339ed3da3d9STakashi Iwai new_hw_ptr = hw_base + pos; 340f240406bSJaroslav Kysela if (in_interrupt) { 341f240406bSJaroslav Kysela /* we know that one period was processed */ 342f240406bSJaroslav Kysela /* delta = "expected next hw_ptr" for in_interrupt != 0 */ 343e7636925SJaroslav Kysela delta = runtime->hw_ptr_interrupt + runtime->period_size; 344f240406bSJaroslav Kysela if (delta > new_hw_ptr) { 345bd76af0fSJaroslav Kysela /* check for double acknowledged interrupts */ 346bd76af0fSJaroslav Kysela hdelta = jiffies - runtime->hw_ptr_jiffies; 347bd76af0fSJaroslav Kysela if (hdelta > runtime->hw_ptr_buffer_jiffies/2) { 348f240406bSJaroslav Kysela hw_base += runtime->buffer_size; 349f240406bSJaroslav Kysela if (hw_base >= runtime->boundary) 350f240406bSJaroslav Kysela hw_base = 0; 351f240406bSJaroslav Kysela new_hw_ptr = hw_base + pos; 352f240406bSJaroslav Kysela goto __delta; 353ded652f7STakashi Iwai } 354f240406bSJaroslav Kysela } 355bd76af0fSJaroslav Kysela } 356f240406bSJaroslav Kysela /* new_hw_ptr might be lower than old_hw_ptr in case when */ 357f240406bSJaroslav Kysela /* pointer crosses the end of the ring buffer */ 358f240406bSJaroslav Kysela if (new_hw_ptr < old_hw_ptr) { 359ed3da3d9STakashi Iwai hw_base += runtime->buffer_size; 3608b22d943STakashi Iwai if (hw_base >= runtime->boundary) 361ed3da3d9STakashi Iwai hw_base = 0; 362ed3da3d9STakashi Iwai new_hw_ptr = hw_base + pos; 3631da177e4SLinus Torvalds } 364f240406bSJaroslav Kysela __delta: 365b406e610SClemens Ladisch delta = new_hw_ptr - old_hw_ptr; 366b406e610SClemens Ladisch if (delta < 0) 367b406e610SClemens Ladisch delta += runtime->boundary; 368f240406bSJaroslav Kysela if (xrun_debug(substream, in_interrupt ? 369f240406bSJaroslav Kysela XRUN_DEBUG_PERIODUPDATE : XRUN_DEBUG_HWPTRUPDATE)) { 370f240406bSJaroslav Kysela char name[16]; 371acb03d44SEliot Blennerhassett snd_pcm_debug_name(substream, name, sizeof(name)); 372f240406bSJaroslav Kysela snd_printd("%s_update: %s: pos=%u/%u/%u, " 373f240406bSJaroslav Kysela "hwptr=%ld/%ld/%ld/%ld\n", 374f240406bSJaroslav Kysela in_interrupt ? "period" : "hwptr", 375f240406bSJaroslav Kysela name, 376f240406bSJaroslav Kysela (unsigned int)pos, 377f240406bSJaroslav Kysela (unsigned int)runtime->period_size, 378f240406bSJaroslav Kysela (unsigned int)runtime->buffer_size, 379f240406bSJaroslav Kysela (unsigned long)delta, 380f240406bSJaroslav Kysela (unsigned long)old_hw_ptr, 381f240406bSJaroslav Kysela (unsigned long)new_hw_ptr, 382f240406bSJaroslav Kysela (unsigned long)runtime->hw_ptr_base); 383f240406bSJaroslav Kysela } 384ab69a490SClemens Ladisch 38559ff878fSClemens Ladisch if (runtime->no_period_wakeup) { 38612ff414eSKelly Anderson snd_pcm_sframes_t xrun_threshold; 38759ff878fSClemens Ladisch /* 38859ff878fSClemens Ladisch * Without regular period interrupts, we have to check 38959ff878fSClemens Ladisch * the elapsed time to detect xruns. 39059ff878fSClemens Ladisch */ 39159ff878fSClemens Ladisch jdelta = jiffies - runtime->hw_ptr_jiffies; 39247228e48SClemens Ladisch if (jdelta < runtime->hw_ptr_buffer_jiffies / 2) 39347228e48SClemens Ladisch goto no_delta_check; 39459ff878fSClemens Ladisch hdelta = jdelta - delta * HZ / runtime->rate; 39512ff414eSKelly Anderson xrun_threshold = runtime->hw_ptr_buffer_jiffies / 2 + 1; 39612ff414eSKelly Anderson while (hdelta > xrun_threshold) { 39759ff878fSClemens Ladisch delta += runtime->buffer_size; 39859ff878fSClemens Ladisch hw_base += runtime->buffer_size; 39959ff878fSClemens Ladisch if (hw_base >= runtime->boundary) 40059ff878fSClemens Ladisch hw_base = 0; 40159ff878fSClemens Ladisch new_hw_ptr = hw_base + pos; 40259ff878fSClemens Ladisch hdelta -= runtime->hw_ptr_buffer_jiffies; 40359ff878fSClemens Ladisch } 404ab69a490SClemens Ladisch goto no_delta_check; 40559ff878fSClemens Ladisch } 406ab69a490SClemens Ladisch 407f240406bSJaroslav Kysela /* something must be really wrong */ 4087b3a177bSJaroslav Kysela if (delta >= runtime->buffer_size + runtime->period_size) { 409f240406bSJaroslav Kysela hw_ptr_error(substream, 410f240406bSJaroslav Kysela "Unexpected hw_pointer value %s" 411f240406bSJaroslav Kysela "(stream=%i, pos=%ld, new_hw_ptr=%ld, " 412f240406bSJaroslav Kysela "old_hw_ptr=%ld)\n", 413f240406bSJaroslav Kysela in_interrupt ? "[Q] " : "[P]", 414f240406bSJaroslav Kysela substream->stream, (long)pos, 415f240406bSJaroslav Kysela (long)new_hw_ptr, (long)old_hw_ptr); 416f240406bSJaroslav Kysela return 0; 4171da177e4SLinus Torvalds } 418c87d9732STakashi Iwai 419c87d9732STakashi Iwai /* Do jiffies check only in xrun_debug mode */ 420741b20cfSJaroslav Kysela if (!xrun_debug(substream, XRUN_DEBUG_JIFFIESCHECK)) 421c87d9732STakashi Iwai goto no_jiffies_check; 422c87d9732STakashi Iwai 4233e5b5016STakashi Iwai /* Skip the jiffies check for hardwares with BATCH flag. 4243e5b5016STakashi Iwai * Such hardware usually just increases the position at each IRQ, 4253e5b5016STakashi Iwai * thus it can't give any strange position. 4263e5b5016STakashi Iwai */ 4273e5b5016STakashi Iwai if (runtime->hw.info & SNDRV_PCM_INFO_BATCH) 4283e5b5016STakashi Iwai goto no_jiffies_check; 429f240406bSJaroslav Kysela hdelta = delta; 430a4444da3SJaroslav Kysela if (hdelta < runtime->delay) 431a4444da3SJaroslav Kysela goto no_jiffies_check; 432a4444da3SJaroslav Kysela hdelta -= runtime->delay; 433bbf6ad13SJaroslav Kysela jdelta = jiffies - runtime->hw_ptr_jiffies; 434bbf6ad13SJaroslav Kysela if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) { 435bbf6ad13SJaroslav Kysela delta = jdelta / 436bbf6ad13SJaroslav Kysela (((runtime->period_size * HZ) / runtime->rate) 437bbf6ad13SJaroslav Kysela + HZ/100); 438f240406bSJaroslav Kysela /* move new_hw_ptr according jiffies not pos variable */ 439f240406bSJaroslav Kysela new_hw_ptr = old_hw_ptr; 440ed69c6a8SJaroslav Kysela hw_base = delta; 441f240406bSJaroslav Kysela /* use loop to avoid checks for delta overflows */ 442f240406bSJaroslav Kysela /* the delta value is small or zero in most cases */ 443f240406bSJaroslav Kysela while (delta > 0) { 444f240406bSJaroslav Kysela new_hw_ptr += runtime->period_size; 445f240406bSJaroslav Kysela if (new_hw_ptr >= runtime->boundary) 446f240406bSJaroslav Kysela new_hw_ptr -= runtime->boundary; 447f240406bSJaroslav Kysela delta--; 448f240406bSJaroslav Kysela } 449f240406bSJaroslav Kysela /* align hw_base to buffer_size */ 450bbf6ad13SJaroslav Kysela hw_ptr_error(substream, 451f240406bSJaroslav Kysela "hw_ptr skipping! %s" 452bbf6ad13SJaroslav Kysela "(pos=%ld, delta=%ld, period=%ld, " 453f240406bSJaroslav Kysela "jdelta=%lu/%lu/%lu, hw_ptr=%ld/%ld)\n", 454f240406bSJaroslav Kysela in_interrupt ? "[Q] " : "", 455bbf6ad13SJaroslav Kysela (long)pos, (long)hdelta, 456bbf6ad13SJaroslav Kysela (long)runtime->period_size, jdelta, 457ed69c6a8SJaroslav Kysela ((hdelta * HZ) / runtime->rate), hw_base, 458f240406bSJaroslav Kysela (unsigned long)old_hw_ptr, 459f240406bSJaroslav Kysela (unsigned long)new_hw_ptr); 460ed69c6a8SJaroslav Kysela /* reset values to proper state */ 461ed69c6a8SJaroslav Kysela delta = 0; 462ed69c6a8SJaroslav Kysela hw_base = new_hw_ptr - (new_hw_ptr % runtime->buffer_size); 463bbf6ad13SJaroslav Kysela } 4643e5b5016STakashi Iwai no_jiffies_check: 465bbf6ad13SJaroslav Kysela if (delta > runtime->period_size + runtime->period_size / 2) { 466ed3da3d9STakashi Iwai hw_ptr_error(substream, 467f240406bSJaroslav Kysela "Lost interrupts? %s" 468f240406bSJaroslav Kysela "(stream=%i, delta=%ld, new_hw_ptr=%ld, " 469f240406bSJaroslav Kysela "old_hw_ptr=%ld)\n", 470f240406bSJaroslav Kysela in_interrupt ? "[Q] " : "", 471ed3da3d9STakashi Iwai substream->stream, (long)delta, 472f240406bSJaroslav Kysela (long)new_hw_ptr, 473f240406bSJaroslav Kysela (long)old_hw_ptr); 4741da177e4SLinus Torvalds } 475f240406bSJaroslav Kysela 476ab69a490SClemens Ladisch no_delta_check: 477f240406bSJaroslav Kysela if (runtime->status->hw_ptr == new_hw_ptr) 478f240406bSJaroslav Kysela return 0; 479ab1863fcSTakashi Iwai 4801da177e4SLinus Torvalds if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && 4811da177e4SLinus Torvalds runtime->silence_size > 0) 4821da177e4SLinus Torvalds snd_pcm_playback_silence(substream, new_hw_ptr); 4831da177e4SLinus Torvalds 484e7636925SJaroslav Kysela if (in_interrupt) { 485ead4046bSClemens Ladisch delta = new_hw_ptr - runtime->hw_ptr_interrupt; 486ead4046bSClemens Ladisch if (delta < 0) 487ead4046bSClemens Ladisch delta += runtime->boundary; 488ead4046bSClemens Ladisch delta -= (snd_pcm_uframes_t)delta % runtime->period_size; 489ead4046bSClemens Ladisch runtime->hw_ptr_interrupt += delta; 490ead4046bSClemens Ladisch if (runtime->hw_ptr_interrupt >= runtime->boundary) 491ead4046bSClemens Ladisch runtime->hw_ptr_interrupt -= runtime->boundary; 492e7636925SJaroslav Kysela } 493ed3da3d9STakashi Iwai runtime->hw_ptr_base = hw_base; 4941da177e4SLinus Torvalds runtime->status->hw_ptr = new_hw_ptr; 495bbf6ad13SJaroslav Kysela runtime->hw_ptr_jiffies = jiffies; 49613f040f9SJaroslav Kysela if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) 49713f040f9SJaroslav Kysela snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); 4981da177e4SLinus Torvalds 4991250932eSJaroslav Kysela return snd_pcm_update_state(substream, runtime); 5001da177e4SLinus Torvalds } 5011da177e4SLinus Torvalds 5021da177e4SLinus Torvalds /* CAUTION: call it with irq disabled */ 503877211f5STakashi Iwai int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) 5041da177e4SLinus Torvalds { 505f240406bSJaroslav Kysela return snd_pcm_update_hw_ptr0(substream, 0); 5061da177e4SLinus Torvalds } 5071da177e4SLinus Torvalds 5081da177e4SLinus Torvalds /** 5091da177e4SLinus Torvalds * snd_pcm_set_ops - set the PCM operators 5101da177e4SLinus Torvalds * @pcm: the pcm instance 5111da177e4SLinus Torvalds * @direction: stream direction, SNDRV_PCM_STREAM_XXX 5121da177e4SLinus Torvalds * @ops: the operator table 5131da177e4SLinus Torvalds * 5141da177e4SLinus Torvalds * Sets the given PCM operators to the pcm instance. 5151da177e4SLinus Torvalds */ 516877211f5STakashi Iwai void snd_pcm_set_ops(struct snd_pcm *pcm, int direction, struct snd_pcm_ops *ops) 5171da177e4SLinus Torvalds { 518877211f5STakashi Iwai struct snd_pcm_str *stream = &pcm->streams[direction]; 519877211f5STakashi Iwai struct snd_pcm_substream *substream; 5201da177e4SLinus Torvalds 5211da177e4SLinus Torvalds for (substream = stream->substream; substream != NULL; substream = substream->next) 5221da177e4SLinus Torvalds substream->ops = ops; 5231da177e4SLinus Torvalds } 5241da177e4SLinus Torvalds 525e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_set_ops); 5261da177e4SLinus Torvalds 5271da177e4SLinus Torvalds /** 5281da177e4SLinus Torvalds * snd_pcm_sync - set the PCM sync id 5291da177e4SLinus Torvalds * @substream: the pcm substream 5301da177e4SLinus Torvalds * 5311da177e4SLinus Torvalds * Sets the PCM sync identifier for the card. 5321da177e4SLinus Torvalds */ 533877211f5STakashi Iwai void snd_pcm_set_sync(struct snd_pcm_substream *substream) 5341da177e4SLinus Torvalds { 535877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 5361da177e4SLinus Torvalds 5371da177e4SLinus Torvalds runtime->sync.id32[0] = substream->pcm->card->number; 5381da177e4SLinus Torvalds runtime->sync.id32[1] = -1; 5391da177e4SLinus Torvalds runtime->sync.id32[2] = -1; 5401da177e4SLinus Torvalds runtime->sync.id32[3] = -1; 5411da177e4SLinus Torvalds } 5421da177e4SLinus Torvalds 543e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_set_sync); 544e88e8ae6STakashi Iwai 5451da177e4SLinus Torvalds /* 5461da177e4SLinus Torvalds * Standard ioctl routine 5471da177e4SLinus Torvalds */ 5481da177e4SLinus Torvalds 5491da177e4SLinus Torvalds static inline unsigned int div32(unsigned int a, unsigned int b, 5501da177e4SLinus Torvalds unsigned int *r) 5511da177e4SLinus Torvalds { 5521da177e4SLinus Torvalds if (b == 0) { 5531da177e4SLinus Torvalds *r = 0; 5541da177e4SLinus Torvalds return UINT_MAX; 5551da177e4SLinus Torvalds } 5561da177e4SLinus Torvalds *r = a % b; 5571da177e4SLinus Torvalds return a / b; 5581da177e4SLinus Torvalds } 5591da177e4SLinus Torvalds 5601da177e4SLinus Torvalds static inline unsigned int div_down(unsigned int a, unsigned int b) 5611da177e4SLinus Torvalds { 5621da177e4SLinus Torvalds if (b == 0) 5631da177e4SLinus Torvalds return UINT_MAX; 5641da177e4SLinus Torvalds return a / b; 5651da177e4SLinus Torvalds } 5661da177e4SLinus Torvalds 5671da177e4SLinus Torvalds static inline unsigned int div_up(unsigned int a, unsigned int b) 5681da177e4SLinus Torvalds { 5691da177e4SLinus Torvalds unsigned int r; 5701da177e4SLinus Torvalds unsigned int q; 5711da177e4SLinus Torvalds if (b == 0) 5721da177e4SLinus Torvalds return UINT_MAX; 5731da177e4SLinus Torvalds q = div32(a, b, &r); 5741da177e4SLinus Torvalds if (r) 5751da177e4SLinus Torvalds ++q; 5761da177e4SLinus Torvalds return q; 5771da177e4SLinus Torvalds } 5781da177e4SLinus Torvalds 5791da177e4SLinus Torvalds static inline unsigned int mul(unsigned int a, unsigned int b) 5801da177e4SLinus Torvalds { 5811da177e4SLinus Torvalds if (a == 0) 5821da177e4SLinus Torvalds return 0; 5831da177e4SLinus Torvalds if (div_down(UINT_MAX, a) < b) 5841da177e4SLinus Torvalds return UINT_MAX; 5851da177e4SLinus Torvalds return a * b; 5861da177e4SLinus Torvalds } 5871da177e4SLinus Torvalds 5881da177e4SLinus Torvalds static inline unsigned int muldiv32(unsigned int a, unsigned int b, 5891da177e4SLinus Torvalds unsigned int c, unsigned int *r) 5901da177e4SLinus Torvalds { 5911da177e4SLinus Torvalds u_int64_t n = (u_int64_t) a * b; 5921da177e4SLinus Torvalds if (c == 0) { 5937eaa943cSTakashi Iwai snd_BUG_ON(!n); 5941da177e4SLinus Torvalds *r = 0; 5951da177e4SLinus Torvalds return UINT_MAX; 5961da177e4SLinus Torvalds } 5973f7440a6STakashi Iwai n = div_u64_rem(n, c, r); 5981da177e4SLinus Torvalds if (n >= UINT_MAX) { 5991da177e4SLinus Torvalds *r = 0; 6001da177e4SLinus Torvalds return UINT_MAX; 6011da177e4SLinus Torvalds } 6021da177e4SLinus Torvalds return n; 6031da177e4SLinus Torvalds } 6041da177e4SLinus Torvalds 6051da177e4SLinus Torvalds /** 6061da177e4SLinus Torvalds * snd_interval_refine - refine the interval value of configurator 6071da177e4SLinus Torvalds * @i: the interval value to refine 6081da177e4SLinus Torvalds * @v: the interval value to refer to 6091da177e4SLinus Torvalds * 6101da177e4SLinus Torvalds * Refines the interval value with the reference value. 6111da177e4SLinus Torvalds * The interval is changed to the range satisfying both intervals. 6121da177e4SLinus Torvalds * The interval status (min, max, integer, etc.) are evaluated. 6131da177e4SLinus Torvalds * 6141da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 6151da177e4SLinus Torvalds */ 616877211f5STakashi Iwai int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v) 6171da177e4SLinus Torvalds { 6181da177e4SLinus Torvalds int changed = 0; 6197eaa943cSTakashi Iwai if (snd_BUG_ON(snd_interval_empty(i))) 6207eaa943cSTakashi Iwai return -EINVAL; 6211da177e4SLinus Torvalds if (i->min < v->min) { 6221da177e4SLinus Torvalds i->min = v->min; 6231da177e4SLinus Torvalds i->openmin = v->openmin; 6241da177e4SLinus Torvalds changed = 1; 6251da177e4SLinus Torvalds } else if (i->min == v->min && !i->openmin && v->openmin) { 6261da177e4SLinus Torvalds i->openmin = 1; 6271da177e4SLinus Torvalds changed = 1; 6281da177e4SLinus Torvalds } 6291da177e4SLinus Torvalds if (i->max > v->max) { 6301da177e4SLinus Torvalds i->max = v->max; 6311da177e4SLinus Torvalds i->openmax = v->openmax; 6321da177e4SLinus Torvalds changed = 1; 6331da177e4SLinus Torvalds } else if (i->max == v->max && !i->openmax && v->openmax) { 6341da177e4SLinus Torvalds i->openmax = 1; 6351da177e4SLinus Torvalds changed = 1; 6361da177e4SLinus Torvalds } 6371da177e4SLinus Torvalds if (!i->integer && v->integer) { 6381da177e4SLinus Torvalds i->integer = 1; 6391da177e4SLinus Torvalds changed = 1; 6401da177e4SLinus Torvalds } 6411da177e4SLinus Torvalds if (i->integer) { 6421da177e4SLinus Torvalds if (i->openmin) { 6431da177e4SLinus Torvalds i->min++; 6441da177e4SLinus Torvalds i->openmin = 0; 6451da177e4SLinus Torvalds } 6461da177e4SLinus Torvalds if (i->openmax) { 6471da177e4SLinus Torvalds i->max--; 6481da177e4SLinus Torvalds i->openmax = 0; 6491da177e4SLinus Torvalds } 6501da177e4SLinus Torvalds } else if (!i->openmin && !i->openmax && i->min == i->max) 6511da177e4SLinus Torvalds i->integer = 1; 6521da177e4SLinus Torvalds if (snd_interval_checkempty(i)) { 6531da177e4SLinus Torvalds snd_interval_none(i); 6541da177e4SLinus Torvalds return -EINVAL; 6551da177e4SLinus Torvalds } 6561da177e4SLinus Torvalds return changed; 6571da177e4SLinus Torvalds } 6581da177e4SLinus Torvalds 659e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_interval_refine); 660e88e8ae6STakashi Iwai 661877211f5STakashi Iwai static int snd_interval_refine_first(struct snd_interval *i) 6621da177e4SLinus Torvalds { 6637eaa943cSTakashi Iwai if (snd_BUG_ON(snd_interval_empty(i))) 6647eaa943cSTakashi Iwai return -EINVAL; 6651da177e4SLinus Torvalds if (snd_interval_single(i)) 6661da177e4SLinus Torvalds return 0; 6671da177e4SLinus Torvalds i->max = i->min; 6681da177e4SLinus Torvalds i->openmax = i->openmin; 6691da177e4SLinus Torvalds if (i->openmax) 6701da177e4SLinus Torvalds i->max++; 6711da177e4SLinus Torvalds return 1; 6721da177e4SLinus Torvalds } 6731da177e4SLinus Torvalds 674877211f5STakashi Iwai static int snd_interval_refine_last(struct snd_interval *i) 6751da177e4SLinus Torvalds { 6767eaa943cSTakashi Iwai if (snd_BUG_ON(snd_interval_empty(i))) 6777eaa943cSTakashi Iwai return -EINVAL; 6781da177e4SLinus Torvalds if (snd_interval_single(i)) 6791da177e4SLinus Torvalds return 0; 6801da177e4SLinus Torvalds i->min = i->max; 6811da177e4SLinus Torvalds i->openmin = i->openmax; 6821da177e4SLinus Torvalds if (i->openmin) 6831da177e4SLinus Torvalds i->min--; 6841da177e4SLinus Torvalds return 1; 6851da177e4SLinus Torvalds } 6861da177e4SLinus Torvalds 687877211f5STakashi Iwai void snd_interval_mul(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c) 6881da177e4SLinus Torvalds { 6891da177e4SLinus Torvalds if (a->empty || b->empty) { 6901da177e4SLinus Torvalds snd_interval_none(c); 6911da177e4SLinus Torvalds return; 6921da177e4SLinus Torvalds } 6931da177e4SLinus Torvalds c->empty = 0; 6941da177e4SLinus Torvalds c->min = mul(a->min, b->min); 6951da177e4SLinus Torvalds c->openmin = (a->openmin || b->openmin); 6961da177e4SLinus Torvalds c->max = mul(a->max, b->max); 6971da177e4SLinus Torvalds c->openmax = (a->openmax || b->openmax); 6981da177e4SLinus Torvalds c->integer = (a->integer && b->integer); 6991da177e4SLinus Torvalds } 7001da177e4SLinus Torvalds 7011da177e4SLinus Torvalds /** 7021da177e4SLinus Torvalds * snd_interval_div - refine the interval value with division 703df8db936STakashi Iwai * @a: dividend 704df8db936STakashi Iwai * @b: divisor 705df8db936STakashi Iwai * @c: quotient 7061da177e4SLinus Torvalds * 7071da177e4SLinus Torvalds * c = a / b 7081da177e4SLinus Torvalds * 7091da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 7101da177e4SLinus Torvalds */ 711877211f5STakashi Iwai void snd_interval_div(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c) 7121da177e4SLinus Torvalds { 7131da177e4SLinus Torvalds unsigned int r; 7141da177e4SLinus Torvalds if (a->empty || b->empty) { 7151da177e4SLinus Torvalds snd_interval_none(c); 7161da177e4SLinus Torvalds return; 7171da177e4SLinus Torvalds } 7181da177e4SLinus Torvalds c->empty = 0; 7191da177e4SLinus Torvalds c->min = div32(a->min, b->max, &r); 7201da177e4SLinus Torvalds c->openmin = (r || a->openmin || b->openmax); 7211da177e4SLinus Torvalds if (b->min > 0) { 7221da177e4SLinus Torvalds c->max = div32(a->max, b->min, &r); 7231da177e4SLinus Torvalds if (r) { 7241da177e4SLinus Torvalds c->max++; 7251da177e4SLinus Torvalds c->openmax = 1; 7261da177e4SLinus Torvalds } else 7271da177e4SLinus Torvalds c->openmax = (a->openmax || b->openmin); 7281da177e4SLinus Torvalds } else { 7291da177e4SLinus Torvalds c->max = UINT_MAX; 7301da177e4SLinus Torvalds c->openmax = 0; 7311da177e4SLinus Torvalds } 7321da177e4SLinus Torvalds c->integer = 0; 7331da177e4SLinus Torvalds } 7341da177e4SLinus Torvalds 7351da177e4SLinus Torvalds /** 7361da177e4SLinus Torvalds * snd_interval_muldivk - refine the interval value 737df8db936STakashi Iwai * @a: dividend 1 738df8db936STakashi Iwai * @b: dividend 2 739df8db936STakashi Iwai * @k: divisor (as integer) 740df8db936STakashi Iwai * @c: result 7411da177e4SLinus Torvalds * 7421da177e4SLinus Torvalds * c = a * b / k 7431da177e4SLinus Torvalds * 7441da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 7451da177e4SLinus Torvalds */ 746877211f5STakashi Iwai void snd_interval_muldivk(const struct snd_interval *a, const struct snd_interval *b, 747877211f5STakashi Iwai unsigned int k, struct snd_interval *c) 7481da177e4SLinus Torvalds { 7491da177e4SLinus Torvalds unsigned int r; 7501da177e4SLinus Torvalds if (a->empty || b->empty) { 7511da177e4SLinus Torvalds snd_interval_none(c); 7521da177e4SLinus Torvalds return; 7531da177e4SLinus Torvalds } 7541da177e4SLinus Torvalds c->empty = 0; 7551da177e4SLinus Torvalds c->min = muldiv32(a->min, b->min, k, &r); 7561da177e4SLinus Torvalds c->openmin = (r || a->openmin || b->openmin); 7571da177e4SLinus Torvalds c->max = muldiv32(a->max, b->max, k, &r); 7581da177e4SLinus Torvalds if (r) { 7591da177e4SLinus Torvalds c->max++; 7601da177e4SLinus Torvalds c->openmax = 1; 7611da177e4SLinus Torvalds } else 7621da177e4SLinus Torvalds c->openmax = (a->openmax || b->openmax); 7631da177e4SLinus Torvalds c->integer = 0; 7641da177e4SLinus Torvalds } 7651da177e4SLinus Torvalds 7661da177e4SLinus Torvalds /** 7671da177e4SLinus Torvalds * snd_interval_mulkdiv - refine the interval value 768df8db936STakashi Iwai * @a: dividend 1 769df8db936STakashi Iwai * @k: dividend 2 (as integer) 770df8db936STakashi Iwai * @b: divisor 771df8db936STakashi Iwai * @c: result 7721da177e4SLinus Torvalds * 7731da177e4SLinus Torvalds * c = a * k / b 7741da177e4SLinus Torvalds * 7751da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 7761da177e4SLinus Torvalds */ 777877211f5STakashi Iwai void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k, 778877211f5STakashi Iwai const struct snd_interval *b, struct snd_interval *c) 7791da177e4SLinus Torvalds { 7801da177e4SLinus Torvalds unsigned int r; 7811da177e4SLinus Torvalds if (a->empty || b->empty) { 7821da177e4SLinus Torvalds snd_interval_none(c); 7831da177e4SLinus Torvalds return; 7841da177e4SLinus Torvalds } 7851da177e4SLinus Torvalds c->empty = 0; 7861da177e4SLinus Torvalds c->min = muldiv32(a->min, k, b->max, &r); 7871da177e4SLinus Torvalds c->openmin = (r || a->openmin || b->openmax); 7881da177e4SLinus Torvalds if (b->min > 0) { 7891da177e4SLinus Torvalds c->max = muldiv32(a->max, k, b->min, &r); 7901da177e4SLinus Torvalds if (r) { 7911da177e4SLinus Torvalds c->max++; 7921da177e4SLinus Torvalds c->openmax = 1; 7931da177e4SLinus Torvalds } else 7941da177e4SLinus Torvalds c->openmax = (a->openmax || b->openmin); 7951da177e4SLinus Torvalds } else { 7961da177e4SLinus Torvalds c->max = UINT_MAX; 7971da177e4SLinus Torvalds c->openmax = 0; 7981da177e4SLinus Torvalds } 7991da177e4SLinus Torvalds c->integer = 0; 8001da177e4SLinus Torvalds } 8011da177e4SLinus Torvalds 8021da177e4SLinus Torvalds /* ---- */ 8031da177e4SLinus Torvalds 8041da177e4SLinus Torvalds 8051da177e4SLinus Torvalds /** 8061da177e4SLinus Torvalds * snd_interval_ratnum - refine the interval value 807df8db936STakashi Iwai * @i: interval to refine 808df8db936STakashi Iwai * @rats_count: number of ratnum_t 809df8db936STakashi Iwai * @rats: ratnum_t array 810df8db936STakashi Iwai * @nump: pointer to store the resultant numerator 811df8db936STakashi Iwai * @denp: pointer to store the resultant denominator 8121da177e4SLinus Torvalds * 8131da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 8141da177e4SLinus Torvalds */ 815877211f5STakashi Iwai int snd_interval_ratnum(struct snd_interval *i, 816877211f5STakashi Iwai unsigned int rats_count, struct snd_ratnum *rats, 8171da177e4SLinus Torvalds unsigned int *nump, unsigned int *denp) 8181da177e4SLinus Torvalds { 8198374e24cSKrzysztof Helt unsigned int best_num, best_den; 8208374e24cSKrzysztof Helt int best_diff; 8211da177e4SLinus Torvalds unsigned int k; 822877211f5STakashi Iwai struct snd_interval t; 8231da177e4SLinus Torvalds int err; 8248374e24cSKrzysztof Helt unsigned int result_num, result_den; 8258374e24cSKrzysztof Helt int result_diff; 8261da177e4SLinus Torvalds 8271da177e4SLinus Torvalds best_num = best_den = best_diff = 0; 8281da177e4SLinus Torvalds for (k = 0; k < rats_count; ++k) { 8291da177e4SLinus Torvalds unsigned int num = rats[k].num; 8301da177e4SLinus Torvalds unsigned int den; 8311da177e4SLinus Torvalds unsigned int q = i->min; 8321da177e4SLinus Torvalds int diff; 8331da177e4SLinus Torvalds if (q == 0) 8341da177e4SLinus Torvalds q = 1; 83540962d7cSKrzysztof Helt den = div_up(num, q); 8361da177e4SLinus Torvalds if (den < rats[k].den_min) 8371da177e4SLinus Torvalds continue; 8381da177e4SLinus Torvalds if (den > rats[k].den_max) 8391da177e4SLinus Torvalds den = rats[k].den_max; 8401da177e4SLinus Torvalds else { 8411da177e4SLinus Torvalds unsigned int r; 8421da177e4SLinus Torvalds r = (den - rats[k].den_min) % rats[k].den_step; 8431da177e4SLinus Torvalds if (r != 0) 8441da177e4SLinus Torvalds den -= r; 8451da177e4SLinus Torvalds } 8461da177e4SLinus Torvalds diff = num - q * den; 8478374e24cSKrzysztof Helt if (diff < 0) 8488374e24cSKrzysztof Helt diff = -diff; 8491da177e4SLinus Torvalds if (best_num == 0 || 8501da177e4SLinus Torvalds diff * best_den < best_diff * den) { 8511da177e4SLinus Torvalds best_diff = diff; 8521da177e4SLinus Torvalds best_den = den; 8531da177e4SLinus Torvalds best_num = num; 8541da177e4SLinus Torvalds } 8551da177e4SLinus Torvalds } 8561da177e4SLinus Torvalds if (best_den == 0) { 8571da177e4SLinus Torvalds i->empty = 1; 8581da177e4SLinus Torvalds return -EINVAL; 8591da177e4SLinus Torvalds } 8601da177e4SLinus Torvalds t.min = div_down(best_num, best_den); 8611da177e4SLinus Torvalds t.openmin = !!(best_num % best_den); 8621da177e4SLinus Torvalds 8638374e24cSKrzysztof Helt result_num = best_num; 8648374e24cSKrzysztof Helt result_diff = best_diff; 8658374e24cSKrzysztof Helt result_den = best_den; 8661da177e4SLinus Torvalds best_num = best_den = best_diff = 0; 8671da177e4SLinus Torvalds for (k = 0; k < rats_count; ++k) { 8681da177e4SLinus Torvalds unsigned int num = rats[k].num; 8691da177e4SLinus Torvalds unsigned int den; 8701da177e4SLinus Torvalds unsigned int q = i->max; 8711da177e4SLinus Torvalds int diff; 8721da177e4SLinus Torvalds if (q == 0) { 8731da177e4SLinus Torvalds i->empty = 1; 8741da177e4SLinus Torvalds return -EINVAL; 8751da177e4SLinus Torvalds } 87640962d7cSKrzysztof Helt den = div_down(num, q); 8771da177e4SLinus Torvalds if (den > rats[k].den_max) 8781da177e4SLinus Torvalds continue; 8791da177e4SLinus Torvalds if (den < rats[k].den_min) 8801da177e4SLinus Torvalds den = rats[k].den_min; 8811da177e4SLinus Torvalds else { 8821da177e4SLinus Torvalds unsigned int r; 8831da177e4SLinus Torvalds r = (den - rats[k].den_min) % rats[k].den_step; 8841da177e4SLinus Torvalds if (r != 0) 8851da177e4SLinus Torvalds den += rats[k].den_step - r; 8861da177e4SLinus Torvalds } 8871da177e4SLinus Torvalds diff = q * den - num; 8888374e24cSKrzysztof Helt if (diff < 0) 8898374e24cSKrzysztof Helt diff = -diff; 8901da177e4SLinus Torvalds if (best_num == 0 || 8911da177e4SLinus Torvalds diff * best_den < best_diff * den) { 8921da177e4SLinus Torvalds best_diff = diff; 8931da177e4SLinus Torvalds best_den = den; 8941da177e4SLinus Torvalds best_num = num; 8951da177e4SLinus Torvalds } 8961da177e4SLinus Torvalds } 8971da177e4SLinus Torvalds if (best_den == 0) { 8981da177e4SLinus Torvalds i->empty = 1; 8991da177e4SLinus Torvalds return -EINVAL; 9001da177e4SLinus Torvalds } 9011da177e4SLinus Torvalds t.max = div_up(best_num, best_den); 9021da177e4SLinus Torvalds t.openmax = !!(best_num % best_den); 9031da177e4SLinus Torvalds t.integer = 0; 9041da177e4SLinus Torvalds err = snd_interval_refine(i, &t); 9051da177e4SLinus Torvalds if (err < 0) 9061da177e4SLinus Torvalds return err; 9071da177e4SLinus Torvalds 9081da177e4SLinus Torvalds if (snd_interval_single(i)) { 9098374e24cSKrzysztof Helt if (best_diff * result_den < result_diff * best_den) { 9108374e24cSKrzysztof Helt result_num = best_num; 9118374e24cSKrzysztof Helt result_den = best_den; 9128374e24cSKrzysztof Helt } 9131da177e4SLinus Torvalds if (nump) 9148374e24cSKrzysztof Helt *nump = result_num; 9151da177e4SLinus Torvalds if (denp) 9168374e24cSKrzysztof Helt *denp = result_den; 9171da177e4SLinus Torvalds } 9181da177e4SLinus Torvalds return err; 9191da177e4SLinus Torvalds } 9201da177e4SLinus Torvalds 921e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_interval_ratnum); 922e88e8ae6STakashi Iwai 9231da177e4SLinus Torvalds /** 9241da177e4SLinus Torvalds * snd_interval_ratden - refine the interval value 925df8db936STakashi Iwai * @i: interval to refine 926877211f5STakashi Iwai * @rats_count: number of struct ratden 927877211f5STakashi Iwai * @rats: struct ratden array 928df8db936STakashi Iwai * @nump: pointer to store the resultant numerator 929df8db936STakashi Iwai * @denp: pointer to store the resultant denominator 9301da177e4SLinus Torvalds * 9311da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 9321da177e4SLinus Torvalds */ 933877211f5STakashi Iwai static int snd_interval_ratden(struct snd_interval *i, 934877211f5STakashi Iwai unsigned int rats_count, struct snd_ratden *rats, 9351da177e4SLinus Torvalds unsigned int *nump, unsigned int *denp) 9361da177e4SLinus Torvalds { 9371da177e4SLinus Torvalds unsigned int best_num, best_diff, best_den; 9381da177e4SLinus Torvalds unsigned int k; 939877211f5STakashi Iwai struct snd_interval t; 9401da177e4SLinus Torvalds int err; 9411da177e4SLinus Torvalds 9421da177e4SLinus Torvalds best_num = best_den = best_diff = 0; 9431da177e4SLinus Torvalds for (k = 0; k < rats_count; ++k) { 9441da177e4SLinus Torvalds unsigned int num; 9451da177e4SLinus Torvalds unsigned int den = rats[k].den; 9461da177e4SLinus Torvalds unsigned int q = i->min; 9471da177e4SLinus Torvalds int diff; 9481da177e4SLinus Torvalds num = mul(q, den); 9491da177e4SLinus Torvalds if (num > rats[k].num_max) 9501da177e4SLinus Torvalds continue; 9511da177e4SLinus Torvalds if (num < rats[k].num_min) 9521da177e4SLinus Torvalds num = rats[k].num_max; 9531da177e4SLinus Torvalds else { 9541da177e4SLinus Torvalds unsigned int r; 9551da177e4SLinus Torvalds r = (num - rats[k].num_min) % rats[k].num_step; 9561da177e4SLinus Torvalds if (r != 0) 9571da177e4SLinus Torvalds num += rats[k].num_step - r; 9581da177e4SLinus Torvalds } 9591da177e4SLinus Torvalds diff = num - q * den; 9601da177e4SLinus Torvalds if (best_num == 0 || 9611da177e4SLinus Torvalds diff * best_den < best_diff * den) { 9621da177e4SLinus Torvalds best_diff = diff; 9631da177e4SLinus Torvalds best_den = den; 9641da177e4SLinus Torvalds best_num = num; 9651da177e4SLinus Torvalds } 9661da177e4SLinus Torvalds } 9671da177e4SLinus Torvalds if (best_den == 0) { 9681da177e4SLinus Torvalds i->empty = 1; 9691da177e4SLinus Torvalds return -EINVAL; 9701da177e4SLinus Torvalds } 9711da177e4SLinus Torvalds t.min = div_down(best_num, best_den); 9721da177e4SLinus Torvalds t.openmin = !!(best_num % best_den); 9731da177e4SLinus Torvalds 9741da177e4SLinus Torvalds best_num = best_den = best_diff = 0; 9751da177e4SLinus Torvalds for (k = 0; k < rats_count; ++k) { 9761da177e4SLinus Torvalds unsigned int num; 9771da177e4SLinus Torvalds unsigned int den = rats[k].den; 9781da177e4SLinus Torvalds unsigned int q = i->max; 9791da177e4SLinus Torvalds int diff; 9801da177e4SLinus Torvalds num = mul(q, den); 9811da177e4SLinus Torvalds if (num < rats[k].num_min) 9821da177e4SLinus Torvalds continue; 9831da177e4SLinus Torvalds if (num > rats[k].num_max) 9841da177e4SLinus Torvalds num = rats[k].num_max; 9851da177e4SLinus Torvalds else { 9861da177e4SLinus Torvalds unsigned int r; 9871da177e4SLinus Torvalds r = (num - rats[k].num_min) % rats[k].num_step; 9881da177e4SLinus Torvalds if (r != 0) 9891da177e4SLinus Torvalds num -= r; 9901da177e4SLinus Torvalds } 9911da177e4SLinus Torvalds diff = q * den - num; 9921da177e4SLinus Torvalds if (best_num == 0 || 9931da177e4SLinus Torvalds diff * best_den < best_diff * den) { 9941da177e4SLinus Torvalds best_diff = diff; 9951da177e4SLinus Torvalds best_den = den; 9961da177e4SLinus Torvalds best_num = num; 9971da177e4SLinus Torvalds } 9981da177e4SLinus Torvalds } 9991da177e4SLinus Torvalds if (best_den == 0) { 10001da177e4SLinus Torvalds i->empty = 1; 10011da177e4SLinus Torvalds return -EINVAL; 10021da177e4SLinus Torvalds } 10031da177e4SLinus Torvalds t.max = div_up(best_num, best_den); 10041da177e4SLinus Torvalds t.openmax = !!(best_num % best_den); 10051da177e4SLinus Torvalds t.integer = 0; 10061da177e4SLinus Torvalds err = snd_interval_refine(i, &t); 10071da177e4SLinus Torvalds if (err < 0) 10081da177e4SLinus Torvalds return err; 10091da177e4SLinus Torvalds 10101da177e4SLinus Torvalds if (snd_interval_single(i)) { 10111da177e4SLinus Torvalds if (nump) 10121da177e4SLinus Torvalds *nump = best_num; 10131da177e4SLinus Torvalds if (denp) 10141da177e4SLinus Torvalds *denp = best_den; 10151da177e4SLinus Torvalds } 10161da177e4SLinus Torvalds return err; 10171da177e4SLinus Torvalds } 10181da177e4SLinus Torvalds 10191da177e4SLinus Torvalds /** 10201da177e4SLinus Torvalds * snd_interval_list - refine the interval value from the list 10211da177e4SLinus Torvalds * @i: the interval value to refine 10221da177e4SLinus Torvalds * @count: the number of elements in the list 10231da177e4SLinus Torvalds * @list: the value list 10241da177e4SLinus Torvalds * @mask: the bit-mask to evaluate 10251da177e4SLinus Torvalds * 10261da177e4SLinus Torvalds * Refines the interval value from the list. 10271da177e4SLinus Torvalds * When mask is non-zero, only the elements corresponding to bit 1 are 10281da177e4SLinus Torvalds * evaluated. 10291da177e4SLinus Torvalds * 10301da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 10311da177e4SLinus Torvalds */ 1032877211f5STakashi Iwai int snd_interval_list(struct snd_interval *i, unsigned int count, unsigned int *list, unsigned int mask) 10331da177e4SLinus Torvalds { 10341da177e4SLinus Torvalds unsigned int k; 1035b1ddaf68SClemens Ladisch struct snd_interval list_range; 10360981a260STakashi Iwai 10370981a260STakashi Iwai if (!count) { 10380981a260STakashi Iwai i->empty = 1; 10390981a260STakashi Iwai return -EINVAL; 10400981a260STakashi Iwai } 1041b1ddaf68SClemens Ladisch snd_interval_any(&list_range); 1042b1ddaf68SClemens Ladisch list_range.min = UINT_MAX; 1043b1ddaf68SClemens Ladisch list_range.max = 0; 10441da177e4SLinus Torvalds for (k = 0; k < count; k++) { 10451da177e4SLinus Torvalds if (mask && !(mask & (1 << k))) 10461da177e4SLinus Torvalds continue; 1047b1ddaf68SClemens Ladisch if (!snd_interval_test(i, list[k])) 10481da177e4SLinus Torvalds continue; 1049b1ddaf68SClemens Ladisch list_range.min = min(list_range.min, list[k]); 1050b1ddaf68SClemens Ladisch list_range.max = max(list_range.max, list[k]); 10511da177e4SLinus Torvalds } 1052b1ddaf68SClemens Ladisch return snd_interval_refine(i, &list_range); 10531da177e4SLinus Torvalds } 10541da177e4SLinus Torvalds 1055e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_interval_list); 1056e88e8ae6STakashi Iwai 1057877211f5STakashi Iwai static int snd_interval_step(struct snd_interval *i, unsigned int min, unsigned int step) 10581da177e4SLinus Torvalds { 10591da177e4SLinus Torvalds unsigned int n; 10601da177e4SLinus Torvalds int changed = 0; 10611da177e4SLinus Torvalds n = (i->min - min) % step; 10621da177e4SLinus Torvalds if (n != 0 || i->openmin) { 10631da177e4SLinus Torvalds i->min += step - n; 10641da177e4SLinus Torvalds changed = 1; 10651da177e4SLinus Torvalds } 10661da177e4SLinus Torvalds n = (i->max - min) % step; 10671da177e4SLinus Torvalds if (n != 0 || i->openmax) { 10681da177e4SLinus Torvalds i->max -= n; 10691da177e4SLinus Torvalds changed = 1; 10701da177e4SLinus Torvalds } 10711da177e4SLinus Torvalds if (snd_interval_checkempty(i)) { 10721da177e4SLinus Torvalds i->empty = 1; 10731da177e4SLinus Torvalds return -EINVAL; 10741da177e4SLinus Torvalds } 10751da177e4SLinus Torvalds return changed; 10761da177e4SLinus Torvalds } 10771da177e4SLinus Torvalds 10781da177e4SLinus Torvalds /* Info constraints helpers */ 10791da177e4SLinus Torvalds 10801da177e4SLinus Torvalds /** 10811da177e4SLinus Torvalds * snd_pcm_hw_rule_add - add the hw-constraint rule 10821da177e4SLinus Torvalds * @runtime: the pcm runtime instance 10831da177e4SLinus Torvalds * @cond: condition bits 10841da177e4SLinus Torvalds * @var: the variable to evaluate 10851da177e4SLinus Torvalds * @func: the evaluation function 10861da177e4SLinus Torvalds * @private: the private data pointer passed to function 10871da177e4SLinus Torvalds * @dep: the dependent variables 10881da177e4SLinus Torvalds * 10891da177e4SLinus Torvalds * Returns zero if successful, or a negative error code on failure. 10901da177e4SLinus Torvalds */ 1091877211f5STakashi Iwai int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond, 10921da177e4SLinus Torvalds int var, 10931da177e4SLinus Torvalds snd_pcm_hw_rule_func_t func, void *private, 10941da177e4SLinus Torvalds int dep, ...) 10951da177e4SLinus Torvalds { 1096877211f5STakashi Iwai struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; 1097877211f5STakashi Iwai struct snd_pcm_hw_rule *c; 10981da177e4SLinus Torvalds unsigned int k; 10991da177e4SLinus Torvalds va_list args; 11001da177e4SLinus Torvalds va_start(args, dep); 11011da177e4SLinus Torvalds if (constrs->rules_num >= constrs->rules_all) { 1102877211f5STakashi Iwai struct snd_pcm_hw_rule *new; 11031da177e4SLinus Torvalds unsigned int new_rules = constrs->rules_all + 16; 11041da177e4SLinus Torvalds new = kcalloc(new_rules, sizeof(*c), GFP_KERNEL); 110587a1c8aaSJesper Juhl if (!new) { 110687a1c8aaSJesper Juhl va_end(args); 11071da177e4SLinus Torvalds return -ENOMEM; 110887a1c8aaSJesper Juhl } 11091da177e4SLinus Torvalds if (constrs->rules) { 11101da177e4SLinus Torvalds memcpy(new, constrs->rules, 11111da177e4SLinus Torvalds constrs->rules_num * sizeof(*c)); 11121da177e4SLinus Torvalds kfree(constrs->rules); 11131da177e4SLinus Torvalds } 11141da177e4SLinus Torvalds constrs->rules = new; 11151da177e4SLinus Torvalds constrs->rules_all = new_rules; 11161da177e4SLinus Torvalds } 11171da177e4SLinus Torvalds c = &constrs->rules[constrs->rules_num]; 11181da177e4SLinus Torvalds c->cond = cond; 11191da177e4SLinus Torvalds c->func = func; 11201da177e4SLinus Torvalds c->var = var; 11211da177e4SLinus Torvalds c->private = private; 11221da177e4SLinus Torvalds k = 0; 11231da177e4SLinus Torvalds while (1) { 112487a1c8aaSJesper Juhl if (snd_BUG_ON(k >= ARRAY_SIZE(c->deps))) { 112587a1c8aaSJesper Juhl va_end(args); 11267eaa943cSTakashi Iwai return -EINVAL; 112787a1c8aaSJesper Juhl } 11281da177e4SLinus Torvalds c->deps[k++] = dep; 11291da177e4SLinus Torvalds if (dep < 0) 11301da177e4SLinus Torvalds break; 11311da177e4SLinus Torvalds dep = va_arg(args, int); 11321da177e4SLinus Torvalds } 11331da177e4SLinus Torvalds constrs->rules_num++; 11341da177e4SLinus Torvalds va_end(args); 11351da177e4SLinus Torvalds return 0; 11361da177e4SLinus Torvalds } 11371da177e4SLinus Torvalds 1138e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_rule_add); 1139e88e8ae6STakashi Iwai 11401da177e4SLinus Torvalds /** 11411c85cc64SRandy Dunlap * snd_pcm_hw_constraint_mask - apply the given bitmap mask constraint 1142df8db936STakashi Iwai * @runtime: PCM runtime instance 1143df8db936STakashi Iwai * @var: hw_params variable to apply the mask 1144df8db936STakashi Iwai * @mask: the bitmap mask 1145df8db936STakashi Iwai * 11461c85cc64SRandy Dunlap * Apply the constraint of the given bitmap mask to a 32-bit mask parameter. 11471da177e4SLinus Torvalds */ 1148877211f5STakashi Iwai int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, 11491da177e4SLinus Torvalds u_int32_t mask) 11501da177e4SLinus Torvalds { 1151877211f5STakashi Iwai struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; 1152877211f5STakashi Iwai struct snd_mask *maskp = constrs_mask(constrs, var); 11531da177e4SLinus Torvalds *maskp->bits &= mask; 11541da177e4SLinus Torvalds memset(maskp->bits + 1, 0, (SNDRV_MASK_MAX-32) / 8); /* clear rest */ 11551da177e4SLinus Torvalds if (*maskp->bits == 0) 11561da177e4SLinus Torvalds return -EINVAL; 11571da177e4SLinus Torvalds return 0; 11581da177e4SLinus Torvalds } 11591da177e4SLinus Torvalds 11601da177e4SLinus Torvalds /** 11611c85cc64SRandy Dunlap * snd_pcm_hw_constraint_mask64 - apply the given bitmap mask constraint 1162df8db936STakashi Iwai * @runtime: PCM runtime instance 1163df8db936STakashi Iwai * @var: hw_params variable to apply the mask 1164df8db936STakashi Iwai * @mask: the 64bit bitmap mask 1165df8db936STakashi Iwai * 11661c85cc64SRandy Dunlap * Apply the constraint of the given bitmap mask to a 64-bit mask parameter. 11671da177e4SLinus Torvalds */ 1168877211f5STakashi Iwai int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, 11691da177e4SLinus Torvalds u_int64_t mask) 11701da177e4SLinus Torvalds { 1171877211f5STakashi Iwai struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; 1172877211f5STakashi Iwai struct snd_mask *maskp = constrs_mask(constrs, var); 11731da177e4SLinus Torvalds maskp->bits[0] &= (u_int32_t)mask; 11741da177e4SLinus Torvalds maskp->bits[1] &= (u_int32_t)(mask >> 32); 11751da177e4SLinus Torvalds memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */ 11761da177e4SLinus Torvalds if (! maskp->bits[0] && ! maskp->bits[1]) 11771da177e4SLinus Torvalds return -EINVAL; 11781da177e4SLinus Torvalds return 0; 11791da177e4SLinus Torvalds } 11801da177e4SLinus Torvalds 11811da177e4SLinus Torvalds /** 11821c85cc64SRandy Dunlap * snd_pcm_hw_constraint_integer - apply an integer constraint to an interval 1183df8db936STakashi Iwai * @runtime: PCM runtime instance 1184df8db936STakashi Iwai * @var: hw_params variable to apply the integer constraint 1185df8db936STakashi Iwai * 1186df8db936STakashi Iwai * Apply the constraint of integer to an interval parameter. 11871da177e4SLinus Torvalds */ 1188877211f5STakashi Iwai int snd_pcm_hw_constraint_integer(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var) 11891da177e4SLinus Torvalds { 1190877211f5STakashi Iwai struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; 11911da177e4SLinus Torvalds return snd_interval_setinteger(constrs_interval(constrs, var)); 11921da177e4SLinus Torvalds } 11931da177e4SLinus Torvalds 1194e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_integer); 1195e88e8ae6STakashi Iwai 11961da177e4SLinus Torvalds /** 11971c85cc64SRandy Dunlap * snd_pcm_hw_constraint_minmax - apply a min/max range constraint to an interval 1198df8db936STakashi Iwai * @runtime: PCM runtime instance 1199df8db936STakashi Iwai * @var: hw_params variable to apply the range 1200df8db936STakashi Iwai * @min: the minimal value 1201df8db936STakashi Iwai * @max: the maximal value 1202df8db936STakashi Iwai * 1203df8db936STakashi Iwai * Apply the min/max range constraint to an interval parameter. 12041da177e4SLinus Torvalds */ 1205877211f5STakashi Iwai int snd_pcm_hw_constraint_minmax(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, 12061da177e4SLinus Torvalds unsigned int min, unsigned int max) 12071da177e4SLinus Torvalds { 1208877211f5STakashi Iwai struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; 1209877211f5STakashi Iwai struct snd_interval t; 12101da177e4SLinus Torvalds t.min = min; 12111da177e4SLinus Torvalds t.max = max; 12121da177e4SLinus Torvalds t.openmin = t.openmax = 0; 12131da177e4SLinus Torvalds t.integer = 0; 12141da177e4SLinus Torvalds return snd_interval_refine(constrs_interval(constrs, var), &t); 12151da177e4SLinus Torvalds } 12161da177e4SLinus Torvalds 1217e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_minmax); 1218e88e8ae6STakashi Iwai 1219877211f5STakashi Iwai static int snd_pcm_hw_rule_list(struct snd_pcm_hw_params *params, 1220877211f5STakashi Iwai struct snd_pcm_hw_rule *rule) 12211da177e4SLinus Torvalds { 1222877211f5STakashi Iwai struct snd_pcm_hw_constraint_list *list = rule->private; 12231da177e4SLinus Torvalds return snd_interval_list(hw_param_interval(params, rule->var), list->count, list->list, list->mask); 12241da177e4SLinus Torvalds } 12251da177e4SLinus Torvalds 12261da177e4SLinus Torvalds 12271da177e4SLinus Torvalds /** 12281c85cc64SRandy Dunlap * snd_pcm_hw_constraint_list - apply a list of constraints to a parameter 1229df8db936STakashi Iwai * @runtime: PCM runtime instance 1230df8db936STakashi Iwai * @cond: condition bits 1231df8db936STakashi Iwai * @var: hw_params variable to apply the list constraint 1232df8db936STakashi Iwai * @l: list 1233df8db936STakashi Iwai * 1234df8db936STakashi Iwai * Apply the list of constraints to an interval parameter. 12351da177e4SLinus Torvalds */ 1236877211f5STakashi Iwai int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime, 12371da177e4SLinus Torvalds unsigned int cond, 12381da177e4SLinus Torvalds snd_pcm_hw_param_t var, 1239877211f5STakashi Iwai struct snd_pcm_hw_constraint_list *l) 12401da177e4SLinus Torvalds { 12411da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 12421da177e4SLinus Torvalds snd_pcm_hw_rule_list, l, 12431da177e4SLinus Torvalds var, -1); 12441da177e4SLinus Torvalds } 12451da177e4SLinus Torvalds 1246e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_list); 1247e88e8ae6STakashi Iwai 1248877211f5STakashi Iwai static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params, 1249877211f5STakashi Iwai struct snd_pcm_hw_rule *rule) 12501da177e4SLinus Torvalds { 1251877211f5STakashi Iwai struct snd_pcm_hw_constraint_ratnums *r = rule->private; 12521da177e4SLinus Torvalds unsigned int num = 0, den = 0; 12531da177e4SLinus Torvalds int err; 12541da177e4SLinus Torvalds err = snd_interval_ratnum(hw_param_interval(params, rule->var), 12551da177e4SLinus Torvalds r->nrats, r->rats, &num, &den); 12561da177e4SLinus Torvalds if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { 12571da177e4SLinus Torvalds params->rate_num = num; 12581da177e4SLinus Torvalds params->rate_den = den; 12591da177e4SLinus Torvalds } 12601da177e4SLinus Torvalds return err; 12611da177e4SLinus Torvalds } 12621da177e4SLinus Torvalds 12631da177e4SLinus Torvalds /** 12641c85cc64SRandy Dunlap * snd_pcm_hw_constraint_ratnums - apply ratnums constraint to a parameter 1265df8db936STakashi Iwai * @runtime: PCM runtime instance 1266df8db936STakashi Iwai * @cond: condition bits 1267df8db936STakashi Iwai * @var: hw_params variable to apply the ratnums constraint 1268877211f5STakashi Iwai * @r: struct snd_ratnums constriants 12691da177e4SLinus Torvalds */ 1270877211f5STakashi Iwai int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime, 12711da177e4SLinus Torvalds unsigned int cond, 12721da177e4SLinus Torvalds snd_pcm_hw_param_t var, 1273877211f5STakashi Iwai struct snd_pcm_hw_constraint_ratnums *r) 12741da177e4SLinus Torvalds { 12751da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 12761da177e4SLinus Torvalds snd_pcm_hw_rule_ratnums, r, 12771da177e4SLinus Torvalds var, -1); 12781da177e4SLinus Torvalds } 12791da177e4SLinus Torvalds 1280e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums); 1281e88e8ae6STakashi Iwai 1282877211f5STakashi Iwai static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params, 1283877211f5STakashi Iwai struct snd_pcm_hw_rule *rule) 12841da177e4SLinus Torvalds { 1285877211f5STakashi Iwai struct snd_pcm_hw_constraint_ratdens *r = rule->private; 12861da177e4SLinus Torvalds unsigned int num = 0, den = 0; 12871da177e4SLinus Torvalds int err = snd_interval_ratden(hw_param_interval(params, rule->var), 12881da177e4SLinus Torvalds r->nrats, r->rats, &num, &den); 12891da177e4SLinus Torvalds if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { 12901da177e4SLinus Torvalds params->rate_num = num; 12911da177e4SLinus Torvalds params->rate_den = den; 12921da177e4SLinus Torvalds } 12931da177e4SLinus Torvalds return err; 12941da177e4SLinus Torvalds } 12951da177e4SLinus Torvalds 12961da177e4SLinus Torvalds /** 12971c85cc64SRandy Dunlap * snd_pcm_hw_constraint_ratdens - apply ratdens constraint to a parameter 1298df8db936STakashi Iwai * @runtime: PCM runtime instance 1299df8db936STakashi Iwai * @cond: condition bits 1300df8db936STakashi Iwai * @var: hw_params variable to apply the ratdens constraint 1301877211f5STakashi Iwai * @r: struct snd_ratdens constriants 13021da177e4SLinus Torvalds */ 1303877211f5STakashi Iwai int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime, 13041da177e4SLinus Torvalds unsigned int cond, 13051da177e4SLinus Torvalds snd_pcm_hw_param_t var, 1306877211f5STakashi Iwai struct snd_pcm_hw_constraint_ratdens *r) 13071da177e4SLinus Torvalds { 13081da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 13091da177e4SLinus Torvalds snd_pcm_hw_rule_ratdens, r, 13101da177e4SLinus Torvalds var, -1); 13111da177e4SLinus Torvalds } 13121da177e4SLinus Torvalds 1313e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_ratdens); 1314e88e8ae6STakashi Iwai 1315877211f5STakashi Iwai static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params, 1316877211f5STakashi Iwai struct snd_pcm_hw_rule *rule) 13171da177e4SLinus Torvalds { 13181da177e4SLinus Torvalds unsigned int l = (unsigned long) rule->private; 13191da177e4SLinus Torvalds int width = l & 0xffff; 13201da177e4SLinus Torvalds unsigned int msbits = l >> 16; 1321877211f5STakashi Iwai struct snd_interval *i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); 13221da177e4SLinus Torvalds if (snd_interval_single(i) && snd_interval_value(i) == width) 13231da177e4SLinus Torvalds params->msbits = msbits; 13241da177e4SLinus Torvalds return 0; 13251da177e4SLinus Torvalds } 13261da177e4SLinus Torvalds 13271da177e4SLinus Torvalds /** 13281c85cc64SRandy Dunlap * snd_pcm_hw_constraint_msbits - add a hw constraint msbits rule 1329df8db936STakashi Iwai * @runtime: PCM runtime instance 1330df8db936STakashi Iwai * @cond: condition bits 1331df8db936STakashi Iwai * @width: sample bits width 1332df8db936STakashi Iwai * @msbits: msbits width 13331da177e4SLinus Torvalds */ 1334877211f5STakashi Iwai int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime, 13351da177e4SLinus Torvalds unsigned int cond, 13361da177e4SLinus Torvalds unsigned int width, 13371da177e4SLinus Torvalds unsigned int msbits) 13381da177e4SLinus Torvalds { 13391da177e4SLinus Torvalds unsigned long l = (msbits << 16) | width; 13401da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, -1, 13411da177e4SLinus Torvalds snd_pcm_hw_rule_msbits, 13421da177e4SLinus Torvalds (void*) l, 13431da177e4SLinus Torvalds SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); 13441da177e4SLinus Torvalds } 13451da177e4SLinus Torvalds 1346e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_msbits); 1347e88e8ae6STakashi Iwai 1348877211f5STakashi Iwai static int snd_pcm_hw_rule_step(struct snd_pcm_hw_params *params, 1349877211f5STakashi Iwai struct snd_pcm_hw_rule *rule) 13501da177e4SLinus Torvalds { 13511da177e4SLinus Torvalds unsigned long step = (unsigned long) rule->private; 13521da177e4SLinus Torvalds return snd_interval_step(hw_param_interval(params, rule->var), 0, step); 13531da177e4SLinus Torvalds } 13541da177e4SLinus Torvalds 13551da177e4SLinus Torvalds /** 13561c85cc64SRandy Dunlap * snd_pcm_hw_constraint_step - add a hw constraint step rule 1357df8db936STakashi Iwai * @runtime: PCM runtime instance 1358df8db936STakashi Iwai * @cond: condition bits 1359df8db936STakashi Iwai * @var: hw_params variable to apply the step constraint 1360df8db936STakashi Iwai * @step: step size 13611da177e4SLinus Torvalds */ 1362877211f5STakashi Iwai int snd_pcm_hw_constraint_step(struct snd_pcm_runtime *runtime, 13631da177e4SLinus Torvalds unsigned int cond, 13641da177e4SLinus Torvalds snd_pcm_hw_param_t var, 13651da177e4SLinus Torvalds unsigned long step) 13661da177e4SLinus Torvalds { 13671da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 13681da177e4SLinus Torvalds snd_pcm_hw_rule_step, (void *) step, 13691da177e4SLinus Torvalds var, -1); 13701da177e4SLinus Torvalds } 13711da177e4SLinus Torvalds 1372e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_step); 1373e88e8ae6STakashi Iwai 1374877211f5STakashi Iwai static int snd_pcm_hw_rule_pow2(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) 13751da177e4SLinus Torvalds { 137667c39317SMarcin Ślusarz static unsigned int pow2_sizes[] = { 13771da177e4SLinus Torvalds 1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7, 13781da177e4SLinus Torvalds 1<<8, 1<<9, 1<<10, 1<<11, 1<<12, 1<<13, 1<<14, 1<<15, 13791da177e4SLinus Torvalds 1<<16, 1<<17, 1<<18, 1<<19, 1<<20, 1<<21, 1<<22, 1<<23, 13801da177e4SLinus Torvalds 1<<24, 1<<25, 1<<26, 1<<27, 1<<28, 1<<29, 1<<30 13811da177e4SLinus Torvalds }; 13821da177e4SLinus Torvalds return snd_interval_list(hw_param_interval(params, rule->var), 13831da177e4SLinus Torvalds ARRAY_SIZE(pow2_sizes), pow2_sizes, 0); 13841da177e4SLinus Torvalds } 13851da177e4SLinus Torvalds 13861da177e4SLinus Torvalds /** 13871c85cc64SRandy Dunlap * snd_pcm_hw_constraint_pow2 - add a hw constraint power-of-2 rule 1388df8db936STakashi Iwai * @runtime: PCM runtime instance 1389df8db936STakashi Iwai * @cond: condition bits 1390df8db936STakashi Iwai * @var: hw_params variable to apply the power-of-2 constraint 13911da177e4SLinus Torvalds */ 1392877211f5STakashi Iwai int snd_pcm_hw_constraint_pow2(struct snd_pcm_runtime *runtime, 13931da177e4SLinus Torvalds unsigned int cond, 13941da177e4SLinus Torvalds snd_pcm_hw_param_t var) 13951da177e4SLinus Torvalds { 13961da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 13971da177e4SLinus Torvalds snd_pcm_hw_rule_pow2, NULL, 13981da177e4SLinus Torvalds var, -1); 13991da177e4SLinus Torvalds } 14001da177e4SLinus Torvalds 1401e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_pow2); 1402e88e8ae6STakashi Iwai 1403d5b702a6SClemens Ladisch static int snd_pcm_hw_rule_noresample_func(struct snd_pcm_hw_params *params, 1404d5b702a6SClemens Ladisch struct snd_pcm_hw_rule *rule) 1405d5b702a6SClemens Ladisch { 1406d5b702a6SClemens Ladisch unsigned int base_rate = (unsigned int)(uintptr_t)rule->private; 1407d5b702a6SClemens Ladisch struct snd_interval *rate; 1408d5b702a6SClemens Ladisch 1409d5b702a6SClemens Ladisch rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); 1410d5b702a6SClemens Ladisch return snd_interval_list(rate, 1, &base_rate, 0); 1411d5b702a6SClemens Ladisch } 1412d5b702a6SClemens Ladisch 1413d5b702a6SClemens Ladisch /** 1414d5b702a6SClemens Ladisch * snd_pcm_hw_rule_noresample - add a rule to allow disabling hw resampling 1415d5b702a6SClemens Ladisch * @runtime: PCM runtime instance 1416d5b702a6SClemens Ladisch * @base_rate: the rate at which the hardware does not resample 1417d5b702a6SClemens Ladisch */ 1418d5b702a6SClemens Ladisch int snd_pcm_hw_rule_noresample(struct snd_pcm_runtime *runtime, 1419d5b702a6SClemens Ladisch unsigned int base_rate) 1420d5b702a6SClemens Ladisch { 1421d5b702a6SClemens Ladisch return snd_pcm_hw_rule_add(runtime, SNDRV_PCM_HW_PARAMS_NORESAMPLE, 1422d5b702a6SClemens Ladisch SNDRV_PCM_HW_PARAM_RATE, 1423d5b702a6SClemens Ladisch snd_pcm_hw_rule_noresample_func, 1424d5b702a6SClemens Ladisch (void *)(uintptr_t)base_rate, 1425d5b702a6SClemens Ladisch SNDRV_PCM_HW_PARAM_RATE, -1); 1426d5b702a6SClemens Ladisch } 1427d5b702a6SClemens Ladisch EXPORT_SYMBOL(snd_pcm_hw_rule_noresample); 1428d5b702a6SClemens Ladisch 1429877211f5STakashi Iwai static void _snd_pcm_hw_param_any(struct snd_pcm_hw_params *params, 1430123992f7SAdrian Bunk snd_pcm_hw_param_t var) 14311da177e4SLinus Torvalds { 14321da177e4SLinus Torvalds if (hw_is_mask(var)) { 14331da177e4SLinus Torvalds snd_mask_any(hw_param_mask(params, var)); 14341da177e4SLinus Torvalds params->cmask |= 1 << var; 14351da177e4SLinus Torvalds params->rmask |= 1 << var; 14361da177e4SLinus Torvalds return; 14371da177e4SLinus Torvalds } 14381da177e4SLinus Torvalds if (hw_is_interval(var)) { 14391da177e4SLinus Torvalds snd_interval_any(hw_param_interval(params, var)); 14401da177e4SLinus Torvalds params->cmask |= 1 << var; 14411da177e4SLinus Torvalds params->rmask |= 1 << var; 14421da177e4SLinus Torvalds return; 14431da177e4SLinus Torvalds } 14441da177e4SLinus Torvalds snd_BUG(); 14451da177e4SLinus Torvalds } 14461da177e4SLinus Torvalds 1447877211f5STakashi Iwai void _snd_pcm_hw_params_any(struct snd_pcm_hw_params *params) 14481da177e4SLinus Torvalds { 14491da177e4SLinus Torvalds unsigned int k; 14501da177e4SLinus Torvalds memset(params, 0, sizeof(*params)); 14511da177e4SLinus Torvalds for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) 14521da177e4SLinus Torvalds _snd_pcm_hw_param_any(params, k); 14531da177e4SLinus Torvalds for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) 14541da177e4SLinus Torvalds _snd_pcm_hw_param_any(params, k); 14551da177e4SLinus Torvalds params->info = ~0U; 14561da177e4SLinus Torvalds } 14571da177e4SLinus Torvalds 1458e88e8ae6STakashi Iwai EXPORT_SYMBOL(_snd_pcm_hw_params_any); 14591da177e4SLinus Torvalds 14601da177e4SLinus Torvalds /** 14611c85cc64SRandy Dunlap * snd_pcm_hw_param_value - return @params field @var value 1462df8db936STakashi Iwai * @params: the hw_params instance 1463df8db936STakashi Iwai * @var: parameter to retrieve 14641c85cc64SRandy Dunlap * @dir: pointer to the direction (-1,0,1) or %NULL 14651da177e4SLinus Torvalds * 14661c85cc64SRandy Dunlap * Return the value for field @var if it's fixed in configuration space 14671c85cc64SRandy Dunlap * defined by @params. Return -%EINVAL otherwise. 14681da177e4SLinus Torvalds */ 1469e88e8ae6STakashi Iwai int snd_pcm_hw_param_value(const struct snd_pcm_hw_params *params, 14701da177e4SLinus Torvalds snd_pcm_hw_param_t var, int *dir) 14711da177e4SLinus Torvalds { 14721da177e4SLinus Torvalds if (hw_is_mask(var)) { 1473877211f5STakashi Iwai const struct snd_mask *mask = hw_param_mask_c(params, var); 14741da177e4SLinus Torvalds if (!snd_mask_single(mask)) 14751da177e4SLinus Torvalds return -EINVAL; 14761da177e4SLinus Torvalds if (dir) 14771da177e4SLinus Torvalds *dir = 0; 14781da177e4SLinus Torvalds return snd_mask_value(mask); 14791da177e4SLinus Torvalds } 14801da177e4SLinus Torvalds if (hw_is_interval(var)) { 1481877211f5STakashi Iwai const struct snd_interval *i = hw_param_interval_c(params, var); 14821da177e4SLinus Torvalds if (!snd_interval_single(i)) 14831da177e4SLinus Torvalds return -EINVAL; 14841da177e4SLinus Torvalds if (dir) 14851da177e4SLinus Torvalds *dir = i->openmin; 14861da177e4SLinus Torvalds return snd_interval_value(i); 14871da177e4SLinus Torvalds } 14881da177e4SLinus Torvalds return -EINVAL; 14891da177e4SLinus Torvalds } 14901da177e4SLinus Torvalds 1491e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_param_value); 14921da177e4SLinus Torvalds 1493877211f5STakashi Iwai void _snd_pcm_hw_param_setempty(struct snd_pcm_hw_params *params, 14941da177e4SLinus Torvalds snd_pcm_hw_param_t var) 14951da177e4SLinus Torvalds { 14961da177e4SLinus Torvalds if (hw_is_mask(var)) { 14971da177e4SLinus Torvalds snd_mask_none(hw_param_mask(params, var)); 14981da177e4SLinus Torvalds params->cmask |= 1 << var; 14991da177e4SLinus Torvalds params->rmask |= 1 << var; 15001da177e4SLinus Torvalds } else if (hw_is_interval(var)) { 15011da177e4SLinus Torvalds snd_interval_none(hw_param_interval(params, var)); 15021da177e4SLinus Torvalds params->cmask |= 1 << var; 15031da177e4SLinus Torvalds params->rmask |= 1 << var; 15041da177e4SLinus Torvalds } else { 15051da177e4SLinus Torvalds snd_BUG(); 15061da177e4SLinus Torvalds } 15071da177e4SLinus Torvalds } 15081da177e4SLinus Torvalds 1509e88e8ae6STakashi Iwai EXPORT_SYMBOL(_snd_pcm_hw_param_setempty); 15101da177e4SLinus Torvalds 1511877211f5STakashi Iwai static int _snd_pcm_hw_param_first(struct snd_pcm_hw_params *params, 15121da177e4SLinus Torvalds snd_pcm_hw_param_t var) 15131da177e4SLinus Torvalds { 15141da177e4SLinus Torvalds int changed; 15151da177e4SLinus Torvalds if (hw_is_mask(var)) 15161da177e4SLinus Torvalds changed = snd_mask_refine_first(hw_param_mask(params, var)); 15171da177e4SLinus Torvalds else if (hw_is_interval(var)) 15181da177e4SLinus Torvalds changed = snd_interval_refine_first(hw_param_interval(params, var)); 15192f4ca8e5STakashi Iwai else 15201da177e4SLinus Torvalds return -EINVAL; 15211da177e4SLinus Torvalds if (changed) { 15221da177e4SLinus Torvalds params->cmask |= 1 << var; 15231da177e4SLinus Torvalds params->rmask |= 1 << var; 15241da177e4SLinus Torvalds } 15251da177e4SLinus Torvalds return changed; 15261da177e4SLinus Torvalds } 15271da177e4SLinus Torvalds 15281da177e4SLinus Torvalds 15291da177e4SLinus Torvalds /** 15301c85cc64SRandy Dunlap * snd_pcm_hw_param_first - refine config space and return minimum value 1531df8db936STakashi Iwai * @pcm: PCM instance 1532df8db936STakashi Iwai * @params: the hw_params instance 1533df8db936STakashi Iwai * @var: parameter to retrieve 15341c85cc64SRandy Dunlap * @dir: pointer to the direction (-1,0,1) or %NULL 15351da177e4SLinus Torvalds * 15361c85cc64SRandy Dunlap * Inside configuration space defined by @params remove from @var all 15371da177e4SLinus Torvalds * values > minimum. Reduce configuration space accordingly. 15381da177e4SLinus Torvalds * Return the minimum. 15391da177e4SLinus Torvalds */ 1540e88e8ae6STakashi Iwai int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm, 1541877211f5STakashi Iwai struct snd_pcm_hw_params *params, 15421da177e4SLinus Torvalds snd_pcm_hw_param_t var, int *dir) 15431da177e4SLinus Torvalds { 15441da177e4SLinus Torvalds int changed = _snd_pcm_hw_param_first(params, var); 15451da177e4SLinus Torvalds if (changed < 0) 15461da177e4SLinus Torvalds return changed; 15471da177e4SLinus Torvalds if (params->rmask) { 15481da177e4SLinus Torvalds int err = snd_pcm_hw_refine(pcm, params); 15497eaa943cSTakashi Iwai if (snd_BUG_ON(err < 0)) 15507eaa943cSTakashi Iwai return err; 15511da177e4SLinus Torvalds } 15521da177e4SLinus Torvalds return snd_pcm_hw_param_value(params, var, dir); 15531da177e4SLinus Torvalds } 15541da177e4SLinus Torvalds 1555e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_param_first); 1556e88e8ae6STakashi Iwai 1557877211f5STakashi Iwai static int _snd_pcm_hw_param_last(struct snd_pcm_hw_params *params, 15581da177e4SLinus Torvalds snd_pcm_hw_param_t var) 15591da177e4SLinus Torvalds { 15601da177e4SLinus Torvalds int changed; 15611da177e4SLinus Torvalds if (hw_is_mask(var)) 15621da177e4SLinus Torvalds changed = snd_mask_refine_last(hw_param_mask(params, var)); 15631da177e4SLinus Torvalds else if (hw_is_interval(var)) 15641da177e4SLinus Torvalds changed = snd_interval_refine_last(hw_param_interval(params, var)); 15652f4ca8e5STakashi Iwai else 15661da177e4SLinus Torvalds return -EINVAL; 15671da177e4SLinus Torvalds if (changed) { 15681da177e4SLinus Torvalds params->cmask |= 1 << var; 15691da177e4SLinus Torvalds params->rmask |= 1 << var; 15701da177e4SLinus Torvalds } 15711da177e4SLinus Torvalds return changed; 15721da177e4SLinus Torvalds } 15731da177e4SLinus Torvalds 15741da177e4SLinus Torvalds 15751da177e4SLinus Torvalds /** 15761c85cc64SRandy Dunlap * snd_pcm_hw_param_last - refine config space and return maximum value 1577df8db936STakashi Iwai * @pcm: PCM instance 1578df8db936STakashi Iwai * @params: the hw_params instance 1579df8db936STakashi Iwai * @var: parameter to retrieve 15801c85cc64SRandy Dunlap * @dir: pointer to the direction (-1,0,1) or %NULL 15811da177e4SLinus Torvalds * 15821c85cc64SRandy Dunlap * Inside configuration space defined by @params remove from @var all 15831da177e4SLinus Torvalds * values < maximum. Reduce configuration space accordingly. 15841da177e4SLinus Torvalds * Return the maximum. 15851da177e4SLinus Torvalds */ 1586e88e8ae6STakashi Iwai int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm, 1587877211f5STakashi Iwai struct snd_pcm_hw_params *params, 15881da177e4SLinus Torvalds snd_pcm_hw_param_t var, int *dir) 15891da177e4SLinus Torvalds { 15901da177e4SLinus Torvalds int changed = _snd_pcm_hw_param_last(params, var); 15911da177e4SLinus Torvalds if (changed < 0) 15921da177e4SLinus Torvalds return changed; 15931da177e4SLinus Torvalds if (params->rmask) { 15941da177e4SLinus Torvalds int err = snd_pcm_hw_refine(pcm, params); 15957eaa943cSTakashi Iwai if (snd_BUG_ON(err < 0)) 15967eaa943cSTakashi Iwai return err; 15971da177e4SLinus Torvalds } 15981da177e4SLinus Torvalds return snd_pcm_hw_param_value(params, var, dir); 15991da177e4SLinus Torvalds } 16001da177e4SLinus Torvalds 1601e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_param_last); 16021da177e4SLinus Torvalds 16031da177e4SLinus Torvalds /** 16041c85cc64SRandy Dunlap * snd_pcm_hw_param_choose - choose a configuration defined by @params 1605df8db936STakashi Iwai * @pcm: PCM instance 1606df8db936STakashi Iwai * @params: the hw_params instance 16071da177e4SLinus Torvalds * 16081c85cc64SRandy Dunlap * Choose one configuration from configuration space defined by @params. 16091da177e4SLinus Torvalds * The configuration chosen is that obtained fixing in this order: 16101da177e4SLinus Torvalds * first access, first format, first subformat, min channels, 16111da177e4SLinus Torvalds * min rate, min period time, max buffer size, min tick time 16121da177e4SLinus Torvalds */ 16132f4ca8e5STakashi Iwai int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm, 16142f4ca8e5STakashi Iwai struct snd_pcm_hw_params *params) 16151da177e4SLinus Torvalds { 16162f4ca8e5STakashi Iwai static int vars[] = { 16172f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_ACCESS, 16182f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_FORMAT, 16192f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_SUBFORMAT, 16202f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_CHANNELS, 16212f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_RATE, 16222f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_PERIOD_TIME, 16232f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 16242f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_TICK_TIME, 16252f4ca8e5STakashi Iwai -1 16262f4ca8e5STakashi Iwai }; 16272f4ca8e5STakashi Iwai int err, *v; 16281da177e4SLinus Torvalds 16292f4ca8e5STakashi Iwai for (v = vars; *v != -1; v++) { 16302f4ca8e5STakashi Iwai if (*v != SNDRV_PCM_HW_PARAM_BUFFER_SIZE) 16312f4ca8e5STakashi Iwai err = snd_pcm_hw_param_first(pcm, params, *v, NULL); 16322f4ca8e5STakashi Iwai else 16332f4ca8e5STakashi Iwai err = snd_pcm_hw_param_last(pcm, params, *v, NULL); 16347eaa943cSTakashi Iwai if (snd_BUG_ON(err < 0)) 16357eaa943cSTakashi Iwai return err; 16362f4ca8e5STakashi Iwai } 16371da177e4SLinus Torvalds return 0; 16381da177e4SLinus Torvalds } 16391da177e4SLinus Torvalds 1640877211f5STakashi Iwai static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream, 16411da177e4SLinus Torvalds void *arg) 16421da177e4SLinus Torvalds { 1643877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 16441da177e4SLinus Torvalds unsigned long flags; 16451da177e4SLinus Torvalds snd_pcm_stream_lock_irqsave(substream, flags); 16461da177e4SLinus Torvalds if (snd_pcm_running(substream) && 16471da177e4SLinus Torvalds snd_pcm_update_hw_ptr(substream) >= 0) 16481da177e4SLinus Torvalds runtime->status->hw_ptr %= runtime->buffer_size; 16491da177e4SLinus Torvalds else 16501da177e4SLinus Torvalds runtime->status->hw_ptr = 0; 16511da177e4SLinus Torvalds snd_pcm_stream_unlock_irqrestore(substream, flags); 16521da177e4SLinus Torvalds return 0; 16531da177e4SLinus Torvalds } 16541da177e4SLinus Torvalds 1655877211f5STakashi Iwai static int snd_pcm_lib_ioctl_channel_info(struct snd_pcm_substream *substream, 16561da177e4SLinus Torvalds void *arg) 16571da177e4SLinus Torvalds { 1658877211f5STakashi Iwai struct snd_pcm_channel_info *info = arg; 1659877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 16601da177e4SLinus Torvalds int width; 16611da177e4SLinus Torvalds if (!(runtime->info & SNDRV_PCM_INFO_MMAP)) { 16621da177e4SLinus Torvalds info->offset = -1; 16631da177e4SLinus Torvalds return 0; 16641da177e4SLinus Torvalds } 16651da177e4SLinus Torvalds width = snd_pcm_format_physical_width(runtime->format); 16661da177e4SLinus Torvalds if (width < 0) 16671da177e4SLinus Torvalds return width; 16681da177e4SLinus Torvalds info->offset = 0; 16691da177e4SLinus Torvalds switch (runtime->access) { 16701da177e4SLinus Torvalds case SNDRV_PCM_ACCESS_MMAP_INTERLEAVED: 16711da177e4SLinus Torvalds case SNDRV_PCM_ACCESS_RW_INTERLEAVED: 16721da177e4SLinus Torvalds info->first = info->channel * width; 16731da177e4SLinus Torvalds info->step = runtime->channels * width; 16741da177e4SLinus Torvalds break; 16751da177e4SLinus Torvalds case SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED: 16761da177e4SLinus Torvalds case SNDRV_PCM_ACCESS_RW_NONINTERLEAVED: 16771da177e4SLinus Torvalds { 16781da177e4SLinus Torvalds size_t size = runtime->dma_bytes / runtime->channels; 16791da177e4SLinus Torvalds info->first = info->channel * size * 8; 16801da177e4SLinus Torvalds info->step = width; 16811da177e4SLinus Torvalds break; 16821da177e4SLinus Torvalds } 16831da177e4SLinus Torvalds default: 16841da177e4SLinus Torvalds snd_BUG(); 16851da177e4SLinus Torvalds break; 16861da177e4SLinus Torvalds } 16871da177e4SLinus Torvalds return 0; 16881da177e4SLinus Torvalds } 16891da177e4SLinus Torvalds 16908bea869cSJaroslav Kysela static int snd_pcm_lib_ioctl_fifo_size(struct snd_pcm_substream *substream, 16918bea869cSJaroslav Kysela void *arg) 16928bea869cSJaroslav Kysela { 16938bea869cSJaroslav Kysela struct snd_pcm_hw_params *params = arg; 16948bea869cSJaroslav Kysela snd_pcm_format_t format; 16958bea869cSJaroslav Kysela int channels, width; 16968bea869cSJaroslav Kysela 16978bea869cSJaroslav Kysela params->fifo_size = substream->runtime->hw.fifo_size; 16988bea869cSJaroslav Kysela if (!(substream->runtime->hw.info & SNDRV_PCM_INFO_FIFO_IN_FRAMES)) { 16998bea869cSJaroslav Kysela format = params_format(params); 17008bea869cSJaroslav Kysela channels = params_channels(params); 17018bea869cSJaroslav Kysela width = snd_pcm_format_physical_width(format); 17028bea869cSJaroslav Kysela params->fifo_size /= width * channels; 17038bea869cSJaroslav Kysela } 17048bea869cSJaroslav Kysela return 0; 17058bea869cSJaroslav Kysela } 17068bea869cSJaroslav Kysela 17071da177e4SLinus Torvalds /** 17081da177e4SLinus Torvalds * snd_pcm_lib_ioctl - a generic PCM ioctl callback 17091da177e4SLinus Torvalds * @substream: the pcm substream instance 17101da177e4SLinus Torvalds * @cmd: ioctl command 17111da177e4SLinus Torvalds * @arg: ioctl argument 17121da177e4SLinus Torvalds * 17131da177e4SLinus Torvalds * Processes the generic ioctl commands for PCM. 17141da177e4SLinus Torvalds * Can be passed as the ioctl callback for PCM ops. 17151da177e4SLinus Torvalds * 17161da177e4SLinus Torvalds * Returns zero if successful, or a negative error code on failure. 17171da177e4SLinus Torvalds */ 1718877211f5STakashi Iwai int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream, 17191da177e4SLinus Torvalds unsigned int cmd, void *arg) 17201da177e4SLinus Torvalds { 17211da177e4SLinus Torvalds switch (cmd) { 17221da177e4SLinus Torvalds case SNDRV_PCM_IOCTL1_INFO: 17231da177e4SLinus Torvalds return 0; 17241da177e4SLinus Torvalds case SNDRV_PCM_IOCTL1_RESET: 17251da177e4SLinus Torvalds return snd_pcm_lib_ioctl_reset(substream, arg); 17261da177e4SLinus Torvalds case SNDRV_PCM_IOCTL1_CHANNEL_INFO: 17271da177e4SLinus Torvalds return snd_pcm_lib_ioctl_channel_info(substream, arg); 17288bea869cSJaroslav Kysela case SNDRV_PCM_IOCTL1_FIFO_SIZE: 17298bea869cSJaroslav Kysela return snd_pcm_lib_ioctl_fifo_size(substream, arg); 17301da177e4SLinus Torvalds } 17311da177e4SLinus Torvalds return -ENXIO; 17321da177e4SLinus Torvalds } 17331da177e4SLinus Torvalds 1734e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_lib_ioctl); 1735e88e8ae6STakashi Iwai 17361da177e4SLinus Torvalds /** 17371da177e4SLinus Torvalds * snd_pcm_period_elapsed - update the pcm status for the next period 17381da177e4SLinus Torvalds * @substream: the pcm substream instance 17391da177e4SLinus Torvalds * 17401da177e4SLinus Torvalds * This function is called from the interrupt handler when the 17411da177e4SLinus Torvalds * PCM has processed the period size. It will update the current 174231e8960bSTakashi Iwai * pointer, wake up sleepers, etc. 17431da177e4SLinus Torvalds * 17441da177e4SLinus Torvalds * Even if more than one periods have elapsed since the last call, you 17451da177e4SLinus Torvalds * have to call this only once. 17461da177e4SLinus Torvalds */ 1747877211f5STakashi Iwai void snd_pcm_period_elapsed(struct snd_pcm_substream *substream) 17481da177e4SLinus Torvalds { 1749877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 17501da177e4SLinus Torvalds unsigned long flags; 17511da177e4SLinus Torvalds 17527eaa943cSTakashi Iwai if (PCM_RUNTIME_CHECK(substream)) 17537eaa943cSTakashi Iwai return; 17541da177e4SLinus Torvalds runtime = substream->runtime; 17551da177e4SLinus Torvalds 17561da177e4SLinus Torvalds if (runtime->transfer_ack_begin) 17571da177e4SLinus Torvalds runtime->transfer_ack_begin(substream); 17581da177e4SLinus Torvalds 17591da177e4SLinus Torvalds snd_pcm_stream_lock_irqsave(substream, flags); 17601da177e4SLinus Torvalds if (!snd_pcm_running(substream) || 1761f240406bSJaroslav Kysela snd_pcm_update_hw_ptr0(substream, 1) < 0) 17621da177e4SLinus Torvalds goto _end; 17631da177e4SLinus Torvalds 17641da177e4SLinus Torvalds if (substream->timer_running) 17651da177e4SLinus Torvalds snd_timer_interrupt(substream->timer, 1); 17661da177e4SLinus Torvalds _end: 17671da177e4SLinus Torvalds snd_pcm_stream_unlock_irqrestore(substream, flags); 17681da177e4SLinus Torvalds if (runtime->transfer_ack_end) 17691da177e4SLinus Torvalds runtime->transfer_ack_end(substream); 17701da177e4SLinus Torvalds kill_fasync(&runtime->fasync, SIGIO, POLL_IN); 17711da177e4SLinus Torvalds } 17721da177e4SLinus Torvalds 1773e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_period_elapsed); 1774e88e8ae6STakashi Iwai 177513075510STakashi Iwai /* 177613075510STakashi Iwai * Wait until avail_min data becomes available 177713075510STakashi Iwai * Returns a negative error code if any error occurs during operation. 177813075510STakashi Iwai * The available space is stored on availp. When err = 0 and avail = 0 177913075510STakashi Iwai * on the capture stream, it indicates the stream is in DRAINING state. 178013075510STakashi Iwai */ 17815daeba34SDavid Dillow static int wait_for_avail(struct snd_pcm_substream *substream, 178213075510STakashi Iwai snd_pcm_uframes_t *availp) 178313075510STakashi Iwai { 178413075510STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 178513075510STakashi Iwai int is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 178613075510STakashi Iwai wait_queue_t wait; 178713075510STakashi Iwai int err = 0; 178813075510STakashi Iwai snd_pcm_uframes_t avail = 0; 1789f2b3614cSTakashi Iwai long wait_time, tout; 179013075510STakashi Iwai 1791763437a9SArjan van de Ven init_waitqueue_entry(&wait, current); 1792763437a9SArjan van de Ven set_current_state(TASK_INTERRUPTIBLE); 1793763437a9SArjan van de Ven add_wait_queue(&runtime->tsleep, &wait); 1794763437a9SArjan van de Ven 1795f2b3614cSTakashi Iwai if (runtime->no_period_wakeup) 1796f2b3614cSTakashi Iwai wait_time = MAX_SCHEDULE_TIMEOUT; 1797f2b3614cSTakashi Iwai else { 1798f2b3614cSTakashi Iwai wait_time = 10; 1799f2b3614cSTakashi Iwai if (runtime->rate) { 1800f2b3614cSTakashi Iwai long t = runtime->period_size * 2 / runtime->rate; 1801f2b3614cSTakashi Iwai wait_time = max(t, wait_time); 1802f2b3614cSTakashi Iwai } 1803f2b3614cSTakashi Iwai wait_time = msecs_to_jiffies(wait_time * 1000); 1804f2b3614cSTakashi Iwai } 1805763437a9SArjan van de Ven 180613075510STakashi Iwai for (;;) { 180713075510STakashi Iwai if (signal_pending(current)) { 180813075510STakashi Iwai err = -ERESTARTSYS; 180913075510STakashi Iwai break; 181013075510STakashi Iwai } 1811763437a9SArjan van de Ven 1812763437a9SArjan van de Ven /* 1813763437a9SArjan van de Ven * We need to check if space became available already 1814763437a9SArjan van de Ven * (and thus the wakeup happened already) first to close 1815763437a9SArjan van de Ven * the race of space already having become available. 1816763437a9SArjan van de Ven * This check must happen after been added to the waitqueue 1817763437a9SArjan van de Ven * and having current state be INTERRUPTIBLE. 1818763437a9SArjan van de Ven */ 1819763437a9SArjan van de Ven if (is_playback) 1820763437a9SArjan van de Ven avail = snd_pcm_playback_avail(runtime); 1821763437a9SArjan van de Ven else 1822763437a9SArjan van de Ven avail = snd_pcm_capture_avail(runtime); 1823763437a9SArjan van de Ven if (avail >= runtime->twake) 1824763437a9SArjan van de Ven break; 182513075510STakashi Iwai snd_pcm_stream_unlock_irq(substream); 1826763437a9SArjan van de Ven 1827763437a9SArjan van de Ven tout = schedule_timeout(wait_time); 1828763437a9SArjan van de Ven 182913075510STakashi Iwai snd_pcm_stream_lock_irq(substream); 1830763437a9SArjan van de Ven set_current_state(TASK_INTERRUPTIBLE); 183113075510STakashi Iwai switch (runtime->status->state) { 183213075510STakashi Iwai case SNDRV_PCM_STATE_SUSPENDED: 183313075510STakashi Iwai err = -ESTRPIPE; 183413075510STakashi Iwai goto _endloop; 183513075510STakashi Iwai case SNDRV_PCM_STATE_XRUN: 183613075510STakashi Iwai err = -EPIPE; 183713075510STakashi Iwai goto _endloop; 183813075510STakashi Iwai case SNDRV_PCM_STATE_DRAINING: 183913075510STakashi Iwai if (is_playback) 184013075510STakashi Iwai err = -EPIPE; 184113075510STakashi Iwai else 184213075510STakashi Iwai avail = 0; /* indicate draining */ 184313075510STakashi Iwai goto _endloop; 184413075510STakashi Iwai case SNDRV_PCM_STATE_OPEN: 184513075510STakashi Iwai case SNDRV_PCM_STATE_SETUP: 184613075510STakashi Iwai case SNDRV_PCM_STATE_DISCONNECTED: 184713075510STakashi Iwai err = -EBADFD; 184813075510STakashi Iwai goto _endloop; 184913075510STakashi Iwai } 185013075510STakashi Iwai if (!tout) { 185113075510STakashi Iwai snd_printd("%s write error (DMA or IRQ trouble?)\n", 185213075510STakashi Iwai is_playback ? "playback" : "capture"); 185313075510STakashi Iwai err = -EIO; 185413075510STakashi Iwai break; 185513075510STakashi Iwai } 185613075510STakashi Iwai } 185713075510STakashi Iwai _endloop: 1858763437a9SArjan van de Ven set_current_state(TASK_RUNNING); 1859c91a988dSJaroslav Kysela remove_wait_queue(&runtime->tsleep, &wait); 186013075510STakashi Iwai *availp = avail; 186113075510STakashi Iwai return err; 186213075510STakashi Iwai } 186313075510STakashi Iwai 1864877211f5STakashi Iwai static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream, 18651da177e4SLinus Torvalds unsigned int hwoff, 18661da177e4SLinus Torvalds unsigned long data, unsigned int off, 18671da177e4SLinus Torvalds snd_pcm_uframes_t frames) 18681da177e4SLinus Torvalds { 1869877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 18701da177e4SLinus Torvalds int err; 18711da177e4SLinus Torvalds char __user *buf = (char __user *) data + frames_to_bytes(runtime, off); 18721da177e4SLinus Torvalds if (substream->ops->copy) { 18731da177e4SLinus Torvalds if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0) 18741da177e4SLinus Torvalds return err; 18751da177e4SLinus Torvalds } else { 18761da177e4SLinus Torvalds char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); 18771da177e4SLinus Torvalds if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames))) 18781da177e4SLinus Torvalds return -EFAULT; 18791da177e4SLinus Torvalds } 18801da177e4SLinus Torvalds return 0; 18811da177e4SLinus Torvalds } 18821da177e4SLinus Torvalds 1883877211f5STakashi Iwai typedef int (*transfer_f)(struct snd_pcm_substream *substream, unsigned int hwoff, 18841da177e4SLinus Torvalds unsigned long data, unsigned int off, 18851da177e4SLinus Torvalds snd_pcm_uframes_t size); 18861da177e4SLinus Torvalds 1887877211f5STakashi Iwai static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, 18881da177e4SLinus Torvalds unsigned long data, 18891da177e4SLinus Torvalds snd_pcm_uframes_t size, 18901da177e4SLinus Torvalds int nonblock, 18911da177e4SLinus Torvalds transfer_f transfer) 18921da177e4SLinus Torvalds { 1893877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 18941da177e4SLinus Torvalds snd_pcm_uframes_t xfer = 0; 18951da177e4SLinus Torvalds snd_pcm_uframes_t offset = 0; 18961da177e4SLinus Torvalds int err = 0; 18971da177e4SLinus Torvalds 18981da177e4SLinus Torvalds if (size == 0) 18991da177e4SLinus Torvalds return 0; 19001da177e4SLinus Torvalds 19011da177e4SLinus Torvalds snd_pcm_stream_lock_irq(substream); 19021da177e4SLinus Torvalds switch (runtime->status->state) { 19031da177e4SLinus Torvalds case SNDRV_PCM_STATE_PREPARED: 19041da177e4SLinus Torvalds case SNDRV_PCM_STATE_RUNNING: 19051da177e4SLinus Torvalds case SNDRV_PCM_STATE_PAUSED: 19061da177e4SLinus Torvalds break; 19071da177e4SLinus Torvalds case SNDRV_PCM_STATE_XRUN: 19081da177e4SLinus Torvalds err = -EPIPE; 19091da177e4SLinus Torvalds goto _end_unlock; 19101da177e4SLinus Torvalds case SNDRV_PCM_STATE_SUSPENDED: 19111da177e4SLinus Torvalds err = -ESTRPIPE; 19121da177e4SLinus Torvalds goto _end_unlock; 19131da177e4SLinus Torvalds default: 19141da177e4SLinus Torvalds err = -EBADFD; 19151da177e4SLinus Torvalds goto _end_unlock; 19161da177e4SLinus Torvalds } 19171da177e4SLinus Torvalds 19185daeba34SDavid Dillow runtime->twake = runtime->control->avail_min ? : 1; 19191da177e4SLinus Torvalds while (size > 0) { 19201da177e4SLinus Torvalds snd_pcm_uframes_t frames, appl_ptr, appl_ofs; 19211da177e4SLinus Torvalds snd_pcm_uframes_t avail; 19221da177e4SLinus Torvalds snd_pcm_uframes_t cont; 192331e8960bSTakashi Iwai if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) 19241da177e4SLinus Torvalds snd_pcm_update_hw_ptr(substream); 19251da177e4SLinus Torvalds avail = snd_pcm_playback_avail(runtime); 192613075510STakashi Iwai if (!avail) { 19271da177e4SLinus Torvalds if (nonblock) { 19281da177e4SLinus Torvalds err = -EAGAIN; 19291da177e4SLinus Torvalds goto _end_unlock; 19301da177e4SLinus Torvalds } 19315daeba34SDavid Dillow runtime->twake = min_t(snd_pcm_uframes_t, size, 19325daeba34SDavid Dillow runtime->control->avail_min ? : 1); 19335daeba34SDavid Dillow err = wait_for_avail(substream, &avail); 193413075510STakashi Iwai if (err < 0) 19351da177e4SLinus Torvalds goto _end_unlock; 19361da177e4SLinus Torvalds } 19371da177e4SLinus Torvalds frames = size > avail ? avail : size; 19381da177e4SLinus Torvalds cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; 19391da177e4SLinus Torvalds if (frames > cont) 19401da177e4SLinus Torvalds frames = cont; 19417eaa943cSTakashi Iwai if (snd_BUG_ON(!frames)) { 1942c91a988dSJaroslav Kysela runtime->twake = 0; 19437eaa943cSTakashi Iwai snd_pcm_stream_unlock_irq(substream); 19447eaa943cSTakashi Iwai return -EINVAL; 19457eaa943cSTakashi Iwai } 19461da177e4SLinus Torvalds appl_ptr = runtime->control->appl_ptr; 19471da177e4SLinus Torvalds appl_ofs = appl_ptr % runtime->buffer_size; 19481da177e4SLinus Torvalds snd_pcm_stream_unlock_irq(substream); 19491250932eSJaroslav Kysela err = transfer(substream, appl_ofs, data, offset, frames); 19501da177e4SLinus Torvalds snd_pcm_stream_lock_irq(substream); 19511250932eSJaroslav Kysela if (err < 0) 19521250932eSJaroslav Kysela goto _end_unlock; 19531da177e4SLinus Torvalds switch (runtime->status->state) { 19541da177e4SLinus Torvalds case SNDRV_PCM_STATE_XRUN: 19551da177e4SLinus Torvalds err = -EPIPE; 19561da177e4SLinus Torvalds goto _end_unlock; 19571da177e4SLinus Torvalds case SNDRV_PCM_STATE_SUSPENDED: 19581da177e4SLinus Torvalds err = -ESTRPIPE; 19591da177e4SLinus Torvalds goto _end_unlock; 19601da177e4SLinus Torvalds default: 19611da177e4SLinus Torvalds break; 19621da177e4SLinus Torvalds } 19631da177e4SLinus Torvalds appl_ptr += frames; 19641da177e4SLinus Torvalds if (appl_ptr >= runtime->boundary) 19651da177e4SLinus Torvalds appl_ptr -= runtime->boundary; 19661da177e4SLinus Torvalds runtime->control->appl_ptr = appl_ptr; 19671da177e4SLinus Torvalds if (substream->ops->ack) 19681da177e4SLinus Torvalds substream->ops->ack(substream); 19691da177e4SLinus Torvalds 19701da177e4SLinus Torvalds offset += frames; 19711da177e4SLinus Torvalds size -= frames; 19721da177e4SLinus Torvalds xfer += frames; 19731da177e4SLinus Torvalds if (runtime->status->state == SNDRV_PCM_STATE_PREPARED && 19741da177e4SLinus Torvalds snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) { 19751da177e4SLinus Torvalds err = snd_pcm_start(substream); 19761da177e4SLinus Torvalds if (err < 0) 19771da177e4SLinus Torvalds goto _end_unlock; 19781da177e4SLinus Torvalds } 19791da177e4SLinus Torvalds } 19801da177e4SLinus Torvalds _end_unlock: 1981c91a988dSJaroslav Kysela runtime->twake = 0; 19821250932eSJaroslav Kysela if (xfer > 0 && err >= 0) 19831250932eSJaroslav Kysela snd_pcm_update_state(substream, runtime); 19841da177e4SLinus Torvalds snd_pcm_stream_unlock_irq(substream); 19851da177e4SLinus Torvalds return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; 19861da177e4SLinus Torvalds } 19871da177e4SLinus Torvalds 19887eaa943cSTakashi Iwai /* sanity-check for read/write methods */ 19897eaa943cSTakashi Iwai static int pcm_sanity_check(struct snd_pcm_substream *substream) 19907eaa943cSTakashi Iwai { 19917eaa943cSTakashi Iwai struct snd_pcm_runtime *runtime; 19927eaa943cSTakashi Iwai if (PCM_RUNTIME_CHECK(substream)) 19937eaa943cSTakashi Iwai return -ENXIO; 19947eaa943cSTakashi Iwai runtime = substream->runtime; 19957eaa943cSTakashi Iwai if (snd_BUG_ON(!substream->ops->copy && !runtime->dma_area)) 19967eaa943cSTakashi Iwai return -EINVAL; 19977eaa943cSTakashi Iwai if (runtime->status->state == SNDRV_PCM_STATE_OPEN) 19987eaa943cSTakashi Iwai return -EBADFD; 19997eaa943cSTakashi Iwai return 0; 20007eaa943cSTakashi Iwai } 20017eaa943cSTakashi Iwai 2002877211f5STakashi Iwai snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const void __user *buf, snd_pcm_uframes_t size) 20031da177e4SLinus Torvalds { 2004877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 20051da177e4SLinus Torvalds int nonblock; 20067eaa943cSTakashi Iwai int err; 20071da177e4SLinus Torvalds 20087eaa943cSTakashi Iwai err = pcm_sanity_check(substream); 20097eaa943cSTakashi Iwai if (err < 0) 20107eaa943cSTakashi Iwai return err; 20111da177e4SLinus Torvalds runtime = substream->runtime; 20120df63e44STakashi Iwai nonblock = !!(substream->f_flags & O_NONBLOCK); 20131da177e4SLinus Torvalds 20141da177e4SLinus Torvalds if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && 20151da177e4SLinus Torvalds runtime->channels > 1) 20161da177e4SLinus Torvalds return -EINVAL; 20171da177e4SLinus Torvalds return snd_pcm_lib_write1(substream, (unsigned long)buf, size, nonblock, 20181da177e4SLinus Torvalds snd_pcm_lib_write_transfer); 20191da177e4SLinus Torvalds } 20201da177e4SLinus Torvalds 2021e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_lib_write); 2022e88e8ae6STakashi Iwai 2023877211f5STakashi Iwai static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream, 20241da177e4SLinus Torvalds unsigned int hwoff, 20251da177e4SLinus Torvalds unsigned long data, unsigned int off, 20261da177e4SLinus Torvalds snd_pcm_uframes_t frames) 20271da177e4SLinus Torvalds { 2028877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 20291da177e4SLinus Torvalds int err; 20301da177e4SLinus Torvalds void __user **bufs = (void __user **)data; 20311da177e4SLinus Torvalds int channels = runtime->channels; 20321da177e4SLinus Torvalds int c; 20331da177e4SLinus Torvalds if (substream->ops->copy) { 20347eaa943cSTakashi Iwai if (snd_BUG_ON(!substream->ops->silence)) 20357eaa943cSTakashi Iwai return -EINVAL; 20361da177e4SLinus Torvalds for (c = 0; c < channels; ++c, ++bufs) { 20371da177e4SLinus Torvalds if (*bufs == NULL) { 20381da177e4SLinus Torvalds if ((err = substream->ops->silence(substream, c, hwoff, frames)) < 0) 20391da177e4SLinus Torvalds return err; 20401da177e4SLinus Torvalds } else { 20411da177e4SLinus Torvalds char __user *buf = *bufs + samples_to_bytes(runtime, off); 20421da177e4SLinus Torvalds if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0) 20431da177e4SLinus Torvalds return err; 20441da177e4SLinus Torvalds } 20451da177e4SLinus Torvalds } 20461da177e4SLinus Torvalds } else { 20471da177e4SLinus Torvalds /* default transfer behaviour */ 20481da177e4SLinus Torvalds size_t dma_csize = runtime->dma_bytes / channels; 20491da177e4SLinus Torvalds for (c = 0; c < channels; ++c, ++bufs) { 20501da177e4SLinus Torvalds char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff); 20511da177e4SLinus Torvalds if (*bufs == NULL) { 20521da177e4SLinus Torvalds snd_pcm_format_set_silence(runtime->format, hwbuf, frames); 20531da177e4SLinus Torvalds } else { 20541da177e4SLinus Torvalds char __user *buf = *bufs + samples_to_bytes(runtime, off); 20551da177e4SLinus Torvalds if (copy_from_user(hwbuf, buf, samples_to_bytes(runtime, frames))) 20561da177e4SLinus Torvalds return -EFAULT; 20571da177e4SLinus Torvalds } 20581da177e4SLinus Torvalds } 20591da177e4SLinus Torvalds } 20601da177e4SLinus Torvalds return 0; 20611da177e4SLinus Torvalds } 20621da177e4SLinus Torvalds 2063877211f5STakashi Iwai snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream, 20641da177e4SLinus Torvalds void __user **bufs, 20651da177e4SLinus Torvalds snd_pcm_uframes_t frames) 20661da177e4SLinus Torvalds { 2067877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 20681da177e4SLinus Torvalds int nonblock; 20697eaa943cSTakashi Iwai int err; 20701da177e4SLinus Torvalds 20717eaa943cSTakashi Iwai err = pcm_sanity_check(substream); 20727eaa943cSTakashi Iwai if (err < 0) 20737eaa943cSTakashi Iwai return err; 20741da177e4SLinus Torvalds runtime = substream->runtime; 20750df63e44STakashi Iwai nonblock = !!(substream->f_flags & O_NONBLOCK); 20761da177e4SLinus Torvalds 20771da177e4SLinus Torvalds if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) 20781da177e4SLinus Torvalds return -EINVAL; 20791da177e4SLinus Torvalds return snd_pcm_lib_write1(substream, (unsigned long)bufs, frames, 20801da177e4SLinus Torvalds nonblock, snd_pcm_lib_writev_transfer); 20811da177e4SLinus Torvalds } 20821da177e4SLinus Torvalds 2083e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_lib_writev); 2084e88e8ae6STakashi Iwai 2085877211f5STakashi Iwai static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream, 20861da177e4SLinus Torvalds unsigned int hwoff, 20871da177e4SLinus Torvalds unsigned long data, unsigned int off, 20881da177e4SLinus Torvalds snd_pcm_uframes_t frames) 20891da177e4SLinus Torvalds { 2090877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 20911da177e4SLinus Torvalds int err; 20921da177e4SLinus Torvalds char __user *buf = (char __user *) data + frames_to_bytes(runtime, off); 20931da177e4SLinus Torvalds if (substream->ops->copy) { 20941da177e4SLinus Torvalds if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0) 20951da177e4SLinus Torvalds return err; 20961da177e4SLinus Torvalds } else { 20971da177e4SLinus Torvalds char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); 20981da177e4SLinus Torvalds if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames))) 20991da177e4SLinus Torvalds return -EFAULT; 21001da177e4SLinus Torvalds } 21011da177e4SLinus Torvalds return 0; 21021da177e4SLinus Torvalds } 21031da177e4SLinus Torvalds 2104877211f5STakashi Iwai static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, 21051da177e4SLinus Torvalds unsigned long data, 21061da177e4SLinus Torvalds snd_pcm_uframes_t size, 21071da177e4SLinus Torvalds int nonblock, 21081da177e4SLinus Torvalds transfer_f transfer) 21091da177e4SLinus Torvalds { 2110877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 21111da177e4SLinus Torvalds snd_pcm_uframes_t xfer = 0; 21121da177e4SLinus Torvalds snd_pcm_uframes_t offset = 0; 21131da177e4SLinus Torvalds int err = 0; 21141da177e4SLinus Torvalds 21151da177e4SLinus Torvalds if (size == 0) 21161da177e4SLinus Torvalds return 0; 21171da177e4SLinus Torvalds 21181da177e4SLinus Torvalds snd_pcm_stream_lock_irq(substream); 21191da177e4SLinus Torvalds switch (runtime->status->state) { 21201da177e4SLinus Torvalds case SNDRV_PCM_STATE_PREPARED: 21211da177e4SLinus Torvalds if (size >= runtime->start_threshold) { 21221da177e4SLinus Torvalds err = snd_pcm_start(substream); 21231da177e4SLinus Torvalds if (err < 0) 21241da177e4SLinus Torvalds goto _end_unlock; 21251da177e4SLinus Torvalds } 21261da177e4SLinus Torvalds break; 21271da177e4SLinus Torvalds case SNDRV_PCM_STATE_DRAINING: 21281da177e4SLinus Torvalds case SNDRV_PCM_STATE_RUNNING: 21291da177e4SLinus Torvalds case SNDRV_PCM_STATE_PAUSED: 21301da177e4SLinus Torvalds break; 21311da177e4SLinus Torvalds case SNDRV_PCM_STATE_XRUN: 21321da177e4SLinus Torvalds err = -EPIPE; 21331da177e4SLinus Torvalds goto _end_unlock; 21341da177e4SLinus Torvalds case SNDRV_PCM_STATE_SUSPENDED: 21351da177e4SLinus Torvalds err = -ESTRPIPE; 21361da177e4SLinus Torvalds goto _end_unlock; 21371da177e4SLinus Torvalds default: 21381da177e4SLinus Torvalds err = -EBADFD; 21391da177e4SLinus Torvalds goto _end_unlock; 21401da177e4SLinus Torvalds } 21411da177e4SLinus Torvalds 21425daeba34SDavid Dillow runtime->twake = runtime->control->avail_min ? : 1; 21431da177e4SLinus Torvalds while (size > 0) { 21441da177e4SLinus Torvalds snd_pcm_uframes_t frames, appl_ptr, appl_ofs; 21451da177e4SLinus Torvalds snd_pcm_uframes_t avail; 21461da177e4SLinus Torvalds snd_pcm_uframes_t cont; 214731e8960bSTakashi Iwai if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) 21481da177e4SLinus Torvalds snd_pcm_update_hw_ptr(substream); 21491da177e4SLinus Torvalds avail = snd_pcm_capture_avail(runtime); 2150d948035aSTakashi Iwai if (!avail) { 215113075510STakashi Iwai if (runtime->status->state == 215213075510STakashi Iwai SNDRV_PCM_STATE_DRAINING) { 215313075510STakashi Iwai snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); 21541da177e4SLinus Torvalds goto _end_unlock; 21551da177e4SLinus Torvalds } 21561da177e4SLinus Torvalds if (nonblock) { 21571da177e4SLinus Torvalds err = -EAGAIN; 21581da177e4SLinus Torvalds goto _end_unlock; 21591da177e4SLinus Torvalds } 21605daeba34SDavid Dillow runtime->twake = min_t(snd_pcm_uframes_t, size, 21615daeba34SDavid Dillow runtime->control->avail_min ? : 1); 21625daeba34SDavid Dillow err = wait_for_avail(substream, &avail); 216313075510STakashi Iwai if (err < 0) 21641da177e4SLinus Torvalds goto _end_unlock; 216513075510STakashi Iwai if (!avail) 216613075510STakashi Iwai continue; /* draining */ 21671da177e4SLinus Torvalds } 21681da177e4SLinus Torvalds frames = size > avail ? avail : size; 21691da177e4SLinus Torvalds cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; 21701da177e4SLinus Torvalds if (frames > cont) 21711da177e4SLinus Torvalds frames = cont; 21727eaa943cSTakashi Iwai if (snd_BUG_ON(!frames)) { 2173c91a988dSJaroslav Kysela runtime->twake = 0; 21747eaa943cSTakashi Iwai snd_pcm_stream_unlock_irq(substream); 21757eaa943cSTakashi Iwai return -EINVAL; 21767eaa943cSTakashi Iwai } 21771da177e4SLinus Torvalds appl_ptr = runtime->control->appl_ptr; 21781da177e4SLinus Torvalds appl_ofs = appl_ptr % runtime->buffer_size; 21791da177e4SLinus Torvalds snd_pcm_stream_unlock_irq(substream); 21801250932eSJaroslav Kysela err = transfer(substream, appl_ofs, data, offset, frames); 21811da177e4SLinus Torvalds snd_pcm_stream_lock_irq(substream); 21821250932eSJaroslav Kysela if (err < 0) 21831250932eSJaroslav Kysela goto _end_unlock; 21841da177e4SLinus Torvalds switch (runtime->status->state) { 21851da177e4SLinus Torvalds case SNDRV_PCM_STATE_XRUN: 21861da177e4SLinus Torvalds err = -EPIPE; 21871da177e4SLinus Torvalds goto _end_unlock; 21881da177e4SLinus Torvalds case SNDRV_PCM_STATE_SUSPENDED: 21891da177e4SLinus Torvalds err = -ESTRPIPE; 21901da177e4SLinus Torvalds goto _end_unlock; 21911da177e4SLinus Torvalds default: 21921da177e4SLinus Torvalds break; 21931da177e4SLinus Torvalds } 21941da177e4SLinus Torvalds appl_ptr += frames; 21951da177e4SLinus Torvalds if (appl_ptr >= runtime->boundary) 21961da177e4SLinus Torvalds appl_ptr -= runtime->boundary; 21971da177e4SLinus Torvalds runtime->control->appl_ptr = appl_ptr; 21981da177e4SLinus Torvalds if (substream->ops->ack) 21991da177e4SLinus Torvalds substream->ops->ack(substream); 22001da177e4SLinus Torvalds 22011da177e4SLinus Torvalds offset += frames; 22021da177e4SLinus Torvalds size -= frames; 22031da177e4SLinus Torvalds xfer += frames; 22041da177e4SLinus Torvalds } 22051da177e4SLinus Torvalds _end_unlock: 2206c91a988dSJaroslav Kysela runtime->twake = 0; 22071250932eSJaroslav Kysela if (xfer > 0 && err >= 0) 22081250932eSJaroslav Kysela snd_pcm_update_state(substream, runtime); 22091da177e4SLinus Torvalds snd_pcm_stream_unlock_irq(substream); 22101da177e4SLinus Torvalds return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; 22111da177e4SLinus Torvalds } 22121da177e4SLinus Torvalds 2213877211f5STakashi Iwai snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, void __user *buf, snd_pcm_uframes_t size) 22141da177e4SLinus Torvalds { 2215877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 22161da177e4SLinus Torvalds int nonblock; 22177eaa943cSTakashi Iwai int err; 22181da177e4SLinus Torvalds 22197eaa943cSTakashi Iwai err = pcm_sanity_check(substream); 22207eaa943cSTakashi Iwai if (err < 0) 22217eaa943cSTakashi Iwai return err; 22221da177e4SLinus Torvalds runtime = substream->runtime; 22230df63e44STakashi Iwai nonblock = !!(substream->f_flags & O_NONBLOCK); 22241da177e4SLinus Torvalds if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED) 22251da177e4SLinus Torvalds return -EINVAL; 22261da177e4SLinus Torvalds return snd_pcm_lib_read1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_read_transfer); 22271da177e4SLinus Torvalds } 22281da177e4SLinus Torvalds 2229e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_lib_read); 2230e88e8ae6STakashi Iwai 2231877211f5STakashi Iwai static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream, 22321da177e4SLinus Torvalds unsigned int hwoff, 22331da177e4SLinus Torvalds unsigned long data, unsigned int off, 22341da177e4SLinus Torvalds snd_pcm_uframes_t frames) 22351da177e4SLinus Torvalds { 2236877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 22371da177e4SLinus Torvalds int err; 22381da177e4SLinus Torvalds void __user **bufs = (void __user **)data; 22391da177e4SLinus Torvalds int channels = runtime->channels; 22401da177e4SLinus Torvalds int c; 22411da177e4SLinus Torvalds if (substream->ops->copy) { 22421da177e4SLinus Torvalds for (c = 0; c < channels; ++c, ++bufs) { 22431da177e4SLinus Torvalds char __user *buf; 22441da177e4SLinus Torvalds if (*bufs == NULL) 22451da177e4SLinus Torvalds continue; 22461da177e4SLinus Torvalds buf = *bufs + samples_to_bytes(runtime, off); 22471da177e4SLinus Torvalds if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0) 22481da177e4SLinus Torvalds return err; 22491da177e4SLinus Torvalds } 22501da177e4SLinus Torvalds } else { 22511da177e4SLinus Torvalds snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels; 22521da177e4SLinus Torvalds for (c = 0; c < channels; ++c, ++bufs) { 22531da177e4SLinus Torvalds char *hwbuf; 22541da177e4SLinus Torvalds char __user *buf; 22551da177e4SLinus Torvalds if (*bufs == NULL) 22561da177e4SLinus Torvalds continue; 22571da177e4SLinus Torvalds 22581da177e4SLinus Torvalds hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff); 22591da177e4SLinus Torvalds buf = *bufs + samples_to_bytes(runtime, off); 22601da177e4SLinus Torvalds if (copy_to_user(buf, hwbuf, samples_to_bytes(runtime, frames))) 22611da177e4SLinus Torvalds return -EFAULT; 22621da177e4SLinus Torvalds } 22631da177e4SLinus Torvalds } 22641da177e4SLinus Torvalds return 0; 22651da177e4SLinus Torvalds } 22661da177e4SLinus Torvalds 2267877211f5STakashi Iwai snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream, 22681da177e4SLinus Torvalds void __user **bufs, 22691da177e4SLinus Torvalds snd_pcm_uframes_t frames) 22701da177e4SLinus Torvalds { 2271877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 22721da177e4SLinus Torvalds int nonblock; 22737eaa943cSTakashi Iwai int err; 22741da177e4SLinus Torvalds 22757eaa943cSTakashi Iwai err = pcm_sanity_check(substream); 22767eaa943cSTakashi Iwai if (err < 0) 22777eaa943cSTakashi Iwai return err; 22781da177e4SLinus Torvalds runtime = substream->runtime; 22791da177e4SLinus Torvalds if (runtime->status->state == SNDRV_PCM_STATE_OPEN) 22801da177e4SLinus Torvalds return -EBADFD; 22811da177e4SLinus Torvalds 22820df63e44STakashi Iwai nonblock = !!(substream->f_flags & O_NONBLOCK); 22831da177e4SLinus Torvalds if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) 22841da177e4SLinus Torvalds return -EINVAL; 22851da177e4SLinus Torvalds return snd_pcm_lib_read1(substream, (unsigned long)bufs, frames, nonblock, snd_pcm_lib_readv_transfer); 22861da177e4SLinus Torvalds } 22871da177e4SLinus Torvalds 22881da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_lib_readv); 2289