11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Digital Audio (PCM) abstract layer 3c1017a4cSJaroslav Kysela * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 41da177e4SLinus Torvalds * Abramo Bagnara <abramo@alsa-project.org> 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 81da177e4SLinus Torvalds * it under the terms of the GNU General Public License as published by 91da177e4SLinus Torvalds * the Free Software Foundation; either version 2 of the License, or 101da177e4SLinus Torvalds * (at your option) any later version. 111da177e4SLinus Torvalds * 121da177e4SLinus Torvalds * This program is distributed in the hope that it will be useful, 131da177e4SLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 141da177e4SLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 151da177e4SLinus Torvalds * GNU General Public License for more details. 161da177e4SLinus Torvalds * 171da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License 181da177e4SLinus Torvalds * along with this program; if not, write to the Free Software 191da177e4SLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 201da177e4SLinus Torvalds * 211da177e4SLinus Torvalds */ 221da177e4SLinus Torvalds 231da177e4SLinus Torvalds #include <linux/slab.h> 241da177e4SLinus Torvalds #include <linux/time.h> 253f7440a6STakashi Iwai #include <linux/math64.h> 261da177e4SLinus Torvalds #include <sound/core.h> 271da177e4SLinus Torvalds #include <sound/control.h> 281da177e4SLinus Torvalds #include <sound/info.h> 291da177e4SLinus Torvalds #include <sound/pcm.h> 301da177e4SLinus Torvalds #include <sound/pcm_params.h> 311da177e4SLinus Torvalds #include <sound/timer.h> 321da177e4SLinus Torvalds 331da177e4SLinus Torvalds /* 341da177e4SLinus Torvalds * fill ring buffer with silence 351da177e4SLinus Torvalds * runtime->silence_start: starting pointer to silence area 361da177e4SLinus Torvalds * runtime->silence_filled: size filled with silence 371da177e4SLinus Torvalds * runtime->silence_threshold: threshold from application 381da177e4SLinus Torvalds * runtime->silence_size: maximal size from application 391da177e4SLinus Torvalds * 401da177e4SLinus Torvalds * when runtime->silence_size >= runtime->boundary - fill processed area with silence immediately 411da177e4SLinus Torvalds */ 42877211f5STakashi Iwai void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_uframes_t new_hw_ptr) 431da177e4SLinus Torvalds { 44877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 451da177e4SLinus Torvalds snd_pcm_uframes_t frames, ofs, transfer; 461da177e4SLinus Torvalds 471da177e4SLinus Torvalds if (runtime->silence_size < runtime->boundary) { 481da177e4SLinus Torvalds snd_pcm_sframes_t noise_dist, n; 491da177e4SLinus Torvalds if (runtime->silence_start != runtime->control->appl_ptr) { 501da177e4SLinus Torvalds n = runtime->control->appl_ptr - runtime->silence_start; 511da177e4SLinus Torvalds if (n < 0) 521da177e4SLinus Torvalds n += runtime->boundary; 531da177e4SLinus Torvalds if ((snd_pcm_uframes_t)n < runtime->silence_filled) 541da177e4SLinus Torvalds runtime->silence_filled -= n; 551da177e4SLinus Torvalds else 561da177e4SLinus Torvalds runtime->silence_filled = 0; 571da177e4SLinus Torvalds runtime->silence_start = runtime->control->appl_ptr; 581da177e4SLinus Torvalds } 59235475cbSTakashi Iwai if (runtime->silence_filled >= runtime->buffer_size) 601da177e4SLinus Torvalds return; 611da177e4SLinus Torvalds noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silence_filled; 621da177e4SLinus Torvalds if (noise_dist >= (snd_pcm_sframes_t) runtime->silence_threshold) 631da177e4SLinus Torvalds return; 641da177e4SLinus Torvalds frames = runtime->silence_threshold - noise_dist; 651da177e4SLinus Torvalds if (frames > runtime->silence_size) 661da177e4SLinus Torvalds frames = runtime->silence_size; 671da177e4SLinus Torvalds } else { 681da177e4SLinus Torvalds if (new_hw_ptr == ULONG_MAX) { /* initialization */ 691da177e4SLinus Torvalds snd_pcm_sframes_t avail = snd_pcm_playback_hw_avail(runtime); 701da177e4SLinus Torvalds runtime->silence_filled = avail > 0 ? avail : 0; 711da177e4SLinus Torvalds runtime->silence_start = (runtime->status->hw_ptr + 721da177e4SLinus Torvalds runtime->silence_filled) % 731da177e4SLinus Torvalds runtime->boundary; 741da177e4SLinus Torvalds } else { 751da177e4SLinus Torvalds ofs = runtime->status->hw_ptr; 761da177e4SLinus Torvalds frames = new_hw_ptr - ofs; 771da177e4SLinus Torvalds if ((snd_pcm_sframes_t)frames < 0) 781da177e4SLinus Torvalds frames += runtime->boundary; 791da177e4SLinus Torvalds runtime->silence_filled -= frames; 801da177e4SLinus Torvalds if ((snd_pcm_sframes_t)runtime->silence_filled < 0) { 811da177e4SLinus Torvalds runtime->silence_filled = 0; 829a826ddbSClemens Ladisch runtime->silence_start = new_hw_ptr; 831da177e4SLinus Torvalds } else { 849a826ddbSClemens Ladisch runtime->silence_start = ofs; 851da177e4SLinus Torvalds } 861da177e4SLinus Torvalds } 871da177e4SLinus Torvalds frames = runtime->buffer_size - runtime->silence_filled; 881da177e4SLinus Torvalds } 897eaa943cSTakashi Iwai if (snd_BUG_ON(frames > runtime->buffer_size)) 907eaa943cSTakashi Iwai return; 911da177e4SLinus Torvalds if (frames == 0) 921da177e4SLinus Torvalds return; 939a826ddbSClemens Ladisch ofs = runtime->silence_start % runtime->buffer_size; 941da177e4SLinus Torvalds while (frames > 0) { 951da177e4SLinus Torvalds transfer = ofs + frames > runtime->buffer_size ? runtime->buffer_size - ofs : frames; 961da177e4SLinus Torvalds if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || 971da177e4SLinus Torvalds runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) { 981da177e4SLinus Torvalds if (substream->ops->silence) { 991da177e4SLinus Torvalds int err; 1001da177e4SLinus Torvalds err = substream->ops->silence(substream, -1, ofs, transfer); 1017eaa943cSTakashi Iwai snd_BUG_ON(err < 0); 1021da177e4SLinus Torvalds } else { 1031da177e4SLinus Torvalds char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, ofs); 1041da177e4SLinus Torvalds snd_pcm_format_set_silence(runtime->format, hwbuf, transfer * runtime->channels); 1051da177e4SLinus Torvalds } 1061da177e4SLinus Torvalds } else { 1071da177e4SLinus Torvalds unsigned int c; 1081da177e4SLinus Torvalds unsigned int channels = runtime->channels; 1091da177e4SLinus Torvalds if (substream->ops->silence) { 1101da177e4SLinus Torvalds for (c = 0; c < channels; ++c) { 1111da177e4SLinus Torvalds int err; 1121da177e4SLinus Torvalds err = substream->ops->silence(substream, c, ofs, transfer); 1137eaa943cSTakashi Iwai snd_BUG_ON(err < 0); 1141da177e4SLinus Torvalds } 1151da177e4SLinus Torvalds } else { 1161da177e4SLinus Torvalds size_t dma_csize = runtime->dma_bytes / channels; 1171da177e4SLinus Torvalds for (c = 0; c < channels; ++c) { 1181da177e4SLinus Torvalds char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, ofs); 1191da177e4SLinus Torvalds snd_pcm_format_set_silence(runtime->format, hwbuf, transfer); 1201da177e4SLinus Torvalds } 1211da177e4SLinus Torvalds } 1221da177e4SLinus Torvalds } 1231da177e4SLinus Torvalds runtime->silence_filled += transfer; 1241da177e4SLinus Torvalds frames -= transfer; 1251da177e4SLinus Torvalds ofs = 0; 1261da177e4SLinus Torvalds } 1271da177e4SLinus Torvalds } 1281da177e4SLinus Torvalds 129c0070110STakashi Iwai static void pcm_debug_name(struct snd_pcm_substream *substream, 130c0070110STakashi Iwai char *name, size_t len) 1311da177e4SLinus Torvalds { 132c0070110STakashi Iwai snprintf(name, len, "pcmC%dD%d%c:%d", 1331da177e4SLinus Torvalds substream->pcm->card->number, 1341da177e4SLinus Torvalds substream->pcm->device, 135c0070110STakashi Iwai substream->stream ? 'c' : 'p', 136c0070110STakashi Iwai substream->number); 137c0070110STakashi Iwai } 138c0070110STakashi Iwai 1394d96eb25SJaroslav Kysela #define XRUN_DEBUG_BASIC (1<<0) 1404d96eb25SJaroslav Kysela #define XRUN_DEBUG_STACK (1<<1) /* dump also stack */ 1414d96eb25SJaroslav Kysela #define XRUN_DEBUG_JIFFIESCHECK (1<<2) /* do jiffies check */ 1424d96eb25SJaroslav Kysela #define XRUN_DEBUG_PERIODUPDATE (1<<3) /* full period update info */ 1434d96eb25SJaroslav Kysela #define XRUN_DEBUG_HWPTRUPDATE (1<<4) /* full hwptr update info */ 1444d96eb25SJaroslav Kysela #define XRUN_DEBUG_LOG (1<<5) /* show last 10 positions on err */ 1454d96eb25SJaroslav Kysela #define XRUN_DEBUG_LOGONCE (1<<6) /* do above only once */ 1464d96eb25SJaroslav Kysela 1474d96eb25SJaroslav Kysela #ifdef CONFIG_SND_PCM_XRUN_DEBUG 1484d96eb25SJaroslav Kysela 1494d96eb25SJaroslav Kysela #define xrun_debug(substream, mask) \ 1504d96eb25SJaroslav Kysela ((substream)->pstr->xrun_debug & (mask)) 1514d96eb25SJaroslav Kysela 1524d96eb25SJaroslav Kysela #define dump_stack_on_xrun(substream) do { \ 1534d96eb25SJaroslav Kysela if (xrun_debug(substream, XRUN_DEBUG_STACK)) \ 1544d96eb25SJaroslav Kysela dump_stack(); \ 1554d96eb25SJaroslav Kysela } while (0) 1564d96eb25SJaroslav Kysela 1571da177e4SLinus Torvalds static void xrun(struct snd_pcm_substream *substream) 1581da177e4SLinus Torvalds { 15913f040f9SJaroslav Kysela struct snd_pcm_runtime *runtime = substream->runtime; 16013f040f9SJaroslav Kysela 16113f040f9SJaroslav Kysela if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) 16213f040f9SJaroslav Kysela snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); 1631da177e4SLinus Torvalds snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); 164741b20cfSJaroslav Kysela if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { 165c0070110STakashi Iwai char name[16]; 166c0070110STakashi Iwai pcm_debug_name(substream, name, sizeof(name)); 167c0070110STakashi Iwai snd_printd(KERN_DEBUG "XRUN: %s\n", name); 168ed3da3d9STakashi Iwai dump_stack_on_xrun(substream); 1691da177e4SLinus Torvalds } 1701da177e4SLinus Torvalds } 1711da177e4SLinus Torvalds 1724d96eb25SJaroslav Kysela #define hw_ptr_error(substream, fmt, args...) \ 1734d96eb25SJaroslav Kysela do { \ 1744d96eb25SJaroslav Kysela if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \ 175f240406bSJaroslav Kysela xrun_log_show(substream); \ 1764d96eb25SJaroslav Kysela if (printk_ratelimit()) { \ 1774d96eb25SJaroslav Kysela snd_printd("PCM: " fmt, ##args); \ 1784d96eb25SJaroslav Kysela } \ 1794d96eb25SJaroslav Kysela dump_stack_on_xrun(substream); \ 1804d96eb25SJaroslav Kysela } \ 1814d96eb25SJaroslav Kysela } while (0) 1824d96eb25SJaroslav Kysela 1834d96eb25SJaroslav Kysela #define XRUN_LOG_CNT 10 1844d96eb25SJaroslav Kysela 1854d96eb25SJaroslav Kysela struct hwptr_log_entry { 1864d96eb25SJaroslav Kysela unsigned long jiffies; 1874d96eb25SJaroslav Kysela snd_pcm_uframes_t pos; 1884d96eb25SJaroslav Kysela snd_pcm_uframes_t period_size; 1894d96eb25SJaroslav Kysela snd_pcm_uframes_t buffer_size; 1904d96eb25SJaroslav Kysela snd_pcm_uframes_t old_hw_ptr; 1914d96eb25SJaroslav Kysela snd_pcm_uframes_t hw_ptr_base; 1924d96eb25SJaroslav Kysela }; 1934d96eb25SJaroslav Kysela 1944d96eb25SJaroslav Kysela struct snd_pcm_hwptr_log { 1954d96eb25SJaroslav Kysela unsigned int idx; 1964d96eb25SJaroslav Kysela unsigned int hit: 1; 1974d96eb25SJaroslav Kysela struct hwptr_log_entry entries[XRUN_LOG_CNT]; 1984d96eb25SJaroslav Kysela }; 1994d96eb25SJaroslav Kysela 2004d96eb25SJaroslav Kysela static void xrun_log(struct snd_pcm_substream *substream, 2014d96eb25SJaroslav Kysela snd_pcm_uframes_t pos) 2024d96eb25SJaroslav Kysela { 2034d96eb25SJaroslav Kysela struct snd_pcm_runtime *runtime = substream->runtime; 2044d96eb25SJaroslav Kysela struct snd_pcm_hwptr_log *log = runtime->hwptr_log; 2054d96eb25SJaroslav Kysela struct hwptr_log_entry *entry; 2064d96eb25SJaroslav Kysela 2074d96eb25SJaroslav Kysela if (log == NULL) { 2084d96eb25SJaroslav Kysela log = kzalloc(sizeof(*log), GFP_ATOMIC); 2094d96eb25SJaroslav Kysela if (log == NULL) 2104d96eb25SJaroslav Kysela return; 2114d96eb25SJaroslav Kysela runtime->hwptr_log = log; 2124d96eb25SJaroslav Kysela } else { 2134d96eb25SJaroslav Kysela if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit) 2144d96eb25SJaroslav Kysela return; 2154d96eb25SJaroslav Kysela } 2164d96eb25SJaroslav Kysela entry = &log->entries[log->idx]; 2174d96eb25SJaroslav Kysela entry->jiffies = jiffies; 2184d96eb25SJaroslav Kysela entry->pos = pos; 2194d96eb25SJaroslav Kysela entry->period_size = runtime->period_size; 2204d96eb25SJaroslav Kysela entry->buffer_size = runtime->buffer_size;; 2214d96eb25SJaroslav Kysela entry->old_hw_ptr = runtime->status->hw_ptr; 2224d96eb25SJaroslav Kysela entry->hw_ptr_base = runtime->hw_ptr_base; 2234d96eb25SJaroslav Kysela log->idx = (log->idx + 1) % XRUN_LOG_CNT; 2244d96eb25SJaroslav Kysela } 2254d96eb25SJaroslav Kysela 2264d96eb25SJaroslav Kysela static void xrun_log_show(struct snd_pcm_substream *substream) 2274d96eb25SJaroslav Kysela { 2284d96eb25SJaroslav Kysela struct snd_pcm_hwptr_log *log = substream->runtime->hwptr_log; 2294d96eb25SJaroslav Kysela struct hwptr_log_entry *entry; 2304d96eb25SJaroslav Kysela char name[16]; 2314d96eb25SJaroslav Kysela unsigned int idx; 2324d96eb25SJaroslav Kysela int cnt; 2334d96eb25SJaroslav Kysela 2344d96eb25SJaroslav Kysela if (log == NULL) 2354d96eb25SJaroslav Kysela return; 2364d96eb25SJaroslav Kysela if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit) 2374d96eb25SJaroslav Kysela return; 2384d96eb25SJaroslav Kysela pcm_debug_name(substream, name, sizeof(name)); 2394d96eb25SJaroslav Kysela for (cnt = 0, idx = log->idx; cnt < XRUN_LOG_CNT; cnt++) { 2404d96eb25SJaroslav Kysela entry = &log->entries[idx]; 2414d96eb25SJaroslav Kysela if (entry->period_size == 0) 2424d96eb25SJaroslav Kysela break; 243f240406bSJaroslav Kysela snd_printd("hwptr log: %s: j=%lu, pos=%ld/%ld/%ld, " 244f240406bSJaroslav Kysela "hwptr=%ld/%ld\n", 2454d96eb25SJaroslav Kysela name, entry->jiffies, (unsigned long)entry->pos, 2464d96eb25SJaroslav Kysela (unsigned long)entry->period_size, 2474d96eb25SJaroslav Kysela (unsigned long)entry->buffer_size, 2484d96eb25SJaroslav Kysela (unsigned long)entry->old_hw_ptr, 249f240406bSJaroslav Kysela (unsigned long)entry->hw_ptr_base); 2504d96eb25SJaroslav Kysela idx++; 2514d96eb25SJaroslav Kysela idx %= XRUN_LOG_CNT; 2524d96eb25SJaroslav Kysela } 2534d96eb25SJaroslav Kysela log->hit = 1; 2544d96eb25SJaroslav Kysela } 2554d96eb25SJaroslav Kysela 2564d96eb25SJaroslav Kysela #else /* ! CONFIG_SND_PCM_XRUN_DEBUG */ 2574d96eb25SJaroslav Kysela 2584d96eb25SJaroslav Kysela #define xrun_debug(substream, mask) 0 2594d96eb25SJaroslav Kysela #define xrun(substream) do { } while (0) 2604d96eb25SJaroslav Kysela #define hw_ptr_error(substream, fmt, args...) do { } while (0) 2614d96eb25SJaroslav Kysela #define xrun_log(substream, pos) do { } while (0) 2624d96eb25SJaroslav Kysela #define xrun_log_show(substream) do { } while (0) 2634d96eb25SJaroslav Kysela 2644d96eb25SJaroslav Kysela #endif 2654d96eb25SJaroslav Kysela 2661250932eSJaroslav Kysela int snd_pcm_update_state(struct snd_pcm_substream *substream, 267877211f5STakashi Iwai struct snd_pcm_runtime *runtime) 2681da177e4SLinus Torvalds { 2691da177e4SLinus Torvalds snd_pcm_uframes_t avail; 2701da177e4SLinus Torvalds 2711da177e4SLinus Torvalds if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 2721da177e4SLinus Torvalds avail = snd_pcm_playback_avail(runtime); 2731da177e4SLinus Torvalds else 2741da177e4SLinus Torvalds avail = snd_pcm_capture_avail(runtime); 2751da177e4SLinus Torvalds if (avail > runtime->avail_max) 2761da177e4SLinus Torvalds runtime->avail_max = avail; 2774cdc115fSTakashi Iwai if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { 2784cdc115fSTakashi Iwai if (avail >= runtime->buffer_size) { 2791da177e4SLinus Torvalds snd_pcm_drain_done(substream); 2804cdc115fSTakashi Iwai return -EPIPE; 2814cdc115fSTakashi Iwai } 2824cdc115fSTakashi Iwai } else { 2834cdc115fSTakashi Iwai if (avail >= runtime->stop_threshold) { 2841da177e4SLinus Torvalds xrun(substream); 2851da177e4SLinus Torvalds return -EPIPE; 2861da177e4SLinus Torvalds } 2874cdc115fSTakashi Iwai } 2881250932eSJaroslav Kysela if (!runtime->nowake && avail >= runtime->control->avail_min) 2891da177e4SLinus Torvalds wake_up(&runtime->sleep); 2901da177e4SLinus Torvalds return 0; 2911da177e4SLinus Torvalds } 2921da177e4SLinus Torvalds 293f240406bSJaroslav Kysela static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, 294f240406bSJaroslav Kysela unsigned int in_interrupt) 2951da177e4SLinus Torvalds { 296877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 2971da177e4SLinus Torvalds snd_pcm_uframes_t pos; 298f240406bSJaroslav Kysela snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base; 299bbf6ad13SJaroslav Kysela snd_pcm_sframes_t hdelta, delta; 300bbf6ad13SJaroslav Kysela unsigned long jdelta; 3011da177e4SLinus Torvalds 302bbf6ad13SJaroslav Kysela old_hw_ptr = runtime->status->hw_ptr; 303f240406bSJaroslav Kysela pos = substream->ops->pointer(substream); 3041da177e4SLinus Torvalds if (pos == SNDRV_PCM_POS_XRUN) { 3051da177e4SLinus Torvalds xrun(substream); 3061da177e4SLinus Torvalds return -EPIPE; 3071da177e4SLinus Torvalds } 308f240406bSJaroslav Kysela if (pos >= runtime->buffer_size) { 309f240406bSJaroslav Kysela if (printk_ratelimit()) { 310cedb8118STakashi Iwai char name[16]; 311cedb8118STakashi Iwai pcm_debug_name(substream, name, sizeof(name)); 312f240406bSJaroslav Kysela xrun_log_show(substream); 313f240406bSJaroslav Kysela snd_printd(KERN_ERR "BUG: %s, pos = %ld, " 314f240406bSJaroslav Kysela "buffer size = %ld, period size = %ld\n", 315f240406bSJaroslav Kysela name, pos, runtime->buffer_size, 316f240406bSJaroslav Kysela runtime->period_size); 317cedb8118STakashi Iwai } 318f240406bSJaroslav Kysela pos = 0; 319f240406bSJaroslav Kysela } 320f240406bSJaroslav Kysela pos -= pos % runtime->min_align; 321f240406bSJaroslav Kysela if (xrun_debug(substream, XRUN_DEBUG_LOG)) 322f240406bSJaroslav Kysela xrun_log(substream, pos); 323ed3da3d9STakashi Iwai hw_base = runtime->hw_ptr_base; 324ed3da3d9STakashi Iwai new_hw_ptr = hw_base + pos; 325f240406bSJaroslav Kysela if (in_interrupt) { 326f240406bSJaroslav Kysela /* we know that one period was processed */ 327f240406bSJaroslav Kysela /* delta = "expected next hw_ptr" for in_interrupt != 0 */ 328f240406bSJaroslav Kysela delta = old_hw_ptr - (old_hw_ptr % runtime->period_size) 329f240406bSJaroslav Kysela + runtime->period_size; 330f240406bSJaroslav Kysela if (delta > new_hw_ptr) { 331f240406bSJaroslav Kysela hw_base += runtime->buffer_size; 332f240406bSJaroslav Kysela if (hw_base >= runtime->boundary) 333f240406bSJaroslav Kysela hw_base = 0; 334f240406bSJaroslav Kysela new_hw_ptr = hw_base + pos; 335f240406bSJaroslav Kysela goto __delta; 336ded652f7STakashi Iwai } 337f240406bSJaroslav Kysela } 338f240406bSJaroslav Kysela /* new_hw_ptr might be lower than old_hw_ptr in case when */ 339f240406bSJaroslav Kysela /* pointer crosses the end of the ring buffer */ 340f240406bSJaroslav Kysela if (new_hw_ptr < old_hw_ptr) { 341ed3da3d9STakashi Iwai hw_base += runtime->buffer_size; 3428b22d943STakashi Iwai if (hw_base >= runtime->boundary) 343ed3da3d9STakashi Iwai hw_base = 0; 344ed3da3d9STakashi Iwai new_hw_ptr = hw_base + pos; 3451da177e4SLinus Torvalds } 346f240406bSJaroslav Kysela __delta: 347f240406bSJaroslav Kysela delta = (new_hw_ptr - old_hw_ptr) % runtime->boundary; 348f240406bSJaroslav Kysela if (xrun_debug(substream, in_interrupt ? 349f240406bSJaroslav Kysela XRUN_DEBUG_PERIODUPDATE : XRUN_DEBUG_HWPTRUPDATE)) { 350f240406bSJaroslav Kysela char name[16]; 351f240406bSJaroslav Kysela pcm_debug_name(substream, name, sizeof(name)); 352f240406bSJaroslav Kysela snd_printd("%s_update: %s: pos=%u/%u/%u, " 353f240406bSJaroslav Kysela "hwptr=%ld/%ld/%ld/%ld\n", 354f240406bSJaroslav Kysela in_interrupt ? "period" : "hwptr", 355f240406bSJaroslav Kysela name, 356f240406bSJaroslav Kysela (unsigned int)pos, 357f240406bSJaroslav Kysela (unsigned int)runtime->period_size, 358f240406bSJaroslav Kysela (unsigned int)runtime->buffer_size, 359f240406bSJaroslav Kysela (unsigned long)delta, 360f240406bSJaroslav Kysela (unsigned long)old_hw_ptr, 361f240406bSJaroslav Kysela (unsigned long)new_hw_ptr, 362f240406bSJaroslav Kysela (unsigned long)runtime->hw_ptr_base); 363f240406bSJaroslav Kysela } 364f240406bSJaroslav Kysela /* something must be really wrong */ 365f240406bSJaroslav Kysela if (delta >= runtime->buffer_size) { 366f240406bSJaroslav Kysela hw_ptr_error(substream, 367f240406bSJaroslav Kysela "Unexpected hw_pointer value %s" 368f240406bSJaroslav Kysela "(stream=%i, pos=%ld, new_hw_ptr=%ld, " 369f240406bSJaroslav Kysela "old_hw_ptr=%ld)\n", 370f240406bSJaroslav Kysela in_interrupt ? "[Q] " : "[P]", 371f240406bSJaroslav Kysela substream->stream, (long)pos, 372f240406bSJaroslav Kysela (long)new_hw_ptr, (long)old_hw_ptr); 373f240406bSJaroslav Kysela return 0; 3741da177e4SLinus Torvalds } 375c87d9732STakashi Iwai 376c87d9732STakashi Iwai /* Do jiffies check only in xrun_debug mode */ 377741b20cfSJaroslav Kysela if (!xrun_debug(substream, XRUN_DEBUG_JIFFIESCHECK)) 378c87d9732STakashi Iwai goto no_jiffies_check; 379c87d9732STakashi Iwai 3803e5b5016STakashi Iwai /* Skip the jiffies check for hardwares with BATCH flag. 3813e5b5016STakashi Iwai * Such hardware usually just increases the position at each IRQ, 3823e5b5016STakashi Iwai * thus it can't give any strange position. 3833e5b5016STakashi Iwai */ 3843e5b5016STakashi Iwai if (runtime->hw.info & SNDRV_PCM_INFO_BATCH) 3853e5b5016STakashi Iwai goto no_jiffies_check; 386f240406bSJaroslav Kysela hdelta = delta; 387a4444da3SJaroslav Kysela if (hdelta < runtime->delay) 388a4444da3SJaroslav Kysela goto no_jiffies_check; 389a4444da3SJaroslav Kysela hdelta -= runtime->delay; 390bbf6ad13SJaroslav Kysela jdelta = jiffies - runtime->hw_ptr_jiffies; 391bbf6ad13SJaroslav Kysela if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) { 392bbf6ad13SJaroslav Kysela delta = jdelta / 393bbf6ad13SJaroslav Kysela (((runtime->period_size * HZ) / runtime->rate) 394bbf6ad13SJaroslav Kysela + HZ/100); 395f240406bSJaroslav Kysela /* move new_hw_ptr according jiffies not pos variable */ 396f240406bSJaroslav Kysela new_hw_ptr = old_hw_ptr; 397f240406bSJaroslav Kysela /* use loop to avoid checks for delta overflows */ 398f240406bSJaroslav Kysela /* the delta value is small or zero in most cases */ 399f240406bSJaroslav Kysela while (delta > 0) { 400f240406bSJaroslav Kysela new_hw_ptr += runtime->period_size; 401f240406bSJaroslav Kysela if (new_hw_ptr >= runtime->boundary) 402f240406bSJaroslav Kysela new_hw_ptr -= runtime->boundary; 403f240406bSJaroslav Kysela delta--; 404f240406bSJaroslav Kysela } 405f240406bSJaroslav Kysela /* align hw_base to buffer_size */ 406f240406bSJaroslav Kysela hw_base = new_hw_ptr - (new_hw_ptr % runtime->buffer_size); 407f240406bSJaroslav Kysela delta = 0; 408bbf6ad13SJaroslav Kysela hw_ptr_error(substream, 409f240406bSJaroslav Kysela "hw_ptr skipping! %s" 410bbf6ad13SJaroslav Kysela "(pos=%ld, delta=%ld, period=%ld, " 411f240406bSJaroslav Kysela "jdelta=%lu/%lu/%lu, hw_ptr=%ld/%ld)\n", 412f240406bSJaroslav Kysela in_interrupt ? "[Q] " : "", 413bbf6ad13SJaroslav Kysela (long)pos, (long)hdelta, 414bbf6ad13SJaroslav Kysela (long)runtime->period_size, jdelta, 415f240406bSJaroslav Kysela ((hdelta * HZ) / runtime->rate), delta, 416f240406bSJaroslav Kysela (unsigned long)old_hw_ptr, 417f240406bSJaroslav Kysela (unsigned long)new_hw_ptr); 418bbf6ad13SJaroslav Kysela } 4193e5b5016STakashi Iwai no_jiffies_check: 420bbf6ad13SJaroslav Kysela if (delta > runtime->period_size + runtime->period_size / 2) { 421ed3da3d9STakashi Iwai hw_ptr_error(substream, 422f240406bSJaroslav Kysela "Lost interrupts? %s" 423f240406bSJaroslav Kysela "(stream=%i, delta=%ld, new_hw_ptr=%ld, " 424f240406bSJaroslav Kysela "old_hw_ptr=%ld)\n", 425f240406bSJaroslav Kysela in_interrupt ? "[Q] " : "", 426ed3da3d9STakashi Iwai substream->stream, (long)delta, 427f240406bSJaroslav Kysela (long)new_hw_ptr, 428f240406bSJaroslav Kysela (long)old_hw_ptr); 4291da177e4SLinus Torvalds } 430f240406bSJaroslav Kysela 431f240406bSJaroslav Kysela if (runtime->status->hw_ptr == new_hw_ptr) 432f240406bSJaroslav Kysela return 0; 433ab1863fcSTakashi Iwai 4341da177e4SLinus Torvalds if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && 4351da177e4SLinus Torvalds runtime->silence_size > 0) 4361da177e4SLinus Torvalds snd_pcm_playback_silence(substream, new_hw_ptr); 4371da177e4SLinus Torvalds 438ed3da3d9STakashi Iwai runtime->hw_ptr_base = hw_base; 4391da177e4SLinus Torvalds runtime->status->hw_ptr = new_hw_ptr; 440bbf6ad13SJaroslav Kysela runtime->hw_ptr_jiffies = jiffies; 44113f040f9SJaroslav Kysela if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) 44213f040f9SJaroslav Kysela snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); 4431da177e4SLinus Torvalds 4441250932eSJaroslav Kysela return snd_pcm_update_state(substream, runtime); 4451da177e4SLinus Torvalds } 4461da177e4SLinus Torvalds 4471da177e4SLinus Torvalds /* CAUTION: call it with irq disabled */ 448877211f5STakashi Iwai int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) 4491da177e4SLinus Torvalds { 450f240406bSJaroslav Kysela return snd_pcm_update_hw_ptr0(substream, 0); 4511da177e4SLinus Torvalds } 4521da177e4SLinus Torvalds 4531da177e4SLinus Torvalds /** 4541da177e4SLinus Torvalds * snd_pcm_set_ops - set the PCM operators 4551da177e4SLinus Torvalds * @pcm: the pcm instance 4561da177e4SLinus Torvalds * @direction: stream direction, SNDRV_PCM_STREAM_XXX 4571da177e4SLinus Torvalds * @ops: the operator table 4581da177e4SLinus Torvalds * 4591da177e4SLinus Torvalds * Sets the given PCM operators to the pcm instance. 4601da177e4SLinus Torvalds */ 461877211f5STakashi Iwai void snd_pcm_set_ops(struct snd_pcm *pcm, int direction, struct snd_pcm_ops *ops) 4621da177e4SLinus Torvalds { 463877211f5STakashi Iwai struct snd_pcm_str *stream = &pcm->streams[direction]; 464877211f5STakashi Iwai struct snd_pcm_substream *substream; 4651da177e4SLinus Torvalds 4661da177e4SLinus Torvalds for (substream = stream->substream; substream != NULL; substream = substream->next) 4671da177e4SLinus Torvalds substream->ops = ops; 4681da177e4SLinus Torvalds } 4691da177e4SLinus Torvalds 470e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_set_ops); 4711da177e4SLinus Torvalds 4721da177e4SLinus Torvalds /** 4731da177e4SLinus Torvalds * snd_pcm_sync - set the PCM sync id 4741da177e4SLinus Torvalds * @substream: the pcm substream 4751da177e4SLinus Torvalds * 4761da177e4SLinus Torvalds * Sets the PCM sync identifier for the card. 4771da177e4SLinus Torvalds */ 478877211f5STakashi Iwai void snd_pcm_set_sync(struct snd_pcm_substream *substream) 4791da177e4SLinus Torvalds { 480877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 4811da177e4SLinus Torvalds 4821da177e4SLinus Torvalds runtime->sync.id32[0] = substream->pcm->card->number; 4831da177e4SLinus Torvalds runtime->sync.id32[1] = -1; 4841da177e4SLinus Torvalds runtime->sync.id32[2] = -1; 4851da177e4SLinus Torvalds runtime->sync.id32[3] = -1; 4861da177e4SLinus Torvalds } 4871da177e4SLinus Torvalds 488e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_set_sync); 489e88e8ae6STakashi Iwai 4901da177e4SLinus Torvalds /* 4911da177e4SLinus Torvalds * Standard ioctl routine 4921da177e4SLinus Torvalds */ 4931da177e4SLinus Torvalds 4941da177e4SLinus Torvalds static inline unsigned int div32(unsigned int a, unsigned int b, 4951da177e4SLinus Torvalds unsigned int *r) 4961da177e4SLinus Torvalds { 4971da177e4SLinus Torvalds if (b == 0) { 4981da177e4SLinus Torvalds *r = 0; 4991da177e4SLinus Torvalds return UINT_MAX; 5001da177e4SLinus Torvalds } 5011da177e4SLinus Torvalds *r = a % b; 5021da177e4SLinus Torvalds return a / b; 5031da177e4SLinus Torvalds } 5041da177e4SLinus Torvalds 5051da177e4SLinus Torvalds static inline unsigned int div_down(unsigned int a, unsigned int b) 5061da177e4SLinus Torvalds { 5071da177e4SLinus Torvalds if (b == 0) 5081da177e4SLinus Torvalds return UINT_MAX; 5091da177e4SLinus Torvalds return a / b; 5101da177e4SLinus Torvalds } 5111da177e4SLinus Torvalds 5121da177e4SLinus Torvalds static inline unsigned int div_up(unsigned int a, unsigned int b) 5131da177e4SLinus Torvalds { 5141da177e4SLinus Torvalds unsigned int r; 5151da177e4SLinus Torvalds unsigned int q; 5161da177e4SLinus Torvalds if (b == 0) 5171da177e4SLinus Torvalds return UINT_MAX; 5181da177e4SLinus Torvalds q = div32(a, b, &r); 5191da177e4SLinus Torvalds if (r) 5201da177e4SLinus Torvalds ++q; 5211da177e4SLinus Torvalds return q; 5221da177e4SLinus Torvalds } 5231da177e4SLinus Torvalds 5241da177e4SLinus Torvalds static inline unsigned int mul(unsigned int a, unsigned int b) 5251da177e4SLinus Torvalds { 5261da177e4SLinus Torvalds if (a == 0) 5271da177e4SLinus Torvalds return 0; 5281da177e4SLinus Torvalds if (div_down(UINT_MAX, a) < b) 5291da177e4SLinus Torvalds return UINT_MAX; 5301da177e4SLinus Torvalds return a * b; 5311da177e4SLinus Torvalds } 5321da177e4SLinus Torvalds 5331da177e4SLinus Torvalds static inline unsigned int muldiv32(unsigned int a, unsigned int b, 5341da177e4SLinus Torvalds unsigned int c, unsigned int *r) 5351da177e4SLinus Torvalds { 5361da177e4SLinus Torvalds u_int64_t n = (u_int64_t) a * b; 5371da177e4SLinus Torvalds if (c == 0) { 5387eaa943cSTakashi Iwai snd_BUG_ON(!n); 5391da177e4SLinus Torvalds *r = 0; 5401da177e4SLinus Torvalds return UINT_MAX; 5411da177e4SLinus Torvalds } 5423f7440a6STakashi Iwai n = div_u64_rem(n, c, r); 5431da177e4SLinus Torvalds if (n >= UINT_MAX) { 5441da177e4SLinus Torvalds *r = 0; 5451da177e4SLinus Torvalds return UINT_MAX; 5461da177e4SLinus Torvalds } 5471da177e4SLinus Torvalds return n; 5481da177e4SLinus Torvalds } 5491da177e4SLinus Torvalds 5501da177e4SLinus Torvalds /** 5511da177e4SLinus Torvalds * snd_interval_refine - refine the interval value of configurator 5521da177e4SLinus Torvalds * @i: the interval value to refine 5531da177e4SLinus Torvalds * @v: the interval value to refer to 5541da177e4SLinus Torvalds * 5551da177e4SLinus Torvalds * Refines the interval value with the reference value. 5561da177e4SLinus Torvalds * The interval is changed to the range satisfying both intervals. 5571da177e4SLinus Torvalds * The interval status (min, max, integer, etc.) are evaluated. 5581da177e4SLinus Torvalds * 5591da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 5601da177e4SLinus Torvalds */ 561877211f5STakashi Iwai int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v) 5621da177e4SLinus Torvalds { 5631da177e4SLinus Torvalds int changed = 0; 5647eaa943cSTakashi Iwai if (snd_BUG_ON(snd_interval_empty(i))) 5657eaa943cSTakashi Iwai return -EINVAL; 5661da177e4SLinus Torvalds if (i->min < v->min) { 5671da177e4SLinus Torvalds i->min = v->min; 5681da177e4SLinus Torvalds i->openmin = v->openmin; 5691da177e4SLinus Torvalds changed = 1; 5701da177e4SLinus Torvalds } else if (i->min == v->min && !i->openmin && v->openmin) { 5711da177e4SLinus Torvalds i->openmin = 1; 5721da177e4SLinus Torvalds changed = 1; 5731da177e4SLinus Torvalds } 5741da177e4SLinus Torvalds if (i->max > v->max) { 5751da177e4SLinus Torvalds i->max = v->max; 5761da177e4SLinus Torvalds i->openmax = v->openmax; 5771da177e4SLinus Torvalds changed = 1; 5781da177e4SLinus Torvalds } else if (i->max == v->max && !i->openmax && v->openmax) { 5791da177e4SLinus Torvalds i->openmax = 1; 5801da177e4SLinus Torvalds changed = 1; 5811da177e4SLinus Torvalds } 5821da177e4SLinus Torvalds if (!i->integer && v->integer) { 5831da177e4SLinus Torvalds i->integer = 1; 5841da177e4SLinus Torvalds changed = 1; 5851da177e4SLinus Torvalds } 5861da177e4SLinus Torvalds if (i->integer) { 5871da177e4SLinus Torvalds if (i->openmin) { 5881da177e4SLinus Torvalds i->min++; 5891da177e4SLinus Torvalds i->openmin = 0; 5901da177e4SLinus Torvalds } 5911da177e4SLinus Torvalds if (i->openmax) { 5921da177e4SLinus Torvalds i->max--; 5931da177e4SLinus Torvalds i->openmax = 0; 5941da177e4SLinus Torvalds } 5951da177e4SLinus Torvalds } else if (!i->openmin && !i->openmax && i->min == i->max) 5961da177e4SLinus Torvalds i->integer = 1; 5971da177e4SLinus Torvalds if (snd_interval_checkempty(i)) { 5981da177e4SLinus Torvalds snd_interval_none(i); 5991da177e4SLinus Torvalds return -EINVAL; 6001da177e4SLinus Torvalds } 6011da177e4SLinus Torvalds return changed; 6021da177e4SLinus Torvalds } 6031da177e4SLinus Torvalds 604e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_interval_refine); 605e88e8ae6STakashi Iwai 606877211f5STakashi Iwai static int snd_interval_refine_first(struct snd_interval *i) 6071da177e4SLinus Torvalds { 6087eaa943cSTakashi Iwai if (snd_BUG_ON(snd_interval_empty(i))) 6097eaa943cSTakashi Iwai return -EINVAL; 6101da177e4SLinus Torvalds if (snd_interval_single(i)) 6111da177e4SLinus Torvalds return 0; 6121da177e4SLinus Torvalds i->max = i->min; 6131da177e4SLinus Torvalds i->openmax = i->openmin; 6141da177e4SLinus Torvalds if (i->openmax) 6151da177e4SLinus Torvalds i->max++; 6161da177e4SLinus Torvalds return 1; 6171da177e4SLinus Torvalds } 6181da177e4SLinus Torvalds 619877211f5STakashi Iwai static int snd_interval_refine_last(struct snd_interval *i) 6201da177e4SLinus Torvalds { 6217eaa943cSTakashi Iwai if (snd_BUG_ON(snd_interval_empty(i))) 6227eaa943cSTakashi Iwai return -EINVAL; 6231da177e4SLinus Torvalds if (snd_interval_single(i)) 6241da177e4SLinus Torvalds return 0; 6251da177e4SLinus Torvalds i->min = i->max; 6261da177e4SLinus Torvalds i->openmin = i->openmax; 6271da177e4SLinus Torvalds if (i->openmin) 6281da177e4SLinus Torvalds i->min--; 6291da177e4SLinus Torvalds return 1; 6301da177e4SLinus Torvalds } 6311da177e4SLinus Torvalds 632877211f5STakashi Iwai void snd_interval_mul(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c) 6331da177e4SLinus Torvalds { 6341da177e4SLinus Torvalds if (a->empty || b->empty) { 6351da177e4SLinus Torvalds snd_interval_none(c); 6361da177e4SLinus Torvalds return; 6371da177e4SLinus Torvalds } 6381da177e4SLinus Torvalds c->empty = 0; 6391da177e4SLinus Torvalds c->min = mul(a->min, b->min); 6401da177e4SLinus Torvalds c->openmin = (a->openmin || b->openmin); 6411da177e4SLinus Torvalds c->max = mul(a->max, b->max); 6421da177e4SLinus Torvalds c->openmax = (a->openmax || b->openmax); 6431da177e4SLinus Torvalds c->integer = (a->integer && b->integer); 6441da177e4SLinus Torvalds } 6451da177e4SLinus Torvalds 6461da177e4SLinus Torvalds /** 6471da177e4SLinus Torvalds * snd_interval_div - refine the interval value with division 648df8db936STakashi Iwai * @a: dividend 649df8db936STakashi Iwai * @b: divisor 650df8db936STakashi Iwai * @c: quotient 6511da177e4SLinus Torvalds * 6521da177e4SLinus Torvalds * c = a / b 6531da177e4SLinus Torvalds * 6541da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 6551da177e4SLinus Torvalds */ 656877211f5STakashi Iwai void snd_interval_div(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c) 6571da177e4SLinus Torvalds { 6581da177e4SLinus Torvalds unsigned int r; 6591da177e4SLinus Torvalds if (a->empty || b->empty) { 6601da177e4SLinus Torvalds snd_interval_none(c); 6611da177e4SLinus Torvalds return; 6621da177e4SLinus Torvalds } 6631da177e4SLinus Torvalds c->empty = 0; 6641da177e4SLinus Torvalds c->min = div32(a->min, b->max, &r); 6651da177e4SLinus Torvalds c->openmin = (r || a->openmin || b->openmax); 6661da177e4SLinus Torvalds if (b->min > 0) { 6671da177e4SLinus Torvalds c->max = div32(a->max, b->min, &r); 6681da177e4SLinus Torvalds if (r) { 6691da177e4SLinus Torvalds c->max++; 6701da177e4SLinus Torvalds c->openmax = 1; 6711da177e4SLinus Torvalds } else 6721da177e4SLinus Torvalds c->openmax = (a->openmax || b->openmin); 6731da177e4SLinus Torvalds } else { 6741da177e4SLinus Torvalds c->max = UINT_MAX; 6751da177e4SLinus Torvalds c->openmax = 0; 6761da177e4SLinus Torvalds } 6771da177e4SLinus Torvalds c->integer = 0; 6781da177e4SLinus Torvalds } 6791da177e4SLinus Torvalds 6801da177e4SLinus Torvalds /** 6811da177e4SLinus Torvalds * snd_interval_muldivk - refine the interval value 682df8db936STakashi Iwai * @a: dividend 1 683df8db936STakashi Iwai * @b: dividend 2 684df8db936STakashi Iwai * @k: divisor (as integer) 685df8db936STakashi Iwai * @c: result 6861da177e4SLinus Torvalds * 6871da177e4SLinus Torvalds * c = a * b / k 6881da177e4SLinus Torvalds * 6891da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 6901da177e4SLinus Torvalds */ 691877211f5STakashi Iwai void snd_interval_muldivk(const struct snd_interval *a, const struct snd_interval *b, 692877211f5STakashi Iwai unsigned int k, struct snd_interval *c) 6931da177e4SLinus Torvalds { 6941da177e4SLinus Torvalds unsigned int r; 6951da177e4SLinus Torvalds if (a->empty || b->empty) { 6961da177e4SLinus Torvalds snd_interval_none(c); 6971da177e4SLinus Torvalds return; 6981da177e4SLinus Torvalds } 6991da177e4SLinus Torvalds c->empty = 0; 7001da177e4SLinus Torvalds c->min = muldiv32(a->min, b->min, k, &r); 7011da177e4SLinus Torvalds c->openmin = (r || a->openmin || b->openmin); 7021da177e4SLinus Torvalds c->max = muldiv32(a->max, b->max, k, &r); 7031da177e4SLinus Torvalds if (r) { 7041da177e4SLinus Torvalds c->max++; 7051da177e4SLinus Torvalds c->openmax = 1; 7061da177e4SLinus Torvalds } else 7071da177e4SLinus Torvalds c->openmax = (a->openmax || b->openmax); 7081da177e4SLinus Torvalds c->integer = 0; 7091da177e4SLinus Torvalds } 7101da177e4SLinus Torvalds 7111da177e4SLinus Torvalds /** 7121da177e4SLinus Torvalds * snd_interval_mulkdiv - refine the interval value 713df8db936STakashi Iwai * @a: dividend 1 714df8db936STakashi Iwai * @k: dividend 2 (as integer) 715df8db936STakashi Iwai * @b: divisor 716df8db936STakashi Iwai * @c: result 7171da177e4SLinus Torvalds * 7181da177e4SLinus Torvalds * c = a * k / b 7191da177e4SLinus Torvalds * 7201da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 7211da177e4SLinus Torvalds */ 722877211f5STakashi Iwai void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k, 723877211f5STakashi Iwai const struct snd_interval *b, struct snd_interval *c) 7241da177e4SLinus Torvalds { 7251da177e4SLinus Torvalds unsigned int r; 7261da177e4SLinus Torvalds if (a->empty || b->empty) { 7271da177e4SLinus Torvalds snd_interval_none(c); 7281da177e4SLinus Torvalds return; 7291da177e4SLinus Torvalds } 7301da177e4SLinus Torvalds c->empty = 0; 7311da177e4SLinus Torvalds c->min = muldiv32(a->min, k, b->max, &r); 7321da177e4SLinus Torvalds c->openmin = (r || a->openmin || b->openmax); 7331da177e4SLinus Torvalds if (b->min > 0) { 7341da177e4SLinus Torvalds c->max = muldiv32(a->max, k, b->min, &r); 7351da177e4SLinus Torvalds if (r) { 7361da177e4SLinus Torvalds c->max++; 7371da177e4SLinus Torvalds c->openmax = 1; 7381da177e4SLinus Torvalds } else 7391da177e4SLinus Torvalds c->openmax = (a->openmax || b->openmin); 7401da177e4SLinus Torvalds } else { 7411da177e4SLinus Torvalds c->max = UINT_MAX; 7421da177e4SLinus Torvalds c->openmax = 0; 7431da177e4SLinus Torvalds } 7441da177e4SLinus Torvalds c->integer = 0; 7451da177e4SLinus Torvalds } 7461da177e4SLinus Torvalds 7471da177e4SLinus Torvalds /* ---- */ 7481da177e4SLinus Torvalds 7491da177e4SLinus Torvalds 7501da177e4SLinus Torvalds /** 7511da177e4SLinus Torvalds * snd_interval_ratnum - refine the interval value 752df8db936STakashi Iwai * @i: interval to refine 753df8db936STakashi Iwai * @rats_count: number of ratnum_t 754df8db936STakashi Iwai * @rats: ratnum_t array 755df8db936STakashi Iwai * @nump: pointer to store the resultant numerator 756df8db936STakashi Iwai * @denp: pointer to store the resultant denominator 7571da177e4SLinus Torvalds * 7581da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 7591da177e4SLinus Torvalds */ 760877211f5STakashi Iwai int snd_interval_ratnum(struct snd_interval *i, 761877211f5STakashi Iwai unsigned int rats_count, struct snd_ratnum *rats, 7621da177e4SLinus Torvalds unsigned int *nump, unsigned int *denp) 7631da177e4SLinus Torvalds { 7641da177e4SLinus Torvalds unsigned int best_num, best_diff, best_den; 7651da177e4SLinus Torvalds unsigned int k; 766877211f5STakashi Iwai struct snd_interval t; 7671da177e4SLinus Torvalds int err; 7681da177e4SLinus Torvalds 7691da177e4SLinus Torvalds best_num = best_den = best_diff = 0; 7701da177e4SLinus Torvalds for (k = 0; k < rats_count; ++k) { 7711da177e4SLinus Torvalds unsigned int num = rats[k].num; 7721da177e4SLinus Torvalds unsigned int den; 7731da177e4SLinus Torvalds unsigned int q = i->min; 7741da177e4SLinus Torvalds int diff; 7751da177e4SLinus Torvalds if (q == 0) 7761da177e4SLinus Torvalds q = 1; 7771da177e4SLinus Torvalds den = div_down(num, q); 7781da177e4SLinus Torvalds if (den < rats[k].den_min) 7791da177e4SLinus Torvalds continue; 7801da177e4SLinus Torvalds if (den > rats[k].den_max) 7811da177e4SLinus Torvalds den = rats[k].den_max; 7821da177e4SLinus Torvalds else { 7831da177e4SLinus Torvalds unsigned int r; 7841da177e4SLinus Torvalds r = (den - rats[k].den_min) % rats[k].den_step; 7851da177e4SLinus Torvalds if (r != 0) 7861da177e4SLinus Torvalds den -= r; 7871da177e4SLinus Torvalds } 7881da177e4SLinus Torvalds diff = num - q * den; 7891da177e4SLinus Torvalds if (best_num == 0 || 7901da177e4SLinus Torvalds diff * best_den < best_diff * den) { 7911da177e4SLinus Torvalds best_diff = diff; 7921da177e4SLinus Torvalds best_den = den; 7931da177e4SLinus Torvalds best_num = num; 7941da177e4SLinus Torvalds } 7951da177e4SLinus Torvalds } 7961da177e4SLinus Torvalds if (best_den == 0) { 7971da177e4SLinus Torvalds i->empty = 1; 7981da177e4SLinus Torvalds return -EINVAL; 7991da177e4SLinus Torvalds } 8001da177e4SLinus Torvalds t.min = div_down(best_num, best_den); 8011da177e4SLinus Torvalds t.openmin = !!(best_num % best_den); 8021da177e4SLinus Torvalds 8031da177e4SLinus Torvalds best_num = best_den = best_diff = 0; 8041da177e4SLinus Torvalds for (k = 0; k < rats_count; ++k) { 8051da177e4SLinus Torvalds unsigned int num = rats[k].num; 8061da177e4SLinus Torvalds unsigned int den; 8071da177e4SLinus Torvalds unsigned int q = i->max; 8081da177e4SLinus Torvalds int diff; 8091da177e4SLinus Torvalds if (q == 0) { 8101da177e4SLinus Torvalds i->empty = 1; 8111da177e4SLinus Torvalds return -EINVAL; 8121da177e4SLinus Torvalds } 8131da177e4SLinus Torvalds den = div_up(num, q); 8141da177e4SLinus Torvalds if (den > rats[k].den_max) 8151da177e4SLinus Torvalds continue; 8161da177e4SLinus Torvalds if (den < rats[k].den_min) 8171da177e4SLinus Torvalds den = rats[k].den_min; 8181da177e4SLinus Torvalds else { 8191da177e4SLinus Torvalds unsigned int r; 8201da177e4SLinus Torvalds r = (den - rats[k].den_min) % rats[k].den_step; 8211da177e4SLinus Torvalds if (r != 0) 8221da177e4SLinus Torvalds den += rats[k].den_step - r; 8231da177e4SLinus Torvalds } 8241da177e4SLinus Torvalds diff = q * den - num; 8251da177e4SLinus Torvalds if (best_num == 0 || 8261da177e4SLinus Torvalds diff * best_den < best_diff * den) { 8271da177e4SLinus Torvalds best_diff = diff; 8281da177e4SLinus Torvalds best_den = den; 8291da177e4SLinus Torvalds best_num = num; 8301da177e4SLinus Torvalds } 8311da177e4SLinus Torvalds } 8321da177e4SLinus Torvalds if (best_den == 0) { 8331da177e4SLinus Torvalds i->empty = 1; 8341da177e4SLinus Torvalds return -EINVAL; 8351da177e4SLinus Torvalds } 8361da177e4SLinus Torvalds t.max = div_up(best_num, best_den); 8371da177e4SLinus Torvalds t.openmax = !!(best_num % best_den); 8381da177e4SLinus Torvalds t.integer = 0; 8391da177e4SLinus Torvalds err = snd_interval_refine(i, &t); 8401da177e4SLinus Torvalds if (err < 0) 8411da177e4SLinus Torvalds return err; 8421da177e4SLinus Torvalds 8431da177e4SLinus Torvalds if (snd_interval_single(i)) { 8441da177e4SLinus Torvalds if (nump) 8451da177e4SLinus Torvalds *nump = best_num; 8461da177e4SLinus Torvalds if (denp) 8471da177e4SLinus Torvalds *denp = best_den; 8481da177e4SLinus Torvalds } 8491da177e4SLinus Torvalds return err; 8501da177e4SLinus Torvalds } 8511da177e4SLinus Torvalds 852e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_interval_ratnum); 853e88e8ae6STakashi Iwai 8541da177e4SLinus Torvalds /** 8551da177e4SLinus Torvalds * snd_interval_ratden - refine the interval value 856df8db936STakashi Iwai * @i: interval to refine 857877211f5STakashi Iwai * @rats_count: number of struct ratden 858877211f5STakashi Iwai * @rats: struct ratden array 859df8db936STakashi Iwai * @nump: pointer to store the resultant numerator 860df8db936STakashi Iwai * @denp: pointer to store the resultant denominator 8611da177e4SLinus Torvalds * 8621da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 8631da177e4SLinus Torvalds */ 864877211f5STakashi Iwai static int snd_interval_ratden(struct snd_interval *i, 865877211f5STakashi Iwai unsigned int rats_count, struct snd_ratden *rats, 8661da177e4SLinus Torvalds unsigned int *nump, unsigned int *denp) 8671da177e4SLinus Torvalds { 8681da177e4SLinus Torvalds unsigned int best_num, best_diff, best_den; 8691da177e4SLinus Torvalds unsigned int k; 870877211f5STakashi Iwai struct snd_interval t; 8711da177e4SLinus Torvalds int err; 8721da177e4SLinus Torvalds 8731da177e4SLinus Torvalds best_num = best_den = best_diff = 0; 8741da177e4SLinus Torvalds for (k = 0; k < rats_count; ++k) { 8751da177e4SLinus Torvalds unsigned int num; 8761da177e4SLinus Torvalds unsigned int den = rats[k].den; 8771da177e4SLinus Torvalds unsigned int q = i->min; 8781da177e4SLinus Torvalds int diff; 8791da177e4SLinus Torvalds num = mul(q, den); 8801da177e4SLinus Torvalds if (num > rats[k].num_max) 8811da177e4SLinus Torvalds continue; 8821da177e4SLinus Torvalds if (num < rats[k].num_min) 8831da177e4SLinus Torvalds num = rats[k].num_max; 8841da177e4SLinus Torvalds else { 8851da177e4SLinus Torvalds unsigned int r; 8861da177e4SLinus Torvalds r = (num - rats[k].num_min) % rats[k].num_step; 8871da177e4SLinus Torvalds if (r != 0) 8881da177e4SLinus Torvalds num += rats[k].num_step - r; 8891da177e4SLinus Torvalds } 8901da177e4SLinus Torvalds diff = num - q * den; 8911da177e4SLinus Torvalds if (best_num == 0 || 8921da177e4SLinus Torvalds diff * best_den < best_diff * den) { 8931da177e4SLinus Torvalds best_diff = diff; 8941da177e4SLinus Torvalds best_den = den; 8951da177e4SLinus Torvalds best_num = num; 8961da177e4SLinus Torvalds } 8971da177e4SLinus Torvalds } 8981da177e4SLinus Torvalds if (best_den == 0) { 8991da177e4SLinus Torvalds i->empty = 1; 9001da177e4SLinus Torvalds return -EINVAL; 9011da177e4SLinus Torvalds } 9021da177e4SLinus Torvalds t.min = div_down(best_num, best_den); 9031da177e4SLinus Torvalds t.openmin = !!(best_num % best_den); 9041da177e4SLinus Torvalds 9051da177e4SLinus Torvalds best_num = best_den = best_diff = 0; 9061da177e4SLinus Torvalds for (k = 0; k < rats_count; ++k) { 9071da177e4SLinus Torvalds unsigned int num; 9081da177e4SLinus Torvalds unsigned int den = rats[k].den; 9091da177e4SLinus Torvalds unsigned int q = i->max; 9101da177e4SLinus Torvalds int diff; 9111da177e4SLinus Torvalds num = mul(q, den); 9121da177e4SLinus Torvalds if (num < rats[k].num_min) 9131da177e4SLinus Torvalds continue; 9141da177e4SLinus Torvalds if (num > rats[k].num_max) 9151da177e4SLinus Torvalds num = rats[k].num_max; 9161da177e4SLinus Torvalds else { 9171da177e4SLinus Torvalds unsigned int r; 9181da177e4SLinus Torvalds r = (num - rats[k].num_min) % rats[k].num_step; 9191da177e4SLinus Torvalds if (r != 0) 9201da177e4SLinus Torvalds num -= r; 9211da177e4SLinus Torvalds } 9221da177e4SLinus Torvalds diff = q * den - num; 9231da177e4SLinus Torvalds if (best_num == 0 || 9241da177e4SLinus Torvalds diff * best_den < best_diff * den) { 9251da177e4SLinus Torvalds best_diff = diff; 9261da177e4SLinus Torvalds best_den = den; 9271da177e4SLinus Torvalds best_num = num; 9281da177e4SLinus Torvalds } 9291da177e4SLinus Torvalds } 9301da177e4SLinus Torvalds if (best_den == 0) { 9311da177e4SLinus Torvalds i->empty = 1; 9321da177e4SLinus Torvalds return -EINVAL; 9331da177e4SLinus Torvalds } 9341da177e4SLinus Torvalds t.max = div_up(best_num, best_den); 9351da177e4SLinus Torvalds t.openmax = !!(best_num % best_den); 9361da177e4SLinus Torvalds t.integer = 0; 9371da177e4SLinus Torvalds err = snd_interval_refine(i, &t); 9381da177e4SLinus Torvalds if (err < 0) 9391da177e4SLinus Torvalds return err; 9401da177e4SLinus Torvalds 9411da177e4SLinus Torvalds if (snd_interval_single(i)) { 9421da177e4SLinus Torvalds if (nump) 9431da177e4SLinus Torvalds *nump = best_num; 9441da177e4SLinus Torvalds if (denp) 9451da177e4SLinus Torvalds *denp = best_den; 9461da177e4SLinus Torvalds } 9471da177e4SLinus Torvalds return err; 9481da177e4SLinus Torvalds } 9491da177e4SLinus Torvalds 9501da177e4SLinus Torvalds /** 9511da177e4SLinus Torvalds * snd_interval_list - refine the interval value from the list 9521da177e4SLinus Torvalds * @i: the interval value to refine 9531da177e4SLinus Torvalds * @count: the number of elements in the list 9541da177e4SLinus Torvalds * @list: the value list 9551da177e4SLinus Torvalds * @mask: the bit-mask to evaluate 9561da177e4SLinus Torvalds * 9571da177e4SLinus Torvalds * Refines the interval value from the list. 9581da177e4SLinus Torvalds * When mask is non-zero, only the elements corresponding to bit 1 are 9591da177e4SLinus Torvalds * evaluated. 9601da177e4SLinus Torvalds * 9611da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 9621da177e4SLinus Torvalds */ 963877211f5STakashi Iwai int snd_interval_list(struct snd_interval *i, unsigned int count, unsigned int *list, unsigned int mask) 9641da177e4SLinus Torvalds { 9651da177e4SLinus Torvalds unsigned int k; 966b1ddaf68SClemens Ladisch struct snd_interval list_range; 9670981a260STakashi Iwai 9680981a260STakashi Iwai if (!count) { 9690981a260STakashi Iwai i->empty = 1; 9700981a260STakashi Iwai return -EINVAL; 9710981a260STakashi Iwai } 972b1ddaf68SClemens Ladisch snd_interval_any(&list_range); 973b1ddaf68SClemens Ladisch list_range.min = UINT_MAX; 974b1ddaf68SClemens Ladisch list_range.max = 0; 9751da177e4SLinus Torvalds for (k = 0; k < count; k++) { 9761da177e4SLinus Torvalds if (mask && !(mask & (1 << k))) 9771da177e4SLinus Torvalds continue; 978b1ddaf68SClemens Ladisch if (!snd_interval_test(i, list[k])) 9791da177e4SLinus Torvalds continue; 980b1ddaf68SClemens Ladisch list_range.min = min(list_range.min, list[k]); 981b1ddaf68SClemens Ladisch list_range.max = max(list_range.max, list[k]); 9821da177e4SLinus Torvalds } 983b1ddaf68SClemens Ladisch return snd_interval_refine(i, &list_range); 9841da177e4SLinus Torvalds } 9851da177e4SLinus Torvalds 986e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_interval_list); 987e88e8ae6STakashi Iwai 988877211f5STakashi Iwai static int snd_interval_step(struct snd_interval *i, unsigned int min, unsigned int step) 9891da177e4SLinus Torvalds { 9901da177e4SLinus Torvalds unsigned int n; 9911da177e4SLinus Torvalds int changed = 0; 9921da177e4SLinus Torvalds n = (i->min - min) % step; 9931da177e4SLinus Torvalds if (n != 0 || i->openmin) { 9941da177e4SLinus Torvalds i->min += step - n; 9951da177e4SLinus Torvalds changed = 1; 9961da177e4SLinus Torvalds } 9971da177e4SLinus Torvalds n = (i->max - min) % step; 9981da177e4SLinus Torvalds if (n != 0 || i->openmax) { 9991da177e4SLinus Torvalds i->max -= n; 10001da177e4SLinus Torvalds changed = 1; 10011da177e4SLinus Torvalds } 10021da177e4SLinus Torvalds if (snd_interval_checkempty(i)) { 10031da177e4SLinus Torvalds i->empty = 1; 10041da177e4SLinus Torvalds return -EINVAL; 10051da177e4SLinus Torvalds } 10061da177e4SLinus Torvalds return changed; 10071da177e4SLinus Torvalds } 10081da177e4SLinus Torvalds 10091da177e4SLinus Torvalds /* Info constraints helpers */ 10101da177e4SLinus Torvalds 10111da177e4SLinus Torvalds /** 10121da177e4SLinus Torvalds * snd_pcm_hw_rule_add - add the hw-constraint rule 10131da177e4SLinus Torvalds * @runtime: the pcm runtime instance 10141da177e4SLinus Torvalds * @cond: condition bits 10151da177e4SLinus Torvalds * @var: the variable to evaluate 10161da177e4SLinus Torvalds * @func: the evaluation function 10171da177e4SLinus Torvalds * @private: the private data pointer passed to function 10181da177e4SLinus Torvalds * @dep: the dependent variables 10191da177e4SLinus Torvalds * 10201da177e4SLinus Torvalds * Returns zero if successful, or a negative error code on failure. 10211da177e4SLinus Torvalds */ 1022877211f5STakashi Iwai int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond, 10231da177e4SLinus Torvalds int var, 10241da177e4SLinus Torvalds snd_pcm_hw_rule_func_t func, void *private, 10251da177e4SLinus Torvalds int dep, ...) 10261da177e4SLinus Torvalds { 1027877211f5STakashi Iwai struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; 1028877211f5STakashi Iwai struct snd_pcm_hw_rule *c; 10291da177e4SLinus Torvalds unsigned int k; 10301da177e4SLinus Torvalds va_list args; 10311da177e4SLinus Torvalds va_start(args, dep); 10321da177e4SLinus Torvalds if (constrs->rules_num >= constrs->rules_all) { 1033877211f5STakashi Iwai struct snd_pcm_hw_rule *new; 10341da177e4SLinus Torvalds unsigned int new_rules = constrs->rules_all + 16; 10351da177e4SLinus Torvalds new = kcalloc(new_rules, sizeof(*c), GFP_KERNEL); 10361da177e4SLinus Torvalds if (!new) 10371da177e4SLinus Torvalds return -ENOMEM; 10381da177e4SLinus Torvalds if (constrs->rules) { 10391da177e4SLinus Torvalds memcpy(new, constrs->rules, 10401da177e4SLinus Torvalds constrs->rules_num * sizeof(*c)); 10411da177e4SLinus Torvalds kfree(constrs->rules); 10421da177e4SLinus Torvalds } 10431da177e4SLinus Torvalds constrs->rules = new; 10441da177e4SLinus Torvalds constrs->rules_all = new_rules; 10451da177e4SLinus Torvalds } 10461da177e4SLinus Torvalds c = &constrs->rules[constrs->rules_num]; 10471da177e4SLinus Torvalds c->cond = cond; 10481da177e4SLinus Torvalds c->func = func; 10491da177e4SLinus Torvalds c->var = var; 10501da177e4SLinus Torvalds c->private = private; 10511da177e4SLinus Torvalds k = 0; 10521da177e4SLinus Torvalds while (1) { 10537eaa943cSTakashi Iwai if (snd_BUG_ON(k >= ARRAY_SIZE(c->deps))) 10547eaa943cSTakashi Iwai return -EINVAL; 10551da177e4SLinus Torvalds c->deps[k++] = dep; 10561da177e4SLinus Torvalds if (dep < 0) 10571da177e4SLinus Torvalds break; 10581da177e4SLinus Torvalds dep = va_arg(args, int); 10591da177e4SLinus Torvalds } 10601da177e4SLinus Torvalds constrs->rules_num++; 10611da177e4SLinus Torvalds va_end(args); 10621da177e4SLinus Torvalds return 0; 10631da177e4SLinus Torvalds } 10641da177e4SLinus Torvalds 1065e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_rule_add); 1066e88e8ae6STakashi Iwai 10671da177e4SLinus Torvalds /** 10681c85cc64SRandy Dunlap * snd_pcm_hw_constraint_mask - apply the given bitmap mask constraint 1069df8db936STakashi Iwai * @runtime: PCM runtime instance 1070df8db936STakashi Iwai * @var: hw_params variable to apply the mask 1071df8db936STakashi Iwai * @mask: the bitmap mask 1072df8db936STakashi Iwai * 10731c85cc64SRandy Dunlap * Apply the constraint of the given bitmap mask to a 32-bit mask parameter. 10741da177e4SLinus Torvalds */ 1075877211f5STakashi Iwai int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, 10761da177e4SLinus Torvalds u_int32_t mask) 10771da177e4SLinus Torvalds { 1078877211f5STakashi Iwai struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; 1079877211f5STakashi Iwai struct snd_mask *maskp = constrs_mask(constrs, var); 10801da177e4SLinus Torvalds *maskp->bits &= mask; 10811da177e4SLinus Torvalds memset(maskp->bits + 1, 0, (SNDRV_MASK_MAX-32) / 8); /* clear rest */ 10821da177e4SLinus Torvalds if (*maskp->bits == 0) 10831da177e4SLinus Torvalds return -EINVAL; 10841da177e4SLinus Torvalds return 0; 10851da177e4SLinus Torvalds } 10861da177e4SLinus Torvalds 10871da177e4SLinus Torvalds /** 10881c85cc64SRandy Dunlap * snd_pcm_hw_constraint_mask64 - apply the given bitmap mask constraint 1089df8db936STakashi Iwai * @runtime: PCM runtime instance 1090df8db936STakashi Iwai * @var: hw_params variable to apply the mask 1091df8db936STakashi Iwai * @mask: the 64bit bitmap mask 1092df8db936STakashi Iwai * 10931c85cc64SRandy Dunlap * Apply the constraint of the given bitmap mask to a 64-bit mask parameter. 10941da177e4SLinus Torvalds */ 1095877211f5STakashi Iwai int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, 10961da177e4SLinus Torvalds u_int64_t mask) 10971da177e4SLinus Torvalds { 1098877211f5STakashi Iwai struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; 1099877211f5STakashi Iwai struct snd_mask *maskp = constrs_mask(constrs, var); 11001da177e4SLinus Torvalds maskp->bits[0] &= (u_int32_t)mask; 11011da177e4SLinus Torvalds maskp->bits[1] &= (u_int32_t)(mask >> 32); 11021da177e4SLinus Torvalds memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */ 11031da177e4SLinus Torvalds if (! maskp->bits[0] && ! maskp->bits[1]) 11041da177e4SLinus Torvalds return -EINVAL; 11051da177e4SLinus Torvalds return 0; 11061da177e4SLinus Torvalds } 11071da177e4SLinus Torvalds 11081da177e4SLinus Torvalds /** 11091c85cc64SRandy Dunlap * snd_pcm_hw_constraint_integer - apply an integer constraint to an interval 1110df8db936STakashi Iwai * @runtime: PCM runtime instance 1111df8db936STakashi Iwai * @var: hw_params variable to apply the integer constraint 1112df8db936STakashi Iwai * 1113df8db936STakashi Iwai * Apply the constraint of integer to an interval parameter. 11141da177e4SLinus Torvalds */ 1115877211f5STakashi Iwai int snd_pcm_hw_constraint_integer(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var) 11161da177e4SLinus Torvalds { 1117877211f5STakashi Iwai struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; 11181da177e4SLinus Torvalds return snd_interval_setinteger(constrs_interval(constrs, var)); 11191da177e4SLinus Torvalds } 11201da177e4SLinus Torvalds 1121e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_integer); 1122e88e8ae6STakashi Iwai 11231da177e4SLinus Torvalds /** 11241c85cc64SRandy Dunlap * snd_pcm_hw_constraint_minmax - apply a min/max range constraint to an interval 1125df8db936STakashi Iwai * @runtime: PCM runtime instance 1126df8db936STakashi Iwai * @var: hw_params variable to apply the range 1127df8db936STakashi Iwai * @min: the minimal value 1128df8db936STakashi Iwai * @max: the maximal value 1129df8db936STakashi Iwai * 1130df8db936STakashi Iwai * Apply the min/max range constraint to an interval parameter. 11311da177e4SLinus Torvalds */ 1132877211f5STakashi Iwai int snd_pcm_hw_constraint_minmax(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, 11331da177e4SLinus Torvalds unsigned int min, unsigned int max) 11341da177e4SLinus Torvalds { 1135877211f5STakashi Iwai struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; 1136877211f5STakashi Iwai struct snd_interval t; 11371da177e4SLinus Torvalds t.min = min; 11381da177e4SLinus Torvalds t.max = max; 11391da177e4SLinus Torvalds t.openmin = t.openmax = 0; 11401da177e4SLinus Torvalds t.integer = 0; 11411da177e4SLinus Torvalds return snd_interval_refine(constrs_interval(constrs, var), &t); 11421da177e4SLinus Torvalds } 11431da177e4SLinus Torvalds 1144e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_minmax); 1145e88e8ae6STakashi Iwai 1146877211f5STakashi Iwai static int snd_pcm_hw_rule_list(struct snd_pcm_hw_params *params, 1147877211f5STakashi Iwai struct snd_pcm_hw_rule *rule) 11481da177e4SLinus Torvalds { 1149877211f5STakashi Iwai struct snd_pcm_hw_constraint_list *list = rule->private; 11501da177e4SLinus Torvalds return snd_interval_list(hw_param_interval(params, rule->var), list->count, list->list, list->mask); 11511da177e4SLinus Torvalds } 11521da177e4SLinus Torvalds 11531da177e4SLinus Torvalds 11541da177e4SLinus Torvalds /** 11551c85cc64SRandy Dunlap * snd_pcm_hw_constraint_list - apply a list of constraints to a parameter 1156df8db936STakashi Iwai * @runtime: PCM runtime instance 1157df8db936STakashi Iwai * @cond: condition bits 1158df8db936STakashi Iwai * @var: hw_params variable to apply the list constraint 1159df8db936STakashi Iwai * @l: list 1160df8db936STakashi Iwai * 1161df8db936STakashi Iwai * Apply the list of constraints to an interval parameter. 11621da177e4SLinus Torvalds */ 1163877211f5STakashi Iwai int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime, 11641da177e4SLinus Torvalds unsigned int cond, 11651da177e4SLinus Torvalds snd_pcm_hw_param_t var, 1166877211f5STakashi Iwai struct snd_pcm_hw_constraint_list *l) 11671da177e4SLinus Torvalds { 11681da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 11691da177e4SLinus Torvalds snd_pcm_hw_rule_list, l, 11701da177e4SLinus Torvalds var, -1); 11711da177e4SLinus Torvalds } 11721da177e4SLinus Torvalds 1173e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_list); 1174e88e8ae6STakashi Iwai 1175877211f5STakashi Iwai static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params, 1176877211f5STakashi Iwai struct snd_pcm_hw_rule *rule) 11771da177e4SLinus Torvalds { 1178877211f5STakashi Iwai struct snd_pcm_hw_constraint_ratnums *r = rule->private; 11791da177e4SLinus Torvalds unsigned int num = 0, den = 0; 11801da177e4SLinus Torvalds int err; 11811da177e4SLinus Torvalds err = snd_interval_ratnum(hw_param_interval(params, rule->var), 11821da177e4SLinus Torvalds r->nrats, r->rats, &num, &den); 11831da177e4SLinus Torvalds if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { 11841da177e4SLinus Torvalds params->rate_num = num; 11851da177e4SLinus Torvalds params->rate_den = den; 11861da177e4SLinus Torvalds } 11871da177e4SLinus Torvalds return err; 11881da177e4SLinus Torvalds } 11891da177e4SLinus Torvalds 11901da177e4SLinus Torvalds /** 11911c85cc64SRandy Dunlap * snd_pcm_hw_constraint_ratnums - apply ratnums constraint to a parameter 1192df8db936STakashi Iwai * @runtime: PCM runtime instance 1193df8db936STakashi Iwai * @cond: condition bits 1194df8db936STakashi Iwai * @var: hw_params variable to apply the ratnums constraint 1195877211f5STakashi Iwai * @r: struct snd_ratnums constriants 11961da177e4SLinus Torvalds */ 1197877211f5STakashi Iwai int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime, 11981da177e4SLinus Torvalds unsigned int cond, 11991da177e4SLinus Torvalds snd_pcm_hw_param_t var, 1200877211f5STakashi Iwai struct snd_pcm_hw_constraint_ratnums *r) 12011da177e4SLinus Torvalds { 12021da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 12031da177e4SLinus Torvalds snd_pcm_hw_rule_ratnums, r, 12041da177e4SLinus Torvalds var, -1); 12051da177e4SLinus Torvalds } 12061da177e4SLinus Torvalds 1207e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums); 1208e88e8ae6STakashi Iwai 1209877211f5STakashi Iwai static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params, 1210877211f5STakashi Iwai struct snd_pcm_hw_rule *rule) 12111da177e4SLinus Torvalds { 1212877211f5STakashi Iwai struct snd_pcm_hw_constraint_ratdens *r = rule->private; 12131da177e4SLinus Torvalds unsigned int num = 0, den = 0; 12141da177e4SLinus Torvalds int err = snd_interval_ratden(hw_param_interval(params, rule->var), 12151da177e4SLinus Torvalds r->nrats, r->rats, &num, &den); 12161da177e4SLinus Torvalds if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { 12171da177e4SLinus Torvalds params->rate_num = num; 12181da177e4SLinus Torvalds params->rate_den = den; 12191da177e4SLinus Torvalds } 12201da177e4SLinus Torvalds return err; 12211da177e4SLinus Torvalds } 12221da177e4SLinus Torvalds 12231da177e4SLinus Torvalds /** 12241c85cc64SRandy Dunlap * snd_pcm_hw_constraint_ratdens - apply ratdens constraint to a parameter 1225df8db936STakashi Iwai * @runtime: PCM runtime instance 1226df8db936STakashi Iwai * @cond: condition bits 1227df8db936STakashi Iwai * @var: hw_params variable to apply the ratdens constraint 1228877211f5STakashi Iwai * @r: struct snd_ratdens constriants 12291da177e4SLinus Torvalds */ 1230877211f5STakashi Iwai int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime, 12311da177e4SLinus Torvalds unsigned int cond, 12321da177e4SLinus Torvalds snd_pcm_hw_param_t var, 1233877211f5STakashi Iwai struct snd_pcm_hw_constraint_ratdens *r) 12341da177e4SLinus Torvalds { 12351da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 12361da177e4SLinus Torvalds snd_pcm_hw_rule_ratdens, r, 12371da177e4SLinus Torvalds var, -1); 12381da177e4SLinus Torvalds } 12391da177e4SLinus Torvalds 1240e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_ratdens); 1241e88e8ae6STakashi Iwai 1242877211f5STakashi Iwai static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params, 1243877211f5STakashi Iwai struct snd_pcm_hw_rule *rule) 12441da177e4SLinus Torvalds { 12451da177e4SLinus Torvalds unsigned int l = (unsigned long) rule->private; 12461da177e4SLinus Torvalds int width = l & 0xffff; 12471da177e4SLinus Torvalds unsigned int msbits = l >> 16; 1248877211f5STakashi Iwai struct snd_interval *i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); 12491da177e4SLinus Torvalds if (snd_interval_single(i) && snd_interval_value(i) == width) 12501da177e4SLinus Torvalds params->msbits = msbits; 12511da177e4SLinus Torvalds return 0; 12521da177e4SLinus Torvalds } 12531da177e4SLinus Torvalds 12541da177e4SLinus Torvalds /** 12551c85cc64SRandy Dunlap * snd_pcm_hw_constraint_msbits - add a hw constraint msbits rule 1256df8db936STakashi Iwai * @runtime: PCM runtime instance 1257df8db936STakashi Iwai * @cond: condition bits 1258df8db936STakashi Iwai * @width: sample bits width 1259df8db936STakashi Iwai * @msbits: msbits width 12601da177e4SLinus Torvalds */ 1261877211f5STakashi Iwai int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime, 12621da177e4SLinus Torvalds unsigned int cond, 12631da177e4SLinus Torvalds unsigned int width, 12641da177e4SLinus Torvalds unsigned int msbits) 12651da177e4SLinus Torvalds { 12661da177e4SLinus Torvalds unsigned long l = (msbits << 16) | width; 12671da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, -1, 12681da177e4SLinus Torvalds snd_pcm_hw_rule_msbits, 12691da177e4SLinus Torvalds (void*) l, 12701da177e4SLinus Torvalds SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); 12711da177e4SLinus Torvalds } 12721da177e4SLinus Torvalds 1273e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_msbits); 1274e88e8ae6STakashi Iwai 1275877211f5STakashi Iwai static int snd_pcm_hw_rule_step(struct snd_pcm_hw_params *params, 1276877211f5STakashi Iwai struct snd_pcm_hw_rule *rule) 12771da177e4SLinus Torvalds { 12781da177e4SLinus Torvalds unsigned long step = (unsigned long) rule->private; 12791da177e4SLinus Torvalds return snd_interval_step(hw_param_interval(params, rule->var), 0, step); 12801da177e4SLinus Torvalds } 12811da177e4SLinus Torvalds 12821da177e4SLinus Torvalds /** 12831c85cc64SRandy Dunlap * snd_pcm_hw_constraint_step - add a hw constraint step rule 1284df8db936STakashi Iwai * @runtime: PCM runtime instance 1285df8db936STakashi Iwai * @cond: condition bits 1286df8db936STakashi Iwai * @var: hw_params variable to apply the step constraint 1287df8db936STakashi Iwai * @step: step size 12881da177e4SLinus Torvalds */ 1289877211f5STakashi Iwai int snd_pcm_hw_constraint_step(struct snd_pcm_runtime *runtime, 12901da177e4SLinus Torvalds unsigned int cond, 12911da177e4SLinus Torvalds snd_pcm_hw_param_t var, 12921da177e4SLinus Torvalds unsigned long step) 12931da177e4SLinus Torvalds { 12941da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 12951da177e4SLinus Torvalds snd_pcm_hw_rule_step, (void *) step, 12961da177e4SLinus Torvalds var, -1); 12971da177e4SLinus Torvalds } 12981da177e4SLinus Torvalds 1299e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_step); 1300e88e8ae6STakashi Iwai 1301877211f5STakashi Iwai static int snd_pcm_hw_rule_pow2(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) 13021da177e4SLinus Torvalds { 130367c39317SMarcin Ślusarz static unsigned int pow2_sizes[] = { 13041da177e4SLinus Torvalds 1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7, 13051da177e4SLinus Torvalds 1<<8, 1<<9, 1<<10, 1<<11, 1<<12, 1<<13, 1<<14, 1<<15, 13061da177e4SLinus Torvalds 1<<16, 1<<17, 1<<18, 1<<19, 1<<20, 1<<21, 1<<22, 1<<23, 13071da177e4SLinus Torvalds 1<<24, 1<<25, 1<<26, 1<<27, 1<<28, 1<<29, 1<<30 13081da177e4SLinus Torvalds }; 13091da177e4SLinus Torvalds return snd_interval_list(hw_param_interval(params, rule->var), 13101da177e4SLinus Torvalds ARRAY_SIZE(pow2_sizes), pow2_sizes, 0); 13111da177e4SLinus Torvalds } 13121da177e4SLinus Torvalds 13131da177e4SLinus Torvalds /** 13141c85cc64SRandy Dunlap * snd_pcm_hw_constraint_pow2 - add a hw constraint power-of-2 rule 1315df8db936STakashi Iwai * @runtime: PCM runtime instance 1316df8db936STakashi Iwai * @cond: condition bits 1317df8db936STakashi Iwai * @var: hw_params variable to apply the power-of-2 constraint 13181da177e4SLinus Torvalds */ 1319877211f5STakashi Iwai int snd_pcm_hw_constraint_pow2(struct snd_pcm_runtime *runtime, 13201da177e4SLinus Torvalds unsigned int cond, 13211da177e4SLinus Torvalds snd_pcm_hw_param_t var) 13221da177e4SLinus Torvalds { 13231da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 13241da177e4SLinus Torvalds snd_pcm_hw_rule_pow2, NULL, 13251da177e4SLinus Torvalds var, -1); 13261da177e4SLinus Torvalds } 13271da177e4SLinus Torvalds 1328e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_pow2); 1329e88e8ae6STakashi Iwai 1330877211f5STakashi Iwai static void _snd_pcm_hw_param_any(struct snd_pcm_hw_params *params, 1331123992f7SAdrian Bunk snd_pcm_hw_param_t var) 13321da177e4SLinus Torvalds { 13331da177e4SLinus Torvalds if (hw_is_mask(var)) { 13341da177e4SLinus Torvalds snd_mask_any(hw_param_mask(params, var)); 13351da177e4SLinus Torvalds params->cmask |= 1 << var; 13361da177e4SLinus Torvalds params->rmask |= 1 << var; 13371da177e4SLinus Torvalds return; 13381da177e4SLinus Torvalds } 13391da177e4SLinus Torvalds if (hw_is_interval(var)) { 13401da177e4SLinus Torvalds snd_interval_any(hw_param_interval(params, var)); 13411da177e4SLinus Torvalds params->cmask |= 1 << var; 13421da177e4SLinus Torvalds params->rmask |= 1 << var; 13431da177e4SLinus Torvalds return; 13441da177e4SLinus Torvalds } 13451da177e4SLinus Torvalds snd_BUG(); 13461da177e4SLinus Torvalds } 13471da177e4SLinus Torvalds 1348877211f5STakashi Iwai void _snd_pcm_hw_params_any(struct snd_pcm_hw_params *params) 13491da177e4SLinus Torvalds { 13501da177e4SLinus Torvalds unsigned int k; 13511da177e4SLinus Torvalds memset(params, 0, sizeof(*params)); 13521da177e4SLinus Torvalds for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) 13531da177e4SLinus Torvalds _snd_pcm_hw_param_any(params, k); 13541da177e4SLinus Torvalds for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) 13551da177e4SLinus Torvalds _snd_pcm_hw_param_any(params, k); 13561da177e4SLinus Torvalds params->info = ~0U; 13571da177e4SLinus Torvalds } 13581da177e4SLinus Torvalds 1359e88e8ae6STakashi Iwai EXPORT_SYMBOL(_snd_pcm_hw_params_any); 13601da177e4SLinus Torvalds 13611da177e4SLinus Torvalds /** 13621c85cc64SRandy Dunlap * snd_pcm_hw_param_value - return @params field @var value 1363df8db936STakashi Iwai * @params: the hw_params instance 1364df8db936STakashi Iwai * @var: parameter to retrieve 13651c85cc64SRandy Dunlap * @dir: pointer to the direction (-1,0,1) or %NULL 13661da177e4SLinus Torvalds * 13671c85cc64SRandy Dunlap * Return the value for field @var if it's fixed in configuration space 13681c85cc64SRandy Dunlap * defined by @params. Return -%EINVAL otherwise. 13691da177e4SLinus Torvalds */ 1370e88e8ae6STakashi Iwai int snd_pcm_hw_param_value(const struct snd_pcm_hw_params *params, 13711da177e4SLinus Torvalds snd_pcm_hw_param_t var, int *dir) 13721da177e4SLinus Torvalds { 13731da177e4SLinus Torvalds if (hw_is_mask(var)) { 1374877211f5STakashi Iwai const struct snd_mask *mask = hw_param_mask_c(params, var); 13751da177e4SLinus Torvalds if (!snd_mask_single(mask)) 13761da177e4SLinus Torvalds return -EINVAL; 13771da177e4SLinus Torvalds if (dir) 13781da177e4SLinus Torvalds *dir = 0; 13791da177e4SLinus Torvalds return snd_mask_value(mask); 13801da177e4SLinus Torvalds } 13811da177e4SLinus Torvalds if (hw_is_interval(var)) { 1382877211f5STakashi Iwai const struct snd_interval *i = hw_param_interval_c(params, var); 13831da177e4SLinus Torvalds if (!snd_interval_single(i)) 13841da177e4SLinus Torvalds return -EINVAL; 13851da177e4SLinus Torvalds if (dir) 13861da177e4SLinus Torvalds *dir = i->openmin; 13871da177e4SLinus Torvalds return snd_interval_value(i); 13881da177e4SLinus Torvalds } 13891da177e4SLinus Torvalds return -EINVAL; 13901da177e4SLinus Torvalds } 13911da177e4SLinus Torvalds 1392e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_param_value); 13931da177e4SLinus Torvalds 1394877211f5STakashi Iwai void _snd_pcm_hw_param_setempty(struct snd_pcm_hw_params *params, 13951da177e4SLinus Torvalds snd_pcm_hw_param_t var) 13961da177e4SLinus Torvalds { 13971da177e4SLinus Torvalds if (hw_is_mask(var)) { 13981da177e4SLinus Torvalds snd_mask_none(hw_param_mask(params, var)); 13991da177e4SLinus Torvalds params->cmask |= 1 << var; 14001da177e4SLinus Torvalds params->rmask |= 1 << var; 14011da177e4SLinus Torvalds } else if (hw_is_interval(var)) { 14021da177e4SLinus Torvalds snd_interval_none(hw_param_interval(params, var)); 14031da177e4SLinus Torvalds params->cmask |= 1 << var; 14041da177e4SLinus Torvalds params->rmask |= 1 << var; 14051da177e4SLinus Torvalds } else { 14061da177e4SLinus Torvalds snd_BUG(); 14071da177e4SLinus Torvalds } 14081da177e4SLinus Torvalds } 14091da177e4SLinus Torvalds 1410e88e8ae6STakashi Iwai EXPORT_SYMBOL(_snd_pcm_hw_param_setempty); 14111da177e4SLinus Torvalds 1412877211f5STakashi Iwai static int _snd_pcm_hw_param_first(struct snd_pcm_hw_params *params, 14131da177e4SLinus Torvalds snd_pcm_hw_param_t var) 14141da177e4SLinus Torvalds { 14151da177e4SLinus Torvalds int changed; 14161da177e4SLinus Torvalds if (hw_is_mask(var)) 14171da177e4SLinus Torvalds changed = snd_mask_refine_first(hw_param_mask(params, var)); 14181da177e4SLinus Torvalds else if (hw_is_interval(var)) 14191da177e4SLinus Torvalds changed = snd_interval_refine_first(hw_param_interval(params, var)); 14202f4ca8e5STakashi Iwai else 14211da177e4SLinus Torvalds return -EINVAL; 14221da177e4SLinus Torvalds if (changed) { 14231da177e4SLinus Torvalds params->cmask |= 1 << var; 14241da177e4SLinus Torvalds params->rmask |= 1 << var; 14251da177e4SLinus Torvalds } 14261da177e4SLinus Torvalds return changed; 14271da177e4SLinus Torvalds } 14281da177e4SLinus Torvalds 14291da177e4SLinus Torvalds 14301da177e4SLinus Torvalds /** 14311c85cc64SRandy Dunlap * snd_pcm_hw_param_first - refine config space and return minimum value 1432df8db936STakashi Iwai * @pcm: PCM instance 1433df8db936STakashi Iwai * @params: the hw_params instance 1434df8db936STakashi Iwai * @var: parameter to retrieve 14351c85cc64SRandy Dunlap * @dir: pointer to the direction (-1,0,1) or %NULL 14361da177e4SLinus Torvalds * 14371c85cc64SRandy Dunlap * Inside configuration space defined by @params remove from @var all 14381da177e4SLinus Torvalds * values > minimum. Reduce configuration space accordingly. 14391da177e4SLinus Torvalds * Return the minimum. 14401da177e4SLinus Torvalds */ 1441e88e8ae6STakashi Iwai int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm, 1442877211f5STakashi Iwai struct snd_pcm_hw_params *params, 14431da177e4SLinus Torvalds snd_pcm_hw_param_t var, int *dir) 14441da177e4SLinus Torvalds { 14451da177e4SLinus Torvalds int changed = _snd_pcm_hw_param_first(params, var); 14461da177e4SLinus Torvalds if (changed < 0) 14471da177e4SLinus Torvalds return changed; 14481da177e4SLinus Torvalds if (params->rmask) { 14491da177e4SLinus Torvalds int err = snd_pcm_hw_refine(pcm, params); 14507eaa943cSTakashi Iwai if (snd_BUG_ON(err < 0)) 14517eaa943cSTakashi Iwai return err; 14521da177e4SLinus Torvalds } 14531da177e4SLinus Torvalds return snd_pcm_hw_param_value(params, var, dir); 14541da177e4SLinus Torvalds } 14551da177e4SLinus Torvalds 1456e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_param_first); 1457e88e8ae6STakashi Iwai 1458877211f5STakashi Iwai static int _snd_pcm_hw_param_last(struct snd_pcm_hw_params *params, 14591da177e4SLinus Torvalds snd_pcm_hw_param_t var) 14601da177e4SLinus Torvalds { 14611da177e4SLinus Torvalds int changed; 14621da177e4SLinus Torvalds if (hw_is_mask(var)) 14631da177e4SLinus Torvalds changed = snd_mask_refine_last(hw_param_mask(params, var)); 14641da177e4SLinus Torvalds else if (hw_is_interval(var)) 14651da177e4SLinus Torvalds changed = snd_interval_refine_last(hw_param_interval(params, var)); 14662f4ca8e5STakashi Iwai else 14671da177e4SLinus Torvalds return -EINVAL; 14681da177e4SLinus Torvalds if (changed) { 14691da177e4SLinus Torvalds params->cmask |= 1 << var; 14701da177e4SLinus Torvalds params->rmask |= 1 << var; 14711da177e4SLinus Torvalds } 14721da177e4SLinus Torvalds return changed; 14731da177e4SLinus Torvalds } 14741da177e4SLinus Torvalds 14751da177e4SLinus Torvalds 14761da177e4SLinus Torvalds /** 14771c85cc64SRandy Dunlap * snd_pcm_hw_param_last - refine config space and return maximum value 1478df8db936STakashi Iwai * @pcm: PCM instance 1479df8db936STakashi Iwai * @params: the hw_params instance 1480df8db936STakashi Iwai * @var: parameter to retrieve 14811c85cc64SRandy Dunlap * @dir: pointer to the direction (-1,0,1) or %NULL 14821da177e4SLinus Torvalds * 14831c85cc64SRandy Dunlap * Inside configuration space defined by @params remove from @var all 14841da177e4SLinus Torvalds * values < maximum. Reduce configuration space accordingly. 14851da177e4SLinus Torvalds * Return the maximum. 14861da177e4SLinus Torvalds */ 1487e88e8ae6STakashi Iwai int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm, 1488877211f5STakashi Iwai struct snd_pcm_hw_params *params, 14891da177e4SLinus Torvalds snd_pcm_hw_param_t var, int *dir) 14901da177e4SLinus Torvalds { 14911da177e4SLinus Torvalds int changed = _snd_pcm_hw_param_last(params, var); 14921da177e4SLinus Torvalds if (changed < 0) 14931da177e4SLinus Torvalds return changed; 14941da177e4SLinus Torvalds if (params->rmask) { 14951da177e4SLinus Torvalds int err = snd_pcm_hw_refine(pcm, params); 14967eaa943cSTakashi Iwai if (snd_BUG_ON(err < 0)) 14977eaa943cSTakashi Iwai return err; 14981da177e4SLinus Torvalds } 14991da177e4SLinus Torvalds return snd_pcm_hw_param_value(params, var, dir); 15001da177e4SLinus Torvalds } 15011da177e4SLinus Torvalds 1502e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_param_last); 15031da177e4SLinus Torvalds 15041da177e4SLinus Torvalds /** 15051c85cc64SRandy Dunlap * snd_pcm_hw_param_choose - choose a configuration defined by @params 1506df8db936STakashi Iwai * @pcm: PCM instance 1507df8db936STakashi Iwai * @params: the hw_params instance 15081da177e4SLinus Torvalds * 15091c85cc64SRandy Dunlap * Choose one configuration from configuration space defined by @params. 15101da177e4SLinus Torvalds * The configuration chosen is that obtained fixing in this order: 15111da177e4SLinus Torvalds * first access, first format, first subformat, min channels, 15121da177e4SLinus Torvalds * min rate, min period time, max buffer size, min tick time 15131da177e4SLinus Torvalds */ 15142f4ca8e5STakashi Iwai int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm, 15152f4ca8e5STakashi Iwai struct snd_pcm_hw_params *params) 15161da177e4SLinus Torvalds { 15172f4ca8e5STakashi Iwai static int vars[] = { 15182f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_ACCESS, 15192f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_FORMAT, 15202f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_SUBFORMAT, 15212f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_CHANNELS, 15222f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_RATE, 15232f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_PERIOD_TIME, 15242f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 15252f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_TICK_TIME, 15262f4ca8e5STakashi Iwai -1 15272f4ca8e5STakashi Iwai }; 15282f4ca8e5STakashi Iwai int err, *v; 15291da177e4SLinus Torvalds 15302f4ca8e5STakashi Iwai for (v = vars; *v != -1; v++) { 15312f4ca8e5STakashi Iwai if (*v != SNDRV_PCM_HW_PARAM_BUFFER_SIZE) 15322f4ca8e5STakashi Iwai err = snd_pcm_hw_param_first(pcm, params, *v, NULL); 15332f4ca8e5STakashi Iwai else 15342f4ca8e5STakashi Iwai err = snd_pcm_hw_param_last(pcm, params, *v, NULL); 15357eaa943cSTakashi Iwai if (snd_BUG_ON(err < 0)) 15367eaa943cSTakashi Iwai return err; 15372f4ca8e5STakashi Iwai } 15381da177e4SLinus Torvalds return 0; 15391da177e4SLinus Torvalds } 15401da177e4SLinus Torvalds 1541877211f5STakashi Iwai static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream, 15421da177e4SLinus Torvalds void *arg) 15431da177e4SLinus Torvalds { 1544877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 15451da177e4SLinus Torvalds unsigned long flags; 15461da177e4SLinus Torvalds snd_pcm_stream_lock_irqsave(substream, flags); 15471da177e4SLinus Torvalds if (snd_pcm_running(substream) && 15481da177e4SLinus Torvalds snd_pcm_update_hw_ptr(substream) >= 0) 15491da177e4SLinus Torvalds runtime->status->hw_ptr %= runtime->buffer_size; 15501da177e4SLinus Torvalds else 15511da177e4SLinus Torvalds runtime->status->hw_ptr = 0; 15521da177e4SLinus Torvalds snd_pcm_stream_unlock_irqrestore(substream, flags); 15531da177e4SLinus Torvalds return 0; 15541da177e4SLinus Torvalds } 15551da177e4SLinus Torvalds 1556877211f5STakashi Iwai static int snd_pcm_lib_ioctl_channel_info(struct snd_pcm_substream *substream, 15571da177e4SLinus Torvalds void *arg) 15581da177e4SLinus Torvalds { 1559877211f5STakashi Iwai struct snd_pcm_channel_info *info = arg; 1560877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 15611da177e4SLinus Torvalds int width; 15621da177e4SLinus Torvalds if (!(runtime->info & SNDRV_PCM_INFO_MMAP)) { 15631da177e4SLinus Torvalds info->offset = -1; 15641da177e4SLinus Torvalds return 0; 15651da177e4SLinus Torvalds } 15661da177e4SLinus Torvalds width = snd_pcm_format_physical_width(runtime->format); 15671da177e4SLinus Torvalds if (width < 0) 15681da177e4SLinus Torvalds return width; 15691da177e4SLinus Torvalds info->offset = 0; 15701da177e4SLinus Torvalds switch (runtime->access) { 15711da177e4SLinus Torvalds case SNDRV_PCM_ACCESS_MMAP_INTERLEAVED: 15721da177e4SLinus Torvalds case SNDRV_PCM_ACCESS_RW_INTERLEAVED: 15731da177e4SLinus Torvalds info->first = info->channel * width; 15741da177e4SLinus Torvalds info->step = runtime->channels * width; 15751da177e4SLinus Torvalds break; 15761da177e4SLinus Torvalds case SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED: 15771da177e4SLinus Torvalds case SNDRV_PCM_ACCESS_RW_NONINTERLEAVED: 15781da177e4SLinus Torvalds { 15791da177e4SLinus Torvalds size_t size = runtime->dma_bytes / runtime->channels; 15801da177e4SLinus Torvalds info->first = info->channel * size * 8; 15811da177e4SLinus Torvalds info->step = width; 15821da177e4SLinus Torvalds break; 15831da177e4SLinus Torvalds } 15841da177e4SLinus Torvalds default: 15851da177e4SLinus Torvalds snd_BUG(); 15861da177e4SLinus Torvalds break; 15871da177e4SLinus Torvalds } 15881da177e4SLinus Torvalds return 0; 15891da177e4SLinus Torvalds } 15901da177e4SLinus Torvalds 15918bea869cSJaroslav Kysela static int snd_pcm_lib_ioctl_fifo_size(struct snd_pcm_substream *substream, 15928bea869cSJaroslav Kysela void *arg) 15938bea869cSJaroslav Kysela { 15948bea869cSJaroslav Kysela struct snd_pcm_hw_params *params = arg; 15958bea869cSJaroslav Kysela snd_pcm_format_t format; 15968bea869cSJaroslav Kysela int channels, width; 15978bea869cSJaroslav Kysela 15988bea869cSJaroslav Kysela params->fifo_size = substream->runtime->hw.fifo_size; 15998bea869cSJaroslav Kysela if (!(substream->runtime->hw.info & SNDRV_PCM_INFO_FIFO_IN_FRAMES)) { 16008bea869cSJaroslav Kysela format = params_format(params); 16018bea869cSJaroslav Kysela channels = params_channels(params); 16028bea869cSJaroslav Kysela width = snd_pcm_format_physical_width(format); 16038bea869cSJaroslav Kysela params->fifo_size /= width * channels; 16048bea869cSJaroslav Kysela } 16058bea869cSJaroslav Kysela return 0; 16068bea869cSJaroslav Kysela } 16078bea869cSJaroslav Kysela 16081da177e4SLinus Torvalds /** 16091da177e4SLinus Torvalds * snd_pcm_lib_ioctl - a generic PCM ioctl callback 16101da177e4SLinus Torvalds * @substream: the pcm substream instance 16111da177e4SLinus Torvalds * @cmd: ioctl command 16121da177e4SLinus Torvalds * @arg: ioctl argument 16131da177e4SLinus Torvalds * 16141da177e4SLinus Torvalds * Processes the generic ioctl commands for PCM. 16151da177e4SLinus Torvalds * Can be passed as the ioctl callback for PCM ops. 16161da177e4SLinus Torvalds * 16171da177e4SLinus Torvalds * Returns zero if successful, or a negative error code on failure. 16181da177e4SLinus Torvalds */ 1619877211f5STakashi Iwai int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream, 16201da177e4SLinus Torvalds unsigned int cmd, void *arg) 16211da177e4SLinus Torvalds { 16221da177e4SLinus Torvalds switch (cmd) { 16231da177e4SLinus Torvalds case SNDRV_PCM_IOCTL1_INFO: 16241da177e4SLinus Torvalds return 0; 16251da177e4SLinus Torvalds case SNDRV_PCM_IOCTL1_RESET: 16261da177e4SLinus Torvalds return snd_pcm_lib_ioctl_reset(substream, arg); 16271da177e4SLinus Torvalds case SNDRV_PCM_IOCTL1_CHANNEL_INFO: 16281da177e4SLinus Torvalds return snd_pcm_lib_ioctl_channel_info(substream, arg); 16298bea869cSJaroslav Kysela case SNDRV_PCM_IOCTL1_FIFO_SIZE: 16308bea869cSJaroslav Kysela return snd_pcm_lib_ioctl_fifo_size(substream, arg); 16311da177e4SLinus Torvalds } 16321da177e4SLinus Torvalds return -ENXIO; 16331da177e4SLinus Torvalds } 16341da177e4SLinus Torvalds 1635e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_lib_ioctl); 1636e88e8ae6STakashi Iwai 16371da177e4SLinus Torvalds /** 16381da177e4SLinus Torvalds * snd_pcm_period_elapsed - update the pcm status for the next period 16391da177e4SLinus Torvalds * @substream: the pcm substream instance 16401da177e4SLinus Torvalds * 16411da177e4SLinus Torvalds * This function is called from the interrupt handler when the 16421da177e4SLinus Torvalds * PCM has processed the period size. It will update the current 164331e8960bSTakashi Iwai * pointer, wake up sleepers, etc. 16441da177e4SLinus Torvalds * 16451da177e4SLinus Torvalds * Even if more than one periods have elapsed since the last call, you 16461da177e4SLinus Torvalds * have to call this only once. 16471da177e4SLinus Torvalds */ 1648877211f5STakashi Iwai void snd_pcm_period_elapsed(struct snd_pcm_substream *substream) 16491da177e4SLinus Torvalds { 1650877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 16511da177e4SLinus Torvalds unsigned long flags; 16521da177e4SLinus Torvalds 16537eaa943cSTakashi Iwai if (PCM_RUNTIME_CHECK(substream)) 16547eaa943cSTakashi Iwai return; 16551da177e4SLinus Torvalds runtime = substream->runtime; 16561da177e4SLinus Torvalds 16571da177e4SLinus Torvalds if (runtime->transfer_ack_begin) 16581da177e4SLinus Torvalds runtime->transfer_ack_begin(substream); 16591da177e4SLinus Torvalds 16601da177e4SLinus Torvalds snd_pcm_stream_lock_irqsave(substream, flags); 16611da177e4SLinus Torvalds if (!snd_pcm_running(substream) || 1662f240406bSJaroslav Kysela snd_pcm_update_hw_ptr0(substream, 1) < 0) 16631da177e4SLinus Torvalds goto _end; 16641da177e4SLinus Torvalds 16651da177e4SLinus Torvalds if (substream->timer_running) 16661da177e4SLinus Torvalds snd_timer_interrupt(substream->timer, 1); 16671da177e4SLinus Torvalds _end: 16681da177e4SLinus Torvalds snd_pcm_stream_unlock_irqrestore(substream, flags); 16691da177e4SLinus Torvalds if (runtime->transfer_ack_end) 16701da177e4SLinus Torvalds runtime->transfer_ack_end(substream); 16711da177e4SLinus Torvalds kill_fasync(&runtime->fasync, SIGIO, POLL_IN); 16721da177e4SLinus Torvalds } 16731da177e4SLinus Torvalds 1674e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_period_elapsed); 1675e88e8ae6STakashi Iwai 167613075510STakashi Iwai /* 167713075510STakashi Iwai * Wait until avail_min data becomes available 167813075510STakashi Iwai * Returns a negative error code if any error occurs during operation. 167913075510STakashi Iwai * The available space is stored on availp. When err = 0 and avail = 0 168013075510STakashi Iwai * on the capture stream, it indicates the stream is in DRAINING state. 168113075510STakashi Iwai */ 168213075510STakashi Iwai static int wait_for_avail_min(struct snd_pcm_substream *substream, 168313075510STakashi Iwai snd_pcm_uframes_t *availp) 168413075510STakashi Iwai { 168513075510STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 168613075510STakashi Iwai int is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 168713075510STakashi Iwai wait_queue_t wait; 168813075510STakashi Iwai int err = 0; 168913075510STakashi Iwai snd_pcm_uframes_t avail = 0; 169013075510STakashi Iwai long tout; 169113075510STakashi Iwai 169213075510STakashi Iwai init_waitqueue_entry(&wait, current); 169313075510STakashi Iwai add_wait_queue(&runtime->sleep, &wait); 169413075510STakashi Iwai for (;;) { 169513075510STakashi Iwai if (signal_pending(current)) { 169613075510STakashi Iwai err = -ERESTARTSYS; 169713075510STakashi Iwai break; 169813075510STakashi Iwai } 169913075510STakashi Iwai set_current_state(TASK_INTERRUPTIBLE); 170013075510STakashi Iwai snd_pcm_stream_unlock_irq(substream); 170113075510STakashi Iwai tout = schedule_timeout(msecs_to_jiffies(10000)); 170213075510STakashi Iwai snd_pcm_stream_lock_irq(substream); 170313075510STakashi Iwai switch (runtime->status->state) { 170413075510STakashi Iwai case SNDRV_PCM_STATE_SUSPENDED: 170513075510STakashi Iwai err = -ESTRPIPE; 170613075510STakashi Iwai goto _endloop; 170713075510STakashi Iwai case SNDRV_PCM_STATE_XRUN: 170813075510STakashi Iwai err = -EPIPE; 170913075510STakashi Iwai goto _endloop; 171013075510STakashi Iwai case SNDRV_PCM_STATE_DRAINING: 171113075510STakashi Iwai if (is_playback) 171213075510STakashi Iwai err = -EPIPE; 171313075510STakashi Iwai else 171413075510STakashi Iwai avail = 0; /* indicate draining */ 171513075510STakashi Iwai goto _endloop; 171613075510STakashi Iwai case SNDRV_PCM_STATE_OPEN: 171713075510STakashi Iwai case SNDRV_PCM_STATE_SETUP: 171813075510STakashi Iwai case SNDRV_PCM_STATE_DISCONNECTED: 171913075510STakashi Iwai err = -EBADFD; 172013075510STakashi Iwai goto _endloop; 172113075510STakashi Iwai } 172213075510STakashi Iwai if (!tout) { 172313075510STakashi Iwai snd_printd("%s write error (DMA or IRQ trouble?)\n", 172413075510STakashi Iwai is_playback ? "playback" : "capture"); 172513075510STakashi Iwai err = -EIO; 172613075510STakashi Iwai break; 172713075510STakashi Iwai } 172813075510STakashi Iwai if (is_playback) 172913075510STakashi Iwai avail = snd_pcm_playback_avail(runtime); 173013075510STakashi Iwai else 173113075510STakashi Iwai avail = snd_pcm_capture_avail(runtime); 173213075510STakashi Iwai if (avail >= runtime->control->avail_min) 173313075510STakashi Iwai break; 173413075510STakashi Iwai } 173513075510STakashi Iwai _endloop: 173613075510STakashi Iwai remove_wait_queue(&runtime->sleep, &wait); 173713075510STakashi Iwai *availp = avail; 173813075510STakashi Iwai return err; 173913075510STakashi Iwai } 174013075510STakashi Iwai 1741877211f5STakashi Iwai static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream, 17421da177e4SLinus Torvalds unsigned int hwoff, 17431da177e4SLinus Torvalds unsigned long data, unsigned int off, 17441da177e4SLinus Torvalds snd_pcm_uframes_t frames) 17451da177e4SLinus Torvalds { 1746877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 17471da177e4SLinus Torvalds int err; 17481da177e4SLinus Torvalds char __user *buf = (char __user *) data + frames_to_bytes(runtime, off); 17491da177e4SLinus Torvalds if (substream->ops->copy) { 17501da177e4SLinus Torvalds if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0) 17511da177e4SLinus Torvalds return err; 17521da177e4SLinus Torvalds } else { 17531da177e4SLinus Torvalds char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); 17541da177e4SLinus Torvalds if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames))) 17551da177e4SLinus Torvalds return -EFAULT; 17561da177e4SLinus Torvalds } 17571da177e4SLinus Torvalds return 0; 17581da177e4SLinus Torvalds } 17591da177e4SLinus Torvalds 1760877211f5STakashi Iwai typedef int (*transfer_f)(struct snd_pcm_substream *substream, unsigned int hwoff, 17611da177e4SLinus Torvalds unsigned long data, unsigned int off, 17621da177e4SLinus Torvalds snd_pcm_uframes_t size); 17631da177e4SLinus Torvalds 1764877211f5STakashi Iwai static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, 17651da177e4SLinus Torvalds unsigned long data, 17661da177e4SLinus Torvalds snd_pcm_uframes_t size, 17671da177e4SLinus Torvalds int nonblock, 17681da177e4SLinus Torvalds transfer_f transfer) 17691da177e4SLinus Torvalds { 1770877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 17711da177e4SLinus Torvalds snd_pcm_uframes_t xfer = 0; 17721da177e4SLinus Torvalds snd_pcm_uframes_t offset = 0; 17731da177e4SLinus Torvalds int err = 0; 17741da177e4SLinus Torvalds 17751da177e4SLinus Torvalds if (size == 0) 17761da177e4SLinus Torvalds return 0; 17771da177e4SLinus Torvalds 17781da177e4SLinus Torvalds snd_pcm_stream_lock_irq(substream); 17791da177e4SLinus Torvalds switch (runtime->status->state) { 17801da177e4SLinus Torvalds case SNDRV_PCM_STATE_PREPARED: 17811da177e4SLinus Torvalds case SNDRV_PCM_STATE_RUNNING: 17821da177e4SLinus Torvalds case SNDRV_PCM_STATE_PAUSED: 17831da177e4SLinus Torvalds break; 17841da177e4SLinus Torvalds case SNDRV_PCM_STATE_XRUN: 17851da177e4SLinus Torvalds err = -EPIPE; 17861da177e4SLinus Torvalds goto _end_unlock; 17871da177e4SLinus Torvalds case SNDRV_PCM_STATE_SUSPENDED: 17881da177e4SLinus Torvalds err = -ESTRPIPE; 17891da177e4SLinus Torvalds goto _end_unlock; 17901da177e4SLinus Torvalds default: 17911da177e4SLinus Torvalds err = -EBADFD; 17921da177e4SLinus Torvalds goto _end_unlock; 17931da177e4SLinus Torvalds } 17941da177e4SLinus Torvalds 17951250932eSJaroslav Kysela runtime->nowake = 1; 17961da177e4SLinus Torvalds while (size > 0) { 17971da177e4SLinus Torvalds snd_pcm_uframes_t frames, appl_ptr, appl_ofs; 17981da177e4SLinus Torvalds snd_pcm_uframes_t avail; 17991da177e4SLinus Torvalds snd_pcm_uframes_t cont; 180031e8960bSTakashi Iwai if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) 18011da177e4SLinus Torvalds snd_pcm_update_hw_ptr(substream); 18021da177e4SLinus Torvalds avail = snd_pcm_playback_avail(runtime); 180313075510STakashi Iwai if (!avail) { 18041da177e4SLinus Torvalds if (nonblock) { 18051da177e4SLinus Torvalds err = -EAGAIN; 18061da177e4SLinus Torvalds goto _end_unlock; 18071da177e4SLinus Torvalds } 180813075510STakashi Iwai err = wait_for_avail_min(substream, &avail); 180913075510STakashi Iwai if (err < 0) 18101da177e4SLinus Torvalds goto _end_unlock; 18111da177e4SLinus Torvalds } 18121da177e4SLinus Torvalds frames = size > avail ? avail : size; 18131da177e4SLinus Torvalds cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; 18141da177e4SLinus Torvalds if (frames > cont) 18151da177e4SLinus Torvalds frames = cont; 18167eaa943cSTakashi Iwai if (snd_BUG_ON(!frames)) { 18171250932eSJaroslav Kysela runtime->nowake = 0; 18187eaa943cSTakashi Iwai snd_pcm_stream_unlock_irq(substream); 18197eaa943cSTakashi Iwai return -EINVAL; 18207eaa943cSTakashi Iwai } 18211da177e4SLinus Torvalds appl_ptr = runtime->control->appl_ptr; 18221da177e4SLinus Torvalds appl_ofs = appl_ptr % runtime->buffer_size; 18231da177e4SLinus Torvalds snd_pcm_stream_unlock_irq(substream); 18241250932eSJaroslav Kysela err = transfer(substream, appl_ofs, data, offset, frames); 18251da177e4SLinus Torvalds snd_pcm_stream_lock_irq(substream); 18261250932eSJaroslav Kysela if (err < 0) 18271250932eSJaroslav Kysela goto _end_unlock; 18281da177e4SLinus Torvalds switch (runtime->status->state) { 18291da177e4SLinus Torvalds case SNDRV_PCM_STATE_XRUN: 18301da177e4SLinus Torvalds err = -EPIPE; 18311da177e4SLinus Torvalds goto _end_unlock; 18321da177e4SLinus Torvalds case SNDRV_PCM_STATE_SUSPENDED: 18331da177e4SLinus Torvalds err = -ESTRPIPE; 18341da177e4SLinus Torvalds goto _end_unlock; 18351da177e4SLinus Torvalds default: 18361da177e4SLinus Torvalds break; 18371da177e4SLinus Torvalds } 18381da177e4SLinus Torvalds appl_ptr += frames; 18391da177e4SLinus Torvalds if (appl_ptr >= runtime->boundary) 18401da177e4SLinus Torvalds appl_ptr -= runtime->boundary; 18411da177e4SLinus Torvalds runtime->control->appl_ptr = appl_ptr; 18421da177e4SLinus Torvalds if (substream->ops->ack) 18431da177e4SLinus Torvalds substream->ops->ack(substream); 18441da177e4SLinus Torvalds 18451da177e4SLinus Torvalds offset += frames; 18461da177e4SLinus Torvalds size -= frames; 18471da177e4SLinus Torvalds xfer += frames; 18481da177e4SLinus Torvalds if (runtime->status->state == SNDRV_PCM_STATE_PREPARED && 18491da177e4SLinus Torvalds snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) { 18501da177e4SLinus Torvalds err = snd_pcm_start(substream); 18511da177e4SLinus Torvalds if (err < 0) 18521da177e4SLinus Torvalds goto _end_unlock; 18531da177e4SLinus Torvalds } 18541da177e4SLinus Torvalds } 18551da177e4SLinus Torvalds _end_unlock: 18561250932eSJaroslav Kysela runtime->nowake = 0; 18571250932eSJaroslav Kysela if (xfer > 0 && err >= 0) 18581250932eSJaroslav Kysela snd_pcm_update_state(substream, runtime); 18591da177e4SLinus Torvalds snd_pcm_stream_unlock_irq(substream); 18601da177e4SLinus Torvalds return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; 18611da177e4SLinus Torvalds } 18621da177e4SLinus Torvalds 18637eaa943cSTakashi Iwai /* sanity-check for read/write methods */ 18647eaa943cSTakashi Iwai static int pcm_sanity_check(struct snd_pcm_substream *substream) 18657eaa943cSTakashi Iwai { 18667eaa943cSTakashi Iwai struct snd_pcm_runtime *runtime; 18677eaa943cSTakashi Iwai if (PCM_RUNTIME_CHECK(substream)) 18687eaa943cSTakashi Iwai return -ENXIO; 18697eaa943cSTakashi Iwai runtime = substream->runtime; 18707eaa943cSTakashi Iwai if (snd_BUG_ON(!substream->ops->copy && !runtime->dma_area)) 18717eaa943cSTakashi Iwai return -EINVAL; 18727eaa943cSTakashi Iwai if (runtime->status->state == SNDRV_PCM_STATE_OPEN) 18737eaa943cSTakashi Iwai return -EBADFD; 18747eaa943cSTakashi Iwai return 0; 18757eaa943cSTakashi Iwai } 18767eaa943cSTakashi Iwai 1877877211f5STakashi Iwai snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const void __user *buf, snd_pcm_uframes_t size) 18781da177e4SLinus Torvalds { 1879877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 18801da177e4SLinus Torvalds int nonblock; 18817eaa943cSTakashi Iwai int err; 18821da177e4SLinus Torvalds 18837eaa943cSTakashi Iwai err = pcm_sanity_check(substream); 18847eaa943cSTakashi Iwai if (err < 0) 18857eaa943cSTakashi Iwai return err; 18861da177e4SLinus Torvalds runtime = substream->runtime; 18870df63e44STakashi Iwai nonblock = !!(substream->f_flags & O_NONBLOCK); 18881da177e4SLinus Torvalds 18891da177e4SLinus Torvalds if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && 18901da177e4SLinus Torvalds runtime->channels > 1) 18911da177e4SLinus Torvalds return -EINVAL; 18921da177e4SLinus Torvalds return snd_pcm_lib_write1(substream, (unsigned long)buf, size, nonblock, 18931da177e4SLinus Torvalds snd_pcm_lib_write_transfer); 18941da177e4SLinus Torvalds } 18951da177e4SLinus Torvalds 1896e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_lib_write); 1897e88e8ae6STakashi Iwai 1898877211f5STakashi Iwai static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream, 18991da177e4SLinus Torvalds unsigned int hwoff, 19001da177e4SLinus Torvalds unsigned long data, unsigned int off, 19011da177e4SLinus Torvalds snd_pcm_uframes_t frames) 19021da177e4SLinus Torvalds { 1903877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 19041da177e4SLinus Torvalds int err; 19051da177e4SLinus Torvalds void __user **bufs = (void __user **)data; 19061da177e4SLinus Torvalds int channels = runtime->channels; 19071da177e4SLinus Torvalds int c; 19081da177e4SLinus Torvalds if (substream->ops->copy) { 19097eaa943cSTakashi Iwai if (snd_BUG_ON(!substream->ops->silence)) 19107eaa943cSTakashi Iwai return -EINVAL; 19111da177e4SLinus Torvalds for (c = 0; c < channels; ++c, ++bufs) { 19121da177e4SLinus Torvalds if (*bufs == NULL) { 19131da177e4SLinus Torvalds if ((err = substream->ops->silence(substream, c, hwoff, frames)) < 0) 19141da177e4SLinus Torvalds return err; 19151da177e4SLinus Torvalds } else { 19161da177e4SLinus Torvalds char __user *buf = *bufs + samples_to_bytes(runtime, off); 19171da177e4SLinus Torvalds if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0) 19181da177e4SLinus Torvalds return err; 19191da177e4SLinus Torvalds } 19201da177e4SLinus Torvalds } 19211da177e4SLinus Torvalds } else { 19221da177e4SLinus Torvalds /* default transfer behaviour */ 19231da177e4SLinus Torvalds size_t dma_csize = runtime->dma_bytes / channels; 19241da177e4SLinus Torvalds for (c = 0; c < channels; ++c, ++bufs) { 19251da177e4SLinus Torvalds char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff); 19261da177e4SLinus Torvalds if (*bufs == NULL) { 19271da177e4SLinus Torvalds snd_pcm_format_set_silence(runtime->format, hwbuf, frames); 19281da177e4SLinus Torvalds } else { 19291da177e4SLinus Torvalds char __user *buf = *bufs + samples_to_bytes(runtime, off); 19301da177e4SLinus Torvalds if (copy_from_user(hwbuf, buf, samples_to_bytes(runtime, frames))) 19311da177e4SLinus Torvalds return -EFAULT; 19321da177e4SLinus Torvalds } 19331da177e4SLinus Torvalds } 19341da177e4SLinus Torvalds } 19351da177e4SLinus Torvalds return 0; 19361da177e4SLinus Torvalds } 19371da177e4SLinus Torvalds 1938877211f5STakashi Iwai snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream, 19391da177e4SLinus Torvalds void __user **bufs, 19401da177e4SLinus Torvalds snd_pcm_uframes_t frames) 19411da177e4SLinus Torvalds { 1942877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 19431da177e4SLinus Torvalds int nonblock; 19447eaa943cSTakashi Iwai int err; 19451da177e4SLinus Torvalds 19467eaa943cSTakashi Iwai err = pcm_sanity_check(substream); 19477eaa943cSTakashi Iwai if (err < 0) 19487eaa943cSTakashi Iwai return err; 19491da177e4SLinus Torvalds runtime = substream->runtime; 19500df63e44STakashi Iwai nonblock = !!(substream->f_flags & O_NONBLOCK); 19511da177e4SLinus Torvalds 19521da177e4SLinus Torvalds if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) 19531da177e4SLinus Torvalds return -EINVAL; 19541da177e4SLinus Torvalds return snd_pcm_lib_write1(substream, (unsigned long)bufs, frames, 19551da177e4SLinus Torvalds nonblock, snd_pcm_lib_writev_transfer); 19561da177e4SLinus Torvalds } 19571da177e4SLinus Torvalds 1958e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_lib_writev); 1959e88e8ae6STakashi Iwai 1960877211f5STakashi Iwai static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream, 19611da177e4SLinus Torvalds unsigned int hwoff, 19621da177e4SLinus Torvalds unsigned long data, unsigned int off, 19631da177e4SLinus Torvalds snd_pcm_uframes_t frames) 19641da177e4SLinus Torvalds { 1965877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 19661da177e4SLinus Torvalds int err; 19671da177e4SLinus Torvalds char __user *buf = (char __user *) data + frames_to_bytes(runtime, off); 19681da177e4SLinus Torvalds if (substream->ops->copy) { 19691da177e4SLinus Torvalds if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0) 19701da177e4SLinus Torvalds return err; 19711da177e4SLinus Torvalds } else { 19721da177e4SLinus Torvalds char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); 19731da177e4SLinus Torvalds if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames))) 19741da177e4SLinus Torvalds return -EFAULT; 19751da177e4SLinus Torvalds } 19761da177e4SLinus Torvalds return 0; 19771da177e4SLinus Torvalds } 19781da177e4SLinus Torvalds 1979877211f5STakashi Iwai static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, 19801da177e4SLinus Torvalds unsigned long data, 19811da177e4SLinus Torvalds snd_pcm_uframes_t size, 19821da177e4SLinus Torvalds int nonblock, 19831da177e4SLinus Torvalds transfer_f transfer) 19841da177e4SLinus Torvalds { 1985877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 19861da177e4SLinus Torvalds snd_pcm_uframes_t xfer = 0; 19871da177e4SLinus Torvalds snd_pcm_uframes_t offset = 0; 19881da177e4SLinus Torvalds int err = 0; 19891da177e4SLinus Torvalds 19901da177e4SLinus Torvalds if (size == 0) 19911da177e4SLinus Torvalds return 0; 19921da177e4SLinus Torvalds 19931da177e4SLinus Torvalds snd_pcm_stream_lock_irq(substream); 19941da177e4SLinus Torvalds switch (runtime->status->state) { 19951da177e4SLinus Torvalds case SNDRV_PCM_STATE_PREPARED: 19961da177e4SLinus Torvalds if (size >= runtime->start_threshold) { 19971da177e4SLinus Torvalds err = snd_pcm_start(substream); 19981da177e4SLinus Torvalds if (err < 0) 19991da177e4SLinus Torvalds goto _end_unlock; 20001da177e4SLinus Torvalds } 20011da177e4SLinus Torvalds break; 20021da177e4SLinus Torvalds case SNDRV_PCM_STATE_DRAINING: 20031da177e4SLinus Torvalds case SNDRV_PCM_STATE_RUNNING: 20041da177e4SLinus Torvalds case SNDRV_PCM_STATE_PAUSED: 20051da177e4SLinus Torvalds break; 20061da177e4SLinus Torvalds case SNDRV_PCM_STATE_XRUN: 20071da177e4SLinus Torvalds err = -EPIPE; 20081da177e4SLinus Torvalds goto _end_unlock; 20091da177e4SLinus Torvalds case SNDRV_PCM_STATE_SUSPENDED: 20101da177e4SLinus Torvalds err = -ESTRPIPE; 20111da177e4SLinus Torvalds goto _end_unlock; 20121da177e4SLinus Torvalds default: 20131da177e4SLinus Torvalds err = -EBADFD; 20141da177e4SLinus Torvalds goto _end_unlock; 20151da177e4SLinus Torvalds } 20161da177e4SLinus Torvalds 20171250932eSJaroslav Kysela runtime->nowake = 1; 20181da177e4SLinus Torvalds while (size > 0) { 20191da177e4SLinus Torvalds snd_pcm_uframes_t frames, appl_ptr, appl_ofs; 20201da177e4SLinus Torvalds snd_pcm_uframes_t avail; 20211da177e4SLinus Torvalds snd_pcm_uframes_t cont; 202231e8960bSTakashi Iwai if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) 20231da177e4SLinus Torvalds snd_pcm_update_hw_ptr(substream); 20241da177e4SLinus Torvalds avail = snd_pcm_capture_avail(runtime); 2025d948035aSTakashi Iwai if (!avail) { 202613075510STakashi Iwai if (runtime->status->state == 202713075510STakashi Iwai SNDRV_PCM_STATE_DRAINING) { 202813075510STakashi Iwai snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); 20291da177e4SLinus Torvalds goto _end_unlock; 20301da177e4SLinus Torvalds } 20311da177e4SLinus Torvalds if (nonblock) { 20321da177e4SLinus Torvalds err = -EAGAIN; 20331da177e4SLinus Torvalds goto _end_unlock; 20341da177e4SLinus Torvalds } 203513075510STakashi Iwai err = wait_for_avail_min(substream, &avail); 203613075510STakashi Iwai if (err < 0) 20371da177e4SLinus Torvalds goto _end_unlock; 203813075510STakashi Iwai if (!avail) 203913075510STakashi Iwai continue; /* draining */ 20401da177e4SLinus Torvalds } 20411da177e4SLinus Torvalds frames = size > avail ? avail : size; 20421da177e4SLinus Torvalds cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; 20431da177e4SLinus Torvalds if (frames > cont) 20441da177e4SLinus Torvalds frames = cont; 20457eaa943cSTakashi Iwai if (snd_BUG_ON(!frames)) { 20461250932eSJaroslav Kysela runtime->nowake = 0; 20477eaa943cSTakashi Iwai snd_pcm_stream_unlock_irq(substream); 20487eaa943cSTakashi Iwai return -EINVAL; 20497eaa943cSTakashi Iwai } 20501da177e4SLinus Torvalds appl_ptr = runtime->control->appl_ptr; 20511da177e4SLinus Torvalds appl_ofs = appl_ptr % runtime->buffer_size; 20521da177e4SLinus Torvalds snd_pcm_stream_unlock_irq(substream); 20531250932eSJaroslav Kysela err = transfer(substream, appl_ofs, data, offset, frames); 20541da177e4SLinus Torvalds snd_pcm_stream_lock_irq(substream); 20551250932eSJaroslav Kysela if (err < 0) 20561250932eSJaroslav Kysela goto _end_unlock; 20571da177e4SLinus Torvalds switch (runtime->status->state) { 20581da177e4SLinus Torvalds case SNDRV_PCM_STATE_XRUN: 20591da177e4SLinus Torvalds err = -EPIPE; 20601da177e4SLinus Torvalds goto _end_unlock; 20611da177e4SLinus Torvalds case SNDRV_PCM_STATE_SUSPENDED: 20621da177e4SLinus Torvalds err = -ESTRPIPE; 20631da177e4SLinus Torvalds goto _end_unlock; 20641da177e4SLinus Torvalds default: 20651da177e4SLinus Torvalds break; 20661da177e4SLinus Torvalds } 20671da177e4SLinus Torvalds appl_ptr += frames; 20681da177e4SLinus Torvalds if (appl_ptr >= runtime->boundary) 20691da177e4SLinus Torvalds appl_ptr -= runtime->boundary; 20701da177e4SLinus Torvalds runtime->control->appl_ptr = appl_ptr; 20711da177e4SLinus Torvalds if (substream->ops->ack) 20721da177e4SLinus Torvalds substream->ops->ack(substream); 20731da177e4SLinus Torvalds 20741da177e4SLinus Torvalds offset += frames; 20751da177e4SLinus Torvalds size -= frames; 20761da177e4SLinus Torvalds xfer += frames; 20771da177e4SLinus Torvalds } 20781da177e4SLinus Torvalds _end_unlock: 20791250932eSJaroslav Kysela runtime->nowake = 0; 20801250932eSJaroslav Kysela if (xfer > 0 && err >= 0) 20811250932eSJaroslav Kysela snd_pcm_update_state(substream, runtime); 20821da177e4SLinus Torvalds snd_pcm_stream_unlock_irq(substream); 20831da177e4SLinus Torvalds return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; 20841da177e4SLinus Torvalds } 20851da177e4SLinus Torvalds 2086877211f5STakashi Iwai snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, void __user *buf, snd_pcm_uframes_t size) 20871da177e4SLinus Torvalds { 2088877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 20891da177e4SLinus Torvalds int nonblock; 20907eaa943cSTakashi Iwai int err; 20911da177e4SLinus Torvalds 20927eaa943cSTakashi Iwai err = pcm_sanity_check(substream); 20937eaa943cSTakashi Iwai if (err < 0) 20947eaa943cSTakashi Iwai return err; 20951da177e4SLinus Torvalds runtime = substream->runtime; 20960df63e44STakashi Iwai nonblock = !!(substream->f_flags & O_NONBLOCK); 20971da177e4SLinus Torvalds if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED) 20981da177e4SLinus Torvalds return -EINVAL; 20991da177e4SLinus Torvalds return snd_pcm_lib_read1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_read_transfer); 21001da177e4SLinus Torvalds } 21011da177e4SLinus Torvalds 2102e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_lib_read); 2103e88e8ae6STakashi Iwai 2104877211f5STakashi Iwai static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream, 21051da177e4SLinus Torvalds unsigned int hwoff, 21061da177e4SLinus Torvalds unsigned long data, unsigned int off, 21071da177e4SLinus Torvalds snd_pcm_uframes_t frames) 21081da177e4SLinus Torvalds { 2109877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 21101da177e4SLinus Torvalds int err; 21111da177e4SLinus Torvalds void __user **bufs = (void __user **)data; 21121da177e4SLinus Torvalds int channels = runtime->channels; 21131da177e4SLinus Torvalds int c; 21141da177e4SLinus Torvalds if (substream->ops->copy) { 21151da177e4SLinus Torvalds for (c = 0; c < channels; ++c, ++bufs) { 21161da177e4SLinus Torvalds char __user *buf; 21171da177e4SLinus Torvalds if (*bufs == NULL) 21181da177e4SLinus Torvalds continue; 21191da177e4SLinus Torvalds buf = *bufs + samples_to_bytes(runtime, off); 21201da177e4SLinus Torvalds if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0) 21211da177e4SLinus Torvalds return err; 21221da177e4SLinus Torvalds } 21231da177e4SLinus Torvalds } else { 21241da177e4SLinus Torvalds snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels; 21251da177e4SLinus Torvalds for (c = 0; c < channels; ++c, ++bufs) { 21261da177e4SLinus Torvalds char *hwbuf; 21271da177e4SLinus Torvalds char __user *buf; 21281da177e4SLinus Torvalds if (*bufs == NULL) 21291da177e4SLinus Torvalds continue; 21301da177e4SLinus Torvalds 21311da177e4SLinus Torvalds hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff); 21321da177e4SLinus Torvalds buf = *bufs + samples_to_bytes(runtime, off); 21331da177e4SLinus Torvalds if (copy_to_user(buf, hwbuf, samples_to_bytes(runtime, frames))) 21341da177e4SLinus Torvalds return -EFAULT; 21351da177e4SLinus Torvalds } 21361da177e4SLinus Torvalds } 21371da177e4SLinus Torvalds return 0; 21381da177e4SLinus Torvalds } 21391da177e4SLinus Torvalds 2140877211f5STakashi Iwai snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream, 21411da177e4SLinus Torvalds void __user **bufs, 21421da177e4SLinus Torvalds snd_pcm_uframes_t frames) 21431da177e4SLinus Torvalds { 2144877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 21451da177e4SLinus Torvalds int nonblock; 21467eaa943cSTakashi Iwai int err; 21471da177e4SLinus Torvalds 21487eaa943cSTakashi Iwai err = pcm_sanity_check(substream); 21497eaa943cSTakashi Iwai if (err < 0) 21507eaa943cSTakashi Iwai return err; 21511da177e4SLinus Torvalds runtime = substream->runtime; 21521da177e4SLinus Torvalds if (runtime->status->state == SNDRV_PCM_STATE_OPEN) 21531da177e4SLinus Torvalds return -EBADFD; 21541da177e4SLinus Torvalds 21550df63e44STakashi Iwai nonblock = !!(substream->f_flags & O_NONBLOCK); 21561da177e4SLinus Torvalds if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) 21571da177e4SLinus Torvalds return -EINVAL; 21581da177e4SLinus Torvalds return snd_pcm_lib_read1(substream, (unsigned long)bufs, frames, nonblock, snd_pcm_lib_readv_transfer); 21591da177e4SLinus Torvalds } 21601da177e4SLinus Torvalds 21611da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_lib_readv); 2162