0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023 #include <linux/time.h>
0024 #include <sound/core.h>
0025 #include <sound/pcm.h>
0026 #include "pcm_plugin.h"
0027
0028
0029
0030
0031
0032 struct linear_priv {
0033 int cvt_endian;
0034 unsigned int src_ofs;
0035 unsigned int dst_ofs;
0036 unsigned int copy_ofs;
0037 unsigned int dst_bytes;
0038 unsigned int copy_bytes;
0039 unsigned int flip;
0040 };
0041
0042 static inline void do_convert(struct linear_priv *data,
0043 unsigned char *dst, unsigned char *src)
0044 {
0045 unsigned int tmp = 0;
0046 unsigned char *p = (unsigned char *)&tmp;
0047
0048 memcpy(p + data->copy_ofs, src + data->src_ofs, data->copy_bytes);
0049 if (data->cvt_endian)
0050 tmp = swab32(tmp);
0051 tmp ^= data->flip;
0052 memcpy(dst, p + data->dst_ofs, data->dst_bytes);
0053 }
0054
0055 static void convert(struct snd_pcm_plugin *plugin,
0056 const struct snd_pcm_plugin_channel *src_channels,
0057 struct snd_pcm_plugin_channel *dst_channels,
0058 snd_pcm_uframes_t frames)
0059 {
0060 struct linear_priv *data = (struct linear_priv *)plugin->extra_data;
0061 int channel;
0062 int nchannels = plugin->src_format.channels;
0063 for (channel = 0; channel < nchannels; ++channel) {
0064 char *src;
0065 char *dst;
0066 int src_step, dst_step;
0067 snd_pcm_uframes_t frames1;
0068 if (!src_channels[channel].enabled) {
0069 if (dst_channels[channel].wanted)
0070 snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format);
0071 dst_channels[channel].enabled = 0;
0072 continue;
0073 }
0074 dst_channels[channel].enabled = 1;
0075 src = src_channels[channel].area.addr + src_channels[channel].area.first / 8;
0076 dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
0077 src_step = src_channels[channel].area.step / 8;
0078 dst_step = dst_channels[channel].area.step / 8;
0079 frames1 = frames;
0080 while (frames1-- > 0) {
0081 do_convert(data, dst, src);
0082 src += src_step;
0083 dst += dst_step;
0084 }
0085 }
0086 }
0087
0088 static snd_pcm_sframes_t linear_transfer(struct snd_pcm_plugin *plugin,
0089 const struct snd_pcm_plugin_channel *src_channels,
0090 struct snd_pcm_plugin_channel *dst_channels,
0091 snd_pcm_uframes_t frames)
0092 {
0093 if (snd_BUG_ON(!plugin || !src_channels || !dst_channels))
0094 return -ENXIO;
0095 if (frames == 0)
0096 return 0;
0097 #ifdef CONFIG_SND_DEBUG
0098 {
0099 unsigned int channel;
0100 for (channel = 0; channel < plugin->src_format.channels; channel++) {
0101 if (snd_BUG_ON(src_channels[channel].area.first % 8 ||
0102 src_channels[channel].area.step % 8))
0103 return -ENXIO;
0104 if (snd_BUG_ON(dst_channels[channel].area.first % 8 ||
0105 dst_channels[channel].area.step % 8))
0106 return -ENXIO;
0107 }
0108 }
0109 #endif
0110 if (frames > dst_channels[0].frames)
0111 frames = dst_channels[0].frames;
0112 convert(plugin, src_channels, dst_channels, frames);
0113 return frames;
0114 }
0115
0116 static void init_data(struct linear_priv *data,
0117 snd_pcm_format_t src_format, snd_pcm_format_t dst_format)
0118 {
0119 int src_le, dst_le, src_bytes, dst_bytes;
0120
0121 src_bytes = snd_pcm_format_width(src_format) / 8;
0122 dst_bytes = snd_pcm_format_width(dst_format) / 8;
0123 src_le = snd_pcm_format_little_endian(src_format) > 0;
0124 dst_le = snd_pcm_format_little_endian(dst_format) > 0;
0125
0126 data->dst_bytes = dst_bytes;
0127 data->cvt_endian = src_le != dst_le;
0128 data->copy_bytes = src_bytes < dst_bytes ? src_bytes : dst_bytes;
0129 if (src_le) {
0130 data->copy_ofs = 4 - data->copy_bytes;
0131 data->src_ofs = src_bytes - data->copy_bytes;
0132 } else
0133 data->src_ofs = snd_pcm_format_physical_width(src_format) / 8 -
0134 src_bytes;
0135 if (dst_le)
0136 data->dst_ofs = 4 - data->dst_bytes;
0137 else
0138 data->dst_ofs = snd_pcm_format_physical_width(dst_format) / 8 -
0139 dst_bytes;
0140 if (snd_pcm_format_signed(src_format) !=
0141 snd_pcm_format_signed(dst_format)) {
0142 if (dst_le)
0143 data->flip = (__force u32)cpu_to_le32(0x80000000);
0144 else
0145 data->flip = (__force u32)cpu_to_be32(0x80000000);
0146 }
0147 }
0148
0149 int snd_pcm_plugin_build_linear(struct snd_pcm_substream *plug,
0150 struct snd_pcm_plugin_format *src_format,
0151 struct snd_pcm_plugin_format *dst_format,
0152 struct snd_pcm_plugin **r_plugin)
0153 {
0154 int err;
0155 struct linear_priv *data;
0156 struct snd_pcm_plugin *plugin;
0157
0158 if (snd_BUG_ON(!r_plugin))
0159 return -ENXIO;
0160 *r_plugin = NULL;
0161
0162 if (snd_BUG_ON(src_format->rate != dst_format->rate))
0163 return -ENXIO;
0164 if (snd_BUG_ON(src_format->channels != dst_format->channels))
0165 return -ENXIO;
0166 if (snd_BUG_ON(!snd_pcm_format_linear(src_format->format) ||
0167 !snd_pcm_format_linear(dst_format->format)))
0168 return -ENXIO;
0169
0170 err = snd_pcm_plugin_build(plug, "linear format conversion",
0171 src_format, dst_format,
0172 sizeof(struct linear_priv), &plugin);
0173 if (err < 0)
0174 return err;
0175 data = (struct linear_priv *)plugin->extra_data;
0176 init_data(data, src_format->format, dst_format->format);
0177 plugin->transfer = linear_transfer;
0178 *r_plugin = plugin;
0179 return 0;
0180 }