1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Helper functions for indirect PCM data transfer to a simple FIFO in
4 * hardware (small, no possibility to read "hardware io position",
5 * updating position done by interrupt, ...)
7 * Copyright (c) by 2007 Joachim Foerster <JOFT@gmx.de>
9 * Based on "pcm-indirect.h" (alsa-driver-1.0.13) by
11 * Copyright (c) by Takashi Iwai <tiwai@suse.de>
12 * Jaroslav Kysela <perex@suse.cz>
16 #include <sound/core.h>
17 /* struct snd_pcm_substream, struct snd_pcm_runtime, snd_pcm_uframes_t
18 * snd_pcm_period_elapsed() */
19 #include <sound/pcm.h>
21 #include "pcm-indirect2.h"
23 #ifdef SND_PCM_INDIRECT2_STAT
25 #include <linux/jiffies.h>
27 void snd_pcm_indirect2_stat(struct snd_pcm_substream *substream,
28 struct snd_pcm_indirect2 *rec)
30 struct snd_pcm_runtime *runtime = substream->runtime;
34 int seconds = (rec->lastbytetime - rec->firstbytetime) / HZ;
36 snd_printk(KERN_DEBUG "STAT: mul_elapsed: %u, mul_elapsed_real: %d, "
38 rec->mul_elapsed, rec->mul_elapsed_real, rec->irq_occured);
39 snd_printk(KERN_DEBUG "STAT: min_multiple: %d (irqs/period)\n",
41 snd_printk(KERN_DEBUG "STAT: firstbytetime: %lu, lastbytetime: %lu, "
42 "firstzerotime: %lu\n",
43 rec->firstbytetime, rec->lastbytetime, rec->firstzerotime);
44 snd_printk(KERN_DEBUG "STAT: bytes2hw: %u Bytes => (by runtime->rate) "
46 rec->bytes2hw, rec->bytes2hw / 2 / 2 / runtime->rate);
47 snd_printk(KERN_DEBUG "STAT: (by measurement) length: %d => "
48 "rate: %d Bytes/s = %d Frames/s|Hz\n",
49 seconds, rec->bytes2hw / seconds,
50 rec->bytes2hw / 2 / 2 / seconds);
52 "STAT: zeros2hw: %u = %d ms ~ %d * %d zero copies\n",
53 rec->zeros2hw, ((rec->zeros2hw / 2 / 2) * 1000) /
55 rec->zeros2hw / (rec->hw_buffer_size / 2),
56 (rec->hw_buffer_size / 2));
57 snd_printk(KERN_DEBUG "STAT: pointer_calls: %u, lastdifftime: %u\n",
58 rec->pointer_calls, rec->lastdifftime);
59 snd_printk(KERN_DEBUG "STAT: sw_io: %d, sw_data: %d\n", rec->sw_io,
61 snd_printk(KERN_DEBUG "STAT: byte_sizes[]:\n");
63 for (j = 0; j < 8; j++) {
64 for (i = j * 8; i < (j + 1) * 8; i++)
65 if (rec->byte_sizes[i] != 0) {
66 snd_printk(KERN_DEBUG "%u: %u",
67 i, rec->byte_sizes[i]);
70 if (((k % 8) == 0) && (k != 0)) {
71 snd_printk(KERN_DEBUG "\n");
75 snd_printk(KERN_DEBUG "\n");
76 snd_printk(KERN_DEBUG "STAT: zero_sizes[]:\n");
77 for (j = 0; j < 8; j++) {
79 for (i = j * 8; i < (j + 1) * 8; i++)
80 if (rec->zero_sizes[i] != 0)
81 snd_printk(KERN_DEBUG "%u: %u",
82 i, rec->zero_sizes[i]);
86 snd_printk(KERN_DEBUG "\n");
88 snd_printk(KERN_DEBUG "\n");
89 snd_printk(KERN_DEBUG "STAT: min_adds[]:\n");
90 for (j = 0; j < 8; j++) {
91 if (rec->min_adds[j] != 0)
92 snd_printk(KERN_DEBUG "%u: %u", j, rec->min_adds[j]);
94 snd_printk(KERN_DEBUG "\n");
95 snd_printk(KERN_DEBUG "STAT: mul_adds[]:\n");
96 for (j = 0; j < 8; j++) {
97 if (rec->mul_adds[j] != 0)
98 snd_printk(KERN_DEBUG "%u: %u", j, rec->mul_adds[j]);
100 snd_printk(KERN_DEBUG "\n");
101 snd_printk(KERN_DEBUG
102 "STAT: zero_times_saved: %d, zero_times_notsaved: %d\n",
103 rec->zero_times_saved, rec->zero_times_notsaved);
104 /* snd_printk(KERN_DEBUG "STAT: zero_times[]\n");
106 for (j = 0; j < 3750; j++) {
107 if (rec->zero_times[j] != 0) {
108 snd_printk(KERN_DEBUG "%u: %u", j, rec->zero_times[j]);
111 if (((i % 8) == 0) && (i != 0))
112 snd_printk(KERN_DEBUG "\n");
114 snd_printk(KERN_DEBUG "\n"); */
120 * _internal_ helper function for playback/capture transfer function
123 snd_pcm_indirect2_increase_min_periods(struct snd_pcm_substream *substream,
124 struct snd_pcm_indirect2 *rec,
125 int isplay, int iscopy,
128 if (rec->min_periods >= 0) {
131 if (rec->sw_io >= rec->sw_buffer_size)
132 rec->sw_io -= rec->sw_buffer_size;
134 /* If application does not write data in multiples of
135 * a period, move sw_data to the next correctly aligned
136 * position, so that sw_io can converge to it (in the
139 if (!rec->check_alignment) {
141 snd_pcm_lib_period_bytes(substream)) {
142 unsigned bytes2hw_aligned =
145 snd_pcm_lib_period_bytes
147 snd_pcm_lib_period_bytes
152 #ifdef SND_PCM_INDIRECT2_STAT
153 snd_printk(KERN_DEBUG
154 "STAT: @re-align: aligned "
155 "bytes2hw to next period "
160 snd_printk(KERN_DEBUG
161 "STAT: @re-align: sw_data "
166 rec->check_alignment = 1;
168 /* We are at the end and are copying zeros into the
170 * Now, we have to make sure that sw_io is increased
171 * until the position of sw_data: Filling the fifo with
172 * the first zeros means, the last bytes were played.
174 if (rec->sw_io != rec->sw_data) {
176 if (rec->sw_data > rec->sw_io)
177 diff = rec->sw_data - rec->sw_io;
179 diff = (rec->sw_buffer_size -
183 rec->sw_io = rec->sw_data;
186 if (rec->sw_io >= rec->sw_buffer_size)
192 rec->min_period_count += bytes;
193 if (rec->min_period_count >= (rec->hw_buffer_size / 2)) {
194 rec->min_periods += (rec->min_period_count /
195 (rec->hw_buffer_size / 2));
196 #ifdef SND_PCM_INDIRECT2_STAT
197 if ((rec->min_period_count /
198 (rec->hw_buffer_size / 2)) > 7)
199 snd_printk(KERN_DEBUG
200 "STAT: more than 7 (%d) min_adds "
201 "at once - too big to save!\n",
202 (rec->min_period_count /
203 (rec->hw_buffer_size / 2)));
205 rec->min_adds[(rec->min_period_count /
206 (rec->hw_buffer_size / 2))]++;
208 rec->min_period_count = (rec->min_period_count %
209 (rec->hw_buffer_size / 2));
211 } else if (isplay && iscopy)
212 rec->min_periods = 0;
216 * helper function for playback/capture pointer callback
219 snd_pcm_indirect2_pointer(struct snd_pcm_substream *substream,
220 struct snd_pcm_indirect2 *rec)
222 #ifdef SND_PCM_INDIRECT2_STAT
223 rec->pointer_calls++;
225 return bytes_to_frames(substream->runtime, rec->sw_io);
229 * _internal_ helper function for playback interrupt callback
232 snd_pcm_indirect2_playback_transfer(struct snd_pcm_substream *substream,
233 struct snd_pcm_indirect2 *rec,
234 snd_pcm_indirect2_copy_t copy,
235 snd_pcm_indirect2_zero_t zero)
237 struct snd_pcm_runtime *runtime = substream->runtime;
238 snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
240 /* runtime->control->appl_ptr: position where ALSA will write next time
241 * rec->appl_ptr: position where ALSA was last time
242 * diff: obviously ALSA wrote that much bytes into the intermediate
243 * buffer since we checked last time
245 snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
248 #ifdef SND_PCM_INDIRECT2_STAT
249 rec->lastdifftime = jiffies;
251 if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
252 diff += runtime->boundary;
253 /* number of bytes "added" by ALSA increases the number of
254 * bytes which are ready to "be transferred to HW"/"played"
255 * Then, set rec->appl_ptr to not count bytes twice next time.
257 rec->sw_ready += (int)frames_to_bytes(runtime, diff);
258 rec->appl_ptr = appl_ptr;
260 if (rec->hw_ready && (rec->sw_ready <= 0)) {
263 #ifdef SND_PCM_INDIRECT2_STAT
264 if (rec->firstzerotime == 0) {
265 rec->firstzerotime = jiffies;
266 snd_printk(KERN_DEBUG
267 "STAT: @firstzerotime: mul_elapsed: %d, "
268 "min_period_count: %d\n",
269 rec->mul_elapsed, rec->min_period_count);
270 snd_printk(KERN_DEBUG
271 "STAT: @firstzerotime: sw_io: %d, "
272 "sw_data: %d, appl_ptr: %u\n",
273 rec->sw_io, rec->sw_data,
274 (unsigned int)appl_ptr);
276 if ((jiffies - rec->firstzerotime) < 3750) {
277 rec->zero_times[(jiffies - rec->firstzerotime)]++;
278 rec->zero_times_saved++;
280 rec->zero_times_notsaved++;
282 bytes = zero(substream, rec);
284 #ifdef SND_PCM_INDIRECT2_STAT
285 rec->zeros2hw += bytes;
287 rec->zero_sizes[bytes]++;
289 snd_printk(KERN_DEBUG
290 "STAT: %d zero Bytes copied to hardware at "
291 "once - too big to save!\n",
294 snd_pcm_indirect2_increase_min_periods(substream, rec, 1, 0,
298 while (rec->hw_ready && (rec->sw_ready > 0)) {
299 /* sw_to_end: max. number of bytes that can be read/take from
300 * the current position (sw_data) in _one_ step
302 unsigned int sw_to_end = rec->sw_buffer_size - rec->sw_data;
304 /* bytes: number of bytes we have available (for reading) */
305 unsigned int bytes = rec->sw_ready;
307 if (sw_to_end < bytes)
312 #ifdef SND_PCM_INDIRECT2_STAT
313 if (rec->firstbytetime == 0)
314 rec->firstbytetime = jiffies;
315 rec->lastbytetime = jiffies;
317 /* copy bytes from intermediate buffer position sw_data to the
318 * HW and return number of bytes actually written
319 * Furthermore, set hw_ready to 0, if the fifo isn't empty
320 * now => more could be transferred to fifo
322 bytes = copy(substream, rec, bytes);
323 rec->bytes2hw += bytes;
325 #ifdef SND_PCM_INDIRECT2_STAT
327 rec->byte_sizes[bytes]++;
329 snd_printk(KERN_DEBUG
330 "STAT: %d Bytes copied to hardware at once "
331 "- too big to save!\n",
334 /* increase sw_data by the number of actually written bytes
335 * (= number of taken bytes from intermediate buffer)
337 rec->sw_data += bytes;
338 if (rec->sw_data == rec->sw_buffer_size)
340 /* now sw_data is the position where ALSA is going to write
341 * in the intermediate buffer next time = position we are going
342 * to read from next time
345 snd_pcm_indirect2_increase_min_periods(substream, rec, 1, 1,
348 /* we read bytes from intermediate buffer, so we need to say
349 * that the number of bytes ready for transfer are decreased
352 rec->sw_ready -= bytes;
358 * helper function for playback interrupt routine
361 snd_pcm_indirect2_playback_interrupt(struct snd_pcm_substream *substream,
362 struct snd_pcm_indirect2 *rec,
363 snd_pcm_indirect2_copy_t copy,
364 snd_pcm_indirect2_zero_t zero)
366 #ifdef SND_PCM_INDIRECT2_STAT
369 /* hardware played some bytes, so there is room again (in fifo) */
372 /* don't call ack() now, instead call transfer() function directly
373 * (normally called by ack() )
375 snd_pcm_indirect2_playback_transfer(substream, rec, copy, zero);
377 if (rec->min_periods >= rec->min_multiple) {
378 #ifdef SND_PCM_INDIRECT2_STAT
379 if ((rec->min_periods / rec->min_multiple) > 7)
380 snd_printk(KERN_DEBUG
381 "STAT: more than 7 (%d) mul_adds - too big "
383 (rec->min_periods / rec->min_multiple));
385 rec->mul_adds[(rec->min_periods /
386 rec->min_multiple)]++;
387 rec->mul_elapsed_real += (rec->min_periods /
391 rec->min_periods = (rec->min_periods % rec->min_multiple);
392 snd_pcm_period_elapsed(substream);
397 * _internal_ helper function for capture interrupt callback
400 snd_pcm_indirect2_capture_transfer(struct snd_pcm_substream *substream,
401 struct snd_pcm_indirect2 *rec,
402 snd_pcm_indirect2_copy_t copy,
403 snd_pcm_indirect2_zero_t null)
405 struct snd_pcm_runtime *runtime = substream->runtime;
406 snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
407 snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
410 #ifdef SND_PCM_INDIRECT2_STAT
411 rec->lastdifftime = jiffies;
413 if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
414 diff += runtime->boundary;
415 rec->sw_ready -= frames_to_bytes(runtime, diff);
416 rec->appl_ptr = appl_ptr;
418 /* if hardware has something, but the intermediate buffer is full
419 * => skip contents of buffer
421 if (rec->hw_ready && (rec->sw_ready >= (int)rec->sw_buffer_size)) {
424 #ifdef SND_PCM_INDIRECT2_STAT
425 if (rec->firstzerotime == 0) {
426 rec->firstzerotime = jiffies;
427 snd_printk(KERN_DEBUG "STAT: (capture) "
428 "@firstzerotime: mul_elapsed: %d, "
429 "min_period_count: %d\n",
430 rec->mul_elapsed, rec->min_period_count);
431 snd_printk(KERN_DEBUG "STAT: (capture) "
432 "@firstzerotime: sw_io: %d, sw_data: %d, "
434 rec->sw_io, rec->sw_data,
435 (unsigned int)appl_ptr);
437 if ((jiffies - rec->firstzerotime) < 3750) {
438 rec->zero_times[(jiffies - rec->firstzerotime)]++;
439 rec->zero_times_saved++;
441 rec->zero_times_notsaved++;
443 bytes = null(substream, rec);
445 #ifdef SND_PCM_INDIRECT2_STAT
446 rec->zeros2hw += bytes;
448 rec->zero_sizes[bytes]++;
450 snd_printk(KERN_DEBUG
451 "STAT: (capture) %d zero Bytes copied to "
452 "hardware at once - too big to save!\n",
455 snd_pcm_indirect2_increase_min_periods(substream, rec, 0, 0,
457 /* report an overrun */
458 rec->sw_io = SNDRV_PCM_POS_XRUN;
461 while (rec->hw_ready && (rec->sw_ready < (int)rec->sw_buffer_size)) {
462 /* sw_to_end: max. number of bytes that we can write to the
463 * intermediate buffer (until it's end)
465 size_t sw_to_end = rec->sw_buffer_size - rec->sw_data;
467 /* bytes: max. number of bytes, which may be copied to the
468 * intermediate buffer without overflow (in _one_ step)
470 size_t bytes = rec->sw_buffer_size - rec->sw_ready;
472 /* limit number of bytes (for transfer) by available room in
473 * the intermediate buffer
475 if (sw_to_end < bytes)
480 #ifdef SND_PCM_INDIRECT2_STAT
481 if (rec->firstbytetime == 0)
482 rec->firstbytetime = jiffies;
483 rec->lastbytetime = jiffies;
485 /* copy bytes from the intermediate buffer (position sw_data)
486 * to the HW at most and return number of bytes actually copied
488 * Furthermore, set hw_ready to 0, if the fifo is empty now.
490 bytes = copy(substream, rec, bytes);
491 rec->bytes2hw += bytes;
493 #ifdef SND_PCM_INDIRECT2_STAT
495 rec->byte_sizes[bytes]++;
497 snd_printk(KERN_DEBUG
498 "STAT: (capture) %d Bytes copied to "
499 "hardware at once - too big to save!\n",
502 /* increase sw_data by the number of actually copied bytes from
505 rec->sw_data += bytes;
506 if (rec->sw_data == rec->sw_buffer_size)
509 snd_pcm_indirect2_increase_min_periods(substream, rec, 0, 1,
512 /* number of bytes in the intermediate buffer, which haven't
513 * been fetched by ALSA yet.
515 rec->sw_ready += bytes;
521 * helper function for capture interrupt routine
524 snd_pcm_indirect2_capture_interrupt(struct snd_pcm_substream *substream,
525 struct snd_pcm_indirect2 *rec,
526 snd_pcm_indirect2_copy_t copy,
527 snd_pcm_indirect2_zero_t null)
529 #ifdef SND_PCM_INDIRECT2_STAT
532 /* hardware recorded some bytes, so there is something to read from the
537 /* don't call ack() now, instead call transfer() function directly
538 * (normally called by ack() )
540 snd_pcm_indirect2_capture_transfer(substream, rec, copy, null);
542 if (rec->min_periods >= rec->min_multiple) {
544 #ifdef SND_PCM_INDIRECT2_STAT
545 if ((rec->min_periods / rec->min_multiple) > 7)
546 snd_printk(KERN_DEBUG
547 "STAT: more than 7 (%d) mul_adds - "
548 "too big to save!\n",
549 (rec->min_periods / rec->min_multiple));
551 rec->mul_adds[(rec->min_periods /
552 rec->min_multiple)]++;
553 rec->mul_elapsed_real += (rec->min_periods /
557 rec->min_periods = (rec->min_periods % rec->min_multiple);
558 snd_pcm_period_elapsed(substream);