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