1 // SPDX-License-Identifier: LGPL-2.0+ 2 /* 3 * Linear conversion Plug-In 4 * Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>, 5 * Abramo Bagnara <abramo@alsa-project.org> 6 */ 7 8 #include <linux/time.h> 9 #include <sound/core.h> 10 #include <sound/pcm.h> 11 #include "pcm_plugin.h" 12 13 /* 14 * Basic linear conversion plugin 15 */ 16 17 struct linear_priv { 18 int cvt_endian; /* need endian conversion? */ 19 unsigned int src_ofs; /* byte offset in source format */ 20 unsigned int dst_ofs; /* byte soffset in destination format */ 21 unsigned int copy_ofs; /* byte offset in temporary u32 data */ 22 unsigned int dst_bytes; /* byte size of destination format */ 23 unsigned int copy_bytes; /* bytes to copy per conversion */ 24 unsigned int flip; /* MSB flip for signeness, done after endian conv */ 25 }; 26 27 static inline void do_convert(struct linear_priv *data, 28 unsigned char *dst, unsigned char *src) 29 { 30 unsigned int tmp = 0; 31 unsigned char *p = (unsigned char *)&tmp; 32 33 memcpy(p + data->copy_ofs, src + data->src_ofs, data->copy_bytes); 34 if (data->cvt_endian) 35 tmp = swab32(tmp); 36 tmp ^= data->flip; 37 memcpy(dst, p + data->dst_ofs, data->dst_bytes); 38 } 39 40 static void convert(struct snd_pcm_plugin *plugin, 41 const struct snd_pcm_plugin_channel *src_channels, 42 struct snd_pcm_plugin_channel *dst_channels, 43 snd_pcm_uframes_t frames) 44 { 45 struct linear_priv *data = (struct linear_priv *)plugin->extra_data; 46 int channel; 47 int nchannels = plugin->src_format.channels; 48 for (channel = 0; channel < nchannels; ++channel) { 49 char *src; 50 char *dst; 51 int src_step, dst_step; 52 snd_pcm_uframes_t frames1; 53 if (!src_channels[channel].enabled) { 54 if (dst_channels[channel].wanted) 55 snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format); 56 dst_channels[channel].enabled = 0; 57 continue; 58 } 59 dst_channels[channel].enabled = 1; 60 src = src_channels[channel].area.addr + src_channels[channel].area.first / 8; 61 dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8; 62 src_step = src_channels[channel].area.step / 8; 63 dst_step = dst_channels[channel].area.step / 8; 64 frames1 = frames; 65 while (frames1-- > 0) { 66 do_convert(data, dst, src); 67 src += src_step; 68 dst += dst_step; 69 } 70 } 71 } 72 73 static snd_pcm_sframes_t linear_transfer(struct snd_pcm_plugin *plugin, 74 const struct snd_pcm_plugin_channel *src_channels, 75 struct snd_pcm_plugin_channel *dst_channels, 76 snd_pcm_uframes_t frames) 77 { 78 if (snd_BUG_ON(!plugin || !src_channels || !dst_channels)) 79 return -ENXIO; 80 if (frames == 0) 81 return 0; 82 #ifdef CONFIG_SND_DEBUG 83 { 84 unsigned int channel; 85 for (channel = 0; channel < plugin->src_format.channels; channel++) { 86 if (snd_BUG_ON(src_channels[channel].area.first % 8 || 87 src_channels[channel].area.step % 8)) 88 return -ENXIO; 89 if (snd_BUG_ON(dst_channels[channel].area.first % 8 || 90 dst_channels[channel].area.step % 8)) 91 return -ENXIO; 92 } 93 } 94 #endif 95 if (frames > dst_channels[0].frames) 96 frames = dst_channels[0].frames; 97 convert(plugin, src_channels, dst_channels, frames); 98 return frames; 99 } 100 101 static void init_data(struct linear_priv *data, 102 snd_pcm_format_t src_format, snd_pcm_format_t dst_format) 103 { 104 int src_le, dst_le, src_bytes, dst_bytes; 105 106 src_bytes = snd_pcm_format_width(src_format) / 8; 107 dst_bytes = snd_pcm_format_width(dst_format) / 8; 108 src_le = snd_pcm_format_little_endian(src_format) > 0; 109 dst_le = snd_pcm_format_little_endian(dst_format) > 0; 110 111 data->dst_bytes = dst_bytes; 112 data->cvt_endian = src_le != dst_le; 113 data->copy_bytes = src_bytes < dst_bytes ? src_bytes : dst_bytes; 114 if (src_le) { 115 data->copy_ofs = 4 - data->copy_bytes; 116 data->src_ofs = src_bytes - data->copy_bytes; 117 } else 118 data->src_ofs = snd_pcm_format_physical_width(src_format) / 8 - 119 src_bytes; 120 if (dst_le) 121 data->dst_ofs = 4 - data->dst_bytes; 122 else 123 data->dst_ofs = snd_pcm_format_physical_width(dst_format) / 8 - 124 dst_bytes; 125 if (snd_pcm_format_signed(src_format) != 126 snd_pcm_format_signed(dst_format)) { 127 if (dst_le) 128 data->flip = (__force u32)cpu_to_le32(0x80000000); 129 else 130 data->flip = (__force u32)cpu_to_be32(0x80000000); 131 } 132 } 133 134 int snd_pcm_plugin_build_linear(struct snd_pcm_substream *plug, 135 struct snd_pcm_plugin_format *src_format, 136 struct snd_pcm_plugin_format *dst_format, 137 struct snd_pcm_plugin **r_plugin) 138 { 139 int err; 140 struct linear_priv *data; 141 struct snd_pcm_plugin *plugin; 142 143 if (snd_BUG_ON(!r_plugin)) 144 return -ENXIO; 145 *r_plugin = NULL; 146 147 if (snd_BUG_ON(src_format->rate != dst_format->rate)) 148 return -ENXIO; 149 if (snd_BUG_ON(src_format->channels != dst_format->channels)) 150 return -ENXIO; 151 if (snd_BUG_ON(!snd_pcm_format_linear(src_format->format) || 152 !snd_pcm_format_linear(dst_format->format))) 153 return -ENXIO; 154 155 err = snd_pcm_plugin_build(plug, "linear format conversion", 156 src_format, dst_format, 157 sizeof(struct linear_priv), &plugin); 158 if (err < 0) 159 return err; 160 data = (struct linear_priv *)plugin->extra_data; 161 init_data(data, src_format->format, dst_format->format); 162 plugin->transfer = linear_transfer; 163 *r_plugin = plugin; 164 return 0; 165 } 166