msm6258.c revision 1.17.8.1 1 /* $NetBSD: msm6258.c,v 1.17.8.1 2017/12/03 11:37:03 jdolecek Exp $ */
2
3 /*
4 * Copyright (c) 2001 Tetsuya Isaki. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
22 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 /*
29 * OKI MSM6258 ADPCM voice synthesizer codec.
30 */
31
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: msm6258.c,v 1.17.8.1 2017/12/03 11:37:03 jdolecek Exp $");
34
35 #include <sys/systm.h>
36 #include <sys/device.h>
37 #include <sys/kmem.h>
38 #include <sys/select.h>
39 #include <sys/audioio.h>
40
41 #include <dev/audio_if.h>
42 #include <dev/auconv.h>
43 #include <dev/audiovar.h>
44 #include <dev/mulaw.h>
45 #include <dev/ic/msm6258var.h>
46
47 struct msm6258_codecvar {
48 stream_filter_t base;
49 short mc_amp;
50 char mc_estim;
51 };
52
53 static stream_filter_t *msm6258_factory
54 (struct audio_softc *,
55 int (*)(struct audio_softc *, stream_fetcher_t *, audio_stream_t *, int));
56 static void msm6258_dtor(struct stream_filter *);
57 static inline uint8_t pcm2adpcm_step(struct msm6258_codecvar *, int16_t);
58 static inline int16_t adpcm2pcm_step(struct msm6258_codecvar *, uint8_t);
59
60 static const int adpcm_estimindex[16] = {
61 2, 6, 10, 14, 18, 22, 26, 30,
62 -2, -6, -10, -14, -18, -22, -26, -30
63 };
64
65 static const int adpcm_estim[49] = {
66 16, 17, 19, 21, 23, 25, 28, 31, 34, 37,
67 41, 45, 50, 55, 60, 66, 73, 80, 88, 97,
68 107, 118, 130, 143, 157, 173, 190, 209, 230, 253,
69 279, 307, 337, 371, 408, 449, 494, 544, 598, 658,
70 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552
71 };
72
73 static const int adpcm_estimstep[16] = {
74 -1, -1, -1, -1, 2, 4, 6, 8,
75 -1, -1, -1, -1, 2, 4, 6, 8
76 };
77
78 static int16_t buzzer; /* sound for debug */
79
80 static stream_filter_t *
81 msm6258_factory(struct audio_softc *asc,
82 int (*fetch_to)(struct audio_softc *, stream_fetcher_t *, audio_stream_t *, int))
83 {
84 struct msm6258_codecvar *this;
85
86 this = kmem_alloc(sizeof(struct msm6258_codecvar), KM_SLEEP);
87 this->base.base.fetch_to = fetch_to;
88 this->base.dtor = msm6258_dtor;
89 this->base.set_fetcher = stream_filter_set_fetcher;
90 this->base.set_inputbuffer = stream_filter_set_inputbuffer;
91 return &this->base;
92 }
93
94 static void
95 msm6258_dtor(struct stream_filter *this)
96 {
97 if (this != NULL)
98 kmem_free(this, sizeof(struct msm6258_codecvar));
99 }
100
101 /*
102 * signed 16bit linear PCM -> OkiADPCM
103 */
104 static inline uint8_t
105 pcm2adpcm_step(struct msm6258_codecvar *mc, int16_t a)
106 {
107 int estim = (int)mc->mc_estim;
108 int df;
109 short dl, c;
110 uint8_t b;
111 uint8_t s;
112
113 df = a - mc->mc_amp;
114 dl = adpcm_estim[estim];
115 c = (df / 16) * 8 / dl;
116 if (df < 0) {
117 b = (unsigned char)(-c) / 2;
118 s = 0x08;
119 } else {
120 b = (unsigned char)(c) / 2;
121 s = 0;
122 }
123 if (b > 7)
124 b = 7;
125 s |= b;
126 mc->mc_amp += (short)(adpcm_estimindex[(int)s] * dl);
127 estim += adpcm_estimstep[b];
128 if (estim < 0)
129 estim = 0;
130 else if (estim > 48)
131 estim = 48;
132
133 mc->mc_estim = estim;
134 return s;
135 }
136
137 #define DEFINE_FILTER(name) \
138 static int \
139 name##_fetch_to(struct audio_softc *, stream_fetcher_t *, audio_stream_t *, int); \
140 stream_filter_t * \
141 name(struct audio_softc *sc, const audio_params_t *from, \
142 const audio_params_t *to) \
143 { \
144 return msm6258_factory(sc, name##_fetch_to); \
145 } \
146 static int \
147 name##_fetch_to(struct audio_softc *asc, stream_fetcher_t *self, audio_stream_t *dst, int max_used)
148
149 DEFINE_FILTER(msm6258_slinear16_to_adpcm)
150 {
151 stream_filter_t *this;
152 struct msm6258_codecvar *mc;
153 uint8_t *d;
154 const uint8_t *s;
155 int m, err, enc_src;
156
157 this = (stream_filter_t *)self;
158 mc = (struct msm6258_codecvar *)self;
159 if ((err = this->prev->fetch_to(asc, this->prev, this->src, max_used * 4)))
160 return err;
161 m = dst->end - dst->start;
162 m = min(m, max_used);
163 d = dst->inp;
164 s = this->src->outp;
165 enc_src = this->src->param.encoding;
166 if (enc_src == AUDIO_ENCODING_SLINEAR_LE) {
167 while (dst->used < m && this->src->used >= 4) {
168 uint8_t f;
169 int16_t ss;
170 ss = le16toh(*(const int16_t*)s);
171 f = pcm2adpcm_step(mc, ss);
172 s = audio_stream_add_outp(this->src, s, 2);
173 ss = le16toh(*(const int16_t*)s);
174 f |= pcm2adpcm_step(mc, ss) << 4;
175 s = audio_stream_add_outp(this->src, s, 2);
176 *d = f;
177 d = audio_stream_add_inp(dst, d, 1);
178 }
179 } else if (enc_src == AUDIO_ENCODING_SLINEAR_BE) {
180 while (dst->used < m && this->src->used >= 4) {
181 uint8_t f;
182 int16_t ss;
183 ss = be16toh(*(const int16_t*)s);
184 s = audio_stream_add_outp(this->src, s, 2);
185 f = pcm2adpcm_step(mc, ss);
186 ss = be16toh(*(const int16_t*)s);
187 s = audio_stream_add_outp(this->src, s, 2);
188 f |= pcm2adpcm_step(mc, ss) << 4;
189 *d = f;
190 d = audio_stream_add_inp(dst, d, 1);
191 }
192 } else {
193 #if defined(DIAGNOSTIC)
194 panic("msm6258_slinear16_to_adpcm: unsupported enc_src(%d)", enc_src);
195 #endif
196 /* dummy run */
197 while (dst->used < m && this->src->used >= 4) {
198 s = audio_stream_add_outp(this->src, s, 2);
199 s = audio_stream_add_outp(this->src, s, 2);
200 *d = buzzer++;
201 d = audio_stream_add_inp(dst, d, 1);
202 }
203 }
204 dst->inp = d;
205 this->src->outp = s;
206 return 0;
207 }
208
209 DEFINE_FILTER(msm6258_linear8_to_adpcm)
210 {
211 stream_filter_t *this;
212 struct msm6258_codecvar *mc;
213 uint8_t *d;
214 const uint8_t *s;
215 int m, err, enc_src;
216
217 this = (stream_filter_t *)self;
218 mc = (struct msm6258_codecvar *)self;
219 if ((err = this->prev->fetch_to(asc, this->prev, this->src, max_used * 2)))
220 return err;
221 m = dst->end - dst->start;
222 m = min(m, max_used);
223 d = dst->inp;
224 s = this->src->outp;
225 enc_src = this->src->param.encoding;
226 if (enc_src == AUDIO_ENCODING_SLINEAR_LE
227 || enc_src == AUDIO_ENCODING_SLINEAR_BE) {
228 while (dst->used < m && this->src->used >= 4) {
229 uint8_t f;
230 int16_t ss;
231 ss = ((int16_t)s[0]) * 256;
232 s = audio_stream_add_outp(this->src, s, 1);
233 f = pcm2adpcm_step(mc, ss);
234 ss = ((int16_t)s[0]) * 256;
235 s = audio_stream_add_outp(this->src, s, 1);
236 f |= pcm2adpcm_step(mc, ss) << 4;
237 *d = f;
238 d = audio_stream_add_inp(dst, d, 1);
239 }
240 } else if (enc_src == AUDIO_ENCODING_ULINEAR_LE
241 || enc_src == AUDIO_ENCODING_ULINEAR_BE) {
242 while (dst->used < m && this->src->used >= 4) {
243 uint8_t f;
244 int16_t ss;
245 ss = ((int16_t)(s[0] ^ 0x80)) * 256;
246 s = audio_stream_add_outp(this->src, s, 1);
247 f = pcm2adpcm_step(mc, ss);
248 ss = ((int16_t)(s[0] ^ 0x80)) * 256;
249 s = audio_stream_add_outp(this->src, s, 1);
250 f |= pcm2adpcm_step(mc, ss) << 4;
251 *d = f;
252 d = audio_stream_add_inp(dst, d, 1);
253 }
254 } else {
255 #if defined(DIAGNOSTIC)
256 panic("msm6258_linear8_to_adpcm: unsupported enc_src(%d)", enc_src);
257 #endif
258 /* dummy run */
259 while (dst->used < m && this->src->used >= 4) {
260 s = audio_stream_add_outp(this->src, s, 1);
261 s = audio_stream_add_outp(this->src, s, 1);
262 *d = buzzer++;
263 d = audio_stream_add_inp(dst, d, 1);
264 }
265 }
266 dst->inp = d;
267 this->src->outp = s;
268 return 0;
269 }
270
271 /*
272 * OkiADPCM -> signed 16bit linear PCM
273 */
274 static inline int16_t
275 adpcm2pcm_step(struct msm6258_codecvar *mc, uint8_t b)
276 {
277 int estim = (int)mc->mc_estim;
278
279 mc->mc_amp += adpcm_estim[estim] * adpcm_estimindex[b];
280 estim += adpcm_estimstep[b];
281
282 if (estim < 0)
283 estim = 0;
284 else if (estim > 48)
285 estim = 48;
286
287 mc->mc_estim = estim;
288
289 return mc->mc_amp;
290 }
291
292 DEFINE_FILTER(msm6258_adpcm_to_slinear16)
293 {
294 stream_filter_t *this;
295 struct msm6258_codecvar *mc;
296 uint8_t *d;
297 const uint8_t *s;
298 int m, err, enc_dst;
299
300 this = (stream_filter_t *)self;
301 mc = (struct msm6258_codecvar *)self;
302 max_used = (max_used + 3) & ~3; /* round up multiple of 4 */
303 if ((err = this->prev->fetch_to(asc, this->prev, this->src, max_used / 4)))
304 return err;
305 m = (dst->end - dst->start) & ~3;
306 m = min(m, max_used);
307 d = dst->inp;
308 s = this->src->outp;
309 enc_dst = dst->param.encoding;
310 if (enc_dst == AUDIO_ENCODING_SLINEAR_LE) {
311 while (dst->used < m && this->src->used >= 1) {
312 uint8_t a;
313 int16_t s1, s2;
314 a = s[0];
315 s1 = adpcm2pcm_step(mc, a & 0x0f);
316 s2 = adpcm2pcm_step(mc, a >> 4);
317 *(int16_t*)d = htole16(s1);
318 d = audio_stream_add_inp(dst, d, 2);
319 *(int16_t*)d = htole16(s2);
320 d = audio_stream_add_inp(dst, d, 2);
321 s = audio_stream_add_outp(this->src, s, 1);
322 }
323 } else if (enc_dst == AUDIO_ENCODING_SLINEAR_BE) {
324 while (dst->used < m && this->src->used >= 1) {
325 uint8_t a;
326 int16_t s1, s2;
327 a = s[0];
328 s1 = adpcm2pcm_step(mc, a & 0x0f);
329 s2 = adpcm2pcm_step(mc, a >> 4);
330 *(int16_t*)d = htobe16(s1);
331 d = audio_stream_add_inp(dst, d, 2);
332 *(int16_t*)d = htobe16(s2);
333 d = audio_stream_add_inp(dst, d, 2);
334 s = audio_stream_add_outp(this->src, s, 1);
335 }
336 } else {
337 #if defined(DIAGNOSTIC)
338 panic("msm6258_adpcm_to_slinear16: unsupported enc_dst(%d)", enc_dst);
339 #endif
340 /* dummy run */
341 while (dst->used < m && this->src->used >= 1) {
342 *d = buzzer++;
343 d = audio_stream_add_inp(dst, d, 2);
344 *d = buzzer++;
345 d = audio_stream_add_inp(dst, d, 2);
346 s = audio_stream_add_outp(this->src, s, 1);
347 }
348 }
349 dst->inp = d;
350 this->src->outp = s;
351 return 0;
352 }
353
354 DEFINE_FILTER(msm6258_adpcm_to_linear8)
355 {
356 stream_filter_t *this;
357 struct msm6258_codecvar *mc;
358 uint8_t *d;
359 const uint8_t *s;
360 int m, err, enc_dst;
361
362 this = (stream_filter_t *)self;
363 mc = (struct msm6258_codecvar *)self;
364 max_used = (max_used + 1) & ~1; /* round up multiple of 4 */
365 if ((err = this->prev->fetch_to(asc, this->prev, this->src, max_used / 2)))
366 return err;
367 m = (dst->end - dst->start) & ~1;
368 m = min(m, max_used);
369 d = dst->inp;
370 s = this->src->outp;
371 enc_dst = dst->param.encoding;
372 if (enc_dst == AUDIO_ENCODING_SLINEAR_LE) {
373 while (dst->used < m && this->src->used >= 1) {
374 uint8_t a;
375 int16_t s1, s2;
376 a = s[0];
377 s = audio_stream_add_outp(this->src, s, 1);
378 s1 = adpcm2pcm_step(mc, a & 0x0f);
379 s2 = adpcm2pcm_step(mc, a >> 4);
380 d[0] = s1 / 256;
381 d = audio_stream_add_inp(dst, d, 1);
382 d[0] = s2 / 256;
383 d = audio_stream_add_inp(dst, d, 1);
384 }
385 } else if (enc_dst == AUDIO_ENCODING_ULINEAR_LE) {
386 while (dst->used < m && this->src->used >= 1) {
387 uint8_t a;
388 int16_t s1, s2;
389 a = s[0];
390 s = audio_stream_add_outp(this->src, s, 1);
391 s1 = adpcm2pcm_step(mc, a & 0x0f);
392 s2 = adpcm2pcm_step(mc, a >> 4);
393 d[0] = (s1 / 256) ^ 0x80;
394 d = audio_stream_add_inp(dst, d, 1);
395 d[0] = (s2 / 256) ^ 0x80;
396 d = audio_stream_add_inp(dst, d, 1);
397 }
398 } else {
399 #if defined(DIAGNOSTIC)
400 panic("msm6258_adpcm_to_linear8: unsupported enc_dst(%d)", enc_dst);
401 #endif
402 /* dummy run */
403 while (dst->used < m && this->src->used >= 1) {
404 *d = buzzer++;
405 d = audio_stream_add_inp(dst, d, 1);
406 *d = buzzer++;
407 d = audio_stream_add_inp(dst, d, 1);
408 s = audio_stream_add_outp(this->src, s, 1);
409 }
410 }
411 dst->inp = d;
412 this->src->outp = s;
413 return 0;
414 }
415