^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) /* SPDX-License-Identifier: GPL-2.0-or-later */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Helper functions for indirect PCM data transfer
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (c) by Takashi Iwai <tiwai@suse.de>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * Jaroslav Kysela <perex@perex.cz>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #ifndef __SOUND_PCM_INDIRECT_H
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #define __SOUND_PCM_INDIRECT_H
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <sound/pcm.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) struct snd_pcm_indirect {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) unsigned int hw_buffer_size; /* Byte size of hardware buffer */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) unsigned int hw_queue_size; /* Max queue size of hw buffer (0 = buffer size) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) unsigned int hw_data; /* Offset to next dst (or src) in hw ring buffer */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) unsigned int hw_io; /* Ring buffer hw pointer */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) int hw_ready; /* Bytes ready for play (or captured) in hw ring buffer */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) unsigned int sw_buffer_size; /* Byte size of software buffer */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) unsigned int sw_data; /* Offset to next dst (or src) in sw ring buffer */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) unsigned int sw_io; /* Current software pointer in bytes */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) int sw_ready; /* Bytes ready to be transferred to/from hw */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) snd_pcm_uframes_t appl_ptr; /* Last seen appl_ptr */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) typedef void (*snd_pcm_indirect_copy_t)(struct snd_pcm_substream *substream,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) struct snd_pcm_indirect *rec, size_t bytes);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) * helper function for playback ack callback
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) static inline int
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) snd_pcm_indirect_playback_transfer(struct snd_pcm_substream *substream,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) struct snd_pcm_indirect *rec,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) snd_pcm_indirect_copy_t copy)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) struct snd_pcm_runtime *runtime = substream->runtime;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) int qsize;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) if (diff) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) diff += runtime->boundary;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) if (diff < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) rec->sw_ready += (int)frames_to_bytes(runtime, diff);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) rec->appl_ptr = appl_ptr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) qsize = rec->hw_queue_size ? rec->hw_queue_size : rec->hw_buffer_size;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) while (rec->hw_ready < qsize && rec->sw_ready > 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) unsigned int hw_to_end = rec->hw_buffer_size - rec->hw_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) unsigned int sw_to_end = rec->sw_buffer_size - rec->sw_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) unsigned int bytes = qsize - rec->hw_ready;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) if (rec->sw_ready < (int)bytes)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) bytes = rec->sw_ready;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) if (hw_to_end < bytes)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) bytes = hw_to_end;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) if (sw_to_end < bytes)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) bytes = sw_to_end;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) if (! bytes)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) copy(substream, rec, bytes);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) rec->hw_data += bytes;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) if (rec->hw_data == rec->hw_buffer_size)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) rec->hw_data = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) rec->sw_data += bytes;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) if (rec->sw_data == rec->sw_buffer_size)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) rec->sw_data = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) rec->hw_ready += bytes;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) rec->sw_ready -= bytes;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) * helper function for playback pointer callback
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) * ptr = current byte pointer
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) static inline snd_pcm_uframes_t
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) snd_pcm_indirect_playback_pointer(struct snd_pcm_substream *substream,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) struct snd_pcm_indirect *rec, unsigned int ptr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) int bytes = ptr - rec->hw_io;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) if (bytes < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) bytes += rec->hw_buffer_size;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) rec->hw_io = ptr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) rec->hw_ready -= bytes;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) rec->sw_io += bytes;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) if (rec->sw_io >= rec->sw_buffer_size)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) rec->sw_io -= rec->sw_buffer_size;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) if (substream->ops->ack)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) substream->ops->ack(substream);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) return bytes_to_frames(substream->runtime, rec->sw_io);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) * helper function for capture ack callback
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) static inline int
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) snd_pcm_indirect_capture_transfer(struct snd_pcm_substream *substream,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) struct snd_pcm_indirect *rec,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) snd_pcm_indirect_copy_t copy)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) struct snd_pcm_runtime *runtime = substream->runtime;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) if (diff) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) diff += runtime->boundary;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) if (diff < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) rec->sw_ready -= frames_to_bytes(runtime, diff);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) rec->appl_ptr = appl_ptr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) while (rec->hw_ready > 0 &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) rec->sw_ready < (int)rec->sw_buffer_size) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) size_t hw_to_end = rec->hw_buffer_size - rec->hw_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) size_t sw_to_end = rec->sw_buffer_size - rec->sw_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) size_t bytes = rec->sw_buffer_size - rec->sw_ready;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) if (rec->hw_ready < (int)bytes)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) bytes = rec->hw_ready;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) if (hw_to_end < bytes)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) bytes = hw_to_end;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) if (sw_to_end < bytes)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) bytes = sw_to_end;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) if (! bytes)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) copy(substream, rec, bytes);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) rec->hw_data += bytes;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) if ((int)rec->hw_data == rec->hw_buffer_size)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) rec->hw_data = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) rec->sw_data += bytes;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) if (rec->sw_data == rec->sw_buffer_size)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) rec->sw_data = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) rec->hw_ready -= bytes;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) rec->sw_ready += bytes;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) * helper function for capture pointer callback,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) * ptr = current byte pointer
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) static inline snd_pcm_uframes_t
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) snd_pcm_indirect_capture_pointer(struct snd_pcm_substream *substream,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) struct snd_pcm_indirect *rec, unsigned int ptr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) int qsize;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) int bytes = ptr - rec->hw_io;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) if (bytes < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) bytes += rec->hw_buffer_size;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) rec->hw_io = ptr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) rec->hw_ready += bytes;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) qsize = rec->hw_queue_size ? rec->hw_queue_size : rec->hw_buffer_size;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) if (rec->hw_ready > qsize)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) return SNDRV_PCM_POS_XRUN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) rec->sw_io += bytes;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) if (rec->sw_io >= rec->sw_buffer_size)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) rec->sw_io -= rec->sw_buffer_size;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) if (substream->ops->ack)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) substream->ops->ack(substream);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) return bytes_to_frames(substream->runtime, rec->sw_io);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) #endif /* __SOUND_PCM_INDIRECT_H */