xref: /linux/sound/core/oss/pcm_plugin.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *  PCM Plug-In shared (kernel/library) code
3c1017a4cSJaroslav Kysela  *  Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
41da177e4SLinus Torvalds  *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  *   This library is free software; you can redistribute it and/or modify
81da177e4SLinus Torvalds  *   it under the terms of the GNU Library General Public License as
91da177e4SLinus Torvalds  *   published by the Free Software Foundation; either version 2 of
101da177e4SLinus Torvalds  *   the License, or (at your option) any later version.
111da177e4SLinus Torvalds  *
121da177e4SLinus Torvalds  *   This program is distributed in the hope that it will be useful,
131da177e4SLinus Torvalds  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
141da177e4SLinus Torvalds  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
151da177e4SLinus Torvalds  *   GNU Library General Public License for more details.
161da177e4SLinus Torvalds  *
171da177e4SLinus Torvalds  *   You should have received a copy of the GNU Library General Public
181da177e4SLinus Torvalds  *   License along with this library; if not, write to the Free Software
191da177e4SLinus Torvalds  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
201da177e4SLinus Torvalds  *
211da177e4SLinus Torvalds  */
221da177e4SLinus Torvalds 
231da177e4SLinus Torvalds #if 0
241da177e4SLinus Torvalds #define PLUGIN_DEBUG
251da177e4SLinus Torvalds #endif
261da177e4SLinus Torvalds 
271da177e4SLinus Torvalds #include <linux/slab.h>
281da177e4SLinus Torvalds #include <linux/time.h>
291da177e4SLinus Torvalds #include <linux/vmalloc.h>
301da177e4SLinus Torvalds #include <sound/core.h>
311da177e4SLinus Torvalds #include <sound/pcm.h>
321da177e4SLinus Torvalds #include <sound/pcm_params.h>
331da177e4SLinus Torvalds #include "pcm_plugin.h"
341da177e4SLinus Torvalds 
351da177e4SLinus Torvalds #define snd_pcm_plug_first(plug) ((plug)->runtime->oss.plugin_first)
361da177e4SLinus Torvalds #define snd_pcm_plug_last(plug) ((plug)->runtime->oss.plugin_last)
371da177e4SLinus Torvalds 
381da177e4SLinus Torvalds /*
391da177e4SLinus Torvalds  *  because some cards might have rates "very close", we ignore
401da177e4SLinus Torvalds  *  all "resampling" requests within +-5%
411da177e4SLinus Torvalds  */
rate_match(unsigned int src_rate,unsigned int dst_rate)421da177e4SLinus Torvalds static int rate_match(unsigned int src_rate, unsigned int dst_rate)
431da177e4SLinus Torvalds {
441da177e4SLinus Torvalds 	unsigned int low = (src_rate * 95) / 100;
451da177e4SLinus Torvalds 	unsigned int high = (src_rate * 105) / 100;
461da177e4SLinus Torvalds 	return dst_rate >= low && dst_rate <= high;
471da177e4SLinus Torvalds }
481da177e4SLinus Torvalds 
snd_pcm_plugin_alloc(struct snd_pcm_plugin * plugin,snd_pcm_uframes_t frames)496ac77bc1STakashi Iwai static int snd_pcm_plugin_alloc(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t frames)
501da177e4SLinus Torvalds {
516ac77bc1STakashi Iwai 	struct snd_pcm_plugin_format *format;
521da177e4SLinus Torvalds 	ssize_t width;
531da177e4SLinus Torvalds 	size_t size;
541da177e4SLinus Torvalds 	unsigned int channel;
556ac77bc1STakashi Iwai 	struct snd_pcm_plugin_channel *c;
561da177e4SLinus Torvalds 
571da177e4SLinus Torvalds 	if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK) {
581da177e4SLinus Torvalds 		format = &plugin->src_format;
591da177e4SLinus Torvalds 	} else {
601da177e4SLinus Torvalds 		format = &plugin->dst_format;
611da177e4SLinus Torvalds 	}
6251c816fdSTakashi Iwai 	width = snd_pcm_format_physical_width(format->format);
6351c816fdSTakashi Iwai 	if (width < 0)
641da177e4SLinus Torvalds 		return width;
65*efb6402cSTakashi Iwai 	size = array3_size(frames, format->channels, width);
66*efb6402cSTakashi Iwai 	/* check for too large period size once again */
67*efb6402cSTakashi Iwai 	if (size > 1024 * 1024)
68*efb6402cSTakashi Iwai 		return -ENOMEM;
697eaa943cSTakashi Iwai 	if (snd_BUG_ON(size % 8))
707eaa943cSTakashi Iwai 		return -ENXIO;
711da177e4SLinus Torvalds 	size /= 8;
721da177e4SLinus Torvalds 	if (plugin->buf_frames < frames) {
7365766ee0STakashi Iwai 		kvfree(plugin->buf);
7465766ee0STakashi Iwai 		plugin->buf = kvzalloc(size, GFP_KERNEL);
751da177e4SLinus Torvalds 		plugin->buf_frames = frames;
761da177e4SLinus Torvalds 	}
771da177e4SLinus Torvalds 	if (!plugin->buf) {
781da177e4SLinus Torvalds 		plugin->buf_frames = 0;
791da177e4SLinus Torvalds 		return -ENOMEM;
801da177e4SLinus Torvalds 	}
811da177e4SLinus Torvalds 	c = plugin->buf_channels;
821da177e4SLinus Torvalds 	if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
831da177e4SLinus Torvalds 		for (channel = 0; channel < format->channels; channel++, c++) {
841da177e4SLinus Torvalds 			c->frames = frames;
851da177e4SLinus Torvalds 			c->enabled = 1;
861da177e4SLinus Torvalds 			c->wanted = 0;
871da177e4SLinus Torvalds 			c->area.addr = plugin->buf;
881da177e4SLinus Torvalds 			c->area.first = channel * width;
891da177e4SLinus Torvalds 			c->area.step = format->channels * width;
901da177e4SLinus Torvalds 		}
911da177e4SLinus Torvalds 	} else if (plugin->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) {
927eaa943cSTakashi Iwai 		if (snd_BUG_ON(size % format->channels))
937eaa943cSTakashi Iwai 			return -EINVAL;
941da177e4SLinus Torvalds 		size /= format->channels;
951da177e4SLinus Torvalds 		for (channel = 0; channel < format->channels; channel++, c++) {
961da177e4SLinus Torvalds 			c->frames = frames;
971da177e4SLinus Torvalds 			c->enabled = 1;
981da177e4SLinus Torvalds 			c->wanted = 0;
991da177e4SLinus Torvalds 			c->area.addr = plugin->buf + (channel * size);
1001da177e4SLinus Torvalds 			c->area.first = 0;
1011da177e4SLinus Torvalds 			c->area.step = width;
1021da177e4SLinus Torvalds 		}
1031da177e4SLinus Torvalds 	} else
1041da177e4SLinus Torvalds 		return -EINVAL;
1051da177e4SLinus Torvalds 	return 0;
1061da177e4SLinus Torvalds }
1071da177e4SLinus Torvalds 
snd_pcm_plug_alloc(struct snd_pcm_substream * plug,snd_pcm_uframes_t frames)1086ac77bc1STakashi Iwai int snd_pcm_plug_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t frames)
1091da177e4SLinus Torvalds {
1101da177e4SLinus Torvalds 	int err;
1117eaa943cSTakashi Iwai 	if (snd_BUG_ON(!snd_pcm_plug_first(plug)))
1127eaa943cSTakashi Iwai 		return -ENXIO;
1131da177e4SLinus Torvalds 	if (snd_pcm_plug_stream(plug) == SNDRV_PCM_STREAM_PLAYBACK) {
1146ac77bc1STakashi Iwai 		struct snd_pcm_plugin *plugin = snd_pcm_plug_first(plug);
1151da177e4SLinus Torvalds 		while (plugin->next) {
1161da177e4SLinus Torvalds 			if (plugin->dst_frames)
1171da177e4SLinus Torvalds 				frames = plugin->dst_frames(plugin, frames);
1185461e053STakashi Iwai 			if ((snd_pcm_sframes_t)frames <= 0)
1197eaa943cSTakashi Iwai 				return -ENXIO;
1201da177e4SLinus Torvalds 			plugin = plugin->next;
1211da177e4SLinus Torvalds 			err = snd_pcm_plugin_alloc(plugin, frames);
1221da177e4SLinus Torvalds 			if (err < 0)
1231da177e4SLinus Torvalds 				return err;
1241da177e4SLinus Torvalds 		}
1251da177e4SLinus Torvalds 	} else {
1266ac77bc1STakashi Iwai 		struct snd_pcm_plugin *plugin = snd_pcm_plug_last(plug);
1271da177e4SLinus Torvalds 		while (plugin->prev) {
1281da177e4SLinus Torvalds 			if (plugin->src_frames)
1291da177e4SLinus Torvalds 				frames = plugin->src_frames(plugin, frames);
1305461e053STakashi Iwai 			if ((snd_pcm_sframes_t)frames <= 0)
1317eaa943cSTakashi Iwai 				return -ENXIO;
1321da177e4SLinus Torvalds 			plugin = plugin->prev;
1331da177e4SLinus Torvalds 			err = snd_pcm_plugin_alloc(plugin, frames);
1341da177e4SLinus Torvalds 			if (err < 0)
1351da177e4SLinus Torvalds 				return err;
1361da177e4SLinus Torvalds 		}
1371da177e4SLinus Torvalds 	}
1381da177e4SLinus Torvalds 	return 0;
1391da177e4SLinus Torvalds }
1401da177e4SLinus Torvalds 
1411da177e4SLinus Torvalds 
snd_pcm_plugin_client_channels(struct snd_pcm_plugin * plugin,snd_pcm_uframes_t frames,struct snd_pcm_plugin_channel ** channels)1426ac77bc1STakashi Iwai snd_pcm_sframes_t snd_pcm_plugin_client_channels(struct snd_pcm_plugin *plugin,
1431da177e4SLinus Torvalds 				       snd_pcm_uframes_t frames,
1446ac77bc1STakashi Iwai 				       struct snd_pcm_plugin_channel **channels)
1451da177e4SLinus Torvalds {
1461da177e4SLinus Torvalds 	*channels = plugin->buf_channels;
1471da177e4SLinus Torvalds 	return frames;
1481da177e4SLinus Torvalds }
1491da177e4SLinus Torvalds 
snd_pcm_plugin_build(struct snd_pcm_substream * plug,const char * name,struct snd_pcm_plugin_format * src_format,struct snd_pcm_plugin_format * dst_format,size_t extra,struct snd_pcm_plugin ** ret)1506ac77bc1STakashi Iwai int snd_pcm_plugin_build(struct snd_pcm_substream *plug,
1511da177e4SLinus Torvalds 			 const char *name,
1526ac77bc1STakashi Iwai 			 struct snd_pcm_plugin_format *src_format,
1536ac77bc1STakashi Iwai 			 struct snd_pcm_plugin_format *dst_format,
1541da177e4SLinus Torvalds 			 size_t extra,
1556ac77bc1STakashi Iwai 			 struct snd_pcm_plugin **ret)
1561da177e4SLinus Torvalds {
1576ac77bc1STakashi Iwai 	struct snd_pcm_plugin *plugin;
1581da177e4SLinus Torvalds 	unsigned int channels;
1591da177e4SLinus Torvalds 
1607eaa943cSTakashi Iwai 	if (snd_BUG_ON(!plug))
1617eaa943cSTakashi Iwai 		return -ENXIO;
1627eaa943cSTakashi Iwai 	if (snd_BUG_ON(!src_format || !dst_format))
1637eaa943cSTakashi Iwai 		return -ENXIO;
164ca2c0966STakashi Iwai 	plugin = kzalloc(sizeof(*plugin) + extra, GFP_KERNEL);
1651da177e4SLinus Torvalds 	if (plugin == NULL)
1661da177e4SLinus Torvalds 		return -ENOMEM;
1671da177e4SLinus Torvalds 	plugin->name = name;
1681da177e4SLinus Torvalds 	plugin->plug = plug;
1691da177e4SLinus Torvalds 	plugin->stream = snd_pcm_plug_stream(plug);
1701da177e4SLinus Torvalds 	plugin->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
1711da177e4SLinus Torvalds 	plugin->src_format = *src_format;
1721da177e4SLinus Torvalds 	plugin->src_width = snd_pcm_format_physical_width(src_format->format);
1737eaa943cSTakashi Iwai 	snd_BUG_ON(plugin->src_width <= 0);
1741da177e4SLinus Torvalds 	plugin->dst_format = *dst_format;
1751da177e4SLinus Torvalds 	plugin->dst_width = snd_pcm_format_physical_width(dst_format->format);
1767eaa943cSTakashi Iwai 	snd_BUG_ON(plugin->dst_width <= 0);
1771da177e4SLinus Torvalds 	if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK)
1781da177e4SLinus Torvalds 		channels = src_format->channels;
1791da177e4SLinus Torvalds 	else
1801da177e4SLinus Torvalds 		channels = dst_format->channels;
1811da177e4SLinus Torvalds 	plugin->buf_channels = kcalloc(channels, sizeof(*plugin->buf_channels), GFP_KERNEL);
1821da177e4SLinus Torvalds 	if (plugin->buf_channels == NULL) {
1831da177e4SLinus Torvalds 		snd_pcm_plugin_free(plugin);
1841da177e4SLinus Torvalds 		return -ENOMEM;
1851da177e4SLinus Torvalds 	}
1861da177e4SLinus Torvalds 	plugin->client_channels = snd_pcm_plugin_client_channels;
1871da177e4SLinus Torvalds 	*ret = plugin;
1881da177e4SLinus Torvalds 	return 0;
1891da177e4SLinus Torvalds }
1901da177e4SLinus Torvalds 
snd_pcm_plugin_free(struct snd_pcm_plugin * plugin)1916ac77bc1STakashi Iwai int snd_pcm_plugin_free(struct snd_pcm_plugin *plugin)
1921da177e4SLinus Torvalds {
1931da177e4SLinus Torvalds 	if (! plugin)
1941da177e4SLinus Torvalds 		return 0;
1951da177e4SLinus Torvalds 	if (plugin->private_free)
1961da177e4SLinus Torvalds 		plugin->private_free(plugin);
1971da177e4SLinus Torvalds 	kfree(plugin->buf_channels);
19865766ee0STakashi Iwai 	kvfree(plugin->buf);
1991da177e4SLinus Torvalds 	kfree(plugin);
2001da177e4SLinus Torvalds 	return 0;
2011da177e4SLinus Torvalds }
2021da177e4SLinus Torvalds 
calc_dst_frames(struct snd_pcm_substream * plug,snd_pcm_sframes_t frames,bool check_size)2033bbf9e2fSTakashi Iwai static snd_pcm_sframes_t calc_dst_frames(struct snd_pcm_substream *plug,
204ff7e06a5STakashi Iwai 					 snd_pcm_sframes_t frames,
205ff7e06a5STakashi Iwai 					 bool check_size)
2061da177e4SLinus Torvalds {
2073bbf9e2fSTakashi Iwai 	struct snd_pcm_plugin *plugin, *plugin_next;
2081da177e4SLinus Torvalds 
2091da177e4SLinus Torvalds 	plugin = snd_pcm_plug_first(plug);
2101da177e4SLinus Torvalds 	while (plugin && frames > 0) {
2111da177e4SLinus Torvalds 		plugin_next = plugin->next;
212ac957e8cSTakashi Iwai 		if (check_size && plugin->buf_frames &&
213ac957e8cSTakashi Iwai 		    frames > plugin->buf_frames)
214ac957e8cSTakashi Iwai 			frames = plugin->buf_frames;
2151da177e4SLinus Torvalds 		if (plugin->dst_frames) {
2161da177e4SLinus Torvalds 			frames = plugin->dst_frames(plugin, frames);
2171da177e4SLinus Torvalds 			if (frames < 0)
2181da177e4SLinus Torvalds 				return frames;
2191da177e4SLinus Torvalds 		}
2201da177e4SLinus Torvalds 		plugin = plugin_next;
2211da177e4SLinus Torvalds 	}
2223bbf9e2fSTakashi Iwai 	return frames;
2233bbf9e2fSTakashi Iwai }
2243bbf9e2fSTakashi Iwai 
calc_src_frames(struct snd_pcm_substream * plug,snd_pcm_sframes_t frames,bool check_size)2253bbf9e2fSTakashi Iwai static snd_pcm_sframes_t calc_src_frames(struct snd_pcm_substream *plug,
226ff7e06a5STakashi Iwai 					 snd_pcm_sframes_t frames,
227ff7e06a5STakashi Iwai 					 bool check_size)
2283bbf9e2fSTakashi Iwai {
2293bbf9e2fSTakashi Iwai 	struct snd_pcm_plugin *plugin, *plugin_prev;
2303bbf9e2fSTakashi Iwai 
2311da177e4SLinus Torvalds 	plugin = snd_pcm_plug_last(plug);
2323bbf9e2fSTakashi Iwai 	while (plugin && frames > 0) {
2331da177e4SLinus Torvalds 		plugin_prev = plugin->prev;
2341da177e4SLinus Torvalds 		if (plugin->src_frames) {
2351da177e4SLinus Torvalds 			frames = plugin->src_frames(plugin, frames);
2361da177e4SLinus Torvalds 			if (frames < 0)
2371da177e4SLinus Torvalds 				return frames;
2381da177e4SLinus Torvalds 		}
239ac957e8cSTakashi Iwai 		if (check_size && plugin->buf_frames &&
240ac957e8cSTakashi Iwai 		    frames > plugin->buf_frames)
241ac957e8cSTakashi Iwai 			frames = plugin->buf_frames;
2421da177e4SLinus Torvalds 		plugin = plugin_prev;
2431da177e4SLinus Torvalds 	}
2441da177e4SLinus Torvalds 	return frames;
2451da177e4SLinus Torvalds }
2461da177e4SLinus Torvalds 
snd_pcm_plug_client_size(struct snd_pcm_substream * plug,snd_pcm_uframes_t drv_frames)2473bbf9e2fSTakashi Iwai snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *plug, snd_pcm_uframes_t drv_frames)
2483bbf9e2fSTakashi Iwai {
2493bbf9e2fSTakashi Iwai 	if (snd_BUG_ON(!plug))
2503bbf9e2fSTakashi Iwai 		return -ENXIO;
2513bbf9e2fSTakashi Iwai 	switch (snd_pcm_plug_stream(plug)) {
2523bbf9e2fSTakashi Iwai 	case SNDRV_PCM_STREAM_PLAYBACK:
253ff7e06a5STakashi Iwai 		return calc_src_frames(plug, drv_frames, false);
2543bbf9e2fSTakashi Iwai 	case SNDRV_PCM_STREAM_CAPTURE:
255ff7e06a5STakashi Iwai 		return calc_dst_frames(plug, drv_frames, false);
2563bbf9e2fSTakashi Iwai 	default:
2573bbf9e2fSTakashi Iwai 		snd_BUG();
2583bbf9e2fSTakashi Iwai 		return -EINVAL;
2593bbf9e2fSTakashi Iwai 	}
2603bbf9e2fSTakashi Iwai }
2613bbf9e2fSTakashi Iwai 
snd_pcm_plug_slave_size(struct snd_pcm_substream * plug,snd_pcm_uframes_t clt_frames)2623bbf9e2fSTakashi Iwai snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *plug, snd_pcm_uframes_t clt_frames)
2633bbf9e2fSTakashi Iwai {
2643bbf9e2fSTakashi Iwai 	if (snd_BUG_ON(!plug))
2653bbf9e2fSTakashi Iwai 		return -ENXIO;
2663bbf9e2fSTakashi Iwai 	switch (snd_pcm_plug_stream(plug)) {
2673bbf9e2fSTakashi Iwai 	case SNDRV_PCM_STREAM_PLAYBACK:
268ff7e06a5STakashi Iwai 		return calc_dst_frames(plug, clt_frames, false);
2693bbf9e2fSTakashi Iwai 	case SNDRV_PCM_STREAM_CAPTURE:
270ff7e06a5STakashi Iwai 		return calc_src_frames(plug, clt_frames, false);
2713bbf9e2fSTakashi Iwai 	default:
2723bbf9e2fSTakashi Iwai 		snd_BUG();
2733bbf9e2fSTakashi Iwai 		return -EINVAL;
2743bbf9e2fSTakashi Iwai 	}
2753bbf9e2fSTakashi Iwai }
2763bbf9e2fSTakashi Iwai 
snd_pcm_plug_formats(const struct snd_mask * mask,snd_pcm_format_t format)277e76bf3c4STakashi Sakamoto static int snd_pcm_plug_formats(const struct snd_mask *mask,
278e76bf3c4STakashi Sakamoto 				snd_pcm_format_t format)
2791da177e4SLinus Torvalds {
2806ac77bc1STakashi Iwai 	struct snd_mask formats = *mask;
2811da177e4SLinus Torvalds 	u64 linfmts = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
2821da177e4SLinus Torvalds 		       SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE |
2831da177e4SLinus Torvalds 		       SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE |
2841da177e4SLinus Torvalds 		       SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_S24_LE |
2851da177e4SLinus Torvalds 		       SNDRV_PCM_FMTBIT_U24_BE | SNDRV_PCM_FMTBIT_S24_BE |
28664d27f96STakashi Iwai 		       SNDRV_PCM_FMTBIT_U24_3LE | SNDRV_PCM_FMTBIT_S24_3LE |
28764d27f96STakashi Iwai 		       SNDRV_PCM_FMTBIT_U24_3BE | SNDRV_PCM_FMTBIT_S24_3BE |
2881da177e4SLinus Torvalds 		       SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE |
2891da177e4SLinus Torvalds 		       SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE);
290fea952e5SClemens Ladisch 	snd_mask_set(&formats, (__force int)SNDRV_PCM_FORMAT_MU_LAW);
2911da177e4SLinus Torvalds 
292191bb51eSTakashi Iwai 	if (formats.bits[0] & lower_32_bits(linfmts))
293191bb51eSTakashi Iwai 		formats.bits[0] |= lower_32_bits(linfmts);
294191bb51eSTakashi Iwai 	if (formats.bits[1] & upper_32_bits(linfmts))
295191bb51eSTakashi Iwai 		formats.bits[1] |= upper_32_bits(linfmts);
296fea952e5SClemens Ladisch 	return snd_mask_test(&formats, (__force int)format);
2971da177e4SLinus Torvalds }
2981da177e4SLinus Torvalds 
299b40fe4bbSTakashi Iwai static const snd_pcm_format_t preferred_formats[] = {
3001da177e4SLinus Torvalds 	SNDRV_PCM_FORMAT_S16_LE,
3011da177e4SLinus Torvalds 	SNDRV_PCM_FORMAT_S16_BE,
3021da177e4SLinus Torvalds 	SNDRV_PCM_FORMAT_U16_LE,
3031da177e4SLinus Torvalds 	SNDRV_PCM_FORMAT_U16_BE,
30464d27f96STakashi Iwai 	SNDRV_PCM_FORMAT_S24_3LE,
30564d27f96STakashi Iwai 	SNDRV_PCM_FORMAT_S24_3BE,
30664d27f96STakashi Iwai 	SNDRV_PCM_FORMAT_U24_3LE,
30764d27f96STakashi Iwai 	SNDRV_PCM_FORMAT_U24_3BE,
3081da177e4SLinus Torvalds 	SNDRV_PCM_FORMAT_S24_LE,
3091da177e4SLinus Torvalds 	SNDRV_PCM_FORMAT_S24_BE,
3101da177e4SLinus Torvalds 	SNDRV_PCM_FORMAT_U24_LE,
3111da177e4SLinus Torvalds 	SNDRV_PCM_FORMAT_U24_BE,
3121da177e4SLinus Torvalds 	SNDRV_PCM_FORMAT_S32_LE,
3131da177e4SLinus Torvalds 	SNDRV_PCM_FORMAT_S32_BE,
3141da177e4SLinus Torvalds 	SNDRV_PCM_FORMAT_U32_LE,
3151da177e4SLinus Torvalds 	SNDRV_PCM_FORMAT_U32_BE,
3161da177e4SLinus Torvalds 	SNDRV_PCM_FORMAT_S8,
3171da177e4SLinus Torvalds 	SNDRV_PCM_FORMAT_U8
3181da177e4SLinus Torvalds };
3191da177e4SLinus Torvalds 
snd_pcm_plug_slave_format(snd_pcm_format_t format,const struct snd_mask * format_mask)320fea952e5SClemens Ladisch snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format,
321e76bf3c4STakashi Sakamoto 					   const struct snd_mask *format_mask)
3221da177e4SLinus Torvalds {
32364d27f96STakashi Iwai 	int i;
32464d27f96STakashi Iwai 
325fea952e5SClemens Ladisch 	if (snd_mask_test(format_mask, (__force int)format))
3261da177e4SLinus Torvalds 		return format;
3271da177e4SLinus Torvalds 	if (!snd_pcm_plug_formats(format_mask, format))
328fea952e5SClemens Ladisch 		return (__force snd_pcm_format_t)-EINVAL;
3291da177e4SLinus Torvalds 	if (snd_pcm_format_linear(format)) {
33064d27f96STakashi Iwai 		unsigned int width = snd_pcm_format_width(format);
33164d27f96STakashi Iwai 		int unsignd = snd_pcm_format_unsigned(format) > 0;
33264d27f96STakashi Iwai 		int big = snd_pcm_format_big_endian(format) > 0;
33364d27f96STakashi Iwai 		unsigned int badness, best = -1;
334fea952e5SClemens Ladisch 		snd_pcm_format_t best_format = (__force snd_pcm_format_t)-1;
33564d27f96STakashi Iwai 		for (i = 0; i < ARRAY_SIZE(preferred_formats); i++) {
336fea952e5SClemens Ladisch 			snd_pcm_format_t f = preferred_formats[i];
33764d27f96STakashi Iwai 			unsigned int w;
338fea952e5SClemens Ladisch 			if (!snd_mask_test(format_mask, (__force int)f))
33964d27f96STakashi Iwai 				continue;
34064d27f96STakashi Iwai 			w = snd_pcm_format_width(f);
34164d27f96STakashi Iwai 			if (w >= width)
34264d27f96STakashi Iwai 				badness = w - width;
34364d27f96STakashi Iwai 			else
34464d27f96STakashi Iwai 				badness = width - w + 32;
34564d27f96STakashi Iwai 			badness += snd_pcm_format_unsigned(f) != unsignd;
34664d27f96STakashi Iwai 			badness += snd_pcm_format_big_endian(f) != big;
34764d27f96STakashi Iwai 			if (badness < best) {
34864d27f96STakashi Iwai 				best_format = f;
34964d27f96STakashi Iwai 				best = badness;
3501da177e4SLinus Torvalds 			}
3511da177e4SLinus Torvalds 		}
352fea952e5SClemens Ladisch 		if ((__force int)best_format >= 0)
353fea952e5SClemens Ladisch 			return best_format;
354fea952e5SClemens Ladisch 		else
355fea952e5SClemens Ladisch 			return (__force snd_pcm_format_t)-EINVAL;
3561da177e4SLinus Torvalds 	} else {
3571da177e4SLinus Torvalds 		switch (format) {
3581da177e4SLinus Torvalds 		case SNDRV_PCM_FORMAT_MU_LAW:
3591da177e4SLinus Torvalds 			for (i = 0; i < ARRAY_SIZE(preferred_formats); ++i) {
360fea952e5SClemens Ladisch 				snd_pcm_format_t format1 = preferred_formats[i];
361fea952e5SClemens Ladisch 				if (snd_mask_test(format_mask, (__force int)format1))
3621da177e4SLinus Torvalds 					return format1;
3631da177e4SLinus Torvalds 			}
364c0dbbdadSGustavo A. R. Silva 			fallthrough;
3651da177e4SLinus Torvalds 		default:
366fea952e5SClemens Ladisch 			return (__force snd_pcm_format_t)-EINVAL;
3671da177e4SLinus Torvalds 		}
3681da177e4SLinus Torvalds 	}
3691da177e4SLinus Torvalds }
3701da177e4SLinus Torvalds 
snd_pcm_plug_format_plugins(struct snd_pcm_substream * plug,struct snd_pcm_hw_params * params,struct snd_pcm_hw_params * slave_params)3716ac77bc1STakashi Iwai int snd_pcm_plug_format_plugins(struct snd_pcm_substream *plug,
3726ac77bc1STakashi Iwai 				struct snd_pcm_hw_params *params,
3736ac77bc1STakashi Iwai 				struct snd_pcm_hw_params *slave_params)
3741da177e4SLinus Torvalds {
3756ac77bc1STakashi Iwai 	struct snd_pcm_plugin_format tmpformat;
3766ac77bc1STakashi Iwai 	struct snd_pcm_plugin_format dstformat;
3776ac77bc1STakashi Iwai 	struct snd_pcm_plugin_format srcformat;
378fea952e5SClemens Ladisch 	snd_pcm_access_t src_access, dst_access;
3796ac77bc1STakashi Iwai 	struct snd_pcm_plugin *plugin = NULL;
3801da177e4SLinus Torvalds 	int err;
3811da177e4SLinus Torvalds 	int stream = snd_pcm_plug_stream(plug);
3821da177e4SLinus Torvalds 	int slave_interleaved = (params_channels(slave_params) == 1 ||
3831da177e4SLinus Torvalds 				 params_access(slave_params) == SNDRV_PCM_ACCESS_RW_INTERLEAVED);
3841da177e4SLinus Torvalds 
3851da177e4SLinus Torvalds 	switch (stream) {
3861da177e4SLinus Torvalds 	case SNDRV_PCM_STREAM_PLAYBACK:
3871da177e4SLinus Torvalds 		dstformat.format = params_format(slave_params);
3881da177e4SLinus Torvalds 		dstformat.rate = params_rate(slave_params);
3891da177e4SLinus Torvalds 		dstformat.channels = params_channels(slave_params);
3901da177e4SLinus Torvalds 		srcformat.format = params_format(params);
3911da177e4SLinus Torvalds 		srcformat.rate = params_rate(params);
3921da177e4SLinus Torvalds 		srcformat.channels = params_channels(params);
3931da177e4SLinus Torvalds 		src_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
3941da177e4SLinus Torvalds 		dst_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED :
3951da177e4SLinus Torvalds 						  SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);
3961da177e4SLinus Torvalds 		break;
3971da177e4SLinus Torvalds 	case SNDRV_PCM_STREAM_CAPTURE:
3981da177e4SLinus Torvalds 		dstformat.format = params_format(params);
3991da177e4SLinus Torvalds 		dstformat.rate = params_rate(params);
4001da177e4SLinus Torvalds 		dstformat.channels = params_channels(params);
4011da177e4SLinus Torvalds 		srcformat.format = params_format(slave_params);
4021da177e4SLinus Torvalds 		srcformat.rate = params_rate(slave_params);
4031da177e4SLinus Torvalds 		srcformat.channels = params_channels(slave_params);
4041da177e4SLinus Torvalds 		src_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED :
4051da177e4SLinus Torvalds 						  SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);
4061da177e4SLinus Torvalds 		dst_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
4071da177e4SLinus Torvalds 		break;
4081da177e4SLinus Torvalds 	default:
4091da177e4SLinus Torvalds 		snd_BUG();
4101da177e4SLinus Torvalds 		return -EINVAL;
4111da177e4SLinus Torvalds 	}
4121da177e4SLinus Torvalds 	tmpformat = srcformat;
4131da177e4SLinus Torvalds 
4141da177e4SLinus Torvalds 	pdprintf("srcformat: format=%i, rate=%i, channels=%i\n",
4151da177e4SLinus Torvalds 		 srcformat.format,
4161da177e4SLinus Torvalds 		 srcformat.rate,
4171da177e4SLinus Torvalds 		 srcformat.channels);
4181da177e4SLinus Torvalds 	pdprintf("dstformat: format=%i, rate=%i, channels=%i\n",
4191da177e4SLinus Torvalds 		 dstformat.format,
4201da177e4SLinus Torvalds 		 dstformat.rate,
4211da177e4SLinus Torvalds 		 dstformat.channels);
4221da177e4SLinus Torvalds 
4231da177e4SLinus Torvalds 	/* Format change (linearization) */
4240534ab42STakashi Iwai 	if (! rate_match(srcformat.rate, dstformat.rate) &&
4251da177e4SLinus Torvalds 	    ! snd_pcm_format_linear(srcformat.format)) {
4260534ab42STakashi Iwai 		if (srcformat.format != SNDRV_PCM_FORMAT_MU_LAW)
4270534ab42STakashi Iwai 			return -EINVAL;
4281da177e4SLinus Torvalds 		tmpformat.format = SNDRV_PCM_FORMAT_S16;
4291da177e4SLinus Torvalds 		err = snd_pcm_plugin_build_mulaw(plug,
4301da177e4SLinus Torvalds 						 &srcformat, &tmpformat,
4311da177e4SLinus Torvalds 						 &plugin);
4321da177e4SLinus Torvalds 		if (err < 0)
4331da177e4SLinus Torvalds 			return err;
4341da177e4SLinus Torvalds 		err = snd_pcm_plugin_append(plugin);
4351da177e4SLinus Torvalds 		if (err < 0) {
4361da177e4SLinus Torvalds 			snd_pcm_plugin_free(plugin);
4371da177e4SLinus Torvalds 			return err;
4381da177e4SLinus Torvalds 		}
4391da177e4SLinus Torvalds 		srcformat = tmpformat;
4401da177e4SLinus Torvalds 		src_access = dst_access;
4411da177e4SLinus Torvalds 	}
4421da177e4SLinus Torvalds 
4431da177e4SLinus Torvalds 	/* channels reduction */
4441da177e4SLinus Torvalds 	if (srcformat.channels > dstformat.channels) {
4451da177e4SLinus Torvalds 		tmpformat.channels = dstformat.channels;
4460534ab42STakashi Iwai 		err = snd_pcm_plugin_build_route(plug, &srcformat, &tmpformat, &plugin);
4471da177e4SLinus Torvalds 		pdprintf("channels reduction: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err);
4480534ab42STakashi Iwai 		if (err < 0)
4491da177e4SLinus Torvalds 			return err;
4501da177e4SLinus Torvalds 		err = snd_pcm_plugin_append(plugin);
4511da177e4SLinus Torvalds 		if (err < 0) {
4521da177e4SLinus Torvalds 			snd_pcm_plugin_free(plugin);
4531da177e4SLinus Torvalds 			return err;
4541da177e4SLinus Torvalds 		}
4551da177e4SLinus Torvalds 		srcformat = tmpformat;
4561da177e4SLinus Torvalds 		src_access = dst_access;
4571da177e4SLinus Torvalds 	}
4581da177e4SLinus Torvalds 
4591da177e4SLinus Torvalds 	/* rate resampling */
4601da177e4SLinus Torvalds 	if (!rate_match(srcformat.rate, dstformat.rate)) {
4610534ab42STakashi Iwai 		if (srcformat.format != SNDRV_PCM_FORMAT_S16) {
4620534ab42STakashi Iwai 			/* convert to S16 for resampling */
4630534ab42STakashi Iwai 			tmpformat.format = SNDRV_PCM_FORMAT_S16;
4640534ab42STakashi Iwai 			err = snd_pcm_plugin_build_linear(plug,
4651da177e4SLinus Torvalds 							  &srcformat, &tmpformat,
4661da177e4SLinus Torvalds 							  &plugin);
4670534ab42STakashi Iwai 			if (err < 0)
4681da177e4SLinus Torvalds 				return err;
4691da177e4SLinus Torvalds 			err = snd_pcm_plugin_append(plugin);
4701da177e4SLinus Torvalds 			if (err < 0) {
4711da177e4SLinus Torvalds 				snd_pcm_plugin_free(plugin);
4721da177e4SLinus Torvalds 				return err;
4731da177e4SLinus Torvalds 			}
4741da177e4SLinus Torvalds 			srcformat = tmpformat;
4751da177e4SLinus Torvalds 			src_access = dst_access;
4761da177e4SLinus Torvalds 		}
4770534ab42STakashi Iwai 		tmpformat.rate = dstformat.rate;
4780534ab42STakashi Iwai         	err = snd_pcm_plugin_build_rate(plug,
4791da177e4SLinus Torvalds         					&srcformat, &tmpformat,
4800534ab42STakashi Iwai 						&plugin);
4810534ab42STakashi Iwai 		pdprintf("rate down resampling: src=%i, dst=%i returns %i\n", srcformat.rate, tmpformat.rate, err);
4820534ab42STakashi Iwai 		if (err < 0)
4831da177e4SLinus Torvalds 			return err;
4841da177e4SLinus Torvalds 		err = snd_pcm_plugin_append(plugin);
4851da177e4SLinus Torvalds 		if (err < 0) {
4861da177e4SLinus Torvalds 			snd_pcm_plugin_free(plugin);
4871da177e4SLinus Torvalds 			return err;
4881da177e4SLinus Torvalds 		}
4891da177e4SLinus Torvalds 		srcformat = tmpformat;
4901da177e4SLinus Torvalds 		src_access = dst_access;
4911da177e4SLinus Torvalds         }
4921da177e4SLinus Torvalds 
4931da177e4SLinus Torvalds 	/* format change */
4941da177e4SLinus Torvalds 	if (srcformat.format != dstformat.format) {
4951da177e4SLinus Torvalds 		tmpformat.format = dstformat.format;
496c82590d2STakashi Iwai 		if (srcformat.format == SNDRV_PCM_FORMAT_MU_LAW ||
497c82590d2STakashi Iwai 		    tmpformat.format == SNDRV_PCM_FORMAT_MU_LAW) {
4981da177e4SLinus Torvalds 			err = snd_pcm_plugin_build_mulaw(plug,
4991da177e4SLinus Torvalds 							 &srcformat, &tmpformat,
5001da177e4SLinus Torvalds 							 &plugin);
5011da177e4SLinus Torvalds 		}
5021da177e4SLinus Torvalds 		else if (snd_pcm_format_linear(srcformat.format) &&
5031da177e4SLinus Torvalds 			 snd_pcm_format_linear(tmpformat.format)) {
5041da177e4SLinus Torvalds 			err = snd_pcm_plugin_build_linear(plug,
5051da177e4SLinus Torvalds 							  &srcformat, &tmpformat,
5061da177e4SLinus Torvalds 							  &plugin);
5071da177e4SLinus Torvalds 		}
5081da177e4SLinus Torvalds 		else
5091da177e4SLinus Torvalds 			return -EINVAL;
5101da177e4SLinus Torvalds 		pdprintf("format change: src=%i, dst=%i returns %i\n", srcformat.format, tmpformat.format, err);
5111da177e4SLinus Torvalds 		if (err < 0)
5121da177e4SLinus Torvalds 			return err;
5131da177e4SLinus Torvalds 		err = snd_pcm_plugin_append(plugin);
5141da177e4SLinus Torvalds 		if (err < 0) {
5151da177e4SLinus Torvalds 			snd_pcm_plugin_free(plugin);
5161da177e4SLinus Torvalds 			return err;
5171da177e4SLinus Torvalds 		}
5181da177e4SLinus Torvalds 		srcformat = tmpformat;
5191da177e4SLinus Torvalds 		src_access = dst_access;
5201da177e4SLinus Torvalds 	}
5211da177e4SLinus Torvalds 
5220534ab42STakashi Iwai 	/* channels extension */
5230534ab42STakashi Iwai 	if (srcformat.channels < dstformat.channels) {
5240534ab42STakashi Iwai 		tmpformat.channels = dstformat.channels;
5250534ab42STakashi Iwai 		err = snd_pcm_plugin_build_route(plug, &srcformat, &tmpformat, &plugin);
5260534ab42STakashi Iwai 		pdprintf("channels extension: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err);
5270534ab42STakashi Iwai 		if (err < 0)
5280534ab42STakashi Iwai 			return err;
5290534ab42STakashi Iwai 		err = snd_pcm_plugin_append(plugin);
5300534ab42STakashi Iwai 		if (err < 0) {
5310534ab42STakashi Iwai 			snd_pcm_plugin_free(plugin);
5320534ab42STakashi Iwai 			return err;
5330534ab42STakashi Iwai 		}
5340534ab42STakashi Iwai 		srcformat = tmpformat;
5350534ab42STakashi Iwai 		src_access = dst_access;
5360534ab42STakashi Iwai 	}
5370534ab42STakashi Iwai 
5381da177e4SLinus Torvalds 	/* de-interleave */
5391da177e4SLinus Torvalds 	if (src_access != dst_access) {
5401da177e4SLinus Torvalds 		err = snd_pcm_plugin_build_copy(plug,
5411da177e4SLinus Torvalds 						&srcformat,
5421da177e4SLinus Torvalds 						&tmpformat,
5431da177e4SLinus Torvalds 						&plugin);
5441da177e4SLinus Torvalds 		pdprintf("interleave change (copy: returns %i)\n", err);
5451da177e4SLinus Torvalds 		if (err < 0)
5461da177e4SLinus Torvalds 			return err;
5471da177e4SLinus Torvalds 		err = snd_pcm_plugin_append(plugin);
5481da177e4SLinus Torvalds 		if (err < 0) {
5491da177e4SLinus Torvalds 			snd_pcm_plugin_free(plugin);
5501da177e4SLinus Torvalds 			return err;
5511da177e4SLinus Torvalds 		}
5521da177e4SLinus Torvalds 	}
5531da177e4SLinus Torvalds 
5541da177e4SLinus Torvalds 	return 0;
5551da177e4SLinus Torvalds }
5561da177e4SLinus Torvalds 
snd_pcm_plug_client_channels_buf(struct snd_pcm_substream * plug,char * buf,snd_pcm_uframes_t count,struct snd_pcm_plugin_channel ** channels)5576ac77bc1STakashi Iwai snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(struct snd_pcm_substream *plug,
5581da177e4SLinus Torvalds 					 char *buf,
5591da177e4SLinus Torvalds 					 snd_pcm_uframes_t count,
5606ac77bc1STakashi Iwai 					 struct snd_pcm_plugin_channel **channels)
5611da177e4SLinus Torvalds {
5626ac77bc1STakashi Iwai 	struct snd_pcm_plugin *plugin;
5636ac77bc1STakashi Iwai 	struct snd_pcm_plugin_channel *v;
5646ac77bc1STakashi Iwai 	struct snd_pcm_plugin_format *format;
5651da177e4SLinus Torvalds 	int width, nchannels, channel;
5661da177e4SLinus Torvalds 	int stream = snd_pcm_plug_stream(plug);
5671da177e4SLinus Torvalds 
5687eaa943cSTakashi Iwai 	if (snd_BUG_ON(!buf))
5697eaa943cSTakashi Iwai 		return -ENXIO;
5701da177e4SLinus Torvalds 	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
5711da177e4SLinus Torvalds 		plugin = snd_pcm_plug_first(plug);
5721da177e4SLinus Torvalds 		format = &plugin->src_format;
5731da177e4SLinus Torvalds 	} else {
5741da177e4SLinus Torvalds 		plugin = snd_pcm_plug_last(plug);
5751da177e4SLinus Torvalds 		format = &plugin->dst_format;
5761da177e4SLinus Torvalds 	}
5771da177e4SLinus Torvalds 	v = plugin->buf_channels;
5781da177e4SLinus Torvalds 	*channels = v;
57951c816fdSTakashi Iwai 	width = snd_pcm_format_physical_width(format->format);
58051c816fdSTakashi Iwai 	if (width < 0)
5811da177e4SLinus Torvalds 		return width;
5821da177e4SLinus Torvalds 	nchannels = format->channels;
5837eaa943cSTakashi Iwai 	if (snd_BUG_ON(plugin->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED &&
5847eaa943cSTakashi Iwai 		       format->channels > 1))
5857eaa943cSTakashi Iwai 		return -ENXIO;
5861da177e4SLinus Torvalds 	for (channel = 0; channel < nchannels; channel++, v++) {
5871da177e4SLinus Torvalds 		v->frames = count;
5881da177e4SLinus Torvalds 		v->enabled = 1;
5891da177e4SLinus Torvalds 		v->wanted = (stream == SNDRV_PCM_STREAM_CAPTURE);
5901da177e4SLinus Torvalds 		v->area.addr = buf;
5911da177e4SLinus Torvalds 		v->area.first = channel * width;
5921da177e4SLinus Torvalds 		v->area.step = nchannels * width;
5931da177e4SLinus Torvalds 	}
5941da177e4SLinus Torvalds 	return count;
5951da177e4SLinus Torvalds }
5961da177e4SLinus Torvalds 
snd_pcm_plug_write_transfer(struct snd_pcm_substream * plug,struct snd_pcm_plugin_channel * src_channels,snd_pcm_uframes_t size)5976ac77bc1STakashi Iwai snd_pcm_sframes_t snd_pcm_plug_write_transfer(struct snd_pcm_substream *plug, struct snd_pcm_plugin_channel *src_channels, snd_pcm_uframes_t size)
5981da177e4SLinus Torvalds {
5996ac77bc1STakashi Iwai 	struct snd_pcm_plugin *plugin, *next;
6006ac77bc1STakashi Iwai 	struct snd_pcm_plugin_channel *dst_channels;
6011da177e4SLinus Torvalds 	int err;
6021da177e4SLinus Torvalds 	snd_pcm_sframes_t frames = size;
6031da177e4SLinus Torvalds 
6041da177e4SLinus Torvalds 	plugin = snd_pcm_plug_first(plug);
60567089137STakashi Iwai 	while (plugin) {
60667089137STakashi Iwai 		if (frames <= 0)
60767089137STakashi Iwai 			return frames;
60851c816fdSTakashi Iwai 		next = plugin->next;
60951c816fdSTakashi Iwai 		if (next) {
6101da177e4SLinus Torvalds 			snd_pcm_sframes_t frames1 = frames;
61167089137STakashi Iwai 			if (plugin->dst_frames) {
6121da177e4SLinus Torvalds 				frames1 = plugin->dst_frames(plugin, frames);
61367089137STakashi Iwai 				if (frames1 <= 0)
61467089137STakashi Iwai 					return frames1;
61567089137STakashi Iwai 			}
61651c816fdSTakashi Iwai 			err = next->client_channels(next, frames1, &dst_channels);
61751c816fdSTakashi Iwai 			if (err < 0)
6181da177e4SLinus Torvalds 				return err;
6191da177e4SLinus Torvalds 			if (err != frames1) {
6201da177e4SLinus Torvalds 				frames = err;
62167089137STakashi Iwai 				if (plugin->src_frames) {
6221da177e4SLinus Torvalds 					frames = plugin->src_frames(plugin, frames1);
62367089137STakashi Iwai 					if (frames <= 0)
62467089137STakashi Iwai 						return frames;
62567089137STakashi Iwai 				}
6261da177e4SLinus Torvalds 			}
6271da177e4SLinus Torvalds 		} else
6281da177e4SLinus Torvalds 			dst_channels = NULL;
6291da177e4SLinus Torvalds 		pdprintf("write plugin: %s, %li\n", plugin->name, frames);
63051c816fdSTakashi Iwai 		frames = plugin->transfer(plugin, src_channels, dst_channels, frames);
63151c816fdSTakashi Iwai 		if (frames < 0)
6321da177e4SLinus Torvalds 			return frames;
6331da177e4SLinus Torvalds 		src_channels = dst_channels;
6341da177e4SLinus Torvalds 		plugin = next;
6351da177e4SLinus Torvalds 	}
636ff7e06a5STakashi Iwai 	return calc_src_frames(plug, frames, true);
6371da177e4SLinus Torvalds }
6381da177e4SLinus Torvalds 
snd_pcm_plug_read_transfer(struct snd_pcm_substream * plug,struct snd_pcm_plugin_channel * dst_channels_final,snd_pcm_uframes_t size)6396ac77bc1STakashi Iwai snd_pcm_sframes_t snd_pcm_plug_read_transfer(struct snd_pcm_substream *plug, struct snd_pcm_plugin_channel *dst_channels_final, snd_pcm_uframes_t size)
6401da177e4SLinus Torvalds {
6416ac77bc1STakashi Iwai 	struct snd_pcm_plugin *plugin, *next;
6426ac77bc1STakashi Iwai 	struct snd_pcm_plugin_channel *src_channels, *dst_channels;
6431da177e4SLinus Torvalds 	snd_pcm_sframes_t frames = size;
6441da177e4SLinus Torvalds 	int err;
6451da177e4SLinus Torvalds 
646ff7e06a5STakashi Iwai 	frames = calc_src_frames(plug, frames, true);
6471da177e4SLinus Torvalds 	if (frames < 0)
6481da177e4SLinus Torvalds 		return frames;
6491da177e4SLinus Torvalds 
6501da177e4SLinus Torvalds 	src_channels = NULL;
6511da177e4SLinus Torvalds 	plugin = snd_pcm_plug_first(plug);
6521da177e4SLinus Torvalds 	while (plugin && frames > 0) {
65351c816fdSTakashi Iwai 		next = plugin->next;
65451c816fdSTakashi Iwai 		if (next) {
65551c816fdSTakashi Iwai 			err = plugin->client_channels(plugin, frames, &dst_channels);
65651c816fdSTakashi Iwai 			if (err < 0)
6571da177e4SLinus Torvalds 				return err;
6581da177e4SLinus Torvalds 			frames = err;
6591da177e4SLinus Torvalds 		} else {
6601da177e4SLinus Torvalds 			dst_channels = dst_channels_final;
6611da177e4SLinus Torvalds 		}
6621da177e4SLinus Torvalds 		pdprintf("read plugin: %s, %li\n", plugin->name, frames);
66351c816fdSTakashi Iwai 		frames = plugin->transfer(plugin, src_channels, dst_channels, frames);
66451c816fdSTakashi Iwai 		if (frames < 0)
6651da177e4SLinus Torvalds 			return frames;
6661da177e4SLinus Torvalds 		plugin = next;
6671da177e4SLinus Torvalds 		src_channels = dst_channels;
6681da177e4SLinus Torvalds 	}
6691da177e4SLinus Torvalds 	return frames;
6701da177e4SLinus Torvalds }
6711da177e4SLinus Torvalds 
snd_pcm_area_silence(const struct snd_pcm_channel_area * dst_area,size_t dst_offset,size_t samples,snd_pcm_format_t format)6726ac77bc1STakashi Iwai int snd_pcm_area_silence(const struct snd_pcm_channel_area *dst_area, size_t dst_offset,
673fea952e5SClemens Ladisch 			 size_t samples, snd_pcm_format_t format)
6741da177e4SLinus Torvalds {
6751da177e4SLinus Torvalds 	/* FIXME: sub byte resolution and odd dst_offset */
6761da177e4SLinus Torvalds 	unsigned char *dst;
6771da177e4SLinus Torvalds 	unsigned int dst_step;
6781da177e4SLinus Torvalds 	int width;
6791da177e4SLinus Torvalds 	const unsigned char *silence;
6801da177e4SLinus Torvalds 	if (!dst_area->addr)
6811da177e4SLinus Torvalds 		return 0;
6821da177e4SLinus Torvalds 	dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8;
6831da177e4SLinus Torvalds 	width = snd_pcm_format_physical_width(format);
6841da177e4SLinus Torvalds 	if (width <= 0)
6851da177e4SLinus Torvalds 		return -EINVAL;
6861da177e4SLinus Torvalds 	if (dst_area->step == (unsigned int) width && width >= 8)
6871da177e4SLinus Torvalds 		return snd_pcm_format_set_silence(format, dst, samples);
6881da177e4SLinus Torvalds 	silence = snd_pcm_format_silence_64(format);
6891da177e4SLinus Torvalds 	if (! silence)
6901da177e4SLinus Torvalds 		return -EINVAL;
6911da177e4SLinus Torvalds 	dst_step = dst_area->step / 8;
6921da177e4SLinus Torvalds 	if (width == 4) {
6931da177e4SLinus Torvalds 		/* Ima ADPCM */
6941da177e4SLinus Torvalds 		int dstbit = dst_area->first % 8;
6951da177e4SLinus Torvalds 		int dstbit_step = dst_area->step % 8;
6961da177e4SLinus Torvalds 		while (samples-- > 0) {
6971da177e4SLinus Torvalds 			if (dstbit)
6981da177e4SLinus Torvalds 				*dst &= 0xf0;
6991da177e4SLinus Torvalds 			else
7001da177e4SLinus Torvalds 				*dst &= 0x0f;
7011da177e4SLinus Torvalds 			dst += dst_step;
7021da177e4SLinus Torvalds 			dstbit += dstbit_step;
7031da177e4SLinus Torvalds 			if (dstbit == 8) {
7041da177e4SLinus Torvalds 				dst++;
7051da177e4SLinus Torvalds 				dstbit = 0;
7061da177e4SLinus Torvalds 			}
7071da177e4SLinus Torvalds 		}
7081da177e4SLinus Torvalds 	} else {
7091da177e4SLinus Torvalds 		width /= 8;
7101da177e4SLinus Torvalds 		while (samples-- > 0) {
7111da177e4SLinus Torvalds 			memcpy(dst, silence, width);
7121da177e4SLinus Torvalds 			dst += dst_step;
7131da177e4SLinus Torvalds 		}
7141da177e4SLinus Torvalds 	}
7151da177e4SLinus Torvalds 	return 0;
7161da177e4SLinus Torvalds }
7171da177e4SLinus Torvalds 
snd_pcm_area_copy(const struct snd_pcm_channel_area * src_area,size_t src_offset,const struct snd_pcm_channel_area * dst_area,size_t dst_offset,size_t samples,snd_pcm_format_t format)7186ac77bc1STakashi Iwai int snd_pcm_area_copy(const struct snd_pcm_channel_area *src_area, size_t src_offset,
7196ac77bc1STakashi Iwai 		      const struct snd_pcm_channel_area *dst_area, size_t dst_offset,
720fea952e5SClemens Ladisch 		      size_t samples, snd_pcm_format_t format)
7211da177e4SLinus Torvalds {
7221da177e4SLinus Torvalds 	/* FIXME: sub byte resolution and odd dst_offset */
7231da177e4SLinus Torvalds 	char *src, *dst;
7241da177e4SLinus Torvalds 	int width;
7251da177e4SLinus Torvalds 	int src_step, dst_step;
7261da177e4SLinus Torvalds 	src = src_area->addr + (src_area->first + src_area->step * src_offset) / 8;
7271da177e4SLinus Torvalds 	if (!src_area->addr)
7281da177e4SLinus Torvalds 		return snd_pcm_area_silence(dst_area, dst_offset, samples, format);
7291da177e4SLinus Torvalds 	dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8;
7301da177e4SLinus Torvalds 	if (!dst_area->addr)
7311da177e4SLinus Torvalds 		return 0;
7321da177e4SLinus Torvalds 	width = snd_pcm_format_physical_width(format);
7331da177e4SLinus Torvalds 	if (width <= 0)
7341da177e4SLinus Torvalds 		return -EINVAL;
7351da177e4SLinus Torvalds 	if (src_area->step == (unsigned int) width &&
7361da177e4SLinus Torvalds 	    dst_area->step == (unsigned int) width && width >= 8) {
7371da177e4SLinus Torvalds 		size_t bytes = samples * width / 8;
7381da177e4SLinus Torvalds 		memcpy(dst, src, bytes);
7391da177e4SLinus Torvalds 		return 0;
7401da177e4SLinus Torvalds 	}
7411da177e4SLinus Torvalds 	src_step = src_area->step / 8;
7421da177e4SLinus Torvalds 	dst_step = dst_area->step / 8;
7431da177e4SLinus Torvalds 	if (width == 4) {
7441da177e4SLinus Torvalds 		/* Ima ADPCM */
7451da177e4SLinus Torvalds 		int srcbit = src_area->first % 8;
7461da177e4SLinus Torvalds 		int srcbit_step = src_area->step % 8;
7471da177e4SLinus Torvalds 		int dstbit = dst_area->first % 8;
7481da177e4SLinus Torvalds 		int dstbit_step = dst_area->step % 8;
7491da177e4SLinus Torvalds 		while (samples-- > 0) {
7501da177e4SLinus Torvalds 			unsigned char srcval;
7511da177e4SLinus Torvalds 			if (srcbit)
7521da177e4SLinus Torvalds 				srcval = *src & 0x0f;
7531da177e4SLinus Torvalds 			else
7541da177e4SLinus Torvalds 				srcval = (*src & 0xf0) >> 4;
7551da177e4SLinus Torvalds 			if (dstbit)
7561da177e4SLinus Torvalds 				*dst = (*dst & 0xf0) | srcval;
7571da177e4SLinus Torvalds 			else
7581da177e4SLinus Torvalds 				*dst = (*dst & 0x0f) | (srcval << 4);
7591da177e4SLinus Torvalds 			src += src_step;
7601da177e4SLinus Torvalds 			srcbit += srcbit_step;
7611da177e4SLinus Torvalds 			if (srcbit == 8) {
7621da177e4SLinus Torvalds 				src++;
7631da177e4SLinus Torvalds 				srcbit = 0;
7641da177e4SLinus Torvalds 			}
7651da177e4SLinus Torvalds 			dst += dst_step;
7661da177e4SLinus Torvalds 			dstbit += dstbit_step;
7671da177e4SLinus Torvalds 			if (dstbit == 8) {
7681da177e4SLinus Torvalds 				dst++;
7691da177e4SLinus Torvalds 				dstbit = 0;
7701da177e4SLinus Torvalds 			}
7711da177e4SLinus Torvalds 		}
7721da177e4SLinus Torvalds 	} else {
7731da177e4SLinus Torvalds 		width /= 8;
7741da177e4SLinus Torvalds 		while (samples-- > 0) {
7751da177e4SLinus Torvalds 			memcpy(dst, src, width);
7761da177e4SLinus Torvalds 			src += src_step;
7771da177e4SLinus Torvalds 			dst += dst_step;
7781da177e4SLinus Torvalds 		}
7791da177e4SLinus Torvalds 	}
7801da177e4SLinus Torvalds 	return 0;
7811da177e4SLinus Torvalds }
782