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