Back to home page

OSCL-LXR

 
 

    


0001 /* SPDX-License-Identifier: GPL-2.0-or-later */
0002 /*
0003  * Helper functions for indirect PCM data transfer
0004  *
0005  *  Copyright (c) by Takashi Iwai <tiwai@suse.de>
0006  *                   Jaroslav Kysela <perex@perex.cz>
0007  */
0008 
0009 #ifndef __SOUND_PCM_INDIRECT_H
0010 #define __SOUND_PCM_INDIRECT_H
0011 
0012 #include <sound/pcm.h>
0013 
0014 struct snd_pcm_indirect {
0015     unsigned int hw_buffer_size;    /* Byte size of hardware buffer */
0016     unsigned int hw_queue_size; /* Max queue size of hw buffer (0 = buffer size) */
0017     unsigned int hw_data;   /* Offset to next dst (or src) in hw ring buffer */
0018     unsigned int hw_io; /* Ring buffer hw pointer */
0019     int hw_ready;       /* Bytes ready for play (or captured) in hw ring buffer */
0020     unsigned int sw_buffer_size;    /* Byte size of software buffer */
0021     unsigned int sw_data;   /* Offset to next dst (or src) in sw ring buffer */
0022     unsigned int sw_io; /* Current software pointer in bytes */
0023     int sw_ready;       /* Bytes ready to be transferred to/from hw */
0024     snd_pcm_uframes_t appl_ptr; /* Last seen appl_ptr */
0025 };
0026 
0027 typedef void (*snd_pcm_indirect_copy_t)(struct snd_pcm_substream *substream,
0028                     struct snd_pcm_indirect *rec, size_t bytes);
0029 
0030 /*
0031  * helper function for playback ack callback
0032  */
0033 static inline int
0034 snd_pcm_indirect_playback_transfer(struct snd_pcm_substream *substream,
0035                    struct snd_pcm_indirect *rec,
0036                    snd_pcm_indirect_copy_t copy)
0037 {
0038     struct snd_pcm_runtime *runtime = substream->runtime;
0039     snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
0040     snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
0041     int qsize;
0042 
0043     if (diff) {
0044         if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
0045             diff += runtime->boundary;
0046         if (diff < 0)
0047             return -EINVAL;
0048         rec->sw_ready += (int)frames_to_bytes(runtime, diff);
0049         rec->appl_ptr = appl_ptr;
0050     }
0051     qsize = rec->hw_queue_size ? rec->hw_queue_size : rec->hw_buffer_size;
0052     while (rec->hw_ready < qsize && rec->sw_ready > 0) {
0053         unsigned int hw_to_end = rec->hw_buffer_size - rec->hw_data;
0054         unsigned int sw_to_end = rec->sw_buffer_size - rec->sw_data;
0055         unsigned int bytes = qsize - rec->hw_ready;
0056         if (rec->sw_ready < (int)bytes)
0057             bytes = rec->sw_ready;
0058         if (hw_to_end < bytes)
0059             bytes = hw_to_end;
0060         if (sw_to_end < bytes)
0061             bytes = sw_to_end;
0062         if (! bytes)
0063             break;
0064         copy(substream, rec, bytes);
0065         rec->hw_data += bytes;
0066         if (rec->hw_data == rec->hw_buffer_size)
0067             rec->hw_data = 0;
0068         rec->sw_data += bytes;
0069         if (rec->sw_data == rec->sw_buffer_size)
0070             rec->sw_data = 0;
0071         rec->hw_ready += bytes;
0072         rec->sw_ready -= bytes;
0073     }
0074     return 0;
0075 }
0076 
0077 /*
0078  * helper function for playback pointer callback
0079  * ptr = current byte pointer
0080  */
0081 static inline snd_pcm_uframes_t
0082 snd_pcm_indirect_playback_pointer(struct snd_pcm_substream *substream,
0083                   struct snd_pcm_indirect *rec, unsigned int ptr)
0084 {
0085     int bytes = ptr - rec->hw_io;
0086     if (bytes < 0)
0087         bytes += rec->hw_buffer_size;
0088     rec->hw_io = ptr;
0089     rec->hw_ready -= bytes;
0090     rec->sw_io += bytes;
0091     if (rec->sw_io >= rec->sw_buffer_size)
0092         rec->sw_io -= rec->sw_buffer_size;
0093     if (substream->ops->ack)
0094         substream->ops->ack(substream);
0095     return bytes_to_frames(substream->runtime, rec->sw_io);
0096 }
0097 
0098 
0099 /*
0100  * helper function for capture ack callback
0101  */
0102 static inline int
0103 snd_pcm_indirect_capture_transfer(struct snd_pcm_substream *substream,
0104                   struct snd_pcm_indirect *rec,
0105                   snd_pcm_indirect_copy_t copy)
0106 {
0107     struct snd_pcm_runtime *runtime = substream->runtime;
0108     snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
0109     snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
0110 
0111     if (diff) {
0112         if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
0113             diff += runtime->boundary;
0114         if (diff < 0)
0115             return -EINVAL;
0116         rec->sw_ready -= frames_to_bytes(runtime, diff);
0117         rec->appl_ptr = appl_ptr;
0118     }
0119     while (rec->hw_ready > 0 && 
0120            rec->sw_ready < (int)rec->sw_buffer_size) {
0121         size_t hw_to_end = rec->hw_buffer_size - rec->hw_data;
0122         size_t sw_to_end = rec->sw_buffer_size - rec->sw_data;
0123         size_t bytes = rec->sw_buffer_size - rec->sw_ready;
0124         if (rec->hw_ready < (int)bytes)
0125             bytes = rec->hw_ready;
0126         if (hw_to_end < bytes)
0127             bytes = hw_to_end;
0128         if (sw_to_end < bytes)
0129             bytes = sw_to_end;
0130         if (! bytes)
0131             break;
0132         copy(substream, rec, bytes);
0133         rec->hw_data += bytes;
0134         if ((int)rec->hw_data == rec->hw_buffer_size)
0135             rec->hw_data = 0;
0136         rec->sw_data += bytes;
0137         if (rec->sw_data == rec->sw_buffer_size)
0138             rec->sw_data = 0;
0139         rec->hw_ready -= bytes;
0140         rec->sw_ready += bytes;
0141     }
0142     return 0;
0143 }
0144 
0145 /*
0146  * helper function for capture pointer callback,
0147  * ptr = current byte pointer
0148  */
0149 static inline snd_pcm_uframes_t
0150 snd_pcm_indirect_capture_pointer(struct snd_pcm_substream *substream,
0151                  struct snd_pcm_indirect *rec, unsigned int ptr)
0152 {
0153     int qsize;
0154     int bytes = ptr - rec->hw_io;
0155     if (bytes < 0)
0156         bytes += rec->hw_buffer_size;
0157     rec->hw_io = ptr;
0158     rec->hw_ready += bytes;
0159     qsize = rec->hw_queue_size ? rec->hw_queue_size : rec->hw_buffer_size;
0160     if (rec->hw_ready > qsize)
0161         return SNDRV_PCM_POS_XRUN;
0162     rec->sw_io += bytes;
0163     if (rec->sw_io >= rec->sw_buffer_size)
0164         rec->sw_io -= rec->sw_buffer_size;
0165     if (substream->ops->ack)
0166         substream->ops->ack(substream);
0167     return bytes_to_frames(substream->runtime, rec->sw_io);
0168 }
0169 
0170 #endif /* __SOUND_PCM_INDIRECT_H */