pcm-indirect.h 5.2 KB

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