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