pcm_lib.c 67.6 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
/*
 *  Digital Audio (PCM) abstract layer
3
 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
Linus Torvalds's avatar
Linus Torvalds committed
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 *                   Abramo Bagnara <abramo@alsa-project.org>
 *
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 */

#include <linux/slab.h>
24
#include <linux/sched/signal.h>
Linus Torvalds's avatar
Linus Torvalds committed
25
#include <linux/time.h>
26
#include <linux/math64.h>
27
#include <linux/export.h>
Linus Torvalds's avatar
Linus Torvalds committed
28
29
#include <sound/core.h>
#include <sound/control.h>
30
#include <sound/tlv.h>
Linus Torvalds's avatar
Linus Torvalds committed
31
32
33
34
35
#include <sound/info.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/timer.h>

36
37
#include "pcm_local.h"

38
39
40
41
42
43
44
45
46
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
#define CREATE_TRACE_POINTS
#include "pcm_trace.h"
#else
#define trace_hwptr(substream, pos, in_interrupt)
#define trace_xrun(substream)
#define trace_hw_ptr_error(substream, reason)
#endif

Linus Torvalds's avatar
Linus Torvalds committed
47
48
49
50
51
52
53
54
55
/*
 * fill ring buffer with silence
 * runtime->silence_start: starting pointer to silence area
 * runtime->silence_filled: size filled with silence
 * runtime->silence_threshold: threshold from application
 * runtime->silence_size: maximal size from application
 *
 * when runtime->silence_size >= runtime->boundary - fill processed area with silence immediately
 */
56
void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_uframes_t new_hw_ptr)
Linus Torvalds's avatar
Linus Torvalds committed
57
{
58
	struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds's avatar
Linus Torvalds committed
59
	snd_pcm_uframes_t frames, ofs, transfer;
60
61
	char *hwbuf;
	int err;
Linus Torvalds's avatar
Linus Torvalds committed
62
63
64
65
66
67
68
69
70
71
72
73
74

	if (runtime->silence_size < runtime->boundary) {
		snd_pcm_sframes_t noise_dist, n;
		if (runtime->silence_start != runtime->control->appl_ptr) {
			n = runtime->control->appl_ptr - runtime->silence_start;
			if (n < 0)
				n += runtime->boundary;
			if ((snd_pcm_uframes_t)n < runtime->silence_filled)
				runtime->silence_filled -= n;
			else
				runtime->silence_filled = 0;
			runtime->silence_start = runtime->control->appl_ptr;
		}
Takashi Iwai's avatar
Takashi Iwai committed
75
		if (runtime->silence_filled >= runtime->buffer_size)
Linus Torvalds's avatar
Linus Torvalds committed
76
77
78
79
80
81
82
83
84
85
			return;
		noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silence_filled;
		if (noise_dist >= (snd_pcm_sframes_t) runtime->silence_threshold)
			return;
		frames = runtime->silence_threshold - noise_dist;
		if (frames > runtime->silence_size)
			frames = runtime->silence_size;
	} else {
		if (new_hw_ptr == ULONG_MAX) {	/* initialization */
			snd_pcm_sframes_t avail = snd_pcm_playback_hw_avail(runtime);
86
87
			if (avail > runtime->buffer_size)
				avail = runtime->buffer_size;
Linus Torvalds's avatar
Linus Torvalds committed
88
89
90
91
92
93
94
95
96
97
98
99
			runtime->silence_filled = avail > 0 ? avail : 0;
			runtime->silence_start = (runtime->status->hw_ptr +
						  runtime->silence_filled) %
						 runtime->boundary;
		} else {
			ofs = runtime->status->hw_ptr;
			frames = new_hw_ptr - ofs;
			if ((snd_pcm_sframes_t)frames < 0)
				frames += runtime->boundary;
			runtime->silence_filled -= frames;
			if ((snd_pcm_sframes_t)runtime->silence_filled < 0) {
				runtime->silence_filled = 0;
100
				runtime->silence_start = new_hw_ptr;
Linus Torvalds's avatar
Linus Torvalds committed
101
			} else {
102
				runtime->silence_start = ofs;
Linus Torvalds's avatar
Linus Torvalds committed
103
104
105
106
			}
		}
		frames = runtime->buffer_size - runtime->silence_filled;
	}
107
108
	if (snd_BUG_ON(frames > runtime->buffer_size))
		return;
Linus Torvalds's avatar
Linus Torvalds committed
109
110
	if (frames == 0)
		return;
111
	ofs = runtime->silence_start % runtime->buffer_size;
Linus Torvalds's avatar
Linus Torvalds committed
112
113
114
115
	while (frames > 0) {
		transfer = ofs + frames > runtime->buffer_size ? runtime->buffer_size - ofs : frames;
		if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED ||
		    runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) {
116
117
118
119
120
			if (substream->ops->fill_silence) {
				err = substream->ops->fill_silence(substream, 0,
								   frames_to_bytes(runtime, ofs),
								   frames_to_bytes(runtime, transfer));
				snd_BUG_ON(err < 0);
Linus Torvalds's avatar
Linus Torvalds committed
121
			} else {
122
				hwbuf = runtime->dma_area + frames_to_bytes(runtime, ofs);
Linus Torvalds's avatar
Linus Torvalds committed
123
124
125
126
127
				snd_pcm_format_set_silence(runtime->format, hwbuf, transfer * runtime->channels);
			}
		} else {
			unsigned int c;
			unsigned int channels = runtime->channels;
128
129
130
131
132
133
134
			if (substream->ops->fill_silence) {
				for (c = 0; c < channels; ++c) {
					err = substream->ops->fill_silence(substream, c,
									   samples_to_bytes(runtime, ofs),
									   samples_to_bytes(runtime, transfer));
					snd_BUG_ON(err < 0);
				}
Linus Torvalds's avatar
Linus Torvalds committed
135
136
137
			} else {
				size_t dma_csize = runtime->dma_bytes / channels;
				for (c = 0; c < channels; ++c) {
138
					hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, ofs);
Linus Torvalds's avatar
Linus Torvalds committed
139
140
141
142
143
144
145
146
147
148
					snd_pcm_format_set_silence(runtime->format, hwbuf, transfer);
				}
			}
		}
		runtime->silence_filled += transfer;
		frames -= transfer;
		ofs = 0;
	}
}

149
150
#ifdef CONFIG_SND_DEBUG
void snd_pcm_debug_name(struct snd_pcm_substream *substream,
151
152
153
154
155
156
157
158
			   char *name, size_t len)
{
	snprintf(name, len, "pcmC%dD%d%c:%d",
		 substream->pcm->card->number,
		 substream->pcm->device,
		 substream->stream ? 'c' : 'p',
		 substream->number);
}
159
160
EXPORT_SYMBOL(snd_pcm_debug_name);
#endif
161

162
163
164
165
#define XRUN_DEBUG_BASIC	(1<<0)
#define XRUN_DEBUG_STACK	(1<<1)	/* dump also stack */
#define XRUN_DEBUG_JIFFIESCHECK	(1<<2)	/* do jiffies check */

Takashi Iwai's avatar
Takashi Iwai committed
166
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
167

168
169
#define xrun_debug(substream, mask) \
			((substream)->pstr->xrun_debug & (mask))
170
171
172
#else
#define xrun_debug(substream, mask)	0
#endif
Takashi Iwai's avatar
Takashi Iwai committed
173

174
175
176
#define dump_stack_on_xrun(substream) do {			\
		if (xrun_debug(substream, XRUN_DEBUG_STACK))	\
			dump_stack();				\
Takashi Iwai's avatar
Takashi Iwai committed
177
178
	} while (0)

179
static void xrun(struct snd_pcm_substream *substream)
Linus Torvalds's avatar
Linus Torvalds committed
180
{
181
182
	struct snd_pcm_runtime *runtime = substream->runtime;

183
	trace_xrun(substream);
184
185
	if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
		snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
Linus Torvalds's avatar
Linus Torvalds committed
186
	snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
187
	if (xrun_debug(substream, XRUN_DEBUG_BASIC)) {
188
		char name[16];
189
		snd_pcm_debug_name(substream, name, sizeof(name));
190
		pcm_warn(substream->pcm, "XRUN: %s\n", name);
Takashi Iwai's avatar
Takashi Iwai committed
191
		dump_stack_on_xrun(substream);
Linus Torvalds's avatar
Linus Torvalds committed
192
193
194
	}
}

195
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
196
#define hw_ptr_error(substream, in_interrupt, reason, fmt, args...)	\
197
	do {								\
198
		trace_hw_ptr_error(substream, reason);	\
199
		if (xrun_debug(substream, XRUN_DEBUG_BASIC)) {		\
200
201
			pr_err_ratelimited("ALSA: PCM: [%c] " reason ": " fmt, \
					   (in_interrupt) ? 'Q' : 'P', ##args);	\
202
203
204
205
206
207
208
209
210
211
			dump_stack_on_xrun(substream);			\
		}							\
	} while (0)

#else /* ! CONFIG_SND_PCM_XRUN_DEBUG */

#define hw_ptr_error(substream, fmt, args...) do { } while (0)

#endif

212
213
int snd_pcm_update_state(struct snd_pcm_substream *substream,
			 struct snd_pcm_runtime *runtime)
Linus Torvalds's avatar
Linus Torvalds committed
214
215
216
217
218
219
220
221
222
{
	snd_pcm_uframes_t avail;

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
		avail = snd_pcm_playback_avail(runtime);
	else
		avail = snd_pcm_capture_avail(runtime);
	if (avail > runtime->avail_max)
		runtime->avail_max = avail;
223
224
	if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
		if (avail >= runtime->buffer_size) {
Linus Torvalds's avatar
Linus Torvalds committed
225
			snd_pcm_drain_done(substream);
226
227
228
229
			return -EPIPE;
		}
	} else {
		if (avail >= runtime->stop_threshold) {
Linus Torvalds's avatar
Linus Torvalds committed
230
			xrun(substream);
231
232
			return -EPIPE;
		}
Linus Torvalds's avatar
Linus Torvalds committed
233
	}
234
235
236
237
238
	if (runtime->twake) {
		if (avail >= runtime->twake)
			wake_up(&runtime->tsleep);
	} else if (avail >= runtime->control->avail_min)
		wake_up(&runtime->sleep);
Linus Torvalds's avatar
Linus Torvalds committed
239
240
241
	return 0;
}

242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
static void update_audio_tstamp(struct snd_pcm_substream *substream,
				struct timespec *curr_tstamp,
				struct timespec *audio_tstamp)
{
	struct snd_pcm_runtime *runtime = substream->runtime;
	u64 audio_frames, audio_nsecs;
	struct timespec driver_tstamp;

	if (runtime->tstamp_mode != SNDRV_PCM_TSTAMP_ENABLE)
		return;

	if (!(substream->ops->get_time_info) ||
		(runtime->audio_tstamp_report.actual_type ==
			SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)) {

		/*
		 * provide audio timestamp derived from pointer position
		 * add delay only if requested
		 */

		audio_frames = runtime->hw_ptr_wrap + runtime->status->hw_ptr;

		if (runtime->audio_tstamp_config.report_delay) {
			if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
				audio_frames -=  runtime->delay;
			else
				audio_frames +=  runtime->delay;
		}
		audio_nsecs = div_u64(audio_frames * 1000000000LL,
				runtime->rate);
		*audio_tstamp = ns_to_timespec(audio_nsecs);
	}
	runtime->status->audio_tstamp = *audio_tstamp;
	runtime->status->tstamp = *curr_tstamp;

	/*
	 * re-take a driver timestamp to let apps detect if the reference tstamp
	 * read by low-level hardware was provided with a delay
	 */
	snd_pcm_gettime(substream->runtime, (struct timespec *)&driver_tstamp);
	runtime->driver_tstamp = driver_tstamp;
}

285
286
static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
				  unsigned int in_interrupt)
Linus Torvalds's avatar
Linus Torvalds committed
287
{
288
	struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds's avatar
Linus Torvalds committed
289
	snd_pcm_uframes_t pos;
290
	snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base;
291
292
	snd_pcm_sframes_t hdelta, delta;
	unsigned long jdelta;
293
294
	unsigned long curr_jiffies;
	struct timespec curr_tstamp;
295
	struct timespec audio_tstamp;
296
	int crossed_boundary = 0;
Linus Torvalds's avatar
Linus Torvalds committed
297

298
	old_hw_ptr = runtime->status->hw_ptr;
299
300
301
302
303
304
305

	/*
	 * group pointer, time and jiffies reads to allow for more
	 * accurate correlations/corrections.
	 * The values are stored at the end of this routine after
	 * corrections for hw_ptr position
	 */
306
	pos = substream->ops->pointer(substream);
307
	curr_jiffies = jiffies;
308
	if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
309
310
311
312
313
314
315
316
317
318
319
320
		if ((substream->ops->get_time_info) &&
			(runtime->audio_tstamp_config.type_requested != SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)) {
			substream->ops->get_time_info(substream, &curr_tstamp,
						&audio_tstamp,
						&runtime->audio_tstamp_config,
						&runtime->audio_tstamp_report);

			/* re-test in case tstamp type is not supported in hardware and was demoted to DEFAULT */
			if (runtime->audio_tstamp_report.actual_type == SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)
				snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp);
		} else
			snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp);
321
322
	}

Linus Torvalds's avatar
Linus Torvalds committed
323
324
325
326
	if (pos == SNDRV_PCM_POS_XRUN) {
		xrun(substream);
		return -EPIPE;
	}
327
	if (pos >= runtime->buffer_size) {
328
		if (printk_ratelimit()) {
329
			char name[16];
330
			snd_pcm_debug_name(substream, name, sizeof(name));
331
			pcm_err(substream->pcm,
332
				"invalid position: %s, pos = %ld, buffer size = %ld, period size = %ld\n",
333
334
				name, pos, runtime->buffer_size,
				runtime->period_size);
335
336
		}
		pos = 0;
337
	}
338
	pos -= pos % runtime->min_align;
339
	trace_hwptr(substream, pos, in_interrupt);
Takashi Iwai's avatar
Takashi Iwai committed
340
341
	hw_base = runtime->hw_ptr_base;
	new_hw_ptr = hw_base + pos;
342
343
344
	if (in_interrupt) {
		/* we know that one period was processed */
		/* delta = "expected next hw_ptr" for in_interrupt != 0 */
345
		delta = runtime->hw_ptr_interrupt + runtime->period_size;
346
		if (delta > new_hw_ptr) {
347
			/* check for double acknowledged interrupts */
348
			hdelta = curr_jiffies - runtime->hw_ptr_jiffies;
349
			if (hdelta > runtime->hw_ptr_buffer_jiffies/2 + 1) {
350
				hw_base += runtime->buffer_size;
351
				if (hw_base >= runtime->boundary) {
352
					hw_base = 0;
353
354
					crossed_boundary++;
				}
355
356
357
				new_hw_ptr = hw_base + pos;
				goto __delta;
			}
Linus Torvalds's avatar
Linus Torvalds committed
358
359
		}
	}
360
361
362
363
	/* new_hw_ptr might be lower than old_hw_ptr in case when */
	/* pointer crosses the end of the ring buffer */
	if (new_hw_ptr < old_hw_ptr) {
		hw_base += runtime->buffer_size;
364
		if (hw_base >= runtime->boundary) {
365
			hw_base = 0;
366
367
			crossed_boundary++;
		}
368
369
370
		new_hw_ptr = hw_base + pos;
	}
      __delta:
371
372
373
	delta = new_hw_ptr - old_hw_ptr;
	if (delta < 0)
		delta += runtime->boundary;
374

375
	if (runtime->no_period_wakeup) {
376
		snd_pcm_sframes_t xrun_threshold;
377
378
379
380
		/*
		 * Without regular period interrupts, we have to check
		 * the elapsed time to detect xruns.
		 */
381
		jdelta = curr_jiffies - runtime->hw_ptr_jiffies;
382
383
		if (jdelta < runtime->hw_ptr_buffer_jiffies / 2)
			goto no_delta_check;
384
		hdelta = jdelta - delta * HZ / runtime->rate;
385
386
		xrun_threshold = runtime->hw_ptr_buffer_jiffies / 2 + 1;
		while (hdelta > xrun_threshold) {
387
388
			delta += runtime->buffer_size;
			hw_base += runtime->buffer_size;
389
			if (hw_base >= runtime->boundary) {
390
				hw_base = 0;
391
392
				crossed_boundary++;
			}
393
394
395
			new_hw_ptr = hw_base + pos;
			hdelta -= runtime->hw_ptr_buffer_jiffies;
		}
396
		goto no_delta_check;
397
	}
398

399
	/* something must be really wrong */
400
	if (delta >= runtime->buffer_size + runtime->period_size) {
401
402
403
404
		hw_ptr_error(substream, in_interrupt, "Unexpected hw_ptr",
			     "(stream=%i, pos=%ld, new_hw_ptr=%ld, old_hw_ptr=%ld)\n",
			     substream->stream, (long)pos,
			     (long)new_hw_ptr, (long)old_hw_ptr);
405
406
		return 0;
	}
407
408

	/* Do jiffies check only in xrun_debug mode */
409
	if (!xrun_debug(substream, XRUN_DEBUG_JIFFIESCHECK))
410
411
		goto no_jiffies_check;

412
413
414
415
416
417
	/* Skip the jiffies check for hardwares with BATCH flag.
	 * Such hardware usually just increases the position at each IRQ,
	 * thus it can't give any strange position.
	 */
	if (runtime->hw.info & SNDRV_PCM_INFO_BATCH)
		goto no_jiffies_check;
418
	hdelta = delta;
419
420
421
	if (hdelta < runtime->delay)
		goto no_jiffies_check;
	hdelta -= runtime->delay;
422
	jdelta = curr_jiffies - runtime->hw_ptr_jiffies;
423
424
425
426
	if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) {
		delta = jdelta /
			(((runtime->period_size * HZ) / runtime->rate)
								+ HZ/100);
427
428
		/* move new_hw_ptr according jiffies not pos variable */
		new_hw_ptr = old_hw_ptr;
429
		hw_base = delta;
430
431
432
433
		/* use loop to avoid checks for delta overflows */
		/* the delta value is small or zero in most cases */
		while (delta > 0) {
			new_hw_ptr += runtime->period_size;
434
			if (new_hw_ptr >= runtime->boundary) {
435
				new_hw_ptr -= runtime->boundary;
436
437
				crossed_boundary--;
			}
438
439
440
			delta--;
		}
		/* align hw_base to buffer_size */
441
442
		hw_ptr_error(substream, in_interrupt, "hw_ptr skipping",
			     "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu/%lu, hw_ptr=%ld/%ld)\n",
443
444
			     (long)pos, (long)hdelta,
			     (long)runtime->period_size, jdelta,
445
			     ((hdelta * HZ) / runtime->rate), hw_base,
446
447
			     (unsigned long)old_hw_ptr,
			     (unsigned long)new_hw_ptr);
448
449
450
		/* reset values to proper state */
		delta = 0;
		hw_base = new_hw_ptr - (new_hw_ptr % runtime->buffer_size);
451
	}
452
 no_jiffies_check:
453
	if (delta > runtime->period_size + runtime->period_size / 2) {
454
455
456
		hw_ptr_error(substream, in_interrupt,
			     "Lost interrupts?",
			     "(stream=%i, delta=%ld, new_hw_ptr=%ld, old_hw_ptr=%ld)\n",
Takashi Iwai's avatar
Takashi Iwai committed
457
			     substream->stream, (long)delta,
458
459
			     (long)new_hw_ptr,
			     (long)old_hw_ptr);
Takashi Iwai's avatar
Takashi Iwai committed
460
	}
461

462
 no_delta_check:
463
464
	if (runtime->status->hw_ptr == new_hw_ptr) {
		update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp);
465
		return 0;
466
	}
467

Linus Torvalds's avatar
Linus Torvalds committed
468
469
470
471
	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
	    runtime->silence_size > 0)
		snd_pcm_playback_silence(substream, new_hw_ptr);

472
	if (in_interrupt) {
473
474
475
476
477
478
479
		delta = new_hw_ptr - runtime->hw_ptr_interrupt;
		if (delta < 0)
			delta += runtime->boundary;
		delta -= (snd_pcm_uframes_t)delta % runtime->period_size;
		runtime->hw_ptr_interrupt += delta;
		if (runtime->hw_ptr_interrupt >= runtime->boundary)
			runtime->hw_ptr_interrupt -= runtime->boundary;
480
	}
Takashi Iwai's avatar
Takashi Iwai committed
481
	runtime->hw_ptr_base = hw_base;
Linus Torvalds's avatar
Linus Torvalds committed
482
	runtime->status->hw_ptr = new_hw_ptr;
483
	runtime->hw_ptr_jiffies = curr_jiffies;
484
485
486
487
	if (crossed_boundary) {
		snd_BUG_ON(crossed_boundary != 1);
		runtime->hw_ptr_wrap += runtime->boundary;
	}
488

489
	update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp);
490

491
	return snd_pcm_update_state(substream, runtime);
Linus Torvalds's avatar
Linus Torvalds committed
492
493
494
}

/* CAUTION: call it with irq disabled */
495
int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
Linus Torvalds's avatar
Linus Torvalds committed
496
{
497
	return snd_pcm_update_hw_ptr0(substream, 0);
Linus Torvalds's avatar
Linus Torvalds committed
498
499
500
501
502
503
504
505
506
507
}

/**
 * snd_pcm_set_ops - set the PCM operators
 * @pcm: the pcm instance
 * @direction: stream direction, SNDRV_PCM_STREAM_XXX
 * @ops: the operator table
 *
 * Sets the given PCM operators to the pcm instance.
 */
508
509
void snd_pcm_set_ops(struct snd_pcm *pcm, int direction,
		     const struct snd_pcm_ops *ops)
Linus Torvalds's avatar
Linus Torvalds committed
510
{
511
512
	struct snd_pcm_str *stream = &pcm->streams[direction];
	struct snd_pcm_substream *substream;
Linus Torvalds's avatar
Linus Torvalds committed
513
514
515
516
517
	
	for (substream = stream->substream; substream != NULL; substream = substream->next)
		substream->ops = ops;
}

518
EXPORT_SYMBOL(snd_pcm_set_ops);
Linus Torvalds's avatar
Linus Torvalds committed
519
520
521
522
523
524
525

/**
 * snd_pcm_sync - set the PCM sync id
 * @substream: the pcm substream
 *
 * Sets the PCM sync identifier for the card.
 */
526
void snd_pcm_set_sync(struct snd_pcm_substream *substream)
Linus Torvalds's avatar
Linus Torvalds committed
527
{
528
	struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds's avatar
Linus Torvalds committed
529
530
531
532
533
534
535
	
	runtime->sync.id32[0] = substream->pcm->card->number;
	runtime->sync.id32[1] = -1;
	runtime->sync.id32[2] = -1;
	runtime->sync.id32[3] = -1;
}

536
537
EXPORT_SYMBOL(snd_pcm_set_sync);

Linus Torvalds's avatar
Linus Torvalds committed
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
/*
 *  Standard ioctl routine
 */

static inline unsigned int div32(unsigned int a, unsigned int b, 
				 unsigned int *r)
{
	if (b == 0) {
		*r = 0;
		return UINT_MAX;
	}
	*r = a % b;
	return a / b;
}

static inline unsigned int div_down(unsigned int a, unsigned int b)
{
	if (b == 0)
		return UINT_MAX;
	return a / b;
}

static inline unsigned int div_up(unsigned int a, unsigned int b)
{
	unsigned int r;
	unsigned int q;
	if (b == 0)
		return UINT_MAX;
	q = div32(a, b, &r);
	if (r)
		++q;
	return q;
}

static inline unsigned int mul(unsigned int a, unsigned int b)
{
	if (a == 0)
		return 0;
	if (div_down(UINT_MAX, a) < b)
		return UINT_MAX;
	return a * b;
}

static inline unsigned int muldiv32(unsigned int a, unsigned int b,
				    unsigned int c, unsigned int *r)
{
	u_int64_t n = (u_int64_t) a * b;
	if (c == 0) {
586
		snd_BUG_ON(!n);
Linus Torvalds's avatar
Linus Torvalds committed
587
588
589
		*r = 0;
		return UINT_MAX;
	}
590
	n = div_u64_rem(n, c, r);
Linus Torvalds's avatar
Linus Torvalds committed
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
	if (n >= UINT_MAX) {
		*r = 0;
		return UINT_MAX;
	}
	return n;
}

/**
 * snd_interval_refine - refine the interval value of configurator
 * @i: the interval value to refine
 * @v: the interval value to refer to
 *
 * Refines the interval value with the reference value.
 * The interval is changed to the range satisfying both intervals.
 * The interval status (min, max, integer, etc.) are evaluated.
 *
607
608
 * Return: Positive if the value is changed, zero if it's not changed, or a
 * negative error code.
Linus Torvalds's avatar
Linus Torvalds committed
609
 */
610
int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v)
Linus Torvalds's avatar
Linus Torvalds committed
611
612
{
	int changed = 0;
613
614
	if (snd_BUG_ON(snd_interval_empty(i)))
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
	if (i->min < v->min) {
		i->min = v->min;
		i->openmin = v->openmin;
		changed = 1;
	} else if (i->min == v->min && !i->openmin && v->openmin) {
		i->openmin = 1;
		changed = 1;
	}
	if (i->max > v->max) {
		i->max = v->max;
		i->openmax = v->openmax;
		changed = 1;
	} else if (i->max == v->max && !i->openmax && v->openmax) {
		i->openmax = 1;
		changed = 1;
	}
	if (!i->integer && v->integer) {
		i->integer = 1;
		changed = 1;
	}
	if (i->integer) {
		if (i->openmin) {
			i->min++;
			i->openmin = 0;
		}
		if (i->openmax) {
			i->max--;
			i->openmax = 0;
		}
	} else if (!i->openmin && !i->openmax && i->min == i->max)
		i->integer = 1;
	if (snd_interval_checkempty(i)) {
		snd_interval_none(i);
		return -EINVAL;
	}
	return changed;
}

653
654
EXPORT_SYMBOL(snd_interval_refine);

655
static int snd_interval_refine_first(struct snd_interval *i)
Linus Torvalds's avatar
Linus Torvalds committed
656
{
657
658
	if (snd_BUG_ON(snd_interval_empty(i)))
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
659
660
661
662
663
664
665
666
667
	if (snd_interval_single(i))
		return 0;
	i->max = i->min;
	i->openmax = i->openmin;
	if (i->openmax)
		i->max++;
	return 1;
}

668
static int snd_interval_refine_last(struct snd_interval *i)
Linus Torvalds's avatar
Linus Torvalds committed
669
{
670
671
	if (snd_BUG_ON(snd_interval_empty(i)))
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
672
673
674
675
676
677
678
679
680
	if (snd_interval_single(i))
		return 0;
	i->min = i->max;
	i->openmin = i->openmax;
	if (i->openmin)
		i->min--;
	return 1;
}

681
void snd_interval_mul(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c)
Linus Torvalds's avatar
Linus Torvalds committed
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
{
	if (a->empty || b->empty) {
		snd_interval_none(c);
		return;
	}
	c->empty = 0;
	c->min = mul(a->min, b->min);
	c->openmin = (a->openmin || b->openmin);
	c->max = mul(a->max,  b->max);
	c->openmax = (a->openmax || b->openmax);
	c->integer = (a->integer && b->integer);
}

/**
 * snd_interval_div - refine the interval value with division
Takashi Iwai's avatar
Takashi Iwai committed
697
698
699
 * @a: dividend
 * @b: divisor
 * @c: quotient
Linus Torvalds's avatar
Linus Torvalds committed
700
701
702
703
704
 *
 * c = a / b
 *
 * Returns non-zero if the value is changed, zero if not changed.
 */
705
void snd_interval_div(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c)
Linus Torvalds's avatar
Linus Torvalds committed
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
{
	unsigned int r;
	if (a->empty || b->empty) {
		snd_interval_none(c);
		return;
	}
	c->empty = 0;
	c->min = div32(a->min, b->max, &r);
	c->openmin = (r || a->openmin || b->openmax);
	if (b->min > 0) {
		c->max = div32(a->max, b->min, &r);
		if (r) {
			c->max++;
			c->openmax = 1;
		} else
			c->openmax = (a->openmax || b->openmin);
	} else {
		c->max = UINT_MAX;
		c->openmax = 0;
	}
	c->integer = 0;
}

/**
 * snd_interval_muldivk - refine the interval value
Takashi Iwai's avatar
Takashi Iwai committed
731
732
733
734
735
 * @a: dividend 1
 * @b: dividend 2
 * @k: divisor (as integer)
 * @c: result
  *
Linus Torvalds's avatar
Linus Torvalds committed
736
737
738
739
 * c = a * b / k
 *
 * Returns non-zero if the value is changed, zero if not changed.
 */
740
741
void snd_interval_muldivk(const struct snd_interval *a, const struct snd_interval *b,
		      unsigned int k, struct snd_interval *c)
Linus Torvalds's avatar
Linus Torvalds committed
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
{
	unsigned int r;
	if (a->empty || b->empty) {
		snd_interval_none(c);
		return;
	}
	c->empty = 0;
	c->min = muldiv32(a->min, b->min, k, &r);
	c->openmin = (r || a->openmin || b->openmin);
	c->max = muldiv32(a->max, b->max, k, &r);
	if (r) {
		c->max++;
		c->openmax = 1;
	} else
		c->openmax = (a->openmax || b->openmax);
	c->integer = 0;
}

/**
 * snd_interval_mulkdiv - refine the interval value
Takashi Iwai's avatar
Takashi Iwai committed
762
763
764
765
 * @a: dividend 1
 * @k: dividend 2 (as integer)
 * @b: divisor
 * @c: result
Linus Torvalds's avatar
Linus Torvalds committed
766
767
768
769
770
 *
 * c = a * k / b
 *
 * Returns non-zero if the value is changed, zero if not changed.
 */
771
772
void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k,
		      const struct snd_interval *b, struct snd_interval *c)
Linus Torvalds's avatar
Linus Torvalds committed
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
{
	unsigned int r;
	if (a->empty || b->empty) {
		snd_interval_none(c);
		return;
	}
	c->empty = 0;
	c->min = muldiv32(a->min, k, b->max, &r);
	c->openmin = (r || a->openmin || b->openmax);
	if (b->min > 0) {
		c->max = muldiv32(a->max, k, b->min, &r);
		if (r) {
			c->max++;
			c->openmax = 1;
		} else
			c->openmax = (a->openmax || b->openmin);
	} else {
		c->max = UINT_MAX;
		c->openmax = 0;
	}
	c->integer = 0;
}

/* ---- */


/**
 * snd_interval_ratnum - refine the interval value
Takashi Iwai's avatar
Takashi Iwai committed
801
802
803
804
805
 * @i: interval to refine
 * @rats_count: number of ratnum_t 
 * @rats: ratnum_t array
 * @nump: pointer to store the resultant numerator
 * @denp: pointer to store the resultant denominator
Linus Torvalds's avatar
Linus Torvalds committed
806
 *
807
808
 * Return: Positive if the value is changed, zero if it's not changed, or a
 * negative error code.
Linus Torvalds's avatar
Linus Torvalds committed
809
 */
810
int snd_interval_ratnum(struct snd_interval *i,
811
			unsigned int rats_count, const struct snd_ratnum *rats,
812
			unsigned int *nump, unsigned int *denp)
Linus Torvalds's avatar
Linus Torvalds committed
813
{
814
815
	unsigned int best_num, best_den;
	int best_diff;
Linus Torvalds's avatar
Linus Torvalds committed
816
	unsigned int k;
817
	struct snd_interval t;
Linus Torvalds's avatar
Linus Torvalds committed
818
	int err;
819
820
	unsigned int result_num, result_den;
	int result_diff;
Linus Torvalds's avatar
Linus Torvalds committed
821
822
823
824
825
826
827
828
829

	best_num = best_den = best_diff = 0;
	for (k = 0; k < rats_count; ++k) {
		unsigned int num = rats[k].num;
		unsigned int den;
		unsigned int q = i->min;
		int diff;
		if (q == 0)
			q = 1;
830
		den = div_up(num, q);
Linus Torvalds's avatar
Linus Torvalds committed
831
832
833
834
835
836
837
838
839
840
841
		if (den < rats[k].den_min)
			continue;
		if (den > rats[k].den_max)
			den = rats[k].den_max;
		else {
			unsigned int r;
			r = (den - rats[k].den_min) % rats[k].den_step;
			if (r != 0)
				den -= r;
		}
		diff = num - q * den;
842
843
		if (diff < 0)
			diff = -diff;
Linus Torvalds's avatar
Linus Torvalds committed
844
845
846
847
848
849
850
851
852
853
854
855
856
857
		if (best_num == 0 ||
		    diff * best_den < best_diff * den) {
			best_diff = diff;
			best_den = den;
			best_num = num;
		}
	}
	if (best_den == 0) {
		i->empty = 1;
		return -EINVAL;
	}
	t.min = div_down(best_num, best_den);
	t.openmin = !!(best_num % best_den);
	
858
859
860
	result_num = best_num;
	result_diff = best_diff;
	result_den = best_den;
Linus Torvalds's avatar
Linus Torvalds committed
861
862
863
864
865
866
867
868
869
870
	best_num = best_den = best_diff = 0;
	for (k = 0; k < rats_count; ++k) {
		unsigned int num = rats[k].num;
		unsigned int den;
		unsigned int q = i->max;
		int diff;
		if (q == 0) {
			i->empty = 1;
			return -EINVAL;
		}
871
		den = div_down(num, q);
Linus Torvalds's avatar
Linus Torvalds committed
872
873
874
875
876
877
878
879
880
881
882
		if (den > rats[k].den_max)
			continue;
		if (den < rats[k].den_min)
			den = rats[k].den_min;
		else {
			unsigned int r;
			r = (den - rats[k].den_min) % rats[k].den_step;
			if (r != 0)
				den += rats[k].den_step - r;
		}
		diff = q * den - num;
883
884
		if (diff < 0)
			diff = -diff;
Linus Torvalds's avatar
Linus Torvalds committed
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
		if (best_num == 0 ||
		    diff * best_den < best_diff * den) {
			best_diff = diff;
			best_den = den;
			best_num = num;
		}
	}
	if (best_den == 0) {
		i->empty = 1;
		return -EINVAL;
	}
	t.max = div_up(best_num, best_den);
	t.openmax = !!(best_num % best_den);
	t.integer = 0;
	err = snd_interval_refine(i, &t);
	if (err < 0)
		return err;

	if (snd_interval_single(i)) {
904
905
906
907
		if (best_diff * result_den < result_diff * best_den) {
			result_num = best_num;
			result_den = best_den;
		}
Linus Torvalds's avatar
Linus Torvalds committed
908
		if (nump)
909
			*nump = result_num;
Linus Torvalds's avatar
Linus Torvalds committed
910
		if (denp)
911
			*denp = result_den;
Linus Torvalds's avatar
Linus Torvalds committed
912
913
914
915
	}
	return err;
}

916
917
EXPORT_SYMBOL(snd_interval_ratnum);

Linus Torvalds's avatar
Linus Torvalds committed
918
919
/**
 * snd_interval_ratden - refine the interval value
Takashi Iwai's avatar
Takashi Iwai committed
920
 * @i: interval to refine
921
922
 * @rats_count: number of struct ratden
 * @rats: struct ratden array
Takashi Iwai's avatar
Takashi Iwai committed
923
924
 * @nump: pointer to store the resultant numerator
 * @denp: pointer to store the resultant denominator
Linus Torvalds's avatar
Linus Torvalds committed
925
 *
926
927
 * Return: Positive if the value is changed, zero if it's not changed, or a
 * negative error code.
Linus Torvalds's avatar
Linus Torvalds committed
928
 */
929
static int snd_interval_ratden(struct snd_interval *i,
930
931
			       unsigned int rats_count,
			       const struct snd_ratden *rats,
Linus Torvalds's avatar
Linus Torvalds committed
932
933
934
935
			       unsigned int *nump, unsigned int *denp)
{
	unsigned int best_num, best_diff, best_den;
	unsigned int k;
936
	struct snd_interval t;
Linus Torvalds's avatar
Linus Torvalds committed
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
	int err;

	best_num = best_den = best_diff = 0;
	for (k = 0; k < rats_count; ++k) {
		unsigned int num;
		unsigned int den = rats[k].den;
		unsigned int q = i->min;
		int diff;
		num = mul(q, den);
		if (num > rats[k].num_max)
			continue;
		if (num < rats[k].num_min)
			num = rats[k].num_max;
		else {
			unsigned int r;
			r = (num - rats[k].num_min) % rats[k].num_step;
			if (r != 0)
				num += rats[k].num_step - r;
		}
		diff = num - q * den;
		if (best_num == 0 ||
		    diff * best_den < best_diff * den) {
			best_diff = diff;
			best_den = den;
			best_num = num;
		}
	}
	if (best_den == 0) {
		i->empty = 1;
		return -EINVAL;
	}
	t.min = div_down(best_num, best_den);
	t.openmin = !!(best_num % best_den);
	
	best_num = best_den = best_diff = 0;
	for (k = 0; k < rats_count; ++k) {
		unsigned int num;
		unsigned int den = rats[k].den;
		unsigned int q = i->max;
		int diff;
		num = mul(q, den);
		if (num < rats[k].num_min)
			continue;
		if (num > rats[k].num_max)
			num = rats[k].num_max;
		else {
			unsigned int r;
			r = (num - rats[k].num_min) % rats[k].num_step;
			if (r != 0)
				num -= r;
		}
		diff = q * den - num;
		if (best_num == 0 ||
		    diff * best_den < best_diff * den) {
			best_diff = diff;
			best_den = den;
			best_num = num;
		}
	}
	if (best_den == 0) {
		i->empty = 1;
		return -EINVAL;
	}
	t.max = div_up(best_num, best_den);