1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2008-2009 Ariff Abdullah <ariff@FreeBSD.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 /*
30 * feeder_format: New generation of generic, any-to-any format converter, as
31 * long as the sample values can be read _and_ write.
32 */
33
34 #ifdef _KERNEL
35 #ifdef HAVE_KERNEL_OPTION_HEADERS
36 #include "opt_snd.h"
37 #endif
38 #include <dev/sound/pcm/sound.h>
39 #include <dev/sound/pcm/g711.h>
40 #include "feeder_if.h"
41
42 #define SND_USE_FXDIV
43 #include "snd_fxdiv_gen.h"
44 #endif
45
46 #define FEEDFORMAT_RESERVOIR (SND_CHN_MAX * PCM_32_BPS)
47
48 struct feed_format_info {
49 uint32_t ibps, obps;
50 uint32_t ialign, oalign, channels;
51 intpcm_read_t *read;
52 intpcm_write_t *write;
53 uint8_t reservoir[FEEDFORMAT_RESERVOIR];
54 };
55
56 #define INTPCM_DECLARE_OP_WRITE(SIGN, BIT, ENDIAN, SHIFT) \
57 static __inline void \
58 intpcm_write_##SIGN##BIT##ENDIAN(uint8_t *dst, intpcm_t v) \
59 { \
60 \
61 _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, v >> SHIFT); \
62 }
63
64 #define INTPCM_DECLARE_OP_8(SIGN, ENDIAN) \
65 static __inline intpcm_t \
66 intpcm_read_##SIGN##8##ENDIAN(uint8_t *src) \
67 { \
68 \
69 return (_PCM_READ_##SIGN##8##_##ENDIAN(src) << 24); \
70 } \
71 INTPCM_DECLARE_OP_WRITE(SIGN, 8, ENDIAN, 24)
72
73 #define INTPCM_DECLARE_OP_16(SIGN, ENDIAN) \
74 static __inline intpcm_t \
75 intpcm_read_##SIGN##16##ENDIAN(uint8_t *src) \
76 { \
77 \
78 return (_PCM_READ_##SIGN##16##_##ENDIAN(src) << 16); \
79 } \
80 INTPCM_DECLARE_OP_WRITE(SIGN, 16, ENDIAN, 16)
81
82 #define INTPCM_DECLARE_OP_24(SIGN, ENDIAN) \
83 static __inline intpcm_t \
84 intpcm_read_##SIGN##24##ENDIAN(uint8_t *src) \
85 { \
86 \
87 return (_PCM_READ_##SIGN##24##_##ENDIAN(src) << 8); \
88 } \
89 INTPCM_DECLARE_OP_WRITE(SIGN, 24, ENDIAN, 8)
90
91 #define INTPCM_DECLARE_OP_32(SIGN, ENDIAN) \
92 static __inline intpcm_t \
93 intpcm_read_##SIGN##32##ENDIAN(uint8_t *src) \
94 { \
95 \
96 return (_PCM_READ_##SIGN##32##_##ENDIAN(src)); \
97 } \
98 \
99 static __inline void \
100 intpcm_write_##SIGN##32##ENDIAN(uint8_t *dst, intpcm_t v) \
101 { \
102 \
103 _PCM_WRITE_##SIGN##32##_##ENDIAN(dst, v); \
104 }
105
106 INTPCM_DECLARE_OP_8(S, NE)
107 INTPCM_DECLARE_OP_16(S, LE)
108 INTPCM_DECLARE_OP_16(S, BE)
109 INTPCM_DECLARE_OP_24(S, LE)
110 INTPCM_DECLARE_OP_24(S, BE)
111 INTPCM_DECLARE_OP_32(S, LE)
112 INTPCM_DECLARE_OP_32(S, BE)
113 INTPCM_DECLARE_OP_8(U, NE)
114 INTPCM_DECLARE_OP_16(U, LE)
115 INTPCM_DECLARE_OP_16(U, BE)
116 INTPCM_DECLARE_OP_24(U, LE)
117 INTPCM_DECLARE_OP_24(U, BE)
118 INTPCM_DECLARE_OP_32(U, LE)
119 INTPCM_DECLARE_OP_32(U, BE)
120
121 static const struct {
122 const uint8_t ulaw_to_u8[G711_TABLE_SIZE];
123 const uint8_t alaw_to_u8[G711_TABLE_SIZE];
124 const uint8_t u8_to_ulaw[G711_TABLE_SIZE];
125 const uint8_t u8_to_alaw[G711_TABLE_SIZE];
126 } xlaw_conv_tables = {
127 ULAW_TO_U8,
128 ALAW_TO_U8,
129 U8_TO_ULAW,
130 U8_TO_ALAW
131 };
132
133 static __inline intpcm_t
intpcm_read_ulaw(uint8_t * src)134 intpcm_read_ulaw(uint8_t *src)
135 {
136 return (_G711_TO_INTPCM(xlaw_conv_tables.ulaw_to_u8, *src) << 24);
137 }
138
139 static __inline intpcm_t
intpcm_read_alaw(uint8_t * src)140 intpcm_read_alaw(uint8_t *src)
141 {
142 return (_G711_TO_INTPCM(xlaw_conv_tables.alaw_to_u8, *src) << 24);
143 }
144
145 static __inline void
intpcm_write_ulaw(uint8_t * dst,intpcm_t v)146 intpcm_write_ulaw(uint8_t *dst, intpcm_t v)
147 {
148 *dst = _INTPCM_TO_G711(xlaw_conv_tables.u8_to_ulaw, v >> 24);
149 }
150
151 static __inline void
intpcm_write_alaw(uint8_t * dst,intpcm_t v)152 intpcm_write_alaw(uint8_t *dst, intpcm_t v)
153 {
154 *dst = _INTPCM_TO_G711(xlaw_conv_tables.u8_to_alaw, v >> 24);
155 }
156
157 /*
158 * dummy ac3/dts passthrough, etc.
159 * XXX assume as s16le.
160 */
161 static __inline intpcm_t
intpcm_read_null(uint8_t * src __unused)162 intpcm_read_null(uint8_t *src __unused)
163 {
164
165 return (0);
166 }
167
168 static __inline void
intpcm_write_null(uint8_t * dst,intpcm_t v __unused)169 intpcm_write_null(uint8_t *dst, intpcm_t v __unused)
170 {
171
172 _PCM_WRITE_S16_LE(dst, 0);
173 }
174
175 #define FEEDFORMAT_ENTRY(SIGN, BIT, ENDIAN) \
176 { \
177 AFMT_##SIGN##BIT##_##ENDIAN, \
178 intpcm_read_##SIGN##BIT##ENDIAN, \
179 intpcm_write_##SIGN##BIT##ENDIAN \
180 }
181
182 static const struct {
183 uint32_t format;
184 intpcm_read_t *read;
185 intpcm_write_t *write;
186 } feed_format_ops[] = {
187 FEEDFORMAT_ENTRY(S, 8, NE),
188 FEEDFORMAT_ENTRY(S, 16, LE),
189 FEEDFORMAT_ENTRY(S, 24, LE),
190 FEEDFORMAT_ENTRY(S, 32, LE),
191 FEEDFORMAT_ENTRY(S, 16, BE),
192 FEEDFORMAT_ENTRY(S, 24, BE),
193 FEEDFORMAT_ENTRY(S, 32, BE),
194 FEEDFORMAT_ENTRY(U, 8, NE),
195 FEEDFORMAT_ENTRY(U, 16, LE),
196 FEEDFORMAT_ENTRY(U, 24, LE),
197 FEEDFORMAT_ENTRY(U, 32, LE),
198 FEEDFORMAT_ENTRY(U, 16, BE),
199 FEEDFORMAT_ENTRY(U, 24, BE),
200 FEEDFORMAT_ENTRY(U, 32, BE),
201 {
202 AFMT_MU_LAW,
203 intpcm_read_ulaw, intpcm_write_ulaw
204 },
205 {
206 AFMT_A_LAW,
207 intpcm_read_alaw, intpcm_write_alaw
208 },
209 {
210 AFMT_AC3,
211 intpcm_read_null, intpcm_write_null
212 }
213 };
214
215 static int
feed_format_init(struct pcm_feeder * f)216 feed_format_init(struct pcm_feeder *f)
217 {
218 struct feed_format_info *info;
219 intpcm_read_t *rd_op;
220 intpcm_write_t *wr_op;
221 size_t i;
222
223 if (f->desc->in == f->desc->out ||
224 AFMT_CHANNEL(f->desc->in) != AFMT_CHANNEL(f->desc->out))
225 return (EINVAL);
226
227 rd_op = NULL;
228 wr_op = NULL;
229
230 for (i = 0; i < nitems(feed_format_ops) &&
231 (rd_op == NULL || wr_op == NULL); i++) {
232 if (rd_op == NULL &&
233 AFMT_ENCODING(f->desc->in) == feed_format_ops[i].format)
234 rd_op = feed_format_ops[i].read;
235 if (wr_op == NULL &&
236 AFMT_ENCODING(f->desc->out) == feed_format_ops[i].format)
237 wr_op = feed_format_ops[i].write;
238 }
239
240 if (rd_op == NULL || wr_op == NULL) {
241 printf("%s(): failed to initialize io ops "
242 "in=0x%08x out=0x%08x\n",
243 __func__, f->desc->in, f->desc->out);
244 return (EINVAL);
245 }
246
247 info = malloc(sizeof(*info), M_DEVBUF, M_NOWAIT | M_ZERO);
248 if (info == NULL)
249 return (ENOMEM);
250
251 info->channels = AFMT_CHANNEL(f->desc->in);
252
253 info->ibps = AFMT_BPS(f->desc->in);
254 info->ialign = info->ibps * info->channels;
255 info->read = rd_op;
256
257 info->obps = AFMT_BPS(f->desc->out);
258 info->oalign = info->obps * info->channels;
259 info->write = wr_op;
260
261 f->data = info;
262
263 return (0);
264 }
265
266 static int
feed_format_free(struct pcm_feeder * f)267 feed_format_free(struct pcm_feeder *f)
268 {
269 struct feed_format_info *info;
270
271 info = f->data;
272 if (info != NULL)
273 free(info, M_DEVBUF);
274
275 f->data = NULL;
276
277 return (0);
278 }
279
280 static int
feed_format_set(struct pcm_feeder * f,int what,int value)281 feed_format_set(struct pcm_feeder *f, int what, int value)
282 {
283 struct feed_format_info *info;
284
285 info = f->data;
286
287 switch (what) {
288 case FEEDFORMAT_CHANNELS:
289 if (value < SND_CHN_MIN || value > SND_CHN_MAX)
290 return (EINVAL);
291 info->channels = (uint32_t)value;
292 info->ialign = info->ibps * info->channels;
293 info->oalign = info->obps * info->channels;
294 break;
295 default:
296 return (EINVAL);
297 break;
298 }
299
300 return (0);
301 }
302
303 static int
feed_format_feed(struct pcm_feeder * f,struct pcm_channel * c,uint8_t * b,uint32_t count,void * source)304 feed_format_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
305 uint32_t count, void *source)
306 {
307 struct feed_format_info *info;
308 intpcm_t v;
309 uint32_t j;
310 uint8_t *src, *dst;
311
312 info = f->data;
313 dst = b;
314 count = SND_FXROUND(count, info->oalign);
315
316 do {
317 if (count < info->oalign)
318 break;
319
320 if (count < info->ialign) {
321 src = info->reservoir;
322 j = info->ialign;
323 } else {
324 if (info->ialign == info->oalign)
325 j = count;
326 else if (info->ialign > info->oalign)
327 j = SND_FXROUND(count, info->ialign);
328 else
329 j = SND_FXDIV(count, info->oalign) *
330 info->ialign;
331 src = dst + count - j;
332 }
333
334 j = SND_FXDIV(FEEDER_FEED(f->source, c, src, j, source),
335 info->ialign);
336 if (j == 0)
337 break;
338
339 j *= info->channels;
340 count -= j * info->obps;
341
342 do {
343 v = info->read(src);
344 info->write(dst, v);
345 dst += info->obps;
346 src += info->ibps;
347 } while (--j != 0);
348
349 } while (count != 0);
350
351 return (dst - b);
352 }
353
354 static struct pcm_feederdesc feeder_format_desc[] = {
355 { FEEDER_FORMAT, 0, 0, 0, 0 },
356 { 0, 0, 0, 0, 0 }
357 };
358
359 static kobj_method_t feeder_format_methods[] = {
360 KOBJMETHOD(feeder_init, feed_format_init),
361 KOBJMETHOD(feeder_free, feed_format_free),
362 KOBJMETHOD(feeder_set, feed_format_set),
363 KOBJMETHOD(feeder_feed, feed_format_feed),
364 KOBJMETHOD_END
365 };
366
367 FEEDER_DECLARE(feeder_format, NULL);
368
369 intpcm_read_t *
feeder_format_read_op(uint32_t format)370 feeder_format_read_op(uint32_t format)
371 {
372 size_t i;
373
374 for (i = 0; i < nitems(feed_format_ops); i++) {
375 if (AFMT_ENCODING(format) == feed_format_ops[i].format)
376 return (feed_format_ops[i].read);
377 }
378
379 return (NULL);
380 }
381
382 intpcm_write_t *
feeder_format_write_op(uint32_t format)383 feeder_format_write_op(uint32_t format)
384 {
385 size_t i;
386
387 for (i = 0; i < nitems(feed_format_ops); i++) {
388 if (AFMT_ENCODING(format) == feed_format_ops[i].format)
389 return (feed_format_ops[i].write);
390 }
391
392 return (NULL);
393 }
394