1 /* $NetBSD: msm6258.c,v 1.10 2002/04/13 12:40:50 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. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed by Tetsuya Isaki. 17 * 4. The name of the author may not be used to endorse or promote products 18 * derived from this software without specific prior written permission 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 25 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 27 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 /* 34 * OKI MSM6258 ADPCM voice synthesizer codec. 35 */ 36 37 #include <sys/cdefs.h> 38 __KERNEL_RCSID(0, "$NetBSD: msm6258.c,v 1.10 2002/04/13 12:40:50 isaki Exp $"); 39 40 #include <sys/systm.h> 41 #include <sys/device.h> 42 #include <sys/malloc.h> 43 #include <sys/select.h> 44 #include <sys/audioio.h> 45 46 #include <dev/audio_if.h> 47 #include <dev/auconv.h> 48 #include <dev/audiovar.h> 49 #include <dev/mulaw.h> 50 #include <dev/ic/msm6258var.h> 51 52 struct msm6258_codecvar { 53 /* ADPCM stream must be converted in order. */ 54 u_char mc_buf[AU_RING_SIZE]; /* XXX */ 55 56 short mc_amp; 57 char mc_estim; 58 }; 59 60 struct msm6258_softc { 61 struct device sc_dev; 62 struct msm6258_codecvar *sc_mc; 63 /* MD vars follow */ 64 }; 65 66 static inline u_char pcm2adpcm_step(struct msm6258_codecvar *, short); 67 static inline short adpcm2pcm_step(struct msm6258_codecvar *, u_char); 68 69 static int adpcm_estimindex[16] = { 70 2, 6, 10, 14, 18, 22, 26, 30, 71 -2, -6, -10, -14, -18, -22, -26, -30 72 }; 73 74 static int adpcm_estim[49] = { 75 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 76 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 77 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 78 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 79 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552 80 }; 81 82 static int adpcm_estimstep[16] = { 83 -1, -1, -1, -1, 2, 4, 6, 8, 84 -1, -1, -1, -1, 2, 4, 6, 8 85 }; 86 87 void * 88 msm6258_codec_init (void) 89 { 90 struct msm6258_codecvar *r; 91 92 r = malloc (sizeof(*r), M_DEVBUF, M_NOWAIT); 93 if (r == 0) 94 return 0; 95 r->mc_amp = r->mc_estim = 0; 96 97 return r; 98 } 99 100 int 101 msm6258_codec_open(void *hdl) 102 { 103 struct msm6258_softc *sc = hdl; 104 struct msm6258_codecvar *mc = sc->sc_mc; 105 106 mc->mc_amp = 0; 107 mc->mc_estim = 0; 108 109 return 0; 110 } 111 112 /* 113 * signed 16bit linear PCM -> OkiADPCM 114 */ 115 static inline u_char 116 pcm2adpcm_step(struct msm6258_codecvar *mc, short a) 117 { 118 int estim = (int)mc->mc_estim; 119 int df; 120 short dl, c; 121 unsigned char b; 122 unsigned char s; 123 124 df = a - mc->mc_amp; 125 dl = adpcm_estim[estim]; 126 c = (df / 16) * 8 / dl; 127 if (df < 0) { 128 b = (unsigned char)(-c) / 2; 129 s = 0x08; 130 } else { 131 b = (unsigned char)(c) / 2; 132 s = 0; 133 } 134 if (b > 7) 135 b = 7; 136 s |= b; 137 mc->mc_amp += (short)(adpcm_estimindex[(int)s] * dl); 138 estim += adpcm_estimstep[b]; 139 if (estim < 0) 140 estim = 0; 141 else if (estim > 48) 142 estim = 48; 143 144 mc->mc_estim = estim; 145 return s; 146 } 147 148 void 149 msm6258_slinear16_host_to_adpcm(void *hdl, u_char *p, int cc) 150 { 151 struct msm6258_softc *sc = hdl; 152 struct msm6258_codecvar *mc = sc->sc_mc; 153 short *s = (short *)p; 154 int i; 155 u_char f; 156 157 for (i = 0; i < cc; i += 4) { 158 f = pcm2adpcm_step(mc, *s++); 159 f |= pcm2adpcm_step(mc, *s++) << 4; 160 *p++ = f; 161 } 162 } 163 164 void 165 msm6258_slinear16_le_to_adpcm(void *hdl, u_char *p, int cc) 166 { 167 #if BYTE_ORDER == BIG_ENDIAN 168 swap_bytes(hdl, p, cc); 169 #endif 170 msm6258_slinear16_host_to_adpcm(hdl, p, cc); 171 } 172 173 void 174 msm6258_slinear16_be_to_adpcm(void *hdl, u_char *p, int cc) 175 { 176 #if BYTE_ORDER == LITTLE_ENDIAN 177 swap_bytes(hdl, p, cc); 178 #endif 179 msm6258_slinear16_host_to_adpcm(hdl, p, cc); 180 } 181 182 void 183 msm6258_slinear8_to_adpcm(void *hdl, u_char *p, int cc) 184 { 185 struct msm6258_softc *sc = hdl; 186 struct msm6258_codecvar *mc = sc->sc_mc; 187 u_char *s = p; 188 int i; 189 u_char f; 190 191 for (i = 0; i < cc; i += 2) { 192 f = pcm2adpcm_step(mc, (short)(*s++) * 256); 193 f |= pcm2adpcm_step(mc, (short)(*s++) * 256) << 4; 194 *p++ = f; 195 } 196 } 197 198 void 199 msm6258_ulinear8_to_adpcm(void *hdl, u_char *p, int cc) 200 { 201 change_sign8(hdl, p, cc); 202 msm6258_slinear8_to_adpcm(hdl, p, cc); 203 } 204 205 void 206 msm6258_mulaw_to_adpcm(void *hdl, u_char *p, int cc) 207 { 208 mulaw_to_slinear8(hdl, p, cc); 209 msm6258_slinear8_to_adpcm(hdl, p, cc); 210 } 211 212 213 /* 214 * OkiADPCM -> signed 16bit linear PCM 215 */ 216 static inline short 217 adpcm2pcm_step(struct msm6258_codecvar *mc, u_char b) 218 { 219 int estim = (int)mc->mc_estim; 220 221 mc->mc_amp += adpcm_estim[estim] * adpcm_estimindex[b]; 222 estim += adpcm_estimstep[b]; 223 224 if (estim < 0) 225 estim = 0; 226 else if (estim > 48) 227 estim = 48; 228 229 mc->mc_estim = estim; 230 231 return mc->mc_amp; 232 } 233 234 void 235 msm6258_adpcm_to_slinear16_host(void *hdl, u_char *p, int cc) 236 { 237 struct msm6258_softc *sc = hdl; 238 struct msm6258_codecvar *mc = sc->sc_mc; 239 short *d = (short *)p; 240 int i; 241 u_char a; 242 243 /* XXX alignment ? */ 244 memcpy(mc->mc_buf, p, cc / 4); 245 for (i = 0; i < cc / 4;) { 246 a = mc->mc_buf[i++]; 247 248 *d++ = adpcm2pcm_step(mc, a & 0x0f); 249 *d++ = adpcm2pcm_step(mc, (a >> 4) & 0x0f); 250 } 251 } 252 253 void 254 msm6258_adpcm_to_slinear16_le(void *hdl, u_char *p, int cc) 255 { 256 msm6258_adpcm_to_slinear16_host(hdl, p, cc); 257 #if BYTE_ORDER == BIG_ENDIAN 258 swap_bytes(hdl, p, cc); 259 #endif 260 } 261 262 void 263 msm6258_adpcm_to_slinear16_be(void *hdl, u_char *p, int cc) 264 { 265 msm6258_adpcm_to_slinear16_host(hdl, p, cc); 266 #if BYTE_ORDER == LITTLE_ENDIAN 267 swap_bytes(hdl, p, cc); 268 #endif 269 } 270 271 void 272 msm6258_adpcm_to_slinear8(void *hdl, u_char *p, int cc) 273 { 274 struct msm6258_softc *sc = hdl; 275 struct msm6258_codecvar *mc = sc->sc_mc; 276 char *d = (char *)p; 277 int i; 278 u_char a; 279 280 /* cc may be even. XXX alignment ? */ 281 memcpy(mc->mc_buf, p, cc / 2); 282 for (i = 0; i < cc / 2;) { 283 a = mc->mc_buf[i++]; 284 285 *d++ = adpcm2pcm_step(mc, a & 0x0f) / 256; 286 *d++ = adpcm2pcm_step(mc, (a >> 4) & 0x0f) / 256; 287 } 288 } 289 290 void 291 msm6258_adpcm_to_ulinear8(void *hdl, u_char *p, int cc) 292 { 293 msm6258_adpcm_to_slinear8(hdl, p, cc); 294 change_sign8(hdl, p, cc); 295 } 296 297 void 298 msm6258_adpcm_to_mulaw(void *hdl, u_char *p, int cc) 299 { 300 msm6258_adpcm_to_slinear8(hdl, p, cc); 301 slinear8_to_mulaw(hdl, p, cc); 302 } 303 304