xref: /netbsd-src/sys/dev/ic/msm6258.c (revision a5847cc334d9a7029f6352b847e9e8d71a0f9e0c)
1 /*	$NetBSD: msm6258.c,v 1.16 2011/10/16 03:10:18 isaki 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.16 2011/10/16 03:10:18 isaki Exp $");
34 
35 #include <sys/systm.h>
36 #include <sys/device.h>
37 #include <sys/malloc.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 	(int (*)(stream_fetcher_t *, audio_stream_t *, int));
55 static void msm6258_dtor(struct stream_filter *);
56 static inline uint8_t	pcm2adpcm_step(struct msm6258_codecvar *, int16_t);
57 static inline int16_t	adpcm2pcm_step(struct msm6258_codecvar *, uint8_t);
58 
59 static const int adpcm_estimindex[16] = {
60 	 2,  6,  10,  14,  18,  22,  26,  30,
61 	-2, -6, -10, -14, -18, -22, -26, -30
62 };
63 
64 static const int adpcm_estim[49] = {
65 	 16,  17,  19,  21,  23,  25,  28,  31,  34,  37,
66 	 41,  45,  50,  55,  60,  66,  73,  80,  88,  97,
67 	107, 118, 130, 143, 157, 173, 190, 209, 230, 253,
68 	279, 307, 337, 371, 408, 449, 494, 544, 598, 658,
69 	724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552
70 };
71 
72 static const int adpcm_estimstep[16] = {
73 	-1, -1, -1, -1, 2, 4, 6, 8,
74 	-1, -1, -1, -1, 2, 4, 6, 8
75 };
76 
77 static stream_filter_t *
78 msm6258_factory(int (*fetch_to)(stream_fetcher_t *, audio_stream_t *, int))
79 {
80 	struct msm6258_codecvar *this;
81 
82 	this = malloc(sizeof(*this), M_DEVBUF, M_WAITOK | M_ZERO);
83 	this->base.base.fetch_to = fetch_to;
84 	this->base.dtor = msm6258_dtor;
85 	this->base.set_fetcher = stream_filter_set_fetcher;
86 	this->base.set_inputbuffer = stream_filter_set_inputbuffer;
87 	return &this->base;
88 }
89 
90 static void
91 msm6258_dtor(struct stream_filter *this)
92 {
93 	if (this != NULL)
94 		free(this, M_DEVBUF);
95 }
96 
97 /*
98  * signed 16bit linear PCM -> OkiADPCM
99  */
100 static inline uint8_t
101 pcm2adpcm_step(struct msm6258_codecvar *mc, int16_t a)
102 {
103 	int estim = (int)mc->mc_estim;
104 	int df;
105 	short dl, c;
106 	uint8_t b;
107 	uint8_t s;
108 
109 	df = a - mc->mc_amp;
110 	dl = adpcm_estim[estim];
111 	c = (df / 16) * 8 / dl;
112 	if (df < 0) {
113 		b = (unsigned char)(-c) / 2;
114 		s = 0x08;
115 	} else {
116 		b = (unsigned char)(c) / 2;
117 		s = 0;
118 	}
119 	if (b > 7)
120 		b = 7;
121 	s |= b;
122 	mc->mc_amp += (short)(adpcm_estimindex[(int)s] * dl);
123 	estim += adpcm_estimstep[b];
124 	if (estim < 0)
125 		estim = 0;
126 	else if (estim > 48)
127 		estim = 48;
128 
129 	mc->mc_estim = estim;
130 	return s;
131 }
132 
133 #define DEFINE_FILTER(name)	\
134 static int \
135 name##_fetch_to(stream_fetcher_t *, audio_stream_t *, int); \
136 stream_filter_t * \
137 name(struct audio_softc *sc, const audio_params_t *from, \
138      const audio_params_t *to) \
139 { \
140 	return msm6258_factory(name##_fetch_to); \
141 } \
142 static int \
143 name##_fetch_to(stream_fetcher_t *self, audio_stream_t *dst, int max_used)
144 
145 DEFINE_FILTER(msm6258_slinear16_to_adpcm)
146 {
147 	stream_filter_t *this;
148 	struct msm6258_codecvar *mc;
149 	uint8_t *d;
150 	const uint8_t *s;
151 	int m, err, enc_src;
152 
153 	this = (stream_filter_t *)self;
154 	mc = (struct msm6258_codecvar *)self;
155 	if ((err = this->prev->fetch_to(this->prev, this->src, max_used * 4)))
156 		return err;
157 	m = dst->end - dst->start;
158 	m = min(m, max_used);
159 	d = dst->inp;
160 	s = this->src->outp;
161 	enc_src = this->src->param.encoding;
162 	if (enc_src == AUDIO_ENCODING_SLINEAR_LE) {
163 		while (dst->used < m && this->src->used >= 4) {
164 			uint8_t f;
165 			int16_t ss;
166 #if BYTE_ORDER == LITTLE_ENDIAN
167 			ss = *(const int16_t*)s;
168 			s = audio_stream_add_outp(this->src, s, 2);
169 			f  = pcm2adpcm_step(mc, ss);
170 			ss = *(const int16_t*)s;
171 #else
172 			ss = (s[1] << 8) | s[0];
173 			s = audio_stream_add_outp(this->src, s, 2);
174 			f  = pcm2adpcm_step(mc, ss);
175 			ss = (s[1] << 8) | s[0];
176 #endif
177 			f |= pcm2adpcm_step(mc, ss) << 4;
178 			*d = f;
179 			d = audio_stream_add_inp(dst, d, 1);
180 			s = audio_stream_add_outp(this->src, s, 2);
181 		}
182 	} else {
183 		while (dst->used < m && this->src->used >= 4) {
184 			uint8_t f;
185 			int16_t ss;
186 #if BYTE_ORDER == BIG_ENDIAN
187 			ss = *(const int16_t*)s;
188 			s = audio_stream_add_outp(this->src, s, 2);
189 			f  = pcm2adpcm_step(mc, ss);
190 			ss = *(const int16_t*)s;
191 #else
192 			ss = (s[0] << 8) | s[1];
193 			s = audio_stream_add_outp(this->src, s, 2);
194 			f  = pcm2adpcm_step(mc, ss);
195 			ss = (s[0] << 8) | s[1];
196 #endif
197 			f |= pcm2adpcm_step(mc, ss) << 4;
198 			*d = f;
199 			d = audio_stream_add_inp(dst, d, 1);
200 			s = audio_stream_add_outp(this->src, s, 2);
201 		}
202 	}
203 	dst->inp = d;
204 	this->src->outp = s;
205 	return 0;
206 }
207 
208 DEFINE_FILTER(msm6258_linear8_to_adpcm)
209 {
210 	stream_filter_t *this;
211 	struct msm6258_codecvar *mc;
212 	uint8_t *d;
213 	const uint8_t *s;
214 	int m, err, enc_src;
215 
216 	this = (stream_filter_t *)self;
217 	mc = (struct msm6258_codecvar *)self;
218 	if ((err = this->prev->fetch_to(this->prev, this->src, max_used * 2)))
219 		return err;
220 	m = dst->end - dst->start;
221 	m = min(m, max_used);
222 	d = dst->inp;
223 	s = this->src->outp;
224 	enc_src = this->src->param.encoding;
225 	if (enc_src == AUDIO_ENCODING_SLINEAR_LE) {
226 		while (dst->used < m && this->src->used >= 4) {
227 			uint8_t f;
228 			int16_t ss;
229 			ss = ((int16_t)s[0]) * 256;
230 			s = audio_stream_add_outp(this->src, s, 1);
231 			f  = pcm2adpcm_step(mc, ss);
232 			ss = ((int16_t)s[0]) * 256;
233 			f |= pcm2adpcm_step(mc, ss) << 4;
234 			*d = f;
235 			d = audio_stream_add_inp(dst, d, 1);
236 			s = audio_stream_add_outp(this->src, s, 1);
237 		}
238 	} else {
239 		while (dst->used < m && this->src->used >= 4) {
240 			uint8_t f;
241 			int16_t ss;
242 			ss = ((int16_t)(s[0] ^ 0x80)) * 256;
243 			s = audio_stream_add_outp(this->src, s, 1);
244 			f  = pcm2adpcm_step(mc, ss);
245 			ss = ((int16_t)(s[0] ^ 0x80)) * 256;
246 			f |= pcm2adpcm_step(mc, ss) << 4;
247 			*d = f;
248 			d = audio_stream_add_inp(dst, d, 1);
249 			s = audio_stream_add_outp(this->src, s, 1);
250 		}
251 	}
252 	dst->inp = d;
253 	this->src->outp = s;
254 	return 0;
255 }
256 
257 /*
258  * OkiADPCM -> signed 16bit linear PCM
259  */
260 static inline int16_t
261 adpcm2pcm_step(struct msm6258_codecvar *mc, uint8_t b)
262 {
263 	int estim = (int)mc->mc_estim;
264 
265 	mc->mc_amp += adpcm_estim[estim] * adpcm_estimindex[b];
266 	estim += adpcm_estimstep[b];
267 
268 	if (estim < 0)
269 		estim = 0;
270 	else if (estim > 48)
271 		estim = 48;
272 
273 	mc->mc_estim = estim;
274 
275 	return mc->mc_amp;
276 }
277 
278 DEFINE_FILTER(msm6258_adpcm_to_slinear16)
279 {
280 	stream_filter_t *this;
281 	struct msm6258_codecvar *mc;
282 	uint8_t *d;
283 	const uint8_t *s;
284 	int m, err, enc_dst;
285 
286 	this = (stream_filter_t *)self;
287 	mc = (struct msm6258_codecvar *)self;
288 	max_used = (max_used + 3) & ~3; /* round up multiple of 4 */
289 	if ((err = this->prev->fetch_to(this->prev, this->src, max_used / 4)))
290 		return err;
291 	m = (dst->end - dst->start) & ~3;
292 	m = min(m, max_used);
293 	d = dst->inp;
294 	s = this->src->outp;
295 	enc_dst = dst->param.encoding;
296 	if (enc_dst == AUDIO_ENCODING_SLINEAR_LE) {
297 		while (dst->used < m && this->src->used >= 1) {
298 			uint8_t a;
299 			int16_t s1, s2;
300 			a = s[0];
301 			s1 = adpcm2pcm_step(mc, a & 0x0f);
302 			s2 = adpcm2pcm_step(mc, a >> 4);
303 #if BYTE_ORDER == LITTLE_ENDIAN
304 			*(int16_t*)d = s1;
305 			d = audio_stream_add_inp(dst, d, 2);
306 			*(int16_t*)d = s2;
307 #else
308 			d[0] = s1;
309 			d[1] = s1 >> 8;
310 			d = audio_stream_add_inp(dst, d, 2);
311 			d[0] = s2;
312 			d[1] = s2 >> 8;
313 #endif
314 			d = audio_stream_add_inp(dst, d, 2);
315 			s = audio_stream_add_outp(this->src, s, 1);
316 		}
317 	} else {
318 		while (dst->used < m && this->src->used >= 1) {
319 			uint8_t a;
320 			int16_t s1, s2;
321 			a = s[0];
322 			s1 = adpcm2pcm_step(mc, a & 0x0f);
323 			s2 = adpcm2pcm_step(mc, a >> 4);
324 #if BYTE_ORDER == BIG_ENDIAN
325 			*(int16_t*)d = s1;
326 			d = audio_stream_add_inp(dst, d, 2);
327 			*(int16_t*)d = s2;
328 #else
329 			d[1] = s1;
330 			d[0] = s1 >> 8;
331 			d = audio_stream_add_inp(dst, d, 2);
332 			d[1] = s2;
333 			d[0] = s2 >> 8;
334 #endif
335 			d = audio_stream_add_inp(dst, d, 2);
336 			s = audio_stream_add_outp(this->src, s, 1);
337 		}
338 	}
339 	dst->inp = d;
340 	this->src->outp = s;
341 	return 0;
342 }
343 
344 DEFINE_FILTER(msm6258_adpcm_to_linear8)
345 {
346 	stream_filter_t *this;
347 	struct msm6258_codecvar *mc;
348 	uint8_t *d;
349 	const uint8_t *s;
350 	int m, err, enc_dst;
351 
352 	this = (stream_filter_t *)self;
353 	mc = (struct msm6258_codecvar *)self;
354 	max_used = (max_used + 1) & ~1; /* round up multiple of 4 */
355 	if ((err = this->prev->fetch_to(this->prev, this->src, max_used / 2)))
356 		return err;
357 	m = (dst->end - dst->start) & ~1;
358 	m = min(m, max_used);
359 	d = dst->inp;
360 	s = this->src->outp;
361 	enc_dst = dst->param.encoding;
362 	if (enc_dst == AUDIO_ENCODING_SLINEAR_LE) {
363 		while (dst->used < m && this->src->used >= 1) {
364 			uint8_t a;
365 			int16_t s1, s2;
366 			a = s[0];
367 			s1 = adpcm2pcm_step(mc, a & 0x0f);
368 			s2 = adpcm2pcm_step(mc, a >> 4);
369 			d[0] = s1 / 266;
370 			d = audio_stream_add_inp(dst, d, 1);
371 			d[0] = s2 / 266;
372 			d = audio_stream_add_inp(dst, d, 1);
373 			s = audio_stream_add_outp(this->src, s, 1);
374 		}
375 	} else {
376 		while (dst->used < m && this->src->used >= 1) {
377 			uint8_t a;
378 			int16_t s1, s2;
379 			a = s[0];
380 			s1 = adpcm2pcm_step(mc, a & 0x0f);
381 			s2 = adpcm2pcm_step(mc, a >> 4);
382 			d[0] = (s1 / 266) ^ 0x80;
383 			d = audio_stream_add_inp(dst, d, 1);
384 			d[0] = (s2 / 266) ^ 0x80;
385 			d = audio_stream_add_inp(dst, d, 1);
386 			s = audio_stream_add_outp(this->src, s, 1);
387 		}
388 	}
389 	dst->inp = d;
390 	this->src->outp = s;
391 	return 0;
392 }
393