11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Helper functions for indirect PCM data transfer
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  Copyright (c) by Takashi Iwai <tiwai@suse.de>
5c1017a4cdb68ae5368fbc9ee42c77f1f5dca8916Jaroslav Kysela *                   Jaroslav Kysela <perex@perex.cz>
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *   This program is free software; you can redistribute it and/or modify
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *   it under the terms of the GNU General Public License as published by
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *   the Free Software Foundation; either version 2 of the License, or
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *   (at your option) any later version.
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *   This program is distributed in the hope that it will be useful,
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *   but WITHOUT ANY WARRANTY; without even the implied warranty of
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *   GNU General Public License for more details.
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *   You should have received a copy of the GNU General Public License
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *   along with this program; if not, write to the Free Software
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifndef __SOUND_PCM_INDIRECT_H
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define __SOUND_PCM_INDIRECT_H
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <sound/pcm.h>
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
27877211f5e1b1196179ba1290e8e1a3dc00427c55Takashi Iwaistruct snd_pcm_indirect {
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int hw_buffer_size;	/* Byte size of hardware buffer */
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int hw_queue_size;	/* Max queue size of hw buffer (0 = buffer size) */
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int hw_data;	/* Offset to next dst (or src) in hw ring buffer */
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int hw_io;	/* Ring buffer hw pointer */
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int hw_ready;		/* Bytes ready for play (or captured) in hw ring buffer */
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int sw_buffer_size;	/* Byte size of software buffer */
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int sw_data;	/* Offset to next dst (or src) in sw ring buffer */
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int sw_io;	/* Current software pointer in bytes */
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int sw_ready;		/* Bytes ready to be transferred to/from hw */
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	snd_pcm_uframes_t appl_ptr;	/* Last seen appl_ptr */
38877211f5e1b1196179ba1290e8e1a3dc00427c55Takashi Iwai};
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
40877211f5e1b1196179ba1290e8e1a3dc00427c55Takashi Iwaitypedef void (*snd_pcm_indirect_copy_t)(struct snd_pcm_substream *substream,
41877211f5e1b1196179ba1290e8e1a3dc00427c55Takashi Iwai					struct snd_pcm_indirect *rec, size_t bytes);
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * helper function for playback ack callback
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void
47877211f5e1b1196179ba1290e8e1a3dc00427c55Takashi Iwaisnd_pcm_indirect_playback_transfer(struct snd_pcm_substream *substream,
48877211f5e1b1196179ba1290e8e1a3dc00427c55Takashi Iwai				   struct snd_pcm_indirect *rec,
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   snd_pcm_indirect_copy_t copy)
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
51877211f5e1b1196179ba1290e8e1a3dc00427c55Takashi Iwai	struct snd_pcm_runtime *runtime = substream->runtime;
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int qsize;
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (diff) {
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			diff += runtime->boundary;
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rec->sw_ready += (int)frames_to_bytes(runtime, diff);
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rec->appl_ptr = appl_ptr;
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	qsize = rec->hw_queue_size ? rec->hw_queue_size : rec->hw_buffer_size;
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (rec->hw_ready < qsize && rec->sw_ready > 0) {
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		unsigned int hw_to_end = rec->hw_buffer_size - rec->hw_data;
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		unsigned int sw_to_end = rec->sw_buffer_size - rec->sw_data;
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		unsigned int bytes = qsize - rec->hw_ready;
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (rec->sw_ready < (int)bytes)
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			bytes = rec->sw_ready;
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (hw_to_end < bytes)
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			bytes = hw_to_end;
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (sw_to_end < bytes)
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			bytes = sw_to_end;
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (! bytes)
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		copy(substream, rec, bytes);
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rec->hw_data += bytes;
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (rec->hw_data == rec->hw_buffer_size)
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			rec->hw_data = 0;
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rec->sw_data += bytes;
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (rec->sw_data == rec->sw_buffer_size)
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			rec->sw_data = 0;
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rec->hw_ready += bytes;
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rec->sw_ready -= bytes;
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * helper function for playback pointer callback
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ptr = current byte pointer
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline snd_pcm_uframes_t
92877211f5e1b1196179ba1290e8e1a3dc00427c55Takashi Iwaisnd_pcm_indirect_playback_pointer(struct snd_pcm_substream *substream,
93877211f5e1b1196179ba1290e8e1a3dc00427c55Takashi Iwai				  struct snd_pcm_indirect *rec, unsigned int ptr)
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int bytes = ptr - rec->hw_io;
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (bytes < 0)
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		bytes += rec->hw_buffer_size;
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rec->hw_io = ptr;
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rec->hw_ready -= bytes;
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rec->sw_io += bytes;
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (rec->sw_io >= rec->sw_buffer_size)
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rec->sw_io -= rec->sw_buffer_size;
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (substream->ops->ack)
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		substream->ops->ack(substream);
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return bytes_to_frames(substream->runtime, rec->sw_io);
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * helper function for capture ack callback
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void
113877211f5e1b1196179ba1290e8e1a3dc00427c55Takashi Iwaisnd_pcm_indirect_capture_transfer(struct snd_pcm_substream *substream,
114877211f5e1b1196179ba1290e8e1a3dc00427c55Takashi Iwai				  struct snd_pcm_indirect *rec,
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				  snd_pcm_indirect_copy_t copy)
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
117877211f5e1b1196179ba1290e8e1a3dc00427c55Takashi Iwai	struct snd_pcm_runtime *runtime = substream->runtime;
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (diff) {
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			diff += runtime->boundary;
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rec->sw_ready -= frames_to_bytes(runtime, diff);
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rec->appl_ptr = appl_ptr;
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (rec->hw_ready > 0 &&
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       rec->sw_ready < (int)rec->sw_buffer_size) {
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		size_t hw_to_end = rec->hw_buffer_size - rec->hw_data;
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		size_t sw_to_end = rec->sw_buffer_size - rec->sw_data;
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		size_t bytes = rec->sw_buffer_size - rec->sw_ready;
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (rec->hw_ready < (int)bytes)
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			bytes = rec->hw_ready;
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (hw_to_end < bytes)
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			bytes = hw_to_end;
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (sw_to_end < bytes)
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			bytes = sw_to_end;
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (! bytes)
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		copy(substream, rec, bytes);
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rec->hw_data += bytes;
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((int)rec->hw_data == rec->hw_buffer_size)
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			rec->hw_data = 0;
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rec->sw_data += bytes;
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (rec->sw_data == rec->sw_buffer_size)
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			rec->sw_data = 0;
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rec->hw_ready -= bytes;
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rec->sw_ready += bytes;
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * helper function for capture pointer callback,
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ptr = current byte pointer
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline snd_pcm_uframes_t
157877211f5e1b1196179ba1290e8e1a3dc00427c55Takashi Iwaisnd_pcm_indirect_capture_pointer(struct snd_pcm_substream *substream,
158877211f5e1b1196179ba1290e8e1a3dc00427c55Takashi Iwai				 struct snd_pcm_indirect *rec, unsigned int ptr)
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int qsize;
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int bytes = ptr - rec->hw_io;
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (bytes < 0)
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		bytes += rec->hw_buffer_size;
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rec->hw_io = ptr;
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rec->hw_ready += bytes;
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	qsize = rec->hw_queue_size ? rec->hw_queue_size : rec->hw_buffer_size;
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (rec->hw_ready > qsize)
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return SNDRV_PCM_POS_XRUN;
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rec->sw_io += bytes;
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (rec->sw_io >= rec->sw_buffer_size)
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rec->sw_io -= rec->sw_buffer_size;
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (substream->ops->ack)
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		substream->ops->ack(substream);
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return bytes_to_frames(substream->runtime, rec->sw_io);
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif /* __SOUND_PCM_INDIRECT_H */
178