1/*
2 * Helper functions for indirect PCM data transfer
3 *
4 *  Copyright (c) by Takashi Iwai <tiwai@suse.de>
5 *                   Jaroslav Kysela <perex@suse.cz>
6 *
7 *   This program is free software; you can redistribute it and/or modify
8 *   it under the terms of the GNU General Public License as published by
9 *   the Free Software Foundation; either version 2 of the License, or
10 *   (at your option) any later version.
11 *
12 *   This program is distributed in the hope that it will be useful,
13 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 *   GNU General Public License for more details.
16 *
17 *   You should have received a copy of the GNU General Public License
18 *   along with this program; if not, write to the Free Software
19 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22#ifndef __SOUND_PCM_INDIRECT_H
23#define __SOUND_PCM_INDIRECT_H
24
25#include <sound/pcm.h>
26
27struct snd_pcm_indirect {
28	unsigned int hw_buffer_size;	/* Byte size of hardware buffer */
29	unsigned int hw_queue_size;	/* Max queue size of hw buffer (0 = buffer size) */
30	unsigned int hw_data;	/* Offset to next dst (or src) in hw ring buffer */
31	unsigned int hw_io;	/* Ring buffer hw pointer */
32	int hw_ready;		/* Bytes ready for play (or captured) in hw ring buffer */
33	unsigned int sw_buffer_size;	/* Byte size of software buffer */
34	unsigned int sw_data;	/* Offset to next dst (or src) in sw ring buffer */
35	unsigned int sw_io;	/* Current software pointer in bytes */
36	int sw_ready;		/* Bytes ready to be transferred to/from hw */
37	snd_pcm_uframes_t appl_ptr;	/* Last seen appl_ptr */
38};
39
40typedef void (*snd_pcm_indirect_copy_t)(struct snd_pcm_substream *substream,
41					struct snd_pcm_indirect *rec, size_t bytes);
42
43/*
44 * helper function for playback ack callback
45 */
46static inline void
47snd_pcm_indirect_playback_transfer(struct snd_pcm_substream *substream,
48				   struct snd_pcm_indirect *rec,
49				   snd_pcm_indirect_copy_t copy)
50{
51	struct snd_pcm_runtime *runtime = substream->runtime;
52	snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
53	snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
54	int qsize;
55
56	if (diff) {
57		if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
58			diff += runtime->boundary;
59		rec->sw_ready += (int)frames_to_bytes(runtime, diff);
60		rec->appl_ptr = appl_ptr;
61	}
62	qsize = rec->hw_queue_size ? rec->hw_queue_size : rec->hw_buffer_size;
63	while (rec->hw_ready < qsize && rec->sw_ready > 0) {
64		unsigned int hw_to_end = rec->hw_buffer_size - rec->hw_data;
65		unsigned int sw_to_end = rec->sw_buffer_size - rec->sw_data;
66		unsigned int bytes = qsize - rec->hw_ready;
67		if (rec->sw_ready < (int)bytes)
68			bytes = rec->sw_ready;
69		if (hw_to_end < bytes)
70			bytes = hw_to_end;
71		if (sw_to_end < bytes)
72			bytes = sw_to_end;
73		if (! bytes)
74			break;
75		copy(substream, rec, bytes);
76		rec->hw_data += bytes;
77		if (rec->hw_data == rec->hw_buffer_size)
78			rec->hw_data = 0;
79		rec->sw_data += bytes;
80		if (rec->sw_data == rec->sw_buffer_size)
81			rec->sw_data = 0;
82		rec->hw_ready += bytes;
83		rec->sw_ready -= bytes;
84	}
85}
86
87/*
88 * helper function for playback pointer callback
89 * ptr = current byte pointer
90 */
91static inline snd_pcm_uframes_t
92snd_pcm_indirect_playback_pointer(struct snd_pcm_substream *substream,
93				  struct snd_pcm_indirect *rec, unsigned int ptr)
94{
95	int bytes = ptr - rec->hw_io;
96	if (bytes < 0)
97		bytes += rec->hw_buffer_size;
98	rec->hw_io = ptr;
99	rec->hw_ready -= bytes;
100	rec->sw_io += bytes;
101	if (rec->sw_io >= rec->sw_buffer_size)
102		rec->sw_io -= rec->sw_buffer_size;
103	if (substream->ops->ack)
104		substream->ops->ack(substream);
105	return bytes_to_frames(substream->runtime, rec->sw_io);
106}
107
108
109/*
110 * helper function for capture ack callback
111 */
112static inline void
113snd_pcm_indirect_capture_transfer(struct snd_pcm_substream *substream,
114				  struct snd_pcm_indirect *rec,
115				  snd_pcm_indirect_copy_t copy)
116{
117	struct snd_pcm_runtime *runtime = substream->runtime;
118	snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
119	snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
120
121	if (diff) {
122		if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
123			diff += runtime->boundary;
124		rec->sw_ready -= frames_to_bytes(runtime, diff);
125		rec->appl_ptr = appl_ptr;
126	}
127	while (rec->hw_ready > 0 &&
128	       rec->sw_ready < (int)rec->sw_buffer_size) {
129		size_t hw_to_end = rec->hw_buffer_size - rec->hw_data;
130		size_t sw_to_end = rec->sw_buffer_size - rec->sw_data;
131		size_t bytes = rec->sw_buffer_size - rec->sw_ready;
132		if (rec->hw_ready < (int)bytes)
133			bytes = rec->hw_ready;
134		if (hw_to_end < bytes)
135			bytes = hw_to_end;
136		if (sw_to_end < bytes)
137			bytes = sw_to_end;
138		if (! bytes)
139			break;
140		copy(substream, rec, bytes);
141		rec->hw_data += bytes;
142		if ((int)rec->hw_data == rec->hw_buffer_size)
143			rec->hw_data = 0;
144		rec->sw_data += bytes;
145		if (rec->sw_data == rec->sw_buffer_size)
146			rec->sw_data = 0;
147		rec->hw_ready -= bytes;
148		rec->sw_ready += bytes;
149	}
150}
151
152/*
153 * helper function for capture pointer callback,
154 * ptr = current byte pointer
155 */
156static inline snd_pcm_uframes_t
157snd_pcm_indirect_capture_pointer(struct snd_pcm_substream *substream,
158				 struct snd_pcm_indirect *rec, unsigned int ptr)
159{
160	int qsize;
161	int bytes = ptr - rec->hw_io;
162	if (bytes < 0)
163		bytes += rec->hw_buffer_size;
164	rec->hw_io = ptr;
165	rec->hw_ready += bytes;
166	qsize = rec->hw_queue_size ? rec->hw_queue_size : rec->hw_buffer_size;
167	if (rec->hw_ready > qsize)
168		return SNDRV_PCM_POS_XRUN;
169	rec->sw_io += bytes;
170	if (rec->sw_io >= rec->sw_buffer_size)
171		rec->sw_io -= rec->sw_buffer_size;
172	if (substream->ops->ack)
173		substream->ops->ack(substream);
174	return bytes_to_frames(substream->runtime, rec->sw_io);
175}
176
177#endif /* __SOUND_PCM_INDIRECT_H */
178