FreeBSD/Linux Kernel Cross Reference
sys/dev/ic/msm6258.c
1 /* $NetBSD: msm6258.c,v 1.11 2003/09/07 04:24:07 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 * 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.11 2003/09/07 04:24:07 isaki 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 /* ADPCM stream must be converted in order. */
51 u_char mc_buf[AU_RING_SIZE]; /* XXX */
52
53 short mc_amp;
54 char mc_estim;
55 };
56
57 struct msm6258_softc {
58 struct device sc_dev;
59 struct msm6258_codecvar *sc_mc;
60 /* MD vars follow */
61 };
62
63 static inline u_char pcm2adpcm_step(struct msm6258_codecvar *, short);
64 static inline short adpcm2pcm_step(struct msm6258_codecvar *, u_char);
65
66 static int adpcm_estimindex[16] = {
67 2, 6, 10, 14, 18, 22, 26, 30,
68 -2, -6, -10, -14, -18, -22, -26, -30
69 };
70
71 static int adpcm_estim[49] = {
72 16, 17, 19, 21, 23, 25, 28, 31, 34, 37,
73 41, 45, 50, 55, 60, 66, 73, 80, 88, 97,
74 107, 118, 130, 143, 157, 173, 190, 209, 230, 253,
75 279, 307, 337, 371, 408, 449, 494, 544, 598, 658,
76 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552
77 };
78
79 static int adpcm_estimstep[16] = {
80 -1, -1, -1, -1, 2, 4, 6, 8,
81 -1, -1, -1, -1, 2, 4, 6, 8
82 };
83
84 void *
85 msm6258_codec_init (void)
86 {
87 struct msm6258_codecvar *r;
88
89 r = malloc (sizeof(*r), M_DEVBUF, M_NOWAIT);
90 if (r == 0)
91 return 0;
92 r->mc_amp = r->mc_estim = 0;
93
94 return r;
95 }
96
97 int
98 msm6258_codec_open(void *hdl)
99 {
100 struct msm6258_softc *sc = hdl;
101 struct msm6258_codecvar *mc = sc->sc_mc;
102
103 mc->mc_amp = 0;
104 mc->mc_estim = 0;
105
106 return 0;
107 }
108
109 /*
110 * signed 16bit linear PCM -> OkiADPCM
111 */
112 static inline u_char
113 pcm2adpcm_step(struct msm6258_codecvar *mc, short a)
114 {
115 int estim = (int)mc->mc_estim;
116 int df;
117 short dl, c;
118 unsigned char b;
119 unsigned char s;
120
121 df = a - mc->mc_amp;
122 dl = adpcm_estim[estim];
123 c = (df / 16) * 8 / dl;
124 if (df < 0) {
125 b = (unsigned char)(-c) / 2;
126 s = 0x08;
127 } else {
128 b = (unsigned char)(c) / 2;
129 s = 0;
130 }
131 if (b > 7)
132 b = 7;
133 s |= b;
134 mc->mc_amp += (short)(adpcm_estimindex[(int)s] * dl);
135 estim += adpcm_estimstep[b];
136 if (estim < 0)
137 estim = 0;
138 else if (estim > 48)
139 estim = 48;
140
141 mc->mc_estim = estim;
142 return s;
143 }
144
145 void
146 msm6258_slinear16_host_to_adpcm(void *hdl, u_char *p, int cc)
147 {
148 struct msm6258_softc *sc = hdl;
149 struct msm6258_codecvar *mc = sc->sc_mc;
150 short *s = (short *)p;
151 int i;
152 u_char f;
153
154 for (i = 0; i < cc; i += 4) {
155 f = pcm2adpcm_step(mc, *s++);
156 f |= pcm2adpcm_step(mc, *s++) << 4;
157 *p++ = f;
158 }
159 }
160
161 void
162 msm6258_slinear16_le_to_adpcm(void *hdl, u_char *p, int cc)
163 {
164 #if BYTE_ORDER == BIG_ENDIAN
165 swap_bytes(hdl, p, cc);
166 #endif
167 msm6258_slinear16_host_to_adpcm(hdl, p, cc);
168 }
169
170 void
171 msm6258_slinear16_be_to_adpcm(void *hdl, u_char *p, int cc)
172 {
173 #if BYTE_ORDER == LITTLE_ENDIAN
174 swap_bytes(hdl, p, cc);
175 #endif
176 msm6258_slinear16_host_to_adpcm(hdl, p, cc);
177 }
178
179 void
180 msm6258_slinear8_to_adpcm(void *hdl, u_char *p, int cc)
181 {
182 struct msm6258_softc *sc = hdl;
183 struct msm6258_codecvar *mc = sc->sc_mc;
184 u_char *s = p;
185 int i;
186 u_char f;
187
188 for (i = 0; i < cc; i += 2) {
189 f = pcm2adpcm_step(mc, (short)(*s++) * 256);
190 f |= pcm2adpcm_step(mc, (short)(*s++) * 256) << 4;
191 *p++ = f;
192 }
193 }
194
195 void
196 msm6258_ulinear8_to_adpcm(void *hdl, u_char *p, int cc)
197 {
198 change_sign8(hdl, p, cc);
199 msm6258_slinear8_to_adpcm(hdl, p, cc);
200 }
201
202 void
203 msm6258_mulaw_to_adpcm(void *hdl, u_char *p, int cc)
204 {
205 mulaw_to_slinear8(hdl, p, cc);
206 msm6258_slinear8_to_adpcm(hdl, p, cc);
207 }
208
209
210 /*
211 * OkiADPCM -> signed 16bit linear PCM
212 */
213 static inline short
214 adpcm2pcm_step(struct msm6258_codecvar *mc, u_char b)
215 {
216 int estim = (int)mc->mc_estim;
217
218 mc->mc_amp += adpcm_estim[estim] * adpcm_estimindex[b];
219 estim += adpcm_estimstep[b];
220
221 if (estim < 0)
222 estim = 0;
223 else if (estim > 48)
224 estim = 48;
225
226 mc->mc_estim = estim;
227
228 return mc->mc_amp;
229 }
230
231 void
232 msm6258_adpcm_to_slinear16_host(void *hdl, u_char *p, int cc)
233 {
234 struct msm6258_softc *sc = hdl;
235 struct msm6258_codecvar *mc = sc->sc_mc;
236 short *d = (short *)p;
237 int i;
238 u_char a;
239
240 /* XXX alignment ? */
241 memcpy(mc->mc_buf, p, cc / 4);
242 for (i = 0; i < cc / 4;) {
243 a = mc->mc_buf[i++];
244
245 *d++ = adpcm2pcm_step(mc, a & 0x0f);
246 *d++ = adpcm2pcm_step(mc, (a >> 4) & 0x0f);
247 }
248 }
249
250 void
251 msm6258_adpcm_to_slinear16_le(void *hdl, u_char *p, int cc)
252 {
253 msm6258_adpcm_to_slinear16_host(hdl, p, cc);
254 #if BYTE_ORDER == BIG_ENDIAN
255 swap_bytes(hdl, p, cc);
256 #endif
257 }
258
259 void
260 msm6258_adpcm_to_slinear16_be(void *hdl, u_char *p, int cc)
261 {
262 msm6258_adpcm_to_slinear16_host(hdl, p, cc);
263 #if BYTE_ORDER == LITTLE_ENDIAN
264 swap_bytes(hdl, p, cc);
265 #endif
266 }
267
268 void
269 msm6258_adpcm_to_slinear8(void *hdl, u_char *p, int cc)
270 {
271 struct msm6258_softc *sc = hdl;
272 struct msm6258_codecvar *mc = sc->sc_mc;
273 char *d = (char *)p;
274 int i;
275 u_char a;
276
277 /* cc may be even. XXX alignment ? */
278 memcpy(mc->mc_buf, p, cc / 2);
279 for (i = 0; i < cc / 2;) {
280 a = mc->mc_buf[i++];
281
282 *d++ = adpcm2pcm_step(mc, a & 0x0f) / 256;
283 *d++ = adpcm2pcm_step(mc, (a >> 4) & 0x0f) / 256;
284 }
285 }
286
287 void
288 msm6258_adpcm_to_ulinear8(void *hdl, u_char *p, int cc)
289 {
290 msm6258_adpcm_to_slinear8(hdl, p, cc);
291 change_sign8(hdl, p, cc);
292 }
293
294 void
295 msm6258_adpcm_to_mulaw(void *hdl, u_char *p, int cc)
296 {
297 msm6258_adpcm_to_slinear8(hdl, p, cc);
298 slinear8_to_mulaw(hdl, p, cc);
299 }
300
Cache object: 852fa258492dfa8ef8feb9902c588b8c
|