11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds * Linear conversion Plug-In
3c1017a4cSJaroslav Kysela * Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>,
41da177e4SLinus Torvalds * 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 #include <linux/time.h>
241da177e4SLinus Torvalds #include <sound/core.h>
251da177e4SLinus Torvalds #include <sound/pcm.h>
261da177e4SLinus Torvalds #include "pcm_plugin.h"
271da177e4SLinus Torvalds
281da177e4SLinus Torvalds /*
291da177e4SLinus Torvalds * Basic linear conversion plugin
301da177e4SLinus Torvalds */
311da177e4SLinus Torvalds
326ac77bc1STakashi Iwai struct linear_priv {
339390ec85STakashi Iwai int cvt_endian; /* need endian conversion? */
349390ec85STakashi Iwai unsigned int src_ofs; /* byte offset in source format */
359390ec85STakashi Iwai unsigned int dst_ofs; /* byte soffset in destination format */
369390ec85STakashi Iwai unsigned int copy_ofs; /* byte offset in temporary u32 data */
379390ec85STakashi Iwai unsigned int dst_bytes; /* byte size of destination format */
389390ec85STakashi Iwai unsigned int copy_bytes; /* bytes to copy per conversion */
399390ec85STakashi Iwai unsigned int flip; /* MSB flip for signeness, done after endian conv */
406ac77bc1STakashi Iwai };
411da177e4SLinus Torvalds
do_convert(struct linear_priv * data,unsigned char * dst,unsigned char * src)429390ec85STakashi Iwai static inline void do_convert(struct linear_priv *data,
439390ec85STakashi Iwai unsigned char *dst, unsigned char *src)
449390ec85STakashi Iwai {
459390ec85STakashi Iwai unsigned int tmp = 0;
469390ec85STakashi Iwai unsigned char *p = (unsigned char *)&tmp;
479390ec85STakashi Iwai
489390ec85STakashi Iwai memcpy(p + data->copy_ofs, src + data->src_ofs, data->copy_bytes);
499390ec85STakashi Iwai if (data->cvt_endian)
509390ec85STakashi Iwai tmp = swab32(tmp);
519390ec85STakashi Iwai tmp ^= data->flip;
529390ec85STakashi Iwai memcpy(dst, p + data->dst_ofs, data->dst_bytes);
539390ec85STakashi Iwai }
549390ec85STakashi Iwai
convert(struct snd_pcm_plugin * plugin,const struct snd_pcm_plugin_channel * src_channels,struct snd_pcm_plugin_channel * dst_channels,snd_pcm_uframes_t frames)556ac77bc1STakashi Iwai static void convert(struct snd_pcm_plugin *plugin,
566ac77bc1STakashi Iwai const struct snd_pcm_plugin_channel *src_channels,
576ac77bc1STakashi Iwai struct snd_pcm_plugin_channel *dst_channels,
581da177e4SLinus Torvalds snd_pcm_uframes_t frames)
591da177e4SLinus Torvalds {
606ac77bc1STakashi Iwai struct linear_priv *data = (struct linear_priv *)plugin->extra_data;
611da177e4SLinus Torvalds int channel;
621da177e4SLinus Torvalds int nchannels = plugin->src_format.channels;
631da177e4SLinus Torvalds for (channel = 0; channel < nchannels; ++channel) {
641da177e4SLinus Torvalds char *src;
651da177e4SLinus Torvalds char *dst;
661da177e4SLinus Torvalds int src_step, dst_step;
671da177e4SLinus Torvalds snd_pcm_uframes_t frames1;
681da177e4SLinus Torvalds if (!src_channels[channel].enabled) {
691da177e4SLinus Torvalds if (dst_channels[channel].wanted)
701da177e4SLinus Torvalds snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format);
711da177e4SLinus Torvalds dst_channels[channel].enabled = 0;
721da177e4SLinus Torvalds continue;
731da177e4SLinus Torvalds }
741da177e4SLinus Torvalds dst_channels[channel].enabled = 1;
751da177e4SLinus Torvalds src = src_channels[channel].area.addr + src_channels[channel].area.first / 8;
761da177e4SLinus Torvalds dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
771da177e4SLinus Torvalds src_step = src_channels[channel].area.step / 8;
781da177e4SLinus Torvalds dst_step = dst_channels[channel].area.step / 8;
791da177e4SLinus Torvalds frames1 = frames;
801da177e4SLinus Torvalds while (frames1-- > 0) {
819390ec85STakashi Iwai do_convert(data, dst, src);
821da177e4SLinus Torvalds src += src_step;
831da177e4SLinus Torvalds dst += dst_step;
841da177e4SLinus Torvalds }
851da177e4SLinus Torvalds }
861da177e4SLinus Torvalds }
871da177e4SLinus Torvalds
linear_transfer(struct snd_pcm_plugin * plugin,const struct snd_pcm_plugin_channel * src_channels,struct snd_pcm_plugin_channel * dst_channels,snd_pcm_uframes_t frames)886ac77bc1STakashi Iwai static snd_pcm_sframes_t linear_transfer(struct snd_pcm_plugin *plugin,
896ac77bc1STakashi Iwai const struct snd_pcm_plugin_channel *src_channels,
906ac77bc1STakashi Iwai struct snd_pcm_plugin_channel *dst_channels,
911da177e4SLinus Torvalds snd_pcm_uframes_t frames)
921da177e4SLinus Torvalds {
937eaa943cSTakashi Iwai if (snd_BUG_ON(!plugin || !src_channels || !dst_channels))
947eaa943cSTakashi Iwai return -ENXIO;
951da177e4SLinus Torvalds if (frames == 0)
961da177e4SLinus Torvalds return 0;
971da177e4SLinus Torvalds #ifdef CONFIG_SND_DEBUG
981da177e4SLinus Torvalds {
991da177e4SLinus Torvalds unsigned int channel;
1001da177e4SLinus Torvalds for (channel = 0; channel < plugin->src_format.channels; channel++) {
1017eaa943cSTakashi Iwai if (snd_BUG_ON(src_channels[channel].area.first % 8 ||
1027eaa943cSTakashi Iwai src_channels[channel].area.step % 8))
1037eaa943cSTakashi Iwai return -ENXIO;
1047eaa943cSTakashi Iwai if (snd_BUG_ON(dst_channels[channel].area.first % 8 ||
1057eaa943cSTakashi Iwai dst_channels[channel].area.step % 8))
1067eaa943cSTakashi Iwai return -ENXIO;
1071da177e4SLinus Torvalds }
1081da177e4SLinus Torvalds }
1091da177e4SLinus Torvalds #endif
110*4cc8d650STakashi Iwai if (frames > dst_channels[0].frames)
111*4cc8d650STakashi Iwai frames = dst_channels[0].frames;
1121da177e4SLinus Torvalds convert(plugin, src_channels, dst_channels, frames);
1131da177e4SLinus Torvalds return frames;
1141da177e4SLinus Torvalds }
1151da177e4SLinus Torvalds
init_data(struct linear_priv * data,snd_pcm_format_t src_format,snd_pcm_format_t dst_format)116fea952e5SClemens Ladisch static void init_data(struct linear_priv *data,
117fea952e5SClemens Ladisch snd_pcm_format_t src_format, snd_pcm_format_t dst_format)
1181da177e4SLinus Torvalds {
1199390ec85STakashi Iwai int src_le, dst_le, src_bytes, dst_bytes;
1201da177e4SLinus Torvalds
1219390ec85STakashi Iwai src_bytes = snd_pcm_format_width(src_format) / 8;
1229390ec85STakashi Iwai dst_bytes = snd_pcm_format_width(dst_format) / 8;
1239390ec85STakashi Iwai src_le = snd_pcm_format_little_endian(src_format) > 0;
1249390ec85STakashi Iwai dst_le = snd_pcm_format_little_endian(dst_format) > 0;
1251da177e4SLinus Torvalds
1269390ec85STakashi Iwai data->dst_bytes = dst_bytes;
1279390ec85STakashi Iwai data->cvt_endian = src_le != dst_le;
1289390ec85STakashi Iwai data->copy_bytes = src_bytes < dst_bytes ? src_bytes : dst_bytes;
1299390ec85STakashi Iwai if (src_le) {
1309390ec85STakashi Iwai data->copy_ofs = 4 - data->copy_bytes;
1319390ec85STakashi Iwai data->src_ofs = src_bytes - data->copy_bytes;
1329390ec85STakashi Iwai } else
1339390ec85STakashi Iwai data->src_ofs = snd_pcm_format_physical_width(src_format) / 8 -
1349390ec85STakashi Iwai src_bytes;
1359390ec85STakashi Iwai if (dst_le)
1369390ec85STakashi Iwai data->dst_ofs = 4 - data->dst_bytes;
1379390ec85STakashi Iwai else
1389390ec85STakashi Iwai data->dst_ofs = snd_pcm_format_physical_width(dst_format) / 8 -
1399390ec85STakashi Iwai dst_bytes;
1409390ec85STakashi Iwai if (snd_pcm_format_signed(src_format) !=
1419390ec85STakashi Iwai snd_pcm_format_signed(dst_format)) {
1429390ec85STakashi Iwai if (dst_le)
143fea952e5SClemens Ladisch data->flip = (__force u32)cpu_to_le32(0x80000000);
1449390ec85STakashi Iwai else
145fea952e5SClemens Ladisch data->flip = (__force u32)cpu_to_be32(0x80000000);
1469390ec85STakashi Iwai }
1471da177e4SLinus Torvalds }
1481da177e4SLinus Torvalds
snd_pcm_plugin_build_linear(struct snd_pcm_substream * plug,struct snd_pcm_plugin_format * src_format,struct snd_pcm_plugin_format * dst_format,struct snd_pcm_plugin ** r_plugin)1496ac77bc1STakashi Iwai int snd_pcm_plugin_build_linear(struct snd_pcm_substream *plug,
1506ac77bc1STakashi Iwai struct snd_pcm_plugin_format *src_format,
1516ac77bc1STakashi Iwai struct snd_pcm_plugin_format *dst_format,
1526ac77bc1STakashi Iwai struct snd_pcm_plugin **r_plugin)
1531da177e4SLinus Torvalds {
1541da177e4SLinus Torvalds int err;
1556ac77bc1STakashi Iwai struct linear_priv *data;
1566ac77bc1STakashi Iwai struct snd_pcm_plugin *plugin;
1571da177e4SLinus Torvalds
1587eaa943cSTakashi Iwai if (snd_BUG_ON(!r_plugin))
1597eaa943cSTakashi Iwai return -ENXIO;
1601da177e4SLinus Torvalds *r_plugin = NULL;
1611da177e4SLinus Torvalds
1627eaa943cSTakashi Iwai if (snd_BUG_ON(src_format->rate != dst_format->rate))
1637eaa943cSTakashi Iwai return -ENXIO;
1647eaa943cSTakashi Iwai if (snd_BUG_ON(src_format->channels != dst_format->channels))
1657eaa943cSTakashi Iwai return -ENXIO;
1667eaa943cSTakashi Iwai if (snd_BUG_ON(!snd_pcm_format_linear(src_format->format) ||
1677eaa943cSTakashi Iwai !snd_pcm_format_linear(dst_format->format)))
1687eaa943cSTakashi Iwai return -ENXIO;
1691da177e4SLinus Torvalds
1701da177e4SLinus Torvalds err = snd_pcm_plugin_build(plug, "linear format conversion",
1711da177e4SLinus Torvalds src_format, dst_format,
1726ac77bc1STakashi Iwai sizeof(struct linear_priv), &plugin);
1731da177e4SLinus Torvalds if (err < 0)
1741da177e4SLinus Torvalds return err;
1756ac77bc1STakashi Iwai data = (struct linear_priv *)plugin->extra_data;
1769390ec85STakashi Iwai init_data(data, src_format->format, dst_format->format);
1771da177e4SLinus Torvalds plugin->transfer = linear_transfer;
1781da177e4SLinus Torvalds *r_plugin = plugin;
1791da177e4SLinus Torvalds return 0;
1801da177e4SLinus Torvalds }
181