Sync usage with man page.
[netbsd-mini2440.git] / sys / dev / ic / msm6258.c
blobb79a02890fe11e1f13b6c4e5cd50212e0d9ddc74
1 /* $NetBSD: msm6258.c,v 1.14 2005/12/11 12:21:28 christos Exp $ */
3 /*
4 * Copyright (c) 2001 Tetsuya Isaki. All rights reserved.
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
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.
31 * OKI MSM6258 ADPCM voice synthesizer codec.
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: msm6258.c,v 1.14 2005/12/11 12:21:28 christos Exp $");
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>
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>
49 struct msm6258_codecvar {
50 stream_filter_t base;
51 short mc_amp;
52 char mc_estim;
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);
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
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
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
79 static stream_filter_t *
80 msm6258_factory(int (*fetch_to)(stream_fetcher_t *, audio_stream_t *, int))
82 struct msm6258_codecvar *this;
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;
92 static void
93 msm6258_dtor(struct stream_filter *this)
95 if (this != NULL)
96 free(this, M_DEVBUF);
100 * signed 16bit linear PCM -> OkiADPCM
102 static inline uint8_t
103 pcm2adpcm_step(struct msm6258_codecvar *mc, int16_t a)
105 int estim = (int)mc->mc_estim;
106 int df;
107 short dl, c;
108 uint8_t b;
109 uint8_t s;
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;
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;
131 mc->mc_estim = estim;
132 return s;
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) \
142 return msm6258_factory(name##_fetch_to); \
144 static int \
145 name##_fetch_to(stream_fetcher_t *self, audio_stream_t *dst, int max_used)
147 DEFINE_FILTER(msm6258_slinear16_to_adpcm)
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;
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);
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);
205 dst->inp = d;
206 this->src->outp = s;
207 return 0;
210 DEFINE_FILTER(msm6258_linear8_to_adpcm)
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;
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);
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);
254 dst->inp = d;
255 this->src->outp = s;
256 return 0;
260 * OkiADPCM -> signed 16bit linear PCM
262 static inline int16_t
263 adpcm2pcm_step(struct msm6258_codecvar *mc, uint8_t b)
265 int estim = (int)mc->mc_estim;
267 mc->mc_amp += adpcm_estim[estim] * adpcm_estimindex[b];
268 estim += adpcm_estimstep[b];
270 if (estim < 0)
271 estim = 0;
272 else if (estim > 48)
273 estim = 48;
275 mc->mc_estim = estim;
277 return mc->mc_amp;
280 DEFINE_FILTER(msm6258_adpcm_to_slinear16)
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;
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);
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);
341 dst->inp = d;
342 this->src->outp = s;
343 return 0;
346 DEFINE_FILTER(msm6258_adpcm_to_linear8)
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;
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);
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);
391 dst->inp = d;
392 this->src->outp = s;
393 return 0;