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> 24174cd4b1SIngo Molnar #include <linux/sched/signal.h> 251da177e4SLinus Torvalds #include <linux/time.h> 263f7440a6STakashi Iwai #include <linux/math64.h> 27d81a6d71SPaul Gortmaker #include <linux/export.h> 281da177e4SLinus Torvalds #include <sound/core.h> 291da177e4SLinus Torvalds #include <sound/control.h> 302d3391ecSTakashi Iwai #include <sound/tlv.h> 311da177e4SLinus Torvalds #include <sound/info.h> 321da177e4SLinus Torvalds #include <sound/pcm.h> 331da177e4SLinus Torvalds #include <sound/pcm_params.h> 341da177e4SLinus Torvalds #include <sound/timer.h> 351da177e4SLinus Torvalds 362c4842d3STakashi Sakamoto #include "pcm_local.h" 372c4842d3STakashi Sakamoto 38f5914908STakashi Iwai #ifdef CONFIG_SND_PCM_XRUN_DEBUG 39f5914908STakashi Iwai #define CREATE_TRACE_POINTS 40f5914908STakashi Iwai #include "pcm_trace.h" 41f5914908STakashi Iwai #else 42f5914908STakashi Iwai #define trace_hwptr(substream, pos, in_interrupt) 43f5914908STakashi Iwai #define trace_xrun(substream) 44f5914908STakashi Iwai #define trace_hw_ptr_error(substream, reason) 45f5914908STakashi Iwai #endif 46f5914908STakashi Iwai 471da177e4SLinus Torvalds /* 481da177e4SLinus Torvalds * fill ring buffer with silence 491da177e4SLinus Torvalds * runtime->silence_start: starting pointer to silence area 501da177e4SLinus Torvalds * runtime->silence_filled: size filled with silence 511da177e4SLinus Torvalds * runtime->silence_threshold: threshold from application 521da177e4SLinus Torvalds * runtime->silence_size: maximal size from application 531da177e4SLinus Torvalds * 541da177e4SLinus Torvalds * when runtime->silence_size >= runtime->boundary - fill processed area with silence immediately 551da177e4SLinus Torvalds */ 56877211f5STakashi Iwai void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_uframes_t new_hw_ptr) 571da177e4SLinus Torvalds { 58877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 591da177e4SLinus Torvalds snd_pcm_uframes_t frames, ofs, transfer; 6029d1a873STakashi Iwai char *hwbuf; 6129d1a873STakashi Iwai int err; 621da177e4SLinus Torvalds 631da177e4SLinus Torvalds if (runtime->silence_size < runtime->boundary) { 641da177e4SLinus Torvalds snd_pcm_sframes_t noise_dist, n; 651da177e4SLinus Torvalds if (runtime->silence_start != runtime->control->appl_ptr) { 661da177e4SLinus Torvalds n = runtime->control->appl_ptr - runtime->silence_start; 671da177e4SLinus Torvalds if (n < 0) 681da177e4SLinus Torvalds n += runtime->boundary; 691da177e4SLinus Torvalds if ((snd_pcm_uframes_t)n < runtime->silence_filled) 701da177e4SLinus Torvalds runtime->silence_filled -= n; 711da177e4SLinus Torvalds else 721da177e4SLinus Torvalds runtime->silence_filled = 0; 731da177e4SLinus Torvalds runtime->silence_start = runtime->control->appl_ptr; 741da177e4SLinus Torvalds } 75235475cbSTakashi Iwai if (runtime->silence_filled >= runtime->buffer_size) 761da177e4SLinus Torvalds return; 771da177e4SLinus Torvalds noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silence_filled; 781da177e4SLinus Torvalds if (noise_dist >= (snd_pcm_sframes_t) runtime->silence_threshold) 791da177e4SLinus Torvalds return; 801da177e4SLinus Torvalds frames = runtime->silence_threshold - noise_dist; 811da177e4SLinus Torvalds if (frames > runtime->silence_size) 821da177e4SLinus Torvalds frames = runtime->silence_size; 831da177e4SLinus Torvalds } else { 841da177e4SLinus Torvalds if (new_hw_ptr == ULONG_MAX) { /* initialization */ 851da177e4SLinus Torvalds snd_pcm_sframes_t avail = snd_pcm_playback_hw_avail(runtime); 869e216e8aSJaroslav Kysela if (avail > runtime->buffer_size) 879e216e8aSJaroslav Kysela avail = runtime->buffer_size; 881da177e4SLinus Torvalds runtime->silence_filled = avail > 0 ? avail : 0; 891da177e4SLinus Torvalds runtime->silence_start = (runtime->status->hw_ptr + 901da177e4SLinus Torvalds runtime->silence_filled) % 911da177e4SLinus Torvalds runtime->boundary; 921da177e4SLinus Torvalds } else { 931da177e4SLinus Torvalds ofs = runtime->status->hw_ptr; 941da177e4SLinus Torvalds frames = new_hw_ptr - ofs; 951da177e4SLinus Torvalds if ((snd_pcm_sframes_t)frames < 0) 961da177e4SLinus Torvalds frames += runtime->boundary; 971da177e4SLinus Torvalds runtime->silence_filled -= frames; 981da177e4SLinus Torvalds if ((snd_pcm_sframes_t)runtime->silence_filled < 0) { 991da177e4SLinus Torvalds runtime->silence_filled = 0; 1009a826ddbSClemens Ladisch runtime->silence_start = new_hw_ptr; 1011da177e4SLinus Torvalds } else { 1029a826ddbSClemens Ladisch runtime->silence_start = ofs; 1031da177e4SLinus Torvalds } 1041da177e4SLinus Torvalds } 1051da177e4SLinus Torvalds frames = runtime->buffer_size - runtime->silence_filled; 1061da177e4SLinus Torvalds } 1077eaa943cSTakashi Iwai if (snd_BUG_ON(frames > runtime->buffer_size)) 1087eaa943cSTakashi Iwai return; 1091da177e4SLinus Torvalds if (frames == 0) 1101da177e4SLinus Torvalds return; 1119a826ddbSClemens Ladisch ofs = runtime->silence_start % runtime->buffer_size; 1121da177e4SLinus Torvalds while (frames > 0) { 1131da177e4SLinus Torvalds transfer = ofs + frames > runtime->buffer_size ? runtime->buffer_size - ofs : frames; 1141da177e4SLinus Torvalds if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || 1151da177e4SLinus Torvalds runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) { 11629d1a873STakashi Iwai if (substream->ops->fill_silence) { 11729d1a873STakashi Iwai err = substream->ops->fill_silence(substream, 0, 11829d1a873STakashi Iwai frames_to_bytes(runtime, ofs), 11929d1a873STakashi Iwai frames_to_bytes(runtime, transfer)); 12029d1a873STakashi Iwai snd_BUG_ON(err < 0); 1211da177e4SLinus Torvalds } else { 12229d1a873STakashi Iwai hwbuf = runtime->dma_area + frames_to_bytes(runtime, ofs); 1231da177e4SLinus Torvalds snd_pcm_format_set_silence(runtime->format, hwbuf, transfer * runtime->channels); 1241da177e4SLinus Torvalds } 1251da177e4SLinus Torvalds } else { 1261da177e4SLinus Torvalds unsigned int c; 1271da177e4SLinus Torvalds unsigned int channels = runtime->channels; 12829d1a873STakashi Iwai if (substream->ops->fill_silence) { 1291da177e4SLinus Torvalds for (c = 0; c < channels; ++c) { 13029d1a873STakashi Iwai err = substream->ops->fill_silence(substream, c, 13129d1a873STakashi Iwai samples_to_bytes(runtime, ofs), 13229d1a873STakashi Iwai samples_to_bytes(runtime, transfer)); 13329d1a873STakashi Iwai snd_BUG_ON(err < 0); 13429d1a873STakashi Iwai } 1351da177e4SLinus Torvalds } else { 1361da177e4SLinus Torvalds size_t dma_csize = runtime->dma_bytes / channels; 1371da177e4SLinus Torvalds for (c = 0; c < channels; ++c) { 13829d1a873STakashi Iwai hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, ofs); 1391da177e4SLinus Torvalds snd_pcm_format_set_silence(runtime->format, hwbuf, transfer); 1401da177e4SLinus Torvalds } 1411da177e4SLinus Torvalds } 1421da177e4SLinus Torvalds } 1431da177e4SLinus Torvalds runtime->silence_filled += transfer; 1441da177e4SLinus Torvalds frames -= transfer; 1451da177e4SLinus Torvalds ofs = 0; 1461da177e4SLinus Torvalds } 1471da177e4SLinus Torvalds } 1481da177e4SLinus Torvalds 149acb03d44SEliot Blennerhassett #ifdef CONFIG_SND_DEBUG 150acb03d44SEliot Blennerhassett void snd_pcm_debug_name(struct snd_pcm_substream *substream, 151c0070110STakashi Iwai char *name, size_t len) 1521da177e4SLinus Torvalds { 153c0070110STakashi Iwai snprintf(name, len, "pcmC%dD%d%c:%d", 1541da177e4SLinus Torvalds substream->pcm->card->number, 1551da177e4SLinus Torvalds substream->pcm->device, 156c0070110STakashi Iwai substream->stream ? 'c' : 'p', 157c0070110STakashi Iwai substream->number); 158c0070110STakashi Iwai } 159acb03d44SEliot Blennerhassett EXPORT_SYMBOL(snd_pcm_debug_name); 160acb03d44SEliot Blennerhassett #endif 161c0070110STakashi Iwai 1624d96eb25SJaroslav Kysela #define XRUN_DEBUG_BASIC (1<<0) 1634d96eb25SJaroslav Kysela #define XRUN_DEBUG_STACK (1<<1) /* dump also stack */ 1644d96eb25SJaroslav Kysela #define XRUN_DEBUG_JIFFIESCHECK (1<<2) /* do jiffies check */ 1654d96eb25SJaroslav Kysela 1664d96eb25SJaroslav Kysela #ifdef CONFIG_SND_PCM_XRUN_DEBUG 1674d96eb25SJaroslav Kysela 1684d96eb25SJaroslav Kysela #define xrun_debug(substream, mask) \ 1694d96eb25SJaroslav Kysela ((substream)->pstr->xrun_debug & (mask)) 1700f17014bSJarkko Nikula #else 1710f17014bSJarkko Nikula #define xrun_debug(substream, mask) 0 1720f17014bSJarkko Nikula #endif 1734d96eb25SJaroslav Kysela 1744d96eb25SJaroslav Kysela #define dump_stack_on_xrun(substream) do { \ 1754d96eb25SJaroslav Kysela if (xrun_debug(substream, XRUN_DEBUG_STACK)) \ 1764d96eb25SJaroslav Kysela dump_stack(); \ 1774d96eb25SJaroslav Kysela } while (0) 1784d96eb25SJaroslav Kysela 1791da177e4SLinus Torvalds static void xrun(struct snd_pcm_substream *substream) 1801da177e4SLinus Torvalds { 18113f040f9SJaroslav Kysela struct snd_pcm_runtime *runtime = substream->runtime; 18213f040f9SJaroslav Kysela 183f5914908STakashi Iwai trace_xrun(substream); 18413f040f9SJaroslav Kysela if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) 18513f040f9SJaroslav Kysela snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); 1861da177e4SLinus Torvalds snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); 187741b20cfSJaroslav Kysela if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { 188c0070110STakashi Iwai char name[16]; 189acb03d44SEliot Blennerhassett snd_pcm_debug_name(substream, name, sizeof(name)); 19009e56df8STakashi Iwai pcm_warn(substream->pcm, "XRUN: %s\n", name); 191ed3da3d9STakashi Iwai dump_stack_on_xrun(substream); 1921da177e4SLinus Torvalds } 1931da177e4SLinus Torvalds } 1941da177e4SLinus Torvalds 1950f17014bSJarkko Nikula #ifdef CONFIG_SND_PCM_XRUN_DEBUG 196f5914908STakashi Iwai #define hw_ptr_error(substream, in_interrupt, reason, fmt, args...) \ 1974d96eb25SJaroslav Kysela do { \ 198f5914908STakashi Iwai trace_hw_ptr_error(substream, reason); \ 1994d96eb25SJaroslav Kysela if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \ 200f5914908STakashi Iwai pr_err_ratelimited("ALSA: PCM: [%c] " reason ": " fmt, \ 201f5914908STakashi Iwai (in_interrupt) ? 'Q' : 'P', ##args); \ 2024d96eb25SJaroslav Kysela dump_stack_on_xrun(substream); \ 2034d96eb25SJaroslav Kysela } \ 2044d96eb25SJaroslav Kysela } while (0) 2054d96eb25SJaroslav Kysela 2064d96eb25SJaroslav Kysela #else /* ! CONFIG_SND_PCM_XRUN_DEBUG */ 2074d96eb25SJaroslav Kysela 2084d96eb25SJaroslav Kysela #define hw_ptr_error(substream, fmt, args...) do { } while (0) 2094d96eb25SJaroslav Kysela 2104d96eb25SJaroslav Kysela #endif 2114d96eb25SJaroslav Kysela 2121250932eSJaroslav Kysela int snd_pcm_update_state(struct snd_pcm_substream *substream, 213877211f5STakashi Iwai struct snd_pcm_runtime *runtime) 2141da177e4SLinus Torvalds { 2151da177e4SLinus Torvalds snd_pcm_uframes_t avail; 2161da177e4SLinus Torvalds 2171da177e4SLinus Torvalds if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 2181da177e4SLinus Torvalds avail = snd_pcm_playback_avail(runtime); 2191da177e4SLinus Torvalds else 2201da177e4SLinus Torvalds avail = snd_pcm_capture_avail(runtime); 2211da177e4SLinus Torvalds if (avail > runtime->avail_max) 2221da177e4SLinus Torvalds runtime->avail_max = avail; 2234cdc115fSTakashi Iwai if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { 2244cdc115fSTakashi Iwai if (avail >= runtime->buffer_size) { 2251da177e4SLinus Torvalds snd_pcm_drain_done(substream); 2264cdc115fSTakashi Iwai return -EPIPE; 2274cdc115fSTakashi Iwai } 2284cdc115fSTakashi Iwai } else { 2294cdc115fSTakashi Iwai if (avail >= runtime->stop_threshold) { 2301da177e4SLinus Torvalds xrun(substream); 2311da177e4SLinus Torvalds return -EPIPE; 2321da177e4SLinus Torvalds } 2334cdc115fSTakashi Iwai } 2345daeba34SDavid Dillow if (runtime->twake) { 2355daeba34SDavid Dillow if (avail >= runtime->twake) 2365daeba34SDavid Dillow wake_up(&runtime->tsleep); 2375daeba34SDavid Dillow } else if (avail >= runtime->control->avail_min) 2385daeba34SDavid Dillow wake_up(&runtime->sleep); 2391da177e4SLinus Torvalds return 0; 2401da177e4SLinus Torvalds } 2411da177e4SLinus Torvalds 2423179f620SPierre-Louis Bossart static void update_audio_tstamp(struct snd_pcm_substream *substream, 2433179f620SPierre-Louis Bossart struct timespec *curr_tstamp, 2443179f620SPierre-Louis Bossart struct timespec *audio_tstamp) 2453179f620SPierre-Louis Bossart { 2463179f620SPierre-Louis Bossart struct snd_pcm_runtime *runtime = substream->runtime; 2473179f620SPierre-Louis Bossart u64 audio_frames, audio_nsecs; 2483179f620SPierre-Louis Bossart struct timespec driver_tstamp; 2493179f620SPierre-Louis Bossart 2503179f620SPierre-Louis Bossart if (runtime->tstamp_mode != SNDRV_PCM_TSTAMP_ENABLE) 2513179f620SPierre-Louis Bossart return; 2523179f620SPierre-Louis Bossart 2533179f620SPierre-Louis Bossart if (!(substream->ops->get_time_info) || 2543179f620SPierre-Louis Bossart (runtime->audio_tstamp_report.actual_type == 2553179f620SPierre-Louis Bossart SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)) { 2563179f620SPierre-Louis Bossart 2573179f620SPierre-Louis Bossart /* 2583179f620SPierre-Louis Bossart * provide audio timestamp derived from pointer position 2593179f620SPierre-Louis Bossart * add delay only if requested 2603179f620SPierre-Louis Bossart */ 2613179f620SPierre-Louis Bossart 2623179f620SPierre-Louis Bossart audio_frames = runtime->hw_ptr_wrap + runtime->status->hw_ptr; 2633179f620SPierre-Louis Bossart 2643179f620SPierre-Louis Bossart if (runtime->audio_tstamp_config.report_delay) { 2653179f620SPierre-Louis Bossart if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 2663179f620SPierre-Louis Bossart audio_frames -= runtime->delay; 2673179f620SPierre-Louis Bossart else 2683179f620SPierre-Louis Bossart audio_frames += runtime->delay; 2693179f620SPierre-Louis Bossart } 2703179f620SPierre-Louis Bossart audio_nsecs = div_u64(audio_frames * 1000000000LL, 2713179f620SPierre-Louis Bossart runtime->rate); 2723179f620SPierre-Louis Bossart *audio_tstamp = ns_to_timespec(audio_nsecs); 2733179f620SPierre-Louis Bossart } 2743179f620SPierre-Louis Bossart runtime->status->audio_tstamp = *audio_tstamp; 2753179f620SPierre-Louis Bossart runtime->status->tstamp = *curr_tstamp; 2763179f620SPierre-Louis Bossart 2773179f620SPierre-Louis Bossart /* 2783179f620SPierre-Louis Bossart * re-take a driver timestamp to let apps detect if the reference tstamp 2793179f620SPierre-Louis Bossart * read by low-level hardware was provided with a delay 2803179f620SPierre-Louis Bossart */ 2813179f620SPierre-Louis Bossart snd_pcm_gettime(substream->runtime, (struct timespec *)&driver_tstamp); 2823179f620SPierre-Louis Bossart runtime->driver_tstamp = driver_tstamp; 2833179f620SPierre-Louis Bossart } 2843179f620SPierre-Louis Bossart 285f240406bSJaroslav Kysela static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, 286f240406bSJaroslav Kysela unsigned int in_interrupt) 2871da177e4SLinus Torvalds { 288877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 2891da177e4SLinus Torvalds snd_pcm_uframes_t pos; 290f240406bSJaroslav Kysela snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base; 291bbf6ad13SJaroslav Kysela snd_pcm_sframes_t hdelta, delta; 292bbf6ad13SJaroslav Kysela unsigned long jdelta; 2933509a03fSPierre-Louis Bossart unsigned long curr_jiffies; 2943509a03fSPierre-Louis Bossart struct timespec curr_tstamp; 2954eeaaeaeSPierre-Louis Bossart struct timespec audio_tstamp; 2960e8014d7SPierre-Louis Bossart int crossed_boundary = 0; 2971da177e4SLinus Torvalds 298bbf6ad13SJaroslav Kysela old_hw_ptr = runtime->status->hw_ptr; 2993509a03fSPierre-Louis Bossart 3003509a03fSPierre-Louis Bossart /* 3013509a03fSPierre-Louis Bossart * group pointer, time and jiffies reads to allow for more 3023509a03fSPierre-Louis Bossart * accurate correlations/corrections. 3033509a03fSPierre-Louis Bossart * The values are stored at the end of this routine after 3043509a03fSPierre-Louis Bossart * corrections for hw_ptr position 3053509a03fSPierre-Louis Bossart */ 306f240406bSJaroslav Kysela pos = substream->ops->pointer(substream); 3073509a03fSPierre-Louis Bossart curr_jiffies = jiffies; 3084eeaaeaeSPierre-Louis Bossart if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { 3093179f620SPierre-Louis Bossart if ((substream->ops->get_time_info) && 3103179f620SPierre-Louis Bossart (runtime->audio_tstamp_config.type_requested != SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)) { 3113179f620SPierre-Louis Bossart substream->ops->get_time_info(substream, &curr_tstamp, 3123179f620SPierre-Louis Bossart &audio_tstamp, 3133179f620SPierre-Louis Bossart &runtime->audio_tstamp_config, 3143179f620SPierre-Louis Bossart &runtime->audio_tstamp_report); 3153509a03fSPierre-Louis Bossart 3163179f620SPierre-Louis Bossart /* re-test in case tstamp type is not supported in hardware and was demoted to DEFAULT */ 3173179f620SPierre-Louis Bossart if (runtime->audio_tstamp_report.actual_type == SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT) 3183179f620SPierre-Louis Bossart snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp); 3193179f620SPierre-Louis Bossart } else 3203179f620SPierre-Louis Bossart snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp); 3214eeaaeaeSPierre-Louis Bossart } 3224eeaaeaeSPierre-Louis Bossart 3231da177e4SLinus Torvalds if (pos == SNDRV_PCM_POS_XRUN) { 3241da177e4SLinus Torvalds xrun(substream); 3251da177e4SLinus Torvalds return -EPIPE; 3261da177e4SLinus Torvalds } 327f240406bSJaroslav Kysela if (pos >= runtime->buffer_size) { 32809e56df8STakashi Iwai if (printk_ratelimit()) { 329cedb8118STakashi Iwai char name[16]; 330acb03d44SEliot Blennerhassett snd_pcm_debug_name(substream, name, sizeof(name)); 33109e56df8STakashi Iwai pcm_err(substream->pcm, 3320ab1ace8STakashi Iwai "invalid position: %s, pos = %ld, buffer size = %ld, period size = %ld\n", 333f240406bSJaroslav Kysela name, pos, runtime->buffer_size, 334f240406bSJaroslav Kysela runtime->period_size); 335cedb8118STakashi Iwai } 336f240406bSJaroslav Kysela pos = 0; 337f240406bSJaroslav Kysela } 338f240406bSJaroslav Kysela pos -= pos % runtime->min_align; 339f5914908STakashi Iwai trace_hwptr(substream, pos, in_interrupt); 340ed3da3d9STakashi Iwai hw_base = runtime->hw_ptr_base; 341ed3da3d9STakashi Iwai new_hw_ptr = hw_base + pos; 342f240406bSJaroslav Kysela if (in_interrupt) { 343f240406bSJaroslav Kysela /* we know that one period was processed */ 344f240406bSJaroslav Kysela /* delta = "expected next hw_ptr" for in_interrupt != 0 */ 345e7636925SJaroslav Kysela delta = runtime->hw_ptr_interrupt + runtime->period_size; 346f240406bSJaroslav Kysela if (delta > new_hw_ptr) { 347bd76af0fSJaroslav Kysela /* check for double acknowledged interrupts */ 3483509a03fSPierre-Louis Bossart hdelta = curr_jiffies - runtime->hw_ptr_jiffies; 34913a98839SKoro Chen if (hdelta > runtime->hw_ptr_buffer_jiffies/2 + 1) { 350f240406bSJaroslav Kysela hw_base += runtime->buffer_size; 3510e8014d7SPierre-Louis Bossart if (hw_base >= runtime->boundary) { 352f240406bSJaroslav Kysela hw_base = 0; 3530e8014d7SPierre-Louis Bossart crossed_boundary++; 3540e8014d7SPierre-Louis Bossart } 355f240406bSJaroslav Kysela new_hw_ptr = hw_base + pos; 356f240406bSJaroslav Kysela goto __delta; 357ded652f7STakashi Iwai } 358f240406bSJaroslav Kysela } 359bd76af0fSJaroslav Kysela } 360f240406bSJaroslav Kysela /* new_hw_ptr might be lower than old_hw_ptr in case when */ 361f240406bSJaroslav Kysela /* pointer crosses the end of the ring buffer */ 362f240406bSJaroslav Kysela if (new_hw_ptr < old_hw_ptr) { 363ed3da3d9STakashi Iwai hw_base += runtime->buffer_size; 3640e8014d7SPierre-Louis Bossart if (hw_base >= runtime->boundary) { 365ed3da3d9STakashi Iwai hw_base = 0; 3660e8014d7SPierre-Louis Bossart crossed_boundary++; 3670e8014d7SPierre-Louis Bossart } 368ed3da3d9STakashi Iwai new_hw_ptr = hw_base + pos; 3691da177e4SLinus Torvalds } 370f240406bSJaroslav Kysela __delta: 371b406e610SClemens Ladisch delta = new_hw_ptr - old_hw_ptr; 372b406e610SClemens Ladisch if (delta < 0) 373b406e610SClemens Ladisch delta += runtime->boundary; 374ab69a490SClemens Ladisch 37559ff878fSClemens Ladisch if (runtime->no_period_wakeup) { 37612ff414eSKelly Anderson snd_pcm_sframes_t xrun_threshold; 37759ff878fSClemens Ladisch /* 37859ff878fSClemens Ladisch * Without regular period interrupts, we have to check 37959ff878fSClemens Ladisch * the elapsed time to detect xruns. 38059ff878fSClemens Ladisch */ 3813509a03fSPierre-Louis Bossart jdelta = curr_jiffies - runtime->hw_ptr_jiffies; 38247228e48SClemens Ladisch if (jdelta < runtime->hw_ptr_buffer_jiffies / 2) 38347228e48SClemens Ladisch goto no_delta_check; 38459ff878fSClemens Ladisch hdelta = jdelta - delta * HZ / runtime->rate; 38512ff414eSKelly Anderson xrun_threshold = runtime->hw_ptr_buffer_jiffies / 2 + 1; 38612ff414eSKelly Anderson while (hdelta > xrun_threshold) { 38759ff878fSClemens Ladisch delta += runtime->buffer_size; 38859ff878fSClemens Ladisch hw_base += runtime->buffer_size; 3890e8014d7SPierre-Louis Bossart if (hw_base >= runtime->boundary) { 39059ff878fSClemens Ladisch hw_base = 0; 3910e8014d7SPierre-Louis Bossart crossed_boundary++; 3920e8014d7SPierre-Louis Bossart } 39359ff878fSClemens Ladisch new_hw_ptr = hw_base + pos; 39459ff878fSClemens Ladisch hdelta -= runtime->hw_ptr_buffer_jiffies; 39559ff878fSClemens Ladisch } 396ab69a490SClemens Ladisch goto no_delta_check; 39759ff878fSClemens Ladisch } 398ab69a490SClemens Ladisch 399f240406bSJaroslav Kysela /* something must be really wrong */ 4007b3a177bSJaroslav Kysela if (delta >= runtime->buffer_size + runtime->period_size) { 401f5914908STakashi Iwai hw_ptr_error(substream, in_interrupt, "Unexpected hw_ptr", 402f5914908STakashi Iwai "(stream=%i, pos=%ld, new_hw_ptr=%ld, old_hw_ptr=%ld)\n", 403f240406bSJaroslav Kysela substream->stream, (long)pos, 404f240406bSJaroslav Kysela (long)new_hw_ptr, (long)old_hw_ptr); 405f240406bSJaroslav Kysela return 0; 4061da177e4SLinus Torvalds } 407c87d9732STakashi Iwai 408c87d9732STakashi Iwai /* Do jiffies check only in xrun_debug mode */ 409741b20cfSJaroslav Kysela if (!xrun_debug(substream, XRUN_DEBUG_JIFFIESCHECK)) 410c87d9732STakashi Iwai goto no_jiffies_check; 411c87d9732STakashi Iwai 4123e5b5016STakashi Iwai /* Skip the jiffies check for hardwares with BATCH flag. 4133e5b5016STakashi Iwai * Such hardware usually just increases the position at each IRQ, 4143e5b5016STakashi Iwai * thus it can't give any strange position. 4153e5b5016STakashi Iwai */ 4163e5b5016STakashi Iwai if (runtime->hw.info & SNDRV_PCM_INFO_BATCH) 4173e5b5016STakashi Iwai goto no_jiffies_check; 418f240406bSJaroslav Kysela hdelta = delta; 419a4444da3SJaroslav Kysela if (hdelta < runtime->delay) 420a4444da3SJaroslav Kysela goto no_jiffies_check; 421a4444da3SJaroslav Kysela hdelta -= runtime->delay; 4223509a03fSPierre-Louis Bossart jdelta = curr_jiffies - runtime->hw_ptr_jiffies; 423bbf6ad13SJaroslav Kysela if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) { 424bbf6ad13SJaroslav Kysela delta = jdelta / 425bbf6ad13SJaroslav Kysela (((runtime->period_size * HZ) / runtime->rate) 426bbf6ad13SJaroslav Kysela + HZ/100); 427f240406bSJaroslav Kysela /* move new_hw_ptr according jiffies not pos variable */ 428f240406bSJaroslav Kysela new_hw_ptr = old_hw_ptr; 429ed69c6a8SJaroslav Kysela hw_base = delta; 430f240406bSJaroslav Kysela /* use loop to avoid checks for delta overflows */ 431f240406bSJaroslav Kysela /* the delta value is small or zero in most cases */ 432f240406bSJaroslav Kysela while (delta > 0) { 433f240406bSJaroslav Kysela new_hw_ptr += runtime->period_size; 4340e8014d7SPierre-Louis Bossart if (new_hw_ptr >= runtime->boundary) { 435f240406bSJaroslav Kysela new_hw_ptr -= runtime->boundary; 4360e8014d7SPierre-Louis Bossart crossed_boundary--; 4370e8014d7SPierre-Louis Bossart } 438f240406bSJaroslav Kysela delta--; 439f240406bSJaroslav Kysela } 440f240406bSJaroslav Kysela /* align hw_base to buffer_size */ 441f5914908STakashi Iwai hw_ptr_error(substream, in_interrupt, "hw_ptr skipping", 442f5914908STakashi Iwai "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu/%lu, hw_ptr=%ld/%ld)\n", 443bbf6ad13SJaroslav Kysela (long)pos, (long)hdelta, 444bbf6ad13SJaroslav Kysela (long)runtime->period_size, jdelta, 445ed69c6a8SJaroslav Kysela ((hdelta * HZ) / runtime->rate), hw_base, 446f240406bSJaroslav Kysela (unsigned long)old_hw_ptr, 447f240406bSJaroslav Kysela (unsigned long)new_hw_ptr); 448ed69c6a8SJaroslav Kysela /* reset values to proper state */ 449ed69c6a8SJaroslav Kysela delta = 0; 450ed69c6a8SJaroslav Kysela hw_base = new_hw_ptr - (new_hw_ptr % runtime->buffer_size); 451bbf6ad13SJaroslav Kysela } 4523e5b5016STakashi Iwai no_jiffies_check: 453bbf6ad13SJaroslav Kysela if (delta > runtime->period_size + runtime->period_size / 2) { 454f5914908STakashi Iwai hw_ptr_error(substream, in_interrupt, 455f5914908STakashi Iwai "Lost interrupts?", 456f5914908STakashi Iwai "(stream=%i, delta=%ld, new_hw_ptr=%ld, old_hw_ptr=%ld)\n", 457ed3da3d9STakashi Iwai substream->stream, (long)delta, 458f240406bSJaroslav Kysela (long)new_hw_ptr, 459f240406bSJaroslav Kysela (long)old_hw_ptr); 4601da177e4SLinus Torvalds } 461f240406bSJaroslav Kysela 462ab69a490SClemens Ladisch no_delta_check: 4633179f620SPierre-Louis Bossart if (runtime->status->hw_ptr == new_hw_ptr) { 4643179f620SPierre-Louis Bossart update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp); 465f240406bSJaroslav Kysela return 0; 4663179f620SPierre-Louis Bossart } 467ab1863fcSTakashi Iwai 4681da177e4SLinus Torvalds if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && 4691da177e4SLinus Torvalds runtime->silence_size > 0) 4701da177e4SLinus Torvalds snd_pcm_playback_silence(substream, new_hw_ptr); 4711da177e4SLinus Torvalds 472e7636925SJaroslav Kysela if (in_interrupt) { 473ead4046bSClemens Ladisch delta = new_hw_ptr - runtime->hw_ptr_interrupt; 474ead4046bSClemens Ladisch if (delta < 0) 475ead4046bSClemens Ladisch delta += runtime->boundary; 476ead4046bSClemens Ladisch delta -= (snd_pcm_uframes_t)delta % runtime->period_size; 477ead4046bSClemens Ladisch runtime->hw_ptr_interrupt += delta; 478ead4046bSClemens Ladisch if (runtime->hw_ptr_interrupt >= runtime->boundary) 479ead4046bSClemens Ladisch runtime->hw_ptr_interrupt -= runtime->boundary; 480e7636925SJaroslav Kysela } 481ed3da3d9STakashi Iwai runtime->hw_ptr_base = hw_base; 4821da177e4SLinus Torvalds runtime->status->hw_ptr = new_hw_ptr; 4833509a03fSPierre-Louis Bossart runtime->hw_ptr_jiffies = curr_jiffies; 4840e8014d7SPierre-Louis Bossart if (crossed_boundary) { 4850e8014d7SPierre-Louis Bossart snd_BUG_ON(crossed_boundary != 1); 4860e8014d7SPierre-Louis Bossart runtime->hw_ptr_wrap += runtime->boundary; 4870e8014d7SPierre-Louis Bossart } 4881da177e4SLinus Torvalds 4893179f620SPierre-Louis Bossart update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp); 4904eeaaeaeSPierre-Louis Bossart 4911250932eSJaroslav Kysela return snd_pcm_update_state(substream, runtime); 4921da177e4SLinus Torvalds } 4931da177e4SLinus Torvalds 4941da177e4SLinus Torvalds /* CAUTION: call it with irq disabled */ 495877211f5STakashi Iwai int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) 4961da177e4SLinus Torvalds { 497f240406bSJaroslav Kysela return snd_pcm_update_hw_ptr0(substream, 0); 4981da177e4SLinus Torvalds } 4991da177e4SLinus Torvalds 5001da177e4SLinus Torvalds /** 5011da177e4SLinus Torvalds * snd_pcm_set_ops - set the PCM operators 5021da177e4SLinus Torvalds * @pcm: the pcm instance 5031da177e4SLinus Torvalds * @direction: stream direction, SNDRV_PCM_STREAM_XXX 5041da177e4SLinus Torvalds * @ops: the operator table 5051da177e4SLinus Torvalds * 5061da177e4SLinus Torvalds * Sets the given PCM operators to the pcm instance. 5071da177e4SLinus Torvalds */ 508e6c2e7ebSLars-Peter Clausen void snd_pcm_set_ops(struct snd_pcm *pcm, int direction, 509e6c2e7ebSLars-Peter Clausen const struct snd_pcm_ops *ops) 5101da177e4SLinus Torvalds { 511877211f5STakashi Iwai struct snd_pcm_str *stream = &pcm->streams[direction]; 512877211f5STakashi Iwai struct snd_pcm_substream *substream; 5131da177e4SLinus Torvalds 5141da177e4SLinus Torvalds for (substream = stream->substream; substream != NULL; substream = substream->next) 5151da177e4SLinus Torvalds substream->ops = ops; 5161da177e4SLinus Torvalds } 5171da177e4SLinus Torvalds 518e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_set_ops); 5191da177e4SLinus Torvalds 5201da177e4SLinus Torvalds /** 5211da177e4SLinus Torvalds * snd_pcm_sync - set the PCM sync id 5221da177e4SLinus Torvalds * @substream: the pcm substream 5231da177e4SLinus Torvalds * 5241da177e4SLinus Torvalds * Sets the PCM sync identifier for the card. 5251da177e4SLinus Torvalds */ 526877211f5STakashi Iwai void snd_pcm_set_sync(struct snd_pcm_substream *substream) 5271da177e4SLinus Torvalds { 528877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 5291da177e4SLinus Torvalds 5301da177e4SLinus Torvalds runtime->sync.id32[0] = substream->pcm->card->number; 5311da177e4SLinus Torvalds runtime->sync.id32[1] = -1; 5321da177e4SLinus Torvalds runtime->sync.id32[2] = -1; 5331da177e4SLinus Torvalds runtime->sync.id32[3] = -1; 5341da177e4SLinus Torvalds } 5351da177e4SLinus Torvalds 536e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_set_sync); 537e88e8ae6STakashi Iwai 5381da177e4SLinus Torvalds /* 5391da177e4SLinus Torvalds * Standard ioctl routine 5401da177e4SLinus Torvalds */ 5411da177e4SLinus Torvalds 5421da177e4SLinus Torvalds static inline unsigned int div32(unsigned int a, unsigned int b, 5431da177e4SLinus Torvalds unsigned int *r) 5441da177e4SLinus Torvalds { 5451da177e4SLinus Torvalds if (b == 0) { 5461da177e4SLinus Torvalds *r = 0; 5471da177e4SLinus Torvalds return UINT_MAX; 5481da177e4SLinus Torvalds } 5491da177e4SLinus Torvalds *r = a % b; 5501da177e4SLinus Torvalds return a / b; 5511da177e4SLinus Torvalds } 5521da177e4SLinus Torvalds 5531da177e4SLinus Torvalds static inline unsigned int div_down(unsigned int a, unsigned int b) 5541da177e4SLinus Torvalds { 5551da177e4SLinus Torvalds if (b == 0) 5561da177e4SLinus Torvalds return UINT_MAX; 5571da177e4SLinus Torvalds return a / b; 5581da177e4SLinus Torvalds } 5591da177e4SLinus Torvalds 5601da177e4SLinus Torvalds static inline unsigned int div_up(unsigned int a, unsigned int b) 5611da177e4SLinus Torvalds { 5621da177e4SLinus Torvalds unsigned int r; 5631da177e4SLinus Torvalds unsigned int q; 5641da177e4SLinus Torvalds if (b == 0) 5651da177e4SLinus Torvalds return UINT_MAX; 5661da177e4SLinus Torvalds q = div32(a, b, &r); 5671da177e4SLinus Torvalds if (r) 5681da177e4SLinus Torvalds ++q; 5691da177e4SLinus Torvalds return q; 5701da177e4SLinus Torvalds } 5711da177e4SLinus Torvalds 5721da177e4SLinus Torvalds static inline unsigned int mul(unsigned int a, unsigned int b) 5731da177e4SLinus Torvalds { 5741da177e4SLinus Torvalds if (a == 0) 5751da177e4SLinus Torvalds return 0; 5761da177e4SLinus Torvalds if (div_down(UINT_MAX, a) < b) 5771da177e4SLinus Torvalds return UINT_MAX; 5781da177e4SLinus Torvalds return a * b; 5791da177e4SLinus Torvalds } 5801da177e4SLinus Torvalds 5811da177e4SLinus Torvalds static inline unsigned int muldiv32(unsigned int a, unsigned int b, 5821da177e4SLinus Torvalds unsigned int c, unsigned int *r) 5831da177e4SLinus Torvalds { 5841da177e4SLinus Torvalds u_int64_t n = (u_int64_t) a * b; 5851da177e4SLinus Torvalds if (c == 0) { 5867eaa943cSTakashi Iwai snd_BUG_ON(!n); 5871da177e4SLinus Torvalds *r = 0; 5881da177e4SLinus Torvalds return UINT_MAX; 5891da177e4SLinus Torvalds } 5903f7440a6STakashi Iwai n = div_u64_rem(n, c, r); 5911da177e4SLinus Torvalds if (n >= UINT_MAX) { 5921da177e4SLinus Torvalds *r = 0; 5931da177e4SLinus Torvalds return UINT_MAX; 5941da177e4SLinus Torvalds } 5951da177e4SLinus Torvalds return n; 5961da177e4SLinus Torvalds } 5971da177e4SLinus Torvalds 5981da177e4SLinus Torvalds /** 5991da177e4SLinus Torvalds * snd_interval_refine - refine the interval value of configurator 6001da177e4SLinus Torvalds * @i: the interval value to refine 6011da177e4SLinus Torvalds * @v: the interval value to refer to 6021da177e4SLinus Torvalds * 6031da177e4SLinus Torvalds * Refines the interval value with the reference value. 6041da177e4SLinus Torvalds * The interval is changed to the range satisfying both intervals. 6051da177e4SLinus Torvalds * The interval status (min, max, integer, etc.) are evaluated. 6061da177e4SLinus Torvalds * 607eb7c06e8SYacine Belkadi * Return: Positive if the value is changed, zero if it's not changed, or a 608eb7c06e8SYacine Belkadi * negative error code. 6091da177e4SLinus Torvalds */ 610877211f5STakashi Iwai int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v) 6111da177e4SLinus Torvalds { 6121da177e4SLinus Torvalds int changed = 0; 6137eaa943cSTakashi Iwai if (snd_BUG_ON(snd_interval_empty(i))) 6147eaa943cSTakashi Iwai return -EINVAL; 6151da177e4SLinus Torvalds if (i->min < v->min) { 6161da177e4SLinus Torvalds i->min = v->min; 6171da177e4SLinus Torvalds i->openmin = v->openmin; 6181da177e4SLinus Torvalds changed = 1; 6191da177e4SLinus Torvalds } else if (i->min == v->min && !i->openmin && v->openmin) { 6201da177e4SLinus Torvalds i->openmin = 1; 6211da177e4SLinus Torvalds changed = 1; 6221da177e4SLinus Torvalds } 6231da177e4SLinus Torvalds if (i->max > v->max) { 6241da177e4SLinus Torvalds i->max = v->max; 6251da177e4SLinus Torvalds i->openmax = v->openmax; 6261da177e4SLinus Torvalds changed = 1; 6271da177e4SLinus Torvalds } else if (i->max == v->max && !i->openmax && v->openmax) { 6281da177e4SLinus Torvalds i->openmax = 1; 6291da177e4SLinus Torvalds changed = 1; 6301da177e4SLinus Torvalds } 6311da177e4SLinus Torvalds if (!i->integer && v->integer) { 6321da177e4SLinus Torvalds i->integer = 1; 6331da177e4SLinus Torvalds changed = 1; 6341da177e4SLinus Torvalds } 6351da177e4SLinus Torvalds if (i->integer) { 6361da177e4SLinus Torvalds if (i->openmin) { 6371da177e4SLinus Torvalds i->min++; 6381da177e4SLinus Torvalds i->openmin = 0; 6391da177e4SLinus Torvalds } 6401da177e4SLinus Torvalds if (i->openmax) { 6411da177e4SLinus Torvalds i->max--; 6421da177e4SLinus Torvalds i->openmax = 0; 6431da177e4SLinus Torvalds } 6441da177e4SLinus Torvalds } else if (!i->openmin && !i->openmax && i->min == i->max) 6451da177e4SLinus Torvalds i->integer = 1; 6461da177e4SLinus Torvalds if (snd_interval_checkempty(i)) { 6471da177e4SLinus Torvalds snd_interval_none(i); 6481da177e4SLinus Torvalds return -EINVAL; 6491da177e4SLinus Torvalds } 6501da177e4SLinus Torvalds return changed; 6511da177e4SLinus Torvalds } 6521da177e4SLinus Torvalds 653e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_interval_refine); 654e88e8ae6STakashi Iwai 655877211f5STakashi Iwai static int snd_interval_refine_first(struct snd_interval *i) 6561da177e4SLinus Torvalds { 6577eaa943cSTakashi Iwai if (snd_BUG_ON(snd_interval_empty(i))) 6587eaa943cSTakashi Iwai return -EINVAL; 6591da177e4SLinus Torvalds if (snd_interval_single(i)) 6601da177e4SLinus Torvalds return 0; 6611da177e4SLinus Torvalds i->max = i->min; 6621da177e4SLinus Torvalds i->openmax = i->openmin; 6631da177e4SLinus Torvalds if (i->openmax) 6641da177e4SLinus Torvalds i->max++; 6651da177e4SLinus Torvalds return 1; 6661da177e4SLinus Torvalds } 6671da177e4SLinus Torvalds 668877211f5STakashi Iwai static int snd_interval_refine_last(struct snd_interval *i) 6691da177e4SLinus Torvalds { 6707eaa943cSTakashi Iwai if (snd_BUG_ON(snd_interval_empty(i))) 6717eaa943cSTakashi Iwai return -EINVAL; 6721da177e4SLinus Torvalds if (snd_interval_single(i)) 6731da177e4SLinus Torvalds return 0; 6741da177e4SLinus Torvalds i->min = i->max; 6751da177e4SLinus Torvalds i->openmin = i->openmax; 6761da177e4SLinus Torvalds if (i->openmin) 6771da177e4SLinus Torvalds i->min--; 6781da177e4SLinus Torvalds return 1; 6791da177e4SLinus Torvalds } 6801da177e4SLinus Torvalds 681877211f5STakashi Iwai void snd_interval_mul(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c) 6821da177e4SLinus Torvalds { 6831da177e4SLinus Torvalds if (a->empty || b->empty) { 6841da177e4SLinus Torvalds snd_interval_none(c); 6851da177e4SLinus Torvalds return; 6861da177e4SLinus Torvalds } 6871da177e4SLinus Torvalds c->empty = 0; 6881da177e4SLinus Torvalds c->min = mul(a->min, b->min); 6891da177e4SLinus Torvalds c->openmin = (a->openmin || b->openmin); 6901da177e4SLinus Torvalds c->max = mul(a->max, b->max); 6911da177e4SLinus Torvalds c->openmax = (a->openmax || b->openmax); 6921da177e4SLinus Torvalds c->integer = (a->integer && b->integer); 6931da177e4SLinus Torvalds } 6941da177e4SLinus Torvalds 6951da177e4SLinus Torvalds /** 6961da177e4SLinus Torvalds * snd_interval_div - refine the interval value with division 697df8db936STakashi Iwai * @a: dividend 698df8db936STakashi Iwai * @b: divisor 699df8db936STakashi Iwai * @c: quotient 7001da177e4SLinus Torvalds * 7011da177e4SLinus Torvalds * c = a / b 7021da177e4SLinus Torvalds * 7031da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 7041da177e4SLinus Torvalds */ 705877211f5STakashi Iwai void snd_interval_div(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c) 7061da177e4SLinus Torvalds { 7071da177e4SLinus Torvalds unsigned int r; 7081da177e4SLinus Torvalds if (a->empty || b->empty) { 7091da177e4SLinus Torvalds snd_interval_none(c); 7101da177e4SLinus Torvalds return; 7111da177e4SLinus Torvalds } 7121da177e4SLinus Torvalds c->empty = 0; 7131da177e4SLinus Torvalds c->min = div32(a->min, b->max, &r); 7141da177e4SLinus Torvalds c->openmin = (r || a->openmin || b->openmax); 7151da177e4SLinus Torvalds if (b->min > 0) { 7161da177e4SLinus Torvalds c->max = div32(a->max, b->min, &r); 7171da177e4SLinus Torvalds if (r) { 7181da177e4SLinus Torvalds c->max++; 7191da177e4SLinus Torvalds c->openmax = 1; 7201da177e4SLinus Torvalds } else 7211da177e4SLinus Torvalds c->openmax = (a->openmax || b->openmin); 7221da177e4SLinus Torvalds } else { 7231da177e4SLinus Torvalds c->max = UINT_MAX; 7241da177e4SLinus Torvalds c->openmax = 0; 7251da177e4SLinus Torvalds } 7261da177e4SLinus Torvalds c->integer = 0; 7271da177e4SLinus Torvalds } 7281da177e4SLinus Torvalds 7291da177e4SLinus Torvalds /** 7301da177e4SLinus Torvalds * snd_interval_muldivk - refine the interval value 731df8db936STakashi Iwai * @a: dividend 1 732df8db936STakashi Iwai * @b: dividend 2 733df8db936STakashi Iwai * @k: divisor (as integer) 734df8db936STakashi Iwai * @c: result 7351da177e4SLinus Torvalds * 7361da177e4SLinus Torvalds * c = a * b / k 7371da177e4SLinus Torvalds * 7381da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 7391da177e4SLinus Torvalds */ 740877211f5STakashi Iwai void snd_interval_muldivk(const struct snd_interval *a, const struct snd_interval *b, 741877211f5STakashi Iwai unsigned int k, struct snd_interval *c) 7421da177e4SLinus Torvalds { 7431da177e4SLinus Torvalds unsigned int r; 7441da177e4SLinus Torvalds if (a->empty || b->empty) { 7451da177e4SLinus Torvalds snd_interval_none(c); 7461da177e4SLinus Torvalds return; 7471da177e4SLinus Torvalds } 7481da177e4SLinus Torvalds c->empty = 0; 7491da177e4SLinus Torvalds c->min = muldiv32(a->min, b->min, k, &r); 7501da177e4SLinus Torvalds c->openmin = (r || a->openmin || b->openmin); 7511da177e4SLinus Torvalds c->max = muldiv32(a->max, b->max, k, &r); 7521da177e4SLinus Torvalds if (r) { 7531da177e4SLinus Torvalds c->max++; 7541da177e4SLinus Torvalds c->openmax = 1; 7551da177e4SLinus Torvalds } else 7561da177e4SLinus Torvalds c->openmax = (a->openmax || b->openmax); 7571da177e4SLinus Torvalds c->integer = 0; 7581da177e4SLinus Torvalds } 7591da177e4SLinus Torvalds 7601da177e4SLinus Torvalds /** 7611da177e4SLinus Torvalds * snd_interval_mulkdiv - refine the interval value 762df8db936STakashi Iwai * @a: dividend 1 763df8db936STakashi Iwai * @k: dividend 2 (as integer) 764df8db936STakashi Iwai * @b: divisor 765df8db936STakashi Iwai * @c: result 7661da177e4SLinus Torvalds * 7671da177e4SLinus Torvalds * c = a * k / b 7681da177e4SLinus Torvalds * 7691da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 7701da177e4SLinus Torvalds */ 771877211f5STakashi Iwai void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k, 772877211f5STakashi Iwai const struct snd_interval *b, struct snd_interval *c) 7731da177e4SLinus Torvalds { 7741da177e4SLinus Torvalds unsigned int r; 7751da177e4SLinus Torvalds if (a->empty || b->empty) { 7761da177e4SLinus Torvalds snd_interval_none(c); 7771da177e4SLinus Torvalds return; 7781da177e4SLinus Torvalds } 7791da177e4SLinus Torvalds c->empty = 0; 7801da177e4SLinus Torvalds c->min = muldiv32(a->min, k, b->max, &r); 7811da177e4SLinus Torvalds c->openmin = (r || a->openmin || b->openmax); 7821da177e4SLinus Torvalds if (b->min > 0) { 7831da177e4SLinus Torvalds c->max = muldiv32(a->max, k, b->min, &r); 7841da177e4SLinus Torvalds if (r) { 7851da177e4SLinus Torvalds c->max++; 7861da177e4SLinus Torvalds c->openmax = 1; 7871da177e4SLinus Torvalds } else 7881da177e4SLinus Torvalds c->openmax = (a->openmax || b->openmin); 7891da177e4SLinus Torvalds } else { 7901da177e4SLinus Torvalds c->max = UINT_MAX; 7911da177e4SLinus Torvalds c->openmax = 0; 7921da177e4SLinus Torvalds } 7931da177e4SLinus Torvalds c->integer = 0; 7941da177e4SLinus Torvalds } 7951da177e4SLinus Torvalds 7961da177e4SLinus Torvalds /* ---- */ 7971da177e4SLinus Torvalds 7981da177e4SLinus Torvalds 7991da177e4SLinus Torvalds /** 8001da177e4SLinus Torvalds * snd_interval_ratnum - refine the interval value 801df8db936STakashi Iwai * @i: interval to refine 802df8db936STakashi Iwai * @rats_count: number of ratnum_t 803df8db936STakashi Iwai * @rats: ratnum_t array 804df8db936STakashi Iwai * @nump: pointer to store the resultant numerator 805df8db936STakashi Iwai * @denp: pointer to store the resultant denominator 8061da177e4SLinus Torvalds * 807eb7c06e8SYacine Belkadi * Return: Positive if the value is changed, zero if it's not changed, or a 808eb7c06e8SYacine Belkadi * negative error code. 8091da177e4SLinus Torvalds */ 810877211f5STakashi Iwai int snd_interval_ratnum(struct snd_interval *i, 811e5e113cfSLars-Peter Clausen unsigned int rats_count, const struct snd_ratnum *rats, 8121da177e4SLinus Torvalds unsigned int *nump, unsigned int *denp) 8131da177e4SLinus Torvalds { 8148374e24cSKrzysztof Helt unsigned int best_num, best_den; 8158374e24cSKrzysztof Helt int best_diff; 8161da177e4SLinus Torvalds unsigned int k; 817877211f5STakashi Iwai struct snd_interval t; 8181da177e4SLinus Torvalds int err; 8198374e24cSKrzysztof Helt unsigned int result_num, result_den; 8208374e24cSKrzysztof Helt int result_diff; 8211da177e4SLinus Torvalds 8221da177e4SLinus Torvalds best_num = best_den = best_diff = 0; 8231da177e4SLinus Torvalds for (k = 0; k < rats_count; ++k) { 8241da177e4SLinus Torvalds unsigned int num = rats[k].num; 8251da177e4SLinus Torvalds unsigned int den; 8261da177e4SLinus Torvalds unsigned int q = i->min; 8271da177e4SLinus Torvalds int diff; 8281da177e4SLinus Torvalds if (q == 0) 8291da177e4SLinus Torvalds q = 1; 83040962d7cSKrzysztof Helt den = div_up(num, q); 8311da177e4SLinus Torvalds if (den < rats[k].den_min) 8321da177e4SLinus Torvalds continue; 8331da177e4SLinus Torvalds if (den > rats[k].den_max) 8341da177e4SLinus Torvalds den = rats[k].den_max; 8351da177e4SLinus Torvalds else { 8361da177e4SLinus Torvalds unsigned int r; 8371da177e4SLinus Torvalds r = (den - rats[k].den_min) % rats[k].den_step; 8381da177e4SLinus Torvalds if (r != 0) 8391da177e4SLinus Torvalds den -= r; 8401da177e4SLinus Torvalds } 8411da177e4SLinus Torvalds diff = num - q * den; 8428374e24cSKrzysztof Helt if (diff < 0) 8438374e24cSKrzysztof Helt diff = -diff; 8441da177e4SLinus Torvalds if (best_num == 0 || 8451da177e4SLinus Torvalds diff * best_den < best_diff * den) { 8461da177e4SLinus Torvalds best_diff = diff; 8471da177e4SLinus Torvalds best_den = den; 8481da177e4SLinus Torvalds best_num = num; 8491da177e4SLinus Torvalds } 8501da177e4SLinus Torvalds } 8511da177e4SLinus Torvalds if (best_den == 0) { 8521da177e4SLinus Torvalds i->empty = 1; 8531da177e4SLinus Torvalds return -EINVAL; 8541da177e4SLinus Torvalds } 8551da177e4SLinus Torvalds t.min = div_down(best_num, best_den); 8561da177e4SLinus Torvalds t.openmin = !!(best_num % best_den); 8571da177e4SLinus Torvalds 8588374e24cSKrzysztof Helt result_num = best_num; 8598374e24cSKrzysztof Helt result_diff = best_diff; 8608374e24cSKrzysztof Helt result_den = best_den; 8611da177e4SLinus Torvalds best_num = best_den = best_diff = 0; 8621da177e4SLinus Torvalds for (k = 0; k < rats_count; ++k) { 8631da177e4SLinus Torvalds unsigned int num = rats[k].num; 8641da177e4SLinus Torvalds unsigned int den; 8651da177e4SLinus Torvalds unsigned int q = i->max; 8661da177e4SLinus Torvalds int diff; 8671da177e4SLinus Torvalds if (q == 0) { 8681da177e4SLinus Torvalds i->empty = 1; 8691da177e4SLinus Torvalds return -EINVAL; 8701da177e4SLinus Torvalds } 87140962d7cSKrzysztof Helt den = div_down(num, q); 8721da177e4SLinus Torvalds if (den > rats[k].den_max) 8731da177e4SLinus Torvalds continue; 8741da177e4SLinus Torvalds if (den < rats[k].den_min) 8751da177e4SLinus Torvalds den = rats[k].den_min; 8761da177e4SLinus Torvalds else { 8771da177e4SLinus Torvalds unsigned int r; 8781da177e4SLinus Torvalds r = (den - rats[k].den_min) % rats[k].den_step; 8791da177e4SLinus Torvalds if (r != 0) 8801da177e4SLinus Torvalds den += rats[k].den_step - r; 8811da177e4SLinus Torvalds } 8821da177e4SLinus Torvalds diff = q * den - num; 8838374e24cSKrzysztof Helt if (diff < 0) 8848374e24cSKrzysztof Helt diff = -diff; 8851da177e4SLinus Torvalds if (best_num == 0 || 8861da177e4SLinus Torvalds diff * best_den < best_diff * den) { 8871da177e4SLinus Torvalds best_diff = diff; 8881da177e4SLinus Torvalds best_den = den; 8891da177e4SLinus Torvalds best_num = num; 8901da177e4SLinus Torvalds } 8911da177e4SLinus Torvalds } 8921da177e4SLinus Torvalds if (best_den == 0) { 8931da177e4SLinus Torvalds i->empty = 1; 8941da177e4SLinus Torvalds return -EINVAL; 8951da177e4SLinus Torvalds } 8961da177e4SLinus Torvalds t.max = div_up(best_num, best_den); 8971da177e4SLinus Torvalds t.openmax = !!(best_num % best_den); 8981da177e4SLinus Torvalds t.integer = 0; 8991da177e4SLinus Torvalds err = snd_interval_refine(i, &t); 9001da177e4SLinus Torvalds if (err < 0) 9011da177e4SLinus Torvalds return err; 9021da177e4SLinus Torvalds 9031da177e4SLinus Torvalds if (snd_interval_single(i)) { 9048374e24cSKrzysztof Helt if (best_diff * result_den < result_diff * best_den) { 9058374e24cSKrzysztof Helt result_num = best_num; 9068374e24cSKrzysztof Helt result_den = best_den; 9078374e24cSKrzysztof Helt } 9081da177e4SLinus Torvalds if (nump) 9098374e24cSKrzysztof Helt *nump = result_num; 9101da177e4SLinus Torvalds if (denp) 9118374e24cSKrzysztof Helt *denp = result_den; 9121da177e4SLinus Torvalds } 9131da177e4SLinus Torvalds return err; 9141da177e4SLinus Torvalds } 9151da177e4SLinus Torvalds 916e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_interval_ratnum); 917e88e8ae6STakashi Iwai 9181da177e4SLinus Torvalds /** 9191da177e4SLinus Torvalds * snd_interval_ratden - refine the interval value 920df8db936STakashi Iwai * @i: interval to refine 921877211f5STakashi Iwai * @rats_count: number of struct ratden 922877211f5STakashi Iwai * @rats: struct ratden array 923df8db936STakashi Iwai * @nump: pointer to store the resultant numerator 924df8db936STakashi Iwai * @denp: pointer to store the resultant denominator 9251da177e4SLinus Torvalds * 926eb7c06e8SYacine Belkadi * Return: Positive if the value is changed, zero if it's not changed, or a 927eb7c06e8SYacine Belkadi * negative error code. 9281da177e4SLinus Torvalds */ 929877211f5STakashi Iwai static int snd_interval_ratden(struct snd_interval *i, 930e5e113cfSLars-Peter Clausen unsigned int rats_count, 931e5e113cfSLars-Peter Clausen const struct snd_ratden *rats, 9321da177e4SLinus Torvalds unsigned int *nump, unsigned int *denp) 9331da177e4SLinus Torvalds { 9341da177e4SLinus Torvalds unsigned int best_num, best_diff, best_den; 9351da177e4SLinus Torvalds unsigned int k; 936877211f5STakashi Iwai struct snd_interval t; 9371da177e4SLinus Torvalds int err; 9381da177e4SLinus Torvalds 9391da177e4SLinus Torvalds best_num = best_den = best_diff = 0; 9401da177e4SLinus Torvalds for (k = 0; k < rats_count; ++k) { 9411da177e4SLinus Torvalds unsigned int num; 9421da177e4SLinus Torvalds unsigned int den = rats[k].den; 9431da177e4SLinus Torvalds unsigned int q = i->min; 9441da177e4SLinus Torvalds int diff; 9451da177e4SLinus Torvalds num = mul(q, den); 9461da177e4SLinus Torvalds if (num > rats[k].num_max) 9471da177e4SLinus Torvalds continue; 9481da177e4SLinus Torvalds if (num < rats[k].num_min) 9491da177e4SLinus Torvalds num = rats[k].num_max; 9501da177e4SLinus Torvalds else { 9511da177e4SLinus Torvalds unsigned int r; 9521da177e4SLinus Torvalds r = (num - rats[k].num_min) % rats[k].num_step; 9531da177e4SLinus Torvalds if (r != 0) 9541da177e4SLinus Torvalds num += rats[k].num_step - r; 9551da177e4SLinus Torvalds } 9561da177e4SLinus Torvalds diff = num - q * den; 9571da177e4SLinus Torvalds if (best_num == 0 || 9581da177e4SLinus Torvalds diff * best_den < best_diff * den) { 9591da177e4SLinus Torvalds best_diff = diff; 9601da177e4SLinus Torvalds best_den = den; 9611da177e4SLinus Torvalds best_num = num; 9621da177e4SLinus Torvalds } 9631da177e4SLinus Torvalds } 9641da177e4SLinus Torvalds if (best_den == 0) { 9651da177e4SLinus Torvalds i->empty = 1; 9661da177e4SLinus Torvalds return -EINVAL; 9671da177e4SLinus Torvalds } 9681da177e4SLinus Torvalds t.min = div_down(best_num, best_den); 9691da177e4SLinus Torvalds t.openmin = !!(best_num % best_den); 9701da177e4SLinus Torvalds 9711da177e4SLinus Torvalds best_num = best_den = best_diff = 0; 9721da177e4SLinus Torvalds for (k = 0; k < rats_count; ++k) { 9731da177e4SLinus Torvalds unsigned int num; 9741da177e4SLinus Torvalds unsigned int den = rats[k].den; 9751da177e4SLinus Torvalds unsigned int q = i->max; 9761da177e4SLinus Torvalds int diff; 9771da177e4SLinus Torvalds num = mul(q, den); 9781da177e4SLinus Torvalds if (num < rats[k].num_min) 9791da177e4SLinus Torvalds continue; 9801da177e4SLinus Torvalds if (num > rats[k].num_max) 9811da177e4SLinus Torvalds num = rats[k].num_max; 9821da177e4SLinus Torvalds else { 9831da177e4SLinus Torvalds unsigned int r; 9841da177e4SLinus Torvalds r = (num - rats[k].num_min) % rats[k].num_step; 9851da177e4SLinus Torvalds if (r != 0) 9861da177e4SLinus Torvalds num -= r; 9871da177e4SLinus Torvalds } 9881da177e4SLinus Torvalds diff = q * den - num; 9891da177e4SLinus Torvalds if (best_num == 0 || 9901da177e4SLinus Torvalds diff * best_den < best_diff * den) { 9911da177e4SLinus Torvalds best_diff = diff; 9921da177e4SLinus Torvalds best_den = den; 9931da177e4SLinus Torvalds best_num = num; 9941da177e4SLinus Torvalds } 9951da177e4SLinus Torvalds } 9961da177e4SLinus Torvalds if (best_den == 0) { 9971da177e4SLinus Torvalds i->empty = 1; 9981da177e4SLinus Torvalds return -EINVAL; 9991da177e4SLinus Torvalds } 10001da177e4SLinus Torvalds t.max = div_up(best_num, best_den); 10011da177e4SLinus Torvalds t.openmax = !!(best_num % best_den); 10021da177e4SLinus Torvalds t.integer = 0; 10031da177e4SLinus Torvalds err = snd_interval_refine(i, &t); 10041da177e4SLinus Torvalds if (err < 0) 10051da177e4SLinus Torvalds return err; 10061da177e4SLinus Torvalds 10071da177e4SLinus Torvalds if (snd_interval_single(i)) { 10081da177e4SLinus Torvalds if (nump) 10091da177e4SLinus Torvalds *nump = best_num; 10101da177e4SLinus Torvalds if (denp) 10111da177e4SLinus Torvalds *denp = best_den; 10121da177e4SLinus Torvalds } 10131da177e4SLinus Torvalds return err; 10141da177e4SLinus Torvalds } 10151da177e4SLinus Torvalds 10161da177e4SLinus Torvalds /** 10171da177e4SLinus Torvalds * snd_interval_list - refine the interval value from the list 10181da177e4SLinus Torvalds * @i: the interval value to refine 10191da177e4SLinus Torvalds * @count: the number of elements in the list 10201da177e4SLinus Torvalds * @list: the value list 10211da177e4SLinus Torvalds * @mask: the bit-mask to evaluate 10221da177e4SLinus Torvalds * 10231da177e4SLinus Torvalds * Refines the interval value from the list. 10241da177e4SLinus Torvalds * When mask is non-zero, only the elements corresponding to bit 1 are 10251da177e4SLinus Torvalds * evaluated. 10261da177e4SLinus Torvalds * 1027eb7c06e8SYacine Belkadi * Return: Positive if the value is changed, zero if it's not changed, or a 1028eb7c06e8SYacine Belkadi * negative error code. 10291da177e4SLinus Torvalds */ 10304af87a93SMark Brown int snd_interval_list(struct snd_interval *i, unsigned int count, 10314af87a93SMark Brown const unsigned int *list, unsigned int mask) 10321da177e4SLinus Torvalds { 10331da177e4SLinus Torvalds unsigned int k; 1034b1ddaf68SClemens Ladisch struct snd_interval list_range; 10350981a260STakashi Iwai 10360981a260STakashi Iwai if (!count) { 10370981a260STakashi Iwai i->empty = 1; 10380981a260STakashi Iwai return -EINVAL; 10390981a260STakashi Iwai } 1040b1ddaf68SClemens Ladisch snd_interval_any(&list_range); 1041b1ddaf68SClemens Ladisch list_range.min = UINT_MAX; 1042b1ddaf68SClemens Ladisch list_range.max = 0; 10431da177e4SLinus Torvalds for (k = 0; k < count; k++) { 10441da177e4SLinus Torvalds if (mask && !(mask & (1 << k))) 10451da177e4SLinus Torvalds continue; 1046b1ddaf68SClemens Ladisch if (!snd_interval_test(i, list[k])) 10471da177e4SLinus Torvalds continue; 1048b1ddaf68SClemens Ladisch list_range.min = min(list_range.min, list[k]); 1049b1ddaf68SClemens Ladisch list_range.max = max(list_range.max, list[k]); 10501da177e4SLinus Torvalds } 1051b1ddaf68SClemens Ladisch return snd_interval_refine(i, &list_range); 10521da177e4SLinus Torvalds } 10531da177e4SLinus Torvalds 1054e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_interval_list); 1055e88e8ae6STakashi Iwai 1056f66f898eSPeter Rosin /** 1057f66f898eSPeter Rosin * snd_interval_ranges - refine the interval value from the list of ranges 1058f66f898eSPeter Rosin * @i: the interval value to refine 1059f66f898eSPeter Rosin * @count: the number of elements in the list of ranges 1060f66f898eSPeter Rosin * @ranges: the ranges list 1061f66f898eSPeter Rosin * @mask: the bit-mask to evaluate 1062f66f898eSPeter Rosin * 1063f66f898eSPeter Rosin * Refines the interval value from the list of ranges. 1064f66f898eSPeter Rosin * When mask is non-zero, only the elements corresponding to bit 1 are 1065f66f898eSPeter Rosin * evaluated. 1066f66f898eSPeter Rosin * 1067f66f898eSPeter Rosin * Return: Positive if the value is changed, zero if it's not changed, or a 1068f66f898eSPeter Rosin * negative error code. 1069f66f898eSPeter Rosin */ 1070f66f898eSPeter Rosin int snd_interval_ranges(struct snd_interval *i, unsigned int count, 1071f66f898eSPeter Rosin const struct snd_interval *ranges, unsigned int mask) 1072f66f898eSPeter Rosin { 1073f66f898eSPeter Rosin unsigned int k; 1074f66f898eSPeter Rosin struct snd_interval range_union; 1075f66f898eSPeter Rosin struct snd_interval range; 1076f66f898eSPeter Rosin 1077f66f898eSPeter Rosin if (!count) { 1078f66f898eSPeter Rosin snd_interval_none(i); 1079f66f898eSPeter Rosin return -EINVAL; 1080f66f898eSPeter Rosin } 1081f66f898eSPeter Rosin snd_interval_any(&range_union); 1082f66f898eSPeter Rosin range_union.min = UINT_MAX; 1083f66f898eSPeter Rosin range_union.max = 0; 1084f66f898eSPeter Rosin for (k = 0; k < count; k++) { 1085f66f898eSPeter Rosin if (mask && !(mask & (1 << k))) 1086f66f898eSPeter Rosin continue; 1087f66f898eSPeter Rosin snd_interval_copy(&range, &ranges[k]); 1088f66f898eSPeter Rosin if (snd_interval_refine(&range, i) < 0) 1089f66f898eSPeter Rosin continue; 1090f66f898eSPeter Rosin if (snd_interval_empty(&range)) 1091f66f898eSPeter Rosin continue; 1092f66f898eSPeter Rosin 1093f66f898eSPeter Rosin if (range.min < range_union.min) { 1094f66f898eSPeter Rosin range_union.min = range.min; 1095f66f898eSPeter Rosin range_union.openmin = 1; 1096f66f898eSPeter Rosin } 1097f66f898eSPeter Rosin if (range.min == range_union.min && !range.openmin) 1098f66f898eSPeter Rosin range_union.openmin = 0; 1099f66f898eSPeter Rosin if (range.max > range_union.max) { 1100f66f898eSPeter Rosin range_union.max = range.max; 1101f66f898eSPeter Rosin range_union.openmax = 1; 1102f66f898eSPeter Rosin } 1103f66f898eSPeter Rosin if (range.max == range_union.max && !range.openmax) 1104f66f898eSPeter Rosin range_union.openmax = 0; 1105f66f898eSPeter Rosin } 1106f66f898eSPeter Rosin return snd_interval_refine(i, &range_union); 1107f66f898eSPeter Rosin } 1108f66f898eSPeter Rosin EXPORT_SYMBOL(snd_interval_ranges); 1109f66f898eSPeter Rosin 11100f519b62SClemens Ladisch static int snd_interval_step(struct snd_interval *i, unsigned int step) 11111da177e4SLinus Torvalds { 11121da177e4SLinus Torvalds unsigned int n; 11131da177e4SLinus Torvalds int changed = 0; 11140f519b62SClemens Ladisch n = i->min % step; 11151da177e4SLinus Torvalds if (n != 0 || i->openmin) { 11161da177e4SLinus Torvalds i->min += step - n; 1117df1e4719SClemens Ladisch i->openmin = 0; 11181da177e4SLinus Torvalds changed = 1; 11191da177e4SLinus Torvalds } 11200f519b62SClemens Ladisch n = i->max % step; 11211da177e4SLinus Torvalds if (n != 0 || i->openmax) { 11221da177e4SLinus Torvalds i->max -= n; 1123df1e4719SClemens Ladisch i->openmax = 0; 11241da177e4SLinus Torvalds changed = 1; 11251da177e4SLinus Torvalds } 11261da177e4SLinus Torvalds if (snd_interval_checkempty(i)) { 11271da177e4SLinus Torvalds i->empty = 1; 11281da177e4SLinus Torvalds return -EINVAL; 11291da177e4SLinus Torvalds } 11301da177e4SLinus Torvalds return changed; 11311da177e4SLinus Torvalds } 11321da177e4SLinus Torvalds 11331da177e4SLinus Torvalds /* Info constraints helpers */ 11341da177e4SLinus Torvalds 11351da177e4SLinus Torvalds /** 11361da177e4SLinus Torvalds * snd_pcm_hw_rule_add - add the hw-constraint rule 11371da177e4SLinus Torvalds * @runtime: the pcm runtime instance 11381da177e4SLinus Torvalds * @cond: condition bits 11391da177e4SLinus Torvalds * @var: the variable to evaluate 11401da177e4SLinus Torvalds * @func: the evaluation function 11411da177e4SLinus Torvalds * @private: the private data pointer passed to function 11421da177e4SLinus Torvalds * @dep: the dependent variables 11431da177e4SLinus Torvalds * 1144eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 11451da177e4SLinus Torvalds */ 1146877211f5STakashi Iwai int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond, 11471da177e4SLinus Torvalds int var, 11481da177e4SLinus Torvalds snd_pcm_hw_rule_func_t func, void *private, 11491da177e4SLinus Torvalds int dep, ...) 11501da177e4SLinus Torvalds { 1151877211f5STakashi Iwai struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; 1152877211f5STakashi Iwai struct snd_pcm_hw_rule *c; 11531da177e4SLinus Torvalds unsigned int k; 11541da177e4SLinus Torvalds va_list args; 11551da177e4SLinus Torvalds va_start(args, dep); 11561da177e4SLinus Torvalds if (constrs->rules_num >= constrs->rules_all) { 1157877211f5STakashi Iwai struct snd_pcm_hw_rule *new; 11581da177e4SLinus Torvalds unsigned int new_rules = constrs->rules_all + 16; 11591da177e4SLinus Torvalds new = kcalloc(new_rules, sizeof(*c), GFP_KERNEL); 116087a1c8aaSJesper Juhl if (!new) { 116187a1c8aaSJesper Juhl va_end(args); 11621da177e4SLinus Torvalds return -ENOMEM; 116387a1c8aaSJesper Juhl } 11641da177e4SLinus Torvalds if (constrs->rules) { 11651da177e4SLinus Torvalds memcpy(new, constrs->rules, 11661da177e4SLinus Torvalds constrs->rules_num * sizeof(*c)); 11671da177e4SLinus Torvalds kfree(constrs->rules); 11681da177e4SLinus Torvalds } 11691da177e4SLinus Torvalds constrs->rules = new; 11701da177e4SLinus Torvalds constrs->rules_all = new_rules; 11711da177e4SLinus Torvalds } 11721da177e4SLinus Torvalds c = &constrs->rules[constrs->rules_num]; 11731da177e4SLinus Torvalds c->cond = cond; 11741da177e4SLinus Torvalds c->func = func; 11751da177e4SLinus Torvalds c->var = var; 11761da177e4SLinus Torvalds c->private = private; 11771da177e4SLinus Torvalds k = 0; 11781da177e4SLinus Torvalds while (1) { 117987a1c8aaSJesper Juhl if (snd_BUG_ON(k >= ARRAY_SIZE(c->deps))) { 118087a1c8aaSJesper Juhl va_end(args); 11817eaa943cSTakashi Iwai return -EINVAL; 118287a1c8aaSJesper Juhl } 11831da177e4SLinus Torvalds c->deps[k++] = dep; 11841da177e4SLinus Torvalds if (dep < 0) 11851da177e4SLinus Torvalds break; 11861da177e4SLinus Torvalds dep = va_arg(args, int); 11871da177e4SLinus Torvalds } 11881da177e4SLinus Torvalds constrs->rules_num++; 11891da177e4SLinus Torvalds va_end(args); 11901da177e4SLinus Torvalds return 0; 11911da177e4SLinus Torvalds } 11921da177e4SLinus Torvalds 1193e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_rule_add); 1194e88e8ae6STakashi Iwai 11951da177e4SLinus Torvalds /** 11961c85cc64SRandy Dunlap * snd_pcm_hw_constraint_mask - apply the given bitmap mask constraint 1197df8db936STakashi Iwai * @runtime: PCM runtime instance 1198df8db936STakashi Iwai * @var: hw_params variable to apply the mask 1199df8db936STakashi Iwai * @mask: the bitmap mask 1200df8db936STakashi Iwai * 12011c85cc64SRandy Dunlap * Apply the constraint of the given bitmap mask to a 32-bit mask parameter. 1202eb7c06e8SYacine Belkadi * 1203eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 12041da177e4SLinus Torvalds */ 1205877211f5STakashi Iwai int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, 12061da177e4SLinus Torvalds u_int32_t mask) 12071da177e4SLinus Torvalds { 1208877211f5STakashi Iwai struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; 1209877211f5STakashi Iwai struct snd_mask *maskp = constrs_mask(constrs, var); 12101da177e4SLinus Torvalds *maskp->bits &= mask; 12111da177e4SLinus Torvalds memset(maskp->bits + 1, 0, (SNDRV_MASK_MAX-32) / 8); /* clear rest */ 12121da177e4SLinus Torvalds if (*maskp->bits == 0) 12131da177e4SLinus Torvalds return -EINVAL; 12141da177e4SLinus Torvalds return 0; 12151da177e4SLinus Torvalds } 12161da177e4SLinus Torvalds 12171da177e4SLinus Torvalds /** 12181c85cc64SRandy Dunlap * snd_pcm_hw_constraint_mask64 - apply the given bitmap mask constraint 1219df8db936STakashi Iwai * @runtime: PCM runtime instance 1220df8db936STakashi Iwai * @var: hw_params variable to apply the mask 1221df8db936STakashi Iwai * @mask: the 64bit bitmap mask 1222df8db936STakashi Iwai * 12231c85cc64SRandy Dunlap * Apply the constraint of the given bitmap mask to a 64-bit mask parameter. 1224eb7c06e8SYacine Belkadi * 1225eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 12261da177e4SLinus Torvalds */ 1227877211f5STakashi Iwai int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, 12281da177e4SLinus Torvalds u_int64_t mask) 12291da177e4SLinus Torvalds { 1230877211f5STakashi Iwai struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; 1231877211f5STakashi Iwai struct snd_mask *maskp = constrs_mask(constrs, var); 12321da177e4SLinus Torvalds maskp->bits[0] &= (u_int32_t)mask; 12331da177e4SLinus Torvalds maskp->bits[1] &= (u_int32_t)(mask >> 32); 12341da177e4SLinus Torvalds memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */ 12351da177e4SLinus Torvalds if (! maskp->bits[0] && ! maskp->bits[1]) 12361da177e4SLinus Torvalds return -EINVAL; 12371da177e4SLinus Torvalds return 0; 12381da177e4SLinus Torvalds } 123963a5d4c6SMark Brown EXPORT_SYMBOL(snd_pcm_hw_constraint_mask64); 12401da177e4SLinus Torvalds 12411da177e4SLinus Torvalds /** 12421c85cc64SRandy Dunlap * snd_pcm_hw_constraint_integer - apply an integer constraint to an interval 1243df8db936STakashi Iwai * @runtime: PCM runtime instance 1244df8db936STakashi Iwai * @var: hw_params variable to apply the integer constraint 1245df8db936STakashi Iwai * 1246df8db936STakashi Iwai * Apply the constraint of integer to an interval parameter. 1247eb7c06e8SYacine Belkadi * 1248eb7c06e8SYacine Belkadi * Return: Positive if the value is changed, zero if it's not changed, or a 1249eb7c06e8SYacine Belkadi * negative error code. 12501da177e4SLinus Torvalds */ 1251877211f5STakashi Iwai int snd_pcm_hw_constraint_integer(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var) 12521da177e4SLinus Torvalds { 1253877211f5STakashi Iwai struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; 12541da177e4SLinus Torvalds return snd_interval_setinteger(constrs_interval(constrs, var)); 12551da177e4SLinus Torvalds } 12561da177e4SLinus Torvalds 1257e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_integer); 1258e88e8ae6STakashi Iwai 12591da177e4SLinus Torvalds /** 12601c85cc64SRandy Dunlap * snd_pcm_hw_constraint_minmax - apply a min/max range constraint to an interval 1261df8db936STakashi Iwai * @runtime: PCM runtime instance 1262df8db936STakashi Iwai * @var: hw_params variable to apply the range 1263df8db936STakashi Iwai * @min: the minimal value 1264df8db936STakashi Iwai * @max: the maximal value 1265df8db936STakashi Iwai * 1266df8db936STakashi Iwai * Apply the min/max range constraint to an interval parameter. 1267eb7c06e8SYacine Belkadi * 1268eb7c06e8SYacine Belkadi * Return: Positive if the value is changed, zero if it's not changed, or a 1269eb7c06e8SYacine Belkadi * negative error code. 12701da177e4SLinus Torvalds */ 1271877211f5STakashi Iwai int snd_pcm_hw_constraint_minmax(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, 12721da177e4SLinus Torvalds unsigned int min, unsigned int max) 12731da177e4SLinus Torvalds { 1274877211f5STakashi Iwai struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; 1275877211f5STakashi Iwai struct snd_interval t; 12761da177e4SLinus Torvalds t.min = min; 12771da177e4SLinus Torvalds t.max = max; 12781da177e4SLinus Torvalds t.openmin = t.openmax = 0; 12791da177e4SLinus Torvalds t.integer = 0; 12801da177e4SLinus Torvalds return snd_interval_refine(constrs_interval(constrs, var), &t); 12811da177e4SLinus Torvalds } 12821da177e4SLinus Torvalds 1283e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_minmax); 1284e88e8ae6STakashi Iwai 1285877211f5STakashi Iwai static int snd_pcm_hw_rule_list(struct snd_pcm_hw_params *params, 1286877211f5STakashi Iwai struct snd_pcm_hw_rule *rule) 12871da177e4SLinus Torvalds { 1288877211f5STakashi Iwai struct snd_pcm_hw_constraint_list *list = rule->private; 12891da177e4SLinus Torvalds return snd_interval_list(hw_param_interval(params, rule->var), list->count, list->list, list->mask); 12901da177e4SLinus Torvalds } 12911da177e4SLinus Torvalds 12921da177e4SLinus Torvalds 12931da177e4SLinus Torvalds /** 12941c85cc64SRandy Dunlap * snd_pcm_hw_constraint_list - apply a list of constraints to a parameter 1295df8db936STakashi Iwai * @runtime: PCM runtime instance 1296df8db936STakashi Iwai * @cond: condition bits 1297df8db936STakashi Iwai * @var: hw_params variable to apply the list constraint 1298df8db936STakashi Iwai * @l: list 1299df8db936STakashi Iwai * 1300df8db936STakashi Iwai * Apply the list of constraints to an interval parameter. 1301eb7c06e8SYacine Belkadi * 1302eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 13031da177e4SLinus Torvalds */ 1304877211f5STakashi Iwai int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime, 13051da177e4SLinus Torvalds unsigned int cond, 13061da177e4SLinus Torvalds snd_pcm_hw_param_t var, 13071464189fSMark Brown const struct snd_pcm_hw_constraint_list *l) 13081da177e4SLinus Torvalds { 13091da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 13101464189fSMark Brown snd_pcm_hw_rule_list, (void *)l, 13111da177e4SLinus Torvalds var, -1); 13121da177e4SLinus Torvalds } 13131da177e4SLinus Torvalds 1314e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_list); 1315e88e8ae6STakashi Iwai 1316f66f898eSPeter Rosin static int snd_pcm_hw_rule_ranges(struct snd_pcm_hw_params *params, 1317f66f898eSPeter Rosin struct snd_pcm_hw_rule *rule) 1318f66f898eSPeter Rosin { 1319f66f898eSPeter Rosin struct snd_pcm_hw_constraint_ranges *r = rule->private; 1320f66f898eSPeter Rosin return snd_interval_ranges(hw_param_interval(params, rule->var), 1321f66f898eSPeter Rosin r->count, r->ranges, r->mask); 1322f66f898eSPeter Rosin } 1323f66f898eSPeter Rosin 1324f66f898eSPeter Rosin 1325f66f898eSPeter Rosin /** 1326f66f898eSPeter Rosin * snd_pcm_hw_constraint_ranges - apply list of range constraints to a parameter 1327f66f898eSPeter Rosin * @runtime: PCM runtime instance 1328f66f898eSPeter Rosin * @cond: condition bits 1329f66f898eSPeter Rosin * @var: hw_params variable to apply the list of range constraints 1330f66f898eSPeter Rosin * @r: ranges 1331f66f898eSPeter Rosin * 1332f66f898eSPeter Rosin * Apply the list of range constraints to an interval parameter. 1333f66f898eSPeter Rosin * 1334f66f898eSPeter Rosin * Return: Zero if successful, or a negative error code on failure. 1335f66f898eSPeter Rosin */ 1336f66f898eSPeter Rosin int snd_pcm_hw_constraint_ranges(struct snd_pcm_runtime *runtime, 1337f66f898eSPeter Rosin unsigned int cond, 1338f66f898eSPeter Rosin snd_pcm_hw_param_t var, 1339f66f898eSPeter Rosin const struct snd_pcm_hw_constraint_ranges *r) 1340f66f898eSPeter Rosin { 1341f66f898eSPeter Rosin return snd_pcm_hw_rule_add(runtime, cond, var, 1342f66f898eSPeter Rosin snd_pcm_hw_rule_ranges, (void *)r, 1343f66f898eSPeter Rosin var, -1); 1344f66f898eSPeter Rosin } 1345f66f898eSPeter Rosin EXPORT_SYMBOL(snd_pcm_hw_constraint_ranges); 1346f66f898eSPeter Rosin 1347877211f5STakashi Iwai static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params, 1348877211f5STakashi Iwai struct snd_pcm_hw_rule *rule) 13491da177e4SLinus Torvalds { 1350e5e113cfSLars-Peter Clausen const struct snd_pcm_hw_constraint_ratnums *r = rule->private; 13511da177e4SLinus Torvalds unsigned int num = 0, den = 0; 13521da177e4SLinus Torvalds int err; 13531da177e4SLinus Torvalds err = snd_interval_ratnum(hw_param_interval(params, rule->var), 13541da177e4SLinus Torvalds r->nrats, r->rats, &num, &den); 13551da177e4SLinus Torvalds if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { 13561da177e4SLinus Torvalds params->rate_num = num; 13571da177e4SLinus Torvalds params->rate_den = den; 13581da177e4SLinus Torvalds } 13591da177e4SLinus Torvalds return err; 13601da177e4SLinus Torvalds } 13611da177e4SLinus Torvalds 13621da177e4SLinus Torvalds /** 13631c85cc64SRandy Dunlap * snd_pcm_hw_constraint_ratnums - apply ratnums constraint to a parameter 1364df8db936STakashi Iwai * @runtime: PCM runtime instance 1365df8db936STakashi Iwai * @cond: condition bits 1366df8db936STakashi Iwai * @var: hw_params variable to apply the ratnums constraint 1367877211f5STakashi Iwai * @r: struct snd_ratnums constriants 1368eb7c06e8SYacine Belkadi * 1369eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 13701da177e4SLinus Torvalds */ 1371877211f5STakashi Iwai int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime, 13721da177e4SLinus Torvalds unsigned int cond, 13731da177e4SLinus Torvalds snd_pcm_hw_param_t var, 1374e5e113cfSLars-Peter Clausen const struct snd_pcm_hw_constraint_ratnums *r) 13751da177e4SLinus Torvalds { 13761da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 1377e5e113cfSLars-Peter Clausen snd_pcm_hw_rule_ratnums, (void *)r, 13781da177e4SLinus Torvalds var, -1); 13791da177e4SLinus Torvalds } 13801da177e4SLinus Torvalds 1381e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums); 1382e88e8ae6STakashi Iwai 1383877211f5STakashi Iwai static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params, 1384877211f5STakashi Iwai struct snd_pcm_hw_rule *rule) 13851da177e4SLinus Torvalds { 1386e5e113cfSLars-Peter Clausen const struct snd_pcm_hw_constraint_ratdens *r = rule->private; 13871da177e4SLinus Torvalds unsigned int num = 0, den = 0; 13881da177e4SLinus Torvalds int err = snd_interval_ratden(hw_param_interval(params, rule->var), 13891da177e4SLinus Torvalds r->nrats, r->rats, &num, &den); 13901da177e4SLinus Torvalds if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { 13911da177e4SLinus Torvalds params->rate_num = num; 13921da177e4SLinus Torvalds params->rate_den = den; 13931da177e4SLinus Torvalds } 13941da177e4SLinus Torvalds return err; 13951da177e4SLinus Torvalds } 13961da177e4SLinus Torvalds 13971da177e4SLinus Torvalds /** 13981c85cc64SRandy Dunlap * snd_pcm_hw_constraint_ratdens - apply ratdens constraint to a parameter 1399df8db936STakashi Iwai * @runtime: PCM runtime instance 1400df8db936STakashi Iwai * @cond: condition bits 1401df8db936STakashi Iwai * @var: hw_params variable to apply the ratdens constraint 1402877211f5STakashi Iwai * @r: struct snd_ratdens constriants 1403eb7c06e8SYacine Belkadi * 1404eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 14051da177e4SLinus Torvalds */ 1406877211f5STakashi Iwai int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime, 14071da177e4SLinus Torvalds unsigned int cond, 14081da177e4SLinus Torvalds snd_pcm_hw_param_t var, 1409e5e113cfSLars-Peter Clausen const struct snd_pcm_hw_constraint_ratdens *r) 14101da177e4SLinus Torvalds { 14111da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 1412e5e113cfSLars-Peter Clausen snd_pcm_hw_rule_ratdens, (void *)r, 14131da177e4SLinus Torvalds var, -1); 14141da177e4SLinus Torvalds } 14151da177e4SLinus Torvalds 1416e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_ratdens); 1417e88e8ae6STakashi Iwai 1418877211f5STakashi Iwai static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params, 1419877211f5STakashi Iwai struct snd_pcm_hw_rule *rule) 14201da177e4SLinus Torvalds { 14211da177e4SLinus Torvalds unsigned int l = (unsigned long) rule->private; 14221da177e4SLinus Torvalds int width = l & 0xffff; 14231da177e4SLinus Torvalds unsigned int msbits = l >> 16; 1424b55f9fdcSTakashi Sakamoto const struct snd_interval *i = 1425b55f9fdcSTakashi Sakamoto hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); 14268ef9df55SLars-Peter Clausen 14278ef9df55SLars-Peter Clausen if (!snd_interval_single(i)) 14288ef9df55SLars-Peter Clausen return 0; 14298ef9df55SLars-Peter Clausen 14308ef9df55SLars-Peter Clausen if ((snd_interval_value(i) == width) || 14318ef9df55SLars-Peter Clausen (width == 0 && snd_interval_value(i) > msbits)) 143219f52faeSLars-Peter Clausen params->msbits = min_not_zero(params->msbits, msbits); 14338ef9df55SLars-Peter Clausen 14341da177e4SLinus Torvalds return 0; 14351da177e4SLinus Torvalds } 14361da177e4SLinus Torvalds 14371da177e4SLinus Torvalds /** 14381c85cc64SRandy Dunlap * snd_pcm_hw_constraint_msbits - add a hw constraint msbits rule 1439df8db936STakashi Iwai * @runtime: PCM runtime instance 1440df8db936STakashi Iwai * @cond: condition bits 1441df8db936STakashi Iwai * @width: sample bits width 1442df8db936STakashi Iwai * @msbits: msbits width 1443eb7c06e8SYacine Belkadi * 14448ef9df55SLars-Peter Clausen * This constraint will set the number of most significant bits (msbits) if a 14458ef9df55SLars-Peter Clausen * sample format with the specified width has been select. If width is set to 0 14468ef9df55SLars-Peter Clausen * the msbits will be set for any sample format with a width larger than the 14478ef9df55SLars-Peter Clausen * specified msbits. 14488ef9df55SLars-Peter Clausen * 1449eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 14501da177e4SLinus Torvalds */ 1451877211f5STakashi Iwai int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime, 14521da177e4SLinus Torvalds unsigned int cond, 14531da177e4SLinus Torvalds unsigned int width, 14541da177e4SLinus Torvalds unsigned int msbits) 14551da177e4SLinus Torvalds { 14561da177e4SLinus Torvalds unsigned long l = (msbits << 16) | width; 14571da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, -1, 14581da177e4SLinus Torvalds snd_pcm_hw_rule_msbits, 14591da177e4SLinus Torvalds (void*) l, 14601da177e4SLinus Torvalds SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); 14611da177e4SLinus Torvalds } 14621da177e4SLinus Torvalds 1463e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_msbits); 1464e88e8ae6STakashi Iwai 1465877211f5STakashi Iwai static int snd_pcm_hw_rule_step(struct snd_pcm_hw_params *params, 1466877211f5STakashi Iwai struct snd_pcm_hw_rule *rule) 14671da177e4SLinus Torvalds { 14681da177e4SLinus Torvalds unsigned long step = (unsigned long) rule->private; 14690f519b62SClemens Ladisch return snd_interval_step(hw_param_interval(params, rule->var), step); 14701da177e4SLinus Torvalds } 14711da177e4SLinus Torvalds 14721da177e4SLinus Torvalds /** 14731c85cc64SRandy Dunlap * snd_pcm_hw_constraint_step - add a hw constraint step rule 1474df8db936STakashi Iwai * @runtime: PCM runtime instance 1475df8db936STakashi Iwai * @cond: condition bits 1476df8db936STakashi Iwai * @var: hw_params variable to apply the step constraint 1477df8db936STakashi Iwai * @step: step size 1478eb7c06e8SYacine Belkadi * 1479eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 14801da177e4SLinus Torvalds */ 1481877211f5STakashi Iwai int snd_pcm_hw_constraint_step(struct snd_pcm_runtime *runtime, 14821da177e4SLinus Torvalds unsigned int cond, 14831da177e4SLinus Torvalds snd_pcm_hw_param_t var, 14841da177e4SLinus Torvalds unsigned long step) 14851da177e4SLinus Torvalds { 14861da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 14871da177e4SLinus Torvalds snd_pcm_hw_rule_step, (void *) step, 14881da177e4SLinus Torvalds var, -1); 14891da177e4SLinus Torvalds } 14901da177e4SLinus Torvalds 1491e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_step); 1492e88e8ae6STakashi Iwai 1493877211f5STakashi Iwai static int snd_pcm_hw_rule_pow2(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) 14941da177e4SLinus Torvalds { 149567c39317SMarcin Ślusarz static unsigned int pow2_sizes[] = { 14961da177e4SLinus Torvalds 1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7, 14971da177e4SLinus Torvalds 1<<8, 1<<9, 1<<10, 1<<11, 1<<12, 1<<13, 1<<14, 1<<15, 14981da177e4SLinus Torvalds 1<<16, 1<<17, 1<<18, 1<<19, 1<<20, 1<<21, 1<<22, 1<<23, 14991da177e4SLinus Torvalds 1<<24, 1<<25, 1<<26, 1<<27, 1<<28, 1<<29, 1<<30 15001da177e4SLinus Torvalds }; 15011da177e4SLinus Torvalds return snd_interval_list(hw_param_interval(params, rule->var), 15021da177e4SLinus Torvalds ARRAY_SIZE(pow2_sizes), pow2_sizes, 0); 15031da177e4SLinus Torvalds } 15041da177e4SLinus Torvalds 15051da177e4SLinus Torvalds /** 15061c85cc64SRandy Dunlap * snd_pcm_hw_constraint_pow2 - add a hw constraint power-of-2 rule 1507df8db936STakashi Iwai * @runtime: PCM runtime instance 1508df8db936STakashi Iwai * @cond: condition bits 1509df8db936STakashi Iwai * @var: hw_params variable to apply the power-of-2 constraint 1510eb7c06e8SYacine Belkadi * 1511eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 15121da177e4SLinus Torvalds */ 1513877211f5STakashi Iwai int snd_pcm_hw_constraint_pow2(struct snd_pcm_runtime *runtime, 15141da177e4SLinus Torvalds unsigned int cond, 15151da177e4SLinus Torvalds snd_pcm_hw_param_t var) 15161da177e4SLinus Torvalds { 15171da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 15181da177e4SLinus Torvalds snd_pcm_hw_rule_pow2, NULL, 15191da177e4SLinus Torvalds var, -1); 15201da177e4SLinus Torvalds } 15211da177e4SLinus Torvalds 1522e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_pow2); 1523e88e8ae6STakashi Iwai 1524d5b702a6SClemens Ladisch static int snd_pcm_hw_rule_noresample_func(struct snd_pcm_hw_params *params, 1525d5b702a6SClemens Ladisch struct snd_pcm_hw_rule *rule) 1526d5b702a6SClemens Ladisch { 1527d5b702a6SClemens Ladisch unsigned int base_rate = (unsigned int)(uintptr_t)rule->private; 1528d5b702a6SClemens Ladisch struct snd_interval *rate; 1529d5b702a6SClemens Ladisch 1530d5b702a6SClemens Ladisch rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); 1531d5b702a6SClemens Ladisch return snd_interval_list(rate, 1, &base_rate, 0); 1532d5b702a6SClemens Ladisch } 1533d5b702a6SClemens Ladisch 1534d5b702a6SClemens Ladisch /** 1535d5b702a6SClemens Ladisch * snd_pcm_hw_rule_noresample - add a rule to allow disabling hw resampling 1536d5b702a6SClemens Ladisch * @runtime: PCM runtime instance 1537d5b702a6SClemens Ladisch * @base_rate: the rate at which the hardware does not resample 1538eb7c06e8SYacine Belkadi * 1539eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 1540d5b702a6SClemens Ladisch */ 1541d5b702a6SClemens Ladisch int snd_pcm_hw_rule_noresample(struct snd_pcm_runtime *runtime, 1542d5b702a6SClemens Ladisch unsigned int base_rate) 1543d5b702a6SClemens Ladisch { 1544d5b702a6SClemens Ladisch return snd_pcm_hw_rule_add(runtime, SNDRV_PCM_HW_PARAMS_NORESAMPLE, 1545d5b702a6SClemens Ladisch SNDRV_PCM_HW_PARAM_RATE, 1546d5b702a6SClemens Ladisch snd_pcm_hw_rule_noresample_func, 1547d5b702a6SClemens Ladisch (void *)(uintptr_t)base_rate, 1548d5b702a6SClemens Ladisch SNDRV_PCM_HW_PARAM_RATE, -1); 1549d5b702a6SClemens Ladisch } 1550d5b702a6SClemens Ladisch EXPORT_SYMBOL(snd_pcm_hw_rule_noresample); 1551d5b702a6SClemens Ladisch 1552877211f5STakashi Iwai static void _snd_pcm_hw_param_any(struct snd_pcm_hw_params *params, 1553123992f7SAdrian Bunk snd_pcm_hw_param_t var) 15541da177e4SLinus Torvalds { 15551da177e4SLinus Torvalds if (hw_is_mask(var)) { 15561da177e4SLinus Torvalds snd_mask_any(hw_param_mask(params, var)); 15571da177e4SLinus Torvalds params->cmask |= 1 << var; 15581da177e4SLinus Torvalds params->rmask |= 1 << var; 15591da177e4SLinus Torvalds return; 15601da177e4SLinus Torvalds } 15611da177e4SLinus Torvalds if (hw_is_interval(var)) { 15621da177e4SLinus Torvalds snd_interval_any(hw_param_interval(params, var)); 15631da177e4SLinus Torvalds params->cmask |= 1 << var; 15641da177e4SLinus Torvalds params->rmask |= 1 << var; 15651da177e4SLinus Torvalds return; 15661da177e4SLinus Torvalds } 15671da177e4SLinus Torvalds snd_BUG(); 15681da177e4SLinus Torvalds } 15691da177e4SLinus Torvalds 1570877211f5STakashi Iwai void _snd_pcm_hw_params_any(struct snd_pcm_hw_params *params) 15711da177e4SLinus Torvalds { 15721da177e4SLinus Torvalds unsigned int k; 15731da177e4SLinus Torvalds memset(params, 0, sizeof(*params)); 15741da177e4SLinus Torvalds for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) 15751da177e4SLinus Torvalds _snd_pcm_hw_param_any(params, k); 15761da177e4SLinus Torvalds for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) 15771da177e4SLinus Torvalds _snd_pcm_hw_param_any(params, k); 15781da177e4SLinus Torvalds params->info = ~0U; 15791da177e4SLinus Torvalds } 15801da177e4SLinus Torvalds 1581e88e8ae6STakashi Iwai EXPORT_SYMBOL(_snd_pcm_hw_params_any); 15821da177e4SLinus Torvalds 15831da177e4SLinus Torvalds /** 15841c85cc64SRandy Dunlap * snd_pcm_hw_param_value - return @params field @var value 1585df8db936STakashi Iwai * @params: the hw_params instance 1586df8db936STakashi Iwai * @var: parameter to retrieve 15871c85cc64SRandy Dunlap * @dir: pointer to the direction (-1,0,1) or %NULL 15881da177e4SLinus Torvalds * 1589eb7c06e8SYacine Belkadi * Return: The value for field @var if it's fixed in configuration space 1590eb7c06e8SYacine Belkadi * defined by @params. -%EINVAL otherwise. 15911da177e4SLinus Torvalds */ 1592e88e8ae6STakashi Iwai int snd_pcm_hw_param_value(const struct snd_pcm_hw_params *params, 15931da177e4SLinus Torvalds snd_pcm_hw_param_t var, int *dir) 15941da177e4SLinus Torvalds { 15951da177e4SLinus Torvalds if (hw_is_mask(var)) { 1596877211f5STakashi Iwai const struct snd_mask *mask = hw_param_mask_c(params, var); 15971da177e4SLinus Torvalds if (!snd_mask_single(mask)) 15981da177e4SLinus Torvalds return -EINVAL; 15991da177e4SLinus Torvalds if (dir) 16001da177e4SLinus Torvalds *dir = 0; 16011da177e4SLinus Torvalds return snd_mask_value(mask); 16021da177e4SLinus Torvalds } 16031da177e4SLinus Torvalds if (hw_is_interval(var)) { 1604877211f5STakashi Iwai const struct snd_interval *i = hw_param_interval_c(params, var); 16051da177e4SLinus Torvalds if (!snd_interval_single(i)) 16061da177e4SLinus Torvalds return -EINVAL; 16071da177e4SLinus Torvalds if (dir) 16081da177e4SLinus Torvalds *dir = i->openmin; 16091da177e4SLinus Torvalds return snd_interval_value(i); 16101da177e4SLinus Torvalds } 16111da177e4SLinus Torvalds return -EINVAL; 16121da177e4SLinus Torvalds } 16131da177e4SLinus Torvalds 1614e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_param_value); 16151da177e4SLinus Torvalds 1616877211f5STakashi Iwai void _snd_pcm_hw_param_setempty(struct snd_pcm_hw_params *params, 16171da177e4SLinus Torvalds snd_pcm_hw_param_t var) 16181da177e4SLinus Torvalds { 16191da177e4SLinus Torvalds if (hw_is_mask(var)) { 16201da177e4SLinus Torvalds snd_mask_none(hw_param_mask(params, var)); 16211da177e4SLinus Torvalds params->cmask |= 1 << var; 16221da177e4SLinus Torvalds params->rmask |= 1 << var; 16231da177e4SLinus Torvalds } else if (hw_is_interval(var)) { 16241da177e4SLinus Torvalds snd_interval_none(hw_param_interval(params, var)); 16251da177e4SLinus Torvalds params->cmask |= 1 << var; 16261da177e4SLinus Torvalds params->rmask |= 1 << var; 16271da177e4SLinus Torvalds } else { 16281da177e4SLinus Torvalds snd_BUG(); 16291da177e4SLinus Torvalds } 16301da177e4SLinus Torvalds } 16311da177e4SLinus Torvalds 1632e88e8ae6STakashi Iwai EXPORT_SYMBOL(_snd_pcm_hw_param_setempty); 16331da177e4SLinus Torvalds 1634877211f5STakashi Iwai static int _snd_pcm_hw_param_first(struct snd_pcm_hw_params *params, 16351da177e4SLinus Torvalds snd_pcm_hw_param_t var) 16361da177e4SLinus Torvalds { 16371da177e4SLinus Torvalds int changed; 16381da177e4SLinus Torvalds if (hw_is_mask(var)) 16391da177e4SLinus Torvalds changed = snd_mask_refine_first(hw_param_mask(params, var)); 16401da177e4SLinus Torvalds else if (hw_is_interval(var)) 16411da177e4SLinus Torvalds changed = snd_interval_refine_first(hw_param_interval(params, var)); 16422f4ca8e5STakashi Iwai else 16431da177e4SLinus Torvalds return -EINVAL; 16441da177e4SLinus Torvalds if (changed) { 16451da177e4SLinus Torvalds params->cmask |= 1 << var; 16461da177e4SLinus Torvalds params->rmask |= 1 << var; 16471da177e4SLinus Torvalds } 16481da177e4SLinus Torvalds return changed; 16491da177e4SLinus Torvalds } 16501da177e4SLinus Torvalds 16511da177e4SLinus Torvalds 16521da177e4SLinus Torvalds /** 16531c85cc64SRandy Dunlap * snd_pcm_hw_param_first - refine config space and return minimum value 1654df8db936STakashi Iwai * @pcm: PCM instance 1655df8db936STakashi Iwai * @params: the hw_params instance 1656df8db936STakashi Iwai * @var: parameter to retrieve 16571c85cc64SRandy Dunlap * @dir: pointer to the direction (-1,0,1) or %NULL 16581da177e4SLinus Torvalds * 16591c85cc64SRandy Dunlap * Inside configuration space defined by @params remove from @var all 16601da177e4SLinus Torvalds * values > minimum. Reduce configuration space accordingly. 1661eb7c06e8SYacine Belkadi * 1662eb7c06e8SYacine Belkadi * Return: The minimum, or a negative error code on failure. 16631da177e4SLinus Torvalds */ 1664e88e8ae6STakashi Iwai int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm, 1665877211f5STakashi Iwai struct snd_pcm_hw_params *params, 16661da177e4SLinus Torvalds snd_pcm_hw_param_t var, int *dir) 16671da177e4SLinus Torvalds { 16681da177e4SLinus Torvalds int changed = _snd_pcm_hw_param_first(params, var); 16691da177e4SLinus Torvalds if (changed < 0) 16701da177e4SLinus Torvalds return changed; 16711da177e4SLinus Torvalds if (params->rmask) { 16721da177e4SLinus Torvalds int err = snd_pcm_hw_refine(pcm, params); 16737eaa943cSTakashi Iwai if (snd_BUG_ON(err < 0)) 16747eaa943cSTakashi Iwai return err; 16751da177e4SLinus Torvalds } 16761da177e4SLinus Torvalds return snd_pcm_hw_param_value(params, var, dir); 16771da177e4SLinus Torvalds } 16781da177e4SLinus Torvalds 1679e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_param_first); 1680e88e8ae6STakashi Iwai 1681877211f5STakashi Iwai static int _snd_pcm_hw_param_last(struct snd_pcm_hw_params *params, 16821da177e4SLinus Torvalds snd_pcm_hw_param_t var) 16831da177e4SLinus Torvalds { 16841da177e4SLinus Torvalds int changed; 16851da177e4SLinus Torvalds if (hw_is_mask(var)) 16861da177e4SLinus Torvalds changed = snd_mask_refine_last(hw_param_mask(params, var)); 16871da177e4SLinus Torvalds else if (hw_is_interval(var)) 16881da177e4SLinus Torvalds changed = snd_interval_refine_last(hw_param_interval(params, var)); 16892f4ca8e5STakashi Iwai else 16901da177e4SLinus Torvalds return -EINVAL; 16911da177e4SLinus Torvalds if (changed) { 16921da177e4SLinus Torvalds params->cmask |= 1 << var; 16931da177e4SLinus Torvalds params->rmask |= 1 << var; 16941da177e4SLinus Torvalds } 16951da177e4SLinus Torvalds return changed; 16961da177e4SLinus Torvalds } 16971da177e4SLinus Torvalds 16981da177e4SLinus Torvalds 16991da177e4SLinus Torvalds /** 17001c85cc64SRandy Dunlap * snd_pcm_hw_param_last - refine config space and return maximum value 1701df8db936STakashi Iwai * @pcm: PCM instance 1702df8db936STakashi Iwai * @params: the hw_params instance 1703df8db936STakashi Iwai * @var: parameter to retrieve 17041c85cc64SRandy Dunlap * @dir: pointer to the direction (-1,0,1) or %NULL 17051da177e4SLinus Torvalds * 17061c85cc64SRandy Dunlap * Inside configuration space defined by @params remove from @var all 17071da177e4SLinus Torvalds * values < maximum. Reduce configuration space accordingly. 1708eb7c06e8SYacine Belkadi * 1709eb7c06e8SYacine Belkadi * Return: The maximum, or a negative error code on failure. 17101da177e4SLinus Torvalds */ 1711e88e8ae6STakashi Iwai int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm, 1712877211f5STakashi Iwai struct snd_pcm_hw_params *params, 17131da177e4SLinus Torvalds snd_pcm_hw_param_t var, int *dir) 17141da177e4SLinus Torvalds { 17151da177e4SLinus Torvalds int changed = _snd_pcm_hw_param_last(params, var); 17161da177e4SLinus Torvalds if (changed < 0) 17171da177e4SLinus Torvalds return changed; 17181da177e4SLinus Torvalds if (params->rmask) { 17191da177e4SLinus Torvalds int err = snd_pcm_hw_refine(pcm, params); 17207eaa943cSTakashi Iwai if (snd_BUG_ON(err < 0)) 17217eaa943cSTakashi Iwai return err; 17221da177e4SLinus Torvalds } 17231da177e4SLinus Torvalds return snd_pcm_hw_param_value(params, var, dir); 17241da177e4SLinus Torvalds } 17251da177e4SLinus Torvalds 1726e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_param_last); 17271da177e4SLinus Torvalds 17281da177e4SLinus Torvalds /** 17291c85cc64SRandy Dunlap * snd_pcm_hw_param_choose - choose a configuration defined by @params 1730df8db936STakashi Iwai * @pcm: PCM instance 1731df8db936STakashi Iwai * @params: the hw_params instance 17321da177e4SLinus Torvalds * 17331c85cc64SRandy Dunlap * Choose one configuration from configuration space defined by @params. 17341da177e4SLinus Torvalds * The configuration chosen is that obtained fixing in this order: 17351da177e4SLinus Torvalds * first access, first format, first subformat, min channels, 17361da177e4SLinus Torvalds * min rate, min period time, max buffer size, min tick time 1737eb7c06e8SYacine Belkadi * 1738eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 17391da177e4SLinus Torvalds */ 17402f4ca8e5STakashi Iwai int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm, 17412f4ca8e5STakashi Iwai struct snd_pcm_hw_params *params) 17421da177e4SLinus Torvalds { 1743b46fe5d9STakashi Sakamoto static const int vars[] = { 17442f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_ACCESS, 17452f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_FORMAT, 17462f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_SUBFORMAT, 17472f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_CHANNELS, 17482f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_RATE, 17492f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_PERIOD_TIME, 17502f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 17512f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_TICK_TIME, 17522f4ca8e5STakashi Iwai -1 17532f4ca8e5STakashi Iwai }; 1754b46fe5d9STakashi Sakamoto const int *v; 1755b46fe5d9STakashi Sakamoto int err; 17561da177e4SLinus Torvalds 17572f4ca8e5STakashi Iwai for (v = vars; *v != -1; v++) { 17582f4ca8e5STakashi Iwai if (*v != SNDRV_PCM_HW_PARAM_BUFFER_SIZE) 17592f4ca8e5STakashi Iwai err = snd_pcm_hw_param_first(pcm, params, *v, NULL); 17602f4ca8e5STakashi Iwai else 17612f4ca8e5STakashi Iwai err = snd_pcm_hw_param_last(pcm, params, *v, NULL); 17627eaa943cSTakashi Iwai if (snd_BUG_ON(err < 0)) 17637eaa943cSTakashi Iwai return err; 17642f4ca8e5STakashi Iwai } 17651da177e4SLinus Torvalds return 0; 17661da177e4SLinus Torvalds } 17671da177e4SLinus Torvalds 1768877211f5STakashi Iwai static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream, 17691da177e4SLinus Torvalds void *arg) 17701da177e4SLinus Torvalds { 1771877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 17721da177e4SLinus Torvalds unsigned long flags; 17731da177e4SLinus Torvalds snd_pcm_stream_lock_irqsave(substream, flags); 17741da177e4SLinus Torvalds if (snd_pcm_running(substream) && 17751da177e4SLinus Torvalds snd_pcm_update_hw_ptr(substream) >= 0) 17761da177e4SLinus Torvalds runtime->status->hw_ptr %= runtime->buffer_size; 17770e8014d7SPierre-Louis Bossart else { 17781da177e4SLinus Torvalds runtime->status->hw_ptr = 0; 17790e8014d7SPierre-Louis Bossart runtime->hw_ptr_wrap = 0; 17800e8014d7SPierre-Louis Bossart } 17811da177e4SLinus Torvalds snd_pcm_stream_unlock_irqrestore(substream, flags); 17821da177e4SLinus Torvalds return 0; 17831da177e4SLinus Torvalds } 17841da177e4SLinus Torvalds 1785877211f5STakashi Iwai static int snd_pcm_lib_ioctl_channel_info(struct snd_pcm_substream *substream, 17861da177e4SLinus Torvalds void *arg) 17871da177e4SLinus Torvalds { 1788877211f5STakashi Iwai struct snd_pcm_channel_info *info = arg; 1789877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 17901da177e4SLinus Torvalds int width; 17911da177e4SLinus Torvalds if (!(runtime->info & SNDRV_PCM_INFO_MMAP)) { 17921da177e4SLinus Torvalds info->offset = -1; 17931da177e4SLinus Torvalds return 0; 17941da177e4SLinus Torvalds } 17951da177e4SLinus Torvalds width = snd_pcm_format_physical_width(runtime->format); 17961da177e4SLinus Torvalds if (width < 0) 17971da177e4SLinus Torvalds return width; 17981da177e4SLinus Torvalds info->offset = 0; 17991da177e4SLinus Torvalds switch (runtime->access) { 18001da177e4SLinus Torvalds case SNDRV_PCM_ACCESS_MMAP_INTERLEAVED: 18011da177e4SLinus Torvalds case SNDRV_PCM_ACCESS_RW_INTERLEAVED: 18021da177e4SLinus Torvalds info->first = info->channel * width; 18031da177e4SLinus Torvalds info->step = runtime->channels * width; 18041da177e4SLinus Torvalds break; 18051da177e4SLinus Torvalds case SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED: 18061da177e4SLinus Torvalds case SNDRV_PCM_ACCESS_RW_NONINTERLEAVED: 18071da177e4SLinus Torvalds { 18081da177e4SLinus Torvalds size_t size = runtime->dma_bytes / runtime->channels; 18091da177e4SLinus Torvalds info->first = info->channel * size * 8; 18101da177e4SLinus Torvalds info->step = width; 18111da177e4SLinus Torvalds break; 18121da177e4SLinus Torvalds } 18131da177e4SLinus Torvalds default: 18141da177e4SLinus Torvalds snd_BUG(); 18151da177e4SLinus Torvalds break; 18161da177e4SLinus Torvalds } 18171da177e4SLinus Torvalds return 0; 18181da177e4SLinus Torvalds } 18191da177e4SLinus Torvalds 18208bea869cSJaroslav Kysela static int snd_pcm_lib_ioctl_fifo_size(struct snd_pcm_substream *substream, 18218bea869cSJaroslav Kysela void *arg) 18228bea869cSJaroslav Kysela { 18238bea869cSJaroslav Kysela struct snd_pcm_hw_params *params = arg; 18248bea869cSJaroslav Kysela snd_pcm_format_t format; 1825a9960e6aSClemens Ladisch int channels; 1826a9960e6aSClemens Ladisch ssize_t frame_size; 18278bea869cSJaroslav Kysela 18288bea869cSJaroslav Kysela params->fifo_size = substream->runtime->hw.fifo_size; 18298bea869cSJaroslav Kysela if (!(substream->runtime->hw.info & SNDRV_PCM_INFO_FIFO_IN_FRAMES)) { 18308bea869cSJaroslav Kysela format = params_format(params); 18318bea869cSJaroslav Kysela channels = params_channels(params); 1832a9960e6aSClemens Ladisch frame_size = snd_pcm_format_size(format, channels); 1833a9960e6aSClemens Ladisch if (frame_size > 0) 1834a9960e6aSClemens Ladisch params->fifo_size /= (unsigned)frame_size; 18358bea869cSJaroslav Kysela } 18368bea869cSJaroslav Kysela return 0; 18378bea869cSJaroslav Kysela } 18388bea869cSJaroslav Kysela 18391da177e4SLinus Torvalds /** 18401da177e4SLinus Torvalds * snd_pcm_lib_ioctl - a generic PCM ioctl callback 18411da177e4SLinus Torvalds * @substream: the pcm substream instance 18421da177e4SLinus Torvalds * @cmd: ioctl command 18431da177e4SLinus Torvalds * @arg: ioctl argument 18441da177e4SLinus Torvalds * 18451da177e4SLinus Torvalds * Processes the generic ioctl commands for PCM. 18461da177e4SLinus Torvalds * Can be passed as the ioctl callback for PCM ops. 18471da177e4SLinus Torvalds * 1848eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 18491da177e4SLinus Torvalds */ 1850877211f5STakashi Iwai int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream, 18511da177e4SLinus Torvalds unsigned int cmd, void *arg) 18521da177e4SLinus Torvalds { 18531da177e4SLinus Torvalds switch (cmd) { 18541da177e4SLinus Torvalds case SNDRV_PCM_IOCTL1_INFO: 18551da177e4SLinus Torvalds return 0; 18561da177e4SLinus Torvalds case SNDRV_PCM_IOCTL1_RESET: 18571da177e4SLinus Torvalds return snd_pcm_lib_ioctl_reset(substream, arg); 18581da177e4SLinus Torvalds case SNDRV_PCM_IOCTL1_CHANNEL_INFO: 18591da177e4SLinus Torvalds return snd_pcm_lib_ioctl_channel_info(substream, arg); 18608bea869cSJaroslav Kysela case SNDRV_PCM_IOCTL1_FIFO_SIZE: 18618bea869cSJaroslav Kysela return snd_pcm_lib_ioctl_fifo_size(substream, arg); 18621da177e4SLinus Torvalds } 18631da177e4SLinus Torvalds return -ENXIO; 18641da177e4SLinus Torvalds } 18651da177e4SLinus Torvalds 1866e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_lib_ioctl); 1867e88e8ae6STakashi Iwai 18681da177e4SLinus Torvalds /** 18691da177e4SLinus Torvalds * snd_pcm_period_elapsed - update the pcm status for the next period 18701da177e4SLinus Torvalds * @substream: the pcm substream instance 18711da177e4SLinus Torvalds * 18721da177e4SLinus Torvalds * This function is called from the interrupt handler when the 18731da177e4SLinus Torvalds * PCM has processed the period size. It will update the current 187431e8960bSTakashi Iwai * pointer, wake up sleepers, etc. 18751da177e4SLinus Torvalds * 18761da177e4SLinus Torvalds * Even if more than one periods have elapsed since the last call, you 18771da177e4SLinus Torvalds * have to call this only once. 18781da177e4SLinus Torvalds */ 1879877211f5STakashi Iwai void snd_pcm_period_elapsed(struct snd_pcm_substream *substream) 18801da177e4SLinus Torvalds { 1881877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 18821da177e4SLinus Torvalds unsigned long flags; 18831da177e4SLinus Torvalds 18847eaa943cSTakashi Iwai if (PCM_RUNTIME_CHECK(substream)) 18857eaa943cSTakashi Iwai return; 18861da177e4SLinus Torvalds runtime = substream->runtime; 18871da177e4SLinus Torvalds 18881da177e4SLinus Torvalds snd_pcm_stream_lock_irqsave(substream, flags); 18891da177e4SLinus Torvalds if (!snd_pcm_running(substream) || 1890f240406bSJaroslav Kysela snd_pcm_update_hw_ptr0(substream, 1) < 0) 18911da177e4SLinus Torvalds goto _end; 18921da177e4SLinus Torvalds 189390bbaf66SJie Yang #ifdef CONFIG_SND_PCM_TIMER 18941da177e4SLinus Torvalds if (substream->timer_running) 18951da177e4SLinus Torvalds snd_timer_interrupt(substream->timer, 1); 189690bbaf66SJie Yang #endif 18971da177e4SLinus Torvalds _end: 18981da177e4SLinus Torvalds kill_fasync(&runtime->fasync, SIGIO, POLL_IN); 18993aa02cb6STakashi Iwai snd_pcm_stream_unlock_irqrestore(substream, flags); 19001da177e4SLinus Torvalds } 19011da177e4SLinus Torvalds 1902e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_period_elapsed); 1903e88e8ae6STakashi Iwai 190413075510STakashi Iwai /* 190513075510STakashi Iwai * Wait until avail_min data becomes available 190613075510STakashi Iwai * Returns a negative error code if any error occurs during operation. 190713075510STakashi Iwai * The available space is stored on availp. When err = 0 and avail = 0 190813075510STakashi Iwai * on the capture stream, it indicates the stream is in DRAINING state. 190913075510STakashi Iwai */ 19105daeba34SDavid Dillow static int wait_for_avail(struct snd_pcm_substream *substream, 191113075510STakashi Iwai snd_pcm_uframes_t *availp) 191213075510STakashi Iwai { 191313075510STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 191413075510STakashi Iwai int is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 191513075510STakashi Iwai wait_queue_t wait; 191613075510STakashi Iwai int err = 0; 191713075510STakashi Iwai snd_pcm_uframes_t avail = 0; 1918f2b3614cSTakashi Iwai long wait_time, tout; 191913075510STakashi Iwai 1920763437a9SArjan van de Ven init_waitqueue_entry(&wait, current); 1921763437a9SArjan van de Ven set_current_state(TASK_INTERRUPTIBLE); 1922763437a9SArjan van de Ven add_wait_queue(&runtime->tsleep, &wait); 1923763437a9SArjan van de Ven 1924f2b3614cSTakashi Iwai if (runtime->no_period_wakeup) 1925f2b3614cSTakashi Iwai wait_time = MAX_SCHEDULE_TIMEOUT; 1926f2b3614cSTakashi Iwai else { 1927f2b3614cSTakashi Iwai wait_time = 10; 1928f2b3614cSTakashi Iwai if (runtime->rate) { 1929f2b3614cSTakashi Iwai long t = runtime->period_size * 2 / runtime->rate; 1930f2b3614cSTakashi Iwai wait_time = max(t, wait_time); 1931f2b3614cSTakashi Iwai } 1932f2b3614cSTakashi Iwai wait_time = msecs_to_jiffies(wait_time * 1000); 1933f2b3614cSTakashi Iwai } 1934763437a9SArjan van de Ven 193513075510STakashi Iwai for (;;) { 193613075510STakashi Iwai if (signal_pending(current)) { 193713075510STakashi Iwai err = -ERESTARTSYS; 193813075510STakashi Iwai break; 193913075510STakashi Iwai } 1940763437a9SArjan van de Ven 1941763437a9SArjan van de Ven /* 1942763437a9SArjan van de Ven * We need to check if space became available already 1943763437a9SArjan van de Ven * (and thus the wakeup happened already) first to close 1944763437a9SArjan van de Ven * the race of space already having become available. 1945763437a9SArjan van de Ven * This check must happen after been added to the waitqueue 1946763437a9SArjan van de Ven * and having current state be INTERRUPTIBLE. 1947763437a9SArjan van de Ven */ 1948763437a9SArjan van de Ven if (is_playback) 1949763437a9SArjan van de Ven avail = snd_pcm_playback_avail(runtime); 1950763437a9SArjan van de Ven else 1951763437a9SArjan van de Ven avail = snd_pcm_capture_avail(runtime); 1952763437a9SArjan van de Ven if (avail >= runtime->twake) 1953763437a9SArjan van de Ven break; 195413075510STakashi Iwai snd_pcm_stream_unlock_irq(substream); 1955763437a9SArjan van de Ven 1956763437a9SArjan van de Ven tout = schedule_timeout(wait_time); 1957763437a9SArjan van de Ven 195813075510STakashi Iwai snd_pcm_stream_lock_irq(substream); 1959763437a9SArjan van de Ven set_current_state(TASK_INTERRUPTIBLE); 196013075510STakashi Iwai switch (runtime->status->state) { 196113075510STakashi Iwai case SNDRV_PCM_STATE_SUSPENDED: 196213075510STakashi Iwai err = -ESTRPIPE; 196313075510STakashi Iwai goto _endloop; 196413075510STakashi Iwai case SNDRV_PCM_STATE_XRUN: 196513075510STakashi Iwai err = -EPIPE; 196613075510STakashi Iwai goto _endloop; 196713075510STakashi Iwai case SNDRV_PCM_STATE_DRAINING: 196813075510STakashi Iwai if (is_playback) 196913075510STakashi Iwai err = -EPIPE; 197013075510STakashi Iwai else 197113075510STakashi Iwai avail = 0; /* indicate draining */ 197213075510STakashi Iwai goto _endloop; 197313075510STakashi Iwai case SNDRV_PCM_STATE_OPEN: 197413075510STakashi Iwai case SNDRV_PCM_STATE_SETUP: 197513075510STakashi Iwai case SNDRV_PCM_STATE_DISCONNECTED: 197613075510STakashi Iwai err = -EBADFD; 197713075510STakashi Iwai goto _endloop; 1978ed697e1aSJongHo Kim case SNDRV_PCM_STATE_PAUSED: 1979ed697e1aSJongHo Kim continue; 198013075510STakashi Iwai } 198113075510STakashi Iwai if (!tout) { 198209e56df8STakashi Iwai pcm_dbg(substream->pcm, 198309e56df8STakashi Iwai "%s write error (DMA or IRQ trouble?)\n", 198413075510STakashi Iwai is_playback ? "playback" : "capture"); 198513075510STakashi Iwai err = -EIO; 198613075510STakashi Iwai break; 198713075510STakashi Iwai } 198813075510STakashi Iwai } 198913075510STakashi Iwai _endloop: 1990763437a9SArjan van de Ven set_current_state(TASK_RUNNING); 1991c91a988dSJaroslav Kysela remove_wait_queue(&runtime->tsleep, &wait); 199213075510STakashi Iwai *availp = avail; 199313075510STakashi Iwai return err; 199413075510STakashi Iwai } 199513075510STakashi Iwai 1996bdc4acf7STakashi Iwai typedef int (*transfer_f)(struct snd_pcm_substream *substream, unsigned int hwoff, 1997bdc4acf7STakashi Iwai unsigned long data, unsigned int off, 1998bdc4acf7STakashi Iwai snd_pcm_uframes_t size); 1999bdc4acf7STakashi Iwai 2000877211f5STakashi Iwai static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream, 20011da177e4SLinus Torvalds unsigned int hwoff, 20021da177e4SLinus Torvalds unsigned long data, unsigned int off, 20031da177e4SLinus Torvalds snd_pcm_uframes_t frames) 20041da177e4SLinus Torvalds { 2005877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 20061da177e4SLinus Torvalds int err; 20071da177e4SLinus Torvalds char __user *buf = (char __user *) data + frames_to_bytes(runtime, off); 200829d1a873STakashi Iwai if (substream->ops->copy_user) { 200929d1a873STakashi Iwai hwoff = frames_to_bytes(runtime, hwoff); 201029d1a873STakashi Iwai frames = frames_to_bytes(runtime, frames); 201129d1a873STakashi Iwai err = substream->ops->copy_user(substream, 0, hwoff, buf, frames); 201229d1a873STakashi Iwai if (err < 0) 201329d1a873STakashi Iwai return err; 20141da177e4SLinus Torvalds } else { 20151da177e4SLinus Torvalds char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); 20161da177e4SLinus Torvalds if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames))) 20171da177e4SLinus Torvalds return -EFAULT; 20181da177e4SLinus Torvalds } 20191da177e4SLinus Torvalds return 0; 20201da177e4SLinus Torvalds } 20211da177e4SLinus Torvalds 2022bdc4acf7STakashi Iwai static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream, 2023bdc4acf7STakashi Iwai unsigned int hwoff, 20241da177e4SLinus Torvalds unsigned long data, unsigned int off, 2025bdc4acf7STakashi Iwai snd_pcm_uframes_t frames) 2026bdc4acf7STakashi Iwai { 2027bdc4acf7STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 2028bdc4acf7STakashi Iwai int err; 2029bdc4acf7STakashi Iwai void __user **bufs = (void __user **)data; 2030bdc4acf7STakashi Iwai int channels = runtime->channels; 2031bdc4acf7STakashi Iwai char __user *buf; 2032bdc4acf7STakashi Iwai int c; 2033bdc4acf7STakashi Iwai 2034bdc4acf7STakashi Iwai if (substream->ops->copy_user) { 2035bdc4acf7STakashi Iwai hwoff = samples_to_bytes(runtime, hwoff); 2036bdc4acf7STakashi Iwai off = samples_to_bytes(runtime, off); 2037bdc4acf7STakashi Iwai frames = samples_to_bytes(runtime, frames); 2038bdc4acf7STakashi Iwai for (c = 0; c < channels; ++c, ++bufs) { 2039bdc4acf7STakashi Iwai buf = *bufs + off; 2040bdc4acf7STakashi Iwai if (!*bufs) { 2041bdc4acf7STakashi Iwai if (snd_BUG_ON(!substream->ops->fill_silence)) 2042bdc4acf7STakashi Iwai return -EINVAL; 2043bdc4acf7STakashi Iwai err = substream->ops->fill_silence(substream, c, 2044bdc4acf7STakashi Iwai hwoff, 2045bdc4acf7STakashi Iwai frames); 2046bdc4acf7STakashi Iwai } else { 2047bdc4acf7STakashi Iwai err = substream->ops->copy_user(substream, c, 2048bdc4acf7STakashi Iwai hwoff, buf, 2049bdc4acf7STakashi Iwai frames); 2050bdc4acf7STakashi Iwai } 2051bdc4acf7STakashi Iwai if (err < 0) 2052bdc4acf7STakashi Iwai return err; 2053bdc4acf7STakashi Iwai } 2054bdc4acf7STakashi Iwai } else { 2055bdc4acf7STakashi Iwai /* default transfer behaviour */ 2056bdc4acf7STakashi Iwai size_t dma_csize = runtime->dma_bytes / channels; 2057bdc4acf7STakashi Iwai for (c = 0; c < channels; ++c, ++bufs) { 2058bdc4acf7STakashi Iwai char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff); 2059bdc4acf7STakashi Iwai if (*bufs == NULL) { 2060bdc4acf7STakashi Iwai snd_pcm_format_set_silence(runtime->format, hwbuf, frames); 2061bdc4acf7STakashi Iwai } else { 2062bdc4acf7STakashi Iwai char __user *buf = *bufs + samples_to_bytes(runtime, off); 2063bdc4acf7STakashi Iwai if (copy_from_user(hwbuf, buf, samples_to_bytes(runtime, frames))) 2064bdc4acf7STakashi Iwai return -EFAULT; 2065bdc4acf7STakashi Iwai } 2066bdc4acf7STakashi Iwai } 2067bdc4acf7STakashi Iwai } 2068bdc4acf7STakashi Iwai return 0; 2069bdc4acf7STakashi Iwai } 2070bdc4acf7STakashi Iwai 2071bdc4acf7STakashi Iwai /* sanity-check for read/write methods */ 2072bdc4acf7STakashi Iwai static int pcm_sanity_check(struct snd_pcm_substream *substream) 2073bdc4acf7STakashi Iwai { 2074bdc4acf7STakashi Iwai struct snd_pcm_runtime *runtime; 2075bdc4acf7STakashi Iwai if (PCM_RUNTIME_CHECK(substream)) 2076bdc4acf7STakashi Iwai return -ENXIO; 2077bdc4acf7STakashi Iwai runtime = substream->runtime; 2078bdc4acf7STakashi Iwai if (snd_BUG_ON(!substream->ops->copy_user && !runtime->dma_area)) 2079bdc4acf7STakashi Iwai return -EINVAL; 2080bdc4acf7STakashi Iwai if (runtime->status->state == SNDRV_PCM_STATE_OPEN) 2081bdc4acf7STakashi Iwai return -EBADFD; 2082bdc4acf7STakashi Iwai return 0; 2083bdc4acf7STakashi Iwai } 20841da177e4SLinus Torvalds 20856ba63929STakashi Iwai static int pcm_accessible_state(struct snd_pcm_runtime *runtime) 20866ba63929STakashi Iwai { 20876ba63929STakashi Iwai switch (runtime->status->state) { 20886ba63929STakashi Iwai case SNDRV_PCM_STATE_PREPARED: 20896ba63929STakashi Iwai case SNDRV_PCM_STATE_RUNNING: 20906ba63929STakashi Iwai case SNDRV_PCM_STATE_PAUSED: 20916ba63929STakashi Iwai return 0; 20926ba63929STakashi Iwai case SNDRV_PCM_STATE_XRUN: 20936ba63929STakashi Iwai return -EPIPE; 20946ba63929STakashi Iwai case SNDRV_PCM_STATE_SUSPENDED: 20956ba63929STakashi Iwai return -ESTRPIPE; 20966ba63929STakashi Iwai default: 20976ba63929STakashi Iwai return -EBADFD; 20986ba63929STakashi Iwai } 20996ba63929STakashi Iwai } 21006ba63929STakashi Iwai 2101877211f5STakashi Iwai static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, 21021da177e4SLinus Torvalds unsigned long data, 21031da177e4SLinus Torvalds snd_pcm_uframes_t size, 21041da177e4SLinus Torvalds int nonblock, 21051da177e4SLinus Torvalds transfer_f transfer) 21061da177e4SLinus Torvalds { 2107877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 21081da177e4SLinus Torvalds snd_pcm_uframes_t xfer = 0; 21091da177e4SLinus Torvalds snd_pcm_uframes_t offset = 0; 21100910c216STakashi Iwai snd_pcm_uframes_t avail; 21111da177e4SLinus Torvalds int err = 0; 21121da177e4SLinus Torvalds 21131da177e4SLinus Torvalds if (size == 0) 21141da177e4SLinus Torvalds return 0; 21151da177e4SLinus Torvalds 21161da177e4SLinus Torvalds snd_pcm_stream_lock_irq(substream); 21176ba63929STakashi Iwai err = pcm_accessible_state(runtime); 21186ba63929STakashi Iwai if (err < 0) 21191da177e4SLinus Torvalds goto _end_unlock; 21201da177e4SLinus Torvalds 21215daeba34SDavid Dillow runtime->twake = runtime->control->avail_min ? : 1; 212231e8960bSTakashi Iwai if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) 21231da177e4SLinus Torvalds snd_pcm_update_hw_ptr(substream); 21241da177e4SLinus Torvalds avail = snd_pcm_playback_avail(runtime); 21250910c216STakashi Iwai while (size > 0) { 21260910c216STakashi Iwai snd_pcm_uframes_t frames, appl_ptr, appl_ofs; 21270910c216STakashi Iwai snd_pcm_uframes_t cont; 212813075510STakashi Iwai if (!avail) { 21291da177e4SLinus Torvalds if (nonblock) { 21301da177e4SLinus Torvalds err = -EAGAIN; 21311da177e4SLinus Torvalds goto _end_unlock; 21321da177e4SLinus Torvalds } 21335daeba34SDavid Dillow runtime->twake = min_t(snd_pcm_uframes_t, size, 21345daeba34SDavid Dillow runtime->control->avail_min ? : 1); 21355daeba34SDavid Dillow err = wait_for_avail(substream, &avail); 213613075510STakashi Iwai if (err < 0) 21371da177e4SLinus Torvalds goto _end_unlock; 21381da177e4SLinus Torvalds } 21391da177e4SLinus Torvalds frames = size > avail ? avail : size; 21401da177e4SLinus Torvalds cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; 21411da177e4SLinus Torvalds if (frames > cont) 21421da177e4SLinus Torvalds frames = cont; 21437eaa943cSTakashi Iwai if (snd_BUG_ON(!frames)) { 2144c91a988dSJaroslav Kysela runtime->twake = 0; 21457eaa943cSTakashi Iwai snd_pcm_stream_unlock_irq(substream); 21467eaa943cSTakashi Iwai return -EINVAL; 21477eaa943cSTakashi Iwai } 21481da177e4SLinus Torvalds appl_ptr = runtime->control->appl_ptr; 21491da177e4SLinus Torvalds appl_ofs = appl_ptr % runtime->buffer_size; 21501da177e4SLinus Torvalds snd_pcm_stream_unlock_irq(substream); 21511250932eSJaroslav Kysela err = transfer(substream, appl_ofs, data, offset, frames); 21521da177e4SLinus Torvalds snd_pcm_stream_lock_irq(substream); 21531250932eSJaroslav Kysela if (err < 0) 21541250932eSJaroslav Kysela goto _end_unlock; 21556ba63929STakashi Iwai err = pcm_accessible_state(runtime); 21566ba63929STakashi Iwai if (err < 0) 21571da177e4SLinus Torvalds goto _end_unlock; 21581da177e4SLinus Torvalds appl_ptr += frames; 21591da177e4SLinus Torvalds if (appl_ptr >= runtime->boundary) 21601da177e4SLinus Torvalds appl_ptr -= runtime->boundary; 21611da177e4SLinus Torvalds runtime->control->appl_ptr = appl_ptr; 21621da177e4SLinus Torvalds if (substream->ops->ack) 21631da177e4SLinus Torvalds substream->ops->ack(substream); 21641da177e4SLinus Torvalds 21651da177e4SLinus Torvalds offset += frames; 21661da177e4SLinus Torvalds size -= frames; 21671da177e4SLinus Torvalds xfer += frames; 21680910c216STakashi Iwai avail -= frames; 21691da177e4SLinus Torvalds if (runtime->status->state == SNDRV_PCM_STATE_PREPARED && 21701da177e4SLinus Torvalds snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) { 21711da177e4SLinus Torvalds err = snd_pcm_start(substream); 21721da177e4SLinus Torvalds if (err < 0) 21731da177e4SLinus Torvalds goto _end_unlock; 21741da177e4SLinus Torvalds } 21751da177e4SLinus Torvalds } 21761da177e4SLinus Torvalds _end_unlock: 2177c91a988dSJaroslav Kysela runtime->twake = 0; 21781250932eSJaroslav Kysela if (xfer > 0 && err >= 0) 21791250932eSJaroslav Kysela snd_pcm_update_state(substream, runtime); 21801da177e4SLinus Torvalds snd_pcm_stream_unlock_irq(substream); 21811da177e4SLinus Torvalds return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; 21821da177e4SLinus Torvalds } 21831da177e4SLinus Torvalds 2184877211f5STakashi Iwai snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const void __user *buf, snd_pcm_uframes_t size) 21851da177e4SLinus Torvalds { 2186877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 21871da177e4SLinus Torvalds int nonblock; 21887eaa943cSTakashi Iwai int err; 21891da177e4SLinus Torvalds 21907eaa943cSTakashi Iwai err = pcm_sanity_check(substream); 21917eaa943cSTakashi Iwai if (err < 0) 21927eaa943cSTakashi Iwai return err; 21931da177e4SLinus Torvalds runtime = substream->runtime; 21940df63e44STakashi Iwai nonblock = !!(substream->f_flags & O_NONBLOCK); 21951da177e4SLinus Torvalds 21961da177e4SLinus Torvalds if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && 21971da177e4SLinus Torvalds runtime->channels > 1) 21981da177e4SLinus Torvalds return -EINVAL; 21991da177e4SLinus Torvalds return snd_pcm_lib_write1(substream, (unsigned long)buf, size, nonblock, 22001da177e4SLinus Torvalds snd_pcm_lib_write_transfer); 22011da177e4SLinus Torvalds } 22021da177e4SLinus Torvalds 2203e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_lib_write); 2204e88e8ae6STakashi Iwai 2205877211f5STakashi Iwai snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream, 22061da177e4SLinus Torvalds void __user **bufs, 22071da177e4SLinus Torvalds snd_pcm_uframes_t frames) 22081da177e4SLinus Torvalds { 2209877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 22101da177e4SLinus Torvalds int nonblock; 22117eaa943cSTakashi Iwai int err; 22121da177e4SLinus Torvalds 22137eaa943cSTakashi Iwai err = pcm_sanity_check(substream); 22147eaa943cSTakashi Iwai if (err < 0) 22157eaa943cSTakashi Iwai return err; 22161da177e4SLinus Torvalds runtime = substream->runtime; 22170df63e44STakashi Iwai nonblock = !!(substream->f_flags & O_NONBLOCK); 22181da177e4SLinus Torvalds 22191da177e4SLinus Torvalds if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) 22201da177e4SLinus Torvalds return -EINVAL; 22211da177e4SLinus Torvalds return snd_pcm_lib_write1(substream, (unsigned long)bufs, frames, 22221da177e4SLinus Torvalds nonblock, snd_pcm_lib_writev_transfer); 22231da177e4SLinus Torvalds } 22241da177e4SLinus Torvalds 2225e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_lib_writev); 2226e88e8ae6STakashi Iwai 2227877211f5STakashi Iwai static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream, 22281da177e4SLinus Torvalds unsigned int hwoff, 22291da177e4SLinus Torvalds unsigned long data, unsigned int off, 22301da177e4SLinus Torvalds snd_pcm_uframes_t frames) 22311da177e4SLinus Torvalds { 2232877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 22331da177e4SLinus Torvalds int err; 22341da177e4SLinus Torvalds char __user *buf = (char __user *) data + frames_to_bytes(runtime, off); 223529d1a873STakashi Iwai if (substream->ops->copy_user) { 223629d1a873STakashi Iwai hwoff = frames_to_bytes(runtime, hwoff); 223729d1a873STakashi Iwai frames = frames_to_bytes(runtime, frames); 223829d1a873STakashi Iwai err = substream->ops->copy_user(substream, 0, hwoff, buf, frames); 223929d1a873STakashi Iwai if (err < 0) 224029d1a873STakashi Iwai return err; 22411da177e4SLinus Torvalds } else { 22421da177e4SLinus Torvalds char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); 22431da177e4SLinus Torvalds if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames))) 22441da177e4SLinus Torvalds return -EFAULT; 22451da177e4SLinus Torvalds } 22461da177e4SLinus Torvalds return 0; 22471da177e4SLinus Torvalds } 22481da177e4SLinus Torvalds 2249bdc4acf7STakashi Iwai static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream, 2250bdc4acf7STakashi Iwai unsigned int hwoff, 2251bdc4acf7STakashi Iwai unsigned long data, unsigned int off, 2252bdc4acf7STakashi Iwai snd_pcm_uframes_t frames) 2253bdc4acf7STakashi Iwai { 2254bdc4acf7STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 2255bdc4acf7STakashi Iwai int err; 2256bdc4acf7STakashi Iwai void __user **bufs = (void __user **)data; 2257bdc4acf7STakashi Iwai int channels = runtime->channels; 2258bdc4acf7STakashi Iwai char __user *buf; 2259bdc4acf7STakashi Iwai char *hwbuf; 2260bdc4acf7STakashi Iwai int c; 2261bdc4acf7STakashi Iwai 2262bdc4acf7STakashi Iwai if (substream->ops->copy_user) { 2263bdc4acf7STakashi Iwai hwoff = samples_to_bytes(runtime, hwoff); 2264bdc4acf7STakashi Iwai off = samples_to_bytes(runtime, off); 2265bdc4acf7STakashi Iwai frames = samples_to_bytes(runtime, frames); 2266bdc4acf7STakashi Iwai for (c = 0; c < channels; ++c, ++bufs) { 2267bdc4acf7STakashi Iwai if (!*bufs) 2268bdc4acf7STakashi Iwai continue; 2269bdc4acf7STakashi Iwai err = substream->ops->copy_user(substream, c, hwoff, 2270bdc4acf7STakashi Iwai *bufs + off, frames); 2271bdc4acf7STakashi Iwai if (err < 0) 2272bdc4acf7STakashi Iwai return err; 2273bdc4acf7STakashi Iwai } 2274bdc4acf7STakashi Iwai } else { 2275bdc4acf7STakashi Iwai snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels; 2276bdc4acf7STakashi Iwai for (c = 0; c < channels; ++c, ++bufs) { 2277bdc4acf7STakashi Iwai if (*bufs == NULL) 2278bdc4acf7STakashi Iwai continue; 2279bdc4acf7STakashi Iwai 2280bdc4acf7STakashi Iwai hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff); 2281bdc4acf7STakashi Iwai buf = *bufs + samples_to_bytes(runtime, off); 2282bdc4acf7STakashi Iwai if (copy_to_user(buf, hwbuf, samples_to_bytes(runtime, frames))) 2283bdc4acf7STakashi Iwai return -EFAULT; 2284bdc4acf7STakashi Iwai } 2285bdc4acf7STakashi Iwai } 2286bdc4acf7STakashi Iwai return 0; 2287bdc4acf7STakashi Iwai } 2288bdc4acf7STakashi Iwai 2289877211f5STakashi Iwai static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, 22901da177e4SLinus Torvalds unsigned long data, 22911da177e4SLinus Torvalds snd_pcm_uframes_t size, 22921da177e4SLinus Torvalds int nonblock, 22931da177e4SLinus Torvalds transfer_f transfer) 22941da177e4SLinus Torvalds { 2295877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 22961da177e4SLinus Torvalds snd_pcm_uframes_t xfer = 0; 22971da177e4SLinus Torvalds snd_pcm_uframes_t offset = 0; 22980910c216STakashi Iwai snd_pcm_uframes_t avail; 22991da177e4SLinus Torvalds int err = 0; 23001da177e4SLinus Torvalds 23011da177e4SLinus Torvalds if (size == 0) 23021da177e4SLinus Torvalds return 0; 23031da177e4SLinus Torvalds 23041da177e4SLinus Torvalds snd_pcm_stream_lock_irq(substream); 23056ba63929STakashi Iwai err = pcm_accessible_state(runtime); 23061da177e4SLinus Torvalds if (err < 0) 23071da177e4SLinus Torvalds goto _end_unlock; 23086ba63929STakashi Iwai if (runtime->status->state == SNDRV_PCM_STATE_PREPARED && 23096ba63929STakashi Iwai size >= runtime->start_threshold) { 23106ba63929STakashi Iwai err = snd_pcm_start(substream); 23116ba63929STakashi Iwai if (err < 0) 23121da177e4SLinus Torvalds goto _end_unlock; 23131da177e4SLinus Torvalds } 23141da177e4SLinus Torvalds 23155daeba34SDavid Dillow runtime->twake = runtime->control->avail_min ? : 1; 231631e8960bSTakashi Iwai if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) 23171da177e4SLinus Torvalds snd_pcm_update_hw_ptr(substream); 23181da177e4SLinus Torvalds avail = snd_pcm_capture_avail(runtime); 23190910c216STakashi Iwai while (size > 0) { 23200910c216STakashi Iwai snd_pcm_uframes_t frames, appl_ptr, appl_ofs; 23210910c216STakashi Iwai snd_pcm_uframes_t cont; 2322d948035aSTakashi Iwai if (!avail) { 232313075510STakashi Iwai if (runtime->status->state == 232413075510STakashi Iwai SNDRV_PCM_STATE_DRAINING) { 232513075510STakashi Iwai snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); 23261da177e4SLinus Torvalds goto _end_unlock; 23271da177e4SLinus Torvalds } 23281da177e4SLinus Torvalds if (nonblock) { 23291da177e4SLinus Torvalds err = -EAGAIN; 23301da177e4SLinus Torvalds goto _end_unlock; 23311da177e4SLinus Torvalds } 23325daeba34SDavid Dillow runtime->twake = min_t(snd_pcm_uframes_t, size, 23335daeba34SDavid Dillow runtime->control->avail_min ? : 1); 23345daeba34SDavid Dillow err = wait_for_avail(substream, &avail); 233513075510STakashi Iwai if (err < 0) 23361da177e4SLinus Torvalds goto _end_unlock; 233713075510STakashi Iwai if (!avail) 233813075510STakashi Iwai continue; /* draining */ 23391da177e4SLinus Torvalds } 23401da177e4SLinus Torvalds frames = size > avail ? avail : size; 23411da177e4SLinus Torvalds cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; 23421da177e4SLinus Torvalds if (frames > cont) 23431da177e4SLinus Torvalds frames = cont; 23447eaa943cSTakashi Iwai if (snd_BUG_ON(!frames)) { 2345c91a988dSJaroslav Kysela runtime->twake = 0; 23467eaa943cSTakashi Iwai snd_pcm_stream_unlock_irq(substream); 23477eaa943cSTakashi Iwai return -EINVAL; 23487eaa943cSTakashi Iwai } 23491da177e4SLinus Torvalds appl_ptr = runtime->control->appl_ptr; 23501da177e4SLinus Torvalds appl_ofs = appl_ptr % runtime->buffer_size; 23511da177e4SLinus Torvalds snd_pcm_stream_unlock_irq(substream); 23521250932eSJaroslav Kysela err = transfer(substream, appl_ofs, data, offset, frames); 23531da177e4SLinus Torvalds snd_pcm_stream_lock_irq(substream); 23541250932eSJaroslav Kysela if (err < 0) 23551250932eSJaroslav Kysela goto _end_unlock; 23566ba63929STakashi Iwai err = pcm_accessible_state(runtime); 23576ba63929STakashi Iwai if (err < 0) 23581da177e4SLinus Torvalds goto _end_unlock; 23591da177e4SLinus Torvalds appl_ptr += frames; 23601da177e4SLinus Torvalds if (appl_ptr >= runtime->boundary) 23611da177e4SLinus Torvalds appl_ptr -= runtime->boundary; 23621da177e4SLinus Torvalds runtime->control->appl_ptr = appl_ptr; 23631da177e4SLinus Torvalds if (substream->ops->ack) 23641da177e4SLinus Torvalds substream->ops->ack(substream); 23651da177e4SLinus Torvalds 23661da177e4SLinus Torvalds offset += frames; 23671da177e4SLinus Torvalds size -= frames; 23681da177e4SLinus Torvalds xfer += frames; 23690910c216STakashi Iwai avail -= frames; 23701da177e4SLinus Torvalds } 23711da177e4SLinus Torvalds _end_unlock: 2372c91a988dSJaroslav Kysela runtime->twake = 0; 23731250932eSJaroslav Kysela if (xfer > 0 && err >= 0) 23741250932eSJaroslav Kysela snd_pcm_update_state(substream, runtime); 23751da177e4SLinus Torvalds snd_pcm_stream_unlock_irq(substream); 23761da177e4SLinus Torvalds return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; 23771da177e4SLinus Torvalds } 23781da177e4SLinus Torvalds 2379877211f5STakashi Iwai snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, void __user *buf, snd_pcm_uframes_t size) 23801da177e4SLinus Torvalds { 2381877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 23821da177e4SLinus Torvalds int nonblock; 23837eaa943cSTakashi Iwai int err; 23841da177e4SLinus Torvalds 23857eaa943cSTakashi Iwai err = pcm_sanity_check(substream); 23867eaa943cSTakashi Iwai if (err < 0) 23877eaa943cSTakashi Iwai return err; 23881da177e4SLinus Torvalds runtime = substream->runtime; 23890df63e44STakashi Iwai nonblock = !!(substream->f_flags & O_NONBLOCK); 23901da177e4SLinus Torvalds if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED) 23911da177e4SLinus Torvalds return -EINVAL; 23921da177e4SLinus Torvalds return snd_pcm_lib_read1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_read_transfer); 23931da177e4SLinus Torvalds } 23941da177e4SLinus Torvalds 2395e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_lib_read); 2396e88e8ae6STakashi Iwai 2397877211f5STakashi Iwai snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream, 23981da177e4SLinus Torvalds void __user **bufs, 23991da177e4SLinus Torvalds snd_pcm_uframes_t frames) 24001da177e4SLinus Torvalds { 2401877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 24021da177e4SLinus Torvalds int nonblock; 24037eaa943cSTakashi Iwai int err; 24041da177e4SLinus Torvalds 24057eaa943cSTakashi Iwai err = pcm_sanity_check(substream); 24067eaa943cSTakashi Iwai if (err < 0) 24077eaa943cSTakashi Iwai return err; 24081da177e4SLinus Torvalds runtime = substream->runtime; 24091da177e4SLinus Torvalds if (runtime->status->state == SNDRV_PCM_STATE_OPEN) 24101da177e4SLinus Torvalds return -EBADFD; 24111da177e4SLinus Torvalds 24120df63e44STakashi Iwai nonblock = !!(substream->f_flags & O_NONBLOCK); 24131da177e4SLinus Torvalds if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) 24141da177e4SLinus Torvalds return -EINVAL; 24151da177e4SLinus Torvalds return snd_pcm_lib_read1(substream, (unsigned long)bufs, frames, nonblock, snd_pcm_lib_readv_transfer); 24161da177e4SLinus Torvalds } 24171da177e4SLinus Torvalds 24181da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_lib_readv); 24192d3391ecSTakashi Iwai 24202d3391ecSTakashi Iwai /* 24212d3391ecSTakashi Iwai * standard channel mapping helpers 24222d3391ecSTakashi Iwai */ 24232d3391ecSTakashi Iwai 24242d3391ecSTakashi Iwai /* default channel maps for multi-channel playbacks, up to 8 channels */ 24252d3391ecSTakashi Iwai const struct snd_pcm_chmap_elem snd_pcm_std_chmaps[] = { 24262d3391ecSTakashi Iwai { .channels = 1, 24275efbc261STakashi Iwai .map = { SNDRV_CHMAP_MONO } }, 24282d3391ecSTakashi Iwai { .channels = 2, 24292d3391ecSTakashi Iwai .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, 24302d3391ecSTakashi Iwai { .channels = 4, 24312d3391ecSTakashi Iwai .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, 24322d3391ecSTakashi Iwai SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, 24332d3391ecSTakashi Iwai { .channels = 6, 24342d3391ecSTakashi Iwai .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, 24352d3391ecSTakashi Iwai SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, 24362d3391ecSTakashi Iwai SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } }, 24372d3391ecSTakashi Iwai { .channels = 8, 24382d3391ecSTakashi Iwai .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, 24392d3391ecSTakashi Iwai SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, 24402d3391ecSTakashi Iwai SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE, 24412d3391ecSTakashi Iwai SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } }, 24422d3391ecSTakashi Iwai { } 24432d3391ecSTakashi Iwai }; 24442d3391ecSTakashi Iwai EXPORT_SYMBOL_GPL(snd_pcm_std_chmaps); 24452d3391ecSTakashi Iwai 24462d3391ecSTakashi Iwai /* alternative channel maps with CLFE <-> surround swapped for 6/8 channels */ 24472d3391ecSTakashi Iwai const struct snd_pcm_chmap_elem snd_pcm_alt_chmaps[] = { 24482d3391ecSTakashi Iwai { .channels = 1, 24495efbc261STakashi Iwai .map = { SNDRV_CHMAP_MONO } }, 24502d3391ecSTakashi Iwai { .channels = 2, 24512d3391ecSTakashi Iwai .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, 24522d3391ecSTakashi Iwai { .channels = 4, 24532d3391ecSTakashi Iwai .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, 24542d3391ecSTakashi Iwai SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, 24552d3391ecSTakashi Iwai { .channels = 6, 24562d3391ecSTakashi Iwai .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, 24572d3391ecSTakashi Iwai SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE, 24582d3391ecSTakashi Iwai SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, 24592d3391ecSTakashi Iwai { .channels = 8, 24602d3391ecSTakashi Iwai .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, 24612d3391ecSTakashi Iwai SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE, 24622d3391ecSTakashi Iwai SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, 24632d3391ecSTakashi Iwai SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } }, 24642d3391ecSTakashi Iwai { } 24652d3391ecSTakashi Iwai }; 24662d3391ecSTakashi Iwai EXPORT_SYMBOL_GPL(snd_pcm_alt_chmaps); 24672d3391ecSTakashi Iwai 24682d3391ecSTakashi Iwai static bool valid_chmap_channels(const struct snd_pcm_chmap *info, int ch) 24692d3391ecSTakashi Iwai { 24702d3391ecSTakashi Iwai if (ch > info->max_channels) 24712d3391ecSTakashi Iwai return false; 24722d3391ecSTakashi Iwai return !info->channel_mask || (info->channel_mask & (1U << ch)); 24732d3391ecSTakashi Iwai } 24742d3391ecSTakashi Iwai 24752d3391ecSTakashi Iwai static int pcm_chmap_ctl_info(struct snd_kcontrol *kcontrol, 24762d3391ecSTakashi Iwai struct snd_ctl_elem_info *uinfo) 24772d3391ecSTakashi Iwai { 24782d3391ecSTakashi Iwai struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); 24792d3391ecSTakashi Iwai 24802d3391ecSTakashi Iwai uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 24812d3391ecSTakashi Iwai uinfo->count = 0; 24822d3391ecSTakashi Iwai uinfo->count = info->max_channels; 24832d3391ecSTakashi Iwai uinfo->value.integer.min = 0; 24842d3391ecSTakashi Iwai uinfo->value.integer.max = SNDRV_CHMAP_LAST; 24852d3391ecSTakashi Iwai return 0; 24862d3391ecSTakashi Iwai } 24872d3391ecSTakashi Iwai 24882d3391ecSTakashi Iwai /* get callback for channel map ctl element 24892d3391ecSTakashi Iwai * stores the channel position firstly matching with the current channels 24902d3391ecSTakashi Iwai */ 24912d3391ecSTakashi Iwai static int pcm_chmap_ctl_get(struct snd_kcontrol *kcontrol, 24922d3391ecSTakashi Iwai struct snd_ctl_elem_value *ucontrol) 24932d3391ecSTakashi Iwai { 24942d3391ecSTakashi Iwai struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); 24952d3391ecSTakashi Iwai unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 24962d3391ecSTakashi Iwai struct snd_pcm_substream *substream; 24972d3391ecSTakashi Iwai const struct snd_pcm_chmap_elem *map; 24982d3391ecSTakashi Iwai 24992d3391ecSTakashi Iwai if (snd_BUG_ON(!info->chmap)) 25002d3391ecSTakashi Iwai return -EINVAL; 25012d3391ecSTakashi Iwai substream = snd_pcm_chmap_substream(info, idx); 25022d3391ecSTakashi Iwai if (!substream) 25032d3391ecSTakashi Iwai return -ENODEV; 25042d3391ecSTakashi Iwai memset(ucontrol->value.integer.value, 0, 25052d3391ecSTakashi Iwai sizeof(ucontrol->value.integer.value)); 25062d3391ecSTakashi Iwai if (!substream->runtime) 25072d3391ecSTakashi Iwai return 0; /* no channels set */ 25082d3391ecSTakashi Iwai for (map = info->chmap; map->channels; map++) { 25092d3391ecSTakashi Iwai int i; 25102d3391ecSTakashi Iwai if (map->channels == substream->runtime->channels && 25112d3391ecSTakashi Iwai valid_chmap_channels(info, map->channels)) { 25122d3391ecSTakashi Iwai for (i = 0; i < map->channels; i++) 25132d3391ecSTakashi Iwai ucontrol->value.integer.value[i] = map->map[i]; 25142d3391ecSTakashi Iwai return 0; 25152d3391ecSTakashi Iwai } 25162d3391ecSTakashi Iwai } 25172d3391ecSTakashi Iwai return -EINVAL; 25182d3391ecSTakashi Iwai } 25192d3391ecSTakashi Iwai 25202d3391ecSTakashi Iwai /* tlv callback for channel map ctl element 25212d3391ecSTakashi Iwai * expands the pre-defined channel maps in a form of TLV 25222d3391ecSTakashi Iwai */ 25232d3391ecSTakashi Iwai static int pcm_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, 25242d3391ecSTakashi Iwai unsigned int size, unsigned int __user *tlv) 25252d3391ecSTakashi Iwai { 25262d3391ecSTakashi Iwai struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); 25272d3391ecSTakashi Iwai const struct snd_pcm_chmap_elem *map; 25282d3391ecSTakashi Iwai unsigned int __user *dst; 25292d3391ecSTakashi Iwai int c, count = 0; 25302d3391ecSTakashi Iwai 25312d3391ecSTakashi Iwai if (snd_BUG_ON(!info->chmap)) 25322d3391ecSTakashi Iwai return -EINVAL; 25332d3391ecSTakashi Iwai if (size < 8) 25342d3391ecSTakashi Iwai return -ENOMEM; 25352d3391ecSTakashi Iwai if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv)) 25362d3391ecSTakashi Iwai return -EFAULT; 25372d3391ecSTakashi Iwai size -= 8; 25382d3391ecSTakashi Iwai dst = tlv + 2; 25392d3391ecSTakashi Iwai for (map = info->chmap; map->channels; map++) { 25402d3391ecSTakashi Iwai int chs_bytes = map->channels * 4; 25412d3391ecSTakashi Iwai if (!valid_chmap_channels(info, map->channels)) 25422d3391ecSTakashi Iwai continue; 25432d3391ecSTakashi Iwai if (size < 8) 25442d3391ecSTakashi Iwai return -ENOMEM; 25452d3391ecSTakashi Iwai if (put_user(SNDRV_CTL_TLVT_CHMAP_FIXED, dst) || 25462d3391ecSTakashi Iwai put_user(chs_bytes, dst + 1)) 25472d3391ecSTakashi Iwai return -EFAULT; 25482d3391ecSTakashi Iwai dst += 2; 25492d3391ecSTakashi Iwai size -= 8; 25502d3391ecSTakashi Iwai count += 8; 25512d3391ecSTakashi Iwai if (size < chs_bytes) 25522d3391ecSTakashi Iwai return -ENOMEM; 25532d3391ecSTakashi Iwai size -= chs_bytes; 25542d3391ecSTakashi Iwai count += chs_bytes; 25552d3391ecSTakashi Iwai for (c = 0; c < map->channels; c++) { 25562d3391ecSTakashi Iwai if (put_user(map->map[c], dst)) 25572d3391ecSTakashi Iwai return -EFAULT; 25582d3391ecSTakashi Iwai dst++; 25592d3391ecSTakashi Iwai } 25602d3391ecSTakashi Iwai } 25612d3391ecSTakashi Iwai if (put_user(count, tlv + 1)) 25622d3391ecSTakashi Iwai return -EFAULT; 25632d3391ecSTakashi Iwai return 0; 25642d3391ecSTakashi Iwai } 25652d3391ecSTakashi Iwai 25662d3391ecSTakashi Iwai static void pcm_chmap_ctl_private_free(struct snd_kcontrol *kcontrol) 25672d3391ecSTakashi Iwai { 25682d3391ecSTakashi Iwai struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); 25692d3391ecSTakashi Iwai info->pcm->streams[info->stream].chmap_kctl = NULL; 25702d3391ecSTakashi Iwai kfree(info); 25712d3391ecSTakashi Iwai } 25722d3391ecSTakashi Iwai 25732d3391ecSTakashi Iwai /** 25742d3391ecSTakashi Iwai * snd_pcm_add_chmap_ctls - create channel-mapping control elements 25752d3391ecSTakashi Iwai * @pcm: the assigned PCM instance 25762d3391ecSTakashi Iwai * @stream: stream direction 25772d3391ecSTakashi Iwai * @chmap: channel map elements (for query) 25782d3391ecSTakashi Iwai * @max_channels: the max number of channels for the stream 25792d3391ecSTakashi Iwai * @private_value: the value passed to each kcontrol's private_value field 25802d3391ecSTakashi Iwai * @info_ret: store struct snd_pcm_chmap instance if non-NULL 25812d3391ecSTakashi Iwai * 25822d3391ecSTakashi Iwai * Create channel-mapping control elements assigned to the given PCM stream(s). 2583eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error value. 25842d3391ecSTakashi Iwai */ 25852d3391ecSTakashi Iwai int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream, 25862d3391ecSTakashi Iwai const struct snd_pcm_chmap_elem *chmap, 25872d3391ecSTakashi Iwai int max_channels, 25882d3391ecSTakashi Iwai unsigned long private_value, 25892d3391ecSTakashi Iwai struct snd_pcm_chmap **info_ret) 25902d3391ecSTakashi Iwai { 25912d3391ecSTakashi Iwai struct snd_pcm_chmap *info; 25922d3391ecSTakashi Iwai struct snd_kcontrol_new knew = { 25932d3391ecSTakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_PCM, 25942d3391ecSTakashi Iwai .access = SNDRV_CTL_ELEM_ACCESS_READ | 25952d3391ecSTakashi Iwai SNDRV_CTL_ELEM_ACCESS_TLV_READ | 25962d3391ecSTakashi Iwai SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, 25972d3391ecSTakashi Iwai .info = pcm_chmap_ctl_info, 25982d3391ecSTakashi Iwai .get = pcm_chmap_ctl_get, 25992d3391ecSTakashi Iwai .tlv.c = pcm_chmap_ctl_tlv, 26002d3391ecSTakashi Iwai }; 26012d3391ecSTakashi Iwai int err; 26022d3391ecSTakashi Iwai 26038d879be8STakashi Iwai if (WARN_ON(pcm->streams[stream].chmap_kctl)) 26048d879be8STakashi Iwai return -EBUSY; 26052d3391ecSTakashi Iwai info = kzalloc(sizeof(*info), GFP_KERNEL); 26062d3391ecSTakashi Iwai if (!info) 26072d3391ecSTakashi Iwai return -ENOMEM; 26082d3391ecSTakashi Iwai info->pcm = pcm; 26092d3391ecSTakashi Iwai info->stream = stream; 26102d3391ecSTakashi Iwai info->chmap = chmap; 26112d3391ecSTakashi Iwai info->max_channels = max_channels; 26122d3391ecSTakashi Iwai if (stream == SNDRV_PCM_STREAM_PLAYBACK) 26132d3391ecSTakashi Iwai knew.name = "Playback Channel Map"; 26142d3391ecSTakashi Iwai else 26152d3391ecSTakashi Iwai knew.name = "Capture Channel Map"; 26162d3391ecSTakashi Iwai knew.device = pcm->device; 26172d3391ecSTakashi Iwai knew.count = pcm->streams[stream].substream_count; 26182d3391ecSTakashi Iwai knew.private_value = private_value; 26192d3391ecSTakashi Iwai info->kctl = snd_ctl_new1(&knew, info); 26202d3391ecSTakashi Iwai if (!info->kctl) { 26212d3391ecSTakashi Iwai kfree(info); 26222d3391ecSTakashi Iwai return -ENOMEM; 26232d3391ecSTakashi Iwai } 26242d3391ecSTakashi Iwai info->kctl->private_free = pcm_chmap_ctl_private_free; 26252d3391ecSTakashi Iwai err = snd_ctl_add(pcm->card, info->kctl); 26262d3391ecSTakashi Iwai if (err < 0) 26272d3391ecSTakashi Iwai return err; 26282d3391ecSTakashi Iwai pcm->streams[stream].chmap_kctl = info->kctl; 26292d3391ecSTakashi Iwai if (info_ret) 26302d3391ecSTakashi Iwai *info_ret = info; 26312d3391ecSTakashi Iwai return 0; 26322d3391ecSTakashi Iwai } 26332d3391ecSTakashi Iwai EXPORT_SYMBOL_GPL(snd_pcm_add_chmap_ctls); 2634